Skip to content

fix(compile-dag): ComputeGraphExecutor replays permute with its recorded axes#803

Merged
michalharakal merged 1 commit into
developfrom
fix/graph-executor-permute-axes
Jul 9, 2026
Merged

fix(compile-dag): ComputeGraphExecutor replays permute with its recorded axes#803
michalharakal merged 1 commit into
developfrom
fix/graph-executor-permute-axes

Conversation

@michalharakal

Copy link
Copy Markdown
Contributor

What

ComputeGraphExecutor's builtin dispatch grouped permute with transpose/transpose2d and replayed all three as ops.transpose(inputs[0]) — a last-two-dims swap that silently drops the recorded axes and is only coincidentally correct for rank-2 tensors.

Why it went unnoticed

Any rank-3 permutation in a traced graph — e.g. multi-head attention's heads/sequence swap [1, 0, 2] on a multi-token (encoder or prefill) forward — produced the wrong layout with no error. Single-token decode never hits the permute path (swapSeqHeadDims takes the d0==1/d1==1 reshape shortcut), so generation workloads never surfaced it. It was found by DIRECT-vs-OPTIMIZED trace-parity bisection during the BERT DSL-path encoder migration in SKaiNET-transformers.

Fix

permute now replays with its recorded axes, parsed with the same IntArray | List<Int> convention that permuteBackward (DefaultExecutionTape) and the StableHLO LinalgOperationsConverter already use — the executor was the one inconsistent consumer. Traces without usable axes (older recordings) keep the legacy transpose behavior, so existing graphs are unaffected.

Tests

  • permuteReplaysRecordedAxes — rank-3 input, recorded axes [1, 0, 2]; asserts ops.permute is invoked with exactly those axes and ops.transpose is not called.
  • permuteWithoutRecordedAxesFallsBackToTranspose — legacy-trace fallback pinned.
  • Full :skainet-compile:skainet-compile-dag:jvmTest suite and apiCheck green.

Downstream note: SKaiNET-transformers currently carries a temporary axes-aware "permute" FusedOpHandler override in LLMFusedOpHandlers (identical logic; the handler registry precedes the builtin dispatch, so the overlap is harmless). It will be removed once a release containing this fix is consumed there.

🤖 Generated with Claude Code

…ded axes

The builtin dispatch grouped 'permute' with 'transpose'/'transpose2d' and
replayed all three as ops.transpose — a last-two-dims swap that drops the
recorded axes and is only coincidentally correct for rank-2. Any rank-3
permutation in a traced graph (e.g. MHA's heads/sequence swap [1, 0, 2] on
multi-token encoder or prefill forwards) silently produced the wrong
layout; single-token decode never hits the permute path, which is why
generation workloads didn't surface it.

permute now replays with its recorded axes, parsed with the same
IntArray | List<Int> convention permuteBackward and the StableHLO
converter already use, and falls back to the legacy transpose behavior
when no usable axes were recorded (older traces).

Found by the BERT DSL-path encoder migration in SKaiNET-transformers
(DIRECT-vs-OPTIMIZED trace parity bisection); that repo carries a
temporary axes-aware FusedOpHandler override to be dropped once this
ships. Regression-tested: permuteReplaysRecordedAxes,
permuteWithoutRecordedAxesFallsBackToTranspose.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@michalharakal michalharakal merged commit 9cb2ee8 into develop Jul 9, 2026
4 checks passed
@michalharakal michalharakal deleted the fix/graph-executor-permute-axes branch July 9, 2026 15:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant