Part II · SSM Core Week 4 Published s4_hippo.py test_s4_hippo.py Notebook

S4 / HiPPO: Continuous to Discrete

Implement S4 from continuous-time SSM through HiPPO initialization, ZOH/bilinear discretization, and the recurrent/convolutional duality that makes S4 practical. The pedagogical bridge that underpins every selective SSM in production.

On this page
  1. The continuous SSM
  2. HiPPO: principled initialization
  3. Discretization: continuous to discrete
  4. Zero-Order Hold (ZOH)
  5. Bilinear (Tustin) transform
  6. Three views of SSM computation
  7. Recurrent view
  8. Convolutional view
  9. Mode switching
  10. The S4 layer in Flax NNX
  11. Post-training eigenvalue analysis
  12. Reflections
  13. What transfers
  14. What requires adaptation
  15. Forward map

The continuous SSM

The starting point is the state-space representation — a standard way to describe dynamical systems in control theory and signal processing. Any system of linear ordinary differential equations (ODEs) can be written in this form: The SSM deep learning literature adopted this formulation from the HiPPO paper Gu et al. (2020) .

ddth(t)=Ah(t)+Bu(t),y(t)=Ch(t)+Du(t),\ddt \statevec(t) = \statemat\,\statevec(t) + \inputmat\,u(t), \qquad y(t) = \outputmat\,\statevec(t) + \feedmat\,u(t),

where h(t)RN\statevec(t) \in \R^\statedim is the hidden state (a vector that summarizes the system’s memory — not to be confused with “state” in the ML training sense), u(t)u(t) is the scalar input, y(t)y(t) is the scalar output, and the system is defined by four matrices: ARN×N\statemat \in \R^{\statedim \times \statedim} (dynamics — how the state evolves), BRN×1\inputmat \in \R^{\statedim \times 1} (how input enters the state), CR1×N\outputmat \in \R^{1 \times \statedim} (how state maps to output), and DR\feedmat \in \R (direct input-to-output skip connection). These matrices are time-invariant: the same values apply at every time step. This is the Linear Time-Invariant (LTI) assumption that makes the convolutional view possible (later section). Mamba (Week 7) breaks LTI by making B\inputmat, C\outputmat, Δ\stepsize input-dependent, gaining content-based selection but losing the convolutional view.

The connection to Week 1 is direct: the damped oscillator q¨+γq˙+ω2q=0\ddot{q} + \gamma \dot{q} + \omega^2 q = 0 is exactly the continuous SSM with A=(01ω2γ)\statemat = \begin{pmatrix} 0 & 1 \\ -\omega^2 & -\gamma \end{pmatrix} and B=0\inputmat = 0. Now A\statemat will be learned, not hand-crafted — but the stability requirements are identical: eigenvalues in the left half-plane.

HiPPO: principled initialization

The central question is: how do we initialize A\statemat? Random initialization leads to vanishing or exploding dynamics — the same pathology that makes recurrent neural networks difficult to train.

HiPPO (High-order Polynomial Projection Operators) Gu et al. (2020) provides a principled answer: initialize A\statemat so that the state h(t)\statevec(t) encodes the best N\statedim-term Legendre polynomial approximation of the input history u(τ)u(\tau) for τ[0,t]\tau \in [0, t]. Legendre polynomials are an orthogonal basis for representing functions on an interval — the optimal choice for approximating smooth signals with a finite number of coefficients. The state vector’s kk-th entry holds the kk-th Legendre coefficient of the input history, giving the SSM a mathematically principled “memory” of the past.

Definition 4.1 (HiPPO-LegS).

The HiPPO-LegS (Legendre, Scaled) matrix is:

