//! Out-of-time-order correlators of the published circuits by sparse Pauli //! dynamics: the 40-qubit OTOC(1) circuits against their published exact //! values, and the 65- and 95-qubit circuits as a measured exploration against //! the processor's values, each run with a signed receipt. //! //! ```text //! cargo run --release --example otoc_spd --features quantum_sv,quantum_spd_receipt -- SET.txt --threshold T [options] //! ``` //! //! `SET.txt` is a circuit set in the text form `quantum_otoc::EchoSet::parse` //! reads. Options: //! //! - `--threshold T`: drop terms whose coefficient falls below `T` (required); //! - `--max-terms M`: hold at most `M` terms, doubling the threshold past it (2³¹); //! - `--circuits 0-9` or `--circuits 0,4,7`: the published indices to run (all by default); //! - `--instances N`, `--seed S`: OTOC(2)'s off-diagonal estimate (plain by default); //! - `--threads K`: worker threads (time only, never bits; all by default); //! - `--receipt PATH`, and the key as `otoc_sv` takes it. //! //! Each circuit prints its value, the propagation's truncation bound (the //! value is within it of the exact one, up to rounding), the most terms held, //! the published value and the difference; then the set's mean absolute //! difference, the correlation with the published values, the work, the time //! and the energy. use std::time::Instant; use wai_quantum::ed25519_dalek::SigningKey; use wai_quantum::quantum_otoc::{EchoSet, Method, OtocJob, Quantity, published_for, seal_metered}; fn main() { let args: Vec = std::env::args().skip(1).collect(); let path = args.first().expect("usage: otoc_spd SET.txt --threshold T [--max-terms M] [--circuits A-B] [--instances N] [--threads K] [--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 opt("--kind").as_deref() == Some("otoc1") { // Exploration: the OTOC(1) of the same blocks, which nothing published. set.kind = wai_quantum::quantum_otoc::Kind::Otoc1; set.name += "-as-otoc1"; } let threshold: f64 = opt("--threshold").expect("--threshold T").parse().expect("a threshold"); let max_terms: u64 = opt("--max-terms").map_or(1 << 31, |m| m.parse().expect("a count")); let threads: u32 = opt("--threads").map_or_else(|| std::thread::available_parallelism().map_or(1, |t| t.get() as u32), |t| t.parse().expect("a count")); let quantity = match opt("--instances") { Some(n) => Quantity::OffDiagonal { instances: n.parse().expect("a count"), seed: opt("--seed").map_or(1, |s| s.parse().expect("a seed")) }, None => Quantity::Plain, }; let (key, id) = signer(); let published = published_for(&set.name); println!("set {} · {} qubits · {} cycles · {} circuits · {quantity:?} · threshold {threshold:e} · cap {max_terms} terms · {threads} threads", set.name, set.n, set.cycles, set.circuits.len()); if let Some((_, what)) = published { println!("published: {what}"); } let job = OtocJob { set, quantity, method: Method::Spd { threshold, max_terms, threads } }; let mut last = Instant::now(); let mut pairs = 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(); let p = published.and_then(|(p, _)| p.get(o.index as usize)).copied(); let tail = p.map_or(String::new(), |p| { pairs.push((ours, p)); format!(" published {p:+.6} diff {:+.6}", ours - p) }); println!("{:4} value {ours:+.6} plain {:+.6} bound {:.3e} peak {:>11}{tail} {secs:.1}s", o.index, o.plain, o.plain_bound, o.peak); }) .expect("the job runs"); if pairs.len() > 1 { let k = pairs.len() as f64; let mad = pairs.iter().map(|(a, b)| (a - b).abs()).sum::() / k; let (ma, mb) = (pairs.iter().map(|p| p.0).sum::() / k, pairs.iter().map(|p| p.1).sum::() / k); let cov: f64 = pairs.iter().map(|(a, b)| (a - ma) * (b - mb)).sum(); let (va, vb): (f64, f64) = (pairs.iter().map(|(a, _)| (a - ma) * (a - ma)).sum(), pairs.iter().map(|(_, b)| (b - mb) * (b - mb)).sum()); let rms_pub = (pairs.iter().map(|(_, b)| b * b).sum::() / k).sqrt(); println!("mean |diff| {mad:.3e} · rms published {rms_pub:.3e} · correlation {:.3} · means {ma:+.3e} / {mb:+.3e}", cov / (va * vb).sqrt()); } let work: u64 = receipt.outcomes.iter().map(|o| o.work).sum(); let peak = receipt.outcomes.iter().map(|o| o.peak).max().unwrap_or(0); println!("work {work} term updates · peak {peak} terms · {:.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())); } } fn indices(s: &str) -> Vec { s.split(',') .flat_map(|part| match part.split_once('-') { Some((a, b)) => (a.parse::().expect("an index")..=b.parse::().expect("an index")).collect::>(), 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 = (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() }