CompressedGemma commited on
Commit
43faf5d
·
verified ·
1 Parent(s): 396a58e

Upload 4 files

Browse files
Files changed (3) hide show
  1. gguf.py +2 -1
  2. patch_llamacpp_hpcq_seg.py +20 -2
  3. segmentation.py +151 -33
gguf.py CHANGED
@@ -1057,7 +1057,8 @@ def write_quantized_gguf(
1057
  t_q3_start = time.time()
1058
  q3_labels: list[np.ndarray] = []
1059
 
1060
- for x_chunk, row, _r0, _r1 in reader.iter_tensor_row_chunks(ti.name, max_elements=8_388_608):
 
1061
  if stop_event is not None and stop_event.is_set():
1062
  raise InterruptedError("Quantization cancelled by user")
1063
  imp_chunk = expand_importance(raw_imp, x_chunk.size, row) if has_imat else None
 
1057
  t_q3_start = time.time()
1058
  q3_labels: list[np.ndarray] = []
1059
 
1060
+ q3_max_elems = ti.n_elements if seg_active else 8_388_608
1061
+ for x_chunk, row, _r0, _r1 in reader.iter_tensor_row_chunks(ti.name, max_elements=q3_max_elems):
1062
  if stop_event is not None and stop_event.is_set():
1063
  raise InterruptedError("Quantization cancelled by user")
1064
  imp_chunk = expand_importance(raw_imp, x_chunk.size, row) if has_imat else None
patch_llamacpp_hpcq_seg.py CHANGED
@@ -78,11 +78,17 @@ struct hpcq_seg_entry {
78
  uintptr_t end; // payload end (exclusive)
79
  int64_t block_bytes; // 84 for Q2_K, 110 for Q3_K
80
  int64_t n_blocks; // number of 256-weight superblocks
81
- uint8_t enc; // 0 = 4-bit nibbles (128B/blk), 1 = 8-bit (256B/blk)
82
- uint8_t * packed; // compact packed label stream (128B or 256B per block)
83
  void * dev_packed; // lazily allocated device copy for CUDA (or NULL)
84
  };
85
 
 
 
 
 
 
 
86
  GGML_API void ggml_hpcq_seg_bind_raw(
87
  const void * payload, int64_t n_bytes,
88
  const uint8_t * hqs1, size_t hqs1_len, int64_t block_bytes);
@@ -131,6 +137,18 @@ static inline HPCQ_HD void hpcq_seg_make_order_block(
131
  }
132
  }
133
 
 
 
 
 
 
 
 
 
 
 
 
 
134
  #ifdef __cplusplus
135
  }
136
  #endif
 
78
  uintptr_t end; // payload end (exclusive)
79
  int64_t block_bytes; // 84 for Q2_K, 110 for Q3_K
80
  int64_t n_blocks; // number of 256-weight superblocks
81
+ uint8_t enc; // 0 = 4-bit nibbles, 1 = 8-bit, 2 = 256-entry static table + 1B/blk
82
+ uint8_t * packed; // compact packed label stream
83
  void * dev_packed; // lazily allocated device copy for CUDA (or NULL)
84
  };
85
 
86
+ static inline HPCQ_HD size_t hpcq_seg_packed_bytes(int64_t n_blocks, uint8_t enc) {
87
+ if (enc == 2) return 32768u + (size_t) n_blocks;
88
+ if (enc == 0) return (size_t) n_blocks * 128u;
89
+ return (size_t) n_blocks * 256u;
90
+ }
91
+
92
  GGML_API void ggml_hpcq_seg_bind_raw(
93
  const void * payload, int64_t n_bytes,
94
  const uint8_t * hqs1, size_t hqs1_len, int64_t block_bytes);
 
137
  }
138
  }
139
 
140
+ static inline HPCQ_HD void hpcq_seg_make_order_at(
141
+ const uint8_t * packed_base, uint8_t enc, int64_t blk_idx, uint16_t * order) {
142
+ if (enc == 2) {
143
+ const uint8_t m = packed_base[32768u + (size_t) blk_idx];
144
+ hpcq_seg_make_order_block(packed_base + (size_t) m * 128u, 0, order);
145
+ } else if (enc == 0) {
146
+ hpcq_seg_make_order_block(packed_base + (size_t) blk_idx * 128u, 0, order);
147
+ } else {
148
+ hpcq_seg_make_order_block(packed_base + (size_t) blk_idx * 256u, 1, order);
149
+ }
150
+ }
151
+
152
  #ifdef __cplusplus
153
  }
154
  #endif
segmentation.py CHANGED
@@ -113,17 +113,40 @@ def scatter_to_original(xh_slots: np.ndarray, order: np.ndarray) -> np.ndarray:
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. enc=0 packs labels 0..15 as nibbles (2 labels per byte),
117
- # enc=1 stores raw uint8 labels (k > 16 only).
 
 
 
 
118
 
