use std::iter::zip;
#[derive(Debug, Clone, Copy)]
pub struct EquilibOptions {
pub array_base: usize, pub max_iterations: usize,
pub tol: f64,
}
impl Default for EquilibOptions {
fn default() -> Self {
Self {
array_base: 0, max_iterations: 10,
tol: 1e-8,
}
}
}
#[derive(Default, Debug, Clone, Copy)]
pub struct EquilibInform {
pub flag: i32,
pub stat: i32,
pub iterations: usize,
}
pub fn equilib_scale_sym(
n: usize,
ptr: &[usize],
row: &[usize],
val: &[f64],
scaling: &mut [f64],
options: &EquilibOptions,
inform: &mut EquilibInform,
) {
inform.flag = 0;
let mut maxentry = vec![0.0; n];
scaling.fill(1.0);
for itr in 1..=options.max_iterations {
maxentry.fill(0.0);
for c in 0..n {
for j in ptr[c]..ptr[c + 1] {
let r = row[j];
let v = (scaling[r] * val[j] * scaling[c]).abs();
maxentry[r] = f64::max(maxentry[r], v);
maxentry[c] = f64::max(maxentry[c], v);
}
}
for (s, &m) in zip(scaling.iter_mut(), &maxentry) {
if m > 0.0 {
*s /= m.sqrt();
}
}
if maxentry
.iter()
.map(|&m| (1.0 - m).abs())
.max_by(|a, b| a.partial_cmp(b).unwrap())
.unwrap()
< options.tol
{
break;
}
inform.iterations = itr;
}
}
pub fn equilib_scale_unsym(
m: usize,
n: usize,
ptr: &[usize],
row: &[usize],
val: &[f64],
rscaling: &mut [f64],
cscaling: &mut [f64],
options: &EquilibOptions,
inform: &mut EquilibInform,
) {
inform.flag = 0;
let mut rmaxentry = vec![0.0; m];
let mut cmaxentry = vec![0.0; n];
rscaling.fill(1.0);
cscaling.fill(1.0);
for itr in 1..options.max_iterations {
rmaxentry.fill(0.0);
cmaxentry.fill(0.0);
for c in 0..n {
for j in ptr[c]..ptr[c + 1] {
let r = row[j];
let v = (rscaling[r] * val[j] * cscaling[c]).abs();
rmaxentry[r] = f64::max(rmaxentry[r], v);
cmaxentry[c] = f64::max(cmaxentry[c], v);
}
}
for (r, &m) in zip(rscaling.iter_mut(), &rmaxentry) {
if m > 0.0 {
*r /= m.sqrt();
}
}
for (c, &m) in zip(cscaling.iter_mut(), &cmaxentry) {
if m > 0.0 {
*c /= m.sqrt();
}
}
let rmax_diff = rmaxentry
.iter()
.map(|&m| (1.0 - m).abs())
.fold(0.0, f64::max);
let cmax_diff = cmaxentry
.iter()
.map(|&m| (1.0 - m).abs())
.fold(0.0, f64::max);
if rmax_diff < options.tol && cmax_diff < options.tol {
break;
}
inform.iterations = itr;
}
}