open-standards/wai/wai-quantum/examples/tracker/echo.rs
Transaction Science 72145ce6f9
All checks were successful
WAI conformance / does this change touch it (push) Has been skipped
WAI conformance / cargo-class corpora · linux (push) Has been skipped
WAI conformance / cargo-class corpora · linux-arm64 (push) Has been skipped
WAI conformance / cargo-class corpora · macos (push) Has been skipped
WAI conformance / cargo-class corpora · windows (push) Has been skipped
WAI conformance / fast integer kernels, every x86_64 kernel set (AVX-512 silicon) (push) Has been skipped
WAI conformance / quantum core and corpus under wasm32-wasip2 (wasmtime) (push) Has been skipped
WAI conformance / faces from keypoints under wasm32-wasip2 (wasmtime) (push) Has been skipped
WAI conformance / v2 vocabulary · manifests validate as stated (push) Has been skipped
WAI conformance / independent verifiers · claims, JWP, key discovery and integer payloads (Python) (push) Has been skipped
WAI conformance / independent verifiers · every verifier step, each corpus's Rust side (push) Has been skipped
WAI conformance / each feature of the independent verifiers' steps compiles alone (push) Has been skipped
WAI conformance / delivery telemetry · CMCD and CMSD against an independent library (push) Has been skipped
WAI conformance / cross-arch byte-equality · linux (push) Has been skipped
WAI conformance / cross-arch byte-equality · linux-arm64 (push) Has been skipped
WAI conformance / cross-arch byte-equality · macos (push) Has been skipped
WAI conformance / codec wrappers · fuzz smoke, text decode limits, C entry points, provenance, the semantic path (push) Has been skipped
WAI conformance / C2PA manifest emission · signed, read back, validated (push) Has been skipped
WAI conformance / independent verifiers · each needs= is what a run without packages shows (nightly) (push) Has been skipped
WAI operation catalogue / does this change touch it (push) Successful in 1m1s
wai-quantum pulse and dynamics / does this change touch it (push) Successful in 1m20s
wai-quantum OpenQASM reader / does this change touch it (push) Successful in 1m22s
wai-quantum resource estimation / does this change touch it (push) Successful in 1m22s
wai-quantum for JavaScript / does this change touch it (push) Successful in 1m23s
wai-quantum docs, course and locales / does this change touch it (push) Successful in 1m23s
wai-audit collector and player integration / does this change touch it (push) Successful in 1m37s
wai-quantum other formats / does this change touch it (push) Successful in 1m25s
wai-quantum for Python / does this change touch it (push) Successful in 1m18s
wai-quantum contract / does this change touch it (push) Successful in 2m50s
wai-quantum service / does this change touch it (push) Successful in 1m40s
wai-quantum extension points / does this change touch it (push) Successful in 1m44s
wai-quantum command line / does this change touch it (push) Successful in 3m8s
wai-quantum studio and desktop / does this change touch it (push) Successful in 1m46s
wai-quantum variational and algorithm library / does this change touch it (push) Successful in 1m46s
wai-quantum verdicts and repairs / does this change touch it (push) Successful in 1m47s
wai-quantum error correction and mitigation / does this change touch it (push) Successful in 1m53s
wai-quantum views in agent hosts / does this change touch it (push) Successful in 1m47s
WAI Studio / does this change touch it (push) Successful in 59s
wai-sink-android native sink library / does this change touch it (push) Successful in 1m55s
wai-quantum clippy / does this change touch it (push) Successful in 4m26s
wai-quantum editors / does this change touch it (push) Successful in 5m18s
wai-quantum pulse and dynamics / the same bits · macos (push) Successful in 3m34s
wai-sink-swift native sink package / does this change touch it (push) Successful in 4m43s
wai-audit collector and player integration / collector, player integration and the browser end to end · macos (push) Successful in 2m20s
wai-quantum command line / command line · macos (push) Successful in 3m20s
wai-quantum service / service energy · linux packages (push) Successful in 8m59s
WAI breaking changes and the conformance runner / WAI breaking · does this change touch WAI (push) Successful in 14s
WAI examples / WAI examples · does this change touch them (push) Successful in 14s
wai-quantum wasmQ / does this change touch it (push) Successful in 12s
WAI conformance / a push to main · corpus provenance and sidecars, signer ids, the registry export (push) Successful in 1m4s
wai-quantum service / service · macos (push) Successful in 8m12s
wai-quantum studio and desktop / desktop app · macos (push) Successful in 5m33s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs under wasm32-wasip2 (wasmtime) (push) Successful in 3m0s
wai-quantum pulse and dynamics / the same bits · linux-arm64 (push) Successful in 7m42s
wai-quantum editors / editors · macos (push) Successful in 1m59s
repository checks / lockfiles, advisories, tests in CI, workflow hygiene, spec and section references, public text (push) Successful in 10m31s
wai-quantum OpenQASM reader / corpus under wasm32-wasip2 (wasmtime) (push) Successful in 1m52s
wai-quantum pulse and dynamics / reader, simulator and pulse jobs · linux, release (push) Successful in 4m5s
wai-quantum docs, course and locales / the Studio's words in every locale · linux (push) Successful in 13s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux-arm64, release (push) Successful in 4m10s
wai-quantum OpenQASM reader / reader and corpus · linux (push) Successful in 5m9s
wai-sink-android native sink library / every ABI, the AAR, and the instrumented tests in a headless emulator · macos (push) Successful in 8m44s
wai-quantum for Python / python · macos (push) Successful in 14m49s
wai-quantum other formats / the set under wasm32-wasip2 (wasmtime) (push) Successful in 2m46s
wai-audit collector and player integration / collector · linux (push) Successful in 5m8s
wai-quantum contract / the policy is linked, and the deprecations page reads (push) Successful in 17s
wai-quantum docs, course and locales / the course and the command line's languages · linux (push) Successful in 8m34s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · macos (push) Successful in 5m51s
wai-quantum other formats / readers, writers and the set · linux (push) Successful in 8m34s
wai-quantum for JavaScript / javascript · linux (push) Successful in 11m31s
WAI Studio / Studio in Chromium and Firefox · macos (push) Successful in 15m31s
wai-quantum extension points / the registry under wasm32-wasip2 (wasmtime) (push) Successful in 5m22s
wai-sink-android native sink library / the JNI layer and the platform-free half on the host JVM · linux-arm64 (push) Successful in 17m18s
wai-quantum contract / API compatibility · linux (push) Successful in 8m40s
wai-quantum for Python / python · linux (push) Successful in 11m36s
wai-quantum extension points / the registry and the example · linux-arm64 (push) Successful in 22m27s
WAI operation catalogue / the catalogue and every door · linux (push) Successful in 21m15s
wai-quantum verdicts and repairs / the intent set and the verdict under wasm32-wasip2 (wasmtime) (push) Successful in 3m51s
wai-quantum variational and algorithm library / parameters, gradients, optimisers, VQE, QAOA and builders · linux, release (push) Successful in 4m6s
WAI Studio / the catalogue's actions, the unit tests and the core · linux (push) Successful in 1m14s
WAI examples / WAI examples · android-app · linux-arm64 (push) Successful in 19m9s
wai-quantum studio and desktop / studio · browser (push) Successful in 7m37s
wai-quantum verdicts and repairs / verdicts, repairs and the intent set · linux (push) Successful in 6m50s
wai-sink-swift native sink package / libwai slices, desktop tests, simulator tests · macos (push) Successful in 21m21s
wai-quantum extension points / the registry and the example · linux (push) Successful in 13m41s
wai-quantum studio and desktop / desktop app · windows (push) Successful in 12m53s
wai-quantum resource estimation / estimation and calibration · linux, release (push) Successful in 21m40s
wai-quantum wasmQ / the reference modules rebuild to their modules of record · linux (push) Successful in 48s
wai-quantum studio and desktop / desktop app · linux (push) Successful in 10m7s
WAI breaking changes and the conformance runner / WAI breaking · wire, record and API against main · linux (push) Successful in 3m58s
wai-quantum views in agent hosts / views · the page and its engine (push) Successful in 8m43s
wai-quantum views in agent hosts / views · a scripted host (push) Successful in 8m26s
wai-quantum error correction and mitigation / codes, decoders, texts and mitigation · linux, release (push) Successful in 9m18s
wai-quantum clippy / clippy, warnings denied, every feature set · linux (push) Successful in 6m3s
wai-quantum contract / schemas and the committed tool lists · linux (push) Successful in 19m32s
wai-quantum editors / editors · linux (push) Successful in 7m22s
wai-quantum service / service · linux (push) Successful in 20m11s
wai-quantum wasmQ / interpreter and vectors under wasm32-wasip2 (wasmtime) (push) Successful in 6m34s
WAI breaking changes and the conformance runner / WAI breaking and conform · their tests, and the independent verifiers through wai conform · linux (push) Successful in 11m33s
wai-quantum wasmQ / profile, engines, reference modules, adversarial, vectors · linux (push) Successful in 10m43s
WAI examples / WAI examples · the archive, the readiness check, the browser and Apple · macos (push) Successful in 32m24s
wai-quantum command line / command line · linux (push) Successful in 34m57s
WAI Studio / Studio in WebKit · linux (push) Successful in 28m32s
WAI examples / WAI examples · portable · linux (push) Successful in 28m33s
wai-quantum docs, course and locales / every command and snippet in the docs · linux (push) Successful in 53m22s
WAI Quantum: the tracker's 49-qubit operator Loschmidt echo corrected loop by loop around belief propagation, and the four fast receipts sealed with the operator's key again
quantum_bptn::clusters (written from J. Gray, G. Park, G. Evenbly,
N. Pancotti, E. F. Kjonstad and G. K.-L. Chan 2025, and S. Midha and
Y. F. Zhang 2025): Tns::expect_region_bp contracts any region exactly with
belief propagation's messages on its boundary, which is the loop series
restricted to the region summed to every order. Each piece is contracted
from the leaves of a spanning tree to a root, the bonds the tree leaves out
cut and summed over their ket and bra indices; every set of cuts that
leaves a spanning tree (up to 20,000) is tried with every root against an
estimate of the work and of the tables' memory, and the cheapest whose
tables fit in 16 GiB is taken (RegionPlan). On the 49x648 echo at chi = 64
that halves the largest three-ring region's work (1.0e15 -> 4.9e14) and
cuts another's by 4.5 (8.3e13 -> 1.8e13) against cutting the smallest bonds
of a spanning tree of the largest. Combinations are summed as they come,
each sum in a fixed order and each rescaling by a power of two: the same
bits at any worker count and on every platform. Also loop_weight (a ring's
own correction Z_l, the loop correlation check), counting_numbers and
loop_cluster_terms (Gray et al.'s Algorithm 1), cluster_combine (product
and sum formulas), short_cycles and cell_unions.

