Skip to content

[diff-only] Mamba + KDA support: common state engine, hybrid DSE - #113

Closed
qichao-arlo-wang wants to merge 15 commits into
mainfrom
feature/mamba-kda-support
Closed

[diff-only] Mamba + KDA support: common state engine, hybrid DSE#113
qichao-arlo-wang wants to merge 15 commits into
mainfrom
feature/mamba-kda-support

Conversation

@qichao-arlo-wang

Copy link
Copy Markdown
Collaborator

Opened to make the diff reviewable in the GitHub UI. Not proposed for merge.
See the pinned comment below.

Michael's feature/mamba-kda-support, opened as a draft so the change is browsable
and commentable per-line. Ownership stays with @michael; nobody should merge or push
here on the basis of this PR existing.

13 commits · 138 files · +44,334 / −293 · tip e93b832f (2026-08-23)

What it adds

  • transactional_emulator/src/state_engine/ — a common recurrent-state engine
    executing both Mamba-2 and KDA behind one X_STATE contract: descriptor decode,
    banked head-tile layout, residency cache, timing, precision, and per-algorithm
    functional cores (functional/mamba.rs, functional/kda.rs).
  • Hybrid system DSE under analytic_models/performance/ — Nemotron-3 and Kimi K3
    workload models, routing/MoE-event/sensitivity/formal sweeps, GPU microprofile
    validators, and an HBM-bandwidth sweep.
  • Connected testbenchesmodels/nemotron3/mamba_connected_test.py,
    models/kimi3/kda_connected_test.py, models/kimi3/connected_blocks_test.py,
    bit-exact against CPU references.
  • test_x_state_contract_sync.py — cross-repo guard that this repo's generated
    contract matches the Compiler's spec, including a tampering test so a passing
    check is evidence rather than a no-op.

Companion

Compiler side: AICrossSim/PLENA_Compiler#76. This branch's gitlink pins it.

Read alongside

  • doc/COMMON_STATE_ENGINE_DESIGN_ZH.md — why one engine serves both, and the
    GPU profiling that reshaped the design
  • doc/connected_hybrid_validation.md — what is validated and at what scale

Happymic and others added 13 commits August 19, 2026 10:04
Two reporting defects in the layout ablation and the KDA reference.

_row_major() rebuilt the baseline with replace(), which carried the
skewed plan's physical_values_per_token over. The skewed layout widens
each group span so field origins stay bank aligned, so the baseline was
charged for padding it does not need - 50,688 against its own 49,248
values per token on KDA - and the published SRAM column flattered the
layout under test. Cycle columns are unaffected: read service stays
53,176 -> 14,904 on Mamba and 536,544 -> 430,560 on KDA.

The KDA reference folded the recurrent state and the conv state onto one
storage parameter, but the descriptor carries state_precision and
conv_state_precision independently and the shipped Kimi configuration is
FP32 state with BF16 conv. That combination had no CPU reference at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three changes to the layout engine, all about things no test could see.

The lane widths were constants here while the Compiler swept them, so a
non-default sweep would have silently kept 8/4/8 packets and diverged
every bank counter. They now arrive in the descriptor trailer.

mapping() and address() carried two copies of the same skew placement
rule, and only mapping() feeds the bijection check and the CRC, so a
divergence would have surfaced as a runtime "reads unwritten cell" far
from its cause. address() is now the only placement site.

The row comes from the physical linearisation and the bank from the
field-local index, which agree only when the group span and every field
origin are bank aligned. That precondition was unwritten and a violation
reported the unrelated "layout aliases two sources".

Every existing Mamba and KDA test configures the short convolution as
identity on the newest tap with conv_bias absent, so the ring-buffer
shift, the per-channel weight stride and the bias add were all
unobservable - deleting any of them kept the suite green. Verified by
mutation: removing the shift now fails.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
… freeze

fifo_capacity_values reaches only fifo_peak_values = min(burst, capacity)
in the Rust flow; backpressure is a closed form over the spill width and
the producer burst. No capacity can move the reported cycle count, so
"64 and 256 produce the same result" is a tautology of that model rather
than a measurement. The depth is justified by the analytic
ProjectionFifoSpillModel, which steps a real queue and reports a measured
high-watermark. Point both documents at it.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Every point in the quick grid held hbm_bytes_per_cycle at 64 while sweeping
Matrix width, state lanes, banks, FIFO depth and cache capacity. At that
bandwidth the memory server is 97.75% busy on Nemotron and 96.75% busy on
Kimi, so it absorbs nearly every on-chip win: the parameter that decides
most of decode time was the only one never varied, and every other
sensitivity in the report was quoted at a single unverified value.

