Delete segmentation.py
Browse files- segmentation.py +0 -480
segmentation.py
DELETED
|
@@ -1,480 +0,0 @@
|
|
| 1 |
-
"""Self-describing per-block segmentation (V2-only stage).
|
| 2 |
-
|
| 3 |
-
Deterministic cluster labels are recomputed from a block's own DEQUANTIZED
|
| 4 |
-
values with an unweighted K-means (Lloyd, quantile init, centers sorted
|
| 5 |
-
ascending, stable value tie-breaks). Because the label rule is a pure
|
| 6 |
-
function of the stored slot values, the decoder recovers the identical
|
| 7 |
-
slot->position permutation by recomputation -- no sidecar storage, the
|
| 8 |
-
GGUF block layout is untouched.
|
| 9 |
-
|
| 10 |
-
Encode loop:
|
| 11 |
-
|
| 12 |
-
order_0 = S(x) (labels from the originals)
|
| 13 |
-
x_slot = x[order_0]
|
| 14 |
-
order_{n+1} = S(x̂_slot) (labels from the dequantized view)
|
| 15 |
-
... repeat until the permutation is stable; store quantize(x[order]).
|
| 16 |
-
|
| 17 |
-
Decode:
|
| 18 |
-
|
| 19 |
-
x̂_slot = dequantize(payload)
|
| 20 |
-
order = S(x̂_slot)
|
| 21 |
-
x̂_orig = scatter(x̂_slot, order)
|
| 22 |
-
|
| 23 |
-
Q2_K / Q3_K segment the 256-weight superblock into ``k`` bands aligned with
|
| 24 |
-
the 16-weight sub-scale groups; Q4_0's unit block is 32 wide with a single
|
| 25 |
-
scale, so it uses ``k=1`` (pure ascending value order, which the spectral
|
| 26 |
-
pair solvers can exploit).
|
| 27 |
-
"""
|
| 28 |
-
|
| 29 |
-
from __future__ import annotations
|
| 30 |
-
|
| 31 |
-
import numpy as np
|
| 32 |
-
|
| 33 |
-
from .config import HPCConfig
|
| 34 |
-
from .fp16 import nearest_int
|
| 35 |
-
|
| 36 |
-
_ROW_CHUNK = 4096
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
def _rowwise(fn, v: np.ndarray, chunk: int = _ROW_CHUNK) -> np.ndarray:
|
| 40 |
-
n = v.shape[0]
|
| 41 |
-
if n <= chunk:
|
| 42 |
-
return fn(v)
|
| 43 |
-
return np.concatenate([fn(v[s : s + chunk]) for s in range(0, n, chunk)], axis=0)
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
def band_labels(values: np.ndarray, k: int, iters: int = 12) -> np.ndarray:
|
| 47 |
-
"""Per-row unweighted K-means labels; centers sorted ascending (canonical)."""
|
| 48 |
-
v = np.asarray(values, dtype=np.float64)
|
| 49 |
-
if k <= 1:
|
| 50 |
-
return np.zeros(v.shape, dtype=np.int64)
|
| 51 |
-
|
| 52 |
-
def _run(v: np.ndarray) -> np.ndarray:
|
| 53 |
-
qs = (np.arange(k) + 0.5) / k
|
| 54 |
-
c = np.quantile(v, qs, axis=1).T
|
| 55 |
-
a = None
|
| 56 |
-
for _ in range(iters):
|
| 57 |
-
a = np.abs(v[:, :, None] - c[:, None, :]).argmin(axis=2)
|
| 58 |
-
mask = a[:, :, None] == np.arange(k)[None, None, :]
|
| 59 |
-
num = (v[:, :, None] * mask).sum(axis=1)
|
| 60 |
-
cnt = mask.sum(axis=1)
|
| 61 |
-
c = np.sort(num / np.maximum(cnt, 1), axis=1)
|
| 62 |
-
return np.abs(v[:, :, None] - c[:, None, :]).argmin(axis=2)
|
| 63 |
-
|
| 64 |
-
return _rowwise(_run, v).astype(np.int64)
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
def make_order(labels: np.ndarray, values: np.ndarray | None = None) -> np.ndarray:
|
| 68 |
-
"""Per-row slot->original index: band-major layout, position-ascending
|
| 69 |
-
inside each band (stable argsort). With this layout the label channel
|
| 70 |
-
(one label per ORIGINAL position) inverts the permutation exactly, and
|
| 71 |
-
per-16 ladder error depends only on band width, not intra-band order.
|
| 72 |
-
``values`` is accepted but ignored (kept for call-site compatibility)."""
|
| 73 |
-
|
| 74 |
-
def _run(labels: np.ndarray) -> np.ndarray:
|
| 75 |
-
return np.argsort(labels, axis=1, kind="stable").astype(np.int64)
|
| 76 |
-
|
| 77 |
-
n = labels.shape[0]
|
| 78 |
-
if n <= _ROW_CHUNK:
|
| 79 |
-
return _run(labels)
|
| 80 |
-
outs = []
|
| 81 |
-
for s in range(0, n, _ROW_CHUNK):
|
| 82 |
-
e = min(s + _ROW_CHUNK, n)
|
| 83 |
-
outs.append(_run(labels[s:e]))
|
| 84 |
-
return np.concatenate(outs, axis=0)
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
def apply_order(a: np.ndarray, order: np.ndarray) -> np.ndarray:
|
| 88 |
-
"""Slot view: out[b, s] = a[b, order[b, s]]."""
|
| 89 |
-
return np.take_along_axis(a, order, axis=1)
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
def invert_order(order: np.ndarray) -> np.ndarray:
|
| 93 |
-
"""Inverse permutation: inv[b, orig] = slot."""
|
| 94 |
-
inv = np.empty_like(order)
|
| 95 |
-
rows = np.arange(order.shape[0])[:, None]
|
| 96 |
-
cols = np.arange(order.shape[1])[None, :]
|
| 97 |
-
inv[rows, order] = cols
|
| 98 |
-
return inv
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
def recover_order(labels: np.ndarray) -> np.ndarray:
|
| 102 |
-
"""Exact inversion given a label channel (band-major, position-ascending)."""
|
| 103 |
-
return make_order(labels)
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
def scatter_to_original(xh_slots: np.ndarray, order: np.ndarray) -> np.ndarray:
|
| 107 |
-
"""Inverse of apply_order: out[b, order[b, s]] = xh_slots[b, s]."""
|
| 108 |
-
out = np.empty(xh_slots.shape, dtype=xh_slots.dtype)
|
| 109 |
-
np.put_along_axis(out, order, xh_slots, axis=1)
|
| 110 |
-
return out
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
# ---- label channel codec (in-format persistence inside a GGUF KV) --------
|
| 114 |
-
#
|
| 115 |
-
# Layout: magic "HQS1" | uint8 k | uint8 enc | uint32 n_labels (little-endian)
|
| 116 |
-
# | payload.
|
| 117 |
-
# enc=0: packs labels 0..15 as nibbles (2 labels per byte; full per-block or small tensors)
|
| 118 |
-
# enc=1: stores raw uint8 labels (k > 16 only)
|
| 119 |
-
# enc=2: generalized permutation lookup table with 1-byte block indices (table_size in 4..256):
|
| 120 |
-
# uint16 table_size | uint8 table[table_size * 128] | uint8 block_idx[n_blocks]
|
| 121 |
-
# enc=3: generalized permutation lookup table with 2-byte block indices (table_size in 257..65535):
|
| 122 |
-
# uint16 table_size | uint8 table[table_size * 128] | uint16 block_idx[n_blocks]
|
| 123 |
-
|
| 124 |
-
import struct as _struct
|
| 125 |
-
|
| 126 |
-
SEG_MAGIC = b"HQS1"
|
| 127 |
-
SEG_TABLE_SIZE = 256
|
| 128 |
-
|
| 129 |
-
|
| 130 |
-
def _pack_nibbles_1d(flat: np.ndarray) -> bytes:
|
| 131 |
-
lo = flat[0::2].astype(np.uint8)
|
| 132 |
-
hi = flat[1::2].astype(np.uint8)
|
| 133 |
-
if hi.size < lo.size:
|
| 134 |
-
hi = np.concatenate([hi, np.zeros(1, dtype=np.uint8)])
|
| 135 |
-
return (lo | (hi << 4)).astype(np.uint8).tobytes()
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
def _unpack_nibbles_1d(data: bytes, n: int) -> np.ndarray:
|
| 139 |
-
raw = np.frombuffer(data, dtype=np.uint8)
|
| 140 |
-
out = np.empty(raw.size * 2, dtype=np.uint8)
|
| 141 |
-
out[0::2] = raw & np.uint8(0x0F)
|
| 142 |
-
out[1::2] = raw >> np.uint8(4)
|
| 143 |
-
return out[:n]
|
| 144 |
-
|
| 145 |
-
|
| 146 |
-
def label_channel_bytes(n: int, k: int, table_size: int = SEG_TABLE_SIZE) -> int:
|
| 147 |
-
"""Exact content size of the packed label channel."""
|
| 148 |
-
table_size = int(table_size)
|
| 149 |
-
if int(k) <= 16:
|
| 150 |
-
if table_size > 0 and n > table_size * 256 and n % 256 == 0:
|
| 151 |
-
n_blocks = n // 256
|
| 152 |
-
idx_bytes = 1 if table_size <= 256 else 2
|
| 153 |
-
return 10 + 2 + table_size * 128 + n_blocks * idx_bytes
|
| 154 |
-
return 10 + (n + 1) // 2
|
| 155 |
-
return 10 + n
|
| 156 |
-
|
| 157 |
-
|
| 158 |
-
def pack_label_channel(labels: np.ndarray, k: int, table_size: int = SEG_TABLE_SIZE) -> bytes:
|
| 159 |
-
k = int(k)
|
| 160 |
-
table_size = int(table_size)
|
| 161 |
-
# Fast path when segment_stage attached pre-packed LUT data
|
| 162 |
-
lut_cache = getattr(labels, "_hpcq_lut_bytes", None)
|
| 163 |
-
if lut_cache is not None and lut_cache[0] == (k, table_size):
|
| 164 |
-
return lut_cache[1]
|
| 165 |
-
|
| 166 |
-
flat = np.ascontiguousarray(labels, dtype=np.uint8).ravel()
|
| 167 |
-
n = int(flat.size)
|
| 168 |
-
if k <= 16:
|
| 169 |
-
if flat.size and int(flat.max()) > 15:
|
| 170 |
-
raise ValueError(f"label {int(flat.max())} does not fit nibble packing (k={k})")
|
| 171 |
-
if table_size > 0 and n > table_size * 256 and n % 256 == 0:
|
| 172 |
-
enc = 2 if table_size <= 256 else 3
|
| 173 |
-
idx_dt = np.uint8 if enc == 2 else np.dtype("<u2")
|
| 174 |
-
n_blocks = n // 256
|
| 175 |
-
rows = flat.reshape(n_blocks, 256)
|
| 176 |
-
lo = rows[:, 0::2]
|
| 177 |
-
hi = rows[:, 1::2]
|
| 178 |
-
packed_rows = (lo | (hi << 4)).astype(np.uint8) # (n_blocks, 128)
|
| 179 |
-
row_dt = np.dtype((np.void, 128))
|
| 180 |
-
row_views = np.ascontiguousarray(packed_rows).view(row_dt).ravel()
|
| 181 |
-
uniq_views, inv_idx, counts = np.unique(
|
| 182 |
-
row_views, return_inverse=True, return_counts=True
|
| 183 |
-
)
|
| 184 |
-
uniq_rows = uniq_views.view(np.uint8).reshape(-1, 128)
|
| 185 |
-
table = np.zeros((table_size, 128), dtype=np.uint8)
|
| 186 |
-
if uniq_rows.shape[0] <= table_size:
|
| 187 |
-
table[: uniq_rows.shape[0]] = uniq_rows
|
| 188 |
-
block_idx = inv_idx.astype(idx_dt)
|
| 189 |
-
else:
|
| 190 |
-
top_u = np.argsort(-counts)[:table_size]
|
| 191 |
-
table[:] = uniq_rows[top_u]
|
| 192 |
-
remap = np.zeros(uniq_rows.shape[0], dtype=idx_dt)
|
| 193 |
-
remap[top_u] = np.arange(table_size, dtype=idx_dt)
|
| 194 |
-
block_idx = remap[inv_idx]
|
| 195 |
-
data = _struct.pack("<H", table_size) + table.tobytes() + block_idx.tobytes()
|
| 196 |
-
else:
|
| 197 |
-
enc = 0
|
| 198 |
-
data = _pack_nibbles_1d(flat)
|
| 199 |
-
else:
|
| 200 |
-
enc = 1
|
| 201 |
-
if flat.size and (int(labels.max()) > 255 or int(labels.min()) < 0):
|
| 202 |
-
raise ValueError("labels must be in [0, 255] for 8-bit packing")
|
| 203 |
-
data = flat.tobytes()
|
| 204 |
-
return SEG_MAGIC + bytes([k, enc]) + _struct.pack("<I", n) + data
|
| 205 |
-
|
| 206 |
-
|
| 207 |
-
def unpack_label_channel(content: bytes) -> tuple[int, np.ndarray]:
|
| 208 |
-
if content[:4] != SEG_MAGIC:
|
| 209 |
-
raise ValueError(f"bad label channel magic {content[:4]!r}")
|
| 210 |
-
k = int(content[4])
|
| 211 |
-
enc = int(content[5])
|
| 212 |
-
n = _struct.unpack("<I", content[6:10])[0]
|
| 213 |
-
data = content[10:]
|
| 214 |
-
if enc == 0:
|
| 215 |
-
out = _unpack_nibbles_1d(data, n)
|
| 216 |
-
elif enc in (2, 3):
|
| 217 |
-
n_blocks = n // 256
|
| 218 |
-
tsize = _struct.unpack("<H", data[:2])[0]
|
| 219 |
-
tbytes = tsize * 128
|
| 220 |
-
table = _unpack_nibbles_1d(data[2 : 2 + tbytes], tsize * 256).reshape(
|
| 221 |
-
tsize, 256
|
| 222 |
-
)
|
| 223 |
-
if enc == 2:
|
| 224 |
-
idx = np.frombuffer(data[2 + tbytes : 2 + tbytes + n_blocks], dtype=np.uint8)
|
| 225 |
-
else:
|
| 226 |
-
idx = np.frombuffer(
|
| 227 |
-
data[2 + tbytes : 2 + tbytes + 2 * n_blocks], dtype=np.dtype("<u2")
|
| 228 |
-
)
|
| 229 |
-
out = table[idx].reshape(-1)
|
| 230 |
-
else:
|
| 231 |
-
out = np.frombuffer(data, dtype=np.uint8).copy()
|
| 232 |
-
return k, out[:n]
|
| 233 |
-
|
| 234 |
-
|
| 235 |
-
def _order_to_canonical_labels(ord_row: np.ndarray, k: int = 16) -> np.ndarray:
|
| 236 |
-
lab = np.empty(256, dtype=np.int64)
|
| 237 |
-
step = 256 // k
|
| 238 |
-
for g in range(k):
|
| 239 |
-
s0 = g * step
|
| 240 |
-
s1 = 256 if g == k - 1 else (g + 1) * step
|
| 241 |
-
sub = np.sort(ord_row[s0:s1])
|
| 242 |
-
lab[sub] = g
|
| 243 |
-
return lab
|
| 244 |
-
|
| 245 |
-
|
| 246 |
-
def _segment_stage_dense(
|
| 247 |
-
Xo: np.ndarray,
|
| 248 |
-
Wo: np.ndarray | None,
|
| 249 |
-
cfg: HPCConfig,
|
| 250 |
-
proxy,
|
| 251 |
-
k: int,
|
| 252 |
-
) -> tuple[np.ndarray, np.ndarray, int]:
|
| 253 |
-
labels = band_labels(Xo, k)
|
| 254 |
-
order = make_order(labels)
|
| 255 |
-
rounds = 0
|
| 256 |
-
for _ in range(max(1, int(cfg.segment_iters))):
|
| 257 |
-
rounds += 1
|
| 258 |
-
xg = apply_order(Xo, order)
|
| 259 |
-
wgg = None if Wo is None else apply_order(Wo, order)
|
| 260 |
-
xh = proxy(xg, wgg)
|
| 261 |
-
labels_slot = band_labels(xh, k)
|
| 262 |
-
labels = np.empty_like(labels_slot)
|
| 263 |
-
np.put_along_axis(labels, order, labels_slot, axis=1)
|
| 264 |
-
order = make_order(labels)
|
| 265 |
-
return order, labels, rounds
|
| 266 |
-
|
| 267 |
-
|
| 268 |
-
class _LUTLabels(np.ndarray):
|
| 269 |
-
_hpcq_lut_bytes: tuple[tuple[int, int], bytes] | None = None
|
| 270 |
-
|
| 271 |
-
|
| 272 |
-
def segment_stage(
|
| 273 |
-
X: np.ndarray,
|
| 274 |
-
W: np.ndarray | None,
|
| 275 |
-
cfg: HPCConfig,
|
| 276 |
-
proxy,
|
| 277 |
-
k: int,
|
| 278 |
-
) -> tuple[np.ndarray, np.ndarray, int]:
|
| 279 |
-
"""Run the label fixed-point loop.
|
| 280 |
-
|
| 281 |
-
For tensors with ``n_blocks <= M`` (or ``k > 16``), refines a per-block
|
| 282 |
-
label vector directly. For larger tensors (``n_blocks > M, k <= 16``),
|
| 283 |
-
constructs an ``M``-entry generalized permutation lookup table (Entry 0 =
|
| 284 |
-
natural order, Entries 1..2 = column RMS/abs profiles, Entries 3..M-1 =
|
| 285 |
-
prototype blocks refined by ``_segment_stage_dense``) and assigns each
|
| 286 |
-
superblock its optimal table index in chunked float32 / CUDA batches.
|
| 287 |
-
"""
|
| 288 |
-
k = max(1, int(k))
|
| 289 |
-
n_blocks, B = X.shape
|
| 290 |
-
M = int(getattr(cfg, "segment_table_size", SEG_TABLE_SIZE))
|
| 291 |
-
if M <= 0 or n_blocks <= M or B != 256 or k > 16:
|
| 292 |
-
Xo = np.asarray(X, dtype=np.float64)
|
| 293 |
-
Wo = None if W is None else np.asarray(W, dtype=np.float64)
|
| 294 |
-
return _segment_stage_dense(Xo, Wo, cfg, proxy, k)
|
| 295 |
-
|
| 296 |
-
Xf = np.ascontiguousarray(X, dtype=np.float32)
|
| 297 |
-
table_labels = np.empty((M, 256), dtype=np.uint8)
|
| 298 |
-
# Entry 0: natural order (monotone labels so make_order is identity [0..255])
|
| 299 |
-
table_labels[0] = _order_to_canonical_labels(np.arange(256, dtype=np.int64), k).astype(np.uint8)
|
| 300 |
-
# Entry 1 & 2: column-wide RMS and abs profiles (sampled across up to 4096 blocks)
|
| 301 |
-
s_step = max(1, n_blocks // 4096)
|
| 302 |
-
X_sample = Xf[::s_step].astype(np.float64)
|
| 303 |
-
col_rms = np.sqrt(np.mean(X_sample * X_sample, axis=0))
|
| 304 |
-
table_labels[1] = _order_to_canonical_labels(np.argsort(col_rms, kind="stable"), k).astype(np.uint8)
|
| 305 |
-
col_abs = np.mean(np.abs(X_sample), axis=0)
|
| 306 |
-
table_labels[2] = _order_to_canonical_labels(np.argsort(col_abs, kind="stable"), k).astype(np.uint8)
|
| 307 |
-
|
| 308 |
-
# Entries 3..M-1: up to 253 prototype blocks refined with the proxy loop,
|
| 309 |
-
# plus fast direct band_labels prototypes when M > 256
|
| 310 |
-
n_need = M - 3
|
| 311 |
-
n_dense = min(n_need, 253)
|
| 312 |
-
proto_idx = np.linspace(0, n_blocks - 1, n_dense, dtype=np.int64)
|
| 313 |
-
Xp = Xf[proto_idx].astype(np.float64)
|
| 314 |
-
Wp = None if W is None else np.asarray(W[proto_idx], dtype=np.float64)
|
| 315 |
-
ord_p, _, rounds = _segment_stage_dense(Xp, Wp, cfg, proxy, k)
|
| 316 |
-
for i in range(n_dense):
|
| 317 |
-
table_labels[3 + i] = _order_to_canonical_labels(ord_p[i], k).astype(np.uint8)
|
| 318 |
-
if n_need > n_dense:
|
| 319 |
-
extra_idx = np.linspace(1, n_blocks - 2, n_need - n_dense, dtype=np.int64)
|
| 320 |
-
ord_e = make_order(band_labels(Xf[extra_idx].astype(np.float64), k))
|
| 321 |
-
for i in range(n_need - n_dense):
|
| 322 |
-
table_labels[3 + n_dense + i] = _order_to_canonical_labels(ord_e[i], k).astype(np.uint8)
|
| 323 |
-
|
| 324 |
-
table_orders = make_order(table_labels) # (M, 256) int64
|
| 325 |
-
|
| 326 |
-
# Build sub-block projection matrix P: (256, M * 16)
|
| 327 |
-
P = np.zeros((256, M * 16), dtype=np.float32)
|
| 328 |
-
for m in range(M):
|
| 329 |
-
for g in range(16):
|
| 330 |
-
P[table_orders[m, 16 * g : 16 * g + 16], m * 16 + g] = 0.25
|
| 331 |
-
|
| 332 |
-
topK = min(4, M)
|
| 333 |
-
best_m = np.zeros(n_blocks, dtype=np.int64)
|
| 334 |
-
nmax_sub = 3.0 if str(getattr(cfg, "quant_format", "q2_k")) != "q3_k" else 7.0
|
| 335 |
-
|
| 336 |
-
use_torch_cuda = False
|
| 337 |
-
if getattr(cfg, "device", "auto") != "cpu":
|
| 338 |
-
try:
|
| 339 |
-
import torch
|
| 340 |
-
use_torch_cuda = torch.cuda.is_available()
|
| 341 |
-
except Exception:
|
| 342 |
-
use_torch_cuda = False
|
| 343 |
-
|
| 344 |
-
if use_torch_cuda:
|
| 345 |
-
import torch
|
| 346 |
-
dev_str = "cuda" if cfg.device == "auto" else cfg.device
|
| 347 |
-
dev = torch.device(dev_str)
|
| 348 |
-
P_t = torch.from_numpy(P).to(dev)
|
| 349 |
-
orders_t = torch.from_numpy(table_orders).to(dev)
|
| 350 |
-
chunk = 16384
|
| 351 |
-
with torch.no_grad():
|
| 352 |
-
for s in range(0, n_blocks, chunk):
|
| 353 |
-
e = min(s + chunk, n_blocks)
|
| 354 |
-
xb = torch.from_numpy(Xf[s:e]).to(dev)
|
| 355 |
-
sc = (xb @ P_t).square().view(-1, M, 16).sum(dim=2) + 0.5 * (
|
| 356 |
-
(xb.abs() @ P_t).square().view(-1, M, 16).sum(dim=2)
|
| 357 |
-
)
|
| 358 |
-
top_idx = torch.topk(sc, k=topK, dim=1).indices
|
| 359 |
-
top_idx[:, 0] = 0
|
| 360 |
-
ords = orders_t[top_idx]
|
| 361 |
-
u = torch.gather(xb.unsqueeze(1).expand(-1, topK, -1), 2, ords).view(
|
| 362 |
-
-1, topK, 16, 16
|
| 363 |
-
)
|
| 364 |
-
umin = torch.clamp_max(u.amin(dim=3, keepdim=True), 0.0)
|
| 365 |
-
umax = u.amax(dim=3, keepdim=True)
|
| 366 |
-
step = torch.clamp_min((umax - umin) * (1.0 / nmax_sub), 1.0e-30)
|
| 367 |
-
q = torch.clamp(torch.round((u - umin) / step), 0.0, nmax_sub)
|
| 368 |
-
err = u - (umin + step * q)
|
| 369 |
-
if W is not None:
|
| 370 |
-
wb = torch.from_numpy(np.ascontiguousarray(W[s:e], dtype=np.float32)).to(dev)
|
| 371 |
-
wg = torch.gather(wb.unsqueeze(1).expand(-1, topK, -1), 2, ords).view(
|
| 372 |
-
-1, topK, 16, 16
|
| 373 |
-
)
|
| 374 |
-
sse = (wg * err.square()).sum(dim=(2, 3))
|
| 375 |
-
else:
|
| 376 |
-
sse = err.square().sum(dim=(2, 3))
|
| 377 |
-
best_local = sse.argmin(dim=1)
|
| 378 |
-
best_m[s:e] = (
|
| 379 |
-
top_idx[torch.arange(e - s, device=dev), best_local].cpu().numpy()
|
| 380 |
-
)
|
| 381 |
-
else:
|
| 382 |
-
chunk = 8192
|
| 383 |
-
for s in range(0, n_blocks, chunk):
|
| 384 |
-
e = min(s + chunk, n_blocks)
|
| 385 |
-
xb = Xf[s:e]
|
| 386 |
-
sc = ((xb @ P) ** 2).reshape(e - s, M, 16).sum(axis=2) + 0.5 * (
|
| 387 |
-
(np.abs(xb) @ P) ** 2
|
| 388 |
-
).reshape(e - s, M, 16).sum(axis=2)
|
| 389 |
-
top_idx = np.argpartition(-sc, topK - 1, axis=1)[:, :topK]
|
| 390 |
-
top_idx[:, 0] = 0
|
| 391 |
-
ords = table_orders[top_idx]
|
| 392 |
-
u = np.take_along_axis(xb[:, None, :], ords, axis=2).reshape(
|
| 393 |
-
e - s, topK, 16, 16
|
| 394 |
-
)
|
| 395 |
-
umin = np.minimum(u.min(axis=3, keepdims=True), 0.0)
|
| 396 |
-
umax = u.max(axis=3, keepdims=True)
|
| 397 |
-
step = np.maximum((umax - umin) * (1.0 / nmax_sub), 1.0e-30)
|
| 398 |
-
q = np.clip(np.rint((u - umin) / step), 0.0, nmax_sub)
|
| 399 |
-
err = u - (umin + step * q)
|
| 400 |
-
if W is not None:
|
| 401 |
-
wb = np.ascontiguousarray(W[s:e], dtype=np.float32)
|
| 402 |
-
wg = np.take_along_axis(wb[:, None, :], ords, axis=2).reshape(
|
| 403 |
-
e - s, topK, 16, 16
|
| 404 |
-
)
|
| 405 |
-
sse = np.sum(wg * err * err, axis=(2, 3))
|
| 406 |
-
else:
|
| 407 |
-
sse = np.sum(err * err, axis=(2, 3))
|
| 408 |
-
best_local = np.argmin(sse, axis=1)
|
| 409 |
-
best_m[s:e] = top_idx[np.arange(e - s), best_local]
|
| 410 |
-
|
| 411 |
-
out_labels = table_labels[best_m].view(_LUTLabels)
|
| 412 |
-
enc = 2 if M <= 256 else 3
|
| 413 |
-
idx_dt = np.uint8 if enc == 2 else np.dtype("<u2")
|
| 414 |
-
lo = table_labels[:, 0::2]
|
| 415 |
-
hi = table_labels[:, 1::2]
|
| 416 |
-
table_nib = (lo | (hi << 4)).astype(np.uint8)
|
| 417 |
-
raw_lut = (
|
| 418 |
-
SEG_MAGIC
|
| 419 |
-
+ bytes([k, enc])
|
| 420 |
-
+ _struct.pack("<I", n_blocks * 256)
|
| 421 |
-
+ _struct.pack("<H", M)
|
| 422 |
-
+ table_nib.tobytes()
|
| 423 |
-
+ best_m.astype(idx_dt).tobytes()
|
| 424 |
-
)
|
| 425 |
-
out_labels._hpcq_lut_bytes = ((k, M), raw_lut)
|
| 426 |
-
return table_orders[best_m], out_labels, rounds
|
| 427 |
-
|
| 428 |
-
|
| 429 |
-
def q2k_proxy_cfg(clamp_val: float, cfg: HPCConfig):
|
| 430 |
-
"""Cheap phase1+2 proxy for Q2_K label iteration (GGML seeds + WLS)."""
|
| 431 |
-
from .q2k import codes_from_params_batch, dequantize_params_batch
|
| 432 |
-
from .seeds import build_seeds
|
| 433 |
-
from .pipeline import _phase2_wls
|
| 434 |
-
|
| 435 |
-
def _proxy(xg: np.ndarray, wg: np.ndarray | None) -> np.ndarray:
|
| 436 |
-
seeds = build_seeds(xg, wg, cfg, clamp_val)
|
| 437 |
-
d, m = _phase2_wls(xg, wg, seeds, cfg)
|
| 438 |
-
sd = np.where(d > 1.0e-15, d, 1.0).astype(np.float32)
|
| 439 |
-
sm = np.where(m > 1.0e-15, m, 1.0).astype(np.float32)
|
| 440 |
-
ls = np.where(
|
| 441 |
-
d[:, None] > 1.0e-15,
|
| 442 |
-
np.clip(nearest_int(seeds["scales"] / sd[:, None]), 0, 15),
|
| 443 |
-
0,
|
| 444 |
-
).astype(np.uint8)
|
| 445 |
-
lm = np.where(
|
| 446 |
-
m[:, None] > 1.0e-15,
|
| 447 |
-
np.clip(nearest_int(seeds["mins"] / sm[:, None]), 0, 15),
|
| 448 |
-
0,
|
| 449 |
-
).astype(np.uint8)
|
| 450 |
-
codes = codes_from_params_batch(xg, d, m, ls, lm)
|
| 451 |
-
return dequantize_params_batch(d, m, ls, lm, codes).astype(np.float64)
|
| 452 |
-
|
| 453 |
-
return _proxy
|
| 454 |
-
|
| 455 |
-
|
| 456 |
-
def q3k_proxy(xg: np.ndarray, wg: np.ndarray | None) -> np.ndarray:
|
| 457 |
-
"""Cheap per-16 sub-block 3-bit ladder proxy for Q3_K label iteration."""
|
| 458 |
-
from .seeds import make_qkx2_batch
|
| 459 |
-
|
| 460 |
-
n, B = xg.shape
|
| 461 |
-
u = np.asarray(xg, dtype=np.float64).reshape(n * (B // 16), 16)
|
| 462 |
-
if wg is None:
|
| 463 |
-
wu = np.ones_like(u)
|
| 464 |
-
else:
|
| 465 |
-
wu = np.asarray(wg, dtype=np.float64).reshape(n * (B // 16), 16)
|
| 466 |
-
# make_qkx2_batch returns (scale, -min, codes) with the GGML positive-dmin
|
| 467 |
-
# convention; reconstruction is scale * code - (-min).
|
| 468 |
-
scale, neg_min, codes = make_qkx2_batch(u, wu, nmax=7)
|
| 469 |
-
deq = scale[:, None] * codes - neg_min[:, None]
|
| 470 |
-
return deq.reshape(n, B)
|
| 471 |
-
|
| 472 |
-
|
| 473 |
-
def q4_0_proxy(xg: np.ndarray, wg: np.ndarray | None) -> np.ndarray:
|
| 474 |
-
"""GGML-reference Q4_0 proxy (single scale + min per 32-block)."""
|
| 475 |
-
x = np.asarray(xg, dtype=np.float64)
|
| 476 |
-
xmin = x.min(axis=1, keepdims=True)
|
| 477 |
-
xmax = x.max(axis=1, keepdims=True)
|
| 478 |
-
d = np.maximum((xmax - xmin) / 15.0, 1.0e-30)
|
| 479 |
-
q = np.clip(nearest_int(((x - xmin) / d).astype(np.float32)), 0, 15)
|
| 480 |
-
return xmin + d * q.astype(np.float64)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|