Add multi-threading
Browse files- __main__.py +3 -0
- config.py +4 -1
- gui.py +5 -0
- pipeline.py +251 -111
__main__.py
CHANGED
|
@@ -38,6 +38,8 @@ def _config_from_args(args) -> HPCConfig:
|
|
| 38 |
cfg.n_cand_d = args.cand_d
|
| 39 |
if args.cand_m is not None:
|
| 40 |
cfg.n_cand_m = args.cand_m
|
|
|
|
|
|
|
| 41 |
if args.mse:
|
| 42 |
cfg.optimizer = "mse"
|
| 43 |
if args.no_spectral:
|
|
@@ -223,6 +225,7 @@ def main() -> None:
|
|
| 223 |
parser.add_argument("--seed", type=int, default=1234, help="demo RNG seed")
|
| 224 |
parser.add_argument("--cand-d", type=int, default=None, help="candidate d count")
|
| 225 |
parser.add_argument("--cand-m", type=int, default=None, help="candidate min count")
|
|
|
|
| 226 |
parser.add_argument("--mse", action="store_true", help="disable HPC sequence search")
|
| 227 |
parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
|
| 228 |
parser.add_argument("--verbose", action="store_true")
|
|
|
|
| 38 |
cfg.n_cand_d = args.cand_d
|
| 39 |
if args.cand_m is not None:
|
| 40 |
cfg.n_cand_m = args.cand_m
|
| 41 |
+
if args.workers is not None:
|
| 42 |
+
cfg.workers = args.workers
|
| 43 |
if args.mse:
|
| 44 |
cfg.optimizer = "mse"
|
| 45 |
if args.no_spectral:
|
|
|
|
| 225 |
parser.add_argument("--seed", type=int, default=1234, help="demo RNG seed")
|
| 226 |
parser.add_argument("--cand-d", type=int, default=None, help="candidate d count")
|
| 227 |
parser.add_argument("--cand-m", type=int, default=None, help="candidate min count")
|
| 228 |
+
parser.add_argument("--workers", type=int, default=None, help="worker processes (0=auto CPU cores, 1=single)")
|
| 229 |
parser.add_argument("--mse", action="store_true", help="disable HPC sequence search")
|
| 230 |
parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
|
| 231 |
parser.add_argument("--verbose", action="store_true")
|
config.py
CHANGED
|
@@ -75,7 +75,8 @@ class HPCConfig:
|
|
| 75 |
clamp_sigma_soft: float = 6.0
|
| 76 |
clamp_sigma_hard: float = 4.0
|
| 77 |
|
| 78 |
-
# ---- reporting ----------------------------------------
|
|
|
|
| 79 |
verbose: bool = False
|
| 80 |
|
| 81 |
def validate(self) -> "HPCConfig":
|
|
@@ -109,4 +110,6 @@ class HPCConfig:
|
|
| 109 |
raise ValueError("log_slack must be non-negative")
|
| 110 |
if self.viterbi_alpha < 0.0 or self.viterbi_beta < 0.0:
|
| 111 |
raise ValueError("Viterbi weights must be non-negative")
|
|
|
|
|
|
|
| 112 |
return self
|
|
|
|
| 75 |
clamp_sigma_soft: float = 6.0
|
| 76 |
clamp_sigma_hard: float = 4.0
|
| 77 |
|
| 78 |
+
# ---- reporting & parallelism ----------------------------------------
|
| 79 |
+
workers: int = 0
|
| 80 |
verbose: bool = False
|
| 81 |
|
| 82 |
def validate(self) -> "HPCConfig":
|
|
|
|
| 110 |
raise ValueError("log_slack must be non-negative")
|
| 111 |
if self.viterbi_alpha < 0.0 or self.viterbi_beta < 0.0:
|
| 112 |
raise ValueError("Viterbi weights must be non-negative")
|
| 113 |
+
if self.workers < 0:
|
| 114 |
+
raise ValueError("workers must be >= 0 (0 = auto CPU count)")
|
| 115 |
return self
|
gui.py
CHANGED
|
@@ -97,6 +97,7 @@ class HPCQuantizeGUI:
|
|
| 97 |
self.iters_var = tk.IntVar(value=8)
|
| 98 |
self.ulp_var = tk.IntVar(value=4)
|
| 99 |
self.limit_var = tk.IntVar(value=0)
|
|
|
|
| 100 |
|
| 101 |
self.use_analogs_var = tk.BooleanVar(value=True)
|
| 102 |
self.coupled_q2k_var = tk.BooleanVar(value=True)
|
|
@@ -136,6 +137,7 @@ class HPCQuantizeGUI:
|
|
| 136 |
self.cand_d_var,
|
| 137 |
self.cand_m_var,
|
| 138 |
self.limit_var,
|
|
|
|
| 139 |
self.use_spectral_var,
|
| 140 |
):
|
| 141 |
v.trace_add("write", lambda *_: self._update_cli_preview())
|
|
@@ -271,6 +273,7 @@ class HPCQuantizeGUI:
|
|
| 271 |
self._spin_pair(grid_f, 1, 2, "Solver Iters:", self.iters_var, 1, 32)
|
| 272 |
self._spin_pair(grid_f, 2, 0, "FP16 ULP Radius:", self.ulp_var, 0, 16)
|
| 273 |
self._spin_pair(grid_f, 2, 2, "Tensor Limit (0=all):", self.limit_var, 0, 9999)
|
|
|
|
| 274 |
|
| 275 |
spec_f = ttk.LabelFrame(opt_frame, text="Spectral & DC Parameters", padding=6)
|
| 276 |
spec_f.pack(fill=tk.X, pady=2)
|
|
@@ -481,6 +484,7 @@ class HPCQuantizeGUI:
|
|
| 481 |
vw_lambda=float(self.vw_lambda_var.get()),
|
| 482 |
dc_decay=float(self.dc_decay_var.get()),
|
| 483 |
sse_budget=float(self.sse_budget_var.get()),
|
|
|
|
| 484 |
verbose=False,
|
| 485 |
)
|
| 486 |
return cfg.validate()
|
|
@@ -503,6 +507,7 @@ class HPCQuantizeGUI:
|
|
| 503 |
cmd += ["--output", out]
|
| 504 |
if self.limit_var.get() > 0:
|
| 505 |
cmd += ["--limit", str(self.limit_var.get())]
|
|
|
|
| 506 |
cmd += ["--cand-d", str(self.cand_d_var.get()), "--cand-m", str(self.cand_m_var.get())]
|
| 507 |
if self.optimizer_var.get() == "mse":
|
| 508 |
cmd.append("--mse")
|
|
|
|
| 97 |
self.iters_var = tk.IntVar(value=8)
|
| 98 |
self.ulp_var = tk.IntVar(value=4)
|
| 99 |
self.limit_var = tk.IntVar(value=0)
|
| 100 |
+
self.workers_var = tk.IntVar(value=0)
|
| 101 |
|
| 102 |
self.use_analogs_var = tk.BooleanVar(value=True)
|
| 103 |
self.coupled_q2k_var = tk.BooleanVar(value=True)
|
|
|
|
| 137 |
self.cand_d_var,
|
| 138 |
self.cand_m_var,
|
| 139 |
self.limit_var,
|
| 140 |
+
self.workers_var,
|
| 141 |
self.use_spectral_var,
|
| 142 |
):
|
| 143 |
v.trace_add("write", lambda *_: self._update_cli_preview())
|
|
|
|
| 273 |
self._spin_pair(grid_f, 1, 2, "Solver Iters:", self.iters_var, 1, 32)
|
| 274 |
self._spin_pair(grid_f, 2, 0, "FP16 ULP Radius:", self.ulp_var, 0, 16)
|
| 275 |
self._spin_pair(grid_f, 2, 2, "Tensor Limit (0=all):", self.limit_var, 0, 9999)
|
| 276 |
+
self._spin_pair(grid_f, 3, 0, "Workers (0=Auto CPU):", self.workers_var, 0, 256)
|
| 277 |
|
| 278 |
spec_f = ttk.LabelFrame(opt_frame, text="Spectral & DC Parameters", padding=6)
|
| 279 |
spec_f.pack(fill=tk.X, pady=2)
|
|
|
|
| 484 |
vw_lambda=float(self.vw_lambda_var.get()),
|
| 485 |
dc_decay=float(self.dc_decay_var.get()),
|
| 486 |
sse_budget=float(self.sse_budget_var.get()),
|
| 487 |
+
workers=int(self.workers_var.get()),
|
| 488 |
verbose=False,
|
| 489 |
)
|
| 490 |
return cfg.validate()
|
|
|
|
| 507 |
cmd += ["--output", out]
|
| 508 |
if self.limit_var.get() > 0:
|
| 509 |
cmd += ["--limit", str(self.limit_var.get())]
|
| 510 |
+
cmd += ["--workers", str(self.workers_var.get())]
|
| 511 |
cmd += ["--cand-d", str(self.cand_d_var.get()), "--cand-m", str(self.cand_m_var.get())]
|
| 512 |
if self.optimizer_var.get() == "mse":
|
| 513 |
cmd.append("--mse")
|
pipeline.py
CHANGED
|
@@ -17,6 +17,8 @@ from __future__ import annotations
|
|
| 17 |
|
| 18 |
from dataclasses import dataclass
|
| 19 |
from math import ceil
|
|
|
|
|
|
|
| 20 |
import time
|
| 21 |
|
| 22 |
import numpy as np
|
|
@@ -405,6 +407,107 @@ def _apply_dc_nudge(X, W, blocks, cfg, row_width):
|
|
| 405 |
return blocks
|
| 406 |
|
| 407 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 408 |
class Q2KQuantizer:
|
| 409 |
def __init__(self, config: HPCConfig | None = None):
|
| 410 |
self.config = (config or HPCConfig()).validate()
|
|
@@ -481,37 +584,75 @@ class Q2KQuantizer:
|
|
| 481 |
t_cand = time.time()
|
| 482 |
last_emit = 0.0
|
| 483 |
step_report = max(1, min(32, n_blocks // 20 or 1))
|
| 484 |
-
|
| 485 |
-
|
| 486 |
-
|
| 487 |
-
|
| 488 |
-
|
| 489 |
-
|
| 490 |
-
|
| 491 |
-
|
| 492 |
-
|
| 493 |
-
|
| 494 |
-
|
| 495 |
-
|
| 496 |
-
|
| 497 |
-
|
| 498 |
-
|
| 499 |
-
|
| 500 |
-
|
| 501 |
-
|
| 502 |
-
|
| 503 |
-
|
| 504 |
-
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 515 |
|
| 516 |
_check_stop()
|
| 517 |
_emit(
|
|
@@ -532,27 +673,13 @@ class Q2KQuantizer:
|
|
| 532 |
sigma=sigma,
|
| 533 |
)
|
| 534 |
|
| 535 |
-
|
| 536 |
-
|
| 537 |
-
|
| 538 |
-
|
| 539 |
-
|
| 540 |
-
|
| 541 |
-
|
| 542 |
-
X[i],
|
| 543 |
-
wi,
|
| 544 |
-
cfg,
|
| 545 |
-
init_s=seeds["scales"][i],
|
| 546 |
-
init_t=seeds["mins"][i],
|
| 547 |
-
d_seed=float(from_fp16_bits(d16)),
|
| 548 |
-
m_seed=float(from_fp16_bits(m16)),
|
| 549 |
-
pin=cfg.factor_pin,
|
| 550 |
-
use_spectral=cfg.use_spectral,
|
| 551 |
-
max_iter=cfg.factor_iters,
|
| 552 |
-
starts=1,
|
| 553 |
-
dc_carry=0.0,
|
| 554 |
-
)
|
| 555 |
-
)
|
| 556 |
|
| 557 |
blocks: list[Q2KBlock] = []
|
| 558 |
factor_floor_used = 0
|
|
@@ -560,67 +687,80 @@ class Q2KQuantizer:
|
|
| 560 |
running_sse = 0.0
|
| 561 |
t_asm = time.time()
|
| 562 |
last_emit = 0.0
|
| 563 |
-
for i in range(n_blocks):
|
| 564 |
-
_check_stop()
|
| 565 |
-
wi = None if W is None else W[i]
|
| 566 |
-
d, m, ls, lm, codes, obj, sse = _assemble_block(
|
| 567 |
-
X[i], wi, seeds, base_d[i], base_m[i], best[i], carry[i], cfg
|
| 568 |
-
)
|
| 569 |
|
| 570 |
-
|
| 571 |
-
|
| 572 |
-
|
| 573 |
-
|
| 574 |
-
|
| 575 |
-
|
| 576 |
-
|
| 577 |
-
)
|
| 578 |
-
|
| 579 |
-
|
| 580 |
-
candidate_floor_used +=
|
| 581 |
-
|
| 582 |
-
|
| 583 |
-
|
| 584 |
-
|
| 585 |
-
|
| 586 |
-
|
| 587 |
-
|
| 588 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 589 |
)
|
| 590 |
-
|
| 591 |
-
|
| 592 |
-
m = float(fac.dmin)
|
| 593 |
-
ls = fac.ls.copy()
|
| 594 |
-
lm = fac.lm.copy()
|
| 595 |
-
codes = fcodes.copy()
|
| 596 |
-
obj, sse = fobj, fsse
|
| 597 |
-
factor_floor_used += 1
|
| 598 |
-
|
| 599 |
-
blk_obj = make_block(d, m, ls, lm, codes)
|
| 600 |
-
blocks.append(blk_obj)
|
| 601 |
-
running_sse += blk_obj.error(X[i])
|
| 602 |
-
now = time.time()
|
| 603 |
-
if (i + 1) % step_report == 0 or (i + 1) == n_blocks or (now - last_emit) >= 0.25:
|
| 604 |
-
last_emit = now
|
| 605 |
-
elapsed = max(now - t_asm, 1.0e-6)
|
| 606 |
-
rate = (i + 1) / elapsed
|
| 607 |
-
eta = (n_blocks - (i + 1)) / max(rate, 1.0e-9)
|
| 608 |
-
cur_rmse = float(np.sqrt(running_sse / ((i + 1) * QK_K)))
|
| 609 |
-
_emit(
|
| 610 |
-
"Phase 4: Sub-block Sieve + ULP + Hadamard Assembly",
|
| 611 |
-
i + 1,
|
| 612 |
-
n_blocks,
|
| 613 |
-
f"blk {i+1}/{n_blocks} ({rate:.1f} blk/s, ETA {eta:.1f}s) "
|
| 614 |
-
f"rmse={cur_rmse:.6e} rmse/sigma={cur_rmse/max(sigma, 1e-15):.4f} "
|
| 615 |
-
f"floors(cand={candidate_floor_used}, fac={factor_floor_used})",
|
| 616 |
-
rmse=cur_rmse,
|
| 617 |
-
sigma=sigma,
|
| 618 |
-
temperature=temperature,
|
| 619 |
-
cand_floors=candidate_floor_used,
|
| 620 |
-
fac_floors=factor_floor_used,
|
| 621 |
-
rate=rate,
|
| 622 |
-
eta=eta,
|
| 623 |
-
)
|
| 624 |
|
| 625 |
if cfg.apply_dc_carry and (cfg.dc_lambda > 0.0 or cfg.dc_decay > 0.0):
|
| 626 |
_check_stop()
|
|
|
|
| 17 |
|
| 18 |
from dataclasses import dataclass
|
| 19 |
from math import ceil
|
| 20 |
+
import multiprocessing as mp
|
| 21 |
+
import os
|
| 22 |
import time
|
| 23 |
|
| 24 |
import numpy as np
|
|
|
|
| 407 |
return blocks
|
| 408 |
|
| 409 |
|
| 410 |
+
_MP_STATE: dict[str, object] = {}
|
| 411 |
+
|
| 412 |
+
|
| 413 |
+
def _resolve_workers(cfg: HPCConfig, n_blocks: int) -> int:
|
| 414 |
+
if cfg.workers == 1 or n_blocks < 4:
|
| 415 |
+
return 1
|
| 416 |
+
if cfg.workers > 1:
|
| 417 |
+
return min(int(cfg.workers), n_blocks)
|
| 418 |
+
if n_blocks < 16:
|
| 419 |
+
return 1
|
| 420 |
+
cpu = os.cpu_count() or 1
|
| 421 |
+
return min(cpu, n_blocks)
|
| 422 |
+
|
| 423 |
+
|
| 424 |
+
def _phase2b_one(i: int) -> tuple[np.ndarray, FactorResult]:
|
| 425 |
+
X = _MP_STATE["X"]
|
| 426 |
+
W = _MP_STATE["W"]
|
| 427 |
+
seeds = _MP_STATE["seeds"]
|
| 428 |
+
base_d = _MP_STATE["base_d"]
|
| 429 |
+
base_m = _MP_STATE["base_m"]
|
| 430 |
+
cfg = _MP_STATE["cfg"]
|
| 431 |
+
wi = None if W is None else W[i]
|
| 432 |
+
cand = factor_candidates(
|
| 433 |
+
X[i],
|
| 434 |
+
wi,
|
| 435 |
+
seeds["scales"][i],
|
| 436 |
+
seeds["mins"][i],
|
| 437 |
+
base_d[i],
|
| 438 |
+
base_m[i],
|
| 439 |
+
cfg,
|
| 440 |
+
)
|
| 441 |
+
fac_res = factor_only_solve(X[i], wi, cfg)
|
| 442 |
+
return cand, fac_res
|
| 443 |
+
|
| 444 |
+
|
| 445 |
+
def _phase4_one(i: int) -> tuple[Q2KBlock, int, int, float]:
|
| 446 |
+
X = _MP_STATE["X"]
|
| 447 |
+
W = _MP_STATE["W"]
|
| 448 |
+
seeds = _MP_STATE["seeds"]
|
| 449 |
+
base_d = _MP_STATE["base_d"]
|
| 450 |
+
base_m = _MP_STATE["base_m"]
|
| 451 |
+
best = _MP_STATE["best"]
|
| 452 |
+
carry = _MP_STATE["carry"]
|
| 453 |
+
standalone = _MP_STATE["standalone"]
|
| 454 |
+
cfg = _MP_STATE["cfg"]
|
| 455 |
+
|
| 456 |
+
wi = None if W is None else W[i]
|
| 457 |
+
d16, m16 = candidate_pair(base_d[i], base_m[i], best[i])
|
| 458 |
+
at_slot = factor_q2k_block(
|
| 459 |
+
X[i],
|
| 460 |
+
wi,
|
| 461 |
+
cfg,
|
| 462 |
+
init_s=seeds["scales"][i],
|
| 463 |
+
init_t=seeds["mins"][i],
|
| 464 |
+
d_seed=float(from_fp16_bits(d16)),
|
| 465 |
+
m_seed=float(from_fp16_bits(m16)),
|
| 466 |
+
pin=cfg.factor_pin,
|
| 467 |
+
use_spectral=cfg.use_spectral,
|
| 468 |
+
max_iter=cfg.factor_iters,
|
| 469 |
+
starts=1,
|
| 470 |
+
dc_carry=0.0,
|
| 471 |
+
)
|
| 472 |
+
d, m, ls, lm, codes, obj, sse = _assemble_block(
|
| 473 |
+
X[i], wi, seeds, base_d[i], base_m[i], best[i], carry[i], cfg
|
| 474 |
+
)
|
| 475 |
+
cand_hit = 0
|
| 476 |
+
fac_hit = 0
|
| 477 |
+
|
| 478 |
+
if cfg.use_candidate_floor:
|
| 479 |
+
cd, cm, cls, clm = _candidate_config(
|
| 480 |
+
seeds["scales"][i], seeds["mins"][i], base_d[i], base_m[i], best[i]
|
| 481 |
+
)
|
| 482 |
+
ccodes = codes_from_params(X[i], cd, cm, cls, clm)
|
| 483 |
+
cobj, csse = objective(
|
| 484 |
+
X[i], wi, cd, cm, cls, clm, ccodes, cfg, float(carry[i])
|
| 485 |
+
)
|
| 486 |
+
if cobj < obj - 1.0e-15:
|
| 487 |
+
d, m, ls, lm, codes, obj, sse = cd, cm, cls, clm, ccodes, cobj, csse
|
| 488 |
+
cand_hit = 1
|
| 489 |
+
|
| 490 |
+
if cfg.use_factor_floor:
|
| 491 |
+
for fac in (at_slot, standalone[i]):
|
| 492 |
+
fcodes = refine_codes_hadamard(
|
| 493 |
+
X[i], wi, fac.d, fac.dmin, fac.ls, fac.lm, fac.codes, cfg, float(carry[i])
|
| 494 |
+
)
|
| 495 |
+
fobj, fsse = objective(
|
| 496 |
+
X[i], wi, fac.d, fac.dmin, fac.ls, fac.lm, fcodes, cfg, float(carry[i])
|
| 497 |
+
)
|
| 498 |
+
if fobj < obj - 1.0e-15:
|
| 499 |
+
d = float(fac.d)
|
| 500 |
+
m = float(fac.dmin)
|
| 501 |
+
ls = fac.ls.copy()
|
| 502 |
+
lm = fac.lm.copy()
|
| 503 |
+
codes = fcodes.copy()
|
| 504 |
+
obj, sse = fobj, fsse
|
| 505 |
+
fac_hit = 1
|
| 506 |
+
|
| 507 |
+
blk_obj = make_block(d, m, ls, lm, codes)
|
| 508 |
+
return blk_obj, cand_hit, fac_hit, blk_obj.error(X[i])
|
| 509 |
+
|
| 510 |
+
|
| 511 |
class Q2KQuantizer:
|
| 512 |
def __init__(self, config: HPCConfig | None = None):
|
| 513 |
self.config = (config or HPCConfig()).validate()
|
|
|
|
| 584 |
t_cand = time.time()
|
| 585 |
last_emit = 0.0
|
| 586 |
step_report = max(1, min(32, n_blocks // 20 or 1))
|
| 587 |
+
n_workers = _resolve_workers(cfg, n_blocks)
|
| 588 |
+
_MP_STATE.update(
|
| 589 |
+
{
|
| 590 |
+
"X": X,
|
| 591 |
+
"W": W,
|
| 592 |
+
"seeds": seeds,
|
| 593 |
+
"base_d": base_d,
|
| 594 |
+
"base_m": base_m,
|
| 595 |
+
"cfg": cfg,
|
| 596 |
+
}
|
| 597 |
+
)
|
| 598 |
+
|
| 599 |
+
if n_workers > 1 and "fork" in mp.get_all_start_methods():
|
| 600 |
+
ctx = mp.get_context("fork")
|
| 601 |
+
chunksize = max(1, min(16, n_blocks // (n_workers * 4) or 1))
|
| 602 |
+
pool = ctx.Pool(processes=n_workers)
|
| 603 |
+
try:
|
| 604 |
+
for i, (cand_row, fac_res) in enumerate(
|
| 605 |
+
pool.imap(_phase2b_one, range(n_blocks), chunksize=chunksize)
|
| 606 |
+
):
|
| 607 |
+
_check_stop()
|
| 608 |
+
candidate_errors[i] = cand_row
|
| 609 |
+
standalone.append(fac_res)
|
| 610 |
+
now = time.time()
|
| 611 |
+
if (i + 1) % step_report == 0 or (i + 1) == n_blocks or (now - last_emit) >= 0.25:
|
| 612 |
+
last_emit = now
|
| 613 |
+
elapsed = max(now - t_cand, 1.0e-6)
|
| 614 |
+
rate = (i + 1) / elapsed
|
| 615 |
+
eta = (n_blocks - (i + 1)) / max(rate, 1.0e-9)
|
| 616 |
+
_emit(
|
| 617 |
+
f"Phase 2b: Analog-Guided Factored Field ({n_workers} workers)",
|
| 618 |
+
i + 1,
|
| 619 |
+
n_blocks,
|
| 620 |
+
f"blk {i+1}/{n_blocks} ({rate:.1f} blk/s, ETA {eta:.1f}s) "
|
| 621 |
+
f"d={float(fac_res.d):.5f} dmin={float(fac_res.dmin):.5f} "
|
| 622 |
+
f"obj={fac_res.objective:.5e} starts={fac_res.starts}",
|
| 623 |
+
rate=rate,
|
| 624 |
+
eta=eta,
|
| 625 |
+
sigma=sigma,
|
| 626 |
+
)
|
| 627 |
+
pool.close()
|
| 628 |
+
pool.join()
|
| 629 |
+
except BaseException:
|
| 630 |
+
pool.terminate()
|
| 631 |
+
pool.join()
|
| 632 |
+
raise
|
| 633 |
+
else:
|
| 634 |
+
for i in range(n_blocks):
|
| 635 |
+
_check_stop()
|
| 636 |
+
cand_row, fac_res = _phase2b_one(i)
|
| 637 |
+
candidate_errors[i] = cand_row
|
| 638 |
+
standalone.append(fac_res)
|
| 639 |
+
now = time.time()
|
| 640 |
+
if (i + 1) % step_report == 0 or (i + 1) == n_blocks or (now - last_emit) >= 0.25:
|
| 641 |
+
last_emit = now
|
| 642 |
+
elapsed = max(now - t_cand, 1.0e-6)
|
| 643 |
+
rate = (i + 1) / elapsed
|
| 644 |
+
eta = (n_blocks - (i + 1)) / max(rate, 1.0e-9)
|
| 645 |
+
_emit(
|
| 646 |
+
"Phase 2b: Analog-Guided Factored Candidate Field",
|
| 647 |
+
i + 1,
|
| 648 |
+
n_blocks,
|
| 649 |
+
f"blk {i+1}/{n_blocks} ({rate:.1f} blk/s, ETA {eta:.1f}s) "
|
| 650 |
+
f"d={float(fac_res.d):.5f} dmin={float(fac_res.dmin):.5f} "
|
| 651 |
+
f"obj={fac_res.objective:.5e} starts={fac_res.starts}",
|
| 652 |
+
rate=rate,
|
| 653 |
+
eta=eta,
|
| 654 |
+
sigma=sigma,
|
| 655 |
+
)
|
| 656 |
|
| 657 |
_check_stop()
|
| 658 |
_emit(
|
|
|
|
| 673 |
sigma=sigma,
|
| 674 |
)
|
| 675 |
|
| 676 |
+
_MP_STATE.update(
|
| 677 |
+
{
|
| 678 |
+
"best": best,
|
| 679 |
+
"carry": carry,
|
| 680 |
+
"standalone": standalone,
|
| 681 |
+
}
|
| 682 |
+
)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 683 |
|
| 684 |
blocks: list[Q2KBlock] = []
|
| 685 |
factor_floor_used = 0
|
|
|
|
| 687 |
running_sse = 0.0
|
| 688 |
t_asm = time.time()
|
| 689 |
last_emit = 0.0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 690 |
|
| 691 |
+
if n_workers > 1 and "fork" in mp.get_all_start_methods():
|
| 692 |
+
ctx = mp.get_context("fork")
|
| 693 |
+
chunksize = max(1, min(16, n_blocks // (n_workers * 4) or 1))
|
| 694 |
+
pool = ctx.Pool(processes=n_workers)
|
| 695 |
+
try:
|
| 696 |
+
for i, (blk_obj, cand_hit, fac_hit, blk_err) in enumerate(
|
| 697 |
+
pool.imap(_phase4_one, range(n_blocks), chunksize=chunksize)
|
| 698 |
+
):
|
| 699 |
+
_check_stop()
|
| 700 |
+
blocks.append(blk_obj)
|
| 701 |
+
candidate_floor_used += cand_hit
|
| 702 |
+
factor_floor_used += fac_hit
|
| 703 |
+
running_sse += blk_err
|
| 704 |
+
now = time.time()
|
| 705 |
+
if (i + 1) % step_report == 0 or (i + 1) == n_blocks or (now - last_emit) >= 0.25:
|
| 706 |
+
last_emit = now
|
| 707 |
+
elapsed = max(now - t_asm, 1.0e-6)
|
| 708 |
+
rate = (i + 1) / elapsed
|
| 709 |
+
eta = (n_blocks - (i + 1)) / max(rate, 1.0e-9)
|
| 710 |
+
cur_rmse = float(np.sqrt(running_sse / ((i + 1) * QK_K)))
|
| 711 |
+
_emit(
|
| 712 |
+
f"Phase 4: Sub-block Sieve + ULP + Hadamard Assembly ({n_workers} workers)",
|
| 713 |
+
i + 1,
|
| 714 |
+
n_blocks,
|
| 715 |
+
f"blk {i+1}/{n_blocks} ({rate:.1f} blk/s, ETA {eta:.1f}s) "
|
| 716 |
+
f"rmse={cur_rmse:.6e} rmse/sigma={cur_rmse/max(sigma, 1e-15):.4f} "
|
| 717 |
+
f"floors(cand={candidate_floor_used}, fac={factor_floor_used})",
|
| 718 |
+
rmse=cur_rmse,
|
| 719 |
+
sigma=sigma,
|
| 720 |
+
temperature=temperature,
|
| 721 |
+
cand_floors=candidate_floor_used,
|
| 722 |
+
fac_floors=factor_floor_used,
|
| 723 |
+
rate=rate,
|
| 724 |
+
eta=eta,
|
| 725 |
+
)
|
| 726 |
+
pool.close()
|
| 727 |
+
pool.join()
|
| 728 |
+
except BaseException:
|
| 729 |
+
pool.terminate()
|
| 730 |
+
pool.join()
|
| 731 |
+
raise
|
| 732 |
+
else:
|
| 733 |
+
for i in range(n_blocks):
|
| 734 |
+
_check_stop()
|
| 735 |
+
blk_obj, cand_hit, fac_hit, blk_err = _phase4_one(i)
|
| 736 |
+
blocks.append(blk_obj)
|
| 737 |
+
candidate_floor_used += cand_hit
|
| 738 |
+
factor_floor_used += fac_hit
|
| 739 |
+
running_sse += blk_err
|
| 740 |
+
now = time.time()
|
| 741 |
+
if (i + 1) % step_report == 0 or (i + 1) == n_blocks or (now - last_emit) >= 0.25:
|
| 742 |
+
last_emit = now
|
| 743 |
+
elapsed = max(now - t_asm, 1.0e-6)
|
| 744 |
+
rate = (i + 1) / elapsed
|
| 745 |
+
eta = (n_blocks - (i + 1)) / max(rate, 1.0e-9)
|
| 746 |
+
cur_rmse = float(np.sqrt(running_sse / ((i + 1) * QK_K)))
|
| 747 |
+
_emit(
|
| 748 |
+
"Phase 4: Sub-block Sieve + ULP + Hadamard Assembly",
|
| 749 |
+
i + 1,
|
| 750 |
+
n_blocks,
|
| 751 |
+
f"blk {i+1}/{n_blocks} ({rate:.1f} blk/s, ETA {eta:.1f}s) "
|
| 752 |
+
f"rmse={cur_rmse:.6e} rmse/sigma={cur_rmse/max(sigma, 1e-15):.4f} "
|
| 753 |
+
f"floors(cand={candidate_floor_used}, fac={factor_floor_used})",
|
| 754 |
+
rmse=cur_rmse,
|
| 755 |
+
sigma=sigma,
|
| 756 |
+
temperature=temperature,
|
| 757 |
+
cand_floors=candidate_floor_used,
|
| 758 |
+
fac_floors=factor_floor_used,
|
| 759 |
+
rate=rate,
|
| 760 |
+
eta=eta,
|
| 761 |
)
|
| 762 |
+
|
| 763 |
+
_MP_STATE.clear()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 764 |
|
| 765 |
if cfg.apply_dc_carry and (cfg.dc_lambda > 0.0 or cfg.dc_decay > 0.0):
|
| 766 |
_check_stop()
|