Skip to content

Quickstart

Run this without installing anything

Every snippet below runs in your browser — open the live notebook and press Shift+Enter:

Open the live quickstart

It's a real Python environment (JupyterLite + Pyodide); edit and re-run any cell.

Run it right here

The full quickstart notebook, embedded and runnable in this page (give it a few seconds to boot the in-browser kernel, then run the cells top to bottom):

There is also a federated strategies notebook.

Train a single Echo State Network

An ESN has a large, fixed, random reservoir and a single trained linear readout. Training is a one-shot ridge regression — no backprop.

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

# A benchmark task
u, y = datasets.narma10(3000, rng=0)
u_tr, y_tr, u_te, y_te = datasets.split(u, y, train_frac=0.7)

# Build a reservoir and an ESN, then fit the readout
W = topologies.random_reservoir(200, density=0.1, rng=0)
esn = EchoStateNetwork(1, 1, W, spectral_radius=0.9, leaking_rate=1.0, washout=100)
esn.fit(u_tr, y_tr)

pred = esn.predict(u_te)
print("NRMSE:", metrics.nrmse(y_te[100:], pred[100:]))

Federate it — exactly

With a shared reservoir, each client sends only the sufficient statistics of its local ridge problem (A = ZᵀZ, B = ZᵀY). The server sums them and solves once. The result is identical to pooling all the data centrally — but no raw data ever leaves a client.

from esnfed import datasets, federated, topologies, metrics

u, y = datasets.load_ted_spread()                  # bundled real risk series
u_tr, y_tr, u_te, y_te = datasets.split(u, y, 0.7)
parts = datasets.partition_iid(u_tr, y_tr, n_clients=10)   # 10 "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))

Heterogeneous clients — ensemble

When clients hold different reservoirs, parameters can't be averaged — but predictions can.

kinds = ["random", "small_world", "scale_free", "ring"]
reservoirs = [topologies.make_reservoir(kinds[i % 4], 200, rng=i) for i in range(len(parts))]
het = federated.make_heterogeneous_clients(reservoirs, parts, esn_kwargs=esn_kw)
federated.train_local(het)
pred = federated.ensemble_predict(het, u_te)
print("Ensemble NRMSE:", metrics.nrmse(y_te[100:], pred[100:]))

Continue to the User guide for the details of each piece, or jump to the Examples.