open-standards/wai/wai-quantum/examples/tracker/operator.rs
Transaction Science 5909d6a219
Some checks failed
wai-audit collector and player integration / does this change touch it (push) Successful in 1m23s
WAI operation catalogue / does this change touch it (push) Successful in 1m5s
wai-quantum other formats / does this change touch it (push) Successful in 21s
wai-quantum-landscape-fpga reference design / does this change touch it (push) Successful in 26s
wai-quantum editors / does this change touch it (push) Successful in 28s
wai-quantum resource estimation / does this change touch it (push) Successful in 28s
wai-quantum error correction and mitigation / does this change touch it (push) Successful in 12s
wai-quantum extension points / does this change touch it (push) Successful in 25s
wai-quantum qudit jobs / does this change touch it (push) Successful in 34s
wai-quantum service / does this change touch it (push) Successful in 19s
wai-quantum studio and desktop / does this change touch it (push) Successful in 20s
wai-quantum service / service energy · linux packages (push) Failing after 12s
wai-quantum variational and algorithm library / does this change touch it (push) Successful in 18s
wai-quantum verdicts and repairs / does this change touch it (push) Successful in 29s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 28s
wai-quantum on Windows / does this change touch it (push) Successful in 26s
wai-sink-android native sink library / does this change touch it (push) Successful in 35s
wai-sink-swift native sink package / does this change touch it (push) Successful in 32s
wai-quantum clippy / does this change touch it (push) Successful in 3m3s
wai-quantum OpenQASM reader / does this change touch it (push) Successful in 2m30s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 2m48s
wai-quantum contract / does this change touch it (push) Successful in 2m53s
wai-quantum for JavaScript / does this change touch it (push) Successful in 2m40s
wai-quantum pulse and dynamics / does this change touch it (push) Successful in 2m31s
wai-quantum for Python / does this change touch it (push) Successful in 2m30s
wai-quantum command line / does this change touch it (push) Successful in 3m32s
WAI Studio / does this change touch it (push) Successful in 30s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 13s
WAI examples / WAI examples · does this change touch them (push) Successful in 14s
wai-quantum landscape energy to solution / does this change touch it (push) Successful in 13s
wai-quantum landscape stage 0, the sampler against the exact law / does this change touch it (push) Successful in 12s
wai-quantum energy-landscape jobs / does this change touch it (push) Successful in 11s
wai-quantum wasmQ / does this change touch it (push) Successful in 10s
wai-audit collector and player integration / collector · linux (push) Successful in 3m57s
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (push) Successful in 3m13s
wai-quantum other formats / readers, writers and the set · linux (push) Successful in 7m22s
wai-quantum editors / editors · linux (push) Successful in 5m34s
wai-quantum qudit jobs / the module and the conformance set under wasm32-wasip2 (wasmtime) (push) Successful in 2m16s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 15m22s
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (push) Successful in 4m40s
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (push) Successful in 9m5s
wai-quantum extension points / the registry and the example · linux (push) Successful in 9m0s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (push) Successful in 2m59s
wai-quantum studio and desktop / studio · browser (push) Successful in 5m24s
wai-quantum qudit jobs / the module, the job, the conformance set, the referee and the verb · linux (push) Successful in 10m39s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (push) Successful in 4m23s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (push) Successful in 3m37s
wai-quantum resource estimation / estimation and calibration · linux, release (push) Successful in 14m26s
wai-quantum views in agent hosts / views · the page and its engine (push) Successful in 6m21s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 6m36s
wai-quantum service / service · linux (push) Successful in 17m0s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 16m40s
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (push) Successful in 1m51s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 13s
wai-quantum OpenQASM reader / reader and corpus · linux (push) Successful in 5m37s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 27s
wai-quantum clippy / clippy, warnings denied, every feature set · linux (push) Successful in 7m31s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (push) Successful in 2m0s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (push) Successful in 4m19s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 7m14s
wai-quantum contract / API compatibility · linux (push) Successful in 11m45s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m16s
wai-quantum-landscape-fpga reference design / the member against the model of the logic, and the logic against the model · linux, release (push) Successful in 43m41s
wai-quantum for JavaScript / javascript · linux (push) Successful in 11m32s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 17m19s
wai-quantum for Python / python · linux (push) Successful in 11m47s
WAI Studio / Studio in WebKit · linux (push) Successful in 25m18s
wai-quantum command line / command line · linux (push) Successful in 40m56s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Failing after 51m56s
wai-quantum-landscape-fpga reference design / the board's code on the board's processor's architecture · linux-arm64, release (push) Successful in 1m43s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 8m45s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m43s
wai-quantum editors / editors · macos (push) Successful in 2m46s
wai-quantum qudit jobs / the same bits · macos (push) Successful in 2m42s
wai-quantum extension points / the registry and the example · linux-arm64 (push) Successful in 6m44s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (push) Successful in 4m3s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (push) Successful in 6m0s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 8m4s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 5m30s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 7m17s
wai-quantum service / service · macos (push) Successful in 12m57s
wai-quantum landscape stage 0, the sampler against the exact law / the small tier, to the byte · linux (push) Successful in 1m6s
wai-quantum landscape energy to solution / the small tier, to the byte · linux (push) Successful in 3m6s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 4m9s
wai-quantum pulse and dynamics / the same bits · macos (push) Successful in 1m24s
wai-quantum wasmQ / the reference modules rebuild to their modules of record · linux (push) Successful in 36s
wai-quantum for Python / python · macos (push) Successful in 3m12s
wai-quantum command line / command line · macos (push) Successful in 2m41s
wai-quantum wasmQ / interpreter and vectors under wasm32-wasip2 (wasmtime) (push) Successful in 5m31s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · linux (push) Successful in 7m1s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 12m56s
wai-quantum energy-landscape jobs / native against WebAssembly, over many seeds (push) Successful in 11m33s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 9m3s
wai-quantum energy-landscape jobs / reader, kernels, answers and the stated result · linux, release (push) Successful in 15m48s
wai-quantum energy-landscape jobs / the module under wasm32-wasip2 (wasmtime) (push) Successful in 19m24s
WAI examples / WAI examples · portable · linux (push) Successful in 25m12s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Failing after 37m26s
WAI examples / WAI examples · android-app · linux-arm64 (push) Successful in 16m3s
wai-quantum landscape stage 0, the sampler against the exact law / the small tier, to the byte · macos (push) Successful in 37s
wai-quantum energy-landscape jobs / the same bits · linux-arm64 (push) Successful in 17m52s
wai-quantum landscape energy to solution / the small tier, to the byte · macos (push) Successful in 3m14s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · macos (push) Successful in 2m41s
wai-quantum energy-landscape jobs / the same bits · macos (push) Successful in 7m13s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Successful in 21m17s
wai-quantum on Windows / engine · windows (push) Has been cancelled
wai-quantum on Windows / command line · windows (push) Has been cancelled
wai-quantum on Windows / job service · windows (push) Has been cancelled
wai-quantum on Windows / python · windows (push) Has been cancelled
wai-quantum studio and desktop / desktop app · windows (push) Has been cancelled
WAI Quantum tracker reproductions: the echo as an operator grown from the perturbation, routing that keeps pairs, and the sweeps' receipts
- tracker_ole mpo: the circuit's operator as a matrix-product state on row
  and column sites, grown outward from the perturbation's centre, the echo
  read as <<W|(O x O)|W>>. Held to the exact echo on small circuits at two
  junctions, and to sparse Pauli dynamics on sub-echoes of the 49-qubit
  circuit. On the full circuits its truncation drops the operator's whole
  departure from the identity (discarded weight about 0.53, 24 delta^2 =
  0.54) and the value reads 1 at chi up to 64, on all three instances.
