gguf-py : add DT3
Registers the type id, file type and block size, and implements numpy dequantization (verified bit-exact against the C implementation with gguf-py/tests/test_quants.py, including random byte payloads). Quantization is intentionally left unimplemented, like the K-quants: DT3 planes come from an external solver (PTQTP) and are packed directly, so a from-float numpy path would only invite quantizing models with the wrong algorithm.
This commit is contained in:
@@ -5019,6 +5019,7 @@ class GGMLQuantizationType(IntEnum):
|
||||
NVFP4 = 40
|
||||
Q1_0 = 41
|
||||
Q2_0 = 42
|
||||
DT3 = 43
|
||||
|
||||
|
||||
class ExpertGatingFuncType(IntEnum):
|
||||
@@ -5075,6 +5076,7 @@ class LlamaFileType(IntEnum):
|
||||
MOSTLY_NVFP4 = 39 # except 1d tensors
|
||||
MOSTLY_Q1_0 = 40 # except 1d tensors
|
||||
MOSTLY_Q2_0 = 41 # except 1d tensors
|
||||
MOSTLY_DT3 = 42 # except 1d tensors
|
||||
|
||||
GUESSED = 1024 # not specified in the model file
|
||||
|
||||
@@ -5206,6 +5208,7 @@ GGML_QUANT_SIZES: dict[GGMLQuantizationType, tuple[int, int]] = {
|
||||
GGMLQuantizationType.NVFP4: (64, 4 + 32),
|
||||
GGMLQuantizationType.Q1_0: (128, 2 + 16),
|
||||
GGMLQuantizationType.Q2_0: (64, 2 + 16),
|
||||
GGMLQuantizationType.DT3: (128, 2 * (24 + 2 + 2)),
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -654,6 +654,42 @@ class TQ2_0(__Quant, qtype=GGMLQuantizationType.TQ2_0):
|
||||
return (d * qs.astype(np.float32))
|
||||
|
||||
|
||||
class DT3(__Quant, qtype=GGMLQuantizationType.DT3):
|
||||
# Dual-plane ternary: w = d1 * t1 + d2 * t2 with t in {-1, 0, 1}. Each of
|
||||
# the two planes is packed like TQ1_0 with all constants halved (blocks of
|
||||
# 128 elements): 24 bytes of qs (passes of 16 and 8 bytes) and 2 bytes of
|
||||
# qh per plane, then the two fp16 scales.
|
||||
# Quantization is intentionally not implemented here: DT3 planes are
|
||||
# produced by an external solver (PTQTP) and packed directly.
|
||||
@classmethod
|
||||
def dequantize_blocks(cls, blocks: np.ndarray) -> np.ndarray:
|
||||
n_blocks = blocks.shape[0]
|
||||
|
||||
qs, rest = np.hsplit(blocks, [2 * 24])
|
||||
qh, d = np.hsplit(rest, [2 * 2])
|
||||
|
||||
d = d.view(np.float16).astype(np.float32).reshape((n_blocks, 2, 1))
|
||||
|
||||
planes = []
|
||||
for p in range(2):
|
||||
pqs = qs[..., p * 24:(p + 1) * 24]
|
||||
pqh = qh[..., p * 2:(p + 1) * 2]
|
||||
|
||||
qs0, qs1 = pqs[..., :16], pqs[..., 16:]
|
||||
qs0 = qs0.reshape((n_blocks, -1, 1, 16)) * np.array([1, 3, 9, 27, 81], dtype=np.uint8).reshape((1, 1, 5, 1))
|
||||
qs0 = qs0.reshape((n_blocks, -1))
|
||||
qs1 = qs1.reshape((n_blocks, -1, 1, 8)) * np.array([1, 3, 9, 27, 81], dtype=np.uint8).reshape((1, 1, 5, 1))
|
||||
qs1 = qs1.reshape((n_blocks, -1))
|
||||
# only 4 trits per qh byte, the 5th base-3 digit is packer padding
|
||||
pqh = pqh.reshape((n_blocks, -1, 1, 2)) * np.array([1, 3, 9, 27], dtype=np.uint8).reshape((1, 1, 4, 1))
|
||||
pqh = pqh.reshape((n_blocks, -1))
|
||||
pq = np.concatenate([qs0, qs1, pqh], axis=-1)
|
||||
pq = ((pq.astype(np.uint16) * 3) >> 8).astype(np.int8) - np.int8(1)
|
||||
planes.append(pq.astype(np.float32))
|
||||
|
||||
return d[:, 0] * planes[0] + d[:, 1] * planes[1]
|
||||
|
||||
|
||||
class MXFP4(__Quant, qtype=GGMLQuantizationType.MXFP4):
|
||||
# e2m1 values (doubled)
|
||||
# ref: https://www.opencompute.org/documents/ocp-microscaling-formats-mx-v1-0-spec-final-pdf
|
||||
|
||||
Reference in New Issue
Block a user