AHiPPO=TMT1,Mij={(i+1),i=j,(2i+1),i>j,0,i<j,T=diag ⁣(2k+1).\begin{aligned} \statemat_{\text{HiPPO}} &= T \, M \, T^{-1}, \\ M_{ij} &= \begin{cases} -(i+1), & i=j, \\ -(2i+1), & i>j, \\ 0, & i<j, \end{cases} \\ T &= \diag\!\bigl(\sqrt{2k+1}\bigr). \end{aligned}

The input matrix is B=diag(T)\inputmat = \diag(T).

Example 4.2 (HiPPO-LegS at N = 4).

The small-dimensional structure fits on one line of paper:

M=(1000320053307534),T=diag ⁣(1,3,5,7).M = \begin{pmatrix} -1 & 0 & 0 & 0 \\ -3 & -2 & 0 & 0 \\ -5 & -3 & -3 & 0 \\ -7 & -5 & -3 & -4 \end{pmatrix}, \quad T = \diag\!\bigl(1, \sqrt{3}, \sqrt{5}, \sqrt{7}\bigr).

MM is lower-triangular with diagonal (1,2,3,4)(-1, -2, -3, -4), so those are exactly the eigenvalues (each strictly negative). TT is a similarity transform, so AHiPPO=TMT1\statemat_{\text{HiPPO}} = T M T^{-1} inherits the same spectrum. For arbitrary N\statedim the pattern continues: eigenvalues 1,2,,N-1, -2, \ldots, -\statedim, stability guaranteed by construction.

Proposition 4.3 (HiPPO stability).

All eigenvalues of AHiPPO\statemat_{\text{HiPPO}} have strictly negative real part: Re(λk)<0\operatorname{Re}(\lambda_k) < 0 for all kk. For the local LegS construction used in this guide, they are exactly {1,2,,N}\{-1, -2, \ldots, -\statedim\} up to floating-point roundoff.

Proof.

The similarity transform TT preserves eigenvalues. The matrix MM is lower-triangular with diagonal entries (i+1)-(i+1), so its eigenvalues are exactly 1,2,,N-1, -2, \ldots, -\statedim. The same therefore holds for AHiPPO\statemat_{\text{HiPPO}}, placing the entire spectrum in the open left half-plane. Strict negativity is also checked numerically for N256\statedim \leq 256 in the week 4 test test_hippo_eigenvalues_negative_real.

HiPPO-LegS eigenvalue structure for N = 16 and N = 64. All eigenvalues have strictly negative real part, and in this implementation they lie on the negative real axis at approximately -1, -2, …, -N. Larger N spreads the eigenvalues further along the negative real axis, encoding finer-grained timescales.
HiPPO-LegS eigenvalue structure for N = 16 and N = 64. All eigenvalues have strictly negative real part, and in this implementation they lie on the negative real axis at approximately -1, -2, …, -N. Larger N spreads the eigenvalues further along the negative real axis, encoding finer-grained timescales.

Discretization: continuous to discrete

Discretization here means converting a continuous-time ODE (h˙=Ah+Bu\dot{\statevec} = \statemat\statevec + \inputmat u, defined for all tRt \in \R) into a discrete-time recurrence (ht+1=Aˉht+Bˉut\statevec_{t+1} = \discA\statevec_t + \discB u_t, defined at integer time steps). This is the same process as converting a differential equation into a difference equation for numerical simulation — but here the step size Δ\stepsize is a learnable parameter, not a fixed choice. Note: “discretization” in NLP often means tokenization; here it strictly means ODE-to-recurrence conversion.

Given Δ>0\stepsize > 0, we seek discrete matrices Aˉ,Bˉ\discA, \discB such that:

ht=Aˉht1+Bˉut,yt=Cht+Dut.\statevec_t = \discA\,\statevec_{t-1} + \discB\,u_t, \qquad y_t = \outputmat\,\statevec_t + \feedmat\,u_t.

Zero-Order Hold (ZOH)

Zero-Order Hold (ZOH) assumes the input u(t)u(t) is piecewise constant over each discretization interval [t,t+Δ)[t, t{+}\stepsize) — that is, the input doesn’t change between time steps. This is a reasonable assumption when the step size Δ\stepsize is small relative to the input’s rate of change, which is the typical regime for SSMs. ZOH is the default discretization across S4, S4D, S5, and Mamba reference implementations. [Convergence] The resulting formulas are:

Aˉ=eAΔ,Bˉ=A1(eAΔI)B.\discA = e^{\statemat\,\stepsize}, \qquad \discB = \statemat^{-1}\bigl(e^{\statemat\,\stepsize} - I\bigr)\,\inputmat.

We avoid computing A1\statemat^{-1} by using the stacked matrix exponential: build the augmented matrix (AΔBΔ00)\begin{pmatrix} \statemat\stepsize & \inputmat\stepsize \\ 0 & 0 \end{pmatrix}, compute its expm, and extract Aˉ\discA, Bˉ\discB from the result. Implemented in s4_hippo.py:discretize_zoh .

Remark.

This works because the block matrix exponential satisfies

exp ⁣(AΔBΔ00)=(eAΔA1(eAΔI)B0I),\exp\!\begin{pmatrix} \statemat\stepsize & \inputmat\stepsize \\ 0 & 0 \end{pmatrix} = \begin{pmatrix} e^{\statemat\stepsize} & \statemat^{-1}(e^{\statemat\stepsize} - I)\inputmat \\ 0 & I \end{pmatrix},

so a single expm call yields both Aˉ\discA (top-left block) and Bˉ\discB (top-right block) without explicitly inverting A\statemat.

Proposition 4.4 (ZOH stability preservation).

If Re(λk(A))<0\operatorname{Re}(\lambda_k(\statemat)) < 0 and Δ>0\stepsize > 0, then λk(Aˉ)<1\abs{\lambda_k(\discA)} < 1.

Proof.

λk(Aˉ)=eλkΔ=eRe(λk)Δ<1\abs{\lambda_k(\discA)} = \abs{e^{\lambda_k \stepsize}} = e^{\operatorname{Re}(\lambda_k) \stepsize} < 1 since Re(λk)<0\operatorname{Re}(\lambda_k) < 0 and Δ>0\stepsize > 0.

Bilinear (Tustin) transform

The bilinear transform maps the imaginary axis to the unit circle:

Aˉ=(IΔ2A)1(I+Δ2A),Bˉ=(IΔ2A)1ΔB.\discA = \bigl(I - \tfrac{\stepsize}{2}\statemat\bigr)^{-1} \bigl(I + \tfrac{\stepsize}{2}\statemat\bigr), \qquad \discB = \bigl(I - \tfrac{\stepsize}{2}\statemat\bigr)^{-1} \stepsize\,\inputmat.

This is second-order accurate, vs. first-order for forward Euler.

Proposition 4.5 (Bilinear stability preservation).

If all eigenvalues of A\statemat lie in the open left half-plane (Re(λk)<0\operatorname{Re}(\lambda_k) < 0) and Δ>0\stepsize > 0, then all eigenvalues of Aˉ\discA lie strictly inside the unit disk (μk<1\abs{\mu_k} < 1).

Proof.

The bilinear transform maps eigenvalue λk\lambda_k of A\statemat to eigenvalue μk\mu_k of Aˉ\discA via the Möbius map (a fractional linear transformation z(az+b)/(cz+d)z \mapsto (az+b)/(cz+d) of the complex plane):

μk=1+Δ2λk1Δ2λk.\mu_k = \frac{1 + \tfrac{\stepsize}{2}\lambda_k}{1 - \tfrac{\stepsize}{2}\lambda_k}.

Write z=Δ2λkz = \tfrac{\stepsize}{2}\lambda_k with Re(z)<0\operatorname{Re}(z) < 0. Then 1+z2=(1+Rez)2+(Imz)2\abs{1 + z}^2 = (1 + \operatorname{Re}z)^2 + (\operatorname{Im}z)^2 and 1z2=(1Rez)2+(Imz)2\abs{1 - z}^2 = (1 - \operatorname{Re}z)^2 + (\operatorname{Im}z)^2. Since Re(z)<0\operatorname{Re}(z) < 0, we have 1+Rez<1Rez\abs{1 + \operatorname{Re}z} < \abs{1 - \operatorname{Re}z}, giving 1+z<1z\abs{1+z} < \abs{1-z} and therefore μk<1\abs{\mu_k} < 1. Geometrically, the bilinear transform maps the left half-plane exactly to the open unit disk — it is the standard conformal map (an angle-preserving holomorphic bijection) between these two stability regions.

Eigenvalue migration from continuous to discrete domain. Left: continuous eigenvalues in the left half-plane (stability region shaded). Right: discrete eigenvalues inside the unit disk after ZOH discretization. Both ZOH and bilinear preserve stability.
Eigenvalue migration from continuous to discrete domain. Left: continuous eigenvalues in the left half-plane (stability region shaded). Right: discrete eigenvalues inside the unit disk after ZOH discretization. Both ZOH and bilinear preserve stability.

Three views of SSM computation

An LTI SSM can be computed in two equivalent ways, enabling the train-fast / infer-cheap trick that makes S4 practical.

Recurrent view

The discrete state update is a direct recurrence computed via lax.scan with O(L)\bigO{\seqlen} sequential steps and O(N)\bigO{\statedim} memory per token. This is the same scan primitive from Week 1, with carry=ht\text{carry} = \statevec_t and input=ut\text{input} = u_t.

Convolutional view

The term “kernel” here means a convolution filter — a fixed sequence of coefficients that defines a weighted combination of nearby inputs. This is the signal processing sense, unrelated to kernel methods in classical ML (support vector machines, Gaussian processes) or operating system kernels.

Expanding the recurrence with h0=0\statevec_0 = 0 gives:

yt=k=0tCAˉkBˉutk+Dut=(Ku)t+Dut,y_t = \sum_{k=0}^{t} \outputmat\,\discA^k\,\discB\,u_{t-k} + \feedmat\,u_t = (K * u)_t + \feedmat\,u_t,

where K=(CBˉ,CAˉBˉ,CAˉ2Bˉ,)K = (\outputmat\discB, \outputmat\discA\discB, \outputmat\discA^2\discB, \ldots) is the SSM kernel. This is a causal convolution (output at time tt depends only on inputs at times t\leq t), computed in O(LlogL)\bigO{\seqlen \log \seqlen} via FFT.

Our implementation computes KK via iterative matrix powers in a lax.scanO(N2L)\bigO{\statedim^2\seqlen}, deliberately naive. Production S4 exploits DPLR (Diagonal Plus Low-Rank) structure to evaluate KK via Cauchy matrices (matrices with entries 1/(xiyj)1/(x_i - y_j), enabling fast structured products) in O(Nlog2N)\bigO{\statedim \log^2 \statedim}.

Proposition 4.6 (Recurrent-convolutional equivalence).

For an LTI SSM (Aˉ,Bˉ,C,D)(\discA, \discB, \outputmat, \feedmat) with h0=0\statevec_0 = 0, the recurrent scan and causal FFT convolution produce identical outputs.

Proof.

By induction. The recurrence gives ht=k=0t1AˉkBˉut1k\statevec_t = \sum_{k=0}^{t-1} \discA^k \discB\,u_{t-1-k}. Left-multiplying by C\outputmat yields the Toeplitz convolution with kernel Kj=CAˉjBˉK_j = \outputmat\discA^j\discB. FFT computes exactly this (with zero-padding to avoid circular artifacts). Verified numerically at atol=104\text{atol} = 10^{-4} in the week 4 test test_recurrent_convolutional_equivalence.

Recurrent-convolutional equivalence. The recurrent scan and FFT convolution produce identical outputs for the same SSM parameters. The absolute difference panel confirms agreement to machine precision (< 10⁻⁴).
Recurrent-convolutional equivalence. The recurrent scan and FFT convolution produce identical outputs for the same SSM parameters. The absolute difference panel confirms agreement to machine precision (< 10⁻⁴).

Mode switching

S4’s practical trick: convolutional mode for training (O(LlogL)\bigO{\seqlen \log \seqlen} parallel) and recurrent mode for inference (O(1)\bigO{1} per token). Both modes share the same parameters; only the computation changes.

The S4 layer in Flax NNX

The S4Layer module wraps the SSM as a Flax NNX module, with parameters split by type for dual-optimizer treatment:

  • SSMParam: A\statemat, B\inputmat, logΔ\log\stepsize — SSM optimizer (Adam, LR 103\leq 10^{-3}, no weight decay)
  • nnx.Param: C\outputmat, D\feedmat, encoder, decoder — backbone optimizer (AdamW, LR =8×104= 8 \times 10^{-4}, WD 0.010.01)

This is the same SSMParam split from Week 2. The week 2 harness works unchanged: the dual optimizer filters SSMParam variables with wrt=SSMParam.

class S4Layer(nnx.Module):
    def __init__(self, d_state=32, *, rngs):
        A, B = make_hippo_legs(d_state)
        self.A = SSMParam(A)
        self.B = SSMParam(B.squeeze())
        self.C = nnx.Param(random.normal(rngs.params(), (d_state,)))
        self.D = nnx.Param(jnp.ones(()))
        self.log_dt = SSMParam(...)  # log-uniform in [log(0.001), log(0.1)]

The S4SeqModel is a drop-in for SimpleSeqModel (Week 2), preserving the (B,L,1)(B,L,1)(B, L, 1) \to (B, L, 1) contract. With H=32H = 32 features and N=32\statedim = 32, the S4SeqModel has ~35K parameters vs. ~2K for SimpleSeqModel.

Post-training eigenvalue analysis

After training, we extract the learned A\statemat matrices and verify stability:

  • Continuous: Re(λk)<0\operatorname{Re}(\lambda_k) < 0 (left half-plane)
  • Discrete: λˉk<1\abs{\bar{\lambda}_k} < 1 (inside unit disk)

The low learning rate on SSMParam keeps eigenvalue drift small. [Official] The two-LR recipe (Adam 103\leq 10^{-3} on SSM params, AdamW on backbone) originates in the S4 paper Gu et al. (2020) and reproduces across S4D, S5, and Mamba reference code.

Week 18 performs full Lyapunov stability analysis of trained SSMs, using the QR-based infrastructure from Week 1.

Eigenvalue dynamics during S4 training. Left: loss curve. Center: continuous eigenvalues (pre- and post-training). Right: discrete eigenvalues with unit-disk stability boundary. The low learning rate on SSMParam keeps eigenvalue drift small, preserving the HiPPO stability structure.
Eigenvalue dynamics during S4 training. Left: loss curve. Center: continuous eigenvalues (pre- and post-training). Right: discrete eigenvalues with unit-disk stability boundary. The low learning rate on SSMParam keeps eigenvalue drift small, preserving the HiPPO stability structure.
S4 vs. Conv1D on the selective copying task (delay = 8 tokens). Conv1D with kernel size 3 cannot bridge the gap; S4's recurrent state propagates information across arbitrary delays. This is the capability gap that motivates the SSM approach.
S4 vs. Conv1D on the selective copying task (delay = 8 tokens). Conv1D with kernel size 3 cannot bridge the gap; S4's recurrent state propagates information across arbitrary delays. This is the capability gap that motivates the SSM approach.

Reflections

What transfers

  • Discretization awareness: The choice of integrator (Euler, ZOH, bilinear) determines training stability — exactly as in numerical PDE solvers.
  • Eigenvalue monitoring: Checking Re(λ)<0\operatorname{Re}(\lambda) < 0 after training is the SSM equivalent of checking CFL conditions.
  • Dual computation modes: The recurrent/convolutional duality is unique to LTI systems and is lost in selective models (Week 7+).

What requires adaptation

  1. A\statemat is learned, not given. In your PhD, the state matrix comes from physics. In S4, HiPPO provides a principled initialization, but training moves A\statemat — possibly off the stability manifold. This is why the ZOH stability proposition only bounds initialization; post-training stability is a Week 18 question.
  2. Diagonalization is not free. Week 5’s S4D simplification abandons the HiPPO theoretical interpretation. The diagonal form reproduces S4’s empirical performance but loses the Legendre-polynomial memory interpretation — a theory-for-speed trade-off with no clean analogue in numerical PDE solvers.

Forward map

  • Week 5: S4D makes A\statemat diagonal, collapsing the O(N2L)\bigO{\statedim^2\seqlen} kernel to O(NL)\bigO{\statedim\seqlen}.
  • Week 6: S5 replaces lax.scan with associative scan for O(logL)\bigO{\log \seqlen} parallel depth.
  • Week 7: Mamba makes B,C,Δ\inputmat, \outputmat, \stepsize input-dependent, breaking the convolutional view.
  • Week 18: Lyapunov analysis of trained S4 layers using Week 1 QR infrastructure.
  • Week 9 (Julia): ZOH, bilinear, and exponential-trapezoidal compared on a forced oscillator, with empirical order-of-accuracy verification in experiments/julia/week09/discretization.jl.
  • Week 20: Stability regions plotted as contour heatmaps in the complex zz plane, experiments/julia/week20/stability_regions.jl.