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_norm<T: ComplexField>(A: MatRef<'_, T>, subdiag: usize) -> T::Real {
655 let n = A.nrows();
656 let eps = eps::<T::Real>();
657 let rows = |j: usize| Ord::min(j + 1 + subdiag, n);
658 let mut norm = zero::<T::Real>();
659 for j in 0..n {
660 for i in 0..rows(j) {
661 norm += A[(i, j)].abs1();
662 }
663 }
664 let mut eps_norm = &eps * &norm;
665 if !eps_norm.is_finite() {
666 let mut acc = zero::<T::Real>();
667 for j in 0..n {
668 for i in 0..rows(j) {
669 let a = &A[(i, j)];
670 acc += &eps * &a.real().abs() + &eps * &a.imag().abs();
673 }
674 }
675 eps_norm = acc;
676 }
677 eps_norm
678}
679pub(crate) fn evd_from_real_schur_imp<T: RealField>(
680 A: MatRef<'_, T>,
681 V: MatMut<'_, T>,
682 mut par: Par,
683 params: EvdFromSchurParams,
684) {
685 let one = one::<T>;
686 let zero = zero::<T>;
687 let mut V = V;
688 let n = A.nrows();
689 let eps_norm = eps_norm(A, 1);
690 let mut k = n;
691 loop {
692 if k == 0 {
693 break;
694 }
695 k -= 1;
696 if k == 0 || A[(k, k - 1)] == zero() {
697 let ref p = A[(k, k)].copy();
698 V[(k, k)] = one();
699 for i in 0..k {
700 V[(i, k)] = -&A[(i, k)];
701 }
702 let scale = solve_real_shifted_upper_quasi_triangular_system(
703 A.submatrix(0, 0, k, k),
704 p.copy(),
705 V.rb_mut().subrows_mut(0, k).col_mut(k),
706 eps_norm.copy(),
707 par.reborrow(),
708 params,
709 );
710 V[(k, k)] *= scale;
714 } else {
715 let ref p = A[(k, k)].copy();
716 let ref q = A[(k, k - 1)].abs().sqrt() * A[(k - 1, k)].abs().sqrt();
717 if A[(k - 1, k)].abs() >= A[(k, k - 1)].abs() {
718 V[(k - 1, k - 1)] = one();
719 V[(k, k)] = q / &A[(k - 1, k)];
720 } else {
721 V[(k - 1, k - 1)] = -q / &A[(k, k - 1)];
722 V[(k, k)] = one();
723 }
724 V[(k - 1, k)] = zero();
725 V[(k, k - 1)] = zero();
726 for i in 0..k - 1 {
727 V[(i, k - 1)] = -&V[(k - 1, k - 1)] * &A[(i, k - 1)];
728 V[(i, k)] = -&V[(k, k)] * &A[(i, k)];
729 }
730 let scale = solve_cplx_shifted_upper_quasi_triangular_system(
731 A.submatrix(0, 0, k - 1, k - 1),
732 p.copy(),
733 q.copy(),
734 V.rb_mut().submatrix_mut(0, k - 1, k - 1, 2),
735 eps_norm.copy(),
736 par.reborrow(),
737 params,
738 );
739 V[(k - 1, k - 1)] *= scale.copy();
740 V[(k, k)] *= scale;
741 k -= 1;
742 }
743 }
744}
745pub(crate) fn evd_from_cplx_schur_imp<T: ComplexField>(
746 A: MatRef<'_, T>,
747 conj_A: Conj,
748 V: MatMut<'_, T>,
749 mut par: Par,
750 params: EvdFromSchurParams,
751) {
752 let one = one::<T>;
753 let mut V = V;
754 let n = A.nrows();
755 let eps_norm = eps_norm(A, 0);
756 let mut k = n;
757 loop {
758 if k == 0 {
759 break;
760 }
761 k -= 1;
762 let ref p = if conj_A == Conj::Yes {
763 A[(k, k)].conj()
764 } else {
765 A[(k, k)].copy()
766 };
767 V[(k, k)] = one();
768 if conj_A == Conj::Yes {
769 for i in 0..k {
770 V[(i, k)] = -A[(i, k)].conj();
771 }
772 } else {
773 for i in 0..k {
774 V[(i, k)] = -&A[(i, k)];
775 }
776 }
777 let scale = solve_shifted_upper_triangular_system(
778 A.submatrix(0, 0, k, k),
779 conj_A,
780 p.copy(),
781 V.rb_mut().subrows_mut(0, k).col_mut(k),
782 eps_norm.copy(),
783 par.reborrow(),
784 params,
785 );
786 let scale: T = scale.to_cplx();
787 V[(k, k)] *= scale;
788 }
789}
790fn solve_real_shifted_upper_quasi_triangular_system<T: RealField>(
791 A: MatRef<'_, T>,
792 p: T,
793 x: ColMut<'_, T>,
794 eps_norm: T,
795 mut par: Par,
796 params: EvdFromSchurParams,
797) -> T {
798 let n = A.nrows();
799 let one = one::<T>;
800 let zero = zero::<T>;
801 let ref p = p;
802 let mut x = x;
803 let big = sqrt_max_positive::<T>();
809 let mut scale = one();
810 if par.degree() == 0 || n < params.recursion_threshold {
811 let mut i = n;
812 loop {
813 if i == 0 {
814 break;
815 }
816 i -= 1;
817 if i == 0 || A[(i, i - 1)] == zero() {
818 let dot = linalg::matmul::dot::inner_prod(
819 A.row(i).subcols(i + 1, n - i - 1),
820 Conj::No,
821 x.rb().subrows(i + 1, n - i - 1),
822 Conj::No,
823 );
824 x[i] -= dot;
825 let mut z = &A[(i, i)] - p;
826 if z.abs() < eps_norm {
827 z = eps_norm.copy();
828 }
829 if x[i] != zero() {
830 x[i] /= z;
831 }
832 let m = x[i].abs();
833 if m > big {
834 let r = m.recip();
835 for j in 0..x.nrows() {
836 x[j] *= r.copy();
837 }
838 scale *= r;
839 }
840 } else {
841 let (dot0, dot1) = dot2x1(
842 A.row(i - 1).subcols(i + 1, n - i - 1),
843 A.row(i).subcols(i + 1, n - i - 1),
844 x.rb().subrows(i + 1, n - i - 1),
845 );
846 x[i - 1] -= dot0;
847 x[i] -= dot1;
848 let ref a = &A[(i, i)] - p;
849 let ref b = A[(i - 1, i)];
850 let ref c = A[(i, i - 1)];
851 let ref r0 = x[i - 1];
852 let ref r1 = x[i];
853 let ref inv_det = (a.abs2() - b * c).recip();
854 (x[i - 1], x[i]) =
855 ((a * r0 - b * r1) * inv_det, (a * r1 - c * r0) * inv_det);
856 let a0 = x[i - 1].abs();
857 let a1 = x[i].abs();
858 let m = if a0 > a1 { a0 } else { a1 };
859 if m > big {
860 let r = m.recip();
861 for j in 0..x.nrows() {
862 x[j] *= r.copy();
863 }
864 scale *= r;
865 }
866 i -= 1;
867 }
868 }
869 scale
870 } else {
871 let mut mid = n / 2;
872 if A[(mid, mid - 1)] != zero() {
873 mid -= 1;
874 }
875 let (A00, A01, _, A11) = A.split_at(mid, mid);
876 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
877 let f1 = solve_real_shifted_upper_quasi_triangular_system(
878 A11,
879 p.copy(),
880 x1.rb_mut(),
881 eps_norm.copy(),
882 par.reborrow(),
883 params,
884 );
885 for j in 0..x0.nrows() {
889 x0[j] *= f1.copy();
890 }
891 linalg::matmul::matmul(
892 x0.rb_mut().as_mat_mut(),
893 Accum::Add,
894 A01,
895 x1.rb().as_mat(),
896 -one(),
897 par.reborrow(),
898 );
899 let f0 = solve_real_shifted_upper_quasi_triangular_system(
900 A00,
901 p.copy(),
902 x0.rb_mut(),
903 eps_norm,
904 par,
905 params,
906 );
907 for j in 0..x1.nrows() {
908 x1[j] *= f0.copy();
909 }
910 f0 * f1
911 }
912}
913fn solve_cplx_shifted_upper_quasi_triangular_system<T: RealField>(
914 A: MatRef<'_, T>,
915 p: T,
916 q: T,
917 x: MatMut<'_, T>,
918 eps_norm: T,
919 mut par: Par,
920 params: EvdFromSchurParams,
921) -> T {
922 let n = A.nrows();
923 let one = one::<T>;
924 let zero = zero::<T>;
925 let ref p = p;
926 let ref q = q;
927 let mut x = x;
928 let big = sqrt_max_positive::<T>();
929 let mut scale = one();
930 if par.degree() == 0 || n < params.recursion_threshold {
931 let mut i = n;
932 loop {
933 if i == 0 {
934 break;
935 }
936 i -= 1;
937 if i == 0 || A[(i, i - 1)] == zero() {
938 let (re, im) = dot2x1(
939 x.rb().subrows(i + 1, n - i - 1).col(0).transpose(),
940 x.rb().subrows(i + 1, n - i - 1).col(1).transpose(),
941 A.row(i).subcols(i + 1, n - i - 1).transpose(),
942 );
943 x[(i, 0)] = &x[(i, 0)] - re;
944 x[(i, 1)] = &x[(i, 1)] - im;
945 let mut z_re = &A[(i, i)] - p;
946 let mut z_im = -q;
947 let mut z = z_re.hypot(&z_im);
948 if z < eps_norm {
949 z_re = eps_norm.copy();
950 z_im = zero();
951 z = z_re.copy();
952 }
953 let ref z = z;
954 let ref z_re = (z_re / z) / z;
955 let ref z_im = (-z_im / z) / z;
956 let ref x_re = x[(i, 0)].copy();
957 let ref x_im = x[(i, 1)].copy();
958 x[(i, 0)] = x_re * z_re - x_im * z_im;
959 x[(i, 1)] = x_re * z_im + x_im * z_re;
960 let a0 = x[(i, 0)].abs();
961 let a1 = x[(i, 1)].abs();
962 let m = if a0 > a1 { a0 } else { a1 };
963 if m > big {
964 let r = m.recip();
965 for j in 0..x.nrows() {
966 x[(j, 0)] *= r.copy();
967 x[(j, 1)] *= r.copy();
968 }
969 scale *= r;
970 }
971 } else {
972 let (re0, re1, im0, im1) = dot2x2(
973 A.row(i - 1).subcols(i + 1, n - i - 1),
974 A.row(i).subcols(i + 1, n - i - 1),
975 x.rb().col(0).subrows(i + 1, n - i - 1),
976 x.rb().col(1).subrows(i + 1, n - i - 1),
977 );
978 x[(i - 1, 0)] = &x[(i - 1, 0)] - re0;
979 x[(i - 1, 1)] = &x[(i - 1, 1)] - im0;
980 x[(i, 0)] = &x[(i, 0)] - re1;
981 x[(i, 1)] = &x[(i, 1)] - im1;
982 let ref a_re = &A[(i, i)] - p;
983 let ref a_im = -q;
984 let ref b = A[(i - 1, i)];
985 let ref c = A[(i, i - 1)];
986 let ref r0_re = x[(i - 1, 0)];
987 let ref r0_im = x[(i - 1, 1)];
988 let ref r1_re = x[(i, 0)];
989 let ref r1_im = x[(i, 1)];
990 let mut z_re = a_re.abs2() - a_im.abs2() - b * c;
991 let mut z_im = (a_re * a_im).mul_pow2(from_f64::<T>(2.0));
992 let mut z = z_re.hypot(&z_im);
993 if z < eps_norm {
994 z_re = eps_norm.copy();
995 z_im = zero();
996 z = z_re.copy();
997 }
998 let ref z = z;
999 let ref z_re = (z_re / z) / z;
1000 let ref z_im = (-z_im / z) / z;
1001 let ref x0_re = (a_re * r0_re - a_im * r0_im) - b * r1_re;
1002 let ref x0_im = (a_re * r0_im + a_im * r0_re) - b * r1_im;
1003 let ref x1_re = (a_re * r1_re - a_im * r1_im) - c * r0_re;
1004 let ref x1_im = (a_re * r1_im + a_im * r1_re) - c * r0_im;
1005 x[(i - 1, 0)] = x0_re * z_re - x0_im * z_im;
1006 x[(i - 1, 1)] = x0_re * z_im + x0_im * z_re;
1007 x[(i, 0)] = x1_re * z_re - x1_im * z_im;
1008 x[(i, 1)] = x1_re * z_im + x1_im * z_re;
1009 let mut m = x[(i - 1, 0)].abs();
1010 let v = x[(i - 1, 1)].abs();
1011 if v > m {
1012 m = v;
1013 }
1014 let v = x[(i, 0)].abs();
1015 if v > m {
1016 m = v;
1017 }
1018 let v = x[(i, 1)].abs();
1019 if v > m {
1020 m = v;
1021 }
1022 if m > big {
1023 let r = m.recip();
1024 for j in 0..x.nrows() {
1025 x[(j, 0)] *= r.copy();
1026 x[(j, 1)] *= r.copy();
1027 }
1028 scale *= r;
1029 }
1030 i -= 1;
1031 }
1032 }
1033 scale
1034 } else {
1035 let mut mid = n / 2;
1036 if A[(mid, mid - 1)] != zero() {
1037 mid -= 1;
1038 }
1039 let (A00, A01, _, A11) = A.split_at(mid, mid);
1040 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
1041 let f1 = solve_cplx_shifted_upper_quasi_triangular_system(
1042 A11,
1043 p.copy(),
1044 q.copy(),
1045 x1.rb_mut(),
1046 eps_norm.copy(),
1047 par.reborrow(),
1048 params,
1049 );
1050 for j in 0..x0.nrows() {
1051 x0[(j, 0)] *= f1.copy();
1052 x0[(j, 1)] *= f1.copy();
1053 }
1054 linalg::matmul::matmul(
1055 x0.rb_mut(),
1056 Accum::Add,
1057 A01,
1058 x1.rb(),
1059 -one(),
1060 par.reborrow(),
1061 );
1062 let f0 = solve_cplx_shifted_upper_quasi_triangular_system(
1063 A00,
1064 p.copy(),
1065 q.copy(),
1066 x0.rb_mut(),
1067 eps_norm,
1068 par,
1069 params,
1070 );
1071 for j in 0..x1.nrows() {
1072 x1[(j, 0)] *= f0.copy();
1073 x1[(j, 1)] *= f0.copy();
1074 }
1075 f0 * f1
1076 }
1077}
1078fn solve_shifted_upper_triangular_system<T: ComplexField>(
1079 A: MatRef<'_, T>,
1080 conj_A: Conj,
1081 p: T,
1082 x: ColMut<'_, T>,
1083 eps_norm: T::Real,
1084 mut par: Par,
1085 params: EvdFromSchurParams,
1086) -> T::Real {
1087 let n = A.nrows();
1088 let mut scale = one::<T::Real>();
1089 let one = one::<T>;
1090 let zero = zero::<T>;
1091 let ref p = p;
1092 let mut x = x;
1093 let big = sqrt_max_positive::<T::Real>();
1094 if par.degree() == 0 || n < params.recursion_threshold {
1095 let mut i = n;
1096 loop {
1097 if i == 0 {
1098 break;
1099 }
1100 i -= 1;
1101 let dot = linalg::matmul::dot::inner_prod(
1102 A.row(i).subcols(i + 1, n - i - 1),
1103 conj_A,
1104 x.rb().subrows(i + 1, n - i - 1),
1105 Conj::No,
1106 );
1107 x[i] -= dot;
1108 let mut z = if conj_A == Conj::Yes {
1109 A[(i, i)].conj()
1110 } else {
1111 A[(i, i)].copy()
1112 } - p;
1113 if z.abs() < eps_norm {
1114 z = eps_norm.to_cplx();
1115 }
1116 if x[i] != zero() {
1117 x[i] *= z.recip();
1118 }
1119 let m = x[i].abs();
1120 if m > big {
1121 let r = m.recip();
1122 let rc: T = r.to_cplx();
1123 for j in 0..x.nrows() {
1124 x[j] *= rc.copy();
1125 }
1126 scale *= r;
1127 }
1128 }
1129 scale
1130 } else {
1131 let mid = n / 2;
1132 let (A00, A01, _, A11) = A.split_at(mid, mid);
1133 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
1134 let f1 = solve_shifted_upper_triangular_system(
1135 A11,
1136 conj_A,
1137 p.copy(),
1138 x1.rb_mut(),
1139 eps_norm.copy(),
1140 par.reborrow(),
1141 params,
1142 );
1143 let f1c: T = f1.to_cplx();
1144 for j in 0..x0.nrows() {
1145 x0[j] *= f1c.copy();
1146 }
1147 linalg::matmul::matmul_with_conj(
1148 x0.rb_mut().as_mat_mut(),
1149 Accum::Add,
1150 A01,
1151 conj_A,
1152 x1.rb().as_mat(),
1153 Conj::No,
1154 -one(),
1155 par.reborrow(),
1156 );
1157 let f0 = solve_shifted_upper_triangular_system(
1158 A00,
1159 conj_A,
1160 p.copy(),
1161 x0.rb_mut(),
1162 eps_norm,
1163 par,
1164 params,
1165 );
1166 let f0c: T = f0.to_cplx();
1167 for j in 0..x1.nrows() {
1168 x1[j] *= f0c.copy();
1169 }
1170 f0 * f1
1171 }
1172}
1173pub fn evd_scratch<T: ComplexField>(
1176 dim: usize,
1177 eigen_left: ComputeEigenvectors,
1178 eigen_right: ComputeEigenvectors,
1179 par: ParShape,
1180 params: Spec<EvdParams, T>,
1181) -> StackReq {
1182 let n = dim;
1183 if n == 0 {
1184 return StackReq::EMPTY;
1185 }
1186 let compute_eigen = eigen_left == ComputeEigenvectors::Yes
1187 || eigen_right == ComputeEigenvectors::Yes;
1188 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(
1189 n - 1,
1190 n - 1,
1191 );
1192 let H = temp_mat_scratch::<T>(n, n);
1193 let X = H;
1194 let Z = temp_mat_scratch::<T>(n, if compute_eigen { n } else { 0 });
1195 let householder = temp_mat_scratch::<T>(bs, n);
1196 let apply = linalg::householder::apply_block_householder_sequence_on_the_right_in_place_scratch::<T>(n - 1, bs, n - 1);
1197 StackReq::all_of(&[
1198 H,
1199 Z,
1200 StackReq::any_of(&[
1201 householder.and(
1202 hessenberg::hessenberg_in_place_scratch::<T>(
1203 n,
1204 bs,
1205 par,
1206 params.hessenberg.into(),
1207 )
1208 .or(apply),
1209 ),
1210 schur::multishift_qr_scratch::<T>(
1211 n,
1212 n,
1213 compute_eigen,
1214 compute_eigen,
1215 par,
1216 params.schur,
1217 ),
1218 X,
1219 ]),
1220 ])
1221}
1222pub fn hessenberg_schur_scratch<T: ComplexField>(
1234 dim: usize,
1235 compute_z: bool,
1236 par: ParShape,
1237 params: Spec<SchurParams, T>,
1238) -> StackReq {
1239 schur::multishift_qr_scratch::<T>(dim, dim, true, compute_z, par, *params)
1240}
1241pub fn hessenberg_schur_real<T: RealField>(
1283 mut h: MatMut<'_, T>,
1284 z: Option<MatMut<'_, T>>,
1285 mut wr: ColMut<'_, T>,
1286 mut wi: ColMut<'_, T>,
1287 par: Par,
1288 stack: &mut MemStack,
1289 params: Spec<SchurParams, T>,
1290) -> Result<(), EvdError> {
1291 let n = h.nrows();
1292 assert!(h.ncols() == n && wr.nrows() == n && wi.nrows() == n);
1293 if let Some(z) = z.rb() {
1294 assert!(z.nrows() == n && z.ncols() == n);
1295 }
1296 if !h.is_all_finite() {
1297 return Err(EvdError::NoConvergence);
1298 }
1299 if n <= 1 {
1300 if n == 1 {
1301 wr[0] = h[(0, 0)].copy();
1302 wi[0] = zero();
1303 }
1304 return Ok(());
1305 }
1306 let (info, _, _) = schur::real_schur::multishift_qr(
1307 true,
1308 h.rb_mut(),
1309 z,
1310 wr,
1311 wi,
1312 0,
1313 n,
1314 par,
1315 stack,
1316 *params,
1317 );
1318 if info != 0 {
1319 return Err(EvdError::NoConvergence);
1320 }
1321 for j in 0..n {
1323 for i in j + 2..n {
1324 h[(i, j)] = zero();
1325 }
1326 }
1327 Ok(())
1328}
1329pub fn hessenberg_schur_cplx<T: ComplexField>(
1366 mut h: MatMut<'_, T>,
1367 z: Option<MatMut<'_, T>>,
1368 mut w: ColMut<'_, T>,
1369 par: Par,
1370 stack: &mut MemStack,
1371 params: Spec<SchurParams, T>,
1372) -> Result<(), EvdError> {
1373 assert!(!T::IS_REAL);
1374 let n = h.nrows();
1375 assert!(h.ncols() == n && w.nrows() == n);
1376 if let Some(z) = z.rb() {
1377 assert!(z.nrows() == n && z.ncols() == n);
1378 }
1379 if !h.is_all_finite() {
1380 return Err(EvdError::NoConvergence);
1381 }
1382 if n <= 1 {
1383 if n == 1 {
1384 w[0] = h[(0, 0)].copy();
1385 }
1386 return Ok(());
1387 }
1388 let (info, _, _) = schur::complex_schur::multishift_qr(
1389 true,
1390 h.rb_mut(),
1391 z,
1392 w,
1393 0,
1394 n,
1395 par,
1396 stack,
1397 *params,
1398 );
1399 if info != 0 {
1400 return Err(EvdError::NoConvergence);
1401 }
1402 for j in 0..n {
1404 for i in j + 1..n {
1405 h[(i, j)] = zero();
1406 }
1407 }
1408 Ok(())
1409}
1410#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1420pub struct SchurReorderError {
1421 pub position: usize,
1423}
1424pub fn reorder_schur_scratch<T: ComplexField>(dim: usize) -> StackReq {
1434 StackReq::all_of(&[
1435 temp_mat_scratch::<T>(4, dim),
1436 temp_mat_scratch::<T>(dim, 4),
1437 temp_mat_scratch::<T>(dim, 4),
1438 ])
1439}
1440pub fn reorder_schur_real<T: RealField>(
1483 mut t: MatMut<'_, T>,
1484 mut q: Option<MatMut<'_, T>>,
1485 mut ifst: usize,
1486 mut ilst: usize,
1487 _par: Par,
1488 stack: &mut MemStack,
1489) -> Result<usize, SchurReorderError> {
1490 let n = t.nrows();
1491 assert!(t.ncols() == n && ifst < n && ilst < n);
1492 if let Some(q) = q.rb() {
1493 assert!(q.nrows() == n && q.ncols() == n);
1494 }
1495 let (mut saved_rows, stack) = temp_mat_zeroed::<T, _, _>(4, n, stack);
1496 let (mut saved_cols, stack) = temp_mat_zeroed::<T, _, _>(n, 4, stack);
1497 let (mut saved_q, _) = temp_mat_zeroed::<T, _, _>(n, 4, stack);
1498 let mut saved_rows = saved_rows.as_mat_mut();
1499 let mut saved_cols = saved_cols.as_mat_mut();
1500 let mut saved_q = saved_q.as_mat_mut();
1501 if ifst > 0 && t[(ifst, ifst - 1)] != zero() {
1502 ifst -= 1;
1503 }
1504 if ilst > 0 && t[(ilst, ilst - 1)] != zero() {
1505 ilst -= 1;
1506 }
1507 let block_size = |t: MatRef<'_, T>, i: usize| {
1508 if i + 1 < n && t[(i + 1, i)] != zero() {
1509 2
1510 } else {
1511 1
1512 }
1513 };
1514 let moving = block_size(t.rb(), ifst);
1515 if ifst < ilst {
1516 let dest = block_size(t.rb(), ilst);
1517 ilst = ilst + dest - moving;
1518 }
1519 let mut here = ifst;
1520 while here != ilst {
1521 let down = here < ilst;
1522 let next = if down {
1523 block_size(t.rb(), here + moving)
1524 } else if here > 1 && t[(here - 1, here - 2)] != zero() {
1525 2
1526 } else {
1527 1
1528 };
1529 let start = if down { here } else { here - next };
1530 let (n1, n2) = if down { (moving, next) } else { (next, moving) };
1531 let k = n1 + n2;
1536 let w = n - start;
1537 let h = start + k;
1538 saved_rows
1539 .rb_mut()
1540 .submatrix_mut(0, 0, k, w)
1541 .copy_from(t.rb().submatrix(start, start, k, w));
1542 saved_cols
1543 .rb_mut()
1544 .submatrix_mut(0, 0, h, k)
1545 .copy_from(t.rb().submatrix(0, start, h, k));
1546 if let Some(q) = q.rb() {
1547 saved_q
1548 .rb_mut()
1549 .submatrix_mut(0, 0, n, k)
1550 .copy_from(q.submatrix(0, start, n, k));
1551 }
1552 let status = schur::real_schur::schur_swap_checked(
1553 t.rb_mut(),
1554 q.rb_mut(),
1555 start,
1556 n1,
1557 n2,
1558 true,
1559 );
1560 let finite = t.rb().submatrix(start, start, k, w).is_all_finite()
1561 && t.rb().submatrix(0, start, h, k).is_all_finite()
1562 && q.rb()
1563 .is_none_or(|q| q.submatrix(0, start, n, k).is_all_finite());
1564 if status != 0 || !finite {
1565 t.rb_mut()
1566 .submatrix_mut(0, start, h, k)
1567 .copy_from(saved_cols.rb().submatrix(0, 0, h, k));
1568 t.rb_mut()
1569 .submatrix_mut(start, start, k, w)
1570 .copy_from(saved_rows.rb().submatrix(0, 0, k, w));
1571 if let Some(q) = q.rb_mut() {
1572 q.submatrix_mut(0, start, n, k)
1573 .copy_from(saved_q.rb().submatrix(0, 0, n, k));
1574 }
1575 return Err(SchurReorderError { position: here });
1576 }
1577 here = if down { here + next } else { here - next };
1578 }
1579 Ok(here)
1580}
1581pub fn reorder_schur_cplx<T: ComplexField>(
1608 mut t: MatMut<'_, T>,
1609 mut q: Option<MatMut<'_, T>>,
1610 ifst: usize,
1611 ilst: usize,
1612 _par: Par,
1613 _stack: &mut MemStack,
1614) -> usize {
1615 let n = t.nrows();
1616 assert!(t.ncols() == n && ifst < n && ilst < n);
1617 if let Some(q) = q.rb() {
1618 assert!(q.nrows() == n && q.ncols() == n);
1619 }
1620 let mut here = ifst;
1621 while here != ilst {
1622 let start = if here < ilst { here } else { here - 1 };
1623 schur::complex_schur::schur_swap(t.rb_mut(), q.rb_mut(), start);
1624 here = if here < ilst { here + 1 } else { here - 1 };
1625 }
1626 here
1627}
1628pub fn eigenvectors_from_schur_scratch<T: ComplexField>(
1637 dim: usize,
1638) -> StackReq {
1639 temp_mat_scratch::<T>(dim, dim)
1640}
1641pub fn eigenvectors_from_schur_real<T: RealField>(
1674 t: MatRef<'_, T>,
1675 q: Option<MatRef<'_, T>>,
1676 mut x: MatMut<'_, T>,
1677 mut par: Par,
1678 stack: &mut MemStack,
1679 params: Spec<EvdFromSchurParams, T>,
1680) -> Result<(), EvdError> {
1681 let n = t.nrows();
1682 assert!(t.ncols() == n && x.nrows() == n && x.ncols() == n);
1683 if let Some(q) = q {
1684 assert!(q.nrows() == n && q.ncols() == n);
1685 }
1686 if !t.is_all_finite() {
1687 return Err(EvdError::NoConvergence);
1688 }
1689 let (mut v, _) = temp_mat_zeroed::<T, _, _>(n, n, stack);
1690 let mut v = v.as_mat_mut();
1691 evd_from_real_schur_imp(t, v.rb_mut(), par.reborrow(), *params);
1692 if let Some(q) = q {
1693 linalg::matmul::matmul(
1694 x.rb_mut(),
1695 Accum::Replace,
1696 q,
1697 v.rb(),
1698 one(),
1699 par,
1700 );
1701 } else {
1702 x.copy_from(v.rb());
1703 }
1704 normalize_eigenvectors(x.rb_mut(), t);
1705 if eigenvectors_ok(x.rb()) {
1706 Ok(())
1707 } else {
1708 Err(EvdError::NoConvergence)
1709 }
1710}
1711pub fn eigenvectors_from_schur_cplx<T: ComplexField>(
1743 t: MatRef<'_, T>,
1744 q: Option<MatRef<'_, T>>,
1745 mut x: MatMut<'_, T>,
1746 mut par: Par,
1747 stack: &mut MemStack,
1748 params: Spec<EvdFromSchurParams, T>,
1749) -> Result<(), EvdError> {
1750 let n = t.nrows();
1751 assert!(t.ncols() == n && x.nrows() == n && x.ncols() == n);
1752 if let Some(q) = q {
1753 assert!(q.nrows() == n && q.ncols() == n);
1754 }
1755 if !t.is_all_finite() {
1756 return Err(EvdError::NoConvergence);
1757 }
1758 let (mut v, _) = temp_mat_zeroed::<T, _, _>(n, n, stack);
1759 let mut v = v.as_mat_mut();
1760 evd_from_cplx_schur_imp(t, Conj::No, v.rb_mut(), par.reborrow(), *params);
1761 if let Some(q) = q {
1762 linalg::matmul::matmul(
1763 x.rb_mut(),
1764 Accum::Replace,
1765 q,
1766 v.rb(),
1767 one(),
1768 par,
1769 );
1770 } else {
1771 x.copy_from(v.rb());
1772 }
1773 normalize_eigenvectors(x.rb_mut(), t);
1774 if eigenvectors_ok(x.rb()) {
1775 Ok(())
1776 } else {
1777 Err(EvdError::NoConvergence)
1778 }
1779}
1780fn evd_imp<T: ComplexField>(
1781 A: MatRef<'_, T>,
1782 s: ColMut<'_, T>,
1783 s_im: Option<ColMut<'_, T>>,
1784 u_left: Option<MatMut<'_, T>>,
1785 u_right: Option<MatMut<'_, T>>,
1786 mut par: Par,
1787 stack: &mut MemStack,
1788 params: EvdParams,
1789) -> Result<(), EvdError> {
1790 let n = A.nrows();
1791 if n == 0 {
1792 return Ok(());
1793 }
1794 for j in 0..n {
1795 for i in 0..n {
1796 if !A[(i, j)].is_finite() {
1797 return Err(EvdError::NoConvergence);
1798 }
1799 }
1800 }
1801 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(
1802 n - 1,
1803 n - 1,
1804 );
1805 let mut s = s;
1806 let mut s_im = s_im;
1807 let (mut H, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1808 let (mut Z, stack) = unsafe {
1809 temp_mat_uninit::<T, _, _>(
1810 n,
1811 if u_left.is_some() || u_right.is_some() {
1812 n
1813 } else {
1814 0
1815 },
1816 stack,
1817 )
1818 };
1819 let mut H = H.as_mat_mut();
1820 let mut Z = if u_left.is_some() || u_right.is_some() {
1821 Some(Z.as_mat_mut())
1822 } else {
1823 None
1824 };
1825 H.copy_from(A);
1826 {
1827 let (mut householder, stack) =
1828 unsafe { temp_mat_uninit::<T, _, _>(bs, n - 1, stack) };
1829 let mut householder = householder.as_mat_mut();
1830 hessenberg::hessenberg_in_place(
1831 H.rb_mut(),
1832 householder.rb_mut(),
1833 par.reborrow(),
1834 stack,
1835 params.hessenberg.into(),
1836 );
1837 if let Some(mut Z) = Z.rb_mut() {
1838 Z.fill(zero());
1839 Z.rb_mut().diagonal_mut().fill(one());
1840 linalg::householder::apply_block_householder_sequence_on_the_right_in_place_with_conj(
1841 H.rb().submatrix(1, 0, n - 1, n - 1),
1842 householder.rb(),
1843 Conj::No,
1844 Z.rb_mut().submatrix_mut(1, 1, n - 1, n - 1),
1845 par.reborrow(),
1846 stack,
1847 );
1848 }
1849 for j in 0..n {
1850 for i in j + 2..n {
1851 H[(i, j)] = zero();
1852 }
1853 }
1854 }
1855 if const { T::IS_REAL } {
1856 let (info, _, _) = schur::real_schur::multishift_qr::<T::Real>(
1857 unsafe { core::mem::transmute(Z.is_some()) },
1858 unsafe { core::mem::transmute(H.rb_mut()) },
1859 unsafe { core::mem::transmute(Z.rb_mut()) },
1860 unsafe { core::mem::transmute(s.rb_mut()) },
1861 unsafe { core::mem::transmute(s_im.rb_mut().unwrap()) },
1862 0,
1863 n,
1864 par.reborrow(),
1865 stack,
1866 params.schur,
1867 );
1868 if info != 0 {
1869 return Err(EvdError::NoConvergence);
1870 }
1871 } else {
1872 let (info, _, _) = schur::complex_schur::multishift_qr::<T>(
1873 Z.is_some(),
1874 H.rb_mut(),
1875 Z.rb_mut(),
1876 s.rb_mut(),
1877 0,
1878 n,
1879 par.reborrow(),
1880 stack,
1881 params.schur,
1882 );
1883 if info != 0 {
1884 return Err(EvdError::NoConvergence);
1885 }
1886 }
1887 let H = H.rb();
1888 if let (Some(mut u), Some(Z)) = (u_right, Z.rb()) {
1889 let (mut X, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1890 let mut X = X.as_mat_mut();
1891 if const { T::IS_REAL } {
1892 evd_from_real_schur_imp::<T::Real>(
1893 unsafe { core::mem::transmute(H) },
1894 unsafe { core::mem::transmute(X.rb_mut()) },
1895 par.reborrow(),
1896 params.evd_from_schur,
1897 );
1898 } else {
1899 evd_from_cplx_schur_imp::<T>(
1900 H,
1901 Conj::No,
1902 X.rb_mut(),
1903 par.reborrow(),
1904 params.evd_from_schur,
1905 );
1906 }
1907 linalg::matmul::triangular::matmul(
1908 u.rb_mut(),
1909 BlockStructure::Rectangular,
1910 Accum::Replace,
1911 Z,
1912 BlockStructure::Rectangular,
1913 X.rb(),
1914 BlockStructure::TriangularUpper,
1915 one(),
1916 par.reborrow(),
1917 );
1918 normalize_eigenvectors(u.rb_mut(), H);
1919 if !eigenvectors_ok(u.rb()) {
1920 return Err(EvdError::NoConvergence);
1921 }
1922 }
1923 if let (Some(mut u), Some(Z)) = (u_left, Z.rb()) {
1924 let (mut X, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1925 let mut X = X.as_mat_mut().reverse_rows_mut();
1926 if const { T::IS_REAL } {
1927 evd_from_real_schur_imp::<T::Real>(
1928 unsafe {
1929 core::mem::transmute(H.transpose().reverse_rows_and_cols())
1930 },
1931 unsafe { core::mem::transmute(X.rb_mut()) },
1932 par.reborrow(),
1933 params.evd_from_schur,
1934 );
1935 } else {
1936 evd_from_cplx_schur_imp::<T>(
1937 H.transpose().reverse_rows_and_cols(),
1938 Conj::Yes,
1939 X.rb_mut(),
1940 par.reborrow(),
1941 params.evd_from_schur,
1942 );
1943 }
1944 linalg::matmul::triangular::matmul(
1945 u.rb_mut(),
1946 BlockStructure::Rectangular,
1947 Accum::Replace,
1948 Z,
1949 BlockStructure::Rectangular,
1950 X.rb().reverse_rows_and_cols(),
1951 BlockStructure::TriangularLower,
1952 one(),
1953 par,
1954 );
1955 normalize_eigenvectors(u.rb_mut(), H);
1956 if !eigenvectors_ok(u.rb()) {
1957 return Err(EvdError::NoConvergence);
1958 }
1959 }
1960 Ok(())
1961}
1962#[track_caller]
1971pub fn evd_cplx<T: RealField>(
1972 A: MatRef<'_, Complex<T>>,
1973 s: DiagMut<'_, Complex<T>>,
1974 u_left: Option<MatMut<'_, Complex<T>>>,
1975 u_right: Option<MatMut<'_, Complex<T>>>,
1976 par: Par,
1977 stack: &mut MemStack,
1978 params: Spec<EvdParams, Complex<T>>,
1979) -> Result<(), EvdError> {
1980 let n = A.nrows();
1981 assert!(all(A.nrows() == n, A.ncols() == n, s.dim() == n));
1982 if let Some(u) = u_left.rb() {
1983 assert!(all(u.nrows() == n, u.ncols() == n));
1984 }
1985 if let Some(u) = u_right.rb() {
1986 assert!(all(u.nrows() == n, u.ncols() == n));
1987 }
1988 evd_imp(
1989 A,
1990 s.column_vector_mut(),
1991 None,
1992 u_left,
1993 u_right,
1994 par,
1995 stack,
1996 params.config,
1997 )
1998}
1999#[track_caller]
2011pub fn evd_real<T: RealField>(
2012 A: MatRef<'_, T>,
2013 s_re: DiagMut<'_, T>,
2014 s_im: DiagMut<'_, T>,
2015 u_left: Option<MatMut<'_, T>>,
2016 u_right: Option<MatMut<'_, T>>,
2017 par: Par,
2018 stack: &mut MemStack,
2019 params: Spec<EvdParams, T>,
2020) -> Result<(), EvdError> {
2021 let n = A.nrows();
2022 assert!(all(
2023 A.nrows() == n,
2024 A.ncols() == n,
2025 s_re.dim() == n,
2026 s_im.dim() == n
2027 ));
2028 if let Some(u) = u_left.rb() {
2029 assert!(all(u.nrows() == n, u.ncols() == n));
2030 }
2031 if let Some(u) = u_right.rb() {
2032 assert!(all(u.nrows() == n, u.ncols() == n));
2033 }
2034 evd_imp(
2035 A,
2036 s_re.column_vector_mut(),
2037 Some(s_im.column_vector_mut()),
2038 u_left,
2039 u_right,
2040 par,
2041 stack,
2042 params.config,
2043 )
2044}
2045#[cfg(test)]
2046mod general_tests {
2047 use super::*;
2048 use crate::assert;
2049 use crate::stats::prelude::*;
2050 use crate::utils::approx::*;
2051 use dyn_stack::MemBuffer;
2052 fn test_cplx_evd(mat: MatRef<'_, c64>) {
2053 let n = mat.nrows();
2054 let params = Spec::new(EvdParams {
2055 hessenberg: auto!(c64),
2056 schur: auto!(c64),
2057 evd_from_schur: EvdFromSchurParams {
2058 recursion_threshold: 8,
2059 ..auto!(c64)
2060 },
2061 ..auto!(c64)
2062 });
2063 let approx_eq = CwiseMat(ApproxEq::eps() * (8.0 * n as f64).sqrt());
2064 let mut s = Mat::zeros(n, n);
2065 {
2066 let mut ul = Mat::zeros(n, n);
2067 let mut ur = Mat::zeros(n, n);
2068 evd_cplx(
2069 mat.as_ref(),
2070 s.as_mut().diagonal_mut(),
2071 Some(ul.as_mut()),
2072 Some(ur.as_mut()),
2073 Par::Seq,
2074 MemStack::new(&mut MemBuffer::new(evd_scratch::<c64>(
2075 n,
2076 ComputeEigenvectors::Yes,
2077 ComputeEigenvectors::Yes,
2078 ParShape::Seq,
2079 params,
2080 ))),
2081 params,
2082 )
2083 .unwrap();
2084 assert!(& ur * & s ~ mat * & ur);
2085 assert!(& s * ul.adjoint() ~ ul.adjoint() * mat);
2086 }
2087 }
2088 fn test_real_evd(mat: MatRef<'_, f64>) {
2089 let n = mat.nrows();
2090 let params = Spec::new(EvdParams {
2091 hessenberg: auto!(f64),
2092 schur: auto!(f64),
2093 evd_from_schur: EvdFromSchurParams {
2094 recursion_threshold: 8,
2095 ..auto!(f64)
2096 },
2097 ..auto!(f64)
2098 });
2099 let approx_eq =
2100 CwiseMat(ApproxEq::<f64>::eps() * (8.0 * n as f64).sqrt());
2101 let mut s_re = Diag::zeros(n);
2102 let mut s_im = Diag::zeros(n);
2103 {
2104 let mut ul = Mat::zeros(n, n);
2105 let mut ur = Mat::zeros(n, n);
2106 evd_real(
2107 mat.as_ref(),
2108 s_re.as_mut(),
2109 s_im.as_mut(),
2110 Some(ul.as_mut()),
2111 Some(ur.as_mut()),
2112 Par::Seq,
2113 MemStack::new(&mut MemBuffer::new(evd_scratch::<f64>(
2114 n,
2115 ComputeEigenvectors::Yes,
2116 ComputeEigenvectors::Yes,
2117 ParShape::Seq,
2118 params,
2119 ))),
2120 params,
2121 )
2122 .unwrap();
2123 let mut i = 0;
2124 while i < n {
2125 if s_im[i] == 0.0 {
2126 let ur = ur.col(i);
2127 let ul = ul.col(i);
2128 let s = Scale(s_re[i]);
2129 assert!((& ur * s).as_mat() ~ (mat * & ur).as_mat());
2130 assert!(
2131 (& ul.adjoint() * s).as_mat() ~ (& ul.adjoint() * mat).as_mat()
2132 );
2133 i += 1;
2134 } else {
2135 let re = ur.col(i);
2136 let im = ur.col(i + 1);
2137 let ur = &Col::from_fn(n, |i| c64::new(re[i], im[i]));
2138 let re = ul.col(i);
2139 let im = ul.col(i + 1);
2140 let ul = &Col::from_fn(n, |i| c64::new(re[i], im[i]));
2141 let mat =
2142 &Mat::from_fn(n, n, |i, j| c64::from(mat[(i, j)]));
2143 let s = Scale(c64::new(s_re[i], s_im[i]));
2144 let approx_eq =
2145 CwiseMat(ApproxEq::eps() * (8.0 * n as f64).sqrt());
2146 assert!((ur * s).as_mat() ~ (mat * ur).as_mat());
2147 assert!((ul.adjoint() * s).as_mat() ~ (ul.adjoint() * mat).as_mat());
2148 i += 2;
2149 }
2150 }
2151 }
2152 }
2153 #[test]
2154 fn test_cplx() {
2155 let rng = &mut StdRng::seed_from_u64(1);
2156 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
2157 let mat = CwiseMatDistribution {
2158 nrows: n,
2159 ncols: n,
2160 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
2161 }
2162 .rand::<Mat<c64>>(rng);
2163 test_cplx_evd(mat.as_ref());
2164 }
2165 }
2166 #[test]
2167 fn test_real() {
2168 let rng = &mut StdRng::seed_from_u64(0);
2169 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
2170 let mat = CwiseMatDistribution {
2171 nrows: n,
2172 ncols: n,
2173 dist: StandardNormal,
2174 }
2175 .rand::<Mat<f64>>(rng);
2176 test_real_evd(mat.as_ref());
2177 }
2178 }
2179}
2180#[cfg(test)]
2181mod eig_2131_tests {
2182 use super::*;
2188 use crate::assert;
2189 use dyn_stack::MemBuffer;
2190
2191 fn relative_residual<T: RealField>(
2194 mat: MatRef<'_, T>,
2195 v: ColRef<'_, T>,
2196 lambda: T,
2197 ) -> T {
2198 let n = mat.nrows();
2199 let mut anorm = zero::<T>();
2200 for j in 0..n {
2201 let mut col = zero::<T>();
2202 for i in 0..n {
2203 col += mat[(i, j)].abs1();
2204 }
2205 if col > anorm {
2206 anorm = col;
2207 }
2208 }
2209 let mut vnorm2 = zero::<T>();
2210 let mut rnorm2 = zero::<T>();
2211 for i in 0..n {
2212 let mut acc = zero::<T>();
2213 for j in 0..n {
2214 acc += &mat[(i, j)] * &v[j];
2215 }
2216 acc -= &v[i] * λ
2217 rnorm2 += acc.abs2();
2218 vnorm2 += v[i].abs2();
2219 }
2220 rnorm2.sqrt() / (anorm * vnorm2.sqrt())
2221 }
2222
2223 fn evd_real_vectors<T: RealField>(
2224 mat: MatRef<'_, T>,
2225 params: Spec<EvdParams, T>,
2226 ) -> (Diag<T>, Diag<T>, Mat<T>, Mat<T>) {
2227 let n = mat.nrows();
2228 let mut s_re = Diag::zeros(n);
2229 let mut s_im = Diag::zeros(n);
2230 let mut u = Mat::zeros(n, n);
2231 let mut ul = Mat::zeros(n, n);
2232 let mut mem = MemBuffer::new(evd_scratch::<T>(
2233 n,
2234 ComputeEigenvectors::Yes,
2235 ComputeEigenvectors::Yes,
2236 ParShape::Seq,
2237 params,
2238 ));
2239 evd_real(
2240 mat,
2241 s_re.as_mut(),
2242 s_im.as_mut(),
2243 Some(ul.as_mut()),
2244 Some(u.as_mut()),
2245 Par::Seq,
2246 MemStack::new(&mut mem),
2247 params,
2248 )
2249 .unwrap();
2250 (s_re, s_im, u, ul)
2251 }
2252
2253 fn jordan<T: RealField>(n: usize) -> Mat<T> {
2255 let two = from_f64::<T>(2.0);
2256 let one = one::<T>();
2257 Mat::<T>::from_fn(n, n, |i, j| {
2258 if i == j {
2259 two.copy()
2260 } else if j == i + 1 {
2261 one.copy()
2262 } else {
2263 zero::<T>()
2264 }
2265 })
2266 }
2267
2268 fn check_jordan<T: RealField>(n: usize, threshold: usize, tol: f64) {
2269 let params = Spec::new(EvdParams {
2270 evd_from_schur: EvdFromSchurParams {
2271 recursion_threshold: threshold,
2272 ..auto!(T)
2273 },
2274 ..auto!(T)
2275 });
2276 let mat = jordan::<T>(n);
2277 let (s_re, _, u, _) = evd_real_vectors(mat.as_ref(), params);
2278 let tol = from_f64::<T>(tol);
2279 for j in 0..n {
2280 let v = u.col(j);
2281 assert!(v.iter().all(|x| x.is_finite()));
2282 assert!((v.norm_l2() - one::<T>()).abs() < from_f64::<T>(1e-5));
2283 assert!(relative_residual(mat.as_ref(), v, s_re[j].copy()) < tol);
2284 }
2285 }
2286
2287 #[test]
2288 fn jordan_eigenvectors_are_finite_and_unit_norm() {
2289 for threshold in [3usize, 64] {
2294 for n in [2, 4, 8, 20, 26, 30, 64, 65, 100] {
2295 check_jordan::<f64>(n, threshold, 1e-10);
2296 }
2297 for n in [2, 4, 8, 16, 30] {
2298 check_jordan::<f32>(n, threshold, 1e-3);
2299 }
2300 }
2301 }
2302
2303 #[test]
2304 fn conjugate_pair_eigenvector_has_unit_norm() {
2305 fn run<T: RealField>(threshold: usize) {
2309 let n = 6;
2310 let mat = Mat::<T>::from_fn(n, n, |i, j| {
2311 if i == j {
2312 if i >= 4 {
2313 zero::<T>()
2314 } else {
2315 from_f64::<T>((i + 1) as f64)
2316 }
2317 } else if i == 4 && j == 5 {
2318 one::<T>()
2319 } else if i == 5 && j == 4 {
2320 -one::<T>()
2321 } else if i + 2 <= j {
2322 from_f64::<T>(0.25)
2323 } else {
2324 zero::<T>()
2325 }
2326 });
2327 let params = Spec::new(EvdParams {
2328 evd_from_schur: EvdFromSchurParams {
2329 recursion_threshold: threshold,
2330 ..auto!(T)
2331 },
2332 ..auto!(T)
2333 });
2334 let (s_re, s_im, u, ul) = evd_real_vectors(mat.as_ref(), params);
2335 let tol = from_f64::<T>(1e-5);
2336 let mut j = 0;
2338 while j < n {
2339 let v = u.col(j);
2340 let vl = ul.col(j);
2341 if s_im[j] == zero::<T>() {
2342 assert!((v.norm_l2() - one::<T>()).abs() < tol);
2343 assert!((vl.norm_l2() - one::<T>()).abs() < tol);
2344 assert!(
2345 relative_residual(mat.as_ref(), v, s_re[j].copy())
2346 < from_f64::<T>(1e-4)
2347 );
2348 j += 1;
2349 } else {
2350 let nrm = v.norm_l2().hypot(&u.col(j + 1).norm_l2());
2351 assert!((nrm - one::<T>()).abs() < tol);
2352 let nrm = vl.norm_l2().hypot(&ul.col(j + 1).norm_l2());
2353 assert!((nrm - one::<T>()).abs() < tol);
2354 j += 2;
2355 }
2356 }
2357 }
2358 for threshold in [4usize, 64] {
2359 run::<f64>(threshold);
2360 run::<f32>(threshold);
2361 }
2362 }
2363
2364 #[test]
2365 fn complex_eigenvectors_have_unit_norm() {
2366 fn run<T: RealField>(threshold: usize) {
2368 let n = 6;
2369 let mat = Mat::<Complex<T>>::from_fn(n, n, |i, j| {
2370 Complex::new(
2371 from_f64::<T>(i as f64)
2372 + from_f64::<T>(j as f64) * from_f64::<T>(0.1),
2373 from_f64::<T>(i as f64 - j as f64) * from_f64::<T>(0.3),
2374 )
2375 });
2376 let params = Spec::new(EvdParams {
2377 evd_from_schur: EvdFromSchurParams {
2378 recursion_threshold: threshold,
2379 ..auto!(Complex<T>)
2380 },
2381 ..auto!(Complex<T>)
2382 });
2383 let mut s = Diag::zeros(n);
2384 let mut u = Mat::zeros(n, n);
2385 let mut mem = MemBuffer::new(evd_scratch::<Complex<T>>(
2386 n,
2387 ComputeEigenvectors::No,
2388 ComputeEigenvectors::Yes,
2389 ParShape::Seq,
2390 params,
2391 ));
2392 evd_cplx(
2393 mat.as_ref(),
2394 s.as_mut(),
2395 None,
2396 Some(u.as_mut()),
2397 Par::Seq,
2398 MemStack::new(&mut mem),
2399 params,
2400 )
2401 .unwrap();
2402 let tol = from_f64::<T>(1e-5);
2403 for j in 0..n {
2404 assert!((u.col(j).norm_l2() - one::<T>()).abs() < tol);
2405 }
2406 }
2407 for threshold in [4usize, 64] {
2408 run::<f64>(threshold);
2409 run::<f32>(threshold);
2410 }
2411 }
2412}
2413#[cfg(test)]
2414mod range_limit_vector_tests {
2415 use super::*;
2425 use crate::assert;
2426 use dyn_stack::MemBuffer;
2427
2428 const SHIFT_2X2: [[f64; 2]; 2] = [[1.0, 1.0], [0.0, 0.5]];
2430 const PAIR_3X3: [[f64; 3]; 3] =
2433 [[0.0, -1.0, 1.0], [1.0, 0.0, 0.0], [0.0, 0.0, 0.5]];
2434 const TWO_PAIRS_4X4: [[f64; 4]; 4] = [
2436 [0.0, -1.0, 1.0, 0.0],
2437 [1.0, 0.0, 0.0, 0.0],
2438 [0.0, 0.0, 0.0, -0.5],
2439 [0.0, 0.0, 0.5, 0.0],
2440 ];
2441 const JORDAN_2X2: [[f64; 2]; 2] = [[1.0, 1.0], [0.0, 1.0]];
2443
2444 fn unit<T: RealField, const N: usize>(
2445 mat: &[[f64; N]; N],
2446 ) -> Mat<Complex<T>> {
2447 Mat::from_fn(N, N, |i, j| {
2448 Complex::new(from_f64::<T>(mat[i][j]), zero::<T>())
2449 })
2450 }
2451
2452 fn scaled_real<T: RealField>(
2454 unit: MatRef<'_, Complex<T>>,
2455 scale: &T,
2456 ) -> Mat<T> {
2457 Mat::from_fn(unit.nrows(), unit.ncols(), |i, j| {
2458 &unit[(i, j)].re * scale
2459 })
2460 }
2461
2462 fn scaled_cplx<T: RealField>(
2464 unit: MatRef<'_, Complex<T>>,
2465 scale: &T,
2466 ) -> Mat<Complex<T>> {
2467 Mat::from_fn(unit.nrows(), unit.ncols(), |i, j| {
2468 Complex::new(&unit[(i, j)].re * scale, &unit[(i, j)].im * scale)
2469 })
2470 }
2471
2472 fn complex_unit<T: RealField>() -> Mat<Complex<T>> {
2476 let z = |re: f64, im: f64| {
2477 Complex::new(from_f64::<T>(re), from_f64::<T>(im))
2478 };
2479 Mat::from_fn(3, 3, |i, j| match (i, j) {
2480 (0, 1) => z(-1.0, 0.5),
2481 (0, 2) => z(1.0, -1.0),
2482 (1, 1) => z(0.5, 0.0),
2483 (2, 2) => z(0.25, 0.0),
2484 _ => z(0.0, 0.0),
2485 })
2486 }
2487
2488 fn residual<T: RealField>(
2491 a: MatRef<'_, Complex<T>>,
2492 v: &[Complex<T>],
2493 lambda: &Complex<T>,
2494 ) -> (T, T) {
2495 let n = a.nrows();
2496 let mut anorm = zero::<T>();
2497 for j in 0..n {
2498 let mut col = zero::<T>();
2499 for i in 0..n {
2500 col += &a[(i, j)].re.abs() + &a[(i, j)].im.abs();
2501 }
2502 anorm = anorm.fmax(col);
2503 }
2504 let mut rnorm2 = zero::<T>();
2505 let mut vnorm2 = zero::<T>();
2506 for i in 0..n {
2507 let mut acc = Complex::new(zero::<T>(), zero::<T>());
2508 for j in 0..n {
2509 acc += &a[(i, j)] * &v[j];
2510 }
2511 acc -= lambda * &v[i];
2512 rnorm2 += acc.re.abs2() + acc.im.abs2();
2513 vnorm2 += v[i].re.abs2() + v[i].im.abs2();
2514 }
2515 (rnorm2.sqrt() / (anorm * vnorm2.sqrt()), vnorm2.sqrt())
2516 }
2517
2518 fn try_real_vectors<T: RealField>(t: MatRef<'_, T>) -> Option<Mat<T>> {
2519 let n = t.nrows();
2520 let mut x = Mat::zeros(n, n);
2521 let mut mem = MemBuffer::new(eigenvectors_from_schur_scratch::<T>(n));
2522 eigenvectors_from_schur_real(
2523 t,
2524 None,
2525 x.as_mut(),
2526 Par::Seq,
2527 MemStack::new(&mut mem),
2528 Default::default(),
2529 )
2530 .ok()?;
2531 Some(x)
2532 }
2533
2534 fn real_vectors<T: RealField>(t: MatRef<'_, T>) -> Mat<T> {
2535 try_real_vectors(t).unwrap()
2536 }
2537
2538 fn cplx_vectors<T: ComplexField>(t: MatRef<'_, T>) -> Mat<T> {
2539 let n = t.nrows();
2540 let mut x = Mat::zeros(n, n);
2541 let mut mem = MemBuffer::new(eigenvectors_from_schur_scratch::<T>(n));
2542 eigenvectors_from_schur_cplx(
2543 t,
2544 None,
2545 x.as_mut(),
2546 Par::Seq,
2547 MemStack::new(&mut mem),
2548 Default::default(),
2549 )
2550 .unwrap();
2551 x
2552 }
2553
2554 fn real_column<T: RealField>(
2556 x: MatRef<'_, T>,
2557 j: usize,
2558 ) -> Vec<Complex<T>> {
2559 (0..x.nrows())
2560 .map(|i| Complex::new(x[(i, j)].copy(), zero::<T>()))
2561 .collect()
2562 }
2563
2564 fn real_pair<T: RealField>(x: MatRef<'_, T>, j: usize) -> Vec<Complex<T>> {
2566 (0..x.nrows())
2567 .map(|i| Complex::new(x[(i, j)].copy(), x[(i, j + 1)].copy()))
2568 .collect()
2569 }
2570
2571 fn cplx_column<T: RealField>(
2572 x: MatRef<'_, Complex<T>>,
2573 j: usize,
2574 ) -> Vec<Complex<T>> {
2575 (0..x.nrows()).map(|i| x[(i, j)].copy()).collect()
2576 }
2577
2578 fn assert_eigenvector<T: RealField>(
2579 unit: MatRef<'_, Complex<T>>,
2580 v: &[Complex<T>],
2581 lambda: &Complex<T>,
2582 tol: &T,
2583 ) {
2584 let (res, nrm) = residual(unit, v, lambda);
2585 let nrm_err = (&nrm - &one::<T>()).abs();
2586 assert!(nrm_err <= *tol, "unit 2-norm {nrm:?}, expected 1");
2587 assert!(res <= *tol, "relative residual {res:?}");
2588 }
2589
2590 fn check_shift_2x2<T: RealField>(scale: T, tol: T) {
2591 let unit = unit::<T, 2>(&SHIFT_2X2);
2592 let lambda = Complex::new(from_f64::<T>(0.5), zero::<T>());
2593
2594 let x = real_vectors::<T>(scaled_real(unit.as_ref(), &scale).as_ref());
2595 let v = real_column(x.as_ref(), 1);
2596 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2597
2598 let x = cplx_vectors::<Complex<T>>(
2599 scaled_cplx(unit.as_ref(), &scale).as_ref(),
2600 );
2601 let v = cplx_column(x.as_ref(), 1);
2602 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2603 }
2604
2605 fn check_pair_3x3<T: RealField>(scale: T, tol: T, may_fail: bool) {
2608 let unit = unit::<T, 3>(&PAIR_3X3);
2609 let Some(x) =
2610 try_real_vectors::<T>(scaled_real(unit.as_ref(), &scale).as_ref())
2611 else {
2612 assert!(may_fail, "extraction failed at scale {scale:?}");
2613 return;
2614 };
2615 let lambda = Complex::new(zero::<T>(), one::<T>());
2617 let v = real_pair(x.as_ref(), 0);
2618 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2619 let lambda = Complex::new(from_f64::<T>(0.5), zero::<T>());
2620 let v = real_column(x.as_ref(), 2);
2621 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2622 }
2623
2624 fn check_cplx_triangular<T: RealField>(scale: T, tol: T) {
2625 let unit = complex_unit::<T>();
2626 let t = scaled_cplx(unit.as_ref(), &scale);
2627 let x = cplx_vectors(t.as_ref());
2628 for j in 0..3 {
2629 let lambda = unit[(j, j)].copy();
2630 let v = cplx_column(x.as_ref(), j);
2631 assert_eigenvector(unit.as_ref(), &v, &lambda, &tol);
2632 }
2633 }
2634
2635 #[test]
2636 fn two_by_two_at_the_range_limit() {
2637 for (scale, tol) in [(1.0f32, 1e-4f32), (2.0f32.powi(127), 1e-4)] {
2639 check_shift_2x2::<f32>(scale, tol);
2640 }
2641 for (scale, tol) in [(1.0f64, 1e-12f64), (2.0f64.powi(1023), 1e-12)] {
2642 check_shift_2x2::<f64>(scale, tol);
2643 }
2644 }
2645
2646 #[test]
2647 fn complex_triangular_at_the_range_limit() {
2648 for (scale, tol) in [(1.0f32, 1e-4f32), (2.0f32.powi(127), 1e-4)] {
2649 check_cplx_triangular::<f32>(scale, tol);
2650 }
2651 for (scale, tol) in [(1.0f64, 1e-12f64), (2.0f64.powi(1023), 1e-12)] {
2652 check_cplx_triangular::<f64>(scale, tol);
2653 }
2654 }
2655
2656 #[test]
2657 fn conjugate_pair_block_is_never_a_wrong_success() {
2658 check_pair_3x3::<f32>(1.0, 1e-4, false);
2659 check_pair_3x3::<f32>(2.0f32.powi(127), 1e-4, true);
2660 check_pair_3x3::<f64>(1.0, 1e-12, false);
2661 check_pair_3x3::<f64>(2.0f64.powi(1023), 1e-12, true);
2662 for (scale, tol, may_fail) in
2665 [(1.0, 1e-12, false), (2.0f64.powi(1023), 1e-12, true)]
2666 {
2667 let unit = unit::<f64, 4>(&TWO_PAIRS_4X4);
2668 let t = scaled_real(unit.as_ref(), &scale);
2669 match try_real_vectors::<f64>(t.as_ref()) {
2670 Some(x) => {
2671 for (j, im) in [(0, 1.0), (2, 0.5)] {
2672 let v = real_pair(x.as_ref(), j);
2673 assert_eigenvector(
2674 unit.as_ref(),
2675 &v,
2676 &Complex::new(0.0, im),
2677 &tol,
2678 );
2679 }
2680 },
2681 None => assert!(may_fail, "two pairs failed at scale 1"),
2682 }
2683 }
2684 let s = 2.0f64.powi(1023);
2686 let t = crate::mat![[s, -s, 1.0], [s, s, 0.0], [0.0, 0.0, -s]];
2687 if let Some(x) = try_real_vectors::<f64>(t.as_ref()) {
2688 let unit = Mat::from_fn(3, 3, |i, j| {
2689 Complex::new(t[(i, j)] / s, 0.0)
2690 });
2691 let v = real_pair(x.as_ref(), 0);
2692 assert_eigenvector(
2693 unit.as_ref(),
2694 &v,
2695 &Complex::new(1.0, 1.0),
2696 &1e-12,
2697 );
2698 }
2699 }
2700
2701 #[test]
2704 fn conjugate_pair_with_a_wide_dynamic_range() {
2705 let t = crate::mat![
2706 [0.0, -2.0f64.powi(1023), 1.0],
2707 [2.0f64.powi(-1022), 0.0, 0.0],
2708 [0.0, 0.0, 0.5],
2709 ];
2710 let x = real_vectors::<f64>(t.as_ref());
2711 assert!(x.is_all_finite());
2712 let unit = Mat::from_fn(3, 3, |i, j| Complex::new(t[(i, j)], 0.0));
2713 let lambda = Complex::new(0.5, 0.0);
2714 let v = real_column(x.as_ref(), 2);
2715 assert_eigenvector(unit.as_ref(), &v, &lambda, &1e-12);
2716 }
2717
2718 #[test]
2722 fn subnormal_shift_clamp_is_unchanged() {
2723 let scale = 2.0f64.powi(-1023);
2724 let unit = unit::<f64, 2>(&JORDAN_2X2);
2725 let x =
2726 real_vectors::<f64>(scaled_real(unit.as_ref(), &scale).as_ref());
2727 let lambda = Complex::new(1.0, 0.0);
2728 let v = real_column(x.as_ref(), 1);
2729 assert_eigenvector(unit.as_ref(), &v, &lambda, &1e-12);
2730 }
2731}
2732#[cfg(test)]
2733mod self_adjoint_tests {
2734 use super::*;
2735 use crate::assert;
2736 use crate::stats::prelude::*;
2737 use crate::utils::approx::*;
2738 use dyn_stack::MemBuffer;
2739 fn test_self_adjoint_evd<T: ComplexField>(mat: MatRef<'_, T>) {
2740 let n = mat.nrows();
2741 let params = Spec::new(SelfAdjointEvdParams {
2742 recursion_threshold: 8,
2743 ..auto!(T)
2744 });
2745 let approx_eq = CwiseMat(
2746 ApproxEq::<T::Real>::eps()
2747 * from_f64::<T::Real>(8.0 * n as f64).sqrt(),
2748 );
2749 let mut s = Mat::zeros(n, n);
2750 {
2751 let mut u = Mat::zeros(n, n);
2752 self_adjoint_evd(
2753 mat.as_ref(),
2754 s.as_mut().diagonal_mut(),
2755 Some(u.as_mut()),
2756 Par::Seq,
2757 MemStack::new(&mut MemBuffer::new(
2758 self_adjoint_evd_scratch::<T>(
2759 n,
2760 ComputeEigenvectors::Yes,
2761 ParShape::Seq,
2762 params,
2763 ),
2764 )),
2765 params,
2766 )
2767 .unwrap();
2768 let reconstructed = &u * &s * u.adjoint();
2769 assert!(reconstructed ~ mat);
2770 }
2771 {
2772 let mut s2 = Mat::zeros(n, n);
2773 self_adjoint_evd(
2774 mat.as_ref(),
2775 s2.as_mut().diagonal_mut(),
2776 None,
2777 Par::Seq,
2778 MemStack::new(&mut MemBuffer::new(
2779 self_adjoint_evd_scratch::<T>(
2780 n,
2781 ComputeEigenvectors::No,
2782 ParShape::Seq,
2783 params,
2784 ),
2785 )),
2786 params,
2787 )
2788 .unwrap();
2789 assert!(s2 ~ s);
2790 }
2791 }
2792 #[test]
2793 fn test_real() {
2794 let rng = &mut StdRng::seed_from_u64(1);
2795 for n in [1, 2, 4, 10, 15, 20, 50, 100, 150] {
2796 let mat = CwiseMatDistribution {
2797 nrows: n,
2798 ncols: n,
2799 dist: StandardNormal,
2800 }
2801 .rand::<Mat<f64>>(rng);
2802 let mat = &mat + mat.adjoint();
2803 test_self_adjoint_evd(mat.as_ref());
2804 }
2805 }
2806 #[test]
2807 fn test_cplx() {
2808 let rng = &mut StdRng::seed_from_u64(1);
2809 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
2810 let mat = CwiseMatDistribution {
2811 nrows: n,
2812 ncols: n,
2813 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
2814 }
2815 .rand::<Mat<c64>>(rng);
2816 let mat = &mat + mat.adjoint();
2817 test_self_adjoint_evd(mat.as_ref());
2818 }
2819 }
2820 #[test]
2821 fn test_special() {
2822 for n in [1, 2, 4, 10, 15, 20, 50, 100, 150] {
2823 test_self_adjoint_evd(Mat::full(n, n, 0.0).as_ref());
2824 test_self_adjoint_evd(Mat::full(n, n, c64::ZERO).as_ref());
2825 test_self_adjoint_evd(Mat::full(n, n, 1.0).as_ref());
2826 test_self_adjoint_evd(Mat::full(n, n, c64::ONE).as_ref());
2827 test_self_adjoint_evd(Mat::<f64>::identity(n, n).as_ref());
2828 test_self_adjoint_evd(Mat::<c64>::identity(n, n).as_ref());
2829 }
2830 }
2831 #[test]
2832 fn test_pinv() {
2833 let rng = &mut StdRng::seed_from_u64(0);
2834 let n = 36;
2835 let mat = CwiseMatDistribution {
2836 nrows: n,
2837 ncols: n,
2838 dist: StandardNormal,
2839 }
2840 .rand::<Mat<f64>>(rng);
2841 let mat = &mat + mat.adjoint();
2842 let pinv = mat.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap().pseudoinverse(Par::Seq);
2843 let err = &mat * &pinv - Mat::<f64>::identity(n, n);
2844 assert!(err.norm_max() < 1e-10);
2845 }
2846}
2847#[cfg(test)]
2848mod scale_and_empty_tests {
2849 use super::*;
2854 #[test]
2855 fn two_by_two_finite_spectrum_at_the_range_limit() {
2856 let a = crate::mat![[1e308f64, 0.0], [0.0, 1e308]];
2857 let eig = a.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap();
2858 assert_eq!(eig.S()[0], 1e308);
2859 assert_eq!(eig.S()[1], 1e308);
2860 let a = crate::mat![[1e38f32, 0.0], [0.0, 1e38]];
2861 let eig = a.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap();
2862 assert_eq!(eig.S()[0], 1e38);
2863 assert_eq!(eig.S()[1], 1e38);
2864 }
2865 #[test]
2868 fn two_by_two_subnormal_diagonal() {
2869 let q = f64::from_bits(1);
2870 let a = Mat::<f64>::from_fn(2, 2, |i, j| if i == j { q } else { 0.0 });
2871 let eig = a.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap();
2872 assert_eq!(eig.S()[0], q);
2873 assert_eq!(eig.S()[1], q);
2874 }
2875 #[test]
2876 fn empty_matrix() {
2877 let a = Mat::<f64>::zeros(0, 0);
2878 let eig = a.self_adjoint_eigen(Side::Lower, Par::Seq).unwrap();
2879 assert_eq!(eig.U().shape(), (0, 0));
2880 assert_eq!(eig.S().dim(), 0);
2881 }
2882 #[test]
2885 fn empty_tridiagonal_with_vectors() {
2886 fn check<T: ComplexField>() {
2887 let empty = Diag::<T>::zeros(0);
2888 let mut s = Diag::<T>::zeros(0);
2889 let mut u = Mat::<T>::zeros(0, 0);
2890 let params = default();
2891 let mut mem = dyn_stack::MemBuffer::new(
2892 tridiagonal_self_adjoint_evd_scratch::<T>(
2893 0,
2894 ComputeEigenvectors::Yes,
2895 ParShape::Seq,
2896 params,
2897 ),
2898 );
2899 tridiagonal_self_adjoint_evd(
2900 empty.as_ref(),
2901 empty.as_ref(),
2902 s.as_mut(),
2903 Some(u.as_mut()),
2904 Par::Seq,
2905 MemStack::new(&mut mem),
2906 params,
2907 )
2908 .unwrap();
2909 }
2910 check::<f64>();
2911 check::<c64>();
2912 check::<c32>();
2913 }
2914}
2915#[cfg(test)]
2916mod tridiagonal_range_tests {
2917 use super::*;
2923 use crate::assert;
2924 use crate::diag::{DiagMut, DiagRef};
2925 use crate::dyn_stack::MemBuffer;
2926
2927 fn tridiagonal_evd(
2928 diag: &[f64],
2929 offdiag: &[f64],
2930 threshold: usize,
2931 ) -> Result<Vec<f64>, EvdError> {
2932 let n = diag.len();
2933 let mut s = vec![0.0; n];
2934 let mut u = Mat::<f64>::zeros(n, n);
2935 let mut params = Spec::<SelfAdjointEvdParams, f64>::default();
2936 params.recursion_threshold = threshold;
2937 let mut mem =
2938 MemBuffer::new(tridiagonal_self_adjoint_evd_scratch::<f64>(
2939 n,
2940 ComputeEigenvectors::Yes,
2941 crate::ParShape::Seq,
2942 params,
2943 ));
2944 tridiagonal_self_adjoint_evd(
2945 DiagRef::from_slice(diag),
2946 DiagRef::from_slice(offdiag),
2947 DiagMut::from_slice_mut(&mut s),
2948 Some(u.as_mut()),
2949 Par::Seq,
2950 MemStack::new(&mut mem),
2951 params,
2952 )?;
2953 Ok(s)
2954 }
2955
2956 #[test]
2959 fn two_by_two_at_the_range_limit() {
2960 let s = tridiagonal_evd(&[-1e308, 1e308], &[0.0], usize::MAX).unwrap();
2961 assert_eq!(s, [-1e308, 1e308]);
2962 let s =
2963 tridiagonal_evd(&[-1e308, 1e308], &[1e308], usize::MAX).unwrap();
2964 let sqrt2 = 2.0f64.sqrt();
2965 assert_eq!(s[0], -sqrt2 * 1e308);
2966 assert_eq!(s[1], sqrt2 * 1e308);
2967 }
2968
2969 #[test]
2972 fn subnormal_three_by_three() {
2973 let s = 1e-310f64;
2974 let sqrt2 = 2.0f64.sqrt();
2975 let got =
2976 tridiagonal_evd(&[2.0 * s; 3], &[-s, -s], usize::MAX).unwrap();
2977 for (got, exact) in
2978 got.iter()
2979 .zip([s * (2.0 - sqrt2), 2.0 * s, s * (2.0 + sqrt2)])
2980 {
2981 assert!(
2982 (got - exact).abs() <= 1e-13 * s,
2983 "got {got:e}, exact {exact:e}"
2984 );
2985 }
2986 }
2987
2988 #[test]
2991 fn spectrum_at_both_range_limits() {
2992 let n = 5;
2993 for scale in [1e308f64, 1e-310] {
2994 let diag = vec![scale; n];
2995 let offdiag = vec![0.3 * scale; n - 1];
2996 for threshold in [usize::MAX, 4] {
2998 let s = tridiagonal_evd(&diag, &offdiag, threshold).unwrap();
2999 for (k, x) in s.iter().enumerate() {
3000 let exact = scale
3001 * (1.0
3002 + 0.6
3003 * (core::f64::consts::PI * (n - k) as f64
3004 / (n + 1) as f64)
3005 .cos());
3006 assert!(x.is_finite());
3007 assert!(
3008 (x - exact).abs() <= 1e-13 * scale,
3009 "threshold {threshold}: got {x:e}, exact {exact:e}"
3010 );
3011 }
3012 }
3013 }
3014 }
3015}
3016
3017#[cfg(test)]
3018#[path = "tests/mod.rs"]
3019mod lapack;