Upload 5 files
Browse files
anneal.py
CHANGED
|
@@ -563,3 +563,180 @@ def thermal_refine_q3_k(
|
|
| 563 |
lam_vw, lam_dc, 8, cfg, chunk=chunk, progress=progress,
|
| 564 |
)
|
| 565 |
return codes_out - 4, improved
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 563 |
lam_vw, lam_dc, 8, cfg, chunk=chunk, progress=progress,
|
| 564 |
)
|
| 565 |
return codes_out - 4, improved
|
| 566 |
+
|
| 567 |
+
|
| 568 |
+
def thermal_refine_uniform_codes_cuda(
|
| 569 |
+
X: "torch.Tensor",
|
| 570 |
+
w: "torch.Tensor | None",
|
| 571 |
+
codes: "torch.Tensor",
|
| 572 |
+
step: "torch.Tensor",
|
| 573 |
+
m_const: "torch.Tensor",
|
| 574 |
+
carry: "torch.Tensor | None",
|
| 575 |
+
lam_vw: float,
|
| 576 |
+
lam_dc: float,
|
| 577 |
+
levels: int,
|
| 578 |
+
cfg: HPCConfig,
|
| 579 |
+
passes: int | None = None,
|
| 580 |
+
seed: int | None = None,
|
| 581 |
+
) -> tuple["torch.Tensor", int]:
|
| 582 |
+
"""Torch/CUDA sibling of ``thermal_refine_uniform_codes``.
|
| 583 |
+
|
| 584 |
+
Identical algorithm and acceptance structure (extended-nonincreasing
|
| 585 |
+
best-state with an ``sse_budget`` guard, seeded heat-bath sweeps plus a
|
| 586 |
+
greedy endgame), run on-device in float64 so results are deterministic
|
| 587 |
+
per device class (bit-exact across runs at the same seed, but not
|
| 588 |
+
bit-identical to the numpy CPU chain — floating-point ordering differs).
|
| 589 |
+
|
| 590 |
+
All tensor inputs live on one CUDA device: ``codes`` int64 (B, n),
|
| 591 |
+
the rest float32/float64. Returns ``(codes, n_improved)`` with
|
| 592 |
+
``codes`` still on-device.
|
| 593 |
+
"""
|
| 594 |
+
import torch # local import keeps anneal.py torch-free for CPU installs
|
| 595 |
+
|
| 596 |
+
dev = X.device
|
| 597 |
+
f64 = torch.float64
|
| 598 |
+
B, n = X.shape
|
| 599 |
+
half = n // 2
|
| 600 |
+
n_passes = cfg.therm_passes if passes is None else int(passes)
|
| 601 |
+
if B == 0 or n % 2 != 0:
|
| 602 |
+
return codes.to(torch.int64), 0
|
| 603 |
+
if n_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0):
|
| 604 |
+
return codes.to(torch.int64).clone(), 0
|
| 605 |
+
if cfg.fold_depth > 1:
|
| 606 |
+
raise ValueError("thermal refinement supports fold_depth <= 1")
|
| 607 |
+
|
| 608 |
+
Xf = X.to(f64)
|
| 609 |
+
wf = None if w is None else w.to(f64)
|
| 610 |
+
cf = None if carry is None else carry.to(f64)
|
| 611 |
+
if cf is None:
|
| 612 |
+
cf = torch.zeros(B, dtype=f64, device=dev)
|
| 613 |
+
sf = step.to(f64)
|
| 614 |
+
mf_data = m_const.to(f64)
|
| 615 |
+
cc = codes.to(torch.int64).clone()
|
| 616 |
+
grid = torch.arange(levels, device=dev, dtype=torch.int64)
|
| 617 |
+
ones_col = torch.ones(B, dtype=f64, device=dev)
|
| 618 |
+
|
| 619 |
+
def energies(c):
|
| 620 |
+
e = Xf - (sf * c.to(f64) - mf_data)
|
| 621 |
+
w2 = torch.sum(e * e, dim=1) if wf is None else torch.sum(wf * e * e, dim=1)
|
| 622 |
+
v = e[:, :half] + e[:, half:]
|
| 623 |
+
dc = torch.sum(e, dim=1) + cf
|
| 624 |
+
return (w2 + lam_vw * torch.sum(v * v, dim=1) + lam_dc * dc * dc), e, w2, v, dc
|
| 625 |
+
|
| 626 |
+
energy_best, e, sse0, v, dc = energies(cc)
|
| 627 |
+
energy0 = energy_best.clone()
|
| 628 |
+
sse_cap = sse0 * (1.0 + float(cfg.sse_budget))
|
| 629 |
+
best = cc.clone()
|
| 630 |
+
gen = torch.Generator(device=dev)
|
| 631 |
+
gen.manual_seed(int(cfg.therm_seed if seed is None else seed) & 0x7FFFFFFFFFFFFFFF)
|
| 632 |
+
|
| 633 |
+
for t in range(n_passes):
|
| 634 |
+
temperature = (
|
| 635 |
+
cfg.therm_t0
|
| 636 |
+
* torch.maximum(energy_best, torch.full_like(energy_best, 1.0e-30))
|
| 637 |
+
/ n
|
| 638 |
+
* (cfg.therm_t_decay ** t)
|
| 639 |
+
)
|
| 640 |
+
broke = False
|
| 641 |
+
for sweep in range(cfg.therm_sweeps + 2):
|
| 642 |
+
greedy = sweep >= cfg.therm_sweeps
|
| 643 |
+
energy_cur, e, _, v, dc = energies(cc)
|
| 644 |
+
for p in range(half):
|
| 645 |
+
e_lo = e[:, p]
|
| 646 |
+
e_hi = e[:, half + p]
|
| 647 |
+
q_lo = cc[:, p]
|
| 648 |
+
q_hi = cc[:, half + p]
|
| 649 |
+
ds_lo = sf[:, p]
|
| 650 |
+
ds_hi = sf[:, half + p]
|
| 651 |
+
act_lo = ds_lo.abs() >= EPS_D
|
| 652 |
+
act_hi = ds_hi.abs() >= EPS_D
|
| 653 |
+
w_lo = ones_col if wf is None else wf[:, p]
|
| 654 |
+
w_hi = ones_col if wf is None else wf[:, half + p]
|
| 655 |
+
de_lo = ds_lo[:, None] * (q_lo[:, None] - grid[None, :])
|
| 656 |
+
de_lo = torch.where(act_lo[:, None], de_lo, torch.zeros_like(de_lo))
|
| 657 |
+
de_hi = ds_hi[:, None] * (q_hi[:, None] - grid[None, :])
|
| 658 |
+
de_hi = torch.where(act_hi[:, None], de_hi, torch.zeros_like(de_hi))
|
| 659 |
+
de_sum = de_lo[:, :, None] + de_hi[:, None, :]
|
| 660 |
+
v_p = v[:, p]
|
| 661 |
+
d_e = (
|
| 662 |
+
w_lo[:, None, None] * (2.0 * e_lo[:, None, None] * de_lo[:, :, None] + de_lo[:, :, None] ** 2)
|
| 663 |
+
+ w_hi[:, None, None] * (2.0 * e_hi[:, None, None] * de_hi[:, None, :] + de_hi[:, None, :] ** 2)
|
| 664 |
+
+ lam_vw * (2.0 * v_p[:, None, None] * de_sum + de_sum * de_sum)
|
| 665 |
+
+ lam_dc * (2.0 * dc[:, None, None] * de_sum + de_sum * de_sum)
|
| 666 |
+
)
|
| 667 |
+
cur = q_lo * levels + q_hi
|
| 668 |
+
if greedy:
|
| 669 |
+
flat = d_e.reshape(B, -1)
|
| 670 |
+
pick = flat.argmin(dim=1)
|
| 671 |
+
improve = torch.gather(flat, 1, pick[:, None]).squeeze(1) < -1.0e-18
|
| 672 |
+
pick = torch.where(improve, pick, cur)
|
| 673 |
+
else:
|
| 674 |
+
scc = torch.clamp(temperature, min=1.0e-300)[:, None, None]
|
| 675 |
+
logits = -d_e / scc
|
| 676 |
+
logits = logits - logits.amax(dim=(1, 2), keepdim=True)
|
| 677 |
+
probs = logits.exp().reshape(B, -1)
|
| 678 |
+
dead = ~(act_lo | act_hi)
|
| 679 |
+
if bool(dead.any()):
|
| 680 |
+
probs[dead] = 0.0
|
| 681 |
+
probs[dead, cur[dead]] = 1.0
|
| 682 |
+
cum = probs.cumsum(dim=1)
|
| 683 |
+
cum = cum / cum[:, -1:]
|
| 684 |
+
u = torch.rand(B, 1, generator=gen, device=dev, dtype=f64)
|
| 685 |
+
pick = (cum < u).sum(dim=1).clamp(max=levels * levels - 1)
|
| 686 |
+
ia = pick // levels
|
| 687 |
+
ib = pick - ia * levels
|
| 688 |
+
upd_lo = torch.gather(de_lo, 1, ia[:, None]).squeeze(1)
|
| 689 |
+
upd_hi = torch.gather(de_hi, 1, ib[:, None]).squeeze(1)
|
| 690 |
+
usum = upd_lo + upd_hi
|
| 691 |
+
e[:, p] = e_lo + upd_lo
|
| 692 |
+
e[:, half + p] = e_hi + upd_hi
|
| 693 |
+
v[:, p] += usum
|
| 694 |
+
dc = dc + usum
|
| 695 |
+
cc[:, p] = ia
|
| 696 |
+
cc[:, half + p] = ib
|
| 697 |
+
if greedy:
|
| 698 |
+
energy_now, _, _, _, _ = energies(cc)
|
| 699 |
+
if not bool(torch.any(energy_now < energy_cur - 1.0e-18)):
|
| 700 |
+
broke = True
|
| 701 |
+
break
|
| 702 |
+
_, _, sse_now, _, _ = energies(cc)
|
| 703 |
+
take = (energy_now < energy_best - 1.0e-18) & (sse_now <= sse_cap)
|
| 704 |
+
best = torch.where(take[:, None], cc, best)
|
| 705 |
+
energy_best = torch.where(take, energy_now, energy_best)
|
| 706 |
+
cc = best.clone()
|
| 707 |
+
if broke and bool(torch.all(energy_now >= energy_best)):
|
| 708 |
+
pass # keep exact CPU-loop semantics: continue across passes regardless
|
| 709 |
+
|
| 710 |
+
n_improved = int((energy_best < energy0 - 1.0e-18).sum().item())
|
| 711 |
+
return best, n_improved
|
| 712 |
+
|
| 713 |
+
|
| 714 |
+
def thermal_refine_q3_k_cuda(
|
| 715 |
+
X: "torch.Tensor",
|
| 716 |
+
W: "torch.Tensor | None",
|
| 717 |
+
scale: "torch.Tensor",
|
| 718 |
+
codes: "torch.Tensor",
|
| 719 |
+
cfg: HPCConfig,
|
| 720 |
+
) -> tuple["torch.Tensor", int]:
|
| 721 |
+
"""On-device anneal for Q3_K signed 3-bit codes (see the CPU twin).
|
| 722 |
+
|
| 723 |
+
``scale``: per-superblock scale values (B, 16) on-device, ``codes``:
|
| 724 |
+
signed values (B, 256) in [-4, 3]. Exactly mirrors
|
| 725 |
+
``thermal_refine_q3_k`` without any host round-trip.
|
| 726 |
+
"""
|
| 727 |
+
import torch
|
| 728 |
+
n_blocks, n = X.shape
|
| 729 |
+
lam_vw, lam_dc = thermal_spectral_lams(cfg, n)
|
| 730 |
+
if n_blocks == 0 or cfg.therm_passes == 0 or (lam_vw == 0.0 and lam_dc == 0.0):
|
| 731 |
+
return codes.to(torch.int64).clone(), 0
|
| 732 |
+
|
| 733 |
+
def build_state(start, stop):
|
| 734 |
+
ds_full = scale.repeat_interleave(16, dim=1) # (B, 256)
|
| 735 |
+
return ds_full, 4.0 * ds_full
|
| 736 |
+
|
| 737 |
+
ds_full, m_full = build_state(0, n_blocks)
|
| 738 |
+
codes_out, improved = thermal_refine_uniform_codes_cuda(
|
| 739 |
+
X, W, codes.to(torch.int64) + 4, ds_full, m_full, None,
|
| 740 |
+
lam_vw, lam_dc, 8, cfg,
|
| 741 |
+
)
|
| 742 |
+
return codes_out - 4, improved
|
q3_k.py
CHANGED
|
@@ -48,7 +48,7 @@ try:
|
|
| 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
|
|
@@ -814,18 +814,17 @@ def _fit_q3_k_blocks_cuda(
|
|
| 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 =
|
| 819 |
-
thermal_c, _ =
|
| 820 |
-
Xf
|
| 821 |
-
|
| 822 |
-
scale_rp,
|
| 823 |
-
code.reshape(B, QK3_K).cpu().numpy(),
|
| 824 |
-
cfg,
|
| 825 |
)
|
| 826 |
-
|
| 827 |
-
|
| 828 |
-
).to(dev)
|
| 829 |
|
| 830 |
return (
|
| 831 |
best_d16.cpu().numpy().astype(np.uint16),
|
|
|
|
| 48 |
except ImportError: # pragma: no cover
|
| 49 |
torch = None # type: ignore[assignment]
|
| 50 |
|
| 51 |
+
from .anneal import thermal_refine_q3_k, thermal_refine_q3_k_cuda
|
| 52 |
from .config import HPCConfig
|
| 53 |
from .cuda_backend import cuda_is_available
|
| 54 |
from .factored import MULTS_D
|
|
|
|
| 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 |
+
# On CUDA the anneal keeps every tensor on-device — the CPU/numpy
|
| 818 |
+
# chain previously forced a full D2H→CPU→D2H round-trip per chunk
|
| 819 |
+
# and made the GPU idle for the whole refinery.
|
| 820 |
if cfg.therm_passes > 0:
|
| 821 |
+
scale_rp = best_d[:, None] * sc.to(torch.float32) # (B, 16) per-superblock
|
| 822 |
+
thermal_c, _ = thermal_refine_q3_k_cuda(
|
| 823 |
+
Xf, None if Wf is None else Wf, scale_rp,
|
| 824 |
+
code.reshape(B, QK3_K), cfg,
|
|
|
|
|
|
|
|
|
|
| 825 |
)
|
| 826 |
+
# thermal_refine_q3_k_cuda already returns source-signed codes [-4, 3]
|
| 827 |
+
code = thermal_c.reshape(B, Q3_K_N_SUBBLOCKS, Q3_K_SUBBLOCK).to(torch.int16)
|
|
|
|
| 828 |
|
| 829 |
return (
|
| 830 |
best_d16.cpu().numpy().astype(np.uint16),
|