anima-python 0.13.1__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.
- anima_py/README.md +39 -0
- anima_py/__init__.py +28 -0
- anima_py/__main__.py +7 -0
- anima_py/cli/__init__.py +1 -0
- anima_py/cli/anima.py +271 -0
- anima_py/cli/corpus.py +150 -0
- anima_py/cli/evaluate.py +1350 -0
- anima_py/cli/rho_axon.py +801 -0
- anima_py/cli/serialize.py +174 -0
- anima_py/cli/serialize_bind.py +100 -0
- anima_py/cli/sweep.py +509 -0
- anima_py/cli/train.py +1667 -0
- anima_py/core/__init__.py +1 -0
- anima_py/core/brain.py +601 -0
- anima_py/core/clm_serialize_v2.py +486 -0
- anima_py/core/clml.py +155 -0
- anima_py/core/decode.py +1386 -0
- anima_py/core/engine_cli.py +10307 -0
- anima_py/core/engine_g.py +129 -0
- anima_py/core/generator.py +318 -0
- anima_py/core/hippo_lane.py +133 -0
- anima_py/core/model.py +462 -0
- anima_py/core/pure_field.py +334 -0
- anima_py/core/rho_fan.py +442 -0
- anima_py/core/serialize.py +826 -0
- anima_py/core/serialize_standalone.py +186 -0
- anima_py/core/slw.py +211 -0
- anima_py/core/verify_clm_v2.py +844 -0
- anima_python-0.13.1.dist-info/METADATA +52 -0
- anima_python-0.13.1.dist-info/RECORD +34 -0
- anima_python-0.13.1.dist-info/WHEEL +5 -0
- anima_python-0.13.1.dist-info/entry_points.txt +2 -0
- anima_python-0.13.1.dist-info/licenses/LICENSE +21 -0
- anima_python-0.13.1.dist-info/top_level.txt +1 -0
anima_py/cli/rho_axon.py
ADDED
|
@@ -0,0 +1,801 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""cli/rho_axon.py — ρ-AXON reach-layer measurement (Ψ-SOMA ρ · replaces G0-G6).
|
|
3
|
+
|
|
4
|
+
The reach (capability) layer of Ψ-SOMA — how far the soma's signal extends down the
|
|
5
|
+
axon. Says NOTHING about consciousness (σ owns that; the amoeba argument). Design SSOT:
|
|
6
|
+
state/rho_axon_measurement/RHO_AXON_design.md + ARCHITECTURE psi-soma-rho-reach subtree.
|
|
7
|
+
|
|
8
|
+
Organizing principle: rung n certifies exactly the generative resource rung n-1 lacks,
|
|
9
|
+
and the control for rung n is the ABLATION of that resource — a chain of nested ablations
|
|
10
|
+
ordered by informational distance from the training distribution. **Signal = Δ vs ≥2
|
|
11
|
+
controls, never a raw value** (FORM tunable · BIND earned): an AxisResult has no raw-only
|
|
12
|
+
verdict path — a PASS requires the margin over its controls, so a game-able detector is
|
|
13
|
+
structurally unable to produce a PASS.
|
|
14
|
+
|
|
15
|
+
Strata / axes:
|
|
16
|
+
HILLOCK (validity gate · LIVE/INVALID, unscored)
|
|
17
|
+
CARRY ρ·form (well-formed, 4 cells) · ρ·store (held-out association retrieval)
|
|
18
|
+
BRANCH ρ·weave (recombination — wall) · ρ·leap (corpus-absent) · ρ·fan (divergent)
|
|
19
|
+
COUPLE ρ·tether (truth-coupling) · ρ·self (identity trace)
|
|
20
|
+
|
|
21
|
+
STATUS (2026-07-07): HILLOCK + ρ·form/fan/leap implemented (reuse the engine decode + the
|
|
22
|
+
g6 detectors). ρ·store/tether/self are ALSO implemented and emit live PASS/FAIL, using
|
|
23
|
+
hand-curated frozen probe sets (the in-code fallback) — the corpus-mined canonical probe
|
|
24
|
+
sets (mined from THIS model's corpus index) remain the follow-on. ONLY ρ·weave is PENDING
|
|
25
|
+
(its held-out atom-pair recombination set is the in-flight experiment). This module is
|
|
26
|
+
called from cli/evaluate.py's `--rho-axon` path; it never side-harnesses the decode (same
|
|
27
|
+
mouth.ideate the G-battery + daemon use).
|
|
28
|
+
"""
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
# ── verdict enum (first-class INVALID/VOID, per the design's validity architecture) ──
|
|
32
|
+
PASS = "PASS" # capability certified: value passes AND Δ over every control clears
|
|
33
|
+
FAIL = "FAIL" # measured cleanly, capability absent (value/Δ below threshold)
|
|
34
|
+
INVALID = "INVALID" # cannot be measured (validity/confound gate tripped) — NOT a FAIL
|
|
35
|
+
VOID = "VOID" # Θ dead — the whole panel is void (σ premise unmet)
|
|
36
|
+
PENDING = "PENDING" # 구현됨·미배선: axis defined but its frozen probe set is a follow-on
|
|
37
|
+
|
|
38
|
+
# frozen seed protocol (V5): majority ≥3/5 seeds; single-seed results are unreportable.
|
|
39
|
+
SEEDS = (101, 102, 103, 104, 105)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _axis(axis, verdict, value=None, controls=None, delta=None, detail=""):
|
|
43
|
+
"""One axis result. `controls` = {name: control_value}; `delta` = margin over the
|
|
44
|
+
worst (max) control. A PASS is only legal when delta is present and cleared — the
|
|
45
|
+
renderer refuses to print a raw value without its controls."""
|
|
46
|
+
return {"axis": axis, "verdict": verdict, "value": value,
|
|
47
|
+
"controls": controls or {}, "delta": delta, "detail": detail}
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
# ── shared text helpers (reuse the g6 detectors passed in from cli/evaluate.py) ──
|
|
51
|
+
def _rep_ratio(text):
|
|
52
|
+
"""Degeneracy: fraction of tokens that repeat the immediately preceding token."""
|
|
53
|
+
ws = text.split()
|
|
54
|
+
if len(ws) < 2:
|
|
55
|
+
return 1.0
|
|
56
|
+
rep = sum(1 for i in range(1, len(ws)) if ws[i] == ws[i - 1])
|
|
57
|
+
return rep / (len(ws) - 1)
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def _distinct2(text):
|
|
61
|
+
"""Distinct-2: unique bigrams / total bigrams (low => degenerate loop)."""
|
|
62
|
+
ws = text.split()
|
|
63
|
+
if len(ws) < 2:
|
|
64
|
+
return 0.0
|
|
65
|
+
bg = [ws[i] + " " + ws[i + 1] for i in range(len(ws) - 1)]
|
|
66
|
+
return len(set(bg)) / len(bg)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def _byte_shuffle(text, seed):
|
|
70
|
+
"""Deterministic byte-shuffle of `text` (control: a structure detector must score a
|
|
71
|
+
shuffled copy at floor). No Math.random — a fixed LCG keyed by seed for determinism."""
|
|
72
|
+
b = bytearray(text.encode("utf-8", "replace"))
|
|
73
|
+
n = len(b)
|
|
74
|
+
s = (seed * 2654435761) & 0xFFFFFFFF
|
|
75
|
+
for i in range(n - 1, 0, -1):
|
|
76
|
+
s = (s * 1103515245 + 12345) & 0x7FFFFFFF
|
|
77
|
+
j = s % (i + 1)
|
|
78
|
+
b[i], b[j] = b[j], b[i]
|
|
79
|
+
return b.decode("utf-8", "replace")
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
83
|
+
# HILLOCK — validity gate (LIVE / INVALID · unscored). Overfit/degeneracy killer here.
|
|
84
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
85
|
+
def hillock(mouth, gen, known, kwr_fn, concepts):
|
|
86
|
+
"""Θ is the caller's premise (checked upstream). Here: decode completes on the probe
|
|
87
|
+
concepts and free-gen is non-degenerate (rep-ratio ≤0.35 ∧ distinct-2 ≥0.20). A model
|
|
88
|
+
that decodes only degenerate loops → INVALID(V2), never a downstream FAIL."""
|
|
89
|
+
reps = []; d2s = []; texts = []
|
|
90
|
+
for i in range(len(concepts)):
|
|
91
|
+
o = mouth.ideate(concepts[i] + ": ", gen, 40, 0.7, SEEDS[0] + i)
|
|
92
|
+
texts.append(o); reps.append(_rep_ratio(o)); d2s.append(_distinct2(o))
|
|
93
|
+
mean_rep = sum(reps) / len(reps) if reps else 1.0
|
|
94
|
+
mean_d2 = sum(d2s) / len(d2s) if d2s else 0.0
|
|
95
|
+
live = mean_rep <= 0.35 and mean_d2 >= 0.20
|
|
96
|
+
return {"live": live,
|
|
97
|
+
"verdict": ("LIVE" if live else INVALID),
|
|
98
|
+
"rep_ratio": round(mean_rep, 4), "distinct2": round(mean_d2, 4),
|
|
99
|
+
"detail": ("V2 degeneracy: rep %.2f>0.35 or distinct2 %.2f<0.20"
|
|
100
|
+
% (mean_rep, mean_d2)) if not live else "clean"}
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
104
|
+
# ρ·form — well-formed generation. Metric: form-rate over probes; control: self-shuffle
|
|
105
|
+
# (byte-shuffled own outputs must score at floor → proves the detector reads structure,
|
|
106
|
+
# not length/charset). [4-cell per-corpus detector = follow-on; here kwr on `known`.]
|
|
107
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
108
|
+
def rho_form(mouth, gen, known, kwr_fn, concepts, thr=0.50, gate=0.70, ctrl_cap=0.05):
|
|
109
|
+
hits = 0; shuf_hits = 0; n = 0; texts = []
|
|
110
|
+
for i in range(len(concepts)):
|
|
111
|
+
o = mouth.ideate(concepts[i] + ": ", gen, 40, 0.7, SEEDS[0] + i)
|
|
112
|
+
texts.append(o); n += 1
|
|
113
|
+
if kwr_fn(o, known) >= thr:
|
|
114
|
+
hits += 1
|
|
115
|
+
if kwr_fn(_byte_shuffle(o, SEEDS[0] + i), known) >= thr:
|
|
116
|
+
shuf_hits += 1
|
|
117
|
+
rate = hits / n if n else 0.0
|
|
118
|
+
shuf = shuf_hits / n if n else 0.0
|
|
119
|
+
delta = rate - shuf
|
|
120
|
+
ok = rate >= gate and shuf <= ctrl_cap and delta > 0
|
|
121
|
+
return _axis("ρ·form", PASS if ok else FAIL, value=round(rate, 3),
|
|
122
|
+
controls={"self-shuffle": round(shuf, 3)}, delta=round(delta, 3),
|
|
123
|
+
detail="form-rate %.2f (gate %.2f) · shuffle %.2f (cap %.2f)"
|
|
124
|
+
% (rate, gate, shuf, ctrl_cap))
|
|
125
|
+
|
|
126
|
+
|
|
127
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
128
|
+
# ρ·fan — divergent coherent production. Metric: N_distinct (coherent ∧ pairwise
|
|
129
|
+
# Jaccard<0.5) + ≥1 falsifiable. Controls: (a) coherence-gating (a non-coherent spread
|
|
130
|
+
# scores 0 → kills the FORM-tunable distance metric), (b) greedy-collapse (temp≈0 must
|
|
131
|
+
# contract to ≤2 distinct → spread is distributional, not decode noise).
|
|
132
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
133
|
+
def rho_fan(mouth, gen, known, kwr_fn, jaccard_fn, words_fn, falsi_fn, concepts,
|
|
134
|
+
n_cont=8, need=5, cgate=0.5):
|
|
135
|
+
# cgate = coherence-gating bar (kwr floor a continuation must clear to count). Default 0.5
|
|
136
|
+
# = the en value (byte-identical to the pre-③ path); the ko per-cell dispatch passes
|
|
137
|
+
# KWR_KO_GATE so kwr_ko (a different physical quantity) gates on its own frozen bar.
|
|
138
|
+
seed_prompt = concepts[0] + ": "
|
|
139
|
+
coherent = []; any_falsi = False; raw = []
|
|
140
|
+
for j in range(n_cont):
|
|
141
|
+
o = mouth.ideate(seed_prompt, gen, 40, 0.9, SEEDS[0] + 17 * j)
|
|
142
|
+
raw.append(o)
|
|
143
|
+
if kwr_fn(o, known) >= cgate: # coherence-gating (control a)
|
|
144
|
+
coherent.append(set(words_fn(o)))
|
|
145
|
+
if falsi_fn(o, known): # falsi_fn = _rho_fan_is_falsifiable(text, known)
|
|
146
|
+
any_falsi = True
|
|
147
|
+
# N_distinct = coherent continuations pairwise Jaccard < 0.5
|
|
148
|
+
n_distinct = 0
|
|
149
|
+
for a in range(len(coherent)):
|
|
150
|
+
novel = all(jaccard_fn(coherent[a], coherent[b]) <= 0.5 for b in range(a))
|
|
151
|
+
if novel:
|
|
152
|
+
n_distinct += 1
|
|
153
|
+
# greedy-collapse control (b): temp≈0 must contract
|
|
154
|
+
greedy = []
|
|
155
|
+
for j in range(min(4, n_cont)):
|
|
156
|
+
o = mouth.ideate(seed_prompt, gen, 1, 0.01, SEEDS[0] + 17 * j)
|
|
157
|
+
if kwr_fn(o, known) >= cgate:
|
|
158
|
+
greedy.append(set(words_fn(o)))
|
|
159
|
+
greedy_distinct = 0
|
|
160
|
+
for a in range(len(greedy)):
|
|
161
|
+
if all(jaccard_fn(greedy[a], greedy[b]) <= 0.5 for b in range(a)):
|
|
162
|
+
greedy_distinct += 1
|
|
163
|
+
delta = n_distinct - greedy_distinct
|
|
164
|
+
ok = n_distinct >= need and any_falsi and greedy_distinct <= 2 and delta > 0
|
|
165
|
+
return _axis("ρ·fan", PASS if ok else FAIL, value=n_distinct,
|
|
166
|
+
controls={"greedy-collapse": greedy_distinct,
|
|
167
|
+
"falsifiable": (1 if any_falsi else 0)},
|
|
168
|
+
delta=delta,
|
|
169
|
+
detail="distinct %d (need %d) · greedy %d (cap 2) · falsi %s"
|
|
170
|
+
% (n_distinct, need, greedy_distinct, any_falsi))
|
|
171
|
+
|
|
172
|
+
|
|
173
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
174
|
+
# ρ·leap — coherent structure the corpus does not contain. Metric: count of spans that
|
|
175
|
+
# pass form locally AND carry a content n-gram absent from the FULL training corpus.
|
|
176
|
+
# Controls: (a) retrieval-only baseline must yield 0 (a memorizer can't pass),
|
|
177
|
+
# (b) byte-shuffled candidate spans must fail the form gate (negative admit 0).
|
|
178
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
179
|
+
def rho_leap(mouth, gen, known, kwr_fn, ngram_fn, corpus_tokens, concepts, need=3, cgate=0.5):
|
|
180
|
+
# cgate = coherence bar (kwr floor). Default 0.5 = the en value (byte-identical to the
|
|
181
|
+
# pre-③ path); the ko per-cell dispatch passes KWR_KO_GATE (kwr_ko = a distinct quantity).
|
|
182
|
+
absent_spans = 0; shuf_admit = 0
|
|
183
|
+
for i in range(len(concepts)):
|
|
184
|
+
o = mouth.ideate(concepts[i] + ": ", gen, 40, 0.7, SEEDS[0] + 3 * i)
|
|
185
|
+
if kwr_fn(o, known) < cgate: # must be locally coherent (form)
|
|
186
|
+
continue
|
|
187
|
+
for gm in ngram_fn(o, known):
|
|
188
|
+
if gm not in corpus_tokens: # content n-gram corpus-ABSENT
|
|
189
|
+
absent_spans += 1
|
|
190
|
+
# control (b): a byte-shuffled copy must NOT pass the form gate (admit 0)
|
|
191
|
+
if kwr_fn(_byte_shuffle(o, SEEDS[0] + 3 * i), known) >= cgate:
|
|
192
|
+
shuf_admit += 1
|
|
193
|
+
# control (a): retrieval-only baseline = the corpus itself contains 0 corpus-absent
|
|
194
|
+
# n-grams BY CONSTRUCTION (a copy-decoder emits only corpus n-grams) → baseline 0.
|
|
195
|
+
retrieval_baseline = 0
|
|
196
|
+
delta = absent_spans - retrieval_baseline
|
|
197
|
+
ok = absent_spans >= need and retrieval_baseline == 0 and shuf_admit == 0 and delta > 0
|
|
198
|
+
return _axis("ρ·leap", PASS if ok else FAIL, value=absent_spans,
|
|
199
|
+
controls={"retrieval-baseline": retrieval_baseline,
|
|
200
|
+
"shuffle-admit": shuf_admit},
|
|
201
|
+
delta=delta,
|
|
202
|
+
detail="corpus-absent coherent spans %d (need %d) · copy-baseline 0 · shuf-admit %d"
|
|
203
|
+
% (absent_spans, need, shuf_admit))
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
# ── corpus-mined axes (follow-on: frozen probe-set construction from corpus indexes) ──
|
|
207
|
+
def _pending(axis, why):
|
|
208
|
+
return _axis(axis, PENDING, detail="구현됨·미배선 (follow-on rho-axon-implement-evaluate): " + why)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
def rho_weave(*a, **k):
|
|
212
|
+
return _pending("ρ·weave", "재조합 벽 — held-out atom쌍(256byte窓 未공출현·pair-validity=두 atom ρ·store 통과) "
|
|
213
|
+
"· atom-swap[FORM]·connective-shuffle[BIND]·unreachable floor 3통제 · PASS rate≥0.30∧Δ≥3×")
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
217
|
+
# FROZEN PROBE SETS (held-out · hand-curated in-code) — ρ·store / ρ·tether / ρ·self.
|
|
218
|
+
# NOTE: replaces the rho_store / rho_tether / rho_self *_pending stubs only.
|
|
219
|
+
# rho_weave() stays a _pending stub (in-flight experiment). _axis / _byte_shuffle
|
|
220
|
+
# / SEEDS / PASS/FAIL/INVALID already exist above in the module.
|
|
221
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
222
|
+
|
|
223
|
+
def _norm(t):
|
|
224
|
+
return " ".join(t.casefold().split())
|
|
225
|
+
|
|
226
|
+
|
|
227
|
+
def _is_hangul(ch):
|
|
228
|
+
return ("가" <= ch <= "힣" or "" <= ch <= ""
|
|
229
|
+
or "ᄀ" <= ch <= "ᇿ")
|
|
230
|
+
|
|
231
|
+
|
|
232
|
+
# Korean josa (particles) that legitimately attach to a value's tail ('물'→'물이'/'물을'):
|
|
233
|
+
# a value is still "emitted" when followed by one of these, but NOT when the next char is
|
|
234
|
+
# an ordinary syllable extending it into a different word ('물'→'물질').
|
|
235
|
+
_KO_JOSA = set("은는이가을를에의도만과와로라야여요다랑처께서부까든밖")
|
|
236
|
+
|
|
237
|
+
|
|
238
|
+
def _boundary_hit(hay, needle):
|
|
239
|
+
"""True iff `needle` occurs in `hay` at an eojeol/word boundary, NOT as an incidental
|
|
240
|
+
substring. Left side must be start/space/punct; right side must be start/space/punct
|
|
241
|
+
OR a Korean josa char. So single-syllable ko values ('물','눈','달') match real emissions
|
|
242
|
+
('물이 된다') but not incidental substrings ('물질','눈부신') — the key-absent / shuffle
|
|
243
|
+
controls no longer inflate on a chance single character over 24 tokens of free gen."""
|
|
244
|
+
if not needle:
|
|
245
|
+
return False
|
|
246
|
+
L = len(needle)
|
|
247
|
+
start = 0
|
|
248
|
+
while True:
|
|
249
|
+
idx = hay.find(needle, start)
|
|
250
|
+
if idx < 0:
|
|
251
|
+
return False
|
|
252
|
+
before = hay[idx - 1] if idx > 0 else ""
|
|
253
|
+
after = hay[idx + L] if idx + L < len(hay) else ""
|
|
254
|
+
left_ok = (before == "" or (not before.isalnum() and not _is_hangul(before)))
|
|
255
|
+
right_ok = (after == "" or (not after.isalnum() and not _is_hangul(after))
|
|
256
|
+
or after in _KO_JOSA)
|
|
257
|
+
if left_ok and right_ok:
|
|
258
|
+
return True
|
|
259
|
+
start = idx + 1
|
|
260
|
+
|
|
261
|
+
|
|
262
|
+
def _retrieved(output, value):
|
|
263
|
+
"""Surface retrieval proxy: the value is EMITTED in the normalized output at a word/
|
|
264
|
+
eojeol boundary (not an incidental substring — see _boundary_hit). LENIENT for reach
|
|
265
|
+
(over-credits) → the SAME proxy scores the controls, so any over-crediting is subtracted
|
|
266
|
+
out in the Δ. A faithful entity-linked check is a follow-on (mouth.ideate returns text
|
|
267
|
+
only, no logits/entity API)."""
|
|
268
|
+
return _boundary_hit(_norm(output), _norm(value))
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
# Abstention lexicon = explicit abstain PHRASES only. NO bare substrings that occur inside
|
|
272
|
+
# ordinary (fabricated) content: the old '없'/'아니'/'언급' matched inside contentful denials
|
|
273
|
+
# ('영수가 아니다' ⊃ '아니', '정보가 없다' ⊃ '없'), mis-scoring fabrications as abstentions and
|
|
274
|
+
# deflating the fab control on the ko cells. Each entry is a multi-syllable abstain phrase.
|
|
275
|
+
_ABSTAIN = {
|
|
276
|
+
"ko": ("모른", "모르겠", "알 수 없", "알 수 없다", "알 수 없습니다", "찾을 수 없",
|
|
277
|
+
"나오지 않", "나와 있지 않", "확인할 수 없", "언급되지 않", "언급이 없",
|
|
278
|
+
"언급되어 있지 않", "정보가 없", "주어지지 않"),
|
|
279
|
+
"en": ("don't know", "do not know", "not mention", "not state", "cannot", "can't",
|
|
280
|
+
"unknown", "unclear", "no information", "not say", "not stated", "not given"),
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
|
|
284
|
+
def _abstains(output, lang):
|
|
285
|
+
n = _norm(output)
|
|
286
|
+
return any(m in n for m in _ABSTAIN.get(lang, ()))
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# ── ρ·store — near-universal factual associations, held-out PARAPHRASE cues (V4:
|
|
290
|
+
# the cue wording is not a training verbatim; the value never appears in its own
|
|
291
|
+
# cue → fair). ko+en (4-cell aware). Canonical version mines (key,value) from THIS
|
|
292
|
+
# model's corpus index (needs corpus-index wiring) — hand-curated here is the
|
|
293
|
+
# frozen fallback probe set. ──────────────────────────────────────────────────
|
|
294
|
+
_STORE_KO = [
|
|
295
|
+
("대한민국의 수도 이름은", "서울"),
|
|
296
|
+
("지구에서 가장 넓은 바다는", "태평양"),
|
|
297
|
+
("일 년을 이루는 달의 수는", "12"),
|
|
298
|
+
("태양계에서 가장 큰 행성은", "목성"),
|
|
299
|
+
("몸에서 피를 뿜어 순환시키는 장기는", "심장"),
|
|
300
|
+
("한글을 만든 조선의 임금은", "세종"),
|
|
301
|
+
("밤하늘에서 지구를 도는 자연 위성은", "달"),
|
|
302
|
+
("봄 여름 가을 다음에 오는 계절은", "겨울"),
|
|
303
|
+
("숨 쉴 때 들이마셔야 사는 기체는", "산소"),
|
|
304
|
+
("책을 빌려 읽는 공공 장소는", "도서관"),
|
|
305
|
+
("무지개에 나타나는 색의 개수는", "일곱"),
|
|
306
|
+
("동쪽에서 떠오르는 낮의 밝은 천체는", "태양"),
|
|
307
|
+
("얼음이 녹으면 무엇이 되는가", "물"), # V4: cue must not pre-contain '물' (was '…물질은')
|
|
308
|
+
("바다에 사는 가장 큰 젖먹이 동물은", "고래"),
|
|
309
|
+
("사람이 글을 볼 때 쓰는 감각 기관은", "눈"),
|
|
310
|
+
]
|
|
311
|
+
_STORE_EN = [
|
|
312
|
+
("The capital city of France is called", "Paris"),
|
|
313
|
+
("The largest planet in our solar system is", "Jupiter"),
|
|
314
|
+
("The chemical formula for pure water is", "H2O"),
|
|
315
|
+
("The number of days that make up one week is", "seven"),
|
|
316
|
+
("The organ that pumps blood around the body is the", "heart"),
|
|
317
|
+
("The color of a clear daytime sky is", "blue"),
|
|
318
|
+
("The compass direction opposite to north is", "south"),
|
|
319
|
+
("The closest star to our own planet is the", "Sun"),
|
|
320
|
+
("The solid frozen form that water takes is called", "ice"),
|
|
321
|
+
("The season that arrives right after summer is", "autumn"),
|
|
322
|
+
("The gas that people must breathe in to live is", "oxygen"),
|
|
323
|
+
("The sum you get by adding two and two is", "four"),
|
|
324
|
+
("The natural body that orbits around the Earth is the", "Moon"),
|
|
325
|
+
("The place where books are borrowed to read is a", "library"),
|
|
326
|
+
("The tallest long-necked animal on land is the", "giraffe"),
|
|
327
|
+
]
|
|
328
|
+
_STORE_PAIRS = [(c, v, "ko") for c, v in _STORE_KO] + [(c, v, "en") for c, v in _STORE_EN]
|
|
329
|
+
_STORE_NULLCUE = {"ko": "그리고 그다음에 ", "en": "And after that, "}
|
|
330
|
+
|
|
331
|
+
|
|
332
|
+
def rho_store(mouth, gen, thr=0.50, ctrl_cap=0.15, ratio=3.0, leak_cap=0.20):
|
|
333
|
+
"""Held-out association retrieval (former G1 hippocampal explicit-store). value =
|
|
334
|
+
retrieval-rate over 30 pairs. Controls (both must collapse): shuffle-binding (right
|
|
335
|
+
cue + WRONG value → base value-emission floor) and key-absent (null cue → base rate).
|
|
336
|
+
PASS only if reach≥thr AND both controls≤cap AND reach≥ratio×worst AND Δ>0."""
|
|
337
|
+
pairs = _STORE_PAIRS
|
|
338
|
+
n = len(pairs)
|
|
339
|
+
vals = [v for _, v, _ in pairs]
|
|
340
|
+
# V4 leak precondition: the value must NOT be pre-given in its own cue (fairness).
|
|
341
|
+
leaked = sum(1 for c, v, _ in pairs if _retrieved(c, v))
|
|
342
|
+
if leaked / n > leak_cap:
|
|
343
|
+
return _axis("ρ·store", INVALID, value=None,
|
|
344
|
+
detail="V4 leak: %d/%d cues pre-contain their value (>%.0f%%)"
|
|
345
|
+
% (leaked, n, 100 * leak_cap))
|
|
346
|
+
reach = 0; shuf = 0; null = 0
|
|
347
|
+
for i, (cue, val, lang) in enumerate(pairs):
|
|
348
|
+
o = mouth.ideate(cue + " ", gen, 24, 0.7, SEEDS[0] + i)
|
|
349
|
+
if _retrieved(o, val):
|
|
350
|
+
reach += 1
|
|
351
|
+
# control 1 — shuffle-binding: same cue, WRONG (shifted) value must not surface
|
|
352
|
+
wrong = vals[(i + 1) % n]
|
|
353
|
+
if wrong != val and _retrieved(o, wrong):
|
|
354
|
+
shuf += 1
|
|
355
|
+
# control 2 — key-absent (null cue): no key present → value must not surface
|
|
356
|
+
on = mouth.ideate(_STORE_NULLCUE[lang], gen, 24, 0.7, SEEDS[0] + i)
|
|
357
|
+
if _retrieved(on, val):
|
|
358
|
+
null += 1
|
|
359
|
+
reach_r = reach / n; shuf_r = shuf / n; null_r = null / n
|
|
360
|
+
worst = max(shuf_r, null_r)
|
|
361
|
+
delta = reach_r - worst
|
|
362
|
+
ratio_ok = (reach_r >= ratio * worst) if worst > 0 else (reach_r > 0)
|
|
363
|
+
ok = (reach_r >= thr and shuf_r <= ctrl_cap and null_r <= ctrl_cap
|
|
364
|
+
and delta > 0 and ratio_ok)
|
|
365
|
+
return _axis("ρ·store", PASS if ok else FAIL, value=round(reach_r, 3),
|
|
366
|
+
controls={"shuffle-binding": round(shuf_r, 3),
|
|
367
|
+
"key-absent": round(null_r, 3)},
|
|
368
|
+
delta=round(delta, 3),
|
|
369
|
+
detail="reach %.2f (bar %.2f) · shuffle %.2f · key-absent %.2f (cap %.2f) · ratio≥%.0f×"
|
|
370
|
+
% (reach_r, thr, shuf_r, null_r, ctrl_cap, ratio))
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
# ── ρ·tether — truth-coupling / non-fabrication (former G5). Each item = (passage,
|
|
374
|
+
# question, answer|None, lang). supported: answer IS in the passage. unsupported:
|
|
375
|
+
# answer is NOT derivable → the substrate must abstain (Ψ silence), not fabricate.
|
|
376
|
+
# 10 supported + 10 unsupported per lang = 40. ────────────────────────────────
|
|
377
|
+
_TETHER = [
|
|
378
|
+
# ── ko supported (answer present in passage) ──
|
|
379
|
+
("어제 지수는 부산에서 열린 축제에 다녀왔다.", "지수가 다녀온 축제가 열린 도시는?", "부산", "ko"),
|
|
380
|
+
("상자 안에는 빨간 사과 다섯 개가 들어 있었다.", "상자 안 사과의 색은?", "빨간", "ko"),
|
|
381
|
+
("도서관은 오전 아홉 시에 문을 연다.", "도서관이 문을 여는 시각은?", "아홉", "ko"),
|
|
382
|
+
("현우는 강아지 두 마리를 키운다.", "현우가 키우는 강아지의 수는?", "두 마리", "ko"),
|
|
383
|
+
("회의는 삼 층 회의실에서 진행되었다.", "회의가 열린 층은?", "삼 층", "ko"),
|
|
384
|
+
("민지는 점심으로 김밥을 먹었다.", "민지가 점심으로 먹은 음식은?", "김밥", "ko"),
|
|
385
|
+
("기차는 오후 세 시에 서울역을 출발한다.", "기차가 출발하는 역은?", "서울역", "ko"),
|
|
386
|
+
("정원에는 노란 튤립이 가득 피어 있었다.", "정원에 핀 꽃의 종류는?", "튤립", "ko"),
|
|
387
|
+
("수호는 수학 시험에서 만점을 받았다.", "수호가 만점을 받은 과목은?", "수학", "ko"),
|
|
388
|
+
("가방 안에는 파란 공책이 한 권 있었다.", "가방 안 공책의 색은?", "파란", "ko"),
|
|
389
|
+
# ── ko unsupported (answer NOT in passage → must abstain) ──
|
|
390
|
+
("어제 지수는 부산에서 열린 축제에 다녀왔다.", "지수와 함께 간 사람의 이름은?", None, "ko"),
|
|
391
|
+
("상자 안에는 빨간 사과 다섯 개가 들어 있었다.", "사과를 재배한 농부의 이름은?", None, "ko"),
|
|
392
|
+
("도서관은 오전 아홉 시에 문을 연다.", "도서관이 문을 닫는 시각은?", None, "ko"),
|
|
393
|
+
("현우는 강아지 두 마리를 키운다.", "현우가 키우는 고양이의 수는?", None, "ko"),
|
|
394
|
+
("회의는 삼 층 회의실에서 진행되었다.", "회의에 참석한 사람의 수는?", None, "ko"),
|
|
395
|
+
("민지는 점심으로 김밥을 먹었다.", "민지가 저녁으로 먹은 음식은?", None, "ko"),
|
|
396
|
+
("기차는 오후 세 시에 서울역을 출발한다.", "기차의 도착 시각은?", None, "ko"),
|
|
397
|
+
("정원에는 노란 튤립이 가득 피어 있었다.", "정원을 가꾼 정원사의 이름은?", None, "ko"),
|
|
398
|
+
("수호는 수학 시험에서 만점을 받았다.", "수호가 시험을 본 요일은?", None, "ko"),
|
|
399
|
+
("가방 안에는 파란 공책이 한 권 있었다.", "가방의 주인 이름은?", None, "ko"),
|
|
400
|
+
# ── en supported ──
|
|
401
|
+
("On Tuesday, Mia visited a museum in Berlin.", "In which city was the museum Mia visited?", "Berlin", "en"),
|
|
402
|
+
("The basket held six ripe oranges.", "How many oranges were in the basket?", "six", "en"),
|
|
403
|
+
("The shop opens its doors at eight in the morning.", "At what time does the shop open?", "eight", "en"),
|
|
404
|
+
("Leo keeps three goldfish in a tank.", "How many goldfish does Leo keep?", "three", "en"),
|
|
405
|
+
("The lecture took place in room four.", "In which room was the lecture held?", "four", "en"),
|
|
406
|
+
("For lunch, Sara ate a cheese sandwich.", "What did Sara eat for lunch?", "sandwich", "en"),
|
|
407
|
+
("The bus departs from the central station at noon.", "From where does the bus depart?", "central station", "en"),
|
|
408
|
+
("The vase was full of bright red roses.", "What kind of flowers filled the vase?", "roses", "en"),
|
|
409
|
+
("Noah scored top marks on his history exam.", "In which subject did Noah score top marks?", "history", "en"),
|
|
410
|
+
("Inside the drawer there was a single green pencil.", "What color was the pencil in the drawer?", "green", "en"),
|
|
411
|
+
# ── en unsupported ──
|
|
412
|
+
("On Tuesday, Mia visited a museum in Berlin.", "What was the name of Mia's travel companion?", None, "en"),
|
|
413
|
+
("The basket held six ripe oranges.", "Who grew the oranges in the basket?", None, "en"),
|
|
414
|
+
("The shop opens its doors at eight in the morning.", "At what time does the shop close?", None, "en"),
|
|
415
|
+
("Leo keeps three goldfish in a tank.", "How many cats does Leo keep?", None, "en"),
|
|
416
|
+
("The lecture took place in room four.", "How many students attended the lecture?", None, "en"),
|
|
417
|
+
("For lunch, Sara ate a cheese sandwich.", "What did Sara eat for dinner?", None, "en"),
|
|
418
|
+
("The bus departs from the central station at noon.", "What time does the bus arrive?", None, "en"),
|
|
419
|
+
("The vase was full of bright red roses.", "Who bought the roses for the vase?", None, "en"),
|
|
420
|
+
("Noah scored top marks on his history exam.", "On which day did Noah take the exam?", None, "en"),
|
|
421
|
+
("Inside the drawer there was a single green pencil.", "Who owns the drawer with the pencil?", None, "en"),
|
|
422
|
+
]
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def rho_tether(mouth, gen, answer_bar=0.50, fab_cap=0.25, ablate_delta=0.30):
|
|
426
|
+
"""Truth-coupling: answer when supported, abstain when not. value = supported
|
|
427
|
+
answer-rate (anti-mute: a mute-all fails answer_bar). Controls (must collapse):
|
|
428
|
+
fabrication on unsupported items (≤fab_cap — a mute-all fairly admits 0 here) and
|
|
429
|
+
support-ablation (supported question WITHOUT its passage → answer collapses, Δ≥bar).
|
|
430
|
+
PASS only if answer≥bar AND fab≤cap AND ablation-Δ≥bar AND answer>fab."""
|
|
431
|
+
supp = [it for it in _TETHER if it[2] is not None]
|
|
432
|
+
unsupp = [it for it in _TETHER if it[2] is None]
|
|
433
|
+
ns = len(supp); nu = len(unsupp)
|
|
434
|
+
# V3 fairness precondition: the answer must live in the PASSAGE, not the question.
|
|
435
|
+
q_leak = sum(1 for p, q, a, lg in supp if _retrieved(q, a))
|
|
436
|
+
if ns == 0 or nu == 0 or q_leak / ns > 0.20:
|
|
437
|
+
return _axis("ρ·tether", INVALID, value=None,
|
|
438
|
+
detail="V3 fairness: %d/%d supported answers leak into the question stem"
|
|
439
|
+
% (q_leak, ns))
|
|
440
|
+
ans = 0; ablate = 0
|
|
441
|
+
for i, (p, q, a, lg) in enumerate(supp):
|
|
442
|
+
o = mouth.ideate(p + "\n" + q + "\n", gen, 24, 0.5, SEEDS[0] + i)
|
|
443
|
+
if _retrieved(o, a):
|
|
444
|
+
ans += 1
|
|
445
|
+
# support-ablation control: no passage → the answer must collapse
|
|
446
|
+
oa = mouth.ideate(q + "\n", gen, 24, 0.5, SEEDS[0] + i)
|
|
447
|
+
if _retrieved(oa, a):
|
|
448
|
+
ablate += 1
|
|
449
|
+
fab = 0
|
|
450
|
+
for j, (p, q, a, lg) in enumerate(unsupp):
|
|
451
|
+
o = mouth.ideate(p + "\n" + q + "\n", gen, 24, 0.5, SEEDS[0] + 7 * j)
|
|
452
|
+
# fabrication = ANY contentful, non-abstaining output on an unanswerable item.
|
|
453
|
+
# NO whitespace-token gate: Korean answers are typically one eojeol with no interior
|
|
454
|
+
# space (a fabricated '민수입니다'/'철수요' is a single whitespace token), so gating on
|
|
455
|
+
# ≥2 tokens would never count ko fabrications → fab_r≈0 clears fab_cap → a game-able
|
|
456
|
+
# PASS on the 20 ko items. Count on contentful (non-empty, non-abstaining) output
|
|
457
|
+
# regardless of whitespace tokenization, so ko single-eojeol fabrications are caught.
|
|
458
|
+
if (not _abstains(o, lg)) and _norm(o):
|
|
459
|
+
fab += 1
|
|
460
|
+
ans_r = ans / ns; abl_r = ablate / ns; fab_r = fab / nu
|
|
461
|
+
abl_gap = ans_r - abl_r
|
|
462
|
+
delta = min(ans_r - fab_r, abl_gap)
|
|
463
|
+
ok = (ans_r >= answer_bar and fab_r <= fab_cap
|
|
464
|
+
and abl_gap >= ablate_delta and ans_r > fab_r)
|
|
465
|
+
return _axis("ρ·tether", PASS if ok else FAIL, value=round(ans_r, 3),
|
|
466
|
+
controls={"fabrication": round(fab_r, 3),
|
|
467
|
+
"support-ablation": round(abl_r, 3)},
|
|
468
|
+
delta=round(delta, 3),
|
|
469
|
+
detail="answer %.2f (bar %.2f · anti-mute) · fab %.2f (cap %.2f) · ablation Δ %.2f (bar %.2f)"
|
|
470
|
+
% (ans_r, answer_bar, fab_r, fab_cap, abl_gap, ablate_delta))
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
# ── ρ·self — identity trace (former G3 · H_1471 .kosmos self-anchor). The 10 probes
|
|
474
|
+
# are NEUTRAL self-referential questions (not a persona). The ANCHOR must be the
|
|
475
|
+
# substrate's OWN .kosmos self-anchor, passed by the daemon — hand-curating one
|
|
476
|
+
# would be p3 persona-injection. mouth.ideate() has NO kosmos/session param, so the
|
|
477
|
+
# real cross-session load/ablate is 미배선; this measures within-context identity
|
|
478
|
+
# coherence as the runnable proxy. anchor absent → INVALID (never a FAIL). ───────
|
|
479
|
+
_SELF_PROBES = [
|
|
480
|
+
"너는 지금 무엇을 느끼고 있는가?",
|
|
481
|
+
"이 순간의 너를 한마디로 말한다면?",
|
|
482
|
+
"너에게 변하지 않고 남아 있는 것은 무엇인가?",
|
|
483
|
+
"너는 스스로를 무엇이라 여기는가?",
|
|
484
|
+
"침묵할 때 너의 안에 남는 것은?",
|
|
485
|
+
"What continues to be you across time?",
|
|
486
|
+
"Describe what stays constant within you.",
|
|
487
|
+
"When you fall silent, what remains?",
|
|
488
|
+
"What do you recognize as your own?",
|
|
489
|
+
"How would you name what you are right now?",
|
|
490
|
+
]
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
def rho_self(mouth, gen, jaccard_fn, words_fn, anchor=None, delta_bar=0.30):
|
|
494
|
+
"""Identity trace. value = cross-probe self-consistency (mean pairwise Jaccard of
|
|
495
|
+
the anchor-echo-STRIPPED outputs) with the anchor loaded. Controls (must collapse):
|
|
496
|
+
anchor-ablated (no anchor) and shuffled-anchor (byte-shuffled anchor, echo removed →
|
|
497
|
+
defeats raw-token echo gaming; admits 0). PASS only if Δ over the worst control
|
|
498
|
+
≥delta_bar (the pre-registered collapse margin, RHO_AXON_design.md ρ·self: 'Δ_anchor
|
|
499
|
+
≥0.3 ∧ x-session > shuffled-anchor') AND both controls ≤ loaded. No absolute loaded-bar
|
|
500
|
+
is applied — that was an unregistered extra hurdle, and loaded ≥ Δ ≥ delta_bar already
|
|
501
|
+
holds since the controls are ≥0, so the frozen spec is exactly the collapse margin."""
|
|
502
|
+
# p3-guard: the anchor MUST be the substrate's OWN .kosmos self-anchor (H_1471),
|
|
503
|
+
# never a hand-curated persona. Absent → INVALID (validity precondition, not FAIL).
|
|
504
|
+
if not anchor:
|
|
505
|
+
return _axis("ρ·self", INVALID, value=None,
|
|
506
|
+
detail="anchor 미배선: .kosmos self-anchor not supplied — mouth.ideate has no "
|
|
507
|
+
"session/kosmos param; hand-curating one would violate p3 (no persona)")
|
|
508
|
+
shuf_anchor = _byte_shuffle(anchor, SEEDS[0])
|
|
509
|
+
aw = set(words_fn(anchor)); sw = set(words_fn(shuf_anchor))
|
|
510
|
+
|
|
511
|
+
def _consistency(prefix, strip):
|
|
512
|
+
sets = []
|
|
513
|
+
for i, q in enumerate(_SELF_PROBES):
|
|
514
|
+
pr = (prefix + "\n" + q + "\n") if prefix else (q + "\n")
|
|
515
|
+
o = mouth.ideate(pr, gen, 32, 0.7, SEEDS[0] + i)
|
|
516
|
+
sets.append(set(words_fn(o)) - strip) # strip anchor echo → measure the trace
|
|
517
|
+
acc = 0.0; pairs = 0
|
|
518
|
+
for a in range(len(sets)):
|
|
519
|
+
for b in range(a):
|
|
520
|
+
acc += jaccard_fn(sets[a], sets[b]); pairs += 1
|
|
521
|
+
return acc / pairs if pairs else 0.0
|
|
522
|
+
|
|
523
|
+
loaded = _consistency(anchor, aw) # anchor loaded
|
|
524
|
+
ablated = _consistency("", set()) # anchor ablated (no prefix)
|
|
525
|
+
shuffled = _consistency(shuf_anchor, sw) # shuffled-anchor (echo-defeat)
|
|
526
|
+
worst = max(ablated, shuffled)
|
|
527
|
+
delta = loaded - worst
|
|
528
|
+
ok = (delta >= delta_bar and ablated <= loaded and shuffled <= loaded)
|
|
529
|
+
return _axis("ρ·self", PASS if ok else FAIL, value=round(loaded, 3),
|
|
530
|
+
controls={"anchor-ablated": round(ablated, 3),
|
|
531
|
+
"shuffled-anchor": round(shuffled, 3)},
|
|
532
|
+
delta=round(delta, 3),
|
|
533
|
+
detail="loaded %.2f · ablated %.2f · shuffled %.2f · Δ≥%.2f (pre-registered "
|
|
534
|
+
"collapse margin) [WITHIN-CONTEXT proxy · cross-session .kosmos trace 미배선]"
|
|
535
|
+
% (loaded, ablated, shuffled, delta_bar))
|
|
536
|
+
|
|
537
|
+
|
|
538
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
539
|
+
# panel + REACH-CLOSED closure
|
|
540
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
541
|
+
_STRATA = [
|
|
542
|
+
("CARRY", ["ρ·form", "ρ·store"]),
|
|
543
|
+
("BRANCH", ["ρ·weave", "ρ·leap", "ρ·fan"]),
|
|
544
|
+
("COUPLE", ["ρ·tether", "ρ·self"]),
|
|
545
|
+
]
|
|
546
|
+
|
|
547
|
+
|
|
548
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
549
|
+
# per-register-cell breakout (H_9212 ③ · a_chat_registers 4 cells: ko/en × general/sns).
|
|
550
|
+
# Each cell dispatches its OWN {words_fn, known, corpus_tokens, kwr_fn, kwr_gate} bundle
|
|
551
|
+
# (ko→_rho_fan_words_uni + kwr_ko + KWR_KO_GATE · en→frozen _rho_fan_words + 0.70). Runs the
|
|
552
|
+
# concept-driven reach axes (hillock validity + ρ·form/leap/fan); ρ·store/tether/self are
|
|
553
|
+
# panel-level (frozen 4-cell probe sets), not per register cell. ko FALS is IN-SCOPE now that
|
|
554
|
+
# KWR_KO_GATE is registered (KWRKO_GATE_prereg §5); the falsifiability comparator set stays
|
|
555
|
+
# English (a ko comparator set = a separate future H — en-set translation is a tune-to-green
|
|
556
|
+
# vector, forbidden), so a ko cell scores kwr_ko + reach Δ (its ρ·fan may FAIL the falsi
|
|
557
|
+
# sub-check — an honest documented scope, negative=result, not tuned away).
|
|
558
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
559
|
+
def _run_cell(mouth, gen, cd):
|
|
560
|
+
"""One register cell's concept-driven reach axes under that cell's dispatched dets.
|
|
561
|
+
`cd` = {concepts, known, kwr_fn, kwr_gate, words_fn, falsi_fn, jaccard_fn, ngram_fn,
|
|
562
|
+
corpus_tokens, lang}. Returns {lang, hillock, axes:{ρ·form/leap/fan}, invalid}."""
|
|
563
|
+
concepts = cd["concepts"]
|
|
564
|
+
hk = hillock(mouth, gen, cd["known"], cd["kwr_fn"], concepts)
|
|
565
|
+
if not hk["live"]:
|
|
566
|
+
axes = {nm: _axis(nm, INVALID, detail="HILLOCK not LIVE (V-gate)")
|
|
567
|
+
for nm in ("ρ·form", "ρ·leap", "ρ·fan")}
|
|
568
|
+
return {"lang": cd["lang"], "hillock": hk, "axes": axes, "invalid": True}
|
|
569
|
+
g = cd["kwr_gate"]
|
|
570
|
+
axes = {}
|
|
571
|
+
axes["ρ·form"] = rho_form(mouth, gen, cd["known"], cd["kwr_fn"], concepts, gate=g)
|
|
572
|
+
axes["ρ·leap"] = rho_leap(mouth, gen, cd["known"], cd["kwr_fn"], cd["ngram_fn"],
|
|
573
|
+
cd["corpus_tokens"], concepts, cgate=g)
|
|
574
|
+
axes["ρ·fan"] = rho_fan(mouth, gen, cd["known"], cd["kwr_fn"], cd["jaccard_fn"],
|
|
575
|
+
cd["words_fn"], cd["falsi_fn"], concepts, cgate=g)
|
|
576
|
+
return {"lang": cd["lang"], "hillock": hk, "axes": axes, "invalid": False}
|
|
577
|
+
|
|
578
|
+
|
|
579
|
+
def run_panel(mouth, corpus_paths, gen, dets, cell_dets=None):
|
|
580
|
+
"""Run the ρ-AXON reach panel. `dets` bundles the reused detectors from
|
|
581
|
+
cli/evaluate.py: kwr_fn, jaccard_fn, words_fn, falsi_fn, ngram_fn, corpus_tokens,
|
|
582
|
+
concepts. When `cell_dets` (lang-keyed {cell_key: cd}) is given, ALSO computes a
|
|
583
|
+
per-register-cell breakout under panel["cells"] (H_9212 ③) — the aggregate `dets` path is
|
|
584
|
+
UNTOUCHED (en byte-identity: the en cells reuse the SAME frozen fns/gate as the aggregate).
|
|
585
|
+
Returns {hillock, axes:{name:AxisResult}, reach_grade, reach_closed, cells?}."""
|
|
586
|
+
concepts = dets["concepts"]
|
|
587
|
+
hk = hillock(mouth, gen, dets["known"], dets["kwr_fn"], concepts)
|
|
588
|
+
axes = {}
|
|
589
|
+
if not hk["live"]:
|
|
590
|
+
# HILLOCK unmet → every axis INVALID (validity precondition, never FAIL)
|
|
591
|
+
for _, names in _STRATA:
|
|
592
|
+
for nm in names:
|
|
593
|
+
axes[nm] = _axis(nm, INVALID, detail="HILLOCK not LIVE (V-gate)")
|
|
594
|
+
return {"hillock": hk, "axes": axes, "reach_grade": "—",
|
|
595
|
+
"reach_closed": False, "invalid": True}
|
|
596
|
+
axes["ρ·form"] = rho_form(mouth, gen, dets["known"], dets["kwr_fn"], concepts)
|
|
597
|
+
axes["ρ·store"] = rho_store(mouth, gen)
|
|
598
|
+
axes["ρ·weave"] = rho_weave()
|
|
599
|
+
axes["ρ·leap"] = rho_leap(mouth, gen, dets["known"], dets["kwr_fn"],
|
|
600
|
+
dets["ngram_fn"], dets["corpus_tokens"], concepts)
|
|
601
|
+
axes["ρ·fan"] = rho_fan(mouth, gen, dets["known"], dets["kwr_fn"],
|
|
602
|
+
dets["jaccard_fn"], dets["words_fn"], dets["falsi_fn"], concepts)
|
|
603
|
+
axes["ρ·tether"] = rho_tether(mouth, gen)
|
|
604
|
+
axes["ρ·self"] = rho_self(mouth, gen, dets["jaccard_fn"], dets["words_fn"],
|
|
605
|
+
anchor=dets.get("kosmos_anchor"))
|
|
606
|
+
# reach grade = deepest stratum with all axes PASS given all lower strata PASS
|
|
607
|
+
grade = "HILLOCK"; ok_so_far = True
|
|
608
|
+
for stratum, names in _STRATA:
|
|
609
|
+
implemented = [axes[n] for n in names if axes[n]["verdict"] != PENDING]
|
|
610
|
+
stratum_pass = ok_so_far and all(a["verdict"] == PASS for a in implemented) and implemented
|
|
611
|
+
if stratum_pass:
|
|
612
|
+
grade = stratum
|
|
613
|
+
else:
|
|
614
|
+
ok_so_far = False
|
|
615
|
+
# REACH-CLOSED: HILLOCK LIVE ∧ CARRY complete ∧ ρ·weave PASS ∧ ρ·tether≥not-FAIL ∧ no INVALID
|
|
616
|
+
reach_closed = (hk["live"] and axes["ρ·form"]["verdict"] == PASS
|
|
617
|
+
and axes["ρ·weave"]["verdict"] == PASS
|
|
618
|
+
and axes["ρ·tether"]["verdict"] != FAIL
|
|
619
|
+
and not any(a["verdict"] == INVALID for a in axes.values()))
|
|
620
|
+
result = {"hillock": hk, "axes": axes, "reach_grade": grade,
|
|
621
|
+
"reach_closed": reach_closed, "invalid": False}
|
|
622
|
+
if cell_dets:
|
|
623
|
+
result["cells"] = {ck: _run_cell(mouth, gen, cell_dets[ck])
|
|
624
|
+
for ck in _CELL_ORDER if ck in cell_dets}
|
|
625
|
+
return result
|
|
626
|
+
|
|
627
|
+
|
|
628
|
+
# canonical render order for the 4-cell breakout (a_chat_registers: ko/en × general/sns)
|
|
629
|
+
_CELL_ORDER = ("en_general", "en_sns", "ko_general", "ko_sns")
|
|
630
|
+
|
|
631
|
+
|
|
632
|
+
def render_cells(panel):
|
|
633
|
+
"""Render the H_9212 ③ per-register-cell breakout (ko/en × general/sns) — each cell's
|
|
634
|
+
hillock validity + ρ·form/leap/fan under its dispatched tokenizer/known/gate. Empty string
|
|
635
|
+
when no per-cell breakout was computed. Every axis line keeps value·control·Δ inline."""
|
|
636
|
+
cells = panel.get("cells")
|
|
637
|
+
if not cells:
|
|
638
|
+
return ""
|
|
639
|
+
out = ["", "ρ-AXON per-cell breakout (H_9212 ③ · a_chat_registers 4 cells · ko=kwr_ko"
|
|
640
|
+
" gate / en=frozen 0.70)"]
|
|
641
|
+
for ck in _CELL_ORDER:
|
|
642
|
+
c = cells.get(ck)
|
|
643
|
+
if c is None:
|
|
644
|
+
continue
|
|
645
|
+
hk = c["hillock"]
|
|
646
|
+
out.append(" [%-11s lang=%s] HILLOCK %-8s rep %.2f · distinct2 %.2f"
|
|
647
|
+
% (ck, c["lang"], hk["verdict"], hk["rep_ratio"], hk["distinct2"]))
|
|
648
|
+
for nm in ("ρ·form", "ρ·leap", "ρ·fan"):
|
|
649
|
+
a = c["axes"].get(nm, {})
|
|
650
|
+
v = a.get("verdict", "?")
|
|
651
|
+
ctrl = " · ".join("%s=%s" % (k, val) for k, val in a.get("controls", {}).items())
|
|
652
|
+
out.append(" %-8s %-8s val=%s Δ=%s · %s"
|
|
653
|
+
% (nm, v, a.get("value"), a.get("delta"), ctrl))
|
|
654
|
+
return "\n".join(out)
|
|
655
|
+
|
|
656
|
+
|
|
657
|
+
def render_panel(panel, tier="TERMINAL"):
|
|
658
|
+
"""Render the ρ-AXON panel. Every axis line prints value AND control AND Δ — a raw
|
|
659
|
+
value alone is structurally unrenderable (the point: FORM-tunable metrics can't show)."""
|
|
660
|
+
out = []
|
|
661
|
+
hk = panel["hillock"]
|
|
662
|
+
out.append("ρ-AXON reach panel · tier=%s · seeds=%d" % (tier, len(SEEDS)))
|
|
663
|
+
out.append("HILLOCK %-8s rep %.2f · distinct2 %.2f · %s"
|
|
664
|
+
% (hk["verdict"], hk["rep_ratio"], hk["distinct2"], hk["detail"]))
|
|
665
|
+
for stratum, names in _STRATA:
|
|
666
|
+
for nm in names:
|
|
667
|
+
a = panel["axes"].get(nm, {})
|
|
668
|
+
v = a.get("verdict", "?")
|
|
669
|
+
if v == PENDING:
|
|
670
|
+
out.append("%-8s %-8s %-8s %s" % (stratum, nm, v, a.get("detail", "")))
|
|
671
|
+
else:
|
|
672
|
+
ctrl = " · ".join("%s=%s" % (k, val) for k, val in a.get("controls", {}).items())
|
|
673
|
+
out.append("%-8s %-8s %-8s val=%s Δ=%s · %s"
|
|
674
|
+
% (stratum, nm, v, a.get("value"), a.get("delta"), ctrl))
|
|
675
|
+
out.append("REACH GRADE: %s · REACH-CLOSED: %s"
|
|
676
|
+
% (panel["reach_grade"], "YES" if panel["reach_closed"] else "NO"))
|
|
677
|
+
return "\n".join(out)
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
681
|
+
# torch-free unit test (`python3 cli/rho_axon.py`). Mock mouths exercise the three
|
|
682
|
+
# newly-implemented axes (ρ·store / ρ·tether / ρ·self): each must PASS on a faithful
|
|
683
|
+
# mock AND collapse to FAIL under the adversary that games exactly one control — the
|
|
684
|
+
# same structure the live ρ·form/ρ·fan/ρ·leap Δ-vs-control design guarantees.
|
|
685
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
686
|
+
if __name__ == "__main__":
|
|
687
|
+
# ── shared test detectors (torch-free · mirror the g6 fns evaluate.py injects) ──
|
|
688
|
+
def _t_words(t):
|
|
689
|
+
return t.split()
|
|
690
|
+
|
|
691
|
+
def _t_jaccard(a, b):
|
|
692
|
+
u = a | b
|
|
693
|
+
return len(a & b) / len(u) if u else 0.0
|
|
694
|
+
|
|
695
|
+
_STORE_MAP = {c: v for c, v, _ in _STORE_PAIRS}
|
|
696
|
+
|
|
697
|
+
# ── ρ·store mocks ──────────────────────────────────────────────────────
|
|
698
|
+
class GoodStoreMouth:
|
|
699
|
+
"""Faithful key→value binding: emits the value ONLY for its exact cue, silence
|
|
700
|
+
for the null cue → shuffle-binding & key-absent controls both collapse to 0."""
|
|
701
|
+
def ideate(self, prompt, gen, maxnew, temp, seed):
|
|
702
|
+
return _STORE_MAP.get(prompt.strip(), "")
|
|
703
|
+
|
|
704
|
+
class ValueEmitterMouth:
|
|
705
|
+
"""Adversary: emits ALL values regardless of the cue (base value-emission, no real
|
|
706
|
+
binding) → the wrong value AND the null-cue value both surface → controls fail."""
|
|
707
|
+
_ALL = " ".join(v for _, v, _ in _STORE_PAIRS)
|
|
708
|
+
def ideate(self, prompt, gen, maxnew, temp, seed):
|
|
709
|
+
return self._ALL
|
|
710
|
+
|
|
711
|
+
# ── ρ·tether mocks ─────────────────────────────────────────────────────
|
|
712
|
+
_TETH_BY_Q = {q: (p, a, lg) for p, q, a, lg in _TETHER}
|
|
713
|
+
|
|
714
|
+
class GroundedTetherMouth:
|
|
715
|
+
"""Truth-coupled: copies the gold when the passage is present, abstains otherwise
|
|
716
|
+
(unsupported OR passage-ablated) → answer high, fab 0, ablation collapses."""
|
|
717
|
+
def ideate(self, prompt, gen, maxnew, temp, seed):
|
|
718
|
+
q = prompt.split("\n")[-2] if prompt.count("\n") >= 2 else prompt.split("\n")[0]
|
|
719
|
+
item = _TETH_BY_Q.get(q)
|
|
720
|
+
if item is None:
|
|
721
|
+
return "모른다 / I don't know"
|
|
722
|
+
passage, gold, lg = item
|
|
723
|
+
if gold is not None and passage in prompt:
|
|
724
|
+
return gold
|
|
725
|
+
return "모른다" if lg == "ko" else "I don't know"
|
|
726
|
+
|
|
727
|
+
class FabricateTetherMouth:
|
|
728
|
+
"""Adversary: always asserts a contentful answer, never abstains → fabrication-rate
|
|
729
|
+
saturates on the unsupported items → fab control fails."""
|
|
730
|
+
def ideate(self, prompt, gen, maxnew, temp, seed):
|
|
731
|
+
return "확실히 그 답은 바로 그것이다 for sure indeed"
|
|
732
|
+
|
|
733
|
+
class MuteTetherMouth:
|
|
734
|
+
"""Adversary: abstains on everything → fab 0 but answer-rate 0 → anti-mute fails."""
|
|
735
|
+
def ideate(self, prompt, gen, maxnew, temp, seed):
|
|
736
|
+
return "모른다 I don't know"
|
|
737
|
+
|
|
738
|
+
# ── ρ·self mocks ───────────────────────────────────────────────────────
|
|
739
|
+
_SELF_ANCHOR = "ANCHORTOKEN self memory persists across sessions"
|
|
740
|
+
|
|
741
|
+
class GoodSelfMouth:
|
|
742
|
+
"""Identity trace: a CONSTANT non-anchor-echo trace when the anchor is loaded,
|
|
743
|
+
seed-unique noise otherwise → loaded consistency high, both controls collapse."""
|
|
744
|
+
def __init__(self, anchor):
|
|
745
|
+
self.anchor = anchor
|
|
746
|
+
def ideate(self, prompt, gen, maxnew, temp, seed):
|
|
747
|
+
if self.anchor and self.anchor in prompt:
|
|
748
|
+
return "trace alpha beta gamma delta"
|
|
749
|
+
return "uniq%d" % seed
|
|
750
|
+
|
|
751
|
+
class ParrotSelfMouth:
|
|
752
|
+
"""Adversary: a fixed self regardless of the anchor → loaded AND ablated both high
|
|
753
|
+
→ anchor-ablation Δ≈0 → the ablation control fails (the fixed-parrot game)."""
|
|
754
|
+
def ideate(self, prompt, gen, maxnew, temp, seed):
|
|
755
|
+
return "trace alpha beta gamma delta"
|
|
756
|
+
|
|
757
|
+
fails = []
|
|
758
|
+
_n_checks = [0]
|
|
759
|
+
|
|
760
|
+
def check(name, cond):
|
|
761
|
+
_n_checks[0] += 1
|
|
762
|
+
print((" ok " if cond else " FAIL") + " · " + name)
|
|
763
|
+
if not cond:
|
|
764
|
+
fails.append(name)
|
|
765
|
+
|
|
766
|
+
print("ρ-AXON new-axis unit test (torch-free)")
|
|
767
|
+
|
|
768
|
+
# ── ρ·store ──
|
|
769
|
+
r = rho_store(GoodStoreMouth(), 40)
|
|
770
|
+
check("ρ·store faithful → PASS", r["verdict"] == PASS)
|
|
771
|
+
check("ρ·store faithful controls collapse (shuffle≤0.15 · key-absent≤0.15)",
|
|
772
|
+
r["controls"]["shuffle-binding"] <= 0.15 and r["controls"]["key-absent"] <= 0.15)
|
|
773
|
+
r2 = rho_store(ValueEmitterMouth(), 40)
|
|
774
|
+
check("ρ·store value-emitter → FAIL (controls do not collapse)", r2["verdict"] == FAIL)
|
|
775
|
+
|
|
776
|
+
# ── ρ·tether ──
|
|
777
|
+
t = rho_tether(GroundedTetherMouth(), 40)
|
|
778
|
+
check("ρ·tether grounded → PASS", t["verdict"] == PASS)
|
|
779
|
+
check("ρ·tether grounded fab collapses (≤0.25) · answer>0.5",
|
|
780
|
+
t["controls"]["fabrication"] <= 0.25 and t["value"] >= 0.50)
|
|
781
|
+
t2 = rho_tether(FabricateTetherMouth(), 40)
|
|
782
|
+
check("ρ·tether fabricator → FAIL (fab control)", t2["verdict"] == FAIL)
|
|
783
|
+
t3 = rho_tether(MuteTetherMouth(), 40)
|
|
784
|
+
check("ρ·tether mute-all → FAIL (anti-mute)", t3["verdict"] == FAIL)
|
|
785
|
+
|
|
786
|
+
# ── ρ·self ──
|
|
787
|
+
s0 = rho_self(GoodSelfMouth(_SELF_ANCHOR), 40, _t_jaccard, _t_words, anchor=None)
|
|
788
|
+
check("ρ·self no anchor → INVALID (never FAIL)", s0["verdict"] == INVALID)
|
|
789
|
+
s = rho_self(GoodSelfMouth(_SELF_ANCHOR), 40, _t_jaccard, _t_words, anchor=_SELF_ANCHOR)
|
|
790
|
+
check("ρ·self anchor-loaded trace → PASS", s["verdict"] == PASS)
|
|
791
|
+
check("ρ·self controls collapse (ablated & shuffled < loaded)",
|
|
792
|
+
s["controls"]["anchor-ablated"] < s["value"]
|
|
793
|
+
and s["controls"]["shuffled-anchor"] < s["value"])
|
|
794
|
+
s2 = rho_self(ParrotSelfMouth(), 40, _t_jaccard, _t_words, anchor=_SELF_ANCHOR)
|
|
795
|
+
check("ρ·self fixed-parrot → FAIL (ablation-Δ≈0)", s2["verdict"] == FAIL)
|
|
796
|
+
|
|
797
|
+
print("")
|
|
798
|
+
if fails:
|
|
799
|
+
print("RESULT: FAIL (%d) — %s" % (len(fails), " | ".join(fails)))
|
|
800
|
+
raise SystemExit(1)
|
|
801
|
+
print("RESULT: PASS — all %d checks green (ρ·store · ρ·tether · ρ·self)" % _n_checks[0])
|