dual-loop-controller 2.0.0a1__tar.gz → 2.0.0a2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (26) hide show
  1. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/PKG-INFO +21 -4
  2. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/README.md +20 -3
  3. dual_loop_controller-2.0.0a2/dual_loop/__init__.py +66 -0
  4. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/comprehensive_suite.py +39 -16
  5. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/graph_reasoning.py +30 -8
  6. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/halting_audit.py +11 -10
  7. dual_loop_controller-2.0.0a2/dual_loop/checkpoints/checkpoint_trained_dualloop.pt +0 -0
  8. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/decoder.py +53 -5
  9. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/halting.py +2 -2
  10. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/PKG-INFO +21 -4
  11. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/SOURCES.txt +1 -0
  12. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/pyproject.toml +4 -1
  13. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/tests/test_dual_loop.py +41 -0
  14. dual_loop_controller-2.0.0a1/dual_loop/__init__.py +0 -22
  15. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/LICENSE +0 -0
  16. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/adapters/__init__.py +0 -0
  17. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/adapters/latent_adapter.py +0 -0
  18. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/__init__.py +0 -0
  19. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/initiative_benchmark.py +0 -0
  20. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/controller.py +0 -0
  21. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/memory.py +0 -0
  22. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/dependency_links.txt +0 -0
  23. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/requires.txt +0 -0
  24. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/top_level.txt +0 -0
  25. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/setup.cfg +0 -0
  26. {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/tests/test_adapter_integration.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dual-loop-controller
3
- Version: 2.0.0a1
3
+ Version: 2.0.0a2
4
4
  Summary: A hardware-aligned, manifold-preserving latent deliberation framework for Transformers
5
5
  Author: Ch3nOff
6
6
  License-Expression: MIT
@@ -30,6 +30,7 @@ Dynamic: license-file
30
30
  # Dual-Loop Cognitive Controller v2.0
31
31
  > **A Hardware-Aligned Latent Deliberation Framework for Transformers: Architecture & Empirical Analysis**
32
32
 
33
+ [![PyPI](https://img.shields.io/pypi/v/dual-loop-controller.svg)](https://pypi.org/project/dual-loop-controller/)
33
34
  [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests/)
34
35
  [![PyTorch](https://img.shields.io/badge/PyTorch-2.14%2B-ee4c2c.svg)](https://pytorch.org/)
35
36
  [![Status](https://img.shields.io/badge/status-empirical--audit-orange.svg)](#empirical-findings)
@@ -118,22 +119,38 @@ Despite the scaling limits at small model regimes, the repository provides clean
118
119
 
119
120
  ### 1. Installation
120
121
  ```bash
122
+ # Install officially from PyPI:
123
+ pip install --pre dual-loop-controller
124
+ # or exact version: pip install dual-loop-controller==2.0.0a2
125
+
126
+ # Or install direct from GitHub release tag:
127
+ pip install git+https://github.com/Ch3nOff/dual-loop-controller.git@v2.0.0a2
128
+
129
+ # Or clone locally and install in editable mode:
121
130
  git clone https://github.com/Ch3nOff/dual-loop-controller.git
122
131
  cd dual-loop-controller
123
- python -m pip install torch numpy
132
+ pip install -e .
124
133
  ```
125
134
 
135
+ > [!NOTE]
136
+ > **Pretrained Weights Bundled**: A 225K parameter trained reference checkpoint (~912 KB) is bundled directly in `dual_loop/checkpoints/checkpoint_trained_dualloop.pt`. Fresh clones and pip installs run inference and audits out-of-the-box without requiring a training step first.
137
+
126
138
  ### 2. Running Component Tests (Verifying Shapes & Gradients)
127
139
  ```bash
128
140
  python -m unittest discover -s tests -p "test_*.py"
129
141
  ```
130
142
 
131
- ### 3. Running the Honest Benchmark Suite (Live Tensor Computations)
143
+ ### 3. Verifying Dynamic Halting & Pareto Calibration
144
+ ```bash
145
+ python verify_dynamic_inference.py
146
+ ```
147
+
148
+ ### 4. Running the Honest Benchmark Suite (Live Tensor Computations)
132
149
  ```bash
133
150
  python -m dual_loop.benchmarks.comprehensive_suite
134
151
  ```
135
152
 
136
- ### 4. Re-Training from Scratch
153
+ ### 5. Re-Training from Scratch
137
154
  ```bash
138
155
  python train.py --epochs 35 --hops 3 --k_steps 3 --d_model 64
139
156
  ```
@@ -1,6 +1,7 @@
1
1
  # Dual-Loop Cognitive Controller v2.0
2
2
  > **A Hardware-Aligned Latent Deliberation Framework for Transformers: Architecture & Empirical Analysis**
3
3
 
4
+ [![PyPI](https://img.shields.io/pypi/v/dual-loop-controller.svg)](https://pypi.org/project/dual-loop-controller/)
4
5
  [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests/)
5
6
  [![PyTorch](https://img.shields.io/badge/PyTorch-2.14%2B-ee4c2c.svg)](https://pytorch.org/)
6
7
  [![Status](https://img.shields.io/badge/status-empirical--audit-orange.svg)](#empirical-findings)
@@ -89,22 +90,38 @@ Despite the scaling limits at small model regimes, the repository provides clean
89
90
 
90
91
  ### 1. Installation
91
92
  ```bash
93
+ # Install officially from PyPI:
94
+ pip install --pre dual-loop-controller
95
+ # or exact version: pip install dual-loop-controller==2.0.0a2
96
+
97
+ # Or install direct from GitHub release tag:
98
+ pip install git+https://github.com/Ch3nOff/dual-loop-controller.git@v2.0.0a2
99
+
100
+ # Or clone locally and install in editable mode:
92
101
  git clone https://github.com/Ch3nOff/dual-loop-controller.git
93
102
  cd dual-loop-controller
94
- python -m pip install torch numpy
103
+ pip install -e .
95
104
  ```
96
105
 
106
+ > [!NOTE]
107
+ > **Pretrained Weights Bundled**: A 225K parameter trained reference checkpoint (~912 KB) is bundled directly in `dual_loop/checkpoints/checkpoint_trained_dualloop.pt`. Fresh clones and pip installs run inference and audits out-of-the-box without requiring a training step first.
108
+
97
109
  ### 2. Running Component Tests (Verifying Shapes & Gradients)
98
110
  ```bash
99
111
  python -m unittest discover -s tests -p "test_*.py"
100
112
  ```
101
113
 
102
- ### 3. Running the Honest Benchmark Suite (Live Tensor Computations)
114
+ ### 3. Verifying Dynamic Halting & Pareto Calibration
115
+ ```bash
116
+ python verify_dynamic_inference.py
117
+ ```
118
+
119
+ ### 4. Running the Honest Benchmark Suite (Live Tensor Computations)
103
120
  ```bash
104
121
  python -m dual_loop.benchmarks.comprehensive_suite
105
122
  ```
106
123
 
107
- ### 4. Re-Training from Scratch
124
+ ### 5. Re-Training from Scratch
108
125
  ```bash
109
126
  python train.py --epochs 35 --hops 3 --k_steps 3 --d_model 64
110
127
  ```
@@ -0,0 +1,66 @@
1
+ """
2
+ Dual-Loop Cognitive Controller v2.0
3
+ ===================================
4
+ A hardware-aligned, manifold-preserving latent reasoning framework
5
+ for Transformer architectures.
6
+ """
7
+
8
+ from .memory import CognitiveWorkingMemory
9
+ from .halting import EntropyHaltingUnit
10
+ from .controller import RecurrentLatentController, TopKCapacityCrossAttention
11
+ from .decoder import DualLoopTransformer
12
+ from .adapters.latent_adapter import LatentDeliberationAdapter
13
+
14
+ import os
15
+ import torch
16
+ from typing import Optional
17
+
18
+ __version__ = "2.0.0a2"
19
+
20
+ def get_default_checkpoint_path() -> Optional[str]:
21
+ """
22
+ Resolves the pre-trained reference checkpoint path by checking:
23
+ 1. Local working directory: './checkpoint_trained_dualloop.pt'
24
+ 2. Bundled package data: 'dual_loop/checkpoints/checkpoint_trained_dualloop.pt'
25
+ """
26
+ local_path = "checkpoint_trained_dualloop.pt"
27
+ if os.path.exists(local_path):
28
+ return os.path.abspath(local_path)
29
+
30
+ pkg_path = os.path.join(os.path.dirname(__file__), "checkpoints", "checkpoint_trained_dualloop.pt")
31
+ if os.path.exists(pkg_path):
32
+ return os.path.abspath(pkg_path)
33
+
34
+ return None
35
+
36
+ def load_trained_checkpoint(model: Optional[DualLoopTransformer] = None, checkpoint_path: Optional[str] = None):
37
+ """
38
+ Loads pre-trained weights into the DualLoopTransformer model, automatically falling back
39
+ to the bundled package checkpoint if no path is provided.
40
+ """
41
+ resolved_path = checkpoint_path or get_default_checkpoint_path()
42
+ if resolved_path is None or not os.path.exists(resolved_path):
43
+ raise FileNotFoundError(
44
+ "Pretrained checkpoint not found in local directory or bundled package data.\n"
45
+ "To train the model from scratch, execute:\n"
46
+ " python train.py --epochs 35 --hops 3 --k_steps 3\n"
47
+ "or run:\n"
48
+ " python evaluate_real_behavior.py"
49
+ )
50
+
51
+ state_dict = torch.load(resolved_path, map_location="cpu", weights_only=True)
52
+ if model is not None:
53
+ model.load_state_dict(state_dict, strict=False)
54
+ return model, resolved_path
55
+ return state_dict
56
+
57
+ __all__ = [
58
+ "CognitiveWorkingMemory",
59
+ "EntropyHaltingUnit",
60
+ "RecurrentLatentController",
61
+ "TopKCapacityCrossAttention",
62
+ "DualLoopTransformer",
63
+ "LatentDeliberationAdapter",
64
+ "get_default_checkpoint_path",
65
+ "load_trained_checkpoint",
66
+ ]
@@ -13,13 +13,15 @@ Evaluations Performed:
13
13
 
14
14
  import os
15
15
  import time
16
+ import random
17
+ import numpy as np
16
18
  import torch
17
19
  import torch.nn.functional as F
18
20
 
19
- from dual_loop import DualLoopTransformer
21
+ from dual_loop import DualLoopTransformer, load_trained_checkpoint
20
22
  from dual_loop.benchmarks import MultiHopGraphDataset
21
23
 
22
- def load_or_instantiate_model(checkpoint_path="checkpoint_trained_dualloop.pt", num_nodes=16):
24
+ def load_or_instantiate_model(checkpoint_path=None, num_nodes=16):
23
25
  vocab_size = num_nodes + 3
24
26
  model = DualLoopTransformer(
25
27
  vocab_size=vocab_size,
@@ -30,19 +32,33 @@ def load_or_instantiate_model(checkpoint_path="checkpoint_trained_dualloop.pt",
30
32
  num_cwm_slots=12,
31
33
  max_ponder_steps=3,
32
34
  capacity_factor=0.5,
33
- entropy_threshold=1.30
35
+ entropy_threshold=1.35
34
36
  )
35
- if os.path.exists(checkpoint_path):
36
- state_dict = torch.load(checkpoint_path, map_location="cpu", weights_only=True)
37
- model.load_state_dict(state_dict, strict=False)
38
- print(f"[Model Loader] Loaded weights from '{checkpoint_path}'.")
39
- else:
40
- print("[Model Loader] Warning: Checkpoint not found; running with initialized weights.")
37
+ try:
38
+ _, loaded_path = load_trained_checkpoint(model, checkpoint_path)
39
+ print(f"[Model Loader] Successfully loaded weights from '{loaded_path}'.")
40
+ except FileNotFoundError as e:
41
+ print("\n" + "!" * 80)
42
+ print("CRITICAL WARNING: TRAINED CHECKPOINT NOT FOUND!")
43
+ print("The benchmark suite is currently executing on UNTRAINED (random) weights.")
44
+ print("Reported metrics will reflect random baseline (~6.25%).")
45
+ print("To evaluate true model capabilities (29-34% accuracy), generate weights with:")
46
+ print(" python train.py --epochs 35 --hops 3 --k_steps 3")
47
+ print("or:")
48
+ print(" python evaluate_real_behavior.py")
49
+ print("!" * 80 + "\n")
50
+
41
51
  model.eval()
42
52
  return model
43
53
 
44
54
  def run_suite():
55
+ # Deterministic Seeding for 100% Reproducibility
56
+ random.seed(42)
57
+ np.random.seed(42)
45
58
  torch.manual_seed(42)
59
+ if torch.cuda.is_available():
60
+ torch.cuda.manual_seed_all(42)
61
+
46
62
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
47
63
  num_nodes = 16
48
64
  chance_baseline = 100.0 / num_nodes # 6.25%
@@ -61,8 +77,8 @@ def run_suite():
61
77
  hop_results = {}
62
78
  with torch.no_grad():
63
79
  for h in [1, 2, 3]:
64
- ds = MultiHopGraphDataset(num_samples=400, num_nodes=num_nodes, num_edges=6, hops=h)
65
- x, y_all = ds.get_batch(400)
80
+ ds = MultiHopGraphDataset(num_samples=400, num_nodes=num_nodes, num_edges=6, hops=h, seed=100 + h)
81
+ x, y_all = ds.get_batch(400, shuffle=False)
66
82
  x, y = x.to(device), y_all[:, -1].to(device)
67
83
  logits, _ = model(x, k_steps=h)
68
84
  acc = (logits.argmax(dim=-1) == y).float().mean().item() * 100.0
@@ -76,8 +92,8 @@ def run_suite():
76
92
  distractor_results = {}
77
93
  with torch.no_grad():
78
94
  for e in [6, 8, 12, 16]:
79
- ds_e = MultiHopGraphDataset(num_samples=300, num_nodes=num_nodes, num_edges=e, hops=3)
80
- x_e, y_e_all = ds_e.get_batch(300)
95
+ ds_e = MultiHopGraphDataset(num_samples=300, num_nodes=num_nodes, num_edges=e, hops=3, seed=200 + e)
96
+ x_e, y_e_all = ds_e.get_batch(300, shuffle=False)
81
97
  x_e, y_e = x_e.to(device), y_e_all[:, -1].to(device)
82
98
  logits_e, _ = model(x_e, k_steps=3)
83
99
  acc_e = (logits_e.argmax(dim=-1) == y_e).float().mean().item() * 100.0
@@ -85,11 +101,11 @@ def run_suite():
85
101
  print(f" {e:2d} Total Edges -> Test Accuracy (Computed from Logits): {acc_e:5.1f}%")
86
102
 
87
103
  # -------------------------------------------------------------------------
88
- # BENCHMARK 3: Unvarnished Test-Time Compute Scaling (K = 0 .. 5)
104
+ # BENCHMARK 3: UnvarnISHED TEST-TIME COMPUTE SCALING (K = 0 .. 5)
89
105
  # -------------------------------------------------------------------------
90
106
  print("\n[BENCHMARK 3: UNVARNISHED TEST-TIME COMPUTE SCALING (K = 0 .. 5)]")
91
- ds_scale = MultiHopGraphDataset(num_samples=500, num_nodes=num_nodes, num_edges=6, hops=3)
92
- xs, ys_all = ds_scale.get_batch(500)
107
+ ds_scale = MultiHopGraphDataset(num_samples=500, num_nodes=num_nodes, num_edges=6, hops=3, seed=42)
108
+ xs, ys_all = ds_scale.get_batch(500, shuffle=False)
93
109
  xs, ys = xs.to(device), ys_all[:, -1].to(device)
94
110
  scale_results = {}
95
111
  with torch.no_grad():
@@ -107,6 +123,13 @@ def run_suite():
107
123
  print("\n[BENCHMARK 4: PER-SAMPLE DYNAMIC HALTING & PARETO FRONTIER]")
108
124
  print("Evaluating individual sample halting without artificial batch-mean collapsing:")
109
125
  with torch.no_grad():
126
+ opt_thresh = model.calibrate_halting(xs, target_labels=ys, verbose=False)
127
+ logits_dyn, info_dyn = model(xs, dynamic_halting=True)
128
+ acc_dyn = (logits_dyn.argmax(-1) == ys).float().mean().item() * 100.0
129
+ st = info_dyn['steps_taken']
130
+ print(f" Direct Inference: Calibrated Threshold={opt_thresh:.3f} nats | Accuracy={acc_dyn:.1f}% | Effective K={info_dyn['effective_k']:.2f} steps")
131
+ print(f" Steps Taken: K=1: {(st==1).float().mean()*100:.1f}%, K=2: {(st==2).float().mean()*100:.1f}%, K=3: {(st==3).float().mean()*100:.1f}%\n")
132
+
110
133
  logits_k1, _ = model(xs, k_steps=1)
111
134
  logits_k2, _ = model(xs, k_steps=2)
112
135
  logits_k3, _ = model(xs, k_steps=3)
@@ -1,17 +1,27 @@
1
1
  import random
2
2
  import torch
3
- from typing import List, Tuple
3
+ from typing import List, Tuple, Optional
4
4
 
5
5
  class MultiHopGraphDataset:
6
6
  """
7
7
  Synthetic Multi-Hop Graph Reasoning Benchmark.
8
8
  Generates directed pointer chains with random distractor edges.
9
+ Supports isolated deterministic seeding for 100% reproducible benchmarks.
9
10
  """
10
- def __init__(self, num_samples: int = 2000, num_nodes: int = 20, num_edges: int = 8, hops: int = 3):
11
+ def __init__(
12
+ self,
13
+ num_samples: int = 2000,
14
+ num_nodes: int = 20,
15
+ num_edges: int = 8,
16
+ hops: int = 3,
17
+ seed: Optional[int] = None
18
+ ):
11
19
  self.num_samples = num_samples
12
20
  self.num_nodes = num_nodes
13
21
  self.num_edges = num_edges
14
22
  self.hops = hops
23
+ self.seed = seed
24
+ self.rng = random.Random(seed) if seed is not None else random.Random()
15
25
 
16
26
  self.ARROW = num_nodes
17
27
  self.SEP = num_nodes + 1
@@ -23,7 +33,7 @@ class MultiHopGraphDataset:
23
33
  samples = []
24
34
  for _ in range(self.num_samples):
25
35
  nodes = list(range(self.num_nodes))
26
- random.shuffle(nodes)
36
+ self.rng.shuffle(nodes)
27
37
 
28
38
  chain = nodes[:self.hops + 1]
29
39
  edges = []
@@ -31,12 +41,12 @@ class MultiHopGraphDataset:
31
41
  edges.append((chain[i], chain[i+1]))
32
42
 
33
43
  while len(edges) < self.num_edges:
34
- u = random.choice(nodes)
35
- v = random.choice([n for n in nodes if n != u])
44
+ u = self.rng.choice(nodes)
45
+ v = self.rng.choice([n for n in nodes if n != u])
36
46
  if (u, v) not in edges:
37
47
  edges.append((u, v))
38
48
 
39
- random.shuffle(edges)
49
+ self.rng.shuffle(edges)
40
50
 
41
51
  seq = []
42
52
  for u, v in edges:
@@ -52,8 +62,20 @@ class MultiHopGraphDataset:
52
62
  ))
53
63
  return samples
54
64
 
55
- def get_batch(self, batch_size: int = 64) -> Tuple[torch.Tensor, torch.Tensor]:
56
- indices = random.sample(range(self.num_samples), batch_size)
65
+ def get_batch(
66
+ self,
67
+ batch_size: int = 64,
68
+ shuffle: bool = True,
69
+ seed: Optional[int] = None
70
+ ) -> Tuple[torch.Tensor, torch.Tensor]:
71
+ if not shuffle:
72
+ indices = list(range(min(batch_size, self.num_samples)))
73
+ elif seed is not None:
74
+ batch_rng = random.Random(seed)
75
+ indices = batch_rng.sample(range(self.num_samples), batch_size)
76
+ else:
77
+ indices = self.rng.sample(range(self.num_samples), batch_size)
78
+
57
79
  seqs = [self.data[i][0] for i in indices]
58
80
  targets = torch.stack([self.data[i][1] for i in indices])
59
81
  inputs = torch.stack(seqs)
@@ -10,14 +10,19 @@ import torch
10
10
  import torch.nn.functional as F
11
11
  import numpy as np
12
12
 
13
- from dual_loop import DualLoopTransformer
13
+ from dual_loop import DualLoopTransformer, load_trained_checkpoint
14
14
  from dual_loop.benchmarks import MultiHopGraphDataset
15
15
 
16
16
  def audit_halting_pareto():
17
+ import random
18
+ random.seed(42)
19
+ np.random.seed(42)
17
20
  torch.manual_seed(42)
21
+ if torch.cuda.is_available():
22
+ torch.cuda.manual_seed_all(42)
23
+
18
24
  device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
19
25
  num_nodes = 16
20
- checkpoint_path = "checkpoint_trained_dualloop.pt"
21
26
 
22
27
  model = DualLoopTransformer(
23
28
  vocab_size=num_nodes + 3,
@@ -30,15 +35,11 @@ def audit_halting_pareto():
30
35
  capacity_factor=0.5
31
36
  ).to(device)
32
37
 
33
- if os.path.exists(checkpoint_path):
34
- state_dict = torch.load(checkpoint_path, map_location="cpu", weights_only=True)
35
- model.load_state_dict(state_dict, strict=False)
36
- else:
37
- raise FileNotFoundError("Checkpoint not found.")
38
-
38
+ load_trained_checkpoint(model)
39
39
  model.eval()
40
- dataset = MultiHopGraphDataset(num_samples=500, num_nodes=num_nodes, num_edges=6, hops=3)
41
- x_test, y_test_all = dataset.get_batch(500)
40
+
41
+ dataset = MultiHopGraphDataset(num_samples=500, num_nodes=num_nodes, num_edges=6, hops=3, seed=42)
42
+ x_test, y_test_all = dataset.get_batch(500, shuffle=False)
42
43
  x_test, y_test = x_test.to(device), y_test_all[:, -1].to(device)
43
44
 
44
45
  # 1. Measure raw step-by-step entropy distribution
@@ -60,18 +60,66 @@ class DualLoopTransformer(nn.Module):
60
60
  self.inner_decoder = nn.TransformerEncoder(dec_layer, num_layers=num_decoder_layers)
61
61
  self.lm_head = nn.Linear(d_model, vocab_size)
62
62
 
63
- def calibrate_halting(self, sample_inputs: torch.Tensor, percentile: float = 35.0):
63
+ def calibrate_halting(
64
+ self,
65
+ sample_inputs: torch.Tensor,
66
+ target_labels: Optional[torch.Tensor] = None,
67
+ percentile: Optional[float] = None,
68
+ candidate_percentiles: Tuple[float, ...] = (25.0, 35.0, 50.0, 65.0, 75.0, 85.0),
69
+ verbose: bool = False
70
+ ) -> float:
64
71
  """
65
72
  Dynamically calibrates the halting threshold against the model's actual
66
- decoder entropy distribution on step 1 of validation samples.
73
+ decoder predictive entropy distribution on validation samples.
74
+
75
+ Modes:
76
+ 1. Fixed Quantile (default: 75th percentile / upper quartile):
77
+ If `percentile` is specified (or when `target_labels is None`), sets the threshold
78
+ directly to the specified quantile of step-1 predictive entropy.
79
+ 2. Automated Pareto Grid-Search:
80
+ If `target_labels` is provided and `percentile is None`, sweeps across candidate
81
+ percentiles to find the Pareto-optimal threshold balancing high accuracy with reduced
82
+ pondering steps (effective K).
67
83
  """
68
84
  self.eval()
69
85
  with torch.no_grad():
70
86
  logits_k1, _ = self.forward(sample_inputs, k_steps=1, dynamic_halting=False)
71
87
  ent_k1 = self.outer_loop.halting_unit.calculate_entropy(logits_k1)
72
- calibrated = torch.quantile(ent_k1, percentile / 100.0).item()
73
- self.outer_loop.halting_unit.entropy_threshold = calibrated
74
- return calibrated
88
+
89
+ if target_labels is not None and percentile is None:
90
+ best_score = -float('inf')
91
+ best_thresh = None
92
+ best_p = 75.0
93
+
94
+ for p in candidate_percentiles:
95
+ cand_thresh = torch.quantile(ent_k1, p / 100.0).item()
96
+ self.outer_loop.halting_unit.entropy_threshold = cand_thresh
97
+ logits_dyn, info = self.forward(sample_inputs, dynamic_halting=True)
98
+ acc = (logits_dyn.argmax(dim=-1) == target_labels).float().mean().item() * 100.0
99
+ eff_k = info["effective_k"]
100
+
101
+ # Pareto scoring: maximize accuracy, reward compute savings
102
+ max_k = float(self.outer_loop.max_ponder_steps)
103
+ compute_saving = (max_k - eff_k) / max_k if max_k > 0 else 0.0
104
+ score = acc + (compute_saving * 5.0)
105
+
106
+ if verbose:
107
+ print(f"[Calibration Search] P={p:4.1f}% -> Thresh={cand_thresh:.3f} | Acc={acc:5.1f}% | Avg K={eff_k:.2f} | Score={score:.2f}")
108
+
109
+ if score > best_score:
110
+ best_score = score
111
+ best_thresh = cand_thresh
112
+ best_p = p
113
+
114
+ self.outer_loop.halting_unit.entropy_threshold = best_thresh
115
+ if verbose:
116
+ print(f"[Calibration Search] Selected Pareto-optimal Percentile={best_p}% (Threshold={best_thresh:.3f} nats)")
117
+ return best_thresh
118
+ else:
119
+ p = 75.0 if percentile is None else percentile
120
+ calibrated = torch.quantile(ent_k1, p / 100.0).item()
121
+ self.outer_loop.halting_unit.entropy_threshold = calibrated
122
+ return calibrated
75
123
 
76
124
  def forward(
77
125
  self,
@@ -33,10 +33,10 @@ class EntropyHaltingUnit(nn.Module):
33
33
  entropy = -torch.sum(probs * log_probs, dim=-1)
34
34
  return entropy
35
35
 
36
- def calibrate_threshold(self, sample_logits: torch.Tensor, percentile: float = 50.0):
36
+ def calibrate_threshold(self, sample_logits: torch.Tensor, percentile: float = 75.0):
37
37
  """
38
38
  Calibrates the entropy threshold to match the model's actual empirical
39
- predictive distribution (e.g. median / 50th percentile of validation entropy).
39
+ predictive distribution (defaults to 75th percentile / upper quartile of validation entropy).
40
40
  """
41
41
  with torch.no_grad():
42
42
  entropies = self.calculate_entropy(sample_logits)
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dual-loop-controller
3
- Version: 2.0.0a1
3
+ Version: 2.0.0a2
4
4
  Summary: A hardware-aligned, manifold-preserving latent deliberation framework for Transformers
5
5
  Author: Ch3nOff
6
6
  License-Expression: MIT
@@ -30,6 +30,7 @@ Dynamic: license-file
30
30
  # Dual-Loop Cognitive Controller v2.0
31
31
  > **A Hardware-Aligned Latent Deliberation Framework for Transformers: Architecture & Empirical Analysis**
32
32
 
33
+ [![PyPI](https://img.shields.io/pypi/v/dual-loop-controller.svg)](https://pypi.org/project/dual-loop-controller/)
33
34
  [![Tests](https://img.shields.io/badge/tests-passing-brightgreen.svg)](tests/)
34
35
  [![PyTorch](https://img.shields.io/badge/PyTorch-2.14%2B-ee4c2c.svg)](https://pytorch.org/)
35
36
  [![Status](https://img.shields.io/badge/status-empirical--audit-orange.svg)](#empirical-findings)
@@ -118,22 +119,38 @@ Despite the scaling limits at small model regimes, the repository provides clean
118
119
 
119
120
  ### 1. Installation
120
121
  ```bash
122
+ # Install officially from PyPI:
123
+ pip install --pre dual-loop-controller
124
+ # or exact version: pip install dual-loop-controller==2.0.0a2
125
+
126
+ # Or install direct from GitHub release tag:
127
+ pip install git+https://github.com/Ch3nOff/dual-loop-controller.git@v2.0.0a2
128
+
129
+ # Or clone locally and install in editable mode:
121
130
  git clone https://github.com/Ch3nOff/dual-loop-controller.git
122
131
  cd dual-loop-controller
123
- python -m pip install torch numpy
132
+ pip install -e .
124
133
  ```
125
134
 
135
+ > [!NOTE]
136
+ > **Pretrained Weights Bundled**: A 225K parameter trained reference checkpoint (~912 KB) is bundled directly in `dual_loop/checkpoints/checkpoint_trained_dualloop.pt`. Fresh clones and pip installs run inference and audits out-of-the-box without requiring a training step first.
137
+
126
138
  ### 2. Running Component Tests (Verifying Shapes & Gradients)
127
139
  ```bash
128
140
  python -m unittest discover -s tests -p "test_*.py"
129
141
  ```
130
142
 
131
- ### 3. Running the Honest Benchmark Suite (Live Tensor Computations)
143
+ ### 3. Verifying Dynamic Halting & Pareto Calibration
144
+ ```bash
145
+ python verify_dynamic_inference.py
146
+ ```
147
+
148
+ ### 4. Running the Honest Benchmark Suite (Live Tensor Computations)
132
149
  ```bash
133
150
  python -m dual_loop.benchmarks.comprehensive_suite
134
151
  ```
135
152
 
136
- ### 4. Re-Training from Scratch
153
+ ### 5. Re-Training from Scratch
137
154
  ```bash
138
155
  python train.py --epochs 35 --hops 3 --k_steps 3 --d_model 64
139
156
  ```
@@ -13,6 +13,7 @@ dual_loop/benchmarks/comprehensive_suite.py
13
13
  dual_loop/benchmarks/graph_reasoning.py
14
14
  dual_loop/benchmarks/halting_audit.py
15
15
  dual_loop/benchmarks/initiative_benchmark.py
16
+ dual_loop/checkpoints/checkpoint_trained_dualloop.pt
16
17
  dual_loop_controller.egg-info/PKG-INFO
17
18
  dual_loop_controller.egg-info/SOURCES.txt
18
19
  dual_loop_controller.egg-info/dependency_links.txt
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "dual-loop-controller"
7
- version = "2.0.0a1"
7
+ version = "2.0.0a2"
8
8
  description = "A hardware-aligned, manifold-preserving latent deliberation framework for Transformers"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.9"
@@ -51,3 +51,6 @@ Repository = "https://github.com/Ch3nOff/dual-loop-controller.git"
51
51
  where = ["."]
52
52
  include = ["dual_loop*"]
53
53
  exclude = ["tests*", "dist*", "build*"]
54
+
55
+ [tool.setuptools.package-data]
56
+ dual_loop = ["checkpoints/*.pt"]
@@ -106,5 +106,46 @@ class TestDualLoopComponents(unittest.TestCase):
106
106
  self.assertIsNotNone(model.outer_loop.query_projector[0].weight.grad)
107
107
  self.assertGreater(model.outer_loop.query_projector[0].weight.grad.norm().item(), 0.0)
108
108
 
109
+ def test_calibrate_halting_default_and_search(self):
110
+ model = DualLoopTransformer(
111
+ vocab_size=self.vocab_size,
112
+ d_model=self.D,
113
+ max_ponder_steps=3
114
+ )
115
+ sample_inputs = torch.randint(0, self.vocab_size, (16, self.N))
116
+
117
+ # Test 1: Default percentile (75.0)
118
+ calib_thresh = model.calibrate_halting(sample_inputs)
119
+ self.assertIsInstance(calib_thresh, float)
120
+ self.assertEqual(model.outer_loop.halting_unit.entropy_threshold, calib_thresh)
121
+
122
+ # Test 2: Automated Grid-Search with target_labels
123
+ target_labels = torch.randint(0, self.vocab_size, (16,))
124
+ best_thresh = model.calibrate_halting(sample_inputs, target_labels=target_labels)
125
+ self.assertIsInstance(best_thresh, float)
126
+ self.assertEqual(model.outer_loop.halting_unit.entropy_threshold, best_thresh)
127
+
128
+ def test_load_trained_checkpoint(self):
129
+ from dual_loop import load_trained_checkpoint
130
+ model = DualLoopTransformer(vocab_size=19, d_model=64, num_cwm_slots=12, max_ponder_steps=3)
131
+ loaded_model, loaded_path = load_trained_checkpoint(model)
132
+ self.assertIsNotNone(loaded_model)
133
+ self.assertTrue(len(loaded_path) > 0)
134
+
135
+ def test_multihop_graph_dataset_reproducibility(self):
136
+ from dual_loop.benchmarks import MultiHopGraphDataset
137
+ ds1 = MultiHopGraphDataset(num_samples=50, num_nodes=16, num_edges=6, hops=3, seed=123)
138
+ ds2 = MultiHopGraphDataset(num_samples=50, num_nodes=16, num_edges=6, hops=3, seed=123)
139
+
140
+ # Check first sequence and targets match exactly
141
+ self.assertTrue(torch.equal(ds1.data[0][0], ds2.data[0][0]))
142
+ self.assertTrue(torch.equal(ds1.data[0][1], ds2.data[0][1]))
143
+
144
+ # Check get_batch matches
145
+ b1_x, b1_y = ds1.get_batch(20, shuffle=False)
146
+ b2_x, b2_y = ds2.get_batch(20, shuffle=False)
147
+ self.assertTrue(torch.equal(b1_x, b2_x))
148
+ self.assertTrue(torch.equal(b1_y, b2_y))
149
+
109
150
  if __name__ == "__main__":
110
151
  unittest.main()
@@ -1,22 +0,0 @@
1
- """
2
- Dual-Loop Cognitive Controller v2.0
3
- ===================================
4
- A hardware-aligned, manifold-preserving latent reasoning framework
5
- for Transformer architectures.
6
- """
7
-
8
- from .memory import CognitiveWorkingMemory
9
- from .halting import EntropyHaltingUnit
10
- from .controller import RecurrentLatentController, TopKCapacityCrossAttention
11
- from .decoder import DualLoopTransformer
12
- from .adapters.latent_adapter import LatentDeliberationAdapter
13
-
14
- __version__ = "2.0.0a1"
15
- __all__ = [
16
- "CognitiveWorkingMemory",
17
- "EntropyHaltingUnit",
18
- "RecurrentLatentController",
19
- "TopKCapacityCrossAttention",
20
- "DualLoopTransformer",
21
- "LatentDeliberationAdapter",
22
- ]