From an independent review of the tile implementation (no input gave wrong bits); each finding fixed: - expected.json is authoritative: tests/fabric_expected.rs (excluded from the package, so publishing is unaffected; cargo drops its target and verification builds the library only) requires quantum-conformance/fabric/expected.json to be exactly the four certificates of the pinned corpora (gateset, corpus, seed, cases, wide circuits, the digest the CPU computes) and both implementation ids; a missing file fails. The browser job runs it in place of the grep step, whose comment now says what is checked. A copy giving fabric-v1 random-v1's digests fails it. - The checker's step 6 adds a uniformity taint check: a barrier must not sit under a condition, or after a return, break or continue under a condition, that depends on the invocation's ids or on memory other invocations write (read-write storage, workgroup memory, atomics), directly or through a variable or a call. Uniform buffers, read-only storage, buffer lengths and the workgroup id, count and size are uniform. Planted early returns on local_invocation_index (the reviewer's variant among them), on a read-write load, through a variable and through a call each fail; the real shaders pass. - The tile kernel refuses a run record (bit 4, nothing touched) unless its tiles cover each state plane exactly and its grid (num_workgroups, one deep) holds every tile once with x_groups to a row. The tile implementation id is now wgsl-lanes32-tile/54e443520c020013, re-pinned in wgsl.rs and expected.json. - PlannedGate::is_well_formed: diag must match the words and the op index must not be u32::MAX (the tile kernel's "no op"). plan::execute_from returns None and the member refuses Internal(4) otherwise; compile never makes either. - The member splits a tile step into dispatches of at most SUBMIT_BUDGET pair-updates (splitting a run is exact), so every submission keeps the budget the module and POLL_TIMEOUT documents state. - g12 adds, for the tile member: starts other than zero; a first out-of-range op in a later submission with circuit/1 P(19) as filler (never skipped); a 600-gate run at 20 qubits that the member splits; ill-formed planned gates refused by both implementations; and, inside WAI_QUANTUM_WEBGPU_WIDE only, the widest sizes (run cap 16, grid (8, 8192) at 26 qubits), which the member script then requires. - Placement's tie order is documented as it is: with both implementations' entries for one member and no energy figures, place picks the tile implementation (its name sorts first); discovery opens per-gate members, which are the fallback. A test pins the order. - wai/wai-web/src/lib.rs is in the workflow's paths; the wasip2 job requires the new unit test by name. Co-Authored-By: Claude Opus 5.5 <[email protected]>
61 lines
3 KiB
Rust
61 lines
3 KiB
Rust
//! `quantum-conformance/fabric/expected.json` is what the browser leg of the
|
|
//! fabric's WebGPU member holds a page to (`browser_webgpu.mjs`). This holds
|
|
//! the file to the crate: it must be exactly the four certificates of the
|
|
//! fabric's pinned corpora (both gatesets; random-v1 and fabric-v1 with their
|
|
//! seeds, sizes and wide circuits; the corpus digests the CPU computes) and
|
|
//! the two implementation ids, so that editing the file alone cannot weaken
|
|
//! the leg, for instance by giving fabric-v1 random-v1's digest and so never
|
|
//! running a circuit wider than one tile. A missing or unreadable file fails.
|
|
//!
|
|
//! The file lives outside this package, so this test is excluded from it.
|
|
|
|
use std::path::Path;
|
|
use serde_json::{Value, json};
|
|
use wai_quantum::quantum::Gateset;
|
|
use wai_quantum::quantum_fabric::{Corpus, FABRIC_DEFAULT, apply_kernel, lanes, wgsl};
|
|
|
|
fn hex(b: &[u8]) -> String {
|
|
b.iter().map(|x| format!("{x:02x}")).collect()
|
|
}
|
|
|
|
#[test]
|
|
fn the_browser_legs_pins_are_the_crates() {
|
|
let path = Path::new(env!("CARGO_MANIFEST_DIR")).join("../quantum-conformance/fabric/expected.json");
|
|
let text = std::fs::read_to_string(&path).unwrap_or_else(|e| panic!("{}: {e}", path.display()));
|
|
let file: Value = serde_json::from_str(&text).unwrap_or_else(|e| panic!("{}: {e}", path.display()));
|
|
let Corpus::Fabric { seed, cases, wide } = FABRIC_DEFAULT else { panic!("FABRIC_DEFAULT is a fabric corpus") };
|
|
let random = Corpus::Random { seed, cases };
|
|
let mut certificates = Vec::new();
|
|
for (corpus, wide) in [(random, 0), (FABRIC_DEFAULT, wide)] {
|
|
for (number, gs) in [(1, Gateset::V1), (2, Gateset::V2)] {
|
|
let cert = lanes::certify_corpus(gs, "local/test", corpus);
|
|
certificates.push(json!({
|
|
"gateset": number,
|
|
"kernel": apply_kernel(gs),
|
|
"corpus": corpus.name(),
|
|
"seed": seed,
|
|
"cases": cases,
|
|
"wide": wide,
|
|
"cases_compared": cert.cases,
|
|
"corpus_hex": hex(&cert.corpus),
|
|
}));
|
|
}
|
|
}
|
|
let want = json!({
|
|
"implementation": wgsl::implementation_id(),
|
|
"tile_implementation": wgsl::tile_implementation_id(),
|
|
"certificates": certificates,
|
|
});
|
|
assert_eq!(file, want, "{} is not the crate's pins; the crate's are:\n{}", path.display(), serde_json::to_string_pretty(&want).unwrap());
|
|
// The four digests are the pinned ones, and fabric-v1's are not random-v1's.
|
|
let digests: Vec<&str> = file["certificates"].as_array().unwrap().iter().map(|c| c["corpus_hex"].as_str().unwrap()).collect();
|
|
assert_eq!(
|
|
digests,
|
|
[
|
|
"d18454c639acfd9c7b66450f4ded1aac4ab46cc10a7bedbcf4958a96e204fb36",
|
|
"3dbde276c1095bf827faec71d5980e050f8a55a5ba7d97cd3bbdaec3f43a13b2",
|
|
"41c726a0ac80f7defef92e15ecf893bdb7a3076b5e7f670fa0b7142e5c033df7",
|
|
"9a84b55af68f1a8b07390b234cf562dd8d85cef3185a697ab85690c8790a7809",
|
|
]
|
|
);
|
|
}
|