- The operator chain now keeps each qubit's row and column in one slot and
  moves slots, not sites, to bring a two-qubit gate's qubits together, so no
  Bell pair is stretched across the chain; tracker_peaked scan grows a window
  around each of 39 junctions to find where a circuit's halves undo each
  other (none does in the 49-qubit peaked circuit at chi 16).
- Receipts for every sweep run so far: sparse Pauli dynamics on the three
  echoes, the purified network on 49 qubits to chi 64 (and re-gauged),
  49x1296 to chi 32, the operator echo on all three, and the peaked circuits
  49x4020 and 49x5072 by state network and operator. The test re-runs the
  operator echo's cheapest receipt bit for bit.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-11 22:19:56 -05:00

266 lines
11 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

//! A circuit as an operator, grown from its middle outward: the method the
//! peaked circuits are read by (`tracker_peaked`, `mpo`).
#![allow(dead_code)]
use wai_quantum::gates::{C, Gate};
use wai_quantum::quantum_mpsim::{Chain, MpsConfig};
use wai_quantum::quantum_spd::{Op, Pauli, RotCircuit, sin_cos};
/// A gate's matrix on the qubits it names (the first the most significant bit).
pub fn matrix(op: &Op) -> (Vec<u32>, Vec<C>) {
let c = |re: f64, im: f64| C { re, im };
let pauli = |p: Pauli| -> [C; 4] {
match p {
Pauli::X => [c(0.0, 0.0), c(1.0, 0.0), c(1.0, 0.0), c(0.0, 0.0)],
Pauli::Y => [c(0.0, 0.0), c(0.0, -1.0), c(0.0, 1.0), c(0.0, 0.0)],
Pauli::Z => [c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(-1.0, 0.0)],
}
};
let kron = |a: &[C], b: &[C], da: usize, db: usize| -> Vec<C> {
let d = da * db;
let mut out = vec![c(0.0, 0.0); d * d];
for i in 0..d {
for j in 0..d {
let (x, y) = (a[(i / db) * da + j / db], b[(i % db) * db + j % db]);
out[i * d + j] = c(x.re * y.re - x.im * y.im, x.re * y.im + x.im * y.re);
}
}
out
};
// exp(−iθP/2) = cos(θ/2)·I − i·sin(θ/2)·P, for the string P on `qs`.
let rotation = |axis: &[(u32, Pauli)], cos: f64, sin: f64| -> (Vec<u32>, Vec<C>) {
let qs: Vec<u32> = axis.iter().map(|(q, _)| *q).collect();
let mut p = pauli(axis[0].1).to_vec();
let mut d = 2;
for &(_, f) in &axis[1..] {
p = kron(&p, &pauli(f), d, 2);
d *= 2;
}
let d = 1usize << qs.len();
let m = (0..d * d).map(|k| {
let id = if k / d == k % d { cos } else { 0.0 };
// −i·sin·P: (re, im) of P times (0, −sin).
c(id + sin * p[k].im, -sin * p[k].re)
});
(qs, m.collect())
};
let h = std::f64::consts::FRAC_1_SQRT_2;
match op {
Op::Rot { axis, theta } => {
let (s, co) = sin_cos(0.5 * theta);
rotation(axis, co, s)
}
Op::QuarterRot { axis, k } => {
let (co, s) = match k {
1 => (h, h),
2 => (0.0, 1.0),
_ => (-h, h),
};
rotation(axis, co, s)
}
Op::CZ(a, b) => (vec![*a, *b], (0..16).map(|k| if k % 5 == 0 { c(if k == 15 { -1.0 } else { 1.0 }, 0.0) } else { c(0.0, 0.0) }).collect()),
Op::CX(a, b) => {
let perm = [0usize, 1, 3, 2];
(vec![*a, *b], (0..16).map(|k| c(if perm[k / 4] == k % 4 { 1.0 } else { 0.0 }, 0.0)).collect())
}
Op::H(q) => (vec![*q], vec![c(h, 0.0), c(h, 0.0), c(h, 0.0), c(-h, 0.0)]),
Op::X(q) => (vec![*q], pauli(Pauli::X).to_vec()),
Op::Y(q) => (vec![*q], pauli(Pauli::Y).to_vec()),
Op::Z(q) => (vec![*q], pauli(Pauli::Z).to_vec()),
Op::S(q) => (vec![*q], vec![c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(0.0, 1.0)]),
Op::Sdg(q) => (vec![*q], vec![c(1.0, 0.0), c(0.0, 0.0), c(0.0, 0.0), c(0.0, -1.0)]),
Op::SX(q) => (vec![*q], vec![c(0.5, 0.5), c(0.5, -0.5), c(0.5, -0.5), c(0.5, 0.5)]),
Op::SXdg(q) => (vec![*q], vec![c(0.5, -0.5), c(0.5, 0.5), c(0.5, 0.5), c(0.5, -0.5)]),
Op::Swap(a, b) => (vec![*a, *b], Gate::swap(0, 1).matrix),
}
}
/// The transpose of a square row-major matrix.
pub fn transposed(m: &[C]) -> Vec<C> {
let d = (m.len() as f64).sqrt() as usize;
(0..d * d).map(|k| m[(k % d) * d + k / d]).collect()
}
/// What growing the operator from the middle gives.
pub struct Grown {
/// Each qubit's `⟨Z⟩` in the state the circuit prepares from `|0…0⟩`.
pub z: Vec<f64>,
/// The string the signs of `z` give, bit `q` qubit `q`.
pub string: Vec<u8>,
/// The string's probability.
pub probability: f64,
/// The chain's qubit order.
pub order: Vec<u32>,
pub max_bond: usize,
pub discarded: f64,
pub fidelity_estimate: f64,
}
/// The circuit as an operator, a matrix-product state on `2n` sites (each
/// qubit's row index beside its column index, the qubits in breadth-first
/// order of `edges`), started as the identity (a Bell pair a qubit) and grown
/// from the middle of the circuit outward: the gates after the midpoint on the
/// rows, in order, and each gate before it transposed on the columns, in
/// reverse, one two-qubit gate a side at a time. That is
/// `(A ⊗ Bᵀ)|Φ⟩ = (A·B ⊗ I)|Φ⟩` for the circuit `A·B`. The columns are then
/// projected on `|0⟩`, which leaves `2^{−n/2}·A·B|0…0⟩` on the rows.
pub fn grow_from_middle(circuit: &RotCircuit, edges: &[(u32, u32)], chi: usize) -> Grown {
grow_from(circuit, edges, chi, circuit.ops.len() / 2, usize::MAX).expect("the whole circuit")
}
/// A circuit's operator on a chain, as [`grow_chain`] leaves it.
pub struct Chained {
pub chain: Chain,
/// The qubit in each slot of the chain at the end, and each qubit's slot.
pub order: Vec<u32>,
pub pos: Vec<u32>,
/// Whether every gate was applied.
pub whole: bool,
}
impl Chained {
/// The site of qubit `q`'s row index, and of its column index.
pub fn row(&self, q: u32) -> u32 {
2 * self.pos[q as usize]
}
pub fn col(&self, q: u32) -> u32 {
2 * self.pos[q as usize] + 1
}
}
/// The circuit's operator `(A ⊗ Bᵀ)|Φ⟩`, `A` the gates from `mid` on and `B`
/// those before it, grown outward from `mid` by at most `window` two-qubit
/// gates a side, at bond dimension `chi`.
///
/// Each qubit's row and column sit side by side in a slot of the chain, the
/// slots first in breadth-first order of `edges`. A two-qubit gate on qubits in
/// slots apart first moves the second qubit's slot, both its sites together,
/// next to the first's, and the slots stay where they are moved: a qubit's row
/// never leaves its column, so routing stretches no pair across the chain.
pub fn grow_chain(circuit: &RotCircuit, edges: &[(u32, u32)], chi: usize, mid: usize, window: usize) -> Chained {
let n = circuit.n;
let order = super::common::bfs_chain(n, edges);
let mut pos = vec![0u32; n as usize];
for (i, &q) in order.iter().enumerate() {
pos[q as usize] = i as u32;
}
let mut at = order.clone();
let swap = Gate::swap(0, 1).matrix;
let ops = &circuit.ops;
let two = |op: &Op| matrix(op).0.len() == 2;
let mut chain = Chain::new(2 * n, MpsConfig { max_bond: chi, cutoff: 1e-14 }).expect("a chain");
for q in 0..n {
let s = 2 * pos[q as usize];
chain.apply(&Gate { qubits: vec![s], matrix: matrix(&Op::H(0)).1 }).expect("a gate");
chain.apply(&Gate { qubits: vec![s, s + 1], matrix: matrix(&Op::CX(0, 1)).1 }).expect("a gate");
}
// Exchange the pairs in slots `s` and `s + 1`: sites (r, c, r', c') become
// (r', c', r, c) by four swaps of neighbours.
let swap_slots = |chain: &mut Chain, pos: &mut [u32], at: &mut [u32], s: u32| {
let b = 2 * s;
for i in [b + 1, b, b + 2, b + 1] {
chain.apply(&Gate { qubits: vec![i, i + 1], matrix: swap.clone() }).expect("a swap");
}
let (x, y) = (at[s as usize], at[s as usize + 1]);
at.swap(s as usize, s as usize + 1);
pos[x as usize] = s + 1;
pos[y as usize] = s;
};
let apply = |chain: &mut Chain, pos: &mut [u32], at: &mut [u32], op: &Op, columns: bool| {
let (qs, m) = matrix(op);
if let [a, b] = qs[..] {
while pos[b as usize] > pos[a as usize] + 1 {
swap_slots(chain, pos, at, pos[b as usize] - 1);
}
while pos[b as usize] + 1 < pos[a as usize] {
swap_slots(chain, pos, at, pos[b as usize]);
}
}
let site = |q: u32| 2 * pos[q as usize] + u32::from(columns);
let m = if columns { transposed(&m) } else { m };
chain.apply(&Gate { qubits: qs.iter().map(|&q| site(q)).collect(), matrix: m }).expect("a gate");
};
let (mut f, mut b) = (mid, mid);
let mut sides = 0usize;
while (f < ops.len() || b > 0) && sides < window {
sides += 1;
while f < ops.len() {
apply(&mut chain, &mut pos, &mut at, &ops[f], false);
f += 1;
if two(&ops[f - 1]) {
break;
}
}
while b > 0 {
b -= 1;
apply(&mut chain, &mut pos, &mut at, &ops[b], true);
if two(&ops[b]) {
break;
}
}
}
let whole = f == ops.len() && b == 0;
Chained { chain, order: at, pos, whole }
}
/// The echo `2⁻ⁿ Tr(O · W† O W)` of a circuit `W` for `O` the `Z`s on `sites`,
/// from its operator grown outward from gate `mid`: with `|W⟩⟩ = (W ⊗ I)|Φ⟩`,
/// it is `⟨⟨W|(O ⊗ O)|W⟩⟩`, since `O` is real and diagonal. For a circuit
/// written `U · V · U†`, `mid` at `V`'s centre starts the operator at `V`, a
/// product near the identity, and grows it by `V`'s dressing alone. Returns the
/// echo and the chain's report.
pub fn echo_by_operator(circuit: &RotCircuit, edges: &[(u32, u32)], chi: usize, mid: usize, sites: &[u32]) -> (f64, wai_quantum::quantum_mpsim::MpsReport) {
let g = grow_chain(circuit, edges, chi, mid, usize::MAX);
let mut z: Vec<(u32, char)> = Vec::new();
for &q in sites {
z.push((g.row(q), 'Z'));
z.push((g.col(q), 'Z'));
}
let f = g.chain.expectation(&z).expect("sites on the chain") / g.chain.norm2();
(f, g.chain.report())
}
/// The operator grown outward from gate `mid` by at most `window` two-qubit
/// gates a side: with the whole circuit, what [`grow_from_middle`] reads;
/// with less, the chain's report alone (no readout), to find where the
/// circuit's halves undo each other.
pub fn grow_from(circuit: &RotCircuit, edges: &[(u32, u32)], chi: usize, mid: usize, window: usize) -> Option<Grown> {
let n = circuit.n;
let Chained { mut chain, order, pos, whole } = grow_chain(circuit, edges, chi, mid, window);
let (row, col) = (|q: u32| 2 * pos[q as usize], |q: u32| 2 * pos[q as usize] + 1);
let report = chain.report();
if !whole {
return Some(Grown {
z: Vec::new(),
string: Vec::new(),
probability: f64::NAN,
order,
max_bond: report.max_bond_used,
discarded: report.discarded,
fidelity_estimate: report.fidelity_estimate,
});
}
let zero = C { re: 0.0, im: 0.0 };
let project = vec![C { re: 1.0, im: 0.0 }, zero, zero, zero];
for q in 0..n {
chain.apply(&Gate { qubits: vec![col(q)], matrix: project.clone() }).expect("a projection");
}
let norm = chain.norm2();
let z: Vec<f64> = (0..n).map(|q| chain.expectation(&[(row(q), 'Z')]).expect("a qubit") / norm).collect();
let string: Vec<u8> = z.iter().map(|&v| u8::from(v < 0.0)).collect();
let mut bits = vec![0u8; 2 * n as usize];
for q in 0..n {
bits[row(q) as usize] = string[q as usize];
}
let a = chain.amplitude(&bits).expect("a string");
Some(Grown {
z,
string,
probability: (a.re * a.re + a.im * a.im) / norm,
order,
max_bond: report.max_bond_used,
discarded: report.discarded,
fidelity_estimate: report.fidelity_estimate,
})
}