Skip to content

ltm: mapped-dimension sliced reducers are not hoisted into aggregate nodes (compute_read_slice declines a remapped iterated axis) #534

Description

@bpowers

Summary

compute_read_slice in src/simlin-engine/src/ltm_agg.rs (added in Phase 4 / #514) decides whether an inlined array-reducer subexpression is hoistable into a synthetic $⁚ltm⁚agg⁚{n} node by computing, per source axis, an AxisRead ∈ {Pinned(elem), Iterated(dim), Reduced}. For the Iterated case -- a subscript index that names one of the enclosing (apply-to-all) equation's iterated dimensions in the position matching the source's declared dimension order -- the index dimension must match the source's row-axis dimension. As resolved by code review (commit 7f837928), the iterated-index branch now returns None (not hoistable) when the index matches the source's row axis only via a dimension mapping rather than literally being the same dimension -- e.g. SUM(matrix[State,*]) inside an apply-to-all-over-State body, where matrix is declared matrix[Region,D2] and there is a State→Region mapping.

So a mapped-dimension sliced reducer falls back to the conservative full-cross-product element-graph path (Direct Wildcard reference → emit_edges_for_reference's cross-product arm, a …→{from} per-shape link score) instead of being hoisted into a synthetic agg with read-slice-driven element edges and per-read-row link scores. (Whole-extent reducers SUM(matrix[*,*]), literally-same-dim sliced reducers SUM(matrix[D1,*]) over matrix[D1,D2], and pinned-slice reducers SUM(matrix[NYC,*]) are all still hoisted -- this is specifically the mapped-iterated case that is declined.)

The reason: the Iterated-axis-driven agg-routing machinery -- emit_agg_routed_edges (db_analysis.rs), emit_source_to_agg_link_scores / read_slice_rows, emit_agg_to_target_link_scores (db_ltm.rs) -- assumes the agg's result axis (recorded in AggNode::result_dims) and the arrayed source's row axis are literally the same dimension. Honoring a mapping would require the Iterated AxisRead (and result_dims) to carry both the target-equation dim and the source-row dim, and to remap between them in all three emitters (the element-graph fan-out, the source→agg link-score row enumeration, and the agg→target link-score fan-out). Rather than thread a (target dim, source dim) pair through everywhere for a case no current model exercises, Phase 4 declined the mapped axis. Removing the mapping handling also left compute_read_slice's DimensionsContext parameter dead, so it was dropped (see commit 7f837928).

Why it matters

Low. It's a conservatism, never a crash: a mapped-dimension sliced reducer gets less-precise LTM scoring (the conservative full-cross-product element graph + a lumped per-shape link score) instead of the fine-grained per-read-row aggregate edges + per-row reducer link scores. The conservative path is still correct (more edges than necessary, never fewer; the spurious ones get effectively-zero runtime scores) -- just imprecise, the same flavor of over-approximation as the pre-Phase-4 wildcard-link-score path for all sliced reducers (#514).

Components affected

  • src/simlin-engine/src/ltm_agg.rs -- compute_read_slice (the mapped-iterated-axis branch that returns None; ~the Iterated recognition logic, post-ltm: sliced reducer sub-expressions are not hoisted into aggregate nodes (stay on the wildcard-link-score path) #514), AggNode::read_slice / AxisRead / result_dims (the descriptor that would need the Iterated variant to carry a (target dim, source dim) pair)
  • src/simlin-engine/src/db_analysis.rs -- emit_agg_routed_edges (assumes result_dims ⊆ the source's row dims by name; would need to remap an Iterated axis from the source's declared dim to the target's iterated dim when fanning source[<...iterated...>] → agg[<iterated>] and agg[<iterated>] → to[e])
  • src/simlin-engine/src/db_ltm.rs -- emit_source_to_agg_link_scores / read_slice_rows / ReadSliceRow (the source→agg per-read-row link-score emitter; the row enumeration would need the mapping to know which source elements map to which agg result slot), emit_agg_to_target_link_scores (the agg→target half)

Possible approaches

Discovery context

Identified during the Phase 4 code review of the LTM arrays hardening epic (#488; commit 7f837928 on branch ltm-arrays-hardening; design plan and per-phase tasks under docs/implementation-plans/2026-05-11-ltm-arrays-hardening/, see phase_04.md -- the sliced-reducer-hoisting work). The mapped-axis carve-out was the deliberate resolution of a code-review IMPORTANT issue: the Iterated-axis machinery assumes the agg result axis and the source row axis are the same dimension, and Phase 4 chose to decline the mapped axis rather than thread a (target dim, source dim) pair through all three emitters for an unexercised case. Not a regression -- no sliced reducer was hoisted at all before Phase 4.

Tracking

Part of LTM tracking epic #488. Related to: #514 (sliced-reducer hoisting -- the Phase-4 work; distinct: #514 was about whether sliced reducers are hoisted at all in the un-mapped case, this is the residual mapped-dimension carve-out within that work), #527 (expand_same_element over-conservative for mapped dims in direct references -- same "honor the mapping in the element correspondence" requirement, different code path: db_analysis.rs element-edge count vs ltm_agg.rs whether-to-hoist; could be folded together), #528 (agg→target link-score over-subscripts an arrayed synthetic agg -- another arrayed-synthetic-agg edge case from the same Phase-4 work), #520 (unify the reference-site walkers), and the LTM array-support umbrella #273.

Activity

  1. added
    ltmLoops that Matter (LTM) analysis subsystem
    on May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    ltmLoops that Matter (LTM) analysis subsystem

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions