quantum-cortex 1.0.0__py3-none-any.whl
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.
- cortex_c2/__init__.py +161 -0
- cortex_c2/consolidation.py +146 -0
- cortex_c2/router_ablation.py +254 -0
- cortex_c2/router_v2.py +109 -0
- cortex_c2b/__init__.py +724 -0
- cortex_c2b/companion_demo.py +203 -0
- cortex_c2b/crypto.py +102 -0
- cortex_c2b/external_arm.py +591 -0
- cortex_c2b/hm_lm.py +983 -0
- cortex_c2b/hm_protocol.py +308 -0
- cortex_c2b/lifecycle.py +454 -0
- cortex_c2b/lm_bridge.py +338 -0
- cortex_c2b/organ_use.py +254 -0
- cortex_c2b/read_path.py +218 -0
- cortex_c2b/reconstruction_probe.py +471 -0
- cortex_c2b/write_path.py +176 -0
- cortex_data/__init__.py +439 -0
- cortex_data/relay.py +174 -0
- cortex_eval/__init__.py +20 -0
- cortex_eval/ablation_mqar.py +187 -0
- cortex_eval/confirmation_run.py +133 -0
- cortex_eval/domain_map.py +127 -0
- cortex_eval/external_baseline.py +148 -0
- cortex_eval/ingest_confirmation.py +194 -0
- cortex_eval/mqar.py +122 -0
- cortex_eval/multiseed.py +148 -0
- cortex_eval/recurrent_base.py +197 -0
- cortex_eval/recurrent_ladder.py +159 -0
- cortex_eval/resumable_ladder.py +231 -0
- cortex_eval/resumable_rb.py +207 -0
- cortex_eval/run_mqar.py +127 -0
- cortex_eval/serial_position.py +116 -0
- quantum_cortex-1.0.0.dist-info/METADATA +209 -0
- quantum_cortex-1.0.0.dist-info/RECORD +41 -0
- quantum_cortex-1.0.0.dist-info/WHEEL +5 -0
- quantum_cortex-1.0.0.dist-info/licenses/AUTHORS.md +30 -0
- quantum_cortex-1.0.0.dist-info/licenses/LICENSE +73 -0
- quantum_cortex-1.0.0.dist-info/licenses/LICENSE-DOCS.md +34 -0
- quantum_cortex-1.0.0.dist-info/licenses/NOTICE +50 -0
- quantum_cortex-1.0.0.dist-info/top_level.txt +5 -0
- train.py +885 -0
cortex_c2b/read_path.py
ADDED
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
"""cortex_c2b.read_path — the journal as path 4 of the RES-18 router (ADR-007 D3), Slice C.
|
|
2
|
+
|
|
3
|
+
No new router is built. The versioned `Router` contract already iterates over
|
|
4
|
+
`range(1, len(scores))` with the control at index 0 as the floor — so adding a
|
|
5
|
+
path is adding an index, never touching the floor logic. Retro-compatibility in
|
|
6
|
+
action: PATH_JOURNAL = 3 joins control / hopfield / delta.
|
|
7
|
+
|
|
8
|
+
- The floor guarantee holds for the journal exactly as for the memories: the
|
|
9
|
+
journal path is used on a span only where its (predicted or known) score
|
|
10
|
+
beats the control's; else the control. C2b can never degrade the model.
|
|
11
|
+
- The read is sub-linear (ADR-003 D4 read law): `CueIndex` is an ANN over
|
|
12
|
+
cues — a small from-scratch LSH (random hyperplanes → bucket signatures),
|
|
13
|
+
so a query touches buckets, never the whole journal. HNSW-class libraries
|
|
14
|
+
may replace it later (license-checked); the contract (never scan) holds.
|
|
15
|
+
- `JournalPath.score(span_cue)` is the journal's per-span score: a retrieval
|
|
16
|
+
confidence (best cosine among candidates), so the router can compare it to
|
|
17
|
+
the control on the same footing as the memory paths.
|
|
18
|
+
|
|
19
|
+
Written from scratch (our filon).
|
|
20
|
+
"""
|
|
21
|
+
from __future__ import annotations
|
|
22
|
+
|
|
23
|
+
from collections import deque
|
|
24
|
+
|
|
25
|
+
import numpy as np
|
|
26
|
+
|
|
27
|
+
from cortex_c2 import PATH_NAMES, PATH_CONTROL
|
|
28
|
+
from cortex_c2b import Journal, Entry, STATE_EVICTED, CUE_DIM
|
|
29
|
+
|
|
30
|
+
# ---- the fourth path, registered into the router's vocabulary ------------- #
|
|
31
|
+
PATH_JOURNAL = 3
|
|
32
|
+
if PATH_JOURNAL not in PATH_NAMES:
|
|
33
|
+
PATH_NAMES[PATH_JOURNAL] = "journal" # extend, never break (retro-compat)
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
# ---------------------------------------------------------------------------- #
|
|
37
|
+
# ANN over cues: random-hyperplane LSH (sub-linear; touches buckets, not all) #
|
|
38
|
+
# ---------------------------------------------------------------------------- #
|
|
39
|
+
FLY_EXPAND = 1024 # expanded units against CUE_DIM inputs (the fly: 2000 Kenyon cells for 50 projection neurons)
|
|
40
|
+
FLY_SAMPLE = 6 # inputs each expanded unit samples (the fly: about 6)
|
|
41
|
+
FLY_SPARSITY = 0.05 # the winner take all keeps this fraction (the fly's APL silences all but about 5 %)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def fly_planes(dim: int = CUE_DIM, expand: int = FLY_EXPAND, sample: int = FLY_SAMPLE, seed: int = 0) -> np.ndarray:
|
|
45
|
+
"""The fixed sparse binary projection of the mushroom body (Dasgupta, Stevens and
|
|
46
|
+
Navlakha, Science 2017): each expanded unit sums a few inputs, drawn once and never
|
|
47
|
+
learned. Returned as an (expand, dim) matrix of zeros and ones."""
|
|
48
|
+
rng = np.random.default_rng(seed)
|
|
49
|
+
P = np.zeros((expand, dim), dtype=np.float32)
|
|
50
|
+
for i in range(expand):
|
|
51
|
+
P[i, rng.choice(dim, size=min(sample, dim), replace=False)] = 1.0
|
|
52
|
+
return P
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def fly_tag(cue, planes: np.ndarray, sparsity: float = FLY_SPARSITY) -> np.ndarray:
|
|
56
|
+
"""Expand, then sparsify: the indices of the highest firing fraction of the expanded
|
|
57
|
+
units, as a sorted array. Two cues are compared by how many indices they share."""
|
|
58
|
+
y = planes @ np.asarray(cue, dtype=np.float32)
|
|
59
|
+
keep = max(1, int(round(sparsity * y.shape[0])))
|
|
60
|
+
idx = np.argpartition(-y, keep - 1)[:keep]
|
|
61
|
+
return np.sort(idx)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class CueIndex:
|
|
65
|
+
"""Locality-sensitive hashing over cues. `n_bits` hyperplanes → a signature;
|
|
66
|
+
`n_tables` independent tables raise recall. A query probes n_tables buckets
|
|
67
|
+
and ranks the candidates by exact cosine — never the whole store."""
|
|
68
|
+
|
|
69
|
+
def __init__(self, dim: int = CUE_DIM, n_bits: int = 10, n_tables: int = 4, seed: int = 0,
|
|
70
|
+
rank: str = "cosine"):
|
|
71
|
+
# `rank`: "cosine" (the dense cue, every run to date) or "fly" (the expand and
|
|
72
|
+
# sparsify tag of ADR-003 line 6, declared 2026-09-19, probe only); the candidate
|
|
73
|
+
# buckets are the same in both, only the ranking of the candidates changes.
|
|
74
|
+
if rank not in ("cosine", "fly"):
|
|
75
|
+
raise ValueError("rank is cosine or fly")
|
|
76
|
+
self.rank = rank
|
|
77
|
+
self.fly_planes = fly_planes(dim, seed=seed) if rank == "fly" else None
|
|
78
|
+
self.tags: dict[str, np.ndarray] = {}
|
|
79
|
+
rng = np.random.default_rng(seed)
|
|
80
|
+
self.planes = [rng.standard_normal((dim, n_bits)).astype(np.float32) for _ in range(n_tables)]
|
|
81
|
+
self.tables: list[dict[int, list[str]]] = [dict() for _ in range(n_tables)]
|
|
82
|
+
self.vecs: dict[str, np.ndarray] = {}
|
|
83
|
+
self.touched = 0 # instrumentation for the no-scan test
|
|
84
|
+
self.alias: dict[str, str] = {} # Slice E: source id -> summary id
|
|
85
|
+
self.touch_log: deque = deque(maxlen=256) # Slice E: entries touched per query (latency proxy)
|
|
86
|
+
|
|
87
|
+
def _sig(self, v: np.ndarray, t: int) -> int:
|
|
88
|
+
bits = (v @ self.planes[t]) > 0
|
|
89
|
+
return int(np.packbits(bits.astype(np.uint8)).view(np.uint8).astype(np.int64).sum()
|
|
90
|
+
+ bits.astype(np.int64) @ (1 << np.arange(len(bits))))
|
|
91
|
+
|
|
92
|
+
def add(self, entry_id: str, cue: np.ndarray):
|
|
93
|
+
v = np.asarray(cue, dtype=np.float32)
|
|
94
|
+
self.vecs[entry_id] = v
|
|
95
|
+
if self.rank == "fly":
|
|
96
|
+
self.tags[entry_id] = fly_tag(v, self.fly_planes)
|
|
97
|
+
for t in range(len(self.planes)):
|
|
98
|
+
self.tables[t].setdefault(self._sig(v, t), []).append(entry_id)
|
|
99
|
+
|
|
100
|
+
def query(self, cue: np.ndarray, k: int = 5) -> list[tuple[str, float]]:
|
|
101
|
+
q = np.asarray(cue, dtype=np.float32)
|
|
102
|
+
cand: set[str] = set()
|
|
103
|
+
for t in range(len(self.planes)):
|
|
104
|
+
cand.update(self.tables[t].get(self._sig(q, t), []))
|
|
105
|
+
self.touched += len(cand)
|
|
106
|
+
self.touch_log.append(len(cand))
|
|
107
|
+
if not cand:
|
|
108
|
+
return []
|
|
109
|
+
if self.rank == "fly": # shared indices of the two sparse tags, in [0, 1]
|
|
110
|
+
qt = fly_tag(q, self.fly_planes); n_keep = float(qt.size)
|
|
111
|
+
scored = [(eid, float(np.intersect1d(qt, self.tags[eid], assume_unique=True).size) / n_keep) for eid in cand]
|
|
112
|
+
scored.sort(key=lambda x: -x[1])
|
|
113
|
+
return scored[:k]
|
|
114
|
+
qn = np.linalg.norm(q) + 1e-8
|
|
115
|
+
scored = []
|
|
116
|
+
for eid in cand:
|
|
117
|
+
v = self.vecs[eid]
|
|
118
|
+
scored.append((eid, float(q @ v / (qn * (np.linalg.norm(v) + 1e-8)))))
|
|
119
|
+
scored.sort(key=lambda x: -x[1])
|
|
120
|
+
return scored[:k]
|
|
121
|
+
|
|
122
|
+
def __len__(self):
|
|
123
|
+
return len(self.vecs)
|
|
124
|
+
|
|
125
|
+
def candidates(self, cue: np.ndarray) -> list[str]:
|
|
126
|
+
"""The keys sharing a bucket with `cue` in any table, sorted -- the
|
|
127
|
+
neighbourhood the Slice E scheduler groups within. Not a read: it is
|
|
128
|
+
not counted in the latency telemetry."""
|
|
129
|
+
q = np.asarray(cue, dtype=np.float32)
|
|
130
|
+
cand: set[str] = set()
|
|
131
|
+
for t in range(len(self.planes)):
|
|
132
|
+
cand.update(self.tables[t].get(self._sig(q, t), []))
|
|
133
|
+
return sorted(cand)
|
|
134
|
+
|
|
135
|
+
# ---- Slice E: demotion aliases and the latency proxy ----------------------
|
|
136
|
+
def set_alias(self, source_id: str, summary_id: str) -> None:
|
|
137
|
+
"""After a demotion the summary is reachable from every source cue: the
|
|
138
|
+
source stays indexed under its own cue, and a hit on it resolves to the
|
|
139
|
+
summary (ADR-007 D4, keeping pointers). No second vector is stored."""
|
|
140
|
+
self.alias[source_id] = summary_id
|
|
141
|
+
|
|
142
|
+
def p95_touched(self) -> float:
|
|
143
|
+
"""95th percentile of entries touched per query over the recent window."""
|
|
144
|
+
if not self.touch_log:
|
|
145
|
+
return 0.0
|
|
146
|
+
return float(np.percentile(np.asarray(self.touch_log, dtype=np.float64), 95))
|
|
147
|
+
|
|
148
|
+
|
|
149
|
+
# ---------------------------------------------------------------------------- #
|
|
150
|
+
# The journal path: score + retrieve, on the router's footing #
|
|
151
|
+
# ---------------------------------------------------------------------------- #
|
|
152
|
+
class JournalPath:
|
|
153
|
+
"""Exposes the journal to the router as path PATH_JOURNAL.
|
|
154
|
+
|
|
155
|
+
score(span_cue) -> the journal's confidence that it holds something for
|
|
156
|
+
this span: best cosine among ANN candidates (0 if empty).
|
|
157
|
+
retrieve(span_cue)-> the entries behind that score (non-evicted), with payloads.
|
|
158
|
+
"""
|
|
159
|
+
|
|
160
|
+
def __init__(self, journal: Journal, index: CueIndex | None = None, seed: int = 0):
|
|
161
|
+
"""`seed` is the declared seed of the cue index (Decision 8: the index is
|
|
162
|
+
recomputed at every open from the log; the seed is part of the scope's
|
|
163
|
+
configuration, so two opens build the same buckets)."""
|
|
164
|
+
self.j = journal
|
|
165
|
+
self.index = index or CueIndex(dim=journal.cue_dim, seed=seed)
|
|
166
|
+
# index everything already in the journal (demoted sources included: a hit on
|
|
167
|
+
# them resolves to their summary through the alias)
|
|
168
|
+
for eid, e in journal._entries.items():
|
|
169
|
+
if e.state != STATE_EVICTED or eid in journal.summary_of:
|
|
170
|
+
self.index.add(eid, np.asarray(e.cue, dtype=np.float32))
|
|
171
|
+
for src, sid in journal.summary_of.items():
|
|
172
|
+
self.index.set_alias(src, sid)
|
|
173
|
+
|
|
174
|
+
def on_write(self, entry: Entry):
|
|
175
|
+
"""Keep the index in step with the journal (call after each admitted write)."""
|
|
176
|
+
self.index.add(entry.entry_id, np.asarray(entry.cue, dtype=np.float32))
|
|
177
|
+
|
|
178
|
+
def _resolve(self, key: str) -> tuple[Entry, str]:
|
|
179
|
+
"""A hit is an entry id or a demoted source id. Returns the effective entry
|
|
180
|
+
(the summary, for a source) and the pointer of the payload to return (the
|
|
181
|
+
source's own payload, alive as long as its summary is)."""
|
|
182
|
+
hit = self.j.get(key)
|
|
183
|
+
effective, target, hops = hit, self.index.alias.get(key), 0
|
|
184
|
+
while target is not None and hops < 64: # up the hierarchy (RES-16), no cycle by construction
|
|
185
|
+
effective, target, hops = self.j.get(target), self.index.alias.get(target), hops + 1
|
|
186
|
+
return effective, hit.pointer
|
|
187
|
+
|
|
188
|
+
def score(self, span_cue) -> float:
|
|
189
|
+
hits = self.index.query(np.asarray(span_cue, dtype=np.float32), k=1)
|
|
190
|
+
hits = [(eid, s) for eid, s in hits if self._resolve(eid)[0].state != STATE_EVICTED]
|
|
191
|
+
return float(max(0.0, hits[0][1])) if hits else 0.0
|
|
192
|
+
|
|
193
|
+
def cue_similarity(self, span_cue, pointer: str) -> float:
|
|
194
|
+
"""The organ's own evidence for one line (RES-23, the familiarity mark): the
|
|
195
|
+
cosine between a query's cue and the stored cue of the alive entry whose
|
|
196
|
+
payload is `pointer`, computed exactly as the index scores a retrieval;
|
|
197
|
+
0.0 when no alive entry holds that pointer. Never a value the harness supplies."""
|
|
198
|
+
q = np.asarray(span_cue, dtype=np.float32)
|
|
199
|
+
qn = float(np.linalg.norm(q)) + 1e-8
|
|
200
|
+
for eid, e in self.j._entries.items():
|
|
201
|
+
if e.pointer == pointer and e.state != STATE_EVICTED and eid in self.index.vecs:
|
|
202
|
+
v = self.index.vecs[eid]
|
|
203
|
+
return float(q @ v / (qn * (float(np.linalg.norm(v)) + 1e-8)))
|
|
204
|
+
return 0.0
|
|
205
|
+
|
|
206
|
+
def retrieve(self, span_cue, k: int = 3) -> list[tuple[Entry, bytes, float]]:
|
|
207
|
+
out = []
|
|
208
|
+
for eid, s in self.index.query(np.asarray(span_cue, dtype=np.float32), k=k):
|
|
209
|
+
e, pointer = self._resolve(eid)
|
|
210
|
+
if e.state != STATE_EVICTED:
|
|
211
|
+
out.append((e, self.j.payloads.get(pointer), s))
|
|
212
|
+
return out
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
def path_scores_with_journal(control: float, hopfield: float, delta: float,
|
|
216
|
+
journal: float) -> list[float]:
|
|
217
|
+
"""Assemble the 4-path score vector in router order (control first — the floor)."""
|
|
218
|
+
return [control, hopfield, delta, journal]
|