Frontier Questions and Research Roadmaps › Experiment 2: Held-Out Combinations and When Factorization Fails
World Models HandbookAll chapters Frontier Questions and Research Roadmaps › Experiment 2: Held-Out Combinations and When Factorization FailsOne section · Sections in this chapter6 / 11 ↓
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
The second experiment asks whether a factorized representation can predict combinations of factors it never saw together. We construct a transition family in which the true interaction between two context factors is multiplicative, so that an additive model is structurally wrong, and then we add a stress condition in which even the multiplicative assumption fails.
The product law supplies a specific interaction that the additive feature family cannot represent exactly across the chosen factor levels. Even when an additive law is correct, its finite-data estimation and held-out predictions can still be tested. Here we use a normalized quadratic damping term, , and let the coefficient depend on two context factors; this is a declared toy law, not a universal physical drag model. The capped regime asks a separate question: how does the product-feature model behave when that law changes? A test supporting a model is not thereby rigged, but testing a violated assumption makes the boundary of the result explicit.
The Transition Family
The system is a scalar velocity with quadratic damping:
where:
- : scalar velocity at step
- : action (normalized acceleration) at step
- : integration timestep
- : damping coefficient determined by the surface factor and payload factor
- : surface factor level
- : payload factor level
- : additive noise at step
where is a surface factor, is a payload factor, and both take levels in . The coefficient is what we will try to learn. In the main regime,
In the stress regime, the damping saturates:
Here is the saturation ceiling: at high the damping stops growing, so the true law is no longer a pure product.
The stress regime specifies a capped coefficient, . This is a deliberate alternative law, not a claim that every physical damping coefficient saturates. The held-out products are 3 and 6, so the test includes uncapped and capped combinations. A pure-product feature family is therefore misspecified in this regime. The coefficients and factors here are normalized toy quantities; the experiment does not establish a physical stability margin.
Rather than fit a full transition network, we estimate the single scalar for each context by least squares, then study how meta-models map context to . This controls the feature families and makes the scalar identifiability condition explicit. It does not remove estimation error: the finite noisy identification samples produce uncertain coefficient labels, and that uncertainty propagates into the meta-model errors alongside approximation error. Known ground truth lets us measure those errors, not declare the identification step exact. A real system may also permit scalar fitting, or may require a richer model with additional measurement and model errors.
FACTOR_LEVELS = (1.0, 2.0, 3.0)
SEEN_COMBOS = [(1, 1), (1, 2), (2, 1), (2, 2), (3, 3)]
HELD_OUT_COMBOS = [(1, 3), (2, 3), (3, 1), (3, 2)]
DT_C = 0.2
KAPPA_CAP = 5.0
IDENT_NOISE = 0.4
N_SAMPLES = 60
def kappa_true(s, p, saturated):
k = float(s) * float(p)
return min(k, KAPPA_CAP) if saturated else k
def identify_kappa(s, p, saturated, rng, n_samples=N_SAMPLES):
v = rng.normal(scale=1.2, size=n_samples)
a = rng.uniform(-1.0, 1.0, size=n_samples)
k = kappa_true(s, p, saturated)
v_next = (
v
+ DT_C * (a - k * v * np.abs(v))
+ IDENT_NOISE * rng.normal(size=n_samples)
)
regressor = -DT_C * v * np.abs(v)
target = v_next - v - DT_C * a
return float(regressor @ target / (regressor @ regressor))The identification samples pairs independently rather than rolling the system forward. For this one-parameter least-squares fit, a unique coefficient estimate requires . More spread can improve information under the stated noise model, but a visual velocity range is not a general persistent-excitation test. Gaussian velocity samples have unbounded support; the code imposes no hard limit.
The Split and the Two Meta-Models
Every factor level appears in training. What is held out is specific combinations: , , , and . The training set contains the diagonal and two symmetric off-diagonal cells and .
The structure of the split is deliberate. Every level of and every level of appears in training, so the model has seen each factor individually. What it has not seen is the specific way high combines with high and vice versa. If the factors combine additively, the model can predict the held-out cells by extrapolating each factor's effect. If they combine multiplicatively, the model needs to know the product, and the training cells do not uniquely determine the product unless the model's functional form includes it. This is the sense in which the split tests the inductive bias rather than the data coverage.
Two meta-models map context to the damping coefficient:
- Factorized (additive) baseline. . No interaction term.
- Interaction-capable baseline. . Characterizes the multiplicative structure.
Both are fit by least squares on the training cells. We check the design matrix rank and condition number so we are not fitting an arbitrary solution.
def fit_additive(pairs):
X = np.array([[1.0, s, p] for s, p, _ in pairs])
y = np.array([k for _, _, k in pairs])
w, *_ = np.linalg.lstsq(X, y, rcond=None)
return (lambda s, p: w[0] + w[1] * s + w[2] * p), X
def fit_interaction(pairs):
X = np.array([[1.0, s, p, s * p] for s, p, _ in pairs])
y = np.array([k for _, _, k in pairs])
w, *_ = np.linalg.lstsq(X, y, rcond=None)
return (lambda s, p: w[0] + w[1] * s + w[2] * p + w[3] * s * p), X
_rng_check = np.random.default_rng(3)
_check_seen = [
(s, p, identify_kappa(s, p, False, _rng_check)) for s, p in SEEN_COMBOS
]
_, X_add = fit_additive(_check_seen)
_, X_int = fit_interaction(_check_seen)
print(
f"Additive design rank: {np.linalg.matrix_rank(X_add)} of {X_add.shape[1]}"
)
print(
f"Interaction design rank: {np.linalg.matrix_rank(X_int)} of {X_int.shape[1]}"
)
print(f"Interaction condition number: {np.linalg.cond(X_int):.1f}")Additive design rank: 3 of 3 Interaction design rank: 4 of 4 Interaction condition number: 58.4
Both design matrices have full column rank, so least-squares coefficients are uniquely determined within each selected feature family. The reported interaction-design condition number diagnoses sensitivity for that matrix. These checks support the earlier section's model-family-specific identification discussion; they are not prerequisites for every held-out-combination performance test and do not establish identifiability for arbitrary nonlinear models.
Before comparing meta-models, we check the scalar identification procedure against analytic truth on the held-out cells. This diagnostic measures its error in the chosen unsaturated contexts; it does not make finite noisy labels exact. Downstream error still combines identification noise with estimation and functional-form error. Keep that contribution visible rather than attributing every discrepancy to the meta-model's structure.
_rng_verify = np.random.default_rng(77)
verify_rows = []
for combo in HELD_OUT_COMBOS:
s, p = combo
est = np.mean([identify_kappa(s, p, False, _rng_verify) for _ in range(20)])
verify_rows.append((s, p, kappa_true(s, p, False), est))
print(f"{'s':>3}{'p':>3}{'true kappa':>13}{'identified':>13}")
for s, p, truth, est in verify_rows:
print(f"{s:>3}{p:>3}{truth:>13.3f}{est:>13.3f}")s p true kappa identified 1 3 3.000 2.984 2 3 6.000 5.982 3 1 3.000 3.028 3 2 6.000 6.009
These diagnostic averages track the analytic coefficients closely in the unsaturated regime. Identification noise still enters every fitted meta-model, so held-out error combines that noise, estimation variance, and functional-form mismatch. The repeated study below measures this combined variability rather than attributing every error exclusively to structure.
Running Both Regimes with Repeats
We repeat the whole procedure eight times with fresh identification noise, so the error bars capture variation from sampling, identification and meta-model fitting under this fixed split. They are standard errors of each model's mean, not standard deviations of individual runs. Assess a model difference against uncertainty in its estimated mean difference; it need not exceed individual-run variability. The models share each repeat's training data, so paired repeat differences would provide a direct uncertainty estimate for their comparison. The separate marginal bars below do not supply that estimate.
def run_composition_study(saturated, n_repeats=8, seed=0):
rng = np.random.default_rng(seed)
add_e = np.zeros((n_repeats, len(HELD_OUT_COMBOS)))
int_e = np.zeros_like(add_e)
seen_add_e = np.zeros((n_repeats, len(SEEN_COMBOS)))
seen_int_e = np.zeros_like(seen_add_e)
for r in range(n_repeats):
seen = [
(s, p, identify_kappa(s, p, saturated, rng)) for s, p in SEEN_COMBOS
]
f_add, _ = fit_additive(seen)
f_int, _ = fit_interaction(seen)
for j, (s, p) in enumerate(SEEN_COMBOS):
truth = kappa_true(s, p, saturated)
seen_add_e[r, j] = abs(f_add(s, p) - truth)
seen_int_e[r, j] = abs(f_int(s, p) - truth)
for j, (s, p) in enumerate(HELD_OUT_COMBOS):
truth = kappa_true(s, p, saturated)
add_e[r, j] = abs(f_add(s, p) - truth)
int_e[r, j] = abs(f_int(s, p) - truth)
return add_e, int_e, seen_add_e, seen_int_e
add_main, int_main, seen_add_main, seen_int_main = run_composition_study(
saturated=False, seed=101
)
add_stress, int_stress, seen_add_stress, seen_int_stress = (
run_composition_study(saturated=True, seed=202)
)
def summarize(e):
per_repeat = e.mean(axis=1)
return float(per_repeat.mean()), float(
per_repeat.std(ddof=1) / np.sqrt(len(per_repeat))
)
summary = {
("Factorized", "Seen, multiplicative"): summarize(seen_add_main),
("Interaction", "Seen, multiplicative"): summarize(seen_int_main),
("Factorized", "Seen, saturating"): summarize(seen_add_stress),
("Interaction", "Seen, saturating"): summarize(seen_int_stress),
("Factorized", "Held-out, multiplicative"): summarize(add_main),
("Interaction", "Held-out, multiplicative"): summarize(int_main),
("Factorized", "Held-out, saturating"): summarize(add_stress),
("Interaction", "Held-out, saturating"): summarize(int_stress),
}model regime mean |err| SEM Factorized Seen, multiplicative 0.450 0.005 Interaction Seen, multiplicative 0.072 0.005 Factorized Seen, saturating 0.321 0.012 Interaction Seen, saturating 0.312 0.011 Factorized Held-out, multiplicative 0.978 0.022 Interaction Held-out, multiplicative 0.131 0.016 Factorized Held-out, saturating 0.492 0.023 Interaction Held-out, saturating 0.629 0.016

