Skip to main content

esnfed — a federated reservoir computing toolkit

esnfed is a small, dependency-light Python library for training Echo State Networks (reservoir computing) in a federated setting — where several parties jointly train a model without sharing their raw data. It accompanies the Final Degree Project Ensemble of Recurrent Networks for Federated Learning (ETSINF, Universitat Politècnica de València).

The core library depends only on NumPy and NetworkX.

Why this and not ReservoirPy? ReservoirPy is the mature, full-featured library for building and tuning reservoir computing models, and esnfed does not try to replace it. esnfed focuses on the part ReservoirPy does not cover: federating reservoir models across parties. You can design a reservoir in ReservoirPy and federate it here in one line (see Interoperability below).

Gallery

All four plots below are produced by esnfed.viz (see Visualising your ESN).

Reservoir topology Eigenvalue spectrum
Reservoir topology graph Eigenvalue spectrum
Reservoir activations Forecast vs. actual
Reservoir state activations Forecast vs actual

Features

  • Echo State Network with leaky-integrator neurons and a closed-form ridge readout (EchoStateNetwork); a deep / hierarchical stack (DeepEchoStateNetwork); heterogeneous leaking rates and mixed activations.
  • Federated strategies:
    • federated_ridge — exact federated training for a shared reservoir (clients exchange only ridge sufficient statistics; provably equal to pooled training, in one communication round);
    • fedavg — iterative FedAvg on the readout;
    • ensemble_predict — prediction ensemble for heterogeneous reservoirs;
    • structural_alignment — interpolate reservoirs toward a shared structure.
  • Privacy & continual learning — all on the same sufficient statistics:
    • federated_ridge_dp — (ε, δ)-differential privacy (analytic Gaussian mechanism);
    • federated_ridge_secure — secure aggregation (additive masking; the server only sees the sum);
    • StreamingRidge / RLSReadout — incremental / online ridge for streaming and continual federated learning.
  • Sequence classification (classification) with the same exact federated aggregation (e.g. speaker ID, activity recognition).
  • Reservoir topologies: Erdős–Rényi, small-world, scale-free, ring.
  • Data: synthetic benchmarks (NARMA-10, Mackey-Glass, Lorenz); real series — a bundled counterparty-risk series (the TED spread) and FRED loaders (load_fred, multivariate load_fred_matrix); and UCI sequence-classification benchmarks (load_japanese_vowels, load_har).
  • Interoperability: adapters for ReservoirPy reservoirs and an example integration with the Flower federated-learning framework.

Installation

pip install esnfed                  # core (numpy + networkx)
pip install "esnfed[viz]"           # + plotly/matplotlib/seaborn plots
pip install "esnfed[experiments]"   # + matplotlib/pandas/scipy/scikit-learn
pip install "esnfed[reservoirpy]"   # + ReservoirPy interop
pip install "esnfed[flower]"        # + Flower integration

Quick start

Federated counterparty-risk forecasting (real data)

Several institutions jointly forecast the TED spread (a classic gauge of interbank/counterparty credit risk) without sharing their data. Exact federated ridge equals pooled training in a single round:

from esnfed import datasets, federated, topologies, metrics

u, y = datasets.load_ted_spread()                 # bundled real series (FRED)
u_tr, y_tr, u_te, y_te = datasets.split(u, y, 0.7)
parts = datasets.partition_iid(u_tr, y_tr, n_clients=8)   # 8 "institutions"

W = topologies.random_reservoir(200, density=0.1, rng=0)
esn_kw = dict(spectral_radius=0.9, leaking_rate=0.5, washout=100, ridge=1e-6)
clients, ref = federated.make_shared_clients(W, parts, input_seed=0, esn_kwargs=esn_kw)

W_out = federated.federated_ridge(clients, ref)   # one round, exact, private
Z_test = ref.harvest(u_te)[ref.washout:]
print("federated NRMSE:", metrics.nrmse(y_te[ref.washout:], Z_test @ W_out))

