Files
wifi-densepose/crates/ruvector-mincut/benches/paper_algorithms_bench.rs
ruv d803bfe2b1 Squashed 'vendor/ruvector/' content from commit b64c2172
git-subtree-dir: vendor/ruvector
git-subtree-split: b64c21726f2bb37286d9ee36a7869fef60cc6900
2026-02-28 14:39:40 -05:00

306 lines
9.7 KiB
Rust

//! Benchmarks for 2025 paper algorithm implementations
//!
//! Tests performance of:
//! - PolylogConnectivity (arXiv:2510.08297)
//! - ApproxMinCut (SODA 2025, arXiv:2412.15069)
//! - CacheOptBFS
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use ruvector_mincut::connectivity::cache_opt::{BatchProcessor, CacheOptAdjacency, CacheOptBFS};
use ruvector_mincut::{
ApproxMinCut, ApproxMinCutConfig, DynamicConnectivity, PolylogConnectivity, PolylogStats,
};
/// Generate a random graph with n vertices and m edges
fn generate_graph(n: usize, m: usize, seed: u64) -> Vec<(u64, u64)> {
let mut edges = Vec::with_capacity(m);
let mut rng = seed;
for _ in 0..m {
// Simple LCG for deterministic "random" numbers
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
let u = (rng % n as u64) as u64;
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
let v = (rng % n as u64) as u64;
if u != v {
edges.push((u, v));
}
}
edges
}
/// Generate weighted graph edges
fn generate_weighted_graph(n: usize, m: usize, seed: u64) -> Vec<(u64, u64, f64)> {
let mut edges = Vec::with_capacity(m);
let mut rng = seed;
for _ in 0..m {
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
let u = (rng % n as u64) as u64;
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
let v = (rng % n as u64) as u64;
rng = rng.wrapping_mul(6364136223846793005).wrapping_add(1);
let w = ((rng % 100) as f64 + 1.0) / 10.0;
if u != v {
edges.push((u, v, w));
}
}
edges
}
// ============================================================================
// PolylogConnectivity Benchmarks
// ============================================================================
fn bench_polylog_insert(c: &mut Criterion) {
let mut group = c.benchmark_group("polylog_insert");
for size in [100, 500, 1000, 5000].iter() {
let edges = generate_graph(*size, size * 2, 42);
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter(|| {
let mut conn = PolylogConnectivity::new();
for &(u, v) in &edges {
conn.insert_edge(u, v);
}
black_box(conn.component_count())
});
});
}
group.finish();
}
fn bench_polylog_delete(c: &mut Criterion) {
let mut group = c.benchmark_group("polylog_delete");
for size in [100, 500, 1000].iter() {
let edges = generate_graph(*size, size * 2, 42);
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter_batched(
|| {
let mut conn = PolylogConnectivity::new();
for &(u, v) in &edges {
conn.insert_edge(u, v);
}
conn
},
|mut conn| {
// Delete half the edges
for i in 0..edges.len() / 2 {
let (u, v) = edges[i];
conn.delete_edge(u, v);
}
black_box(conn.component_count())
},
criterion::BatchSize::SmallInput,
);
});
}
group.finish();
}
fn bench_polylog_query(c: &mut Criterion) {
let mut group = c.benchmark_group("polylog_query");
for size in [100, 500, 1000, 5000].iter() {
let edges = generate_graph(*size, size * 2, 42);
let mut conn = PolylogConnectivity::new();
for &(u, v) in &edges {
conn.insert_edge(u, v);
}
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, &size| {
let queries: Vec<(u64, u64)> = (0..100)
.map(|i| {
(
(i * 7) as u64 % size as u64,
(i * 13 + 1) as u64 % size as u64,
)
})
.collect();
b.iter(|| {
let mut count = 0;
for &(u, v) in &queries {
if conn.connected(u, v) {
count += 1;
}
}
black_box(count)
});
});
}
group.finish();
}
// ============================================================================
// ApproxMinCut Benchmarks
// ============================================================================
fn bench_approx_insert(c: &mut Criterion) {
let mut group = c.benchmark_group("approx_mincut_insert");
for size in [100, 500, 1000].iter() {
let edges = generate_weighted_graph(*size, size * 2, 42);
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter(|| {
let mut approx = ApproxMinCut::with_epsilon(0.1);
for &(u, v, w) in &edges {
approx.insert_edge(u, v, w);
}
black_box(approx.edge_count())
});
});
}
group.finish();
}
fn bench_approx_query(c: &mut Criterion) {
let mut group = c.benchmark_group("approx_mincut_query");
for size in [50, 100, 200, 500].iter() {
let edges = generate_weighted_graph(*size, size * 2, 42);
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter_batched(
|| {
let mut approx = ApproxMinCut::with_epsilon(0.1);
for &(u, v, w) in &edges {
approx.insert_edge(u, v, w);
}
approx
},
|mut approx| black_box(approx.min_cut_value()),
criterion::BatchSize::SmallInput,
);
});
}
group.finish();
}
fn bench_approx_epsilon_comparison(c: &mut Criterion) {
let mut group = c.benchmark_group("approx_epsilon");
let size = 200;
let edges = generate_weighted_graph(size, size * 2, 42);
for epsilon in [0.05, 0.1, 0.2, 0.5].iter() {
group.bench_with_input(BenchmarkId::from_parameter(epsilon), epsilon, |b, &eps| {
b.iter_batched(
|| {
let mut approx = ApproxMinCut::with_epsilon(eps);
for &(u, v, w) in &edges {
approx.insert_edge(u, v, w);
}
approx
},
|mut approx| black_box(approx.min_cut_value()),
criterion::BatchSize::SmallInput,
);
});
}
group.finish();
}
// ============================================================================
// CacheOptBFS Benchmarks
// ============================================================================
fn bench_cache_opt_bfs(c: &mut Criterion) {
let mut group = c.benchmark_group("cache_opt_bfs");
for size in [100, 500, 1000, 5000].iter() {
let edges: Vec<(u64, u64, f64)> = generate_weighted_graph(*size, size * 3, 42);
let max_v = edges
.iter()
.map(|(u, v, _)| (*u).max(*v))
.max()
.unwrap_or(0);
let adj = CacheOptAdjacency::from_edges(&edges, max_v);
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
b.iter(|| {
let bfs = CacheOptBFS::new(&adj, 0);
black_box(bfs.run().len())
});
});
}
group.finish();
}
fn bench_batch_processor(c: &mut Criterion) {
let mut group = c.benchmark_group("batch_processor");
for size in [100, 500, 1000, 5000].iter() {
let edges: Vec<(u64, u64, f64)> = generate_weighted_graph(*size, size * 3, 42);
let max_v = edges
.iter()
.map(|(u, v, _)| (*u).max(*v))
.max()
.unwrap_or(0);
let adj = CacheOptAdjacency::from_edges(&edges, max_v);
let vertices: Vec<u64> = (0..=max_v).collect();
group.bench_with_input(BenchmarkId::from_parameter(size), size, |b, _| {
let processor = BatchProcessor::new();
b.iter(|| {
let degrees = processor.compute_degrees(&adj, &vertices);
black_box(degrees.len())
});
});
}
group.finish();
}
// ============================================================================
// Comparison: PolylogConnectivity vs DynamicConnectivity
// ============================================================================
fn bench_connectivity_comparison(c: &mut Criterion) {
let mut group = c.benchmark_group("connectivity_comparison");
let size = 500;
let edges = generate_graph(size, size * 2, 42);
// Polylog insert
group.bench_function("polylog_build_500", |b| {
b.iter(|| {
let mut conn = PolylogConnectivity::new();
for &(u, v) in &edges {
conn.insert_edge(u, v);
}
black_box(conn.component_count())
});
});
// Standard DynamicConnectivity insert
group.bench_function("dynamic_build_500", |b| {
b.iter(|| {
let mut conn = DynamicConnectivity::new();
for &(u, v) in &edges {
conn.insert_edge(u, v);
}
black_box(conn.component_count())
});
});
group.finish();
}
criterion_group!(
benches,
bench_polylog_insert,
bench_polylog_delete,
bench_polylog_query,
bench_approx_insert,
bench_approx_query,
bench_approx_epsilon_comparison,
bench_cache_opt_bfs,
bench_batch_processor,
bench_connectivity_comparison,
);
criterion_main!(benches);