Interpreting the Two Regimes
In the multiplicative regime the interaction model has much lower held-out coefficient error than the additive model. The table also reports error against analytic truth on seen combinations: training-side approximation error is part of this comparison, not something to assume away. The product feature represents the specified law, while the additive family cannot represent it over the full grid. Under the capped law that advantage does not persist. These results concern two chosen feature families and splits, not every factorized representation or a universal compositionality advantage.
Under saturation, the interaction model's held-out error increases, while the additive model's error decreases relative to its own multiplicative-regime result. The mean ranking reverses in this seeded study. Both remain misspecified: neither functional form represents the capped product exactly. This is not evidence that saturation always favors an additive model. The product feature has lower error under the product law here, and that advantage does not survive this cap. Misspecification alone does not establish operational unreliability: deciding whether either error is acceptable requires a task-specific tolerance. Test the target regime and report the measured errors against that requirement.
The practical implications are direct:
- Test compositional claims with held-out combinations and report seen and unseen separately.
- When interpreting recovered factor effects, establish identifiability under the chosen feature family and report its design rank. Keep this identification claim separate from measured held-out performance.
- Include a violation condition so that the reader knows what happens when the assumed structure breaks.
- Prefer a model whose inductive bias matches the regime you will deploy in. Justify that regime from known structure or an appropriately powered target-system study; a small trial need not detect rare or later changes.
A predeclared held-out comparison can be a valid test even when it supports the proposed method. Adding a violation condition asks a separate question: how does performance change when the assumed law no longer holds? The capped regime supplies that boundary check here; it does not make all positive-only studies invalid. For deployment, test the target regime and ask whether the measured errors meet its requirements rather than seeking an unconditional model ranking.
An illustrative controlled example with a known ground-truth transition family, not a benchmark result.