Super small quants
Browse files- hexstate_requantize.py +50 -23
hexstate_requantize.py
CHANGED
|
@@ -643,6 +643,26 @@ def is_attention_tensor(name):
|
|
| 643 |
return False
|
| 644 |
|
| 645 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 646 |
def q2k_row_compatible(n_dims, dims):
|
| 647 |
return n_dims >= 2 and bool(dims) and int(dims[0]) % QK_K == 0
|
| 648 |
|
|
@@ -683,12 +703,11 @@ def should_quantize(name, n_dims, dims, tied_embeddings=False):
|
|
| 683 |
- ffn_gate_inp — MoE routing gate
|
| 684 |
- layer_output_scale — per-layer scaling factor (scalar)
|
| 685 |
- altup, laurel — small Gemma-specific tensors
|
| 686 |
-
|
| 687 |
-
|
| 688 |
-
|
| 689 |
-
|
| 690 |
-
|
| 691 |
-
it at source precision instead.
|
| 692 |
"""
|
| 693 |
n_elements = 1
|
| 694 |
for d in dims:
|
|
@@ -705,20 +724,25 @@ def should_quantize(name, n_dims, dims, tied_embeddings=False):
|
|
| 705 |
return False
|
| 706 |
if 'layer_output_scale' in name:
|
| 707 |
return False
|
| 708 |
-
# Embedding table
|
| 709 |
-
#
|
| 710 |
-
|
| 711 |
-
|
| 712 |
-
#
|
| 713 |
-
#
|
| 714 |
-
|
| 715 |
-
|
| 716 |
-
#
|
| 717 |
-
if 'ssm_a' in name or 'A_log' in name:
|
| 718 |
return False
|
| 719 |
if 'ssm_dt' in name or 'dt_bias' in name:
|
| 720 |
return False
|
| 721 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 722 |
return False
|
| 723 |
# When embeddings are tied, token_embd.weight doubles as the output
|
| 724 |
# projection (LM head). It gets routed to Q4_0 in the quant plan
|
|
@@ -881,8 +905,11 @@ def main():
|
|
| 881 |
else:
|
| 882 |
will_quant = False; total_keep += 1
|
| 883 |
print(f" Q2ALL→KEEP (row width not block-aligned): {ti['name']}")
|
| 884 |
-
elif
|
| 885 |
-
|
|
|
|
|
|
|
|
|
|
| 886 |
elif q2k_row_compatible(ti['n_dims'], ti['dims']):
|
| 887 |
will_quant = True; total_quant += 1
|
| 888 |
elif q4_row_compatible(ti['n_dims'], ti['dims']):
|
|
@@ -920,12 +947,12 @@ def main():
|
|
| 920 |
n_blocks = ti['n_elements'] // 32
|
| 921 |
out_size = n_blocks * 34
|
| 922 |
print(f" [EMBD→Q8_0·Sieve] {ti['name']} ({ti['n_elements']:,} elements)")
|
| 923 |
-
elif quant_plan[i] == '
|
| 924 |
# Attention tensor → Q4_0 HPC (4.5 bpw)
|
| 925 |
out_type = GGML_TYPE_Q4_0
|
| 926 |
n_blocks = (ti['n_elements'] + 31) // 32
|
| 927 |
out_size = n_blocks * 18
|
| 928 |
-
print(f" [
|
| 929 |
elif quant_plan[i] == 'Q4_0':
|
| 930 |
out_type = GGML_TYPE_Q4_0
|
| 931 |
n_blocks = ti['n_elements'] // 32
|
|
@@ -1151,7 +1178,7 @@ def main():
|
|
| 1151 |
quant_count += 1
|
| 1152 |
total_quant_bytes += n_blocks_q8 * 34
|
| 1153 |
|
| 1154 |
-
elif quant_plan[i] in ('Q4_0', '
|
| 1155 |
# ── Q4_0 quantization (fallback or attention HPC) ──
|
| 1156 |
if ti['type'] == GGML_TYPE_BF16:
|
| 1157 |
f32 = bf16_to_f32(raw_data, ti['n_elements'])
|
|
@@ -1171,7 +1198,7 @@ def main():
|
|
| 1171 |
n_blocks_q4 = n_el // 32
|
| 1172 |
|
| 1173 |
# Use HPC for attention tensors if available
|
| 1174 |
-
if quant_plan[i] == '
|
| 1175 |
output_buf = np.zeros(n_blocks_q4 * 18, dtype=np.uint8)
|
| 1176 |
error = ctypes.c_float(0.0)
|
| 1177 |
f32_c = np.ascontiguousarray(f32, dtype=np.float32)
|
|
|
|
| 643 |
return False
|
| 644 |
|
| 645 |
|
| 646 |
+
def is_q4_tensor(name):
|
| 647 |
+
"""Tensors demoted to Q4_0 (HPC sieve) while Q2_K tensors stay Q2_K.
|
| 648 |
+
|
| 649 |
+
The full-precision leftovers: LM head / embedding tables plus
|
| 650 |
+
SSM-side weights (ssm_alpha mirrors ssm_beta, which already ships
|
| 651 |
+
Q2_K; ssm_conv1d kernels are small dense 2D projections).
|
| 652 |
+
Attention/FFN are NOT matched here — they keep their Q2_K plan.
|
| 653 |
+
NOTE: 'output.weight' is exact-matched so 'attn_output.weight'
|
| 654 |
+
never matches.
|
| 655 |
+
"""
|
| 656 |
+
if name == 'output.weight' or name.endswith('.output.weight'):
|
| 657 |
+
return True
|
| 658 |
+
if name == 'token_embd.weight' or '.token_embd.weight' in name:
|
| 659 |
+
return True
|
| 660 |
+
for pat in ('ssm_alpha.weight', 'ssm_conv1d.weight', 'conv1d.weight'):
|
| 661 |
+
if pat in name:
|
| 662 |
+
return True
|
| 663 |
+
return False
|
| 664 |
+
|
| 665 |
+
|
| 666 |
def q2k_row_compatible(n_dims, dims):
|
| 667 |
return n_dims >= 2 and bool(dims) and int(dims[0]) % QK_K == 0
|
| 668 |
|
|
|
|
| 703 |
- ffn_gate_inp — MoE routing gate
|
| 704 |
- layer_output_scale — per-layer scaling factor (scalar)
|
| 705 |
- altup, laurel — small Gemma-specific tensors
|
| 706 |
+
- token_embd.weight / output.weight — demoted to Q4_0 in main()
|
| 707 |
+
(is_q4_tensor). When embeddings are TIED, main() routes
|
| 708 |
+
token_embd.weight to Q8_0 (HPC sieve pipeline) instead: the same
|
| 709 |
+
tensor serves as both embedding lookup AND LM head. --keep-embd
|
| 710 |
+
keeps it at source precision instead.
|
|
|
|
| 711 |
"""
|
| 712 |
n_elements = 1
|
| 713 |
for d in dims:
|
|
|
|
| 724 |
return False
|
| 725 |
if 'layer_output_scale' in name:
|
| 726 |
return False
|
| 727 |
+
# Embedding table / LM head — demoted to Q4_0 in main()
|
| 728 |
+
# (is_q4_tensor), NOT kept at source precision. (When embeddings are
|
| 729 |
+
# TIED, main() routes token_embd.weight to Q8_0 instead — that branch
|
| 730 |
+
# runs before should_quantize is consulted.)
|
| 731 |
+
# DeltaNet state-space parameters — keep truly scalar/vector ones
|
| 732 |
+
# at full precision. NOTE: 'ssm_a' must match the standalone 1D tensor
|
| 733 |
+
# only ('blk.N.ssm_a'), NOT 'ssm_alpha' (contains 'ssm_a' as a
|
| 734 |
+
# substring). ssm_alpha is a regular 2D projection weight and is
|
| 735 |
+
# quantizable — it mirrors ssm_beta, which already ships Q2_K.
|
| 736 |
+
if 'ssm_a' in name.split('.') or 'A_log' in name:
|
| 737 |
return False
|
| 738 |
if 'ssm_dt' in name or 'dt_bias' in name:
|
| 739 |
return False
|
| 740 |
+
# ssm_conv1d kernels are quantizable ONLY when their row width is a
|
| 741 |
+
# multiple of the quant block size: GGUF quant blocks tile along dim0,
|
| 742 |
+
# so e.g. dims [4, 10240] can never be Q4_0/Q8_0/Q2_K regardless of
|
| 743 |
+
# total-element divisibility (llama.cpp rejects such files at load).
|
| 744 |
+
if ('ssm_conv1d' in name or 'conv1d.weight' in name) \
|
| 745 |
+
and (not dims or dims[0] % 32 != 0):
|
| 746 |
return False
|
| 747 |
# When embeddings are tied, token_embd.weight doubles as the output
|
| 748 |
# projection (LM head). It gets routed to Q4_0 in the quant plan
|
|
|
|
| 905 |
else:
|
| 906 |
will_quant = False; total_keep += 1
|
| 907 |
print(f" Q2ALL→KEEP (row width not block-aligned): {ti['name']}")
|
| 908 |
+
elif is_q4_tensor(ti['name']) and q4_row_compatible(ti['n_dims'], ti['dims']):
|
| 909 |
+
# Q4_0 path (HPC sieve). dim0 — not just total n — must be
|
| 910 |
+
# block-aligned: GGUF quants tile along rows and llama.cpp
|
| 911 |
+
# refuses to load otherwise.
|
| 912 |
+
will_quant = 'Q4_HPC'; total_attn += 1
|
| 913 |
elif q2k_row_compatible(ti['n_dims'], ti['dims']):
|
| 914 |
will_quant = True; total_quant += 1
|
| 915 |
elif q4_row_compatible(ti['n_dims'], ti['dims']):
|
|
|
|
| 947 |
n_blocks = ti['n_elements'] // 32
|
| 948 |
out_size = n_blocks * 34
|
| 949 |
print(f" [EMBD→Q8_0·Sieve] {ti['name']} ({ti['n_elements']:,} elements)")
|
| 950 |
+
elif quant_plan[i] == 'Q4_HPC':
|
| 951 |
# Attention tensor → Q4_0 HPC (4.5 bpw)
|
| 952 |
out_type = GGML_TYPE_Q4_0
|
| 953 |
n_blocks = (ti['n_elements'] + 31) // 32
|
| 954 |
out_size = n_blocks * 18
|
| 955 |
+
print(f" [Q4_0·HPC] {ti['name']} ({ti['n_elements']} elements)")
|
| 956 |
elif quant_plan[i] == 'Q4_0':
|
| 957 |
out_type = GGML_TYPE_Q4_0
|
| 958 |
n_blocks = ti['n_elements'] // 32
|
|
|
|
| 1178 |
quant_count += 1
|
| 1179 |
total_quant_bytes += n_blocks_q8 * 34
|
| 1180 |
|
| 1181 |
+
elif quant_plan[i] in ('Q4_0', 'Q4_HPC'):
|
| 1182 |
# ── Q4_0 quantization (fallback or attention HPC) ──
|
| 1183 |
if ti['type'] == GGML_TYPE_BF16:
|
| 1184 |
f32 = bf16_to_f32(raw_data, ti['n_elements'])
|
|
|
|
| 1198 |
n_blocks_q4 = n_el // 32
|
| 1199 |
|
| 1200 |
# Use HPC for attention tensors if available
|
| 1201 |
+
if quant_plan[i] == 'Q4_HPC' and use_hpc and hasattr(_HEXSTATE_LIB, 'hexstate_quantize_tensor_q4_0_hpc'):
|
| 1202 |
output_buf = np.zeros(n_blocks_q4 * 18, dtype=np.uint8)
|
| 1203 |
error = ctypes.c_float(0.0)
|
| 1204 |
f32_c = np.ascontiguousarray(f32, dtype=np.float32)
|