Summary
classify_iterated_dim_shape's mapped arm (src/simlin-engine/src/db/ltm_ir.rs, ~line 270) accepts a mapped iterated-dim subscript only when the mapping is declared on the target equation's iterated dimension toward the source's dimension -- has_mapping_to(index_dim, source_dim). When the mapping is declared in the REVERSE direction, on the source's dimension (Region→State, with x over Region, target over State), a SUBSCRIPTED reference target[State] = x[State] * c fails that check, classifies DynamicIndex, and keeps the conservative full Region × State cross-product of element edges -- even though the compiler's translate_via_mapping handles both declaration directions and resolves the reference through the same mapping diagonal (x[a]→target[s1], x[b]→target[s2]).
The BARE form in the reverse direction already gets the diagonal: target[State] = x * c classifies Bare (no subscript to inspect) and expand_same_element's mapping-aware projection (commit a010931e on ltm-core-batch, the #527 fix) consults DimensionsContext::mapped_element_correspondence, which handles both directions. So the residual asymmetry is: bare reverse-declared = diagonal, subscripted reverse-declared = cross-product, while the compiled simulation semantics are identical for both.
Current behavior is pinned by element_graph_mapped_reverse_declared_subscripted_stays_cross_product in src/simlin-engine/src/db/element_graph_tests.rs (~line 1371), which documents the classification ⟺ expansion agreement: the diagonal is only emitted for shapes the classifier calls Bare. The classifier arm's own comment (ltm_ir.rs ~line 262) records the direction restriction deliberately, to keep classification and expansion consistent rather than to model anything real.
Why it matters
Low severity -- over-conservatism only. The spurious cross-product edges carry near-zero runtime link scores (same flavor as pre-#448 FixedIndex N×N and pre-#527 mapped broadcast), so loops/edges are over-enumerated, never under-enumerated, and no scores are wrong. Only positional mappings are in scope: different-cardinality element-map references currently fail to compile outright (#753), so the element-map flavor of this asymmetry is moot until that is fixed.
Components affected
src/simlin-engine/src/db/ltm_ir.rs -- classify_iterated_dim_shape's mapped arm (the has_mapping_to(d_canon, src_canon) single-direction check, ~line 270)
src/simlin-engine/src/dimensions.rs -- DimensionsContext::mapped_element_correspondence already resolves both declaration directions; has_mapping_to is the one-directional primitive
src/simlin-engine/src/db/element_graph_tests.rs -- element_graph_mapped_reverse_declared_subscripted_stays_cross_product (~line 1371) pins current behavior and should flip to asserting the diagonal when fixed
Fix shape
Extend the classifier's mapped arm to accept the reverse declaration direction -- e.g. has_mapping_to(d, src) || has_mapping_to(src, d), or better, gate on the same mapped_element_correspondence(d, src).is_some() the expansion uses -- so classification and expansion consult the SAME correspondence data and stay consistent by construction (the invariant the current arm's comment is protecting). Then update the pinning test to assert the diagonal, mirroring element_graph_mapped_reverse_declared_bare_is_diagonal.
Discovery context
Identified during adversarial review of the #527 work on branch ltm-core-batch (commit a010931e, which made expand_same_element mapping-aware and covered the bare reverse-declared case). Residual conservatism, not a regression.
Tracking
Part of LTM tracking epic #488 (Core algorithm group, alongside #527). Related: #527 (the parent mapped-diagonal projection fix), #534 (mapped-dimension sliced reducers -- the reducer-path flavor of "honor the mapping"), #753 (different-cardinality element-map references fail to compile -- bounds this issue to positional mappings), #755 (mapped-diagonal loop-family grouping downstream of #527).
Summary
classify_iterated_dim_shape's mapped arm (src/simlin-engine/src/db/ltm_ir.rs, ~line 270) accepts a mapped iterated-dim subscript only when the mapping is declared on the target equation's iterated dimension toward the source's dimension --has_mapping_to(index_dim, source_dim). When the mapping is declared in the REVERSE direction, on the source's dimension (Region→State, withxoverRegion,targetoverState), a SUBSCRIPTED referencetarget[State] = x[State] * cfails that check, classifiesDynamicIndex, and keeps the conservative fullRegion × Statecross-product of element edges -- even though the compiler'stranslate_via_mappinghandles both declaration directions and resolves the reference through the same mapping diagonal (x[a]→target[s1],x[b]→target[s2]).The BARE form in the reverse direction already gets the diagonal:
target[State] = x * cclassifiesBare(no subscript to inspect) andexpand_same_element's mapping-aware projection (commita010931eonltm-core-batch, the #527 fix) consultsDimensionsContext::mapped_element_correspondence, which handles both directions. So the residual asymmetry is: bare reverse-declared = diagonal, subscripted reverse-declared = cross-product, while the compiled simulation semantics are identical for both.Current behavior is pinned by
element_graph_mapped_reverse_declared_subscripted_stays_cross_productinsrc/simlin-engine/src/db/element_graph_tests.rs(~line 1371), which documents the classification ⟺ expansion agreement: the diagonal is only emitted for shapes the classifier callsBare. The classifier arm's own comment (ltm_ir.rs ~line 262) records the direction restriction deliberately, to keep classification and expansion consistent rather than to model anything real.Why it matters
Low severity -- over-conservatism only. The spurious cross-product edges carry near-zero runtime link scores (same flavor as pre-#448 FixedIndex N×N and pre-#527 mapped broadcast), so loops/edges are over-enumerated, never under-enumerated, and no scores are wrong. Only positional mappings are in scope: different-cardinality element-map references currently fail to compile outright (#753), so the element-map flavor of this asymmetry is moot until that is fixed.
Components affected
src/simlin-engine/src/db/ltm_ir.rs--classify_iterated_dim_shape's mapped arm (thehas_mapping_to(d_canon, src_canon)single-direction check, ~line 270)src/simlin-engine/src/dimensions.rs--DimensionsContext::mapped_element_correspondencealready resolves both declaration directions;has_mapping_tois the one-directional primitivesrc/simlin-engine/src/db/element_graph_tests.rs--element_graph_mapped_reverse_declared_subscripted_stays_cross_product(~line 1371) pins current behavior and should flip to asserting the diagonal when fixedFix shape
Extend the classifier's mapped arm to accept the reverse declaration direction -- e.g.
has_mapping_to(d, src) || has_mapping_to(src, d), or better, gate on the samemapped_element_correspondence(d, src).is_some()the expansion uses -- so classification and expansion consult the SAME correspondence data and stay consistent by construction (the invariant the current arm's comment is protecting). Then update the pinning test to assert the diagonal, mirroringelement_graph_mapped_reverse_declared_bare_is_diagonal.Discovery context
Identified during adversarial review of the #527 work on branch
ltm-core-batch(commita010931e, which madeexpand_same_elementmapping-aware and covered the bare reverse-declared case). Residual conservatism, not a regression.Tracking
Part of LTM tracking epic #488 (Core algorithm group, alongside #527). Related: #527 (the parent mapped-diagonal projection fix), #534 (mapped-dimension sliced reducers -- the reducer-path flavor of "honor the mapping"), #753 (different-cardinality element-map references fail to compile -- bounds this issue to positional mappings), #755 (mapped-diagonal loop-family grouping downstream of #527).