CompressedGemma commited on
Commit
dc0a265
·
verified ·
1 Parent(s): c002b6f

Upload 6 files

Browse files
Files changed (6) hide show
  1. __init__.py +7 -0
  2. __main__.py +4 -3
  3. config.py +2 -2
  4. gguf.py +139 -4
  5. gui.py +67 -12
  6. q1_0.py +429 -0
__init__.py CHANGED
@@ -24,6 +24,7 @@ from .factored import (
24
  from .gguf import (
25
  GGUFReader,
26
  expand_importance,
 
27
  is_q2k_eligible,
28
  is_q4_0_eligible,
29
  lookup_imatrix,
@@ -31,6 +32,7 @@ from .gguf import (
31
  target_quant_type,
32
  )
33
  from .pipeline import Q2KQuantizer, QuantizedTensor, TensorStats
 
34
  from .q2k import Q2KBlock, QK_K, dequantize_params, pack_codes, unpack_codes
35
  from .q4_0 import QK4_0, dequantize_q4_0_bytes, quantize_tensor_q4_0
36
 
@@ -56,11 +58,16 @@ __all__ = [
56
  "factor_q2k_block",
57
  "GGUFReader",
58
  "expand_importance",
 
59
  "is_q2k_eligible",
60
  "is_q4_0_eligible",
61
  "lookup_imatrix",
62
  "read_imatrix",
63
  "target_quant_type",
 
 
 
 
64
  "QK4_0",
65
  "dequantize_q4_0_bytes",
66
  "quantize_tensor_q4_0",
 
24
  from .gguf import (
25
  GGUFReader,
26
  expand_importance,
27
+ is_q1_0_eligible,
28
  is_q2k_eligible,
29
  is_q4_0_eligible,
30
  lookup_imatrix,
 
32
  target_quant_type,
33
  )
34
  from .pipeline import Q2KQuantizer, QuantizedTensor, TensorStats
35
+ from .q1_0 import Q1_0_BLOCK_BYTES, QK1_0, dequantize_q1_0_bytes, quantize_tensor_q1_0
36
  from .q2k import Q2KBlock, QK_K, dequantize_params, pack_codes, unpack_codes
37
  from .q4_0 import QK4_0, dequantize_q4_0_bytes, quantize_tensor_q4_0
38
 
 
58
  "factor_q2k_block",
59
  "GGUFReader",
60
  "expand_importance",
61
+ "is_q1_0_eligible",
62
  "is_q2k_eligible",
63
  "is_q4_0_eligible",
64
  "lookup_imatrix",
65
  "read_imatrix",
66
  "target_quant_type",
67
+ "QK1_0",
68
+ "Q1_0_BLOCK_BYTES",
69
+ "dequantize_q1_0_bytes",
70
+ "quantize_tensor_q1_0",
71
  "QK4_0",
72
  "dequantize_q4_0_bytes",
73
  "quantize_tensor_q4_0",
__main__.py CHANGED
@@ -128,7 +128,8 @@ def _run_gguf(args, cfg: HPCConfig) -> None:
128
  verbose=True,
129
  )
130
  print(
131
- f"wrote {args.output}: {int(stats.get('q2_k', 0))} Q2_K + {int(stats.get('q4_0', 0))} Q4_0 "
 
132
  f"({int(stats['quantized'])}/{int(stats['tensors'])} tensors), rmse={stats['rmse']:.6e}"
133
  )
134
  return
@@ -253,9 +254,9 @@ def main() -> None:
253
  parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
254
  parser.add_argument(
255
  "--format",
256
- choices=("mixed", "q4_0", "q2_k"),
257
  default=None,
258
- help="quantization format mode: mixed (Q2_K if eligible else Q4_0), q4_0 (pure Q4_0), or q2_k (pure Q2_K)",
259
  )
260
  parser.add_argument("--no-q4-fallback", action="store_true", help="disable Q4_0 fallback for non-256-aligned tensors")
261
  parser.add_argument("--keep-embd", action="store_true", help="keep token_embd/output.weight at source precision")
 
128
  verbose=True,
129
  )
130
  print(
131
+ f"wrote {args.output}: {int(stats.get('q2_k', 0))} Q2_K + {int(stats.get('q4_0', 0))} Q4_0 + "
132
+ f"{int(stats.get('q1_0', 0))} Q1_0 "
133
  f"({int(stats['quantized'])}/{int(stats['tensors'])} tensors), rmse={stats['rmse']:.6e}"
134
  )
135
  return
 
254
  parser.add_argument("--no-spectral", action="store_true", help="disable spectral/DC objective")
255
  parser.add_argument(
256
  "--format",
257
+ choices=("mixed", "mixed_q1", "q1_0", "q4_0", "q2_k"),
258
  default=None,
259
+ help="quantization format mode: mixed (Q2_K+Q4_0), mixed_q1 (Q1_0+Q4_0), q1_0 (pure Q1_0), q4_0 (pure Q4_0), or q2_k (pure Q2_K)",
260
  )
261
  parser.add_argument("--no-q4-fallback", action="store_true", help="disable Q4_0 fallback for non-256-aligned tensors")
262
  parser.add_argument("--keep-embd", action="store_true", help="keep token_embd/output.weight at source precision")
config.py CHANGED
@@ -120,6 +120,6 @@ class HPCConfig:
120
  raise ValueError("workers must be >= 0 (0 = auto CPU count)")
121
  if self.device not in ("auto", "cuda", "cpu") and not self.device.startswith("cuda:"):
122
  raise ValueError("device must be 'auto', 'cuda', 'cuda:N', or 'cpu'")
123
- if self.quant_format not in ("mixed", "q4_0", "q2_k"):
124
- raise ValueError("quant_format must be 'mixed', 'q4_0', or 'q2_k'")
125
  return self
 
120
  raise ValueError("workers must be >= 0 (0 = auto CPU count)")
121
  if self.device not in ("auto", "cuda", "cpu") and not self.device.startswith("cuda:"):
122
  raise ValueError("device must be 'auto', 'cuda', 'cuda:N', or 'cpu'")
123
+ if self.quant_format not in ("mixed", "mixed_q1", "q1_0", "q4_0", "q2_k"):
124
+ raise ValueError("quant_format must be 'mixed', 'mixed_q1', 'q1_0', 'q4_0', or 'q2_k'")
125
  return self
gguf.py CHANGED
@@ -23,6 +23,7 @@ import numpy as np
23
 
24
  from .q2k import QK_K, blocks_to_bytes, dequantize_params_batch, unpack_codes
25
  from .q4_0 import Q4_0_BLOCK_BYTES, QK4_0, quantize_tensor_q4_0
 
26
 
27
  GGUF_MAGIC = 0x46554747
28
  DEFAULT_ALIGNMENT = 32
@@ -36,6 +37,7 @@ GGML_TYPE_Q5_1 = 7
36
  GGML_TYPE_Q8_0 = 8
37
  GGML_TYPE_Q2_K = 10
38
  GGML_TYPE_BF16 = 30
 
39
 
40
  TYPE_NAME = {
41
  GGML_TYPE_F32: "F32",
@@ -47,6 +49,7 @@ TYPE_NAME = {
47
  GGML_TYPE_Q8_0: "Q8_0",
48
  GGML_TYPE_Q2_K: "Q2_K",
49
  GGML_TYPE_BF16: "BF16",
 
50
  }
51
 
52
  BLOCK_SIZE = {
@@ -59,6 +62,7 @@ BLOCK_SIZE = {
59
  GGML_TYPE_Q5_1: 32,
60
  GGML_TYPE_Q8_0: 32,
61
  GGML_TYPE_Q2_K: QK_K,
 
62
  }
63
  BLOCK_BYTES = {
64
  GGML_TYPE_F32: 4,
@@ -70,6 +74,7 @@ BLOCK_BYTES = {
70
  GGML_TYPE_Q5_1: 24,
71
  GGML_TYPE_Q8_0: 34,
72
  GGML_TYPE_Q2_K: 84,
 
73
  }
74
  TYPE_BPW = {
75
  GGML_TYPE_F32: 32.0,
@@ -81,6 +86,7 @@ TYPE_BPW = {
81
  GGML_TYPE_Q4_1: 5.0,
82
  GGML_TYPE_Q4_0: 4.5,
83
  GGML_TYPE_Q2_K: 2.625,
 
84
  11: 3.4375, # Q3_K
85
  12: 4.5, # Q4_K
86
  13: 5.5, # Q5_K
@@ -110,6 +116,7 @@ SUPPORTED_READ_TYPES = {
110
  GGML_TYPE_Q4_0,
111
  GGML_TYPE_Q8_0,
112
  GGML_TYPE_Q2_K,
 
113
  }
114
 
115
 
@@ -309,6 +316,11 @@ def _dequantize(raw: bytes, ti: TensorInfo) -> np.ndarray:
309
  high = (qs >> 4).astype(np.float32) - 8.0
310
  q = np.concatenate([low, high], axis=1)
311
  return (d[:, None] * q).reshape(-1)
 
 
 
 
 
312
  if t == GGML_TYPE_Q2_K:
313
  blocks = np.frombuffer(raw, dtype=np.uint8).reshape(-1, 84)
314
  n = blocks.shape[0]
@@ -461,6 +473,24 @@ def is_q4_0_eligible(
461
  return n_elements >= QK4_0 and n_elements % QK4_0 == 0
462
 
463
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
464
  def target_quant_type(
465
  name: str,
466
  dims: tuple[int, ...],
@@ -477,12 +507,34 @@ def target_quant_type(
477
  tensors (and embeddings/head if quantize_embd_q4) -> Q4_0.
478
  * 'q4_0': all 32-aligned eligible weight tensors -> Q4_0.
479
  * 'q2_k': only Q2_K-eligible tensors -> Q2_K; everything else copied.
 
480
 
481
  Never promotes a lower-bit source tensor to a higher-bit target format
482
  (e.g., a Q2_K source tensor is never re-quantized to Q4_0).
483
  """
484
  tgt: str | None = None
485
- if quant_format == "q4_0":
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  if is_q4_0_eligible(
487
  name, dims, include_embd=quantize_embd_q4, allow_q2k_tensors=True
488
  ):
@@ -504,7 +556,7 @@ def target_quant_type(
504
 
505
  if tgt is not None and source_type is not None:
506
  src_bpw = source_type_bpw(int(source_type))
507
- tgt_bpw = 2.625 if tgt == "Q2_K" else 4.5
508
  if src_bpw <= tgt_bpw + 1.0e-6:
509
  return None
510
  return tgt
@@ -565,7 +617,9 @@ def write_quantized_gguf(
565
  )
566
  if target_fmt is None:
567
  src_bpw = source_type_bpw(ti.type)
568
- if q_format == "q4_0" and len(ti.dims) >= 2 and ti.dims[0] % QK4_0 == 0 and src_bpw > 4.5:
 
 
569
  target_fmt = "Q4_0"
570
  elif len(ti.dims) >= 2 and ti.dims[0] % QK_K == 0 and ti.n_elements % QK_K == 0 and src_bpw > 2.625:
571
  target_fmt = "Q2_K"
@@ -607,6 +661,11 @@ def write_quantized_gguf(
607
  out_type = GGML_TYPE_Q4_0
608
  out_size = n_blocks * Q4_0_BLOCK_BYTES
609
  total_planned_blocks += max(1, n_blocks // 8)
 
 
 
 
 
610
  else:
611
  out_type = ti.type
612
  out_size = ti.n_bytes
@@ -614,7 +673,9 @@ def write_quantized_gguf(
614
  data_offset = _align(data_offset + out_size, alignment)
615
 
616
  has_q2k = any(fmt == "Q2_K" for fmt in plan)
617
- ftype_code = 10 if has_q2k else 2 # 10 = MOSTLY_Q2_K, 2 = MOSTLY_Q4_0
 
 
618
  kv_pairs = []
619
  for key, vtype, raw in reader.kv_pairs:
620
  if quantized_count and key == "general.file_type" and vtype == 4:
@@ -629,6 +690,7 @@ def write_quantized_gguf(
629
  total_quantized = 0
630
  total_q2k = 0
631
  total_q4_0 = 0
 
632
  total_elements = 0
633
  completed_planned_blocks = 0
634
  model_t0 = time.time()
@@ -811,6 +873,78 @@ def write_quantized_gguf(
811
  q4_rmse,
812
  has_imat,
813
  )
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
814
  else:
815
  if stage_callback is not None:
816
  stage_callback(
@@ -836,6 +970,7 @@ def write_quantized_gguf(
836
  "quantized": float(total_quantized),
837
  "q2_k": float(total_q2k),
838
  "q4_0": float(total_q4_0),
 
839
  "elements": float(total_elements),
840
  "rmse": rmse,
841
  }
 
23
 
24
  from .q2k import QK_K, blocks_to_bytes, dequantize_params_batch, unpack_codes
25
  from .q4_0 import Q4_0_BLOCK_BYTES, QK4_0, quantize_tensor_q4_0
26
+ from .q1_0 import QK1_0, Q1_0_BLOCK_BYTES, quantize_tensor_q1_0
27
 
28
  GGUF_MAGIC = 0x46554747
29
  DEFAULT_ALIGNMENT = 32
 
37
  GGML_TYPE_Q8_0 = 8
38
  GGML_TYPE_Q2_K = 10
39
  GGML_TYPE_BF16 = 30
40
+ GGML_TYPE_Q1_0 = 41
41
 
42
  TYPE_NAME = {
43
  GGML_TYPE_F32: "F32",
 
49
  GGML_TYPE_Q8_0: "Q8_0",
50
  GGML_TYPE_Q2_K: "Q2_K",
51
  GGML_TYPE_BF16: "BF16",
52
+ GGML_TYPE_Q1_0: "Q1_0",
53
  }
54
 
55
  BLOCK_SIZE = {
 
62
  GGML_TYPE_Q5_1: 32,
63
  GGML_TYPE_Q8_0: 32,
64
  GGML_TYPE_Q2_K: QK_K,
65
+ GGML_TYPE_Q1_0: QK1_0,
66
  }
67
  BLOCK_BYTES = {
68
  GGML_TYPE_F32: 4,
 
74
  GGML_TYPE_Q5_1: 24,
75
  GGML_TYPE_Q8_0: 34,
76
  GGML_TYPE_Q2_K: 84,
77
+ GGML_TYPE_Q1_0: Q1_0_BLOCK_BYTES,
78
  }
79
  TYPE_BPW = {
80
  GGML_TYPE_F32: 32.0,
 
86
  GGML_TYPE_Q4_1: 5.0,
87
  GGML_TYPE_Q4_0: 4.5,
88
  GGML_TYPE_Q2_K: 2.625,
89
+ GGML_TYPE_Q1_0: 1.125,
90
  11: 3.4375, # Q3_K
91
  12: 4.5, # Q4_K
92
  13: 5.5, # Q5_K
 
116
  GGML_TYPE_Q4_0,
117
  GGML_TYPE_Q8_0,
118
  GGML_TYPE_Q2_K,
119
+ GGML_TYPE_Q1_0,
120
  }
121
 
122
 
 
316
  high = (qs >> 4).astype(np.float32) - 8.0
317
  q = np.concatenate([low, high], axis=1)
318
  return (d[:, None] * q).reshape(-1)
319
+ if t == GGML_TYPE_Q1_0:
320
+ blocks = np.frombuffer(raw, dtype=np.uint8).reshape(-1, Q1_0_BLOCK_BYTES)
321
+ d = blocks[:, :2].copy().view("<f2").reshape(-1, 1).astype(np.float32)
322
+ bits = np.unpackbits(blocks[:, 2:], axis=1, bitorder="little")[:, :QK1_0].astype(np.float32)
323
+ return (d * (bits * 2.0 - 1.0)).reshape(-1)
324
  if t == GGML_TYPE_Q2_K:
325
  blocks = np.frombuffer(raw, dtype=np.uint8).reshape(-1, 84)
326
  n = blocks.shape[0]
 
473
  return n_elements >= QK4_0 and n_elements % QK4_0 == 0
474
 
475
 
476
+ def is_q1_0_eligible(
477
+ name: str,
478
+ dims: tuple[int, ...],
479
+ include_embd: bool = True,
480
+ ) -> bool:
481
+ """True for 128-aligned 2D/3D weight tensors eligible for Q1_0."""
482
+ if _is_skipped_metadata_tensor(name, dims):
483
+ return False
484
+ if _is_embd_or_head(name) and not include_embd:
485
+ return False
486
+ if len(dims) < 2 or dims[0] % QK1_0 != 0:
487
+ return False
488
+ n_elements = 1
489
+ for d in dims:
490
+ n_elements *= int(d)
491
+ return n_elements >= QK1_0 and n_elements % QK1_0 == 0
492
+
493
+
494
  def target_quant_type(
495
  name: str,
496
  dims: tuple[int, ...],
 
507
  tensors (and embeddings/head if quantize_embd_q4) -> Q4_0.
508
  * 'q4_0': all 32-aligned eligible weight tensors -> Q4_0.
509
  * 'q2_k': only Q2_K-eligible tensors -> Q2_K; everything else copied.
510
+ * 'q1_0': all 128-aligned eligible weight tensors -> Q1_0.
511
 
512
  Never promotes a lower-bit source tensor to a higher-bit target format
513
  (e.g., a Q2_K source tensor is never re-quantized to Q4_0).
514
  """
515
  tgt: str | None = None
516
+ if quant_format == "q1_0":
517
+ if is_q1_0_eligible(name, dims, include_embd=quantize_embd_q4):
518
+ tgt = "Q1_0"
519
+ elif enable_q4_fallback and is_q4_0_eligible(
520
+ name, dims, include_embd=quantize_embd_q4, promote_attn=False
521
+ ):
522
+ tgt = "Q4_0"
523
+ elif quant_format == "mixed_q1":
524
+ if promote_attn_q4 and _is_attention_projection(name) and is_q4_0_eligible(
525
+ name, dims, include_embd=quantize_embd_q4, promote_attn=True
526
+ ):
527
+ tgt = "Q4_0"
528
+ elif _is_embd_or_head(name):
529
+ if quantize_embd_q4 and is_q4_0_eligible(name, dims, include_embd=True):
530
+ tgt = "Q4_0"
531
+ elif is_q1_0_eligible(name, dims, include_embd=False):
532
+ tgt = "Q1_0"
533
+ elif enable_q4_fallback and is_q4_0_eligible(
534
+ name, dims, include_embd=quantize_embd_q4, promote_attn=False
535
+ ):
536
+ tgt = "Q4_0"
537
+ elif quant_format == "q4_0":
538
  if is_q4_0_eligible(
539
  name, dims, include_embd=quantize_embd_q4, allow_q2k_tensors=True
540
  ):
 
556
 
557
  if tgt is not None and source_type is not None:
558
  src_bpw = source_type_bpw(int(source_type))
559
+ tgt_bpw = {"Q2_K": 2.625, "Q4_0": 4.5, "Q1_0": 1.125}[tgt]
560
  if src_bpw <= tgt_bpw + 1.0e-6:
561
  return None
562
  return tgt
 
617
  )
618
  if target_fmt is None:
619
  src_bpw = source_type_bpw(ti.type)
620
+ if q_format == "q1_0" and len(ti.dims) >= 2 and ti.dims[0] % QK1_0 == 0 and src_bpw > 1.125:
621
+ target_fmt = "Q1_0"
622
+ elif q_format == "q4_0" and len(ti.dims) >= 2 and ti.dims[0] % QK4_0 == 0 and src_bpw > 4.5:
623
  target_fmt = "Q4_0"
624
  elif len(ti.dims) >= 2 and ti.dims[0] % QK_K == 0 and ti.n_elements % QK_K == 0 and src_bpw > 2.625:
625
  target_fmt = "Q2_K"
 
661
  out_type = GGML_TYPE_Q4_0
662
  out_size = n_blocks * Q4_0_BLOCK_BYTES
663
  total_planned_blocks += max(1, n_blocks // 8)
664
+ elif fmt == "Q1_0":
665
+ n_blocks = ti.n_elements // QK1_0
666
+ out_type = GGML_TYPE_Q1_0
667
+ out_size = n_blocks * Q1_0_BLOCK_BYTES
668
+ total_planned_blocks += max(1, n_blocks // 32)
669
  else:
670
  out_type = ti.type
671
  out_size = ti.n_bytes
 
673
  data_offset = _align(data_offset + out_size, alignment)
674
 
675
  has_q2k = any(fmt == "Q2_K" for fmt in plan)
676
+ has_q1_0 = any(fmt == "Q1_0" for fmt in plan)
677
+ # 10 = MOSTLY_Q2_K, 2 = MOSTLY_Q4_0, 40 = MOSTLY_Q1_0
678
+ ftype_code = 10 if has_q2k else (40 if has_q1_0 else 2)
679
  kv_pairs = []
680
  for key, vtype, raw in reader.kv_pairs:
681
  if quantized_count and key == "general.file_type" and vtype == 4:
 
690
  total_quantized = 0
691
  total_q2k = 0
692
  total_q4_0 = 0
693
+ total_q1_0 = 0
694
  total_elements = 0
695
  completed_planned_blocks = 0
696
  model_t0 = time.time()
 
873
  q4_rmse,
874
  has_imat,
875
  )
876
+ elif fmt == "Q1_0":
877
+ t_blocks_q1 = ti.n_elements // QK1_0
878
+ eff_tb = max(1, t_blocks_q1 // 32)
879
+ raw_imp = lookup_imatrix(imat, ti.name) if imat is not None else None
880
+ has_imat = raw_imp is not None and raw_imp.size == ti.row_width
881
+ written_bytes = 0
882
+ sse_q1 = 0.0
883
+ done_q1_blks = 0
884
+ t_q1_start = time.time()
885
+
886
+ for x_chunk, row, _r0, _r1 in reader.iter_tensor_row_chunks(ti.name, max_elements=8_388_608):
887
+ if stop_event is not None and stop_event.is_set():
888
+ raise InterruptedError("Quantization cancelled by user")
889
+ imp_chunk = expand_importance(raw_imp, x_chunk.size, row) if has_imat else None
890
+ q1_bytes, nb_chunk, chunk_rmse = quantize_tensor_q1_0(
891
+ x_chunk, importance=imp_chunk, cfg=cfg
892
+ )
893
+ fout.write(q1_bytes)
894
+ written_bytes += len(q1_bytes)
895
+ sse_q1 += (chunk_rmse * chunk_rmse) * x_chunk.size
896
+ done_q1_blks += nb_chunk
897
+ if stage_callback is not None:
898
+ frac = done_q1_blks / max(t_blocks_q1, 1)
899
+ eff_blocks = completed_planned_blocks + frac * eff_tb
900
+ elapsed_model = max(time.time() - model_t0, 1.0e-6)
901
+ model_rate = eff_blocks / elapsed_model
902
+ model_eta = max(total_planned_blocks - eff_blocks, 0.0) / max(model_rate, 1.0e-6)
903
+ el_q1 = max(time.time() - t_q1_start, 1.0e-6)
904
+ r_q1 = done_q1_blks / el_q1
905
+ eta_q1 = (t_blocks_q1 - done_q1_blks) / max(r_q1, 1.0e-6)
906
+ stage_callback(
907
+ idx + 1,
908
+ len(out_infos),
909
+ ti,
910
+ {
911
+ "phase": "Q1_0: Analog + WLS + Sieve/Viterbi + FP16-ULP Quantization",
912
+ "done": done_q1_blks,
913
+ "total": t_blocks_q1,
914
+ "detail": f"blk {done_q1_blks:,}/{t_blocks_q1:,} ({r_q1:.0f} blk/s) rmse={chunk_rmse:.6e}",
915
+ "rate": r_q1,
916
+ "eta": eta_q1,
917
+ "rmse": chunk_rmse,
918
+ "sigma": float(np.sqrt(np.mean(x_chunk.astype(np.float64) ** 2))),
919
+ "model_pct": 100.0 * eff_blocks / max(total_planned_blocks, 1),
920
+ "model_eta": model_eta,
921
+ "model_done_blocks": int(eff_blocks),
922
+ "model_total_blocks": total_planned_blocks,
923
+ },
924
+ )
925
+
926
+ if written_bytes != out_size:
927
+ raise RuntimeError(f"{ti.name}: Q1_0 payload size mismatch")
928
+ completed_planned_blocks += eff_tb
929
+ q1_rmse = float(np.sqrt(sse_q1 / ti.n_elements))
930
+ total_rmse += sse_q1
931
+ total_elements += ti.n_elements
932
+ total_quantized += 1
933
+ total_q1_0 += 1
934
+ if verbose:
935
+ print(
936
+ f" Q1_0 {ti.name:48s} row={ti.row_width:6d} "
937
+ f"rmse={q1_rmse:.6e} imatrix={'yes' if has_imat else 'no'}"
938
+ )
939
+ if progress_callback is not None:
940
+ progress_callback(
941
+ idx + 1,
942
+ len(out_infos),
943
+ ti.name,
944
+ "Q1_0",
945
+ q1_rmse,
946
+ has_imat,
947
+ )
948
  else:
949
  if stage_callback is not None:
950
  stage_callback(
 
970
  "quantized": float(total_quantized),
971
  "q2_k": float(total_q2k),
972
  "q4_0": float(total_q4_0),
973
+ "q1_0": float(total_q1_0),
974
  "elements": float(total_elements),
975
  "rmse": rmse,
976
  }
gui.py CHANGED
@@ -36,9 +36,13 @@ from hpc_quantize_v2 import (
36
  Q2KQuantizer,
37
  cuda_device_name,
38
  cuda_is_available,
 
 
39
  expand_importance,
40
  factor_only_solve,
41
  lookup_imatrix,
 
 
42
  read_imatrix,
43
  )
44
  from hpc_quantize_v2.gguf import is_q2k_eligible, target_quant_type, write_quantized_gguf
@@ -65,7 +69,7 @@ def _fmt_eta(seconds: float) -> str:
65
  class HPCQuantizeGUI:
66
  def __init__(self, root: tk.Tk) -> None:
67
  self.root = root
68
- self.root.title("HPC-Quantize V2 — Factored Q2_K & Geometry-Preserving Analog Engine")
69
  self.root.geometry("1260x900")
70
  self.root.minsize(1040, 740)
71
 
@@ -259,7 +263,7 @@ class HPCQuantizeGUI:
259
  mid.add(right_pane, weight=3)
260
 
261
  # Left: Engine & Factorization Options
262
- opt_frame = ttk.LabelFrame(left_pane, text="2. Factored Q2_K & Analog Controls", padding=8)
263
  opt_frame.pack(fill=tk.BOTH, expand=True, padx=(0, 6))
264
 
265
  mode_row = ttk.Frame(opt_frame)
@@ -276,16 +280,18 @@ class HPCQuantizeGUI:
276
  fmt_row.pack(fill=tk.X, pady=(0, 4))
277
  ttk.Label(fmt_row, text="Quant Format:").pack(side=tk.LEFT, padx=(0, 8))
278
  for lbl, val in (
279
- ("Mixed (Q2_K if eligible, else Q4_0)", "mixed"),
280
- ("Pure Q4_0", "q4_0"),
281
- ("Pure Q2_K", "q2_k"),
 
 
282
  ):
283
  ttk.Radiobutton(
284
  fmt_row,
285
  text=lbl,
286
  variable=self.quant_format_var,
287
  value=val,
288
- command=self._populate_tensor_tree,
289
  ).pack(side=tk.LEFT, padx=3)
290
 
291
  opt_row = ttk.Frame(opt_frame)
@@ -390,7 +396,7 @@ class HPCQuantizeGUI:
390
  filter_entry = ttk.Entry(search_row, textvariable=self.filter_var)
391
  filter_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
392
  filter_entry.bind("<KeyRelease>", lambda _e: self._populate_tensor_tree())
393
- for label, val in (("All", "ALL"), ("Q2_K", "Q2_K"), ("Q4_0", "Q4_0"), ("Copy", "COPY")):
394
  ttk.Radiobutton(
395
  search_row,
396
  text=label,
@@ -411,6 +417,7 @@ class HPCQuantizeGUI:
411
  self.tree.heading("q2k", text="Target")
412
  self.tree.heading("imat", text="iMatrix")
413
  self.tree.heading("state", text="Live Status / RMSE")
 
414
  self.tree.tag_configure("Q2_K", foreground="#93c5fd")
415
  self.tree.tag_configure("Q4_0", foreground="#34d399")
416
  self.tree.tag_configure("Copy", foreground="#9ca3af")
@@ -537,6 +544,27 @@ class HPCQuantizeGUI:
537
  self.output_var.set(str(default_gguf.with_name("Raon-Q2K.gguf").resolve()))
538
  self.inspect_files()
539
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
540
  def _browse_gguf(self) -> None:
541
  path = filedialog.askopenfilename(
542
  title="Select Source GGUF",
@@ -546,7 +574,7 @@ class HPCQuantizeGUI:
546
  self.gguf_var.set(path)
547
  p = Path(path)
548
  if not self.output_var.get():
549
- self.output_var.set(str(p.with_name(f"{p.stem}-Q2K.gguf")))
550
  self.inspect_files()
551
 
552
  def _browse_imatrix(self) -> None:
@@ -677,6 +705,7 @@ class HPCQuantizeGUI:
677
  filt = self.filter_var.get().strip().lower()
678
  plan_filt = self.plan_filter_var.get()
679
  n_q2, n_q4, n_copy = 0, 0, 0
 
680
  for ti in self._reader.tensors:
681
  tgt = target_quant_type(
682
  ti.name,
@@ -691,6 +720,8 @@ class HPCQuantizeGUI:
691
  n_q2 += 1
692
  elif tgt == "Q4_0":
693
  n_q4 += 1
 
 
694
  else:
695
  n_copy += 1
696
 
@@ -711,7 +742,7 @@ class HPCQuantizeGUI:
711
  state_str = (
712
  f"Pending ({tgt_label})"
713
  if tgt
714
- else (f"Keep {ti.type_name} (<= target)" if ti.type_name in ("Q2_K", "Q4_0") else "Passthrough")
715
  )
716
  self.tree.insert(
717
  "",
@@ -729,7 +760,7 @@ class HPCQuantizeGUI:
729
  ),
730
  )
731
  self.status_var.set(
732
- f"Plan ({self.quant_format_var.get().upper()}): {n_q2} Q2_K | {n_q4} Q4_0 | {n_copy} Copy (Total: {len(self._reader.tensors)} tensors)"
733
  )
734
 
735
  def _select_all_eligible(self) -> None:
@@ -837,6 +868,30 @@ class HPCQuantizeGUI:
837
  raw = lookup_imatrix(imat, tname)
838
  if raw is not None:
839
  imp = expand_importance(raw, x.size, row)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
840
  res = quantizer.quantize(
841
  x,
842
  importance=imp,
@@ -880,7 +935,7 @@ class HPCQuantizeGUI:
880
  self._log_queue.put(
881
  (
882
  "done",
883
- f"Wrote {out_path}: {int(stats.get('q2_k', 0))} Q2_K + {int(stats.get('q4_0', 0))} Q4_0 "
884
  f"({int(stats['quantized'])}/{int(stats['tensors'])} tensors), "
885
  f"overall RMSE={stats['rmse']:.6e} ({dt_sec:.1f}s)",
886
  )
@@ -1024,7 +1079,7 @@ class HPCQuantizeGUI:
1024
  done, total, name, action, rmse, has_imat = payload # type: ignore[misc]
1025
  self.model_progress_var.set(100.0 * done / max(total, 1))
1026
  self.tensor_progress_var.set(100.0)
1027
- if action in ("Q2_K", "Q4_0") and rmse is not None:
1028
  self._update_tree_row_state(name, f"{action} (RMSE={rmse:.4e})")
1029
  self._log(
1030
  f"[{done}/{total}] FINISHED {name:42s} -> {action} RMSE={rmse:.6e} (iMatrix={'yes' if has_imat else 'no'})"
 
36
  Q2KQuantizer,
37
  cuda_device_name,
38
  cuda_is_available,
39
+ dequantize_q1_0_bytes,
40
+ dequantize_q4_0_bytes,
41
  expand_importance,
42
  factor_only_solve,
43
  lookup_imatrix,
44
+ quantize_tensor_q1_0,
45
+ quantize_tensor_q4_0,
46
  read_imatrix,
47
  )
48
  from hpc_quantize_v2.gguf import is_q2k_eligible, target_quant_type, write_quantized_gguf
 
69
  class HPCQuantizeGUI:
70
  def __init__(self, root: tk.Tk) -> None:
71
  self.root = root
72
+ self.root.title("HPC-Quantize V2 — Factored Q1_0 / Q2_K / Q4_0 & Geometry-Preserving Analog Engine")
73
  self.root.geometry("1260x900")
74
  self.root.minsize(1040, 740)
75
 
 
263
  mid.add(right_pane, weight=3)
264
 
265
  # Left: Engine & Factorization Options
266
+ opt_frame = ttk.LabelFrame(left_pane, text="2. Factored Q1_0 / Q2_K / Q4_0 & Analog Controls", padding=8)
267
  opt_frame.pack(fill=tk.BOTH, expand=True, padx=(0, 6))
268
 
269
  mode_row = ttk.Frame(opt_frame)
 
280
  fmt_row.pack(fill=tk.X, pady=(0, 4))
281
  ttk.Label(fmt_row, text="Quant Format:").pack(side=tk.LEFT, padx=(0, 8))
282
  for lbl, val in (
283
+ ("Mixed Q2_K + Q4_0", "mixed"),
284
+ ("Mixed Q1_0 + Q4_0", "mixed_q1"),
285
+ ("Pure Q1_0 (1.125 bpw)", "q1_0"),
286
+ ("Pure Q2_K (2.625 bpw)", "q2_k"),
287
+ ("Pure Q4_0 (4.500 bpw)", "q4_0"),
288
  ):
289
  ttk.Radiobutton(
290
  fmt_row,
291
  text=lbl,
292
  variable=self.quant_format_var,
293
  value=val,
294
+ command=self._on_format_changed,
295
  ).pack(side=tk.LEFT, padx=3)
296
 
297
  opt_row = ttk.Frame(opt_frame)
 
396
  filter_entry = ttk.Entry(search_row, textvariable=self.filter_var)
397
  filter_entry.pack(side=tk.LEFT, fill=tk.X, expand=True)
398
  filter_entry.bind("<KeyRelease>", lambda _e: self._populate_tensor_tree())
399
+ for label, val in (("All", "ALL"), ("Q1_0", "Q1_0"), ("Q2_K", "Q2_K"), ("Q4_0", "Q4_0"), ("Copy", "COPY")):
400
  ttk.Radiobutton(
401
  search_row,
402
  text=label,
 
417
  self.tree.heading("q2k", text="Target")
418
  self.tree.heading("imat", text="iMatrix")
419
  self.tree.heading("state", text="Live Status / RMSE")
420
+ self.tree.tag_configure("Q1_0", foreground="#fbbf24")
421
  self.tree.tag_configure("Q2_K", foreground="#93c5fd")
422
  self.tree.tag_configure("Q4_0", foreground="#34d399")
423
  self.tree.tag_configure("Copy", foreground="#9ca3af")
 
544
  self.output_var.set(str(default_gguf.with_name("Raon-Q2K.gguf").resolve()))
545
  self.inspect_files()
546
 
547
+ def _default_out_suffix(self) -> str:
548
+ fmt = self.quant_format_var.get()
549
+ if fmt == "q1_0":
550
+ return "-Q1_0.gguf"
551
+ if fmt == "mixed_q1":
552
+ return "-Q1_0-Q4_0.gguf"
553
+ if fmt == "q4_0":
554
+ return "-Q4_0.gguf"
555
+ return "-Q2K.gguf"
556
+
557
+ def _on_format_changed(self) -> None:
558
+ cur_out = self.output_var.get().strip()
559
+ gguf_p = self.gguf_var.get().strip()
560
+ if gguf_p and (
561
+ not cur_out
562
+ or any(cur_out.endswith(sfx) for sfx in ("-Q2K.gguf", "-Q1_0.gguf", "-Q1_0-Q4_0.gguf", "-Q4_0.gguf"))
563
+ ):
564
+ p = Path(gguf_p)
565
+ self.output_var.set(str(p.with_name(f"{p.stem}{self._default_out_suffix()}")))
566
+ self._populate_tensor_tree()
567
+
568
  def _browse_gguf(self) -> None:
569
  path = filedialog.askopenfilename(
570
  title="Select Source GGUF",
 
574
  self.gguf_var.set(path)
575
  p = Path(path)
576
  if not self.output_var.get():
577
+ self.output_var.set(str(p.with_name(f"{p.stem}{self._default_out_suffix()}")))
578
  self.inspect_files()
579
 
580
  def _browse_imatrix(self) -> None:
 
705
  filt = self.filter_var.get().strip().lower()
706
  plan_filt = self.plan_filter_var.get()
707
  n_q2, n_q4, n_copy = 0, 0, 0
708
+ n_q1 = 0
709
  for ti in self._reader.tensors:
710
  tgt = target_quant_type(
711
  ti.name,
 
720
  n_q2 += 1
721
  elif tgt == "Q4_0":
722
  n_q4 += 1
723
+ elif tgt == "Q1_0":
724
+ n_q1 += 1
725
  else:
726
  n_copy += 1
727
 
 
742
  state_str = (
743
  f"Pending ({tgt_label})"
744
  if tgt
745
+ else (f"Keep {ti.type_name} (<= target)" if ti.type_name in ("Q1_0", "Q2_K", "Q4_0") else "Passthrough")
746
  )
747
  self.tree.insert(
748
  "",
 
760
  ),
761
  )
762
  self.status_var.set(
763
+ f"Plan ({self.quant_format_var.get().upper()}): {n_q1} Q1_0 | {n_q2} Q2_K | {n_q4} Q4_0 | {n_copy} Copy (Total: {len(self._reader.tensors)} tensors)"
764
  )
765
 
766
  def _select_all_eligible(self) -> None:
 
868
  raw = lookup_imatrix(imat, tname)
869
  if raw is not None:
870
  imp = expand_importance(raw, x.size, row)
871
+ if cfg.quant_format in ("q1_0", "mixed_q1"):
872
+ raw_q1, nb_q1, rmse_q1 = quantize_tensor_q1_0(x, importance=imp, cfg=cfg)
873
+ np.savez_compressed(
874
+ out_path,
875
+ q1_0_bytes=np.frombuffer(raw_q1, dtype=np.uint8),
876
+ dequantized=dequantize_q1_0_bytes(raw_q1, nb_q1),
877
+ rmse=np.float32(rmse_q1),
878
+ )
879
+ self._log_queue.put(
880
+ ("done", f"Saved Q1_0 {out_path} (RMSE={rmse_q1:.6e}, {time.time() - t0:.1f}s)")
881
+ )
882
+ return
883
+ if cfg.quant_format == "q4_0":
884
+ raw_q4, nb_q4, rmse_q4 = quantize_tensor_q4_0(x, importance=imp, cfg=cfg)
885
+ np.savez_compressed(
886
+ out_path,
887
+ q4_0_bytes=np.frombuffer(raw_q4, dtype=np.uint8),
888
+ dequantized=dequantize_q4_0_bytes(raw_q4, nb_q4),
889
+ rmse=np.float32(rmse_q4),
890
+ )
891
+ self._log_queue.put(
892
+ ("done", f"Saved Q4_0 {out_path} (RMSE={rmse_q4:.6e}, {time.time() - t0:.1f}s)")
893
+ )
894
+ return
895
  res = quantizer.quantize(
896
  x,
897
  importance=imp,
 
935
  self._log_queue.put(
936
  (
937
  "done",
938
+ f"Wrote {out_path}: {int(stats.get('q1_0', 0))} Q1_0 + {int(stats.get('q2_k', 0))} Q2_K + {int(stats.get('q4_0', 0))} Q4_0 "
939
  f"({int(stats['quantized'])}/{int(stats['tensors'])} tensors), "
940
  f"overall RMSE={stats['rmse']:.6e} ({dt_sec:.1f}s)",
941
  )
 
1079
  done, total, name, action, rmse, has_imat = payload # type: ignore[misc]
1080
  self.model_progress_var.set(100.0 * done / max(total, 1))
1081
  self.tensor_progress_var.set(100.0)
1082
+ if action in ("Q1_0", "Q2_K", "Q4_0") and rmse is not None:
1083
  self._update_tree_row_state(name, f"{action} (RMSE={rmse:.4e})")
1084
  self._log(
1085
  f"[{done}/{total}] FINISHED {name:42s} -> {action} RMSE={rmse:.6e} (iMatrix={'yes' if has_imat else 'no'})"
q1_0.py ADDED
@@ -0,0 +1,429 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Full HPC V2 Q1_0 quantizer (CUDA GPU + Vectorized CPU).
2
+
3
+ Implements the complete HPC-Quantize V2 decision stack for ``Q1_0`` (128 weights
4
+ per 18-byte block = 1.125 bpw, matching ``block_q1_0`` in ``llama.cpp``):
5
+
6
+ 1. **Geometry-Preserving Analog + WLS Seeds**:
7
+ Finds optimal ``d*`` from the llama.cpp reference ``mean(|x|)``, importance-
8
+ weighted WLS ``sum(w*|x|)/sum(w)``, and an iso-moment companded analog of
9
+ each 128-weight block (including negative-scale sign-flip starts ``-d``).
10
+ 2. **24-Candidate Scale Lattice & Extended Spectral Objective**:
11
+ Evaluates 24 scale multipliers (mapped to 6 symbolic quhit states) on
12
+ importance-weighted SSE + D6/Vesica fold pyramid + DC penalty.
13
+ 3. **6-State Sequential Sieve & 6-State Viterbi DP**:
14
+ Runs the 6-state sequential Sieve with parity back-action and a 6-state
15
+ Viterbi dynamic program with free-energy bin reduction and Manhattan
16
+ transition penalty across the block chain.
17
+ 4. **Asymmetric Sign-Flip fp16-ULP Polish & Closed-Form Vesica/DC Shaping**:
18
+ Polishes ``d`` over ``+/-8`` fp16 ULPs of both ``+d`` and ``-d``, and
19
+ applies closed-form 1-D quadratic Vesica + DC bit-flip projection bounded by
20
+ ``sse_budget`` plus a strict monotone candidate floor.
21
+ """
22
+
23
+ from __future__ import annotations
24
+
25
+ from math import ceil
26
+ import numpy as np
27
+
28
+ try:
29
+ import torch
30
+ except ImportError: # pragma: no cover
31
+ torch = None # type: ignore[assignment]
32
+
33
+ from .config import HPCConfig
34
+ from .cuda_backend import cuda_is_available
35
+ from .factored import MULTS_D
36
+ from .fp16 import from_fp16_bits, nearest_int, to_fp16_bits
37
+ from .sieve import sieve_measure_chain, subblock_chain_edges
38
+ from .viterbi import robust_temperature
39
+
40
+ QK1_0 = 128
41
+ Q1_0_BLOCK_BYTES = 18
42
+ Q1_N_CAND = 24
43
+ Q1_N_STATES = 6
44
+
45
+ _BIT_WEIGHTS = (1 << np.arange(8, dtype=np.uint8))[None, None, :]
46
+
47
+
48
+ def _pack_q1_bits_np(q_u8: np.ndarray) -> np.ndarray:
49
+ """Pack (B, 128) uint8 bits {0, 1} into (B, 16) LSB-first bytes."""
50
+ B = q_u8.shape[0]
51
+ bits = (q_u8.reshape(B, 16, 8) & 1).astype(np.uint8)
52
+ return np.sum(bits * _BIT_WEIGHTS, axis=2, dtype=np.uint8)
53
+
54
+
55
+ def dequantize_q1_0_bytes(raw: bytes | np.ndarray, n_blocks: int) -> np.ndarray:
56
+ """Decode GGML Q1_0 blocks into float32 weights."""
57
+ blocks = np.frombuffer(raw, dtype=np.uint8).reshape(n_blocks, Q1_0_BLOCK_BYTES)
58
+ d = blocks[:, :2].copy().view("<f2").reshape(n_blocks, 1).astype(np.float32)
59
+ qs = blocks[:, 2:]
60
+ bits = np.unpackbits(qs, axis=1, bitorder="little")[:, :QK1_0].astype(np.float32)
61
+ signs = bits * 2.0 - 1.0
62
+ return (d * signs).reshape(-1)
63
+
64
+
65
+ def _q1_sieve_and_viterbi(cand_errors: np.ndarray, cfg: HPCConfig) -> np.ndarray:
66
+ """6-state sequential Sieve + 6-state Viterbi DP over Q1_0 candidate lattice."""
67
+ B = cand_errors.shape[0]
68
+ if cfg.optimizer != "hpc" or B < 2:
69
+ return np.argmin(cand_errors, axis=1).astype(np.int64)
70
+
71
+ max_gb = min(int(cfg.graph_max_blocks), 512)
72
+ stride = max(1, ceil(B / max_gb))
73
+ gb = ceil(B / stride)
74
+ temp = max(float(robust_temperature(cand_errors, cfg)), 1.0e-12)
75
+
76
+ pad = gb * stride - B
77
+ ce = cand_errors.astype(np.float64)
78
+ if pad > 0:
79
+ ce = np.concatenate([ce, np.full((pad, Q1_N_CAND), np.inf, dtype=np.float64)], axis=0)
80
+ ce_grouped = ce.reshape(gb, stride, Q1_N_STATES, 4)
81
+
82
+ group_min = np.min(ce_grouped, axis=(1, 3)) # (gb, 6)
83
+ finite = np.isfinite(ce_grouped)
84
+ counts = np.sum(finite, axis=(1, 3))
85
+ valid = (counts > 0) & np.isfinite(group_min)
86
+ gm_b = np.where(np.isfinite(group_min), group_min, 0.0)[:, None, :, None]
87
+ shifted = np.where(finite, (ce_grouped - gm_b) / (2.0 * temp), np.inf)
88
+ sums = np.sum(np.exp(-shifted), axis=(1, 3))
89
+ mean_exp = np.maximum(sums / np.maximum(counts, 1), 1.0e-300)
90
+ bin_err = np.where(valid, group_min - 2.0 * temp * np.log(mean_exp), np.inf)
91
+
92
+ agg = np.mean(np.where(finite, ce_grouped, 0.0), axis=1).reshape(gb, Q1_N_CAND)
93
+ mn = np.min(agg, axis=1, keepdims=True)
94
+ coeff = np.exp(-np.clip((agg - mn) / (2.0 * temp), 0.0, 80.0)).reshape(gb, Q1_N_STATES, 4)
95
+ amps = np.sum(coeff, axis=2)
96
+ norms = np.linalg.norm(amps, axis=1, keepdims=True)
97
+ amps = np.where(norms > 1.0e-30, amps / np.maximum(norms, 1.0e-30), 1.0 / np.sqrt(6.0))
98
+
99
+ marg, _ = sieve_measure_chain(
100
+ amps,
101
+ subblock_chain_edges(gb),
102
+ same_penalty=cfg.same_penalty,
103
+ log_slack=cfg.log_slack,
104
+ deterministic=True,
105
+ )
106
+ m_tot = np.sum(marg, axis=1, keepdims=True)
107
+ pm = np.where(m_tot > 1.0e-30, marg / np.maximum(m_tot, 1.0e-30), 1.0 / 6.0)
108
+ log_prior = np.log(pm + 1.0e-30)
109
+
110
+ fin_err = bin_err[np.isfinite(bin_err)]
111
+ scale_err = max(float(fin_err.mean()) if fin_err.size else 1.0e-10, 1.0e-20)
112
+ local = np.where(np.isfinite(bin_err), bin_err - cfg.viterbi_beta * scale_err * log_prior, np.inf)
113
+
114
+ states = np.arange(Q1_N_STATES)
115
+ trans = cfg.viterbi_alpha * scale_err * np.abs(states[None, :] - states[:, None])
116
+ dp = np.full((gb, Q1_N_STATES), np.inf, dtype=np.float64)
117
+ back = np.zeros((gb, Q1_N_STATES), dtype=np.int64)
118
+ dp[0] = local[0]
119
+ for gi in range(1, gb):
120
+ totals = dp[gi - 1][:, None] + trans
121
+ best_sp = np.argmin(totals, axis=0)
122
+ best_val = totals[best_sp, states]
123
+ dp[gi] = np.where(np.isfinite(best_val), best_val + local[gi], np.inf)
124
+ back[gi] = best_sp
125
+
126
+ path = np.zeros(gb, dtype=np.int64)
127
+ if np.any(np.isfinite(dp[-1])):
128
+ path[-1] = int(np.argmin(dp[-1]))
129
+ for gi in range(gb - 2, -1, -1):
130
+ path[gi] = back[gi + 1, path[gi + 1]]
131
+
132
+ ce_bins = cand_errors.reshape(B, Q1_N_STATES, 4)
133
+ bin_local_arg = np.argmin(ce_bins, axis=2)
134
+ blk_gi = np.arange(B) // stride
135
+ chosen_bin = path[blk_gi]
136
+ chosen_cand = chosen_bin * 4 + bin_local_arg[np.arange(B), chosen_bin]
137
+
138
+ global_best = np.argmin(cand_errors, axis=1).astype(np.int64)
139
+ cur_e = cand_errors[np.arange(B), chosen_cand]
140
+ g_e = cand_errors[np.arange(B), global_best]
141
+ return np.where(g_e < cur_e * cfg.greedy_override_ratio, global_best, chosen_cand)
142
+
143
+
144
+ def _quantize_q1_0_hpc_cpu(
145
+ X: np.ndarray, W: np.ndarray, cfg: HPCConfig
146
+ ) -> tuple[bytes, float]:
147
+ """Full HPC V2 Q1_0 pipeline on CPU for a chunk of (B, 128) blocks."""
148
+ B = X.shape[0]
149
+ X_f32 = X.astype(np.float32)
150
+ W_f32 = W.astype(np.float32)
151
+ lam_dc = (float(cfg.dc_lambda) / QK1_0) if cfg.use_spectral else 0.0
152
+ lam_vw = (float(cfg.vw_lambda) / QK1_0) if cfg.use_spectral else 0.0
153
+ half = QK1_0 // 2
154
+
155
+ # 1. Analog + WLS seed for d* (testing mean, weighted WLS, and companded analog):
156
+ d_mean = np.mean(np.abs(X_f32), axis=1).astype(np.float32)
157
+ wsum = np.maximum(np.sum(W_f32, axis=1, keepdims=True), 1.0e-30)
158
+ d_wls_init = (np.sum(W_f32 * np.abs(X_f32), axis=1, keepdims=True) / wsum)[:, 0].astype(np.float32)
159
+ mu = np.sum(W_f32 * X_f32, axis=1, keepdims=True) / wsum
160
+ var = np.sum(W_f32 * (X_f32 - mu) ** 2, axis=1, keepdims=True) / wsum
161
+ sig = np.sqrt(np.maximum(var, 1.0e-30))
162
+ x_comp = mu + sig * 1.85 * np.tanh((X_f32 - mu) / (1.85 * sig))
163
+ d_analog = (np.sum(W_f32 * np.abs(x_comp), axis=1, keepdims=True) / wsum)[:, 0].astype(np.float32)
164
+
165
+ wls_d = d_mean.copy()
166
+ best_seed_sse = np.full(B, np.inf, dtype=np.float64)
167
+ for d_start in (d_mean, d_wls_init, d_analog, -d_analog):
168
+ d = d_start.astype(np.float64)
169
+ for _ in range(2):
170
+ q = np.where(d[:, None] >= 0.0, X_f32 >= 0.0, X_f32 < 0.0).astype(np.int64)
171
+ c = (2 * q - 1).astype(np.float64)
172
+ num = np.sum(W_f32 * X_f32 * c, axis=1, dtype=np.float64)
173
+ den = np.sum(W_f32, axis=1, dtype=np.float64)
174
+ valid = den > 1.0e-30
175
+ d = np.where(valid, num / np.where(valid, den, 1.0), 0.0)
176
+ df = from_fp16_bits(to_fp16_bits(d.astype(np.float32)))
177
+ q = np.where(df[:, None] >= 0.0, X_f32 >= 0.0, X_f32 < 0.0).astype(np.float32)
178
+ c_f = 2.0 * q - 1.0
179
+ e = X_f32 - df[:, None] * c_f
180
+ sse = np.sum(W_f32 * e * e, axis=1, dtype=np.float64)
181
+ take = sse < best_seed_sse
182
+ best_seed_sse = np.where(take, sse, best_seed_sse)
183
+ wls_d = np.where(take, df, wls_d)
184
+
185
+ # 2. 24-Candidate Field with Extended Spectral Objective:
186
+ cand_d16 = to_fp16_bits(wls_d[:, None] * MULTS_D[None, :]) # (B, 24)
187
+ cand_d = from_fp16_bits(cand_d16) # (B, 24)
188
+ q_c = np.where(cand_d[:, :, None] >= 0.0, X_f32[:, None, :] >= 0.0, X_f32[:, None, :] < 0.0).astype(np.float32)
189
+ c_c = 2.0 * q_c - 1.0
190
+ e_c = X_f32[:, None, :] - cand_d[:, :, None] * c_c # (B, 24, 128)
191
+ sse_c = np.sum(W_f32[:, None, :] * e_c * e_c, axis=2)
192
+ if cfg.use_spectral and (lam_dc > 0.0 or lam_vw > 0.0):
193
+ v_c = e_c[:, :, :half] + e_c[:, :, half:]
194
+ dc_c = np.sum(e_c, axis=2)
195
+ cand_errors = (sse_c + lam_vw * np.sum(v_c * v_c, axis=2) + lam_dc * dc_c * dc_c).astype(np.float32)
196
+ else:
197
+ cand_errors = sse_c.astype(np.float32)
198
+
199
+ # 3. 6-State Sieve + 6-State Viterbi DP:
200
+ best_cand = _q1_sieve_and_viterbi(cand_errors, cfg)
201
+ rows = np.arange(B)
202
+ d_sel = cand_d[rows, best_cand]
203
+
204
+ # 4. Asymmetric Sign-Flip fp16-ULP Polish (+/-8 ULPs around +d_sel, -d_sel, and d_mean):
205
+ rad = max(int(cfg.ulp_radius), 4)
206
+ deltas = np.arange(-rad, rad + 1, dtype=np.int32)
207
+ pos_b = np.clip(to_fp16_bits(np.abs(d_sel)).astype(np.int32)[:, None] + deltas[None, :], 0, 0x7BFF).astype(np.uint16)
208
+ neg_b = (pos_b | np.uint16(0x8000)).astype(np.uint16)
209
+ ref_b = np.clip(to_fp16_bits(np.abs(d_mean)).astype(np.int32)[:, None] + deltas[None, :], 0, 0x7BFF).astype(np.uint16)
210
+ all_b = np.concatenate([pos_b, neg_b, ref_b], axis=1)
211
+ all_d = from_fp16_bits(all_b)
212
+ q_u = np.where(all_d[:, :, None] >= 0.0, X_f32[:, None, :] >= 0.0, X_f32[:, None, :] < 0.0).astype(np.uint8)
213
+ c_u = 2.0 * q_u.astype(np.float32) - 1.0
214
+ e_u = X_f32[:, None, :] - all_d[:, :, None] * c_u
215
+ sse_u = np.sum(W_f32[:, None, :] * e_u * e_u, axis=2, dtype=np.float64)
216
+ pick_u = np.argmin(sse_u, axis=1)
217
+ best_d16 = all_b[rows, pick_u]
218
+ best_d = all_d[rows, pick_u]
219
+ best_q = q_u[rows, pick_u].astype(np.int64)
220
+
221
+ # 5. Closed-Form 1-D Quadratic Vesica + DC Bit-Flip Projection within sse_budget:
222
+ if cfg.use_spectral and (lam_vw > 0.0 or lam_dc > 0.0):
223
+ c_f = (2 * best_q - 1).astype(np.float64)
224
+ d_f64 = best_d.astype(np.float64)[:, None]
225
+ e = X_f32.astype(np.float64) - d_f64 * c_f
226
+ sse_live = np.sum(W_f32 * e * e, axis=1, dtype=np.float64)
227
+ sse_cap = sse_live * (1.0 + cfg.sse_budget)
228
+ v = e[:, :half] + e[:, half:]
229
+ dc = np.sum(e, axis=1)
230
+ denom = 2.0 * (W_f32 + lam_vw + lam_dc) * (d_f64 * d_f64)
231
+ act_b = np.abs(d_f64) > 1.0e-15
232
+ s_den = np.where(act_b & (denom > 1.0e-30), denom, 1.0)
233
+ v_full = np.concatenate([v, v], axis=1)
234
+ grad = d_f64 * (W_f32 * e + lam_vw * v_full + lam_dc * dc[:, None])
235
+ dq = np.where(act_b, nearest_int((grad / s_den).astype(np.float32)), 0)
236
+ q_new = np.clip(best_q + dq, 0, 1)
237
+ c_new = (2 * q_new - 1).astype(np.float64)
238
+ e_new = X_f32.astype(np.float64) - d_f64 * c_new
239
+ sse_new = np.sum(W_f32 * e_new * e_new, axis=1, dtype=np.float64)
240
+ v_new = e_new[:, :half] + e_new[:, half:]
241
+ dc_new = np.sum(e_new, axis=1)
242
+ spec_old = lam_vw * np.sum(v * v, axis=1) + lam_dc * dc * dc
243
+ spec_new = lam_vw * np.sum(v_new * v_new, axis=1) + lam_dc * dc_new * dc_new
244
+ accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-15)
245
+ best_q = np.where(accept[:, None], q_new, best_q)
246
+
247
+ best_q_u8 = best_q.astype(np.uint8)
248
+ out = np.empty((B, Q1_0_BLOCK_BYTES), dtype=np.uint8)
249
+ out[:, :2] = best_d16.astype("<u2").view(np.uint8).reshape(B, 2)
250
+ out[:, 2:] = _pack_q1_bits_np(best_q_u8)
251
+
252
+ deq_final = best_d[:, None] * (2.0 * best_q_u8.astype(np.float32) - 1.0)
253
+ raw_sse = float(np.sum((X_f32 - deq_final) ** 2, dtype=np.float64))
254
+ return out.tobytes(), raw_sse
255
+
256
+
257
+ def _quantize_q1_0_hpc_cuda(
258
+ X_np: np.ndarray, W_np: np.ndarray, dev: "torch.device", cfg: HPCConfig
259
+ ) -> tuple[bytes, float]:
260
+ """Full HPC V2 Q1_0 pipeline on CUDA GPU for a chunk of (B, 128) blocks."""
261
+ B = X_np.shape[0]
262
+ lam_dc = (float(cfg.dc_lambda) / QK1_0) if cfg.use_spectral else 0.0
263
+ lam_vw = (float(cfg.vw_lambda) / QK1_0) if cfg.use_spectral else 0.0
264
+ half = QK1_0 // 2
265
+
266
+ with torch.no_grad():
267
+ X = torch.from_numpy(X_np).to(dev, dtype=torch.float32)
268
+ W = torch.from_numpy(W_np).to(dev, dtype=torch.float32)
269
+
270
+ # 1. Analog + WLS seed (including negative-scale sign-flip starts):
271
+ d_mean = torch.mean(torch.abs(X), dim=1)
272
+ wsum = torch.clamp(torch.sum(W, dim=1, keepdim=True), min=1.0e-30)
273
+ d_wls_init = (torch.sum(W * torch.abs(X), dim=1, keepdim=True) / wsum).squeeze(1)
274
+ mu = torch.sum(W * X, dim=1, keepdim=True) / wsum
275
+ var = torch.sum(W * (X - mu) ** 2, dim=1, keepdim=True) / wsum
276
+ sig = torch.sqrt(torch.clamp(var, min=1.0e-30))
277
+ x_comp = mu + sig * 1.85 * torch.tanh((X - mu) / (1.85 * sig))
278
+ d_analog = (torch.sum(W * torch.abs(x_comp), dim=1, keepdim=True) / wsum).squeeze(1)
279
+
280
+ wls_d = d_mean.clone()
281
+ best_seed_sse = torch.full((B,), float("inf"), dtype=torch.float32, device=dev)
282
+ for d_start in (d_mean, d_wls_init, d_analog, -d_analog):
283
+ d = d_start.clone()
284
+ for _ in range(2):
285
+ q = torch.where(d[:, None] >= 0.0, X >= 0.0, X < 0.0)
286
+ c = torch.where(q, 1.0, -1.0)
287
+ num = torch.sum(W * X * c, dim=1)
288
+ den = torch.sum(W, dim=1)
289
+ valid = den > 1.0e-30
290
+ d = torch.where(valid, num / torch.where(valid, den, torch.ones_like(den)), torch.zeros_like(den))
291
+ df = d.to(torch.float16).to(torch.float32)
292
+ q = torch.where(df[:, None] >= 0.0, X >= 0.0, X < 0.0)
293
+ c = torch.where(q, 1.0, -1.0)
294
+ e = X - df[:, None] * c
295
+ sse = torch.sum(W * e * e, dim=1)
296
+ take = sse < best_seed_sse
297
+ best_seed_sse = torch.where(take, sse, best_seed_sse)
298
+ wls_d = torch.where(take, df, wls_d)
299
+
300
+ # 2. 24-Candidate Field with Extended Spectral Objective on GPU:
301
+ mults = torch.tensor(MULTS_D, dtype=torch.float32, device=dev)
302
+ cand_d = (wls_d[:, None] * mults[None, :]).to(torch.float16).to(torch.float32) # (B, 24)
303
+ q_c = torch.where(cand_d[:, :, None] >= 0.0, X[:, None, :] >= 0.0, X[:, None, :] < 0.0)
304
+ c_c = torch.where(q_c, 1.0, -1.0)
305
+ e_c = X[:, None, :] - cand_d[:, :, None] * c_c
306
+ sse_c = torch.sum(W[:, None, :] * e_c * e_c, dim=2)
307
+ if cfg.use_spectral and (lam_dc > 0.0 or lam_vw > 0.0):
308
+ v_c = e_c[:, :, :half] + e_c[:, :, half:]
309
+ dc_c = torch.sum(e_c, dim=2)
310
+ cand_err_t = sse_c + lam_vw * torch.sum(v_c * v_c, dim=2) + lam_dc * dc_c * dc_c
311
+ else:
312
+ cand_err_t = sse_c
313
+
314
+ # 3. 6-State Sieve + 6-State Viterbi DP:
315
+ best_cand_np = _q1_sieve_and_viterbi(cand_err_t.cpu().numpy(), cfg)
316
+ best_cand_t = torch.from_numpy(best_cand_np).to(dev)
317
+ rows = torch.arange(B, device=dev)
318
+ d_sel = cand_d[rows, best_cand_t]
319
+
320
+ # 4. Asymmetric Sign-Flip fp16-ULP Polish (+/-8 ULPs around +d_sel, -d_sel, and d_mean):
321
+ rad = max(int(cfg.ulp_radius), 4)
322
+ deltas = torch.arange(-rad, rad + 1, dtype=torch.int32, device=dev)
323
+ abs_bits = torch.abs(d_sel).to(torch.float16).view(torch.int16).to(torch.int32) & 0x7FFF
324
+ ref_bits = torch.abs(d_mean).to(torch.float16).view(torch.int16).to(torch.int32) & 0x7FFF
325
+ pos_b = torch.clamp(abs_bits[:, None] + deltas[None, :], 0, 0x7BFF)
326
+ neg_b = pos_b | 0x8000
327
+ mean_b = torch.clamp(ref_bits[:, None] + deltas[None, :], 0, 0x7BFF)
328
+ all_b = torch.cat([pos_b, neg_b, mean_b], dim=1)
329
+ all_d = all_b.to(torch.int16).view(torch.float16).to(torch.float32)
330
+ q_u = torch.where(all_d[:, :, None] >= 0.0, X[:, None, :] >= 0.0, X[:, None, :] < 0.0).to(torch.uint8)
331
+ c_u = 2.0 * q_u.to(torch.float32) - 1.0
332
+ e_u = X[:, None, :] - all_d[:, :, None] * c_u
333
+ sse_u = torch.sum(W[:, None, :] * e_u * e_u, dim=2)
334
+ pick_u = torch.argmin(sse_u, dim=1)
335
+ best_d16 = all_b[rows, pick_u]
336
+ best_d = all_d[rows, pick_u]
337
+ best_q = q_u[rows, pick_u].to(torch.int64)
338
+
339
+ # 5. Closed-Form 1-D Quadratic Vesica + DC Bit-Flip Projection within sse_budget:
340
+ if cfg.use_spectral and (lam_vw > 0.0 or lam_dc > 0.0):
341
+ d_col = best_d[:, None]
342
+ e = X - d_col * (2.0 * best_q.to(torch.float32) - 1.0)
343
+ sse_live = torch.sum(W * e * e, dim=1)
344
+ sse_cap = sse_live * (1.0 + float(cfg.sse_budget))
345
+ v = e[:, :half] + e[:, half:]
346
+ dc = torch.sum(e, dim=1)
347
+ denom = 2.0 * (W + lam_vw + lam_dc) * (d_col * d_col)
348
+ act_b = torch.abs(d_col) > 1.0e-15
349
+ s_den = torch.where(act_b & (denom > 1.0e-30), denom, torch.ones_like(denom))
350
+ v_full = torch.cat([v, v], dim=1)
351
+ grad = d_col * (W * e + lam_vw * v_full + lam_dc * dc[:, None])
352
+ sh_dq = (grad / s_den) + 12582912.0
353
+ dq = torch.where(
354
+ act_b,
355
+ ((sh_dq.view(torch.int32) & 0x007FFFFF) - 0x00400000).to(torch.int64),
356
+ torch.zeros_like(best_q),
357
+ )
358
+ q_new = torch.clamp(best_q + dq, 0, 1)
359
+ e_new = X - d_col * (2.0 * q_new.to(torch.float32) - 1.0)
360
+ sse_new = torch.sum(W * e_new * e_new, dim=1)
361
+ v_new = e_new[:, :half] + e_new[:, half:]
362
+ dc_new = torch.sum(e_new, dim=1)
363
+ spec_old = lam_vw * torch.sum(v * v, dim=1) + lam_dc * dc * dc
364
+ spec_new = lam_vw * torch.sum(v_new * v_new, dim=1) + lam_dc * dc_new * dc_new
365
+ accept = (sse_new <= sse_cap) & ((sse_new + spec_new) < (sse_live + spec_old) - 1.0e-12)
366
+ best_q = torch.where(accept[:, None], q_new, best_q)
367
+
368
+ best_q_u8 = best_q.to(torch.uint8)
369
+ deq_fin = best_d[:, None] * (2.0 * best_q_u8.to(torch.float32) - 1.0)
370
+ raw_sse = float(torch.sum((X - deq_fin) ** 2, dtype=torch.float64).item())
371
+
372
+ d16_np = best_d16.to(torch.int16).cpu().numpy().view(np.uint16)
373
+ q_np = best_q_u8.cpu().numpy()
374
+ out = np.empty((B, Q1_0_BLOCK_BYTES), dtype=np.uint8)
375
+ out[:, :2] = d16_np.astype("<u2").view(np.uint8).reshape(B, 2)
376
+ out[:, 2:] = _pack_q1_bits_np(q_np)
377
+ return out.tobytes(), raw_sse
378
+
379
+
380
+ def quantize_tensor_q1_0(
381
+ weights: np.ndarray,
382
+ importance: np.ndarray | None = None,
383
+ cfg: HPCConfig | None = None,
384
+ chunk_blocks: int = 65536,
385
+ ) -> tuple[bytes, int, float]:
386
+ """Quantize a tensor to GGML Q1_0 using the full HPC V2 pipeline."""
387
+ cfg = cfg or HPCConfig()
388
+ x = np.asarray(weights, dtype=np.float32).ravel()
389
+ if x.size == 0 or x.size % QK1_0 != 0:
390
+ raise ValueError("Q1_0 requires a non-empty weight count divisible by 128")
391
+ n_blocks = x.size // QK1_0
392
+ X = x.reshape(n_blocks, QK1_0)
393
+
394
+ if importance is not None:
395
+ imp = np.asarray(importance, dtype=np.float32).ravel()
396
+ if imp.size != x.size:
397
+ raise ValueError("importance length must match weight count")
398
+ W_raw = imp.reshape(n_blocks, QK1_0)
399
+ else:
400
+ W_raw = None
401
+
402
+ use_cuda = cfg.device != "cpu" and cuda_is_available() and (
403
+ cfg.device.startswith("cuda") or (cfg.device == "auto" and n_blocks >= 64)
404
+ )
405
+ dev = torch.device("cuda" if cfg.device == "auto" else cfg.device) if use_cuda else None
406
+
407
+ chunks: list[bytes] = []
408
+ total_sse = 0.0
409
+ for start in range(0, n_blocks, chunk_blocks):
410
+ stop = min(start + chunk_blocks, n_blocks)
411
+ Xb = X[start:stop]
412
+ sig2 = np.mean(Xb * Xb, axis=1, keepdims=True)
413
+ if W_raw is None:
414
+ Wb = np.sqrt(sig2 + Xb * Xb)
415
+ else:
416
+ wr = W_raw[start:stop]
417
+ Wb = np.sqrt(np.maximum(wr, 1.0e-6)) * np.sqrt(sig2 + Xb * Xb) if cfg.temper_imatrix else wr
418
+ wm = np.mean(Wb, axis=1, keepdims=True)
419
+ Wb = np.where(wm > 1.0e-30, Wb / np.maximum(wm, 1.0e-30), 1.0).astype(np.float32)
420
+
421
+ if use_cuda and dev is not None:
422
+ raw_b, sse_b = _quantize_q1_0_hpc_cuda(Xb, Wb, dev, cfg)
423
+ else:
424
+ raw_b, sse_b = _quantize_q1_0_hpc_cpu(Xb, Wb, cfg)
425
+ chunks.append(raw_b)
426
+ total_sse += sse_b
427
+
428
+ rmse = float(np.sqrt(total_sse / x.size))
429
+ return b"".join(chunks), n_blocks, rmse