1pub mod hessenberg;
17#[doc(hidden)]
18pub mod schur;
19pub mod tridiag;
21pub(crate) mod tridiag_evd;
22use crate::assert;
23use crate::internal_prelude::*;
24use hessenberg::HessenbergParams;
25use linalg::matmul::triangular::BlockStructure;
26pub use schur::SchurParams;
27use tridiag::TridiagParams;
28#[derive(Copy, Clone, Debug, PartialEq, Eq)]
30pub enum EvdError {
31 NoConvergence,
33}
34#[derive(Clone, Copy, Debug)]
36pub struct EvdFromSchurParams {
37 pub recursion_threshold: usize,
39 #[doc(hidden)]
40 pub non_exhaustive: NonExhaustive,
41}
42#[derive(Clone, Copy, Debug)]
44pub struct EvdParams {
45 pub hessenberg: HessenbergParams,
47 pub schur: SchurParams,
49 pub evd_from_schur: EvdFromSchurParams,
51 #[doc(hidden)]
52 pub non_exhaustive: NonExhaustive,
53}
54#[derive(Clone, Copy, Debug)]
56pub struct SelfAdjointEvdParams {
57 pub tridiag: TridiagParams,
59 pub recursion_threshold: usize,
61 #[doc(hidden)]
62 pub non_exhaustive: NonExhaustive,
63}
64impl<T: ComplexField> Auto<T> for EvdParams {
65 fn auto() -> Self {
66 Self {
67 hessenberg: auto!(T),
68 schur: auto!(T),
69 evd_from_schur: auto!(T),
70 non_exhaustive: NonExhaustive(()),
71 }
72 }
73}
74impl<T: ComplexField> Auto<T> for EvdFromSchurParams {
75 fn auto() -> Self {
76 Self {
77 recursion_threshold: 64,
78 non_exhaustive: NonExhaustive(()),
79 }
80 }
81}
82impl<T: ComplexField> Auto<T> for SelfAdjointEvdParams {
83 fn auto() -> Self {
84 Self {
85 tridiag: auto!(T),
86 recursion_threshold: 128,
87 non_exhaustive: NonExhaustive(()),
88 }
89 }
90}
91#[derive(Copy, Clone, Debug, PartialEq, Eq)]
93pub enum ComputeEigenvectors {
94 No,
96 Yes,
98}
99pub fn self_adjoint_evd_scratch<T: ComplexField>(
102 dim: usize,
103 compute_u: ComputeEigenvectors,
104 par: Par,
105 params: Spec<SelfAdjointEvdParams, T>,
106) -> StackReq {
107 let n = dim;
108 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(n, n);
109 let prologue = StackReq::all_of(&[
110 temp_mat_scratch::<T>(n, n),
111 temp_mat_scratch::<T>(bs, n),
112 StackReq::any_of(&[tridiag::tridiag_in_place_scratch::<T>(
113 n,
114 par,
115 params.tridiag.into(),
116 )]),
117 temp_mat_scratch::<T::Real>(n, 1).array(2),
118 ]);
119 if compute_u == ComputeEigenvectors::No {
120 return prologue;
121 }
122 StackReq::all_of(&[
123 prologue,
124 temp_mat_scratch::<T::Real>(
125 n,
126 if const {
127 T::IS_REAL
128 } {
129 0
130 } else {
131 n
132 },
133 )
134 .array(2),
135 StackReq::any_of(&[
136 if n < params.recursion_threshold {
137 StackReq::empty()
138 } else {
139 tridiag_evd::divide_and_conquer_scratch::<T>(n, par)
140 },
141 temp_mat_scratch::<T>(n, 1),
142 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_scratch::<T>(n - 1, bs, n),
143 ]),
144 ])
145}
146pub fn tridiagonal_self_adjoint_evd<T: ComplexField>(
152 diag: DiagRef<'_, T>,
153 subdiag: DiagRef<'_, T>,
154 s: DiagMut<'_, T>,
155 u: Option<MatMut<'_, T>>,
156 par: Par,
157 stack: &mut MemStack,
158 params: Spec<SelfAdjointEvdParams, T>,
159) -> Result<(), EvdError> {
160 let n = diag.dim();
161 let (mut real_diag, stack) =
162 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
163 let (mut real_offdiag, stack) =
164 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
165 let mut real_diag = real_diag
166 .as_mat_mut()
167 .col_mut(0)
168 .try_as_col_major_mut()
169 .unwrap();
170 let mut real_offdiag = real_offdiag
171 .as_mat_mut()
172 .col_mut(0)
173 .try_as_col_major_mut()
174 .unwrap();
175 for i in 0..n {
176 real_diag[i] = diag[i].real();
177 if i + 1 < n {
178 if const { T::IS_REAL } {
179 real_offdiag[i] = subdiag[i].real();
180 } else {
181 real_offdiag[i] = subdiag[i].abs();
182 }
183 } else {
184 real_offdiag[i] = zero();
185 }
186 }
187 let mut s = s;
188 let mut u = match u {
189 Some(u) => u,
190 None => {
191 tridiag_evd::qr_algorithm(
192 real_diag.rb_mut(),
193 real_offdiag.rb_mut(),
194 None,
195 )?;
196 for i in 0..n {
197 s[i] = real_diag[i].to_cplx();
198 }
199 return Ok(());
200 },
201 };
202 let (mut u_real, stack) = unsafe {
203 temp_mat_uninit::<T::Real, _, _>(
204 n,
205 if T::IS_REAL { 0 } else { n },
206 stack,
207 )
208 };
209 let mut u_real = u_real.as_mat_mut();
210 let mut u_evd = if const { T::IS_REAL } {
211 unsafe { core::mem::transmute(u.rb_mut()) }
212 } else {
213 u_real.rb_mut()
214 };
215 if n < params.recursion_threshold {
216 tridiag_evd::qr_algorithm(
217 real_diag.rb_mut(),
218 real_offdiag.rb_mut(),
219 Some(u_evd.rb_mut()),
220 )?;
221 } else {
222 tridiag_evd::divide_and_conquer::<T::Real>(
223 real_diag.rb_mut(),
224 real_offdiag.rb_mut(),
225 u_evd.rb_mut(),
226 par,
227 stack,
228 params.recursion_threshold,
229 )?;
230 }
231 if const { !T::IS_REAL } {
232 let normalized = |x: T| {
233 if x == zero() {
234 one()
235 } else {
236 x.mul_real(x.abs().recip())
237 }
238 };
239 let (mut scale, _) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
240 let mut scale = scale
241 .as_mat_mut()
242 .col_mut(0)
243 .try_as_col_major_mut()
244 .unwrap();
245 let mut x = one::<T>();
246 scale[0] = one();
247 for i in 1..n {
248 x = normalized(&subdiag[i - 1] * x);
249 scale[i] = x.copy();
250 }
251 for j in 0..n {
252 z!(u.rb_mut().col_mut(j), u_real.rb().col(j), scale.rb()).for_each(
253 |uz!(u, real, scale)| {
254 *u = scale.mul_real(real);
255 },
256 );
257 }
258 }
259 for i in 0..n {
260 s[i] = real_diag[i].to_cplx();
261 }
262 Ok(())
263}
264pub fn self_adjoint_evd<T: ComplexField>(
271 A: MatRef<'_, T>,
272 s: DiagMut<'_, T>,
273 u: Option<MatMut<'_, T>>,
274 par: Par,
275 stack: &mut MemStack,
276 params: Spec<SelfAdjointEvdParams, T>,
277) -> Result<(), EvdError> {
278 let n = A.nrows();
279 assert!(all(A.nrows() == A.ncols(), s.dim() == n));
280 if let Some(u) = u.rb() {
281 assert!(all(u.nrows() == n, u.ncols() == n));
282 }
283 let s = s.column_vector_mut();
284 if n == 0 {
285 return Ok(());
286 }
287 #[cfg(feature = "perf-warn")]
288 if let Some(matrix) = u.rb() {
289 if matrix.row_stride().unsigned_abs() != 1
290 && crate::__perf_warn!(QR_WARN)
291 {
292 if matrix.col_stride().unsigned_abs() == 1 {
293 log::warn!(
294 target : "faer_perf",
295 "EVD prefers column-major eigenvector matrix. Found row-major matrix."
296 );
297 } else {
298 log::warn!(
299 target : "faer_perf",
300 "EVD prefers column-major eigenvector matrix. Found matrix with generic strides."
301 );
302 }
303 }
304 }
305 let (mut trid, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
306 let mut trid = trid.as_mat_mut();
307 trid.copy_from_triangular_lower(A);
308 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(n, n);
309 let (mut householder, stack) =
310 unsafe { temp_mat_uninit::<T, _, _>(bs, n - 1, stack) };
311 let mut householder = householder.as_mat_mut();
312 {
313 tridiag::tridiag_in_place(
314 trid.rb_mut(),
315 householder.rb_mut(),
316 par,
317 stack,
318 params.tridiag.into(),
319 );
320 }
321 let trid = trid.rb();
322 let (mut diag, stack) =
323 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
324 let (mut offdiag, stack) =
325 unsafe { temp_mat_uninit::<T::Real, _, _>(n, 1, stack) };
326 let mut diag = diag.as_mat_mut().col_mut(0).try_as_col_major_mut().unwrap();
327 let mut offdiag = offdiag
328 .as_mat_mut()
329 .col_mut(0)
330 .try_as_col_major_mut()
331 .unwrap();
332 for i in 0..n {
333 diag[i] = trid[(i, i)].real();
334 if i + 1 < n {
335 if const { T::IS_REAL } {
336 offdiag[i] = trid[(i + 1, i)].real();
337 } else {
338 offdiag[i] = trid[(i + 1, i)].abs();
339 }
340 } else {
341 offdiag[i] = zero();
342 }
343 }
344 let mut s = s;
345 let mut u = match u {
346 Some(u) => u,
347 None => {
348 tridiag_evd::qr_algorithm(diag.rb_mut(), offdiag.rb_mut(), None)?;
349 for i in 0..n {
350 s[i] = diag[i].to_cplx();
351 }
352 return Ok(());
353 },
354 };
355 let (mut u_real, stack) = unsafe {
356 temp_mat_uninit::<T::Real, _, _>(
357 n,
358 if T::IS_REAL { 0 } else { n },
359 stack,
360 )
361 };
362 let mut u_real = u_real.as_mat_mut();
363 let mut u_evd = if const { T::IS_REAL } {
364 unsafe { core::mem::transmute(u.rb_mut()) }
365 } else {
366 u_real.rb_mut()
367 };
368 if n < params.recursion_threshold {
369 tridiag_evd::qr_algorithm(
370 diag.rb_mut(),
371 offdiag.rb_mut(),
372 Some(u_evd.rb_mut()),
373 )?;
374 } else {
375 tridiag_evd::divide_and_conquer::<T::Real>(
376 diag.rb_mut(),
377 offdiag.rb_mut(),
378 u_evd.rb_mut(),
379 par,
380 stack,
381 params.recursion_threshold,
382 )?;
383 }
384 if const { !T::IS_REAL } {
385 let normalized = |x: T| {
386 if x == zero() {
387 one()
388 } else {
389 x.mul_real(x.abs().recip())
390 }
391 };
392 let (mut scale, _) = unsafe { temp_mat_uninit::<T, _, _>(n, 1, stack) };
393 let mut scale = scale
394 .as_mat_mut()
395 .col_mut(0)
396 .try_as_col_major_mut()
397 .unwrap();
398 let mut x = one::<T>();
399 scale[0] = one();
400 for i in 1..n {
401 x = normalized(&trid[(i, i - 1)] * x);
402 scale[i] = x.copy();
403 }
404 for j in 0..n {
405 z!(u.rb_mut().col_mut(j), u_real.rb().col(j), scale.rb()).for_each(
406 |uz!(u, real, scale)| {
407 *u = scale.mul_real(real);
408 },
409 );
410 }
411 }
412 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
413 trid.submatrix(1, 0, n - 1, n - 1),
414 householder.rb(),
415 Conj::No,
416 u.rb_mut().subrows_mut(1, n - 1),
417 par,
418 stack,
419 );
420 for i in 0..n {
421 s[i] = diag[i].to_cplx();
422 }
423 Ok(())
424}
425pub fn pseudoinverse_from_self_adjoint_evd_scratch<T: ComplexField>(
428 dim: usize,
429 par: Par,
430) -> StackReq {
431 _ = par;
432 temp_mat_scratch::<T>(dim, dim).array(2)
433}
434#[track_caller]
437pub fn pseudoinverse_from_self_adjoint_evd<T: ComplexField>(
438 pinv: MatMut<'_, T>,
439 s: DiagRef<'_, T>,
440 u: MatRef<'_, T>,
441 par: Par,
442 stack: &mut MemStack,
443) {
444 pseudoinverse_from_self_adjoint_evd_with_tolerance(
445 pinv,
446 s,
447 u,
448 zero(),
449 eps::<T::Real>() * from_f64::<T::Real>(u.ncols() as f64),
450 par,
451 stack,
452 );
453}
454#[track_caller]
458pub fn pseudoinverse_from_self_adjoint_evd_with_tolerance<T: ComplexField>(
459 pinv: MatMut<'_, T>,
460 s: DiagRef<'_, T>,
461 u: MatRef<'_, T>,
462 abs_tol: T::Real,
463 rel_tol: T::Real,
464 par: Par,
465 stack: &mut MemStack,
466) {
467 let s = s.column_vector();
468 let mut pinv = pinv;
469 let n = u.ncols();
470 assert!(all(u.nrows() == n, u.ncols() == n, s.nrows() == n));
471 let smax = s.norm_max();
472 let tol = abs_tol.fmax(rel_tol * smax);
473 let (mut u_trunc, stack) =
474 unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
475 let (mut up_trunc, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
476 let mut u_trunc = u_trunc.as_mat_mut();
477 let mut up_trunc = up_trunc.as_mat_mut();
478 let mut len = 0;
479 for j in 0..n {
480 let x = s[j].absmax();
481 if x > tol {
482 let ref p = s[j].real().recip();
483 u_trunc.rb_mut().col_mut(len).copy_from(u.col(j));
484 z!(up_trunc.rb_mut().col_mut(len), u.col(j))
485 .for_each(|uz!(dst, src)| *dst = src.mul_real(p));
486 len += 1;
487 }
488 }
489 let u_trunc = u_trunc.get(.., ..len);
490 let up_trunc = up_trunc.get(.., ..len);
491 linalg::matmul::triangular::matmul(
492 pinv.rb_mut(),
493 BlockStructure::TriangularLower,
494 Accum::Replace,
495 up_trunc.rb(),
496 BlockStructure::Rectangular,
497 u_trunc.rb().adjoint(),
498 BlockStructure::Rectangular,
499 one(),
500 par,
501 );
502 for j in 0..n {
503 for i in 0..j {
504 pinv[(i, j)] = pinv[(j, i)].conj();
505 }
506 }
507}
508fn dot2x1<T: RealField>(
509 lhs0: RowRef<'_, T>,
510 lhs1: RowRef<'_, T>,
511 rhs: ColRef<'_, T>,
512) -> (T, T) {
513 let n = rhs.nrows();
514 assert!(all(lhs0.ncols() == n, lhs1.ncols() == n));
515 let mut acc00 = zero::<T>();
516 let mut acc01 = zero::<T>();
517 let mut acc10 = zero::<T>();
518 let mut acc11 = zero::<T>();
519 let n2 = n / 2 * 2;
520 let mut i = 0;
521 while i < n2 {
522 acc00 += &lhs0[i] * &rhs[i];
523 acc10 += &lhs1[i] * &rhs[i];
524 acc01 += &lhs0[i + 1] * &rhs[i + 1];
525 acc11 += &lhs1[i + 1] * &rhs[i + 1];
526 i += 2;
527 }
528 while i < n {
529 acc00 += &lhs0[i] * &rhs[i];
530 acc10 += &lhs1[i] * &rhs[i];
531 i += 1;
532 }
533 (acc00 + acc01, acc10 + acc11)
534}
535fn dot2x2<T: RealField>(
536 lhs0: RowRef<'_, T>,
537 lhs1: RowRef<'_, T>,
538 rhs0: ColRef<'_, T>,
539 rhs1: ColRef<'_, T>,
540) -> (T, T, T, T) {
541 let n = rhs0.nrows();
542 assert!(all(lhs0.ncols() == n, lhs1.ncols() == n));
543 let mut acc0 = zero::<T>();
544 let mut acc1 = zero::<T>();
545 let mut acc2 = zero::<T>();
546 let mut acc3 = zero::<T>();
547 let mut i = 0;
548 while i < n {
549 acc0 += &lhs0[i] * &rhs0[i];
550 acc1 += &lhs1[i] * &rhs0[i];
551 acc2 += &lhs0[i] * &rhs1[i];
552 acc3 += &lhs1[i] * &rhs1[i];
553 i += 1;
554 }
555 (acc0, acc1, acc2, acc3)
556}
557fn normalize_eigenvectors<T: ComplexField>(
562 mut u: MatMut<'_, T>,
563 h: MatRef<'_, T>,
564) {
565 let n = u.nrows();
566 let mut j = 0;
567 while j < n {
568 let paired = T::IS_REAL && j + 1 < n && h[(j + 1, j)] != zero::<T>();
569 let nrm = if paired {
570 u.rb().col(j).norm_l2().hypot(&u.rb().col(j + 1).norm_l2())
571 } else {
572 u.rb().col(j).norm_l2()
573 };
574 if nrm != zero::<T::Real>() {
575 let inv: T = nrm.recip().to_cplx();
576 for i in 0..n {
577 u[(i, j)] *= inv.copy();
578 if paired {
579 u[(i, j + 1)] *= inv.copy();
580 }
581 }
582 }
583 j += if paired { 2 } else { 1 };
584 }
585}
586fn eigenvectors_ok<T: ComplexField>(u: MatRef<'_, T>) -> bool {
589 u.is_all_finite()
590 && (0..u.ncols()).all(|j| u.col(j).norm_l2() > zero::<T::Real>())
591}
592pub(crate) fn evd_from_real_schur_imp<T: RealField>(
593 A: MatRef<'_, T>,
594 V: MatMut<'_, T>,
595 par: Par,
596 params: EvdFromSchurParams,
597) {
598 let one = one::<T>;
599 let zero = zero::<T>;
600 let mut V = V;
601 let n = A.nrows();
602 let mut norm = zero();
603 for j in 0..n {
604 for i in 0..Ord::min(j + 2, n) {
605 norm += A[(i, j)].abs1();
606 }
607 }
608 let mut k = n;
609 loop {
610 if k == 0 {
611 break;
612 }
613 k -= 1;
614 if k == 0 || A[(k, k - 1)] == zero() {
615 let ref p = A[(k, k)].copy();
616 V[(k, k)] = one();
617 for i in 0..k {
618 V[(i, k)] = -&A[(i, k)];
619 }
620 let scale = solve_real_shifted_upper_quasi_triangular_system(
621 A.submatrix(0, 0, k, k),
622 p.copy(),
623 V.rb_mut().subrows_mut(0, k).col_mut(k),
624 norm.copy(),
625 par,
626 params,
627 );
628 V[(k, k)] *= scale;
632 } else {
633 let ref p = A[(k, k)].copy();
634 let ref q = A[(k, k - 1)].abs().sqrt() * A[(k - 1, k)].abs().sqrt();
635 if A[(k - 1, k)].abs() >= A[(k, k - 1)].abs() {
636 V[(k - 1, k - 1)] = one();
637 V[(k, k)] = q / &A[(k - 1, k)];
638 } else {
639 V[(k - 1, k - 1)] = -q / &A[(k, k - 1)];
640 V[(k, k)] = one();
641 }
642 V[(k - 1, k)] = zero();
643 V[(k, k - 1)] = zero();
644 for i in 0..k - 1 {
645 V[(i, k - 1)] = -&V[(k - 1, k - 1)] * &A[(i, k - 1)];
646 V[(i, k)] = -&V[(k, k)] * &A[(i, k)];
647 }
648 let scale = solve_cplx_shifted_upper_quasi_triangular_system(
649 A.submatrix(0, 0, k - 1, k - 1),
650 p.copy(),
651 q.copy(),
652 V.rb_mut().submatrix_mut(0, k - 1, k - 1, 2),
653 norm.copy(),
654 par,
655 params,
656 );
657 V[(k - 1, k - 1)] *= scale.copy();
658 V[(k, k)] *= scale;
659 k -= 1;
660 }
661 }
662}
663pub(crate) fn evd_from_cplx_schur_imp<T: ComplexField>(
664 A: MatRef<'_, T>,
665 conj_A: Conj,
666 V: MatMut<'_, T>,
667 par: Par,
668 params: EvdFromSchurParams,
669) {
670 let one = one::<T>;
671 let mut V = V;
672 let n = A.nrows();
673 let mut norm = zero::<T::Real>();
674 for j in 0..n {
675 for i in 0..j + 1 {
676 norm += A[(i, j)].abs1();
677 }
678 }
679 let mut k = n;
680 loop {
681 if k == 0 {
682 break;
683 }
684 k -= 1;
685 let ref p = if conj_A == Conj::Yes {
686 A[(k, k)].conj()
687 } else {
688 A[(k, k)].copy()
689 };
690 V[(k, k)] = one();
691 if conj_A == Conj::Yes {
692 for i in 0..k {
693 V[(i, k)] = -A[(i, k)].conj();
694 }
695 } else {
696 for i in 0..k {
697 V[(i, k)] = -&A[(i, k)];
698 }
699 }
700 let scale = solve_shifted_upper_triangular_system(
701 A.submatrix(0, 0, k, k),
702 conj_A,
703 p.copy(),
704 V.rb_mut().subrows_mut(0, k).col_mut(k),
705 norm.copy(),
706 par,
707 params,
708 );
709 let scale: T = scale.to_cplx();
710 V[(k, k)] *= scale;
711 }
712}
713fn solve_real_shifted_upper_quasi_triangular_system<T: RealField>(
714 A: MatRef<'_, T>,
715 p: T,
716 x: ColMut<'_, T>,
717 norm: T,
718 par: Par,
719 params: EvdFromSchurParams,
720) -> T {
721 let n = A.nrows();
722 let one = one::<T>;
723 let zero = zero::<T>;
724 let ref eps = eps::<T>();
725 let ref p = p;
726 let mut x = x;
727 let ref norm = norm;
728 let big = sqrt_max_positive::<T>();
734 let mut scale = one();
735 if par.degree() == 0 || n < params.recursion_threshold {
736 let mut i = n;
737 loop {
738 if i == 0 {
739 break;
740 }
741 i -= 1;
742 if i == 0 || A[(i, i - 1)] == zero() {
743 let dot = linalg::matmul::dot::inner_prod(
744 A.row(i).subcols(i + 1, n - i - 1),
745 Conj::No,
746 x.rb().subrows(i + 1, n - i - 1),
747 Conj::No,
748 );
749 x[i] -= dot;
750 let mut z = &A[(i, i)] - p;
751 if z.abs() < eps * norm {
752 z = eps * norm;
753 }
754 if x[i] != zero() {
755 x[i] /= z;
756 }
757 let m = x[i].abs();
758 if m > big {
759 let r = m.recip();
760 for j in 0..x.nrows() {
761 x[j] *= r.copy();
762 }
763 scale *= r;
764 }
765 } else {
766 let (dot0, dot1) = dot2x1(
767 A.row(i - 1).subcols(i + 1, n - i - 1),
768 A.row(i).subcols(i + 1, n - i - 1),
769 x.rb().subrows(i + 1, n - i - 1),
770 );
771 x[i - 1] -= dot0;
772 x[i] -= dot1;
773 let ref a = &A[(i, i)] - p;
774 let ref b = A[(i - 1, i)];
775 let ref c = A[(i, i - 1)];
776 let ref r0 = x[i - 1];
777 let ref r1 = x[i];
778 let ref inv_det = (a.abs2() - b * c).recip();
779 (x[i - 1], x[i]) =
780 ((a * r0 - b * r1) * inv_det, (a * r1 - c * r0) * inv_det);
781 let a0 = x[i - 1].abs();
782 let a1 = x[i].abs();
783 let m = if a0 > a1 { a0 } else { a1 };
784 if m > big {
785 let r = m.recip();
786 for j in 0..x.nrows() {
787 x[j] *= r.copy();
788 }
789 scale *= r;
790 }
791 i -= 1;
792 }
793 }
794 scale
795 } else {
796 let mut mid = n / 2;
797 if A[(mid, mid - 1)] != zero() {
798 mid -= 1;
799 }
800 let (A00, A01, _, A11) = A.split_at(mid, mid);
801 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
802 let f1 = solve_real_shifted_upper_quasi_triangular_system(
803 A11,
804 p.copy(),
805 x1.rb_mut(),
806 norm.copy(),
807 par,
808 params,
809 );
810 for j in 0..x0.nrows() {
814 x0[j] *= f1.copy();
815 }
816 linalg::matmul::matmul(
817 x0.rb_mut().as_mat_mut(),
818 Accum::Add,
819 A01,
820 x1.rb().as_mat(),
821 -one(),
822 par,
823 );
824 let f0 = solve_real_shifted_upper_quasi_triangular_system(
825 A00,
826 p.copy(),
827 x0.rb_mut(),
828 norm.copy(),
829 par,
830 params,
831 );
832 for j in 0..x1.nrows() {
833 x1[j] *= f0.copy();
834 }
835 f0 * f1
836 }
837}
838fn solve_cplx_shifted_upper_quasi_triangular_system<T: RealField>(
839 A: MatRef<'_, T>,
840 p: T,
841 q: T,
842 x: MatMut<'_, T>,
843 norm: T,
844 par: Par,
845 params: EvdFromSchurParams,
846) -> T {
847 let n = A.nrows();
848 let one = one::<T>;
849 let zero = zero::<T>;
850 let ref eps = eps::<T>();
851 let ref norm = norm;
852 let ref p = p;
853 let ref q = q;
854 let mut x = x;
855 let big = sqrt_max_positive::<T>();
856 let mut scale = one();
857 if par.degree() == 0 || n < params.recursion_threshold {
858 let mut i = n;
859 loop {
860 if i == 0 {
861 break;
862 }
863 i -= 1;
864 if i == 0 || A[(i, i - 1)] == zero() {
865 let (re, im) = dot2x1(
866 x.rb().subrows(i + 1, n - i - 1).col(0).transpose(),
867 x.rb().subrows(i + 1, n - i - 1).col(1).transpose(),
868 A.row(i).subcols(i + 1, n - i - 1).transpose(),
869 );
870 x[(i, 0)] = &x[(i, 0)] - re;
871 x[(i, 1)] = &x[(i, 1)] - im;
872 let mut z_re = &A[(i, i)] - p;
873 let mut z_im = -q;
874 let mut z = z_re.hypot(&z_im);
875 if z < eps * norm {
876 z_re = eps * norm;
877 z_im = zero();
878 z = z_re.copy();
879 }
880 let ref z = z;
881 let ref z_re = (z_re / z) / z;
882 let ref z_im = (-z_im / z) / z;
883 let ref x_re = x[(i, 0)].copy();
884 let ref x_im = x[(i, 1)].copy();
885 x[(i, 0)] = x_re * z_re - x_im * z_im;
886 x[(i, 1)] = x_re * z_im + x_im * z_re;
887 let a0 = x[(i, 0)].abs();
888 let a1 = x[(i, 1)].abs();
889 let m = if a0 > a1 { a0 } else { a1 };
890 if m > big {
891 let r = m.recip();
892 for j in 0..x.nrows() {
893 x[(j, 0)] *= r.copy();
894 x[(j, 1)] *= r.copy();
895 }
896 scale *= r;
897 }
898 } else {
899 let (re0, re1, im0, im1) = dot2x2(
900 A.row(i - 1).subcols(i + 1, n - i - 1),
901 A.row(i).subcols(i + 1, n - i - 1),
902 x.rb().col(0).subrows(i + 1, n - i - 1),
903 x.rb().col(1).subrows(i + 1, n - i - 1),
904 );
905 x[(i - 1, 0)] = &x[(i - 1, 0)] - re0;
906 x[(i - 1, 1)] = &x[(i - 1, 1)] - im0;
907 x[(i, 0)] = &x[(i, 0)] - re1;
908 x[(i, 1)] = &x[(i, 1)] - im1;
909 let ref a_re = &A[(i, i)] - p;
910 let ref a_im = -q;
911 let ref b = A[(i - 1, i)];
912 let ref c = A[(i, i - 1)];
913 let ref r0_re = x[(i - 1, 0)];
914 let ref r0_im = x[(i - 1, 1)];
915 let ref r1_re = x[(i, 0)];
916 let ref r1_im = x[(i, 1)];
917 let mut z_re = a_re.abs2() - a_im.abs2() - b * c;
918 let mut z_im = (a_re * a_im).mul_pow2(from_f64::<T>(2.0));
919 let mut z = z_re.hypot(&z_im);
920 if z < eps * norm {
921 z_re = eps * norm;
922 z_im = zero();
923 z = z_re.copy();
924 }
925 let ref z = z;
926 let ref z_re = (z_re / z) / z;
927 let ref z_im = (-z_im / z) / z;
928 let ref x0_re = (a_re * r0_re - a_im * r0_im) - b * r1_re;
929 let ref x0_im = (a_re * r0_im + a_im * r0_re) - b * r1_im;
930 let ref x1_re = (a_re * r1_re - a_im * r1_im) - c * r0_re;
931 let ref x1_im = (a_re * r1_im + a_im * r1_re) - c * r0_im;
932 x[(i - 1, 0)] = x0_re * z_re - x0_im * z_im;
933 x[(i - 1, 1)] = x0_re * z_im + x0_im * z_re;
934 x[(i, 0)] = x1_re * z_re - x1_im * z_im;
935 x[(i, 1)] = x1_re * z_im + x1_im * z_re;
936 let mut m = x[(i - 1, 0)].abs();
937 let v = x[(i - 1, 1)].abs();
938 if v > m {
939 m = v;
940 }
941 let v = x[(i, 0)].abs();
942 if v > m {
943 m = v;
944 }
945 let v = x[(i, 1)].abs();
946 if v > m {
947 m = v;
948 }
949 if m > big {
950 let r = m.recip();
951 for j in 0..x.nrows() {
952 x[(j, 0)] *= r.copy();
953 x[(j, 1)] *= r.copy();
954 }
955 scale *= r;
956 }
957 i -= 1;
958 }
959 }
960 scale
961 } else {
962 let mut mid = n / 2;
963 if A[(mid, mid - 1)] != zero() {
964 mid -= 1;
965 }
966 let (A00, A01, _, A11) = A.split_at(mid, mid);
967 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
968 let f1 = solve_cplx_shifted_upper_quasi_triangular_system(
969 A11,
970 p.copy(),
971 q.copy(),
972 x1.rb_mut(),
973 norm.copy(),
974 par,
975 params,
976 );
977 for j in 0..x0.nrows() {
978 x0[(j, 0)] *= f1.copy();
979 x0[(j, 1)] *= f1.copy();
980 }
981 linalg::matmul::matmul(
982 x0.rb_mut(),
983 Accum::Add,
984 A01,
985 x1.rb(),
986 -one(),
987 par,
988 );
989 let f0 = solve_cplx_shifted_upper_quasi_triangular_system(
990 A00,
991 p.copy(),
992 q.copy(),
993 x0.rb_mut(),
994 norm.copy(),
995 par,
996 params,
997 );
998 for j in 0..x1.nrows() {
999 x1[(j, 0)] *= f0.copy();
1000 x1[(j, 1)] *= f0.copy();
1001 }
1002 f0 * f1
1003 }
1004}
1005fn solve_shifted_upper_triangular_system<T: ComplexField>(
1006 A: MatRef<'_, T>,
1007 conj_A: Conj,
1008 p: T,
1009 x: ColMut<'_, T>,
1010 norm: T::Real,
1011 par: Par,
1012 params: EvdFromSchurParams,
1013) -> T::Real {
1014 let n = A.nrows();
1015 let mut scale = one::<T::Real>();
1016 let one = one::<T>;
1017 let zero = zero::<T>;
1018 let ref eps = eps::<T::Real>();
1019 let ref norm = norm;
1020 let ref p = p;
1021 let mut x = x;
1022 let big = sqrt_max_positive::<T::Real>();
1023 if par.degree() == 0 || n < params.recursion_threshold {
1024 let mut i = n;
1025 loop {
1026 if i == 0 {
1027 break;
1028 }
1029 i -= 1;
1030 let dot = linalg::matmul::dot::inner_prod(
1031 A.row(i).subcols(i + 1, n - i - 1),
1032 conj_A,
1033 x.rb().subrows(i + 1, n - i - 1),
1034 Conj::No,
1035 );
1036 x[i] -= dot;
1037 let mut z = if conj_A == Conj::Yes {
1038 A[(i, i)].conj()
1039 } else {
1040 A[(i, i)].copy()
1041 } - p;
1042 if z.abs() < eps * norm {
1043 z = (eps * norm).to_cplx();
1044 }
1045 if x[i] != zero() {
1046 x[i] *= z.recip();
1047 }
1048 let m = x[i].abs();
1049 if m > big {
1050 let r = m.recip();
1051 let rc: T = r.to_cplx();
1052 for j in 0..x.nrows() {
1053 x[j] *= rc.copy();
1054 }
1055 scale *= r;
1056 }
1057 }
1058 scale
1059 } else {
1060 let mid = n / 2;
1061 let (A00, A01, _, A11) = A.split_at(mid, mid);
1062 let (mut x0, mut x1) = x.rb_mut().split_at_row_mut(mid);
1063 let f1 = solve_shifted_upper_triangular_system(
1064 A11,
1065 conj_A,
1066 p.copy(),
1067 x1.rb_mut(),
1068 norm.copy(),
1069 par,
1070 params,
1071 );
1072 let f1c: T = f1.to_cplx();
1073 for j in 0..x0.nrows() {
1074 x0[j] *= f1c.copy();
1075 }
1076 linalg::matmul::matmul_with_conj(
1077 x0.rb_mut().as_mat_mut(),
1078 Accum::Add,
1079 A01,
1080 conj_A,
1081 x1.rb().as_mat(),
1082 Conj::No,
1083 -one(),
1084 par,
1085 );
1086 let f0 = solve_shifted_upper_triangular_system(
1087 A00,
1088 conj_A,
1089 p.copy(),
1090 x0.rb_mut(),
1091 norm.copy(),
1092 par,
1093 params,
1094 );
1095 let f0c: T = f0.to_cplx();
1096 for j in 0..x1.nrows() {
1097 x1[j] *= f0c.copy();
1098 }
1099 f0 * f1
1100 }
1101}
1102pub fn evd_scratch<T: ComplexField>(
1105 dim: usize,
1106 eigen_left: ComputeEigenvectors,
1107 eigen_right: ComputeEigenvectors,
1108 par: Par,
1109 params: Spec<EvdParams, T>,
1110) -> StackReq {
1111 let n = dim;
1112 if n == 0 {
1113 return StackReq::EMPTY;
1114 }
1115 let compute_eigen = eigen_left == ComputeEigenvectors::Yes
1116 || eigen_right == ComputeEigenvectors::Yes;
1117 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(
1118 n - 1,
1119 n - 1,
1120 );
1121 let H = temp_mat_scratch::<T>(n, n);
1122 let X = H;
1123 let Z = temp_mat_scratch::<T>(n, if compute_eigen { n } else { 0 });
1124 let householder = temp_mat_scratch::<T>(bs, n);
1125 let apply = linalg::householder::apply_block_householder_sequence_on_the_right_in_place_scratch::<T>(n - 1, bs, n - 1);
1126 StackReq::all_of(&[
1127 H,
1128 Z,
1129 StackReq::any_of(&[
1130 householder.and(
1131 hessenberg::hessenberg_in_place_scratch::<T>(
1132 n,
1133 bs,
1134 par,
1135 params.hessenberg.into(),
1136 )
1137 .or(apply),
1138 ),
1139 schur::multishift_qr_scratch::<T>(
1140 n,
1141 n,
1142 compute_eigen,
1143 compute_eigen,
1144 par,
1145 params.schur,
1146 ),
1147 X,
1148 ]),
1149 ])
1150}
1151fn evd_imp<T: ComplexField>(
1152 A: MatRef<'_, T>,
1153 s: ColMut<'_, T>,
1154 s_im: Option<ColMut<'_, T>>,
1155 u_left: Option<MatMut<'_, T>>,
1156 u_right: Option<MatMut<'_, T>>,
1157 par: Par,
1158 stack: &mut MemStack,
1159 params: EvdParams,
1160) -> Result<(), EvdError> {
1161 let n = A.nrows();
1162 if n == 0 {
1163 return Ok(());
1164 }
1165 for j in 0..n {
1166 for i in 0..n {
1167 if !A[(i, j)].is_finite() {
1168 return Err(EvdError::NoConvergence);
1169 }
1170 }
1171 }
1172 let bs = linalg::qr::no_pivoting::factor::recommended_block_size::<T>(
1173 n - 1,
1174 n - 1,
1175 );
1176 let mut s = s;
1177 let mut s_im = s_im;
1178 let (mut H, stack) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1179 let (mut Z, stack) = unsafe {
1180 temp_mat_uninit::<T, _, _>(
1181 n,
1182 if u_left.is_some() || u_right.is_some() {
1183 n
1184 } else {
1185 0
1186 },
1187 stack,
1188 )
1189 };
1190 let mut H = H.as_mat_mut();
1191 let mut Z = if u_left.is_some() || u_right.is_some() {
1192 Some(Z.as_mat_mut())
1193 } else {
1194 None
1195 };
1196 H.copy_from(A);
1197 {
1198 let (mut householder, stack) =
1199 unsafe { temp_mat_uninit::<T, _, _>(bs, n - 1, stack) };
1200 let mut householder = householder.as_mat_mut();
1201 hessenberg::hessenberg_in_place(
1202 H.rb_mut(),
1203 householder.rb_mut(),
1204 par,
1205 stack,
1206 params.hessenberg.into(),
1207 );
1208 if let Some(mut Z) = Z.rb_mut() {
1209 Z.fill(zero());
1210 Z.rb_mut().diagonal_mut().fill(one());
1211 linalg::householder::apply_block_householder_sequence_on_the_right_in_place_with_conj(
1212 H.rb().submatrix(1, 0, n - 1, n - 1),
1213 householder.rb(),
1214 Conj::No,
1215 Z.rb_mut().submatrix_mut(1, 1, n - 1, n - 1),
1216 par,
1217 stack,
1218 );
1219 }
1220 for j in 0..n {
1221 for i in j + 2..n {
1222 H[(i, j)] = zero();
1223 }
1224 }
1225 }
1226 if const { T::IS_REAL } {
1227 let (info, _, _) = schur::real_schur::multishift_qr::<T::Real>(
1228 unsafe { core::mem::transmute(Z.is_some()) },
1229 unsafe { core::mem::transmute(H.rb_mut()) },
1230 unsafe { core::mem::transmute(Z.rb_mut()) },
1231 unsafe { core::mem::transmute(s.rb_mut()) },
1232 unsafe { core::mem::transmute(s_im.rb_mut().unwrap()) },
1233 0,
1234 n,
1235 par,
1236 stack,
1237 params.schur,
1238 );
1239 if info != 0 {
1240 return Err(EvdError::NoConvergence);
1241 }
1242 } else {
1243 let (info, _, _) = schur::complex_schur::multishift_qr::<T>(
1244 Z.is_some(),
1245 H.rb_mut(),
1246 Z.rb_mut(),
1247 s.rb_mut(),
1248 0,
1249 n,
1250 par,
1251 stack,
1252 params.schur,
1253 );
1254 if info != 0 {
1255 return Err(EvdError::NoConvergence);
1256 }
1257 }
1258 let H = H.rb();
1259 if let (Some(mut u), Some(Z)) = (u_right, Z.rb()) {
1260 let (mut X, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1261 let mut X = X.as_mat_mut();
1262 if const { T::IS_REAL } {
1263 evd_from_real_schur_imp::<T::Real>(
1264 unsafe { core::mem::transmute(H) },
1265 unsafe { core::mem::transmute(X.rb_mut()) },
1266 par,
1267 params.evd_from_schur,
1268 );
1269 } else {
1270 evd_from_cplx_schur_imp::<T>(
1271 H,
1272 Conj::No,
1273 X.rb_mut(),
1274 par,
1275 params.evd_from_schur,
1276 );
1277 }
1278 linalg::matmul::triangular::matmul(
1279 u.rb_mut(),
1280 BlockStructure::Rectangular,
1281 Accum::Replace,
1282 Z,
1283 BlockStructure::Rectangular,
1284 X.rb(),
1285 BlockStructure::TriangularUpper,
1286 one(),
1287 par,
1288 );
1289 normalize_eigenvectors(u.rb_mut(), H);
1290 if !eigenvectors_ok(u.rb()) {
1291 return Err(EvdError::NoConvergence);
1292 }
1293 }
1294 if let (Some(mut u), Some(Z)) = (u_left, Z.rb()) {
1295 let (mut X, _) = unsafe { temp_mat_uninit::<T, _, _>(n, n, stack) };
1296 let mut X = X.as_mat_mut().reverse_rows_mut();
1297 if const { T::IS_REAL } {
1298 evd_from_real_schur_imp::<T::Real>(
1299 unsafe {
1300 core::mem::transmute(H.transpose().reverse_rows_and_cols())
1301 },
1302 unsafe { core::mem::transmute(X.rb_mut()) },
1303 par,
1304 params.evd_from_schur,
1305 );
1306 } else {
1307 evd_from_cplx_schur_imp::<T>(
1308 H.transpose().reverse_rows_and_cols(),
1309 Conj::Yes,
1310 X.rb_mut(),
1311 par,
1312 params.evd_from_schur,
1313 );
1314 }
1315 linalg::matmul::triangular::matmul(
1316 u.rb_mut(),
1317 BlockStructure::Rectangular,
1318 Accum::Replace,
1319 Z,
1320 BlockStructure::Rectangular,
1321 X.rb().reverse_rows_and_cols(),
1322 BlockStructure::TriangularLower,
1323 one(),
1324 par,
1325 );
1326 normalize_eigenvectors(u.rb_mut(), H);
1327 if !eigenvectors_ok(u.rb()) {
1328 return Err(EvdError::NoConvergence);
1329 }
1330 }
1331 Ok(())
1332}
1333#[track_caller]
1342pub fn evd_cplx<T: RealField>(
1343 A: MatRef<'_, Complex<T>>,
1344 s: DiagMut<'_, Complex<T>>,
1345 u_left: Option<MatMut<'_, Complex<T>>>,
1346 u_right: Option<MatMut<'_, Complex<T>>>,
1347 par: Par,
1348 stack: &mut MemStack,
1349 params: Spec<EvdParams, Complex<T>>,
1350) -> Result<(), EvdError> {
1351 let n = A.nrows();
1352 assert!(all(A.nrows() == n, A.ncols() == n, s.dim() == n));
1353 if let Some(u) = u_left.rb() {
1354 assert!(all(u.nrows() == n, u.ncols() == n));
1355 }
1356 if let Some(u) = u_right.rb() {
1357 assert!(all(u.nrows() == n, u.ncols() == n));
1358 }
1359 evd_imp(
1360 A,
1361 s.column_vector_mut(),
1362 None,
1363 u_left,
1364 u_right,
1365 par,
1366 stack,
1367 params.config,
1368 )
1369}
1370#[track_caller]
1382pub fn evd_real<T: RealField>(
1383 A: MatRef<'_, T>,
1384 s_re: DiagMut<'_, T>,
1385 s_im: DiagMut<'_, T>,
1386 u_left: Option<MatMut<'_, T>>,
1387 u_right: Option<MatMut<'_, T>>,
1388 par: Par,
1389 stack: &mut MemStack,
1390 params: Spec<EvdParams, T>,
1391) -> Result<(), EvdError> {
1392 let n = A.nrows();
1393 assert!(all(
1394 A.nrows() == n,
1395 A.ncols() == n,
1396 s_re.dim() == n,
1397 s_im.dim() == n
1398 ));
1399 if let Some(u) = u_left.rb() {
1400 assert!(all(u.nrows() == n, u.ncols() == n));
1401 }
1402 if let Some(u) = u_right.rb() {
1403 assert!(all(u.nrows() == n, u.ncols() == n));
1404 }
1405 evd_imp(
1406 A,
1407 s_re.column_vector_mut(),
1408 Some(s_im.column_vector_mut()),
1409 u_left,
1410 u_right,
1411 par,
1412 stack,
1413 params.config,
1414 )
1415}
1416#[cfg(test)]
1417mod general_tests {
1418 use super::*;
1419 use crate::assert;
1420 use crate::stats::prelude::*;
1421 use crate::utils::approx::*;
1422 use dyn_stack::MemBuffer;
1423 fn test_cplx_evd(mat: MatRef<'_, c64>) {
1424 let n = mat.nrows();
1425 let params = Spec::new(EvdParams {
1426 hessenberg: auto!(c64),
1427 schur: auto!(c64),
1428 evd_from_schur: EvdFromSchurParams {
1429 recursion_threshold: 8,
1430 ..auto!(c64)
1431 },
1432 ..auto!(c64)
1433 });
1434 let approx_eq = CwiseMat(ApproxEq::eps() * (8.0 * n as f64).sqrt());
1435 let mut s = Mat::zeros(n, n);
1436 {
1437 let mut ul = Mat::zeros(n, n);
1438 let mut ur = Mat::zeros(n, n);
1439 evd_cplx(
1440 mat.as_ref(),
1441 s.as_mut().diagonal_mut(),
1442 Some(ul.as_mut()),
1443 Some(ur.as_mut()),
1444 Par::Seq,
1445 MemStack::new(&mut MemBuffer::new(evd_scratch::<c64>(
1446 n,
1447 ComputeEigenvectors::Yes,
1448 ComputeEigenvectors::Yes,
1449 Par::Seq,
1450 params,
1451 ))),
1452 params,
1453 )
1454 .unwrap();
1455 assert!(& ur * & s ~ mat * & ur);
1456 assert!(& s * ul.adjoint() ~ ul.adjoint() * mat);
1457 }
1458 }
1459 fn test_real_evd(mat: MatRef<'_, f64>) {
1460 let n = mat.nrows();
1461 let params = Spec::new(EvdParams {
1462 hessenberg: auto!(f64),
1463 schur: auto!(f64),
1464 evd_from_schur: EvdFromSchurParams {
1465 recursion_threshold: 8,
1466 ..auto!(f64)
1467 },
1468 ..auto!(f64)
1469 });
1470 let approx_eq =
1471 CwiseMat(ApproxEq::<f64>::eps() * (8.0 * n as f64).sqrt());
1472 let mut s_re = Diag::zeros(n);
1473 let mut s_im = Diag::zeros(n);
1474 {
1475 let mut ul = Mat::zeros(n, n);
1476 let mut ur = Mat::zeros(n, n);
1477 evd_real(
1478 mat.as_ref(),
1479 s_re.as_mut(),
1480 s_im.as_mut(),
1481 Some(ul.as_mut()),
1482 Some(ur.as_mut()),
1483 Par::Seq,
1484 MemStack::new(&mut MemBuffer::new(evd_scratch::<f64>(
1485 n,
1486 ComputeEigenvectors::Yes,
1487 ComputeEigenvectors::Yes,
1488 Par::Seq,
1489 params,
1490 ))),
1491 params,
1492 )
1493 .unwrap();
1494 let mut i = 0;
1495 while i < n {
1496 if s_im[i] == 0.0 {
1497 let ur = ur.col(i);
1498 let ul = ul.col(i);
1499 let s = Scale(s_re[i]);
1500 assert!((& ur * s).as_mat() ~ (mat * & ur).as_mat());
1501 assert!(
1502 (& ul.adjoint() * s).as_mat() ~ (& ul.adjoint() * mat).as_mat()
1503 );
1504 i += 1;
1505 } else {
1506 let re = ur.col(i);
1507 let im = ur.col(i + 1);
1508 let ur = &Col::from_fn(n, |i| c64::new(re[i], im[i]));
1509 let re = ul.col(i);
1510 let im = ul.col(i + 1);
1511 let ul = &Col::from_fn(n, |i| c64::new(re[i], im[i]));
1512 let mat =
1513 &Mat::from_fn(n, n, |i, j| c64::from(mat[(i, j)]));
1514 let s = Scale(c64::new(s_re[i], s_im[i]));
1515 let approx_eq =
1516 CwiseMat(ApproxEq::eps() * (8.0 * n as f64).sqrt());
1517 assert!((ur * s).as_mat() ~ (mat * ur).as_mat());
1518 assert!((ul.adjoint() * s).as_mat() ~ (ul.adjoint() * mat).as_mat());
1519 i += 2;
1520 }
1521 }
1522 }
1523 }
1524 #[test]
1525 fn test_cplx() {
1526 let rng = &mut StdRng::seed_from_u64(1);
1527 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
1528 let mat = CwiseMatDistribution {
1529 nrows: n,
1530 ncols: n,
1531 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
1532 }
1533 .rand::<Mat<c64>>(rng);
1534 test_cplx_evd(mat.as_ref());
1535 }
1536 }
1537 #[test]
1538 fn test_real() {
1539 let rng = &mut StdRng::seed_from_u64(0);
1540 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
1541 let mat = CwiseMatDistribution {
1542 nrows: n,
1543 ncols: n,
1544 dist: StandardNormal,
1545 }
1546 .rand::<Mat<f64>>(rng);
1547 test_real_evd(mat.as_ref());
1548 }
1549 }
1550}
1551#[cfg(test)]
1552mod eig_2131_tests {
1553 use super::*;
1559 use crate::assert;
1560 use dyn_stack::MemBuffer;
1561
1562 fn relative_residual<T: RealField>(
1565 mat: MatRef<'_, T>,
1566 v: ColRef<'_, T>,
1567 lambda: T,
1568 ) -> T {
1569 let n = mat.nrows();
1570 let mut anorm = zero::<T>();
1571 for j in 0..n {
1572 let mut col = zero::<T>();
1573 for i in 0..n {
1574 col += mat[(i, j)].abs1();
1575 }
1576 if col > anorm {
1577 anorm = col;
1578 }
1579 }
1580 let mut vnorm2 = zero::<T>();
1581 let mut rnorm2 = zero::<T>();
1582 for i in 0..n {
1583 let mut acc = zero::<T>();
1584 for j in 0..n {
1585 acc += &mat[(i, j)] * &v[j];
1586 }
1587 acc -= &v[i] * λ
1588 rnorm2 += acc.abs2();
1589 vnorm2 += v[i].abs2();
1590 }
1591 rnorm2.sqrt() / (anorm * vnorm2.sqrt())
1592 }
1593
1594 fn evd_real_vectors<T: RealField>(
1595 mat: MatRef<'_, T>,
1596 params: Spec<EvdParams, T>,
1597 ) -> (Diag<T>, Diag<T>, Mat<T>, Mat<T>) {
1598 let n = mat.nrows();
1599 let mut s_re = Diag::zeros(n);
1600 let mut s_im = Diag::zeros(n);
1601 let mut u = Mat::zeros(n, n);
1602 let mut ul = Mat::zeros(n, n);
1603 let mut mem = MemBuffer::new(evd_scratch::<T>(
1604 n,
1605 ComputeEigenvectors::Yes,
1606 ComputeEigenvectors::Yes,
1607 Par::Seq,
1608 params,
1609 ));
1610 evd_real(
1611 mat,
1612 s_re.as_mut(),
1613 s_im.as_mut(),
1614 Some(ul.as_mut()),
1615 Some(u.as_mut()),
1616 Par::Seq,
1617 MemStack::new(&mut mem),
1618 params,
1619 )
1620 .unwrap();
1621 (s_re, s_im, u, ul)
1622 }
1623
1624 fn jordan<T: RealField>(n: usize) -> Mat<T> {
1626 let two = from_f64::<T>(2.0);
1627 let one = one::<T>();
1628 Mat::<T>::from_fn(n, n, |i, j| {
1629 if i == j {
1630 two.copy()
1631 } else if j == i + 1 {
1632 one.copy()
1633 } else {
1634 zero::<T>()
1635 }
1636 })
1637 }
1638
1639 fn check_jordan<T: RealField>(n: usize, threshold: usize, tol: f64) {
1640 let params = Spec::new(EvdParams {
1641 evd_from_schur: EvdFromSchurParams {
1642 recursion_threshold: threshold,
1643 ..auto!(T)
1644 },
1645 ..auto!(T)
1646 });
1647 let mat = jordan::<T>(n);
1648 let (s_re, _, u, _) = evd_real_vectors(mat.as_ref(), params);
1649 let tol = from_f64::<T>(tol);
1650 for j in 0..n {
1651 let v = u.col(j);
1652 assert!(v.iter().all(|x| x.is_finite()));
1653 assert!((v.norm_l2() - one::<T>()).abs() < from_f64::<T>(1e-5));
1654 assert!(relative_residual(mat.as_ref(), v, s_re[j].copy()) < tol);
1655 }
1656 }
1657
1658 #[test]
1659 fn jordan_eigenvectors_are_finite_and_unit_norm() {
1660 for threshold in [3usize, 64] {
1665 for n in [2, 4, 8, 20, 26, 30, 64, 65, 100] {
1666 check_jordan::<f64>(n, threshold, 1e-10);
1667 }
1668 for n in [2, 4, 8, 16, 30] {
1669 check_jordan::<f32>(n, threshold, 1e-3);
1670 }
1671 }
1672 }
1673
1674 #[test]
1675 fn conjugate_pair_eigenvector_has_unit_norm() {
1676 fn run<T: RealField>(threshold: usize) {
1680 let n = 6;
1681 let mat = Mat::<T>::from_fn(n, n, |i, j| {
1682 if i == j {
1683 if i >= 4 {
1684 zero::<T>()
1685 } else {
1686 from_f64::<T>((i + 1) as f64)
1687 }
1688 } else if i == 4 && j == 5 {
1689 one::<T>()
1690 } else if i == 5 && j == 4 {
1691 -one::<T>()
1692 } else if i + 2 <= j {
1693 from_f64::<T>(0.25)
1694 } else {
1695 zero::<T>()
1696 }
1697 });
1698 let params = Spec::new(EvdParams {
1699 evd_from_schur: EvdFromSchurParams {
1700 recursion_threshold: threshold,
1701 ..auto!(T)
1702 },
1703 ..auto!(T)
1704 });
1705 let (s_re, s_im, u, ul) = evd_real_vectors(mat.as_ref(), params);
1706 let tol = from_f64::<T>(1e-5);
1707 let mut j = 0;
1709 while j < n {
1710 let v = u.col(j);
1711 let vl = ul.col(j);
1712 if s_im[j] == zero::<T>() {
1713 assert!((v.norm_l2() - one::<T>()).abs() < tol);
1714 assert!((vl.norm_l2() - one::<T>()).abs() < tol);
1715 assert!(
1716 relative_residual(mat.as_ref(), v, s_re[j].copy())
1717 < from_f64::<T>(1e-4)
1718 );
1719 j += 1;
1720 } else {
1721 let nrm = v.norm_l2().hypot(&u.col(j + 1).norm_l2());
1722 assert!((nrm - one::<T>()).abs() < tol);
1723 let nrm = vl.norm_l2().hypot(&ul.col(j + 1).norm_l2());
1724 assert!((nrm - one::<T>()).abs() < tol);
1725 j += 2;
1726 }
1727 }
1728 }
1729 for threshold in [4usize, 64] {
1730 run::<f64>(threshold);
1731 run::<f32>(threshold);
1732 }
1733 }
1734
1735 #[test]
1736 fn complex_eigenvectors_have_unit_norm() {
1737 fn run<T: RealField>(threshold: usize) {
1739 let n = 6;
1740 let mat = Mat::<Complex<T>>::from_fn(n, n, |i, j| {
1741 Complex::new(
1742 from_f64::<T>(i as f64)
1743 + from_f64::<T>(j as f64) * from_f64::<T>(0.1),
1744 from_f64::<T>(i as f64 - j as f64) * from_f64::<T>(0.3),
1745 )
1746 });
1747 let params = Spec::new(EvdParams {
1748 evd_from_schur: EvdFromSchurParams {
1749 recursion_threshold: threshold,
1750 ..auto!(Complex<T>)
1751 },
1752 ..auto!(Complex<T>)
1753 });
1754 let mut s = Diag::zeros(n);
1755 let mut u = Mat::zeros(n, n);
1756 let mut mem = MemBuffer::new(evd_scratch::<Complex<T>>(
1757 n,
1758 ComputeEigenvectors::No,
1759 ComputeEigenvectors::Yes,
1760 Par::Seq,
1761 params,
1762 ));
1763 evd_cplx(
1764 mat.as_ref(),
1765 s.as_mut(),
1766 None,
1767 Some(u.as_mut()),
1768 Par::Seq,
1769 MemStack::new(&mut mem),
1770 params,
1771 )
1772 .unwrap();
1773 let tol = from_f64::<T>(1e-5);
1774 for j in 0..n {
1775 assert!((u.col(j).norm_l2() - one::<T>()).abs() < tol);
1776 }
1777 }
1778 for threshold in [4usize, 64] {
1779 run::<f64>(threshold);
1780 run::<f32>(threshold);
1781 }
1782 }
1783}
1784#[cfg(test)]
1785mod self_adjoint_tests {
1786 use super::*;
1787 use crate::assert;
1788 use crate::stats::prelude::*;
1789 use crate::utils::approx::*;
1790 use dyn_stack::MemBuffer;
1791 fn test_self_adjoint_evd<T: ComplexField>(mat: MatRef<'_, T>) {
1792 let n = mat.nrows();
1793 let params = Spec::new(SelfAdjointEvdParams {
1794 recursion_threshold: 8,
1795 ..auto!(T)
1796 });
1797 let approx_eq = CwiseMat(
1798 ApproxEq::<T::Real>::eps()
1799 * from_f64::<T::Real>(8.0 * n as f64).sqrt(),
1800 );
1801 let mut s = Mat::zeros(n, n);
1802 {
1803 let mut u = Mat::zeros(n, n);
1804 self_adjoint_evd(
1805 mat.as_ref(),
1806 s.as_mut().diagonal_mut(),
1807 Some(u.as_mut()),
1808 Par::Seq,
1809 MemStack::new(&mut MemBuffer::new(
1810 self_adjoint_evd_scratch::<T>(
1811 n,
1812 ComputeEigenvectors::Yes,
1813 Par::Seq,
1814 params,
1815 ),
1816 )),
1817 params,
1818 )
1819 .unwrap();
1820 let reconstructed = &u * &s * u.adjoint();
1821 assert!(reconstructed ~ mat);
1822 }
1823 {
1824 let mut s2 = Mat::zeros(n, n);
1825 self_adjoint_evd(
1826 mat.as_ref(),
1827 s2.as_mut().diagonal_mut(),
1828 None,
1829 Par::Seq,
1830 MemStack::new(&mut MemBuffer::new(
1831 self_adjoint_evd_scratch::<T>(
1832 n,
1833 ComputeEigenvectors::No,
1834 Par::Seq,
1835 params,
1836 ),
1837 )),
1838 params,
1839 )
1840 .unwrap();
1841 assert!(s2 ~ s);
1842 }
1843 }
1844 #[test]
1845 fn test_real() {
1846 let rng = &mut StdRng::seed_from_u64(1);
1847 for n in [1, 2, 4, 10, 15, 20, 50, 100, 150] {
1848 let mat = CwiseMatDistribution {
1849 nrows: n,
1850 ncols: n,
1851 dist: StandardNormal,
1852 }
1853 .rand::<Mat<f64>>(rng);
1854 let mat = &mat + mat.adjoint();
1855 test_self_adjoint_evd(mat.as_ref());
1856 }
1857 }
1858 #[test]
1859 fn test_cplx() {
1860 let rng = &mut StdRng::seed_from_u64(1);
1861 for n in [2, 4, 10, 15, 20, 50, 100, 150] {
1862 let mat = CwiseMatDistribution {
1863 nrows: n,
1864 ncols: n,
1865 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
1866 }
1867 .rand::<Mat<c64>>(rng);
1868 let mat = &mat + mat.adjoint();
1869 test_self_adjoint_evd(mat.as_ref());
1870 }
1871 }
1872 #[test]
1873 fn test_special() {
1874 for n in [1, 2, 4, 10, 15, 20, 50, 100, 150] {
1875 test_self_adjoint_evd(Mat::full(n, n, 0.0).as_ref());
1876 test_self_adjoint_evd(Mat::full(n, n, c64::ZERO).as_ref());
1877 test_self_adjoint_evd(Mat::full(n, n, 1.0).as_ref());
1878 test_self_adjoint_evd(Mat::full(n, n, c64::ONE).as_ref());
1879 test_self_adjoint_evd(Mat::<f64>::identity(n, n).as_ref());
1880 test_self_adjoint_evd(Mat::<c64>::identity(n, n).as_ref());
1881 }
1882 }
1883 #[test]
1884 fn test_pinv() {
1885 let rng = &mut StdRng::seed_from_u64(0);
1886 let n = 36;
1887 let mat = CwiseMatDistribution {
1888 nrows: n,
1889 ncols: n,
1890 dist: StandardNormal,
1891 }
1892 .rand::<Mat<f64>>(rng);
1893 let mat = &mat + mat.adjoint();
1894 let pinv = mat.self_adjoint_eigen(Side::Lower).unwrap().pseudoinverse();
1895 let err = &mat * &pinv - Mat::<f64>::identity(n, n);
1896 assert!(err.norm_max() < 1e-10);
1897 }
1898}