-
Notifications
You must be signed in to change notification settings - Fork 1
Add GGUF Q1_0 kernel support (MMQ/MMVQ + enum patch) #1
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||
|---|---|---|---|---|
| @@ -0,0 +1,84 @@ | ||||
| # SPDX-License-Identifier: Apache-2.0 | ||||
| """Monkey-patch the gguf Python library to recognize Q1_0 if it's not in the | ||||
| released pip package yet. This is a temporary shim — remove once the upstream | ||||
| gguf pip package includes Q1_0 natively.""" | ||||
|
|
||||
| import logging | ||||
|
|
||||
| import numpy as np | ||||
|
|
||||
| logger = logging.getLogger(__name__) | ||||
|
|
||||
| GGML_TYPE_Q1_0 = 41 # matches ggml.h GGML_TYPE_Q1_0 | ||||
| Q1_0_BLOCK_SIZE = 128 | ||||
| Q1_0_TYPE_SIZE = 18 # 2 bytes fp16 scale + 16 bytes packed bits | ||||
|
|
||||
|
|
||||
| def _dequantize_q1_0_blocks(blocks: np.ndarray) -> np.ndarray: | ||||
| """Dequantize Q1_0 blocks using numpy. | ||||
|
|
||||
| Each block: 2 bytes fp16 scale, 16 bytes packed 1-bit quants (128 bits). | ||||
| Bit value 1 -> +scale, 0 -> -scale. | ||||
| """ | ||||
| n_blocks = blocks.shape[0] | ||||
| scales = blocks[:, :2].view(np.float16).astype(np.float32) # (n_blocks, 1) | ||||
| packed = blocks[:, 2:] # (n_blocks, 16) | ||||
| # Unpack bits: each byte -> 8 bits, little-endian bit order | ||||
| signs = np.unpackbits(packed, axis=1, bitorder="little")[ | ||||
| :, :Q1_0_BLOCK_SIZE | ||||
| ] # (n_blocks, 128) | ||||
| # Map: 1 -> +scale, 0 -> -scale | ||||
| result = np.where(signs == 1, scales, -scales) | ||||
| return result.astype(np.float32) | ||||
|
|
||||
|
|
||||
| def ensure_q1_0_gguf_compat(): | ||||
| """Register Q1_0 type in the gguf library if not already present.""" | ||||
| import gguf | ||||
|
Comment on lines
+35
to
+37
|
||||
| import gguf.quants as gguf_quants | ||||
|
|
||||
| if getattr(gguf, "_sglang_q1_0_compat", False): | ||||
| return | ||||
|
|
||||
| # Check if Q1_0 is already natively supported | ||||
| from gguf import GGMLQuantizationType | ||||
|
|
||||
| if hasattr(GGMLQuantizationType, "Q1_0"): | ||||
| logger.debug("Q1_0 already in gguf library, skipping compat shim") | ||||
| gguf._sglang_q1_0_compat = True | ||||
| return | ||||
|
|
||||
| logger.info("Applying Q1_0 compat shim to gguf library") | ||||
|
|
||||
| # 0. Add Q1_0 to the GGMLQuantizationType enum so GGUFReader can parse it | ||||
| from gguf import GGMLQuantizationType | ||||
| import enum | ||||
|
||||
| import enum |
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -184,6 +184,10 @@ torch::Tensor ggml_mul_mat_vec_a8( | |||||||||
| mul_mat_vec_iq1_m_q8_1_cuda<scalar_t>( | ||||||||||
| (void*)W.data_ptr(), (void*)quant_X.data_ptr(), (scalar_t*)Y.data_ptr(), col, row, vecs, stream); | ||||||||||
| break; | ||||||||||
| case 41: | ||||||||||
| mul_mat_vec_q1_0_q8_1_cuda<scalar_t>( | ||||||||||
| (void*)W.data_ptr(), (void*)quant_X.data_ptr(), (scalar_t*)Y.data_ptr(), col, row, vecs, stream); | ||||||||||
| break; | ||||||||||
|
||||||||||
| break; | |
| break; | |
| default: | |
| TORCH_CHECK(false, "ggml_mul_mat_vec_a8: unsupported GGUF type: ", type); |
Copilot
AI
Apr 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This switch has no default/error handling. If an unsupported type is passed, no kernel will run and Y (allocated with torch::empty) will be returned uninitialized, leading to silent incorrect outputs. Consider adding a default: that raises (e.g., TORCH_CHECK(false, ...)) or otherwise initializes/handles the output for unknown types.
| break; | |
| break; | |
| default: | |
| TORCH_CHECK(false, "Unsupported GGUF quantization type in ggml_mul_mat_a8: ", type); |
Copilot
AI
Apr 18, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This switch has no default/error handling. If an unsupported type is passed, the function will silently return all-zeros (since Y is initialized with torch::zeros) and no kernel will run. Consider adding a default: that raises (e.g., TORCH_CHECK(false, ...)) to avoid masking unsupported quantization types as valid outputs.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The new
Q1_0_TYPESare added toMMQ_QUANT_TYPES, which can makefused_moe_gguf()take theggml_moe_a8(MMQ) path for Q1_0 whenx.shape[0] > 64. Ingguf_kernel.cu,ggml_moe_get_block_size()andggml_moe_a8()don’t handle type 41, so this will return a block size of 0 and/or skip launching any kernel. Either add Q1_0 support to the MMQ MoE kernels + block-size mapping, or exclude Q1_0 from the MMQ MoE fast path (e.g., keep it MMVQ-only for MoE for now).