Need another series? datasets.load_fred("BAMLH0A0HYM2") pulls a high-yield credit spread from FRED; datasets.from_array(my_series) or datasets.load_csv("my.csv") turn any series into a forecasting task.

Train a single ESN

import numpy as np
from esnfed import EchoStateNetwork, datasets, topologies, metrics

u, y = datasets.narma10(3000, rng=0)
u_tr, y_tr, u_te, y_te = datasets.split(u, y)
W = topologies.random_reservoir(200, density=0.1, rng=0)
esn = EchoStateNetwork(1, 1, W, spectral_radius=0.9, washout=100).fit(u_tr, y_tr)
print("NRMSE:", metrics.nrmse(y_te[100:], esn.predict(u_te)[100:]))

Differential privacy & secure aggregation

Harden the federated exchange — the same (A, B) statistics, now with a formal privacy guarantee, or hidden from the server:

from esnfed import federated
from esnfed.privacy import PrivacyConfig

# (ε, δ)-differentially private readout (clip + analytic Gaussian mechanism)
cfg = PrivacyConfig(epsilon=1.0, delta=1e-5, clip_state=5.0, clip_target=1.0)
W_dp = federated.federated_ridge_dp(clients, ref, cfg)

# secure aggregation: the server only ever sees the masked sum of clients
W_secure = federated.federated_ridge_secure(clients, ref, seed=0)

Interoperability

ReservoirPy — design there, federate here

from reservoirpy.nodes import Reservoir
from esnfed import interop, datasets, federated

res = Reservoir(200, sr=0.9, lr=0.5, input_dim=1)   # design/tune in ReservoirPy
esn = interop.to_esn(res, n_inputs=1)               # -> esnfed EchoStateNetwork
W = interop.reservoir_matrix(res)                   # ... then federate it

Flower — a real FL framework

examples/flower_federated_ridge.py shows the exact federated-ridge scheme as a Flower NumPyClient + custom Strategy: each client's fit returns its ridge sufficient statistics and the server sums them and solves once. The Flower-routed result is identical to federated_ridge to numerical precision.

Visualising your ESN

The optional esnfed.viz module (pip install "esnfed[viz]") provides four plots. The default backend is Plotly (interactive); pass backend="matplotlib" or backend="seaborn" for static figures. Each function returns the native figure object.

from esnfed import EchoStateNetwork, topologies, viz

W = topologies.small_world_reservoir(100, k=6, p=0.1, rng=0)
esn = EchoStateNetwork(1, 1, W, spectral_radius=0.9).fit(u_tr, y_tr)

viz.plot_reservoir(esn).show()           # connectivity graph (degree-coloured)
viz.plot_spectrum(esn).show()            # eigenvalues + unit circle + ρ
viz.plot_states(esn, u_te).show()        # reservoir activations over time
viz.plot_forecast(y_te, esn.predict(u_te), washout=100).show()
viz.save(viz.plot_spectrum(esn), "spectrum.html")   # or .png (needs kaleido)
Function Shows
plot_reservoir reservoir connectivity as a network graph
plot_spectrum eigenvalues in the complex plane (echo state property)
plot_states a sample of reservoir activations over time
plot_forecast predicted vs. actual with NRMSE

Modules

Module Contents
esnfed.esn EchoStateNetwork, ridge helpers
esnfed.deep DeepEchoStateNetwork (stacked reservoirs)
esnfed.topologies reservoir generators + graph_metrics
esnfed.datasets NARMA-10, Mackey-Glass, Lorenz; from_array, load_csv, load_ted_spread, load_fred, load_fred_matrix, load_japanese_vowels, load_har
esnfed.metrics nrmse, rmse, mse, mae, r2_score
esnfed.federated Client, federated_ridge, fedavg, ensemble_predict, structural_alignment, federated_ridge_dp, federated_ridge_secure
esnfed.privacy differential privacy (dp_statistics, gaussian_sigma) + secure aggregation (secure_sum)
esnfed.streaming StreamingRidge, RLSReadout (incremental / online ridge)
esnfed.classification sequence classification + exact federated / ensemble variants
esnfed.interop ReservoirPy adapters (to_esn, reservoir_matrix)
esnfed.viz plot_reservoir, plot_spectrum, plot_states, plot_forecast
esnfed.llm_orchestration experimental FedResPrompt (reservoir soft-prompt control of a frozen LLM)

