use super::*;
use crate::assert;
use crate::perm::{swap_cols_idx, swap_rows_idx};
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum BalanceError {
NonFiniteInput,
}
pub fn balance_scratch() -> StackReq {
StackReq::EMPTY
}
pub fn balance_in_place<T: ComplexField>(
mut a: MatMut<'_, T>,
perm: &mut [usize],
scale: &mut [T::Real],
_par: Par,
_stack: &mut MemStack,
) -> Result<core::ops::Range<usize>, BalanceError> {
let n = a.nrows();
assert!(a.ncols() == n && perm.len() == n && scale.len() == n);
if !a.is_all_finite() {
return Err(BalanceError::NonFiniteInput);
}
for i in 0..n {
perm[i] = i;
scale[i] = one();
}
let mut lo = 0;
let mut hi = n;
loop {
let row = (lo..hi)
.rev()
.find(|&i| (lo..hi).all(|j| i == j || a[(i, j)] == zero()));
if let Some(i) = row {
hi -= 1;
swap_rows_idx(a.rb_mut(), i, hi);
swap_cols_idx(a.rb_mut(), i, hi);
perm.swap(i, hi);
continue;
}
let col =
(lo..hi).find(|&j| (lo..hi).all(|i| i == j || a[(i, j)] == zero()));
if let Some(j) = col {
swap_rows_idx(a.rb_mut(), j, lo);
swap_cols_idx(a.rb_mut(), j, lo);
perm.swap(j, lo);
lo += 1;
continue;
}
break;
}
let two = from_f64::<T::Real>(2.0);
let half = from_f64::<T::Real>(0.5);
let factor = from_f64::<T::Real>(0.95);
let small = min_positive::<T::Real>() / eps::<T::Real>();
let large = small.recip();
loop {
let mut changed = false;
for i in lo..hi {
let mut c = zero::<T::Real>();
let mut r = zero::<T::Real>();
for j in lo..hi {
c = c.hypot(&a[(j, i)].abs());
r = r.hypot(&a[(i, j)].abs());
}
if c == zero() || r == zero() || !c.is_finite() || !r.is_finite() {
continue;
}
let mut ca = zero::<T::Real>();
let mut ra = zero::<T::Real>();
for j in 0..n {
ca = ca.fmax(a[(j, i)].abs());
ra = ra.fmax(a[(i, j)].abs());
}
let original = c.mul_pow2(half.copy()) + r.mul_pow2(half.copy());
let mut f = one::<T::Real>();
while c < r.mul_pow2(half.copy())
&& c < large.mul_pow2(half.copy())
&& ca < large.mul_pow2(half.copy())
&& r > &small * &two
&& ra > &small * &two
&& f < large.mul_pow2(half.copy())
{
c *= &two;
r *= ½
ca *= &two;
ra *= ½
f *= &two;
}
while r <= c.mul_pow2(half.copy())
&& r < large.mul_pow2(half.copy())
&& ra < large.mul_pow2(half.copy())
&& c > &small * &two
&& ca > &small * &two
&& f > &small * &two
{
c *= ½
r *= &two;
ca *= ½
ra *= &two;
f *= ½
}
if c.mul_pow2(half.copy()) + r.mul_pow2(half.copy())
>= &factor * &original
{
continue;
}
if (f < one() && scale[i] < &small / &f)
|| (f > one() && scale[i] > &large / &f)
{
continue;
}
scale[i] *= &f;
let inv = f.recip();
for j in 0..n {
if j != i {
a[(i, j)] = a[(i, j)].mul_pow2(inv.copy());
a[(j, i)] = a[(j, i)].mul_pow2(f.copy());
}
}
changed = true;
}
if !changed {
break;
}
}
Ok(lo..hi)
}
pub fn balance_eigenvectors_scratch<T: ComplexField>(dim: usize) -> StackReq {
temp_mat_scratch::<T>(dim, 1)
}
pub fn balance_eigenvectors_in_place<T: ComplexField>(
mut x: MatMut<'_, T>,
perm: &[usize],
scale: &[T::Real],
_par: Par,
stack: &mut MemStack,
) {
let n = x.nrows();
assert!(perm.len() == n && scale.len() == n);
let (mut tmp, _) = temp_mat_zeroed::<T, _, _>(n, 1, stack);
let mut tmp = tmp.as_mat_mut().col_mut(0);
for &i in perm {
assert!(i < n && tmp[i] == zero());
tmp[i] = one();
}
assert!(scale.iter().all(|s| s.is_finite() && *s > zero()));
for j in 0..x.ncols() {
for i in 0..n {
tmp[perm[i]] = x[(i, j)].mul_pow2(scale[i].copy());
}
x.rb_mut().col_mut(j).copy_from(tmp.rb());
}
}