Skip to content

Add the IQ2_S weight-only quantization format - #2512

Open
cjluo-nv wants to merge 2 commits into
mainfrom
chenjiel/iq2-s-format
Open

cjluo-nv wants to merge 2 commits into
mainfrom
chenjiel/iq2-s-format

Conversation

@cjluo-nv

@cjluo-nv cjluo-nv commented Sep 22, 2026

Copy link
Copy Markdown
Collaborator

What does this PR do?

Type of change: new feature

Adds IQ2_S at 2.5625 bits per weight — the widest of the GGML IQ family at one and two bits. Second of three, stacked on #2511 (IQ2_XXS).

Review #2511 first. This PR targets main but branches off chenjiel/iq2-xxs-format, so its diff will include #2511's commit until that merges.

On the mixed-precision checkpoint #2511 measured (unsloth/Qwen3.8-27B-GGUF), IQ2_S covers 9 tensors and 0.6 B parameters.

What's distinctive about it

IQ2_S is the one format llama.cpp's own tooling gives no head start on, so both the search and the kernel are written against the GGML layout directly.

The interesting difference from IQ2_XS and IQ2_XXS is sign handling: IQ2_S stores a full 8-bit sign mask per group rather than a 7-bit parity-coded index. So the encoder takes the input signs as they are, instead of flipping the weakest element to fix parity, and the search compares magnitudes directly — simpler than its siblings.

Its 1024-entry codebook is twice IQ2_XS's, which makes it the most expensive search of the five and pushes the grid past the static shared-memory limit, so the kernel keeps the codebook and its norms in dynamic shared memory.

That cost is why the kernel matters more here than anywhere else:

torch CUDA
IQ2_S, 5632×2048 weight 0.8 M elem/s 725.7 M elem/s 907×
extrapolated to a 27B model ~9.8 hours ~37 s

Without the kernel this format would not be usable on the models these formats exist for.

Usage

python examples/hf_ptq/hf_ptq.py --pyt_ckpt_path <model> --recipe general/ptq/iq2_s

Testing

The decoder is validated against llama.cpp's own output, not just round-tripped:

IQ2_S: 9 tensors, 2,355,200 blocks → 0 mismatched, max|diff| 0.0

The new 1024-entry codebook matches the ggml-common.h table entry for entry. Blocks lifted from that checkpoint ship as conformance vectors so CI keeps checking bytes we did not produce.

The format joins the IQ_FORMATS registry and the shared parametrized test batteries introduced in #2511, so it inherits the whole contract rather than bringing its own set.

  • tests/unit/torch/quantization/ -k 'ggml or iq1 or iq2 or iq_' — 131 passed
  • tests/gpu/torch/quantization/test_iq_formats_cuda.py — 28 passed (7 checks × 4 formats)
  • tests/unit/recipe/test_presets.py — passing; general/ptq now holds 30 recipes, ptq.md updated
  • CUDA encoder byte-identical to the PyTorch reference on a fixed input

Pre-existing failures in test_autoquant.py are a torchvision circular import in my environment — 71 failed / 39 passed identically with and without this change.

Before your PR is "Ready for review"

  • Is this change backward compatible?: ✅
  • If you copied code from any other sources or added a new PIP dependency, did you follow guidance in CONTRIBUTING.md: ✅ — the new codebook is a GGML table, carried in codebooks.py with the source revision recorded. No new dependencies.
  • Did you write any new necessary tests?: ✅
  • Did you update Changelog?: ✅
  • Did you get Claude approval on this PR?: ❌ — not yet run

Additional Information

Second of three: #2511 (IQ2_XXS) → this → IQ1_M. Replaces #2505, which carried all three at once.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Added GGML-compatible IQ2_XXS and IQ2_S weight-only quantization, including CPU and CUDA packing, quantization, and dequantization.
    • Added PTQ recipes and presets for both formats. Calibration data is not required, and weights must use dimensions divisible by 256.
    • Expanded Hugging Face and Megatron export support to cover all four GGML IQ formats.
    • Updated the PTQ catalog with the new formats and their bit rates.

cjluo-nv and others added 2 commits September 22, 2026 22:04
llama.cpp defines five GGML IQ formats at one and two bits; we ship two. This
adds IQ2_XXS, at 2.0625 bits per weight between IQ1_S and IQ2_XS, and is the
first of three changes that close the gap. On a real mixed-precision checkpoint
(unsloth/Qwen3.8-27B-GGUF) IQ2_XXS alone covers 59 tensors and 2.84B
parameters, 10.6% of the file, which a reader limited to IQ1_S and IQ2_XS
cannot consume.

The encoder follows the existing single-pass grid search at a fixed anchored
super-block scale, and the CUDA kernel follows the existing per-block
structure. IQ2_XXS reuses IQ2_XS's even-parity sign rule but packs a 4-bit
sub-block scale into the same 32-bit word as four 7-bit sign indices, and its
256-entry grid needs no high index bits.

