use super::LapackError;
use super::auxiliary::{dlae2, dlaev2, dlapy2};
use super::eig::{dlarfg, dlartg};
#[derive(Clone, Copy, PartialEq, Eq)]
enum Triangle {
Full,
Upper,
Lower,
}
pub fn dsyev(a: &mut [f64], w: &mut [f64], n: usize, upper: bool) -> Result<(), LapackError> {
if a.len() != n * n || w.len() != n {
return Err(LapackError::BadShape);
}
if n == 0 {
return Ok(());
}
if n == 1 {
w[0] = a[0];
a[0] = 1.0;
return Ok(());
}
let safmin = f64::MIN_POSITIVE;
let eps = f64::EPSILON / 2.0;
let smlnum = safmin / eps;
let bignum = 1.0 / smlnum;
let rmin = smlnum.sqrt();
let rmax = bignum.sqrt();
let mut anrm = 0.0f64;
for j in 0..n {
let (i0, i1) = if upper { (0, j + 1) } else { (j, n) };
for i in i0..i1 {
let t = a[i + j * n].abs();
if t > anrm {
anrm = t;
}
}
}
let mut iscale = 0i32;
let mut sigma = 1.0f64;
if anrm > 0.0 && anrm < rmin {
iscale = 1;
sigma = rmin / anrm;
} else if anrm > rmax {
iscale = 1;
sigma = rmax / anrm;
}
if iscale == 1 {
let tri = if upper { Triangle::Upper } else { Triangle::Lower };
dlascl(1.0, sigma, a, n, n, n, tri);
}
let mut d = vec![0.0; n];
let mut e = vec![0.0; n - 1];
let mut tau = vec![0.0; n - 1];
if n >= 128 {
dsytrd(upper, n, a, &mut d, &mut e, &mut tau);
} else {
dsytd2(upper, n, a, &mut d, &mut e, &mut tau);
}
dorgtr(upper, n, a, n, &tau);
let mut work = vec![0.0; 2 * n - 2];
let info = dsteqr(1, n, &mut d, &mut e, a, n, &mut work);
if info != 0 {
return Err(LapackError::Convergence);
}
if iscale == 1 {
let factor = 1.0 / sigma;
for v in d.iter_mut() {
*v *= factor;
}
}
w.copy_from_slice(&d);
Ok(())
}
pub fn dsytrd(upper: bool, n: usize, a: &mut [f64], d: &mut [f64], e: &mut [f64], tau: &mut [f64]) -> usize {
if n == 0 {
return 0;
}
const NB_ILAENV: usize = 32;
const NX_ILAENV: usize = 32;
let nb = if NB_ILAENV > 1 && NB_ILAENV < n { NB_ILAENV } else { 1 };
let nx = if nb > 1 { NB_ILAENV.max(NX_ILAENV) } else { n };
let mut w = vec![0.0; n * nb];
if upper {
let kk = n - (n - nx).div_ceil(nb) * nb;
let mut i_fortran = n - nb + 1;
while i_fortran > kk {
let i0 = i_fortran - 1;
dlatrd(true, i0 + nb, nb, a, n, 0, e, 0, tau, 0, &mut w, n);
dsyr2k(true, i0, nb, -1.0, a, n, i0 * n, &w, n, 0, 1.0, n, 0);
for j in i0..i0 + nb {
a[(j - 1) + j * n] = e[j - 1];
d[j] = a[j + j * n];
}
match i_fortran.checked_sub(nb) {
Some(next) => i_fortran = next,
None => break,
}
}
let mut block = vec![0.0; kk * kk];
for j in 0..kk {
for i in 0..kk {
block[i + j * kk] = a[i + j * n];
}
}
let ne = kk.min(e.len());
let nt = kk.min(tau.len());
dsytd2(true, kk, &mut block, &mut d[..kk], &mut e[..ne], &mut tau[..nt]);
for j in 0..kk {
for i in 0..kk {
a[i + j * n] = block[i + j * kk];
}
}
} else {
let mut i0 = 0usize;
while i0 < n - nx {
dlatrd(false, n - i0, nb, a, n, i0 + i0 * n, e, i0, tau, i0, &mut w, n);
dsyr2k(
false,
n - i0 - nb,
nb,
-1.0,
a,
n,
(i0 + nb) + i0 * n,
&w,
n,
nb,
1.0,
n,
(i0 + nb) + (i0 + nb) * n,
);
for j in i0..i0 + nb {
a[(j + 1) + j * n] = e[j];
d[j] = a[j + j * n];
}
i0 += nb;
}
let nr = n - i0;
let mut block = vec![0.0; nr * nr];
for j in 0..nr {
for i in 0..nr {
block[i + j * nr] = a[(i0 + i) + (i0 + j) * n];
}
}
dsytd2(false, nr, &mut block, &mut d[i0..], &mut e[i0..], &mut tau[i0..]);
for j in 0..nr {
for i in 0..nr {
a[(i0 + i) + (i0 + j) * n] = block[i + j * nr];
}
}
}
0
}
fn dsytd2(upper: bool, n: usize, a: &mut [f64], d: &mut [f64], e: &mut [f64], tau: &mut [f64]) {
if n == 0 {
return;
}
if upper {
for i in (0..n - 1).rev() {
let len = i + 1; let alpha = a[i + (i + 1) * n];
let x: Vec<f64> = (0..i).map(|t| a[t + (i + 1) * n]).collect();
let (beta, taui, v) = dlarfg(alpha, &x);
e[i] = beta;
for t in 0..i {
a[t + (i + 1) * n] = v[t + 1];
}
if taui != 0.0 {
a[i + (i + 1) * n] = 1.0;
let mut vnat = vec![0.0; len];
for t in 0..i {
vnat[t] = a[t + (i + 1) * n];
}
vnat[i] = 1.0;
let mut work = vec![0.0; len];
for r in 0..len {
let mut s = a[r + r * n] * vnat[r];
for c in 0..r {
s += a[c + r * n] * vnat[c];
}
for c in (r + 1)..len {
s += a[r + c * n] * vnat[c];
}
work[r] = taui * s;
}
let mut alpha2 = 0.0;
for r in 0..len {
alpha2 += work[r] * vnat[r];
}
alpha2 *= -0.5 * taui;
for r in 0..len {
work[r] += alpha2 * vnat[r];
}
for c in 0..len {
for r in 0..=c {
a[r + c * n] += -(vnat[r] * work[c] + work[r] * vnat[c]);
}
}
a[i + (i + 1) * n] = e[i];
}
d[i + 1] = a[(i + 1) + (i + 1) * n];
tau[i] = taui;
}
d[0] = a[0];
} else {
for i in 0..n - 1 {
let len = n - i - 1; let alpha = a[(i + 1) + i * n];
let tail_first = i + 2;
let x: Vec<f64> = if tail_first < n {
(tail_first..n).map(|t| a[t + i * n]).collect()
} else {
Vec::new()
};
let (beta, taui, v) = dlarfg(alpha, &x);
e[i] = beta;
for t in tail_first..n {
a[t + i * n] = v[t - tail_first + 1];
}
if taui != 0.0 {
a[(i + 1) + i * n] = 1.0;
let mut vnat = vec![0.0; len];
vnat[0] = 1.0;
for t in 1..len {
vnat[t] = a[(i + 1 + t) + i * n];
}
let mut work = vec![0.0; len];
for r in 0..len {
let base = i + 1;
let mut s = a[(base + r) + (base + r) * n] * vnat[r];
for c in 0..r {
s += a[(base + r) + (base + c) * n] * vnat[c];
}
for c in (r + 1)..len {
s += a[(base + c) + (base + r) * n] * vnat[c];
}
work[r] = taui * s;
}
let mut alpha2 = 0.0;
for r in 0..len {
alpha2 += work[r] * vnat[r];
}
alpha2 *= -0.5 * taui;
for r in 0..len {
work[r] += alpha2 * vnat[r];
}
for c in 0..len {
for r in c..len {
a[(i + 1 + r) + (i + 1 + c) * n] += -(vnat[r] * work[c] + work[r] * vnat[c]);
}
}
a[(i + 1) + i * n] = e[i];
}
d[i] = a[i + i * n];
tau[i] = taui;
}
d[n - 1] = a[(n - 1) + (n - 1) * n];
}
}
#[allow(clippy::too_many_arguments)]
fn dlatrd(
upper: bool,
n: usize,
nb: usize,
a: &mut [f64],
lda: usize,
a_off: usize,
e: &mut [f64],
e0: usize,
tau: &mut [f64],
t0: usize,
w: &mut [f64],
ldw: usize,
) {
if n == 0 {
return;
}
if upper {
for i1 in (n - nb + 1..=n).rev() {
let iw = i1 + nb - n; if i1 < n {
let m = i1;
let k = n - i1;
let y_col = a_off + (i1 - 1) * lda;
let mut y = vec![0.0; m];
y.copy_from_slice(&a[y_col..y_col + m]);
let mut x = vec![0.0; k];
for r in 0..k {
x[r] = w[(i1 - 1) + (iw + r) * ldw];
}
gemv_n(-1.0, a, lda, a_off + i1 * lda, &x, 1.0, &mut y, m, k);
for r in 0..k {
x[r] = a[a_off + (i1 - 1) + (i1 + r) * lda];
}
gemv_n(-1.0, w, ldw, iw * ldw, &x, 1.0, &mut y, m, k);
a[y_col..y_col + m].copy_from_slice(&y[..m]);
}
if i1 > 1 {
let len = i1 - 1; let alpha = a[a_off + (i1 - 2) + (i1 - 1) * lda];
let xt: Vec<f64> = (0..len - 1).map(|t| a[a_off + (i1 - 1) * lda + t]).collect();
let (beta, taui, v) = dlarfg(alpha, &xt);
e[e0 + i1 - 2] = beta;
tau[t0 + i1 - 2] = taui;
for t in 0..len - 1 {
a[a_off + (i1 - 1) * lda + t] = v[t + 1];
}
a[a_off + (i1 - 2) + (i1 - 1) * lda] = 1.0;
let x: Vec<f64> = (0..len).map(|t| a[a_off + (i1 - 1) * lda + t]).collect();
let mut y = vec![0.0; len];
dsymv_up(1.0, a, lda, a_off, &x, 0.0, &mut y, len);
if i1 < n {
let k = n - i1;
let mut z = vec![0.0; k];
gemv_t(1.0, w, ldw, iw * ldw, &x, 0.0, &mut z, len, k);
gemv_n(-1.0, a, lda, a_off + i1 * lda, &z, 1.0, &mut y, len, k);
gemv_t(1.0, a, lda, a_off + i1 * lda, &x, 0.0, &mut z, len, k);
gemv_n(-1.0, w, ldw, iw * ldw, &z, 1.0, &mut y, len, k);
for r in 0..k {
w[(i1 + r) + (iw - 1) * ldw] = z[r];
}
}
for r in 0..len {
y[r] *= taui;
}
let mut alpha = 0.0;
for r in 0..len {
alpha += y[r] * x[r];
}
alpha *= -0.5 * taui;
for r in 0..len {
y[r] += alpha * x[r];
}
for r in 0..len {
w[r + (iw - 1) * ldw] = y[r];
}
}
}
} else {
for i1 in 1..=nb {
let m = n - i1 + 1;
let k = i1 - 1;
let y_col = a_off + (i1 - 1) + (i1 - 1) * lda;
let mut y = vec![0.0; m];
y.copy_from_slice(&a[y_col..y_col + m]);
if k > 0 {
let x: Vec<f64> = (0..k).map(|t| w[(i1 - 1) + t * ldw]).collect();
gemv_n(-1.0, a, lda, a_off + (i1 - 1), &x, 1.0, &mut y, m, k);
let x: Vec<f64> = (0..k).map(|t| a[a_off + (i1 - 1) + t * lda]).collect();
gemv_n(-1.0, w, ldw, i1 - 1, &x, 1.0, &mut y, m, k);
}
a[y_col..y_col + m].copy_from_slice(&y[..m]);
if i1 < n {
let len = n - i1; let alpha = a[a_off + i1 + (i1 - 1) * lda];
let xt: Vec<f64> = (0..len - 1).map(|t| a[a_off + (i1 + 1 + t) + (i1 - 1) * lda]).collect();
let (beta, taui, v) = dlarfg(alpha, &xt);
e[e0 + i1 - 1] = beta;
tau[t0 + i1 - 1] = taui;
for t in 0..len - 1 {
a[a_off + (i1 + 1 + t) + (i1 - 1) * lda] = v[t + 1];
}
a[a_off + i1 + (i1 - 1) * lda] = 1.0;
let x: Vec<f64> = (0..len).map(|t| a[a_off + (i1 + t) + (i1 - 1) * lda]).collect();
let mut y = vec![0.0; len];
dsymv_lo(1.0, a, lda, a_off + i1 + i1 * lda, &x, 0.0, &mut y, len);
if k > 0 {
let mut z = vec![0.0; k];
gemv_t(1.0, w, ldw, i1, &x, 0.0, &mut z, len, k);
gemv_n(-1.0, a, lda, a_off + i1, &z, 1.0, &mut y, len, k);
gemv_t(1.0, a, lda, a_off + i1, &x, 0.0, &mut z, len, k);
gemv_n(-1.0, w, ldw, i1, &z, 1.0, &mut y, len, k);
for r in 0..k {
w[r + (i1 - 1) * ldw] = z[r];
}
}
for r in 0..len {
y[r] *= taui;
}
let mut alpha = 0.0;
for r in 0..len {
alpha += y[r] * x[r];
}
alpha *= -0.5 * taui;
for r in 0..len {
y[r] += alpha * x[r];
}
for r in 0..len {
w[(i1 + r) + (i1 - 1) * ldw] = y[r];
}
}
}
}
}
#[allow(clippy::too_many_arguments)]
fn dsyr2k(
upper: bool,
m: usize,
k: usize,
alpha: f64,
data: &mut [f64],
lv: usize,
vo: usize,
w: &[f64],
lw: usize,
wo: usize,
beta: f64,
lc: usize,
co: usize,
) {
if m == 0 || k == 0 {
if alpha == 0.0 {
scale_triangle(upper, data, lc, co, m, beta);
}
return;
}
let mut vc = vec![0.0; m * k];
let mut wc = vec![0.0; m * k];
let mut vt = vec![0.0; k * m];
let mut wt = vec![0.0; k * m];
for l in 0..k {
for i in 0..m {
let x = data[vo + i + l * lv];
let y = w[wo + i + l * lw];
vc[i + l * m] = x;
wc[i + l * m] = y;
vt[l + i * k] = x;
wt[l + i * k] = y;
}
}
let mut t = vec![0.0; m * m];
crate::blas::dgemm(alpha, &vc, &wt, 0.0, &mut t, m, m, k);
crate::blas::dgemm(alpha, &wc, &vt, 1.0, &mut t, m, m, k);
if upper {
for j in 0..m {
let cj = co + j * lc;
for i in 0..=j {
let v = t[i + j * m];
data[cj + i] = if beta == 0.0 { v } else { beta * data[cj + i] + v };
}
}
} else {
for j in 0..m {
let cj = co + j * lc;
for i in j..m {
let v = t[i + j * m];
data[cj + i] = if beta == 0.0 { v } else { beta * data[cj + i] + v };
}
}
}
}
fn scale_triangle(upper: bool, c: &mut [f64], lc: usize, co: usize, m: usize, beta: f64) {
if beta == 1.0 {
return;
}
for j in 0..m {
let cj = co + j * lc;
let (i0, i1) = if upper { (0, j + 1) } else { (j, m) };
for i in i0..i1 {
if beta == 0.0 {
c[cj + i] = 0.0;
} else {
c[cj + i] *= beta;
}
}
}
}
fn gemv_n(alpha: f64, mat: &[f64], lda: usize, a_off: usize, x: &[f64], beta: f64, y: &mut [f64], m: usize, n: usize) {
if beta == 0.0 {
y[..m].fill(0.0);
} else if beta != 1.0 {
for v in &mut y[..m] {
*v *= beta;
}
}
for j in 0..n {
let xj = x[j];
if xj == 0.0 {
continue;
}
let c = alpha * xj;
let col = a_off + j * lda;
for i in 0..m {
y[i] += c * mat[col + i];
}
}
}
fn gemv_t(alpha: f64, mat: &[f64], lda: usize, a_off: usize, x: &[f64], beta: f64, y: &mut [f64], m: usize, n: usize) {
for j in 0..n {
let col = a_off + j * lda;
let mut s = 0.0;
for i in 0..m {
s += mat[col + i] * x[i];
}
y[j] = if beta == 0.0 { alpha * s } else { alpha * s + beta * y[j] };
}
}
fn dsymv_up(alpha: f64, mat: &[f64], lda: usize, a_off: usize, x: &[f64], beta: f64, y: &mut [f64], m: usize) {
scale_y(beta, y, m);
for j in 0..m {
let col = a_off + j * lda;
let t1 = alpha * x[j];
let mut t2 = 0.0;
for i in 0..j {
y[i] += t1 * mat[col + i];
t2 += mat[col + i] * x[i];
}
y[j] += t1 * mat[col + j] + alpha * t2;
}
}
fn dsymv_lo(alpha: f64, mat: &[f64], lda: usize, a_off: usize, x: &[f64], beta: f64, y: &mut [f64], m: usize) {
scale_y(beta, y, m);
for j in 0..m {
let col = a_off + j * lda;
let t1 = alpha * x[j];
let mut t2 = 0.0;
for i in (j + 1)..m {
y[i] += t1 * mat[col + i];
t2 += mat[col + i] * x[i];
}
y[j] += t1 * mat[col + j] + alpha * t2;
}
}
fn scale_y(beta: f64, y: &mut [f64], m: usize) {
if beta == 0.0 {
y[..m].fill(0.0);
} else if beta != 1.0 {
for v in &mut y[..m] {
*v *= beta;
}
}
}
fn dorgtr(upper: bool, n: usize, a: &mut [f64], lda: usize, tau: &[f64]) {
if n == 0 {
return;
}
if upper {
for j in 0..n - 1 {
for i in 0..j {
a[i + j * lda] = a[i + (j + 1) * lda];
}
a[(n - 1) + j * lda] = 0.0;
}
for i in 0..n - 1 {
a[i + (n - 1) * lda] = 0.0;
}
a[(n - 1) + (n - 1) * lda] = 1.0;
dorg2l(n - 1, n - 1, n - 1, a, lda, tau);
} else {
for j in (1..n).rev() {
a[j * lda] = 0.0;
for i in (j + 1)..n {
a[i + j * lda] = a[i + (j - 1) * lda];
}
}
a[0] = 1.0;
for i in 1..n {
a[i] = 0.0;
}
dorg2r(n - 1, n - 1, n - 1, a, lda, tau, 1, 1);
}
}
fn dorg2l(m: usize, ncols: usize, k: usize, a: &mut [f64], lda: usize, tau: &[f64]) {
if ncols == 0 {
return;
}
for j in 0..(ncols - k) {
for l in 0..m {
a[l + j * lda] = 0.0;
}
a[(m - ncols + j) + j * lda] = 1.0;
}
for i in 0..k {
let ii = ncols - k + i;
let mrows = m - ncols + ii + 1; if mrows == 0 {
continue;
}
let mut v = vec![0.0; mrows];
for t in 0..mrows - 1 {
v[t] = a[t + ii * lda];
}
v[mrows - 1] = 1.0;
hh_left(a, lda, 0, mrows, 0, ii, &v, tau[i]);
for t in 0..mrows - 1 {
a[t + ii * lda] *= -tau[i];
}
a[(mrows - 1) + ii * lda] = 1.0 - tau[i];
for l in mrows..m {
a[l + ii * lda] = 0.0;
}
}
}
fn dorg2r(m: usize, ncols: usize, k: usize, a: &mut [f64], lda: usize, tau: &[f64], r0: usize, c0: usize) {
if ncols == 0 {
return;
}
for j in k..ncols {
for l in 0..m {
a[(r0 + l) + (c0 + j) * lda] = 0.0;
}
a[(r0 + j) + (c0 + j) * lda] = 1.0;
}
for i in (0..k).rev() {
if i < ncols - 1 {
let mrows = m - i; let ncolc = ncols - i - 1;
let mut v = vec![0.0; mrows];
v[0] = 1.0;
for t in 1..mrows {
v[t] = a[(r0 + i + t) + (c0 + i) * lda];
}
hh_left(a, lda, r0 + i, mrows, c0 + i + 1, ncolc, &v, tau[i]);
}
if i < m - 1 {
for t in 1..m - i {
a[(r0 + i + t) + (c0 + i) * lda] *= -tau[i];
}
}
a[(r0 + i) + (c0 + i) * lda] = 1.0 - tau[i];
for l in 0..i {
a[(r0 + l) + (c0 + i) * lda] = 0.0;
}
}
}
fn hh_left(a: &mut [f64], lda: usize, r0: usize, m: usize, c0: usize, ncols: usize, v: &[f64], tau: f64) {
if tau == 0.0 || m == 0 || ncols == 0 {
return;
}
let mut w = vec![0.0; ncols];
for (j, wj) in w.iter_mut().enumerate() {
let mut s = 0.0;
for i in 0..m {
s += a[(r0 + i) + (c0 + j) * lda] * v[i];
}
*wj = s;
}
for (j, &wj) in w.iter().enumerate() {
for i in 0..m {
a[(r0 + i) + (c0 + j) * lda] -= tau * v[i] * wj;
}
}
}
fn dsteqr(icompz: i32, n: usize, d: &mut [f64], e: &mut [f64], z: &mut [f64], ldz: usize, work: &mut [f64]) -> usize {
let mut info = 0usize;
if n == 0 {
return 0;
}
if n == 1 {
if icompz == 2 {
z[0] = 1.0;
}
return 0;
}
const MAXIT: usize = 30;
let eps = f64::EPSILON / 2.0;
let eps2 = eps * eps;
let safmin = f64::MIN_POSITIVE;
let safmax = 1.0 / safmin;
let ssfmax = safmax.sqrt() / 3.0;
let ssfmin = safmin.sqrt() / eps2;
if icompz == 2 {
for j in 0..n {
for i in 0..n {
z[i + j * ldz] = if i == j { 1.0 } else { 0.0 };
}
}
}
let nmaxit = n * MAXIT;
let mut jtot = 0usize;
let mut l1 = 0usize; let nm1 = n - 1;
'outer: loop {
if l1 >= n {
break 'outer;
}
if l1 > 0 {
e[l1 - 1] = 0.0;
}
let mut m = n - 1;
if l1 < nm1 {
for mm in l1..nm1 {
let tst = e[mm].abs();
if tst == 0.0 {
m = mm;
break;
}
if tst <= (d[mm].abs().sqrt() * d[mm + 1].abs().sqrt()) * eps {
e[mm] = 0.0;
m = mm;
break;
}
}
}
let mut l = l1;
let lsv = l;
let mut lend = m;
let lendsv = lend;
l1 = m + 1;
if lend == l {
continue 'outer;
}
let mut anorm = 0.0f64;
for i in l..=lend {
let t = d[i].abs();
if t > anorm {
anorm = t;
}
}
for i in l..lend {
let t = e[i].abs();
if t > anorm {
anorm = t;
}
}
let mut iscale = 0i32;
if anorm == 0.0 {
continue 'outer;
}
if anorm > ssfmax {
iscale = 1;
let len = lend - l + 1;
dlascl(anorm, ssfmax, &mut d[l..=lend], len, 1, len, Triangle::Full);
if lend > l {
let len_e = lend - l;
dlascl(anorm, ssfmax, &mut e[l..lend], len_e, 1, len_e, Triangle::Full);
}
} else if anorm < ssfmin {
iscale = 2;
let len = lend - l + 1;
dlascl(anorm, ssfmin, &mut d[l..=lend], len, 1, len, Triangle::Full);
if lend > l {
let len_e = lend - l;
dlascl(anorm, ssfmin, &mut e[l..lend], len_e, 1, len_e, Triangle::Full);
}
}
if d[lend].abs() < d[l].abs() {
lend = lsv;
l = lendsv;
}
if lend > l {
'ql: loop {
let mut m = lend;
if l != lend {
let lendm1 = lend - 1;
for mm in l..=lendm1 {
let tst = e[mm] * e[mm];
if tst <= (eps2 * d[mm].abs()) * d[mm + 1].abs() + safmin {
m = mm;
break;
}
}
}
if m < lend {
e[m] = 0.0;
}
let p = d[l];
if m == l {
d[l] = p;
l += 1;
if l <= lend {
continue 'ql;
}
break 'ql;
}
if m == l + 1 {
let (rt1, rt2, c, s) = if icompz > 0 {
dlaev2(d[l], e[l], d[l + 1])
} else {
let (rt1, rt2) = dlae2(d[l], e[l], d[l + 1]);
(rt1, rt2, 0.0, 0.0)
};
if icompz > 0 {
work[l] = c;
work[n - 1 + l] = s;
let nc = 1; dlasr_v(n, 2, &work[l..l + nc], &work[n - 1 + l..n + l], z, ldz, l, true);
}
d[l] = rt1;
d[l + 1] = rt2;
e[l] = 0.0;
l += 2;
if l <= lend {
continue 'ql;
}
break 'ql;
}
if jtot == nmaxit {
break 'ql;
}
jtot += 1;
let mut g = (d[l + 1] - p) / (2.0 * e[l]);
let mut r = dlapy2(g, 1.0);
g = d[m] - p + (e[l] / (g + r.abs() * g.signum()));
let mut s = 1.0;
let mut c = 1.0;
let mut p = 0.0;
let mm1 = m - 1;
for i in (l..=mm1).rev() {
let f = s * e[i];
let b = c * e[i];
let (cc, ss, rr) = dlartg(g, f);
c = cc;
s = ss;
if i != m - 1 {
e[i + 1] = rr;
}
g = d[i + 1] - p;
r = (d[i] - g) * s + 2.0 * c * b;
p = s * r;
d[i + 1] = g + p;
g = c * r - b;
if icompz > 0 {
work[i] = c;
work[n - 1 + i] = -s;
}
}
if icompz > 0 {
let mm = m - l + 1;
let nc = mm - 1;
dlasr_v(n, mm, &work[l..l + nc], &work[n - 1 + l..n - 1 + l + nc], z, ldz, l, true);
}
d[l] -= p;
e[l] = g;
}
} else {
'qr: loop {
let mut m = lend;
if l != lend {
for mm in ((lend + 1)..=l).rev() {
let tst = e[mm - 1] * e[mm - 1];
if tst <= (eps2 * d[mm].abs()) * d[mm - 1].abs() + safmin {
m = mm;
break;
}
}
}
if m > lend {
e[m - 1] = 0.0;
}
let p = d[l];
if m == l {
d[l] = p;
if l == 0 {
break 'qr;
}
l -= 1;
if l >= lend {
continue 'qr;
}
break 'qr;
}
if l > 0 && m == l - 1 {
let (rt1, rt2, c, s) = if icompz > 0 {
dlaev2(d[l - 1], e[l - 1], d[l])
} else {
let (rt1, rt2) = dlae2(d[l - 1], e[l - 1], d[l]);
(rt1, rt2, 0.0, 0.0)
};
if icompz > 0 {
work[m] = c;
work[n - 1 + m] = s;
let nc = 1;
dlasr_v(n, 2, &work[m..m + nc], &work[n - 1 + m..n + m], z, ldz, l - 1, false);
}
d[l - 1] = rt1;
d[l] = rt2;
e[l - 1] = 0.0;
if l < 2 {
break 'qr;
}
l -= 2;
if l >= lend {
continue 'qr;
}
break 'qr;
}
if jtot == nmaxit {
break 'qr;
}
jtot += 1;
let mut g = (d[l - 1] - p) / (2.0 * e[l - 1]);
let mut r = dlapy2(g, 1.0);
g = d[m] - p + (e[l - 1] / (g + r.abs() * g.signum()));
let mut s = 1.0;
let mut c = 1.0;
let mut p = 0.0;
let lm1 = l - 1;
for i in m..=lm1 {
let f = s * e[i];
let b = c * e[i];
let (cc, ss, rr) = dlartg(g, f);
c = cc;
s = ss;
if i != m {
e[i - 1] = rr;
}
g = d[i] - p;
r = (d[i + 1] - g) * s + 2.0 * c * b;
p = s * r;
d[i] = g + p;
g = c * r - b;
if icompz > 0 {
work[i] = c;
work[n - 1 + i] = s;
}
}
if icompz > 0 {
let mm = l - m + 1;
let nc = mm - 1;
dlasr_v(n, mm, &work[m..m + nc], &work[n - 1 + m..n - 1 + m + nc], z, ldz, m, false);
}
d[l] -= p;
e[lm1] = g;
}
}
if iscale == 1 {
let len = lendsv - lsv + 1;
dlascl(ssfmax, anorm, &mut d[lsv..=lendsv], len, 1, len, Triangle::Full);
if lendsv > lsv {
let len_e = lendsv - lsv;
dlascl(ssfmax, anorm, &mut e[lsv..lendsv], len_e, 1, len_e, Triangle::Full);
}
} else if iscale == 2 {
let len = lendsv - lsv + 1;
dlascl(ssfmin, anorm, &mut d[lsv..=lendsv], len, 1, len, Triangle::Full);
if lendsv > lsv {
let len_e = lendsv - lsv;
dlascl(ssfmin, anorm, &mut e[lsv..lendsv], len_e, 1, len_e, Triangle::Full);
}
}
if jtot < nmaxit {
continue 'outer;
}
for i in 0..n - 1 {
if e[i] != 0.0 {
info += 1;
}
}
return info;
}
if icompz == 0 {
for i in 0..n - 1 {
let mut k = i;
let mut p = d[i];
for j in (i + 1)..n {
if d[j] < p {
k = j;
p = d[j];
}
}
if k != i {
d[k] = d[i];
d[i] = p;
}
}
} else {
for i in 0..n - 1 {
let mut k = i;
let mut p = d[i];
for j in (i + 1)..n {
if d[j] < p {
k = j;
p = d[j];
}
}
if k != i {
d[k] = d[i];
d[i] = p;
for row in 0..n {
z.swap(row + i * ldz, row + k * ldz);
}
}
}
}
0
}
fn dlasr_v(m: usize, ncols: usize, c: &[f64], s: &[f64], a: &mut [f64], lda: usize, col0: usize, backward: bool) {
if m == 0 || ncols < 2 {
return;
}
debug_assert!(c.len() >= ncols - 1 && s.len() >= ncols - 1);
if backward {
for j in (1..ncols).rev() {
let ctemp = c[j - 1];
let stemp = s[j - 1];
if ctemp != 1.0 || stemp != 0.0 {
for i in 0..m {
let temp = a[i + (col0 + j) * lda];
a[i + (col0 + j) * lda] = ctemp * temp - stemp * a[i + (col0 + j - 1) * lda];
a[i + (col0 + j - 1) * lda] = stemp * temp + ctemp * a[i + (col0 + j - 1) * lda];
}
}
}
} else {
for j in 1..ncols {
let ctemp = c[j - 1];
let stemp = s[j - 1];
if ctemp != 1.0 || stemp != 0.0 {
for i in 0..m {
let temp = a[i + (col0 + j) * lda];
a[i + (col0 + j) * lda] = ctemp * temp - stemp * a[i + (col0 + j - 1) * lda];
a[i + (col0 + j - 1) * lda] = stemp * temp + ctemp * a[i + (col0 + j - 1) * lda];
}
}
}
}
}
fn dlascl(cfrom: f64, cto: f64, a: &mut [f64], m: usize, ncols: usize, lda: usize, tri: Triangle) {
if m == 0 || ncols == 0 {
return;
}
let smlnum = f64::MIN_POSITIVE;
let bignum = 1.0 / smlnum;
let mut cfromc = cfrom;
let mut ctoc = cto;
loop {
let done;
let mul;
let cfrom1 = cfromc * smlnum;
if cfrom1 == cfromc {
mul = ctoc / cfromc;
done = true;
} else {
let cto1 = ctoc / bignum;
if cto1 == ctoc {
mul = ctoc;
done = true;
cfromc = 1.0;
} else if cfrom1.abs() > ctoc.abs() && ctoc != 0.0 {
mul = smlnum;
done = false;
cfromc = cfrom1;
} else if cto1.abs() > cfromc.abs() {
mul = bignum;
done = false;
ctoc = cto1;
} else {
mul = ctoc / cfromc;
done = true;
if mul == 1.0 {
return;
}
}
}
match tri {
Triangle::Full => {
for j in 0..ncols {
for i in 0..m {
a[i + j * lda] *= mul;
}
}
}
Triangle::Lower => {
for j in 0..ncols {
for i in j..m {
a[i + j * lda] *= mul;
}
}
}
Triangle::Upper => {
for j in 0..ncols {
for i in 0..=j.min(m - 1) {
a[i + j * lda] *= mul;
}
}
}
}
if done {
return;
}
}
}
#[cfg(test)]
mod tests {
use super::*;
fn symmetric_data(n: usize) -> Vec<f64> {
let mut a = vec![0.0; n * n];
for i in 0..n {
for j in 0..n {
let v = ((i as f64) * 0.7 + (j as f64) * 1.3).sin() + 0.3 * ((i * j) as f64).cos();
a[i + j * n] = v;
a[j + i * n] = v;
}
}
a
}
fn tridiagonal_data(d: &[f64], e: &[f64], n: usize) -> Vec<f64> {
let mut a = vec![0.0; n * n];
for i in 0..n {
a[i + i * n] = d[i];
if i + 1 < n {
a[i + (i + 1) * n] = e[i];
a[(i + 1) + i * n] = e[i];
}
}
a
}
fn tridiagonal_sample(n: usize) -> (Vec<f64>, Vec<f64>) {
let d = (0..n).map(|i| 1.5 * (i as f64) - 3.0 + 0.25 * ((i * 7) % 5) as f64).collect();
let mut e = vec![0.0; n];
for i in 0..n.saturating_sub(1) {
e[i] = 0.5 + 0.125 * ((i * 3) % 4) as f64;
}
(d, e)
}
fn norm_1(a: &[f64], m: usize, n: usize) -> f64 {
let mut best = 0.0f64;
for j in 0..n {
let mut sum = 0.0;
for i in 0..m {
sum += a[i + j * m].abs();
}
best = best.max(sum);
}
best
}
fn sum_abs(a: &[f64]) -> f64 {
a.iter().map(|x| x.abs()).sum()
}
#[test]
fn dsytrd_matches_the_unblocked_reduction() {
for n in [1usize, 2, 3, 5, 8, 16, 17, 32, 33, 64, 65, 100] {
for upper in [false, true] {
let a0 = symmetric_data(n);
let scale = a0.iter().fold(0.0f64, |m, x| m.max(x.abs())).max(1.0);
let mut a_unb = a0.clone();
let (mut d_u, mut e_u) = (vec![0.0; n], vec![0.0; n]);
let mut tau_u = vec![0.0; n];
dsytd2(upper, n, &mut a_unb, &mut d_u, &mut e_u, &mut tau_u);
let mut a_blk = a0.clone();
let (mut d_b, mut e_b) = (vec![0.0; n], vec![0.0; n]);
let mut tau_b = vec![0.0; n];
let info = dsytrd(upper, n, &mut a_blk, &mut d_b, &mut e_b, &mut tau_b);
assert_eq!(info, 0, "n={n} upper={upper}: dsytrd reported info={info}");
let (mut w_u, mut w_b) = (vec![0.0; n], vec![0.0; n]);
let mut work = vec![0.0; 2 * n.max(1)];
let mut z_u = vec![0.0; n * n];
let mut z_b = vec![0.0; n * n];
dsteqr(2, n, &mut d_u, &mut e_u, &mut z_u, n, &mut work);
dsteqr(2, n, &mut d_b, &mut e_b, &mut z_b, n, &mut work);
w_u.copy_from_slice(&d_u);
w_b.copy_from_slice(&d_b);
for i in 0..n {
assert!(
(w_b[i] - w_u[i]).abs() / scale < 1e-12,
"n={n} upper={upper}: eigenvalue {i} of the blocked form is {} but the unblocked form gives {}",
w_b[i],
w_u[i]
);
}
let _ = &z_u;
let mut a_ev = a0.clone();
let mut w_ref = vec![0.0; n];
dsyev(&mut a_ev, &mut w_ref, n, upper).expect("dsyev");
for i in 0..n {
assert!(
(w_b[i] - w_ref[i]).abs() / scale < 1e-11,
"n={n} upper={upper}: eigenvalue {i} of the blocked reduction is {} but dsyev gives {}",
w_b[i],
w_ref[i]
);
}
let _ = &z_b;
}
}
}
#[test]
fn dsytrd_and_dorgtr_satisfy_dsyt21() {
let eps = f64::EPSILON;
for n in [2usize, 3, 5, 8, 13, 21, 34, 40] {
for upper in [false, true] {
let a0 = symmetric_data(n);
let a_norm = norm_1(&a0, n, n).max(f64::MIN_POSITIVE);
let mut q = a0.clone();
let mut d = vec![0.0; n];
let mut e = vec![0.0; n - 1];
let mut tau = vec![0.0; n - 1];
let info = dsytrd(upper, n, &mut q, &mut d, &mut e, &mut tau);
assert_eq!(info, 0, "n={n} upper={upper}: dsytrd reported info={info}");
dorgtr(upper, n, &mut q, n, &tau);
let t = tridiagonal_data(&d, &e, n);
let mut prod = vec![0.0; n * n];
for j in 0..n {
for i in 0..n {
let mut sum = 0.0;
for k in 0..n {
sum += q[i + k * n] * t[k + j * n];
}
prod[i + j * n] = sum;
}
}
let mut rec = vec![0.0; n * n];
for j in 0..n {
for i in 0..n {
let mut sum = 0.0;
for k in 0..n {
sum += prod[i + k * n] * q[j + k * n];
}
rec[i + j * n] = sum;
}
}
let mut diff = 0.0;
let mut orth = 0.0;
for j in 0..n {
for i in 0..n {
diff += (a0[i + j * n] - rec[i + j * n]).abs();
let mut dot = 0.0;
for k in 0..n {
dot += q[k + i * n] * q[k + j * n];
}
orth += (dot - if i == j { 1.0 } else { 0.0 }).abs();
}
}
let ratio1 = diff / (a_norm * n as f64 * eps);
let ratio2 = orth / (n as f64 * eps);
assert!(ratio1 <= 20.0, "n={n} upper={upper}: dsyt21 ratio 1 = {ratio1} exceeds 20");
assert!(ratio2 <= 20.0, "n={n} upper={upper}: dsyt21 ratio 2 = {ratio2} exceeds 20");
}
}
}
#[test]
fn dsteqr_gives_the_eigendecomposition_of_a_tridiagonal_matrix() {
let eps = f64::EPSILON;
let mut cases: Vec<(usize, Vec<f64>, Vec<f64>)> = [1usize, 2, 3, 5, 8, 13, 21, 34]
.iter()
.map(|&n| {
let (d, e) = tridiagonal_sample(n);
(n, d, e)
})
.collect();
cases.push((6, vec![1.0, 1.0, 2.0, 2.0, 3.0, 3.0], vec![0.0; 6]));
cases.push((4, vec![2.0; 4], vec![1e-8, 1e-8, 1e-8, 0.0]));
for (n, mut d, mut e) in cases {
let t = tridiagonal_data(&d, &e, n);
let t_norm = norm_1(&t, n, n).max(f64::MIN_POSITIVE);
let mut z = vec![0.0; n * n];
let mut work = vec![0.0; 2 * n.max(1)];
let info = dsteqr(2, n, &mut d, &mut e, &mut z, n, &mut work);
assert_eq!(info, 0, "n={n}: dsteqr did not converge");
let w = d.clone();
let mut tu = vec![0.0; n * n];
for j in 0..n {
for i in 0..n {
let mut sum = 0.0;
for k in 0..n {
sum += t[i + k * n] * z[k + j * n];
}
tu[i + j * n] = sum - w[j] * z[i + j * n];
}
}
let mut orth = 0.0;
for j in 0..n {
for i in 0..n {
let mut dot = 0.0;
for k in 0..n {
dot += z[k + i * n] * z[k + j * n];
}
orth += (dot - if i == j { 1.0 } else { 0.0 }).abs();
}
}
let mut rec = vec![0.0; n * n];
for j in 0..n {
for i in 0..n {
let mut sum = 0.0;
for k in 0..n {
sum += z[i + k * n] * w[k] * z[j + k * n];
}
rec[i + j * n] = sum;
}
}
let mut rec_diff = 0.0;
for idx in 0..n * n {
rec_diff += (t[idx] - rec[idx]).abs();
}
let ratio1 = sum_abs(&tu) / (t_norm * n as f64 * eps);
let ratio2 = orth / (n as f64 * eps);
let ratio3 = rec_diff / (t_norm * n as f64 * eps);
assert!(ratio1 <= 20.0, "n={n}: dstt22 ratio 1 = {ratio1} exceeds 20");
assert!(ratio2 <= 20.0, "n={n}: dstt22 ratio 2 = {ratio2} exceeds 20");
assert!(ratio3 <= 20.0, "n={n}: dstt21 ratio = {ratio3} exceeds 20");
}
}
#[test]
fn dsteqr_covers_its_modes_and_scaling() {
let (mut d, mut e) = (vec![2.0, 2.0], vec![1.0, 0.0]);
let mut z = vec![7.0; 4];
let mut work = vec![0.0; 4];
let info = dsteqr(0, 2, &mut d, &mut e, &mut z, 2, &mut work);
assert_eq!(info, 0);
assert_eq!(z, vec![7.0; 4], "icompz = 0 must not touch Z");
let mut w = d.clone();
w.sort_by(|a, b| a.partial_cmp(b).unwrap());
assert!((w[0] - 1.0).abs() < 1e-14 && (w[1] - 3.0).abs() < 1e-14, "w = {w:?}");
let (mut d, mut e) = (vec![2.0, 2.0], vec![1.0, 0.0]);
let mut z = vec![0.0; 4];
for i in 0..2 {
z[i + i * 2] = 1.0;
}
let info = dsteqr(1, 2, &mut d, &mut e, &mut z, 2, &mut work);
assert_eq!(info, 0);
assert!((d[0] - 1.0).abs() < 1e-14 && (d[1] - 3.0).abs() < 1e-14, "d = {d:?}");
let dot = z[0] * z[2] + z[1] * z[3];
let n0 = z[0] * z[0] + z[1] * z[1];
let n1 = z[2] * z[2] + z[3] * z[3];
assert!(dot.abs() < 1e-14 && (n0 - 1.0).abs() < 1e-14 && (n1 - 1.0).abs() < 1e-14);
let (mut d, mut e) = (vec![1.0, 2.0, 3.0], vec![0.0, 0.0, 0.0]);
let mut z = vec![0.0; 9];
let mut work = vec![0.0; 8];
let info = dsteqr(2, 3, &mut d, &mut e, &mut z, 3, &mut work);
assert_eq!(info, 0);
for k in 0..3 {
assert!((d[k] - (k as f64 + 1.0)).abs() < 1e-14, "d = {d:?}");
}
let (mut d, mut e) = (vec![4.5], vec![0.0]);
let mut z = vec![0.0];
let info = dsteqr(2, 1, &mut d, &mut e, &mut z, 1, &mut work);
assert_eq!(info, 0);
assert_eq!(d[0], 4.5);
assert_eq!(z[0], 1.0);
for (d0, e0, want) in [(1e300, 1e300, [0.0, 2e300]), (1e-300, 1e-300, [0.0, 2e-300])] {
let (mut d, mut e) = (vec![d0, d0], vec![e0, 0.0]);
let mut z = vec![0.0; 4];
let mut work = vec![0.0; 8];
let info = dsteqr(2, 2, &mut d, &mut e, &mut z, 2, &mut work);
assert_eq!(info, 0, "d0 = {d0:e}: info = {info}");
assert!(
(d[0] - want[0]).abs() <= 1e-14 * want[1] && (d[1] - want[1]).abs() <= 1e-14 * want[1],
"d0 = {d0:e}: d = {d:?}, want {want:?}"
);
let dot = z[0] * z[2] + z[1] * z[3];
assert!(dot.abs() < 1e-13, "d0 = {d0:e}: Z is not orthogonal ({dot})");
}
}
#[test]
fn dsyev_handles_matrices_at_the_edges_of_the_range() {
let n = 5usize;
let eps = f64::EPSILON;
for s in [1e300f64, 1e-300, 1e250, 1e-250, 1.0] {
let mut a0 = vec![0.0; n * n];
for i in 0..n {
a0[i + i * n] = s * (i as f64 + 1.0);
for j in (i + 1)..n {
let v = s * 0.25 / (j - i) as f64;
a0[i + j * n] = v;
a0[j + i * n] = v;
}
}
let mut a = a0.clone();
let mut w = vec![0.0; n];
dsyev(&mut a, &mut w, n, true).expect("dsyev");
let anorm = a0.iter().fold(0.0f64, |m, x| m.max(x.abs()));
for k in 0..n {
assert!(w[k].is_finite(), "s = {s}: eigenvalue {k} is not finite");
}
for k in 1..n {
assert!(w[k - 1] <= w[k], "s = {s}: eigenvalues are not ordered");
}
for k in 0..n {
let mut worst = 0.0f64;
for i in 0..n {
let mut row = 0.0;
for j in 0..n {
row += a0[i + j * n] * a[j + k * n];
}
worst = worst.max((row - w[k] * a[i + k * n]).abs());
}
let ratio = worst / (n as f64 * anorm * eps);
assert!(ratio <= 20.0, "s = {s}, k = {k}: residual ratio = {ratio:e}");
}
let mut orth = 0.0f64;
for i in 0..n {
for j in 0..n {
let mut dot = 0.0;
for k in 0..n {
dot += a[k + i * n] * a[k + j * n];
}
let want = if i == j { 1.0 } else { 0.0 };
orth = orth.max((dot - want).abs());
}
}
let ratio = orth / (n as f64 * eps);
assert!(ratio <= 20.0, "s = {s}: orthogonality ratio = {ratio:e}");
}
}
}