The question
Should a ceteris-paribus partial hold TIME at its previous value?
Today it does not. db/ltm/compile.rs (three sites, each
|| matches!(effective, "time" | "dt" | "initial_time" | "final_time")) skips those idents during
dependency processing, and the ceteris-paribus wrap freezes dependencies. So for a target whose
equation reads time() — directly or through STEP/RAMP/PULSE — the partial advances the clock
while holding every model input back, and the target's autonomous time evolution is attributed to
the link.
Why the reference implies TIME should be frozen
docs/reference/ltm--loops-that-matter.md:231-233 defines the quantity as
Delta_x(z) = f(x_current, y_previous) - z_previous: the partial change in z due to x alone,
computed by re-evaluating f with the current value of x but the previous values of all other
inputs (ceteris paribus)
The document never mentions TIME, so this is an inference from the formula rather than a quotation.
But the formula only closes if "all other inputs" covers everything f reads that varies. Take a
source x that does not appear in f: then f(x_current, y_previous) reduces to f(y_previous),
and the anchor z_previous is f at the previous step's inputs, so Delta_x(z) = 0 identically.
Exclude TIME and that identity fails — a source with no influence whatsoever scores nonzero, which
also contradicts the reference's own edge case at :249 ("If x does not change, the link score is
0").
Evidence that this is what is happening
Measured on C-LEARN v77 with LTM enabled, by zeroing the arms whose model dependencies are all
frozen and diffing all 30,123 result slots × 251 saved steps:
- 35 of 35 link-score variables whose values move carry a live time-dependent term in their
partial. No exceptions.
- 26 of them sit at exactly ±1.0 — which for a fully-frozen arm can only mean
|N| = |Δz|, i.e.
the entire change in the target came from something the freeze did not reach. In a fully-frozen
partial the only such thing is the clock.
- The competing explanation for unit magnitude does not apply: the single-live-input case
(docs/design/ltm--loops-that-matter.md:391, "for a single-input black box z = F(x) all of Δz
is attributable to x") requires a live input, and these arms have none.
Two independent derivations converge: the algebra above, and the runtime measurement.
Blast radius
This is a scoring-semantics change, not a refactor. On C-LEARN, 6,975 of 16,967 link-score
arms (41.1%) retain a live time-dependent term (time 7,812 occurrences, step 330, time_step
74, final_time 12, ramp 4). Freezing TIME would move roughly two in five link scores: the
fully-frozen ones to exactly 0, and the rest to a different, clock-free magnitude.
The guard template contributes none of these — its TIME/INITIAL_TIME are bare globals, not calls
— so every occurrence is inside a partial.
Link scores feed loop scores and dominance ranking, and relative loop scores are normalized over a
partition, so a changed magnitude moves the denominator every sibling score is divided by. Validation
therefore has to compare dominance rankings, not epsilon-compare series.
What is unmeasured
- Nobody has implemented TIME-freezing or measured the resulting scores. What exists is the arm
count it unblocks (measured) and the partial count it touches (measured) — not the new values,
and not whether any dominance ranking flips.
- No claim is made about what Vensim or Stella do here. That is an external-tool claim and it is
unverified. If it should be settled against the tools rather than against the formula, the
ground-truth runs under test/ are the instrument, and per this repo's standing rule an
unverified claim about another tool must not carry the design decision.
- The live-time census is a
PREVIOUS-depth-tracking text scan of generated arms, consistent with
the AST-level evidence but not itself an AST walk.
Why it also gates a large performance win
#977 (arm materialization on arrayed targets) can only omit arms whose partial is provably
PREVIOUS(target). On C-LEARN that is 4,036 arms today; 5,035 further arms are blocked solely by a
live time(). Under a TIME-freezing convention those become zero by construction and omission
stops being an approximation — taking that fix from −17.4% of the flow program to roughly −42%.
The two questions are the same question: those 5,035 arms are either scoring the clock (and are
wrong today) or they are not (and must keep their values). The performance consequence follows from
the correctness answer, not the other way round, and must not be allowed to motivate it.
Related
The question
Should a ceteris-paribus partial hold TIME at its previous value?
Today it does not.
db/ltm/compile.rs(three sites, each|| matches!(effective, "time" | "dt" | "initial_time" | "final_time")) skips those idents duringdependency processing, and the ceteris-paribus wrap freezes dependencies. So for a target whose
equation reads
time()— directly or throughSTEP/RAMP/PULSE— the partial advances the clockwhile holding every model input back, and the target's autonomous time evolution is attributed to
the link.
Why the reference implies TIME should be frozen
docs/reference/ltm--loops-that-matter.md:231-233defines the quantity asThe document never mentions TIME, so this is an inference from the formula rather than a quotation.
But the formula only closes if "all other inputs" covers everything
freads that varies. Take asource
xthat does not appear inf: thenf(x_current, y_previous)reduces tof(y_previous),and the anchor
z_previousisfat the previous step's inputs, soDelta_x(z) = 0identically.Exclude TIME and that identity fails — a source with no influence whatsoever scores nonzero, which
also contradicts the reference's own edge case at
:249("If x does not change, the link score is0").
Evidence that this is what is happening
Measured on C-LEARN v77 with LTM enabled, by zeroing the arms whose model dependencies are all
frozen and diffing all 30,123 result slots × 251 saved steps:
partial. No exceptions.
|N| = |Δz|, i.e.the entire change in the target came from something the freeze did not reach. In a fully-frozen
partial the only such thing is the clock.
(
docs/design/ltm--loops-that-matter.md:391, "for a single-input black boxz = F(x)all ofΔzis attributable to
x") requires a live input, and these arms have none.Two independent derivations converge: the algebra above, and the runtime measurement.
Blast radius
This is a scoring-semantics change, not a refactor. On C-LEARN, 6,975 of 16,967 link-score
arms (41.1%) retain a live time-dependent term (
time7,812 occurrences,step330,time_step74,
final_time12,ramp4). Freezing TIME would move roughly two in five link scores: thefully-frozen ones to exactly 0, and the rest to a different, clock-free magnitude.
The guard template contributes none of these — its
TIME/INITIAL_TIMEare bare globals, not calls— so every occurrence is inside a partial.
Link scores feed loop scores and dominance ranking, and relative loop scores are normalized over a
partition, so a changed magnitude moves the denominator every sibling score is divided by. Validation
therefore has to compare dominance rankings, not epsilon-compare series.
What is unmeasured
count it unblocks (measured) and the partial count it touches (measured) — not the new values,
and not whether any dominance ranking flips.
unverified. If it should be settled against the tools rather than against the formula, the
ground-truth runs under
test/are the instrument, and per this repo's standing rule anunverified claim about another tool must not carry the design decision.
PREVIOUS-depth-tracking text scan of generated arms, consistent withthe AST-level evidence but not itself an AST walk.
Why it also gates a large performance win
#977(arm materialization on arrayed targets) can only omit arms whose partial is provablyPREVIOUS(target). On C-LEARN that is 4,036 arms today; 5,035 further arms are blocked solely by alive
time(). Under a TIME-freezing convention those become zero by construction and omissionstops being an approximation — taking that fix from −17.4% of the flow program to roughly −42%.
The two questions are the same question: those 5,035 arms are either scoring the clock (and are
wrong today) or they are not (and must keep their values). The performance consequence follows from
the correctness answer, not the other way round, and must not be allowed to motivate it.
Related
freeze_pinned_body/reducer_body_ctx_partsexclude TIME, breaking thePREVIOUS(agg)anchor)