wai: the integer decode paths hold no floating point in their source, and read no JSON with a float parser #635

Merged
dcharlot merged 1 commit from wai/det-source-fixes into main 2026-11-05 07:46:57 -05:00
Owner

What this is

The integer capabilities decode to the same bytes on every machine because no step of their decode rounds. This change takes floating point out of the source of every integer decode path. It also reads the parameter sets' JSON with the strict reader, which converts no number to a float. A test of the sources keeps both true. No payload format and no corpus file changes.

Merge order. This branch sits on #632's commit, 946f3beda, which moves changelog entries into wai/changes/ and the conformance workflow's lists into step files. Merge #632 first; until it merges, this pull request's diff also shows #632's change.

What it carries. One commit, e8f8e0033: the change of wai/det-source-fixes (152aab5c7..9c4159958) in #632's layout. Its message lists the source commits. The branch's changelog entry is a file of wai/changes/. It has no edit to the conformance workflow. Every other file is as the source branch has it.

What it fixes

  • The inverse GDN's integer square root. Each root was seeded from the IEEE-754 square root ((n as f64).sqrt()), and the wide vector kernel took eight at a time with a vector square root; an integer correction then decided the result. The seed is now a fixed-point reciprocal square root, formed exactly in u64 and u128 and within one of the root: a table on the norm's top bits and two Newton steps. The vector kernel takes its roots digit by digit in integer lanes. The roots are the same integers.
  • WIS1 ranges are read, checked and written as binary32 bits (int_payload::wire::Binary32), and lo ≤ hi is compared as IEEE 754 compares two finite values, in integer arithmetic. The rule (integer-payloads §6) is unchanged.
  • The splat JSON form (splat.json) is read from its text with the strict JSON reader: its integers as integer spellings, and each range by int_splat::binary32_of in exact integer arithmetic. integer-payloads §10 now says how a number denotes a binary32 value. This is how the independent converter, int_payload_verify.py, reads a number. The general JSON reader used before disagreed with the converter in two places: it kept the sign of the integer -0, and it rounded an integer past u64. splat_int no longer turns on that reader's float_roundtrip feature for the whole crate; splat_exact, which reads an asset's POSITION bounds as JSON numbers, does.
  • int_entropy::fixed_scale_edges and quantize_scale form ⌊x·2^frac⌋ from the binary32 bits in integers, giving the values the binary64 product and its floor gave.
  • The parameter sets' JSON (entropy.json of wai.neural.int_hyper and wai.video.int_hyper, integer-payloads §9, and a WMB1 bundle's tables, §14) is read by the strict reader, each integer from its spelling. §9 now says how the text is read. Two readings change. -0 is zero, as §9's integer rule already gave. A member named twice, or a value more than 64 levels deep, refuses the set (model_mismatch). The committed sets read as before.
  • Breaking: IntDecoded::Attr's ranges and Wis1::ranges are (Binary32, Binary32) bits. CondEntropy::try_from_value is replaced by CondEntropy::read, which takes the file's bytes. SplatJson::ranges gives binary32 values. SplatJson::from_value, int_splat::decode and int_payload::wis1_from_json take the file's bytes in place of a parsed value. The entry file names each with its migration.

