Recurrent State-Space Models

Michael BrenndoerferJune 25, 202664 min read

Part of World Models Handbook

Explains how recurrent state-space models combine deterministic memory with stochastic latents for filtering and imagination, and train them with the ELBO.

Choose your expertise level to adjust how many terms are explained. Beginners see more tooltips, experts see fewer to maintain reading flow. Hover over underlined terms for instant definitions.

Article links

Make inline references clickable

Recurrent State-Space Models

Watch a soccer ball roll behind a sofa. You can often predict where it will reappear, provided nothing hidden changes its motion. That familiar inference suggests a concrete engineering problem: maintain an internal state that keeps evolving when observations stop. A robot cannot pause its predictions whenever a camera is occluded. It must project forward from the information it has, while retaining uncertainty about what happens out of sight. In this chapter we build a small model that filters noisy observations and then predicts without seeing the future. We will also check where that model falls short.

In Part II: Inference, Decisions, and Control you met the classical answer, the Bayesian filter. A Kalman filter represents a Gaussian belief by its mean and covariance. With known linear transition and observation models, a Gaussian initial belief, and independent Gaussian process and measurement noise, its updates give the exact state posterior and minimum-mean-squared-error state estimate. Outside those conditions, ordinary Kalman updates generally cease to be exact Bayesian inference, although optimal linear estimation and other tractable special cases remain possible. In Part III: Representing Agents and Worlds you saw learned latent representations. The recurrent state-space model (RSSM), used by PlaNet and the Dreamer family, combines learned recurrent memory with stochastic latent dynamics.

An RSSM keeps two components of internal state. A deterministic recurrent state hth_t, carried by a GRU or LSTM, provides memory. Deterministic describes its update given its inputs; it does not mean that the model is certain about the world. A stochastic latent ztz_t is sampled from a conditional distribution. During filtering, a posterior network uses the current observation to infer that distribution. A prior network predicts it without the current observation, using past information through hth_t. A KL divergence encourages the prior to match the observation-conditioned posterior and regularizes how much that posterior can differ. During acting, the agent can still filter its current observation; only its imagined future lacks future observations and therefore uses the prior alone.

The filtering/imagination distinction is the central operating boundary in the chapter, not a train/test split. Both regimes can be evaluated on held-out data, and filtering also occurs during deployment. The inference model asks, "what latent states explain what I have just observed?" The dynamics model asks, "what latent states should I predict before looking?" Training encourages consistency between them, but it cannot make unavailable future information available. A good predictive prior can therefore differ from the posterior even when neither is defective.

Hidden State: What Deterministic and Stochastic Each Buy You

This section builds the two ingredients separately before combining them. PlaNet's experiments motivate the combination, but neither ingredient is a universal requirement for a capable world model. We need to distinguish how a state is computed from what distribution its outputs can represent.

The problem a hidden state solves

Recall from Bayesian Filtering and Belief States that an observation-action history contains the information available for inferring hidden state. Storing that raw history costs more as an episode grows. Some observations are redundant, while repeated noisy measurements can still help reduce uncertainty. A belief bt=p(st∣o≤t,a<t)b_t = p(s_t \mid o_{\le t}, a_{<t}) is the distribution of hidden environment state sts_t given observations through tt and earlier actions. It is a sufficient summary for the usual known-model POMDP prediction and decision problem, but a general belief distribution need not have an exact finite-dimensional representation. Classical filters exploit tractable special families. A learned world model instead fits a compact representation intended to retain the information its prediction tasks need.

There are two computational choices to separate:

  • A deterministic hidden vector is a fixed function of its inputs and parameters. It can store point estimates, probabilities, variances, or other learned features.
  • A stochastic latent is a random variable. Its distribution represents modeled alternatives; one sample is only one realization, not the distribution itself.

A Kalman filter is a useful counterexample to the idea that deterministic memory means certainty: its mean and covariance are updated deterministically, yet jointly describe an uncertain state. Likewise, a neural vector can encode the weights, means, and scales of a mixture distribution. The RSSM chooses a different implementation: recurrent memory plus sampled latent states. That choice can help retain information and generate alternative trajectories, but it does not by itself guarantee good inference or calibrated uncertainty.

Deterministic hidden state

A simple learned filter is a recurrent neural network that ingests observations and actions and maintains a fixed-size vector:

ht=fθ(ht−1,at−1,ot)h_t = f_\theta(h_{t-1}, a_{t-1}, o_t)

where:

  • hth_t: the learned hidden state at time tt, a fixed-size vector that summarizes the observation and action history
  • fθf_\theta: the learned recurrence, for instance a GRU or LSTM cell, with parameters θ\theta
  • at−1a_{t-1}: the action taken at the previous step
  • oto_t: the observation arriving at the current step

A related recurrent-memory pattern appears in Ha and Schmidhuber's World Models (2018). Its LSTM consumes VAE codes and actions and outputs a mixture distribution, not a point prediction. In our simpler recurrence, hth_t is a learned predictive summary, not a guaranteed sufficient statistic. A fixed-width representation puts pressure on the network to retain task-relevant information; it may also forget information that later turns out to matter. The prediction objective determines what is rewarded, and held-out tests tell us whether that training signal was enough.

Deterministic state has real strengths:

  • Repeatable updates. With fixed inputs, initialization, parameters, and deterministic numerical execution, the same recurrence returns the same state. This removes latent-sampling variance from that update, not representation or floating-point error.
  • Direct differentiation. Ordinary backpropagation follows the recurrence without a fresh sampling operation on each edge. Gradients can still vanish or explode.
  • Simple inference. The point-output baseline below needs one recurrent pass and no variational posterior.

The limitation appears when we attach a single point output and train it with mean squared error. If the next observation has two plausible modes, that output cannot report both. Conditional squared risk decomposes into the irreducible conditional variance plus the squared distance from the conditional mean, so the MSE-optimal point forecast is the mean. For a balanced symmetric bimodal distribution, that mean lies between the modes. A forecast of a ball on the left or right of a pillar can therefore average into a low-probability location in the middle. The cause is the point-output objective, not deterministic recurrent memory: a distributional head can report both alternatives.

The failure is easiest to see with numbers rather than words, so we make it concrete before moving on.

In[3]:
Code
import numpy as np

## Bimodal belief over the ball's next position: left of the pillar or right.
try:
    _trapz = np.trapezoid  # NumPy >= 2.0
except AttributeError:
    _trapz = np.trapz  # NumPy 1.x
mixture_x = np.linspace(-3.0, 3.0, 400)
mode_centers = np.array([-1.5, 1.5])
mode_std = 0.45
dens = 0.5 * (
    np.exp(-0.5 * ((mixture_x - mode_centers[0]) / mode_std) ** 2)
    + np.exp(-0.5 * ((mixture_x - mode_centers[1]) / mode_std) ** 2)
)
dens /= _trapz(dens, mixture_x)

## The MSE-optimal deterministic forecast is the conditional mean, which for
## this symmetric mixture sits exactly between the two modes.
cond_mean = _trapz(mixture_x * dens, mixture_x)
mean_gap_to_modes = float(np.min(np.abs(mode_centers - cond_mean)))
Out[4]:
Console
conditional mean = -0.000 (midway between modes)
distance from conditional mean to the nearest mode: 1.50
density at the conditional mean: 0.0034
peak density: 0.4434
Out[5]:
Visualization
Bimodal density curve with a marker at the low-density conditional mean.
Distribution of a bimodal belief over the ball's next position, with the MSE-optimal deterministic forecast marked at the conditional mean. The forecast lands in the low-probability valley between the two modes, a low-probability location between the plausible modes.

The plot illustrates mode averaging: the conditional mean lies in the low-density valley between the modes, not at the global minimum of the density. Enlarging a model while retaining an MSE point-output objective does not change the optimum into two alternatives. Changing the output distribution or the prediction objective addresses a different problem than adding hidden-state capacity.

A point output also leaves uncertainty unreported. Consider two grasps with the same predicted mean outcome but different failure risks. A planner receiving only those mean outcomes cannot distinguish them by uncertainty. Risk-sensitive decisions or information gathering may need a distribution, quantiles, or other uncertainty-sensitive information. These can come from deterministic memory with a probabilistic head, from ensembles, or from stochastic latent rollouts. A point forecast can still be useful for decisions; it simply does not answer every decision question.

Stochastic hidden state

One way to generate alternatives is to make part of the latent state stochastic. Instead of reporting only a point, the model predicts a distribution and samples from it. One trajectory may go left and another right if its learned distribution represents both outcomes. Sampling represents what the model believes, not a certificate that the belief is correct. Calibration must be checked against held-out outcomes.

Stochastic latent sequence models precede RSSMs: variational RNNs (Chung et al., 2015), stochastic RNNs (Fraccaro et al., 2016), deep variational Bayes filters (Karl et al., 2017), and structured inference networks (Krishnan et al., 2017) develop related latent-state and inference ideas. In these models, distinguish the approximate latent distribution from a sample drawn from it. For an RSSM, the joint distribution of (ht,zt)(h_t,z_t) matters: hth_t varies across previously sampled latent histories, even though each recurrent update is deterministic conditional on its inputs.

Stochastic recurrence can make retaining information and optimizing long sequences difficult. PlaNet reported that its combined recurrent and stochastic state performed substantially better than the particular single-path variants it tested. That result does not establish a universal law that each additional sample increases gradient variance. For example, in zt=zt−1+θ+ϵtz_t=z_{t-1}+\theta+\epsilon_t with a terminal linear loss zTz_T, the pathwise derivative is exactly TT for every noise draw. Actual gradient variance depends on the transition, objective, and estimator. The RSSM's deterministic path offers a route for recurrent information without requiring a fresh sample on every edge.

The learned state is not the environment's state

The hidden states hth_t and ztz_t are model-internal quantities. Nothing forces them to correspond one-to-one to the environment's true state variables, and in general they will not. What matters is the notion of sufficiency you met in Sufficient State and State Abstraction: the learned state must contain enough information to predict the observations, rewards, and dynamics the agent cares about. In the worked example below, the true state is (position, velocity), but the learned latent may encode position in some nonlinear combination with velocity, and that is fine as long as predictions are good.

The Recurrent State-Space Model

