"""Thermal refinery: seeded pair heat-bath annealing on the Q2_K lattice. With ``fold_depth == 1`` the extended objective for one block is exactly:: E = sum_i w_i e_i^2 + (vw_lambda / n) * sum_p (e_p + e_{p+n/2})^2 + (dc_lambda / n) * (sum_i e_i + carry)^2 so the 256 weights decompose into n/2 vesica pairs that interact with each other only through the scalar DC residual: with every error affine in its code, each pair's conditional energy over its joint states is exactly quadratic and closed-form. ``gibbs_pair_pass`` resamples the pairs from those conditionals at a temperature (heat bath), fully batched across blocks over any uniform lattice size (``levels``: 4 = Q2_K, 16 = Q4_0, 2 = Q1_0, 8 = Q3_K K-quants). ``greedy=True`` resamples at the conditional argmin instead, which is a monotone descent. ``thermal_refine_codes`` / ``thermal_refine_uniform_codes`` schedule temperatures, then keep the elementwise best against the pre-anneal state, so the pass can never regress a block. All randomness comes from a seeded generator, and a fixed seed reproduces bit-identical quantizations. """ from __future__ import annotations import numpy as np from .config import HPCConfig from .fp16 import nearest_int from .q2k import QK_K EPS_D = 1.0e-15 def thermal_spectral_lams(cfg: HPCConfig, n: int) -> tuple[float, float]: """Per-code-normalized ``(lam_vw, lam_dc)`` with the zero-residual tilt applied. ``therm_lambda_tilt`` scales both spectral lambdas during annealing so the exact Gibbs chain trades weighted SSE for coherent-residual (DC/vesica) drain; ``1.0`` (default) is the shipping objective. """ if not cfg.use_spectral: return 0.0, 0.0 tilt = max(1.0, float(cfg.therm_lambda_tilt)) return (float(cfg.vw_lambda) / n) * tilt, (float(cfg.dc_lambda) / n) * tilt def pair_energy( e: np.ndarray, w: np.ndarray | None, lam_vw: float, lam_dc: float, carry: np.ndarray, ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray]: """Block energies plus the (sse, v, dc) live state for exact deltas.""" half = e.shape[1] // 2 ww = np.ones_like(e) if w is None else np.asarray(w, dtype=np.float64) we_sq = ww * e * e sse = np.sum(we_sq, axis=1) v = e[:, :half] + e[:, half:] dc = np.sum(e, axis=1) + np.asarray(carry, dtype=np.float64) energy = sse + lam_vw * np.sum(v * v, axis=1) + lam_dc * dc * dc return energy, we_sq, v, dc def gibbs_pair_pass( X_f64: np.ndarray, w_full: np.ndarray | None, d_sub: np.ndarray, m_sub: np.ndarray, codes: np.ndarray, e: np.ndarray, v: np.ndarray, dc: np.ndarray, sse: np.ndarray, temperature: np.ndarray, lam_vw: float, lam_dc: float, rng: np.random.Generator, greedy: bool = False, levels: int = 4, ) -> None: """One exact sequential Gibbs sweep over all vesica pairs, in place. Updates (codes, e, v, dc, sse) for the batch of blocks. With ``greedy=False`` each pair is resampled from its exact conditional energy via a heat bath at ``temperature`` (per-block vector); with ``greedy=True`` the conditional argmin is taken and only strict energy decreases are accepted. ``levels`` selects the code lattice size (4 = Q2_K 2-bit, 16 = Q4_0 nibble, 2 = Q1_0 bit, 8 = Q3_K tri); ``d_sub`` holds the per-column error step so that any code move ``q`` produces the exact error delta ``d_sub * (q_cur - q)`` (formula is shared across formats; Q1_0's sign trick is ``d_sub = 2*d``), and ``m_sub`` is accepted for signature symmetry but unused here. """ n_blocks, n = X_f64.shape half = n // 2 q_grid = np.arange(levels, dtype=np.int64) w_full = np.ones_like(e) if w_full is None else np.asarray(w_full, dtype=np.float64) w_lo, w_hi = w_full[:, :half], w_full[:, half:] ds_lo, ds_hi = d_sub[:, :half], d_sub[:, half:] active_lo, active_hi = np.abs(ds_lo) >= EPS_D, np.abs(ds_hi) >= EPS_D idx = np.arange(n_blocks) for p in range(half): e_lo = e[:, p].copy() e_hi = e[:, half + p].copy() q_lo = codes[:, p].astype(np.int64) q_hi = codes[:, half + p].astype(np.int64) v_p = v[:, p] dc_p = dc # candidate error deltas (affine in q): de(q) = d_sub * (q_cur - q) de_lo = ds_lo[:, p : p + 1] * (q_lo[:, None] - q_grid[None, :]) de_hi = ds_hi[:, p : p + 1] * (q_hi[:, None] - q_grid[None, :]) de_lo = np.where(active_lo[:, p : p + 1], de_lo, 0.0) de_hi = np.where(active_hi[:, p : p + 1], de_hi, 0.0) de_sum = de_lo[:, :, None] + de_hi[:, None, :] # (n_blocks, L, L) d_e = ( w_lo[:, p, None, None] * (2.0 * e_lo[:, None, None] * de_lo[:, :, None] + de_lo[:, :, None] ** 2) + w_hi[:, p, None, None] * (2.0 * e_hi[:, None, None] * de_hi[:, None, :] + de_hi[:, None, :] ** 2) + lam_vw * (2.0 * v_p[:, None, None] * de_sum + de_sum * de_sum) + lam_dc * (2.0 * dc_p[:, None, None] * de_sum + de_sum * de_sum) ) if greedy: flat = d_e.reshape(n_blocks, -1) cur = q_lo * levels + q_hi pick = np.argmin(flat, axis=1) improve = flat[idx, pick] < -1.0e-18 pick = np.where(improve, pick, cur) else: cur = q_lo * levels + q_hi scale = np.maximum(temperature, 1.0e-300) logits = -d_e / scale[:, None, None] logits -= logits.max(axis=(1, 2), keepdims=True) probs = np.exp(logits).reshape(n_blocks, -1) # dead pairs (both sub-scales ~0): codes carry no energy, keep current dead = ~(active_lo[:, p] | active_hi[:, p]) if np.any(dead): probs[dead] = 0.0 probs[dead, cur[dead]] = 1.0 cum = np.cumsum(probs, axis=1) cum /= cum[:, -1:] pick = np.sum(cum < rng.random((n_blocks, 1)), axis=1) pick = np.minimum(pick, levels * levels - 1) ia, ib = pick // levels, pick % levels upd_lo = np.take_along_axis(de_lo, ia[:, None], axis=1)[:, 0] upd_hi = np.take_along_axis(de_hi, ib[:, None], axis=1)[:, 0] upd_sum = upd_lo + upd_hi sse += ( w_lo[:, p] * (2.0 * e_lo * upd_lo + upd_lo * upd_lo) + w_hi[:, p] * (2.0 * e_hi * upd_hi + upd_hi * upd_hi) ) e[:, p] = e_lo + upd_lo e[:, half + p] = e_hi + upd_hi v[:, p] = v_p + upd_sum dc = dc + upd_sum codes[:, p] = ia codes[:, half + p] = ib def codes_recompute_for( X_f64: np.ndarray, d: np.ndarray, m: np.ndarray, ls: np.ndarray, lm: np.ndarray ) -> np.ndarray: """Nearest rounding of codes, matching q2k.codes_from_params semantics.""" x32 = X_f64.astype(np.float32) d32 = np.asarray(d, dtype=np.float32) m32 = np.asarray(m, dtype=np.float32) d_sub = np.repeat(d32[:, None] * ls.astype(np.float32), 16, axis=1) m_sub = np.repeat(m32[:, None] * lm.astype(np.float32), 16, axis=1) safe = d_sub >= np.float32(EPS_D) safe_d = np.where(safe, d_sub, np.float32(1.0)) q = np.clip(nearest_int((x32 + m_sub) / safe_d), 0, 3) return np.where(safe, q, 0).astype(np.int64) def thermal_refine_codes( X: np.ndarray, w: np.ndarray | None, d: np.ndarray, m: np.ndarray, ls: np.ndarray, lm: np.ndarray, codes: np.ndarray, carry: np.ndarray, cfg: HPCConfig, dm_solve=None, passes: int | None = None, seed: int | None = None, ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, int]: """Seeded heat-bath annealing of the code lattice for a batch of blocks. Returns ``(d, m, ls, lm, codes, n_improved)``. Per block the returned state never has a higher extended energy than the input state: every end-of-pass candidate is compared against the running elementwise best and only strict decreases are kept. """ n_blocks = X.shape[0] n_passes = cfg.therm_passes if passes is None else int(passes) lam_vw, lam_dc = thermal_spectral_lams(cfg, QK_K) if n_blocks == 0 or n_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0): return d, m, ls, lm, codes, 0 if cfg.fold_depth > 1: raise ValueError("thermal refinement supports fold_depth <= 1") X_f64 = np.asarray(X, dtype=np.float64) w_f64 = None if w is None else np.asarray(w, dtype=np.float64) carry_f64 = np.asarray(carry, dtype=np.float64) ls = np.asarray(ls, dtype=np.int64) lm = np.asarray(lm, dtype=np.int64) d = np.asarray(d, dtype=np.float64) m = np.asarray(m, dtype=np.float64) codes = np.asarray(codes, dtype=np.int64).copy() rng = np.random.default_rng(cfg.therm_seed if seed is None else seed) def energies(dd, mm, cc): d_sub = np.repeat(dd[:, None] * ls, 16, axis=1) m_sub = np.repeat(mm[:, None] * lm, 16, axis=1) e = X_f64 - (d_sub * cc - m_sub) return pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64)[0] energy_best = energies(d, m, codes) energy0 = energy_best.copy() best_d, best_m, best_codes = d.copy(), m.copy(), codes.copy() d_cur, m_cur, codes_cur = d.copy(), m.copy(), codes.copy() for t in range(n_passes): temperature = cfg.therm_t0 * np.maximum(energy_best, 1.0e-30) / QK_K * (cfg.therm_t_decay**t) for sweep in range(cfg.therm_sweeps + 2): greedy = sweep >= cfg.therm_sweeps d_sub = np.repeat(d_cur[:, None] * ls, 16, axis=1) m_sub = np.repeat(m_cur[:, None] * lm, 16, axis=1) e = X_f64 - (d_sub * codes_cur - m_sub) energy_cur, wesq, v, dc = pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64) sse = energy_cur - lam_vw * np.sum(v * v, axis=1) - lam_dc * dc * dc gibbs_pair_pass( X_f64, w_f64, d_sub, m_sub, codes_cur, e, v, dc, sse, temperature, lam_vw, lam_dc, np.random.default_rng(rng.integers(0, 2**62)), greedy=greedy, ) if greedy: energy_now, _, _, _ = pair_energy( X_f64 - (d_sub * codes_cur - m_sub), w_f64, lam_vw, lam_dc, carry_f64 ) if not np.any(energy_now < energy_cur - 1.0e-18): break codes_cur = np.clip(codes_cur, 0, 3) energy_a = energies(d_cur, m_cur, codes_cur) if dm_solve is not None: dm_w = w_f64 if w_f64 is not None else np.ones_like(X_f64) d2, m2 = dm_solve(X_f64, dm_w, ls, lm, codes_cur, d_cur, m_cur, carry_f64, cfg) codes2 = codes_recompute_for(X_f64, d2, m2, ls, lm) energy_b = energies(d2, m2, codes2) take_b = energy_b < energy_a - 1.0e-18 d_cur = np.where(take_b, d2, d_cur) m_cur = np.where(take_b, m2, m_cur) codes_cur = np.where(take_b[:, None], codes2, codes_cur) energy_now = energies(d_cur, m_cur, codes_cur) take = energy_now < energy_best - 1.0e-18 best_d = np.where(take, d_cur, best_d) best_m = np.where(take, m_cur, best_m) best_codes = np.where(take[:, None], codes_cur, best_codes) energy_best = np.where(take, energy_now, energy_best) n_improved = int(np.sum(energy_best < energy0 - 1.0e-18)) return best_d, best_m, ls.copy(), lm.copy(), best_codes.astype(np.uint8), n_improved def thermal_refine_tensor( X: np.ndarray, w: np.ndarray | None, d: np.ndarray, m: np.ndarray, ls: np.ndarray, lm: np.ndarray, codes: np.ndarray, carry: np.ndarray, cfg: HPCConfig, dm_solve=None, chunk: int = 2048, progress=None, ) -> tuple[np.ndarray, np.ndarray, np.ndarray, np.ndarray, np.ndarray, int]: """Chunked wrapper so peak memory stays bounded on large tensors.""" n_blocks = X.shape[0] step = max(1, int(chunk)) total_improved = 0 for start in range(0, n_blocks, step): stop = min(start + step, n_blocks) sel = slice(start, stop) d_c, m_c, ls_c, lm_c, c_c, improved = thermal_refine_codes( X[sel], None if w is None else w[sel], d[sel], m[sel], ls[sel], lm[sel], codes[sel], carry[sel], cfg, dm_solve=dm_solve, seed=cfg.therm_seed ^ (0x9E3779B97F4A7C15 * (start // step) & 0x3FFFFFFFFFFFFFFF), ) d[sel], m[sel], ls[sel], lm[sel], codes[sel] = d_c, m_c, ls_c, lm_c, c_c total_improved += improved if progress is not None: progress(stop, n_blocks, improved) return d, m, ls, lm, codes, total_improved def thermal_refine_uniform_codes( X: np.ndarray, w: np.ndarray | None, codes: np.ndarray, step: np.ndarray, m_const: np.ndarray, carry: np.ndarray, cfg: HPCConfig, lam_vw: float, lam_dc: float, levels: int, passes: int | None = None, seed: int | None = None, ) -> tuple[np.ndarray, int]: """Heat-bath annealing of a level-index code lattice with FIXED step scales. Uniform variant of ``thermal_refine_codes`` for the format quantizers (Q4_0 / Q1_0 / Q3_K): the per-code error step ``step`` (shape (B, n)) and the affine constant ``m_const`` (shape (B, n)) are fixed inputs — the dequantized value of level index ``q`` is ``step*q - m_const`` so the error is ``e = X - (step*q - m_const)`` and every move ``q -> q'`` has the exact closed-form delta again affine in the level grid. ``codes`` are integer level indices in ``[0, levels)``. Returns ``(codes, n_improved)``. Per block the returned state never has a higher extended energy than the input state and its SSE never exceeds the input SSE by more than ``cfg.sse_budget`` (extended-nonincreasing with an SSE guard, matching the pipeline's shaper semantics). """ n_blocks, n = X.shape if n_blocks == 0: return codes.astype(np.int64).copy(), 0 if n % 2 != 0: raise ValueError("thermal refinement needs an even row width") n_passes = cfg.therm_passes if passes is None else int(passes) if n_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0): return codes.astype(np.int64).copy(), 0 if cfg.fold_depth > 1: raise ValueError("thermal refinement supports fold_depth <= 1") X_f64 = np.asarray(X, dtype=np.float64) w_f64 = None if w is None else np.asarray(w, dtype=np.float64) carry_f64 = np.asarray(carry, dtype=np.float64) step = np.asarray(step, dtype=np.float64) m_b = np.asarray(m_const, dtype=np.float64) codes = np.asarray(codes, dtype=np.int64).copy() rng = np.random.default_rng(cfg.therm_seed if seed is None else seed) def energies(cc): e = X_f64 - (step * cc - m_b) return pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64) energy_best, we_sq, v0, dc0 = energies(codes) energy0 = energy_best.copy() sse0 = np.sum(we_sq, axis=1) sse_cap = sse0 * (1.0 + float(cfg.sse_budget)) best_codes = codes.copy() codes_cur = codes.copy() for t in range(n_passes): temperature = cfg.therm_t0 * np.maximum(energy_best, 1.0e-30) / n * (cfg.therm_t_decay**t) for sweep in range(cfg.therm_sweeps + 2): greedy = sweep >= cfg.therm_sweeps e = X_f64 - (step * codes_cur - m_b) energy_cur, we_sq, v, dc = pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64) sse = np.sum(we_sq, axis=1) gibbs_pair_pass( X_f64, w_f64, step, m_b, codes_cur, e, v, dc, sse, temperature, lam_vw, lam_dc, np.random.default_rng(rng.integers(0, 2**62)), greedy=greedy, levels=levels, ) if greedy: e = X_f64 - (step * codes_cur - m_b) energy_now, _, _, _ = pair_energy(e, w_f64, lam_vw, lam_dc, carry_f64) if not np.any(energy_now < energy_cur - 1.0e-18): break energy_now, we_sq, v_now, dc_now = energies(codes_cur) sse_now = np.sum(we_sq, axis=1) take = (energy_now < energy_best - 1.0e-18) & (sse_now <= sse_cap) best_codes = np.where(take[:, None], codes_cur, best_codes) energy_best = np.where(take, energy_now, energy_best) codes_cur = best_codes.copy() n_improved = int(np.sum(energy_best < energy0 - 1.0e-18)) return best_codes, n_improved def _refine_uniform_driver( X: np.ndarray, w: np.ndarray | None, codes: np.ndarray, build_state, carry: np.ndarray | None, lam_vw: float, lam_dc: float, levels: int, cfg: HPCConfig, chunk: int = 2048, progress=None, ) -> tuple[np.ndarray, int]: """Chunked uniform annealing so peak memory stays bounded on big tensors.""" n_blocks = X.shape[0] codes_out = np.asarray(codes, dtype=np.int64).copy() step = max(1, int(chunk)) total_improved = 0 for start in range(0, n_blocks, step): stop = min(start + step, n_blocks) sel = slice(start, stop) step_c, m_c = build_state(start, stop) carry_c = np.zeros(stop - start) if carry is None else carry[sel] c_c, improved = thermal_refine_uniform_codes( X[sel], None if w is None else w[sel], codes_out[sel], step_c, m_c, carry_c, cfg, lam_vw, lam_dc, levels, seed=cfg.therm_seed ^ (0x9E3779B97F4A7C15 * (start // step) & 0x3FFFFFFFFFFFFFFF), ) codes_out[sel] = c_c total_improved += improved if progress is not None: progress(stop, n_blocks, improved) return codes_out, total_improved def thermal_refine_q4_0( X: np.ndarray, W: np.ndarray | None, d: np.ndarray, q: np.ndarray, cfg: HPCConfig, chunk: int = 2048, progress=None, ) -> tuple[np.ndarray, int]: """Anneal Q4_0 nibble codes against fixed fp16 super-block scales. ``X``: raw block targets (B, 32); ``W``: importance weights (B, 32) or None; ``d``: fp16-precision block scales (B,), possibly negative (sign-flip polish outputs); ``q``: nibble codes (B, 32) with dequantization ``d*(q-8)``. The Gibbs step grid is the signed slope ``d`` itself, so negatives fall out naturally with no code remapping. Returns refined uint8 codes and the number of strictly improved blocks; a no-op when the refinery or spectrals are disabled. """ n_blocks, n = X.shape lam_vw, lam_dc = thermal_spectral_lams(cfg, n) if n_blocks == 0 or cfg.therm_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0): return q.copy(), 0 d64 = np.asarray(d, dtype=np.float64) def build_state(start, stop): ds = np.repeat(d64[start:stop][:, None], n, axis=1) return ds, 8.0 * ds codes_out, improved = _refine_uniform_driver( X, W, np.asarray(q, dtype=np.int64), build_state, None, lam_vw, lam_dc, 16, cfg, chunk=chunk, progress=progress, ) return codes_out.astype(np.uint8), improved def thermal_refine_q1_0( X: np.ndarray, W: np.ndarray | None, d: np.ndarray, bits: np.ndarray, cfg: HPCConfig, chunk: int = 2048, progress=None, ) -> tuple[np.ndarray, int]: """Anneal Q1_0 bits against fixed fp16 super-block magnitudes. ``bits``: binary codes (B, 32) with dequantization ``d*(2b-1)``; the Gibbs step grid is ``2*d`` with affine constant ``d``. Returns refined uint8 bits and the number of strictly improved blocks. """ n_blocks, n = X.shape lam_vw, lam_dc = thermal_spectral_lams(cfg, n) if n_blocks == 0 or cfg.therm_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0): return bits.copy(), 0 d64 = np.asarray(d, dtype=np.float64) def build_state(start, stop): ds = np.repeat(2.0 * np.abs(d64[start:stop])[:, None], n, axis=1) return ds, np.repeat(np.abs(d64[start:stop])[:, None], n, axis=1) codes_out, improved = _refine_uniform_driver( X, W, np.asarray(bits, dtype=np.int64), build_state, None, lam_vw, lam_dc, 2, cfg, chunk=chunk, progress=progress, ) return codes_out.astype(np.uint8), improved def thermal_refine_q3_k( X: np.ndarray, W: np.ndarray | None, scale: np.ndarray, codes: np.ndarray, cfg: HPCConfig, chunk: int = 2048, progress=None, ) -> tuple[np.ndarray, int]: """Anneal Q3_K signed 3-bit codes against fixed per-superblock scales. ``scale``: per-superblock scale values (B, 16) broadcast over the 16 codes each; ``codes``: source-signed values (B, 256) in [-4, 3]. The Gibbs grid covers all eight signed levels; negative scales (sign-flip polish outputs) are handled exactly via signed steps. """ n_blocks, n = X.shape lam_vw, lam_dc = thermal_spectral_lams(cfg, n) if n_blocks == 0 or cfg.therm_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0): return np.asarray(codes).copy(), 0 scale64 = np.asarray(scale, dtype=np.float64) def build_state(start, stop): ds = np.repeat(scale64[start:stop], 16, axis=1) return ds, 4.0 * ds codes_out, improved = _refine_uniform_driver( X, W, np.asarray(codes, dtype=np.int64) + 4, build_state, None, lam_vw, lam_dc, 8, cfg, chunk=chunk, progress=progress, ) return codes_out - 4, improved def thermal_refine_uniform_codes_cuda( X: "torch.Tensor", w: "torch.Tensor | None", codes: "torch.Tensor", step: "torch.Tensor", m_const: "torch.Tensor", carry: "torch.Tensor | None", lam_vw: float, lam_dc: float, levels: int, cfg: HPCConfig, passes: int | None = None, seed: int | None = None, ) -> tuple["torch.Tensor", int]: """Torch/CUDA sibling of ``thermal_refine_uniform_codes``. Identical algorithm and acceptance structure (extended-nonincreasing best-state with an ``sse_budget`` guard, seeded heat-bath sweeps plus a greedy endgame), run on-device in float64 so results are deterministic per device class (bit-exact across runs at the same seed, but not bit-identical to the numpy CPU chain — floating-point ordering differs). All tensor inputs live on one CUDA device: ``codes`` int64 (B, n), the rest float32/float64. Returns ``(codes, n_improved)`` with ``codes`` still on-device. """ import torch # local import keeps anneal.py torch-free for CPU installs dev = X.device f64 = torch.float64 B, n = X.shape half = n // 2 n_passes = cfg.therm_passes if passes is None else int(passes) if B == 0 or n % 2 != 0: return codes.to(torch.int64), 0 if n_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0): return codes.to(torch.int64).clone(), 0 if cfg.fold_depth > 1: raise ValueError("thermal refinement supports fold_depth <= 1") Xf = X.to(f64) wf = None if w is None else w.to(f64) cf = None if carry is None else carry.to(f64) if cf is None: cf = torch.zeros(B, dtype=f64, device=dev) sf = step.to(f64) mf_data = m_const.to(f64) cc = codes.to(torch.int64).clone() grid = torch.arange(levels, device=dev, dtype=torch.int64) ones_col = torch.ones(B, dtype=f64, device=dev) def energies(c): e = Xf - (sf * c.to(f64) - mf_data) w2 = torch.sum(e * e, dim=1) if wf is None else torch.sum(wf * e * e, dim=1) v = e[:, :half] + e[:, half:] dc = torch.sum(e, dim=1) + cf return (w2 + lam_vw * torch.sum(v * v, dim=1) + lam_dc * dc * dc), e, w2, v, dc energy_best, e, sse0, v, dc = energies(cc) energy0 = energy_best.clone() sse_cap = sse0 * (1.0 + float(cfg.sse_budget)) best = cc.clone() gen = torch.Generator(device=dev) gen.manual_seed(int(cfg.therm_seed if seed is None else seed) & 0x7FFFFFFFFFFFFFFF) for t in range(n_passes): temperature = ( cfg.therm_t0 * torch.maximum(energy_best, torch.full_like(energy_best, 1.0e-30)) / n * (cfg.therm_t_decay ** t) ) broke = False for sweep in range(cfg.therm_sweeps + 2): greedy = sweep >= cfg.therm_sweeps energy_cur, e, _, v, dc = energies(cc) for p in range(half): e_lo = e[:, p] e_hi = e[:, half + p] q_lo = cc[:, p] q_hi = cc[:, half + p] ds_lo = sf[:, p] ds_hi = sf[:, half + p] act_lo = ds_lo.abs() >= EPS_D act_hi = ds_hi.abs() >= EPS_D w_lo = ones_col if wf is None else wf[:, p] w_hi = ones_col if wf is None else wf[:, half + p] de_lo = ds_lo[:, None] * (q_lo[:, None] - grid[None, :]) de_lo = torch.where(act_lo[:, None], de_lo, torch.zeros_like(de_lo)) de_hi = ds_hi[:, None] * (q_hi[:, None] - grid[None, :]) de_hi = torch.where(act_hi[:, None], de_hi, torch.zeros_like(de_hi)) de_sum = de_lo[:, :, None] + de_hi[:, None, :] v_p = v[:, p] d_e = ( w_lo[:, None, None] * (2.0 * e_lo[:, None, None] * de_lo[:, :, None] + de_lo[:, :, None] ** 2) + w_hi[:, None, None] * (2.0 * e_hi[:, None, None] * de_hi[:, None, :] + de_hi[:, None, :] ** 2) + lam_vw * (2.0 * v_p[:, None, None] * de_sum + de_sum * de_sum) + lam_dc * (2.0 * dc[:, None, None] * de_sum + de_sum * de_sum) ) cur = q_lo * levels + q_hi if greedy: flat = d_e.reshape(B, -1) pick = flat.argmin(dim=1) improve = torch.gather(flat, 1, pick[:, None]).squeeze(1) < -1.0e-18 pick = torch.where(improve, pick, cur) else: scc = torch.clamp(temperature, min=1.0e-300)[:, None, None] logits = -d_e / scc logits = logits - logits.amax(dim=(1, 2), keepdim=True) probs = logits.exp().reshape(B, -1) dead = ~(act_lo | act_hi) if bool(dead.any()): probs[dead] = 0.0 probs[dead, cur[dead]] = 1.0 cum = probs.cumsum(dim=1) cum = cum / cum[:, -1:] u = torch.rand(B, 1, generator=gen, device=dev, dtype=f64) pick = (cum < u).sum(dim=1).clamp(max=levels * levels - 1) ia = pick // levels ib = pick - ia * levels upd_lo = torch.gather(de_lo, 1, ia[:, None]).squeeze(1) upd_hi = torch.gather(de_hi, 1, ib[:, None]).squeeze(1) usum = upd_lo + upd_hi e[:, p] = e_lo + upd_lo e[:, half + p] = e_hi + upd_hi v[:, p] += usum dc = dc + usum cc[:, p] = ia cc[:, half + p] = ib if greedy: energy_now, _, _, _, _ = energies(cc) if not bool(torch.any(energy_now < energy_cur - 1.0e-18)): broke = True break _, _, sse_now, _, _ = energies(cc) take = (energy_now < energy_best - 1.0e-18) & (sse_now <= sse_cap) best = torch.where(take[:, None], cc, best) energy_best = torch.where(take, energy_now, energy_best) cc = best.clone() if broke and bool(torch.all(energy_now >= energy_best)): pass # keep exact CPU-loop semantics: continue across passes regardless n_improved = int((energy_best < energy0 - 1.0e-18).sum().item()) return best, n_improved def thermal_refine_q3_k_cuda( X: "torch.Tensor", W: "torch.Tensor | None", scale: "torch.Tensor", codes: "torch.Tensor", cfg: HPCConfig, seed: int | None = None, ) -> tuple["torch.Tensor", int]: """On-device anneal for Q3_K signed 3-bit codes (see the CPU twin). ``scale``: per-superblock scale values (B, 16) on-device, ``codes``: signed values (B, 256) in [-4, 3]. Exactly mirrors ``thermal_refine_q3_k`` without any host round-trip. """ import torch n_blocks, n = X.shape lam_vw, lam_dc = thermal_spectral_lams(cfg, n) if n_blocks == 0 or cfg.therm_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0): return codes.to(torch.int64).clone(), 0 def build_state(start, stop): ds_full = scale.repeat_interleave(16, dim=1) # (B, 256) return ds_full, 4.0 * ds_full ds_full, m_full = build_state(0, n_blocks) codes_out, improved = thermal_refine_uniform_codes_cuda( X, W, codes.to(torch.int64) + 4, ds_full, m_full, None, lam_vw, lam_dc, 8, cfg, seed=seed, ) return codes_out - 4, improved