Skip to content

feat(gguf): implement Q8_0 dequantization - #368

Open
Cyber-Marty wants to merge 1 commit into
FlashML-org:mainfrom
Cyber-Marty:feat/q8_0-dequant-clean
Open

feat(gguf): implement Q8_0 dequantization#368
Cyber-Marty wants to merge 1 commit into
FlashML-org:mainfrom
Cyber-Marty:feat/q8_0-dequant-clean

Conversation

@Cyber-Marty

Copy link
Copy Markdown

Summary

dequant.py declared Q8_0 in BLOCK_SHAPE, GGML_NAME and __all__, but there was no
dequant_q8_0, so dequantize() raised NotImplementedError on the format that the most
widely distributed Gemma-4 GGUFs use: unsloth's UD-*_XL dynamic quants place Q8_0 on the
attention projections, the dense FFN and token_embd237 of 658 tensors in
gemma-4-26B-A4B-it-UD-Q6_K_XL.gguf (see #358).

Fix

dequant_q8_0 follows the ggml block_q8_0 layout: per 32-elem block, fp16 scale d

  • 32 int8 quants q, w = d*q with no offset (unlike Q4_0's (q-8)*d — ggml's
    quantize_row_q8_0 stores q = round(w/d), d = max|w|/127).

Registered in _DEQUANT and __all__; module docstring updated to list Q8_0. No
existing code path changes — Q4_0 / Q6_K / F32 / F16 / BF16 untouched.

Tests

New tests/models/test_dequant_q8_0.py (4 cases, pure torch / CPU):

  • round-trip: random fp32 → reference quantizer (mirrors quantize_row_q8_0) →
    dequant_q8_0 → reconstruction error bounded by half a quantization step;
  • exact values: hand-computed block (d=0.5, quants incl. -128/127/0) decodes exactly;
  • dispatch: dequantize(raw, GGML_Q8_0) routes instead of raising;
  • metadata honesty: BLOCK_SHAPE[Q8_0] == (32, 34) and row_bytes math unchanged.

tests/models/test_gemma4_gguf_config.py + test_dequant_q8_0.py + test_cpu_moe_q4_0.py:
7 passed, 5 skipped (CUDA-only cases, by design).

Fixes #358

dequant.py declared Q8_0 in BLOCK_SHAPE, GGML_NAME and __all__ but had
no dequant_q8_0, so dequantize() raised NotImplementedError on the
format that the most widely distributed Gemma-4 GGUFs (unsloth's
UD-*_XL dynamic quants) use on attention projections, the dense FFN
and token_embd -- 237 of 658 tensors in the 26B-A4B UD-Q6_K_XL file.

dequant_q8_0 follows the ggml block_q8_0 layout: fp16 scale d + 32
int8 quants, w = d*q with no offset. Tests round-trip through a
reference quantizer mirroring quantize_row_q8_0 (d = max|w|/127,
q = round(w/d)), bounding the error at half a quantization step, plus
a hand-computed exact-values case and a dequantize() dispatch check.
All pure torch / CPU.

Fixes FlashML-org#358
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.

gemma4 GGUF: Q8_0 is declared in BLOCK_SHAPE/GGML_NAME/__all__ but missing from _DEQUANT, so unsloth UD-*_XL quants fail with NotImplementedError

1 participant