@astermind/astermind-premium
Version:
Astermind Premium - Premium ML Toolkit
168 lines • 6.08 kB
JavaScript
// time-series-elm.ts — Time-Series ELM for temporal pattern recognition
// Sequence-to-sequence ELM with temporal dependencies
import { ELM } from '@astermind/astermind-elm';
import { requireLicense } from '../core/license.js';
/**
* Time-Series ELM for temporal pattern recognition
* Features:
* - Temporal pattern recognition
* - Optional recurrent connections
* - Sequence-to-sequence prediction
* - Forecasting capabilities
*/
export class TimeSeriesELM {
constructor(options) {
this.history = []; // Store recent history for recurrent mode
this.trained = false;
requireLicense(); // Premium feature - requires valid license
this.categories = options.categories;
this.options = {
categories: options.categories,
hiddenUnits: options.hiddenUnits ?? 256,
sequenceLength: options.sequenceLength ?? 10,
lookbackWindow: options.lookbackWindow ?? 5,
useRecurrent: options.useRecurrent ?? false,
activation: options.activation ?? 'relu',
maxLen: options.maxLen ?? 100,
useTokenizer: options.useTokenizer ?? true,
};
this.elm = new ELM({
useTokenizer: this.options.useTokenizer ? true : undefined,
hiddenUnits: this.options.hiddenUnits,
categories: this.options.categories,
maxLen: this.options.maxLen,
activation: this.options.activation,
});
}
/**
* Train on time-series data
* @param X Sequences of features (each element is a time step)
* @param y Labels for each sequence
*/
train(X, y) {
// Prepare labels
const labelIndices = y.map(label => typeof label === 'number'
? label
: this.options.categories.indexOf(label));
// Flatten sequences to features
const flattenedFeatures = this._flattenSequences(X);
// Train base ELM
this.elm.setCategories?.(this.options.categories);
this.elm.trainFromData?.(flattenedFeatures, labelIndices);
this.trained = true;
}
/**
* Train on single sequences (convenience method)
*/
trainSequences(sequences, labels) {
this.train(sequences, labels);
}
/**
* Predict from time-series sequence
*/
predict(sequence, topK = 3) {
if (!this.trained) {
throw new Error('Model must be trained before prediction');
}
const sequences = Array.isArray(sequence[0][0])
? sequence
: [sequence];
const allResults = [];
for (const seq of sequences) {
// Flatten sequence to features
const features = this._flattenSequence(seq);
// Update history if using recurrent mode
if (this.options.useRecurrent) {
this._updateHistory(features);
// Use history-enhanced features
const enhancedFeatures = this._enhanceWithHistory(features);
const preds = this.elm.predictFromVector?.([enhancedFeatures], topK) || [];
allResults.push(...preds.map((p) => ({
label: p.label || this.options.categories[p.index || 0],
prob: p.prob || 0,
})));
}
else {
const preds = this.elm.predictFromVector?.([features], topK) || [];
allResults.push(...preds.map((p) => ({
label: p.label || this.options.categories[p.index || 0],
prob: p.prob || 0,
})));
}
}
return allResults;
}
/**
* Forecast future values (for regression/forecasting tasks)
*/
forecast(sequence, steps = 1) {
if (!this.trained) {
throw new Error('Model must be trained before forecasting');
}
const forecasts = [];
let currentSeq = [...sequence];
for (let step = 0; step < steps; step++) {
const features = this._flattenSequence(currentSeq);
const pred = this.elm.predictLogitsFromVector?.(features) || [];
// Use prediction as next step (simplified - in practice, you'd have a regression head)
forecasts.push([...pred]);
// Update sequence for next step
currentSeq.push(pred);
if (currentSeq.length > this.options.sequenceLength) {
currentSeq.shift();
}
}
return forecasts;
}
/**
* Flatten sequences to feature vectors
*/
_flattenSequences(sequences) {
return sequences.map(seq => this._flattenSequence(seq));
}
/**
* Flatten a single sequence
*/
_flattenSequence(sequence) {
// Concatenate all time steps
const flattened = [];
// Take last lookbackWindow steps
const relevantSteps = sequence.slice(-this.options.lookbackWindow);
for (const step of relevantSteps) {
flattened.push(...step);
}
// Pad if necessary
while (flattened.length < this.options.lookbackWindow * (sequence[0]?.length || 1)) {
flattened.push(0);
}
return flattened;
}
/**
* Update history for recurrent mode
*/
_updateHistory(features) {
this.history.push([...features]);
// Keep only recent history
if (this.history.length > this.options.lookbackWindow) {
this.history.shift();
}
}
/**
* Enhance features with history (recurrent mode)
*/
_enhanceWithHistory(currentFeatures) {
if (this.history.length === 0) {
return currentFeatures;
}
// Concatenate history with current features
const historyFeatures = this.history.flat();
return [...historyFeatures, ...currentFeatures];
}
/**
* Clear history (useful for new sequences)
*/
clearHistory() {
this.history = [];
}
}
//# sourceMappingURL=time-series-elm.js.map