CompressedGemma commited on
Commit
cec4bd4
·
verified ·
1 Parent(s): 22f2fee
Files changed (8) hide show
  1. __main__.py +9 -0
  2. anneal.py +556 -0
  3. config.py +18 -0
  4. gui.py +9 -0
  5. pipeline.py +72 -1
  6. q1_0.py +23 -0
  7. q3_k.py +21 -0
  8. q4_0.py +24 -3
__main__.py CHANGED
@@ -47,6 +47,8 @@ def _config_from_args(args) -> HPCConfig:
47
  cfg.optimizer = "mse"
48
  if args.no_spectral:
49
  cfg.use_spectral = False
 
 
50
  if args.no_q4_fallback:
51
  cfg.enable_q4_fallback = False
52
  if args.keep_embd:
@@ -277,6 +279,13 @@ def main() -> None:
277
  parser.add_argument("--device", default=None, help="compute device: auto (default), cuda, or cpu")
278
  parser.add_argument("--mse", action="store_true", help="disable HPC sequence search")
279
  parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
 
 
 
 
 
 
 
280
  parser.add_argument(
281
  "--format",
282
  choices=("mixed", "mixed_q1", "custom", "q1_0", "q3_k", "q4_0", "q2_k"),
 
47
  cfg.optimizer = "mse"
48
  if args.no_spectral:
49
  cfg.use_spectral = False
50
+ if args.thermal is not None:
51
+ cfg.therm_passes = int(args.thermal)
52
  if args.no_q4_fallback:
53
  cfg.enable_q4_fallback = False
54
  if args.keep_embd:
 
279
  parser.add_argument("--device", default=None, help="compute device: auto (default), cuda, or cpu")
280
  parser.add_argument("--mse", action="store_true", help="disable HPC sequence search")
281
  parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
282
+ parser.add_argument(
283
+ "--thermal",
284
+ type=int,
285
+ default=None,
286
+ metavar="PASSES",
287
+ help="thermal refinery heat-bath passes after Phase 4 (default 0 = off; seeded, bit-exact per seed)",
288
+ )
289
  parser.add_argument(
290
  "--format",
291
  choices=("mixed", "mixed_q1", "custom", "q1_0", "q3_k", "q4_0", "q2_k"),
anneal.py ADDED
@@ -0,0 +1,556 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Thermal refinery: seeded pair heat-bath annealing on the Q2_K lattice.
2
+
3
+ With ``fold_depth == 1`` the extended objective for one block is exactly::
4
+
5
+ E = sum_i w_i e_i^2
6
+ + (vw_lambda / n) * sum_p (e_p + e_{p+n/2})^2
7
+ + (dc_lambda / n) * (sum_i e_i + carry)^2
8
+
9
+ so the 256 weights decompose into n/2 vesica pairs that interact with each
10
+ other only through the scalar DC residual: with every error affine in its
11
+ code, each pair's conditional energy over its joint states is exactly
12
+ quadratic and closed-form. ``gibbs_pair_pass`` resamples the pairs from
13
+ those conditionals at a temperature (heat bath), fully batched across blocks
14
+ over any uniform lattice size (``levels``: 4 = Q2_K, 16 = Q4_0, 2 = Q1_0,
15
+ 8 = Q3_K K-quants). ``greedy=True`` resamples at the conditional argmin
16
+ instead, which is a monotone descent. ``thermal_refine_codes`` /
17
+ ``thermal_refine_uniform_codes`` schedule temperatures, then keep the
18
+ elementwise best against the pre-anneal state, so the pass can never regress
19
+ a block. All randomness comes from a seeded generator, and a fixed seed
20
+ reproduces bit-identical quantizations.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ import numpy as np
26
+
27
+ from .config import HPCConfig
28
+ from .fp16 import nearest_int
29
+ from .q2k import QK_K
30
+
31
+ EPS_D = 1.0e-15
32
+
33
+
34
+ def pair_energy(
35
+ e: np.ndarray,
36
+ w: np.ndarray | None,
37
+ lam_vw: float,
38
+ lam_dc: float,
39
+ carry: np.ndarray,
40
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]:
41
+ """Block energies plus the (sse, v, dc) live state for exact deltas."""
42
+ half = e.shape[1] // 2
43
+ ww = np.ones_like(e) if w is None else np.asarray(w, dtype=np.float64)
44
+ we_sq = ww * e * e
45
+ sse = np.sum(we_sq, axis=1)
46
+ v = e[:, :half] + e[:, half:]
47
+ dc = np.sum(e, axis=1) + np.asarray(carry, dtype=np.float64)
48
+ energy = sse + lam_vw * np.sum(v * v, axis=1) + lam_dc * dc * dc
49
+ return energy, we_sq, v, dc
50
+
51
+
52
+ def gibbs_pair_pass(
53
+ X_f64: np.ndarray,
54
+ w_full: np.ndarray | None,
55
+ d_sub: np.ndarray,
56
+ m_sub: np.ndarray,
57
+ codes: np.ndarray,
58
+ e: np.ndarray,
59
+ v: np.ndarray,
60
+ dc: np.ndarray,
61
+ sse: np.ndarray,
62
+ temperature: np.ndarray,
63
+ lam_vw: float,
64
+ lam_dc: float,
65
+ rng: np.random.Generator,
66
+ greedy: bool = False,
67
+ levels: int = 4,
68
+ ) -> None:
69
+ """One exact sequential Gibbs sweep over all vesica pairs, in place.
70
+
71
+ Updates (codes, e, v, dc, sse) for the batch of blocks. With
72
+ ``greedy=False`` each pair is resampled from its exact conditional
73
+ energy via a heat bath at ``temperature`` (per-block vector); with
74
+ ``greedy=True`` the conditional argmin is taken and only strict
75
+ energy decreases are accepted. ``levels`` selects the code lattice
76
+ size (4 = Q2_K 2-bit, 16 = Q4_0 nibble, 2 = Q1_0 bit, 8 = Q3_K tri);
77
+ ``d_sub`` holds the per-column error step so that any code move ``q``
78
+ produces the exact error delta ``d_sub * (q_cur - q)`` (formula is
79
+ shared across formats; Q1_0's sign trick is ``d_sub = 2*d``), and
80
+ ``m_sub`` is accepted for signature symmetry but unused here.
81
+ """
82
+ n_blocks, n = X_f64.shape
83
+ half = n // 2
84
+ q_grid = np.arange(levels, dtype=np.int64)
85
+ w_full = np.ones_like(e) if w_full is None else np.asarray(w_full, dtype=np.float64)
86
+ w_lo, w_hi = w_full[:, :half], w_full[:, half:]
87
+ ds_lo, ds_hi = d_sub[:, :half], d_sub[:, half:]
88
+ active_lo, active_hi = np.abs(ds_lo) >= EPS_D, np.abs(ds_hi) >= EPS_D
89
+ idx = np.arange(n_blocks)
90
+
91
+ for p in range(half):
92
+ e_lo = e[:, p].copy()
93
+ e_hi = e[:, half + p].copy()
94
+ q_lo = codes[:, p].astype(np.int64)
95
+ q_hi = codes[:, half + p].astype(np.int64)
96
+ v_p = v[:, p]
97
+ dc_p = dc
98
+
99
+ # candidate error deltas (affine in q): de(q) = d_sub * (q_cur - q)
100
+ de_lo = ds_lo[:, p : p + 1] * (q_lo[:, None] - q_grid[None, :])
101
+ de_hi = ds_hi[:, p : p + 1] * (q_hi[:, None] - q_grid[None, :])
102
+ de_lo = np.where(active_lo[:, p : p + 1], de_lo, 0.0)
103
+ de_hi = np.where(active_hi[:, p : p + 1], de_hi, 0.0)
104
+ de_sum = de_lo[:, :, None] + de_hi[:, None, :] # (n_blocks, L, L)
105
+
106
+ d_e = (
107
+ w_lo[:, p, None, None]
108
+ * (2.0 * e_lo[:, None, None] * de_lo[:, :, None] + de_lo[:, :, None] ** 2)
109
+ + w_hi[:, p, None, None]
110
+ * (2.0 * e_hi[:, None, None] * de_hi[:, None, :] + de_hi[:, None, :] ** 2)
111
+ + lam_vw * (2.0 * v_p[:, None, None] * de_sum + de_sum * de_sum)
112
+ + lam_dc * (2.0 * dc_p[:, None, None] * de_sum + de_sum * de_sum)
113
+ )
114
+
115
+ if greedy:
116
+ flat = d_e.reshape(n_blocks, -1)
117
+ cur = q_lo * levels + q_hi
118
+ pick = np.argmin(flat, axis=1)
119
+ improve = flat[idx, pick] < -1.0e-18
120
+ pick = np.where(improve, pick, cur)
121
+ else:
122
+ cur = q_lo * levels + q_hi
123
+ scale = np.maximum(temperature, 1.0e-300)
124
+ logits = -d_e / scale[:, None, None]
125
+ logits -= logits.max(axis=(1, 2), keepdims=True)
126
+ probs = np.exp(logits).reshape(n_blocks, -1)
127
+ # dead pairs (both sub-scales ~0): codes carry no energy, keep current
128
+ dead = ~(active_lo[:, p] | active_hi[:, p])
129
+ if np.any(dead):
130
+ probs[dead] = 0.0
131
+ probs[dead, cur[dead]] = 1.0
132
+ cum = np.cumsum(probs, axis=1)
133
+ cum /= cum[:, -1:]
134
+ pick = np.sum(cum < rng.random((n_blocks, 1)), axis=1)
135
+ pick = np.minimum(pick, levels * levels - 1)
136
+
137
+ ia, ib = pick // levels, pick % levels
138
+ upd_lo = np.take_along_axis(de_lo, ia[:, None], axis=1)[:, 0]
139
+ upd_hi = np.take_along_axis(de_hi, ib[:, None], axis=1)[:, 0]
140
+ upd_sum = upd_lo + upd_hi
141
+ sse += (
142
+ w_lo[:, p] * (2.0 * e_lo * upd_lo + upd_lo * upd_lo)
143
+ + w_hi[:, p] * (2.0 * e_hi * upd_hi + upd_hi * upd_hi)
144
+ )
145
+ e[:, p] = e_lo + upd_lo
146
+ e[:, half + p] = e_hi + upd_hi
147
+ v[:, p] = v_p + upd_sum
148
+ dc = dc + upd_sum
149
+ codes[:, p] = ia
150
+ codes[:, half + p] = ib
151
+
152
+
153
+ def codes_recompute_for(
154
+ X_f64: np.ndarray, d: np.ndarray, m: np.ndarray, ls: np.ndarray, lm: np.ndarray
155
+ ) -> np.ndarray:
156
+ """Nearest rounding of codes, matching q2k.codes_from_params semantics."""
157
+ x32 = X_f64.astype(np.float32)
158
+ d32 = np.asarray(d, dtype=np.float32)
159
+ m32 = np.asarray(m, dtype=np.float32)
160
+ d_sub = np.repeat(d32[:, None] * ls.astype(np.float32), 16, axis=1)
161
+ m_sub = np.repeat(m32[:, None] * lm.astype(np.float32), 16, axis=1)
162
+ safe = d_sub >= np.float32(EPS_D)
163
+ safe_d = np.where(safe, d_sub, np.float32(1.0))
164
+ q = np.clip(nearest_int((x32 + m_sub) / safe_d), 0, 3)
165
+ return np.where(safe, q, 0).astype(np.int64)
166
+
167
+
168
+ def thermal_refine_codes(
169
+ X: np.ndarray,
170
+ w: np.ndarray | None,
171
+ d: np.ndarray,
172
+ m: np.ndarray,
173
+ ls: np.ndarray,
174
+ lm: np.ndarray,
175
+ codes: np.ndarray,
176
+ carry: np.ndarray,
177
+ cfg: HPCConfig,
178
+ dm_solve=None,
179
+ passes: int | None = None,
180
+ seed: int | None = None,
181
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, int]:
182
+ """Seeded heat-bath annealing of the code lattice for a batch of blocks.
183
+
184
+ Returns ``(d, m, ls, lm, codes, n_improved)``. Per block the returned
185
+ state never has a higher extended energy than the input state: every
186
+ end-of-pass candidate is compared against the running elementwise best
187
+ and only strict decreases are kept.
188
+ """
189
+ n_blocks = X.shape[0]
190
+ n_passes = cfg.therm_passes if passes is None else int(passes)
191
+ lam_vw = (float(cfg.vw_lambda) / QK_K) if cfg.use_spectral else 0.0
192
+ lam_dc = (float(cfg.dc_lambda) / QK_K) if cfg.use_spectral else 0.0
193
+ if n_blocks == 0 or n_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0):
194
+ return d, m, ls, lm, codes, 0
195
+ if cfg.fold_depth > 1:
196
+ raise ValueError("thermal refinement supports fold_depth <= 1")
197
+
198
+ X_f64 = np.asarray(X, dtype=np.float64)
199
+ w_f64 = None if w is None else np.asarray(w, dtype=np.float64)
200
+ carry_f64 = np.asarray(carry, dtype=np.float64)
201
+ ls = np.asarray(ls, dtype=np.int64)
202
+ lm = np.asarray(lm, dtype=np.int64)
203
+ d = np.asarray(d, dtype=np.float64)
204
+ m = np.asarray(m, dtype=np.float64)
205
+ codes = np.asarray(codes, dtype=np.int64).copy()
206
+ rng = np.random.default_rng(cfg.therm_seed if seed is None else seed)
207
+
208
+ def energies(dd, mm, cc):
209
+ d_sub = np.repeat(dd[:, None] * ls, 16, axis=1)
210
+ m_sub = np.repeat(mm[:, None] * lm, 16, axis=1)
211
+ e = X_f64 - (d_sub * cc - m_sub)
212
+ return pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64)[0]
213
+
214
+ energy_best = energies(d, m, codes)
215
+ energy0 = energy_best.copy()
216
+ best_d, best_m, best_codes = d.copy(), m.copy(), codes.copy()
217
+ d_cur, m_cur, codes_cur = d.copy(), m.copy(), codes.copy()
218
+
219
+ for t in range(n_passes):
220
+ temperature = cfg.therm_t0 * np.maximum(energy_best, 1.0e-30) / QK_K * (cfg.therm_t_decay**t)
221
+ for sweep in range(cfg.therm_sweeps + 2):
222
+ greedy = sweep >= cfg.therm_sweeps
223
+ d_sub = np.repeat(d_cur[:, None] * ls, 16, axis=1)
224
+ m_sub = np.repeat(m_cur[:, None] * lm, 16, axis=1)
225
+ e = X_f64 - (d_sub * codes_cur - m_sub)
226
+ energy_cur, wesq, v, dc = pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64)
227
+ sse = energy_cur - lam_vw * np.sum(v * v, axis=1) - lam_dc * dc * dc
228
+ gibbs_pair_pass(
229
+ X_f64,
230
+ w_f64,
231
+ d_sub,
232
+ m_sub,
233
+ codes_cur,
234
+ e,
235
+ v,
236
+ dc,
237
+ sse,
238
+ temperature,
239
+ lam_vw,
240
+ lam_dc,
241
+ np.random.default_rng(rng.integers(0, 2**62)),
242
+ greedy=greedy,
243
+ )
244
+ if greedy:
245
+ energy_now, _, _, _ = pair_energy(
246
+ X_f64 - (d_sub * codes_cur - m_sub), w_f64, lam_vw, lam_dc, carry_f64
247
+ )
248
+ if not np.any(energy_now < energy_cur - 1.0e-18):
249
+ break
250
+ codes_cur = np.clip(codes_cur, 0, 3)
251
+ energy_a = energies(d_cur, m_cur, codes_cur)
252
+ if dm_solve is not None:
253
+ dm_w = w_f64 if w_f64 is not None else np.ones_like(X_f64)
254
+ d2, m2 = dm_solve(X_f64, dm_w, ls, lm, codes_cur, d_cur, m_cur, carry_f64, cfg)
255
+ codes2 = codes_recompute_for(X_f64, d2, m2, ls, lm)
256
+ energy_b = energies(d2, m2, codes2)
257
+ take_b = energy_b < energy_a - 1.0e-18
258
+ d_cur = np.where(take_b, d2, d_cur)
259
+ m_cur = np.where(take_b, m2, m_cur)
260
+ codes_cur = np.where(take_b[:, None], codes2, codes_cur)
261
+ energy_now = energies(d_cur, m_cur, codes_cur)
262
+ take = energy_now < energy_best - 1.0e-18
263
+ best_d = np.where(take, d_cur, best_d)
264
+ best_m = np.where(take, m_cur, best_m)
265
+ best_codes = np.where(take[:, None], codes_cur, best_codes)
266
+ energy_best = np.where(take, energy_now, energy_best)
267
+
268
+ n_improved = int(np.sum(energy_best < energy0 - 1.0e-18))
269
+ return best_d, best_m, ls.copy(), lm.copy(), best_codes.astype(np.uint8), n_improved
270
+
271
+
272
+ def thermal_refine_tensor(
273
+ X: np.ndarray,
274
+ w: np.ndarray | None,
275
+ d: np.ndarray,
276
+ m: np.ndarray,
277
+ ls: np.ndarray,
278
+ lm: np.ndarray,
279
+ codes: np.ndarray,
280
+ carry: np.ndarray,
281
+ cfg: HPCConfig,
282
+ dm_solve=None,
283
+ chunk: int = 2048,
284
+ progress=None,
285
+ ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, int]:
286
+ """Chunked wrapper so peak memory stays bounded on large tensors."""
287
+ n_blocks = X.shape[0]
288
+ step = max(1, int(chunk))
289
+ total_improved = 0
290
+ for start in range(0, n_blocks, step):
291
+ stop = min(start + step, n_blocks)
292
+ sel = slice(start, stop)
293
+ d_c, m_c, ls_c, lm_c, c_c, improved = thermal_refine_codes(
294
+ X[sel],
295
+ None if w is None else w[sel],
296
+ d[sel],
297
+ m[sel],
298
+ ls[sel],
299
+ lm[sel],
300
+ codes[sel],
301
+ carry[sel],
302
+ cfg,
303
+ dm_solve=dm_solve,
304
+ seed=cfg.therm_seed ^ (0x9E3779B97F4A7C15 * (start // step) & 0x3FFFFFFFFFFFFFFF),
305
+ )
306
+ d[sel], m[sel], ls[sel], lm[sel], codes[sel] = d_c, m_c, ls_c, lm_c, c_c
307
+ total_improved += improved
308
+ if progress is not None:
309
+ progress(stop, n_blocks, improved)
310
+ return d, m, ls, lm, codes, total_improved
311
+
312
+
313
+ def thermal_refine_uniform_codes(
314
+ X: np.ndarray,
315
+ w: np.ndarray | None,
316
+ codes: np.ndarray,
317
+ step: np.ndarray,
318
+ m_const: np.ndarray,
319
+ carry: np.ndarray,
320
+ cfg: HPCConfig,
321
+ lam_vw: float,
322
+ lam_dc: float,
323
+ levels: int,
324
+ passes: int | None = None,
325
+ seed: int | None = None,
326
+ ) -> tuple[np.ndarray, int]:
327
+ """Heat-bath annealing of a level-index code lattice with FIXED step scales.
328
+
329
+ Uniform variant of ``thermal_refine_codes`` for the format quantizers
330
+ (Q4_0 / Q1_0 / Q3_K): the per-code error step ``step`` (shape (B, n)) and
331
+ the affine constant ``m_const`` (shape (B, n)) are fixed inputs — the
332
+ dequantized value of level index ``q`` is ``step*q - m_const`` so the
333
+ error is ``e = X - (step*q - m_const)`` and every move ``q -> q'`` has
334
+ the exact closed-form delta again affine in the level grid. ``codes``
335
+ are integer level indices in ``[0, levels)``.
336
+
337
+ Returns ``(codes, n_improved)``. Per block the returned state never has
338
+ a higher extended energy than the input state and its SSE never exceeds
339
+ the input SSE by more than ``cfg.sse_budget`` (extended-nonincreasing
340
+ with an SSE guard, matching the pipeline's shaper semantics).
341
+ """
342
+ n_blocks, n = X.shape
343
+ if n_blocks == 0:
344
+ return codes.astype(np.int64).copy(), 0
345
+ if n % 2 != 0:
346
+ raise ValueError("thermal refinement needs an even row width")
347
+ n_passes = cfg.therm_passes if passes is None else int(passes)
348
+ if n_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0):
349
+ return codes.astype(np.int64).copy(), 0
350
+ if cfg.fold_depth > 1:
351
+ raise ValueError("thermal refinement supports fold_depth <= 1")
352
+
353
+ X_f64 = np.asarray(X, dtype=np.float64)
354
+ w_f64 = None if w is None else np.asarray(w, dtype=np.float64)
355
+ carry_f64 = np.asarray(carry, dtype=np.float64)
356
+ step = np.asarray(step, dtype=np.float64)
357
+ m_b = np.asarray(m_const, dtype=np.float64)
358
+ codes = np.asarray(codes, dtype=np.int64).copy()
359
+ rng = np.random.default_rng(cfg.therm_seed if seed is None else seed)
360
+
361
+ def energies(cc):
362
+ e = X_f64 - (step * cc - m_b)
363
+ return pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64)
364
+
365
+ energy_best, we_sq, v0, dc0 = energies(codes)
366
+ energy0 = energy_best.copy()
367
+ sse0 = np.sum(we_sq, axis=1)
368
+ sse_cap = sse0 * (1.0 + float(cfg.sse_budget))
369
+ best_codes = codes.copy()
370
+ codes_cur = codes.copy()
371
+
372
+ for t in range(n_passes):
373
+ temperature = cfg.therm_t0 * np.maximum(energy_best, 1.0e-30) / n * (cfg.therm_t_decay**t)
374
+ for sweep in range(cfg.therm_sweeps + 2):
375
+ greedy = sweep >= cfg.therm_sweeps
376
+ e = X_f64 - (step * codes_cur - m_b)
377
+ energy_cur, we_sq, v, dc = pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64)
378
+ sse = np.sum(we_sq, axis=1)
379
+ gibbs_pair_pass(
380
+ X_f64,
381
+ w_f64,
382
+ step,
383
+ m_b,
384
+ codes_cur,
385
+ e,
386
+ v,
387
+ dc,
388
+ sse,
389
+ temperature,
390
+ lam_vw,
391
+ lam_dc,
392
+ np.random.default_rng(rng.integers(0, 2**62)),
393
+ greedy=greedy,
394
+ levels=levels,
395
+ )
396
+ if greedy:
397
+ e = X_f64 - (step * codes_cur - m_b)
398
+ energy_now, _, _, _ = pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64)
399
+ if not np.any(energy_now < energy_cur - 1.0e-18):
400
+ break
401
+ energy_now, we_sq, v_now, dc_now = energies(codes_cur)
402
+ sse_now = np.sum(we_sq, axis=1)
403
+ take = (energy_now < energy_best - 1.0e-18) & (sse_now <= sse_cap)
404
+ best_codes = np.where(take[:, None], codes_cur, best_codes)
405
+ energy_best = np.where(take, energy_now, energy_best)
406
+ codes_cur = best_codes.copy()
407
+
408
+ n_improved = int(np.sum(energy_best < energy0 - 1.0e-18))
409
+ return best_codes, n_improved
410
+
411
+
412
+ def _refine_uniform_driver(
413
+ X: np.ndarray,
414
+ w: np.ndarray | None,
415
+ codes: np.ndarray,
416
+ build_state,
417
+ carry: np.ndarray | None,
418
+ lam_vw: float,
419
+ lam_dc: float,
420
+ levels: int,
421
+ cfg: HPCConfig,
422
+ chunk: int = 2048,
423
+ progress=None,
424
+ ) -> tuple[np.ndarray, int]:
425
+ """Chunked uniform annealing so peak memory stays bounded on big tensors."""
426
+ n_blocks = X.shape[0]
427
+ codes_out = np.asarray(codes, dtype=np.int64).copy()
428
+ step = max(1, int(chunk))
429
+ total_improved = 0
430
+ for start in range(0, n_blocks, step):
431
+ stop = min(start + step, n_blocks)
432
+ sel = slice(start, stop)
433
+ step_c, m_c = build_state(start, stop)
434
+ carry_c = np.zeros(stop - start) if carry is None else carry[sel]
435
+ c_c, improved = thermal_refine_uniform_codes(
436
+ X[sel],
437
+ None if w is None else w[sel],
438
+ codes_out[sel],
439
+ step_c,
440
+ m_c,
441
+ carry_c,
442
+ cfg,
443
+ lam_vw,
444
+ lam_dc,
445
+ levels,
446
+ seed=cfg.therm_seed ^ (0x9E3779B97F4A7C15 * (start // step) & 0x3FFFFFFFFFFFFFFF),
447
+ )
448
+ codes_out[sel] = c_c
449
+ total_improved += improved
450
+ if progress is not None:
451
+ progress(stop, n_blocks, improved)
452
+ return codes_out, total_improved
453
+
454
+
455
+ def thermal_refine_q4_0(
456
+ X: np.ndarray,
457
+ W: np.ndarray | None,
458
+ d: np.ndarray,
459
+ q: np.ndarray,
460
+ cfg: HPCConfig,
461
+ chunk: int = 2048,
462
+ progress=None,
463
+ ) -> tuple[np.ndarray, int]:
464
+ """Anneal Q4_0 nibble codes against fixed fp16 super-block scales.
465
+
466
+ ``X``: raw block targets (B, 32); ``W``: importance weights (B, 32) or
467
+ None; ``d``: fp16-precision block scales (B,), possibly negative
468
+ (sign-flip polish outputs); ``q``: nibble codes (B, 32) with
469
+ dequantization ``d*(q-8)``. The Gibbs step grid is the signed slope
470
+ ``d`` itself, so negatives fall out naturally with no code remapping.
471
+ Returns refined uint8 codes and the number of strictly improved blocks;
472
+ a no-op when the refinery or spectrals are disabled.
473
+ """
474
+ n_blocks, n = X.shape
475
+ lam_vw = (float(cfg.vw_lambda) / n) if cfg.use_spectral else 0.0
476
+ lam_dc = (float(cfg.dc_lambda) / n) if cfg.use_spectral else 0.0
477
+ if n_blocks == 0 or cfg.therm_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0):
478
+ return q.copy(), 0
479
+ d64 = np.asarray(d, dtype=np.float64)
480
+
481
+ def build_state(start, stop):
482
+ ds = np.repeat(d64[start:stop][:, None], n, axis=1)
483
+ return ds, 8.0 * ds
484
+
485
+ codes_out, improved = _refine_uniform_driver(
486
+ X, W, np.asarray(q, dtype=np.int64), build_state, None,
487
+ lam_vw, lam_dc, 16, cfg, chunk=chunk, progress=progress,
488
+ )
489
+ return codes_out.astype(np.uint8), improved
490
+
491
+
492
+ def thermal_refine_q1_0(
493
+ X: np.ndarray,
494
+ W: np.ndarray | None,
495
+ d: np.ndarray,
496
+ bits: np.ndarray,
497
+ cfg: HPCConfig,
498
+ chunk: int = 2048,
499
+ progress=None,
500
+ ) -> tuple[np.ndarray, int]:
501
+ """Anneal Q1_0 bits against fixed fp16 super-block magnitudes.
502
+
503
+ ``bits``: binary codes (B, 32) with dequantization ``d*(2b-1)``; the
504
+ Gibbs step grid is ``2*d`` with affine constant ``d``. Returns refined
505
+ uint8 bits and the number of strictly improved blocks.
506
+ """
507
+ n_blocks, n = X.shape
508
+ lam_vw = (float(cfg.vw_lambda) / n) if cfg.use_spectral else 0.0
509
+ lam_dc = (float(cfg.dc_lambda) / n) if cfg.use_spectral else 0.0
510
+ if n_blocks == 0 or cfg.therm_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0):
511
+ return bits.copy(), 0
512
+ d64 = np.asarray(d, dtype=np.float64)
513
+
514
+ def build_state(start, stop):
515
+ ds = np.repeat(2.0 * np.abs(d64[start:stop])[:, None], n, axis=1)
516
+ return ds, np.repeat(np.abs(d64[start:stop])[:, None], n, axis=1)
517
+
518
+ codes_out, improved = _refine_uniform_driver(
519
+ X, W, np.asarray(bits, dtype=np.int64), build_state, None,
520
+ lam_vw, lam_dc, 2, cfg, chunk=chunk, progress=progress,
521
+ )
522
+ return codes_out.astype(np.uint8), improved
523
+
524
+
525
+ def thermal_refine_q3_k(
526
+ X: np.ndarray,
527
+ W: np.ndarray | None,
528
+ scale: np.ndarray,
529
+ codes: np.ndarray,
530
+ cfg: HPCConfig,
531
+ chunk: int = 2048,
532
+ progress=None,
533
+ ) -> tuple[np.ndarray, int]:
534
+ """Anneal Q3_K signed 3-bit codes against fixed per-superblock scales.
535
+
536
+ ``scale``: per-superblock scale values (B, 16) broadcast over the 16
537
+ codes each; ``codes``: source-signed values (B, 256) in [-4, 3]. The
538
+ Gibbs grid covers all eight signed levels; negative scales (sign-flip
539
+ polish outputs) are handled exactly via signed steps.
540
+ """
541
+ n_blocks, n = X.shape
542
+ lam_vw = (float(cfg.vw_lambda) / n) if cfg.use_spectral else 0.0
543
+ lam_dc = (float(cfg.dc_lambda) / n) if cfg.use_spectral else 0.0
544
+ if n_blocks == 0 or cfg.therm_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0):
545
+ return np.asarray(codes).copy(), 0
546
+ scale64 = np.asarray(scale, dtype=np.float64)
547
+
548
+ def build_state(start, stop):
549
+ ds = np.repeat(scale64[start:stop], 16, axis=1)
550
+ return ds, 4.0 * ds
551
+
552
+ codes_out, improved = _refine_uniform_driver(
553
+ X, W, np.asarray(codes, dtype=np.int64) + 4, build_state, None,
554
+ lam_vw, lam_dc, 8, cfg, chunk=chunk, progress=progress,
555
+ )
556
+ return codes_out - 4, improved
config.py CHANGED
@@ -74,6 +74,16 @@ class HPCConfig:
74
  quantize_embd_q4: bool = True
75
  promote_attn_q4: bool = False
76
 
 
 
 
 
 
 
 
 
 
 
77
  # ---- seed clamp -----------------------------------------------------
78
  clamp_kurtosis_off: float = 6.0
79
  clamp_kurtosis_soft: float = 20.0
@@ -119,6 +129,14 @@ class HPCConfig:
119
  raise ValueError("Viterbi weights must be non-negative")
120
  if self.workers < 0:
121
  raise ValueError("workers must be >= 0 (0 = auto CPU count)")
 
 
 
 
 
 
 
 
122
  if self.device not in ("auto", "cuda", "cpu") and not self.device.startswith("cuda:"):
123
  raise ValueError("device must be 'auto', 'cuda', 'cuda:N', or 'cpu'")
124
  if self.quant_format not in ("mixed", "mixed_q1", "custom", "q1_0", "q3_k", "q4_0", "q2_k"):
 
74
  quantize_embd_q4: bool = True
75
  promote_attn_q4: bool = False
76
 
77
+ # ---- thermal refinement (pair heat-bath annealer, opt-in) ------------
78
+ # V2-only knob (0 = off, bit-identical to the deterministic pipeline).
79
+ # Runs a seeded checkerboard Gibbs sampler over the vesica-pair lattice
80
+ # after Phase 4; late passes are monotone so output never regresses.
81
+ therm_passes: int = 0
82
+ therm_sweeps: int = 2
83
+ therm_t0: float = 0.25
84
+ therm_t_decay: float = 0.35
85
+ therm_seed: int = 271828
86
+
87
  # ---- seed clamp -----------------------------------------------------
88
  clamp_kurtosis_off: float = 6.0
89
  clamp_kurtosis_soft: float = 20.0
 
129
  raise ValueError("Viterbi weights must be non-negative")
130
  if self.workers < 0:
131
  raise ValueError("workers must be >= 0 (0 = auto CPU count)")
132
+ if self.therm_passes < 0:
133
+ raise ValueError("therm_passes must be >= 0 (0 disables thermal refinement)")
134
+ if self.therm_sweeps < 1:
135
+ raise ValueError("therm_sweeps must be >= 1")
136
+ if self.therm_t0 <= 0.0:
137
+ raise ValueError("therm_t0 must be positive")
138
+ if not 0.0 < self.therm_t_decay < 1.0:
139
+ raise ValueError("therm_t_decay must lie in (0, 1)")
140
  if self.device not in ("auto", "cuda", "cpu") and not self.device.startswith("cuda:"):
141
  raise ValueError("device must be 'auto', 'cuda', 'cuda:N', or 'cpu'")
142
  if self.quant_format not in ("mixed", "mixed_q1", "custom", "q1_0", "q3_k", "q4_0", "q2_k"):
gui.py CHANGED
@@ -181,6 +181,7 @@ class HPCQuantizeGUI:
181
  self.vw_lambda_var = tk.DoubleVar(value=1.0)
182
  self.dc_decay_var = tk.DoubleVar(value=0.85)
183
  self.sse_budget_var = tk.DoubleVar(value=0.0005)
 
184
 
185
  self.cli_preview_var = tk.StringVar()
186
  self.status_var = tk.StringVar(value="Ready.")
@@ -213,6 +214,7 @@ class HPCQuantizeGUI:
213
  self.embd_q4_var,
214
  self.promote_attn_q4_var,
215
  self.quant_format_var,
 
216
  ):
217
  v.trace_add("write", lambda *_: self._update_cli_preview())
218
 
@@ -468,6 +470,10 @@ class HPCQuantizeGUI:
468
  ttk.Entry(spec_f, textvariable=self.dc_decay_var, width=8).grid(row=1, column=1, sticky=tk.EW, padx=4, pady=2)
469
  ttk.Label(spec_f, text="SSE Budget:").grid(row=1, column=2, sticky=tk.W, padx=4, pady=2)
470
  ttk.Entry(spec_f, textvariable=self.sse_budget_var, width=8).grid(row=1, column=3, sticky=tk.EW, padx=4, pady=2)
 
 
 
 
471
 
472
  # Right: GGUF Tensor Browser
473
  browser_frame = ttk.LabelFrame(right_pane, text="3. GGUF Tensor Inspector & Live Status", padding=8)
@@ -794,6 +800,7 @@ class HPCQuantizeGUI:
794
  vw_lambda=float(self.vw_lambda_var.get()),
795
  dc_decay=float(self.dc_decay_var.get()),
796
  sse_budget=float(self.sse_budget_var.get()),
 
797
  quant_format=self.quant_format_var.get(),
798
  custom_quant_map=self._custom_map_for_plan() or {},
799
  enable_q4_fallback=bool(self.q4_fallback_var.get()) and self.quant_format_var.get() not in ("q2_k", "q3_k"),
@@ -841,6 +848,8 @@ class HPCQuantizeGUI:
841
  cmd.append("--keep-embd")
842
  if self.promote_attn_q4_var.get():
843
  cmd.append("--promote-attn-q4")
 
 
844
  cmd.append("--verbose")
845
  return " ".join(shlex.quote(part) for part in cmd)
846
 
 
181
  self.vw_lambda_var = tk.DoubleVar(value=1.0)
182
  self.dc_decay_var = tk.DoubleVar(value=0.85)
183
  self.sse_budget_var = tk.DoubleVar(value=0.0005)
184
+ self.thermal_var = tk.IntVar(value=0)
185
 
186
  self.cli_preview_var = tk.StringVar()
187
  self.status_var = tk.StringVar(value="Ready.")
 
214
  self.embd_q4_var,
215
  self.promote_attn_q4_var,
216
  self.quant_format_var,
217
+ self.thermal_var,
218
  ):
219
  v.trace_add("write", lambda *_: self._update_cli_preview())
220
 
 
470
  ttk.Entry(spec_f, textvariable=self.dc_decay_var, width=8).grid(row=1, column=1, sticky=tk.EW, padx=4, pady=2)
471
  ttk.Label(spec_f, text="SSE Budget:").grid(row=1, column=2, sticky=tk.W, padx=4, pady=2)
472
  ttk.Entry(spec_f, textvariable=self.sse_budget_var, width=8).grid(row=1, column=3, sticky=tk.EW, padx=4, pady=2)
473
+ ttk.Label(spec_f, text="Thermal Passes (0=off):").grid(row=2, column=0, sticky=tk.W, padx=4, pady=2)
474
+ ttk.Spinbox(
475
+ spec_f, from_=0, to=16, increment=1, textvariable=self.thermal_var, width=8
476
+ ).grid(row=2, column=1, sticky=tk.EW, padx=4, pady=2)
477
 
478
  # Right: GGUF Tensor Browser
479
  browser_frame = ttk.LabelFrame(right_pane, text="3. GGUF Tensor Inspector & Live Status", padding=8)
 
800
  vw_lambda=float(self.vw_lambda_var.get()),
801
  dc_decay=float(self.dc_decay_var.get()),
802
  sse_budget=float(self.sse_budget_var.get()),
803
+ therm_passes=max(0, int(self.thermal_var.get())),
804
  quant_format=self.quant_format_var.get(),
805
  custom_quant_map=self._custom_map_for_plan() or {},
806
  enable_q4_fallback=bool(self.q4_fallback_var.get()) and self.quant_format_var.get() not in ("q2_k", "q3_k"),
 
848
  cmd.append("--keep-embd")
849
  if self.promote_attn_q4_var.get():
850
  cmd.append("--promote-attn-q4")
851
+ if self.thermal_var.get() > 0:
852
+ cmd += ["--thermal", str(int(self.thermal_var.get()))]
853
  cmd.append("--verbose")
854
  return " ".join(shlex.quote(part) for part in cmd)
855
 
pipeline.py CHANGED
@@ -23,6 +23,7 @@ import time
23
 
24
  import numpy as np
25
 
 
26
  from .config import HPCConfig
27
  from .factored import (
28
  CAND_TO_QUHIT,
@@ -69,6 +70,7 @@ class TensorStats:
69
  temperature: float
70
  factor_floor_used: int
71
  candidate_floor_used: int
 
72
 
73
 
74
  @dataclass(slots=True)
@@ -1070,6 +1072,72 @@ class Q2KQuantizer:
1070
  eta=eta,
1071
  )
1072
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1073
  if cfg.apply_dc_carry and (cfg.dc_lambda > 0.0 or cfg.dc_decay > 0.0):
1074
  _check_stop()
1075
  _emit(
@@ -1103,6 +1171,7 @@ class Q2KQuantizer:
1103
  temperature=temperature,
1104
  factor_floor_used=factor_floor_used,
1105
  candidate_floor_used=candidate_floor_used,
 
1106
  )
1107
  _emit(
1108
  "Phase 5: Tensor Complete",
@@ -1116,6 +1185,7 @@ class Q2KQuantizer:
1116
  temperature=temperature,
1117
  cand_floors=candidate_floor_used,
1118
  fac_floors=factor_floor_used,
 
1119
  )
1120
  scales, qs, dm = _stack_blocks(blocks)
1121
  factor_objectives = np.array([r.objective for r in standalone], dtype=np.float64)
@@ -1148,8 +1218,9 @@ def _stack_blocks(blocks):
1148
 
1149
 
1150
  def _report(stats: TensorStats) -> None:
 
1151
  print(
1152
  f"Q2_K V2: blocks={stats.n_blocks} rmse={stats.rmse:.6e} "
1153
  f"rmse/sigma={stats.rmse_over_sigma:.5f} temp={stats.temperature:.3e} "
1154
- f"floors(cand={stats.candidate_floor_used}, factor={stats.factor_floor_used})"
1155
  )
 
23
 
24
  import numpy as np
25
 
26
+ from .anneal import thermal_refine_tensor
27
  from .config import HPCConfig
28
  from .factored import (
29
  CAND_TO_QUHIT,
 
70
  temperature: float
71
  factor_floor_used: int
72
  candidate_floor_used: int
73
+ therm_improved: int = 0
74
 
75
 
76
  @dataclass(slots=True)
 
1072
  eta=eta,
1073
  )
1074
 
1075
+ therm_improved = 0
1076
+ if cfg.therm_passes > 0 and n_blocks > 0:
1077
+ _check_stop()
1078
+ t_therm = time.time()
1079
+ _emit(
1080
+ "Phase 4.5: Thermal Refinery (Pair Heat-Bath)",
1081
+ 0,
1082
+ n_blocks,
1083
+ f"annealing {n_blocks} blocks: passes={cfg.therm_passes} "
1084
+ f"sweeps={cfg.therm_sweeps} T0={cfg.therm_t0} decay={cfg.therm_t_decay} "
1085
+ f"seed={cfg.therm_seed}",
1086
+ sigma=sigma,
1087
+ temperature=temperature,
1088
+ )
1089
+ therm_d = np.empty(n_blocks, dtype=np.float64)
1090
+ therm_m = np.empty(n_blocks, dtype=np.float64)
1091
+ therm_ls = np.empty((n_blocks, NS), dtype=np.uint8)
1092
+ therm_lm = np.empty((n_blocks, NS), dtype=np.uint8)
1093
+ therm_codes = np.empty((n_blocks, QK_K), dtype=np.uint8)
1094
+ for i, b in enumerate(blocks):
1095
+ therm_d[i] = b.d_float
1096
+ therm_m[i] = b.dmin_float
1097
+ therm_ls[i], therm_lm[i] = b.unpack_scales()
1098
+ therm_codes[i] = unpack_codes(b.qs)
1099
+ last_therm = [0.0, 0]
1100
+
1101
+ def _therm_progress(done, total, improved):
1102
+ now = time.time()
1103
+ if (now - last_therm[0]) >= 0.25 or done == total:
1104
+ last_therm[0] = now
1105
+ _emit(
1106
+ "Phase 4.5: Thermal Refinery (Pair Heat-Bath)",
1107
+ done,
1108
+ total,
1109
+ f"blk {done}/{total} ({max(done, 1)/max(now - t_therm, 1.0e-6):.1f} blk/s) "
1110
+ f"improved={improved}",
1111
+ sigma=sigma,
1112
+ temperature=temperature,
1113
+ )
1114
+
1115
+ therm_d, therm_m, therm_ls, therm_lm, therm_codes, therm_improved = thermal_refine_tensor(
1116
+ X,
1117
+ None if W is None else W,
1118
+ therm_d,
1119
+ therm_m,
1120
+ therm_ls,
1121
+ therm_lm,
1122
+ therm_codes,
1123
+ carry,
1124
+ cfg,
1125
+ dm_solve=_batch_extended_dm_solve_and_ulp,
1126
+ progress=_therm_progress,
1127
+ )
1128
+ blocks = [
1129
+ make_block(therm_d[i], therm_m[i], therm_ls[i], therm_lm[i], therm_codes[i])
1130
+ for i in range(n_blocks)
1131
+ ]
1132
+ _emit(
1133
+ "Phase 4.5: Thermal Refinery (Pair Heat-Bath)",
1134
+ n_blocks,
1135
+ n_blocks,
1136
+ f"done in {time.time() - t_therm:.1f}s, blocks improved={therm_improved}/{n_blocks}",
1137
+ sigma=sigma,
1138
+ temperature=temperature,
1139
+ )
1140
+
1141
  if cfg.apply_dc_carry and (cfg.dc_lambda > 0.0 or cfg.dc_decay > 0.0):
1142
  _check_stop()
1143
  _emit(
 
1171
  temperature=temperature,
1172
  factor_floor_used=factor_floor_used,
1173
  candidate_floor_used=candidate_floor_used,
1174
+ therm_improved=therm_improved,
1175
  )
1176
  _emit(
1177
  "Phase 5: Tensor Complete",
 
1185
  temperature=temperature,
1186
  cand_floors=candidate_floor_used,
1187
  fac_floors=factor_floor_used,
1188
+ therm_improved=therm_improved,
1189
  )
1190
  scales, qs, dm = _stack_blocks(blocks)
1191
  factor_objectives = np.array([r.objective for r in standalone], dtype=np.float64)
 
1218
 
1219
 
1220
  def _report(stats: TensorStats) -> None:
1221
+ extra = f" therm={stats.therm_improved}" if stats.therm_improved else ""
1222
  print(
1223
  f"Q2_K V2: blocks={stats.n_blocks} rmse={stats.rmse:.6e} "
1224
  f"rmse/sigma={stats.rmse_over_sigma:.5f} temp={stats.temperature:.3e} "
1225
+ f"floors(cand={stats.candidate_floor_used}, factor={stats.factor_floor_used}){extra}"
1226
  )
q1_0.py CHANGED
@@ -18,6 +18,10 @@ per 18-byte block = 1.125 bpw, matching ``block_q1_0`` in ``llama.cpp``):
18
  Polishes ``d`` over ``+/-8`` fp16 ULPs of both ``+d`` and ``-d``, and
19
  applies closed-form 1-D quadratic Vesica + DC bit-flip projection bounded by
20
  ``sse_budget`` plus a strict monotone candidate floor.
 
 
 
 
21
  """
22
 
23
  from __future__ import annotations
@@ -30,6 +34,7 @@ try:
30
  except ImportError: # pragma: no cover
31
  torch = None # type: ignore[assignment]
32
 
 
33
  from .config import HPCConfig
34
  from .cuda_backend import cuda_is_available
35
  from .factored import MULTS_D
@@ -251,6 +256,13 @@ def _quantize_q1_0_hpc_cpu(
251
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-15)
252
  best_q = np.where(accept[:, None], q_new, best_q)
253
 
 
 
 
 
 
 
 
254
  best_q_u8 = best_q.astype(np.uint8)
255
  out = np.empty((B, Q1_0_BLOCK_BYTES), dtype=np.uint8)
256
  out[:, :2] = best_d16.astype("<u2").view(np.uint8).reshape(B, 2)
@@ -375,6 +387,17 @@ def _quantize_q1_0_hpc_cuda(
375
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-12)
376
  best_q = torch.where(accept[:, None], q_new, best_q)
377
 
 
 
 
 
 
 
 
 
 
 
 
378
  best_q_u8 = best_q.to(torch.uint8)
379
  deq_fin = best_d[:, None] * (2.0 * best_q_u8.to(torch.float32) - 1.0)
380
  raw_sse = float(torch.sum((X - deq_fin) ** 2, dtype=torch.float64).item())
 
18
  Polishes ``d`` over ``+/-8`` fp16 ULPs of both ``+d`` and ``-d``, and
19
  applies closed-form 1-D quadratic Vesica + DC bit-flip projection bounded by
20
  ``sse_budget`` plus a strict monotone candidate floor.
21
+ 5. **Optional Thermal Refinery (Pair Heat-Bath, opt-in)**:
22
+ With ``cfg.therm_passes > 0`` the finished bits are re-swept by the seeded
23
+ pair heat-bath annealer (Anneal) on the 2-level Q1_0 lattice in the same
24
+ companded target space, non-increasing under the ``sse_budget`` cap.
25
  """
26
 
27
  from __future__ import annotations
 
34
  except ImportError: # pragma: no cover
35
  torch = None # type: ignore[assignment]
36
 
37
+ from .anneal import thermal_refine_q1_0
38
  from .config import HPCConfig
39
  from .cuda_backend import cuda_is_available
40
  from .factored import MULTS_D
 
256
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-15)
257
  best_q = np.where(accept[:, None], q_new, best_q)
258
 
259
+ # 5. Optional Thermal Refinery (Pair Heat-Bath, non-regressing):
260
+ if cfg.therm_passes > 0:
261
+ thermal_q, _ = thermal_refine_q1_0(
262
+ X_target.astype(np.float32), W_f32, best_d, best_q, cfg
263
+ )
264
+ best_q = thermal_q.astype(np.int64)
265
+
266
  best_q_u8 = best_q.astype(np.uint8)
267
  out = np.empty((B, Q1_0_BLOCK_BYTES), dtype=np.uint8)
268
  out[:, :2] = best_d16.astype("<u2").view(np.uint8).reshape(B, 2)
 
387
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-12)
388
  best_q = torch.where(accept[:, None], q_new, best_q)
389
 
390
+ # 5. Optional Thermal Refinery (Pair Heat-Bath, non-regressing):
391
+ if cfg.therm_passes > 0:
392
+ thermal_q, _ = thermal_refine_q1_0(
393
+ X_target.cpu().numpy(),
394
+ None if W_np is None else W.cpu().numpy(),
395
+ best_d.cpu().numpy(),
396
+ best_q.cpu().numpy(),
397
+ cfg,
398
+ )
399
+ best_q = torch.from_numpy(thermal_q.astype(np.int64)).to(dev)
400
+
401
  best_q_u8 = best_q.to(torch.uint8)
402
  deq_fin = best_d[:, None] * (2.0 * best_q_u8.to(torch.float32) - 1.0)
403
  raw_sse = float(torch.sum((X - deq_fin) ** 2, dtype=torch.float64).item())
q3_k.py CHANGED
@@ -48,6 +48,7 @@ try:
48
  except ImportError: # pragma: no cover
49
  torch = None # type: ignore[assignment]
50
 
 
51
  from .config import HPCConfig
52
  from .cuda_backend import cuda_is_available
53
  from .factored import MULTS_D
@@ -646,6 +647,12 @@ def _fit_q3_k_blocks(
646
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-15)
647
  code = np.where(accept[:, None], code_new, code_f).reshape(B, Q3_K_N_SUBBLOCKS, Q3_K_SUBBLOCK)
648
 
 
 
 
 
 
 
649
  return best_d16.astype(np.uint16), sc.astype(np.int16), code.astype(np.int16)
650
 
651
 
@@ -806,6 +813,20 @@ def _fit_q3_k_blocks_cuda(
806
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-12)
807
  code = torch.where(accept[:, None], code_new, code_f).reshape(B, Q3_K_N_SUBBLOCKS, Q3_K_SUBBLOCK)
808
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
809
  return (
810
  best_d16.cpu().numpy().astype(np.uint16),
811
  sc.cpu().numpy().astype(np.int16),
 
48
  except ImportError: # pragma: no cover
49
  torch = None # type: ignore[assignment]
50
 
51
+ from .anneal import thermal_refine_q3_k
52
  from .config import HPCConfig
53
  from .cuda_backend import cuda_is_available
54
  from .factored import MULTS_D
 
647
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-15)
648
  code = np.where(accept[:, None], code_new, code_f).reshape(B, Q3_K_N_SUBBLOCKS, Q3_K_SUBBLOCK)
649
 
650
+ # 5. Optional Thermal Refinery (Pair Heat-Bath, non-regressing):
651
+ if cfg.therm_passes > 0:
652
+ scale_rp = best_d[:, None] * sc.astype(np.float32)
653
+ thermal_c, _ = thermal_refine_q3_k(Xf, Wf, scale_rp, code.reshape(B, QK3_K), cfg)
654
+ code = thermal_c.astype(np.int16).reshape(B, Q3_K_N_SUBBLOCKS, Q3_K_SUBBLOCK)
655
+
656
  return best_d16.astype(np.uint16), sc.astype(np.int16), code.astype(np.int16)
657
 
658
 
 
813
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-12)
814
  code = torch.where(accept[:, None], code_new, code_f).reshape(B, Q3_K_N_SUBBLOCKS, Q3_K_SUBBLOCK)
815
 
816
+ # 5. Optional Thermal Refinery (Pair Heat-Bath, non-regressing):
817
+ if cfg.therm_passes > 0:
818
+ scale_rp = (best_d[:, None] * sc.to(torch.float32)).cpu().numpy()
819
+ thermal_c, _ = thermal_refine_q3_k(
820
+ Xf.cpu().numpy(),
821
+ Wf.cpu().numpy(),
822
+ scale_rp,
823
+ code.reshape(B, QK3_K).cpu().numpy(),
824
+ cfg,
825
+ )
826
+ code = torch.from_numpy(
827
+ thermal_c.astype(np.int16).reshape(B, Q3_K_N_SUBBLOCKS, Q3_K_SUBBLOCK)
828
+ ).to(dev)
829
+
830
  return (
831
  best_d16.cpu().numpy().astype(np.uint16),
832
  sc.cpu().numpy().astype(np.int16),
q4_0.py CHANGED
@@ -16,9 +16,13 @@ per 18-byte block = 4.5 bpw):
16
  Viterbi dynamic program with free-energy bin reduction and Manhattan
17
  transition penalty across the block chain.
18
  4. **Asymmetric Sign-Flip fp16-ULP Polish & Closed-Form Vesica/DC Shaping**:
19
- Polishes ``d`` over ``+/-8`` fp16 ULPs of both ``+d`` and ``-d``, and
20
- applies closed-form 1-D quadratic Vesica + DC code projection bounded by
21
- ``sse_budget`` plus a strict monotone candidate floor.
 
 
 
 
22
  """
23
 
24
  from __future__ import annotations
@@ -31,6 +35,7 @@ try:
31
  except ImportError: # pragma: no cover
32
  torch = None # type: ignore[assignment]
33
 
 
34
  from .config import HPCConfig
35
  from .cuda_backend import cuda_is_available
36
  from .factored import CAND_TO_QUHIT, MULTS_D
@@ -248,6 +253,11 @@ def _quantize_q4_0_hpc_cpu(
248
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-15)
249
  best_q = np.where(accept[:, None], q_new, best_q)
250
 
 
 
 
 
 
251
  best_q_u8 = best_q.astype(np.uint8)
252
  out = np.empty((B, Q4_0_BLOCK_BYTES), dtype=np.uint8)
253
  out[:, :2] = best_d16.astype("<u2").view(np.uint8).reshape(B, 2)
@@ -380,6 +390,17 @@ def _quantize_q4_0_hpc_cuda(
380
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-12)
381
  best_q = torch.where(accept[:, None], q_new, best_q)
382
 
 
 
 
 
 
 
 
 
 
 
 
383
  best_q_u8 = best_q.to(torch.uint8)
384
  deq_fin = best_d[:, None] * (best_q_u8.to(torch.float32) - 8.0)
385
  raw_sse = float(torch.sum((X - deq_fin) ** 2, dtype=torch.float64).item())
 
16
  Viterbi dynamic program with free-energy bin reduction and Manhattan
17
  transition penalty across the block chain.
18
  4. **Asymmetric Sign-Flip fp16-ULP Polish & Closed-Form Vesica/DC Shaping**:
19
+ Polishes ``d`` over ``+/-8`` fp16 ULPs of both ``+d`` and ``-d``, and
20
+ applies closed-form 1-D quadratic Vesica + DC code projection bounded by
21
+ ``sse_budget`` plus a strict monotone candidate floor.
22
+ 5. **Optional Thermal Refinery (Pair Heat-Bath, opt-in)**:
23
+ With ``cfg.therm_passes > 0`` the finished nibble codes are re-swept by
24
+ the seeded pair heat-bath annealer (Anneal) on the 16-level Q4_0 lattice
25
+ with extended-nonincreasing acceptance under the ``sse_budget`` cap.
26
  """
27
 
28
  from __future__ import annotations
 
35
  except ImportError: # pragma: no cover
36
  torch = None # type: ignore[assignment]
37
 
38
+ from .anneal import thermal_refine_q4_0
39
  from .config import HPCConfig
40
  from .cuda_backend import cuda_is_available
41
  from .factored import CAND_TO_QUHIT, MULTS_D
 
253
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-15)
254
  best_q = np.where(accept[:, None], q_new, best_q)
255
 
256
+ # 5. Optional Thermal Refinery (Pair Heat-Bath, non-regressing):
257
+ if cfg.therm_passes > 0:
258
+ thermal_q, _ = thermal_refine_q4_0(X_f32, W_f32, best_d, best_q, cfg)
259
+ best_q = thermal_q.astype(np.int64)
260
+
261
  best_q_u8 = best_q.astype(np.uint8)
262
  out = np.empty((B, Q4_0_BLOCK_BYTES), dtype=np.uint8)
263
  out[:, :2] = best_d16.astype("<u2").view(np.uint8).reshape(B, 2)
 
390
  accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-12)
391
  best_q = torch.where(accept[:, None], q_new, best_q)
392
 
393
+ # 5. Optional Thermal Refinery (Pair Heat-Bath, non-regressing):
394
+ if cfg.therm_passes > 0:
395
+ thermal_q, _ = thermal_refine_q4_0(
396
+ X.cpu().numpy(),
397
+ None if W_np is None else W.cpu().numpy(),
398
+ best_d.cpu().numpy(),
399
+ best_q.cpu().numpy(),
400
+ cfg,
401
+ )
402
+ best_q = torch.from_numpy(thermal_q.astype(np.int64)).to(dev)
403
+
404
  best_q_u8 = best_q.to(torch.uint8)
405
  deq_fin = best_d[:, None] * (best_q_u8.to(torch.float32) - 8.0)
406
  raw_sse = float(torch.sum((X - deq_fin) ** 2, dtype=torch.float64).item())