119
  import struct as _struct
120
 
121
  SEG_MAGIC = b"HQS1"
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
122
 
123
 
124
  def label_channel_bytes(n: int, k: int) -> int:
125
  """Exact content size of the packed label channel."""
126
  if int(k) <= 16:
 
 
127
  return 10 + (n + 1) // 2
128
  return 10 + n
129
 
@@ -135,12 +158,33 @@ def pack_label_channel(labels: np.ndarray, k: int) -> bytes:
135
  if k <= 16:
136
  if flat.size and int(flat.max()) > 15:
137
  raise ValueError(f"label {int(flat.max())} does not fit nibble packing (k={k})")
138
- enc = 0
139
- lo = flat[0::2].astype(np.uint8)
140
- hi = flat[1::2].astype(np.uint8)
141
- if hi.size < lo.size:
142
- hi = np.concatenate([hi, np.zeros(1, dtype=np.uint8)])
143
- data = (lo | (hi << 4)).astype(np.uint8).tobytes()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
144
  else:
145
  enc = 1
146
  if flat.size and (int(flat.max()) > 255 or int(flat.min()) < 0):
@@ -157,15 +201,53 @@ def unpack_label_channel(content: bytes) -> tuple[int, np.ndarray]:
157
  n = _struct.unpack("<I", content[6:10])[0]
158
  data = content[10:]
159
  if enc == 0:
160
- raw = np.frombuffer(data, dtype=np.uint8)
161
- out = np.empty(raw.size * 2, dtype=np.uint8)
162
- out[0::2] = raw & np.uint8(0x0F)
163
- out[1::2] = raw >> np.uint8(4)
 
 
 
 
 
164
  else:
165
  out = np.frombuffer(data, dtype=np.uint8).copy()
166
  return k, out[:n]
167
 
168
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
169
  def segment_stage(
170
  X: np.ndarray,
171
  W: np.ndarray | None,
@@ -175,31 +257,67 @@ def segment_stage(
175
  ) -> tuple[np.ndarray, np.ndarray, int]:
176
  """Run the label fixed-point loop.
177
 
178
- Returns ``(order, labels, rounds)`` where ``labels`` holds one band id
179
- per ORIGINAL position (this is the label channel the decoder needs for
180
- exact inversion) and ``order`` is the slot->original permutation.
181
- ``proxy(xg, wg)`` must return a dequantized slot view from a cheap
182
- quantize/dequantize of the permuted view; labels are refined from that
183
- proxy reconstruction so the stored channel matches what a channelless
184
- re-derivation would see.
185
  """
186
  Xo = np.asarray(X, dtype=np.float64)
187
  Wo = None if W is None else np.asarray(W, dtype=np.float64)
188
  k = max(1, int(k))
189
- labels = band_labels(Xo, k) # label channel, ORIGINAL position order
190
- order = make_order(labels)
191
- wg = None if Wo is None else np.asarray(Wo, dtype=np.float64)
192
- rounds = 0
193
- for _ in range(max(1, int(cfg.segment_iters))):
194
- rounds += 1
195
- xg = apply_order(Xo, order)
196
- wgg = None if wg is None else apply_order(wg, order)
197
- xh = proxy(xg, wgg) # slot-ordered reconstruction
198
- labels_slot = band_labels(xh, k) # labels in SLOT order
199
- labels = np.empty_like(labels_slot)
200
- np.put_along_axis(labels, order, labels_slot, axis=1) # back to original order
201
- order = make_order(labels)
202
- return order, labels, rounds
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
203
 
204
 
205
  def q2k_proxy_cfg(clamp_val: float, cfg: HPCConfig):
 
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; used for n <= 65536)
118
+ # enc=1: stores raw uint8 labels (k > 16 only)
119
+ # enc=2: generalized 256-entry permutation table (32 KB nibble table + 1 byte
120
+ # index per 256-weight superblock = +0.03125 bpw; used for n > 65536,
121
+ # k <= 16, n % 256 == 0).
122
 
123
  import struct as _struct
124
 
125
  SEG_MAGIC = b"HQS1"
126
+ SEG_TABLE_SIZE = 256
127
+
128
+
129
+ def _pack_nibbles_1d(flat: np.ndarray) -> bytes:
130
+ lo = flat[0::2].astype(np.uint8)
131
+ hi = flat[1::2].astype(np.uint8)
132
+ if hi.size < lo.size:
133
+ hi = np.concatenate([hi, np.zeros(1, dtype=np.uint8)])
134
+ return (lo | (hi << 4)).astype(np.uint8).tobytes()
135
+
136
+
137
+ def _unpack_nibbles_1d(data: bytes, n: int) -> np.ndarray:
138
+ raw = np.frombuffer(data, dtype=np.uint8)
139
+ out = np.empty(raw.size * 2, dtype=np.uint8)
140
+ out[0::2] = raw & np.uint8(0x0F)
141
+ out[1::2] = raw >> np.uint8(4)
142
+ return out[:n]
143
 
