Skip to content

fix(cpu): packed ternary weights reach dispatch through the Wᵀ marker (#1136) - #1181

Merged
michalharakal merged 1 commit into
developfrom
fix/1136-ternary-transpose-dispatch
Aug 26, 2026
Merged

michalharakal merged 1 commit into
developfrom
fix/1136-ternary-transpose-dispatch

Conversation

@michalharakal

Copy link
Copy Markdown
Contributor

Found wiring the packed BitNet GGUF path in SKaiNET-transformers (transformers#337): linearProject's ops.matmul(x, ops.transpose(W)) threw ClassCastException (Byte → Float) for a packed BITNET_B1_58 weight.

Root cause

isHeapPackedWeight — the predicate gating both transpose's Wᵀ-marker and matmulWeightTransposed's views→KernelDispatch path — was a hardcoded list of the seven Q-format interfaces. The ternary types weren't in it, so transpose dense-copied them through a decoding get() that returns ternary codes, not values.

Fix

The predicate becomes data is PackedBlockStorage — format-driven, like everything else in #1136. The JVM tier keeps its narrower isHeapPackedWeightForJvm (its relayout-and-cache kernels read specific formats); all other packed encodings land in matmulWeightTransposedViaViews, which serves any encoding through dispatch — the exact ternary keys included when the packs are installed.

Tests

  • New TernaryWeightTransposeDispatchTest: matmul(x, transpose(W)) vs the decoded matmul for BITNET_B1_58 and BITNET_PLANES — the consumer shape every dense layer produces.
  • Full backend-cpu jvm suite green.
  • Downstream proof: with this fix, SKaiNET-transformers' end-to-end test (synthetic I2_S GGUF → packed load → OptimizedLLMRuntime forward) matches the FP32-widened baseline, with and without the native packs.

🤖 Generated with Claude Code

…ch path

isHeapPackedWeight was a hardcoded list of the seven Q-format interfaces,
so the packed ternary types (BitNetB158TensorData, BitNetPlanesTensorData)
fell into transpose's dense decode-copy — whose get() returns ternary
CODES, not values: ClassCastException (Byte → Float) on the first
linearProject over a packed BitNet weight, found wiring the packed GGUF
path in SKaiNET-transformers (transformers#337).

The predicate becomes `data is PackedBlockStorage` — the #1136 physiology
stated once more: the Wᵀ-marker path that reaches KernelDispatch selects
by storage format, not by an enumeration of formats. The JVM tier keeps
its own narrower isHeapPackedWeightForJvm for the formats its
relayout-and-cache kernels actually read; every other packed encoding
lands in matmulWeightTransposedViaViews, which serves any encoding
through dispatch (exact ternary keys included, when the packs are
installed).

Pinned by TernaryWeightTransposeDispatchTest: matmul(x, transpose(W))
against the decoded matmul for both ternary formats — the consumer shape
every dense layer's linearProject produces.

Refs #1136

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@michalharakal
michalharakal merged commit f4e4134 into develop Aug 26, 2026
16 checks passed
@michalharakal
michalharakal deleted the fix/1136-ternary-transpose-dispatch branch August 26, 2026 18:38
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