GitHub ↗
Contents

The Zyphra family: Zamba2, Zamba v1 and BlackMamba#

The three Zyphra architectures the engine carries — zamba2, zamba (v1, with its MoE) and blackmamba (Mamba-1 with alternating MoE) — what their files weigh at the current pin, which quantization to publish for each, and the two things that will silently go wrong if you convert or quantize them with an older tree.

The architecture work is in Models measured rows and in the fork; this page is the file-level record. Engine issue: #1036 (the Zamba2 sharing work) and #991 (HRX placement classes).

File sizes: a Zamba2 GGUF is now the size of its checkpoint#

Zamba2 checkpoints store the transformer block once per shared block (two of them at 2.7B and 7B) and hold each hybrid layer's MLP difference in a rank-128 adapter pair. Converting used to expand both — the block once per hybrid layer, and each layer's adapter merged into a full FFN matrix — so a 7B BF16 GGUF came out at 20.45 GiB against a 14.82 GB checkpoint. Two changes fixed that, and both are in the pin:

change fork PR what it stops
shared block stored once per block, aliased per layer #203 13 copies of a set that has 2 distinct values
per-layer MLP adapter stored as a low-rank pair, applied in the graph as base + B*(A*x) #218 13 copies of a 102.76 M-param matrix (205 MB each at bf16) where 2 plus a 4.13 M pair per layer do
model merged form (old) adapter form (current) change
Zamba2-2.7B-instruct 6,009,822,848 B (5.60 GiB), 520 tensors 5,328,903,360 B (4.96 GiB), 531 tensors −680,919,488 B (−0.63 GiB)
Zamba2-7B-Instruct 16,875,503,904 B (15.72 GiB), 771 tensors 14,722,123,360 B (13.71 GiB), 786 tensors −2,153,380,544 B (−2.005 GiB)

Both forms load from one binary. A GGUF written before these changes keeps taking the merged path — the loader requires ffn_up per layer when the file carries no zamba2.ffn_gate_up_adapter_rank key, and reads the low-rank pair when it does. Nothing published before the change stops working.

The two forms are not bit-identical, and that is expected: the merged file has up @ down computed in f32 at conversion time and rounded to bf16, while the adapter path evaluates the pair at run time. Measured against the merged form's own logits (CPU, 16 × 512):

model mean KLD same top-p PPL
Zamba2-2.7B-instruct 0.000067 99.510 % 16.795417 (merged 16.7937)
Zamba2-7B-Instruct 0.000095 99.755 % 8.499977 (merged 8.5028)

Both are two to three orders of magnitude below the family bar below, so a quantized arm still has to be re-measured rather than inherited from the merged form — see the next section, where one arm changed sides.

What to publish per model#

The family bar is mean KLD ≤ 0.03 AND same-top-1 ≥ 95 %, and each model gets a sweet spot, a max-quality and a smallest-usable pick. The Zamba2 rows are the adapter form; the others are unchanged.

model arm role size KLD top-1
Zamba2-2.7B-instruct Q6_K sweet spot 2119 MiB 0.007244 † 95.711 % †
Q8_0 max quality 2728 MiB 0.001242 98.235 %
Q4_K_M-imx smallest usable 1511 MiB 0.043205 90.858 %
Zamba2-7B-Instruct Q6_K sweet spot and smallest usable 5829 MiB 0.010430 96.593 %
Q8_0 max quality 7515 MiB 0.001669 98.603 %
~~Q5_K_M~~ withdrawn ~~4938 MiB~~ 0.019383 94.792 % — misses the bar by 0.21 points
Zamba-7B-v1 Q6_K sweet spot and smallest usable 6943 MiB 0.002685 97.16 %
Q8_0 max quality 7484 MiB 0.000962 97.97 %
BlackMamba-2.8B Q8_0 sweet spot and max quality 2831 MiB 0.001312 97.99 %
IQ4_XS-imx smallest usable 1542 MiB 0.026519 91.01 %

† 32-chunk run (32 × 512 = 16384 tokens), because the 16-chunk run sat too close to the bar to decide; every other row is a 16-chunk run. Mark the long rows when quoting them.

Why the 7B moves up a tier. The adapter form perturbs top-1 in proportion to how close an arm already sat to the bar, and the two Zamba2 models reacted differently to the same change: the 2.7B's top-1 moved up 0.05 points, the 7B's down 0.52. Q5_K_M was clearing the 95 % bar by 0.025 points on the merged form, so it no longer clears it (94.792 %), and Q6_K — 5829 MiB, still 11-12 % smaller than the merged Q5_K_M — is the smallest arm that does. Q5_K_M is listed above as withdrawn rather than deleted because it is the smaller file for anyone who accepts the lower top-1 deliberately.

Two things that go wrong with an older tree#

1. A quantizer that predates the F16 default will drop the adapter off HRX. The adapter pair stays at F16 by default, because its B contraction has input_size = rank = 128: the float weight path admits that (input_size ≥ 4 && % 4 == 0), the block formats do not (their block layout needs ≥ 256). Quantize the pair with an older llama-quantize and that matmul leaves the device on every hybrid layer — measured on a 2.7B Q5_K_M as 9 of 3051 nodes on the CPU and pp512 463.66 vs 589.47 tok/s (0.79x, -21 %), with no error to tell you. If your tree predates the default, pass it explicitly:

llama-quantize --tensor-type ffn_gate_up_adapter=f16 <in.gguf> <out.gguf> Q6_K

Cost of the F16 pair: +31.8 MiB on an 1804.93 MiB 2.7B file. An explicit --tensor-type still wins; --pure still means pure.

2. Assert the layout before you trust a PPL. A pair written transposed ({rank, n_embd} instead of {n_embd, rank}) is refused by the loader with check_tensor_dims, but only after a conversion; the cheap check is on the container: ffn_up ×2 (not one per hybrid layer), zamba2.ffn_gate_up_adapter_rank = 128, adapters 9 + 9 at 2.7B and 13 + 13 at 7B, and the byte sizes in the table above.

HRX placement, if you are quoting a number#

The adapter path adds two MUL_MATs and an ADD per hybrid layer and all of them are placed on HRX — +27 nodes at 2.7B (9 × 3), with the same off-device residue as the merged form, and GGML_HRX_STRICT=1 exits 0 on both forms at prefill and decode. There is no throughput cost to report: pp512 441.98 → 440.57 and tg64 22.87 → 22.60 on one binary with both forms (0.997 / 0.988).

One caveat for anyone running -ngl 99 today: the token-embedding GET_ROWS is currently not on HRX (1 of 3024 nodes, GET_ROWS x 1, 2 graph splits) after the host-pointer weight import landed — that is base-level and affects both forms equally, tracked as engine #1316, not in this family's changes

View this page's source on GitHub ↗