Skip to content

engine: VECTOR ELM MAP with scalar source + arithmetic (expression) offset fails to compile (no diagnostic) -- narrows the Phase 5 vector.xmile gate #578

@bpowers

Description

@bpowers

Problem

VECTOR ELM MAP with a scalar source and an arithmetic (expression) offset fails to compile with NotSimulatable: "failed to compile fragments for variables: y" and no model-level diagnostic (it fails silently at the assembly stage rather than emitting a parse/compile error).

The concrete pattern is in test/sdeverywhere/models/vector/vector.mdl:49 (and the equivalent test/sdeverywhere/models/vector/vector.xmile:40):

DimX : one, two, three, four, five
x[DimX] = 1, 2, 3, 4, 5
y[DimA] = VECTOR ELM MAP(x[three], (DimA - 1))

Here the source x[three] is a single fully-collapsed element (scalar, not an array view), and the offset (DimA - 1) is an arithmetic Op2(Sub, DimA, 1) dimension-position expression (not a static subscript).

Root cause (verified against the pipeline)

VECTOR ELM MAP derives its result-array shape from the offset argument's view via find_expr_array_view in src/simlin-engine/src/compiler/mod.rs:872-879:

fn find_expr_array_view(expr: &Expr) -> Option<ArrayView> {
    match expr {
        Expr::StaticSubscript(_, view, _) | Expr::TempArray(_, view, _) => Some(view.clone()),
        Expr::App(builtin, _) => match builtin {
            BuiltinFn::VectorElmMap(_, offset) => find_expr_array_view(offset),
            ...

find_expr_array_view only yields a view for Expr::StaticSubscript / Expr::TempArray offsets. For y:

  • the source x[three] is scalar (no array view), and
  • the offset (DimA - 1) is an arithmetic expression, so find_expr_array_view returns None for it.

With no view derivable, the hoister never builds the AssignTemp per-element fragment for y, and assembly later reports the missing fragment (silent until the assembly stage, db.rs ~4979-4983). Verified by the Phase 5 spike with a minimal 4-variable repro (DimA, DimX, x[DimX]=1..5, y[DimA] = VECTOR ELM MAP(x[three], (DimA - 1))).

How this differs from already-tracked ELM MAP issues

This is distinct from and larger than the closed cross-dimension ELM MAP work and the Phase 5 VM base+stride correction:

Fixing this requires VECTOR ELM MAP to (a) derive its result shape when the offset is an arbitrary scalar-valued expression evaluated per result element, (b) evaluate the per-element offset (position(DimA) - 1, recomputed for each DimA element) -- the current ELM MAP hoist models only a single whole-array offset view, and (c) support a scalar source broadcast. This touches find_expr_array_view / the expand_arrayed_with_hoisting shape inference in src/simlin-engine/src/compiler/mod.rs (around 872-879 and ~1598-1650), not just the VM view push.

Why it matters / current handling

Phase 5 Task 6 of the element-level cycle resolution work un-excludes test/sdeverywhere/models/vector/vector.xmile as a genuine-Vensim regression gate. Because y does not compile, Phase 5 (following the phase file's own Task 6 guidance -- "prefer full inclusion; only narrow with a tracked issue if a non-ELM-MAP-fixable variable genuinely resists a general fix") will include vector.xmile but narrow the gate to exclude the y variable, with this issue as the tracking reference.

The genuine-Vensim expected output for y (from real-Vensim test/sdeverywhere/models/vector/vector.dat:195-203) is y[A1]=3, y[A2]=4, y[A3]=5. Closing this issue (scalar-source / expression-offset ELM MAP shape inference + per-element offset evaluation) lets y be added back to the gate and the vector.xmile comparison fully un-narrowed.

Components affected

  • src/simlin-engine/src/compiler/mod.rs -- find_expr_array_view (lines 872-879) and expand_arrayed_with_hoisting shape inference (~1598-1650)
  • src/simlin-engine/src/compiler/codegen.rs -- ELM MAP source/offset view emission (per-element expression-offset evaluation)
  • src/simlin-engine/src/vm.rs -- ELM MAP dispatch (if per-element offset evaluation needs a runtime path)
  • test/sdeverywhere/models/vector/vector.mdl:49 / test/sdeverywhere/models/vector/vector.xmile:40 -- the y definition (fixture)
  • test/sdeverywhere/models/vector/vector.dat:195-203 -- genuine-Vensim expected y values (y[A1]=3, y[A2]=4, y[A3]=5)
  • The Phase 5 vector.xmile gate (narrowed to exclude y until this is closed)

Possible approaches

  1. Extend find_expr_array_view (or the surrounding expand_arrayed_with_hoisting shape inference) so an ELM MAP whose offset is a scalar-valued expression derives its result shape from the target iteration dimension (the LHS DimA) rather than only from a StaticSubscript/TempArray offset view.
  2. Add a per-element-evaluated offset path for ELM MAP: evaluate offset once per result element (e.g. position(DimA) - 1 per DimA), instead of modeling only a single whole-array offset view.
  3. Support a scalar source operand for ELM MAP (broadcast / single-element source) so x[three] is a valid source.

Context

Discovered during the Phase 5 Task 1 spike of the element-level cycle resolution work (branch clearn-hero-model; spike commit 00f25d1f). Full analysis and reproduction in docs/implementation-plans/2026-05-18-element-cycle-resolution/phase_05_spike_findings.md (section 6). The spike verified the root cause by reading the compilation pipeline and running a minimal 4-variable repro through the engine with a temporary read-only diagnostic (reverted; no engine code changed by the spike). The now-stale src/simlin-engine/tests/simulate.rs:651-657 exclusion comment flagged this same y pattern but mis-attributed it to the VM incremental path; it actually fails at compile/assembly.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions