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]>
106 lines
4.2 KiB
Rust
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()
|
|
}
|