use crate::matrix_util::half_to_full;
use crate::postproc::match_postproc;
#[derive(Debug, Clone, Copy)]
pub struct AuctionOptions {
pub array_base: usize, pub max_iterations: usize,
pub max_unchanged: [usize; 3],
pub min_proportion: [f64; 3],
pub eps_initial: f64,
}
impl Default for AuctionOptions {
fn default() -> Self {
Self {
array_base: 0, max_iterations: 30000,
max_unchanged: [10, 100, 100],
min_proportion: [0.90, 0.0, 0.0],
eps_initial: 0.01,
}
}
}
#[derive(Default, Debug, Clone, Copy)]
pub struct AuctionInform {
pub flag: i32,
pub stat: i32,
pub matched: usize,
pub iterations: usize,
pub unmatchable: usize,
}
pub fn auction_scale_sym(
n: usize,
ptr: &[usize],
row: &[usize],
val: &[f64],
scaling: &mut [f64],
options: &AuctionOptions,
inform: &mut AuctionInform,
match_result: Option<&mut [i32]>,
) {
inform.flag = 0;
let mut rscaling = vec![0.0; n];
let mut cscaling = vec![0.0; n];
match match_result {
Some(match_vec) => {
auction_match(
true,
n,
n,
ptr,
row,
val,
match_vec,
&mut rscaling,
&mut cscaling,
options,
inform,
);
}
None => {
let mut perm = vec![0; n];
auction_match(
true,
n,
n,
ptr,
row,
val,
&mut perm,
&mut rscaling,
&mut cscaling,
options,
inform,
);
}
}
for (s, (&r, &c)) in scaling.iter_mut().zip(rscaling.iter().zip(cscaling.iter())) {
*s = f64::exp((r + c) / 2.0);
}
}
pub fn auction_scale_unsym(
m: usize,
n: usize,
ptr: &[usize],
row: &[usize],
val: &[f64],
rscaling: &mut [f64],
cscaling: &mut [f64],
options: &AuctionOptions,
inform: &mut AuctionInform,
match_result: Option<&mut [i32]>,
) {
inform.flag = 0;
match match_result {
Some(match_vec) => {
auction_match(
false, m, n, ptr, row, val, match_vec, rscaling, cscaling, options, inform,
);
}
None => {
let mut perm = vec![0; m];
auction_match(
false, m, n, ptr, row, val, &mut perm, rscaling, cscaling, options, inform,
);
}
}
for r in rscaling.iter_mut() {
*r = r.exp();
}
for c in cscaling.iter_mut() {
*c = c.exp();
}
}
fn auction_match_core(
m: usize,
n: usize,
ptr: &[usize],
row: &[usize],
val: &[f64],
match_result: &mut [i32],
dualu: &mut [f64],
dualv: &mut [f64],
options: &AuctionOptions,
inform: &mut AuctionInform,
) {
inform.flag = 0;
inform.unmatchable = 0;
let mut owner = vec![0; m];
let mut next = vec![0; n];
let minmn = m.min(n);
let mut unmatched = minmn;
match_result.fill(0); owner.fill(0);
dualu.fill(0.0);
let mut prev = usize::MAX;
let mut nunchanged = 0;
let mut tail = n;
for i in 0..n {
next[i] = i;
}
let mut eps = options.eps_initial;
for itr in 0..options.max_iterations {
if unmatched == 0 {
break; }
if unmatched != prev {
nunchanged = 0;
}
prev = unmatched;
nunchanged += 1;
for i in 0..3 {
if nunchanged >= options.max_unchanged[i]
&& (minmn - unmatched) as f64 / minmn as f64 >= options.min_proportion[i]
{
inform.iterations = itr;
return;
}
}
eps = f64::min(1.0, eps + 1.0 / (n as f64 + 1.0));
let mut insert = 0;
for cptr in 0..tail {
let col = next[cptr];
if match_result[col] != 0 {
continue; }
if ptr[col] == ptr[col + 1] {
continue; }
let j = ptr[col];
let mut bestr = row[j];
let mut bestu = val[j] - dualu[bestr];
let mut bestv = f64::NEG_INFINITY;
for j in ptr[col] + 1..ptr[col + 1] {
let u = val[j] - dualu[row[j]];
if u > bestu {
bestv = bestu;
bestr = row[j];
bestu = u;
} else if u > bestv {
bestv = u;
}
}
if bestv == f64::NEG_INFINITY {
bestv = 0.0; }
if bestu > 0.0 {
dualu[bestr] += bestu - bestv + eps;
dualv[col] = bestv - eps; match_result[col] = bestr as i32;
unmatched -= 1;
let k = owner[bestr];
owner[bestr] = col;
if k != 0 {
match_result[k] = 0; unmatched += 1;
next[insert] = k;
insert += 1;
}
} else {
match_result[col] = -1; unmatched -= 1;
inform.unmatchable += 1;
}
}
tail = insert;
}
inform.iterations = options.max_iterations;
for m in match_result.iter_mut() {
if *m == -1 {
*m = 0;
}
}
}
fn auction_match(
expand: bool,
m: usize,
n: usize,
ptr: &[usize],
row: &[usize],
val: &[f64],
match_result: &mut [i32],
rscaling: &mut [f64],
cscaling: &mut [f64],
options: &AuctionOptions,
inform: &mut AuctionInform,
) {
inform.flag = 0;
let mut ne = ptr[n] - 1;
ne = 2 * ne;
let mut ptr2 = vec![0; n + 1];
let mut row2 = vec![0; ne];
let mut val2 = vec![0.0; ne];
let mut cmax = vec![0.0; n];
let mut cmatch = vec![0; n];
let mut klong = 0;
for i in 0..n {
ptr2[i] = klong;
for jlong in ptr[i]..ptr[i + 1] {
if val[jlong] == 0.0 {
continue;
}
row2[klong] = row[jlong];
val2[klong] = val[jlong].abs().ln();
klong += 1;
}
}
ptr2[n] = klong;
if expand {
if m != n {
inform.flag = -99;
return;
}
let mut iw = vec![0; 5 * n];
half_to_full(n, &mut row2, &mut ptr2, &mut iw, Some(&mut val2), true);
}
for i in 0..n {
if ptr2[i + 1] <= ptr2[i] {
cmax[i] = 0.0;
continue;
}
let colmax = val2[ptr2[i]..ptr2[i + 1]]
.iter()
.fold(f64::NEG_INFINITY, |a, &b| a.max(b));
cmax[i] = colmax;
for v in &mut val2[ptr2[i]..ptr2[i + 1]] {
*v = colmax - *v;
}
}
let maxentry = val2[..ptr2[n]]
.iter()
.fold(f64::NEG_INFINITY, |a, &b| a.max(b));
let maxentry = 2.0 * maxentry + 1.0;
for v in &mut val2[..ptr2[n]] {
*v = maxentry - *v;
}
for (c, &max) in cscaling.iter_mut().zip(cmax.iter()) {
*c = -max; }
auction_match_core(
m,
n,
&ptr2,
&row2,
&val2,
&mut cmatch,
rscaling,
cscaling,
options,
inform,
);
inform.matched = cmatch.iter().filter(|&&x| x != 0).count();
for r in rscaling.iter_mut() {
*r = -(*r) + maxentry;
}
for (c, &max) in cscaling.iter_mut().zip(cmax.iter()) {
*c = -(*c) - max;
}
match_result.fill(0);
for (i, &m) in cmatch.iter().enumerate() {
if m == 0 {
continue; }
match_result[m as usize] = i as i32;
}
match_postproc(
m,
n,
ptr,
row,
val,
rscaling,
cscaling,
inform.matched,
match_result,
&mut inform.flag,
);
}