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: Par,
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: Par,
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 let (mut real_diag, stack) =
211 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
212 let (mut real_offdiag, stack) =
213 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
214 let mut real_diag = real_diag
215 .as_mat_mut()
216 .col_mut(0)
217 .try_as_col_major_mut()
218 .unwrap();
219 let mut real_offdiag = real_offdiag
220 .as_mat_mut()
221 .col_mut(0)
222 .try_as_col_major_mut()
223 .unwrap();
224 for i in 0..n {
225 real_diag[i] = diag[i].real();
226 if i + 1 < n {
227 if const { T::IS_REAL } {
228 real_offdiag[i] = subdiag[i].real();
229 } else {
230 real_offdiag[i] = subdiag[i].abs();
231 }
232 } else {
233 real_offdiag[i] = zero();
234 }
235 }
236 let mut s = s;
237 let mut u = match u {
238 Some(u) => u,
239 None => {
240 tridiag_evd::qr_algorithm(
241 real_diag.rb_mut(),
242 real_offdiag.rb_mut(),
243 None,
244 )?;
245 for i in 0..n {
246 s[i] = real_diag[i].to_cplx();
247 }
248 return Ok(());
249 },
250 };
251 let (mut u_real, stack) = unsafe {
252 temp_mat_uninit::<T::Real, _, _>(
253 n,
254 if T::IS_REAL { 0 } else { n },
255 stack,
256 )
257 };
258 let mut u_real = u_real.as_mat_mut();
259 let mut u_evd = if const { T::IS_REAL } {
260 unsafe { core::mem::transmute(u.rb_mut()) }
261 } else {
262 u_real.rb_mut()
263 };
264 if n < params.recursion_threshold {
265 tridiag_evd::qr_algorithm(
266 real_diag.rb_mut(),
267 real_offdiag.rb_mut(),
268 Some(u_evd.rb_mut()),
269 )?;
270 } else {
271 tridiag_evd::divide_and_conquer::<T::Real>(
272 real_diag.rb_mut(),
273 real_offdiag.rb_mut(),
274 u_evd.rb_mut(),
275 par,
276 stack,
277 params.recursion_threshold,
278 )?;
279 }
280 if const { !T::IS_REAL } {
281 let normalized = |x: T| {
282 if x == zero() {
283 one()
284 } else {
285 x.mul_real(x.abs().recip())
286 }
287 };
288 let (mut scale, _) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
289 let mut scale = scale
290 .as_mat_mut()
291 .col_mut(0)
292 .try_as_col_major_mut()
293 .unwrap();
294 let mut x = one::<T>();
295 scale[0] = one();
296 for i in 1..n {
297 x = normalized(&subdiag[i - 1] * x);
298 scale[i] = x.copy();
299 }
300 for j in 0..n {
301 z!(u.rb_mut().col_mut(j), u_real.rb().col(j), scale.rb()).for_each(
302 |uz!(u, real, scale)| {
303 *u = scale.mul_real(real);
304 },
305 );
306 }
307 }
308 for i in 0..n {
309 s[i] = real_diag[i].to_cplx();
310 }
311 Ok(())
312}
313pub fn self_adjoint_evd<T: ComplexField>(
320 A: MatRef<'_, T>,
321 s: DiagMut<'_, T>,
322 u: Option<MatMut<'_, T>>,
323 par: Par,
324 stack: &mut MemStack,
325 params: Spec<SelfAdjointEvdParams, T>,
326) -> Result<(), EvdError> {
327 let n = A.nrows();
328 assert!(all(A.nrows() == A.ncols(), s.dim() == n));
329 if let Some(u) = u.rb() {
330 assert!(all(u.nrows() == n, u.ncols() == n));
331 }
332 let s = s.column_vector_mut();
333 if n == 0 {
334 return Ok(());
335 }
336 #[cfg(feature = "perf-warn")]
337 if let Some(matrix) = u.rb() {
338 if matrix.row_stride().unsigned_abs() != 1
339 && crate::__perf_warn!(QR_WARN)
340 {
341 if matrix.col_stride().unsigned_abs() == 1 {
342 log::warn!(
343 target : "faer_perf",
344 "EVD prefers column-major eigenvector matrix. Found row-major matrix."
345 );
346 } else {
347 log::warn!(
348 target : "faer_perf",
349 "EVD prefers column-major eigenvector matrix. Found matrix with generic strides."
350 );
351 }
352 }
353 }
354 let (mut trid, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
355 let mut trid = trid.as_mat_mut();
356 trid.copy_from_triangular_lower(A);
357 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(n, n);
358 let (mut householder, stack) =
359 unsafe { temp_mat_uninit::<T, _, _>(bs, n - 1, stack) };
360 let mut householder = householder.as_mat_mut();
361 {
362 tridiag::tridiag_in_place(
363 trid.rb_mut(),
364 householder.rb_mut(),
365 par,
366 stack,
367 params.tridiag.into(),
368 );
369 }
370 let trid = trid.rb();
371 let (mut diag, stack) =
372 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
373 let (mut offdiag, stack) =
374 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
375 let mut diag = diag.as_mat_mut().col_mut(0).try_as_col_major_mut().unwrap();
376 let mut offdiag = offdiag
377 .as_mat_mut()
378 .col_mut(0)
379 .try_as_col_major_mut()
380 .unwrap();
381 for i in 0..n {
382 diag[i] = trid[(i, i)].real();
383 if i + 1 < n {
384 if const { T::IS_REAL } {
385 offdiag[i] = trid[(i + 1, i)].real();
386 } else {
387 offdiag[i] = trid[(i + 1, i)].abs();
388 }
389 } else {
390 offdiag[i] = zero();
391 }
392 }
393 let mut s = s;
394 let mut u = match u {
395 Some(u) => u,
396 None => {
397 tridiag_evd::qr_algorithm(diag.rb_mut(), offdiag.rb_mut(), None)?;
398 for i in 0..n {
399 s[i] = diag[i].to_cplx();
400 }
401 return Ok(());
402 },
403 };
404 let (mut u_real, stack) = unsafe {
405 temp_mat_uninit::<T::Real, _, _>(
406 n,
407 if T::IS_REAL { 0 } else { n },
408 stack,
409 )
410 };
411 let mut u_real = u_real.as_mat_mut();
412 let mut u_evd = if const { T::IS_REAL } {
413 unsafe { core::mem::transmute(u.rb_mut()) }
414 } else {
415 u_real.rb_mut()
416 };
417 if n < params.recursion_threshold {
418 tridiag_evd::qr_algorithm(
419 diag.rb_mut(),
420 offdiag.rb_mut(),
421 Some(u_evd.rb_mut()),
422 )?;
423 } else {
424 tridiag_evd::divide_and_conquer::<T::Real>(
425 diag.rb_mut(),
426 offdiag.rb_mut(),
427 u_evd.rb_mut(),
428 par,
429 stack,
430 params.recursion_threshold,
431 )?;
432 }
433 if const { !T::IS_REAL } {
434 let normalized = |x: T| {
435 if x == zero() {
436 one()
437 } else {
438 x.mul_real(x.abs().recip())
439 }
440 };
441 let (mut scale, _) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
442 let mut scale = scale
443 .as_mat_mut()
444 .col_mut(0)
445 .try_as_col_major_mut()
446 .unwrap();
447 let mut x = one::<T>();
448 scale[0] = one();
449 for i in 1..n {
450 x = normalized(&trid[(i, i - 1)] * x);
451 scale[i] = x.copy();
452 }
453 for j in 0..n {
454 z!(u.rb_mut().col_mut(j), u_real.rb().col(j), scale.rb()).for_each(
455 |uz!(u, real, scale)| {
456 *u = scale.mul_real(real);
457 },
458 );
459 }
460 }
461 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
462 trid.submatrix(1, 0, n - 1, n - 1),
463 householder.rb(),
464 Conj::No,
465 u.rb_mut().subrows_mut(1, n - 1),
466 par,
467 stack,
468 );
469 for i in 0..n {
470 s[i] = diag[i].to_cplx();
471 }
472 Ok(())
473}
474pub fn pseudoinverse_from_self_adjoint_evd_scratch<T: ComplexField>(
477 dim: usize,
478 par: Par,
479) -> StackReq {
480 _ = par;
481 temp_mat_scratch::<T>(dim, dim).array(2)
482}
483#[track_caller]
486pub fn pseudoinverse_from_self_adjoint_evd<T: ComplexField>(
487 pinv: MatMut<'_, T>,
488 s: DiagRef<'_, T>,
489 u: MatRef<'_, T>,
490 par: Par,
491 stack: &mut MemStack,
492) {
493 pseudoinverse_from_self_adjoint_evd_with_tolerance(
494 pinv,
495 s,
496 u,
497 zero(),
498 eps::<T::Real>() * from_f64::<T::Real>(u.ncols() as f64),
499 par,
500 stack,
501 );
502}
503#[track_caller]
507pub fn pseudoinverse_from_self_adjoint_evd_with_tolerance<T: ComplexField>(
508 pinv: MatMut<'_, T>,
509 s: DiagRef<'_, T>,
510 u: MatRef<'_, T>,
511 abs_tol: T::Real,
512 rel_tol: T::Real,
513 par: Par,
514 stack: &mut MemStack,
515) {
516 let s = s.column_vector();
517 let mut pinv = pinv;
518 let n = u.ncols();
519 assert!(all(u.nrows() == n, u.ncols() == n, s.nrows() == n));
520 let smax = s.norm_max();
521 let tol = abs_tol.fmax(rel_tol * smax);
522 let (mut u_trunc, stack) =
523 unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
524 let (mut up_trunc, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
525 let mut u_trunc = u_trunc.as_mat_mut();
526 let mut up_trunc = up_trunc.as_mat_mut();
527 let mut len = 0;
528 for j in 0..n {
529 let x = s[j].absmax();
530 if x > tol {
531 let ref p = s[j].real().recip();
532 u_trunc.rb_mut().col_mut(len).copy_from(u.col(j));
533 z!(up_trunc.rb_mut().col_mut(len), u.col(j))
534 .for_each(|uz!(dst, src)| *dst = src.mul_real(p));
535 len += 1;
536 }
537 }
538 let u_trunc = u_trunc.get(.., ..len);
539 let up_trunc = up_trunc.get(.., ..len);
540 linalg::matmul::triangular::matmul(
541 pinv.rb_mut(),
542 BlockStructure::TriangularLower,
543 Accum::Replace,
544 up_trunc.rb(),
545 BlockStructure::Rectangular,
546 u_trunc.rb().adjoint(),
547 BlockStructure::Rectangular,
548 one(),
549 par,
550 );
551 for j in 0..n {
552 for i in 0..j {
553 pinv[(i, j)] = pinv[(j, i)].conj();
554 }
555 }
556}
557fn dot2x1<T: RealField>(
558 lhs0: RowRef<'_, T>,
559 lhs1: RowRef<'_, T>,
560 rhs: ColRef<'_, T>,
561) -> (T, T) {
562 let n = rhs.nrows();
563 assert!(all(lhs0.ncols() == n, lhs1.ncols() == n));
564 let mut acc00 = zero::<T>();
565 let mut acc01 = zero::<T>();
566 let mut acc10 = zero::<T>();
567 let mut acc11 = zero::<T>();
568 let n2 = n / 2 * 2;
569 let mut i = 0;
570 while i < n2 {
571 acc00 += &lhs0[i] * &rhs[i];
572 acc10 += &lhs1[i] * &rhs[i];
573 acc01 += &lhs0[i + 1] * &rhs[i + 1];
574 acc11 += &lhs1[i + 1] * &rhs[i + 1];
575 i += 2;
576 }
577 while i < n {
578 acc00 += &lhs0[i] * &rhs[i];
579 acc10 += &lhs1[i] * &rhs[i];
580 i += 1;
581 }
582 (acc00 + acc01, acc10 + acc11)
583}
584fn dot2x2<T: RealField>(
585 lhs0: RowRef<'_, T>,
586 lhs1: RowRef<'_, T>,
587 rhs0: ColRef<'_, T>,
588 rhs1: ColRef<'_, T>,
589) -> (T, T, T, T) {
590 let n = rhs0.nrows();
591 assert!(all(lhs0.ncols() == n, lhs1.ncols() == n));
592 let mut acc0 = zero::<T>();
593 let mut acc1 = zero::<T>();
594 let mut acc2 = zero::<T>();
595 let mut acc3 = zero::<T>();
596 let mut i = 0;
597 while i < n {
598 acc0 += &lhs0[i] * &rhs0[i];
599 acc1 += &lhs1[i] * &rhs0[i];
600 acc2 += &lhs0[i] * &rhs1[i];
601 acc3 += &lhs1[i] * &rhs1[i];
602 i += 1;
603 }
604 (acc0, acc1, acc2, acc3)
605}
606fn normalize_eigenvectors<T: ComplexField>(
611 mut u: MatMut<'_, T>,
612 h: MatRef<'_, T>,
613) {
614 let n = u.nrows();
615 let mut j = 0;
616 while j < n {
617 let paired = T::IS_REAL && j + 1 < n && h[(j + 1, j)] != zero::<T>();
618 let nrm = if paired {
619 u.rb().col(j).norm_l2().hypot(&u.rb().col(j + 1).norm_l2())
620 } else {
621 u.rb().col(j).norm_l2()
622 };
623 if nrm != zero::<T::Real>() {
624 let inv: T = nrm.recip().to_cplx();
625 for i in 0..n {
626 u[(i, j)] *= inv.copy();
627 if paired {
628 u[(i, j + 1)] *= inv.copy();
629 }
630 }
631 }
632 j += if paired { 2 } else { 1 };
633 }
634}
635fn eigenvectors_ok<T: ComplexField>(u: MatRef<'_, T>) -> bool {
638 u.is_all_finite()
639 && (0..u.ncols()).all(|j| u.col(j).norm_l2() > zero::<T::Real>())
640}
641pub(crate) fn evd_from_real_schur_imp<T: RealField>(
642 A: MatRef<'_, T>,
643 V: MatMut<'_, T>,
644 par: Par,
645 params: EvdFromSchurParams,
646) {
647 let one = one::<T>;
648 let zero = zero::<T>;
649 let mut V = V;
650 let n = A.nrows();
651 let mut norm = zero();
652 for j in 0..n {
653 for i in 0..Ord::min(j + 2, n) {
654 norm += A[(i, j)].abs1();
655 }
656 }
657 let mut k = n;
658 loop {
659 if k == 0 {
660 break;
661 }
662 k -= 1;
663 if k == 0 || A[(k, k - 1)] == zero() {
664 let ref p = A[(k, k)].copy();
665 V[(k, k)] = one();
666 for i in 0..k {
667 V[(i, k)] = -&A[(i, k)];
668 }
669 let scale = solve_real_shifted_upper_quasi_triangular_system(
670 A.submatrix(0, 0, k, k),
671 p.copy(),
672 V.rb_mut().subrows_mut(0, k).col_mut(k),
673 norm.copy(),
674 par,
675 params,
676 );
677 V[(k, k)] *= scale;
681 } else {
682 let ref p = A[(k, k)].copy();
683 let ref q = A[(k, k - 1)].abs().sqrt() * A[(k - 1, k)].abs().sqrt();
684 if A[(k - 1, k)].abs() >= A[(k, k - 1)].abs() {
685 V[(k - 1, k - 1)] = one();
686 V[(k, k)] = q / &A[(k - 1, k)];
687 } else {
688 V[(k - 1, k - 1)] = -q / &A[(k, k - 1)];
689 V[(k, k)] = one();
690 }
691 V[(k - 1, k)] = zero();
692 V[(k, k - 1)] = zero();
693 for i in 0..k - 1 {
694 V[(i, k - 1)] = -&V[(k - 1, k - 1)] * &A[(i, k - 1)];
695 V[(i, k)] = -&V[(k, k)] * &A[(i, k)];
696 }
697 let scale = solve_cplx_shifted_upper_quasi_triangular_system(
698 A.submatrix(0, 0, k - 1, k - 1),
699 p.copy(),
700 q.copy(),
701 V.rb_mut().submatrix_mut(0, k - 1, k - 1, 2),
702 norm.copy(),
703 par,
704 params,
705 );
706 V[(k - 1, k - 1)] *= scale.copy();
707 V[(k, k)] *= scale;
708 k -= 1;
709 }
710 }
711}
712pub(crate) fn evd_from_cplx_schur_imp<T: ComplexField>(
713 A: MatRef<'_, T>,
714 conj_A: Conj,
715 V: MatMut<'_, T>,
716 par: Par,
717 params: EvdFromSchurParams,
718) {
719 let one = one::<T>;
720 let mut V = V;
721 let n = A.nrows();
722 let mut norm = zero::<T::Real>();
723 for j in 0..n {
724 for i in 0..j + 1 {
725 norm += A[(i, j)].abs1();
726 }
727 }
728 let mut k = n;
729 loop {
730 if k == 0 {
731 break;
732 }
733 k -= 1;
734 let ref p = if conj_A == Conj::Yes {
735 A[(k, k)].conj()
736 } else {
737 A[(k, k)].copy()
738 };
739 V[(k, k)] = one();
740 if conj_A == Conj::Yes {
741 for i in 0..k {
742 V[(i, k)] = -A[(i, k)].conj();
743 }
744 } else {
745 for i in 0..k {
746 V[(i, k)] = -&A[(i, k)];
747 }
748 }
749 let scale = solve_shifted_upper_triangular_system(
750 A.submatrix(0, 0, k, k),
751 conj_A,
752 p.copy(),
753 V.rb_mut().subrows_mut(0, k).col_mut(k),
754 norm.copy(),
755 par,
756 params,
757 );
758 let scale: T = scale.to_cplx();
759 V[(k, k)] *= scale;
760 }
761}
762fn solve_real_shifted_upper_quasi_triangular_system<T: RealField>(
763 A: MatRef<'_, T>,
764 p: T,
765 x: ColMut<'_, T>,
766 norm: T,
767 par: Par,
768 params: EvdFromSchurParams,
769) -> T {
770 let n = A.nrows();
771 let one = one::<T>;
772 let zero = zero::<T>;
773 let ref eps = eps::<T>();
774 let ref p = p;
775 let mut x = x;
776 let ref norm = norm;
777 let big = sqrt_max_positive::<T>();
783 let mut scale = one();
784 if par.degree() == 0 || n < params.recursion_threshold {
785 let mut i = n;
786 loop {
787 if i == 0 {
788 break;
789 }
790 i -= 1;
791 if i == 0 || A[(i, i - 1)] == zero() {
792 let dot = linalg::matmul::dot::inner_prod(
793 A.row(i).subcols(i + 1, n - i - 1),
794 Conj::No,
795 x.rb().subrows(i + 1, n - i - 1),
796 Conj::No,
797 );
798 x[i] -= dot;
799 let mut z = &A[(i, i)] - p;
800 if z.abs() < eps * norm {
801 z = eps * norm;
802 }
803 if x[i] != zero() {
804 x[i] /= z;
805 }
806 let m = x[i].abs();
807 if m > big {
808 let r = m.recip();
809 for j in 0..x.nrows() {
810 x[j] *= r.copy();
811 }
812 scale *= r;
813 }
814 } else {
815 let (dot0, dot1) = dot2x1(
816 A.row(i - 1).subcols(i + 1, n - i - 1),
817 A.row(i).subcols(i + 1, n - i - 1),
818 x.rb().subrows(i + 1, n - i - 1),
819 );
820 x[i - 1] -= dot0;
821 x[i] -= dot1;
822 let ref a = &A[(i, i)] - p;
823 let ref b = A[(i - 1, i)];
824 let ref c = A[(i, i - 1)];
825 let ref r0 = x[i - 1];
826 let ref r1 = x[i];
827 let ref inv_det = (a.abs2() - b * c).recip();
828 (x[i - 1], x[i]) =
829 ((a * r0 - b * r1) * inv_det, (a * r1 - c * r0) * inv_det);
830 let a0 = x[i - 1].abs();
831 let a1 = x[i].abs();
832 let m = if a0 > a1 { a0 } else { a1 };
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 i -= 1;
841 }
842 }
843 scale
844 } else {
845 let mut mid = n / 2;
846 if A[(mid, mid - 1)] != zero() {
847 mid -= 1;
848 }
849 let (A00, A01, _, A11) = A.split_at(mid, mid);
850 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
851 let f1 = solve_real_shifted_upper_quasi_triangular_system(
852 A11,
853 p.copy(),
854 x1.rb_mut(),
855 norm.copy(),
856 par,
857 params,
858 );
859 for j in 0..x0.nrows() {
863 x0[j] *= f1.copy();
864 }
865 linalg::matmul::matmul(
866 x0.rb_mut().as_mat_mut(),
867 Accum::Add,
868 A01,
869 x1.rb().as_mat(),
870 -one(),
871 par,
872 );
873 let f0 = solve_real_shifted_upper_quasi_triangular_system(
874 A00,
875 p.copy(),
876 x0.rb_mut(),
877 norm.copy(),
878 par,
879 params,
880 );
881 for j in 0..x1.nrows() {
882 x1[j] *= f0.copy();
883 }
884 f0 * f1
885 }
886}
887fn solve_cplx_shifted_upper_quasi_triangular_system<T: RealField>(
888 A: MatRef<'_, T>,
889 p: T,
890 q: T,
891 x: MatMut<'_, T>,
892 norm: T,
893 par: Par,
894 params: EvdFromSchurParams,
895) -> T {
896 let n = A.nrows();
897 let one = one::<T>;
898 let zero = zero::<T>;
899 let ref eps = eps::<T>();
900 let ref norm = norm;
901 let ref p = p;
902 let ref q = q;
903 let mut x = x;
904 let big = sqrt_max_positive::<T>();
905 let mut scale = one();
906 if par.degree() == 0 || n < params.recursion_threshold {
907 let mut i = n;
908 loop {
909 if i == 0 {
910 break;
911 }
912 i -= 1;
913 if i == 0 || A[(i, i - 1)] == zero() {
914 let (re, im) = dot2x1(
915 x.rb().subrows(i + 1, n - i - 1).col(0).transpose(),
916 x.rb().subrows(i + 1, n - i - 1).col(1).transpose(),
917 A.row(i).subcols(i + 1, n - i - 1).transpose(),
918 );
919 x[(i, 0)] = &x[(i, 0)] - re;
920 x[(i, 1)] = &x[(i, 1)] - im;
921 let mut z_re = &A[(i, i)] - p;
922 let mut z_im = -q;
923 let mut z = z_re.hypot(&z_im);
924 if z < eps * norm {
925 z_re = eps * norm;
926 z_im = zero();
927 z = z_re.copy();
928 }
929 let ref z = z;
930 let ref z_re = (z_re / z) / z;
931 let ref z_im = (-z_im / z) / z;
932 let ref x_re = x[(i, 0)].copy();
933 let ref x_im = x[(i, 1)].copy();
934 x[(i, 0)] = x_re * z_re - x_im * z_im;
935 x[(i, 1)] = x_re * z_im + x_im * z_re;
936 let a0 = x[(i, 0)].abs();
937 let a1 = x[(i, 1)].abs();
938 let m = if a0 > a1 { a0 } else { a1 };
939 if m > big {
940 let r = m.recip();
941 for j in 0..x.nrows() {
942 x[(j, 0)] *= r.copy();
943 x[(j, 1)] *= r.copy();
944 }
945 scale *= r;
946 }
947 } else {
948 let (re0, re1, im0, im1) = dot2x2(
949 A.row(i - 1).subcols(i + 1, n - i - 1),
950 A.row(i).subcols(i + 1, n - i - 1),
951 x.rb().col(0).subrows(i + 1, n - i - 1),
952 x.rb().col(1).subrows(i + 1, n - i - 1),
953 );
954 x[(i - 1, 0)] = &x[(i - 1, 0)] - re0;
955 x[(i - 1, 1)] = &x[(i - 1, 1)] - im0;
956 x[(i, 0)] = &x[(i, 0)] - re1;
957 x[(i, 1)] = &x[(i, 1)] - im1;
958 let ref a_re = &A[(i, i)] - p;
959 let ref a_im = -q;
960 let ref b = A[(i - 1, i)];
961 let ref c = A[(i, i - 1)];
962 let ref r0_re = x[(i - 1, 0)];
963 let ref r0_im = x[(i - 1, 1)];
964 let ref r1_re = x[(i, 0)];
965 let ref r1_im = x[(i, 1)];
966 let mut z_re = a_re.abs2() - a_im.abs2() - b * c;
967 let mut z_im = (a_re * a_im).mul_pow2(from_f64::<T>(2.0));
968 let mut z = z_re.hypot(&z_im);
969 if z < eps * norm {
970 z_re = eps * norm;
971 z_im = zero();
972 z = z_re.copy();
973 }
974 let ref z = z;
975 let ref z_re = (z_re / z) / z;
976 let ref z_im = (-z_im / z) / z;
977 let ref x0_re = (a_re * r0_re - a_im * r0_im) - b * r1_re;
978 let ref x0_im = (a_re * r0_im + a_im * r0_re) - b * r1_im;
979 let ref x1_re = (a_re * r1_re - a_im * r1_im) - c * r0_re;
980 let ref x1_im = (a_re * r1_im + a_im * r1_re) - c * r0_im;
981 x[(i - 1, 0)] = x0_re * z_re - x0_im * z_im;
982 x[(i - 1, 1)] = x0_re * z_im + x0_im * z_re;
983 x[(i, 0)] = x1_re * z_re - x1_im * z_im;
984 x[(i, 1)] = x1_re * z_im + x1_im * z_re;
985 let mut m = x[(i - 1, 0)].abs();
986 let v = x[(i - 1, 1)].abs();
987 if v > m {
988 m = v;
989 }
990 let v = x[(i, 0)].abs();
991 if v > m {
992 m = v;
993 }
994 let v = x[(i, 1)].abs();
995 if v > m {
996 m = v;
997 }
998 if m > big {
999 let r = m.recip();
1000 for j in 0..x.nrows() {
1001 x[(j, 0)] *= r.copy();
1002 x[(j, 1)] *= r.copy();
1003 }
1004 scale *= r;
1005 }
1006 i -= 1;
1007 }
1008 }
1009 scale
1010 } else {
1011 let mut mid = n / 2;
1012 if A[(mid, mid - 1)] != zero() {
1013 mid -= 1;
1014 }
1015 let (A00, A01, _, A11) = A.split_at(mid, mid);
1016 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
1017 let f1 = solve_cplx_shifted_upper_quasi_triangular_system(
1018 A11,
1019 p.copy(),
1020 q.copy(),
1021 x1.rb_mut(),
1022 norm.copy(),
1023 par,
1024 params,
1025 );
1026 for j in 0..x0.nrows() {
1027 x0[(j, 0)] *= f1.copy();
1028 x0[(j, 1)] *= f1.copy();
1029 }
1030 linalg::matmul::matmul(
1031 x0.rb_mut(),
1032 Accum::Add,
1033 A01,
1034 x1.rb(),
1035 -one(),
1036 par,
1037 );
1038 let f0 = solve_cplx_shifted_upper_quasi_triangular_system(
1039 A00,
1040 p.copy(),
1041 q.copy(),
1042 x0.rb_mut(),
1043 norm.copy(),
1044 par,
1045 params,
1046 );
1047 for j in 0..x1.nrows() {
1048 x1[(j, 0)] *= f0.copy();
1049 x1[(j, 1)] *= f0.copy();
1050 }
1051 f0 * f1
1052 }
1053}
1054fn solve_shifted_upper_triangular_system<T: ComplexField>(
1055 A: MatRef<'_, T>,
1056 conj_A: Conj,
1057 p: T,
1058 x: ColMut<'_, T>,
1059 norm: T::Real,
1060 par: Par,
1061 params: EvdFromSchurParams,
1062) -> T::Real {
1063 let n = A.nrows();
1064 let mut scale = one::<T::Real>();
1065 let one = one::<T>;
1066 let zero = zero::<T>;
1067 let ref eps = eps::<T::Real>();
1068 let ref norm = norm;
1069 let ref p = p;
1070 let mut x = x;
1071 let big = sqrt_max_positive::<T::Real>();
1072 if par.degree() == 0 || n < params.recursion_threshold {
1073 let mut i = n;
1074 loop {
1075 if i == 0 {
1076 break;
1077 }
1078 i -= 1;
1079 let dot = linalg::matmul::dot::inner_prod(
1080 A.row(i).subcols(i + 1, n - i - 1),
1081 conj_A,
1082 x.rb().subrows(i + 1, n - i - 1),
1083 Conj::No,
1084 );
1085 x[i] -= dot;
1086 let mut z = if conj_A == Conj::Yes {
1087 A[(i, i)].conj()
1088 } else {
1089 A[(i, i)].copy()
1090 } - p;
1091 if z.abs() < eps * norm {
1092 z = (eps * norm).to_cplx();
1093 }
1094 if x[i] != zero() {
1095 x[i] *= z.recip();
1096 }
1097 let m = x[i].abs();
1098 if m > big {
1099 let r = m.recip();
1100 let rc: T = r.to_cplx();
1101 for j in 0..x.nrows() {
1102 x[j] *= rc.copy();
1103 }
1104 scale *= r;
1105 }
1106 }
1107 scale
1108 } else {
1109 let mid = n / 2;
1110 let (A00, A01, _, A11) = A.split_at(mid, mid);
1111 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
1112 let f1 = solve_shifted_upper_triangular_system(
1113 A11,
1114 conj_A,
1115 p.copy(),
1116 x1.rb_mut(),
1117 norm.copy(),
1118 par,
1119 params,
1120 );
1121 let f1c: T = f1.to_cplx();
1122 for j in 0..x0.nrows() {
1123 x0[j] *= f1c.copy();
1124 }
1125 linalg::matmul::matmul_with_conj(
1126 x0.rb_mut().as_mat_mut(),
1127 Accum::Add,
1128 A01,
1129 conj_A,
1130 x1.rb().as_mat(),
1131 Conj::No,
1132 -one(),
1133 par,
1134 );
1135 let f0 = solve_shifted_upper_triangular_system(
1136 A00,
1137 conj_A,
1138 p.copy(),
1139 x0.rb_mut(),
1140 norm.copy(),
1141 par,
1142 params,
1143 );
1144 let f0c: T = f0.to_cplx();
1145 for j in 0..x1.nrows() {
1146 x1[j] *= f0c.copy();
1147 }
1148 f0 * f1
1149 }
1150}
1151pub fn evd_scratch<T: ComplexField>(
1154 dim: usize,
1155 eigen_left: ComputeEigenvectors,
1156 eigen_right: ComputeEigenvectors,
1157 par: Par,
1158 params: Spec<EvdParams, T>,
1159) -> StackReq {
1160 let n = dim;
1161 if n == 0 {
1162 return StackReq::EMPTY;
1163 }
1164 let compute_eigen = eigen_left == ComputeEigenvectors::Yes
1165 || eigen_right == ComputeEigenvectors::Yes;
1166 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(
1167 n - 1,
1168 n - 1,
1169 );
1170 let H = temp_mat_scratch::<T>(n, n);
1171 let X = H;
1172 let Z = temp_mat_scratch::<T>(n, if compute_eigen { n } else { 0 });
1173 let householder = temp_mat_scratch::<T>(bs, n);
1174 let apply = linalg::householder::apply_block_householder_sequence_on_the_right_in_place_scratch::<T>(n - 1, bs, n - 1);
1175 StackReq::all_of(&[
1176 H,
1177 Z,
1178 StackReq::any_of(&[
1179 householder.and(
1180 hessenberg::hessenberg_in_place_scratch::<T>(
1181 n,
1182 bs,
1183 par,
1184 params.hessenberg.into(),
1185 )
1186 .or(apply),
1187 ),
1188 schur::multishift_qr_scratch::<T>(
1189 n,
1190 n,
1191 compute_eigen,
1192 compute_eigen,
1193 par,
1194 params.schur,
1195 ),
1196 X,
1197 ]),
1198 ])
1199}
1200pub fn hessenberg_schur_scratch<T: ComplexField>(
1212 dim: usize,
1213 compute_z: bool,
1214 par: Par,
1215 params: Spec<SchurParams, T>,
1216) -> StackReq {
1217 schur::multishift_qr_scratch::<T>(dim, dim, true, compute_z, par, *params)
1218}
1219pub fn hessenberg_schur_real<T: RealField>(
1261 mut h: MatMut<'_, T>,
1262 z: Option<MatMut<'_, T>>,
1263 mut wr: ColMut<'_, T>,
1264 mut wi: ColMut<'_, T>,
1265 par: Par,
1266 stack: &mut MemStack,
1267 params: Spec<SchurParams, T>,
1268) -> Result<(), EvdError> {
1269 let n = h.nrows();
1270 assert!(h.ncols() == n && wr.nrows() == n && wi.nrows() == n);
1271 if let Some(z) = z.rb() {
1272 assert!(z.nrows() == n && z.ncols() == n);
1273 }
1274 if !h.is_all_finite() {
1275 return Err(EvdError::NoConvergence);
1276 }
1277 if n <= 1 {
1278 if n == 1 {
1279 wr[0] = h[(0, 0)].copy();
1280 wi[0] = zero();
1281 }
1282 return Ok(());
1283 }
1284 let (info, _, _) = schur::real_schur::multishift_qr(
1285 true,
1286 h.rb_mut(),
1287 z,
1288 wr,
1289 wi,
1290 0,
1291 n,
1292 par,
1293 stack,
1294 *params,
1295 );
1296 if info != 0 {
1297 return Err(EvdError::NoConvergence);
1298 }
1299 for j in 0..n {
1301 for i in j + 2..n {
1302 h[(i, j)] = zero();
1303 }
1304 }
1305 Ok(())
1306}
1307pub fn hessenberg_schur_cplx<T: ComplexField>(
1344 mut h: MatMut<'_, T>,
1345 z: Option<MatMut<'_, T>>,
1346 mut w: ColMut<'_, T>,
1347 par: Par,
1348 stack: &mut MemStack,
1349 params: Spec<SchurParams, T>,
1350) -> Result<(), EvdError> {
1351 assert!(!T::IS_REAL);
1352 let n = h.nrows();
1353 assert!(h.ncols() == n && w.nrows() == n);
1354 if let Some(z) = z.rb() {
1355 assert!(z.nrows() == n && z.ncols() == n);
1356 }
1357 if !h.is_all_finite() {
1358 return Err(EvdError::NoConvergence);
1359 }
1360 if n <= 1 {
1361 if n == 1 {
1362 w[0] = h[(0, 0)].copy();
1363 }
1364 return Ok(());
1365 }
1366 let (info, _, _) = schur::complex_schur::multishift_qr(
1367 true,
1368 h.rb_mut(),
1369 z,
1370 w,
1371 0,
1372 n,
1373 par,
1374 stack,
1375 *params,
1376 );
1377 if info != 0 {
1378 return Err(EvdError::NoConvergence);
1379 }
1380 for j in 0..n {
1382 for i in j + 1..n {
1383 h[(i, j)] = zero();
1384 }
1385 }
1386 Ok(())
1387}
1388#[derive(Copy, Clone, Debug, PartialEq, Eq)]
1398pub struct SchurReorderError {
1399 pub position: usize,
1401}
1402pub fn reorder_schur_scratch<T: ComplexField>(dim: usize) -> StackReq {
1412 StackReq::all_of(&[
1413 temp_mat_scratch::<T>(4, dim),
1414 temp_mat_scratch::<T>(dim, 4),
1415 temp_mat_scratch::<T>(dim, 4),
1416 ])
1417}
1418pub fn reorder_schur_real<T: RealField>(
1461 mut t: MatMut<'_, T>,
1462 mut q: Option<MatMut<'_, T>>,
1463 mut ifst: usize,
1464 mut ilst: usize,
1465 _par: Par,
1466 stack: &mut MemStack,
1467) -> Result<usize, SchurReorderError> {
1468 let n = t.nrows();
1469 assert!(t.ncols() == n && ifst < n && ilst < n);
1470 if let Some(q) = q.rb() {
1471 assert!(q.nrows() == n && q.ncols() == n);
1472 }
1473 let (mut saved_rows, stack) = temp_mat_zeroed::<T, _, _>(4, n, stack);
1474 let (mut saved_cols, stack) = temp_mat_zeroed::<T, _, _>(n, 4, stack);
1475 let (mut saved_q, _) = temp_mat_zeroed::<T, _, _>(n, 4, stack);
1476 let mut saved_rows = saved_rows.as_mat_mut();
1477 let mut saved_cols = saved_cols.as_mat_mut();
1478 let mut saved_q = saved_q.as_mat_mut();
1479 if ifst > 0 && t[(ifst, ifst - 1)] != zero() {
1480 ifst -= 1;
1481 }
1482 if ilst > 0 && t[(ilst, ilst - 1)] != zero() {
1483 ilst -= 1;
1484 }
1485 let block_size = |t: MatRef<'_, T>, i: usize| {
1486 if i + 1 < n && t[(i + 1, i)] != zero() {
1487 2
1488 } else {
1489 1
1490 }
1491 };
1492 let moving = block_size(t.rb(), ifst);
1493 if ifst < ilst {
1494 let dest = block_size(t.rb(), ilst);
1495 ilst = ilst + dest - moving;
1496 }
1497 let mut here = ifst;
1498 while here != ilst {
1499 let down = here < ilst;
1500 let next = if down {
1501 block_size(t.rb(), here + moving)
1502 } else if here > 1 && t[(here - 1, here - 2)] != zero() {
1503 2
1504 } else {
1505 1
1506 };
1507 let start = if down { here } else { here - next };
1508 let (n1, n2) = if down { (moving, next) } else { (next, moving) };
1509 let k = n1 + n2;
1514 let w = n - start;
1515 let h = start + k;
1516 saved_rows
1517 .rb_mut()
1518 .submatrix_mut(0, 0, k, w)
1519 .copy_from(t.rb().submatrix(start, start, k, w));
1520 saved_cols
1521 .rb_mut()
1522 .submatrix_mut(0, 0, h, k)
1523 .copy_from(t.rb().submatrix(0, start, h, k));
1524 if let Some(q) = q.rb() {
1525 saved_q
1526 .rb_mut()
1527 .submatrix_mut(0, 0, n, k)
1528 .copy_from(q.submatrix(0, start, n, k));
1529 }
1530 let status = schur::real_schur::schur_swap_checked(
1531 t.rb_mut(),
1532 q.rb_mut(),
1533 start,
1534 n1,
1535 n2,
1536 true,
1537 );
1538 let finite = t.rb().submatrix(start, start, k, w).is_all_finite()
1539 && t.rb().submatrix(0, start, h, k).is_all_finite()
1540 && q.rb()
1541 .is_none_or(|q| q.submatrix(0, start, n, k).is_all_finite());
1542 if status != 0 || !finite {
1543 t.rb_mut()
1544 .submatrix_mut(0, start, h, k)
1545 .copy_from(saved_cols.rb().submatrix(0, 0, h, k));
1546 t.rb_mut()
1547 .submatrix_mut(start, start, k, w)
1548 .copy_from(saved_rows.rb().submatrix(0, 0, k, w));
1549 if let Some(q) = q.rb_mut() {
1550 q.submatrix_mut(0, start, n, k)
1551 .copy_from(saved_q.rb().submatrix(0, 0, n, k));
1552 }
1553 return Err(SchurReorderError { position: here });
1554 }
1555 here = if down { here + next } else { here - next };
1556 }
1557 Ok(here)
1558}
1559pub fn reorder_schur_cplx<T: ComplexField>(
1586 mut t: MatMut<'_, T>,
1587 mut q: Option<MatMut<'_, T>>,
1588 ifst: usize,
1589 ilst: usize,
1590 _par: Par,
1591 _stack: &mut MemStack,
1592) -> usize {
1593 let n = t.nrows();
1594 assert!(t.ncols() == n && ifst < n && ilst < n);
1595 if let Some(q) = q.rb() {
1596 assert!(q.nrows() == n && q.ncols() == n);
1597 }
1598 let mut here = ifst;
1599 while here != ilst {
1600 let start = if here < ilst { here } else { here - 1 };
1601 schur::complex_schur::schur_swap(t.rb_mut(), q.rb_mut(), start);
1602 here = if here < ilst { here + 1 } else { here - 1 };
1603 }
1604 here
1605}
1606pub fn eigenvectors_from_schur_scratch<T: ComplexField>(
1615 dim: usize,
1616) -> StackReq {
1617 temp_mat_scratch::<T>(dim, dim)
1618}
1619pub fn eigenvectors_from_schur_real<T: RealField>(
1652 t: MatRef<'_, T>,
1653 q: Option<MatRef<'_, T>>,
1654 mut x: MatMut<'_, T>,
1655 par: Par,
1656 stack: &mut MemStack,
1657 params: Spec<EvdFromSchurParams, T>,
1658) -> Result<(), EvdError> {
1659 let n = t.nrows();
1660 assert!(t.ncols() == n && x.nrows() == n && x.ncols() == n);
1661 if let Some(q) = q {
1662 assert!(q.nrows() == n && q.ncols() == n);
1663 }
1664 if !t.is_all_finite() {
1665 return Err(EvdError::NoConvergence);
1666 }
1667 let (mut v, _) = temp_mat_zeroed::<T, _, _>(n, n, stack);
1668 let mut v = v.as_mat_mut();
1669 evd_from_real_schur_imp(t, v.rb_mut(), par, *params);
1670 if let Some(q) = q {
1671 linalg::matmul::matmul(
1672 x.rb_mut(),
1673 Accum::Replace,
1674 q,
1675 v.rb(),
1676 one(),
1677 par,
1678 );
1679 } else {
1680 x.copy_from(v.rb());
1681 }
1682 normalize_eigenvectors(x.rb_mut(), t);
1683 if eigenvectors_ok(x.rb()) {
1684 Ok(())
1685 } else {
1686 Err(EvdError::NoConvergence)
1687 }
1688}
1689pub fn eigenvectors_from_schur_cplx<T: ComplexField>(
1721 t: MatRef<'_, T>,
1722 q: Option<MatRef<'_, T>>,
1723 mut x: MatMut<'_, T>,
1724 par: Par,
1725 stack: &mut MemStack,
1726 params: Spec<EvdFromSchurParams, T>,
1727) -> Result<(), EvdError> {
1728 let n = t.nrows();
1729 assert!(t.ncols() == n && x.nrows() == n && x.ncols() == n);
1730 if let Some(q) = q {
1731 assert!(q.nrows() == n && q.ncols() == n);
1732 }
1733 if !t.is_all_finite() {
1734 return Err(EvdError::NoConvergence);
1735 }
1736 let (mut v, _) = temp_mat_zeroed::<T, _, _>(n, n, stack);
1737 let mut v = v.as_mat_mut();
1738 evd_from_cplx_schur_imp(t, Conj::No, v.rb_mut(), par, *params);
1739 if let Some(q) = q {
1740 linalg::matmul::matmul(
1741 x.rb_mut(),
1742 Accum::Replace,
1743 q,
1744 v.rb(),
1745 one(),
1746 par,
1747 );
1748 } else {
1749 x.copy_from(v.rb());
1750 }
1751 normalize_eigenvectors(x.rb_mut(), t);
1752 if eigenvectors_ok(x.rb()) {
1753 Ok(())
1754 } else {
1755 Err(EvdError::NoConvergence)
1756 }
1757}
1758fn evd_imp<T: ComplexField>(
1759 A: MatRef<'_, T>,
1760 s: ColMut<'_, T>,
1761 s_im: Option<ColMut<'_, T>>,
1762 u_left: Option<MatMut<'_, T>>,
1763 u_right: Option<MatMut<'_, T>>,
1764 par: Par,
1765 stack: &mut MemStack,
1766 params: EvdParams,
1767) -> Result<(), EvdError> {
1768 let n = A.nrows();
1769 if n == 0 {
1770 return Ok(());
1771 }
1772 for j in 0..n {
1773 for i in 0..n {
1774 if !A[(i, j)].is_finite() {
1775 return Err(EvdError::NoConvergence);
1776 }
1777 }
1778 }
1779 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(
1780 n - 1,
1781 n - 1,
1782 );
1783 let mut s = s;
1784 let mut s_im = s_im;
1785 let (mut H, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1786 let (mut Z, stack) = unsafe {
1787 temp_mat_uninit::<T, _, _>(
1788 n,
1789 if u_left.is_some() || u_right.is_some() {
1790 n
1791 } else {
1792 0
1793 },
1794 stack,
1795 )
1796 };
1797 let mut H = H.as_mat_mut();
1798 let mut Z = if u_left.is_some() || u_right.is_some() {
1799 Some(Z.as_mat_mut())
1800 } else {
1801 None
1802 };
1803 H.copy_from(A);
1804 {
1805 let (mut householder, stack) =
1806 unsafe { temp_mat_uninit::<T, _, _>(bs, n - 1, stack) };
1807 let mut householder = householder.as_mat_mut();
1808 hessenberg::hessenberg_in_place(
1809 H.rb_mut(),
1810 householder.rb_mut(),
1811 par,
1812 stack,
1813 params.hessenberg.into(),
1814 );
1815 if let Some(mut Z) = Z.rb_mut() {
1816 Z.fill(zero());
1817 Z.rb_mut().diagonal_mut().fill(one());
1818 linalg::householder::apply_block_householder_sequence_on_the_right_in_place_with_conj(
1819 H.rb().submatrix(1, 0, n - 1, n - 1),
1820 householder.rb(),
1821 Conj::No,
1822 Z.rb_mut().submatrix_mut(1, 1, n - 1, n - 1),
1823 par,
1824 stack,
1825 );
1826 }
1827 for j in 0..n {
1828 for i in j + 2..n {
1829 H[(i, j)] = zero();
1830 }
1831 }
1832 }
1833 if const { T::IS_REAL } {
1834 let (info, _, _) = schur::real_schur::multishift_qr::<T::Real>(
1835 unsafe { core::mem::transmute(Z.is_some()) },
1836 unsafe { core::mem::transmute(H.rb_mut()) },
1837 unsafe { core::mem::transmute(Z.rb_mut()) },
1838 unsafe { core::mem::transmute(s.rb_mut()) },
1839 unsafe { core::mem::transmute(s_im.rb_mut().unwrap()) },
1840 0,
1841 n,
1842 par,
1843 stack,
1844 params.schur,
1845 );
1846 if info != 0 {
1847 return Err(EvdError::NoConvergence);
1848 }
1849 } else {
1850 let (info, _, _) = schur::complex_schur::multishift_qr::<T>(
1851 Z.is_some(),
1852 H.rb_mut(),
1853 Z.rb_mut(),
1854 s.rb_mut(),
1855 0,
1856 n,
1857 par,
1858 stack,
1859 params.schur,
1860 );
1861 if info != 0 {
1862 return Err(EvdError::NoConvergence);
1863 }
1864 }
1865 let H = H.rb();
1866 if let (Some(mut u), Some(Z)) = (u_right, Z.rb()) {
1867 let (mut X, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1868 let mut X = X.as_mat_mut();
1869 if const { T::IS_REAL } {
1870 evd_from_real_schur_imp::<T::Real>(
1871 unsafe { core::mem::transmute(H) },
1872 unsafe { core::mem::transmute(X.rb_mut()) },
1873 par,
1874 params.evd_from_schur,
1875 );
1876 } else {
1877 evd_from_cplx_schur_imp::<T>(
1878 H,
1879 Conj::No,
1880 X.rb_mut(),
1881 par,
1882 params.evd_from_schur,
1883 );
1884 }
1885 linalg::matmul::triangular::matmul(
1886 u.rb_mut(),
1887 BlockStructure::Rectangular,
1888 Accum::Replace,
1889 Z,
1890 BlockStructure::Rectangular,
1891 X.rb(),
1892 BlockStructure::TriangularUpper,
1893 one(),
1894 par,
1895 );
1896 normalize_eigenvectors(u.rb_mut(), H);
1897 if !eigenvectors_ok(u.rb()) {
1898 return Err(EvdError::NoConvergence);
1899 }
1900 }
1901 if let (Some(mut u), Some(Z)) = (u_left, Z.rb()) {
1902 let (mut X, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1903 let mut X = X.as_mat_mut().reverse_rows_mut();
1904 if const { T::IS_REAL } {
1905 evd_from_real_schur_imp::<T::Real>(
1906 unsafe {
1907 core::mem::transmute(H.transpose().reverse_rows_and_cols())
1908 },
1909 unsafe { core::mem::transmute(X.rb_mut()) },
1910 par,
1911 params.evd_from_schur,
1912 );
1913 } else {
1914 evd_from_cplx_schur_imp::<T>(
1915 H.transpose().reverse_rows_and_cols(),
1916 Conj::Yes,
1917 X.rb_mut(),
1918 par,
1919 params.evd_from_schur,
1920 );
1921 }
1922 linalg::matmul::triangular::matmul(
1923 u.rb_mut(),
1924 BlockStructure::Rectangular,
1925 Accum::Replace,
1926 Z,
1927 BlockStructure::Rectangular,
1928 X.rb().reverse_rows_and_cols(),
1929 BlockStructure::TriangularLower,
1930 one(),
1931 par,
1932 );
1933 normalize_eigenvectors(u.rb_mut(), H);
1934 if !eigenvectors_ok(u.rb()) {
1935 return Err(EvdError::NoConvergence);
1936 }
1937 }
1938 Ok(())
1939}
1940#[track_caller]
1949pub fn evd_cplx<T: RealField>(
1950 A: MatRef<'_, Complex<T>>,
1951 s: DiagMut<'_, Complex<T>>,
1952 u_left: Option<MatMut<'_, Complex<T>>>,
1953 u_right: Option<MatMut<'_, Complex<T>>>,
1954 par: Par,
1955 stack: &mut MemStack,
1956 params: Spec<EvdParams, Complex<T>>,
1957) -> Result<(), EvdError> {
1958 let n = A.nrows();
1959 assert!(all(A.nrows() == n, A.ncols() == n, s.dim() == n));
1960 if let Some(u) = u_left.rb() {
1961 assert!(all(u.nrows() == n, u.ncols() == n));
1962 }
1963 if let Some(u) = u_right.rb() {
1964 assert!(all(u.nrows() == n, u.ncols() == n));
1965 }
1966 evd_imp(
1967 A,
1968 s.column_vector_mut(),
1969 None,
1970 u_left,
1971 u_right,
1972 par,
1973 stack,
1974 params.config,
1975 )
1976}
1977#[track_caller]
1989pub fn evd_real<T: RealField>(
1990 A: MatRef<'_, T>,
1991 s_re: DiagMut<'_, T>,
1992 s_im: DiagMut<'_, T>,
1993 u_left: Option<MatMut<'_, T>>,
1994 u_right: Option<MatMut<'_, T>>,
1995 par: Par,
1996 stack: &mut MemStack,
1997 params: Spec<EvdParams, T>,
1998) -> Result<(), EvdError> {
1999 let n = A.nrows();
2000 assert!(all(
2001 A.nrows() == n,
2002 A.ncols() == n,
2003 s_re.dim() == n,
2004 s_im.dim() == n
2005 ));
2006 if let Some(u) = u_left.rb() {
2007 assert!(all(u.nrows() == n, u.ncols() == n));
2008 }
2009 if let Some(u) = u_right.rb() {
2010 assert!(all(u.nrows() == n, u.ncols() == n));
2011 }
2012 evd_imp(
2013 A,
2014 s_re.column_vector_mut(),
2015 Some(s_im.column_vector_mut()),
2016 u_left,
2017 u_right,
2018 par,
2019 stack,
2020 params.config,
2021 )
2022}
2023#[cfg(test)]
2024mod general_tests {
2025 use super::*;
2026 use crate::assert;
2027 use crate::stats::prelude::*;
2028 use crate::utils::approx::*;
2029 use dyn_stack::MemBuffer;
2030 fn test_cplx_evd(mat: MatRef<'_, c64>) {
2031 let n = mat.nrows();
2032 let params = Spec::new(EvdParams {
2033 hessenberg: auto!(c64),
2034 schur: auto!(c64),
2035 evd_from_schur: EvdFromSchurParams {
2036 recursion_threshold: 8,
2037 ..auto!(c64)
2038 },
2039 ..auto!(c64)
2040 });
2041 let approx_eq = CwiseMat(ApproxEq::eps() * (8.0 * n as f64).sqrt());
2042 let mut s = Mat::zeros(n, n);
2043 {
2044 let mut ul = Mat::zeros(n, n);
2045 let mut ur = Mat::zeros(n, n);
2046 evd_cplx(
2047 mat.as_ref(),
2048 s.as_mut().diagonal_mut(),
2049 Some(ul.as_mut()),
2050 Some(ur.as_mut()),
2051 Par::Seq,
2052 MemStack::new(&mut MemBuffer::new(evd_scratch::<c64>(
2053 n,
2054 ComputeEigenvectors::Yes,
2055 ComputeEigenvectors::Yes,
2056 Par::Seq,
2057 params,
2058 ))),
2059 params,
2060 )
2061 .unwrap();
2062 assert!(& ur * & s ~ mat * & ur);
2063 assert!(& s * ul.adjoint() ~ ul.adjoint() * mat);
2064 }
2065 }
2066 fn test_real_evd(mat: MatRef<'_, f64>) {
2067 let n = mat.nrows();
2068 let params = Spec::new(EvdParams {
2069 hessenberg: auto!(f64),
2070 schur: auto!(f64),
2071 evd_from_schur: EvdFromSchurParams {
2072 recursion_threshold: 8,
2073 ..auto!(f64)
2074 },
2075 ..auto!(f64)
2076 });
2077 let approx_eq =
2078 CwiseMat(ApproxEq::<f64>::eps() * (8.0 * n as f64).sqrt());
2079 let mut s_re = Diag::zeros(n);
2080 let mut s_im = Diag::zeros(n);
2081 {
2082 let mut ul = Mat::zeros(n, n);
2083 let mut ur = Mat::zeros(n, n);
2084 evd_real(
2085 mat.as_ref(),
2086 s_re.as_mut(),
2087 s_im.as_mut(),
2088 Some(ul.as_mut()),
2089 Some(ur.as_mut()),
2090 Par::Seq,
2091 MemStack::new(&mut MemBuffer::new(evd_scratch::<f64>(
2092 n,
2093 ComputeEigenvectors::Yes,
2094 ComputeEigenvectors::Yes,
2095 Par::Seq,
2096 params,
2097 ))),
2098 params,
2099 )
2100 .unwrap();
2101 let mut i = 0;
2102 while i < n {
2103 if s_im[i] == 0.0 {
2104 let ur = ur.col(i);
2105 let ul = ul.col(i);
2106 let s = Scale(s_re[i]);
2107 assert!((& ur * s).as_mat() ~ (mat * & ur).as_mat());
2108 assert!(
2109 (& ul.adjoint() * s).as_mat() ~ (& ul.adjoint() * mat).as_mat()
2110 );
2111 i += 1;
2112 } else {
2113 let re = ur.col(i);
2114 let im = ur.col(i + 1);
2115 let ur = &Col::from_fn(n, |i| c64::new(re[i], im[i]));
2116 let re = ul.col(i);
2117 let im = ul.col(i + 1);
2118 let ul = &Col::from_fn(n, |i| c64::new(re[i], im[i]));
2119 let mat =
2120 &Mat::from_fn(n, n, |i, j| c64::from(mat[(i, j)]));
2121 let s = Scale(c64::new(s_re[i], s_im[i]));
2122 let approx_eq =
2123 CwiseMat(ApproxEq::eps() * (8.0 * n as f64).sqrt());
2124 assert!((ur * s).as_mat() ~ (mat * ur).as_mat());
2125 assert!((ul.adjoint() * s).as_mat() ~ (ul.adjoint() * mat).as_mat());
2126 i += 2;
2127 }
2128 }
2129 }
2130 }
2131 #[test]
2132 fn test_cplx() {
2133 let rng = &mut StdRng::seed_from_u64(1);
2134 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
2135 let mat = CwiseMatDistribution {
2136 nrows: n,
2137 ncols: n,
2138 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
2139 }
2140 .rand::<Mat<c64>>(rng);
2141 test_cplx_evd(mat.as_ref());
2142 }
2143 }
2144 #[test]
2145 fn test_real() {
2146 let rng = &mut StdRng::seed_from_u64(0);
2147 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
2148 let mat = CwiseMatDistribution {
2149 nrows: n,
2150 ncols: n,
2151 dist: StandardNormal,
2152 }
2153 .rand::<Mat<f64>>(rng);
2154 test_real_evd(mat.as_ref());
2155 }
2156 }
2157}
2158#[cfg(test)]
2159mod eig_2131_tests {
2160 use super::*;
2166 use crate::assert;
2167 use dyn_stack::MemBuffer;
2168
2169 fn relative_residual<T: RealField>(
2172 mat: MatRef<'_, T>,
2173 v: ColRef<'_, T>,
2174 lambda: T,
2175 ) -> T {
2176 let n = mat.nrows();
2177 let mut anorm = zero::<T>();
2178 for j in 0..n {
2179 let mut col = zero::<T>();
2180 for i in 0..n {
2181 col += mat[(i, j)].abs1();
2182 }
2183 if col > anorm {
2184 anorm = col;
2185 }
2186 }
2187 let mut vnorm2 = zero::<T>();
2188 let mut rnorm2 = zero::<T>();
2189 for i in 0..n {
2190 let mut acc = zero::<T>();
2191 for j in 0..n {
2192 acc += &mat[(i, j)] * &v[j];
2193 }
2194 acc -= &v[i] * λ
2195 rnorm2 += acc.abs2();
2196 vnorm2 += v[i].abs2();
2197 }
2198 rnorm2.sqrt() / (anorm * vnorm2.sqrt())
2199 }
2200
2201 fn evd_real_vectors<T: RealField>(
2202 mat: MatRef<'_, T>,
2203 params: Spec<EvdParams, T>,
2204 ) -> (Diag<T>, Diag<T>, Mat<T>, Mat<T>) {
2205 let n = mat.nrows();
2206 let mut s_re = Diag::zeros(n);
2207 let mut s_im = Diag::zeros(n);
2208 let mut u = Mat::zeros(n, n);
2209 let mut ul = Mat::zeros(n, n);
2210 let mut mem = MemBuffer::new(evd_scratch::<T>(
2211 n,
2212 ComputeEigenvectors::Yes,
2213 ComputeEigenvectors::Yes,
2214 Par::Seq,
2215 params,
2216 ));
2217 evd_real(
2218 mat,
2219 s_re.as_mut(),
2220 s_im.as_mut(),
2221 Some(ul.as_mut()),
2222 Some(u.as_mut()),
2223 Par::Seq,
2224 MemStack::new(&mut mem),
2225 params,
2226 )
2227 .unwrap();
2228 (s_re, s_im, u, ul)
2229 }
2230
2231 fn jordan<T: RealField>(n: usize) -> Mat<T> {
2233 let two = from_f64::<T>(2.0);
2234 let one = one::<T>();
2235 Mat::<T>::from_fn(n, n, |i, j| {
2236 if i == j {
2237 two.copy()
2238 } else if j == i + 1 {
2239 one.copy()
2240 } else {
2241 zero::<T>()
2242 }
2243 })
2244 }
2245
2246 fn check_jordan<T: RealField>(n: usize, threshold: usize, tol: f64) {
2247 let params = Spec::new(EvdParams {
2248 evd_from_schur: EvdFromSchurParams {
2249 recursion_threshold: threshold,
2250 ..auto!(T)
2251 },
2252 ..auto!(T)
2253 });
2254 let mat = jordan::<T>(n);
2255 let (s_re, _, u, _) = evd_real_vectors(mat.as_ref(), params);
2256 let tol = from_f64::<T>(tol);
2257 for j in 0..n {
2258 let v = u.col(j);
2259 assert!(v.iter().all(|x| x.is_finite()));
2260 assert!((v.norm_l2() - one::<T>()).abs() < from_f64::<T>(1e-5));
2261 assert!(relative_residual(mat.as_ref(), v, s_re[j].copy()) < tol);
2262 }
2263 }
2264
2265 #[test]
2266 fn jordan_eigenvectors_are_finite_and_unit_norm() {
2267 for threshold in [3usize, 64] {
2272 for n in [2, 4, 8, 20, 26, 30, 64, 65, 100] {
2273 check_jordan::<f64>(n, threshold, 1e-10);
2274 }
2275 for n in [2, 4, 8, 16, 30] {
2276 check_jordan::<f32>(n, threshold, 1e-3);
2277 }
2278 }
2279 }
2280
2281 #[test]
2282 fn conjugate_pair_eigenvector_has_unit_norm() {
2283 fn run<T: RealField>(threshold: usize) {
2287 let n = 6;
2288 let mat = Mat::<T>::from_fn(n, n, |i, j| {
2289 if i == j {
2290 if i >= 4 {
2291 zero::<T>()
2292 } else {
2293 from_f64::<T>((i + 1) as f64)
2294 }
2295 } else if i == 4 && j == 5 {
2296 one::<T>()
2297 } else if i == 5 && j == 4 {
2298 -one::<T>()
2299 } else if i + 2 <= j {
2300 from_f64::<T>(0.25)
2301 } else {
2302 zero::<T>()
2303 }
2304 });
2305 let params = Spec::new(EvdParams {
2306 evd_from_schur: EvdFromSchurParams {
2307 recursion_threshold: threshold,
2308 ..auto!(T)
2309 },
2310 ..auto!(T)
2311 });
2312 let (s_re, s_im, u, ul) = evd_real_vectors(mat.as_ref(), params);
2313 let tol = from_f64::<T>(1e-5);
2314 let mut j = 0;
2316 while j < n {
2317 let v = u.col(j);
2318 let vl = ul.col(j);
2319 if s_im[j] == zero::<T>() {
2320 assert!((v.norm_l2() - one::<T>()).abs() < tol);
2321 assert!((vl.norm_l2() - one::<T>()).abs() < tol);
2322 assert!(
2323 relative_residual(mat.as_ref(), v, s_re[j].copy())
2324 < from_f64::<T>(1e-4)
2325 );
2326 j += 1;
2327 } else {
2328 let nrm = v.norm_l2().hypot(&u.col(j + 1).norm_l2());
2329 assert!((nrm - one::<T>()).abs() < tol);
2330 let nrm = vl.norm_l2().hypot(&ul.col(j + 1).norm_l2());
2331 assert!((nrm - one::<T>()).abs() < tol);
2332 j += 2;
2333 }
2334 }
2335 }
2336 for threshold in [4usize, 64] {
2337 run::<f64>(threshold);
2338 run::<f32>(threshold);
2339 }
2340 }
2341
2342 #[test]
2343 fn complex_eigenvectors_have_unit_norm() {
2344 fn run<T: RealField>(threshold: usize) {
2346 let n = 6;
2347 let mat = Mat::<Complex<T>>::from_fn(n, n, |i, j| {
2348 Complex::new(
2349 from_f64::<T>(i as f64)
2350 + from_f64::<T>(j as f64) * from_f64::<T>(0.1),
2351 from_f64::<T>(i as f64 - j as f64) * from_f64::<T>(0.3),
2352 )
2353 });
2354 let params = Spec::new(EvdParams {
2355 evd_from_schur: EvdFromSchurParams {
2356 recursion_threshold: threshold,
2357 ..auto!(Complex<T>)
2358 },
2359 ..auto!(Complex<T>)
2360 });
2361 let mut s = Diag::zeros(n);
2362 let mut u = Mat::zeros(n, n);
2363 let mut mem = MemBuffer::new(evd_scratch::<Complex<T>>(
2364 n,
2365 ComputeEigenvectors::No,
2366 ComputeEigenvectors::Yes,
2367 Par::Seq,
2368 params,
2369 ));
2370 evd_cplx(
2371 mat.as_ref(),
2372 s.as_mut(),
2373 None,
2374 Some(u.as_mut()),
2375 Par::Seq,
2376 MemStack::new(&mut mem),
2377 params,
2378 )
2379 .unwrap();
2380 let tol = from_f64::<T>(1e-5);
2381 for j in 0..n {
2382 assert!((u.col(j).norm_l2() - one::<T>()).abs() < tol);
2383 }
2384 }
2385 for threshold in [4usize, 64] {
2386 run::<f64>(threshold);
2387 run::<f32>(threshold);
2388 }
2389 }
2390}
2391#[cfg(test)]
2392mod self_adjoint_tests {
2393 use super::*;
2394 use crate::assert;
2395 use crate::stats::prelude::*;
2396 use crate::utils::approx::*;
2397 use dyn_stack::MemBuffer;
2398 fn test_self_adjoint_evd<T: ComplexField>(mat: MatRef<'_, T>) {
2399 let n = mat.nrows();
2400 let params = Spec::new(SelfAdjointEvdParams {
2401 recursion_threshold: 8,
2402 ..auto!(T)
2403 });
2404 let approx_eq = CwiseMat(
2405 ApproxEq::<T::Real>::eps()
2406 * from_f64::<T::Real>(8.0 * n as f64).sqrt(),
2407 );
2408 let mut s = Mat::zeros(n, n);
2409 {
2410 let mut u = Mat::zeros(n, n);
2411 self_adjoint_evd(
2412 mat.as_ref(),
2413 s.as_mut().diagonal_mut(),
2414 Some(u.as_mut()),
2415 Par::Seq,
2416 MemStack::new(&mut MemBuffer::new(
2417 self_adjoint_evd_scratch::<T>(
2418 n,
2419 ComputeEigenvectors::Yes,
2420 Par::Seq,
2421 params,
2422 ),
2423 )),
2424 params,
2425 )
2426 .unwrap();
2427 let reconstructed = &u * &s * u.adjoint();
2428 assert!(reconstructed ~ mat);
2429 }
2430 {
2431 let mut s2 = Mat::zeros(n, n);
2432 self_adjoint_evd(
2433 mat.as_ref(),
2434 s2.as_mut().diagonal_mut(),
2435 None,
2436 Par::Seq,
2437 MemStack::new(&mut MemBuffer::new(
2438 self_adjoint_evd_scratch::<T>(
2439 n,
2440 ComputeEigenvectors::No,
2441 Par::Seq,
2442 params,
2443 ),
2444 )),
2445 params,
2446 )
2447 .unwrap();
2448 assert!(s2 ~ s);
2449 }
2450 }
2451 #[test]
2452 fn test_real() {
2453 let rng = &mut StdRng::seed_from_u64(1);
2454 for n in [1, 2, 4, 10, 15, 20, 50, 100, 150] {
2455 let mat = CwiseMatDistribution {
2456 nrows: n,
2457 ncols: n,
2458 dist: StandardNormal,
2459 }
2460 .rand::<Mat<f64>>(rng);
2461 let mat = &mat + mat.adjoint();
2462 test_self_adjoint_evd(mat.as_ref());
2463 }
2464 }
2465 #[test]
2466 fn test_cplx() {
2467 let rng = &mut StdRng::seed_from_u64(1);
2468 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
2469 let mat = CwiseMatDistribution {
2470 nrows: n,
2471 ncols: n,
2472 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
2473 }
2474 .rand::<Mat<c64>>(rng);
2475 let mat = &mat + mat.adjoint();
2476 test_self_adjoint_evd(mat.as_ref());
2477 }
2478 }
2479 #[test]
2480 fn test_special() {
2481 for n in [1, 2, 4, 10, 15, 20, 50, 100, 150] {
2482 test_self_adjoint_evd(Mat::full(n, n, 0.0).as_ref());
2483 test_self_adjoint_evd(Mat::full(n, n, c64::ZERO).as_ref());
2484 test_self_adjoint_evd(Mat::full(n, n, 1.0).as_ref());
2485 test_self_adjoint_evd(Mat::full(n, n, c64::ONE).as_ref());
2486 test_self_adjoint_evd(Mat::<f64>::identity(n, n).as_ref());
2487 test_self_adjoint_evd(Mat::<c64>::identity(n, n).as_ref());
2488 }
2489 }
2490 #[test]
2491 fn test_pinv() {
2492 let rng = &mut StdRng::seed_from_u64(0);
2493 let n = 36;
2494 let mat = CwiseMatDistribution {
2495 nrows: n,
2496 ncols: n,
2497 dist: StandardNormal,
2498 }
2499 .rand::<Mat<f64>>(rng);
2500 let mat = &mat + mat.adjoint();
2501 let pinv = mat.self_adjoint_eigen(Side::Lower).unwrap().pseudoinverse();
2502 let err = &mat * &pinv - Mat::<f64>::identity(n, n);
2503 assert!(err.norm_max() < 1e-10);
2504 }
2505}
2506#[cfg(test)]
2507mod scale_and_empty_tests {
2508 use super::*;
2513 #[test]
2514 fn two_by_two_finite_spectrum_at_the_range_limit() {
2515 let a = crate::mat![[1e308f64, 0.0], [0.0, 1e308]];
2516 let eig = a.self_adjoint_eigen(Side::Lower).unwrap();
2517 assert_eq!(eig.S()[0], 1e308);
2518 assert_eq!(eig.S()[1], 1e308);
2519 let a = crate::mat![[1e38f32, 0.0], [0.0, 1e38]];
2520 let eig = a.self_adjoint_eigen(Side::Lower).unwrap();
2521 assert_eq!(eig.S()[0], 1e38);
2522 assert_eq!(eig.S()[1], 1e38);
2523 }
2524 #[test]
2527 fn two_by_two_subnormal_diagonal() {
2528 let q = f64::from_bits(1);
2529 let a = Mat::<f64>::from_fn(2, 2, |i, j| if i == j { q } else { 0.0 });
2530 let eig = a.self_adjoint_eigen(Side::Lower).unwrap();
2531 assert_eq!(eig.S()[0], q);
2532 assert_eq!(eig.S()[1], q);
2533 }
2534 #[test]
2535 fn empty_matrix() {
2536 let a = Mat::<f64>::zeros(0, 0);
2537 let eig = a.self_adjoint_eigen(Side::Lower).unwrap();
2538 assert_eq!(eig.U().shape(), (0, 0));
2539 assert_eq!(eig.S().dim(), 0);
2540 }
2541}