Merge commit 'd803bfe2b1fe7f5e219e50ac20d6801a0a58ac75' as 'vendor/ruvector'

This commit is contained in:
ruv
2026-02-28 14:39:40 -05:00
7854 changed files with 3522914 additions and 0 deletions

View File

@@ -0,0 +1,132 @@
//! Benchmarks for information geometry operations
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use rand::prelude::*;
use rand_distr::StandardNormal;
use ruvector_math::information_geometry::{FisherInformation, KFACApproximation, NaturalGradient};
fn generate_gradients(n: usize, dim: usize, seed: u64) -> Vec<Vec<f64>> {
let mut rng = StdRng::seed_from_u64(seed);
(0..n)
.map(|_| (0..dim).map(|_| rng.sample(StandardNormal)).collect())
.collect()
}
fn bench_fisher_information(c: &mut Criterion) {
let mut group = c.benchmark_group("fisher_information");
for dim in [32, 64, 128, 256] {
let samples = 100;
let gradients = generate_gradients(samples, dim, 42);
group.throughput(Throughput::Elements((samples * dim) as u64));
// Diagonal FIM (fast)
let fisher = FisherInformation::new();
group.bench_with_input(
BenchmarkId::new("diagonal_fim", dim),
&gradients,
|b, grads| {
b.iter(|| fisher.diagonal_fim(black_box(grads)));
},
);
// Full FIM (slower but more accurate)
if dim <= 128 {
group.bench_with_input(
BenchmarkId::new("empirical_fim", dim),
&gradients,
|b, grads| {
b.iter(|| fisher.empirical_fim(black_box(grads)));
},
);
}
}
group.finish();
}
fn bench_natural_gradient(c: &mut Criterion) {
let mut group = c.benchmark_group("natural_gradient");
for dim in [32, 64, 128] {
let samples = 50;
let gradients = generate_gradients(samples, dim, 42);
let gradient = gradients[0].clone();
group.throughput(Throughput::Elements(dim as u64));
// Diagonal natural gradient (fast)
let mut ng = NaturalGradient::new(0.01).with_diagonal(true);
group.bench_with_input(
BenchmarkId::new("diagonal_step", dim),
&(&gradient, &gradients),
|b, (g, gs)| {
b.iter(|| {
ng.reset();
ng.step(black_box(g), Some(black_box(gs)))
});
},
);
}
group.finish();
}
fn bench_kfac(c: &mut Criterion) {
let mut group = c.benchmark_group("kfac");
for (input_dim, output_dim) in [(32, 16), (64, 32), (128, 64)] {
let batch_size = 32;
let mut rng = StdRng::seed_from_u64(42);
let activations: Vec<Vec<f64>> = (0..batch_size)
.map(|_| (0..input_dim).map(|_| rng.sample(StandardNormal)).collect())
.collect();
let gradients: Vec<Vec<f64>> = (0..batch_size)
.map(|_| {
(0..output_dim)
.map(|_| rng.sample(StandardNormal))
.collect()
})
.collect();
let weight_grad: Vec<Vec<f64>> = (0..output_dim)
.map(|_| (0..input_dim).map(|_| rng.sample(StandardNormal)).collect())
.collect();
group.throughput(Throughput::Elements((input_dim * output_dim) as u64));
// K-FAC update
let mut kfac =
ruvector_math::information_geometry::KFACApproximation::new(&[(input_dim, output_dim)]);
group.bench_function(
BenchmarkId::new("kfac_update", format!("{}x{}", input_dim, output_dim)),
|b| {
b.iter(|| kfac.update_layer(0, black_box(&activations), black_box(&gradients)));
},
);
// K-FAC natural gradient
kfac.update_layer(0, &activations, &gradients).unwrap();
group.bench_function(
BenchmarkId::new("kfac_nat_grad", format!("{}x{}", input_dim, output_dim)),
|b| {
b.iter(|| kfac.natural_gradient_layer(0, black_box(&weight_grad)));
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_fisher_information,
bench_natural_gradient,
bench_kfac,
);
criterion_main!(benches);

View File

@@ -0,0 +1,94 @@
//! Benchmarks for optimal transport algorithms
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use rand::prelude::*;
use rand_distr::StandardNormal;
use ruvector_math::optimal_transport::{OptimalTransport, SinkhornSolver, SlicedWasserstein};
fn generate_points(n: usize, dim: usize, seed: u64) -> Vec<Vec<f64>> {
let mut rng = StdRng::seed_from_u64(seed);
(0..n)
.map(|_| (0..dim).map(|_| rng.sample(StandardNormal)).collect())
.collect()
}
fn bench_sliced_wasserstein(c: &mut Criterion) {
let mut group = c.benchmark_group("sliced_wasserstein");
for n in [100, 500, 1000, 5000] {
group.throughput(Throughput::Elements(n as u64));
let source = generate_points(n, 128, 42);
let target = generate_points(n, 128, 43);
// Vary number of projections
for projections in [50, 100, 200] {
let sw = SlicedWasserstein::new(projections).with_seed(42);
group.bench_with_input(
BenchmarkId::new(format!("n={}_proj={}", n, projections), n),
&(&source, &target),
|b, (s, t)| {
b.iter(|| sw.distance(black_box(s), black_box(t)));
},
);
}
}
group.finish();
}
fn bench_sinkhorn(c: &mut Criterion) {
let mut group = c.benchmark_group("sinkhorn");
for n in [50, 100, 200, 500] {
group.throughput(Throughput::Elements((n * n) as u64));
let source = generate_points(n, 32, 42);
let target = generate_points(n, 32, 43);
for reg in [0.01, 0.05, 0.1] {
let solver = SinkhornSolver::new(reg, 100);
group.bench_with_input(
BenchmarkId::new(format!("n={}_reg={}", n, reg), n),
&(&source, &target),
|b, (s, t)| {
b.iter(|| solver.distance(black_box(s), black_box(t)));
},
);
}
}
group.finish();
}
fn bench_scaling(c: &mut Criterion) {
let mut group = c.benchmark_group("scaling");
// Test how Sliced Wasserstein scales with dimension
let n = 500;
for dim in [32, 64, 128, 256, 512] {
let source = generate_points(n, dim, 42);
let target = generate_points(n, dim, 43);
let sw = SlicedWasserstein::new(100).with_seed(42);
group.bench_with_input(
BenchmarkId::new("sw_dim_scaling", dim),
&(&source, &target),
|b, (s, t)| {
b.iter(|| sw.distance(black_box(s), black_box(t)));
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_sliced_wasserstein,
bench_sinkhorn,
bench_scaling,
);
criterion_main!(benches);

View File

@@ -0,0 +1,169 @@
//! Benchmarks for product manifold operations
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use rand::prelude::*;
use rand_distr::StandardNormal;
use ruvector_math::product_manifold::ProductManifold;
use ruvector_math::spherical::SphericalSpace;
fn generate_point(dim: usize, rng: &mut impl Rng) -> Vec<f64> {
(0..dim).map(|_| rng.sample(StandardNormal)).collect()
}
fn bench_product_manifold_distance(c: &mut Criterion) {
let mut group = c.benchmark_group("product_manifold_distance");
// Various configurations
let configs = [
(32, 0, 0, "euclidean_only"),
(0, 16, 0, "hyperbolic_only"),
(0, 0, 8, "spherical_only"),
(32, 16, 8, "mixed_small"),
(64, 32, 16, "mixed_medium"),
(128, 64, 32, "mixed_large"),
];
for (e, h, s, name) in configs.iter() {
let manifold = ProductManifold::new(*e, *h, *s);
let dim = manifold.dim();
let mut rng = StdRng::seed_from_u64(42);
let x = manifold.project(&generate_point(dim, &mut rng)).unwrap();
let y = manifold.project(&generate_point(dim, &mut rng)).unwrap();
group.throughput(Throughput::Elements(dim as u64));
group.bench_with_input(BenchmarkId::new(*name, dim), &(&x, &y), |b, (px, py)| {
b.iter(|| manifold.distance(black_box(px), black_box(py)));
});
}
group.finish();
}
fn bench_product_manifold_exp_log(c: &mut Criterion) {
let mut group = c.benchmark_group("product_manifold_exp_log");
let manifold = ProductManifold::new(64, 32, 16);
let dim = manifold.dim();
let mut rng = StdRng::seed_from_u64(42);
let x = manifold.project(&generate_point(dim, &mut rng)).unwrap();
let y = manifold.project(&generate_point(dim, &mut rng)).unwrap();
let v = manifold.log_map(&x, &y).unwrap();
group.throughput(Throughput::Elements(dim as u64));
group.bench_function("exp_map", |b| {
b.iter(|| manifold.exp_map(black_box(&x), black_box(&v)));
});
group.bench_function("log_map", |b| {
b.iter(|| manifold.log_map(black_box(&x), black_box(&y)));
});
group.bench_function("geodesic", |b| {
b.iter(|| manifold.geodesic(black_box(&x), black_box(&y), 0.5));
});
group.finish();
}
fn bench_frechet_mean(c: &mut Criterion) {
let mut group = c.benchmark_group("frechet_mean");
for n in [10, 50, 100, 200] {
let manifold = ProductManifold::new(32, 16, 8);
let dim = manifold.dim();
let mut rng = StdRng::seed_from_u64(42);
let points: Vec<Vec<f64>> = (0..n)
.map(|_| manifold.project(&generate_point(dim, &mut rng)).unwrap())
.collect();
group.throughput(Throughput::Elements((n * dim) as u64));
group.bench_with_input(
BenchmarkId::new("product_manifold", n),
&points,
|b, pts| {
b.iter(|| manifold.frechet_mean(black_box(pts), None));
},
);
}
group.finish();
}
fn bench_spherical_operations(c: &mut Criterion) {
let mut group = c.benchmark_group("spherical");
for dim in [8, 16, 32, 64, 128] {
let sphere = SphericalSpace::new(dim);
let mut rng = StdRng::seed_from_u64(42);
let x = sphere.project(&generate_point(dim, &mut rng)).unwrap();
let y = sphere.project(&generate_point(dim, &mut rng)).unwrap();
group.throughput(Throughput::Elements(dim as u64));
group.bench_with_input(
BenchmarkId::new("distance", dim),
&(&x, &y),
|b, (px, py)| {
b.iter(|| sphere.distance(black_box(px), black_box(py)));
},
);
group.bench_with_input(
BenchmarkId::new("exp_map", dim),
&(&x, &y),
|b, (px, py)| {
if let Ok(v) = sphere.log_map(px, py) {
b.iter(|| sphere.exp_map(black_box(px), black_box(&v)));
}
},
);
}
group.finish();
}
fn bench_knn(c: &mut Criterion) {
let mut group = c.benchmark_group("knn");
let manifold = ProductManifold::new(64, 32, 16);
let dim = manifold.dim();
for n in [100, 500, 1000] {
let mut rng = StdRng::seed_from_u64(42);
let points: Vec<Vec<f64>> = (0..n)
.map(|_| manifold.project(&generate_point(dim, &mut rng)).unwrap())
.collect();
let query = manifold.project(&generate_point(dim, &mut rng)).unwrap();
group.throughput(Throughput::Elements(n as u64));
for k in [5, 10, 20] {
group.bench_with_input(
BenchmarkId::new(format!("n={}_k={}", n, k), n),
&(&query, &points),
|b, (q, pts)| {
b.iter(|| manifold.knn(black_box(q), black_box(pts), k));
},
);
}
}
group.finish();
}
criterion_group!(
benches,
bench_product_manifold_distance,
bench_product_manifold_exp_log,
bench_frechet_mean,
bench_spherical_operations,
bench_knn,
);
criterion_main!(benches);

View File

@@ -0,0 +1,99 @@
//! Benchmarks for spectral methods
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion};
use ruvector_math::spectral::{ChebyshevExpansion, ChebyshevPolynomial};
fn bench_chebyshev_eval(c: &mut Criterion) {
let mut group = c.benchmark_group("chebyshev_eval");
for degree in [10, 20, 50, 100] {
group.bench_with_input(
BenchmarkId::new("degree", degree),
&degree,
|bench, &deg| {
let poly = ChebyshevPolynomial::new(deg);
bench.iter(|| poly.eval(black_box(0.5)));
},
);
}
group.finish();
}
fn bench_chebyshev_eval_all(c: &mut Criterion) {
let mut group = c.benchmark_group("chebyshev_eval_all");
for degree in [10, 20, 50, 100] {
group.bench_with_input(
BenchmarkId::new("degree", degree),
&degree,
|bench, &deg| {
bench.iter(|| ChebyshevPolynomial::eval_all(black_box(0.5), black_box(deg)));
},
);
}
group.finish();
}
fn bench_chebyshev_expansion(c: &mut Criterion) {
let mut group = c.benchmark_group("chebyshev_expansion");
for degree in [10, 20, 50] {
group.bench_with_input(
BenchmarkId::new("from_function", degree),
&degree,
|bench, &deg| {
bench.iter(|| ChebyshevExpansion::from_function(|x| x.sin(), black_box(deg)));
},
);
}
group.finish();
}
fn bench_heat_kernel(c: &mut Criterion) {
let mut group = c.benchmark_group("heat_kernel");
for degree in [10, 20, 50] {
for t in [0.1, 1.0, 10.0] {
group.bench_with_input(
BenchmarkId::new(format!("deg={}_t={}", degree, t), degree),
&(degree, t),
|bench, &(deg, t)| {
bench.iter(|| ChebyshevExpansion::heat_kernel(black_box(t), black_box(deg)));
},
);
}
}
group.finish();
}
fn bench_clenshaw_eval(c: &mut Criterion) {
let mut group = c.benchmark_group("clenshaw_eval");
for degree in [10, 20, 50, 100] {
let expansion = ChebyshevExpansion::from_function(|x| x.sin(), degree);
group.bench_with_input(
BenchmarkId::new("degree", degree),
&expansion,
|bench, exp| {
bench.iter(|| exp.eval(black_box(0.5)));
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_chebyshev_eval,
bench_chebyshev_eval_all,
bench_chebyshev_expansion,
bench_heat_kernel,
bench_clenshaw_eval,
);
criterion_main!(benches);

View File

@@ -0,0 +1,117 @@
//! Benchmarks for tropical algebra operations
use criterion::{black_box, criterion_group, criterion_main, BenchmarkId, Criterion, Throughput};
use rand::prelude::*;
use ruvector_math::tropical::{MinPlusMatrix, TropicalMatrix};
fn generate_tropical_matrix(n: usize, seed: u64) -> TropicalMatrix {
let mut rng = StdRng::seed_from_u64(seed);
let data: Vec<Vec<f64>> = (0..n)
.map(|_| (0..n).map(|_| rng.gen_range(-10.0..10.0)).collect())
.collect();
TropicalMatrix::from_rows(data)
}
fn generate_minplus_matrix(n: usize, seed: u64) -> MinPlusMatrix {
let mut rng = StdRng::seed_from_u64(seed);
let adj: Vec<Vec<f64>> = (0..n)
.map(|i| {
(0..n)
.map(|j| {
if i == j {
0.0
} else if rng.gen_bool(0.3) {
rng.gen_range(1.0..20.0)
} else {
f64::INFINITY
}
})
.collect()
})
.collect();
MinPlusMatrix::from_adjacency(adj)
}
fn bench_tropical_matmul(c: &mut Criterion) {
let mut group = c.benchmark_group("tropical_matmul");
for n in [32, 64, 128, 256] {
group.throughput(Throughput::Elements((n * n) as u64));
let a = generate_tropical_matrix(n, 42);
let b = generate_tropical_matrix(n, 43);
group.bench_with_input(BenchmarkId::new("size", n), &(&a, &b), |bench, (a, b)| {
bench.iter(|| a.mul(black_box(b)));
});
}
group.finish();
}
fn bench_tropical_power(c: &mut Criterion) {
let mut group = c.benchmark_group("tropical_power");
for n in [16, 32, 64] {
let m = generate_tropical_matrix(n, 42);
for k in [2, 4, 8] {
group.bench_with_input(
BenchmarkId::new(format!("n={}_k={}", n, k), n),
&m,
|bench, m: &TropicalMatrix| {
bench.iter(|| m.pow(black_box(k)));
},
);
}
}
group.finish();
}
fn bench_shortest_paths(c: &mut Criterion) {
let mut group = c.benchmark_group("shortest_paths");
for n in [32, 64, 128, 256] {
group.throughput(Throughput::Elements((n * n) as u64));
let m = generate_minplus_matrix(n, 42);
group.bench_with_input(
BenchmarkId::new("floyd_warshall", n),
&m,
|bench, m: &MinPlusMatrix| {
bench.iter(|| m.all_pairs_shortest_paths());
},
);
}
group.finish();
}
fn bench_tropical_eigenvalue(c: &mut Criterion) {
let mut group = c.benchmark_group("tropical_eigenvalue");
for n in [16, 32, 64, 128] {
let m = generate_tropical_matrix(n, 42);
group.bench_with_input(
BenchmarkId::new("max_cycle_mean", n),
&m,
|bench, m: &TropicalMatrix| {
bench.iter(|| m.max_cycle_mean());
},
);
}
group.finish();
}
criterion_group!(
benches,
bench_tropical_matmul,
bench_tropical_power,
bench_shortest_paths,
bench_tropical_eigenvalue,
);
criterion_main!(benches);