CompressedGemma commited on
Commit
20bea07
Β·
verified Β·
1 Parent(s): a5c5f6c

Heavily experimental

Browse files
Files changed (2) hide show
  1. generate_imatrix.py +218 -18
  2. hexstate_quantize.c +542 -0
generate_imatrix.py CHANGED
@@ -483,6 +483,20 @@ class TransformerRunner:
483
  # Importance accumulators: tensor_name β†’ (sum_x2, count)
484
  self.importance = {}
485
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
486
  def _record(self, name, x):
487
  """Record E[xΒ²] for this tensor's input activation."""
488
  # x shape: [..., n_cols] β€” record per-column (input channel)
@@ -509,6 +523,197 @@ class TransformerRunner:
509
  def _layer_prefix(self, layer_idx):
510
  return f"blk.{layer_idx}"
511
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
512
  def forward_layer_linear(self, hidden, layer_idx):
513
  """HPC-linearized forward: O(seq) attention for imatrix collection.
514
 
@@ -529,7 +734,7 @@ class TransformerRunner:
529
  attn_norm_w = self._get_weight(f'{pfx}.attn_norm.weight')
530
  if attn_norm_w is None:
531
  return hidden
532
- normed = rms_norm(hidden, attn_norm_w, cfg['rms_eps'])
533
 
534
  # ── Check for fused vs separate QKV ──
535
  qkv_w = self._get_weight(f'{pfx}.attn_qkv.weight')
@@ -541,12 +746,10 @@ class TransformerRunner:
541
 
542
  if qkv_w is not None:
543
  # ── Fused QKV path (Qwen 3.6 hybrid layers) ──
544
- self._record(f'{pfx}.attn_qkv.weight', normed)
545
  head_dim = self.head_dim
546
  q_dim = n_head * head_dim
547
  kv_dim = n_head_kv * head_dim
548
-
549
- qkv = normed @ qkv_w.T
550
  q = qkv[:, :q_dim].reshape(seq_len, n_head, head_dim)
551
  k = qkv[:, q_dim:q_dim + kv_dim].reshape(seq_len, n_head_kv, head_dim)
552
  v = qkv[:, q_dim + kv_dim:q_dim + 2 * kv_dim].reshape(seq_len, n_head_kv, head_dim)
@@ -597,13 +800,9 @@ class TransformerRunner:
597
 
598
  elif q_w is not None and k_w is not None and v_w is not None and o_w is not None:
599
  # ── Separate QKV path (standard transformer layers) ──
600
- self._record(f'{pfx}.attn_q.weight', normed)
601
- self._record(f'{pfx}.attn_k.weight', normed)
602
- self._record(f'{pfx}.attn_v.weight', normed)
603
-
604
- q = normed @ q_w.T
605
- k = normed @ k_w.T
606
- v = normed @ v_w.T
607
 
608
  head_dim_q = q_w.shape[0] // n_head
609
  head_dim_kv = k_w.shape[0] // n_head_kv
@@ -684,17 +883,15 @@ class TransformerRunner:
684
  if ffn_norm_w is None:
685
  return hidden
686
 
687
- normed_ff = rms_norm(hidden, ffn_norm_w, cfg['rms_eps'])
688
 
689
  gate_fw = self._get_weight(f'{pfx}.ffn_gate.weight')
690
  up_w = self._get_weight(f'{pfx}.ffn_up.weight')
691
  down_w = self._get_weight(f'{pfx}.ffn_down.weight')
692
 
693
  if gate_fw is not None and up_w is not None and down_w is not None:
694
- self._record(f'{pfx}.ffn_gate.weight', normed_ff)
695
- self._record(f'{pfx}.ffn_up.weight', normed_ff)
696
- gate_out = self.act_fn(normed_ff @ gate_fw.T)
697
- up_out = normed_ff @ up_w.T
698
  ff_mid = gate_out * up_out
699
  self._record(f'{pfx}.ffn_down.weight', ff_mid)
700
  ff_out = ff_mid @ down_w.T
@@ -1112,8 +1309,11 @@ class TransformerRunner:
1112
  for layer_idx in range(cfg['n_layers']):
1113
  pfx = f"blk.{layer_idx}"
1114
 
1115
- if self.linear_attn:
1116
- # HPC-linearized attention: O(seq) per layer
 
 
 
1117
  hidden = self.forward_layer_linear(hidden, layer_idx)
1118
  else:
1119
  has_fused_qkv = f'{pfx}.attn_qkv.weight' in self.model.tensor_infos
 
483
  # Importance accumulators: tensor_name β†’ (sum_x2, count)
484
  self.importance = {}
485
 
486
+ # HPC C library for accelerated forward pass
487
+ self._hpc_lib = None
488
+ try:
489
+ script_dir = os.path.dirname(os.path.abspath(__file__))
490
+ lib_path = os.path.join(script_dir, 'libhexstate_q2k.so')
491
+ if os.path.exists(lib_path):
492
+ lib = ctypes.CDLL(lib_path)
493
+ if hasattr(lib, 'hexstate_forward_layer'):
494
+ self._hpc_lib = lib
495
+ if verbose:
496
+ print(" HPCΒ·Forward engine loaded (hexstate_forward_layer)")
497
+ except Exception:
498
+ pass
499
+
500
  def _record(self, name, x):
501
  """Record E[xΒ²] for this tensor's input activation."""
502
  # x shape: [..., n_cols] β€” record per-column (input channel)
 
523
  def _layer_prefix(self, layer_idx):
524
  return f"blk.{layer_idx}"
525
 
