open-standards/wai/tools/wai_ops_check.py
Transaction Science bef055304b wai-ops: a vector for each run and load rule the text now states
A review found rules that only the crate and the README settled, and that no
vector pinned: changing the checker to another reading of each changed no
committed byte. integer-operators now states each one, and each now has a
vector that a different reading fails:

- state: a refused decode changes no state; until a decode completes, a state
  input is `init`, sized by input 0 of the decode being made (§10.3).
- run step 1 runs input by input; an input with a dimension of 0 is
  model_mismatch (§4).
- run step 3: conv_transpose's uncropped output of cout planes is capped, not
  one plane; the caps come before the shape rule, both for that output and for
  an uncropped side; the shape rule comes before max_elems (crop_to) (§4, §5.3).
- crop: a scalar start of −1 is refused though −1 + h ≤ H, and at load a
  scalar start is planned at 0, not at its range's low end (§5.10).
- load step 5: chscale's m_bytes and layernorm_c's g_bytes of 2 are refused,
  and 4 loads (§10.4).

subpos_merge now caps its output sides before its shape rule, in the crate
and the checker, as §4 states for every operator. No input within the caps
can tell the two orders apart.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-09 19:31:39 -04:00

2381 lines
84 KiB
Python

#!/usr/bin/env python3
"""Independent checker for the integer operator corpus (extensions/integer-operators.md).
Python standard library only; it shares no code with wai-ops. Written from the
extension's text (and, for the source forms it lowers, integer-payloads), it:
- recomputes every vector file under ops-conformance/vectors/, stage/ and graphs/:
each graph vector's `load` (WHG1 parse and load steps 1-8, section 10.5), its
`profiles` (section 14.1, from the bounds pass and verdict of sections 8.1-8.5),
and each decode's `expect` (the run of section 4, every graph operator of
section 5), and each stage vector's `expect` (section 5E); it writes each file
again canonically (sections 8.5 and 14.1) and requires the bytes of the
committed file;
- re-lowers every parameter set certificates/index.json lists, by the canonical
lowerings of section 13, and requires each lowered graph's bytes, each set's
digest (SPEC 3.1), and that every lowered graph loads; and requires the graph
of each graphs/lower_*.jsonl vector to be the role of the set its id names, as
re-lowered.
The bounds pass decides the verdict only. It does not write the certificate of
section 8.5, which the certifier half of this checker will.
python3 -I tools/wai_ops_check.py ops-conformance # check
python3 -I tools/wai_ops_check.py ops-conformance --write DIR # also write the files
Exit status 1 on any difference or failure.
"""
import hashlib
import json
import math
import os
import struct
import sys
CAP = 1 << 26
I64_MIN, I64_MAX = -(1 << 63), (1 << 63) - 1
I32_MIN, I32_MAX = -(1 << 31), (1 << 31) - 1
class Refuse(Exception):
"""A refusal with a code of section 4 or 5E, and, for a stage operator, `at`."""
def __init__(self, code, at=None):
super().__init__(code)
self.code = code
self.at = at
# --------------------------------------------------------------- section 2
def R(p, s, mode):
"""R_floor, R_half_up, R_half_even (section 2)."""
if s == 0:
return p
if mode == 0:
return p >> s
if mode == 1:
return (p + (1 << (s - 1))) >> s
q = p >> s
r = p - (q << s)
h = 1 << (s - 1)
if r > h or (r == h and q % 2 == 1):
return q + 1
return q
def exp_neg_q16(x):
"""exp_neg_q16(x), x <= 0, in the order section 2 writes it."""
u = -x
n = u // 45426
g = 45426 * n
r = x + g
t = r + 88670
t2 = t * t
q = t2 >> 16
c = 23495 * q
y = (c >> 16) + 22544
return 0 if n >= 63 else y >> n
def rsqrt_q16(v):
r = math.isqrt(v)
return 0 if r == 0 else (1 << 32) // r
def trunc_div(a, b):
q = abs(a) // abs(b)
return q if (a >= 0) == (b > 0) else -q
# --------------------------------------------------------------- WHG1 (section 10)
NAMES = {
0x01: "conv", 0x02: "rescale", 0x03: "chscale", 0x04: "lut16", 0x05: "add",
0x06: "concat", 0x07: "chunk_add", 0x08: "split", 0x09: "pixel_shuffle",
0x0A: "pixel_unshuffle", 0x0B: "clamp", 0x0C: "relu", 0x0D: "leaky",
0x0E: "pad_rep", 0x0F: "crop_to", 0x10: "mul", 0x11: "conv_transpose",
0x12: "igdn", 0x13: "abs", 0x14: "crop", 0x15: "subpos_split",
0x16: "subpos_merge", 0x17: "fill", 0x18: "layernorm_c", 0x19: "l2norm",
0x1A: "softmax", 0x1B: "sigmoid", 0x1C: "elu", 0x1D: "gram", 0x1E: "apply",
}
CODE = {v: k for k, v in NAMES.items()}
TWO_IN = {"add", "crop_to", "mul", "gram", "apply"}
def arity(name):
if name in TWO_IN:
return (2, 1)
if name == "concat":
return (None, 1)
if name == "subpos_split":
return (1, 4)
if name == "subpos_merge":
return (4, 1)
return (1, 1)
class Reader:
def __init__(self, b, pos=0):
self.b, self.p = b, pos
def take(self, n):
if n < 0 or self.p + n > len(self.b):
raise Refuse("model_mismatch")
s = self.b[self.p:self.p + n]
self.p += n
return s
def u(self, n):
return int.from_bytes(self.take(n), "little")
def i(self, n):
return int.from_bytes(self.take(n), "little", signed=True)
def ints(self, count, width):
raw = self.take(count * width)
return [int.from_bytes(raw[k:k + width], "little", signed=True) for k in range(0, len(raw), width)]
def zeros(self, n):
if any(self.take(n)):
raise Refuse("model_mismatch")
def bad():
raise Refuse("model_mismatch")
def read_rq(rd, C):
"""The RQ block (section 10.4) over C channels."""
rq = {}
rq["round"], rq["bias"], rq["m_kind"], rq["s_kind"] = rd.u(1), rd.u(1), rd.u(1), rd.u(1)
rq["m_bytes"], rq["b_bytes"] = rd.u(1), rd.u(1)
if rd.u(2) != 0:
bad()
if rq["round"] > 2 or rq["bias"] > 2 or rq["m_kind"] > 2 or rq["s_kind"] not in (1, 2):
bad()
if rq["m_kind"] == 0:
if rq["m_bytes"] != 0:
bad()
elif rq["m_bytes"] not in (4, 8):
bad()
if rq["bias"] == 0:
if rq["b_bytes"] != 0:
bad()
elif rq["b_bytes"] not in (4, 8):
bad()
nm = {0: 0, 1: 1, 2: C}[rq["m_kind"]]
rq["mult"] = rd.ints(nm, rq["m_bytes"]) if nm else []
ns = 1 if rq["s_kind"] == 1 else C
rq["shift"] = list(rd.take(ns))
if any(s > 127 for s in rq["shift"]):
bad()
rq["biasv"] = rd.ints(C, rq["b_bytes"]) if rq["bias"] else []
return rq
def rq_m(rq, o):
return None if rq["m_kind"] == 0 else rq["mult"][0 if rq["m_kind"] == 1 else o]
def rq_s(rq, o):
return rq["shift"][0 if rq["s_kind"] == 1 else o]
def rq_apply(rq, o, v):
"""rq_o(v), section 5.1."""
t = v + (rq["biasv"][o] if rq["bias"] == 1 else 0)
m = rq_m(rq, o)
p = t * (1 if m is None else m)
q = R(p, rq_s(rq, o), rq["round"])
return q + (rq["biasv"][o] if rq["bias"] == 2 else 0)
def read_operand(rd, n_scalars):
kind = rd.u(1)
rd.zeros(3)
v = rd.u(4)
if kind == 0:
return ("const", v)
if kind == 1 and v < n_scalars:
return ("scalar", v)
bad()
def read_params(name, pb, cin, n_scalars):
rd = Reader(pb)
P = {}
if name == "conv":
for k in ("cout", "kh", "kw", "sh", "sw", "ph", "pw", "groups"):
P[k] = rd.u(4)
P["w_bytes"] = rd.u(1)
rd.zeros(3)
if min(P["cout"], P["kh"], P["kw"], P["sh"], P["sw"], P["groups"]) == 0 or P["w_bytes"] not in (1, 2, 4, 8):
bad()
if cin % P["groups"] or P["cout"] % P["groups"]:
bad()
P["rq"] = read_rq(rd, P["cout"])
P["w"] = rd.ints(P["cout"] * (cin // P["groups"]) * P["kh"] * P["kw"], P["w_bytes"])
elif name == "conv_transpose":
for k in ("cout", "kh", "kw", "sh", "sw", "ph", "pw", "oph", "opw"):
P[k] = rd.u(4)
P["w_bytes"] = rd.u(1)
rd.zeros(3)
if min(P["cout"], P["kh"], P["kw"], P["sh"], P["sw"]) == 0 or P["w_bytes"] not in (1, 2, 4, 8):
bad()
P["rq"] = read_rq(rd, P["cout"])
P["w"] = rd.ints(cin * P["cout"] * P["kh"] * P["kw"], P["w_bytes"])
elif name == "rescale":
P["rq"] = read_rq(rd, cin)
elif name == "chscale":
P["scalar"], P["n_rows"] = rd.u(4), rd.u(4)
P["round"], P["m_bytes"], P["shift"] = rd.u(1), rd.u(1), rd.u(1)
rd.zeros(1)
if P["scalar"] >= n_scalars or P["n_rows"] == 0 or P["m_bytes"] not in (4, 8) or P["shift"] > 127 or P["round"] > 2:
bad()
P["M"] = rd.ints(P["n_rows"] * cin, P["m_bytes"])
elif name == "mul":
P["rq"] = read_rq(rd, cin)
if P["rq"]["bias"] != 0:
bad()
elif name == "lut16":
P["e_in"], P["e_out"] = rd.i(1), rd.i(1)
rd.zeros(2)
P["T"] = rd.ints(65536, 2)
if P["T"][0] != 0:
bad()
elif name in ("add", "concat", "chunk_add", "relu", "abs", "crop_to", "softmax", "sigmoid"):
pass
elif name == "split":
P["start"], P["count"] = rd.u(4), rd.u(4)
if P["count"] == 0:
bad()
elif name in ("pixel_shuffle", "pixel_unshuffle"):
P["r"] = rd.u(4)
if P["r"] == 0:
bad()
elif name == "clamp":
P["lo"], P["hi"] = rd.i(8), rd.i(8)
if P["lo"] > P["hi"]:
bad()
elif name == "leaky":
P["a"], P["f"] = rd.i(8), rd.u(4)
if P["f"] > 63:
bad()
elif name == "pad_rep":
P["m"] = rd.u(4)
if P["m"] == 0:
bad()
elif name == "crop":
P["ops"] = [read_operand(rd, n_scalars) for _ in range(4)]
elif name in ("subpos_split", "subpos_merge"):
P["order"] = list(rd.take(4))
if sorted(P["order"]) != [0, 1, 2, 3]:
bad()
elif name == "fill":
P["kind"] = rd.u(1)
rd.zeros(7)
P["value"] = rd.i(8)
if not (P["kind"] == 0 or (P["kind"] == 1 and 0 <= P["value"] < n_scalars)):
bad()
elif name == "igdn":
P["gf"], P["frac"] = rd.u(4), rd.u(4)
P["g_bytes"] = rd.u(1)
rd.zeros(7)
if P["gf"] > 127 or P["frac"] > 127 or P["g_bytes"] not in (4, 8):
bad()
P["gamma"] = rd.ints(cin * cin, P["g_bytes"])
P["beta"] = rd.ints(cin, 8)
elif name == "layernorm_c":
P["eps"] = rd.u(8)
P["g_bytes"] = rd.u(1)
rd.zeros(7)
if P["eps"] == 0 or P["g_bytes"] not in (4, 8):
bad()
P["gamma"] = rd.ints(cin, P["g_bytes"])
P["beta"] = rd.ints(cin, 8)
elif name == "l2norm":
P["eps"] = rd.u(8)
if P["eps"] == 0:
bad()
elif name == "elu":
P["alpha"] = rd.i(8)
elif name in ("gram", "apply"):
P["groups"] = rd.u(4)
P["rq"] = read_rq(rd, cin)
if P["groups"] == 0 or P["rq"]["m_kind"] != 0 or P["rq"]["bias"] != 0 or P["rq"]["s_kind"] != 1:
bad()
if rd.p != len(pb):
bad()
return P
def parse_whg1(b):
"""Load steps 1-6 of section 10.5. Every refusal is model_mismatch."""
if len(b) < 32 or b[0:4] != b"WHG1" or b[4] != 1 or b[5] != 1:
bad()
rd = Reader(b, 6)
r0 = rd.u(2)
n_t, n_o = rd.u(4), rd.u(4)
n_i, n_out, n_s = rd.u(2), rd.u(2), rd.u(2)
r1 = rd.u(2)
max_elems = rd.u(4)
r2 = rd.u(4)
if r0 or r1 or r2:
bad()
if not (n_t and n_o and n_i and n_out) or not (1 <= max_elems <= CAP):
bad()
if 32 + 16 * n_s + 32 * n_i + 4 * n_out + 24 * n_t + 12 * n_o > len(b):
bad()
g = {"max_elems": max_elems, "scalars": [], "inputs": [], "outputs": [], "tensors": [], "ops": []}
for _ in range(n_s):
lo, hi = rd.i(8), rd.i(8)
if lo > hi:
bad()
g["scalars"].append((lo, hi))
for _ in range(n_i):
t, mh, mw = rd.u(4), rd.u(4), rd.u(4)
kind = rd.u(1)
rd.zeros(3)
src = rd.u(4)
rd.zeros(4)
init = rd.i(8)
if (mh == 0) != (mw == 0) or kind > 1 or t >= n_t:
bad()
if kind == 0 and (src or init):
bad()
g["inputs"].append({"tensor": t, "max_h": mh, "max_w": mw, "kind": kind, "src": src, "init": init})
for _ in range(n_out):
t = rd.u(4)
if t >= n_t:
bad()
g["outputs"].append(t)
for _ in range(n_t):
ch = rd.u(4)
ex = rd.i(1)
pol = rd.u(1)
rd.zeros(2)
lo, hi = rd.i(8), rd.i(8)
if ch == 0 or pol > 2:
bad()
if pol == 0 and (lo or hi):
bad()
if pol != 0 and lo > hi:
bad()
g["tensors"].append({"c": ch, "e": ex, "policy": pol, "lo": lo, "hi": hi})
for _ in range(n_o):
code, ni, no = rd.u(1), rd.u(1), rd.u(1)
rd.zeros(1)
name = NAMES.get(code)
if name is None:
bad()
ai, ao = arity(name)
if no != ao or (ai is None and ni < 2) or (ai is not None and ni != ai):
bad()
ins = [rd.u(4) for _ in range(ni)]
outs = [rd.u(4) for _ in range(no)]
if any(t >= n_t for t in ins + outs):
bad()
plen = rd.u(4)
pb = rd.take(plen)
cin = g["tensors"][ins[0]]["c"]
g["ops"].append({"name": name, "ins": ins, "outs": outs, "p": read_params(name, pb, cin, n_s)})
if rd.p != len(b):
bad()
rules(g)
return g
def rules(g):
"""Load step 6: the graph rules of section 10.3, state, exponents (3, 5.6)."""
T = g["tensors"]
defined = [False] * len(T)
for i in g["inputs"]:
if defined[i["tensor"]] or T[i["tensor"]]["policy"] == 0:
bad()
defined[i["tensor"]] = True
if g["inputs"][0]["kind"] != 0:
bad()
if len({i["max_h"] != 0 for i in g["inputs"]}) != 1:
bad()
for op in g["ops"]:
if not all(defined[t] for t in op["ins"]):
bad()
for t in op["outs"]:
if defined[t]:
bad()
defined[t] = True
if not all(defined):
bad()
for i in g["inputs"]:
if i["kind"] == 1:
if i["src"] >= len(g["outputs"]):
bad()
rec, out = T[i["tensor"]], T[g["outputs"][i["src"]]]
if out["c"] != rec["c"] or not (rec["lo"] <= i["init"] <= rec["hi"]):
bad()
for op in g["ops"]:
n, P = op["name"], op["p"]
C = T[op["ins"][0]]["c"]
cs = [T[t]["c"] for t in op["ins"]]
want = C
if n in ("conv", "conv_transpose"):
want = P["cout"]
elif n == "add":
want = C if cs[1] == C else None
elif n == "mul":
want = C if cs[1] in (C, 1) else None
elif n == "concat":
want = sum(cs)
elif n == "chunk_add":
want = C // 2 if C % 2 == 0 else None
elif n == "split":
want = P["count"] if P["start"] + P["count"] <= C else None
elif n == "pixel_shuffle":
want = C // (P["r"] ** 2) if C % (P["r"] ** 2) == 0 else None
elif n == "pixel_unshuffle":
want = C * P["r"] ** 2
elif n == "subpos_merge":
want = C if all(c == C for c in cs) else None
elif n == "fill":
want = T[op["outs"][0]]["c"]
elif n in ("gram", "apply"):
want = C if cs[1] == C and C % P["groups"] == 0 else None
elif n == "chscale":
lo, hi = g["scalars"][P["scalar"]]
want = C if lo >= 0 and hi <= P["n_rows"] - 1 else None
if want is None or any(T[t]["c"] != want for t in op["outs"]):
bad()
if n in ("add", "concat", "chunk_add"):
es = [T[t]["e"] for t in op["ins"] + op["outs"]]
if not (all(e == -128 for e in es) or (all(e != -128 for e in es) and len(set(es)) == 1)):
bad()
if n == "lut16":
if P["e_in"] != T[op["ins"][0]]["e"] or P["e_out"] != T[op["outs"][0]]["e"]:
bad()
# --------------------------------------------------------------- plans (section 4, 10.5 step 7)
class Big(Exception):
pass
class Shape(Exception):
pass
def capped(v):
if v > CAP:
raise Big()
return v
def op_sizes(g, op, hw, scal):
"""The output sizes of one operator, from its inputs' sizes. `scal` is None at load
(scalars by their ranges) or the scalar values."""
n, P = op["name"], op["p"]
h, w = hw[op["ins"][0]]
same = lambda o: hw[o] == (h, w)
if n == "conv":
hp, wp = capped(h + 2 * P["ph"]), capped(w + 2 * P["pw"])
if hp < P["kh"] or wp < P["kw"]:
raise Shape()
return [((hp - P["kh"]) // P["sh"] + 1, (wp - P["kw"]) // P["sw"] + 1)]
if n == "conv_transpose":
uh = capped((h - 1) * P["sh"] + P["kh"] + P["oph"])
uw = capped((w - 1) * P["sw"] + P["kw"] + P["opw"])
capped(P["cout"] * uh * uw)
if uh <= 2 * P["ph"] or uw <= 2 * P["pw"]:
raise Shape()
return [(uh - 2 * P["ph"], uw - 2 * P["pw"])]
if n in ("add", "mul"):
if not same(op["ins"][1]):
raise Shape()
return [(h, w)]
if n in ("concat", "subpos_merge"):
# subpos_merge's output sides are sizes it forms: capped before the shape rule.
out = (capped(2 * h), capped(2 * w)) if n == "subpos_merge" else (h, w)
if not all(same(t) for t in op["ins"]):
raise Shape()
return [out]
if n == "pixel_shuffle":
return [(capped(h * P["r"]), capped(w * P["r"]))]
if n == "pixel_unshuffle":
if h % P["r"] or w % P["r"]:
raise Shape()
return [(h // P["r"], w // P["r"])]
if n == "pad_rep":
m = P["m"]
return [(capped(-(-h // m) * m), capped(-(-w // m) * m))]
if n == "crop_to":
rh, rw = hw[op["ins"][1]]
if rh > h or rw > w:
raise Shape()
return [(rh, rw)]
if n == "crop":
y0, x0, ch, cw = P["ops"]
def side(start, length, size):
if scal is not None:
val = lambda o: o[1] if o[0] == "const" else scal[o[1]]
s0, ln = val(start), val(length)
if s0 < 0 or ln < 1 or s0 + ln > size:
raise Shape()
return ln
s0 = start[1] if start[0] == "const" else 0
ln = length[1] if length[0] == "const" else min(g["scalars"][length[1]][1], size - s0)
if ln < 1 or s0 + ln > size:
raise Shape()
return ln
return [(side(y0, ch, h), side(x0, cw, w))]
if n == "subpos_split":
if h % 2 or w % 2:
raise Shape()
return [(h // 2, w // 2)] * 4
if n == "gram":
if not same(op["ins"][1]):
raise Shape()
return [(1, g["tensors"][op["ins"][0]]["c"] // P["groups"])]
if n == "apply":
d = g["tensors"][op["ins"][0]]["c"] // P["groups"]
if (h, w) != (1, d):
raise Shape()
return [hw[op["ins"][1]]]
return [(h, w)]
def walk(g, hw, scal, load):
for op in g["ops"]:
try:
outs = op_sizes(g, op, hw, scal)
except Big:
raise Refuse("too_large")
except Shape:
raise Refuse("model_mismatch" if load else "geometry_mismatch")
for t, (oh, ow) in zip(op["outs"], outs):
if g["tensors"][t]["c"] * oh * ow > g["max_elems"]:
raise Refuse("too_large")
hw[t] = (oh, ow)
def load(b):
"""Load steps 1-8 of section 10.5. Step 8 is the bounds pass and the verdict
(section 8): a graph whose verdict meets neither profile is refused, so a graph
that loads meets acc64 at least, and g["verdict"] says whether it meets acc32."""
g = parse_whg1(b)
if g["inputs"][0]["max_h"] == 0:
if any(op["name"] in ("gram", "l2norm", "softmax") for op in g["ops"]):
bad()
g["sizes"] = None
else:
hw = {}
for i in g["inputs"]:
c = g["tensors"][i["tensor"]]["c"]
if c * i["max_h"] * i["max_w"] > g["max_elems"]:
raise Refuse("too_large")
hw[i["tensor"]] = (i["max_h"], i["max_w"])
walk(g, hw, None, True)
g["sizes"] = hw
g["verdict"] = bounds(g)
if not g["verdict"]["acc64"]:
bad()
return g
# --------------------------------------------------------------- bounds (section 8)
SAT = 1 << 255
# Section 7.1: the width of each class under each profile, in bits.
WIDTH = {
"acc64": {"tensor": 64, "acc": 64, "wide": 128, "product": 128},
"acc32": {"tensor": 32, "acc": 32, "wide": 32, "product": 64},
}
# The class of each named intermediate (sections 5 and 2).
CLASS = {
"conv.term": "acc", "conv.acc": "acc", "convT.term": "acc", "convT.acc": "acc",
"rq.t": "acc", "rq.p": "product", "rq.q": "acc", "rq.y": "acc",
"mul.p": "acc", "leaky.p": "acc",
"gram.term": "acc", "gram.acc": "acc", "apply.term": "acc", "apply.acc": "acc",
"igdn.x2": "wide", "igdn.gx2": "product", "igdn.term": "wide", "igdn.N": "wide",
"igdn.r": "acc", "igdn.xr": "wide",
"ln.S": "wide", "ln.T": "wide", "ln.m": "wide", "ln.v": "wide", "ln.dk": "wide", "ln.ng": "wide",
"ln.d": "acc", "rsq.num": "acc", "ln.k": "acc", "ln.n": "acc",
"l2.T": "wide", "l2.vk": "wide", "l2.k": "acc",
"sm.diff": "acc", "sm.sum": "acc", "sm.num": "acc", "sig.num": "acc", "elu.p": "wide",
"exp.negx": "acc", "exp.nl": "acc", "exp.t2": "acc", "exp.c": "acc",
}
def fits_bits(iv, n):
return -(1 << (n - 1)) <= iv[0] and iv[1] <= (1 << (n - 1)) - 1
def hull(ivs):
return (min(v[0] for v in ivs), max(v[1] for v in ivs))
def isqrt_ceil(n):
r = math.isqrt(n)
return r if r * r == n else r + 1
class Node:
"""One node of the bounds pass. Every +, -, x and floor division (a shift
included) goes through sat (section 8.1), and the node is saturated when sat
changes any result. min, max, |.|, negation and isqrt cannot leave
[-2^255, 2^255] when their operands lie in it. `rec` holds each named
intermediate's interval, hulled over channels."""
def __init__(self):
self.saturated = False
self.rec = {}
def sat(self, v):
if v > SAT:
self.saturated = True
return SAT
if v < -SAT:
self.saturated = True
return -SAT
return v
def add(self, a, b):
return self.sat(a + b)
def sub(self, a, b):
return self.sat(a - b)
def mul(self, a, b):
return self.sat(a * b)
def div(self, a, b):
return self.sat(a // b)
def shr(self, a, s):
return self.sat(a >> s)
def note(self, key, iv):
old = self.rec.get(key)
self.rec[key] = iv if old is None else (min(old[0], iv[0]), max(old[1], iv[1]))
return iv
def term(self, w, x):
"""The term interval of a weight w over x (section 8.2)."""
a, b = self.mul(w, x[0]), self.mul(w, x[1])
return (min(a, b), max(a, b))
def corner(self, x, y):
c = [self.mul(x[0], y[0]), self.mul(x[0], y[1]), self.mul(x[1], y[0]), self.mul(x[1], y[1])]
return (min(c), max(c))
def A(self, terms, b):
"""The partial-sum interval A(K, b) of section 8.2."""
lo, hi = min(0, b), max(0, b)
for t in terms:
lo = self.add(lo, min(0, t[0]))
hi = self.add(hi, max(0, t[1]))
return (lo, hi)
def R(self, p, s, mode):
"""R_floor, R_half_up, R_half_even (section 2), as written there."""
if s == 0:
return p
if mode == 0:
return self.shr(p, s)
if mode == 1:
return self.shr(self.add(p, 1 << (s - 1)), s)
q = self.shr(p, s)
r = self.sub(p, self.mul(q, 1 << s))
h = 1 << (s - 1)
return self.add(q, 1) if r > h or (r == h and q % 2 == 1) else q
def rq(self, t, m, s, mode, post):
"""The rq.p, rq.q and rq.y rules of section 8.3 for one channel."""
self.note("rq.t", t)
P = self.term(m, t)
a = (1 << (s - 1)) if mode != 0 and s >= 1 else 0
self.note("rq.p", (P[0], self.add(P[1], a)))
q = (self.R(P[0], s, mode), self.R(P[1], s, mode))
self.note("rq.q", q)
return self.note("rq.y", (self.add(q[0], post), self.add(q[1], post)))
def exp(self, u):
"""The exp_neg_q16 rule of section 8.3, given the interval of u = -x."""
self.note("exp.negx", u)
self.note("exp.nl", (self.mul(45426, self.div(u[0], 45426)), self.mul(45426, self.div(u[1], 45426))))
self.note("exp.t2", (self.mul(43245, 43245), self.mul(88670, 88670)))
self.note("exp.c", (0, self.mul(23495, self.div(self.mul(88670, 88670), 1 << 16))))
def bpre(rq, o):
return rq["biasv"][o] if rq["bias"] == 1 else 0
def bpost(rq, o):
return rq["biasv"][o] if rq["bias"] == 2 else 0
def rq_mult(rq, o):
m = rq_m(rq, o)
return 1 if m is None else m
def node_bounds(nb, g, op, iv):
"""The producer results of one operator (section 8.3), recording its named
intermediates in nb. Refuses (model_mismatch) when lut16's input range is not
within [-32767, 32767] (section 5.6)."""
T = g["tensors"]
n, P = op["name"], op["p"]
x = iv[op["ins"][0]]
l, h = x
C = T[op["ins"][0]]["c"]
if n == "conv":
rq, cg, kk = P["rq"], C // P["groups"], P["kh"] * P["kw"]
outs = []
for o in range(P["cout"]):
taps = [nb.term(P["w"][(o * cg + ci) * kk + k], x) for ci in range(cg) for k in range(kk)]
nb.note("conv.term", hull(taps))
acc = nb.note("conv.acc", nb.A(taps, bpre(rq, o)))
outs.append(nb.rq(acc, rq_mult(rq, o), rq_s(rq, o), rq["round"], bpost(rq, o)))
return [hull(outs)]
if n == "conv_transpose":
rq, cout, kh, kw, sh, sw = P["rq"], P["cout"], P["kh"], P["kw"], P["sh"], P["sw"]
outs = []
for o in range(cout):
every, by_res = [], {}
for ci in range(C):
for i in range(kh):
for j in range(kw):
t = nb.term(P["w"][((ci * cout + o) * kh + i) * kw + j], x)
every.append(t)
by_res.setdefault((i % sh, j % sw), []).append(t)
nb.note("convT.term", hull(every))
b = bpre(rq, o)
accs = [nb.A(by_res[r], b) for r in sorted(by_res)]
if len(by_res) < sh * sw:
# A residue that no tap has: A over no terms.
accs.append(nb.A([], b))
acc = nb.note("convT.acc", hull(accs))
outs.append(nb.rq(acc, rq_mult(rq, o), rq_s(rq, o), rq["round"], bpost(rq, o)))
return [hull(outs)]
if n == "rescale":
rq = P["rq"]
t = lambda c: (nb.add(l, bpre(rq, c)), nb.add(h, bpre(rq, c)))
return [hull([nb.rq(t(c), rq_mult(rq, c), rq_s(rq, c), rq["round"], bpost(rq, c)) for c in range(C)])]
if n == "chscale":
lo_r, hi_r = g["scalars"][P["scalar"]]
s, mode = P["shift"], P["round"]
a = (1 << (s - 1)) if mode != 0 and s >= 1 else 0
outs = []
for c in range(C):
ps, qs = [], []
for r in range(lo_r, hi_r + 1):
Pm = nb.term(P["M"][r * C + c], x)
ps.append((Pm[0], nb.add(Pm[1], a)))
qs.append((nb.R(Pm[0], s, mode), nb.R(Pm[1], s, mode)))
nb.note("rq.t", x)
nb.note("rq.p", hull(ps))
q = nb.note("rq.q", hull(qs))
outs.append(nb.note("rq.y", q))
return [hull(outs)]
if n == "mul":
rq = P["rq"]
mp = nb.note("mul.p", nb.corner(x, iv[op["ins"][1]]))
return [hull([nb.rq(mp, rq_mult(rq, c), rq_s(rq, c), rq["round"], 0) for c in range(C)])]
if n in ("gram", "apply"):
rq = P["rq"]
t = nb.note(n + ".term", nb.corner(x, iv[op["ins"][1]]))
if n == "gram":
L = max(g["sizes"][u][0] * g["sizes"][u][1] for u in op["ins"])
else:
L = C // P["groups"]
acc = nb.note(n + ".acc", (nb.mul(L, min(0, t[0])), nb.mul(L, max(0, t[1]))))
return [nb.rq(acc, 1, rq_s(rq, 0), rq["round"], 0)]
if n == "add":
b = iv[op["ins"][1]]
return [(nb.add(l, b[0]), nb.add(h, b[1]))]
if n == "chunk_add":
return [(nb.mul(2, l), nb.mul(2, h))]
if n == "clamp":
return [(min(max(l, P["lo"]), P["hi"]), min(max(h, P["lo"]), P["hi"]))]
if n == "relu":
return [(max(l, 0), max(h, 0))]
if n == "abs":
return [(l, h) if l >= 0 else ((-h, -l) if h <= 0 else (0, max(-l, h)))]
if n == "leaky":
parts = []
if l < 0:
p = nb.note("leaky.p", nb.corner((l, min(h, -1)), (P["a"], P["a"])))
parts.append((nb.shr(p[0], P["f"]), nb.shr(p[1], P["f"])))
else:
nb.note("leaky.p", (0, 0))
if h >= 0:
parts.append((max(l, 0), h))
return [hull(parts)]
if n == "lut16":
if l < -32767 or h > 32767:
bad()
vals = P["T"][l + 32768:h + 32769]
return [(min(vals), max(vals))]
if n == "igdn":
l2, h2 = nb.mul(l, l), nb.mul(h, h)
x2 = nb.note("igdn.x2", (0, max(l2, h2)) if l <= 0 <= h else (min(l2, h2), max(l2, h2)))
outs = []
for i in range(C):
terms = []
for j in range(C):
gm = P["gamma"][i * C + j]
gx = nb.note("igdn.gx2", nb.corner((gm, gm), x2))
terms.append(nb.note("igdn.term", (nb.shr(gx[0], P["gf"]), nb.shr(gx[1], P["gf"]))))
nb.note("igdn.N", nb.A(terms, P["beta"][i]))
# The final norm, a left fold over j from beta_i; not recorded.
f_lo = f_hi = P["beta"][i]
for t in terms:
f_lo, f_hi = nb.add(f_lo, t[0]), nb.add(f_hi, t[1])
r = nb.note("igdn.r", (math.isqrt(max(0, f_lo)), math.isqrt(max(0, f_hi))))
xr = nb.note("igdn.xr", nb.corner(x, r))
outs.append((nb.shr(xr[0], P["frac"]), nb.shr(xr[1], P["frac"])))
return [hull(outs)]
if n == "layernorm_c":
e = P["eps"]
r0 = math.isqrt(e)
nb.note("ln.S", (nb.mul(C, min(0, l)), nb.mul(C, max(0, h))))
half = C // 2
nb.note("ln.m", (nb.sub(nb.mul(C, l), half), nb.add(nb.mul(C, h), half)))
d = nb.sub(h, l)
nb.note("ln.d", (nb.sub(l, h), d))
dd = nb.mul(d, d)
nb.note("ln.T", (0, nb.mul(C, dd)))
nb.note("ln.v", (e, nb.add(dd, e)))
nb.note("rsq.num", (1 << 32, 1 << 32))
k = nb.div(1 << 32, r0)
nb.note("ln.k", (0, k))
ic = isqrt_ceil(C)
big = nb.mul(nb.mul(ic, 1 << 32), nb.add(r0, 1))
D = min(nb.mul(d, k), -nb.div(-big, r0))
nb.note("ln.dk", (-D, D))
nmax = nb.add(nb.div(nb.mul(nb.mul(ic, 1 << 16), nb.add(r0, 1)), r0), 1)
N = min(nb.add(nb.shr(nb.mul(d, k), 16), 1), nmax)
nb.note("ln.n", (-N, N))
outs = []
for c in range(C):
gm = P["gamma"][c]
ng = nb.note("ln.ng", nb.corner((-N, N), (gm, gm)))
outs.append((nb.add(nb.shr(ng[0], 16), P["beta"][c]), nb.add(nb.shr(ng[1], 16), P["beta"][c])))
return [hull(outs)]
if n == "l2norm":
e = P["eps"]
hh, ww = g["sizes"][op["ins"][0]]
X = max(abs(l), abs(h))
nb.note("l2.T", (0, nb.add(nb.mul(nb.mul(hh, ww), nb.mul(X, X)), e)))
nb.note("rsq.num", (1 << 32, 1 << 32))
nb.note("l2.k", (0, nb.div(1 << 32, math.isqrt(e))))
nb.note("l2.vk", (-(1 << 32), 1 << 32))
return [(-(1 << 16), 1 << 16)]
if n == "softmax":
nb.note("sm.diff", (nb.sub(l, h), 0))
nb.exp((0, nb.sub(h, l)))
wm = g["sizes"][op["ins"][0]][1]
nb.note("sm.sum", (0, nb.mul(wm, 65553)))
nb.note("sm.num", (0, nb.add(nb.mul(65553, 1 << 16), nb.div(nb.mul(wm, 65553), 2))))
return [(0, 1 << 16)]
if n == "sigmoid":
nb.exp((l, h) if l >= 0 else ((-h, -l) if h <= 0 else (0, max(-l, h))))
nb.note("sig.num", (0, nb.mul(65553, 1 << 16)))
return [(0, 1 << 16)]
if n == "elu":
nb.exp((max(0, -h), max(0, -l)))
al = P["alpha"]
p = nb.note("elu.p", nb.corner((al, al), (-(1 << 16), 17)))
parts = []
if h > 0:
parts.append((max(l, 1), h))
if l <= 0:
parts.append((nb.shr(p[0], 16), nb.shr(p[1], 16)))
return [hull(parts)]
if n in ("concat", "subpos_merge"):
return [hull([iv[t] for t in op["ins"]])]
if n == "subpos_split":
return [x] * 4
if n == "fill":
return [(P["value"], P["value"]) if P["kind"] == 0 else g["scalars"][P["value"]]]
# split, pixel_shuffle, pixel_unshuffle, pad_rep, crop_to (its first input), crop.
return [x]
def params32(op):
"""The parameter conditions of section 7.2: whether the node meets every one."""
n, P = op["name"], op["p"]
f32 = lambda v: I32_MIN <= v <= I32_MAX
ok = True
rq = P.get("rq")
if rq is not None:
ok = all(map(f32, rq["mult"])) and all(s <= 63 for s in rq["shift"]) and (rq["bias"] != 2 or all(map(f32, rq["biasv"])))
if n in ("conv", "conv_transpose"):
ok = ok and all(map(f32, P["w"]))
elif n == "chscale":
ok = all(map(f32, P["M"])) and P["shift"] <= 63
elif n == "igdn":
ok = all(map(f32, P["gamma"])) and P["gf"] <= 63 and P["frac"] <= 63
elif n == "leaky":
ok = f32(P["a"])
return ok
def bounds(g):
"""The bounds pass (sections 8.1-8.4) and the verdict (section 8.5) of a graph
that passed load steps 1-7: {"acc32": bool, "acc64": bool}. Refuses
(model_mismatch) where the pass itself refuses the graph at load: a `check`
interval that is empty (section 8.1), or lut16's input range (section 5.6)."""
T = g["tensors"]
ok = {"acc32": True, "acc64": True}
iv = {}
declared = [(T[i["tensor"]]["lo"], T[i["tensor"]]["hi"]) for i in g["inputs"]] + list(g["scalars"])
for r in declared:
for p in ok:
ok[p] = ok[p] and fits_bits(r, WIDTH[p]["tensor"])
for i in g["inputs"]:
iv[i["tensor"]] = (T[i["tensor"]]["lo"], T[i["tensor"]]["hi"])
for op in g["ops"]:
nb = Node()
results = node_bounds(nb, g, op, iv)
fit = {p: not nb.saturated for p in ok}
for p in ok:
for k, r in nb.rec.items():
fit[p] = fit[p] and fits_bits(r, WIDTH[p][CLASS[k]])
for r in results:
fit[p] = fit[p] and fits_bits(r, WIDTH[p]["tensor"])
fit["acc32"] = fit["acc32"] and params32(op)
for t, (a, b) in zip(op["outs"], results):
rec = T[t]
if rec["policy"] == 0:
iv[t] = (a, b)
elif rec["policy"] == 1:
iv[t] = (min(max(a, rec["lo"]), rec["hi"]), min(max(b, rec["lo"]), rec["hi"]))
else:
fit["acc32"] = False
iv[t] = (max(a, rec["lo"]), min(b, rec["hi"]))
if iv[t][0] > iv[t][1]:
bad()
for p in ok:
ok[p] = ok[p] and fit[p]
return ok
# --------------------------------------------------------------- the operators (section 5)
class T:
"""A tensor [c, h, w], elements in CHW order."""
def __init__(self, c, h, w, d):
self.c, self.h, self.w, self.d = c, h, w, d
def at(self, c, y, x):
return self.d[(c * self.h + y) * self.w + x]
def ev_conv(x, P, oh, ow):
cin, H, W = x.c, x.h, x.w
G = P["groups"]
cg, og = cin // G, P["cout"] // G
kh, kw = P["kh"], P["kw"]
d = []
for o in range(P["cout"]):
base = (o // og) * cg
for y in range(oh):
for xx in range(ow):
acc = 0
for ci in range(cg):
for i in range(kh):
u = y * P["sh"] + i - P["ph"]
if not 0 <= u < H:
continue
for j in range(kw):
v = xx * P["sw"] + j - P["pw"]
if 0 <= v < W:
acc += P["w"][((o * cg + ci) * kh + i) * kw + j] * x.at(base + ci, u, v)
d.append(rq_apply(P["rq"], o, acc))
return T(P["cout"], oh, ow, d)
def ev_conv_t(x, P, oh, ow):
"""The gather form of section 5.3."""
cin, H, W = x.c, x.h, x.w
kh, kw, sh, sw, cout = P["kh"], P["kw"], P["sh"], P["sw"], P["cout"]
d = []
for o in range(cout):
for y in range(oh):
for xx in range(ow):
acc = 0
for i in range(kh):
ty = y + P["ph"] - i
if ty < 0 or ty % sh or ty // sh >= H:
continue
for j in range(kw):
tx = xx + P["pw"] - j
if tx < 0 or tx % sw or tx // sw >= W:
continue
for ci in range(cin):
acc += P["w"][((ci * cout + o) * kh + i) * kw + j] * x.at(ci, ty // sh, tx // sw)
d.append(rq_apply(P["rq"], o, acc))
return T(cout, oh, ow, d)
def per_channel(x, f):
plane = x.h * x.w
return T(x.c, x.h, x.w, [f(k // plane, v) for k, v in enumerate(x.d)])
def ev(g, op, vals, hw, scal):
n, P = op["name"], op["p"]
x = vals[op["ins"][0]]
oh, ow = hw[op["outs"][0]]
oc = [g["tensors"][t]["c"] for t in op["outs"]]
if n == "conv":
return [ev_conv(x, P, oh, ow)]
if n == "conv_transpose":
return [ev_conv_t(x, P, oh, ow)]
if n == "rescale":
return [per_channel(x, lambda c, v: rq_apply(P["rq"], c, v))]
if n == "chscale":
r = scal[P["scalar"]]
return [per_channel(x, lambda c, v: R(v * P["M"][r * x.c + c], P["shift"], P["round"]))]
if n == "mul":
b = vals[op["ins"][1]]
plane = x.h * x.w
d = []
for c in range(x.c):
cb = c if b.c == x.c else 0
for k in range(plane):
d.append(rq_apply(P["rq"], c, x.d[c * plane + k] * b.d[cb * plane + k]))
return [T(x.c, x.h, x.w, d)]
if n == "lut16":
return [T(x.c, x.h, x.w, [P["T"][v + 32768] if -32768 <= v <= 32767 else None for v in x.d])]
if n == "add":
b = vals[op["ins"][1]]
return [T(x.c, x.h, x.w, [p + q for p, q in zip(x.d, b.d)])]
if n == "concat":
d = []
for t in op["ins"]:
d += vals[t].d
return [T(oc[0], x.h, x.w, d)]
if n == "chunk_add":
half = (x.c // 2) * x.h * x.w
return [T(x.c // 2, x.h, x.w, [x.d[k] + x.d[k + half] for k in range(half)])]
if n == "split":
plane = x.h * x.w
return [T(P["count"], x.h, x.w, x.d[P["start"] * plane:(P["start"] + P["count"]) * plane])]
if n == "pixel_shuffle":
r = P["r"]
co = x.c // (r * r)
d = [0] * (co * oh * ow)
for c in range(co):
for i in range(r):
for j in range(r):
for hh in range(x.h):
for ww in range(x.w):
d[(c * oh + hh * r + i) * ow + ww * r + j] = x.at(c * r * r + i * r + j, hh, ww)
return [T(co, oh, ow, d)]
if n == "pixel_unshuffle":
r = P["r"]
d = [0] * (x.c * r * r * oh * ow)
for c in range(x.c):
for i in range(r):
for j in range(r):
for hh in range(oh):
for ww in range(ow):
d[((c * r * r + i * r + j) * oh + hh) * ow + ww] = x.at(c, hh * r + i, ww * r + j)
return [T(x.c * r * r, oh, ow, d)]
if n == "clamp":
return [T(x.c, x.h, x.w, [min(max(v, P["lo"]), P["hi"]) for v in x.d])]
if n == "relu":
return [T(x.c, x.h, x.w, [max(v, 0) for v in x.d])]
if n == "leaky":
return [T(x.c, x.h, x.w, [v if v >= 0 else (v * P["a"]) >> P["f"] for v in x.d])]
if n == "abs":
return [T(x.c, x.h, x.w, [abs(v) for v in x.d])]
if n == "pad_rep":
return [T(x.c, oh, ow, [x.at(c, min(y, x.h - 1), min(xx, x.w - 1)) for c in range(x.c) for y in range(oh) for xx in range(ow)])]
if n in ("crop_to", "crop"):
y0 = x0 = 0
if n == "crop":
val = lambda o: o[1] if o[0] == "const" else scal[o[1]]
y0, x0 = val(P["ops"][0]), val(P["ops"][1])
return [T(x.c, oh, ow, [x.at(c, y0 + y, x0 + xx) for c in range(x.c) for y in range(oh) for xx in range(ow)])]
if n == "subpos_split":
outs = []
for k in range(4):
a, b2 = P["order"][k] >> 1, P["order"][k] & 1
outs.append(T(x.c, oh, ow, [x.at(c, 2 * y + a, 2 * xx + b2) for c in range(x.c) for y in range(oh) for xx in range(ow)]))
return outs
if n == "subpos_merge":
d = [0] * (x.c * oh * ow)
for k in range(4):
a, b2 = P["order"][k] >> 1, P["order"][k] & 1
t = vals[op["ins"][k]]
for c in range(x.c):
for y in range(t.h):
for xx in range(t.w):
d[(c * oh + 2 * y + a) * ow + 2 * xx + b2] = t.at(c, y, xx)
return [T(x.c, oh, ow, d)]
if n == "fill":
v = P["value"] if P["kind"] == 0 else scal[P["value"]]
return [T(oc[0], x.h, x.w, [v] * (oc[0] * x.h * x.w))]
if n == "igdn":
C, plane = x.c, x.h * x.w
out = [0] * (C * plane)
for p in range(plane):
xs = [x.d[j * plane + p] for j in range(C)]
for i in range(C):
N = P["beta"][i] + sum((P["gamma"][i * C + j] * xs[j] * xs[j]) // (1 << P["gf"]) for j in range(C))
r = math.isqrt(max(0, N))
out[i * plane + p] = (xs[i] * r) // (1 << P["frac"])
return [T(C, x.h, x.w, out)]
if n == "layernorm_c":
C, plane = x.c, x.h * x.w
out = [0] * (C * plane)
for p in range(plane):
xs = [x.d[c * plane + p] for c in range(C)]
S = sum(xs)
sg = (S > 0) - (S < 0)
M = S + (C // 2) * sg
m = trunc_div(M, C)
ds = [v - m for v in xs]
V = sum(d * d for d in ds) // C
k = rsqrt_q16(V + P["eps"])
for c in range(C):
nn = (ds[c] * k) >> 16
out[c * plane + p] = ((nn * P["gamma"][c]) >> 16) + P["beta"][c]
return [T(C, x.h, x.w, out)]
if n == "l2norm":
plane = x.h * x.w
out = []
for c in range(x.c):
vs = x.d[c * plane:(c + 1) * plane]
k = rsqrt_q16(sum(v * v for v in vs) + P["eps"])
out += [(v * k) >> 16 for v in vs]
return [T(x.c, x.h, x.w, out)]
if n == "softmax":
out = []
for row in range(x.c * x.h):
vs = x.d[row * x.w:(row + 1) * x.w]
m = max(vs)
es = [exp_neg_q16(v - m) for v in vs]
s = sum(es)
out += [(e * 65536 + s // 2) // s for e in es]
return [T(x.c, x.h, x.w, out)]
if n == "sigmoid":
def sig(v):
if v >= 0:
return (1 << 32) // (65536 + exp_neg_q16(-v))
e = exp_neg_q16(v)
return (e * 65536) // (65536 + e)
return [T(x.c, x.h, x.w, [sig(v) for v in x.d])]
if n == "elu":
return [T(x.c, x.h, x.w, [v if v > 0 else (P["alpha"] * (exp_neg_q16(v) - 65536)) >> 16 for v in x.d])]
if n == "gram":
b = vals[op["ins"][1]]
G = P["groups"]
dd = x.c // G
plane = x.h * x.w
out = [0] * (x.c * dd)
for gi in range(G):
for i in range(dd):
for j in range(dd):
s = sum(x.d[(gi * dd + i) * plane + p] * b.d[(gi * dd + j) * plane + p] for p in range(plane))
out[(gi * dd + i) * dd + j] = R(s, P["rq"]["shift"][0], P["rq"]["round"])
return [T(x.c, 1, dd, out)]
if n == "apply":
V = vals[op["ins"][1]]
G = P["groups"]
dd = x.c // G
plane = V.h * V.w
out = [0] * (V.c * plane)
for gi in range(G):
for i in range(dd):
for p in range(plane):
s = sum(x.d[(gi * dd + i) * dd + j] * V.d[(gi * dd + j) * plane + p] for j in range(dd))
out[(gi * dd + i) * plane + p] = R(s, P["rq"]["shift"][0], P["rq"]["round"])
return [T(V.c, V.h, V.w, out)]
raise Refuse("model_mismatch")
def apply_policy(rec, t):
"""Returns False when a `check` tensor holds an element outside its range."""
if rec["policy"] == 1:
t.d = [min(max(v, rec["lo"]), rec["hi"]) for v in t.d]
elif rec["policy"] == 2:
return all(rec["lo"] <= v <= rec["hi"] for v in t.d)
return True
def run(g, data_inputs, scalars, state):
"""One decode (section 4): the data inputs in input-table order; each state input
from `state` (section 10.3), which the decode updates when it completes."""
allin = []
di = 0
si = 0
if len(data_inputs) != sum(1 for i in g["inputs"] if i["kind"] == 0):
raise Refuse("model_mismatch")
for i in g["inputs"]:
if i["kind"] == 0:
allin.append(data_inputs[di])
di += 1
else:
if state.get("s") is None:
x0 = data_inputs[0]
c = g["tensors"][i["tensor"]]["c"]
allin.append(T(c, x0.h, x0.w, [i["init"]] * (c * x0.h * x0.w)))
else:
allin.append(state["s"][si])
si += 1
hw = {}
# Step 1: each input's channel count, then its size.
for i, t in zip(g["inputs"], allin):
if t.c != g["tensors"][i["tensor"]]["c"] or t.h < 1 or t.w < 1:
raise Refuse("model_mismatch")
if i["max_h"] and (t.h > i["max_h"] or t.w > i["max_w"]):
raise Refuse("too_large")
if t.c * t.h * t.w > g["max_elems"]:
raise Refuse("too_large")
hw[i["tensor"]] = (t.h, t.w)
# Step 2: each scalar against its range.
if len(scalars) != len(g["scalars"]):
raise Refuse("model_mismatch")
for v, (lo, hi) in zip(scalars, g["scalars"]):
if not lo <= v <= hi:
raise Refuse("field_range")
# Step 3: the run-time plan.
walk(g, hw, scalars, False)
# Step 4: evaluation, the inputs' policies first.
vals = {}
for i, t in zip(g["inputs"], allin):
t = T(t.c, t.h, t.w, list(t.d))
if not apply_policy(g["tensors"][i["tensor"]], t):
raise Refuse("field_range")
vals[i["tensor"]] = t
for op in g["ops"]:
outs = ev(g, op, vals, hw, scalars)
for t, ten in zip(op["outs"], outs):
# A value outside i64 is one load step 8 excludes; before the certifier
# exists, such a decode is refused.
if any(v is None or not I64_MIN <= v <= I64_MAX for v in ten.d):
raise Refuse("model_mismatch")
if not apply_policy(g["tensors"][t], ten):
raise Refuse("field_range")
vals[t] = ten
outs = [vals[t] for t in g["outputs"]]
state["s"] = [outs[i["src"]] for i in g["inputs"] if i["kind"] == 1]
return outs
# --------------------------------------------------------------- canonical JSON (8.5, 14.1)
def cw(v):
"""The canonical form: ints as decimal strings, members sorted by key bytes."""
if v is None:
return "null"
if v is True:
return "true"
if v is False:
return "false"
if isinstance(v, int):
return '"%d"' % v
if isinstance(v, str):
return '"' + v.replace("\\", "\\\\").replace('"', '\\"') + '"'
if isinstance(v, list):
return "[" + ",".join(cw(x) for x in v) + "]"
if isinstance(v, dict):
keys = sorted(v, key=lambda k: k.encode())
return "{" + ",".join(cw(k) + ":" + cw(v[k]) for k in keys) + "}"
raise TypeError(v)
def dec(s):
"""A canonical decimal integer string."""
if not isinstance(s, str) or not s or s == "-0":
raise ValueError("not a canonical integer: %r" % (s,))
d = s[1:] if s[0] == "-" else s
if not d.isdigit() or (len(d) > 1 and d[0] == "0") or not d.isascii():
raise ValueError("not a canonical integer: %r" % (s,))
return int(s)
def tensor_in(v):
c, h, w = (dec(x) for x in v["shape"])
d = [dec(x) for x in v["data"]]
if len(d) != c * h * w:
raise ValueError("a tensor's data does not fill its shape")
return T(c, h, w, d)
def tensor_out(t):
return {"data": list(t.d), "shape": [t.c, t.h, t.w]}
def graph_line(v):
"""A graph vector, recomputed: `load` (steps 1-8), `profiles` (section 14.1: both
when the load is refused, else exactly the profiles the verdict meets) and every
decode's `expect`."""
b = bytes.fromhex(v["graph"])
out = {"graph": b.hex(), "id": v["id"], "decodes": []}
try:
g = load(b)
out["load"] = "ok"
out["profiles"] = [p for p in ("acc32", "acc64") if g["verdict"][p]]
except Refuse as e:
out["load"] = e.code
out["profiles"] = ["acc32", "acc64"]
g = None
if g is not None:
state = {}
for d in v["decodes"]:
ins = [tensor_in(t) for t in d["inputs"]]
sc = [dec(s) for s in d["scalars"]]
try:
res = run(g, ins, sc, state)
exp = {"outputs": [tensor_out(t) for t in res]}
except Refuse as e:
exp = {"refuse": e.code}
out["decodes"].append({"expect": exp, "inputs": [tensor_out(t) for t in ins], "scalars": sc})
return out
# --------------------------------------------------------------- stage operators (5E)
class Rans:
"""integer-payloads section 2's decoder state."""
def __init__(self, b):
self.b = b
self.x = (int.from_bytes(b[4:8], "little") << 32) | int.from_bytes(b[0:4], "little")
self.i = 2
self.out = False
def renorm(self):
if self.x < (1 << 31):
if 4 * self.i + 4 <= len(self.b):
self.x = (self.x << 32) | int.from_bytes(self.b[4 * self.i:4 * self.i + 4], "little")
self.i += 1
else:
self.out = True
def bits(self, n):
v = self.x & ((1 << n) - 1)
self.x >>= n
self.renorm()
return v
def sat32(v):
return max(I32_MIN, min(I32_MAX, v))
def rans64_decode(i, m):
table_rule, escape, end = m["table"], m["escape"], m["end"]
lo = hi = None
if escape == "strict":
lo, hi = dec(m["lo"]), dec(m["hi"])
if not (I32_MIN <= lo <= hi <= I32_MAX):
raise Refuse("model_mismatch", -1)
tables = [([dec(c) for c in t["cdf"]], dec(t["offset"])) for t in i["tables"]]
reads = [dec(r) for r in i["reads"]]
for cdf, o in tables:
n = len(cdf)
ok_n = 3 <= n <= 4097 if table_rule == "t4097" else 2 <= n <= 65537
if not ok_n or cdf[0] != 0 or cdf[-1] != 65536 or any(cdf[k] >= cdf[k + 1] for k in range(n - 1)) or not I32_MIN <= o <= I32_MAX:
raise Refuse("model_mismatch", -1)
if any(not 0 <= r < len(tables) for r in reads):
raise Refuse("model_mismatch", -1)
stream = bytes.fromhex(i["stream"])
if len(stream) < 8 or len(stream) % 4:
raise Refuse("stream_exhausted", 0)
d = Rans(stream)
syms = []
for k, r in enumerate(reads):
cdf, o = tables[r]
n = len(cdf)
cum = d.x & 0xFFFF
s = max(j for j in range(n - 1) if cdf[j] <= cum)
d.x = (cdf[s + 1] - cdf[s]) * (d.x >> 16) + cum - cdf[s]
d.renorm()
code = None
if s < n - 2:
sym = s + o
if escape == "lenient":
sym = sat32(sym)
elif not lo <= sym <= hi:
code = "field_range"
elif escape == "lenient":
v = d.bits(4)
count = v
groups = 0
while v == 15 and groups < 8 and not d.out:
v = d.bits(4)
count += v
groups += 1
count = min(count, 8)
raw = 0
for j in range(count):
raw |= d.bits(4) << (4 * j)
value = -(raw >> 1) - 1 if raw & 1 else (raw >> 1) + (n - 2)
sym = sat32(sat32(value) + o)
else:
count = d.bits(4)
raw, last = 0, None
if count <= 8:
for j in range(count):
last = d.bits(4)
raw += last << (4 * j)
value = -(raw // 2) - 1 if raw % 2 else raw // 2 + (n - 2)
sym = value + o
if count > 8 or (count >= 1 and last == 0):
code = "stream_noncanonical"
elif not lo <= sym <= hi:
code = "field_range"
if end == "canonical" and d.out:
raise Refuse("stream_exhausted", k)
if code:
raise Refuse(code, k)
syms.append(sym)
if end == "canonical":
if d.out or d.x != (1 << 31) or 4 * d.i != len(stream):
raise Refuse("stream_noncanonical", len(reads))
elif d.out:
raise Refuse("stream_exhausted", len(reads))
return {"symbols": syms}
class Win:
"""E8's window and states over memory M (8 zero bytes, then the stream)."""
def __init__(self, stream):
self.M = bytes(8) + stream
self.p = len(stream)
self.h = self.le64(self.p)
self.pos = 0
self.last_in = 0
if self.h == 0:
raise Refuse("stream_exhausted", 0)
self.b = self.h.bit_length() - 1
self.h -= 1 << self.b
self.s = [0, 0]
try:
self.s[0] = self.read(8)
self.s[1] = self.read(8)
self.flush()
except Refuse:
raise Refuse("stream_exhausted", 0)
def le64(self, i):
return int.from_bytes(self.M[i:i + 8], "little")
def read(self, k):
if k > self.b:
raise Refuse("stream_exhausted", self.pos)
self.b -= k
r = self.h >> self.b
self.h -= r << self.b
return r
def flush(self):
d = 7 ^ (self.b // 8)
if d > self.p:
raise Refuse("stream_exhausted", self.pos)
self.p -= d
self.h = self.le64(self.p) % (1 << (56 + self.b % 8))
self.b = 56 + self.b % 8
def step(self, which, t):
self.s[which] = ((t >> 16) | self.read(t >> 24)) % 256
def at(self, k):
"""The `in` or `out` step of position k runs: a refusal in it is at k."""
self.pos = k
def at_in(self, k):
self.pos = k
self.last_in = k
def group_flush(self):
"""A flush that belongs to no single position (section 14.1, `at`): it reports
the last position whose `in` step ran."""
self.pos = self.last_in
self.flush()
def wrap16(a):
return ((a + 32768) % 65536) - 32768
def latent_tables(pmf):
w = len(pmf[0]) if pmf else 0
if w < 1 or any(len(r) != w or min(r) < 0 or sum(r) != 256 for r in pmf):
raise Refuse("model_mismatch", -1)
tabs = []
for P in pmf:
C = [0]
for p in P:
C.append(C[-1] + p)
F = lambda k: C[k] - C[k] // 2
G = lambda k: C[k] // 2 + 128
sym = lambda k: k // 2 if k % 2 == 0 else -((k + 1) // 2)
slot_sym, slot_next = [None] * 256, [None] * 256
k = 0
while C[k] < 256:
for u in range(F(k), F(k + 1)):
slot_sym[u], slot_next[u] = sym(k), P[k] + (u - F(k))
for u in range(G(k), G(k + 1)):
slot_sym[u], slot_next[u] = sym(k), P[k] + (F(k + 1) - F(k)) + (u - G(k))
k += 1
t = []
for u in range(256):
nb = 0
while slot_next[u] * (1 << nb) < 256:
nb += 1
base = (slot_next[u] * (1 << nb)) ^ 256
t.append(nb * (1 << 24) + base * (1 << 16) + slot_sym[u] % 65536)
tabs.append(t)
return tabs
def mans_sgm(i):
pmf = [[dec(x) for x in r] for r in i["pmf"]]
bounds = [dec(x) for x in i["bounds"]]
spans = [([bool(b) for b in s["mask"]], [dec(x) for x in s["sigma"]]) for s in i["spans"]]
tabs = latent_tables(pmf)
if any(not 0 <= b <= 255 for b in bounds):
raise Refuse("model_mismatch", -1)
nd = min(len(tabs), len(bounds))
if any(len(m) != len(sg) or any(not 0 <= x < nd for x in sg) for m, sg in spans):
raise Refuse("model_mismatch", -1)
w = Win(bytes.fromhex(i["memory"]))
out = []
base = 0
for mask, sigma in spans:
L = len(mask)
sy = [0] * L
def inb(which, k):
w.at_in(base + k)
if mask[k]:
t = tabs[sigma[k]][w.s[which]]
w.step(which, t)
sy[k] = wrap16(t % 65536)
def outb(k):
w.at(base + k)
if mask[k]:
beta = bounds[sigma[k]]
if sy[k] + beta == 0:
f = w.read(1)
v = w.read(3 if f == 1 else 16)
sy[k] = wrap16(v // 2 + beta) if v % 2 == 0 else wrap16(-(v // 2) - beta)
w.flush()
k = 0
while k + 3 < L:
inb(0, k); inb(1, k + 1); inb(0, k + 2); inb(1, k + 3)
w.group_flush()
for q in range(4):
outb(k + q)
k += 4
if L % 4 >= 2:
inb(0, k); outb(k); inb(1, k + 1); outb(k + 1)
k += 2
w.group_flush()
if L % 2 == 1:
inb(0, k); outb(k)
w.group_flush()
out.append(sy)
base += L
return {"symbols": out}
def mans_fac(i):
rows = [[dec(x) for x in r] for r in i["rows"]]
lens = [dec(x) for x in i["lengths"]]
if len(rows) != len(lens) or any(len(r) != 63 or r[0] < 0 or r[62] > 255 or any(r[k] > r[k + 1] for k in range(62)) for r in rows):
raise Refuse("model_mismatch", -1)
Zp = [0] * 512
nb = 0
for i_ in range(511, 0, -1):
if i_ * (1 << nb) < 256:
nb += 1
Zp[i_] = ((i_ * (1 << nb)) ^ 256) + 256 * nb
w = Win(bytes.fromhex(i["memory"]))
Tz = [0] * 256
out = []
base = 0
for r, L in zip(rows, lens):
prev = 0
for a in range(63):
k = r[a] - prev
for j in range(prev, r[a]):
Tz[j] = Zp[k + j - prev] * 65536 + a
prev = r[a]
Tz[255] = Zp[1] * 65536 + 63
sy = [0] * L
def inb(which, k):
w.at_in(base + k)
t = Tz[w.s[which]]
w.step(which, t)
sy[k] = t % 256
def outb(k):
w.at(base + k)
if sy[k] == 63:
sy[k] = w.read(6)
k = 0
while k + 3 < L:
inb(0, k); inb(1, k + 1); inb(0, k + 2); inb(1, k + 3)
for q in range(4):
outb(k + q)
w.group_flush()
k += 4
if L % 4 >= 2:
inb(0, k); outb(k); inb(1, k + 1); outb(k + 1)
k += 2
if L % 2 == 1:
inb(0, k); outb(k)
w.group_flush()
out.append(sy)
base += L
return {"symbols": out}
def rdiv(n, d):
if d < 1:
return 0
sg = (n > 0) - (n < 0)
return sg * ((2 * abs(n) + d) // (2 * d))
def stage_expect(op, i, m):
D = lambda k: dec(i[k])
if op == "rans64_decode":
return rans64_decode(i, m)
if op == "dequantize":
f, s, b = D("f"), D("sym"), D("base")
if not 0 <= f <= 62:
raise Refuse("field_range", 0)
v = s * (1 << f) + b
if not I64_MIN <= v <= I64_MAX:
raise Refuse("field_range", 0)
return {"value": v}
if op == "bucket":
sigma, e = D("sigma"), [dec(x) for x in i["edges"]]
if m["bucket"] == "ceil":
if not e:
raise Refuse("model_mismatch", -1)
s2 = max(sigma, e[0])
return {"index": (len(e) - 1) - sum(1 for k in range(len(e) - 1) if s2 <= e[k])}
if not e or any(e[k] > e[k + 1] for k in range(len(e) - 1)):
raise Refuse("model_mismatch", -1)
return {"index": sum(1 for x in e if x <= sigma)}
if op == "coded":
return {"coded": D("v") > D("thr")}
if op == "scale_index":
n, s = D("n"), D("s")
if n < 1 or not 0 <= s <= 127:
raise Refuse("model_mismatch", -1)
return {"index": min(max(R(D("v"), s, 1), 0), n - 1)}
if op == "block_stat":
x = tensor_in(i["x"])
b, s, lo, hi = D("b"), D("s"), D("lo"), D("hi")
if b < 1 or not 0 <= s <= 127 or lo > hi:
raise Refuse("model_mismatch", -1)
d = list(x.d)
for c in range(x.c):
for by in range(0, x.h, b):
for bx in range(0, x.w, b):
cells = [(y, xx) for y in range(by, min(by + b, x.h)) for xx in range(bx, min(bx + b, x.w))]
v = min(max(R(sum(x.at(c, y, xx) for y, xx in cells), s, 1), lo), hi)
for y, xx in cells:
d[(c * x.h + y) * x.w + xx] = v
return {"out": tensor_out(T(x.c, x.h, x.w, d))}
if op == "band_lookup":
e = [dec(x) for x in i["edges"]]
table = [[dec(x) for x in r] for r in i["table"]]
row = D("row")
if not 0 <= row < len(table) or len(table[row]) != len(e) + 1:
raise Refuse("model_mismatch", -1)
return {"value": table[row][sum(1 for k in e if D("v") >= k)]}
if op == "mans_decode":
return mans_sgm(i) if i["mode"] == "sgm" else mans_fac(i)
if op == "residual_step":
pred, r, q, lo, hi = D("pred"), D("r"), D("q"), D("lo"), D("hi")
if lo >= hi or not lo <= pred <= hi or not 1 <= q <= 65535 or not I32_MIN <= r <= I32_MAX:
raise Refuse("model_mismatch", -1)
return {"value": min(max(pred + r * q, lo), hi)}
if op == "mc_predict":
f = tensor_in(i["frame"])
y, x, c, dy, dx, pr = D("y"), D("x"), D("c"), D("dy"), D("dx"), D("precision")
if not (0 <= c < f.c and 0 <= y < f.h and 0 <= x < f.w) or pr not in (1, 2):
raise Refuse("model_mismatch", -1)
p = lambda Y, X: f.at(c, min(max(Y, 0), f.h - 1), min(max(X, 0), f.w - 1))
if pr == 1:
return {"value": p(y + dy, x + dx)}
iy, fy, ix, fx = y + (dy >> 1), dy & 1, x + (dx >> 1), dx & 1
if not fy and not fx:
v = p(iy, ix)
elif not fy:
v = (p(iy, ix) + p(iy, ix + 1) + 1) >> 1
elif not fx:
v = (p(iy, ix) + p(iy + 1, ix) + 1) >> 1
else:
v = (p(iy, ix) + p(iy, ix + 1) + p(iy + 1, ix) + p(iy + 1, ix + 1) + 2) >> 2
return {"value": v}
if op == "up2_predict":
prev = tensor_in(i["prev"])
d0, d1 = D("d0"), D("d1")
hp, wp = -(-d0 // 2), -(-d1 // 2)
if d0 < 1 or d1 < 1 or (prev.h, prev.w) != (hp, wp):
raise Refuse("geometry_mismatch", -1)
p = lambda c, Y, X: prev.at(c, min(Y, hp - 1), min(X, wp - 1))
d = []
for c in range(prev.c):
for y in range(d0):
for x in range(d1):
iy, fy, ix, fx = y >> 1, y & 1, x >> 1, x & 1
if not fy and not fx:
v = p(c, iy, ix)
elif not fy:
v = (p(c, iy, ix) + p(c, iy, ix + 1) + 1) >> 1
elif not fx:
v = (p(c, iy, ix) + p(c, iy + 1, ix) + 1) >> 1
else:
v = (p(c, iy, ix) + p(c, iy, ix + 1) + p(c, iy + 1, ix) + p(c, iy + 1, ix + 1) + 2) >> 2
d.append(v)
return {"out": tensor_out(T(prev.c, d0, d1, d))}
if op == "rate_predict":
prev = tensor_in(i["prev"])
num, den, d0 = D("rate_num"), D("rate_den"), D("d0")
n = prev.w
if prev.h != 1 or num < 1 or den < 1 or n * num // den != d0:
raise Refuse("geometry_mismatch", -1)
d = []
for c in range(prev.c):
for k in range(d0):
s = k * den
lo, rho = s // num, s % num
hi = min(lo + 1, n - 1)
a, b = prev.at(c, 0, lo), prev.at(c, 0, hi)
d.append(rdiv(a * num + (b - a) * rho, num))
return {"out": tensor_out(T(prev.c, 1, d0, d))}
if op == "coding_order":
c, h, w = (dec(x) for x in i["shape"])
cb = None if i["order"] == "raster" else i["anchor"]
idx = [(cc * h + y) * w + x for cc in range(c) for y in range(h) for x in range(w) if cb is None or (((y + x) % 2 == 0) == cb)]
return {"indices": idx}
raise ValueError("unknown stage operator %r" % op)
def stage_line(v):
try:
exp = stage_expect(v["op"], v["in"], v["modes"])
except Refuse as e:
exp = {"at": e.at, "refuse": e.code}
return {"expect": exp, "id": v["id"], "in": v["in"], "modes": v["modes"], "op": v["op"]}
# --------------------------------------------------------------- lowerings (section 13)
class WB:
"""A WHG1 graph under construction with section 13.0's numbering."""
def __init__(self):
self.scalars, self.inputs, self.tensors, self.ops = [], [], [], []
def tensor(self, c, policy=0, lo=0, hi=0):
self.tensors.append((c, policy, lo, hi))
return len(self.tensors) - 1
def input(self, c, lo, hi, mh=0, mw=0):
t = self.tensor(c, 2, lo, hi)
self.inputs.append((t, mh, mw))
return t
def op(self, code, ins, out_rec, params):
t = self.tensor(*out_rec)
self.ops.append((code, ins, [t], params))
return t
def bytes(self, outputs):
b = bytearray(b"WHG1")
b += bytes([1, 1, 0, 0])
b += struct.pack("<IIHHHHII", len(self.tensors), len(self.ops), len(self.inputs), len(outputs), len(self.scalars), 0, CAP, 0)
for lo, hi in self.scalars:
b += struct.pack("<qq", lo, hi)
for t, mh, mw in self.inputs:
b += struct.pack("<IIIB3xI4xq", t, mh, mw, 0, 0, 0)
for t in outputs:
b += struct.pack("<I", t)
for c, pol, lo, hi in self.tensors:
b += struct.pack("<Ibb2xqq", c, -128, pol, lo, hi)
for code, ins, outs, params in self.ops:
b += struct.pack("<BBBx", code, len(ins), len(outs))
for t in ins + outs:
b += struct.pack("<I", t)
b += struct.pack("<I", len(params)) + params
return bytes(b)
def ints_le(v, width):
return b"".join(x.to_bytes(width, "little", signed=True) for x in v)
def rq_block(round_, bias, mult, mkind, mbytes, shifts, skind, biasv, bbytes):
"""The RQ block of section 10.4."""
b = struct.pack("<BBBBBBH", round_, bias, mkind, skind, mbytes, bbytes, 0)
b += ints_le(mult, mbytes) if mkind else b""
b += bytes(shifts)
b += ints_le(biasv, bbytes) if bias else b""
return b
def smallest(v, allowed):
for w in allowed:
lo, hi = -(1 << (8 * w - 1)), (1 << (8 * w - 1)) - 1
if all(lo <= x <= hi for x in v):
return w
return 8
class LowerRefuse(Exception):
pass
def lref():
raise LowerRefuse()
def p_conv(cout, kh, kw, sh, sw, ph, pw, groups, w_bytes, rq, w):
return struct.pack("<8IB3x", cout, kh, kw, sh, sw, ph, pw, groups, w_bytes) + rq + ints_le(w, w_bytes)
def p_convt(cout, kh, kw, sh, sw, ph, pw, oph, opw, w_bytes, rq, w):
return struct.pack("<9IB3x", cout, kh, kw, sh, sw, ph, pw, oph, opw, w_bytes) + rq + ints_le(w, w_bytes)
def p_clamp(lo, hi):
return struct.pack("<qq", lo, hi)
def int_synth_lower(js):
"""int_synth/1 (section 13.1)."""
try:
root = json.loads(js)
except ValueError:
lref()
layers = root.get("layers") if isinstance(root, dict) else None
if not isinstance(layers, list) or not layers:
lref()
def u(l, k, default=None):
v = l.get(k, default)
if v is None or isinstance(v, bool) or not isinstance(v, int) or not 0 <= v <= 0xFFFFFFFF:
lref()
return v
def i32s(l, k):
a = l.get(k)
if not isinstance(a, list) or any(isinstance(x, bool) or not isinstance(x, int) or not I32_MIN <= x <= I32_MAX for x in a):
lref()
return a
if not isinstance(layers[0], dict):
lref()
c = u(layers[0], "in_ch")
if c == 0:
lref()
wb = WB()
x = wb.input(c, -128, 127)
for l in layers:
if not isinstance(l, dict):
lref()
ic, oc, k, s, p = u(l, "in_ch"), u(l, "out_ch"), u(l, "k"), u(l, "stride"), u(l, "pad")
op_, sh = u(l, "out_pad", 0), u(l, "shift", 0)
relu = l.get("relu", False)
if not isinstance(relu, bool):
lref()
w, b = i32s(l, "weight"), i32s(l, "bias")
if ic != c or oc == 0 or k == 0 or s == 0 or sh > 127 or len(w) != ic * oc * k * k or len(b) != oc:
lref()
rq = rq_block(0, 1, [], 0, 0, [sh], 1, b, 4)
x = wb.op(0x11, [x], (oc,), p_convt(oc, k, k, s, s, p, p, op_, op_, 4, rq, w))
if relu:
x = wb.op(0x0C, [x], (oc,), b"")
c = oc
x = wb.op(0x0B, [x], (c,), p_clamp(0, 255))
return [("synth", wb.bytes([x]))]
def parse_net(b, magic, audio):
"""WHM1 / WAM1 (integer-payloads section 9)."""
if b[:4] != magic:
lref()
rd = Reader(b, 4)
try:
afrac, gf, shift, n = rd.u(4), rd.u(4), rd.u(4), rd.u(4)
layers = []
for _ in range(n):
tag = rd.u(1)
if tag == 0:
inc, out, k, s, p, op_ = (rd.u(4) for _ in range(6))
w = rd.ints(inc * out * k * (1 if audio else k), 2)
layers.append(("convT", inc, out, k, s, p, op_, w, rd.ints(out, 8), rd.ints(out, 8)))
elif tag == 1 and not audio:
inc, out, k, s, p = (rd.u(4) for _ in range(5))
w = rd.ints(out * inc * k * k, 2)
layers.append(("conv", inc, out, k, s, p, 0, w, rd.ints(out, 8), rd.ints(out, 8)))
elif tag == 2:
c = rd.u(4)
layers.append(("igdn", c, rd.ints(c * c, 4), rd.ints(c, 8)))
elif tag == 3 and not audio:
layers.append(("relu",))
elif tag == 4 and not audio:
layers.append(("leaky", rd.i(8), rd.u(4)))
else:
lref()
except Refuse:
lref()
if rd.p != len(b):
lref()
return {"afrac": afrac, "gf": gf, "shift": shift, "layers": layers, "audio": audio}
def largest_x(pred):
"""The largest X in [0, 2^63] with pred(X), pred monotone; None if pred(0) fails."""
if not pred(0):
return None
lo, hi = 0, 1 << 63
if pred(hi):
return hi
while hi - lo > 1:
mid = (lo + hi) // 2
if pred(mid):
lo = mid
else:
hi = mid
return lo
def x_l(net, layer, c):
"""X_l of section 13.2 for a layer of tag 0, 1, 2 or 4 reading c channels; the set
is refused when P_l(0) fails."""
I64, I128 = (1 << 63) - 1, (1 << 127) - 1
kind = layer[0]
if kind in ("conv", "convT"):
k = layer[3]
terms = c * k * (1 if net["audio"] else k)
Wm, Mm, Bm = max(map(abs, layer[7]), default=0), max(map(abs, layer[8]), default=0), max(map(abs, layer[9]), default=0)
p = lambda X: terms * Wm * X <= I64 and (terms * Wm * X * Mm) // (1 << net["shift"]) + 1 + Bm <= I64
elif kind == "igdn":
G, Bb = max(map(abs, layer[2]), default=0), max(map(abs, layer[3]), default=0)
def p(X):
if G * X * X > I128:
return False
N = Bb + c * ((G * X * X) // (1 << net["gf"]) + 1)
if N > I128:
return False
r = math.isqrt(N)
return r <= I64 and (X * r) // (1 << net["afrac"]) + 1 <= I64
else:
a = abs(layer[1])
p = lambda X: X * a <= I64
x = largest_x(p)
if x is None:
lref()
return x
def x_record(net, k, c):
"""The tensor record of x_k on c channels (section 13.2, Range checks). x_k is read
by layer k, if there is one. When that layer is of tag 0, 1, 2 or 4, x_k is
check [-X_k, min(X_k, I64)]; that includes the network's input. A network input
that no such layer reads is check [-2^63, I64]. Every other tensor is derived."""
L = net["layers"]
if k < len(L) and L[k][0] in ("convT", "conv", "igdn", "leaky"):
x = x_l(net, L[k], c)
return (c, 2, -x, min(x, I64_MAX))
if k == 0:
return (c, 2, I64_MIN, I64_MAX)
return (c,)
def net_layers(wb, net, x):
L = net["layers"]
for k, l in enumerate(L):
c = wb.tensors[x][0]
oc = l[2] if l[0] in ("conv", "convT") else c
rec = x_record(net, k + 1, oc)
if l[0] in ("conv", "convT"):
_, inc, out, kk, s, p, op_, w, mult, bias = l
if inc != c or out == 0 or kk == 0 or s == 0:
lref()
rq = rq_block(0, 2, mult, 2, 8, [net["shift"]], 1, bias, 8)
if l[0] == "conv":
x = wb.op(0x01, [x], rec, p_conv(out, kk, kk, s, s, p, p, 1, 2, rq, w))
elif net["audio"]:
x = wb.op(0x11, [x], rec, p_convt(out, 1, kk, 1, s, 0, p, 0, op_, 2, rq, w))
else:
x = wb.op(0x11, [x], rec, p_convt(out, kk, kk, s, s, p, p, op_, op_, 2, rq, w))
elif l[0] == "igdn":
if l[1] != c:
lref()
x = wb.op(0x12, [x], rec, struct.pack("<IIB7x", net["gf"], net["afrac"], 4) + ints_le(l[2], 4) + ints_le(l[3], 8))
elif l[0] == "relu":
x = wb.op(0x0C, [x], rec, b"")
else:
if l[2] > 63:
lref()
x = wb.op(0x0D, [x], rec, struct.pack("<qI", l[1], l[2]))
return x
def net_check(net):
if net["shift"] > 127 or net["gf"] > 127 or not 1 <= net["afrac"] <= 30 or not net["layers"]:
lref()
for l in net["layers"]:
if l[0] in ("conv", "convT"):
return l[1]
if l[0] == "igdn":
return l[1]
lref()
def rgb8(wb, x, afrac):
c = wb.tensors[x][0]
t = wb.op(0x0B, [x], (c,), p_clamp(0, 1 << afrac))
return wb.op(0x02, [t], (c,), rq_block(1, 0, [255], 1, 4, [afrac], 1, [], 0))
def whm1_lower(b, output_step):
net = parse_net(b, b"WHM1", False)
c0 = net_check(net)
wb = WB()
r = x_record(net, 0, c0)
x = wb.input(r[0], r[2], r[3])
x = net_layers(wb, net, x)
if output_step:
x = rgb8(wb, x, net["afrac"])
return wb.bytes([x])
def wam1_lower(b):
net = parse_net(b, b"WAM1", True)
c0 = net_check(net)
wb = WB()
r = x_record(net, 0, c0)
x = wb.input(r[0], r[2], r[3])
wb.scalars.append((1, 1 << 26))
x = net_layers(wb, net, x)
c = wb.tensors[x][0]
lo = -((1 << 63) // 32767)
hi = (I64_MAX - (1 << (net["afrac"] - 1))) // 32767
crop = struct.pack("<B3xIB3xIB3xIB3xI", 0, 0, 0, 0, 0, 1, 1, 0)
t = wb.op(0x14, [x], (c, 2, lo, hi), crop)
t = wb.op(0x02, [t], (c,), rq_block(1, 0, [32767], 1, 4, [net["afrac"]], 1, [], 0))
t = wb.op(0x0B, [t], (c,), p_clamp(-32768, 32767))
return wb.bytes([t])
def wmb1_lower(b):
if b[:4] != b"WMB1":
lref()
rd = Reader(b, 4)
try:
secs = [rd.take(rd.u(4)) for _ in range(4)]
except Refuse:
lref()
if rd.p != len(b):
lref()
nets = [parse_net(s, b"WHM1", False) for s in secs[:3]]
if len({n["afrac"] for n in nets}) != 1:
lref()
try:
tables = json.loads(secs[3])
except ValueError:
lref()
a = tables.get("afrac") if isinstance(tables, dict) else None
if isinstance(a, bool) or not isinstance(a, int) or a != nets[2]["afrac"]:
lref()
return [("h_s", net_graph(nets[0], False)), ("entropy", net_graph(nets[1], False)), ("g_s", net_graph(nets[2], True))]
def net_graph(net, step):
c0 = net_check(net)
wb = WB()
r = x_record(net, 0, c0)
x = wb.input(r[0], r[2], r[3])
x = net_layers(wb, net, x)
if step:
x = rgb8(wb, x, net["afrac"])
return wb.bytes([x])
def world_lower(tb, gb, c):
tn, gn = parse_net(tb, b"WHM1", False), parse_net(gb, b"WHM1", False)
cin = net_check(tn)
net_check(gn)
if tn["afrac"] != gn["afrac"] or c < 1 or cin <= c:
lref()
A = cin - c
wb = WB()
z = wb.input(c, I64_MIN, I64_MAX)
parts = [z]
for _ in range(A):
wb.scalars.append((I64_MIN, I64_MAX))
s = len(wb.scalars) - 1
parts.append(wb.op(0x17, [z], (1,), struct.pack("<B7xq", 1, s)))
x0 = wb.op(0x06, parts, x_record(tn, 0, cin), b"")
x = net_layers(wb, tn, x0)
one = 1 << tn["afrac"]
out = wb.op(0x0B, [x], (wb.tensors[x][0],), p_clamp(-one, one))
return [("T", wb.bytes([out])), ("g_s", net_graph(gn, True))]
def jpegai_lower(norm_b, hy_b, huv_b):
"""jpegai-int/1, section 13.6.1."""
try:
norm, hy, huv = json.loads(norm_b), json.loads(hy_b), json.loads(huv_b)
except ValueError:
lref()
isint = lambda v: isinstance(v, int) and not isinstance(v, bool)
ints = lambda v: v if isinstance(v, list) and all(isint(x) for x in v) else lref()
if not isinstance(norm, dict):
lref()
pmf = norm.get("pmf")
if not isinstance(pmf, list) or len(pmf) < 32:
lref()
widths = set()
for row in pmf:
r = ints(row)
if not r or min(r) < 0 or sum(r) != 256:
lref()
widths.add(len(r))
if len(widths) != 1:
lref()
bounds = ints(norm.get("bounds"))
if len(bounds) < 32 or any(not 0 <= v <= 255 for v in bounds):
lref()
if not isint(norm.get("per_channel")) or norm["per_channel"] != 16:
lref()
for k, rows in (("cdfs_y", 160), ("cdfs_uv", 96)):
v = ints(norm.get(k))
if len(v) != rows * 63 or any(not 0 <= x <= 255 for x in v) or any(v[r * 63 + j] > v[r * 63 + j + 1] for r in range(rows) for j in range(62)):
lref()
gy, guv = ints(norm.get("gain_y")), ints(norm.get("gain_uv"))
if len(gy) != 160 or len(guv) != 96 or any(not I64_MIN <= v <= I64_MAX for v in gy + guv):
lref()
def member(o, k, dims):
m = o.get(k)
if not isinstance(m, dict) or m.get("dims") != dims:
lref()
v = ints(m.get("v"))
n = 1
for d in dims:
n *= d
if len(v) != n or any(not I64_MIN <= x <= I64_MAX for x in v):
lref()
return v
def plane(o, cz, gains):
if not isinstance(o, dict):
lref()
def d0(k):
m = o.get(k)
dims = m.get("dims") if isinstance(m, dict) else None
if not isinstance(dims, list) or not dims or not isint(dims[0]) or not 1 <= dims[0] <= 0xFFFFFFFF:
lref()
return dims[0]
c1, c2 = d0("conv1.weight"), d0("depthwise.weight")
c3 = 16 * cz
wb = WB()
x = wb.input(cz, -31, 32, 4, 4)
blocks = (("conv1", c1, cz, 1, 0, "onnx::Pow_85"), ("depthwise", c2, c1, 3, 1, "onnx::Pow_91"), ("pointwise", c3, c2, 1, 0, "onnx::Pow_97"))
for bi, (name, cout, cin, k, pad, sh) in enumerate(blocks):
w = member(o, name + ".weight", [cout, cin, k, k])
bias = member(o, name + ".bias", [cout])
shifts = member(o, sh, [1, cout, 1, 1])
if any(not 0 <= s <= 127 for s in shifts):
lref()
x = wb.op(0x0B, [x], (wb.tensors[x][0],), p_clamp(-128, 127))
rq = rq_block(0, 1, [], 0, 0, shifts, 2, bias, smallest(bias, (4, 8)))
wbytes = smallest(w, (1, 2, 4, 8))
x = wb.op(0x01, [x], (cout,), p_conv(cout, k, k, 1, 1, pad, pad, 1, wbytes, rq, w))
if bi < 2:
x = wb.op(0x0C, [x], (cout,), b"")
x = wb.op(0x09, [x], (cz,), struct.pack("<I", 4))
x = wb.op(0x13, [x], (cz,), b"")
x = wb.op(0x0B, [x], (cz,), p_clamp(0, 3967))
x = wb.op(0x02, [x], (cz,), rq_block(0, 1, [], 0, 0, [0], 1, gains, smallest(gains, (4, 8))))
return wb.bytes([x])
return [("hs_y", plane(hy, 160, gy)), ("hs_uv", plane(huv, 96, guv))]
def lower_set(entry, files):
low, cap = entry["lowering"], entry["capability"]
try:
if low == "int_synth/1":
roles = int_synth_lower(files["int_synth.json"])
elif low == "whm1/1" and cap == "wai.splat.int_codec":
roles = [("g_s", whm1_lower(files["g_s.whm"], True))]
elif low == "whm1/1":
roles = [("h_s", whm1_lower(files["h_s.whm"], False)), ("g_s", whm1_lower(files["g_s.whm"], True))]
elif low == "wam1/1":
roles = [("g_s", wam1_lower(files["model.wam"]))]
elif low == "wmb1/1":
roles = wmb1_lower(files["model.wmb"])
elif low == "world/1":
roles = world_lower(files["T.whm"], files["g_s.whm"], dec(entry["c"]))
elif low == "jpegai-int/1":
roles = jpegai_lower(files["norm.json"], files["weights/hs_y.json"], files["weights/hs_uv.json"])
else:
raise ValueError("no lowering %r" % low)
except LowerRefuse:
raise ValueError("the lowering refused the set")
# Every lowered graph loads.
for _, b in roles:
load(b)
return roles
def set_digest(files):
if len(files) == 1:
return hashlib.sha256(next(iter(files.values()))).hexdigest()
listing = "".join("%s %s\n" % (hashlib.sha256(files[n]).hexdigest(), n) for n in sorted(files, key=lambda s: s.encode()))
return hashlib.sha256(listing.encode()).hexdigest()
# --------------------------------------------------------------- main
def check(root, write):
wai = os.path.dirname(os.path.abspath(root))
failures = 0
nfiles = nvec = 0
for sub, fn in (("vectors", graph_line), ("stage", stage_line), ("graphs", graph_line)):
d = os.path.join(root, sub)
for name in sorted(os.listdir(d)):
if not name.endswith(".jsonl"):
continue
raw = open(os.path.join(d, name), "rb").read()
out = []
try:
for line in raw.decode("ascii").split("\n")[:-1]:
out.append(cw(fn(json.loads(line))) + "\n")
except Exception as e:
failures += 1
print("FAIL %s/%s: %s: %s" % (sub, name, type(e).__name__, e))
continue
got = "".join(out).encode()
nfiles += 1
nvec += len(out)
if write:
os.makedirs(os.path.join(write, sub), exist_ok=True)
open(os.path.join(write, sub, name), "wb").write(got)
if got != raw:
failures += 1
a, b = got.split(b"\n"), raw.split(b"\n")
k = next((k for k in range(min(len(a), len(b))) if a[k] != b[k]), min(len(a), len(b)))
print("DIFF %s/%s: line %d differs" % (sub, name, k + 1))
print("rewrote %d vector files, %d vectors" % (nfiles, nvec))
idx = json.load(open(os.path.join(root, "certificates", "index.json")))
nsets = ngraphs = 0
lowered = {}
for e in idx["sets"]:
try:
files = {m: open(os.path.join(wai, p), "rb").read() for m, p in e["members"].items()}
if set_digest(files) != e["set_sha256"]:
raise ValueError("the set's digest differs")
roles = lower_set(e, files)
if len(roles) != len(e["roles"]):
raise ValueError("the roles differ")
for (role, b), path in zip(roles, e["roles"]):
if not path.endswith("/%s/%s.whg1" % (e["set_sha256"], role)):
raise ValueError("role %s is at %s" % (role, path))
if write:
p = os.path.join(write, os.path.relpath(os.path.join(wai, path), root))
os.makedirs(os.path.dirname(p), exist_ok=True)
open(p, "wb").write(b)
if open(os.path.join(wai, path), "rb").read() != b:
raise ValueError("%s: the lowered graph differs" % role)
if e.get("synthetic"):
lowered[(FORM_FILE[e["lowering"]], e["name"], role)] = b
ngraphs += 1
nsets += 1
except Exception as ex:
failures += 1
print("FAIL lowering %s (%s): %s" % (e.get("name"), e.get("capability"), ex))
print("re-lowered %d sets, %d graphs" % (nsets, ngraphs))
failures += check_lower_vectors(root, lowered)
return failures
FORM_FILE = {
"int_synth/1": "lower_int_synth", "whm1/1": "lower_whm1", "wam1/1": "lower_wam1",
"wmb1/1": "lower_wmb1", "world/1": "lower_world", "jpegai-int/1": "lower_jpegai",
}
def check_lower_vectors(root, lowered):
"""Each vector of graphs/lower_<form>.jsonl has the id <file>/<set>/<role>/...: its
graph must be that role of that synthetic set as re-lowered here, and every role
of every synthetic set must have one. `lowered` maps (file, set, role) to bytes."""
failures = 0
seen = set()
d = os.path.join(root, "graphs")
for name in sorted(os.listdir(d)):
if not (name.startswith("lower_") and name.endswith(".jsonl")):
continue
f = name[:-len(".jsonl")]
for k, line in enumerate(open(os.path.join(d, name), "rb").read().decode("ascii").split("\n")[:-1]):
v = json.loads(line)
parts = v["id"].split("/")
key = tuple(parts[:3])
if len(parts) < 4 or parts[0] != f or key not in lowered:
failures += 1
print("FAIL graphs/%s line %d: %s names no lowered role" % (name, k + 1, v["id"]))
elif bytes.fromhex(v["graph"]) != lowered[key]:
failures += 1
print("FAIL graphs/%s line %d: %s is not the role as lowered" % (name, k + 1, v["id"]))
seen.add(key)
for key in sorted(set(lowered) - seen):
failures += 1
print("FAIL %s: no lowering vector for this role" % "/".join(key))
print("matched %d lowered roles to their lowering vectors" % len(seen & set(lowered)))
return failures
def main(argv):
if len(argv) not in (2, 4) or (len(argv) == 4 and argv[2] != "--write"):
print(__doc__)
return 2
write = argv[3] if len(argv) == 4 else None
failures = check(argv[1], write)
if failures:
print("%d failures" % failures)
return 1
print("ops-conformance: every vector file and every lowered graph rewritten byte for byte")
return 0
if __name__ == "__main__":
sys.exit(main(sys.argv))