open-standards/wai/wai-quantum/examples/otoc_sv.rs
Transaction Science e82e69f303
Some checks failed
wai-quantum clippy / does this change touch it (push) Successful in 14s
WAI operation catalogue / does this change touch it (push) Successful in 26s
wai-audit collector and player integration / does this change touch it (push) Successful in 33s
wai-quantum contract / does this change touch it (push) Successful in 44s
wai-quantum editors / does this change touch it (push) Successful in 40s
wai-quantum command line / does this change touch it (push) Successful in 43s
wai-quantum resource estimation / does this change touch it (push) Successful in 55s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 1m0s
wai-quantum other formats / does this change touch it (push) Successful in 52s
wai-quantum pulse and dynamics / does this change touch it (push) Successful in 30s
wai-quantum OpenQASM reader / does this change touch it (push) Successful in 32s
wai-quantum error correction and mitigation / does this change touch it (push) Successful in 26s
wai-quantum for Python / does this change touch it (push) Successful in 34s
wai-quantum for JavaScript / does this change touch it (push) Successful in 31s
wai-quantum extension points / does this change touch it (push) Successful in 30s
wai-quantum service / does this change touch it (push) Successful in 41s
wai-quantum studio and desktop / does this change touch it (push) Successful in 35s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 43s
wai-sink-android native sink library / does this change touch it (push) Successful in 21s
wai-quantum variational and algorithm library / does this change touch it (push) Successful in 48s
wai-quantum verdicts and repairs / does this change touch it (push) Successful in 46s
wai-quantum service / service energy · linux packages (push) Failing after 16s
wai-sink-swift native sink package / does this change touch it (push) Successful in 39s
WAI Studio / does this change touch it (push) Successful in 47s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 10s
WAI examples / WAI examples · does this change touch them (push) Successful in 11s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 10s
wai-audit collector and player integration / collector · linux (push) Successful in 2m48s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 20s
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (push) Successful in 2m23s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 3m47s
wai-quantum clippy / clippy, warnings denied, every feature set · linux (push) Successful in 6m41s
wai-quantum wasmQ / does this change touch it (push) Successful in 15s
wai-quantum editors / editors · linux (push) Successful in 5m56s
wai-quantum other formats / readers, writers and the set · linux (push) Successful in 4m44s
wai-quantum resource estimation / estimation and calibration · linux, release (push) Successful in 6m59s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (push) Successful in 2m37s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (push) Successful in 3m53s
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (push) Successful in 56s
wai-quantum contract / API compatibility · linux (push) Successful in 9m41s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 9m52s
wai-quantum OpenQASM reader / reader and corpus · linux (push) Successful in 2m57s
wai-quantum for Python / python · linux (push) Successful in 4m56s
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (push) Successful in 3m48s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (push) Successful in 2m46s
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (push) Successful in 8m12s
wai-quantum extension points / the registry and the example · linux (push) Successful in 6m10s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (push) Successful in 2m54s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m27s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (push) Successful in 3m53s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 5m1s
wai-quantum for JavaScript / javascript · linux (push) Successful in 9m46s
wai-quantum views in agent hosts / views · the page and its engine (push) Has started running
wai-quantum studio and desktop / studio · browser (push) Successful in 6m52s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 6m58s
wai-quantum service / service · linux (push) Successful in 11m0s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 21m42s
WAI Studio / Studio in WebKit · linux (push) Successful in 14m53s
wai-quantum command line / command line · linux (push) Successful in 33m33s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 3m49s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Failing after 5m6s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Successful in 53m22s
wai-quantum wasmQ / the reference modules rebuild to their modules of record · linux (push) Successful in 48s
wai-quantum wasmQ / interpreter and vectors under wasm32-wasip2 (wasmtime) (push) Successful in 3m10s
WAI examples / WAI examples · portable · linux (push) Successful in 23m41s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · linux (push) Successful in 8m49s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m9s
wai-quantum editors / editors · macos (push) Successful in 1m57s
wai-quantum pulse and dynamics / the same bits · macos (push) Successful in 1m23s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (push) Successful in 2m56s
wai-quantum command line / command line · macos (push) Successful in 2m42s
wai-quantum for Python / python · macos (push) Successful in 3m10s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (push) Successful in 3m10s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 5m16s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Failing after 9m21s
wai-quantum service / service · macos (push) Successful in 7m51s
wai-quantum extension points / the registry and the example · linux-arm64 (push) Successful in 7m9s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 7m18s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 11m7s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 16m28s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 31m29s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · macos (push) Successful in 2m23s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Successful in 20m38s
WAI examples / WAI examples · android-app · linux-arm64 (push) Successful in 18m35s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 17m52s
WAI Quantum OTOC at 31 qubits: all 50 circuits' ΔC4, the four outside three standard errors named, and the spread's own uncertainty read as Student's t
- The other 45 circuits' receipts (3 and 6 to 49: moment 19, three
  instances, seed 1, width 3, one job each on the shared build host, 8 to 23
  minutes each), one line each in otoc2_31q_sv_n3_d19_w3_c3_c6-49.jsonl.
  The test checks every one of the 50: signed by the build hosts' key, the
  patch and moment of circuit 0's receipt, the plain correlator the plain
  runs' bits for circuits 0 to 19, each circuit once.
