Download viterbi.py from CompressedGemma/HPC-Quantize-V2: direct link, hf CLI and curl.
- Browser
- Download file 6.29 kB
-
https://huggingface.co/CompressedGemma/HPC-Quantize-V2/resolve/main/viterbi.py
- Command line
-
hf download hf://CompressedGemma/HPC-Quantize-V2/viterbi.py
-
curl -L -o viterbi.py https://huggingface.co/CompressedGemma/HPC-Quantize-V2/resolve/main/viterbi.py
6.29 kB
| """36-state Viterbi selection over the Q2 symbolic lattice. | |
| This is a direct port of Phase 3 of the C engine. Each graph block owns a | |
| ``6 x 6`` coarse/fine lattice; candidate errors are reduced to a free-energy | |
| per bin, the Sieve marginals become a log-prior bonus, and an exact DP finds | |
| the minimum-cost state sequence with a Manhattan transition penalty. | |
| Both the 576->36 bin reduction and the path->candidate mapping are fully | |
| vectorised by reshaping the ``(24, 24)`` candidate grid into ``(6, 4, 6, 4)``. | |
| """ | |
| from __future__ import annotations | |
| import numpy as np | |
| from .config import HPCConfig | |
| from .factored import CAND_TO_QUHIT, N_CAND_M, TOTAL_CANDIDATES | |
| N_STATES = 36 | |
| _BIN_CAND_MAP = ( | |
| np.arange(TOTAL_CANDIDATES, dtype=np.int64) | |
| .reshape(6, 4, 6, 4) | |
| .transpose(0, 2, 1, 3) | |
| .reshape(N_STATES, 16) | |
| ) | |
| def _median_upper(values: np.ndarray) -> float: | |
| arr = np.asarray(values) | |
| if arr.size == 0: | |
| return 0.0 | |
| k = arr.size // 2 | |
| return float(np.partition(arr, k)[k]) | |
| def robust_temperature( | |
| candidate_errors: np.ndarray, cfg: HPCConfig | |
| ) -> float: | |
| n_blocks = candidate_errors.shape[0] | |
| if n_blocks <= 0: | |
| return 1.0e-10 | |
| step = max(1, n_blocks // cfg.temperature_sample_blocks) | |
| medians = [] | |
| for b in range(0, n_blocks, step): | |
| row = candidate_errors[b] | |
| finite = row[np.isfinite(row)] | |
| if finite.size == 0: | |
| continue | |
| medians.append(_median_upper(np.maximum(0.0, row - finite.min()))) | |
| if len(medians) >= cfg.temperature_sample_blocks: | |
| break | |
| t = _median_upper(np.asarray(medians)) | |
| return t if np.isfinite(t) and t > 1.0e-10 else 1.0e-10 | |
| def _bin_geometry(): | |
| di = np.arange(TOTAL_CANDIDATES) // N_CAND_M | |
| mi = np.arange(TOTAL_CANDIDATES) % N_CAND_M | |
| qi_d = CAND_TO_QUHIT[di] | |
| qi_m = CAND_TO_QUHIT[mi] | |
| return qi_d, qi_m, qi_d * 6 + qi_m | |
| def viterbi_select( | |
| candidate_errors: np.ndarray, | |
| coarse_marg: np.ndarray, | |
| fine_marg: np.ndarray, | |
| temperature: float, | |
| stride: int, | |
| graph_blocks: int, | |
| cfg: HPCConfig, | |
| ) -> tuple[np.ndarray, np.ndarray]: | |
| n_blocks = candidate_errors.shape[0] | |
| temp = max(float(temperature), 1.0e-12) | |
| # Reshape (n_blocks, 576) -> view (n_blocks, 6, 4, 6, 4) without copying | |
| ce_f32 = np.asarray(candidate_errors, dtype=np.float32) | |
| ce_view = ce_f32.reshape(n_blocks, 6, 4, 6, 4) | |
| blk_bin_min = np.min(ce_view, axis=(2, 4)).reshape(n_blocks, N_STATES).astype(np.float64) | |
| ce_bins = ce_view.transpose(0, 1, 3, 2, 4).reshape(n_blocks, N_STATES, 16) | |
| blk_bin_arg = np.argmin(ce_bins, axis=2) | |
| blk_bin_cand = _BIN_CAND_MAP[np.arange(N_STATES)[None, :], blk_bin_arg] | |
| blk_bin_cand = np.where(np.isfinite(blk_bin_min), blk_bin_cand, -1) | |
| pad = graph_blocks * stride - n_blocks | |
| if pad > 0: | |
| pad_arr = np.full((pad, 6, 4, 6, 4), np.inf, dtype=np.float32) | |
| grouped = np.concatenate([ce_view, pad_arr], axis=0).reshape(graph_blocks, stride, 6, 4, 6, 4) | |
| else: | |
| grouped = ce_view.reshape(graph_blocks, stride, 6, 4, 6, 4) | |
| group_min_4d = np.min(grouped, axis=(1, 3, 5)) # (graph_blocks, 6, 6) | |
| group_min = group_min_4d.reshape(graph_blocks, N_STATES).astype(np.float64) | |
| finite = np.isfinite(grouped) | |
| counts = np.sum(finite, axis=(1, 3, 5)).reshape(graph_blocks, N_STATES) | |
| valid = (counts > 0) & np.isfinite(group_min) | |
| gm_b = np.where( np.isfinite(group_min_4d), group_min_4d, 0.0)[:, None, :, None, :, None] | |
| shifted = np.where(finite, (grouped - gm_b) / np.float32(2.0 * temp), np.inf) | |
| sums = np.sum(np.exp(-shifted), axis=(1, 3, 5), dtype=np.float64).reshape(graph_blocks, N_STATES) | |
| mean_exp = np.maximum(sums / np.maximum(counts, 1), 1.0e-300) | |
| bin_err = np.where(valid, group_min - 2.0 * temp * np.log(mean_exp), np.inf) | |
| rep_idx = np.arange(graph_blocks) * stride | |
| bin_cand = blk_bin_cand[rep_idx] # (graph_blocks, 36) | |
| c_tot = np.sum(coarse_marg, axis=1, keepdims=True) | |
| f_tot = np.sum(fine_marg, axis=1, keepdims=True) | |
| pc = np.where(c_tot > 1.0e-30, coarse_marg / np.maximum(c_tot, 1.0e-30), 1.0 / 6.0) | |
| pf = np.where(f_tot > 1.0e-30, fine_marg / np.maximum(f_tot, 1.0e-30), 1.0 / 6.0) | |
| log_prior = ( | |
| np.log(pc[:, :, None] + 1.0e-30) + np.log(pf[:, None, :] + 1.0e-30) | |
| ).reshape(graph_blocks, N_STATES) | |
| finite_err = bin_err[np.isfinite(bin_err)] | |
| scale_err = float(finite_err.mean()) if finite_err.size else 1.0e-10 | |
| scale_err = max(scale_err, 1.0e-20) | |
| local = np.where( | |
| np.isfinite(bin_err), | |
| bin_err - cfg.viterbi_beta * scale_err * log_prior, | |
| np.inf, | |
| ) | |
| state_d = np.arange(N_STATES) // 6 | |
| state_m = np.arange(N_STATES) % 6 | |
| trans = ( | |
| cfg.viterbi_alpha | |
| * scale_err | |
| * (np.abs(state_d[None, :] - state_d[:, None]) + np.abs(state_m[None, :] - state_m[:, None])) | |
| ) | |
| dp = np.full((graph_blocks, N_STATES), np.inf, dtype=np.float64) | |
| back = np.zeros((graph_blocks, N_STATES), dtype=np.int64) | |
| dp[0] = local[0] | |
| s_range = np.arange(N_STATES) | |
| for gi in range(1, graph_blocks): | |
| totals = dp[gi - 1][:, None] + trans | |
| best_sp = np.argmin(totals, axis=0) | |
| best_val = totals[best_sp, s_range] | |
| dp[gi] = np.where(np.isfinite(best_val), best_val + local[gi], np.inf) | |
| back[gi] = best_sp | |
| if not np.any(np.isfinite(dp[-1])): | |
| path = np.zeros(graph_blocks, dtype=np.int64) | |
| else: | |
| path = np.zeros(graph_blocks, dtype=np.int64) | |
| path[-1] = int(np.argmin(dp[-1])) | |
| for gi in range(graph_blocks - 2, -1, -1): | |
| path[gi] = back[gi + 1, path[gi + 1]] | |
| # Vectorized path -> block candidate mapping: | |
| blk_gi = np.arange(n_blocks) // stride | |
| chosen_state = path[blk_gi] | |
| chosen_cand = blk_bin_cand[np.arange(n_blocks), chosen_state] | |
| global_best_cand = np.argmin(candidate_errors, axis=1).astype(np.int64) | |
| best_candidate = np.where(chosen_cand >= 0, chosen_cand, global_best_cand) | |
| cur_err = candidate_errors[np.arange(n_blocks), best_candidate] | |
| g_err = candidate_errors[np.arange(n_blocks), global_best_cand] | |
| best_candidate = np.where( | |
| g_err < cur_err * cfg.greedy_override_ratio, global_best_cand, best_candidate | |
| ) | |
| return best_candidate, path | |