tracker_ole mode loops: the arc through the observable's sites (order 0),
then order k = the observable's ring F joined with every connected set of
k - 1 other rings, closed under intersection and counted; regions past the
run's work or memory budget are left out and named, and the order over the
regions read is reported beside it.

49x648, chi = 64, 96, 128, 160, 192, 256, 320 (summed discarded weight
0.088 to 0.0015): order 1 (F exact) is the fast sweep's loop readout, the
arc its arc bit for bit. Order 2 (F joined with D and with E, the two rings
sharing a path with it) adds +0.0019, +0.0026, +0.0031, +0.0034, +0.0036,
+0.0039, +0.0040, growing with chi, a nineteenth to a twelfth of what order
1 adds to the arc; at chi = 320 it reads 0.8235. On a line in the discarded
weight through the last three chi (residual 3e-6) order 2 meets none
discarded at 0.82251 (order 1: 0.81845, as before). The ring D gives almost
all of it (DF over F +0.20 % at chi 64, +0.46 % at 320). Each ring's own
Z_l is at most 2.8e-4 in size and falls with chi.

Order 3 not reached: of its five three-ring regions only BDF fits the
budgets run (6e12 to 2e13 multiply-adds), and it moves the value by at most
2e-10; at chi = 64 the others need 1.8e13 to 4.9e14 multiply-adds (ADF's
tables 309 GB, past every host's cap), above chi = 64 1.4e14 to 1.2e16.
Error estimate, with its assumption: if each order adds the same fraction
of the one before as order 2 does of order 1 (Aitken's delta-squared on
arc, order 1, order 2), the orders not run add 1.0e-4 at chi = 64 and
3.5e-4 at chi = 320, and the estimate extrapolates to 0.82287. That is an
estimate, not a bound, and it is not tested against a third order here.
Published: device 0.824, belief-propagation tensor networks 0.8203 (bond
192) and 0.8217 (bond 512), single-path Monte Carlo 0.808.

49x1296 and 70x1872: not converged (summed discarded weight 13 to 29), and
order 2 there needs 6e13 to 1e14 multiply-adds at chi = 32, 1.6e15 at 48
and 1.6e16 at 64 (plan-only runs); the expansion corrects contraction, not
truncation, so it cannot make those readings bound the echo.

At chi = 96 all 24 numbers of the expansion have the same bits on Linux
x86_64 (32 workers) and macOS arm64 (16 workers).

Receipts: 49x648 loops at chi 96 and 128 sealed with the operator's key on
Linux x86_64; chi 64, 160, 192, 256 and 320 with the demonstration key on
macOS arm64. The four fast receipts cb44dd4a8 re-sealed with the
demonstration key (49x648 chi 64 and 96, 49x1296 and 70x1872 chi 64) are
sealed with the operator's key again: re-run on Linux x86_64 in their
committed settings, each request equal to the committed one and every
number in each result the committed bits.

Tests: clusters unit tests (a region holding every loop is exact whatever
its cuts; worker-count bits; the cuts chosen cost no more than the largest
bonds' tree's); tracker_reference: on a ladder of three squares order 2 is
nearer the network's exact contraction than order 1 in four trials of six
(summed distances: arc 3.43e-3, order 1 2.89e-3, order 2 1.85e-3), and the
49x648 loops receipts pinned.

Change note: wai/changes/2026-11-14-wai-quantum-ole-loop-cluster-expansion.md.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-11-14 08:34:52 -05:00

283 lines
13 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.

//! The operator Loschmidt echo `f = 2⁻ⁿ Tr(O W† O W)` of a circuit written
//! `W = T · M · T⁻¹` (tail, middle, head), as a tensor network on the coupling
//! graph with an ancilla on every qubit: the purification of `W` as an
//! operator, `|W⟩⟩ = (W ⊗ I)|Φ⟩`, `|Φ⟩` a Bell pair on each qubit and its
//! ancilla. After each ancilla's `Z` is folded onto its qubit by a `CX`, `f` is
//! `⟨Π_{q ∈ O} Z_q⟩` in that state (see `common::purified_echo`).
//!
//! The network is evolved in the circuit's order (head, middle, tail) by
//! simple update with the dense blocked linear algebra
//! (`Tns::with_blocked_linalg`), and read three ways: along the arc of the
//! loop through the observable's sites with the simple-update environment
//! (the bond weights), the same with belief propagation's environment, and
//! around the whole loop with belief propagation's environment on every bond
//! off it. Without the middle the exact echo is 1, which gives the divisor of
//! the published "rescaled" values.
#![allow(dead_code)]
use wai_quantum::quantum_bptn::{BpEnv, Tns};
use wai_quantum::quantum_spd::{Op, Pauli, RotCircuit};
use crate::common;
/// The purified echo cut into its parts, on `2n` qubits (ancilla `n + q` of
/// qubit `q`).
pub struct Purified {
pub n: u32,
pub edges: Vec<(u32, u32)>,
/// The Bell pairs.
pub prep: RotCircuit,
/// `T⁻¹`, on the qubits.
pub head: RotCircuit,
/// `M`, on the qubits.
pub middle: RotCircuit,
/// `T`, then the folds of the ancillas' `Z`s.
pub tail: RotCircuit,
/// The observable's qubits.
pub sites: Vec<u32>,
/// The loop through them.
pub ring: Vec<u32>,
/// The loop's arc from the first site through the others to the last.
pub path: Vec<u32>,
}
/// Cut `circuit` (head of `head_len` ops, middle of `middle_len`, the rest the
/// tail) and purify it for the `Z`s on `sites`.
pub fn purified(circuit: &RotCircuit, head_len: usize, middle_len: usize, sites: &[u32]) -> Purified {
let n = circuit.n;
let mut edges = common::coupling(circuit);
edges.extend((0..n).map(|q| (q, n + q)));
let mut prep = RotCircuit::new(2 * n);
for q in 0..n {
prep.push(Op::H(q)).push(Op::CX(q, n + q));
}
let part = |ops: &[Op]| RotCircuit { n: 2 * n, ops: ops.to_vec() };
let head = part(&circuit.ops[..head_len]);
let middle = part(&circuit.ops[head_len..head_len + middle_len]);
let mut tail = part(&circuit.ops[head_len + middle_len..]);
for &q in sites {
tail.push(Op::CX(n + q, q));
}
let ring = common::loop_through(&common::coupling(circuit), sites).expect("the observable's qubits lie on one loop");
// The ring starts at the first site and meets the others in order.
let last = ring.iter().position(|&q| q == *sites.last().expect("a site")).expect("on the loop");
let path = ring[..=last].to_vec();
Purified { n, edges, prep, head, middle, tail, sites: sites.to_vec(), ring, path }
}
/// What a network reads, and what it took.
#[derive(Clone, Debug)]
pub struct Reading {
/// Along the path, the simple-update environment.
pub vidal_path: f64,
/// Along the path, belief propagation's environment.
pub bp_path: Option<f64>,
/// Around the loop, belief propagation's environment.
pub bp_loop: Option<f64>,
pub bp_sweeps: usize,
pub bp_residual: f64,
pub max_bond: usize,
/// The loop's bond dimensions, in its order from its first vertex.
pub loop_bonds: Vec<usize>,
pub discarded: f64,
pub seconds: f64,
}
/// Read `Π_{q ∈ sites} Z_q` from `t`: along the path with the simple-update
/// environment; with `bp_sweeps > 0` also with belief propagation's, and
/// around the loop when the largest bond is at most `loop_max`.
pub fn read(t: &Tns, p: &Purified, bp_sweeps: usize, bp_tol: f64, loop_max: usize) -> Reading {
let started = std::time::Instant::now();
let ops: Vec<(u32, Pauli)> = p.sites.iter().map(|&q| (q, Pauli::Z)).collect();
let vidal_path = t.expect_path_bp(&t.vidal_env(), &p.path, &ops).expect("the path");
let (mut bp_path, mut bp_loop, mut sweeps, mut residual) = (None, None, 0, f64::NAN);
if bp_sweeps > 0 {
let env: BpEnv = t.belief_propagation(bp_sweeps, bp_tol);
sweeps = env.sweeps;
residual = env.residual;
bp_path = Some(t.expect_path_bp(&env, &p.path, &ops).expect("the path"));
if t.max_bond() <= loop_max {
bp_loop = Some(t.expect_loop_bp(&env, &p.ring, &ops).expect("the loop"));
}
}
let weights = t.bond_weights();
let loop_bonds = (0..p.ring.len())
.map(|i| {
let (a, b) = (p.ring[i], p.ring[(i + 1) % p.ring.len()]);
let e = p.edges.iter().position(|&(x, y)| (x, y) == (a.min(b), a.max(b)) || (x, y) == (a, b) || (x, y) == (b, a)).expect("a loop edge");
weights[e].len()
})
.collect();
Reading {
vidal_path,
bp_path,
bp_loop,
bp_sweeps: sweeps,
bp_residual: residual,
max_bond: t.max_bond(),
loop_bonds,
discarded: t.discarded(),
seconds: started.elapsed().as_secs_f64(),
}
}
/// One region of the loop cluster expansion, read.
#[derive(Clone, Debug)]
pub struct RegionRead {
/// The region's qubits (network vertices), ascending.
pub region: Vec<u32>,
/// How many of the graph's rings it joins (the observable's counted).
pub cells: usize,
pub cuts: Vec<(u32, u32, usize)>,
pub combinations: u64,
/// The contraction's estimated work (complex multiply-adds).
pub work: f64,
/// The most bytes its tables hold at once (estimated).
pub bytes: f64,
/// `None` where its work or memory was past the budget.
pub value: Option<f64>,
pub norm_ratio: Option<f64>,
pub seconds: f64,
}
/// One order of the expansion: every union of the observable's ring with at
/// most `cells − 1` other rings as the largest regions.
#[derive(Clone, Debug)]
pub struct Order {
pub cells: usize,
/// (region, counting number), as `Tns::loop_cluster_terms` gives them.
pub terms: Vec<(Vec<u32>, i64)>,
/// The product and sum formulas, where every term's region was read.
pub product: Option<f64>,
pub sum: Option<f64>,
/// Where some region was past the budget: the expansion over the
/// largest regions that were read.
pub partial: Option<Partial>,
}
/// An order's expansion with some of its largest regions left out (each one
/// past the budget): the regions left are closed under intersection and
/// counted afresh, so this is the loop cluster expansion over them.
#[derive(Clone, Debug)]
pub struct Partial {
/// The largest regions left out, as unions of rings (not pruned).
pub left_out: Vec<Vec<u32>>,
pub terms: Vec<(Vec<u32>, i64)>,
pub product: Option<f64>,
pub sum: Option<f64>,
}
/// What [`clusters`] gives: the graph's rings, the orders, every region read
/// (in the order first met), and each ring's norm over its belief-propagation
/// value where it was within the budget.
#[derive(Clone, Debug)]
pub struct Expansion {
pub cells: Vec<Vec<u32>>,
pub orders: Vec<Order>,
pub reads: Vec<RegionRead>,
pub weights: Vec<Option<f64>>,
}
/// The loop cluster expansion of `Π_{q ∈ sites} Z_q` around the observable's
/// ring: the graph's rings (cycles of at most `ring_len` vertices), the
/// orders up to `max_cells`, each region contracted exactly with `env` on its
/// boundary when its estimated work is at most `max_work` multiply-adds and
/// its tables at most `max_bytes`. An order missing a region has no value;
/// the higher orders' regions within the budget are still read. Also each
/// ring's own correction to belief propagation (its norm over its
/// belief-propagation value), where within the budget.
pub fn clusters(t: &Tns, env: &BpEnv, p: &Purified, ring_len: usize, max_cells: usize, max_work: f64, max_bytes: f64) -> Expansion {
use wai_quantum::quantum_bptn::{cell_unions, cluster_combine, short_cycles};
let ops: Vec<(u32, Pauli)> = p.sites.iter().map(|&q| (q, Pauli::Z)).collect();
let mut keep = p.sites.clone();
keep.sort_unstable();
keep.dedup();
let cells = short_cycles(2 * p.n as usize, &p.edges, ring_len);
let mut reads: Vec<RegionRead> = Vec::new();
let mut orders = Vec::new();
let count_cells = |r: &[u32]| cells.iter().filter(|c| c.iter().all(|v| r.binary_search(v).is_ok())).count();
// A region's value, read once (within the budget) and kept.
let value_of = |r: &[u32], reads: &mut Vec<RegionRead>| -> Option<f64> {
if let Some(x) = reads.iter().find(|x| x.region == r) {
return x.value;
}
let plan = t.region_plan(r, &ops).expect("a region");
let started = std::time::Instant::now();
let (value, norm_ratio) = if plan.work <= max_work && plan.bytes <= max_bytes {
let got = t.expect_region_bp(env, r, &ops).expect("a region");
(Some(got.value), Some(got.norm_ratio))
} else {
(None, None)
};
let read = RegionRead { region: r.to_vec(), cells: count_cells(r), cuts: plan.cuts, combinations: plan.combinations, work: plan.work, bytes: plan.bytes, value, norm_ratio, seconds: started.elapsed().as_secs_f64() };
println!(
" region of {} rings, {} qubits: cuts {:?}, {} combinations, work {:.2e}, tables {:.2e} B: {} ({:.1} s)",
read.cells,
r.len(),
read.cuts.iter().map(|c| c.2).collect::<Vec<_>>(),
read.combinations,
read.work,
read.bytes,
read.value.map_or("past the budget".to_string(), |v| format!("{v:.6}, norm ratio {:.6}", read.norm_ratio.unwrap_or(f64::NAN))),
read.seconds
);
reads.push(read);
value
};
let combine = |terms: &[(Vec<u32>, i64)], values: &[Option<f64>]| -> (Option<f64>, Option<f64>) {
match values.iter().copied().collect::<Option<Vec<f64>>>() {
Some(v) => {
let (a, b) = cluster_combine(terms, &v);
(Some(a), Some(b))
}
None => (None, None),
}
};
for k in 1..=max_cells {
let maxima = cell_unions(&p.ring, &cells, k);
let terms = t.loop_cluster_terms(&maxima, &ops).expect("regions holding the observable");
let values: Vec<Option<f64>> = terms.iter().map(|(r, _)| value_of(r, &mut reads)).collect();
let (product, sum) = combine(&terms, &values);
println!(" order {k}: {} terms, product {:?}, sum {:?}", terms.len(), product, sum);
// Where a region is past the budget: the expansion over the maxima
// that were read, its intersections read too where within the budget.
let mut partial = None;
if product.is_none() {
let (read, left_out): (Vec<Vec<u32>>, Vec<Vec<u32>>) = maxima.iter().cloned().partition(|m| {
let r = t.prune_region(m, &keep).expect("a region");
reads.iter().any(|x| x.region == r && x.value.is_some())
});
if !read.is_empty() {
let terms = t.loop_cluster_terms(&read, &ops).expect("regions holding the observable");
let values: Vec<Option<f64>> = terms.iter().map(|(r, _)| value_of(r, &mut reads)).collect();
let (product, sum) = combine(&terms, &values);
println!(" order {k} without {} of its {} largest regions: {} terms, product {:?}, sum {:?}", left_out.len(), maxima.len(), terms.len(), product, sum);
partial = Some(Partial { left_out, terms, product, sum });
}
}
orders.push(Order { cells: k, terms, product, sum, partial });
}
let weights = cells
.iter()
.map(|c| {
let plan = t.region_plan(c, &[]).expect("a ring");
(plan.work <= max_work && plan.bytes <= max_bytes).then(|| t.loop_weight(env, c).expect("a ring"))
})
.collect();
Expansion { cells, orders, reads, weights }
}
/// The whole operator `|W⟩⟩` (or, without the middle, `|T T⁻¹⟩⟩`, whose exact
/// echo is 1) at bond cap `chi`. Returns the network and the seconds taken.
pub fn whole(p: &Purified, chi: usize, threads: usize, with_middle: bool) -> (Tns, f64) {
let started = std::time::Instant::now();
let mut t = Tns::new(2 * p.n, &p.edges, chi).expect("the graph").with_blocked_linalg(threads);
t.run(&p.prep).expect("gates on the graph");
t.run(&p.head).expect("gates on the graph");
if with_middle {
t.run(&p.middle).expect("gates on the graph");
}
t.run(&p.tail).expect("gates on the graph");
(t, started.elapsed().as_secs_f64())
}