This section assembles the architecture, name by name, and explains the wiring that makes the posterior and prior cooperate. Read it slowly: every design choice here has a reason, and the reasons compound.

The five components

An RSSM for a partially observable environment with observations oto_t and actions ata_t consists of:

  • Deterministic recurrence (the memory path). A GRU (or LSTM) updates hth_t from the previous deterministic state, the previous sampled latent, and the previous action:

    ht=fθ(ht−1, zt−1, at−1)h_t = f_\theta\big(h_{t-1},\, z_{t-1},\, a_{t-1}\big)
  • Prior (transition model). Before seeing anything at time tt, the model guesses the stochastic state:

    zt∼pθ(zt∣ht)=N(μθ(ht), diag(σθ2(ht)))z_t \sim p_\theta(z_t \mid h_t) = \mathcal{N}\big(\mu_\theta(h_t),\ \mathrm{diag}(\sigma^2_\theta(h_t))\big)
  • Posterior (inference model). During training and filtering, an encoder sees the current observation and re-places ztz_t where the evidence says it should be:

    zt∼qϕ(zt∣ht,ot)=N(μϕ(ht,ot), diag(σϕ2(ht,ot)))z_t \sim q_\phi(z_t \mid h_t, o_t) = \mathcal{N}\big(\mu_\phi(h_t, o_t),\ \mathrm{diag}(\sigma^2_\phi(h_t, o_t))\big)
  • Observation model (decoder). Reconstructs or predicts the observation from the joint state:

    ot∼pθ(ot∣ht,zt)o_t \sim p_\theta(o_t \mid h_t, z_t)
  • Optional heads. In the Dreamer line, a reward head pθ(rt∣ht,zt)p_\theta(r_t \mid h_t, z_t) and a discount or continuation head for episode boundaries (the original Dreamer already modeled early termination). We will use them in Part VII: Planning and Agency; here we focus on the observation model.

where:

  • fθf_\theta: the GRU or LSTM update, a neural network with parameters θ\theta applied identically at every step
  • μθ(ht)\mu_\theta(h_t), σθ2(ht)\sigma^2_\theta(h_t): the prior's mean vector and per-dimension variances, produced from hth_t by a small network head
  • μϕ(ht,ot)\mu_\phi(h_t, o_t), σϕ2(ht,ot)\sigma^2_\phi(h_t, o_t): the posterior's mean vector and per-dimension variances, produced from the pair (ht,ot)(h_t, o_t) by the encoder with parameters ϕ\phi
  • diag(⋅)\mathrm{diag}(\cdot): a covariance matrix carrying the given variance vector on its diagonal and zeros elsewhere, so the Gaussian treats the coordinates as independent conditional on its inputs
  • rtr_t: the reward at time tt

Notice the timing convention, because it trips everyone up at least once. The GRU input at time tt is (zt−1,at−1)(z_{t-1}, a_{t-1}), not ztz_t. So hth_t is the model's prediction of the situation at time tt before the observation oto_t arrives. The observation never touches hth_t directly; it enters the model only through the posterior, which shapes ztz_t, which enters the recurrence at the next step. All per-step information about the current observation must therefore flow through the stochastic latent. This is deliberate, and it is what makes the KL term meaningful, as you will see below. If the observation could leak into hth_t by some side channel, the prior itself could use the observation it is supposed to predict, and the KL would no longer measure a real prediction gap. By forcing all observation information through the sampled bottleneck ztz_t, the architecture guarantees that the only way the model can use the present is by encoding it in exactly the place the prior is trying to predict.

Prior versus posterior

The prior pθ(zt∣ht)p_\theta(z_t \mid h_t) is the transition model: what the model expects before it looks. It exists at all times, including during imagination. The posterior qϕ(zt∣ht,ot)q_\phi(z_t \mid h_t, o_t) is the inference model: an approximate latent distribution after incorporating the evidence. It exists only when an observation is available, such as during training and filtering. The two have the same functional form but different inputs and different jobs.

Assumptions of the RSSM

Separate the modeled structure from assumptions about its adequacy:

  • The generative model is Markov in its joint internal state. Whether the learned representation retains enough history for the environment's prediction tasks must be tested, not assumed from its fixed width.
  • This example uses stationary dynamics and shared parameters across time. Other settings may require time or regime information in the state or inputs.
  • History and actions must contain enough information for the task. Relevant unobservable quantities require a belief over alternatives, not a promise of exact recovery; task-irrelevant hidden variables need not be recovered.
  • Recorded actions are available during training and evaluation. The model is not asked to infer which actions occurred.

The figure below shows the wiring unrolled over three steps. Read the middle column: the prior flows down from hth_t into ztz_t; the posterior flows up from oto_t into ztz_t; the decoder emits a predicted observation from (ht,zt)(h_t, z_t); and the GRU carries (ht−1,zt−1,at−1)(h_{t-1}, z_{t-1}, a_{t-1}) into hth_t. The third column is drawn faded deliberately: it is imagination, where the posterior arrows do not exist and the prior must carry the sequence on its own.

Out[6]:
Visualization
Diagram of RSSM boxes and arrows for prior, posterior, decoder, and GRU across three timesteps.
Schematic RSSM dependencies across three steps. Solid arrows show recurrent, prior, and decoder paths; dashed arrows show observation-conditioned inference and disappear in the imagined third step. For readability the extra h inputs to the posterior and decoder are written in their conditional-distribution labels rather than drawn as separate arrows. Both must be included in an implementation.

Why keep both paths

The division of labor between hh and zz is the design insight that makes the architecture work, so it deserves a careful statement.

The deterministic path provides a direct recurrent route for information and gradients. With differentiable gates, backpropagation through a GRU is an ordinary chain-rule calculation conditional on its inputs. This can help preserve information that would be difficult to carry through repeated stochastic transitions. It does not guarantee small gradient variance or stable optimization: the inputs still include sampled latents, and the recurrent Jacobians can attenuate or amplify gradients. PlaNet's ablation is evidence about its tested architecture and tasks, rather than a proof that stochastic-only models cannot learn.

The stochastic path supplies sampled latent variation. In the graph defined here, oto_t enters the joint state through the posterior distribution for ztz_t, then influences later memory through the recurrence. The KL compares distributions, not two sampled values: even identical distributions can generate different samples, and an observation can change posterior variance without changing its mean. Uncertainty can also reside in distribution parameters carried by memory, in the observation model, and in uncertainty about fitted parameters. No single latent draw contains all of those uncertainties.

The Kalman analogy concerns the order of computation: predict a state distribution, then update it using an observation. A Kalman filter represents uncertainty with both a mean and a covariance, and its innovation is an observation residual, not a KL divergence. Under the linear-Gaussian assumptions its update is exact; an RSSM uses learned nonlinear dynamics and approximate inference. Comparing filtering with imagination can help locate a failure, but does not isolate one guilty component: the encoder, transition, decoder, and available information all influence the observed errors.

DreamerV2 (Hafner et al., 2021) uses 32 categorical variables with 32 classes each, sampled with straight-through gradients; DreamerV3 retains categorical stochastic latents. Categoricals provide discrete alternatives in latent coordinates. A diagonal Gaussian is unimodal conditional on its context in those coordinates, but a nonlinear decoder can still turn Gaussian latents into multimodal or correlated observations. The high-level prior/posterior/recurrence graph remains recognizable across the variants, while the distribution parameterization, KL calculation, and gradient estimator change. Straight-through differentiation is a biased surrogate; its practical usefulness does not imply that score-function estimators inevitably have exploding variance.

Learning the Model: Posterior Inference and the ELBO

You now know what the parts are. This section explains how they are trained, and why the training objective has the peculiar two-part structure it does.

The filtering ELBO

Consider a single training sequence of observations o1:To_{1:T} and actions a1:Ta_{1:T}. The generative story is:

  1. Start from an initial belief (h1h_1 can be a learned vector or fixed zeros, with the initial latent prior conditioned on it).
  2. For each tt: draw ztz_t from the prior pθ(zt∣ht)p_\theta(z_t \mid h_t), emit oto_t from pθ(ot∣ht,zt)p_\theta(o_t \mid h_t, z_t), then update ht+1=fθ(ht,zt,at)h_{t+1} = f_\theta(h_t, z_t, a_t).

The action-conditioned likelihood pθ(o1:T∣a1:T−1)p_\theta(o_{1:T}\mid a_{1:T-1}) integrates over latent trajectories. For a general nonlinear neural model, that sum or integral is intractable; linear-Gaussian models are an important tractable exception. We use the filtering variational factorization q(z1:T∣o1:T,a1:T−1)=∏tqϕ(zt∣ht,ot)q(z_{1:T}\mid o_{1:T},a_{1:T-1})=\prod_t q_\phi(z_t\mid h_t,o_t), where each hth_t depends on earlier sampled latents. Jensen's inequality gives an evidence lower bound on the log likelihood. Written with all expectations visible, it is

L=∑t=1TEq(z<t) ⁣[Eqϕ(zt∣ht,ot)[log⁡pθ(ot∣ht,zt)]⏟reconstruction−KL ⁣(qϕ(zt∣ht,ot) ∥ pθ(zt∣ht))⏟prior-posterior regularization]\mathcal{L} = \sum_{t=1}^{T}\mathbb{E}_{q(z_{<t})}\!\left[ \underbrace{\mathbb{E}_{q_\phi(z_t\mid h_t,o_t)}[\log p_\theta(o_t\mid h_t,z_t)]}_{\text{reconstruction}} -\underbrace{\mathrm{KL}\!\left(q_\phi(z_t\mid h_t,o_t)\,\|\,p_\theta(z_t\mid h_t)\right)}_{\text{prior-posterior regularization}} \right]

The outer expectation averages over the earlier posterior draws that determine hth_t; at t=1t=1 there are no earlier latents. The inner expectation averages the current reconstruction likelihood. We estimate these expectations with a sampled trajectory, drawing zt=μϕ+σϕ⊙ϵtz_t=\mu_\phi+\sigma_\phi\odot\epsilon_t, where ϵt∼N(0,I)\epsilon_t\sim\mathcal{N}(0,I) is parameter-independent noise and σϕ>0\sigma_\phi>0. This reparameterization exposes a differentiable map from the mean and scale to the sample, so ordinary backpropagation supplies a pathwise Monte Carlo gradient under the usual differentiability and integrability conditions. The expected objective is a lower bound; an individual Monte Carlo estimate is not guaranteed to lie below the log likelihood.