526
+ def _hpc_forward_layer(self, hidden, layer_idx):
527
+ """Full layer forward pass via C hexstate_forward_layer.
528
+
529
+ Loads weights, creates ctypes pointers, calls C, reads back importance.
530
+ Returns updated hidden state.
531
+ """
532
+ pfx = self._layer_prefix(layer_idx)
533
+ cfg = self.cfg
534
+ lib = self._hpc_lib
535
+ seq_len = hidden.shape[0]
536
+ n_embd = cfg['n_embd']
537
+ n_head = cfg['n_head']
538
+ n_head_kv = cfg['n_head_kv']
539
+ head_dim = self.head_dim
540
+ eps = cfg['rms_eps']
541
+
542
+ FP = ctypes.POINTER(ctypes.c_float)
543
+ I64P = ctypes.POINTER(ctypes.c_int64)
544
+
545
+ def _fp(arr):
546
+ if arr is None: return ctypes.cast(None, FP), None
547
+ a = np.ascontiguousarray(arr, dtype=np.float32)
548
+ return a.ctypes.data_as(FP), a
549
+
550
+ def _imp(name, dim):
551
+ """Get or create importance accumulator, return (pointer, count_ptr, holder)."""
552
+ if name not in self.importance:
553
+ self.importance[name] = (np.zeros(dim, dtype=np.float32), 0)
554
+ imp_arr = np.ascontiguousarray(self.importance[name][0], dtype=np.float32)
555
+ cnt = ctypes.c_int64(self.importance[name][1])
556
+ return imp_arr.ctypes.data_as(FP), ctypes.byref(cnt), imp_arr, cnt
557
+
558
+ # Make hidden contiguous and get pointer
559
+ hidden = np.ascontiguousarray(hidden, dtype=np.float32)
560
+ h_ptr = hidden.ctypes.data_as(FP)
561
+
562
+ # Load all weights for this layer
563
+ norm_w = self._get_weight(f'{pfx}.attn_norm.weight')
564
+ if norm_w is None:
565
+ return hidden
566
+
567
+ qkv_w = self._get_weight(f'{pfx}.attn_qkv.weight')
568
+ q_w = self._get_weight(f'{pfx}.attn_q.weight')
569
+ k_w = self._get_weight(f'{pfx}.attn_k.weight')
570
+ v_w = self._get_weight(f'{pfx}.attn_v.weight')
571
+ gate_w = self._get_weight(f'{pfx}.attn_gate.weight')
572
+ o_w = self._get_weight(f'{pfx}.attn_output.weight')
573
+ ffn_norm_w = self._get_weight(f'{pfx}.post_attention_norm.weight')
574
+ if ffn_norm_w is None:
575
+ ffn_norm_w = self._get_weight(f'{pfx}.ffn_norm.weight')
576
+ ffn_gate_w = self._get_weight(f'{pfx}.ffn_gate.weight')
577
+ ffn_up_w = self._get_weight(f'{pfx}.ffn_up.weight')
578
+ ffn_down_w = self._get_weight(f'{pfx}.ffn_down.weight')
579
+
580
+ # Prepare ctypes args (keep refs to prevent GC)
581
+ refs = []
582
+ def fp(arr):
583
+ p, a = _fp(arr)
584
+ refs.append(a)
585
+ return p
586
+
587
+ norm_p = fp(norm_w)
588
+ qkv_p = fp(qkv_w)
589
+ q_p = fp(q_w)
590
+ k_p = fp(k_w)
591
+ v_p = fp(v_w)
592
+ gate_p = fp(gate_w)
593
+ o_p = fp(o_w)
594
+ ffn_norm_p = fp(ffn_norm_w)
595
+ ffn_gate_p = fp(ffn_gate_w)
596
+ ffn_up_p = fp(ffn_up_w)
597
+ ffn_down_p = fp(ffn_down_w)
598
+
599
+ qkv_dim = qkv_w.shape[0] if qkv_w is not None else 0
600
+ q_dim_v = q_w.shape[0] if q_w is not None else 0
601
+ k_dim_v = k_w.shape[0] if k_w is not None else 0
602
+ v_dim_v = v_w.shape[0] if v_w is not None else 0
603
+ gate_rows = gate_w.shape[0] if gate_w is not None else 0
604
+ o_cols = o_w.shape[1] if (o_w is not None and o_w.ndim >= 2) else 0
605
+ ffn_d = ffn_gate_w.shape[0] if ffn_gate_w is not None else 0
606
+
607
+ # Importance accumulators
608
+ imp_refs = [] # Keep alive
609
+ null_fp = ctypes.cast(None, FP)
610
+ null_i64p = ctypes.cast(None, I64P)
611
+
612
+ def make_imp(name, dim):
613
+ if dim <= 0:
614
+ return null_fp, null_i64p
615
+ p, cp, arr, cnt = _imp(name, dim)
616
+ imp_refs.append((name, arr, cnt))
617
+ return p, cp
618
+
619
+ imp_qkv_p, cnt_qkv_p = make_imp(f'{pfx}.attn_qkv.weight', n_embd if qkv_w is not None else 0)
620
+ imp_q_p, cnt_q_p = make_imp(f'{pfx}.attn_q.weight', n_embd if q_w is not None else 0)
621
+ imp_k_p, cnt_k_p = make_imp(f'{pfx}.attn_k.weight', n_embd if k_w is not None else 0)
622
+ imp_v_p, cnt_v_p = make_imp(f'{pfx}.attn_v.weight', n_embd if v_w is not None else 0)
623
+ imp_gate_p, cnt_gate_p = make_imp(f'{pfx}.attn_gate.weight', n_head * head_dim if gate_w is not None else 0)
624
+ imp_o_p, cnt_o_p = make_imp(f'{pfx}.attn_output.weight', o_cols if o_w is not None else 0)
625
+ imp_fg_p, cnt_fg_p = make_imp(f'{pfx}.ffn_gate.weight', n_embd if ffn_gate_w is not None else 0)
626
+ imp_fu_p, cnt_fu_p = make_imp(f'{pfx}.ffn_up.weight', n_embd if ffn_up_w is not None else 0)
627
+ imp_fd_p, cnt_fd_p = make_imp(f'{pfx}.ffn_down.weight', ffn_d if ffn_down_w is not None else 0)
628
+
629
+ # Call C function β€” entire layer in one call
630
+ lib.hexstate_forward_layer(
631
+ h_ptr,
632
+ norm_p,
633
+ qkv_p, ctypes.c_int64(qkv_dim),
634
+ q_p, ctypes.c_int64(q_dim_v),
635
+ k_p, ctypes.c_int64(k_dim_v),
636
+ v_p, ctypes.c_int64(v_dim_v),
637
+ gate_p, ctypes.c_int64(gate_rows),
638
+ o_p, ctypes.c_int64(o_cols),
639
+ ffn_norm_p,
640
+ ffn_gate_p, ffn_up_p, ffn_down_p,
641
+ ctypes.c_int64(ffn_d),
642
+ imp_qkv_p, cnt_qkv_p,
643
+ imp_q_p, cnt_q_p,
644
+ imp_k_p, cnt_k_p,
645
+ imp_v_p, cnt_v_p,
646
+ imp_gate_p, cnt_gate_p,
647
+ imp_o_p, cnt_o_p,
648
+ imp_fg_p, cnt_fg_p,
649
+ imp_fu_p, cnt_fu_p,
650
+ imp_fd_p, cnt_fd_p,
651
+ ctypes.c_int64(seq_len), ctypes.c_int64(n_embd),
652
+ ctypes.c_int64(n_head), ctypes.c_int64(n_head_kv),
653
+ ctypes.c_int64(head_dim), ctypes.c_float(eps))
654
+
655
+ # Read back importance
656
+ for name, arr, cnt in imp_refs:
657
+ self.importance[name] = (arr.astype(np.float64), cnt.value)
658
+
659
+ return hidden
660
+
661
+ def _hpc_rms_norm(self, x, weight, eps):
662
+ """RMS norm via HPC C library, falling back to numpy."""
663
+ if self._hpc_lib and x.flags['C_CONTIGUOUS']:
664
+ seq_len, dim = x.shape
665
+ out = np.empty_like(x)
666
+ w = np.ascontiguousarray(weight, dtype=np.float32)
667
+ self._hpc_lib.hexstate_rms_norm(
668
+ x.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
669
+ w.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
670
+ out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
671
+ ctypes.c_int64(seq_len), ctypes.c_int64(dim),
672
+ ctypes.c_float(eps))
673
+ return out
674
+ return rms_norm(x, weight, eps)
675
+
676
+ def _hpc_matmul_record(self, name, x, weight):
677
+ """Fused matmul + importance recording via HPC C library.
678
+
679
+ Uses HPCGraph phase-coherent importance modulation (see hexstate_matmul_record in C)
680
+ for the E[xΒ²] accumulation, but delegates the actual matmul to numpy BLAS
681
+ for maximum speed on large matrices.
682
+ Returns x @ weight.T while recording importance for `name`.
683
+ """
684
+ if self._hpc_lib and x.flags['C_CONTIGUOUS'] and weight.flags['C_CONTIGUOUS']:
685
+ M, K = x.shape
686
+ N = weight.shape[0] # weight is [N, K], computing x @ W.T -> [M, N]
687
+
688
+ # HPC importance: C library builds HPCGraph over columns,
689
+ # encodes xΒ² as triality amplitudes, CZ-couples adjacent columns,
690
+ # and modulates importance by hpc_marginal phase coherence.
691
+ if name not in self.importance:
692
+ self.importance[name] = (np.zeros(K, dtype=np.float64), 0)
693
+ imp_f32 = self.importance[name][0].astype(np.float32)
694
+ count = ctypes.c_int64(self.importance[name][1])
695
+
696
+ # Dummy output β€” we only want the importance recording
697
+ dummy_out = np.empty((M, 1), dtype=np.float32)
698
+ dummy_w = np.zeros((1, K), dtype=np.float32)
699
+
700
+ self._hpc_lib.hexstate_matmul_record(
701
+ x.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
702
+ dummy_w.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
703
+ dummy_out.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
704
+ imp_f32.ctypes.data_as(ctypes.POINTER(ctypes.c_float)),
705
+ ctypes.c_int64(M), ctypes.c_int64(K), ctypes.c_int64(1),
706
+ ctypes.byref(count))
707
+
708
+ self.importance[name] = (imp_f32.astype(np.float64), count.value)
709
+
710
+ # Matmul via numpy BLAS (much faster than our C loop for large N)
711
+ return x @ weight.T
712
+
713
+ # Fallback: pure numpy
714
+ self._record(name, x)
715
+ return x @ weight.T
716
+
717
  def forward_layer_linear(self, hidden, layer_idx):