Reproducing the research experiments

pip install -e ".[experiments,reservoirpy,flower,viz]"
python -m pytest                    # 499 tests
python experiments/run_all.py       # synthetic-benchmark figures and tables
python experiments/exp6_finance.py  # federated counterparty-risk (TED spread)

Correctness

The library is written for producing research results, so it is built to fail loudly rather than return a plausible-looking wrong number:

  • Shapes are checked, not coerced. A (T,) target compared against a (T, 1) prediction, or a transposed (n_inputs, T) input, raises instead of silently broadcasting or reinterleaving the data.
  • Degenerate configurations are rejected at construction — a washout longer than the sequence, a non-positive leaking rate, a negative spectral radius, a zero-sum weight vector.
  • Numerical claims are tested as invariants, not as golden numbers: exactness of federated aggregation, streaming/batch equivalence, the echo state property, permutation invariance of reservoir node labelling, and the (ε, δ) guarantee of the analytic Gaussian mechanism.

See CHANGELOG.md for the defects this uncovered, and the note on what exact federated ridge means precisely (it is exact per partition; the readout itself is limited by the conditioning of the Gram matrix).

Releasing

Releases are published to PyPI by pushing a version tag; the workflow verifies that the tag matches esnfed.__version__, runs the test suite, lints, builds and uploads via PyPI Trusted Publishing (no API token is stored in the repository).

# 1. bump __version__ in esnfed/__init__.py and update CHANGELOG.md
# 2. tag and push
git tag v1.7.0 && git push origin v1.7.0

A one-time setup is needed on PyPI before the first automated release — add a trusted publisher at https://pypi.org/manage/project/esnfed/settings/publishing/ with workflow release.yml and environment pypi. Until then, publish manually:

python -m build && python -m twine upload dist/*

Data attribution

The bundled TED spread is sourced from the Federal Reserve Bank of St. Louis (FRED, series TEDRATE) and is used for demonstration under FRED's terms.

Citation

@thesis{benites2026esnfed,
  author = {Benites Aldaz, Dairon Andres},
  title  = {Ensemble of Recurrent Networks for Federated Learning},
  school = {Universitat Politecnica de Valencia (ETSINF)},
  year   = {2026},
  type   = {Bachelor's thesis},
}

License

MIT — see LICENSE.

Metadata

Release files for esnfed 1.7.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for esnfed 1.7.1
File Size Uploaded Provenance Actions
esnfed-1.7.1.tar.gz 120.9 kB With provenance Details for esnfed-1.7.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for esnfed 1.7.1
File Interpreter ABI Platform Actions
esnfed-1.7.1-py3-none-any.whl Python 3 none any Details for esnfed-1.7.1-py3-none-any.whl

Total release size: 209.7 kB

Release files / esnfed-1.7.1.tar.gz

Download URL esnfed-1.7.1.tar.gz
Size 120.9 kB
Tags Source
SHA-256 checksum
How to use checksums
23ab17dd91d4090ba3c6b331144c19004e866c44663b43311eb50f676ca2fdc6
BLAKE2b-256 checksum
How to use checksums
a4b7febd81f980d2b349e2a43d44f183612350aab79040811b4310dd5236be6d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Release files / esnfed-1.7.1-py3-none-any.whl

Download URL esnfed-1.7.1-py3-none-any.whl
Size 88.8 kB
Tags Python 3
SHA-256 checksum
How to use checksums
c24593a98520753dd13927e38eb3db9ab5969ca038626e4b7b49727cede44837
BLAKE2b-256 checksum
How to use checksums
fec260f787a60ea684af7d156465bdcb0de6bc4a9ed95c931945195fc5cc1211
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

Security for esnfed 1.7.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for esnfed-1.7.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page