Filtering uses only past and current observations. If a smoothing variational family contains this filtering family, its optimal ELBO cannot be lower, because it can use future observations too. That is a statement about the families' optima, not a promise about two fitted networks. One posterior trajectory is also not a particle filter with importance weighting and resampling. Finally, the decoder log density at a given sample may be analytic while its expectation through a nonlinear decoder is not. The conditional diagonal-Gaussian KL does have the closed form below; its outer expectation over earlier latent history is still estimated by sampling.

KL(q ∥ p)=∑i[log⁡σp,iσq,i+σq,i2+(μq,i−μp,i)22σp,i2−12]\mathrm{KL}(q \,\|\, p) = \sum_{i} \left[ \log \frac{\sigma_{p,i}}{\sigma_{q,i}} + \frac{\sigma_{q,i}^2 + (\mu_{q,i} - \mu_{p,i})^2}{2\sigma_{p,i}^2} - \frac{1}{2} \right]

where:

  • qq: shorthand for the posterior Gaussian qϕ(zt∣ht,ot)q_\phi(z_t \mid h_t, o_t), with mean μq,i\mu_{q,i} and standard deviation σq,i\sigma_{q,i} along latent dimension ii
  • pp: shorthand for the prior Gaussian pθ(zt∣ht)p_\theta(z_t \mid h_t), with mean μp,i\mu_{p,i} and standard deviation σp,i\sigma_{p,i}
  • ii: an index over the latent dimensions, so the sum runs over the coordinates of ztz_t

The three summands divide the work: the log-scale ratio and normalized variance compare their widths, the normalized squared mean gap penalizes mean disagreement, and −1/2-1/2 subtracts the posterior's expected standardized quadratic contribution. The terms cancel jointly: when the two Gaussians coincide the log ratio is zero and the variance term plus the constant −1/2-1/2 sum to zero, so the divergence vanishes exactly when the two Gaussians coincide. This closed form is what the code below implements directly. Notice also the asymmetry of the divergence: because it is the KL with qq first, the prior's standard deviation σp,i\sigma_{p,i} appears in the denominator, so a prior that is too confident (small σp\sigma_p) pays a steep price when the posterior disagrees with it. For distinct prior-head parameters under this posterior-sampled objective, the 1/σp21/\sigma_p^2 denominator punishes prior overconfidence steeply, and the KL is the only term whose gradient reaches the prior distribution's parameters (the mean and scale head of pθ(zt∣ht)p_\theta(z_t \mid h_t); the reconstruction gradient still reaches the deterministic transition fθf_\theta through the sampled ztz_t). Its gradient pulls the posterior toward the prior too. That is the collapse route the stabilizers below are designed to suppress.

The asymmetry is worth seeing as a curve rather than a formula.

In[7]:
Code
import numpy as np

## KL( N(mu_q, 1) || N(mu_p, sigma_p) ) for a unit-width posterior, as the
## posterior mean mu_q moves away from the prior mean mu_p = 0, for several
## prior standard deviations. Fixed seed is not needed: the grid is
## deterministic.
gap = np.linspace(0.0, 3.0, 300)
prior_stds = np.array([0.25, 0.5, 1.0, 2.0])
kl_curves = {}
for sp in prior_stds:
    kl_curves[sp] = np.log(sp) + (1.0 + gap**2) / (2.0 * sp**2) - 0.5
Out[8]:
Console
prior sigma=0.25: KL at gap 1 =  14.11 nats, KL at gap 3 =    78.11 nats
prior sigma=0.50: KL at gap 1 =   2.81 nats, KL at gap 3 =    18.81 nats
prior sigma=1.00: KL at gap 1 =   0.50 nats, KL at gap 3 =     4.50 nats
prior sigma=2.00: KL at gap 1 =   0.44 nats, KL at gap 3 =     1.44 nats
Out[9]:
Visualization
Rising curves of KL versus mean gap for four prior widths.
KL divergence against the posterior-prior mean gap for priors of different width, with the posterior held at unit variance. An overconfident prior (small sigma) pays a steeply growing penalty for the same disagreement, while a wider prior has a smaller mean-mismatch penalty over this range but still pays for variance mismatch.

Read the vertical gap between the curves: at a mean gap of one, the narrowest prior already pays about 13.7 nats more than the widest one (14.11 versus 0.44) for the identical posterior. The KL direction therefore punishes prior overconfidence, which is exactly the behavior a dynamics-learning signal should have.

What each term teaches

The two terms have distinct jobs. Their traces can suggest where to investigate a broken training run, but do not uniquely identify a failing component.

The reconstruction term trains the posterior, decoder, and recurrent transition to make observations predictable. It encourages the posterior to encode current information that helps the decoder, but does not force use of every latent dimension. The current reconstruction has no dependency on ht+1h_{t+1}: it is later losses that reward retaining useful information there. In this observation-only example, reconstruction is the direct incentive to encode observations; an agent with reward or other auxiliary heads would have additional incentives.

The KL term trains the prior and regularizes the posterior. Either distribution can move to reduce it. Its initial value and subsequent trend depend on both networks, so neither a large starting KL nor a monotonic decline is guaranteed. Residual KL is not sensor-noise variance. In a scalar exact Gaussian update with prior variance PP and measurement variance RR, expected information gain is 12log⁡(1+P/R)\tfrac12\log(1+P/R), while innovation variance is P+RP+R. Increasing sensor noise can therefore decrease expected information gain while increasing innovation variance. Nats and squared observation units describe different quantities.

Posterior collapse, free nats, and KL balancing

Posterior collapse

Posterior collapse means the posterior stops using observation information beyond what the prior already supplies. Exact equality q(zt∣ht,ot)=p(zt∣ht)q(z_t\mid h_t,o_t)=p(z_t\mid h_t) gives zero conditional KL, but zero KL alone does not prove useless prediction: the history may already explain the relevant observation. Collapse is harmful when the latent drops information needed for the task. Check observation dependence and prediction quality, rather than treating a low scalar KL as a complete diagnosis.

Collapse happens when the KL cost of encoding information exceeds the reconstruction benefit of using it. The ELBO literature developed several remedies, and the RSSM line uses two that you should know by name.

Free nats replace the per-step KL by max⁡(KL,ϵfree)\max(\mathrm{KL},\epsilon_{\mathrm{free}}). PlaNet and the original Dreamer use 3 nats; the cited DreamerV3 paper uses 1 nat. Below the threshold, this term is constant and supplies no gradient through either KL argument. It removes marginal regularization pressure in that region; it does not measure irreducible surprise or guarantee that the posterior uses the available capacity. A collapsed posterior equal to its prior also has a clipped value at the floor. We use a 1-nat threshold below and log both raw and clipped KL to keep these cases distinguishable. Reconstruction and shared upstream parameters can still change the distributions while the clipped KL's own gradient is zero.

KL balancing (DreamerV2, kept in DreamerV3): split the KL gradient by stopping gradients through one argument at a time:

Ldyn=λdyn KL(sg[q] ∥ p),Lrep=λrep KL(q ∥ sg[p])\mathcal{L}_{\mathrm{dyn}} = \lambda_{\mathrm{dyn}}\, \mathrm{KL}\big(\mathrm{sg}[q] \,\big\|\, p\big), \qquad \mathcal{L}_{\mathrm{rep}} = \lambda_{\mathrm{rep}}\, \mathrm{KL}\big(q \,\big\|\, \mathrm{sg}[p]\big)

where:

  • qq and pp: shorthand for the per-step posterior qϕ(zt∣ht,ot)q_\phi(z_t \mid h_t, o_t) and prior pθ(zt∣ht)p_\theta(z_t \mid h_t)
  • sg[⋅]\mathrm{sg}[\cdot]: stop-gradient, meaning the bracketed distribution is held fixed, so that term's gradient can only reach the other argument
  • λdyn\lambda_{\mathrm{dyn}} and λrep\lambda_{\mathrm{rep}}: the loss weights. DreamerV2 introduced the balance with the factor α=0.8\alpha = 0.8, weighting the dynamics term four times the representation term; the DreamerV3 paper v2 applies loss scales of λdyn=1.0\lambda_{\mathrm{dyn}} = 1.0 and λrep=0.1\lambda_{\mathrm{rep}} = 0.1 to the two terms

Both terms are minimized alongside the negative reconstruction objective. The first differentiates through the prior argument with the posterior argument stopped; the second reverses those roles. Shared upstream parameters, including the recurrent context, may influence both distributions after an update. Larger dynamics coefficients emphasize direct prior-fitting gradients, but coefficient ratios are not gradient-norm ratios and do not guarantee an optimizer's outcome. The aim is to reduce the incentive to discard useful observation information merely to make matching easier. DreamerV3 reports results across its evaluated domains with a shared configuration and several coordinated design choices; that empirical result should not be attributed to KL balancing alone.

Loss units matter for both images and low-dimensional observations. A sum over many pixels differs from a mean over pixels, and the observation likelihood's scale determines reconstruction strength. For a fixed scalar Gaussian decoder variance σo2\sigma_o^2, negative log likelihood equals squared error divided by 2σo22\sigma_o^2, plus a parameter-independent constant. Thus squared error plus β KL\beta\,\mathrm{KL} is, up to positive scaling and an additive constant, a Gaussian negative-ELBO objective with σo2=β/2\sigma_o^2=\beta/2. Here β=0.1\beta=0.1 corresponds to variance 0.050.05 before clipping. Free-nat clipping modifies that objective; a learned variance would also introduce scale-dependent terms. Scalar loss magnitudes alone do not establish gradient dominance, especially when a large clipped term is locally constant.

The posterior-prior gap

During filtering, the posterior uses the currently available observation; during imagination, future states come from the prior. At fixed recurrent context hh, averaging KL(q(z∣h,o)∥p(z∣h))\mathrm{KL}(q(z\mid h,o)\|p(z\mid h)) over a specified observation distribution gives two terms: the mutual information between zz and oo under that joint distribution, plus the KL between the observation-averaged posterior and the prior. Consequently, expected KL upper-bounds that observation information but is not equal to it in general. It mixes useful or irrelevant encoded information with prior mismatch; it is not a direct measure of forecast error.

