You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
mdl: reader keeps 3 more SIR.mdl connectors than xmutil #512
test_mdl_equivalence compared our MDL reader against xmutil on src/libsimlin/testdata/SIR.mdl and failed: we produced 16 connectors in the diagram, xmutil 13.
The 3 extra connectors are arrows drawn into the stock Susceptible Population S from the variables its initial value uses.
xmutil drops every connector that ends at a stock (XMILEGenerator.cpp, meant for flow pipes); our reader drops only flow pipes.
Ours is right: the arrows are in the sketch, and XMILE puts no restriction on a <connector>'s target (docs/reference/xmile-v1.0.html).
Status on main
xmutil, the xmutil feature and tests/mdl_equivalence.rs were deleted in #1080 (ea8367a).
The reader's checks are now Vensim's own output in test/, so nothing remains to fix here.
Related: #570 -- same class (an xmutil-feature-gated tests/mdl_equivalence.rs assertion that is not exercised by the binding pre-commit/CI cargo test), distinct instance. #512 is a pre-existing-on-main divergence in the view/sketch connector parser (src/simlin-engine/src/mdl/view/) on SIR.mdl in the non-ignored test_mdl_equivalence. #570 is a deliberately introduced (commit eae79426, branch clearn-hero-model) flow-decomposition divergence (mdl/convert/stocks.rs) for a rank-changing subscripted flow, whose only mdl_equivalence.rs surface is the #[ignore]d test_clearn_equivalence -- a binding follow-up for the B2 PR (#559). Whatever fix-or-policy resolves the 'xmutil-gated equivalence assertions are outside the canonical gate' root class for #512 should account for #570 too.
Still reproduces on main (base of PR #1040, compiler-unification-v2) under cargo test -p simlin-engine --all-features (which enables xmutil), with the same numbers: xmutil 29 elements / link=13, native 32 / link=16. Re-observed independently in three of that branch's review reports.
The 3 extra links
They are sketch arrows 42, 43, 44 in src/libsimlin/testdata/SIR.mdl, all pointing INTO the stock Susceptible Population S (sketch id 1):
1,42,2,1,0,0,0,0,0,64,1,-1--1--1,,1|(301,190)| Infectious Population I -> S
1,43,15,1,0,0,0,0,0,64,1,-1--1--1,,1|(423,190)| Recovered Population R -> S
1,44,24,1,0,0,0,0,0,64,1,-1--1--1,,1|(213,264)| Total Population P -> S
These are exactly S's INTEG initial-value inputs (SIR.mdl:90-92):
Susceptible Population S= INTEG (
-Infection Rate,
Total Population P - Infectious Population I - Recovered Population R)
They are not flow pipes (shape field is 0; pipe arrows in this file are shape 4/100 and originate at valves). They are the only arrows in the file with sketch field 10 = 1. The second SIR fixture in EQUIVALENT_MODELS, test/test-models/samples/SIR/SIR.mdl, has the identical pattern (arrow 21: Total Population -> stock Susceptible, shape 0, field10 = 1), so it will fail by 1 link as soon as the first fixture passes -- the test aborts at the first fixture, so every later fixture is currently unverified. What Vensim means by field 10 = 1 is unverified (plausibly its initial-value-arrow marker, since it co-occurs with to=stock in both fixtures); the diagnosis below does not depend on it.
Why the two importers disagree
xmutil drops every connector whose to variable is a stock, unconditionally: src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.cpp:1087-1090 (to->GetVariable()->VariableType() != XMILE_Type_STOCK gates "valid xmile connector"). That check exists to suppress flow-pipe arrows (valve -> stock, where xmutil has already redirected from to the flow variable), but it is broader than that and also eats initial-value arrows.
native drops connectors into stocks only when they are flow pipes: src/simlin-engine/src/mdl/view/convert.rs:827-836 (field4 == 4 || field4 == 100 || from_attached_valve || to_attached_valve), so ordinary causal arrows into a stock are kept as ViewElement::Links.
13 shared links + 3 initial-value links = 16.
Which side is right
The native parser. Checked against the sources rather than assumed:
The XMILE spec's <connector> definition (docs/reference/xmile-v1.0.html, display section) says only "to REQUIRED -- The name of the model entity to which this connector ends"; it places no restriction on the target type. Connectors into stocks are legal XMILE.
The arrows are genuinely declared in the Vensim sketch and correspond one-for-one to real initial-value dependencies of the stock. Dropping them is lossy: a model roundtripped through the xmutil path loses arrows the author drew.
So this is an xmutil (reference-side) limitation, not a native-parser bug, and the native parser should NOT be changed to match.
Fix options
Fix the reference: narrow xmutil's stock filter to to is stock AND from was an attached valve (mirroring the native rule), so only pipe arrows are suppressed. xmutil is test-only third-party code, so the blast radius is the xmutil-path fixtures; test/sdeverywhere xmutil-converted expectations would need re-checking.
Normalize in the test: in tests/integration/mdl_equivalence.rs, drop native links whose to is a stock before comparing, with a comment naming this xmutil limitation and pointing here. Cheapest; keeps the assertion honest about what it does and does not check.
Either way the comparison should then be run through the remaining 12 EQUIVALENT_MODELS fixtures, which have never been reached.
changed the title [-]engine: test_mdl_equivalence fails on main -- native MDL parser emits 3 extra link elements for SIR.mdl[/-][+]mdl: reader keeps 3 more SIR.mdl connectors than xmutil[/+]on Oct 12, 2026
Obsolete: xmutil, the xmutil feature and tests/mdl_equivalence.rs were deleted in ea8367a (#1080); no src/xmutil directory, Cargo feature or equivalence test remains on main. The divergence was xmutil dropping every connector into a stock, not a reader bug, so there is nothing left to compare against.
Activity
bpowers commentedon May 16, 2026
Related: #570 -- same class (an
xmutil-feature-gatedtests/mdl_equivalence.rsassertion that is not exercised by the binding pre-commit/CIcargo test), distinct instance. #512 is a pre-existing-on-maindivergence in the view/sketch connector parser (src/simlin-engine/src/mdl/view/) on SIR.mdl in the non-ignoredtest_mdl_equivalence. #570 is a deliberately introduced (commiteae79426, branchclearn-hero-model) flow-decomposition divergence (mdl/convert/stocks.rs) for a rank-changing subscripted flow, whose onlymdl_equivalence.rssurface is the#[ignore]dtest_clearn_equivalence-- a binding follow-up for the B2 PR (#559). Whatever fix-or-policy resolves the 'xmutil-gated equivalence assertions are outside the canonical gate' root class for #512 should account for #570 too.bpowers commentedon Sep 4, 2026
Root cause identified (static read, 2026-09-03)
Still reproduces on
main(base of PR #1040,compiler-unification-v2) undercargo test -p simlin-engine --all-features(which enablesxmutil), with the same numbers: xmutil 29 elements / link=13, native 32 / link=16. Re-observed independently in three of that branch's review reports.The 3 extra links
They are sketch arrows 42, 43, 44 in
src/libsimlin/testdata/SIR.mdl, all pointing INTO the stockSusceptible Population S(sketch id 1):These are exactly S's INTEG initial-value inputs (
SIR.mdl:90-92):They are not flow pipes (shape field is 0; pipe arrows in this file are shape 4/100 and originate at valves). They are the only arrows in the file with sketch field 10 = 1. The second SIR fixture in
EQUIVALENT_MODELS,test/test-models/samples/SIR/SIR.mdl, has the identical pattern (arrow 21:Total Population-> stockSusceptible, shape 0, field10 = 1), so it will fail by 1 link as soon as the first fixture passes -- the test aborts at the first fixture, so every later fixture is currently unverified. What Vensim means by field 10 = 1 is unverified (plausibly its initial-value-arrow marker, since it co-occurs with to=stock in both fixtures); the diagnosis below does not depend on it.Why the two importers disagree
tovariable is a stock, unconditionally:src/xmutil/third_party/xmutil/Xmile/XMILEGenerator.cpp:1087-1090(to->GetVariable()->VariableType() != XMILE_Type_STOCKgates "valid xmile connector"). That check exists to suppress flow-pipe arrows (valve -> stock, where xmutil has already redirectedfromto the flow variable), but it is broader than that and also eats initial-value arrows.src/simlin-engine/src/mdl/view/convert.rs:827-836(field4 == 4 || field4 == 100 || from_attached_valve || to_attached_valve), so ordinary causal arrows into a stock are kept asViewElement::Links.13 shared links + 3 initial-value links = 16.
Which side is right
The native parser. Checked against the sources rather than assumed:
<connector>definition (docs/reference/xmile-v1.0.html, display section) says only "toREQUIRED -- The name of the model entity to which this connector ends"; it places no restriction on the target type. Connectors into stocks are legal XMILE.So this is an xmutil (reference-side) limitation, not a native-parser bug, and the native parser should NOT be changed to match.
Fix options
to is stock AND from was an attached valve(mirroring the native rule), so only pipe arrows are suppressed. xmutil is test-only third-party code, so the blast radius is the xmutil-path fixtures;test/sdeverywherexmutil-converted expectations would need re-checking.tests/integration/mdl_equivalence.rs, drop native links whosetois a stock before comparing, with a comment naming this xmutil limitation and pointing here. Cheapest; keeps the assertion honest about what it does and does not check.Either way the comparison should then be run through the remaining 12
EQUIVALENT_MODELSfixtures, which have never been reached.[-]engine: test_mdl_equivalence fails on main -- native MDL parser emits 3 extra link elements for SIR.mdl[/-][+]mdl: reader keeps 3 more SIR.mdl connectors than xmutil[/+]bpowers commentedon Oct 12, 2026
Closing: already fixed on main.
Obsolete: xmutil, the
xmutilfeature andtests/mdl_equivalence.rswere deleted in ea8367a (#1080); nosrc/xmutildirectory, Cargo feature or equivalence test remains on main. The divergence was xmutil dropping every connector into a stock, not a reader bug, so there is nothing left to compare against.