//! Noisy Clifford circuits from the command line. //! //! ```text //! cargo run --release --example qec_frame --features quantum_frame -- gen 5 5 0.001 //! cargo run --release --example qec_frame --features quantum_frame -- dem circuit.txt //! cargo run --release --example qec_frame --features quantum_frame -- sample circuit.txt 100000 7 //! cargo run --release --example qec_frame --features quantum_frame -- decode circuit.txt events.txt //! cargo run --release --example qec_frame --features quantum_frame -- rate circuit.txt 100000 7 //! cargo run --release --example qec_frame --features quantum_frame -- records circuit.txt 100000 7 //! cargo run --release --example qec_frame --features quantum_frame -- speed 7 7 0.001 4000000 7 //! ``` use wai_quantum::quantum_frame::{error_model, parse, reference, sample, sample_measurements, surface_code_memory}; use wai_quantum::quantum_match::MatchingGraph; fn main() { let a: Vec = std::env::args().collect(); let read = |p: &str| std::fs::read_to_string(p).expect("read circuit"); match a.get(1).map(String::as_str) { Some("gen") => { let d: u32 = a[2].parse().unwrap(); let r: u32 = a[3].parse().unwrap(); let p: f64 = a[4].parse().unwrap(); print!("{}", surface_code_memory(d, r, p)); } Some("dem") => { let c = parse(&read(&a[2])).expect("parse"); print!("{}", error_model(&c).expect("model").to_text()); } Some("sample") => { let c = parse(&read(&a[2])).expect("parse"); let shots: usize = a[3].parse().unwrap(); let seed: u64 = a[4].parse().unwrap(); let s = sample(&c, shots, seed); for d in 0..s.detectors { println!("D{d} {}", s.count(d)); } let flips = (0..shots).filter(|&i| s.flips(i) & 1 == 1).count(); println!("L0 {flips}"); } // One line per shot of fired detector indices; prints one predicted // observable mask per line. Some("decode") => { let c = parse(&read(&a[2])).expect("parse"); let g = MatchingGraph::from_model(&error_model(&c).expect("model")).expect("graphlike"); let mut out = String::new(); for line in read(&a[3]).lines() { let fired: Vec = line.split_whitespace().map(|t| t.parse().unwrap()).collect(); out += &format!("{}\n", g.decode(&fired).expect("decode")); } print!("{out}"); } // Sample and decode: logical failures out of shots, and the time spent. Some("rate") => { let c = parse(&read(&a[2])).expect("parse"); let shots: usize = a[3].parse().unwrap(); let seed: u64 = a[4].parse().unwrap(); let g = MatchingGraph::from_model(&error_model(&c).expect("model")).expect("graphlike"); let t0 = std::time::Instant::now(); let s = sample(&c, shots, seed); let t1 = t0.elapsed(); let f = g.failures(&s).expect("decode"); let t2 = t0.elapsed() - t1; println!("{f} {shots} sample_s={:.3} decode_s={:.3}", t1.as_secs_f64(), t2.as_secs_f64()); } // Every measurement's result: how many shots read 1 at each. Some("records") => { let c = parse(&read(&a[2])).expect("parse"); let shots: usize = a[3].parse().unwrap(); let seed: u64 = a[4].parse().unwrap(); let s = sample_measurements(&c, shots, seed); for r in 0..s.measurements { println!("M{r} {}", s.count(r)); } } // Shots a second on one thread for a rotated surface-code memory // experiment: detection events, then full measurement records (the // reference run's time counted in). The digests are of every bit // sampled, so two machines can be compared. Some("speed") => { let d: u32 = a[2].parse().unwrap(); let rounds: u32 = a[3].parse().unwrap(); let p: f64 = a[4].parse().unwrap(); let shots: usize = a[5].parse().unwrap(); let seed: u64 = a[6].parse().unwrap(); let c = parse(&surface_code_memory(d, rounds, p)).expect("parse"); println!("d={d} rounds={rounds} p={p}: {} qubits, {} operations, {} measurements, {} detectors", c.qubits, c.ops.len(), c.measurements, c.detectors.len()); let digest = |words: &mut dyn Iterator| { let mut h = blake3::Hasher::new(); for w in words { h.update(&w.to_le_bytes()); } h.finalize().to_hex()[..16].to_string() }; let mut best = (f64::MAX, f64::MAX); let mut digests = (String::new(), String::new()); for _ in 0..5 { let t0 = std::time::Instant::now(); let s = sample(&c, shots, seed); best.0 = best.0.min(t0.elapsed().as_secs_f64()); let t0 = std::time::Instant::now(); let m = sample_measurements(&c, shots, seed); best.1 = best.1.min(t0.elapsed().as_secs_f64()); digests.0 = digest(&mut (0..s.detectors).map(|k| s.count(k)).chain((0..shots).map(|i| s.flips(i)))); digests.1 = digest(&mut c.detectors.iter().flat_map(|recs| m.parity(recs)).chain((0..m.measurements).map(|r| m.count(r)))); } let t0 = std::time::Instant::now(); let r = reference(&c); let reference_s = t0.elapsed().as_secs_f64(); println!("detection events: {shots} shots in {:.4} s, {:.0} shots/s on one thread, digest {}", best.0, shots as f64 / best.0, digests.0); println!("measurement records: {shots} shots in {:.4} s, {:.0} shots/s on one thread, digest {}", best.1, shots as f64 / best.1, digests.1); println!("reference run: {:.6} s, {} of {} results random without noise", reference_s, r.random, c.measurements); } _ => eprintln!("usage: qec_frame gen D ROUNDS P | dem FILE | sample FILE SHOTS SEED | decode FILE EVENTS | rate FILE SHOTS SEED | records FILE SHOTS SEED | speed D ROUNDS P SHOTS SEED"), } }