Keep observation-conditioned filtering and open-loop prediction separate when evaluating a model. The experiment below measures both; it does not train a policy or measure closed-loop decision usefulness. A good posterior reconstruction can coexist with poor imagination because observations supply information that an open-loop forecast lacks. Approximation and training failures can widen that difference too. A high held-out KL is a reason to investigate, not proof of a failed prior: check predictions, interval quality, and which observations change the inferred state.

Multi-Step Latent Rollout

Once trained, the model is used by imagining: rolling the latent state forward under actions, without observations. This section explains how rollouts work, when their error can grow, and what keeps them useful.

How imagination works

A rollout has two phases.

Burn-in. Run posterior updates over an observed prefix to initialize an approximate filtered belief. A known initial environment state can instead inform a suitable initializer; an unconditional constant is not a substitute for arbitrary known states. How much history is needed depends on observability and the model. To represent uncertainty over the installed belief, run several independently sampled posterior histories through the same observations and actions. Repeating one sampled endpoint would condition away the differences between those histories.

Imagination. From the installed belief, iterate the dynamics with the prior only, conditioned on a candidate action sequence:

ht+k=fθ(ht+k−1,zt+k−1,at+k−1),zt+k∼pθ(zt+k∣ht+k)h_{t+k} = f_\theta(h_{t+k-1}, z_{t+k-1}, a_{t+k-1}), \qquad z_{t+k} \sim p_\theta(z_{t+k} \mid h_{t+k})

where:

  • kk: the number of steps beyond the last observed time tt; the pair of updates is iterated for k=1,2,…,Hk = 1, 2, \ldots, H over the imagination horizon HH
  • at+k−1a_{t+k-1}: the candidate action supplied by the planner at step t+k−1t+k-1
  • pθ(zt+k∣ht+k)p_\theta(z_{t+k} \mid h_{t+k}): the prior, now the only source of the stochastic state because no observation arrives to correct it

The two updates play the same roles as during training: the GRU refreshes the memory from the previous belief and action, and the prior samples a fresh uncertain state. The agent then decodes whatever it needs from (ht+k,zt+k)(h_{t+k}, z_{t+k}) — typically only the reward inside the planning loop, with observations decoded for inspection or evaluation rather than per candidate plan.

Two properties make latent imagination useful in PlaNet and Dreamer. Their action evaluation or actor training can operate through latent reward and value predictions without decoding images at every step. This avoids the image decoder's computation in those implementations. PlaNet evaluates candidate action sequences with a planner; Dreamer instead learns an actor through imagined trajectories and value bootstrapping. A single sampled rollout is one trajectory. Repeated independent posterior and prior noise draws approximate the model's conditional distribution of futures, which still needs validation. We will build the decision-making machinery in Part VII: Planning and Agency.

Why rollout error compounds

In System Identification, you saw why evaluating a one-step model recursively is a different test from predicting with fresh observations. An imperfect prediction can become an input to the next transition and affect later errors. Stable dynamics may attenuate that error; unstable dynamics may amplify it. Neither error growth nor monotonic widening is guaranteed merely by rolling a model forward.

Two contributions are useful to distinguish when interpreting forecast error:

  • True conditional variability. Even a perfect conditional-mean predictor makes errors against individual stochastic futures. Process noise and uncertainty left after filtering affect both point forecasts and sampled forecasts. For a stable linear system, predictive covariance can approach a finite stationary level rather than grow forever.
  • Model and inference error. Misspecified dynamics, imperfect optimization, a poor initial belief, or evaluation outside the training distribution can add error. Uncertainty about fitted parameters is one part of this problem, often called epistemic uncertainty; it is not a synonym for every approximation error. Recursion may amplify or attenuate these defects.

Accurately represented environmental variability should not be removed just to make intervals narrower. An inaccurate model may instead need better data coverage, inference, or dynamics fitting; shorter rollouts reduce reliance on extrapolation without necessarily fixing it. A point-output model has no interval, but that absence is not a declaration of confidence. Conversely, drawing a broad stochastic ensemble does not establish that the model knows its errors.

Use matched reference predictions when the environment is known. Below we implement the exact linear-Gaussian filter and its open-loop conditional mean and variance under the same observed prefix and actions. This gives a reference for irreducible squared error rather than comparing a random forecast sample with a conditional mean. We also measure interval coverage and width. Empirical 95% coverage at one horizon is only a marginal diagnostic: an overly wide interval can buy coverage, biases can be hidden across subgroups, and finite validation sets give noisy proportions. Neither coverage nor an RMSE curve alone decomposes model error from process noise.

What keeps rollouts usable

Useful tools for managing rollout degradation include:

  • Short horizons. PlaNet's reported setup plans 12 steps; the original Dreamer imagines 15 steps and bootstraps values beyond that window. PlaNet replans as observations arrive, while Dreamer acts through a learned policy conditioned on an updated latent state.
  • Free nats and KL balancing. These adjust information regularization and gradient allocation. Free nats explicitly stop KL pressure below a threshold; neither technique guarantees a smaller prediction gap.
  • Latent overshooting. PlaNet compares multistep prior predictions with posteriors inferred from real observations, adding direct multistep consistency pressure beyond one-step training.
  • Stochastic latents. Sampling can retain modeled alternative futures instead of feeding back an intermodal mean. A sample lies in the learned prior's support, not necessarily in a well-trained region or in the true environment's support.
  • Limited planner pressure. Optimizing strongly against an imperfect model can exploit its errors. Shorter horizons and uncertainty-sensitive objectives are possible defenses, not guarantees; Part XII: Reliable World Models develops this issue.

Evaluate the operating regimes separately. Posterior-pass error and prior-only rollout error depend on several components, but they ask different questions about access to observations. Reporting only posterior reconstruction can conceal open-loop weaknesses because each fresh observation offers a chance to correct the model. A point-error curve alone cannot assess distribution quality.

Worked Example: Inferring a Velocity You Never See

We now build an RSSM for a small controlled linear-Gaussian environment. The experiment exposes true hidden states and the data-generating equations, so we can check filtering, open-loop forecasts, and uncertainty against a known-model reference. It does not illustrate every difficulty of world modeling: in particular, this environment's conditional futures are Gaussian, not multimodal. Training is CPU-sized; runtime depends on hardware and numerical libraries.

The environment is a discrete spring-damper system with position and velocity as its two state variables. Position is observed with sensor noise; velocity is never an input or target during world-model training. Recorded force actions are also supplied. Position, velocity, actions, and process noise jointly determine future motion. With the known model parameters and Gaussian initial distribution, a Kalman filter gives the exact posterior. The position observation map is C=[1,0]C=[1,0], and the two-step observability matrix has rows CC and CA=[1,0.1]CA=[1,0.1], so its determinant is 0.10.1: position history contains information about velocity, even though noise prevents exact recovery.

The experiment asks four questions, each mapping to a section of the chapter:

  1. Can the model filter? Score posterior decoder means against clean positions, alongside raw-observation echo, a memoryless shrinkage estimate, and a known-model Kalman filter. Beating echo shows lower estimation error, not by itself use of history.
  2. Can the model imagine? Observe 10 positions, then predict 40 steps under the recorded future actions. Compare the RSSM ensemble mean with the deterministic baseline and the Kalman conditional mean using the same point-RMSE calculation.
  3. How informative are its intervals? Score central sample-quantile intervals against clean future positions, report width and sampling uncertainty, and compare with exact Kalman intervals. These are diagnostics, not a proof of full calibration.
  4. What information is recoverable? Fit a linear probe on training representations and test it on held-out sequences. A high velocity R2R^2 supports linear decodability; it does not prove an identifiable physical coordinate, causal use, or Markov sufficiency.

We retain a deterministic GRU with a point decoder as an illustrative baseline. The RSSM has 20,625 parameters and 1,500 updates; the baseline has 10,689 parameters and 800 updates, and their objectives and observation timing differ. This is not a controlled ablation isolating stochastic state. The shared evaluation cutoff, actions, and point-error metric make the resulting forecasts comparable, but cannot attribute their differences to one architectural choice.

Code Implementation

The simulator below uses an explicit-Euler spring-damper transition with step size 0.10.1, plus discrete process noise. It is the discrete model being studied, not the exact sampled transition of a continuous-time stochastic oscillator. Observation oto_t measures the position in state sts_t; action ata_t affects st+1s_{t+1}. We use consistent arbitrary position and time units, so velocity has position-per-time units. The learned models receive positions and actions, not the true state, simulator coefficients, or individual noise realizations.

In[10]:
Code
import numpy as np
import torch
import torch.nn as nn

rng = np.random.default_rng(7)

dt = 0.1
A = np.array([[1.0, dt], [-4.0 * dt, 1.0 - 0.5 * dt]])  # spring-damper dynamics
B = np.array([[0.0], [dt]])  # actions push on velocity
Q = 0.01 * np.eye(2)  # process noise
obs_std = 0.25  # sensor noise on position


def simulate_batch(n_seqs, T, rng):
    """Spring-damper with force actions; only noisy positions are observed."""
    states = np.zeros((n_seqs, T + 1, 2))
    obs = np.zeros((n_seqs, T, 1))
    actions = rng.normal(scale=0.5, size=(n_seqs, T, 1))
    states[:, 0] = rng.normal(scale=[1.0, 0.5], size=(n_seqs, 2))
    for t in range(T):
        noise = rng.multivariate_normal(np.zeros(2), Q, size=n_seqs)
        states[:, t + 1] = (
            states[:, t] @ A.T + np.outer(actions[:, t], B.ravel()) + noise
        )
        obs[:, t] = states[:, t, 0:1] + rng.normal(
            scale=obs_std, size=(n_seqs, 1)
        )
    return states, obs, actions

Now we generate a training set and a held-out validation set, and pick one demonstration sequence for plotting later.

In[11]:
Code
states, obs, actions = simulate_batch(480, 50, rng)
train_states, val_states = states[:400], states[400:]
train_obs, val_obs = obs[:400], obs[400:]
train_act, val_act = actions[:400], actions[400:]