- Every estimate has the published sign; the differences' rms is 3.1e-4.
  Read as a normal variable, z puts circuits 3, 15, 19 and 26 outside three
  standard errors (+8.33, -3.75, +3.39, -4.04), where chance gives about
  0.1 of 50. They are pinned by name and kept.
- The published values are themselves the plain value less a mean over 20
  random insertion instances (the published script says its numbers differ
  for that reason), and that term was already in the error. What the normal
  reading left out is that each spread is estimated from the circuit's own
  three instances, so z is Student's t with two degrees of freedom: 4.7 of
  50 are expected past 3 and 4 are; no tail is below the normal 3-sigma
  0.0027 (the smallest, circuit 3's, 0.014, comes in half of all sets of
  50); the 50 tails are uniform (Kolmogorov-Smirnov 0.087). The earlier
  floor at circuit 0's spread is gone.
- The insertion moment at 18 qubits: circuits 0 to 9, 200 instances, after
  moments 14, 16 and 17 (receipts added; the first instance of circuit 0
  re-run in the test at each): chi2 per circuit 3.9, 0.80 and 2.6, against
  0.82 at 15. The strings go after the single-qubit layer at moment 14 and
  before the one at 16, so 19 (or 20) at 31 qubits, as used. A second run
  at 15 gave the committed receipt's bits for circuits 0 to 9.
- otoc_sv's doc says how to read its z when the instances are few.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-12 12:36:17 -05:00

177 lines
8.8 KiB
Rust

