Skip to content

Use LinearAttention to implement Mamba2 and other cleanup - #163

Merged
gramalingam merged 7 commits into
mainfrom
rama/nemotron-h-quality
Apr 13, 2026
Merged

Use LinearAttention to implement Mamba2 and other cleanup#163
gramalingam merged 7 commits into
mainfrom
rama/nemotron-h-quality

Conversation

@gramalingam

Copy link
Copy Markdown
Collaborator

Update the NemotronH/Mamba2 implementations to use LinearAttention.

gramalingam and others added 5 commits April 12, 2026 01:20
- Replace op.Constant calls with raw Python values in _ssm.py to avoid
  potential constant caching issues across dtype builds
- Create L2 YAML test case (nemotron-h-nano-4b.yaml)
- Add nemotron_h to L3 synthetic parity _SKIP_REASONS (Mamba2BlockSingle
  only handles seq_len=1)
- Reorder example script CLI args (--ci before --repetition-penalty)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: G Ramalingam <grama@microsoft.com>
These shapes are reused for both B and C group-to-head expansion, so
drop the b_ prefix to reflect shared usage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: G Ramalingam <grama@microsoft.com>
Replace the three Mamba2Block variants (Single, Scan, ChunkedSSD) with a
single unified Mamba2Block that uses com.microsoft.LinearAttention
(update_rule="gated") for the SSM recurrence and CausalConvWithState
for the depthwise Conv1D. This supports both T=1 decode and T>1 prefill
in one code path.

Key changes:
- Rewrite _mamba_block.py: single Mamba2Block class using LinearAttention
- Delete _mamba_block_scan.py and _mamba_block_chunked.py (3 variants → 1)
- Remove mamba_scan flag from _flags.py (no longer needed)
- Fix GatedRMSNorm to handle 3D input (B, T, H) for grouped normalization
- Update SSM2CausalLMTask: new state shape (H, d_state, d_head) matching
  LinearAttention convention, register function ops
- Add _register_linear_attention_functions_for_ssm2 for pure Mamba2 models
- Add Mamba2-using models to _CHECKER_SKIP_MODELS (custom op shape limits)

The Mamba2 SSD recurrence maps to LinearAttention as:
  query=C, key=B, value=dt*x, decay=A*dt (log-space)
with kv_num_heads=num_heads (each head has its own state/decay).

All 2390 tests pass. Lint clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: G Ramalingam <grama@microsoft.com>
Replace the three Mamba2Block variants (Single, Scan, ChunkedSSD) with a
single unified Mamba2Block that uses com.microsoft.LinearAttention
(update_rule="gated") for the SSM recurrence and CausalConvWithState
for the depthwise Conv1D. This supports both T=1 decode and T>1 prefill
in one code path.

Key changes:
- Rewrite _mamba_block.py: single Mamba2Block class using LinearAttention
- Delete _mamba_block_scan.py and _mamba_block_chunked.py (3 variants → 1)
- Remove mamba_scan flag from _flags.py (no longer needed)
- Fix GatedRMSNorm to handle 3D input (B, T, H) for grouped normalization
- Update SSM2CausalLMTask: new state shape (H, d_state, d_head) matching
  LinearAttention convention, register function ops
- Add _register_linear_attention_functions_for_ssm2 for pure Mamba2 models
- Add Mamba2-using models to _CHECKER_SKIP_MODELS (custom op shape limits)

The Mamba2 SSD recurrence maps to LinearAttention as:
  query=C, key=B, value=dt*x, decay=A*dt (log-space)
with kv_num_heads=num_heads (each head has its own state/decay).

All 2390 tests pass. Lint clean.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: G Ramalingam <grama@microsoft.com>
…tant conflict

- Fix SSM state shape: (B, H, d_head, d_state) → (B, H, d_state, d_head)
  to match LinearAttention convention
- Add --ep flag to example script (cpu/cuda/onnx-standard)
- Fix op.Clip constant conflict: use op.Constant(value_float=...) to avoid
  initializer name collision across layers

Verified: ONNX output matches HuggingFace exactly on f32/cpu/onnx-standard.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: G Ramalingam <grama@microsoft.com>
@github-actions

github-actions Bot commented Apr 12, 2026

Copy link
Copy Markdown

Performance Comparison

Comparing 7c1972d0d85e31

