Pranavz commited on
Commit
beaedc8
·
verified ·
1 Parent(s): 6460845

pylexnet: 827-number Python highlighter network

Browse files
.gitattributes CHANGED
@@ -33,3 +33,4 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ demo.mp4 filter=lfs diff=lfs merge=lfs -text
LICENSE ADDED
@@ -0,0 +1 @@
 
 
1
+ Licensed under the Apache License, Version 2.0: https://www.apache.org/licenses/LICENSE-2.0
README.md ADDED
@@ -0,0 +1,77 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ license: apache-2.0
3
+ tags:
4
+ - syntax-highlighting
5
+ - python
6
+ - hand-designed
7
+ - no-training
8
+ ---
9
+
10
+ # pylexnet
11
+
12
+ A Python syntax highlighter that is a recurrent neural network with **827 numbers and no training data**. Every unit was designed for a job, and the weights were written by construction, not learned.
13
+
14
+ Inspired by [alpindale/lexnet](https://huggingface.co/alpindale/lexnet), which does this for C. Designed with Claude Fable 5.1 and Claude Opus 5.5.
15
+
16
+ <video src="https://huggingface.co/Pranavz/pylexnet/resolve/main/demo.mp4" controls width="100%"></video>
17
+
18
+ The colours in the video are the network's own output.
19
+
20
+ ## Try it
21
+
22
+ ```bash
23
+ git clone https://huggingface.co/Pranavz/pylexnet && cd pylexnet
24
+ cc -O2 -o pylexnet pylexnet.c -lm
25
+ ./pylexnet yourfile.py > /dev/null # default output is ANSI colour; drop the redirect to see it
26
+ ./pylexnet --classes yourfile.py # one class id per byte
27
+ ./pylexnet --interp --classes yourfile.py # generic reference evaluation of the same weights
28
+ ```
29
+
30
+ ## Three builds
31
+
32
+ | Build | Numbers | ns/byte, 1 core | Stdlib held-out: bytes, files exact | Third-party held-out: bytes, files exact |
33
+ |---|---|---|---|---|
34
+ | **main, smallest** | **827** | 94 | 99.996%, 264 of 293 | 99.995%, 3,059 of 3,177 |
35
+ | `builds/fast-994` | 994 (1,502 if its tied threshold weight is counted per row) | **63** | 99.991%, 259 of 293 | 99.9945%, 3,036 of 3,177 |
36
+ | `builds/accurate-7370`, a compiled automaton, not hand-designed | 7,370 | ~89 | ~100.000%, 292 of 293 | **99.9992%, 3,175 of 3,177** |
37
+
38
+ "Numbers" counts every non-zero value the network uses: embedding, weights, biases, clip ceilings, head, initial state.
39
+
40
+ Agreement is per byte against Pygments 2.17.2 `PythonLexer`, over 9 classes: text, keyword, builtin, string, comment, number, function, class, decorator. Stdlib held-out is half the Python 3.12 standard library by path hash (5.8 MB). Third-party held-out is 16 packages, 30 MB, not used for rule fixing. Each was run once per build at the end.
41
+
42
+ **Agreement with Pygments isn't correctness.** On the third-party held-out set, CPython's tokenizer sided with the `accurate-7370` build on all 255 bytes where it differs from Pygments. That audit hasn't been run on the small builds.
43
+
44
+ ## Speed, measured fairly
45
+
46
+ One machine (Intel i5-9400F), one core, `-O2`, the same 293 held-out Python files, best of runs per file, start-up excluded. Ruff is Rust at `opt-level=3`.
47
+
48
+ | Tool | ns/byte | Does |
49
+ |---|---|---|
50
+ | The `accurate-7370` rules as a plain lookup table, no network | 5.5 | Same labels as that build |
51
+ | Ruff parser 0.8.6 | 14 | Full lexing and AST, no highlight classes |
52
+ | `builds/accurate-7370` predecessor | 90.7 | Labels bytes |
53
+ | tree-sitter-python, parse only | 106.7 | Full syntax tree |
54
+ | tree-sitter-python, parse + highlights query | 164.8 | Tree and highlight captures |
55
+ | Pygments `PythonLexer` | 1,153 | Tokens |
56
+
57
+ The three builds above were timed on `argparse.py` (100 KB). The corpus-wide row is an earlier build of the accurate network. This is not the fastest way to lex Python: a plain table and Ruff are both faster, and tree-sitter builds a tree that this doesn't. The point is that a tiny network with no training data can do this job at all.
58
+
59
+ ## How it works
60
+
61
+ - **Embedding:** 18 byte features, including a character code and string-prefix letters.
62
+ - **Forward cell:** about 55 logic units, each designed: inside-string, quote-run counter, triple-quote, f-string and escape flags, one saved outer level for f-string fields with bracket depth, comment, number with exponent, word and length, and `def`/`class`/decorator context.
63
+ - **Vocabulary:** additive retrieval. Solved value tables over byte position sum to a class code for each of 269 keywords, builtins and special method names, in 249 numbers. Ten threshold units read the class windows.
64
+ - **Backward cell:** carries a word's class back over its bytes, because a word's class is only known at its end.
65
+ - **Runtime:** `pylexnet_kernel.h` is the architecture generated as C, like lexnet's. Weight values live only in `pylexnet_weights.bin`. The loader checks a structure hash and guard inequalities. `--interp` runs the generic evaluation of the same weights.
66
+
67
+ `compile.py` regenerates everything: `python3 compile.py --matcher add` for the main build, `--matcher hash --vocab 220` for `fast-994`.
68
+
69
+ ## Limits
70
+
71
+ - **Approximate vocabulary in the main build:** a few non-keywords are flagged, such as `an` and `ad`. That was 185 bytes in a 74.8 MB tuning corpus.
72
+ - **`fast-994` keeps 220 of the words** and is exact on those, ranked by frequency in the tuning corpus.
73
+ - **Missing in the small builds:** soft keywords `match`/`case`, the space in `yield from`, two-level nested f-strings, `{x=}`, and `.5` floats after a dot. These are why their exact-file counts trail the accurate build.
74
+ - **Weights come from a generator script:** the network is designed by hand, unit by unit, but the numbers are produced by `compile.py`, not typed out.
75
+ - **Python 3.12 vocabulary, one Pygments version.** Results don't transfer automatically to other versions.
76
+ - **Speeds are from one machine.** The builds table is one 100 KB file.
77
+ - The tools in `tools/` were written for the development setup and may need paths adjusted.
builds/accurate-7370/compile.py ADDED
@@ -0,0 +1,371 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Compile the rules into exact network weights. No training.
2
+
3
+ The network is a feed-forward stack of sparse blocks with skip connections,
4
+ run once per byte. Input x = [mode one-hot | registers | matcher state | byte one-hot].
5
+ B1 (ReLU) byte class c_k = sum of its bytes; base mode = relu(1 - other modes);
6
+ matcher node = sum of the edges into it (only where that is cheaper)
7
+ B2 (ReLU) AND units: p = relu(mode_q + c_k - 1) (or relu(mode_q - other classes) when
8
+ the cell is large); word category f_w = sum of matcher nodes (or the edges
9
+ into them) that end a word of category w
10
+ B3 (ReLU) AND units: relu(p + register_v [+ f_w] - (arity - 1))
11
+ B4 (ReLU) next mode, register deltas, label and event one-hots, and the word
12
+ matcher: edge = relu(source + byte - 1), where the source is a node unit
13
+ (sum of incoming edges) or the incoming edges themselves; the state carries
14
+ the edge that fired, not a node one-hot. Every B4 output is 0 or 1.
15
+ Backward cell, per byte, on one-hot register r:
16
+ r' = event + relu(r - any_event) + sum over map events m, classes c of relu(r_c + m - 1) e_{M_m(c)}
17
+ Head: class = label + relu(wordlabel + r - 1)
18
+
19
+ The mode, the registers (context, f-string depth and quote, quote kind, triple,
20
+ f-prefix) and the label are one-hot with the most common value encoded as all zeros, so
21
+ nothing fires in the common case. A unit that depends on register values is derived by
22
+ Mobius inversion of the rule table over the register lattice: it only ever conditions on
23
+ non-base values.
24
+ """
25
+ import struct
26
+ from collections import Counter
27
+
28
+ import numpy as np
29
+
30
+ import spec
31
+
32
+ NF = 11
33
+ ORDER, ROWS = spec.build_table()
34
+ IDS = {st: i for i, st in enumerate(ORDER)}
35
+
36
+ # --- 1. factor each rule state into (mode, registers) ----------------------------
37
+ FIELDS = {"code": ("ctx", "fr"), "word": ("ctx", "fr"), "at": ("ctx", "fr"),
38
+ "bang": ("fr",), "bang2": ("fr",), "dot": ("fr",), "cmt": ("fr",),
39
+ "int": ("fr",), "hex": ("fr",), "frac": ("fr",), "exp0": ("fr",), "exp": ("fr",), "numend": ("fr",),
40
+ "q1": ("q", "f", "fr"), "q2": ("q", "f", "fr"),
41
+ "str": ("q", "tr", "close", "f", "fr"), "fopen": ("q", "tr", "fr"), "esc": ("q", "tr", "f", "fr"),
42
+ "escn": ("q", "tr", "f", "fr"), "nname": ("q", "tr", "f", "fr")}
43
+ INMODE = {"close"} # fields that stay part of the mode name
44
+ # The f-string context fr = None | (quote, triple, depth, outer) is factored into three registers:
45
+ # fd (bracket depth, one-hot over 0..3, all zero outside an f-string expression), fq and
46
+ # ftr (the enclosing string's quote and triple), so units condition on single bits.
47
+ BASE = {"ctx": "none", "fd": None, "fq": 34, "ftr": 0, "od": None, "oq": 34, "otr": 0, "q": 34, "tr": 0, "f": False}
48
+ REGS = tuple(BASE)
49
+ BASE_MODE = "code"
50
+ BASE_LABEL = spec.TEXT
51
+
52
+
53
+ def factor(st):
54
+ name, regs = st[0], {}
55
+ for fld, v in zip(FIELDS[st[0]], st[1:]):
56
+ if fld in INMODE:
57
+ name = f"{name}{v}"
58
+ elif fld == "fr":
59
+ if v is not None:
60
+ regs["fq"], regs["ftr"], regs["fd"] = v[:3]
61
+ if v[3] is not None: # the field around this one: nested f-strings
62
+ regs["oq"], regs["otr"], regs["od"] = v[3]
63
+ else:
64
+ regs[fld] = v
65
+ return name, regs
66
+
67
+
68
+ modes, states_of = {}, {}
69
+ rvals = {x: set() for x in REGS}
70
+ for st in ORDER:
71
+ q, regs = factor(st)
72
+ modes.setdefault(q, len(modes))
73
+ states_of.setdefault(q, []).append(st)
74
+ for x, v in regs.items():
75
+ rvals[x].add(v)
76
+ M = len(modes)
77
+ midx = {} # mode -> input column offset; the base mode has none
78
+ for q in modes:
79
+ if q != BASE_MODE:
80
+ midx[q] = len(midx)
81
+ ridx = {}
82
+ for x in REGS:
83
+ for v in sorted(rvals[x] - {BASE[x]}, key=str):
84
+ ridx[(x, v)] = len(ridx)
85
+ R = len(ridx)
86
+
87
+ # --- 2. byte classes: bytes the rules never tell apart -----------------------------
88
+ sig = {}
89
+ bclass = [0] * 256
90
+ for b in range(256):
91
+ key = tuple(ROWS[s][f][b] for s in range(len(ORDER)) for f in range(NF))
92
+ bclass[b] = sig.setdefault(key, len(sig))
93
+ C = len(sig)
94
+ member = {}
95
+ for b, k in enumerate(bclass):
96
+ member.setdefault(k, b)
97
+
98
+ # --- 3. minimise the word matcher: merge trie nodes with identical futures ---------
99
+ T, TERM = spec.TRIE, spec.TERM
100
+ cls = {n: TERM.get(n, spec.F_NONE) for n in range(len(T))}
101
+ while True:
102
+ key = {n: (cls[n], tuple(sorted((b, cls[c]) for b, c in T[n].items()))) for n in range(len(T))}
103
+ new_ids = {}
104
+ nxt = {n: new_ids.setdefault(key[n], len(new_ids)) for n in range(len(T))}
105
+ if len(new_ids) == len(set(cls.values())):
106
+ break
107
+ cls = nxt
108
+ order = sorted(set(cls.values()), key=lambda c: c != cls[0]) # root first
109
+ node_id = {c: i for i, c in enumerate(order)}
110
+ N = len(order)
111
+ EDGES = sorted({(node_id[cls[n]], b, node_id[cls[c]]) for n in range(len(T)) for b, c in T[n].items()})
112
+ NTERM = {}
113
+ for n, w in TERM.items():
114
+ NTERM[node_id[cls[n]]] = w
115
+
116
+ # --- 3b. matcher state = the edge that fired last. An edge unit is an AND of its byte(s)
117
+ # and its source; the source is either a materialised node unit (sum of its incoming
118
+ # edges) or, when that is cheaper, the incoming edges themselves. Edges with the same
119
+ # source and target share one unit, since their bytes are mutually exclusive.
120
+ EBYTES = {}
121
+ for i, b, c in EDGES:
122
+ EBYTES.setdefault((i, c), []).append(b)
123
+ EUNITS = sorted(EBYTES)
124
+ IN_OF, OUT_OF = {}, {}
125
+ for i, c in EUNITS:
126
+ IN_OF.setdefault(c, []).append((i, c))
127
+ OUT_OF.setdefault(i, []).append((i, c))
128
+ MAT = set() # no node units: a state-derived hop before the edges costs more time than weights
129
+ SLOT = {("node", 0): 0}
130
+ for e in EUNITS:
131
+ SLOT[("edge",) + e] = len(SLOT)
132
+ NS = len(SLOT)
133
+
134
+
135
+ def node_cols(n):
136
+ """Columns whose sum is the one-hot "matcher is at node n"."""
137
+ if n == 0:
138
+ return [oN + SLOT[("node", 0)]]
139
+ if n in MAT:
140
+ return [gidx(B1, NODE_ROW[n])]
141
+ return [oN + SLOT[("edge",) + e] for e in IN_OF.get(n, [])]
142
+
143
+
144
+ # --- layout -----------------------------------------------------------------------
145
+ oM, oR, oN = 0, len(midx), len(midx) + R
146
+ NSTATE = oN + NS
147
+ oB = NSTATE
148
+ NIN = oB + 256
149
+ LAB, EV = 10, 9 + len(spec.EV_MAP)
150
+ oL, oE = NSTATE, NSTATE + LAB
151
+ NOUT = oE + EV
152
+
153
+
154
+ class Block:
155
+ def __init__(self, relu):
156
+ self.relu, self.rows, self.bias, self.entries = relu, 0, [], []
157
+
158
+ def unit(self, inputs, bias=0.0):
159
+ """inputs: list of (global column, weight)."""
160
+ i = self.rows
161
+ self.rows += 1
162
+ self.bias.append(bias)
163
+ for j, w in inputs:
164
+ self.entries.append((i, j, w))
165
+ return i
166
+
167
+
168
+ B1, B2, B3, B4 = Block(1), Block(1), Block(1), Block(1)
169
+ blocks = [B1, B2, B3, B4]
170
+ base_of = {}
171
+
172
+
173
+ def begin(blk):
174
+ """Fix the block's place in the global index space: inputs, then blocks in order.
175
+ Called once the previous blocks are complete."""
176
+ base_of[id(blk)] = NIN + sum(b.rows for b in blocks[:blocks.index(blk)])
177
+
178
+
179
+ def gidx(blk, i):
180
+ return base_of[id(blk)] + i
181
+
182
+
183
+ # --- B1: byte classes, base mode, materialised matcher nodes ----------------------
184
+ begin(B1)
185
+ CLS_ROW = {}
186
+ for k in range(C):
187
+ CLS_ROW[k] = B1.unit([(oB + b, 1.0) for b in range(256) if bclass[b] == k])
188
+ WORD_CLS = sorted({bclass[b] for b in spec.WORDCH}) # classes never mix word and non-word bytes
189
+ MODE0 = B1.unit([(oM + i, -1.0) for i in range(len(midx))], 1.0)
190
+ NODE_ROW = {}
191
+ for n in sorted(MAT):
192
+ NODE_ROW[n] = B1.unit([(oN + SLOT[("edge",) + e], 1.0) for e in IN_OF[n]])
193
+ begin(B2)
194
+ FLAG_ROW = {}
195
+ for w in range(1, NF):
196
+ FLAG_ROW[w] = B2.unit([(j, 1.0) for i, t in sorted(NTERM.items()) if t == w for j in node_cols(i)])
197
+
198
+
199
+ def mode_col(q):
200
+ return gidx(B1, MODE0) if q == BASE_MODE else oM + midx[q]
201
+
202
+
203
+ # --- the rule table on factored states --------------------------------------------
204
+
205
+
206
+ def outcome(st, f, k):
207
+ """Output vector for (state, word category, byte class): {output row: weight}."""
208
+ n, lab, ev = ROWS[IDS[st]][f][member[k]]
209
+ q2, regs2 = factor(ORDER[n])
210
+ _, regs = factor(st)
211
+ vec = Counter()
212
+ if q2 != BASE_MODE:
213
+ vec[oM + midx[q2]] = 1.0
214
+ if lab != BASE_LABEL:
215
+ vec[oL + lab] = 1.0
216
+ if ev >= 0:
217
+ vec[oE + ev] = 1.0
218
+ for x in REGS:
219
+ old, new = regs.get(x, BASE[x]), regs2.get(x, BASE[x])
220
+ if old != new:
221
+ if new != BASE[x]:
222
+ vec[oR + ridx[(x, new)]] += 1.0
223
+ if old != BASE[x]:
224
+ vec[oR + ridx[(x, old)]] -= 1.0
225
+ return tuple(sorted((r, v) for r, v in vec.items() if v))
226
+
227
+
228
+ def nonbase(st, f):
229
+ _, regs = factor(st)
230
+ t = {(x, v) for x, v in regs.items() if v != BASE[x]}
231
+ if f != spec.F_NONE:
232
+ t.add(("flag", f))
233
+ return frozenset(t)
234
+
235
+
236
+ def mobius(F):
237
+ """F: {set of non-base conditions: vector}. Returns corrections g with
238
+ F[T] = sum of g[U] over U subset of T, using only conditions that occur."""
239
+ g = {}
240
+ for Tset in sorted(F, key=len):
241
+ pred = Counter()
242
+ for U, gu in g.items():
243
+ if U < Tset:
244
+ pred.update(dict(gu))
245
+ d = Counter(dict(F[Tset]))
246
+ d.subtract(pred)
247
+ d = tuple(sorted((r, v) for r, v in d.items() if v))
248
+ if d:
249
+ g[Tset] = d
250
+ return g
251
+
252
+
253
+ def cond_col(c):
254
+ x, v = c
255
+ return gidx(B2, FLAG_ROW[v]) if x == "flag" else oR + ridx[(x, v)]
256
+
257
+
258
+ # --- cells: for each mode, the byte classes it treats alike; Mobius corrections ----
259
+ CELLS, PLAN = {}, {}
260
+ for q in modes:
261
+ cells = {}
262
+ for k in range(C):
263
+ s = tuple(outcome(st, f, k) for st in states_of[q] for f in range(NF))
264
+ cells.setdefault(s, []).append(k)
265
+ CELLS[q] = cells
266
+ for s, ks in cells.items():
267
+ F = {}
268
+ for st in states_of[q]:
269
+ for f in range(NF):
270
+ F[nonbase(st, f)] = outcome(st, f, ks[0])
271
+ PLAN[(q, tuple(ks))] = mobius(F)
272
+
273
+ # --- B2: mode AND units p = relu(mode + class - 1), only for cells that do something --
274
+ P_ROW = {}
275
+ for q in modes:
276
+ cells = CELLS[q]
277
+ for s, ks in cells.items():
278
+ if not PLAN[(q, tuple(ks))]:
279
+ continue
280
+ # p is a global column: the mode itself when it has one cell; otherwise an AND
281
+ # unit over the cell's classes, or over the complement when that is smaller:
282
+ # relu(mode - sum of other classes) is the same function.
283
+ if len(cells) == 1:
284
+ P_ROW[(q, tuple(ks))] = mode_col(q)
285
+ elif len(ks) <= C - len(ks):
286
+ P_ROW[(q, tuple(ks))] = gidx(B2, B2.unit([(mode_col(q), 1.0)] + [(gidx(B1, CLS_ROW[k]), 1.0) for k in ks], -1.0))
287
+ else:
288
+ P_ROW[(q, tuple(ks))] = gidx(B2, B2.unit([(mode_col(q), 1.0)] + [(gidx(B1, CLS_ROW[k]), -1.0) for k in range(C) if k not in ks]))
289
+ begin(B3)
290
+
291
+ # --- B3: register-conditioned corrections, shared between cells with the same one --
292
+ cond_units = {} # (conditions, correction vector) -> list of p columns feeding it
293
+ for (q, ks), g in PLAN.items():
294
+ if not g:
295
+ continue
296
+ p = P_ROW[(q, ks)]
297
+ for Tset, gv in g.items():
298
+ if not Tset:
299
+ for r, v in gv:
300
+ B4.entries.append((r, p, v))
301
+ else:
302
+ cond_units.setdefault((tuple(sorted(Tset, key=str)), gv), []).append(p)
303
+ for (conds, gv), ps in cond_units.items():
304
+ u = B3.unit([(p, 1.0) for p in ps] + [(cond_col(c), 1.0) for c in conds], -float(len(conds)))
305
+ for r, v in gv:
306
+ B4.entries.append((r, gidx(B3, u), v))
307
+ begin(B4)
308
+
309
+ # --- B4: outputs. Registers pass through unless a unit changes them. Matcher: edge units
310
+ # (AND of source and byte); the root is on after a non-word byte.
311
+ for i in range(R):
312
+ B4.entries.append((oR + i, oR + i, 1.0))
313
+ B4.rows = NOUT
314
+ B4.bias = [0.0] * NOUT
315
+ B4.bias[oN + 0] = 1.0
316
+ for k in WORD_CLS:
317
+ B4.entries.append((oN + 0, gidx(B1, CLS_ROW[k]), -1.0))
318
+ for i, c in EUNITS:
319
+ r = oN + SLOT[("edge", i, c)]
320
+ B4.bias[r] = -1.0
321
+ for j in node_cols(i):
322
+ B4.entries.append((r, j, 1.0))
323
+ for b in EBYTES[(i, c)]:
324
+ B4.entries.append((r, oB + b, 1.0))
325
+
326
+
327
+ # --- pack -------------------------------------------------------------------------
328
+ def dedupe(entries):
329
+ acc = {}
330
+ for i, j, v in entries:
331
+ acc[(i, j)] = acc.get((i, j), 0.0) + v
332
+ return [(i, j, v) for (i, j), v in acc.items() if v != 0.0]
333
+
334
+
335
+ def pack(blk, rbase):
336
+ """Column-compressed over the global columns this block reads."""
337
+ ent = sorted(dedupe(blk.entries), key=lambda e: (e[1], e[0]))
338
+ lo = min(j for _, j, _ in ent)
339
+ hi = max(j for _, j, _ in ent) + 1
340
+ ptr = np.zeros(hi - lo + 1, dtype=np.int32)
341
+ for _, j, _ in ent:
342
+ ptr[j - lo + 1] += 1
343
+ ptr = np.cumsum(ptr).astype(np.int32)
344
+ idx = np.array([e[0] for e in ent], dtype=np.int32)
345
+ val = np.array([e[2] for e in ent], dtype=np.float32)
346
+ head = struct.pack("<6i", blk.rows, lo, hi, len(ent), blk.relu, rbase)
347
+ return len(ent), head + ptr.tobytes() + idx.tobytes() + val.tobytes() + np.asarray(blk.bias, dtype=np.float32).tobytes()
348
+
349
+
350
+ start_mode, start_regs = factor(spec.START)
351
+ init = ([oM + midx[start_mode]] if start_mode != BASE_MODE else []) \
352
+ + [oR + ridx[(x, v)] for x, v in start_regs.items() if v != BASE[x]] + [oN + 0]
353
+ BOUND = 1 # every activation in this network is 0 or 1
354
+ header = struct.pack("<10i", NIN, NSTATE, len(blocks), len(init), oL, LAB, oE, EV, oB, BOUND) + np.array(init, dtype=np.int32).tobytes() \
355
+ + np.array(spec.EV_MAP, dtype=np.int32).tobytes()
356
+ blob = header
357
+ nnz = 0
358
+ per = []
359
+ rbase = NIN
360
+ for blk in blocks:
361
+ assert base_of[id(blk)] == rbase
362
+ n, packed = pack(blk, rbase)
363
+ nnz += n
364
+ per.append(n)
365
+ blob += packed
366
+ rbase += blk.rows
367
+ open("pylexnet_weights.bin", "wb").write(blob)
368
+ print(f"modes {M}, registers {R}, byte classes {C}, matcher nodes {N} ({len(EDGES)} edges, {len(EUNITS)} edge units, {len(MAT)} node units)")
369
+ print(f"AND units: mode {B2.rows - (NF - 1)}, conditioned {B3.rows}, matcher {len(EUNITS)}")
370
+ print(f"non-zero weights {nnz} (blocks {per}), rows {[b.rows for b in blocks]}")
371
+ print(f"weights file {len(blob) / 1024:.0f} KiB")
builds/accurate-7370/pylexnet.c ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* pylexnet: a recurrent network with hand-compiled weights that classifies Python bytes.
2
+ * This runtime knows nothing about Python: it only does sparse matrix products and ReLU.
3
+ *
4
+ * The forward cell is a stack of sparse blocks with skip connections over one global
5
+ * activation vector: inputs [state | byte one-hot], then each block's rows in order.
6
+ * Block k computes y_k = act(W_k a + b_k) where a is everything computed so far.
7
+ * The last block's first rows are the next state; then the label and event one-hots.
8
+ *
9
+ * Two kernels compute the same products:
10
+ * --reference the plain one: scatter every active column, track touched rows with flags.
11
+ * default splits the columns of every block into "known" and "unknown" ones.
12
+ * The byte one-hot columns are known: the runtime builds that input itself,
13
+ * so their values given the byte are known before any weight is read. A row
14
+ * whose every entry sits in known columns is a pure function of the byte, so
15
+ * its column is known too (B1's byte-class rows, for instance). At load time
16
+ * the kernel evaluates, for each of the 256 bytes, the known part of every
17
+ * row: K_i(b) = sum of w_ij * a_j(b) over known j, and act(b_i + K_i(b)).
18
+ * Per byte it then scatters only the unknown columns (the state and the rows
19
+ * derived from it), and completes each touched row with its K_i(b). Rows not
20
+ * touched by any unknown column produce act(b_i + K_i(b)), which is the
21
+ * precomputed value; the ones where it is non-zero are emitted from a per-byte
22
+ * "loud" list. Every row still receives exactly W_i . a + b_i; the split only
23
+ * moves the one-hot part of the product to load time.
24
+ * On top of that, dead-row pruning: the weights file declares a bound U on every
25
+ * activation. For byte b, a ReLU row whose bias + K_i(b) + (largest possible
26
+ * contribution of its unknown columns under U) is still <= 0 is provably zero, so
27
+ * its entries are left out of that byte's scatter, and its own column counts as
28
+ * zero for the rows downstream. The kernel checks every activation it produces
29
+ * against U; if one ever exceeds it, the file is redone with --reference. Nothing
30
+ * here depends on the state: the state still flows through every block's weights. */
31
+ #include <math.h>
32
+ #include <stdint.h>
33
+ #include <stdio.h>
34
+ #include <stdlib.h>
35
+ #include <string.h>
36
+ #include <time.h>
37
+
38
+ typedef struct { int i; float w; } Ent;
39
+ typedef struct { int c; float v; } Act;
40
+ typedef struct {
41
+ int rows, lo, hi, nnz, relu, base; /* columns [lo, hi) of the global vector; rows live at base */
42
+ const int *ptr, *idx; const float *val, *bias;
43
+ int *touched, *brow, nb; char *seen; float *y;
44
+ /* fast kernel */
45
+ int *uptr, *uidx; float *uval; /* CSC over the unknown columns only */
46
+ int *kptr, *kcol; float *kval; /* CSR, per row, of its entries in known columns */
47
+ int *lptr, *lrow; float *lval; /* per byte: rows with act(bias + K_i(b)) != 0, and that value */
48
+ signed char *ktab; int hask; /* K_i(b) as a dense int8 table [b * rows + i] when it fits, for rows touched by unknown columns */
49
+ int nvar, *vmap; /* per byte: which pruned CSC variant to scatter with */
50
+ int **vptr, **vidx; float **vval; /* pruned CSC variants: only entries of rows that can be non-zero for that byte */
51
+ Ent **vent; /* the same entries interleaved (row, weight); vptr has a sentinel empty column at index w */
52
+ } Block;
53
+ static const unsigned char *P;
54
+ static const void *take(size_t n) { const void *r = P; P += n; return r; }
55
+ static void load(Block *l) {
56
+ const int *h = take(24);
57
+ l->rows = h[0]; l->lo = h[1]; l->hi = h[2]; l->nnz = h[3]; l->relu = h[4]; l->base = h[5];
58
+ l->ptr = take(4 * (size_t)(l->hi - l->lo + 1));
59
+ l->idx = take(4 * (size_t)l->nnz);
60
+ l->val = take(4 * (size_t)l->nnz);
61
+ l->bias = take(4 * (size_t)l->rows);
62
+ l->touched = malloc(sizeof(int) * (size_t)(l->rows + l->nnz));
63
+ l->brow = malloc(sizeof(int) * (size_t)l->rows);
64
+ l->seen = calloc((size_t)l->rows, 1);
65
+ l->y = malloc(sizeof(float) * (size_t)l->rows);
66
+ memcpy(l->y, l->bias, sizeof(float) * (size_t)l->rows);
67
+ l->nb = 0;
68
+ for (int i = 0; i < l->rows; i++) if (l->bias[i] > 0 || (!l->relu && l->bias[i] != 0)) l->brow[l->nb++] = i;
69
+ }
70
+ static inline float act(int relu, float v) { return relu && v < 0 ? 0 : v; }
71
+
72
+ /* Known-column analysis. known[j] for every global column; kv[b][...] sparse lists of the
73
+ * known columns' values for byte b. */
74
+ static char *g_known;
75
+ static int *g_kvptr, *g_kvcol; static float *g_kvval; static int g_kvn, g_kvcap;
76
+ static int g_needkv;
77
+ static float g_U; /* declared |activation| bound */
78
+ static float g_vmax; /* largest |activation| the fast kernel has produced */
79
+ static char *g_nonneg; /* column is a ReLU output */
80
+ static char *g_dead; /* per byte, columns that are provably zero (dead rows of earlier blocks) */
81
+ static long long g_tb[16]; /* touched rows per block (bench) */
82
+ static float *g_kv; /* dense scratch over the global vector: values of known columns for the current byte */
83
+ static void kv_push(int col, float v) {
84
+ if (g_kvn == g_kvcap) { g_kvcap = g_kvcap ? 2 * g_kvcap : 4096; g_kvcol = realloc(g_kvcol, sizeof(int) * (size_t)g_kvcap); g_kvval = realloc(g_kvval, sizeof(float) * (size_t)g_kvcap); }
85
+ g_kvcol[g_kvn] = col; g_kvval[g_kvn++] = v;
86
+ }
87
+ static void specialise(Block *L, int NB, int total, int oB) {
88
+ g_known = calloc((size_t)total, 1);
89
+ g_nonneg = calloc((size_t)total, 1);
90
+ g_dead = calloc((size_t)total * 256, 1); /* [b * total + col]: column provably zero for byte b */
91
+ g_kv = calloc((size_t)total, sizeof(float));
92
+ for (int b = 0; b < 256; b++) g_known[oB + b] = 1;
93
+ /* per byte lists of known values; byte columns first */
94
+ /* temporary per-byte dynamic lists for known values, built block by block */
95
+ int *tcap = calloc(256, sizeof(int)), *tn = calloc(256, sizeof(int));
96
+ int **tcol = calloc(256, sizeof(int *)); float **tval = calloc(256, sizeof(float *));
97
+ for (int b = 0; b < 256; b++) { tcap[b] = 64; tcol[b] = malloc(sizeof(int) * 64); tval[b] = malloc(sizeof(float) * 64); tcol[b][0] = oB + b; tval[b][0] = 1; tn[b] = 1; }
98
+ #define TPUSH(b, c, v) do { if (tn[b] == tcap[b]) { tcap[b] *= 2; tcol[b] = realloc(tcol[b], sizeof(int) * (size_t)tcap[b]); tval[b] = realloc(tval[b], sizeof(float) * (size_t)tcap[b]); } tcol[b][tn[b]] = (c); tval[b][tn[b]++] = (v); } while (0)
99
+ for (int k = 0; k < NB; k++) {
100
+ Block *l = &L[k];
101
+ int w = l->hi - l->lo;
102
+ /* row degrees in known columns, and whether the row has any unknown entry */
103
+ int *kdeg = calloc((size_t)l->rows, sizeof(int));
104
+ char *hasu = calloc((size_t)l->rows, 1);
105
+ int nu = 0;
106
+ for (int j = 0; j < w; j++)
107
+ for (int e = l->ptr[j]; e < l->ptr[j + 1]; e++) {
108
+ if (g_known[l->lo + j]) kdeg[l->idx[e]]++; else { hasu[l->idx[e]] = 1; nu++; }
109
+ }
110
+ /* CSC over unknown columns */
111
+ l->uptr = malloc(sizeof(int) * (size_t)(w + 1)); l->uidx = malloc(sizeof(int) * (size_t)(nu + 1)); l->uval = malloc(sizeof(float) * (size_t)(nu + 1));
112
+ /* CSR over known columns */
113
+ l->kptr = malloc(sizeof(int) * (size_t)(l->rows + 1));
114
+ l->kptr[0] = 0;
115
+ for (int i = 0; i < l->rows; i++) l->kptr[i + 1] = l->kptr[i] + kdeg[i];
116
+ l->kcol = malloc(sizeof(int) * (size_t)(l->kptr[l->rows] + 1)); l->kval = malloc(sizeof(float) * (size_t)(l->kptr[l->rows] + 1));
117
+ int *fill = calloc((size_t)l->rows, sizeof(int));
118
+ nu = 0;
119
+ for (int j = 0; j < w; j++) {
120
+ l->uptr[j] = nu;
121
+ for (int e = l->ptr[j]; e < l->ptr[j + 1]; e++) {
122
+ int i = l->idx[e];
123
+ if (g_known[l->lo + j]) { l->kcol[l->kptr[i] + fill[i]] = l->lo + j; l->kval[l->kptr[i] + fill[i]++] = l->val[e]; }
124
+ else { l->uidx[nu] = i; l->uval[nu++] = l->val[e]; }
125
+ }
126
+ }
127
+ l->uptr[w] = nu;
128
+ /* rows with no unknown entry are known columns themselves */
129
+ for (int i = 0; i < l->rows; i++) if (!hasu[i]) g_known[l->base + i] = 1;
130
+ /* per byte: K_i(b) for every row with known entries; loud rows; values of known rows */
131
+ l->lptr = malloc(sizeof(int) * 257);
132
+ int lcap = 1024, ln = 0;
133
+ l->lrow = malloc(sizeof(int) * (size_t)lcap); l->lval = malloc(sizeof(float) * (size_t)lcap);
134
+ for (int b = 0; b < 256; b++) {
135
+ l->lptr[b] = ln;
136
+ for (int a = 0; a < tn[b]; a++) g_kv[tcol[b][a]] = tval[b][a];
137
+ for (int i = 0; i < l->rows; i++) {
138
+ float K = 0;
139
+ for (int e = l->kptr[i]; e < l->kptr[i + 1]; e++) K += l->kval[e] * g_kv[l->kcol[e]];
140
+ float v = act(l->relu, l->bias[i] + K);
141
+ if (!hasu[i] && v != 0) TPUSH(b, l->base + i, v);
142
+ if (v != 0 && (hasu[i] || k == NB - 1)) { /* known rows are consumed through K only; last block's are the outputs */
143
+ if (ln == lcap) { lcap *= 2; l->lrow = realloc(l->lrow, sizeof(int) * (size_t)lcap); l->lval = realloc(l->lval, sizeof(float) * (size_t)lcap); }
144
+ l->lrow[ln] = i; l->lval[ln++] = v;
145
+ }
146
+ }
147
+ for (int a = 0; a < tn[b]; a++) g_kv[tcol[b][a]] = 0;
148
+ }
149
+ l->lptr[256] = ln;
150
+ /* dense int8 table of K_i(b) for rows that mix known and unknown entries */
151
+ l->hask = 0; l->ktab = NULL;
152
+ for (int i = 0; i < l->rows; i++) if (hasu[i] && kdeg[i]) l->hask = 1;
153
+ if (l->hask) {
154
+ int ok = 1;
155
+ signed char *tab = calloc((size_t)l->rows * 256, 1);
156
+ for (int b = 0; b < 256 && ok; b++) {
157
+ for (int a = 0; a < tn[b]; a++) g_kv[tcol[b][a]] = tval[b][a];
158
+ for (int i = 0; i < l->rows; i++) {
159
+ if (!hasu[i]) continue;
160
+ float K = 0;
161
+ for (int e = l->kptr[i]; e < l->kptr[i + 1]; e++) K += l->kval[e] * g_kv[l->kcol[e]];
162
+ if (K != (float)(int)K || K < -127 || K > 127) ok = 0;
163
+ tab[(size_t)b * l->rows + i] = (signed char)(int)K;
164
+ }
165
+ for (int a = 0; a < tn[b]; a++) g_kv[tcol[b][a]] = 0;
166
+ }
167
+ if (ok) l->ktab = tab; else free(tab);
168
+ }
169
+ /* dead-row pruning: for byte b, row i can only be non-zero if
170
+ * bias_i + K_i(b) + sum over unknown entries of (max contribution under the bound) > 0 (ReLU rows)
171
+ * Entries of rows that cannot be non-zero are left out of the scatter for that byte. The
172
+ * pruned column structures are deduplicated across bytes. */
173
+ {
174
+ int *aptr = malloc(sizeof(int) * (size_t)(w + 1)), *aidx = malloc(sizeof(int) * (size_t)(nu + 1));
175
+ float *aval = malloc(sizeof(float) * (size_t)(nu + 1));
176
+ float *slack = malloc(sizeof(float) * (size_t)l->rows); /* max unknown contribution per row, for this byte */
177
+ char *alive = malloc((size_t)l->rows);
178
+ l->nvar = 0; l->vmap = malloc(sizeof(int) * 256);
179
+ l->vptr = malloc(sizeof(int *) * 256); l->vidx = malloc(sizeof(int *) * 256); l->vval = malloc(sizeof(float *) * 256);
180
+ l->vent = malloc(sizeof(Ent *) * 256);
181
+ for (int b = 0; b < 256; b++) {
182
+ const char *db = g_dead + (size_t)b * total;
183
+ for (int i = 0; i < l->rows; i++) slack[i] = 0;
184
+ for (int j = 0; j < w; j++) {
185
+ if (db[l->lo + j]) continue; /* a column that is provably zero for this byte contributes nothing */
186
+ for (int e = l->uptr[j]; e < l->uptr[j + 1]; e++) {
187
+ float wv = l->uval[e];
188
+ slack[l->uidx[e]] += (g_nonneg[l->lo + j] ? (wv > 0 ? wv : 0) : (wv < 0 ? -wv : wv)) * g_U;
189
+ }
190
+ }
191
+ for (int a = 0; a < tn[b]; a++) g_kv[tcol[b][a]] = tval[b][a];
192
+ for (int i = 0; i < l->rows; i++) {
193
+ float K = 0;
194
+ for (int e = l->kptr[i]; e < l->kptr[i + 1]; e++) K += l->kval[e] * g_kv[l->kcol[e]];
195
+ alive[i] = l->relu ? (l->bias[i] + K + slack[i] > 0) : 1;
196
+ if (!alive[i] && hasu[i]) g_dead[(size_t)b * total + l->base + i] = 1;
197
+ }
198
+ for (int a = 0; a < tn[b]; a++) g_kv[tcol[b][a]] = 0;
199
+ int na = 0;
200
+ for (int j = 0; j < w; j++) {
201
+ aptr[j] = na;
202
+ if (db[l->lo + j]) continue;
203
+ for (int e = l->uptr[j]; e < l->uptr[j + 1]; e++)
204
+ if (alive[l->uidx[e]]) { aidx[na] = l->uidx[e]; aval[na++] = l->uval[e]; }
205
+ }
206
+ aptr[w] = na;
207
+ int v;
208
+ for (v = 0; v < l->nvar; v++)
209
+ if (l->vptr[v][w] == na && !memcmp(l->vptr[v], aptr, sizeof(int) * (size_t)(w + 1)) &&
210
+ !memcmp(l->vidx[v], aidx, sizeof(int) * (size_t)na) && !memcmp(l->vval[v], aval, sizeof(float) * (size_t)na)) break;
211
+ if (v == l->nvar) {
212
+ l->vptr[v] = malloc(sizeof(int) * (size_t)(w + 2)); memcpy(l->vptr[v], aptr, sizeof(int) * (size_t)(w + 1));
213
+ l->vptr[v][w + 1] = na; /* sentinel: column w is empty, used for columns outside [lo, hi) */
214
+ l->vent[v] = malloc(sizeof(Ent) * (size_t)(na + 1));
215
+ for (int e = 0; e < na; e++) { l->vent[v][e].i = aidx[e]; l->vent[v][e].w = aval[e]; }
216
+ l->vidx[v] = malloc(sizeof(int) * (size_t)(na + 1)); memcpy(l->vidx[v], aidx, sizeof(int) * (size_t)na);
217
+ l->vval[v] = malloc(sizeof(float) * (size_t)(na + 1)); memcpy(l->vval[v], aval, sizeof(float) * (size_t)na);
218
+ l->nvar++;
219
+ }
220
+ l->vmap[b] = v;
221
+ }
222
+ free(aptr); free(aidx); free(aval); free(slack); free(alive);
223
+ }
224
+ if (l->relu) for (int i = 0; i < l->rows; i++) g_nonneg[l->base + i] = 1;
225
+ free(kdeg); free(hasu); free(fill);
226
+ }
227
+ g_kvptr = malloc(sizeof(int) * 257);
228
+ for (int b = 0; b < 256; b++) { g_kvptr[b] = g_kvn; for (int a = 0; a < tn[b]; a++) kv_push(tcol[b][a], tval[b][a]); }
229
+ g_kvptr[256] = g_kvn;
230
+ g_needkv = 0;
231
+ for (int k = 0; k < NB; k++) if (L[k].hask && !L[k].ktab) g_needkv = 1;
232
+ for (int b = 0; b < 256; b++) { free(tcol[b]); free(tval[b]); }
233
+ free(tcol); free(tval); free(tn); free(tcap);
234
+ }
235
+
236
+ static long long g_touched;
237
+ static Block *g_L;
238
+ static int g_ref;
239
+ /* y = act(W a + b), visiting only the non-zero entries of a. Appends the non-zero rows
240
+ * (as global indices) to the active list. */
241
+ static int apply_ref(Block *l, const int *in, const float *inv, int nin, int *out, float *outv) {
242
+ float *y = l->y;
243
+ /* y holds the bias outside this call; touched rows are restored afterwards */
244
+ int nt = 0;
245
+ for (int a = 0; a < nin; a++) {
246
+ int j = in[a] - l->lo;
247
+ if (j < 0 || j >= l->hi - l->lo) continue;
248
+ float v = inv[a];
249
+ for (int e = l->ptr[j]; e < l->ptr[j + 1]; e++) {
250
+ int i = l->idx[e];
251
+ if (!l->seen[i]) { l->seen[i] = 1; l->touched[nt++] = i; }
252
+ y[i] += l->val[e] * v;
253
+ }
254
+ }
255
+ for (int a = 0; a < l->nb; a++) {
256
+ int i = l->brow[a];
257
+ if (!l->seen[i]) { l->seen[i] = 1; l->touched[nt++] = i; }
258
+ }
259
+ g_touched += nt;
260
+ int n = 0;
261
+ for (int a = 0; a < nt; a++) {
262
+ int i = l->touched[a];
263
+ float v = y[i];
264
+ if (l->relu && v < 0) v = 0;
265
+ if (v != 0) { out[n] = l->base + i; outv[n++] = v; }
266
+ y[i] = l->bias[i]; l->seen[i] = 0;
267
+ }
268
+ return n;
269
+ }
270
+
271
+ /* Fast kernel. y rests at the bias; a row is touched iff y != bias (a row equal to its bias
272
+ * yields exactly what an untouched row yields, so duplicates in the touched list and
273
+ * cancellations are harmless). The byte's known values are in g_kv. */
274
+ static inline int apply(Block *restrict l, int b, const Act *restrict in, int nin, Act *restrict out) {
275
+ float *restrict y = l->y;
276
+ const float *restrict bias = l->bias;
277
+ int var = l->vmap[b];
278
+ const int *restrict ptr = l->vptr[var];
279
+ const Ent *restrict ent = l->vent[var];
280
+ int *restrict tl = l->touched;
281
+ int nt = 0, lo = l->lo, w = l->hi - l->lo, base = l->base, relu = l->relu;
282
+ float vmax = g_vmax;
283
+ for (int a = 0; a < nin; a++) {
284
+ unsigned j = (unsigned)(in[a].c - lo);
285
+ j = j < (unsigned)w ? j : (unsigned)w; /* outside the block's columns: the empty sentinel column */
286
+ float v = in[a].v;
287
+ int e = ptr[j], ee = ptr[j + 1];
288
+ for (; e < ee; e++) { int i = ent[e].i; y[i] += ent[e].w * v; tl[nt++] = i; }
289
+ }
290
+ g_touched += nt; g_tb[l - g_L] += nt;
291
+ int n = 0;
292
+ /* rows that are non-zero from their known part alone, unless an unknown column touched them */
293
+ for (int a = l->lptr[b], ae = l->lptr[b + 1]; a < ae; a++) {
294
+ int i = l->lrow[a];
295
+ float v = l->lval[a];
296
+ out[n].c = base + i; out[n].v = v; n += y[i] == bias[i]; vmax = fabsf(v) > vmax ? fabsf(v) : vmax;
297
+ }
298
+ if (!l->hask) {
299
+ if (relu) {
300
+ for (int a = 0; a < nt; a++) {
301
+ int i = tl[a];
302
+ float v = y[i], bi = bias[i]; y[i] = bi;
303
+ v = (v != bi) ? v : 0;
304
+ out[n].c = base + i; out[n].v = v; n += v > 0; vmax = v > vmax ? v : vmax;
305
+ }
306
+ } else {
307
+ for (int a = 0; a < nt; a++) {
308
+ int i = tl[a];
309
+ float v = y[i], bi = bias[i]; y[i] = bi;
310
+ v = (v != bi) ? v : 0;
311
+ out[n].c = base + i; out[n].v = v; n += v != 0; vmax = fabsf(v) > vmax ? fabsf(v) : vmax;
312
+ }
313
+ }
314
+ } else if (l->ktab) {
315
+ const signed char *restrict kt = l->ktab + (size_t)b * l->rows;
316
+ if (relu) {
317
+ for (int a = 0; a < nt; a++) {
318
+ int i = tl[a];
319
+ float v = y[i], bi = bias[i]; y[i] = bi;
320
+ v = (v != bi) ? v + (float)kt[i] : 0; /* at rest: contributes nothing here (loud list covers it) */
321
+ out[n].c = base + i; out[n].v = v; n += v > 0; vmax = v > vmax ? v : vmax;
322
+ }
323
+ } else {
324
+ for (int a = 0; a < nt; a++) {
325
+ int i = tl[a];
326
+ float v = y[i], bi = bias[i]; y[i] = bi;
327
+ v = (v != bi) ? v + (float)kt[i] : 0;
328
+ out[n].c = base + i; out[n].v = v; n += v != 0; vmax = fabsf(v) > vmax ? fabsf(v) : vmax;
329
+ }
330
+ }
331
+ } else {
332
+ const int *restrict kptr = l->kptr, *restrict kcol = l->kcol;
333
+ const float *restrict kval = l->kval, *restrict kv = g_kv;
334
+ for (int a = 0; a < nt; a++) {
335
+ int i = tl[a];
336
+ float v = y[i], bi = bias[i];
337
+ if (v != bi) {
338
+ y[i] = bi;
339
+ for (int e = kptr[i], ee = kptr[i + 1]; e < ee; e++) v += kval[e] * kv[kcol[e]];
340
+ if (relu && v < 0) v = 0;
341
+ if (v != 0) { out[n].c = base + i; out[n].v = v; n++; if (fabsf(v) > vmax) vmax = fabsf(v); }
342
+ }
343
+ }
344
+ }
345
+ g_vmax = vmax;
346
+ return n;
347
+ }
348
+ static const char *ANSI[9] = {"", "\x1b[1;35m", "\x1b[36m", "\x1b[32m", "\x1b[2;3;37m", "\x1b[33m", "\x1b[1;34m", "\x1b[1;33m", "\x1b[38;5;208m"};
349
+
350
+ int main(int argc, char **argv) {
351
+ const char *wpath = "pylexnet_weights.bin", *path = NULL;
352
+ int classes = 0, bench = 0;
353
+ for (int i = 1; i < argc; i++) {
354
+ if (!strcmp(argv[i], "--classes")) classes = 1;
355
+ else if (!strcmp(argv[i], "--bench")) bench = 1;
356
+ else if (!strcmp(argv[i], "--reference")) g_ref = 1;
357
+ else if (!strcmp(argv[i], "-w")) wpath = argv[++i];
358
+ else path = argv[i];
359
+ }
360
+ FILE *f = fopen(wpath, "rb");
361
+ if (!f) { perror(wpath); return 1; }
362
+ fseek(f, 0, SEEK_END); long ws = ftell(f); rewind(f);
363
+ unsigned char *wb = malloc((size_t)ws);
364
+ if (fread(wb, 1, (size_t)ws, f) != (size_t)ws) return 1;
365
+ fclose(f);
366
+ P = wb;
367
+ const int *h = take(40);
368
+ int NIN = h[0], NSTATE = h[1], NB = h[2], ninit = h[3], oL = h[4], LAB = h[5], oE = h[6], EV = h[7], oB = h[8];
369
+ g_U = (float)h[9]; /* declared bound on every activation; verified at runtime, see check below */
370
+ const int *init = take(4 * (size_t)ninit);
371
+ const int *evmap = take(4 * (size_t)(EV - 9) * 9); /* events >= 9 map the backward register instead of setting it */
372
+ Block *L = malloc(sizeof(Block) * (size_t)NB);
373
+ g_L = L;
374
+ int total = NIN;
375
+ for (int k = 0; k < NB; k++) { load(&L[k]); total += L[k].rows; }
376
+ if (!g_ref) specialise(L, NB, total, oB);
377
+ f = path ? fopen(path, "rb") : stdin;
378
+ if (!f) { perror(path); return 1; }
379
+ size_t cap = 1 << 20, n = 0;
380
+ unsigned char *src = malloc(cap);
381
+ size_t got;
382
+ while ((got = fread(src + n, 1, cap - n, f)) > 0) { n += got; if (n == cap) src = realloc(src, cap *= 2); }
383
+
384
+ float *av = malloc(sizeof(float) * (size_t)total);
385
+ int *ai = malloc(sizeof(int) * (size_t)total);
386
+ Act *A0 = malloc(sizeof(Act) * (size_t)total), *A1 = malloc(sizeof(Act) * (size_t)total);
387
+ unsigned char *lab = malloc(n + 1); signed char *ev = malloc(n + 2);
388
+ int *st = malloc(sizeof(int) * (size_t)NSTATE); float *sv = malloc(sizeof(float) * (size_t)NSTATE);
389
+ Block *last = &L[NB - 1];
390
+ int lbase = last->base, lim = oL + LAB;
391
+ struct timespec t0, t1;
392
+ clock_gettime(CLOCK_MONOTONIC, &t0);
393
+ for (int pass = 0; pass < 2; pass++) {
394
+ int ref = g_ref || pass;
395
+ g_vmax = 0;
396
+ if (ref) {
397
+ int ns = ninit;
398
+ for (int i = 0; i < ninit; i++) { st[i] = init[i]; sv[i] = 1; }
399
+ for (size_t t = 0; t <= n; t++) {
400
+ unsigned char b = t < n ? src[t] : 10;
401
+ int na = 0;
402
+ for (int i = 0; i < ns; i++) { ai[na] = st[i]; av[na++] = sv[i]; }
403
+ ai[na] = oB + b; av[na++] = 1;
404
+ int from = 0;
405
+ for (int k = 0; k < NB; k++) { from = na; na += apply_ref(&L[k], ai, av, na, ai + na, av + na); }
406
+ ns = 0; int l = 0, e = -1;
407
+ for (int i = from; i < na; i++) {
408
+ int r = ai[i] - lbase;
409
+ if (r < NSTATE) { st[ns] = r; sv[ns++] = av[i]; }
410
+ else if (r < lim) l = r - oL;
411
+ else e = r - oE;
412
+ }
413
+ if (t < n) lab[t] = (unsigned char)l;
414
+ ev[t] = (signed char)e;
415
+ }
416
+ } else {
417
+ Act *cur = A0, *nxt = A1;
418
+ int ns = ninit;
419
+ for (int i = 0; i < ninit; i++) { cur[i].c = init[i]; cur[i].v = 1; }
420
+ for (size_t t = 0; t <= n; t++) {
421
+ unsigned char b = t < n ? src[t] : 10;
422
+ if (g_needkv) for (int a = g_kvptr[b]; a < g_kvptr[b + 1]; a++) g_kv[g_kvcol[a]] = g_kvval[a];
423
+ int na = ns, from = 0;
424
+ for (int k = 0; k < NB; k++) { from = na; na += apply(&L[k], b, cur, na, cur + na); }
425
+ if (g_needkv) for (int a = g_kvptr[b]; a < g_kvptr[b + 1]; a++) g_kv[g_kvcol[a]] = 0;
426
+ /* next state goes straight into the other buffer; label and event are read off */
427
+ ns = 0; int l = 0, e = -1;
428
+ for (int i = from; i < na; i++) {
429
+ int r = cur[i].c - lbase;
430
+ if (r < NSTATE) { nxt[ns].c = r; nxt[ns].v = cur[i].v; ns++; }
431
+ else if (r < lim) l = r - oL;
432
+ else e = r - oE;
433
+ }
434
+ if (t < n) lab[t] = (unsigned char)l;
435
+ ev[t] = (signed char)e;
436
+ Act *tmp = cur; cur = nxt; nxt = tmp;
437
+ }
438
+ }
439
+ /* The pruning assumed |activation| <= bound. Every activation the fast kernel produced was
440
+ * checked against it; if any broke it the pruning was unsound and the file is redone with
441
+ * the reference kernel. */
442
+ if (ref || g_vmax <= g_U) break;
443
+ fprintf(stderr, "pylexnet: activation %g exceeds declared bound %g; using the reference kernel\n", (double)g_vmax, (double)g_U);
444
+ }
445
+ /* backward cell and head: r' = event, or the event's map of r, or r; class = label or r */
446
+ int reg = 0;
447
+ for (size_t t = n; t-- > 0;) {
448
+ if (ev[t + 1] >= 0) reg = ev[t + 1] < 9 ? ev[t + 1] : evmap[(ev[t + 1] - 9) * 9 + reg];
449
+ if (lab[t] == 9) lab[t] = (unsigned char)reg;
450
+ }
451
+ clock_gettime(CLOCK_MONOTONIC, &t1);
452
+ if (bench) {
453
+ double s = (double)(t1.tv_sec - t0.tv_sec) + 1e-9 * (double)(t1.tv_nsec - t0.tv_nsec);
454
+ fprintf(stderr, "%zu bytes, %.1f ns/byte, %.1f rows touched/byte (per block:", n, s * 1e9 / (double)n, (double)g_touched / (double)n);
455
+ for (int k = 0; k < NB && k < 16; k++) fprintf(stderr, " %.1f", (double)g_tb[k] / (double)n);
456
+ fprintf(stderr, ")\n");
457
+ return 0;
458
+ }
459
+ if (classes) { fwrite(lab, 1, n, stdout); return 0; }
460
+ int cur = 0;
461
+ for (size_t t = 0; t < n; t++) {
462
+ if (lab[t] != cur) { fputs("\x1b[0m", stdout); fputs(ANSI[lab[t]], stdout); cur = lab[t]; }
463
+ putchar(src[t]);
464
+ }
465
+ fputs("\x1b[0m", stdout);
466
+ return 0;
467
+ }
builds/accurate-7370/pylexnet_weights.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:c5976e0d344c629ec943c8ff5794669284d5088eb8bbe636e7c4ac2fe4bb3248
3
+ size 89840
builds/accurate-7370/spec.py ADDED
@@ -0,0 +1,373 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Python lexical rules as one finite automaton plus a one-register backward pass.
2
+
3
+ The automaton is what gets compiled into network weights. Nothing here is learned.
4
+
5
+ Forward pass, per byte: next state, a direct label, and an event. An event says
6
+ "the word that just ended has this class". Backward pass: carry that class back
7
+ over the word's bytes.
8
+ """
9
+ import builtins
10
+ import keyword
11
+ import re
12
+ from pathlib import Path
13
+
14
+ import pygments.lexers.python as _pl
15
+
16
+ TEXT, KEYWORD, BUILTIN, STRING, COMMENT, NUMBER, FUNC, CLASS, DECOR = range(9)
17
+ NAMES = ["text", "keyword", "builtin", "string", "comment", "number", "func", "class", "decorator"]
18
+ WORD = 9 # label meaning "resolve from the backward pass"
19
+ # Events 0..8 set the backward register. Events 9, 10 instead map it: they fire at the first byte of a word
20
+ # and turn that word's class, once it is known, into the class of whatever came before it.
21
+ EV_MATCH, EV_YIELD = 9, 10
22
+ EV_MAP = [[TEXT if c == KEYWORD else KEYWORD for c in range(9)], # match/case: a keyword next means it was a plain name
23
+ [KEYWORD if c == KEYWORD else TEXT for c in range(9)]] # the space in `yield from` belongs to the keyword
24
+
25
+ KEYWORDS = set(keyword.kwlist)
26
+ # Pygments' word lists, so both sides share the vocabulary.
27
+ _src = Path(_pl.__file__).read_text()
28
+ _b = _src.index("'builtins': [")
29
+ _a = _src.index("'magicfuncs': [")
30
+ BUILTINS = set(re.findall(r"'(\w+)'", _src[_b:_a])) - {"builtins", "exceptions"} | {"self", "cls", "Ellipsis", "NotImplemented"}
31
+ MAGIC = set(re.findall(r"'(__\w+__)'", _src[_a:_src.index("'magicvars': [")])) - BUILTINS
32
+ PLAIN_PRE = {"r", "u", "b", "br", "rb"}
33
+ F_PRE = {"f", "fr", "rf"}
34
+
35
+
36
+ def _cases(words):
37
+ out = set()
38
+ for p in words:
39
+ for m in range(1 << len(p)):
40
+ out.add("".join(c.upper() if m >> i & 1 else c for i, c in enumerate(p)))
41
+ return out
42
+
43
+
44
+ PLAIN_PRE, F_PRE = _cases(PLAIN_PRE), _cases(F_PRE)
45
+
46
+ ALPHA = set(b"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_") | set(range(128, 256))
47
+ DIGIT = set(b"0123456789")
48
+ WORDCH = ALPHA | DIGIT
49
+ NL, BS, HASH, DOT, AT = 10, 92, 35, 46, 64
50
+ QUOTES = (34, 39)
51
+ SPACE = {32, 9, 12}
52
+
53
+
54
+ # Word categories the trie sub-network reports. One small integer per word.
55
+ F_NONE, F_KW, F_BI, F_MAGIC, F_PRE_S, F_PRE_F, F_DEF, F_CLASS, F_IMP, F_SOFT, F_YIELD = range(11)
56
+
57
+
58
+ def build_trie():
59
+ nodes = [{}]
60
+ term = {}
61
+ groups = ((F_KW, KEYWORDS), (F_BI, BUILTINS), (F_MAGIC, MAGIC), (F_PRE_S, PLAIN_PRE), (F_PRE_F, F_PRE),
62
+ (F_DEF, {"def"}), (F_CLASS, {"class"}), (F_IMP, {"import", "from"}), (F_SOFT, {"match", "case"}), (F_YIELD, {"yield"}))
63
+ for flag, words in groups:
64
+ for w in words:
65
+ n = 0
66
+ for ch in w.encode():
67
+ if ch not in nodes[n]:
68
+ nodes.append({})
69
+ nodes[n][ch] = len(nodes) - 1
70
+ n = nodes[n][ch]
71
+ term[n] = flag
72
+ return nodes, term
73
+
74
+
75
+ TRIE, TERM = build_trie()
76
+
77
+
78
+ def word_class(flag, ctx):
79
+ if ctx == "us":
80
+ return KEYWORD
81
+ if ctx == "dec":
82
+ return DECOR
83
+ if ctx == "def":
84
+ return FUNC
85
+ if ctx == "class":
86
+ return CLASS
87
+ if flag in (F_KW, F_DEF, F_CLASS, F_IMP, F_YIELD):
88
+ return KEYWORD
89
+ if flag == F_MAGIC:
90
+ return FUNC
91
+ if ctx in ("dot", "imp"):
92
+ return TEXT
93
+ if flag == F_BI:
94
+ return BUILTIN
95
+ return TEXT
96
+
97
+
98
+ def ctx_after_word(flag, ctx):
99
+ if ctx != "dot":
100
+ if flag == F_DEF:
101
+ return "def"
102
+ if flag == F_CLASS:
103
+ return "class"
104
+ if flag == F_YIELD:
105
+ return "yield"
106
+ if flag == F_IMP: # `from a import b` turns it off again; so does `yield from`
107
+ return "none" if ctx in ("imp", "yield") else "imp"
108
+ return ctx if ctx in STICKY else "none"
109
+
110
+
111
+ STICKY = ("imp", "soft") # contexts that last to the end of the line
112
+ # After match/case these mean an ordinary name.
113
+ NOT_SOFT = b":,;=^&|@~)]}"
114
+ BOL = "numend" # line-start mode. Takes over the kind name compile.py already lists, now that numbers don't use it.
115
+
116
+
117
+ def code(ctx, fr):
118
+ return ("code", ctx, fr)
119
+
120
+
121
+ def line_start(fr):
122
+ return (BOL, fr) if fr is None else code("none", fr)
123
+
124
+
125
+ def after_string(fr):
126
+ return code("none", fr)
127
+
128
+
129
+ def back(fr):
130
+ """Leave a replacement field: back to the string that owns it, inside any field that holds that string."""
131
+ return ("str", fr[0], fr[1], 0, True, fr[3] + (None,) if fr[3] else None)
132
+
133
+
134
+ def step(state, b, flag):
135
+ """(next state, label for this byte, event for the word just ended)."""
136
+ kind = state[0]
137
+ fr = state[-1] # f-string context: None or (quote, triple, bracket depth, outer field or None)
138
+ if kind == "code":
139
+ ctx = state[1]
140
+ if ctx == "ysp": # `yield ` then: only `from` keeps the space in the keyword
141
+ if b == 102:
142
+ return ("word", "yield", fr), WORD, EV_YIELD
143
+ nxt, lab, _ = step(code("none", fr), b, flag)
144
+ return nxt, lab, TEXT
145
+ if ctx == "feq": # `=` in a field at depth 0: only `}`, `:` or `!r` after it make it the debug specifier
146
+ if b in SPACE:
147
+ return state, WORD, None
148
+ if b == 33:
149
+ return ("bang", fr), WORD, None
150
+ if b in b"}:":
151
+ return step(("bang2", fr), b, flag)
152
+ nxt, lab, _ = step(code("none", fr), b, flag)
153
+ return nxt, lab, TEXT
154
+ if ctx == "sq": # line-start match/case, class still open: the next byte decides
155
+ if b == 32 or b == 9:
156
+ return state, TEXT, None
157
+ nxt, lab, _ = step(code("none" if b in NOT_SOFT else "soft", fr), b, flag)
158
+ # True/False/None start with capitals: lower-case keywords are the ones that undo a match/case
159
+ return nxt, lab, TEXT if b in NOT_SOFT else EV_MATCH if 97 <= b <= 122 else KEYWORD
160
+ if fr is not None:
161
+ q, triple, depth, out = fr
162
+ if b in b"([{":
163
+ return code("none", (q, triple, min(depth + 1, 3), out)), TEXT, None
164
+ if b in b")]" and depth:
165
+ return code("none", (q, triple, depth - 1, out)), TEXT, None
166
+ if b == 125: # }
167
+ if depth:
168
+ return code("none", (q, triple, depth - 1, out)), TEXT, None
169
+ return back(fr), STRING, None
170
+ if b == 33 and depth == 0: # !r conversion, unless it turns out to be !=
171
+ return ("bang", fr), WORD, None
172
+ if b == 61 and depth == 0: # = may be the debug specifier
173
+ return code("feq", fr), WORD, None
174
+ if b == 58 and depth == 0: # : starts the format spec, which is string text
175
+ return back(fr), STRING, None
176
+ if b == NL:
177
+ return line_start(fr), TEXT, None
178
+ if ctx == "yield" and b == 32:
179
+ return code("ysp", fr), WORD, None
180
+ if b in SPACE or b == BS:
181
+ return state, TEXT, None
182
+ if b == HASH:
183
+ return ("cmt", fr), COMMENT, None
184
+ if b in QUOTES:
185
+ return ("q1", b, False, fr), STRING, None
186
+ if b in DIGIT:
187
+ return ("int", fr), NUMBER, None
188
+ if b == DOT:
189
+ if ctx == "imp": # dotted module name
190
+ return state, TEXT, None
191
+ return ("dot", fr), WORD, None
192
+ if b == AT:
193
+ return ("at", "none", fr), WORD, None
194
+ if ctx == "soft" and b == 95: # `_` in a match/case line
195
+ return ("word", "us", fr), WORD, None
196
+ if b in ALPHA:
197
+ return ("word", ctx, fr), WORD, None
198
+ return code(ctx if ctx in STICKY else "none", fr), TEXT, None
199
+ if kind == BOL: # indentation costs no register; only m and c can start a soft keyword
200
+ if b in SPACE:
201
+ return state, TEXT, None
202
+ if b in b"mc":
203
+ return ("word", "bol", fr), WORD, None
204
+ return step(code("none", fr), b, flag)
205
+ if kind == "bang":
206
+ if b in b"sraf":
207
+ return ("bang2", fr), WORD, None
208
+ nxt, lab, _ = step(code("none", fr), b, flag)
209
+ return nxt, lab, TEXT
210
+ if kind == "bang2":
211
+ if b in b"}:":
212
+ return back(fr), STRING, STRING
213
+ nxt, lab, _ = step(code("none", fr), b, flag)
214
+ return nxt, lab, TEXT
215
+ if kind == "dot": # the dot's class waits for this byte: .5 or an attribute
216
+ if b in DIGIT:
217
+ return ("frac", fr), NUMBER, NUMBER
218
+ nxt, lab, _ = step(code("dot", fr), b, flag)
219
+ return nxt, lab, TEXT
220
+ if kind == "at":
221
+ if b in ALPHA:
222
+ return ("word", "dec", fr), WORD, None
223
+ nxt, lab, _ = step(code("none", fr), b, flag)
224
+ return nxt, lab, TEXT
225
+ if kind == "word":
226
+ ctx = state[1]
227
+ if b in WORDCH:
228
+ return (("word", "none", fr) if ctx == "us" else state), WORD, None
229
+ if b in QUOTES and flag in (F_PRE_S, F_PRE_F) and ctx not in ("dec", "def", "class"):
230
+ return ("q1", b, flag == F_PRE_F, fr), STRING, STRING
231
+ if ctx == "bol" and flag == F_SOFT:
232
+ return step(code("sq", fr), b, F_NONE)
233
+ if b == DOT and flag not in (F_KW, F_DEF, F_CLASS, F_IMP, F_YIELD) and ctx != "imp":
234
+ return code("dot", fr), TEXT, word_class(flag, ctx) # after a name a dot is always an attribute, no need to wait
235
+ nxt, lab, _ = step(code(ctx_after_word(flag, ctx), fr), b, F_NONE)
236
+ return nxt, lab, word_class(flag, ctx)
237
+ if kind == "cmt":
238
+ if b == NL:
239
+ return line_start(fr), TEXT, None
240
+ return state, COMMENT, None
241
+ if kind == "q1":
242
+ q, f = state[1], state[2]
243
+ if b == q:
244
+ return ("q2", q, f, fr), STRING, None
245
+ return step(("str", q, 0, 0, f, fr), b, flag)
246
+ if kind == "q2":
247
+ q, f = state[1], state[2]
248
+ if b == q:
249
+ return ("str", q, 1, 0, f, fr), STRING, None
250
+ return step(after_string(fr), b, flag)
251
+ if kind == "str":
252
+ q, triple, close, f = state[1], state[2], state[3], state[4]
253
+ if b == BS:
254
+ return ("esc", q, triple, f, fr), STRING, None
255
+ if f and b == 123: # {
256
+ return ("fopen", q, triple, fr), STRING, None
257
+ if triple:
258
+ if b == q:
259
+ if close == 2:
260
+ return after_string(fr), STRING, None
261
+ return ("str", q, 1, close + 1, f, fr), STRING, None
262
+ return ("str", q, 1, 0, f, fr), STRING, None
263
+ if b == q:
264
+ return after_string(fr), STRING, None
265
+ if b == NL:
266
+ return line_start(fr), TEXT, None
267
+ return state, STRING, None
268
+ if kind == "fopen": # just saw { in an f-string: {{ is a literal brace
269
+ q, triple = state[1], state[2]
270
+ if b == 123:
271
+ return ("str", q, triple, 0, True, fr), STRING, None
272
+ return step(code("none", (q, triple, 0, fr[:3] if fr else None)), b, flag)
273
+ if kind == "esc":
274
+ q, triple, f = state[1], state[2], state[3]
275
+ if f and b == 123: # `\{` escapes nothing: the brace still opens a field
276
+ return ("fopen", q, triple, fr), STRING, None
277
+ if f and b == 78: # \N{NAME} is one character, not a field
278
+ return ("escn", q, triple, f, fr), STRING, None
279
+ return ("str", q, triple, 0, f, fr), STRING, None
280
+ if kind == "escn":
281
+ if b == 123:
282
+ return ("nname", state[1], state[2], state[3], fr), STRING, None
283
+ return step(("str", state[1], state[2], 0, state[3], fr), b, flag)
284
+ if kind == "nname":
285
+ if b == 125:
286
+ return ("str", state[1], state[2], 0, state[3], fr), STRING, None
287
+ if b == state[1] or b == NL:
288
+ return step(("str", state[1], state[2], 0, state[3], fr), b, flag)
289
+ return state, STRING, None
290
+ if kind == "int":
291
+ if b in DIGIT or b == 95:
292
+ return state, NUMBER, None
293
+ if b in b"xXoObB":
294
+ return ("hex", fr), NUMBER, None
295
+ if b == DOT:
296
+ return ("frac", fr), NUMBER, None
297
+ if b in b"eE":
298
+ return ("exp0", fr), NUMBER, None
299
+ return step(code("none", fr), b, flag)
300
+ if kind == "hex":
301
+ if b in DIGIT or b in b"abcdefABCDEF_":
302
+ return state, NUMBER, None
303
+ return step(code("none", fr), b, flag)
304
+ if kind == "frac":
305
+ if b in DIGIT or b == 95:
306
+ return state, NUMBER, None
307
+ if b in b"eE":
308
+ return ("exp0", fr), NUMBER, None
309
+ return step(code("none", fr), b, flag)
310
+ if kind == "exp0":
311
+ if b in b"+-" or b in DIGIT:
312
+ return ("exp", fr), NUMBER, None
313
+ return step(code("none", fr), b, flag)
314
+ if kind == "exp":
315
+ if b in DIGIT or b == 95:
316
+ return state, NUMBER, None
317
+ if b in b"jJ": # imaginary; ends the literal
318
+ return code("none", fr), NUMBER, None
319
+ return step(code("none", fr), b, flag)
320
+ raise ValueError(state)
321
+
322
+
323
+ START = line_start(None)
324
+
325
+
326
+ def build_table():
327
+ """Enumerate reachable mode states. rows[state][flag][byte] = (next, label, event)."""
328
+ ids = {START: 0}
329
+ order = [START]
330
+ rows = []
331
+ cells = {}
332
+ i = 0
333
+ while i < len(order):
334
+ s = order[i]
335
+ table = []
336
+ for flag in range(11 if s[0] == "word" else 1): # only a word's end reads the word category
337
+ row = []
338
+ for b in range(256):
339
+ n, lab, ev = step(s, b, flag)
340
+ if n not in ids:
341
+ ids[n] = len(order)
342
+ order.append(n)
343
+ t = (ids[n], lab, -1 if ev is None else ev)
344
+ row.append(cells.setdefault(t, t))
345
+ table.append(row)
346
+ rows.append(table if s[0] == "word" else table * 11)
347
+ i += 1
348
+ return order, rows
349
+
350
+
351
+ def run_table(data, rows):
352
+ """Reference execution: trie, mode machine, then backward pass."""
353
+ n = len(data)
354
+ lab = [0] * n
355
+ ev = [-1] * (n + 1)
356
+ s = 0
357
+ node = 0 # trie position; None once the word has left the trie
358
+ for i, b in enumerate(data):
359
+ flag = TERM.get(node, F_NONE) if node is not None else F_NONE
360
+ s, lab[i], ev[i] = rows[s][flag][b]
361
+ if b in WORDCH:
362
+ node = TRIE[node].get(b) if node is not None else None
363
+ else:
364
+ node = 0
365
+ flag = TERM.get(node, F_NONE) if node is not None else F_NONE
366
+ ev[n] = rows[s][flag][NL][2]
367
+ out = bytearray(n)
368
+ reg = TEXT
369
+ for i in range(n - 1, -1, -1):
370
+ if ev[i + 1] >= 0:
371
+ reg = ev[i + 1] if ev[i + 1] < EV_MATCH else EV_MAP[ev[i + 1] - EV_MATCH][reg]
372
+ out[i] = reg if lab[i] == WORD else lab[i]
373
+ return out
builds/fast-994/pylexnet.c ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* pylexnet (hand lane): run the hand-designed Python highlighter network.
2
+ *
3
+ * Two paths over the same weights file:
4
+ * default the generated kernel in pylexnet_kernel.h (architecture only; values gathered from the file).
5
+ * The loader checks the structure hash and proves every guard inequality from the loaded values.
6
+ * --interp the generic layered sparse evaluation (clip(W v + b, 0, hi) + carried entries), no kernel.
7
+ * usage: pylexnet [-w weights.bin] [--interp] (--classes [file] | --bench file)
8
+ * --classes writes one class byte (0..8) per input byte; without a file it reads stdin.
9
+ */
10
+ #include <math.h>
11
+ #include <stdint.h>
12
+ #include <stdio.h>
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+ #include <time.h>
16
+
17
+ typedef struct {
18
+ int64_t rows, cols, nskip, tied; /* tied: one weight row and one ceiling shared by all rows */
19
+ const double *W, *b, *hi;
20
+ const int64_t *skip;
21
+ int *rowptr, *col;
22
+ double *val;
23
+ } Layer;
24
+
25
+ typedef struct {
26
+ int64_t n_feats, n_fstate, n_fout, n_bstate, n_classes, head_cols, maxw;
27
+ const double *emb, *h0, *head_W, *head_b;
28
+ const int64_t *f_state, *f_out, *b_state;
29
+ Layer *fwd, *bwd;
30
+ int64_t n_fwd, n_bwd;
31
+ } Net;
32
+
33
+ static int use_interp = 0;
34
+
35
+ /* ---- loading ------------------------------------------------------------- */
36
+ typedef struct { const unsigned char *p, *end; } Cursor;
37
+
38
+ static const void *take(Cursor *c, size_t bytes) {
39
+ if (c->p + bytes > c->end) { fprintf(stderr, "weights: truncated\n"); exit(1); }
40
+ const void *r = c->p;
41
+ c->p += bytes;
42
+ return r;
43
+ }
44
+ static int64_t take_i64(Cursor *c) { int64_t v; memcpy(&v, take(c, 8), 8); return v; }
45
+
46
+ static Layer *load_cell(Cursor *c, int64_t *n, int64_t *maxw) {
47
+ *n = take_i64(c);
48
+ Layer *L = calloc((size_t)*n, sizeof(Layer));
49
+ for (int64_t l = 0; l < *n; l++) {
50
+ Layer *y = &L[l];
51
+ y->rows = take_i64(c); y->cols = take_i64(c); y->nskip = take_i64(c); y->tied = take_i64(c);
52
+ int64_t wrows = y->tied ? 1 : y->rows;
53
+ y->W = take(c, (size_t)(wrows * y->cols) * 8);
54
+ y->b = take(c, (size_t)y->rows * 8);
55
+ y->hi = take(c, (size_t)wrows * 8);
56
+ y->skip = take(c, (size_t)y->nskip * 8);
57
+ if (y->cols > *maxw) *maxw = y->cols;
58
+ if (y->rows + y->nskip > *maxw) *maxw = y->rows + y->nskip;
59
+ int nnz = 0;
60
+ for (int64_t i = 0; i < wrows * y->cols; i++) nnz += y->W[i] != 0;
61
+ if (y->tied) nnz *= (int)y->rows;
62
+ y->rowptr = malloc((size_t)(y->rows + 1) * sizeof(int));
63
+ y->col = malloc((size_t)(nnz + 1) * sizeof(int));
64
+ y->val = malloc((size_t)(nnz + 1) * sizeof(double));
65
+ int k = 0;
66
+ for (int64_t r = 0; r < y->rows; r++) {
67
+ y->rowptr[r] = k;
68
+ const double *w = y->W + (y->tied ? 0 : r) * y->cols;
69
+ for (int64_t cc = 0; cc < y->cols; cc++) if (w[cc] != 0) y->col[k] = (int)cc, y->val[k++] = w[cc];
70
+ }
71
+ y->rowptr[y->rows] = k;
72
+ }
73
+ return L;
74
+ }
75
+
76
+ static Net load(const char *path) {
77
+ FILE *f = fopen(path, "rb");
78
+ if (!f) { perror(path); exit(1); }
79
+ fseek(f, 0, SEEK_END);
80
+ size_t n = (size_t)ftell(f);
81
+ fseek(f, 0, SEEK_SET);
82
+ unsigned char *blob = malloc(n);
83
+ if (fread(blob, 1, n, f) != n) { perror(path); exit(1); }
84
+ fclose(f);
85
+ Cursor c = { blob, blob + n };
86
+ if (memcmp(take(&c, 8), "PYLX02\0\0", 8)) { fprintf(stderr, "%s: not a PYLX02 weight file\n", path); exit(1); }
87
+ Net net = {0};
88
+ net.n_feats = take_i64(&c); net.n_fstate = take_i64(&c); net.n_fout = take_i64(&c);
89
+ net.n_bstate = take_i64(&c); net.n_classes = take_i64(&c); net.head_cols = take_i64(&c);
90
+ net.emb = take(&c, (size_t)(128 * net.n_feats) * 8);
91
+ net.maxw = net.n_feats + net.n_fstate;
92
+ if (net.n_fout + net.n_bstate > net.maxw) net.maxw = net.n_fout + net.n_bstate;
93
+ net.fwd = load_cell(&c, &net.n_fwd, &net.maxw);
94
+ net.bwd = load_cell(&c, &net.n_bwd, &net.maxw);
95
+ net.f_state = take(&c, (size_t)net.n_fstate * 8);
96
+ net.f_out = take(&c, (size_t)net.n_fout * 8);
97
+ net.b_state = take(&c, (size_t)net.n_bstate * 8);
98
+ net.h0 = take(&c, (size_t)net.n_fstate * 8);
99
+ net.head_W = take(&c, (size_t)net.head_cols * 8); /* one integer-valued readout unit: the class id */
100
+ net.head_b = take(&c, 8);
101
+ if (c.p != c.end) { fprintf(stderr, "weights: %zu trailing bytes\n", (size_t)(c.end - c.p)); exit(1); }
102
+ return net;
103
+ }
104
+
105
+ /* ---- generic inference --------------------------------------------------- */
106
+ static void run_cell(const Layer *layers, int64_t nl, double *v, double *tmp) {
107
+ for (int64_t l = 0; l < nl; l++) {
108
+ const Layer *L = &layers[l];
109
+ for (int64_t r = 0; r < L->rows; r++) {
110
+ double s = L->b[r];
111
+ for (int k = L->rowptr[r]; k < L->rowptr[r + 1]; k++) s += L->val[k] * v[L->col[k]];
112
+ double hi = L->hi[L->tied ? 0 : r];
113
+ tmp[r] = s < 0 ? 0 : (s > hi ? hi : s);
114
+ }
115
+ for (int64_t k = 0; k < L->nskip; k++) tmp[L->rows + k] = v[L->skip[k]];
116
+ memcpy(v, tmp, (size_t)(L->rows + L->nskip) * sizeof(double));
117
+ }
118
+ }
119
+
120
+ /* n input bytes followed by one virtual newline, so a word at the end of the file still ends. */
121
+ static void classify_interp(const Net *net, const unsigned char *src, size_t n, unsigned char *cls, uint16_t *bits) {
122
+ double *v = malloc((size_t)net->maxw * 8), *tmp = malloc((size_t)net->maxw * 8);
123
+ double *h = malloc((size_t)net->n_fstate * 8), *r = calloc((size_t)net->n_bstate, 8);
124
+ memcpy(h, net->h0, (size_t)net->n_fstate * 8);
125
+ for (size_t t = 0; t <= n; t++) {
126
+ unsigned c = t < n ? (src[t] < 128 ? src[t] : 127) : 10;
127
+ memcpy(v, net->emb + c * net->n_feats, (size_t)net->n_feats * 8);
128
+ memcpy(v + net->n_feats, h, (size_t)net->n_fstate * 8);
129
+ run_cell(net->fwd, net->n_fwd, v, tmp);
130
+ for (int64_t i = 0; i < net->n_fstate; i++) h[i] = v[net->f_state[i]];
131
+ uint16_t bt = 0;
132
+ for (int64_t i = 0; i < net->n_fout; i++) bt |= (uint16_t)((v[net->f_out[i]] != 0) << i);
133
+ bits[t] = bt;
134
+ }
135
+ for (size_t t = n + 1; t-- > 0;) {
136
+ for (int64_t i = 0; i < net->n_fout; i++) v[i] = (bits[t] >> i) & 1;
137
+ memcpy(v + net->n_fout, r, (size_t)net->n_bstate * 8);
138
+ run_cell(net->bwd, net->n_bwd, v, tmp);
139
+ for (int64_t i = 0; i < net->n_bstate; i++) r[i] = v[net->b_state[i]];
140
+ double s = net->head_b[0];
141
+ for (int64_t c = 0; c < net->head_cols; c++) s += net->head_W[c] * v[c];
142
+ s = s < 0 ? 0 : (s > net->n_classes - 1 ? net->n_classes - 1 : s);
143
+ if (t < n) cls[t] = (unsigned char)s;
144
+ }
145
+ free(v); free(tmp); free(h); free(r);
146
+ }
147
+
148
+ /* ---- compiled mode ------------------------------------------------------- */
149
+ #include <emmintrin.h>
150
+ static inline double clip_sse(double x, double hi) { return _mm_cvtsd_f64(_mm_min_sd(_mm_max_sd(_mm_set_sd(x), _mm_setzero_pd()), _mm_set_sd(hi))); }
151
+ static inline double clip_fm(double x, double hi) { return __builtin_fmin(__builtin_fmax(x, 0.0), hi); }
152
+ #include "pylexnet_kernel.h"
153
+
154
+ typedef struct { uint64_t h; } Fnv;
155
+ static void fnv_bytes(Fnv *f, const void *p, size_t n) {
156
+ for (size_t i = 0; i < n; i++) { f->h ^= ((const unsigned char *)p)[i]; f->h *= 0x100000001B3ULL; }
157
+ }
158
+ static void fnv_i64(Fnv *f, int64_t x) { fnv_bytes(f, &x, 8); }
159
+ static void fnv_mask(Fnv *f, const double *a, int64_t n) {
160
+ for (int64_t i = 0; i < n; i++) { unsigned char m = a[i] != 0; fnv_bytes(f, &m, 1); }
161
+ }
162
+ static uint64_t struct_hash(const Net *net) {
163
+ Fnv f = { 0xCBF29CE484222325ULL };
164
+ fnv_i64(&f, net->n_feats); fnv_i64(&f, net->n_fstate); fnv_i64(&f, net->n_fout);
165
+ fnv_i64(&f, net->n_bstate); fnv_i64(&f, net->n_classes); fnv_i64(&f, net->head_cols);
166
+ const Layer *cells[2] = { net->fwd, net->bwd };
167
+ int64_t counts[2] = { net->n_fwd, net->n_bwd };
168
+ for (int c = 0; c < 2; c++) {
169
+ fnv_i64(&f, counts[c]);
170
+ for (int64_t l = 0; l < counts[c]; l++) {
171
+ const Layer *L = &cells[c][l];
172
+ fnv_i64(&f, L->rows); fnv_i64(&f, L->cols); fnv_i64(&f, L->nskip); fnv_i64(&f, L->tied);
173
+ fnv_bytes(&f, L->hi, (size_t)(L->tied ? 1 : L->rows) * 8);
174
+ for (int64_t k = 0; k < L->nskip; k++) fnv_i64(&f, L->skip[k]);
175
+ fnv_mask(&f, L->W, (L->tied ? 1 : L->rows) * L->cols);
176
+ }
177
+ }
178
+ for (int64_t i = 0; i < net->n_fstate; i++) fnv_i64(&f, net->f_state[i]);
179
+ for (int64_t i = 0; i < net->n_fout; i++) fnv_i64(&f, net->f_out[i]);
180
+ for (int64_t i = 0; i < net->n_bstate; i++) fnv_i64(&f, net->b_state[i]);
181
+ fnv_mask(&f, net->head_W, net->head_cols);
182
+ return f.h;
183
+ }
184
+
185
+ /* Every number in the file must be an integer: then all activations are exact integers in double arithmetic,
186
+ * sums may be re-associated, and a clip1 unit is exactly 0 or 1. */
187
+ static int all_integers(const Net *net) {
188
+ int bad = 0;
189
+ for (int64_t i = 0; i < 128 * net->n_feats; i++) bad += net->emb[i] != (int64_t)net->emb[i];
190
+ const Layer *cells[2] = { net->fwd, net->bwd };
191
+ int64_t counts[2] = { net->n_fwd, net->n_bwd };
192
+ for (int c = 0; c < 2; c++)
193
+ for (int64_t l = 0; l < counts[c]; l++) {
194
+ const Layer *L = &cells[c][l];
195
+ int64_t wrows = L->tied ? 1 : L->rows;
196
+ for (int64_t i = 0; i < wrows * L->cols; i++) bad += L->W[i] != (int64_t)L->W[i];
197
+ for (int64_t i = 0; i < L->rows; i++) bad += L->b[i] != (int64_t)L->b[i];
198
+ for (int64_t i = 0; i < wrows; i++) bad += L->hi[i] != (int64_t)L->hi[i];
199
+ }
200
+ for (int64_t i = 0; i < net->head_cols; i++) bad += net->head_W[i] != (int64_t)net->head_W[i];
201
+ for (int64_t i = 0; i < net->n_fstate; i++) bad += net->h0[i] != (int64_t)net->h0[i];
202
+ return bad;
203
+ }
204
+
205
+ /* Sorted threshold families: tied, one input, ceiling 1, biases monotone so the outputs form a step vector.
206
+ * Then rank(h) by binary search reproduces every unit, and a consumer's weights over the family reduce to a
207
+ * prefix sum indexed by the rank. Both tables are built here from the loaded values. */
208
+ static int check_families(const Net *net) {
209
+ int bad = 0;
210
+ for (int g = 0; g < K_N_FAMILY; g++) {
211
+ int cell = K_FAMILY[g][0], l = K_FAMILY[g][1], n = K_FAMILY[g][3];
212
+ const Layer *L = &(cell ? net->bwd : net->fwd)[l];
213
+ int nz = 0;
214
+ for (int64_t c = 0; c < L->cols; c++) nz += L->W[c] != 0;
215
+ if (!L->tied || L->rows != n || L->hi[0] != 1.0 || nz != 1) { bad++; continue; }
216
+ double w = 0;
217
+ for (int64_t c = 0; c < L->cols; c++) if (L->W[c] != 0) w = L->W[c];
218
+ for (int r = 1; r < n; r++) if ((w > 0 && L->b[r] > L->b[r - 1]) || (w < 0 && L->b[r] < L->b[r - 1])) bad++;
219
+ }
220
+ return bad;
221
+ }
222
+
223
+ static double *build_prefix(const Net *net) {
224
+ double *kp = malloc(sizeof(double) * (K_PREFIX_LEN + 1));
225
+ int pos = 0;
226
+ for (int g = 0; g < K_N_PREFIX; g++) {
227
+ int cell = K_PREFIX[g][0], l = K_PREFIX[g][1], r = K_PREFIX[g][2], c0 = K_PREFIX[g][3], n = K_PREFIX[g][4];
228
+ const Layer *L = &(cell ? net->bwd : net->fwd)[l];
229
+ kp[pos] = 0;
230
+ for (int k = 0; k < n; k++) kp[pos + k + 1] = kp[pos + k] + L->W[(L->tied ? 0 : r) * L->cols + c0 + k];
231
+ pos += n + 1;
232
+ }
233
+ return kp;
234
+ }
235
+
236
+ static void gather(const Net *net, double *kc, double *kb) {
237
+ for (int i = 0; i < K_N_COEF; i++) {
238
+ int cell = K_COEF[i][0], l = K_COEF[i][1], r = K_COEF[i][2], c = K_COEF[i][3];
239
+ if (cell == 2) kc[i] = net->head_W[c];
240
+ else { const Layer *L = &(cell ? net->bwd : net->fwd)[l]; kc[i] = L->W[(L->tied ? 0 : r) * L->cols + c]; }
241
+ }
242
+ for (int i = 0; i < K_N_BIAS; i++) {
243
+ int cell = K_BIAS[i][0], l = K_BIAS[i][1], r = K_BIAS[i][2];
244
+ kb[i] = cell == 2 ? net->head_b[r] : (cell ? net->bwd : net->fwd)[l].b[r];
245
+ }
246
+ }
247
+
248
+ /* Upper bound of an entry of a layer's input vector, from the loaded ceilings and embedding table.
249
+ * layer -1 is the cell's own input: byte features then carried state (forward), bits then state (backward). */
250
+ static double entry_max(const Net *net, int cell, int64_t layer, int64_t idx, int depth) {
251
+ if (depth > 64) return 1e300;
252
+ const Layer *L = cell ? net->bwd : net->fwd;
253
+ if (layer < 0) {
254
+ if (cell == 0) {
255
+ if (idx < net->n_feats) {
256
+ double m = 0;
257
+ for (int b = 0; b < 128; b++) if (net->emb[b * net->n_feats + idx] > m) m = net->emb[b * net->n_feats + idx];
258
+ return m;
259
+ }
260
+ return entry_max(net, 0, net->n_fwd - 1, net->f_state[idx - net->n_feats], depth + 1);
261
+ }
262
+ if (idx < net->n_fout) return 1.0;
263
+ return entry_max(net, 1, net->n_bwd - 1, net->b_state[idx - net->n_fout], depth + 1);
264
+ }
265
+ if (idx < L[layer].rows) return L[layer].hi[L[layer].tied ? 0 : idx];
266
+ return entry_max(net, cell, layer - 1, L[layer].skip[idx - L[layer].rows], depth + 1);
267
+ }
268
+
269
+ /* Each guarded unit must be provably 0 when its guard columns are 0: bias plus every other positive
270
+ * contribution at its input's ceiling must not exceed 0. Returns the number of failed guards. */
271
+ static int check_guards(const Net *net) {
272
+ int bad = 0;
273
+ for (int g = 0; g < K_N_GUARD; g++) {
274
+ int cell = K_GUARD[g][0], l = K_GUARD[g][1], r = K_GUARD[g][2];
275
+ const Layer *L = &(cell ? net->bwd : net->fwd)[l];
276
+ double bound = L->b[r];
277
+ for (int64_t c = 0; c < L->cols; c++) {
278
+ double w = L->W[(L->tied ? 0 : r) * L->cols + c];
279
+ if (w <= 0) continue;
280
+ int guarded = 0;
281
+ for (int k = 0; k < K_MAX_GUARD; k++) if (K_GUARD[g][3 + k] == c) guarded = 1;
282
+ if (!guarded) bound += w * entry_max(net, cell, l - 1, c, 0);
283
+ }
284
+ if (bound > 0) { bad++; fprintf(stderr, "guard %d (cell %d layer %d row %d) not provable: bound %g\n", g, cell, l, r, bound); }
285
+ }
286
+ return bad;
287
+ }
288
+
289
+ static void classify_compiled(const Net *net, const double *kc, const double *kb, const double *kp,
290
+ const unsigned char *src, size_t n, unsigned char *cls, uint16_t *bits) {
291
+ FState a, b, *p = &a, *s = &b, *t;
292
+ k_init_state(p, net->h0);
293
+ for (size_t i = 0; i < n; i++) {
294
+ bits[i] = k_fwd_step(kc, kb, kp, net->emb + (src[i] < 128 ? src[i] : 127) * K_N_FEATS, p, s);
295
+ t = p; p = s; s = t;
296
+ }
297
+ bits[n] = k_fwd_step(kc, kb, kp, net->emb + 10 * K_N_FEATS, p, s);
298
+ BState ra, rb, *r = &ra, *q = &rb, *u;
299
+ memset(&ra, 0, sizeof ra);
300
+ k_bwd_step(kc, kb, bits[n], r, q);
301
+ u = r; r = q; q = u;
302
+ for (size_t i = n; i-- > 0;) {
303
+ cls[i] = (unsigned char)k_bwd_step(kc, kb, bits[i], r, q);
304
+ u = r; r = q; q = u;
305
+ }
306
+ }
307
+
308
+ /* ---- cli ----------------------------------------------------------------- */
309
+ static char *default_weights(const char *argv0) {
310
+ const char *slash = strrchr(argv0, '/');
311
+ size_t dir = slash ? (size_t)(slash - argv0 + 1) : 0;
312
+ char *p = malloc(dir + 32);
313
+ memcpy(p, argv0, dir);
314
+ strcpy(p + dir, "pylexnet_weights.bin");
315
+ return p;
316
+ }
317
+
318
+ int main(int argc, char **argv) {
319
+ int classes = 0, bench = 0;
320
+ const char *path = NULL, *wpath = NULL;
321
+ for (int i = 1; i < argc; i++) {
322
+ if (!strcmp(argv[i], "--classes")) classes = 1;
323
+ else if (!strcmp(argv[i], "--bench")) bench = 1;
324
+ else if (!strcmp(argv[i], "--interp")) use_interp = 1;
325
+ else if (!strcmp(argv[i], "-w")) wpath = argv[++i];
326
+ else path = argv[i];
327
+ }
328
+ if (!classes && !bench) { fprintf(stderr, "usage: pylexnet [-w weights.bin] [--interp] (--classes [file] | --bench file)\n"); return 2; }
329
+ Net net = load(wpath ? wpath : default_weights(argv[0]));
330
+ double *kc = malloc(sizeof(double) * (K_N_COEF + 1)), *kb = malloc(sizeof(double) * (K_N_BIAS + 1)), *kp = NULL;
331
+ if (!use_interp) {
332
+ uint64_t h = struct_hash(&net);
333
+ if (h != K_STRUCT_HASH) {
334
+ fprintf(stderr, "weights: architecture hash %016llx does not match compiled kernel %016llx; regenerate (compile.py) or run --interp\n",
335
+ (unsigned long long)h, (unsigned long long)K_STRUCT_HASH);
336
+ return 1;
337
+ }
338
+ if (check_guards(&net)) { fprintf(stderr, "weights: guard check failed, refusing the kernel\n"); return 1; }
339
+ if (all_integers(&net)) { fprintf(stderr, "weights: non-integer values, refusing the kernel\n"); return 1; }
340
+ if (check_families(&net)) { fprintf(stderr, "weights: a threshold family is not tied/sorted, refusing the kernel\n"); return 1; }
341
+ gather(&net, kc, kb);
342
+ kp = build_prefix(&net);
343
+ }
344
+ FILE *f = path ? fopen(path, "rb") : stdin;
345
+ if (!f) { perror(path); return 1; }
346
+ size_t cap = 1 << 20, n = 0, got;
347
+ unsigned char *src = malloc(cap);
348
+ while ((got = fread(src + n, 1, cap - n, f)) > 0) { n += got; if (n == cap) src = realloc(src, cap *= 2); }
349
+ if (path) fclose(f);
350
+ unsigned char *cls = malloc(n + 1);
351
+ uint16_t *bits = malloc((n + 2) * sizeof(uint16_t));
352
+ if (bench) {
353
+ int reps = 5;
354
+ struct timespec a, b;
355
+ clock_gettime(CLOCK_MONOTONIC, &a);
356
+ for (int i = 0; i < reps; i++) {
357
+ if (use_interp) classify_interp(&net, src, n, cls, bits);
358
+ else classify_compiled(&net, kc, kb, kp, src, n, cls, bits);
359
+ }
360
+ clock_gettime(CLOCK_MONOTONIC, &b);
361
+ double sec = (b.tv_sec - a.tv_sec) + (b.tv_nsec - a.tv_nsec) * 1e-9;
362
+ fprintf(stderr, "%zu bytes, %.1f ns/byte\n", n, sec / reps / (double)n * 1e9);
363
+ return 0;
364
+ }
365
+ if (use_interp) classify_interp(&net, src, n, cls, bits);
366
+ else classify_compiled(&net, kc, kb, kp, src, n, cls, bits);
367
+ fwrite(cls, 1, n, stdout);
368
+ return 0;
369
+ }
builds/fast-994/pylexnet_kernel.h ADDED
@@ -0,0 +1,171 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Generated by compile.py: the network's ARCHITECTURE as C. No weight values live here: pylexnet.c
2
+ * loads them, checks the structure hash and the guard inequalities, and gathers kc[]/kb[] by K_COEF/K_BIAS. */
3
+ #include <stdint.h>
4
+ #include <string.h>
5
+ #define K_STRUCT_HASH 0x848E1BC62ED893C7ULL
6
+ #define K_N_FEATS 18
7
+ #define K_N_CLASSES 9
8
+ #define K_N_BITS 10
9
+ #ifndef CLIP
10
+ #define CLIP(x, hi) clipn((x), (hi))
11
+ static inline double clipn(double x, double hi) { x = x < 0.0 ? 0.0 : x; return x > hi ? hi : x; }
12
+ #endif
13
+ /* A tied, sorted threshold family is a step vector: unit k is 1 iff kb[k] + w*h >= 1, true for k < rank.
14
+ * The loader verifies integrality, tying and monotonicity, so a binary search gives the same values. */
15
+ static inline int k_rank(const double *kb, int n, double w, double h) {
16
+ int lo = 0, hi = n;
17
+ while (lo < hi) { int mid = (lo + hi) >> 1; if (kb[mid] + w * h >= 1.0) lo = mid + 1; else hi = mid; }
18
+ return lo;
19
+ }
20
+ typedef struct { double S, sq1, T, F, E, c, fo, FS, FD, oq1, oT, C, N, ee, W, d, pf, fp, cD, cC, cAt, atp, cDot, h1; } FState;
21
+ typedef struct { double u_reg_KW, u_reg_BI, u_reg_FN, u_reg_CL, u_reg_DEC, u_reg_STR, b_ev_KW, b_ev_BI, b_ev_FN, b_ev_CL, b_ev_DEC, b_ev_STR; } BState;
22
+ static inline void k_init_state(FState *s, const double *h0) { memcpy(s, h0, sizeof *s); }
23
+ static inline __attribute__((always_inline)) uint16_t k_fwd_step(const double *restrict kc, const double *restrict kb, const double *restrict kp, const double *restrict x,
24
+ const FState *restrict p, FState *restrict s) {
25
+ double u_wordch = 0.0, u_qmd = 0.0, u_qms = 0.0, u_c2 = 0.0, u_lt7 = 0.0, u_wend = 0.0, u_fieldopen = 0.0, u_pf_new = 0.0, u_S_eff = 0.0, u_fp_new = 0.0, u_sclose = 0.0, u_tclose = 0.0, u_tripleopen = 0.0, u_nlclose = 0.0, u_E_new = 0.0, u_fo_new = 0.0, u_C_new = 0.0, u_open = 0.0, u_open_f = 0.0, u_open_qs = 0.0, u_fieldclose = 0.0, u_inc = 0.0, u_dec = 0.0, u_S_new = 0.0, u_c_new = 0.0, u_T_new = 0.0, u_F_new = 0.0, u_sq1_new = 0.0, u_FS_new = 0.0, u_FD_new = 0.0, u_oq1_new = 0.0, u_oT_new = 0.0, u_lab_STR = 0.0, u_N_new = 0.0, u_ee_new = 0.0, u_W_new = 0.0, u_d_new = 0.0, u_gA = 0.0, u_gB = 0.0, u_cA = 0.0, u_cB = 0.0, u_h1_new = 0.0, u_E_KW = 0.0, u_E_BI = 0.0, u_E_MAG = 0.0, u_is_def = 0.0, u_is_class = 0.0, u_cD_new = 0.0, u_cC_new = 0.0, u_atp_new = 0.0, u_cAt_new = 0.0, u_cDot_new = 0.0, u_ev_DEC = 0.0, u_ev_CL = 0.0, u_ev_KW = 0.0, u_ev_FN = 0.0, u_ev_BI = 0.0, u_ev_STR = 0.0, u_lab_W = 0.0;
26
+ int r_ge = 0;
27
+ u_lt7 = CLIP((kb[0] + kc[0] * p->d), 1.0);
28
+ if ((p->S + p->fo + p->FS + p->FD + p->c + p->T + p->F + p->sq1 + p->oq1 + p->oT + x[2] + x[3]) != 0.0) { /* all activations are >= 0 */
29
+ u_qmd = CLIP(((kb[1] + kc[1] * x[2]) + (kc[2] * p->sq1 + kc[3] * p->E)), 1.0);
30
+ u_qms = CLIP(((kb[2] + kc[4] * x[3]) + (kc[5] * p->sq1 + kc[6] * p->E)), 1.0);
31
+ u_c2 = CLIP((kb[3] + kc[7] * p->c), 1.0);
32
+ u_fieldopen = CLIP(((kb[4] + kc[8] * p->fo) + kc[9] * x[8]), 1.0);
33
+ u_S_eff = CLIP((((kb[5] + kc[10] * p->S) + (kc[11] * u_fieldopen + kc[12] * p->T)) + ((kc[13] * u_qmd + kc[14] * u_qms) + kc[15] * u_c2)), 1.0);
34
+ u_sclose = CLIP((((kb[6] + kc[16] * u_S_eff) + (kc[17] * p->T + kc[18] * u_qmd)) + (kc[19] * u_qms + kc[20] * p->c)), 1.0);
35
+ u_tclose = CLIP((((kb[7] + kc[21] * u_S_eff) + (kc[22] * p->T + kc[23] * u_qmd)) + (kc[24] * u_qms + kc[25] * p->c)), 1.0);
36
+ u_tripleopen = CLIP((((kb[8] + kc[26] * u_S_eff) + (kc[27] * p->T + kc[28] * u_qmd)) + (kc[29] * u_qms + kc[30] * p->c)), 1.0);
37
+ u_nlclose = CLIP((((kb[9] + kc[31] * u_S_eff) + (kc[32] * p->T + kc[33] * x[1])) + kc[34] * p->E), 1.0);
38
+ u_E_new = CLIP(((kb[10] + kc[35] * u_S_eff) + (kc[36] * x[4] + kc[37] * p->E)), 1.0);
39
+ u_fo_new = CLIP((((kb[11] + kc[38] * u_S_eff) + (kc[39] * p->F + kc[40] * x[8])) + (kc[41] * p->E + kc[42] * p->fo)), 1.0);
40
+ u_open = CLIP((((kb[12] + kc[43] * x[2]) + (kc[44] * x[3] + kc[45] * u_S_eff)) + (kc[46] * p->C + kc[47] * p->N)), 1.0);
41
+ u_open_f = CLIP((((kb[13] + kc[48] * x[2]) + (kc[49] * x[3] + kc[50] * u_S_eff)) + ((kc[51] * p->C + kc[52] * p->N) + (kc[53] * p->pf + kc[54] * p->fp))), 1.0);
42
+ u_open_qs = CLIP((((kb[14] + kc[55] * x[2]) + (kc[56] * x[3] + kc[57] * u_S_eff)) + (kc[58] * p->C + kc[59] * p->N)), 1.0);
43
+ u_fieldclose = CLIP((((kb[15] + kc[60] * p->FS) + (kc[61] * u_S_eff + kc[62] * p->C)) + (kc[63] * p->FD + kc[64] * x[12])), 1.0);
44
+ u_inc = CLIP((((kb[16] + kc[65] * p->FS) + (kc[66] * u_S_eff + kc[67] * p->C)) + kc[68] * x[10]), 1.0);
45
+ u_dec = CLIP((((kb[17] + kc[69] * p->FS) + (kc[70] * u_S_eff + kc[71] * p->C)) + (kc[72] * x[11] + kc[73] * p->FD)), 1.0);
46
+ u_S_new = CLIP((((kb[18] + kc[74] * u_S_eff) + (kc[75] * u_sclose + kc[76] * u_tclose)) + ((kc[77] * u_nlclose + kc[78] * u_open) + kc[79] * u_fieldclose)), 1.0);
47
+ u_c_new = CLIP(((((kb[19] + kc[80] * p->c) + (kc[81] * u_S_eff + kc[82] * u_qmd)) + ((kc[83] * u_qms + kc[84] * u_tripleopen) + (kc[85] * u_tclose + kc[86] * u_sclose))) + (kc[87] * u_open + kc[88] * u_open_qs)), 3.0);
48
+ u_T_new = CLIP((((kb[20] + kc[89] * p->T) + (kc[90] * u_tripleopen + kc[91] * u_tclose)) + ((kc[92] * u_open + kc[93] * u_fieldclose) + kc[94] * p->oT)), 1.0);
49
+ u_F_new = CLIP((((kb[21] + kc[95] * p->F) + (kc[96] * u_open_f + kc[97] * u_open)) + ((kc[98] * u_sclose + kc[99] * u_tclose) + (kc[100] * u_nlclose + kc[101] * u_fieldclose))), 1.0);
50
+ u_sq1_new = CLIP(((((kb[22] + kc[102] * p->sq1) + (kc[103] * u_open_qs + kc[104] * u_sclose)) + ((kc[105] * u_tclose + kc[106] * u_nlclose) + (kc[107] * p->S + kc[108] * u_S_eff))) + (kc[109] * u_fieldclose + kc[110] * p->oq1)), 1.0);
51
+ u_FS_new = CLIP(((kb[23] + kc[111] * p->FS) + (kc[112] * u_fieldopen + kc[113] * u_fieldclose)), 1.0);
52
+ u_FD_new = CLIP(((kb[24] + kc[114] * p->FD) + (kc[115] * u_inc + kc[116] * u_dec)), 3.0);
53
+ u_oq1_new = CLIP((((kb[25] + kc[117] * p->oq1) + (kc[118] * u_fieldopen + kc[119] * p->sq1)) + kc[120] * u_fieldclose), 1.0);
54
+ u_oT_new = CLIP((((kb[26] + kc[121] * p->oT) + (kc[122] * u_fieldopen + kc[123] * p->T)) + kc[124] * u_fieldclose), 1.0);
55
+ u_lab_STR = CLIP((((kb[27] + kc[125] * u_S_eff) + (kc[126] * u_nlclose + kc[127] * u_open)) + kc[128] * u_fieldclose), 1.0);
56
+ }
57
+ if ((p->W + x[13] + x[6] + x[7] + p->atp + p->cD + p->cC + p->cAt + p->cDot + x[15] + x[14] + p->fp + p->d) != 0.0) { /* all activations are >= 0 */
58
+ u_wordch = CLIP((kb[28] + kc[129] * x[13]), 1.0);
59
+ u_wend = CLIP(((kb[29] + kc[130] * p->W) + kc[131] * x[13]), 1.0);
60
+ u_pf_new = CLIP((((kb[30] + kc[132] * x[15]) + (kc[133] * x[14] + kc[134] * p->pf)) + (kc[135] * p->W + kc[136] * p->d)), 1.0);
61
+ u_atp_new = CLIP(((kb[31] + kc[137] * x[7]) + (kc[138] * u_S_eff + kc[139] * p->C)), 1.0);
62
+ u_fp_new = CLIP(((kb[32] + kc[140] * x[14]) + (kc[141] * p->fp + kc[142] * u_wordch)), 1.0);
63
+ u_gA = CLIP((((kb[33] + kc[143] * p->h1) + (kc[144] * p->W + kc[145] * u_wordch)) + kc[146] * u_lt7), 2199023255552.0);
64
+ u_gB = CLIP((((kb[34] + kc[147] * p->h1) + (kc[148] * p->W + kc[149] * u_wordch)) + kc[150] * u_lt7), 2199023255552.0);
65
+ u_cAt_new = CLIP((((kb[35] + kc[151] * p->cAt) + (kc[152] * p->atp + kc[153] * u_wordch)) + kc[154] * u_wend), 1.0);
66
+ u_W_new = CLIP((((kb[36] + kc[155] * u_wordch) + (kc[156] * x[0] + kc[157] * p->W)) + ((kc[158] * p->N + kc[159] * u_S_eff) + kc[160] * p->C)), 1.0);
67
+ u_cDot_new = CLIP((((kb[37] + kc[161] * x[6]) + (kc[162] * u_S_eff + kc[163] * p->C)) + ((kc[164] * p->N + kc[165] * p->cDot) + (kc[166] * u_wordch + kc[167] * u_wend))), 1.0);
68
+ u_d_new = CLIP(((kb[38] + kc[168] * p->d) + kc[169] * u_W_new), 30.0);
69
+ u_cA = CLIP(((kb[39] + kc[170] * x[13]) + (kc[171] * u_W_new + kc[172] * u_lt7)), 2199023255552.0);
70
+ u_cB = CLIP((((kb[40] + kc[173] * x[13]) + (kc[174] * p->d + kc[175] * u_W_new)) + kc[176] * u_lt7), 2199023255552.0);
71
+ u_lab_W = CLIP(((kb[41] + kc[177] * u_W_new) + kc[178] * u_atp_new), 1.0);
72
+ }
73
+ u_h1_new = CLIP((((kb[42] + kc[179] * u_gA) + (kc[180] * u_gB + kc[181] * u_cA)) + kc[182] * u_cB), 2199023255552.0);
74
+ if ((u_wend) != 0.0) { /* all activations are >= 0 */
75
+ r_ge = k_rank(kb + 43, 255, kc[183], p->h1);
76
+ u_ev_DEC = CLIP(((kb[298] + kc[184] * u_wend) + kc[185] * p->cAt), 1.0);
77
+ u_ev_CL = CLIP(((kb[299] + kc[186] * u_wend) + (kc[187] * p->cC + kc[188] * p->cAt)), 1.0);
78
+ u_ev_STR = CLIP(((kb[300] + kc[189] * u_wend) + (kc[190] * u_open + kc[191] * p->pf)), 1.0);
79
+ u_E_KW = CLIP(((kb[301] + kc[192] * p->h1) + kp[0 + r_ge]), 1.0);
80
+ u_E_BI = CLIP(((kb[302] + kc[193] * p->h1) + kp[256 + r_ge]), 1.0);
81
+ u_E_MAG = CLIP(((kb[303] + kc[194] * p->h1) + kp[512 + r_ge]), 1.0);
82
+ u_is_def = CLIP((((kb[304] + kc[195] * u_wend) + (kc[196] * u_E_KW + kc[197] * (double)(12 < r_ge))) + kc[198] * (double)(13 < r_ge)), 1.0);
83
+ u_is_class = CLIP((((kb[305] + kc[199] * u_wend) + (kc[200] * u_E_KW + kc[201] * (double)(50 < r_ge))) + kc[202] * (double)(51 < r_ge)), 1.0);
84
+ u_ev_KW = CLIP((((kb[306] + kc[203] * u_wend) + (kc[204] * u_E_KW + kc[205] * p->cAt)) + (kc[206] * p->cC + kc[207] * p->cD)), 1.0);
85
+ u_ev_FN = CLIP((((kb[307] + kc[208] * u_wend) + (kc[209] * u_E_MAG + kc[210] * p->cD)) + (kc[211] * p->cAt + kc[212] * p->cC)), 1.0);
86
+ u_ev_BI = CLIP((((kb[308] + kc[213] * u_wend) + (kc[214] * u_E_BI + kc[215] * p->cAt)) + ((kc[216] * p->cC + kc[217] * p->cD) + kc[218] * p->cDot)), 1.0);
87
+ }
88
+ if ((p->W + x[13] + x[6] + x[7] + p->atp + p->cD + p->cC + p->cAt + p->cDot + x[15] + x[14] + p->fp + p->d) != 0.0) { /* all activations are >= 0 */
89
+ u_cD_new = CLIP((((kb[309] + kc[219] * p->cD) + (kc[220] * u_is_def + kc[221] * u_wend)) + kc[222] * x[1]), 1.0);
90
+ u_cC_new = CLIP((((kb[310] + kc[223] * p->cC) + (kc[224] * u_is_class + kc[225] * u_wend)) + kc[226] * x[1]), 1.0);
91
+ }
92
+ if ((x[5] + p->C) != 0.0) { /* all activations are >= 0 */
93
+ u_C_new = CLIP((((kb[311] + kc[227] * x[5]) + (kc[228] * u_S_eff + kc[229] * p->C)) + kc[230] * x[1]), 1.0);
94
+ }
95
+ if ((x[0] + p->N + p->ee) != 0.0) { /* all activations are >= 0 */
96
+ u_N_new = CLIP(((((kb[312] + kc[231] * x[0]) + (kc[232] * p->W + kc[233] * p->N)) + ((kc[234] * u_wordch + kc[235] * x[6]) + (kc[236] * x[17] + kc[237] * p->ee))) + (kc[238] * u_S_eff + kc[239] * p->C)), 1.0);
97
+ u_ee_new = CLIP(((kb[313] + kc[240] * x[16]) + kc[241] * u_N_new), 1.0);
98
+ }
99
+ s->S = u_S_new;
100
+ s->sq1 = u_sq1_new;
101
+ s->T = u_T_new;
102
+ s->F = u_F_new;
103
+ s->E = u_E_new;
104
+ s->c = u_c_new;
105
+ s->fo = u_fo_new;
106
+ s->FS = u_FS_new;
107
+ s->FD = u_FD_new;
108
+ s->oq1 = u_oq1_new;
109
+ s->oT = u_oT_new;
110
+ s->C = u_C_new;
111
+ s->N = u_N_new;
112
+ s->ee = u_ee_new;
113
+ s->W = u_W_new;
114
+ s->d = u_d_new;
115
+ s->pf = u_pf_new;
116
+ s->fp = u_fp_new;
117
+ s->cD = u_cD_new;
118
+ s->cC = u_cC_new;
119
+ s->cAt = u_cAt_new;
120
+ s->atp = u_atp_new;
121
+ s->cDot = u_cDot_new;
122
+ s->h1 = u_h1_new;
123
+ return (uint16_t)(((uint16_t)(u_lab_STR != 0.0) << 0) | ((uint16_t)(u_C_new != 0.0) << 1) | ((uint16_t)(u_N_new != 0.0) << 2) | ((uint16_t)(u_lab_W != 0.0) << 3) | ((uint16_t)(u_ev_KW != 0.0) << 4) | ((uint16_t)(u_ev_BI != 0.0) << 5) | ((uint16_t)(u_ev_FN != 0.0) << 6) | ((uint16_t)(u_ev_CL != 0.0) << 7) | ((uint16_t)(u_ev_DEC != 0.0) << 8) | ((uint16_t)(u_ev_STR != 0.0) << 9));
124
+ }
125
+ static inline __attribute__((always_inline)) int k_bwd_step(const double *restrict kc, const double *restrict kb, uint16_t bits,
126
+ const BState *restrict r, BState *restrict s) {
127
+ const double f_lab_STR = (double)((bits >> 0) & 1);
128
+ const double f_C_new = (double)((bits >> 1) & 1);
129
+ const double f_N_new = (double)((bits >> 2) & 1);
130
+ const double f_lab_W = (double)((bits >> 3) & 1);
131
+ const double f_ev_KW = (double)((bits >> 4) & 1);
132
+ const double f_ev_BI = (double)((bits >> 5) & 1);
133
+ const double f_ev_FN = (double)((bits >> 6) & 1);
134
+ const double f_ev_CL = (double)((bits >> 7) & 1);
135
+ const double f_ev_DEC = (double)((bits >> 8) & 1);
136
+ const double f_ev_STR = (double)((bits >> 9) & 1);
137
+ const double u_reg_KW = CLIP(((kb[314] + kc[242] * r->b_ev_KW) + (kc[243] * r->u_reg_KW + kc[244] * f_lab_W)), 1.0);
138
+ const double u_reg_BI = CLIP(((kb[315] + kc[245] * r->b_ev_BI) + (kc[246] * r->u_reg_BI + kc[247] * f_lab_W)), 1.0);
139
+ const double u_reg_FN = CLIP(((kb[316] + kc[248] * r->b_ev_FN) + (kc[249] * r->u_reg_FN + kc[250] * f_lab_W)), 1.0);
140
+ const double u_reg_CL = CLIP(((kb[317] + kc[251] * r->b_ev_CL) + (kc[252] * r->u_reg_CL + kc[253] * f_lab_W)), 1.0);
141
+ const double u_reg_DEC = CLIP(((kb[318] + kc[254] * r->b_ev_DEC) + (kc[255] * r->u_reg_DEC + kc[256] * f_lab_W)), 1.0);
142
+ const double u_reg_STR = CLIP(((kb[319] + kc[257] * r->b_ev_STR) + (kc[258] * r->u_reg_STR + kc[259] * f_lab_W)), 1.0);
143
+ s->u_reg_KW = u_reg_KW;
144
+ s->u_reg_BI = u_reg_BI;
145
+ s->u_reg_FN = u_reg_FN;
146
+ s->u_reg_CL = u_reg_CL;
147
+ s->u_reg_DEC = u_reg_DEC;
148
+ s->u_reg_STR = u_reg_STR;
149
+ s->b_ev_KW = f_ev_KW;
150
+ s->b_ev_BI = f_ev_BI;
151
+ s->b_ev_FN = f_ev_FN;
152
+ s->b_ev_CL = f_ev_CL;
153
+ s->b_ev_DEC = f_ev_DEC;
154
+ s->b_ev_STR = f_ev_STR;
155
+ return (int)CLIP(((((kb[320] + kc[260] * u_reg_KW) + (kc[261] * u_reg_BI + kc[262] * u_reg_FN)) + ((kc[263] * u_reg_CL + kc[264] * u_reg_DEC) + (kc[265] * u_reg_STR + kc[266] * f_lab_STR))) + (kc[267] * f_C_new + kc[268] * f_N_new)), 8.0);
156
+ }
157
+ #define K_N_COEF 269
158
+ #define K_N_BIAS 321
159
+ #define K_N_GUARD 54
160
+ #define K_N_FAMILY 1
161
+ #define K_N_PREFIX 3
162
+ #define K_PREFIX_LEN 768
163
+ /* sorted threshold families: {cell, layer, first bias index, rows, coef index of the tied weight} */
164
+ static const int32_t K_FAMILY[K_N_FAMILY + 1][5] = {{0,1,43,255,183},{0,0,0,0,0}};
165
+ /* prefix sums of a unit's weights over a family's columns: {cell, layer, row, first column, rows} */
166
+ static const int32_t K_PREFIX[K_N_PREFIX + 1][5] = {{0,3,15,7,255},{0,3,16,7,255},{0,3,17,7,255},{0,0,0,0,0}};
167
+ #define K_MAX_GUARD 4
168
+ static const int32_t K_COEF[K_N_COEF][4] = {{0,0,4,33},{0,0,1,2},{0,0,1,19},{0,0,1,22},{0,0,2,3},{0,0,2,19},{0,0,2,22},{0,0,3,23},{0,0,6,24},{0,0,6,8},{0,2,0,279},{0,2,0,261},{0,2,0,281},{0,2,0,256},{0,2,0,257},{0,2,0,258},{0,3,0,0},{0,3,0,286},{0,3,0,263},{0,3,0,264},{0,3,0,289},{0,3,1,0},{0,3,1,286},{0,3,1,263},{0,3,1,264},{0,3,1,289},{0,3,2,0},{0,3,2,286},{0,3,2,263},{0,3,2,264},{0,3,2,289},{0,3,3,0},{0,3,3,286},{0,3,3,270},{0,3,3,288},{0,3,4,0},{0,3,4,273},{0,3,4,288},{0,3,5,0},{0,3,5,287},{0,3,5,277},{0,3,5,288},{0,3,5,290},{0,3,7,271},{0,3,7,272},{0,3,7,0},{0,3,7,295},{0,3,7,296},{0,3,8,271},{0,3,8,272},{0,3,8,0},{0,3,8,295},{0,3,8,296},{0,3,8,300},{0,3,8,301},{0,3,9,271},{0,3,9,272},{0,3,9,0},{0,3,9,295},{0,3,9,296},{0,3,10,291},{0,3,10,0},{0,3,10,295},{0,3,10,292},{0,3,10,280},{0,3,11,291},{0,3,11,0},{0,3,11,295},{0,3,11,278},{0,3,12,291},{0,3,12,0},{0,3,12,295},{0,3,12,279},{0,3,12,292},{0,4,0,20},{0,4,0,0},{0,4,0,1},{0,4,0,3},{0,4,0,7},{0,4,0,10},{0,4,1,44},{0,4,1,20},{0,4,1,31},{0,4,1,32},{0,4,1,2},{0,4,1,1},{0,4,1,0},{0,4,1,7},{0,4,1,9},{0,4,2,42},{0,4,2,2},{0,4,2,1},{0,4,2,7},{0,4,2,10},{0,4,2,48},{0,4,3,43},{0,4,3,8},{0,4,3,7},{0,4,3,0},{0,4,3,1},{0,4,3,3},{0,4,3,10},{0,4,4,41},{0,4,4,9},{0,4,4,0},{0,4,4,1},{0,4,4,3},{0,4,4,40},{0,4,4,20},{0,4,4,10},{0,4,4,47},{0,4,5,45},{0,4,5,35},{0,4,5,10},{0,4,6,46},{0,4,6,11},{0,4,6,12},{0,4,7,47},{0,4,7,35},{0,4,7,41},{0,4,7,10},{0,4,8,48},{0,4,8,35},{0,4,8,42},{0,4,8,10},{0,4,9,20},{0,4,9,3},{0,4,9,7},{0,4,9,10},{0,0,0,13},{0,0,5,32},{0,0,5,13},{0,0,7,15},{0,0,7,14},{0,0,7,34},{0,0,7,32},{0,0,7,33},{0,3,18,276},{0,3,18,0},{0,3,18,295},{0,2,1,276},{0,2,1,296},{0,2,1,255},{0,2,2,302},{0,2,2,293},{0,2,2,255},{0,2,2,259},{0,2,3,302},{0,2,3,293},{0,2,3,255},{0,2,3,259},{0,2,4,299},{0,2,4,300},{0,2,4,255},{0,2,4,260},{0,3,14,262},{0,3,14,269},{0,3,14,298},{0,3,14,296},{0,3,14,0},{0,3,14,295},{0,3,19,275},{0,3,19,0},{0,3,19,295},{0,3,19,296},{0,3,19,305},{0,3,19,262},{0,3,19,266},{0,4,11,49},{0,4,11,14},{0,4,12,38},{0,4,12,14},{0,4,12,33},{0,4,13,38},{0,4,13,49},{0,4,13,14},{0,4,13,33},{0,4,20,14},{0,4,20,18},{0,5,0,29},{0,5,0,30},{0,5,0,12},{0,5,0,13},{0,1,0,47},{0,2,5,260},{0,2,5,299},{0,2,6,260},{0,2,6,298},{0,2,6,299},{0,4,19,34},{0,4,19,7},{0,4,19,50},{0,3,15,306},{0,3,16,306},{0,3,17,306},{0,4,14,34},{0,4,14,15},{0,4,14,27},{0,4,14,28},{0,4,15,34},{0,4,15,15},{0,4,15,29},{0,4,15,30},{0,4,16,34},{0,4,16,15},{0,4,16,53},{0,4,16,52},{0,4,16,51},{0,4,17,34},{0,4,17,17},{0,4,17,51},{0,4,17,53},{0,4,17,52},{0,4,18,34},{0,4,18,16},{0,4,18,53},{0,4,18,52},{0,4,18,51},{0,4,18,54},{0,5,1,37},{0,5,1,14},{0,5,1,34},{0,5,1,36},{0,5,2,38},{0,5,2,15},{0,5,2,34},{0,5,2,36},{0,3,6,274},{0,3,6,0},{0,3,6,295},{0,3,6,270},{0,3,13,269},{0,3,13,298},{0,3,13,296},{0,3,13,262},{0,3,13,275},{0,3,13,283},{0,3,13,297},{0,3,13,0},{0,3,13,295},{0,4,10,39},{0,4,10,13},{1,0,0,16},{1,0,0,10},{1,0,0,3},{1,0,1,17},{1,0,1,11},{1,0,1,3},{1,0,2,18},{1,0,2,12},{1,0,2,3},{1,0,3,19},{1,0,3,13},{1,0,3,3},{1,0,4,20},{1,0,4,14},{1,0,4,3},{1,0,5,21},{1,0,5,15},{1,0,5,3},{2,0,0,0},{2,0,0,1},{2,0,0,2},{2,0,0,3},{2,0,0,4},{2,0,0,5},{2,0,0,6},{2,0,0,7},{2,0,0,8}};
169
+ static const int32_t K_BIAS[K_N_BIAS][3] = {{0,0,4},{0,0,1},{0,0,2},{0,0,3},{0,0,6},{0,2,0},{0,3,0},{0,3,1},{0,3,2},{0,3,3},{0,3,4},{0,3,5},{0,3,7},{0,3,8},{0,3,9},{0,3,10},{0,3,11},{0,3,12},{0,4,0},{0,4,1},{0,4,2},{0,4,3},{0,4,4},{0,4,5},{0,4,6},{0,4,7},{0,4,8},{0,4,9},{0,0,0},{0,0,5},{0,0,7},{0,3,18},{0,2,1},{0,2,2},{0,2,3},{0,2,4},{0,3,14},{0,3,19},{0,4,11},{0,4,12},{0,4,13},{0,4,20},{0,5,0},{0,1,0},{0,1,1},{0,1,2},{0,1,3},{0,1,4},{0,1,5},{0,1,6},{0,1,7},{0,1,8},{0,1,9},{0,1,10},{0,1,11},{0,1,12},{0,1,13},{0,1,14},{0,1,15},{0,1,16},{0,1,17},{0,1,18},{0,1,19},{0,1,20},{0,1,21},{0,1,22},{0,1,23},{0,1,24},{0,1,25},{0,1,26},{0,1,27},{0,1,28},{0,1,29},{0,1,30},{0,1,31},{0,1,32},{0,1,33},{0,1,34},{0,1,35},{0,1,36},{0,1,37},{0,1,38},{0,1,39},{0,1,40},{0,1,41},{0,1,42},{0,1,43},{0,1,44},{0,1,45},{0,1,46},{0,1,47},{0,1,48},{0,1,49},{0,1,50},{0,1,51},{0,1,52},{0,1,53},{0,1,54},{0,1,55},{0,1,56},{0,1,57},{0,1,58},{0,1,59},{0,1,60},{0,1,61},{0,1,62},{0,1,63},{0,1,64},{0,1,65},{0,1,66},{0,1,67},{0,1,68},{0,1,69},{0,1,70},{0,1,71},{0,1,72},{0,1,73},{0,1,74},{0,1,75},{0,1,76},{0,1,77},{0,1,78},{0,1,79},{0,1,80},{0,1,81},{0,1,82},{0,1,83},{0,1,84},{0,1,85},{0,1,86},{0,1,87},{0,1,88},{0,1,89},{0,1,90},{0,1,91},{0,1,92},{0,1,93},{0,1,94},{0,1,95},{0,1,96},{0,1,97},{0,1,98},{0,1,99},{0,1,100},{0,1,101},{0,1,102},{0,1,103},{0,1,104},{0,1,105},{0,1,106},{0,1,107},{0,1,108},{0,1,109},{0,1,110},{0,1,111},{0,1,112},{0,1,113},{0,1,114},{0,1,115},{0,1,116},{0,1,117},{0,1,118},{0,1,119},{0,1,120},{0,1,121},{0,1,122},{0,1,123},{0,1,124},{0,1,125},{0,1,126},{0,1,127},{0,1,128},{0,1,129},{0,1,130},{0,1,131},{0,1,132},{0,1,133},{0,1,134},{0,1,135},{0,1,136},{0,1,137},{0,1,138},{0,1,139},{0,1,140},{0,1,141},{0,1,142},{0,1,143},{0,1,144},{0,1,145},{0,1,146},{0,1,147},{0,1,148},{0,1,149},{0,1,150},{0,1,151},{0,1,152},{0,1,153},{0,1,154},{0,1,155},{0,1,156},{0,1,157},{0,1,158},{0,1,159},{0,1,160},{0,1,161},{0,1,162},{0,1,163},{0,1,164},{0,1,165},{0,1,166},{0,1,167},{0,1,168},{0,1,169},{0,1,170},{0,1,171},{0,1,172},{0,1,173},{0,1,174},{0,1,175},{0,1,176},{0,1,177},{0,1,178},{0,1,179},{0,1,180},{0,1,181},{0,1,182},{0,1,183},{0,1,184},{0,1,185},{0,1,186},{0,1,187},{0,1,188},{0,1,189},{0,1,190},{0,1,191},{0,1,192},{0,1,193},{0,1,194},{0,1,195},{0,1,196},{0,1,197},{0,1,198},{0,1,199},{0,1,200},{0,1,201},{0,1,202},{0,1,203},{0,1,204},{0,1,205},{0,1,206},{0,1,207},{0,1,208},{0,1,209},{0,1,210},{0,1,211},{0,1,212},{0,1,213},{0,1,214},{0,1,215},{0,1,216},{0,1,217},{0,1,218},{0,1,219},{0,1,220},{0,1,221},{0,1,222},{0,1,223},{0,1,224},{0,1,225},{0,1,226},{0,1,227},{0,1,228},{0,1,229},{0,1,230},{0,1,231},{0,1,232},{0,1,233},{0,1,234},{0,1,235},{0,1,236},{0,1,237},{0,1,238},{0,1,239},{0,1,240},{0,1,241},{0,1,242},{0,1,243},{0,1,244},{0,1,245},{0,1,246},{0,1,247},{0,1,248},{0,1,249},{0,1,250},{0,1,251},{0,1,252},{0,1,253},{0,1,254},{0,2,5},{0,2,6},{0,4,19},{0,3,15},{0,3,16},{0,3,17},{0,4,14},{0,4,15},{0,4,16},{0,4,17},{0,4,18},{0,5,1},{0,5,2},{0,3,6},{0,3,13},{0,4,10},{1,0,0},{1,0,1},{1,0,2},{1,0,3},{1,0,4},{1,0,5},{2,0,0}};
170
+ /* units whose block is skipped when the listed input columns are all zero; the loader proves each one */
171
+ static const int32_t K_GUARD[K_N_GUARD][3 + K_MAX_GUARD] = {{0,0,1,2,-1,-1,-1},{0,0,2,3,-1,-1,-1},{0,0,3,23,-1,-1,-1},{0,0,6,24,-1,-1,-1},{0,2,0,279,-1,-1,-1},{0,3,0,0,-1,-1,-1},{0,3,1,0,-1,-1,-1},{0,3,2,0,-1,-1,-1},{0,3,3,0,-1,-1,-1},{0,3,4,0,-1,-1,-1},{0,3,5,0,-1,-1,-1},{0,3,7,271,272,-1,-1},{0,3,8,271,272,-1,-1},{0,3,9,272,-1,-1,-1},{0,3,10,291,-1,-1,-1},{0,3,11,291,-1,-1,-1},{0,3,12,291,-1,-1,-1},{0,4,0,20,7,10,-1},{0,4,1,20,7,9,-1},{0,4,2,42,2,10,-1},{0,4,3,43,8,10,-1},{0,4,4,41,9,20,10},{0,4,5,45,35,-1,-1},{0,4,6,46,11,-1,-1},{0,4,7,47,35,-1,-1},{0,4,8,48,35,-1,-1},{0,4,9,20,7,10,-1},{0,0,0,13,-1,-1,-1},{0,0,5,32,-1,-1,-1},{0,0,7,15,14,-1,-1},{0,3,18,276,-1,-1,-1},{0,2,1,276,296,-1,-1},{0,2,2,293,-1,-1,-1},{0,2,3,293,-1,-1,-1},{0,2,4,299,300,-1,-1},{0,3,14,262,298,-1,-1},{0,3,19,275,305,-1,-1},{0,4,11,14,-1,-1,-1},{0,4,12,14,-1,-1,-1},{0,4,13,14,-1,-1,-1},{0,4,20,14,18,-1,-1},{0,2,5,260,-1,-1,-1},{0,2,6,260,-1,-1,-1},{0,4,19,34,-1,-1,-1},{0,4,14,34,-1,-1,-1},{0,4,15,34,-1,-1,-1},{0,4,16,34,-1,-1,-1},{0,4,17,34,-1,-1,-1},{0,4,18,34,-1,-1,-1},{0,5,1,37,14,-1,-1},{0,5,2,38,15,-1,-1},{0,3,6,274,295,-1,-1},{0,3,13,269,296,-1,-1},{0,4,10,13,-1,-1,-1}};
builds/fast-994/pylexnet_weights.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:470c37fad3e65ae40ac70be637ab57626042f34db93107dec411b1eeca0ee6fa
3
+ size 106744
compile.py ADDED
@@ -0,0 +1,838 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """pylexnet 'hand' lane: a Python syntax-highlighter network designed unit by unit, lexnet style.
2
+
3
+ No automaton is compiled. Every unit below does one designed job: ~60 booleans for strings, comments,
4
+ numbers, words and contexts, a length counter, two hash-gate registers, a sorted threshold family on the
5
+ word hash, and one "distance to the nearest vocabulary word of category c" unit per category.
6
+ Activation is clip(x, 0, hi) everywhere.
7
+
8
+ Two vocabulary matchers, chosen by --matcher:
9
+ add (default) additive retrieval: per-position value tables (positions 0..P-1 plus a constant each, a tail
10
+ table, three last-byte tables) solved so that every vocabulary word's sum of values is its class target
11
+ t*2^30 (t = 1 keyword, 2 def, 3 class, 4 builtin, 5 magic), rounded to integers (window +-64). A one-hot
12
+ position chain gates the values into an accumulator; three delayed registers add the last bytes' values at
13
+ the word's end; nine threshold units read the class windows. ~1 number per word. Not exact: a non-vocabulary
14
+ word whose sum lands in a window is a false positive (fpcheck.py measures them on the fit split; pinning
15
+ words to 0 via zero_words.txt is possible when the system stays solvable).
16
+ hash exact: a polynomial hash h over the word, a sorted tied threshold family ge_k = clip1(h - T_k + 1) (the rank
17
+ of h), and per category E_c = clip1(h - sum_k ge_k * D_c[k]) with D_c telescoping to the largest category-c
18
+ threshold <= h, so E_c == 0 exactly when h is a category-c word. 2 numbers per word.
19
+
20
+ Writes pylexnet_weights.bin (PYLX02) and pylexnet_kernel.h (architecture only, with a structure hash).
21
+ usage: compile.py [--matcher add|hash] [--vocab N] [--addP P] [--sim TEXT] N = builtin/magic words kept, by fit frequency.
22
+ """
23
+ import keyword
24
+ import re
25
+ import struct
26
+ import sys
27
+ from pathlib import Path
28
+
29
+ import numpy as np
30
+
31
+ HERE = Path(__file__).resolve().parent
32
+
33
+ # ---------------------------------------------------------------- byte features (the embedding table)
34
+ FEATS = ["digit", "nl", "dq", "sq", "bs", "hash", "dot", "at", "lbr", "rbr",
35
+ "obr", "cbr", "fcch", "code", "pf", "po", "eE", "pm"]
36
+ MATCHER = "add" # "add": additive retrieval (IDEAS.md idea 1, default). "hash": sorted threshold family on a polynomial hash
37
+ ADD_P, ADD_L = 7, 3 # additive matcher: value tables (+ a constant) for positions 0..P-1, a tail table, 3 last-byte tables (L is fixed at 3)
38
+ ADD_EPS = 64 # window half-width around each class target
39
+ ADD_T = 2 ** 30 # class targets are t * ADD_T, t = 1 keyword, 2 def, 3 class, 4 builtin, 5 magic
40
+ if "--matcher" in sys.argv:
41
+ MATCHER = sys.argv[sys.argv.index("--matcher") + 1]
42
+ if "--addP" in sys.argv:
43
+ ADD_P = int(sys.argv[sys.argv.index("--addP") + 1])
44
+ if "--addL" in sys.argv:
45
+ ADD_L = int(sys.argv[sys.argv.index("--addL") + 1])
46
+ if MATCHER == "add":
47
+ FEATS += [f"v{p}" for p in range(ADD_P)] + ["vt"] + [f"e{j}" for j in range(1, ADD_L + 1)]
48
+ NF = len(FEATS)
49
+ FI = {f: i for i, f in enumerate(FEATS)}
50
+ ADD_TABLES = {} # feature name -> {code: value}, filled by solve_additive
51
+
52
+
53
+ def code_of(b):
54
+ """Word-character code for the hash: a-z 1..26, A-Z 27..52, _ 53, digits 54, bytes >= 127 55, else 0."""
55
+ if 97 <= b <= 122:
56
+ return b - 96
57
+ if 65 <= b <= 90:
58
+ return b - 64 + 26
59
+ if b == 95:
60
+ return 53
61
+ if 48 <= b <= 57:
62
+ return 54
63
+ if b >= 127:
64
+ return 55
65
+ return 0
66
+
67
+
68
+ BASE = 56 # hash base: one more than the largest code
69
+ WIN = 7 # exact polynomial over the first WIN chars; later chars add code + 8*position
70
+ HMAX = 2 ** 41 # ceiling of the hash registers (> BASE**WIN + tail)
71
+ KG = 2 ** 42 # gate constant for the hash units: larger than any hash value
72
+
73
+
74
+ def emb_table():
75
+ E = np.zeros((128, NF))
76
+ for b in range(128):
77
+ r = E[b]
78
+ c = code_of(b)
79
+ isalpha = (65 <= b <= 90) or (97 <= b <= 122) or b == 95 or b == 127
80
+ isdigit = 48 <= b <= 57
81
+ r[FI["digit"]] = isdigit
82
+ r[FI["nl"]] = b == 10
83
+ r[FI["dq"]] = b == 34
84
+ r[FI["sq"]] = b == 39
85
+ r[FI["bs"]] = b == 92
86
+ r[FI["hash"]] = b == 35
87
+ r[FI["dot"]] = b == 46
88
+ r[FI["at"]] = b == 64
89
+ r[FI["lbr"]] = b == 123
90
+ r[FI["rbr"]] = b == 125
91
+ r[FI["obr"]] = b in (40, 91, 123)
92
+ r[FI["cbr"]] = b in (41, 93, 125)
93
+ r[FI["fcch"]] = b in (125, 58, 33) # } : ! end a replacement field at depth 0
94
+ r[FI["code"]] = c
95
+ r[FI["pf"]] = b in (102, 70) # f F
96
+ r[FI["eE"]] = b in (101, 69)
97
+ r[FI["pm"]] = b in (43, 45)
98
+ r[FI["po"]] = b in (114, 82, 98, 66, 117, 85) # r R b B u U
99
+ for f, tab in ADD_TABLES.items():
100
+ r[FI[f]] = tab.get(c, 0) if c else 0
101
+ return E
102
+
103
+
104
+ def word_feats(w):
105
+ """Additive matcher: the (table, code) terms of a word."""
106
+ cs = [code_of(b) for b in w.encode()]
107
+ out = {}
108
+ for i, c in enumerate(cs):
109
+ k = (f"v{i}" if i < ADD_P else "vt", c)
110
+ out[k] = out.get(k, 0) + 1
111
+ k = (k[0], 0) # the position's constant (the gate unit's bias)
112
+ out[k] = out.get(k, 0) + 1
113
+ for j in range(1, ADD_L + 1):
114
+ if len(cs) >= j:
115
+ k = (f"e{j}", cs[-j])
116
+ out[k] = out.get(k, 0) + 1
117
+ return out
118
+
119
+
120
+ def solve_additive(vocab):
121
+ """Integer value tables such that every vocabulary word's sum lands on its class target (within ADD_EPS).
122
+ Orthogonal matching pursuit picks a sparse support, least squares solves it, rounding keeps it integer."""
123
+ words = sorted(vocab)
124
+ tgt = {"KW": 1, "BI": 4, "MAG": 5}
125
+ zero = [w for w in (HERE / "zero_words.txt").read_text().split() if w not in vocab] if (HERE / "zero_words.txt").exists() else []
126
+ T = np.array([ADD_T * (2 if w == "def" else 3 if w == "class" else tgt[vocab[w]]) for w in words] + [0.0] * len(zero))
127
+ words = words + zero # frequent non-vocabulary words pinned to 0 (chosen on the fit split, see zero_words.txt)
128
+ cols = {}
129
+ rows = [word_feats(w) for w in words]
130
+ for r in rows:
131
+ for k in r:
132
+ cols.setdefault(k, len(cols))
133
+ A = np.zeros((len(words), len(cols)))
134
+ for i, r in enumerate(rows):
135
+ for k, n in r.items():
136
+ A[i, cols[k]] = n
137
+ S, x = [], np.zeros(len(cols))
138
+ for _ in range(len(cols)):
139
+ res = T - A @ x
140
+ if np.abs(res).max() < 1e-3:
141
+ break
142
+ corr = A.T @ res
143
+ corr[S] = 0
144
+ j = int(np.argmax(np.abs(corr)))
145
+ if corr[j] == 0:
146
+ break
147
+ S.append(j)
148
+ xs, *_ = np.linalg.lstsq(A[:, S], T, rcond=None)
149
+ x = np.zeros(len(cols)); x[S] = xs
150
+ x = np.round(x)
151
+ res = A @ x - T
152
+ unsolved = int((np.abs(res) > ADD_EPS).sum())
153
+ for (f, c), j in cols.items():
154
+ if x[j] != 0:
155
+ ADD_TABLES.setdefault(f, {})[c] = int(x[j])
156
+ nz = int((x != 0).sum())
157
+ vmax = max(abs(v) for tab in ADD_TABLES.values() for v in tab.values())
158
+ print(f"additive matcher: {len(words) - len(zero)} words + {len(zero)} zero-pinned, {len(cols)} candidate values, support {nz}, max |value| {vmax}, "
159
+ f"unsolved {unsolved}, max residual {np.abs(res).max():g}")
160
+ assert unsolved == 0, "additive system not solvable with these features"
161
+ return vmax
162
+
163
+
164
+ def word_hash(w):
165
+ h = 0
166
+ for i, ch in enumerate(w.encode()):
167
+ c = code_of(ch)
168
+ h = BASE * h + c if i < WIN else h + c + 8 * i
169
+ return h
170
+
171
+
172
+ # ---------------------------------------------------------------- vocabulary
173
+ import pygments.lexers.python as _pl
174
+ _src = Path(_pl.__file__).read_text()
175
+ _b = _src.index("'builtins': [")
176
+ _a = _src.index("'magicfuncs': [")
177
+ _v = _src.index("'magicvars': [")
178
+ BUILTINS = set(re.findall(r"'(\w+)'", _src[_b:_a])) - {"builtins", "exceptions"} | {"self", "cls", "Ellipsis", "NotImplemented"}
179
+ MAGIC = set(re.findall(r"'(__\w+__)'", _src[_a:_v])) - BUILTINS
180
+ KEYWORDS = set(keyword.kwlist)
181
+ # frequency order measured once on accuracy2's fit split (vocab_prio.txt); words below its cut-off come last, alphabetically
182
+ PRIO = [w for w, _ in eval("[" + (HERE / "vocab_prio.txt").read_text() + "]")]
183
+
184
+
185
+ def pick_vocab(limit):
186
+ """Keywords always; builtins and magic words by fit-corpus frequency up to `limit`."""
187
+ ranked = [w for w in PRIO if w in BUILTINS or w in MAGIC]
188
+ ranked += sorted((BUILTINS | MAGIC) - set(ranked))
189
+ chosen = ranked if limit is None else ranked[:limit]
190
+ return {w: ("BI" if w in BUILTINS else "MAG") for w in chosen} | {w: "KW" for w in KEYWORDS}
191
+
192
+
193
+ # ---------------------------------------------------------------- the network, unit by unit
194
+ class Unit:
195
+ def __init__(self, name, ins, b=0.0, hi=1.0, guard=None, fam=None):
196
+ self.name, self.ins, self.b, self.hi, self.guard, self.fam = name, ins, float(b), float(hi), guard, fam
197
+
198
+
199
+ UNITS = []
200
+
201
+
202
+ def u(name, b=0.0, hi=1.0, guard=None, fam=None, **ins):
203
+ """ins: ref -> weight; refs written as x_feat (byte feature), p_name (previous state) or a unit name."""
204
+ d = {}
205
+ for k, w in ins.items():
206
+ ref = "x." + k[2:] if k.startswith("x_") else "p." + k[2:] if k.startswith("p_") else "u." + k
207
+ d[ref] = float(w)
208
+ UNITS.append(Unit(name, d, b, hi, guard, fam))
209
+ return name
210
+
211
+
212
+ STATE = ["S", "sq1", "T", "F", "E", "c", "fo", "FS", "FD", "oq1", "oT", "C", "N", "ee", "W", "d", "pf", "fp",
213
+ "cD", "cC", "cAt", "atp", "cDot"]
214
+ STATE += ["h1"] if MATCHER == "hash" else [f"pos{p}" for p in range(ADD_P)] + ["tail", "A", "e1", "e2", "e2d", "e3", "e3d", "e3dd"]
215
+ STATE_UNIT = {s: s + "_new" for s in STATE}
216
+ for _p in range(ADD_P):
217
+ STATE_UNIT[f"pos{_p}"] = f"pos{_p}"
218
+ STATE_UNIT["tail"] = "tail"
219
+ CATS = ["KW", "BI", "MAG"]
220
+
221
+
222
+ def build(vocab):
223
+ UNITS.clear()
224
+ # ---- layer 0: from the byte and the previous state only
225
+ u("wordch", guard=["x.code"], x_code=1) # word character (any code > 0)
226
+ u("qmd", guard=["x.dq"], x_dq=1, p_sq1=-1, p_E=-1) # unescaped matching " in a "-string
227
+ u("qms", guard=["x.sq"], x_sq=1, p_sq1=1, p_E=-1, b=-1) # unescaped matching ' in a '-string
228
+ u("c2", guard=["p.c"], p_c=1, b=-1) # quote run >= 2
229
+ if MATCHER == "hash":
230
+ u("lt7", p_d=-1, b=WIN) # fewer than WIN chars of the word seen
231
+ u("wend", guard=["p.W"], p_W=1, x_code=-1) # a word ended just before this byte
232
+ u("fieldopen", guard=["p.fo"], p_fo=1, x_lbr=-1) # { was a field start: this byte is code
233
+ u("pf_new", guard=["x.po", "x.pf"], x_po=3, x_pf=3, p_pf=2, p_W=-1, p_d=-1, b=-2) # word so far could be a string prefix
234
+ # ---- strings. S_eff: this byte is inside a string (p.S unless "" just closed or a field just opened)
235
+ u("S_eff", guard=["p.S"], p_S=4, fieldopen=-4, p_T=1, qmd=1, qms=1, c2=-1, b=-3)
236
+ u("fp_new", guard=["x.pf", "p.fp"], x_pf=1, p_fp=1, wordch=1, b=-1) # word contains f
237
+ u("sclose", guard=["u.S_eff"], S_eff=2, p_T=-1, qmd=1, qms=1, p_c=-1, b=-2)
238
+ u("tclose", guard=["u.S_eff"], S_eff=3, p_T=1, qmd=1, qms=1, p_c=1, b=-6)
239
+ u("tripleopen", guard=["u.S_eff"], S_eff=3, p_T=-1, qmd=1, qms=1, p_c=1, b=-5)
240
+ u("nlclose", guard=["u.S_eff"], S_eff=1, p_T=-1, x_nl=1, p_E=-1, b=-1)
241
+ u("E_new", guard=["u.S_eff"], S_eff=1, x_bs=1, p_E=-1, b=-1)
242
+ u("fo_new", guard=["u.S_eff"], S_eff=1, p_F=1, x_lbr=1, p_E=-1, p_fo=-1, b=-2)
243
+ u("C_new", guard=["x.hash", "p.C"], x_hash=1, S_eff=-1, p_C=1, x_nl=-1)
244
+ u("open", guard=["x.dq", "x.sq"], x_dq=1, x_sq=1, S_eff=-1, p_C=-1, p_N=-1)
245
+ u("open_f", guard=["x.dq", "x.sq"], x_dq=1, x_sq=1, S_eff=-1, p_C=-1, p_N=-1, p_pf=1, p_fp=1, b=-2)
246
+ u("open_qs", guard=["x.sq"], x_dq=1, x_sq=2, S_eff=-1, p_C=-1, p_N=-1, b=-1)
247
+ u("fieldclose", guard=["p.FS"], p_FS=1, S_eff=-1, p_C=-1, p_FD=-1, x_fcch=1, b=-1)
248
+ u("inc", guard=["p.FS"], p_FS=1, S_eff=-1, p_C=-1, x_obr=1, b=-1)
249
+ u("dec", guard=["p.FS"], p_FS=4, S_eff=-4, p_C=-4, x_cbr=1, p_FD=1, b=-5)
250
+ u("S_new", guard=["u.S_eff", "u.open", "u.fieldclose"], S_eff=1, sclose=-1, tclose=-1, nlclose=-1, open=1, fieldclose=1)
251
+ u("c_new", hi=3, guard=["u.S_eff", "u.open", "u.open_qs"], p_c=1, S_eff=10, qmd=5, qms=5, tripleopen=-5, tclose=-5, sclose=-5,
252
+ open=10, open_qs=5, b=-14)
253
+ u("T_new", guard=["p.T", "u.tripleopen", "u.fieldclose"], p_T=2, tripleopen=2, tclose=-2, open=-2, fieldclose=1, p_oT=1, b=-1)
254
+ u("F_new", guard=["p.F", "u.open_f", "u.fieldclose"], p_F=1, open_f=2, open=-1, sclose=-1, tclose=-1, nlclose=-1, fieldclose=1)
255
+ u("sq1_new", guard=["p.sq1", "u.open_qs", "u.S_eff", "u.fieldclose"], p_sq1=2, open_qs=4, sclose=-2, tclose=-2, nlclose=-2,
256
+ p_S=-2, S_eff=2, fieldclose=1, p_oq1=1, b=-1) # quote type; cleared when the string dies (unless a nested one opens); restored from oq1
257
+ u("FS_new", guard=["p.FS", "u.fieldopen"], p_FS=1, fieldopen=1, fieldclose=-1)
258
+ u("FD_new", hi=3, guard=["p.FD", "u.inc"], p_FD=1, inc=1, dec=-1)
259
+ u("oq1_new", guard=["p.oq1", "u.fieldopen"], p_oq1=2, fieldopen=1, p_sq1=1, fieldclose=-2, b=-1) # outer quote type, saved at a field's start
260
+ u("oT_new", guard=["p.oT", "u.fieldopen"], p_oT=2, fieldopen=1, p_T=1, fieldclose=-2, b=-1)
261
+ u("lab_STR", guard=["u.S_eff", "u.open", "u.fieldclose"], S_eff=1, nlclose=-1, open=1, fieldclose=1)
262
+ # ---- numbers and words
263
+ u("N_new", guard=["x.digit", "p.N"], x_digit=6, p_W=-3, p_N=5, wordch=2, x_dot=2, x_pm=1, p_ee=1, S_eff=-2, p_C=-2, b=-6)
264
+ u("ee_new", guard=["u.N_new"], x_eE=1, N_new=1, b=-1) # an exponent e: a sign may follow
265
+ u("W_new", guard=["u.wordch", "p.W"], wordch=2, x_digit=-1, p_W=1, p_N=-2, S_eff=-2, p_C=-2, b=-1)
266
+ u("d_new", hi=30, guard=["u.W_new"], p_d=1, W_new=40, b=-39)
267
+ if MATCHER == "hash":
268
+ u("gA", hi=HMAX, guard=["p.W"], p_h1=1, p_W=KG, wordch=KG, lt7=KG, b=-3 * KG) # old hash, still in the window
269
+ u("gB", hi=HMAX, guard=["p.W"], p_h1=1, p_W=KG, wordch=KG, lt7=-KG, b=-2 * KG) # old hash, past the window
270
+ u("cA", hi=HMAX, guard=["u.W_new"], x_code=1, W_new=KG, lt7=KG, b=-2 * KG) # this char's code, in window
271
+ u("cB", hi=HMAX, guard=["u.W_new"], x_code=1, p_d=8, W_new=KG, lt7=-KG, b=-KG) # past the window: code + 8*position
272
+ u("h1_new", hi=HMAX, gA=BASE, gB=1, cA=1, cB=1)
273
+ else:
274
+ build_additive(vocab)
275
+ if MATCHER == "add":
276
+ u("is_def", guard=["u.wend"], wend=1, ge2=1, le2=1, b=-2)
277
+ u("is_class", guard=["u.wend"], wend=1, ge3=1, le3=1, b=-2)
278
+ u("cD_new", guard=["p.cD", "u.is_def"], p_cD=1, is_def=2, wend=-1, x_nl=-1)
279
+ u("cC_new", guard=["p.cC", "u.is_class"], p_cC=1, is_class=2, wend=-1, x_nl=-1)
280
+ u("atp_new", guard=["x.at"], x_at=1, S_eff=-1, p_C=-1)
281
+ u("cAt_new", guard=["p.cAt", "p.atp"], p_cAt=1, p_atp=1, wordch=1, wend=-1, b=-1)
282
+ u("cDot_new", guard=["x.dot", "p.cDot"], x_dot=4, S_eff=-4, p_C=-4, p_N=-4, p_cDot=2, wordch=1, wend=-1, b=-2)
283
+ u("ev_DEC", guard=["u.wend"], wend=1, p_cAt=1, b=-1)
284
+ u("ev_CL", guard=["u.wend"], wend=1, p_cC=1, p_cAt=-1, b=-1)
285
+ # keyword targets 1, 2 (def), 3 (class): a value in one of the three windows scores 4 on the sorted sum, 3 otherwise
286
+ u("ev_KW", guard=["u.wend"], wend=3, ge1=1, le1=1, ge2=1, le2=1, ge3=1, le3=1, p_cAt=-1, p_cC=-1, p_cD=-1, b=-6)
287
+ u("ev_FN", guard=["u.wend"], wend=3, ge5=1, le5=1, p_cD=2, p_cAt=-2, p_cC=-2, b=-4) # after def, or a magic method name
288
+ u("ev_BI", guard=["u.wend"], wend=1, ge4=1, le4=1, p_cAt=-1, p_cC=-1, p_cD=-1, p_cDot=-1, b=-2)
289
+ u("ev_STR", guard=["u.wend"], wend=1, open=1, p_pf=1, b=-2)
290
+ u("lab_W", guard=["u.W_new", "u.atp_new"], W_new=1, atp_new=1)
291
+ return [], []
292
+ # ---- vocabulary: sorted thresholds (one tied family), one distance unit per category
293
+ byhash = {}
294
+ for w, cat in sorted(vocab.items()):
295
+ byhash.setdefault(word_hash(w), set()).add(cat)
296
+ for h, cats in byhash.items():
297
+ assert len(cats) == 1, ("hash collision across categories", h, cats)
298
+ hashes = sorted(byhash)
299
+ cat_of = [next(iter(byhash[h])) for h in hashes]
300
+ for k, h in enumerate(hashes):
301
+ u(f"ge_{k}", p_h1=1, b=1 - h, fam="ge")
302
+ for cat in CATS:
303
+ prev = 0
304
+ deltas = {}
305
+ for k, h in enumerate(hashes):
306
+ if cat_of[k] == cat:
307
+ deltas[f"ge_{k}"] = -(h - prev)
308
+ prev = h
309
+ u(f"E_{cat}", p_h1=1, **deltas) # 0 exactly when h is a category word, else 1
310
+ kdef, kclass = hashes.index(word_hash("def")), hashes.index(word_hash("class"))
311
+ nxt = lambda k: {f"ge_{k + 1}": -1} if k + 1 < len(hashes) else {}
312
+ u("is_def", guard=["u.wend"], wend=1, E_KW=-1, b=-1, **{f"ge_{kdef}": 1}, **nxt(kdef))
313
+ u("is_class", guard=["u.wend"], wend=1, E_KW=-1, b=-1, **{f"ge_{kclass}": 1}, **nxt(kclass))
314
+ # ---- contexts: after def, after class, after @, after a dot
315
+ u("cD_new", guard=["p.cD", "u.is_def"], p_cD=1, is_def=2, wend=-1, x_nl=-1)
316
+ u("cC_new", guard=["p.cC", "u.is_class"], p_cC=1, is_class=2, wend=-1, x_nl=-1)
317
+ u("atp_new", guard=["x.at"], x_at=1, S_eff=-1, p_C=-1)
318
+ u("cAt_new", guard=["p.cAt", "p.atp"], p_cAt=1, p_atp=1, wordch=1, wend=-1, b=-1)
319
+ u("cDot_new", guard=["x.dot", "p.cDot"], x_dot=4, S_eff=-4, p_C=-4, p_N=-4, p_cDot=2, wordch=1, wend=-1, b=-2)
320
+ # ---- events at a word's end (read by the backward pass) and the word label
321
+ u("ev_DEC", guard=["u.wend"], wend=1, p_cAt=1, b=-1)
322
+ u("ev_CL", guard=["u.wend"], wend=1, p_cC=1, p_cAt=-1, b=-1)
323
+ u("ev_KW", guard=["u.wend"], wend=1, E_KW=-1, p_cAt=-1, p_cC=-1, p_cD=-1)
324
+ u("ev_FN", guard=["u.wend"], wend=2, E_MAG=-1, p_cD=1, p_cAt=-1, p_cC=-1, b=-1) # after def, or a magic method name
325
+ u("ev_BI", guard=["u.wend"], wend=1, E_BI=-1, p_cAt=-1, p_cC=-1, p_cD=-1, p_cDot=-1)
326
+ u("ev_STR", guard=["u.wend"], wend=1, open=1, p_pf=1, b=-2) # the word was a string prefix
327
+ u("lab_W", guard=["u.W_new", "u.atp_new"], W_new=1, atp_new=1)
328
+ return hashes, cat_of
329
+
330
+
331
+ def build_additive(vocab):
332
+ """Additive retrieval: A accumulates shifted position values over the word, three delayed last-byte values are added
333
+ at the end, and the class is read from windows around the targets."""
334
+ vmax = solve_additive(vocab)
335
+ C = 2 * vmax # shift: every gated value (+ its position constant) is >= 0; the readout subtracts C per byte
336
+ M = 4 * C + 4 # gate constant
337
+ AMAX = 2 ** 45; K = 2 ** 46
338
+ u("pos0", guard=["u.W_new"], W_new=1, p_W=-1) # first byte of a word
339
+ for p in range(1, ADD_P):
340
+ u(f"pos{p}", guard=[f"p.pos{p - 1}"], **{f"p_pos{p - 1}": 1}, W_new=1, b=-1)
341
+ u("tail", guard=[f"p.pos{ADD_P - 1}", "p.tail"], **{f"p_pos{ADD_P - 1}": 1}, p_tail=1, W_new=1, b=-1)
342
+ for p in range(ADD_P):
343
+ u(f"a{p}", hi=2 * C, guard=[f"u.pos{p}"], **{f"x_v{p}": 1, f"pos{p}": M}, b=C - M + ADD_TABLES.get(f"v{p}", {}).get(0, 0))
344
+ u("at_", hi=2 * C, guard=["u.tail"], x_vt=1, tail=M, b=C - M + ADD_TABLES.get("vt", {}).get(0, 0))
345
+ u("A_new", hi=AMAX, guard=["u.W_new"], p_A=1, W_new=K, b=-K, **{f"a{p}": 1 for p in range(ADD_P)}, at_=1)
346
+ for j in range(1, ADD_L + 1):
347
+ u(f"e{j}_new", hi=2 * C, guard=["u.W_new"], **{f"x_e{j}": 1}, W_new=M, b=C - M)
348
+ u("e2d_new", hi=2 * C, guard=["p.e2"], p_e2=1)
349
+ u("e3d_new", hi=2 * C, guard=["p.e3"], p_e3=1)
350
+ u("e3dd_new", hi=2 * C, guard=["p.e3d"], p_e3d=1)
351
+ u("m3", hi=3, guard=["p.d"], p_d=1) # min(length, 3): last-byte terms present
352
+ u("SE", hi=AMAX, p_A=1, p_e1=1, p_e2d=1, p_e3dd=1, p_d=-C, m3=-C) # the word's value sum
353
+ for t in (1, 2, 3, 4, 5):
354
+ u(f"ge{t}", SE=1, b=1 - (t * ADD_T - ADD_EPS))
355
+ u(f"le{t}", SE=-1, b=1 + (t * ADD_T + ADD_EPS))
356
+
357
+
358
+ # Kernel blocks: a unit is computed inside the first block whose condition (any listed atom non-zero) provably
359
+ # covers its guard, following guard chains down to byte features and previous state. Everything else is unconditional.
360
+ BLOCKS = [("vocab", ["u.wend"]),
361
+ ("str", ["p.S", "p.fo", "p.FS", "p.FD", "p.c", "p.T", "p.F", "p.sq1", "p.oq1", "p.oT", "x.dq", "x.sq"]),
362
+ ("word", ["p.W", "x.code", "x.dot", "x.at", "p.atp", "p.cD", "p.cC", "p.cAt", "p.cDot", "x.po", "x.pf", "p.fp", "p.d"]
363
+ + ([] if MATCHER == "hash" else [f"p.pos{p}" for p in range(ADD_P)] + ["p.tail", "p.e2", "p.e3", "p.e3d"])),
364
+ ("num", ["x.digit", "p.N", "p.ee"]),
365
+ ("cmt", ["x.hash", "p.C"])]
366
+
367
+ BITS = ["lab_STR", "C_new", "N_new", "lab_W", "ev_KW", "ev_BI", "ev_FN", "ev_CL", "ev_DEC", "ev_STR"]
368
+ EV = ["KW", "BI", "FN", "CL", "DEC", "STR"]
369
+ CLASSES = ["text", "keyword", "builtin", "string", "comment", "number", "func", "class", "decorator"]
370
+ BSTATE = ["u.reg_" + e for e in EV] + ["b.ev_" + e for e in EV]
371
+ # class id read out by one integer unit: direct labels, or the carried word class on word bytes
372
+ HEAD = {"b.lab_STR": 3, "b.C_new": 4, "b.N_new": 5, "u.reg_KW": 1, "u.reg_BI": 2, "u.reg_STR": 3, "u.reg_FN": 6, "u.reg_CL": 7, "u.reg_DEC": 8}
373
+
374
+
375
+ # ---------------------------------------------------------------- layering and matrices
376
+ def layer_units(units):
377
+ """Layers by dependency on same-step units (guards count); a family gets a layer of its own (tied weights)."""
378
+ byname = {x.name: x for x in units}
379
+ lvl = {}
380
+
381
+ def L(n):
382
+ if n in lvl:
383
+ return lvl[n]
384
+ x = byname[n]
385
+ deps = [r for r in x.ins if r.startswith("u.")] + [g for g in (x.guard or []) if g.startswith("u.")]
386
+ lvl[n] = 1 + max([L(r[2:]) for r in deps], default=-1)
387
+ return lvl[n]
388
+ for x in units:
389
+ L(x.name)
390
+ layers = []
391
+ for l in range(1 + max(lvl.values())):
392
+ xs = [x for x in units if lvl[x.name] == l]
393
+ for fam in sorted({x.fam for x in xs if x.fam}):
394
+ layers.append([x for x in xs if x.fam == fam])
395
+ rest = [x for x in xs if not x.fam]
396
+ if rest:
397
+ layers.append(rest)
398
+ return layers
399
+
400
+
401
+ def check_guards(units, emb):
402
+ """Same proof as the C loader: with the guard inputs at 0, bias plus every other positive term at its ceiling must be <= 0."""
403
+ hi = {"u." + x.name: x.hi for x in units}
404
+ for s_, un in STATE_UNIT.items():
405
+ if "u." + un in hi: # matcher-specific state units exist only in that matcher's build
406
+ hi["p." + s_] = hi["u." + un]
407
+ for f in FEATS:
408
+ hi["x." + f] = float(emb[:, FI[f]].max())
409
+ bad = []
410
+ for x in units:
411
+ if not x.guard or getattr(x, "inherited", False):
412
+ continue
413
+ bound = x.b + sum(w * hi[r] for r, w in x.ins.items() if w > 0 and r not in x.guard)
414
+ if bound > 0:
415
+ bad.append((x.name, bound))
416
+ assert not bad, f"guards not provable: {bad}"
417
+
418
+
419
+ def inherit_guards(units):
420
+ """A unit that is not an output and whose consumers all share one guard set inherits it (it is dead otherwise)."""
421
+ cons = {x.name: [] for x in units}
422
+ for x in units:
423
+ for r in x.ins:
424
+ if r.startswith("u."):
425
+ cons[r[2:]].append(x)
426
+ outputs = set(STATE_UNIT.values()) | set(BITS)
427
+ changed = True
428
+ while changed:
429
+ changed = False
430
+ for x in units:
431
+ if x.guard is not None or x.name in outputs or not cons[x.name]:
432
+ continue
433
+ gs = {tuple(c.guard) if c.guard is not None else None for c in cons[x.name]}
434
+ if len(gs) == 1 and None not in gs:
435
+ x.guard = list(gs.pop())
436
+ x.inherited = True
437
+ changed = True
438
+
439
+
440
+ def build_cell(layers, inputs, outputs_needed):
441
+ """Layered units -> (W, b, hi, skip, names, tied) over a growing vector, lexnet style.
442
+ A tied layer stores one weight row and one ceiling for all its rows (a threshold family)."""
443
+ vec = list(inputs)
444
+ mats = []
445
+ later_needs = []
446
+ for l, xs in enumerate(layers):
447
+ need = set(outputs_needed)
448
+ for ys in layers[l + 1:]:
449
+ for y in ys:
450
+ need |= set(y.ins)
451
+ later_needs.append(need)
452
+ for l, xs in enumerate(layers):
453
+ idx = {n: i for i, n in enumerate(vec)}
454
+ tied = all(x.fam for x in xs)
455
+ if tied:
456
+ assert all(x.ins == xs[0].ins and x.hi == xs[0].hi for x in xs)
457
+ W = np.zeros((1 if tied else len(xs), len(vec)))
458
+ b = np.zeros(len(xs)); hi = np.zeros(1 if tied else len(xs))
459
+ for r, x in enumerate(xs):
460
+ if not tied or r == 0:
461
+ for ref, w in x.ins.items():
462
+ W[r, idx[ref]] = w
463
+ hi[r] = x.hi
464
+ b[r] = x.b
465
+ skip = [i for i, n in enumerate(vec) if n in later_needs[l]]
466
+ mats.append((W, b, hi, np.array(skip, dtype=np.int64), [x.name for x in xs], tied))
467
+ vec = ["u." + x.name for x in xs] + [vec[i] for i in skip]
468
+ return mats, vec
469
+
470
+
471
+ # ---------------------------------------------------------------- weights file
472
+ def write_weights(path, emb, fwd, fvec, bwd, bvec, head_W, head_b, h0):
473
+ out = bytearray(b"PYLX02\0\0")
474
+ out += struct.pack("<6q", NF, len(STATE), len(BITS), len(BSTATE), len(CLASSES), head_W.shape[1])
475
+ out += emb.astype("<f8").tobytes()
476
+ for cell in (fwd, bwd):
477
+ out += struct.pack("<q", len(cell))
478
+ for W, b, hi, skip, names, tied in cell:
479
+ out += struct.pack("<4q", len(names), W.shape[1], len(skip), int(tied))
480
+ out += W.astype("<f8").tobytes() + b.astype("<f8").tobytes() + hi.astype("<f8").tobytes() + skip.astype("<q").tobytes()
481
+ f_state = [fvec.index("u." + STATE_UNIT[s]) for s in STATE]
482
+ f_out = [fvec.index("u." + bname) for bname in BITS]
483
+ b_state = [bvec.index(n) for n in BSTATE]
484
+ out += np.array(f_state, dtype="<q").tobytes() + np.array(f_out, dtype="<q").tobytes() + np.array(b_state, dtype="<q").tobytes()
485
+ out += h0.astype("<f8").tobytes() + head_W.astype("<f8").tobytes() + head_b.astype("<f8").tobytes()
486
+ Path(path).write_bytes(out)
487
+ return len(out)
488
+
489
+
490
+ def count(emb, fwd, bwd, head_W, head_b, h0):
491
+ """Every non-zero number the network uses: embedding, weights, biases, ceilings, head, initial state."""
492
+ nz = lambda a: int((np.asarray(a) != 0).sum())
493
+ tied = nz(emb) + nz(head_W) + nz(head_b) + nz(h0) + 1 # +1: the head ceiling
494
+ untied = tied
495
+ for cell in (fwd, bwd):
496
+ for W, b, hi, skip, names, t in cell:
497
+ tied += nz(W) + nz(b) + nz(hi)
498
+ untied += (nz(W) + nz(hi)) * (len(names) if t else 1) + nz(b)
499
+ return tied, untied
500
+
501
+
502
+ # ---------------------------------------------------------------- kernel codegen
503
+ def struct_hash(fwd, bwd, f_state, f_out, b_state, head_W, head_cols):
504
+ """FNV-1a over shapes, ceilings, skips, sparsity masks and index maps; identical to the C loader's."""
505
+ h = 0xCBF29CE484222325
506
+
507
+ def by(bs):
508
+ nonlocal h
509
+ for c in bs:
510
+ h ^= c; h = (h * 0x100000001B3) & 0xFFFFFFFFFFFFFFFF
511
+
512
+ def i64(x):
513
+ by(struct.pack("<q", x))
514
+ i64(NF); i64(len(STATE)); i64(len(BITS)); i64(len(BSTATE)); i64(len(CLASSES)); i64(head_cols)
515
+ for cell in (fwd, bwd):
516
+ i64(len(cell))
517
+ for W, b, hi, skip, names, tied in cell:
518
+ i64(len(names)); i64(W.shape[1]); i64(len(skip)); i64(int(tied))
519
+ by(hi.astype("<f8").tobytes())
520
+ for s in skip:
521
+ i64(int(s))
522
+ by(bytes((W != 0).astype(np.uint8).ravel()))
523
+ for i in f_state: i64(i)
524
+ for i in f_out: i64(i)
525
+ for i in b_state: i64(i)
526
+ by(bytes((head_W != 0).astype(np.uint8).ravel()))
527
+ return h
528
+
529
+
530
+ def cfmt(v):
531
+ return repr(float(v)) if v != int(v) else f"{int(v)}.0"
532
+
533
+
534
+ def tree(terms):
535
+ """Balanced sum: every value is an exact integer, so the order does not change the result but halves the latency."""
536
+ while len(terms) > 1:
537
+ terms = [f"({terms[i]} + {terms[i + 1]})" if i + 1 < len(terms) else terms[i] for i in range(0, len(terms), 2)]
538
+ return terms[0]
539
+
540
+
541
+ _FVEC = {}
542
+
543
+
544
+ def fvec_index(cell, l, ref):
545
+ return _FVEC[(id(cell), l)][ref]
546
+
547
+
548
+ def gen_kernel(path, fwd, fvec, bwd, bvec, head_W, head_b, head_hi, shash, units, bunits):
549
+ coef, bias, guards, families, prefixes = [], [], [], [], []
550
+ lines = []
551
+ A = lines.append
552
+ A("/* Generated by compile.py: the network's ARCHITECTURE as C. No weight values live here: pylexnet.c")
553
+ A(" * loads them, checks the structure hash and the guard inequalities, and gathers kc[]/kb[] by K_COEF/K_BIAS. */")
554
+ A("#include <stdint.h>")
555
+ A("#include <string.h>")
556
+ A(f"#define K_STRUCT_HASH 0x{shash:016X}ULL")
557
+ A(f"#define K_N_FEATS {NF}")
558
+ A(f"#define K_N_CLASSES {len(CLASSES)}")
559
+ A(f"#define K_N_BITS {len(BITS)}")
560
+ A("#ifndef CLIP")
561
+ A("#define CLIP(x, hi) clipn((x), (hi))")
562
+ A("static inline double clipn(double x, double hi) { x = x < 0.0 ? 0.0 : x; return x > hi ? hi : x; }")
563
+ A("#endif")
564
+ A("/* A tied, sorted threshold family is a step vector: unit k is 1 iff kb[k] + w*h >= 1, true for k < rank.")
565
+ A(" * The loader verifies integrality, tying and monotonicity, so a binary search gives the same values. */")
566
+ A("static inline int k_rank(const double *kb, int n, double w, double h) {")
567
+ A(" int lo = 0, hi = n;")
568
+ A(" while (lo < hi) { int mid = (lo + hi) >> 1; if (kb[mid] + w * h >= 1.0) lo = mid + 1; else hi = mid; }")
569
+ A(" return lo;")
570
+ A("}")
571
+ A("typedef struct { double " + ", ".join(STATE) + "; } FState;")
572
+ A("typedef struct { double " + ", ".join(n.replace(".", "_") for n in BSTATE) + "; } BState;")
573
+ A("static inline void k_init_state(FState *s, const double *h0) { memcpy(s, h0, sizeof *s); }")
574
+ # ---- forward
575
+ A("static inline __attribute__((always_inline)) uint16_t k_fwd_step(const double *restrict kc, const double *restrict kb, const double *restrict kp, const double *restrict x,")
576
+ A(" const FState *restrict p, FState *restrict s) {")
577
+ byname = {x.name: x for x in units}
578
+ famidx, fams = {}, {}
579
+ for x in units:
580
+ if x.fam:
581
+ famidx[x.name] = len(fams.setdefault(x.fam, []))
582
+ fams[x.fam].append(x)
583
+
584
+ def ref(r):
585
+ if r.startswith("x."):
586
+ return f"x[{FI[r[2:]]}]"
587
+ if r.startswith("p."):
588
+ return f"p->{r[2:]}"
589
+ x = byname[r[2:]]
590
+ return f"(double)({famidx[x.name]} < r_{x.fam})" if x.fam else f"u_{x.name}"
591
+ scalars = [x.name for x in units if not x.fam]
592
+ A(" double " + ", ".join(f"u_{n} = 0.0" for n in scalars) + ";")
593
+ for fam in fams:
594
+ A(f" int r_{fam} = 0;")
595
+
596
+ def covered(atom, cond, seen=()):
597
+ if atom in cond:
598
+ return True
599
+ if not atom.startswith("u.") or atom in seen:
600
+ return False
601
+ g = byname[atom[2:]].guard
602
+ return g is not None and all(covered(a, cond, seen + (atom,)) for a in g)
603
+ blk = {}
604
+ for x in units:
605
+ blk[x.name] = None
606
+ if x.guard:
607
+ for bname, cond in BLOCKS:
608
+ if all(covered(a, cond) for a in x.guard):
609
+ blk[x.name] = bname
610
+ break
611
+ cond_of = dict(BLOCKS)
612
+ layer_of = {}
613
+ for l, (W, b, hi, skip, names, tied) in enumerate(fwd):
614
+ for row, n in enumerate(names):
615
+ layer_of[n] = (l, row)
616
+
617
+ def deps(x):
618
+ d = {r[2:] for r in x.ins if r.startswith("u.")}
619
+ if blk[x.name]:
620
+ d |= {a[2:] for a in cond_of[blk[x.name]] if a.startswith("u.")}
621
+ return d
622
+ remaining = {x.name for x in units}
623
+ done = set()
624
+ cur = None
625
+ nseg = 0
626
+ while remaining:
627
+ ready = [byname[n] for n in remaining if deps(byname[n]) <= done]
628
+ assert ready, "cyclic dependency"
629
+ bybl = {}
630
+ for x in ready:
631
+ bybl.setdefault(blk[x.name], []).append(x)
632
+ if cur not in bybl:
633
+ cur = max(bybl, key=lambda k: (len(bybl[k]), k is None))
634
+ if cur is not None:
635
+ A(" if ((" + " + ".join(ref(a) for a in cond_of[cur]) + ") != 0.0) { /* all activations are >= 0 */")
636
+ nseg += 1
637
+ batch = sorted(bybl[cur], key=lambda x: (layer_of[x.name], x.fam or "", famidx.get(x.name, 0)))
638
+ indent = " " if cur is not None else " "
639
+ i = 0
640
+ while i < len(batch):
641
+ x = batch[i]
642
+ l, row = layer_of[x.name]
643
+ if x.fam:
644
+ j = i
645
+ while j < len(batch) and batch[j].fam == x.fam and famidx[batch[j].name] == famidx[x.name] + (j - i):
646
+ j += 1
647
+ run = batch[i:j]
648
+ assert len(run) == len(fams[x.fam]) and famidx[x.name] == 0, "a family is emitted whole"
649
+ (inref, _), = run[0].ins.items()
650
+ cb, bb = len(coef), len(bias)
651
+ coef.append((0, l, 0, fvec_index(fwd, l, inref))) # tied: one weight for the family
652
+ for y in run:
653
+ bias.append((0,) + layer_of[y.name])
654
+ families.append((0, l, bb, len(run), cb))
655
+ A(f"{indent}r_{x.fam} = k_rank(kb + {bb}, {len(run)}, kc[{cb}], {ref(inref)});")
656
+ i = j
657
+ continue
658
+ terms = [f"kb[{len(bias)}]"]
659
+ bias.append((0, l, row))
660
+ famins = {}
661
+ for inref, w in x.ins.items():
662
+ y = byname[inref[2:]] if inref.startswith("u.") else None
663
+ if y is not None and y.fam:
664
+ famins.setdefault(y.fam, []).append(inref)
665
+ for inref, w in x.ins.items():
666
+ y = byname[inref[2:]] if inref.startswith("u.") else None
667
+ if y is not None and y.fam and len(famins[y.fam]) >= 4:
668
+ if inref == famins[y.fam][0]:
669
+ # all of this family's weights into the unit, as a prefix sum indexed by the rank (built at load)
670
+ cols = [fvec_index(fwd, l, "u." + z.name) for z in fams[y.fam]]
671
+ assert cols == list(range(cols[0], cols[0] + len(cols))), "family columns are contiguous"
672
+ pb = sum(n + 1 for _, _, _, _, n in prefixes)
673
+ prefixes.append((0, l, row, cols[0], len(cols)))
674
+ terms.append(f"kp[{pb} + r_{y.fam}]")
675
+ continue
676
+ terms.append(f"kc[{len(coef)}] * {ref(inref)}")
677
+ coef.append((0, l, row, fvec_index(fwd, l, inref)))
678
+ A(f"{indent}u_{x.name} = CLIP({tree(terms)}, {cfmt(x.hi)});")
679
+ if x.guard and not getattr(x, "inherited", False):
680
+ guards.append((0, l, row, [fvec_index(fwd, l, gr) for gr in x.guard]))
681
+ i += 1
682
+ for x in batch:
683
+ done.add(x.name); remaining.discard(x.name)
684
+ nxt = [n for n in remaining if blk[n] == cur and deps(byname[n]) <= done]
685
+ if not nxt and cur is not None:
686
+ A(" }")
687
+ cur = None
688
+ print(f"kernel: {nseg} block segments; units per block " + str({b: sum(1 for x in units if blk[x.name] == b) for b in [None] + [b for b, _ in BLOCKS]}))
689
+ for sname in STATE:
690
+ A(f" s->{sname} = u_{STATE_UNIT[sname]};")
691
+ A(" return (uint16_t)(" + " | ".join(f"((uint16_t)(u_{bn} != 0.0) << {i})" for i, bn in enumerate(BITS)) + ");")
692
+ A("}")
693
+ # ---- backward: carried word classes, then the integer class id
694
+ A("static inline __attribute__((always_inline)) int k_bwd_step(const double *restrict kc, const double *restrict kb, uint16_t bits,")
695
+ A(" const BState *restrict r, BState *restrict s) {")
696
+ for i, bn in enumerate(BITS):
697
+ A(f" const double f_{bn} = (double)((bits >> {i}) & 1);")
698
+
699
+ def bref(rf):
700
+ if rf.startswith("b."):
701
+ return "f_" + rf[2:]
702
+ if rf.startswith("r."):
703
+ return "r->" + rf[2:].replace(".", "_")
704
+ return "u_" + rf[2:]
705
+ for l, (W, b, hi, skip, names, tied) in enumerate(bwd):
706
+ for row, n in enumerate(names):
707
+ x = bunits[n]
708
+ terms = [f"kb[{len(bias)}]"]
709
+ bias.append((1, l, row))
710
+ for inref, w in x.ins.items():
711
+ terms.append(f"kc[{len(coef)}] * {bref(inref)}")
712
+ coef.append((1, l, row, fvec_index(bwd, l, inref)))
713
+ A(f" const double u_{n} = CLIP({tree(terms)}, {cfmt(x.hi)});")
714
+ for n in BSTATE:
715
+ A(f" s->{n.replace('.', '_')} = {bref(n) if not n.startswith('u.') else 'u_' + n[2:]};")
716
+ terms = [f"kb[{len(bias)}]"]
717
+ bias.append((2, 0, 0))
718
+ for c in np.nonzero(head_W[0])[0]:
719
+ terms.append(f"kc[{len(coef)}] * {bref(bvec[c])}")
720
+ coef.append((2, 0, 0, int(c)))
721
+ A(f" return (int)CLIP({tree(terms)}, {cfmt(head_hi)});")
722
+ A("}")
723
+ A(f"#define K_N_COEF {len(coef)}")
724
+ A(f"#define K_N_BIAS {len(bias)}")
725
+ A(f"#define K_N_GUARD {len(guards)}")
726
+ A(f"#define K_N_FAMILY {len(families)}")
727
+ A(f"#define K_N_PREFIX {len(prefixes)}")
728
+ A(f"#define K_PREFIX_LEN {sum(n + 1 for *_, n in prefixes)}")
729
+ A("/* sorted threshold families: {cell, layer, first bias index, rows, coef index of the tied weight} */")
730
+ A("static const int32_t K_FAMILY[K_N_FAMILY + 1][5] = {" + "".join("{%d,%d,%d,%d,%d}," % f for f in families) + "{0,0,0,0,0}};")
731
+ A("/* prefix sums of a unit's weights over a family's columns: {cell, layer, row, first column, rows} */")
732
+ A("static const int32_t K_PREFIX[K_N_PREFIX + 1][5] = {" + "".join("{%d,%d,%d,%d,%d}," % f for f in prefixes) + "{0,0,0,0,0}};")
733
+ A("#define K_MAX_GUARD 4")
734
+ A("static const int32_t K_COEF[K_N_COEF][4] = {" + ",".join("{%d,%d,%d,%d}" % c for c in coef) + "};")
735
+ A("static const int32_t K_BIAS[K_N_BIAS][3] = {" + ",".join("{%d,%d,%d}" % c for c in bias) + "};")
736
+ A("/* units whose block is skipped when the listed input columns are all zero; the loader proves each one */")
737
+ A("static const int32_t K_GUARD[K_N_GUARD][3 + K_MAX_GUARD] = {" +
738
+ ",".join("{%d,%d,%d,%s}" % (c, l, r, ",".join(str(v) for v in (cols + [-1] * 4)[:4])) for c, l, r, cols in guards) + "};")
739
+ Path(path).write_text("\n".join(lines) + "\n")
740
+ return len(coef), len(bias), len(guards)
741
+
742
+
743
+ # ---------------------------------------------------------------- simulator (debugging: a third evaluation of the same matrices)
744
+ def run_layers(v, layers):
745
+ for W, b, hi, skip, names, tied in layers:
746
+ pre = (W @ v)
747
+ if tied:
748
+ pre = np.full(len(names), pre[0])
749
+ new = np.clip(pre + b, 0, hi if not tied else hi[0])
750
+ v = np.concatenate([new, v[skip]])
751
+ return v
752
+
753
+
754
+ def simulate(text, emb, fwd, fvec, f_state, f_out, bwd, b_state, head_W, head_b, head_hi, h0):
755
+ data = text.encode() + b"\n"
756
+ st = h0.copy()
757
+ allbits = []
758
+ for t, byte in enumerate(data):
759
+ v = run_layers(np.concatenate([emb[byte if byte < 128 else 127], st]), fwd)
760
+ st = v[f_state]
761
+ bits = [int(v[i] != 0) for i in f_out]
762
+ allbits.append(bits)
763
+ on = [n[2:] + ("" if v[i] == 1 else f"={v[i]:g}") for i, n in enumerate(fvec)
764
+ if n.startswith("u.") and v[i] != 0 and not n.startswith("u.ge_")]
765
+ print(f"{t:3} {chr(byte)!r:6} bits={''.join(map(str, bits))} " + " ".join(on))
766
+ r = np.zeros(len(b_state))
767
+ out = []
768
+ for t in range(len(data) - 1, -1, -1):
769
+ v = run_layers(np.concatenate([np.array(allbits[t], dtype=float), r]), bwd)
770
+ r = v[b_state]
771
+ out.append(int(np.clip(head_W @ v + head_b, 0, head_hi)[0]))
772
+ print("classes:", "".join(str(c) for c in out[::-1][:-1]))
773
+
774
+
775
+ # ---------------------------------------------------------------- main
776
+ class BUnit:
777
+ def __init__(self, name, ins, b=0.0, hi=1.0):
778
+ self.name, self.ins, self.b, self.hi = name, ins, b, hi
779
+
780
+
781
+ def main():
782
+ limit = None
783
+ if "--vocab" in sys.argv:
784
+ limit = int(sys.argv[sys.argv.index("--vocab") + 1])
785
+ vocab = pick_vocab(limit)
786
+ hashes, cat_of = build(vocab)
787
+ check_guards(UNITS, emb_table())
788
+ inherit_guards(UNITS)
789
+ layers = layer_units(UNITS)
790
+ inputs = ["x." + f for f in FEATS] + ["p." + s for s in STATE]
791
+ needed = {"u." + STATE_UNIT[s] for s in STATE} | {"u." + b for b in BITS}
792
+ fwd, fvec = build_cell(layers, inputs, needed)
793
+ vec = list(inputs)
794
+ for l, (W, b, hi, skip, names, tied) in enumerate(fwd):
795
+ _FVEC[(id(fwd), l)] = {n: i for i, n in enumerate(vec)}
796
+ vec = ["u." + n for n in names] + [vec[i] for i in skip]
797
+ # backward cell: reg_X(i) = clip1(ev_X(i+1) + reg_X(i+1) + lab_W(i) - 1): set at a word's end, carried over
798
+ # the word's bytes, zero elsewhere. Carries this byte's event bits for the next step.
799
+ bunits = {}
800
+ for e in EV:
801
+ bunits["reg_" + e] = BUnit("reg_" + e, {"r.b.ev_" + e: 1.0, "r.u.reg_" + e: 1.0, "b.lab_W": 1.0}, b=-1.0)
802
+ binputs = ["b." + bn for bn in BITS] + ["r." + n for n in BSTATE]
803
+ bidx = {n: i for i, n in enumerate(binputs)}
804
+ Wb = np.zeros((len(EV), len(binputs))); bb = -np.ones(len(EV)); hb = np.ones(len(EV))
805
+ for r, e in enumerate(EV):
806
+ for ref, w in bunits["reg_" + e].ins.items():
807
+ Wb[r, bidx[ref]] = w
808
+ bskip = [bidx["b." + bn] for bn in BITS]
809
+ bwd = [(Wb, bb, hb, np.array(bskip, dtype=np.int64), ["reg_" + e for e in EV], False)]
810
+ _FVEC[(id(bwd), 0)] = bidx
811
+ bvec_out = ["u.reg_" + e for e in EV] + ["b." + bn for bn in BITS]
812
+ hcol = {n: i for i, n in enumerate(bvec_out)}
813
+ head_W = np.zeros((1, len(bvec_out))); head_b = np.zeros(1); head_hi = 8.0
814
+ for n, w in HEAD.items():
815
+ head_W[0, hcol[n]] = w
816
+ emb = emb_table()
817
+ h0 = np.zeros(len(STATE))
818
+ size = write_weights(HERE / "pylexnet_weights.bin", emb, fwd, fvec, bwd, bvec_out, head_W, head_b, h0)
819
+ f_state = [fvec.index("u." + STATE_UNIT[s]) for s in STATE]
820
+ f_out = [fvec.index("u." + bn) for bn in BITS]
821
+ b_state = [bvec_out.index(n) for n in BSTATE]
822
+ shash = struct_hash(fwd, bwd, f_state, f_out, b_state, head_W, head_W.shape[1])
823
+ ncoef, nbias, nguard = gen_kernel(HERE / "pylexnet_kernel.h", fwd, fvec, bwd, bvec_out, head_W, head_b, head_hi, shash, UNITS, bunits)
824
+ if "--sim" in sys.argv:
825
+ simulate(sys.argv[sys.argv.index("--sim") + 1], emb, fwd, fvec, f_state, f_out, bwd, b_state, head_W, head_b, head_hi, h0)
826
+ tied, untied = count(emb, fwd, bwd, head_W, head_b, h0)
827
+ nlog = sum(1 for x in UNITS if not x.fam and not x.name.startswith("E_"))
828
+ vocab_numbers = 2 * len(hashes) if MATCHER == "hash" else sum(len(t) for t in ADD_TABLES.values())
829
+ print(f"units {len(UNITS)} forward in {len(fwd)} layers ({nlog} logic), state {len(STATE)}, bits {len(BITS)}, vocabulary {len(vocab)} words "
830
+ f"({sum(1 for c in vocab.values() if c == 'KW')} keywords, {sum(1 for c in vocab.values() if c == 'BI')} builtins, "
831
+ f"{sum(1 for c in vocab.values() if c == 'MAG')} magic) = {len(hashes)} thresholds; kernel coef {ncoef} bias {nbias} guards {nguard}")
832
+ print(f"non-zero weights {tied} (every number: embedding {int((emb != 0).sum())}, vocabulary {vocab_numbers}; "
833
+ f"{untied} if the threshold family's weight and ceiling were not tied)")
834
+ print(f"weights file {size // 1024} KiB")
835
+
836
+
837
+ if __name__ == "__main__":
838
+ main()
demo.mp4 ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:962068fdc4eb6dab035aa97c40011bcb46e60ffd3eb8e2f77537672b3af2d99a
3
+ size 204263
pylexnet.c ADDED
@@ -0,0 +1,369 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* pylexnet (hand lane): run the hand-designed Python highlighter network.
2
+ *
3
+ * Two paths over the same weights file:
4
+ * default the generated kernel in pylexnet_kernel.h (architecture only; values gathered from the file).
5
+ * The loader checks the structure hash and proves every guard inequality from the loaded values.
6
+ * --interp the generic layered sparse evaluation (clip(W v + b, 0, hi) + carried entries), no kernel.
7
+ * usage: pylexnet [-w weights.bin] [--interp] (--classes [file] | --bench file)
8
+ * --classes writes one class byte (0..8) per input byte; without a file it reads stdin.
9
+ */
10
+ #include <math.h>
11
+ #include <stdint.h>
12
+ #include <stdio.h>
13
+ #include <stdlib.h>
14
+ #include <string.h>
15
+ #include <time.h>
16
+
17
+ typedef struct {
18
+ int64_t rows, cols, nskip, tied; /* tied: one weight row and one ceiling shared by all rows */
19
+ const double *W, *b, *hi;
20
+ const int64_t *skip;
21
+ int *rowptr, *col;
22
+ double *val;
23
+ } Layer;
24
+
25
+ typedef struct {
26
+ int64_t n_feats, n_fstate, n_fout, n_bstate, n_classes, head_cols, maxw;
27
+ const double *emb, *h0, *head_W, *head_b;
28
+ const int64_t *f_state, *f_out, *b_state;
29
+ Layer *fwd, *bwd;
30
+ int64_t n_fwd, n_bwd;
31
+ } Net;
32
+
33
+ static int use_interp = 0;
34
+
35
+ /* ---- loading ------------------------------------------------------------- */
36
+ typedef struct { const unsigned char *p, *end; } Cursor;
37
+
38
+ static const void *take(Cursor *c, size_t bytes) {
39
+ if (c->p + bytes > c->end) { fprintf(stderr, "weights: truncated\n"); exit(1); }
40
+ const void *r = c->p;
41
+ c->p += bytes;
42
+ return r;
43
+ }
44
+ static int64_t take_i64(Cursor *c) { int64_t v; memcpy(&v, take(c, 8), 8); return v; }
45
+
46
+ static Layer *load_cell(Cursor *c, int64_t *n, int64_t *maxw) {
47
+ *n = take_i64(c);
48
+ Layer *L = calloc((size_t)*n, sizeof(Layer));
49
+ for (int64_t l = 0; l < *n; l++) {
50
+ Layer *y = &L[l];
51
+ y->rows = take_i64(c); y->cols = take_i64(c); y->nskip = take_i64(c); y->tied = take_i64(c);
52
+ int64_t wrows = y->tied ? 1 : y->rows;
53
+ y->W = take(c, (size_t)(wrows * y->cols) * 8);
54
+ y->b = take(c, (size_t)y->rows * 8);
55
+ y->hi = take(c, (size_t)wrows * 8);
56
+ y->skip = take(c, (size_t)y->nskip * 8);
57
+ if (y->cols > *maxw) *maxw = y->cols;
58
+ if (y->rows + y->nskip > *maxw) *maxw = y->rows + y->nskip;
59
+ int nnz = 0;
60
+ for (int64_t i = 0; i < wrows * y->cols; i++) nnz += y->W[i] != 0;
61
+ if (y->tied) nnz *= (int)y->rows;
62
+ y->rowptr = malloc((size_t)(y->rows + 1) * sizeof(int));
63
+ y->col = malloc((size_t)(nnz + 1) * sizeof(int));
64
+ y->val = malloc((size_t)(nnz + 1) * sizeof(double));
65
+ int k = 0;
66
+ for (int64_t r = 0; r < y->rows; r++) {
67
+ y->rowptr[r] = k;
68
+ const double *w = y->W + (y->tied ? 0 : r) * y->cols;
69
+ for (int64_t cc = 0; cc < y->cols; cc++) if (w[cc] != 0) y->col[k] = (int)cc, y->val[k++] = w[cc];
70
+ }
71
+ y->rowptr[y->rows] = k;
72
+ }
73
+ return L;
74
+ }
75
+
76
+ static Net load(const char *path) {
77
+ FILE *f = fopen(path, "rb");
78
+ if (!f) { perror(path); exit(1); }
79
+ fseek(f, 0, SEEK_END);
80
+ size_t n = (size_t)ftell(f);
81
+ fseek(f, 0, SEEK_SET);
82
+ unsigned char *blob = malloc(n);
83
+ if (fread(blob, 1, n, f) != n) { perror(path); exit(1); }
84
+ fclose(f);
85
+ Cursor c = { blob, blob + n };
86
+ if (memcmp(take(&c, 8), "PYLX02\0\0", 8)) { fprintf(stderr, "%s: not a PYLX02 weight file\n", path); exit(1); }
87
+ Net net = {0};
88
+ net.n_feats = take_i64(&c); net.n_fstate = take_i64(&c); net.n_fout = take_i64(&c);
89
+ net.n_bstate = take_i64(&c); net.n_classes = take_i64(&c); net.head_cols = take_i64(&c);
90
+ net.emb = take(&c, (size_t)(128 * net.n_feats) * 8);
91
+ net.maxw = net.n_feats + net.n_fstate;
92
+ if (net.n_fout + net.n_bstate > net.maxw) net.maxw = net.n_fout + net.n_bstate;
93
+ net.fwd = load_cell(&c, &net.n_fwd, &net.maxw);
94
+ net.bwd = load_cell(&c, &net.n_bwd, &net.maxw);
95
+ net.f_state = take(&c, (size_t)net.n_fstate * 8);
96
+ net.f_out = take(&c, (size_t)net.n_fout * 8);
97
+ net.b_state = take(&c, (size_t)net.n_bstate * 8);
98
+ net.h0 = take(&c, (size_t)net.n_fstate * 8);
99
+ net.head_W = take(&c, (size_t)net.head_cols * 8); /* one integer-valued readout unit: the class id */
100
+ net.head_b = take(&c, 8);
101
+ if (c.p != c.end) { fprintf(stderr, "weights: %zu trailing bytes\n", (size_t)(c.end - c.p)); exit(1); }
102
+ return net;
103
+ }
104
+
105
+ /* ---- generic inference --------------------------------------------------- */
106
+ static void run_cell(const Layer *layers, int64_t nl, double *v, double *tmp) {
107
+ for (int64_t l = 0; l < nl; l++) {
108
+ const Layer *L = &layers[l];
109
+ for (int64_t r = 0; r < L->rows; r++) {
110
+ double s = L->b[r];
111
+ for (int k = L->rowptr[r]; k < L->rowptr[r + 1]; k++) s += L->val[k] * v[L->col[k]];
112
+ double hi = L->hi[L->tied ? 0 : r];
113
+ tmp[r] = s < 0 ? 0 : (s > hi ? hi : s);
114
+ }
115
+ for (int64_t k = 0; k < L->nskip; k++) tmp[L->rows + k] = v[L->skip[k]];
116
+ memcpy(v, tmp, (size_t)(L->rows + L->nskip) * sizeof(double));
117
+ }
118
+ }
119
+
120
+ /* n input bytes followed by one virtual newline, so a word at the end of the file still ends. */
121
+ static void classify_interp(const Net *net, const unsigned char *src, size_t n, unsigned char *cls, uint16_t *bits) {
122
+ double *v = malloc((size_t)net->maxw * 8), *tmp = malloc((size_t)net->maxw * 8);
123
+ double *h = malloc((size_t)net->n_fstate * 8), *r = calloc((size_t)net->n_bstate, 8);
124
+ memcpy(h, net->h0, (size_t)net->n_fstate * 8);
125
+ for (size_t t = 0; t <= n; t++) {
126
+ unsigned c = t < n ? (src[t] < 128 ? src[t] : 127) : 10;
127
+ memcpy(v, net->emb + c * net->n_feats, (size_t)net->n_feats * 8);
128
+ memcpy(v + net->n_feats, h, (size_t)net->n_fstate * 8);
129
+ run_cell(net->fwd, net->n_fwd, v, tmp);
130
+ for (int64_t i = 0; i < net->n_fstate; i++) h[i] = v[net->f_state[i]];
131
+ uint16_t bt = 0;
132
+ for (int64_t i = 0; i < net->n_fout; i++) bt |= (uint16_t)((v[net->f_out[i]] != 0) << i);
133
+ bits[t] = bt;
134
+ }
135
+ for (size_t t = n + 1; t-- > 0;) {
136
+ for (int64_t i = 0; i < net->n_fout; i++) v[i] = (bits[t] >> i) & 1;
137
+ memcpy(v + net->n_fout, r, (size_t)net->n_bstate * 8);
138
+ run_cell(net->bwd, net->n_bwd, v, tmp);
139
+ for (int64_t i = 0; i < net->n_bstate; i++) r[i] = v[net->b_state[i]];
140
+ double s = net->head_b[0];
141
+ for (int64_t c = 0; c < net->head_cols; c++) s += net->head_W[c] * v[c];
142
+ s = s < 0 ? 0 : (s > net->n_classes - 1 ? net->n_classes - 1 : s);
143
+ if (t < n) cls[t] = (unsigned char)s;
144
+ }
145
+ free(v); free(tmp); free(h); free(r);
146
+ }
147
+
148
+ /* ---- compiled mode ------------------------------------------------------- */
149
+ #include <emmintrin.h>
150
+ static inline double clip_sse(double x, double hi) { return _mm_cvtsd_f64(_mm_min_sd(_mm_max_sd(_mm_set_sd(x), _mm_setzero_pd()), _mm_set_sd(hi))); }
151
+ static inline double clip_fm(double x, double hi) { return __builtin_fmin(__builtin_fmax(x, 0.0), hi); }
152
+ #include "pylexnet_kernel.h"
153
+
154
+ typedef struct { uint64_t h; } Fnv;
155
+ static void fnv_bytes(Fnv *f, const void *p, size_t n) {
156
+ for (size_t i = 0; i < n; i++) { f->h ^= ((const unsigned char *)p)[i]; f->h *= 0x100000001B3ULL; }
157
+ }
158
+ static void fnv_i64(Fnv *f, int64_t x) { fnv_bytes(f, &x, 8); }
159
+ static void fnv_mask(Fnv *f, const double *a, int64_t n) {
160
+ for (int64_t i = 0; i < n; i++) { unsigned char m = a[i] != 0; fnv_bytes(f, &m, 1); }
161
+ }
162
+ static uint64_t struct_hash(const Net *net) {
163
+ Fnv f = { 0xCBF29CE484222325ULL };
164
+ fnv_i64(&f, net->n_feats); fnv_i64(&f, net->n_fstate); fnv_i64(&f, net->n_fout);
165
+ fnv_i64(&f, net->n_bstate); fnv_i64(&f, net->n_classes); fnv_i64(&f, net->head_cols);
166
+ const Layer *cells[2] = { net->fwd, net->bwd };
167
+ int64_t counts[2] = { net->n_fwd, net->n_bwd };
168
+ for (int c = 0; c < 2; c++) {
169
+ fnv_i64(&f, counts[c]);
170
+ for (int64_t l = 0; l < counts[c]; l++) {
171
+ const Layer *L = &cells[c][l];
172
+ fnv_i64(&f, L->rows); fnv_i64(&f, L->cols); fnv_i64(&f, L->nskip); fnv_i64(&f, L->tied);
173
+ fnv_bytes(&f, L->hi, (size_t)(L->tied ? 1 : L->rows) * 8);
174
+ for (int64_t k = 0; k < L->nskip; k++) fnv_i64(&f, L->skip[k]);
175
+ fnv_mask(&f, L->W, (L->tied ? 1 : L->rows) * L->cols);
176
+ }
177
+ }
178
+ for (int64_t i = 0; i < net->n_fstate; i++) fnv_i64(&f, net->f_state[i]);
179
+ for (int64_t i = 0; i < net->n_fout; i++) fnv_i64(&f, net->f_out[i]);
180
+ for (int64_t i = 0; i < net->n_bstate; i++) fnv_i64(&f, net->b_state[i]);
181
+ fnv_mask(&f, net->head_W, net->head_cols);
182
+ return f.h;
183
+ }
184
+
185
+ /* Every number in the file must be an integer: then all activations are exact integers in double arithmetic,
186
+ * sums may be re-associated, and a clip1 unit is exactly 0 or 1. */
187
+ static int all_integers(const Net *net) {
188
+ int bad = 0;
189
+ for (int64_t i = 0; i < 128 * net->n_feats; i++) bad += net->emb[i] != (int64_t)net->emb[i];
190
+ const Layer *cells[2] = { net->fwd, net->bwd };
191
+ int64_t counts[2] = { net->n_fwd, net->n_bwd };
192
+ for (int c = 0; c < 2; c++)
193
+ for (int64_t l = 0; l < counts[c]; l++) {
194
+ const Layer *L = &cells[c][l];
195
+ int64_t wrows = L->tied ? 1 : L->rows;
196
+ for (int64_t i = 0; i < wrows * L->cols; i++) bad += L->W[i] != (int64_t)L->W[i];
197
+ for (int64_t i = 0; i < L->rows; i++) bad += L->b[i] != (int64_t)L->b[i];
198
+ for (int64_t i = 0; i < wrows; i++) bad += L->hi[i] != (int64_t)L->hi[i];
199
+ }
200
+ for (int64_t i = 0; i < net->head_cols; i++) bad += net->head_W[i] != (int64_t)net->head_W[i];
201
+ for (int64_t i = 0; i < net->n_fstate; i++) bad += net->h0[i] != (int64_t)net->h0[i];
202
+ return bad;
203
+ }
204
+
205
+ /* Sorted threshold families: tied, one input, ceiling 1, biases monotone so the outputs form a step vector.
206
+ * Then rank(h) by binary search reproduces every unit, and a consumer's weights over the family reduce to a
207
+ * prefix sum indexed by the rank. Both tables are built here from the loaded values. */
208
+ static int check_families(const Net *net) {
209
+ int bad = 0;
210
+ for (int g = 0; g < K_N_FAMILY; g++) {
211
+ int cell = K_FAMILY[g][0], l = K_FAMILY[g][1], n = K_FAMILY[g][3];
212
+ const Layer *L = &(cell ? net->bwd : net->fwd)[l];
213
+ int nz = 0;
214
+ for (int64_t c = 0; c < L->cols; c++) nz += L->W[c] != 0;
215
+ if (!L->tied || L->rows != n || L->hi[0] != 1.0 || nz != 1) { bad++; continue; }
216
+ double w = 0;
217
+ for (int64_t c = 0; c < L->cols; c++) if (L->W[c] != 0) w = L->W[c];
218
+ for (int r = 1; r < n; r++) if ((w > 0 && L->b[r] > L->b[r - 1]) || (w < 0 && L->b[r] < L->b[r - 1])) bad++;
219
+ }
220
+ return bad;
221
+ }
222
+
223
+ static double *build_prefix(const Net *net) {
224
+ double *kp = malloc(sizeof(double) * (K_PREFIX_LEN + 1));
225
+ int pos = 0;
226
+ for (int g = 0; g < K_N_PREFIX; g++) {
227
+ int cell = K_PREFIX[g][0], l = K_PREFIX[g][1], r = K_PREFIX[g][2], c0 = K_PREFIX[g][3], n = K_PREFIX[g][4];
228
+ const Layer *L = &(cell ? net->bwd : net->fwd)[l];
229
+ kp[pos] = 0;
230
+ for (int k = 0; k < n; k++) kp[pos + k + 1] = kp[pos + k] + L->W[(L->tied ? 0 : r) * L->cols + c0 + k];
231
+ pos += n + 1;
232
+ }
233
+ return kp;
234
+ }
235
+
236
+ static void gather(const Net *net, double *kc, double *kb) {
237
+ for (int i = 0; i < K_N_COEF; i++) {
238
+ int cell = K_COEF[i][0], l = K_COEF[i][1], r = K_COEF[i][2], c = K_COEF[i][3];
239
+ if (cell == 2) kc[i] = net->head_W[c];
240
+ else { const Layer *L = &(cell ? net->bwd : net->fwd)[l]; kc[i] = L->W[(L->tied ? 0 : r) * L->cols + c]; }
241
+ }
242
+ for (int i = 0; i < K_N_BIAS; i++) {
243
+ int cell = K_BIAS[i][0], l = K_BIAS[i][1], r = K_BIAS[i][2];
244
+ kb[i] = cell == 2 ? net->head_b[r] : (cell ? net->bwd : net->fwd)[l].b[r];
245
+ }
246
+ }
247
+
248
+ /* Upper bound of an entry of a layer's input vector, from the loaded ceilings and embedding table.
249
+ * layer -1 is the cell's own input: byte features then carried state (forward), bits then state (backward). */
250
+ static double entry_max(const Net *net, int cell, int64_t layer, int64_t idx, int depth) {
251
+ if (depth > 64) return 1e300;
252
+ const Layer *L = cell ? net->bwd : net->fwd;
253
+ if (layer < 0) {
254
+ if (cell == 0) {
255
+ if (idx < net->n_feats) {
256
+ double m = 0;
257
+ for (int b = 0; b < 128; b++) if (net->emb[b * net->n_feats + idx] > m) m = net->emb[b * net->n_feats + idx];
258
+ return m;
259
+ }
260
+ return entry_max(net, 0, net->n_fwd - 1, net->f_state[idx - net->n_feats], depth + 1);
261
+ }
262
+ if (idx < net->n_fout) return 1.0;
263
+ return entry_max(net, 1, net->n_bwd - 1, net->b_state[idx - net->n_fout], depth + 1);
264
+ }
265
+ if (idx < L[layer].rows) return L[layer].hi[L[layer].tied ? 0 : idx];
266
+ return entry_max(net, cell, layer - 1, L[layer].skip[idx - L[layer].rows], depth + 1);
267
+ }
268
+
269
+ /* Each guarded unit must be provably 0 when its guard columns are 0: bias plus every other positive
270
+ * contribution at its input's ceiling must not exceed 0. Returns the number of failed guards. */
271
+ static int check_guards(const Net *net) {
272
+ int bad = 0;
273
+ for (int g = 0; g < K_N_GUARD; g++) {
274
+ int cell = K_GUARD[g][0], l = K_GUARD[g][1], r = K_GUARD[g][2];
275
+ const Layer *L = &(cell ? net->bwd : net->fwd)[l];
276
+ double bound = L->b[r];
277
+ for (int64_t c = 0; c < L->cols; c++) {
278
+ double w = L->W[(L->tied ? 0 : r) * L->cols + c];
279
+ if (w <= 0) continue;
280
+ int guarded = 0;
281
+ for (int k = 0; k < K_MAX_GUARD; k++) if (K_GUARD[g][3 + k] == c) guarded = 1;
282
+ if (!guarded) bound += w * entry_max(net, cell, l - 1, c, 0);
283
+ }
284
+ if (bound > 0) { bad++; fprintf(stderr, "guard %d (cell %d layer %d row %d) not provable: bound %g\n", g, cell, l, r, bound); }
285
+ }
286
+ return bad;
287
+ }
288
+
289
+ static void classify_compiled(const Net *net, const double *kc, const double *kb, const double *kp,
290
+ const unsigned char *src, size_t n, unsigned char *cls, uint16_t *bits) {
291
+ FState a, b, *p = &a, *s = &b, *t;
292
+ k_init_state(p, net->h0);
293
+ for (size_t i = 0; i < n; i++) {
294
+ bits[i] = k_fwd_step(kc, kb, kp, net->emb + (src[i] < 128 ? src[i] : 127) * K_N_FEATS, p, s);
295
+ t = p; p = s; s = t;
296
+ }
297
+ bits[n] = k_fwd_step(kc, kb, kp, net->emb + 10 * K_N_FEATS, p, s);
298
+ BState ra, rb, *r = &ra, *q = &rb, *u;
299
+ memset(&ra, 0, sizeof ra);
300
+ k_bwd_step(kc, kb, bits[n], r, q);
301
+ u = r; r = q; q = u;
302
+ for (size_t i = n; i-- > 0;) {
303
+ cls[i] = (unsigned char)k_bwd_step(kc, kb, bits[i], r, q);
304
+ u = r; r = q; q = u;
305
+ }
306
+ }
307
+
308
+ /* ---- cli ----------------------------------------------------------------- */
309
+ static char *default_weights(const char *argv0) {
310
+ const char *slash = strrchr(argv0, '/');
311
+ size_t dir = slash ? (size_t)(slash - argv0 + 1) : 0;
312
+ char *p = malloc(dir + 32);
313
+ memcpy(p, argv0, dir);
314
+ strcpy(p + dir, "pylexnet_weights.bin");
315
+ return p;
316
+ }
317
+
318
+ int main(int argc, char **argv) {
319
+ int classes = 0, bench = 0;
320
+ const char *path = NULL, *wpath = NULL;
321
+ for (int i = 1; i < argc; i++) {
322
+ if (!strcmp(argv[i], "--classes")) classes = 1;
323
+ else if (!strcmp(argv[i], "--bench")) bench = 1;
324
+ else if (!strcmp(argv[i], "--interp")) use_interp = 1;
325
+ else if (!strcmp(argv[i], "-w")) wpath = argv[++i];
326
+ else path = argv[i];
327
+ }
328
+ if (!classes && !bench) { fprintf(stderr, "usage: pylexnet [-w weights.bin] [--interp] (--classes [file] | --bench file)\n"); return 2; }
329
+ Net net = load(wpath ? wpath : default_weights(argv[0]));
330
+ double *kc = malloc(sizeof(double) * (K_N_COEF + 1)), *kb = malloc(sizeof(double) * (K_N_BIAS + 1)), *kp = NULL;
331
+ if (!use_interp) {
332
+ uint64_t h = struct_hash(&net);
333
+ if (h != K_STRUCT_HASH) {
334
+ fprintf(stderr, "weights: architecture hash %016llx does not match compiled kernel %016llx; regenerate (compile.py) or run --interp\n",
335
+ (unsigned long long)h, (unsigned long long)K_STRUCT_HASH);
336
+ return 1;
337
+ }
338
+ if (check_guards(&net)) { fprintf(stderr, "weights: guard check failed, refusing the kernel\n"); return 1; }
339
+ if (all_integers(&net)) { fprintf(stderr, "weights: non-integer values, refusing the kernel\n"); return 1; }
340
+ if (check_families(&net)) { fprintf(stderr, "weights: a threshold family is not tied/sorted, refusing the kernel\n"); return 1; }
341
+ gather(&net, kc, kb);
342
+ kp = build_prefix(&net);
343
+ }
344
+ FILE *f = path ? fopen(path, "rb") : stdin;
345
+ if (!f) { perror(path); return 1; }
346
+ size_t cap = 1 << 20, n = 0, got;
347
+ unsigned char *src = malloc(cap);
348
+ while ((got = fread(src + n, 1, cap - n, f)) > 0) { n += got; if (n == cap) src = realloc(src, cap *= 2); }
349
+ if (path) fclose(f);
350
+ unsigned char *cls = malloc(n + 1);
351
+ uint16_t *bits = malloc((n + 2) * sizeof(uint16_t));
352
+ if (bench) {
353
+ int reps = 5;
354
+ struct timespec a, b;
355
+ clock_gettime(CLOCK_MONOTONIC, &a);
356
+ for (int i = 0; i < reps; i++) {
357
+ if (use_interp) classify_interp(&net, src, n, cls, bits);
358
+ else classify_compiled(&net, kc, kb, kp, src, n, cls, bits);
359
+ }
360
+ clock_gettime(CLOCK_MONOTONIC, &b);
361
+ double sec = (b.tv_sec - a.tv_sec) + (b.tv_nsec - a.tv_nsec) * 1e-9;
362
+ fprintf(stderr, "%zu bytes, %.1f ns/byte\n", n, sec / reps / (double)n * 1e9);
363
+ return 0;
364
+ }
365
+ if (use_interp) classify_interp(&net, src, n, cls, bits);
366
+ else classify_compiled(&net, kc, kb, kp, src, n, cls, bits);
367
+ fwrite(cls, 1, n, stdout);
368
+ return 0;
369
+ }
pylexnet_kernel.h ADDED
@@ -0,0 +1,209 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /* Generated by compile.py: the network's ARCHITECTURE as C. No weight values live here: pylexnet.c
2
+ * loads them, checks the structure hash and the guard inequalities, and gathers kc[]/kb[] by K_COEF/K_BIAS. */
3
+ #include <stdint.h>
4
+ #include <string.h>
5
+ #define K_STRUCT_HASH 0x5AB2AC2E97E0387CULL
6
+ #define K_N_FEATS 29
7
+ #define K_N_CLASSES 9
8
+ #define K_N_BITS 10
9
+ #ifndef CLIP
10
+ #define CLIP(x, hi) clipn((x), (hi))
11
+ static inline double clipn(double x, double hi) { x = x < 0.0 ? 0.0 : x; return x > hi ? hi : x; }
12
+ #endif
13
+ /* A tied, sorted threshold family is a step vector: unit k is 1 iff kb[k] + w*h >= 1, true for k < rank.
14
+ * The loader verifies integrality, tying and monotonicity, so a binary search gives the same values. */
15
+ static inline int k_rank(const double *kb, int n, double w, double h) {
16
+ int lo = 0, hi = n;
17
+ while (lo < hi) { int mid = (lo + hi) >> 1; if (kb[mid] + w * h >= 1.0) lo = mid + 1; else hi = mid; }
18
+ return lo;
19
+ }
20
+ typedef struct { double S, sq1, T, F, E, c, fo, FS, FD, oq1, oT, C, N, ee, W, d, pf, fp, cD, cC, cAt, atp, cDot, pos0, pos1, pos2, pos3, pos4, pos5, pos6, tail, A, e1, e2, e2d, e3, e3d, e3dd; } FState;
21
+ typedef struct { double u_reg_KW, u_reg_BI, u_reg_FN, u_reg_CL, u_reg_DEC, u_reg_STR, b_ev_KW, b_ev_BI, b_ev_FN, b_ev_CL, b_ev_DEC, b_ev_STR; } BState;
22
+ static inline void k_init_state(FState *s, const double *h0) { memcpy(s, h0, sizeof *s); }
23
+ static inline __attribute__((always_inline)) uint16_t k_fwd_step(const double *restrict kc, const double *restrict kb, const double *restrict kp, const double *restrict x,
24
+ const FState *restrict p, FState *restrict s) {
25
+ double u_wordch = 0.0, u_qmd = 0.0, u_qms = 0.0, u_c2 = 0.0, u_wend = 0.0, u_fieldopen = 0.0, u_pf_new = 0.0, u_S_eff = 0.0, u_fp_new = 0.0, u_sclose = 0.0, u_tclose = 0.0, u_tripleopen = 0.0, u_nlclose = 0.0, u_E_new = 0.0, u_fo_new = 0.0, u_C_new = 0.0, u_open = 0.0, u_open_f = 0.0, u_open_qs = 0.0, u_fieldclose = 0.0, u_inc = 0.0, u_dec = 0.0, u_S_new = 0.0, u_c_new = 0.0, u_T_new = 0.0, u_F_new = 0.0, u_sq1_new = 0.0, u_FS_new = 0.0, u_FD_new = 0.0, u_oq1_new = 0.0, u_oT_new = 0.0, u_lab_STR = 0.0, u_N_new = 0.0, u_ee_new = 0.0, u_W_new = 0.0, u_d_new = 0.0, u_pos0 = 0.0, u_pos1 = 0.0, u_pos2 = 0.0, u_pos3 = 0.0, u_pos4 = 0.0, u_pos5 = 0.0, u_pos6 = 0.0, u_tail = 0.0, u_a0 = 0.0, u_a1 = 0.0, u_a2 = 0.0, u_a3 = 0.0, u_a4 = 0.0, u_a5 = 0.0, u_a6 = 0.0, u_at_ = 0.0, u_A_new = 0.0, u_e1_new = 0.0, u_e2_new = 0.0, u_e3_new = 0.0, u_e2d_new = 0.0, u_e3d_new = 0.0, u_e3dd_new = 0.0, u_m3 = 0.0, u_SE = 0.0, u_ge1 = 0.0, u_le1 = 0.0, u_ge2 = 0.0, u_le2 = 0.0, u_ge3 = 0.0, u_le3 = 0.0, u_ge4 = 0.0, u_le4 = 0.0, u_ge5 = 0.0, u_le5 = 0.0, u_is_def = 0.0, u_is_class = 0.0, u_cD_new = 0.0, u_cC_new = 0.0, u_atp_new = 0.0, u_cAt_new = 0.0, u_cDot_new = 0.0, u_ev_DEC = 0.0, u_ev_CL = 0.0, u_ev_KW = 0.0, u_ev_FN = 0.0, u_ev_BI = 0.0, u_ev_STR = 0.0, u_lab_W = 0.0;
26
+ if ((p->W + x[13] + x[6] + x[7] + p->atp + p->cD + p->cC + p->cAt + p->cDot + x[15] + x[14] + p->fp + p->d + p->pos0 + p->pos1 + p->pos2 + p->pos3 + p->pos4 + p->pos5 + p->pos6 + p->tail + p->e2 + p->e3 + p->e3d) != 0.0) { /* all activations are >= 0 */
27
+ u_wordch = CLIP((kb[0] + kc[0] * x[13]), 1.0);
28
+ u_wend = CLIP(((kb[1] + kc[1] * p->W) + kc[2] * x[13]), 1.0);
29
+ u_pf_new = CLIP((((kb[2] + kc[3] * x[15]) + (kc[4] * x[14] + kc[5] * p->pf)) + (kc[6] * p->W + kc[7] * p->d)), 1.0);
30
+ u_e2d_new = CLIP((kb[3] + kc[8] * p->e2), 117026723984.0);
31
+ u_e3d_new = CLIP((kb[4] + kc[9] * p->e3), 117026723984.0);
32
+ u_e3dd_new = CLIP((kb[5] + kc[10] * p->e3d), 117026723984.0);
33
+ u_m3 = CLIP((kb[6] + kc[11] * p->d), 3.0);
34
+ u_fp_new = CLIP(((kb[7] + kc[12] * x[14]) + (kc[13] * p->fp + kc[14] * u_wordch)), 1.0);
35
+ u_cAt_new = CLIP((((kb[8] + kc[15] * p->cAt) + (kc[16] * p->atp + kc[17] * u_wordch)) + kc[18] * u_wend), 1.0);
36
+ }
37
+ if ((p->S + p->fo + p->FS + p->FD + p->c + p->T + p->F + p->sq1 + p->oq1 + p->oT + x[2] + x[3]) != 0.0) { /* all activations are >= 0 */
38
+ u_qmd = CLIP(((kb[9] + kc[19] * x[2]) + (kc[20] * p->sq1 + kc[21] * p->E)), 1.0);
39
+ u_qms = CLIP(((kb[10] + kc[22] * x[3]) + (kc[23] * p->sq1 + kc[24] * p->E)), 1.0);
40
+ u_c2 = CLIP((kb[11] + kc[25] * p->c), 1.0);
41
+ u_fieldopen = CLIP(((kb[12] + kc[26] * p->fo) + kc[27] * x[8]), 1.0);
42
+ u_S_eff = CLIP((((kb[13] + kc[28] * p->S) + (kc[29] * u_fieldopen + kc[30] * p->T)) + ((kc[31] * u_qmd + kc[32] * u_qms) + kc[33] * u_c2)), 1.0);
43
+ u_sclose = CLIP((((kb[14] + kc[34] * u_S_eff) + (kc[35] * p->T + kc[36] * u_qmd)) + (kc[37] * u_qms + kc[38] * p->c)), 1.0);
44
+ u_tclose = CLIP((((kb[15] + kc[39] * u_S_eff) + (kc[40] * p->T + kc[41] * u_qmd)) + (kc[42] * u_qms + kc[43] * p->c)), 1.0);
45
+ u_tripleopen = CLIP((((kb[16] + kc[44] * u_S_eff) + (kc[45] * p->T + kc[46] * u_qmd)) + (kc[47] * u_qms + kc[48] * p->c)), 1.0);
46
+ u_nlclose = CLIP((((kb[17] + kc[49] * u_S_eff) + (kc[50] * p->T + kc[51] * x[1])) + kc[52] * p->E), 1.0);
47
+ u_E_new = CLIP(((kb[18] + kc[53] * u_S_eff) + (kc[54] * x[4] + kc[55] * p->E)), 1.0);
48
+ u_fo_new = CLIP((((kb[19] + kc[56] * u_S_eff) + (kc[57] * p->F + kc[58] * x[8])) + (kc[59] * p->E + kc[60] * p->fo)), 1.0);
49
+ u_open = CLIP((((kb[20] + kc[61] * x[2]) + (kc[62] * x[3] + kc[63] * u_S_eff)) + (kc[64] * p->C + kc[65] * p->N)), 1.0);
50
+ u_open_f = CLIP((((kb[21] + kc[66] * x[2]) + (kc[67] * x[3] + kc[68] * u_S_eff)) + ((kc[69] * p->C + kc[70] * p->N) + (kc[71] * p->pf + kc[72] * p->fp))), 1.0);
51
+ u_open_qs = CLIP((((kb[22] + kc[73] * x[2]) + (kc[74] * x[3] + kc[75] * u_S_eff)) + (kc[76] * p->C + kc[77] * p->N)), 1.0);
52
+ u_fieldclose = CLIP((((kb[23] + kc[78] * p->FS) + (kc[79] * u_S_eff + kc[80] * p->C)) + (kc[81] * p->FD + kc[82] * x[12])), 1.0);
53
+ u_inc = CLIP((((kb[24] + kc[83] * p->FS) + (kc[84] * u_S_eff + kc[85] * p->C)) + kc[86] * x[10]), 1.0);
54
+ u_dec = CLIP((((kb[25] + kc[87] * p->FS) + (kc[88] * u_S_eff + kc[89] * p->C)) + (kc[90] * x[11] + kc[91] * p->FD)), 1.0);
55
+ u_S_new = CLIP((((kb[26] + kc[92] * u_S_eff) + (kc[93] * u_sclose + kc[94] * u_tclose)) + ((kc[95] * u_nlclose + kc[96] * u_open) + kc[97] * u_fieldclose)), 1.0);
56
+ u_c_new = CLIP(((((kb[27] + kc[98] * p->c) + (kc[99] * u_S_eff + kc[100] * u_qmd)) + ((kc[101] * u_qms + kc[102] * u_tripleopen) + (kc[103] * u_tclose + kc[104] * u_sclose))) + (kc[105] * u_open + kc[106] * u_open_qs)), 3.0);
57
+ u_T_new = CLIP((((kb[28] + kc[107] * p->T) + (kc[108] * u_tripleopen + kc[109] * u_tclose)) + ((kc[110] * u_open + kc[111] * u_fieldclose) + kc[112] * p->oT)), 1.0);
58
+ u_F_new = CLIP((((kb[29] + kc[113] * p->F) + (kc[114] * u_open_f + kc[115] * u_open)) + ((kc[116] * u_sclose + kc[117] * u_tclose) + (kc[118] * u_nlclose + kc[119] * u_fieldclose))), 1.0);
59
+ u_sq1_new = CLIP(((((kb[30] + kc[120] * p->sq1) + (kc[121] * u_open_qs + kc[122] * u_sclose)) + ((kc[123] * u_tclose + kc[124] * u_nlclose) + (kc[125] * p->S + kc[126] * u_S_eff))) + (kc[127] * u_fieldclose + kc[128] * p->oq1)), 1.0);
60
+ u_FS_new = CLIP(((kb[31] + kc[129] * p->FS) + (kc[130] * u_fieldopen + kc[131] * u_fieldclose)), 1.0);
61
+ u_FD_new = CLIP(((kb[32] + kc[132] * p->FD) + (kc[133] * u_inc + kc[134] * u_dec)), 3.0);
62
+ u_oq1_new = CLIP((((kb[33] + kc[135] * p->oq1) + (kc[136] * u_fieldopen + kc[137] * p->sq1)) + kc[138] * u_fieldclose), 1.0);
63
+ u_oT_new = CLIP((((kb[34] + kc[139] * p->oT) + (kc[140] * u_fieldopen + kc[141] * p->T)) + kc[142] * u_fieldclose), 1.0);
64
+ u_lab_STR = CLIP((((kb[35] + kc[143] * u_S_eff) + (kc[144] * u_nlclose + kc[145] * u_open)) + kc[146] * u_fieldclose), 1.0);
65
+ }
66
+ if ((u_wend) != 0.0) { /* all activations are >= 0 */
67
+ u_SE = CLIP((((kb[36] + kc[147] * p->A) + (kc[148] * p->e1 + kc[149] * p->e2d)) + ((kc[150] * p->e3dd + kc[151] * p->d) + kc[152] * u_m3)), 35184372088832.0);
68
+ u_ev_DEC = CLIP(((kb[37] + kc[153] * u_wend) + kc[154] * p->cAt), 1.0);
69
+ u_ev_CL = CLIP(((kb[38] + kc[155] * u_wend) + (kc[156] * p->cC + kc[157] * p->cAt)), 1.0);
70
+ u_ev_STR = CLIP(((kb[39] + kc[158] * u_wend) + (kc[159] * u_open + kc[160] * p->pf)), 1.0);
71
+ u_ge1 = CLIP((kb[40] + kc[161] * u_SE), 1.0);
72
+ u_le1 = CLIP((kb[41] + kc[162] * u_SE), 1.0);
73
+ u_ge2 = CLIP((kb[42] + kc[163] * u_SE), 1.0);
74
+ u_le2 = CLIP((kb[43] + kc[164] * u_SE), 1.0);
75
+ u_ge3 = CLIP((kb[44] + kc[165] * u_SE), 1.0);
76
+ u_le3 = CLIP((kb[45] + kc[166] * u_SE), 1.0);
77
+ u_ge4 = CLIP((kb[46] + kc[167] * u_SE), 1.0);
78
+ u_le4 = CLIP((kb[47] + kc[168] * u_SE), 1.0);
79
+ u_ge5 = CLIP((kb[48] + kc[169] * u_SE), 1.0);
80
+ u_le5 = CLIP((kb[49] + kc[170] * u_SE), 1.0);
81
+ u_is_def = CLIP(((kb[50] + kc[171] * u_wend) + (kc[172] * u_ge2 + kc[173] * u_le2)), 1.0);
82
+ u_is_class = CLIP(((kb[51] + kc[174] * u_wend) + (kc[175] * u_ge3 + kc[176] * u_le3)), 1.0);
83
+ u_ev_KW = CLIP(((((kb[52] + kc[177] * u_wend) + (kc[178] * u_ge1 + kc[179] * u_le1)) + ((kc[180] * u_ge2 + kc[181] * u_le2) + (kc[182] * u_ge3 + kc[183] * u_le3))) + ((kc[184] * p->cAt + kc[185] * p->cC) + kc[186] * p->cD)), 1.0);
84
+ u_ev_FN = CLIP((((kb[53] + kc[187] * u_wend) + (kc[188] * u_ge5 + kc[189] * u_le5)) + ((kc[190] * p->cD + kc[191] * p->cAt) + kc[192] * p->cC)), 1.0);
85
+ u_ev_BI = CLIP((((kb[54] + kc[193] * u_wend) + (kc[194] * u_ge4 + kc[195] * u_le4)) + ((kc[196] * p->cAt + kc[197] * p->cC) + (kc[198] * p->cD + kc[199] * p->cDot))), 1.0);
86
+ }
87
+ if ((p->W + x[13] + x[6] + x[7] + p->atp + p->cD + p->cC + p->cAt + p->cDot + x[15] + x[14] + p->fp + p->d + p->pos0 + p->pos1 + p->pos2 + p->pos3 + p->pos4 + p->pos5 + p->pos6 + p->tail + p->e2 + p->e3 + p->e3d) != 0.0) { /* all activations are >= 0 */
88
+ u_W_new = CLIP((((kb[55] + kc[200] * u_wordch) + (kc[201] * x[0] + kc[202] * p->W)) + ((kc[203] * p->N + kc[204] * u_S_eff) + kc[205] * p->C)), 1.0);
89
+ u_atp_new = CLIP(((kb[56] + kc[206] * x[7]) + (kc[207] * u_S_eff + kc[208] * p->C)), 1.0);
90
+ u_cDot_new = CLIP((((kb[57] + kc[209] * x[6]) + (kc[210] * u_S_eff + kc[211] * p->C)) + ((kc[212] * p->N + kc[213] * p->cDot) + (kc[214] * u_wordch + kc[215] * u_wend))), 1.0);
91
+ u_cD_new = CLIP((((kb[58] + kc[216] * p->cD) + (kc[217] * u_is_def + kc[218] * u_wend)) + kc[219] * x[1]), 1.0);
92
+ u_cC_new = CLIP((((kb[59] + kc[220] * p->cC) + (kc[221] * u_is_class + kc[222] * u_wend)) + kc[223] * x[1]), 1.0);
93
+ u_d_new = CLIP(((kb[60] + kc[224] * p->d) + kc[225] * u_W_new), 30.0);
94
+ u_pos0 = CLIP(((kb[61] + kc[226] * u_W_new) + kc[227] * p->W), 1.0);
95
+ u_pos1 = CLIP(((kb[62] + kc[228] * p->pos0) + kc[229] * u_W_new), 1.0);
96
+ u_pos2 = CLIP(((kb[63] + kc[230] * p->pos1) + kc[231] * u_W_new), 1.0);
97
+ u_pos3 = CLIP(((kb[64] + kc[232] * p->pos2) + kc[233] * u_W_new), 1.0);
98
+ u_pos4 = CLIP(((kb[65] + kc[234] * p->pos3) + kc[235] * u_W_new), 1.0);
99
+ u_pos5 = CLIP(((kb[66] + kc[236] * p->pos4) + kc[237] * u_W_new), 1.0);
100
+ u_pos6 = CLIP(((kb[67] + kc[238] * p->pos5) + kc[239] * u_W_new), 1.0);
101
+ u_tail = CLIP(((kb[68] + kc[240] * p->pos6) + (kc[241] * p->tail + kc[242] * u_W_new)), 1.0);
102
+ u_e1_new = CLIP(((kb[69] + kc[243] * x[26]) + kc[244] * u_W_new), 117026723984.0);
103
+ u_e2_new = CLIP(((kb[70] + kc[245] * x[27]) + kc[246] * u_W_new), 117026723984.0);
104
+ u_e3_new = CLIP(((kb[71] + kc[247] * x[28]) + kc[248] * u_W_new), 117026723984.0);
105
+ u_lab_W = CLIP(((kb[72] + kc[249] * u_W_new) + kc[250] * u_atp_new), 1.0);
106
+ u_a0 = CLIP(((kb[73] + kc[251] * x[18]) + kc[252] * u_pos0), 117026723984.0);
107
+ u_a1 = CLIP(((kb[74] + kc[253] * x[19]) + kc[254] * u_pos1), 117026723984.0);
108
+ u_a2 = CLIP(((kb[75] + kc[255] * x[20]) + kc[256] * u_pos2), 117026723984.0);
109
+ u_a3 = CLIP(((kb[76] + kc[257] * x[21]) + kc[258] * u_pos3), 117026723984.0);
110
+ u_a4 = CLIP(((kb[77] + kc[259] * x[22]) + kc[260] * u_pos4), 117026723984.0);
111
+ u_a5 = CLIP(((kb[78] + kc[261] * x[23]) + kc[262] * u_pos5), 117026723984.0);
112
+ u_a6 = CLIP(((kb[79] + kc[263] * x[24]) + kc[264] * u_pos6), 117026723984.0);
113
+ u_at_ = CLIP(((kb[80] + kc[265] * x[25]) + kc[266] * u_tail), 117026723984.0);
114
+ u_A_new = CLIP(((((kb[81] + kc[267] * p->A) + (kc[268] * u_W_new + kc[269] * u_a0)) + ((kc[270] * u_a1 + kc[271] * u_a2) + (kc[272] * u_a3 + kc[273] * u_a4))) + ((kc[274] * u_a5 + kc[275] * u_a6) + kc[276] * u_at_)), 35184372088832.0);
115
+ }
116
+ if ((x[5] + p->C) != 0.0) { /* all activations are >= 0 */
117
+ u_C_new = CLIP((((kb[82] + kc[277] * x[5]) + (kc[278] * u_S_eff + kc[279] * p->C)) + kc[280] * x[1]), 1.0);
118
+ }
119
+ if ((x[0] + p->N + p->ee) != 0.0) { /* all activations are >= 0 */
120
+ u_N_new = CLIP(((((kb[83] + kc[281] * x[0]) + (kc[282] * p->W + kc[283] * p->N)) + ((kc[284] * u_wordch + kc[285] * x[6]) + (kc[286] * x[17] + kc[287] * p->ee))) + (kc[288] * u_S_eff + kc[289] * p->C)), 1.0);
121
+ u_ee_new = CLIP(((kb[84] + kc[290] * x[16]) + kc[291] * u_N_new), 1.0);
122
+ }
123
+ s->S = u_S_new;
124
+ s->sq1 = u_sq1_new;
125
+ s->T = u_T_new;
126
+ s->F = u_F_new;
127
+ s->E = u_E_new;
128
+ s->c = u_c_new;
129
+ s->fo = u_fo_new;
130
+ s->FS = u_FS_new;
131
+ s->FD = u_FD_new;
132
+ s->oq1 = u_oq1_new;
133
+ s->oT = u_oT_new;
134
+ s->C = u_C_new;
135
+ s->N = u_N_new;
136
+ s->ee = u_ee_new;
137
+ s->W = u_W_new;
138
+ s->d = u_d_new;
139
+ s->pf = u_pf_new;
140
+ s->fp = u_fp_new;
141
+ s->cD = u_cD_new;
142
+ s->cC = u_cC_new;
143
+ s->cAt = u_cAt_new;
144
+ s->atp = u_atp_new;
145
+ s->cDot = u_cDot_new;
146
+ s->pos0 = u_pos0;
147
+ s->pos1 = u_pos1;
148
+ s->pos2 = u_pos2;
149
+ s->pos3 = u_pos3;
150
+ s->pos4 = u_pos4;
151
+ s->pos5 = u_pos5;
152
+ s->pos6 = u_pos6;
153
+ s->tail = u_tail;
154
+ s->A = u_A_new;
155
+ s->e1 = u_e1_new;
156
+ s->e2 = u_e2_new;
157
+ s->e2d = u_e2d_new;
158
+ s->e3 = u_e3_new;
159
+ s->e3d = u_e3d_new;
160
+ s->e3dd = u_e3dd_new;
161
+ return (uint16_t)(((uint16_t)(u_lab_STR != 0.0) << 0) | ((uint16_t)(u_C_new != 0.0) << 1) | ((uint16_t)(u_N_new != 0.0) << 2) | ((uint16_t)(u_lab_W != 0.0) << 3) | ((uint16_t)(u_ev_KW != 0.0) << 4) | ((uint16_t)(u_ev_BI != 0.0) << 5) | ((uint16_t)(u_ev_FN != 0.0) << 6) | ((uint16_t)(u_ev_CL != 0.0) << 7) | ((uint16_t)(u_ev_DEC != 0.0) << 8) | ((uint16_t)(u_ev_STR != 0.0) << 9));
162
+ }
163
+ static inline __attribute__((always_inline)) int k_bwd_step(const double *restrict kc, const double *restrict kb, uint16_t bits,
164
+ const BState *restrict r, BState *restrict s) {
165
+ const double f_lab_STR = (double)((bits >> 0) & 1);
166
+ const double f_C_new = (double)((bits >> 1) & 1);
167
+ const double f_N_new = (double)((bits >> 2) & 1);
168
+ const double f_lab_W = (double)((bits >> 3) & 1);
169
+ const double f_ev_KW = (double)((bits >> 4) & 1);
170
+ const double f_ev_BI = (double)((bits >> 5) & 1);
171
+ const double f_ev_FN = (double)((bits >> 6) & 1);
172
+ const double f_ev_CL = (double)((bits >> 7) & 1);
173
+ const double f_ev_DEC = (double)((bits >> 8) & 1);
174
+ const double f_ev_STR = (double)((bits >> 9) & 1);
175
+ const double u_reg_KW = CLIP(((kb[85] + kc[292] * r->b_ev_KW) + (kc[293] * r->u_reg_KW + kc[294] * f_lab_W)), 1.0);
176
+ const double u_reg_BI = CLIP(((kb[86] + kc[295] * r->b_ev_BI) + (kc[296] * r->u_reg_BI + kc[297] * f_lab_W)), 1.0);
177
+ const double u_reg_FN = CLIP(((kb[87] + kc[298] * r->b_ev_FN) + (kc[299] * r->u_reg_FN + kc[300] * f_lab_W)), 1.0);
178
+ const double u_reg_CL = CLIP(((kb[88] + kc[301] * r->b_ev_CL) + (kc[302] * r->u_reg_CL + kc[303] * f_lab_W)), 1.0);
179
+ const double u_reg_DEC = CLIP(((kb[89] + kc[304] * r->b_ev_DEC) + (kc[305] * r->u_reg_DEC + kc[306] * f_lab_W)), 1.0);
180
+ const double u_reg_STR = CLIP(((kb[90] + kc[307] * r->b_ev_STR) + (kc[308] * r->u_reg_STR + kc[309] * f_lab_W)), 1.0);
181
+ s->u_reg_KW = u_reg_KW;
182
+ s->u_reg_BI = u_reg_BI;
183
+ s->u_reg_FN = u_reg_FN;
184
+ s->u_reg_CL = u_reg_CL;
185
+ s->u_reg_DEC = u_reg_DEC;
186
+ s->u_reg_STR = u_reg_STR;
187
+ s->b_ev_KW = f_ev_KW;
188
+ s->b_ev_BI = f_ev_BI;
189
+ s->b_ev_FN = f_ev_FN;
190
+ s->b_ev_CL = f_ev_CL;
191
+ s->b_ev_DEC = f_ev_DEC;
192
+ s->b_ev_STR = f_ev_STR;
193
+ return (int)CLIP(((((kb[91] + kc[310] * u_reg_KW) + (kc[311] * u_reg_BI + kc[312] * u_reg_FN)) + ((kc[313] * u_reg_CL + kc[314] * u_reg_DEC) + (kc[315] * u_reg_STR + kc[316] * f_lab_STR))) + (kc[317] * f_C_new + kc[318] * f_N_new)), 8.0);
194
+ }
195
+ #define K_N_COEF 319
196
+ #define K_N_BIAS 92
197
+ #define K_N_GUARD 74
198
+ #define K_N_FAMILY 0
199
+ #define K_N_PREFIX 0
200
+ #define K_PREFIX_LEN 0
201
+ /* sorted threshold families: {cell, layer, first bias index, rows, coef index of the tied weight} */
202
+ static const int32_t K_FAMILY[K_N_FAMILY + 1][5] = {{0,0,0,0,0}};
203
+ /* prefix sums of a unit's weights over a family's columns: {cell, layer, row, first column, rows} */
204
+ static const int32_t K_PREFIX[K_N_PREFIX + 1][5] = {{0,0,0,0,0}};
205
+ #define K_MAX_GUARD 4
206
+ static const int32_t K_COEF[K_N_COEF][4] = {{0,0,0,13},{0,0,4,43},{0,0,4,13},{0,0,6,15},{0,0,6,14},{0,0,6,45},{0,0,6,43},{0,0,6,44},{0,0,7,62},{0,0,8,64},{0,0,9,65},{0,0,10,44},{0,1,1,23},{0,1,1,54},{0,1,1,0},{0,1,3,57},{0,1,3,58},{0,1,3,0},{0,1,3,4},{0,0,1,2},{0,0,1,30},{0,0,1,33},{0,0,2,3},{0,0,2,30},{0,0,2,33},{0,0,3,34},{0,0,5,35},{0,0,5,8},{0,1,0,37},{0,1,0,5},{0,1,0,39},{0,1,0,1},{0,1,0,2},{0,1,0,3},{0,2,0,0},{0,2,0,42},{0,2,0,7},{0,2,0,8},{0,2,0,45},{0,2,1,0},{0,2,1,42},{0,2,1,7},{0,2,1,8},{0,2,1,45},{0,2,2,0},{0,2,2,42},{0,2,2,7},{0,2,2,8},{0,2,2,45},{0,2,3,0},{0,2,3,42},{0,2,3,16},{0,2,3,44},{0,2,4,0},{0,2,4,19},{0,2,4,44},{0,2,5,0},{0,2,5,43},{0,2,5,23},{0,2,5,44},{0,2,5,46},{0,2,7,17},{0,2,7,18},{0,2,7,0},{0,2,7,51},{0,2,7,52},{0,2,8,17},{0,2,8,18},{0,2,8,0},{0,2,8,51},{0,2,8,52},{0,2,8,56},{0,2,8,57},{0,2,9,17},{0,2,9,18},{0,2,9,0},{0,2,9,51},{0,2,9,52},{0,2,10,47},{0,2,10,0},{0,2,10,51},{0,2,10,48},{0,2,10,26},{0,2,11,47},{0,2,11,0},{0,2,11,51},{0,2,11,24},{0,2,12,47},{0,2,12,0},{0,2,12,51},{0,2,12,25},{0,2,12,48},{0,3,0,27},{0,3,0,0},{0,3,0,1},{0,3,0,3},{0,3,0,7},{0,3,0,10},{0,3,1,57},{0,3,1,27},{0,3,1,32},{0,3,1,33},{0,3,1,2},{0,3,1,1},{0,3,1,0},{0,3,1,7},{0,3,1,9},{0,3,2,55},{0,3,2,2},{0,3,2,1},{0,3,2,7},{0,3,2,10},{0,3,2,61},{0,3,3,56},{0,3,3,8},{0,3,3,7},{0,3,3,0},{0,3,3,1},{0,3,3,3},{0,3,3,10},{0,3,4,54},{0,3,4,9},{0,3,4,0},{0,3,4,1},{0,3,4,3},{0,3,4,53},{0,3,4,27},{0,3,4,10},{0,3,4,60},{0,3,5,58},{0,3,5,35},{0,3,5,10},{0,3,6,59},{0,3,6,11},{0,3,6,12},{0,3,7,60},{0,3,7,35},{0,3,7,54},{0,3,7,10},{0,3,8,61},{0,3,8,35},{0,3,8,55},{0,3,8,10},{0,3,9,27},{0,3,9,3},{0,3,9,7},{0,3,9,10},{0,1,2,68},{0,1,2,69},{0,1,2,70},{0,1,2,71},{0,1,2,52},{0,1,2,10},{0,1,4,4},{0,1,4,57},{0,1,5,4},{0,1,5,56},{0,1,5,57},{0,3,28,34},{0,3,28,7},{0,3,28,64},{0,2,15,2},{0,2,16,2},{0,2,17,2},{0,2,18,2},{0,2,19,2},{0,2,20,2},{0,2,21,2},{0,2,22,2},{0,2,23,2},{0,2,24,2},{0,3,23,34},{0,3,23,17},{0,3,23,18},{0,3,24,34},{0,3,24,19},{0,3,24,20},{0,3,25,34},{0,3,25,15},{0,3,25,16},{0,3,25,17},{0,3,25,18},{0,3,25,19},{0,3,25,20},{0,3,25,67},{0,3,25,66},{0,3,25,65},{0,3,26,34},{0,3,26,23},{0,3,26,24},{0,3,26,65},{0,3,26,67},{0,3,26,66},{0,3,27,34},{0,3,27,21},{0,3,27,22},{0,3,27,67},{0,3,27,66},{0,3,27,65},{0,3,27,68},{0,2,14,6},{0,2,14,15},{0,2,14,54},{0,2,14,52},{0,2,14,0},{0,2,14,51},{0,2,25,22},{0,2,25,0},{0,2,25,51},{0,2,26,21},{0,2,26,0},{0,2,26,51},{0,2,26,52},{0,2,26,61},{0,2,26,6},{0,2,26,9},{0,4,8,55},{0,4,8,23},{0,4,8,41},{0,4,8,46},{0,4,9,56},{0,4,9,24},{0,4,9,41},{0,4,9,46},{0,3,11,63},{0,3,11,14},{0,3,12,14},{0,3,12,62},{0,3,13,69},{0,3,13,14},{0,3,14,70},{0,3,14,14},{0,3,15,71},{0,3,15,14},{0,3,16,72},{0,3,16,14},{0,3,17,73},{0,3,17,14},{0,3,18,74},{0,3,18,14},{0,3,19,75},{0,3,19,76},{0,3,19,14},{0,3,20,50},{0,3,20,14},{0,3,21,51},{0,3,21,14},{0,3,22,52},{0,3,22,14},{0,3,29,14},{0,3,29,25},{0,4,0,47},{0,4,0,12},{0,4,1,48},{0,4,1,13},{0,4,2,49},{0,4,2,14},{0,4,3,50},{0,4,3,15},{0,4,4,51},{0,4,4,16},{0,4,5,52},{0,4,5,17},{0,4,6,53},{0,4,6,18},{0,4,7,54},{0,4,7,19},{0,5,0,53},{0,5,0,42},{0,5,0,0},{0,5,0,1},{0,5,0,2},{0,5,0,3},{0,5,0,4},{0,5,0,5},{0,5,0,6},{0,5,0,7},{0,2,6,20},{0,2,6,0},{0,2,6,51},{0,2,6,16},{0,2,13,15},{0,2,13,54},{0,2,13,52},{0,2,13,6},{0,2,13,21},{0,2,13,28},{0,2,13,53},{0,2,13,0},{0,2,13,51},{0,3,10,41},{0,3,10,13},{1,0,0,16},{1,0,0,10},{1,0,0,3},{1,0,1,17},{1,0,1,11},{1,0,1,3},{1,0,2,18},{1,0,2,12},{1,0,2,3},{1,0,3,19},{1,0,3,13},{1,0,3,3},{1,0,4,20},{1,0,4,14},{1,0,4,3},{1,0,5,21},{1,0,5,15},{1,0,5,3},{2,0,0,0},{2,0,0,1},{2,0,0,2},{2,0,0,3},{2,0,0,4},{2,0,0,5},{2,0,0,6},{2,0,0,7},{2,0,0,8}};
207
+ static const int32_t K_BIAS[K_N_BIAS][3] = {{0,0,0},{0,0,4},{0,0,6},{0,0,7},{0,0,8},{0,0,9},{0,0,10},{0,1,1},{0,1,3},{0,0,1},{0,0,2},{0,0,3},{0,0,5},{0,1,0},{0,2,0},{0,2,1},{0,2,2},{0,2,3},{0,2,4},{0,2,5},{0,2,7},{0,2,8},{0,2,9},{0,2,10},{0,2,11},{0,2,12},{0,3,0},{0,3,1},{0,3,2},{0,3,3},{0,3,4},{0,3,5},{0,3,6},{0,3,7},{0,3,8},{0,3,9},{0,1,2},{0,1,4},{0,1,5},{0,3,28},{0,2,15},{0,2,16},{0,2,17},{0,2,18},{0,2,19},{0,2,20},{0,2,21},{0,2,22},{0,2,23},{0,2,24},{0,3,23},{0,3,24},{0,3,25},{0,3,26},{0,3,27},{0,2,14},{0,2,25},{0,2,26},{0,4,8},{0,4,9},{0,3,11},{0,3,12},{0,3,13},{0,3,14},{0,3,15},{0,3,16},{0,3,17},{0,3,18},{0,3,19},{0,3,20},{0,3,21},{0,3,22},{0,3,29},{0,4,0},{0,4,1},{0,4,2},{0,4,3},{0,4,4},{0,4,5},{0,4,6},{0,4,7},{0,5,0},{0,2,6},{0,2,13},{0,3,10},{1,0,0},{1,0,1},{1,0,2},{1,0,3},{1,0,4},{1,0,5},{2,0,0}};
208
+ /* units whose block is skipped when the listed input columns are all zero; the loader proves each one */
209
+ static const int32_t K_GUARD[K_N_GUARD][3 + K_MAX_GUARD] = {{0,0,0,13,-1,-1,-1},{0,0,4,43,-1,-1,-1},{0,0,6,15,14,-1,-1},{0,0,7,62,-1,-1,-1},{0,0,8,64,-1,-1,-1},{0,0,9,65,-1,-1,-1},{0,0,10,44,-1,-1,-1},{0,1,1,23,54,-1,-1},{0,1,3,57,58,-1,-1},{0,0,1,2,-1,-1,-1},{0,0,2,3,-1,-1,-1},{0,0,3,34,-1,-1,-1},{0,0,5,35,-1,-1,-1},{0,1,0,37,-1,-1,-1},{0,2,0,0,-1,-1,-1},{0,2,1,0,-1,-1,-1},{0,2,2,0,-1,-1,-1},{0,2,3,0,-1,-1,-1},{0,2,4,0,-1,-1,-1},{0,2,5,0,-1,-1,-1},{0,2,7,17,18,-1,-1},{0,2,8,17,18,-1,-1},{0,2,9,18,-1,-1,-1},{0,2,10,47,-1,-1,-1},{0,2,11,47,-1,-1,-1},{0,2,12,47,-1,-1,-1},{0,3,0,27,7,10,-1},{0,3,1,27,7,9,-1},{0,3,2,55,2,10,-1},{0,3,3,56,8,10,-1},{0,3,4,54,9,27,10},{0,3,5,58,35,-1,-1},{0,3,6,59,11,-1,-1},{0,3,7,60,35,-1,-1},{0,3,8,61,35,-1,-1},{0,3,9,27,7,10,-1},{0,1,4,4,-1,-1,-1},{0,1,5,4,-1,-1,-1},{0,3,28,34,-1,-1,-1},{0,3,23,34,-1,-1,-1},{0,3,24,34,-1,-1,-1},{0,3,25,34,-1,-1,-1},{0,3,26,34,-1,-1,-1},{0,3,27,34,-1,-1,-1},{0,2,14,6,54,-1,-1},{0,2,25,22,-1,-1,-1},{0,2,26,21,61,-1,-1},{0,4,8,55,23,-1,-1},{0,4,9,56,24,-1,-1},{0,3,11,14,-1,-1,-1},{0,3,12,14,-1,-1,-1},{0,3,13,69,-1,-1,-1},{0,3,14,70,-1,-1,-1},{0,3,15,71,-1,-1,-1},{0,3,16,72,-1,-1,-1},{0,3,17,73,-1,-1,-1},{0,3,18,74,-1,-1,-1},{0,3,19,75,76,-1,-1},{0,3,20,14,-1,-1,-1},{0,3,21,14,-1,-1,-1},{0,3,22,14,-1,-1,-1},{0,3,29,14,25,-1,-1},{0,4,0,12,-1,-1,-1},{0,4,1,13,-1,-1,-1},{0,4,2,14,-1,-1,-1},{0,4,3,15,-1,-1,-1},{0,4,4,16,-1,-1,-1},{0,4,5,17,-1,-1,-1},{0,4,6,18,-1,-1,-1},{0,4,7,19,-1,-1,-1},{0,5,0,42,-1,-1,-1},{0,2,6,20,51,-1,-1},{0,2,13,15,52,-1,-1},{0,3,10,13,-1,-1,-1}};
pylexnet_weights.bin ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:01a918daefff41cabc4064c47a04c6c893d3a018ac6c71ac8a922a3793c7242d
3
+ size 84336
tools/corpus_score.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Score the compiled network on the second corpus against cached Pygments labels. PC only, one process.
2
+ usage: corpus_score.py <fit|check> [report file]. Prints a one-line summary; ranked mismatches go to the report."""
3
+ import re, subprocess, sys
4
+ from collections import Counter, defaultdict
5
+ from pathlib import Path
6
+
7
+ import numpy as np
8
+
9
+ NAMES = ["text", "keyword", "builtin", "string", "comment", "number", "func", "class", "decorator"]
10
+ C = Path(__file__).resolve().parent / "corpus_cache"
11
+ split = sys.argv[1] if len(sys.argv) > 1 else "fit"
12
+ rep = open(sys.argv[2], "w") if len(sys.argv) > 2 else None
13
+ data = np.memmap(C / "data.bin", dtype=np.uint8, mode="r")
14
+ gold = np.memmap(C / "gold.bin", dtype=np.uint8, mode="r")
15
+ tot = wrong = exact = nfiles = 0
16
+ conf = Counter()
17
+ bytext = Counter()
18
+ byprev = Counter()
19
+ sample = {}
20
+ pkgs = defaultdict(lambda: [0, 0])
21
+ wordre = re.compile(rb"[A-Za-z_][A-Za-z_0-9]*|[^\sA-Za-z_0-9]")
22
+ for line in open(C / "index.tsv"):
23
+ sp, pkg, rel, off, n = line.rstrip("\n").split("\t")
24
+ if sp != split:
25
+ continue
26
+ off, n = int(off), int(n)
27
+ d = data[off:off + n].tobytes()
28
+ g = gold[off:off + n]
29
+ out = subprocess.run(["./pylexnet", "--classes"], input=d, capture_output=True).stdout
30
+ p = np.frombuffer(out, dtype=np.uint8)
31
+ assert len(p) == n, rel
32
+ nfiles += 1
33
+ tot += n
34
+ pkgs[pkg][1] += n
35
+ bad = np.nonzero(g != p)[0]
36
+ if not len(bad):
37
+ exact += 1
38
+ continue
39
+ wrong += len(bad)
40
+ pkgs[pkg][0] += len(bad)
41
+ s = e = int(bad[0])
42
+ runs = []
43
+ for i in map(int, bad[1:]):
44
+ if i == e + 1 and g[i] == g[e] and p[i] == p[e]:
45
+ e = i
46
+ else:
47
+ runs.append((s, e)); s = e = i
48
+ runs.append((s, e))
49
+ for s, e in runs:
50
+ k = f"{NAMES[g[s]]}>{NAMES[p[s]]}"
51
+ m = e - s + 1
52
+ conf[k] += m
53
+ a = d.rfind(b"\n", 0, s) + 1
54
+ z = d.find(b"\n", e)
55
+ z = n if z < 0 else z
56
+ t = re.sub(rb"\d", b"0", d[s:e + 1][:18]).decode("utf-8", "replace")
57
+ prev = wordre.findall(d[a:s])[-1:] or [b"^"]
58
+ k1 = (k, t)
59
+ k2 = (k, prev[0].decode("utf-8", "replace"))
60
+ bytext[k1] += m
61
+ byprev[k2] += m
62
+ if k1 not in sample:
63
+ sample[k1] = (rel, d[a:z][:110].decode("utf-8", "replace"))
64
+ if k2 not in sample:
65
+ sample[k2] = (rel, d[a:z][:110].decode("utf-8", "replace"))
66
+ print(f"{split}: files={nfiles} MB={tot/1e6:.1f} wrong={wrong} acc={1-wrong/tot:.6f} exact={exact}/{nfiles}")
67
+ if rep:
68
+ print(f"{split}: files={nfiles} MB={tot/1e6:.1f} wrong={wrong} acc={1-wrong/tot:.6f} exact={exact}/{nfiles}", file=rep)
69
+ print("confusions", conf.most_common(30), file=rep)
70
+ print("by text", file=rep)
71
+ for k, v in bytext.most_common(70):
72
+ print(f"{v:7} {k[0]} {k[1]!r} [{sample[k][0].split('/')[0]}] {sample[k][1]!r}", file=rep)
73
+ print("by previous token", file=rep)
74
+ for k, v in byprev.most_common(50):
75
+ print(f"{v:7} {k[0]} after {k[1]!r} {sample[k][1]!r}", file=rep)
76
+ print("by package", file=rep)
77
+ for k, (w, n) in sorted(pkgs.items(), key=lambda kv: -kv[1][0]):
78
+ print(f"{w:7} {n/1e6:6.1f}MB {1-w/n:.6f} {k}", file=rep)
tools/demo.py ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Render a demo video from the network's real output."""
2
+ import subprocess, sys
3
+ from pathlib import Path
4
+ import numpy as np
5
+ from PIL import Image, ImageDraw, ImageFont
6
+ import imageio
7
+ code = '''@dataclass
8
+ class Greeter:
9
+ """Say hello, politely."""
10
+ name: str = "world"
11
+
12
+ def greet(self, times=3):
13
+ # f-strings nest, and the network keeps track
14
+ for i in range(times):
15
+ print(f"hello {self.name!r} #{i + 1:>2}")
16
+ return len(self.name) * 0x10 + 1.5e3
17
+
18
+ async def main():
19
+ g = Greeter("827 weights")
20
+ await g.greet()
21
+ raise SystemExit(None) from None
22
+ '''
23
+ Path("demo_in.py").write_text(code)
24
+ lab = subprocess.run(["./pylexnet", "--classes", "demo_in.py"], capture_output=True).stdout
25
+ assert len(lab) == len(code.encode())
26
+ PAL = {0:(205,214,244),1:(203,166,247),2:(137,220,235),3:(166,227,161),4:(127,132,156),5:(250,179,135),6:(137,180,250),7:(249,226,175),8:(245,194,231)}
27
+ BG=(30,30,46); W,H=1280,720; S=22; font=ImageFont.truetype(sys.argv[1],S); big=ImageFont.truetype(sys.argv[1],26)
28
+ cw=font.getlength("M"); lh=int(S*1.5)
29
+ def frame(n, caption):
30
+ im=Image.new("RGB",(W,H),BG); d=ImageDraw.Draw(im); x0,y0=40,92
31
+ d.text((40,28),"pylexnet: a Python highlighter that is a neural network",font=big,fill=(205,214,244))
32
+ x=x0;y=y0
33
+ for i,ch in enumerate(code[:n]):
34
+ if ch=="\n": x=x0;y+=lh;continue
35
+ d.text((x,y),ch,font=font,fill=PAL[lab[i]]); x+=cw
36
+ d.rectangle([x,y+2,x+cw-2,y+S+4],fill=(205,214,244))
37
+ d.text((40,H-46),caption,font=font,fill=(127,132,156))
38
+ return np.asarray(im)
39
+ w=imageio.get_writer("demo.mp4",fps=30,codec="libx264",quality=8,macro_block_size=8)
40
+ cap="827 numbers, no training data. Colours are the network's own output."
41
+ for n in range(0,len(code)+1,2): w.append_data(frame(n,cap))
42
+ last=frame(len(code),cap)
43
+ for _ in range(90): w.append_data(last)
44
+ w.close(); print("frames ok", Path("demo.mp4").stat().st_size)
tools/fpcheck.py ADDED
@@ -0,0 +1,37 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """PC tool: false positives of the additive matcher over the fit corpus's words. usage: fpcheck.py [--matcher add]
2
+ Prints words (not in the vocabulary) whose value sum falls in a class window, with their counts."""
3
+ import re, sys
4
+ from collections import Counter
5
+ from pathlib import Path
6
+ import numpy as np
7
+ import compile as c
8
+ vocab = c.pick_vocab(None)
9
+ c.solve_additive(vocab)
10
+ C = Path("corpus_cache")
11
+ data = np.memmap(C / "data.bin", dtype=np.uint8, mode="r")
12
+ gold = np.memmap(C / "gold.bin", dtype=np.uint8, mode="r")
13
+ wre = re.compile(rb"[A-Za-z_\x80-\xff][A-Za-z_0-9\x80-\xff]*")
14
+ cnt = Counter(); nbytes = Counter()
15
+ for line in open(C / "index.tsv"):
16
+ sp, pkg, rel, off, n = line.rstrip("\n").split("\t")
17
+ if sp != "fit":
18
+ continue
19
+ off, n = int(off), int(n)
20
+ d = data[off:off + n].tobytes(); g = gold[off:off + n]
21
+ for m in wre.finditer(d):
22
+ if g[m.start()] == 0: # labelled text by Pygments: a hit here is a false positive
23
+ w = m.group().decode("latin1")
24
+ cnt[w] += 1; nbytes[w] += m.end() - m.start()
25
+ tot = fp = fpb = 0
26
+ hits = Counter(); hitb = Counter()
27
+ for w, n in cnt.items():
28
+ if w in vocab:
29
+ continue
30
+ tot += n
31
+ v = sum(c.ADD_TABLES.get(f, {}).get(code, 0) * k for (f, code), k in c.word_feats(w).items())
32
+ for t in (1, 2, 3, 4, 5):
33
+ if abs(v - t * c.ADD_T) <= c.ADD_EPS:
34
+ hits[w] += n; hitb[w] += nbytes[w]; fp += n; fpb += nbytes[w]
35
+ print(f"non-vocabulary text words: {tot} occurrences, {len(cnt)} distinct; false positives: {fp} occurrences, {fpb} bytes ({fpb / 74787051:.6f} of corpus)")
36
+ for w, n in hits.most_common(40):
37
+ print(f" {w:30} {n:7} occ {hitb[w]:8} B")
tools/score.py ADDED
@@ -0,0 +1,118 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Score ./pylexnet against Pygments' PythonLexer, per byte, on the dev split of the stdlib. PC only, one process.
2
+ Pygments labels are cached in dev_cache/ after the first run. usage: score.py [dev] [report file]"""
3
+ import hashlib
4
+ import subprocess
5
+ import sys
6
+ from collections import Counter
7
+ from pathlib import Path
8
+
9
+ import numpy as np
10
+
11
+ ROOT = Path("/usr/lib/python3.12")
12
+ HERE = Path(__file__).resolve().parent
13
+ CACHE = HERE / "dev_cache"
14
+ NAMES = ["text", "keyword", "builtin", "string", "comment", "number", "func", "class", "decorator"]
15
+
16
+
17
+ def files(split):
18
+ out = []
19
+ for p in sorted(ROOT.rglob("*.py")):
20
+ if "site-packages" in p.parts or "dist-packages" in p.parts or "lib2to3" in p.parts:
21
+ continue
22
+ if p.stat().st_size > 300_000:
23
+ continue
24
+ h = hashlib.sha256(str(p).encode()).digest()[0]
25
+ if (h < 128) == (split == "dev"):
26
+ out.append(p)
27
+ return out
28
+
29
+
30
+ def oracle(text):
31
+ from pygments.lexers import PythonLexer
32
+ from pygments.token import Token as T
33
+ lex = PythonLexer(stripnl=False, ensurenl=False, stripall=False)
34
+ out = bytearray()
35
+ for _, tok, val in lex.get_tokens_unprocessed(text):
36
+ if tok in T.Comment: c = 4
37
+ elif tok in T.Literal.String: c = 3
38
+ elif tok in T.Literal.Number: c = 5
39
+ elif tok in T.Keyword or tok in T.Operator.Word: c = 1
40
+ elif tok in T.Name.Decorator: c = 8
41
+ elif tok in T.Name.Function: c = 6
42
+ elif tok in T.Name.Class: c = 7
43
+ elif tok in T.Name.Builtin or tok in T.Name.Exception: c = 2
44
+ else: c = 0
45
+ out += bytes([c]) * len(val.encode("utf-8", "surrogateescape"))
46
+ return out
47
+
48
+
49
+ def build_cache(split):
50
+ CACHE.mkdir(exist_ok=True)
51
+ idx, data, gold = [], bytearray(), bytearray()
52
+ for p in files(split):
53
+ try:
54
+ text = p.read_text("utf-8")
55
+ except UnicodeDecodeError:
56
+ continue
57
+ text = text.replace("\r\n", "\n")
58
+ d = text.encode()
59
+ g = oracle(text)
60
+ if len(g) != len(d):
61
+ continue
62
+ idx.append(f"{p}\t{len(data)}\t{len(d)}\n")
63
+ data += d; gold += g
64
+ (CACHE / f"{split}_data.bin").write_bytes(data)
65
+ (CACHE / f"{split}_gold.bin").write_bytes(gold)
66
+ (CACHE / f"{split}_index.tsv").write_text("".join(idx))
67
+
68
+
69
+ def main():
70
+ split = sys.argv[1] if len(sys.argv) > 1 else "dev"
71
+ rep = open(sys.argv[2], "w") if len(sys.argv) > 2 else None
72
+ if not (CACHE / f"{split}_index.tsv").exists():
73
+ build_cache(split)
74
+ data = np.memmap(CACHE / f"{split}_data.bin", dtype=np.uint8, mode="r")
75
+ gold = np.memmap(CACHE / f"{split}_gold.bin", dtype=np.uint8, mode="r")
76
+ conf = np.zeros((9, 9), dtype=np.int64)
77
+ exact = nfiles = 0
78
+ bytext = Counter(); sample = {}
79
+ for line in open(CACHE / f"{split}_index.tsv"):
80
+ path, off, n = line.rstrip("\n").split("\t")
81
+ off, n = int(off), int(n)
82
+ d = data[off:off + n].tobytes(); g = gold[off:off + n]
83
+ out = subprocess.run([str(HERE / "pylexnet"), "--classes"], input=d, capture_output=True).stdout
84
+ p = np.frombuffer(out, dtype=np.uint8)
85
+ assert len(p) == n, (path, len(p), n)
86
+ nfiles += 1
87
+ np.add.at(conf, (g, p), 1)
88
+ bad = np.nonzero(g != p)[0]
89
+ if not len(bad):
90
+ exact += 1
91
+ continue
92
+ s = e = int(bad[0]); runs = []
93
+ for i in map(int, bad[1:]):
94
+ if i == e + 1 and g[i] == g[e] and p[i] == p[e]:
95
+ e = i
96
+ else:
97
+ runs.append((s, e)); s = e = i
98
+ runs.append((s, e))
99
+ for s, e in runs:
100
+ k = (f"{NAMES[g[s]]}>{NAMES[p[s]]}", d[s:e + 1][:18].decode("utf-8", "replace"))
101
+ bytext[k] += e - s + 1
102
+ if k not in sample:
103
+ a = d.rfind(b"\n", 0, s) + 1; z = d.find(b"\n", e); z = n if z < 0 else z
104
+ sample[k] = (Path(path).name, d[a:z][:110].decode("utf-8", "replace"))
105
+ tot = conf.sum()
106
+ print(f"{split}: {nfiles} files, {tot / 1e6:.1f} MB")
107
+ print(f"byte accuracy {np.trace(conf) / tot:.5f}, exact files {exact}/{nfiles}, wrong bytes {tot - np.trace(conf)}")
108
+ for k in range(9):
109
+ pr = conf[k, k] / max(1, conf[:, k].sum()); rc = conf[k, k] / max(1, conf[k].sum())
110
+ print(f" {NAMES[k]:10} P {pr:.5f} R {rc:.5f} n {conf[k].sum()}")
111
+ off = conf.copy(); np.fill_diagonal(off, 0)
112
+ print("top confusions:", [(f"{NAMES[i // 9]}>{NAMES[i % 9]}", int(off.ravel()[i])) for i in np.argsort(off.ravel())[::-1][:8] if off.ravel()[i]])
113
+ for k, v in bytext.most_common(25 if rep is None else 80):
114
+ print(f"{v:7} {k[0]} {k[1]!r} [{sample[k][0]}] {sample[k][1]!r}", file=rep or sys.stdout)
115
+
116
+
117
+ if __name__ == "__main__":
118
+ main()
tools/verify.py ADDED
@@ -0,0 +1,24 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """The generated kernel must equal the generic interpreter over the same weights, byte for byte, on the dev split."""
2
+ import subprocess
3
+ import sys
4
+ from pathlib import Path
5
+
6
+ import score
7
+
8
+ HERE = Path(__file__).resolve().parent
9
+ fs = score.files(sys.argv[1] if len(sys.argv) > 1 else "dev")
10
+ bad = tot = 0
11
+ for p in fs:
12
+ try:
13
+ data = p.read_text("utf-8").replace("\r\n", "\n").encode()
14
+ except UnicodeDecodeError:
15
+ continue
16
+ out = subprocess.run([str(HERE / "pylexnet"), "--classes"], input=data, capture_output=True).stdout
17
+ ref = subprocess.run([str(HERE / "pylexnet"), "--interp", "--classes"], input=data, capture_output=True).stdout
18
+ tot += len(data)
19
+ if out != ref:
20
+ bad += 1
21
+ if bad < 4:
22
+ i = next((j for j in range(min(len(out), len(ref))) if out[j] != ref[j]), -1)
23
+ print("MISMATCH", p, i, out[i] if i >= 0 else len(out), ref[i] if i >= 0 else len(ref), data[max(0, i - 30):i + 10])
24
+ print(f"{len(fs)} files, {tot / 1e6:.1f} MB, files differing from rules: {bad}")
vocab_prio.txt ADDED
@@ -0,0 +1 @@
 
 
1
+ ("self",995220),("isinstance",193560),("str",140403),("bool",62712),("__init__",61888),("list",58384),("ValueError",45510),("int",44250),("NotImplementedError",34941),("len",33720),("dict",32812),("tuple",30510),("cls",25692),("type",25108),("super",24615),("getattr",22036),("Exception",20016),("TypeError",19161),("AttributeError",17066),("float",16230),("set",16092),("object",14388),("range",13825),("sorted",13572),("frozenset",13194),("hasattr",12684),("enumerate",11943),("bytes",10995),("BaseException",10686),("RuntimeError",10380),("KeyError",9624),("id",8882),("ImportError",8129),("__repr__",7152),("AssertionError",5978),("NotImplemented",5586),("__getitem__",4785),("print",4640),("OSError",4599),("next",4464),("__call__",4352),("any",4065),("DeprecationWarning",3996),("zip",3897),("repr",3840),("issubclass",3790),("reversed",3576),("format",3408),("IndexError",3290),("setattr",3269),("__hash__",3240),("__eq__",3204),("StopIteration",3107),("__str__",3038),("callable",3016),("__new__",2954),("filter",2946),("__getattr__",2860),("all",2835),("max",2691),("iter",2648),("__iter__",2592),("abs",2526),("hash",2404),("ConnectionError",2355),("__setattr__",2321),("map",2307),("__contains__",2268),("sum",2247),("min",1974),("FileNotFoundError",1955),("KeyboardInterrupt",1887),("open",1860),("__enter__",1764),("bytearray",1674),("ModuleNotFoundError",1672),("__exit__",1648),("__len__",1505),("LookupError",1485),("__setitem__",1474),("dir",1284),("slice",1270),("TimeoutError",1236),("__rich_repr__",1222),("UnicodeDecodeError",1188),("__get__",1183),("ZeroDivisionError",1173),("memoryview",1160),("property",1112),("staticmethod",1092),("__rich_console__",1088),("__aenter__",1060),("input",1060),("__aexit__",1026),("SystemExit",1020),("__bool__",904),("SyntaxError",902),("__getattribute__",880),("__post_init__",858),("globals",854),("FutureWarning",793),("__delitem__",770),("UnicodeEncodeError",756),("RuntimeWarning",714),("Warning",714),("locals",714),("__getstate__",708),("__reduce__",700),("__init_subclass__",697),("StopAsyncIteration",630),("classmethod",627),("__setstate__",612),("round",590),("vars",588),("OverflowError",572),("complex",553),("EOFError",552),("__add__",546),("ord",540),("UserWarning",539),("__anext__",477),("__import__",470),("__del__",455),("PermissionError",450),("compile",448),("divmod",432),("__set__",427),("__aiter__",414),("__rich_measure__",384),("NameError",378),("UnicodeError",372),("chr",357),("__delattr__",352),("__deepcopy__",348),("__await__",342),("GeneratorExit",338),("PendingDeprecationWarning",325),("__ne__",324),("__lt__",318),("delattr",315),("__next__",304),("FileExistsError",300),("__sub__",294),("__coerce__",290),