Two pieces of groundwork come with it, both of which the next two formats
reuse. Export spelled the IQ family as a two-element tuple at nine sites;
those become an IQ_FORMATS frozenset plus per-format packer and block-geometry
tables, so a format is a row rather than a sweep through the exporters. And
the per-format test files, which had drifted apart, become one parametrized
module per layer, so every format is held to the same contract.

The decoder is validated against llama.cpp's own output over 11,100,160 blocks
from that checkpoint, all bit-identical to dequantize_row_iq2_xxs, and the new
codebook matches the ggml-common.h table entry for entry. Blocks lifted from
the checkpoint ship as conformance vectors so CI keeps checking bytes we did
not produce.

Measured on a 5632x2048 weight, the CUDA encoder runs at 1047.9 M elem/s
against the torch search's 10.7.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
Second of three changes completing the GGML IQ formats at one and two bits,
after IQ2_XXS. IQ2_S is the widest of the family at 2.5625 bits per weight,
and covers 9 tensors and 0.6B parameters of the mixed-precision checkpoint the
first change measured.

It is the one format llama.cpp's own tooling gives no head start on, so both
the search and the kernel are written against the GGML layout directly. The
interesting difference from IQ2_XS and IQ2_XXS is the sign handling: IQ2_S
stores a full eight-bit sign mask per group rather than a seven-bit
parity-coded index, so the encoder takes the input signs as they are instead
of flipping the weakest element to fix parity, and the search compares
magnitudes directly.

Its 1024-entry codebook is twice IQ2_XS's, which makes it the most expensive
search of the five and pushes the grid past the static shared memory limit, so
the kernel keeps the codebook and its norms in dynamic shared memory. On a
5632x2048 weight that is 725.7 M elem/s against the torch search's 0.8 --
without the kernel, a 27B model would take about ten hours to pack.

The decoder is validated against llama.cpp's own output over 2,355,200 blocks
from the same checkpoint, all bit-identical to dequantize_row_iq2_s, and the
new codebook matches the ggml-common.h table entry for entry. The format joins
the shared parametrized test batteries and the IQ_FORMATS registry introduced
with IQ2_XXS, so it inherits the whole contract rather than bringing its own.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Chenjie Luo <chenjiel@nvidia.com>
@cjluo-nv
cjluo-nv requested review from a team as code owners September 22, 2026 22:12
@coderabbitai

coderabbitai Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: NVIDIA/Model-Optimizer/.coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: cf415608-6e2c-4d91-8ab7-e5d0d931402a

📥 Commits

Reviewing files that changed from the base of the PR and between 7159c01 and 6410b27.

📒 Files selected for processing (28)
  • CHANGELOG.rst
  • modelopt/torch/export/quant_format.py
  • modelopt/torch/export/quant_utils.py
  • modelopt/torch/export/unified_export_hf.py
  • modelopt/torch/export/unified_export_megatron.py
  • modelopt/torch/kernels/quantization/ggml/common.cuh
  • modelopt/torch/kernels/quantization/ggml/ggml.cpp
  • modelopt/torch/kernels/quantization/ggml/iq2_s.cu
  • modelopt/torch/kernels/quantization/ggml/iq2_xxs.cu
  • modelopt/torch/quantization/extensions.py
  • modelopt/torch/quantization/ggml/__init__.py
  • modelopt/torch/quantization/ggml/backend.py
  • modelopt/torch/quantization/ggml/codebooks.py
  • modelopt/torch/quantization/ggml/iq2_s.py
  • modelopt/torch/quantization/ggml/iq2_xxs.py
  • modelopt_recipes/configs/numerics/iq2_s.yaml
  • modelopt_recipes/configs/numerics/iq2_xxs.yaml
  • modelopt_recipes/configs/ptq/presets/model/iq2_s.yaml
  • modelopt_recipes/configs/ptq/presets/model/iq2_xxs.yaml
  • modelopt_recipes/general/ptq/iq2_s.yaml
  • modelopt_recipes/general/ptq/iq2_xxs.yaml
  • modelopt_recipes/ptq.md
  • tests/_test_utils/torch/quantization/iq_llama_cpp_vectors.py
  • tests/examples/hf_ptq/test_llm_ptq.py
  • tests/gpu/torch/quantization/test_iq_formats_cuda.py
  • tests/unit/recipe/test_presets.py
  • tests/unit/torch/quantization/test_ggml_backend.py
  • tests/unit/torch/quantization/test_iq_formats.py

Included review availability: Your plan provides up to 12 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

The change adds IQ2_XXS and IQ2_S GGML quantization with Python and CUDA packers. It integrates both formats into fake quantization, Hugging Face and Megatron export, and PTQ recipes. Tests cover format conformance, CUDA parity, and recipe integration.

Changes

GGML IQ2 Format Support

