use xingo_sparse::{Amd, Colamd, CscMatrix, Natural, Ordering, OrderingTarget, Pattern, Triplets};
fn grid_laplacian(k: usize) -> CscMatrix {
let n = k * k;
let node = |r: usize, c: usize| u32::try_from(r * k + c).expect("grid fits u32");
let mut t = Triplets::new(n, n);
for r in 0..k {
for c in 0..k {
t.push(node(r, c), node(r, c), 4.0);
if r + 1 < k {
t.push(node(r, c), node(r + 1, c), -1.0);
t.push(node(r + 1, c), node(r, c), -1.0);
}
if c + 1 < k {
t.push(node(r, c), node(r, c + 1), -1.0);
t.push(node(r, c + 1), node(r, c), -1.0);
}
}
}
t.to_csc().expect("valid triplets")
}
fn dense_column_first(m: usize, n: usize) -> CscMatrix {
let mut t = Triplets::new(m, n);
for i in 0..m {
t.push(u32::try_from(i).expect("fits u32"), 0, 1.0);
}
for j in 1..n {
let jj = u32::try_from(j).expect("fits u32");
t.push(jj - 1, jj, 1.0);
t.push(jj, jj, -1.0);
}
t.to_csc().expect("valid triplets")
}
fn fill(o: &dyn Ordering, s: &Pattern, target: OrderingTarget) -> usize {
let perm = o
.order(s, target)
.expect("a square or rectangular pattern as the target requires");
let graph = match target {
OrderingTarget::Symmetric => s.clone(),
OrderingTarget::Columns => s.ata(),
};
graph
.cholesky_nnz(&perm)
.expect("square graph, permutation of its order")
}
fn report(
title: &str,
s: &Pattern,
target: OrderingTarget,
orderings: &[&dyn Ordering],
) -> Vec<usize> {
println!(
"{title}: {} × {}, {} entries",
s.n_rows(),
s.n_cols(),
s.nnz()
);
let fills: Vec<usize> = orderings.iter().map(|o| fill(*o, s, target)).collect();
let natural = fills[0];
for (o, &f) in orderings.iter().zip(&fills) {
#[allow(clippy::cast_precision_loss)] let ratio = f as f64 / natural as f64;
println!(
" {:<8} nnz(L) = {f:>6} ({ratio:.2}× the natural order)",
o.name()
);
}
fills
}
fn main() {
let k = 12;
let grid = grid_laplacian(k).pattern();
let symmetric = report(
&format!("5-point Laplacian of a {k} × {k} grid"),
&grid,
OrderingTarget::Symmetric,
&[&Natural, &Amd::default(), &Colamd::default()],
);
let (m, n) = (40, 30);
let arrow = dense_column_first(m, n).pattern();
let columns = report(
"dense column first",
&arrow,
OrderingTarget::Columns,
&[&Natural, &Colamd::default()],
);
assert!(
symmetric[1] < symmetric[0],
"AMD beats the band order on the grid"
);
assert!(
symmetric[2] < symmetric[0],
"SYMAMD beats the band order on the grid"
);
assert_eq!(
columns[0],
n * (n + 1) / 2,
"the natural order fills the whole triangle"
);
assert!(
columns[1] < columns[0],
"COLAMD moves the dense column last"
);
println!("ok");
}