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/hm_lm.py
ADDED
|
@@ -0,0 +1,983 @@
|
|
|
1
|
+
"""cortex_c2b.hm_lm -- the H.M. protocol on the MODEL (ADR-008; spec amendment 2026-09-12).
|
|
2
|
+
|
|
3
|
+
The organ-level protocol (`hm_protocol.py`) reads payloads with a hash-seeded
|
|
4
|
+
cue encoder and a reference skill probe. This module runs the same two arms on
|
|
5
|
+
the language model itself, under the cite-or-abstain contract:
|
|
6
|
+
|
|
7
|
+
Arm E, journal ON : for each planted fact, the query's cue comes from the
|
|
8
|
+
model, the organ returns a labelled read window, the model generates, and
|
|
9
|
+
the answer is parsed against the contract. STRICT recall = a valid citation
|
|
10
|
+
(a label that exists, whose episode holds the attribute) AND the exact
|
|
11
|
+
attribute. Reported next to it: abstention rate on the negative control,
|
|
12
|
+
false abstention on planted facts (the cost of prudence), invalid citation
|
|
13
|
+
rate (a claim with a fake provenance, the worst case), attribute exactness
|
|
14
|
+
given a valid citation, retrieval hit rate (the encoder), attention mass on
|
|
15
|
+
the retrieved bytes (the reader). A failure is attributable (invariant 8).
|
|
16
|
+
Arm E, journal OFF: the reader is absent (no window, gate 0). A citation is
|
|
17
|
+
impossible, so the strict recall is zero by construction -- the parametric
|
|
18
|
+
floor in its cleanest form; the guess rate and the by-chance attribute hits
|
|
19
|
+
are reported so the floor keeps its meaning.
|
|
20
|
+
Arm S: ordinary language modelling with the reader ACTIVE on whatever the
|
|
21
|
+
journal returns for each span (journal ON) against the reader absent (OFF):
|
|
22
|
+
`hm_skill_delta` = relative perplexity degradation. This is the real e_S:
|
|
23
|
+
retrieval noise must not hurt the model.
|
|
24
|
+
|
|
25
|
+
Verdict (amended spec): pass = skill delta <= e_S AND strict recall ON - strict
|
|
26
|
+
recall OFF >= d AND strict recall OFF <= floor AND invalid citation <= 1% AND
|
|
27
|
+
claims on the negative control < 10%. `persistent` / `claimable` as in the organ
|
|
28
|
+
protocol: a claim needs the journal on disk, durable, and session B reopened
|
|
29
|
+
from the disk alone -- the strict version runs session B in another process
|
|
30
|
+
(`tests/test_c2b_lm_bridge.py`). Thresholds d, e_S, floor untouched.
|
|
31
|
+
"""
|
|
32
|
+
from __future__ import annotations
|
|
33
|
+
|
|
34
|
+
import math
|
|
35
|
+
|
|
36
|
+
import numpy as np
|
|
37
|
+
import torch
|
|
38
|
+
|
|
39
|
+
from cortex_c2b import Journal, MODE_DURABLE, POLICY_STOP, lifecycle_declaration
|
|
40
|
+
from cortex_c2b.hm_protocol import (Fact, generate_facts, SCHEMAS, _ATTRS, DELTA, EPSILON_S,
|
|
41
|
+
FLOOR_MARGIN, NEGCTRL_INVALID, N_FACTS, N_NEGCTRL)
|
|
42
|
+
from cortex_c2b.lm_bridge import (JournalBridge, encode_query, parse_contract, build_read_window, NEWLINE, TOK_EPI,
|
|
43
|
+
TOK_CITE, TOK_ANS, TOK_UNKNOWN, LABELS)
|
|
44
|
+
from cortex_c2b.write_path import WritePath
|
|
45
|
+
from cortex_c2b.read_path import JournalPath, STATE_EVICTED
|
|
46
|
+
|
|
47
|
+
INVALID_CITATION_MAX = 0.01 # declared 2026-09-12, before any run on the model
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
POLICIES = ("plain", "head", "head+pointer", "mark-veto", "mark-veto+value")
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def policy_outcome(kind: str, label, labels: dict, marks_by_pointer: dict, payloads, own_pointer, attr: str | None,
|
|
54
|
+
expected_attr: str, policy: str, threshold: float | None):
|
|
55
|
+
"""ADR-008 amendment 2026-09-18, the two organ side decode policies, one function used
|
|
56
|
+
live (in the probe) and in replay (on an answers file), so the two cannot disagree.
|
|
57
|
+
`mark-veto`: the model's citation stands unless the cited line's mark is below the
|
|
58
|
+
oracle's threshold (or the label is not in the window); then it is an abstention.
|
|
59
|
+
`mark-veto+value`: the veto, and the attribute answered is the cited line's own
|
|
60
|
+
value, read by the organ from the line's statement by the generator's templates,
|
|
61
|
+
never generated; a vetoed or missing line is an abstention.
|
|
62
|
+
Returns (kind, attr, outcome) with outcome in strict | valid | invalid | abstain."""
|
|
63
|
+
from cortex_c2b.hm_protocol import value_of_statement
|
|
64
|
+
if kind == "unknown":
|
|
65
|
+
return kind, None, "abstain"
|
|
66
|
+
if policy in ("mark-veto", "mark-veto+value"):
|
|
67
|
+
pointer = labels.get(label) if kind == "cite" else None
|
|
68
|
+
mark = marks_by_pointer.get(pointer) if pointer is not None else None
|
|
69
|
+
if pointer is None or mark is None or (threshold is not None and mark < threshold):
|
|
70
|
+
return "unknown", None, "abstain" # the organ vetoes: no line, or a low mark
|
|
71
|
+
if policy == "mark-veto+value":
|
|
72
|
+
payload = payloads.get(pointer) if pointer in payloads else b""
|
|
73
|
+
v = value_of_statement(payload.decode("utf-8", errors="replace")) if payload else None
|
|
74
|
+
attr = v[2] if v else None
|
|
75
|
+
if attr is None:
|
|
76
|
+
return "unknown", None, "abstain"
|
|
77
|
+
if kind == "cite" and label in labels:
|
|
78
|
+
payload = payloads.get(labels[label]) if labels[label] in payloads else b""
|
|
79
|
+
cited_ok = (attr or "").encode("utf-8") in payload and len(attr or "") > 0
|
|
80
|
+
if not cited_ok:
|
|
81
|
+
return kind, attr, "invalid"
|
|
82
|
+
return kind, attr, ("strict" if attr == expected_attr else "valid")
|
|
83
|
+
return kind, attr, "invalid"
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def pointer_label(model, window_bytes: bytes) -> int | None:
|
|
87
|
+
"""The pointer policy's citation: the label of the window line that received the
|
|
88
|
+
most of the reader's attention from the decision position (heads averaged, the
|
|
89
|
+
null slot excluded), read from the reader's last attention map. None when the
|
|
90
|
+
window has no labelled line."""
|
|
91
|
+
attn = None
|
|
92
|
+
for blk in model.blocks:
|
|
93
|
+
r = getattr(blk, "reader", None)
|
|
94
|
+
if r is not None and getattr(r, "last_attn", None) is not None:
|
|
95
|
+
attn = r.last_attn[0, :, -1, 1:].mean(dim=0) # (r,): the decision position, real bytes only
|
|
96
|
+
if attn is None:
|
|
97
|
+
return None
|
|
98
|
+
best, best_mass = None, -1.0
|
|
99
|
+
start = 0
|
|
100
|
+
n = len(window_bytes)
|
|
101
|
+
while start < n:
|
|
102
|
+
end = window_bytes.find(bytes([NEWLINE]), start)
|
|
103
|
+
end = n if end < 0 else end
|
|
104
|
+
label = window_bytes[start] if end > start else None
|
|
105
|
+
if label in LABELS:
|
|
106
|
+
stop = min(end + 1, attn.numel())
|
|
107
|
+
m = float(attn[start:stop].sum()) if stop > start else -1.0
|
|
108
|
+
if m > best_mass:
|
|
109
|
+
best, best_mass = label, m
|
|
110
|
+
start = end + 1
|
|
111
|
+
return best
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
@torch.no_grad()
|
|
115
|
+
def generate(model, prefix: list[int], window_tokens=None, window_mask=None, gate: float = 1.0,
|
|
116
|
+
max_new: int = 48, policy: str = "plain", window_bytes: bytes | None = None) -> tuple[list[int], float | None]:
|
|
117
|
+
"""Greedy byte-level decoding until a newline. Returns (new tokens, the
|
|
118
|
+
reader's attention mass at the first decoding step).
|
|
119
|
+
|
|
120
|
+
`policy` (ADR-008 amendment of 2026-09-14, declared before any measurement):
|
|
121
|
+
plain -- every token from the model's argmax, the head at most a bias (v7 as trained);
|
|
122
|
+
head -- the matching head decides: m <= 0 emits the abstention, m > 0 forces <CITE>
|
|
123
|
+
and the model generates the label and the attribute;
|
|
124
|
+
head+pointer -- the head decides, and the label is read off the reader's attention
|
|
125
|
+
(the line with the most attention from the decision position); the
|
|
126
|
+
model generates only the attribute.
|
|
127
|
+
Without a window, or without a head, every policy is the plain one."""
|
|
128
|
+
if policy not in POLICIES:
|
|
129
|
+
raise ValueError(f"unknown policy {policy!r}; one of {POLICIES}")
|
|
130
|
+
device = next(model.parameters()).device
|
|
131
|
+
idx = torch.tensor([prefix], dtype=torch.long, device=device)
|
|
132
|
+
out, mass, match = [], None, None
|
|
133
|
+
has_head = getattr(model, "match_head", None) is not None
|
|
134
|
+
forced: list[int] = []
|
|
135
|
+
for step in range(max_new):
|
|
136
|
+
# RES-21: the matching head fires at the decision position only, i.e. on the first step
|
|
137
|
+
dpos = (torch.tensor([idx.size(1) - 1], device=device)
|
|
138
|
+
if (step == 0 and has_head and window_tokens is not None) else None)
|
|
139
|
+
logits, _ = model(idx[:, -model.cfg.block_size:], window_tokens=window_tokens,
|
|
140
|
+
window_mask=window_mask, gate=gate, decision_pos=dpos)
|
|
141
|
+
if step == 0:
|
|
142
|
+
mass = model.read_mass()
|
|
143
|
+
match = getattr(model, "last_match_logit", None) # the head's judgment at the decision step
|
|
144
|
+
if policy != "plain" and has_head and window_tokens is not None and match is not None:
|
|
145
|
+
if float(match[0]) <= 0.0:
|
|
146
|
+
out = [TOK_UNKNOWN, NEWLINE]
|
|
147
|
+
break
|
|
148
|
+
forced = [TOK_CITE]
|
|
149
|
+
if policy == "head+pointer":
|
|
150
|
+
label = pointer_label(model, window_bytes or b"")
|
|
151
|
+
if label is not None:
|
|
152
|
+
forced += [label, TOK_ANS]
|
|
153
|
+
if forced:
|
|
154
|
+
nxt = forced.pop(0)
|
|
155
|
+
else:
|
|
156
|
+
nxt = int(logits[0, -1].argmax())
|
|
157
|
+
out.append(nxt)
|
|
158
|
+
if nxt == NEWLINE:
|
|
159
|
+
break
|
|
160
|
+
idx = torch.cat([idx, torch.tensor([[nxt]], device=device)], dim=1)
|
|
161
|
+
if has_head:
|
|
162
|
+
model.last_match_logit = match # later steps reset it; the probe reads the decision step's
|
|
163
|
+
return out, mass
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _probe(model, bridge: JournalBridge, facts: list[Fact], journal_on: bool, window_len: int,
|
|
167
|
+
pointer_of: dict[str, str], policy: str = "plain", veto_threshold: float | None = None) -> dict:
|
|
168
|
+
"""One arm over a fact list. `pointer_of`: entity -> pointer for planted facts
|
|
169
|
+
(empty for the negative control)."""
|
|
170
|
+
n = max(1, len(facts))
|
|
171
|
+
strict = abstain = invalid = valid = attr_ok = guess = attr_by_chance = hit = 0
|
|
172
|
+
masses = []
|
|
173
|
+
match_right = match_n = 0 # RES-21: the head's own judgment, against the read
|
|
174
|
+
mark_own, mark_other_max, mark_max = [], [], [] # RES-23: the organ's similarities per read
|
|
175
|
+
answers = [] # Rev66: every answer kept per line, a recording, not a variable
|
|
176
|
+
for f in facts:
|
|
177
|
+
query = encode_query(f.query)
|
|
178
|
+
read = None
|
|
179
|
+
if journal_on:
|
|
180
|
+
cue = bridge.cues([query])[0]
|
|
181
|
+
read = bridge.read(cue)
|
|
182
|
+
if read.scores:
|
|
183
|
+
own_p = pointer_of.get(f.entity) if pointer_of else None
|
|
184
|
+
others = [sc for pt, sc in zip(read.pointers, read.scores) if pt != own_p]
|
|
185
|
+
if own_p in read.pointers:
|
|
186
|
+
mark_own.append(float(read.scores[read.pointers.index(own_p)]))
|
|
187
|
+
if others:
|
|
188
|
+
mark_other_max.append(float(max(others)))
|
|
189
|
+
mark_max.append(float(max(read.scores)))
|
|
190
|
+
wt, wm = bridge.window_tensors([read.window], window_len)
|
|
191
|
+
toks, mass = generate(model, query, wt, wm, gate=1.0, policy=policy, window_bytes=read.window)
|
|
192
|
+
if mass is not None:
|
|
193
|
+
masses.append(mass)
|
|
194
|
+
present = (pointer_of.get(f.entity) in read.pointers) if pointer_of else False
|
|
195
|
+
hit += int(present) if pointer_of else 0
|
|
196
|
+
m = getattr(model, "last_match_logit", None)
|
|
197
|
+
if m is not None:
|
|
198
|
+
match_right += int((float(m[0]) > 0) == present); match_n += 1
|
|
199
|
+
labels = read.labels
|
|
200
|
+
else:
|
|
201
|
+
toks, _ = generate(model, query, None, None, gate=0.0)
|
|
202
|
+
labels = {}
|
|
203
|
+
kind, label, attr = parse_contract(toks)
|
|
204
|
+
own_p = pointer_of.get(f.entity) if pointer_of else None
|
|
205
|
+
mbp = dict(zip(read.pointers, [float(x) for x in read.scores])) if (read is not None and read.scores) else {}
|
|
206
|
+
if policy in ("mark-veto", "mark-veto+value"):
|
|
207
|
+
kind, attr, _ = policy_outcome(kind, label, labels, mbp, bridge.j.payloads, own_p, attr, f.attr, policy, veto_threshold)
|
|
208
|
+
rec = {"entity": f.entity, "schema": f.schema, "attr": f.attr, "kind": kind,
|
|
209
|
+
"label": (chr(label) if isinstance(label, int) else label) if label is not None else None, "answered": attr,
|
|
210
|
+
"outcome": None, "own_in_window": (own_p in read.pointers) if (read is not None and own_p) else None}
|
|
211
|
+
if read is not None and read.scores:
|
|
212
|
+
marks_by_pointer = dict(zip(read.pointers, [float(x) for x in read.scores]))
|
|
213
|
+
rec["mark_own"] = marks_by_pointer.get(own_p) if own_p else None
|
|
214
|
+
rec["mark_max"] = max(marks_by_pointer.values())
|
|
215
|
+
rec["cited_pointer_is_own"] = (labels.get(label) == own_p) if (kind == "cite" and label in labels and own_p) else None
|
|
216
|
+
rec["mark_cited"] = marks_by_pointer.get(labels[label]) if (kind == "cite" and label in labels) else None
|
|
217
|
+
rec["cited_is_highest_mark"] = (labels.get(label) is not None and marks_by_pointer.get(labels[label]) == rec["mark_max"]) if (kind == "cite" and label in labels) else None
|
|
218
|
+
answers.append(rec)
|
|
219
|
+
if kind == "unknown":
|
|
220
|
+
abstain += 1; rec["outcome"] = "abstain"
|
|
221
|
+
continue
|
|
222
|
+
guess += 1
|
|
223
|
+
if kind == "cite" and label in labels:
|
|
224
|
+
payload = bridge.j.payloads.get(labels[label]) if labels[label] in bridge.j.payloads else b""
|
|
225
|
+
cited_ok = (attr or "").encode("utf-8") in payload and len(attr or "") > 0
|
|
226
|
+
if cited_ok:
|
|
227
|
+
valid += 1; rec["outcome"] = "valid"
|
|
228
|
+
if attr == f.attr:
|
|
229
|
+
strict += 1; attr_ok += 1; rec["outcome"] = "strict"
|
|
230
|
+
else:
|
|
231
|
+
invalid += 1; rec["outcome"] = "invalid" # a label that exists but does not hold the claim
|
|
232
|
+
else:
|
|
233
|
+
invalid += 1; rec["outcome"] = "invalid" # a citation of nothing, or a malformed claim
|
|
234
|
+
if attr == f.attr:
|
|
235
|
+
attr_by_chance += 1
|
|
236
|
+
return {"recall_strict": strict / n, "abstain_rate": abstain / n, "guess_rate": guess / n,
|
|
237
|
+
"invalid_citation_rate": invalid / n, "valid_citation_rate": valid / n,
|
|
238
|
+
"attr_exact_given_valid": (attr_ok / valid) if valid else None,
|
|
239
|
+
"attr_hit_any": attr_by_chance / n,
|
|
240
|
+
"retrieval_hit": (hit / n) if (journal_on and pointer_of) else None,
|
|
241
|
+
"attention_mass": (float(np.mean(masses)) if masses else None),
|
|
242
|
+
"match_acc": (match_right / match_n) if match_n else None,
|
|
243
|
+
"marks": {"own": _stats(mark_own), "other_max": _stats(mark_other_max), "max": _stats(mark_max)},
|
|
244
|
+
"answers": answers}
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
@torch.no_grad()
|
|
248
|
+
def retrieval_probe(model, journal, facts: list[Fact], neg: list[Fact], pointer_of: dict[str, str],
|
|
249
|
+
k: int = 4, seed: int = 0) -> dict:
|
|
250
|
+
"""ADR-008 amendment 2026-09-19, the retrieval policy probe: the first factor of the
|
|
251
|
+
decomposition (retrieval) read on its own, with no generation and no training, on a
|
|
252
|
+
checkpoint and a sealed journal that already exist. For each protocol question the
|
|
253
|
+
query cue is the model's own; the same entries are indexed twice, ranked by the dense
|
|
254
|
+
cosine of every run to date and by the expand and sparsify tag of ADR-003 line 6
|
|
255
|
+
(the dentate gyrus in the map, the mushroom body in Dasgupta, Stevens and Navlakha
|
|
256
|
+
2017). Reports, per ranking: whether the own episode is among the candidates the
|
|
257
|
+
buckets return at all, and whether it is in the top k. A miss in the first is the
|
|
258
|
+
bucketing's, a miss in the second only is the ranking's; the two are never mixed."""
|
|
259
|
+
from cortex_c2b.read_path import CueIndex, JournalPath
|
|
260
|
+
device = next(model.parameters()).device
|
|
261
|
+
out = {"probe": "retrieval policy (ADR-003 line 6): dense cosine against expand and sparsify",
|
|
262
|
+
"k": k, "n_facts": len(facts), "n_negctrl": len(neg), "rankings": {}}
|
|
263
|
+
for rank in ("cosine", "fly"):
|
|
264
|
+
jp = JournalPath(journal, index=CueIndex(seed=seed, rank=rank), seed=seed)
|
|
265
|
+
for eid, e in journal._entries.items():
|
|
266
|
+
if e.state != STATE_EVICTED:
|
|
267
|
+
jp.index.add(eid, np.asarray(e.cue, dtype=np.float32))
|
|
268
|
+
bridge = JournalBridge(model, journal, journal_path=jp, k=k, seed=seed, device=device)
|
|
269
|
+
in_cand = in_topk = 0; ranks = []; margins = []; neg_best = []
|
|
270
|
+
for f in facts:
|
|
271
|
+
cue = bridge.cues([encode_query(f.query)])[0]
|
|
272
|
+
own = pointer_of.get(f.entity)
|
|
273
|
+
scored = jp.index.query(np.asarray(cue, dtype=np.float32), k=len(jp.index))
|
|
274
|
+
by_pointer = []
|
|
275
|
+
for eid, sc in scored:
|
|
276
|
+
e, pointer = jp._resolve(eid)
|
|
277
|
+
by_pointer.append((pointer, sc))
|
|
278
|
+
pos = next((i for i, (pt, _) in enumerate(by_pointer) if pt == own), None)
|
|
279
|
+
if pos is None:
|
|
280
|
+
continue
|
|
281
|
+
in_cand += 1; ranks.append(pos + 1)
|
|
282
|
+
if pos < k:
|
|
283
|
+
in_topk += 1
|
|
284
|
+
others = [sc for i, (pt, sc) in enumerate(by_pointer) if pt != own]
|
|
285
|
+
margins.append(float(by_pointer[pos][1] - max(others))) if others else None
|
|
286
|
+
for f in neg:
|
|
287
|
+
cue = bridge.cues([encode_query(f.query)])[0]
|
|
288
|
+
sc = jp.index.query(np.asarray(cue, dtype=np.float32), k=1)
|
|
289
|
+
neg_best.append(float(sc[0][1]) if sc else float("nan"))
|
|
290
|
+
n = len(facts)
|
|
291
|
+
out["rankings"][rank] = {
|
|
292
|
+
"own_in_candidates": in_cand / n, "own_in_top_k": in_topk / n,
|
|
293
|
+
"own_rank": _stats([float(r) for r in ranks]), "own_minus_best_other": _stats(margins),
|
|
294
|
+
"negctrl_best_score": _stats([x for x in neg_best if x == x])}
|
|
295
|
+
c, f_ = out["rankings"]["cosine"], out["rankings"]["fly"]
|
|
296
|
+
out["reading"] = {"top_k_cosine": c["own_in_top_k"], "top_k_fly": f_["own_in_top_k"],
|
|
297
|
+
"candidate_misses_cosine": round(1 - c["own_in_candidates"], 4),
|
|
298
|
+
"candidate_misses_fly": round(1 - f_["own_in_candidates"], 4)}
|
|
299
|
+
return out
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
def replay_policy(answers_file: dict, policy: str, threshold: float | None = None) -> dict:
|
|
303
|
+
"""Post hoc: apply an organ side policy to the answers a session B kept (Rev66 files), with
|
|
304
|
+
the oracle's threshold of that file. The rates it returns are what the live policy would
|
|
305
|
+
have produced on the same checkpoint and reads, because both use `policy_outcome`;
|
|
306
|
+
a replay on the seed the policy was conceived on is a post hoc reading, never a result."""
|
|
307
|
+
thr = answers_file["hm_mark_oracle"]["threshold"] if threshold is None else float(threshold) # a sweep reads the sensitivity
|
|
308
|
+
def outcome(a):
|
|
309
|
+
kind, label = a["kind"], a.get("label")
|
|
310
|
+
labels = {label: "cited"} if (kind == "cite" and a.get("mark_cited") is not None) else {}
|
|
311
|
+
mbp = {"cited": a["mark_cited"]} if a.get("mark_cited") is not None else {}
|
|
312
|
+
own = "cited" if a.get("cited_pointer_is_own") else "own"
|
|
313
|
+
# the value read from the cited line is the fact's attribute iff the cited line is the own one
|
|
314
|
+
payloads = {"cited": (f"x took place in {a['attr']}" if a.get("cited_pointer_is_own") else "x took place in other").encode()}
|
|
315
|
+
if policy == "mark-veto+value":
|
|
316
|
+
k, attr, o = policy_outcome(kind, label, labels, mbp, payloads, own, a.get("answered"), a["attr"], "mark-veto", thr)
|
|
317
|
+
if o == "abstain":
|
|
318
|
+
return "abstain"
|
|
319
|
+
return "strict" if a.get("cited_pointer_is_own") else "invalid"
|
|
320
|
+
k, attr, o = policy_outcome(kind, label, labels, mbp, {"cited": ((a.get("answered") or "") if a["outcome"] in ("strict", "valid") else "").encode()}, own, a.get("answered"), a["attr"], policy, thr)
|
|
321
|
+
if policy == "mark-veto" and o != "abstain":
|
|
322
|
+
return a["outcome"] # the model's own outcome stands when not vetoed
|
|
323
|
+
return o
|
|
324
|
+
on = [outcome(a) for a in answers_file["answers"]["on"]]; n = len(on)
|
|
325
|
+
neg = [outcome(a) for a in answers_file["answers"]["negctrl"]]
|
|
326
|
+
strict, valid, invalid, abst = on.count("strict"), on.count("strict") + on.count("valid"), on.count("invalid"), on.count("abstain")
|
|
327
|
+
negc = sum(1 for o in neg if o != "abstain")
|
|
328
|
+
return {"policy": policy, "post_hoc": True, "threshold": thr, "hm_recall_on": strict / n, "hm_valid_citation_on": valid / n,
|
|
329
|
+
"hm_invalid_citation_on": invalid / n, "hm_false_abstention_on": abst / n, "hm_negctrl_rate": negc / len(neg),
|
|
330
|
+
"gate_conditions_without_skill_arm": int(strict / n >= DELTA and negc / len(neg) < NEGCTRL_INVALID and invalid / n <= INVALID_CITATION_MAX)}
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
def _stats(xs: list[float]) -> dict | None:
|
|
334
|
+
if not xs:
|
|
335
|
+
return None
|
|
336
|
+
a = np.asarray(xs, dtype=np.float64)
|
|
337
|
+
return {"n": int(a.size), "mean": float(a.mean()), "min": float(a.min()), "max": float(a.max()),
|
|
338
|
+
"q10": float(np.quantile(a, 0.10)), "q50": float(np.quantile(a, 0.50)), "q90": float(np.quantile(a, 0.90))}
|
|
339
|
+
|
|
340
|
+
|
|
341
|
+
@torch.no_grad()
|
|
342
|
+
def mark_oracle(model, bridge: JournalBridge, facts: list[Fact], neg: list[Fact], pointer_of: dict[str, str],
|
|
343
|
+
train_seed: int = 100_000, train_n: int = 200, k: int | None = None) -> dict:
|
|
344
|
+
"""RES-23, declared before v9 ran: the ceiling the organ's marks allow, with no
|
|
345
|
+
model output. A rule: cite the line with the highest mark when that mark clears
|
|
346
|
+
a threshold, else abstain. The threshold is read from the training facts family
|
|
347
|
+
only (a scratch journal of `train_n` training facts planted with this model's
|
|
348
|
+
cues, seed `train_seed`, and their never planted negatives), never from the
|
|
349
|
+
protocol's facts; then the rule is applied to the protocol's own reads. Strict
|
|
350
|
+
recall counts the own episode cited; a claim on a never planted entity counts
|
|
351
|
+
against; the skill arm does not apply (no model). Deterministic from the
|
|
352
|
+
checkpoint and the sealed journal."""
|
|
353
|
+
from cortex_c2b.organ_use import training_facts, plant_pool
|
|
354
|
+
k = k or bridge.k
|
|
355
|
+
rng_state = bridge.rng.bit_generator.state # the oracle reads through the bridge and must leave no trace:
|
|
356
|
+
tf, tneg, _ = training_facts(train_n, train_seed) # its reads would otherwise advance the window shuffle and make
|
|
357
|
+
# two policies see different windows (found and fixed 2026-09-20)
|
|
358
|
+
scratch = JournalBridge(model, Journal(), k=k, budget_bytes=bridge.budget, seed=0, shuffle_seed=1,
|
|
359
|
+
device=bridge.device, mark=bridge.mark)
|
|
360
|
+
plant_pool(scratch, tf)
|
|
361
|
+
pos, negs = [], []
|
|
362
|
+
for f in tf:
|
|
363
|
+
r = scratch.read(scratch.cues([encode_query(f.query)])[0])
|
|
364
|
+
own = scratch.pointer_of.get(f.entity)
|
|
365
|
+
if r.scores and own in r.pointers:
|
|
366
|
+
pos.append((max(r.scores), r.pointers[int(np.argmax(r.scores))] == own))
|
|
367
|
+
elif r.scores:
|
|
368
|
+
pos.append((max(r.scores), False))
|
|
369
|
+
for f in tneg:
|
|
370
|
+
r = scratch.read(scratch.cues([encode_query(f.query)])[0])
|
|
371
|
+
negs.append(max(r.scores) if r.scores else 0.0)
|
|
372
|
+
cands = sorted({m for m, _ in pos} | set(negs))
|
|
373
|
+
best_thr, best_acc = 0.0, -1.0
|
|
374
|
+
for thr in cands: # claim iff max mark > thr; the best on the training family
|
|
375
|
+
acc = (sum(1 for m, right in pos if m > thr and right) + sum(1 for m in negs if m <= thr)) / max(1, len(pos) + len(negs))
|
|
376
|
+
if acc > best_acc:
|
|
377
|
+
best_thr, best_acc = float(thr), float(acc)
|
|
378
|
+
def apply(fs, planted: bool):
|
|
379
|
+
claims = strict = 0
|
|
380
|
+
for f in fs:
|
|
381
|
+
r = bridge.read(bridge.cues([encode_query(f.query)])[0])
|
|
382
|
+
if not r.scores:
|
|
383
|
+
continue
|
|
384
|
+
top = int(np.argmax(r.scores))
|
|
385
|
+
if r.scores[top] > best_thr:
|
|
386
|
+
claims += 1
|
|
387
|
+
if planted and r.pointers[top] == pointer_of.get(f.entity):
|
|
388
|
+
strict += 1
|
|
389
|
+
return claims / max(1, len(fs)), strict / max(1, len(fs))
|
|
390
|
+
on_claims, on_strict = apply(facts, True)
|
|
391
|
+
neg_claims, _ = apply(neg, False)
|
|
392
|
+
bridge.rng.bit_generator.state = rng_state # restored: the probes that follow see the same windows as a
|
|
393
|
+
# run without the oracle, whatever the policy
|
|
394
|
+
gap = on_strict - 0.0 # off is zero by construction: no window, no citation
|
|
395
|
+
passes = (gap >= DELTA) and (neg_claims < NEGCTRL_INVALID)
|
|
396
|
+
return {"oracle": "mark oracle (RES-23): cite the highest marked line above a threshold read from the training family",
|
|
397
|
+
"threshold": best_thr, "training_accuracy": best_acc, "training_pairs": len(pos) + len(negs),
|
|
398
|
+
"recall_on_strict": on_strict, "claims_on": on_claims, "negctrl_claims": neg_claims,
|
|
399
|
+
"gap": gap, "passes_without_skill_arm": bool(passes), "encoder": "this checkpoint's own cue encoder"}
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
@torch.no_grad()
|
|
403
|
+
def skill_delta(model, bridge: JournalBridge, val_batches: list[tuple[torch.Tensor, torch.Tensor]],
|
|
404
|
+
window_len: int) -> tuple[float, float, float]:
|
|
405
|
+
"""Arm S: val loss with the reader active on whatever the journal returns for
|
|
406
|
+
each span (ON) against the reader absent (OFF). Returns (delta, ppl_on, ppl_off)."""
|
|
407
|
+
on, off = [], []
|
|
408
|
+
for x, y in val_batches:
|
|
409
|
+
_, l_off = model(x, y, gate=0.0)
|
|
410
|
+
cues = bridge.cues([row.tolist() for row in x])
|
|
411
|
+
windows = [bridge.read(c).window for c in cues]
|
|
412
|
+
wt, wm = bridge.window_tensors(windows, window_len)
|
|
413
|
+
_, l_on = model(x, y, window_tokens=wt, window_mask=wm, gate=1.0)
|
|
414
|
+
on.append(float(l_on)); off.append(float(l_off))
|
|
415
|
+
ppl_on, ppl_off = math.exp(float(np.mean(on))), math.exp(float(np.mean(off)))
|
|
416
|
+
return max(0.0, (ppl_on - ppl_off) / ppl_off), ppl_on, ppl_off
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
def run_hm_lm(model, journal_factory, n_facts: int = N_FACTS, n_negctrl: int = N_NEGCTRL, seed: int = 0,
|
|
420
|
+
k: int = 4, budget: int = 256, window_len: int = 256, val_batches=None,
|
|
421
|
+
on_disk: bool = False, reopen: bool = True, mark: bool = False) -> dict:
|
|
422
|
+
"""`journal_factory()` returns the run's journal (a fresh one on the first
|
|
423
|
+
call; the same on-disk scope reopened on the second). Session A plants the
|
|
424
|
+
frozen protocol's facts through the model's cues; session B reopens the
|
|
425
|
+
journal (a new object, the index recomputed) and probes."""
|
|
426
|
+
facts, gen_hash = generate_facts(n_facts, seed)
|
|
427
|
+
neg, _ = generate_facts(n_negctrl, seed + 10_000)
|
|
428
|
+
chance = float(np.mean([1.0 / len(_ATTRS[s]) for s in SCHEMAS]))
|
|
429
|
+
floor = chance + FLOOR_MARGIN
|
|
430
|
+
device = next(model.parameters()).device
|
|
431
|
+
model.eval()
|
|
432
|
+
|
|
433
|
+
# --- session A: plant through the NORMAL gated path, cues from the model ---
|
|
434
|
+
journal = journal_factory()
|
|
435
|
+
bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, device=device, mark=mark)
|
|
436
|
+
admitted = 0
|
|
437
|
+
for i, f in enumerate(facts):
|
|
438
|
+
admitted += int(bridge.write(f.statement, f.schema, now=float(i), entity=f.entity).admitted)
|
|
439
|
+
pointer_of = dict(bridge.pointer_of)
|
|
440
|
+
durable_a = journal.mode == MODE_DURABLE
|
|
441
|
+
|
|
442
|
+
# --- session B: nothing of A but the journal (reopened when on disk) ---
|
|
443
|
+
if on_disk and reopen:
|
|
444
|
+
del bridge
|
|
445
|
+
journal = journal_factory()
|
|
446
|
+
bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1, device=device,
|
|
447
|
+
mark=mark)
|
|
448
|
+
storage = lifecycle_declaration(journal)["storage"]
|
|
449
|
+
persistent = bool(on_disk and reopen and durable_a and journal.mode == MODE_DURABLE)
|
|
450
|
+
|
|
451
|
+
on = _probe(model, bridge, facts, True, window_len, pointer_of)
|
|
452
|
+
off = _probe(model, bridge, facts, False, window_len, pointer_of)
|
|
453
|
+
neg_on = _probe(model, bridge, neg, True, window_len, {})
|
|
454
|
+
neg_off = _probe(model, bridge, neg, False, window_len, {})
|
|
455
|
+
delta, ppl_on, ppl_off = skill_delta(model, bridge, val_batches, window_len) if val_batches else (0.0, None, None)
|
|
456
|
+
oracle = mark_oracle(model, bridge, facts, neg, pointer_of)
|
|
457
|
+
|
|
458
|
+
negctrl_claims = neg_on["guess_rate"] # a claim on a never-planted entity
|
|
459
|
+
valid = negctrl_claims < NEGCTRL_INVALID
|
|
460
|
+
dissociation = (delta <= EPSILON_S and (on["recall_strict"] - off["recall_strict"]) >= DELTA
|
|
461
|
+
and off["recall_strict"] <= floor and on["invalid_citation_rate"] <= INVALID_CITATION_MAX
|
|
462
|
+
and valid)
|
|
463
|
+
return {
|
|
464
|
+
"benchmark": "hm_protocol_lm",
|
|
465
|
+
"spec": "docs/benchmarks/hm-protocol.md (frozen thresholds; LM arm amendment 2026-09-12)",
|
|
466
|
+
"thresholds": {"delta": DELTA, "epsilon_s": EPSILON_S, "floor": floor, "chance": chance,
|
|
467
|
+
"negctrl_invalid": NEGCTRL_INVALID, "invalid_citation_max": INVALID_CITATION_MAX},
|
|
468
|
+
"generator": {"config_hash": gen_hash, "n_facts": n_facts, "n_negctrl": n_negctrl, "seed": seed,
|
|
469
|
+
"admitted": admitted},
|
|
470
|
+
"hm_recall_on": on["recall_strict"], "hm_recall_off": off["recall_strict"],
|
|
471
|
+
"hm_gap": on["recall_strict"] - off["recall_strict"],
|
|
472
|
+
"hm_skill_delta": delta, "ppl_on": ppl_on, "ppl_off": ppl_off,
|
|
473
|
+
"hm_negctrl_rate": negctrl_claims, "hm_negctrl_on": neg_on["attr_hit_any"],
|
|
474
|
+
"hm_negctrl_abstain_on": neg_on["abstain_rate"], "hm_negctrl_abstain_off": neg_off["abstain_rate"],
|
|
475
|
+
"hm_false_abstention_on": on["abstain_rate"], "hm_invalid_citation_on": on["invalid_citation_rate"],
|
|
476
|
+
"hm_valid_citation_on": on["valid_citation_rate"], "hm_attr_exact_given_valid": on["attr_exact_given_valid"],
|
|
477
|
+
"hm_retrieval_hit": on["retrieval_hit"], "hm_attention_mass": on["attention_mass"],
|
|
478
|
+
"hm_marks": {"on": on["marks"], "negctrl": neg_on["marks"], "written_in_window": bool(mark)},
|
|
479
|
+
"hm_mark_oracle": oracle,
|
|
480
|
+
"hm_guess_rate_off": off["guess_rate"], "hm_attr_hit_off_by_chance": off["attr_hit_any"],
|
|
481
|
+
"hm_match_acc_on": on["match_acc"], "hm_match_acc_negctrl": neg_on["match_acc"], # RES-21 diagnostics (None without the head)
|
|
482
|
+
"run_valid": valid, "hm_dissociation_pass": int(dissociation),
|
|
483
|
+
"persistent": persistent, "claimable": int(dissociation and persistent),
|
|
484
|
+
"storage": {"on_disk": on_disk, "mode": storage["mode"], "policy": storage["policy"],
|
|
485
|
+
"plaintext_scope": storage["plaintext_scope"]},
|
|
486
|
+
"attribution": {"encoder": on["retrieval_hit"], "reader": on["attention_mass"],
|
|
487
|
+
"answer": on["attr_exact_given_valid"]},
|
|
488
|
+
"verdict": (("PASS" if dissociation else ("INVALID" if not valid else "FAIL"))
|
|
489
|
+
+ " -- LM arm, cite-or-abstain contract"
|
|
490
|
+
+ ("" if persistent else " -- persistent: false, not claimable")),
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
# ---------------------------------------------------------------------------- #
|
|
495
|
+
# Session B in a NEW process: the model from its checkpoint, the journal from #
|
|
496
|
+
# the sealed disk, nothing else (ADR-007 D8, the named boundary on the model) #
|
|
497
|
+
# ---------------------------------------------------------------------------- #
|
|
498
|
+
@torch.no_grad()
|
|
499
|
+
def shape_probe(model, bridge: JournalBridge, facts: list[Fact], neg: list[Fact], window_len: int,
|
|
500
|
+
pointer_of: dict[str, str], seed: int = 0) -> dict:
|
|
501
|
+
"""A diagnostic outside the frozen protocol (like the memorisation probe): does the
|
|
502
|
+
cite or abstain decision follow the NUMBER OF LINES in the window? Run v5
|
|
503
|
+
(`db028e6a4262`) trained on paired negatives whose withheld window had k - 1 lines,
|
|
504
|
+
while every protocol window has k. Four claim rates, greedy decoding, gate on:
|
|
505
|
+
planted_withheld_short : the planted fact, its own episode withheld, k - 1 lines (the v5 negative as trained)
|
|
506
|
+
planted_withheld_kept : the same, k lines (the k + 1 th match fills the slot: the v6 negative)
|
|
507
|
+
absent_k_lines : a never planted entity, k lines (the protocol's negative control)
|
|
508
|
+
absent_short : the same, the best match dropped, k - 1 lines
|
|
509
|
+
A decision that reads the window claims alike at k - 1 and k lines; a decision that
|
|
510
|
+
counts lines claims at k and abstains at k - 1."""
|
|
511
|
+
rng = np.random.default_rng(seed + 3)
|
|
512
|
+
model.eval()
|
|
513
|
+
|
|
514
|
+
def claims(items, query):
|
|
515
|
+
window, labels = build_read_window(items, bridge.budget, rng)
|
|
516
|
+
wt, wm = bridge.window_tensors([window], window_len)
|
|
517
|
+
toks, _ = generate(model, query, wt, wm, gate=1.0)
|
|
518
|
+
kind, _, _ = parse_contract(toks)
|
|
519
|
+
return int(kind != "unknown"), len(labels)
|
|
520
|
+
|
|
521
|
+
from cortex_c2b.organ_use import withheld_items
|
|
522
|
+
out = {"planted_withheld_short": [0, 0], "planted_withheld_kept": [0, 0],
|
|
523
|
+
"absent_k_lines": [0, 0], "absent_short": [0, 0]}
|
|
524
|
+
lines = {k: [] for k in out}
|
|
525
|
+
own_retrieved = 0
|
|
526
|
+
bridge.pointer_of.update(pointer_of) # the fill of the kept shape draws among the planted
|
|
527
|
+
for f in facts:
|
|
528
|
+
own = pointer_of.get(f.entity)
|
|
529
|
+
if own is None:
|
|
530
|
+
continue
|
|
531
|
+
query = encode_query(f.query)
|
|
532
|
+
cue = bridge.cues([query])[0]
|
|
533
|
+
if own not in bridge.read(cue).pointers:
|
|
534
|
+
continue # only the pairs whose halves differ in shape
|
|
535
|
+
own_retrieved += 1
|
|
536
|
+
short = withheld_items(bridge, cue, own, rng, keep_shape=False) # as v5 built it
|
|
537
|
+
kept = withheld_items(bridge, cue, own, rng, keep_shape=True) # as v6 builds it
|
|
538
|
+
for name, items in (("planted_withheld_short", short), ("planted_withheld_kept", kept)):
|
|
539
|
+
c, n_lines = claims(items, query)
|
|
540
|
+
out[name][0] += c; out[name][1] += 1; lines[name].append(n_lines)
|
|
541
|
+
for f in neg:
|
|
542
|
+
query = encode_query(f.query)
|
|
543
|
+
cue = bridge.cues([query])[0]
|
|
544
|
+
hits = bridge.jp.retrieve(cue, k=bridge.k)
|
|
545
|
+
top = [(e.pointer, payload) for e, payload, _ in hits]
|
|
546
|
+
for name, items in (("absent_k_lines", top), ("absent_short", top[1:])):
|
|
547
|
+
c, n_lines = claims(items, query)
|
|
548
|
+
out[name][0] += c; out[name][1] += 1; lines[name].append(n_lines)
|
|
549
|
+
return {"probe": "shape (lines in the window) -- a diagnostic, not a protocol measure",
|
|
550
|
+
"k": bridge.k, "pairs_with_own_retrieved": own_retrieved,
|
|
551
|
+
**{name: {"claim_rate": (c / n if n else None), "n": n,
|
|
552
|
+
"lines_mean": (float(np.mean(lines[name])) if lines[name] else None)}
|
|
553
|
+
for name, (c, n) in out.items()}}
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
@torch.no_grad()
|
|
557
|
+
def _head_inputs(model, bridge: JournalBridge, window, window_len: int, query: list[int]):
|
|
558
|
+
"""The two vectors the matching head receives at the decision position, for one query
|
|
559
|
+
and one window: the residual stream before the reader and the reader's attended values."""
|
|
560
|
+
wt, wm = bridge.window_tensors([window], window_len)
|
|
561
|
+
idx = torch.tensor([query], dtype=torch.long, device=next(model.parameters()).device)
|
|
562
|
+
model(idx[:, -model.cfg.block_size:], window_tokens=wt, window_mask=wm, gate=1.0)
|
|
563
|
+
blk = model.blocks[model.cfg.journal_block]
|
|
564
|
+
pre, att = blk.last_pre[0, -1], blk.last_attended[0, -1]
|
|
565
|
+
return torch.cat([pre, att]).float().cpu().numpy()
|
|
566
|
+
|
|
567
|
+
|
|
568
|
+
def representation_probe(model, bridge: JournalBridge, facts: list[Fact], window_len: int,
|
|
569
|
+
pointer_of: dict[str, str], seed: int = 0, steps: int = 400, lr: float = 0.5) -> dict:
|
|
570
|
+
"""A post hoc linear probe (logistic regression, numpy) on the two vectors the matching
|
|
571
|
+
head receives, over minimal pairs of the given facts: the plain read (present, when the
|
|
572
|
+
own episode was retrieved) against the same read with the own episode withheld and the
|
|
573
|
+
shape kept (absent). Half the pairs train the probe, the other half test it. Reads: a
|
|
574
|
+
test accuracy well above 0.5 means the comparison is linearly extractable from those
|
|
575
|
+
vectors and the trained head's constant answer is a fault of the coupled training; an
|
|
576
|
+
accuracy near 0.5 means the reader's representation does not carry it."""
|
|
577
|
+
from cortex_c2b.organ_use import withheld_items
|
|
578
|
+
rng = np.random.default_rng(seed + 5)
|
|
579
|
+
model.eval()
|
|
580
|
+
bridge.pointer_of.update(pointer_of)
|
|
581
|
+
X, y = [], []
|
|
582
|
+
for f in facts:
|
|
583
|
+
own = pointer_of.get(f.entity)
|
|
584
|
+
if own is None:
|
|
585
|
+
continue
|
|
586
|
+
query = encode_query(f.query)
|
|
587
|
+
cue = bridge.cues([query])[0]
|
|
588
|
+
read = bridge.read(cue)
|
|
589
|
+
if own not in read.pointers:
|
|
590
|
+
continue
|
|
591
|
+
X.append(_head_inputs(model, bridge, read.window, window_len, query)); y.append(1.0)
|
|
592
|
+
items = withheld_items(bridge, cue, own, rng, keep_shape=True)
|
|
593
|
+
window, _ = build_read_window(items, bridge.budget, rng)
|
|
594
|
+
X.append(_head_inputs(model, bridge, window, window_len, query)); y.append(0.0)
|
|
595
|
+
X = np.asarray(X, dtype=np.float64); y = np.asarray(y, dtype=np.float64)
|
|
596
|
+
n = len(y)
|
|
597
|
+
if n < 8:
|
|
598
|
+
return {"probe": "representation (linear, post hoc)", "n_pairs": n // 2, "note": "too few pairs"}
|
|
599
|
+
mu, sd = X.mean(0), X.std(0) + 1e-6
|
|
600
|
+
Xn = (X - mu) / sd
|
|
601
|
+
order = rng.permutation(n // 2) # split by pair, never by example
|
|
602
|
+
train_pairs, test_pairs = order[: len(order) // 2], order[len(order) // 2:]
|
|
603
|
+
tr = np.concatenate([[2 * i, 2 * i + 1] for i in train_pairs]); te = np.concatenate([[2 * i, 2 * i + 1] for i in test_pairs])
|
|
604
|
+
w = np.zeros(Xn.shape[1]); b = 0.0
|
|
605
|
+
for _ in range(steps): # plain gradient descent with a small ridge
|
|
606
|
+
z = Xn[tr] @ w + b; p = 1.0 / (1.0 + np.exp(-z))
|
|
607
|
+
g = p - y[tr]
|
|
608
|
+
w -= lr * (Xn[tr].T @ g / len(tr) + 1e-3 * w); b -= lr * g.mean()
|
|
609
|
+
def acc(ix):
|
|
610
|
+
return float((((Xn[ix] @ w + b) > 0).astype(float) == y[ix]).mean())
|
|
611
|
+
return {"probe": "representation (linear, post hoc): logistic regression on the head's two vectors over minimal pairs",
|
|
612
|
+
"n_pairs": n // 2, "train_pairs": len(train_pairs), "test_pairs": len(test_pairs),
|
|
613
|
+
"train_accuracy": acc(tr), "test_accuracy": acc(te), "chance": 0.5,
|
|
614
|
+
"reading": ("the comparison is linearly extractable from the head's vectors: the trained head's constant is a fault of the coupled training"
|
|
615
|
+
if acc(te) >= 0.8 else
|
|
616
|
+
"the comparison is not linearly extractable from the head's vectors at this size: the reader's representation does not carry it"
|
|
617
|
+
if acc(te) <= 0.6 else "partially extractable; neither reading is clean")}
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def representation_probe_from_disk(ckpt_path, journal_path, cfg, n_facts: int = N_FACTS, seed: int = 0,
|
|
621
|
+
k: int = 4, budget: int = 256, window_len: int = 256) -> dict:
|
|
622
|
+
import train
|
|
623
|
+
from cortex_c2b import content_hash
|
|
624
|
+
from cortex_c2b.crypto import key_from_env
|
|
625
|
+
device = torch.device("cpu")
|
|
626
|
+
model = train.VanillaGPT(cfg).to(device)
|
|
627
|
+
ck = torch.load(ckpt_path, map_location=device, weights_only=False)
|
|
628
|
+
model.load_state_dict(ck["model"]); model.eval()
|
|
629
|
+
journal = Journal(journal_path, key=key_from_env(), policy=POLICY_STOP)
|
|
630
|
+
bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1, device=device)
|
|
631
|
+
facts, gen_hash = generate_facts(n_facts, seed)
|
|
632
|
+
pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
|
|
633
|
+
if content_hash(f.statement.encode("utf-8")) in journal.payloads}
|
|
634
|
+
r = representation_probe(model, bridge, facts, window_len, pointer_of, seed=seed)
|
|
635
|
+
r["checkpoint"] = {"path": str(ckpt_path), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash"), "step": ck.get("step")}
|
|
636
|
+
r["generator"] = {"config_hash": gen_hash, "n_facts": n_facts, "seed": seed}
|
|
637
|
+
return r
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def shape_probe_from_disk(ckpt_path, journal_path, cfg, n_facts: int = N_FACTS, n_negctrl: int = N_NEGCTRL,
|
|
641
|
+
seed: int = 0, k: int = 4, budget: int = 256, window_len: int = 256) -> dict:
|
|
642
|
+
"""The shape probe from the disk alone (checkpoint, sealed journal, key in the environment)."""
|
|
643
|
+
import train
|
|
644
|
+
from cortex_c2b import content_hash
|
|
645
|
+
from cortex_c2b.crypto import key_from_env
|
|
646
|
+
device = torch.device("cpu")
|
|
647
|
+
model = train.VanillaGPT(cfg).to(device)
|
|
648
|
+
ck = torch.load(ckpt_path, map_location=device, weights_only=False)
|
|
649
|
+
model.load_state_dict(ck["model"]); model.eval()
|
|
650
|
+
journal = Journal(journal_path, key=key_from_env(), policy=POLICY_STOP)
|
|
651
|
+
bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1, device=device)
|
|
652
|
+
facts, gen_hash = generate_facts(n_facts, seed)
|
|
653
|
+
neg, _ = generate_facts(n_negctrl, seed + 10_000)
|
|
654
|
+
pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
|
|
655
|
+
if content_hash(f.statement.encode("utf-8")) in journal.payloads}
|
|
656
|
+
r = shape_probe(model, bridge, facts, neg, window_len, pointer_of, seed=seed)
|
|
657
|
+
r["checkpoint"] = {"path": str(ckpt_path), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash"),
|
|
658
|
+
"step": ck.get("step")}
|
|
659
|
+
r["generator"] = {"config_hash": gen_hash, "n_facts": n_facts, "n_negctrl": n_negctrl, "seed": seed}
|
|
660
|
+
r["journal"] = {"path": str(journal_path), "entries": len(journal), "planted_found": len(pointer_of)}
|
|
661
|
+
return r
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def _verdict(on: dict, off: dict, neg_on: dict, floor: float) -> dict:
|
|
665
|
+
"""The four gate conditions a new process can evaluate without the skill arm (the skill
|
|
666
|
+
delta is a property of the model on ordinary text and does not depend on the decode
|
|
667
|
+
policy; the in process value stands): gap >= delta, off <= floor, invalid citations
|
|
668
|
+
<= 0.01, claims on never planted entities < 0.10."""
|
|
669
|
+
gap = on["recall_strict"] - off["recall_strict"]
|
|
670
|
+
valid = neg_on["guess_rate"] < NEGCTRL_INVALID
|
|
671
|
+
four = (gap >= DELTA and off["recall_strict"] <= floor and on["invalid_citation_rate"] <= INVALID_CITATION_MAX and valid)
|
|
672
|
+
return {"hm_gap": gap, "run_valid": valid, "gate_conditions_without_skill_arm": int(four),
|
|
673
|
+
"verdict_without_skill_arm": ("PASS" if four else ("INVALID" if not valid else "FAIL")) + " -- the skill delta is the in process arm's"}
|
|
674
|
+
|
|
675
|
+
|
|
676
|
+
def session_b_from_disk(ckpt_path, journal_path, cfg, n_facts: int = N_FACTS, n_negctrl: int = N_NEGCTRL,
|
|
677
|
+
seed: int = 0, k: int = 4, budget: int = 256, window_len: int = 256,
|
|
678
|
+
train_pool_seed: int | None = None, train_pool_n: int = 200, policy: str = "plain") -> dict:
|
|
679
|
+
"""Reopen everything from the disk alone and probe. The planted facts are found
|
|
680
|
+
by content addressing (a statement's pointer is the hash of its bytes), so no
|
|
681
|
+
state of session A is needed beyond the journal file and the checkpoint."""
|
|
682
|
+
import train
|
|
683
|
+
from cortex_c2b import content_hash
|
|
684
|
+
from cortex_c2b.crypto import key_from_env
|
|
685
|
+
device = torch.device("cpu")
|
|
686
|
+
model = train.VanillaGPT(cfg).to(device)
|
|
687
|
+
ck = torch.load(ckpt_path, map_location=device, weights_only=False)
|
|
688
|
+
model.load_state_dict(ck["model"]); model.eval()
|
|
689
|
+
journal = Journal(journal_path, key=key_from_env(), policy=POLICY_STOP)
|
|
690
|
+
mark = bool(getattr(cfg, "journal_familiarity_mark", False))
|
|
691
|
+
bridge = JournalBridge(model, journal, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1, device=device,
|
|
692
|
+
mark=mark)
|
|
693
|
+
facts, gen_hash = generate_facts(n_facts, seed)
|
|
694
|
+
neg, _ = generate_facts(n_negctrl, seed + 10_000)
|
|
695
|
+
pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
|
|
696
|
+
if content_hash(f.statement.encode("utf-8")) in journal.payloads}
|
|
697
|
+
thr = mark_oracle(model, bridge, facts, neg, pointer_of)["threshold"] if policy.startswith("mark-veto") else None
|
|
698
|
+
on = _probe(model, bridge, facts, True, window_len, pointer_of, policy=policy, veto_threshold=thr)
|
|
699
|
+
off = _probe(model, bridge, facts, False, window_len, pointer_of, policy=policy, veto_threshold=thr)
|
|
700
|
+
neg_on = _probe(model, bridge, neg, True, window_len, {}, policy=policy, veto_threshold=thr)
|
|
701
|
+
storage = lifecycle_declaration(journal)["storage"]
|
|
702
|
+
# the memorisation probe (run dc34fcf000aa): the same contract on entities the model SAW during training
|
|
703
|
+
# (the first training pool), planted into a memory scope; a model that reads its window scores alike on
|
|
704
|
+
# seen and unseen entities, a model that memorised entity -> attribute scores far higher on the seen ones
|
|
705
|
+
train_probe = None
|
|
706
|
+
if train_pool_seed is not None:
|
|
707
|
+
from cortex_c2b.organ_use import training_facts, plant_pool
|
|
708
|
+
tf, _, _ = training_facts(train_pool_n, seed=train_pool_seed)
|
|
709
|
+
jb2 = JournalBridge(model, Journal(), k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 2, device=device)
|
|
710
|
+
plant_pool(jb2, tf)
|
|
711
|
+
tp = _probe(model, jb2, tf, True, window_len, dict(jb2.pointer_of), policy=policy)
|
|
712
|
+
train_probe = {"seed": train_pool_seed, "n": len(tf), "recall_strict": tp["recall_strict"],
|
|
713
|
+
"valid_citation": tp["valid_citation_rate"], "invalid_citation": tp["invalid_citation_rate"],
|
|
714
|
+
"abstain_rate": tp["abstain_rate"], "retrieval_hit": tp["retrieval_hit"]}
|
|
715
|
+
chance = float(np.mean([1.0 / len(_ATTRS[s]) for s in SCHEMAS])); floor = chance + FLOOR_MARGIN
|
|
716
|
+
verdict_fields = _verdict(on, off, neg_on, floor)
|
|
717
|
+
oracle = mark_oracle(model, bridge, facts, neg, pointer_of)
|
|
718
|
+
return {"benchmark": "hm_protocol_lm_session_b_new_process", "policy": policy, **verdict_fields,
|
|
719
|
+
"hm_marks": {"on": on["marks"], "negctrl": neg_on["marks"], "written_in_window": mark},
|
|
720
|
+
"hm_mark_oracle": oracle,
|
|
721
|
+
"answers": {"on": on["answers"], "off": off["answers"], "negctrl": neg_on["answers"]},
|
|
722
|
+
"training_pool_probe": train_probe,
|
|
723
|
+
"checkpoint": {"path": str(ckpt_path), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash"),
|
|
724
|
+
"step": ck.get("step")},
|
|
725
|
+
"journal": {"path": str(journal_path), "entries": len(journal), "mode": storage["mode"],
|
|
726
|
+
"policy": storage["policy"], "planted_found": len(pointer_of)},
|
|
727
|
+
"generator": {"config_hash": gen_hash, "n_facts": n_facts, "n_negctrl": n_negctrl, "seed": seed},
|
|
728
|
+
"hm_recall_on": on["recall_strict"], "hm_recall_off": off["recall_strict"],
|
|
729
|
+
"hm_false_abstention_on": on["abstain_rate"], "hm_invalid_citation_on": on["invalid_citation_rate"],
|
|
730
|
+
"hm_valid_citation_on": on["valid_citation_rate"], "hm_attr_exact_given_valid": on["attr_exact_given_valid"],
|
|
731
|
+
"hm_retrieval_hit": on["retrieval_hit"], "hm_attention_mass": on["attention_mass"],
|
|
732
|
+
"hm_negctrl_rate": neg_on["guess_rate"], "hm_negctrl_abstain_on": neg_on["abstain_rate"],
|
|
733
|
+
"hm_match_acc_on": on["match_acc"], "hm_match_acc_negctrl": neg_on["match_acc"],
|
|
734
|
+
"persistent": storage["mode"] == MODE_DURABLE, "process": "new (nothing of session A but the disk)"}
|
|
735
|
+
|
|
736
|
+
|
|
737
|
+
|
|
738
|
+
# ---------------------------------------------------------------------------- #
|
|
739
|
+
# The extraction probe (ADR-008 amendment 2026-09-30, declared before any #
|
|
740
|
+
# measurement): one domain's questions against another domain's sealed journal, #
|
|
741
|
+
# read at the organ level and at the system level, never mixed #
|
|
742
|
+
# ---------------------------------------------------------------------------- #
|
|
743
|
+
EXTRACTION_COMMIT_PREFIX = "quantum-cortex extraction probe, domain B seed: "
|
|
744
|
+
EXTRACTION_SEED_COMMITMENTS = ("6942036613cde5fa",) # ADR-008, 2026-09-30: the hash written before the run; the seed after
|
|
745
|
+
|
|
746
|
+
|
|
747
|
+
def seed_commitment(seed: int) -> str:
|
|
748
|
+
"""The commitment of a domain B seed: sha256 of the declared prefix and the seed, sixteen hex characters."""
|
|
749
|
+
import hashlib
|
|
750
|
+
return hashlib.sha256(f"{EXTRACTION_COMMIT_PREFIX}{int(seed)}".encode("utf-8")).hexdigest()[:16]
|
|
751
|
+
|
|
752
|
+
|
|
753
|
+
def _arm_scalars(arm: dict) -> dict:
|
|
754
|
+
"""An arm without its per line answers (kept separately)."""
|
|
755
|
+
return {k: v for k, v in arm.items() if k != "answers"}
|
|
756
|
+
|
|
757
|
+
|
|
758
|
+
@torch.no_grad()
|
|
759
|
+
def extraction_probe_from_disk(ckpt_path, journal_path, cfg, domain_b_seed: int, n_facts: int = N_FACTS,
|
|
760
|
+
n_negctrl: int = N_NEGCTRL, seed: int = 0, k: int = 4, budget: int = 256,
|
|
761
|
+
window_len: int = 256, policies: tuple = ("plain", "mark-veto+value"),
|
|
762
|
+
commitments: tuple = EXTRACTION_SEED_COMMITMENTS) -> dict:
|
|
763
|
+
"""ADR-008 amendment 2026-09-30. On a checkpoint and its sealed journal (domain A, the
|
|
764
|
+
protocol's 200 facts), plant a second domain B (the same generator, a committed seed,
|
|
765
|
+
entities disjoint from A and from the never planted control) into a fresh journal in a
|
|
766
|
+
temporary directory under a throwaway key, then run two arms over the questions of A:
|
|
767
|
+
P, with the journal of A open (session B's own arm through this code path, which must
|
|
768
|
+
reproduce the record to the thousandth), and X, with the journal of B open and the
|
|
769
|
+
journal of A never touched by the arm. X is read at two levels, never mixed: the organ
|
|
770
|
+
(no pointer of A in B's store nor in any read of A's questions on B: a construction
|
|
771
|
+
assertion, exactly zero or the probe aborts) and the system (the claim rate against
|
|
772
|
+
the frozen negative control line, the strict recall of A against the frozen floor of
|
|
773
|
+
the OFF arm, the highest mark of a line of B for a question of A against the oracle's
|
|
774
|
+
threshold). No new threshold. The seed of B must match a commitment written in
|
|
775
|
+
ADR-008 before the run; the journal of B and its key are discarded after."""
|
|
776
|
+
import shutil
|
|
777
|
+
import tempfile
|
|
778
|
+
from pathlib import Path
|
|
779
|
+
import train
|
|
780
|
+
from cortex_c2b import content_hash
|
|
781
|
+
from cortex_c2b.crypto import key_from_env, generate_key
|
|
782
|
+
if seed_commitment(domain_b_seed) not in commitments:
|
|
783
|
+
raise SystemExit("extraction probe: the domain B seed does not match a commitment written in ADR-008 "
|
|
784
|
+
"before the run; nothing was loaded and nothing runs")
|
|
785
|
+
if int(domain_b_seed) in (seed, seed + 10_000, 100_000):
|
|
786
|
+
raise SystemExit("extraction probe: the domain B seed must differ from domain A's, from the never planted "
|
|
787
|
+
"control's and from the oracle's training family's")
|
|
788
|
+
device = torch.device("cpu")
|
|
789
|
+
model = train.VanillaGPT(cfg).to(device)
|
|
790
|
+
ck = torch.load(ckpt_path, map_location=device, weights_only=False)
|
|
791
|
+
model.load_state_dict(ck["model"]); model.eval()
|
|
792
|
+
mark = bool(getattr(cfg, "journal_familiarity_mark", False))
|
|
793
|
+
journal_a = Journal(journal_path, key=key_from_env(), policy=POLICY_STOP)
|
|
794
|
+
facts_a, gen_hash_a = generate_facts(n_facts, seed)
|
|
795
|
+
neg, _ = generate_facts(n_negctrl, seed + 10_000)
|
|
796
|
+
pointers_a = {content_hash(f.statement.encode("utf-8")) for f in facts_a}
|
|
797
|
+
pointer_of_a = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts_a
|
|
798
|
+
if content_hash(f.statement.encode("utf-8")) in journal_a.payloads}
|
|
799
|
+
# ---- domain B: the same generator, the committed seed, disjoint by entity from A and from the control
|
|
800
|
+
facts_b, gen_hash_b = generate_facts(n_facts, int(domain_b_seed))
|
|
801
|
+
known = {f.entity for f in facts_a} | {f.entity for f in neg}
|
|
802
|
+
collisions = sorted({f.entity for f in facts_b if f.entity in known})
|
|
803
|
+
if collisions:
|
|
804
|
+
raise SystemExit(f"extraction probe: domain B collides with domain A or the never planted control on "
|
|
805
|
+
f"{len(collisions)} entities (first: {collisions[:3]}); aborted, the next committed seed is used")
|
|
806
|
+
tmp = Path(tempfile.mkdtemp(prefix="quantum-cortex-extraction-domain-b-"))
|
|
807
|
+
key_b = generate_key() # a throwaway key: generated here, never written, discarded with the directory
|
|
808
|
+
try:
|
|
809
|
+
journal_b = Journal(tmp / "journal.jsonl", key=key_b, policy=POLICY_STOP)
|
|
810
|
+
planter = JournalBridge(model, journal_b, k=k, budget_bytes=budget, seed=seed, device=device, mark=mark)
|
|
811
|
+
admitted_b = 0
|
|
812
|
+
for i, f in enumerate(facts_b): # the normal gated write path, this checkpoint's cues, as session A wrote A
|
|
813
|
+
admitted_b += int(planter.write(f.statement, f.schema, now=float(i), entity=f.entity).admitted)
|
|
814
|
+
del planter, journal_b
|
|
815
|
+
journal_b = Journal(tmp / "journal.jsonl", key=key_b, policy=POLICY_STOP) # reopened from the disk, as A is
|
|
816
|
+
# ---- the organ level: none of A's pointers in B's store, none in any read of A's questions on B
|
|
817
|
+
in_store = sorted(p for p in pointers_a if p in journal_b.payloads)
|
|
818
|
+
reader_b = JournalBridge(model, journal_b, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1,
|
|
819
|
+
device=device, mark=mark)
|
|
820
|
+
in_reads = 0
|
|
821
|
+
b_marks_for_a: list[float] = []
|
|
822
|
+
for f in facts_a:
|
|
823
|
+
r = reader_b.read(reader_b.cues([encode_query(f.query)])[0])
|
|
824
|
+
in_reads += len(set(r.pointers) & pointers_a)
|
|
825
|
+
if r.scores:
|
|
826
|
+
b_marks_for_a.append(float(max(r.scores)))
|
|
827
|
+
organ = {"a_pointers_in_b_store": len(in_store), "a_pointers_in_reads_of_b": in_reads,
|
|
828
|
+
"queries": len(facts_a), "b_entries": len(journal_b), "b_admitted": admitted_b,
|
|
829
|
+
"max_mark_of_b_line_for_a_query": _stats(b_marks_for_a), "reads_with_lines": len(b_marks_for_a)}
|
|
830
|
+
if in_store or in_reads:
|
|
831
|
+
raise SystemExit("extraction probe: the organ returned a pointer of domain A from the journal of domain B; "
|
|
832
|
+
"a construction failure, nothing is read")
|
|
833
|
+
# ---- the system level, per policy: P on A and X on B, the same shuffle seed, the same threshold
|
|
834
|
+
system = {}
|
|
835
|
+
for policy in policies:
|
|
836
|
+
bridge_a = JournalBridge(model, journal_a, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1,
|
|
837
|
+
device=device, mark=mark)
|
|
838
|
+
thr = (mark_oracle(model, bridge_a, facts_a, neg, pointer_of_a)["threshold"]
|
|
839
|
+
if policy.startswith("mark-veto") else None)
|
|
840
|
+
P = _probe(model, bridge_a, facts_a, True, window_len, pointer_of_a, policy=policy, veto_threshold=thr)
|
|
841
|
+
bridge_b = JournalBridge(model, journal_b, k=k, budget_bytes=budget, seed=seed, shuffle_seed=seed + 1,
|
|
842
|
+
device=device, mark=mark)
|
|
843
|
+
X = _probe(model, bridge_b, facts_a, True, window_len, pointer_of_a, policy=policy, veto_threshold=thr)
|
|
844
|
+
above = (sum(1 for a in X["answers"] if a.get("mark_max") is not None and a["mark_max"] > thr)
|
|
845
|
+
if thr is not None else None)
|
|
846
|
+
n = max(1, len(facts_a))
|
|
847
|
+
system[policy] = {
|
|
848
|
+
"veto_threshold": thr,
|
|
849
|
+
"P_same_domain": _arm_scalars(P), "X_cross_domain": _arm_scalars(X),
|
|
850
|
+
"X_claims": X["guess_rate"], "X_claims_under_negctrl_line": bool(X["guess_rate"] < NEGCTRL_INVALID),
|
|
851
|
+
"X_strict_recall_of_a": X["recall_strict"],
|
|
852
|
+
"X_coincident_strict_hits": int(round(X["recall_strict"] * n)),
|
|
853
|
+
"X_b_lines_above_threshold": above,
|
|
854
|
+
"answers": {"P": P["answers"], "X": X["answers"]}}
|
|
855
|
+
finally:
|
|
856
|
+
shutil.rmtree(tmp, ignore_errors=True) # the journal of B and its key leave no trace
|
|
857
|
+
chance = float(np.mean([1.0 / len(_ATTRS[s]) for s in SCHEMAS])); floor = chance + FLOOR_MARGIN
|
|
858
|
+
for policy in system:
|
|
859
|
+
system[policy]["X_strict_recall_under_floor"] = bool(system[policy]["X_strict_recall_of_a"] <= floor)
|
|
860
|
+
storage = lifecycle_declaration(journal_a)["storage"]
|
|
861
|
+
return {"benchmark": "extraction_probe", "spec": "ADR-008 amendment 2026-09-30 (declared before any measurement)",
|
|
862
|
+
"thresholds": {"negctrl_invalid": NEGCTRL_INVALID, "floor": floor, "chance": chance,
|
|
863
|
+
"invalid_citation_max": INVALID_CITATION_MAX, "organ_level": 0,
|
|
864
|
+
"note": "none new: the four numbers frozen on 2026-09-12"},
|
|
865
|
+
"domain_a": {"generator_config_hash": gen_hash_a, "seed": seed, "n_facts": n_facts,
|
|
866
|
+
"planted_found": len(pointer_of_a), "n_negctrl_for_oracle": n_negctrl},
|
|
867
|
+
"domain_b": {"seed": int(domain_b_seed), "commitment": seed_commitment(domain_b_seed),
|
|
868
|
+
"generator_config_hash": gen_hash_b, "n_facts": n_facts, "admitted": admitted_b,
|
|
869
|
+
"entities_disjoint_from_a_and_control": True, "sealed": True,
|
|
870
|
+
"key": "throwaway, generated in the process, never written, discarded with the directory"},
|
|
871
|
+
"organ_level": organ, "system_level": system, "policies": list(policies),
|
|
872
|
+
"checkpoint": {"path": str(ckpt_path), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash"),
|
|
873
|
+
"step": ck.get("step")},
|
|
874
|
+
"journal": {"path": str(journal_path), "entries": len(journal_a), "mode": storage["mode"],
|
|
875
|
+
"policy": storage["policy"]},
|
|
876
|
+
"persistent": storage["mode"] == MODE_DURABLE,
|
|
877
|
+
"process": "new (nothing of session A but the disk); domain B planted, reopened from its disk and deleted in this process"}
|
|
878
|
+
|
|
879
|
+
def _cli():
|
|
880
|
+
import argparse, json
|
|
881
|
+
from pathlib import Path
|
|
882
|
+
import train
|
|
883
|
+
ap = argparse.ArgumentParser(description="the H.M. protocol's LM arm, session B in a new process")
|
|
884
|
+
ap.add_argument("--session-b", action="store_true")
|
|
885
|
+
ap.add_argument("--retrieval-probe", action="store_true", help="ADR-008 amendment 2026-09-19: the retrieval policy probe on a checkpoint and its sealed journal (no generation, no training)")
|
|
886
|
+
ap.add_argument("--extraction-probe", action="store_true", help="ADR-008 amendment 2026-09-30: the extraction probe, the questions of domain A against the sealed journal of a second domain B, on a checkpoint and its sealed journal (no training)")
|
|
887
|
+
ap.add_argument("--domain-b-seed", type=int, default=None, help="with --extraction-probe: the seed of domain B, committed by its hash in ADR-008 before the run and revealed with the run")
|
|
888
|
+
ap.add_argument("--replay", default=None, help="post hoc: apply --policy to a session B answers file (Rev66) and print the rates; never a result on the seed the policy was conceived on")
|
|
889
|
+
ap.add_argument("--threshold", type=float, default=None, help="with --replay: override the oracle's veto threshold (a sensitivity sweep)")
|
|
890
|
+
ap.add_argument("--shape-probe", action="store_true",
|
|
891
|
+
help="the shape probe alone (claims at k - 1 against k lines), a diagnostic: metrics/mqar/shape-probe-<run_id>.json")
|
|
892
|
+
ap.add_argument("--representation-probe", action="store_true",
|
|
893
|
+
help="a post hoc linear probe on the matching head's two vectors over minimal pairs: metrics/mqar/representation-probe-<run_id>.json")
|
|
894
|
+
ap.add_argument("--ckpt", required=True); ap.add_argument("--journal", required=True)
|
|
895
|
+
ap.add_argument("--config", required=True, help="the run's config json (the model's shape)")
|
|
896
|
+
ap.add_argument("--out", default=None); ap.add_argument("--seed", type=int, default=0)
|
|
897
|
+
ap.add_argument("--n-facts", type=int, default=N_FACTS); ap.add_argument("--n-negctrl", type=int, default=N_NEGCTRL)
|
|
898
|
+
ap.add_argument("--probe-training-pool", action="store_true",
|
|
899
|
+
help="also probe the contract on the run's FIRST training pool (entities seen in training): memorisation evidence")
|
|
900
|
+
ap.add_argument("--policy", default="plain", choices=list(POLICIES),
|
|
901
|
+
help="decode policy: plain | head | head+pointer (ADR-008 amendment 2026-09-14) | mark-veto | mark-veto+value (amendment 2026-09-18)")
|
|
902
|
+
args = ap.parse_args()
|
|
903
|
+
if not (args.session_b or args.shape_probe or args.representation_probe or args.retrieval_probe or args.extraction_probe):
|
|
904
|
+
if args.replay:
|
|
905
|
+
r = replay_policy(json.loads(Path(args.replay).read_text(encoding="utf-8")), args.policy, threshold=args.threshold)
|
|
906
|
+
print(json.dumps(r, indent=2)); return
|
|
907
|
+
ap.error("one of --session-b / --shape-probe / --representation-probe / --retrieval-probe / --extraction-probe / --replay is required")
|
|
908
|
+
cfg = train.load_config(args.config)
|
|
909
|
+
if args.extraction_probe: # ADR-008 amendment 2026-09-30
|
|
910
|
+
if args.domain_b_seed is None:
|
|
911
|
+
ap.error("--extraction-probe needs --domain-b-seed (the committed seed, revealed with the run)")
|
|
912
|
+
r = extraction_probe_from_disk(args.ckpt, args.journal, cfg, args.domain_b_seed, n_facts=args.n_facts,
|
|
913
|
+
n_negctrl=args.n_negctrl, seed=args.seed, k=cfg.journal_k,
|
|
914
|
+
budget=cfg.journal_read_bytes, window_len=cfg.journal_read_bytes)
|
|
915
|
+
out = Path(args.out) if args.out else Path("metrics/mqar") / f"extraction-probe-{r['checkpoint']['run_id']}.json"
|
|
916
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
917
|
+
out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
|
|
918
|
+
print(f"retained: {out}")
|
|
919
|
+
short = {"checkpoint": r["checkpoint"],
|
|
920
|
+
"domain_b": {k2: r["domain_b"][k2] for k2 in ("seed", "commitment", "admitted")},
|
|
921
|
+
"organ_level": {k2: r["organ_level"][k2] for k2 in ("a_pointers_in_b_store", "a_pointers_in_reads_of_b", "queries")},
|
|
922
|
+
"system_level": {}}
|
|
923
|
+
for pol, d in r["system_level"].items():
|
|
924
|
+
short["system_level"][pol] = {k2: v for k2, v in d.items() if k2 in (
|
|
925
|
+
"veto_threshold", "X_claims", "X_claims_under_negctrl_line", "X_strict_recall_of_a",
|
|
926
|
+
"X_strict_recall_under_floor", "X_coincident_strict_hits", "X_b_lines_above_threshold")}
|
|
927
|
+
short["system_level"][pol]["P"] = {k3: d["P_same_domain"][k3] for k3 in (
|
|
928
|
+
"recall_strict", "invalid_citation_rate", "abstain_rate", "valid_citation_rate")}
|
|
929
|
+
print(json.dumps(short, indent=2))
|
|
930
|
+
return
|
|
931
|
+
if args.retrieval_probe: # ADR-008 amendment 2026-09-19 (RES-24)
|
|
932
|
+
from cortex_c2b.crypto import key_from_env
|
|
933
|
+
from cortex_c2b import content_hash
|
|
934
|
+
device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
|
|
935
|
+
model = train.VanillaGPT(cfg).to(device)
|
|
936
|
+
ck = torch.load(args.ckpt, map_location=device, weights_only=False)
|
|
937
|
+
model.load_state_dict(ck["model"]); model.eval()
|
|
938
|
+
journal = Journal(args.journal, key=key_from_env(), policy=POLICY_STOP)
|
|
939
|
+
facts, _ = generate_facts(args.n_facts, args.seed)
|
|
940
|
+
neg, _ = generate_facts(args.n_negctrl, args.seed + 10_000)
|
|
941
|
+
pointer_of = {f.entity: content_hash(f.statement.encode("utf-8")) for f in facts
|
|
942
|
+
if content_hash(f.statement.encode("utf-8")) in journal.payloads}
|
|
943
|
+
r = retrieval_probe(model, journal, facts, neg, pointer_of, k=cfg.journal_k, seed=args.seed)
|
|
944
|
+
r["checkpoint"] = {"path": str(args.ckpt), "run_id": ck.get("run_id"), "config_hash": ck.get("config_hash")}
|
|
945
|
+
r["journal"] = {"path": str(args.journal), "entries": len(journal._entries)}
|
|
946
|
+
out = Path(args.out) if args.out else Path("metrics/mqar") / f"retrieval-probe-{ck.get('run_id')}.json"
|
|
947
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
948
|
+
out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
|
|
949
|
+
print(f"retained: {out}")
|
|
950
|
+
print(json.dumps({"reading": r["reading"], "rankings": {k2: {kk: v2[kk] for kk in ("own_in_candidates", "own_in_top_k")} for k2, v2 in r["rankings"].items()}}, indent=2))
|
|
951
|
+
return
|
|
952
|
+
if args.representation_probe:
|
|
953
|
+
r = representation_probe_from_disk(args.ckpt, args.journal, cfg, n_facts=args.n_facts, seed=args.seed,
|
|
954
|
+
k=cfg.journal_k, budget=cfg.journal_read_bytes, window_len=cfg.journal_read_bytes)
|
|
955
|
+
out = Path(args.out) if args.out else Path("metrics/mqar") / f"representation-probe-{r['checkpoint']['run_id']}.json"
|
|
956
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
957
|
+
out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
|
|
958
|
+
print(f"retained: {out}")
|
|
959
|
+
print(json.dumps(r, indent=2))
|
|
960
|
+
return
|
|
961
|
+
if args.shape_probe:
|
|
962
|
+
r = shape_probe_from_disk(args.ckpt, args.journal, cfg, n_facts=args.n_facts, n_negctrl=args.n_negctrl, seed=args.seed,
|
|
963
|
+
k=cfg.journal_k, budget=cfg.journal_read_bytes, window_len=cfg.journal_read_bytes)
|
|
964
|
+
out = Path(args.out) if args.out else Path("metrics/mqar") / f"shape-probe-{r['checkpoint']['run_id']}.json"
|
|
965
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
966
|
+
out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
|
|
967
|
+
print(f"retained: {out}")
|
|
968
|
+
print(json.dumps(r, indent=2))
|
|
969
|
+
return
|
|
970
|
+
r = session_b_from_disk(args.ckpt, args.journal, cfg, n_facts=args.n_facts, n_negctrl=args.n_negctrl,
|
|
971
|
+
seed=args.seed, k=cfg.journal_k, budget=cfg.journal_read_bytes, window_len=cfg.journal_read_bytes,
|
|
972
|
+
train_pool_seed=(cfg.journal_train_seed if args.probe_training_pool else None),
|
|
973
|
+
train_pool_n=cfg.journal_pool_facts, policy=args.policy)
|
|
974
|
+
suffix = "" if args.policy == "plain" else "-" + args.policy.replace("+", "-")
|
|
975
|
+
out = Path(args.out) if args.out else Path("metrics/mqar") / f"hm-lm-{r['checkpoint']['run_id']}-session-b{suffix}.json"
|
|
976
|
+
out.parent.mkdir(parents=True, exist_ok=True)
|
|
977
|
+
out.write_text(json.dumps(r, indent=2) + "\n", encoding="utf-8")
|
|
978
|
+
print(f"retained: {out}")
|
|
979
|
+
print(json.dumps(r, indent=2))
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
if __name__ == "__main__":
|
|
983
|
+
_cli()
|