What the checks hold

  • float_free, a test of the sources (a library test under neural_int, run with the crate's unit tests). It fails on a floating-point type, literal or vector intrinsic in any module on the integer decode paths, outside tests and model-checker harnesses. Those modules are the kernels, the operator-set bridge, the payloads, the integer codecs, the rANS coder and its bucketing, the strict JSON reader, and the other integer entropy decoders and the decodes built on them. It reads every file of the operator set (wai-ops) too. It also fails on a call of the general JSON reader's parser in those modules, but for two named lines that are not decodes. Every int_* file, and every file under int_kernels/ and int_payload/, must be among those it reads, so a new one fails it until it is listed. It reads source, not a compiled decode.
  • The root is tested against its definition: an independent digit-by-digit root and u64::isqrt. The test covers the edges of u64: 0, 1, u64::MAX, and every k² − 1, k² and k² + 1 around the powers of two and across the range. An ignored test checks every boundary for k below 2^32.
  • The range rule is held to the float comparison on every class of bit pattern. The scale conversions are checked at every exponent and every frac.
  • The splat reader is tested at a range one digit from the binary64 tie between 1 and the next binary64 value, which reads as binary64 rounds it on each side of the tie. It is also tested at two spellings one last digit apart (4194304.0 and 4194304.5, 8.388608e6 and 8.388609e6), which decode to the two adjacent values the payload presents.
  • The parameter sets' two changed readings are tested through decode_int and through a WMB1 bundle.
  • The integer corpora' existing steps regenerate them and require the committed bytes. This change adds no step and no corpus file.

How it was verified

This commit, e8f8e0033.

  • Locally (git and Python checks only): scripts/wai-conformance-lists.py selftest and check, scripts/wai-conformance-lists-selftest.sh, scripts/check-changelog-fragment.py against main, scripts/changelog-assemble.py --check and scripts/changelog-fragments-selftest.sh all pass. Rebasing from the conversion's first base, #632's earlier tip 14f1abb7c, onto 946f3beda left every file's change the same.
  • On a Linux x86_64 build host: in wai/wai-rs, cargo check --locked --no-default-features --tests with each of neural_int, splat_int and splat_exact; and in wai/byte-exact-conformance, cargo check --locked --tests. All pass.
  • With the other converted branches. This branch merges without conflict with each of the other branches converted to #632's layout (by git merge-tree). Merging all of them in forward, reverse and a mixed order gives one tree. On the build host, that tree passes these runs: cargo check --tests under the derived WAI_FEATURES and under WAI_PQ_FEATURES; cargo check --tests of quickstart,app; the catalogue's library tests; and the wai binary's unit tests.

The tests above, float_free among them, and the corpora's regeneration did not run on this commit here; their results come from this pull request's CI.

On the CI hosts. Results on the CI runners come from this pull request's own CI run. None of the runs above is a CI result.

🤖 Generated with Claude Code

## What this is The integer capabilities decode to the same bytes on every machine because no step of their decode rounds. This change takes floating point out of the source of every integer decode path. It also reads the parameter sets' JSON with the strict reader, which converts no number to a float. A test of the sources keeps both true. No payload format and no corpus file changes. **Merge order.** This branch sits on #632's commit, 946f3beda, which moves changelog entries into `wai/changes/` and the conformance workflow's lists into step files. Merge #632 first; until it merges, this pull request's diff also shows #632's change. **What it carries.** One commit, e8f8e0033: the change of `wai/det-source-fixes` (152aab5c7..9c4159958) in #632's layout. Its message lists the source commits. The branch's changelog entry is a file of `wai/changes/`. It has no edit to the conformance workflow. Every other file is as the source branch has it. ## What it fixes - **The inverse GDN's integer square root.** Each root was seeded from the IEEE-754 square root (`(n as f64).sqrt()`), and the wide vector kernel took eight at a time with a vector square root; an integer correction then decided the result. The seed is now a fixed-point reciprocal square root, formed exactly in `u64` and `u128` and within one of the root: a table on the norm's top bits and two Newton steps. The vector kernel takes its roots digit by digit in integer lanes. The roots are the same integers. - **`WIS1` ranges** are read, checked and written as binary32 bits (`int_payload::wire::Binary32`), and `lo ≤ hi` is compared as IEEE 754 compares two finite values, in integer arithmetic. The rule (integer-payloads §6) is unchanged. - **The splat JSON form** (`splat.json`) is read from its text with the strict JSON reader: its integers as integer spellings, and each range by `int_splat::binary32_of` in exact integer arithmetic. integer-payloads §10 now says how a number denotes a binary32 value. This is how the independent converter, `int_payload_verify.py`, reads a number. The general JSON reader used before disagreed with the converter in two places: it kept the sign of the integer `-0`, and it rounded an integer past `u64`. `splat_int` no longer turns on that reader's `float_roundtrip` feature for the whole crate; `splat_exact`, which reads an asset's `POSITION` bounds as JSON numbers, does. - **`int_entropy::fixed_scale_edges` and `quantize_scale`** form `⌊x·2^frac⌋` from the binary32 bits in integers, giving the values the binary64 product and its floor gave. - **The parameter sets' JSON** (`entropy.json` of `wai.neural.int_hyper` and `wai.video.int_hyper`, integer-payloads §9, and a `WMB1` bundle's tables, §14) is read by the strict reader, each integer from its spelling. §9 now says how the text is read. Two readings change. `-0` is zero, as §9's integer rule already gave. A member named twice, or a value more than 64 levels deep, refuses the set (`model_mismatch`). The committed sets read as before. - **Breaking:** `IntDecoded::Attr`'s ranges and `Wis1::ranges` are `(Binary32, Binary32)` bits. `CondEntropy::try_from_value` is replaced by `CondEntropy::read`, which takes the file's bytes. `SplatJson::ranges` gives binary32 values. `SplatJson::from_value`, `int_splat::decode` and `int_payload::wis1_from_json` take the file's bytes in place of a parsed value. The entry file names each with its migration. ## What the checks hold - **`float_free`**, a test of the sources (a library test under `neural_int`, run with the crate's unit tests). It fails on a floating-point type, literal or vector intrinsic in any module on the integer decode paths, outside tests and model-checker harnesses. Those modules are the kernels, the operator-set bridge, the payloads, the integer codecs, the rANS coder and its bucketing, the strict JSON reader, and the other integer entropy decoders and the decodes built on them. It reads every file of the operator set (`wai-ops`) too. It also fails on a call of the general JSON reader's parser in those modules, but for two named lines that are not decodes. Every `int_*` file, and every file under `int_kernels/` and `int_payload/`, must be among those it reads, so a new one fails it until it is listed. It reads source, not a compiled decode. - **The root** is tested against its definition: an independent digit-by-digit root and `u64::isqrt`. The test covers the edges of `u64`: 0, 1, `u64::MAX`, and every `k² − 1`, `k²` and `k² + 1` around the powers of two and across the range. An ignored test checks every boundary for `k` below `2^32`. - **The range rule** is held to the float comparison on every class of bit pattern. The scale conversions are checked at every exponent and every `frac`. - **The splat reader** is tested at a range one digit from the binary64 tie between `1` and the next binary64 value, which reads as binary64 rounds it on each side of the tie. It is also tested at two spellings one last digit apart (`4194304.0` and `4194304.5`, `8.388608e6` and `8.388609e6`), which decode to the two adjacent values the payload presents. - **The parameter sets'** two changed readings are tested through `decode_int` and through a `WMB1` bundle. - **The integer corpora**' existing steps regenerate them and require the committed bytes. This change adds no step and no corpus file. ## How it was verified **This commit, e8f8e0033.** - **Locally** (git and Python checks only): `scripts/wai-conformance-lists.py selftest` and `check`, `scripts/wai-conformance-lists-selftest.sh`, `scripts/check-changelog-fragment.py` against main, `scripts/changelog-assemble.py --check` and `scripts/changelog-fragments-selftest.sh` all pass. Rebasing from the conversion's first base, #632's earlier tip 14f1abb7c, onto 946f3beda left every file's change the same. - **On a Linux x86_64 build host:** in `wai/wai-rs`, `cargo check --locked --no-default-features --tests` with each of `neural_int`, `splat_int` and `splat_exact`; and in `wai/byte-exact-conformance`, `cargo check --locked --tests`. All pass. - **With the other converted branches.** This branch merges without conflict with each of the other branches converted to #632's layout (by `git merge-tree`). Merging all of them in forward, reverse and a mixed order gives one tree. On the build host, that tree passes these runs: `cargo check --tests` under the derived `WAI_FEATURES` and under `WAI_PQ_FEATURES`; `cargo check --tests` of `quickstart,app`; the catalogue's library tests; and the `wai` binary's unit tests. The tests above, `float_free` among them, and the corpora's regeneration did not run on this commit here; their results come from this pull request's CI. **On the CI hosts.** Results on the CI runners come from this pull request's own CI run. None of the runs above is a CI result. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
wai/det-source-fixes, converted to the layout of the fix: no shared append point
All checks were successful
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (pull_request) Successful in 2m1s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 25s
wai cross-architecture byte-equality / macos-arm64 (reference) (pull_request) Successful in 34s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 13m23s
joule-moq-web browser client / joule-moq-web · browser end-to-end (macos) (pull_request) Successful in 22m1s
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (pull_request) Successful in 13s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 30s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 8m13s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 1m58s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 13m35s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 37s
wai-pack / wai-pack · linux-arm64 (pull_request) Successful in 2m10s
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (pull_request) Successful in 1m14s
wai-fleet service / service · macos (pull_request) Successful in 46s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 19m28s
wai-ledger service / service · macos (pull_request) Successful in 1m8s
wai-pack / wai-pack · the floor through a media prober (pull_request) Successful in 22s
wai-pack / wai-pack · dependency licences (pull_request) Successful in 17s
wai-pack / wai-pack · macos (pull_request) Successful in 2m28s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (pull_request) Successful in 2m46s
WAI conformance / each feature of the independent verifiers' steps compiles alone (pull_request) Successful in 12m58s
wai-quantum studio and desktop / desktop app · macos (pull_request) Successful in 3m29s
wai-pack / wai-pack · independent verifier (Python) (pull_request) Successful in 7m30s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (pull_request) Successful in 14m5s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (pull_request) Successful in 4m57s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 15m46s
wai-ops-gpu integer operators on GPUs / Metal GPU and browser WebGPU (macos) (pull_request) Successful in 10m40s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (pull_request) Successful in 11m35s
WAI Studio / Studio in Chromium and Firefox · macos (pull_request) Successful in 10m40s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 1h30m4s
e8f8e00337
The change of wai/det-source-fixes (152aab5c7..9c4159958, 15 commits), on the fix branch: its
changelog entries as files of wai/changes/, its wai-conformance.yml edits as step files,
corpus files and test tags, and every other file as the branch has it.

- a5bb23634 wai-rs: the inverse GDN's integer roots take no floating point
- b9463b4d0 wai-rs: the splat ranges and JSON form are read in integer arithmetic
- b20be5196 wai-rs: int_entropy's scale conversions are formed in integers
- 437669081 wai-rs: a test that the integer decode paths hold no floating point
- 36aade4f1 wai: the integer decode paths without floating point, in the changelog and integer-payloads §10
- f781bf091 wai-rs: the AVX-512 root's comment states the bound s + bit keeps
- 799ab8476 wai-rs: float_free reads the other integer entropy decoders too
- b79447e64 wai-rs: splat ranges one last digit apart decode to adjacent values
- 43f75cc36 wai: the changelog names the decoders float_free reads and the last-digit pairs
- 2e308fd28 wai-rs: the parameter sets' JSON is read by the strict reader
- 42f848583 wai-rs: int_payload re-exports Binary32
- d1dbe4936 wai-rs: float_free reads the operator set, every integer module, and serde_json's parser
- 931699331 wai: the parameter sets' JSON read strictly, in integer-payloads §9 and §14 and the changelog
- 97d133380 wai-rs: float_free names what a decode reaches beyond what it reads
- 9c4159958 wai: the changelog names the general JSON reader once

Co-Authored-By: Claude Opus 5.5 <[email protected]>
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
Transaction-Science/open-standards!635
No description provided.