Upload gui.py
Browse files
gui.py
CHANGED
|
@@ -7,16 +7,19 @@ Launch::
|
|
| 7 |
|
| 8 |
Provides:
|
| 9 |
* Source GGUF, iMatrix (.gguf / .dat), and Output (.gguf / .npz) file pickers
|
| 10 |
-
* Interactive GGUF tensor table with
|
|
|
|
|
|
|
|
|
|
| 11 |
* Full HPCConfig controls (Geometry-Preserving Analogs, Coupled Bilinear Q2_K,
|
| 12 |
Sieve + 36-state Viterbi, Spectral/Vesica/DC shaping, Candidate lattice size)
|
| 13 |
-
*
|
| 14 |
-
* Threaded execution with per-tensor progress bar, live log, and cancellation
|
| 15 |
"""
|
| 16 |
|
| 17 |
from __future__ import annotations
|
| 18 |
|
| 19 |
import argparse
|
|
|
|
| 20 |
import queue
|
| 21 |
import shlex
|
| 22 |
import threading
|
|
@@ -40,25 +43,31 @@ from hpc_quantize_v2.gguf import is_q2k_eligible, write_quantized_gguf
|
|
| 40 |
from hpc_quantize_v2.reference import reference_solve, synthetic_cases
|
| 41 |
|
| 42 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 43 |
class HPCQuantizeGUI:
|
| 44 |
def __init__(self, root: tk.Tk) -> None:
|
| 45 |
self.root = root
|
| 46 |
self.root.title("HPC-Quantize V2 — Factored Q2_K & Geometry-Preserving Analog Engine")
|
| 47 |
-
self.root.geometry("
|
| 48 |
-
self.root.minsize(
|
| 49 |
|
| 50 |
self._log_queue: queue.Queue[tuple[str, object]] = queue.Queue()
|
| 51 |
self._stop_event = threading.Event()
|
| 52 |
self._worker: threading.Thread | None = None
|
| 53 |
self._reader: GGUFReader | None = None
|
| 54 |
self._imatrix: dict[str, np.ndarray] | None = None
|
|
|
|
|
|
|
| 55 |
|
| 56 |
self._init_vars()
|
| 57 |
self._apply_theme()
|
| 58 |
self._build_layout()
|
| 59 |
self._auto_populate_defaults()
|
| 60 |
self._update_cli_preview()
|
| 61 |
-
self.root.after(
|
| 62 |
|
| 63 |
def _init_vars(self) -> None:
|
| 64 |
self.gguf_var = tk.StringVar()
|
|
@@ -66,6 +75,7 @@ class HPCQuantizeGUI:
|
|
| 66 |
self.output_var = tk.StringVar()
|
| 67 |
self.mode_var = tk.StringVar(value="all")
|
| 68 |
self.filter_var = tk.StringVar()
|
|
|
|
| 69 |
|
| 70 |
self.optimizer_var = tk.StringVar(value="hpc")
|
| 71 |
self.cand_d_var = tk.IntVar(value=6)
|
|
@@ -91,7 +101,18 @@ class HPCQuantizeGUI:
|
|
| 91 |
|
| 92 |
self.cli_preview_var = tk.StringVar()
|
| 93 |
self.status_var = tk.StringVar(value="Ready.")
|
| 94 |
-
self.
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 95 |
|
| 96 |
for v in (
|
| 97 |
self.gguf_var,
|
|
@@ -120,6 +141,8 @@ class HPCQuantizeGUI:
|
|
| 120 |
style.configure("TLabelframe", background=bg, foreground=fg)
|
| 121 |
style.configure("TLabelframe.Label", background=bg, foreground="#93c5fd", font=("Sans", 10, "bold"))
|
| 122 |
style.configure("TLabel", background=bg, foreground=fg)
|
|
|
|
|
|
|
| 123 |
style.configure("TCheckbutton", background=bg, foreground=fg)
|
| 124 |
style.configure("TRadiobutton", background=bg, foreground=fg)
|
| 125 |
style.configure("TButton", background=panel, foreground=fg, padding=(10, 5))
|
|
@@ -130,7 +153,7 @@ class HPCQuantizeGUI:
|
|
| 130 |
background=panel,
|
| 131 |
fieldbackground=panel,
|
| 132 |
foreground=fg,
|
| 133 |
-
rowheight=
|
| 134 |
)
|
| 135 |
style.configure("Treeview.Heading", background="#262f3f", foreground=fg, font=("Sans", 9, "bold"))
|
| 136 |
style.map("Treeview", background=[("selected", accent)])
|
|
@@ -140,27 +163,27 @@ class HPCQuantizeGUI:
|
|
| 140 |
main.pack(fill=tk.BOTH, expand=True)
|
| 141 |
|
| 142 |
# 1. File Selection Frame
|
| 143 |
-
files_frame = ttk.LabelFrame(main, text="1. GGUF & iMatrix Paths", padding=
|
| 144 |
-
files_frame.pack(fill=tk.X, pady=(0,
|
| 145 |
files_frame.columnconfigure(1, weight=1)
|
| 146 |
|
| 147 |
-
ttk.Label(files_frame, text="Source GGUF:").grid(row=0, column=0, sticky=tk.W, padx=(0, 8), pady=
|
| 148 |
-
ttk.Entry(files_frame, textvariable=self.gguf_var).grid(row=0, column=1, sticky=tk.EW, pady=
|
| 149 |
-
ttk.Button(files_frame, text="Browse...", command=self._browse_gguf).grid(row=0, column=2, padx=4, pady=
|
| 150 |
-
ttk.Button(files_frame, text="Inspect GGUF", command=self.inspect_files).grid(row=0, column=3, pady=
|
| 151 |
|
| 152 |
-
ttk.Label(files_frame, text="iMatrix (.gguf/.dat):").grid(row=1, column=0, sticky=tk.W, padx=(0, 8), pady=
|
| 153 |
-
ttk.Entry(files_frame, textvariable=self.imatrix_var).grid(row=1, column=1, sticky=tk.EW, pady=
|
| 154 |
-
ttk.Button(files_frame, text="Browse...", command=self._browse_imatrix).grid(row=1, column=2, padx=4, pady=
|
| 155 |
-
ttk.Button(files_frame, text="Clear", command=lambda: self.imatrix_var.set("")).grid(row=1, column=3, pady=
|
| 156 |
|
| 157 |
-
ttk.Label(files_frame, text="Output (.gguf/.npz):").grid(row=2, column=0, sticky=tk.W, padx=(0, 8), pady=
|
| 158 |
-
ttk.Entry(files_frame, textvariable=self.output_var).grid(row=2, column=1, sticky=tk.EW, pady=
|
| 159 |
-
ttk.Button(files_frame, text="Save As...", command=self._browse_output).grid(row=2, column=2, padx=4, pady=
|
| 160 |
|
| 161 |
# 2. Middle Split: Left = Settings, Right = Tensor Browser
|
| 162 |
mid = ttk.PanedWindow(main, orient=tk.HORIZONTAL)
|
| 163 |
-
mid.pack(fill=tk.BOTH, expand=True, pady=(0,
|
| 164 |
|
| 165 |
left_pane = ttk.Frame(mid)
|
| 166 |
right_pane = ttk.Frame(mid)
|
|
@@ -168,11 +191,11 @@ class HPCQuantizeGUI:
|
|
| 168 |
mid.add(right_pane, weight=3)
|
| 169 |
|
| 170 |
# Left: Engine & Factorization Options
|
| 171 |
-
opt_frame = ttk.LabelFrame(left_pane, text="2. Factored Q2_K & Analog Controls", padding=
|
| 172 |
opt_frame.pack(fill=tk.BOTH, expand=True, padx=(0, 6))
|
| 173 |
|
| 174 |
mode_row = ttk.Frame(opt_frame)
|
| 175 |
-
mode_row.pack(fill=tk.X, pady=(0,
|
| 176 |
ttk.Label(mode_row, text="Target Scope:").pack(side=tk.LEFT, padx=(0, 8))
|
| 177 |
ttk.Radiobutton(
|
| 178 |
mode_row, text="All Q2_K-Eligible Tensors", variable=self.mode_var, value="all"
|
|
@@ -182,7 +205,7 @@ class HPCQuantizeGUI:
|
|
| 182 |
).pack(side=tk.LEFT, padx=4)
|
| 183 |
|
| 184 |
opt_row = ttk.Frame(opt_frame)
|
| 185 |
-
opt_row.pack(fill=tk.X, pady=(0,
|
| 186 |
ttk.Label(opt_row, text="Global Optimizer:").pack(side=tk.LEFT, padx=(0, 8))
|
| 187 |
ttk.Radiobutton(
|
| 188 |
opt_row, text="HPC (Sieve + 36-State Viterbi)", variable=self.optimizer_var, value="hpc"
|
|
@@ -192,40 +215,40 @@ class HPCQuantizeGUI:
|
|
| 192 |
).pack(side=tk.LEFT, padx=4)
|
| 193 |
|
| 194 |
checks = ttk.Frame(opt_frame)
|
| 195 |
-
checks.pack(fill=tk.X, pady=
|
| 196 |
ttk.Checkbutton(
|
| 197 |
checks,
|
| 198 |
text="Geometry-Preserving Weight Analogs (Iso-Moment + Vesica Gram + Subscale Lattice)",
|
| 199 |
variable=self.use_analogs_var,
|
| 200 |
-
).pack(anchor=tk.W, pady=
|
| 201 |
ttk.Checkbutton(
|
| 202 |
checks,
|
| 203 |
text="Coupled Orthogonal-Basis Bilinear Q2_K Factorization",
|
| 204 |
variable=self.coupled_q2k_var,
|
| 205 |
-
).pack(anchor=tk.W, pady=
|
| 206 |
ttk.Checkbutton(
|
| 207 |
checks,
|
| 208 |
text="Exact Joint (Ls, Lm) 16x16 Sub-block Scan",
|
| 209 |
variable=self.joint_subscales_var,
|
| 210 |
-
).pack(anchor=tk.W, pady=
|
| 211 |
ttk.Checkbutton(
|
| 212 |
checks,
|
| 213 |
text="Spectral Fold / Vesica / DC Objective Penalty",
|
| 214 |
variable=self.use_spectral_var,
|
| 215 |
-
).pack(anchor=tk.W, pady=
|
| 216 |
ttk.Checkbutton(
|
| 217 |
checks,
|
| 218 |
text="Sub-block 6-State Sieve & Rolling DC Residual Carry",
|
| 219 |
variable=self.subblock_sieve_var,
|
| 220 |
-
).pack(anchor=tk.W, pady=
|
| 221 |
ttk.Checkbutton(
|
| 222 |
checks,
|
| 223 |
text="Local-Safety Candidate & Factored Floors (Monotone Bound)",
|
| 224 |
variable=self.factor_floor_var,
|
| 225 |
-
).pack(anchor=tk.W, pady=
|
| 226 |
|
| 227 |
grid_f = ttk.Frame(opt_frame)
|
| 228 |
-
grid_f.pack(fill=tk.X, pady=
|
| 229 |
for col in range(4):
|
| 230 |
grid_f.columnconfigure(col, weight=1)
|
| 231 |
|
|
@@ -237,7 +260,7 @@ class HPCQuantizeGUI:
|
|
| 237 |
self._spin_pair(grid_f, 2, 2, "Tensor Limit (0=all):", self.limit_var, 0, 9999)
|
| 238 |
|
| 239 |
spec_f = ttk.LabelFrame(opt_frame, text="Spectral & DC Parameters", padding=6)
|
| 240 |
-
spec_f.pack(fill=tk.X, pady=
|
| 241 |
for col in range(4):
|
| 242 |
spec_f.columnconfigure(col, weight=1)
|
| 243 |
ttk.Label(spec_f, text="DC Lambda:").grid(row=0, column=0, sticky=tk.W, padx=4, pady=2)
|
|
@@ -250,7 +273,7 @@ class HPCQuantizeGUI:
|
|
| 250 |
ttk.Entry(spec_f, textvariable=self.sse_budget_var, width=8).grid(row=1, column=3, sticky=tk.EW, padx=4, pady=2)
|
| 251 |
|
| 252 |
# Right: GGUF Tensor Browser
|
| 253 |
-
browser_frame = ttk.LabelFrame(right_pane, text="3. GGUF Tensor Inspector", padding=
|
| 254 |
browser_frame.pack(fill=tk.BOTH, expand=True)
|
| 255 |
|
| 256 |
search_row = ttk.Frame(browser_frame)
|
|
@@ -263,7 +286,7 @@ class HPCQuantizeGUI:
|
|
| 263 |
search_row, text="Select Eligible", command=self._select_all_eligible
|
| 264 |
).pack(side=tk.LEFT, padx=4)
|
| 265 |
|
| 266 |
-
cols = ("name", "type", "dims", "row", "q2k", "imat")
|
| 267 |
self.tree = ttk.Treeview(browser_frame, columns=cols, show="headings", selectmode="extended")
|
| 268 |
self.tree.heading("name", text="Tensor Name")
|
| 269 |
self.tree.heading("type", text="Type")
|
|
@@ -271,13 +294,15 @@ class HPCQuantizeGUI:
|
|
| 271 |
self.tree.heading("row", text="Row Width")
|
| 272 |
self.tree.heading("q2k", text="Q2_K")
|
| 273 |
self.tree.heading("imat", text="iMatrix")
|
| 274 |
-
|
| 275 |
-
|
| 276 |
-
self.tree.column("
|
| 277 |
-
self.tree.column("
|
| 278 |
-
self.tree.column("
|
| 279 |
-
self.tree.column("
|
| 280 |
-
self.tree.column("
|
|
|
|
|
|
|
| 281 |
self.tree.bind("<<TreeviewSelect>>", lambda _e: self._update_cli_preview())
|
| 282 |
|
| 283 |
tree_scroll = ttk.Scrollbar(browser_frame, orient=tk.VERTICAL, command=self.tree.yview)
|
|
@@ -286,16 +311,50 @@ class HPCQuantizeGUI:
|
|
| 286 |
tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)
|
| 287 |
|
| 288 |
# 4. Equivalent CLI Command Preview
|
| 289 |
-
cli_frame = ttk.LabelFrame(main, text="4. Equivalent Terminal Command", padding=
|
| 290 |
-
cli_frame.pack(fill=tk.X, pady=(0,
|
| 291 |
ttk.Entry(cli_frame, textvariable=self.cli_preview_var, state="readonly").pack(
|
| 292 |
side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 6)
|
| 293 |
)
|
| 294 |
ttk.Button(cli_frame, text="Copy Command", command=self._copy_cli).pack(side=tk.RIGHT)
|
| 295 |
|
| 296 |
-
# 5.
|
| 297 |
-
bottom = ttk.LabelFrame(main, text="5.
|
| 298 |
-
bottom.pack(fill=tk.BOTH, expand=
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 299 |
|
| 300 |
btn_row = ttk.Frame(bottom)
|
| 301 |
btn_row.pack(fill=tk.X, pady=(0, 6))
|
|
@@ -310,22 +369,30 @@ class HPCQuantizeGUI:
|
|
| 310 |
self.bench_btn = ttk.Button(
|
| 311 |
btn_row, text="Run Solver Benchmark", command=self.start_benchmark
|
| 312 |
)
|
| 313 |
-
self.bench_btn.pack(side=tk.LEFT, padx=(0,
|
| 314 |
-
ttk.
|
|
|
|
|
|
|
| 315 |
|
| 316 |
-
|
| 317 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 318 |
|
| 319 |
log_frame = ttk.Frame(bottom)
|
| 320 |
log_frame.pack(fill=tk.BOTH, expand=True)
|
| 321 |
self.log_text = tk.Text(
|
| 322 |
log_frame,
|
| 323 |
-
height=
|
| 324 |
bg="#0f131a",
|
| 325 |
fg="#d1d5db",
|
| 326 |
insertbackground="#ffffff",
|
| 327 |
font=("Monospace", 9),
|
| 328 |
-
wrap=tk.
|
| 329 |
)
|
| 330 |
log_scroll = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview)
|
| 331 |
self.log_text.configure(yscrollcommand=log_scroll.set)
|
|
@@ -378,7 +445,7 @@ class HPCQuantizeGUI:
|
|
| 378 |
self.output_var.set(path)
|
| 379 |
|
| 380 |
def _log(self, msg: str) -> None:
|
| 381 |
-
self.log_text.insert(tk.END,
|
| 382 |
self.log_text.see(tk.END)
|
| 383 |
|
| 384 |
def build_config(self) -> HPCConfig:
|
|
@@ -495,6 +562,7 @@ class HPCQuantizeGUI:
|
|
| 495 |
ti.row_width,
|
| 496 |
"Yes" if elig else "No",
|
| 497 |
"Yes" if has_imat else "-",
|
|
|
|
| 498 |
),
|
| 499 |
)
|
| 500 |
|
|
@@ -514,7 +582,8 @@ class HPCQuantizeGUI:
|
|
| 514 |
def stop_worker(self) -> None:
|
| 515 |
if self._worker and self._worker.is_alive():
|
| 516 |
self._stop_event.set()
|
| 517 |
-
self.status_var.set("
|
|
|
|
| 518 |
|
| 519 |
def start_quantization(self) -> None:
|
| 520 |
if self._worker and self._worker.is_alive():
|
|
@@ -543,11 +612,19 @@ class HPCQuantizeGUI:
|
|
| 543 |
limit = int(self.limit_var.get())
|
| 544 |
|
| 545 |
self._stop_event.clear()
|
| 546 |
-
self.
|
|
|
|
|
|
|
|
|
|
| 547 |
self.run_btn.configure(state=tk.DISABLED)
|
| 548 |
self.bench_btn.configure(state=tk.DISABLED)
|
| 549 |
self.stop_btn.configure(state=tk.NORMAL)
|
| 550 |
-
self.status_var.set("
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 551 |
|
| 552 |
self._worker = threading.Thread(
|
| 553 |
target=self._quantize_worker,
|
|
@@ -569,20 +646,28 @@ class HPCQuantizeGUI:
|
|
| 569 |
try:
|
| 570 |
imat = None
|
| 571 |
if imat_path and Path(imat_path).exists():
|
|
|
|
| 572 |
imat = read_imatrix(imat_path)
|
| 573 |
-
self._log_queue.put(("log", f"Loaded iMatrix
|
| 574 |
|
| 575 |
quantizer = Q2KQuantizer(cfg)
|
| 576 |
if out_path.lower().endswith(".npz") and selected and len(selected) == 1:
|
| 577 |
reader = GGUFReader(gguf_path)
|
| 578 |
tname = next(iter(selected))
|
|
|
|
| 579 |
x, row = reader.read_tensor(tname)
|
| 580 |
imp = None
|
| 581 |
if imat is not None:
|
| 582 |
raw = lookup_imatrix(imat, tname)
|
| 583 |
if raw is not None:
|
| 584 |
imp = expand_importance(raw, x.size, row)
|
| 585 |
-
res = quantizer.quantize(
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 586 |
np.savez_compressed(
|
| 587 |
out_path,
|
| 588 |
scales=res.scales,
|
|
@@ -596,16 +681,11 @@ class HPCQuantizeGUI:
|
|
| 596 |
)
|
| 597 |
return
|
| 598 |
|
|
|
|
|
|
|
|
|
|
| 599 |
def on_progress(done: int, total: int, name: str, action: str, rmse: float | None, has_imat: bool):
|
| 600 |
-
|
| 601 |
-
self._log_queue.put(("progress", (pct, f"[{done}/{total}] {action} {name}")))
|
| 602 |
-
if action == "Q2_K" and rmse is not None:
|
| 603 |
-
self._log_queue.put(
|
| 604 |
-
(
|
| 605 |
-
"log",
|
| 606 |
-
f"[{done}/{total}] Q2_K {name:44s} RMSE={rmse:.6e} iMatrix={'yes' if has_imat else 'no'}",
|
| 607 |
-
)
|
| 608 |
-
)
|
| 609 |
|
| 610 |
stats = write_quantized_gguf(
|
| 611 |
gguf_path,
|
|
@@ -617,14 +697,15 @@ class HPCQuantizeGUI:
|
|
| 617 |
limit=limit,
|
| 618 |
verbose=False,
|
| 619 |
progress_callback=on_progress,
|
|
|
|
| 620 |
stop_event=self._stop_event,
|
| 621 |
)
|
| 622 |
-
|
| 623 |
self._log_queue.put(
|
| 624 |
(
|
| 625 |
"done",
|
| 626 |
f"Wrote {out_path}: {int(stats['quantized'])}/{int(stats['tensors'])} tensors Q2_K, "
|
| 627 |
-
f"overall RMSE={stats['rmse']:.6e} ({
|
| 628 |
)
|
| 629 |
)
|
| 630 |
except InterruptedError as exc:
|
|
@@ -641,7 +722,9 @@ class HPCQuantizeGUI:
|
|
| 641 |
messagebox.showerror("Invalid Configuration", str(exc))
|
| 642 |
return
|
| 643 |
self._stop_event.clear()
|
| 644 |
-
self.
|
|
|
|
|
|
|
| 645 |
self.run_btn.configure(state=tk.DISABLED)
|
| 646 |
self.bench_btn.configure(state=tk.DISABLED)
|
| 647 |
self.stop_btn.configure(state=tk.NORMAL)
|
|
@@ -662,10 +745,24 @@ class HPCQuantizeGUI:
|
|
| 662 |
w = np.ones(x.size, dtype=np.float32)
|
| 663 |
v2 = factor_only_solve(x, w, cfg)
|
| 664 |
ref_obj, _ = reference_solve(x, w, cfg, cfg.use_spectral)
|
| 665 |
-
|
| 666 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 667 |
|
| 668 |
-
self._log_queue.put(
|
|
|
|
|
|
|
| 669 |
for kind, vals in gains.items():
|
| 670 |
arr = np.asarray(vals)
|
| 671 |
self._log_queue.put(
|
|
@@ -681,6 +778,16 @@ class HPCQuantizeGUI:
|
|
| 681 |
except Exception as exc:
|
| 682 |
self._log_queue.put(("error", str(exc)))
|
| 683 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
def _poll_queue(self) -> None:
|
| 685 |
while True:
|
| 686 |
try:
|
|
@@ -689,20 +796,76 @@ class HPCQuantizeGUI:
|
|
| 689 |
break
|
| 690 |
if kind == "log":
|
| 691 |
self._log(str(payload))
|
| 692 |
-
elif kind == "
|
| 693 |
-
|
| 694 |
-
|
| 695 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 696 |
elif kind == "done":
|
| 697 |
-
self.
|
|
|
|
|
|
|
| 698 |
self.status_var.set(str(payload))
|
| 699 |
self._log(str(payload))
|
| 700 |
self._reset_buttons()
|
| 701 |
elif kind in ("cancelled", "error"):
|
|
|
|
| 702 |
self.status_var.set(f"{kind.upper()}: {payload}")
|
| 703 |
self._log(f"{kind.upper()}: {payload}")
|
| 704 |
self._reset_buttons()
|
| 705 |
-
self.root.after(
|
| 706 |
|
| 707 |
def _reset_buttons(self) -> None:
|
| 708 |
self.run_btn.configure(state=tk.NORMAL)
|
|
|
|
| 7 |
|
| 8 |
Provides:
|
| 9 |
* Source GGUF, iMatrix (.gguf / .dat), and Output (.gguf / .npz) file pickers
|
| 10 |
+
* Interactive GGUF tensor table with live per-tensor status & RMSE updates
|
| 11 |
+
* Real-time Telemetry Dashboard showing active tensor, current pipeline phase,
|
| 12 |
+
dual progress bars (overall model + intra-tensor superblocks), throughput
|
| 13 |
+
(blk/s), ETA, running RMSE, RMSE/sigma, Boltzmann temperature, and floor hits
|
| 14 |
* Full HPCConfig controls (Geometry-Preserving Analogs, Coupled Bilinear Q2_K,
|
| 15 |
Sieve + 36-state Viterbi, Spectral/Vesica/DC shaping, Candidate lattice size)
|
| 16 |
+
* Timestamped verbose log stream with selectable verbosity level
|
|
|
|
| 17 |
"""
|
| 18 |
|
| 19 |
from __future__ import annotations
|
| 20 |
|
| 21 |
import argparse
|
| 22 |
+
import datetime as dt
|
| 23 |
import queue
|
| 24 |
import shlex
|
| 25 |
import threading
|
|
|
|
| 43 |
from hpc_quantize_v2.reference import reference_solve, synthetic_cases
|
| 44 |
|
| 45 |
|
| 46 |
+
def _ts() -> str:
|
| 47 |
+
return dt.datetime.now().strftime("%H:%M:%S.%f")[:-3]
|
| 48 |
+
|
| 49 |
+
|
| 50 |
class HPCQuantizeGUI:
|
| 51 |
def __init__(self, root: tk.Tk) -> None:
|
| 52 |
self.root = root
|
| 53 |
self.root.title("HPC-Quantize V2 — Factored Q2_K & Geometry-Preserving Analog Engine")
|
| 54 |
+
self.root.geometry("1260x900")
|
| 55 |
+
self.root.minsize(1040, 740)
|
| 56 |
|
| 57 |
self._log_queue: queue.Queue[tuple[str, object]] = queue.Queue()
|
| 58 |
self._stop_event = threading.Event()
|
| 59 |
self._worker: threading.Thread | None = None
|
| 60 |
self._reader: GGUFReader | None = None
|
| 61 |
self._imatrix: dict[str, np.ndarray] | None = None
|
| 62 |
+
self._start_time: float | None = None
|
| 63 |
+
self._last_logged_phase: str = ""
|
| 64 |
|
| 65 |
self._init_vars()
|
| 66 |
self._apply_theme()
|
| 67 |
self._build_layout()
|
| 68 |
self._auto_populate_defaults()
|
| 69 |
self._update_cli_preview()
|
| 70 |
+
self.root.after(80, self._poll_queue)
|
| 71 |
|
| 72 |
def _init_vars(self) -> None:
|
| 73 |
self.gguf_var = tk.StringVar()
|
|
|
|
| 75 |
self.output_var = tk.StringVar()
|
| 76 |
self.mode_var = tk.StringVar(value="all")
|
| 77 |
self.filter_var = tk.StringVar()
|
| 78 |
+
self.log_level_var = tk.StringVar(value="ultra")
|
| 79 |
|
| 80 |
self.optimizer_var = tk.StringVar(value="hpc")
|
| 81 |
self.cand_d_var = tk.IntVar(value=6)
|
|
|
|
| 101 |
|
| 102 |
self.cli_preview_var = tk.StringVar()
|
| 103 |
self.status_var = tk.StringVar(value="Ready.")
|
| 104 |
+
self.model_progress_var = tk.DoubleVar(value=0.0)
|
| 105 |
+
self.tensor_progress_var = tk.DoubleVar(value=0.0)
|
| 106 |
+
|
| 107 |
+
# Live telemetry variables
|
| 108 |
+
self.tele_tensor_var = tk.StringVar(value="Idle")
|
| 109 |
+
self.tele_phase_var = tk.StringVar(value="Waiting to start")
|
| 110 |
+
self.tele_blocks_var = tk.StringVar(value="0 / 0")
|
| 111 |
+
self.tele_speed_var = tk.StringVar(value="0.0 blk/s (ETA --)")
|
| 112 |
+
self.tele_rmse_var = tk.StringVar(value="--")
|
| 113 |
+
self.tele_temp_var = tk.StringVar(value="--")
|
| 114 |
+
self.tele_floors_var = tk.StringVar(value="cand=0, factor=0")
|
| 115 |
+
self.tele_elapsed_var = tk.StringVar(value="0.0s")
|
| 116 |
|
| 117 |
for v in (
|
| 118 |
self.gguf_var,
|
|
|
|
| 141 |
style.configure("TLabelframe", background=bg, foreground=fg)
|
| 142 |
style.configure("TLabelframe.Label", background=bg, foreground="#93c5fd", font=("Sans", 10, "bold"))
|
| 143 |
style.configure("TLabel", background=bg, foreground=fg)
|
| 144 |
+
style.configure("TeleKey.TLabel", background=bg, foreground="#9ca3af", font=("Sans", 9))
|
| 145 |
+
style.configure("TeleVal.TLabel", background=bg, foreground="#60a5fa", font=("Monospace", 9, "bold"))
|
| 146 |
style.configure("TCheckbutton", background=bg, foreground=fg)
|
| 147 |
style.configure("TRadiobutton", background=bg, foreground=fg)
|
| 148 |
style.configure("TButton", background=panel, foreground=fg, padding=(10, 5))
|
|
|
|
| 153 |
background=panel,
|
| 154 |
fieldbackground=panel,
|
| 155 |
foreground=fg,
|
| 156 |
+
rowheight=23,
|
| 157 |
)
|
| 158 |
style.configure("Treeview.Heading", background="#262f3f", foreground=fg, font=("Sans", 9, "bold"))
|
| 159 |
style.map("Treeview", background=[("selected", accent)])
|
|
|
|
| 163 |
main.pack(fill=tk.BOTH, expand=True)
|
| 164 |
|
| 165 |
# 1. File Selection Frame
|
| 166 |
+
files_frame = ttk.LabelFrame(main, text="1. GGUF & iMatrix Paths", padding=8)
|
| 167 |
+
files_frame.pack(fill=tk.X, pady=(0, 6))
|
| 168 |
files_frame.columnconfigure(1, weight=1)
|
| 169 |
|
| 170 |
+
ttk.Label(files_frame, text="Source GGUF:").grid(row=0, column=0, sticky=tk.W, padx=(0, 8), pady=2)
|
| 171 |
+
ttk.Entry(files_frame, textvariable=self.gguf_var).grid(row=0, column=1, sticky=tk.EW, pady=2)
|
| 172 |
+
ttk.Button(files_frame, text="Browse...", command=self._browse_gguf).grid(row=0, column=2, padx=4, pady=2)
|
| 173 |
+
ttk.Button(files_frame, text="Inspect GGUF", command=self.inspect_files).grid(row=0, column=3, pady=2)
|
| 174 |
|
| 175 |
+
ttk.Label(files_frame, text="iMatrix (.gguf/.dat):").grid(row=1, column=0, sticky=tk.W, padx=(0, 8), pady=2)
|
| 176 |
+
ttk.Entry(files_frame, textvariable=self.imatrix_var).grid(row=1, column=1, sticky=tk.EW, pady=2)
|
| 177 |
+
ttk.Button(files_frame, text="Browse...", command=self._browse_imatrix).grid(row=1, column=2, padx=4, pady=2)
|
| 178 |
+
ttk.Button(files_frame, text="Clear", command=lambda: self.imatrix_var.set("")).grid(row=1, column=3, pady=2)
|
| 179 |
|
| 180 |
+
ttk.Label(files_frame, text="Output (.gguf/.npz):").grid(row=2, column=0, sticky=tk.W, padx=(0, 8), pady=2)
|
| 181 |
+
ttk.Entry(files_frame, textvariable=self.output_var).grid(row=2, column=1, sticky=tk.EW, pady=2)
|
| 182 |
+
ttk.Button(files_frame, text="Save As...", command=self._browse_output).grid(row=2, column=2, padx=4, pady=2)
|
| 183 |
|
| 184 |
# 2. Middle Split: Left = Settings, Right = Tensor Browser
|
| 185 |
mid = ttk.PanedWindow(main, orient=tk.HORIZONTAL)
|
| 186 |
+
mid.pack(fill=tk.BOTH, expand=True, pady=(0, 6))
|
| 187 |
|
| 188 |
left_pane = ttk.Frame(mid)
|
| 189 |
right_pane = ttk.Frame(mid)
|
|
|
|
| 191 |
mid.add(right_pane, weight=3)
|
| 192 |
|
| 193 |
# Left: Engine & Factorization Options
|
| 194 |
+
opt_frame = ttk.LabelFrame(left_pane, text="2. Factored Q2_K & Analog Controls", padding=8)
|
| 195 |
opt_frame.pack(fill=tk.BOTH, expand=True, padx=(0, 6))
|
| 196 |
|
| 197 |
mode_row = ttk.Frame(opt_frame)
|
| 198 |
+
mode_row.pack(fill=tk.X, pady=(0, 4))
|
| 199 |
ttk.Label(mode_row, text="Target Scope:").pack(side=tk.LEFT, padx=(0, 8))
|
| 200 |
ttk.Radiobutton(
|
| 201 |
mode_row, text="All Q2_K-Eligible Tensors", variable=self.mode_var, value="all"
|
|
|
|
| 205 |
).pack(side=tk.LEFT, padx=4)
|
| 206 |
|
| 207 |
opt_row = ttk.Frame(opt_frame)
|
| 208 |
+
opt_row.pack(fill=tk.X, pady=(0, 4))
|
| 209 |
ttk.Label(opt_row, text="Global Optimizer:").pack(side=tk.LEFT, padx=(0, 8))
|
| 210 |
ttk.Radiobutton(
|
| 211 |
opt_row, text="HPC (Sieve + 36-State Viterbi)", variable=self.optimizer_var, value="hpc"
|
|
|
|
| 215 |
).pack(side=tk.LEFT, padx=4)
|
| 216 |
|
| 217 |
checks = ttk.Frame(opt_frame)
|
| 218 |
+
checks.pack(fill=tk.X, pady=2)
|
| 219 |
ttk.Checkbutton(
|
| 220 |
checks,
|
| 221 |
text="Geometry-Preserving Weight Analogs (Iso-Moment + Vesica Gram + Subscale Lattice)",
|
| 222 |
variable=self.use_analogs_var,
|
| 223 |
+
).pack(anchor=tk.W, pady=1)
|
| 224 |
ttk.Checkbutton(
|
| 225 |
checks,
|
| 226 |
text="Coupled Orthogonal-Basis Bilinear Q2_K Factorization",
|
| 227 |
variable=self.coupled_q2k_var,
|
| 228 |
+
).pack(anchor=tk.W, pady=1)
|
| 229 |
ttk.Checkbutton(
|
| 230 |
checks,
|
| 231 |
text="Exact Joint (Ls, Lm) 16x16 Sub-block Scan",
|
| 232 |
variable=self.joint_subscales_var,
|
| 233 |
+
).pack(anchor=tk.W, pady=1)
|
| 234 |
ttk.Checkbutton(
|
| 235 |
checks,
|
| 236 |
text="Spectral Fold / Vesica / DC Objective Penalty",
|
| 237 |
variable=self.use_spectral_var,
|
| 238 |
+
).pack(anchor=tk.W, pady=1)
|
| 239 |
ttk.Checkbutton(
|
| 240 |
checks,
|
| 241 |
text="Sub-block 6-State Sieve & Rolling DC Residual Carry",
|
| 242 |
variable=self.subblock_sieve_var,
|
| 243 |
+
).pack(anchor=tk.W, pady=1)
|
| 244 |
ttk.Checkbutton(
|
| 245 |
checks,
|
| 246 |
text="Local-Safety Candidate & Factored Floors (Monotone Bound)",
|
| 247 |
variable=self.factor_floor_var,
|
| 248 |
+
).pack(anchor=tk.W, pady=1)
|
| 249 |
|
| 250 |
grid_f = ttk.Frame(opt_frame)
|
| 251 |
+
grid_f.pack(fill=tk.X, pady=4)
|
| 252 |
for col in range(4):
|
| 253 |
grid_f.columnconfigure(col, weight=1)
|
| 254 |
|
|
|
|
| 260 |
self._spin_pair(grid_f, 2, 2, "Tensor Limit (0=all):", self.limit_var, 0, 9999)
|
| 261 |
|
| 262 |
spec_f = ttk.LabelFrame(opt_frame, text="Spectral & DC Parameters", padding=6)
|
| 263 |
+
spec_f.pack(fill=tk.X, pady=2)
|
| 264 |
for col in range(4):
|
| 265 |
spec_f.columnconfigure(col, weight=1)
|
| 266 |
ttk.Label(spec_f, text="DC Lambda:").grid(row=0, column=0, sticky=tk.W, padx=4, pady=2)
|
|
|
|
| 273 |
ttk.Entry(spec_f, textvariable=self.sse_budget_var, width=8).grid(row=1, column=3, sticky=tk.EW, padx=4, pady=2)
|
| 274 |
|
| 275 |
# Right: GGUF Tensor Browser
|
| 276 |
+
browser_frame = ttk.LabelFrame(right_pane, text="3. GGUF Tensor Inspector & Live Status", padding=8)
|
| 277 |
browser_frame.pack(fill=tk.BOTH, expand=True)
|
| 278 |
|
| 279 |
search_row = ttk.Frame(browser_frame)
|
|
|
|
| 286 |
search_row, text="Select Eligible", command=self._select_all_eligible
|
| 287 |
).pack(side=tk.LEFT, padx=4)
|
| 288 |
|
| 289 |
+
cols = ("name", "type", "dims", "row", "q2k", "imat", "state")
|
| 290 |
self.tree = ttk.Treeview(browser_frame, columns=cols, show="headings", selectmode="extended")
|
| 291 |
self.tree.heading("name", text="Tensor Name")
|
| 292 |
self.tree.heading("type", text="Type")
|
|
|
|
| 294 |
self.tree.heading("row", text="Row Width")
|
| 295 |
self.tree.heading("q2k", text="Q2_K")
|
| 296 |
self.tree.heading("imat", text="iMatrix")
|
| 297 |
+
self.tree.heading("state", text="Live Status / RMSE")
|
| 298 |
+
|
| 299 |
+
self.tree.column("name", width=220, anchor=tk.W)
|
| 300 |
+
self.tree.column("type", width=52, anchor=tk.CENTER)
|
| 301 |
+
self.tree.column("dims", width=105, anchor=tk.CENTER)
|
| 302 |
+
self.tree.column("row", width=70, anchor=tk.E)
|
| 303 |
+
self.tree.column("q2k", width=48, anchor=tk.CENTER)
|
| 304 |
+
self.tree.column("imat", width=56, anchor=tk.CENTER)
|
| 305 |
+
self.tree.column("state", width=165, anchor=tk.W)
|
| 306 |
self.tree.bind("<<TreeviewSelect>>", lambda _e: self._update_cli_preview())
|
| 307 |
|
| 308 |
tree_scroll = ttk.Scrollbar(browser_frame, orient=tk.VERTICAL, command=self.tree.yview)
|
|
|
|
| 311 |
tree_scroll.pack(side=tk.RIGHT, fill=tk.Y)
|
| 312 |
|
| 313 |
# 4. Equivalent CLI Command Preview
|
| 314 |
+
cli_frame = ttk.LabelFrame(main, text="4. Equivalent Terminal Command", padding=6)
|
| 315 |
+
cli_frame.pack(fill=tk.X, pady=(0, 6))
|
| 316 |
ttk.Entry(cli_frame, textvariable=self.cli_preview_var, state="readonly").pack(
|
| 317 |
side=tk.LEFT, fill=tk.X, expand=True, padx=(0, 6)
|
| 318 |
)
|
| 319 |
ttk.Button(cli_frame, text="Copy Command", command=self._copy_cli).pack(side=tk.RIGHT)
|
| 320 |
|
| 321 |
+
# 5. Live Telemetry Dashboard + Execution + Verbose Console
|
| 322 |
+
bottom = ttk.LabelFrame(main, text="5. Live Telemetry Dashboard & Verbose Execution Stream", padding=8)
|
| 323 |
+
bottom.pack(fill=tk.BOTH, expand=True)
|
| 324 |
+
|
| 325 |
+
tele = ttk.Frame(bottom)
|
| 326 |
+
tele.pack(fill=tk.X, pady=(0, 6))
|
| 327 |
+
for col in (1, 3, 5, 7):
|
| 328 |
+
tele.columnconfigure(col, weight=1)
|
| 329 |
+
|
| 330 |
+
ttk.Label(tele, text="Active Tensor:", style="TeleKey.TLabel").grid(row=0, column=0, sticky=tk.W, padx=4, pady=1)
|
| 331 |
+
ttk.Label(tele, textvariable=self.tele_tensor_var, style="TeleVal.TLabel").grid(
|
| 332 |
+
row=0, column=1, columnspan=3, sticky=tk.W, padx=4, pady=1
|
| 333 |
+
)
|
| 334 |
+
ttk.Label(tele, text="Current Stage:", style="TeleKey.TLabel").grid(row=0, column=4, sticky=tk.W, padx=4, pady=1)
|
| 335 |
+
ttk.Label(tele, textvariable=self.tele_phase_var, style="TeleVal.TLabel").grid(
|
| 336 |
+
row=0, column=5, columnspan=3, sticky=tk.W, padx=4, pady=1
|
| 337 |
+
)
|
| 338 |
+
|
| 339 |
+
ttk.Label(tele, text="Superblocks:", style="TeleKey.TLabel").grid(row=1, column=0, sticky=tk.W, padx=4, pady=1)
|
| 340 |
+
ttk.Label(tele, textvariable=self.tele_blocks_var, style="TeleVal.TLabel").grid(row=1, column=1, sticky=tk.W, padx=4, pady=1)
|
| 341 |
+
ttk.Label(tele, text="Throughput:", style="TeleKey.TLabel").grid(row=1, column=2, sticky=tk.W, padx=4, pady=1)
|
| 342 |
+
ttk.Label(tele, textvariable=self.tele_speed_var, style="TeleVal.TLabel").grid(row=1, column=3, sticky=tk.W, padx=4, pady=1)
|
| 343 |
+
ttk.Label(tele, text="Running RMSE:", style="TeleKey.TLabel").grid(row=1, column=4, sticky=tk.W, padx=4, pady=1)
|
| 344 |
+
ttk.Label(tele, textvariable=self.tele_rmse_var, style="TeleVal.TLabel").grid(row=1, column=5, sticky=tk.W, padx=4, pady=1)
|
| 345 |
+
ttk.Label(tele, text="Sieve Temp / Floors:", style="TeleKey.TLabel").grid(row=1, column=6, sticky=tk.W, padx=4, pady=1)
|
| 346 |
+
ttk.Label(tele, textvariable=self.tele_floors_var, style="TeleVal.TLabel").grid(row=1, column=7, sticky=tk.W, padx=4, pady=1)
|
| 347 |
+
|
| 348 |
+
bars = ttk.Frame(bottom)
|
| 349 |
+
bars.pack(fill=tk.X, pady=(0, 6))
|
| 350 |
+
bars.columnconfigure(1, weight=1)
|
| 351 |
+
ttk.Label(bars, text="Model Progress:", style="TeleKey.TLabel").grid(row=0, column=0, sticky=tk.W, padx=(0, 6), pady=2)
|
| 352 |
+
self.model_progress = ttk.Progressbar(bars, variable=self.model_progress_var, maximum=100.0)
|
| 353 |
+
self.model_progress.grid(row=0, column=1, sticky=tk.EW, pady=2)
|
| 354 |
+
|
| 355 |
+
ttk.Label(bars, text="Tensor Blocks:", style="TeleKey.TLabel").grid(row=1, column=0, sticky=tk.W, padx=(0, 6), pady=2)
|
| 356 |
+
self.tensor_progress = ttk.Progressbar(bars, variable=self.tensor_progress_var, maximum=100.0)
|
| 357 |
+
self.tensor_progress.grid(row=1, column=1, sticky=tk.EW, pady=2)
|
| 358 |
|
| 359 |
btn_row = ttk.Frame(bottom)
|
| 360 |
btn_row.pack(fill=tk.X, pady=(0, 6))
|
|
|
|
| 369 |
self.bench_btn = ttk.Button(
|
| 370 |
btn_row, text="Run Solver Benchmark", command=self.start_benchmark
|
| 371 |
)
|
| 372 |
+
self.bench_btn.pack(side=tk.LEFT, padx=(0, 6))
|
| 373 |
+
ttk.Button(
|
| 374 |
+
btn_row, text="Clear Log", command=lambda: self.log_text.delete("1.0", tk.END)
|
| 375 |
+
).pack(side=tk.LEFT, padx=(0, 12))
|
| 376 |
|
| 377 |
+
ttk.Label(btn_row, text="Log Verbosity:").pack(side=tk.LEFT, padx=(0, 4))
|
| 378 |
+
ttk.Radiobutton(
|
| 379 |
+
btn_row, text="Ultra-Verbose (Every Block Step)", variable=self.log_level_var, value="ultra"
|
| 380 |
+
).pack(side=tk.LEFT, padx=2)
|
| 381 |
+
ttk.Radiobutton(
|
| 382 |
+
btn_row, text="Stage Transitions", variable=self.log_level_var, value="stages"
|
| 383 |
+
).pack(side=tk.LEFT, padx=2)
|
| 384 |
+
ttk.Label(btn_row, textvariable=self.status_var).pack(side=tk.RIGHT, padx=4)
|
| 385 |
|
| 386 |
log_frame = ttk.Frame(bottom)
|
| 387 |
log_frame.pack(fill=tk.BOTH, expand=True)
|
| 388 |
self.log_text = tk.Text(
|
| 389 |
log_frame,
|
| 390 |
+
height=11,
|
| 391 |
bg="#0f131a",
|
| 392 |
fg="#d1d5db",
|
| 393 |
insertbackground="#ffffff",
|
| 394 |
font=("Monospace", 9),
|
| 395 |
+
wrap=tk.NONE,
|
| 396 |
)
|
| 397 |
log_scroll = ttk.Scrollbar(log_frame, orient=tk.VERTICAL, command=self.log_text.yview)
|
| 398 |
self.log_text.configure(yscrollcommand=log_scroll.set)
|
|
|
|
| 445 |
self.output_var.set(path)
|
| 446 |
|
| 447 |
def _log(self, msg: str) -> None:
|
| 448 |
+
self.log_text.insert(tk.END, f"[{_ts()}] {msg}\n")
|
| 449 |
self.log_text.see(tk.END)
|
| 450 |
|
| 451 |
def build_config(self) -> HPCConfig:
|
|
|
|
| 562 |
ti.row_width,
|
| 563 |
"Yes" if elig else "No",
|
| 564 |
"Yes" if has_imat else "-",
|
| 565 |
+
"Pending (Q2_K)" if elig else "Passthrough",
|
| 566 |
),
|
| 567 |
)
|
| 568 |
|
|
|
|
| 582 |
def stop_worker(self) -> None:
|
| 583 |
if self._worker and self._worker.is_alive():
|
| 584 |
self._stop_event.set()
|
| 585 |
+
self.status_var.set("Stopping immediately...")
|
| 586 |
+
self._log("Cancel requested — halting active block loop...")
|
| 587 |
|
| 588 |
def start_quantization(self) -> None:
|
| 589 |
if self._worker and self._worker.is_alive():
|
|
|
|
| 612 |
limit = int(self.limit_var.get())
|
| 613 |
|
| 614 |
self._stop_event.clear()
|
| 615 |
+
self._start_time = time.time()
|
| 616 |
+
self._last_logged_phase = ""
|
| 617 |
+
self.model_progress_var.set(0.0)
|
| 618 |
+
self.tensor_progress_var.set(0.0)
|
| 619 |
self.run_btn.configure(state=tk.DISABLED)
|
| 620 |
self.bench_btn.configure(state=tk.DISABLED)
|
| 621 |
self.stop_btn.configure(state=tk.NORMAL)
|
| 622 |
+
self.status_var.set("Starting quantization...")
|
| 623 |
+
self._log(
|
| 624 |
+
f"Starting quantization: source={Path(gguf_path).name} -> output={Path(out_path).name} "
|
| 625 |
+
f"(cand={cfg.n_cand_d}x{cfg.n_cand_m}, analogs={cfg.use_weight_analogs}, "
|
| 626 |
+
f"coupled={cfg.factor_coupled_q2k}, optimizer={cfg.optimizer.upper()})"
|
| 627 |
+
)
|
| 628 |
|
| 629 |
self._worker = threading.Thread(
|
| 630 |
target=self._quantize_worker,
|
|
|
|
| 646 |
try:
|
| 647 |
imat = None
|
| 648 |
if imat_path and Path(imat_path).exists():
|
| 649 |
+
self._log_queue.put(("log", f"Loading iMatrix from {imat_path} ..."))
|
| 650 |
imat = read_imatrix(imat_path)
|
| 651 |
+
self._log_queue.put(("log", f"Loaded iMatrix: {len(imat)} tensor entries ready."))
|
| 652 |
|
| 653 |
quantizer = Q2KQuantizer(cfg)
|
| 654 |
if out_path.lower().endswith(".npz") and selected and len(selected) == 1:
|
| 655 |
reader = GGUFReader(gguf_path)
|
| 656 |
tname = next(iter(selected))
|
| 657 |
+
ti = reader.info(tname)
|
| 658 |
x, row = reader.read_tensor(tname)
|
| 659 |
imp = None
|
| 660 |
if imat is not None:
|
| 661 |
raw = lookup_imatrix(imat, tname)
|
| 662 |
if raw is not None:
|
| 663 |
imp = expand_importance(raw, x.size, row)
|
| 664 |
+
res = quantizer.quantize(
|
| 665 |
+
x,
|
| 666 |
+
importance=imp,
|
| 667 |
+
row_width=row,
|
| 668 |
+
status_callback=lambda ev: self._log_queue.put(("stage", (1, 1, ti, ev))),
|
| 669 |
+
stop_event=self._stop_event,
|
| 670 |
+
)
|
| 671 |
np.savez_compressed(
|
| 672 |
out_path,
|
| 673 |
scales=res.scales,
|
|
|
|
| 681 |
)
|
| 682 |
return
|
| 683 |
|
| 684 |
+
def on_stage(idx: int, total: int, ti, ev: dict):
|
| 685 |
+
self._log_queue.put(("stage", (idx, total, ti, ev)))
|
| 686 |
+
|
| 687 |
def on_progress(done: int, total: int, name: str, action: str, rmse: float | None, has_imat: bool):
|
| 688 |
+
self._log_queue.put(("tensor_done", (done, total, name, action, rmse, has_imat)))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 689 |
|
| 690 |
stats = write_quantized_gguf(
|
| 691 |
gguf_path,
|
|
|
|
| 697 |
limit=limit,
|
| 698 |
verbose=False,
|
| 699 |
progress_callback=on_progress,
|
| 700 |
+
stage_callback=on_stage,
|
| 701 |
stop_event=self._stop_event,
|
| 702 |
)
|
| 703 |
+
dt_sec = time.time() - t0
|
| 704 |
self._log_queue.put(
|
| 705 |
(
|
| 706 |
"done",
|
| 707 |
f"Wrote {out_path}: {int(stats['quantized'])}/{int(stats['tensors'])} tensors Q2_K, "
|
| 708 |
+
f"overall RMSE={stats['rmse']:.6e} ({dt_sec:.1f}s)",
|
| 709 |
)
|
| 710 |
)
|
| 711 |
except InterruptedError as exc:
|
|
|
|
| 722 |
messagebox.showerror("Invalid Configuration", str(exc))
|
| 723 |
return
|
| 724 |
self._stop_event.clear()
|
| 725 |
+
self._start_time = time.time()
|
| 726 |
+
self.model_progress_var.set(0.0)
|
| 727 |
+
self.tensor_progress_var.set(0.0)
|
| 728 |
self.run_btn.configure(state=tk.DISABLED)
|
| 729 |
self.bench_btn.configure(state=tk.DISABLED)
|
| 730 |
self.stop_btn.configure(state=tk.NORMAL)
|
|
|
|
| 745 |
w = np.ones(x.size, dtype=np.float32)
|
| 746 |
v2 = factor_only_solve(x, w, cfg)
|
| 747 |
ref_obj, _ = reference_solve(x, w, cfg, cfg.use_spectral)
|
| 748 |
+
gain_pct = (ref_obj - v2.objective) / max(abs(ref_obj), 1.0e-30) * 100.0
|
| 749 |
+
gains[kind].append(gain_pct / 100.0)
|
| 750 |
+
self._log_queue.put(
|
| 751 |
+
(
|
| 752 |
+
"bench_step",
|
| 753 |
+
(
|
| 754 |
+
idx,
|
| 755 |
+
total,
|
| 756 |
+
kind,
|
| 757 |
+
f"Case {idx}/{total} [{kind}]: ref={ref_obj:.5e} -> V2={v2.objective:.5e} "
|
| 758 |
+
f"(+{gain_pct:.2f}%, d={float(v2.d):.5f}, dmin={float(v2.dmin):.5f}, starts={v2.starts})",
|
| 759 |
+
),
|
| 760 |
+
)
|
| 761 |
+
)
|
| 762 |
|
| 763 |
+
self._log_queue.put(
|
| 764 |
+
("log", f"=== Summary (spectral={cfg.use_spectral}, analogs={cfg.use_weight_analogs}) ===")
|
| 765 |
+
)
|
| 766 |
for kind, vals in gains.items():
|
| 767 |
arr = np.asarray(vals)
|
| 768 |
self._log_queue.put(
|
|
|
|
| 778 |
except Exception as exc:
|
| 779 |
self._log_queue.put(("error", str(exc)))
|
| 780 |
|
| 781 |
+
def _update_tree_row_state(self, name: str, state_text: str, focus: bool = False) -> None:
|
| 782 |
+
if not self.tree.exists(name):
|
| 783 |
+
return
|
| 784 |
+
vals = list(self.tree.item(name, "values"))
|
| 785 |
+
if len(vals) >= 7:
|
| 786 |
+
vals[6] = state_text
|
| 787 |
+
self.tree.item(name, values=vals)
|
| 788 |
+
if focus:
|
| 789 |
+
self.tree.see(name)
|
| 790 |
+
|
| 791 |
def _poll_queue(self) -> None:
|
| 792 |
while True:
|
| 793 |
try:
|
|
|
|
| 796 |
break
|
| 797 |
if kind == "log":
|
| 798 |
self._log(str(payload))
|
| 799 |
+
elif kind == "stage":
|
| 800 |
+
idx, total, ti, ev = payload # type: ignore[misc]
|
| 801 |
+
phase = ev.get("phase", "")
|
| 802 |
+
done = int(ev.get("done", 0))
|
| 803 |
+
tot_blk = max(int(ev.get("total", 1)), 1)
|
| 804 |
+
detail = ev.get("detail", "")
|
| 805 |
+
blk_pct = 100.0 * done / tot_blk
|
| 806 |
+
overall_pct = 100.0 * ((idx - 1) + (done / tot_blk)) / max(total, 1)
|
| 807 |
+
|
| 808 |
+
self.model_progress_var.set(overall_pct)
|
| 809 |
+
self.tensor_progress_var.set(blk_pct)
|
| 810 |
+
self.tele_tensor_var.set(f"[{idx}/{total}] {ti.name} ({ti.type_name}, dims={ti.dims})")
|
| 811 |
+
self.tele_phase_var.set(phase)
|
| 812 |
+
self.tele_blocks_var.set(f"{done:,} / {tot_blk:,} ({blk_pct:.1f}%)")
|
| 813 |
+
|
| 814 |
+
if "rate" in ev and "eta" in ev:
|
| 815 |
+
self.tele_speed_var.set(f"{ev['rate']:.1f} blk/s (ETA {ev['eta']:.1f}s)")
|
| 816 |
+
if "rmse" in ev and "sigma" in ev:
|
| 817 |
+
r, s = float(ev["rmse"]), float(ev["sigma"])
|
| 818 |
+
self.tele_rmse_var.set(f"{r:.6e} (r/σ={r/max(s, 1e-15):.4f})")
|
| 819 |
+
if "temperature" in ev or "cand_floors" in ev:
|
| 820 |
+
t_val = ev.get("temperature", 0.0)
|
| 821 |
+
cf = ev.get("cand_floors", 0)
|
| 822 |
+
ff = ev.get("fac_floors", 0)
|
| 823 |
+
self.tele_floors_var.set(f"T={t_val:.2e} | cand={cf}, fac={ff}")
|
| 824 |
+
|
| 825 |
+
self.status_var.set(f"[{idx}/{total}] {ti.name} — {phase} ({blk_pct:.0f}%)")
|
| 826 |
+
self._update_tree_row_state(ti.name, f"{phase.split(':')[0]} ({blk_pct:.0f}%)", focus=True)
|
| 827 |
+
|
| 828 |
+
is_Copy = phase.startswith("Passthrough Copy")
|
| 829 |
+
phase_key = f"{ti.name}::{phase}"
|
| 830 |
+
if not is_Copy:
|
| 831 |
+
if self.log_level_var.get() == "ultra" or phase_key != self._last_logged_phase:
|
| 832 |
+
self._log(f"[{idx}/{total}] {ti.name} | {phase} | {detail}")
|
| 833 |
+
self._last_logged_phase = phase_key
|
| 834 |
+
elif kind == "tensor_done":
|
| 835 |
+
done, total, name, action, rmse, has_imat = payload # type: ignore[misc]
|
| 836 |
+
self.model_progress_var.set(100.0 * done / max(total, 1))
|
| 837 |
+
self.tensor_progress_var.set(100.0)
|
| 838 |
+
if action == "Q2_K" and rmse is not None:
|
| 839 |
+
self._update_tree_row_state(name, f"Q2_K (RMSE={rmse:.4e})")
|
| 840 |
+
self._log(
|
| 841 |
+
f"[{done}/{total}] FINISHED {name:42s} -> Q2_K RMSE={rmse:.6e} (iMatrix={'yes' if has_imat else 'no'})"
|
| 842 |
+
)
|
| 843 |
+
else:
|
| 844 |
+
self._update_tree_row_state(name, "Copied verbatim")
|
| 845 |
+
if self.log_level_var.get() == "ultra" or done % 25 == 0 or done == total:
|
| 846 |
+
self._log(f"[{done}/{total}] COPIED {name}")
|
| 847 |
+
elif kind == "bench_step":
|
| 848 |
+
idx, total, bkind, msg = payload # type: ignore[misc]
|
| 849 |
+
pct = 100.0 * idx / max(total, 1)
|
| 850 |
+
self.model_progress_var.set(pct)
|
| 851 |
+
self.tensor_progress_var.set(pct)
|
| 852 |
+
self.tele_tensor_var.set(f"Synthetic Benchmark ({bkind})")
|
| 853 |
+
self.tele_phase_var.set(f"Block {idx}/{total}")
|
| 854 |
+
self.tele_blocks_var.set(f"{idx} / {total}")
|
| 855 |
+
self._log(str(msg))
|
| 856 |
elif kind == "done":
|
| 857 |
+
self.model_progress_var.set(100.0)
|
| 858 |
+
self.tensor_progress_var.set(100.0)
|
| 859 |
+
self.tele_phase_var.set("Complete")
|
| 860 |
self.status_var.set(str(payload))
|
| 861 |
self._log(str(payload))
|
| 862 |
self._reset_buttons()
|
| 863 |
elif kind in ("cancelled", "error"):
|
| 864 |
+
self.tele_phase_var.set(kind.upper())
|
| 865 |
self.status_var.set(f"{kind.upper()}: {payload}")
|
| 866 |
self._log(f"{kind.upper()}: {payload}")
|
| 867 |
self._reset_buttons()
|
| 868 |
+
self.root.after(80, self._poll_queue)
|
| 869 |
|
| 870 |
def _reset_buttons(self) -> None:
|
| 871 |
self.run_btn.configure(state=tk.NORMAL)
|