Model Metric Baseline Current Delta
bert (feature-extraction) model_size_bytes 359 KB 359 KB +0.0%
bert (feature-extraction) num_nodes 61 61 +0.0%
falcon model_size_bytes 364 KB 364 KB +0.0%
falcon num_nodes 66 66 +0.0%
gemma2 model_size_bytes 428 KB 428 KB +0.0%
gemma2 num_nodes 107 107 +0.0%
gpt2 model_size_bytes 388 KB 388 KB +0.0%
gpt2 num_nodes 53 53 +0.0%
llama model_size_bytes 425 KB 425 KB +0.0%
llama num_nodes 61 61 +0.0%
llama (static-cache) model_size_bytes 425 KB 425 KB +0.0%
llama (static-cache) num_nodes 58 58 +0.0%
mamba (ssm-text-generation) model_size_bytes 360 KB 360 KB +0.0%
mamba (ssm-text-generation) num_nodes 99 98 -1.0%
phi3 model_size_bytes 421 KB 421 KB +0.0%
phi3 num_nodes 59 59 +0.0%
phi3 (static-cache) model_size_bytes 421 KB 421 KB +0.0%
phi3 (static-cache) num_nodes 56 56 +0.0%
qwen2 model_size_bytes 425 KB 425 KB +0.0%
qwen2 num_nodes 61 61 +0.0%
qwen2 (static-cache) model_size_bytes 425 KB 425 KB +0.0%
qwen2 (static-cache) num_nodes 58 58 +0.0%
qwen3_5_moe (hybrid-text-generation) model_size_bytes 506 KB 506 KB +0.0%
qwen3_5_moe (hybrid-text-generation) num_nodes 275 275 +0.0%
qwen3_5_text (hybrid-text-generation) model_size_bytes 458 KB 458 KB +0.0%
qwen3_5_text (hybrid-text-generation) num_nodes 129 129 +0.0%
qwen3_5_vl (hybrid-qwen-vl) model_size_bytes 977 KB 977 KB +0.0%
qwen3_5_vl (hybrid-qwen-vl) num_nodes 409 409 +0.0%
t5 (seq2seq) model_size_bytes 836 KB 836 KB +0.0%
t5 (seq2seq) num_nodes 174 174 +0.0%
whisper (speech-to-text) model_size_bytes 1008 KB 1008 KB +0.0%
whisper (speech-to-text) num_nodes 128 128 +0.0%

No performance regressions.

@codecov

codecov Bot commented Apr 12, 2026

Copy link
Copy Markdown

The author of this PR, gramalingam, is not an activated member of this organization on Codecov.
Please activate this user on Codecov to display this PR comment.
Coverage data is still being uploaded to Codecov.io for purposes of overall coverage calculations.
Please don't hesitate to email us at support@codecov.io with any questions.

@github-actions

github-actions Bot commented Apr 12, 2026

Copy link
Copy Markdown

🏗️ Architecture Diff

Comparing 7c1972d0d85e31

Model Sub-model Changes Status
bert (feature-extraction) model 0
falcon model 0
gemma2 model 0
gpt2 model 0
llama model 0
llama (static-cache) model 0
mamba (ssm-text-generation) model 22 🟡
phi3 model 0
phi3 (static-cache) model 0
qwen model 0
qwen (static-cache) model 0
qwen2 model 0
qwen2 (static-cache) model 0
qwen2_moe model 0
qwen2_moe (static-cache) model 0
qwen3 model 0
qwen3 (static-cache) model 0
qwen3_5_moe (hybrid-text-generation) model 0
qwen3_5_text (hybrid-text-generation) model 0
qwen3_5_vl (hybrid-qwen-vl) decoder 0
qwen3_5_vl (hybrid-qwen-vl) embedding 0
qwen3_5_vl (hybrid-qwen-vl) vision 0
qwen3_moe model 0
qwen3_moe (static-cache) model 0
qwen3_next (hybrid-text-generation) model 0
t5 (seq2seq) decoder 0
t5 (seq2seq) encoder 0
whisper (speech-to-text) decoder 0
whisper (speech-to-text) encoder 0
mamba (ssm-text-generation) / model — 22 change(s)

Op summary: 99 → 98 nodes

--- base
+++ head
@@ -14,7 +14,6 @@
 Transpose
 Transpose
 MatMul
-Constant
 Split
 Transpose
 MatMul

Removed nodes:

  • - Constant

Modified attributes:

  • node[61] Transpose: perm: [0, 2, 1] → [1, 0]

Connectivity changes:

  • node[1] RMSNormalization: input_ids [35, 6] → [36, 6]
  • node[3] MatMul: input_ids [36, 37] → [37, 38]
  • node[4] Split: input_ids [38, 8] → [39, 8]
  • node[5] Transpose: input_ids [39] → [40]
  • node[6] Concat: input_ids [1, 41] → [1, 42]
  • node[7] Slice: input_ids [42, 9, 10, 11] → [43, 9, 10, 11]
  • node[8] Conv: input_ids [42, 12] → [43, 12]
  • node[10] Add: input_ids [44, 45] → [45, 46]
  • node[11] Sigmoid: input_ids [46] → [47]
  • node[12] Mul: input_ids [46, 47] → [47, 48]
  • node[13] Transpose: input_ids [48] → [49]
  • node[15] MatMul: input_ids [49, 50] → [50, 51]
  • node[24] Unsqueeze: input_ids [59, 20] → [61, 22]
  • node[32] Squeeze: input_ids [65, 9] → [69, 9]
  • node[45] Mul: input_ids [40, 82] → [81, 83]
  • node[61] Transpose: input_ids [102] → [30]
  • node[71] Unsqueeze: input_ids [112, 20] → [114, 22]
  • node[79] Squeeze: input_ids [118, 9] → [122, 9]
  • node[92] Mul: input_ids [94, 135] → [134, 136]

Initializer changes:

  • initializer count 30 → 31

