Quickstart#
Every model keeps its reservoirs fixed after random initialization and trains only a linear ridge readout. You feed a series, choose a horizon, and fit.
The class hierarchy at a glance#
BaseESN single reservoir + ridge readout
MultiESN N reservoirs, one input each -> fit([X1, ..., Xn], y)
└─ DoubleReservoirESN fixed to two reservoirs -> fit([X1, X2], y)
├─ fESN memory reservoir <- ((1-B)^d - 1) u(t)
└─ wESN memory reservoir <- ((1-B)^d - 1) MODWT(u)
fESN, fractional memory#
import numpy as np
from memory_esn import fESN
u = np.cumsum(np.random.randn(600)).reshape(-1, 1) * 0.1 # raw series u(t)
Y = np.column_stack([np.roll(u[:, 0], -h) for h in (1, 2, 3)]) # H = 3 horizons
fesn = fESN(
n_reservoir=(200, 150), # (vanilla p, memory q)
d=0.4, K=80, # fractional order, filter lags
spectral_radius=0.9, # echo-state property (< 1)
sparsity=0.9, # 90% sparse reservoirs
noise=(0.0, 0.01), # optional per-reservoir state noise sigma
random_state=0,
)
fesn.fit(u, Y, washout=100)
preds = fesn.predict(u, continuation=True) # (T, H)
d may be a list, several differencing orders stacked as a multivariate memory input:
fESN(d=[0.3, 0.5, 0.8]) # 3 memory channels per feature
wESN, wavelet-smoothed memory#
from memory_esn import wESN
wesn = wESN(
n_reservoir=(200, 150),
d=0.25, K=100,
wavelet="db4", wavelet_level=2, # MODWT smoothing
wavelet_components="smooth", # which components feed reservoir 2
wavelet_norm="modwt", # 'modwt' (Percival-Walden) | 'classic'
random_state=0,
)
wesn.fit(u, Y, washout=100)
preds = wesn.predict(u, continuation=True)
Select any detail level(s) and/or the smooth; multiple selections stack into a multivariate input:
wESN(wavelet_level=3, wavelet_components=[1, 2, "smooth"]) # D1, D2, A3
Alignment & continuation#
The fractional filter needs K past samples. With skip_initial=True (default) and
continuation=False, predictions are K steps shorter than the input. With
continuation=True the model prepends stored history so the output matches the input length.
This is the normal mode for streaming / iterative forecasting. wESN.predict uses
continuation=True by default (it also re-smooths using training history to avoid wavelet
boundary effects).
Persistence#
fesn.save("model.joblib")
from memory_esn import fESN
fesn = fESN.load("model.joblib")
Data splitting#
TimeSeriesDataset builds sliding-window train/val/test splits with leakage-free scaling
(scaling parameters are fit on the train split only):
from memory_esn import TimeSeriesDataset
ds = TimeSeriesDataset(series, lookback=20, lookahead=5, test_size=50, scaling="standard")
X_train, y_train = ds.get_full_train_data()
X_test, y_test = ds.get_test_data()