use crate::ordering::elimination_tree::EliminationTree;
use crate::sparse::csc::CscPattern;
pub fn column_counts(pattern: &CscPattern, _etree: &EliminationTree) -> Vec<usize> {
let n = pattern.n;
if n == 0 {
return Vec::new();
}
let mut col_rows: Vec<Vec<usize>> = vec![Vec::new(); n];
for (j, col_j) in col_rows.iter_mut().enumerate() {
for k in pattern.col_ptr[j]..pattern.col_ptr[j + 1] {
let i = pattern.row_idx[k];
if i > j {
col_j.push(i);
}
}
col_j.sort_unstable();
col_j.dedup();
}
let mut counts = vec![1usize; n];
for j in 0..n {
let rows = std::mem::take(&mut col_rows[j]);
counts[j] += rows.len();
if rows.len() > 1 {
let min_row = rows[0]; for &row in &rows[1..] {
if !col_rows[min_row].contains(&row) {
col_rows[min_row].push(row);
}
}
col_rows[min_row].sort_unstable();
col_rows[min_row].dedup();
}
}
counts
}
pub fn total_factor_nnz(counts: &[usize]) -> usize {
counts.iter().sum()
}
pub fn column_counts_gnp(pattern: &CscPattern, etree: &EliminationTree) -> Vec<usize> {
let n = pattern.n;
if n == 0 {
return Vec::new();
}
let post = etree.postorder();
let first = etree.first_descendants(&post);
let children = etree.children();
let mut delta: Vec<i64> = (0..n)
.map(|i| if children[i].is_empty() { 1 } else { 0 })
.collect();
let mut maxfirst = vec![-1i64; n];
let mut prevleaf = vec![-1i64; n];
let mut ancestor: Vec<usize> = (0..n).collect();
for &i in &post {
if let Some(p) = etree.parent[i] {
delta[p] -= 1;
}
let fi = first[i] as i64;
let row_start = pattern.col_ptr[i];
let row_end = pattern.col_ptr[i + 1];
for k in row_start..row_end {
let partner = pattern.row_idx[k];
if partner <= i {
continue;
}
if fi > maxfirst[partner] {
delta[i] += 1;
let pl = prevleaf[partner];
if pl != -1 {
let mut q = pl as usize;
while ancestor[q] != q {
q = ancestor[q];
}
let root = q;
let mut cur = pl as usize;
while cur != root {
let next = ancestor[cur];
ancestor[cur] = root;
cur = next;
}
delta[root] -= 1;
}
prevleaf[partner] = i as i64;
maxfirst[partner] = fi;
}
}
if let Some(p) = etree.parent[i] {
ancestor[i] = p;
}
}
for &i in &post {
if let Some(p) = etree.parent[i] {
delta[p] += delta[i];
}
}
delta.iter().map(|&d| d as usize).collect()
}
#[cfg(test)]
mod tests {
use super::*;
use crate::sparse::csc::CscMatrix;
#[test]
fn test_column_counts_diagonal() {
let m = CscMatrix::from_triplets(4, &[0, 1, 2, 3], &[0, 1, 2, 3], &[1.0; 4]).unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
let counts = column_counts(&pat, &etree);
assert_eq!(counts, vec![1, 1, 1, 1]);
assert_eq!(total_factor_nnz(&counts), 4);
}
#[test]
fn test_column_counts_tridiagonal() {
let m =
CscMatrix::from_triplets(4, &[0, 1, 1, 2, 2, 3, 3], &[0, 0, 1, 1, 2, 2, 3], &[1.0; 7])
.unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
let counts = column_counts(&pat, &etree);
assert_eq!(counts, vec![2, 2, 2, 1]);
assert_eq!(total_factor_nnz(&counts), 7);
}
#[test]
fn test_column_counts_dense() {
let m = CscMatrix::from_triplets(3, &[0, 1, 2, 1, 2, 2], &[0, 0, 0, 1, 1, 2], &[1.0; 6])
.unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
let counts = column_counts(&pat, &etree);
assert_eq!(counts, vec![3, 2, 1]);
assert_eq!(total_factor_nnz(&counts), 6); }
#[test]
fn test_column_counts_arrow() {
let m = CscMatrix::from_triplets(
5,
&[0, 1, 2, 3, 4, 1, 2, 3, 4],
&[0, 0, 0, 0, 0, 1, 2, 3, 4],
&[1.0; 9],
)
.unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
let counts = column_counts(&pat, &etree);
assert_eq!(counts, vec![5, 4, 3, 2, 1]);
assert_eq!(total_factor_nnz(&counts), 15); }
#[test]
fn test_column_counts_block_diagonal() {
let m = CscMatrix::from_triplets(4, &[0, 1, 1, 2, 3, 3], &[0, 0, 1, 2, 2, 3], &[1.0; 6])
.unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
let counts = column_counts(&pat, &etree);
assert_eq!(counts, vec![2, 1, 2, 1]);
assert_eq!(total_factor_nnz(&counts), 6);
}
#[test]
fn gnp_matches_reference_diagonal() {
let m = CscMatrix::from_triplets(4, &[0, 1, 2, 3], &[0, 1, 2, 3], &[1.0; 4]).unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
assert_eq!(column_counts_gnp(&pat, &etree), column_counts(&pat, &etree));
}
#[test]
fn gnp_matches_reference_tridiagonal() {
let m =
CscMatrix::from_triplets(4, &[0, 1, 1, 2, 2, 3, 3], &[0, 0, 1, 1, 2, 2, 3], &[1.0; 7])
.unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
assert_eq!(column_counts_gnp(&pat, &etree), column_counts(&pat, &etree));
}
#[test]
fn gnp_matches_reference_dense() {
let m = CscMatrix::from_triplets(3, &[0, 1, 2, 1, 2, 2], &[0, 0, 0, 1, 1, 2], &[1.0; 6])
.unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
assert_eq!(column_counts_gnp(&pat, &etree), column_counts(&pat, &etree));
}
#[test]
fn gnp_matches_reference_arrow() {
let m = CscMatrix::from_triplets(
5,
&[0, 1, 2, 3, 4, 1, 2, 3, 4],
&[0, 0, 0, 0, 0, 1, 2, 3, 4],
&[1.0; 9],
)
.unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
assert_eq!(column_counts_gnp(&pat, &etree), column_counts(&pat, &etree));
}
#[test]
fn gnp_matches_reference_block_diagonal() {
let m = CscMatrix::from_triplets(4, &[0, 1, 1, 2, 3, 3], &[0, 0, 1, 2, 2, 3], &[1.0; 6])
.unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
assert_eq!(column_counts_gnp(&pat, &etree), column_counts(&pat, &etree));
}
#[test]
fn gnp_matches_reference_banded() {
let rows = vec![0, 1, 2, 1, 2, 3, 2, 3, 4, 3, 4, 5, 4, 5, 6, 5, 6, 6];
let cols = vec![0, 0, 0, 1, 1, 1, 2, 2, 2, 3, 3, 3, 4, 4, 4, 5, 5, 6];
let m = CscMatrix::from_triplets(7, &rows, &cols, &vec![1.0; rows.len()]).unwrap();
let pat = m.symmetric_pattern();
let etree = EliminationTree::from_pattern(&pat);
assert_eq!(column_counts_gnp(&pat, &etree), column_counts(&pat, &etree));
}
}