Configuration#
Every reservoir weight is drawn from a configurable distribution, then (for the recurrent matrix) sparsified and rescaled to the target spectral radius. Defaults follow the paper.
Weight distributions#
Parameter |
Controls |
Options |
Default |
|---|---|---|---|
|
input weights \(\mathbf{W}_{xu}\) |
|
|
|
reservoir weights \(\mathbf{W}_{xx}\) |
same |
|
|
neuron bias |
same |
|
uniform: \(\mathcal{U}[-s, s]\) (the paper’s choice; \(s\) is
input_scalingfor the input)gaussian: \(\mathcal{N}(0, s^2)\)
bernoulli: bimodal \(\{-s, +s\}\)
laplace: \(\mathrm{Laplace}(0, s)\)
For the reservoir the absolute scale is irrelevant, since it is set afterwards by the spectral-radius rescaling, so only the shape of the distribution matters there.
Reservoir knobs#
Parameter |
Meaning |
Range |
Default |
|---|---|---|---|
|
\(\mathbf{W}_{xx}\) rescaled to this \(\rho\) |
below 1 |
|
|
fraction of \(\mathbf{W}_{xx}\) set to zero (\(\varphi\)) |
\([0, 1)\) |
|
|
scale of the bias (0 = none) |
\(\ge 0\) |
|
|
std \(\sigma\) of state noise \(\eta(t)\sim\mathcal{N}(0,\sigma^2 I)\) |
\(\ge 0\) |
|
Activation functions#
The reservoir nonlinearity is chosen with activation=. Seventeen options are built in, each
implemented in the Cython kernel with a matching NumPy fallback:
Name |
Definition |
|---|---|
|
\(\tanh(x)\) |
|
\(1/(1+e^{-x})\) |
|
\(\max(0, x)\) |
|
\(x\) if \(x>0\) else \(0.01x\) |
|
\(x\) if \(x>0\) else \(e^{x}-1\) |
|
scaled ELU |
|
\(\log(1+e^{x})\) |
|
\(x\,\sigma(x)\) |
|
Gaussian error linear unit |
|
\(\mathrm{clip}(x, -1, 1)\) |
|
\(x/(1+\lvert x\rvert)\) |
|
\(x\tanh(\mathrm{softplus}(x))\) |
|
\(\mathrm{clip}(0.2x+0.5, 0, 1)\) |
|
\(\mathrm{clip}(x, 0, 6)\) |
|
\(x-\tanh(x)\) |
|
\(\sin(x)\) |
|
\(x\) |
In the two-reservoir models the activation can differ per reservoir, for example
activation=("tanh", "sin").
Broadcasting rules#
Per-reservoir hyperparameters accept either a single value (applied to every reservoir) or a
list matching the reservoir count. Otherwise a ValueError is raised.
MultiESN: a scalar broadcasts to alln_reservoirs; a list must have lengthn_reservoirs.DoubleReservoirESN/fESN/wESN: a scalar broadcasts to both; a(vanilla, memory)pair sets them individually.random_state: a single int derives distinct per-reservoir seeds (reproducible but not identical); a list sets the seeds explicitly and must match the reservoir count;Nonedisables reproducibility.
from memory_esn import fESN
fESN(
spectral_radius=(0.95, 0.85), # vanilla / memory
sparsity=0.9, # both
noise=(0.0, 0.02), # only the memory reservoir
activation=("tanh", "sin"), # per-reservoir nonlinearity
reservoir_init="uniform",
random_state=0,
)