kquant source (vectorized packers) for reproducibility
Browse files- .gitattributes +1 -0
- src/agiws_neural_quant/kquant/__init__.py +330 -0
- src/agiws_neural_quant/kquant/__pycache__/__init__.cpython-313.pyc +0 -0
- src/agiws_neural_quant/kquant/__pycache__/_gguf_bits.cpython-313.pyc +0 -0
- src/agiws_neural_quant/kquant/__pycache__/_iq_tables.cpython-313.pyc +3 -0
- src/agiws_neural_quant/kquant/__pycache__/gguf_pack.cpython-313.pyc +0 -0
- src/agiws_neural_quant/kquant/__pycache__/gguf_packed.cpython-313.pyc +0 -0
- src/agiws_neural_quant/kquant/__pycache__/gguf_packed_q2q3.cpython-313.pyc +0 -0
- src/agiws_neural_quant/kquant/__pycache__/gguf_plan.cpython-313.pyc +0 -0
- src/agiws_neural_quant/kquant/__pycache__/iq.cpython-313.pyc +0 -0
- src/agiws_neural_quant/kquant/_gguf_bits.py +29 -0
- src/agiws_neural_quant/kquant/_iq_tables.py +0 -0
- src/agiws_neural_quant/kquant/gguf_pack.py +455 -0
- src/agiws_neural_quant/kquant/gguf_packed.py +283 -0
- src/agiws_neural_quant/kquant/gguf_packed_q2q3.py +126 -0
- src/agiws_neural_quant/kquant/gguf_plan.py +67 -0
- src/agiws_neural_quant/kquant/iq.py +878 -0
.gitattributes
CHANGED
|
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
|
|
|
|
|
| 33 |
*.zip filter=lfs diff=lfs merge=lfs -text
|
| 34 |
*.zst filter=lfs diff=lfs merge=lfs -text
|
| 35 |
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
| 36 |
+
src/agiws_neural_quant/kquant/__pycache__/_iq_tables.cpython-313.pyc filter=lfs diff=lfs merge=lfs -text
|
src/agiws_neural_quant/kquant/__init__.py
ADDED
|
@@ -0,0 +1,330 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""kquant — GGUF k-quant block-wise quantization formats (llama.cpp).
|
| 2 |
+
|
| 3 |
+
Block-wise quantization with super-block structure. Each format packs N weights
|
| 4 |
+
per block with a shared scale (and optionally min/d-scale).
|
| 5 |
+
|
| 6 |
+
Formats:
|
| 7 |
+
Q4_0 — block_size=32, fp16 scale, 4-bit values. ~4.5 bpw.
|
| 8 |
+
Q4_1 — block_size=32, fp16 scale + fp16 min, 4-bit values. ~5 bpw.
|
| 9 |
+
Q4_K — super-block 256, 8 sub-blocks of 32. 6-bit scale + 6-bit d-scale + 4-bit
|
| 10 |
+
values. ~4.5 bpw. _S/_M/_L variants differ in d-scale precision.
|
| 11 |
+
Q5_K — super-block 256, 5-bit values + 6-bit scale + 6-bit d-scale. ~5.5 bpw.
|
| 12 |
+
Q6_K — super-block 256, 6-bit values + 8-bit d-scale + 6-bit scale. ~6.5 bpw.
|
| 13 |
+
Q8_0 — block_size=32, fp16 scale, 8-bit values. ~8.5 bpw. Near-lossless.
|
| 14 |
+
Q2_K — super-block 256, 4-bit Q2-quants + 4-bit d-scale. ~2.6 bpw.
|
| 15 |
+
Q3_K — super-block 256, 3-bit values + 6-bit scale. ~3.5 bpw.
|
| 16 |
+
|
| 17 |
+
For NeuralQuant we implement the math (quantize/dequantize per block); packing
|
| 18 |
+
into GGUF binary layout is handled by the GGUF writer (stage 22.8 converters).
|
| 19 |
+
Here we store block data as tensors and dequantize on-the-fly for inference.
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
from __future__ import annotations
|
| 23 |
+
|
| 24 |
+
import torch
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
# ---------------------------------------------------------------------------
|
| 28 |
+
# Q8_0 — block_size=32, fp16 scale, int8 values. Near-lossless int8.
|
| 29 |
+
# ---------------------------------------------------------------------------
|
| 30 |
+
|
| 31 |
+
def quantize_q8_0_block(w: torch.Tensor) -> dict[str, torch.Tensor]:
|
| 32 |
+
"""Quantize a block of 32 values to Q8_0.
|
| 33 |
+
|
| 34 |
+
Returns dict with 'scale' (fp32 scalar) and 'values' (int8 [32]).
|
| 35 |
+
scale = max(abs(w)) / 127; values = round(w / scale).
|
| 36 |
+
"""
|
| 37 |
+
assert w.numel() == 32, f"Q8_0 block must be 32 elements, got {w.numel()}"
|
| 38 |
+
max_abs = w.abs().amax().clamp(min=1e-8)
|
| 39 |
+
scale = max_abs / 127.0
|
| 40 |
+
values = torch.clamp(torch.round(w / scale), min=-127, max=127).to(torch.int8)
|
| 41 |
+
return {"scale": scale.to(torch.float32), "values": values}
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def dequantize_q8_0_block(scale: torch.Tensor, values: torch.Tensor) -> torch.Tensor:
|
| 45 |
+
"""Reconstruct 32 values from Q8_0 block."""
|
| 46 |
+
return values.to(torch.float32) * scale.to(torch.float32)
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
# ---------------------------------------------------------------------------
|
| 50 |
+
# Q4_0 — block_size=32, fp16 scale, 4-bit values [-8, 7].
|
| 51 |
+
# ---------------------------------------------------------------------------
|
| 52 |
+
|
| 53 |
+
def quantize_q4_0_block(w: torch.Tensor) -> dict[str, torch.Tensor]:
|
| 54 |
+
assert w.numel() == 32, f"Q4_0 block must be 32 elements, got {w.numel()}"
|
| 55 |
+
max_abs = w.abs().amax().clamp(min=1e-8)
|
| 56 |
+
scale = max_abs / 7.0 # 4-bit symmetric: [-8, 7], use 7 for scale
|
| 57 |
+
values = torch.clamp(torch.round(w / scale), min=-8, max=7).to(torch.int8)
|
| 58 |
+
return {"scale": scale.to(torch.float32), "values": values}
|
| 59 |
+
|
| 60 |
+
|
| 61 |
+
def dequantize_q4_0_block(scale: torch.Tensor, values: torch.Tensor) -> torch.Tensor:
|
| 62 |
+
return values.to(torch.float32) * scale.to(torch.float32)
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
# ---------------------------------------------------------------------------
|
| 66 |
+
# Q4_K — super-block 256 = 8 sub-blocks of 32. 6-bit packed scale + 6-bit
|
| 67 |
+
# d-scale per sub-block + 4-bit values.
|
| 68 |
+
# ---------------------------------------------------------------------------
|
| 69 |
+
|
| 70 |
+
def quantize_q4_k_superblock(w: torch.Tensor) -> dict[str, torch.Tensor]:
|
| 71 |
+
"""Quantize 256 values to Q4_K super-block.
|
| 72 |
+
|
| 73 |
+
Structure:
|
| 74 |
+
- 8 sub-blocks of 32 values, each 4-bit.
|
| 75 |
+
- super-block scale (fp32, derived from sub-block scales).
|
| 76 |
+
- per-sub-block d-scale (fp32, ratio sub-scale / super-scale).
|
| 77 |
+
|
| 78 |
+
For NeuralQuant inference we store the actual sub-block scales (not the
|
| 79 |
+
6-bit packed GGUF representation); the GGUF writer (stage 22.8) handles
|
| 80 |
+
bit-packing.
|
| 81 |
+
"""
|
| 82 |
+
assert w.numel() == 256, f"Q4_K super-block must be 256 elements, got {w.numel()}"
|
| 83 |
+
sub = w.reshape(8, 32)
|
| 84 |
+
# Per-sub-block scale: absmax / 7 (4-bit symmetric).
|
| 85 |
+
sub_scales = sub.abs().amax(dim=1).clamp(min=1e-8) / 7.0 # [8]
|
| 86 |
+
values = torch.clamp(torch.round(sub / sub_scales.unsqueeze(1)), min=-8, max=7).to(torch.int8)
|
| 87 |
+
# Super-block scale = max(sub_scales). d-scale = sub_scale / super_scale.
|
| 88 |
+
super_scale = sub_scales.amax().clamp(min=1e-8)
|
| 89 |
+
d_scales = sub_scales / super_scale # [8], in (0, 1]
|
| 90 |
+
return {
|
| 91 |
+
"super_scale": super_scale.to(torch.float32),
|
| 92 |
+
"d_scales": d_scales.to(torch.float32),
|
| 93 |
+
"values": values.reshape(256), # int8 [256]
|
| 94 |
+
}
|
| 95 |
+
|
| 96 |
+
|
| 97 |
+
def dequantize_q4_k_superblock(super_scale, d_scales, values) -> torch.Tensor:
|
| 98 |
+
"""Reconstruct 256 values from Q4_K super-block."""
|
| 99 |
+
vals = values.to(torch.int8).reshape(8, 32)
|
| 100 |
+
sub_scales = super_scale.to(torch.float32) * d_scales.to(torch.float32) # [8]
|
| 101 |
+
return (vals.to(torch.float32) * sub_scales.unsqueeze(1)).reshape(256)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# ---------------------------------------------------------------------------
|
| 105 |
+
# Q6_K — super-block 256, 6-bit values + 8-bit d-scale + 6-bit super-scale.
|
| 106 |
+
# ---------------------------------------------------------------------------
|
| 107 |
+
|
| 108 |
+
_Q6_LEVELS = 31 # 6-bit symmetric [-32, 31], use 31 for scale
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def quantize_q6_k_superblock(w: torch.Tensor) -> dict[str, torch.Tensor]:
|
| 112 |
+
assert w.numel() == 256, f"Q6_K super-block must be 256 elements, got {w.numel()}"
|
| 113 |
+
sub = w.reshape(8, 32)
|
| 114 |
+
sub_scales = sub.abs().amax(dim=1).clamp(min=1e-8) / _Q6_LEVELS # [8]
|
| 115 |
+
values = torch.clamp(torch.round(sub / sub_scales.unsqueeze(1)), min=-32, max=31).to(torch.int8)
|
| 116 |
+
super_scale = sub_scales.amax().clamp(min=1e-8)
|
| 117 |
+
d_scales = sub_scales / super_scale # [8]
|
| 118 |
+
return {
|
| 119 |
+
"super_scale": super_scale.to(torch.float32),
|
| 120 |
+
"d_scales": d_scales.to(torch.float32),
|
| 121 |
+
"values": values.reshape(256),
|
| 122 |
+
}
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def dequantize_q6_k_superblock(super_scale, d_scales, values) -> torch.Tensor:
|
| 126 |
+
vals = values.to(torch.int8).reshape(8, 32)
|
| 127 |
+
sub_scales = super_scale.to(torch.float32) * d_scales.to(torch.float32)
|
| 128 |
+
return (vals.to(torch.float32) * sub_scales.unsqueeze(1)).reshape(256)
|
| 129 |
+
|
| 130 |
+
|
| 131 |
+
# ---------------------------------------------------------------------------
|
| 132 |
+
# Q5_K — super-block 256, 5-bit values + 6-bit d-scale.
|
| 133 |
+
# ---------------------------------------------------------------------------
|
| 134 |
+
|
| 135 |
+
_Q5_LEVELS = 15 # 5-bit symmetric [-16, 15], use 15
|
| 136 |
+
|
| 137 |
+
|
| 138 |
+
def quantize_q5_k_superblock(w: torch.Tensor) -> dict[str, torch.Tensor]:
|
| 139 |
+
assert w.numel() == 256
|
| 140 |
+
sub = w.reshape(8, 32)
|
| 141 |
+
sub_scales = sub.abs().amax(dim=1).clamp(min=1e-8) / _Q5_LEVELS
|
| 142 |
+
values = torch.clamp(torch.round(sub / sub_scales.unsqueeze(1)), min=-16, max=15).to(torch.int8)
|
| 143 |
+
super_scale = sub_scales.amax().clamp(min=1e-8)
|
| 144 |
+
d_scales = sub_scales / super_scale
|
| 145 |
+
return {
|
| 146 |
+
"super_scale": super_scale.to(torch.float32),
|
| 147 |
+
"d_scales": d_scales.to(torch.float32),
|
| 148 |
+
"values": values.reshape(256),
|
| 149 |
+
}
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def dequantize_q5_k_superblock(super_scale, d_scales, values) -> torch.Tensor:
|
| 153 |
+
vals = values.to(torch.int8).reshape(8, 32)
|
| 154 |
+
sub_scales = super_scale.to(torch.float32) * d_scales.to(torch.float32)
|
| 155 |
+
return (vals.to(torch.float32) * sub_scales.unsqueeze(1)).reshape(256)
|
| 156 |
+
|
| 157 |
+
|
| 158 |
+
# ---------------------------------------------------------------------------
|
| 159 |
+
# Q2_K — super-block 256, 2-bit values + 4-bit d-scale. Extreme compression.
|
| 160 |
+
# ---------------------------------------------------------------------------
|
| 161 |
+
|
| 162 |
+
_Q2_LEVELS = 1 # 2-bit symmetric [-2, 1], use 1 for scale (coarse)
|
| 163 |
+
|
| 164 |
+
|
| 165 |
+
def quantize_q2_k_superblock(w: torch.Tensor) -> dict[str, torch.Tensor]:
|
| 166 |
+
assert w.numel() == 256
|
| 167 |
+
sub = w.reshape(8, 32)
|
| 168 |
+
sub_scales = sub.abs().amax(dim=1).clamp(min=1e-8) / _Q2_LEVELS
|
| 169 |
+
values = torch.clamp(torch.round(sub / sub_scales.unsqueeze(1)), min=-2, max=1).to(torch.int8)
|
| 170 |
+
super_scale = sub_scales.amax().clamp(min=1e-8)
|
| 171 |
+
d_scales = sub_scales / super_scale
|
| 172 |
+
return {
|
| 173 |
+
"super_scale": super_scale.to(torch.float32),
|
| 174 |
+
"d_scales": d_scales.to(torch.float32),
|
| 175 |
+
"values": values.reshape(256),
|
| 176 |
+
}
|
| 177 |
+
|
| 178 |
+
|
| 179 |
+
def dequantize_q2_k_superblock(super_scale, d_scales, values) -> torch.Tensor:
|
| 180 |
+
vals = values.to(torch.int8).reshape(8, 32)
|
| 181 |
+
sub_scales = super_scale.to(torch.float32) * d_scales.to(torch.float32)
|
| 182 |
+
return (vals.to(torch.float32) * sub_scales.unsqueeze(1)).reshape(256)
|
| 183 |
+
|
| 184 |
+
|
| 185 |
+
# ---------------------------------------------------------------------------
|
| 186 |
+
# Q3_K — super-block 256, 3-bit values + 6-bit d-scale.
|
| 187 |
+
# ---------------------------------------------------------------------------
|
| 188 |
+
|
| 189 |
+
_Q3_LEVELS = 3 # 3-bit symmetric [-4, 3], use 3
|
| 190 |
+
|
| 191 |
+
|
| 192 |
+
def quantize_q3_k_superblock(w: torch.Tensor) -> dict[str, torch.Tensor]:
|
| 193 |
+
assert w.numel() == 256
|
| 194 |
+
sub = w.reshape(8, 32)
|
| 195 |
+
sub_scales = sub.abs().amax(dim=1).clamp(min=1e-8) / _Q3_LEVELS
|
| 196 |
+
values = torch.clamp(torch.round(sub / sub_scales.unsqueeze(1)), min=-4, max=3).to(torch.int8)
|
| 197 |
+
super_scale = sub_scales.amax().clamp(min=1e-8)
|
| 198 |
+
d_scales = sub_scales / super_scale
|
| 199 |
+
return {
|
| 200 |
+
"super_scale": super_scale.to(torch.float32),
|
| 201 |
+
"d_scales": d_scales.to(torch.float32),
|
| 202 |
+
"values": values.reshape(256),
|
| 203 |
+
}
|
| 204 |
+
|
| 205 |
+
|
| 206 |
+
def dequantize_q3_k_superblock(super_scale, d_scales, values) -> torch.Tensor:
|
| 207 |
+
vals = values.to(torch.int8).reshape(8, 32)
|
| 208 |
+
sub_scales = super_scale.to(torch.float32) * d_scales.to(torch.float32)
|
| 209 |
+
return (vals.to(torch.float32) * sub_scales.unsqueeze(1)).reshape(256)
|
| 210 |
+
|
| 211 |
+
|
| 212 |
+
# ---------------------------------------------------------------------------
|
| 213 |
+
# Format registry: format string -> (block quantize, block dequantize, block_size)
|
| 214 |
+
# ---------------------------------------------------------------------------
|
| 215 |
+
|
| 216 |
+
BLOCK_SIZE_32 = 32
|
| 217 |
+
BLOCK_SIZE_256 = 256
|
| 218 |
+
|
| 219 |
+
FORMATS = {
|
| 220 |
+
"q8_0": (quantize_q8_0_block, dequantize_q8_0_block, BLOCK_SIZE_32),
|
| 221 |
+
"q4_0": (quantize_q4_0_block, dequantize_q4_0_block, BLOCK_SIZE_32),
|
| 222 |
+
"q4_k": (quantize_q4_k_superblock, dequantize_q4_k_superblock, BLOCK_SIZE_256),
|
| 223 |
+
"q5_k": (quantize_q5_k_superblock, dequantize_q5_k_superblock, BLOCK_SIZE_256),
|
| 224 |
+
"q6_k": (quantize_q6_k_superblock, dequantize_q6_k_superblock, BLOCK_SIZE_256),
|
| 225 |
+
"q2_k": (quantize_q2_k_superblock, dequantize_q2_k_superblock, BLOCK_SIZE_256),
|
| 226 |
+
"q3_k": (quantize_q3_k_superblock, dequantize_q3_k_superblock, BLOCK_SIZE_256),
|
| 227 |
+
}
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
def quantize_blocks(w: torch.Tensor, fmt: str) -> dict[str, torch.Tensor]:
|
| 231 |
+
"""Quantize a flat weight tensor into blocks of the given format.
|
| 232 |
+
|
| 233 |
+
Pads the last dim to be divisible by block_size.
|
| 234 |
+
|
| 235 |
+
Returns dict with stacked block tensors.
|
| 236 |
+
"""
|
| 237 |
+
quant_fn, _, block_size = FORMATS[fmt]
|
| 238 |
+
out_features = w.shape[0]
|
| 239 |
+
in_features = w.shape[1] if w.dim() > 1 else w.numel()
|
| 240 |
+
if w.dim() > 1:
|
| 241 |
+
flat = w
|
| 242 |
+
else:
|
| 243 |
+
flat = w.reshape(1, -1)
|
| 244 |
+
out_features = 1
|
| 245 |
+
# Pad in_features to be divisible by block_size.
|
| 246 |
+
pad = (block_size - (flat.shape[1] % block_size)) % block_size
|
| 247 |
+
if pad > 0:
|
| 248 |
+
flat = torch.nn.functional.pad(flat, (0, pad))
|
| 249 |
+
in_padded = flat.shape[1]
|
| 250 |
+
num_blocks = in_padded // block_size
|
| 251 |
+
|
| 252 |
+
# Reshape to [out, num_blocks, block_size].
|
| 253 |
+
blocks = flat.reshape(out_features, num_blocks, block_size)
|
| 254 |
+
|
| 255 |
+
if fmt in ("q8_0", "q4_0"):
|
| 256 |
+
# Simple block: per-block absmax scale + int values.
|
| 257 |
+
n_levels = 127 if fmt == "q8_0" else 7
|
| 258 |
+
max_val = n_levels if fmt == "q8_0" else 7
|
| 259 |
+
min_val = -n_levels if fmt == "q8_0" else -8
|
| 260 |
+
scales = blocks.abs().amax(dim=2).clamp(min=1e-8) / n_levels # [out, num_blocks]
|
| 261 |
+
values = torch.clamp(
|
| 262 |
+
torch.round(blocks / scales.unsqueeze(2)), min=min_val, max=max_val
|
| 263 |
+
).to(torch.int8)
|
| 264 |
+
return {
|
| 265 |
+
"scales": scales.to(torch.float32),
|
| 266 |
+
"values": values,
|
| 267 |
+
"in_features": in_features,
|
| 268 |
+
"in_padded": in_padded,
|
| 269 |
+
"out_features": out_features,
|
| 270 |
+
"block_size": block_size,
|
| 271 |
+
}
|
| 272 |
+
else:
|
| 273 |
+
# K-formats: super-block 256 = 8 sub-blocks of 32.
|
| 274 |
+
# blocks already [out, num_blocks, 256]; reshape to [out, num_blocks, 8, 32].
|
| 275 |
+
n_levels_map = {"q4_k": 7, "q5_k": 15, "q6_k": 31, "q2_k": 1, "q3_k": 3}
|
| 276 |
+
min_val_map = {"q4_k": -8, "q5_k": -16, "q6_k": -32, "q2_k": -2, "q3_k": -4}
|
| 277 |
+
n_levels = n_levels_map[fmt]
|
| 278 |
+
min_val = min_val_map[fmt]
|
| 279 |
+
sub = blocks.reshape(out_features, num_blocks, 8, 32)
|
| 280 |
+
sub_scales = sub.abs().amax(dim=3).clamp(min=1e-8) / n_levels # [out, num_blocks, 8]
|
| 281 |
+
values = torch.clamp(
|
| 282 |
+
torch.round(sub / sub_scales.unsqueeze(3)), min=min_val, max=n_levels
|
| 283 |
+
).to(torch.int8)
|
| 284 |
+
super_scales = sub_scales.amax(dim=2).clamp(min=1e-8) # [out, num_blocks]
|
| 285 |
+
d_scales = sub_scales / super_scales.unsqueeze(2) # [out, num_blocks, 8]
|
| 286 |
+
return {
|
| 287 |
+
"super_scales": super_scales.to(torch.float32),
|
| 288 |
+
"d_scales": d_scales.to(torch.float32),
|
| 289 |
+
"values": values.reshape(out_features, num_blocks, 256),
|
| 290 |
+
"in_features": in_features,
|
| 291 |
+
"in_padded": in_padded,
|
| 292 |
+
"out_features": out_features,
|
| 293 |
+
"block_size": 256,
|
| 294 |
+
"num_super": num_blocks,
|
| 295 |
+
}
|
| 296 |
+
|
| 297 |
+
|
| 298 |
+
def dequantize_blocks(qd: dict[str, torch.Tensor], fmt: str) -> torch.Tensor:
|
| 299 |
+
"""Reconstruct the weight tensor from block-quantized data.
|
| 300 |
+
|
| 301 |
+
Supports both symmetric (our quantize_blocks output) and asymmetric
|
| 302 |
+
(GGUF Q4_K/Q5_K with min offset) formats. Detection is by key presence:
|
| 303 |
+
- If 'mins' and 'super_min_scales' are in qd -> asymmetric:
|
| 304 |
+
x = super_scale * scale[i] * q[i] + super_min_scale * min[i]
|
| 305 |
+
- Otherwise -> symmetric:
|
| 306 |
+
x = super_scale * d_scale[i] * q[i]
|
| 307 |
+
"""
|
| 308 |
+
in_features = qd["in_features"]
|
| 309 |
+
in_padded = qd["in_padded"]
|
| 310 |
+
out_features = qd["out_features"]
|
| 311 |
+
if fmt in ("q8_0", "q4_0"):
|
| 312 |
+
scales = qd["scales"] # [out, num_blocks]
|
| 313 |
+
values = qd["values"] # [out, num_blocks, block_size]
|
| 314 |
+
w = values.to(torch.float32) * scales.to(torch.float32).unsqueeze(2)
|
| 315 |
+
w = w.reshape(out_features, in_padded)[:, :in_features]
|
| 316 |
+
else:
|
| 317 |
+
super_scales = qd["super_scales"] # [out, num_blocks]
|
| 318 |
+
d_scales = qd["d_scales"] # [out, num_blocks, 8]
|
| 319 |
+
values = qd["values"] # [out, num_blocks, 256]
|
| 320 |
+
sub_scales = super_scales.to(torch.float32).unsqueeze(2) * d_scales.to(torch.float32)
|
| 321 |
+
sub = values.reshape(out_features, -1, 8, 32)
|
| 322 |
+
w = sub.to(torch.float32) * sub_scales.unsqueeze(3)
|
| 323 |
+
# Asymmetric: add min offset (GGUF Q4_K/Q5_K).
|
| 324 |
+
if "mins" in qd and "super_min_scales" in qd:
|
| 325 |
+
mins = qd["mins"] # [out, num_blocks, 8]
|
| 326 |
+
super_min_scales = qd["super_min_scales"] # [out, num_blocks]
|
| 327 |
+
sub_mins = super_min_scales.to(torch.float32).unsqueeze(2) * mins.to(torch.float32)
|
| 328 |
+
w = w + sub_mins.unsqueeze(3)
|
| 329 |
+
w = w.reshape(out_features, -1)[:, :in_features]
|
| 330 |
+
return w
|
src/agiws_neural_quant/kquant/__pycache__/__init__.cpython-313.pyc
ADDED
|
Binary file (17.7 kB). View file
|
|
|
src/agiws_neural_quant/kquant/__pycache__/_gguf_bits.cpython-313.pyc
ADDED
|
Binary file (2.51 kB). View file
|
|
|
src/agiws_neural_quant/kquant/__pycache__/_iq_tables.cpython-313.pyc
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
version https://git-lfs.github.com/spec/v1
|
| 2 |
+
oid sha256:1e64273922ab23df733b43120821c6c80c72efecb8fb95eafd8f4a4193928b96
|
| 3 |
+
size 183305
|
src/agiws_neural_quant/kquant/__pycache__/gguf_pack.cpython-313.pyc
ADDED
|
Binary file (33.2 kB). View file
|
|
|
src/agiws_neural_quant/kquant/__pycache__/gguf_packed.cpython-313.pyc
ADDED
|
Binary file (15.9 kB). View file
|
|
|
src/agiws_neural_quant/kquant/__pycache__/gguf_packed_q2q3.cpython-313.pyc
ADDED
|
Binary file (7.48 kB). View file
|
|
|
src/agiws_neural_quant/kquant/__pycache__/gguf_plan.cpython-313.pyc
ADDED
|
Binary file (3.1 kB). View file
|
|
|
src/agiws_neural_quant/kquant/__pycache__/iq.cpython-313.pyc
ADDED
|
Binary file (61.1 kB). View file
|
|
|
src/agiws_neural_quant/kquant/_gguf_bits.py
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Shared bit-twiddling helpers for GGUF k-quant packed dequantization."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def f16_view_to_f32(u8: torch.Tensor) -> torch.Tensor:
|
| 9 |
+
"""View uint8 bytes as float16 then cast to float32. u8: [..., 2] uint8."""
|
| 10 |
+
flat = u8.reshape(-1, 2).to(torch.int16)
|
| 11 |
+
val = flat[:, 0] | (flat[:, 1].to(torch.int16) << 8)
|
| 12 |
+
f16 = val.view(torch.float16)
|
| 13 |
+
return f16.to(torch.float32).reshape(u8.shape[:-1])
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def unpack_scales_k4(scales: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 17 |
+
"""Unpack 8 (scale, min) 6-bit pairs from 12 bytes. scales: [..., 12] uint8.
|
| 18 |
+
|
| 19 |
+
Returns (sc, m) each [..., 8] (6-bit values as uint8).
|
| 20 |
+
"""
|
| 21 |
+
q = scales.to(torch.int32)
|
| 22 |
+
sh = q.shape[:-1]
|
| 23 |
+
sc = torch.zeros(*sh, 8, dtype=torch.int32, device=q.device)
|
| 24 |
+
m = torch.zeros(*sh, 8, dtype=torch.int32, device=q.device)
|
| 25 |
+
sc[..., 0:4] = q[..., 0:4] & 0x3F
|
| 26 |
+
m[..., 0:4] = q[..., 4:8] & 0x3F
|
| 27 |
+
sc[..., 4:8] = (q[..., 8:12] & 0x0F) | ((q[..., 0:4] >> 6) << 4)
|
| 28 |
+
m[..., 4:8] = (q[..., 8:12] >> 4) | ((q[..., 4:8] >> 6) << 4)
|
| 29 |
+
return sc.to(torch.uint8), m.to(torch.uint8)
|
src/agiws_neural_quant/kquant/_iq_tables.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
src/agiws_neural_quant/kquant/gguf_pack.py
ADDED
|
@@ -0,0 +1,455 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""GGUF k-quant packers - torch vectorized, float32 -> packed GGUF bytes.
|
| 2 |
+
|
| 3 |
+
Reverse of dequant_gguf_slice (gguf_packed.py). Each packer is a direct
|
| 4 |
+
translation of llama.cpp ggml-quants.c quantize_row_*_ref:
|
| 5 |
+
|
| 6 |
+
Q8_0 (34 B): d(f16) + qs[32] (int8) y = d*q8 block 32
|
| 7 |
+
Q2_K (84 B): scales[16] + qs[64] + d(f16) + dmin(f16)
|
| 8 |
+
y = d*sc*q - dmin*m (q 0..3) 16 sub-blocks of 16
|
| 9 |
+
Q3_K (110 B): hmask[32] + qs[64] + scales[12] + d(f16)
|
| 10 |
+
y = d*sc*(q - (hm ? 0 : 4)) 16 sub-blocks of 16
|
| 11 |
+
Q4_K (144 B): d(f16) + dmin(f16) + scales[12] + qs[128]
|
| 12 |
+
y = d*sc*q - dmin*m (q 0..15) 8 sub-blocks of 32
|
| 13 |
+
Q5_K (176 B): d(f16) + dmin(f16) + scales[12] + qh[32] + qs[128]
|
| 14 |
+
y = d*sc*(q+16*bit5) - dmin*m 8 sub-blocks of 32
|
| 15 |
+
Q6_K (210 B): ql[128] + qh[64] + scales[16] + d(f16)
|
| 16 |
+
y = d*sc*(q6-32) 16 sub-blocks of 16
|
| 17 |
+
|
| 18 |
+
i-quants are NOT packed here - kquant/iq.py quantize_iq*_s() already returns
|
| 19 |
+
packed GGUF bytes.
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
from __future__ import annotations
|
| 23 |
+
|
| 24 |
+
import torch
|
| 25 |
+
|
| 26 |
+
QK_K = 256
|
| 27 |
+
EPS = 1e-15
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _nearest_int(x: torch.Tensor) -> torch.Tensor:
|
| 31 |
+
"""Round half away from zero (C nearest_int / llm_rintf)."""
|
| 32 |
+
return torch.where(x >= 0, torch.floor(x + 0.5), torch.ceil(x - 0.5))
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def _f16_bytes(t: torch.Tensor) -> torch.Tensor:
|
| 36 |
+
"""fp32 [..., n] -> uint8 [..., n, 2] little-endian f16 bytes."""
|
| 37 |
+
u = (t.half().view(torch.int16).to(torch.int64)) & 0xFFFF
|
| 38 |
+
return torch.stack([u & 0xFF, (u >> 8) & 0xFF], dim=-1).to(torch.uint8)
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def _cat_blocks(segments: list[torch.Tensor], out_f: int, nblk: int,
|
| 42 |
+
bpb: int) -> torch.Tensor:
|
| 43 |
+
"""Concatenate per-block byte segments into one [out_f, nblk*bpb] row buffer.
|
| 44 |
+
|
| 45 |
+
Each segment is [out_f, nblk, seg_len] uint8 in the GGUF block layout;
|
| 46 |
+
the result is row-major [out_f, nblk*bpb] (no Python row loop).
|
| 47 |
+
"""
|
| 48 |
+
return torch.cat(segments, dim=-1).reshape(out_f, nblk * bpb)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def _make_qkx2(nmax: int, x: torch.Tensor, weights: torch.Tensor, rmin: float,
|
| 52 |
+
rdelta: float, nstep: int, use_mad: bool
|
| 53 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 54 |
+
"""Vectorized make_qkx2_quants (ggml-quants.c, line 799).
|
| 55 |
+
|
| 56 |
+
Args:
|
| 57 |
+
nmax: max level (15 Q4_K, 31 Q5_K, 3 Q2_K).
|
| 58 |
+
x: [..., n] values.
|
| 59 |
+
weights: [..., n] per-value weights.
|
| 60 |
+
Returns:
|
| 61 |
+
(scale, the_min) each [..., 1].
|
| 62 |
+
"""
|
| 63 |
+
n = x.shape[-1]
|
| 64 |
+
min_v = x.min(dim=-1, keepdim=True).values
|
| 65 |
+
max_v = x.max(dim=-1, keepdim=True).values
|
| 66 |
+
min_v = torch.where(min_v > 0, torch.zeros_like(min_v), min_v) # if min > 0: min = 0
|
| 67 |
+
|
| 68 |
+
sum_w = weights.sum(dim=-1, keepdim=True)
|
| 69 |
+
sum_x = (weights * x).sum(dim=-1, keepdim=True)
|
| 70 |
+
|
| 71 |
+
# Initial quantization with iscale = nmax/(max-min).
|
| 72 |
+
iscale = nmax / (max_v - min_v).clamp_min(EPS)
|
| 73 |
+
scale = 1.0 / iscale
|
| 74 |
+
l0 = _nearest_int(iscale * (x - min_v)).clamp(0, nmax)
|
| 75 |
+
diff = scale * l0 + min_v - x
|
| 76 |
+
best_error = (weights * (diff.abs() if use_mad else diff * diff)).sum(dim=-1, keepdim=True)
|
| 77 |
+
|
| 78 |
+
# Candidate sweep: is in 0..nstep, iscale = (rmin + rdelta*is + nmax)/(max-min).
|
| 79 |
+
is_grid = rmin + rdelta * torch.arange(nstep + 1, device=x.device, dtype=x.dtype) + nmax
|
| 80 |
+
isc_all = is_grid.view(-1, *([1] * x.dim())) / (max_v - min_v).clamp_min(EPS).unsqueeze(0)
|
| 81 |
+
l_all = _nearest_int(isc_all * (x - min_v).unsqueeze(0)).clamp(0, nmax) # [S, ..., n]
|
| 82 |
+
wl = weights.unsqueeze(0) * l_all
|
| 83 |
+
sum_l = wl.sum(dim=-1, keepdim=True)
|
| 84 |
+
sum_l2 = (wl * l_all).sum(dim=-1, keepdim=True)
|
| 85 |
+
sum_xl = (weights.unsqueeze(0) * l_all * x.unsqueeze(0)).sum(dim=-1, keepdim=True)
|
| 86 |
+
|
| 87 |
+
D = sum_w.unsqueeze(0) * sum_l2 - sum_l * sum_l
|
| 88 |
+
this_scale = (sum_w.unsqueeze(0) * sum_xl - sum_x.unsqueeze(0) * sum_l) / D.clamp_min(EPS)
|
| 89 |
+
this_min = (sum_l2 * sum_x.unsqueeze(0) - sum_l * sum_xl) / D.clamp_min(EPS)
|
| 90 |
+
# if this_min > 0: this_min = 0; this_scale = sum_xl / sum_l2
|
| 91 |
+
pos_min = this_min > 0
|
| 92 |
+
this_scale = torch.where(pos_min, sum_xl / sum_l2.clamp_min(EPS), this_scale)
|
| 93 |
+
this_min = torch.where(pos_min, torch.zeros_like(this_min), this_min)
|
| 94 |
+
|
| 95 |
+
diff2 = this_scale * l_all + this_min - x.unsqueeze(0)
|
| 96 |
+
cur_error = (weights.unsqueeze(0) * (diff2.abs() if use_mad else diff2 * diff2)).sum(dim=-1, keepdim=True)
|
| 97 |
+
|
| 98 |
+
better = cur_error < best_error # [S, ..., 1]
|
| 99 |
+
any_better = better.any(dim=0)
|
| 100 |
+
best_idx = better.int().argmax(dim=0) # [..., 1]
|
| 101 |
+
scale = torch.where(any_better, this_scale.gather(0, best_idx.unsqueeze(0)).squeeze(0), scale)
|
| 102 |
+
min_f = torch.where(any_better, this_min.gather(0, best_idx.unsqueeze(0)).squeeze(0), min_v)
|
| 103 |
+
return scale, -min_f
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def _pack_q2_q3(l: torch.Tensor) -> torch.Tensor:
|
| 107 |
+
"""Pack 256 x 2-bit levels into 64 bytes (llama.cpp q2_K/q3_K qs layout).
|
| 108 |
+
|
| 109 |
+
qs[j/4 + l] = L[j+l] | L[j+l+32]<<2 | L[j+l+64]<<4 | L[j+l+96]<<6, j = 0, 128.
|
| 110 |
+
l: [..., 256] uint8 in [0, 3].
|
| 111 |
+
"""
|
| 112 |
+
v = l.to(torch.int64)
|
| 113 |
+
lo = v[..., 0:32]; q1 = v[..., 32:64]; q2 = v[..., 64:96]; q3 = v[..., 96:128]
|
| 114 |
+
lo2 = v[..., 128:160]; q12 = v[..., 160:192]; q22 = v[..., 192:224]; q32 = v[..., 224:256]
|
| 115 |
+
first = lo | (q1 << 2) | (q2 << 4) | (q3 << 6) # [..., 32]
|
| 116 |
+
second = lo2 | (q12 << 2) | (q22 << 4) | (q32 << 6)
|
| 117 |
+
return torch.cat([first, second], dim=-1).to(torch.uint8) # [..., 64]
|
| 118 |
+
|
| 119 |
+
|
| 120 |
+
def _pack_scales_min_k4(sc: torch.Tensor, m: torch.Tensor) -> torch.Tensor:
|
| 121 |
+
"""Pack 8 (scale, min) 6-bit pairs into 12 bytes (get_scale_min_k4 layout).
|
| 122 |
+
|
| 123 |
+
j<4: q[j]=sc[j], q[j+4]=m[j]; j>=4: q[j+4]=(sc&0xF)|((m&0xF)<<4),
|
| 124 |
+
q[j-4] |= (sc>>4)<<6, q[j] |= (m>>4)<<6.
|
| 125 |
+
"""
|
| 126 |
+
sc = sc.clamp(0, 63).to(torch.int64)
|
| 127 |
+
m = m.clamp(0, 63).to(torch.int64)
|
| 128 |
+
q = torch.zeros(*sc.shape[:-1], 12, dtype=torch.int64, device=sc.device)
|
| 129 |
+
q[..., 0:4] = sc[..., 0:4]
|
| 130 |
+
q[..., 4:8] = m[..., 0:4]
|
| 131 |
+
q[..., 8:12] = (sc[..., 4:8] & 0xF) | ((m[..., 4:8] & 0xF) << 4)
|
| 132 |
+
q[..., 0:4] |= (sc[..., 4:8] >> 4) << 6
|
| 133 |
+
q[..., 4:8] |= (m[..., 4:8] >> 4) << 6
|
| 134 |
+
return q.to(torch.uint8)
|
| 135 |
+
|
| 136 |
+
|
| 137 |
+
def _unpack_scales_min_k4(q: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 138 |
+
"""Mirror of _gguf_bits.unpack_scales_k4 for packed [..., 12] int64 bytes."""
|
| 139 |
+
q = q.to(torch.int64)
|
| 140 |
+
sc = torch.zeros(*q.shape[:-1], 8, dtype=torch.int64, device=q.device)
|
| 141 |
+
m = torch.zeros_like(sc)
|
| 142 |
+
sc[..., 0:4] = q[..., 0:4] & 0x3F
|
| 143 |
+
m[..., 0:4] = q[..., 4:8] & 0x3F
|
| 144 |
+
sc[..., 4:8] = (q[..., 8:12] & 0x0F) | ((q[..., 0:4] >> 6) << 4)
|
| 145 |
+
m[..., 4:8] = (q[..., 8:12] >> 4) | ((q[..., 4:8] >> 6) << 4)
|
| 146 |
+
return sc, m
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
def _pad(w: torch.Tensor, block: int) -> tuple[torch.Tensor, int]:
|
| 150 |
+
out_f, in_f = w.shape
|
| 151 |
+
nblk = (in_f + block - 1) // block
|
| 152 |
+
wp = torch.zeros(out_f, nblk * block, dtype=torch.float32, device=w.device)
|
| 153 |
+
wp[:, :in_f] = w
|
| 154 |
+
return wp, nblk
|
| 155 |
+
|
| 156 |
+
|
| 157 |
+
# ---------------------------------------------------------------------------
|
| 158 |
+
# Q8_0 (llama.cpp:276) - block 32.
|
| 159 |
+
# ---------------------------------------------------------------------------
|
| 160 |
+
|
| 161 |
+
def pack_q8_0(w: torch.Tensor) -> torch.Tensor:
|
| 162 |
+
out_f, in_f = w.shape
|
| 163 |
+
wp, nblk = _pad(w, 32)
|
| 164 |
+
x = wp.view(out_f, nblk, 32)
|
| 165 |
+
amax = x.abs().amax(dim=-1).clamp_min(EPS)
|
| 166 |
+
d = amax / 127.0
|
| 167 |
+
qs = _nearest_int(x / d.unsqueeze(-1)).clamp(-127, 127).to(torch.int8).view(torch.uint8)
|
| 168 |
+
return _cat_blocks([_f16_bytes(d), qs], out_f, nblk, 34)
|
| 169 |
+
|
| 170 |
+
|
| 171 |
+
# ---------------------------------------------------------------------------
|
| 172 |
+
# Q2_K (llama.cpp:891) - 16 sub-blocks of 16.
|
| 173 |
+
# ---------------------------------------------------------------------------
|
| 174 |
+
|
| 175 |
+
def pack_q2_k(w: torch.Tensor) -> torch.Tensor:
|
| 176 |
+
out_f, in_f = w.shape
|
| 177 |
+
wp, nblk = _pad(w, QK_K)
|
| 178 |
+
x = wp.view(out_f, nblk, 16, 16)
|
| 179 |
+
scale, m = _make_qkx2(3, x, x.abs(), -0.5, 0.1, 15, True) # [.., 16, 1]
|
| 180 |
+
scale = scale.squeeze(-1)
|
| 181 |
+
m = m.squeeze(-1)
|
| 182 |
+
max_scale = scale.amax(dim=-1).clamp_min(EPS)
|
| 183 |
+
max_min = m.amax(dim=-1).clamp_min(EPS)
|
| 184 |
+
|
| 185 |
+
ls = _nearest_int((15.0 / max_scale).unsqueeze(-1) * scale).clamp(0, 15).to(torch.int64)
|
| 186 |
+
lm = _nearest_int((15.0 / max_min).unsqueeze(-1) * m).clamp(0, 15).to(torch.int64)
|
| 187 |
+
scales = (ls & 0xF) | ((lm & 0xF) << 4) # [out, nb, 16]
|
| 188 |
+
d = max_scale / 15.0
|
| 189 |
+
dmin = max_min / 15.0
|
| 190 |
+
|
| 191 |
+
# Re-quantize with stored d/dmin/scales.
|
| 192 |
+
d_f = d.unsqueeze(-1).unsqueeze(-1)
|
| 193 |
+
dm_f = dmin.unsqueeze(-1).unsqueeze(-1)
|
| 194 |
+
sc16 = (scales & 0xF).to(torch.float32).unsqueeze(-1)
|
| 195 |
+
m16 = (scales >> 4).to(torch.float32).unsqueeze(-1)
|
| 196 |
+
l = _nearest_int((x + dm_f * m16) / (d_f * sc16).clamp_min(EPS)).clamp(0, 3).to(torch.int64)
|
| 197 |
+
qs = _pack_q2_q3(l.view(out_f, nblk, QK_K))
|
| 198 |
+
|
| 199 |
+
return _cat_blocks([scales.to(torch.uint8), qs,
|
| 200 |
+
_f16_bytes(d), _f16_bytes(dmin)],
|
| 201 |
+
out_f, nblk, 84)
|
| 202 |
+
|
| 203 |
+
|
| 204 |
+
# ---------------------------------------------------------------------------
|
| 205 |
+
# Q3_K (llama.cpp:1229) - 16 sub-blocks of 16.
|
| 206 |
+
# ---------------------------------------------------------------------------
|
| 207 |
+
|
| 208 |
+
def _make_q3_quants(x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 209 |
+
"""Vectorized make_q3_quants(16, 4, x, L, do_rmse=true).
|
| 210 |
+
|
| 211 |
+
x: [..., 16]. Returns (scale, L) with L in [-4, 3] (int), scale [..., 1].
|
| 212 |
+
"""
|
| 213 |
+
amax = x.abs().amax(dim=-1)
|
| 214 |
+
max_v = x.gather(-1, x.abs().argmax(dim=-1, keepdim=True)).squeeze(-1) # signed max|x| element
|
| 215 |
+
max_s = torch.where(amax < EPS, torch.zeros_like(max_v), max_v)
|
| 216 |
+
iscale = -4.0 / max_s.clamp_min(EPS)
|
| 217 |
+
l = _nearest_int(iscale.unsqueeze(-1) * x).clamp(-4, 3).to(torch.float32)
|
| 218 |
+
w_ = x * x
|
| 219 |
+
sumlx = (w_ * x * l).sum(dim=-1, keepdim=True)
|
| 220 |
+
suml2 = (w_ * l * l).sum(dim=-1, keepdim=True)
|
| 221 |
+
for _ in range(5):
|
| 222 |
+
slx = sumlx - w_ * x * l # [..., 16] per-element
|
| 223 |
+
sl2 = suml2 - w_ * l * l # [..., 16] per-element
|
| 224 |
+
new_l = _nearest_int(x * sl2 / torch.where(slx > 0, slx, torch.ones_like(slx))).clamp(-4, 3)
|
| 225 |
+
slx2 = slx + w_ * x * new_l # [..., 16]
|
| 226 |
+
sl2_new = sl2 + w_ * new_l * new_l # [..., 16]
|
| 227 |
+
better = (slx > 0) & (sl2_new > 0) & (slx2 * slx2 * suml2 > sumlx * sumlx * sl2_new) & (new_l != l)
|
| 228 |
+
l = torch.where(better, new_l, l)
|
| 229 |
+
# Global sums update incrementally over accepted elements.
|
| 230 |
+
sumlx = sumlx + (better * (slx2 - slx)).sum(dim=-1, keepdim=True)
|
| 231 |
+
suml2 = suml2 + (better * (sl2_new - sl2)).sum(dim=-1, keepdim=True)
|
| 232 |
+
scale = torch.where(suml2 > 0, sumlx / suml2.clamp_min(EPS), torch.zeros_like(sumlx))
|
| 233 |
+
return scale, l
|
| 234 |
+
|
| 235 |
+
|
| 236 |
+
def pack_q3_k(w: torch.Tensor) -> torch.Tensor:
|
| 237 |
+
out_f, in_f = w.shape
|
| 238 |
+
wp, nblk = _pad(w, QK_K)
|
| 239 |
+
x = wp.view(out_f, nblk, 16, 16)
|
| 240 |
+
scale, _l = _make_q3_quants(x) # [.., 16, 1] per sub-block
|
| 241 |
+
|
| 242 |
+
sc4 = scale.squeeze(-1) # [out, nb, 16] signed
|
| 243 |
+
max_sc = sc4.gather(-1, sc4.abs().argmax(dim=-1, keepdim=True)).squeeze(-1) # [out, nb]
|
| 244 |
+
max_sc = torch.where(sc4.abs().amax(dim=-1) < EPS, torch.zeros_like(max_sc), max_sc)
|
| 245 |
+
|
| 246 |
+
isc = -32.0 / torch.where(max_sc.abs() < EPS, torch.ones_like(max_sc), max_sc)
|
| 247 |
+
l8 = (_nearest_int(isc.unsqueeze(-1) * sc4).clamp(-32, 31) + 32).to(torch.int64) # [.., 16] 0..63
|
| 248 |
+
l4 = l8 & 0xF
|
| 249 |
+
hi = (l8 >> 4) & 0x3
|
| 250 |
+
scales = torch.zeros(*l8.shape[:-1], 12, dtype=torch.int64, device=w.device)
|
| 251 |
+
scales[..., 0:8] = l4[..., 0:8]
|
| 252 |
+
scales[..., 0:8] |= l4[..., 8:16] << 4
|
| 253 |
+
hib = hi.view(*hi.shape[:-1], 4, 4).transpose(-1, -2) # [.., j%4, j//4]
|
| 254 |
+
hi_sh = (hib << (2 * torch.arange(4, device=w.device)).view(1, 1, 1, 4)).sum(-1)
|
| 255 |
+
scales[..., 8:12] = hi_sh
|
| 256 |
+
d = -max_sc / 32.0
|
| 257 |
+
|
| 258 |
+
# Re-quantize levels with the actual stored scales (aux unpacking, same as
|
| 259 |
+
# dequantize_row_q3_K / dequant_q3_k_packed_rows).
|
| 260 |
+
s0 = (scales[..., 0] | (scales[..., 1] << 8) | (scales[..., 2] << 16) | (scales[..., 3] << 24)).to(torch.int64)
|
| 261 |
+
s1 = (scales[..., 4] | (scales[..., 5] << 8) | (scales[..., 6] << 16) | (scales[..., 7] << 24)).to(torch.int64)
|
| 262 |
+
s2 = (scales[..., 8] | (scales[..., 9] << 8) | (scales[..., 10] << 16) | (scales[..., 11] << 24)).to(torch.int64)
|
| 263 |
+
kmask1 = 0x03030303
|
| 264 |
+
kmask2 = 0x0F0F0F0F
|
| 265 |
+
tmp = s2
|
| 266 |
+
aux0 = (s0 & kmask2) | (((tmp >> 0) & kmask1) << 4)
|
| 267 |
+
aux1 = (s1 & kmask2) | (((tmp >> 2) & kmask1) << 4)
|
| 268 |
+
aux2 = ((s0 >> 4) & kmask2) | (((tmp >> 4) & kmask1) << 4)
|
| 269 |
+
aux3 = ((s1 >> 4) & kmask2) | (((tmp >> 6) & kmask1) << 4)
|
| 270 |
+
aux = torch.stack([aux0, aux1, aux2, aux3], dim=-1) # [..., 4]
|
| 271 |
+
byte = (aux.unsqueeze(-1) >> (8 * torch.arange(4, device=w.device))).reshape(
|
| 272 |
+
*scales.shape[:-1], 16) & 0xFF
|
| 273 |
+
sc8 = torch.where(byte >= 128, byte - 256, byte).to(torch.float32)
|
| 274 |
+
sc = (sc8 - 32)
|
| 275 |
+
dl = d.unsqueeze(-1).unsqueeze(-1) * sc.unsqueeze(-1)
|
| 276 |
+
dl_safe = torch.where(dl == 0, torch.ones_like(dl), dl) # C: if (!d) continue
|
| 277 |
+
Lq = _nearest_int(x / dl_safe).clamp(-4, 3).to(torch.int64) # [-4, 3]
|
| 278 |
+
|
| 279 |
+
# hmask: bit (j%32) set when level in 4..7.
|
| 280 |
+
lq_shift = Lq + 4
|
| 281 |
+
is_hi = (lq_shift > 3).to(torch.int64)
|
| 282 |
+
lq_final = (lq_shift - 4 * is_hi).reshape(out_f, nblk, QK_K)
|
| 283 |
+
is_hi_flat = is_hi.reshape(out_f, nblk, QK_K)
|
| 284 |
+
idx = torch.arange(QK_K, device=w.device)
|
| 285 |
+
hmask = torch.zeros(out_f, nblk, 32, dtype=torch.int64, device=w.device)
|
| 286 |
+
hmask.scatter_add_(-1, (idx % 32).view(1, 1, -1).expand(out_f, nblk, QK_K),
|
| 287 |
+
is_hi_flat << (idx // 32).view(1, 1, -1))
|
| 288 |
+
qs = _pack_q2_q3(lq_final)
|
| 289 |
+
|
| 290 |
+
return _cat_blocks([hmask.to(torch.uint8), qs, scales.to(torch.uint8),
|
| 291 |
+
_f16_bytes(d)], out_f, nblk, 110)
|
| 292 |
+
|
| 293 |
+
|
| 294 |
+
# ---------------------------------------------------------------------------
|
| 295 |
+
# Q4_K (llama.cpp:1457) - 8 sub-blocks of 32.
|
| 296 |
+
# ---------------------------------------------------------------------------
|
| 297 |
+
|
| 298 |
+
def pack_q4_k(w: torch.Tensor) -> torch.Tensor:
|
| 299 |
+
out_f, in_f = w.shape
|
| 300 |
+
wp, nblk = _pad(w, QK_K)
|
| 301 |
+
x = wp.view(out_f, nblk, 8, 32)
|
| 302 |
+
av_x = (x * x).mean(dim=-1, keepdim=True).sqrt() # [.., 8, 1]
|
| 303 |
+
weights = av_x + x.abs()
|
| 304 |
+
scale, m = _make_qkx2(15, x, weights, -1.0, 0.1, 20, False)
|
| 305 |
+
scale = scale.squeeze(-1)
|
| 306 |
+
m = m.squeeze(-1)
|
| 307 |
+
max_scale = scale.amax(dim=-1).clamp_min(EPS)
|
| 308 |
+
max_min = m.amax(dim=-1).clamp_min(EPS)
|
| 309 |
+
ls = _nearest_int((63.0 / max_scale).unsqueeze(-1) * scale).clamp(0, 63)
|
| 310 |
+
lm = _nearest_int((63.0 / max_min).unsqueeze(-1) * m).clamp(0, 63)
|
| 311 |
+
q = _pack_scales_min_k4(ls, lm) # [out, nb, 12]
|
| 312 |
+
d = max_scale / 63.0
|
| 313 |
+
dmin = max_min / 63.0
|
| 314 |
+
|
| 315 |
+
d_f = d.unsqueeze(-1).unsqueeze(-1)
|
| 316 |
+
dm_f = dmin.unsqueeze(-1).unsqueeze(-1)
|
| 317 |
+
sc, mm = _unpack_scales_min_k4(q)
|
| 318 |
+
dl = d_f * sc.to(torch.float32).unsqueeze(-1)
|
| 319 |
+
ml = dm_f * mm.to(torch.float32).unsqueeze(-1)
|
| 320 |
+
l = _nearest_int((x + ml) / dl.clamp_min(EPS)).clamp(0, 15).to(torch.int64)
|
| 321 |
+
l2 = l.reshape(out_f, nblk, 4, 2, 32)
|
| 322 |
+
qs = (l2[..., 0, :] | (l2[..., 1, :] << 4)).reshape(out_f, nblk, 128)
|
| 323 |
+
|
| 324 |
+
return _cat_blocks([_f16_bytes(d), _f16_bytes(dmin), q, qs],
|
| 325 |
+
out_f, nblk, 144)
|
| 326 |
+
|
| 327 |
+
|
| 328 |
+
# ---------------------------------------------------------------------------
|
| 329 |
+
# Q5_K (llama.cpp:1644) - 8 sub-blocks of 32.
|
| 330 |
+
# ---------------------------------------------------------------------------
|
| 331 |
+
|
| 332 |
+
def pack_q5_k(w: torch.Tensor) -> torch.Tensor:
|
| 333 |
+
out_f, in_f = w.shape
|
| 334 |
+
wp, nblk = _pad(w, QK_K)
|
| 335 |
+
x = wp.view(out_f, nblk, 8, 32)
|
| 336 |
+
av_x = (x * x).mean(dim=-1, keepdim=True).sqrt()
|
| 337 |
+
weights = av_x + x.abs()
|
| 338 |
+
scale, m = _make_qkx2(31, x, weights, -0.5, 0.1, 15, False)
|
| 339 |
+
scale = scale.squeeze(-1)
|
| 340 |
+
m = m.squeeze(-1)
|
| 341 |
+
max_scale = scale.amax(dim=-1).clamp_min(EPS)
|
| 342 |
+
max_min = m.amax(dim=-1).clamp_min(EPS)
|
| 343 |
+
ls = _nearest_int((63.0 / max_scale).unsqueeze(-1) * scale).clamp(0, 63)
|
| 344 |
+
lm = _nearest_int((63.0 / max_min).unsqueeze(-1) * m).clamp(0, 63)
|
| 345 |
+
q = _pack_scales_min_k4(ls, lm)
|
| 346 |
+
d = max_scale / 63.0
|
| 347 |
+
dmin = max_min / 63.0
|
| 348 |
+
|
| 349 |
+
d_f = d.unsqueeze(-1).unsqueeze(-1)
|
| 350 |
+
dm_f = dmin.unsqueeze(-1).unsqueeze(-1)
|
| 351 |
+
sc, mm = _unpack_scales_min_k4(q)
|
| 352 |
+
dl = d_f * sc.to(torch.float32).unsqueeze(-1)
|
| 353 |
+
ml = dm_f * mm.to(torch.float32).unsqueeze(-1)
|
| 354 |
+
l = _nearest_int((x + ml) / dl.clamp_min(EPS)).clamp(0, 31).to(torch.int64)
|
| 355 |
+
|
| 356 |
+
# ql + qh: for g in 0..3 (groups of 64): pair of 32-elem sub-blocks.
|
| 357 |
+
# Each sub-block s (0..7) contributes bit s when its level > 15.
|
| 358 |
+
hi_s = (l > 15).to(torch.int64) # [out, nblk, 8, 32]
|
| 359 |
+
qh = (hi_s * (1 << torch.arange(8, device=w.device)).view(1, 1, 8, 1)).sum(-2) # [out, nblk, 32]
|
| 360 |
+
lo1 = torch.where(hi_s.bool(), l - 16, l) # [out, nblk, 8, 32]
|
| 361 |
+
ql = (lo1[..., 0::2] | (lo1[..., 1::2] << 4)).reshape(out_f, nblk, 128)
|
| 362 |
+
|
| 363 |
+
return _cat_blocks([_f16_bytes(d), _f16_bytes(dmin), q,
|
| 364 |
+
qh.to(torch.uint8), ql], out_f, nblk, 176)
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
# ---------------------------------------------------------------------------
|
| 368 |
+
# Q6_K (llama.cpp:1869) - 16 sub-blocks of 16.
|
| 369 |
+
# ---------------------------------------------------------------------------
|
| 370 |
+
|
| 371 |
+
def _make_qx_quants_rmse(x: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 372 |
+
"""Vectorized make_qx_quants(16, 32, x, L, rmse_type=1, qw=NULL) (ggml-quants.c:628).
|
| 373 |
+
|
| 374 |
+
RMSE-weighted scale: scale = sumlx/suml2 with w = x^2.
|
| 375 |
+
x: [..., 16]. Returns (scale [..., 1], L in [-32, 31] as float).
|
| 376 |
+
"""
|
| 377 |
+
amax = x.abs().amax(dim=-1)
|
| 378 |
+
max_v = x.gather(-1, x.abs().argmax(dim=-1, keepdim=True)).squeeze(-1)
|
| 379 |
+
max_s = torch.where(amax < EPS, torch.zeros_like(max_v), max_v)
|
| 380 |
+
iscale = -32.0 / max_s.clamp_min(EPS)
|
| 381 |
+
l = _nearest_int(iscale.unsqueeze(-1) * x).clamp(-32, 31).to(torch.float32)
|
| 382 |
+
w_ = x * x
|
| 383 |
+
sumlx = (w_ * x * l).sum(dim=-1, keepdim=True)
|
| 384 |
+
suml2 = (w_ * l * l).sum(dim=-1, keepdim=True)
|
| 385 |
+
scale = torch.where(suml2 > 0, sumlx / suml2.clamp_min(EPS), torch.zeros_like(sumlx))
|
| 386 |
+
return scale, l
|
| 387 |
+
|
| 388 |
+
|
| 389 |
+
def pack_q6_k(w: torch.Tensor) -> torch.Tensor:
|
| 390 |
+
out_f, in_f = w.shape
|
| 391 |
+
wp, nblk = _pad(w, QK_K)
|
| 392 |
+
x = wp.view(out_f, nblk, 16, 16)
|
| 393 |
+
scale, _l = _make_qx_quants_rmse(x) # [.., 16, 1]
|
| 394 |
+
|
| 395 |
+
sc4 = scale.squeeze(-1) # [out, nb, 16] signed
|
| 396 |
+
max_sc = sc4.gather(-1, sc4.abs().argmax(dim=-1, keepdim=True)).squeeze(-1)
|
| 397 |
+
max_sc = torch.where(sc4.abs().amax(dim=-1) < EPS, torch.zeros_like(max_sc), max_sc)
|
| 398 |
+
|
| 399 |
+
isc = -128.0 / torch.where(max_sc.abs() < EPS, torch.ones_like(max_sc), max_sc)
|
| 400 |
+
sc16 = _nearest_int(isc.unsqueeze(-1) * sc4).clamp(-127, 127).to(torch.int64)
|
| 401 |
+
d = -max_sc / 128.0
|
| 402 |
+
|
| 403 |
+
dl = d.unsqueeze(-1).unsqueeze(-1) * sc16.to(torch.float32).unsqueeze(-1)
|
| 404 |
+
dl_safe = torch.where(dl == 0, torch.ones_like(dl), dl) # C: if (!d) continue
|
| 405 |
+
l = (_nearest_int(x / dl_safe).clamp(-32, 31).to(torch.int64) + 32).to(torch.int64) # 0..63
|
| 406 |
+
|
| 407 |
+
lo = l & 0xF
|
| 408 |
+
hi = (l >> 4) & 0x3
|
| 409 |
+
seg = l.view(out_f, nblk, 2, 8, 16) # [.., h, 8 subs, 16]
|
| 410 |
+
seg_lo = seg & 0xF
|
| 411 |
+
seg_hi = (seg >> 4) & 0x3
|
| 412 |
+
ql_h = torch.stack([
|
| 413 |
+
seg_lo[..., 0, :] | (seg_lo[..., 4, :] << 4),
|
| 414 |
+
seg_lo[..., 1, :] | (seg_lo[..., 5, :] << 4),
|
| 415 |
+
seg_lo[..., 2, :] | (seg_lo[..., 6, :] << 4),
|
| 416 |
+
seg_lo[..., 3, :] | (seg_lo[..., 7, :] << 4),
|
| 417 |
+
], dim=-2) # [out, nblk, 2, 4, 16]
|
| 418 |
+
ql = ql_h.reshape(out_f, nblk, 128)
|
| 419 |
+
qh_h = torch.stack([
|
| 420 |
+
seg_hi[..., 0, :] | (seg_hi[..., 2, :] << 2)
|
| 421 |
+
| (seg_hi[..., 4, :] << 4) | (seg_hi[..., 6, :] << 6),
|
| 422 |
+
seg_hi[..., 1, :] | (seg_hi[..., 3, :] << 2)
|
| 423 |
+
| (seg_hi[..., 5, :] << 4) | (seg_hi[..., 7, :] << 6),
|
| 424 |
+
], dim=-2) # [out, nblk, 2, 2, 16]
|
| 425 |
+
qh = qh_h.reshape(out_f, nblk, 64)
|
| 426 |
+
|
| 427 |
+
return _cat_blocks([ql, qh, sc16.to(torch.uint8), _f16_bytes(d)],
|
| 428 |
+
out_f, nblk, 210)
|
| 429 |
+
|
| 430 |
+
|
| 431 |
+
_GGUF_PACK = {
|
| 432 |
+
8: pack_q8_0,
|
| 433 |
+
10: pack_q2_k,
|
| 434 |
+
11: pack_q3_k,
|
| 435 |
+
12: pack_q4_k,
|
| 436 |
+
13: pack_q5_k,
|
| 437 |
+
14: pack_q6_k,
|
| 438 |
+
}
|
| 439 |
+
|
| 440 |
+
|
| 441 |
+
def pack_gguf_bytes(w: torch.Tensor, gguf_dtype: int) -> torch.Tensor:
|
| 442 |
+
"""Pack fp32 [out, in] weight into GGUF bytes for a Q*_K / Q8_0 dtype."""
|
| 443 |
+
fn = _GGUF_PACK.get(gguf_dtype)
|
| 444 |
+
if fn is None:
|
| 445 |
+
raise ValueError(f"pack_gguf_bytes: no packer for GGML dtype {gguf_dtype}")
|
| 446 |
+
return fn(w)
|
| 447 |
+
|
| 448 |
+
|
| 449 |
+
def packer_for(gguf_dtype: int):
|
| 450 |
+
"""Return the packer callable for a GGML dtype, or None for i-quants."""
|
| 451 |
+
return _GGUF_PACK.get(gguf_dtype)
|
| 452 |
+
|
| 453 |
+
|
| 454 |
+
def supported_pack_dtypes() -> list[int]:
|
| 455 |
+
return sorted(_GGUF_PACK.keys())
|
src/agiws_neural_quant/kquant/gguf_packed.py
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""GGUF k-quant packed slice-dequantization — torch, GPU-friendly, output-row slice.
|
| 2 |
+
|
| 3 |
+
Reads packed GGUF k-quant raw bytes (uint8 [out, bytes_per_row]) and dequantizes
|
| 4 |
+
ONLY output rows [start:end] on-the-fly — for chunked matmul via QuantizedModule.
|
| 5 |
+
Weights stay packed in VRAM; only the requested row slice is unpacked to fp16/fp32.
|
| 6 |
+
|
| 7 |
+
This is the memory-efficient path: a Q3_K_S 12 GB model stays ~12 GB in VRAM,
|
| 8 |
+
with only a small per-chunk dequant overhead (chunk_size rows at a time).
|
| 9 |
+
|
| 10 |
+
Block layouts (from ggml-common.h, same as gguf_kquant_unpack.py):
|
| 11 |
+
Q8_0 (34 B): d(f16) + qs[32] (int8). y = d * q8
|
| 12 |
+
Q4_K (144 B): d(f16) + dmin(f16) + scales[12] + qs[128] (4-bit).
|
| 13 |
+
y = d * sc * q4 - dmin * m (8 sub-blocks × 32, asymmetric)
|
| 14 |
+
Q5_K (176 B): d(f16) + dmin(f16) + scales[12] + qh[32] + qs[128] (4-bit).
|
| 15 |
+
y = d * sc * (q4 + 16*bit5) - dmin * m
|
| 16 |
+
Q6_K (210 B): ql[128] + qh[64] + scales[16] (int8) + d(f16).
|
| 17 |
+
y = d * sc * (q6 - 32) (16 sub-blocks × 16, symmetric)
|
| 18 |
+
|
| 19 |
+
All operations are on torch tensors (view uint8/int8, bit ops via int16/int32
|
| 20 |
+
intermediate) so they run on GPU when the buffer is on cuda.
|
| 21 |
+
"""
|
| 22 |
+
|
| 23 |
+
from __future__ import annotations
|
| 24 |
+
|
| 25 |
+
import torch
|
| 26 |
+
|
| 27 |
+
from agiws_neural_quant.kquant._gguf_bits import f16_view_to_f32, unpack_scales_k4
|
| 28 |
+
from agiws_neural_quant.kquant.gguf_packed_q2q3 import (
|
| 29 |
+
dequant_q2_k_packed_rows, dequant_q3_k_packed_rows,
|
| 30 |
+
)
|
| 31 |
+
from agiws_neural_quant.kquant.iq import (
|
| 32 |
+
dequant_iq2_xxs_packed_rows,
|
| 33 |
+
dequant_iq2_xs_packed,
|
| 34 |
+
dequant_iq2_s_packed,
|
| 35 |
+
dequant_iq3_xxs_packed,
|
| 36 |
+
dequant_iq3_s_packed,
|
| 37 |
+
dequant_iq1_s_packed,
|
| 38 |
+
dequant_iq1_m_packed,
|
| 39 |
+
dequant_iq4_nl_packed,
|
| 40 |
+
dequant_iq4_xs_packed,
|
| 41 |
+
)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
# GGML dtype ids (kept here to avoid a top-level import of converters.gguf_reader
|
| 45 |
+
# which would create a circular import via converters/__init__ -> universal -> quantizer).
|
| 46 |
+
GGML_TYPE_Q8_0 = 8
|
| 47 |
+
GGML_TYPE_Q4_K = 12
|
| 48 |
+
GGML_TYPE_Q5_K = 13
|
| 49 |
+
GGML_TYPE_Q6_K = 14
|
| 50 |
+
GGML_TYPE_Q2_K = 10
|
| 51 |
+
GGML_TYPE_Q3_K = 11
|
| 52 |
+
GGML_TYPE_IQ2_XXS = 16
|
| 53 |
+
GGML_TYPE_IQ2_XS = 17
|
| 54 |
+
GGML_TYPE_IQ3_XXS = 18
|
| 55 |
+
GGML_TYPE_IQ1_S = 19
|
| 56 |
+
GGML_TYPE_IQ4_NL = 20
|
| 57 |
+
GGML_TYPE_IQ3_S = 21
|
| 58 |
+
GGML_TYPE_IQ2_S = 22
|
| 59 |
+
GGML_TYPE_IQ4_XS = 23
|
| 60 |
+
GGML_TYPE_IQ1_M = 29
|
| 61 |
+
|
| 62 |
+
|
| 63 |
+
# Aliases kept for any external callers / tests.
|
| 64 |
+
def _f16_view_to_f32(u8: torch.Tensor) -> torch.Tensor:
|
| 65 |
+
return f16_view_to_f32(u8)
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def _unpack_scales_k4(scales: torch.Tensor):
|
| 69 |
+
return unpack_scales_k4(scales)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
def dequant_q8_0_packed_rows(
|
| 73 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 74 |
+
) -> torch.Tensor:
|
| 75 |
+
"""Dequant Q8_0 rows [start:end]. raw: [out, bytes_per_row] uint8."""
|
| 76 |
+
block = 32
|
| 77 |
+
elem = 34
|
| 78 |
+
chunk = raw[start:end].to(torch.int32) # [cr, bytes_per_row]
|
| 79 |
+
cr = chunk.shape[0]
|
| 80 |
+
n_blocks = (cols + block - 1) // block
|
| 81 |
+
chunk = chunk.reshape(cr, n_blocks, elem)
|
| 82 |
+
d = _f16_view_to_f32(chunk[..., 0:2]) # [cr, n_blocks]
|
| 83 |
+
qs = chunk[..., 2:34].to(torch.int8).to(torch.float32) # [cr, n_blocks, 32]
|
| 84 |
+
y = qs * d.unsqueeze(2)
|
| 85 |
+
y = y.reshape(cr, n_blocks * block)[:, :cols]
|
| 86 |
+
return y
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
def dequant_q4_k_packed_rows(
|
| 90 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 91 |
+
) -> torch.Tensor:
|
| 92 |
+
"""Dequant Q4_K rows [start:end]. raw: [out, bytes_per_row] uint8."""
|
| 93 |
+
block = 256
|
| 94 |
+
elem = 144
|
| 95 |
+
chunk = raw[start:end].to(torch.int32)
|
| 96 |
+
cr = chunk.shape[0]
|
| 97 |
+
n_blocks = (cols + block - 1) // block
|
| 98 |
+
chunk = chunk.reshape(cr, n_blocks, elem)
|
| 99 |
+
d = _f16_view_to_f32(chunk[..., 0:2]) # [cr, n_blocks]
|
| 100 |
+
dmin = _f16_view_to_f32(chunk[..., 2:4]) # [cr, n_blocks]
|
| 101 |
+
scales = chunk[..., 4:16].to(torch.uint8) # [cr, n_blocks, 12]
|
| 102 |
+
qs = chunk[..., 16:144] # [cr, n_blocks, 128]
|
| 103 |
+
sc, m = _unpack_scales_k4(scales) # each [cr, n_blocks, 8]
|
| 104 |
+
sc = sc.to(torch.float32)
|
| 105 |
+
m = m.to(torch.float32)
|
| 106 |
+
qs_pairs = qs.reshape(cr, n_blocks, 4, 32)
|
| 107 |
+
low = (qs_pairs & 0x0F).to(torch.float32)
|
| 108 |
+
high = (qs_pairs >> 4).to(torch.float32)
|
| 109 |
+
values = torch.empty(cr, n_blocks, 8, 32, dtype=torch.float32, device=raw.device)
|
| 110 |
+
values[..., 0::2, :] = low
|
| 111 |
+
values[..., 1::2, :] = high
|
| 112 |
+
y = d.unsqueeze(2).unsqueeze(3) * sc.unsqueeze(3) * values \
|
| 113 |
+
- dmin.unsqueeze(2).unsqueeze(3) * m.unsqueeze(3)
|
| 114 |
+
y = y.reshape(cr, n_blocks * block)[:, :cols]
|
| 115 |
+
return y
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
def dequant_q5_k_packed_rows(
|
| 119 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 120 |
+
) -> torch.Tensor:
|
| 121 |
+
"""Dequant Q5_K rows [start:end]. raw: [out, bytes_per_row] uint8."""
|
| 122 |
+
block = 256
|
| 123 |
+
elem = 176
|
| 124 |
+
chunk = raw[start:end].to(torch.int32)
|
| 125 |
+
cr = chunk.shape[0]
|
| 126 |
+
n_blocks = (cols + block - 1) // block
|
| 127 |
+
chunk = chunk.reshape(cr, n_blocks, elem)
|
| 128 |
+
d = _f16_view_to_f32(chunk[..., 0:2])
|
| 129 |
+
dmin = _f16_view_to_f32(chunk[..., 2:4])
|
| 130 |
+
scales = chunk[..., 4:16].to(torch.uint8)
|
| 131 |
+
qh = chunk[..., 16:48] # [cr, n_blocks, 32]
|
| 132 |
+
qs = chunk[..., 48:176]
|
| 133 |
+
sc, m = _unpack_scales_k4(scales)
|
| 134 |
+
sc = sc.to(torch.float32)
|
| 135 |
+
m = m.to(torch.float32)
|
| 136 |
+
qs_pairs = qs.reshape(cr, n_blocks, 4, 32)
|
| 137 |
+
low = (qs_pairs & 0x0F).to(torch.float32)
|
| 138 |
+
high = (qs_pairs >> 4).to(torch.float32)
|
| 139 |
+
values = torch.empty(cr, n_blocks, 8, 32, dtype=torch.float32, device=raw.device)
|
| 140 |
+
values[..., 0::2, :] = low
|
| 141 |
+
values[..., 1::2, :] = high
|
| 142 |
+
bit5 = torch.zeros(cr, n_blocks, 8, 32, dtype=torch.float32, device=raw.device)
|
| 143 |
+
for k in range(4):
|
| 144 |
+
bit5[..., 2 * k, :] = ((qh >> (2 * k)) & 1).to(torch.float32) * 16.0
|
| 145 |
+
bit5[..., 2 * k + 1, :] = ((qh >> (2 * k + 1)) & 1).to(torch.float32) * 16.0
|
| 146 |
+
values5 = values + bit5
|
| 147 |
+
y = d.unsqueeze(2).unsqueeze(3) * sc.unsqueeze(3) * values5 \
|
| 148 |
+
- dmin.unsqueeze(2).unsqueeze(3) * m.unsqueeze(3)
|
| 149 |
+
y = y.reshape(cr, n_blocks * block)[:, :cols]
|
| 150 |
+
return y
|
| 151 |
+
|
| 152 |
+
|
| 153 |
+
def dequant_q6_k_packed_rows(
|
| 154 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 155 |
+
) -> torch.Tensor:
|
| 156 |
+
"""Dequant Q6_K rows [start:end]. raw: [out, bytes_per_row] uint8.
|
| 157 |
+
|
| 158 |
+
Follows dequantize_row_q6_K (llama.cpp ggml-quants.c): per half-block n,
|
| 159 |
+
q1/q3 come from ql low/high nibble of first 32 bytes, q2/q4 of bytes 32-64,
|
| 160 |
+
with scale mapping sc[is+0/1/2/3] (is = l//16) -> sub-blocks 0/4/1/2
|
| 161 |
+
interleaved as in the reference.
|
| 162 |
+
"""
|
| 163 |
+
block = 256
|
| 164 |
+
elem = 210
|
| 165 |
+
chunk = raw[start:end].to(torch.int32)
|
| 166 |
+
cr = chunk.shape[0]
|
| 167 |
+
n_blocks = (cols + block - 1) // block
|
| 168 |
+
chunk = chunk.reshape(cr, n_blocks, elem)
|
| 169 |
+
ql = chunk[..., 0:128]
|
| 170 |
+
qh = chunk[..., 128:192]
|
| 171 |
+
sc = chunk[..., 192:208].to(torch.uint8).view(torch.int8).to(torch.float32) # [cr, n_blocks, 16]
|
| 172 |
+
d = _f16_view_to_f32(chunk[..., 208:210]) # [cr, n_blocks]
|
| 173 |
+
y = torch.zeros(cr, n_blocks, 256, dtype=torch.float32, device=raw.device)
|
| 174 |
+
for n in range(2):
|
| 175 |
+
ql_c = ql[:, :, n * 64:(n + 1) * 64]
|
| 176 |
+
qh_c = qh[:, :, n * 32:(n + 1) * 32]
|
| 177 |
+
q1 = ((ql_c[:, :, 0:32] & 0x0F) | (((qh_c >> 0) & 3) << 4)).to(torch.float32) - 32.0
|
| 178 |
+
q2 = ((ql_c[:, :, 32:64] & 0x0F) | (((qh_c >> 2) & 3) << 4)).to(torch.float32) - 32.0
|
| 179 |
+
q3 = ((ql_c[:, :, 0:32] >> 4) | (((qh_c >> 4) & 3) << 4)).to(torch.float32) - 32.0
|
| 180 |
+
q4 = ((ql_c[:, :, 32:64] >> 4) | (((qh_c >> 6) & 3) << 4)).to(torch.float32) - 32.0
|
| 181 |
+
# scale mapping per C dequantize_row_q6_K: y[l]=d*sc[is+0/2/4/6] (is=l//16),
|
| 182 |
+
# i.e. q1->sc[0..1], q2->sc[2..3], q3->sc[4..5], q4->sc[6..7]; sc+=8 per n.
|
| 183 |
+
base = n * 8
|
| 184 |
+
d4 = d.unsqueeze(-1)
|
| 185 |
+
off = n * 128
|
| 186 |
+
y[:, :, off + 0:off + 16] = d4 * sc[:, :, base + 0].unsqueeze(-1) * q1[:, :, 0:16]
|
| 187 |
+
y[:, :, off + 16:off + 32] = d4 * sc[:, :, base + 1].unsqueeze(-1) * q1[:, :, 16:32]
|
| 188 |
+
y[:, :, off + 32:off + 48] = d4 * sc[:, :, base + 2].unsqueeze(-1) * q2[:, :, 0:16]
|
| 189 |
+
y[:, :, off + 48:off + 64] = d4 * sc[:, :, base + 3].unsqueeze(-1) * q2[:, :, 16:32]
|
| 190 |
+
y[:, :, off + 64:off + 80] = d4 * sc[:, :, base + 4].unsqueeze(-1) * q3[:, :, 0:16]
|
| 191 |
+
y[:, :, off + 80:off + 96] = d4 * sc[:, :, base + 5].unsqueeze(-1) * q3[:, :, 16:32]
|
| 192 |
+
y[:, :, off + 96:off + 112] = d4 * sc[:, :, base + 6].unsqueeze(-1) * q4[:, :, 0:16]
|
| 193 |
+
y[:, :, off + 112:off + 128] = d4 * sc[:, :, base + 7].unsqueeze(-1) * q4[:, :, 16:32]
|
| 194 |
+
return y.reshape(cr, n_blocks * block)[:, :cols]
|
| 195 |
+
|
| 196 |
+
|
| 197 |
+
# Registry: GGML dtype id -> (slice dequant fn, block_size, bytes_per_block).
|
| 198 |
+
_GGUF_SLICE_DEQUANT = {
|
| 199 |
+
GGML_TYPE_Q8_0: (dequant_q8_0_packed_rows, 32, 34),
|
| 200 |
+
GGML_TYPE_Q4_K: (dequant_q4_k_packed_rows, 256, 144),
|
| 201 |
+
GGML_TYPE_Q5_K: (dequant_q5_k_packed_rows, 256, 176),
|
| 202 |
+
GGML_TYPE_Q6_K: (dequant_q6_k_packed_rows, 256, 210),
|
| 203 |
+
GGML_TYPE_Q2_K: (dequant_q2_k_packed_rows, 256, 84),
|
| 204 |
+
GGML_TYPE_Q3_K: (dequant_q3_k_packed_rows, 256, 110),
|
| 205 |
+
# i-quants (QK_K = 256; IQ4_NL uses QK4_NL = 32).
|
| 206 |
+
GGML_TYPE_IQ2_XXS: (dequant_iq2_xxs_packed_rows, 256, 66),
|
| 207 |
+
GGML_TYPE_IQ2_XS: (dequant_iq2_xs_packed, 256, 74),
|
| 208 |
+
GGML_TYPE_IQ2_S: (dequant_iq2_s_packed, 256, 82),
|
| 209 |
+
GGML_TYPE_IQ3_XXS: (dequant_iq3_xxs_packed, 256, 98),
|
| 210 |
+
GGML_TYPE_IQ3_S: (dequant_iq3_s_packed, 256, 110),
|
| 211 |
+
GGML_TYPE_IQ1_S: (dequant_iq1_s_packed, 256, 66),
|
| 212 |
+
GGML_TYPE_IQ1_M: (dequant_iq1_m_packed, 256, 56),
|
| 213 |
+
GGML_TYPE_IQ4_NL: (dequant_iq4_nl_packed, 32, 18),
|
| 214 |
+
GGML_TYPE_IQ4_XS: (dequant_iq4_xs_packed, 256, 136),
|
| 215 |
+
}
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
def supported_gguf_dtypes() -> set[int]:
|
| 219 |
+
"""GGML dtype ids that support packed slice-dequant."""
|
| 220 |
+
return set(_GGUF_SLICE_DEQUANT.keys())
|
| 221 |
+
|
| 222 |
+
|
| 223 |
+
def dequant_gguf_slice(
|
| 224 |
+
weight_raw: torch.Tensor,
|
| 225 |
+
gguf_dtype: int,
|
| 226 |
+
start: int,
|
| 227 |
+
end: int,
|
| 228 |
+
cols: int,
|
| 229 |
+
) -> torch.Tensor:
|
| 230 |
+
"""Dequantize output rows [start:end] from packed GGUF k-quant bytes.
|
| 231 |
+
|
| 232 |
+
Args:
|
| 233 |
+
weight_raw: uint8 tensor [out_total, bytes_per_row] of packed GGUF data.
|
| 234 |
+
gguf_dtype: GGML_TYPE_* id (Q8_0, Q4_K, Q5_K, Q6_K, Q2_K, Q3_K).
|
| 235 |
+
start, end: output row range to dequant (0-based, end exclusive).
|
| 236 |
+
cols: in_features — trim the dequantized output to this many columns
|
| 237 |
+
(the last block may be padded beyond cols).
|
| 238 |
+
|
| 239 |
+
Returns:
|
| 240 |
+
fp32 tensor [end-start, cols].
|
| 241 |
+
"""
|
| 242 |
+
entry = _GGUF_SLICE_DEQUANT.get(gguf_dtype)
|
| 243 |
+
if entry is None:
|
| 244 |
+
from agiws_neural_quant.converters.gguf_reader import GGML_TYPE_NAMES
|
| 245 |
+
raise ValueError(
|
| 246 |
+
f"dequant_gguf_slice: dtype id={gguf_dtype} "
|
| 247 |
+
f"({GGML_TYPE_NAMES.get(gguf_dtype, '?')}) not supported. "
|
| 248 |
+
f"Supported: {sorted(GGML_TYPE_NAMES[d] for d in _GGUF_SLICE_DEQUANT)}"
|
| 249 |
+
)
|
| 250 |
+
fn, _block, _bpb = entry
|
| 251 |
+
return fn(weight_raw, start, end, cols)
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
def bytes_per_row(gguf_dtype: int, cols: int) -> int:
|
| 255 |
+
"""Bytes per output row for a given GGUF dtype and column count."""
|
| 256 |
+
entry = _GGUF_SLICE_DEQUANT.get(gguf_dtype)
|
| 257 |
+
if entry is None:
|
| 258 |
+
raise ValueError(f"bytes_per_row: unsupported dtype {gguf_dtype}")
|
| 259 |
+
_fn, block, bpb = entry
|
| 260 |
+
n_blocks = (cols + block - 1) // block
|
| 261 |
+
return n_blocks * bpb
|
| 262 |
+
|
| 263 |
+
|
| 264 |
+
__all__ = [
|
| 265 |
+
"dequant_gguf_slice",
|
| 266 |
+
"dequant_q8_0_packed_rows",
|
| 267 |
+
"dequant_q4_k_packed_rows",
|
| 268 |
+
"dequant_q5_k_packed_rows",
|
| 269 |
+
"dequant_q6_k_packed_rows",
|
| 270 |
+
"dequant_q2_k_packed_rows",
|
| 271 |
+
"dequant_q3_k_packed_rows",
|
| 272 |
+
"dequant_iq2_xxs_packed_rows",
|
| 273 |
+
"dequant_iq2_xs_packed",
|
| 274 |
+
"dequant_iq2_s_packed",
|
| 275 |
+
"dequant_iq3_xxs_packed",
|
| 276 |
+
"dequant_iq3_s_packed",
|
| 277 |
+
"dequant_iq1_s_packed",
|
| 278 |
+
"dequant_iq1_m_packed",
|
| 279 |
+
"dequant_iq4_nl_packed",
|
| 280 |
+
"dequant_iq4_xs_packed",
|
| 281 |
+
"supported_gguf_dtypes",
|
| 282 |
+
"bytes_per_row",
|
| 283 |
+
]
|
src/agiws_neural_quant/kquant/gguf_packed_q2q3.py
ADDED
|
@@ -0,0 +1,126 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""GGUF Q2_K / Q3_K packed slice-dequantization (torch, output-row slice).
|
| 2 |
+
|
| 3 |
+
Exact llama.cpp layouts (ggml-common.h / ggml-quants.c):
|
| 4 |
+
|
| 5 |
+
block_q2_K (84 B): scales[16] (each byte: low nibble = 4-bit scale,
|
| 6 |
+
high nibble = 4-bit min)
|
| 7 |
+
+ qs[64] (256 x 2-bit) + d (f16) + dmin (f16)
|
| 8 |
+
dequant (dequantize_row_q2_K): 16 sub-blocks of 16.
|
| 9 |
+
sub-block is: n = is//8, j = is%8; qs byte = 32*n + 16*(is%2) + l;
|
| 10 |
+
shift = 2*(j%4); value = (qs[byte] >> shift) & 3;
|
| 11 |
+
y = d*sc*q - dmin*m, sc = scales[is]&0xF, m = scales[is]>>4
|
| 12 |
+
|
| 13 |
+
block_q3_K (110 B): hmask[32] + qs[64] + scales[12] + d(f16)
|
| 14 |
+
dequant (dequantize_row_q3_K): 16 sub-blocks of 16.
|
| 15 |
+
aux unpacking of scales[12] -> 16 int8 (signed), scale = int8 - 32
|
| 16 |
+
sub-block is: n = is//8, j_local = (is%8)//2, half = is%2;
|
| 17 |
+
qs byte = 32*n + 16*half + l; shift = 2*j_local;
|
| 18 |
+
m_bit = 1 << (j_local + 4*n); value = q - (hm[16*half+l]&m_bit ? 0 : 4);
|
| 19 |
+
y = d * scale * value
|
| 20 |
+
"""
|
| 21 |
+
|
| 22 |
+
from __future__ import annotations
|
| 23 |
+
|
| 24 |
+
import torch
|
| 25 |
+
|
| 26 |
+
from agiws_neural_quant.kquant._gguf_bits import f16_view_to_f32
|
| 27 |
+
|
| 28 |
+
# GGML dtype ids (local - avoids circular import via converters/__init__).
|
| 29 |
+
GGML_TYPE_Q2_K = 10
|
| 30 |
+
GGML_TYPE_Q3_K = 11
|
| 31 |
+
|
| 32 |
+
QK_K = 256
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def dequant_q2_k_packed_rows(
|
| 36 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 37 |
+
) -> torch.Tensor:
|
| 38 |
+
"""Dequant Q2_K rows [start:end]. raw: [out, 84*n_blocks] uint8."""
|
| 39 |
+
block = QK_K
|
| 40 |
+
elem = 84
|
| 41 |
+
chunk = raw[start:end].to(torch.int32)
|
| 42 |
+
cr = chunk.shape[0]
|
| 43 |
+
n_blocks = (cols + block - 1) // block
|
| 44 |
+
chunk = chunk.reshape(cr, n_blocks, elem)
|
| 45 |
+
scales = chunk[..., 0:16] # [cr, nb, 16]
|
| 46 |
+
qs = chunk[..., 16:80] # [cr, nb, 64]
|
| 47 |
+
d = f16_view_to_f32(chunk[..., 80:82]) # [cr, nb]
|
| 48 |
+
dmin = f16_view_to_f32(chunk[..., 82:84]) # [cr, nb]
|
| 49 |
+
|
| 50 |
+
sc4 = (scales & 0x0F).to(torch.float32) # [cr, nb, 16]
|
| 51 |
+
m4 = (scales >> 4).to(torch.float32)
|
| 52 |
+
|
| 53 |
+
y = torch.zeros(cr, n_blocks, QK_K, dtype=torch.float32, device=raw.device)
|
| 54 |
+
for is_ in range(16):
|
| 55 |
+
n = is_ // 8
|
| 56 |
+
j_local = (is_ // 2) % 4
|
| 57 |
+
half = is_ % 2
|
| 58 |
+
byte_off = 32 * n + 16 * half
|
| 59 |
+
shift = 2 * j_local
|
| 60 |
+
qb = (qs[..., byte_off:byte_off + 16] >> shift) & 0x03 # [cr, nb, 16]
|
| 61 |
+
sc = sc4[..., is_:is_ + 1].unsqueeze(-1) # [cr, nb, 1, 1]
|
| 62 |
+
mn = m4[..., is_:is_ + 1].unsqueeze(-1)
|
| 63 |
+
d4 = d.unsqueeze(-1).unsqueeze(-1)
|
| 64 |
+
dm4 = dmin.unsqueeze(-1).unsqueeze(-1)
|
| 65 |
+
y[..., is_ * 16:is_ * 16 + 16] = (d4 * sc * qb.unsqueeze(2) - dm4 * mn).squeeze(2)
|
| 66 |
+
return y.reshape(cr, n_blocks * block)[:, :cols]
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def dequant_q3_k_packed_rows(
|
| 70 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 71 |
+
) -> torch.Tensor:
|
| 72 |
+
"""Dequant Q3_K rows [start:end]. raw: [out, 110*n_blocks] uint8."""
|
| 73 |
+
block = QK_K
|
| 74 |
+
elem = 110
|
| 75 |
+
chunk = raw[start:end].to(torch.int32)
|
| 76 |
+
cr = chunk.shape[0]
|
| 77 |
+
n_blocks = (cols + block - 1) // block
|
| 78 |
+
chunk = chunk.reshape(cr, n_blocks, elem)
|
| 79 |
+
hmask = chunk[..., 0:32] # [cr, nb, 32]
|
| 80 |
+
qs = chunk[..., 32:96] # [cr, nb, 64]
|
| 81 |
+
scales = chunk[..., 96:108] # [cr, nb, 12]
|
| 82 |
+
d = f16_view_to_f32(chunk[..., 108:110]) # [cr, nb]
|
| 83 |
+
|
| 84 |
+
# Unpack 12 bytes into 4 little-endian u32, then reorder (aux logic).
|
| 85 |
+
# aux[0..2] = scales[0:12] as 3 LE u32; aux[3] is garbage-filled in C.
|
| 86 |
+
s0 = (scales[..., 0] | (scales[..., 1] << 8) | (scales[..., 2] << 16) | (scales[..., 3] << 24)).to(torch.int64)
|
| 87 |
+
s1 = (scales[..., 4] | (scales[..., 5] << 8) | (scales[..., 6] << 16) | (scales[..., 7] << 24)).to(torch.int64)
|
| 88 |
+
s2 = (scales[..., 8] | (scales[..., 9] << 8) | (scales[..., 10] << 16) | (scales[..., 11] << 24)).to(torch.int64)
|
| 89 |
+
kmask1 = 0x03030303
|
| 90 |
+
kmask2 = 0x0F0F0F0F
|
| 91 |
+
tmp = s2
|
| 92 |
+
aux2 = ((s0 >> 4) & kmask2) | (((tmp >> 4) & kmask1) << 4)
|
| 93 |
+
aux3 = ((s1 >> 4) & kmask2) | (((tmp >> 6) & kmask1) << 4)
|
| 94 |
+
aux0 = (s0 & kmask2) | (((tmp >> 0) & kmask1) << 4)
|
| 95 |
+
aux1 = (s1 & kmask2) | (((tmp >> 2) & kmask1) << 4)
|
| 96 |
+
# 16 int8 scales from the 16 LE bytes of aux0..aux3.
|
| 97 |
+
sc8 = torch.zeros(cr, n_blocks, 16, dtype=torch.int64, device=raw.device)
|
| 98 |
+
for i, aux in enumerate([aux0, aux1, aux2, aux3]):
|
| 99 |
+
for b in range(4):
|
| 100 |
+
byte = (aux >> (8 * b)) & 0xFF
|
| 101 |
+
sc8[..., 4 * i + b] = torch.where(byte >= 128, byte - 256, byte)
|
| 102 |
+
sc_f = (sc8 - 32).to(torch.float32) # [cr, nb, 16]
|
| 103 |
+
|
| 104 |
+
y = torch.zeros(cr, n_blocks, QK_K, dtype=torch.float32, device=raw.device)
|
| 105 |
+
for is_ in range(16):
|
| 106 |
+
n = is_ // 8
|
| 107 |
+
j_local = (is_ // 2) % 4
|
| 108 |
+
half = is_ % 2
|
| 109 |
+
byte_off = 32 * n + 16 * half
|
| 110 |
+
shift = 2 * j_local
|
| 111 |
+
m_shift = j_local + 4 * n # m = 1 << m_shift
|
| 112 |
+
qb = ((qs[..., byte_off:byte_off + 16] >> shift) & 0x03).to(torch.float32)
|
| 113 |
+
hb = ((hmask[..., 16 * half:16 * half + 16] >> m_shift) & 1).to(torch.float32)
|
| 114 |
+
val = qb - 4.0 * (1.0 - hb)
|
| 115 |
+
sc = sc_f[..., is_:is_ + 1].unsqueeze(-1)
|
| 116 |
+
d4 = d.unsqueeze(-1).unsqueeze(-1)
|
| 117 |
+
y[..., is_ * 16:is_ * 16 + 16] = (d4 * sc * val.unsqueeze(2)).squeeze(2)
|
| 118 |
+
return y.reshape(cr, n_blocks * block)[:, :cols]
|
| 119 |
+
|
| 120 |
+
|
| 121 |
+
__all__ = [
|
| 122 |
+
"dequant_q2_k_packed_rows",
|
| 123 |
+
"dequant_q3_k_packed_rows",
|
| 124 |
+
"GGML_TYPE_Q2_K",
|
| 125 |
+
"GGML_TYPE_Q3_K",
|
| 126 |
+
]
|
src/agiws_neural_quant/kquant/gguf_plan.py
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""UD (IQ2_M) layer plan - map GGUF tensor name -> quant dtype.
|
| 2 |
+
|
| 3 |
+
Direct translation of llama.cpp llama-quant.cpp `set_type_for_tensor` for
|
| 4 |
+
`LLAMA_FTYPE_MOSTLY_IQ2_M` (lines 450-529), for the dense `qwen35` arch:
|
| 5 |
+
|
| 6 |
+
token_embd -> IQ3_S
|
| 7 |
+
output (lm_head) -> Q5_K
|
| 8 |
+
attn_v -> Q4_K (n_gqa = 24/4 = 6 >= 4)
|
| 9 |
+
attn_output / ssm_out-> IQ3_S
|
| 10 |
+
ffn_down (first n/8) -> IQ3_S, rest -> IQ2_S
|
| 11 |
+
everything else -> IQ2_S
|
| 12 |
+
|
| 13 |
+
Small / norm / 1D tensors are handled by the caller as F32/F16 and must NOT
|
| 14 |
+
be listed here.
|
| 15 |
+
"""
|
| 16 |
+
|
| 17 |
+
from __future__ import annotations
|
| 18 |
+
|
| 19 |
+
# GGML dtype ids.
|
| 20 |
+
F32 = 0
|
| 21 |
+
F16 = 1
|
| 22 |
+
Q8_0 = 8
|
| 23 |
+
Q2_K = 10
|
| 24 |
+
Q5_K = 13
|
| 25 |
+
Q4_K = 12
|
| 26 |
+
IQ2_S = 22
|
| 27 |
+
IQ3_S = 21
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def _default_dtype(name: str, i_ffn_down: int, n_ffn_down: int,
|
| 31 |
+
n_head: int, n_head_kv: int, ftype_iq2_m: bool) -> int:
|
| 32 |
+
"""llama-quant.cpp set_type_for_tensor for MOSTLY_IQ2_M."""
|
| 33 |
+
if name == "token_embd.weight":
|
| 34 |
+
return IQ3_S if ftype_iq2_m else Q2_K
|
| 35 |
+
if name == "output.weight":
|
| 36 |
+
return Q5_K if ftype_iq2_m else Q8_0
|
| 37 |
+
if name.endswith(".attn_v.weight"):
|
| 38 |
+
# n_gqa >= 4 -> Q4_K; else IQ3_S (or Q2_K for non-IQ2_M).
|
| 39 |
+
if n_head // n_head_kv >= 4:
|
| 40 |
+
return Q4_K
|
| 41 |
+
return IQ3_S if ftype_iq2_m else Q2_K
|
| 42 |
+
if name.endswith(".attn_output.weight") or name.endswith(".ssm_out.weight"):
|
| 43 |
+
return IQ3_S if ftype_iq2_m else Q2_K
|
| 44 |
+
if name.endswith(".ffn_down.weight"):
|
| 45 |
+
if ftype_iq2_m and i_ffn_down < n_ffn_down // 8:
|
| 46 |
+
return IQ3_S
|
| 47 |
+
return IQ2_S
|
| 48 |
+
return IQ2_S
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def make_iq2_m_plan(tensor_names: list[str], *, n_head: int, n_head_kv: int,
|
| 52 |
+
ftype_iq2_m: bool = True) -> dict[str, int]:
|
| 53 |
+
"""Return {gguf_name: gguf_dtype} for the quantizable tensors.
|
| 54 |
+
|
| 55 |
+
tensor_names must be sorted in GGUF info-table order (llama.cpp walks
|
| 56 |
+
them in file order) so the `ffn_down` first-n/8 rule applies correctly.
|
| 57 |
+
"""
|
| 58 |
+
plan: dict[str, int] = {}
|
| 59 |
+
n_ffn_down = sum(1 for n in tensor_names if n.endswith(".ffn_down.weight"))
|
| 60 |
+
i_ffn_down = 0
|
| 61 |
+
for name in tensor_names:
|
| 62 |
+
if name.endswith(".ffn_down.weight"):
|
| 63 |
+
plan[name] = _default_dtype(name, i_ffn_down, n_ffn_down, n_head, n_head_kv, ftype_iq2_m)
|
| 64 |
+
i_ffn_down += 1
|
| 65 |
+
else:
|
| 66 |
+
plan[name] = _default_dtype(name, 0, n_ffn_down, n_head, n_head_kv, ftype_iq2_m)
|
| 67 |
+
return plan
|
src/agiws_neural_quant/kquant/iq.py
ADDED
|
@@ -0,0 +1,878 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""IQ-quant formats (llama.cpp i-quants): packed slice-dequant + quantizers.
|
| 2 |
+
|
| 3 |
+
All i-quants are codebook formats: weight values are indices into a lattice
|
| 4 |
+
grid; the grid entry (8 or 4 values) is scaled by a block delta and optionally
|
| 5 |
+
sign-flipped. Grids come from llama.cpp ggml-common.h (see _iq_tables.py).
|
| 6 |
+
|
| 7 |
+
Formats (block QK_K = 256, IQ4_NL uses QK4_NL = 32):
|
| 8 |
+
IQ2_XXS 2.0625 bpw d(f16) + qs[64B] = 66 B
|
| 9 |
+
IQ2_XS 2.3125 bpw d(f16) + qs[64B] + scales[8] = 74 B
|
| 10 |
+
IQ2_S 2.5625 bpw d(f16) + qs[64] + qh[8] + scales[8] = 82 B
|
| 11 |
+
IQ3_XXS 3.0625 bpw d(f16) + qs[96] = 98 B
|
| 12 |
+
IQ3_S 3.4375 bpw d(f16) + qs[64] + qh[8] + signs[32] + scales[4] = 110 B
|
| 13 |
+
IQ1_S 1.5625 bpw d(f16) + qs[32] + qh[16]u16 = 66 B
|
| 14 |
+
IQ1_M 1.75 bpw qs[32] + qh[16] + scales[8]u16 = 56 B
|
| 15 |
+
IQ4_NL 4.5 bpw d(f16) + qs[16] (block 32) = 18 B
|
| 16 |
+
IQ4_XS 4.25 bpw d(f16) + scales_h[2] + scales_l[4] + qs[128] = 136 B
|
| 17 |
+
|
| 18 |
+
Dequant follows the CPU reference (ggml-quants.c, dequantize_row_iq*_*) so
|
| 19 |
+
results match llama.cpp bit-for-bit on the same packed bytes. All functions
|
| 20 |
+
slice output rows [start:end] and are GPU-friendly (torch ops only).
|
| 21 |
+
|
| 22 |
+
Quantization (quantize_row_iq*_ref): for each 32-value group the sign pattern
|
| 23 |
+
is chosen so the flipped magnitude vector lies on the grid; the best scale is
|
| 24 |
+
found by weighted least squares over candidate scales; grid rows are looked
|
| 25 |
+
up via the precomputed map + neighbour lists (same as iq2xs_init_impl).
|
| 26 |
+
|
| 27 |
+
Author: AGIWS NeuralQuant team
|
| 28 |
+
License: Apache 2.0
|
| 29 |
+
"""
|
| 30 |
+
|
| 31 |
+
from __future__ import annotations
|
| 32 |
+
|
| 33 |
+
import torch
|
| 34 |
+
|
| 35 |
+
from agiws_neural_quant.kquant._iq_tables import iq_table
|
| 36 |
+
from agiws_neural_quant.kquant.gguf_pack import _cat_blocks
|
| 37 |
+
|
| 38 |
+
QK_K = 256
|
| 39 |
+
QK4_NL = 32
|
| 40 |
+
IQ1S_DELTA = 0.125
|
| 41 |
+
IQ1M_DELTA = 0.125
|
| 42 |
+
|
| 43 |
+
# GGML dtype ids (see converters/gguf_reader.py).
|
| 44 |
+
GGML_TYPE_IQ2_XXS = 16
|
| 45 |
+
GGML_TYPE_IQ2_XS = 17
|
| 46 |
+
GGML_TYPE_IQ3_XXS = 18
|
| 47 |
+
GGML_TYPE_IQ1_S = 19
|
| 48 |
+
GGML_TYPE_IQ4_NL = 20
|
| 49 |
+
GGML_TYPE_IQ3_S = 21
|
| 50 |
+
GGML_TYPE_IQ2_S = 22
|
| 51 |
+
GGML_TYPE_IQ4_XS = 23
|
| 52 |
+
GGML_TYPE_IQ1_M = 29
|
| 53 |
+
|
| 54 |
+
_IQ_BYTES_PER_BLOCK = {
|
| 55 |
+
GGML_TYPE_IQ2_XXS: 66,
|
| 56 |
+
GGML_TYPE_IQ2_XS: 74,
|
| 57 |
+
GGML_TYPE_IQ2_S: 82,
|
| 58 |
+
GGML_TYPE_IQ3_XXS: 98,
|
| 59 |
+
GGML_TYPE_IQ3_S: 110,
|
| 60 |
+
GGML_TYPE_IQ1_S: 66,
|
| 61 |
+
GGML_TYPE_IQ1_M: 56,
|
| 62 |
+
GGML_TYPE_IQ4_NL: 18,
|
| 63 |
+
GGML_TYPE_IQ4_XS: 136,
|
| 64 |
+
}
|
| 65 |
+
|
| 66 |
+
|
| 67 |
+
def bytes_per_row_iq(ggml_dtype: int, cols: int) -> int:
|
| 68 |
+
block = QK4_NL if ggml_dtype == GGML_TYPE_IQ4_NL else QK_K
|
| 69 |
+
nbl = (cols + block - 1) // block
|
| 70 |
+
return nbl * _IQ_BYTES_PER_BLOCK[ggml_dtype]
|
| 71 |
+
|
| 72 |
+
|
| 73 |
+
def _f16(u8: torch.Tensor) -> torch.Tensor:
|
| 74 |
+
"""Interpret raw [..., 2] bytes as fp16 -> fp32 (LE). Returns [...]. """
|
| 75 |
+
return u8.view(torch.float16)[..., 0].to(torch.float32)
|
| 76 |
+
|
| 77 |
+
|
| 78 |
+
def _le_u16(u8: torch.Tensor) -> torch.Tensor:
|
| 79 |
+
"""[..., 2] bytes -> int64 (LE)."""
|
| 80 |
+
return (u8[..., 0].to(torch.int64) | (u8[..., 1].to(torch.int64) << 8))
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def _signs_apply(vals: torch.Tensor, signs: torch.Tensor) -> torch.Tensor:
|
| 84 |
+
"""Flip sign of last-dim 8-tuples where the sign byte bit is set.
|
| 85 |
+
|
| 86 |
+
vals [..., 8], signs uint8 [..., 1] (last dim = 8-tuple index).
|
| 87 |
+
"""
|
| 88 |
+
mask = iq_table("kmask_iq2xs").to(signs.device) # [8]
|
| 89 |
+
bit = (signs.unsqueeze(-1) & mask).to(vals.dtype) # [..., 1, 8] -> [..., 8]
|
| 90 |
+
return torch.where(bit != 0, -vals, vals)
|
| 91 |
+
|
| 92 |
+
|
| 93 |
+
def _ksigns(idx: torch.Tensor) -> torch.Tensor:
|
| 94 |
+
"""128-entry ksigns table lookup."""
|
| 95 |
+
t = iq_table("ksigns_iq2xs").to(idx.device)
|
| 96 |
+
return t[idx]
|
| 97 |
+
|
| 98 |
+
|
| 99 |
+
# ---------------------------------------------------------------------------
|
| 100 |
+
# IQ2_XXS -- block 66 B: d(2) + qs(32 x u16 = 64 B)
|
| 101 |
+
# per 32-group ib (8 groups):
|
| 102 |
+
# q2 = qs[4*ib..4*ib+3] (4 u16)
|
| 103 |
+
# aux32 = q2[2] | q2[3]<<16 (upper half carries scale+signs)
|
| 104 |
+
# db = d * (0.5 + (aux32 >> 28)) * 0.25
|
| 105 |
+
# signs = ksigns[(aux32 >> 7*l) & 127]
|
| 106 |
+
# grid = iq2xxs_grid[ (u8)q2[l] ] (l=0..3) -> 8 values
|
| 107 |
+
# ---------------------------------------------------------------------------
|
| 108 |
+
def dequant_iq2_xxs_packed_rows(
|
| 109 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 110 |
+
) -> torch.Tensor:
|
| 111 |
+
chunk = raw[start:end].to(torch.uint8) # [cr, nbl*66]
|
| 112 |
+
cr = chunk.shape[0]
|
| 113 |
+
nblk = chunk.shape[1] // 66
|
| 114 |
+
chunk = chunk.view(cr, nblk, 66)
|
| 115 |
+
d = _f16(chunk[..., 0:2]) # [cr, nblk]
|
| 116 |
+
qs = chunk[..., 2:66].view(cr, nblk, 32, 2) # 32 u16 LE
|
| 117 |
+
q2 = _le_u16(qs).view(cr, nblk, 8, 4) # [cr, nblk, 8 grp, 4 u16]
|
| 118 |
+
aux32 = q2[..., 2] | (q2[..., 3] << 16) # [cr, nblk, 8]
|
| 119 |
+
db = d.unsqueeze(-1) * (0.5 + ((aux32 >> 28) & 0xF).to(torch.float32)) * 0.25 # [cr,nblk,8]
|
| 120 |
+
l4 = torch.arange(4, device=raw.device)
|
| 121 |
+
signs7 = (aux32.unsqueeze(-1) >> (7 * l4).view(1, 1, 1, 4)) & 127 # [cr,nblk,8,4]
|
| 122 |
+
signs = _ksigns(signs7) # [cr,nblk,8,4]
|
| 123 |
+
g = iq_table("iq2xxs_grid").to(raw.device) # [256, 8] int8
|
| 124 |
+
q0 = q2[..., 0]
|
| 125 |
+
q1 = q2[..., 1]
|
| 126 |
+
idx = torch.stack([q0 & 0xFF, q0 >> 8, q1 & 0xFF, q1 >> 8], dim=-1) # [cr,nblk,8,4]
|
| 127 |
+
vals = g[idx.to(torch.int64)] # [cr,nblk,8,4,8]
|
| 128 |
+
vals = _signs_apply(vals, signs)
|
| 129 |
+
out = db.unsqueeze(-1).unsqueeze(-1) * vals # [cr,nblk,8,4,8]
|
| 130 |
+
return out.reshape(cr, nblk * QK_K)[:, :cols]
|
| 131 |
+
|
| 132 |
+
|
| 133 |
+
# ---------------------------------------------------------------------------
|
| 134 |
+
# IQ2_XS -- block 74 B: d(2) + qs(64 B) + scales(8)
|
| 135 |
+
# qs: 32 u16; per group ib (8): q2 = qs16[4*ib+il] (il 0..3)
|
| 136 |
+
# grid = iq2xs_grid[q2 & 511], signs = ksigns[q2 >> 9]
|
| 137 |
+
# db = d * (0.5 + ((scales[ib] >> 4*(il/2)) & 0xF)) * 0.25
|
| 138 |
+
# ---------------------------------------------------------------------------
|
| 139 |
+
def dequant_iq2_xs_packed(
|
| 140 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 141 |
+
) -> torch.Tensor:
|
| 142 |
+
chunk = raw[start:end].to(torch.uint8)
|
| 143 |
+
cr = chunk.shape[0]
|
| 144 |
+
nblk = chunk.shape[1] // 74
|
| 145 |
+
chunk = chunk.view(cr, nblk, 74)
|
| 146 |
+
d = _f16(chunk[..., 0:2])
|
| 147 |
+
qs = chunk[..., 2:66].view(cr, nblk, 32, 2)
|
| 148 |
+
q2 = _le_u16(qs) # [cr, nblk, 32]
|
| 149 |
+
q2 = q2.view(cr, nblk, 8, 4) # [cr, nblk, 8, 4]
|
| 150 |
+
scales = chunk[..., 66:74] # [cr, nblk, 8]
|
| 151 |
+
gr = iq_table("iq2xs_grid").to(raw.device) # [512, 8]
|
| 152 |
+
vals = gr[(q2 & 0x1FF).to(torch.int64)] # [cr, nblk, 8, 4, 8]
|
| 153 |
+
signs = _ksigns((q2 >> 9).to(torch.int64))
|
| 154 |
+
vals = _signs_apply(vals, signs)
|
| 155 |
+
il = torch.arange(4, device=raw.device)
|
| 156 |
+
code = (scales.to(torch.int64).unsqueeze(-1) >> (4 * (il // 2)).view(1, 1, 1, 4)) & 0xF # [cr, nblk, 8, 4]
|
| 157 |
+
db = d.unsqueeze(-1).unsqueeze(-1) * (0.5 + code.to(torch.float32)) * 0.25 # [cr,nblk,8,4]
|
| 158 |
+
out = (db.unsqueeze(-1) * vals).reshape(cr, nblk * QK_K)[:, :cols]
|
| 159 |
+
return out
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
# ---------------------------------------------------------------------------
|
| 163 |
+
# IQ2_S -- block 82 B: d(2) + qs(64) + qh(8) + scales(8)
|
| 164 |
+
# per group ib (8), il 0..3:
|
| 165 |
+
# grid = iq2s_grid[ qs[4*ib+il] | ((qh[ib] << (8-2*il)) & 0x300) ]
|
| 166 |
+
# signs = qs[32 + 4*ib + il]
|
| 167 |
+
# db = d * (0.5 + ((scales[8] >> 4*(il/2)) & 0xF)) * 0.25
|
| 168 |
+
# ---------------------------------------------------------------------------
|
| 169 |
+
def dequant_iq2_s_packed(
|
| 170 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 171 |
+
) -> torch.Tensor:
|
| 172 |
+
chunk = raw[start:end].to(torch.uint8)
|
| 173 |
+
cr = chunk.shape[0]
|
| 174 |
+
nblk = chunk.shape[1] // 82
|
| 175 |
+
chunk = chunk.view(cr, nblk, 82)
|
| 176 |
+
d = _f16(chunk[..., 0:2])
|
| 177 |
+
qs = chunk[..., 2:66].to(torch.int64) # [cr, nblk, 64]
|
| 178 |
+
qh = chunk[..., 66:74].to(torch.int64) # [cr, nblk, 8]
|
| 179 |
+
scales = chunk[..., 74:82].to(torch.int64) # [cr, nblk, 8]
|
| 180 |
+
il = torch.arange(4, device=raw.device)
|
| 181 |
+
# Layout: indexes = qs[0..31] (4 per super-group), signs = qs[32..63].
|
| 182 |
+
idx_b = qs[..., :32].view(cr, nblk, 8, 4)
|
| 183 |
+
sign_b = qs[..., 32:64].view(cr, nblk, 8, 4)
|
| 184 |
+
gi = idx_b + ((qh.view(cr, nblk, 8, 1) << (8 - 2 * il.view(1, 1, 1, 4))) & 0x300)
|
| 185 |
+
gr = iq_table("iq2s_grid").to(raw.device) # [1024, 8]
|
| 186 |
+
vals = gr[gi] # [cr, nblk, 8, 4, 8]
|
| 187 |
+
vals = _signs_apply(vals, sign_b)
|
| 188 |
+
code = (scales.view(cr, nblk, 8, 1) >> (4 * (il.view(1, 1, 1, 4) // 2))) & 0xF
|
| 189 |
+
db = d.unsqueeze(-1).unsqueeze(-1) * (0.5 + code.to(torch.float32)) * 0.25
|
| 190 |
+
out = (db.unsqueeze(-1) * vals).reshape(cr, nblk * QK_K)[:, :cols]
|
| 191 |
+
return out
|
| 192 |
+
|
| 193 |
+
|
| 194 |
+
# ---------------------------------------------------------------------------
|
| 195 |
+
# IQ3_XXS -- block 98 B: d(2) + qs(96)
|
| 196 |
+
# qs[0:64] = 64 grid index bytes (8 groups x 8)
|
| 197 |
+
# qs[64:96] = scales_and_signs (8 groups x 4 = 32 B)
|
| 198 |
+
# per group ib: aux32 = sas[4*ib..4*ib+3] (LE u32)
|
| 199 |
+
# db = d * (0.5 + (aux32 >> 28)) * 0.5
|
| 200 |
+
# signs = ksigns[(aux32 >> 7*il) & 127] (il 0..3)
|
| 201 |
+
# grid1 = iq3xxs_grid[qs[8*ib + 2*il]], grid2 = [qs[8*ib+2*il+1]]
|
| 202 |
+
# y[j] = db*g1[j]*sgn, y[j+4] = db*g2[j]*sgn (j 0..3)
|
| 203 |
+
# ---------------------------------------------------------------------------
|
| 204 |
+
def dequant_iq3_xxs_packed(
|
| 205 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 206 |
+
) -> torch.Tensor:
|
| 207 |
+
chunk = raw[start:end].to(torch.uint8)
|
| 208 |
+
cr = chunk.shape[0]
|
| 209 |
+
nblk = chunk.shape[1] // 98
|
| 210 |
+
chunk = chunk.view(cr, nblk, 98)
|
| 211 |
+
d = _f16(chunk[..., 0:2])
|
| 212 |
+
qs = chunk[..., 2:66].to(torch.int64) # [cr, nblk, 64]
|
| 213 |
+
gas = chunk[..., 66:98].view(cr, nblk, 16, 2) # scales+signs 16 u16
|
| 214 |
+
aux = _le_u16(gas).view(cr, nblk, 8, 2) # [cr, nblk, 8 grp, 2 u16]
|
| 215 |
+
aux32 = (aux[..., 0] | (aux[..., 1] << 16)).to(torch.int64) # [cr, nblk, 8]
|
| 216 |
+
db = d.unsqueeze(-1) * (0.5 + ((aux32 >> 28) & 0xF).to(torch.float32)) * 0.5
|
| 217 |
+
l4 = torch.arange(4, device=raw.device)
|
| 218 |
+
signs7 = (aux32.unsqueeze(-1) >> (7 * l4).view(1, 1, 1, 4)) & 127 # [cr, nblk, 8, 4]
|
| 219 |
+
signs = _ksigns(signs7)
|
| 220 |
+
gr = iq_table("iq3xxs_grid").to(raw.device) # [256, 4]
|
| 221 |
+
idx = qs.view(cr, nblk, 8, 8) # [cr, nblk, 8 grp, 8 idx]
|
| 222 |
+
g1 = gr[idx[..., 0::2].to(torch.int64)] # [cr, nblk, 8, 4, 4]
|
| 223 |
+
g2 = gr[idx[..., 1::2].to(torch.int64)]
|
| 224 |
+
vals = torch.cat([g1, g2], dim=-1) # [cr, nblk, 8, 4, 8]
|
| 225 |
+
vals = _signs_apply(vals, signs)
|
| 226 |
+
out = (db.unsqueeze(-1).unsqueeze(-1) * vals).reshape(cr, nblk * QK_K)[:, :cols]
|
| 227 |
+
return out
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
# ---------------------------------------------------------------------------
|
| 231 |
+
# IQ3_S -- block 110 B: d(2) + qs(64) + qh(8) + signs(32) + scales(4)
|
| 232 |
+
# per group ib (0..7), il (0..3):
|
| 233 |
+
# grid1 = iq3s_grid[ qs[8*ib + 2*il] | ((qh[ib] << (8-2*il)) & 256) ]
|
| 234 |
+
# grid2 = iq3s_grid[ qs[8*ib + 2*il+1] | ((qh[ib] << (7-2*il)) & 256) ]
|
| 235 |
+
# db = d * (1 + 2*((scales[ib/2] >> 4*(ib%2)) & 0xF))
|
| 236 |
+
# signs = signs[4*ib + il]
|
| 237 |
+
# y[j] = db*grid1[j]*sgn, y[j+4] = db*grid2[j]*sgn (j 0..3)
|
| 238 |
+
# ---------------------------------------------------------------------------
|
| 239 |
+
def dequant_iq3_s_packed(
|
| 240 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 241 |
+
) -> torch.Tensor:
|
| 242 |
+
chunk = raw[start:end].to(torch.uint8)
|
| 243 |
+
cr = chunk.shape[0]
|
| 244 |
+
nblk = chunk.shape[1] // 110
|
| 245 |
+
chunk = chunk.view(cr, nblk, 110)
|
| 246 |
+
d = _f16(chunk[..., 0:2]) # [cr, nblk]
|
| 247 |
+
qs = chunk[..., 2:66].to(torch.int64) # [cr, nblk, 64]
|
| 248 |
+
qh = chunk[..., 66:74].to(torch.int64) # [cr, nblk, 8]
|
| 249 |
+
signs = chunk[..., 74:106] # [cr, nblk, 32]
|
| 250 |
+
scales = chunk[..., 106:110].to(torch.int64) # [cr, nblk, 4]
|
| 251 |
+
ib = torch.arange(8, device=raw.device)
|
| 252 |
+
il = torch.arange(4, device=raw.device)
|
| 253 |
+
qs8 = qs.view(cr, nblk, 8, 8) # [cr, nblk, grp, 8 idx bytes]
|
| 254 |
+
qh_sel = qh.view(cr, nblk, 8, 1)
|
| 255 |
+
gi1 = qs8[..., 0::2] + ((qh_sel << (8 - 2 * il.view(1, 1, 1, 4))) & 256) # [cr,nblk,8,4]
|
| 256 |
+
gi2 = qs8[..., 1::2] + ((qh_sel << (7 - 2 * il.view(1, 1, 1, 4))) & 256)
|
| 257 |
+
gr = iq_table("iq3s_grid").to(raw.device) # [512, 4]
|
| 258 |
+
v1 = gr[gi1] # [cr, nblk, 8, 4, 4]
|
| 259 |
+
v2 = gr[gi2]
|
| 260 |
+
vals = torch.cat([v1, v2], dim=-1) # [cr, nblk, 8, 4, 8]
|
| 261 |
+
# db per group (8 groups: scales[ib/2], nibble by ib%2)
|
| 262 |
+
code = (scales.view(cr, nblk, 4).repeat_interleave(2, dim=2)
|
| 263 |
+
>> (4 * (ib % 2).view(1, 1, 8))) & 0xF # [cr, nblk, 8]
|
| 264 |
+
db = d.unsqueeze(-1) * (1 + 2 * code.to(torch.float32)) # [cr, nblk, 8]
|
| 265 |
+
# signs per group/il
|
| 266 |
+
sgn = signs.view(cr, nblk, 8, 4) # [cr, nblk, 8, 4]
|
| 267 |
+
vals = _signs_apply(vals, sgn)
|
| 268 |
+
out = db.unsqueeze(-1).unsqueeze(-1) * vals # [cr, nblk, 8, 4, 8]
|
| 269 |
+
return out.reshape(cr, nblk * QK_K)[:, :cols]
|
| 270 |
+
|
| 271 |
+
|
| 272 |
+
# ---------------------------------------------------------------------------
|
| 273 |
+
# IQ1_S -- block 66 B: d(2) + qs(32) + qh(16 u16)
|
| 274 |
+
# per 32-group ib (8):
|
| 275 |
+
# dl = d * (2*((qh[ib] >> 12) & 7) + 1)
|
| 276 |
+
# delta = qh[ib] & 0x8000 ? -IQ1S_DELTA : +IQ1S_DELTA
|
| 277 |
+
# grid idx = qs[4*ib+il] | (((qh[ib] >> 3*il) & 7) << 8) (il 0..3)
|
| 278 |
+
# grid = iq1s_grid[int8] (signed! values in {-1, 0, 1}-ish)
|
| 279 |
+
# y[j] = dl * (grid[j] + delta)
|
| 280 |
+
# ---------------------------------------------------------------------------
|
| 281 |
+
def dequant_iq1_s_packed(
|
| 282 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 283 |
+
) -> torch.Tensor:
|
| 284 |
+
chunk = raw[start:end].to(torch.uint8)
|
| 285 |
+
cr = chunk.shape[0]
|
| 286 |
+
nblk = chunk.shape[1] // 66
|
| 287 |
+
chunk = chunk.view(cr, nblk, 66)
|
| 288 |
+
d = _f16(chunk[..., 0:2]) # [cr, nblk]
|
| 289 |
+
qs = chunk[..., 2:34].to(torch.int64) # [cr, nblk, 32]
|
| 290 |
+
qh = chunk[..., 34:66].view(cr, nblk, 16, 2)
|
| 291 |
+
qh = _le_u16(qh) # [cr, nblk, 16]
|
| 292 |
+
ib = torch.arange(8, device=raw.device)
|
| 293 |
+
il = torch.arange(4, device=raw.device)
|
| 294 |
+
qs8 = qs.view(cr, nblk, 8, 4)
|
| 295 |
+
qh8 = qh.view(cr, nblk, 8, 2) # 2 u16 per group
|
| 296 |
+
# per group: uses qh8[..., 0] for scale/delta, and qh8[..., 0] for index too
|
| 297 |
+
dl = d.unsqueeze(-1) * (2 * ((qh8[..., 0] >> 12) & 7) + 1).to(torch.float32) # [cr,nblk,8]
|
| 298 |
+
delta = torch.where((qh8[..., 0] & 0x8000) != 0,
|
| 299 |
+
-IQ1S_DELTA, IQ1S_DELTA) # [cr,nblk,8]
|
| 300 |
+
idx = qs8 + (((qh8[..., 0].unsqueeze(-1) >> (3 * il.view(1, 1, 1, 4))) & 7) << 8)
|
| 301 |
+
gr = iq_table("iq1s_grid").to(raw.device) # [2048, 8] int8 signed
|
| 302 |
+
vals = gr[idx].to(torch.float32) # [cr,nblk,8,4,8]
|
| 303 |
+
out = dl.unsqueeze(-1).unsqueeze(-1) * (vals + delta.unsqueeze(-1).unsqueeze(-1))
|
| 304 |
+
return out.reshape(cr, nblk * QK_K)[:, :cols]
|
| 305 |
+
|
| 306 |
+
|
| 307 |
+
# ---------------------------------------------------------------------------
|
| 308 |
+
# IQ1_M -- block 56 B: qs(32) + qh(16) + scales(8 u16)
|
| 309 |
+
# No fp16 d! scale is f16 packed into 4 bytes of scales.
|
| 310 |
+
# scale.f16 = (sc[0]>>12) | ((sc[1]>>8)&0xF0) | ((sc[2]>>4)&0xF00) | (sc[3]&0xF000)
|
| 311 |
+
# per group ib: dl1 = d*(2*((sc[ib/2] >> 6*(ib%2)+0) & 0x7) + 1)
|
| 312 |
+
# dl2 = d*(2*((sc[ib/2] >> 6*(ib%2)+3) & 0x7) + 1)
|
| 313 |
+
# idx[0] = qs[0] | ((qh[0]<<8)&0x700), idx[1] = qs[1] | ((qh[0]<<4)&0x700)
|
| 314 |
+
# idx[2] = qs[2] | ((qh[1]<<8)&0x700), idx[3] = qs[3] | ((qh[1]<<4)&0x700)
|
| 315 |
+
# delta[l] = qh[l/2] & (0x08 << 4*(l%2)) ? -IQ1M_DELTA : +IQ1M_DELTA
|
| 316 |
+
# y = dl1 * (grid[idx[l]] + delta[l]) (l 0,1) dl2 for l 2,3
|
| 317 |
+
# ---------------------------------------------------------------------------
|
| 318 |
+
def dequant_iq1_m_packed(
|
| 319 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 320 |
+
) -> torch.Tensor:
|
| 321 |
+
chunk = raw[start:end].to(torch.uint8)
|
| 322 |
+
cr = chunk.shape[0]
|
| 323 |
+
nblk = chunk.shape[1] // 56
|
| 324 |
+
chunk = chunk.view(cr, nblk, 56)
|
| 325 |
+
qs = chunk[..., 0:32].to(torch.int64) # [cr,nblk,32]
|
| 326 |
+
qh = chunk[..., 32:48].to(torch.int64) # [cr,nblk,16]
|
| 327 |
+
sc = chunk[..., 48:56].view(cr, nblk, 4, 2)
|
| 328 |
+
sc = _le_u16(sc) # [cr,nblk,4]
|
| 329 |
+
# reassemble f16: nibbles from sc[0..3]
|
| 330 |
+
u = (sc[..., 0] >> 12) | ((sc[..., 1] >> 8) & 0x00F0) \
|
| 331 |
+
| ((sc[..., 2] >> 4) & 0x0F00) | (sc[..., 3] & 0xF000) # [cr,nblk]
|
| 332 |
+
d = u.to(torch.int16).view(torch.float16).to(torch.float32)
|
| 333 |
+
ib = torch.arange(8, device=raw.device)
|
| 334 |
+
il = torch.arange(4, device=raw.device)
|
| 335 |
+
sc8 = sc.repeat_interleave(2, dim=2) # [cr,nblk,8]
|
| 336 |
+
# dl1 = d*(2*((sc8[ib] >> 6*(ib%2)) & 7)+1)
|
| 337 |
+
dl1 = d.unsqueeze(-1) * (2 * ((sc8 >> (6 * (ib % 2).view(1, 1, 8))) & 0x7) + 1).to(torch.float32)
|
| 338 |
+
dl2 = d.unsqueeze(-1) * (2 * ((sc8 >> (6 * (ib % 2).view(1, 1, 8) + 3)) & 0x7) + 1).to(torch.float32)
|
| 339 |
+
qh2 = qh.view(cr, nblk, 8, 2) # [cr,nblk,8,2]
|
| 340 |
+
qs8 = qs.view(cr, nblk, 8, 4)
|
| 341 |
+
idx0 = qs8[..., 0] | ((qh2[..., 0] << 8) & 0x700)
|
| 342 |
+
idx1 = qs8[..., 1] | ((qh2[..., 0] << 4) & 0x700)
|
| 343 |
+
idx2 = qs8[..., 2] | ((qh2[..., 1] << 8) & 0x700)
|
| 344 |
+
idx3 = qs8[..., 3] | ((qh2[..., 1] << 4) & 0x700)
|
| 345 |
+
gr = iq_table("iq1s_grid").to(torch.int64) # [2048, 8]
|
| 346 |
+
g0 = gr[idx0].to(torch.float32)
|
| 347 |
+
g1 = gr[idx1].to(torch.float32)
|
| 348 |
+
g2 = gr[idx2].to(torch.float32)
|
| 349 |
+
g3 = gr[idx3].to(torch.float32)
|
| 350 |
+
delta0 = torch.where((qh2[..., 0] & 0x08) != 0, -IQ1M_DELTA, IQ1M_DELTA)
|
| 351 |
+
delta1 = torch.where((qh2[..., 0] & 0x80) != 0, -IQ1M_DELTA, IQ1M_DELTA)
|
| 352 |
+
delta2 = torch.where((qh2[..., 1] & 0x08) != 0, -IQ1M_DELTA, IQ1M_DELTA)
|
| 353 |
+
delta3 = torch.where((qh2[..., 1] & 0x80) != 0, -IQ1M_DELTA, IQ1M_DELTA)
|
| 354 |
+
out = torch.cat([
|
| 355 |
+
(dl1.unsqueeze(-1) * (g0 + delta0.unsqueeze(-1))).unsqueeze(-2),
|
| 356 |
+
(dl1.unsqueeze(-1) * (g1 + delta1.unsqueeze(-1))).unsqueeze(-2),
|
| 357 |
+
(dl2.unsqueeze(-1) * (g2 + delta2.unsqueeze(-1))).unsqueeze(-2),
|
| 358 |
+
(dl2.unsqueeze(-1) * (g3 + delta3.unsqueeze(-1))).unsqueeze(-2),
|
| 359 |
+
], dim=-2) # [cr,nblk,8,4,8]
|
| 360 |
+
return out.reshape(cr, nblk * QK_K)[:, :cols]
|
| 361 |
+
|
| 362 |
+
|
| 363 |
+
# ---------------------------------------------------------------------------
|
| 364 |
+
# IQ4_NL -- block 18 B: d(2) + qs(16)
|
| 365 |
+
# y[j] = d * kvalues_iq4nl[qs[j] & 0xF], y[j+16] = d * kvalues_iq4nl[qs[j] >> 4]
|
| 366 |
+
# ---------------------------------------------------------------------------
|
| 367 |
+
def dequant_iq4_nl_packed(
|
| 368 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 369 |
+
) -> torch.Tensor:
|
| 370 |
+
chunk = raw[start:end].to(torch.uint8)
|
| 371 |
+
cr = chunk.shape[0]
|
| 372 |
+
nblk = chunk.shape[1] // 18
|
| 373 |
+
chunk = chunk.view(cr, nblk, 18)
|
| 374 |
+
d = _f16(chunk[..., 0:2]) # [cr,nblk]
|
| 375 |
+
qs = chunk[..., 2:18] # [cr,nblk,16]
|
| 376 |
+
kv = iq_table("kvalues_iq4nl").to(raw.device) # [16]
|
| 377 |
+
lo = kv[qs.to(torch.int64) & 0xF] # [cr,nblk,16]
|
| 378 |
+
hi = kv[(qs >> 4).to(torch.int64)]
|
| 379 |
+
vals = torch.stack([lo, hi], dim=-1).reshape(cr, nblk, 32)
|
| 380 |
+
return (d.unsqueeze(-1) * vals).reshape(cr, nblk * QK4_NL)[:, :cols]
|
| 381 |
+
|
| 382 |
+
|
| 383 |
+
# ---------------------------------------------------------------------------
|
| 384 |
+
# IQ4_XS -- block 136 B: d(2) + scales_h(2) + scales_l(4) + qs(128)
|
| 385 |
+
# per 32-group ib (8): ls = ((scales_l[ib/2] >> 4*(ib%2)) & 0xF)
|
| 386 |
+
# | (((scales_h >> 2*ib) & 3) << 4)
|
| 387 |
+
# dl = d * (ls - 32)
|
| 388 |
+
# y[j] = dl * kvalues_iq4nl[qs[j] & 0xF], y[j+16] = dl * kvalues_iq4nl[qs[j] >> 4]
|
| 389 |
+
# ---------------------------------------------------------------------------
|
| 390 |
+
def dequant_iq4_xs_packed(
|
| 391 |
+
raw: torch.Tensor, start: int, end: int, cols: int
|
| 392 |
+
) -> torch.Tensor:
|
| 393 |
+
chunk = raw[start:end].to(torch.uint8)
|
| 394 |
+
cr = chunk.shape[0]
|
| 395 |
+
nblk = chunk.shape[1] // 136
|
| 396 |
+
chunk = chunk.view(cr, nblk, 136)
|
| 397 |
+
d = _f16(chunk[..., 0:2])
|
| 398 |
+
scales_h = _le_u16(chunk[..., 2:4]) # [cr, nblk] uint16
|
| 399 |
+
scales_l = chunk[..., 4:8].to(torch.int64) # [cr, nblk, 4]
|
| 400 |
+
qs = chunk[..., 8:136] # [cr, nblk, 128]
|
| 401 |
+
ib = torch.arange(8, device=raw.device)
|
| 402 |
+
ls = (scales_l.gather(2, (ib // 2).view(1, 1, 8).expand(cr, nblk, 8))
|
| 403 |
+
>> (4 * (ib % 2)).view(1, 1, 8)) & 0xF
|
| 404 |
+
ls = ls | (((scales_h.unsqueeze(-1) >> (2 * ib).view(1, 1, 8)) & 3) << 4) # [cr,nblk,8]
|
| 405 |
+
dl = d.unsqueeze(-1) * (ls.to(torch.float32) - 32)
|
| 406 |
+
kv = iq_table("kvalues_iq4nl").to(raw.device)
|
| 407 |
+
qs32 = qs.view(cr, nblk, 8, 16)
|
| 408 |
+
lo = kv[qs32.to(torch.int64) & 0xF] # [cr,nblk,8,16]
|
| 409 |
+
hi = kv[(qs32 >> 4).to(torch.int64)]
|
| 410 |
+
vals = torch.stack([lo, hi], dim=-1).reshape(cr, nblk, 8, 32)
|
| 411 |
+
return (dl.unsqueeze(-1) * vals).reshape(cr, nblk * QK_K)[:, :cols]
|
| 412 |
+
|
| 413 |
+
|
| 414 |
+
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
# ===========================================================================
|
| 418 |
+
# Quantizers (self-consistent with the dequantizers above)
|
| 419 |
+
# ===========================================================================
|
| 420 |
+
|
| 421 |
+
_SCALE_CANDIDATES = 17
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
def _find_nearest(
|
| 425 |
+
x: torch.Tensor, # [M, W] fp32 (magnitudes if not signed)
|
| 426 |
+
grid: torch.Tensor, # [G, W] int8 grid values (W = 4 or 8)
|
| 427 |
+
signed: bool = False, # grid rows carry signs (IQ1-style: {-1,0,1})
|
| 428 |
+
) -> tuple[torch.Tensor, torch.Tensor]:
|
| 429 |
+
"""Best (grid row, scale) for each W-vector by weighted nearest search.
|
| 430 |
+
|
| 431 |
+
Works for any grid row width W (4 for IQ3-style grids, 8 for IQ1/IQ2).
|
| 432 |
+
Weighted (by |x|) squared distance to every grid row over a log-spaced
|
| 433 |
+
candidate-scale grid; chunked over M. Returns (idx [M] int64,
|
| 434 |
+
scale [M] fp32). scale is the per-element multiplier: dequant gives
|
| 435 |
+
y = scale * grid values (scaled by the block d later).
|
| 436 |
+
"""
|
| 437 |
+
M = x.shape[0]
|
| 438 |
+
G = grid.shape[0]
|
| 439 |
+
gf = grid.to(torch.float32) # [G, W]
|
| 440 |
+
xabs = x.abs()
|
| 441 |
+
gmax = gf.abs().amax(dim=-1).max().clamp(min=1e-8)
|
| 442 |
+
amax = xabs.amax(dim=-1).clamp(min=1e-8) # [M]
|
| 443 |
+
cand = (amax / gmax).unsqueeze(-1) * torch.logspace(
|
| 444 |
+
-2.0, 1.0, _SCALE_CANDIDATES, device=x.device) # [M, C] ~0.01..10 x amax
|
| 445 |
+
w = (xabs + 1e-6) # importance weight
|
| 446 |
+
wnorm = w / w.sum(-1, keepdim=True).clamp(min=1e-8) # [M, W]
|
| 447 |
+
idx = torch.empty(M, dtype=torch.int64, device=x.device)
|
| 448 |
+
scale = torch.empty(M, dtype=torch.float32, device=x.device)
|
| 449 |
+
chunk = max(1, min(M, (1 << 21) // max(1, G)))
|
| 450 |
+
for s0 in range(0, M, chunk):
|
| 451 |
+
e = min(s0 + chunk, M)
|
| 452 |
+
xs = x[s0:e] # [K, W]
|
| 453 |
+
wn = wnorm[s0:e] # [K, W]
|
| 454 |
+
cs = cand[s0:e] # [K, C]
|
| 455 |
+
# d2[K, C, G] = sum_i wn_i * (x_i - s*g_i)^2
|
| 456 |
+
xg = (wn * xs) @ gf.T # [K, G]
|
| 457 |
+
g2w = (wn.unsqueeze(1) * gf.unsqueeze(0) ** 2).sum(-1) # [K, G]
|
| 458 |
+
x2w = (wn * xs * xs).sum(-1) # [K]
|
| 459 |
+
d2 = x2w.unsqueeze(-1).unsqueeze(-1) \
|
| 460 |
+
- 2 * cs.unsqueeze(-1) * xg.unsqueeze(1) \
|
| 461 |
+
+ (cs.unsqueeze(-1) ** 2) * g2w.unsqueeze(1) # [K, C, G]
|
| 462 |
+
d2 = d2.clamp(min=0)
|
| 463 |
+
flat = d2.view(e - s0, -1)
|
| 464 |
+
best = flat.argmin(dim=-1) # [K]
|
| 465 |
+
scale_idx = best // G
|
| 466 |
+
grid_idx = best % G
|
| 467 |
+
scale[s0:e] = cs.view(e - s0, -1).gather(1, scale_idx.unsqueeze(-1)).squeeze(-1)
|
| 468 |
+
idx[s0:e] = grid_idx
|
| 469 |
+
# Refine scale by weighted least squares on the chosen row.
|
| 470 |
+
gv = grid[idx].to(torch.float32)
|
| 471 |
+
num = (w * x * gv).sum(-1)
|
| 472 |
+
den = (w * gv * gv).sum(-1).clamp(min=1e-8)
|
| 473 |
+
scale = torch.where(den > 0, num / den, scale)
|
| 474 |
+
return idx, scale
|
| 475 |
+
|
| 476 |
+
|
| 477 |
+
def _pick_signs8(x8: torch.Tensor) -> tuple[torch.Tensor, torch.Tensor]:
|
| 478 |
+
"""Sign bits with even parity (mirror llama.cpp ksigns trick).
|
| 479 |
+
|
| 480 |
+
Returns (sign_bits [.., 8] int64 0/1, xabs [.., 8] fp32). For odd-popcount
|
| 481 |
+
vectors the smallest-magnitude element is flipped.
|
| 482 |
+
"""
|
| 483 |
+
sgn = (x8 < 0).to(torch.int64)
|
| 484 |
+
odd = (sgn.sum(-1) % 2) == 1
|
| 485 |
+
if odd.any():
|
| 486 |
+
mn = x8.abs().argmin(-1) # [..]
|
| 487 |
+
o = odd.unsqueeze(-1)
|
| 488 |
+
m = mn.unsqueeze(-1)
|
| 489 |
+
pos = torch.arange(x8.shape[-1], device=x8.device).view(1, -1) == m
|
| 490 |
+
sgn = torch.where(o & pos, 1 - sgn, sgn)
|
| 491 |
+
return sgn, x8.abs()
|
| 492 |
+
|
| 493 |
+
|
| 494 |
+
def _ksigns_encode(sgn: torch.Tensor) -> torch.Tensor:
|
| 495 |
+
"""sgn [.., 8] int64 bits -> ksigns table index [..] int64."""
|
| 496 |
+
sb = (sgn * (1 << torch.arange(8, device=sgn.device))).sum(-1) # [..] 0..255
|
| 497 |
+
ks = iq_table("ksigns_iq2xs").to(sgn.device) # [128] uint8
|
| 498 |
+
lut = torch.full((256,), -1, dtype=torch.int64, device=sgn.device)
|
| 499 |
+
lut[ks.to(torch.int64)] = torch.arange(128, device=sgn.device)
|
| 500 |
+
return lut[sb.to(torch.int64)]
|
| 501 |
+
|
| 502 |
+
|
| 503 |
+
def _pack_u16(v: torch.Tensor) -> torch.Tensor:
|
| 504 |
+
"""int64 [.., N] -> [.., N, 2] LE bytes."""
|
| 505 |
+
return torch.stack([v & 0xFF, (v >> 8) & 0xFF], dim=-1).to(torch.uint8)
|
| 506 |
+
|
| 507 |
+
|
| 508 |
+
def _pack_f16(d: torch.Tensor) -> torch.Tensor:
|
| 509 |
+
"""fp32 [N] -> [N, 2] LE bytes."""
|
| 510 |
+
return _pack_u16(d.to(torch.float16).view(torch.int16).to(torch.int64) & 0xFFFF)
|
| 511 |
+
|
| 512 |
+
|
| 513 |
+
def quantize_iq2_xxs(W: torch.Tensor) -> torch.Tensor:
|
| 514 |
+
"""[out, in] fp32 -> IQ2_XXS bytes [out, 66*nblk] (d + 32 u16).
|
| 515 |
+
|
| 516 |
+
Block 256 = 8 super-groups x 32; each super-group = 4 8-groups.
|
| 517 |
+
Per super-group: 4 u16 = [idx0|idx1, idx2|idx3, aux_lo, aux_hi];
|
| 518 |
+
aux32 = (ls << 28) | (sgn_idx[0] << 0) | (sgn_idx[1] << 7)
|
| 519 |
+
| (sgn_idx[2] << 14) | (sgn_idx[3] << 21).
|
| 520 |
+
"""
|
| 521 |
+
out_f, in_f = W.shape
|
| 522 |
+
nblk = (in_f + QK_K - 1) // QK_K
|
| 523 |
+
Wp = torch.zeros(out_f, nblk * QK_K, dtype=torch.float32, device=W.device)
|
| 524 |
+
Wp[:, :in_f] = W
|
| 525 |
+
x32 = Wp.view(out_f, nblk, QK_K)
|
| 526 |
+
sgn, xa = _pick_signs8(x32.view(-1, 8))
|
| 527 |
+
sgn = sgn.view(out_f, nblk, 8, 4, 8) # [out, nblk, sg, 4x8, 8]
|
| 528 |
+
xa = xa.view(out_f, nblk, 8, 4, 8)
|
| 529 |
+
grid = iq_table("iq2xxs_grid").to(W.device) # [256, 8]
|
| 530 |
+
idx, scale = _find_nearest(xa.reshape(-1, 8), grid) # [out*nblk*32]
|
| 531 |
+
idx = idx.view(out_f, nblk, 8, 4)
|
| 532 |
+
scale = scale.view(out_f, nblk, 8, 4)
|
| 533 |
+
sc_g = scale.amax(dim=-1).clamp(min=1e-8) # [out, nblk, 8] per super-group
|
| 534 |
+
# llama.cpp: scale is "unit" scale (for q=1); dequant gives y = (scale/8)*grid.
|
| 535 |
+
# Our per-8-group scale is x/grid -> unit scale = 8*scale.
|
| 536 |
+
d = 8.0 * sc_g.amax(dim=-1) / 31.0 # [out, nblk] per block
|
| 537 |
+
ls = torch.clamp(torch.round(0.5 * (8.0 * scale / d.unsqueeze(-1).unsqueeze(-1) - 1)), 0, 15).to(torch.int64)
|
| 538 |
+
sgn_idx = _ksigns_encode(sgn) # [out, nblk, 8, 4] 0..127
|
| 539 |
+
aux32 = (ls << 28) | (sgn_idx << (7 * torch.arange(4, device=W.device)).view(1, 1, 1, 4))
|
| 540 |
+
aux32 = aux32[..., 0] | aux32[..., 1] | aux32[..., 2] | aux32[..., 3] # [out, nblk, 8]
|
| 541 |
+
qb = idx.to(torch.int64) & 0xFF
|
| 542 |
+
q2 = (qb[..., 0] | (qb[..., 1] << 8)) & 0xFFFF # [out, nblk, 8]
|
| 543 |
+
q2h = (qb[..., 2] | (qb[..., 3] << 8)) & 0xFFFF
|
| 544 |
+
aux_lo = aux32 & 0xFFFF
|
| 545 |
+
aux_hi = (aux32 >> 16) & 0xFFFF
|
| 546 |
+
u16 = torch.stack([q2, q2h, aux_lo, aux_hi], dim=-1) # [out, nblk, 8, 4]
|
| 547 |
+
return _cat_blocks([_pack_f16(d),
|
| 548 |
+
_pack_u16(u16).reshape(out_f, nblk, 64)],
|
| 549 |
+
out_f, nblk, 66)
|
| 550 |
+
|
| 551 |
+
|
| 552 |
+
def quantize_iq2_xs(W: torch.Tensor) -> torch.Tensor:
|
| 553 |
+
"""[out, in] fp32 -> IQ2_XS bytes [out, 74*nblk] (d + 32 u16 + scales[8]).
|
| 554 |
+
|
| 555 |
+
Each 32-group: 4 u16, each = 9-bit grid index | (7-bit sign index << 9).
|
| 556 |
+
scales[ib] = 2 4-bit codes (lo = sub-group<2, hi = sub-group>=2) of the
|
| 557 |
+
unit scale / 8.
|
| 558 |
+
"""
|
| 559 |
+
out_f, in_f = W.shape
|
| 560 |
+
nblk = (in_f + QK_K - 1) // QK_K
|
| 561 |
+
Wp = torch.zeros(out_f, nblk * QK_K, dtype=torch.float32, device=W.device)
|
| 562 |
+
Wp[:, :in_f] = W
|
| 563 |
+
x32 = Wp.view(out_f, nblk, QK_K)
|
| 564 |
+
sgn, xa = _pick_signs8(x32.view(-1, 8))
|
| 565 |
+
sgn = sgn.view(out_f, nblk, 8, 4, 8)
|
| 566 |
+
xa = xa.view(out_f, nblk, 8, 4, 8)
|
| 567 |
+
grid = iq_table("iq2xs_grid").to(W.device) # [512, 8]
|
| 568 |
+
idx, scale = _find_nearest(xa.reshape(-1, 8), grid)
|
| 569 |
+
idx = idx.view(out_f, nblk, 8, 4)
|
| 570 |
+
scale = scale.view(out_f, nblk, 8, 4)
|
| 571 |
+
unit = 8.0 * scale # [out, nblk, 8, 4]
|
| 572 |
+
pair = unit.view(out_f, nblk, 8, 2, 2).amax(-1) # [out, nblk, 8, 2]
|
| 573 |
+
d = pair.amax(dim=(-1, -2)) / 31.0 # [out, nblk] per block
|
| 574 |
+
d4 = d.unsqueeze(-1).unsqueeze(-1) # [out, nblk, 1, 1]
|
| 575 |
+
code = torch.clamp(torch.round(0.5 * (pair / d4 - 1)), 0, 15).to(torch.int64)
|
| 576 |
+
scales = code[..., 0] | (code[..., 1] << 4) # [out, nblk, 8]
|
| 577 |
+
sgn_idx = _ksigns_encode(sgn) # [out, nblk, 8, 4] 0..127
|
| 578 |
+
q2 = (idx.to(torch.int64) & 0x1FF) | (sgn_idx << 9) # [out, nblk, 8, 4]
|
| 579 |
+
return _cat_blocks([_pack_f16(d),
|
| 580 |
+
_pack_u16(q2.reshape(out_f, nblk, 32)).reshape(out_f, nblk, 64),
|
| 581 |
+
scales.to(torch.uint8)],
|
| 582 |
+
out_f, nblk, 74)
|
| 583 |
+
|
| 584 |
+
|
| 585 |
+
def quantize_iq2_s(W: torch.Tensor) -> torch.Tensor:
|
| 586 |
+
"""[out, in] fp32 -> IQ2_S bytes [out, 82*nblk].
|
| 587 |
+
|
| 588 |
+
qs[4*ib+il] = low byte of grid index, qh[ib] = 2 high bits per sub-group,
|
| 589 |
+
signs byte at qs[32+4*ib+il], scales[8] = 4-bit pair.
|
| 590 |
+
"""
|
| 591 |
+
out_f, in_f = W.shape
|
| 592 |
+
nblk = (in_f + QK_K - 1) // QK_K
|
| 593 |
+
Wp = torch.zeros(out_f, nblk * QK_K, dtype=torch.float32, device=W.device)
|
| 594 |
+
Wp[:, :in_f] = W
|
| 595 |
+
x32 = Wp.view(out_f, nblk, QK_K)
|
| 596 |
+
sgn, xa = _pick_signs8(x32.view(-1, 8))
|
| 597 |
+
sgn = sgn.view(out_f, nblk, 8, 4, 8)
|
| 598 |
+
xa = xa.view(out_f, nblk, 8, 4, 8)
|
| 599 |
+
grid = iq_table("iq2s_grid").to(W.device) # [1024, 8]
|
| 600 |
+
idx, scale = _find_nearest(xa.reshape(-1, 8), grid)
|
| 601 |
+
idx = idx.view(out_f, nblk, 8, 4)
|
| 602 |
+
scale = scale.view(out_f, nblk, 8, 4)
|
| 603 |
+
unit = 8.0 * scale
|
| 604 |
+
unit_p = unit.view(out_f, nblk, 8, 2, 2).amax(-1) # [out, nblk, 8, 2]
|
| 605 |
+
d = unit_p.amax(dim=(-1, -2)) / 31.0
|
| 606 |
+
d4 = d.unsqueeze(-1).unsqueeze(-1)
|
| 607 |
+
code = torch.clamp(torch.round(0.5 * (unit_p / d4 - 1)), 0, 15).to(torch.int64)
|
| 608 |
+
scales = code[..., 0] | (code[..., 1] << 4)
|
| 609 |
+
# signs: plain 8-bit byte (NOT ksigns!) at qs[32+4*ib+il]
|
| 610 |
+
signs_b = (sgn * (1 << torch.arange(8, device=W.device))).sum(-1).to(torch.uint8)
|
| 611 |
+
qs_low = (idx.to(torch.int64) & 0xFF).to(torch.uint8)
|
| 612 |
+
qs_hi = signs_b
|
| 613 |
+
qh = ((idx.to(torch.int64) >> 8) << (2 * torch.arange(4, device=W.device)).view(1, 1, 1, 4)).sum(-1) & 0xFF
|
| 614 |
+
return _cat_blocks([_pack_f16(d), qs_low.reshape(out_f, nblk, 32),
|
| 615 |
+
qs_hi.reshape(out_f, nblk, 32), qh,
|
| 616 |
+
scales.to(torch.uint8)],
|
| 617 |
+
out_f, nblk, 82)
|
| 618 |
+
|
| 619 |
+
|
| 620 |
+
def quantize_iq3_xxs(W: torch.Tensor) -> torch.Tensor:
|
| 621 |
+
"""[out, in] fp32 -> IQ3_XXS bytes [out, 98*nblk].
|
| 622 |
+
|
| 623 |
+
qs[0:64] = 64 grid-index bytes (8 groups x 8), qs[64:96] = scales+signs
|
| 624 |
+
(8 groups x 4 u16 = aux32 per group). grid = iq3xxs_grid [256, 4].
|
| 625 |
+
"""
|
| 626 |
+
out_f, in_f = W.shape
|
| 627 |
+
nblk = (in_f + QK_K - 1) // QK_K
|
| 628 |
+
Wp = torch.zeros(out_f, nblk * QK_K, dtype=torch.float32, device=W.device)
|
| 629 |
+
Wp[:, :in_f] = W
|
| 630 |
+
x32 = Wp.view(out_f, nblk, QK_K)
|
| 631 |
+
sgn, xa = _pick_signs8(x32.view(-1, 8))
|
| 632 |
+
sgn = sgn.view(out_f, nblk, 8, 4, 8)
|
| 633 |
+
xa = xa.view(out_f, nblk, 8, 4, 8)
|
| 634 |
+
grid = iq_table("iq3xxs_grid").to(W.device) # [256, 4]
|
| 635 |
+
# Each 8-group = 2 interleaved 4-halves (lo at idx[2*il], hi at idx[2*il+1]).
|
| 636 |
+
xa_4 = xa.view(out_f, nblk, 8, 4, 2, 4) # [.,.,8,4,2 halves,4]
|
| 637 |
+
xa_lo = xa_4[..., 0, :].reshape(-1, 4)
|
| 638 |
+
xa_hi = xa_4[..., 1, :].reshape(-1, 4)
|
| 639 |
+
idx_lo, s_lo = _find_nearest(xa_lo, grid)
|
| 640 |
+
idx_hi, s_hi = _find_nearest(xa_hi, grid)
|
| 641 |
+
scale = torch.maximum(s_lo, s_hi).view(out_f, nblk, 8, 4)
|
| 642 |
+
idx = (idx_lo.view(out_f, nblk, 8, 4).to(torch.int64)
|
| 643 |
+
| (idx_hi.view(out_f, nblk, 8, 4).to(torch.int64) << 8)) # 2 x 8-bit per sub-group
|
| 644 |
+
# per-32-group scale (4 sub-groups): max over the 4
|
| 645 |
+
sg = scale.amax(dim=-1).clamp(min=1e-8) # [out, nblk, 8]
|
| 646 |
+
d = sg.amax(dim=-1) / 15.0 # [out, nblk]
|
| 647 |
+
d4 = d.unsqueeze(-1).unsqueeze(-1)
|
| 648 |
+
# db = d*(0.5+(aux>>28))*0.5 -> aux = 4*scale/d - 1... solve: aux = round(scale/d/0.5 - 0.5)
|
| 649 |
+
ls = torch.clamp(torch.round(2.0 * scale / d4 - 0.5), 0, 15).to(torch.int64)
|
| 650 |
+
sgn_idx = _ksigns_encode(sgn) # [out, nblk, 8, 4] 0..127
|
| 651 |
+
aux32 = (ls << 28) | (sgn_idx << (7 * torch.arange(4, device=W.device)).view(1, 1, 1, 4))
|
| 652 |
+
aux32 = aux32[..., 0] | aux32[..., 1] | aux32[..., 2] | aux32[..., 3] # [out, nblk, 8]
|
| 653 |
+
# Per sub-group: 2 grid codes of 4 -> 2 bytes, interleaved (lo, hi).
|
| 654 |
+
# Layout of qs[0:64] per 8-group: [lo_il0, hi_il0, lo_il1, hi_il1, ...].
|
| 655 |
+
lo_b = (idx & 0xFF).view(out_f, nblk, 8, 4) # [.,.,8,4]
|
| 656 |
+
hi_b = ((idx >> 8) & 0xFF).view(out_f, nblk, 8, 4)
|
| 657 |
+
qs_idx = torch.stack([lo_b, hi_b], dim=-1).reshape(out_f, nblk, 64).to(torch.uint8)
|
| 658 |
+
aux_pairs = torch.stack([aux32 & 0xFFFF, aux32 >> 16], dim=-1) # [out, nblk, 8, 2]
|
| 659 |
+
return _cat_blocks([_pack_f16(d), qs_idx,
|
| 660 |
+
_pack_u16(aux_pairs.reshape(out_f, nblk, 16)).reshape(out_f, nblk, 32)],
|
| 661 |
+
out_f, nblk, 98)
|
| 662 |
+
|
| 663 |
+
|
| 664 |
+
def quantize_iq3_s(W: torch.Tensor) -> torch.Tensor:
|
| 665 |
+
"""[out, in] fp32 -> IQ3_S bytes [out, 110*nblk].
|
| 666 |
+
|
| 667 |
+
qs[0:64] grid-index bytes (lo/hi interleaved), qh[8] high bits,
|
| 668 |
+
signs[32], scales[4] (nibble per group of a pair).
|
| 669 |
+
"""
|
| 670 |
+
out_f, in_f = W.shape
|
| 671 |
+
nblk = (in_f + QK_K - 1) // QK_K
|
| 672 |
+
Wp = torch.zeros(out_f, nblk * QK_K, dtype=torch.float32, device=W.device)
|
| 673 |
+
Wp[:, :in_f] = W
|
| 674 |
+
x32 = Wp.view(out_f, nblk, QK_K)
|
| 675 |
+
sgn, xa = _pick_signs8(x32.view(-1, 8))
|
| 676 |
+
sgn = sgn.view(out_f, nblk, 8, 4, 8)
|
| 677 |
+
xa = xa.view(out_f, nblk, 8, 4, 8)
|
| 678 |
+
grid = iq_table("iq3s_grid").to(W.device) # [512, 4]
|
| 679 |
+
xa_4 = xa.view(out_f, nblk, 8, 4, 2, 4)
|
| 680 |
+
idx_lo, s_lo = _find_nearest(xa_4[..., 0, :].reshape(-1, 4), grid)
|
| 681 |
+
idx_hi, s_hi = _find_nearest(xa_4[..., 1, :].reshape(-1, 4), grid)
|
| 682 |
+
idx_lo = idx_lo.view(out_f, nblk, 8, 4).to(torch.int64)
|
| 683 |
+
idx_hi = idx_hi.view(out_f, nblk, 8, 4).to(torch.int64)
|
| 684 |
+
scale = torch.stack([s_lo, s_hi], -1).amax(-1).view(out_f, nblk, 8, 4)
|
| 685 |
+
# db per group (8 groups; scales[ib/2] nibble by ib%2, code = (unit/d-1)/2)
|
| 686 |
+
scale_g = scale.amax(dim=-1) # [out, nblk, 8]
|
| 687 |
+
sg_pairs = scale_g.view(out_f, nblk, 4, 2) # [out, nblk, 4, 2] (even, odd)
|
| 688 |
+
d = scale_g.amax(dim=-1) / 15.0 # [out, nblk]
|
| 689 |
+
d4 = d.unsqueeze(-1).unsqueeze(-1) # [out, nblk, 1, 1]
|
| 690 |
+
code_pair = torch.clamp(torch.round((sg_pairs / d4 - 1.0) * 0.5), 0, 15).to(torch.int64)
|
| 691 |
+
scales = (code_pair[..., 0] | (code_pair[..., 1] << 4)).to(torch.uint8) # [out, nblk, 4]
|
| 692 |
+
# qs bytes: interleave lo/hi codes per sub-group; qh carries high bits.
|
| 693 |
+
lo_code = (idx_lo & 0xFF).to(torch.uint8)
|
| 694 |
+
hi_code = (idx_hi & 0xFF).to(torch.uint8)
|
| 695 |
+
qs_idx = torch.stack([lo_code, hi_code], dim=-1).reshape(out_f, nblk, 64)
|
| 696 |
+
il2 = torch.arange(4, device=W.device)
|
| 697 |
+
qh_lo = (idx_lo >> 8) & 1
|
| 698 |
+
qh_hi = (idx_hi >> 8) & 1
|
| 699 |
+
qh = ((qh_lo << (2 * il2).view(1, 1, 1, 4))
|
| 700 |
+
| (qh_hi << (2 * il2 + 1).view(1, 1, 1, 4))).sum(-1) & 0xFF # [out, nblk, 8]
|
| 701 |
+
qh = qh.to(torch.uint8)
|
| 702 |
+
signs_b = (sgn * (1 << torch.arange(8, device=W.device))).sum(-1).to(torch.uint8) # [out,nblk,8,4]
|
| 703 |
+
return _cat_blocks([_pack_f16(d), qs_idx, qh,
|
| 704 |
+
signs_b.reshape(out_f, nblk, 32), scales],
|
| 705 |
+
out_f, nblk, 110)
|
| 706 |
+
|
| 707 |
+
|
| 708 |
+
def quantize_iq1_s(W: torch.Tensor) -> torch.Tensor:
|
| 709 |
+
"""[out, in] fp32 -> IQ1_S bytes [out, 66*nblk].
|
| 710 |
+
|
| 711 |
+
d(f16) + qs[32] + qh[16]u16. grid = iq1s_grid [2048, 8] (signed, {-1,0,1}).
|
| 712 |
+
Per 32-group ib: dl = d*(2*((qh[ib]>>12)&7)+1); delta = +-IQ1S_DELTA
|
| 713 |
+
(sign from qh[ib]&0x8000); idx = qs[4*ib+il] | (((qh[ib]>>3*il)&7)<<8);
|
| 714 |
+
y[j] = dl * (grid[idx][j] + delta).
|
| 715 |
+
"""
|
| 716 |
+
out_f, in_f = W.shape
|
| 717 |
+
nblk = (in_f + QK_K - 1) // QK_K
|
| 718 |
+
Wp = torch.zeros(out_f, nblk * QK_K, dtype=torch.float32, device=W.device)
|
| 719 |
+
Wp[:, :in_f] = W
|
| 720 |
+
x32 = Wp.view(out_f, nblk, QK_K)
|
| 721 |
+
grid = iq_table("iq1s_grid").to(W.device) # [2048, 8] int8 signed
|
| 722 |
+
x8 = x32.view(out_f * nblk * 32, 8)
|
| 723 |
+
# Search signed grid: dequant y = dl*(g+delta); for a candidate delta
|
| 724 |
+
# the effective target is x/dl - delta. Do a 2-pass: first find the best
|
| 725 |
+
# (idx, scale=dl) on the signed grid, then decide delta by the residual
|
| 726 |
+
# sign and re-pick if needed. Approx is fine at 1.5 bpw.
|
| 727 |
+
idx, scale = _find_nearest(x8, grid, signed=True)
|
| 728 |
+
idx = idx.view(out_f, nblk, 8, 4)
|
| 729 |
+
scale = scale.view(out_f, nblk, 8, 4) # dl per 8-group
|
| 730 |
+
# group multiplier: dl = d*(2*m+1), m 0..7
|
| 731 |
+
g_max = scale.amax(dim=-1).clamp(min=1e-8) # [out, nblk, 8]
|
| 732 |
+
m = torch.clamp(torch.round((g_max / g_max.amax(dim=-1).unsqueeze(-1) * 7 - 1) * 0.5), 0, 7).to(torch.int64)
|
| 733 |
+
d = (g_max.amax(dim=-1) / (2 * 7 + 1)).clamp(min=1e-8) # [out, nblk]
|
| 734 |
+
dl = d.unsqueeze(-1) * (2 * m + 1) # [out, nblk, 8] per super-group
|
| 735 |
+
id_dl = 1.0 / dl.unsqueeze(-1).unsqueeze(-1)
|
| 736 |
+
gv = grid[idx].to(torch.float32) # [out, nblk, 8, 4, 8]
|
| 737 |
+
rec = (dl.unsqueeze(-1).unsqueeze(-1) * gv) # [out, nblk, 8, 4, 8]
|
| 738 |
+
res = x32.view(out_f, nblk, 8, 4, 8) - rec
|
| 739 |
+
# delta: one sign per 32-group (aggregate residual over all 32 values)
|
| 740 |
+
delta = torch.where(res.mean(dim=(-1, -2)) < 0, -IQ1S_DELTA, IQ1S_DELTA) # [out, nblk, 8]
|
| 741 |
+
# adjust idx for the delta (approximate: re-search on x/dl - delta)
|
| 742 |
+
tgt = x32.view(out_f, nblk, 8, 4, 8) * id_dl - delta.unsqueeze(-1).unsqueeze(-1)
|
| 743 |
+
idx2, _ = _find_nearest(tgt.reshape(-1, 8), grid, signed=True)
|
| 744 |
+
idx = idx2.view(out_f, nblk, 8, 4)
|
| 745 |
+
# encode: qh[ib] = (sign delta << 15) | (m << 12) | (3 high bits of idx per il)
|
| 746 |
+
qh = (delta < 0).to(torch.int64) << 15 # [out, nblk, 8]
|
| 747 |
+
qh = qh | (m << 12) # [out, nblk, 8]
|
| 748 |
+
qs = (idx.to(torch.int64) & 0xFF) # low 8 bits per (ib, il)
|
| 749 |
+
hi3 = (idx.to(torch.int64) >> 8) & 7
|
| 750 |
+
qh = qh | (hi3 << (3 * torch.arange(4, device=W.device)).view(1, 1, 1, 4)).sum(-1)
|
| 751 |
+
# qh storage: 16 u16 per block (2 per group); decoder reads first of each pair.
|
| 752 |
+
qh16 = torch.zeros(out_f, nblk, 16, dtype=torch.int64, device=W.device)
|
| 753 |
+
qh16[..., 0::2] = qh
|
| 754 |
+
return _cat_blocks([_pack_f16(d), qs.to(torch.uint8).reshape(out_f, nblk, 32),
|
| 755 |
+
_pack_u16(qh16).reshape(out_f, nblk, 32)],
|
| 756 |
+
out_f, nblk, 66)
|
| 757 |
+
|
| 758 |
+
|
| 759 |
+
def quantize_iq4_xs(W: torch.Tensor) -> torch.Tensor:
|
| 760 |
+
"""[out, in] fp32 -> IQ4_XS bytes [out, 136*nblk].
|
| 761 |
+
|
| 762 |
+
d(f16) + scales_h[2] + scales_l[4] + qs[128].
|
| 763 |
+
"""
|
| 764 |
+
out_f, in_f = W.shape
|
| 765 |
+
nblk = (in_f + QK_K - 1) // QK_K
|
| 766 |
+
Wp = torch.zeros(out_f, nblk * QK_K, dtype=torch.float32, device=W.device)
|
| 767 |
+
Wp[:, :in_f] = W
|
| 768 |
+
bl = Wp.view(out_f, nblk, 8, 32)
|
| 769 |
+
amax = bl.abs().amax(dim=-1).clamp(min=1e-8) # [out, nblk, 8]
|
| 770 |
+
d = amax.amax(dim=-1) / 31.0 # [out, nblk]
|
| 771 |
+
kv = iq_table("kvalues_iq4nl").to(W.device) # [16]
|
| 772 |
+
id_d = 1.0 / d.unsqueeze(-1).unsqueeze(-1)
|
| 773 |
+
al = bl * id_d # [out, nblk, 8, 32]
|
| 774 |
+
codes = (al.unsqueeze(-1) - kv.view(1, 1, 1, 1, 16)).abs().argmin(-1) # [out,nblk,8,32]
|
| 775 |
+
# scales: 6-bit per group = (scales_l nibble) | (scales_h 2-bit << 4), -32 offset
|
| 776 |
+
ls = torch.clamp(torch.round(amax / d.unsqueeze(-1)), 0, 63).to(torch.int64) # [out,nblk,8]
|
| 777 |
+
ls = ls - 32
|
| 778 |
+
scales_l = (ls & 0xF).view(out_f, nblk, 4, 2)
|
| 779 |
+
scales_l = (scales_l[..., 0] | (scales_l[..., 1] << 4)).view(out_f, nblk, 4)
|
| 780 |
+
hi2 = (ls >> 4) & 3 # [out,nblk,8] 2-bit each
|
| 781 |
+
h0 = hi2[..., 0] | (hi2[..., 1] << 2) | (hi2[..., 2] << 4) | (hi2[..., 3] << 6)
|
| 782 |
+
h1 = hi2[..., 4] | (hi2[..., 5] << 2) | (hi2[..., 6] << 4) | (hi2[..., 7] << 6)
|
| 783 |
+
scales_h = torch.stack([h0, h1], dim=-1).view(out_f, nblk, 2).to(torch.int64)
|
| 784 |
+
packed = (codes[..., 0::2].to(torch.int64) | (codes[..., 1::2].to(torch.int64) << 4)).to(torch.uint8) # [out,nblk,8,16]
|
| 785 |
+
return _cat_blocks([_pack_f16(d), scales_h.to(torch.uint8),
|
| 786 |
+
scales_l.to(torch.uint8),
|
| 787 |
+
packed.reshape(out_f, nblk, 128)],
|
| 788 |
+
out_f, nblk, 136)
|
| 789 |
+
|
| 790 |
+
|
| 791 |
+
def quantize_iq4_nl(W: torch.Tensor) -> torch.Tensor:
|
| 792 |
+
"""[out, in] fp32 -> IQ4_NL bytes [out, 18*nblk32].
|
| 793 |
+
|
| 794 |
+
Block = 32 values: d(f16) + qs[16]. y[j] = d*kvalues[qs&0xF],
|
| 795 |
+
y[j+16] = d*kvalues[qs>>4]. kvalues = [-127,-104,-83,-65,-49,-35,-22,
|
| 796 |
+
-10,1,13,25,38,53,69,89,113].
|
| 797 |
+
"""
|
| 798 |
+
out_f, in_f = W.shape
|
| 799 |
+
nblk = (in_f + QK4_NL - 1) // QK4_NL
|
| 800 |
+
Wp = torch.zeros(out_f, nblk * QK4_NL, dtype=torch.float32, device=W.device)
|
| 801 |
+
Wp[:, :in_f] = W
|
| 802 |
+
bl = Wp.view(out_f, nblk, QK4_NL) # [out, nblk, 32]
|
| 803 |
+
kv = iq_table("kvalues_iq4nl").to(W.device) # [16] fp32
|
| 804 |
+
amax = bl.abs().amax(dim=-1).clamp(min=1e-8) # [out, nblk]
|
| 805 |
+
d = amax / kv.abs().max().clamp(min=1e-8) # [out, nblk]
|
| 806 |
+
id_d = 1.0 / d.unsqueeze(-1)
|
| 807 |
+
al = bl * id_d # [out, nblk, 32]
|
| 808 |
+
codes = (al.unsqueeze(-1) - kv.view(1, 1, 1, 16)).abs().argmin(-1) # [out,nblk,32]
|
| 809 |
+
packed = (codes[..., 0::2].to(torch.int64) | (codes[..., 1::2].to(torch.int64) << 4)).to(torch.uint8) # [out,nblk,16]
|
| 810 |
+
return _cat_blocks([_pack_f16(d), packed], out_f, nblk, 18)
|
| 811 |
+
|
| 812 |
+
|
| 813 |
+
def quantize_iq1_m(W: torch.Tensor) -> torch.Tensor:
|
| 814 |
+
"""[out, in] fp32 -> IQ1_M bytes [out, 56*nblk].
|
| 815 |
+
|
| 816 |
+
qs[32] + qh[16] + scales[8 u16]. No fp16 d: scale is reassembled from the
|
| 817 |
+
top nibbles of the 8 scale u16 (see dequant_iq1_m_packed).
|
| 818 |
+
"""
|
| 819 |
+
out_f, in_f = W.shape
|
| 820 |
+
nblk = (in_f + QK_K - 1) // QK_K
|
| 821 |
+
Wp = torch.zeros(out_f, nblk * QK_K, dtype=torch.float32, device=W.device)
|
| 822 |
+
Wp[:, :in_f] = W
|
| 823 |
+
x32 = Wp.view(out_f, nblk, QK_K)
|
| 824 |
+
grid = iq_table("iq1s_grid").to(W.device) # [2048, 8] signed {-1,0,1}
|
| 825 |
+
x8 = x32.view(out_f, nblk, 8, 4, 8)
|
| 826 |
+
# per 8-group: find best (idx, scale) on the signed grid
|
| 827 |
+
idx, scale = _find_nearest(x8.reshape(-1, 8), grid, signed=True)
|
| 828 |
+
idx = idx.view(out_f, nblk, 8, 4)
|
| 829 |
+
scale = scale.view(out_f, nblk, 8, 4)
|
| 830 |
+
# group dl (per 4 sub-groups of a 32-group... actually per 32-group pair):
|
| 831 |
+
# dl1 = d*(2*sc3+1), dl2 = d*(2*sc3+3) -> sc3 = 3-bit scale per 16-group.
|
| 832 |
+
# Approx: per 32-group, dl = max scale over its 4 sub-groups.
|
| 833 |
+
g_max = scale.amax(dim=-1).clamp(min=1e-8) # [out, nblk, 8] per 32-group
|
| 834 |
+
# per 32-group 2 codes: dl1 = max(scale of sub-groups 0,1), dl2 = max(2,3)
|
| 835 |
+
pair_scale = scale.view(out_f, nblk, 8, 2, 2).amax(dim=-1) # [out, nblk, 8, 2]
|
| 836 |
+
dmax = pair_scale.amax(dim=-1).amax(dim=-1).clamp(min=1e-8) # [out, nblk]
|
| 837 |
+
code2 = torch.clamp(torch.round((pair_scale / dmax.unsqueeze(-1).unsqueeze(-1) * 7 - 1) * 0.5), 0, 7).to(torch.int64) # [out,nblk,8,2]
|
| 838 |
+
dl = dmax.unsqueeze(-1).unsqueeze(-1) * (2 * code2 + 1) # [out, nblk, 8, 2]
|
| 839 |
+
# sc[p] u16 (p=0..3): 2 codes per 32-group, groups 2p (even, bits 0-5) and
|
| 840 |
+
# 2p+1 (odd, bits 6-11): [dl1, dl2] of each group.
|
| 841 |
+
c = code2.view(out_f, nblk, 4, 2, 2) # [out,nblk,4 u16,2 groups,2 dl]
|
| 842 |
+
gA1, gA2 = c[..., 0, 0], c[..., 0, 1]
|
| 843 |
+
gB1, gB2 = c[..., 1, 0], c[..., 1, 1]
|
| 844 |
+
sc16 = gA1 | (gA2 << 3) | (gB1 << 6) | (gB2 << 9) # [out, nblk, 4]
|
| 845 |
+
# fp16 d in the top nibbles across the 4 u16:
|
| 846 |
+
# d16 = (sc0>>12) | ((sc1>>8)&0xF0) | ((sc2>>4)&0xF00) | (sc3&0xF000)
|
| 847 |
+
d16 = dmax.to(torch.float16).view(torch.int16).to(torch.int64) & 0xFFFF # [out,nblk]
|
| 848 |
+
d16e = d16.unsqueeze(-1) # [out, nblk, 1]
|
| 849 |
+
sc16 = sc16 | (((d16e >> 12) & 0xF) << 12)
|
| 850 |
+
sc16 = sc16 | ((((d16e >> 8) & 0xF) << 8) & 0x0F00)
|
| 851 |
+
sc16 = sc16 | ((((d16e >> 4) & 0xF) << 4) & 0x00F0)
|
| 852 |
+
sc16 = sc16 | (d16e & 0x000F)
|
| 853 |
+
# delta per group (from qh 0x08 / 0x80 bits) + idx high bits in qh
|
| 854 |
+
delta = torch.where(g_max / dmax.unsqueeze(-1) < 0.5, -IQ1M_DELTA, IQ1M_DELTA) # placeholder
|
| 855 |
+
delta = torch.full_like(g_max, IQ1M_DELTA) # sign from residual below
|
| 856 |
+
# delta: reconstruct with dl per (group, pair): dl1 for il 0..1, dl2 for 2..3
|
| 857 |
+
dl_pair = dl.view(out_f, nblk, 8, 2, 1) # [out,nblk,8,2 dl,1]
|
| 858 |
+
dl_full = dl_pair.expand(-1, -1, -1, -1, 2).reshape(out_f, nblk, 8, 4) # per sub-group
|
| 859 |
+
gv = grid[idx].to(torch.float32) # [out,nblk,8,4,8]
|
| 860 |
+
res = x8 - dl_full.unsqueeze(-1) * gv
|
| 861 |
+
delta = torch.where(res.mean(dim=(-1, -2)) < 0, -IQ1M_DELTA, IQ1M_DELTA) # [out,nblk,8]
|
| 862 |
+
# qh: 16 bytes per block (2 per 32-group). Per 32-group:
|
| 863 |
+
# qh[0] bits: idx0 hi3<<8, idx1 hi3<<4, delta0<<3, delta1<<7 (via 0x08/0x80)
|
| 864 |
+
# qh[1] bits: idx2 hi3<<8, idx3 hi3<<4, delta2<<3, delta3<<7
|
| 865 |
+
hi3 = (idx.to(torch.int64) >> 8) & 7 # [out,nblk,8,4]
|
| 866 |
+
qh2 = torch.zeros(out_f, nblk, 8, 2, dtype=torch.int64, device=W.device)
|
| 867 |
+
qh2[..., 0] |= (hi3[..., 0] << 8) | (hi3[..., 1] << 4)
|
| 868 |
+
qh2[..., 1] |= (hi3[..., 2] << 8) | (hi3[..., 3] << 4)
|
| 869 |
+
dlt = (delta < 0).to(torch.int64) # [out,nblk,8]
|
| 870 |
+
qh2[..., 0] |= (dlt << 3) & 0x08
|
| 871 |
+
qh2[..., 0] |= (dlt << 7) & 0x80
|
| 872 |
+
qh2[..., 1] |= (dlt << 3) & 0x08
|
| 873 |
+
qh2[..., 1] |= (dlt << 7) & 0x80
|
| 874 |
+
qs = (idx.to(torch.int64) & 0xFF)
|
| 875 |
+
return _cat_blocks([qs.to(torch.uint8).reshape(out_f, nblk, 32),
|
| 876 |
+
qh2.to(torch.uint8).reshape(out_f, nblk, 16),
|
| 877 |
+
_pack_u16(sc16).reshape(out_f, nblk, 8)],
|
| 878 |
+
out_f, nblk, 56)
|