pylexnet: 827-number Python highlighter network
Browse files- .gitattributes +1 -0
- LICENSE +1 -0
- README.md +77 -0
- builds/accurate-7370/compile.py +371 -0
- builds/accurate-7370/pylexnet.c +467 -0
- builds/accurate-7370/pylexnet_weights.bin +3 -0
- builds/accurate-7370/spec.py +373 -0
- builds/fast-994/pylexnet.c +369 -0
- builds/fast-994/pylexnet_kernel.h +171 -0
- builds/fast-994/pylexnet_weights.bin +3 -0
- compile.py +838 -0
- demo.mp4 +3 -0
- pylexnet.c +369 -0
- pylexnet_kernel.h +209 -0
- pylexnet_weights.bin +3 -0
- tools/corpus_score.py +78 -0
- tools/demo.py +44 -0
- tools/fpcheck.py +37 -0
- tools/score.py +118 -0
- tools/verify.py +24 -0
- vocab_prio.txt +1 -0
.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),
|