Upload 14 files
Browse files- gguf.py +15 -0
- reference.py +138 -0
gguf.py
CHANGED
|
@@ -376,6 +376,8 @@ def write_quantized_gguf(
|
|
| 376 |
tensor_names: set[str] | None = None,
|
| 377 |
limit: int = 0,
|
| 378 |
verbose: bool = True,
|
|
|
|
|
|
|
| 379 |
) -> dict[str, float]:
|
| 380 |
"""Quantize a source GGUF with the V2 engine and emit a valid GGUF.
|
| 381 |
|
|
@@ -461,6 +463,8 @@ def write_quantized_gguf(
|
|
| 461 |
fout.write(b"\x00" * (data_start - fout.tell()))
|
| 462 |
|
| 463 |
for idx, (ti, out_type, out_size, offset) in enumerate(out_infos):
|
|
|
|
|
|
|
| 464 |
target = data_start + offset
|
| 465 |
if target > fout.tell():
|
| 466 |
fout.write(b"\x00" * (target - fout.tell()))
|
|
@@ -484,10 +488,21 @@ def write_quantized_gguf(
|
|
| 484 |
f" Q2_K {ti.name:48s} row={row:6d} "
|
| 485 |
f"rmse={res.stats.rmse:.6e} imatrix={'yes' if imp is not None else 'no'}"
|
| 486 |
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 487 |
else:
|
| 488 |
reader.copy_tensor_raw_to(ti.name, fout)
|
| 489 |
if verbose and idx % 25 == 0:
|
| 490 |
print(f" copy {ti.name}")
|
|
|
|
|
|
|
| 491 |
|
| 492 |
rmse = float(np.sqrt(total_rmse / total_elements)) if total_elements else 0.0
|
| 493 |
return {
|
|
|
|
| 376 |
tensor_names: set[str] | None = None,
|
| 377 |
limit: int = 0,
|
| 378 |
verbose: bool = True,
|
| 379 |
+
progress_callback=None,
|
| 380 |
+
stop_event=None,
|
| 381 |
) -> dict[str, float]:
|
| 382 |
"""Quantize a source GGUF with the V2 engine and emit a valid GGUF.
|
| 383 |
|
|
|
|
| 463 |
fout.write(b"\x00" * (data_start - fout.tell()))
|
| 464 |
|
| 465 |
for idx, (ti, out_type, out_size, offset) in enumerate(out_infos):
|
| 466 |
+
if stop_event is not None and stop_event.is_set():
|
| 467 |
+
raise InterruptedError("Quantization cancelled by user")
|
| 468 |
target = data_start + offset
|
| 469 |
if target > fout.tell():
|
| 470 |
fout.write(b"\x00" * (target - fout.tell()))
|
|
|
|
| 488 |
f" Q2_K {ti.name:48s} row={row:6d} "
|
| 489 |
f"rmse={res.stats.rmse:.6e} imatrix={'yes' if imp is not None else 'no'}"
|
| 490 |
)
|
| 491 |
+
if progress_callback is not None:
|
| 492 |
+
progress_callback(
|
| 493 |
+
idx + 1,
|
| 494 |
+
len(out_infos),
|
| 495 |
+
ti.name,
|
| 496 |
+
"Q2_K",
|
| 497 |
+
float(res.stats.rmse),
|
| 498 |
+
imp is not None,
|
| 499 |
+
)
|
| 500 |
else:
|
| 501 |
reader.copy_tensor_raw_to(ti.name, fout)
|
| 502 |
if verbose and idx % 25 == 0:
|
| 503 |
print(f" copy {ti.name}")
|
| 504 |
+
if progress_callback is not None:
|
| 505 |
+
progress_callback(idx + 1, len(out_infos), ti.name, "copy", None, False)
|
| 506 |
|
| 507 |
rmse = float(np.sqrt(total_rmse / total_elements)) if total_elements else 0.0
|
| 508 |
return {
|
reference.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Faithful Python port of the original C factored solver.
|
| 2 |
+
|
| 3 |
+
This module is intentionally not on the production path. It exists so the
|
| 4 |
+
V2 improvements can be measured against the exact reference trajectory:
|
| 5 |
+
fixed six-iteration 1-D Lloyd, single range initialisation, no fp16-domain
|
| 6 |
+
polish and no spectral shaping inside the solver.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from __future__ import annotations
|
| 10 |
+
|
| 11 |
+
import numpy as np
|
| 12 |
+
|
| 13 |
+
from .config import HPCConfig
|
| 14 |
+
from .fp16 import from_fp16_bits, nearest_int, to_fp16_bits
|
| 15 |
+
from .q2k import QK_K, codes_from_params, dequantize_params, weighted_sse
|
| 16 |
+
from .spectral import objective, spectral_penalty
|
| 17 |
+
|
| 18 |
+
|
| 19 |
+
def reference_factor_1d(v, w, nmax: int = 15):
|
| 20 |
+
v = np.maximum(np.asarray(v, dtype=np.float64), 0.0)
|
| 21 |
+
w = np.maximum(np.asarray(w, dtype=np.float64), 0.0)
|
| 22 |
+
vmax = float(np.max(v))
|
| 23 |
+
if vmax < 1.0e-30:
|
| 24 |
+
return 0.0, np.zeros(v.size, dtype=np.uint8)
|
| 25 |
+
d = vmax / nmax
|
| 26 |
+
for _ in range(6):
|
| 27 |
+
codes = np.clip(nearest_int(v / d), 0, nmax)
|
| 28 |
+
den = float(np.sum(w * codes * codes))
|
| 29 |
+
if den < 1.0e-30:
|
| 30 |
+
break
|
| 31 |
+
dn = float(np.sum(w * v * codes) / den)
|
| 32 |
+
conv = abs(dn - d) <= 1.0e-7 * abs(d) + 1.0e-30
|
| 33 |
+
d = dn
|
| 34 |
+
if conv:
|
| 35 |
+
break
|
| 36 |
+
codes = np.clip(nearest_int(v / d), 0, nmax)
|
| 37 |
+
return d, codes.astype(np.uint8)
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def reference_sub_ls(x, w, codes):
|
| 41 |
+
NS = 16
|
| 42 |
+
q = np.asarray(codes, dtype=np.float64).reshape(NS, 16)
|
| 43 |
+
xb = np.asarray(x, dtype=np.float64).reshape(NS, 16)
|
| 44 |
+
wb = np.asarray(w, dtype=np.float64).reshape(NS, 16)
|
| 45 |
+
s = np.zeros(NS)
|
| 46 |
+
t = np.zeros(NS)
|
| 47 |
+
for j in range(NS):
|
| 48 |
+
ww, qq, xx = wb[j], q[j], xb[j]
|
| 49 |
+
Sqq = float(np.sum(ww * qq * qq))
|
| 50 |
+
Swq = float(np.sum(ww * qq))
|
| 51 |
+
Sw = float(np.sum(ww))
|
| 52 |
+
Sqx = float(np.sum(ww * qq * xx))
|
| 53 |
+
Sx = float(np.sum(ww * xx))
|
| 54 |
+
det = Sqq * Sw - Swq * Swq
|
| 55 |
+
if det > 1.0e-30:
|
| 56 |
+
sj = (Sqx * Sw - Swq * Sx) / det
|
| 57 |
+
tj = (sj * Swq - Sx) / Sw
|
| 58 |
+
else:
|
| 59 |
+
sj = tj = 0.0
|
| 60 |
+
if sj < 0.0:
|
| 61 |
+
sj = 0.0
|
| 62 |
+
if tj < 0.0:
|
| 63 |
+
tj = 0.0
|
| 64 |
+
sj = Sqx / Sqq if Sqq > 1.0e-30 else 0.0
|
| 65 |
+
if sj < 0.0:
|
| 66 |
+
sj = 0.0
|
| 67 |
+
s[j], t[j] = sj, tj
|
| 68 |
+
return s, t
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def reference_solve(x, w, cfg: HPCConfig, use_spectral: bool) -> tuple[float, float]:
|
| 72 |
+
NS = 16
|
| 73 |
+
xb = np.asarray(x, dtype=np.float64).reshape(NS, 16)
|
| 74 |
+
wb = np.asarray(w, dtype=np.float64).reshape(NS, 16)
|
| 75 |
+
s = np.zeros(NS)
|
| 76 |
+
t = np.zeros(NS)
|
| 77 |
+
codes = np.zeros(QK_K, dtype=np.uint8)
|
| 78 |
+
for j in range(NS):
|
| 79 |
+
sub = xb[j]
|
| 80 |
+
xmin = min(float(sub.min()), 0.0)
|
| 81 |
+
xmax = float(sub.max())
|
| 82 |
+
span = xmax - xmin
|
| 83 |
+
sj = span / 3.0 if span > 1.0e-30 else 0.0
|
| 84 |
+
tj = -xmin
|
| 85 |
+
s[j], t[j] = sj, tj
|
| 86 |
+
if sj > 1.0e-30:
|
| 87 |
+
codes[16 * j : 16 * (j + 1)] = np.clip(
|
| 88 |
+
nearest_int((sub + tj) / sj), 0, 3
|
| 89 |
+
).astype(np.uint8)
|
| 90 |
+
|
| 91 |
+
sw = wb.sum(axis=1)
|
| 92 |
+
d, ls = reference_factor_1d(s, sw)
|
| 93 |
+
m, lm = reference_factor_1d(t, sw)
|
| 94 |
+
L = codes_from_params(x, d, m, ls, lm)
|
| 95 |
+
obj, _ = objective(x, w, d, m, ls, lm, L, cfg)
|
| 96 |
+
best = (obj, d, m, ls.copy(), lm.copy(), L.copy())
|
| 97 |
+
stall = 0
|
| 98 |
+
for _ in range(16):
|
| 99 |
+
L = codes_from_params(x, d, m, ls, lm)
|
| 100 |
+
s, t = reference_sub_ls(x, w, L)
|
| 101 |
+
ws = np.sum(wb * L.astype(np.float64).reshape(NS, 16) ** 2, axis=1)
|
| 102 |
+
wt = wb.sum(axis=1)
|
| 103 |
+
d, ls = reference_factor_1d(s, ws)
|
| 104 |
+
m, lm = reference_factor_1d(t, wt)
|
| 105 |
+
L = codes_from_params(x, d, m, ls, lm)
|
| 106 |
+
cur, _ = objective(x, w, d, m, ls, lm, L, cfg)
|
| 107 |
+
if cur < best[0]:
|
| 108 |
+
best = (cur, d, m, ls.copy(), lm.copy(), L.copy())
|
| 109 |
+
stall = 0
|
| 110 |
+
else:
|
| 111 |
+
stall += 1
|
| 112 |
+
if stall >= 2:
|
| 113 |
+
break
|
| 114 |
+
|
| 115 |
+
_, d, m, ls, lm, L = best
|
| 116 |
+
d = float(from_fp16_bits(to_fp16_bits(np.float32(d))))
|
| 117 |
+
m = float(from_fp16_bits(to_fp16_bits(np.float32(m))))
|
| 118 |
+
L = codes_from_params(x, d, m, ls, lm)
|
| 119 |
+
e = np.asarray(x, dtype=np.float64) - dequantize_params(d, m, ls, lm, L).astype(np.float64)
|
| 120 |
+
sse = weighted_sse(x, w, d, m, ls, lm, L)
|
| 121 |
+
obj = sse + (spectral_penalty(e, cfg) if use_spectral else 0.0)
|
| 122 |
+
return obj, sse
|
| 123 |
+
|
| 124 |
+
|
| 125 |
+
def synthetic_cases(seed: int = 123, per_kind: int = 12):
|
| 126 |
+
rng = np.random.default_rng(seed)
|
| 127 |
+
cases = []
|
| 128 |
+
for _ in range(per_kind):
|
| 129 |
+
cases.append(("gaussian", rng.normal(0, 0.02, QK_K).astype(np.float32)))
|
| 130 |
+
for _ in range(per_kind):
|
| 131 |
+
cases.append(("student-t", (rng.standard_t(3, QK_K) * 0.01).astype(np.float32)))
|
| 132 |
+
for _ in range(per_kind):
|
| 133 |
+
mask = rng.random(QK_K) < 0.1
|
| 134 |
+
cases.append(("sparse", (rng.normal(0, 0.02, QK_K) * mask).astype(np.float32)))
|
| 135 |
+
for _ in range(per_kind):
|
| 136 |
+
mask = rng.random(QK_K) < 0.5
|
| 137 |
+
cases.append(("offset-sparse", (rng.normal(0, 0.02, QK_K) * mask + 0.01).astype(np.float32)))
|
| 138 |
+
return cases
|