Skip to content

compiler: Linearize Indexeds nested in other Indexeds' indices - #3065

Draft
ggorman wants to merge 1 commit into
mainfrom
fix-linearize-nested-guard
Draft

ggorman wants to merge 1 commit into
mainfrom
fix-linearize-nested-guard

Conversation

@ggorman

@ggorman ggorman commented Oct 16, 2026

Copy link
Copy Markdown
Contributor

Uxreplace does not recurse into a replacement, so T[0, I[0, 0, 0], 0] in a guard kept I n-dimensional while its pointer was flattened, a compile error. Nested substitutions are now resolved innermost first, both when generating FIndexeds and when binding macros.

Test: test_linearize.py::test_nested_indexeds_in_guard (macro and values); reverting either site fails it.

Both this PR and #3064 append to the bottom of test_linearize.py; whichever lands second needs a trivial rebase.

🤖 Generated with Claude Code

Uxreplace does not recurse into a replacement, so an Indexed nested in the
indices of another one (e.g. in a guard `T[0, I[0, 0, 0], 0] < 1.5`) was left
in its n-dimensional form while its pointer was flattened, which does not
compile. Resolve the nested substitutions innermost first, both when
generating FIndexeds and when binding them to macros
@codecov

codecov Bot commented Oct 16, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 84.23%. Comparing base (2707ac9) to head (c8aa2c7).
⚠️ Report is 7 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #3065   +/-   ##
=======================================
  Coverage   84.23%   84.23%           
=======================================
  Files         258      258           
  Lines       57070    57091   +21     
  Branches     4874     4877    +3     
=======================================
+ Hits        48072    48091   +19     
- Misses       8170     8172    +2     
  Partials      828      828           
Flag Coverage Δ
pytest-gpu-aomp-amdgpuX 68.52% <100.00%> (+<0.01%) ⬆️
pytest-gpu-gcc- 79.00% <100.00%> (+<0.01%) ⬆️
pytest-gpu-icx- 78.93% <100.00%> (+<0.01%) ⬆️
pytest-gpu-nvc-nvidiaX 69.09% <100.00%> (+<0.01%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

# Uxreplace doesn't recurse into a replacement, so the Indexeds nested in
# another's indices are substituted here, innermost first. E.g.,
# `T[0, I[0, 0, 0], 0] -> TL0(0, IL0(0, 0, 0), 0)`
for i in sorted(subs, key=lambda i: len(retrieve_indexed(i.indices, deep=True))):

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's the wrong fix, Uxreplace should be fixed instead.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed the root cause is in uxreplace: _uxreplace (symbolics/manipulation.py:80-83) returns a matched key's replacement verbatim, like xreplace, so with T[0, I[0,0,0], 0] -> TL0(0, I[0,0,0], 0) the inner I[0,0,0] (also a key) is never visited.

The catch is that any fix there turns the mapper from a simultaneous substitution into a transitive one somewhere, and uxreplace has ~86 call sites here and ~60 in PRO, so I'd like your view before picking one. I prototyped two (with the call-site resolution in this PR reverted, both fix the reported case and 3-level nesting, and the same set of suites passes, ~1230 tests):

  • A. General: on a match, keep substituting inside the replacement's args (the matched key masked so {x: x+1} still terminates). Changes simultaneous semantics, e.g. {x: y+1, y: x+1} on a[x,y,z] gives a[x+2, y+2, z] instead of a[y+1, x+1, z]; ~7% slower on a uxreplace micro-benchmark.
  • B. Indexed only: register _uxreplace for Indexed, and only when key and replacement are both Indexed substitute inside the replacement's indices. Everything else unchanged; the one behaviour change is {a[x,y,z]: b[x,y,z], x: y} on a[x,y,z] now giving b[y,y,z] (was b[x,y,z]). No measurable cost.
  • C. Opt-in: A's mechanics behind Uxreplace(subs, nested=True) (Transformer already has a nested kwarg), used only by linearization. No change for anyone else, a bit more plumbing.

I'd lean to B, but you know the callers better. Which would you prefer?

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants