CompressedGemma commited on
Commit
87354cc
·
verified ·
1 Parent(s): d6ea9a1

Upload 9 files

Browse files
Files changed (6) hide show
  1. config.py +19 -2
  2. factored.py +103 -80
  3. pipeline.py +75 -54
  4. seeds.py +7 -3
  5. spectral.py +17 -7
  6. viterbi.py +6 -5
config.py CHANGED
@@ -24,10 +24,13 @@ class HPCConfig:
24
  n_cand_m: int = 6
25
  factor_pin: int = 3
26
  factor_iters: int = 1
27
- factor_standalone_iters: int = 16
28
- factor_starts: int = 3
 
 
29
  factor_ulp_radius: int = 1
30
  factor_refine_1d: bool = True
 
31
  factor_spectral_refine: bool = True
32
  factor_exact_emit: bool = True
33
 
@@ -89,4 +92,18 @@ class HPCConfig:
89
  raise ValueError("ULP radii must be non-negative")
90
  if self.refine_passes < 1:
91
  raise ValueError("refine_passes must be >= 1")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
92
  return self
 
24
  n_cand_m: int = 6
25
  factor_pin: int = 3
26
  factor_iters: int = 1
27
+ factor_1d_iters: int = 6
28
+ factor_1d_refine_sweeps: int = 1
29
+ factor_standalone_iters: int = 8
30
+ factor_starts: int = 2
31
  factor_ulp_radius: int = 1
32
  factor_refine_1d: bool = True
33
+ factor_portfolio: bool = True
34
  factor_spectral_refine: bool = True
35
  factor_exact_emit: bool = True
36
 
 
92
  raise ValueError("ULP radii must be non-negative")
93
  if self.refine_passes < 1:
94
  raise ValueError("refine_passes must be >= 1")
95
+ if not 1 <= self.subblock_states <= 6:
96
+ raise ValueError("subblock_states must lie in [1, 6]")
97
+ if self.factor_1d_iters < 1 or self.factor_standalone_iters < 1:
98
+ raise ValueError("factor iteration budgets must be >= 1")
99
+ if self.factor_starts < 1:
100
+ raise ValueError("factor_starts must be >= 1")
101
+ if self.sse_budget < 0.0:
102
+ raise ValueError("sse_budget must be non-negative")
103
+ if not 0.0 < self.same_penalty <= 1.0:
104
+ raise ValueError("same_penalty must lie in (0, 1]")
105
+ if self.log_slack < 0.0:
106
+ raise ValueError("log_slack must be non-negative")
107
+ if self.viterbi_alpha < 0.0 or self.viterbi_beta < 0.0:
108
+ raise ValueError("Viterbi weights must be non-negative")
109
  return self
factored.py CHANGED
@@ -201,17 +201,47 @@ def _refine_integer_codes(v, w, d, codes, nmax, sweeps=2):
201
  return d, codes.astype(np.uint8)
202
 
203
 
204
- def factor_1d(v, w, nmax: int = 15, iters: int = 8, refine: bool = True):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
205
  """Weighted 1-D factorisation ``v ~= d * L`` with ``L in [0, nmax]``."""
206
  v = np.maximum(np.asarray(v, dtype=np.float64), 0.0)
207
  w = np.maximum(np.asarray(w, dtype=np.float64), 0.0)
208
  d, codes = _factor_1d_lloyd(v, w, nmax, iters)
209
  if refine and d > 0.0:
210
- d, codes = _refine_integer_codes(v, w, d, codes, nmax)
211
  return d, codes
212
 
213
 
214
- def _range_init(x, w, cfg: HPCConfig):
 
 
 
 
 
 
 
 
 
215
  x = np.asarray(x, dtype=np.float64)
216
  w = np.asarray(w, dtype=np.float64)
217
  blocks = x.reshape(NS, 16)
@@ -233,8 +263,8 @@ def _range_init(x, w, cfg: HPCConfig):
233
  nearest_int((sub + tj) / sj), 0, 3
234
  ).astype(np.uint8)
235
  sw = wblocks.sum(axis=1) if w is not None else np.full(NS, 16.0)
236
- d, ls = factor_1d(s, sw, 15, cfg.factor_iters, cfg.factor_refine_1d)
237
- m, lm = factor_1d(t, sw, 15, cfg.factor_iters, cfg.factor_refine_1d)
238
  return d, m, ls, lm, codes
239
 
240
 
@@ -246,7 +276,7 @@ def _seeded_init(x, scales, mins, d_seed, m_seed):
246
  return d, m, ls, lm, codes
247
 
248
 
249
- def _ls_init(x, w, cfg: HPCConfig):
250
  x = np.asarray(x, dtype=np.float64)
251
  w = np.asarray(w, dtype=np.float64)
252
  blocks = x.reshape(NS, 16)
@@ -262,8 +292,8 @@ def _ls_init(x, w, cfg: HPCConfig):
262
  ).astype(np.uint8)
263
  s, t = _sub_ls(x, w, codes)
264
  sw = w.reshape(NS, 16).sum(axis=1)
265
- d, ls = factor_1d(s, sw, 15, cfg.factor_iters, cfg.factor_refine_1d)
266
- m, lm = factor_1d(t, sw, 15, cfg.factor_iters, cfg.factor_refine_1d)
267
  codes = codes_from_params(x, d, m, ls, lm)
268
  return d, m, ls, lm, codes
269
 
@@ -274,34 +304,23 @@ def _sub_ls(x, w, codes):
274
  q = np.asarray(codes, dtype=np.float64).reshape(NS, 16)
275
  xb = x.reshape(NS, 16)
276
  wb = w.reshape(NS, 16)
277
- s = np.zeros(NS)
278
- t = np.zeros(NS)
279
- for j in range(NS):
280
- ww = wb[j]
281
- qq = q[j]
282
- xx = xb[j]
283
- Sqq = float(np.sum(ww * qq * qq))
284
- Swq = float(np.sum(ww * qq))
285
- Sw = float(np.sum(ww))
286
- Sqx = float(np.sum(ww * qq * xx))
287
- Sx = float(np.sum(ww * xx))
288
- det = Sqq * Sw - Swq * Swq
289
- if det > 1.0e-30:
290
- sj = (Sqx * Sw - Swq * Sx) / det
291
- tj = (sj * Swq - Sx) / Sw
292
- else:
293
- sj = 0.0
294
- tj = 0.0
295
- if sj < 0.0:
296
- sj = 0.0
297
- if tj < 0.0:
298
- tj = 0.0
299
- sj = Sqx / Sqq if Sqq > 1.0e-30 else 0.0
300
- if sj < 0.0:
301
- sj = 0.0
302
- s[j] = sj
303
- t[j] = tj
304
- return s, t
305
 
306
 
307
  def _solve(
@@ -317,6 +336,7 @@ def _solve(
317
  use_spectral: bool,
318
  max_iter: int,
319
  dc_carry: float,
 
320
  ):
321
  x = np.asarray(x, dtype=np.float32)
322
  w = np.asarray(w, dtype=np.float32)
@@ -345,9 +365,7 @@ def _solve(
345
  else:
346
  ls = np.zeros(NS, dtype=np.uint8)
347
  else:
348
- d_new, ls_new = factor_1d(
349
- s, ws, 15, cfg.factor_iters, cfg.factor_refine_1d
350
- )
351
  d, ls = float(d_new), ls_new
352
 
353
  if pin & 2:
@@ -356,9 +374,7 @@ def _solve(
356
  else:
357
  lm = np.zeros(NS, dtype=np.uint8)
358
  else:
359
- m_new, lm_new = factor_1d(
360
- t, wt, 15, cfg.factor_iters, cfg.factor_refine_1d
361
- )
362
  m, lm = float(m_new), lm_new
363
 
364
  codes = codes_from_params(x, d, m, ls, lm)
@@ -432,48 +448,55 @@ def factor_q2k_block(
432
  max_iter = cfg.factor_standalone_iters if max_iter is None else max_iter
433
  starts = cfg.factor_starts if starts is None else starts
434
 
435
- candidates = []
436
- if init_s is not None and init_t is not None and d_seed > 1.0e-30 and m_seed > 1.0e-30:
437
- d, m, ls, lm, codes = _seeded_init(x, init_s, init_t, d_seed, m_seed)
438
- candidates.append((d, m, ls, lm, codes))
439
- else:
440
- d, m, ls, lm, codes = _range_init(x, w, cfg)
441
- if d_seed > 1.0e-30:
442
- d = float(d_seed)
443
- if init_s is not None:
444
- ls = np.clip(nearest_int(np.asarray(init_s) / d), 0, 15).astype(np.uint8)
445
- if m_seed > 1.0e-30:
446
- m = float(m_seed)
447
- if init_t is not None:
448
- lm = np.clip(nearest_int(np.asarray(init_t) / m), 0, 15).astype(np.uint8)
449
- candidates.append((d, m, ls, lm, codes))
450
- if starts > 1:
451
- candidates.append(_ls_init(x, w, cfg))
 
452
 
453
  best = None
454
  total_iters = 0
455
- for d, m, ls, lm, codes in candidates:
456
- d, m, ls, lm, codes, it = _solve(
457
- x, w, cfg, d, m, ls, lm, codes, pin, use_spectral, max_iter, dc_carry
458
- )
459
- total_iters += it
460
- d, m, d16, m16, ls, lm, codes, sse, obj = _emit_polish(
461
- x, w, cfg, d, m, ls, lm, codes, dc_carry
462
- )
463
- if best is None or obj < best.objective - 1.0e-15:
464
- best = FactorResult(
465
- d=np.float32(d),
466
- dmin=np.float32(m),
467
- d16=np.uint16(d16),
468
- m16=np.uint16(m16),
469
- ls=ls.astype(np.uint8),
470
- lm=lm.astype(np.uint8),
471
- codes=codes.astype(np.uint8),
472
- sse=float(sse),
473
- objective=float(obj),
474
- iterations=total_iters,
475
- starts=len(candidates),
476
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
477
  return best
478
 
479
 
 
201
  return d, codes.astype(np.uint8)
202
 
203
 
204
+ def _factor_1d_legacy(v, w, nmax: int = 15, iters: int = 6):
205
+ v = np.maximum(np.asarray(v, dtype=np.float64), 0.0)
206
+ w = np.maximum(np.asarray(w, dtype=np.float64), 0.0)
207
+ vmax = float(v.max())
208
+ if vmax < 1.0e-30:
209
+ return 0.0, np.zeros(v.size, dtype=np.uint8)
210
+ d = vmax / nmax
211
+ for _ in range(iters):
212
+ codes = np.clip(nearest_int(v / d), 0, nmax)
213
+ den = float(np.sum(w * codes * codes))
214
+ if den < 1.0e-30:
215
+ break
216
+ dn = float(np.sum(w * v * codes) / den)
217
+ conv = abs(dn - d) <= 1.0e-7 * abs(d) + 1.0e-30
218
+ d = dn
219
+ if conv:
220
+ break
221
+ codes = np.clip(nearest_int(v / d), 0, nmax)
222
+ return d, codes.astype(np.uint8)
223
+
224
+
225
+ def factor_1d(v, w, nmax: int = 15, iters: int = 8, refine: bool = True, sweeps: int = 1):
226
  """Weighted 1-D factorisation ``v ~= d * L`` with ``L in [0, nmax]``."""
227
  v = np.maximum(np.asarray(v, dtype=np.float64), 0.0)
228
  w = np.maximum(np.asarray(w, dtype=np.float64), 0.0)
229
  d, codes = _factor_1d_lloyd(v, w, nmax, iters)
230
  if refine and d > 0.0:
231
+ d, codes = _refine_integer_codes(v, w, d, codes, nmax, sweeps)
232
  return d, codes
233
 
234
 
235
+ def _factor_1d_for(v, w, nmax, cfg: HPCConfig, legacy: bool):
236
+ if legacy:
237
+ return _factor_1d_legacy(v, w, nmax, 6)
238
+ return factor_1d(
239
+ v, w, nmax, cfg.factor_1d_iters, cfg.factor_refine_1d,
240
+ cfg.factor_1d_refine_sweeps,
241
+ )
242
+
243
+
244
+ def _range_init(x, w, cfg: HPCConfig, legacy_1d: bool = False):
245
  x = np.asarray(x, dtype=np.float64)
246
  w = np.asarray(w, dtype=np.float64)
247
  blocks = x.reshape(NS, 16)
 
263
  nearest_int((sub + tj) / sj), 0, 3
264
  ).astype(np.uint8)
265
  sw = wblocks.sum(axis=1) if w is not None else np.full(NS, 16.0)
266
+ d, ls = _factor_1d_for(s, sw, 15, cfg, legacy_1d)
267
+ m, lm = _factor_1d_for(t, sw, 15, cfg, legacy_1d)
268
  return d, m, ls, lm, codes
269
 
270
 
 
276
  return d, m, ls, lm, codes
277
 
278
 
279
+ def _ls_init(x, w, cfg: HPCConfig, legacy_1d: bool = False):
280
  x = np.asarray(x, dtype=np.float64)
281
  w = np.asarray(w, dtype=np.float64)
282
  blocks = x.reshape(NS, 16)
 
292
  ).astype(np.uint8)
293
  s, t = _sub_ls(x, w, codes)
294
  sw = w.reshape(NS, 16).sum(axis=1)
295
+ d, ls = _factor_1d_for(s, sw, 15, cfg, legacy_1d)
296
+ m, lm = _factor_1d_for(t, sw, 15, cfg, legacy_1d)
297
  codes = codes_from_params(x, d, m, ls, lm)
298
  return d, m, ls, lm, codes
299
 
 
304
  q = np.asarray(codes, dtype=np.float64).reshape(NS, 16)
305
  xb = x.reshape(NS, 16)
306
  wb = w.reshape(NS, 16)
307
+ Sqq = np.sum(wb * q * q, axis=1)
308
+ Swq = np.sum(wb * q, axis=1)
309
+ Sw = np.sum(wb, axis=1)
310
+ Sqx = np.sum(wb * q * xb, axis=1)
311
+ Sx = np.sum(wb * xb, axis=1)
312
+ det = Sqq * Sw - Swq * Swq
313
+ valid = det > 1.0e-30
314
+ safe_det = np.where(valid, det, 1.0)
315
+ safe_sw = np.where(Sw > 1.0e-30, Sw, 1.0)
316
+ sj = np.where(valid, (Sqx * Sw - Swq * Sx) / safe_det, 0.0)
317
+ tj = np.where(valid, (sj * Swq - Sx) / safe_sw, 0.0)
318
+ sj = np.maximum(sj, 0.0)
319
+ bad_tj = tj < 0.0
320
+ tj = np.where(bad_tj, 0.0, tj)
321
+ fallback = np.where(Sqq > 1.0e-30, Sqx / np.where(Sqq > 1.0e-30, Sqq, 1.0), 0.0)
322
+ sj = np.where(bad_tj, np.maximum(fallback, 0.0), sj)
323
+ return sj, tj
 
 
 
 
 
 
 
 
 
 
 
324
 
325
 
326
  def _solve(
 
336
  use_spectral: bool,
337
  max_iter: int,
338
  dc_carry: float,
339
+ legacy_1d: bool = False,
340
  ):
341
  x = np.asarray(x, dtype=np.float32)
342
  w = np.asarray(w, dtype=np.float32)
 
365
  else:
366
  ls = np.zeros(NS, dtype=np.uint8)
367
  else:
368
+ d_new, ls_new = _factor_1d_for(s, ws, 15, cfg, legacy_1d)
 
 
369
  d, ls = float(d_new), ls_new
370
 
371
  if pin & 2:
 
374
  else:
375
  lm = np.zeros(NS, dtype=np.uint8)
376
  else:
377
+ m_new, lm_new = _factor_1d_for(t, wt, 15, cfg, legacy_1d)
 
 
378
  m, lm = float(m_new), lm_new
379
 
380
  codes = codes_from_params(x, d, m, ls, lm)
 
448
  max_iter = cfg.factor_standalone_iters if max_iter is None else max_iter
449
  starts = cfg.factor_starts if starts is None else starts
450
 
451
+ def build_candidates(legacy_1d: bool):
452
+ out = []
453
+ if init_s is not None and init_t is not None and d_seed > 1.0e-30 and m_seed > 1.0e-30:
454
+ out.append(_seeded_init(x, init_s, init_t, d_seed, m_seed))
455
+ else:
456
+ d, m, ls, lm, codes = _range_init(x, w, cfg, legacy_1d)
457
+ if d_seed > 1.0e-30:
458
+ d = float(d_seed)
459
+ if init_s is not None:
460
+ ls = np.clip(nearest_int(np.asarray(init_s) / d), 0, 15).astype(np.uint8)
461
+ if m_seed > 1.0e-30:
462
+ m = float(m_seed)
463
+ if init_t is not None:
464
+ lm = np.clip(nearest_int(np.asarray(init_t) / m), 0, 15).astype(np.uint8)
465
+ out.append((d, m, ls, lm, codes))
466
+ if starts > 1:
467
+ out.append(_ls_init(x, w, cfg, legacy_1d))
468
+ return out
469
 
470
  best = None
471
  total_iters = 0
472
+ n_starts = 0
473
+ portfolio = cfg.factor_portfolio and (starts > 1 or max_iter > 1)
474
+ for legacy_1d in ((False, True) if portfolio else (False,)):
475
+ for d, m, ls, lm, codes in build_candidates(legacy_1d):
476
+ n_starts += 1
477
+ solve_iters = max(max_iter, 16) if legacy_1d else max_iter
478
+ d, m, ls, lm, codes, it = _solve(
479
+ x, w, cfg, d, m, ls, lm, codes, pin, use_spectral,
480
+ solve_iters, dc_carry, legacy_1d,
481
+ )
482
+ total_iters += it
483
+ d, m, d16, m16, ls, lm, codes, sse, obj = _emit_polish(
484
+ x, w, cfg, d, m, ls, lm, codes, dc_carry
 
 
 
 
 
 
 
 
485
  )
486
+ if best is None or obj < best.objective - 1.0e-15:
487
+ best = FactorResult(
488
+ d=np.float32(d),
489
+ dmin=np.float32(m),
490
+ d16=np.uint16(d16),
491
+ m16=np.uint16(m16),
492
+ ls=ls.astype(np.uint8),
493
+ lm=lm.astype(np.uint8),
494
+ codes=codes.astype(np.uint8),
495
+ sse=float(sse),
496
+ objective=float(obj),
497
+ iterations=total_iters,
498
+ starts=n_starts,
499
+ )
500
  return best
501
 
502
 
pipeline.py CHANGED
@@ -39,14 +39,12 @@ from .q2k import (
39
  codes_from_params,
40
  codes_from_params_batch,
41
  dequantize_params,
42
- dequantize_params_batch,
43
  derive_subscales,
44
  make_block,
45
  pack_codes,
46
  pack_scales,
47
  unpack_codes,
48
  weighted_sse,
49
- weighted_sse_batch,
50
  )
51
  from .seeds import build_seeds, seed_clamp_value
52
  from .sieve import block_chain_edges, sieve_measure_chain, subblock_chain_edges
@@ -144,17 +142,17 @@ def _sieve_marginals(candidate_errors, graph_blocks, stride, temperature, cfg):
144
  stop = min((gi + 1) * stride, n_blocks)
145
  group = candidate_errors[start:stop].astype(np.float64)
146
  finite = np.isfinite(group)
147
- if np.any(finite):
148
- agg = np.where(finite, group, np.nan).mean(axis=0)
149
- valid = np.isfinite(agg)
150
- mn = float(np.min(agg[valid])) if np.any(valid) else 0.0
151
- coeff = np.where(
152
- valid,
153
- np.exp(-(agg - mn) / (2.0 * max(temperature, 1.0e-12))),
154
- 0.0,
155
- )
156
- else:
157
- coeff = np.zeros(TOTAL_CANDIDATES, dtype=np.float64)
158
  for v in range(6):
159
  coarse[gi, v] = float(np.sum(coeff[qi_d == v]))
160
  fine[gi, v] = float(np.sum(coeff[qi_m == v]))
@@ -229,28 +227,23 @@ def _per_sub_top_states(x, w, d, m, k=6):
229
  wb = np.ones((NS, 16), dtype=np.float32)
230
  else:
231
  wb = np.asarray(w, dtype=np.float32).reshape(NS, 16)
232
- state_ls = np.zeros((NS, k), dtype=np.uint8)
233
- state_lm = np.zeros((NS, k), dtype=np.uint8)
234
- state_err = np.full((NS, k), np.inf, dtype=np.float64)
235
  ls_opts = np.arange(16, dtype=np.float32)[:, None]
236
  lm_opts = np.arange(16, dtype=np.float32)[None, :]
237
  d_sub = np.float32(d) * ls_opts
238
  m_sub = np.float32(m) * lm_opts
239
- safe = d_sub[:, :, None] >= np.float32(1.0e-15)
240
- safe_d = np.where(safe, d_sub[:, :, None], np.float32(1.0))
241
- for j in range(NS):
242
- sub = xb[j]
243
- wj = wb[j]
244
- raw = (sub[None, None, :] + m_sub[:, :, None]) / safe_d
245
- q = np.clip(nearest_int(raw), 0, 3).astype(np.float32)
246
- q = np.where(safe, q, 0.0)
247
- deq = d_sub[:, :, None] * q - m_sub[:, :, None]
248
- err = np.sum(wj[None, None, :] * (sub[None, None, :] - deq) ** 2, axis=2)
249
- flat = err.ravel()
250
- order = np.argsort(flat, kind="stable")[:k]
251
- state_err[j] = flat[order]
252
- state_ls[j] = (order // 16).astype(np.uint8)
253
- state_lm[j] = (order % 16).astype(np.uint8)
254
  return state_ls, state_lm, state_err
255
 
256
 
@@ -305,18 +298,28 @@ def _ulp_polish(x, w, d, m, ls, lm, radius):
305
  if radius <= 0:
306
  codes = codes_from_params(x, d, m, ls, lm)
307
  return float(d), float(m), codes, weighted_sse(x, w, d, m, ls, lm, codes)
308
- d16 = np.uint16(to_fp16_bits(np.float32(d)))
309
- m16 = np.uint16(to_fp16_bits(np.float32(m)))
310
- best = None
311
- for db in range(max(0, int(d16) - radius), min(0x7BFF, int(d16) + radius) + 1):
312
- dd = float(from_fp16_bits(np.uint16(db)))
313
- for mb in range(max(0, int(m16) - radius), min(0x7BFF, int(m16) + radius) + 1):
314
- mm = float(from_fp16_bits(np.uint16(mb)))
315
- c = codes_from_params(x, dd, mm, ls, lm)
316
- s = weighted_sse(x, w, dd, mm, ls, lm, c)
317
- if best is None or s < best[0] - 1.0e-15:
318
- best = (s, dd, mm, c)
319
- return best[1], best[2], best[3], best[0]
 
 
 
 
 
 
 
 
 
 
320
 
321
 
322
  def _per_sub_scan(x, w, d, m):
@@ -341,19 +344,36 @@ def _assemble_block(x, w, seeds, base_d, base_m, cidx, dc_carry, cfg):
341
  break
342
  prev = cur
343
 
344
- d, m, codes, _ = _ulp_polish(x, w, d, m, ls, lm, cfg.ulp_radius)
345
- ls, lm = _per_sub_scan(x, w, d, m)
346
  codes = codes_from_params(x, d, m, ls, lm)
347
- codes = refine_codes_hadamard(x, w, d, m, ls, lm, codes, cfg, dc_carry)
348
  obj, sse = objective(x, w, d, m, ls, lm, codes, cfg, dc_carry)
349
- return d, m, ls, lm, codes, obj, sse
 
 
 
 
 
350
 
 
 
 
 
 
351
 
352
- def _candidate_config(seeds, base_d, base_m, cidx):
 
 
 
 
 
 
 
 
 
 
353
  d16, m16 = candidate_pair(base_d, base_m, cidx)
354
  d = float(from_fp16_bits(d16))
355
  m = float(from_fp16_bits(m16))
356
- ls, lm = derive_subscales(seeds["scales"], seeds["mins"], d, m)
357
  return d, m, ls, lm
358
 
359
 
@@ -396,6 +416,8 @@ class Q2KQuantizer:
396
  ) -> QuantizedTensor:
397
  cfg = self.config
398
  x = np.asarray(weights, dtype=np.float32)
 
 
399
  if x.size % QK_K != 0:
400
  raise ValueError("weight count must be a multiple of 256")
401
  if x.ndim == 2 and row_width is None and x.shape[1] % QK_K == 0:
@@ -408,12 +430,14 @@ class Q2KQuantizer:
408
  imp = np.asarray(importance, dtype=np.float32).ravel()
409
  if imp.size != x.size:
410
  raise ValueError("importance must match the weight count")
 
 
411
  W = imp.reshape(n_blocks, QK_K)
412
  else:
413
  W = None
414
 
415
- seeds = build_seeds(X, W, cfg)
416
  clamp_val = seed_clamp_value(x, cfg)
 
417
  base_d, base_m = _phase2_wls(X, W, seeds, clamp_val)
418
 
419
  candidate_errors = np.full((n_blocks, TOTAL_CANDIDATES), np.inf, dtype=np.float32)
@@ -466,10 +490,7 @@ class Q2KQuantizer:
466
 
467
  if cfg.use_candidate_floor:
468
  cd, cm, cls, clm = _candidate_config(
469
- {k: v[i] for k, v in seeds.items() if k in ("scales", "mins")},
470
- base_d[i],
471
- base_m[i],
472
- best[i],
473
  )
474
  ccodes = codes_from_params(X[i], cd, cm, cls, clm)
475
  cobj, csse = objective(
 
39
  codes_from_params,
40
  codes_from_params_batch,
41
  dequantize_params,
 
42
  derive_subscales,
43
  make_block,
44
  pack_codes,
45
  pack_scales,
46
  unpack_codes,
47
  weighted_sse,
 
48
  )
49
  from .seeds import build_seeds, seed_clamp_value
50
  from .sieve import block_chain_edges, sieve_measure_chain, subblock_chain_edges
 
142
  stop = min((gi + 1) * stride, n_blocks)
143
  group = candidate_errors[start:stop].astype(np.float64)
144
  finite = np.isfinite(group)
145
+ counts = finite.sum(axis=0)
146
+ valid = counts > 0
147
+ sums = np.where(finite, group, 0.0).sum(axis=0)
148
+ agg = np.where(valid, sums / np.maximum(counts, 1), 0.0)
149
+ mn = float(np.min(agg[valid])) if np.any(valid) else 0.0
150
+ expo = np.where(
151
+ valid,
152
+ (agg - mn) / (2.0 * max(temperature, 1.0e-12)),
153
+ np.inf,
154
+ )
155
+ coeff = np.exp(-expo)
156
  for v in range(6):
157
  coarse[gi, v] = float(np.sum(coeff[qi_d == v]))
158
  fine[gi, v] = float(np.sum(coeff[qi_m == v]))
 
227
  wb = np.ones((NS, 16), dtype=np.float32)
228
  else:
229
  wb = np.asarray(w, dtype=np.float32).reshape(NS, 16)
 
 
 
230
  ls_opts = np.arange(16, dtype=np.float32)[:, None]
231
  lm_opts = np.arange(16, dtype=np.float32)[None, :]
232
  d_sub = np.float32(d) * ls_opts
233
  m_sub = np.float32(m) * lm_opts
234
+ safe = d_sub >= np.float32(1.0e-15)
235
+ safe_d = np.where(safe, d_sub, np.float32(1.0))
236
+ subs = xb[:, None, None, :]
237
+ raw = (subs + m_sub[None, :, :, None]) / safe_d[None, :, :, None]
238
+ q = np.clip(nearest_int(raw), 0, 3).astype(np.float32)
239
+ q = np.where(safe[None, :, :, None], q, 0.0)
240
+ deq = d_sub[None, :, :, None] * q - m_sub[None, :, :, None]
241
+ err = np.sum(wb[:, None, None, :] * (subs - deq) ** 2, axis=3)
242
+ flat = err.reshape(NS, 256)
243
+ order = np.argsort(flat, axis=1, kind="stable")[:, :k]
244
+ state_err = np.take_along_axis(flat, order, axis=1).astype(np.float64)
245
+ state_ls = (order // 16).astype(np.uint8)
246
+ state_lm = (order % 16).astype(np.uint8)
 
 
247
  return state_ls, state_lm, state_err
248
 
249
 
 
298
  if radius <= 0:
299
  codes = codes_from_params(x, d, m, ls, lm)
300
  return float(d), float(m), codes, weighted_sse(x, w, d, m, ls, lm, codes)
301
+ d16 = int(to_fp16_bits(np.float32(d)))
302
+ m16 = int(to_fp16_bits(np.float32(m)))
303
+ d_bits = np.arange(max(0, d16 - radius), min(0x7BFF, d16 + radius) + 1, dtype=np.uint16)
304
+ m_bits = np.arange(max(0, m16 - radius), min(0x7BFF, m16 + radius) + 1, dtype=np.uint16)
305
+ dd = from_fp16_bits(d_bits)
306
+ mm = from_fp16_bits(m_bits)
307
+ d_grid = np.repeat(dd, mm.size)
308
+ m_grid = np.tile(mm, dd.size)
309
+ ls_rep = np.repeat(np.asarray(ls, dtype=np.float32), 16)
310
+ lm_rep = np.repeat(np.asarray(lm, dtype=np.float32), 16)
311
+ d_sub = d_grid[:, None] * ls_rep[None, :]
312
+ m_sub = m_grid[:, None] * lm_rep[None, :]
313
+ safe = d_sub >= np.float32(1.0e-15)
314
+ safe_d = np.where(safe, d_sub, np.float32(1.0))
315
+ q = np.clip(nearest_int((x[None, :] + m_sub) / safe_d), 0, 3).astype(np.float32)
316
+ q = np.where(safe, q, 0.0)
317
+ deq = d_sub * q - m_sub
318
+ ww = np.ones(QK_K, dtype=np.float64) if w is None else np.asarray(w, dtype=np.float64)
319
+ err = np.sum(ww[None, :] * (np.asarray(x, dtype=np.float64)[None, :] - deq) ** 2, axis=1)
320
+ k = int(np.argmin(err))
321
+ codes = q[k].astype(np.uint8)
322
+ return float(d_grid[k]), float(m_grid[k]), codes, float(err[k])
323
 
324
 
325
  def _per_sub_scan(x, w, d, m):
 
344
  break
345
  prev = cur
346
 
 
 
347
  codes = codes_from_params(x, d, m, ls, lm)
 
348
  obj, sse = objective(x, w, d, m, ls, lm, codes, cfg, dc_carry)
349
+ best = (obj, sse, d, m, ls.copy(), lm.copy(), codes.copy())
350
+
351
+ ud, um, uc, _ = _ulp_polish(x, w, d, m, ls, lm, cfg.ulp_radius)
352
+ uobj, usse = objective(x, w, ud, um, ls, lm, uc, cfg, dc_carry)
353
+ if uobj < best[0] - 1.0e-15:
354
+ best = (uobj, usse, ud, um, ls.copy(), lm.copy(), uc.copy())
355
 
356
+ sls, slm = _per_sub_scan(x, w, ud, um)
357
+ scodes = codes_from_params(x, ud, um, sls, slm)
358
+ sobj, ssse = objective(x, w, ud, um, sls, slm, scodes, cfg, dc_carry)
359
+ if sobj < best[0] - 1.0e-15:
360
+ best = (sobj, ssse, ud, um, sls.copy(), slm.copy(), scodes.copy())
361
 
362
+ _, _, bd, bm, bls, blm, bc = best
363
+ refined = refine_codes_hadamard(x, w, bd, bm, bls, blm, bc, cfg, dc_carry)
364
+ robj, rsse = objective(x, w, bd, bm, bls, blm, refined, cfg, dc_carry)
365
+ if robj < best[0] - 1.0e-15:
366
+ best = (robj, rsse, bd, bm, bls.copy(), blm.copy(), refined.copy())
367
+
368
+ _, sse, d, m, ls, lm, codes = best
369
+ return d, m, ls, lm, codes, best[0], sse
370
+
371
+
372
+ def _candidate_config(scales, mins, base_d, base_m, cidx):
373
  d16, m16 = candidate_pair(base_d, base_m, cidx)
374
  d = float(from_fp16_bits(d16))
375
  m = float(from_fp16_bits(m16))
376
+ ls, lm = derive_subscales(scales, mins, d, m)
377
  return d, m, ls, lm
378
 
379
 
 
416
  ) -> QuantizedTensor:
417
  cfg = self.config
418
  x = np.asarray(weights, dtype=np.float32)
419
+ if x.size == 0:
420
+ raise ValueError("weight tensor is empty")
421
  if x.size % QK_K != 0:
422
  raise ValueError("weight count must be a multiple of 256")
423
  if x.ndim == 2 and row_width is None and x.shape[1] % QK_K == 0:
 
430
  imp = np.asarray(importance, dtype=np.float32).ravel()
431
  if imp.size != x.size:
432
  raise ValueError("importance must match the weight count")
433
+ if np.any(imp < 0.0) or not np.all(np.isfinite(imp)):
434
+ raise ValueError("importance must be finite and non-negative")
435
  W = imp.reshape(n_blocks, QK_K)
436
  else:
437
  W = None
438
 
 
439
  clamp_val = seed_clamp_value(x, cfg)
440
+ seeds = build_seeds(X, W, cfg, clamp_val)
441
  base_d, base_m = _phase2_wls(X, W, seeds, clamp_val)
442
 
443
  candidate_errors = np.full((n_blocks, TOTAL_CANDIDATES), np.inf, dtype=np.float32)
 
490
 
491
  if cfg.use_candidate_floor:
492
  cd, cm, cls, clm = _candidate_config(
493
+ seeds["scales"][i], seeds["mins"][i], base_d[i], base_m[i], best[i]
 
 
 
494
  )
495
  ccodes = codes_from_params(X[i], cd, cm, cls, clm)
496
  cobj, csse = objective(
seeds.py CHANGED
@@ -157,18 +157,22 @@ def seed_weights(
157
 
158
 
159
  def build_seeds(
160
- x: np.ndarray, importance: np.ndarray | None, cfg: HPCConfig
 
 
 
161
  ) -> dict[str, np.ndarray]:
162
  """Phase-1 seeds for all blocks: per-sub (scale,min) and shared (d,dmin)."""
163
  n_blocks = x.shape[0]
164
  blocks = np.asarray(x, dtype=np.float64).reshape(n_blocks, 16, 16)
 
165
  if importance is None:
166
  sigma_sq = np.mean(blocks * blocks, axis=(1, 2), keepdims=True)
167
- weights = np.sqrt(sigma_sq + blocks * blocks)
168
  else:
169
  weights = np.asarray(importance, dtype=np.float64).reshape(n_blocks, 16, 16)
170
 
171
- flat_x = blocks.reshape(-1, 16)
172
  flat_w = weights.reshape(-1, 16)
173
  scales, mins, codes = make_qkx2_batch(flat_x, flat_w, nmax=3)
174
  scales = scales.reshape(n_blocks, 16)
 
157
 
158
 
159
  def build_seeds(
160
+ x: np.ndarray,
161
+ importance: np.ndarray | None,
162
+ cfg: HPCConfig,
163
+ clamp_val: float = float("inf"),
164
  ) -> dict[str, np.ndarray]:
165
  """Phase-1 seeds for all blocks: per-sub (scale,min) and shared (d,dmin)."""
166
  n_blocks = x.shape[0]
167
  blocks = np.asarray(x, dtype=np.float64).reshape(n_blocks, 16, 16)
168
+ clipped = np.clip(blocks, -clamp_val, clamp_val)
169
  if importance is None:
170
  sigma_sq = np.mean(blocks * blocks, axis=(1, 2), keepdims=True)
171
+ weights = np.sqrt(sigma_sq + clipped * clipped)
172
  else:
173
  weights = np.asarray(importance, dtype=np.float64).reshape(n_blocks, 16, 16)
174
 
175
+ flat_x = clipped.reshape(-1, 16)
176
  flat_w = weights.reshape(-1, 16)
177
  scales, mins, codes = make_qkx2_batch(flat_x, flat_w, nmax=3)
178
  scales = scales.reshape(n_blocks, 16)
spectral.py CHANGED
@@ -13,27 +13,37 @@ D6/vesica greedy code descent.
13
 
14
  from __future__ import annotations
15
 
 
 
16
  import numpy as np
17
 
18
  from .config import HPCConfig
19
- from .q2k import QK_K, dequantize_params, weighted_sse
20
 
21
 
22
- def fold_weights(n: int, cfg: HPCConfig) -> np.ndarray:
 
23
  levels = int(np.log2(n))
24
  if (1 << levels) != n:
25
  raise ValueError("fold pyramid requires a power-of-two length")
26
  weights = np.zeros(levels, dtype=np.float64)
27
- gamma = 1.0
28
  for k in range(1, levels + 1):
29
  if k == levels:
30
- weights[k - 1] = cfg.dc_lambda / n
31
- elif k <= cfg.fold_depth:
32
- weights[k - 1] = (cfg.vw_lambda / n) * gamma
33
- gamma *= cfg.fold_gamma
 
34
  return weights
35
 
36
 
 
 
 
 
 
 
37
  def fold_build(errors: np.ndarray) -> np.ndarray:
38
  e = np.asarray(errors, dtype=np.float64)
39
  n = e.size
 
13
 
14
  from __future__ import annotations
15
 
16
+ from functools import lru_cache
17
+
18
  import numpy as np
19
 
20
  from .config import HPCConfig
21
+ from .q2k import QK_K, dequantize_params
22
 
23
 
24
+ @lru_cache(maxsize=64)
25
+ def _fold_weights_cached(n: int, dc_lambda: float, vw_lambda: float, depth: int, gamma: float) -> np.ndarray:
26
  levels = int(np.log2(n))
27
  if (1 << levels) != n:
28
  raise ValueError("fold pyramid requires a power-of-two length")
29
  weights = np.zeros(levels, dtype=np.float64)
30
+ g = 1.0
31
  for k in range(1, levels + 1):
32
  if k == levels:
33
+ weights[k - 1] = dc_lambda / n
34
+ elif k <= depth:
35
+ weights[k - 1] = (vw_lambda / n) * g
36
+ g *= gamma
37
+ weights.setflags(write=False)
38
  return weights
39
 
40
 
41
+ def fold_weights(n: int, cfg: HPCConfig) -> np.ndarray:
42
+ return _fold_weights_cached(
43
+ n, cfg.dc_lambda, cfg.vw_lambda, cfg.fold_depth, cfg.fold_gamma
44
+ )
45
+
46
+
47
  def fold_build(errors: np.ndarray) -> np.ndarray:
48
  e = np.asarray(errors, dtype=np.float64)
49
  n = e.size
viterbi.py CHANGED
@@ -76,16 +76,17 @@ def viterbi_select(
76
  stop = min((gi + 1) * stride, n_blocks)
77
  group = candidate_errors[start:stop].astype(np.float64)
78
  flat = group.ravel()
 
79
  finite = np.isfinite(flat)
80
  group_min = np.full(N_STATES, np.inf)
81
  if np.any(finite):
82
- np.minimum.at(group_min, bins[finite], flat[finite])
83
- counts = np.bincount(bins[finite], minlength=N_STATES)
84
  sums = np.zeros(N_STATES, dtype=np.float64)
85
  if np.any(finite):
86
- shifted = flat[finite] - group_min[bins[finite]]
87
  amps = np.exp(-shifted / (2.0 * temp))
88
- sums = np.bincount(bins[finite], weights=amps, minlength=N_STATES)
89
  valid = (counts > 0) & np.isfinite(group_min)
90
  if np.any(valid):
91
  mean_exp = np.maximum(sums[valid] / counts[valid], 1.0e-300)
@@ -140,7 +141,7 @@ def viterbi_select(
140
  for gi in range(graph_blocks - 2, -1, -1):
141
  path[gi] = back[gi + 1, path[gi + 1]]
142
 
143
- best_candidate = np.zeros(n_blocks, dtype=np.int64)
144
  for gi in range(graph_blocks):
145
  s = int(path[gi])
146
  qd, qm = s // 6, s % 6
 
76
  stop = min((gi + 1) * stride, n_blocks)
77
  group = candidate_errors[start:stop].astype(np.float64)
78
  flat = group.ravel()
79
+ bins_flat = np.tile(bins, group.shape[0])
80
  finite = np.isfinite(flat)
81
  group_min = np.full(N_STATES, np.inf)
82
  if np.any(finite):
83
+ np.minimum.at(group_min, bins_flat[finite], flat[finite])
84
+ counts = np.bincount(bins_flat[finite], minlength=N_STATES)
85
  sums = np.zeros(N_STATES, dtype=np.float64)
86
  if np.any(finite):
87
+ shifted = flat[finite] - group_min[bins_flat[finite]]
88
  amps = np.exp(-shifted / (2.0 * temp))
89
+ sums = np.bincount(bins_flat[finite], weights=amps, minlength=N_STATES)
90
  valid = (counts > 0) & np.isfinite(group_min)
91
  if np.any(valid):
92
  mean_exp = np.maximum(sums[valid] / counts[valid], 1.0e-300)
 
141
  for gi in range(graph_blocks - 2, -1, -1):
142
  path[gi] = back[gi + 1, path[gi + 1]]
143
 
144
+ best_candidate = np.argmin(candidate_errors, axis=1).astype(np.int64)
145
  for gi in range(graph_blocks):
146
  s = int(path[gi])
147
  qd, qm = s // 6, s % 6