//! Quantum and Algebraic Topology Benchmarks for Prime-Radiant //! //! Benchmarks for quantum-topological operations including: //! - Persistent homology computation at various dimensions //! - Topological invariant computation (Betti numbers, Euler characteristic) //! - Quantum state operations (density matrices, fidelity) //! - Simplicial complex construction and manipulation //! //! Target metrics: //! - Persistent homology (1K points): < 100ms //! - Betti numbers (dim 2): < 10ms //! - Quantum fidelity: < 1ms per pair use criterion::{ black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput, }; use std::collections::{BinaryHeap, HashMap, HashSet}; use std::cmp::Ordering; // ============================================================================ // SIMPLICIAL COMPLEX TYPES // ============================================================================ /// A simplex is an ordered set of vertices #[derive(Clone, Debug, PartialEq, Eq, Hash)] struct Simplex { vertices: Vec, } impl Simplex { fn new(mut vertices: Vec) -> Self { vertices.sort_unstable(); Self { vertices } } fn dimension(&self) -> usize { if self.vertices.is_empty() { 0 } else { self.vertices.len() - 1 } } fn faces(&self) -> Vec { let mut faces = Vec::new(); for i in 0..self.vertices.len() { let mut face_vertices = self.vertices.clone(); face_vertices.remove(i); if !face_vertices.is_empty() { faces.push(Simplex::new(face_vertices)); } } faces } } /// Filtered simplicial complex for persistent homology struct FilteredComplex { simplices: Vec<(f64, Simplex)>, // (filtration value, simplex) } impl FilteredComplex { fn new() -> Self { Self { simplices: Vec::new() } } fn add(&mut self, filtration: f64, simplex: Simplex) { self.simplices.push((filtration, simplex)); } fn sort_by_filtration(&mut self) { self.simplices.sort_by(|a, b| { a.0.partial_cmp(&b.0).unwrap_or(Ordering::Equal) .then_with(|| a.1.dimension().cmp(&b.1.dimension())) }); } } // ============================================================================ // PERSISTENT HOMOLOGY // ============================================================================ /// Birth-death pair representing a topological feature #[derive(Clone, Debug)] struct PersistencePair { dimension: usize, birth: f64, death: f64, } impl PersistencePair { fn persistence(&self) -> f64 { self.death - self.birth } } /// Union-Find data structure for 0-dimensional homology struct UnionFind { parent: Vec, rank: Vec, birth: Vec, } impl UnionFind { fn new(n: usize) -> Self { Self { parent: (0..n).collect(), rank: vec![0; n], birth: vec![f64::INFINITY; n], } } fn find(&mut self, x: usize) -> usize { if self.parent[x] != x { self.parent[x] = self.find(self.parent[x]); } self.parent[x] } fn union(&mut self, x: usize, y: usize) -> Option<(usize, usize)> { let px = self.find(x); let py = self.find(y); if px == py { return None; } // Younger component dies (larger birth time) let (survivor, dying) = if self.birth[px] <= self.birth[py] { (px, py) } else { (py, px) }; if self.rank[px] < self.rank[py] { self.parent[px] = py; } else if self.rank[px] > self.rank[py] { self.parent[py] = px; } else { self.parent[py] = px; self.rank[px] += 1; } self.parent[dying] = survivor; Some((dying, survivor)) } fn set_birth(&mut self, x: usize, birth: f64) { self.birth[x] = birth; } } /// Compute persistent homology using standard algorithm fn compute_persistent_homology(complex: &FilteredComplex, max_dim: usize) -> Vec { let mut pairs = Vec::new(); let num_vertices = complex.simplices.iter() .filter(|(_, s)| s.dimension() == 0) .count(); // Union-find for H_0 let mut uf = UnionFind::new(num_vertices); // Track active simplices for higher dimensions let mut simplex_index: HashMap, usize> = HashMap::new(); let mut boundary_matrix: Vec> = Vec::new(); let mut pivot_to_col: HashMap = HashMap::new(); for (idx, (filtration, simplex)) in complex.simplices.iter().enumerate() { let dim = simplex.dimension(); if dim == 0 { // Vertex: creates a new H_0 class let v = simplex.vertices[0]; uf.set_birth(v, *filtration); simplex_index.insert(simplex.vertices.clone(), idx); boundary_matrix.push(HashSet::new()); } else if dim == 1 { // Edge: may kill H_0 class let u = simplex.vertices[0]; let v = simplex.vertices[1]; if let Some((dying, _survivor)) = uf.union(u, v) { let birth = uf.birth[dying]; if *filtration > birth { pairs.push(PersistencePair { dimension: 0, birth, death: *filtration, }); } } // Add to boundary matrix for H_1 let mut boundary = HashSet::new(); for &vertex in &simplex.vertices { if let Some(&face_idx) = simplex_index.get(&vec![vertex]) { boundary.insert(face_idx); } } simplex_index.insert(simplex.vertices.clone(), idx); boundary_matrix.push(boundary); } else if dim <= max_dim { // Higher dimensional simplex let faces = simplex.faces(); let mut boundary: HashSet = faces.iter() .filter_map(|f| simplex_index.get(&f.vertices).copied()) .collect(); // Reduce boundary while !boundary.is_empty() { let pivot = *boundary.iter().max().unwrap(); if let Some(&other_col) = pivot_to_col.get(&pivot) { // XOR with the column that has this pivot let other_boundary = &boundary_matrix[other_col]; let symmetric_diff: HashSet = boundary .symmetric_difference(other_boundary) .copied() .collect(); boundary = symmetric_diff; } else { // This column has a new pivot pivot_to_col.insert(pivot, idx); break; } } if boundary.is_empty() { // This simplex creates a new cycle (potential H_{dim-1} class) // For simplicity, we just record it was created } else { // This simplex kills a cycle let pivot = *boundary.iter().max().unwrap(); let birth_filtration = complex.simplices[pivot].0; pairs.push(PersistencePair { dimension: dim - 1, birth: birth_filtration, death: *filtration, }); } simplex_index.insert(simplex.vertices.clone(), idx); boundary_matrix.push(boundary); } } // Add infinite persistence pairs for surviving components for i in 0..num_vertices { if uf.find(i) == i && uf.birth[i] < f64::INFINITY { pairs.push(PersistencePair { dimension: 0, birth: uf.birth[i], death: f64::INFINITY, }); } } pairs } /// Persistence diagram statistics struct PersistenceStats { total_features: usize, max_persistence: f64, mean_persistence: f64, betti_at_threshold: Vec, } fn compute_persistence_stats(pairs: &[PersistencePair], threshold: f64, max_dim: usize) -> PersistenceStats { let finite_pairs: Vec<_> = pairs.iter() .filter(|p| p.death.is_finite()) .collect(); let persistences: Vec = finite_pairs.iter() .map(|p| p.persistence()) .collect(); let max_persistence = persistences.iter().cloned().fold(0.0f64, f64::max); let mean_persistence = if persistences.is_empty() { 0.0 } else { persistences.iter().sum::() / persistences.len() as f64 }; // Betti numbers at threshold let mut betti = vec![0; max_dim + 1]; for pair in pairs { if pair.birth <= threshold && (pair.death.is_infinite() || pair.death > threshold) { if pair.dimension <= max_dim { betti[pair.dimension] += 1; } } } PersistenceStats { total_features: pairs.len(), max_persistence, mean_persistence, betti_at_threshold: betti, } } // ============================================================================ // QUANTUM STATE OPERATIONS // ============================================================================ /// Complex number (simplified for benchmarking) #[derive(Clone, Copy, Debug)] struct Complex { re: f64, im: f64, } impl Complex { fn new(re: f64, im: f64) -> Self { Self { re, im } } fn norm_squared(&self) -> f64 { self.re * self.re + self.im * self.im } fn conjugate(&self) -> Self { Self { re: self.re, im: -self.im } } fn mul(&self, other: &Self) -> Self { Self { re: self.re * other.re - self.im * other.im, im: self.re * other.im + self.im * other.re, } } fn add(&self, other: &Self) -> Self { Self { re: self.re + other.re, im: self.im + other.im, } } fn scale(&self, s: f64) -> Self { Self { re: self.re * s, im: self.im * s, } } } /// Density matrix for mixed quantum states struct DensityMatrix { dimension: usize, data: Vec>, } impl DensityMatrix { fn new(dimension: usize) -> Self { Self { dimension, data: vec![vec![Complex::new(0.0, 0.0); dimension]; dimension], } } fn from_pure_state(state: &[Complex]) -> Self { let n = state.len(); let mut dm = DensityMatrix::new(n); for i in 0..n { for j in 0..n { dm.data[i][j] = state[i].mul(&state[j].conjugate()); } } dm } fn trace(&self) -> Complex { let mut sum = Complex::new(0.0, 0.0); for i in 0..self.dimension { sum = sum.add(&self.data[i][i]); } sum } fn multiply(&self, other: &DensityMatrix) -> DensityMatrix { let n = self.dimension; let mut result = DensityMatrix::new(n); for i in 0..n { for j in 0..n { let mut sum = Complex::new(0.0, 0.0); for k in 0..n { sum = sum.add(&self.data[i][k].mul(&other.data[k][j])); } result.data[i][j] = sum; } } result } /// Compute sqrt(rho) approximately using Newton's method fn sqrt_approx(&self, iterations: usize) -> DensityMatrix { let n = self.dimension; // Start with identity matrix let mut y = DensityMatrix::new(n); for i in 0..n { y.data[i][i] = Complex::new(1.0, 0.0); } // Denman-Beavers iteration: Y_{k+1} = (Y_k + Y_k^{-1} * A) / 2 // Simplified: just use Newton iteration Y = (Y + A/Y) / 2 for _ in 0..iterations { let y_inv = self.clone(); // Simplified: use original matrix let sum = y.add(&y_inv); y = sum.scale_all(0.5); } y } fn add(&self, other: &DensityMatrix) -> DensityMatrix { let n = self.dimension; let mut result = DensityMatrix::new(n); for i in 0..n { for j in 0..n { result.data[i][j] = self.data[i][j].add(&other.data[i][j]); } } result } fn scale_all(&self, s: f64) -> DensityMatrix { let n = self.dimension; let mut result = DensityMatrix::new(n); for i in 0..n { for j in 0..n { result.data[i][j] = self.data[i][j].scale(s); } } result } } impl Clone for DensityMatrix { fn clone(&self) -> Self { Self { dimension: self.dimension, data: self.data.clone(), } } } /// Quantum fidelity between two density matrices /// F(rho, sigma) = (Tr sqrt(sqrt(rho) sigma sqrt(rho)))^2 fn quantum_fidelity(rho: &DensityMatrix, sigma: &DensityMatrix) -> f64 { // Simplified computation for benchmarking // Full computation would require eigendecomposition let sqrt_rho = rho.sqrt_approx(5); let inner = sqrt_rho.multiply(sigma).multiply(&sqrt_rho); let sqrt_inner = inner.sqrt_approx(5); let trace = sqrt_inner.trace(); trace.re * trace.re + trace.im * trace.im } /// Trace distance between density matrices /// D(rho, sigma) = (1/2) Tr |rho - sigma| fn trace_distance(rho: &DensityMatrix, sigma: &DensityMatrix) -> f64 { let n = rho.dimension; let mut sum = 0.0; // Simplified: use Frobenius norm as approximation for i in 0..n { for j in 0..n { let diff = Complex { re: rho.data[i][j].re - sigma.data[i][j].re, im: rho.data[i][j].im - sigma.data[i][j].im, }; sum += diff.norm_squared(); } } 0.5 * sum.sqrt() } /// Von Neumann entropy: S(rho) = -Tr(rho log rho) fn von_neumann_entropy(rho: &DensityMatrix) -> f64 { // Simplified: compute diagonal entropy approximation let mut entropy = 0.0; for i in 0..rho.dimension { let p = rho.data[i][i].re; if p > 1e-10 { entropy -= p * p.ln(); } } entropy } // ============================================================================ // TOPOLOGICAL INVARIANTS // ============================================================================ /// Compute Euler characteristic: chi = V - E + F - ... fn euler_characteristic(complex: &FilteredComplex) -> i64 { let mut chi = 0i64; for (_, simplex) in &complex.simplices { let dim = simplex.dimension(); if dim % 2 == 0 { chi += 1; } else { chi -= 1; } } chi } /// Betti numbers via boundary matrix rank fn betti_numbers(complex: &FilteredComplex, max_dim: usize) -> Vec { // Count simplices by dimension let mut counts = vec![0usize; max_dim + 2]; for (_, simplex) in &complex.simplices { let dim = simplex.dimension(); if dim <= max_dim + 1 { counts[dim] += 1; } } // Simplified Betti number estimation // beta_k = dim(ker d_k) - dim(im d_{k+1}) // Approximation: beta_k ~ C_k - C_{k+1} for highly connected complexes let mut betti = vec![0usize; max_dim + 1]; for k in 0..=max_dim { let c_k = counts[k]; let c_k1 = if k + 1 <= max_dim + 1 { counts[k + 1] } else { 0 }; // Very rough approximation betti[k] = if c_k > c_k1 { c_k - c_k1 } else { 1 }; } // Ensure beta_0 >= 1 (at least one connected component) if betti[0] == 0 { betti[0] = 1; } betti } // ============================================================================ // DATA GENERATORS // ============================================================================ fn generate_rips_complex(points: &[(f64, f64)], max_radius: f64, max_dim: usize) -> FilteredComplex { let n = points.len(); let mut complex = FilteredComplex::new(); // Add vertices (0-simplices) for i in 0..n { complex.add(0.0, Simplex::new(vec![i])); } // Compute pairwise distances let mut edges: Vec<(f64, usize, usize)> = Vec::new(); for i in 0..n { for j in (i + 1)..n { let dist = ((points[i].0 - points[j].0).powi(2) + (points[i].1 - points[j].1).powi(2)) .sqrt(); if dist <= max_radius { edges.push((dist, i, j)); } } } // Add edges (1-simplices) for (dist, i, j) in &edges { complex.add(*dist, Simplex::new(vec![*i, *j])); } // Add triangles (2-simplices) if max_dim >= 2 if max_dim >= 2 { // Build adjacency let mut adj: HashMap> = HashMap::new(); let mut edge_dist: HashMap<(usize, usize), f64> = HashMap::new(); for (dist, i, j) in &edges { adj.entry(*i).or_default().insert(*j); adj.entry(*j).or_default().insert(*i); edge_dist.insert(((*i).min(*j), (*i).max(*j)), *dist); } for i in 0..n { if let Some(neighbors_i) = adj.get(&i) { for &j in neighbors_i { if j > i { if let Some(neighbors_j) = adj.get(&j) { for &k in neighbors_j { if k > j && neighbors_i.contains(&k) { // Found triangle (i, j, k) let d_ij = edge_dist.get(&(i, j)).unwrap_or(&0.0); let d_jk = edge_dist.get(&(j, k)).unwrap_or(&0.0); let d_ik = edge_dist.get(&(i, k)).unwrap_or(&0.0); let max_dist = d_ij.max(*d_jk).max(*d_ik); complex.add(max_dist, Simplex::new(vec![i, j, k])); } } } } } } } } complex.sort_by_filtration(); complex } fn generate_random_points(num_points: usize, seed: u64) -> Vec<(f64, f64)> { let mut rng_state = seed; let mut points = Vec::with_capacity(num_points); for _ in 0..num_points { rng_state = rng_state.wrapping_mul(6364136223846793005).wrapping_add(1); let x = (rng_state >> 33) as f64 / (u32::MAX as f64); rng_state = rng_state.wrapping_mul(6364136223846793005).wrapping_add(1); let y = (rng_state >> 33) as f64 / (u32::MAX as f64); points.push((x, y)); } points } fn generate_random_quantum_state(dimension: usize, seed: u64) -> Vec { let mut rng_state = seed; let mut state = Vec::with_capacity(dimension); let mut norm_sq = 0.0; for _ in 0..dimension { rng_state = rng_state.wrapping_mul(6364136223846793005).wrapping_add(1); let re = ((rng_state >> 33) as f64 / (u32::MAX as f64)) * 2.0 - 1.0; rng_state = rng_state.wrapping_mul(6364136223846793005).wrapping_add(1); let im = ((rng_state >> 33) as f64 / (u32::MAX as f64)) * 2.0 - 1.0; let c = Complex::new(re, im); norm_sq += c.norm_squared(); state.push(c); } // Normalize let norm = norm_sq.sqrt(); for c in &mut state { *c = c.scale(1.0 / norm); } state } // ============================================================================ // BENCHMARKS // ============================================================================ fn bench_persistent_homology(c: &mut Criterion) { let mut group = c.benchmark_group("quantum/persistent_homology"); group.sample_size(20); for &num_points in &[100, 250, 500, 1000] { let points = generate_random_points(num_points, 42); let radius = 0.2; let complex = generate_rips_complex(&points, radius, 2); group.throughput(Throughput::Elements(num_points as u64)); group.bench_with_input( BenchmarkId::new("dim2", num_points), &complex, |b, complex| { b.iter(|| black_box(compute_persistent_homology(black_box(complex), 2))) }, ); } group.finish(); } fn bench_persistence_stats(c: &mut Criterion) { let mut group = c.benchmark_group("quantum/persistence_stats"); group.sample_size(50); for &num_points in &[100, 500, 1000] { let points = generate_random_points(num_points, 42); let complex = generate_rips_complex(&points, 0.2, 2); let pairs = compute_persistent_homology(&complex, 2); group.throughput(Throughput::Elements(pairs.len() as u64)); group.bench_with_input( BenchmarkId::new("compute", num_points), &pairs, |b, pairs| { b.iter(|| black_box(compute_persistence_stats(black_box(pairs), 0.1, 2))) }, ); } group.finish(); } fn bench_topological_invariants(c: &mut Criterion) { let mut group = c.benchmark_group("quantum/invariants"); group.sample_size(50); for &num_points in &[100, 500, 1000] { let points = generate_random_points(num_points, 42); let complex = generate_rips_complex(&points, 0.2, 2); group.throughput(Throughput::Elements(complex.simplices.len() as u64)); group.bench_with_input( BenchmarkId::new("euler", num_points), &complex, |b, complex| { b.iter(|| black_box(euler_characteristic(black_box(complex)))) }, ); group.bench_with_input( BenchmarkId::new("betti", num_points), &complex, |b, complex| { b.iter(|| black_box(betti_numbers(black_box(complex), 2))) }, ); } group.finish(); } fn bench_rips_construction(c: &mut Criterion) { let mut group = c.benchmark_group("quantum/rips_construction"); group.sample_size(20); for &num_points in &[100, 250, 500, 1000] { let points = generate_random_points(num_points, 42); group.throughput(Throughput::Elements(num_points as u64)); group.bench_with_input( BenchmarkId::new("dim2", num_points), &points, |b, points| { b.iter(|| black_box(generate_rips_complex(black_box(points), 0.15, 2))) }, ); group.bench_with_input( BenchmarkId::new("dim1", num_points), &points, |b, points| { b.iter(|| black_box(generate_rips_complex(black_box(points), 0.15, 1))) }, ); } group.finish(); } fn bench_quantum_fidelity(c: &mut Criterion) { let mut group = c.benchmark_group("quantum/fidelity"); group.sample_size(50); for &dim in &[4, 8, 16, 32] { let state1 = generate_random_quantum_state(dim, 42); let state2 = generate_random_quantum_state(dim, 43); let rho = DensityMatrix::from_pure_state(&state1); let sigma = DensityMatrix::from_pure_state(&state2); group.throughput(Throughput::Elements((dim * dim) as u64)); group.bench_with_input( BenchmarkId::new("pure_states", dim), &(&rho, &sigma), |b, (rho, sigma)| { b.iter(|| black_box(quantum_fidelity(black_box(rho), black_box(sigma)))) }, ); group.bench_with_input( BenchmarkId::new("trace_distance", dim), &(&rho, &sigma), |b, (rho, sigma)| { b.iter(|| black_box(trace_distance(black_box(rho), black_box(sigma)))) }, ); } group.finish(); } fn bench_density_matrix_operations(c: &mut Criterion) { let mut group = c.benchmark_group("quantum/density_matrix"); group.sample_size(50); for &dim in &[4, 8, 16, 32, 64] { let state = generate_random_quantum_state(dim, 42); let rho = DensityMatrix::from_pure_state(&state); group.throughput(Throughput::Elements((dim * dim) as u64)); group.bench_with_input( BenchmarkId::new("from_pure_state", dim), &state, |b, state| { b.iter(|| black_box(DensityMatrix::from_pure_state(black_box(state)))) }, ); group.bench_with_input( BenchmarkId::new("multiply", dim), &rho, |b, rho| { b.iter(|| black_box(rho.multiply(black_box(rho)))) }, ); group.bench_with_input( BenchmarkId::new("trace", dim), &rho, |b, rho| { b.iter(|| black_box(rho.trace())) }, ); group.bench_with_input( BenchmarkId::new("von_neumann_entropy", dim), &rho, |b, rho| { b.iter(|| black_box(von_neumann_entropy(black_box(rho)))) }, ); } group.finish(); } fn bench_simplex_operations(c: &mut Criterion) { let mut group = c.benchmark_group("quantum/simplex"); group.sample_size(100); for &dim in &[3, 5, 7, 10] { let vertices: Vec = (0..dim).collect(); let simplex = Simplex::new(vertices.clone()); group.throughput(Throughput::Elements(dim as u64)); group.bench_with_input( BenchmarkId::new("create", dim), &vertices, |b, vertices| { b.iter(|| black_box(Simplex::new(black_box(vertices.clone())))) }, ); group.bench_with_input( BenchmarkId::new("faces", dim), &simplex, |b, simplex| { b.iter(|| black_box(simplex.faces())) }, ); } group.finish(); } criterion_group!( benches, bench_persistent_homology, bench_persistence_stats, bench_topological_invariants, bench_rips_construction, bench_quantum_fidelity, bench_density_matrix_operations, bench_simplex_operations, ); criterion_main!(benches);