Registers wai.avatar.face (GenerativeDeterministic, ops face-int/1, one-file parameter set): the WFG1 generator, WFI1 identity prior and WFK1 keypoint segment readers and writers; the driver's integer warp and keypoint maps; the lowering face-int/1 to one wai-ops/1-i32 graph, with the same arithmetic written out loop by loop for the tests; the identity and track records; verification over them and synthesis's claims, log and label policy; the corpus generator (bin wai_avatar_face_vectors) and the corpus and GPU tests. Registry export regenerated. Co-Authored-By: Claude Opus 5.5 <[email protected]>
125 lines
5.9 KiB
Rust
125 lines
5.9 KiB
Rust
//! `wai.avatar.face` (extensions/avatars.md §6 to §13): the committed corpus
|
|
//! `live-conformance/avatar-face/` regenerates byte for byte from its fixed seeds and its pinned
|
|
//! inputs, holds no other file, every vector in it gets what it states, the extension's §10 lists
|
|
//! exactly the reasons the reference reports and the codes its files are refused with, and §7.9's
|
|
//! measurement is the training tool's report. The generator itself asserts that the corpus gives
|
|
//! every one of the reasons.
|
|
#![cfg(feature = "avatar_face")]
|
|
|
|
use std::collections::BTreeMap;
|
|
use std::path::PathBuf;
|
|
use wai::avatar_face::{self, corpus, FormError, Reason};
|
|
|
|
fn wai_dir() -> PathBuf {
|
|
PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("..")
|
|
}
|
|
|
|
fn committed() -> BTreeMap<String, Vec<u8>> {
|
|
let dir = wai_dir().join("live-conformance/avatar-face");
|
|
let mut files = BTreeMap::new();
|
|
for sub in ["", "identities", "tracks"] {
|
|
for e in std::fs::read_dir(dir.join(sub)).expect("the committed corpus").flatten() {
|
|
let name = e.file_name().to_string_lossy().into_owned();
|
|
if e.path().is_dir() || (sub.is_empty() && name == "README.md") {
|
|
continue;
|
|
}
|
|
let key = if sub.is_empty() { name } else { format!("{sub}/{name}") };
|
|
files.insert(key, std::fs::read(e.path()).expect("a file"));
|
|
}
|
|
}
|
|
files
|
|
}
|
|
|
|
fn inputs(files: &BTreeMap<String, Vec<u8>>) -> BTreeMap<String, Vec<u8>> {
|
|
corpus::INPUTS.iter().filter_map(|(n, _)| files.get(*n).map(|b| (n.to_string(), b.clone()))).collect()
|
|
}
|
|
|
|
fn section<'a>(text: &'a str, heading: &str) -> &'a str {
|
|
let at = text.find(heading).unwrap_or_else(|| panic!("{heading}"));
|
|
let end = text[at + 1..].find("\n## ").map_or(text.len(), |e| at + 1 + e);
|
|
&text[at..end]
|
|
}
|
|
|
|
fn extension() -> String {
|
|
std::fs::read_to_string(wai_dir().join("extensions/avatars.md")).expect("the extension")
|
|
}
|
|
|
|
#[test]
|
|
fn the_inputs_are_the_pinned_ones() {
|
|
assert_eq!(corpus::check_inputs(&inputs(&committed())), Vec::<String>::new());
|
|
}
|
|
|
|
#[test]
|
|
fn the_committed_corpus_regenerates_from_its_seeds() {
|
|
let files = committed();
|
|
let mut generated: BTreeMap<String, Vec<u8>> = corpus::generate(&inputs(&files)).into_iter().collect();
|
|
generated.extend(inputs(&files));
|
|
let extra: Vec<&String> = files.keys().filter(|k| !generated.contains_key(*k)).collect();
|
|
let missing: Vec<&String> = generated.keys().filter(|k| !files.contains_key(*k)).collect();
|
|
let differ: Vec<&String> = generated.iter().filter(|(k, v)| files.get(*k).is_some_and(|f| f != *v)).map(|(k, _)| k).collect();
|
|
assert!(extra.is_empty() && missing.is_empty() && differ.is_empty(), "extra {extra:?}, missing {missing:?}, differ {differ:?}: run `wai_avatar_face_vectors gen`");
|
|
}
|
|
|
|
#[test]
|
|
fn every_vector_gets_what_it_states() {
|
|
let fails = corpus::verify(&committed());
|
|
assert!(fails.is_empty(), "{}", fails.join("\n"));
|
|
}
|
|
|
|
/// §10 lists, in its first table, exactly the codes `Reason::ALL` has, in order, and in its
|
|
/// second exactly the codes the files are refused with.
|
|
#[test]
|
|
fn the_extension_lists_the_reasons() {
|
|
let text = extension();
|
|
let s = section(&text, "\n## 10. Reasons");
|
|
let tables: Vec<Vec<String>> = s
|
|
.split("\n\n")
|
|
.filter(|b| b.lines().any(|l| l.starts_with("| `")))
|
|
.map(|b| b.lines().filter(|l| l.starts_with("| `")).filter_map(|l| l.split('`').nth(1).map(String::from)).collect())
|
|
.collect();
|
|
assert_eq!(tables.len(), 2, "§10 has two tables of codes");
|
|
let want: Vec<String> = Reason::ALL.iter().map(|r| r.code().to_string()).collect();
|
|
assert_eq!(tables[0], want, "§10's first table and Reason::ALL differ");
|
|
let forms: Vec<String> = FormError::ALL.iter().map(|r| r.code().to_string()).collect();
|
|
assert_eq!(tables[1], forms, "§10's second table and FormError::ALL differ");
|
|
}
|
|
|
|
/// §7.9's tables state the training tool's report, `training.json`, figure for figure.
|
|
#[test]
|
|
fn the_measurement_is_the_reports() {
|
|
let text = extension();
|
|
let s = section(&text, "\n### 7.9 ");
|
|
let files = committed();
|
|
let report: serde_json::Value = serde_json::from_slice(&files["training.json"]).expect("training.json");
|
|
let m = &report["measurement"];
|
|
let f = |v: &serde_json::Value| match v.as_f64() {
|
|
Some(x) if x.fract() == 0.0 => format!("{x:.1}"),
|
|
_ => v.to_string(),
|
|
};
|
|
for (key, what) in [("int_float", "integer, float"), ("int_truth", "integer, rendered"), ("float_truth", "float, rendered"), ("warp_truth", "warp alone, rendered")] {
|
|
let p = &m["psnr_db"][key];
|
|
let q = &m["ssim"][key];
|
|
let row = format!("| {what} | {} | {} | {} | {} |", f(&p["mean"]), f(&p["min"]), f(&q["mean"]), f(&q["min"]));
|
|
assert!(s.contains(&row), "§7.9 lacks the row `{row}`");
|
|
}
|
|
for (key, what) in [("truth", "the rendered faces"), ("float", "the float network's"), ("int", "the integer generator's")] {
|
|
let row = format!("| {what} | {} | {} |", f(&m["keypoint_rms_px"][key]), f(&m["mouth_keypoint_rms_px"][key]));
|
|
assert!(s.contains(&row), "§7.9 lacks the row `{row}`");
|
|
}
|
|
for (key, what) in [("truth_step4", "rendered, step 4"), ("truth_step1", "rendered, step 1"), ("detected_step4", "detected, step 4"), ("detected_step1", "detected, step 1")] {
|
|
let row = format!("| {what} | {} |", f(&m["track_kbit_s"][key]));
|
|
assert!(s.contains(&row), "§7.9 lacks the row `{row}`");
|
|
}
|
|
let g = &report["generator"];
|
|
assert_eq!(g["sha256"].as_str(), Some(corpus::INPUTS[0].1), "the report's generator is the corpus's");
|
|
}
|
|
|
|
/// The registered generator's graph meets `acc64` and not `acc32` (§7.6), and its frames are
|
|
/// §7.6's arithmetic written out loop by loop.
|
|
#[test]
|
|
fn the_registered_generator_meets_acc64_only() {
|
|
let files = committed();
|
|
let g = &files["generator.wfg"];
|
|
assert!(avatar_face::load(g).is_ok());
|
|
assert!(!avatar_face::meets_acc32(g), "a 16-bit activation times a 16-bit weight over hundreds of terms passes i32");
|
|
}
|