Stochastic & Neuromorphic Computing  /  Explore  /  Predictive coding & world models

Predictive coding
& world models

A predictive system does not process everything it sees — it predicts the next input and passes on only the part it got wrong. That "prediction error" is the currency of the whole scheme: accurate predictions cost almost nothing, and surprise is what propagates. SC-NeuroCore carries this idea in two forms — an exact linear-Gaussian world model you can filter, smooth, and learn, and a zero-multiplication stochastic realization built for hardware. This page walks both, starting from a filter you can drive below.

The core loop — predict, compare, correct

Every predictive model runs the same two-step loop. First it predicts: given its current belief about a hidden state, it rolls that belief forward one step. Then it corrects: a fresh measurement arrives, the model measures how far off its prediction was, and it nudges its belief toward the evidence — but only in proportion to how much it trusts that evidence over its own prediction. The demo below is exactly that loop, running a Kalman filter on a noisy signal in your browser.

Live — a Kalman filter tracking a noisy signal
true state noisy measurement filter estimate
RMSE, raw measurement vs truth: RMSE, filter estimate vs truth: error reduction:

Raise the measurement noise and the orange dots scatter further from the true grey line, but the blue estimate stays close — the filter leans harder on its own prediction the noisier the sensor gets. Raise the process noise and the filter trusts measurements more and reacts faster, at the cost of following the jitter. There is no free lunch, only a principled trade the filter makes for you. The identical filter equations run in the library; this canvas renders one instance of them.

$$\underbrace{\hat x_{t\mid t-1}=A\,\hat x_{t-1}, \quad P_{t\mid t-1}=A\,P_{t-1}A^{\top}+Q}_{\text{predict}} \qquad \underbrace{\hat x_{t}=\hat x_{t\mid t-1}+K_t\big(z_t-C\,\hat x_{t\mid t-1}\big), \quad P_t=(I-K_tC)\,P_{t\mid t-1}}_{\text{correct}}$$
$$K_t = P_{t\mid t-1}\,C^{\top}S_t^{-1}, \qquad S_t = C\,P_{t\mid t-1}\,C^{\top}+R \qquad\text{(the gain: how far to move toward the measurement)}$$
Two realizations in the toolkit

Predictive coding shows up twice in SC-NeuroCore, at two different levels. One is a numerically exact world model — a validated linear-Gaussian state-space engine for continuous latent state. The other is a hardware-native stochastic form that computes the same prediction-error loop with no multipliers at all. They are complementary, not rivals: the world model is the reference mathematics; the stochastic form is what lowers onto an FPGA.

A  The exact world model — linear-Gaussian state space

The world-model module implements the classical machinery for a linear-Gaussian state-space model in full: the forward Kalman filter for online belief updates, the Rauch–Tung–Striebel smoother for the best hindsight estimate of every past state, and expectation–maximisation to learn the model parameters from data alone. Covariances are kept symmetric and updated in Joseph / Cholesky-stabilised form, so the filter stays numerically well-conditioned rather than drifting into an invalid covariance.

KalmanFilter(model).filter(observations) → RTSSmoother(model).smooth(result) → EMLearner(...).fit(...)
Deeper: what each stage gives you
The filter answers “given everything up to now, where am I?” and returns a log-likelihood as a by-product, which is what scores one model against another. The smoother answers the retrospective question “given the whole record, where was I at every step?” — strictly sharper than the filter because it uses future evidence too, and it also returns the lag-one cross-covariances the learner needs. EM then alternates: an E-step runs filter-then-smoother to fill in the hidden states, and an M-step re-estimates the transition, observation, and noise matrices in closed form. The formulation follows Kalman (1960), Rauch, Tung & Striebel (1965), and the controlled expectation–maximisation of Shumway & Stoffer (1982).

The forward filter carries a native acceleration path — the same one the rest of the toolkit uses — selecting across Mojo, Go, Rust, Julia, and a pure-NumPy fallback, in that availability-aware order. Smoothing and the EM M-step remain NumPy implementations.

B  The zero-multiplication form — XOR is subtraction

On conventional hardware, computing a prediction error means a subtraction and, for the weighting, a multiply. In the stochastic representation neither is needed. When the prediction and the actual input are both stochastic bitstreams, the error is just the XOR of the two — one logic gate per bit — and its magnitude is the popcount of that XOR divided by the stream length, which is a Hamming distance that approximates the absolute difference in the encoded probabilities.

$$\varepsilon = \mathrm{XOR}(\hat a,\,a), \qquad \|\varepsilon\| = \tfrac{1}{L}\,\mathrm{popcount}(\varepsilon) \;\approx\; \big|\,p_{\hat a}-p_a\,\big|$$

The precision (which errors matter) is updated by the same spike-timing rule the spiking pages use, pushing weights toward the actual input. The whole error-and-update path becomes XOR gates and a popcount tree — no DSP block, no multiplier, no carry chain. It is explored in the toolkit as a stated conjecture (C9), not a closed theorem: the mapping is exact for the gate-level identity, and the quality of the probability approximation is what is studied empirically.

Deeper: why XOR behaves like a difference
For two independent stochastic bitstreams whose 1-densities encode $p_{\hat a}$ and $p_a$, the probability that their bits disagree at a given position is $p_{\hat a}(1-p_a)+p_a(1-p_{\hat a})$, which equals $|p_{\hat a}-p_a|$ when the two are perfectly correlated and otherwise sits above it. So popcount-over-length is an upper-biased estimate of the true difference whose behaviour depends on stream correlation — the reason C9 is framed as a conjecture with an empirically characterised error rather than an exact equality. The gate-level claim (one XOR per bit, one popcount tree) is exact; the approximation quality is the researched part.
A learnable spike predictor — prediction as compression

The same predict-then-correct idea powers a codec. A small autoregressive spike predictor forecasts the next multi-channel spike pattern from recent history and learns online, one sample at a time, by least-mean-squares — no gradients, no batches. The encoder transmits only the XOR between prediction and reality; because the decoder runs an identical predictor over the same recovered history, both sides stay in lockstep and the round-trip is exact.

$$\hat s_t = \big[\,W\,h_t + b > \tfrac12\,\big], \qquad e_t = s_t \oplus \hat s_t, \qquad W \mathrel{+}= \eta\,(s_t-\sigma(Wh_t+b))\,h_t^{\top}$$

Here $h_t$ is the flattened recent spike history and the update is the LMS rule applied after each timestep. Determinism is the point: the same history yields the same prediction yields a lossless reconstruction, which is what lets predictive coding double as a spike-codec front-end rather than only a model of perception.

Where it fits

Predictive coding is the bridge between perception and control: a model that can predict its next input can also imagine the consequence of an action, which is what a planner needs. The world-model module exposes exactly that forecasting facade — roll the latent state forward under a sequence of actions, with or without its growing uncertainty — so the same mathematics that filters a sensor stream can score a plan.

Evidence boundary: the world-model module is tier research (experimental). The demo above runs a genuine constant-velocity Kalman filter in your browser to illustrate the predict–correct loop; it is an illustration of the equations, not a benchmark. The zero-multiplication form (C9) is a stated conjecture whose gate-level mapping is exact and whose probability-approximation quality is characterised empirically — not a proven identity. No throughput or latency figures are quoted here.