Adding the sweep changes what the ablations mean. The layout win rises
monotonically with bandwidth - Nemotron 0.0540% at 64 B/cycle to 2.6950%
at 8192 - so the headline 0.0540% cannot be quoted without its bandwidth.
The conclusion survives the sweep: even at an 8 TB/s-class server the
layout is worth 2.6950% on Nemotron and 0.2883% on Kimi, so it is not a
system-level lever, and that is a property of the design rather than an
artefact of a conservative placeholder.

The two load-bearing mechanisms also invert between 512 and 1024 B/cycle.
Saving state bytes matters most while bandwidth is scarce; on-chip bank
conflicts only surface once it is not. A reader given only the 64 B/cycle
column would put on-chip area in the wrong place.

Kimi stays weight-streaming bound across the whole range: 36.757 ms TPOT
and 18.66% HBM utilisation even at 8192 B/cycle. That single-device
verdict does not depend on the bandwidth assumption.

The pinned report hash and candidate count move accordingly, and the new
section is covered by monotonicity assertions rather than by the file
hash alone.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@qichao-arlo-wang

Copy link
Copy Markdown
Collaborator Author

These two PRs exist to make the diff easy to read and comment on. They are not going to be merged as-is.

Opened as a pair so the cross-repo change can be reviewed together:

Please treat them as read-only review surfaces:

  • Do not merge. Draft status is deliberate.
  • Do not push to the branch on the strength of this PR. It is @michael's branch and stays that way.
  • Line comments are welcome — that is the point of opening them.

If any of this does get proposed for merge later, it should be as separate, scoped PRs with their own CI, not by un-drafting these.

qichao-arlo-wang and others added 2 commits August 25, 2026 11:34
`ci.yml`'s lint job fails at `ruff format --check .`: 40 files would be
reformatted. All 40 are files this branch touched and none are pre-existing --
`origin/main` is clean under the same ruff. Formatting only: `ruff check .` was
already passing, so no lint rule was ever violated.

Semantics-preserving, checked rather than assumed. The AST of all 173 tracked
Python files is byte-identical before and after (`ast.dump`, which omits
positions), so nothing in this diff can change behaviour.

This only unblocks the rest of that job. Its "Run hybrid pre-RTL model guards"
step runs after the format check and has therefore never executed on this branch,
so whatever it reports next will be new information rather than a regression.

The other failing job, `MoE stage-attribution guard`, is not this branch's fault
and is not fixed here: it reads only `transactional_emulator.yml` and so cannot
see the two guards this branch correctly wired into `ci.yml`. The fix belongs to
that guard and is in PLENA_Simulator#112.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
`test_every_guard_file_is_wired_into_ci` and
`test_every_guard_is_reachable_from_ci` read only
`.github/workflows/transactional_emulator.yml`. That it was the only workflow
running guards from this tree was true when written and enforced by nothing.

`feature/mamba-kda-support` adds a pytest step to `ci.yml` and wires two
testbench guards into it. Both checks reported those guards as never run by CI:
a correctly wired guard failing the check whose whole purpose is to notice
unwired ones. One of the two was `test_hybrid_isa_freeze_sync.py`, the cross-repo
guard for the frozen opcode allocation -- so the false positive landed on the
wiring it is most costly to mistrust, and the reading it invites is "the guard is
noisy", which is how a real one gets ignored later.

Both now read the whole `.github/workflows` directory. Concatenating is
deliberately permissive: the question is whether *some* job names the file, and
which one is not these checks' business.

`test_the_workflow_scan_reads_every_workflow_file` pins it. Nothing else would:
narrowing the scan makes both checks *more* likely to pass, and only the guards
named in the dropped file start reading as unwired -- which is the failure, not
the signal.

Verified against the failing tree, not just in the abstract: the fixed guard
passes on `feature/mamba-kda-support` at e93b832. Injections kept honest --
dropping a guard from every workflow still fails; naming it only in `ci.yml` now
passes; re-narrowing the scan fails the new test.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
(cherry picked from commit 72e6ac2)

Applied partially. The original also retargets `test_every_guard_is_reachable_from_ci`,
which does not exist on this branch -- it arrives with PLENA_Simulator#112, which is
where this fix belongs. Cherry-picked here only so this branch's CI reflects its own
state rather than a defect in a check it inherited; the duplicate resolves when #112
lands on main.
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.

2 participants