//! OTOC(2) at the edge of ergodicity on the dense state vector: the published
//! 18- to 31-qubit circuits' off-diagonal part `ΔC⁽⁴⁾`, against the values
//! published with them, with a signed receipt.
//!
//! ```text
//! cargo run --release --example otoc_sv --features quantum_sv,quantum_spd_receipt -- SET.txt [options]
//! ```
//!
//! `SET.txt` is a circuit set in the text form `quantum_otoc::EchoSet::parse`
//! reads (`wai/tools/quantum-otoc/` fetches the published circuits and converts
//! them). Options:
//!
//! - `--circuits 0-9` or `--circuits 0,4,7`: the published indices to run (all by default);
//! - `--instances N`: insertion instances for the diagonal part (20, as published);
//! `--plain` runs the plain correlator only;
//! - `--seed S`: the insertion strings' seed (1);
//! - `--insert D`: the moment after which the strings are inserted. A set
//! carries its script's `depth // 2 + shift` (9 for the 18-qubit set); the
//! values published for the 18-qubit set are reproduced at moment 15
//! (`tests/otoc_reference.rs`), and the receipt binds the moment used;
//! - `--width W`: the fusion width (4);
//! - `--receipt PATH`: where the signed receipt goes;
//! - the signing key is 32 bytes of hex in the file `OTOC_SIGNING_KEY` names, or
//! a fixed demonstration key.
//!
//! Prints each circuit's plain value, the instances' mean and spread, the
//! estimate, the published value and the difference in units of the two
//! estimates' combined Monte Carlo error (the published one used 20 instances);
//! then the set's χ² per circuit, the work, the time and the energy. The error
//! comes from the circuit's own instances' spread, itself an estimate: with few
//! instances `z` is Student's t with one degree of freedom fewer than the
//! instances, not a normal variable (at three, `|z| > 3` one time in ten), and
//! the χ² band printed holds only when the instances are many
//! (`tests/otoc_reference.rs` reads the 31-qubit set so).
use std::time::Instant;
use wai_quantum::ed25519_dalek::SigningKey;
use wai_quantum::quantum_otoc::{EchoSet, Kind, Method, OtocJob, Quantity, published_for, seal_metered};
fn main() {
let args: Vec<String> = std::env::args().skip(1).collect();
let path = args.first().expect("usage: otoc_sv SET.txt [--circuits A-B] [--instances N | --plain] [--seed S] [--width W] [--receipt PATH]");
let opt = |k: &str| args.iter().position(|a| a == k).map(|i| args[i + 1].clone());
let mut set = EchoSet::parse(&std::fs::read_to_string(path).expect("the set reads")).expect("a valid set");
if let Some(sel) = opt("--circuits") {
set = set.subset(&indices(&sel));
}
if let Some(d) = opt("--insert") {
// Another insertion moment than the set's (part of the patch's hash).
set.insert = d.parse().expect("a moment");
}
if opt("--kind").as_deref() == Some("otoc1") {
// Exploration: the OTOC(1) of the same blocks, which nothing published.
set.kind = Kind::Otoc1;
set.name += "-as-otoc1";
}
let width: u32 = opt("--width").map_or(4, |w| w.parse().expect("a width"));
if let Some(mode) = opt("--explore") {
explore(&set, &mode, opt("--instances").map_or(100, |n| n.parse().expect("a count")), width);
return;
}
let quantity = if args.iter().any(|a| a == "--plain") || set.kind == Kind::Otoc1 {
Quantity::Plain
} else {
Quantity::OffDiagonal { instances: opt("--instances").map_or(20, |n| n.parse().expect("a count")), seed: opt("--seed").map_or(1, |s| s.parse().expect("a seed")) }
};
let (key, id) = signer();
let published = published_for(&set.name);
println!("set {} · {} qubits · {} cycles · {} circuits · {quantity:?} · fusion width {width}", set.name, set.n, set.cycles, set.circuits.len());
if let Some((_, what)) = published {
println!("published: {what}");
}
let kind = set.kind;
let job = OtocJob { set, quantity, method: Method::Sv { width } };
let mut last = Instant::now();
let mut z2 = Vec::new();
let (receipt, json) = seal_metered(&key, &id, &job, |o| {
let secs = last.elapsed().as_secs_f64();
last = Instant::now();
let ours = o.value();
match published.and_then(|(p, _)| p.get(o.index as usize)) {
Some(&p) if !o.instances.is_empty() => {
// Each estimate's Monte Carlo error from this run's spread.
let k = o.instances.len() as f64;
let se = (o.sd() * o.sd() * (1.0 / 20.0 + 1.0 / k)).sqrt();
let z = (ours - p) / se;
z2.push(z * z);
println!(
"{:4} plain {:+.6} mean {:+.6} sd {:.6} ΔC4 {:+.6} published {:+.6} diff {:+.6} z {:+.2} {secs:.1}s",
o.index, o.plain, o.mean(), o.sd(), ours, p, ours - p, z
);
}
Some(&p) if kind == Kind::Otoc2 => {
println!("{:4} plain {:+.9} published ΔC4 {:+.9} (the plain value less the diagonal part) {secs:.1}s", o.index, ours, p)
}
Some(&p) => println!("{:4} value {:+.9} published {:+.9} diff {:+.2e} {secs:.1}s", o.index, ours, p, ours - p),
None => println!("{:4} value {:+.9} {secs:.1}s", o.index, ours),
}
})
.expect("the job runs");
if !z2.is_empty() {
let k = z2.len() as f64;
println!("chi2 per circuit {:.3} over {} circuits (1 expected; 99% band for 50: 0.59-1.52)", z2.iter().sum::<f64>() / k, z2.len());
}
let work: u64 = receipt.outcomes.iter().map(|o| o.work).sum();
println!("work {work} amplitude-gate updates · {:.1} s · {} µJ (this process's CPU energy)", receipt.elapsed_ms as f64 / 1000.0, receipt.joules_micro);
if let Some(out) = opt("--receipt") {
std::fs::write(&out, json + "\n").expect("the receipt writes");
println!("receipt {out} (receipt hash {})", hex(&receipt.receipt_hash()));
}
}
/// Other readings of the published estimator, for diagnosis (no receipt):
/// `same` puts one string in both echoes, `xyz` draws strings without the
/// identity.
fn explore(set: &EchoSet, mode: &str, instances: u32, width: u32) {
use wai_quantum::quantum_otoc::{SvRunner, insertion_strings};
let mut sv = SvRunner::new(width);
let published = published_for(&set.name).map(|(p, _)| p);
let mut z2 = Vec::new();
for c in &set.circuits {
let plain = sv.otoc(set, c, None).expect("runs");
let mut vals = Vec::new();
for k in 0..instances {
let (mut a, mut b) = insertion_strings(set.n, 1, c.index, k);
match mode {
"same" => b = a.clone(),
"xyz" => {
// Each identity redrawn from X, Y, Z (16 values mod 3: within 1/16 of uniform).
let (fa, fb) = insertion_strings(set.n, 2, c.index, k);
let (ga, gb) = insertion_strings(set.n, 3, c.index, k);
for (s, v) in a.iter_mut().zip(fa.iter().zip(&ga)).chain(b.iter_mut().zip(fb.iter().zip(&gb))) {
if *s == 0 {
*s = 1 + (4 * v.0 + v.1) % 3;
}
}
}
_ => panic!("unknown mode {mode}"),
}
vals.push(sv.otoc(set, c, Some((&a, &b))).expect("runs"));
}
let k = vals.len() as f64;
let mean = vals.iter().sum::<f64>() / k;
let sd = (vals.iter().map(|v| (v - mean) * (v - mean)).sum::<f64>() / (k - 1.0)).sqrt();
let ours = plain - mean;
let p = published.and_then(|p| p.get(c.index as usize)).copied().unwrap_or(f64::NAN);
let z = (ours - p) / (sd * sd * (1.0 / 20.0 + 1.0 / k)).sqrt();
z2.push(z * z);
println!("{mode} {:4} plain {plain:+.6} mean {mean:+.6} sd {sd:.6} ΔC4 {ours:+.6} published {p:+.6} z {z:+.2}", c.index);
}
println!("{mode} chi2 per circuit {:.3}", z2.iter().sum::<f64>() / z2.len() as f64);
}
fn indices(s: &str) -> Vec<u32> {
s.split(',')
.flat_map(|part| match part.split_once('-') {
Some((a, b)) => (a.parse::<u32>().expect("an index")..=b.parse::<u32>().expect("an index")).collect::<Vec<_>>(),
None => vec![part.parse().expect("an index")],
})
.collect()
}
fn signer() -> (SigningKey, String) {
match std::env::var("OTOC_SIGNING_KEY") {
Ok(p) => {
let h = std::fs::read_to_string(&p).expect("the key file reads");
let h = h.trim();
let b: Vec<u8> = (0..32).map(|i| u8::from_str_radix(&h[2 * i..2 * i + 2], 16).expect("hex")).collect();
(SigningKey::from_bytes(&b.try_into().expect("32 bytes")), "wai-quantum otoc reproduction".into())
}
Err(_) => (SigningKey::from_bytes(&[0x0e; 32]), "wai-quantum otoc example (demonstration key)".into()),
}
}
fn hex(b: &[u8]) -> String {
b.iter().map(|x| format!("{x:02x}")).collect()
}