wai-quantum 0.3.34: ground states of spin chains by DMRG, held to exact answers #159

Merged
dcharlot merged 1 commit from wai-quantum-dmrg into main 2026-10-05 12:53:37 -04:00
14 changed files with 1335 additions and 20 deletions

View file

@ -589,7 +589,7 @@ dependencies = [
[[package]]
name = "wai-quantum"
version = "0.3.33"
version = "0.3.34"
dependencies = [
"blake3",
"ed25519-dalek",

View file

@ -415,7 +415,7 @@ checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a"
[[package]]
name = "wai-quantum"
version = "0.3.33"
version = "0.3.34"
dependencies = [
"blake3",
"ed25519-dalek",

View file

@ -1,6 +1,6 @@
[package]
name = "wai-quantum"
version = "0.3.33"
version = "0.3.34"
edition = "2024"
rust-version = "1.97"
license = "Apache-2.0"
@ -79,6 +79,9 @@ quantum_uf = ["quantum_frame"]
# Noisy circuits: quantum trajectories on the state vector, an exact density-
# matrix referee, and cross-entropy fidelity against the digital error model.
quantum_noisy = ["quantum_sv"]
# Ground states of spin chains by two-site DMRG, held to exact diagonalisation
# and to the free-fermion solution of the transverse-field Ising chain.
quantum_dmrg = []
# Mixed states and the observables that distinguish quantum theory from a classical
# account: density matrices, the partial trace, exact Pauli expectations, CHSH.
quantum_info = ["quantum"]
@ -118,7 +121,7 @@ quantum_phasor_meter = ["_signing"]
# Everything.
full = [
"quantum", "quantum_toolchain", "quantum_mps", "quantum_stabilizer", "quantum_pauli", "quantum_spd", "quantum_spd_receipt", "quantum_bptn", "quantum_resource", "quantum_resource_receipt", "quantum_frame", "quantum_tn", "quantum_gbs", "quantum_sv", "quantum_arch", "quantum_ldpc", "quantum_qio", "quantum_uf", "quantum_noisy", "quantum_qir", "quantum_info", "quantum_qudit", "quantum_comm", "quantum_source", "quantum_channel",
"quantum", "quantum_toolchain", "quantum_mps", "quantum_stabilizer", "quantum_pauli", "quantum_spd", "quantum_spd_receipt", "quantum_bptn", "quantum_resource", "quantum_resource_receipt", "quantum_frame", "quantum_tn", "quantum_gbs", "quantum_sv", "quantum_arch", "quantum_ldpc", "quantum_qio", "quantum_uf", "quantum_noisy", "quantum_dmrg", "quantum_qir", "quantum_info", "quantum_qudit", "quantum_comm", "quantum_source", "quantum_channel",
"quantum_receipt", "quantum_ops", "quantum_cal", "quantum_mitigate", "quantum_qec",
"quantum_control", "quantum_noise", "quantum_compile", "quantum_atom", "quantum_qbom",
"quantum_vml", "quantum_phasor", "quantum_qfhrr", "quantum_kernel", "quantum_qdata",
@ -129,6 +132,10 @@ full = [
name = "qio_gset"
required-features = ["quantum_qio"]
[[example]]
name = "dmrg_chain"
required-features = ["quantum_dmrg"]
[[example]]
name = "uf_bench"
required-features = ["quantum_uf"]

View file

@ -66,6 +66,7 @@ never creates information.
| `quantum_resource_receipt` | a signed, energy-accounted receipt over an estimate, re-checkable bit for bit on any machine |
| `quantum_qio` | **quantum-inspired optimisation**: Ising and Max-Cut problems (G-set reader, spin-glass generator), simulated bifurcation in its ballistic, discrete and heated forms (batched eight trials per pass, bit-identical to one at a time), simulated annealing, single-flip descent, an exact Gray-code referee up to 34 spins, and step-to-solution. Reaches the best-known cut on seven G-set instances |
| `quantum_uf` | **union-find decoding in near-linear time** over the same matching graph: event-driven cluster growth on integer weights (a radix heap orders the fill events), merges, and peeling. Measured against exact matching on the same shots: within 0.92–1.18× the failures, up to 77× faster at `d = 11` |
| `quantum_dmrg` | **ground states of spin chains by DMRG**: matrix-product operators for nearest-neighbour chains (XXZ Heisenberg in a field, transverse-field Ising), two-site sweeps with Lanczos and density-matrix truncation, entanglement entropies, a symmetric eigensolver, and two exact referees: full diagonalisation up to 22 sites, and the free-fermion solution of the Ising chain at any length. The critical Ising chain is reproduced to about `10⁻¹¹` per site at 200 sites |
| `quantum_noisy` | **noisy circuits**: quantum trajectories on the state vector (depolarizing, dephasing, amplitude damping, readout), an exact density-matrix referee up to 12 qubits that reuses the same kernels, linear cross-entropy fidelity (sampled, exact and the circuit's own), and the digital error model. Measures where that model is off: below the true fidelity by a margin that grows with the error rate, not with depth |
| `quantum_ldpc` | **qLDPC memory at circuit level**: bivariate-bicycle codes from their two polynomials (`k` by rank, logical bases), the depth-8 syndrome cycle with circuit-level noise, its detector error model, and BP-OSD decoding (min-sum with adaptive scaling, ordered statistics with a combination sweep). The error model has the same columns as the source's own decoding matrices, and on identical syndromes the decoder makes the source's decisions shot for shot |
| `quantum_arch` | **architecture-level estimation**: yoked surface-code storage (row and grid parity checks over patches, hot and cold), magic-state cultivation read off the published simulation curves, CCZ factories, and a clock set by whichever is slowest of lattice surgery, the magic-state supply and the control loop. Assembles whole-machine estimates; its storage search matches the source's footprint tool layout for layout |
@ -178,6 +179,24 @@ in one pass. On the G-set Max-Cut benchmark the best-known cut is reached on
G1, G2, G3, G11, G14, G22 and G43. On G55 the best found is 10,292 against
10,299. The module documents each setting and success rate, and the bound
that sparse graphs put on the time step.
## Spin chains
```sh
wai-quantum dmrg -n 200 --bond 64 --sweeps 4 # critical Ising chain, against its exact energy
wai-quantum dmrg --chain heisenberg -n 100 --bond 128 # entanglement profile and discarded weight included
```
Claims that classical computers cannot simulate a material are claims about
entanglement. For one-dimensional systems the density-matrix renormalization
group usually can simulate it, with an error it reports itself.
`quantum_dmrg` is that method, held to exact answers:
- full diagonalisation for short chains;
- the free-fermion solution of the transverse-field Ising chain at any length.
At 200 sites the critical Ising chain comes out within about `10⁻¹¹` per site
of its exact energy. Runs repeat bit for bit at any thread count, natively and
as WebAssembly.
## Noisy circuits
```sh

View file

@ -0,0 +1,34 @@
//! Ground-state DMRG on a spin chain, against the exact answer where one
//! exists.
//!
//! ```text
//! cargo run --release --example dmrg_chain --features quantum_dmrg -- ising|heisenberg N BOND SWEEPS [h]
//! ```
use wai_quantum::quantum_dmrg::{dmrg, exact_ground_energy, ising_exact_energy, Chain, DmrgConfig};
fn main() {
let a: Vec<String> = std::env::args().collect();
let n: usize = a[2].parse().unwrap();
let bond: usize = a[3].parse().unwrap();
let sweeps: usize = a[4].parse().unwrap();
let field: f64 = a.get(5).map_or(1.0, |x| x.parse().unwrap());
let (chain, exact) = match a[1].as_str() {
"ising" => (Chain::ising(n, 1.0, field), Some(ising_exact_energy(n, 1.0, field))),
"heisenberg" => {
let c = Chain::heisenberg(n, 1.0, 1.0, 0.0);
let e = if n <= 20 { exact_ground_energy(&c) } else { None };
(c, e)
}
other => panic!("{other}"),
};
let t0 = std::time::Instant::now();
let r = dmrg(&chain, &DmrgConfig { max_bond: bond, sweeps, ..DmrgConfig::default() });
let dt = t0.elapsed().as_secs_f64();
let mid = r.entropies[n / 2 - 1];
print!("{} n={n} bond={bond} sweeps={sweeps} E={:.12} per site {:.12} discarded {:.1e} S(mid) {:.4}", a[1], r.energy, r.energy / n as f64, r.discarded, mid);
if let Some(e) = exact {
print!(" exact {e:.12} error/site {:.1e}", (r.energy - e) / n as f64);
}
println!(" {dt:.1}s");
println!("sweeps: {:?}", r.sweep_energies.iter().map(|e| format!("{e:.10}")).collect::<Vec<_>>());
}

View file

@ -37,7 +37,8 @@ pub mod gates;
feature = "quantum_gbs",
feature = "quantum_sv",
feature = "quantum_arch",
feature = "quantum_qio"
feature = "quantum_qio",
feature = "quantum_dmrg"
))]
mod repro;
@ -119,6 +120,9 @@ pub mod quantum_uf;
#[cfg(feature = "quantum_noisy")]
pub mod quantum_noisy;
#[cfg(feature = "quantum_dmrg")]
pub mod quantum_dmrg;
#[cfg(feature = "quantum_frame")]
pub mod quantum_match;

File diff suppressed because it is too large Load diff

View file

@ -34,28 +34,28 @@ pub use trig::sin_cos;
// Natural logarithm
// ---------------------------------------------------------------------------
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LN2_HI: f64 = 6.931_471_803_691_238_164_90e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LN2_LO: f64 = 1.908_214_929_270_587_700_02e-10;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LG1: f64 = 6.666_666_666_666_735_130e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LG2: f64 = 3.999_999_999_940_941_908e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LG3: f64 = 2.857_142_874_366_239_149e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LG4: f64 = 2.222_219_843_214_978_396e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LG5: f64 = 1.818_357_216_161_805_012e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LG6: f64 = 1.531_383_769_920_937_332e-1;
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
const LG7: f64 = 1.479_819_860_511_658_591e-1;
/// The natural logarithm, within an ulp of the true value: `−∞` at zero, NaN
/// below it.
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
pub fn ln(x: f64) -> f64 {
let mut bits = x.to_bits();
let mut hx = (bits >> 32) as u32;
@ -355,7 +355,7 @@ mod tests {
})
}
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio"))]
#[cfg(any(feature = "quantum_vml", feature = "quantum_kernel", feature = "quantum_resource", feature = "quantum_frame", feature = "quantum_tn", feature = "quantum_gbs", feature = "quantum_arch", feature = "quantum_qio", feature = "quantum_dmrg"))]
#[test]
fn ln_is_within_an_ulp_of_the_platform() {
for x in inputs().filter(|&x| x > 0.0) {

View file

@ -24,7 +24,8 @@
feature = "quantum_ldpc",
feature = "quantum_qio",
feature = "quantum_uf",
feature = "quantum_noisy"
feature = "quantum_noisy",
feature = "quantum_dmrg"
))]
use wai_quantum::*;
@ -449,3 +450,27 @@ fn noisy_circuits() {
v.push(digital_fidelity(&g, n, &noise));
assert_eq!(digest(&v), 0x78b2_ddfb_e7c3_6bea);
}
/// DMRG: the symmetric eigensolver, Lanczos with re-orthogonalisation,
/// density-matrix truncation and entropies (in-crate `ln`), on two chains;
/// the free-fermion Ising energy; and exact diagonalisation.
#[test]
fn dmrg_ground_states() {
use quantum_dmrg::*;
let mut v = Vec::new();
let cfg = DmrgConfig { max_bond: 16, sweeps: 2, ..DmrgConfig::default() };
for chain in [Chain::heisenberg(10, 1.0, 0.8, 0.1), Chain::ising(12, 1.0, 0.9)] {
let r = dmrg(&chain, &cfg);
v.extend(r.sweep_energies);
v.push(r.discarded);
v.extend(r.entropies);
v.push(exact_ground_energy(&chain).unwrap());
}
v.push(ising_exact_energy(40, 1.0, 0.7));
let a: Vec<f64> = (0..36).map(|k| ((k * 7 % 11) as f64 - 5.0) / (1.0 + (k % 6) as f64)).collect();
let sym: Vec<f64> = (0..36).map(|k| a[k] + a[(k % 6) * 6 + k / 6]).collect();
let (vals, vecs) = symmetric_eigen(&sym, 6);
v.extend(vals);
v.extend(vecs.iter().map(|x| x.abs()));
assert_eq!(digest(&v), 0x3b28_8bdd_be3f_48a2);
}

2
wai/wai-rs/Cargo.lock generated
View file

@ -3900,7 +3900,7 @@ dependencies = [
[[package]]
name = "wai-quantum"
version = "0.3.33"
version = "0.3.34"
dependencies = [
"blake3",
"ed25519-dalek",

View file

@ -368,7 +368,7 @@ quantum_arch = ["dep:wai-quantum", "wai-quantum/quantum_arch"]
# qLDPC memory at circuit level: bivariate-bicycle codes, BP-OSD.
quantum_ldpc = ["quantum_frame", "wai-quantum/quantum_ldpc"]
# Quantum-inspired optimisation: simulated bifurcation, annealing, exact referee.
quantum_qio = ["dep:wai-quantum", "wai-quantum/quantum_qio"]
quantum_qio = ["dep:wai-quantum", "wai-quantum/quantum_qio", "wai-quantum/quantum_dmrg"]
quantum_qir = ["quantum_toolchain", "wai-quantum/quantum_qir"]
# Variational quantum machine learning (extensions/quantum-sim § VML,
# `wai.quantum.vml`): the interference-based ML the thermodynamic-computing pivot

View file

@ -165,6 +165,9 @@ fn usage() -> ! {
wai-quantum gbs --squeezing r1,r2,… [--eta E] [--seed S | --unitary U.json] [--json]\n \
wai-quantum gbs … --sample K (exact click patterns)\n \
wai-quantum gbs … --check SAMPLES.txt (observed patterns vs exact marginals)\n\n\
SPIN CHAINS (DMRG ground states, exact referees):\n \
wai-quantum dmrg [--chain ising|heisenberg] [-n N] [--j J] [--jz JZ] [--h H] [--bond D] [--sweeps S]\n \
[--cutoff C] [--exact] [--json]\n\n\
OPTIMISATION (simulated bifurcation, annealing, exact):\n \
wai-quantum qio (--gset FILE | --sk N [--instance S]) [--method dsb|bsb|hdsb|hbsb|sa|exact]\n \
[--steps N] [--trials T] [--dt D] [--c1 C] [--gamma G] [--beta0 B --beta1 B] [--descend]\n \
@ -978,6 +981,55 @@ fn main() {
}
}
// Ground states of spin chains by DMRG, against the exact energy
// where one is known (the Ising chain at any length, any chain by
// exact diagonalisation up to 22 sites).
"dmrg" => {
use wai::quantum_dmrg::*;
let n: usize = num(args, "--sites", "-n", 40);
if n < 2 {
die("-n: at least 2 sites");
}
let j: f64 = num(args, "--j", "", 1.0);
let h: f64 = num(args, "--h", "", if opt(args, "--chain", "").as_deref() == Some("heisenberg") { 0.0 } else { 1.0 });
let kind = opt(args, "--chain", "").unwrap_or_else(|| "ising".into());
let chain = match kind.as_str() {
"ising" => Chain::ising(n, j, h),
"heisenberg" => Chain::heisenberg(n, j, num(args, "--jz", "", j), h),
_ => die("--chain: ising or heisenberg"),
};
let cfg = DmrgConfig { max_bond: num(args, "--bond", "", 64), sweeps: num(args, "--sweeps", "", 5), cutoff: num(args, "--cutoff", "", 1e-12), ..DmrgConfig::default() };
let r = dmrg(&chain, &cfg);
let exact = if kind == "ising" {
Some(ising_exact_energy(n, j, h))
} else if flag(args, "--exact") || n <= 16 {
exact_ground_energy(&chain)
} else {
None
};
let max_bond = r.mps.dims.iter().copied().max().unwrap_or(1);
if flag(args, "--json") {
let ent: Vec<String> = r.entropies.iter().map(|e| e.to_string()).collect();
let sw: Vec<String> = r.sweep_energies.iter().map(|e| e.to_string()).collect();
println!(
"{{\"bond\":{max_bond},\"chain\":\"{kind}\",\"discarded\":{},\"energy\":{},\"entropies\":[{}],\"exact\":{},\"sites\":{n},\"sweeps\":[{}]}}",
r.discarded,
r.energy,
ent.join(","),
exact.map_or("null".into(), |e| e.to_string()),
sw.join(",")
);
} else {
println!("{kind} chain, {n} sites, bond ≤ {}, {} sweeps", cfg.max_bond, cfg.sweeps);
println!("energy {:.12} ({:.12} per site)", r.energy, r.energy / n as f64);
if let Some(e) = exact {
println!("exact {e:.12} (error per site {:.1e})", (r.energy - e) / n as f64);
}
println!("discarded {:.1e} largest bond {max_bond}", r.discarded);
println!("entropy, mid {:.6}", r.entropies[n / 2 - 1]);
}
}
// Quantum-inspired optimisation: Max-Cut (G-set text) or a spin
// glass, by simulated bifurcation, annealing, or exact enumeration.
// Max-Cut reports cuts; a spin glass reports energies.

View file

@ -660,6 +660,9 @@ pub use wai_quantum::quantum_ldpc;
#[cfg(feature = "quantum_qio")]
pub use wai_quantum::quantum_qio;
#[cfg(feature = "quantum_qio")]
pub use wai_quantum::quantum_dmrg;
#[cfg(any(feature = "quantum_tn", feature = "quantum_sv"))]
pub use wai_quantum::gates;
#[cfg(feature = "quantum_qir")]

View file

@ -734,7 +734,7 @@ dependencies = [
[[package]]
name = "wai-quantum"
version = "0.3.33"
version = "0.3.34"
dependencies = [
"blake3",
"ed25519-dalek",