open-standards/wai/wai-rs/tests/dred_exact_gpu.rs
Transaction Science 829785fcf6 wai-rs: dred_exact passes clippy; GPU test covers the streams; carriage binds DRED to an Opus packet
The carriage test bound every companion to a video base; the two DRED
enhancement layers bind to their one base, wai.audio.opus.packet. The GPU
test now decodes the corpus's streams as well as its payloads.
determinism-tiers §5 and §6 record the profile, and the live-conformance
index lists its corpus.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-22 04:48:48 -04:00

106 lines
4.2 KiB
Rust

//! `dred-int/1` on a GPU (extensions/opus-dred.md §4.8): the corpus's payload vectors and its
//! streams' reconstructions decoded with every graph on each adapter this process finds, through
//! `wai-ops/1-i32` in WGSL (`int_ops::gpu`), give the same integers as the decode on the CPU.
//! Every graph the device runs is also run on the interpreter and the two compared
//! (`gpu::with_gpu_compared`), so the equality is shown graph by graph.
//!
//! `WAI_DRED_WEIGHTS` names the weights file (`live-conformance/dred/fetch.sh`). **A skip is not
//! a pass:** with no adapter the test prints `dred-gpu: SKIPPED` and fails when
//! `WAI_OPS_GPU_REQUIRE` is set. Each adapter's result is one line, `dred-gpu: key=…`, appended
//! to `WAI_OPS_GPU_REPORT` when it is set.
use std::io::Write;
use std::path::PathBuf;
use std::sync::Arc;
use serde_json::Value;
use wai::dred_exact::{corpus, packet, payload, row_bytes, scenario, Model};
use wai::int_ops::gpu::{self, wai_ops_gpu};
fn report(line: &str) {
println!("{line}");
if let Ok(p) = std::env::var("WAI_OPS_GPU_REPORT") {
let mut f = std::fs::OpenOptions::new().create(true).append(true).open(&p).unwrap();
writeln!(f, "{line}").unwrap();
}
}
fn unhex(s: &str) -> Vec<u8> {
(0..s.len()).step_by(2).map(|i| u8::from_str_radix(&s[i..i + 2], 16).unwrap()).collect()
}
#[test]
fn the_profile_on_every_adapter_gives_the_cpus_integers() {
let w = std::env::var("WAI_DRED_WEIGHTS").expect("WAI_DRED_WEIGHTS must name the draft's weights file (live-conformance/dred/fetch.sh)");
let model = Model::from_weights(&std::fs::read(&w).unwrap()).unwrap();
let corpus: Value = serde_json::from_slice(&std::fs::read(PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../live-conformance/dred/vectors.json")).unwrap()).unwrap();
let payloads: Vec<payload::Payload> = corpus["payloads"]
.as_array()
.unwrap()
.iter()
.filter(|v| v["outcome"] == "dred")
.map(|v| {
let p = unhex(v["packet"].as_str().unwrap());
let (b, _) = packet::find_dred(&p).unwrap().unwrap();
payload::parse(b, None).unwrap()
})
.collect();
// Each stream's packets, received or lost, for its reconstruction.
let streams: Vec<Vec<Option<Vec<u8>>>> = corpus_streams(&corpus);
let decode_all = || {
let mut out: Vec<Vec<u8>> = payloads.iter().map(|p| row_bytes(&model.decode(p, usize::MAX).unwrap())).collect();
for packets in &streams {
let slots = scenario::plan(packets, 8);
out.push(scenario::buffer(&slots, &scenario::rebuild(&model, packets, &slots).unwrap()));
}
out
};
let cpu = decode_all();
let found = wai_ops_gpu::blocking::discover();
let adapters = found.found;
if adapters.is_empty() {
report("dred-gpu: SKIPPED (no adapter)");
assert!(std::env::var("WAI_OPS_GPU_REQUIRE").is_err(), "WAI_OPS_GPU_REQUIRE is set and no adapter was found");
return;
}
for g in adapters {
let g = Arc::new(g);
let (got, stats) = gpu::with_gpu_compared(&g, decode_all);
let same = got == cpu;
report(&format!(
"dred-gpu: key={} payloads={} streams={} same={} graphs_on_gpu={} (acc32 {}, acc64 {}) on_cpu={} compared={} differed={} errors={:?}",
g.adapter().key(),
payloads.len(),
streams.len(),
same,
stats.on_gpu(),
stats.acc32,
stats.acc64,
stats.on_cpu(),
stats.compared,
stats.differed,
stats.errors
));
assert!(same, "the decode on the device gives the CPU's integers");
assert!(stats.on_gpu() > 0 && stats.on_cpu() == 0, "every graph ran on the device");
assert_eq!(stats.differed, 0);
assert!(stats.errors.is_empty());
}
}
fn corpus_streams(corpus: &Value) -> Vec<Vec<Option<Vec<u8>>>> {
corpus["streams"]
.as_object()
.unwrap()
.values()
.map(|st| {
st["objects"]
.as_array()
.unwrap()
.iter()
.map(|o| o.as_str().map(|h| corpus::envelope_payload(&unhex(h)).unwrap().to_vec()))
.collect()
})
.collect()
}