int_mlicpp::decode and decode_traced = ops::decode(_traced); int_mlicpp::legacy (the direct decode) behind cfg(any(test, feature = "legacy_int")), and run_net_checked and to_rgb8 imported only there. int_payload::decode_int's WIM1 branch: the bundle parsed, ops::check, the three networks from the store of loaded sets (key: capability, wmb1/1, acc64, the SHA-256 of model.wmb's listing) or ops::load, ops::fit, ops::decode_loaded. int_ops::WMB1; Resident for int_mlicpp::ops::Nets. Tests: int_mlicpp_ops the_new_load_rules_affect_no_committed_set_or_payload (every committed bundle meets the rules and loads; every committed WIM1 payload, in any file, plans, decodes or is refused alike); int_payload_cost (now also legacy_int): with its networks loaded the decode holds no more than the direct decode; through the sink the decode that finds the bundle held holds less than the one that loads it, and keeps nothing more; int_set_cache: the vector is kept and hit, one byte apart never shares an entry, a refused bundle is never kept. Text: integer-payloads §14 (the decode through wmb1/1, step 1's checks, and the codes the decoder reports: payload_malformed for the payload, its streams and its values, model_mismatch for a bundle that breaks its rules or does not fit); integer-operators §4, the status note and §15.3 item 3; SPEC §8 planned breaks; CHANGELOG. Co-Authored-By: Claude Opus 5.5 <[email protected]>
1548 lines
124 KiB
Markdown
1548 lines
124 KiB
Markdown
# WAI Extension: Integer Operators (`wai-ops/1`)
|
||
|
||
> 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, `acc64` and `acc32`;
|
||
> - 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 `WHG1` graph 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_mlicpp` and `wai.{image,video,audio,splat}.int_refine`, and reproduces the symbols and scale indices that `wai.image.jpegai` entropy-decodes (§13.6.1). The payload text four of these depend on, `entropy.json`, the `int_synth` payload and the `WIM1` payload with its `WMB1` bundle, is in [integer-payloads](integer-payloads.md), and §15.2 says where. The integer renderer of `wai.world.model` and the other integer networks of `wai.image.jpegai` are informative (§13.5, §13.6.2).
|
||
>
|
||
> Reference implementation: the `wai-ops` crate (`wai/wai-ops`): parse, lowering, interpreter, stage operators and bounds. Tool: `wai-verify bounds`. Conformance: `wai/ops-conformance/`. A capability's reference decoder decodes through this operator set from the revision that registers the capability's `ops` member (§11); in this revision, `wai.neural.int_synth`, `wai.splat.int_codec`, `wai.audio.int_codec`, `wai.neural.int_hyper`, `wai.video.int_hyper` and `wai.neural.int_mlicpp`. Until then, a 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](determinism-tiers.md).
|
||
- Fixed generative algorithms that are not networks.
|
||
|
||
**Relation to the payload extensions.** [integer-payloads](integer-payloads.md) and [staged-delivery](staged-delivery.md) 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⌋` for `s ≥ 1`, and `p` for `s = 0`. A tie rounds toward +∞, whatever the sign of `p`.
|
||
- `R_half_even(p, s)` is `p` for `s = 0`. Otherwise, let `q = ⌊p / 2^s⌋`, `r = p − q·2^s` and `h = 2^(s−1)`. The result is `q + 1` if `r > h`, or if `r = h` and `q` is odd. Otherwise it is `q`.
|
||
|
||
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⌋`, and `g = 45426·n` (`exp.nl`);
|
||
- `r = x + g`, so `r ∈ (−45426, 0]`;
|
||
- `t = r + 88670`, so `t ∈ [43245, 88670]`;
|
||
- `t2 = t²` (`exp.t2`), and `q = ⌊t2 / 2^16⌋`;
|
||
- `c = 23495·q` (`exp.c`), and `y = ⌊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 `add` and `concat`, 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.
|
||
|
||
1. **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`, or `too_large` for a size.
|
||
2. **Run**, at each decode, in this order:
|
||
1. each input, one at a time in input-table order, state inputs included: its channel count against its tensor record, and `H ≥ 1` and `W ≥ 1` (`model_mismatch`); then its size, `H ≤ max_h` and `W ≤ max_w` when the input declares them, and `C·H·W ≤ max_elems` (§10.1) (`too_large`). An input's values are not read in this step;
|
||
2. each scalar against its declared range (`field_range`);
|
||
3. 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 are `conv`'s padded sides `H + 2ph` and `W + 2pw`; `conv_transpose`'s uncropped sides and its uncropped output of `cout` planes (§5.3); and the output sides of `pixel_shuffle`, `pad_rep` and `subpos_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_to` or `subpos_merge` whose sizes disagree; a `crop` whose scalars do not fit; a state whose shape no longer matches (§10.3);
|
||
4. evaluation. It starts with the inputs, in input-table order, state inputs included: each input's policy applies to it, so a `clamp` input is clamped, and an element of a `check` input outside its range refuses the decode (`field_range`). Then the operators run one by one in graph order. When a `check` tensor has been produced, an element outside its range refuses the decode (`field_range`).
|
||
|
||
A capability whose payload extension uses fewer codes reports its own in place of these. `wai.neural.int_synth` reports `payload_malformed` for every refusal (integer-payloads §8); `wai.neural.int_mlicpp` reports `payload_malformed` for a refusal of its payload or of a value its decode forms, and `model_mismatch` for a bundle that does not fit (integer-payloads §14).
|
||
|
||
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 in `0..=127`;
|
||
- `bias[o]`: an integer that fits i64, or **absent**, which means 0. A bias has a **placement**, either `pre` or `post`;
|
||
- `round`: one of `floor`, `half_up` or `half_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`, class `acc`.
|
||
- `rq.p`, class `product`, whose operands are `t` and `mult[o]`. The values formed are `p` and, when `round` is not `floor` and `s ≥ 1`, `p + 2^(s−1)`. Its interval covers both (§8.3). With `mult` absent, `p = t`.
|
||
- `rq.q`, class `acc`: the rounded value before a `post` bias.
|
||
- `rq.y`, class `acc`.
|
||
|
||
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, `pre` bias, `floor`: each layer of `int_synth`;
|
||
- no multiplier, one shift per channel, `pre` bias, `floor`: the three convolutions of the `wai.image.jpegai` scale path;
|
||
- no multiplier, shift 0, `pre` bias: the per-channel gain of that scale path;
|
||
- one multiplier per channel, one shift, `post` bias, `floor`: the convolutions of `WHM1` and `WAM1`;
|
||
- one multiplier, one shift, `post` bias, `floor`: the convolutions of the `wai.image.jpegai` PTQ 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`: `gram` and `apply` in channel attention;
|
||
- one multiplier per channel, shift 16, no bias, `half_up`: the attention temperature;
|
||
- one multiplier, shift 0, `post` bias: the denormalisation of `wai.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 `rq` over `cout` channels.
|
||
|
||
**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`, and `Wo` likewise.
|
||
- 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 `rq` over `cout` channels.
|
||
|
||
`groups` is 1.
|
||
|
||
**Shape.** The input is `[cin, H, W]`.
|
||
- `Ho = (H − 1)·sh + kh + oph − 2·ph` must be at least 1, and `Wo` likewise.
|
||
- The uncropped sides `uh = (H − 1)·sh + kh + oph` and `uw = (W − 1)·sw + kw + opw`, and the uncropped output of `cout·uh·uw` elements, are each within `2^26` (§3). They are checked before `Ho` and `Wo` (§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 `mult` and `bias` set, 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 `s` in `0..=127`;
|
||
- a `round`.
|
||
|
||
There is no bias.
|
||
- `out[c][y][x] = R_round(in[c][y][x] · M[r][c], s)`, where `r` is the value of scalar `k`.
|
||
- The scalar's declared range lies within `[0, n_rows − 1]`; this is a load check.
|
||
- **Intermediates:** those of `rq`, with `t` the input value, `M[r][c]` the multiplier and no bias, so `y = 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])`, where `c′ = c` if `b` has `C` channels, and 0 otherwise.
|
||
- **Intermediates:** `mul.p` (each product `a·b`), class `acc`; then `rq`, with `t = 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 | `|v|` |
|
||
|
||
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 `T` of 65,536 integers, each of which fits i16, with `T[0] = 0`;
|
||
- exponents `e_in` and `e_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_in` and the input tensor's exponent are both none or are equal; so are `e_out` and 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 `gf` and `frac`, each in `0..=127`; `γ[C][C]`, row-major with row `i` the output channel and column `j` the input channel; `β[C]`.
|
||
- **Shape:** `[C, H, W]` → `[C, H, W]`.
|
||
- **Definition:** at each position `(y, x)`, with `x_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` (each `x_j²`), class `wide`;
|
||
- `igdn.gx2` (each `γ[i][j]·x_j²`, before the floor), class `product`, whose operands are `x_j²` and `γ[i][j]`;
|
||
- `igdn.term` (each floored term), class `wide`;
|
||
- `igdn.N` (every partial sum of `N_i`, with `β_i` or without), class `wide`;
|
||
- `igdn.r`, class `acc`;
|
||
- `igdn.xr` (each `x_i·r_i`), class `wide`.
|
||
|
||
**`layernorm_c`.**
|
||
- **Parameters:** `ε`, an integer in `1..=2^64 − 1`; `γ[C]`; `β[C]`.
|
||
- **Shape:** `[C, H, W]` → `[C, H, W]`.
|
||
- **Definition:** at each position, over that position's `C` channel values `x_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.S` and `ln.T` (every partial sum), `ln.m`, `ln.v`, `ln.dk` and `ln.ng`, class `wide`; `ln.d`, `rsq.num`, `ln.k` and `ln.n`, class `acc`.
|
||
|
||
**`l2norm`.**
|
||
- **Parameters:** `ε`, an integer in `1..=2^64 − 1`.
|
||
- **Shape:** unchanged.
|
||
- **Definition:** for each channel `c`, over all `H·W` positions of that channel:
|
||
|
||
```
|
||
T = Σ_{y,x} v²
|
||
U = T + ε
|
||
k = rsqrt_q16(U)
|
||
out = ⌊v · k / 2^16⌋
|
||
```
|
||
|
||
- **Intermediates:** `l2.T` (every partial sum of `T`, and `U`) and `l2.vk` (each `v·k`), class `wide`; `rsq.num` and `l2.k`, class `acc`.
|
||
|
||
**`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` (each `v_x − m`), `exp.negx`, `exp.nl`, `exp.t2`, `exp.c` (§2), `sm.sum` (every partial sum of `Σ`) and `sm.num` (each `e_x·2^16 + ⌊Σ/2⌋`), all class `acc`.
|
||
|
||
### 5.8 Scalar functions: `sigmoid` (`0x1B`), `elu` (`0x1C`)
|
||
|
||
**`sigmoid`.** No parameters; the shape is unchanged.
|
||
- If `v ≥ 0`: let `e = exp_neg_q16(−v)`; the output is `⌊2^32 / (2^16 + e)⌋`.
|
||
- If `v < 0`: let `e = exp_neg_q16(v)`; the output is `⌊e · 2^16 / (2^16 + e)⌋`.
|
||
- In both cases the argument of `exp_neg_q16` is `−|v|`, so `exp.negx = |v|`.
|
||
- The output lies in `[0, 2^16]`.
|
||
- **Intermediates:** `exp.negx`, `exp.nl`, `exp.t2`, `exp.c`, and `sig.num` (the dividend: `2^32`, or `e·2^16`), all class `acc`.
|
||
|
||
**`elu(α)`.** `α` fits i64; the shape is unchanged.
|
||
- If `v > 0`, the output is `v`.
|
||
- Otherwise, the output is `⌊α · (exp_neg_q16(v) − 2^16) / 2^16⌋`.
|
||
- At `v = 0` this gives `⌊17·α / 2^16⌋`.
|
||
- **Intermediates:** `exp.negx`, `exp.nl`, `exp.t2` and `exp.c`, class `acc`; `elu.p` (each `α·(e − 2^16)`), class `wide`.
|
||
|
||
### 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 `s0` is 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, with `hi` the top of its scalar's range;
|
||
- the shape rule then applies to `s0` and 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_decode` is defined in integer-payloads §2;
|
||
- `mc_predict` in integer-payloads §3;
|
||
- `up2_predict` and `rate_predict` in 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_range` for E2's `f` and value, as E2 states;
|
||
- `geometry_mismatch` when the `prev` that 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_mismatch` for 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.**
|
||
- `t4097` is integer-payloads §2: `3 ≤ n ≤ 4097`, `cdf[0] = 0`, `cdf[n−1] = 65536`, strictly increasing.
|
||
- `t2` is the same except that `2 ≤ n ≤ 65537`. With `n = 2`, every symbol is the escape.
|
||
- Before any symbol is decoded, every table is checked against the table rule, and its offset `o` must fit i32. Under `strict`, `lo ≤ hi`. Each symbol's table must be one of the tables given.
|
||
- **Escape rule.**
|
||
- `lenient` is 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 to `i32`. The `count` value groups that follow are read whatever the stream's state.
|
||
- `strict(lo, hi)`, with `lo ≤ hi` both fitting i32, reads one 4-bit count and **no continuation groups**:
|
||
1. `count = bits(4)`;
|
||
2. if `count ≤ 8`, `raw = Σ_{j<count} bits(4)·2^(4j)`, reading the groups for `j = 0, 1, …` in turn; if `count > 8`, no group is read;
|
||
3. the value is `−⌊raw/2⌋ − 1` if `raw` is odd and `⌊raw/2⌋ + (n − 2)` if it is even, and the symbol is `value + 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 `bits` reads 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:
|
||
1. under the `canonical` end rule, the stream ran out at any point during this symbol's decode, in its table step or in any `bits` (`stream_exhausted`);
|
||
2. for an escape, `count > 8` (`stream_noncanonical`);
|
||
3. for an escape, `count ≥ 1` and the group read last (`j = count − 1`) is 0, so the encoding is not minimal (`stream_noncanonical`);
|
||
4. for every symbol, escaped or not, a symbol outside `[lo, hi]` (`field_range`).
|
||
- **End rule.**
|
||
- `canonical` is 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_only` tests 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)`.**
|
||
- `f` is at most 62; a larger value is refused with `field_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 `check` range applies.
|
||
|
||
**E3 `bucket(σ, e, rule)`.** Comparisons only.
|
||
- **`ceil`.** It takes `N ≥ 1` edges `e_0 … e_{N−1}`.
|
||
- Let `σ′ = max(σ, e_0)`. The index is `b = (N − 1) − #{ i < N − 1 : σ′ ≤ e_i }`.
|
||
- It lies in `[0, N − 1]`, for any edges.
|
||
- `e_{N−1}` is never compared.
|
||
- **`floor`.** It takes `N − 1 ≥ 1` edges `e_1 … e_{N−1}`, non-decreasing: both are conditions (`model_mismatch`).
|
||
- The index is `b = #{ i : e_i ≤ σ }`.
|
||
- It lies in `[0, N − 1]`.
|
||
|
||
**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.
|
||
- `S` is 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`.
|
||
- `s` is 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.*
|
||
- `M` is 8 zero bytes followed by the stream's `L` bytes.
|
||
- `LE64(i)` is the little-endian 64-bit value at `M[i .. i + 8]`.
|
||
- The window holds an index `p`, a 64-bit head `h`, a bit position `b`, and two 8-bit states `s1` and `s2`.
|
||
- 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]`. If `sym_i + β = 0`:
|
||
- `f = read(1)`;
|
||
- `v = read(f = 1 ? 3 : 16)`;
|
||
- `g = v mod 2`;
|
||
- `sym_i = wrap16(⌊v/2⌋ + β)` if `g = 0`, and `wrap16(−⌊v/2⌋ − β)` if `g = 1`;
|
||
- then `flush`.
|
||
- A **span** of `L′` positions, with a mask, is decoded in groups of four positions `i … 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 `out` for each of the four, only where the mask is set.
|
||
- Then the remainder:
|
||
- if `L′` mod 4 ≥ 2: position `i` (`s1`), then `i+1` (`s2`), each as `in` then `out` where the mask is set; then `flush`;
|
||
- if `L′` is odd: position `i` (`s1`) as `in` then `out` where the mask is set; then `flush`.
|
||
- An unmasked position is 0 and reads nothing. It keeps its place in the `s1`/`s2` alternation.
|
||
- 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/2` if `k` is even, and `−(k + 1)/2` if it is odd.
|
||
|
||
For each column `k` in order, while `C[k] < 256`:
|
||
- every slot `u ∈ [F[k], F[k+1])` gets symbol `sym(k)` and `next = P[k] + (u − F[k])`;
|
||
- every slot `u ∈ [G[k], G[k+1])` gets symbol `sym(k)` and `next = 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:
|
||
- `nb` is the number of doublings that bring `next` to at least 256, so `nb ≤ 8`;
|
||
- `base = (next·2^nb) XOR 256`, so `base < 256`.
|
||
|
||
*Hyper-latent decode (`fac`).*
|
||
- **The preprocessing table.** `Zp[i]` is defined for `i` from 511 down to 1. Keep a counter `nb`, starting at 0. At each `i`, first add 1 to `nb` once if `i·2^nb < 256`. Then `Zp[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`); let `c_{−1} = 0`.
|
||
- For each symbol `a < 63`, let `k = c_a − c_{a−1}`. Each slot `j ∈ [c_{a−1}, c_a)` gets `T_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_z` starts at all zeros.
|
||
- **`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)`:** if `sym_i = 63`, then `sym_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 `out` for 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)`.
|
||
- Each row ends with one `flush`.
|
||
- Rows are decoded in order, each with its own row table, and the window, the states and `T_z` carry 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 equals `clamp(pred + clamp(r, −R, R)·q, lo, hi)`.
|
||
- *Proof.* If `r > R`, then `r·q > R·q > hi − lo`, so both forms give `hi`. The case `r < −R` is symmetric.
|
||
- *Width.* Every value this form forms fits i32 when `hi − lo ≤ 65535` and both `lo − 131070` and `hi + 131070` fit i32. Then `R·q ≤ (hi − lo) + q ≤ 131070`, so `pred + clamp(r, −R, R)·q` lies 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: with `lo = 2^31 − 65536`, `hi = 2^31 − 1`, `q = 65535`, `pred = hi` and `r = 2`, the form forms `hi + 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 = 0` when `anchor` is 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 `P` be the largest magnitude in the operator's term interval (§8.3). Then `K_c = ⌊(2^31 − 1) / P⌋`, or unbounded when `P = 0`, and any partial sum of at most `K_c` terms fits i32. The certificate records `K_c` as `k_c` (§8.5). So an implementation MAY form i32 partial sums of at most `K_c` terms, widen them, and add.
|
||
- **Dot-product instructions.** An instruction that adds `n` products into one i32 lane leaves that lane holding `n·j` products after `j` instructions. The lane widens before `n·j` would exceed `K_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 `pre` bias 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.p` MAY be formed in 64 bits wherever its certified interval fits i64.
|
||
- **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's `acc64` width.
|
||
- This is the reference profile for CPU and Wasm.
|
||
- It needs a 128-bit value only where a `wide` or `product` interval passes 64 bits.
|
||
- **`acc32`.** Three conditions hold:
|
||
1. every intermediate and producer result fits its class's `acc32` width, and every graph input's and scalar's declared range fits i32;
|
||
2. only graph inputs have the policy `check`;
|
||
3. the parameters meet these conditions:
|
||
|
||
| 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 `product` intermediates, each the exact product of two 32-bit operands.
|
||
- An `acc32` graph needs no refusal inside the graph, so a fabric that cannot stop a computation partway through can run it.
|
||
- A set that meets `acc32` also meets `acc64`.
|
||
|
||
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`, `sigmoid` and `elu`, through `exp.t2` (up to `88670² ≈ 2^32.9`) and `exp.c` (up to `23495·119970 ≈ 2^31.4`);
|
||
- `sigmoid`, through `sig.num` (up to `65553·2^16`);
|
||
- `layernorm_c` and `l2norm`, through `rsq.num` (`2^32`), and `l2norm` through `l2.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 `acc64` natively: 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 each `product` intermediate 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 the `post` bias on that two-word value before it narrows the result to i32. Every signed value of an `acc32` graph provably fits i32, so no signed overflow reaches a translated shader.
|
||
- **A fabric that emulates 64-bit and 128-bit arithmetic exactly** is an `acc64` implementation on that fabric.
|
||
|
||
### 7.4 Stage operators
|
||
|
||
Stage operators (§5E) run in the driver at the widths listed in §5E.
|
||
- `rans64_decode` and `mans_decode` need a 64-bit state or window.
|
||
- `dequantize` needs a 128-bit intermediate, or checked 64-bit arithmetic.
|
||
- `rate_predict` forms values of magnitude up to `2^49` (E12), so it needs 64-bit arithmetic.
|
||
- `scale_index`, `block_stat`, `band_lookup`, `coded`, `bucket`, `mc_predict`, `up2_predict` and `coding_order` fit i32, and so does `residual_step` in 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 `sat` to 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, with `sat` after 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, with `sat` after each operation, and `R_round` is evaluated as §2 writes it. Otherwise, a product of three factors, one of which can be 0 while the other two pass `2^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.
|
||
|
||
*Informative: the same integers from a few sums.* Let every term of `K` be a weight's term interval over one input interval `[l, h]` with `l ≤ h`, as for `conv` and for each residue of `conv_transpose` (§8.3). Let `S⁺` and `S⁻` be the sums of the positive and of the negative weights, and `w_min` and `w_max` the least and the greatest weight.
|
||
- A weight `w > 0` has the term `[w·l, w·h]`, and adds `w·min(0, l)` to the lower end and `w·max(0, h)` to the upper end. A weight `w < 0` has the term `[w·h, w·l]`, and adds `w·max(0, h)` and `w·min(0, l)`.
|
||
- So `A(K, b) = [min(0, b) + min(0, l)·S⁺ + max(0, h)·S⁻, max(0, b) + max(0, h)·S⁺ + min(0, l)·S⁻]`.
|
||
- For a fixed end, `w·l` is monotone in `w`, so the hull of the term intervals is the corner hull of `[w_min, w_max]` and `[l, h]`.
|
||
- The left fold of §8.1 adds terms of one sign, so its partial sums lie between 0 and its result. When no term and neither end passes `2^255`, nothing saturates, and these are the pass's integers. Otherwise a certifier folds term by term.
|
||
|
||
A certifier can therefore read each weight once and keep a few sums for each output channel, or for each residue of `conv_transpose`. The reference implementation does this.
|
||
|
||
### 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)]`; `|·|` is `[l, h]` if `l ≥ 0`, `[−h, −l]` if `h ≤ 0`, and `[0, max(−l, h)]` otherwise |
|
||
| `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 `|·|`, then the `exp_neg_q16` rule. `sig.num = [0, 65553·2^16]`. The producer result is `[0, 2^16]`. |
|
||
| `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 meets `P`'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 of `C`.
|
||
- **Search.** Every rule of §8.3 is monotone under inclusion, so `C(X)` implies `C(X′)` for every `X′ ≤ X`. `X*` is the largest `X ∈ [0, 2^63]` for which `C(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 for `P`: no value of `τ` lets its consumers meet `P`. 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 `true` and `false`. An absent value is `null`.
|
||
- 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"`, with `bits` of its declared range;
|
||
- for a saturated node, `"saturated"`, with no `bits`;
|
||
- 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 output `k`. The failure carries its `bits`;
|
||
- otherwise (`acc32` only), the first entry of `acc32_params`, or, failing that, `"policy.check"`, with no `bits`.
|
||
|
||
**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"`: the `acc32` verdict is false;
|
||
- `"op:<name>"`: an operator marked *no* in §5's table, for example `"op:igdn"`;
|
||
- `"conv_groups"`: a `conv` whose `groups` is neither 1 nor its `cin`, or whose `cout` differs from `cin` when `groups` is `cin` and above 1;
|
||
- `"crop_scalar"`: a `crop` with 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.
|
||
|
||
*Informative: what a sink may keep between decodes.* A sink may keep more than the verdict: the set's graphs as loaded, so that a later decode through the same set skips the lowering and the load, which read the whole set again.
|
||
- **The key** is what makes this sound. It is the SHA-256 of the exact bytes the set was lowered from (for a set of several members, of their listing, which is the digest a pin over exactly those members names, [SPEC](../SPEC.md) §3.1), together with the capability, the lowering and the profile the graphs were loaded under. A name, a path or a label does not fix the bytes: keyed by one, a set could decode with the graphs of another set of the same name.
|
||
- **Only what loaded.** An entry is made after every load step of §10.5 has passed. A set that is refused is not kept, so it is lowered and loaded again on its next decode and refused again, with the same code.
|
||
- **Nothing else moves.** A decode makes every check before the load as before, in the order its payload extension gives ([integer-payloads](integer-payloads.md) §7), and the graphs run as they do after a fresh load, so the output is the same bytes.
|
||
- **Memory.** What such entries hold is the set's, the same for every payload, and outside a decode's declared working bytes ([integer-payloads](integer-payloads.md) §7). The sink bounds it with its own budget. The reference implementation keeps them in `int_set_cache`.
|
||
|
||
## 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:
|
||
1. its verdict meets `acc32`, which already excludes a `check` policy inside the graph;
|
||
2. it uses only operators marked *yes* or *flagged* in §5's `wai-ops/1-npu` column;
|
||
3. every `conv` has `groups = 1`, or is depthwise (`groups = cin = cout`);
|
||
4. every `crop` has 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 `crop` from 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` (when `sh = sw`) and cropped by `ph` and `pw`.
|
||
- 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.
|
||
|
||
- **`floor` to `half_up`, with no multiplier.** For `s ≥ 1`, `R_floor(t + b, s) = R_half_up(t + b − 2^(s−1), s)`. So a `floor` requantisation with no multiplier and `pre` bias `b` is a `half_up` one with `pre` bias `b − 2^(s−1)`. With a multiplier there is no such single-bias form in general, hence `rq_floor_mult`.
|
||
- **A negative multiplier.** An `rq` whose channel `o` has `m[o] < 0` equals one with `−m[o]`, channel `o`'s weights negated and its `pre` bias negated. Its `post` bias is unchanged.
|
||
- **A shift outside a unit's range.** `R(t·m, s) = R(t·(m·2^k), s + k)` for every `k ≥ 0` and every rounding of §2. A shift below the range rises into it while `m·2^k` still fits the unit's multiplier. A shift above it needs `m` to be a multiple of `2^k`.
|
||
- **A `post` bias.** `R(t·m, s) + b = R(t·m + b·2^s, s)` for `floor` and `half_up`, and for `half_even` when `b` is even (an odd `b` changes the parity that breaks a tie). When the identity holds and `m` divides `b·2^s`, the `post` bias is a `pre` bias of `b·2^s / m`.
|
||
- **Rounding ties away from zero.** A unit that rounds ties away from zero agrees with `half_up` on non-negative products only. An `rq` whose `rq.p` interval is non-negative maps exactly, and others do not.
|
||
- **Depth-to-space order.** `pixel_shuffle` takes channel `c·r² + i·r + j` to output channel `c` (§5.10). A target whose depth-to-space reads channel `(i·r + j)·C′ + c` instead needs that permutation folded into the output channels of the operator that produces the input.
|
||
- **Replicate padding.** Not every target pads by replication. `pad_rep` can be built from `crop` and `concat` of edge rows and columns.
|
||
- **Storage limits.** A tensor of `2^26` elements 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_scalars` records of 16 bytes: `i64 lo`, `i64 hi`, with `lo ≤ hi`.
|
||
- The driver supplies each scalar's value at run time. A value outside its range is refused (`field_range`).
|
||
- **Inputs:** `n_inputs` records 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;
|
||
- `u32` reserved;
|
||
- `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 `H` and `W` are at most `max_h` and `max_w` when they are declared, otherwise `too_large`.
|
||
- **Outputs:** `n_outputs` records of 4 bytes: `u32 tensor`.
|
||
- **Tensors:** `n_tensors` records of 24 bytes:
|
||
- `u32 channels` (≥ 1);
|
||
- `i8 exponent`, in −127..=127, or −128 for none;
|
||
- `u8 policy`: 0 for `derived`, 1 for `clamp`, 2 for `check`;
|
||
- `u16` reserved;
|
||
- `i64 lo`, `i64 hi`. With `derived`, both are 0. Otherwise `lo ≤ hi`.
|
||
- **Operators:** `n_ops` records, in evaluation order. Each record is:
|
||
- `u8 opcode`, `u8 n_in`, `u8 n_out`, `u8` reserved;
|
||
- `u32 in[n_in]`, `u32 out[n_out]`;
|
||
- `u32 plen`, then `plen` bytes 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 `check` or `clamp`.
|
||
- Input 0 is of kind data.
|
||
- `n_in` and `n_out` are the operator's: §5's table gives them, `concat` has `n_in ≥ 2`, and the `subpos_*` 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 `src` is less than `n_outputs`. That output's tensor has the state input's channel count, and `init` lies 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:
|
||
1. the magic, version and operator set;
|
||
2. the reserved fields;
|
||
3. the counts against the file's length;
|
||
4. each table's records (§10.2);
|
||
5. each operator's opcode, its arity, its parameter length and the ranges of its parameters (§5, §10.4);
|
||
6. the graph rules, state rules included (§10.3), and the exponent rules (§3, §5.6);
|
||
7. the plan. Every operator's channel counts. When the inputs declare their sizes, every shape, planned on `max_h` and `max_w`, where a size past a cap is `too_large`. A graph with a `gram`, `l2norm` or `softmax` whose inputs do not declare their sizes is refused;
|
||
8. 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`:
|
||
|
||
```json
|
||
"ops": {
|
||
"set": "wai-ops/1",
|
||
"profile": "acc64",
|
||
"subset": "full",
|
||
"lowering": "whm1/1",
|
||
"stage": { "rans64": { "table": "t4097", "escape": "lenient", "end": "canonical" },
|
||
"bucket": "ceil" }
|
||
}
|
||
```
|
||
|
||
- `profile` is 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.
|
||
- `subset` is `full` or `npu`. A capability registered `npu` refuses a set that is not in the subset (§9.2).
|
||
- `lowering` names the §13 lowering, or `whg1` when the set carries graphs.
|
||
- `stage` fixes 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 `acc32` set may run on an `acc32` or an `acc64` implementation. An `acc64` set runs only on an `acc64` implementation.
|
||
- A sink that moves a decode from one fabric to another reports where it ran, as [determinism-tiers](determinism-tiers.md) 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-equivalence` claim, whatever operators it can map.
|
||
- A receipt for a decode through `wai-ops/1` names the profile and the fabric in the execution fields of [determinism-tiers](determinism-tiers.md).
|
||
|
||
## 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_h` and `max_w` are the driver contract's (below).
|
||
- **Tensor numbering.** Input `i` is tensor `i`. The operators' output tensors follow, in operator order, and each operator's outputs in their own order take the next unused indices.
|
||
- **Tensor records.** `channels` as planned. Exponent −128 (none). Policy `derived`, with `lo = hi = 0`, except where the lowering gives `check` and 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](integer-payloads.md) §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_transpose` with `cout = out_ch`, `kh = kw = k`, `sh = sw = stride`, `ph = pw = pad`, `oph = opw = out_pad`, the weights as stored, and an `RQ` with `round` `floor`, a `pre` bias, no multiplier and one shift (the layer's);
|
||
- then `relu`, if `relu` is 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`:
|
||
- there is at least one layer;
|
||
- the layers chain: the first `in_ch` is `C`, and each later `in_ch` is the previous `out_ch`;
|
||
- the parameter lengths are exact;
|
||
- `in_ch`, `out_ch`, `k`, `stride`, `pad` and `out_pad` are below `2^32`, the width of their `WHG1` fields (§10.4);
|
||
- `out_ch ≥ 1`, `k ≥ 1`, `stride ≥ 1` and `shift ≤ 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·|slope_q| ≤ I64` |
|
||
|
||
- `P_ℓ` is evaluated exactly. Every value it forms is below `2^255`, because `c` and `k` are below `2^32`.
|
||
- Every `P_ℓ` holds at `X` whenever it holds at a larger `X`, so `X_ℓ` is found exactly by a binary search.
|
||
- If `P_ℓ(0)` fails, the set is refused (`model_mismatch`).
|
||
- `max|x_ℓ| ≤ X_ℓ` exactly when `P_ℓ(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 meets `acc64`.
|
||
|
||
**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 < 128` and `gf < 128`;
|
||
- `afrac` in `1..=30`, the rule of every capability this extension lowers (integer-payloads §7 for `WIH1`, `WIA1` and `WIS1`, and integer-payloads §14 for `WMB1`; `world/1` in §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_transpose` with `kh = 1`, `sh = 1`, `ph = 0`, `oph = 0`, `kw = k`, `sw = stride`, `pw = pad` and `opw = opad`. Its weights are `i16 [c][out][k]`, and it has the `RQ` of §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:
|
||
1. `crop` with `y0 = 0`, `x0 = 0` and `h = 1` constant, and `w` scalar 0 (`t_out`). Its output tensor has the policy `check [⌈−2^63 / 32767⌉, ⌊(I64 − 2^(afrac−1)) / 32767⌋]`. This is the range in which `v·32767 + 2^(afrac−1)` fits i64, which is what integer-payloads §5 step 3 refuses outside.
|
||
2. `rescale` with one multiplier 32767 (`m_bytes` 4), one shift `afrac`, no bias and `half_up`.
|
||
3. `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' `afrac` lie in `1..=30` and are equal. `A`, the number of action planes, is `T`'s input channel count (§13.2) minus `C`, and `A ≥ 1`. Every row has `A` entries.
|
||
- **The graph `T`.** Scalars `0 … A − 1` are the plane values, and input 0 is `z` (§13.0). The operators are:
|
||
- `fill` from scalar `a`, for `a = 0 … A − 1` in turn, each reading `z` and writing a one-channel tensor;
|
||
- `concat(z, fill_0, …, fill_{A−1})`, writing `x_0`, with `T`'s layer-0 range check (§13.2);
|
||
- `T`'s layers (§13.2);
|
||
- `clamp(−2^afrac, 2^afrac)`, with `T`'s `afrac`. Its output is the graph's.
|
||
- **The graph `g_s`** is `whm1/1` with the RGB8 step, which uses `g_s`'s `afrac`. The load check makes the two equal.
|
||
- **The driver.** Frame 0 is `g_s(z_0)`. For each row in order, `z` becomes `T(z)` with the row's entries as the scalars, and the next frame is `g_s(z)`. A single-agent set (`A = 3`) whose rows are moves `(dx, dy)` has the plane values `dequantize(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](determinism-tiers.md) §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 ≥ 32` rows, each an array of `w ≥ 1` non-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·63` integers; `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.json` and `weights/hs_uv.json`**: JSON objects whose members are tensors `{ "dims": [ … ], "v": [ … ] }`, with `v` row-major over `dims`. 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 named `depthwise` is 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:
|
||
1. `clamp(−128, 127)`;
|
||
2. `conv`, block 1: `cout = C1`, 1×1, stride 1, padding 0, `groups` 1; an `RQ` with `round` `floor`, a `pre` bias (the block's bias), no multiplier and one shift per channel (the block's shifts);
|
||
3. `relu`;
|
||
4. `clamp(−128, 127)`;
|
||
5. `conv`, block 2: `cout = C2`, 3×3, stride 1, padding 1, `groups` 1; the same kind of `RQ`;
|
||
6. `relu`;
|
||
7. `clamp(−128, 127)`;
|
||
8. `conv`, block 3: `cout = C3`, 1×1, stride 1, padding 0, `groups` 1; the same kind of `RQ`;
|
||
9. `pixel_shuffle(4)`;
|
||
10. `abs`;
|
||
11. `clamp(0, 3967)`;
|
||
12. `rescale`: no multiplier, shift 0, `floor`, and a `pre` bias 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`.
|
||
|
||
1. **Hyper-latent.** A fresh `mans_decode` in `fac` mode (§5E, E8) runs over the SOZ segment's payload, with `L` its length.
|
||
- It first decodes `skip` rows of 16 symbols, row `i` with row `i` of `cdfs_y`, and discards them. So the chroma plane passes over the luma hyper-latent, with luma's rows.
|
||
- It then decodes `C` rows of 16 symbols, row `c` with row `c` of this plane's rows.
|
||
- `z[c][y][x] = sym_{c, 4y + x} − 31`, so `z ∈ [−31, 32]`.
|
||
2. **Scale.** The scale graph on `z` gives `v`.
|
||
3. **Stage operators**, at every channel `c` and 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]`.
|
||
4. **Latent.** The latent tables are built by E8 from `pmf` and `bounds`: `T_y[σ]` from row `σ`, and `B_y[σ] = bounds[σ]` for `σ < 32`. A fresh `mans_decode` in `sgm` mode runs over the payload of this plane's latent segment, with `L` its length. It decodes spans `c = 0 … C − 1` in order. Span `c` is channel `c`'s 256 positions in raster order, with those positions' `σ_idx` and mask. The symbols form the latent `y_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](determinism-tiers.md) 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 `inputs` lists 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). `load` is the outcome under every profile listed.
|
||
- A vector whose `load` is a refusal has no decodes, and its `profiles` is `["acc32", "acc64"]`: an implementation of either profile refuses it. A vector whose `load` is `"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"`). Under `strict(lo, hi)`, `lo` and `hi` are 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 of `dequantize`; and, for `mans_decode`, a refusal in the window's `init`;
|
||
- for `rans64_decode`, the index in `reads` of the symbol whose decode refuses, by any of E1's checks on one symbol. A refusal after the last symbol, under `canonical` an end that is not canonical and under `final_only` a stream that has run out, has `at` equal 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 in `in(i)` or in `out(i)`, the escape's `flush` included, has `at = i`. A refusal in a `flush` that belongs to no single position has `at` equal to the last position whose `in` step ran: the `sgm` group's flush after its four `in` steps, the `sgm` remainder flushes, the `fac` group's flush and a `fac` row's final flush. In `sgm`, the `in` step 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:**
|
||
1. an input that attains each end of every accumulator's partial-sum interval;
|
||
2. 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 `acc64` only, since §7.2 caps every `acc32` shift at 63;
|
||
3. a kernel larger than its input; output padding at least the stride; a stride larger than the kernel; a 1×1 plane; odd sizes;
|
||
4. for `acc32`, intermediates and products at exactly `±(2^31 − 1)` and `−2^31`; vectors one past those are tagged `acc64` only;
|
||
5. each `check` range 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);
|
||
6. the cases that distinguish this set's definitions from near neighbours:
|
||
- `igdn` where flooring the sum would differ from flooring each term;
|
||
- `leaky` on negative values that floor away from zero;
|
||
- `softmax` rows that do not sum to `2^16`;
|
||
- `elu(0)`;
|
||
- `layernorm_c` with odd `C` and a mean that is a half; and with `ε = 2^64 − 1`, where `V + ε` passes 64 bits;
|
||
- `exp_neg_q16` at `n = 62` and `n = 63`;
|
||
- `sigmoid` and `elu` on an input range that reaches `−2^63`, which fails `acc64` and is refused at load, and on one that stops at `−(2^63 − 1)`, which decodes;
|
||
- an `rq` whose `q` passes 64 bits while `y` fits, refused at load under `acc64`;
|
||
- `bucket` exactly at an edge;
|
||
7. for stage operators:
|
||
- every `rans64_decode` mode, and under `strict` each refusal of E1 alone and in each pair that one symbol can give at once, under both end rules;
|
||
- `mans_decode` on 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_step` at `r = ±R` and `r = ±(R + 1)`, and at the i32 limits.
|
||
|
||
### 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 `acc64` by one unit;
|
||
- a set whose pin claims `acc32` and 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](integer-payloads.md) with this extension:
|
||
|
||
- **integer-payloads §9:** the sentence "The layers' integer arithmetic is the reference decoders'…" becomes "The layers' integer arithmetic is [integer-operators](integer-operators.md) §13.2 (`WHM1`) and §13.3 (`WAM1`)." The text of `entropy.json` (§15.2) follows the `WHM1` and `WAM1` paragraph.
|
||
- **integer-payloads:** two new sections, §13 (the `wai.neural.int_synth` payload and set) and §14 (`WIM1` and `WMB1`).
|
||
|
||
These land with each capability's lowering, capability by capability:
|
||
|
||
- **integer-payloads §2:** one sentence. The decode it defines is `rans64_decode` with `t4097`, `lenient` and `canonical`.
|
||
- **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](integer-operators.md) §13), and a value outside a `check` range 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_synth` and `wai.neural.int_mlicpp` cite integer-payloads §13 and §14, and §13.1 and §13.4.
|
||
- **SPEC §7:** a `NeuralIntegerExact` capability names its `ops` member.
|
||
- **determinism-tiers:** §5 cites §12; the execution fields carry the profile; `wai.image.jpegai`'s exact decoder reports `model_mismatch` and `stream_exhausted` (§13.6.1) and does not fall back silently.
|
||
- **staged-delivery A.4:** cites E9, E11 and E12.
|
||
- **`registry/capabilities.json`:** the `ops` members.
|
||
|
||
### 15.2 The payload text this extension depends on
|
||
|
||
The payload text that §13 relies on for four capabilities is in [integer-payloads](integer-payloads.md). Each part states what the reference decoder does today, except where §15.3 lists a change.
|
||
|
||
- **`entropy.json`**, the tables of `wai.neural.int_hyper` and `wai.video.int_hyper`: integer-payloads §9.
|
||
- **The `wai.neural.int_synth` payload and its set, `int_synth.json`**: integer-payloads §13.
|
||
- **The `WIM1` payload and the `WMB1` bundle** of `wai.neural.int_mlicpp`: integer-payloads §14. Its decode gives the entropy network every element of `y` not yet decoded as 0, and its three networks and its tables share one `afrac` in `1..=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):
|
||
|
||
1. **`wai.neural.int_synth`.**
|
||
- A set whose shift is 128 or more, whose `k` or `stride` is 0, or whose certificate fails `acc64`, is refused.
|
||
- A set with no layers, or with a layer whose `out_ch` is 0, is refused, and so is one with a field of 2^32 or more. The direct decoder decoded each of these where the shapes allowed: no layers presented the latents themselves as the image, and a layer of no channels left the next with only its bias.
|
||
- A payload whose reserved byte is not 0, or whose `C` is 0, is refused. The direct decoder checked neither.
|
||
- A shift from 64 to 127 now has its exact meaning.
|
||
- The caps apply.
|
||
2. **`wai.world.model`'s integer renderer** refuses values outside its lowered `check` ranges, and a set whose `afrac` is outside `1..=30` or differs between its two networks.
|
||
3. **`WHM1` and `WAM1` layers.** A layer whose predicate fails at 0 (a bias of magnitude `2^63 − 1` or more) is refused at load, not at decode. So is a set in which a `check` range cannot hold any value its producer can give (§8.1), which today refuses every decode with `field_range`. Each is refused with `model_mismatch`, before any stream is decoded, where the direct decoder refused with `field_range`, or with a stream's code when the stream failed first. A network with no layer of tag 0, 1 or 2, and one whose header `shift` or `gf` is 128 or more where no layer uses it, are refused too (§13.2); the direct decoder decoded each where the shapes allowed. These take effect for `wai.splat.int_codec`, `wai.audio.int_codec`, `wai.neural.int_hyper`, `wai.video.int_hyper` and `wai.neural.int_mlicpp` in this revision; for `wai.audio.int_codec` the `check` ranges include the PCM step's (§13.3), so a set whose last layer cannot give a sample inside it is refused at load, where every decode was refused at that step. For the conditional codec either network can carry the change, and the set is refused after its plan and its shape checks and before either stream (integer-payloads §7), where the direct decoder refused with the code of whichever stream or network failed first. For `wai.neural.int_mlicpp` any of its three networks can, and the bundle is refused after the header and the networks' geometry are checked and before either stream (integer-payloads §14). Its direct decoder made four more of these checks only when a network ran, after the streams and networks before it: a layer that does not fit the channels the layer before it gives; a network whose input is not the payload's channel count; a header `shift` or `gf` of 128 or more that a layer uses; and a leaky ReLU's shift of 64 or more. It refused each with `model_mismatch` too, so only the order changes: where a stream or a value was refused first, the bundle now is. Its graphs' run-time plan also caps a convolution's padded side at `2^26` (§4, §5.2), which its direct decoder did not; such a bundle is refused (`model_mismatch`).
|
||
4. **`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 outside `0..=127`, which today's loader wraps.
|