use criterion::{criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use std::hint::black_box;
use sublinear_solver::optimized_solver::OptimizedSolverConfig;
use sublinear_solver::{
closure_indices, solve_on_change_sublinear, verify_sparse_solution, IncrementalSolver, Matrix,
NeumannSolver, OptimizedConjugateGradientSolver, OptimizedSparseMatrix, SolverAlgorithm,
SolverOptions, SparseDelta, SparseMatrix,
};
fn build_test_triplets(n: usize) -> Vec<(usize, usize, f64)> {
let mut t = Vec::with_capacity(n * 5);
for i in 0..n {
t.push((i, i, 5.0));
t.push((i, (i + 1) % n, 1.0));
t.push((i, (i + 2) % n, 1.0));
t.push((i, (i + n - 1) % n, -1.0));
t.push((i, (i + n - 2) % n, -1.0));
}
t
}
fn bench_neumann_series(c: &mut Criterion) {
let mut group = c.benchmark_group("neumann_series");
for &n in &[16usize, 64, 256] {
let triplets = build_test_triplets(n);
let matrix = SparseMatrix::from_triplets(triplets, n, n).unwrap();
let b: Vec<f64> = (0..n).map(|i| (i as f64) + 1.0).collect();
let solver = NeumannSolver::new(64, 1e-10);
let opts = SolverOptions {
max_iterations: 200,
tolerance: 1e-4,
..SolverOptions::default()
};
group.throughput(Throughput::Elements(n as u64));
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |bh, _| {
bh.iter(|| {
let r = solver.solve(black_box(&matrix), black_box(&b), black_box(&opts));
black_box(r.is_ok());
});
});
}
group.finish();
}
fn bench_optimized_cg(c: &mut Criterion) {
let mut group = c.benchmark_group("optimized_cg");
for &n in &[16usize, 64, 256] {
let triplets = build_test_triplets(n);
let mut sym = std::collections::HashMap::<(usize, usize), f64>::new();
for &(i, j, v) in &triplets {
*sym.entry((i, j)).or_insert(0.0) += v / 2.0;
*sym.entry((j, i)).or_insert(0.0) += v / 2.0;
}
let sym_triplets: Vec<_> = sym.into_iter().map(|((i, j), v)| (i, j, v)).collect();
let matrix = OptimizedSparseMatrix::from_triplets(sym_triplets, n, n).unwrap();
let b: Vec<f64> = (0..n).map(|i| (i as f64) + 1.0).collect();
let cfg = OptimizedSolverConfig::default();
group.throughput(Throughput::Elements(n as u64));
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |bh, _| {
bh.iter(|| {
let mut solver = OptimizedConjugateGradientSolver::new(cfg.clone());
let r = solver.solve(black_box(&matrix), black_box(&b)).unwrap();
black_box(r.iterations);
});
});
}
group.finish();
}
fn bench_delta_solve(c: &mut Criterion) {
let mut group = c.benchmark_group("delta_solve");
let closure_depth = 4usize;
let max_terms = 32usize;
let tolerance = 1e-8;
for &n in &[64usize, 256, 1024] {
let triplets = build_test_triplets(n);
let matrix = SparseMatrix::from_triplets(triplets, n, n).unwrap();
let b_prev: Vec<f64> = (0..n).map(|i| (i as f64) + 1.0).collect();
let warmup_solver = NeumannSolver::new(128, 1e-12);
let opts_tight = SolverOptions {
max_iterations: 500,
tolerance: 1e-10,
..SolverOptions::default()
};
let prev_solution = warmup_solver
.solve(&matrix, &b_prev, &opts_tight)
.expect("warmup solve failed")
.solution;
let delta_idx = n / 3;
let delta = SparseDelta::new(alloc_vec(delta_idx), alloc_vec_val(0.25)).unwrap();
let mut b_new = b_prev.clone();
delta.apply_to(&mut b_new).unwrap();
let solver = NeumannSolver::new(64, 1e-10);
let opts = SolverOptions {
max_iterations: 200,
tolerance: 1e-6,
..SolverOptions::default()
};
group.throughput(Throughput::Elements(n as u64));
group.bench_with_input(BenchmarkId::new("cold_full", n), &n, |bh, _| {
bh.iter(|| {
let r = solver.solve(black_box(&matrix), black_box(&b_new), black_box(&opts));
black_box(r.is_ok());
});
});
group.bench_with_input(BenchmarkId::new("warm_full", n), &n, |bh, _| {
bh.iter(|| {
let r = solver.solve_on_change(
black_box(&matrix),
black_box(&prev_solution),
black_box(&delta),
black_box(&opts),
);
black_box(r.is_ok());
});
});
group.bench_with_input(BenchmarkId::new("sparse_closure", n), &n, |bh, _| {
bh.iter(|| {
let r = solve_on_change_sublinear(
black_box(&matrix),
black_box(&prev_solution),
black_box(&b_new),
black_box(&delta),
black_box(closure_depth),
black_box(max_terms),
black_box(tolerance),
);
black_box(r.is_ok());
});
});
}
group.finish();
}
fn alloc_vec(i: usize) -> Vec<usize> {
vec![i]
}
fn alloc_vec_val(v: f64) -> Vec<f64> {
vec![v]
}
fn bench_witness_audit(c: &mut Criterion) {
let mut group = c.benchmark_group("witness_audit");
for &n in &[64usize, 256, 1024] {
let triplets = build_test_triplets(n);
let matrix = SparseMatrix::from_triplets(triplets, n, n).unwrap();
let b_prev: Vec<f64> = (0..n).map(|i| i as f64 + 1.0).collect();
let warmup = NeumannSolver::new(128, 1e-12);
let opts = SolverOptions {
max_iterations: 500,
tolerance: 1e-10,
..SolverOptions::default()
};
let prev_solution = warmup.solve(&matrix, &b_prev, &opts).unwrap().solution;
let delta = SparseDelta::new(vec![n / 3], vec![0.25]).unwrap();
let mut b_new = b_prev.clone();
delta.apply_to(&mut b_new).unwrap();
let entries = solve_on_change_sublinear(
&matrix,
&prev_solution,
&b_new,
&delta,
4,
32,
1e-8,
)
.unwrap();
group.throughput(Throughput::Elements(n as u64));
group.bench_with_input(BenchmarkId::new("full_residual", n), &n, |bh, _| {
bh.iter(|| {
let mut x_new = prev_solution.clone();
for &(i, v) in &entries {
x_new[i] = v;
}
let mut ax = vec![0.0f64; n];
let _ = matrix.multiply_vector(&x_new, &mut ax);
let mut max_r = 0.0_f64;
for i in 0..n {
let r = (b_new[i] - ax[i]).abs();
if r > max_r {
max_r = r;
}
}
black_box(max_r);
});
});
group.bench_with_input(BenchmarkId::new("closure_audit", n), &n, |bh, _| {
bh.iter(|| {
let report = verify_sparse_solution(
black_box(&matrix),
black_box(&prev_solution),
black_box(&b_new),
black_box(&entries),
1e-4,
)
.unwrap();
black_box(report.ok);
});
});
}
group.finish();
}
fn bench_closure_only(c: &mut Criterion) {
let mut group = c.benchmark_group("closure_only");
let depth = 4usize;
for &n in &[64usize, 256, 1024, 4096] {
let triplets = build_test_triplets(n);
let matrix = SparseMatrix::from_triplets(triplets, n, n).unwrap();
let seeds = vec![n / 3];
group.throughput(Throughput::Elements(n as u64));
group.bench_with_input(BenchmarkId::from_parameter(n), &n, |bh, _| {
bh.iter(|| {
let c = closure_indices(black_box(&matrix), black_box(&seeds), black_box(depth));
black_box(c.len());
});
});
}
group.finish();
}
criterion_group!(
benches,
bench_neumann_series,
bench_optimized_cg,
bench_delta_solve,
bench_witness_audit,
bench_closure_only
);
criterion_main!(benches);