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.
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/PKG-INFO +21 -4
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/README.md +20 -3
- dual_loop_controller-2.0.0a2/dual_loop/__init__.py +66 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/comprehensive_suite.py +39 -16
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/graph_reasoning.py +30 -8
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/halting_audit.py +11 -10
- dual_loop_controller-2.0.0a2/dual_loop/checkpoints/checkpoint_trained_dualloop.pt +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/decoder.py +53 -5
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/halting.py +2 -2
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/PKG-INFO +21 -4
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/SOURCES.txt +1 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/pyproject.toml +4 -1
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/tests/test_dual_loop.py +41 -0
- dual_loop_controller-2.0.0a1/dual_loop/__init__.py +0 -22
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/LICENSE +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/adapters/__init__.py +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/adapters/latent_adapter.py +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/__init__.py +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/initiative_benchmark.py +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/controller.py +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/memory.py +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/dependency_links.txt +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/requires.txt +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/top_level.txt +0 -0
- {dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/setup.cfg +0 -0
- {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.
|
|
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
|
+
[](https://pypi.org/project/dual-loop-controller/)
|
|
33
34
|
[](tests/)
|
|
34
35
|
[](https://pytorch.org/)
|
|
35
36
|
[](#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
|
-
|
|
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.
|
|
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
|
-
###
|
|
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
|
+
[](https://pypi.org/project/dual-loop-controller/)
|
|
4
5
|
[](tests/)
|
|
5
6
|
[](https://pytorch.org/)
|
|
6
7
|
[](#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
|
-
|
|
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.
|
|
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
|
-
###
|
|
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=
|
|
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.
|
|
35
|
+
entropy_threshold=1.35
|
|
34
36
|
)
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
print("
|
|
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:
|
|
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__(
|
|
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
|
-
|
|
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 =
|
|
35
|
-
v =
|
|
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
|
-
|
|
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(
|
|
56
|
-
|
|
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)
|
{dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/halting_audit.py
RENAMED
|
@@ -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
|
-
|
|
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
|
-
|
|
41
|
-
|
|
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(
|
|
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
|
|
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
|
-
|
|
73
|
-
|
|
74
|
-
|
|
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 =
|
|
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 (
|
|
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)
|
{dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop_controller.egg-info/PKG-INFO
RENAMED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: dual-loop-controller
|
|
3
|
-
Version: 2.0.
|
|
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
|
+
[](https://pypi.org/project/dual-loop-controller/)
|
|
33
34
|
[](tests/)
|
|
34
35
|
[](https://pytorch.org/)
|
|
35
36
|
[](#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
|
-
|
|
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.
|
|
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
|
-
###
|
|
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.
|
|
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
|
-
]
|
|
File without changes
|
{dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/adapters/__init__.py
RENAMED
|
File without changes
|
{dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/adapters/latent_adapter.py
RENAMED
|
File without changes
|
{dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/dual_loop/benchmarks/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{dual_loop_controller-2.0.0a1 → dual_loop_controller-2.0.0a2}/tests/test_adapter_integration.py
RENAMED
|
File without changes
|