144
 
145
  def label_channel_bytes(n: int, k: int) -> int:
146
  """Exact content size of the packed label channel."""
147
  if int(k) <= 16:
148
+ if n > SEG_TABLE_SIZE * 256 and n % 256 == 0:
149
+ return 10 + SEG_TABLE_SIZE * 128 + (n // 256)
150
  return 10 + (n + 1) // 2
151
  return 10 + n
152
 
 
158
  if k <= 16:
159
  if flat.size and int(flat.max()) > 15:
160
  raise ValueError(f"label {int(flat.max())} does not fit nibble packing (k={k})")
161
+ if n > SEG_TABLE_SIZE * 256 and n % 256 == 0:
162
+ enc = 2
163
+ n_blocks = n // 256
164
+ rows = flat.reshape(n_blocks, 256).astype(np.uint8)
165
+ lo = rows[:, 0::2]
166
+ hi = rows[:, 1::2]
167
+ packed_rows = (lo | (hi << 4)).astype(np.uint8) # (n_blocks, 128)
168
+ row_dt = np.dtype((np.void, 128))
169
+ row_views = np.ascontiguousarray(packed_rows).view(row_dt).ravel()
170
+ uniq_views, inv_idx, counts = np.unique(
171
+ row_views, return_inverse=True, return_counts=True
172
+ )
173
+ uniq_rows = uniq_views.view(np.uint8).reshape(-1, 128)
174
+ table = np.zeros((SEG_TABLE_SIZE, 128), dtype=np.uint8)
175
+ if uniq_rows.shape[0] <= SEG_TABLE_SIZE:
176
+ table[: uniq_rows.shape[0]] = uniq_rows
177
+ block_idx = inv_idx.astype(np.uint8)
178
+ else:
179
+ top_u = np.argsort(-counts)[:SEG_TABLE_SIZE]
180
+ table[:] = uniq_rows[top_u]
181
+ remap = np.zeros(uniq_rows.shape[0], dtype=np.uint8)
182
+ remap[top_u] = np.arange(SEG_TABLE_SIZE, dtype=np.uint8)
183
+ block_idx = remap[inv_idx]
184
+ data = table.tobytes() + block_idx.tobytes()
185
+ else:
186
+ enc = 0
187
+ data = _pack_nibbles_1d(flat)
188
  else:
189
  enc = 1
190
  if flat.size and (int(flat.max()) > 255 or int(flat.min()) < 0):
 
201
  n = _struct.unpack("<I", content[6:10])[0]
202
  data = content[10:]
203
  if enc == 0:
204
+ out = _unpack_nibbles_1d(data, n)
205
+ elif enc == 2:
206
+ n_blocks = n // 256
207
+ tbytes = SEG_TABLE_SIZE * 128
208
+ table = _unpack_nibbles_1d(data[:tbytes], SEG_TABLE_SIZE * 256).reshape(
209
+ SEG_TABLE_SIZE, 256
210
+ )
211
+ idx = np.frombuffer(data[tbytes : tbytes + n_blocks], dtype=np.uint8)
212
+ out = table[idx].reshape(-1)
213
  else:
214
  out = np.frombuffer(data, dtype=np.uint8).copy()
215
  return k, out[:n]
216
 
217
 
218
+ def _order_to_canonical_labels(ord_row: np.ndarray, k: int = 16) -> np.ndarray:
219
+ lab = np.empty(256, dtype=np.int64)
220
+ step = 256 // k
221
+ for g in range(k):
222
+ s0 = g * step
223
+ s1 = 256 if g == k - 1 else (g + 1) * step
224
+ sub = np.sort(ord_row[s0:s1])
225
+ lab[sub] = g
226
+ return lab
227
+
228
+
229
+ def _segment_stage_dense(
230
+ Xo: np.ndarray,
231
+ Wo: np.ndarray | None,
232
+ cfg: HPCConfig,
233
+ proxy,
234
+ k: int,
235
+ ) -> tuple[np.ndarray, np.ndarray, int]:
236
+ labels = band_labels(Xo, k)
237
+ order = make_order(labels)
238
+ rounds = 0
239
+ for _ in range(max(1, int(cfg.segment_iters))):
240
+ rounds += 1
241
+ xg = apply_order(Xo, order)
242
+ wgg = None if Wo is None else apply_order(Wo, order)
243
+ xh = proxy(xg, wgg)
244
+ labels_slot = band_labels(xh, k)
245
+ labels = np.empty_like(labels_slot)
246
+ np.put_along_axis(labels, order, labels_slot, axis=1)
247
+ order = make_order(labels)
248
+ return order, labels, rounds
249
+
250
+
251
  def segment_stage(
252
  X: np.ndarray,
253
  W: np.ndarray | None,
 
257
  ) -> tuple[np.ndarray, np.ndarray, int]:
258
  """Run the label fixed-point loop.
259
 
260
+ For tensors with ``n_blocks <= 256`` (or ``k > 16``), refines a per-block
261
+ label vector directly. For larger tensors (``n_blocks > 256, k <= 16``),
262
+ constructs a 256-entry generalized permutation table (Entry 0 = natural
263
+ order, Entries 1..2 = column RMS/abs profiles, Entries 3..255 = prototype
264
+ blocks refined by ``_segment_stage_dense``) and assigns each superblock its
265
+ optimal 1-byte table index (+0.03125 bpw static+index storage tax).
 
266
  """
267
  Xo = np.asarray(X, dtype=np.float64)
268
  Wo = None if W is None else np.asarray(W, dtype=np.float64)
269
  k = max(1, int(k))
270
+ n_blocks, B = Xo.shape
271
+ if n_blocks <= SEG_TABLE_SIZE or B != 256 or k > 16:
272
+ return _segment_stage_dense(Xo, Wo, cfg, proxy, k)
273
+
274
+ M = SEG_TABLE_SIZE
275
+ table_labels = np.empty((M, 256), dtype=np.int64)
276
+ # Entry 0: natural order (monotone labels so make_order is identity [0..255])
277
+ table_labels[0] = _order_to_canonical_labels(np.arange(256, dtype=np.int64), k)
278
+ # Entry 1 & 2: column-wide RMS and abs profiles
279
+ col_rms = np.sqrt(np.mean(Xo * Xo, axis=0))
280
+ table_labels[1] = _order_to_canonical_labels(np.argsort(col_rms, kind="stable"), k)
281
+ col_abs = np.mean(np.abs(Xo), axis=0)
282
+ table_labels[2] = _order_to_canonical_labels(np.argsort(col_abs, kind="stable"), k)
283
+
284
+ # Entries 3..M-1: prototype blocks refined with the proxy fixed-point loop
285
+ proto_idx = np.linspace(0, n_blocks - 1, M - 3, dtype=np.int64)
286
+ Wp = None if Wo is None else Wo[proto_idx]
287
+ ord_p, _, rounds = _segment_stage_dense(Xo[proto_idx], Wp, cfg, proxy, k)
288
+ for i in range(M - 3):
289
+ table_labels[3 + i] = _order_to_canonical_labels(ord_p[i], k)
290
+
291
+ table_orders = make_order(table_labels) # (M, 256)
292
+
293
+ # Prescreen top-8 table entries per block via between-subblock variance GEMM
294
+ P = np.zeros((256, M * 16), dtype=np.float32)
295
+ for m in range(M):
296
+ for g in range(16):
297
+ P[table_orders[m, 16 * g : 16 * g + 16], m * 16 + g] = 0.25
298
+ Xf = Xo.astype(np.float32)
299
+ scores = ((Xf @ P) ** 2).reshape(n_blocks, M, 16).sum(axis=2) + 0.5 * (
300
+ (np.abs(Xf) @ P) ** 2
301
+ ).reshape(n_blocks, M, 16).sum(axis=2)
302
+ topK = 8
303
+ top_idx = np.argpartition(-scores, topK, axis=1)[:, :topK]
304
+ top_idx[:, 0] = 0 # always include identity (Entry 0) so no block ever regresses
305
+
306
+ best_sse = np.full(n_blocks, np.inf, dtype=np.float64)
307
+ best_m = np.zeros(n_blocks, dtype=np.int64)
308
+ for k_i in range(topK):
309
+ m_col = top_idx[:, k_i]
310
+ ords = table_orders[m_col]
311
+ Xk = apply_order(Xo, ords)
312
+ Wk = None if Wo is None else apply_order(Wo, ords)
313
+ xh = proxy(Xk, Wk)
314
+ err = Xk - xh
315
+ sse_k = np.sum(err * err if Wk is None else Wk * err * err, axis=1)
316
+ better = sse_k < best_sse
317
+ best_sse = np.where(better, sse_k, best_sse)
318
+ best_m = np.where(better, m_col, best_m)
319
+
320
+ return table_orders[best_m], table_labels[best_m], rounds
321
 
322
 
323
  def q2k_proxy_cfg(clamp_val: float, cfg: HPCConfig):