Completes the switch (67fa63e4).
- tests/int_audio_ops.rs: the scan. Both committed audio sets (the one the payloads
decode against, and the synthetic one) have layers, header shifts under 128, no
bias of magnitude 2^63 - 1 or more, and lower and load under acc64 (the PCM step's
range included); every committed payload (the vector, the fourteen fixtures, the
staged base's) decodes or is refused through the lowering exactly as directly.
- integer-payloads: 5 cites wam1/1 with t_out as its scalar and the PCM step (and
states its refusal); 7's set rules and order add the synthesis lowered and loaded
after its shape checks; Exactness names WIA1's lowering and its PCM step.
- integer-operators: the status note names wai.audio.int_codec; 15.3 item 3 says its
breaks take effect for it too, the PCM step's range among them.
- SPEC 8's planned breaks and the CHANGELOG entry (the decode, the breaks).
Measured (int_payload_cost): with the set loaded, the audio decode holds 1312099
bytes against 1312704 declared (its largest layer's input, uncropped plane and
output, as the payload text counts them).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
499 lines
27 KiB
Rust
499 lines
27 KiB
Rust
//! `wai.audio.int_codec` through the integer operator set: its lowering `wam1/1`
|
||
//! (`extensions/integer-operators.md` §13.3, with the PCM step) and the certificate of
|
||
//! every committed set (§8.5), as this crate loads them (`int_ops`).
|
||
//!
|
||
//! The lowered graphs and certificates are committed under `ops-conformance/certificates/`,
|
||
//! where `wai-verify` and the independent checker (`tools/wai_ops_check.py`) each
|
||
//! reproduce them byte for byte (`wai-ops.yml`). Here the lowering this crate runs, from
|
||
//! the model it parses (`int_audio::ops::wam1_net`), must write those same bytes, and the
|
||
//! load a sink makes must give the verdict the certificate records.
|
||
//!
|
||
//! The decode through the lowering (`int_audio::ops`) must give, on every committed
|
||
//! vector, the samples of the direct decode (`int_audio::legacy`) and the vector's
|
||
//! committed digests.
|
||
|
||
use std::path::PathBuf;
|
||
|
||
use serde_json::Value;
|
||
use wai::int_audio::{self, AudioJson, AudioModel, AudioStream};
|
||
use wai_ops::Profile;
|
||
|
||
const CAP: &str = "wai.audio.int_codec";
|
||
|
||
fn wai() -> PathBuf {
|
||
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
|
||
}
|
||
|
||
fn read(rel: &str) -> Vec<u8> {
|
||
std::fs::read(wai().join(rel)).unwrap_or_else(|e| panic!("{rel}: {e}"))
|
||
}
|
||
|
||
fn json(rel: &str) -> Value {
|
||
serde_json::from_slice(&read(rel)).unwrap_or_else(|e| panic!("{rel}: {e}"))
|
||
}
|
||
|
||
fn sha256(b: &[u8]) -> String {
|
||
wai_ops::sha256::sha256(b).iter().map(|x| format!("{x:02x}")).collect()
|
||
}
|
||
|
||
/// The `wai.audio.int_codec` sets `ops-conformance/certificates/index.json` lists:
|
||
/// `(name, synthetic, set file, set digest, lowered graph file)`, each relative to `wai/`.
|
||
fn indexed() -> Vec<(String, bool, String, String, String)> {
|
||
json("ops-conformance/certificates/index.json")["sets"]
|
||
.as_array()
|
||
.unwrap()
|
||
.iter()
|
||
.filter(|s| s["capability"] == CAP)
|
||
.map(|s| {
|
||
assert_eq!(s["lowering"], "wam1/1");
|
||
let roles = s["roles"].as_array().unwrap();
|
||
assert_eq!(roles.len(), 1, "wam1/1 lowers an audio set to one graph, g_s");
|
||
(
|
||
s["name"].as_str().unwrap().to_string(),
|
||
s["synthetic"].as_bool().unwrap(),
|
||
s["members"]["model.wam"].as_str().unwrap().to_string(),
|
||
s["set_sha256"].as_str().unwrap().to_string(),
|
||
roles[0].as_str().unwrap().to_string(),
|
||
)
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
/// The graph this crate lowers from the model it parses: `int_audio::ops::wam1_net`,
|
||
/// then `wam1/1` with the PCM step.
|
||
fn lowered_from_the_parsed_model(set: &[u8]) -> wai_ops::Graph {
|
||
let m = AudioModel::from_bytes(set).expect("the WAM1 set parses");
|
||
let net = wai::int_audio::ops::wam1_net(&m).expect("a model parsed from bytes converts");
|
||
assert!(net == wai_ops::lower::parse_wam1(set).unwrap(), "the converted model is the one wai-ops parses from the bytes");
|
||
wai_ops::lower::wam1_net(&net).expect("the set lowers")
|
||
}
|
||
|
||
/// Gate 1 of the migration: every committed `wai.audio.int_codec` set (the conformance
|
||
/// vector's and the synthetic one of `ops-conformance/sets/`) lowers to its committed
|
||
/// graph, from its bytes (`lower::wam1`) and from the model this crate parses; the
|
||
/// certificate beside it is that graph's; and loading the graph under the capability's
|
||
/// profile gives the certificate's verdict. Each set carries `check` ranges (§13.2,
|
||
/// §13.3), so it meets `acc64` by construction and not `acc32`, first failing at `g_s`'s
|
||
/// input range (the vector's at 47 bits, as Appendix C predicts); and the PCM step's
|
||
/// `crop`, whose length is a scalar, keeps it out of the accelerator subset.
|
||
#[test]
|
||
fn every_committed_set_lowers_to_its_committed_graph_and_verdict() {
|
||
let sets = indexed();
|
||
let mut names: Vec<(&str, bool)> = sets.iter().map(|s| (s.0.as_str(), s.1)).collect();
|
||
names.sort();
|
||
assert_eq!(names, [("audio", false), ("audio", true)], "the committed audio sets");
|
||
for (name, synthetic, set, digest, role) in &sets {
|
||
let name = if *synthetic { format!("{name} (synthetic)") } else { name.clone() };
|
||
let bytes = read(set);
|
||
let g = wai_ops::lower::wam1(&bytes).unwrap_or_else(|r| panic!("{name}: does not lower: {r:?}"));
|
||
// The verdict first: the load a sink makes.
|
||
let loaded = wai::int_ops::load(&g).unwrap_or_else(|r| panic!("{name}: refused under acc64: {r:?}"));
|
||
assert!(!loaded.meets(Profile::Acc32), "{name}: a set with check ranges inside its graph does not meet acc32");
|
||
// Then the committed bytes: the set, its lowered graph (both ways) and its certificate.
|
||
assert_eq!(&sha256(&bytes), digest, "{name}: the set's digest");
|
||
let graph = g.to_bytes();
|
||
assert!(graph == read(role), "{name}: the lowering of the bytes differs from the committed {role}");
|
||
assert!(lowered_from_the_parsed_model(&bytes).to_bytes() == graph, "{name}: the lowering of the parsed model differs from the committed {role}");
|
||
let cert = json(&format!("ops-conformance/certificates/{CAP}/{digest}.json"));
|
||
assert_eq!(cert["set_sha256"].as_str(), Some(digest.as_str()), "{name}");
|
||
assert_eq!(cert["lowering"], "wam1/1", "{name}");
|
||
assert_eq!(cert["graphs"][0]["name"], "g_s", "{name}");
|
||
assert_eq!(cert["graphs"][0]["graph_sha256"].as_str(), Some(sha256(&graph).as_str()), "{name}: the certificate is the graph's");
|
||
let v = &cert["verdict"];
|
||
assert_eq!((v["acc64"].as_bool(), v["acc32"].as_bool()), (Some(true), Some(false)), "{name}: the certificate's verdict");
|
||
let first = &v["acc32_first_failure"];
|
||
assert_eq!(
|
||
(first["graph"].as_str(), first["where"].as_str(), first["index"].as_str(), first["key"].as_str()),
|
||
(Some("g_s"), Some("input"), Some("0"), Some("range")),
|
||
"{name}: the first acc32 failure is g_s's input range"
|
||
);
|
||
assert!(v["npu_reasons"].as_array().unwrap().iter().any(|r| r == "crop_scalar"), "{name}: the PCM step's crop");
|
||
let x0 = &cert["graphs"][0]["inputs"][0];
|
||
assert_eq!(x0["policy"].as_str(), Some("check"), "{name}");
|
||
assert_eq!(x0["hi"].as_str(), Some(g.tensors[0].hi.to_string().as_str()), "{name}: the input's range is X_0");
|
||
if !synthetic {
|
||
assert_eq!(first["bits"].as_str(), Some("47"), "{name}: Appendix C's 47 bits");
|
||
assert_eq!(g.tensors[0].hi, 56322496561155, "{name}");
|
||
}
|
||
// The scalar is t_out, `[1, 2^26]` (§13.0's driver contract).
|
||
assert_eq!((g.scalars.len(), g.scalars[0].lo, g.scalars[0].hi), (1, 1, 1 << 26), "{name}: scalar 0 is t_out");
|
||
}
|
||
}
|
||
|
||
/// The typed form of a parsed `WIA1` payload.
|
||
fn stream<'a>(a: &'a wai::int_wire::Wia1<'a>) -> AudioStream<'a> {
|
||
AudioStream {
|
||
afrac: a.afrac as u32, n_ch: a.n_ch as usize, lat_len: a.lat_len as usize, t_out: a.t_out as usize,
|
||
offset: a.offset, median_q: &a.median_q, cdfs: &a.cdfs, rans: a.rans,
|
||
}
|
||
}
|
||
|
||
/// The `wai.audio.int_codec` vectors `int-payload-conformance/vectors.json` indexes:
|
||
/// `(name, payload, set member model.wam, JSON form, sidecar)`, each relative to `wai/`.
|
||
fn vectors() -> Vec<(String, String, String, String, String)> {
|
||
json("int-payload-conformance/vectors.json")["vectors"]
|
||
.as_array()
|
||
.unwrap()
|
||
.iter()
|
||
.filter(|v| v["capability"] == CAP)
|
||
.map(|v| {
|
||
let s = |k: &Value| k.as_str().unwrap().to_string();
|
||
(s(&v["name"]), s(&v["payload"]), s(&v["set"]["model.wam"]), s(&v["converted_from"]["json"]), s(&v["sidecar"]))
|
||
})
|
||
.collect()
|
||
}
|
||
|
||
fn le_bytes(pcm: &[i16]) -> Vec<u8> {
|
||
pcm.iter().flat_map(|v| v.to_le_bytes()).collect()
|
||
}
|
||
|
||
/// Gate 2 of the migration: golden equality. Every committed `wai.audio.int_codec`
|
||
/// vector decodes through the lowering (`int_audio::ops`) to the samples the direct
|
||
/// decode gives (`int_audio::legacy`), and to every digest the corpus commits for it:
|
||
/// the SHA-256 sidecar of the PCM and the export's FNV-1a. The payload (`WIA1`) and the
|
||
/// JSON form it is converted from both decode, and alike. The staged audio base carries
|
||
/// the same payload, so it is covered too.
|
||
#[test]
|
||
fn every_committed_vector_decodes_through_the_lowering_to_its_golden() {
|
||
let vs = vectors();
|
||
assert_eq!(vs.len(), 1, "the committed audio vectors");
|
||
for (name, payload, set, from, sidecar) in &vs {
|
||
let m = AudioModel::from_bytes(&read(set)).unwrap();
|
||
let p = read(payload);
|
||
let a = wai::int_wire::parse_wia1(&p).unwrap_or_else(|e| panic!("{name}: {e:?}"));
|
||
let j = AudioJson::from_value(&json(from)).unwrap();
|
||
let want = json(sidecar)[name]["sha256"].as_str().unwrap().to_string();
|
||
let fnv = json("wai-rs/tests/vectors/int_audio/audio.json")["pcm_fnv1a"].as_str().unwrap().to_string();
|
||
for (form, st) in [("WIA1", stream(&a)), ("JSON", j.stream())] {
|
||
let ops = int_audio::ops::decode_typed(&m, &st).unwrap_or_else(|r| panic!("{name} {form}: {r}"));
|
||
let direct = int_audio::legacy::decode_typed(&m, &st).unwrap_or_else(|r| panic!("{name} {form}: {r}"));
|
||
assert!(ops == direct, "{name} {form}: the decode through the lowering differs from the direct decode");
|
||
assert_eq!(ops.len(), st.t_out, "{name} {form}: t_out samples");
|
||
assert_eq!(sha256(&le_bytes(&ops)), want, "{name} {form}: the SHA-256 sidecar");
|
||
assert_eq!(format!("{:016x}", int_audio::pcm_fnv1a(&ops)), fnv, "{name} {form}: the export's FNV-1a");
|
||
}
|
||
let base = read("staged-conformance/cases/audio_int_codec_1l/base.wai");
|
||
assert!(base.windows(p.len()).any(|w| w == p.as_slice()), "{name}: the staged audio base carries this payload");
|
||
}
|
||
}
|
||
|
||
/// The committed vector's set and payload (`int-payload-conformance`'s `audio`).
|
||
const SET: &str = "wai-rs/tests/vectors/int_audio/audio_model.bin";
|
||
const PAYLOAD: &str = "wai-rs/tests/vectors/int_audio/audio_bitstream.wia1";
|
||
|
||
/// A typed clip that owns its tables, so a fixture can change any field.
|
||
#[derive(Clone)]
|
||
struct Owned {
|
||
afrac: u32,
|
||
n_ch: usize,
|
||
lat_len: usize,
|
||
t_out: usize,
|
||
offset: i32,
|
||
median_q: Vec<i64>,
|
||
cdfs: Vec<Vec<i32>>,
|
||
rans: Vec<u8>,
|
||
}
|
||
|
||
impl Owned {
|
||
fn committed() -> Owned {
|
||
let p = read(PAYLOAD);
|
||
let a = wai::int_wire::parse_wia1(&p).unwrap();
|
||
Owned {
|
||
afrac: a.afrac as u32, n_ch: a.n_ch as usize, lat_len: a.lat_len as usize, t_out: a.t_out as usize,
|
||
offset: a.offset, median_q: a.median_q.clone(), cdfs: a.cdfs.clone(), rans: a.rans.to_vec(),
|
||
}
|
||
}
|
||
/// `n_ch` channels of `lat_len` latents at `afrac` 12, every latent of channel `k`
|
||
/// equal to `values[k]` (one table symbol each, its median placing it), presenting
|
||
/// `t_out` samples.
|
||
fn constant(values: &[i64], lat_len: usize, t_out: usize) -> Owned {
|
||
let (t, offset) = (vec![0, 1 << 15, 1 << 16], 3);
|
||
let mut enc = wai::rans::RansEncoder::new();
|
||
for _ in 0..values.len() * lat_len {
|
||
assert!(enc.encode(offset, &t, 3, offset));
|
||
}
|
||
Owned {
|
||
afrac: 12, n_ch: values.len(), lat_len, t_out, offset,
|
||
median_q: values.iter().map(|v| v - ((offset as i64) << 12)).collect(), cdfs: vec![t; values.len()], rans: enc.flush(),
|
||
}
|
||
}
|
||
fn st(&self) -> AudioStream<'_> {
|
||
AudioStream {
|
||
afrac: self.afrac, n_ch: self.n_ch, lat_len: self.lat_len, t_out: self.t_out, offset: self.offset,
|
||
median_q: &self.median_q, cdfs: &self.cdfs, rans: &self.rans,
|
||
}
|
||
}
|
||
}
|
||
|
||
/// The committed set, parsed, changed by `f`.
|
||
fn model(f: impl FnOnce(&mut AudioModel)) -> AudioModel {
|
||
let mut m = AudioModel::from_bytes(&read(SET)).unwrap();
|
||
f(&mut m);
|
||
m
|
||
}
|
||
|
||
/// The committed clip, changed by `f`.
|
||
fn clip(f: impl FnOnce(&mut Owned)) -> Owned {
|
||
let mut o = Owned::committed();
|
||
f(&mut o);
|
||
o
|
||
}
|
||
|
||
/// The code integer-payloads §8 gives a refusal of the audio decode.
|
||
fn code_of(r: wai::int_hyper_synth::Refused) -> &'static str {
|
||
wai::int_payload::IntPayloadError::from(r).code()
|
||
}
|
||
|
||
/// Both decodes of a clip against a model: through the lowering, then directly.
|
||
fn both(m: &AudioModel, o: &Owned) -> (Result<Vec<i16>, &'static str>, Result<Vec<i16>, &'static str>) {
|
||
(int_audio::ops::decode_typed(m, &o.st()).map_err(code_of), int_audio::legacy::decode_typed(m, &o.st()).map_err(code_of))
|
||
}
|
||
|
||
/// Gate 3 of the migration: refusal parity, on the committed fixtures. Each
|
||
/// `wai.audio.int_codec` fixture of `int-payload-conformance` is refused by a sink with
|
||
/// its code; one that parses (`decode`) is refused by the decode through the lowering and
|
||
/// by the direct decode alike, with that code.
|
||
#[test]
|
||
fn every_committed_refusal_fixture_keeps_its_code() {
|
||
let mut n = 0;
|
||
for f in json("int-payload-conformance/vectors.json")["refusals"].as_array().unwrap().iter().filter(|f| f["capability"] == CAP) {
|
||
let (name, want) = (f["name"].as_str().unwrap(), f["code"].as_str().unwrap());
|
||
let (p, set) = (read(f["payload"].as_str().unwrap()), read(f["set"]["model.wam"].as_str().unwrap()));
|
||
let params = wai::int_payload::resolve(CAP, None, &[wai::pin::HeldBytes::File(&set)], None).expect("an unpinned set resolves");
|
||
let sink = wai::int_payload::decode_int(CAP, &p, ¶ms).map(|_| ()).map_err(|e| e.code());
|
||
assert_eq!(sink, Err(want), "{name}: the sink's code");
|
||
if f["layer"] == "decode" {
|
||
let m = AudioModel::from_bytes(&set).unwrap();
|
||
let a = wai::int_wire::parse_wia1(&p).unwrap_or_else(|e| panic!("{name}: a decode fixture parses: {e:?}"));
|
||
let ops = int_audio::ops::decode_typed(&m, &stream(&a)).err().map(code_of);
|
||
let direct = int_audio::legacy::decode_typed(&m, &stream(&a)).err().map(code_of);
|
||
assert_eq!((ops, direct), (Some(want), Some(want)), "{name}: through the lowering, then directly");
|
||
}
|
||
n += 1;
|
||
}
|
||
assert!(n >= 14, "the committed audio fixtures: {n}");
|
||
}
|
||
|
||
/// Gate 3, beside the committed fixtures: a fixture for each refusal the direct decode
|
||
/// makes (integer-payloads §5 and §7, in their order), each made from the committed set
|
||
/// and clip by one change. Each is refused by both decodes with the same code; the code
|
||
/// it states is integer-payloads §8's for that rule. The committed set and clip decode.
|
||
#[test]
|
||
fn every_refusal_of_the_direct_decode_is_refused_alike_through_the_lowering() {
|
||
use int_audio::ALayer;
|
||
let (m, base) = (model(|_| {}), Owned::committed());
|
||
assert!(int_audio::ops::decode_typed(&m, &base.st()).is_ok(), "the fixtures' base decodes");
|
||
let same = || model(|_| {});
|
||
let last = |m: &mut AudioModel| m.layers.len() - 1;
|
||
let fixtures: Vec<(&str, &str, AudioModel, Owned)> = vec![
|
||
("the set's afrac of 0", "model_mismatch", model(|m| m.afrac = 0), clip(|_| {})),
|
||
("the set's afrac of 31", "model_mismatch", model(|m| m.afrac = 31), clip(|_| {})),
|
||
("a clip afrac not the set's", "model_mismatch", same(), clip(|o| o.afrac = 11)),
|
||
("no latent channels", "field_range", same(), clip(|o| o.n_ch = 0)),
|
||
("a latent length of 0", "field_range", same(), clip(|o| o.lat_len = 0)),
|
||
("a t_out of 0", "field_range", same(), clip(|o| o.t_out = 0)),
|
||
("a median short", "field_range", same(), clip(|o| { o.median_q.pop(); })),
|
||
("a table short", "field_range", same(), clip(|o| { o.cdfs.pop(); })),
|
||
("a median past 2^47", "field_range", same(), clip(|o| o.median_q[3] = -(1 << 47) - 1)),
|
||
("a table that does not end at 2^16", "cdf_malformed", same(), clip(|o| *o.cdfs[2].last_mut().unwrap() = 65535)),
|
||
("a table of another length", "cdf_malformed", same(), clip(|o| { o.cdfs[5].pop(); *o.cdfs[5].last_mut().unwrap() = 1 << 16; })),
|
||
("a latent past 2^26", "too_large", same(), clip(|o| o.lat_len = 1 << 23)),
|
||
("a t_out past 2^26", "too_large", same(), clip(|o| o.t_out = (1 << 26) + 1)),
|
||
("an uncropped buffer past 2^26", "too_large", same(), clip(|o| (o.lat_len, o.t_out) = (1 << 22, 1 << 25))),
|
||
("a layer's bias short", "model_mismatch", model(|m| {
|
||
if let ALayer::ConvT1d { bias, .. } = &mut m.layers[0] { bias.pop(); }
|
||
}), clip(|_| {})),
|
||
("a stride of 0", "model_mismatch", model(|m| {
|
||
if let ALayer::ConvT1d { stride, .. } = &mut m.layers[2] { *stride = 0; }
|
||
}), clip(|_| {})),
|
||
("an inverse GDN short of a beta", "model_mismatch", model(|m| {
|
||
if let ALayer::Igdn { beta, .. } = &mut m.layers[1] { beta.pop(); }
|
||
}), clip(|_| {})),
|
||
("padding that leaves no output", "model_mismatch", model(|m| {
|
||
let l = last(m);
|
||
if let ALayer::ConvT1d { pad, .. } = &mut m.layers[l] { *pad = 1 << 20; }
|
||
}), clip(|_| {})),
|
||
("a synthesis of 16 channels", "model_mismatch", model(|m| { m.layers.pop(); }), clip(|_| {})),
|
||
("a t_out past the synthesis", "geometry_mismatch", same(), clip(|o| o.t_out += 1)),
|
||
("a latent longer than t_out needs", "geometry_mismatch", same(), clip(|o| o.t_out -= 512)),
|
||
("a stream too short to be one", "field_range", same(), clip(|o| o.rans.truncate(4))),
|
||
("a stream that runs out", "stream_exhausted", same(), clip(|o| { let n = o.rans.len() / 2; o.rans.truncate(n - n % 4); })),
|
||
("a word left unread", "stream_noncanonical", same(), clip(|o| o.rans.extend([0, 0, 0, 0]))),
|
||
("every latent past layer 0's range", "field_range", same(), clip(|o| o.median_q.iter_mut().for_each(|m| *m = 1 << 47))),
|
||
("every latent below it", "field_range", same(), clip(|o| o.median_q.iter_mut().for_each(|m| *m = -(1 << 47)))),
|
||
];
|
||
for (what, want, m, o) in &fixtures {
|
||
let (ops, direct) = both(m, o);
|
||
assert_eq!((ops.err(), direct.err()), (Some(*want), Some(*want)), "{what}: through the lowering, then directly");
|
||
}
|
||
}
|
||
|
||
/// Gate 3, at the edge of every `check` range of the committed set: for each of its
|
||
/// layers, a network of that layer alone (with the set's header), followed by a
|
||
/// transposed convolution to one channel whose weights and bias are 0 (whose own range
|
||
/// is all of `i64`), decodes a clip whose latents all lie in `[−X_ℓ, X_ℓ]`, with one
|
||
/// channel at `X_ℓ` (or `−X_ℓ`), to the same samples both ways, and refuses one at
|
||
/// `X_ℓ + 1` (or `−X_ℓ − 1`) both ways (`field_range`), where `X_ℓ` is tensor `ℓ`'s
|
||
/// range in the committed graph. And the PCM step's range: a network adding a bias to
|
||
/// its one channel decodes a sample at the top of the range in which
|
||
/// `v·32767 + 2^(afrac−1)` fits `i64`, and the bottom, and refuses one past each, both
|
||
/// ways.
|
||
#[test]
|
||
fn every_check_range_refuses_exactly_what_the_direct_decode_refuses() {
|
||
use int_audio::ALayer;
|
||
let full = wai_ops::lower::wam1(&read(SET)).unwrap();
|
||
let n = AudioModel::from_bytes(&read(SET)).unwrap().layers.len();
|
||
for l in 0..n {
|
||
let one = model(|m| {
|
||
let layer = m.layers.swap_remove(l);
|
||
let c = match &layer {
|
||
ALayer::ConvT1d { out_ch, .. } => *out_ch,
|
||
ALayer::Igdn { c, .. } => *c,
|
||
};
|
||
m.layers = vec![layer, ALayer::ConvT1d { out_ch: 1, k: 1, stride: 1, pad: 0, opad: 0, weight: vec![0; c], mult: vec![1], bias: vec![0] }];
|
||
});
|
||
let g = wai_ops::lower::wam1_net(&int_audio::ops::wam1_net(&one).unwrap()).unwrap();
|
||
let (c, x) = (g.tensors[0].channels as usize, g.tensors[0].hi);
|
||
assert_eq!((g.tensors[0].lo, x), (full.tensors[l].lo, full.tensors[l].hi), "layer {l}: the range of tensor {l}");
|
||
// The clip's length: the layer's output length on 4 latents.
|
||
let t = match &one.layers[0] {
|
||
ALayer::ConvT1d { k, stride, pad, opad, .. } => 3 * stride + k + opad - 2 * pad,
|
||
ALayer::Igdn { .. } => 4,
|
||
};
|
||
let at = |v: i64| {
|
||
let mut vals = vec![0i64; c];
|
||
vals[c / 2] = v;
|
||
Owned::constant(&vals, 4, t)
|
||
};
|
||
for v in [x, -x] {
|
||
let (ops, direct) = both(&one, &at(v));
|
||
assert!(ops.is_ok() && ops == direct, "layer {l}: at {v}: {ops:?} through the lowering, {direct:?} directly");
|
||
}
|
||
for v in [x + 1, -x - 1] {
|
||
assert_eq!(both(&one, &at(v)), (Err("field_range"), Err("field_range")), "layer {l}: one past the range, {v}");
|
||
}
|
||
}
|
||
// The PCM step: one channel plus a bias.
|
||
let lo = -((1i128 << 63) / 32767) as i64;
|
||
let hi = ((i64::MAX as i128 - (1 << 11)) / 32767) as i64;
|
||
let add = |b: i64| AudioModel {
|
||
afrac: 12, gf: 16, shift: 0,
|
||
layers: vec![ALayer::ConvT1d { out_ch: 1, k: 1, stride: 1, pad: 0, opad: 0, weight: vec![1], mult: vec![1], bias: vec![b] }],
|
||
};
|
||
let one = Owned::constant(&[5 << 12], 4, 4);
|
||
for (b, ok) in [(hi - (5 << 12), Ok(vec![32767; 4])), (lo - (5 << 12), Ok(vec![-32768; 4])), (hi - (5 << 12) + 1, Err("field_range")), (lo - (5 << 12) - 1, Err("field_range"))] {
|
||
let (ops, direct) = both(&add(b), &one);
|
||
assert_eq!((&ops, &direct), (&ok, &ok), "a sample at {}", 5 * 4096 + b);
|
||
}
|
||
}
|
||
|
||
/// Gate 5 of the migration, the whole-decoder half: the decode through the lowering
|
||
/// under the reference kernels (the interpreter computes every operator) and under each
|
||
/// fast policy (the transposed convolutions on this crate's fast kernels, serial and
|
||
/// threaded) gives the same samples, and the same refusals, on every committed vector,
|
||
/// as `WIA1` and as its JSON form, and on clips with a channel moved to the input's
|
||
/// check range and one past it, at each end. The per-operator half is `int_ops`'
|
||
/// equivalence tests, one of which counts the fast kernel's runs on this set.
|
||
#[test]
|
||
fn every_kernel_policy_decodes_through_the_lowering_alike() {
|
||
use wai::int_kernels::{with_policy, Policy};
|
||
const POLICIES: [Policy; 5] = [Policy::reference(), Policy::fast(1), Policy::fast(2), Policy::fast(5), Policy::fast(0)];
|
||
let m = model(|_| {});
|
||
let x0 = wai_ops::lower::wam1(&read(SET)).unwrap().tensors[0].hi;
|
||
let at = |t: i64| clip(move |o| o.median_q[1] = t - ((o.offset as i64) << o.afrac));
|
||
let j = AudioJson::from_value(&json("wai-rs/tests/vectors/int_audio/audio_bitstream.json")).unwrap();
|
||
let mut clips: Vec<(String, Owned)> = vec![
|
||
("WIA1".into(), Owned::committed()),
|
||
("JSON".into(), Owned {
|
||
afrac: j.afrac, n_ch: j.n_ch, lat_len: j.lat_len, t_out: j.t_out, offset: j.offset,
|
||
median_q: j.median_q.clone(), cdfs: j.cdfs.clone(), rans: j.rans.clone(),
|
||
}),
|
||
];
|
||
for (what, t) in [("channel 1's median at X_0", x0), ("at X_0 + 1", x0 + 1), ("at -X_0", -x0), ("at -X_0 - 1", -x0 - 1)] {
|
||
clips.push((what.into(), at(t)));
|
||
}
|
||
for (k, (what, o)) in clips.iter().enumerate() {
|
||
let want = with_policy(POLICIES[0], || int_audio::ops::decode_typed(&m, &o.st())).map_err(code_of);
|
||
assert!(k >= 2 || want.is_ok(), "{what}: the committed form decodes");
|
||
for pol in &POLICIES[1..] {
|
||
let got = with_policy(*pol, || int_audio::ops::decode_typed(&m, &o.st())).map_err(code_of);
|
||
assert!(got == want, "{what}: {pol:?} differs from the interpreter alone");
|
||
}
|
||
}
|
||
}
|
||
|
||
/// The committed `wai.audio.int_codec` sets and payloads, relative to `wai/`: every set
|
||
/// and payload `int-payload-conformance/vectors.json` names for the capability (its
|
||
/// vectors and its refusal fixtures), and every base of a staged case decoded through
|
||
/// the capability, with the set its case names and the payload its envelope carries.
|
||
/// Returns `(set, (where, payload))` pairs.
|
||
fn committed_sets_and_payloads() -> Vec<(String, (String, Vec<u8>))> {
|
||
let mut v = Vec::new();
|
||
let index = json("int-payload-conformance/vectors.json");
|
||
for e in index["vectors"].as_array().unwrap().iter().chain(index["refusals"].as_array().unwrap()) {
|
||
if e["capability"] == CAP {
|
||
let (set, p) = (e["set"]["model.wam"].as_str().unwrap(), e["payload"].as_str().unwrap());
|
||
v.push((set.to_string(), (p.to_string(), read(p))));
|
||
}
|
||
}
|
||
let cases = wai().join("staged-conformance/cases");
|
||
let mut dirs: Vec<_> = std::fs::read_dir(&cases).unwrap().filter_map(|e| e.ok()).map(|e| e.path()).collect();
|
||
dirs.sort();
|
||
for d in dirs {
|
||
let Ok(b) = std::fs::read(d.join("case.json")) else { continue };
|
||
let case: Value = serde_json::from_slice(&b).unwrap();
|
||
for base in case["bases"].as_array().into_iter().flatten().filter(|b| b["decoded_via"] == CAP) {
|
||
let f = d.join(base["file"].as_str().unwrap());
|
||
let env = wai::container::Wai::from_bytes(&std::fs::read(&f).unwrap()).unwrap();
|
||
assert_eq!(env.manifest.model_requirement.capability, CAP, "{}", f.display());
|
||
v.push((base["decode"]["set"]["model.wam"].as_str().unwrap().to_string(), (f.display().to_string(), env.payload)));
|
||
}
|
||
}
|
||
v
|
||
}
|
||
|
||
/// The scan for the load rules the switch brings (integer-operators §15.3 item 3): no
|
||
/// committed set or payload is affected. Every committed audio set (the one the
|
||
/// payloads decode against, and the synthetic one of `ops-conformance/sets/`) has
|
||
/// layers, header shifts under 128, no bias of magnitude `2^63 − 1` or more, and lowers
|
||
/// and loads under `acc64`, its PCM step's range included; every committed payload that
|
||
/// the direct decode decodes decodes through the lowering to the same samples, and every
|
||
/// one it refuses is refused with the same code. (These are the files that carry a `WIA1`
|
||
/// payload or name the capability's set; `WIA1` appears in no other committed payload or
|
||
/// envelope.)
|
||
#[test]
|
||
fn the_new_load_rules_affect_no_committed_set_or_payload() {
|
||
use wai_ops::lower::NetLayer as N;
|
||
let all = committed_sets_and_payloads();
|
||
let mut sets: Vec<String> = all.iter().map(|(s, _)| s.clone()).collect();
|
||
sets.extend(indexed().into_iter().map(|s| s.2));
|
||
sets.sort_unstable();
|
||
sets.dedup();
|
||
for set in &sets {
|
||
let bytes = read(set);
|
||
let net = wai_ops::lower::parse_wam1(&bytes).unwrap_or_else(|r| panic!("{set}: {r:?}"));
|
||
assert!(!net.layers.is_empty(), "{set}: no layers");
|
||
assert!(net.shift < 128 && net.gf < 128, "{set}: a header shift of 128 or more");
|
||
for l in &net.layers {
|
||
if let N::ConvT { bias, .. } = l {
|
||
assert!(bias.iter().all(|b| b.unsigned_abs() < i64::MAX as u64), "{set}: a bias of magnitude 2^63 - 1 or more");
|
||
}
|
||
}
|
||
let g = wai_ops::lower::wam1(&bytes).unwrap_or_else(|r| panic!("{set}: does not lower: {r:?}"));
|
||
assert!(wai::int_ops::load(&g).is_ok(), "{set}: does not load under acc64");
|
||
}
|
||
let (mut decoded, mut refused) = (0, 0);
|
||
for (set, (at, p)) in &all {
|
||
let m = AudioModel::from_bytes(&read(set)).unwrap();
|
||
let Ok(a) = wai::int_wire::parse_wia1(p) else {
|
||
refused += 1;
|
||
continue;
|
||
};
|
||
let ops = int_audio::ops::decode_typed(&m, &stream(&a)).map_err(code_of);
|
||
let direct = int_audio::legacy::decode_typed(&m, &stream(&a)).map_err(code_of);
|
||
assert!(ops == direct, "{at}: the decode through the lowering differs from the direct decode");
|
||
if ops.is_ok() { decoded += 1 } else { refused += 1 }
|
||
}
|
||
assert_eq!(sets.len(), 2, "the committed audio sets: {sets:?}");
|
||
assert!(decoded >= 2 && refused >= 14, "the committed audio payloads, at least those of this revision: {decoded} decoded, {refused} refused");
|
||
}
|