HPC-Quantize-V2 / reference.py
CompressedGemma's picture
Upload 14 files
3324725 verified
Raw History Blame Contribute Delete
4.91 kB
"""Faithful Python port of the original C factored solver.
This module is intentionally not on the production path. It exists so the
V2 improvements can be measured against the exact reference trajectory:
fixed six-iteration 1-D Lloyd, single range initialisation, no fp16-domain
polish and no spectral shaping inside the solver.
"""
from __future__ import annotations
import numpy as np
from .config import HPCConfig
from .fp16 import from_fp16_bits, nearest_int, to_fp16_bits
from .q2k import QK_K, codes_from_params, dequantize_params, weighted_sse
from .spectral import objective, spectral_penalty
def reference_factor_1d(v, w, nmax: int = 15):
v = np.maximum(np.asarray(v, dtype=np.float64), 0.0)
w = np.maximum(np.asarray(w, dtype=np.float64), 0.0)
vmax = float(np.max(v))
if vmax < 1.0e-30:
return 0.0, np.zeros(v.size, dtype=np.uint8)
d = vmax / nmax
for _ in range(6):
codes = np.clip(nearest_int(v / d), 0, nmax)
den = float(np.sum(w * codes * codes))
if den < 1.0e-30:
break
dn = float(np.sum(w * v * codes) / den)
conv = abs(dn - d) <= 1.0e-7 * abs(d) + 1.0e-30
d = dn
if conv:
break
codes = np.clip(nearest_int(v / d), 0, nmax)
return d, codes.astype(np.uint8)
def reference_sub_ls(x, w, codes):
NS = 16
q = np.asarray(codes, dtype=np.float64).reshape(NS, 16)
xb = np.asarray(x, dtype=np.float64).reshape(NS, 16)
wb = np.asarray(w, dtype=np.float64).reshape(NS, 16)
s = np.zeros(NS)
t = np.zeros(NS)
for j in range(NS):
ww, qq, xx = wb[j], q[j], xb[j]
Sqq = float(np.sum(ww * qq * qq))
Swq = float(np.sum(ww * qq))
Sw = float(np.sum(ww))
Sqx = float(np.sum(ww * qq * xx))
Sx = float(np.sum(ww * xx))
det = Sqq * Sw - Swq * Swq
if det > 1.0e-30:
sj = (Sqx * Sw - Swq * Sx) / det
tj = (sj * Swq - Sx) / Sw
else:
sj = tj = 0.0
if sj < 0.0:
sj = 0.0
if tj < 0.0:
tj = 0.0
sj = Sqx / Sqq if Sqq > 1.0e-30 else 0.0
if sj < 0.0:
sj = 0.0
s[j], t[j] = sj, tj
return s, t
def reference_solve(x, w, cfg: HPCConfig, use_spectral: bool) -> tuple[float, float]:
NS = 16
xb = np.asarray(x, dtype=np.float64).reshape(NS, 16)
wb = np.asarray(w, dtype=np.float64).reshape(NS, 16)
s = np.zeros(NS)
t = np.zeros(NS)
codes = np.zeros(QK_K, dtype=np.uint8)
for j in range(NS):
sub = xb[j]
xmin = min(float(sub.min()), 0.0)
xmax = float(sub.max())
span = xmax - xmin
sj = span / 3.0 if span > 1.0e-30 else 0.0
tj = -xmin
s[j], t[j] = sj, tj
if sj > 1.0e-30:
codes[16 * j : 16 * (j + 1)] = np.clip(
nearest_int((sub + tj) / sj), 0, 3
).astype(np.uint8)
sw = wb.sum(axis=1)
d, ls = reference_factor_1d(s, sw)
m, lm = reference_factor_1d(t, sw)
L = codes_from_params(x, d, m, ls, lm)
obj, _ = objective(x, w, d, m, ls, lm, L, cfg)
best = (obj, d, m, ls.copy(), lm.copy(), L.copy())
stall = 0
for _ in range(16):
L = codes_from_params(x, d, m, ls, lm)
s, t = reference_sub_ls(x, w, L)
ws = np.sum(wb * L.astype(np.float64).reshape(NS, 16) ** 2, axis=1)
wt = wb.sum(axis=1)
d, ls = reference_factor_1d(s, ws)
m, lm = reference_factor_1d(t, wt)
L = codes_from_params(x, d, m, ls, lm)
cur, _ = objective(x, w, d, m, ls, lm, L, cfg)
if cur < best[0]:
best = (cur, d, m, ls.copy(), lm.copy(), L.copy())
stall = 0
else:
stall += 1
if stall >= 2:
break
_, d, m, ls, lm, L = best
d = float(from_fp16_bits(to_fp16_bits(np.float32(d))))
m = float(from_fp16_bits(to_fp16_bits(np.float32(m))))
L = codes_from_params(x, d, m, ls, lm)
e = np.asarray(x, dtype=np.float64) - dequantize_params(d, m, ls, lm, L).astype(np.float64)
sse = weighted_sse(x, w, d, m, ls, lm, L)
obj = sse + (spectral_penalty(e, cfg) if use_spectral else 0.0)
return obj, sse
def synthetic_cases(seed: int = 123, per_kind: int = 12):
rng = np.random.default_rng(seed)
cases = []
for _ in range(per_kind):
cases.append(("gaussian", rng.normal(0, 0.02, QK_K).astype(np.float32)))
for _ in range(per_kind):
cases.append(("student-t", (rng.standard_t(3, QK_K) * 0.01).astype(np.float32)))
for _ in range(per_kind):
mask = rng.random(QK_K) < 0.1
cases.append(("sparse", (rng.normal(0, 0.02, QK_K) * mask).astype(np.float32)))
for _ in range(per_kind):
mask = rng.random(QK_K) < 0.5
cases.append(("offset-sparse", (rng.normal(0, 0.02, QK_K) * mask + 0.01).astype(np.float32)))
return cases