1mod balance;
16pub mod hessenberg;
18#[doc(hidden)]
19pub mod schur;
20pub mod tridiag;
22pub(crate) mod tridiag_evd;
23use crate::assert;
24use crate::internal_prelude::*;
25pub use balance::{
26 BalanceError, balance_eigenvectors_in_place, balance_eigenvectors_scratch,
27 balance_in_place, balance_scratch,
28};
29use hessenberg::HessenbergParams;
30use linalg::matmul::triangular::BlockStructure;
31pub use schur::SchurParams;
32use tridiag::TridiagParams;
33#[derive(Copy, Clone, Debug, PartialEq, Eq)]
35pub enum EvdError {
36 NoConvergence,
38}
39#[derive(Clone, Copy, Debug)]
41pub struct EvdFromSchurParams {
42 pub recursion_threshold: usize,
44 #[doc(hidden)]
45 pub non_exhaustive: NonExhaustive,
46}
47#[derive(Clone, Copy, Debug)]
49pub struct EvdParams {
50 pub hessenberg: HessenbergParams,
52 pub schur: SchurParams,
54 pub evd_from_schur: EvdFromSchurParams,
56 #[doc(hidden)]
57 pub non_exhaustive: NonExhaustive,
58}
59#[derive(Clone, Copy, Debug)]
61pub struct SelfAdjointEvdParams {
62 pub tridiag: TridiagParams,
64 pub recursion_threshold: usize,
66 #[doc(hidden)]
67 pub non_exhaustive: NonExhaustive,
68}
69impl<T: ComplexField> Auto<T> for EvdParams {
70 fn auto() -> Self {
71 Self {
72 hessenberg: auto!(T),
73 schur: auto!(T),
74 evd_from_schur: auto!(T),
75 non_exhaustive: NonExhaustive(()),
76 }
77 }
78}
79impl<T: ComplexField> Auto<T> for EvdFromSchurParams {
80 fn auto() -> Self {
81 Self {
82 recursion_threshold: 64,
83 non_exhaustive: NonExhaustive(()),
84 }
85 }
86}
87impl<T: ComplexField> Auto<T> for SelfAdjointEvdParams {
88 fn auto() -> Self {
89 Self {
90 tridiag: auto!(T),
91 recursion_threshold: 128,
92 non_exhaustive: NonExhaustive(()),
93 }
94 }
95}
96#[derive(Copy, Clone, Debug, PartialEq, Eq)]
98pub enum ComputeEigenvectors {
99 No,
101 Yes,
103}
104pub fn self_adjoint_evd_scratch<T: ComplexField>(
107 dim: usize,
108 compute_u: ComputeEigenvectors,
109 par: ParShape,
110 params: Spec<SelfAdjointEvdParams, T>,
111) -> StackReq {
112 let n = dim;
113 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(n, n);
114 let prologue = StackReq::all_of(&[
115 temp_mat_scratch::<T>(n, n),
116 temp_mat_scratch::<T>(bs, n),
117 StackReq::any_of(&[tridiag::tridiag_in_place_scratch::<T>(
118 n,
119 par,
120 params.tridiag.into(),
121 )]),
122 temp_mat_scratch::<T::Real>(n, 1).array(2),
123 ]);
124 if compute_u == ComputeEigenvectors::No {
125 return prologue;
126 }
127 StackReq::all_of(&[
128 prologue,
129 temp_mat_scratch::<T::Real>(
130 n,
131 if const {
132 T::IS_REAL
133 } {
134 0
135 } else {
136 n
137 },
138 )
139 .array(2),
140 StackReq::any_of(&[
141 if n < params.recursion_threshold {
142 StackReq::empty()
143 } else {
144 tridiag_evd::divide_and_conquer_scratch::<T>(n, par)
145 },
146 temp_mat_scratch::<T>(n, 1),
147 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<T>(
148 n.saturating_sub(1),
149 bs,
150 n,
151 ),
152 ]),
153 ])
154}
155pub fn tridiagonal_self_adjoint_evd_scratch<T: ComplexField>(
170 dim: usize,
171 compute_u: ComputeEigenvectors,
172 par: ParShape,
173 params: Spec<SelfAdjointEvdParams, T>,
174) -> StackReq {
175 let n = dim;
176 StackReq::all_of(&[
177 temp_mat_scratch::<T::Real>(n, 1).array(2),
178 if compute_u == ComputeEigenvectors::No {
179 StackReq::EMPTY
180 } else {
181 temp_mat_scratch::<T::Real>(n, if T::IS_REAL { 0 } else { n }).and(
182 temp_mat_scratch::<T>(n, 1).or(
183 if n < params.recursion_threshold {
184 StackReq::EMPTY
185 } else {
186 tridiag_evd::divide_and_conquer_scratch::<T::Real>(
187 n, par,
188 )
189 },
190 ),
191 )
192 },
193 ])
194}
195pub fn tridiagonal_self_adjoint_evd<T: ComplexField>(
201 diag: DiagRef<'_, T>,
202 subdiag: DiagRef<'_, T>,
203 s: DiagMut<'_, T>,
204 u: Option<MatMut<'_, T>>,
205 par: Par,
206 stack: &mut MemStack,
207 params: Spec<SelfAdjointEvdParams, T>,
208) -> Result<(), EvdError> {
209 let n = diag.dim();
210 if n == 0 {
211 return Ok(());
212 }
213 let (mut real_diag, stack) =
214 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
215 let (mut real_offdiag, stack) =
216 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
217 let mut real_diag = real_diag
218 .as_mat_mut()
219 .col_mut(0)
220 .try_as_col_major_mut()
221 .unwrap();
222 let mut real_offdiag = real_offdiag
223 .as_mat_mut()
224 .col_mut(0)
225 .try_as_col_major_mut()
226 .unwrap();
227 for i in 0..n {
228 real_diag[i] = diag[i].real();
229 if i + 1 < n {
230 if const { T::IS_REAL } {
231 real_offdiag[i] = subdiag[i].real();
232 } else {
233 real_offdiag[i] = subdiag[i].abs();
234 }
235 } else {
236 real_offdiag[i] = zero();
237 }
238 }
239 let mut s = s;
240 let mut u = match u {
241 Some(u) => u,
242 None => {
243 tridiag_evd::qr_algorithm(
244 real_diag.rb_mut(),
245 real_offdiag.rb_mut(),
246 None,
247 )?;
248 for i in 0..n {
249 s[i] = real_diag[i].to_cplx();
250 }
251 return Ok(());
252 },
253 };
254 let (mut u_real, stack) = unsafe {
255 temp_mat_uninit::<T::Real, _, _>(
256 n,
257 if T::IS_REAL { 0 } else { n },
258 stack,
259 )
260 };
261 let mut u_real = u_real.as_mat_mut();
262 let mut u_evd = if const { T::IS_REAL } {
263 unsafe { core::mem::transmute(u.rb_mut()) }
264 } else {
265 u_real.rb_mut()
266 };
267 if n < params.recursion_threshold {
268 tridiag_evd::qr_algorithm(
269 real_diag.rb_mut(),
270 real_offdiag.rb_mut(),
271 Some(u_evd.rb_mut()),
272 )?;
273 } else {
274 tridiag_evd::divide_and_conquer::<T::Real>(
275 real_diag.rb_mut(),
276 real_offdiag.rb_mut(),
277 u_evd.rb_mut(),
278 par,
279 stack,
280 params.recursion_threshold,
281 )?;
282 }
283 if const { !T::IS_REAL } {
284 let normalized = |x: T| {
285 if x == zero() {
286 one()
287 } else {
288 x.mul_real(x.abs().recip())
289 }
290 };
291 let (mut scale, _) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
292 let mut scale = scale
293 .as_mat_mut()
294 .col_mut(0)
295 .try_as_col_major_mut()
296 .unwrap();
297 let mut x = one::<T>();
298 scale[0] = one();
299 for i in 1..n {
300 x = normalized(&subdiag[i - 1] * x);
301 scale[i] = x.copy();
302 }
303 for j in 0..n {
304 z!(u.rb_mut().col_mut(j), u_real.rb().col(j), scale.rb()).for_each(
305 |uz!(u, real, scale)| {
306 *u = scale.mul_real(real);
307 },
308 );
309 }
310 }
311 for i in 0..n {
312 s[i] = real_diag[i].to_cplx();
313 }
314 Ok(())
315}
316pub fn self_adjoint_evd<T: ComplexField>(
323 A: MatRef<'_, T>,
324 s: DiagMut<'_, T>,
325 u: Option<MatMut<'_, T>>,
326 mut par: Par,
327 stack: &mut MemStack,
328 params: Spec<SelfAdjointEvdParams, T>,
329) -> Result<(), EvdError> {
330 let n = A.nrows();
331 assert!(all(A.nrows() == A.ncols(), s.dim() == n));
332 if let Some(u) = u.rb() {
333 assert!(all(u.nrows() == n, u.ncols() == n));
334 }
335 let s = s.column_vector_mut();
336 if n == 0 {
337 return Ok(());
338 }
339 #[cfg(feature = "perf-warn")]
340 if let Some(matrix) = u.rb() {
341 if matrix.row_stride().unsigned_abs() != 1
342 && crate::__perf_warn!(QR_WARN)
343 {
344 if matrix.col_stride().unsigned_abs() == 1 {
345 log::warn!(
346 target : "faer_perf",
347 "EVD prefers column-major eigenvector matrix. Found row-major matrix."
348 );
349 } else {
350 log::warn!(
351 target : "faer_perf",
352 "EVD prefers column-major eigenvector matrix. Found matrix with generic strides."
353 );
354 }
355 }
356 }
357 let (mut trid, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
358 let mut trid = trid.as_mat_mut();
359 trid.copy_from_triangular_lower(A);
360 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(n, n);
361 let (mut householder, stack) =
362 unsafe { temp_mat_uninit::<T, _, _>(bs, n - 1, stack) };
363 let mut householder = householder.as_mat_mut();
364 {
365 tridiag::tridiag_in_place(
366 trid.rb_mut(),
367 householder.rb_mut(),
368 par.reborrow(),
369 stack,
370 params.tridiag.into(),
371 );
372 }
373 let trid = trid.rb();
374 let (mut diag, stack) =
375 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
376 let (mut offdiag, stack) =
377 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
378 let mut diag = diag.as_mat_mut().col_mut(0).try_as_col_major_mut().unwrap();
379 let mut offdiag = offdiag
380 .as_mat_mut()
381 .col_mut(0)
382 .try_as_col_major_mut()
383 .unwrap();
384 for i in 0..n {
385 diag[i] = trid[(i, i)].real();
386 if i + 1 < n {
387 if const { T::IS_REAL } {
388 offdiag[i] = trid[(i + 1, i)].real();
389 } else {
390 offdiag[i] = trid[(i + 1, i)].abs();
391 }
392 } else {
393 offdiag[i] = zero();
394 }
395 }
396 let mut s = s;
397 let mut u = match u {
398 Some(u) => u,
399 None => {
400 tridiag_evd::qr_algorithm(diag.rb_mut(), offdiag.rb_mut(), None)?;
401 for i in 0..n {
402 s[i] = diag[i].to_cplx();
403 }
404 return Ok(());
405 },
406 };
407 let (mut u_real, stack) = unsafe {
408 temp_mat_uninit::<T::Real, _, _>(
409 n,
410 if T::IS_REAL { 0 } else { n },
411 stack,
412 )
413 };
414 let mut u_real = u_real.as_mat_mut();
415 let mut u_evd = if const { T::IS_REAL } {
416 unsafe { core::mem::transmute(u.rb_mut()) }
417 } else {
418 u_real.rb_mut()
419 };
420 if n < params.recursion_threshold {
421 tridiag_evd::qr_algorithm(
422 diag.rb_mut(),
423 offdiag.rb_mut(),
424 Some(u_evd.rb_mut()),
425 )?;
426 } else {
427 tridiag_evd::divide_and_conquer::<T::Real>(
428 diag.rb_mut(),
429 offdiag.rb_mut(),
430 u_evd.rb_mut(),
431 par.reborrow(),
432 stack,
433 params.recursion_threshold,
434 )?;
435 }
436 if const { !T::IS_REAL } {
437 let normalized = |x: T| {
438 if x == zero() {
439 one()
440 } else {
441 x.mul_real(x.abs().recip())
442 }
443 };
444 let (mut scale, _) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
445 let mut scale = scale
446 .as_mat_mut()
447 .col_mut(0)
448 .try_as_col_major_mut()
449 .unwrap();
450 let mut x = one::<T>();
451 scale[0] = one();
452 for i in 1..n {
453 x = normalized(&trid[(i, i - 1)] * x);
454 scale[i] = x.copy();
455 }
456 for j in 0..n {
457 z!(u.rb_mut().col_mut(j), u_real.rb().col(j), scale.rb()).for_each(
458 |uz!(u, real, scale)| {
459 *u = scale.mul_real(real);
460 },
461 );
462 }
463 }
464 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
465 trid.submatrix(1, 0, n - 1, n - 1),
466 householder.rb(),
467 Conj::No,
468 u.rb_mut().subrows_mut(1, n - 1),
469 par,
470 stack,
471 );
472 for i in 0..n {
473 s[i] = diag[i].to_cplx();
474 }
475 Ok(())
476}
477pub fn pseudoinverse_from_self_adjoint_evd_scratch<T: ComplexField>(
480 dim: usize,
481 par: ParShape,
482) -> StackReq {
483 _ = par;
484 temp_mat_scratch::<T>(dim, dim).array(2)
485}
486#[track_caller]
489pub fn pseudoinverse_from_self_adjoint_evd<T: ComplexField>(
490 pinv: MatMut<'_, T>,
491 s: DiagRef<'_, T>,
492 u: MatRef<'_, T>,
493 par: Par,
494 stack: &mut MemStack,
495) {
496 pseudoinverse_from_self_adjoint_evd_with_tolerance(
497 pinv,
498 s,
499 u,
500 zero(),
501 eps::<T::Real>() * from_f64::<T::Real>(u.ncols() as f64),
502 par,
503 stack,
504 );
505}
506#[track_caller]
510pub fn pseudoinverse_from_self_adjoint_evd_with_tolerance<T: ComplexField>(
511 pinv: MatMut<'_, T>,
512 s: DiagRef<'_, T>,
513 u: MatRef<'_, T>,
514 abs_tol: T::Real,
515 rel_tol: T::Real,
516 par: Par,
517 stack: &mut MemStack,
518) {
519 let s = s.column_vector();
520 let mut pinv = pinv;
521 let n = u.ncols();
522 assert!(all(u.nrows() == n, u.ncols() == n, s.nrows() == n));
523 let smax = s.norm_max();
524 let tol = abs_tol.fmax(rel_tol * smax);
525 let (mut u_trunc, stack) =
526 unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
527 let (mut up_trunc, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
528 let mut u_trunc = u_trunc.as_mat_mut();
529 let mut up_trunc = up_trunc.as_mat_mut();
530 let mut len = 0;
531 for j in 0..n {
532 let x = s[j].absmax();
533 if x > tol {
534 let ref p = s[j].real().recip();
535 u_trunc.rb_mut().col_mut(len).copy_from(u.col(j));
536 z!(up_trunc.rb_mut().col_mut(len), u.col(j))
537 .for_each(|uz!(dst, src)| *dst = src.mul_real(p));
538 len += 1;
539 }
540 }
541 let u_trunc = u_trunc.get(.., ..len);
542 let up_trunc = up_trunc.get(.., ..len);
543 linalg::matmul::triangular::matmul(
544 pinv.rb_mut(),
545 BlockStructure::TriangularLower,
546 Accum::Replace,
547 up_trunc.rb(),
548 BlockStructure::Rectangular,
549 u_trunc.rb().adjoint(),
550 BlockStructure::Rectangular,
551 one(),
552 par,
553 );
554 for j in 0..n {
555 for i in 0..j {
556 pinv[(i, j)] = pinv[(j, i)].conj();
557 }
558 }
559}
560fn dot2x1<T: RealField>(
561 lhs0: RowRef<'_, T>,
562 lhs1: RowRef<'_, T>,
563 rhs: ColRef<'_, T>,
564) -> (T, T) {
565 let n = rhs.nrows();
566 assert!(all(lhs0.ncols() == n, lhs1.ncols() == n));
567 let mut acc00 = zero::<T>();
568 let mut acc01 = zero::<T>();
569 let mut acc10 = zero::<T>();
570 let mut acc11 = zero::<T>();
571 let n2 = n / 2 * 2;
572 let mut i = 0;
573 while i < n2 {
574 acc00 += &lhs0[i] * &rhs[i];
575 acc10 += &lhs1[i] * &rhs[i];
576 acc01 += &lhs0[i + 1] * &rhs[i + 1];
577 acc11 += &lhs1[i + 1] * &rhs[i + 1];
578 i += 2;
579 }
580 while i < n {
581 acc00 += &lhs0[i] * &rhs[i];
582 acc10 += &lhs1[i] * &rhs[i];
583 i += 1;
584 }
585 (acc00 + acc01, acc10 + acc11)
586}
587fn dot2x2<T: RealField>(
588 lhs0: RowRef<'_, T>,
589 lhs1: RowRef<'_, T>,
590 rhs0: ColRef<'_, T>,
591 rhs1: ColRef<'_, T>,
592) -> (T, T, T, T) {
593 let n = rhs0.nrows();
594 assert!(all(lhs0.ncols() == n, lhs1.ncols() == n));
595 let mut acc0 = zero::<T>();
596 let mut acc1 = zero::<T>();
597 let mut acc2 = zero::<T>();
598 let mut acc3 = zero::<T>();
599 let mut i = 0;
600 while i < n {
601 acc0 += &lhs0[i] * &rhs0[i];
602 acc1 += &lhs1[i] * &rhs0[i];
603 acc2 += &lhs0[i] * &rhs1[i];
604 acc3 += &lhs1[i] * &rhs1[i];
605 i += 1;
606 }
607 (acc0, acc1, acc2, acc3)
608}
609fn normalize_eigenvectors<T: ComplexField>(
614 mut u: MatMut<'_, T>,
615 h: MatRef<'_, T>,
616) {
617 let n = u.nrows();
618 let mut j = 0;
619 while j < n {
620 let paired = T::IS_REAL && j + 1 < n && h[(j + 1, j)] != zero::<T>();
621 let nrm = if paired {
622 u.rb().col(j).norm_l2().hypot(&u.rb().col(j + 1).norm_l2())
623 } else {
624 u.rb().col(j).norm_l2()
625 };
626 if nrm != zero::<T::Real>() {
627 let inv: T = nrm.recip().to_cplx();
628 for i in 0..n {
629 u[(i, j)] *= inv.copy();
630 if paired {
631 u[(i, j + 1)] *= inv.copy();
632 }
633 }
634 }
635 j += if paired { 2 } else { 1 };
636 }
637}
638fn eigenvectors_ok<T: ComplexField>(u: MatRef<'_, T>) -> bool {
641 u.is_all_finite()
642 && (0..u.ncols()).all(|j| u.col(j).norm_l2() > zero::<T::Real>())
643}
644fn eps_abs1<T: ComplexField>(x: &T) -> T::Real {
647 let eps = eps::<T::Real>();
648 &eps * &x.real().abs() + &eps * &x.imag().abs()
649}
650fn entry_floor<T: ComplexField>(A: MatRef<'_, T>, subdiag: usize) -> T::Real {
668 let n = A.nrows();
669 let mut m = min_positive::<T::Real>() * eps::<T::Real>();
670 for j in 0..n {
671 for i in 0..Ord::min(j + 1 + subdiag, n) {
672 m = m.fmax(eps_abs1(&A[(i, j)]));
673 }
674 }
675 m
676}
677fn scaled_into_range<T: ComplexField>(A: MatRef<'_, T>, subdiag: usize) -> Option<Mat<T>> {
685 let n = A.nrows();
686 let rows = |j: usize| Ord::min(j + 1 + subdiag, n);
687 let mut m = zero::<T::Real>();
688 for j in 0..n {
689 for i in 0..rows(j) {
690 m = m.fmax(A[(i, j)].real().abs()).fmax(A[(i, j)].imag().abs());
691 }
692 }
693 let one = one::<T::Real>();
694 let two = from_f64::<T::Real>(2.0);
695 let half = from_f64::<T::Real>(0.5);
696 if m == zero::<T::Real>()
697 || !m.is_finite()
698 || (m >= from_f64::<T::Real>(9.5367431640625e-7) && m <= from_f64::<T::Real>(1048576.0))
700 {
701 return None;
702 }
703 let mut k = 0i32;
705 let mut mm = m;
706 while mm >= two {
707 mm = mm * ½
708 k -= 1;
709 }
710 while mm < one {
711 mm = mm * &two;
712 k += 1;
713 }
714 let pow = |e: i32| {
715 let base = if e >= 0 { two.copy() } else { half.copy() };
716 let mut f = one.copy();
717 for _ in 0..e.unsigned_abs() {
718 f = f * &base;
719 }
720 f
721 };
722 let (fa, fb) = (pow(k / 2), pow(k - k / 2));
723 Some(Mat::from_fn(n, n, |i, j| {
724 if i < rows(j) {
725 A[(i, j)].mul_real(&fa).mul_real(&fb)
726 } else {
727 zero::<T>()
728 }
729 }))
730}
731pub(crate) fn evd_from_real_schur_imp<T: RealField>(
732 A: MatRef<'_, T>,
733 V: MatMut<'_, T>,
734 mut par: Par,
735 params: EvdFromSchurParams,
736) {
737 let one = one::<T>;
738 let zero = zero::<T>;
739 let mut V = V;
740 let n = A.nrows();
741 let scaled = scaled_into_range(A, 1);
742 let A = scaled.as_ref().map_or(A, |s| s.as_ref());
743 let floor = entry_floor(A, 1);
744 let mut k = n;
745 loop {
746 if k == 0 {
747 break;
748 }
749 k -= 1;
750 if k == 0 || A[(k, k - 1)] == zero() {
751 let ref p = A[(k, k)].copy();
752 V[(k, k)] = one();
753 for i in 0..k {
754 V[(i, k)] = -&A[(i, k)];
755 }
756 let scale = solve_real_shifted_upper_quasi_triangular_system(
757 A.submatrix(0, 0, k, k),
758 p.copy(),
759 V.rb_mut().subrows_mut(0, k).col_mut(k),
760 eps_abs1(p).fmax(floor.copy()),
761 par.reborrow(),
762 params,
763 );
764 V[(k, k)] *= scale;
768 } else {
769 let ref p = A[(k, k)].copy();
770 let ref q = A[(k, k - 1)].abs().sqrt() * A[(k - 1, k)].abs().sqrt();
771 if A[(k - 1, k)].abs() >= A[(k, k - 1)].abs() {
772 V[(k - 1, k - 1)] = one();
773 V[(k, k)] = q / &A[(k - 1, k)];
774 } else {
775 V[(k - 1, k - 1)] = -q / &A[(k, k - 1)];
776 V[(k, k)] = one();
777 }
778 V[(k - 1, k)] = zero();
779 V[(k, k - 1)] = zero();
780 for i in 0..k - 1 {
781 V[(i, k - 1)] = -&V[(k - 1, k - 1)] * &A[(i, k - 1)];
782 V[(i, k)] = -&V[(k, k)] * &A[(i, k)];
783 }
784 let scale = solve_cplx_shifted_upper_quasi_triangular_system(
785 A.submatrix(0, 0, k - 1, k - 1),
786 p.copy(),
787 q.copy(),
788 V.rb_mut().submatrix_mut(0, k - 1, k - 1, 2),
789 (eps_abs1(p) + eps_abs1(q)).fmax(floor.copy()),
790 par.reborrow(),
791 params,
792 );
793 V[(k - 1, k - 1)] *= scale.copy();
794 V[(k, k)] *= scale;
795 k -= 1;
796 }
797 }
798}
799pub(crate) fn evd_from_cplx_schur_imp<T: ComplexField>(
800 A: MatRef<'_, T>,
801 conj_A: Conj,
802 V: MatMut<'_, T>,
803 mut par: Par,
804 params: EvdFromSchurParams,
805) {
806 let one = one::<T>;
807 let mut V = V;
808 let n = A.nrows();
809 let scaled = scaled_into_range(A, 0);
810 let A = scaled.as_ref().map_or(A, |s| s.as_ref());
811 let floor = entry_floor(A, 0);
812 let mut k = n;
813 loop {
814 if k == 0 {
815 break;
816 }
817 k -= 1;
818 let ref p = if conj_A == Conj::Yes {
819 A[(k, k)].conj()
820 } else {
821 A[(k, k)].copy()
822 };
823 V[(k, k)] = one();
824 if conj_A == Conj::Yes {
825 for i in 0..k {
826 V[(i, k)] = -A[(i, k)].conj();
827 }
828 } else {
829 for i in 0..k {
830 V[(i, k)] = -&A[(i, k)];
831 }
832 }
833 let scale = solve_shifted_upper_triangular_system(
834 A.submatrix(0, 0, k, k),
835 conj_A,
836 p.copy(),
837 V.rb_mut().subrows_mut(0, k).col_mut(k),
838 eps_abs1(p).fmax(floor.copy()),
839 par.reborrow(),
840 params,
841 );
842 let scale: T = scale.to_cplx();
843 V[(k, k)] *= scale;
844 }
845}
846fn solve_real_shifted_upper_quasi_triangular_system<T: RealField>(
847 A: MatRef<'_, T>,
848 p: T,
849 x: ColMut<'_, T>,
850 eps_norm: T,
851 mut par: Par,
852 params: EvdFromSchurParams,
853) -> T {
854 let n = A.nrows();
855 let one = one::<T>;
856 let zero = zero::<T>;
857 let ref p = p;
858 let mut x = x;
859 let big = sqrt_max_positive::<T>();
865 let mut scale = one();
866 if par.degree() == 0 || n < params.recursion_threshold {
867 let mut i = n;
868 loop {
869 if i == 0 {
870 break;
871 }
872 i -= 1;
873 if i == 0 || A[(i, i - 1)] == zero() {
874 let dot = linalg::matmul::dot::inner_prod(
875 A.row(i).subcols(i + 1, n - i - 1),
876 Conj::No,
877 x.rb().subrows(i + 1, n - i - 1),
878 Conj::No,
879 );
880 x[i] -= dot;
881 let mut z = &A[(i, i)] - p;
882 if z.abs() < eps_norm {
883 z = eps_norm.copy();
884 }
885 if x[i] != zero() {
886 x[i] /= z;
887 }
888 let m = x[i].abs();
889 if m > big {
890 let r = m.recip();
891 for j in 0..x.nrows() {
892 x[j] *= r.copy();
893 }
894 scale *= r;
895 }
896 } else {
897 let (dot0, dot1) = dot2x1(
898 A.row(i - 1).subcols(i + 1, n - i - 1),
899 A.row(i).subcols(i + 1, n - i - 1),
900 x.rb().subrows(i + 1, n - i - 1),
901 );
902 x[i - 1] -= dot0;
903 x[i] -= dot1;
904 let ((y0, _), (y1, _)) = solve_shifted_2x2_cplx(
909 (&A[(i, i)] - p, zero()),
910 A[(i - 1, i)].copy(),
911 A[(i, i - 1)].copy(),
912 (x[i - 1].copy(), zero()),
913 (x[i].copy(), zero()),
914 &eps_norm,
915 );
916 (x[i - 1], x[i]) = (y0, y1);
917 let a0 = x[i - 1].abs();
918 let a1 = x[i].abs();
919 let m = if a0 > a1 { a0 } else { a1 };
920 if m > big {
921 let r = m.recip();
922 for j in 0..x.nrows() {
923 x[j] *= r.copy();
924 }
925 scale *= r;
926 }
927 i -= 1;
928 }
929 }
930 scale
931 } else {
932 let mut mid = n / 2;
933 if A[(mid, mid - 1)] != zero() {
934 mid -= 1;
935 }
936 let (A00, A01, _, A11) = A.split_at(mid, mid);
937 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
938 let f1 = solve_real_shifted_upper_quasi_triangular_system(
939 A11,
940 p.copy(),
941 x1.rb_mut(),
942 eps_norm.copy(),
943 par.reborrow(),
944 params,
945 );
946 for j in 0..x0.nrows() {
950 x0[j] *= f1.copy();
951 }
952 linalg::matmul::matmul(
953 x0.rb_mut().as_mat_mut(),
954 Accum::Add,
955 A01,
956 x1.rb().as_mat(),
957 -one(),
958 par.reborrow(),
959 );
960 let f0 = solve_real_shifted_upper_quasi_triangular_system(
961 A00,
962 p.copy(),
963 x0.rb_mut(),
964 eps_norm,
965 par,
966 params,
967 );
968 for j in 0..x1.nrows() {
969 x1[j] *= f0.copy();
970 }
971 f0 * f1
972 }
973}
974fn solve_shifted_2x2_cplx<T: RealField>(
980 a: (T, T),
981 b: T,
982 c: T,
983 r0: (T, T),
984 r1: (T, T),
985 smin: &T,
986) -> ((T, T), (T, T)) {
987 type C<T> = (T, T);
988 fn abs1<T: RealField>(z: &C<T>) -> T {
989 z.0.abs() + z.1.abs()
990 }
991 fn mul<T: RealField>(x: &C<T>, y: &C<T>) -> C<T> {
992 (&x.0 * &y.0 - &x.1 * &y.1, &x.0 * &y.1 + &x.1 * &y.0)
993 }
994 fn sub<T: RealField>(x: &C<T>, y: &C<T>) -> C<T> {
995 (&x.0 - &y.0, &x.1 - &y.1)
996 }
997 fn div<T: RealField>(x: &C<T>, y: &C<T>) -> C<T> {
998 let s = y.0.abs().fmax(y.1.abs());
1000 let (yr, yi) = (&y.0 / &s, &y.1 / &s);
1001 let (xr, xi) = (&x.0 / &s, &x.1 / &s);
1002 let d = &yr * &yr + &yi * &yi;
1003 ((&xr * &yr + &xi * &yi) / &d, (&xi * &yr - &xr * &yi) / &d)
1004 }
1005 let zero = zero::<T>;
1006 let m = [[(a.0.copy(), a.1.copy()), (b, zero())], [(c, zero()), a]];
1007 let (mut p, mut q) = (0usize, 0usize);
1008 let mut cmax = abs1(&m[0][0]);
1009 for (i, j) in [(0, 1), (1, 0)] {
1010 let v = abs1(&m[i][j]);
1011 if v > cmax {
1012 cmax = v;
1013 (p, q) = (i, j);
1014 }
1015 }
1016 if cmax < *smin {
1017 let inv = smin.recip();
1018 return (
1019 (&r0.0 * &inv, &r0.1 * &inv),
1020 (&r1.0 * &inv, &r1.1 * &inv),
1021 );
1022 }
1023 let (rp, ro) = if p == 0 { (r0, r1) } else { (r1, r0) };
1025 let u11 = (m[p][q].0.copy(), m[p][q].1.copy());
1026 let u12 = (m[p][1 - q].0.copy(), m[p][1 - q].1.copy());
1027 let l21 = div(&m[1 - p][q], &u11);
1028 let mut u22 = sub(&m[1 - p][1 - q], &mul(&l21, &u12));
1029 if abs1(&u22) < *smin {
1030 u22 = (smin.copy(), zero());
1031 }
1032 let y2 = sub(&ro, &mul(&l21, &rp));
1033 let x2 = div(&y2, &u22);
1034 let x1 = div(&sub(&rp, &mul(&u12, &x2)), &u11);
1035 if q == 0 { (x1, x2) } else { (x2, x1) }
1037}
1038fn solve_cplx_shifted_upper_quasi_triangular_system<T: RealField>(
1039 A: MatRef<'_, T>,
1040 p: T,
1041 q: T,
1042 x: MatMut<'_, T>,
1043 eps_norm: T,
1044 mut par: Par,
1045 params: EvdFromSchurParams,
1046) -> T {
1047 let n = A.nrows();
1048 let one = one::<T>;
1049 let zero = zero::<T>;
1050 let ref p = p;
1051 let ref q = q;
1052 let mut x = x;
1053 let big = sqrt_max_positive::<T>();
1054 let mut scale = one();
1055 if par.degree() == 0 || n < params.recursion_threshold {
1056 let mut i = n;
1057 loop {
1058 if i == 0 {
1059 break;
1060 }
1061 i -= 1;
1062 if i == 0 || A[(i, i - 1)] == zero() {
1063 let (re, im) = dot2x1(
1064 x.rb().subrows(i + 1, n - i - 1).col(0).transpose(),
1065 x.rb().subrows(i + 1, n - i - 1).col(1).transpose(),
1066 A.row(i).subcols(i + 1, n - i - 1).transpose(),
1067 );
1068 x[(i, 0)] = &x[(i, 0)] - re;
1069 x[(i, 1)] = &x[(i, 1)] - im;
1070 let mut z_re = &A[(i, i)] - p;
1071 let mut z_im = -q;
1072 let mut z = z_re.hypot(&z_im);
1073 if z < eps_norm {
1074 z_re = eps_norm.copy();
1075 z_im = zero();
1076 z = z_re.copy();
1077 }
1078 let ref z = z;
1079 let ref z_re = (z_re / z) / z;
1080 let ref z_im = (-z_im / z) / z;
1081 let ref x_re = x[(i, 0)].copy();
1082 let ref x_im = x[(i, 1)].copy();
1083 x[(i, 0)] = x_re * z_re - x_im * z_im;
1084 x[(i, 1)] = x_re * z_im + x_im * z_re;
1085 let a0 = x[(i, 0)].abs();
1086 let a1 = x[(i, 1)].abs();
1087 let m = if a0 > a1 { a0 } else { a1 };
1088 if m > big {
1089 let r = m.recip();
1090 for j in 0..x.nrows() {
1091 x[(j, 0)] *= r.copy();
1092 x[(j, 1)] *= r.copy();
1093 }
1094 scale *= r;
1095 }
1096 } else {
1097 let (re0, re1, im0, im1) = dot2x2(
1098 A.row(i - 1).subcols(i + 1, n - i - 1),
1099 A.row(i).subcols(i + 1, n - i - 1),
1100 x.rb().col(0).subrows(i + 1, n - i - 1),
1101 x.rb().col(1).subrows(i + 1, n - i - 1),
1102 );
1103 x[(i - 1, 0)] = &x[(i - 1, 0)] - re0;
1104 x[(i - 1, 1)] = &x[(i - 1, 1)] - im0;
1105 x[(i, 0)] = &x[(i, 0)] - re1;
1106 x[(i, 1)] = &x[(i, 1)] - im1;
1107 let ref a_re = &A[(i, i)] - p;
1108 let ref a_im = -q;
1109 let ref b = A[(i - 1, i)];
1110 let ref c = A[(i, i - 1)];
1111 let ref r0_re = x[(i - 1, 0)];
1112 let ref r0_im = x[(i - 1, 1)];
1113 let ref r1_re = x[(i, 0)];
1114 let ref r1_im = x[(i, 1)];
1115 let ((y0_re, y0_im), (y1_re, y1_im)) = solve_shifted_2x2_cplx(
1122 (a_re.copy(), a_im.copy()),
1123 b.copy(),
1124 c.copy(),
1125 (r0_re.copy(), r0_im.copy()),
1126 (r1_re.copy(), r1_im.copy()),
1127 &eps_norm,
1128 );
1129 x[(i - 1, 0)] = y0_re;
1130 x[(i - 1, 1)] = y0_im;
1131 x[(i, 0)] = y1_re;
1132 x[(i, 1)] = y1_im;
1133 let mut m = x[(i - 1, 0)].abs();
1134 let v = x[(i - 1, 1)].abs();
1135 if v > m {
1136 m = v;
1137 }
1138 let v = x[(i, 0)].abs();
1139 if v > m {
1140 m = v;
1141 }
1142 let v = x[(i, 1)].abs();
1143 if v > m {
1144 m = v;
1145 }
1146 if m > big {
1147 let r = m.recip();
1148 for j in 0..x.nrows() {
1149 x[(j, 0)] *= r.copy();
1150 x[(j, 1)] *= r.copy();
1151 }
1152 scale *= r;
1153 }
1154 i -= 1;
1155 }
1156 }
1157 scale
1158 } else {
1159 let mut mid = n / 2;
1160 if A[(mid, mid - 1)] != zero() {
1161 mid -= 1;
1162 }
1163 let (A00, A01, _, A11) = A.split_at(mid, mid);
1164 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
1165 let f1 = solve_cplx_shifted_upper_quasi_triangular_system(
1166 A11,
1167 p.copy(),
1168 q.copy(),
1169 x1.rb_mut(),
1170 eps_norm.copy(),
1171 par.reborrow(),
1172 params,
1173 );
1174 for j in 0..x0.nrows() {
1175 x0[(j, 0)] *= f1.copy();
1176 x0[(j, 1)] *= f1.copy();
1177 }
1178 linalg::matmul::matmul(
1179 x0.rb_mut(),
1180 Accum::Add,
1181 A01,
1182 x1.rb(),
1183 -one(),
1184 par.reborrow(),
1185 );
1186 let f0 = solve_cplx_shifted_upper_quasi_triangular_system(
1187 A00,
1188 p.copy(),
1189 q.copy(),
1190 x0.rb_mut(),
1191 eps_norm,
1192 par,
1193 params,
1194 );
1195 for j in 0..x1.nrows() {
1196 x1[(j, 0)] *= f0.copy();
1197 x1[(j, 1)] *= f0.copy();
1198 }
1199 f0 * f1
1200 }
1201}
1202fn solve_shifted_upper_triangular_system<T: ComplexField>(
1203 A: MatRef<'_, T>,
1204 conj_A: Conj,
1205 p: T,
1206 x: ColMut<'_, T>,
1207 eps_norm: T::Real,
1208 mut par: Par,
1209 params: EvdFromSchurParams,
1210) -> T::Real {
1211 let n = A.nrows();
1212 let mut scale = one::<T::Real>();
1213 let one = one::<T>;
1214 let zero = zero::<T>;
1215 let ref p = p;
1216 let mut x = x;
1217 let big = sqrt_max_positive::<T::Real>();
1218 if par.degree() == 0 || n < params.recursion_threshold {
1219 let mut i = n;
1220 loop {
1221 if i == 0 {
1222 break;
1223 }
1224 i -= 1;
1225 let dot = linalg::matmul::dot::inner_prod(
1226 A.row(i).subcols(i + 1, n - i - 1),
1227 conj_A,
1228 x.rb().subrows(i + 1, n - i - 1),
1229 Conj::No,
1230 );
1231 x[i] -= dot;
1232 let mut z = if conj_A == Conj::Yes {
1233 A[(i, i)].conj()
1234 } else {
1235 A[(i, i)].copy()
1236 } - p;
1237 if z.abs() < eps_norm {
1238 z = eps_norm.to_cplx();
1239 }
1240 if x[i] != zero() {
1241 x[i] *= z.recip();
1242 }
1243 let m = x[i].abs();
1244 if m > big {
1245 let r = m.recip();
1246 let rc: T = r.to_cplx();
1247 for j in 0..x.nrows() {
1248 x[j] *= rc.copy();
1249 }
1250 scale *= r;
1251 }
1252 }
1253 scale
1254 } else {
1255 let mid = n / 2;
1256 let (A00, A01, _, A11) = A.split_at(mid, mid);
1257 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
1258 let f1 = solve_shifted_upper_triangular_system(
1259 A11,
1260 conj_A,
1261 p.copy(),
1262 x1.rb_mut(),
1263 eps_norm.copy(),
1264 par.reborrow(),
1265 params,
1266 );
1267 let f1c: T = f1.to_cplx();
1268 for j in 0..x0.nrows() {
1269 x0[j] *= f1c.copy();
1270 }
1271 linalg::matmul::matmul_with_conj(
1272 x0.rb_mut().as_mat_mut(),
1273 Accum::Add,
1274 A01,
1275 conj_A,
1276 x1.rb().as_mat(),
1277 Conj::No,
1278 -one(),
1279 par.reborrow(),
1280 );
1281 let f0 = solve_shifted_upper_triangular_system(
1282 A00,
1283 conj_A,
1284 p.copy(),
1285 x0.rb_mut(),
1286 eps_norm,
1287 par,
1288 params,
1289 );
1290 let f0c: T = f0.to_cplx();
1291 for j in 0..x1.nrows() {
1292 x1[j] *= f0c.copy();
1293 }
1294 f0 * f1
1295 }
1296}
1297pub fn evd_scratch<T: ComplexField>(
1300 dim: usize,
1301 eigen_left: ComputeEigenvectors,
1302 eigen_right: ComputeEigenvectors,
1303 par: ParShape,
1304 params: Spec<EvdParams, T>,
1305) -> StackReq {
1306 let n = dim;
1307 if n == 0 {
1308 return StackReq::EMPTY;
1309 }
1310 let compute_eigen = eigen_left == ComputeEigenvectors::Yes
1311 || eigen_right == ComputeEigenvectors::Yes;
1312 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(
1313 n - 1,
1314 n - 1,
1315 );
1316 let H = temp_mat_scratch::<T>(n, n);
1317 let X = H;
1318 let Z = temp_mat_scratch::<T>(n, if compute_eigen { n } else { 0 });
1319 let householder = temp_mat_scratch::<T>(bs, n);
1320 let apply = linalg::householder::apply_block_householder_sequence_on_the_right_in_place_scratch::<T>(n - 1, bs, n - 1);
1321 StackReq::all_of(&[
1322 H,
1323 Z,
1324 StackReq::any_of(&[
1325 householder.and(
1326 hessenberg::hessenberg_in_place_scratch::<T>(
1327 n,
1328 bs,
1329 par,
1330 params.hessenberg.into(),
1331 )
1332 .or(apply),
1333 ),
1334 schur::multishift_qr_scratch::<T>(
1335 n,
1336 n,
1337 compute_eigen,
1338 compute_eigen,
1339 par,
1340 params.schur,
1341 ),
1342 X,
1343 ]),
1344 ])
1345}
1346pub fn hessenberg_schur_scratch<T: ComplexField>(
1358 dim: usize,
1359 compute_z: bool,
1360 par: ParShape,
1361 params: Spec<SchurParams, T>,
1362) -> StackReq {
1363 schur::multishift_qr_scratch::<T>(dim, dim, true, compute_z, par, *params)
1364}
1365pub fn hessenberg_schur_real<T: RealField>(
1407 mut h: MatMut<'_, T>,
1408 z: Option<MatMut<'_, T>>,
1409 mut wr: ColMut<'_, T>,
1410 mut wi: ColMut<'_, T>,
1411 par: Par,
1412 stack: &mut MemStack,
1413 params: Spec<SchurParams, T>,
1414) -> Result<(), EvdError> {
1415 let n = h.nrows();
1416 assert!(h.ncols() == n && wr.nrows() == n && wi.nrows() == n);
1417 if let Some(z) = z.rb() {
1418 assert!(z.nrows() == n && z.ncols() == n);
1419 }
1420 if !h.is_all_finite() {
1421 return Err(EvdError::NoConvergence);
1422 }
1423 if n <= 1 {
1424 if n == 1 {
1425 wr[0] = h[(0, 0)].copy();
1426 wi[0] = zero();
1427 }
1428 return Ok(());
1429 }
1430 let (info, _, _) = schur::real_schur::multishift_qr(
1431 true,
1432 h.rb_mut(),
1433 z,
1434 wr,
1435 wi,
1436 0,
1437 n,
1438 par,
1439 stack,
1440 *params,
1441 );
1442 if info != 0 {
1443 return Err(EvdError::NoConvergence);
1444 }
1445 for j in 0..n {
1447 for i in j + 2..n {
1448 h[(i, j)] = zero();
1449 }
1450 }
1451 Ok(())
1452}
1453pub fn hessenberg_schur_cplx<T: ComplexField>(
1490 mut h: MatMut<'_, T>,
1491 z: Option<MatMut<'_, T>>,
1492 mut w: ColMut<'_, T>,
1493 par: Par,
1494 stack: &mut MemStack,
1495 params: Spec<SchurParams, T>,
1496) -> Result<(), EvdError> {
1497 assert!(!T::IS_REAL);
1498 let n = h.nrows();
1499 assert!(h.ncols() == n && w.nrows() == n);
1500 if let Some(z) = z.rb() {
1501 assert!(z.nrows() == n && z.ncols() == n);
1502 }
1503 if !h.is_all_finite() {
1504 return Err(EvdError::NoConvergence);
1505 }
1506 if n <= 1 {
1507 if n == 1 {
1508 w[0] = h[(0, 0)].copy();
1509 }
1510 return Ok(());
1511 }
1512 let (info, _, _) = schur::complex_schur::multishift_qr(
1513 true,
1514 h.rb_mut(),
1515 z,
1516 w,
1517 0,
1518 n,
1519 par,
1520 stack,
1521 *params,
1522 );
1523 if info != 0 {
1524 return Err(EvdError::NoConvergence);
1525 }
1526 for j in 0..n {
1528 for i in j + 1..n {
1529 h[(i, j)] = zero();
1530 }
1531 }
1532 Ok(())
1533}
1534#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1544pub struct SchurReorderError {
1545 pub position: usize,
1547}
1548pub fn reorder_schur_scratch<T: ComplexField>(dim: usize) -> StackReq {
1558 StackReq::all_of(&[
1559 temp_mat_scratch::<T>(4, dim),
1560 temp_mat_scratch::<T>(dim, 4),
1561 temp_mat_scratch::<T>(dim, 4),
1562 ])
1563}
1564pub fn reorder_schur_real<T: RealField>(
1607 mut t: MatMut<'_, T>,
1608 mut q: Option<MatMut<'_, T>>,
1609 mut ifst: usize,
1610 mut ilst: usize,
1611 _par: Par,
1612 stack: &mut MemStack,
1613) -> Result<usize, SchurReorderError> {
1614 let n = t.nrows();
1615 assert!(t.ncols() == n && ifst < n && ilst < n);
1616 if let Some(q) = q.rb() {
1617 assert!(q.nrows() == n && q.ncols() == n);
1618 }
1619 let (mut saved_rows, stack) = temp_mat_zeroed::<T, _, _>(4, n, stack);
1620 let (mut saved_cols, stack) = temp_mat_zeroed::<T, _, _>(n, 4, stack);
1621 let (mut saved_q, _) = temp_mat_zeroed::<T, _, _>(n, 4, stack);
1622 let mut saved_rows = saved_rows.as_mat_mut();
1623 let mut saved_cols = saved_cols.as_mat_mut();
1624 let mut saved_q = saved_q.as_mat_mut();
1625 if ifst > 0 && t[(ifst, ifst - 1)] != zero() {
1626 ifst -= 1;
1627 }
1628 if ilst > 0 && t[(ilst, ilst - 1)] != zero() {
1629 ilst -= 1;
1630 }
1631 let block_size = |t: MatRef<'_, T>, i: usize| {
1632 if i + 1 < n && t[(i + 1, i)] != zero() {
1633 2
1634 } else {
1635 1
1636 }
1637 };
1638 let moving = block_size(t.rb(), ifst);
1639 if ifst < ilst {
1640 let dest = block_size(t.rb(), ilst);
1641 ilst = ilst + dest - moving;
1642 }
1643 let mut here = ifst;
1644 while here != ilst {
1645 let down = here < ilst;
1646 let next = if down {
1647 block_size(t.rb(), here + moving)
1648 } else if here > 1 && t[(here - 1, here - 2)] != zero() {
1649 2
1650 } else {
1651 1
1652 };
1653 let start = if down { here } else { here - next };
1654 let (n1, n2) = if down { (moving, next) } else { (next, moving) };
1655 let k = n1 + n2;
1660 let w = n - start;
1661 let h = start + k;
1662 saved_rows
1663 .rb_mut()
1664 .submatrix_mut(0, 0, k, w)
1665 .copy_from(t.rb().submatrix(start, start, k, w));
1666 saved_cols
1667 .rb_mut()
1668 .submatrix_mut(0, 0, h, k)
1669 .copy_from(t.rb().submatrix(0, start, h, k));
1670 if let Some(q) = q.rb() {
1671 saved_q
1672 .rb_mut()
1673 .submatrix_mut(0, 0, n, k)
1674 .copy_from(q.submatrix(0, start, n, k));
1675 }
1676 let status = schur::real_schur::schur_swap_checked(
1677 t.rb_mut(),
1678 q.rb_mut(),
1679 start,
1680 n1,
1681 n2,
1682 true,
1683 );
1684 let finite = t.rb().submatrix(start, start, k, w).is_all_finite()
1685 && t.rb().submatrix(0, start, h, k).is_all_finite()
1686 && q.rb()
1687 .is_none_or(|q| q.submatrix(0, start, n, k).is_all_finite());
1688 if status != 0 || !finite {
1689 t.rb_mut()
1690 .submatrix_mut(0, start, h, k)
1691 .copy_from(saved_cols.rb().submatrix(0, 0, h, k));
1692 t.rb_mut()
1693 .submatrix_mut(start, start, k, w)
1694 .copy_from(saved_rows.rb().submatrix(0, 0, k, w));
1695 if let Some(q) = q.rb_mut() {
1696 q.submatrix_mut(0, start, n, k)
1697 .copy_from(saved_q.rb().submatrix(0, 0, n, k));
1698 }
1699 return Err(SchurReorderError { position: here });
1700 }
1701 here = if down { here + next } else { here - next };
1702 }
1703 Ok(here)
1704}
1705pub fn reorder_schur_cplx<T: ComplexField>(
1732 mut t: MatMut<'_, T>,
1733 mut q: Option<MatMut<'_, T>>,
1734 ifst: usize,
1735 ilst: usize,
1736 _par: Par,
1737 _stack: &mut MemStack,
1738) -> usize {
1739 let n = t.nrows();
1740 assert!(t.ncols() == n && ifst < n && ilst < n);
1741 if let Some(q) = q.rb() {
1742 assert!(q.nrows() == n && q.ncols() == n);
1743 }
1744 let mut here = ifst;
1745 while here != ilst {
1746 let start = if here < ilst { here } else { here - 1 };
1747 schur::complex_schur::schur_swap(t.rb_mut(), q.rb_mut(), start);
1748 here = if here < ilst { here + 1 } else { here - 1 };
1749 }
1750 here
1751}
1752pub fn eigenvectors_from_schur_scratch<T: ComplexField>(
1761 dim: usize,
1762) -> StackReq {
1763 temp_mat_scratch::<T>(dim, dim)
1764}
1765pub fn eigenvectors_from_schur_real<T: RealField>(
1798 t: MatRef<'_, T>,
1799 q: Option<MatRef<'_, T>>,
1800 mut x: MatMut<'_, T>,
1801 mut par: Par,
1802 stack: &mut MemStack,
1803 params: Spec<EvdFromSchurParams, T>,
1804) -> Result<(), EvdError> {
1805 let n = t.nrows();
1806 assert!(t.ncols() == n && x.nrows() == n && x.ncols() == n);
1807 if let Some(q) = q {
1808 assert!(q.nrows() == n && q.ncols() == n);
1809 }
1810 if !t.is_all_finite() {
1811 return Err(EvdError::NoConvergence);
1812 }
1813 let (mut v, _) = temp_mat_zeroed::<T, _, _>(n, n, stack);
1814 let mut v = v.as_mat_mut();
1815 evd_from_real_schur_imp(t, v.rb_mut(), par.reborrow(), *params);
1816 if let Some(q) = q {
1817 linalg::matmul::matmul(
1818 x.rb_mut(),
1819 Accum::Replace,
1820 q,
1821 v.rb(),
1822 one(),
1823 par,
1824 );
1825 } else {
1826 x.copy_from(v.rb());
1827 }
1828 normalize_eigenvectors(x.rb_mut(), t);
1829 if eigenvectors_ok(x.rb()) {
1830 Ok(())
1831 } else {
1832 Err(EvdError::NoConvergence)
1833 }
1834}
1835pub fn eigenvectors_from_schur_cplx<T: ComplexField>(
1867 t: MatRef<'_, T>,
1868 q: Option<MatRef<'_, T>>,
1869 mut x: MatMut<'_, T>,
1870 mut par: Par,
1871 stack: &mut MemStack,
1872 params: Spec<EvdFromSchurParams, T>,
1873) -> Result<(), EvdError> {
1874 let n = t.nrows();
1875 assert!(t.ncols() == n && x.nrows() == n && x.ncols() == n);
1876 if let Some(q) = q {
1877 assert!(q.nrows() == n && q.ncols() == n);
1878 }
1879 if !t.is_all_finite() {
1880 return Err(EvdError::NoConvergence);
1881 }
1882 let (mut v, _) = temp_mat_zeroed::<T, _, _>(n, n, stack);
1883 let mut v = v.as_mat_mut();
1884 evd_from_cplx_schur_imp(t, Conj::No, v.rb_mut(), par.reborrow(), *params);
1885 if let Some(q) = q {
1886 linalg::matmul::matmul(
1887 x.rb_mut(),
1888 Accum::Replace,
1889 q,
1890 v.rb(),
1891 one(),
1892 par,
1893 );
1894 } else {
1895 x.copy_from(v.rb());
1896 }
1897 normalize_eigenvectors(x.rb_mut(), t);
1898 if eigenvectors_ok(x.rb()) {
1899 Ok(())
1900 } else {
1901 Err(EvdError::NoConvergence)
1902 }
1903}
1904fn geev_scaling<T: ComplexField>(A: MatRef<'_, T>) -> Option<T::Real> {
1917 let n = A.nrows();
1918 let mut anrm = zero::<T::Real>();
1919 for j in 0..n {
1920 for i in 0..n {
1921 anrm = anrm.fmax(A[(i, j)].real().abs()).fmax(A[(i, j)].imag().abs());
1922 }
1923 }
1924 let smlnum = min_positive::<T::Real>().sqrt() / eps::<T::Real>();
1925 let bignum = smlnum.recip();
1926 let two = from_f64::<T::Real>(2.0);
1927 let half = from_f64::<T::Real>(0.5);
1928 if anrm == zero::<T::Real>() || (anrm >= smlnum && anrm <= bignum) {
1929 return None;
1930 }
1931 let mut f = one::<T::Real>();
1932 if anrm < smlnum {
1933 while &anrm * &f < smlnum {
1935 f = f * &two;
1936 }
1937 } else {
1938 while &anrm * &f > bignum {
1940 f = f * ½
1941 }
1942 }
1943 Some(f)
1944}
1945fn evd_imp<T: ComplexField>(
1946 A: MatRef<'_, T>,
1947 s: ColMut<'_, T>,
1948 s_im: Option<ColMut<'_, T>>,
1949 u_left: Option<MatMut<'_, T>>,
1950 u_right: Option<MatMut<'_, T>>,
1951 mut par: Par,
1952 stack: &mut MemStack,
1953 params: EvdParams,
1954) -> Result<(), EvdError> {
1955 let n = A.nrows();
1956 if n == 0 {
1957 return Ok(());
1958 }
1959 for j in 0..n {
1960 for i in 0..n {
1961 if !A[(i, j)].is_finite() {
1962 return Err(EvdError::NoConvergence);
1963 }
1964 }
1965 }
1966 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(
1967 n - 1,
1968 n - 1,
1969 );
1970 let mut s = s;
1971 let mut s_im = s_im;
1972 let (mut H, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1973 let (mut Z, stack) = unsafe {
1974 temp_mat_uninit::<T, _, _>(
1975 n,
1976 if u_left.is_some() || u_right.is_some() {
1977 n
1978 } else {
1979 0
1980 },
1981 stack,
1982 )
1983 };
1984 let mut H = H.as_mat_mut();
1985 let mut Z = if u_left.is_some() || u_right.is_some() {
1986 Some(Z.as_mat_mut())
1987 } else {
1988 None
1989 };
1990 H.copy_from(A);
1991 let scaling = geev_scaling(A);
1993 if let Some(f) = &scaling {
1994 z!(H.rb_mut()).for_each(|uz!(h)| *h = h.mul_real(f));
1995 }
1996 {
1997 let (mut householder, stack) =
1998 unsafe { temp_mat_uninit::<T, _, _>(bs, n - 1, stack) };
1999 let mut householder = householder.as_mat_mut();
2000 hessenberg::hessenberg_in_place(
2001 H.rb_mut(),
2002 householder.rb_mut(),
2003 par.reborrow(),
2004 stack,
2005 params.hessenberg.into(),
2006 );
2007 if let Some(mut Z) = Z.rb_mut() {
2008 Z.fill(zero());
2009 Z.rb_mut().diagonal_mut().fill(one());
2010 linalg::householder::apply_block_householder_sequence_on_the_right_in_place_with_conj(
2011 H.rb().submatrix(1, 0, n - 1, n - 1),
2012 householder.rb(),
2013 Conj::No,
2014 Z.rb_mut().submatrix_mut(1, 1, n - 1, n - 1),
2015 par.reborrow(),
2016 stack,
2017 );
2018 }
2019 for j in 0..n {
2020 for i in j + 2..n {
2021 H[(i, j)] = zero();
2022 }
2023 }
2024 }
2025 if const { T::IS_REAL } {
2026 let (info, _, _) = schur::real_schur::multishift_qr::<T::Real>(
2027 unsafe { core::mem::transmute(Z.is_some()) },
2028 unsafe { core::mem::transmute(H.rb_mut()) },
2029 unsafe { core::mem::transmute(Z.rb_mut()) },
2030 unsafe { core::mem::transmute(s.rb_mut()) },
2031 unsafe { core::mem::transmute(s_im.rb_mut().unwrap()) },
2032 0,
2033 n,
2034 par.reborrow(),
2035 stack,
2036 params.schur,
2037 );
2038 if info != 0 {
2039 return Err(EvdError::NoConvergence);
2040 }
2041 } else {
2042 let (info, _, _) = schur::complex_schur::multishift_qr::<T>(
2043 Z.is_some(),
2044 H.rb_mut(),
2045 Z.rb_mut(),
2046 s.rb_mut(),
2047 0,
2048 n,
2049 par.reborrow(),
2050 stack,
2051 params.schur,
2052 );
2053 if info != 0 {
2054 return Err(EvdError::NoConvergence);
2055 }
2056 }
2057 let H = H.rb();
2058 if let (Some(mut u), Some(Z)) = (u_right, Z.rb()) {
2059 let (mut X, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
2060 let mut X = X.as_mat_mut();
2061 if const { T::IS_REAL } {
2062 evd_from_real_schur_imp::<T::Real>(
2063 unsafe { core::mem::transmute(H) },
2064 unsafe { core::mem::transmute(X.rb_mut()) },
2065 par.reborrow(),
2066 params.evd_from_schur,
2067 );
2068 } else {
2069 evd_from_cplx_schur_imp::<T>(
2070 H,
2071 Conj::No,
2072 X.rb_mut(),
2073 par.reborrow(),
2074 params.evd_from_schur,
2075 );
2076 }
2077 linalg::matmul::triangular::matmul(
2078 u.rb_mut(),
2079 BlockStructure::Rectangular,
2080 Accum::Replace,
2081 Z,
2082 BlockStructure::Rectangular,
2083 X.rb(),
2084 BlockStructure::TriangularUpper,
2085 one(),
2086 par.reborrow(),
2087 );
2088 normalize_eigenvectors(u.rb_mut(), H);
2089 if !eigenvectors_ok(u.rb()) {
2090 return Err(EvdError::NoConvergence);
2091 }
2092 }
2093 if let (Some(mut u), Some(Z)) = (u_left, Z.rb()) {
2094 let (mut X, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
2095 let mut X = X.as_mat_mut().reverse_rows_mut();
2096 if const { T::IS_REAL } {
2097 evd_from_real_schur_imp::<T::Real>(
2098 unsafe {
2099 core::mem::transmute(H.transpose().reverse_rows_and_cols())
2100 },
2101 unsafe { core::mem::transmute(X.rb_mut()) },
2102 par.reborrow(),
2103 params.evd_from_schur,
2104 );
2105 } else {
2106 evd_from_cplx_schur_imp::<T>(
2107 H.transpose().reverse_rows_and_cols(),
2108 Conj::Yes,
2109 X.rb_mut(),
2110 par.reborrow(),
2111 params.evd_from_schur,
2112 );
2113 }
2114 linalg::matmul::triangular::matmul(
2115 u.rb_mut(),
2116 BlockStructure::Rectangular,
2117 Accum::Replace,
2118 Z,
2119 BlockStructure::Rectangular,
2120 X.rb().reverse_rows_and_cols(),
2121 BlockStructure::TriangularLower,
2122 one(),
2123 par,
2124 );
2125 normalize_eigenvectors(u.rb_mut(), H);
2126 if !eigenvectors_ok(u.rb()) {
2127 return Err(EvdError::NoConvergence);
2128 }
2129 }
2130 if let Some(f) = &scaling {
2131 let f = f.recip();
2132 z!(s.rb_mut()).for_each(|uz!(x)| *x = x.mul_real(&f));
2133 if let Some(s_im) = s_im.rb_mut() {
2134 z!(s_im).for_each(|uz!(x)| *x = x.mul_real(&f));
2135 }
2136 }
2137 Ok(())
2138}
2139#[track_caller]
2148pub fn evd_cplx<T: RealField>(
2149 A: MatRef<'_, Complex<T>>,
2150 s: DiagMut<'_, Complex<T>>,
2151 u_left: Option<MatMut<'_, Complex<T>>>,
2152 u_right: Option<MatMut<'_, Complex<T>>>,
2153 par: Par,
2154 stack: &mut MemStack,
2155 params: Spec<EvdParams, Complex<T>>,
2156) -> Result<(), EvdError> {
2157 let n = A.nrows();
2158 assert!(all(A.nrows() == n, A.ncols() == n, s.dim() == n));
2159 if let Some(u) = u_left.rb() {
2160 assert!(all(u.nrows() == n, u.ncols() == n));
2161 }
2162 if let Some(u) = u_right.rb() {
2163 assert!(all(u.nrows() == n, u.ncols() == n));
2164 }
2165 evd_imp(
2166 A,
2167 s.column_vector_mut(),
2168 None,
2169 u_left,
2170 u_right,
2171 par,
2172 stack,
2173 params.config,
2174 )
2175}
2176#[track_caller]
2188pub fn evd_real<T: RealField>(
2189 A: MatRef<'_, T>,
2190 s_re: DiagMut<'_, T>,
2191 s_im: DiagMut<'_, T>,
2192 u_left: Option<MatMut<'_, T>>,
2193 u_right: Option<MatMut<'_, T>>,
2194 par: Par,
2195 stack: &mut MemStack,
2196 params: Spec<EvdParams, T>,
2197) -> Result<(), EvdError> {
2198 let n = A.nrows();
2199 assert!(all(
2200 A.nrows() == n,
2201 A.ncols() == n,
2202 s_re.dim() == n,
2203 s_im.dim() == n
2204 ));
2205 if let Some(u) = u_left.rb() {
2206 assert!(all(u.nrows() == n, u.ncols() == n));
2207 }
2208 if let Some(u) = u_right.rb() {
2209 assert!(all(u.nrows() == n, u.ncols() == n));
2210 }
2211 evd_imp(
2212 A,
2213 s_re.column_vector_mut(),
2214 Some(s_im.column_vector_mut()),
2215 u_left,
2216 u_right,
2217 par,
2218 stack,
2219 params.config,
2220 )
2221}
2222#[cfg(test)]
2223mod general_tests {
2224 use super::*;
2225 use crate::assert;
2226 use crate::stats::prelude::*;
2227 use crate::utils::approx::*;
2228 use dyn_stack::MemBuffer;
2229 fn test_cplx_evd(mat: MatRef<'_, c64>) {
2230 let n = mat.nrows();
2231 let params = Spec::new(EvdParams {
2232 hessenberg: auto!(c64),
2233 schur: auto!(c64),
2234 evd_from_schur: EvdFromSchurParams {
2235 recursion_threshold: 8,
2236 ..auto!(c64)
2237 },
2238 ..auto!(c64)
2239 });
2240 let approx_eq = CwiseMat(ApproxEq::eps() * (8.0 * n as f64).sqrt());
2241 let mut s = Mat::zeros(n, n);
2242 {
2243 let mut ul = Mat::zeros(n, n);
2244 let mut ur = Mat::zeros(n, n);
2245 evd_cplx(
2246 mat.as_ref(),
2247 s.as_mut().diagonal_mut(),
2248 Some(ul.as_mut()),
2249 Some(ur.as_mut()),
2250 Par::Seq,
2251 MemStack::new(&mut MemBuffer::new(evd_scratch::<c64>(
2252 n,
2253 ComputeEigenvectors::Yes,
2254 ComputeEigenvectors::Yes,
2255 ParShape::Seq,
2256 params,
2257 ))),
2258 params,
2259 )
2260 .unwrap();
2261 assert!(& ur * & s ~ mat * & ur);
2262 assert!(& s * ul.adjoint() ~ ul.adjoint() * mat);
2263 }
2264 }
2265 fn test_real_evd(mat: MatRef<'_, f64>) {
2266 let n = mat.nrows();
2267 let params = Spec::new(EvdParams {
2268 hessenberg: auto!(f64),
2269 schur: auto!(f64),
2270 evd_from_schur: EvdFromSchurParams {
2271 recursion_threshold: 8,
2272 ..auto!(f64)
2273 },
2274 ..auto!(f64)
2275 });
2276 let approx_eq =
2277 CwiseMat(ApproxEq::<f64>::eps() * (8.0 * n as f64).sqrt());
2278 let mut s_re = Diag::zeros(n);
2279 let mut s_im = Diag::zeros(n);
2280 {
2281 let mut ul = Mat::zeros(n, n);
2282 let mut ur = Mat::zeros(n, n);
2283 evd_real(
2284 mat.as_ref(),
2285 s_re.as_mut(),
2286 s_im.as_mut(),
2287 Some(ul.as_mut()),
2288 Some(ur.as_mut()),
2289 Par::Seq,
2290 MemStack::new(&mut MemBuffer::new(evd_scratch::<f64>(
2291 n,
2292 ComputeEigenvectors::Yes,
2293 ComputeEigenvectors::Yes,
2294 ParShape::Seq,
2295 params,
2296 ))),
2297 params,
2298 )
2299 .unwrap();
2300 let mut i = 0;
2301 while i < n {
2302 if s_im[i] == 0.0 {
2303 let ur = ur.col(i);
2304 let ul = ul.col(i);
2305 let s = Scale(s_re[i]);
2306 assert!((& ur * s).as_mat() ~ (mat * & ur).as_mat());
2307 assert!(
2308 (& ul.adjoint() * s).as_mat() ~ (& ul.adjoint() * mat).as_mat()
2309 );
2310 i += 1;
2311 } else {
2312 let re = ur.col(i);
2313 let im = ur.col(i + 1);
2314 let ur = &Col::from_fn(n, |i| c64::new(re[i], im[i]));
2315 let re = ul.col(i);
2316 let im = ul.col(i + 1);
2317 let ul = &Col::from_fn(n, |i| c64::new(re[i], im[i]));
2318 let mat =
2319 &Mat::from_fn(n, n, |i, j| c64::from(mat[(i, j)]));
2320 let s = Scale(c64::new(s_re[i], s_im[i]));
2321 let approx_eq =
2322 CwiseMat(ApproxEq::eps() * (8.0 * n as f64).sqrt());
2323 assert!((ur * s).as_mat() ~ (mat * ur).as_mat());
2324 assert!((ul.adjoint() * s).as_mat() ~ (ul.adjoint() * mat).as_mat());
2325 i += 2;
2326 }
2327 }
2328 }
2329 }
2330 #[test]
2331 fn test_cplx() {
2332 let rng = &mut StdRng::seed_from_u64(1);
2333 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
2334 let mat = CwiseMatDistribution {
2335 nrows: n,
2336 ncols: n,
2337 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
2338 }
2339 .rand::<Mat<c64>>(rng);
2340 test_cplx_evd(mat.as_ref());
2341 }
2342 }
2343 #[test]
2344 fn test_real() {
2345 let rng = &mut StdRng::seed_from_u64(0);
2346 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
2347 let mat = CwiseMatDistribution {
2348 nrows: n,
2349 ncols: n,
2350 dist: StandardNormal,
2351 }
2352 .rand::<Mat<f64>>(rng);
2353 test_real_evd(mat.as_ref());
2354 }
2355 }
2356}
2357#[cfg(test)]
2358mod eig_2131_tests {
2359 use super::*;
2365 use crate::assert;
2366 use dyn_stack::MemBuffer;
2367
2368 fn relative_residual<T: RealField>(
2371 mat: MatRef<'_, T>,
2372 v: ColRef<'_, T>,
2373 lambda: T,
2374 ) -> T {
2375 let n = mat.nrows();
2376 let mut anorm = zero::<T>();
2377 for j in 0..n {
2378 let mut col = zero::<T>();
2379 for i in 0..n {
2380 col += mat[(i, j)].abs1();
2381 }
2382 if col > anorm {
2383 anorm = col;
2384 }
2385 }
2386 let mut vnorm2 = zero::<T>();
2387 let mut rnorm2 = zero::<T>();
2388 for i in 0..n {
2389 let mut acc = zero::<T>();
2390 for j in 0..n {
2391 acc += &mat[(i, j)] * &v[j];
2392 }
2393 acc -= &v[i] * λ
2394 rnorm2 += acc.abs2();
2395 vnorm2 += v[i].abs2();
2396 }
2397 rnorm2.sqrt() / (anorm * vnorm2.sqrt())
2398 }
2399
2400 fn evd_real_vectors<T: RealField>(
2401 mat: MatRef<'_, T>,
2402 params: Spec<EvdParams, T>,
2403 ) -> (Diag<T>, Diag<T>, Mat<T>, Mat<T>) {
2404 let n = mat.nrows();
2405 let mut s_re = Diag::zeros(n);
2406 let mut s_im = Diag::zeros(n);
2407 let mut u = Mat::zeros(n, n);
2408 let mut ul = Mat::zeros(n, n);
2409 let mut mem = MemBuffer::new(evd_scratch::<T>(
2410 n,
2411 ComputeEigenvectors::Yes,
2412 ComputeEigenvectors::Yes,
2413 ParShape::Seq,
2414 params,
2415 ));
2416 evd_real(
2417 mat,
2418 s_re.as_mut(),
2419 s_im.as_mut(),
2420 Some(ul.as_mut()),
2421 Some(u.as_mut()),
2422 Par::Seq,
2423 MemStack::new(&mut mem),
2424 params,
2425 )
2426 .unwrap();
2427 (s_re, s_im, u, ul)
2428 }
2429
2430 fn jordan<T: RealField>(n: usize) -> Mat<T> {
2432 let two = from_f64::<T>(2.0);
2433 let one = one::<T>();
2434 Mat::<T>::from_fn(n, n, |i, j| {
2435 if i == j {
2436 two.copy()
2437 } else if j == i + 1 {
2438 one.copy()
2439 } else {
2440 zero::<T>()
2441 }
2442 })
2443 }
2444
2445 fn check_jordan<T: RealField>(n: usize, threshold: usize, tol: f64) {
2446 let params = Spec::new(EvdParams {
2447 evd_from_schur: EvdFromSchurParams {
2448 recursion_threshold: threshold,
2449 ..auto!(T)
2450 },
2451 ..auto!(T)
2452 });
2453 let mat = jordan::<T>(n);
2454 let (s_re, _, u, _) = evd_real_vectors(mat.as_ref(), params);
2455 let tol = from_f64::<T>(tol);
2456 for j in 0..n {
2457 let v = u.col(j);
2458 assert!(v.iter().all(|x| x.is_finite()));
2459 assert!((v.norm_l2() - one::<T>()).abs() < from_f64::<T>(1e-5));
2460 assert!(relative_residual(mat.as_ref(), v, s_re[j].copy()) < tol);
2461 }
2462 }
2463
2464 #[test]
2465 fn jordan_eigenvectors_are_finite_and_unit_norm() {
2466 for threshold in [3usize, 64] {
2471 for n in [2, 4, 8, 20, 26, 30, 64, 65, 100] {
2472 check_jordan::<f64>(n, threshold, 1e-10);
2473 }
2474 for n in [2, 4, 8, 16, 30] {
2475 check_jordan::<f32>(n, threshold, 1e-3);
2476 }
2477 }
2478 }
2479
2480 #[test]
2481 fn conjugate_pair_eigenvector_has_unit_norm() {
2482 fn run<T: RealField>(threshold: usize) {
2486 let n = 6;
2487 let mat = Mat::<T>::from_fn(n, n, |i, j| {
2488 if i == j {
2489 if i >= 4 {
2490 zero::<T>()
2491 } else {
2492 from_f64::<T>((i + 1) as f64)
2493 }
2494 } else if i == 4 && j == 5 {
2495 one::<T>()
2496 } else if i == 5 && j == 4 {
2497 -one::<T>()
2498 } else if i + 2 <= j {
2499 from_f64::<T>(0.25)
2500 } else {
2501 zero::<T>()
2502 }
2503 });
2504 let params = Spec::new(EvdParams {
2505 evd_from_schur: EvdFromSchurParams {
2506 recursion_threshold: threshold,
2507 ..auto!(T)
2508 },
2509 ..auto!(T)
2510 });
2511 let (s_re, s_im, u, ul) = evd_real_vectors(mat.as_ref(), params);
2512 let tol = from_f64::<T>(1e-5);
2513 let mut j = 0;
2515 while j < n {
2516 let v = u.col(j);
2517 let vl = ul.col(j);
2518 if s_im[j] == zero::<T>() {
2519 assert!((v.norm_l2() - one::<T>()).abs() < tol);
2520 assert!((vl.norm_l2() - one::<T>()).abs() < tol);
2521 assert!(
2522 relative_residual(mat.as_ref(), v, s_re[j].copy())
2523 < from_f64::<T>(1e-4)
2524 );
2525 j += 1;
2526 } else {
2527 let nrm = v.norm_l2().hypot(&u.col(j + 1).norm_l2());
2528 assert!((nrm - one::<T>()).abs() < tol);
2529 let nrm = vl.norm_l2().hypot(&ul.col(j + 1).norm_l2());
2530 assert!((nrm - one::<T>()).abs() < tol);
2531 j += 2;
2532 }
2533 }
2534 }
2535 for threshold in [4usize, 64] {
2536 run::<f64>(threshold);
2537 run::<f32>(threshold);
2538 }
2539 }
2540
2541 #[test]
2542 fn complex_eigenvectors_have_unit_norm() {
2543 fn run<T: RealField>(threshold: usize) {
2545 let n = 6;
2546 let mat = Mat::<Complex<T>>::from_fn(n, n, |i, j| {
2547 Complex::new(
2548 from_f64::<T>(i as f64)
2549 + from_f64::<T>(j as f64) * from_f64::<T>(0.1),
2550 from_f64::<T>(i as f64 - j as f64) * from_f64::<T>(0.3),
2551 )
2552 });
2553 let params = Spec::new(EvdParams {
2554 evd_from_schur: EvdFromSchurParams {
2555 recursion_threshold: threshold,
2556 ..auto!(Complex<T>)
2557 },
2558 ..auto!(Complex<T>)
2559 });
2560 let mut s = Diag::zeros(n);
2561 let mut u = Mat::zeros(n, n);
2562 let mut mem = MemBuffer::new(evd_scratch::<Complex<T>>(
2563 n,
2564 ComputeEigenvectors::No,
2565 ComputeEigenvectors::Yes,
2566 ParShape::Seq,
2567 params,
2568 ));
2569 evd_cplx(
2570 mat.as_ref(),
2571 s.as_mut(),
2572 None,
2573 Some(u.as_mut()),
2574 Par::Seq,
2575 MemStack::new(&mut mem),
2576 params,
2577 )
2578 .unwrap();
2579 let tol = from_f64::<T>(1e-5);
2580 for j in 0..n {
2581 assert!((u.col(j).norm_l2() - one::<T>()).abs() < tol);
2582 }
2583 }
2584 for threshold in [4usize, 64] {
2585 run::<f64>(threshold);
2586 run::<f32>(threshold);
2587 }
2588 }
2589}
2590#[cfg(test)]
2591mod range_limit_vector_tests {
2592 use super::*;
2602 use crate::assert;
2603 use dyn_stack::MemBuffer;
2604
2605 const SHIFT_2X2: [[f64; 2]; 2] = [[1.0, 1.0], [0.0, 0.5]];
2607 const PAIR_3X3: [[f64; 3]; 3] =
2610 [[0.0, -1.0, 1.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.5]];
2611 const TWO_PAIRS_4X4: [[f64; 4]; 4] = [
2613 [0.0, -1.0, 1.0, 0.0],
2614 [1.0, 0.0, 0.0, 0.0],
2615 [0.0, 0.0, 0.0, -0.5],
2616 [0.0, 0.0, 0.5, 0.0],
2617 ];
2618 const JORDAN_2X2: [[f64; 2]; 2] = [[1.0, 1.0], [0.0, 1.0]];
2620
2621 fn unit<T: RealField, const N: usize>(
2622 mat: &[[f64; N]; N],
2623 ) -> Mat<Complex<T>> {
2624 Mat::from_fn(N, N, |i, j| {
2625 Complex::new(from_f64::<T>(mat[i][j]), zero::<T>())
2626 })
2627 }
2628
2629 fn scaled_real<T: RealField>(
2631 unit: MatRef<'_, Complex<T>>,
2632 scale: &T,
2633 ) -> Mat<T> {
2634 Mat::from_fn(unit.nrows(), unit.ncols(), |i, j| {
2635 &unit[(i, j)].re * scale
2636 })
2637 }
2638
2639 fn scaled_cplx<T: RealField>(
2641 unit: MatRef<'_, Complex<T>>,
2642 scale: &T,
2643 ) -> Mat<Complex<T>> {
2644 Mat::from_fn(unit.nrows(), unit.ncols(), |i, j| {
2645 Complex::new(&unit[(i, j)].re * scale, &unit[(i, j)].im * scale)
2646 })
2647 }
2648
2649 fn complex_unit<T: RealField>() -> Mat<Complex<T>> {
2653 let z = |re: f64, im: f64| {
2654 Complex::new(from_f64::<T>(re), from_f64::<T>(im))
2655 };
2656 Mat::from_fn(3, 3, |i, j| match (i, j) {
2657 (0, 1) => z(-1.0, 0.5),
2658 (0, 2) => z(1.0, -1.0),
2659 (1, 1) => z(0.5, 0.0),
2660 (2, 2) => z(0.25, 0.0),
2661 _ => z(0.0, 0.0),
2662 })
2663 }
2664
2665 fn residual<T: RealField>(
2668 a: MatRef<'_, Complex<T>>,
2669 v: &[Complex<T>],
2670 lambda: &Complex<T>,
2671 ) -> (T, T) {
2672 let n = a.nrows();
2673 let mut anorm = zero::<T>();
2674 for j in 0..n {
2675 let mut col = zero::<T>();
2676 for i in 0..n {
2677 col += &a[(i, j)].re.abs() + &a[(i, j)].im.abs();
2678 }
2679 anorm = anorm.fmax(col);
2680 }
2681 let mut rnorm2 = zero::<T>();
2682 let mut vnorm2 = zero::<T>();
2683 for i in 0..n {
2684 let mut acc = Complex::new(zero::<T>(), zero::<T>());
2685 for j in 0..n {
2686 acc += &a[(i, j)] * &v[j];
2687 }
2688 acc -= lambda * &v[i];
2689 rnorm2 += acc.re.abs2() + acc.im.abs2();
2690 vnorm2 += v[i].re.abs2() + v[i].im.abs2();
2691 }
2692 (rnorm2.sqrt() / (anorm * vnorm2.sqrt()), vnorm2.sqrt())
2693 }
2694
2695 fn try_real_vectors<T: RealField>(t: MatRef<'_, T>) -> Option<Mat<T>> {
2696 let n = t.nrows();
2697 let mut x = Mat::zeros(n, n);
2698 let mut mem = MemBuffer::new(eigenvectors_from_schur_scratch::<T>(n));
2699 eigenvectors_from_schur_real(
2700 t,
2701 None,
2702 x.as_mut(),
2703 Par::Seq,
2704 MemStack::new(&mut mem),
2705 Default::default(),
2706 )
2707 .ok()?;
2708 Some(x)
2709 }
2710
2711 fn real_vectors<T: RealField>(t: MatRef<'_, T>) -> Mat<T> {
2712 try_real_vectors(t).unwrap()
2713 }
2714
2715 fn cplx_vectors<T: ComplexField>(t: MatRef<'_, T>) -> Mat<T> {
2716 let n = t.nrows();
2717 let mut x = Mat::zeros(n, n);
2718 let mut mem = MemBuffer::new(eigenvectors_from_schur_scratch::<T>(n));
2719 eigenvectors_from_schur_cplx(
2720 t,
2721 None,
2722 x.as_mut(),
2723 Par::Seq,
2724 MemStack::new(&mut mem),
2725 Default::default(),
2726 )
2727 .unwrap();
2728 x
2729 }
2730
2731 fn real_column<T: RealField>(
2733 x: MatRef<'_, T>,
2734 j: usize,
2735 ) -> Vec<Complex<T>> {
2736 (0..x.nrows())
2737 .map(|i| Complex::new(x[(i, j)].copy(), zero::<T>()))
2738 .collect()
2739 }
2740
2741 fn real_pair<T: RealField>(x: MatRef<'_, T>, j: usize) -> Vec<Complex<T>> {
2743 (0..x.nrows())
2744 .map(|i| Complex::new(x[(i, j)].copy(), x[(i, j + 1)].copy()))
2745 .collect()
2746 }
2747
2748 fn cplx_column<T: RealField>(
2749 x: MatRef<'_, Complex<T>>,
2750 j: usize,
2751 ) -> Vec<Complex<T>> {
2752 (0..x.nrows()).map(|i| x[(i, j)].copy()).collect()
2753 }
2754
2755 fn assert_eigenvector<T: RealField>(
2756 unit: MatRef<'_, Complex<T>>,
2757 v: &[Complex<T>],
2758 lambda: &Complex<T>,
2759 tol: &T,
2760 ) {
2761 let (res, nrm) = residual(unit, v, lambda);
2762 let nrm_err = (&nrm - &one::<T>()).abs();
2763 assert!(nrm_err <= *tol, "unit 2-norm {nrm:?}, expected 1");
2764 assert!(res <= *tol, "relative residual {res:?}");
2765 }
2766
2767 fn check_shift_2x2<T: RealField>(scale: T, tol: T) {
2768 let unit = unit::<T, 2>(&SHIFT_2X2);
2769 let lambda = Complex::new(from_f64::<T>(0.5), zero::<T>());
2770
2771 let x = real_vectors::<T>(scaled_real(unit.as_ref(), &scale).as_ref());
2772 let v = real_column(x.as_ref(), 1);
2773 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2774
2775 let x = cplx_vectors::<Complex<T>>(
2776 scaled_cplx(unit.as_ref(), &scale).as_ref(),
2777 );
2778 let v = cplx_column(x.as_ref(), 1);
2779 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2780 }
2781
2782 fn check_pair_3x3<T: RealField>(scale: T, tol: T, may_fail: bool) {
2785 let unit = unit::<T, 3>(&PAIR_3X3);
2786 let Some(x) =
2787 try_real_vectors::<T>(scaled_real(unit.as_ref(), &scale).as_ref())
2788 else {
2789 assert!(may_fail, "extraction failed at scale {scale:?}");
2790 return;
2791 };
2792 let lambda = Complex::new(zero::<T>(), one::<T>());
2794 let v = real_pair(x.as_ref(), 0);
2795 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2796 let lambda = Complex::new(from_f64::<T>(0.5), zero::<T>());
2797 let v = real_column(x.as_ref(), 2);
2798 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2799 }
2800
2801 fn check_cplx_triangular<T: RealField>(scale: T, tol: T) {
2802 let unit = complex_unit::<T>();
2803 let t = scaled_cplx(unit.as_ref(), &scale);
2804 let x = cplx_vectors(t.as_ref());
2805 for j in 0..3 {
2806 let lambda = unit[(j, j)].copy();
2807 let v = cplx_column(x.as_ref(), j);
2808 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2809 }
2810 }
2811
2812 #[test]
2813 fn two_by_two_at_the_range_limit() {
2814 for (scale, tol) in [(1.0f32, 1e-4f32), (2.0f32.powi(127), 1e-4)] {
2816 check_shift_2x2::<f32>(scale, tol);
2817 }
2818 for (scale, tol) in [(1.0f64, 1e-12f64), (2.0f64.powi(1023), 1e-12)] {
2819 check_shift_2x2::<f64>(scale, tol);
2820 }
2821 }
2822
2823 #[test]
2824 fn complex_triangular_at_the_range_limit() {
2825 for (scale, tol) in [(1.0f32, 1e-4f32), (2.0f32.powi(127), 1e-4)] {
2826 check_cplx_triangular::<f32>(scale, tol);
2827 }
2828 for (scale, tol) in [(1.0f64, 1e-12f64), (2.0f64.powi(1023), 1e-12)] {
2829 check_cplx_triangular::<f64>(scale, tol);
2830 }
2831 }
2832
2833 #[test]
2834 fn conjugate_pair_block_is_never_a_wrong_success() {
2835 check_pair_3x3::<f32>(1.0, 1e-4, false);
2836 check_pair_3x3::<f32>(2.0f32.powi(127), 1e-4, true);
2837 check_pair_3x3::<f64>(1.0, 1e-12, false);
2838 check_pair_3x3::<f64>(2.0f64.powi(1023), 1e-12, true);
2839 for (scale, tol, may_fail) in
2842 [(1.0, 1e-12, false), (2.0f64.powi(1023), 1e-12, true)]
2843 {
2844 let unit = unit::<f64, 4>(&TWO_PAIRS_4X4);
2845 let t = scaled_real(unit.as_ref(), &scale);
2846 match try_real_vectors::<f64>(t.as_ref()) {
2847 Some(x) => {
2848 for (j, im) in [(0, 1.0), (2, 0.5)] {
2849 let v = real_pair(x.as_ref(), j);
2850 assert_eigenvector(
2851 unit.as_ref(),
2852 &v,
2853 &Complex::new(0.0, im),
2854 &tol,
2855 );
2856 }
2857 },
2858 None => assert!(may_fail, "two pairs failed at scale 1"),
2859 }
2860 }
2861 let s = 2.0f64.powi(1023);
2863 let t = crate::mat![[s, -s, 1.0], [s, s, 0.0], [0.0, 0.0, -s]];
2864 if let Some(x) = try_real_vectors::<f64>(t.as_ref()) {
2865 let unit = Mat::from_fn(3, 3, |i, j| {
2866 Complex::new(t[(i, j)] / s, 0.0)
2867 });
2868 let v = real_pair(x.as_ref(), 0);
2869 assert_eigenvector(
2870 unit.as_ref(),
2871 &v,
2872 &Complex::new(1.0, 1.0),
2873 &1e-12,
2874 );
2875 }
2876 }
2877
2878 #[test]
2881 fn conjugate_pair_with_a_wide_dynamic_range() {
2882 let t = crate::mat![
2883 [0.0, -2.0f64.powi(1023), 1.0],
2884 [2.0f64.powi(-1022), 0.0, 0.0],
2885 [0.0, 0.0, 0.5],
2886 ];
2887 let x = real_vectors::<f64>(t.as_ref());
2888 assert!(x.is_all_finite());
2889 let unit = Mat::from_fn(3, 3, |i, j| Complex::new(t[(i, j)], 0.0));
2890 let lambda = Complex::new(0.5, 0.0);
2891 let v = real_column(x.as_ref(), 2);
2892 assert_eigenvector(unit.as_ref(), &v, &lambda, &1e-12);
2893 }
2894
2895 #[test]
2900 fn jordan_chains_at_both_ends_of_the_range() {
2901 const CHAIN_3X3: [[f64; 3]; 3] = [[1.0, 1.0, 0.0], [0.0, 1.0, 1.0], [0.0, 0.0, 1.0]];
2902 fn real<T: RealField>(scale: T, tol: T) {
2903 let unit = unit::<T, 3>(&CHAIN_3X3);
2904 let x = real_vectors::<T>(scaled_real(unit.as_ref(), &scale).as_ref());
2905 let lambda = Complex::new(one::<T>(), zero::<T>());
2906 for j in 0..3 {
2907 let v = real_column(x.as_ref(), j);
2908 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2909 }
2910 }
2911 fn cplx<T: RealField>(scale: T, tol: T) {
2912 let unit = unit::<T, 2>(&JORDAN_2X2);
2913 let x = cplx_vectors(scaled_cplx(unit.as_ref(), &scale).as_ref());
2914 let lambda = Complex::new(one::<T>(), zero::<T>());
2915 for j in 0..2 {
2916 let v = cplx_column(x.as_ref(), j);
2917 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2918 }
2919 }
2920 real::<f64>(2.0f64.powi(972), 1e-12);
2921 real::<f32>(2.0f32.powi(105), 1e-4);
2922 cplx::<f64>(2.0f64.powi(-972), 1e-12);
2923 cplx::<f32>(2.0f32.powi(-105), 1e-4);
2924 }
2925
2926 #[test]
2930 fn subnormal_shift_clamp_is_unchanged() {
2931 let scale = 2.0f64.powi(-1023);
2932 let unit = unit::<f64, 2>(&JORDAN_2X2);
2933 let x =
2934 real_vectors::<f64>(scaled_real(unit.as_ref(), &scale).as_ref());
2935 let lambda = Complex::new(1.0, 0.0);
2936 let v = real_column(x.as_ref(), 1);
2937 assert_eigenvector(unit.as_ref(), &v, &lambda, &1e-12);
2938 }
2939}
2940#[cfg(test)]
2941mod self_adjoint_tests {
2942 use super::*;
2943 use crate::assert;
2944 use crate::stats::prelude::*;
2945 use crate::utils::approx::*;
2946 use dyn_stack::MemBuffer;
2947 fn test_self_adjoint_evd<T: ComplexField>(mat: MatRef<'_, T>) {
2948 let n = mat.nrows();
2949 let params = Spec::new(SelfAdjointEvdParams {
2950 recursion_threshold: 8,
2951 ..auto!(T)
2952 });
2953 let approx_eq = CwiseMat(
2954 ApproxEq::<T::Real>::eps()
2955 * from_f64::<T::Real>(8.0 * n as f64).sqrt(),
2956 );
2957 let mut s = Mat::zeros(n, n);
2958 {
2959 let mut u = Mat::zeros(n, n);
2960 self_adjoint_evd(
2961 mat.as_ref(),
2962 s.as_mut().diagonal_mut(),
2963 Some(u.as_mut()),
2964 Par::Seq,
2965 MemStack::new(&mut MemBuffer::new(
2966 self_adjoint_evd_scratch::<T>(
2967 n,
2968 ComputeEigenvectors::Yes,
2969 ParShape::Seq,
2970 params,
2971 ),
2972 )),
2973 params,
2974 )
2975 .unwrap();
2976 let reconstructed = &u * &s * u.adjoint();
2977 assert!(reconstructed ~ mat);
2978 }
2979 {
2980 let mut s2 = Mat::zeros(n, n);
2981 self_adjoint_evd(
2982 mat.as_ref(),
2983 s2.as_mut().diagonal_mut(),
2984 None,
2985 Par::Seq,
2986 MemStack::new(&mut MemBuffer::new(
2987 self_adjoint_evd_scratch::<T>(
2988 n,
2989 ComputeEigenvectors::No,
2990 ParShape::Seq,
2991 params,
2992 ),
2993 )),
2994 params,
2995 )
2996 .unwrap();
2997 assert!(s2 ~ s);
2998 }
2999 }
3000 #[test]
3001 fn test_real() {
3002 let rng = &mut StdRng::seed_from_u64(1);
3003 for n in [1, 2, 4, 10, 15, 20, 50, 100, 150] {
3004 let mat = CwiseMatDistribution {
3005 nrows: n,
3006 ncols: n,
3007 dist: StandardNormal,
3008 }
3009 .rand::<Mat<f64>>(rng);
3010 let mat = &mat + mat.adjoint();
3011 test_self_adjoint_evd(mat.as_ref());
3012 }
3013 }
3014 #[test]
3015 fn test_cplx() {
3016 let rng = &mut StdRng::seed_from_u64(1);
3017 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
3018 let mat = CwiseMatDistribution {
3019 nrows: n,
3020 ncols: n,
3021 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
3022 }
3023 .rand::<Mat<c64>>(rng);
3024 let mat = &mat + mat.adjoint();
3025 test_self_adjoint_evd(mat.as_ref());
3026 }
3027 }
3028 #[test]
3029 fn test_special() {
3030 for n in [1, 2, 4, 10, 15, 20, 50, 100, 150] {
3031 test_self_adjoint_evd(Mat::full(n, n, 0.0).as_ref());
3032 test_self_adjoint_evd(Mat::full(n, n, c64::ZERO).as_ref());
3033 test_self_adjoint_evd(Mat::full(n, n, 1.0).as_ref());
3034 test_self_adjoint_evd(Mat::full(n, n, c64::ONE).as_ref());
3035 test_self_adjoint_evd(Mat::<f64>::identity(n, n).as_ref());
3036 test_self_adjoint_evd(Mat::<c64>::identity(n, n).as_ref());
3037 }
3038 }
3039 #[test]
3040 fn test_pinv() {
3041 let rng = &mut StdRng::seed_from_u64(0);
3042 let n = 36;
3043 let mat = CwiseMatDistribution {
3044 nrows: n,
3045 ncols: n,
3046 dist: StandardNormal,
3047 }
3048 .rand::<Mat<f64>>(rng);
3049 let mat = &mat + mat.adjoint();
3050 let pinv = mat.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap().pseudoinverse(Par::Seq);
3051 let err = &mat * &pinv - Mat::<f64>::identity(n, n);
3052 assert!(err.norm_max() < 1e-10);
3053 }
3054}
3055#[cfg(test)]
3056mod scale_and_empty_tests {
3057 use super::*;
3062 #[test]
3063 fn two_by_two_finite_spectrum_at_the_range_limit() {
3064 let a = crate::mat![[1e308f64, 0.0], [0.0, 1e308]];
3065 let eig = a.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap();
3066 assert_eq!(eig.S()[0], 1e308);
3067 assert_eq!(eig.S()[1], 1e308);
3068 let a = crate::mat![[1e38f32, 0.0], [0.0, 1e38]];
3069 let eig = a.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap();
3070 assert_eq!(eig.S()[0], 1e38);
3071 assert_eq!(eig.S()[1], 1e38);
3072 }
3073 #[test]
3076 fn two_by_two_subnormal_diagonal() {
3077 let q = f64::from_bits(1);
3078 let a = Mat::<f64>::from_fn(2, 2, |i, j| if i == j { q } else { 0.0 });
3079 let eig = a.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap();
3080 assert_eq!(eig.S()[0], q);
3081 assert_eq!(eig.S()[1], q);
3082 }
3083 #[test]
3084 fn empty_matrix() {
3085 let a = Mat::<f64>::zeros(0, 0);
3086 let eig = a.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap();
3087 assert_eq!(eig.U().shape(), (0, 0));
3088 assert_eq!(eig.S().dim(), 0);
3089 }
3090 #[test]
3093 fn empty_tridiagonal_with_vectors() {
3094 fn check<T: ComplexField>() {
3095 let empty = Diag::<T>::zeros(0);
3096 let mut s = Diag::<T>::zeros(0);
3097 let mut u = Mat::<T>::zeros(0, 0);
3098 let params = default();
3099 let mut mem = dyn_stack::MemBuffer::new(
3100 tridiagonal_self_adjoint_evd_scratch::<T>(
3101 0,
3102 ComputeEigenvectors::Yes,
3103 ParShape::Seq,
3104 params,
3105 ),
3106 );
3107 tridiagonal_self_adjoint_evd(
3108 empty.as_ref(),
3109 empty.as_ref(),
3110 s.as_mut(),
3111 Some(u.as_mut()),
3112 Par::Seq,
3113 MemStack::new(&mut mem),
3114 params,
3115 )
3116 .unwrap();
3117 }
3118 check::<f64>();
3119 check::<c64>();
3120 check::<c32>();
3121 }
3122}
3123#[cfg(test)]
3124mod tridiagonal_range_tests {
3125 use super::*;
3131 use crate::assert;
3132 use crate::diag::{DiagMut, DiagRef};
3133 use crate::dyn_stack::MemBuffer;
3134
3135 fn tridiagonal_evd(
3136 diag: &[f64],
3137 offdiag: &[f64],
3138 threshold: usize,
3139 ) -> Result<Vec<f64>, EvdError> {
3140 let n = diag.len();
3141 let mut s = vec![0.0; n];
3142 let mut u = Mat::<f64>::zeros(n, n);
3143 let mut params = Spec::<SelfAdjointEvdParams, f64>::default();
3144 params.recursion_threshold = threshold;
3145 let mut mem =
3146 MemBuffer::new(tridiagonal_self_adjoint_evd_scratch::<f64>(
3147 n,
3148 ComputeEigenvectors::Yes,
3149 crate::ParShape::Seq,
3150 params,
3151 ));
3152 tridiagonal_self_adjoint_evd(
3153 DiagRef::from_slice(diag),
3154 DiagRef::from_slice(offdiag),
3155 DiagMut::from_slice_mut(&mut s),
3156 Some(u.as_mut()),
3157 Par::Seq,
3158 MemStack::new(&mut mem),
3159 params,
3160 )?;
3161 Ok(s)
3162 }
3163
3164 #[test]
3167 fn two_by_two_at_the_range_limit() {
3168 let s = tridiagonal_evd(&[-1e308, 1e308], &[0.0], usize::MAX).unwrap();
3169 assert_eq!(s, [-1e308, 1e308]);
3170 let s =
3171 tridiagonal_evd(&[-1e308, 1e308], &[1e308], usize::MAX).unwrap();
3172 let sqrt2 = 2.0f64.sqrt();
3173 assert_eq!(s[0], -sqrt2 * 1e308);
3174 assert_eq!(s[1], sqrt2 * 1e308);
3175 }
3176
3177 #[test]
3180 fn subnormal_three_by_three() {
3181 let s = 1e-310f64;
3182 let sqrt2 = 2.0f64.sqrt();
3183 let got =
3184 tridiagonal_evd(&[2.0 * s; 3], &[-s, -s], usize::MAX).unwrap();
3185 for (got, exact) in
3186 got.iter()
3187 .zip([s * (2.0 - sqrt2), 2.0 * s, s * (2.0 + sqrt2)])
3188 {
3189 assert!(
3190 (got - exact).abs() <= 1e-13 * s,
3191 "got {got:e}, exact {exact:e}"
3192 );
3193 }
3194 }
3195
3196 #[test]
3199 fn spectrum_at_both_range_limits() {
3200 let n = 5;
3201 for scale in [1e308f64, 1e-310] {
3202 let diag = vec![scale; n];
3203 let offdiag = vec![0.3 * scale; n - 1];
3204 for threshold in [usize::MAX, 4] {
3206 let s = tridiagonal_evd(&diag, &offdiag, threshold).unwrap();
3207 for (k, x) in s.iter().enumerate() {
3208 let exact = scale
3209 * (1.0
3210 + 0.6
3211 * (core::f64::consts::PI * (n - k) as f64
3212 / (n + 1) as f64)
3213 .cos());
3214 assert!(x.is_finite());
3215 assert!(
3216 (x - exact).abs() <= 1e-13 * scale,
3217 "threshold {threshold}: got {x:e}, exact {exact:e}"
3218 );
3219 }
3220 }
3221 }
3222 }
3223}
3224
3225#[cfg(test)]
3226mod geev_scaling_tests {
3227 use crate::{assert, c32, c64, Mat, Par};
3236 use num_complex::Complex64 as Z;
3237
3238 fn get22(a: &[Vec<Z>], u: &[Vec<Z>], w: &[Z], eps: f64) -> f64 {
3240 let n = a.len();
3241 let (mut an, mut un, mut err) = (0.0f64, 0.0f64, 0.0f64);
3242 for j in 0..n {
3243 let (mut ac, mut uc, mut ec) = (0.0, 0.0, 0.0);
3244 for i in 0..n {
3245 ac += a[i][j].norm();
3246 uc += u[i][j].norm();
3247 let mut r = -u[i][j] * w[j];
3248 for k in 0..n {
3249 r += a[i][k] * u[k][j];
3250 }
3251 ec += r.norm();
3252 }
3253 an = an.max(ac);
3254 un = un.max(uc);
3255 err = err.max(ec);
3256 }
3257 err / an / un / eps
3258 }
3259
3260 fn unit(n: usize, cplx: bool) -> [Vec<Vec<Z>>; 2] {
3263 let grcar = (0..n)
3264 .map(|i| {
3265 (0..n)
3266 .map(|j| match j as isize - i as isize {
3267 -1 => Z::new(-1.0, 0.0),
3268 0..=3 => Z::new(1.0, 0.0),
3269 _ => Z::new(0.0, 0.0),
3270 })
3271 .collect()
3272 })
3273 .collect();
3274 let mut state = 0x2545_f491_4f6c_dd1du64;
3275 let mut next = move || {
3276 state = state.wrapping_mul(6364136223846793005).wrapping_add(1442695040888963407);
3277 ((state >> 54) as f64 - 512.0) / 512.0
3279 };
3280 let rand = (0..n)
3281 .map(|_| (0..n).map(|_| Z::new(next(), if cplx { next() } else { 0.0 })).collect())
3282 .collect();
3283 [grcar, rand]
3284 }
3285
3286 macro_rules! check {
3287 ($t:ty, $mk:expr, $z:expr, $eps:expr, $scales:expr, $cplx:expr) => {{
3288 let n = 32;
3289 let thresh = 20.0 * f64::max(1.0, (n as f64 / 16.0).sqrt());
3291 for unit in unit(n, $cplx) {
3292 for e in $scales {
3293 let scale = 2.0f64.powi(e);
3294 let a = Mat::<$t>::from_fn(n, n, |i, j| $mk(unit[i][j] * scale));
3295 for (side, m) in [("right", a.clone()), ("left", a.adjoint().to_owned())] {
3296 let dimless: Vec<Vec<Z>> = (0..n).map(|i| (0..n).map(|j| $z(m[(i, j)]) / scale).collect()).collect();
3297 let evd = m.eigen(Par::Seq).unwrap();
3298 let w: Vec<Z> = (0..n).map(|i| Z::new(evd.S()[i].re as f64, evd.S()[i].im as f64) / scale).collect();
3299 let u: Vec<Vec<Z>> = (0..n).map(|i| (0..n).map(|j| Z::new(evd.U()[(i, j)].re as f64, evd.U()[(i, j)].im as f64)).collect()).collect();
3300 assert!(w.iter().all(|w| w.is_finite()) && u.iter().flatten().all(|u| u.is_finite()));
3302 let r = get22(&dimless, &u, &w, $eps);
3303 assert!(r <= thresh, "{} scale 2^{e} {side}: GET22 {r}", stringify!($t));
3304 }
3305 }
3306 }
3307 }};
3308 }
3309
3310 #[test]
3314 fn evd_scaling_at_the_complex_range_ends() {
3315 let top = c64::new(2.0f64.powi(1023), 0.0);
3316 let evd = Mat::from_fn(1, 1, |_, _| top).eigen(Par::Seq).unwrap();
3317 assert!(evd.S()[0] == top);
3318 let top = c32::new(2.0f32.powi(127), 0.0);
3319 let evd = Mat::from_fn(1, 1, |_, _| top).eigen(Par::Seq).unwrap();
3320 assert!(evd.S()[0] == top);
3321 let m = f64::from_bits(1);
3323 let a = Mat::from_fn(2, 2, |i, j| match (i, j) {
3324 (0, 1) => c64::new(-m, 0.0),
3325 (1, 0) => c64::new(m, 0.0),
3326 _ => c64::new(0.0, 0.0),
3327 });
3328 let evd = a.eigen(Par::Seq).unwrap();
3329 let mut w = [evd.S()[0], evd.S()[1]];
3330 w.sort_by(|a, b| a.im.partial_cmp(&b.im).unwrap());
3331 assert!(w == [c64::new(0.0, -m), c64::new(0.0, m)]);
3332 let m = f32::from_bits(1);
3333 let a = Mat::from_fn(2, 2, |i, j| match (i, j) {
3334 (0, 1) => c32::new(-m, 0.0),
3335 (1, 0) => c32::new(m, 0.0),
3336 _ => c32::new(0.0, 0.0),
3337 });
3338 let evd = a.eigen(Par::Seq).unwrap();
3339 let mut w = [evd.S()[0], evd.S()[1]];
3340 w.sort_by(|a, b| a.im.partial_cmp(&b.im).unwrap());
3341 assert!(w == [c32::new(0.0, -m), c32::new(0.0, m)]);
3342 }
3343
3344 #[test]
3345 fn evd_scales_its_input_into_range() {
3346 check!(f32, |z: Z| z.re as f32, |x: f32| Z::new(x as f64, 0.0), f32::EPSILON as f64, [-120, -100, 0, 100, 120], false);
3347 check!(c32, |z: Z| c32::new(z.re as f32, z.im as f32), |x: c32| Z::new(x.re as f64, x.im as f64), f32::EPSILON as f64, [-120, -100, 0, 100, 120], true);
3348 check!(f64, |z: Z| z.re, |x: f64| Z::new(x, 0.0), f64::EPSILON, [-1010, -1000, -960, 0, 960, 1015], false);
3349 check!(c64, |z: Z| c64::new(z.re, z.im), |x: c64| Z::new(x.re, x.im), f64::EPSILON, [-1010, -1000, -960, 0, 960, 1015], true);
3350 }
3351}
3352
3353#[cfg(test)]
3354#[path = "tests/mod.rs"]
3355mod lapack;