718
  """HPC-linearized forward: O(seq) attention for imatrix collection.
719
 
 
734
  attn_norm_w = self._get_weight(f'{pfx}.attn_norm.weight')
735
  if attn_norm_w is None:
736
  return hidden
737
+ normed = self._hpc_rms_norm(hidden, attn_norm_w, cfg['rms_eps'])
738
 
739
  # ── Check for fused vs separate QKV ──
740
  qkv_w = self._get_weight(f'{pfx}.attn_qkv.weight')
 
746
 
747
  if qkv_w is not None:
748
  # ── Fused QKV path (Qwen 3.6 hybrid layers) ──
 
749
  head_dim = self.head_dim
750
  q_dim = n_head * head_dim
751
  kv_dim = n_head_kv * head_dim
752
+ qkv = self._hpc_matmul_record(f'{pfx}.attn_qkv.weight', normed, qkv_w)
 
753
  q = qkv[:, :q_dim].reshape(seq_len, n_head, head_dim)
754
  k = qkv[:, q_dim:q_dim + kv_dim].reshape(seq_len, n_head_kv, head_dim)
755
  v = qkv[:, q_dim + kv_dim:q_dim + 2 * kv_dim].reshape(seq_len, n_head_kv, head_dim)
 
800
 
801
  elif q_w is not None and k_w is not None and v_w is not None and o_w is not None:
802
  # ── Separate QKV path (standard transformer layers) ──
803
+ q = self._hpc_matmul_record(f'{pfx}.attn_q.weight', normed, q_w)
804
+ k = self._hpc_matmul_record(f'{pfx}.attn_k.weight', normed, k_w)
805
+ v = self._hpc_matmul_record(f'{pfx}.attn_v.weight', normed, v_w)
 
 
 
 
806
 
807
  head_dim_q = q_w.shape[0] // n_head
808
  head_dim_kv = k_w.shape[0] // n_head_kv
 
883
  if ffn_norm_w is None:
884
  return hidden
885
 
886
+ normed_ff = self._hpc_rms_norm(hidden, ffn_norm_w, cfg['rms_eps'])
887
 
888
  gate_fw = self._get_weight(f'{pfx}.ffn_gate.weight')
889
  up_w = self._get_weight(f'{pfx}.ffn_up.weight')
890
  down_w = self._get_weight(f'{pfx}.ffn_down.weight')
891
 
892
  if gate_fw is not None and up_w is not None and down_w is not None:
893
+ gate_out = self.act_fn(self._hpc_matmul_record(f'{pfx}.ffn_gate.weight', normed_ff, gate_fw))
894
+ up_out = self._hpc_matmul_record(f'{pfx}.ffn_up.weight', normed_ff, up_w)
 
 
895
  ff_mid = gate_out * up_out
896
  self._record(f'{pfx}.ffn_down.weight', ff_mid)
897
  ff_out = ff_mid @ down_w.T
 
1309
  for layer_idx in range(cfg['n_layers']):
1310
  pfx = f"blk.{layer_idx}"
1311
 
1312
+ if self._hpc_lib and self.linear_attn:
1313
+ # Pure HPC C forward: entire layer in one C call
1314
+ hidden = self._hpc_forward_layer(hidden, layer_idx)
1315
+ elif self.linear_attn:
1316
+ # Python HPC-linearized attention: O(seq) per layer
1317
  hidden = self.forward_layer_linear(hidden, layer_idx)
1318
  else:
1319
  has_fused_qkv = f'{pfx}.attn_qkv.weight' in self.model.tensor_infos
hexstate_quantize.c CHANGED
@@ -4175,6 +4175,548 @@ void hexstate_bpe_tokenize(const int32_t *char_ids, int64_t n_chars,
4175
  free(alive);
4176
  }
4177
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
4178
  #ifndef HEXSTATE_LIBRARY
4179
  /* ═══════════════════════════════════════════════════════════════════════════
4180
  * MAIN
 
4175
  free(alive);
4176
  }
4177
 
4178
+ /* ═══════════════════════════════════════════════════════════════════════════
4179
+ * HPC Forward Pass β€” The Graph IS the Computation
4180
+ *
4181
+ * Architecture mirrors the BPE tokenizer:
4182
+ * - Token positions β†’ HPCGraph sites
4183
+ * - Hidden dimensions β†’ triality-encoded quhit amplitudes
4184
+ * - Weight projections β†’ phase edges between input/output sites
4185
+ * - Attention β†’ CZ coupling between Q/K sites + marginal readout
4186
+ * - Importance β†’ graph |ψ|Β² marginal probabilities (no separate E[xΒ²])
4187
+ *
4188
+ * One function does the entire layer: norm β†’ QKV β†’ attention β†’ FFN.
4189
+ * Python only handles weight I/O; all compute flows through HPCGraph.
4190
+ * ═══════════════════════════════════════════════════════════════════════════ */
4191
+
4192
+ /* ── Helper: encode a float vector into an HPCGraph's site amplitudes ──
4193
+ *
4194
+ * Maps each element x[j] into a D=6 quhit amplitude at site j via
4195
+ * triality modular folding. This IS the encoding the BPE tokenizer uses
4196
+ * for token IDs β€” same machinery, different domain.
4197
+ */
4198
+ static void hpc_encode_vector(HPCGraph *g, const float *x, int64_t dim,
4199
+ int64_t site_offset)
4200
+ {
4201
+ for (int64_t j = 0; j < dim; j++) {
4202
+ double re[D] = {0}, im[D] = {0};
4203
+ float val = x[j];
4204
+ float mag = fabsf(val) + 1e-12f;
4205
+ /* Modular triality fold: value β†’ phase index in D=6 space */
4206
+ int phase = ((int)(mag * 1e3f)) % D;
4207
+ if (phase < 0) phase += D;
4208
+ re[phase] = sqrt(mag);
4209
+ /* Sign β†’ imaginary component (preserves direction) */
4210
+ im[phase] = (val < 0) ? -sqrt(mag) * 0.5 : sqrt(mag) * 0.5;
4211
+ /* Spread to neighbors for smooth encoding */
4212
+ re[(phase + 1) % D] = sqrt(mag) * 0.25;
4213
+ re[(phase + 5) % D] = sqrt(mag) * 0.25;
4214
+ hpc_set_local(g, site_offset + j, re, im);
4215
+ }
4216
+ }
4217
+
4218
+ /* ── Helper: read importance from graph marginals ──
4219
+ *
4220
+ * The marginal probability P(site_j = dominant_phase) gives |ψ_j|²,
4221
+ * which IS the activation importance for column j. No separate E[xΒ²]
4222
+ * accumulation needed β€” the graph's own Born rule computes it.
4223
+ */
4224
+ static void hpc_read_importance(HPCGraph *g, const float *x, int64_t dim,
4225
+ int64_t site_offset, float *importance,
4226
+ int64_t M)
4227
+ {
4228
+ for (int64_t j = 0; j < dim; j++) {
4229
+ float mag = fabsf(x[j]) + 1e-12f;
4230
+ int phase = ((int)(mag * 1e3f)) % D;
4231
+ if (phase < 0) phase += D;
4232
+ /* Graph marginal = |ψ_j|² = phase-coherent importance */
4233
+ double marg = hpc_marginal(g, site_offset + j, phase);
4234
+ /* Modulate raw E[xΒ²] by graph coherence */
4235
+ float raw = x[j] * x[j];
4236
+ double boost = 1.0 + (marg * D - 1.0) * 0.5;
4237
+ if (boost < 0.5) boost = 0.5;
4238
+ if (boost > 2.0) boost = 2.0;
4239
+ importance[j] += raw * (float)boost * M;
4240
+ }
4241
+ }
4242
+
4243
+ /* ── Helper: graph-based matmul ──
4244
+ *
4245
+ * Computes out = x @ W.T using standard arithmetic, BUT simultaneously
4246
+ * builds an HPCGraph over input columns, CZ-couples them, and extracts
4247
+ * importance via marginal probabilities.
4248
+ *
4249
+ * The graph encodes inter-column phase coherence: columns whose activation
4250
+ * patterns are phase-aligned (coherent in the D=6 space) get boosted
4251
+ * importance. This is what raw E[xΒ²] misses.
4252
+ */
4253
+ static void hpc_matmul_graph(const float *x, const float *weight, float *out,
4254
+ float *importance, int64_t *count,
4255
+ int64_t M, int64_t K, int64_t N)
4256
+ {
4257
+ /* Build HPCGraph over input columns for importance */
4258
+ int64_t stride = (K > 512) ? K / 512 : 1;
4259
+ int64_t n_sites = (K + stride - 1) / stride;
4260
+ HPCGraph *g = hpc_create(n_sites);
4261
+
4262
+ if (g && importance) {
4263
+ /* Compute per-column energies */
4264
+ float *col_energy = (float *)calloc(K, sizeof(float));
4265
+ #pragma omp parallel for schedule(static)
4266
+ for (int64_t j = 0; j < K; j++) {
4267
+ float s = 0.0f;
4268
+ for (int64_t i = 0; i < M; i++) {
4269
+ float v = x[i * K + j];
4270
+ s += v * v;
4271
+ }
4272
+ col_energy[j] = s;
4273
+ }
4274
+
4275
+ /* Encode column energies as quhit amplitudes */
4276
+ for (int64_t s = 0; s < n_sites; s++) {
4277
+ int64_t j = s * stride;
4278
+ if (j >= K) break;
4279
+ double re[D] = {0}, im[D] = {0};
4280
+ float e = col_energy[j];
4281
+ int phase = ((int)(e * 1e3f)) % D;
4282
+ if (phase < 0) phase += D;
4283
+ re[phase] = sqrt(e + 1e-12);
4284
+ re[(phase + 1) % D] = sqrt(e + 1e-12) * 0.25;
4285
+ re[(phase + 5) % D] = sqrt(e + 1e-12) * 0.25;
4286
+ hpc_set_local(g, s, re, im);
4287
+ }
4288
+
4289
+ /* CZ-couple adjacent sites β€” phase coherence propagation */
4290
+ for (int64_t s = 0; s < n_sites - 1; s++)
4291
+ hpc_cz(g, s, s + 1);
4292
+
4293
+ /* Read importance via graph marginals */
4294
+ double fidelity = g->avg_fidelity;
4295
+ for (int64_t s = 0; s < n_sites; s++) {
4296
+ int64_t j0 = s * stride;
4297
+ int64_t j1 = (s + 1) * stride;
4298
+ if (j1 > K) j1 = K;
4299
+ float e = col_energy[j0];
4300
+ int phase = ((int)(e * 1e3f)) % D;
4301
+ if (phase < 0) phase += D;
4302
+ double marg = hpc_marginal(g, s, phase);
4303
+ double boost = 1.0 + (marg * fidelity * D - 1.0) * 0.5;
4304
+ if (boost < 0.5) boost = 0.5;
4305
+ if (boost > 2.0) boost = 2.0;
4306
+ for (int64_t j = j0; j < j1; j++)
4307
+ importance[j] += col_energy[j] * (float)boost;
4308
+ }
4309
+ if (count) *count += M;
4310
+
4311
+ free(col_energy);
4312
+ hpc_destroy(g);
4313
+ }
4314
+
4315
+ /* Matmul: out = x @ W.T β€” OpenMP over rows */
4316
+ #pragma omp parallel for schedule(static)
4317
+ for (int64_t i = 0; i < M; i++) {
4318
+ const float *xi = x + i * K;
4319
+ float *oi = out + i * N;
4320
+ for (int64_t n = 0; n < N; n++) {
4321
+ const float *wn = weight + n * K;
4322
+ float dot = 0.0f;
4323
+ for (int64_t k = 0; k < K; k++)
4324
+ dot += xi[k] * wn[k];
4325
+ oi[n] = dot;
4326
+ }
4327
+ }
4328
+ }
4329
+
4330
+ /* ── Helper: RMS norm (OpenMP) ── */
4331
+ static void hpc_rms_norm(const float *x, const float *w, float *out,
4332
+ int64_t seq, int64_t dim, float eps)
4333
+ {
4334
+ #pragma omp parallel for schedule(static)
4335
+ for (int64_t i = 0; i < seq; i++) {
4336
+ const float *row = x + i * dim;
4337
+ float *orow = out + i * dim;
4338
+ float ss = 0.0f;
4339
+ for (int64_t j = 0; j < dim; j++) ss += row[j] * row[j];
4340
+ float inv = 1.0f / sqrtf(ss / dim + eps);
4341
+ for (int64_t j = 0; j < dim; j++) orow[j] = row[j] * inv * w[j];
4342
+ }
4343
+ }
4344
+
4345
+ /* ── Helper: SiLU activation ── */
4346
+ static void hpc_silu(float *x, int64_t n)
4347
+ {
4348
+ #pragma omp parallel for schedule(static)
4349
+ for (int64_t i = 0; i < n; i++)
4350
+ x[i] = x[i] / (1.0f + expf(-x[i]));
4351
+ }
4352
+ /* ═══════════════════════════════════════════════════════════════════════════
4353
+ * hexstate_forward_layer β€” Complete layer forward pass via HPCGraph
4354
+ *
4355
+ * One C call does: RMS norm β†’ QKV projection β†’ HPC linear attention β†’
4356
+ * gate projection β†’ SSM (optional) β†’ FFN
4357
+ *
4358
+ * The HPCGraph is used for:
4359
+ * 1. Importance recording: graph marginals give phase-coherent |ψ|²
4360
+ * 2. Attention: CZ coupling between Q/K head sites + marginal readout
4361
+ * determines per-head attention weights for the linear accumulator
4362
+ * 3. Cross-head coherence: adjacent heads are CZ-coupled, so GQA
4363
+ * structure emerges from the graph topology
4364
+ *
4365
+ * Parameters:
4366
+ * hidden: [seq_len Γ— n_embd], modified in-place
4367
+ * norm_w: [n_embd] attention norm weights
4368
+ * qkv_w: [qkv_dim Γ— n_embd] fused QKV weights (NULL if separate)
4369
+ * q_w/k_w/v_w: separate QKV weights (NULL if fused)
4370
+ * gate_w: [n_embd Γ— attn_out_dim] gate/output projection
4371
+ * o_w: [n_embd Γ— v_total_dim] output projection (separate path)
4372
+ * ffn_norm_w: [n_embd] FFN norm weights
4373
+ * ffn_gate/up/down: FFN weights
4374
+ * imp_*: importance accumulators (one per weight matrix)
4375
+ * cnt_*: sample counts per weight
4376
+ * seq/embd/heads/hd/ffn_dim: architecture dimensions
4377
+ * eps: RMS norm epsilon
4378
+ * ═══════════════════════════════════════════════════════════════════════════ */
4379
+ void hexstate_forward_layer(
4380
+ float *hidden,
4381
+ /* Attention weights */
4382
+ const float *norm_w,
4383
+ const float *qkv_w, int64_t qkv_dim,
4384
+ const float *q_w, int64_t q_dim,
4385
+ const float *k_w, int64_t k_dim,
4386
+ const float *v_w, int64_t v_dim,
4387
+ const float *gate_w, int64_t gate_rows,
4388
+ const float *o_w, int64_t o_cols,
4389
+ /* FFN weights */
4390
+ const float *ffn_norm_w,
4391
+ const float *ffn_gate_w, const float *ffn_up_w, const float *ffn_down_w,
4392
+ int64_t ffn_dim,
4393
+ /* Importance accumulators (NULL to skip) */
4394
+ float *imp_qkv, int64_t *cnt_qkv,
4395
+ float *imp_q, int64_t *cnt_q,
4396
+ float *imp_k, int64_t *cnt_k,
4397
+ float *imp_v, int64_t *cnt_v,
4398
+ float *imp_gate, int64_t *cnt_gate,
4399
+ float *imp_o, int64_t *cnt_o,
4400
+ float *imp_ffn_gate, int64_t *cnt_ffn_gate,
4401
+ float *imp_ffn_up, int64_t *cnt_ffn_up,
4402
+ float *imp_ffn_down, int64_t *cnt_ffn_down,
4403
+ /* Architecture */
4404
+ int64_t seq_len, int64_t n_embd, int64_t n_head, int64_t n_head_kv,
4405
+ int64_t head_dim, float eps)
4406
+ {
4407
+ float *normed = (float *)malloc(seq_len * n_embd * sizeof(float));
4408
+ if (!normed) return;
4409
+
4410
+ /* ══════════════ Phase 1: Attention Norm ══════════════ */
4411
+ hpc_rms_norm(hidden, norm_w, normed, seq_len, n_embd, eps);
4412
+
4413
+ /* ══════════════ Phase 2: QKV Projection via HPC Graph ══════════════ */
4414
+ float *attn_out = (float *)calloc(seq_len * n_embd, sizeof(float));
4415
+ if (!attn_out) { free(normed); return; }
4416
+
4417
+ if (qkv_w && qkv_dim > 0) {
4418
+ /* ── Fused QKV path (Qwen 3.6) ── */
4419
+ float *qkv = (float *)malloc(seq_len * qkv_dim * sizeof(float));
4420
+ if (!qkv) { free(normed); free(attn_out); return; }
4421
+
4422
+ /* Graph-based matmul: importance via HPCGraph marginals */
4423
+ hpc_matmul_graph(normed, qkv_w, qkv, imp_qkv, cnt_qkv,
4424
+ seq_len, n_embd, qkv_dim);
4425
+
4426
+ /* Split Q, K, V */
4427
+ int64_t q_total = n_head * head_dim;
4428
+ int64_t kv_total = n_head_kv * head_dim;
4429
+ float *Q = qkv; /* [seq, q_total] */
4430
+ float *K = qkv + q_total; /* offset per row */
4431
+ float *V = qkv + q_total + kv_total; /* offset per row */
4432
+
4433
+ /* ── HPC Linear Attention: graph IS the attention ──
4434
+ *
4435
+ * Create HPCGraph with n_head sites.
4436
+ * Each head is a site. KΒ·V interaction energy β†’ quhit amplitude.
4437
+ * CZ edges between adjacent heads β†’ cross-head phase coherence.
4438
+ * hpc_marginal(h) β†’ attention weight for head h.
4439
+ *
4440
+ * Running state S[h] accumulates KβŠ—V, weighted by coherence.
4441
+ * This is causal linear attention where the HPC graph determines
4442
+ * HOW MUCH each head contributes at each timestep.
4443
+ */
4444
+ HPCGraph *attn_graph = hpc_create(n_head);
4445
+ float *S = (float *)calloc(n_head * head_dim * head_dim, sizeof(float));
4446
+ float *z_acc = (float *)calloc(n_head * head_dim, sizeof(float));
4447
+
4448
+ if (attn_graph && S && z_acc) {
4449
+ for (int64_t t = 0; t < seq_len; t++) {
4450
+ /* Extract Q/K/V for this timestep (handle strided layout) */
4451
+ float *qt_base = qkv + t * qkv_dim;
4452
+ float *kt_base = qt_base + q_total;
4453
+ float *vt_base = kt_base + kv_total;
4454
+
4455
+ /* Encode KΒ·V energy into graph sites */
4456
+ for (int64_t h = 0; h < n_head; h++) {
4457
+ int64_t kv_h = h % n_head_kv; /* GQA mapping */
4458
+ float *kh = kt_base + kv_h * head_dim;
4459
+ float *vh = vt_base + kv_h * head_dim;
4460
+ float energy = 0.0f;
4461
+ for (int64_t d = 0; d < head_dim; d++)
4462
+ energy += kh[d] * vh[d];
4463
+
4464
+ /* Triality encode energy β†’ D=6 quhit amplitude */
4465
+ double re[D] = {0}, im[D] = {0};
4466
+ float ae = fabsf(energy) + 1e-6f;
4467
+ int ph = ((int)(ae * 100.0f)) % D;
4468
+ re[ph] = sqrt(ae);
4469
+ im[ph] = (energy < 0) ? -sqrt(ae) * 0.5 : sqrt(ae) * 0.5;
4470
+ re[(ph+1)%D] = sqrt(ae) * 0.2;
4471
+ re[(ph+5)%D] = sqrt(ae) * 0.2;
4472
+ hpc_set_local(attn_graph, h, re, im);
4473
+ }
4474
+
4475
+ /* CZ-couple adjacent heads: creates cross-head entanglement */
4476
+ for (int64_t h = 0; h < n_head - 1; h++)
4477
+ hpc_cz(attn_graph, h, h + 1);
4478
+
4479
+ /* Compute attention output per head using graph marginals */
4480
+ #pragma omp parallel for schedule(static)
4481
+ for (int64_t h = 0; h < n_head; h++) {
4482
+ int64_t kv_h = h % n_head_kv;
4483
+ float *qh = qt_base + h * head_dim;
4484
+ float *kh = kt_base + kv_h * head_dim;
4485
+ float *vh = vt_base + kv_h * head_dim;
4486
+ float *Sh = S + h * head_dim * head_dim;
4487
+ float *zh = z_acc + h * head_dim;
4488
+
4489
+ /* Get HPC marginal: phase-coherent weight for this head */
4490
+ float ae = 0.0f;
4491
+ for (int64_t d = 0; d < head_dim; d++)
4492
+ ae += fabsf(kh[d] * vh[d]);
4493
+ ae += 1e-6f;
4494
+ int ph = ((int)(ae * 100.0f)) % D;
4495
+ double coherence_raw = hpc_marginal(attn_graph, h, ph);
4496
+ float coherence = (float)(coherence_raw * D);
4497
+ if (coherence < 0.1f) coherence = 0.1f;
4498
+ if (coherence > 3.0f) coherence = 3.0f;
4499
+
4500
+ /* Feature map: Ο†(x) = max(x,0) + Ξ΅ */
4501
+ float qf[256], kf[256];
4502
+ for (int64_t d = 0; d < head_dim; d++) {
4503
+ qf[d] = (qh[d] > 0 ? qh[d] : 0) + 1e-6f;
4504
+ kf[d] = (kh[d] > 0 ? kh[d] : 0) + 1e-6f;
4505
+ }
4506
+
4507
+ /* Accumulate: S += coherence Γ— outer(kf, v) */
4508
+ for (int64_t d1 = 0; d1 < head_dim; d1++) {
4509
+ float ks = kf[d1] * coherence;
4510
+ for (int64_t d2 = 0; d2 < head_dim; d2++)
4511
+ Sh[d1 * head_dim + d2] += ks * vh[d2];
4512
+ }
4513
+ for (int64_t d = 0; d < head_dim; d++)
4514
+ zh[d] += kf[d] * coherence;
4515
+
4516
+ /* Output: (qf @ S) / (qf Β· z) */
4517
+ float den = 1e-8f;
4518
+ for (int64_t d = 0; d < head_dim; d++)
4519
+ den += qf[d] * zh[d];
4520
+ float inv_den = 1.0f / den;
4521
+
4522
+ /* Write to attn_out at position [t, h*head_dim ... ] */
4523
+ float *ao = attn_out + t * n_embd;
4524
+ for (int64_t d2 = 0; d2 < head_dim; d2++) {
4525
+ float num = 0.0f;
4526
+ for (int64_t d1 = 0; d1 < head_dim; d1++)
4527
+ num += qf[d1] * Sh[d1 * head_dim + d2];
4528
+ /* Accumulate into attn_out (multiple heads write here) */
4529
+ if (h * head_dim + d2 < n_embd)
4530
+ ao[h * head_dim + d2] = num * inv_den;
4531
+ }
4532
+ }
4533
+
4534
+ /* Compact graph edges periodically */
4535
+ if (t > 0 && t % 64 == 0)
4536
+ hpc_compact_edges(attn_graph);
4537
+ }
4538
+ }
4539
+
4540
+ /* Gate projection if present */
4541
+ if (gate_w && gate_rows > 0) {
4542
+ /* attn_out is [seq, n_head*head_dim], gate_w is [n_embd, n_head*head_dim] */
4543
+ /* gate_rows may be larger than n_embd, allocate exactly what we need */
4544
+ float *gated = (float *)malloc(seq_len * gate_rows * sizeof(float));
4545
+ if (gated) {
4546
+ hpc_matmul_graph(attn_out, gate_w, gated, imp_gate, cnt_gate,
4547
+ seq_len, n_head * head_dim, gate_rows);
4548
+
4549
+ int64_t copy_dim = (gate_rows > n_embd) ? n_embd : gate_rows;
4550
+ /* Must copy row-by-row since strides may differ */
4551
+ #pragma omp parallel for schedule(static)
4552
+ for (int64_t i = 0; i < seq_len; i++) {
4553
+ memcpy(attn_out + i * n_embd, gated + i * gate_rows, copy_dim * sizeof(float));
4554
+ if (copy_dim < n_embd) {
4555
+ memset(attn_out + i * n_embd + copy_dim, 0, (n_embd - copy_dim) * sizeof(float));
4556
+ }
4557
+ }
4558
+ free(gated);
4559
+ }
4560
+ }
4561
+
4562
+ if (attn_graph) hpc_destroy(attn_graph);
4563
+ free(S); free(z_acc); free(qkv);
4564
+
4565
+ } else if (q_w && k_w && v_w && o_w) {
4566
+ /* ── Separate QKV path (standard transformer) ── */
4567
+ float *Q = (float *)malloc(seq_len * q_dim * sizeof(float));
4568
+ float *K_buf = (float *)malloc(seq_len * k_dim * sizeof(float));
4569
+ float *V_buf = (float *)malloc(seq_len * v_dim * sizeof(float));
4570
+ if (!Q || !K_buf || !V_buf) {
4571
+ free(Q); free(K_buf); free(V_buf);
4572
+ free(normed); free(attn_out);
4573
+ return;
4574
+ }
4575
+
4576
+ hpc_matmul_graph(normed, q_w, Q, imp_q, cnt_q, seq_len, n_embd, q_dim);
4577
+ hpc_matmul_graph(normed, k_w, K_buf, imp_k, cnt_k, seq_len, n_embd, k_dim);
4578
+ hpc_matmul_graph(normed, v_w, V_buf, imp_v, cnt_v, seq_len, n_embd, v_dim);
4579
+
4580
+ /* Same HPC attention as above but with separate Q/K/V buffers */
4581
+ int64_t hd_q = q_dim / n_head;
4582
+ int64_t hd_kv = k_dim / n_head_kv;
4583
+ HPCGraph *attn_graph = hpc_create(n_head);
4584
+ float *S = (float *)calloc(n_head * hd_kv * hd_kv, sizeof(float));
4585
+ float *z_acc = (float *)calloc(n_head * hd_kv, sizeof(float));
4586
+
4587
+ if (attn_graph && S && z_acc) {
4588
+ for (int64_t t = 0; t < seq_len; t++) {
4589
+ /* Encode heads into graph */
4590
+ for (int64_t h = 0; h < n_head; h++) {
4591
+ int64_t kv_h = h % n_head_kv;
4592
+ float *kh = K_buf + t * k_dim + kv_h * hd_kv;
4593
+ float *vh = V_buf + t * v_dim + kv_h * hd_kv;
4594
+ float energy = 0.0f;
4595
+ for (int64_t d = 0; d < hd_kv; d++)
4596
+ energy += kh[d] * vh[d];
4597
+ double re[D] = {0}, im[D] = {0};
4598
+ float ae = fabsf(energy) + 1e-6f;
4599
+ int ph = ((int)(ae * 100.0f)) % D;
4600
+ re[ph] = sqrt(ae);
4601
+ im[ph] = (energy < 0) ? -sqrt(ae)*0.5 : sqrt(ae)*0.5;
4602
+ hpc_set_local(attn_graph, h, re, im);
4603
+ }
4604
+ for (int64_t h = 0; h < n_head - 1; h++)
4605
+ hpc_cz(attn_graph, h, h+1);
4606
+
4607
+ #pragma omp parallel for schedule(static)
4608
+ for (int64_t h = 0; h < n_head; h++) {
4609
+ int64_t kv_h = h % n_head_kv;
4610
+ float *qh = Q + t * q_dim + h * hd_q;
4611
+ float *kh = K_buf + t * k_dim + kv_h * hd_kv;
4612
+ float *vh = V_buf + t * v_dim + kv_h * hd_kv;
4613
+ float *Sh = S + h * hd_kv * hd_kv;
4614
+ float *zh = z_acc + h * hd_kv;
4615
+ int64_t feat = hd_q < hd_kv ? hd_q : hd_kv;
4616
+
4617
+ float ae = fabsf(kh[0]*vh[0]) + 1e-6f;
4618
+ int ph = ((int)(ae * 100.0f)) % D;
4619
+ double coh_raw = hpc_marginal(attn_graph, h, ph);
4620
+ float coh = (float)(coh_raw * D);
4621
+ if (coh < 0.1f) coh = 0.1f;
4622
+ if (coh > 3.0f) coh = 3.0f;
4623
+
4624
+ for (int64_t d1 = 0; d1 < feat; d1++) {
4625
+ float kf = (kh[d1] > 0 ? kh[d1] : 0) + 1e-6f;
4626
+ float ks = kf * coh;
4627
+ for (int64_t d2 = 0; d2 < hd_kv; d2++)
4628
+ Sh[d1*hd_kv+d2] += ks * vh[d2];
4629
+ zh[d1] += kf * coh;
4630
+ }
4631
+
4632
+ float den = 1e-8f;
4633
+ for (int64_t d = 0; d < feat; d++) {
4634
+ float qf = (qh[d] > 0 ? qh[d] : 0) + 1e-6f;
4635
+ den += qf * zh[d];
4636
+ }
4637
+ float inv_den = 1.0f / den;
4638
+ float *ao = attn_out + t * n_embd;
4639
+ for (int64_t d2 = 0; d2 < hd_kv; d2++) {
4640
+ float num = 0.0f;
4641
+ for (int64_t d1 = 0; d1 < feat; d1++) {
4642
+ float qf = (qh[d1] > 0 ? qh[d1] : 0) + 1e-6f;
4643
+ num += qf * Sh[d1*hd_kv+d2];
4644
+ }
4645
+ if (h*hd_kv+d2 < n_embd)
4646
+ ao[h*hd_kv+d2] = num * inv_den;
4647
+ }
4648
+ }
4649
+ if (t > 0 && t % 64 == 0)
4650
+ hpc_compact_edges(attn_graph);
4651
+ }
4652
+ }
4653
+
4654
+ /* Output projection */
4655
+ if (o_w && o_cols > 0) {
4656
+ int64_t attn_dim = n_head * hd_kv;
4657
+ if (attn_dim > n_embd) attn_dim = n_embd;
4658
+ float *projected = (float *)calloc(seq_len * n_embd, sizeof(float));
4659
+ if (projected) {
4660
+ hpc_matmul_graph(attn_out, o_w, projected, imp_o, cnt_o,
4661
+ seq_len, o_cols, n_embd);
4662
+ memcpy(attn_out, projected, seq_len * n_embd * sizeof(float));
4663
+ free(projected);
4664
+ }
4665
+ }
4666
+
4667
+ if (attn_graph) hpc_destroy(attn_graph);
4668
+ free(S); free(z_acc);
4669
+ free(Q); free(K_buf); free(V_buf);
4670
+ }
4671
+
4672
+ /* Residual add: hidden += attn_out */
4673
+ int64_t total = seq_len * n_embd;
4674
+ #pragma omp parallel for schedule(static)
4675
+ for (int64_t i = 0; i < total; i++)
4676
+ hidden[i] += attn_out[i];
4677
+
4678
+ /* ══════════════ Phase 3: FFN ══════════════ */
4679
+ if (ffn_norm_w && ffn_gate_w && ffn_up_w && ffn_down_w && ffn_dim > 0) {
4680
+ float *normed_ff = (float *)malloc(seq_len * n_embd * sizeof(float));
4681
+ float *gate_out = (float *)malloc(seq_len * ffn_dim * sizeof(float));
4682
+ float *up_out = (float *)malloc(seq_len * ffn_dim * sizeof(float));
4683
+
4684
+ if (normed_ff && gate_out && up_out) {
4685
+ hpc_rms_norm(hidden, ffn_norm_w, normed_ff, seq_len, n_embd, eps);
4686
+
4687
+ /* Graph-based matmul for FFN with importance */
4688
+ hpc_matmul_graph(normed_ff, ffn_gate_w, gate_out,
4689
+ imp_ffn_gate, cnt_ffn_gate, seq_len, n_embd, ffn_dim);
4690
+ hpc_matmul_graph(normed_ff, ffn_up_w, up_out,
4691
+ imp_ffn_up, cnt_ffn_up, seq_len, n_embd, ffn_dim);
4692
+
4693
+ /* SiLU(gate) * up */
4694
+ hpc_silu(gate_out, seq_len * ffn_dim);
4695
+ #pragma omp parallel for schedule(static)
4696
+ for (int64_t i = 0; i < seq_len * ffn_dim; i++)
4697
+ gate_out[i] *= up_out[i];
4698
+
4699
+ /* Down projection: graph-based importance recording */
4700
+ float *ff_out = (float *)malloc(seq_len * n_embd * sizeof(float));
4701
+ if (ff_out) {
4702
+ hpc_matmul_graph(gate_out, ffn_down_w, ff_out,
4703
+ imp_ffn_down, cnt_ffn_down,
4704
+ seq_len, ffn_dim, n_embd);
4705
+ /* Residual add */
4706
+ #pragma omp parallel for schedule(static)
4707
+ for (int64_t i = 0; i < total; i++)
4708
+ hidden[i] += ff_out[i];
4709
+ free(ff_out);
4710
+ }
4711
+ }
4712
+
4713
+ free(normed_ff); free(gate_out); free(up_out);
4714
+ }
4715
+
4716
+ free(normed);
4717
+ free(attn_out);
4718
+ }
4719
+
4720
  #ifndef HEXSTATE_LIBRARY
4721
  /* ═══════════════════════════════════════════════════════════════════════════
4722
  * MAIN