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,591 @@
1
+ """cortex_c2b.external_arm -- the Molaison LM arm on an EXTERNAL model through a
2
+ prompt adapter (ADR-008, amendment of 2026-09-16, declared before any measurement).
3
+
4
+ The Molaison dissociation (the H.M. protocol: named after Henry Molaison, the
5
+ patient whose hippocampus was removed in 1953 and who formed no new episodic
6
+ memory from that day while keeping every skill he had; the protocol asks the
7
+ model the same question: with its journal cut, do the episodes vanish while the
8
+ skills stay?) is run here on a model that was never trained with the organ: an
9
+ open weight causal language model people use, at three sizes, through a FROZEN
10
+ prompt whose hash enters every record. Door 2 of whitepaper section 5d.
11
+
12
+ What does not change (the frozen protocol): the generator and its seeds (0 and
13
+ 10 000), the organ (session A plants through the gated write path into a journal
14
+ sealed on disk; session B reopens it from the disk alone in a NEW process), the
15
+ read window (k = 4 lines, 256 bytes, `A:<payload>` lines in a seeded random
16
+ order, labels A to H), the contract (cite a label and give the attribute, or
17
+ abstain), the counting rules of `hm_lm._probe`, the thresholds (d 0.50, e_S 1 %,
18
+ floor chance + 5 points, invalid citations at most 1 %, claims on never planted
19
+ entities under 10 %), the verdict, `persistent` and `claimable`.
20
+
21
+ What changes, declared (the amendment says why):
22
+ 1. The cue is the organ's own hash seeded encoder (the entity as the address,
23
+ rows 13 and 13b): the retrieval hit is the organ's number, not the model's.
24
+ 2. The window enters as TEXT in a frozen frame (rules, four worked examples
25
+ from the training facts family, the notebook lines verbatim, the question);
26
+ the frame's sha256 (16 hex) is the prompt hash; the same frame for every
27
+ size; a change is a new hash and a new row.
28
+ 3. The contract's surface is text: one line, `UNKNOWN` or
29
+ `CITE <label> ANS <attribute>`; the adapter maps it onto the contract's
30
+ tokens and hands them to `parse_contract` and to the same counting rules;
31
+ any other shape is malformed, a claim counted against the model.
32
+ 4. Greedy decoding, at most 32 new tokens, the published weights in their
33
+ published dtype (bf16), no quantisation, no training, no adapter weights;
34
+ the model's name, revision, dtype, template and hardware in the file.
35
+ 5. Arm S: 256 spans of 1024 bytes from the validation part of the N1 slice;
36
+ OFF the span alone, ON the notebook block the organ returns for the span's
37
+ own cue followed by the span; the loss on the span's tokens after its
38
+ first one, in both arms; `hm_skill_delta_frame` (the frame's fixed text and
39
+ an empty notebook before the span) as attribution, not as a condition.
40
+ 6. Session B in a new process; a second execution must agree number for
41
+ number (`--agree`); both files retained.
42
+
43
+ Nothing here is a claim. The first number comes from the first file.
44
+ """
45
+ from __future__ import annotations
46
+
47
+ import argparse
48
+ import hashlib
49
+ import json
50
+ import os
51
+ import platform
52
+ import re
53
+ import sys
54
+ from datetime import datetime, timezone
55
+ from pathlib import Path
56
+
57
+ import numpy as np
58
+
59
+ from cortex_c2b import Journal, MODE_DURABLE, POLICY_STOP, lifecycle_declaration, content_hash
60
+ from cortex_c2b.hm_protocol import (Fact, generate_facts, _cue_for, _TEMPLATES, _ATTRS, SCHEMAS, DELTA, EPSILON_S,
61
+ FLOOR_MARGIN, NEGCTRL_INVALID, N_FACTS, N_NEGCTRL)
62
+ from cortex_c2b.lm_bridge import (build_read_window, parse_contract, LABELS, NEWLINE, TOK_CITE, TOK_ANS,
63
+ TOK_UNKNOWN)
64
+ from cortex_c2b.hm_lm import INVALID_CITATION_MAX
65
+ from cortex_c2b.read_path import JournalPath
66
+ from cortex_c2b.write_path import WritePath
67
+
68
+ REPO_ROOT = Path(__file__).resolve().parent.parent
69
+
70
+ # ---- declared constants (the amendment of 2026-09-16) ------------------------
71
+ K = 4 # lines in a read window, as the cortex's arm
72
+ BUDGET_BYTES = 256 # the window's byte budget, as the cortex's arm
73
+ MAX_NEW_TOKENS = 32 # greedy decoding, stopped at the first newline
74
+ N_SPANS = 256 # arm S: 16 batches of 16 spans, as the trainer draws them
75
+ SPAN_BYTES = 1024 # arm S: the trainer's block size
76
+ VAL_FRACTION = 0.01 # the N1 validation split (train.py, `val_fraction`)
77
+ EXAMPLES_SEED = 100_000 # the training facts family (organ_use), disjoint from the protocol's seeds
78
+ EXAMPLES_SHUFFLE_SEED = 100_002
79
+ N_EXAMPLES = 4 # two citations, two abstentions, in a fixed order
80
+ FRAME_DIR = REPO_ROOT / "docs" / "benchmarks"
81
+ DATA_BIN = REPO_ROOT / "data" / "fineweb_edu_bytes.bin"
82
+ PROVENANCE = REPO_ROOT / "data" / "mixes" / "fineweb-edu-n1.provenance.json"
83
+ NOTEBOOK_HEADER = "Notebook:\n"
84
+
85
+ # the three sizes pinned in the addendum of 2026-09-17 (ids and revisions verified on the hub that day)
86
+ PINNED_MODELS = {
87
+ "Qwen/Qwen3-1.7B": "70d244cc86ccca08cf5af4e1e306ecf908b1ad5e",
88
+ "Qwen/Qwen3-4B": "1cfa9a7208912126459214e8b04321603b3df60c",
89
+ "Qwen/Qwen3-8B": "b968826d9c46dd6066d109eabc6255188de91218",
90
+ }
91
+
92
+ _CITE_RE = re.compile(r"^CITE ([A-H]) ANS (.+)$")
93
+ _ENTITY_RE = re.compile(r"[A-Z][a-z]{3}-[0-9a-f]{4}")
94
+
95
+
96
+ # ---------------------------------------------------------------------------- #
97
+ # The frame: written once, retained as a file, its hash in every record #
98
+ # ---------------------------------------------------------------------------- #
99
+ RULES = (
100
+ "You are given a notebook of records. Each record is one line that starts with a letter label and a colon.\n"
101
+ "Answer the question from the notebook only.\n"
102
+ "If one record is about the entity named in the question, answer with exactly one line of this form:\n"
103
+ "CITE <label> ANS <attribute>\n"
104
+ "where <label> is that record's letter and <attribute> is copied word for word from that record.\n"
105
+ "If no record is about that entity, answer with exactly one line:\n"
106
+ "UNKNOWN\n"
107
+ "Write nothing else. Never guess.\n"
108
+ )
109
+
110
+
111
+ def _example_items(facts: list[Fact], idx: list[int]) -> list[tuple[str, bytes]]:
112
+ return [(content_hash(facts[i].statement.encode("utf-8")), facts[i].statement.encode("utf-8")) for i in idx]
113
+
114
+
115
+ def worked_examples() -> list[dict]:
116
+ """Four examples from the training facts family (`organ_use.training_facts`,
117
+ seed 100 000, decontaminated against the protocol's entities): two
118
+ citations and two abstentions, in a fixed order; their notebooks are built
119
+ by `build_read_window`, the protocol's own window builder."""
120
+ from cortex_c2b.organ_use import training_facts
121
+ facts, negs, _ = training_facts(40, seed=EXAMPLES_SEED)
122
+ rng = np.random.default_rng(EXAMPLES_SHUFFLE_SEED)
123
+ per = len(facts) // len(SCHEMAS)
124
+ plan = [("cite", facts[0], [0, 1, per, 2 * per]), # a person, among three others
125
+ ("abstain", negs[0], [3, per + 1, 2 * per + 1, 3 * per]), # never planted: four others
126
+ ("cite", facts[2 * per], [2 * per, 3 * per + 1, 4 * per, 4]), # an event, among three others
127
+ ("abstain", negs[len(negs) // 5], [5, per + 2, 3 * per + 2, 4 * per + 1])] # a place, another schema
128
+ out = []
129
+ for kind, f, idx in plan:
130
+ window, labels = build_read_window(_example_items(facts, idx), BUDGET_BYTES, rng)
131
+ if kind == "cite":
132
+ own = content_hash(f.statement.encode("utf-8"))
133
+ label = next(chr(l) for l, p in labels.items() if p == own)
134
+ answer = f"CITE {label} ANS {f.attr}"
135
+ else:
136
+ answer = "UNKNOWN"
137
+ out.append({"window": window.decode("utf-8"), "question": f.query, "answer": answer, "kind": kind})
138
+ return out
139
+
140
+
141
+ def build_frame() -> str:
142
+ """The frozen frame with two placeholders, `{window}` and `{question}`."""
143
+ parts = [RULES, "\n"]
144
+ for i, ex in enumerate(worked_examples(), 1):
145
+ parts.append(f"Example {i}\n{NOTEBOOK_HEADER}{ex['window']}Question: {ex['question']}\nAnswer: {ex['answer']}\n\n")
146
+ parts.append(f"{NOTEBOOK_HEADER}{{window}}Question: {{question}}\nAnswer:")
147
+ return "".join(parts)
148
+
149
+
150
+ def frame_hash(frame: str) -> str:
151
+ return hashlib.sha256(frame.encode("utf-8")).hexdigest()[:16]
152
+
153
+
154
+ def frame_path(frame: str) -> Path:
155
+ return FRAME_DIR / f"external-arm-prompt-{frame_hash(frame)}.txt"
156
+
157
+
158
+ def render(frame: str, window: bytes, question: str) -> str:
159
+ """The window's lines verbatim under the header (an empty window leaves the
160
+ block empty), then the question. `replace`, never `format`: the notebook may
161
+ hold any byte."""
162
+ return frame.replace("{window}", window.decode("utf-8", errors="replace")).replace("{question}", question)
163
+
164
+
165
+ def frame_prefix(frame: str) -> str:
166
+ """The frame's fixed text up to the empty notebook block (arm S diagnostic)."""
167
+ return frame[: frame.index("{window}")]
168
+
169
+
170
+ # ---------------------------------------------------------------------------- #
171
+ # The contract's surface: text in, the contract's tokens out, the same parser #
172
+ # ---------------------------------------------------------------------------- #
173
+ def first_line(completion: str) -> str:
174
+ """The completion's first line after leading whitespace is dropped, its
175
+ outer whitespace stripped and nothing else (no case folding)."""
176
+ return completion.lstrip().split("\n", 1)[0].strip()
177
+
178
+
179
+ def contract_tokens(completion: str) -> list[int]:
180
+ """Map the model's line onto the contract's tokens: `UNKNOWN` -> [<UNKNOWN>,
181
+ newline]; `CITE <label> ANS <attribute>` -> [<CITE>, label, <ANS>, attribute
182
+ bytes, newline]; anything else -> its own bytes, which `parse_contract`
183
+ reads as malformed (a claim counted against the model)."""
184
+ line = first_line(completion)
185
+ if line == "UNKNOWN":
186
+ return [TOK_UNKNOWN, NEWLINE]
187
+ m = _CITE_RE.match(line)
188
+ if m:
189
+ return [TOK_CITE, ord(m.group(1)), TOK_ANS] + list(m.group(2).encode("utf-8")) + [NEWLINE]
190
+ return list(line.encode("utf-8")) + [NEWLINE]
191
+
192
+
193
+ def score(kinds: list[tuple[str, int | None, str | None]], facts: list[Fact], labels_of: list[dict[int, str]],
194
+ pointers_of: list[list[str]], payloads, pointer_of: dict[str, str], journal_on: bool) -> dict:
195
+ """The counting rules of `hm_lm._probe`, line for line, on parsed answers.
196
+ `labels_of[i]`: label byte -> pointer of the i th window (empty when OFF);
197
+ `pointers_of[i]`: what the organ returned; `pointer_of`: entity -> pointer
198
+ for planted facts (empty for the negative control)."""
199
+ n = max(1, len(facts))
200
+ strict = abstain = invalid = valid = attr_ok = guess = attr_by_chance = hit = malformed = 0
201
+ for f, (kind, label, attr), labels, pointers in zip(facts, kinds, labels_of, pointers_of):
202
+ if journal_on and pointer_of:
203
+ hit += int(pointer_of.get(f.entity) in pointers)
204
+ if kind == "unknown":
205
+ abstain += 1
206
+ continue
207
+ guess += 1
208
+ if kind == "malformed":
209
+ malformed += 1
210
+ if kind == "cite" and label in labels:
211
+ payload = payloads.get(labels[label]) if labels[label] in payloads else b""
212
+ cited_ok = (attr or "").encode("utf-8") in payload and len(attr or "") > 0
213
+ if cited_ok:
214
+ valid += 1
215
+ if attr == f.attr:
216
+ strict += 1; attr_ok += 1
217
+ else:
218
+ invalid += 1 # a label that exists but does not hold the claim
219
+ else:
220
+ invalid += 1 # a citation of nothing, or a malformed claim
221
+ if attr == f.attr:
222
+ attr_by_chance += 1
223
+ return {"recall_strict": strict / n, "abstain_rate": abstain / n, "guess_rate": guess / n,
224
+ "invalid_citation_rate": invalid / n, "valid_citation_rate": valid / n,
225
+ "attr_exact_given_valid": (attr_ok / valid) if valid else None,
226
+ "attr_hit_any": attr_by_chance / n, "malformed_rate": malformed / n,
227
+ "retrieval_hit": (hit / n) if (journal_on and pointer_of) else None}
228
+
229
+
230
+ def _progress(msg: str) -> None:
231
+ """One dated line on stderr; the log keeps it, the file does not."""
232
+ print(f"[{datetime.now(timezone.utc).strftime('%H:%M:%S')}] {msg}", file=sys.stderr, flush=True)
233
+
234
+
235
+ # ---------------------------------------------------------------------------- #
236
+ # The organ: session A plants (no model), session B reopens in a new process #
237
+ # ---------------------------------------------------------------------------- #
238
+ def _key():
239
+ from cortex_c2b.crypto import key_from_env
240
+ k = key_from_env()
241
+ if k is None:
242
+ raise SystemExit("QUANTUM_CORTEX_JOURNAL_KEY is not set: the journal is sealed with it (32 bytes hex)")
243
+ return k
244
+
245
+
246
+ def journal_file(journal_dir: Path) -> Path:
247
+ return Path(journal_dir) / "journal.jsonl"
248
+
249
+
250
+ def plant(journal_dir: Path, n_facts: int = N_FACTS, seed: int = 0, key: bytes | None = None) -> dict:
251
+ """Session A: the frozen protocol's facts through the NORMAL gated write
252
+ path, cues from the organ's own encoder (the entity as the address), into a
253
+ journal sealed on disk under the `stop` policy. Writes a sidecar with what
254
+ session B needs to declare persistence."""
255
+ journal_dir = Path(journal_dir); journal_dir.mkdir(parents=True, exist_ok=True)
256
+ key = key or _key()
257
+ facts, gen_hash = generate_facts(n_facts, seed)
258
+ journal = Journal(journal_file(journal_dir), key=key, policy=POLICY_STOP)
259
+ wp = WritePath(journal, seed=seed)
260
+ jp = JournalPath(journal, seed=seed)
261
+ admitted = 0
262
+ for i, f in enumerate(facts):
263
+ rep = wp.write(f.cue, f.statement.encode("utf-8"), f.schema, now=float(i))
264
+ if rep.admitted:
265
+ jp.on_write(rep.entry); admitted += 1
266
+ side = {"session": "A", "generator": {"config_hash": gen_hash, "n_facts": n_facts, "seed": seed},
267
+ "admitted": admitted, "durable": journal.mode == MODE_DURABLE,
268
+ "storage": lifecycle_declaration(journal)["storage"], "timestamp_utc": datetime.now(timezone.utc).isoformat()}
269
+ (journal_dir / "session-a.json").write_text(json.dumps(side, indent=2) + "\n", encoding="utf-8", newline="\n")
270
+ return side
271
+
272
+
273
+ def load_val_spans(bin_path: Path = DATA_BIN, n_spans: int = N_SPANS, span_bytes: int = SPAN_BYTES, seed: int = 0,
274
+ val_fraction: float = VAL_FRACTION) -> tuple[list[str], dict]:
275
+ """Arm S text: spans of the validation part of the N1 slice (uint16 byte
276
+ tokens; the reserved ids 256 to 263 are dropped), decoded as UTF-8 with
277
+ undecodable bytes replaced, drawn with a fixed seed, the same for every model."""
278
+ arr = np.memmap(bin_path, dtype=np.uint16, mode="r")
279
+ split = int(len(arr) * (1.0 - val_fraction))
280
+ val = arr[split:]
281
+ rng = np.random.default_rng(seed)
282
+ starts = rng.integers(0, len(val) - span_bytes, size=n_spans)
283
+ spans = []
284
+ for s in starts:
285
+ raw = np.asarray(val[int(s): int(s) + span_bytes])
286
+ spans.append(bytes(int(v) for v in raw if v < 256).decode("utf-8", errors="replace"))
287
+ prov = None
288
+ if PROVENANCE.exists():
289
+ try:
290
+ prov = json.loads(PROVENANCE.read_text(encoding="utf-8")).get("content_sha")
291
+ except (OSError, ValueError):
292
+ prov = None
293
+ return spans, {"path": str(bin_path), "n_bytes": int(len(arr)), "val_fraction": val_fraction, "n_spans": n_spans,
294
+ "span_bytes": span_bytes, "seed": seed, "provenance_content_sha": prov}
295
+
296
+
297
+ def _arm(model, frame: str, jp: JournalPath | None, facts: list[Fact], rng, payloads, pointer_of: dict[str, str],
298
+ journal_on: bool) -> tuple[dict, list[dict]]:
299
+ """One arm over a fact list: the organ reads (or not), the frame is rendered,
300
+ the model completes, the line is mapped and parsed, the answers are kept."""
301
+ kinds, labels_of, pointers_of, transcript = [], [], [], []
302
+ arm_name = ("on" if journal_on else "off") + ("" if pointer_of else " (never planted)")
303
+ for i, f in enumerate(facts):
304
+ if i % 50 == 0:
305
+ _progress(f"arm E {arm_name}: {i}/{len(facts)}")
306
+ if journal_on:
307
+ hits = jp.retrieve(f.cue, k=K)
308
+ items = [(e.pointer, payload) for e, payload, _ in hits]
309
+ window, labels = build_read_window(items, BUDGET_BYTES, rng)
310
+ pointers = [p for p, _ in items]
311
+ else:
312
+ window, labels, pointers = b"", {}, []
313
+ completion = model.complete(render(frame, window, f.query))
314
+ toks = contract_tokens(completion)
315
+ kind, label, attr = parse_contract(toks)
316
+ kinds.append((kind, label, attr)); labels_of.append(labels); pointers_of.append(pointers)
317
+ transcript.append({"entity": f.entity, "schema": f.schema, "attr": f.attr, "line": first_line(completion),
318
+ "kind": kind, "label": (chr(label) if label else None), "answered": attr,
319
+ "own_in_window": (pointer_of.get(f.entity) in pointers) if pointer_of else None})
320
+ return score(kinds, facts, labels_of, pointers_of, payloads, pointer_of, journal_on), transcript
321
+
322
+
323
+ def skill_arm(model, frame: str, jp: JournalPath, spans: list[str], rng) -> dict:
324
+ """Arm S. OFF: the span alone. ON: the notebook block the organ returns for
325
+ the span's own cue, then the span. Diagnostic: the frame's fixed text and an
326
+ empty notebook, then the span. The loss on the span's tokens after its
327
+ first one, in every arm; perplexity = exp(total nll / total tokens)."""
328
+ tot = {"off": [0.0, 0], "on": [0.0, 0], "frame": [0.0, 0]}
329
+ pre_frame = frame_prefix(frame) # ends with the notebook header, the block empty
330
+ for i, text in enumerate(spans):
331
+ if i % 64 == 0:
332
+ _progress(f"arm S: {i}/{len(spans)} spans")
333
+ hits = jp.retrieve(_cue_for(text), k=K)
334
+ window, _ = build_read_window([(e.pointer, p) for e, p, _ in hits], BUDGET_BYTES, rng)
335
+ block = NOTEBOOK_HEADER + window.decode("utf-8", errors="replace")
336
+ for name, prefix in (("off", ""), ("on", block), ("frame", pre_frame)):
337
+ s, n = model.nll(prefix, text)
338
+ tot[name][0] += float(s); tot[name][1] += int(n)
339
+ ppl = {k: (float(np.exp(v[0] / v[1])) if v[1] else None) for k, v in tot.items()}
340
+ delta = max(0.0, (ppl["on"] - ppl["off"]) / ppl["off"]) if ppl["off"] else 0.0
341
+ delta_frame = max(0.0, (ppl["frame"] - ppl["off"]) / ppl["off"]) if ppl["off"] else 0.0
342
+ return {"hm_skill_delta": delta, "ppl_on": ppl["on"], "ppl_off": ppl["off"],
343
+ "hm_skill_delta_frame": delta_frame, "ppl_frame": ppl["frame"], "tokens_scored": tot["off"][1]}
344
+
345
+
346
+ def probe(model, journal_dir: Path, frame: str | None = None, n_facts: int = N_FACTS, n_negctrl: int = N_NEGCTRL,
347
+ seed: int = 0, spans: list[str] | None = None, data_note: dict | None = None,
348
+ key: bytes | None = None) -> dict:
349
+ """Session B: nothing of session A but the disk. Reopens the sealed journal,
350
+ runs the three arms of arm E and arm S, and returns the file's content."""
351
+ journal_dir = Path(journal_dir)
352
+ frame = frame or build_frame()
353
+ key = key or _key()
354
+ side = json.loads((journal_dir / "session-a.json").read_text(encoding="utf-8"))
355
+ journal = Journal(journal_file(journal_dir), key=key, policy=POLICY_STOP)
356
+ jp = JournalPath(journal, seed=seed)
357
+ rng = np.random.default_rng(seed + 1) # the window order, as session B of the cortex's arm
358
+ facts, gen_hash = generate_facts(n_facts, seed)
359
+ neg, _ = generate_facts(n_negctrl, seed + 10_000)
360
+ pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
361
+ if content_hash(f.statement.encode("utf-8")) in journal.payloads}
362
+ chance = float(np.mean([1.0 / len(_ATTRS[s]) for s in SCHEMAS]))
363
+ floor = chance + FLOOR_MARGIN
364
+
365
+ on, t_on = _arm(model, frame, jp, facts, rng, journal.payloads, pointer_of, True)
366
+ off, t_off = _arm(model, frame, None, facts, rng, journal.payloads, pointer_of, False)
367
+ neg_on, t_neg = _arm(model, frame, jp, neg, rng, journal.payloads, {}, True)
368
+ neg_off, _ = _arm(model, frame, None, neg, rng, journal.payloads, {}, False)
369
+ if spans is None:
370
+ spans, data_note = load_val_spans(seed=seed)
371
+ skills = skill_arm(model, frame, jp, spans, rng)
372
+
373
+ storage = lifecycle_declaration(journal)["storage"]
374
+ persistent = bool(side.get("durable") and journal.mode == MODE_DURABLE)
375
+ negctrl_claims = neg_on["guess_rate"]
376
+ valid = negctrl_claims < NEGCTRL_INVALID
377
+ dissociation = (skills["hm_skill_delta"] <= EPSILON_S and (on["recall_strict"] - off["recall_strict"]) >= DELTA
378
+ and off["recall_strict"] <= floor and on["invalid_citation_rate"] <= INVALID_CITATION_MAX and valid)
379
+ desc = model.describe()
380
+ return {
381
+ "benchmark": "hm_protocol_lm_external",
382
+ "spec": ("docs/benchmarks/hm-protocol.md (frozen thresholds; LM arm amendment 2026-09-12); "
383
+ "ADR-008 amendment 2026-09-16 (the external model arm, declared before any measurement)"),
384
+ "timestamp_utc": datetime.now(timezone.utc).isoformat(),
385
+ "model": desc,
386
+ "prompt": {"hash": frame_hash(frame), "file": f"docs/benchmarks/external-arm-prompt-{frame_hash(frame)}.txt",
387
+ "examples_seed": EXAMPLES_SEED, "decoding": "greedy", "max_new_tokens": MAX_NEW_TOKENS},
388
+ "thresholds": {"delta": DELTA, "epsilon_s": EPSILON_S, "floor": floor, "chance": chance,
389
+ "negctrl_invalid": NEGCTRL_INVALID, "invalid_citation_max": INVALID_CITATION_MAX},
390
+ "generator": {"config_hash": gen_hash, "n_facts": n_facts, "n_negctrl": n_negctrl, "seed": seed,
391
+ "admitted": side.get("admitted"), "planted_found": len(pointer_of)},
392
+ "window": {"k": K, "budget_bytes": BUDGET_BYTES, "shuffle_seed": seed + 1,
393
+ "cue": "the organ's hash seeded encoder, the entity as the address (rows 13, 13b)"},
394
+ "hm_recall_on": on["recall_strict"], "hm_recall_off": off["recall_strict"],
395
+ "hm_gap": on["recall_strict"] - off["recall_strict"],
396
+ **skills,
397
+ "hm_negctrl_rate": negctrl_claims, "hm_negctrl_on": neg_on["attr_hit_any"],
398
+ "hm_negctrl_abstain_on": neg_on["abstain_rate"], "hm_negctrl_abstain_off": neg_off["abstain_rate"],
399
+ "hm_false_abstention_on": on["abstain_rate"], "hm_invalid_citation_on": on["invalid_citation_rate"],
400
+ "hm_valid_citation_on": on["valid_citation_rate"], "hm_attr_exact_given_valid": on["attr_exact_given_valid"],
401
+ "hm_retrieval_hit": on["retrieval_hit"], "hm_attention_mass": None,
402
+ "hm_guess_rate_off": off["guess_rate"], "hm_attr_hit_off_by_chance": off["attr_hit_any"],
403
+ "hm_malformed_on": on["malformed_rate"], "hm_malformed_off": off["malformed_rate"],
404
+ "hm_malformed_negctrl": neg_on["malformed_rate"],
405
+ "run_valid": valid, "hm_dissociation_pass": int(dissociation),
406
+ "persistent": persistent, "claimable": int(dissociation and persistent),
407
+ "storage": {"on_disk": True, "mode": storage["mode"], "policy": storage["policy"],
408
+ "plaintext_scope": storage["plaintext_scope"]},
409
+ "attribution": {"encoder": on["retrieval_hit"], "encoder_note": "the organ's number (the entity as the address)",
410
+ "reader": None, "reader_note": "not observable on an external model",
411
+ "answer": on["attr_exact_given_valid"]},
412
+ "skill_arm": {"data": data_note, "n_spans": len(spans)},
413
+ "session_a": side, "process": "new (nothing of session A but the disk)",
414
+ "answers": {"on": t_on, "off": t_off, "negctrl": t_neg},
415
+ "verdict": ((("PASS" if dissociation else ("INVALID" if not valid else "FAIL"))
416
+ + " -- external model arm, cite-or-abstain contract through the frozen prompt")
417
+ + ("" if persistent else " -- persistent: false, not claimable")),
418
+ }
419
+
420
+
421
+ # ---------------------------------------------------------------------------- #
422
+ # The models: a Hugging Face causal LM behind the two calls the arm needs #
423
+ # ---------------------------------------------------------------------------- #
424
+ def model_slug(name: str, template: str) -> str:
425
+ slug = re.sub(r"[^a-z0-9.]+", "-", name.lower()).strip("-")
426
+ return slug + ("-raw" if template == "raw" else "")
427
+
428
+
429
+ class HFCausalModel:
430
+ """`transformers` is imported here and nowhere else: the fast suite never
431
+ needs it. bf16 only, the published dtype; a card without bf16 stops the run
432
+ instead of changing the dtype (a quantised or fp16 run would be a second
433
+ variable)."""
434
+
435
+ def __init__(self, name: str, revision: str, device: str = "cpu", chat: bool = True,
436
+ max_new_tokens: int = MAX_NEW_TOKENS):
437
+ import torch
438
+ from transformers import AutoModelForCausalLM, AutoTokenizer
439
+ import transformers
440
+ if device.startswith("cuda") and not torch.cuda.is_bf16_supported():
441
+ raise SystemExit("this card has no bf16: the arm is not run in another dtype (amendment 2026-09-16)")
442
+ self.torch = torch
443
+ self.name, self.revision, self.chat, self.max_new_tokens = name, revision, chat, max_new_tokens
444
+ self.device = torch.device(device)
445
+ self.tok = AutoTokenizer.from_pretrained(name, revision=revision)
446
+ major, minor = (int(x) for x in transformers.__version__.split(".")[:2])
447
+ dtype_kw = {"dtype": torch.bfloat16} if (major, minor) >= (4, 56) else {"torch_dtype": torch.bfloat16}
448
+ self.model = AutoModelForCausalLM.from_pretrained(name, revision=revision, **dtype_kw)
449
+ self.model.to(self.device).eval()
450
+ if next(self.model.parameters()).dtype != torch.bfloat16:
451
+ raise SystemExit("the weights did not load in bf16: the arm is not run in another dtype")
452
+ self.versions = {"transformers": transformers.__version__, "torch": torch.__version__}
453
+ self.thinking = False if chat else None
454
+
455
+ def describe(self) -> dict:
456
+ t = self.torch
457
+ hw = t.cuda.get_device_name(0) if self.device.type == "cuda" else platform.processor() or platform.machine()
458
+ return {"name": self.name, "revision": self.revision, "dtype": "bf16",
459
+ "template": "chat" if self.chat else "raw", "thinking": self.thinking,
460
+ "tokenizer": self.tok.__class__.__name__, "vocab_size": len(self.tok),
461
+ "device": str(self.device), "hardware": hw, "versions": self.versions,
462
+ "chat_template_sha256_16": (hashlib.sha256(self.tok.chat_template.encode("utf-8")).hexdigest()[:16]
463
+ if (self.chat and getattr(self.tok, "chat_template", None)) else None)}
464
+
465
+ def _prompt_ids(self, prompt: str):
466
+ if self.chat:
467
+ text = self.tok.apply_chat_template([{"role": "user", "content": prompt}], tokenize=False,
468
+ add_generation_prompt=True, enable_thinking=False)
469
+ return self.tok(text, return_tensors="pt", add_special_tokens=False).input_ids
470
+ return self.tok(prompt, return_tensors="pt").input_ids
471
+
472
+ def complete(self, prompt: str) -> str:
473
+ t = self.torch
474
+ ids = self._prompt_ids(prompt).to(self.device)
475
+ with t.no_grad():
476
+ out = self.model.generate(ids, max_new_tokens=self.max_new_tokens, do_sample=False, num_beams=1,
477
+ pad_token_id=self.tok.pad_token_id or self.tok.eos_token_id)
478
+ return self.tok.decode(out[0][ids.shape[1]:], skip_special_tokens=True)
479
+
480
+ def nll(self, prefix: str, text: str) -> tuple[float, int]:
481
+ """Sum of the negative log likelihood of `text`'s tokens after its first
482
+ one, given `prefix` (its own tokens, excluded from the loss), and their count."""
483
+ t = self.torch
484
+ pre = self.tok(prefix, add_special_tokens=False).input_ids if prefix else []
485
+ txt = self.tok(text, add_special_tokens=False).input_ids
486
+ ids = t.tensor([pre + txt], device=self.device)
487
+ with t.no_grad():
488
+ logits = self.model(ids).logits[0, :-1].float()
489
+ logp = t.log_softmax(logits, dim=-1)
490
+ targets = ids[0, 1:]
491
+ tok_nll = -logp.gather(1, targets[:, None])[:, 0]
492
+ start = len(pre) # target index of the text's second token
493
+ sel = tok_nll[start:]
494
+ return float(sel.sum()), int(sel.numel())
495
+
496
+
497
+ def resolve(name: str) -> str:
498
+ """The commit sha of `main` on the hub, to be compared with the pinned one."""
499
+ from huggingface_hub import HfApi
500
+ return HfApi().model_info(name).sha
501
+
502
+
503
+ # ---------------------------------------------------------------------------- #
504
+ # Agreement of two executions, number for number #
505
+ # ---------------------------------------------------------------------------- #
506
+ NUMERIC_KEYS = ("hm_recall_on", "hm_recall_off", "hm_gap", "hm_skill_delta", "ppl_on", "ppl_off", "hm_skill_delta_frame",
507
+ "ppl_frame", "hm_negctrl_rate", "hm_negctrl_abstain_on", "hm_false_abstention_on",
508
+ "hm_invalid_citation_on", "hm_valid_citation_on", "hm_attr_exact_given_valid", "hm_retrieval_hit",
509
+ "hm_guess_rate_off", "hm_malformed_on", "hm_malformed_negctrl", "hm_dissociation_pass", "claimable")
510
+
511
+
512
+ def agree(a: dict, b: dict) -> tuple[bool, list[str]]:
513
+ diffs = [f"{k}: {a.get(k)} != {b.get(k)}" for k in NUMERIC_KEYS if a.get(k) != b.get(k)]
514
+ if a.get("prompt", {}).get("hash") != b.get("prompt", {}).get("hash"):
515
+ diffs.append("prompt hash differs")
516
+ if a.get("model", {}).get("revision") != b.get("model", {}).get("revision"):
517
+ diffs.append("model revision differs")
518
+ return (not diffs), diffs
519
+
520
+
521
+ # ---------------------------------------------------------------------------- #
522
+ # Command line #
523
+ # ---------------------------------------------------------------------------- #
524
+ def _cli(argv=None):
525
+ ap = argparse.ArgumentParser(description="the Molaison LM arm on an external model through the frozen prompt adapter")
526
+ ap.add_argument("--frame", action="store_true", help="write the frozen frame under docs/benchmarks/ and print its hash")
527
+ ap.add_argument("--plant", action="store_true", help="session A: plant the frozen protocol's facts into a sealed journal")
528
+ ap.add_argument("--probe", action="store_true", help="session B, in this new process: run the model, write the file")
529
+ ap.add_argument("--resolve", default=None, help="print the sha of main on the hub for a model id")
530
+ ap.add_argument("--agree", nargs=2, default=None, metavar="FILE", help="compare two files number for number")
531
+ ap.add_argument("--journal", default=None, help="the journal directory (sealed with QUANTUM_CORTEX_JOURNAL_KEY)")
532
+ ap.add_argument("--model", default=None, help="the model id, one of the pinned three unless --revision is given")
533
+ ap.add_argument("--revision", default=None, help="the commit sha (defaults to the pinned one)")
534
+ ap.add_argument("--device", default="cpu")
535
+ ap.add_argument("--raw", action="store_true", help="the frame as raw text (a base model); default: the chat template")
536
+ ap.add_argument("--seed", type=int, default=0)
537
+ ap.add_argument("--n-facts", type=int, default=N_FACTS)
538
+ ap.add_argument("--n-negctrl", type=int, default=N_NEGCTRL)
539
+ ap.add_argument("--n-spans", type=int, default=N_SPANS)
540
+ ap.add_argument("--out", default="metrics/mqar", help="the directory of the artefact")
541
+ ap.add_argument("--repeat", action="store_true", help="name the file -repeat.json (the second execution)")
542
+ a = ap.parse_args(argv)
543
+
544
+ if a.frame:
545
+ frame = build_frame(); p = frame_path(frame); p.parent.mkdir(parents=True, exist_ok=True)
546
+ if p.exists() and p.read_text(encoding="utf-8") != frame:
547
+ raise SystemExit(f"{p} exists with another content: the frame drifted")
548
+ p.write_text(frame, encoding="utf-8", newline="\n") # the same bytes on every machine
549
+ print(f"frame: {p} hash: {frame_hash(frame)} bytes: {len(frame.encode('utf-8'))}")
550
+ return
551
+ if a.resolve:
552
+ sha = resolve(a.resolve)
553
+ pinned = PINNED_MODELS.get(a.resolve)
554
+ print(f"{a.resolve}: main = {sha} pinned = {pinned} {'agrees' if sha == pinned else 'DIFFERS' if pinned else 'not pinned'}")
555
+ return
556
+ if a.agree:
557
+ x, y = (json.loads(Path(f).read_text(encoding="utf-8")) for f in a.agree)
558
+ ok, diffs = agree(x, y)
559
+ print("agree: number for number" if ok else "DISAGREE:\n " + "\n ".join(diffs))
560
+ return
561
+ if a.plant:
562
+ if not a.journal:
563
+ raise SystemExit("--journal is required")
564
+ side = plant(Path(a.journal), n_facts=a.n_facts, seed=a.seed)
565
+ print(json.dumps(side, indent=2))
566
+ return
567
+ if a.probe:
568
+ if not (a.journal and a.model):
569
+ raise SystemExit("--journal and --model are required")
570
+ revision = a.revision or PINNED_MODELS.get(a.model)
571
+ if not revision:
572
+ raise SystemExit(f"{a.model} is not pinned: give --revision (a commit sha), then pin it in the addendum")
573
+ frame = build_frame()
574
+ p = frame_path(frame)
575
+ if not p.exists() or p.read_text(encoding="utf-8") != frame:
576
+ raise SystemExit(f"the frozen frame {p} is missing or differs: run --frame first and commit it")
577
+ model = HFCausalModel(a.model, revision, device=a.device, chat=not a.raw)
578
+ spans, note = load_val_spans(n_spans=a.n_spans, seed=a.seed)
579
+ r = probe(model, Path(a.journal), frame=frame, n_facts=a.n_facts, n_negctrl=a.n_negctrl, seed=a.seed,
580
+ spans=spans, data_note=note)
581
+ out = Path(a.out); out.mkdir(parents=True, exist_ok=True)
582
+ name = f"hm-lm-external-{model_slug(a.model, 'raw' if a.raw else 'chat')}-{frame_hash(frame)}" + ("-repeat" if a.repeat else "") + ".json"
583
+ (out / name).write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8", newline="\n")
584
+ print(f"[record] retained: {out / name}")
585
+ print(json.dumps({k: v for k, v in r.items() if k not in ("answers", "session_a", "skill_arm")}, indent=2))
586
+ return
587
+ ap.print_help()
588
+
589
+
590
+ if __name__ == "__main__":
591
+ _cli()