CompressedGemma commited on
Commit
3324725
·
verified ·
1 Parent(s): 56eff0f

Upload 14 files

Browse files
Files changed (2) hide show
  1. gguf.py +15 -0
  2. 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