Gate MLX negative-stride materialization before v0.32.0 - #2259
Open
cetagostini wants to merge 9 commits into
Open
Gate MLX negative-stride materialization before v0.32.0#2259cetagostini wants to merge 9 commits into
cetagostini wants to merge 9 commits into
Conversation
Port the JAX scan dispatch to MLX. As MLX has no native general-scan primitive, the inner fgraph is driven by a Python carry loop that `mx.compile` unrolls. Because scalar values are not readable while MLX traces, the (full-sized) recurring buffers are used to infer the number of steps, falling back to a constant `n_steps` or the sequence length. Covers seqs, MIT-SOT, SIT-SOT, NIT-SOT, untraced SIT-SOT, MIT-MOT (scan gradients) and non-sequences, mirroring the JAX semantics of recreating the trace and prepending/truncating to the buffer size. Gradients over sequences reverse the trace and currently trip a separate MLX bug (an elementwise op fed by a negative-stride array is miscompiled under `mx.compile`); this is captured as a strict xfail under the full `mode="MLX"` and is addressed by a follow-up. Co-authored-by: Cursor <cursoragent@cursor.com>
Clarify in the dispatch that the Python carry loop is unrolled into the graph at trace time (not by `mx.compile`) and that this is a workaround until MLX exposes a native scan/while primitive (ml-explore/mlx#1441): it needs a static step count and the graph grows as O(n_steps * inner_ops), where a real primitive would keep it O(inner_ops). Co-authored-by: Cursor <cursoragent@cursor.com>
`mx.compile` miscompiles an elementwise op fed by a negative-stride view (`mx.compile(lambda x: 2.0 * x[::-1])` zeroes the trailing entries; eager is correct). The MLX Subtensor dispatch now copies reversed slices into a contiguous array. This unblocks Scan gradients over sequences (which reverse the trace) under the full `mode="MLX"`, and resolves the existing strict xfail `test_mlx_IncSubtensor_negative_step_slice_grad` — whose failure was the same negative-stride read feeding the elementwise gradient term, not the IncSubtensor write it was attributed to (ml-explore/mlx#3716). Co-authored-by: Cursor <cursoragent@cursor.com>
cetagostini
force-pushed
the
mlx-subtensor-negative-stride
branch
from
June 26, 2026 10:33
273b91c to
85e3f6d
Compare
ricardoV94
reviewed
Jun 26, 2026
ricardoV94
left a comment
Member
There was a problem hiding this comment.
There are some edge cases of Scan packaged as reusable tests that you can pass mode to, you should try them
Contributor
Author
|
I'll redo this if mlx patch get merge: ml-explore/mlx#3772 |
check_aliased_inner_outputs passes as-is (static and dynamic shapes). check_higher_order_derivative is correct with use_compile=False but is a strict xfail under mx.compile: the second-order backward Scan reads the forward trace through negative-stride Subtensor views, hitting the same MLX miscompilation already xfailed for first-order sequence grads. The shared check gains an rtol knob since MLX casts float64 to float32. check_grad_until_and_truncate_sequence_taps needs a while Scan, which the MLX dispatch (like JAX) does not support. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cetagostini
marked this pull request as ready for review
July 28, 2026 15:07
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
MLX v0.32.0 fixed compiled-kernel correctness for negative-strided inputs (#3720). This keeps the
Subtensormx.contiguousworkaround only for negative-step slices on MLX versions before 0.32.0, so older MLX releases remain correct while fixed releases avoid an unnecessary materialization.Reproducer and version evidence
The v0.32.0 release notes list “Fix compiled kernel correctness for negative-strided inputs” (#3720). PyTensor does not declare an MLX minimum and its MLX tests use unversioned
pytest.importorskip; more concretely, the current MLX CI job installsmlx<0.29.4(0.29.3), so it requires the compatibility branch. CI exercises the pre-0.32.0 materialization path; the recorded MLX 0.32.0 test run below exercises the fixed no-copy path.The regressions run on every MLX version: first- and second-axis negative steps, a non-unit negative step, and simultaneous reversal of both axes, each feeding an elementwise op under
mode="MLX"; the previously-xfailedIncSubtensornegative-step gradient path also passes.Stacked dependency
This branch is stacked on #2258. Until #2258 merges, this PR's diff includes its Scan dispatch and reusable
ScanCompatibilityTestscoverage; please review #2258 first, then this focused Subtensor change. Ricardo's requested Scan edge cases are present and run withmode="MLX": higher-order derivatives and aliased inner outputs.Test plan
mx.compile(lambda x: 2.0 * x[::-1]): RED on MLX 0.31.2 and GREEN on 0.32.0.mx.contiguouson MLX 0.31.2.PYTENSOR_FLAGS="base_compiledir=/Users/carlostrujillo/Documents/GitHub/_worktrees/pr-2259/.pytensor_compile" /opt/anaconda3/envs/pytensor-pr-2259/bin/pytest -q tests/link/mlx/test_subtensor.py tests/link/mlx/test_scan.pyon MLX 0.32.0 — 41 passed, 2 pre-existing xfailed (boolean indexing and inplace operations)./opt/anaconda3/envs/pytensor-pr-2259/bin/ruff checkandruff format --checkon the touched files.🤖 Generated with assistance from Cursor.
Made with Cursor