use alloc::vec::Vec;
mod inner;
use core::fmt::Debug;
use inner::LapmodInner;
use num_traits::{AsPrimitive, One};
use super::{
LAPError,
lap_error::{validate_lap_entry_costs, validate_sparse_lap_entry_costs},
};
use crate::{
impls::ValuedCSR2D,
traits::{
Finite, MatrixMut, Number, SparseMatrixMut, SparseValuedMatrix2D, TotalOrd, TryFromUsize,
},
};
pub trait LAPMOD: SparseValuedMatrix2D + Sized
where
Self::Value: Number + Finite + TotalOrd,
Self::RowIndex: TryFromUsize,
Self::ColumnIndex: TryFromUsize,
{
#[allow(clippy::type_complexity)]
#[inline]
fn lapmod(
&self,
max_cost: Self::Value,
) -> Result<Vec<(Self::RowIndex, Self::ColumnIndex)>, LAPError>
where
<Self::ColumnIndex as TryFrom<usize>>::Error: Debug,
<Self::RowIndex as TryFrom<usize>>::Error: Debug,
{
validate_lap_entry_costs(max_cost)?;
let n_rows = self.number_of_rows().as_();
let n_cols = self.number_of_columns().as_();
if n_rows != n_cols {
return Err(LAPError::NonSquareMatrix);
}
if n_rows == 0 {
return Ok(Vec::new());
}
let mut inner = LapmodInner::new(self, max_cost)?;
inner.column_reduction_sparse()?;
inner.reduction_transfer_sparse();
inner.augmenting_row_reduction_sparse();
inner.augmenting_row_reduction_sparse();
inner.augmentation_sparse()?;
Ok(inner.into_assignments())
}
}
impl<M: SparseValuedMatrix2D> LAPMOD for M
where
M::Value: Number + Finite + TotalOrd,
M::RowIndex: TryFromUsize,
M::ColumnIndex: TryFromUsize,
{
}
pub trait Jaqaman: SparseValuedMatrix2D + Sized
where
Self::Value: Number,
Self::RowIndex: TryFromUsize,
Self::ColumnIndex: TryFromUsize,
{
#[allow(clippy::type_complexity)]
#[inline]
fn jaqaman(
&self,
padding_cost: Self::Value,
max_cost: Self::Value,
) -> Result<Vec<(Self::RowIndex, Self::ColumnIndex)>, LAPError>
where
Self::Value: Finite + TotalOrd,
<Self::ColumnIndex as TryFrom<usize>>::Error: Debug,
<Self::RowIndex as TryFrom<usize>>::Error: Debug,
{
validate_sparse_lap_entry_costs(padding_cost, max_cost)?;
if self.is_empty() {
return Ok(vec![]);
}
let one = Self::Value::one();
let two = one + one;
let half_eta = padding_cost / two;
let Some(max_sparse_value) = self.max_sparse_value() else {
return Ok(vec![]);
};
if max_sparse_value >= half_eta {
return Err(LAPError::PaddingCostTooSmall);
}
let n_rows = self.number_of_rows().as_();
let n_cols = self.number_of_columns().as_();
let n = n_rows + n_cols;
if n == 0 {
return Ok(vec![]);
}
let p2 = two * two;
let p4 = p2 * p2;
let p8 = p4 * p4;
let p16 = p8 * p8;
let p32 = p16 * p16;
let p40 = p32 * p8;
let bottom_right_cost = half_eta / p40;
let mut col_to_rows: Vec<Vec<usize>> = vec![Vec::new(); n_cols];
let mut n_edges: usize = 0;
for i in 0..n_rows {
let row_idx =
Self::RowIndex::try_from_usize(i).map_err(|_| LAPError::IndexConversionFailed)?;
for col in self.sparse_row(row_idx) {
let column = col.as_();
let Some(rows) = col_to_rows.get_mut(column) else {
return Err(LAPError::ExpandedMatrixBuildFailed);
};
rows.push(i);
n_edges += 1;
}
}
let total_entries = 2 * n_edges + n_rows + n_cols;
let mut expanded: ValuedCSR2D<usize, usize, usize, Self::Value> =
SparseMatrixMut::with_sparse_shaped_capacity((n, n), total_entries);
for i in 0..n_rows {
let row_idx =
Self::RowIndex::try_from_usize(i).map_err(|_| LAPError::IndexConversionFailed)?;
for (col, value) in self.sparse_row(row_idx).zip(self.sparse_row_values(row_idx)) {
expanded
.add((i, col.as_(), value))
.map_err(|_| LAPError::ExpandedMatrixBuildFailed)?;
}
expanded
.add((i, n_cols + i, half_eta))
.map_err(|_| LAPError::ExpandedMatrixBuildFailed)?;
}
for (j, source_rows) in col_to_rows.iter().enumerate() {
let dummy_row = n_rows + j;
expanded
.add((dummy_row, j, half_eta))
.map_err(|_| LAPError::ExpandedMatrixBuildFailed)?;
for &i in source_rows {
expanded
.add((dummy_row, n_cols + i, bottom_right_cost))
.map_err(|_| LAPError::ExpandedMatrixBuildFailed)?;
}
}
let assignment = expanded.lapmod(max_cost)?;
assignment
.into_iter()
.filter(|&(row, col)| row < n_rows && col < n_cols)
.map(|(row, col)| {
Ok((
Self::RowIndex::try_from_usize(row)
.map_err(|_| LAPError::IndexConversionFailed)?,
Self::ColumnIndex::try_from_usize(col)
.map_err(|_| LAPError::IndexConversionFailed)?,
))
})
.collect()
}
}
impl<M: SparseValuedMatrix2D> Jaqaman for M
where
M::Value: Number,
M::RowIndex: TryFromUsize,
M::ColumnIndex: TryFromUsize,
{
}