use super::super::types::WhichEigenvalues;
const DENSE_EIG_TOL: f64 = 1e-14;
pub fn tridiagonal_eig(alphas: &[f64], betas: &[f64]) -> (Vec<f64>, Vec<Vec<f64>>) {
let n = alphas.len();
if n == 0 {
return (vec![], vec![]);
}
if n == 1 {
return (vec![alphas[0]], vec![vec![1.0]]);
}
let mut t = vec![vec![0.0f64; n]; n];
for i in 0..n {
t[i][i] = alphas[i];
if i + 1 < n && i < betas.len() {
t[i][i + 1] = betas[i];
t[i + 1][i] = betas[i];
}
}
let mut v = vec![vec![0.0f64; n]; n];
for i in 0..n {
v[i][i] = 1.0;
}
let max_sweeps = 100 * n * n;
for _ in 0..max_sweeps {
let mut max_val = 0.0f64;
let mut p = 0;
let mut q = 1;
for i in 0..n {
for j in (i + 1)..n {
if t[i][j].abs() > max_val {
max_val = t[i][j].abs();
p = i;
q = j;
}
}
}
if max_val < DENSE_EIG_TOL {
break;
}
let theta = if (t[p][p] - t[q][q]).abs() < 1e-15 {
std::f64::consts::FRAC_PI_4
} else {
0.5 * (2.0 * t[p][q] / (t[p][p] - t[q][q])).atan()
};
let c = theta.cos();
let s = theta.sin();
let mut new_t = t.clone();
for i in 0..n {
if i != p && i != q {
new_t[i][p] = c * t[i][p] + s * t[i][q];
new_t[p][i] = new_t[i][p];
new_t[i][q] = -s * t[i][p] + c * t[i][q];
new_t[q][i] = new_t[i][q];
}
}
new_t[p][p] = c * c * t[p][p] + 2.0 * c * s * t[p][q] + s * s * t[q][q];
new_t[q][q] = s * s * t[p][p] - 2.0 * c * s * t[p][q] + c * c * t[q][q];
new_t[p][q] = 0.0;
new_t[q][p] = 0.0;
t = new_t;
for i in 0..n {
let vip = v[i][p];
let viq = v[i][q];
v[i][p] = c * vip + s * viq;
v[i][q] = -s * vip + c * viq;
}
}
let eigenvalues: Vec<f64> = (0..n).map(|i| t[i][i]).collect();
let eigenvectors: Vec<Vec<f64>> = (0..n).map(|i| (0..n).map(|j| v[j][i]).collect()).collect();
(eigenvalues, eigenvectors)
}
pub fn select_eigenvalues(eigenvalues: &[f64], k: usize, which: &WhichEigenvalues) -> Vec<usize> {
let n = eigenvalues.len();
let k = k.min(n);
let mut indices: Vec<usize> = (0..n).collect();
match which {
WhichEigenvalues::LargestMagnitude => {
indices.sort_by(|&a, &b| {
eigenvalues[b]
.abs()
.partial_cmp(&eigenvalues[a].abs())
.unwrap_or(std::cmp::Ordering::Equal)
});
}
WhichEigenvalues::SmallestMagnitude => {
indices.sort_by(|&a, &b| {
eigenvalues[a]
.abs()
.partial_cmp(&eigenvalues[b].abs())
.unwrap_or(std::cmp::Ordering::Equal)
});
}
WhichEigenvalues::LargestReal => {
indices.sort_by(|&a, &b| {
eigenvalues[b]
.partial_cmp(&eigenvalues[a])
.unwrap_or(std::cmp::Ordering::Equal)
});
}
WhichEigenvalues::SmallestReal => {
indices.sort_by(|&a, &b| {
eigenvalues[a]
.partial_cmp(&eigenvalues[b])
.unwrap_or(std::cmp::Ordering::Equal)
});
}
WhichEigenvalues::ClosestTo(sigma) => {
indices.sort_by(|&a, &b| {
let da = (eigenvalues[a] - sigma).abs();
let db = (eigenvalues[b] - sigma).abs();
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
});
}
}
indices.truncate(k);
indices
}
pub fn hessenberg_eig(h: &[Vec<f64>], m: usize) -> (Vec<f64>, Vec<f64>, Vec<Vec<f64>>) {
if m == 0 {
return (vec![], vec![], vec![]);
}
if m == 1 {
return (vec![h[0][0]], vec![0.0], vec![vec![1.0]]);
}
let mut a = vec![vec![0.0f64; m]; m];
for i in 0..m {
for j in 0..m {
if i < h.len() && j < h[i].len() {
a[i][j] = h[i][j];
}
}
}
let mut q = vec![vec![0.0f64; m]; m];
for i in 0..m {
q[i][i] = 1.0;
}
let max_iters = 200 * m;
let mut p = m;
let mut eig_real = vec![0.0f64; m];
let mut eig_imag = vec![0.0f64; m];
for _ in 0..max_iters {
if p <= 1 {
if p == 1 {
eig_real[0] = a[0][0];
}
break;
}
let mut deflated = false;
for i in (1..p).rev() {
let threshold = DENSE_EIG_TOL * (a[i - 1][i - 1].abs() + a[i][i].abs()).max(1e-20);
if a[i][i - 1].abs() < threshold {
a[i][i - 1] = 0.0;
if i == p - 1 {
eig_real[p - 1] = a[p - 1][p - 1];
eig_imag[p - 1] = 0.0;
p -= 1;
deflated = true;
break;
}
}
}
if deflated {
continue;
}
if p >= 2 {
let i = p - 2;
let tr = a[i][i] + a[i + 1][i + 1];
let det = a[i][i] * a[i + 1][i + 1] - a[i][i + 1] * a[i + 1][i];
let disc = tr * tr - 4.0 * det;
if disc < 0.0 && (p < 3 || a[i][i - 1].abs() < DENSE_EIG_TOL) {
eig_real[p - 2] = tr / 2.0;
eig_real[p - 1] = tr / 2.0;
eig_imag[p - 2] = (-disc).sqrt() / 2.0;
eig_imag[p - 1] = -(-disc).sqrt() / 2.0;
p -= 2;
continue;
}
}
let shift = wilkinson_shift(&a, p);
for i in 0..p {
a[i][i] -= shift;
}
for i in 0..p - 1 {
let (c, s, _r) = super::super::helpers::givens_rotation(a[i][i], a[i + 1][i]);
for j in 0..m {
let t1 = a[i][j];
let t2 = a[i + 1][j];
a[i][j] = c * t1 + s * t2;
a[i + 1][j] = -s * t1 + c * t2;
}
for j in 0..p.min(i + 3) {
let t1 = a[j][i];
let t2 = a[j][i + 1];
a[j][i] = c * t1 + s * t2;
a[j][i + 1] = -s * t1 + c * t2;
}
for j in 0..m {
let t1 = q[j][i];
let t2 = q[j][i + 1];
q[j][i] = c * t1 + s * t2;
q[j][i + 1] = -s * t1 + c * t2;
}
}
for i in 0..p {
a[i][i] += shift;
}
}
for i in 0..p {
eig_real[i] = a[i][i];
if i + 1 < p && a[i + 1][i].abs() > DENSE_EIG_TOL {
let tr = a[i][i] + a[i + 1][i + 1];
let det = a[i][i] * a[i + 1][i + 1] - a[i][i + 1] * a[i + 1][i];
let disc = tr * tr - 4.0 * det;
if disc < 0.0 {
eig_real[i] = tr / 2.0;
eig_real[i + 1] = tr / 2.0;
eig_imag[i] = (-disc).sqrt() / 2.0;
eig_imag[i + 1] = -(-disc).sqrt() / 2.0;
}
}
}
let eigenvectors: Vec<Vec<f64>> = (0..m).map(|i| (0..m).map(|j| q[j][i]).collect()).collect();
(eig_real, eig_imag, eigenvectors)
}
fn wilkinson_shift(a: &[Vec<f64>], p: usize) -> f64 {
if p < 2 {
return a[p - 1][p - 1];
}
let i = p - 2;
let tr = a[i][i] + a[i + 1][i + 1];
let det = a[i][i] * a[i + 1][i + 1] - a[i][i + 1] * a[i + 1][i];
let disc = tr * tr - 4.0 * det;
if disc >= 0.0 {
let s1 = (tr + disc.sqrt()) / 2.0;
let s2 = (tr - disc.sqrt()) / 2.0;
if (s1 - a[p - 1][p - 1]).abs() < (s2 - a[p - 1][p - 1]).abs() {
s1
} else {
s2
}
} else {
a[p - 1][p - 1]
}
}
pub fn select_eigenvalues_complex(
real: &[f64],
imag: &[f64],
k: usize,
which: &WhichEigenvalues,
) -> Vec<usize> {
let n = real.len();
let k = k.min(n);
let mut indices: Vec<usize> = (0..n).collect();
match which {
WhichEigenvalues::LargestMagnitude => {
indices.sort_by(|&a, &b| {
let ma = real[a] * real[a] + imag[a] * imag[a];
let mb = real[b] * real[b] + imag[b] * imag[b];
mb.partial_cmp(&ma).unwrap_or(std::cmp::Ordering::Equal)
});
}
WhichEigenvalues::SmallestMagnitude => {
indices.sort_by(|&a, &b| {
let ma = real[a] * real[a] + imag[a] * imag[a];
let mb = real[b] * real[b] + imag[b] * imag[b];
ma.partial_cmp(&mb).unwrap_or(std::cmp::Ordering::Equal)
});
}
WhichEigenvalues::LargestReal => {
indices.sort_by(|&a, &b| {
real[b]
.partial_cmp(&real[a])
.unwrap_or(std::cmp::Ordering::Equal)
});
}
WhichEigenvalues::SmallestReal => {
indices.sort_by(|&a, &b| {
real[a]
.partial_cmp(&real[b])
.unwrap_or(std::cmp::Ordering::Equal)
});
}
WhichEigenvalues::ClosestTo(sigma) => {
indices.sort_by(|&a, &b| {
let da = (real[a] - sigma) * (real[a] - sigma) + imag[a] * imag[a];
let db = (real[b] - sigma) * (real[b] - sigma) + imag[b] * imag[b];
da.partial_cmp(&db).unwrap_or(std::cmp::Ordering::Equal)
});
}
}
indices.truncate(k);
indices
}