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.
Files changed (41) hide show
  1. cortex_c2/__init__.py +161 -0
  2. cortex_c2/consolidation.py +146 -0
  3. cortex_c2/router_ablation.py +254 -0
  4. cortex_c2/router_v2.py +109 -0
  5. cortex_c2b/__init__.py +724 -0
  6. cortex_c2b/companion_demo.py +203 -0
  7. cortex_c2b/crypto.py +102 -0
  8. cortex_c2b/external_arm.py +591 -0
  9. cortex_c2b/hm_lm.py +983 -0
  10. cortex_c2b/hm_protocol.py +308 -0
  11. cortex_c2b/lifecycle.py +454 -0
  12. cortex_c2b/lm_bridge.py +338 -0
  13. cortex_c2b/organ_use.py +254 -0
  14. cortex_c2b/read_path.py +218 -0
  15. cortex_c2b/reconstruction_probe.py +471 -0
  16. cortex_c2b/write_path.py +176 -0
  17. cortex_data/__init__.py +439 -0
  18. cortex_data/relay.py +174 -0
  19. cortex_eval/__init__.py +20 -0
  20. cortex_eval/ablation_mqar.py +187 -0
  21. cortex_eval/confirmation_run.py +133 -0
  22. cortex_eval/domain_map.py +127 -0
  23. cortex_eval/external_baseline.py +148 -0
  24. cortex_eval/ingest_confirmation.py +194 -0
  25. cortex_eval/mqar.py +122 -0
  26. cortex_eval/multiseed.py +148 -0
  27. cortex_eval/recurrent_base.py +197 -0
  28. cortex_eval/recurrent_ladder.py +159 -0
  29. cortex_eval/resumable_ladder.py +231 -0
  30. cortex_eval/resumable_rb.py +207 -0
  31. cortex_eval/run_mqar.py +127 -0
  32. cortex_eval/serial_position.py +116 -0
  33. quantum_cortex-1.0.0.dist-info/METADATA +209 -0
  34. quantum_cortex-1.0.0.dist-info/RECORD +41 -0
  35. quantum_cortex-1.0.0.dist-info/WHEEL +5 -0
  36. quantum_cortex-1.0.0.dist-info/licenses/AUTHORS.md +30 -0
  37. quantum_cortex-1.0.0.dist-info/licenses/LICENSE +73 -0
  38. quantum_cortex-1.0.0.dist-info/licenses/LICENSE-DOCS.md +34 -0
  39. quantum_cortex-1.0.0.dist-info/licenses/NOTICE +50 -0
  40. quantum_cortex-1.0.0.dist-info/top_level.txt +5 -0
  41. train.py +885 -0
@@ -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]