demo_idx = 3
t_grid = np.arange(50)
env_demo_pos = states[demo_idx, :50, 0]
env_demo_vel = states[demo_idx, :50, 1]
env_demo_obs = obs[demo_idx, :, 0]
Out[12]:
Console
sequences: train=400, val=80, length T=50
true state: (position, velocity); observation: noisy position only
position range: [-3.23, +3.23]
velocity range: [-6.25, +6.08]
sensor noise std: 0.25
Out[13]:
Visualization
Position curve overlaid with noisy position samples.
True position and noisy observations for one simulated sequence. Recorded actions are also supplied to the learned models.
Hidden velocity curve on its own labeled axis.
The same sequence's hidden velocity, shown in position-per-time units. Velocity is not an input to world-model training.

The model receives the noisy position observations shown here and their recorded actions. The true state curves are for our inspection. Try estimating the direction of motion from a short noisy history: a single position generally does not determine velocity, while earlier positions and applied forces provide additional information. Useful predictive memory need not contain a coordinate explicitly named velocity; equivalent learned features can carry motion information.

The model

The RSSM below mirrors the equations of the architecture section exactly: a GRU cell for the deterministic path, two small heads for the prior and posterior Gaussians, and a decoder. Note that the posterior head is the only component that receives the observation as direct input; the decoder touches the observation only indirectly, through z_t sampled from the posterior.

In[14]:
Code
class RSSM(nn.Module):
    """PlaNet-style recurrent state-space model.

    h_t = GRU(h_{t-1}, [z_{t-1}, a_{t-1}])          deterministic memory
    z_t ~ N(mu, diag(sigma**2)) from prior(h_t) or posterior(h_t, o_t)
    o_t is decoded from [h_t, z_t]
    """

    def __init__(self, obs_dim=1, act_dim=1, h_dim=48, z_dim=8, hidden=64):
        super().__init__()
        self.h_dim, self.z_dim = h_dim, z_dim
        self.cell = nn.GRUCell(z_dim + act_dim, h_dim)
        self.prior_net = nn.Sequential(
            nn.Linear(h_dim, hidden), nn.ELU(), nn.Linear(hidden, 2 * z_dim)
        )
        self.posterior_net = nn.Sequential(
            nn.Linear(h_dim + obs_dim, hidden),
            nn.ELU(),
            nn.Linear(hidden, 2 * z_dim),
        )
        self.decoder = nn.Sequential(
            nn.Linear(h_dim + z_dim, hidden),
            nn.ELU(),
            nn.Linear(hidden, obs_dim),
        )

    @staticmethod
    def gauss_head(params):
        mu, log_std = params.chunk(2, dim=-1)
        return mu, log_std.clamp(-5.0, 2.0)

The gauss_head splits each head into a mean and log standard deviation. Exponentiation makes the scale positive; clamping the log scale to [−5,2][-5,2] bounds it to approximately [0.0067,7.39][0.0067,7.39]. The mean is not bounded, and this clamp does not prevent posterior collapse or every numerical failure. The GRU uses (ht−1,zt−1,at−1)(h_{t-1},z_{t-1},a_{t-1}) before the current observation is encoded, matching the timing convention above.

The training objective

The KL helper implements the analytic conditional diagonal-Gaussian expression. The loop updates hth_t, computes both heads, samples the posterior, decodes, and accumulates squared reconstruction error plus weighted clipped KL. This is a single-trajectory estimate of a modified variational objective, not the literal unmodified ELBO displayed earlier.

The free-nat floor is 1.0, applied after summing latent dimensions but separately for each sequence and timestep. The KL coefficient is 0.1. With our scalar squared-error reduction, its fixed-Gaussian interpretation uses decoder variance 0.050.05 before free-nat clipping. This is a chosen observation-model scale, not an estimate of the simulator's sensor variance 0.06250.0625. We log raw KL as well as the clipped loss term. Neither their scalar ratio nor convergence near the threshold establishes that the prior has learned all predictable information.

In[15]:
Code
def gaussian_kl(mu_q, ls_q, mu_p, ls_p):
    """Diagonal Gaussian KL; ls values are log standard deviations, not variances."""
    return (
        ls_p
        - ls_q
        + (torch.exp(2 * ls_q) + (mu_q - mu_p) ** 2) / (2 * torch.exp(2 * ls_p))
        - 0.5
    ).sum(dim=-1)


def train_rssm(
    model,
    obs,
    act,
    steps=1500,
    batch=24,
    lr=1e-3,
    free_nats=1.0,
    kl_weight=0.1,
    seed=0,
):
    torch.manual_seed(seed)
    opt = torch.optim.Adam(model.parameters(), lr=lr)
    n_seq, T, _ = obs.shape
    obs_t = torch.tensor(obs, dtype=torch.float32)
    act_t = torch.tensor(act, dtype=torch.float32)
    history = {"step": [], "recon": [], "kl": [], "raw_kl": []}
    for step in range(steps):
        idx = torch.randint(0, n_seq, (batch,))
        o_b, a_b = obs_t[idx], act_t[idx]
        h = torch.zeros(batch, model.h_dim)
        z = torch.zeros(batch, model.z_dim)
        a_prev = torch.zeros(batch, act_t.shape[-1])
        recon_losses, kl_losses, raw_kls = [], [], []
        for t in range(T):
            h = model.cell(torch.cat([z, a_prev], dim=-1), h)
            mu_p, ls_p = model.gauss_head(model.prior_net(h))
            mu_q, ls_q = model.gauss_head(
                model.posterior_net(torch.cat([h, o_b[:, t]], dim=-1))
            )
            z = mu_q + torch.randn_like(mu_q) * torch.exp(ls_q)
            pred = model.decoder(torch.cat([h, z], dim=-1))
            recon_losses.append(((pred - o_b[:, t]) ** 2).sum(dim=-1))
            kl_t = gaussian_kl(mu_q, ls_q, mu_p, ls_p)
            raw_kls.append(kl_t)
            kl_losses.append(torch.clamp(kl_t, min=free_nats))
            a_prev = a_b[:, t]
        recon = torch.stack(recon_losses).mean()
        kl = torch.stack(kl_losses).mean()
        loss = (
            torch.stack(recon_losses).sum(0)
            + kl_weight * torch.stack(kl_losses).sum(0)
        ).mean()
        opt.zero_grad()
        loss.backward()
        nn.utils.clip_grad_norm_(model.parameters(), 10.0)
        opt.step()
        if step % 10 == 0 or step == steps - 1:
            history["step"].append(step)
            history["recon"].append(recon.item())
            history["kl"].append(kl.item())
            history["raw_kl"].append(torch.stack(raw_kls).mean().item())
    return history

The clamp is applied before the timestep sum. Strictly below the threshold, this KL contribution has zero derivative; other loss paths still train the model. At the threshold, the implementation chooses a subgradient. The loss sums 50 timesteps and then averages the minibatch. Because every sequence has the same length here, averaging over time instead would change the objective by a constant scale; variable-length weighting would need an explicit choice.

In[16]:
Code
torch.manual_seed(0)  # seed weight initialization before construction
rssm = RSSM()
history = train_rssm(rssm, train_obs, train_act, steps=1500, seed=0)
Out[17]:
Console
final reconstruction MSE per step: 0.0175
sensor noise variance:              0.0625
final KL per step:                  1.101 nats (free-nat floor = 1.0)
final raw KL per step:              1.017 nats
Out[18]:
Visualization
Training curve of reconstruction error decreasing and flattening.
Training curve of reconstruction error per step, falling as the posterior and decoder learn to use observations.
Raw and clipped KL traces compared with the chosen 1-nat threshold.
Raw and clipped per-step KL during training, with the 1-nat clipping threshold. A value near the threshold does not establish prior optimality.

Read raw and clipped KL separately. A clipped value exactly at 1 would only imply that every contributing raw value was at or below 1; it would not prove optimal prediction or identify a noise floor. Posterior reconstruction can fall below the sensor variance because its encoder sees the same noisy observation used as its target. That is allowed by the objective, not an accidental input leak. By contrast, a history-only prediction of a fresh observation has expected squared error at least the independent sensor variance. Clean-position evaluation below asks whether the posterior output denoises, and the Kalman reference shows how far it is from the known-model optimum.

In the seeded reproduction, reconstruction fell to 0.0175 per step. The logged clipped KL began near 1, rose, and finished at 1.101 nats; raw KL finished at 1.017 nats. The final values are last-minibatch diagnostics, not held-out averages. The initial increase is another reason not to interpret every training trace as a prior steadily catching up to a fixed posterior.

A deterministic control model

The baseline GRU receives (ot−1,at−1)(o_{t-1},a_{t-1}) and predicts oto_t with a squared-error point decoder. Its hidden memory could encode motion information or distribution parameters, but this particular output head returns only a point. It differs from the mixture-density RNN in World Models. We leave its small training budget explicit rather than describing the comparison as an isolated test of stochasticity.

In[19]:
Code
class DetSSM(nn.Module):
    """Deterministic RNN world model: h_t = f(h_{t-1}, o_{t-1}, a_{t-1})."""

    def __init__(self, obs_dim=1, act_dim=1, h_dim=48, hidden=64):
        super().__init__()
        self.h_dim = h_dim
        self.cell = nn.GRUCell(obs_dim + act_dim, h_dim)
        self.decoder = nn.Sequential(
            nn.Linear(h_dim, hidden), nn.ELU(), nn.Linear(hidden, obs_dim)
        )


def train_det(model, obs, act, steps=800, batch=24, lr=1e-3, seed=1):
    torch.manual_seed(seed)
    opt = torch.optim.Adam(model.parameters(), lr=lr)
    n_seq, T, _ = obs.shape
    obs_t = torch.tensor(obs, dtype=torch.float32)
    act_t = torch.tensor(act, dtype=torch.float32)
    final_loss = None
    for step in range(steps):
        idx = torch.randint(0, n_seq, (batch,))
        o_b, a_b = obs_t[idx], act_t[idx]
        h = torch.zeros(batch, model.h_dim)
        mse = torch.zeros(batch)
        for t in range(1, T):
            h = model.cell(torch.cat([o_b[:, t - 1], a_b[:, t - 1]], dim=-1), h)
            pred = model.decoder(h)
            mse = mse + ((pred - o_b[:, t]) ** 2).sum(dim=-1)
        loss = mse.mean() / (T - 1)
        opt.zero_grad()
        loss.backward()
        nn.utils.clip_grad_norm_(model.parameters(), 10.0)
        opt.step()
        final_loss = loss.item()
    return final_loss
