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