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/core/decode.py
ADDED
|
@@ -0,0 +1,1386 @@
|
|
|
1
|
+
# ==========================================================================
|
|
2
|
+
# ⛔ ENGINE-INTERNAL / DEPRECATED py-MIRROR — DO NOT RUN OR SCORE DIRECTLY
|
|
3
|
+
# 측정/학습/서빙/직렬화는 cli/ 단일진입만: anima eval | train | serialize
|
|
4
|
+
# (canonical = hexa core/*.hexa 단일 SSOT; py 미러는 2026-06-28 폐기, DIRECTIONAL).
|
|
5
|
+
# 이 파일을 `python3 core/decode.py` 로 직접 실행하거나 side-harness로 import-채점하면
|
|
6
|
+
# = 단일진입 우회(#2603 위반) + terminal verdict 불가. cli/가 import하는 경로만 허용.
|
|
7
|
+
# ==========================================================================
|
|
8
|
+
import sys as _anima_entry_guard
|
|
9
|
+
if __name__ == "__main__":
|
|
10
|
+
_anima_entry_guard.exit("⛔ decode.py 직접 실행 금지 — cli/ 단일진입(anima eval/train/serialize, canonical=hexa) 경유. #2603")
|
|
11
|
+
|
|
12
|
+
"""core/decode.py — UNIFIED PY DECODE ENGINE: byte-faithful 1:1 merge of the two
|
|
13
|
+
per-mouth decoder mirrors into ONE module.
|
|
14
|
+
|
|
15
|
+
* CONV (CLM ConvMoE) mouth = the verbatim port of core/clm_decode.hexa
|
|
16
|
+
(formerly core/clm_decode.py)
|
|
17
|
+
* BYTE (ByteGPT transformer) = the verbatim port of core/decode.hexa
|
|
18
|
+
(formerly core/bytegpt_decode.py)
|
|
19
|
+
|
|
20
|
+
Per CLAUDE.md a_engine_native_learning: hexa + py are TWO co-equal production
|
|
21
|
+
engines kept at byte-parity. This module is the numpy mirror of the two decode
|
|
22
|
+
mouths; it exposes the UNION of both modules' public names so a caller can do
|
|
23
|
+
`import decode as clm` OR `import decode as bg` (or `import decode`) with ZERO
|
|
24
|
+
call-site churn (drop-in for clm_decode.py + bytegpt_decode.py).
|
|
25
|
+
|
|
26
|
+
Organization:
|
|
27
|
+
(a) SHARED — imports + the transcendental/PRNG helpers that are BYTE-IDENTICAL
|
|
28
|
+
across both source modules (dt_exp, _MASK, _mix32, _rng_next, _topk_sample).
|
|
29
|
+
(b) CONV (CLM) — the full clm_decode.py public API, verbatim.
|
|
30
|
+
(c) BYTE (ByteGPT) — the full bytegpt_decode.py public API, verbatim, PLUS a
|
|
31
|
+
byte-exact KV-cache fast path that REPLACES the O(gen²) full re-forward
|
|
32
|
+
decode loop (the O(gen²) reference is kept under a `_full` suffix).
|
|
33
|
+
(d) MOUTH DISPATCH — header-sniff (CLM\\x01 magic → conv; 5×u32 → byte),
|
|
34
|
+
mirroring generator.hexa gen_auto_backend.
|
|
35
|
+
|
|
36
|
+
DIVERGENT SAME-NAMED HELPERS — kept under DISTINCT names, never silently merged:
|
|
37
|
+
* clm's `_gn_sqrt` (gn_lib, 40-iter from g0=x) ⊥ bg's `dt_sqrt` (flame_math,
|
|
38
|
+
24-iter from g0=max(x,1)) — different bodies, both retained (distinct names).
|
|
39
|
+
* clm's `_rd_u32` and bg's `_bg_rd_u32` — IDENTICAL bodies; `_bg_rd_u32` is an
|
|
40
|
+
alias of `_rd_u32` (surface preserved, one definition).
|
|
41
|
+
The PRNG trio (_mix32/_rng_next/_topk_sample) + dt_exp were byte-identical across
|
|
42
|
+
the two source files (bg imported dt_exp FROM clm), so they are deduped to one
|
|
43
|
+
copy in the SHARED section with no behavior change.
|
|
44
|
+
|
|
45
|
+
KV-CACHE (the ByteGPT fast path, new here): the hexa KV-cache is byte-identical
|
|
46
|
+
to its full-forward reference (decode.hexa:919). numpy BLAS GEMM is
|
|
47
|
+
M-dependent at the ~1e-15 ULP level (a single-row projection differs from the
|
|
48
|
+
same row of an M=T batch), so the py KV logits are NOT bit-identical to the py
|
|
49
|
+
full-forward — but the DECODE TOKEN STREAM is identical (argmax/inverse-CDF are
|
|
50
|
+
robust to ~1e-13 logit drift), and the cache is fully RE-SYNCED (rebuilt at M=T)
|
|
51
|
+
whenever the window slides past `block`. Byte-exactness of the token stream is
|
|
52
|
+
gated by state/1815_cls_bytegpt/_decode_selftest.py.
|
|
53
|
+
"""
|
|
54
|
+
|
|
55
|
+
import sys
|
|
56
|
+
import math
|
|
57
|
+
import os
|
|
58
|
+
import struct
|
|
59
|
+
import numpy as np
|
|
60
|
+
|
|
61
|
+
# ── H_9200 E1 SLW eval-time controls (process-global; set by cli/evaluate.py) ──
|
|
62
|
+
# The gated-write forward-slot applies by default whenever a .clm carries an
|
|
63
|
+
# "SLW\x01" trailer. These two switches let the pre-registered controls run WITHOUT
|
|
64
|
+
# retraining (frozen-first · no tune-to-green): --slot-off forces γ=0 (bit-exact base
|
|
65
|
+
# trunk = slot-ablation), --slot-shuffle scrambles the WRITE address (shuffle-bind).
|
|
66
|
+
_SLW_GAMMA_OVERRIDE = None # float 0.0 => ablate the slot lane (γ=0 passthrough)
|
|
67
|
+
_SLW_SHUFFLE_SEED = None # int => permute the write address only (reads unpermuted)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def set_slw_controls(gamma_override=None, shuffle_seed=None):
|
|
71
|
+
"""Set the SLW eval-time controls (cli/evaluate.py --slot-off / --slot-shuffle)."""
|
|
72
|
+
global _SLW_GAMMA_OVERRIDE, _SLW_SHUFFLE_SEED
|
|
73
|
+
_SLW_GAMMA_OVERRIDE = gamma_override
|
|
74
|
+
_SLW_SHUFFLE_SEED = shuffle_seed
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
78
|
+
# (a) SHARED — helpers byte-identical across clm_decode.py + bytegpt_decode.py
|
|
79
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
80
|
+
|
|
81
|
+
def dt_exp(x):
|
|
82
|
+
"""flame_math.hexa::dt_exp — halve until |xr|<=0.25, 12-term Taylor (k=1..11),
|
|
83
|
+
then square r times. Vectorized; halving by 2 is exact in fp64, so the
|
|
84
|
+
per-element halve-count r matches the hexa scalar while-loop exactly.
|
|
85
|
+
(SHARED: bg's top-k sampler softmax uses the SAME dt_exp — was `from
|
|
86
|
+
clm_decode import dt_exp` in bytegpt_decode.py.)"""
|
|
87
|
+
x = np.asarray(x, dtype=np.float64)
|
|
88
|
+
scalar = (x.ndim == 0)
|
|
89
|
+
x = np.atleast_1d(x)
|
|
90
|
+
xr = x.copy()
|
|
91
|
+
r = np.zeros(x.shape, dtype=np.int64)
|
|
92
|
+
mask = np.abs(xr) > 0.25
|
|
93
|
+
while mask.any():
|
|
94
|
+
xr = np.where(mask, xr / 2.0, xr)
|
|
95
|
+
r = np.where(mask, r + 1, r)
|
|
96
|
+
mask = np.abs(xr) > 0.25
|
|
97
|
+
term = np.ones_like(xr)
|
|
98
|
+
acc = np.ones_like(xr)
|
|
99
|
+
k = 1
|
|
100
|
+
while k < 12:
|
|
101
|
+
term = term * xr / float(k)
|
|
102
|
+
acc = acc + term
|
|
103
|
+
k = k + 1
|
|
104
|
+
rmax = int(r.max()) if r.size else 0
|
|
105
|
+
s = 0
|
|
106
|
+
while s < rmax:
|
|
107
|
+
m = r > s
|
|
108
|
+
acc = np.where(m, acc * acc, acc)
|
|
109
|
+
s = s + 1
|
|
110
|
+
return float(acc[0]) if scalar else acc
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
# ── seeded top-k temperature sampler PRNG — BYTE-IDENTICAL across both mouths
|
|
114
|
+
# (clm_decode.hexa _clmd_mix32/_rng_next ≡ decode.hexa _g6_mix32/_rng_next;
|
|
115
|
+
# xorshift32 & 0xFFFFFFFF). Deduped to one copy.
|
|
116
|
+
_MASK = 0xFFFFFFFF
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def _mix32(s):
|
|
120
|
+
"""_clmd_mix32 / _g6_mix32 — SplitMix32-style finalizer."""
|
|
121
|
+
z = (s + 0x9E3779B9) & _MASK
|
|
122
|
+
z = ((z ^ (z // 65536)) * 0x85EBCA6B) & _MASK
|
|
123
|
+
z = ((z ^ (z // 8192)) * 0xC2B2AE35) & _MASK
|
|
124
|
+
z = (z ^ (z // 65536)) & _MASK
|
|
125
|
+
if z == 0:
|
|
126
|
+
z = 0x9E3779B9
|
|
127
|
+
return z
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def _rng_next(s):
|
|
131
|
+
"""_clmd_rng_next / _g6_rng_next — xorshift32 step -> (state, u in [0,1))."""
|
|
132
|
+
x = s & _MASK
|
|
133
|
+
if x == 0:
|
|
134
|
+
x = 0x9E3779B9
|
|
135
|
+
x = (x ^ (x * 8192)) & _MASK
|
|
136
|
+
x = (x ^ (x // 131072)) & _MASK
|
|
137
|
+
x = (x ^ (x * 32)) & _MASK
|
|
138
|
+
return x, (float(x) / 4294967296.0)
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _topk_sample(logits, vocab, top_k, temp, rng):
|
|
142
|
+
"""_clmd_topk_sample / _g6_topk_sample — top-k by repeated argmax (ties:
|
|
143
|
+
first/strict >), /temp, dt_exp softmax (max-sub), inverse-CDF draw. logits:[V].
|
|
144
|
+
(SHARED: both mouths' samplers had byte-identical bodies.)"""
|
|
145
|
+
kcap = top_k if (top_k > 0 and top_k < vocab) else vocab
|
|
146
|
+
taken = np.zeros(vocab, dtype=np.float64)
|
|
147
|
+
sel_idx = []
|
|
148
|
+
sel_val = []
|
|
149
|
+
picks = 0
|
|
150
|
+
while picks < kcap:
|
|
151
|
+
bi = -1
|
|
152
|
+
bv = 0.0
|
|
153
|
+
for k in range(vocab):
|
|
154
|
+
if taken[k] < 0.5:
|
|
155
|
+
v = logits[k]
|
|
156
|
+
if bi < 0 or v > bv:
|
|
157
|
+
bi = k; bv = v
|
|
158
|
+
if bi < 0:
|
|
159
|
+
picks = kcap
|
|
160
|
+
else:
|
|
161
|
+
taken[bi] = 1.0
|
|
162
|
+
sel_idx.append(bi)
|
|
163
|
+
sel_val.append(bv / temp)
|
|
164
|
+
picks += 1
|
|
165
|
+
nsel = len(sel_idx)
|
|
166
|
+
mx = sel_val[0]
|
|
167
|
+
for i in range(1, nsel):
|
|
168
|
+
if sel_val[i] > mx:
|
|
169
|
+
mx = sel_val[i]
|
|
170
|
+
probs = []
|
|
171
|
+
summ = 0.0
|
|
172
|
+
for j in range(nsel):
|
|
173
|
+
e = float(dt_exp(np.array(sel_val[j] - mx)))
|
|
174
|
+
probs.append(e); summ += e
|
|
175
|
+
s2, u = _rng_next(rng)
|
|
176
|
+
target = u * summ
|
|
177
|
+
acc = 0.0
|
|
178
|
+
pick = sel_idx[nsel - 1]
|
|
179
|
+
for p in range(nsel):
|
|
180
|
+
acc += probs[p]
|
|
181
|
+
if target <= acc:
|
|
182
|
+
pick = sel_idx[p]
|
|
183
|
+
break
|
|
184
|
+
return pick, s2
|
|
185
|
+
|
|
186
|
+
|
|
187
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
188
|
+
# (b) CONV (CLM ConvMoE) — verbatim port of core/clm_decode.hexa
|
|
189
|
+
# (formerly core/clm_decode.py). PRNG/dt_exp reused from SHARED above.
|
|
190
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
191
|
+
|
|
192
|
+
# ── primitive math — ported 1:1 from stdlib/flame/*.hexa ──
|
|
193
|
+
|
|
194
|
+
def dt_ln(x):
|
|
195
|
+
"""flame_math.hexa::dt_ln — u=(x-1)/(x+1); 2*Σ_{k=0..23} u^(2k+1)/(2k+1).
|
|
196
|
+
KNOWN-BUGGY for x far from 1 (diverges); reproduced verbatim for parity."""
|
|
197
|
+
x = np.asarray(x, dtype=np.float64)
|
|
198
|
+
scalar = (x.ndim == 0)
|
|
199
|
+
x = np.atleast_1d(x)
|
|
200
|
+
u = (x - 1.0) / (x + 1.0)
|
|
201
|
+
u2 = u * u
|
|
202
|
+
termp = u.copy()
|
|
203
|
+
acc = np.zeros_like(u)
|
|
204
|
+
k = 0
|
|
205
|
+
while k < 24:
|
|
206
|
+
acc = acc + termp / float(2 * k + 1)
|
|
207
|
+
termp = termp * u2
|
|
208
|
+
k = k + 1
|
|
209
|
+
out = 2.0 * acc
|
|
210
|
+
return float(out[0]) if scalar else out
|
|
211
|
+
|
|
212
|
+
|
|
213
|
+
def dt_erf(x):
|
|
214
|
+
"""flame_math.hexa::dt_erf — Abramowitz&Stegun 7.1.26, exp via dt_exp."""
|
|
215
|
+
x = np.asarray(x, dtype=np.float64)
|
|
216
|
+
scalar = (x.ndim == 0)
|
|
217
|
+
x = np.atleast_1d(x)
|
|
218
|
+
sign = np.where(x < 0.0, -1.0, 1.0)
|
|
219
|
+
z = np.abs(x)
|
|
220
|
+
t = 1.0 / (1.0 + 0.3275911 * z)
|
|
221
|
+
a1 = 0.254829592
|
|
222
|
+
a2 = -0.284496736
|
|
223
|
+
a3 = 1.421413741
|
|
224
|
+
a4 = -1.453152027
|
|
225
|
+
a5 = 1.061405429
|
|
226
|
+
poly = ((((a5 * t + a4) * t + a3) * t + a2) * t + a1) * t
|
|
227
|
+
out = sign * (1.0 - poly * dt_exp(0.0 - z * z))
|
|
228
|
+
return float(out[0]) if scalar else out
|
|
229
|
+
|
|
230
|
+
|
|
231
|
+
_INV_SQRT2 = 0.70710678118654752440
|
|
232
|
+
_INV_SQRT2PI = 0.39894228040143267794
|
|
233
|
+
|
|
234
|
+
|
|
235
|
+
def _nn_normal_cdf(x):
|
|
236
|
+
return 0.5 * (1.0 + dt_erf(x * _INV_SQRT2))
|
|
237
|
+
|
|
238
|
+
|
|
239
|
+
def nn_gelu_fwd(g):
|
|
240
|
+
"""nn_lib.hexa::nn_gelu_fwd — GELU(x)=x*Phi(x), Phi via dt_erf (EXACT erf)."""
|
|
241
|
+
g = np.asarray(g, dtype=np.float64)
|
|
242
|
+
return g * _nn_normal_cdf(g)
|
|
243
|
+
|
|
244
|
+
|
|
245
|
+
def _moe_exp(x):
|
|
246
|
+
"""moe_lib.hexa::_moe_exp — ln2 range-reduce, 14-term Taylor, *2^n.
|
|
247
|
+
DISTINCT from dt_exp (used ONLY in the MoE router softmax)."""
|
|
248
|
+
x = np.asarray(x, dtype=np.float64)
|
|
249
|
+
scalar = (x.ndim == 0)
|
|
250
|
+
x = np.atleast_1d(x)
|
|
251
|
+
ln2 = 0.6931471805599453
|
|
252
|
+
r = x.copy()
|
|
253
|
+
n = np.zeros(x.shape, dtype=np.int64)
|
|
254
|
+
m = r > 0.34657359
|
|
255
|
+
while m.any():
|
|
256
|
+
r = np.where(m, r - ln2, r)
|
|
257
|
+
n = np.where(m, n + 1, n)
|
|
258
|
+
m = r > 0.34657359
|
|
259
|
+
m = r < -0.34657359
|
|
260
|
+
while m.any():
|
|
261
|
+
r = np.where(m, r + ln2, r)
|
|
262
|
+
n = np.where(m, n - 1, n)
|
|
263
|
+
m = r < -0.34657359
|
|
264
|
+
term = np.ones_like(r)
|
|
265
|
+
summ = np.ones_like(r)
|
|
266
|
+
k = 1
|
|
267
|
+
while k < 14:
|
|
268
|
+
term = term * r / float(k)
|
|
269
|
+
summ = summ + term
|
|
270
|
+
k = k + 1
|
|
271
|
+
# *2^n via exact power-of-two scaling (== repeated *2 / /2 in hexa)
|
|
272
|
+
p = summ * np.power(2.0, n.astype(np.float64))
|
|
273
|
+
return float(p[0]) if scalar else p
|
|
274
|
+
|
|
275
|
+
|
|
276
|
+
def _gn_sqrt(x):
|
|
277
|
+
"""gn_lib.hexa::_gn_sqrt — Newton-Raphson 40 iters from g0=x. scalar.
|
|
278
|
+
(DIVERGENT twin of bg's dt_sqrt — DO NOT merge; distinct name kept.)"""
|
|
279
|
+
if x <= 0.0:
|
|
280
|
+
return 0.0
|
|
281
|
+
g = x
|
|
282
|
+
i = 0
|
|
283
|
+
while i < 40:
|
|
284
|
+
g = 0.5 * (g + x / g)
|
|
285
|
+
i = i + 1
|
|
286
|
+
return g
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
def nn_groupnorm_fwd(x, gamma, beta, T, C, G):
|
|
290
|
+
"""gn_lib.hexa::nn_groupnorm_fwd — eps=1e-5. x:[T,C]. Returns y:[T,C].
|
|
291
|
+
Here G is always 1 (=> normalize over all C per the whole [T,C] group)."""
|
|
292
|
+
eps = 0.00001
|
|
293
|
+
cg = C // G
|
|
294
|
+
m = float(cg * T)
|
|
295
|
+
x = x.reshape(T, C)
|
|
296
|
+
y = np.empty_like(x)
|
|
297
|
+
for grp in range(G):
|
|
298
|
+
c0 = grp * cg
|
|
299
|
+
sl = x[:, c0:c0 + cg]
|
|
300
|
+
mu = sl.sum() / m
|
|
301
|
+
var = ((sl - mu) * (sl - mu)).sum() / m
|
|
302
|
+
inv = 1.0 / _gn_sqrt(var + eps)
|
|
303
|
+
xh = (sl - mu) * inv
|
|
304
|
+
y[:, c0:c0 + cg] = gamma[c0:c0 + cg] * xh + beta[c0:c0 + cg]
|
|
305
|
+
return y
|
|
306
|
+
|
|
307
|
+
|
|
308
|
+
def nn_moe_softmax(logits, T, E):
|
|
309
|
+
"""moe_lib.hexa::nn_moe_softmax — stable softmax over E via _moe_exp. logits:[T,E]."""
|
|
310
|
+
logits = logits.reshape(T, E)
|
|
311
|
+
mx = logits.max(axis=1, keepdims=True)
|
|
312
|
+
ev = _moe_exp(logits - mx)
|
|
313
|
+
s = ev.sum(axis=1, keepdims=True)
|
|
314
|
+
return ev / s
|
|
315
|
+
|
|
316
|
+
|
|
317
|
+
def nn_moe_router_fwd(logits_r, ex_out, T, E, C):
|
|
318
|
+
"""moe_lib.hexa::nn_moe_router_fwd — y[t,c]=Σ_e probs[t,e]*ex_out[e,t,c].
|
|
319
|
+
ex_out:[E,T,C]. Returns y:[T,C]."""
|
|
320
|
+
probs = nn_moe_softmax(logits_r, T, E) # [T,E]
|
|
321
|
+
ex = ex_out.reshape(E, T, C)
|
|
322
|
+
# y[t,c] = Σ_e probs[t,e]*ex[e,t,c]
|
|
323
|
+
y = np.einsum('te,etc->tc', probs, ex)
|
|
324
|
+
return y
|
|
325
|
+
|
|
326
|
+
|
|
327
|
+
def nn_ce_loss_allpos(logits, targets, T, V):
|
|
328
|
+
"""nn_lib.hexa::nn_ce_loss_allpos — mean over T of -ln(softmax[tgt]).
|
|
329
|
+
Stable softmax via dt_exp, pt_safe floor 1e-6, -ln via dt_ln. logits:[T,V]."""
|
|
330
|
+
logits = logits.reshape(T, V)
|
|
331
|
+
total = 0.0
|
|
332
|
+
for t in range(T):
|
|
333
|
+
row = logits[t]
|
|
334
|
+
mx = row.max()
|
|
335
|
+
tot = float(dt_exp(row - mx).sum())
|
|
336
|
+
tgt = int(targets[t])
|
|
337
|
+
p_t = float(dt_exp(np.array(row[tgt] - mx))) / tot
|
|
338
|
+
pt_safe = p_t if p_t >= 0.000001 else 0.000001
|
|
339
|
+
total = total + (0.0 - float(dt_ln(np.array(pt_safe))))
|
|
340
|
+
return total / float(T)
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
# ── .clm file parse + int4 dequant — 1:1 from clm_decode.hexa _clmd_load* ──
|
|
344
|
+
|
|
345
|
+
def _rd_u32(rb, off):
|
|
346
|
+
return rb[off] | (rb[off + 1] << 8) | (rb[off + 2] << 16) | (rb[off + 3] << 24)
|
|
347
|
+
|
|
348
|
+
|
|
349
|
+
def clm_decodable(path):
|
|
350
|
+
"""clm_decode.hexa::clm_decodable — CLM\\x01 header AND CLMX v0.2 trailer."""
|
|
351
|
+
try:
|
|
352
|
+
rb = open(path, 'rb').read()
|
|
353
|
+
except Exception:
|
|
354
|
+
return False
|
|
355
|
+
if len(rb) < 5:
|
|
356
|
+
return False
|
|
357
|
+
if not (rb[0] == 67 and rb[1] == 76 and rb[2] == 77 and rb[3] == 1):
|
|
358
|
+
return False
|
|
359
|
+
nblk = rb[4]
|
|
360
|
+
off = 5
|
|
361
|
+
b = 0
|
|
362
|
+
while b < nblk:
|
|
363
|
+
if off + 8 > len(rb):
|
|
364
|
+
return False
|
|
365
|
+
cout = _rd_u32(rb, off)
|
|
366
|
+
rest = _rd_u32(rb, off + 4)
|
|
367
|
+
off = off + 8
|
|
368
|
+
n = cout * rest
|
|
369
|
+
off = off + (n + 1) // 2
|
|
370
|
+
off = off + cout * 4
|
|
371
|
+
b = b + 1
|
|
372
|
+
if off + 5 > len(rb):
|
|
373
|
+
return False
|
|
374
|
+
return rb[off] == 67 and rb[off + 1] == 76 and rb[off + 2] == 77 and rb[off + 3] == 88
|
|
375
|
+
|
|
376
|
+
|
|
377
|
+
def clm_config(path):
|
|
378
|
+
"""clm_decode.hexa::clm_config — recover (d,K,V,E,L,nblk) from header."""
|
|
379
|
+
if not clm_decodable(path):
|
|
380
|
+
return {"ok": False}
|
|
381
|
+
rb = open(path, 'rb').read()
|
|
382
|
+
nblk = rb[4]
|
|
383
|
+
d = _rd_u32(rb, 5)
|
|
384
|
+
rest0 = _rd_u32(rb, 9)
|
|
385
|
+
K = rest0 // d
|
|
386
|
+
off = 5
|
|
387
|
+
bi = 0
|
|
388
|
+
E = 2
|
|
389
|
+
V = 256
|
|
390
|
+
while bi < nblk:
|
|
391
|
+
c = _rd_u32(rb, off)
|
|
392
|
+
r = _rd_u32(rb, off + 4)
|
|
393
|
+
if bi == nblk - 2:
|
|
394
|
+
E = c
|
|
395
|
+
if bi == nblk - 1:
|
|
396
|
+
V = c
|
|
397
|
+
n = c * r
|
|
398
|
+
off = off + 8 + (n + 1) // 2 + c * 4
|
|
399
|
+
bi = bi + 1
|
|
400
|
+
L = nblk - E - 3
|
|
401
|
+
return {"ok": True, "d": d, "K": K, "V": V, "E": E, "L": L, "nblk": nblk}
|
|
402
|
+
|
|
403
|
+
|
|
404
|
+
def _load_block(rb, off):
|
|
405
|
+
"""_clmd_load_block — int4-sym dequant: w = (nibble-8) * per-channel-scale.
|
|
406
|
+
Returns (w_2d[cout,rest], new_off)."""
|
|
407
|
+
cout = _rd_u32(rb, off); off += 4
|
|
408
|
+
rest = _rd_u32(rb, off); off += 4
|
|
409
|
+
n = cout * rest
|
|
410
|
+
nbytes = (n + 1) // 2
|
|
411
|
+
raw = np.frombuffer(rb, dtype=np.uint8, count=nbytes, offset=off).astype(np.int64)
|
|
412
|
+
off += nbytes
|
|
413
|
+
low = (raw & 0xF) - 8
|
|
414
|
+
high = ((raw >> 4) & 0xF) - 8
|
|
415
|
+
codes = np.empty(2 * len(raw), dtype=np.float64)
|
|
416
|
+
codes[0::2] = low
|
|
417
|
+
codes[1::2] = high
|
|
418
|
+
codes = codes[:n]
|
|
419
|
+
scales = np.frombuffer(rb, dtype='<f4', count=cout, offset=off).astype(np.float64)
|
|
420
|
+
off += cout * 4
|
|
421
|
+
w = codes.reshape(cout, rest) * scales[:, None]
|
|
422
|
+
return w, off
|
|
423
|
+
|
|
424
|
+
|
|
425
|
+
def _load_ext(rb, off):
|
|
426
|
+
"""_clmd_load_ext — length-prefixed fp32 tensor (n:u32, then n*f32 LE)."""
|
|
427
|
+
n = _rd_u32(rb, off); off += 4
|
|
428
|
+
vals = np.frombuffer(rb, dtype='<f4', count=n, offset=off).astype(np.float64)
|
|
429
|
+
off += n * 4
|
|
430
|
+
return vals, off
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
def clm_load_weights(path):
|
|
434
|
+
"""_clmd_load — full file parse into a weight dict. Conv weights are kept
|
|
435
|
+
pre-transposed as Wt[Kdim,Cout] (the _clmd_scratch_new transpose, applied
|
|
436
|
+
once) since the py forward GEMMs xcol[T,Kdim] @ Wt[Kdim,Cout]."""
|
|
437
|
+
if not clm_decodable(path):
|
|
438
|
+
return {"ok": False}
|
|
439
|
+
rb = open(path, 'rb').read()
|
|
440
|
+
nblk = rb[4]
|
|
441
|
+
d = _rd_u32(rb, 5)
|
|
442
|
+
rest0 = _rd_u32(rb, 9)
|
|
443
|
+
K = rest0 // d
|
|
444
|
+
# walk to find E (block nblk-2 cout), V (block nblk-1 cout)
|
|
445
|
+
off = 5
|
|
446
|
+
bi = 0
|
|
447
|
+
E = 2
|
|
448
|
+
V = 256
|
|
449
|
+
while bi < nblk:
|
|
450
|
+
c = _rd_u32(rb, off)
|
|
451
|
+
r = _rd_u32(rb, off + 4)
|
|
452
|
+
if bi == nblk - 2:
|
|
453
|
+
E = c
|
|
454
|
+
if bi == nblk - 1:
|
|
455
|
+
V = c
|
|
456
|
+
n = c * r
|
|
457
|
+
off = off + 8 + (n + 1) // 2 + c * 4
|
|
458
|
+
bi = bi + 1
|
|
459
|
+
L = nblk - E - 3
|
|
460
|
+
|
|
461
|
+
# ── conv blocks, in order: ec, tc[L], eW[E], rW, roW ──
|
|
462
|
+
off = 5
|
|
463
|
+
ecW, off = _load_block(rb, off) # [d, d*K]
|
|
464
|
+
tcW = []
|
|
465
|
+
for _ in range(L):
|
|
466
|
+
w, off = _load_block(rb, off); tcW.append(w) # [d, d*K]
|
|
467
|
+
eW = []
|
|
468
|
+
for _ in range(E):
|
|
469
|
+
w, off = _load_block(rb, off); eW.append(w) # [d, d*K]
|
|
470
|
+
rW, off = _load_block(rb, off) # [E, d]
|
|
471
|
+
roW, off = _load_block(rb, off) # [V, d]
|
|
472
|
+
off = off + 5 # skip "CLMX" + n_ext byte
|
|
473
|
+
|
|
474
|
+
# ── ext tensors, in order ──
|
|
475
|
+
embed, off = _load_ext(rb, off) # [V*d]
|
|
476
|
+
ecB, off = _load_ext(rb, off) # [d]
|
|
477
|
+
tcB = []
|
|
478
|
+
for _ in range(L):
|
|
479
|
+
v, off = _load_ext(rb, off); tcB.append(v) # [d]
|
|
480
|
+
eB = []
|
|
481
|
+
for _ in range(E):
|
|
482
|
+
v, off = _load_ext(rb, off); eB.append(v) # [d]
|
|
483
|
+
rB, off = _load_ext(rb, off) # [E]
|
|
484
|
+
roB, off = _load_ext(rb, off) # [V]
|
|
485
|
+
tgG = []
|
|
486
|
+
for _ in range(L):
|
|
487
|
+
v, off = _load_ext(rb, off); tgG.append(v) # [d]
|
|
488
|
+
tgB = []
|
|
489
|
+
for _ in range(L):
|
|
490
|
+
v, off = _load_ext(rb, off); tgB.append(v) # [d]
|
|
491
|
+
noG, off = _load_ext(rb, off) # [d]
|
|
492
|
+
noB, off = _load_ext(rb, off) # [d]
|
|
493
|
+
|
|
494
|
+
# pre-transpose conv weights -> Wt[Kdim, Cout] (= w_2d.T)
|
|
495
|
+
W = {
|
|
496
|
+
"ok": True, "d": d, "E": E, "V": V, "K": K, "L": L,
|
|
497
|
+
"ecWt": ecW.T.copy(), "ecB": ecB,
|
|
498
|
+
"tcWt": [w.T.copy() for w in tcW], "tcB": tcB,
|
|
499
|
+
"eWt": [w.T.copy() for w in eW], "eB": eB,
|
|
500
|
+
"rWt": rW.T.copy(), "rB": rB,
|
|
501
|
+
"roWt": roW.T.copy(), "roB": roB,
|
|
502
|
+
"embed": embed.reshape(V, d),
|
|
503
|
+
"tgG": tgG, "tgB": tgB, "noG": noG, "noB": noB,
|
|
504
|
+
"bind_type": 0,
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
# ── optional CLMB bind-readout section (serialize_v3_bind) ──────────────
|
|
508
|
+
# "CLMB" = bytes 67,76,77,66. Present only when the .clm was serialized
|
|
509
|
+
# with a Hadamard/linear bind readout retained in-forward (H_1818).
|
|
510
|
+
# If absent, bind_type=0 and the standard additive _conv1d readout is used.
|
|
511
|
+
# CLMB layout (after CLMX ext arrays):
|
|
512
|
+
# CLMB magic 67,76,77,66
|
|
513
|
+
# bind_type u8 (1=Hadamard u*v, 2=linear u+v)
|
|
514
|
+
# Wa block (k, d) int4-sym conv block
|
|
515
|
+
# Wb block (k, d) int4-sym conv block
|
|
516
|
+
# WaB ext u32 k + k*f32
|
|
517
|
+
# WbB ext u32 k + k*f32
|
|
518
|
+
# In a CLMB file, roW holds Wo (V, k) NOT (V, d); roWt = (k, V).
|
|
519
|
+
if (off + 5 <= len(rb)
|
|
520
|
+
and rb[off] == 67 and rb[off + 1] == 76
|
|
521
|
+
and rb[off + 2] == 77 and rb[off + 3] == 66):
|
|
522
|
+
off += 4 # skip "CLMB"
|
|
523
|
+
bind_type = rb[off]; off += 1
|
|
524
|
+
WaW, off = _load_block(rb, off) # (k, d)
|
|
525
|
+
WbW, off = _load_block(rb, off) # (k, d)
|
|
526
|
+
WaB_ext, off = _load_ext(rb, off) # (k,)
|
|
527
|
+
WbB_ext, off = _load_ext(rb, off) # (k,)
|
|
528
|
+
W["bind_type"] = int(bind_type)
|
|
529
|
+
W["WaWt"] = WaW.T.copy() # (d, k)
|
|
530
|
+
W["WbWt"] = WbW.T.copy() # (d, k)
|
|
531
|
+
W["WaB"] = WaB_ext
|
|
532
|
+
W["WbB"] = WbB_ext
|
|
533
|
+
# roWt is already Wo.T = (k, V); roB is already WoB (V,) — loaded above.
|
|
534
|
+
|
|
535
|
+
# ── optional "SLW\x01" gated-write forward-slot trailer (H_9200 E1) ──────
|
|
536
|
+
# End of the trailer chain (after CLMX ext / CLMB), read at the current `off`.
|
|
537
|
+
# Absent/short => slw=None => forward is byte-identical to today (passthrough).
|
|
538
|
+
# Codec is CORE-owned in core/slw.py (owner directive: core lives in core/).
|
|
539
|
+
from slw import read_slw
|
|
540
|
+
W["slw"], off = read_slw(rb, off)
|
|
541
|
+
|
|
542
|
+
# ── optional "CLML" read-side context-pooling lane trailer (fork A · H_9235) ──
|
|
543
|
+
# Appended after the SLW trailer. Absent/short => clml=None => byte-identical forward
|
|
544
|
+
# (lane_apply passthrough). CORE-owned codec in core/clml.py.
|
|
545
|
+
from clml import read_clml
|
|
546
|
+
W["clml"], off = read_clml(rb, off, W["d"], W["V"])
|
|
547
|
+
|
|
548
|
+
return W
|
|
549
|
+
|
|
550
|
+
|
|
551
|
+
# ── forward — 1:1 from clm_decode.hexa _clmd_conv1d / _clmd_fwd_logits_sc ──
|
|
552
|
+
|
|
553
|
+
def _conv1d(x, Wt, b, T, Cin, Cout, K, dil):
|
|
554
|
+
"""_clmd_conv1d_pre (host path) — causal dilated im2col + matmul + bias.
|
|
555
|
+
x:[T,Cin], Wt:[Cin*K, Cout], b:[Cout]. Returns y:[T,Cout].
|
|
556
|
+
im2col layout: xcol[t, ci*K + k] = x[t - dil*(K-1-k), ci] (0 if p<0)."""
|
|
557
|
+
Kdim = Cin * K
|
|
558
|
+
x = x.reshape(T, Cin)
|
|
559
|
+
xcol = np.zeros((T, Cin, K), dtype=np.float64)
|
|
560
|
+
t_idx = np.arange(T)
|
|
561
|
+
for k in range(K):
|
|
562
|
+
offset = dil * (K - 1 - k)
|
|
563
|
+
p = t_idx - offset
|
|
564
|
+
valid = p >= 0
|
|
565
|
+
xcol[valid, :, k] = x[p[valid], :]
|
|
566
|
+
xcol = xcol.reshape(T, Kdim)
|
|
567
|
+
mm = xcol @ Wt # [T, Cout]
|
|
568
|
+
return mm + b[None, :]
|
|
569
|
+
|
|
570
|
+
|
|
571
|
+
def _fwd_trunk(W, tok, T):
|
|
572
|
+
"""Trunk forward through the FINAL groupnorm — returns yn:[T, d], the pre-readout,
|
|
573
|
+
PRE-E1-slot penultimate hidden (post-MoE, post final-GN). This is the pure-trunk
|
|
574
|
+
concept representation (E1-slot independent, matching the H_1822 β 303M-trunk-penult
|
|
575
|
+
precedent). Extracted from _fwd_logits so the decode path (slot+readout applied) and
|
|
576
|
+
the read-only hidden tap (clm_forward_hidden, ρ·weave / γ binding-lane probe H_9235)
|
|
577
|
+
share ONE byte-identical trunk forward."""
|
|
578
|
+
d = W["d"]; E = W["E"]; K = W["K"]; L = W["L"]
|
|
579
|
+
# embedding
|
|
580
|
+
ids = tok.astype(np.int64)
|
|
581
|
+
xe = W["embed"][ids] # [T, d]
|
|
582
|
+
# ec conv (K, dil=1)
|
|
583
|
+
xt = _conv1d(xe, W["ecWt"], W["ecB"], T, d, d, K, 1)
|
|
584
|
+
# L trunk layers: xt = xt + gelu(groupnorm(conv(xt)))
|
|
585
|
+
DIL_CAP = 512
|
|
586
|
+
dil = 1
|
|
587
|
+
for li in range(L):
|
|
588
|
+
dil_eff = dil if dil <= DIL_CAP else DIL_CAP
|
|
589
|
+
h = _conv1d(xt, W["tcWt"][li], W["tcB"][li], T, d, d, K, dil_eff)
|
|
590
|
+
hn = nn_groupnorm_fwd(h, W["tgG"][li], W["tgB"][li], T, d, 1)
|
|
591
|
+
hg = nn_gelu_fwd(hn)
|
|
592
|
+
xt = xt + hg.reshape(T, d)
|
|
593
|
+
dil = dil * 2
|
|
594
|
+
# router conv (K=1, Cout=E)
|
|
595
|
+
logits_r = _conv1d(xt, W["rWt"], W["rB"], T, d, E, 1, 1) # [T, E]
|
|
596
|
+
# E experts: gelu(conv(xt))
|
|
597
|
+
ex_out = np.empty((E, T, d), dtype=np.float64)
|
|
598
|
+
for ej in range(E):
|
|
599
|
+
eo = _conv1d(xt, W["eWt"][ej], W["eB"][ej], T, d, d, K, 1)
|
|
600
|
+
ex_out[ej] = nn_gelu_fwd(eo).reshape(T, d)
|
|
601
|
+
# MoE router mix
|
|
602
|
+
y = nn_moe_router_fwd(logits_r, ex_out, T, E, d) # [T, d]
|
|
603
|
+
# final groupnorm
|
|
604
|
+
yn = nn_groupnorm_fwd(y, W["noG"], W["noB"], T, d, 1)
|
|
605
|
+
return yn
|
|
606
|
+
|
|
607
|
+
|
|
608
|
+
def clm_forward_hidden(W, tok, T):
|
|
609
|
+
"""Read-only penultimate-hidden tap — the pre-readout, pre-E1-slot yn:[T, d] (post-MoE,
|
|
610
|
+
post final groupnorm) for token ids tok:[T]. Engine-native: the EXACT production trunk
|
|
611
|
+
forward (_fwd_trunk, shared with _fwd_logits), so the dumped representation is
|
|
612
|
+
byte-identical to what the gates decode over. For the ρ·weave held-out-pair
|
|
613
|
+
recombination / γ binding-lane probe (H_9235): dumps the pure-trunk concept
|
|
614
|
+
representation a read-side lane consumes. No decode sampling / readout / perturbation."""
|
|
615
|
+
ta = tok if hasattr(tok, "astype") else np.array(tok, dtype=np.float64)
|
|
616
|
+
return _fwd_trunk(W, ta, T)
|
|
617
|
+
|
|
618
|
+
|
|
619
|
+
def clm_forward_hidden_logits(W, tok, T):
|
|
620
|
+
"""Read-only combined tap: (yn_trunk:[T,d], logits:[T,V]) in ONE trunk forward — the pre-slot
|
|
621
|
+
penultimate AND the base (lane-OFF) full-forward logits. Avoids a double _fwd_trunk when a caller
|
|
622
|
+
(the CLML lane trainer, H_9235) needs both the lane input (yn) and the base logits (CE target)."""
|
|
623
|
+
ta = tok if hasattr(tok, "astype") else np.array(tok, dtype=np.float64)
|
|
624
|
+
yn = _fwd_trunk(W, ta, T)
|
|
625
|
+
d = W["d"]; V = W["V"]; x = yn
|
|
626
|
+
if W.get("slw") is not None:
|
|
627
|
+
from slw import slot_apply
|
|
628
|
+
x = slot_apply(x, W["slw"], gamma=_SLW_GAMMA_OVERRIDE,
|
|
629
|
+
shuffle_perm=(None if _SLW_SHUFFLE_SEED is None
|
|
630
|
+
else np.random.RandomState(_SLW_SHUFFLE_SEED).permutation(W["slw"]["n_slot"])))
|
|
631
|
+
if W.get("bind_type", 0) != 0:
|
|
632
|
+
u = x @ W["WaWt"] + W["WaB"]; v = x @ W["WbWt"] + W["WbB"]
|
|
633
|
+
g = u * v if W["bind_type"] == 1 else u + v
|
|
634
|
+
logits = g @ W["roWt"] + W["roB"]
|
|
635
|
+
else:
|
|
636
|
+
logits = _conv1d(x, W["roWt"], W["roB"], T, d, V, 1, 1)
|
|
637
|
+
return yn, logits
|
|
638
|
+
|
|
639
|
+
|
|
640
|
+
def _seed_to_tok(seed, T):
|
|
641
|
+
"""T-window right-aligned byte encoding (1:1 with the decode seed→tok in
|
|
642
|
+
clm_decode_topk_sampled_W): utf-8 surrogateescape bytes, right-aligned into a
|
|
643
|
+
length-T window, left-pad = 32.0 (space). Shared by the hidden-dump probe."""
|
|
644
|
+
seed_b = seed.encode('utf-8', 'surrogateescape')
|
|
645
|
+
slen = len(seed_b)
|
|
646
|
+
tok = np.empty(T, dtype=np.float64)
|
|
647
|
+
for p in range(T):
|
|
648
|
+
si = slen - T + p
|
|
649
|
+
tok[p] = float(seed_b[si]) if si >= 0 else 32.0
|
|
650
|
+
return tok
|
|
651
|
+
|
|
652
|
+
|
|
653
|
+
def _fwd_logits(W, tok, T):
|
|
654
|
+
"""_clmd_fwd_logits_sc (host path) — full CLMConvMoE forward. tok:[T] ids.
|
|
655
|
+
Returns logits:[T, V]."""
|
|
656
|
+
d = W["d"]; V = W["V"]
|
|
657
|
+
yn = _fwd_trunk(W, tok, T) # [T, d] pre-readout, pre-slot penultimate
|
|
658
|
+
yn_trunk = yn # keep pre-slot trunk penultimate for the CLML read-side lane
|
|
659
|
+
# H_9200 E1 — gated-write forward-slot on the post-norm penultimate (before
|
|
660
|
+
# readout), byte-parity with the torch SLWModule + core/decode.hexa. None =>
|
|
661
|
+
# additive golden path untouched. The eval-time controls are process-global
|
|
662
|
+
# (set by cli/evaluate.py set_slw_controls): _SLW_GAMMA_OVERRIDE (--slot-off γ=0
|
|
663
|
+
# ablation) and _SLW_SHUFFLE_SEED (--slot-shuffle: a fixed permutation of the
|
|
664
|
+
# WRITE address only, reads unpermuted, breaks role→slot correspondence).
|
|
665
|
+
if W.get("slw") is not None:
|
|
666
|
+
from slw import slot_apply
|
|
667
|
+
perm = None
|
|
668
|
+
if _SLW_SHUFFLE_SEED is not None:
|
|
669
|
+
perm = np.random.RandomState(_SLW_SHUFFLE_SEED).permutation(W["slw"]["n_slot"])
|
|
670
|
+
yn = slot_apply(yn, W["slw"], gamma=_SLW_GAMMA_OVERRIDE, shuffle_perm=perm)
|
|
671
|
+
# readout: additive Conv1d (standard) OR Hadamard/linear bind (CLMB)
|
|
672
|
+
if W.get("bind_type", 0) != 0:
|
|
673
|
+
# CLMB bind readout: yn → (Wa,Wb) linear projections → Hadamard/+ → Wo
|
|
674
|
+
# WaWt=(d,k), WbWt=(d,k); roWt=(k,V) holds Wo.T; roB=(V,) holds WoB.
|
|
675
|
+
u = yn @ W["WaWt"] + W["WaB"] # [T, k]
|
|
676
|
+
v = yn @ W["WbWt"] + W["WbB"] # [T, k]
|
|
677
|
+
g = u * v if W["bind_type"] == 1 else u + v # Hadamard(1) or linear(2)
|
|
678
|
+
out_logits = g @ W["roWt"] + W["roB"] # [T, V] roWt=(k,V)
|
|
679
|
+
else:
|
|
680
|
+
out_logits = _conv1d(yn, W["roWt"], W["roB"], T, d, V, 1, 1) # [T, V]
|
|
681
|
+
# fork-A CLML read-side context-pooling lane (H_9235) — reads the pre-slot trunk
|
|
682
|
+
# penultimate, causal-mean-pools the full context, adds a gated tether-clipped logit
|
|
683
|
+
# bias. None/absent => passthrough (byte-identical). DISJOINT (read-only + additive bias).
|
|
684
|
+
if W.get("clml") is not None:
|
|
685
|
+
from clml import lane_apply
|
|
686
|
+
out_logits = lane_apply(yn_trunk, out_logits, W["clml"])
|
|
687
|
+
return out_logits
|
|
688
|
+
|
|
689
|
+
|
|
690
|
+
# ── public CLM decode/CE entries — 1:1 from clm_decode.hexa ──
|
|
691
|
+
|
|
692
|
+
def clm_forward_ce(path, corpus, nwin_max):
|
|
693
|
+
"""clm_decode.hexa::clm_forward_ce — mean CE over nwin_max T=24 causal
|
|
694
|
+
windows; uniform_ce = dt_ln(V); green = model_ce < uniform AND < shuffle."""
|
|
695
|
+
if not clm_decodable(path):
|
|
696
|
+
return {"ok": False, "reason": "not v0.2-decodable", "green": False,
|
|
697
|
+
"model_ce": 0.0, "shuffle_ce": 0.0, "uniform_ce": 0.0, "windows": 0}
|
|
698
|
+
W = clm_load_weights(path)
|
|
699
|
+
d = W["d"]; E = W["E"]; V = W["V"]; K = W["K"]
|
|
700
|
+
bytes_arr = np.frombuffer(open(corpus, 'rb').read(), dtype=np.uint8)
|
|
701
|
+
n_bytes = len(bytes_arr)
|
|
702
|
+
T = 24
|
|
703
|
+
stride = (n_bytes - T - 1) // nwin_max
|
|
704
|
+
if stride < 1:
|
|
705
|
+
stride = 1
|
|
706
|
+
sum_model = 0.0; sum_shuf = 0.0; nwin = 0
|
|
707
|
+
for s in range(nwin_max):
|
|
708
|
+
base = s * stride
|
|
709
|
+
if base + T + 1 <= n_bytes:
|
|
710
|
+
tok = bytes_arr[base:base + T].astype(np.float64)
|
|
711
|
+
tgt = bytes_arr[base + 1:base + T + 1].astype(np.float64)
|
|
712
|
+
logits = _fwd_logits(W, tok, T)
|
|
713
|
+
ce = nn_ce_loss_allpos(logits, tgt, T, V)
|
|
714
|
+
sum_model += ce
|
|
715
|
+
tgt_sh = tgt[::-1]
|
|
716
|
+
sum_shuf += nn_ce_loss_allpos(logits, tgt_sh, T, V)
|
|
717
|
+
nwin += 1
|
|
718
|
+
model_ce = sum_model / float(nwin)
|
|
719
|
+
shuf_ce = sum_shuf / float(nwin)
|
|
720
|
+
uniform_ce = float(dt_ln(np.array(float(V))))
|
|
721
|
+
lt_u = model_ce < uniform_ce
|
|
722
|
+
lt_s = model_ce < shuf_ce
|
|
723
|
+
return {"ok": True, "windows": nwin, "d": d, "E": E, "V": V, "K": K, "L": W["L"],
|
|
724
|
+
"model_ce": model_ce, "shuffle_ce": shuf_ce, "uniform_ce": uniform_ce,
|
|
725
|
+
"lt_uniform": lt_u, "lt_shuffle": lt_s, "green": lt_u and lt_s}
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
def clm_decode_argmax(path, seed, gen):
|
|
729
|
+
"""clm_decode.hexa::clm_decode_argmax — greedy continuation. T=24 window,
|
|
730
|
+
right-aligned seed (pad-left byte 32). argmax ties: first (strict >)."""
|
|
731
|
+
if not clm_decodable(path):
|
|
732
|
+
return {"ok": False, "text": ""}
|
|
733
|
+
W = clm_load_weights(path)
|
|
734
|
+
V = W["V"]
|
|
735
|
+
T = 24
|
|
736
|
+
seed_b = seed.encode('utf-8', 'surrogateescape')
|
|
737
|
+
slen = len(seed_b)
|
|
738
|
+
tok = np.empty(T, dtype=np.float64)
|
|
739
|
+
for p in range(T):
|
|
740
|
+
si = slen - T + p
|
|
741
|
+
tok[p] = float(seed_b[si]) if si >= 0 else 32.0
|
|
742
|
+
out = bytearray()
|
|
743
|
+
for _ in range(gen):
|
|
744
|
+
logits = _fwd_logits(W, tok, T)
|
|
745
|
+
row = logits[T - 1]
|
|
746
|
+
besti = 0
|
|
747
|
+
bestv = row[0]
|
|
748
|
+
for k in range(1, V):
|
|
749
|
+
if row[k] > bestv:
|
|
750
|
+
bestv = row[k]; besti = k
|
|
751
|
+
out.append(besti)
|
|
752
|
+
tok[:T - 1] = tok[1:]
|
|
753
|
+
tok[T - 1] = float(besti)
|
|
754
|
+
return {"ok": True, "text": out.decode('utf-8', 'surrogateescape')}
|
|
755
|
+
|
|
756
|
+
|
|
757
|
+
def clm_decode_topk_sampled(path, seed, gen, top_k, temp, seed_rng):
|
|
758
|
+
"""clm_decode.hexa::clm_decode_topk_sampled — seeded top-k temperature draw."""
|
|
759
|
+
if not clm_decodable(path):
|
|
760
|
+
return {"ok": False, "text": ""}
|
|
761
|
+
W = clm_load_weights(path)
|
|
762
|
+
return clm_decode_topk_sampled_W(W, seed, gen, top_k, temp, seed_rng)
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
def clm_decode_topk_sampled_W(W, seed, gen, top_k, temp, seed_rng):
|
|
766
|
+
V = W["V"]
|
|
767
|
+
T = 24
|
|
768
|
+
seed_b = seed.encode('utf-8', 'surrogateescape')
|
|
769
|
+
slen = len(seed_b)
|
|
770
|
+
tok = np.empty(T, dtype=np.float64)
|
|
771
|
+
for p in range(T):
|
|
772
|
+
si = slen - T + p
|
|
773
|
+
tok[p] = float(seed_b[si]) if si >= 0 else 32.0
|
|
774
|
+
out = bytearray()
|
|
775
|
+
rng = _mix32(seed_rng)
|
|
776
|
+
for _ in range(gen):
|
|
777
|
+
logits = _fwd_logits(W, tok, T)
|
|
778
|
+
nb, rng = _topk_sample(logits[T - 1], V, top_k, temp, rng)
|
|
779
|
+
out.append(nb)
|
|
780
|
+
tok[:T - 1] = tok[1:]
|
|
781
|
+
tok[T - 1] = float(nb)
|
|
782
|
+
return {"ok": True, "text": out.decode('utf-8', 'surrogateescape')}
|
|
783
|
+
|
|
784
|
+
|
|
785
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
786
|
+
# (c) BYTE (ByteGPT transformer) — verbatim port of core/decode.hexa
|
|
787
|
+
# (formerly core/bytegpt_decode.py) + byte-exact KV-cache fast path.
|
|
788
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
789
|
+
|
|
790
|
+
# libm erf, vectorized elementwise (object-ufunc -> float64). The hexa GELU calls
|
|
791
|
+
# `extern fn erf` (the C libm scalar erf), so math.erf is the byte-faithful twin.
|
|
792
|
+
_erf_vec = np.frompyfunc(math.erf, 1, 1)
|
|
793
|
+
|
|
794
|
+
|
|
795
|
+
def dt_sqrt(x):
|
|
796
|
+
"""flame_math.hexa::dt_sqrt — Newton-Raphson 24 iters from g0=max(x,1).
|
|
797
|
+
Scalar. (DIVERGENT twin of clm's _gn_sqrt: 40 iters from g0=x — kept distinct.)"""
|
|
798
|
+
if x <= 0.0:
|
|
799
|
+
return 0.0
|
|
800
|
+
g = x if x > 1.0 else 1.0
|
|
801
|
+
i = 0
|
|
802
|
+
while i < 24:
|
|
803
|
+
g = 0.5 * (g + x / g)
|
|
804
|
+
i = i + 1
|
|
805
|
+
return g
|
|
806
|
+
|
|
807
|
+
|
|
808
|
+
def _bg_gelu(x):
|
|
809
|
+
"""decode.hexa::_bg_gelu — 0.5*x*(1+erf(x*0.7071067811865476)).
|
|
810
|
+
erf via libm (math.erf), NOT the dt_erf twin (ING#23 torch-parity)."""
|
|
811
|
+
x = np.asarray(x, dtype=np.float64)
|
|
812
|
+
e = _erf_vec(x * 0.7071067811865476).astype(np.float64)
|
|
813
|
+
return 0.5 * x * (1.0 + e)
|
|
814
|
+
|
|
815
|
+
|
|
816
|
+
def _bg_layernorm_rows(X, g, b, T, d):
|
|
817
|
+
"""decode.hexa::_bg_layernorm — per-row LayerNorm over length d.
|
|
818
|
+
biased variance, eps=1e-5, inv = 1/dt_sqrt(var+eps). X:[T,d]. Returns Y:[T,d].
|
|
819
|
+
dt_sqrt is scalar per row (matching the hexa scalar while-loop)."""
|
|
820
|
+
eps = 0.00001
|
|
821
|
+
X = X.reshape(T, d)
|
|
822
|
+
Y = np.empty_like(X)
|
|
823
|
+
for i in range(T):
|
|
824
|
+
row = X[i]
|
|
825
|
+
mean = row.sum() / float(d)
|
|
826
|
+
dv = row - mean
|
|
827
|
+
var = (dv * dv).sum() / float(d)
|
|
828
|
+
inv = 1.0 / dt_sqrt(var + eps)
|
|
829
|
+
Y[i] = g * (dv * inv) + b
|
|
830
|
+
return Y
|
|
831
|
+
|
|
832
|
+
|
|
833
|
+
# ── .bin header + weight load — 1:1 from decode.hexa _bg_rd_u32 / bg_load ──
|
|
834
|
+
|
|
835
|
+
# _bg_rd_u32 ≡ clm's _rd_u32 (byte-identical). Aliased to the single SHARED def.
|
|
836
|
+
_bg_rd_u32 = _rd_u32
|
|
837
|
+
|
|
838
|
+
|
|
839
|
+
def bg_header(path):
|
|
840
|
+
"""parse the 5xu32 header [vocab,d,n_layer,n_head,block]. Returns dict."""
|
|
841
|
+
with open(path, 'rb') as f:
|
|
842
|
+
hdr = f.read(20)
|
|
843
|
+
if len(hdr) < 20:
|
|
844
|
+
return {"ok": False}
|
|
845
|
+
vocab = _bg_rd_u32(hdr, 0)
|
|
846
|
+
d = _bg_rd_u32(hdr, 4)
|
|
847
|
+
nlay = _bg_rd_u32(hdr, 8)
|
|
848
|
+
nh = _bg_rd_u32(hdr, 12)
|
|
849
|
+
block = _bg_rd_u32(hdr, 16)
|
|
850
|
+
return {"ok": True, "vocab": vocab, "d": d, "nlay": nlay, "nh": nh, "block": block}
|
|
851
|
+
|
|
852
|
+
|
|
853
|
+
def bg_is_bytegpt(path):
|
|
854
|
+
"""mouth-sniff: NOT CLM\\x01 magic AND a sane 5xu32 ByteGPT header.
|
|
855
|
+
Mirrors generator.hexa gen_auto_ideate dispatch (CLM\\x01 -> clm, else bytegpt)."""
|
|
856
|
+
try:
|
|
857
|
+
with open(path, 'rb') as f:
|
|
858
|
+
hdr = f.read(20)
|
|
859
|
+
except Exception:
|
|
860
|
+
return False
|
|
861
|
+
if len(hdr) < 20:
|
|
862
|
+
return False
|
|
863
|
+
# CLM\x01 magic => ConvMoE, not ByteGPT
|
|
864
|
+
if hdr[0] == 67 and hdr[1] == 76 and hdr[2] == 77 and hdr[3] == 1:
|
|
865
|
+
return False
|
|
866
|
+
vocab = _bg_rd_u32(hdr, 0)
|
|
867
|
+
d = _bg_rd_u32(hdr, 4)
|
|
868
|
+
nlay = _bg_rd_u32(hdr, 8)
|
|
869
|
+
nh = _bg_rd_u32(hdr, 12)
|
|
870
|
+
block = _bg_rd_u32(hdr, 16)
|
|
871
|
+
# sane ranges + d divisible by n_head (GPT invariant)
|
|
872
|
+
if not (1 <= vocab <= 1 << 20 and 1 <= d <= 1 << 16 and 1 <= nlay <= 1024
|
|
873
|
+
and 1 <= nh <= 1024 and 1 <= block <= 1 << 20):
|
|
874
|
+
return False
|
|
875
|
+
if d % nh != 0:
|
|
876
|
+
return False
|
|
877
|
+
return True
|
|
878
|
+
|
|
879
|
+
|
|
880
|
+
def _rd_f32(rb, off, n):
|
|
881
|
+
"""read n LE f32 from byte buffer at byte offset off -> float64 array[n]."""
|
|
882
|
+
return np.frombuffer(rb, dtype='<f4', count=n, offset=off).astype(np.float64)
|
|
883
|
+
|
|
884
|
+
|
|
885
|
+
def _bg_read_bind_block(rb, off, d):
|
|
886
|
+
"""Read ONE BGB injected-bind block from the flat trailer at byte offset off.
|
|
887
|
+
Layout = a base ByteGPT layer's 12 param tensors in bg_load order (LE f32),
|
|
888
|
+
then one LE f32 scalar `gate`. Returns (block_dict, new_off).
|
|
889
|
+
(Shapes match bg_load's per-layer read exactly — reference-matched, no reorder.)"""
|
|
890
|
+
blk = {}
|
|
891
|
+
blk["ln1w"] = _rd_f32(rb, off, d); off += d * 4
|
|
892
|
+
blk["ln1b"] = _rd_f32(rb, off, d); off += d * 4
|
|
893
|
+
blk["inW"] = _rd_f32(rb, off, 3 * d * d).reshape(3 * d, d); off += 3 * d * d * 4
|
|
894
|
+
blk["inB"] = _rd_f32(rb, off, 3 * d); off += 3 * d * 4
|
|
895
|
+
blk["oW"] = _rd_f32(rb, off, d * d).reshape(d, d); off += d * d * 4
|
|
896
|
+
blk["oB"] = _rd_f32(rb, off, d); off += d * 4
|
|
897
|
+
blk["ln2w"] = _rd_f32(rb, off, d); off += d * 4
|
|
898
|
+
blk["ln2b"] = _rd_f32(rb, off, d); off += d * 4
|
|
899
|
+
blk["m0W"] = _rd_f32(rb, off, 4 * d * d).reshape(4 * d, d); off += 4 * d * d * 4
|
|
900
|
+
blk["m0B"] = _rd_f32(rb, off, 4 * d); off += 4 * d * 4
|
|
901
|
+
blk["m2W"] = _rd_f32(rb, off, d * 4 * d).reshape(d, 4 * d); off += d * 4 * d * 4
|
|
902
|
+
blk["m2B"] = _rd_f32(rb, off, d); off += d * 4
|
|
903
|
+
blk["gate"] = float(_rd_f32(rb, off, 1)[0]); off += 4
|
|
904
|
+
return blk, off
|
|
905
|
+
|
|
906
|
+
|
|
907
|
+
def bg_load(path):
|
|
908
|
+
"""decode.hexa::bg_load — parse the flat binary ONCE into a weight dict.
|
|
909
|
+
Weight binary layout (LE f32, decode.hexa:29-36):
|
|
910
|
+
[vocab,d,n_layer,n_head,block] 5xu32
|
|
911
|
+
tok[vocab*d] pos[block*d]
|
|
912
|
+
per layer: ln1.w[d] ln1.b[d] in_proj.w[3d*d] in_proj.b[3d]
|
|
913
|
+
out_proj.w[d*d] out_proj.b[d] ln2.w[d] ln2.b[d]
|
|
914
|
+
mlp0.w[4d*d] mlp0.b[4d] mlp2.w[d*4d] mlp2.b[d]
|
|
915
|
+
ln_f.w[d] ln_f.b[d] head[vocab*d]
|
|
916
|
+
(torch Linear stores W as [out,in] row-major; y = x.Wt + b.)"""
|
|
917
|
+
rb = open(path, 'rb').read()
|
|
918
|
+
vocab = _bg_rd_u32(rb, 0)
|
|
919
|
+
d = _bg_rd_u32(rb, 4)
|
|
920
|
+
nlay = _bg_rd_u32(rb, 8)
|
|
921
|
+
nh = _bg_rd_u32(rb, 12)
|
|
922
|
+
block = _bg_rd_u32(rb, 16)
|
|
923
|
+
off = 20
|
|
924
|
+
|
|
925
|
+
tok = _rd_f32(rb, off, vocab * d).reshape(vocab, d); off += vocab * d * 4
|
|
926
|
+
pos = _rd_f32(rb, off, block * d).reshape(block, d); off += block * d * 4
|
|
927
|
+
|
|
928
|
+
ln1w = []; ln1b = []; inW = []; inB = []
|
|
929
|
+
oW = []; oB = []; ln2w = []; ln2b = []
|
|
930
|
+
m0W = []; m0B = []; m2W = []; m2B = []
|
|
931
|
+
for _ in range(nlay):
|
|
932
|
+
ln1w.append(_rd_f32(rb, off, d)); off += d * 4
|
|
933
|
+
ln1b.append(_rd_f32(rb, off, d)); off += d * 4
|
|
934
|
+
inW.append(_rd_f32(rb, off, 3 * d * d).reshape(3 * d, d)); off += 3 * d * d * 4
|
|
935
|
+
inB.append(_rd_f32(rb, off, 3 * d)); off += 3 * d * 4
|
|
936
|
+
oW.append(_rd_f32(rb, off, d * d).reshape(d, d)); off += d * d * 4
|
|
937
|
+
oB.append(_rd_f32(rb, off, d)); off += d * 4
|
|
938
|
+
ln2w.append(_rd_f32(rb, off, d)); off += d * 4
|
|
939
|
+
ln2b.append(_rd_f32(rb, off, d)); off += d * 4
|
|
940
|
+
m0W.append(_rd_f32(rb, off, 4 * d * d).reshape(4 * d, d)); off += 4 * d * d * 4
|
|
941
|
+
m0B.append(_rd_f32(rb, off, 4 * d)); off += 4 * d * 4
|
|
942
|
+
m2W.append(_rd_f32(rb, off, d * 4 * d).reshape(d, 4 * d)); off += d * 4 * d * 4
|
|
943
|
+
m2B.append(_rd_f32(rb, off, d)); off += d * 4
|
|
944
|
+
lnfw = _rd_f32(rb, off, d); off += d * 4
|
|
945
|
+
lnfb = _rd_f32(rb, off, d); off += d * 4
|
|
946
|
+
head = _rd_f32(rb, off, vocab * d).reshape(vocab, d); off += vocab * d * 4
|
|
947
|
+
|
|
948
|
+
# ── optional BGB injected-bind trailer (serialize_bind, H_9027) ──────
|
|
949
|
+
# "BGB\x01" = bytes 66,71,66,1, appended AFTER `head`. Carries n_bind appended
|
|
950
|
+
# GATED transformer blocks (each = a standard base layer: the SAME 12 param
|
|
951
|
+
# tensors in the SAME order/layout as above, then one LE f32 gate). Applied
|
|
952
|
+
# after the L base blocks and BEFORE ln_f (mirrors the CLM "CLMB" bind-trailer
|
|
953
|
+
# precedent). ABSENT trailer => bind=[] => forward BYTE-IDENTICAL to plain ByteGPT.
|
|
954
|
+
bind = []
|
|
955
|
+
if (off + 8 <= len(rb)
|
|
956
|
+
and rb[off] == 66 and rb[off + 1] == 71
|
|
957
|
+
and rb[off + 2] == 66 and rb[off + 3] == 1):
|
|
958
|
+
off += 4 # skip "BGB\x01"
|
|
959
|
+
n_bind = _bg_rd_u32(rb, off); off += 4
|
|
960
|
+
for _ in range(n_bind):
|
|
961
|
+
blk, off = _bg_read_bind_block(rb, off, d)
|
|
962
|
+
bind.append(blk)
|
|
963
|
+
|
|
964
|
+
return {"ok": True, "vocab": vocab, "d": d, "nlay": nlay, "nh": nh, "block": block,
|
|
965
|
+
"tok": tok, "pos": pos, "ln1w": ln1w, "ln1b": ln1b,
|
|
966
|
+
"inW": inW, "inB": inB, "oW": oW, "oB": oB,
|
|
967
|
+
"ln2w": ln2w, "ln2b": ln2b, "m0W": m0W, "m0B": m0B,
|
|
968
|
+
"m2W": m2W, "m2B": m2B, "lnfw": lnfw, "lnfb": lnfb, "head": head,
|
|
969
|
+
"bind": bind}
|
|
970
|
+
|
|
971
|
+
|
|
972
|
+
# bg_load_ranged — byte-identical W-map to bg_load (hexa builds it via ranged
|
|
973
|
+
# read_bytes_at to dodge the whole-file boxing OOM; the resulting weights are the
|
|
974
|
+
# same, and the py read already mmaps lazily via frombuffer). Aliased for surface
|
|
975
|
+
# parity with the hexa public entries (decode.hexa:782).
|
|
976
|
+
bg_load_ranged = bg_load
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
# ── forward — 1:1 from decode.hexa _bg_mha / _bg_linear / bg_forward_last_W ──
|
|
980
|
+
|
|
981
|
+
def _bg_mha(H, inW, inB, oW, oB, T, d, nh):
|
|
982
|
+
"""decode.hexa::_bg_mha — torch nn.MultiheadAttention semantics.
|
|
983
|
+
H:[T,d] pre-normed. in_proj W[3d,d] rows Q|K|V; out_proj W[d,d]+b.
|
|
984
|
+
scale=1/dt_sqrt(hd), causal, libm exp softmax. Returns Aout:[T,d]."""
|
|
985
|
+
hd = d // nh
|
|
986
|
+
scale = 1.0 / dt_sqrt(float(hd))
|
|
987
|
+
# QKV = H[T,d] @ inW.T[d,3d] + inB -> packed [T,3d]; Q=cols0..d, K=d..2d, V=2d..3d
|
|
988
|
+
QKV = H @ inW.T + inB # [T, 3d]
|
|
989
|
+
Q = QKV[:, 0:d]; K = QKV[:, d:2 * d]; V = QKV[:, 2 * d:3 * d]
|
|
990
|
+
ctx = np.zeros((T, d), dtype=np.float64)
|
|
991
|
+
for hh in range(nh):
|
|
992
|
+
base = hh * hd
|
|
993
|
+
Qh = Q[:, base:base + hd]; Kh = K[:, base:base + hd]; Vh = V[:, base:base + hd]
|
|
994
|
+
for i in range(T):
|
|
995
|
+
L = i + 1
|
|
996
|
+
# scores over keys 0..i, scaled
|
|
997
|
+
sc = (Qh[i] @ Kh[0:L].T) * scale # [L]
|
|
998
|
+
mx = sc.max()
|
|
999
|
+
e = np.exp(sc - mx) # libm-equivalent
|
|
1000
|
+
tot = e.sum()
|
|
1001
|
+
ctx[i, base:base + hd] = (e / tot) @ Vh[0:L] # [hd]
|
|
1002
|
+
# out proj: Aout = ctx @ oW.T + oB
|
|
1003
|
+
return ctx @ oW.T + oB
|
|
1004
|
+
|
|
1005
|
+
|
|
1006
|
+
def _bg_apply_bind(x, bind, T, d, nh):
|
|
1007
|
+
"""Apply N appended GATED transformer blocks to the full post-base sequence
|
|
1008
|
+
x:[T,d], AFTER the L base blocks and BEFORE ln_f. Each block is a STANDARD
|
|
1009
|
+
transformer block (reuses _bg_layernorm_rows / _bg_mha / _bg_gelu — the exact
|
|
1010
|
+
base-layer ops) plus a scalar gate:
|
|
1011
|
+
block(x) = x + mha(ln1(x)) + mlp(ln2(x + mha(ln1(x))))
|
|
1012
|
+
x = x + gate * (block(x) - x) # gate=0 => identity (exact)
|
|
1013
|
+
Mirrors BindAttnByteGPT.forward `x = x + gate*(bind(x)-x)`. Returns x:[T,d]."""
|
|
1014
|
+
for blk in bind:
|
|
1015
|
+
nrm = _bg_layernorm_rows(x, blk["ln1w"], blk["ln1b"], T, d)
|
|
1016
|
+
aout = _bg_mha(nrm, blk["inW"], blk["inB"], blk["oW"], blk["oB"], T, d, nh)
|
|
1017
|
+
xa = x + aout # x + mha(ln1(x))
|
|
1018
|
+
nrm2 = _bg_layernorm_rows(xa, blk["ln2w"], blk["ln2b"], T, d)
|
|
1019
|
+
h4 = _bg_gelu(nrm2 @ blk["m0W"].T + blk["m0B"]) # [T, 4d]
|
|
1020
|
+
mlpo = h4 @ blk["m2W"].T + blk["m2B"] # [T, d]
|
|
1021
|
+
blk_out = xa + mlpo # = block(x)
|
|
1022
|
+
x = x + blk["gate"] * (blk_out - x) # gate=0 => x unchanged
|
|
1023
|
+
return x
|
|
1024
|
+
|
|
1025
|
+
|
|
1026
|
+
def bg_forward_last_W(W, ids, T):
|
|
1027
|
+
"""decode.hexa::bg_forward_last_W — full forward from a loaded weight
|
|
1028
|
+
dict; returns last-position next-byte logits float64[vocab]. ids:[T] int.
|
|
1029
|
+
When W["bind"] is non-empty the appended gated blocks run after the L base
|
|
1030
|
+
blocks and before ln_f (H_9027); absent/empty => byte-identical to before."""
|
|
1031
|
+
d = W["d"]; nlay = W["nlay"]; nh = W["nh"]; vocab = W["vocab"]
|
|
1032
|
+
ids = np.asarray(ids, dtype=np.int64)
|
|
1033
|
+
# x[T,d] = tok[id] + pos[t]
|
|
1034
|
+
x = W["tok"][ids] + W["pos"][0:T] # [T, d]
|
|
1035
|
+
for Lr in range(nlay):
|
|
1036
|
+
# attn sub-block: x = x + MHA(LN1(x))
|
|
1037
|
+
nrm = _bg_layernorm_rows(x, W["ln1w"][Lr], W["ln1b"][Lr], T, d)
|
|
1038
|
+
aout = _bg_mha(nrm, W["inW"][Lr], W["inB"][Lr], W["oW"][Lr], W["oB"][Lr], T, d, nh)
|
|
1039
|
+
x = x + aout
|
|
1040
|
+
# mlp sub-block: x = x + Wd(GELU(W0(LN2(x))))
|
|
1041
|
+
nrm = _bg_layernorm_rows(x, W["ln2w"][Lr], W["ln2b"][Lr], T, d)
|
|
1042
|
+
h4 = nrm @ W["m0W"][Lr].T + W["m0B"][Lr] # [T, 4d]
|
|
1043
|
+
h4 = _bg_gelu(h4)
|
|
1044
|
+
mlpo = h4 @ W["m2W"][Lr].T + W["m2B"][Lr] # [T, d]
|
|
1045
|
+
x = x + mlpo
|
|
1046
|
+
# appended injected-bind blocks (H_9027) — after base stack, before ln_f
|
|
1047
|
+
if W.get("bind"):
|
|
1048
|
+
x = _bg_apply_bind(x, W["bind"], T, d, nh)
|
|
1049
|
+
# final LayerNorm on the LAST position only, then tied head
|
|
1050
|
+
lastrow = _bg_layernorm_rows(x[T - 1:T], W["lnfw"], W["lnfb"], 1, d)[0] # [d]
|
|
1051
|
+
logits = W["head"] @ lastrow # [vocab]
|
|
1052
|
+
return logits
|
|
1053
|
+
|
|
1054
|
+
|
|
1055
|
+
def bg_forward_last_hidden(W, ids, T):
|
|
1056
|
+
"""H_9129 L5 rung-3 — same full forward as bg_forward_last_W but returns the
|
|
1057
|
+
final-LN LAST-position hidden state float64[d] (the pre-head representation),
|
|
1058
|
+
NOT the tied-head logits. The hippocampal associative store (core/hippo_assoc)
|
|
1059
|
+
keys on this hidden, not on logits (design: reps = final-LN hidden). Byte-shares
|
|
1060
|
+
the forward body with bg_forward_last_W; the only difference is the return line
|
|
1061
|
+
(head @ lastrow omitted). ids:[T] int; W["bind"] handled identically (H_9027)."""
|
|
1062
|
+
d = W["d"]; nlay = W["nlay"]; nh = W["nh"]
|
|
1063
|
+
ids = np.asarray(ids, dtype=np.int64)
|
|
1064
|
+
x = W["tok"][ids] + W["pos"][0:T] # [T, d]
|
|
1065
|
+
for Lr in range(nlay):
|
|
1066
|
+
nrm = _bg_layernorm_rows(x, W["ln1w"][Lr], W["ln1b"][Lr], T, d)
|
|
1067
|
+
aout = _bg_mha(nrm, W["inW"][Lr], W["inB"][Lr], W["oW"][Lr], W["oB"][Lr], T, d, nh)
|
|
1068
|
+
x = x + aout
|
|
1069
|
+
nrm = _bg_layernorm_rows(x, W["ln2w"][Lr], W["ln2b"][Lr], T, d)
|
|
1070
|
+
h4 = _bg_gelu(nrm @ W["m0W"][Lr].T + W["m0B"][Lr]) # [T, 4d]
|
|
1071
|
+
mlpo = h4 @ W["m2W"][Lr].T + W["m2B"][Lr] # [T, d]
|
|
1072
|
+
x = x + mlpo
|
|
1073
|
+
if W.get("bind"):
|
|
1074
|
+
x = _bg_apply_bind(x, W["bind"], T, d, nh)
|
|
1075
|
+
lastrow = _bg_layernorm_rows(x[T - 1:T], W["lnfw"], W["lnfb"], 1, d)[0] # [d]
|
|
1076
|
+
return lastrow # final-LN last-pos hidden
|
|
1077
|
+
|
|
1078
|
+
|
|
1079
|
+
def bytegpt_forward_last(path, ids, T):
|
|
1080
|
+
"""decode.hexa::bytegpt_forward_last — load + forward (parity probe)."""
|
|
1081
|
+
W = bg_load(path)
|
|
1082
|
+
return bg_forward_last_W(W, ids, T)
|
|
1083
|
+
|
|
1084
|
+
|
|
1085
|
+
def bg_argmax(a):
|
|
1086
|
+
"""decode.hexa::bg_argmax — index of max (ties: first, strict >)."""
|
|
1087
|
+
bi = 0
|
|
1088
|
+
bv = a[0]
|
|
1089
|
+
for k in range(1, len(a)):
|
|
1090
|
+
if a[k] > bv:
|
|
1091
|
+
bv = a[k]; bi = k
|
|
1092
|
+
return bi
|
|
1093
|
+
|
|
1094
|
+
|
|
1095
|
+
def _seed_to_ids(seed):
|
|
1096
|
+
"""string|bytes|list -> list[int] byte ids."""
|
|
1097
|
+
if isinstance(seed, (list, tuple)):
|
|
1098
|
+
return [int(x) for x in seed]
|
|
1099
|
+
if isinstance(seed, str):
|
|
1100
|
+
seed = seed.encode('utf-8', 'surrogateescape')
|
|
1101
|
+
return list(seed)
|
|
1102
|
+
|
|
1103
|
+
|
|
1104
|
+
# ── KV-cache fast path — NEW (byte-exact TOKEN stream vs full-forward) ───────
|
|
1105
|
+
#
|
|
1106
|
+
# The full-forward loop re-forwards ALL T window tokens every decode step
|
|
1107
|
+
# (O(gen²)). The KV path caches per-layer (K,V) = the in_proj K,V slices from
|
|
1108
|
+
# _bg_mha and, on each new token, forwards ONLY the new position, attending it
|
|
1109
|
+
# against the cached K,V of all layers. This mirrors the hexa KV-cache
|
|
1110
|
+
# (decode.hexa:919, proven byte-identical to full-forward there).
|
|
1111
|
+
#
|
|
1112
|
+
# WINDOW-SLIDE RESYNC: ByteGPT indexes the positional embedding by position
|
|
1113
|
+
# WITHIN the window (pos[0:T]), not by absolute token index. While the window is
|
|
1114
|
+
# still GROWING (n <= block, start=0) every cached position keeps its window
|
|
1115
|
+
# index → K,V stay valid → cheap incremental append. Once the window SLIDES
|
|
1116
|
+
# (n > block, start>0) every token's window position shifts by one → its pos
|
|
1117
|
+
# embedding (hence K,V) changes → the cache is INVALID and is fully REBUILT at
|
|
1118
|
+
# M=T (byte-identical to full-forward for that window). So sliding steps cost a
|
|
1119
|
+
# full forward (correct, not faster); the speedup is the growing-window regime,
|
|
1120
|
+
# which is the entire eval decode path (seed+gen << block=512 for the 303M).
|
|
1121
|
+
|
|
1122
|
+
def _bg_forward_build(W, ids, T):
|
|
1123
|
+
"""Full-forward over the window (== bg_forward_last_W) that ALSO captures the
|
|
1124
|
+
per-layer (K,V) in_proj slices for every position. Returns (logits, cache)
|
|
1125
|
+
where cache[Lr] = [K[T,d], V[T,d]]. The last-position logits are byte-
|
|
1126
|
+
identical to bg_forward_last_W (same ops, M=T batched)."""
|
|
1127
|
+
d = W["d"]; nlay = W["nlay"]; nh = W["nh"]
|
|
1128
|
+
ids = np.asarray(ids, dtype=np.int64)
|
|
1129
|
+
x = W["tok"][ids] + W["pos"][0:T] # [T, d]
|
|
1130
|
+
hd = d // nh
|
|
1131
|
+
scale = 1.0 / dt_sqrt(float(hd))
|
|
1132
|
+
cache = []
|
|
1133
|
+
for Lr in range(nlay):
|
|
1134
|
+
nrm = _bg_layernorm_rows(x, W["ln1w"][Lr], W["ln1b"][Lr], T, d)
|
|
1135
|
+
QKV = nrm @ W["inW"][Lr].T + W["inB"][Lr] # [T, 3d]
|
|
1136
|
+
Q = QKV[:, 0:d]; K = QKV[:, d:2 * d]; V = QKV[:, 2 * d:3 * d]
|
|
1137
|
+
ctx = np.zeros((T, d), dtype=np.float64)
|
|
1138
|
+
for hh in range(nh):
|
|
1139
|
+
base = hh * hd
|
|
1140
|
+
Qh = Q[:, base:base + hd]; Kh = K[:, base:base + hd]; Vh = V[:, base:base + hd]
|
|
1141
|
+
for i in range(T):
|
|
1142
|
+
L = i + 1
|
|
1143
|
+
sc = (Qh[i] @ Kh[0:L].T) * scale
|
|
1144
|
+
mx = sc.max()
|
|
1145
|
+
e = np.exp(sc - mx)
|
|
1146
|
+
tot = e.sum()
|
|
1147
|
+
ctx[i, base:base + hd] = (e / tot) @ Vh[0:L]
|
|
1148
|
+
aout = ctx @ W["oW"][Lr].T + W["oB"][Lr]
|
|
1149
|
+
x = x + aout
|
|
1150
|
+
nrm = _bg_layernorm_rows(x, W["ln2w"][Lr], W["ln2b"][Lr], T, d)
|
|
1151
|
+
h4 = nrm @ W["m0W"][Lr].T + W["m0B"][Lr]
|
|
1152
|
+
h4 = _bg_gelu(h4)
|
|
1153
|
+
mlpo = h4 @ W["m2W"][Lr].T + W["m2B"][Lr]
|
|
1154
|
+
x = x + mlpo
|
|
1155
|
+
cache.append([K.copy(), V.copy()])
|
|
1156
|
+
lastrow = _bg_layernorm_rows(x[T - 1:T], W["lnfw"], W["lnfb"], 1, d)[0]
|
|
1157
|
+
logits = W["head"] @ lastrow
|
|
1158
|
+
return logits, cache
|
|
1159
|
+
|
|
1160
|
+
|
|
1161
|
+
def _bg_kv_step(W, cache, id_new, win_pos):
|
|
1162
|
+
"""Forward ONLY the new position (window index win_pos, byte id id_new),
|
|
1163
|
+
attending against + appending to the per-layer K,V cache. Returns the new
|
|
1164
|
+
position's next-byte logits. cache[Lr]=[K,V] is grown in place by one row.
|
|
1165
|
+
Reuses the EXACT _bg_mha attention ops (same scale, libm exp softmax, same
|
|
1166
|
+
per-output accumulation order) so the token stream matches full-forward."""
|
|
1167
|
+
d = W["d"]; nlay = W["nlay"]; nh = W["nh"]
|
|
1168
|
+
hd = d // nh
|
|
1169
|
+
scale = 1.0 / dt_sqrt(float(hd))
|
|
1170
|
+
x = (W["tok"][int(id_new)] + W["pos"][win_pos]).astype(np.float64).reshape(1, d)
|
|
1171
|
+
for Lr in range(nlay):
|
|
1172
|
+
nrm = _bg_layernorm_rows(x, W["ln1w"][Lr], W["ln1b"][Lr], 1, d) # [1,d]
|
|
1173
|
+
qkv = nrm @ W["inW"][Lr].T + W["inB"][Lr] # [1,3d]
|
|
1174
|
+
qn = qkv[:, 0:d]; kn = qkv[:, d:2 * d]; vn = qkv[:, 2 * d:3 * d] # [1,d]
|
|
1175
|
+
Kc = np.concatenate([cache[Lr][0], kn], axis=0) # [T,d]
|
|
1176
|
+
Vc = np.concatenate([cache[Lr][1], vn], axis=0)
|
|
1177
|
+
cache[Lr][0] = Kc; cache[Lr][1] = Vc
|
|
1178
|
+
ctx = np.zeros((1, d), dtype=np.float64)
|
|
1179
|
+
for hh in range(nh):
|
|
1180
|
+
base = hh * hd
|
|
1181
|
+
qh = qn[0, base:base + hd] # [hd]
|
|
1182
|
+
Kh = Kc[:, base:base + hd] # [T,hd]
|
|
1183
|
+
Vh = Vc[:, base:base + hd] # [T,hd]
|
|
1184
|
+
sc = (qh @ Kh.T) * scale # [T]
|
|
1185
|
+
mx = sc.max()
|
|
1186
|
+
e = np.exp(sc - mx)
|
|
1187
|
+
tot = e.sum()
|
|
1188
|
+
ctx[0, base:base + hd] = (e / tot) @ Vh
|
|
1189
|
+
aout = ctx @ W["oW"][Lr].T + W["oB"][Lr] # [1,d]
|
|
1190
|
+
x = x + aout
|
|
1191
|
+
nrm = _bg_layernorm_rows(x, W["ln2w"][Lr], W["ln2b"][Lr], 1, d)
|
|
1192
|
+
h4 = nrm @ W["m0W"][Lr].T + W["m0B"][Lr]
|
|
1193
|
+
h4 = _bg_gelu(h4)
|
|
1194
|
+
mlpo = h4 @ W["m2W"][Lr].T + W["m2B"][Lr]
|
|
1195
|
+
x = x + mlpo
|
|
1196
|
+
lastrow = _bg_layernorm_rows(x, W["lnfw"], W["lnfb"], 1, d)[0]
|
|
1197
|
+
logits = W["head"] @ lastrow
|
|
1198
|
+
return logits
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
def _bg_step_logits(W, toks, st):
|
|
1202
|
+
"""One decode step's last-position logits via KV cache. `st` is a mutable
|
|
1203
|
+
dict {'cache','start'} carried across steps. Builds (M=T) on the first step
|
|
1204
|
+
OR whenever the window slides (start changes = pos-embedding re-index); else
|
|
1205
|
+
appends the single new token incrementally."""
|
|
1206
|
+
block = W["block"]
|
|
1207
|
+
n = len(toks)
|
|
1208
|
+
start = n - block if n > block else 0
|
|
1209
|
+
T = n - start
|
|
1210
|
+
# Injected-bind models (H_9027): the appended blocks have their OWN attention
|
|
1211
|
+
# over the full post-base sequence, which the incremental KV step can't supply
|
|
1212
|
+
# from base-layer K,V alone — so decode via the full-sequence forward (byte-
|
|
1213
|
+
# exact vs the torch reference; gate=0 => identical to the base KV stream since
|
|
1214
|
+
# the base forward is unchanged and the bind contribution is exactly zero).
|
|
1215
|
+
if W.get("bind"):
|
|
1216
|
+
return bg_forward_last_W(W, toks[start:start + T], T)
|
|
1217
|
+
if st['cache'] is None or start != st['start']:
|
|
1218
|
+
logits, cache = _bg_forward_build(W, toks[start:start + T], T)
|
|
1219
|
+
st['cache'] = cache; st['start'] = start
|
|
1220
|
+
return logits
|
|
1221
|
+
# incremental: the new token is at window position T-1 (= toks[start+T-1]).
|
|
1222
|
+
return _bg_kv_step(W, st['cache'], toks[start + T - 1], T - 1)
|
|
1223
|
+
|
|
1224
|
+
|
|
1225
|
+
# ── public decode entries — 1:1 from decode.hexa (KV path wired in) ──
|
|
1226
|
+
|
|
1227
|
+
def bytegpt_decode_argmax(path, seed_ids, gen):
|
|
1228
|
+
"""decode.hexa::bytegpt_decode_argmax — greedy continuation, weights
|
|
1229
|
+
loaded ONCE, prompt window grown (capped at block). Returns {ok,text,ids}.
|
|
1230
|
+
(Decode loop = byte-exact KV cache; full-forward reference = _decode_argmax_W_full.)"""
|
|
1231
|
+
W = bg_load(path)
|
|
1232
|
+
return _decode_argmax_W(W, seed_ids, gen)
|
|
1233
|
+
|
|
1234
|
+
|
|
1235
|
+
# bytegpt_decode_argmax_ranged — OOM-safe twin (hexa: bg_load_ranged). Same compute.
|
|
1236
|
+
def bytegpt_decode_argmax_ranged(path, seed_ids, gen):
|
|
1237
|
+
W = bg_load_ranged(path)
|
|
1238
|
+
return _decode_argmax_W(W, seed_ids, gen)
|
|
1239
|
+
|
|
1240
|
+
|
|
1241
|
+
def _decode_argmax_W(W, seed_ids, gen):
|
|
1242
|
+
"""KV-cache greedy decode from an already-loaded weight dict. Token stream is
|
|
1243
|
+
byte-exact vs _decode_argmax_W_full (the O(gen²) full-forward reference)."""
|
|
1244
|
+
toks = _seed_to_ids(seed_ids)
|
|
1245
|
+
outl = []
|
|
1246
|
+
st = {'cache': None, 'start': None}
|
|
1247
|
+
for _ in range(gen):
|
|
1248
|
+
logits = _bg_step_logits(W, toks, st)
|
|
1249
|
+
nb = bg_argmax(logits)
|
|
1250
|
+
toks.append(nb)
|
|
1251
|
+
outl.append(nb)
|
|
1252
|
+
text = bytes(outl).decode('utf-8', 'surrogateescape')
|
|
1253
|
+
return {"ok": True, "text": text, "ids": outl}
|
|
1254
|
+
|
|
1255
|
+
|
|
1256
|
+
def _decode_argmax_W_full(W, seed_ids, gen):
|
|
1257
|
+
"""ORIGINAL O(gen²) full-forward greedy decode (KV-cache OFF). Retained as the
|
|
1258
|
+
byte-exactness reference for the self-test."""
|
|
1259
|
+
vocab = W["vocab"]; block = W["block"]
|
|
1260
|
+
toks = _seed_to_ids(seed_ids)
|
|
1261
|
+
outl = []
|
|
1262
|
+
for _ in range(gen):
|
|
1263
|
+
n = len(toks)
|
|
1264
|
+
start = n - block if n > block else 0
|
|
1265
|
+
T = n - start
|
|
1266
|
+
ids = toks[start:start + T]
|
|
1267
|
+
logits = bg_forward_last_W(W, ids, T)
|
|
1268
|
+
nb = bg_argmax(logits)
|
|
1269
|
+
toks.append(nb)
|
|
1270
|
+
outl.append(nb)
|
|
1271
|
+
text = bytes(outl).decode('utf-8', 'surrogateescape')
|
|
1272
|
+
return {"ok": True, "text": text, "ids": outl}
|
|
1273
|
+
|
|
1274
|
+
|
|
1275
|
+
def bytegpt_decode_topk_sampled(path, seed_ids, gen, top_k, temp, seed_rng):
|
|
1276
|
+
"""decode.hexa::bytegpt_decode_topk_sampled — seeded top-k temp draw."""
|
|
1277
|
+
W = bg_load(path)
|
|
1278
|
+
return bytegpt_decode_topk_sampled_W(W, seed_ids, gen, top_k, temp, seed_rng)
|
|
1279
|
+
|
|
1280
|
+
|
|
1281
|
+
# OOM-safe twin (hexa: bytegpt_decode_topk_sampled_ranged via bg_load_ranged).
|
|
1282
|
+
def bytegpt_decode_topk_sampled_ranged(path, seed_ids, gen, top_k, temp, seed_rng):
|
|
1283
|
+
W = bg_load_ranged(path)
|
|
1284
|
+
return bytegpt_decode_topk_sampled_W(W, seed_ids, gen, top_k, temp, seed_rng)
|
|
1285
|
+
|
|
1286
|
+
|
|
1287
|
+
def bytegpt_decode_topk_sampled_W(W, seed_ids, gen, top_k, temp, seed_rng):
|
|
1288
|
+
"""KV-cache seeded top-k decode from an already-loaded weight dict (== hexa
|
|
1289
|
+
_bg_gen_from_W KV branch). seed_ids = string|bytes|list of byte ids. Token
|
|
1290
|
+
stream is byte-exact vs bytegpt_decode_topk_sampled_W_full."""
|
|
1291
|
+
vocab = W["vocab"]
|
|
1292
|
+
toks = _seed_to_ids(seed_ids)
|
|
1293
|
+
outl = []
|
|
1294
|
+
rng = _mix32(seed_rng)
|
|
1295
|
+
st = {'cache': None, 'start': None}
|
|
1296
|
+
for _ in range(gen):
|
|
1297
|
+
logits = _bg_step_logits(W, toks, st)
|
|
1298
|
+
nb, rng = _topk_sample(logits, vocab, top_k, temp, rng)
|
|
1299
|
+
toks.append(nb)
|
|
1300
|
+
outl.append(nb)
|
|
1301
|
+
text = bytes(outl).decode('utf-8', 'surrogateescape')
|
|
1302
|
+
return {"ok": True, "text": text, "ids": outl}
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
def bytegpt_decode_topk_sampled_W_full(W, seed_ids, gen, top_k, temp, seed_rng):
|
|
1306
|
+
"""ORIGINAL O(gen²) full-forward seeded top-k decode (KV-cache OFF). Retained
|
|
1307
|
+
as the byte-exactness reference for the self-test."""
|
|
1308
|
+
vocab = W["vocab"]; block = W["block"]
|
|
1309
|
+
toks = _seed_to_ids(seed_ids)
|
|
1310
|
+
outl = []
|
|
1311
|
+
rng = _mix32(seed_rng)
|
|
1312
|
+
for _ in range(gen):
|
|
1313
|
+
n = len(toks)
|
|
1314
|
+
start = n - block if n > block else 0
|
|
1315
|
+
T = n - start
|
|
1316
|
+
ids = toks[start:start + T]
|
|
1317
|
+
logits = bg_forward_last_W(W, ids, T)
|
|
1318
|
+
nb, rng = _topk_sample(logits, vocab, top_k, temp, rng)
|
|
1319
|
+
toks.append(nb)
|
|
1320
|
+
outl.append(nb)
|
|
1321
|
+
text = bytes(outl).decode('utf-8', 'surrogateescape')
|
|
1322
|
+
return {"ok": True, "text": text, "ids": outl}
|
|
1323
|
+
|
|
1324
|
+
|
|
1325
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
1326
|
+
# (d) MOUTH DISPATCH — header-sniff (mirrors generator.hexa gen_auto_backend).
|
|
1327
|
+
# CLM\x01 magic + CLMX trailer => conv (.clm); sane 5xu32 header => byte (.bin).
|
|
1328
|
+
# ════════════════════════════════════════════════════════════════════════
|
|
1329
|
+
|
|
1330
|
+
def decode_mouth_kind(path):
|
|
1331
|
+
"""gen_mouth_kind — 'clm' | 'bytegpt' | 'unknown'. clm checked FIRST (its
|
|
1332
|
+
CLM\\x01 magic is what bg_is_bytegpt explicitly rejects)."""
|
|
1333
|
+
if clm_decodable(path):
|
|
1334
|
+
return "clm"
|
|
1335
|
+
if bg_is_bytegpt(path):
|
|
1336
|
+
return "bytegpt"
|
|
1337
|
+
return "unknown"
|
|
1338
|
+
|
|
1339
|
+
|
|
1340
|
+
def decode_load(path):
|
|
1341
|
+
"""gen_auto_backend — load the correct weight dict by mouth sniff. Returns
|
|
1342
|
+
(kind, W). kind in {'clm','bytegpt'}; raises for an unknown mouth."""
|
|
1343
|
+
k = decode_mouth_kind(path)
|
|
1344
|
+
if k == "clm":
|
|
1345
|
+
return "clm", clm_load_weights(path)
|
|
1346
|
+
if k == "bytegpt":
|
|
1347
|
+
return "bytegpt", bg_load(path)
|
|
1348
|
+
raise RuntimeError("ckpt not decodable (unknown mouth): " + str(path))
|
|
1349
|
+
|
|
1350
|
+
|
|
1351
|
+
def decode_auto_topk_sampled(path, seed, gen, top_k, temp, seed_rng):
|
|
1352
|
+
"""gen_auto_chat — dispatch a seeded top-k decode to the correct mouth by
|
|
1353
|
+
header sniff (conv .clm -> clm_decode_topk_sampled_W; byte .bin ->
|
|
1354
|
+
bytegpt_decode_topk_sampled_W, KV cache). seed = string."""
|
|
1355
|
+
kind, W = decode_load(path)
|
|
1356
|
+
if kind == "clm":
|
|
1357
|
+
return clm_decode_topk_sampled_W(W, seed, gen, top_k, temp, seed_rng)
|
|
1358
|
+
return bytegpt_decode_topk_sampled_W(W, seed, gen, top_k, temp, seed_rng)
|
|
1359
|
+
|
|
1360
|
+
|
|
1361
|
+
def decode_auto_argmax(path, seed, gen):
|
|
1362
|
+
"""gen_auto (greedy) — dispatch argmax decode by header sniff. seed = string.
|
|
1363
|
+
(clm uses its T=24 right-aligned window; bytegpt grows the window to block.)"""
|
|
1364
|
+
kind, W = decode_load(path)
|
|
1365
|
+
if kind == "clm":
|
|
1366
|
+
# clm_decode_argmax loads-from-path; re-wrap on the already-loaded W path
|
|
1367
|
+
V = W["V"]; T = 24
|
|
1368
|
+
seed_b = seed.encode('utf-8', 'surrogateescape')
|
|
1369
|
+
slen = len(seed_b)
|
|
1370
|
+
tok = np.empty(T, dtype=np.float64)
|
|
1371
|
+
for p in range(T):
|
|
1372
|
+
si = slen - T + p
|
|
1373
|
+
tok[p] = float(seed_b[si]) if si >= 0 else 32.0
|
|
1374
|
+
out = bytearray()
|
|
1375
|
+
for _ in range(gen):
|
|
1376
|
+
logits = _fwd_logits(W, tok, T)
|
|
1377
|
+
row = logits[T - 1]
|
|
1378
|
+
besti = 0; bestv = row[0]
|
|
1379
|
+
for k in range(1, V):
|
|
1380
|
+
if row[k] > bestv:
|
|
1381
|
+
bestv = row[k]; besti = k
|
|
1382
|
+
out.append(besti)
|
|
1383
|
+
tok[:T - 1] = tok[1:]
|
|
1384
|
+
tok[T - 1] = float(besti)
|
|
1385
|
+
return {"ok": True, "text": out.decode('utf-8', 'surrogateescape')}
|
|
1386
|
+
return _decode_argmax_W(W, seed, gen)
|