pub mod adjacency;
pub mod matching;
pub mod ordering;
pub mod permutation;
pub mod scc;
use nalgebra::{Dyn, Matrix, PermutationSequence, Scalar, Storage};
use adjacency::{build_row_adjacency, build_row_dependency_graph};
use matching::hopcroft_karp;
use ordering::{col_order_from_row_order, topo_sort_with_tiebreak};
use permutation::permutation_sequence_from_order;
use scc::{condensation_dag, scc_id_map, tarjan_scc};
pub fn upper_triangular_permutations<T, R, C, S>(
mat: &Matrix<T, R, C, S>,
) -> (PermutationSequence<Dyn>, PermutationSequence<Dyn>)
where
T: Scalar + PartialEq + Default,
R: nalgebra::Dim,
C: nalgebra::Dim,
S: Storage<T, R, C>,
{
let structure = upper_block_triangular_structure(mat);
let prow = permutation_sequence_from_order(&structure.row_order);
let pcol = permutation_sequence_from_order(&structure.col_order);
(prow, pcol)
}
#[derive(Debug, Clone)]
pub struct UpperBtfStructure {
pub row_order: Vec<usize>,
pub col_order: Vec<usize>,
pub block_sizes: Vec<usize>,
pub matching_size: usize,
}
pub fn upper_block_triangular_structure<T, R, C, S>(mat: &Matrix<T, R, C, S>) -> UpperBtfStructure
where
T: Scalar + PartialEq + Default,
R: nalgebra::Dim,
C: nalgebra::Dim,
S: Storage<T, R, C>,
{
let nrows = mat.nrows();
let ncols = mat.ncols();
if nrows == 0 || ncols == 0 {
return UpperBtfStructure {
row_order: (0..nrows).collect(),
col_order: (0..ncols).collect(),
block_sizes: Vec::new(),
matching_size: 0,
};
}
let row_adj = build_row_adjacency(mat);
let matching = hopcroft_karp(&row_adj, ncols);
let row_graph = build_row_dependency_graph(&row_adj, &matching.col_to_row);
let sccs = tarjan_scc(&row_graph);
let comp_of = scc_id_map(&sccs, nrows);
let dag = condensation_dag(&row_graph, &comp_of, sccs.len());
let scc_key: Vec<usize> = sccs
.iter()
.map(|comp| comp.iter().copied().min().unwrap_or(usize::MAX))
.collect();
let scc_order = topo_sort_with_tiebreak(&dag, &scc_key);
let mut row_order = Vec::with_capacity(nrows);
let mut block_sizes = Vec::with_capacity(sccs.len());
for &cid in &scc_order {
let mut comp = sccs[cid].clone();
comp.sort_unstable();
block_sizes.push(comp.len());
row_order.extend(comp);
}
let col_order = col_order_from_row_order(&row_order, &matching.row_to_col, ncols);
UpperBtfStructure {
row_order,
col_order,
block_sizes,
matching_size: matching.size,
}
}
impl UpperBtfStructure {
pub fn block_indices(&self) -> Vec<(Vec<usize>, Vec<usize>)> {
let mut blocks = Vec::new();
let mut row_start = 0;
let mut col_start = 0;
for &size in &self.block_sizes {
let row_block: Vec<usize> = self.row_order[row_start..row_start + size].to_vec();
let col_block: Vec<usize> = self.col_order[col_start..col_start + size].to_vec();
blocks.push((row_block, col_block));
row_start += size;
col_start += size;
}
blocks
}
}
pub fn lower_triangular_permutations<T, R, C, S>(
mat: &Matrix<T, R, C, S>,
) -> (PermutationSequence<Dyn>, PermutationSequence<Dyn>)
where
T: Scalar + PartialEq + Default,
R: nalgebra::Dim,
C: nalgebra::Dim,
S: Storage<T, R, C>,
{
let structure = lower_block_triangular_structure(mat);
let prow = permutation_sequence_from_order(&structure.row_order);
let pcol = permutation_sequence_from_order(&structure.col_order);
(prow, pcol)
}
#[derive(Debug, Clone)]
pub struct LowerBtfStructure {
pub row_order: Vec<usize>,
pub col_order: Vec<usize>,
pub block_sizes: Vec<usize>,
pub matching_size: usize,
}
pub fn lower_block_triangular_structure<T, R, C, S>(mat: &Matrix<T, R, C, S>) -> LowerBtfStructure
where
T: Scalar + PartialEq + Default,
R: nalgebra::Dim,
C: nalgebra::Dim,
S: Storage<T, R, C>,
{
let nrows = mat.nrows();
let ncols = mat.ncols();
if nrows == 0 || ncols == 0 {
return LowerBtfStructure {
row_order: (0..nrows).collect(),
col_order: (0..ncols).collect(),
block_sizes: Vec::new(),
matching_size: 0,
};
}
let row_adj = build_row_adjacency(mat);
let matching = hopcroft_karp(&row_adj, ncols);
let row_graph = build_row_dependency_graph(&row_adj, &matching.col_to_row);
let sccs = tarjan_scc(&row_graph);
let comp_of = scc_id_map(&sccs, nrows);
let dag = condensation_dag(&row_graph, &comp_of, sccs.len());
let scc_key: Vec<usize> = sccs
.iter()
.map(|comp| comp.iter().copied().min().unwrap_or(usize::MAX))
.collect();
let mut scc_order = topo_sort_with_tiebreak(&dag, &scc_key);
scc_order.reverse();
let mut row_order = Vec::with_capacity(nrows);
let mut block_sizes = Vec::with_capacity(sccs.len());
for &cid in &scc_order {
let mut comp = sccs[cid].clone();
comp.sort_unstable();
block_sizes.push(comp.len());
row_order.extend(comp);
}
let col_order = col_order_from_row_order(&row_order, &matching.row_to_col, ncols);
LowerBtfStructure {
row_order,
col_order,
block_sizes,
matching_size: matching.size,
}
}
impl LowerBtfStructure {
pub fn block_indices(&self) -> Vec<(Vec<usize>, Vec<usize>)> {
let mut blocks = Vec::new();
let mut row_start = 0;
let mut col_start = 0;
for &size in &self.block_sizes {
let row_block: Vec<usize> = self.row_order[row_start..row_start + size].to_vec();
let col_block: Vec<usize> = self.col_order[col_start..col_start + size].to_vec();
blocks.push((row_block, col_block));
row_start += size;
col_start += size;
}
blocks
}
}