If you have trained an LSTM or standard RNN on a long time series, you know the problem with Backpropagation Through Time (BPTT): gradients vanish exponentially, training is slow, and long-term dependencies are hard to learn.
What if you could skip backpropagation entirely and train an RNN in milliseconds with a single closed-form solve? That is the idea behind Reservoir Computing, and its best-known instantiation: the Echo State Network (ESN). This series builds one from scratch in PyTorch and benchmarks it against an LSTM on chaotic time series forecasting.
The Trappings of BPTT
In a standard RNN, every weight matrix--input weights $W_{in}$, hidden weights $W_{rec}$, and output weights $W_{out}$--is learned via gradient descent. Computing the gradient at time $t=0$ requires multiplying through $W_{rec}$ at every intervening step. Over 1,000 timesteps, eigenvalues of $W_{rec}$ slightly below 1 drive the gradient to zero; slightly above 1, and it explodes.
Concretely, the gradient of the loss at time $T$ with respect to the hidden state at time $t$ involves a product of Jacobians:
Each factor in that product is bounded by the spectral norm of $W_{rec}$. After $T - t$ multiplications, the norm either shrinks exponentially (vanishing) or grows exponentially (exploding). LSTMs and GRUs mitigate this with gating, but they still require unrolling the full computation graph through time and storing activations at every step for the backward pass. That means memory usage scales linearly with sequence length, and wall-clock time grows with both sequence length and number of training epochs.
Enter the Reservoir
ESNs sidestep this entirely. Instead of training $W_{in}$ and $W_{rec}$, we randomly initialize them--typically large (hundreds to thousands of neurons) and sparse--and freeze them. This frozen random RNN is the Reservoir.
We feed the input sequence through the reservoir and record its activation states over time. The large, random connectivity produces a high-dimensional, non-linear temporal expansion of the input signal. A 500-neuron reservoir with 5% sparsity contains only about 12,500 active connections, yet it projects a 1-dimensional input into a 500-dimensional state space at every timestep. Each neuron responds to a different random combination of inputs and recurrent feedback, so the reservoir collectively generates a rich basis of nonlinear temporal features without any learning.
The Leaky Integrator Dynamics
The reservoir state update follows the leaky integrator equation, which controls how quickly the network forgets its previous state:
Here $\alpha \in (0, 1]$ is the leak rate. When $\alpha = 1$, the state is fully replaced at each step (standard ESN). When $\alpha$ is small, the state changes slowly, giving the reservoir a longer effective memory. The tanh nonlinearity ensures bounded activations and introduces the nonlinear mixing that makes the reservoir useful as a feature extractor.
The Echo State Property & Spectral Radius
The whole approach depends on the Echo State Property (ESP): the requirement that the influence of past inputs decays over time, so the reservoir neither blows up nor rings indefinitely. More precisely, the ESP requires that for any two initial states $x(0)$ and $x'(0)$, the reservoir states converge to the same trajectory as $t \to \infty$ when driven by the same input sequence. The network's current state becomes a function of its input history alone--previous states are "echoed out."
Enforcing the ESP comes down to one constraint on the reservoir matrix $W_{rec}$: its Spectral Radius (the largest absolute eigenvalue, $\rho$) must be strictly less than 1.
In practice, we generate a sparse random matrix $W$, compute its eigenvalues via torch.linalg.eigvals, find the current spectral radius, and rescale: $W_{rec} = W \cdot (\rho_{\text{target}} / \rho_{\text{current}})$. Setting $\rho = 0.9$ gives a stable, fading-memory system with moderate temporal reach. Values closer to 1.0 extend memory but approach instability; values closer to 0.5 make the reservoir forget faster.
Closed-Form Readout
If we don't backpropagate, how does training work?
Only the final layer $W_{out}$ is learned. The reservoir states ($X$) are fixed after a single forward pass, so predicting the target ($Y$) reduces to linear regression: $Y \approx W_{out} X$.
We solve for $W_{out}$ in one shot using Ridge Regression (Tikhonov Regularization):
The regularization parameter $\lambda$ (typically $10^{-4}$ to $10^{-2}$) prevents overfitting and ensures numerical stability when inverting $X X^T$. In our implementation, we actually solve the equivalent system $(X^T X + \lambda I) W_{out}^T = X^T Y$ using torch.linalg.solve, which avoids explicitly computing the inverse and is numerically more stable.
The extended state matrix $S$ concatenated during readout includes a bias column of ones and the raw input $u(t)$ alongside the reservoir states, giving the readout access to $1 + d_{\text{input}} + d_{\text{hidden}}$ features per timestep. No epochs, no learning rate schedule, no gradient tape--just a single deterministic matrix solve that finishes in milliseconds.