use criterion::{BenchmarkGroup, Criterion, measurement::WallTime};
use la_stack::{Matrix, Vector};
use pastey::paste;
use std::hint::black_box;
#[inline]
#[allow(clippy::cast_precision_loss)]
const fn matrix_entry<const D: usize>(r: usize, c: usize) -> f64 {
if r == c {
(r as f64).mul_add(1.0e-3, (D as f64) + 1.0)
} else {
0.1 / ((r + c + 1) as f64)
}
}
#[inline]
const fn make_matrix_rows<const D: usize>() -> [[f64; D]; D] {
let mut rows = [[0.0; D]; D];
let mut r = 0;
while r < D {
let mut c = 0;
while c < D {
rows[r][c] = matrix_entry::<D>(r, c);
c += 1;
}
r += 1;
}
rows
}
#[inline]
#[allow(clippy::cast_precision_loss)]
fn make_vector_array<const D: usize>() -> [f64; D] {
let mut data = [0.0; D];
let mut i = 0;
while i < D {
data[i] = (i as f64) + 1.0;
i += 1;
}
data
}
#[inline]
fn near_singular_3x3() -> Matrix<3> {
let perturbation = f64::from_bits(0x3CD0_0000_0000_0000); Matrix::<3>::from_rows([
[1.0 + perturbation, 2.0, 3.0],
[4.0, 5.0, 6.0],
[7.0, 8.0, 9.0],
])
}
#[inline]
fn large_entries_3x3() -> Matrix<3> {
let big = f64::MAX / 2.0;
Matrix::<3>::from_rows([[big, 1.0, 1.0], [1.0, big, 1.0], [1.0, 1.0, big]])
}
#[inline]
#[allow(clippy::cast_precision_loss)]
fn hilbert<const D: usize>() -> Matrix<D> {
let mut rows = [[0.0; D]; D];
let mut r = 0;
while r < D {
let mut c = 0;
while c < D {
rows[r][c] = 1.0 / ((r + c + 1) as f64);
c += 1;
}
r += 1;
}
Matrix::<D>::from_rows(rows)
}
fn bench_extreme_group<const D: usize>(
group: &mut BenchmarkGroup<'_, WallTime>,
m: Matrix<D>,
rhs: Vector<D>,
) {
group.bench_function("det_sign_exact", |bencher| {
bencher.iter(|| {
let sign = black_box(m)
.det_sign_exact()
.expect("finite matrix entries");
black_box(sign);
});
});
group.bench_function("det_exact", |bencher| {
bencher.iter(|| {
let det = black_box(m).det_exact().expect("finite matrix entries");
black_box(det);
});
});
group.bench_function("solve_exact", |bencher| {
bencher.iter(|| {
let x = black_box(m)
.solve_exact(black_box(rhs))
.expect("non-singular matrix with finite entries");
let _ = black_box(x);
});
});
group.bench_function("solve_exact_f64", |bencher| {
bencher.iter(|| {
let x = black_box(m)
.solve_exact_f64(black_box(rhs))
.expect("solution representable in f64");
let _ = black_box(x);
});
});
}
macro_rules! gen_exact_benches_for_dim {
($c:expr, $d:literal) => {
paste! {{
let a = Matrix::<$d>::from_rows(make_matrix_rows::<$d>());
let rhs = Vector::<$d>::new(make_vector_array::<$d>());
let mut [<group_d $d>] = ($c).benchmark_group(concat!("exact_d", stringify!($d)));
[<group_d $d>].bench_function("det", |bencher| {
bencher.iter(|| {
let det = black_box(a)
.det(la_stack::DEFAULT_PIVOT_TOL)
.expect("diagonally dominant matrix is non-singular");
black_box(det);
});
});
[<group_d $d>].bench_function("det_direct", |bencher| {
bencher.iter(|| {
let det = black_box(a).det_direct();
black_box(det);
});
});
[<group_d $d>].bench_function("det_exact", |bencher| {
bencher.iter(|| {
let det = black_box(a).det_exact().expect("finite matrix entries");
black_box(det);
});
});
[<group_d $d>].bench_function("det_exact_f64", |bencher| {
bencher.iter(|| {
let det = black_box(a)
.det_exact_f64()
.expect("det representable in f64");
black_box(det);
});
});
[<group_d $d>].bench_function("det_sign_exact", |bencher| {
bencher.iter(|| {
let sign = black_box(a)
.det_sign_exact()
.expect("finite matrix entries");
black_box(sign);
});
});
[<group_d $d>].bench_function("solve_exact", |bencher| {
bencher.iter(|| {
let x = black_box(a)
.solve_exact(black_box(rhs))
.expect("diagonally dominant matrix is non-singular");
black_box(x);
});
});
[<group_d $d>].bench_function("solve_exact_f64", |bencher| {
bencher.iter(|| {
let x = black_box(a)
.solve_exact_f64(black_box(rhs))
.expect("solution representable in f64");
black_box(x);
});
});
[<group_d $d>].finish();
}};
};
}
fn main() {
let mut c = Criterion::default().configure_from_args();
#[allow(unused_must_use)]
{
gen_exact_benches_for_dim!(&mut c, 2);
gen_exact_benches_for_dim!(&mut c, 3);
gen_exact_benches_for_dim!(&mut c, 4);
gen_exact_benches_for_dim!(&mut c, 5);
}
{
let mut group = c.benchmark_group("exact_near_singular_3x3");
bench_extreme_group(
&mut group,
near_singular_3x3(),
Vector::<3>::new([1.0, 2.0, 3.0]),
);
group.finish();
}
{
let mut group = c.benchmark_group("exact_large_entries_3x3");
bench_extreme_group(
&mut group,
large_entries_3x3(),
Vector::<3>::new([1.0, 1.0, 1.0]),
);
group.finish();
}
{
let mut group = c.benchmark_group("exact_hilbert_4x4");
bench_extreme_group(&mut group, hilbert::<4>(), Vector::<4>::new([1.0; 4]));
group.finish();
}
{
let mut group = c.benchmark_group("exact_hilbert_5x5");
bench_extreme_group(&mut group, hilbert::<5>(), Vector::<5>::new([1.0; 5]));
group.finish();
}
c.final_summary();
}