In[20]:
Code
torch.manual_seed(
    1
)  # initialize independently of earlier training's RNG consumption
det_model = DetSSM()
det_final_loss = train_det(det_model, train_obs, train_act, steps=800, seed=1)
Out[21]:
Console
deterministic model final teacher-forced MSE: 0.0997
(teacher-forced: the real previous observation is fed at every step)

Teacher forcing feeds the real previous observation during baseline training; recursive evaluation feeds the prediction instead. This change can expose compounding errors, but does not guarantee that every horizon worsens. The RSSM also changes operating regime when it replaces future posterior updates with prior draws. It supplies an ensemble while this baseline supplies no interval. That difference in output format does not establish that the ensemble is well calibrated.

Filtering versus imagination

Now the two evaluation regimes. The filter runs the posterior at every step: the model always gets to look. The imagination burns in the belief on the first BURN_IN real observations, then rolls out with the prior only, under the true future actions, sampling N_SAMPLES independent futures. The deterministic baseline rolls out by feeding its own predictions back in place of observations, the classic deterministic world-model rollout.

In[22]:
Code
@torch.no_grad()
def rssm_filter(model, obs_seq, act_seq, seed=0, n_samples=64):
    """Mean decoder output over posterior paths; first path supplies probe features."""
    if len(obs_seq) == 0 or len(obs_seq) != len(act_seq) or n_samples < 1:
        raise ValueError(
            "Need aligned nonempty sequences and positive n_samples."
        )
    g = torch.Generator().manual_seed(seed)
    T = obs_seq.shape[0]
    h = torch.zeros(n_samples, model.h_dim)
    z = torch.zeros(n_samples, model.z_dim)
    a_prev = torch.zeros(n_samples, act_seq.shape[-1])
    preds, feats, kls = [], [], []
    for t in range(T):
        h = model.cell(torch.cat([z, a_prev], dim=-1), h)
        mu_p, ls_p = model.gauss_head(model.prior_net(h))
        mu_q, ls_q = model.gauss_head(
            model.posterior_net(
                torch.cat([h, obs_seq[t : t + 1].expand(n_samples, -1)], dim=-1)
            )
        )
        z = mu_q + torch.randn(mu_q.shape, generator=g) * torch.exp(ls_q)
        preds.append(model.decoder(torch.cat([h, z], dim=-1)).mean(0).numpy())
        feats.append(torch.cat([h, z], dim=-1).numpy()[0])
        kls.append(gaussian_kl(mu_q, ls_q, mu_p, ls_p).mean().item())
        a_prev = act_seq[t : t + 1].expand(n_samples, -1)
    return np.array(preds), np.array(feats), np.array(kls)


@torch.no_grad()
def rssm_imagine(model, obs_seq, act_seq, burn_in=10, n_samples=256, seed=0):
    """Independent posterior paths, followed by prior-only decoder-mean paths."""
    if (
        not 1 <= burn_in < len(obs_seq)
        or len(obs_seq) != len(act_seq)
        or n_samples < 1
    ):
        raise ValueError(
            "Need aligned sequences, 1 <= burn_in < T, and positive n_samples."
        )
    g = torch.Generator().manual_seed(seed)
    T = obs_seq.shape[0]
    h = torch.zeros(n_samples, model.h_dim)
    z = torch.zeros(n_samples, model.z_dim)
    a_prev = torch.zeros(n_samples, act_seq.shape[-1])
    for t in range(burn_in):
        h = model.cell(torch.cat([z, a_prev], dim=-1), h)
        mu_q, ls_q = model.gauss_head(
            model.posterior_net(
                torch.cat([h, obs_seq[t : t + 1].expand(n_samples, -1)], dim=-1)
            )
        )
        z = mu_q + torch.randn(mu_q.shape, generator=g) * torch.exp(ls_q)
        a_prev = act_seq[t : t + 1].expand(n_samples, -1)
    imag = []
    for t in range(burn_in, T):
        h = model.cell(torch.cat([z, a_prev], dim=-1), h)
        mu_p, ls_p = model.gauss_head(model.prior_net(h))
        z = mu_p + torch.randn(mu_p.shape, generator=g) * torch.exp(ls_p)
        imag.append(model.decoder(torch.cat([h, z], dim=-1)).numpy())
        a_prev = act_seq[t : t + 1].repeat(n_samples, 1)
    return np.stack(imag, axis=0)  # (T - burn_in, n_samples, obs_dim)


@torch.no_grad()
def det_imagine(model, obs_seq, act_seq, burn_in=10):
    """Burn in on real observations, then feed the model its own predictions."""
    if not 1 <= burn_in < len(obs_seq) or len(obs_seq) != len(act_seq):
        raise ValueError("Need aligned sequences and 1 <= burn_in < T.")
    T = obs_seq.shape[0]
    h = torch.zeros(1, model.h_dim)
    for t in range(1, burn_in):
        h = model.cell(
            torch.cat([obs_seq[t - 1 : t], act_seq[t - 1 : t]], dim=-1), h
        )
    o_prev = obs_seq[burn_in - 1 : burn_in]  # last real observation
    imag = []
    for t in range(burn_in - 1, T - 1):
        h = model.cell(torch.cat([o_prev, act_seq[t : t + 1]], dim=-1), h)
        o_prev = model.decoder(h)
        imag.append(o_prev.numpy())
    return np.concatenate(imag, axis=0)  # (T - burn_in, obs_dim)

The two RSSM passes reuse the transition and decoder, while changing which distribution supplies the latent when observations are withheld. Future filtering and imagination therefore have different information, even for a perfectly specified model. Here each ensemble member follows its own sampled posterior history during burn-in before continuing with independent prior noise. This retains uncertainty across installed latent states instead of replicating one sampled endpoint. We decode conditional observation means without adding observation-likelihood noise: the bands below describe this decoder-mean ensemble scored against clean position, not the model's full noisy-observation predictive distribution. Its suitability as a clean-state interval is an empirical question, since clean positions were not training targets.

The reference filter uses the exact simulator parameters. At a measurement, it updates a Gaussian mean and covariance with C=[1,0]C=[1,0] and measurement variance R=0.0625R=0.0625. Between measurements, the mean follows mt+1=Amt+Batm_{t+1}=Am_t+Ba_t, and covariance follows Pt+1=APtA⊤+QP_{t+1}=AP_tA^\top+Q. The first term propagates remaining uncertainty; the second adds process noise. Starting from the posterior after observation 9 and omitting further measurement updates gives the exact open-loop reference for positions 10 through 49. Its covariance includes uncertainty after burn-in, not only newly added noise.

The memoryless reference deliberately ignores history and actions. It uses the known marginal position variance VtV_t under the simulator's random action distribution and returns Vt/(Vt+R) otV_t/(V_t+R)\,o_t. Its expected error is VtR/(Vt+R)<RV_tR/(V_t+R)<R, illustrating why beating raw-observation echo alone does not prove temporal inference. Both analytical references have privileged model knowledge and are labeled accordingly.

In[23]:
Code
def kalman_reference(obs_seq, act_seq, stop_observing=None):
    """Exact known-model state moments, before or after the observation cutoff."""
    T = len(obs_seq)
    cutoff = T if stop_observing is None else stop_observing
    if len(act_seq) != T or not 0 <= cutoff <= T:
        raise ValueError("Need aligned sequences and a cutoff in [0, T].")
    m, P = np.zeros(2), np.diag([1.0, 0.25])
    C = np.array([1.0, 0.0])
    means, covariances = [], []
    for t in range(T):
        if t < cutoff:
            K = P @ C / (C @ P @ C + obs_std**2)
            m = m + K * (float(obs_seq[t, 0]) - C @ m)
            J = np.eye(2) - np.outer(K, C)
            P = J @ P @ J.T + obs_std**2 * np.outer(K, K)
        means.append(m.copy())
        covariances.append(P.copy())
        m = A @ m + B[:, 0] * float(act_seq[t, 0])
        P = A @ P @ A.T + Q
    return np.array(means), np.array(covariances)


def point_and_sample_rmse(samples, targets):
    """Inputs (sequence, horizon, sample) and (sequence, horizon)."""
    if (
        samples.ndim != 3
        or samples.shape[:2] != targets.shape
        or samples.shape[2] < 1
    ):
        raise ValueError(
            "Expected aligned sequence/horizon axes and a nonempty sample axis."
        )
    mean = samples.mean(axis=2)
    point = np.sqrt(((mean - targets) ** 2).mean(axis=0))
    sample = np.sqrt(((samples - targets[..., None]) ** 2).mean(axis=(0, 2)))
    return point, sample


def interval_metrics(lower, upper, targets, alpha=0.05):
    """Per-horizon coverage, width and central interval score (smaller is better)."""
    if lower.shape != upper.shape or lower.shape != targets.shape:
        raise ValueError(
            "Interval bounds and targets must have matching shapes."
        )
    if not 0 < alpha < 1 or np.any(lower > upper):
        raise ValueError("Require ordered bounds and alpha in (0, 1).")
    width = upper - lower
    score = width + (2 / alpha) * (
        np.maximum(lower - targets, 0) + np.maximum(targets - upper, 0)
    )
    return (
        ((lower <= targets) & (targets <= upper)).mean(0),
        width.mean(0),
        score.mean(0),
    )


def wilson_interval(proportion, n, z=1.96):
    """Approximate binomial 95% confidence interval for each marginal coverage."""
    if n <= 0 or np.any((proportion < 0) | (proportion > 1)):
        raise ValueError("Need positive sample size and proportions in [0, 1].")
    center = (proportion + z * z / (2 * n)) / (1 + z * z / n)
    half = (
        z
        * np.sqrt(proportion * (1 - proportion) / n + z * z / (4 * n * n))
        / (1 + z * z / n)
    )
    return center - half, center + half

