open-standards/wai/wai-quantum/examples/tn_path.rs
Transaction Science ae0d1c4cd4
Some checks failed
no-vanity-metrics / Scan committed Markdown for vanity metrics (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no comparison (pull_request) Successful in 3s
no-vanity-metrics / WAI public text names no company or product (pull_request) Successful in 3s
standards workspaces build + test / eoc-rs (pull_request) Successful in 1m55s
standards workspaces build + test / jouleclaw-rs (pull_request) Failing after 2m13s
standards workspaces build + test / aac-rs (pull_request) Successful in 1m19s
standards workspaces build + test / jcp-rs (pull_request) Successful in 1m26s
standards workspaces build + test / proof-rs (pull_request) Successful in 1m12s
standards workspaces build + test / joule-code-rs (pull_request) Successful in 2m14s
standards workspaces build + test / sandbox-rs (pull_request) Successful in 2m11s
standards workspaces build + test / joulecontract-rs (pull_request) Successful in 1m11s
standards workspaces build + test / map-rs (pull_request) Successful in 2m6s
standards workspaces build + test / mesh-rs (pull_request) Successful in 57s
standards workspaces build + test / smart-byte-rs (pull_request) Successful in 3m38s
standards workspaces build + test / openpay (pull_request) Successful in 3m56s
WAI conformance / cargo-class corpora · linux (pull_request) Successful in 3m56s
WAI conformance / v2 vocabulary · manifests validate as stated (pull_request) Successful in 14s
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (pull_request) Successful in 2m9s
WAI conformance / cross-arch byte-equality · linux (pull_request) Successful in 22s
WAI conformance / C2PA manifest emission · signed, read back, validated (pull_request) Successful in 44s
WAI conformance / cargo-class corpora · linux-arm64 (pull_request) Successful in 5m7s
WAI energy receipts / energy receipts · linux (pull_request) Successful in 1m9s
WAI conformance / cross-arch byte-equality · linux-arm64 (pull_request) Successful in 37s
WAI JPEG-AI whole decode (pinned inputs, required) / fetch pinned JPEG-AI inputs → whole decode + pixel hash, required (pull_request) Successful in 58s
WAI energy receipts / energy receipts · linux-arm64 (pull_request) Successful in 1m28s
WAI neural sink gates / model-free refusals of the neural sink (pull_request) Successful in 1m0s
WAI mbt2018_mean integer decode (regenerated vectors) / regenerate mbt2018_mean integer vectors → Rust decode, required (pull_request) Successful in 3m55s
wai-web wasm32 / wai-web wasm32 build · corpus, menu and writers (pull_request) Successful in 1m47s
standards workspaces build + test / arl-rs (pull_request) Successful in 3m54s
standards workspaces build + test / joule-ui-rs (pull_request) Successful in 5m38s
standards workspaces build + test / jouledesk-rs (pull_request) Successful in 4m46s
standards workspaces build + test / joulehook-rs (pull_request) Successful in 5m49s
standards workspaces build + test / sense-rs (pull_request) Successful in 5m16s
standards workspaces build + test / macOS energy backends (pull_request) Successful in 7m10s
WAI conformance / cargo-class corpora · macos (pull_request) Successful in 7m41s
WAI conformance / cross-arch byte-equality · macos (pull_request) Successful in 40s
WAI energy receipts / energy receipts · macos (pull_request) Successful in 2m19s
wai-quantum 0.3.26: tensor-network contraction, hyper-optimised, sliced, bit-identical
quantum_tn turns a circuit (any unitary on any qubits, as a matrix) into the
tensor network of one amplitude or an open-output batch, and contracts it.

- Network::amplitude builds the network; simplify absorbs vectors and
  one-qubit gates into neighbours.
- find_path is a seeded hyper-optimiser over two tree families:
  - randomised greedy, scoring size(out)/c - c(size(a)+size(b)) with Gumbel
    noise;
  - recursive bipartition, multilevel (heavy-edge coarsening, several
    initial cuts, refinement at every level), with greedy leaves.
  Families alternate until one leads; later trials sample near the best
  parameters. The best few trees are reconfigured: subtrees of up to eight
  tensors are re-contracted optimally by subset dynamic programming, until
  nothing improves.
- slice fixes the index that leaves the least total work until the largest
  intermediate fits the cap, pricing each candidate incrementally. A second
  search round then finds a tree for the network with the first round's
  slices fixed (all, or the more valuable half).
- contract_plan contracts every slice in a fixed accumulation order and sums
  them in order. Threads are bounded by an 8 GiB budget; WASI runs
  sequentially. The bits are the same everywhere.

Measured on single amplitudes of random circuits on the 54-qubit
advantage-experiment grid (fSim(pi/2, pi/6), staggered couplers), 256 trials,
under ten seconds each, in log10 operations:
  m = 10: 10.48   m = 12: 13.91   m = 14: 14.63   m = 16: 17.60   m = 20: 18.77
An independently developed hyper-optimiser, given the identical networks,
lands within 0.4 of these at every depth, in either direction. The 54-qubit,
10-cycle amplitude, contracted here in 16 slices, agrees with an independent
contraction to 12 significant figures.

Tests:
- Amplitudes match a dense state vector for every tree family, with
  reconfiguration and with slicing.
- Reconfiguration reaches the exact optimum on 40 random small networks.
- Incremental slice pricing matches a full recount.
- The same seed gives the same plan.
- Mutating the permutation, the complex product, the contraction's summed
  indices, slice pricing, the slice offset or the reconfiguration DP fails a
  named test.
- bit_identity pins a plan and its sliced, threaded amplitude natively and
  under wasm32-wasip2.

CLI: wai-quantum tn --file CIRCUIT.jsonl [--bits --open --trials --seed
--cap-log2 --plan-only --json], byte-identical natively and under
wasm32-wasip2.

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

63 lines
3.1 KiB
Rust

//! Contraction trees for a circuit's amplitude, from the command line.
//!
//! Reads a circuit as JSON lines, one gate per line:
//! `{"q":[0,1],"m":[re,im, re,im, …]}` (row-major, rows = outputs, the first
//! qubit most significant). Writes the simplified network's shape (for any
//! other tool to search the same network) and the best plan found.
//!
//! ```text
//! cargo run --release --example tn_path --features quantum_tn -- CIRCUIT N TRIALS SEED [LOG2_CAP] [SHAPE_OUT] [--contract]
//! ```
use wai_quantum::quantum_tn::{contract_plan, find_path, Families, Gate, Network, PathOptions};
fn main() {
let a: Vec<String> = std::env::args().collect();
let text = std::fs::read_to_string(&a[1]).expect("read circuit");
let n: u32 = a[2].parse().unwrap();
let trials: usize = a[3].parse().unwrap();
let seed: u64 = a[4].parse().unwrap();
let cap: Option<f64> = a.get(5).filter(|s| *s != "-").map(|s| 2f64.powf(s.parse().unwrap()));
let shape_out = a.get(6).filter(|s| *s != "-");
let gates: Vec<Gate> = text
.lines()
.filter(|l| !l.trim().is_empty())
.map(|l| {
let v: serde_json::Value = serde_json::from_str(l).expect("json gate");
let qubits: Vec<u32> = v["q"].as_array().unwrap().iter().map(|x| x.as_u64().unwrap() as u32).collect();
let m: Vec<f64> = v["m"].as_array().unwrap().iter().map(|x| x.as_f64().unwrap()).collect();
let matrix = m.chunks(2).map(|c| wai_quantum::quantum_tn::complex(c[0], c[1])).collect();
Gate { qubits, matrix }
})
.collect();
let mut net = Network::amplitude(n, &gates, &vec![0; n as usize], &[]).expect("network");
let raw = net.tensors.len();
net.simplify();
let shape = net.shape();
if let Some(path) = shape_out {
let inputs: Vec<String> = shape.iter().map(|s| format!("{s:?}")).collect();
let dims: Vec<String> = net.dims.iter().map(|d| d.to_string()).collect();
std::fs::write(path, format!("{{\"inputs\":[{}],\"output\":{:?},\"dims\":[{}]}}\n", inputs.join(","), net.open, dims.join(","))).unwrap();
}
let t0 = std::time::Instant::now();
let families = match std::env::var("TN_FAMILIES").as_deref() {
Ok("greedy") => Families::Greedy,
Ok("partition") => Families::Partition,
_ => Families::Both,
};
let reconfigure = std::env::var("TN_RECONF").ok().and_then(|v| v.parse().ok()).unwrap_or(8);
let plan = find_path(&shape, &net.dims, &net.open, &PathOptions { trials, seed, max_size: cap, reconfigure, families });
let dt = t0.elapsed().as_secs_f64();
println!(
"tensors {raw} -> {} log10(flops) {:.3} log2(max size) {:.1} slices {} search {:.1}s",
shape.len(),
plan.total_flops().log10(),
plan.per_slice.max_size.log2(),
plan.slices,
dt
);
if a.iter().any(|x| x == "--contract") {
let t0 = std::time::Instant::now();
let t = contract_plan(&net, &plan).expect("contract");
println!("amplitude {:.12e} {:+.12e}i contract {:.1}s", t.data[0].re, t.data[0].im, t0.elapsed().as_secs_f64());
}
}