Legend: ⚪ No change · 🔵 Minor (attrs/inits) · 🟡 Moderate (nodes added/removed) · 🔴 Major (interface changed)

Copilot AI left a comment

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.

Pull request overview

This PR updates the Mamba2-based model stack (notably NemotronH) to express the Mamba2 recurrence via com.microsoft.LinearAttention + com.microsoft.CausalConvWithState, and cleans up related weight preprocessing, cache tensor layouts, and flags.

Changes:

  • Replaces prior Mamba2 multi-token implementations (Scan / chunked SSD) with a unified LinearAttention-based Mamba2Block, and removes the mamba_scan flag + deleted implementations.
  • Updates cache state tensor layout for Mamba2 to (B, H, d_state, d_head) and registers LinearAttention/CausalConv function bodies in tasks.
  • Aligns HF↔ONNX weight naming for Mamba2 SSM params by removing the .ssm. nesting renames; updates tests/skips and adds a NemotronH Nano 4B test case stub.

Reviewed changes

Copilot reviewed 18 out of 18 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
tests/synthetic_parity_test.py Skips NemotronH synthetic parity due to LinearAttention op availability assumptions.
tests/build_graph_test.py Updates preprocess-weight expectations (no .ssm. nesting) and expands shape-check skip list for custom ops.
testdata/cases/causal-lm/nemotron-h-nano-4b.yaml Adds a NemotronH Nano 4B golden-generation case (skipped due to size).
src/mobius/tasks/_ssm_causal_lm.py Updates SSM2 state layout + registers LinearAttention/CausalConv function ops for pure Mamba2 tasks.
src/mobius/tasks/_cache_utils.py Updates hybrid cache Mamba2 state shape + extends function-op registration to include Mamba2.
src/mobius/models/nemotron_h.py Removes Mamba2 SSM .ssm. nesting from weight renaming rules and docs.
src/mobius/models/mamba.py Removes Mamba2 SSM param renaming (HF/ONNX naming now matches).
src/mobius/models/granitemoehybrid.py Removes Mamba2 SSM .ssm. nesting rename logic.
src/mobius/models/bamba.py Simplifies preprocess_weights to tie embeddings only; no renaming.
src/mobius/components/_ssm.py Minor builder-API cleanups (Split/Reshape/Clip constant handling).
src/mobius/components/_rms_norm.py Fixes grouped RMSNorm reshaping to support 2D/3D inputs by flattening/restoring leading dims.
src/mobius/components/_mamba_block.py Reimplements Mamba2Block using LinearAttention + CausalConvWithState and removes older variant machinery.
src/mobius/components/_mamba_block_scan.py Deleted (old Mamba2 Scan path).
src/mobius/components/_mamba_block_chunked.py Deleted (old chunked SSD path).
src/mobius/components/init.py Removes Mamba2Scan from public exports.
src/mobius/_flags.py Removes the mamba_scan flag and _env_str helper.
pyproject.toml Removes now-unneeded Ruff per-file ignores for deleted chunked SSD file.
examples/nemotron_3_nano_text_generation.py Updates hybrid cache init shape, adds --ep, and tweaks CLI help/arg ordering.

Comment thread src/mobius/tasks/_cache_utils.py
Comment thread src/mobius/components/_mamba_block.py Outdated
Comment thread examples/nemotron_3_nano_text_generation.py Outdated
@gramalingam gramalingam changed the title [DRAFT] Use LinearAttention to implement Mamba2 and other cleanup Use LinearAttention to implement Mamba2 and other cleanup Apr 12, 2026
gramalingam and others added 2 commits April 12, 2026 22:37
Address PR #163 review comments:

1. Upcast Mamba2 SSM recurrence to fp32: Cast dt_raw, dt_bias, A_log,
   x_hidden, B, C, ssm_state to float32 before computing dt/decay/value
   and calling LinearAttention. This matches HuggingFace which computes
   the SSM recurrence in float32. Cast outputs back to model dtype.
   Also set stash_type=ir.DataType.FLOAT for the LinearAttention Scan
   body so the internal recurrence runs in fp32.

2. Fix _FUNCTIONS_DOMAIN: change from 'pkg.mobius' to 'com.microsoft'
   to match the actual function domain. The com.microsoft opset is
   already imported by _make_graph().

3. Fix --device help text to mention both ORT and HF usage.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: G Ramalingam <grama@microsoft.com>
The granitemoehybrid L3 test was never passing on main — the old Mamba2Scan
code crashed at ORT runtime (Squeeze shape error) and was caught by the
pytest.skip('ONNX inference failed...') handler. The LinearAttention rewrite
fixed the crash, but now the test actually runs and reveals the model
produces wrong output due to ORT function inlining dropping initializers
inside function ops. Skip with an explicit reason.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Signed-off-by: G Ramalingam <grama@microsoft.com>
@gramalingam
gramalingam enabled auto-merge (squash) April 13, 2026 02:37
@gramalingam
gramalingam merged commit 2c2e2cd into main Apr 13, 2026
19 of 22 checks passed
@gramalingam
gramalingam deleted the rama/nemotron-h-quality branch April 13, 2026 17:52
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.

3 participants