We evaluate all 80 held-out sequences. Each RSSM imagination uses 256 particles, independently sampled through burn-in as well as the future. Posterior point estimates average 64 paths; probe features below use the first path, and their labels are never used to train the RSSM. A central sample interval uses the 2.5th and 97.5th percentiles, without assuming Gaussian decoded outputs. Its finite-particle endpoints still have Monte Carlo error.

At each horizon, coverage has 80 independent truth sequences, not 80 times 256 independent outcomes. Wilson intervals describe uncertainty in that marginal proportion; they are not simultaneous confidence bands across horizons, whose outcomes are correlated. We also report interval width and the central interval score: width plus 2/α2/\alpha times any distance by which truth misses the interval, where α=0.05\alpha=0.05. This penalizes both unnecessary width and missed outcomes. All computations happen before plotting.

In[24]:
Code
BURN_IN, N_SAMPLES, N_EVAL = 10, 256, len(val_obs)
T = val_obs.shape[1]
H = T - BURN_IN

filter_preds, feat_list, kl_list = [], [], []
imag_list, det_list, true_list = [], [], []
oracle_filters, oracle_means, oracle_covs = [], [], []

for i in range(N_EVAL):
    o_seq = torch.tensor(val_obs[i], dtype=torch.float32)
    a_seq = torch.tensor(val_act[i], dtype=torch.float32)
    f_pred, feats, kls = rssm_filter(rssm, o_seq, a_seq, seed=1000 + i)
    imag_list.append(
        rssm_imagine(rssm, o_seq, a_seq, BURN_IN, N_SAMPLES, seed=2000 + i)
    )
    det_list.append(det_imagine(det_model, o_seq, a_seq, BURN_IN))
    filter_preds.append(f_pred)
    feat_list.append(feats)
    kl_list.append(kls)
    true_list.append(val_states[i, :T, 0])
    oracle_filters.append(kalman_reference(val_obs[i], val_act[i])[0][:, 0])
    om, op = kalman_reference(val_obs[i], val_act[i], stop_observing=BURN_IN)
    oracle_means.append(om[BURN_IN:, 0])
    oracle_covs.append(op[BURN_IN:, 0, 0])

true_pos = np.stack(true_list)  # (N_EVAL, T)
filt_arr = np.stack(filter_preds)  # (N_EVAL, T, 1)
imag = np.stack(imag_list)  # (N_EVAL, H, S, 1)
det_arr = np.stack(det_list)  # (N_EVAL, H, 1)
val_feats = np.stack(feat_list)  # (N_EVAL, T, h_dim + z_dim)
val_kl = np.stack(kl_list)  # (N_EVAL, T)

true_future = true_pos[:, BURN_IN:]  # (N_EVAL, H)
imag_mean = imag[..., 0].mean(axis=2)  # (N_EVAL, H)
imag_std = imag[..., 0].std(axis=2)
imag_rmse, imag_sample_rmse = point_and_sample_rmse(imag[..., 0], true_future)
det_rmse = np.sqrt(((det_arr[..., 0] - true_future) ** 2).mean(axis=0))
filter_err = np.sqrt(((filt_arr[..., 0] - true_pos) ** 2).mean(axis=0))
filter_rmse = np.sqrt(((filt_arr[..., 0] - true_pos) ** 2).mean())
echo_rmse = np.sqrt(((val_obs[:N_EVAL, :, 0] - true_pos) ** 2).mean())
imag_lower, imag_upper = np.quantile(imag[..., 0], [0.025, 0.975], axis=2)
coverage, band_width, interval_score = interval_metrics(
    imag_lower, imag_upper, true_future
)
coverage_low, coverage_high = wilson_interval(coverage, N_EVAL)
oracle_mean, oracle_var = np.stack(oracle_means), np.stack(oracle_covs)
oracle_rmse = np.sqrt(((oracle_mean - true_future) ** 2).mean(0))
oracle_expected_rmse = np.sqrt(oracle_var.mean(0))
oracle_filter_rmse = np.sqrt(
    ((np.stack(oracle_filters) - true_pos) ** 2).mean()
)
oracle_lower = oracle_mean - 1.959963984540054 * np.sqrt(oracle_var)
oracle_upper = oracle_mean + 1.959963984540054 * np.sqrt(oracle_var)
oracle_coverage, oracle_width, oracle_score = interval_metrics(
    oracle_lower, oracle_upper, true_future
)

marginal_P = np.diag([1.0, 0.25])
shrink_gains = []
for t in range(T):
    shrink_gains.append(marginal_P[0, 0] / (marginal_P[0, 0] + obs_std**2))
    marginal_P = A @ marginal_P @ A.T + Q + 0.5**2 * (B @ B.T)
shrink_pred = val_obs[:N_EVAL, :, 0] * np.array(shrink_gains)
shrink_rmse = np.sqrt(((shrink_pred - true_pos) ** 2).mean())
horizons = np.arange(1, H + 1)
Out[25]:
Console
echo-the-observation RMSE:            0.249
RSSM filtering RMSE (vs true pos):    0.210
known-model memoryless RMSE:         0.231
known-model Kalman filtering RMSE:   0.146
RSSM mean RMSE:         h=1 0.198   h=10 0.304   h=40 0.547
deterministic RMSE:      h=1 0.190   h=10 0.300   h=40 0.498
Kalman mean RMSE:       h=1 0.189   h=10 0.280   h=40 0.488
RSSM sample-path RMSE at h=40: 0.927 (different estimand)
h=1: RSSM interval coverage 100.0% (Wilson interval 95.4%–100.0%), width 1.622, interval score 1.622
      Kalman interval coverage 95.0%, width 0.742, interval score 0.930
h=10: RSSM interval coverage 100.0% (Wilson interval 95.4%–100.0%), width 2.108, interval score 2.108
      Kalman interval coverage 97.5%, width 1.161, interval score 1.211
h=40: RSSM interval coverage 98.8% (Wilson interval 93.3%–99.8%), width 2.893, interval score 2.901
      Kalman interval coverage 93.8%, width 1.912, interval score 2.048
mean prior-posterior KL on val:       1.02 nats/step

Read the filter errors against all three references, not echo alone. A memoryless estimate can beat echo through shrinkage toward a marginal mean without consulting any earlier observations. The Kalman filter uses the supplied simulator parameters, which the neural models do not receive, and is an estimation oracle rather than a fair data-only competitor. The two neural point forecasts are now scored identically: first average RSSM samples, then compute squared error across held-out sequences. Individual-sample RMSE is reported separately because it also includes ensemble variance. Coverage and interval width assess a different output property; neither turns point error into a decomposition of model bias and physical randomness. Raw held-out KL is another diagnostic, not a count of useful facts the model has learned.

In the seeded CPU reproduction (PyTorch 2.13.0, NumPy 2.4.2, two CPU threads), posterior-mean position RMSE was 0.210, versus 0.249 for echo, 0.231 for known-model memoryless shrinkage, and 0.146 for Kalman filtering. This RSSM does denoise on these sequences, but falls short of the known-model reference. At horizon 40, ensemble-mean RMSE was 0.547, versus 0.498 for the point baseline and 0.488 for the Kalman mean. These are one-seed results, not a statistically established ranking across training runs. The Monte Carlo ensemble mean also has finite-particle estimation error.

The sample-path RMSE at horizon 40 was 0.927. This is not a contradiction: for the same target, mean squared sample error equals squared ensemble-mean error plus ensemble variance. At a fixed ensemble mean, greater ensemble variance raises the sample-path metric, even when that variance correctly represents possible futures. That is why the point-error plot uses the ensemble mean and the interval diagnostics are reported separately.

The RSSM intervals covered 100% of held-out positions at horizons 1 and 10, and 98.8% at horizon 40. Their mean widths were 1.622, 2.108, and 2.893 position units, compared with Kalman widths of 0.742, 1.161, and 1.912. The central interval scores were also worse than the oracle's at those horizons. This evidence favors a diagnosis of unnecessarily broad decoder-mean intervals here, not a claim of successful calibration. At horizon 40 the coverage estimate's Wilson interval was approximately 93.3%–99.8%; 80 outcomes still leave substantial tail uncertainty.

One sequence, two regimes

The next figure shows a single held-out sequence, which makes the regime split vivid. Before the vertical line the model filters with real observations; after it, imagination runs on the prior alone.

In[26]:
Code
demo = 0
t_all = np.arange(T)
t_imag = np.arange(BURN_IN, T)
demo_true = true_pos[demo]
demo_obs = val_obs[demo, :, 0]
demo_filter = filt_arr[demo, :, 0]
demo_imag_mean = imag_mean[demo]
demo_imag_lower, demo_imag_upper = imag_lower[demo], imag_upper[demo]
demo_det = det_arr[demo, :, 0]
Out[27]:
Visualization
Clean position, noisy observations, posterior estimates, and two open-loop point forecasts with an RSSM sample-quantile band after the cutoff.
One held-out sequence. After the cutoff, prior-only imagination consumes actions but no observations; shading shows the central 95% sample-quantile interval of its decoder means, not a certified clean-state interval. The posterior line continues using observations offline for contrast and is unavailable to the imagined rollout. The deterministic baseline returns a point forecast without an interval.

The posterior trace continues using observations after the cutoff only as an offline comparison; the imagined paths do not. The interval can fluctuate in width with both the inferred dynamics and finite Monte Carlo sampling. Inspect where truth leaves it and compare that with the aggregate coverage and width below. The point baseline has no uncertainty head, so its line alone supplies no interval. Conversely, a visibly broad RSSM band does not certify reliable uncertainty.

Error versus horizon

Aggregating over all evaluated sequences shows the horizon-dependent errors quantitatively.

Out[28]:
Visualization
Point-forecast RMSE curves and an oracle covariance reference, with separate posterior-error comparison.
Matched point-forecast RMSE across 80 held-out sequences: RSSM ensemble mean, deterministic baseline, and known-model Kalman mean. The Kalman covariance gives the root expected squared-error reference. Posterior error uses observations throughout and is shown only for scale, not as a k-step forecast. Both learned forecasts incur process-noise uncertainty as well as possible model or inference error.

