WAI Extension: Integer Operators (wai-ops/1)
Mirrored from the canonical text at commit 4ab66d24 ().
Status: Draft. This extension defines
wai-ops/1, the integer operator set through which every integer-exact WAI capability decodes. It defines:
- the operators, with their exact integer arithmetic;
- the tensor range rules;
- two accumulator profiles,
acc64andacc32;- the static bounds rules that decide which profile a parameter set meets, and the bounds certificate that records the result;
- the accelerator subset
wai-ops/1-npu;- the
WHG1graph form;- how a capability declares the profile it needs;
- how the set relates to the determinism tiers;
- the canonical lowerings of the existing integer network forms to
WHG1;- the conformance that an operator implementation, a parameter set, a sink and a certifier must each pass.
What a reader can build from this text. With the payload extensions it cites, an implementation of this text decodes
wai.video.int_motion,wai.audio.int_codec,wai.splat.int_codec,wai.neural.int_synth,wai.neural.int_hyper,wai.video.int_hyper,wai.neural.int_mlicppandwai.{image,video,audio,splat}.int_refine, and reproduces the symbols and scale indices thatwai.image.jpegaientropy-decodes (§13.6.1). The payload text four of these depend on,entropy.json, theint_synthpayload and theWIM1payload with itsWMB1bundle, is in integer-payloads, and §15.2 says where. The integer renderer ofwai.world.modeland the other integer networks ofwai.image.jpegaiare informative (§13.5, §13.6.2).Reference implementation: the
wai-opscrate (wai/wai-ops): parse, lowering, interpreter, stage operators and bounds. Tool:wai-verify bounds. Conformance:wai/ops-conformance/. This Draft precedes all three. Until they land, each capability’s reference implementation is the decoder its payload extension names, and §15.3 lists where this text and that decoder differ.The keywords MUST, MUST NOT, SHOULD and MAY are as defined in RFC 2119 and RFC 8174.
1. Scope
Conformance targets. This extension states requirements for four things.
- An operator implementation: a library, a set of kernels, a shader set or a toolchain back end. It conforms to a profile (§7) on a fabric when it reproduces every per-operator vector (§14.1) and every graph vector (§14.2) tagged with that profile, refusals included.
- A parameter set: the networks a capability’s decode runs. It conforms to its capability when its bounds certificate (§8) meets the capability’s declared profile (§11).
- A sink decoding a capability that §13 lowers. It runs that capability’s graphs through an operator implementation of a profile that the set’s certificate meets, and it reproduces the capability’s conformance corpora.
- A certifier: a program that computes bounds certificates (§8). It conforms when it reproduces every certificate of §14.3 byte for byte, and the load verdict of every graph vector.
Out of scope
- Floating-point stages.
- How a sink schedules work across threads or devices. §6 says what an implementation may reorder.
- How a sink reports or attests the fabric that ran. That belongs to determinism-tiers.
- Fixed generative algorithms that are not networks.
Relation to the payload extensions. integer-payloads and staged-delivery keep their byte layouts, check orders and refusal codes. This extension:
- defines the arithmetic of the networks those payloads decode against, which integer-payloads §9 used to leave to the reference decoders;
- names the entropy and sample-domain arithmetic they already define, adds the modes other capabilities need, and states the width each operation needs.
2. Notation and integer functions
All values are mathematical integers. No formula in this extension has an implicit width, and none wraps or saturates unless it says so.
| Notation | Meaning |
|---|---|
⌊a / b⌋ | Floor division, for b > 0: the largest integer q with q·b ≤ a. |
⌈a / b⌉ | −⌊−a / b⌋, for b > 0. |
trunc(a / b) | Division rounded toward zero, for b ≠ 0. |
a mod b | a − b·⌊a / b⌋, for b > 0. It always lies in [0, b). |
a OR b, a XOR b | Bitwise, on non-negative integers. |
sgn(a) | −1, 0 or 1. |
clamp(v, lo, hi) | min(max(v, lo), hi), for lo ≤ hi. |
isqrt(n) | For n ≥ 0, the largest r ≥ 0 with r² ≤ n. |
isqrt_ceil(n) | For n ≥ 0, the smallest r ≥ 0 with r² ≥ n. |
wrap16(a) | ((a + 2^15) mod 2^16) − 2^15. |
| fits iN | −2^(N−1) ≤ v ≤ 2^(N−1) − 1. |
bits([lo, hi]) | The smallest N ≥ 1 such that both lo and hi fit iN. |
Rounding a scaled product. For an integer p and a shift s ≥ 0:
R_floor(p, s) = ⌊p / 2^s⌋.R_half_up(p, s) = ⌊(p + 2^(s−1)) / 2^s⌋fors ≥ 1, andpfors = 0. A tie rounds toward +∞, whatever the sign ofp.R_half_even(p, s)ispfors = 0. Otherwise, letq = ⌊p / 2^s⌋,r = p − q·2^sandh = 2^(s−1). The result isq + 1ifr > h, or ifr = handqis odd. Otherwise it isq.
All three are non-decreasing in p. §8 relies on this.
Exponential of a non-positive Q16 value. exp_neg_q16(x) is defined for x ≤ 0. It forms these values, in this order, and the names in brackets are the intermediates §5.7 classifies:
u = −x(exp.negx);n = ⌊u / 45426⌋, andg = 45426·n(exp.nl);r = x + g, sor ∈ (−45426, 0];t = r + 88670, sot ∈ [43245, 88670];t2 = t²(exp.t2), andq = ⌊t2 / 2^16⌋;c = 23495·q(exp.c), andy = ⌊c / 2^16⌋ + 22544.
The result is 0 if n ≥ 63, and ⌊y / 2^n⌋ otherwise. It lies in [0, 65553], and exp_neg_q16(0) = 65553.
Q16 reciprocal square root. rsqrt_q16(v) is defined for v ≥ 0. Let r = isqrt(v). The result is 0 if r = 0. Otherwise it is ⌊D / r⌋, where the dividend D = 2^32 is the intermediate rsq.num. Every operator that calls it has v ≥ 1, so r ≥ 1 and D is always formed.
3. Tensors and ranges
Shape and layout. A tensor has a shape [C, H, W] with every dimension at least 1. Its elements are indexed (c, y, x) at the row-major position (c·H + y)·W + x (CHW). A one-dimensional signal of T samples per channel is [C, 1, T]. An operator’s output shape is a function of its input shapes, its parameters and, for crop, the graph’s scalars (§5). Shapes are planned before any element is computed (§4).
Caps. No tensor exceeds 2^26 elements, and no size an operator forms (a padded side; a transposed convolution’s uncropped side, and its uncropped output of cout planes; an upsampled side) exceeds 2^26. Sizes are formed in 64-bit arithmetic with overflow checked, and a size past 64 bits is past the cap. A size past the cap is refused with too_large before anything is allocated. These are the caps of integer-payloads §7.
Range and policy. Every tensor of a graph has a policy.
| Policy | Meaning |
|---|---|
derived | The tensor’s range is whatever §8 derives from its producer. Nothing is enforced at run time. |
clamp | The tensor declares a range [lo, hi]. Its producer’s results are replaced by clamp(v, lo, hi), and that is part of the operator’s output. |
check | The tensor declares a range [lo, hi]. When the tensor has been produced, the decode is refused (field_range, §4) if any element lies outside [lo, hi]. Nothing that reads the tensor runs. |
A graph input has the policy check or clamp. lo ≤ hi, and both fit i64.
Exponent. A tensor MAY carry an exponent e, an integer in −127..=127. It means that the integer q represents q·2^−e. The arithmetic never reads an exponent. Three load rules use one, and a graph that breaks one is refused (model_mismatch):
- for
addandconcat, take the operator’s input tensors and its output tensor: either none of them declares an exponent, or every one of them declares one and all are equal; - for
chunk_add, the same rule, over its input tensor and its output tensor; - for
lut16, the rule of §5.6.
No other operator’s exponents are constrained. A tensor record encodes “no exponent” as −128 (§10.2).
Storage. An implementation stores a tensor in any integer type that holds the tensor’s certified range (§8), including an unsigned type or an offset encoding. Before an element enters a product that a sum of §5 accumulates, it is decoded to its value: every term of a certified sum is the product of its operands’ values. A multiply-accumulate over offset-encoded operands, such as Σ w·(x + z) followed by subtracting z·Σ w, forms partial sums that the certificate does not bound, and an implementation MUST NOT use one.
4. Exactness, evaluation order and refusal
Exactness. Each operator’s output is an exact integer function of its input tensors, its parameters and the graph’s scalars (§10). An implementation MUST produce exactly those integers. Evaluation order is not part of any operator’s meaning. Where a definition writes a sum, the sum is the exact integer, whatever the order of its terms. §6 lists what an implementation may do with that freedom.
Refusal points. A decode through a graph stops at the first of these that applies, and computes nothing after it.
- Load, once per graph (§10.5). The graph is malformed. Or its channel counts do not plan, or, when it declares its input sizes, its shapes do not plan on those sizes (§5). Or a size passes the caps. Or its certificate does not meet the profile its capability declares (§11). The code is
model_mismatch, ortoo_largefor a size. - Run, at each decode, in this order:
-
each input, one at a time in input-table order, state inputs included: its channel count against its tensor record, and
H ≥ 1andW ≥ 1(model_mismatch); then its size,H ≤ max_handW ≤ max_wwhen the input declares them, andC·H·W ≤ max_elems(§10.1) (too_large). An input’s values are not read in this step; -
each scalar against its declared range (
field_range); -
the run-time plan: every operator in graph order, on the sizes and scalar values of this decode. For each operator, in this order:
- every size it forms before its shape rule is read is checked against
2^26(too_large). Those sizes areconv’s padded sidesH + 2phandW + 2pw;conv_transpose’s uncropped sides and its uncropped output ofcoutplanes (§5.3); and the output sides ofpixel_shuffle,pad_repandsubpos_merge; - then its shape rule (§5) (
geometry_mismatch); - then each output tensor:
C·H·W ≤ max_elems(too_large).
This step catches what load-time planning cannot: a divisibility that holds at the declared sizes but not at a smaller one; a kernel larger than a small padded input; inputs of
add,concat,crop_toorsubpos_mergewhose sizes disagree; acropwhose scalars do not fit; a state whose shape no longer matches (§10.3); - every size it forms before its shape rule is read is checked against
-
evaluation. It starts with the inputs, in input-table order, state inputs included: each input’s policy applies to it, so a
clampinput is clamped, and an element of acheckinput outside its range refuses the decode (field_range). Then the operators run one by one in graph order. When achecktensor has been produced, an element outside its range refuses the decode (field_range).
-
A capability whose payload extension uses one code for every refusal reports that code in place of these. Examples are wai.neural.int_synth and wai.neural.int_mlicpp, which report payload_malformed (integer-payloads §8).
A capability’s driver MAY plan its graphs before it decodes any stream, at the point where its payload extension plans its networks. It then reports its extension’s codes, in its extension’s order, and step 3 never refuses in a decode that reaches it.
An implementation with overflow checks and one without compute the same integers and refuse the same inputs. Overflow is excluded by §8 before any decode starts.
5. Graph operators
A graph operator reads input tensors and writes output tensors. Its parameters are pinned with the parameter set (§10). Each subsection gives the operator’s:
- shape: the shape rule, which §4 applies at load and at run time;
- definition: the exact output;
- intermediates: the named values that §7 and §8 classify.
| code | operator | in → out | family | in wai-ops/1-i32 (§9.1) | in wai-ops/1-npu (§9.2) |
|---|---|---|---|---|---|
0x01 | conv | 1 → 1 | linear | yes | yes; groups 1 or depthwise |
0x02 | rescale | 1 → 1 | requantisation | yes | yes |
0x03 | chscale | 1 → 1 | requantisation | yes | flagged |
0x04 | lut16 | 1 → 1 | table | yes | flagged |
0x05 | add | 2 → 1 | elementwise | yes | yes |
0x06 | concat | n ≥ 2 → 1 | movement | yes | yes |
0x07 | chunk_add | 1 → 1 | elementwise | yes | yes |
0x08 | split | 1 → 1 | movement | yes | yes |
0x09 | pixel_shuffle | 1 → 1 | movement | yes | yes |
0x0A | pixel_unshuffle | 1 → 1 | movement | yes | yes |
0x0B | clamp | 1 → 1 | elementwise | yes | yes |
0x0C | relu | 1 → 1 | elementwise | yes | yes |
0x0D | leaky | 1 → 1 | elementwise | yes | yes |
0x0E | pad_rep | 1 → 1 | movement | yes | yes |
0x0F | crop_to | 2 → 1 | movement | yes | yes |
0x10 | mul | 2 → 1 | elementwise | yes | yes |
0x11 | conv_transpose | 1 → 1 | linear | yes | yes (§9.3) |
0x12 | igdn | 1 → 1 | normalisation | yes | no |
0x13 | abs | 1 → 1 | elementwise | yes | yes |
0x14 | crop | 1 → 1 | movement | yes | yes, with constant operands only |
0x15 | subpos_split | 1 → 4 | movement | yes | yes |
0x16 | subpos_merge | 4 → 1 | movement | yes | yes |
0x17 | fill | 1 → 1 | movement | yes | yes |
0x18 | layernorm_c | 1 → 1 | normalisation | no | no |
0x19 | l2norm | 1 → 1 | normalisation | no | no |
0x1A | softmax | 1 → 1 | normalisation | no | no |
0x1B | sigmoid | 1 → 1 | scalar function | no | no |
0x1C | elu | 1 → 1 | scalar function | no | no |
0x1D | gram | 2 → 1 | contraction | yes | no |
0x1E | apply | 2 → 1 | contraction | yes | no |
5.1 Requantisation (rq)
rq is the epilogue of conv, conv_transpose, rescale, mul, gram and apply, and chscale (§5.4) follows the same steps. It is not an operator in its own right. For output channel o, its parameters are:
mult[o]: an integer that fits i64, or absent, which means 1;shift[o]: an integer in0..=127;bias[o]: an integer that fits i64, or absent, which means 0. A bias has a placement, eitherpreorpost;round: one offloor,half_uporhalf_even.
A mult or shift given as one value is that value for every channel.
rq_o(v): t = v + (bias placed pre ? bias[o] : 0) rq.t
p = t · mult[o] rq.p
q = R_round(p, shift[o]) (§2) rq.q
y = q + (bias placed post ? bias[o] : 0) rq.y
y is the operator’s result. The output tensor’s policy (§3) then applies to it.
Intermediates
rq.t, classacc.rq.p, classproduct, whose operands aretandmult[o]. The values formed arepand, whenroundis notfloorands ≥ 1,p + 2^(s−1). Its interval covers both (§8.3). Withmultabsent,p = t.rq.q, classacc: the rounded value before apostbias.rq.y, classacc.
An implementation forms each of these in a width that holds its certified interval (§6). In particular, it MUST NOT narrow q to a width that does not hold rq.q before it adds a post bias, even where y would fit.
Informative. The registered capabilities use these settings (§13):
- no multiplier, one shift,
prebias,floor: each layer ofint_synth; - no multiplier, one shift per channel,
prebias,floor: the three convolutions of thewai.image.jpegaiscale path; - no multiplier, shift 0,
prebias: the per-channel gain of that scale path; - one multiplier per channel, one shift,
postbias,floor: the convolutions ofWHM1andWAM1; - one multiplier, one shift,
postbias,floor: the convolutions of thewai.image.jpegaiPTQ networks; - no multiplier, shift 16, no bias,
floor: their Q16 products; - one multiplier, one shift, no bias,
half_up: the RGB8 output step (multiplier 255) and the PCM output step (multiplier 32767); - no multiplier, shift 16, no bias,
half_up:gramandapplyin channel attention; - one multiplier per channel, shift 16, no bias,
half_up: the attention temperature; - one multiplier, shift 0,
postbias: the denormalisation ofwai.image.jpegai.
Changing any one setting changes decoded output.
5.2 conv (0x01)
Parameters
cout ≥ 1;- kernel
kh, kw ≥ 1; - stride
sh, sw ≥ 1; - zero padding
ph, pw ≥ 0; groups ≥ 1;- weights
Wt[cout][cin/groups][kh][kw]; - an
rqovercoutchannels.
Shape. The input is [cin, H, W]. Both cin and cout are multiples of groups, with H + 2ph ≥ kh and W + 2pw ≥ kw.
Ho = ⌊(H + 2ph − kh) / sh⌋ + 1, andWolikewise.- The output is
[cout, Ho, Wo].
Definition. Let cg = cin / groups and og = cout / groups. Let X̃[c][u][v] be X[c][u][v] for 0 ≤ u < H and 0 ≤ v < W, and 0 elsewhere.
acc[o][y][x] = Σ_{ci < cg} Σ_{i < kh} Σ_{j < kw} Wt[o][ci][i][j] · X̃[⌊o/og⌋·cg + ci][y·sh + i − ph][x·sw + j − pw]
out[o][y][x] = rq_o(acc[o][y][x])
A depthwise convolution is groups = cin = cout. There is no dilation.
Intermediates: conv.term (each product) and conv.acc (every partial sum), both class acc; then rq.
5.3 conv_transpose (0x11)
Parameters
cout ≥ 1;- kernel
kh, kw ≥ 1; - stride
sh, sw ≥ 1; - padding
ph, pw ≥ 0; - output padding
oph, opw ≥ 0; - weights
Wt[cin][cout][kh][kw]; - an
rqovercoutchannels.
groups is 1.
Shape. The input is [cin, H, W].
Ho = (H − 1)·sh + kh + oph − 2·phmust be at least 1, andWolikewise.- The uncropped sides
uh = (H − 1)·sh + kh + ophanduw = (W − 1)·sw + kw + opw, and the uncropped output ofcout·uh·uwelements, are each within2^26(§3). They are checked beforeHoandWo(§4). - The output is
[cout, Ho, Wo].
Definition
acc[o][y][x] = Σ Wt[ci][o][i][j] · X[ci][ty / sh][tx / sw]
over ci < cin, i < kh, j < kw with
ty = y + ph − i ≥ 0, ty mod sh = 0, ty / sh < H,
tx = x + pw − j ≥ 0, tx mod sw = 0, tx / sw < W
out[o][y][x] = rq_o(acc[o][y][x])
Informative. This is the same set of products as the scatter form: add every Wt[ci][o][i][j]·X[ci][u][v] into an uncropped plane at (u·sh + i, v·sw + j), then keep rows [ph, ph + Ho) and columns [pw, pw + Wo). Output padding extends only the bottom and right edges, and it need not be smaller than the stride. A position that no tap reaches holds rq_o(0). A one-dimensional transposed convolution is kh = 1, sh = 1, ph = 0, oph = 0 over [cin, 1, T].
Intermediates: convT.term (each product) and convT.acc (every partial sum), both class acc; then rq.
5.4 rescale (0x02), chscale (0x03) and mul (0x10)
rescale. The input is [C, H, W], with an rq over C channels.
out[c][y][x] = rq_c(in[c][y][x]).- With
multandbiasset, this is also an affine map: an added constant, a scaled offset, or a rounding shift. - Intermediates: those of
rq.
chscale. The parameters are:
- a scalar index
k(§10); - a table
M[n_rows][C]of integers, each of which fits i64; - one shift
sin0..=127; - a
round.
There is no bias.
out[c][y][x] = R_round(in[c][y][x] · M[r][c], s), whereris the value of scalark.- The scalar’s declared range lies within
[0, n_rows − 1]; this is a load check. - Intermediates: those of
rq, withtthe input value,M[r][c]the multiplier and no bias, soy = q.
mul. The inputs are a [C, H, W] and b, which is either [C, H, W] or [1, H, W]. There is an rq over C channels with no bias.
out[c][y][x] = rq_c(a[c][y][x] · b[c′][y][x]), wherec′ = cifbhasCchannels, and 0 otherwise.- Intermediates:
mul.p(each producta·b), classacc; thenrq, witht = mul.p.
5.5 Elementwise: add (0x05), chunk_add (0x07), clamp (0x0B), relu (0x0C), leaky (0x0D), abs (0x13)
| operator | shape | definition |
|---|---|---|
add | a and b have the same shape | a + b |
chunk_add | [C, H, W] with C even → [C/2, H, W] | out[c] = in[c] + in[c + C/2] |
clamp(lo, hi) | same shape; lo ≤ hi, both fit i64 | clamp(v, lo, hi) |
relu | same shape | max(v, 0) |
leaky(a, f) | same shape; a fits i64, f in 0..=63 | v if v ≥ 0, else ⌊v·a / 2^f⌋ |
abs | same shape | ` |
A one-sided clamp uses −2^63 or 2^63 − 1 for its open side. A saturating sum is add with a clamp policy on its output.
Intermediates: leaky.p (each product v·a, formed for negative v only), class acc. The sums of add and chunk_add are their results.
5.6 lut16 (0x04)
Parameters
- a table
Tof 65,536 integers, each of which fits i16, withT[0] = 0; - exponents
e_inande_out, each an integer in −127..=127 or none (encoded −128, §10.4).
Shape. The output has the input’s shape. Two load checks apply (model_mismatch):
- The input’s certified range (§8) lies within
[−32767, 32767]. e_inand the input tensor’s exponent are both none or are equal; so aree_outand the output tensor’s exponent.
Definition. out = T[v + 32768].
The table is parameter data, pinned with the set. The function a table stands for, and how its entries were computed, are not part of this extension.
5.7 Normalisation: igdn (0x12), layernorm_c (0x18), l2norm (0x19), softmax (0x1A)
igdn.
- Parameters: shifts
gfandfrac, each in0..=127;γ[C][C], row-major with rowithe output channel and columnjthe input channel;β[C]. - Shape:
[C, H, W]→[C, H, W]. - Definition: at each position
(y, x), withx_j = in[j][y][x]:
N_i = β_i + Σ_j ⌊ γ[i][j] · x_j² / 2^gf ⌋ (each term is floored, then the terms are summed)
r_i = isqrt(max(0, N_i))
out[i][y][x] = ⌊ x_i · r_i / 2^frac ⌋
Flooring the sum once, instead of each term, is a different function.
- Intermediates:
igdn.x2(eachx_j²), classwide;igdn.gx2(eachγ[i][j]·x_j², before the floor), classproduct, whose operands arex_j²andγ[i][j];igdn.term(each floored term), classwide;igdn.N(every partial sum ofN_i, withβ_ior without), classwide;igdn.r, classacc;igdn.xr(eachx_i·r_i), classwide.
layernorm_c.
- Parameters:
ε, an integer in1..=2^64 − 1;γ[C];β[C]. - Shape:
[C, H, W]→[C, H, W]. - Definition: at each position, over that position’s
Cchannel valuesx_c:
S = Σ_c x_c ln.S
M = S + ⌊C/2⌋·sgn(S) ln.m
m = trunc(M / C)
d_c = x_c − m ln.d
T = Σ_c d_c² ln.T
V = ⌊T / C⌋
U = V + ε ln.v
k = rsqrt_q16(U) (§2) rsq.num, ln.k
n_c = ⌊d_c · k / 2^16⌋ ln.dk (d_c·k), ln.n
out[c] = ⌊n_c · γ[c] / 2^16⌋ + β[c] ln.ng (n_c·γ[c])
- Intermediates:
ln.Sandln.T(every partial sum),ln.m,ln.v,ln.dkandln.ng, classwide;ln.d,rsq.num,ln.kandln.n, classacc.
l2norm.
- Parameters:
ε, an integer in1..=2^64 − 1. - Shape: unchanged.
- Definition: for each channel
c, over allH·Wpositions of that channel:
T = Σ_{y,x} v²
U = T + ε
k = rsqrt_q16(U)
out = ⌊v · k / 2^16⌋
- Intermediates:
l2.T(every partial sum ofT, andU) andl2.vk(eachv·k), classwide;rsq.numandl2.k, classacc.
softmax.
- Parameters: none.
- Shape: unchanged. It normalises along
W, separately for each(c, y). - Definition:
m = max_x v_x
e_x = exp_neg_q16(v_x − m) (§2)
Σ = Σ_x e_x (at least 65553)
out_x = ⌊(e_x · 2^16 + ⌊Σ / 2⌋) / Σ⌋
Each output lies in [0, 2^16]. The outputs of a row need not sum to 2^16.
- Intermediates:
sm.diff(eachv_x − m),exp.negx,exp.nl,exp.t2,exp.c(§2),sm.sum(every partial sum ofΣ) andsm.num(eache_x·2^16 + ⌊Σ/2⌋), all classacc.
5.8 Scalar functions: sigmoid (0x1B), elu (0x1C)
sigmoid. No parameters; the shape is unchanged.
- If
v ≥ 0: lete = exp_neg_q16(−v); the output is⌊2^32 / (2^16 + e)⌋. - If
v < 0: lete = exp_neg_q16(v); the output is⌊e · 2^16 / (2^16 + e)⌋. - In both cases the argument of
exp_neg_q16is−|v|, soexp.negx = |v|. - The output lies in
[0, 2^16]. - Intermediates:
exp.negx,exp.nl,exp.t2,exp.c, andsig.num(the dividend:2^32, ore·2^16), all classacc.
elu(α). α fits i64; the shape is unchanged.
- If
v > 0, the output isv. - Otherwise, the output is
⌊α · (exp_neg_q16(v) − 2^16) / 2^16⌋. - At
v = 0this gives⌊17·α / 2^16⌋. - Intermediates:
exp.negx,exp.nl,exp.t2andexp.c, classacc;elu.p(eachα·(e − 2^16)), classwide.
5.9 Contractions: gram (0x1D), apply (0x1E)
Both take groups ≥ 1 and an rq with no multiplier, no bias and one shift.
gram. The inputs are A and B, both [C, H, W]. C is a multiple of groups, and d = C / groups. The output is [C, 1, d]:
out[g·d + i][0][j] = R_round( Σ_{y,x} A[g·d + i][y][x] · B[g·d + j][y][x], s )
apply. The inputs are A [C, 1, d] and V [C, H, W], with C = groups·d. The output is [C, H, W]:
out[g·d + i][y][x] = R_round( Σ_{j<d} A[g·d + i][0][j] · V[g·d + j][y][x], s )
Intermediates: gram.term and apply.term (each product), and gram.acc and apply.acc (every partial sum), all class acc; then rq, with t the sum.
5.10 Data movement
| operator | parameters | shape | definition |
|---|---|---|---|
concat (0x06) | none | inputs share H and W → channels summed | the inputs’ channels, in input order |
split (0x08) | start, count ≥ 1 | start + count ≤ C → [count, H, W] | out[c] = in[start + c] |
pixel_shuffle (0x09) | r ≥ 1 | C a multiple of r² → [C/r², H·r, W·r] | out[c][h·r + i][w·r + j] = in[c·r² + i·r + j][h][w] |
pixel_unshuffle (0x0A) | r ≥ 1 | H and W multiples of r → [C·r², H/r, W/r] | out[c·r² + i·r + j][h][w] = in[c][h·r + i][w·r + j] |
pad_rep (0x0E) | m ≥ 1 | → [C, ⌈H/m⌉·m, ⌈W/m⌉·m] | out[c][y][x] = in[c][min(y, H−1)][min(x, W−1)] |
crop_to (0x0F) | none; the inputs are in and ref | ref’s H′ ≤ H and W′ ≤ W → [C, H′, W′] | out[c][y][x] = in[c][y][x]; only ref’s shape is read |
crop (0x14) | y0, x0, h, w, each a constant or a scalar | y0, x0 ≥ 0, h, w ≥ 1, y0 + h ≤ H, x0 + w ≤ W → [C, h, w] | out[c][y][x] = in[c][y0 + y][x0 + x] |
subpos_split (0x15) | order[4], a permutation of (0,0), (0,1), (1,0), (1,1) | H and W even → four outputs [C, H/2, W/2] | out_k[c][y][x] = in[c][2y + a_k][2x + b_k], with (a_k, b_k) = order[k] |
subpos_merge (0x16) | order[4] | four inputs [C, H, W] → [C, 2H, 2W] | the inverse of subpos_split with the same order |
fill (0x17) | a value, constant or scalar; fits i64 | the output’s channels come from its tensor record; H and W come from the input | every element is the value |
pixel_shuffle takes channel c·r² + i·r + j to row offset i and column offset j of output channel c.
A constant operand is a u32 (§10.4), and a scalar operand is any value of its scalar’s range, negative ones included. A crop with a scalar operand is planned once the scalar is known (§4, run step 3), and one that does not fit, a negative start among them, is refused with geometry_mismatch.
At load, when the inputs declare their sizes (§10.5 step 7), each side of a crop is planned on the smallest start and the largest length within the input. Take one side, of input size S:
- the start
s0is the constant, or 0 for a scalar start, whatever the scalar’s range; - the length is the constant, or
min(hi, S − s0)for a scalar length, withhithe top of its scalar’s range; - the shape rule then applies to
s0and that length, and a side that fails it is refused (model_mismatch).
5E. Stage operators
A stage operator runs in a capability’s driver, outside any graph. It works on streams, symbols and samples, and each has a fixed width (§7.4). The payload extensions keep the text that already defines four of them:
rans64_decodeis defined in integer-payloads §2;mc_predictin integer-payloads §3;up2_predictandrate_predictin staged-delivery Appendix A.4.
This section names those four and adds the modes other capabilities need. It also defines the other nine, which no payload extension defines today.
| # | operator | definition | width |
|---|---|---|---|
| E1 | rans64_decode | integer-payloads §2, with the modes below | u64 state |
| E2 | dequantize | sym·2^f + base | 128-bit, or 64-bit checked |
| E3 | bucket | scale to table index, rule ceil or floor | comparisons |
| E4 | coded | v > thr | comparisons |
| E5 | scale_index | clamp(R_half_up(v, s), 0, n − 1) | i32 |
| E6 | block_stat | a rounded block sum, broadcast over its block | i32 |
| E7 | band_lookup | count the edges, then look up the table | comparisons |
| E8 | mans_decode | below | u64 window, u8 states |
| E9 | residual_step | clamp(pred + r·q, lo, hi) | i64, or i32 by identity |
| E10 | mc_predict | integer-payloads §3 | i32 |
| E11 | up2_predict | staged-delivery A.4 | i32 |
| E12 | rate_predict | staged-delivery A.4, with rdiv | i64 |
| E13 | coding_order | raster or checkerboard | indices |
Conditions and their refusals. Each operator below states the conditions its operands, modes and tables meet. An operand, mode or table that breaks one is refused before any symbol or element is decoded, with these codes:
field_rangefor E2’sfand value, as E2 states;geometry_mismatchwhen theprevthat E11 or E12 reads does not have the shape staged-delivery A.4 step 1 requires, or when a size or rate that shape is computed from (d0,d1,rate_num,rate_den) is 0;model_mismatchfor every other condition, the checks of E1’s and E8’s tables included.
The refusals that arise while a stream is decoded are E1’s and E8’s own, and they come after these.
E1 rans64_decode modes. A capability fixes one value for each of the three modes (§11). A symbol is decoded against a table cdf of length n whose offset is o: integer-payloads §2 defines both, and o is added to every value the table decodes.
- Table rule.
t4097is integer-payloads §2:3 ≤ n ≤ 4097,cdf[0] = 0,cdf[n−1] = 65536, strictly increasing.t2is the same except that2 ≤ n ≤ 65537. Withn = 2, every symbol is the escape.- Before any symbol is decoded, every table is checked against the table rule, and its offset
omust fit i32. Understrict,lo ≤ hi. Each symbol’s table must be one of the tables given.
- Escape rule.
lenientis integer-payloads §2. It reads one 4-bit count and then continuation groups while the count group just read is 15 and the stream has not run out, at most 8 of them. It clamps the count to 8, accepts a non-minimal count, and saturates the value and the symbol toi32. Thecountvalue groups that follow are read whatever the stream’s state.strict(lo, hi), withlo ≤ hiboth fitting i32, reads one 4-bit count and no continuation groups:count = bits(4);- if
count ≤ 8,raw = Σ_{j<count} bits(4)·2^(4j), reading the groups forj = 0, 1, …in turn; ifcount > 8, no group is read; - the value is
−⌊raw/2⌋ − 1ifrawis odd and⌊raw/2⌋ + (n − 2)if it is even, and the symbol isvalue + o. Nothing is saturated.
- Reads after a stream runs out. Reads go on: integer-payloads §2 defines renormalisation to leave the state as it stands, and
bitsreads from that state. Whether the decode is refused for it is the end rule’s. - Checks on one symbol under
strict. When a symbol’s reads are done, its refusals are checked in this order, and the first that applies refuses the decode:- under the
canonicalend rule, the stream ran out at any point during this symbol’s decode, in its table step or in anybits(stream_exhausted); - for an escape,
count > 8(stream_noncanonical); - for an escape,
count ≥ 1and the group read last (j = count − 1) is 0, so the encoding is not minimal (stream_noncanonical); - for every symbol, escaped or not, a symbol outside
[lo, hi](field_range).
- under the
- End rule.
canonicalis integer-payloads §2: a decode stops at the first symbol during whose decode the stream runs out (stream_exhausted), and a stream must end canonically after its last symbol (stream_noncanonical).final_onlytests whether the stream has run out once, after its last symbol (stream_exhausted), and has no canonical-end rule. Symbols decoded after the stream ran out are the integers §2 gives from the state as it stands, and the decode is refused at the end. A refusal that a symbol’s own checks give before the end comes first.
E2 dequantize(sym, f, base).
fis at most 62; a larger value is refused withfield_range.- The value is
sym·2^f + base. It is refused (field_range) if it does not fit i64. - The value then enters a graph input, whose
checkrange applies.
E3 bucket(σ, e, rule). Comparisons only.
ceil. It takesN ≥ 1edgese_0 … e_{N−1}.- Let
σ′ = max(σ, e_0). The index isb = (N − 1) − #{ i < N − 1 : σ′ ≤ e_i }. - It lies in
[0, N − 1], for any edges. e_{N−1}is never compared.
- Let
floor. It takesN − 1 ≥ 1edgese_1 … e_{N−1}, non-decreasing: both are conditions (model_mismatch).- The index is
b = #{ i : e_i ≤ σ }. - It lies in
[0, N − 1].
- The index is
E4 coded(v, thr) is v > thr. A position that is not coded consumes no symbol.
E5 scale_index(v, s, n) is clamp(R_half_up(v, s), 0, n − 1), for s in 0..=127 and n ≥ 1.
E6 block_stat(x; b, s, lo, hi), for b ≥ 1, s in 0..=127 and lo ≤ hi. For each channel, the plane is tiled by b × b blocks from (0, 0). The last row and column of blocks may be partial.
Sis the sum over a block’s positions that lie in the plane.v = clamp(R_half_up(S, s), lo, hi).- Every position of the block takes
v. sis applied whatever the number of positions in the block.
E7 band_lookup(v, e[0..m), table[rows][m+1], row), for row in [0, rows), with row row of the table holding m + 1 entries.
band = #{ k : v ≥ e_k }.- The result is
table[row][band].
E8 mans_decode. This is a two-state masked table-ANS decode over a backward 64-bit bit window. Its tables and operands are checked before the window starts, and one that fails is refused with model_mismatch. The checks are the conditions of Building the latent tables and of The row table below; every bound in [0, 255]; for each span, a mask and a list of distributions of one length, each distribution σ naming both a row of T_y and a bound; and one length for each row. Every refusal after those checks is stream_exhausted: the window would need bits or bytes that the stream does not hold. A capability that reports one code for every refusal reports that code instead (§4).
Memory and window.
Mis 8 zero bytes followed by the stream’sLbytes.LE64(i)is the little-endian 64-bit value atM[i .. i + 8].- The window holds an index
p, a 64-bit headh, a bit positionb, and two 8-bit statess1ands2. - The stream is decoded as one thread: the window starts at the end of the memory.
Window operations.
init: p = L; h = LE64(p); if h = 0, refuse (no sentinel bit)
b = the index of h's highest set bit; h = h − 2^b
s1 = read(8); s2 = read(8); flush
read(k): if k > b, refuse
b = b − k; r = ⌊h / 2^b⌋; h = h − r·2^b; return r
flush: d = 7 XOR ⌊b / 8⌋; if d > p, refuse
p = p − d; h = LE64(p) mod 2^(56 + (b mod 8)); b = 56 + (b mod 8)
Latent decode (sgm). For each distribution σ, the table T_y[σ][state] holds 256 entries of 32 bits, and B_y[σ] is an unsigned 8-bit bound: β ∈ [0, 255].
in(which, i):t = T_y[σ_i][s_which];s_which = (⌊t/2^16⌋ OR read(⌊t/2^24⌋)) mod 2^8;sym_i = wrap16(t mod 2^16).
out(i): letβ = B_y[σ_i]. Ifsym_i + β = 0:f = read(1);v = read(f = 1 ? 3 : 16);g = v mod 2;sym_i = wrap16(⌊v/2⌋ + β)ifg = 0, andwrap16(−⌊v/2⌋ − β)ifg = 1;- then
flush.
- A span of
L′positions, with a mask, is decoded in groups of four positionsi … i+3:in(s1, i),in(s2, i+1),in(s1, i+2),in(s2, i+3), each only where the mask is set;- then
flush; - then
outfor each of the four, only where the mask is set.
- Then the remainder:
- if
L′mod 4 ≥ 2: positioni(s1), theni+1(s2), each asinthenoutwhere the mask is set; thenflush; - if
L′is odd: positioni(s1) asinthenoutwhere the mask is set; thenflush.
- if
- An unmasked position is 0 and reads nothing. It keeps its place in the
s1/s2alternation. - The window and the states carry across spans.
Building the latent tables. The rows P[σ], for σ = 0 … R − 1, all have one width w ≥ 1. Each holds non-negative integers that sum to exactly 256. These are conditions (model_mismatch). For row P = P[σ], let C[0] = 0 and C[k+1] = C[k] + P[k]. Then:
F[k] = C[k] − ⌊C[k]/2⌋;G[k] = ⌊C[k]/2⌋ + 128;sym(k) = k/2ifkis even, and−(k + 1)/2if it is odd.
For each column k in order, while C[k] < 256:
- every slot
u ∈ [F[k], F[k+1])gets symbolsym(k)andnext = P[k] + (u − F[k]); - every slot
u ∈ [G[k], G[k+1])gets symbolsym(k)andnext = P[k] + (F[k+1] − F[k]) + (u − G[k]).
Every slot is written, and next ∈ [1, 511]. Each slot u then gets the entry nb·2^24 + base·2^16 + (sym mod 2^16), where:
nbis the number of doublings that bringnextto at least 256, sonb ≤ 8;base = (next·2^nb) XOR 256, sobase < 256.
Hyper-latent decode (fac).
- The preprocessing table.
Zp[i]is defined forifrom 511 down to 1. Keep a counternb, starting at 0. At eachi, first add 1 tonbonce ifi·2^nb < 256. ThenZp[i] = ((i·2^nb) XOR 256) + 256·nb. - The row table. A row is 63 cumulative counts
0 ≤ c_0 ≤ … ≤ c_62 ≤ 255, a condition (model_mismatch); letc_{−1} = 0.- For each symbol
a < 63, letk = c_a − c_{a−1}. Each slotj ∈ [c_{a−1}, c_a)getsT_z[j] = Zp[k + j − c_{a−1}]·2^16 + a. - Then
T_z[255] = Zp[1]·2^16 + 63. - Slots that the row does not write keep the previous row’s entries.
T_zstarts at all zeros.
- For each symbol
in(which, i):t = T_z[s_which];s_which = (⌊t/2^16⌋ OR read(⌊t/2^24⌋)) mod 2^8;sym_i = t mod 2^8.out(i): ifsym_i = 63, thensym_i = read(6).- A row of
L′symbols is decoded in groups of four:in(s1, i),in(s2, i+1),in(s1, i+2),in(s2, i+3);- then
outfor each of the four; - then
flush.
- The remainder of a row:
- if
L′mod 4 ≥ 2:in(s1, i),out(i),in(s2, i+1),out(i+1); - if
L′is odd:in(s1, i),out(i).
- if
- Each row ends with one
flush. - Rows are decoded in order, each with its own row table, and the window, the states and
T_zcarry across them.
How a capability divides its streams into rows and spans, and which tables each uses, is the capability’s (for wai.image.jpegai, §13.6.1).
E9 residual_step(pred, r, q, lo, hi) is clamp(pred + r·q, lo, hi), where lo < hi, pred ∈ [lo, hi], q ∈ [1, 65535] and r fits i32.
- The 32-bit form (identity). With
R = ⌊(hi − lo)/q⌋ + 1, the step equalsclamp(pred + clamp(r, −R, R)·q, lo, hi). - Proof. If
r > R, thenr·q > R·q > hi − lo, so both forms givehi. The caser < −Ris symmetric. - Width. Every value this form forms fits i32 when
hi − lo ≤ 65535and bothlo − 131070andhi + 131070fit i32. ThenR·q ≤ (hi − lo) + q ≤ 131070, sopred + clamp(r, −R, R)·qlies in[lo − 131070, hi + 131070]. The ranges §13.5 registers,[0, 255]and[−32768, 32767], meet both conditions. Near the i32 limits the second fails: withlo = 2^31 − 65536,hi = 2^31 − 1,q = 65535,pred = hiandr = 2, the form formshi + 2·65535. A fabric forms such a step in a wider width.
E10 mc_predict and E11 up2_predict are integer-payloads §3 (whole-pel and half-pel) and staged-delivery A.4 (Up2). E10 is for a pixel (y, x) of the frame, a channel c of 0, 1 or 2, and mv_precision 1 or 2. Every sum they form is at most 4·255 + 2.
E12 rate_predict is staged-delivery A.4 (Rate), with rdiv(n, d) = 0 for d < 1, and sgn(n)·⌊(2|n| + d) / (2d)⌋ otherwise. Its samples are i16, and rate_num and rate_den are u32 fields of at least 1, so ρ < rate_num < 2^32 and |b − a| ≤ 65535. The products a·rate_num and (b − a)·ρ then have magnitudes below 2^48, the sum n below 2^47, and rdiv’s 2|n| + d below 2^49. Every value it forms fits i50, so it needs 64-bit arithmetic.
E13 coding_order.
raster: channel, then row, then column.checkerboard(anchor): the elements with(row + col) mod 2 = 0whenanchoris set, and the rest otherwise, each in raster order.
A capability MAY define other orders in its own extension.
6. What an implementation may change
Because every operator is an exact integer function (§4), an implementation MAY do any of the following.
- Partition outputs across threads, lanes, tiles or devices. A tensor MAY be split across several storage bindings.
- Reorder and regroup the terms of any sum. This includes splitting a sum across workers and adding the partial sums in any order. A term that is zero MAY be skipped, and a zero term MAY be added.
- Compute a value in any width that holds every value it forms. The certificate (§8) gives the interval of every intermediate. In particular:
- Chunked 32-bit partial sums. Let
Pbe the largest magnitude in the operator’s term interval (§8.3). ThenK_c = ⌊(2^31 − 1) / P⌋, or unbounded whenP = 0, and any partial sum of at mostK_cterms fits i32. The certificate recordsK_cask_c(§8.5). So an implementation MAY form i32 partial sums of at mostK_cterms, widen them, and add. - Dot-product instructions. An instruction that adds
nproducts into one i32 lane leaves that lane holdingn·jproducts afterjinstructions. The lane widens beforen·jwould exceedK_c. Each operand must also fit the instruction’s operand type. For an instruction over 8-bit operands, that means the weights fit i8 and the input tensor’s certified interval fits i8; the certificate records both widths per node (§8.5). - Bias. A
prebias joins the wide sum, unless the certificate’s partial-sum interval (§8.3), which includes the bias, fits the narrower width. - Narrowing a product.
rq.pMAY be formed in 64 bits wherever its certified interval fits i64.
- Chunked 32-bit partial sums. Let
- Run later operators speculatively. A refused decode outputs nothing.
An implementation MUST NOT:
- use floating-point arithmetic for any value of a graph or stage operator;
- use an instruction whose result the instruction set leaves to the implementation. On Wasm, that means no relaxed SIMD;
- depend on a value wrapping. A certified graph forms no value outside the width it is formed in, so none wraps;
- form a sum over offset-encoded operands (§3, Storage).
On a fabric whose shift instruction takes its amount modulo the register width, an implementation handles every shift of §5 that reaches or passes that width explicitly. A shift of a 32-bit value by 32 or more is an example.
7. Profiles
7.1 Width classes
Every intermediate that §5 names has a class. Every tensor element, every producer result (§8.1), every graph input and every scalar is class tensor.
| class | acc64 | acc32 |
|---|---|---|
tensor | fits i64 | fits i32 |
acc | fits i64 | fits i32 |
wide | fits i128 | fits i32 |
product | fits i128 | fits i64, formed from two operands that each fit i32 |
There are two product intermediates. rq.p has the operands rq.t and the multiplier (for chscale, the input and M[r][c]). igdn.gx2 has the operands igdn.x2 and γ[i][j]. Under acc32 both operands are certified to fit i32: rq.t and igdn.x2 by their own classes, and the multiplier and γ by §7.2’s parameter conditions.
7.2 The profiles
acc64. Every intermediate and producer result of every operator fits its class’sacc64width.- This is the reference profile for CPU and Wasm.
- It needs a 128-bit value only where a
wideorproductinterval passes 64 bits.
acc32. Three conditions hold:- every intermediate and producer result fits its class’s
acc32width, and every graph input’s and scalar’s declared range fits i32; - only graph inputs have the policy
check; - the parameters meet these conditions:
- every intermediate and producer result fits its class’s
| operator | parameter condition under acc32 | key (§8.5) |
|---|---|---|
every rq (conv, conv_transpose, rescale, mul, gram, apply) | each multiplier fits i32 | rq.mult |
| each shift is at most 63 | rq.shift | |
each post bias fits i32 | rq.post_bias | |
conv, conv_transpose | each weight fits i32 | weights |
chscale | each entry of M fits i32 | chscale.m |
s is at most 63 | chscale.s | |
igdn | each γ fits i32 | igdn.gamma |
gf and frac are each at most 63 | igdn.shift | |
leaky | a fits i32 | leaky.a |
A pre bias needs no condition: it lies in rq.t’s interval, which already fits i32.
- Every accumulator then fits a 32-bit register.
- The only 64-bit values are the two
productintermediates, each the exact product of two 32-bit operands. - An
acc32graph needs no refusal inside the graph, so a fabric that cannot stop a computation partway through can run it. - A set that meets
acc32also meetsacc64.
Which profile a parameter set meets is decided by its certificate (§8). Two sets for one capability can meet different profiles.
7.3 Operators that never meet acc32, and fabrics (informative)
Five operators form values whose intervals §8.3 fixes above i32 whatever their input or parameters:
softmax,sigmoidandelu, throughexp.t2(up to88670² ≈ 2^32.9) andexp.c(up to23495·119970 ≈ 2^31.4);sigmoid, throughsig.num(up to65553·2^16);layernorm_candl2norm, throughrsq.num(2^32), andl2normthroughl2.vk(±2^32).
So a graph that uses any of them meets acc64 at best. The other 25 graph operators form wai-ops/1-i32 (§9.1): the operators a 32-bit fabric must implement to run every graph that can meet acc32.
- CPU and Wasm run
acc64natively: Wasm has 64-bit integers, and a 128-bit value is formed from 64-bit halves. Relaxed SIMD is excluded (§6). - A 32-bit integer shading language runs
acc32. It forms eachproductintermediate from 16-bit halves in unsigned 32-bit arithmetic, which wraps by definition in every such language and so survives translation between them unchanged. It then rounds, shifts and adds thepostbias on that two-word value before it narrows the result to i32. Every signed value of anacc32graph provably fits i32, so no signed overflow reaches a translated shader. - A fabric that emulates 64-bit and 128-bit arithmetic exactly is an
acc64implementation on that fabric.
7.4 Stage operators
Stage operators (§5E) run in the driver at the widths listed in §5E.
rans64_decodeandmans_decodeneed a 64-bit state or window.dequantizeneeds a 128-bit intermediate, or checked 64-bit arithmetic.rate_predictforms values of magnitude up to2^49(E12), so it needs 64-bit arithmetic.scale_index,block_stat,band_lookup,coded,bucket,mc_predict,up2_predictandcoding_orderfit i32, and so doesresidual_stepin its 32-bit form, under E9’s conditions.
A fabric that runs a stage operator MUST reproduce its widths exactly, for example by emulating 64-bit values as pairs of 32-bit values.
8. Static bounds and the bounds certificate
8.1 Intervals
The bounds pass gives every tensor, scalar and named intermediate an integer interval [lo, hi].
- It visits operators in graph order (§10.3).
- Its only inputs are the graph’s declared input and scalar ranges, the input sizes the graph declares (§10.2), the parameters, and the policies.
- Where a rule of §8.3 is per output channel, the pass applies it to each channel and records the hull over channels: the smallest interval that contains every channel’s interval. Every recorded intermediate interval, and every tensor interval, is such a hull.
It never reads a payload. Its result is a function of the graph’s bytes alone.
Arithmetic, and saturation. Let sat(v) = clamp(v, −2^255, 2^255).
- The pass applies
satto the result of every primitive operation it performs:+,−,×,⌊a / b⌋(including a shift),isqrt,isqrt_ceil,min,max,|·|and negation. - A sum that a rule writes with
Σis a left fold of binary additions in the order its index runs, starting from the addend the rule writes first, withsatafter each addition. A partial-sum interval (§8.2) adds terms of one sign only, so for it the order makes no difference. - A product or sum of more than two operands that a rule writes without
Σis a left fold in the order written, withsatafter each operation, andR_roundis evaluated as §2 writes it. Otherwise, a product of three factors, one of which can be 0 while the other two pass2^255, would be saturated in one order and not in another. No rule of §8.3 forms one, and this order is fixed for any rule that does. - Every declared range, parameter, constant and size lies in
[−2^255, 2^255]before any operation.
Width. Every operand of a primitive operation therefore lies in [−2^255, 2^255]. A product then lies in [−2^510, 2^510], a sum or difference in [−2^256, 2^256], and every other result within its operands’ range. A signed 512-bit integer holds [−2^511, 2^511 − 1], so an implementation computes each result exactly in 512 bits and then saturates it. The width is needed: igdn.x2 on an input whose interval reaches ±2^255 forms 2^510, which no narrower signed integer holds.
Soundness. A node during whose computation any primitive result was changed by sat is saturated. It fits no profile, whatever intervals it records (§8.5). An interval of a node that is not saturated is exact for its rule. A saturated node fails the verdict, so the intervals downstream of it never decide a verdict.
Producer results and tensor intervals. A graph input’s interval is its declared range, and a scalar’s is its declared range. For any other tensor, let [a, b] be the interval its producer’s rule gives: the tensor’s producer result. It is classed tensor (§7.1), because it exists before the policy applies. The tensor’s own interval is then:
derived:[a, b];clamp [lo, hi]:[clamp(a, lo, hi), clamp(b, lo, hi)];check [lo, hi]:[max(a, lo), min(b, hi)]. If that interval is empty, every input would be refused, and the graph is refused at load (model_mismatch).
Sizes. A reduction whose length depends on the picture uses the largest length the graph’s declared input sizes allow, with shapes planned on those sizes. Every shape rule of §5 is non-decreasing in its input sizes. This covers gram (L, the largest H·W of its inputs), l2norm (L, the largest H·W of its input) and softmax (W_max, the largest W of its input). A graph that contains one of these three operators MUST declare its input sizes (§10.2).
8.2 Term and partial-sum intervals
For a weight w and an input interval [l, h], the term interval is [min(w·l, w·h), max(w·l, w·h)]. For a product of two run-time values with intervals [l1, h1] and [l2, h2], it is the corner hull [min(C), max(C)] of the four products C = {l1·l2, l1·h2, h1·l2, h1·h2}.
For a set K of terms, with a bias b (0 when there is none), the partial-sum interval is:
A(K, b) = [ min(0, b) + Σ_{k∈K} min(0, lo_k), max(0, b) + Σ_{k∈K} max(0, hi_k) ]
It contains every sum of every subset of K, with or without b. So it bounds every partial sum in every order and grouping (§6), the final value, and the final value at a position where padding removes taps.
8.3 Operator bound rules
The interval of each producer result and named intermediate is as follows. [l, h] is the input tensor’s interval; X = max(|l|, |h|). A rule that names a channel is applied per channel, and §8.1 hulls the results.
| operator | rule |
|---|---|
conv | For output channel o, let K_o be its cg·kh·kw taps, each with its term interval. conv.term is the hull of the term intervals of K_o. conv.acc = A(K_o, b_pre[o]), where b_pre[o] is the pre bias or 0. rq.t is conv.acc. Then the rq rules. |
conv_transpose | For output channel o, convT.term is the hull of the term intervals of all cin·kh·kw taps of o. For each residue ρ = (ρy, ρx) ∈ [0, sh) × [0, sw), K_ρ is the taps with i mod sh = ρy and j mod sw = ρx. convT.acc is the hull over ρ of A(K_ρ, b_pre[o]); one output position reads the taps of one residue only. rq.t is convT.acc. Then the rq rules. |
rq.p | Let [t_lo, t_hi] be rq.t and m the channel’s multiplier (1 when absent). Let P = [min(t_lo·m, t_hi·m), max(t_lo·m, t_hi·m)]. Let a = 2^(s−1) when round is half_up or half_even and s ≥ 1, and a = 0 otherwise. Then rq.p = [P_lo, P_hi + a]. |
rq.q | rq.q = [R(P_lo, s), R(P_hi, s)], with P from the rq.p rule, without the addend a, and R the channel’s rounding (§2). R is non-decreasing, so this holds every q. |
rq.y | rq.y = [q_lo + b, q_hi + b], where b is the channel’s post bias, or 0. The producer result is rq.y. |
rescale | rq.t = [l + b_pre[c], h + b_pre[c]]. Then the rq rules. |
chscale | For channel c: rq.t = [l, h]; rq.p is the hull, over the rows r that the scalar’s range allows, of the rq.p rule with m = M[r][c]; rq.q is likewise the hull of the rq.q rule. rq.y = rq.q. |
mul | mul.p is the corner hull of a’s and b’s intervals. rq.t = mul.p. Then the rq rules. |
gram | gram.term is the corner hull of A’s and B’s intervals. gram.acc = [L·min(0, term_lo), L·max(0, term_hi)]. rq.t = gram.acc. Then the rq rules, with m = 1. |
apply | apply.term is the corner hull of A’s and V’s intervals. apply.acc = [d·min(0, term_lo), d·max(0, term_hi)]. rq.t = apply.acc. Then the rq rules, with m = 1. |
add | [la + lb, ha + hb] |
chunk_add | [2l, 2h] |
clamp, relu, abs | the image of the interval under the map: [clamp(l, lo, hi), clamp(h, lo, hi)]; [max(l, 0), max(h, 0)]; ` |
leaky | If l < 0: leaky.p is the corner hull of [l, min(h, −1)] and [a, a], and the negative part is [⌊p_lo / 2^f⌋, ⌊p_hi / 2^f⌋]. If l ≥ 0: leaky.p = [0, 0] and there is no negative part. The non-negative part is [max(l, 0), h] when h ≥ 0. The producer result is the hull of the parts present. |
lut16 | [min, max] of T[u + 32768] over u ∈ [l, h] |
igdn | igdn.x2 is [0, max(l², h²)] if l ≤ 0 ≤ h, and [min(l², h²), max(l², h²)] otherwise. For output channel i and input channel j: gx2_ij is the corner hull of [γ[i][j], γ[i][j]] and igdn.x2; term_ij = [⌊gx2_lo / 2^gf⌋, ⌊gx2_hi / 2^gf⌋]. igdn.gx2 and igdn.term are the hulls of these. igdn.N = A({term_ij : j < C}, β_i). The final norm is Nf_i = [β_i + Σ_j term_ij,lo, β_i + Σ_j term_ij,hi], each sum a left fold over j from β_i; it is not recorded, because igdn.N contains it. igdn.r = [isqrt(max(0, Nf_lo)), isqrt(max(0, Nf_hi))], from the final norm, not from igdn.N. igdn.xr is the corner hull of [l, h] and igdn.r. The producer result is [⌊xr_lo / 2^frac⌋, ⌊xr_hi / 2^frac⌋]. |
layernorm_c | ln.S = [C·min(0, l), C·max(0, h)]. ln.m = [C·l − ⌊C/2⌋, C·h + ⌊C/2⌋]. The mean lies in [l, h], so ln.d = [l − h, h − l]. ln.T = [0, C·(h − l)²]. ln.v = [ε, (h − l)² + ε]. rsq.num = [2^32, 2^32]. ln.k = [0, ⌊2^32 / r0⌋], with r0 = isqrt(ε). ln.dk = [−D, D] with D = min((h − l)·⌊2^32/r0⌋, ⌈isqrt_ceil(C)·2^32·(r0 + 1) / r0⌉). ln.n = [−N, N] with N = min(⌊(h − l)·⌊2^32/r0⌋ / 2^16⌋ + 1, N_max) and N_max = ⌊isqrt_ceil(C)·2^16·(r0 + 1) / r0⌋ + 1 (lemma L1). For channel c, ln.ng is the corner hull of ln.n and [γ[c], γ[c]], and the producer result is [⌊ng_lo / 2^16⌋ + β[c], ⌊ng_hi / 2^16⌋ + β[c]]. |
l2norm | l2.T = [0, L·X² + ε]. rsq.num = [2^32, 2^32]. l2.k = [0, ⌊2^32 / isqrt(ε)⌋]. l2.vk = [−2^32, 2^32], and the producer result is [−2^16, 2^16] (lemma L2). |
exp_neg_q16 | Given the interval [u_lo, u_hi] of u = −x (each operator below gives it): exp.negx = [u_lo, u_hi]; exp.nl = [45426·⌊u_lo/45426⌋, 45426·⌊u_hi/45426⌋]; exp.t2 = [43245², 88670²]; exp.c = [0, 23495·⌊88670²/2^16⌋]. The result lies in [0, 65553]. These four are recorded for every softmax, sigmoid and elu, whatever their input. |
softmax | sm.diff = [l − h, 0]. u = [0, h − l], then the exp_neg_q16 rule. sm.sum = [0, W_max·65553]. sm.num = [0, 65553·2^16 + ⌊W_max·65553 / 2⌋]. The producer result is [0, 2^16]. |
sigmoid | u is the image of [l, h] under ` |
elu | u = [max(0, −h), max(0, −l)], then the exp_neg_q16 rule. elu.p is the corner hull of [α, α] and [−2^16, 17]. The producer result is the hull of [max(l, 1), h] (when h > 0) and [⌊elu.p_lo / 2^16⌋, ⌊elu.p_hi / 2^16⌋] (when l ≤ 0). |
| movement | The input’s interval, for each output; crop_to’s is its first input’s. concat and subpos_merge take the hull of their inputs’ intervals. fill takes [v, v] for a constant v, or its scalar’s range. |
Lemma L1 (normalised output). Let ε ≥ 1 and r0 = isqrt(ε). Let d_c be the deviations, T = Σ d_c², V = ⌊T/C⌋, r = isqrt(V + ε) and k = ⌊2^32 / r⌋.
d_c² ≤ T < C·(V + 1), and(r + 1)² > V + ε ≥ V + 1.- So
|d_c| < √C·(r + 1). - So
|d_c·k| < √C·2^32·(1 + 1/r) ≤ isqrt_ceil(C)·2^32·(r0 + 1)/r0. - Hence
|n_c| ≤ N_max.
Lemma L2 (L2-normalised output). v² ≤ T < T + ε < (r + 1)², so |v| ≤ r. Then |v·k| ≤ r·⌊2^32/r⌋ ≤ 2^32.
8.4 Admissible ranges
The admissible range of a tensor τ for a profile P is computed as follows.
- Scope. The consumers of
τare the operators that readτdirectly. Operators further downstream do not count. - The condition. For
X ∈ [0, 2^63],C(X)holds when, withτ’s interval replaced by[−X, X]wherever a consumer reads it, and every other tensor’s interval as the pass computed it, every value that a consumer classes meetsP’s width for its class. Those values are the consumer’s named intermediates and its producer results (§7.1), and none of them may be saturated (§8.1). Parameter conditions (§7.2) and policies are not part ofC. - Search. Every rule of §8.3 is monotone under inclusion, so
C(X)impliesC(X′)for everyX′ ≤ X.X*is the largestX ∈ [0, 2^63]for whichC(X)holds, found by binary search on that interval. The admissible range is[−X*, min(X*, 2^63 − 1)], so it always fits i64. - No admissible range. If
C(0)fails,τhas no admissible range forP: no value ofτlets its consumers meetP. A lowering or exporter that needs one refuses the set (model_mismatch).
A consumer whose rule does not depend on τ’s interval, such as fill reading only a shape, never limits X*. An operator whose fixed intervals fail P (§7.3) gives no admissible range for that profile.
A lowering (§13) or an exporter uses admissible ranges to choose check or clamp ranges. The lowerings of §13.2 and §13.3 use their own predicates instead, which the forms they lower fix. Those ranges lie within the admissible ranges for acc64.
8.5 The certificate
A bounds certificate records the bounds pass for one parameter set’s graphs. It is canonical JSON:
- UTF-8 with no byte-order mark, and no whitespace outside strings. The file does not end with a newline.
- Every string holds printable ASCII (U+0020–U+007E) only, with
"written\"and\written\\. No other escape occurs. A role or capability name outside that alphabet cannot be certified. - Object members are in ascending order of their keys’ bytes. Every key is ASCII.
- Every integer is a JSON string of decimal digits: no leading zero, a
-only before a nonzero value, and zero written"0". - Booleans are
trueandfalse. An absent value isnull. - Digests are 64 lowercase hexadecimal digits.
- Arrays are in the order this section gives.
The members.
certificate = {
"capability": the capability string, or null;
"certificate": "wai-ops/1 bounds";
"graphs": [ graph … ], in the order of the lowering's roles (§13.0), or file order for "whg1";
"lowering": the §13 identifier, or "whg1";
"set_sha256": the digest a pin over the set carries (SPEC §3.1): for a one-file set, the SHA-256 of the
file; for a set of several files, the SHA-256 of its wai.prior.set listing; null when the
graphs were certified on their own;
"verdict": verdict
}
graph = {
"graph_sha256": the SHA-256 of the graph's WHG1 bytes, as lowered (§13.0) when lowered;
"inputs": [ { "hi", "init" (state only), "kind": "data" | "state", "lo", "max_h", "max_w",
"policy": "check" | "clamp", "src" (state only), "tensor" } … ], in input-table order;
"name": the graph's role;
"nodes": [ node … ], in graph order;
"scalars": [ { "hi", "index", "lo" } … ], in scalar-table order
}
node = {
"acc32_params": [ key … ]: the parameter conditions of §7.2 the node fails, in that table's order;
"fits": [ profile … ]: the profiles the node meets, in ascending order ("acc32", "acc64");
"index": the operator's index;
"intermediates": { key: { "bits", "hi", "lo" } … }: exactly the keys of Table 8.5-A for the operator;
"k_c": K_c (§6) as an integer, or "unbounded" when P = 0; present for conv, conv_transpose,
igdn, gram and apply only, over conv.term, convT.term, igdn.term, gram.term and apply.term;
"mac": { "a_bits": [ bits of each input tensor's interval, in input order ],
"w_bits": bits of [min, max] over the weights (conv, conv_transpose) or M (chscale) };
present for conv, conv_transpose, chscale, mul, gram and apply only; "w_bits" for the
first three only;
"npu_flags": [ flag … ]: the node's flags of §9.2, in ascending order;
"op": the operator's name (§5);
"out": [ { "bits", "hi", "lo", "policy": "derived" | "clamp" | "check",
"result": { "bits", "hi", "lo" }, "tensor" } … ], in output order.
"lo" and "hi" are the tensor's interval after its policy; "result" is the producer result;
"saturated": true or false (§8.1)
}
verdict = {
"acc32": true or false; "acc32_first_failure": failure, present when "acc32" is false;
"acc64": true or false; "acc64_first_failure": failure, present when "acc64" is false;
"npu": true or false; "npu_flags": [ flag … ]; "npu_reasons": [ reason … ]
}
failure = { "bits" (interval keys only), "graph": the role, "index", "key", "where": "input" | "scalar" | "node" }
bits is bits([lo, hi]) (§2).
Table 8.5-A. The intermediates each operator records.
| operator | keys |
|---|---|
conv | conv.term, conv.acc, rq.t, rq.p, rq.q, rq.y |
conv_transpose | convT.term, convT.acc, rq.t, rq.p, rq.q, rq.y |
rescale, chscale | rq.t, rq.p, rq.q, rq.y |
mul | mul.p, rq.t, rq.p, rq.q, rq.y |
gram | gram.term, gram.acc, rq.t, rq.p, rq.q, rq.y |
apply | apply.term, apply.acc, rq.t, rq.p, rq.q, rq.y |
leaky | leaky.p |
igdn | igdn.x2, igdn.gx2, igdn.term, igdn.N, igdn.r, igdn.xr |
layernorm_c | ln.S, ln.m, ln.d, ln.T, ln.v, rsq.num, ln.k, ln.dk, ln.n, ln.ng |
l2norm | l2.T, rsq.num, l2.k, l2.vk |
softmax | sm.diff, exp.negx, exp.nl, exp.t2, exp.c, sm.sum, sm.num |
sigmoid | exp.negx, exp.nl, exp.t2, exp.c, sig.num |
elu | exp.negx, exp.nl, exp.t2, exp.c, elu.p |
| every other operator | none: the object is {} |
The table lists keys in definition order; the JSON writes them in ascending byte order.
What a node fits. A node fits profile P when it is not saturated, every recorded intermediate and producer result meets P’s width for its class (§7.1), and, for acc32, its acc32_params is empty and none of its output tensors has the policy check.
The verdict. acc64 (or acc32) is true when every graph’s inputs and scalars have declared ranges that meet the profile’s tensor width, and every node of every graph fits the profile.
First failure. When a profile fails, its first failure is found by visiting the graphs in order and, within each, the inputs in table order, then the scalars in table order, then the nodes in graph order. The first input, scalar or node that does not meet the profile is the failure. where says which kind it is, and index is its position in the input table or the scalar table, or the node’s index. Its key is:
- for an input or scalar,
"range", withbitsof its declared range; - for a saturated node,
"saturated", with nobits; - otherwise, among the node’s intermediates and producer results that fail the width, the one with the most
bits, a tie going to the smallest key in byte order. A producer result’s key is"out.<k>", for outputk. The failure carries itsbits; - otherwise (
acc32only), the first entry ofacc32_params, or, failing that,"policy.check", with nobits.
The accelerator verdict. npu_reasons is the set of reasons the graphs are outside wai-ops/1-npu (§9.2), deduplicated, in ascending order, from this closed vocabulary:
"acc32": theacc32verdict is false;"op:<name>": an operator marked no in §5’s table, for example"op:igdn";"conv_groups": aconvwhosegroupsis neither 1 nor itscin, or whosecoutdiffers fromcinwhengroupsiscinand above 1;"crop_scalar": acropwith a scalar operand.
npu is true when npu_reasons is empty. npu_flags is the union of every node’s npu_flags, deduplicated, in ascending order, whatever npu is.
The certificate’s identity is the SHA-256 of its bytes. Two conforming certifiers produce identical bytes for one set.
8.6 What a sink checks
Before a sink decodes through a parameter set, it MUST establish the set’s verdict by the rules of §8.1–§8.5. It need not write the certificate. It MAY cache the verdict by the set’s digest.
- A set whose verdict does not meet its capability’s declared profile (§11) is refused (
model_mismatch). - So is a set whose pin claims a profile that its verdict does not meet.
9. Subsets
9.1 wai-ops/1 and wai-ops/1-i32
wai-ops/1 is the full set of §5 and §5E.
wai-ops/1-i32 is wai-ops/1 without layernorm_c, l2norm, softmax, sigmoid and elu: the operators marked yes in §5’s wai-ops/1-i32 column. A graph that meets acc32 uses only these (§7.3). So an operator implementation for a 32-bit integer fabric, such as a shading language, a DSP or an integer accelerator programmed directly, implements wai-ops/1-i32, and it can run every graph that meets acc32. Stage operators stay at their own widths (§7.4).
9.2 wai-ops/1-npu
A graph is in the accelerator subset when all of the following hold:
- its verdict meets
acc32, which already excludes acheckpolicy inside the graph; - it uses only operators marked yes or flagged in §5’s
wai-ops/1-npucolumn; - every
convhasgroups = 1, or is depthwise (groups = cin = cout); - every
crophas constant operands. A crop with a scalar operand has a shape that depends on a value.
The certificate records why a graph is outside the subset (npu_reasons, §8.5). Membership is decidable from the certificate alone.
Flags. A flag does not change membership. It tells a toolchain which feature a node uses that a fixed-function unit may lack, so that it knows where to look, and which rewrite of §9.4 may apply. Each node records its flags:
| flag | when |
|---|---|
lut16 | a lut16: a 65,536-entry table |
chscale | a chscale: a multiplier row chosen at run time |
state_add | an add or chunk_add with an operand that is a graph input of kind state (§10.2), that is, a value carried across decodes |
rq_half_even | an rq or chscale whose rounding is half_even |
rq_floor_mult | an rq or chscale whose rounding is floor and which has a multiplier |
rq_post_bias | an rq with a post bias |
rq_neg_mult | an rq or chscale with a negative multiplier |
rq_shift_range | an rq or chscale with a shift outside 0..=31 |
A node also records, in mac, the exact bit width of every multiply operand: each input tensor’s interval (a_bits) and the weights’ (w_bits). An integer multiply-accumulate array that takes 8-bit or 16-bit operands needs those widths, not a flag.
Excluded operators, and why.
| excluded | reason |
|---|---|
igdn, layernorm_c, l2norm, softmax | Each normalises by a statistic computed at run time. That needs a square root or a division, and a reduction across channels or positions. |
sigmoid, elu | Each needs a division, or a polynomial followed by a data-dependent shift. Where the input fits i16, an exporter replaces it with lut16. |
gram, apply | Each multiplies two run-time tensors and reduces over a length that depends on the picture. |
a crop with a scalar operand | Its output shape depends on a value. |
a check policy inside a graph | An accelerator cannot refuse partway through a graph. §7.2 already excludes it from acc32. |
| every stage operator | They run in the driver (§7.4). |
Outside wai-ops/1 altogether. These are absent from both sets, so a capability that needs one cannot be expressed in wai-ops/1:
- a learned slope on negative values per channel or per element;
- sampling at data-dependent positions;
- resampling by a non-integer factor;
- dilation;
- shapes that depend on values, other than a
cropfrom scalars.
Membership is not a claim of exactness. It says that a graph uses only what integer accelerators are built to compute. Whether an accelerator computes it exactly is shown only by the vectors of §14, run on that accelerator (§12).
9.3 Transposed convolution as phase convolutions (informative)
A target without a transposed convolution computes one as sh·sw phase convolutions. For residue ρ = (ρy, ρx):
Z_ρ[o][u][v] = Σ_{ci} Σ_{t, t′} Wt[ci][o][ρy + sh·t][ρx + sw·t′] · X̃[ci][u − t][v − t′]
out[o][y][x] = rq_o( Z_ρ[o][⌊(y + ph)/sh⌋][⌊(x + pw)/sw⌋] ) with ρ = ((y + ph) mod sh, (x + pw) mod sw)
Here X̃ is zero outside the input. Each phase is a stride-1 convolution with a flipped sub-kernel of at most ⌈kh/sh⌉ × ⌈kw/sw⌉ taps. Missing taps are zero weights, which add zero terms.
- The phases are interleaved by
pixel_shuffle(whensh = sw) and cropped byphandpw. - The integers are those of §5.3, because the products are the same and sums are exact (§4).
- Each phase’s partial sums lie in
A(K_ρ, b), the interval §8.3 already certifies. - The vectors of §14.1 include phase-form cases.
9.4 Mapping onto a fixed-function unit (informative)
These rewrites are exact: each gives the same integers as the operator it replaces. A toolchain uses them where a flag of §9.2 marks the feature.
floortohalf_up, with no multiplier. Fors ≥ 1,R_floor(t + b, s) = R_half_up(t + b − 2^(s−1), s). So afloorrequantisation with no multiplier andprebiasbis ahalf_upone withprebiasb − 2^(s−1). With a multiplier there is no such single-bias form in general, hencerq_floor_mult.- A negative multiplier. An
rqwhose channelohasm[o] < 0equals one with−m[o], channelo’s weights negated and itsprebias negated. Itspostbias is unchanged. - A shift outside a unit’s range.
R(t·m, s) = R(t·(m·2^k), s + k)for everyk ≥ 0and every rounding of §2. A shift below the range rises into it whilem·2^kstill fits the unit’s multiplier. A shift above it needsmto be a multiple of2^k. - A
postbias.R(t·m, s) + b = R(t·m + b·2^s, s)forfloorandhalf_up, and forhalf_evenwhenbis even (an oddbchanges the parity that breaks a tie). When the identity holds andmdividesb·2^s, thepostbias is aprebias ofb·2^s / m. - Rounding ties away from zero. A unit that rounds ties away from zero agrees with
half_upon non-negative products only. Anrqwhoserq.pinterval is non-negative maps exactly, and others do not. - Depth-to-space order.
pixel_shuffletakes channelc·r² + i·r + jto output channelc(§5.10). A target whose depth-to-space reads channel(i·r + j)·C′ + cinstead needs that permutation folded into the output channels of the operator that produces the input. - Replicate padding. Not every target pads by replication.
pad_repcan be built fromcropandconcatof edge rows and columns. - Storage limits. A tensor of
2^26elements at 4 bytes each is 256 MiB, more than some fabrics allow in one storage binding (128 MiB is a common default). An implementation tiles such a tensor across bindings (§6).
10. WHG1: the graph form
A WHG1 file holds one graph. A parameter set that carries graphs names each one by role, in its capability’s registration. Every multi-byte field is little-endian, and every reserved field is zero.
10.1 Header (32 bytes)
| off | size | field | rule |
|---|---|---|---|
| 0 | 4 | magic WHG1 | |
| 4 | 1 | version | 1 |
| 5 | 1 | operator set | 1 (wai-ops/1) |
| 6 | 2 | reserved | 0 |
| 8 | 4 | n_tensors | ≥ 1 |
| 12 | 4 | n_ops | ≥ 1 |
| 16 | 2 | n_inputs | ≥ 1 |
| 18 | 2 | n_outputs | ≥ 1 |
| 20 | 2 | n_scalars | |
| 22 | 2 | reserved | 0 |
| 24 | 4 | max_elems | 1..=2^26: the cap on every tensor of this graph |
| 28 | 4 | reserved | 0 |
10.2 Tables
The tables follow the header in this order.
- Scalars:
n_scalarsrecords of 16 bytes:i64 lo,i64 hi, withlo ≤ hi.- The driver supplies each scalar’s value at run time. A value outside its range is refused (
field_range).
- The driver supplies each scalar’s value at run time. A value outside its range is refused (
- Inputs:
n_inputsrecords of 32 bytes:u32 tensor;u32 max_h,u32 max_w: the largest size the driver may supply. Either both are at least 1, or both are 0, which means the size is set per decode and bounded only by the caps (§3). Either every input declares its sizes or none does;u8 kind: 0 for data, 1 for state; then 3 bytes reserved;u32 src: for a state input, the position in the outputs table of the output that feeds it; 0 for a data input;u32reserved;i64 init: for a state input, the value of every element at the first decode of a sequence; 0 for a data input.- The driver supplies each data input. Its
HandWare at mostmax_handmax_wwhen they are declared, otherwisetoo_large.
- Outputs:
n_outputsrecords of 4 bytes:u32 tensor. - Tensors:
n_tensorsrecords of 24 bytes:u32 channels(≥ 1);i8 exponent, in −127..=127, or −128 for none;u8 policy: 0 forderived, 1 forclamp, 2 forcheck;u16reserved;i64 lo,i64 hi. Withderived, both are 0. Otherwiselo ≤ hi.
- Operators:
n_opsrecords, in evaluation order. Each record is:u8 opcode,u8 n_in,u8 n_out,u8reserved;u32 in[n_in],u32 out[n_out];u32 plen, thenplenbytes of parameters (§10.4).
Nothing follows the last operator.
10.3 Graph rules
- Every tensor is a graph input or the output of exactly one operator.
- An operator reads only graph inputs and the outputs of earlier operators.
- No graph input is an operator’s output.
- Every graph output is defined.
- A graph input’s policy is
checkorclamp. - Input 0 is of kind data.
n_inandn_outare the operator’s: §5’s table gives them,concathasn_in ≥ 2, and thesubpos_*operators have 4 on their four-tensor side.- Each operator’s output channel count, as §5 derives it, equals its output tensor’s
channels. - State. A state input’s
srcis less thann_outputs. That output’s tensor has the state input’s channel count, andinitlies within the state input’s declared range.
Carrying state. A sequence is a run of decodes that the capability’s driver defines, such as the frames of one clip. A decode completes when it is not refused (§4). Until a decode of the sequence completes, each state input is the tensor [channels, H, W] whose every element is init, where H and W are input 0’s at the decode being made. After that, it is the tensor that output src held at the end of the sequence’s last completed decode, after that tensor’s policy. A refused decode changes no state: the decode after it reads what it would have read had the refused decode not been made. The state input’s own policy then applies to it, as to any input (§4, run step 4). The run-time plan (§4) checks its shape against every operator that reads it.
10.4 Parameter layouts
The requantisation block RQ
u8 round 0 floor, 1 half_up, 2 half_even
u8 bias 0 none, 1 pre, 2 post
u8 m_kind 0 absent, 1 one value, 2 one per channel
u8 s_kind 1 one value, 2 one per channel
u8 m_bytes 4 or 8 (0 when m_kind = 0)
u8 b_bytes 4 or 8 (0 when bias = 0)
u16 reserved
i<m_bytes> mult[0, 1 or C]
u8 shift[1 or C] each ≤ 127
i<b_bytes> bias[C] when bias ≠ 0
Each operator’s parameters
| operator | parameters |
|---|---|
conv | u32 cout, kh, kw, sh, sw, ph, pw, groups; u8 w_bytes (1, 2, 4 or 8), 3 reserved; RQ; i<w_bytes> Wt[cout][cin/groups][kh][kw] |
conv_transpose | u32 cout, kh, kw, sh, sw, ph, pw, oph, opw; u8 w_bytes, 3 reserved; RQ; i<w_bytes> Wt[cin][cout][kh][kw] |
rescale | RQ |
chscale | u32 scalar, n_rows; u8 round (as in RQ), u8 m_bytes (4 or 8), u8 shift (at most 127), 1 reserved; i<m_bytes> M[n_rows][C] |
mul | RQ, with bias = 0 |
lut16 | i8 e_in, i8 e_out (each −128 for none), 2 reserved; i16 T[65536] |
add, concat, chunk_add, relu, abs, crop_to, softmax, sigmoid | none (plen = 0) |
split | u32 start, count |
pixel_shuffle, pixel_unshuffle | u32 r |
clamp | i64 lo, hi |
leaky | i64 a; u32 f |
pad_rep | u32 m |
crop | four operands y0, x0, h, w, each u8 kind (0 constant, 1 scalar), 3 reserved, u32 value-or-index |
subpos_split, subpos_merge | u8 order[4], each 2a + b |
fill | u8 kind (0 constant, 1 scalar), 7 reserved, i64 value (the index when kind = 1) |
igdn | u32 gf, frac; u8 g_bytes (4 or 8), 7 reserved; i<g_bytes> γ[C][C]; i64 β[C] |
layernorm_c | u64 ε; u8 g_bytes (4 or 8), 7 reserved; i<g_bytes> γ[C]; i64 β[C] |
l2norm | u64 ε |
elu | i64 α |
gram, apply | u32 groups; RQ, with m_kind = 0, bias = 0 and s_kind = 1 |
cin and C are the input tensor’s channel count. Every parameter length is exact.
10.5 Load checks
A graph is refused (model_mismatch) at the first of these that fails:
- the magic, version and operator set;
- the reserved fields;
- the counts against the file’s length;
- each table’s records (§10.2);
- each operator’s opcode, its arity, its parameter length and the ranges of its parameters (§5, §10.4);
- the graph rules, state rules included (§10.3), and the exponent rules (§3, §5.6);
- the plan. Every operator’s channel counts. When the inputs declare their sizes, every shape, planned on
max_handmax_w, where a size past a cap istoo_large. A graph with agram,l2normorsoftmaxwhose inputs do not declare their sizes is refused; - the bounds pass (§8), with
lut16’s range check (§5.6), and the capability’s profile against the verdict (§8.6).
The graph’s identity is the SHA-256 of its bytes.
11. Declaring a profile
In the registry. Each capability decoded through wai-ops/1 carries an ops member in registry/capabilities.json:
"ops": {
"set": "wai-ops/1",
"profile": "acc64",
"subset": "full",
"lowering": "whm1/1",
"stage": { "rans64": { "table": "t4097", "escape": "lenient", "end": "canonical" },
"bucket": "ceil" }
}
profileis the profile that every conforming parameter set of the capability meets. A set whose verdict does not meet it is refused. A set MAY meet a stronger profile; its certificate shows that.subsetisfullornpu. A capability registerednpurefuses a set that is not in the subset (§9.2).loweringnames the §13 lowering, orwhg1when the set carries graphs.stagefixes the modes of the stage operators the driver uses (§5E).
Registering a capability’s ops member, or changing it, follows SPEC §8. A stronger profile or subset narrows the sets a capability accepts, so it is a breaking change to that capability.
In the pin. A pin MAY carry ops_profile, the profile the sender claims for the pinned set: "acc32" or "acc64".
- It lets a sink choose a fabric before it fetches the set.
- A sink that resolves the set and finds a weaker verdict refuses it (
model_mismatch). - Without the member, a sink learns the profile from the set.
In the sink.
- A sink runs a set’s graphs only through an operator implementation that conforms to a profile the set’s verdict meets. An
acc32set may run on anacc32or anacc64implementation. Anacc64set runs only on anacc64implementation. - A sink that moves a decode from one fabric to another reports where it ran, as determinism-tiers requires. A move between conforming implementations does not change the output.
12. Relation to the determinism tiers
decode-equivalence. Take a capability whose decode is made only of wai-ops/1 graphs and stage operators. Its output is the same integers on every fabric whose operator implementation conforms (§1) to a profile its set meets, because every operator is an exact integer function (§4) and the certificate excludes overflow (§8). A NeuralIntegerExact capability registered with an ops member backs intent = replicate on those fabrics (SPEC §7).
entropy-consistency. Take a NeuralEntropyExact capability. Its stage operators, and every graph whose output selects a distribution or decides which positions are coded (its scale path), MUST be wai-ops/1, run on a conforming implementation. Its other graphs MAY run on an implementation that does not conform, or in floating point. Their output is then outside the claim.
Evidence for a fabric. Conformance belongs to an implementation on a fabric, and it is shown by the vectors of §14 on that fabric.
- Being in a subset (§9) is not evidence of exactness.
- An accelerator whose implementation has not passed the vectors gives no
decode-equivalenceclaim, whatever operators it can map. - A receipt for a decode through
wai-ops/1names the profile and the fabric in the execution fields of determinism-tiers.
13. Lowerings of the registered forms
A lowering maps a parameter set in an existing form to WHG1 graphs, deterministically and canonically, without changing a byte of the set. Pins, payloads and outputs therefore stay as they are. A sink MAY decode the existing form directly, provided it computes the integers of the lowered graphs and refuses what they refuse. A lowered graph’s certificate is computed on the lowered bytes, and records their digest.
13.0 The canonical form, and the driver contract
A lowering writes exactly one byte sequence for a set. These rules fix it, and §13.1–§13.6 fix each lowering’s operators.
- Header. Version 1, operator set 1,
max_elems = 2^26, and the counts of what is written. - Scalars, inputs and outputs are those the lowering lists, in its order. Every lowered input is of kind data. Its policy and range are the lowering’s, and its
max_handmax_ware the driver contract’s (below). - Tensor numbering. Input
iis tensori. The operators’ output tensors follow, in operator order, and each operator’s outputs in their own order take the next unused indices. - Tensor records.
channelsas planned. Exponent −128 (none). Policyderived, withlo = hi = 0, except where the lowering givescheckand its range. - Operators are written in the lowering’s order, with every reserved byte 0. A parameter that the source form gives once per channel is written per channel (kind 2), even where its values are equal. One that the source form gives once is written once (kind 1).
- Widths. Weights, multipliers, biases and
γtake the width the source form fixes. Where the source form is JSON and fixes none, each takes the smallest width its field allows that holds every value of that field in that operator: 1, 2, 4 or 8 bytes for weights, and 4 or 8 for multipliers, biases andγ. A constant that the lowering itself writes, such as the multiplier 255, takes the smallest allowed width that holds it.
| source | w_bytes | m_bytes | b_bytes | g_bytes |
|---|---|---|---|---|
int_synth.json | 4 | — | 4 | — |
WHM1, WAM1 | 2 | 8 | 8 | 4 |
a wai.image.jpegai bundle (JSON) | smallest | — | smallest | — |
Roles and order. A lowered set’s graphs, by role, in this order:
| capability | lowering | graphs, in order | output step inside the graph |
|---|---|---|---|
wai.neural.int_synth | int_synth/1 | synth | clamp(0, 255) |
wai.neural.int_hyper; the keyframe of wai.video.int_hyper | whm1/1 | h_s, g_s | in g_s, the RGB8 step |
wai.audio.int_codec | wam1/1 | g_s | the PCM step (§13.3) |
wai.splat.int_codec | whm1/1 | g_s | the RGB8 step |
wai.neural.int_mlicpp | wmb1/1 | h_s, entropy, g_s | in g_s, the RGB8 step |
wai.world.model (informative) | world/1 | T, g_s | in g_s, the RGB8 step |
wai.image.jpegai | jpegai-int/1 | hs_y, hs_uv | the per-channel gain |
The RGB8 step is two operators after the network’s last: clamp(0, 2^afrac), then rescale with one multiplier 255 (m_bytes 4), one shift afrac, no bias and half_up. Everything else a driver does with an output, such as splitting h_s’s output into σ and μ, or laying values out as bytes, is a driver step and not an operator.
The driver contract. The declared inputs and scalars of every lowered graph:
| graph | input 0 | max_h, max_w | scalars |
|---|---|---|---|
synth | the latents [C, S, S], check [−128, 127] | 0, 0 | none |
a whm1/1 or wam1/1 graph | x_0, the network’s input, with the policy and range of §13.2 | 0, 0 | wam1/1 only: scalar 0, t_out, [1, 2^26] |
T (world/1) | z, check [−2^63, 2^63 − 1] | 0, 0 | scalars 0 … A − 1, the plane values, each [−2^63, 2^63 − 1] |
hs_y, hs_uv | z [Cz, 4, 4], check [−31, 32] | 4, 4 | none |
Sizes of 0 mean that the input’s size is set per decode (§10.2). A driver plans these graphs on the payload’s sizes, at the point its payload extension plans its networks (§4).
13.1 The int_synth model specification (int_synth/1)
The set. One file, int_synth.json (integer-payloads §13). It has a layers list. Each layer has in_ch, out_ch, k, stride, pad, out_pad (0 when absent), weight as [in_ch][out_ch][k][k] 32-bit integers, bias as [out_ch] 32-bit integers, shift (0 when absent) and relu (false when absent).
The graph. Input 0 is the latents (§13.0). For each layer, in order:
conv_transposewithcout = out_ch,kh = kw = k,sh = sw = stride,ph = pw = pad,oph = opw = out_pad, the weights as stored, and anRQwithroundfloor, aprebias, no multiplier and one shift (the layer’s);- then
relu, ifreluis set.
Then clamp(0, 255). The graph’s one output is the clamp’s.
Load checks. These are in addition to the payload’s own checks, and every refusal is payload_malformed:
- the layers chain: the first
in_chisC, and each laterin_chis the previousout_ch; - the parameter lengths are exact;
k ≥ 1,stride ≥ 1andshift ≤ 127;- the caps (§3);
- the certificate meets
acc64.
Driver. The output, which MUST be [3, H, W] with the payload’s H and W, as row-major HWC bytes.
13.2 WHM1 networks (whm1/1)
The header. WHM1 (integer-payloads §9) carries afrac, gf and shift, then the layers. Layer ℓ reads x_ℓ and writes x_{ℓ+1}. The network’s input is x_0. Input 0 is tensor x_0, and layer ℓ’s operator writes tensor ℓ + 1, so tensor ℓ is x_ℓ. x_0’s channel count is the input channel count that the first layer of tag 0, 1 or 2 declares. A network with no such layer cannot be lowered (model_mismatch).
| tag | lowered to |
|---|---|
| 0 | conv_transpose: kh = kw = k, sh = sw = stride, ph = pw = pad, oph = opw = opad, weights i16 [c][out][k][k]; RQ: one multiplier per channel (i64), one shift (the header’s), a post bias (i64), floor |
| 1 | conv: groups = 1, kh = kw = k, sh = sw = stride, ph = pw = pad, weights i16 [out][c][k][k]; the same RQ |
| 2 | igdn: gf = the header’s gf, frac = the header’s afrac; γ i32 [c][c], β i64 [c] |
| 3 | relu |
| 4 | leaky: a = slope_q, f = slope_frac |
Range checks. Each x_ℓ that is read by a layer of tag 0, 1, 2 or 4 has the policy check [−X_ℓ, min(X_ℓ, I64)]. That includes the network’s input. A network input that no such layer reads has check [−2^63, I64], since a graph input is never derived (§10.3). Every other tensor is derived: in particular each x_ℓ other than the network’s input that a layer of tag 3 reads, and the last layer’s output.
Let c be layer ℓ’s input channels; Wm, Mm and Bm the largest magnitudes of its weights, multipliers and biases; G and Bb those of its γ and β; I64 = 2^63 − 1 and I128 = 2^127 − 1. Then X_ℓ is the largest X in [0, 2^63] for which P_ℓ(X) holds:
| tag | P_ℓ(X) |
|---|---|
| 0, 1 | c·k·k·Wm·X ≤ I64 and ⌊c·k·k·Wm·X·Mm / 2^shift⌋ + 1 + Bm ≤ I64 |
| 2 | G·X² ≤ I128; N = Bb + c·(⌊G·X²/2^gf⌋ + 1) ≤ I128; isqrt(N) ≤ I64; ⌊X·isqrt(N) / 2^afrac⌋ + 1 ≤ I64 |
| 4 | `X· |
P_ℓis evaluated exactly. Every value it forms is below2^255, becausecandkare below2^32.- Every
P_ℓholds atXwhenever it holds at a largerX, soX_ℓis found exactly by a binary search. - If
P_ℓ(0)fails, the set is refused (model_mismatch). max|x_ℓ| ≤ X_ℓexactly whenP_ℓ(max|x_ℓ|)holds. So these ranges refuse exactly the inputs that integer-payloads §7 Exactness refuses, at the same layer.- Every intermediate that §8.3 gives these layers lies within the bounds
P_ℓchecks. So every set this lowering accepts meetsacc64.
With the RGB8 step (§13.0), two operators follow the last layer, and they write tensors L + 1 and L + 2 for a network of L layers.
Load checks. These are integer-payloads §7, Planning the networks:
shift < 128andgf < 128;afracin1..=30, the rule of every capability this extension lowers (integer-payloads §7 forWIH1,WIA1andWIS1, and integer-payloads §14 forWMB1;world/1in §13.5);slope_frac < 64;- the lengths against the running channel count;
- every size within the caps;
- the certificate meets
acc64.
13.3 WAM1 networks (wam1/1)
The header is that of WHM1. The tensors are [C, 1, T].
- Tag 0 is
conv_transposewithkh = 1,sh = 1,ph = 0,oph = 0,kw = k,sw = stride,pw = padandopw = opad. Its weights arei16 [c][out][k], and it has theRQof §13.2. - Tag 2 is
igdn, as in §13.2.
The range checks are those of §13.2, except that the term count for tag 0 is c·k in place of c·k·k.
The PCM step (wai.audio.int_codec). After the last layer, three operators:
cropwithy0 = 0,x0 = 0andh = 1constant, andwscalar 0 (t_out). Its output tensor has the policycheck [⌈−2^63 / 32767⌉, ⌊(I64 − 2^(afrac−1)) / 32767⌋]. This is the range in whichv·32767 + 2^(afrac−1)fits i64, which is what integer-payloads §5 step 3 refuses outside.rescalewith one multiplier 32767 (m_bytes4), one shiftafrac, no bias andhalf_up.clamp(−32768, 32767).
The bytes are 16-bit little-endian samples.
13.4 WMB1 bundles (wmb1/1)
A WMB1 bundle (integer-payloads §14) holds three WHM1 networks, h_s, entropy and g_s, each lowered by §13.2, g_s with the RGB8 step. Its tables section is the stage operators’ data and is not lowered.
13.5 Drivers
Each capability’s driver is defined by its payload extension. This table names the stage operators, modes, graphs and driver steps each one uses. A row marked informative is not part of what §1 says a reader can build.
| capability | payload | status | stage operators and modes | graphs | driver steps |
|---|---|---|---|---|---|
wai.neural.int_synth | integer-payloads §13 | normative | none | synth | the output’s shape; HWC bytes |
wai.neural.int_hyper, and the keyframe of wai.video.int_hyper | WIH1 (integer-payloads §4), with entropy.json (integer-payloads §9) | normative | rans64_decode (t4097, lenient, canonical); dequantize; bucket (ceil); coding_order (raster) | h_s; g_s | h_s’s output is checked against [2m, sh, sw] (geometry_mismatch); its channels [0, m) are σ and [m, 2m) are μ; g_s gives 3 channels (model_mismatch); HWC bytes |
wai.video.int_motion, and the P-frames of wai.video.int_hyper | WIV1 (integer-payloads §3) | normative | rans64_decode (t4097, lenient, canonical); mc_predict; residual_step with [0, 255] | none | RGB8 frames |
wai.audio.int_codec | WIA1 (integer-payloads §5) | normative | rans64_decode (as above); dequantize | g_s | t_out as scalar 0; the samples as bytes |
wai.splat.int_codec | WIS1 (integer-payloads §6) | normative | as above | g_s | CHW bytes |
wai.neural.int_mlicpp | WIM1 (integer-payloads §14) | normative | rans64_decode (t2, lenient, final_only); dequantize; bucket (ceil); coding_order: for each of the K channel slices, checkerboard(true) then checkerboard(false) over the slice’s channels | h_s; entropy before every pass; g_s | the entropy graph’s input is concat(y, h_s output), which the driver forms with every element of y not yet decoded equal to 0; its output’s channels [0, Cy) are σ and [Cy, 2Cy) are μ; HWC bytes |
wai.{image,video,audio,splat}.int_refine | WIR1 (staged-delivery Appendix A) | normative | rans64_decode (t4097, lenient, canonical), one stream per unit; residual_step with [0, 255] or [−32768, 32767]; up2_predict; rate_predict | none | the unit’s elements |
wai.world.model | none yet | informative | dequantize (the plane values) | T; g_s | below |
world/1 (informative). wai.world.model has no payload text yet. Its integer renderer takes two WHM1 networks, the transition T and the synthesis g_s, an initial latent z_0 [C, lat, lat], and a sequence of action rows.
- Load checks (
model_mismatch). Both networks’afraclie in1..=30and are equal.A, the number of action planes, isT’s input channel count (§13.2) minusC, andA ≥ 1. Every row hasAentries. - The graph
T. Scalars0 … A − 1are the plane values, and input 0 isz(§13.0). The operators are:fillfrom scalara, fora = 0 … A − 1in turn, each readingzand writing a one-channel tensor;concat(z, fill_0, …, fill_{A−1}), writingx_0, withT’s layer-0 range check (§13.2);T’s layers (§13.2);clamp(−2^afrac, 2^afrac), withT’safrac. Its output is the graph’s.
- The graph
g_siswhm1/1with the RGB8 step, which usesg_s’safrac. The load check makes the two equal. - The driver. Frame 0 is
g_s(z_0). For each row in order,zbecomesT(z)with the row’s entries as the scalars, and the next frame isg_s(z). A single-agent set (A = 3) whose rows are moves(dx, dy)has the plane valuesdequantize(dx, afrac, 0),dequantize(dy, afrac, 0)and 0. A two-agent set (A = 4) has(dxA, dyA, dxB, dyB), each scaled the same way.
13.6 The integer stages of wai.image.jpegai (jpegai-int/1)
The capability is NeuralEntropyExact (§12). §13.6.1 is its scale path and its two entropy decodes: normative, and complete from this text, the codestream syntax of ISO/IEC 6048-1, and the bundle members it names. §13.6.2 maps its other integer networks, and is informative.
13.6.1 The scale path and the entropy decodes
Inputs
-
From the codestream (ISO/IEC 6048-1). A segment’s payload is the bytes that follow its marker and its length field, and its length is the one that field gives. In the configuration the capability decodes (determinism-tiers §5), each payload below is one entropy substream, decoded as one thread (E8).
- the payload of the SOZ segment, which carries the hyper-latents of both planes, luma first, as one stream;
- the payloads of the SORP segment (the luma latent) and the SORS segment (the chroma latent);
- each component header’s per-channel GRFS flags, one per latent channel.
-
From the bundle’s
norm.json, a JSON object. The members this subsection reads, and their load checks:pmf:R ≥ 32rows, each an array ofw ≥ 1non-negative integers that sum to 256, all of one width;bounds: at least 32 integers, each in[0, 255];per_channel: 16, the hyper-latent symbols of one channel (4 × 4);cdfs_y:160·63integers;cdfs_uv:96·63. Each is in[0, 255], and each run of 63 (a row) is non-decreasing;gain_y: 160 integers;gain_uv: 96. Each fits i64.
Its other members feed floating-point stages and are not read here.
-
From the bundle’s
weights/hs_y.jsonandweights/hs_uv.json: JSON objects whose members are tensors{ "dims": [ … ], "v": [ … ] }, withvrow-major overdims. The scale graph reads the nine members of Table 13.6-A, with the shapes shown. Other members are ignored. The member names are the bundle’s, and mean nothing here. The block nameddepthwiseis a dense convolution.
Table 13.6-A. The scale graph’s members.
| block | weights | bias | per-channel shift |
|---|---|---|---|
| 1 | conv1.weight [C1, Cz, 1, 1] | conv1.bias [C1] | onnx::Pow_85 [1, C1, 1, 1] |
| 2 | depthwise.weight [C2, C1, 3, 3] | depthwise.bias [C2] | onnx::Pow_91 [1, C2, 1, 1] |
| 3 | pointwise.weight [C3, C2, 1, 1] | pointwise.bias [C3] | onnx::Pow_97 [1, C3, 1, 1] |
Cz is 160 in hs_y.json and 96 in hs_uv.json, and C3 = 16·Cz. Every weight and bias fits i64, and every shift is in 0..=127. A bundle that fails any load check of this subsection is refused (model_mismatch).
The scale graph (hs_y from hs_y.json with gain_y; hs_uv from hs_uv.json with gain_uv). Input 0 is z (§13.0). The operators, in order:
clamp(−128, 127);conv, block 1:cout = C1, 1×1, stride 1, padding 0,groups1; anRQwithroundfloor, aprebias (the block’s bias), no multiplier and one shift per channel (the block’s shifts);relu;clamp(−128, 127);conv, block 2:cout = C2, 3×3, stride 1, padding 1,groups1; the same kind ofRQ;relu;clamp(−128, 127);conv, block 3:cout = C3, 1×1, stride 1, padding 0,groups1; the same kind ofRQ;pixel_shuffle(4);abs;clamp(0, 3967);rescale: no multiplier, shift 0,floor, and aprebias per channel, the plane’s gains.
The output is v = I_s + g[c], [Cz, 16, 16].
Each plane. The luma plane has C = 160 channels, the rows cdfs_y, the gains gain_y, the latent segment SORP and skip = 0. The chroma plane has C = 96, cdfs_uv, gain_uv, SORS and skip = 160.
- Hyper-latent. A fresh
mans_decodeinfacmode (§5E, E8) runs over the SOZ segment’s payload, withLits length.- It first decodes
skiprows of 16 symbols, rowiwith rowiofcdfs_y, and discards them. So the chroma plane passes over the luma hyper-latent, with luma’s rows. - It then decodes
Crows of 16 symbols, rowcwith rowcof this plane’s rows. z[c][y][x] = sym_{c, 4y + x} − 31, soz ∈ [−31, 32].
- It first decodes
- Scale. The scale graph on
zgivesv. - Stage operators, at every channel
cand position:mask = coded(v, 382);likely = block_stat(v; 8, 6, 0, 4351);rvs = band_lookup(likely, [1003, 1352, 1937], [[−31, 20, 122, 181], [−80, −31, 71, 130]], row 0 when channel c's GRFS flag is set, else row 1);σ_idx = scale_index(v + rvs, 7, 32), which lies in[0, 31].
- Latent. The latent tables are built by E8 from
pmfandbounds:T_y[σ]from rowσ, andB_y[σ] = bounds[σ]forσ < 32. A freshmans_decodeinsgmmode runs over the payload of this plane’s latent segment, withLits length. It decodes spansc = 0 … C − 1in order. Spancis channelc’s 256 positions in raster order, with those positions’σ_idxand mask. The symbols form the latenty_q [C, 16, 16], with 0 at every unmasked position.
Refusals. A bundle that fails a load check: model_mismatch. A mans_decode refusal: stream_exhausted (E8). determinism-tiers says how the capability reports them. A refused exact decode MUST NOT fall back silently.
What this subsection fixes. z, v, the mask, likely, σ_idx and y_q are integers on every fabric. They are what the capability’s entropy-consistency claim covers (§12).
13.6.2 The other integer networks (informative)
The hyper decoder, the four context stages and the synthesis are integer as well. Their topology is not written out here, so this subsection maps each integer step to wai-ops/1 and does no more.
| integer step | wai-ops/1 |
|---|---|
| the hyper decoder’s input | z·2^16: rescale with one multiplier 2^16 and shift 0 |
| a convolution (grouped or depthwise) | conv; RQ: one multiplier (the convolution’s own mult), one shift (its own shift, except as below), a post bias (its Q16 bias, or 0), floor |
| a transposed convolution | conv_transpose, with the same RQ |
| the shift inside a TAB | the attention half’s three convolutions (prep_data.conv1, prep_data.conv2, attn.project_out) all use prep_data.conv1’s shift; the feed-forward half’s three (ffn.project_in, ffn.dwconv, ffn.project_out) all use ffn.project_in’s. Each keeps its own multiplier |
| ReLU6, clip | clamp(0, 6·2^16), clamp |
| a residual or mean addition | add |
| a Q16 product | mul; RQ: no multiplier, shift 16, floor |
a gate x·(f + 1) in Q16 | rescale (a post bias of 2^16), then mul |
| sigmoid, ELU, softmax | sigmoid, elu(2^16), softmax |
| channel layer normalisation | layernorm_c(ε = 42949) |
| channel attention | l2norm(42949) on q and on k; gram(heads, 16, half_up); rescale with one multiplier per channel (the head’s temperature), shift 16 and half_up; softmax; apply(heads, 16, half_up) |
channel slices; concatenation; the 2×2 sub-lattices, in the order (0,0), (1,1), (0,1), (1,0); depth-to-space | split; concat; subpos_split and subpos_merge; pixel_shuffle |
| denormalisation | rescale with one multiplier 128, shift 0 and a post bias of 127·2^16 + 2^15, with the policy clamp [0, 255·2^16]; for a byte, then rescale with shift 16 and half_up |
Every other convolution uses its own shift. Today’s bundle happens to give every convolution shift 31; the rule above is the decoder’s for any bundle. These graphs’ inputs come from floating-point stages, so once their topology is written, they have the policy check, with their admissible ranges for acc64 (§8.4).
14. Conformance
14.1 Vector files
Encoding. Every vector file is JSON Lines: one canonical JSON value (§8.5’s rules) per line, each line ending with one line feed, including the last. Every integer is a decimal string, shape dimensions included. Bytes are lowercase hexadecimal.
Per-operator vectors. wai/ops-conformance/vectors/<operator>.jsonl holds one vector per line:
vector = { "decodes": [ decode … ],
"graph": hex: a WHG1 file with exactly one operator;
"id": a string, unique in its file;
"load": "ok", or the code its load refuses with (§10.5);
"profiles": [ profile … ], in ascending order: the profiles whose implementations MUST reproduce it }
decode = { "expect": { "outputs": [ tensor … ] } or { "refuse": code },
"inputs": [ tensor … ], in input-table order;
"scalars": [ value … ], in scalar-table order }
tensor = { "data": [ value … ], in CHW order; "shape": [ C, H, W ] }
- The graph’s bytes carry the operator’s parameters, the inputs’ ranges, policies and sizes, the output tensors’ policies, the exponents and the state table. None of these has a second encoding.
- A decode’s
inputslists the graph’s data inputs only, in input-table order. A state input is never supplied: the sequence carries it (§10.3). - An implementation of a profile loads the graph against that profile (§8.6).
loadis the outcome under every profile listed. - A vector whose
loadis a refusal has no decodes, and itsprofilesis["acc32", "acc64"]: an implementation of either profile refuses it. A vector whoseloadis"ok"lists exactly the profiles its graph’s verdict meets (§8.5). - A vector with several decodes is one sequence (§10.3): its state carries from each decode that completes to the next, and a refused decode changes no state.
Stage-operator vectors. wai/ops-conformance/stage/<operator>.jsonl holds one vector per line:
{ "expect": { … } or { "at": an index, "refuse": code },
"id": …, "in": { … }, "modes": { … }, "op": the operator's name (§5E) }
modes is {} except for two operators:
rans64_decode:"table"("t4097"or"t2"),"escape"("lenient"or"strict") and"end"("canonical"or"final_only"). Understrict(lo, hi),loandhiare two more members,"lo"and"hi".bucket:"bucket"("ceil"or"floor").
Each operator’s in and expect are as follows. A tensor is { "data", "shape" }, as in a graph vector, with its elements in CHW order (§3).
| operator | in | expect |
|---|---|---|
rans64_decode | "stream": hex of the stream’s L bytes, L a multiple of 4 and at least 8 (integer-payloads §2); "tables": [ { "cdf": […], "offset" } … ]; "reads": the table index of each symbol, in order | "symbols" |
dequantize | "sym", "f", "base" | "value" |
bucket | "sigma", "edges" | "index" |
coded | "v", "thr" | "coded": true or false |
scale_index | "v", "s", "n" | "index" |
block_stat | "x": a tensor, each channel one plane; "b", "s", "lo", "hi" | "out": a tensor of x’s shape |
band_lookup | "v", "edges", "table": an array of rows, "row" | "value" |
mans_decode | "memory": hex of the stream’s L bytes; "mode": "sgm" or "fac". For sgm: "pmf": the rows P[σ]; "bounds": B_y; and "spans": [ { "mask": [ true … ], "sigma": […] } … ]. For fac: "rows": [ [63 counts] … ], and "lengths": the symbols of each row | "symbols": one array per span or row, with 0 at every unmasked position |
residual_step | "pred", "r", "q", "lo", "hi" | "value" |
mc_predict | "frame": the previous decoded frame R of integer-payloads §3 as a tensor [3, h, w], with element (c, y, x) the sample of channel c (R, G, B) at row y and column x; "y", "x": the pixel; "c": the channel; "dy", "dx": the pixel’s vector; "precision": mv_precision | "value": pred |
up2_predict | "prev": the unit’s current output as a tensor [c, h′, w′], with element (ch, y, x) the element staged-delivery A.4 writes prev[y, x, ch]; "d0", "d1" | "out": the prediction as a tensor [c, d0, d1], laid out likewise |
rate_predict | "prev": the unit’s n′ samples of c channels as a tensor [c, 1, n′], with element (ch, 0, i) the sample A.4 writes prev[i·c + ch]; "rate_num", "rate_den", "d0" | "out": the prediction as a tensor [c, 1, d0], laid out likewise |
coding_order | "shape": [C, H, W]; "order": "raster" or "checkerboard"; "anchor": true or false, read for checkerboard only | "indices": each element’s CHW position (c·H + y)·W + x, in coding order |
The refusal index at. A refused stage vector records where the refusal arose:
−1: a refusal before any symbol or element, for an operand, mode or table that breaks its operator’s conditions (§5E, Conditions and their refusals);0: every refusal ofdequantize; and, formans_decode, a refusal in the window’sinit;- for
rans64_decode, the index inreadsof the symbol whose decode refuses, by any of E1’s checks on one symbol. A refusal after the last symbol, undercanonicalan end that is not canonical and underfinal_onlya stream that has run out, hasatequal to the number of symbols; - for
mans_decode, a position. Positions are numbered from 0 across every position of every span (sgm), or every symbol of every row (fac), in decode order, unmasked positions included. A refusal inin(i)or inout(i), the escape’sflushincluded, hasat = i. A refusal in aflushthat belongs to no single position hasatequal to the last position whoseinstep ran: thesgmgroup’s flush after its fourinsteps, thesgmremainder flushes, thefacgroup’s flush and afacrow’s final flush. Insgm, theinstep of an unmasked position runs and reads nothing.
Who writes the vectors. One reference generator writes every vector file, inputs and expectations alike. The inputs are one shared corpus, and nothing else generates them. An independent checker, written from this text alone, reads each file, recomputes load, profiles (from its own bounds pass, §8) and every expect from the graph bytes and the inputs, and writes the file again. Its file MUST be byte-identical to the committed one. The independence lies in that recomputation.
An operator implementation conforms to a profile when it reproduces every vector tagged with that profile, refusals included. Each operator’s vectors cover at least:
- an input that attains each end of every accumulator’s partial-sum interval;
- for every rounding mode, ties on positive and on negative products, and shifts 0, 1, 31, 32, 62 and 63; and shifts 64 and 127, tagged
acc64only, since §7.2 caps everyacc32shift at 63; - a kernel larger than its input; output padding at least the stride; a stride larger than the kernel; a 1×1 plane; odd sizes;
- for
acc32, intermediates and products at exactly±(2^31 − 1)and−2^31; vectors one past those are taggedacc64only; - each
checkrange at its ends and one past them, and each scalar range likewise; and a run-time plan refusal for every shape rule of §5 (§4); - the cases that distinguish this set’s definitions from near neighbours:
igdnwhere flooring the sum would differ from flooring each term;leakyon negative values that floor away from zero;softmaxrows that do not sum to2^16;elu(0);layernorm_cwith oddCand a mean that is a half; and withε = 2^64 − 1, whereV + εpasses 64 bits;exp_neg_q16atn = 62andn = 63;sigmoidandeluon an input range that reaches−2^63, which failsacc64and is refused at load, and on one that stops at−(2^63 − 1), which decodes;- an
rqwhoseqpasses 64 bits whileyfits, refused at load underacc64; bucketexactly at an edge;
- for stage operators:
- every
rans64_decodemode, and understricteach refusal of E1 alone and in each pair that one symbol can give at once, under both end rules; mans_decodeon a zero tail and on a short stream, both refused; a bound of 128 and one of 255; a row whose escape is taken at each;residual_stepatr = ±Randr = ±(R + 1), and at the i32 limits.
- every
14.2 Graph vectors
wai/ops-conformance/graphs/<name>.jsonl holds vectors of the per-operator form whose graphs have several operators. Among them:
- the phase form of §9.3, set against
conv_transpose; - a state input carried across three decodes, starting from its
init; - a graph refused at each load check of §10.5;
- a graph refused at each step of §4’s run;
- each lowering of §13 on a small set.
14.3 Certificates and lowered graphs
wai/ops-conformance/certificates/<capability>/<set digest>.json holds the certificate of every parameter set in a capability’s conformance corpus. Beside it, <set digest>/<role>.whg1 holds each lowered graph. The directory also holds:
- a set that fails
acc64by one unit; - a set whose pin claims
acc32and that fails it by one weight.
A conforming certifier reproduces every certificate byte for byte. A conforming lowering reproduces every lowered graph byte for byte.
14.4 Capability corpora
Each capability of §13 is decoded through its lowering. It MUST reproduce every committed output digest and every refusal code of its existing corpora, as listed in integer-payloads §12, staged-delivery and SPEC §5.
15. Changes to other documents
15.1 Text changes
These change integer-payloads with this extension:
- integer-payloads §9: the sentence “The layers’ integer arithmetic is the reference decoders’…” becomes “The layers’ integer arithmetic is integer-operators §13.2 (
WHM1) and §13.3 (WAM1).” The text ofentropy.json(§15.2) follows theWHM1andWAM1paragraph. - integer-payloads: two new sections, §13 (the
wai.neural.int_synthpayload and set) and §14 (WIM1andWMB1).
These land with each capability’s lowering, capability by capability:
- integer-payloads §2: one sentence. The decode it defines is
rans64_decodewitht4097,lenientandcanonical. - integer-payloads §4 step 4, §5 step 3 and §6 step 3: cite §13.0’s output steps.
- integer-payloads §7, Exactness: “Each network decodes through its lowering (integer-operators §13), and a value outside a
checkrange is refused (field_range).” The refusal set is unchanged. - SPEC §3.1: the OPTIONAL pin member
ops_profile(§11). - SPEC §5: the rows of
wai.neural.int_synthandwai.neural.int_mlicppcite integer-payloads §13 and §14, and §13.1 and §13.4. - SPEC §7: a
NeuralIntegerExactcapability names itsopsmember. - determinism-tiers: §5 cites §12; the execution fields carry the profile;
wai.image.jpegai’s exact decoder reportsmodel_mismatchandstream_exhausted(§13.6.1) and does not fall back silently. - staged-delivery A.4: cites E9, E11 and E12.
registry/capabilities.json: theopsmembers.
15.2 The payload text this extension depends on
The payload text that §13 relies on for four capabilities is in integer-payloads. Each part states what the reference decoder does today, except where §15.3 lists a change.
entropy.json, the tables ofwai.neural.int_hyperandwai.video.int_hyper: integer-payloads §9.- The
wai.neural.int_synthpayload and its set,int_synth.json: integer-payloads §13. - The
WIM1payload and theWMB1bundle ofwai.neural.int_mlicpp: integer-payloads §14. Its decode gives the entropy network every element ofynot yet decoded as 0, and its three networks and its tables share oneafracin1..=30.
15.3 Behaviour this revision changes
No committed output changes, and no committed refusal fixture changes its code. These changes apply only to inputs that no corpus contains, and each takes effect when its capability decodes through its lowering (§13):
wai.neural.int_synth.- A set whose shift is 128 or more, whose
korstrideis 0, or whose certificate failsacc64, is refused. - A payload whose reserved byte is not 0, or whose
Cis 0, is refused. The reference decoder does not check either today. - A shift from 64 to 127 now has its exact meaning.
- The caps apply.
- A set whose shift is 128 or more, whose
wai.world.model’s integer renderer refuses values outside its loweredcheckranges, and a set whoseafracis outside1..=30or differs between its two networks.WHM1andWAM1layers. A layer whose predicate fails at 0 (a bias of magnitude2^63 − 1or more) is refused at load, not at decode. So is a set in which acheckrange cannot hold any value its producer can give (§8.1), which today refuses every decode withfield_range.wai.image.jpegai.- Its exact decoder refuses a malformed latent or hyper-latent stream (
stream_exhausted) where it panics today. - It refuses a bundle that fails §13.6.1’s load checks (
model_mismatch), including a bound above 255 or a shift outside0..=127, which today’s loader wraps.
- Its exact decoder refuses a malformed latent or hyper-latent stream (