The World-Model Evaluation Ladder › Transfer
World Models HandbookAll chapters The World-Model Evaluation Ladder › TransferOne section · Sections in this chapter6 / 9 ↓
Reading tools Definitions & inline links
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
Transfer repeats an evaluation under a specified change. Several changes should be distinguished.
- A dynamics shift replaces by a different transition law in the queried region. A predictor accurate for can become inaccurate there.
- An initial-state shift changes occupancy and can expose poorly covered regions without changing the transition law.
- A changed objective or action constraint changes which decisions are useful even when the dynamics predictor remains correct.
- An observation shift can change the state or belief supplied to the controller while the physical transition law stays fixed.
Fitting data from alone establishes neither exact correctness under nor inevitable failure under . State what changed, what remains fixed, and whether retraining, adaptation, or extra observations are allowed. The experiment below changes only one true dynamics coefficient and keeps the candidate models, controller, target, initial state, and action grid fixed.
A scientific prior can help if it encodes structure that persists under the chosen shift; it can hurt if that structure changes. Better in-distribution fit does not universally imply worse transfer, and structural bias does not universally imply better transfer. [Part XII, Ch 2: Robustness, Calibration, and Safe Control] is planned to examine these questions more systematically.
Observation quality remains a separate part of the system, as discussed in [Part III, Ch 1: Observations, Sensors, and Multimodal Data]. Controlled changes to the perception pipeline can help distinguish observation failures from transition failures. The next chapters are planned to cover state and physics diagnostics.
A worked example: the model that wins one-step loses closed-loop
The example below is deliberately misspecified and small, so that everything is on the table. It is a compact illustration, not evidence that any particular architecture is inferior.
We use a fully observable linear system with a deterministic state-coupling structure plus additive Gaussian process noise:
with , , and i.i.d. across .
Where:
-
: the scalar state at time
-
: the scalar control action at time
-
: the true state-coupling coefficient
-
: the true action gain
-
: i.i.d. Gaussian process noise with standard deviation
-
State: scalar (fully observed).
-
Action: scalar , with a hard bound for the planning tests.
-
Planning objective: squared predicted next-state distance to , with no action penalty.
-
Control score: mean absolute target error over true states 31–40. This diagnostic is not the cumulative squared-cost gap defined above.
-
Episode horizon: 40 steps for rollouts, 40 steps for closed loop.
-
Data policy: for one-step evaluation, actions are drawn i.i.d. from . For rollout evaluation, we use the fixed sequence we specify in each rung.
-
Evaluation data: 800 independent synthetic transitions. The candidates are hand specified before sampling; no training set or fitted-model claim is involved.
-
Noise regime: Gaussian process noise is present in the one-step sample only. Rollout, intervention, and control tests use the noiseless transition, so their errors measure misspecification rather than different noise draws.
-
Model state: the Delayed candidate also carries the previous action. Its recurrent state is , initialized with in rollout and control tests.
We compare three candidate models of the form
where:
- : the model's next-state prediction;
- : the input state, true for teacher forcing and replanning, predicted for an open-loop rollout;
- : the previous applied action, , with for the trajectory experiments;
- : the model's state-coupling coefficient
- : the model's instantaneous action gain
- : the model's action-lag coefficient, capturing the influence of the previous action
The following candidates are chosen to expose different failure modes.
- Aggressive (): captures the state coupling closely but overshoots it into instability.
- Conservative (): underestimates the state coupling.
- Delayed (, ): gets the state coupling right but adds a spurious action lag.
These are hand-constructed misspecifications, not fitted solutions. Our goal is not to argue that any particular model class fails; it is to show that each rung rewards different inductive biases and that a lower rung does not by itself guarantee a higher one without additional linking assumptions. Notice that each model is a plausible way to be wrong about the same system. The Aggressive model is approximately correct as a one-step predictor because its coefficient error is small; the Conservative model trades a larger bias for more conservative behavior; the Delayed model gets the coupling right but invents a spurious dependence on the previous action. These hand-selected deviations let us isolate what happens when different coefficient errors are pushed through the evaluation tests.
import numpy as np
# Ground-truth one-dimensional controlled dynamics:
# s_{t+1} = A_TRUE * s_t + B_TRUE * u_t + noise
A_TRUE = 0.95
B_TRUE = 0.50
NOISE_STD = 0.02
# Candidate models of the form s_hat_{t+1} = a * s_t + b * u_t + c * u_{t-1}.
MISS = {
"Aggressive (a=1.02)": {"a": 1.02, "b": 0.50, "c": 0.00},
"Conservative (a=0.80)": {"a": 0.80, "b": 0.50, "c": 0.00},
"Delayed (a=0.95, c=0.20)": {"a": 0.95, "b": 0.50, "c": 0.20},
}
def model_step(model, s, u, u_prev):
return model["a"] * s + model["b"] * u + model["c"] * u_prevNow the rung-one evaluation, on 800 i.i.d. transitions.
rng = np.random.default_rng(20240501)
N_EVAL = 800
s_t = rng.normal(0.0, 1.0, size=N_EVAL)
u_prev_t = rng.normal(0.0, 1.0, size=N_EVAL)
u_t = rng.normal(0.0, 1.0, size=N_EVAL)
s_next_t = A_TRUE * s_t + B_TRUE * u_t + rng.normal(0.0, NOISE_STD, size=N_EVAL)
one_step_mse = {}
one_step_losses = {}
expected_one_step_mse = {}
gaussian_mse_se = {}
for name, model in MISS.items():
pred = model["a"] * s_t + model["b"] * u_t + model["c"] * u_prev_t
losses = (pred - s_next_t) ** 2
one_step_losses[name] = losses
one_step_mse[name] = float(np.mean(losses))
expected_risk = (
(model["a"] - A_TRUE) ** 2
+ (model["b"] - B_TRUE) ** 2
+ model["c"] ** 2
+ NOISE_STD**2
)
expected_one_step_mse[name] = expected_risk
gaussian_mse_se[name] = expected_risk * np.sqrt(2 / N_EVAL)
paired_mse_se = {}
names = list(MISS)
for left in range(len(names)):
for right in range(left + 1, len(names)):
pair = (names[left], names[right])
differences = one_step_losses[pair[0]] - one_step_losses[pair[1]]
paired_mse_se[pair] = float(
np.std(differences, ddof=1) / np.sqrt(N_EVAL)
) Aggressive (a=1.02) sample MSE = 0.005843; population MSE = 0.005300; Gaussian marginal SE = 0.000265
Conservative (a=0.80) sample MSE = 0.024686; population MSE = 0.022900; Gaussian marginal SE = 0.001145
Delayed (a=0.95, c=0.20) sample MSE = 0.040841; population MSE = 0.040400; Gaussian marginal SE = 0.002020
Paired Aggressive (a=1.02) minus Conservative (a=0.80): sample gap -0.018842; estimated SE 0.000938
Paired Aggressive (a=1.02) minus Delayed (a=0.95, c=0.20): sample gap -0.034998; estimated SE 0.002047
Paired Conservative (a=0.80) minus Delayed (a=0.95, c=0.20): sample gap -0.016156; estimated SE 0.002321On these independent standard-normal inputs, each model's residual is a zero-mean Gaussian linear combination of state, action, previous action, and independent process noise. Its population MSE is therefore
The resulting risks are for Aggressive, for Conservative, and for Delayed. Aggressive's state coefficient error is smaller than Conservative's; Delayed matches that coefficient exactly. Aggressive has lower total risk than Delayed because Delayed's spurious previous-action term adds variance . The sampled MSEs differ from these population risks, but retain the same order for the stated seed. No candidate was fitted to the sample.
For independent zero-mean Gaussian residuals of variance , the variance of their squared value is . Thus the sample MSE has standard error : at , five percent of the population MSE. The code prints that marginal calculation and separately estimates the standard error of each paired loss difference. The latter includes the covariance between model losses scored on the same transitions.
Now for rung two. We fix and apply constant actions over 40 steps, then measure trajectory RMSE.
HORIZON = 40
S_INIT = 0.3
actions_const = np.full(HORIZON, 0.20)
true_traj_const = np.zeros(HORIZON + 1)
true_traj_const[0] = S_INIT
for t in range(HORIZON):
true_traj_const[t + 1] = (
A_TRUE * true_traj_const[t] + B_TRUE * actions_const[t]
)
rollout_rmse_const = {}
for name, model in MISS.items():
traj = np.zeros(HORIZON + 1)
traj[0] = S_INIT
u_prev = 0.0
for t in range(HORIZON):
traj[t + 1] = model_step(model, traj[t], actions_const[t], u_prev)
u_prev = actions_const[t]
rollout_rmse_const[name] = float(
np.sqrt(np.mean((traj[1:] - true_traj_const[1:]) ** 2))
) Delayed (a=0.95, c=0.20) RMSE = 0.4936
Conservative (a=0.80) RMSE = 0.8969
Aggressive (a=1.02) RMSE = 2.3856Under a constant positive push , the true system's noiseless steady state is . The Aggressive model has , so its rollout diverges. The Conservative model has steady state , well below the truth. The Delayed model acquires an effective action gain , giving steady state , incidentally closer to the true value than the Conservative model's. Delayed wins rung two, despite losing rung one. The gain is not the true gain . Delayed wins this finite-horizon comparison because its state decay coefficient matches the truth and its accumulated gain error is smaller than the competing models' trajectory errors for this initial state and input. This is not a claim that the lagged model has correct constant-input dynamics.
For a controlled-intervention trajectory diagnostic, change the constant input to over the same 40 steps. This is another open-loop trajectory test under a deliberately changed action sequence. It is not the intervention-response difference defined earlier, and it does not establish causal identification. The simulator supplies the true dynamics by construction.
def paired_rollouts(model, actions, initial=0.3):
truth = np.zeros(len(actions) + 1)
predicted = np.zeros_like(truth)
truth[0] = predicted[0] = initial
previous = 0.0
for step, action in enumerate(actions):
truth[step + 1] = A_TRUE * truth[step] + B_TRUE * action
predicted[step + 1] = model_step(
model, predicted[step], action, previous
)
previous = action
return truth, predicted
actions_alt = 0.4 * (-1.0) ** np.arange(HORIZON)
rollout_rmse_alt = {}
long_alt_rmse = {}
alternating_amplitude = {"Truth": 0.4 * B_TRUE / (1 + A_TRUE)}
for name, model in MISS.items():
truth, predicted = paired_rollouts(model, actions_alt)
rollout_rmse_alt[name] = float(
np.sqrt(np.mean((predicted[1:] - truth[1:]) ** 2))
)
truth_long, predicted_long = paired_rollouts(
model, 0.4 * (-1.0) ** np.arange(500)
)
long_alt_rmse[name] = float(
np.sqrt(np.mean((predicted_long[1:] - truth_long[1:]) ** 2))
)
if abs(model["a"]) < 1:
alternating_amplitude[name] = (
0.4 * abs(model["b"] - model["c"]) / (1 + model["a"])
)Aggressive (a=1.02): alternating RMSE H=40 0.504376; H=500 1807.149336 Conservative (a=0.80): alternating RMSE H=40 0.135162; H=500 0.039716 Delayed (a=0.95, c=0.20): alternating RMSE H=40 0.045496; H=500 0.041403 Truth: steady alternating amplitude 0.1025641 Conservative (a=0.80): steady alternating amplitude 0.1111111 Delayed (a=0.95, c=0.20): steady alternating amplitude 0.0615385
Delayed wins the 40-step alternating-trajectory test, but not because its steady alternating response is most accurate. After the initial step, , so its effective alternating gain is . For , substituting into the forced recurrence gives the signed coefficient . The amplitude magnitude is . All displayed stable candidates have , so their coefficients and magnitudes agree. The true amplitude is , Conservative's is , and Delayed's is . Delayed instead benefits at this short horizon from matching the true transient decay coefficient. At 500 steps Conservative has lower trajectory RMSE than Delayed, as the printed results show. Aggressive is unstable, so its formal forced periodic solution is not an attracting steady response.
We also compute the actual response-difference test. Use the constant input as baseline and the alternating input as intervention, starting both trajectories at with previous action zero. Compare their final-state differences, not merely the intervened trajectory:
intervention_error = {}
intervention_responses = {}
for name, model in MISS.items():
truth_base, predicted_base = paired_rollouts(model, actions_const)
truth_changed, predicted_changed = paired_rollouts(model, actions_alt)
delta_true = truth_changed[-1] - truth_base[-1]
delta_model = predicted_changed[-1] - predicted_base[-1]
intervention_responses[name] = (float(delta_true), float(delta_model))
intervention_error[name] = float(abs(delta_model - delta_true))Delayed (a=0.95, c=0.20): true response -1.832359; model response -2.477563; final response error 0.645204 Conservative (a=0.80): true response -1.832359; model response -0.611030; final response error 1.221329 Aggressive (a=1.02): true response -1.832359; model response -5.920590; final response error 4.088231
This response metric and the alternating-trajectory RMSE answer different questions, even if they select the same winner here. Equal additive trajectory biases cancel in the difference, but general dynamical errors need not. The metric tests these two known simulator interventions only; it does not certify unseen interventions or an observationally learned causal model.
Rung four is closed-loop planning. We use a one-step MPC that searches 201 equally spaced actions in and minimizes squared predicted next-state distance to . It then executes the chosen action in the true deterministic environment and reobserves the state. This costs 201 candidate one-step predictions per control step, with no action penalty and no lookahead beyond one step. The grid enforces the action constraint by construction; no state-safety constraint is imposed.
U_GRID = np.linspace(-0.5, 0.5, 201)
def mpc_choose(model, s, u_prev, target):
preds = model["a"] * s + model["b"] * U_GRID + model["c"] * u_prev
idx = int(np.argmin((preds - target) ** 2))
return float(U_GRID[idx])
def closed_loop(model, s_start, target, steps, a_env, b_env):
s = s_start
u_prev = 0.0
traj = [s]
for _ in range(steps):
u = mpc_choose(model, s, u_prev, target)
s = a_env * s + b_env * u
traj.append(s)
u_prev = u
return np.array(traj)
STEPS = 40
TARGET = 1.0
closed_loop_error = {}
for name, model in MISS.items():
traj = closed_loop(
model,
s_start=0.0,
target=TARGET,
steps=STEPS,
a_env=A_TRUE,
b_env=B_TRUE,
)
tail = traj[-10:]
closed_loop_error[name] = float(np.mean(np.abs(tail - TARGET)))
transfer_error = {}
A_SHIFT = 0.80
for name, model in MISS.items():
traj = closed_loop(
model,
s_start=0.0,
target=TARGET,
steps=STEPS,
a_env=A_SHIFT,
b_env=B_TRUE,
)
tail = traj[-10:]
transfer_error[name] = float(np.mean(np.abs(tail - TARGET)))Closed-loop tracking error (same environment):
Delayed (a=0.95, c=0.20) |s - s*| = 0.0200
Aggressive (a=1.02) |s - s*| = 0.0654
Conservative (a=0.80) |s - s*| = 0.1765
Transfer to a changed environment (a shifts from 0.95 to 0.80):
Conservative (a=0.80) |s - s*| = 0.0000
Aggressive (a=1.02) |s - s*| = 0.1803
Delayed (a=0.95, c=0.20) |s - s*| = 0.1875An idealized continuous-action controller helps explain the tracking bias. Assume deterministic dynamics, an inactive action bound, no action penalty, and one-step squared next-state tracking toward target . With , its unconstrained choice is
At a fixed point both state and action are constant. The true environment therefore satisfies , while the model's target equation becomes . Eliminating yields
where is the continuous controller's equilibrium, and are true environment coefficients, and are model coefficients. The denominator must be nonzero. This algebra finds an equilibrium, not a guarantee of stability or feasibility; those must be checked separately.
def continuous_equilibrium(model, a_env, b_env, target):
denominator = (model["b"] + model["c"]) * (1 - a_env) + b_env * model["a"]
return b_env * target / denominator
continuous_fixed_points = {
"same": {
name: continuous_equilibrium(model, A_TRUE, B_TRUE, TARGET)
for name, model in MISS.items()
},
"transfer": {
name: continuous_equilibrium(model, A_SHIFT, B_TRUE, TARGET)
for name, model in MISS.items()
},
}same continuous-action equilibria: Aggressive (a=1.02): 0.9345794393 Conservative (a=0.80): 1.1764705882 Delayed (a=0.95, c=0.20): 0.9803921569 transfer continuous-action equilibria: Aggressive (a=1.02): 0.8196721311 Conservative (a=0.80): 1.0000000000 Delayed (a=0.95, c=0.20): 0.8130081301
The continuous same-environment equilibria are approximately (Aggressive), (Conservative), and (Delayed). In transfer they are , , and . The lagged-action term remains in the transfer equation; dropping it would give the wrong Delayed equilibrium.
The implemented controller searches 201 discrete actions instead. It need not converge to those continuous equilibria: grid quantization can produce small periodic trajectories. We report the actual mean absolute tracking error over states 31 through 40, not an analytically inferred bias. Delayed has the lowest same-environment tail error, while Conservative has the lowest transfer tail error. Conservative is worst in the same-environment closed-loop comparison, not least accurate on every metric.
delayed_name = "Delayed (a=0.95, c=0.20)"
delayed_same_long = closed_loop(
MISS[delayed_name], 0.0, TARGET, 1000, A_TRUE, B_TRUE
)
delayed_transfer_long = closed_loop(
MISS[delayed_name], 0.0, TARGET, 1000, A_SHIFT, B_TRUE
)
delayed_same_40 = closed_loop(
MISS[delayed_name], 0.0, TARGET, STEPS, A_TRUE, B_TRUE
)
delayed_transfer_tail_mae = float(
np.mean(np.abs(delayed_transfer_long[-10:] - TARGET))
)Delayed actual state after 40 same-environment steps: 0.9794848891 Delayed last five same-environment states: [0.98050003 0.97897503 0.98002627 0.98102496 0.97947371] Delayed last two transfer states: [0.81527778 0.80972222] Delayed long-run transfer tail MAE: 0.1875000
From this initialization, Delayed approaches a five-step cycle in the same environment and a two-step cycle in transfer. The transfer cycle has states approximately and , with corresponding executed actions and ; its tail MAE is . These grid trajectories explain why a finite-run statistic differs from a continuous-action fixed point. Neither statistic is an architectural ranking.
Closing the loop also introduces a decision baseline. An oracle one-step controller using the true coefficients separates dynamics misspecification from action-grid and startup effects in this toy; a zero-action baseline shows the value of control. This is a diagnostic baseline, not a claim that oracle dynamics are available in deployments.
oracle_model = {"a": A_TRUE, "b": B_TRUE, "c": 0.0}
oracle_traj = closed_loop(oracle_model, 0.0, TARGET, STEPS, A_TRUE, B_TRUE)
oracle_tail_mae = float(np.mean(np.abs(oracle_traj[-10:] - TARGET)))
zero_action_tail_mae = float(np.mean(np.abs(np.zeros(10) - TARGET)))True-dynamics one-step controller tail MAE: 0.0002316 Zero-action baseline tail MAE: 1.0000000
The actual grid-controller trajectories show the residual tracking errors.
closed_loop_trajs = {}
for name, model in MISS.items():
closed_loop_trajs[name] = closed_loop(
model,
s_start=0.0,
target=TARGET,
steps=STEPS,
a_env=A_TRUE,
b_env=B_TRUE,
)
Now compare rankings without dividing by a near-zero winner error. The transfer winner's error is almost zero, so dividing other models' errors by it would create enormous ratios and hide differences on the other tests. We show within-test ranks, with 1 best. Ranks discard the size of the gaps; the printed raw metrics retain that information. Do not sum the ranks into a universal score.

The pattern is exactly what the ladder is designed to expose: different rungs can favour different winners.
- One-step MSE favors Aggressive on the stated independent Gaussian inputs.
- Constant-input rollout favors Delayed at the specified 40-step horizon, despite its incorrect gain.
- The response-difference metric favors Delayed for this baseline/intervention pair. The additional alternating-trajectory test is not the same metric.
- Same-environment grid control also favors Delayed; the example does not show a winner reversal between that rollout and that controller.
- Transfer favors Conservative because its coefficients match the changed environment more closely. This is no general guarantee of robust transfer.
These rankings are conditional on the test distribution, action sequence, horizon, initial state, and scoring rule. A scalar chosen for a declared deployment objective can be useful. Report that choice explicitly, and retain the other diagnostics when they describe additional intended uses.