BitNet b1.58 β Ternary Inference End to End
BitNet b1.58 models (e.g. microsoft/BitNet-b1.58-2B-4T) store every projection weight as a
ternary value in {β1, 0, +1} β 1.58 bits of information, packed 2 bits per weight. SKaiNET runs
them end to end in the packed form: weights are never widened to FP32, and the matmuls
dispatch to the vendored NeoGPU LUT kernels (exact FP32Γternary β no activation quantization).
Greedy decode is verified token-for-token against both bitnet.cpp and the HuggingFace BF16 reference on 2B4T (see The parity story (and the bug it caught)).
The pipeline
GGUF (I2_S, type 36)
β BitNetWeightLoader β engine StreamingGgufParametersLoader + WeightForm
βββ ternary projections β BITNET_B1_58, 0.25 B/weight (KeepAsStored, MAPPED where servable)
βββ lm_head β BITNET_PLANES (8 trit planes + FP16 row scales)
β output.weight present β lossless requantize
β tied embeddings (2B4T) β head materialized from token_embd (bounded 8-plane encode)
βββ norms / embeddings β as stored (F32/F16/BF16)
The network definition (bitnetNetwork) is a thin decoderTransformerNetwork call: squared-ReLU
FFN with ffn_sub_norm, attn_sub_norm before o_proj, NEOX-style RoPE pairing
(RoPEMode.SPLIT_HALF β the pairing bitnet.cpp assigns all BITNET arches). Kernel selection is
format-driven: the DSL keeps plain matmul`s, and `KernelDispatch picks the ternary kernels from
the weight’s storage encoding. On the JVM and Android the ternary packs are discovered by the
engine’s self-healing dispatch (SKaiNET#1240); Kotlin/Native installs them explicitly.
The two-stage lm_head
The 128k-vocab head dominates decode cost. NeoGPU’s two-stage design, implemented in
BitNetTwoStageDecode + generateTwoStage:
-
OptimizedLLMRuntime.forwardHiddenruns the trunk only β the full-vocab projection never executes, prefill included. -
Stage 1 scores all rows with planes 0β3 (one fused
lmhead_stage1kernel call β half the work of the exact 8-plane matmul). -
Every row whose error bound could still reach the top-k is rescored exactly; greedy decode is provably identical to the full matmul.
sampleFromCandidatessamples the result.
The CLI enables this automatically when the head loaded as BITNET_PLANES.
Running it
./gradlew :llm-apps:skainet-cli:run --args="-m BitNet-b1.58-2B-4T-i2s.gguf -s 32 'The capital of France is'"
# knobs: --i2s-layout=group128|group64|sequential (converter flavor of the file)
# --context=N (cap KV allocation)
From code, the family facade:
val weights = BitNetIngestion(ctx).loadStreaming({ JvmRandomAccessSource.open(path) })
val model = weights.toModule()
val head = weights.planesHead // non-null => two-stage decode available
Measured on 2B4T (macOS Apple Silicon, FFM kernels, DIRECT): ~4.2 tok/s with the as-stored head β ~4.6 with the planes matmul β ~6.2 tok/s with two-stage decode.
The parity story (and the bug it caught)
The family’s maturity gate (BitNetGoldenTokenParityTest, model-gated) asserts prompt-tokenization
parity with the bitnet.cpp oracle and greedy-continuation equality against a committed golden.
Building that gate caught a real bug: the original RoPE pairing (INTERLEAVED) produced perfectly
coherent text that silently diverged from the reference at generated token 4. BF16 arbitration
(the HF checkpoint: exact ternary weights, BF16 activations) sided with bitnet.cpp; with
SPLIT_HALF, all three implementations agree token-for-token on the 32-token fixture.
Two caveats recorded in the fixture header for future regenerations: bitnet.cpp’s own CPU path quantizes activations to int8 (its current HEAD also does not compile β the fixture names the pinned working commit), and the exact dense-F16 tied head flips one reference-side near-tie.
Pointers
-
Engine-side ternary internals (encodings, kernels, GGUF I2_S import, AOT conversion): the SKaiNET engine’s Ternary β Getting Started tutorial and architecture reference.
-
Add a New Model Family β BitNet is the template’s reference instantiation.
-
Weight Quantization β where
BITNET_B1_58sits among the block formats.