Layer / File(s) Summary
IQ2 encoding and decoding
modelopt/torch/quantization/ggml/codebooks.py, modelopt/torch/quantization/ggml/iq2_s.py, modelopt/torch/quantization/ggml/iq2_xxs.py, modelopt/torch/quantization/ggml/__init__.py
Adds IQ2_XXS and IQ2_S codebooks, quantizers, decoders, and fake-quantization adapters. The package exports the formats.
CUDA packers
modelopt/torch/kernels/quantization/ggml/common.cuh, modelopt/torch/kernels/quantization/ggml/ggml.cpp, modelopt/torch/kernels/quantization/ggml/iq2_*.cu, modelopt/torch/quantization/extensions.py
Adds CUDA packing kernels and bindings for both formats and includes the new sources in the GGML extension build.
Backend and export integration
modelopt/torch/export/quant_format.py, modelopt/torch/export/quant_utils.py, modelopt/torch/export/unified_export_*.py, modelopt/torch/quantization/ggml/backend.py
Recognizes all four IQ formats in format metadata, fake-quant dispatch, and Hugging Face and Megatron export paths.
PTQ recipes and catalog
modelopt_recipes/configs/numerics/iq2_*.yaml, modelopt_recipes/configs/ptq/presets/model/iq2_*.yaml, modelopt_recipes/general/ptq/iq2_*.yaml, modelopt_recipes/ptq.md, CHANGELOG.rst
Adds IQ2_XXS and IQ2_S PTQ configurations, presets, and recipes, and updates the catalog and changelog.
Format conformance and integration tests
tests/_test_utils/torch/quantization/iq_llama_cpp_vectors.py, tests/unit/torch/quantization/test_iq_formats.py, tests/gpu/torch/quantization/test_iq_formats_cuda.py, tests/unit/recipe/test_presets.py, tests/examples/hf_ptq/test_llm_ptq.py, tests/unit/torch/quantization/test_ggml_backend.py
Adds llama.cpp conformance vectors and tests for quantization, decoding, CUDA parity, PTQ recipes, and backend dispatch.

Priority: ➖ Normal

Estimated code review effort: 4 (Complex) | ~60 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant TensorQuantizer
  participant ggml_fake_quant
  participant quantize_iq2_s
  participant GGML_CUDA_extension
  participant iq2_s_pack_cuda
  TensorQuantizer->>ggml_fake_quant: dispatches iq2_s fake quantization
  ggml_fake_quant->>quantize_iq2_s: calls format quantizer
  quantize_iq2_s->>GGML_CUDA_extension: sends blocks, grid, and scales when CUDA extension is available
  GGML_CUDA_extension->>iq2_s_pack_cuda: dispatches IQ2_S packing
  iq2_s_pack_cuda-->>quantize_iq2_s: returns packed payload
Loading

Merge Risk: ⚪ Minimal · up to 6410b

No material merge risk remains in the supplied review context; the new formats are wired through export and have conformance and integration coverage.

🚥 Pre-merge checks | ✅ 5 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 64.47% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 76 functions across 19 files. (9 skipped:… Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly identifies IQ2_S weight-only quantization, the primary objective of the pull request. The changes also add IQ2_XXS support.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Security Anti-Patterns ✅ Passed The reviewed diff adds no prohibited security patterns. The added Python lines contain no torch.load(..., weights_only=False), numpy.load(..., allow_pickle=True), hardcoded `trust_remote_code=True…
Full details: Docstring Coverage

Explanation

Docstring coverage is 64.47% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 76 functions across 19 files. (9 skipped: 9 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Comment @coderabbitai help to get the list of available commands.

@github-actions

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://NVIDIA.github.io/Model-Optimizer/pr-preview/pr-2512/

Built to branch gh-pages at 2026-09-22 22:17 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

@codecov

codecov Bot commented Sep 22, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 78.48%. Comparing base (1b4e7df) to head (6410b27).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #2512      +/-   ##
==========================================
+ Coverage   71.20%   78.48%   +7.27%     
==========================================
  Files         603      605       +2     
  Lines       66796    67061     +265     
==========================================
+ Hits        47564    52634    +5070     
+ Misses      19232    14427    -4805     
Flag Coverage Δ
examples-diffusers 21.43% <28.23%> (+0.03%) ⬆️
examples-gpt-oss 13.53% <27.21%> (+0.06%) ⬆️
examples-hf_ptq 23.03% <65.98%> (+0.42%) ⬆️
examples-llm_distill 13.59% <27.21%> (+0.06%) ⬆️
examples-llm_eval 17.50% <28.23%> (+0.05%) ⬆️
examples-llm_qat 17.78% <28.23%> (+0.04%) ⬆️
examples-llm_sparsity 16.02% <27.21%> (+0.05%) ⬆️
examples-megatron_bridge 26.17% <29.93%> (-0.11%) ⬇️
examples-specdec_bench 13.29% <27.21%> (+0.06%) ⬆️
examples-speculative_decoding 17.85% <28.23%> (-0.02%) ⬇️
examples-torch_onnx 21.96% <27.21%> (+0.03%) ⬆️
examples-torch_trt 15.36% <27.21%> (+0.05%) ⬆️
examples-vllm_serve 13.93% <27.21%> (+0.06%) ⬆️
gpu 58.98% <97.61%> (+25.67%) ⬆️
regression 15.18% <27.21%> (+0.10%) ⬆️
unit 58.44% <93.19%> (+0.16%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

This branch has not been deployed

No deployments
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