use derive_more::{Display, Error};
#[derive(Debug, Display, Error)]
pub enum LSAPError {
Invalid,
Infeasible,
}
pub fn get_assigned_cost(
nr: usize,
nc: usize,
cost: &Vec<f64>,
maximize: bool,
) -> Result<f64, LSAPError> {
let (rows, cols) = solve(nr, nc, cost, maximize)?;
let mut score = 0.0;
for i in 0..rows.len() {
score += cost[rows[i] * nc + cols[i]];
}
return Ok(score);
}
pub fn solve(
mut nr: usize,
mut nc: usize,
cost: &Vec<f64>,
maximize: bool,
) -> Result<(Vec<usize>, Vec<usize>), LSAPError> {
if nr == 0 || nc == 0 {
return Ok((vec![], vec![]));
}
let transpose = nc < nr;
let mut temp: Vec<f64>;
let surrogated_cost = if transpose || maximize {
if transpose {
temp = vec![0.0; nc * nr];
for i in 0..nr {
for j in 0..nc {
temp[j * nr + i] = cost[i * nc + j];
}
}
std::mem::swap(&mut nr, &mut nc);
} else {
temp = cost.clone();
}
if maximize {
for i in 0..(nr * nc) {
temp[i] = -temp[i];
}
}
&temp
} else {
cost
};
for i in 0..(nr * nc) {
if surrogated_cost[i].is_nan() || surrogated_cost[i].is_infinite() {
return Err(LSAPError::Invalid);
}
}
let MINUS_1: usize = nr * nc;
let mut u = vec![0.0; nr];
let mut v = vec![0.0; nc];
let mut shortest_path_costs: Vec<f64> = vec![f64::INFINITY; nc];
let mut path: Vec<usize> = vec![MINUS_1; nc];
let mut col4row: Vec<usize> = vec![MINUS_1; nr];
let mut row4col: Vec<usize> = vec![MINUS_1; nc];
let mut SR: Vec<bool> = vec![false; nr];
let mut SC: Vec<bool> = vec![false; nc];
let mut remaining: Vec<usize> = vec![MINUS_1; nc];
for cur_row in 0..nr {
let (sink, min_val) = augmenting_path(
nc,
&surrogated_cost,
&mut u,
&mut v,
&mut path,
&row4col,
&mut shortest_path_costs,
cur_row,
&mut SR,
&mut SC,
&mut remaining,
MINUS_1,
);
if sink == MINUS_1 {
return Err(LSAPError::Infeasible);
}
u[cur_row] += min_val;
for i in 0..nr {
if SR[i] && i != cur_row {
u[i] += min_val - shortest_path_costs[col4row[i]];
}
}
for j in 0..nc {
if SC[j] {
v[j] -= min_val - shortest_path_costs[j];
}
}
let mut j = sink;
loop {
let i = path[j];
row4col[j] = i;
std::mem::swap(&mut col4row[i], &mut j);
if i == cur_row {
break;
}
}
}
let mut a = Vec::with_capacity(nr);
let mut b = Vec::with_capacity(nr);
if transpose {
for v in argsort_iter(&col4row) {
a.push(col4row[v]);
b.push(v);
}
} else {
for i in 0..nr {
a.push(i);
b.push(col4row[i]);
}
}
return Ok((a, b));
}
fn augmenting_path(
nc: usize,
cost: &Vec<f64>,
u: &mut Vec<f64>,
v: &mut Vec<f64>,
path: &mut Vec<usize>,
row4col: &Vec<usize>,
shortest_path_costs: &mut Vec<f64>,
mut i: usize,
SR: &mut Vec<bool>,
SC: &mut Vec<bool>,
remaining: &mut Vec<usize>,
MINUS_1: usize,
) -> (usize, f64) {
let mut min_val = 0.0;
let mut num_remaining = nc;
for it in 0..nc {
remaining[it] = nc - it - 1;
}
SR.fill(false);
SC.fill(false);
shortest_path_costs.fill(f64::INFINITY);
let mut sink = MINUS_1;
while sink == MINUS_1 {
let mut index = MINUS_1;
let mut lowest = f64::INFINITY;
SR[i] = true;
for it in 0..num_remaining {
let j = remaining[it];
let r: f64 = min_val + cost[i * nc + j] - u[i] - v[j];
if r < shortest_path_costs[j] {
path[j] = i;
shortest_path_costs[j] = r;
}
if shortest_path_costs[j] < lowest
|| (shortest_path_costs[j] == lowest && row4col[j] == MINUS_1)
{
lowest = shortest_path_costs[j];
index = it;
}
}
min_val = lowest;
if min_val.is_infinite() {
return (MINUS_1, min_val); }
let j = remaining[index];
if row4col[j] == MINUS_1 {
sink = j;
} else {
i = row4col[j];
}
SC[j] = true;
num_remaining -= 1;
remaining[index] = remaining[num_remaining];
}
return (sink, min_val); }
fn argsort_iter<T: Ord>(v: &Vec<T>) -> Vec<usize> {
let mut index = (0..v.len()).collect::<Vec<_>>();
index.sort_by_key(|&i| &v[i]);
index
}