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
|
@@ -0,0 +1,844 @@
|
|
|
1
|
+
# ==========================================================================
|
|
2
|
+
# ⛔ ENGINE-INTERNAL TOOL — agent 가 직접 타이핑 금지. 학습/직렬화는 cli/ 단일진입만:
|
|
3
|
+
# anima train | anima serialize (canonical=hexa cli/{train,serialize}.hexa).
|
|
4
|
+
# 단, cli/train.hexa 가 held-out DESCENT 게이트를 `python3 … verify_clm_v2.py descent` 로
|
|
5
|
+
# SUBPROCESS 호출한다(=canonical 경로의 정당한 내부 shell-out) — 그래서 __main__ hard-exit 가드는
|
|
6
|
+
# 두지 않는다(그건 canonical 게이트를 깨뜨림). agent 직접실행 차단은 .harness/enforcement.json
|
|
7
|
+
# H-ANIMA-SINGLE-ENTRY pre_bash 룰이 담당(hexa 내부 subprocess 는 안 건드림). #2603
|
|
8
|
+
# ==========================================================================
|
|
9
|
+
#!/usr/bin/env python3
|
|
10
|
+
"""Lane P REMEDY — pure-Python mirror of CORE/clm_decode.hexa's clm_decodable()
|
|
11
|
+
plus a fuller parse, and a byte-roundtrip + golden-reference test harness.
|
|
12
|
+
|
|
13
|
+
This re-parses the raw .clm bytes with the SAME logic as clm_decode.hexa:
|
|
14
|
+
clm_decodable(): magic "CLM\\x01", walk nblk conv blocks via (cout,rest) to find
|
|
15
|
+
the trailer offset, require "CLMX" there.
|
|
16
|
+
parse_clm(): also re-reads block dims and the CLMX ext count (n_ext + each
|
|
17
|
+
ext array's element count), so a round-trip can assert
|
|
18
|
+
structural consistency.
|
|
19
|
+
|
|
20
|
+
No torch / numpy needed for verification (pure stdlib struct). The serializer is
|
|
21
|
+
imported (it guards its torch import) only for the synthetic round-trip.
|
|
22
|
+
|
|
23
|
+
Outputs (verbatim, p7/g63 honest):
|
|
24
|
+
F-CLM-V2-ROUNDTRIP=1 on a clean synthetic round-trip, else =0 + the mismatch.
|
|
25
|
+
golden decodable=true/false + nblk + block0 dims + CLMX-found.
|
|
26
|
+
"""
|
|
27
|
+
from __future__ import annotations
|
|
28
|
+
|
|
29
|
+
import struct
|
|
30
|
+
import sys
|
|
31
|
+
import os
|
|
32
|
+
import math
|
|
33
|
+
|
|
34
|
+
MAGIC = bytes([67, 76, 77, 1])
|
|
35
|
+
CLMX = bytes([67, 76, 77, 88])
|
|
36
|
+
CLMB = bytes([67, 76, 77, 66]) # bind-readout (Hadamard) extension
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _ru32(b: bytes, off: int) -> int:
|
|
40
|
+
return b[off] + b[off + 1] * 0x100 + b[off + 2] * 0x10000 + b[off + 3] * 0x1000000
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def clm_decodable(path_or_bytes) -> bool:
|
|
44
|
+
"""Pure-Python mirror of clm_decode.hexa::clm_decodable (lines 43-64)."""
|
|
45
|
+
rb = path_or_bytes if isinstance(path_or_bytes, (bytes, bytearray)) else open(path_or_bytes, "rb").read()
|
|
46
|
+
if len(rb) < 5:
|
|
47
|
+
return False
|
|
48
|
+
if not (rb[0] == 67 and rb[1] == 76 and rb[2] == 77 and rb[3] == 1):
|
|
49
|
+
return False
|
|
50
|
+
nblk = rb[4]
|
|
51
|
+
off = 5
|
|
52
|
+
b = 0
|
|
53
|
+
while b < nblk:
|
|
54
|
+
if off + 8 > len(rb):
|
|
55
|
+
return False
|
|
56
|
+
cout = _ru32(rb, off)
|
|
57
|
+
rest = _ru32(rb, off + 4)
|
|
58
|
+
off += 8
|
|
59
|
+
n = cout * rest
|
|
60
|
+
off += (n + 1) // 2 # int4 nibbles, 2/byte
|
|
61
|
+
off += cout * 4 # per-channel fp32 scale
|
|
62
|
+
b += 1
|
|
63
|
+
if off + 5 > len(rb):
|
|
64
|
+
return False
|
|
65
|
+
return rb[off] == 67 and rb[off + 1] == 76 and rb[off + 2] == 77 and rb[off + 3] == 88
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def parse_clm(path_or_bytes) -> dict:
|
|
69
|
+
"""Fuller parse: returns block dims + CLMX ext counts. Raises on structural EOF."""
|
|
70
|
+
rb = path_or_bytes if isinstance(path_or_bytes, (bytes, bytearray)) else open(path_or_bytes, "rb").read()
|
|
71
|
+
out = {"len": len(rb), "magic_ok": False, "nblk": None, "blocks": [],
|
|
72
|
+
"clmx_found": False, "n_ext": None, "ext_counts": [], "final_off": None,
|
|
73
|
+
"exact_eof": None, "clmb_found": False, "readout_type": 0,
|
|
74
|
+
"bind_blocks": [], "bind_ext_counts": []}
|
|
75
|
+
if len(rb) < 5:
|
|
76
|
+
return out
|
|
77
|
+
out["magic_ok"] = (rb[0] == 67 and rb[1] == 76 and rb[2] == 77 and rb[3] == 1)
|
|
78
|
+
nblk = rb[4]
|
|
79
|
+
out["nblk"] = nblk
|
|
80
|
+
off = 5
|
|
81
|
+
for _ in range(nblk):
|
|
82
|
+
if off + 8 > len(rb):
|
|
83
|
+
raise ValueError(f"EOF reading block header at off={off}")
|
|
84
|
+
cout = _ru32(rb, off)
|
|
85
|
+
rest = _ru32(rb, off + 4)
|
|
86
|
+
off += 8
|
|
87
|
+
n = cout * rest
|
|
88
|
+
nib = (n + 1) // 2
|
|
89
|
+
off += nib
|
|
90
|
+
off += cout * 4
|
|
91
|
+
out["blocks"].append({"cout": cout, "rest": rest, "nibbles": nib})
|
|
92
|
+
if off > len(rb):
|
|
93
|
+
raise ValueError(f"EOF walking block (cout={cout},rest={rest}) off={off}>len={len(rb)}")
|
|
94
|
+
if off + 5 <= len(rb) and rb[off:off + 4] == CLMX:
|
|
95
|
+
out["clmx_found"] = True
|
|
96
|
+
n_ext = rb[off + 4]
|
|
97
|
+
out["n_ext"] = n_ext
|
|
98
|
+
o2 = off + 5
|
|
99
|
+
for _ in range(n_ext):
|
|
100
|
+
if o2 + 4 > len(rb):
|
|
101
|
+
raise ValueError(f"EOF reading ext count at off={o2}")
|
|
102
|
+
cnt = _ru32(rb, o2)
|
|
103
|
+
o2 += 4
|
|
104
|
+
o2 += cnt * 4
|
|
105
|
+
out["ext_counts"].append(cnt)
|
|
106
|
+
if o2 > len(rb):
|
|
107
|
+
raise ValueError(f"EOF reading ext array (count={cnt}) off={o2}>len={len(rb)}")
|
|
108
|
+
# OPTIONAL CLMB bind-readout trailer: readout_type:u8 + Wa/Wb blocks + 2 ext.
|
|
109
|
+
if o2 + 5 <= len(rb) and rb[o2:o2 + 4] == CLMB:
|
|
110
|
+
out["clmb_found"] = True
|
|
111
|
+
out["readout_type"] = rb[o2 + 4]
|
|
112
|
+
o3 = o2 + 5
|
|
113
|
+
for _ in range(2): # Wa, Wb conv blocks
|
|
114
|
+
cout = _ru32(rb, o3); rest = _ru32(rb, o3 + 4); o3 += 8
|
|
115
|
+
nib = (cout * rest + 1) // 2
|
|
116
|
+
o3 += nib + cout * 4
|
|
117
|
+
out["bind_blocks"].append({"cout": cout, "rest": rest, "nibbles": nib})
|
|
118
|
+
if o3 > len(rb):
|
|
119
|
+
raise ValueError(f"EOF walking CLMB block off={o3}>len={len(rb)}")
|
|
120
|
+
for _ in range(2): # Wa_bias, Wb_bias ext
|
|
121
|
+
cnt = _ru32(rb, o3); o3 += 4 + cnt * 4
|
|
122
|
+
out["bind_ext_counts"].append(cnt)
|
|
123
|
+
if o3 > len(rb):
|
|
124
|
+
raise ValueError(f"EOF reading CLMB ext off={o3}>len={len(rb)}")
|
|
125
|
+
o2 = o3
|
|
126
|
+
out["final_off"] = o2
|
|
127
|
+
out["exact_eof"] = (o2 == len(rb))
|
|
128
|
+
return out
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
# --------------------------------------------------------------------------- #
|
|
132
|
+
# Synthetic round-trip test (no torch): tiny E=2 / 1-trunk model, d=32,K=3,V=256
|
|
133
|
+
# --------------------------------------------------------------------------- #
|
|
134
|
+
def _build_synthetic(d=32, K=3, V=256, E=2, seed=1234):
|
|
135
|
+
"""Legacy L=1/E=2 synthetic — used by the v0.2 byte-eq regression check.
|
|
136
|
+
Returns a logical-slot dict (serialize_v2 accepts logical keys directly)."""
|
|
137
|
+
import numpy as np
|
|
138
|
+
rng = np.random.default_rng(seed)
|
|
139
|
+
def r(*shape):
|
|
140
|
+
return (rng.standard_normal(shape) * 0.1).astype(np.float32)
|
|
141
|
+
sd = {
|
|
142
|
+
# conv blocks: ec/tc/e0/e1 are (cout=d, in=d, K) -> rest=d*K
|
|
143
|
+
"ecW": r(d, d, K),
|
|
144
|
+
"tcW": r(d, d, K),
|
|
145
|
+
"e0W": r(d, d, K),
|
|
146
|
+
"e1W": r(d, d, K),
|
|
147
|
+
"rW": r(E, d, 1), # router conv1d k=1 -> rest=d
|
|
148
|
+
"roW": r(V, d, 1), # readout conv1d k=1 -> rest=d
|
|
149
|
+
# CLMX ext (fp32)
|
|
150
|
+
"embed": r(V, d), # V*d
|
|
151
|
+
"ecB": r(d), "tcB": r(d), "e0B": r(d), "e1B": r(d),
|
|
152
|
+
"rB": r(E), "roB": r(V),
|
|
153
|
+
"tgG": r(d), "tgB": r(d), "noG": r(d), "noB": r(d),
|
|
154
|
+
}
|
|
155
|
+
expect = {
|
|
156
|
+
"nblk": 6,
|
|
157
|
+
"blocks": [
|
|
158
|
+
{"cout": d, "rest": d * K}, {"cout": d, "rest": d * K},
|
|
159
|
+
{"cout": d, "rest": d * K}, {"cout": d, "rest": d * K},
|
|
160
|
+
{"cout": E, "rest": d}, {"cout": V, "rest": d},
|
|
161
|
+
],
|
|
162
|
+
"n_ext": 11,
|
|
163
|
+
"ext_counts": [V * d, d, d, d, d, E, V, d, d, d, d],
|
|
164
|
+
}
|
|
165
|
+
return sd, expect
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
def _build_synthetic_general(d, L, E, K=3, V=256, seed=2026):
|
|
169
|
+
"""General (L,E) synthetic in TORCH state_dict key layout — used by the
|
|
170
|
+
v0.3 general round-trip test. Keys match model.py's CLMConvMoE so the same
|
|
171
|
+
dict drives serialize_v3. Returns (sd, expect)."""
|
|
172
|
+
import numpy as np
|
|
173
|
+
rng = np.random.default_rng(seed)
|
|
174
|
+
def r(*shape):
|
|
175
|
+
return (rng.standard_normal(shape) * 0.1).astype(np.float32)
|
|
176
|
+
sd = {"embed.weight": r(V, d),
|
|
177
|
+
"embed_conv.conv.weight": r(d, d, K), "embed_conv.conv.bias": r(d)}
|
|
178
|
+
for i in range(L):
|
|
179
|
+
sd[f"trunk.{i}.conv.conv.weight"] = r(d, d, K)
|
|
180
|
+
sd[f"trunk.{i}.conv.conv.bias"] = r(d)
|
|
181
|
+
sd[f"trunk.{i}.norm.weight"] = r(d)
|
|
182
|
+
sd[f"trunk.{i}.norm.bias"] = r(d)
|
|
183
|
+
for j in range(E):
|
|
184
|
+
sd[f"moe.experts.{j}.conv.conv.weight"] = r(d, d, K)
|
|
185
|
+
sd[f"moe.experts.{j}.conv.conv.bias"] = r(d)
|
|
186
|
+
sd["moe.router.weight"] = r(E, d, 1)
|
|
187
|
+
sd["moe.router.bias"] = r(E)
|
|
188
|
+
sd["readout.weight"] = r(V, d, 1)
|
|
189
|
+
sd["readout.bias"] = r(V)
|
|
190
|
+
sd["norm_out.weight"] = r(d)
|
|
191
|
+
sd["norm_out.bias"] = r(d)
|
|
192
|
+
# expected layout: blocks = ecW, tcW*L, eW*E, rW(E), roW(V)
|
|
193
|
+
blocks = [{"cout": d, "rest": d * K}] # ecW
|
|
194
|
+
blocks += [{"cout": d, "rest": d * K} for _ in range(L)] # trunk
|
|
195
|
+
blocks += [{"cout": d, "rest": d * K} for _ in range(E)] # experts
|
|
196
|
+
blocks += [{"cout": E, "rest": d}, {"cout": V, "rest": d}] # router, readout
|
|
197
|
+
# ext = embed, ecB, tcB*L, eB*E, rB, roB, tgG*L, tgB*L, noG, noB
|
|
198
|
+
ext = [V * d, d] + [d] * L + [d] * E + [E, V] + [d] * L + [d] * L + [d, d]
|
|
199
|
+
expect = {"nblk": len(blocks), "blocks": blocks, "n_ext": len(ext), "ext_counts": ext}
|
|
200
|
+
return sd, expect
|
|
201
|
+
|
|
202
|
+
|
|
203
|
+
def _build_synthetic_bind(d, L, E, k, V=256, K=3, seed=4242):
|
|
204
|
+
"""General (L,E) BIND synthetic in LOGICAL-slot keys (serialize_v3_bind accepts
|
|
205
|
+
them). Trunk slots match the v3 general order; the readout is Wa/Wb (k,d,1) +
|
|
206
|
+
Wo (V,k,1) — the EXP-3 BindCLM. Returns (sd, expect_main, expect_bind)."""
|
|
207
|
+
import numpy as np
|
|
208
|
+
rng = np.random.default_rng(seed)
|
|
209
|
+
def r(*shape):
|
|
210
|
+
return (rng.standard_normal(shape) * 0.1).astype(np.float32)
|
|
211
|
+
sd = {"embed": r(V, d), "ecW": r(d, d, K), "ecB": r(d)}
|
|
212
|
+
for i in range(L):
|
|
213
|
+
sd[f"tc{i}W"] = r(d, d, K); sd[f"tc{i}B"] = r(d)
|
|
214
|
+
sd[f"tg{i}G"] = r(d); sd[f"tg{i}B"] = r(d)
|
|
215
|
+
for j in range(E):
|
|
216
|
+
sd[f"e{j}W"] = r(d, d, K); sd[f"e{j}B"] = r(d)
|
|
217
|
+
sd["rW"] = r(E, d, 1); sd["rB"] = r(E)
|
|
218
|
+
sd["noG"] = r(d); sd["noB"] = r(d)
|
|
219
|
+
# bind readout: Wa,Wb (k,d,1), Wo (V,k,1)
|
|
220
|
+
sd["Wa"] = r(k, d, 1); sd["WaB"] = r(k)
|
|
221
|
+
sd["Wb"] = r(k, d, 1); sd["WbB"] = r(k)
|
|
222
|
+
sd["Wo"] = r(V, k, 1); sd["WoB"] = r(V)
|
|
223
|
+
# expected MAIN: blocks = ecW, tcW*L, eW*E, rW(E,d), roW(=Wo: V,k)
|
|
224
|
+
blocks = [{"cout": d, "rest": d * K}]
|
|
225
|
+
blocks += [{"cout": d, "rest": d * K} for _ in range(L)]
|
|
226
|
+
blocks += [{"cout": d, "rest": d * K} for _ in range(E)]
|
|
227
|
+
blocks += [{"cout": E, "rest": d}, {"cout": V, "rest": k}] # router, Wo readout
|
|
228
|
+
ext = [V * d, d] + [d] * L + [d] * E + [E, V] + [d] * L + [d] * L + [d, d]
|
|
229
|
+
expect_main = {"nblk": len(blocks), "blocks": blocks, "n_ext": len(ext),
|
|
230
|
+
"ext_counts": ext}
|
|
231
|
+
expect_bind = {"bind_blocks": [{"cout": k, "rest": d}, {"cout": k, "rest": d}],
|
|
232
|
+
"bind_ext_counts": [k, k]}
|
|
233
|
+
return sd, expect_main, expect_bind
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
def run_roundtrip_bind(tmp_path, d, L, E, k, readout_type, V=256, K=3):
|
|
237
|
+
"""v0.3 BIND round-trip via serialize_v3_bind: structural (CLMB) + forward
|
|
238
|
+
sanity (bind decode produces finite logits with the Hadamard/add readout)."""
|
|
239
|
+
import clm_serialize_v2 as S
|
|
240
|
+
import numpy as np
|
|
241
|
+
sd, exp_main, exp_bind = _build_synthetic_bind(d, L, E, k, V=V, K=K)
|
|
242
|
+
S.serialize_v3_bind(sd, n_trunk_layers=L, n_experts=E,
|
|
243
|
+
readout_type=readout_type, out_path=tmp_path)
|
|
244
|
+
rb = open(tmp_path, "rb").read()
|
|
245
|
+
ok, why = _structural_check(rb, exp_main) # main body unchanged vs additive grammar
|
|
246
|
+
if not ok:
|
|
247
|
+
return False, f"main: {why}"
|
|
248
|
+
p = parse_clm(rb)
|
|
249
|
+
if not p["clmb_found"]:
|
|
250
|
+
return False, "CLMB trailer not found"
|
|
251
|
+
if p["readout_type"] != readout_type:
|
|
252
|
+
return False, f"readout_type {p['readout_type']} != {readout_type}"
|
|
253
|
+
bb = [{"cout": x["cout"], "rest": x["rest"]} for x in p["bind_blocks"]]
|
|
254
|
+
if bb != exp_bind["bind_blocks"]:
|
|
255
|
+
return False, f"bind_blocks {bb} != {exp_bind['bind_blocks']}"
|
|
256
|
+
if p["bind_ext_counts"] != exp_bind["bind_ext_counts"]:
|
|
257
|
+
return False, f"bind_ext {p['bind_ext_counts']} != {exp_bind['bind_ext_counts']}"
|
|
258
|
+
if not p["exact_eof"]:
|
|
259
|
+
return False, f"trailing bytes: final_off={p['final_off']} != len={p['len']}"
|
|
260
|
+
# forward sanity: the bind mirror loads + forwards to finite [T,V] logits.
|
|
261
|
+
W = _load_clm_weights(rb)
|
|
262
|
+
if W is None or W["rtype"] != readout_type or W["kbind"] != k:
|
|
263
|
+
return False, f"weights load rtype={W and W['rtype']} kbind={W and W['kbind']}"
|
|
264
|
+
tok = (np.arange(24) % V).astype(float)
|
|
265
|
+
lg = _fwd_logits(W, tok, 24)
|
|
266
|
+
if lg.shape != (24, V) or not np.all(np.isfinite(lg)):
|
|
267
|
+
return False, f"forward shape={lg.shape} finite={np.all(np.isfinite(lg))}"
|
|
268
|
+
return True, f"ok (rtype={readout_type} k={k}, logits {lg.shape} finite)"
|
|
269
|
+
|
|
270
|
+
|
|
271
|
+
def run_roundtrip(tmp_path: str) -> tuple[bool, str]:
|
|
272
|
+
import clm_serialize_v2 as S
|
|
273
|
+
sd, expect = _build_synthetic()
|
|
274
|
+
S.serialize_v2(sd, cfg=None, out_path=tmp_path) # cfg=None: synthetic vouches E=2/L1
|
|
275
|
+
rb = open(tmp_path, "rb").read()
|
|
276
|
+
|
|
277
|
+
if not clm_decodable(rb):
|
|
278
|
+
return False, "clm_decodable(synthetic)=false"
|
|
279
|
+
p = parse_clm(rb)
|
|
280
|
+
if not p["magic_ok"]:
|
|
281
|
+
return False, "magic mismatch"
|
|
282
|
+
if p["nblk"] != expect["nblk"]:
|
|
283
|
+
return False, f"nblk {p['nblk']} != {expect['nblk']}"
|
|
284
|
+
if len(p["blocks"]) != 6:
|
|
285
|
+
return False, f"walked {len(p['blocks'])} blocks != 6"
|
|
286
|
+
for i, (got, exp) in enumerate(zip(p["blocks"], expect["blocks"])):
|
|
287
|
+
if got["cout"] != exp["cout"] or got["rest"] != exp["rest"]:
|
|
288
|
+
return False, (f"block{i} dims got cout={got['cout']},rest={got['rest']} "
|
|
289
|
+
f"!= cout={exp['cout']},rest={exp['rest']}")
|
|
290
|
+
if not p["clmx_found"]:
|
|
291
|
+
return False, "CLMX trailer not found"
|
|
292
|
+
if p["n_ext"] != expect["n_ext"]:
|
|
293
|
+
return False, f"n_ext {p['n_ext']} != {expect['n_ext']}"
|
|
294
|
+
if p["ext_counts"] != expect["ext_counts"]:
|
|
295
|
+
return False, f"ext_counts {p['ext_counts']} != {expect['ext_counts']}"
|
|
296
|
+
if not p["exact_eof"]:
|
|
297
|
+
return False, f"trailing bytes: final_off={p['final_off']} != len={p['len']}"
|
|
298
|
+
|
|
299
|
+
# value round-trip: re-dequant the readout-bias ext and the ecW block, confirm
|
|
300
|
+
# the int4 dequant is within the quant step of the original (sanity on packing).
|
|
301
|
+
import numpy as np
|
|
302
|
+
# check ext embed first element survives fp32 exactly
|
|
303
|
+
# (parse the embed ext payload directly)
|
|
304
|
+
off = 5
|
|
305
|
+
for _ in range(6):
|
|
306
|
+
cout = _ru32(rb, off); rest = _ru32(rb, off + 4); off += 8
|
|
307
|
+
off += (cout * rest + 1) // 2 + cout * 4
|
|
308
|
+
off += 5 # CLMX + n_ext
|
|
309
|
+
cnt = _ru32(rb, off); off += 4
|
|
310
|
+
embed_back = np.frombuffer(rb[off:off + cnt * 4], dtype="<f4")
|
|
311
|
+
if not np.allclose(embed_back, sd["embed"].reshape(-1), atol=0, rtol=0):
|
|
312
|
+
return False, "embed ext fp32 round-trip mismatch (lossless expected)"
|
|
313
|
+
|
|
314
|
+
return True, "ok"
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def _structural_check(rb, expect):
|
|
318
|
+
"""Shared structural assertions: decodable + nblk + per-block dims + CLMX +
|
|
319
|
+
n_ext + ext_counts + exact_eof. Returns (ok, why)."""
|
|
320
|
+
if not clm_decodable(rb):
|
|
321
|
+
return False, "clm_decodable=false"
|
|
322
|
+
p = parse_clm(rb)
|
|
323
|
+
if not p["magic_ok"]:
|
|
324
|
+
return False, "magic mismatch"
|
|
325
|
+
if p["nblk"] != expect["nblk"]:
|
|
326
|
+
return False, f"nblk {p['nblk']} != {expect['nblk']}"
|
|
327
|
+
if len(p["blocks"]) != expect["nblk"]:
|
|
328
|
+
return False, f"walked {len(p['blocks'])} blocks != {expect['nblk']}"
|
|
329
|
+
for i, (got, exp) in enumerate(zip(p["blocks"], expect["blocks"])):
|
|
330
|
+
if got["cout"] != exp["cout"] or got["rest"] != exp["rest"]:
|
|
331
|
+
return False, (f"block{i} got cout={got['cout']},rest={got['rest']} "
|
|
332
|
+
f"!= cout={exp['cout']},rest={exp['rest']}")
|
|
333
|
+
if not p["clmx_found"]:
|
|
334
|
+
return False, "CLMX trailer not found"
|
|
335
|
+
if p["n_ext"] != expect["n_ext"]:
|
|
336
|
+
return False, f"n_ext {p['n_ext']} != {expect['n_ext']}"
|
|
337
|
+
if p["ext_counts"] != expect["ext_counts"]:
|
|
338
|
+
return False, f"ext_counts {p['ext_counts']} != {expect['ext_counts']}"
|
|
339
|
+
if not p["exact_eof"]:
|
|
340
|
+
return False, f"trailing bytes: final_off={p['final_off']} != len={p['len']}"
|
|
341
|
+
return True, "ok"
|
|
342
|
+
|
|
343
|
+
|
|
344
|
+
def run_roundtrip_general(tmp_path, d, L, E, K=3, V=256):
|
|
345
|
+
"""v0.3 general (L,E) round-trip via serialize_v3 (torch-key state_dict)."""
|
|
346
|
+
import clm_serialize_v2 as S
|
|
347
|
+
import numpy as np
|
|
348
|
+
sd, expect = _build_synthetic_general(d, L, E, K=K, V=V)
|
|
349
|
+
S.serialize_v3(sd, n_trunk_layers=L, n_experts=E, out_path=tmp_path)
|
|
350
|
+
rb = open(tmp_path, "rb").read()
|
|
351
|
+
ok, why = _structural_check(rb, expect)
|
|
352
|
+
if not ok:
|
|
353
|
+
return False, why
|
|
354
|
+
# value sanity: embed ext (first ext after CLMX) survives fp32 lossless.
|
|
355
|
+
off = 5
|
|
356
|
+
for blk in expect["blocks"]:
|
|
357
|
+
n = blk["cout"] * blk["rest"]
|
|
358
|
+
off += 8 + (n + 1) // 2 + blk["cout"] * 4
|
|
359
|
+
off += 5 # CLMX + n_ext
|
|
360
|
+
cnt = _ru32(rb, off); off += 4
|
|
361
|
+
embed_back = np.frombuffer(rb[off:off + cnt * 4], dtype="<f4")
|
|
362
|
+
if not np.allclose(embed_back, sd["embed.weight"].reshape(-1), atol=0, rtol=0):
|
|
363
|
+
return False, "embed ext fp32 round-trip mismatch"
|
|
364
|
+
return True, "ok"
|
|
365
|
+
|
|
366
|
+
|
|
367
|
+
def run_v3_byteeq_v2(here):
|
|
368
|
+
"""v0.3 byte-eq REGRESSION: serialize_v3(L=1,E=2) must be byte-IDENTICAL to
|
|
369
|
+
serialize_v2 on the same logical weights (no regression on the existing
|
|
370
|
+
format). Builds the legacy L1/E2 synthetic and packs it both ways."""
|
|
371
|
+
import clm_serialize_v2 as S
|
|
372
|
+
sd2, _ = _build_synthetic() # logical-slot keys, L1/E2
|
|
373
|
+
# serialize_v2 (logical keys, cfg=None)
|
|
374
|
+
p2 = os.path.join(here, "_rt_v2.clm")
|
|
375
|
+
S.serialize_v2(sd2, cfg=None, out_path=p2)
|
|
376
|
+
b2 = open(p2, "rb").read()
|
|
377
|
+
# serialize_v3 needs torch-key OR logical-key; the general keymap falls back
|
|
378
|
+
# to logical slot names (_get checks `logical in sd` first). The v3 logical
|
|
379
|
+
# slot names for L1/E2 are ec/tc0/e0/e1... so remap the legacy logical dict.
|
|
380
|
+
sd3 = {"ecW": sd2["ecW"], "tc0W": sd2["tcW"], "e0W": sd2["e0W"], "e1W": sd2["e1W"],
|
|
381
|
+
"rW": sd2["rW"], "roW": sd2["roW"],
|
|
382
|
+
"embed": sd2["embed"], "ecB": sd2["ecB"], "tc0B": sd2["tcB"],
|
|
383
|
+
"e0B": sd2["e0B"], "e1B": sd2["e1B"], "rB": sd2["rB"], "roB": sd2["roB"],
|
|
384
|
+
"tg0G": sd2["tgG"], "tg0B": sd2["tgB"], "noG": sd2["noG"], "noB": sd2["noB"]}
|
|
385
|
+
p3 = os.path.join(here, "_rt_v3.clm")
|
|
386
|
+
S.serialize_v3(sd3, n_trunk_layers=1, n_experts=2, out_path=p3)
|
|
387
|
+
b3 = open(p3, "rb").read()
|
|
388
|
+
for pth in (p2, p3):
|
|
389
|
+
try:
|
|
390
|
+
os.remove(pth)
|
|
391
|
+
except OSError:
|
|
392
|
+
pass
|
|
393
|
+
if b2 == b3:
|
|
394
|
+
return True, f"byte-identical ({len(b2)} bytes)"
|
|
395
|
+
return False, f"v2 ({len(b2)}B) != v3 ({len(b3)}B)"
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
# --------------------------------------------------------------------------- #
|
|
399
|
+
# HELD-OUT mirror-DESCENT gate (the OVERFIT detector)
|
|
400
|
+
# --------------------------------------------------------------------------- #
|
|
401
|
+
# WHY this exists: the structural round-trips above only prove the .clm is
|
|
402
|
+
# *decodable* (right shape/headers). They say NOTHING about whether the
|
|
403
|
+
# serialized weights actually PREDICT TEXT. A model that memorized a tiny corpus
|
|
404
|
+
# (overfit) is structurally perfect yet useless on held-out text — and the
|
|
405
|
+
# engine's own clm_forward_ce can MISS this, because hexa-lang's dt_ln (atanh
|
|
406
|
+
# series, flame_math.hexa) is numerically wrong away from x=1: dt_ln(256)=4.799
|
|
407
|
+
# (true ln=5.545), dt_ln(1e-6)=-5.14 (true -13.82). That clamps per-position CE
|
|
408
|
+
# at ~5.14 (nn_lib.hexa nn_ce_loss_allpos uses -dt_ln(p_t)), so a broken/overfit
|
|
409
|
+
# model gets a falsely-low engine CE and reads GREEN. (clm303 precedent 2026-06-24:
|
|
410
|
+
# engine model_ce 3.30 < shuffle 4.93 = "GREEN" while the true held-out CE was
|
|
411
|
+
# 7.6-13.7 = worse than random.)
|
|
412
|
+
#
|
|
413
|
+
# This gate is a FAITHFUL pure-numpy mirror of core/clm_decode.hexa's forward
|
|
414
|
+
# (byte-reproduces state/mid_convmoe_fire/clm_decode_mirror.py and the engine
|
|
415
|
+
# clm_forward_ce) BUT uses math.log (correct, dt_ln-immune), reading the .clm
|
|
416
|
+
# BYTES directly. It is build-time SERIALIZE-INTEGRITY tooling, NOT a ρ-AXON reach
|
|
417
|
+
# capability verdict — capability verdicts still go through the single entry
|
|
418
|
+
# `cli/anima.hexa -- eval` (a_engine_native_learning). It exists so a broken or
|
|
419
|
+
# overfit .clm cannot be marked "done" or HF-uploaded.
|
|
420
|
+
#
|
|
421
|
+
# It MUST be evaluated on HELD-OUT text (never the training corpus — that would
|
|
422
|
+
# hide overfitting), and optionally reports the train-vs-held-out gap.
|
|
423
|
+
|
|
424
|
+
INT4_OFF = 8 # nibble code = (byte&0xF) - 8
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def _load_clm_weights(path):
|
|
428
|
+
"""Pure-numpy load of a .clm (v0.2/v0.3) into a weight dict, matching
|
|
429
|
+
core/clm_decode.hexa::_clmd_load EXACTLY (int4 dequant w=code*scale, general
|
|
430
|
+
(L,E) derivation). Requires numpy. Returns None if not decodable."""
|
|
431
|
+
import numpy as np
|
|
432
|
+
rb = open(path, "rb").read() if not isinstance(path, (bytes, bytearray)) else path
|
|
433
|
+
if not clm_decodable(rb):
|
|
434
|
+
return None
|
|
435
|
+
nblk = rb[4]
|
|
436
|
+
# walk headers for (cout,rest) to derive E,V and block offsets
|
|
437
|
+
off = 5
|
|
438
|
+
hdrs = []
|
|
439
|
+
for _ in range(nblk):
|
|
440
|
+
c = _ru32(rb, off); r = _ru32(rb, off + 4)
|
|
441
|
+
hdrs.append((c, r))
|
|
442
|
+
off += 8 + (c * r + 1) // 2 + c * 4
|
|
443
|
+
E = hdrs[nblk - 2][0]
|
|
444
|
+
V = hdrs[nblk - 1][0]
|
|
445
|
+
L = nblk - E - 3
|
|
446
|
+
d = hdrs[0][0]
|
|
447
|
+
K = hdrs[0][1] // d
|
|
448
|
+
|
|
449
|
+
def rd_block(o):
|
|
450
|
+
cout = _ru32(rb, o); o += 4
|
|
451
|
+
rest = _ru32(rb, o); o += 4
|
|
452
|
+
n = cout * rest
|
|
453
|
+
nb = (n + 1) // 2
|
|
454
|
+
raw = np.frombuffer(rb, dtype=np.uint8, count=nb, offset=o); o += nb
|
|
455
|
+
lo = (raw & 0xF).astype(np.int64) - INT4_OFF
|
|
456
|
+
hi = ((raw >> 4) & 0xF).astype(np.int64) - INT4_OFF
|
|
457
|
+
codes = np.empty(2 * nb, dtype=np.float64)
|
|
458
|
+
codes[0::2] = lo
|
|
459
|
+
codes[1::2] = hi
|
|
460
|
+
codes = codes[:n].reshape(cout, rest)
|
|
461
|
+
scale = np.frombuffer(rb, dtype="<f4", count=cout, offset=o).astype(np.float64)
|
|
462
|
+
o += 4 * cout
|
|
463
|
+
return codes * scale[:, None], o
|
|
464
|
+
|
|
465
|
+
def rd_ext(o):
|
|
466
|
+
n = _ru32(rb, o); o += 4
|
|
467
|
+
arr = np.frombuffer(rb, dtype="<f4", count=n, offset=o).astype(np.float64)
|
|
468
|
+
o += 4 * n
|
|
469
|
+
return arr, o
|
|
470
|
+
|
|
471
|
+
off = 5
|
|
472
|
+
ecW, off = rd_block(off)
|
|
473
|
+
tcW = []
|
|
474
|
+
for _ in range(L):
|
|
475
|
+
w, off = rd_block(off); tcW.append(w)
|
|
476
|
+
eW = []
|
|
477
|
+
for _ in range(E):
|
|
478
|
+
w, off = rd_block(off); eW.append(w)
|
|
479
|
+
rW, off = rd_block(off)
|
|
480
|
+
roW, off = rd_block(off)
|
|
481
|
+
off += 5 # CLMX + n_ext
|
|
482
|
+
embed, off = rd_ext(off)
|
|
483
|
+
ecB, off = rd_ext(off)
|
|
484
|
+
tcB = []
|
|
485
|
+
for _ in range(L):
|
|
486
|
+
a, off = rd_ext(off); tcB.append(a)
|
|
487
|
+
eB = []
|
|
488
|
+
for _ in range(E):
|
|
489
|
+
a, off = rd_ext(off); eB.append(a)
|
|
490
|
+
rB, off = rd_ext(off)
|
|
491
|
+
roB, off = rd_ext(off)
|
|
492
|
+
tgG = []
|
|
493
|
+
for _ in range(L):
|
|
494
|
+
a, off = rd_ext(off); tgG.append(a)
|
|
495
|
+
tgB = []
|
|
496
|
+
for _ in range(L):
|
|
497
|
+
a, off = rd_ext(off); tgB.append(a)
|
|
498
|
+
noG, off = rd_ext(off)
|
|
499
|
+
noB, off = rd_ext(off)
|
|
500
|
+
# OPTIONAL CLMB bind-readout trailer (math.log mirror — dt_ln-immune gate).
|
|
501
|
+
rtype = 0
|
|
502
|
+
Wa = Wb = WaB = WbB = None
|
|
503
|
+
kbind = roW.shape[1]
|
|
504
|
+
if off + 5 <= len(rb) and rb[off] == 67 and rb[off + 1] == 76 \
|
|
505
|
+
and rb[off + 2] == 77 and rb[off + 3] == 66: # "CLMB"
|
|
506
|
+
rtype = rb[off + 4]
|
|
507
|
+
off += 5
|
|
508
|
+
Wa, off = rd_block(off) # [k, d]
|
|
509
|
+
Wb, off = rd_block(off) # [k, d]
|
|
510
|
+
WaB, off = rd_ext(off) # [k]
|
|
511
|
+
WbB, off = rd_ext(off) # [k]
|
|
512
|
+
kbind = Wa.shape[0]
|
|
513
|
+
return dict(d=d, E=E, V=V, K=K, L=L, ecW=ecW, tcW=tcW, eW=eW, rW=rW, roW=roW,
|
|
514
|
+
embed=embed.reshape(V, d), ecB=ecB, tcB=tcB, eB=eB, rB=rB, roB=roB,
|
|
515
|
+
tgG=tgG, tgB=tgB, noG=noG, noB=noB,
|
|
516
|
+
rtype=rtype, kbind=kbind, Wa=Wa, Wb=Wb, WaB=WaB, WbB=WbB)
|
|
517
|
+
|
|
518
|
+
|
|
519
|
+
def _gelu(x):
|
|
520
|
+
import numpy as np
|
|
521
|
+
# tanh approx — matches generator.hexa::_gen_gelu (same form clm_decode uses)
|
|
522
|
+
inner = 0.7978845608 * (x + 0.044715 * x * x * x)
|
|
523
|
+
a = np.clip(inner, -15.0, 15.0)
|
|
524
|
+
e2 = np.exp(2.0 * a)
|
|
525
|
+
return 0.5 * x * (1.0 + (e2 - 1.0) / (e2 + 1.0))
|
|
526
|
+
|
|
527
|
+
|
|
528
|
+
def _conv1d(x, w2d, b, T, Cin, Cout, K, dil):
|
|
529
|
+
import numpy as np
|
|
530
|
+
Kdim = Cin * K
|
|
531
|
+
xcol = np.zeros((T, Kdim))
|
|
532
|
+
idx = np.arange(Cin) * K
|
|
533
|
+
for k in range(K):
|
|
534
|
+
shift = dil * (K - 1 - k)
|
|
535
|
+
if shift == 0:
|
|
536
|
+
xcol[:, idx + k] = x
|
|
537
|
+
elif shift < T:
|
|
538
|
+
xcol[shift:, idx + k] = x[:T - shift]
|
|
539
|
+
return xcol @ w2d.T + b[None, :]
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
def _gn1(x, g, b):
|
|
543
|
+
import numpy as np
|
|
544
|
+
mu = x.mean(1, keepdims=True)
|
|
545
|
+
var = x.var(1, keepdims=True)
|
|
546
|
+
return (x - mu) / np.sqrt(var + 1e-5) * g[None, :] + b[None, :]
|
|
547
|
+
|
|
548
|
+
|
|
549
|
+
def _fwd_logits(W, tok, T):
|
|
550
|
+
import numpy as np
|
|
551
|
+
d, E, V, K, L = W["d"], W["E"], W["V"], W["K"], W["L"]
|
|
552
|
+
xe = W["embed"][tok.astype(int)]
|
|
553
|
+
xt = _conv1d(xe, W["ecW"], W["ecB"], T, d, d, K, 1)
|
|
554
|
+
dil = 1
|
|
555
|
+
for li in range(L):
|
|
556
|
+
de = min(dil, 512)
|
|
557
|
+
h = _conv1d(xt, W["tcW"][li], W["tcB"][li], T, d, d, K, de)
|
|
558
|
+
xt = xt + _gelu(_gn1(h, W["tgG"][li], W["tgB"][li]))
|
|
559
|
+
dil *= 2
|
|
560
|
+
lr = _conv1d(xt, W["rW"], W["rB"], T, d, E, 1, 1)
|
|
561
|
+
exo = [_gelu(_conv1d(xt, W["eW"][e], W["eB"][e], T, d, d, K, 1)) for e in range(E)]
|
|
562
|
+
p = np.exp(lr - lr.max(1, keepdims=True)); p /= p.sum(1, keepdims=True)
|
|
563
|
+
y = sum(p[:, e:e + 1] * exo[e] for e in range(E))
|
|
564
|
+
yn = _gn1(y, W["noG"], W["noB"])
|
|
565
|
+
rtype = W.get("rtype", 0)
|
|
566
|
+
if rtype:
|
|
567
|
+
k = W["kbind"]
|
|
568
|
+
u = _conv1d(yn, W["Wa"], W["WaB"], T, d, k, 1, 1) # [T,k]
|
|
569
|
+
v = _conv1d(yn, W["Wb"], W["WbB"], T, d, k, 1, 1) # [T,k]
|
|
570
|
+
g = (u * v) if rtype == 1 else (u + v)
|
|
571
|
+
return _conv1d(g, W["roW"], W["roB"], T, k, V, 1, 1) # Wo: [T,V]
|
|
572
|
+
return _conv1d(yn, W["roW"], W["roB"], T, d, V, 1, 1)
|
|
573
|
+
|
|
574
|
+
|
|
575
|
+
def _ce_allpos(logits, tgt, T):
|
|
576
|
+
import numpy as np
|
|
577
|
+
z = logits - logits.max(1, keepdims=True)
|
|
578
|
+
lse = np.log(np.exp(z).sum(1))
|
|
579
|
+
return float((-(z[np.arange(T), tgt.astype(int)] - lse)).sum() / T)
|
|
580
|
+
|
|
581
|
+
|
|
582
|
+
def mirror_ce(clm_path, corpus_path, nwin=64, T=24):
|
|
583
|
+
"""Faithful mirror next-byte CE over `nwin` windows of a byte corpus, using
|
|
584
|
+
math.log (correct — dt_ln-immune). Returns (model_ce, shuffle_ce, uniform_ce,
|
|
585
|
+
windows). uniform_ce = ln(V) (the TRUE blind baseline, not the buggy engine
|
|
586
|
+
dt_ln(V))."""
|
|
587
|
+
import numpy as np
|
|
588
|
+
W = _load_clm_weights(clm_path)
|
|
589
|
+
if W is None:
|
|
590
|
+
raise ValueError(f"{clm_path}: not v0.2/v0.3 decodable")
|
|
591
|
+
V = W["V"]
|
|
592
|
+
rb = open(corpus_path, "rb").read()
|
|
593
|
+
n = len(rb)
|
|
594
|
+
stride = max(1, (n - T - 1) // nwin)
|
|
595
|
+
sm = ss = 0.0
|
|
596
|
+
cnt = 0
|
|
597
|
+
for s in range(nwin):
|
|
598
|
+
base = s * stride
|
|
599
|
+
if base + T + 1 <= n:
|
|
600
|
+
tok = np.frombuffer(rb, np.uint8, T, base).astype(float)
|
|
601
|
+
tgt = np.frombuffer(rb, np.uint8, T, base + 1).astype(float)
|
|
602
|
+
lg = _fwd_logits(W, tok, T)
|
|
603
|
+
sm += _ce_allpos(lg, tgt, T)
|
|
604
|
+
ss += _ce_allpos(lg, tgt[::-1].copy(), T)
|
|
605
|
+
cnt += 1
|
|
606
|
+
return sm / cnt, ss / cnt, math.log(V), cnt
|
|
607
|
+
|
|
608
|
+
|
|
609
|
+
def descent_gate(clm_path, heldout_corpus, train_corpus=None, nwin=64,
|
|
610
|
+
max_gap=3.0):
|
|
611
|
+
"""The OVERFIT detector. PASS iff the serialized .clm genuinely models
|
|
612
|
+
HELD-OUT text: model_ce < uniform AND model_ce < shuffle. If a `train_corpus`
|
|
613
|
+
is given, also reports the train-vs-held-out gap (large gap = overfit warning)
|
|
614
|
+
and FAILs when held-out fails the descent test (the gap alone is advisory).
|
|
615
|
+
|
|
616
|
+
Returns (ok: bool, report: dict). Frozen bars: uniform=ln(V), shuffle=
|
|
617
|
+
reversed-target control. NOT tunable (a_break_the_wall: frozen-first)."""
|
|
618
|
+
h_model, h_shuf, uni, h_win = mirror_ce(clm_path, heldout_corpus, nwin)
|
|
619
|
+
lt_uniform = h_model < uni
|
|
620
|
+
lt_shuffle = h_model < h_shuf
|
|
621
|
+
ok = lt_uniform and lt_shuffle
|
|
622
|
+
rep = {"heldout_model_ce": round(h_model, 5),
|
|
623
|
+
"heldout_shuffle_ce": round(h_shuf, 5),
|
|
624
|
+
"uniform_ce_lnV": round(uni, 5),
|
|
625
|
+
"heldout_lt_uniform": lt_uniform,
|
|
626
|
+
"heldout_lt_shuffle": lt_shuffle,
|
|
627
|
+
"heldout_windows": h_win,
|
|
628
|
+
"train_model_ce": None, "train_heldout_gap": None,
|
|
629
|
+
"overfit_warning": False}
|
|
630
|
+
if train_corpus and os.path.exists(train_corpus):
|
|
631
|
+
t_model, _, _, _ = mirror_ce(clm_path, train_corpus, nwin)
|
|
632
|
+
gap = h_model - t_model
|
|
633
|
+
rep["train_model_ce"] = round(t_model, 5)
|
|
634
|
+
rep["train_heldout_gap"] = round(gap, 5)
|
|
635
|
+
# Overfit signature: descends on train but the held-out gap is huge.
|
|
636
|
+
rep["overfit_warning"] = (t_model < uni) and (gap > max_gap)
|
|
637
|
+
return ok, rep
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def run_descent_gate_cli(clm_path, heldout_corpus, train_corpus=None, nwin=64):
|
|
641
|
+
"""Verbatim-output CLI wrapper (p7/c9 honest). Prints F-CLM-DESCENT=1/0 +
|
|
642
|
+
the report, exits 0 on PASS else 1. Used by train.py / train.hexa as the
|
|
643
|
+
post-serialize self-verify (a_clm_gen_pipeline)."""
|
|
644
|
+
try:
|
|
645
|
+
ok, rep = descent_gate(clm_path, heldout_corpus, train_corpus, nwin)
|
|
646
|
+
except Exception as e: # numpy missing / undecodable — honest fail
|
|
647
|
+
print(f"F-CLM-DESCENT=0\nDESCENT_ERROR: {e}")
|
|
648
|
+
return 1
|
|
649
|
+
print(f"F-CLM-DESCENT={'1' if ok else '0'}")
|
|
650
|
+
print("DESCENT " + repr(rep))
|
|
651
|
+
if rep["overfit_warning"]:
|
|
652
|
+
print("OVERFIT_WARNING: descends on train but held-out gap "
|
|
653
|
+
f"{rep['train_heldout_gap']} > 3.0 — the serialized .clm memorized "
|
|
654
|
+
"its corpus and does NOT generalize. Re-train with regularization / "
|
|
655
|
+
"a larger corpus (re-serialization will NOT fix this).")
|
|
656
|
+
if not ok:
|
|
657
|
+
print("DESCENT_FAIL: serialized .clm does NOT model held-out text "
|
|
658
|
+
f"(model_ce {rep['heldout_model_ce']} vs uniform {rep['uniform_ce_lnV']}, "
|
|
659
|
+
f"shuffle {rep['heldout_shuffle_ce']}). Broken or overfit — do NOT "
|
|
660
|
+
"mark done / HF-upload.")
|
|
661
|
+
return 0 if ok else 1
|
|
662
|
+
|
|
663
|
+
|
|
664
|
+
def serialize_self_verify(clm_path, train_corpus, heldout=None, skip=False,
|
|
665
|
+
nwin=64, tail_frac=0.1, tag="CLM"):
|
|
666
|
+
"""Post-serialize convenience for the trainers (train.py / train.hexa bridge).
|
|
667
|
+
Runs the held-out mirror-DESCENT gate right after a .clm is written, so a
|
|
668
|
+
broken/overfit artifact can't be marked done. If `heldout` is None, holds out
|
|
669
|
+
a DEEP TAIL slice of `train_corpus` (last `tail_frac`, written to a temp file)
|
|
670
|
+
as a stand-in held-out set — never evaluate the gate on the head the model
|
|
671
|
+
trained on. Returns the gate rc (0=PASS). Honest no-op (rc 0) when skip=True
|
|
672
|
+
or no usable corpus, with a printed reason."""
|
|
673
|
+
if skip:
|
|
674
|
+
print(f"{tag}_DESCENT_GATE=SKIPPED (--no-descent-gate)")
|
|
675
|
+
return 0
|
|
676
|
+
held = heldout
|
|
677
|
+
tmp_made = None
|
|
678
|
+
try:
|
|
679
|
+
if held is None:
|
|
680
|
+
if not train_corpus or not os.path.exists(train_corpus):
|
|
681
|
+
print(f"{tag}_DESCENT_GATE=SKIPPED (no held-out and no readable "
|
|
682
|
+
"train corpus to slice)")
|
|
683
|
+
return 0
|
|
684
|
+
rb = open(train_corpus, "rb").read()
|
|
685
|
+
n = len(rb)
|
|
686
|
+
cut = int(n * (1.0 - tail_frac))
|
|
687
|
+
if n - cut < 4096:
|
|
688
|
+
print(f"{tag}_DESCENT_GATE=SKIPPED (corpus too small to hold out "
|
|
689
|
+
f"a {tail_frac:.0%} tail)")
|
|
690
|
+
return 0
|
|
691
|
+
import tempfile
|
|
692
|
+
fd, tmp_made = tempfile.mkstemp(suffix=".heldtail")
|
|
693
|
+
with os.fdopen(fd, "wb") as f:
|
|
694
|
+
f.write(rb[cut:])
|
|
695
|
+
held = tmp_made
|
|
696
|
+
print(f"{tag}_DESCENT_GATE: holding out deep tail slice "
|
|
697
|
+
f"(last {tail_frac:.0%} = {n - cut} bytes) of {train_corpus}")
|
|
698
|
+
return run_descent_gate_cli(clm_path, held, train_corpus, nwin)
|
|
699
|
+
finally:
|
|
700
|
+
if tmp_made and os.path.exists(tmp_made):
|
|
701
|
+
try:
|
|
702
|
+
os.remove(tmp_made)
|
|
703
|
+
except OSError:
|
|
704
|
+
pass
|
|
705
|
+
|
|
706
|
+
|
|
707
|
+
def run_descent_selftest(here):
|
|
708
|
+
"""CI self-test of the DESCENT-gate MACHINERY (no torch, no big artifacts):
|
|
709
|
+
serialize a tiny RANDOM-weight model to .clm, run the held-out mirror gate on
|
|
710
|
+
a small byte corpus, and assert the broken-detector FIRES (random weights ⇒
|
|
711
|
+
NO-DESCENT, F-CLM-DESCENT=0). This proves the gate loads/forwards/scores and
|
|
712
|
+
that a non-modeling .clm is correctly REJECTED — the exact failure the gate
|
|
713
|
+
must catch. (A PASS-side fixture would need a trained model = torch; the
|
|
714
|
+
real PASS path is exercised by the control clm_d768 + the trainers in CI.)"""
|
|
715
|
+
import numpy as np
|
|
716
|
+
sd, _ = _build_synthetic_general(d=24, L=2, E=2, K=3, V=256, seed=7)
|
|
717
|
+
ctmp = os.path.join(here, "_rt_descent.clm")
|
|
718
|
+
import clm_serialize_v2 as S
|
|
719
|
+
S.serialize_v3(sd, n_trunk_layers=2, n_experts=2, out_path=ctmp)
|
|
720
|
+
# small deterministic byte corpus (English-ish) — random weights can't model it
|
|
721
|
+
corp = os.path.join(here, "_rt_descent_corpus.txt")
|
|
722
|
+
with open(corp, "wb") as f:
|
|
723
|
+
f.write((b"the mind is a fire to be kindled not a vessel to be filled. ") * 64)
|
|
724
|
+
try:
|
|
725
|
+
ok, rep = descent_gate(ctmp, corp, None, nwin=16)
|
|
726
|
+
# machinery sanity: produced finite CEs + the random model does NOT descend
|
|
727
|
+
finite = (rep["heldout_model_ce"] == rep["heldout_model_ce"] # not NaN
|
|
728
|
+
and rep["uniform_ce_lnV"] > 5.0)
|
|
729
|
+
fires = (ok is False) # broken-detector must reject random weights
|
|
730
|
+
return (finite and fires), rep
|
|
731
|
+
finally:
|
|
732
|
+
for p in (ctmp, corp):
|
|
733
|
+
try:
|
|
734
|
+
os.remove(p)
|
|
735
|
+
except OSError:
|
|
736
|
+
pass
|
|
737
|
+
|
|
738
|
+
|
|
739
|
+
def main():
|
|
740
|
+
# `verify_clm_v2.py descent <clm> <heldout> [train] [nwin]` runs the
|
|
741
|
+
# held-out overfit gate (the post-serialize self-verify). No args → the
|
|
742
|
+
# structural round-trip self-test suite.
|
|
743
|
+
if len(sys.argv) >= 2 and sys.argv[1] == "descent":
|
|
744
|
+
clm = sys.argv[2]
|
|
745
|
+
heldout = sys.argv[3]
|
|
746
|
+
train = sys.argv[4] if len(sys.argv) > 4 and not sys.argv[4].isdigit() else None
|
|
747
|
+
nwin = int(sys.argv[-1]) if sys.argv[-1].isdigit() else 64
|
|
748
|
+
sys.exit(run_descent_gate_cli(clm, heldout, train, nwin))
|
|
749
|
+
|
|
750
|
+
here = os.path.dirname(os.path.abspath(__file__))
|
|
751
|
+
if here not in sys.path:
|
|
752
|
+
sys.path.insert(0, here)
|
|
753
|
+
tmp = os.path.join(here, "_rt_synth.clm")
|
|
754
|
+
|
|
755
|
+
ok, why = run_roundtrip(tmp)
|
|
756
|
+
if ok:
|
|
757
|
+
print("F-CLM-V2-ROUNDTRIP=1")
|
|
758
|
+
else:
|
|
759
|
+
print("F-CLM-V2-ROUNDTRIP=0")
|
|
760
|
+
print(f"MISMATCH: {why}")
|
|
761
|
+
try:
|
|
762
|
+
os.remove(tmp)
|
|
763
|
+
except OSError:
|
|
764
|
+
pass
|
|
765
|
+
|
|
766
|
+
# v0.3 byte-eq regression (L1/E2: v3 == v2)
|
|
767
|
+
eq_ok, eq_why = run_v3_byteeq_v2(here)
|
|
768
|
+
print(f"F-CLM-V3-BYTEEQ-V2={'1' if eq_ok else '0'} ({eq_why})")
|
|
769
|
+
|
|
770
|
+
# v0.3 general round-trips: a small multi-layer/more-expert config + the
|
|
771
|
+
# actual 3B-class config dims (structure only — no weights materialized at
|
|
772
|
+
# full 3B; the dim-bookkeeping is what the gate proves).
|
|
773
|
+
gtmp = os.path.join(here, "_rt_gen.clm")
|
|
774
|
+
g_ok_small, g_why_small = run_roundtrip_general(gtmp, d=48, L=4, E=6)
|
|
775
|
+
print(f"F-CLM-V3-ROUNDTRIP-SMALL={'1' if g_ok_small else '0'} "
|
|
776
|
+
f"(d=48 L=4 E=6: {g_why_small})")
|
|
777
|
+
g_ok_3b, g_why_3b = run_roundtrip_general(gtmp, d=128, L=30, E=30)
|
|
778
|
+
print(f"F-CLM-V3-ROUNDTRIP-3BDIMS={'1' if g_ok_3b else '0'} "
|
|
779
|
+
f"(d=128 L=30 E=30 [3B block/ext topology, reduced d]: {g_why_3b})")
|
|
780
|
+
try:
|
|
781
|
+
os.remove(gtmp)
|
|
782
|
+
except OSError:
|
|
783
|
+
pass
|
|
784
|
+
|
|
785
|
+
# v0.3 BIND round-trips (CLMB bind-readout codec): Hadamard (rt=1) + linear (rt=2).
|
|
786
|
+
btmp = os.path.join(here, "_rt_bind.clm")
|
|
787
|
+
b_ok_h, b_why_h = run_roundtrip_bind(btmp, d=48, L=2, E=2, k=64, readout_type=1)
|
|
788
|
+
print(f"F-CLM-BIND-ROUNDTRIP-HADAMARD={'1' if b_ok_h else '0'} "
|
|
789
|
+
f"(d=48 L=2 E=2 k=64 rt=1: {b_why_h})")
|
|
790
|
+
b_ok_l, b_why_l = run_roundtrip_bind(btmp, d=48, L=2, E=2, k=64, readout_type=2)
|
|
791
|
+
print(f"F-CLM-BIND-ROUNDTRIP-LINEAR={'1' if b_ok_l else '0'} "
|
|
792
|
+
f"(d=48 L=2 E=2 k=64 rt=2: {b_why_l})")
|
|
793
|
+
# 303M-class bind dims (block/ext topology only; reduced d for the bookkeeping).
|
|
794
|
+
b_ok_303, b_why_303 = run_roundtrip_bind(btmp, d=128, L=4, E=4, k=512, readout_type=1)
|
|
795
|
+
print(f"F-CLM-BIND-ROUNDTRIP-303MDIMS={'1' if b_ok_303 else '0'} "
|
|
796
|
+
f"(d=128 L=4 E=4 k=512 [303M ARM-BIND topology, reduced d]: {b_why_303})")
|
|
797
|
+
try:
|
|
798
|
+
os.remove(btmp)
|
|
799
|
+
except OSError:
|
|
800
|
+
pass
|
|
801
|
+
|
|
802
|
+
# held-out DESCENT-gate machinery self-test (broken-detector must fire on
|
|
803
|
+
# random weights). numpy required; honest SKIP if absent.
|
|
804
|
+
try:
|
|
805
|
+
import numpy as _np # noqa: F401 @root-cause-ok probe whether numpy is importable for the gate self-test
|
|
806
|
+
d_ok, d_rep = run_descent_selftest(here)
|
|
807
|
+
print(f"F-CLM-DESCENT-SELFTEST={'1' if d_ok else '0'} "
|
|
808
|
+
f"(random-weight .clm correctly rejected: heldout_ce="
|
|
809
|
+
f"{d_rep['heldout_model_ce']} >= uniform={d_rep['uniform_ce_lnV']})")
|
|
810
|
+
except ImportError:
|
|
811
|
+
print("F-CLM-DESCENT-SELFTEST=SKIP (numpy unavailable)")
|
|
812
|
+
d_ok = True # don't fail the structural suite on a missing optional dep
|
|
813
|
+
|
|
814
|
+
ok = ok and eq_ok and g_ok_small and g_ok_3b and d_ok and b_ok_h and b_ok_l and b_ok_303
|
|
815
|
+
|
|
816
|
+
# golden-reference parse: prove the mirror matches the REAL flame format.
|
|
817
|
+
golden = None
|
|
818
|
+
for cand in [
|
|
819
|
+
os.environ.get("GOLDEN_CLM", ""),
|
|
820
|
+
os.path.join(here, "..", "..", "state", "laneg_d768_recover", "reexport_d768_v2_fast.clm"),
|
|
821
|
+
"/Users/mini/dancinlab/anima/state/laneg_d768_recover/reexport_d768_v2_fast.clm",
|
|
822
|
+
]:
|
|
823
|
+
if cand and os.path.exists(cand):
|
|
824
|
+
golden = cand
|
|
825
|
+
break
|
|
826
|
+
if golden is None:
|
|
827
|
+
print("GOLDEN: file not found (skipped) — set GOLDEN_CLM=<path>")
|
|
828
|
+
else:
|
|
829
|
+
dec = clm_decodable(golden)
|
|
830
|
+
g = parse_clm(golden)
|
|
831
|
+
b0 = g["blocks"][0] if g["blocks"] else {}
|
|
832
|
+
print(f"GOLDEN path={golden}")
|
|
833
|
+
print(f"GOLDEN decodable={'true' if dec else 'false'} "
|
|
834
|
+
f"nblk={g['nblk']} "
|
|
835
|
+
f"block0={{cout:{b0.get('cout')},rest:{b0.get('rest')}}} "
|
|
836
|
+
f"CLMX-found={'true' if g['clmx_found'] else 'false'} "
|
|
837
|
+
f"n_ext={g['n_ext']} ext_counts={g['ext_counts']} "
|
|
838
|
+
f"exact_eof={g['exact_eof']}")
|
|
839
|
+
|
|
840
|
+
sys.exit(0 if ok else 1)
|
|
841
|
+
|
|
842
|
+
|
|
843
|
+
if __name__ == "__main__":
|
|
844
|
+
main()
|