Both neural point forecasts and the exact Kalman mean incur error against stochastic futures. Even the oracle cannot predict the realized process noise. Its covariance recursion supplies the irreducible conditional mean-squared-error reference; finite held-out RMSE also fluctuates with the sampled outcomes. An excess over the oracle in expectation can reflect model or inference defects, while a crossing on a small test set is not proof that a learned model beats the conditional-mean optimum. The posterior curve uses observations throughout and is shown only for scale. It need not be flat, and none of these curves is required to rise monotonically.

What the belief actually contains

Finally, we test linear recoverability of physical-state information. The RSSM is frozen. A ridge readout sees training features and true-state labels, and is evaluated on held-out sequences. Those labels train the probe, not the world model. Success supports a relationship between the learned representation and velocity under this data distribution; it does not establish how the RSSM uses that information or whether its full belief is sufficient.

In[29]:
Code
probe_feats, probe_targets = [], []
for i in range(0, len(train_obs), 4):
    o_seq = torch.tensor(train_obs[i], dtype=torch.float32)
    a_seq = torch.tensor(train_act[i], dtype=torch.float32)
    _, feats, _ = rssm_filter(rssm, o_seq, a_seq, seed=i, n_samples=64)
    probe_feats.append(feats)
    probe_targets.append(train_states[i, :T, :])
Xp = np.concatenate(probe_feats)
yp = np.concatenate(probe_targets)

Xv = val_feats.reshape(-1, val_feats.shape[-1])
yv = np.stack([val_states[i, :T, :] for i in range(N_EVAL)]).reshape(-1, 2)


def ridge_r2(X_tr, y_tr, X_te, y_te, lam=1e-2):
    x_mean, y_mean = X_tr.mean(0), y_tr.mean(0)
    Xc, yc = X_tr - x_mean, y_tr - y_mean
    gram = Xc.T @ Xc + lam * np.eye(X_tr.shape[1])
    w = np.linalg.solve(gram, Xc.T @ yc)
    pred = (X_te - x_mean) @ w + y_mean
    ss_res = ((y_te - pred) ** 2).sum(axis=0)
    ss_tot = ((y_te - y_te.mean(axis=0)) ** 2).sum(axis=0)
    if np.any(ss_tot == 0):
        raise ValueError("R-squared requires nonconstant held-out targets.")
    return 1.0 - ss_res / ss_tot


r2 = ridge_r2(Xp, yp, Xv, yv)

## Fit PCA on training features; project held-out features without using labels.
Xall = val_feats.reshape(-1, val_feats.shape[-1])
Xc = Xall - Xp.mean(axis=0)
_, _, Vt = np.linalg.svd(Xp - Xp.mean(axis=0), full_matrices=False)
belief_pc = Xc @ Vt[:2].T
train_pc = (Xp - Xp.mean(axis=0)) @ Vt[:2].T
pca_r2 = ridge_r2(train_pc, yp, belief_pc, yv)
vel_color = np.stack([val_states[i, :T, 1] for i in range(N_EVAL)]).reshape(-1)
Out[30]:
Console
linear probe R^2, belief -> position: 0.94
linear probe R^2, belief -> velocity: 0.93
two-PC probe R^2, projection -> velocity: 0.05
(velocity labels train only the separate probe, not the RSSM)
Out[31]:
Visualization
Two-dimensional PCA scatter colored by true velocity; mixed colors illustrate the limits of the projection.
Held-out filtered features projected onto the first two principal components fitted on training features, colored by true velocity. These high-variance directions need not preserve the velocity information recoverable from the full 56-dimensional representation. Compare the two-PC probe with the full-feature probe instead of assuming smooth color structure.

The full-feature probe and the two-dimensional PCA answer different questions. The probe tests whether a supervised linear readout can recover state information on held-out sequences. PCA retains directions of high feature variance without using velocity labels. Mixed colors in its first two components therefore do not contradict strong recoverability from all 56 features; informative directions may lie elsewhere. A weak linear probe would not rule out nonlinear information either. The held-out split reduces direct trajectory memorization, but additional seeds, input-only or untrained-feature controls, and tests under new action distributions would be needed for stronger conclusions.

The useful result is more limited than autonomous discovery of a named physical variable: a predictive model can learn features from noisy positions and recorded actions from which a separate readout recovers velocity-related information. It was not given true velocities, the spring constant, or damping during world-model training. Good probe scores do not by themselves establish a calibrated posterior, good long-horizon forecasts, or causal use of the recovered feature. The experiment keeps those questions separate.

The reproduced held-out full-feature probe scored about 0.94 for position and 0.93 for velocity. Restricting the probe to the first two training-fitted principal components reduced velocity R2R^2 to about 0.05. The mixed colors in this projection are therefore a useful warning: the directions with most representation variance are not necessarily those carrying the state information of interest. Neither number establishes Markov sufficiency.

Limitations and Impact

The limitations discussed here concern inference and rollout quality, representation, and computation.

The first theme is inference and rollout quality. During acting, observations can update the posterior; beyond the latest observation, imagination uses the prior with historical context. Some difference between these regimes is unavoidable because they have different information. Approximation, poorly fitted dynamics, and distribution shift can add further error. Held-out raw KL helps diagnose the representation, while point forecasts and distribution-sensitive tests assess its consequences. Free nats, balancing, and overshooting address different parts of training; none certifies trustworthy imagination.

The second theme is representation. A diagonal Gaussian restricts the conditional latent density at fixed context to an independent, unimodal family in its chosen coordinates. It does not forbid correlated or multimodal decoded physical variables: nonlinear transformations and mixtures over histories can produce both. Factorized categorical latents change those conditional modeling choices without guaranteeing an adequate belief. A finite-precision recurrent state can also lose relevant history, although some processes admit finite sufficient statistics even for unbounded histories. Low current-observation reconstruction error alone will not reveal every loss of predictive information.

The third theme is computation. An ordinary nonlinear GRU processes a length-TT sequence through TT sequential recurrent updates. Transformer training can process positions in parallel within each layer when the whole sequence is available, though this does not mean constant total work or universally faster training. Practical cost depends on sequence length, model size, hardware, and latency needs. The next chapter, Transformers and Neural State-Space Architectures, compares attention with structured and selective state-space sequence layers. S4 and Mamba also summarize history but are not RSSM Bayesian filters, and their update structure differs materially from a GRU. The autoregressive token models that follow inherit the evaluation questions: what history is represented, how do predictions change without future observations, and where do they fail?

PlaNet demonstrated latent-space planning from images. Dreamer learned an actor and value model through latent imagination; DreamerV2 reported Atari results, and DreamerV3 extended the evaluated domains to tasks including Minecraft. DayDreamer applied world-model learning to physical robots, including a quadruped learning to stand and walk in one hour. These systems reuse Bayesian terms such as prior, posterior, and belief rather than originating them. Their lasting practical question is whether an internal predictive state remains useful when it must continue without the observations that trained or corrected it.

Summary

  • An RSSM combines recurrent memory hth_t with a stochastic latent ztz_t. Deterministic computation is not certainty, and one sampled joint state is not a full belief distribution.
  • The prior predicts before the current observation; the posterior incorporates it during filtering, including at deployment. Their conditional KL trains the distinct prior head and regularizes the posterior, but is not forecast error or sensor-noise variance.
  • The deterministic recurrent edge bypasses fresh sampling operations; the latent distribution can carry observation-conditioned alternatives. PlaNet's ablations support this combination in its tested setup, not a universal necessity theorem.
  • The exact filtering ELBO averages over latent histories. A Monte Carlo estimate need not itself be a lower bound. The example uses a weighted, free-nat-clipped modification; free nats and KL balancing adjust gradients without guaranteeing prevention of collapse.
  • Imagination starts from independently sampled posterior histories and proceeds under actions with prior draws only. Compare ensemble-mean and point-forecast RMSE on equal terms, and evaluate intervals separately with coverage, width, and a known-model reference when available.
  • The worked example tests filtering, open-loop prediction, and linear state recoverability separately. A velocity probe does not establish Markov sufficiency, and a broad interval does not establish calibration.
  • Approximate inference, restricted conditional latent families, finite memory, and sequential computation motivate comparison with the architectures in the next chapter.

Quiz

Ready to test your understanding? Take this quick quiz to reinforce what you've learned about recurrent state-space models, filtering, and imagination.

Recurrent State-Space Models Quiz

Question 1 of 80 of 8 completed
In an RSSM, what does the 'deterministic' recurrent state h_t mean?

Comments

No comments yet. Be the first to share your thoughts!

Reference

Citation details

Cite or share this article.

BIBTEXAcademic
@misc{brenndoerfer2026recurrentstate, author = {Michael Brenndoerfer}, title = {Recurrent State-Space Models}, year = {2026}, url = {https://mbrenndoerfer.com/writing/recurrent-state-space-models-rssm-world-models}, organization = {mbrenndoerfer.com}, note = {Accessed: 2026-10-15} }
APAAcademic
Michael Brenndoerfer (2026). Recurrent State-Space Models. Retrieved from https://mbrenndoerfer.com/writing/recurrent-state-space-models-rssm-world-models
MLAAcademic
Michael Brenndoerfer. "Recurrent State-Space Models." 2026. Web. October 15, 2026. <https://mbrenndoerfer.com/writing/recurrent-state-space-models-rssm-world-models>.
CHICAGOAcademic
Michael Brenndoerfer. "Recurrent State-Space Models." Accessed October 15, 2026. https://mbrenndoerfer.com/writing/recurrent-state-space-models-rssm-world-models.
HARVARDAcademic
Michael Brenndoerfer (2026) 'Recurrent State-Space Models'. Available at: https://mbrenndoerfer.com/writing/recurrent-state-space-models-rssm-world-models (Accessed: October 15, 2026).
SimpleBasic
Michael Brenndoerfer (2026). Recurrent State-Space Models. https://mbrenndoerfer.com/writing/recurrent-state-space-models-rssm-world-models

About the author

Continue with the full handbook

This chapter is part of World Models Handbook. Use the handbook page to browse the complete table of contents and continue reading in sequence.

Explore World Models Handbook
Newsletter

Stay up to date

Get articles, book updates, and news delivered to your inbox.

No spam, unsubscribe anytime.

or

Join the community

Sign in to remove popups, track your reading progress, and join the discussion.