ggml: add Q2_0 quantization support (CPU) - #24448
Conversation
|
Ran the CPU correctness checks for this
Both built clean from this branch (CPU backend, No issues found on the CPU side. |
|
Just so you are prepared, our current workload means this will take some time to get reviewed, and #22836 is first in the quant queue. :) |
|
@CISC, sounds good, thanks for the heads up. |
Hard to say unfortunately, new quants in particular are hard to allocate reviewer/time for. No point in updating enums until conflicts are merged. |
max-krasnyansky
left a comment
There was a problem hiding this comment.
This looks very clean to me.
Very similar to Q4_0 in terms of the overall integration (kernels, blocks, etc).
I'm interested in adding this to ggml-hexagon since it maps pretty well.
Unfortunately, the compute would have to be done in FP16 (HMX) and INT8 (HVX) but the memory footprint reduction will definitely help speed things up.
@khosravipasha Please rebase with the latest master and I'll approve the workflow to kick the CI
dc7c932 to
4b7044d
Compare
|
@max-krasnyansky For now if you want to try with one of the uploaded models use the Nice, |
Block size 32 would be the most ideal for |
|
Seems there is some conflicts now, will resolve them. @max-krasnyansky Main issue with group size 32 will have large overhead will become 2.5 bpw. |
4b7044d to
ed93eda
Compare
|
Fixed two merge conflicts |
|
We need this PR ! |
Sounds good. I can work with 64 and 128 too. |
|
@khosravipasha letting you know that Ternary-Bonsai-27B-Q2_0.gguf does not work on x86_64 CPU in llama.cpp with your patch right now (b10015, commit 12127de) |
|
@ValdikSS Currently models ending in |
Q2_0 (ternary) GPU kernels for CUDA and Vulkan, cherry-picked from the upstream-targeted PrismML PR branches (ggml-org#25603 cuda, ggml-org#25430 vulkan). Upstream already carries the Q2_0 type + CPU backend (ggml-org#24448) and the qwen35 arch, so these kernels are the only gap for running Ternary-Bonsai-27B (Q2_g64).
@khosravipasha For my understanding, is it correct that the Bonsai ternary models never utilize the
|
|
@ggerganov Yes, that's correct current models only use the {0,1,-1}. But we pack it into 2-bit as it was easier to accelerate on Metal/CUDA. That being said we might do 2-bit models too (or mixed quants of tensors) in the future that utilizes the 2 so Q2_0 should be useful for those. |
sync with llama.cpp which added Q2_0 in ggml-org/llama.cpp#24448 (ternary quant, 64-weight blocks with fp16 scale, 2.25 bpw) adds it to GGMLFileQuantizationType, GGMLQuantizationType, GGUF_QUANT_ORDER and the gguf quant descriptions/sizes disclaimer: my julien-cto agent helped me write this <!-- CURSOR_SUMMARY --> --- > [!NOTE] > **Low Risk** > Additive enum and metadata only; no runtime inference or parsing logic changes beyond existing quant registration patterns. > > **Overview** > Adds **Q2_0** support to stay aligned with llama.cpp ([ggml-org/llama.cpp#24448](ggml-org/llama.cpp#24448)): a 2-bit scheme with fp16 block scale, 64 weights per block, and ~2.25 bpw. > > In `packages/tasks`, `Q2_0` is registered on `GGMLFileQuantizationType` (41) and `GGMLQuantizationType` (42), and listed in `GGUF_QUANT_ORDER` under 2-bit quants so filename parsing and nearest-quant logic recognize it. > > In `packages/gguf`, `quant-descriptions.ts` gains the human-readable description and `GGML_QUANT_SIZES` entry (`calcBPW(64, 2 + 16)`). > > <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit c68a9dc. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup> <!-- /CURSOR_SUMMARY -->
ADR-0012's table emits only k-quants below 8 bits, and every k-quant needs rows divisible by 256. The 30B target's expert stacks carry rows of 2688 and 1856, so `llama-quantize` rewrites every k-quant type on 93.0 % of the parameters — after the manual override, on a zero exit, with a warning pack discards. The packed file stops being recipe-driven, which ADR-0012 decision 3 forbids, and the Destination's 2-bit tier has no table entry at all. #189 carries the defect; the maintainer ruled it lands as a new ADR with the fallback-warning capture folded in. - Add ADR-0028: expert-stack groups map through their own table (8→Q8_0, 4→Q4_0, 2→Q2_0), the backend refuses nominal 3 on the empty 2.25–4.25 gap, and a type-fallback warning halts the pack - Amend ADR-0012's header (decisions 1 and 5), narrow its toolchain-output open question, and add the index row - Add the expert-stack carve-out to the glossary's Type mapping entry Test: `pre-commit run --all-files` (docs-only; docvet, doc-refs, and banned-terms gates pass) Resolves the question on #189 — the ticket closes manually with its pointer comment, not by this PR, so the record link lands first (the PR #184 lesson). #228 carries the build. --- ## PR Review ### Checklist - [x] Self-reviewed my code - [x] All gates pass (`pre-commit run --all-files` and a push-stage run) - [x] Doc statuses promoted/demoted where code moved (CLAUDE.md trust rules) - [ ] New ports have verified-fake contract suites (ADR-0009) — n/a, docs only - [ ] Breaking changes use `!` in title and `BREAKING CHANGE:` in body — n/a ### Review Focus The decision 1 table's 4-bit row: Q4_0 over MXFP4, argued from `quantize_q4_0` consuming the per-expert imatrix slice while `quantize_mxfp4` ignores it. The swap condition is an open question in the record. Also decision 3's halt-vs-record split against the ADR-0016 imatrix-miss scan. ### Related #189 (chart:discuss, chart #158), #228 (build ticket), #159 findings, ADR-0012, ADR-0021 decision 4 (untouched — the table changes meaning, not the 2-bit buying bar), #183 (owns the remaining Nemotron-H classes), upstream [llama.cpp #24448](ggml-org/llama.cpp#24448).


Overview
This PR adds Q2_0 support for CPU. Main motivation is to support Ternary Bonsai models (1.7B, 4B, 8B) and upcoming models. This PR is CPU only (ARM NEON + generic scalar fallback).
This completes the Q1_0, Q2_0, Q4_0, Q8_0 family.
We have the x86, Metal, CUDA, and Vulkan backends ready to submit later.
Notes
Format: Each group of 64 weights shares one fp16 scale
d; weights are packed at 2 bits each withmapping of
{0,1,2,3} => {-1,0,+1,+2} * dOur models natively support group size 128; however, it was requested to do group size 64 for the official Q2_0 format (see discussion #22019), so this PR uses 64.
We plan to also maintain a sibling group-128 variant (
PQ2_0) in our fork since the0.125 extra bpw becomes significant on larger models. If you have a cleaner way to do this, please let us know. For future release we will pack the models into both Q2_0 and PQ2_0 formats.
Why not use TQ1_0 / TQ2_0? They support group size 256, our models are group size 128, and also cpu-only and harder to accelerate on Metal/CUDA, etc. More info in: discussion #22019
Speed/Correctness Summary
-t 8,-ngl 0).More details and raw outputs in appendix.
Additional Info
Model Repos:
Links + Evals
More info on the models and working demos can be found below:
Each repo has three gguf variants:
Q2_0_g64.gguf: the new group-64 format this PR adds. The_g64suffix is forconvenience; it will be renamed to plain
Q2_0once these PRs merge. Use this file with this PR.Q2_0.gguf: the old Q2_0 from our fork (group 128); predates the group-64 changeand does not load with this PR. Will be deleted/renamed once this PR merges.
PQ2_0.gguf: the sibling format with group size 128 we keep maintaining in our fork (fork-only,not part of this PR).
Testing
Tested on Mac M4 Pro, 48 GB. Two CPU routes: ARM NEON and Generic Scalar Fallback
(generic built by steering ggml to the portable path:
GGML_SYSTEM_ARCH=UNKNOWN, NEONarch/arm/quants.cnot compiled).Pack to Q2_0 (from F16 GGUF)
Speed Benchmarks Details
ARM NEON
Generic Scalar Fallback (1.7B, small
-p 16 -n 8)KL Kernel Accuracy Test Details (Q2_0 g64 vs F16, packed vs unpacked)
ARM NEON: Q2_0 g64 vs F16, by size
1.7B: full statistics (ARM NEON vs F16)
1.7B: Generic Scalar Fallback vs F16 (matches NEON)
Requirements