Upload 13 files
Browse files- __init__.py +10 -0
- analog.py +213 -0
- config.py +3 -0
- factored.py +121 -5
- pipeline.py +15 -12
__init__.py
CHANGED
|
@@ -6,6 +6,12 @@ Viterbi, spectral/DC shaping, local-safety floors) while replacing the
|
|
| 6 |
original factored solver with a monotone, multi-start, fp16-exact version.
|
| 7 |
"""
|
| 8 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
from .config import HPCConfig
|
| 10 |
from .factored import (
|
| 11 |
FactorResult,
|
|
@@ -26,6 +32,10 @@ __all__ = [
|
|
| 26 |
"QuantizedTensor",
|
| 27 |
"TensorStats",
|
| 28 |
"QK_K",
|
|
|
|
|
|
|
|
|
|
|
|
|
| 29 |
"dequantize_params",
|
| 30 |
"factor_1d",
|
| 31 |
"factor_candidates",
|
|
|
|
| 6 |
original factored solver with a monotone, multi-start, fp16-exact version.
|
| 7 |
"""
|
| 8 |
|
| 9 |
+
from .analog import (
|
| 10 |
+
build_block_analogs,
|
| 11 |
+
subblock_isomoment_analog,
|
| 12 |
+
subscale_lattice_analog,
|
| 13 |
+
vesica_gram_analog,
|
| 14 |
+
)
|
| 15 |
from .config import HPCConfig
|
| 16 |
from .factored import (
|
| 17 |
FactorResult,
|
|
|
|
| 32 |
"QuantizedTensor",
|
| 33 |
"TensorStats",
|
| 34 |
"QK_K",
|
| 35 |
+
"build_block_analogs",
|
| 36 |
+
"subblock_isomoment_analog",
|
| 37 |
+
"subscale_lattice_analog",
|
| 38 |
+
"vesica_gram_analog",
|
| 39 |
"dequantize_params",
|
| 40 |
"factor_1d",
|
| 41 |
"factor_candidates",
|
analog.py
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Geometry-preserving weight analogs for Q2_K bilinear factorization.
|
| 2 |
+
|
| 3 |
+
At 2 bits per weight (4 levels ``q in {0, 1, 2, 3}`` and 4-bit sub-block
|
| 4 |
+
scales ``Ls, Lm in {0..15}``), raw weight blocks suffer from two structural
|
| 5 |
+
bottlenecks:
|
| 6 |
+
|
| 7 |
+
1. **Intra-sub-block outlier collapse**: a single heavy-tailed weight stretches
|
| 8 |
+
the sub-block span ``xmax - xmin``, forcing 13-15 of the 16 weights into
|
| 9 |
+
one or two quantization bins.
|
| 10 |
+
2. **Inter-sub-block scale crushing**: a single high-variance sub-block inflates
|
| 11 |
+
the shared superblock scale ``d``, collapsing ``Ls_j`` of the remaining
|
| 12 |
+
15 sub-blocks toward small integers with severe rounding loss.
|
| 13 |
+
|
| 14 |
+
Rather than factoring raw weights alone, V2 constructs a bank of
|
| 15 |
+
**geometry-preserving weight analogs** ``x_tilde`` of the eligible weights:
|
| 16 |
+
|
| 17 |
+
* :func:`subblock_isomoment_analog` maps each 16-weight sub-block through an
|
| 18 |
+
importance-weighted saturating transport blended with the optimal 4-level
|
| 19 |
+
quantile spacing, then orthogonally projects back onto the exact weighted
|
| 20 |
+
mean ``mu_j`` and weighted variance ``sigma_j^2`` of the original sub-block.
|
| 21 |
+
* :func:`vesica_gram_analog` operates in the D6 / Vesica fold basis
|
| 22 |
+
``(x_p + x_{p+128}, x_p - x_{p+128})``, regularizing outliers in the whitened
|
| 23 |
+
eigen-basis and coloring back so the exact 2x2 weighted Gram matrix and means
|
| 24 |
+
of the two half-blocks are preserved.
|
| 25 |
+
* :func:`subscale_lattice_analog` tempers cross-sub-block dispersion toward the
|
| 26 |
+
4-bit ``{0..15}`` scale lattice while preserving every sub-block's unit
|
| 27 |
+
direction, mean, and total block weighted energy.
|
| 28 |
+
|
| 29 |
+
The discrete Q2_K lattice coordinates ``(Ls, Lm, q)`` discovered on each analog
|
| 30 |
+
are then projected back onto the **true weights** ``x`` via coupled bilinear
|
| 31 |
+
least-squares, guaranteeing that the final emitted block is strictly optimal
|
| 32 |
+
under the true objective.
|
| 33 |
+
"""
|
| 34 |
+
|
| 35 |
+
from __future__ import annotations
|
| 36 |
+
|
| 37 |
+
import numpy as np
|
| 38 |
+
|
| 39 |
+
from .q2k import NS, QK_K
|
| 40 |
+
|
| 41 |
+
_Q2_IDEAL_QUANTILES = np.linspace(-1.35, 1.35, 16, dtype=np.float64)
|
| 42 |
+
|
| 43 |
+
|
| 44 |
+
def subblock_isomoment_analog(
|
| 45 |
+
x: np.ndarray,
|
| 46 |
+
w: np.ndarray | None = None,
|
| 47 |
+
tau: float = 1.65,
|
| 48 |
+
beta: float = 0.25,
|
| 49 |
+
quantile_blend: float = 0.30,
|
| 50 |
+
enforce_nonpositive_min: bool = False,
|
| 51 |
+
) -> np.ndarray:
|
| 52 |
+
"""Create a sub-block iso-moment analog preserving weighted mean & variance.
|
| 53 |
+
|
| 54 |
+
Invariants per 16-weight sub-block ``j`` (when ``enforce_nonpositive_min``
|
| 55 |
+
is ``False``):
|
| 56 |
+
* ``sum_k w_{j,k} x_tilde_{j,k} == sum_k w_{j,k} x_{j,k}``
|
| 57 |
+
* ``sum_k w_{j,k} (x_tilde_{j,k} - mu_j)^2 == sum_k w_{j,k} (x_{j,k} - mu_j)^2``
|
| 58 |
+
* strict rank-order preservation with respect to ``x_{j,k}`` (when ``beta == 0``)
|
| 59 |
+
"""
|
| 60 |
+
xb = np.asarray(x, dtype=np.float64).reshape(NS, 16)
|
| 61 |
+
wb = (
|
| 62 |
+
np.ones((NS, 16), dtype=np.float64)
|
| 63 |
+
if w is None
|
| 64 |
+
else np.asarray(w, dtype=np.float64).reshape(NS, 16)
|
| 65 |
+
)
|
| 66 |
+
wsum = np.maximum(np.sum(wb, axis=1, keepdims=True), 1.0e-30)
|
| 67 |
+
mu = np.sum(wb * xb, axis=1, keepdims=True) / wsum
|
| 68 |
+
centered = xb - mu
|
| 69 |
+
var = np.sum(wb * centered * centered, axis=1, keepdims=True) / wsum
|
| 70 |
+
sigma = np.sqrt(np.maximum(var, 1.0e-30))
|
| 71 |
+
active = (sigma[:, 0] >= 1.0e-15)
|
| 72 |
+
|
| 73 |
+
z = centered / sigma
|
| 74 |
+
w_mean = wsum / 16.0
|
| 75 |
+
w_rel = np.power(np.maximum(wb / w_mean, 1.0e-12), beta) if beta != 0.0 else 1.0
|
| 76 |
+
z_comp = tau * np.tanh((z * w_rel) / max(tau, 1.0e-6))
|
| 77 |
+
|
| 78 |
+
if quantile_blend > 0.0:
|
| 79 |
+
order = np.argsort(z, axis=1, kind="stable")
|
| 80 |
+
z_quant = np.empty_like(z)
|
| 81 |
+
rows = np.arange(NS)[:, None]
|
| 82 |
+
z_quant[rows, order] = _Q2_IDEAL_QUANTILES[None, :]
|
| 83 |
+
z_analog = (1.0 - quantile_blend) * z_comp + quantile_blend * z_quant
|
| 84 |
+
else:
|
| 85 |
+
z_analog = z_comp
|
| 86 |
+
|
| 87 |
+
mu_a = np.sum(wb * z_analog, axis=1, keepdims=True) / wsum
|
| 88 |
+
z_analog = z_analog - mu_a
|
| 89 |
+
var_a = np.sum(wb * z_analog * z_analog, axis=1, keepdims=True) / wsum
|
| 90 |
+
scale = np.where(var_a > 1.0e-30, np.sqrt(var / np.maximum(var_a, 1.0e-30)), 0.0)
|
| 91 |
+
out = np.where(active[:, None], mu + z_analog * scale, xb)
|
| 92 |
+
|
| 93 |
+
if enforce_nonpositive_min:
|
| 94 |
+
orig_min = np.min(xb, axis=1)
|
| 95 |
+
new_min = np.min(out, axis=1)
|
| 96 |
+
shift = np.where((orig_min <= 0.0) & (new_min > 0.0), new_min, 0.0)
|
| 97 |
+
out = out - shift[:, None]
|
| 98 |
+
|
| 99 |
+
return out.reshape(QK_K).astype(np.float32)
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
def vesica_gram_analog(
|
| 103 |
+
x: np.ndarray,
|
| 104 |
+
w: np.ndarray | None = None,
|
| 105 |
+
tau: float = 1.9,
|
| 106 |
+
) -> np.ndarray:
|
| 107 |
+
"""Create a D6 / Vesica fold analog preserving the 2x2 half-block Gram matrix.
|
| 108 |
+
|
| 109 |
+
Decomposes ``x`` into symmetric ``v = 0.5*(x[:128] + x[128:])`` and
|
| 110 |
+
antisymmetric ``u = 0.5*(x[:128] - x[128:])`` modes under pair weights
|
| 111 |
+
``wp = 0.5*(w[:128] + w[128:])``. Whitens ``(v, u)``, applies smooth
|
| 112 |
+
hyperbolic saturation in the principal axes, and colors back with the exact
|
| 113 |
+
2x2 covariance square-root so ``Gram(v_tilde, u_tilde) == Gram(v, u)``.
|
| 114 |
+
"""
|
| 115 |
+
x_f64 = np.asarray(x, dtype=np.float64).reshape(QK_K)
|
| 116 |
+
w_f64 = (
|
| 117 |
+
np.ones(QK_K, dtype=np.float64)
|
| 118 |
+
if w is None
|
| 119 |
+
else np.asarray(w, dtype=np.float64).reshape(QK_K)
|
| 120 |
+
)
|
| 121 |
+
half = QK_K // 2
|
| 122 |
+
x0, x1 = x_f64[:half], x_f64[half:]
|
| 123 |
+
wp = np.maximum(0.5 * (w_f64[:half] + w_f64[half:]), 1.0e-30)
|
| 124 |
+
wsum = float(np.sum(wp))
|
| 125 |
+
|
| 126 |
+
v = 0.5 * (x0 + x1)
|
| 127 |
+
u = 0.5 * (x0 - x1)
|
| 128 |
+
mv = float(np.sum(wp * v) / wsum)
|
| 129 |
+
mu = float(np.sum(wp * u) / wsum)
|
| 130 |
+
Y = np.vstack([v - mv, u - mu]) # (2, 128)
|
| 131 |
+
|
| 132 |
+
G = (Y * wp[None, :]) @ Y.T / wsum # 2x2 weighted covariance
|
| 133 |
+
evals, evecs = np.linalg.eigh(G)
|
| 134 |
+
if float(np.max(evals)) < 1.0e-28:
|
| 135 |
+
return x_f64.astype(np.float32)
|
| 136 |
+
|
| 137 |
+
pos = evals > 1.0e-28
|
| 138 |
+
inv_sqrt = np.where(pos, 1.0 / np.sqrt(np.maximum(evals, 1.0e-30)), 0.0)
|
| 139 |
+
W_white = (evecs * inv_sqrt[None, :]) @ evecs.T
|
| 140 |
+
Z = W_white @ Y # whitened (2, 128)
|
| 141 |
+
|
| 142 |
+
Z_comp = tau * np.tanh(Z / max(tau, 1.0e-6))
|
| 143 |
+
Z_mean = np.sum(Z_comp * wp[None, :], axis=1, keepdims=True) / wsum
|
| 144 |
+
Z_comp = Z_comp - Z_mean
|
| 145 |
+
|
| 146 |
+
G_z = (Z_comp * wp[None, :]) @ Z_comp.T / wsum
|
| 147 |
+
ez, Vz = np.linalg.eigh(G_z)
|
| 148 |
+
pos_z = ez > 1.0e-28
|
| 149 |
+
if not np.all(pos_z):
|
| 150 |
+
return x_f64.astype(np.float32)
|
| 151 |
+
inv_sqrt_z = np.where(pos_z, 1.0 / np.sqrt(np.maximum(ez, 1.0e-30)), 0.0)
|
| 152 |
+
Z_orth = (Vz * inv_sqrt_z[None, :]) @ Vz.T @ Z_comp
|
| 153 |
+
|
| 154 |
+
sqrt_G = (evecs * np.sqrt(np.maximum(evals, 0.0))[None, :]) @ evecs.T
|
| 155 |
+
Y_tilde = sqrt_G @ Z_orth
|
| 156 |
+
v_new = mv + Y_tilde[0]
|
| 157 |
+
u_new = mu + Y_tilde[1]
|
| 158 |
+
return np.concatenate([v_new + u_new, v_new - u_new]).astype(np.float32)
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def subscale_lattice_analog(
|
| 162 |
+
x: np.ndarray,
|
| 163 |
+
w: np.ndarray | None = None,
|
| 164 |
+
gamma: float = 0.65,
|
| 165 |
+
) -> np.ndarray:
|
| 166 |
+
"""Create a cross-sub-block analog whose sub-block dispersions fit 4-bit scales.
|
| 167 |
+
|
| 168 |
+
Preserves each sub-block's mean ``mu_j`` and unit direction
|
| 169 |
+
``(x_j - mu_j) / sigma_j``, while tempering extreme sub-block standard
|
| 170 |
+
deviations ``sigma_j`` toward the superblock root-mean-square dispersion
|
| 171 |
+
and rescaling to preserve total centered weighted energy.
|
| 172 |
+
"""
|
| 173 |
+
xb = np.asarray(x, dtype=np.float64).reshape(NS, 16)
|
| 174 |
+
wb = (
|
| 175 |
+
np.ones((NS, 16), dtype=np.float64)
|
| 176 |
+
if w is None
|
| 177 |
+
else np.asarray(w, dtype=np.float64).reshape(NS, 16)
|
| 178 |
+
)
|
| 179 |
+
wsum = np.maximum(np.sum(wb, axis=1, keepdims=True), 1.0e-30)
|
| 180 |
+
mu = np.sum(wb * xb, axis=1, keepdims=True) / wsum
|
| 181 |
+
centered = xb - mu
|
| 182 |
+
energy_j = np.sum(wb * centered * centered, axis=1, keepdims=True)
|
| 183 |
+
total_energy = float(np.sum(energy_j))
|
| 184 |
+
if total_energy <= 1.0e-30:
|
| 185 |
+
return xb.reshape(QK_K).astype(np.float32)
|
| 186 |
+
|
| 187 |
+
sigma = np.sqrt(np.maximum(energy_j / wsum, 1.0e-30))
|
| 188 |
+
sigma_ref = float(np.sqrt(total_energy / max(float(np.sum(wsum)), 1.0e-30)))
|
| 189 |
+
target_sigma = np.power(np.maximum(sigma, 1.0e-30), gamma) * (
|
| 190 |
+
sigma_ref ** (1.0 - gamma)
|
| 191 |
+
)
|
| 192 |
+
scaled = np.where(sigma > 1.0e-15, centered * (target_sigma / sigma), 0.0)
|
| 193 |
+
new_energy = float(np.sum(wb * scaled * scaled))
|
| 194 |
+
if new_energy > 1.0e-30:
|
| 195 |
+
scaled = scaled * np.sqrt(total_energy / new_energy)
|
| 196 |
+
out = mu + scaled
|
| 197 |
+
return out.reshape(QK_K).astype(np.float32)
|
| 198 |
+
|
| 199 |
+
|
| 200 |
+
def build_block_analogs(
|
| 201 |
+
x: np.ndarray,
|
| 202 |
+
w: np.ndarray | None = None,
|
| 203 |
+
) -> list[np.ndarray]:
|
| 204 |
+
"""Return the portfolio of geometry-preserving weight analogs for a block."""
|
| 205 |
+
a1 = subblock_isomoment_analog(
|
| 206 |
+
x, w, tau=1.60, beta=0.25, quantile_blend=0.30, enforce_nonpositive_min=True
|
| 207 |
+
)
|
| 208 |
+
a2 = subblock_isomoment_analog(
|
| 209 |
+
x, w, tau=2.15, beta=0.15, quantile_blend=0.15, enforce_nonpositive_min=True
|
| 210 |
+
)
|
| 211 |
+
a3 = vesica_gram_analog(x, w, tau=1.85)
|
| 212 |
+
a4 = subscale_lattice_analog(a1, w, gamma=0.70)
|
| 213 |
+
return [a1, a2, a3, a4]
|
config.py
CHANGED
|
@@ -33,6 +33,9 @@ class HPCConfig:
|
|
| 33 |
factor_portfolio: bool = True
|
| 34 |
factor_spectral_refine: bool = True
|
| 35 |
factor_exact_emit: bool = True
|
|
|
|
|
|
|
|
|
|
| 36 |
|
| 37 |
# ---- objective ------------------------------------------------------
|
| 38 |
use_spectral: bool = True
|
|
|
|
| 33 |
factor_portfolio: bool = True
|
| 34 |
factor_spectral_refine: bool = True
|
| 35 |
factor_exact_emit: bool = True
|
| 36 |
+
use_weight_analogs: bool = True
|
| 37 |
+
factor_coupled_q2k: bool = True
|
| 38 |
+
factor_joint_subscales: bool = True
|
| 39 |
|
| 40 |
# ---- objective ------------------------------------------------------
|
| 41 |
use_spectral: bool = True
|
factored.py
CHANGED
|
@@ -28,6 +28,7 @@ from dataclasses import dataclass
|
|
| 28 |
|
| 29 |
import numpy as np
|
| 30 |
|
|
|
|
| 31 |
from .config import HPCConfig
|
| 32 |
from .fp16 import from_fp16_bits, fp16_ulp_neighbors, nearest_int, round_fp16, to_fp16_bits
|
| 33 |
from .q2k import NS, QK_K, codes_from_params, derive_subscales
|
|
@@ -298,6 +299,83 @@ def _ls_init(x, w, cfg: HPCConfig, legacy_1d: bool = False):
|
|
| 298 |
return d, m, ls, lm, codes
|
| 299 |
|
| 300 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 301 |
def _sub_ls(x, w, codes):
|
| 302 |
x = np.asarray(x, dtype=np.float64)
|
| 303 |
w = np.asarray(w, dtype=np.float64)
|
|
@@ -350,14 +428,26 @@ def _solve(
|
|
| 350 |
best = (obj, d, m, ls.copy(), lm.copy(), codes.copy())
|
| 351 |
stall = 0
|
| 352 |
iters = 0
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 353 |
for it in range(max(1, max_iter)):
|
| 354 |
iters = it + 1
|
| 355 |
codes = codes_from_params(x, d, m, ls, lm)
|
| 356 |
s, t = _sub_ls(x, w, codes)
|
| 357 |
q = codes.astype(np.float64).reshape(NS, 16)
|
| 358 |
-
|
| 359 |
-
|
| 360 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 361 |
|
| 362 |
if pin & 1:
|
| 363 |
if d > 1.0e-30:
|
|
@@ -368,16 +458,39 @@ def _solve(
|
|
| 368 |
d_new, ls_new = _factor_1d_for(s, ws, 15, cfg, legacy_1d)
|
| 369 |
d, ls = float(d_new), ls_new
|
| 370 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 371 |
if pin & 2:
|
| 372 |
if m > 1.0e-30:
|
| 373 |
-
lm = np.clip(nearest_int(
|
| 374 |
else:
|
| 375 |
lm = np.zeros(NS, dtype=np.uint8)
|
| 376 |
else:
|
| 377 |
-
m_new, lm_new = _factor_1d_for(
|
| 378 |
m, lm = float(m_new), lm_new
|
| 379 |
|
| 380 |
codes = codes_from_params(x, d, m, ls, lm)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 381 |
cur, _ = objective(x, w, d, m, ls, lm, codes, cfg, dc_carry)
|
| 382 |
if cur < best[0] - 1.0e-15:
|
| 383 |
best = (cur, d, m, ls.copy(), lm.copy(), codes.copy())
|
|
@@ -465,6 +578,9 @@ def factor_q2k_block(
|
|
| 465 |
out.append((d, m, ls, lm, codes))
|
| 466 |
if starts > 1:
|
| 467 |
out.append(_ls_init(x, w, cfg, legacy_1d))
|
|
|
|
|
|
|
|
|
|
| 468 |
return out
|
| 469 |
|
| 470 |
best = None
|
|
|
|
| 28 |
|
| 29 |
import numpy as np
|
| 30 |
|
| 31 |
+
from .analog import build_block_analogs
|
| 32 |
from .config import HPCConfig
|
| 33 |
from .fp16 import from_fp16_bits, fp16_ulp_neighbors, nearest_int, round_fp16, to_fp16_bits
|
| 34 |
from .q2k import NS, QK_K, codes_from_params, derive_subscales
|
|
|
|
| 299 |
return d, m, ls, lm, codes
|
| 300 |
|
| 301 |
|
| 302 |
+
def _analog_init(x, w, x_analog, cfg: HPCConfig):
|
| 303 |
+
"""Extract discrete Q2_K lattice coordinates from a geometry-preserving analog
|
| 304 |
+
and project continuous scales onto the true weights ``x``."""
|
| 305 |
+
_, _, _, _, analog_codes = _range_init(x_analog, w, cfg, False)
|
| 306 |
+
s, t = _sub_ls(x, w, analog_codes)
|
| 307 |
+
q = analog_codes.astype(np.float64).reshape(NS, 16)
|
| 308 |
+
wb = np.asarray(w, dtype=np.float64).reshape(NS, 16)
|
| 309 |
+
xb = np.asarray(x, dtype=np.float64).reshape(NS, 16)
|
| 310 |
+
Sw = np.sum(wb, axis=1)
|
| 311 |
+
Swq = np.sum(wb * q, axis=1)
|
| 312 |
+
Sqq = np.sum(wb * q * q, axis=1)
|
| 313 |
+
Sx = np.sum(wb * xb, axis=1)
|
| 314 |
+
q_bar = np.where(Sw > 1.0e-30, Swq / np.maximum(Sw, 1e-30), 0.0)
|
| 315 |
+
Vqq = np.maximum(Sqq - Swq * q_bar, 1.0e-12 * Sw)
|
| 316 |
+
d, ls = _factor_1d_for(s, Vqq, 15, cfg, False)
|
| 317 |
+
t_coupled = np.maximum(
|
| 318 |
+
0.0, (d * ls.astype(np.float64) * Swq - Sx) / np.maximum(Sw, 1e-30)
|
| 319 |
+
)
|
| 320 |
+
m, lm = _factor_1d_for(t_coupled, Sw, 15, cfg, False)
|
| 321 |
+
codes = codes_from_params(x, d, m, ls, lm)
|
| 322 |
+
return d, m, ls, lm, codes
|
| 323 |
+
|
| 324 |
+
|
| 325 |
+
def _joint_subscale_scan(x, w, d: float, m: float) -> tuple[np.ndarray, np.ndarray]:
|
| 326 |
+
"""Vectorized exact (Ls_j, Lm_j) in {0..15}^2 minimizer per sub-block."""
|
| 327 |
+
xb = np.asarray(x, dtype=np.float32).reshape(NS, 16)
|
| 328 |
+
wb = np.asarray(w, dtype=np.float32).reshape(NS, 16)
|
| 329 |
+
ls_opts = np.arange(16, dtype=np.float32)[:, None]
|
| 330 |
+
lm_opts = np.arange(16, dtype=np.float32)[None, :]
|
| 331 |
+
d_sub = np.float32(d) * ls_opts
|
| 332 |
+
m_sub = np.float32(m) * lm_opts
|
| 333 |
+
safe = d_sub >= np.float32(1.0e-15)
|
| 334 |
+
safe_d = np.where(safe, d_sub, np.float32(1.0))
|
| 335 |
+
subs = xb[:, None, None, :]
|
| 336 |
+
raw = (subs + m_sub[None, :, :, None]) / safe_d[None, :, :, None]
|
| 337 |
+
q = np.clip(nearest_int(raw), 0, 3).astype(np.float32)
|
| 338 |
+
q = np.where(safe[None, :, :, None], q, 0.0)
|
| 339 |
+
deq = d_sub[None, :, :, None] * q - m_sub[None, :, :, None]
|
| 340 |
+
err = np.sum(wb[:, None, None, :] * (subs - deq) ** 2, axis=3).reshape(NS, 256)
|
| 341 |
+
best_idx = np.argmin(err, axis=1)
|
| 342 |
+
return (best_idx // 16).astype(np.uint8), (best_idx % 16).astype(np.uint8)
|
| 343 |
+
|
| 344 |
+
|
| 345 |
+
def _global_bilinear_dm_solve(
|
| 346 |
+
x, w, d: float, m: float, ls, lm, codes, cfg: HPCConfig, dc_carry: float, use_spectral: bool
|
| 347 |
+
) -> tuple[float, float]:
|
| 348 |
+
"""Exact 2x2 weighted least-squares solve for superblock (d, dmin)."""
|
| 349 |
+
x_f64 = np.asarray(x, dtype=np.float64).ravel()
|
| 350 |
+
w_f64 = np.asarray(w, dtype=np.float64).ravel()
|
| 351 |
+
A = np.repeat(np.asarray(ls, dtype=np.float64), 16) * np.asarray(codes, dtype=np.float64)
|
| 352 |
+
B = np.repeat(np.asarray(lm, dtype=np.float64), 16)
|
| 353 |
+
Saa = float(np.sum(w_f64 * A * A))
|
| 354 |
+
Sab = float(np.sum(w_f64 * A * B))
|
| 355 |
+
Sbb = float(np.sum(w_f64 * B * B))
|
| 356 |
+
Sxa = float(np.sum(w_f64 * x_f64 * A))
|
| 357 |
+
Sxb = float(np.sum(w_f64 * x_f64 * B))
|
| 358 |
+
if use_spectral and cfg.dc_lambda > 0.0:
|
| 359 |
+
rw = float(cfg.dc_lambda) / QK_K
|
| 360 |
+
rA = float(np.sum(A))
|
| 361 |
+
rB = float(np.sum(B))
|
| 362 |
+
rS = float(np.sum(x_f64)) + float(dc_carry)
|
| 363 |
+
Saa += rw * rA * rA
|
| 364 |
+
Sab += rw * rA * rB
|
| 365 |
+
Sbb += rw * rB * rB
|
| 366 |
+
Sxa += rw * rS * rA
|
| 367 |
+
Sxb += rw * rS * rB
|
| 368 |
+
det = Saa * Sbb - Sab * Sab
|
| 369 |
+
if det > 1.0e-30:
|
| 370 |
+
d_opt = (Sbb * Sxa - Sab * Sxb) / det
|
| 371 |
+
m_opt = (Sab * Sxa - Saa * Sxb) / det
|
| 372 |
+
if d_opt > 0.0 and np.isfinite(d_opt):
|
| 373 |
+
d = float(d_opt)
|
| 374 |
+
if m_opt >= 0.0 and np.isfinite(m_opt):
|
| 375 |
+
m = float(m_opt)
|
| 376 |
+
return d, m
|
| 377 |
+
|
| 378 |
+
|
| 379 |
def _sub_ls(x, w, codes):
|
| 380 |
x = np.asarray(x, dtype=np.float64)
|
| 381 |
w = np.asarray(w, dtype=np.float64)
|
|
|
|
| 428 |
best = (obj, d, m, ls.copy(), lm.copy(), codes.copy())
|
| 429 |
stall = 0
|
| 430 |
iters = 0
|
| 431 |
+
xb_f64 = x.astype(np.float64).reshape(NS, 16)
|
| 432 |
+
wb_f64 = w.astype(np.float64).reshape(NS, 16)
|
| 433 |
+
Sw = np.sum(wb_f64, axis=1)
|
| 434 |
+
Sx = np.sum(wb_f64 * xb_f64, axis=1)
|
| 435 |
+
coupled = (not legacy_1d) and cfg.factor_coupled_q2k
|
| 436 |
+
|
| 437 |
for it in range(max(1, max_iter)):
|
| 438 |
iters = it + 1
|
| 439 |
codes = codes_from_params(x, d, m, ls, lm)
|
| 440 |
s, t = _sub_ls(x, w, codes)
|
| 441 |
q = codes.astype(np.float64).reshape(NS, 16)
|
| 442 |
+
Sqq = np.sum(wb_f64 * q * q, axis=1)
|
| 443 |
+
Swq = np.sum(wb_f64 * q, axis=1)
|
| 444 |
+
|
| 445 |
+
if coupled:
|
| 446 |
+
q_bar = np.where(Sw > 1.0e-30, Swq / np.maximum(Sw, 1.0e-30), 0.0)
|
| 447 |
+
ws = np.maximum(Sqq - Swq * q_bar, 1.0e-12 * Sw)
|
| 448 |
+
else:
|
| 449 |
+
ws = Sqq
|
| 450 |
+
wt = Sw
|
| 451 |
|
| 452 |
if pin & 1:
|
| 453 |
if d > 1.0e-30:
|
|
|
|
| 458 |
d_new, ls_new = _factor_1d_for(s, ws, 15, cfg, legacy_1d)
|
| 459 |
d, ls = float(d_new), ls_new
|
| 460 |
|
| 461 |
+
t_target = (
|
| 462 |
+
np.maximum(0.0, (d * ls.astype(np.float64) * Swq - Sx) / np.maximum(Sw, 1.0e-30))
|
| 463 |
+
if coupled
|
| 464 |
+
else t
|
| 465 |
+
)
|
| 466 |
+
|
| 467 |
if pin & 2:
|
| 468 |
if m > 1.0e-30:
|
| 469 |
+
lm = np.clip(nearest_int(t_target / m), 0, 15).astype(np.uint8)
|
| 470 |
else:
|
| 471 |
lm = np.zeros(NS, dtype=np.uint8)
|
| 472 |
else:
|
| 473 |
+
m_new, lm_new = _factor_1d_for(t_target, wt, 15, cfg, legacy_1d)
|
| 474 |
m, lm = float(m_new), lm_new
|
| 475 |
|
| 476 |
codes = codes_from_params(x, d, m, ls, lm)
|
| 477 |
+
if coupled:
|
| 478 |
+
if pin == 0:
|
| 479 |
+
d, m = _global_bilinear_dm_solve(
|
| 480 |
+
x, w, d, m, ls, lm, codes, cfg, dc_carry, use_spectral
|
| 481 |
+
)
|
| 482 |
+
if cfg.factor_joint_subscales:
|
| 483 |
+
d_f16 = float(d) if (pin & 1) else float(round_fp16(np.float32(d)))
|
| 484 |
+
m_f16 = float(m) if (pin & 2) else float(round_fp16(np.float32(m)))
|
| 485 |
+
ls_j, lm_j = _joint_subscale_scan(x, w, d_f16, m_f16)
|
| 486 |
+
codes_j = codes_from_params(x, d_f16, m_f16, ls_j, lm_j)
|
| 487 |
+
obj_j, _ = objective(x, w, d_f16, m_f16, ls_j, lm_j, codes_j, cfg, dc_carry)
|
| 488 |
+
if obj_j < best[0] - 1.0e-15:
|
| 489 |
+
d, m, ls, lm, codes = d_f16, m_f16, ls_j, lm_j, codes_j
|
| 490 |
+
best = (obj_j, d, m, ls.copy(), lm.copy(), codes.copy())
|
| 491 |
+
stall = 0
|
| 492 |
+
continue
|
| 493 |
+
|
| 494 |
cur, _ = objective(x, w, d, m, ls, lm, codes, cfg, dc_carry)
|
| 495 |
if cur < best[0] - 1.0e-15:
|
| 496 |
best = (cur, d, m, ls.copy(), lm.copy(), codes.copy())
|
|
|
|
| 578 |
out.append((d, m, ls, lm, codes))
|
| 579 |
if starts > 1:
|
| 580 |
out.append(_ls_init(x, w, cfg, legacy_1d))
|
| 581 |
+
if (not legacy_1d) and cfg.use_weight_analogs and d_seed <= 1.0e-30 and m_seed <= 1.0e-30:
|
| 582 |
+
for x_analog in build_block_analogs(x, w):
|
| 583 |
+
out.append(_analog_init(x, w, x_analog, cfg))
|
| 584 |
return out
|
| 585 |
|
| 586 |
best = None
|
pipeline.py
CHANGED
|
@@ -501,18 +501,21 @@ class Q2KQuantizer:
|
|
| 501 |
candidate_floor_used += 1
|
| 502 |
|
| 503 |
if cfg.use_factor_floor:
|
| 504 |
-
fac
|
| 505 |
-
|
| 506 |
-
|
| 507 |
-
|
| 508 |
-
|
| 509 |
-
|
| 510 |
-
|
| 511 |
-
|
| 512 |
-
|
| 513 |
-
|
| 514 |
-
|
| 515 |
-
|
|
|
|
|
|
|
|
|
|
| 516 |
|
| 517 |
blocks.append(make_block(d, m, ls, lm, codes))
|
| 518 |
|
|
|
|
| 501 |
candidate_floor_used += 1
|
| 502 |
|
| 503 |
if cfg.use_factor_floor:
|
| 504 |
+
for fac in (at_slot[i], standalone[i]):
|
| 505 |
+
fcodes = refine_codes_hadamard(
|
| 506 |
+
X[i], wi, fac.d, fac.dmin, fac.ls, fac.lm, fac.codes, cfg, float(carry[i])
|
| 507 |
+
)
|
| 508 |
+
fobj, fsse = objective(
|
| 509 |
+
X[i], wi, fac.d, fac.dmin, fac.ls, fac.lm, fcodes, cfg, float(carry[i])
|
| 510 |
+
)
|
| 511 |
+
if fobj < obj - 1.0e-15:
|
| 512 |
+
d = float(fac.d)
|
| 513 |
+
m = float(fac.dmin)
|
| 514 |
+
ls = fac.ls.copy()
|
| 515 |
+
lm = fac.lm.copy()
|
| 516 |
+
codes = fcodes.copy()
|
| 517 |
+
obj, sse = fobj, fsse
|
| 518 |
+
factor_floor_used += 1
|
| 519 |
|
| 520 |
blocks.append(make_block(d, m, ls, lm, codes))
|
| 521 |
|