1use crate::internal_prelude::*;
20use complex::Complex;
21use equator::assert;
22pub use linalg::evd::ComputeEigenvectors;
23#[derive(Copy, Clone, Debug, PartialEq, Eq)]
25pub enum GevdError {
26 NoConvergence,
28}
29#[derive(Copy, Clone, Debug, PartialEq, Eq)]
31pub enum SelfAdjointGevdError {
32 NoConvergence,
34 NonPositiveDefinite,
36}
37#[derive(Clone, Copy, Debug)]
39pub struct GeneralizedSchurParams {
40 pub relative_cost_estimate_of_shift_chase_to_matmul: extern "C" fn(
43 matrix_dimension: usize,
44 active_block_dimension: usize,
45 )
46 -> usize,
47 pub recommended_shift_count: extern "C" fn(
50 matrix_dimension: usize,
51 active_block_dimension: usize,
52 ) -> usize,
53 pub recommended_deflation_window: extern "C" fn(
56 matrix_dimension: usize,
57 active_block_dimension: usize,
58 ) -> usize,
59 pub blocking_threshold: usize,
61 pub nibble_threshold: usize,
64 #[doc(hidden)]
65 pub non_exhaustive: NonExhaustive,
66}
67#[derive(Clone, Copy, Debug)]
69pub struct GevdFromSchurParams {
70 #[doc(hidden)]
71 pub non_exhaustive: NonExhaustive,
72}
73#[derive(Clone, Copy, Debug)]
75pub struct GevdParams {
76 pub hessenberg: gen_hessenberg::GeneralizedHessenbergParams,
78 pub schur: GeneralizedSchurParams,
80 pub evd_from_schur: GevdFromSchurParams,
82 #[doc(hidden)]
83 pub non_exhaustive: NonExhaustive,
84}
85extern "C" fn default_relative_cost_estimate_of_shift_chase_to_matmul(
86 n: usize,
87 nh: usize,
88) -> usize {
89 _ = (n, nh);
90 10
91}
92impl<T: ComplexField> Auto<T> for GeneralizedSchurParams {
93 fn auto() -> Self {
94 let schur: linalg::evd::SchurParams = auto!(T);
95 Self {
96 relative_cost_estimate_of_shift_chase_to_matmul:
97 default_relative_cost_estimate_of_shift_chase_to_matmul,
98 recommended_shift_count: schur.recommended_shift_count,
99 recommended_deflation_window: schur.recommended_deflation_window,
100 blocking_threshold: schur.blocking_threshold,
101 nibble_threshold: schur.nibble_threshold,
102 non_exhaustive: NonExhaustive(()),
103 }
104 }
105}
106impl<T: ComplexField> Auto<T> for GevdParams {
107 fn auto() -> Self {
108 Self {
109 hessenberg: auto!(T),
110 schur: auto!(T),
111 evd_from_schur: auto!(T),
112 non_exhaustive: NonExhaustive(()),
113 }
114 }
115}
116impl<T: ComplexField> Auto<T> for GevdFromSchurParams {
117 fn auto() -> Self {
118 Self {
119 non_exhaustive: NonExhaustive(()),
120 }
121 }
122}
123pub mod gen_hessenberg;
125pub mod qz_cplx;
127pub mod qz_real;
129#[track_caller]
130fn compute_gevd_generic<T: ComplexField>(
131 A: MatMut<'_, T>,
132 B: MatMut<'_, T>,
133 alpha_re: ColMut<'_, T>,
134 alpha_im: ColMut<'_, T>,
135 beta: ColMut<'_, T>,
136 u_left: Option<MatMut<'_, T>>,
137 u_right: Option<MatMut<'_, T>>,
138 mut par: Par,
139 stack: &mut MemStack,
140 params: GevdParams,
141 hessenberg_to_qz: fn(
142 A: MatMut<'_, T>,
143 B: MatMut<'_, T>,
144 Q: Option<MatMut<'_, T>>,
145 Z: Option<MatMut<'_, T>>,
146 alphar: ColMut<'_, T>,
147 alphai: ColMut<'_, T>,
148 beta: ColMut<'_, T>,
149 eigenvectors: ComputeEigenvectors,
150 par: Par,
151 params: GeneralizedSchurParams,
152 stack: &mut MemStack,
153 ),
154 qz_to_gevd: fn(
155 A: MatRef<'_, T>,
156 B: MatRef<'_, T>,
157 Q: Option<MatMut<'_, T>>,
158 Z: Option<MatMut<'_, T>>,
159 par: Par,
160 stack: &mut MemStack,
161 ),
162) -> Result<(), GevdError> {
163 let n = A.nrows();
164 assert!(all(
165 A.nrows() == n,
166 A.ncols() == n,
167 B.nrows() == n,
168 B.ncols() == n,
169 alpha_re.nrows() == n,
170 if const { T::IS_REAL } {
171 alpha_im.nrows()
172 } else {
173 n
174 } == n,
175 beta.nrows() == n,
176 ));
177 if let Some(u_left) = u_left.rb() {
178 assert!(all(u_left.nrows() == n, u_left.ncols() == n));
179 }
180 if let Some(u_right) = u_right.rb() {
181 assert!(all(u_right.nrows() == n, u_right.ncols() == n));
182 }
183 if n == 0 {
184 return Ok(());
185 }
186 #[cfg(feature = "perf-warn")]
187 {
188 for u in [u_left.rb(), u_right.rb()] {
189 if let Some(matrix) = u.rb() {
190 if matrix.row_stride().unsigned_abs() != 1
191 && crate::__perf_warn!(GEVD_WARN)
192 {
193 if matrix.col_stride().unsigned_abs() == 1 {
194 log::warn!(
195 target : "faer_perf",
196 "GEVD prefers column-major eigenvector matrix. Found row-major matrix."
197 );
198 } else {
199 log::warn!(
200 target : "faer_perf",
201 "GEVD prefers column-major eigenvector matrix. Found matrix with generic strides."
202 );
203 }
204 }
205 }
206 }
207 for M in [A.rb(), B.rb()] {
208 if M.row_stride().unsigned_abs() != 1
209 && crate::__perf_warn!(GEVD_WARN)
210 {
211 if M.col_stride().unsigned_abs() == 1 {
212 log::warn!(
213 target : "faer_perf",
214 "GEVD prefers column-major input matrix. Found row-major matrix."
215 );
216 } else {
217 log::warn!(
218 target : "faer_perf",
219 "GEVD prefers column-major input matrix. Found matrix with generic strides."
220 );
221 }
222 }
223 }
224 }
225 if !(A.is_all_finite() && B.is_all_finite()) {
226 { alpha_re }.fill(nan());
227 { alpha_im }.fill(nan());
228 { beta }.fill(nan());
229 for u in [u_left, u_right] {
230 if let Some(mut u) = u {
231 u.fill(nan());
232 }
233 }
234 return Err(GevdError::NoConvergence);
235 }
236 let need_qz = u_left.is_some() || u_right.is_some();
237 let mut A = A;
238 let mut B = B;
239 let mut u_left = u_left;
240 let mut u_right = u_right;
241 let mut alpha_re = alpha_re;
242 let mut alpha_im = alpha_im;
243 let mut beta = beta;
244 for u in [u_left.rb_mut(), u_right.rb_mut()] {
245 if let Some(mut u) = u {
246 u.fill(zero());
250 u.diagonal_mut().column_vector_mut().fill(one());
251 }
252 }
253 {
254 let block_size =
255 linalg::qr::no_pivoting::factor::recommended_block_size::<T>(n, n);
256 let (mut householder, stack) =
257 unsafe { linalg::temp_mat_uninit::<T, _, _>(block_size, n, stack) };
258 let mut householder = householder.as_mat_mut();
259 linalg::qr::no_pivoting::factor::qr_in_place(
260 B.rb_mut(),
261 householder.rb_mut(),
262 par.reborrow(),
263 stack,
264 default(),
265 );
266 linalg::householder::apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
267 B.rb(),
268 householder.rb(),
269 Conj::Yes,
270 A.rb_mut(),
271 par.reborrow(),
272 stack,
273 );
274 if let Some(u_left) = u_left.rb_mut() {
275 linalg::householder::apply_block_householder_sequence_on_the_left_in_place_with_conj(
276 B.rb(),
277 householder.rb(),
278 Conj::No,
279 u_left,
280 par.reborrow(),
281 stack,
282 );
283 }
284 zip!(B.rb_mut())
285 .for_each_triangular_lower(linalg::zip::Diag::Skip, |unzip!(x)| {
286 *x = zero()
287 });
288 }
289 gen_hessenberg::generalized_hessenberg(
290 A.rb_mut(),
291 B.rb_mut(),
292 u_left.rb_mut(),
293 u_right.rb_mut(),
294 par.reborrow(),
295 stack,
296 params.hessenberg,
297 );
298 hessenberg_to_qz(
299 A.rb_mut(),
300 B.rb_mut(),
301 u_left.rb_mut(),
302 u_right.rb_mut(),
303 alpha_re.rb_mut(),
304 alpha_im.rb_mut(),
305 beta.rb_mut(),
306 if need_qz {
307 ComputeEigenvectors::Yes
308 } else {
309 ComputeEigenvectors::No
310 },
311 par.reborrow(),
312 params.schur,
313 stack,
314 );
315 qz_to_gevd(
316 A.rb(),
317 B.rb(),
318 u_left.rb_mut(),
319 u_right.rb_mut(),
320 par,
321 stack,
322 );
323 Ok(())
324}
325fn solve_shifted_1x1<T: ComplexField>(
326 smin: T::Real,
327 ca: T::Real,
328 A: T,
329 d0: T,
330 B: &mut T,
331 w: T,
332) {
333 let ref safmin = min_positive::<T::Real>();
334 let ref smlnum = safmin + safmin;
335 let ref smin = smin.fmax(smlnum);
336 let CR = A.mul_real(ca) - w * d0;
337 let cmax = CR.abs();
338 if cmax < *smin {
339 let smin_inv = smin.recip();
340 *B = B.mul_real(smin_inv);
341 }
342 let C = CR.recip();
343 *B *= C;
344}
345fn solve_complex_shifted_1x1<T: RealField>(
346 smin: T,
347 ca: T,
348 A: MatRef<'_, T>,
349 ref d0: T,
350 mut B: MatMut<'_, T>,
351 wr: T,
352 wi: T,
353) {
354 let nw = B.ncols();
355 let ref safmin = min_positive::<T>();
356 let ref smlnum = safmin + safmin;
357 let ref smin = smin.fmax(smlnum);
358 let CR = ca * &A[(0, 0)] - wr * d0;
359 let CI = -wi * d0;
360 let cmax = CR.abs().fmax(CI.abs());
361 if cmax < *smin {
362 let ref smin_inv = smin.recip();
363 zip!(B.rb_mut()).for_each(|unzip!(x)| *x *= smin_inv);
364 }
365 if nw == 1 {
366 let ref C = CR.recip();
367 zip!(B.rb_mut()).for_each(|unzip!(x)| *x *= C);
368 } else {
369 let (Br, Bi) = B.two_cols_mut(0, 1);
370 let C = Complex { re: CR, im: CI }.recip();
371 zip!(Br, Bi).for_each(|unzip!(re, im)| {
372 (*re, *im) =
373 (&*re * &C.re - &*im * &C.im, &*re * &C.im + &*im * &C.re);
374 });
375 }
376}
377fn solve_complex_shifted_2x2<T: RealField>(
378 smin: T,
379 ref ca: T,
380 A: MatRef<'_, T>,
381 ref d0: T,
382 ref d1: T,
383 mut B: MatMut<'_, T>,
384 ref wr: T,
385 ref wi: T,
386 stack: &mut MemStack,
387) {
388 let nw = B.ncols();
389 let zero = zero::<T>;
390 let ref safmin = min_positive::<T>();
391 let ref smlnum = safmin + safmin;
392 let ref smin = smin.fmax(smlnum);
393 if nw == 1 {
394 let (mut C, stack) =
395 unsafe { linalg::temp_mat_uninit::<T, _, _>(2, 2, stack) };
396 let mut C = C.as_mat_mut();
397 let mut row_perm_fwd = [0usize; 2];
398 let mut row_perm_inv = [0usize; 2];
399 let mut col_perm_fwd = [0usize; 2];
400 let mut col_perm_inv = [0usize; 2];
401 C[(0, 0)] = ca * &A[(0, 0)] - wr * d0;
402 C[(1, 0)] = ca * &A[(1, 0)];
403 C[(0, 1)] = ca * &A[(0, 1)];
404 C[(1, 1)] = ca * &A[(1, 1)] - wr * d1;
405 let cmax = C.norm_max();
406 if cmax < *smin {
407 let ref smin_inv = smin.recip();
408 zip!(B.rb_mut()).for_each(|unzip!(x)| *x *= smin_inv);
409 }
410 let (_, row_perm, col_perm) =
411 linalg::lu::full_pivoting::factor::lu_in_place(
412 C.rb_mut(),
413 &mut row_perm_fwd,
414 &mut row_perm_inv,
415 &mut col_perm_fwd,
416 &mut col_perm_inv,
417 Par::Seq,
418 stack,
419 Default::default(),
420 );
421 linalg::lu::full_pivoting::solve::solve_in_place(
422 C.rb(),
423 C.rb(),
424 row_perm,
425 col_perm,
426 B.rb_mut(),
427 Par::Seq,
428 stack,
429 );
430 } else {
431 let (mut C, stack) =
432 unsafe { linalg::temp_mat_uninit::<Complex<T>, _, _>(2, 2, stack) };
433 let mut C = C.as_mat_mut();
434 let mut row_perm_fwd = [0usize; 2];
435 let mut row_perm_inv = [0usize; 2];
436 let mut col_perm_fwd = [0usize; 2];
437 let mut col_perm_inv = [0usize; 2];
438 C[(0, 0)] = Complex {
439 re: ca * &A[(0, 0)] - wr * d0,
440 im: -wi * d0,
441 };
442 C[(1, 0)] = Complex {
443 re: ca * &A[(1, 0)],
444 im: zero(),
445 };
446 C[(0, 1)] = Complex {
447 re: ca * &A[(0, 1)],
448 im: zero(),
449 };
450 C[(1, 1)] = Complex {
451 re: ca * &A[(1, 1)] - wr * d1,
452 im: -wi * d1,
453 };
454 let cmax = C.norm_max();
455 if cmax < *smin {
456 let ref smin_inv = smin.recip();
457 zip!(B.rb_mut()).for_each(|unzip!(x)| *x *= smin_inv);
458 }
459 let (_, row_perm, col_perm) =
460 linalg::lu::full_pivoting::factor::lu_in_place(
461 C.rb_mut(),
462 &mut row_perm_fwd,
463 &mut row_perm_inv,
464 &mut col_perm_fwd,
465 &mut col_perm_inv,
466 Par::Seq,
467 stack,
468 Default::default(),
469 );
470 let n = B.nrows();
471 let (Br, Bi) = B.two_cols_mut(0, 1);
472 let (mut B, stack) =
473 unsafe { linalg::temp_mat_uninit::<Complex<T>, _, _>(n, 1, stack) };
474 let mut B = B.as_mat_mut().col_mut(0);
475 zip!(&mut B, &Br, &Bi).for_each(|unzip!(z, re, im)| {
476 *z = Complex {
477 re: re.copy(),
478 im: im.copy(),
479 }
480 });
481 linalg::lu::full_pivoting::solve::solve_in_place(
482 C.rb(),
483 C.rb(),
484 row_perm,
485 col_perm,
486 B.rb_mut().as_mat_mut(),
487 Par::Seq,
488 stack,
489 );
490 zip!(Br, Bi, &B).for_each(|unzip!(re, im, z)| {
491 (*re, *im) = (z.re.copy(), z.im.copy())
492 });
493 }
494}
495fn qz_to_gevd_real<T: RealField>(
496 A: MatRef<'_, T>,
497 B: MatRef<'_, T>,
498 Q: Option<MatMut<'_, T>>,
499 Z: Option<MatMut<'_, T>>,
500 mut par: Par,
501 stack: &mut MemStack,
502) {
503 let n = A.nrows();
504 if n == 0 {
505 return;
506 }
507 let one = one::<T>;
508 let zero = zero::<T>;
509 let ref ulp = eps::<T>();
510 let ref safmin = min_positive::<T>();
511 let ref smallnum = safmin * from_f64::<T::Real>(n as f64);
512 let ref small = smallnum * ulp.recip();
513 let ref bignum = smallnum.recip();
514 let ref big = small.recip();
515 let (mut acolnorm, stack) = linalg::temp_mat_zeroed::<T, _, _>(n, 1, stack);
516 let acolnorm = acolnorm.as_mat_mut();
517 let (mut bcolnorm, stack) = linalg::temp_mat_zeroed::<T, _, _>(n, 1, stack);
518 let bcolnorm = bcolnorm.as_mat_mut();
519 let mut acolnorm = acolnorm.col_mut(0);
520 let mut bcolnorm = bcolnorm.col_mut(0);
521 let mut anorm = zero();
522 let mut bnorm = zero();
523 let mut j = 0;
524 while j < n {
525 let cplx = j + 1 < n && A[(j + 1, j)] != zero();
526 if !cplx {
527 let a = A.rb().col(j).get(..j).norm_l1();
528 acolnorm[j] = a.copy();
529 anorm = anorm.fmax(a + A[(j, j)].abs());
530 j += 1;
531 } else {
532 for jc in j..j + 2 {
533 let a = A.rb().col(jc).get(..j).norm_l1();
534 acolnorm[jc] = a.copy();
535 anorm = anorm.fmax(a + A[(j, jc)].abs() + A[(j + 1, jc)].abs());
536 }
537 j += 2;
538 }
539 }
540 for j in 0..n {
541 let b = B.rb().col(j).get(..j).norm_l1();
542 bcolnorm[j] = b.copy();
543 bnorm = bnorm.fmax(b + B[(j, j)].abs());
544 }
545 let ref anorm = anorm;
546 let ref bnorm = bnorm;
547 let ref ascale = anorm.fmax(safmin).recip();
548 let ref bscale = bnorm.fmax(safmin).recip();
549 if let Some(mut u) = Q {
550 let mut je = 0usize;
551 while je < n {
552 let cplx_eigval = je + 1 < n && A[(je + 1, je)] != zero();
553 let nw = if cplx_eigval { 2 } else { 1 };
554 if !cplx_eigval
555 && A[(je, je)].abs().fmax(&B[(je, je)].abs()) < *safmin
556 {
557 u.rb_mut().col_mut(je).fill(zero());
558 u[(je, je)] = one();
559 je += 1;
560 continue;
561 }
562 let mut acoef;
563 let mut acoefa;
564 let mut bcoefa;
565 let mut bcoefr;
566 let mut bcoefi;
567 let mut xmax;
568 let (mut rhs, stack) =
569 linalg::temp_mat_zeroed::<T, _, _>(n, nw, stack);
570 let mut rhs = rhs.as_mat_mut();
571 if !cplx_eigval {
572 let ref temp = safmin.fmax(
573 (A[(je, je)].abs() * ascale)
574 .fmax(B[(je, je)].abs() * bscale),
575 );
576 let ref salfar = A[(je, je)].mul_real(temp).mul_real(ascale);
577 let ref sbeta = B[(je, je)].real() * temp * bscale;
578 acoef = sbeta * ascale;
579 bcoefr = salfar * bscale;
580 bcoefi = zero();
581 let mut scale = one();
582 let lsa = sbeta.abs() >= *safmin && acoef.abs() < *small;
583 let lsb = salfar.abs() >= *safmin && bcoefr.abs() < *small;
584 if lsa {
585 scale = (small / sbeta.abs()) * anorm.fmin(big);
586 }
587 if lsb {
588 scale =
589 scale.fmax((small / salfar.abs()) * bnorm.fmin(big));
590 }
591 if lsa || lsb {
592 scale = scale.fmin(
593 one()
594 / (safmin
595 * one().fmax(acoef.abs()).fmax(bcoefr.abs())),
596 );
597 if lsa {
598 acoef = ascale * (&scale * sbeta)
599 } else {
600 acoef = &scale * acoef
601 }
602 if lsb {
603 bcoefr = bscale * (&scale * salfar)
604 } else {
605 bcoefr = &scale * bcoefr
606 }
607 }
608 acoefa = acoef.abs();
609 bcoefa = bcoefr.abs();
610 rhs[(je, 0)] = one();
611 xmax = one();
612 } else {
613 let (ref scale, _, wr, _, wi) = qz_real::generalized_eigval_2x2(
614 (
615 A[(je, je)].copy(),
616 A[(je, je + 1)].copy(),
617 A[(je + 1, je)].copy(),
618 A[(je + 1, je + 1)].copy(),
619 ),
620 (
621 B[(je, je)].copy(),
622 B[(je, je + 1)].copy(),
623 B[(je + 1, je)].copy(),
624 B[(je + 1, je + 1)].copy(),
625 ),
626 );
627 acoef = scale.copy();
628 bcoefr = wr;
629 bcoefi = wi;
630 acoefa = acoef.abs();
631 bcoefa = bcoefr.abs() + bcoefi.abs();
632 let mut scale = one();
633 if &acoefa * ulp < *safmin && acoefa >= *safmin {
634 scale = (safmin / ulp) / &acoefa;
635 }
636 if &bcoefa * ulp < *safmin && bcoefa >= *safmin {
637 scale = scale.fmax((safmin / ulp) / &bcoefa);
638 }
639 if safmin * &acoefa > *ascale {
640 scale = ascale / (safmin * &acoefa);
641 }
642 if safmin * &bcoefa > *bscale {
643 scale = scale.fmin(bscale / (safmin * &bcoefa));
644 }
645 if scale != one() {
646 acoef = &scale * &acoef;
647 acoefa = acoef.abs();
648 bcoefr = &scale * &bcoefr;
649 bcoefi = &scale * &bcoefi;
650 bcoefa = bcoefr.abs() + bcoefi.abs();
651 }
652 let ref temp = &acoef * &A[(je + 1, je)];
653 let temp2r = &acoef * &A[(je, je)] - &bcoefr * &B[(je, je)];
654 let temp2i = -&bcoefi * &B[(je, je)];
655 if temp.abs() > temp2r.abs() + temp2i.abs() {
656 rhs[(je, 0)] = one();
657 rhs[(je, 1)] = zero();
658 rhs[(je + 1, 0)] = -temp2r / temp;
659 rhs[(je + 1, 1)] = -temp2i / temp;
660 } else {
661 rhs[(je + 1, 0)] = one();
662 rhs[(je + 1, 1)] = zero();
663 let ref temp = &acoef * &A[(je, je + 1)];
664 rhs[(je, 0)] = (&bcoefr * &B[(je + 1, je + 1)]
665 - &acoef * &A[(je + 1, je + 1)])
666 / temp;
667 rhs[(je, 1)] = &bcoefi * &B[(je + 1, je + 1)] / temp;
668 }
669 xmax = (rhs[(je, 0)].abs() + rhs[(je, 1)].abs())
670 .fmax(rhs[(je + 1, 0)].abs() + rhs[(je + 1, 1)].abs());
671 }
672 let dmin = safmin
673 .fmax(ulp * &acoefa * anorm)
674 .fmax(ulp * &bcoefa * bnorm);
675 let mut j = je + nw;
676 while j < n {
677 let cplx = j + 1 < n && A[(j + 1, j)] != zero();
678 let na = if cplx { 2 } else { 1 };
679 let ref xscale = xmax.recip();
680 let mut temp = acolnorm[j]
681 .fmax(&bcolnorm[j])
682 .fmax(&acoefa * &acolnorm[j] + &bcoefa * &bcolnorm[j]);
683 let b0 = B[(j, j)].copy();
684 let b1;
685 if cplx {
686 temp = temp
687 .fmax(
688 &acoefa * &acolnorm[j + 1]
689 + &bcoefa * &bcolnorm[j + 1],
690 )
691 .fmax(&acolnorm[j + 1])
692 .fmax(&acolnorm[j + 1]);
693 b1 = B[(j + 1, j + 1)].copy();
694 } else {
695 b1 = zero();
696 }
697 if temp > bignum * xscale {
698 for jw in 0..nw {
699 for jr in je..j {
700 rhs[(jr, jw)] = xscale * &rhs[(jr, jw)];
701 }
702 }
703 xmax = xmax * xscale;
704 }
705 let (mut sums, stack) = unsafe {
706 linalg::temp_mat_uninit::<T, _, _>(na, nw, stack)
707 };
708 let mut sums = sums.as_mat_mut();
709 let (mut sump, stack) = unsafe {
710 linalg::temp_mat_uninit::<T, _, _>(na, nw, stack)
711 };
712 let mut sump = sump.as_mat_mut();
713 for jw in 0..nw {
714 for ja in 0..na {
715 sums[(ja, jw)] = zero();
716 sump[(ja, jw)] = zero();
717 for jr in je..j {
718 sums[(ja, jw)] = &sums[(ja, jw)]
719 + &A[(jr, j + ja)] * &rhs[(jr, jw)];
720 sump[(ja, jw)] = &sump[(ja, jw)]
721 + &B[(jr, j + ja)] * &rhs[(jr, jw)];
722 }
723 }
724 }
725 for ja in 0..na {
726 if cplx_eigval {
727 rhs[(j + ja, 0)] = -&acoef * &sums[(ja, 0)]
728 + &bcoefr * &sump[(ja, 0)]
729 - &bcoefi * &sump[(ja, 1)];
730 rhs[(j + ja, 1)] = -&acoef * &sums[(ja, 1)]
731 + &bcoefr * &sump[(ja, 1)]
732 + &bcoefi * &sump[(ja, 0)];
733 } else {
734 rhs[(j + ja, 0)] =
735 -&acoef * &sums[(ja, 0)] + &bcoefr * &sump[(ja, 0)];
736 }
737 }
738 if cplx {
739 solve_complex_shifted_2x2(
740 dmin.copy(),
741 acoef.copy(),
742 A.submatrix(j, j, na, na).transpose(),
743 b0,
744 b1,
745 rhs.rb_mut().subrows_mut(j, na),
746 bcoefr.copy(),
747 bcoefi.copy(),
748 stack,
749 );
750 } else {
751 solve_complex_shifted_1x1(
752 dmin.copy(),
753 acoef.copy(),
754 A.submatrix(j, j, na, na).transpose(),
755 b0,
756 rhs.rb_mut().subrows_mut(j, na),
757 bcoefr.copy(),
758 bcoefi.copy(),
759 );
760 }
761 j += na;
762 }
763 let (mut tmp, _) = linalg::temp_mat_zeroed::<T, _, _>(n, nw, stack);
764 let mut tmp = tmp.as_mat_mut();
765 linalg::matmul::matmul(
766 tmp.rb_mut(),
767 Accum::Replace,
768 u.rb().get(.., je..),
769 rhs.rb().get(je.., ..),
770 one(),
771 par.reborrow(),
772 );
773 let mut u = u.rb_mut().subcols_mut(je, nw);
774 u.copy_from(&tmp);
775 let ref scale = u.norm_l2().recip();
776 zip!(u).for_each(|unzip!(u)| {
777 *u *= scale;
778 });
779 je += nw;
780 }
781 }
782 if let Some(mut u) = Z {
783 let mut je = n;
784 while je > 0 {
785 je -= 1;
786 let cplx_eigval = je > 0 && A[(je, je - 1)] != zero();
787 let nw = if cplx_eigval { 2 } else { 1 };
788 je -= nw - 1;
789 if !cplx_eigval
790 && A[(je, je)].abs().fmax(&B[(je, je)].abs()) < *safmin
791 {
792 u.rb_mut().col_mut(je).fill(zero());
793 u[(je, je)] = one();
794 continue;
795 }
796 let mut acoef;
797 let mut acoefa;
798 let mut bcoefa;
799 let mut bcoefr;
800 let mut bcoefi;
801 let (mut rhs, stack) =
802 linalg::temp_mat_zeroed::<T, _, _>(n, nw, stack);
803 let mut rhs = rhs.as_mat_mut();
804 if !cplx_eigval {
805 let ref temp = safmin.fmax(
806 (A[(je, je)].abs() * ascale)
807 .fmax(B[(je, je)].abs() * bscale),
808 );
809 let ref salfar = A[(je, je)].mul_real(temp).mul_real(ascale);
810 let ref sbeta = B[(je, je)].real() * temp * bscale;
811 acoef = sbeta * ascale;
812 bcoefr = salfar * bscale;
813 bcoefi = zero();
814 let mut scale = one();
815 let lsa = sbeta.abs() >= *safmin && acoef.abs() < *small;
816 let lsb = salfar.abs() >= *safmin && bcoefr.abs() < *small;
817 if lsa {
818 scale = (small / sbeta.abs()) * anorm.fmin(big);
819 }
820 if lsb {
821 scale =
822 scale.fmax((small / salfar.abs()) * bnorm.fmin(big));
823 }
824 if lsa || lsb {
825 scale = scale.fmin(
826 one()
827 / (safmin
828 * one().fmax(acoef.abs()).fmax(bcoefr.abs())),
829 );
830 if lsa {
831 acoef = ascale * (&scale * sbeta)
832 } else {
833 acoef = &scale * &acoef
834 }
835 if lsb {
836 bcoefr = bscale * (&scale * salfar)
837 } else {
838 bcoefr = &scale * &bcoefr
839 }
840 }
841 acoefa = acoef.abs();
842 bcoefa = bcoefr.abs();
843 rhs[(je, 0)] = one();
844 for jr in 0..je {
845 rhs[(jr, 0)] =
846 &bcoefr * &B[(jr, je)] - &acoef * &A[(jr, je)];
847 }
848 } else {
849 let (scale, _, wr, _, wi) = qz_real::generalized_eigval_2x2(
850 (
851 A[(je, je)].copy(),
852 A[(je, je + 1)].copy(),
853 A[(je + 1, je)].copy(),
854 A[(je + 1, je + 1)].copy(),
855 ),
856 (
857 B[(je, je)].copy(),
858 B[(je, je + 1)].copy(),
859 B[(je + 1, je)].copy(),
860 B[(je + 1, je + 1)].copy(),
861 ),
862 );
863 acoef = scale;
864 bcoefr = wr;
865 bcoefi = wi;
866 acoefa = acoef.abs();
867 bcoefa = bcoefr.abs() + bcoefi.abs();
868 let mut scale = one();
869 if &acoefa * ulp < *safmin && acoefa >= *safmin {
870 scale = (safmin / ulp) / &acoefa;
871 }
872 if &bcoefa * ulp < *safmin && bcoefa >= *safmin {
873 scale = scale.fmax((safmin / ulp) / &bcoefa);
874 }
875 if safmin * &acoefa > *ascale {
876 scale = ascale / (safmin * &acoefa);
877 }
878 if safmin * &bcoefa > *bscale {
879 scale = scale.fmin(bscale / (safmin * &bcoefa));
880 }
881 if scale != one() {
882 acoef = &scale * &acoef;
883 acoefa = acoef.abs();
884 bcoefr = &scale * &bcoefr;
885 bcoefi = &scale * &bcoefi;
886 bcoefa = bcoefr.abs() + bcoefi.abs();
887 }
888 let ref temp = &acoef * &A[(je + 1, je)];
889 let ref temp2r = &acoef * &A[(je + 1, je + 1)]
890 - &bcoefr * &B[(je + 1, je + 1)];
891 let ref temp2i = -&bcoefi * &B[(je + 1, je + 1)];
892 if temp.abs() > temp2r.abs() + temp2i.abs() {
893 rhs[(je + 1, 0)] = one();
894 rhs[(je + 1, 1)] = zero();
895 rhs[(je, 0)] = -temp2r / temp;
896 rhs[(je, 1)] = -temp2i / temp;
897 } else {
898 rhs[(je, 0)] = one();
899 rhs[(je, 1)] = zero();
900 let ref temp = &acoef * &A[(je, je + 1)];
901 rhs[(je + 1, 0)] =
902 (&bcoefr * &B[(je, je)] - &acoef * &A[(je, je)]) / temp;
903 rhs[(je + 1, 1)] = &bcoefi * &B[(je, je)] / temp;
904 }
905 let ref creala = &acoef * &rhs[(je, 0)];
906 let ref cimaga = &acoef * &rhs[(je, 1)];
907 let ref crealb =
908 &bcoefr * &rhs[(je, 0)] - &bcoefi * &rhs[(je, 1)];
909 let ref cimagb =
910 &bcoefi * &rhs[(je, 0)] + &bcoefr * &rhs[(je, 1)];
911 let ref cre2a = &acoef * &rhs[(je + 1, 0)];
912 let ref cim2a = &acoef * &rhs[(je + 1, 1)];
913 let ref cre2b =
914 &bcoefr * &rhs[(je + 1, 0)] - &bcoefi * &rhs[(je + 1, 1)];
915 let ref cim2b =
916 &bcoefi * &rhs[(je + 1, 0)] + &bcoefr * &rhs[(je + 1, 1)];
917 for jr in 0..je {
918 rhs[(jr, 0)] = -creala * &A[(jr, je)]
919 + crealb * &B[(jr, je)]
920 - cre2a * &A[(jr, je + 1)]
921 + cre2b * &B[(jr, je + 1)];
922 rhs[(jr, 1)] = -cimaga * &A[(jr, je)]
923 + cimagb * &B[(jr, je)]
924 - cim2a * &A[(jr, je + 1)]
925 + cim2b * &B[(jr, je + 1)];
926 }
927 }
928 let dmin = safmin
929 .fmax(ulp * &acoefa * anorm)
930 .fmax(ulp * &bcoefa * bnorm);
931 let mut j = je;
932 while j > 0 {
933 j -= 1;
934 let cplx = j > 0 && A[(j, j - 1)] != zero();
935 let na = if cplx { 2 } else { 1 };
936 j -= na - 1;
937 let b0 = B[(j, j)].copy();
938 let b1;
939 if cplx {
940 b1 = B[(j + 1, j + 1)].copy();
941 } else {
942 b1 = zero();
943 }
944 if cplx {
945 solve_complex_shifted_2x2(
946 dmin.copy(),
947 acoef.copy(),
948 A.submatrix(j, j, na, na),
949 b0,
950 b1,
951 rhs.rb_mut().subrows_mut(j, na),
952 bcoefr.copy(),
953 bcoefi.copy(),
954 stack,
955 );
956 } else {
957 solve_complex_shifted_1x1(
958 dmin.copy(),
959 acoef.copy(),
960 A.submatrix(j, j, na, na),
961 b0,
962 rhs.rb_mut().subrows_mut(j, na),
963 bcoefr.copy(),
964 bcoefi.copy(),
965 );
966 }
967 for ja in 0..na {
968 if cplx_eigval {
969 let ref creala = &acoef * &rhs[(j + ja, 0)];
970 let ref cimaga = &acoef * &rhs[(j + ja, 1)];
971 let ref crealb = &bcoefr * &rhs[(j + ja, 0)]
972 - &bcoefi * &rhs[(j + ja, 1)];
973 let ref cimagb = &bcoefi * &rhs[(j + ja, 0)]
974 + &bcoefr * &rhs[(j + ja, 1)];
975 for jr in 0..j {
976 rhs[(jr, 0)] -= creala * &A[(jr, j + ja)]
977 - crealb * &B[(jr, j + ja)];
978 rhs[(jr, 1)] -= cimaga * &A[(jr, j + ja)]
979 - cimagb * &B[(jr, j + ja)];
980 }
981 } else {
982 let ref creala = &acoef * &rhs[(j + ja, 0)];
983 let ref crealb = &bcoefr * &rhs[(j + ja, 0)];
984 for jr in 0..j {
985 rhs[(jr, 0)] -= creala * &A[(jr, j + ja)]
986 - crealb * &B[(jr, j + ja)];
987 }
988 }
989 }
990 }
991 let (mut tmp, _) = linalg::temp_mat_zeroed::<T, _, _>(n, nw, stack);
992 let mut tmp = tmp.as_mat_mut();
993 linalg::matmul::matmul(
994 tmp.rb_mut(),
995 Accum::Replace,
996 u.rb().get(.., ..je + nw),
997 rhs.rb().get(..je + nw, ..),
998 one(),
999 par.reborrow(),
1000 );
1001 let mut u = u.rb_mut().subcols_mut(je, nw);
1002 u.copy_from(&tmp);
1003 let ref scale = u.norm_l2().recip();
1004 zip!(u).for_each(|unzip!(u)| {
1005 *u *= scale;
1006 });
1007 }
1008 }
1009}
1010fn qz_to_gevd_cplx<T: ComplexField>(
1011 A: MatRef<'_, T>,
1012 B: MatRef<'_, T>,
1013 Q: Option<MatMut<'_, T>>,
1014 Z: Option<MatMut<'_, T>>,
1015 mut par: Par,
1016 stack: &mut MemStack,
1017) {
1018 let n = A.nrows();
1019 if n == 0 {
1020 return;
1021 }
1022 let one = one::<T::Real>;
1023 let ref ulp = eps::<T::Real>();
1024 let ref safmin = min_positive::<T::Real>();
1025 let ref smallnum = safmin * from_f64::<T::Real>(n as f64);
1026 let ref small = smallnum * ulp.recip();
1027 let ref bignum = smallnum.recip();
1028 let ref big = small.recip();
1029 let (mut acolnorm, stack) =
1030 linalg::temp_mat_zeroed::<T::Real, _, _>(n, 1, stack);
1031 let acolnorm = acolnorm.as_mat_mut();
1032 let (mut bcolnorm, stack) =
1033 linalg::temp_mat_zeroed::<T::Real, _, _>(n, 1, stack);
1034 let bcolnorm = bcolnorm.as_mat_mut();
1035 let mut acolnorm = acolnorm.col_mut(0);
1036 let mut bcolnorm = bcolnorm.col_mut(0);
1037 let mut anorm = zero::<T::Real>();
1038 let mut bnorm = zero::<T::Real>();
1039 let mut j = 0;
1040 while j < n {
1041 let a = A.rb().col(j).get(..j).norm_l1();
1042 acolnorm[j] = a.copy();
1043 anorm = anorm.fmax(a + A[(j, j)].abs());
1044 j += 1;
1045 }
1046 for j in 0..n {
1047 let ref b = B.rb().col(j).get(..j).norm_l1();
1048 bcolnorm[j] = b.copy();
1049 bnorm = bnorm.fmax(b + B[(j, j)].abs());
1050 }
1051 let ref anorm = anorm;
1052 let ref bnorm = bnorm;
1053 let ref ascale = anorm.fmax(safmin).recip();
1054 let ref bscale = bnorm.fmax(safmin).recip();
1055 if let Some(mut u) = Q {
1056 let mut je = 0usize;
1057 while je < n {
1058 if A[(je, je)].abs().fmax(&B[(je, je)].abs()) < *safmin {
1059 u.rb_mut().col_mut(je).fill(zero());
1060 u[(je, je)] = one().to_cplx();
1061 je += 1;
1062 continue;
1063 }
1064 let mut acoef;
1065 let acoefa;
1066 let bcoefa;
1067 let mut bcoef;
1068 let mut xmax;
1069 let (mut rhs, stack) =
1070 linalg::temp_mat_zeroed::<T, _, _>(n, 1, stack);
1071 let mut rhs = rhs.as_mat_mut().col_mut(0);
1072 {
1073 let ref temp = safmin.fmax(
1074 (A[(je, je)].abs() * ascale)
1075 .fmax(B[(je, je)].abs() * bscale),
1076 );
1077 let ref salfar = A[(je, je)].mul_real(temp).mul_real(ascale);
1078 let ref sbeta = B[(je, je)].real() * temp * bscale;
1079 acoef = sbeta * ascale;
1080 bcoef = salfar.mul_real(bscale);
1081 let mut scale = one();
1082 let lsa = sbeta.abs() >= *safmin && acoef.abs() < *small;
1083 let lsb = salfar.abs() >= *safmin && bcoef.abs() < *small;
1084 if lsa {
1085 scale = (small / sbeta.abs()) * anorm.fmin(big);
1086 }
1087 if lsb {
1088 scale =
1089 scale.fmax((small / salfar.abs()) * bnorm.fmin(big));
1090 }
1091 if lsa || lsb {
1092 scale = scale.fmin(
1093 one()
1094 / (safmin
1095 * one().fmax(acoef.abs()).fmax(bcoef.abs())),
1096 );
1097 if lsa {
1098 acoef = sbeta * &scale * ascale
1099 } else {
1100 acoef = acoef * &scale
1101 }
1102 if lsb {
1103 bcoef = salfar.mul_real(&scale).mul_real(bscale)
1104 } else {
1105 bcoef = bcoef.mul_real(&scale)
1106 }
1107 }
1108 acoefa = acoef.abs();
1109 bcoefa = bcoef.abs();
1110 rhs[je] = one().to_cplx();
1111 xmax = one();
1112 }
1113 let dmin = safmin
1114 .fmax(ulp * &acoefa * anorm)
1115 .fmax(ulp * &bcoefa * bnorm);
1116 let mut j = je + 1;
1117 while j < n {
1118 let ref xscale = xmax.recip();
1119 let ref temp = acolnorm[j]
1120 .fmax(&bcolnorm[j])
1121 .fmax(&acoefa * &acolnorm[j] + &bcoefa * &bcolnorm[j]);
1122 let b0 = B[(j, j)].copy();
1123 if *temp > bignum * xscale {
1124 for jr in je..j {
1125 rhs[jr] = rhs[jr].mul_real(xscale);
1126 }
1127 xmax = xmax * xscale;
1128 }
1129 let mut sums = zero::<T>();
1130 let mut sump = zero::<T>();
1131 for jr in je..j {
1132 sums += A[(jr, j)].conj() * &rhs[jr];
1133 sump += B[(jr, j)].conj() * &rhs[jr];
1134 }
1135 rhs[j] = bcoef.conj() * &sump - sums.mul_real(&acoef);
1136 solve_shifted_1x1(
1137 dmin.copy(),
1138 acoef.copy(),
1139 A[(j, j)].conj(),
1140 b0.conj(),
1141 &mut rhs[j],
1142 bcoef.conj(),
1143 );
1144 j += 1;
1145 }
1146 let (mut tmp, _) = linalg::temp_mat_zeroed::<T, _, _>(n, 1, stack);
1147 let mut tmp = tmp.as_mat_mut().col_mut(0);
1148 linalg::matmul::matmul(
1149 tmp.rb_mut(),
1150 Accum::Replace,
1151 u.rb().get(.., je..),
1152 rhs.rb().get(je..),
1153 one().to_cplx(),
1154 par.reborrow(),
1155 );
1156 let mut u = u.rb_mut().col_mut(je);
1157 u.copy_from(&tmp);
1158 let ref scale = u.norm_l2().recip();
1159 zip!(u).for_each(|unzip!(u)| {
1160 *u = u.mul_real(scale);
1161 });
1162 je += 1;
1163 }
1164 }
1165 if let Some(mut u) = Z {
1166 let mut je = n;
1167 while je > 0 {
1168 je -= 1;
1169 if A[(je, je)].abs().fmax(&B[(je, je)].abs()) < *safmin {
1170 u.rb_mut().col_mut(je).fill(zero());
1171 u[(je, je)] = one().to_cplx();
1172 continue;
1173 }
1174 let mut acoef;
1175 let acoefa;
1176 let bcoefa;
1177 let mut bcoefr;
1178 let (mut rhs, stack) =
1179 linalg::temp_mat_zeroed::<T, _, _>(n, 1, stack);
1180 let mut rhs = rhs.as_mat_mut().col_mut(0);
1181 {
1182 let ref temp = safmin.fmax(
1183 (A[(je, je)].abs() * ascale)
1184 .fmax(B[(je, je)].abs() * bscale),
1185 );
1186 let ref salfar = A[(je, je)].mul_real(temp).mul_real(ascale);
1187 let ref sbeta = B[(je, je)].real() * temp * bscale;
1188 acoef = sbeta * ascale;
1189 bcoefr = salfar.mul_real(bscale);
1190 let mut scale = one();
1191 let lsa = sbeta.abs() >= *safmin && acoef.abs() < *small;
1192 let lsb = salfar.abs() >= *safmin && bcoefr.abs() < *small;
1193 if lsa {
1194 scale = (small / sbeta.abs()) * anorm.fmin(big);
1195 }
1196 if lsb {
1197 scale =
1198 scale.fmax((small / salfar.abs()) * bnorm.fmin(big));
1199 }
1200 if lsa || lsb {
1201 scale = scale.fmin(
1202 one()
1203 / (safmin
1204 * one().fmax(acoef.abs()).fmax(bcoefr.abs())),
1205 );
1206 if lsa {
1207 acoef = sbeta * &scale * ascale
1208 } else {
1209 acoef = acoef * &scale
1210 }
1211 if lsb {
1212 bcoefr = salfar.mul_real(&scale).mul_real(bscale)
1213 } else {
1214 bcoefr = bcoefr.mul_real(&scale)
1215 }
1216 }
1217 acoefa = acoef.abs();
1218 bcoefa = bcoefr.abs();
1219 rhs[je] = one().to_cplx();
1220 for jr in 0..je {
1221 rhs[jr] =
1222 &bcoefr * &B[(jr, je)] - A[(jr, je)].mul_real(&acoef);
1223 }
1224 }
1225 let dmin = safmin
1226 .fmax(ulp * &acoefa * anorm)
1227 .fmax(ulp * &bcoefa * bnorm);
1228 let mut j = je;
1229 while j > 0 {
1230 j -= 1;
1231 let b0 = B[(j, j)].copy();
1232 solve_shifted_1x1(
1233 dmin.copy(),
1234 acoef.copy(),
1235 A[(j, j)].copy(),
1236 b0,
1237 &mut rhs[j],
1238 bcoefr.copy(),
1239 );
1240 let ref creala = rhs[j].mul_real(&acoef);
1241 let ref crealb = &bcoefr * &rhs[j];
1242 for jr in 0..j {
1243 rhs[jr] =
1244 &rhs[jr] - creala * &A[(jr, j)] + crealb * &B[(jr, j)];
1245 }
1246 }
1247 let (mut tmp, _) = linalg::temp_mat_zeroed::<T, _, _>(n, 1, stack);
1248 let mut tmp = tmp.as_mat_mut().col_mut(0);
1249 linalg::matmul::matmul(
1250 tmp.rb_mut(),
1251 Accum::Replace,
1252 u.rb().get(.., ..je + 1),
1253 rhs.rb().get(..je + 1),
1254 one().to_cplx(),
1255 par.reborrow(),
1256 );
1257 let mut u = u.rb_mut().col_mut(je);
1258 u.copy_from(&tmp);
1259 let ref scale = u.norm_l2().recip();
1260 zip!(u).for_each(|unzip!(u)| {
1261 *u = u.mul_real(scale);
1262 });
1263 }
1264 }
1265}
1266
1267fn qz_to_gevd_real_scratch<T: RealField>(n: usize, _: ParShape) -> StackReq {
1268 StackReq::all_of(&[
1269 temp_mat_scratch::<T>(n, 1),
1270 temp_mat_scratch::<T>(n, 1),
1271 temp_mat_scratch::<T>(n, 2),
1272 temp_mat_scratch::<T>(2, 2),
1273 temp_mat_scratch::<T>(2, 2),
1274 StackReq::all_of(&[
1275 temp_mat_scratch::<T>(2, 2),
1276 temp_mat_scratch::<T>(n, 1),
1277 linalg::lu::full_pivoting::factor::lu_in_place_scratch::<
1278 usize,
1279 Complex<T>,
1280 >(2, 2, ParShape::Seq, Default::default()),
1281 linalg::lu::full_pivoting::solve::solve_in_place_scratch::<
1282 usize,
1283 Complex<T>,
1284 >(2, 1, ParShape::Seq),
1285 ]),
1286 temp_mat_scratch::<T>(n, 2),
1287 ])
1288}
1289
1290fn qz_to_gevd_cplx_scratch<T: ComplexField>(n: usize, _: ParShape) -> StackReq {
1291 StackReq::all_of(&[temp_mat_scratch::<T>(n, 1); 4])
1292}
1293
1294pub fn gevd_scratch<T: ComplexField>(
1297 dim: usize,
1298 left: ComputeEigenvectors,
1299 right: ComputeEigenvectors,
1300 par: ParShape,
1301 params: Spec<GevdParams, T>,
1302) -> StackReq {
1303 let _ = (left, right);
1304 let n = dim;
1305 let block_size =
1306 linalg::qr::no_pivoting::factor::recommended_block_size::<T>(n, n);
1307 StackReq::any_of(&[
1308 linalg::temp_mat_scratch::<T>(block_size, n).and(
1309 linalg::qr::no_pivoting::factor::qr_in_place_scratch::<T>(n, n, block_size, par, default())
1310 .or(linalg::householder::apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<T>(n, block_size, n)),
1311 ),
1312 gen_hessenberg::generalized_hessenberg_scratch::<T>(n, auto!(T)),
1313 if const { T::IS_REAL } {
1314 qz_real::hessenberg_to_qz_scratch::<T::Real>(n, par, params.schur)
1315 } else {
1316 qz_cplx::hessenberg_to_qz_scratch::<T>(n, par, params.schur)
1317 },
1318 if const { T::IS_REAL } {
1319 qz_to_gevd_real_scratch::<T::Real>(n, par)
1320 } else {
1321 qz_to_gevd_cplx_scratch::<T>(n, par)
1322 },
1323 ])
1324}
1325#[track_caller]
1332pub fn gevd_real<T: RealField>(
1333 A: MatMut<'_, T>,
1334 B: MatMut<'_, T>,
1335 S_re: DiagMut<'_, T>,
1336 S_im: DiagMut<'_, T>,
1337 beta: DiagMut<'_, T>,
1338 U_left: Option<MatMut<'_, T>>,
1339 U_right: Option<MatMut<'_, T>>,
1340 par: Par,
1341 stack: &mut MemStack,
1342 params: Spec<GevdParams, T>,
1343) -> Result<(), GevdError> {
1344 compute_gevd_generic(
1345 A,
1346 B,
1347 S_re.column_vector_mut(),
1348 S_im.column_vector_mut(),
1349 beta.column_vector_mut(),
1350 U_left,
1351 U_right,
1352 par,
1353 stack,
1354 params.config,
1355 qz_real::hessenberg_to_qz,
1356 qz_to_gevd_real,
1357 )
1358}
1359#[track_caller]
1366pub fn gevd_cplx<T: ComplexField>(
1367 A: MatMut<'_, T>,
1368 B: MatMut<'_, T>,
1369 S: DiagMut<'_, T>,
1370 beta: DiagMut<'_, T>,
1371 U_left: Option<MatMut<'_, T>>,
1372 U_right: Option<MatMut<'_, T>>,
1373 par: Par,
1374 stack: &mut MemStack,
1375 params: Spec<GevdParams, T>,
1376) -> Result<(), GevdError> {
1377 compute_gevd_generic(
1378 A,
1379 B,
1380 S.column_vector_mut(),
1381 ColMut::from_slice_mut(&mut []),
1382 beta.column_vector_mut(),
1383 U_left,
1384 U_right,
1385 par,
1386 stack,
1387 params.config,
1388 |A: MatMut<'_, T>,
1389 B: MatMut<'_, T>,
1390 Q: Option<MatMut<'_, T>>,
1391 Z: Option<MatMut<'_, T>>,
1392 alphar: ColMut<'_, T>,
1393 _: ColMut<'_, T>,
1394 beta: ColMut<'_, T>,
1395 eigenvectors: ComputeEigenvectors,
1396 par: Par,
1397 params: GeneralizedSchurParams,
1398 stack: &mut MemStack| {
1399 qz_cplx::hessenberg_to_qz(
1400 A,
1401 B,
1402 Q,
1403 Z,
1404 alphar,
1405 beta,
1406 eigenvectors,
1407 par,
1408 params,
1409 stack,
1410 )
1411 },
1412 qz_to_gevd_cplx,
1413 )
1414}
1415#[cfg(test)]
1416mod tests {
1417 use super::*;
1418 use crate::stats::prelude::*;
1419 use crate::utils;
1420 use dyn_stack::MemBuffer;
1421 use equator::assert;
1422 #[test]
1423 fn test_lishen_() {
1424 let approx_eq = utils::approx::CwiseMat(
1425 utils::approx::ApproxEq::<f64>::eps() * 128.0,
1426 );
1427 let A = &[
1428 [
1429 0.0000000067116330731078674,
1430 -0.0000000024189842394753674,
1431 0.00000000032505193899740187,
1432 0.0,
1433 0.0,
1434 0.0,
1435 ],
1436 [
1437 -0.0000000024189842394753674,
1438 0.000000011866543705551487,
1439 -0.00000001582083742901503,
1440 0.0,
1441 0.0,
1442 0.0,
1443 ],
1444 [
1445 0.00000000032505193899740187,
1446 -0.00000001582083742901503,
1447 0.00000002523087227632826,
1448 0.0,
1449 0.0,
1450 0.0,
1451 ],
1452 [0.0, 0.0, 0.0, 1.0, 0.0, 0.0],
1453 [0.0, 0.0, 0.0, 0.0, 1.0, 0.0],
1454 [0.0, 0.0, 0.0, 0.0, 0.0, 1.0f64],
1455 ];
1456 let B = &[
1457 [
1458 0.00016038376001067668,
1459 -0.00003674235041182116,
1460 -0.0000029397921270058582,
1461 -1.0,
1462 -0.0,
1463 -0.0,
1464 ],
1465 [
1466 -0.00003674235041182116,
1467 0.0002038454538304322,
1468 -0.00021213416901900504,
1469 -0.0,
1470 -1.0,
1471 -0.0,
1472 ],
1473 [
1474 -0.0000029397921270058582,
1475 -0.00021213416901900504,
1476 0.0003954706173886255,
1477 -0.0,
1478 -0.0,
1479 -1.0,
1480 ],
1481 [1.0, 0.0, 0.0, 0.0, 0.0, 0.0],
1482 [0.0, 1.0, 0.0, 0.0, 0.0, 0.0],
1483 [0.0, 0.0, 1.0, 0.0, 0.0, 0.0f64],
1484 ];
1485 let A = MatRef::from_row_major_array(A);
1486 let B = MatRef::from_row_major_array(B);
1487 let n = A.nrows();
1488 {
1489 let mut H = A.to_owned();
1490 let mut T = B.to_owned();
1491 let mut alpha_re = Diag::<f64>::zeros(n);
1492 let mut alpha_im = Diag::<f64>::zeros(n);
1493 let mut beta = Diag::<f64>::zeros(n);
1494 let mut UL = Mat::<f64>::identity(n, n);
1495 let mut UR = Mat::<f64>::identity(n, n);
1496 gevd_real(
1497 H.as_mut(),
1498 T.as_mut(),
1499 alpha_re.rb_mut(),
1500 alpha_im.rb_mut(),
1501 beta.rb_mut(),
1502 Some(UL.as_mut()),
1503 Some(UR.as_mut()),
1504 Par::Seq,
1505 MemStack::new(&mut MemBuffer::new(gevd_scratch::<f64>(
1506 n,
1507 ComputeEigenvectors::Yes,
1508 ComputeEigenvectors::Yes,
1509 ParShape::Seq,
1510 default(),
1511 ))),
1512 default(),
1513 )
1514 .unwrap();
1515 {
1516 let mut i = 0;
1517 while i < n {
1518 if alpha_im[i] == 0.0 {
1519 let u = UR.col(i);
1520 let a = alpha_re[i];
1521 let b = beta[i];
1522 assert!((b / a) * & A * u ~ B * u);
1523 i += 1;
1524 } else {
1525 let ur = UR.col(i);
1526 let ui = UR.col(i + 1);
1527 let ar = alpha_re[i];
1528 let ai = alpha_im[i];
1529 let b = beta[i];
1530 assert!(b * & A * ur ~ B * (ar * ur - ai * ui));
1531 assert!(b * & A * ui ~ B * (ar * ui + ai * ur));
1532 i += 2;
1533 }
1534 }
1535 }
1536 {
1537 let mut i = 0;
1538 while i < n {
1539 if alpha_im[i] == 0.0 {
1540 let u = UL.col(i);
1541 let a = alpha_re[i];
1542 let b = beta[i];
1543 assert!(b / a * u.adjoint() * & A ~ u.adjoint() * B);
1544 i += 1;
1545 } else {
1546 let ur = UL.col(i);
1547 let ui = UL.col(i + 1);
1548 let ar = alpha_re[i];
1549 let ai = alpha_im[i];
1550 let b = beta[i];
1551 assert!(
1552 b * ur.adjoint() * & A ~ (ar * ur - ai * ui).adjoint() * B
1553 );
1554 assert!(
1555 b * ui.adjoint() * & A ~ (ar * ui + ai * ur).adjoint() * B
1556 );
1557 i += 2;
1558 }
1559 }
1560 }
1561 }
1562 }
1563 #[test]
1564 fn test_cplx() {
1565 let rng = &mut StdRng::seed_from_u64(1);
1566 let approx_eq = utils::approx::CwiseMat(
1567 utils::approx::ApproxEq::<f64>::eps() * 128.0,
1568 );
1569 let n = 20;
1570 let A = &CwiseMatDistribution {
1571 nrows: n,
1572 ncols: n,
1573 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
1574 }
1575 .rand::<Mat<c64>>(rng);
1576 let B = &CwiseMatDistribution {
1577 nrows: n,
1578 ncols: n,
1579 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
1580 }
1581 .rand::<Mat<c64>>(rng);
1582 {
1583 let mut H = A.to_owned();
1584 let mut T = B.to_owned();
1585 let mut alpha_re = Diag::<c64>::zeros(n);
1586 let mut beta = Diag::<c64>::zeros(n);
1587 let mut UL = Mat::<c64>::identity(n, n);
1588 let mut UR = Mat::<c64>::identity(n, n);
1589 gevd_cplx(
1590 H.as_mut(),
1591 T.as_mut(),
1592 alpha_re.rb_mut(),
1593 beta.rb_mut(),
1594 Some(UL.as_mut()),
1595 Some(UR.as_mut()),
1596 Par::Seq,
1597 MemStack::new(&mut MemBuffer::new(gevd_scratch::<c64>(
1598 n,
1599 ComputeEigenvectors::Yes,
1600 ComputeEigenvectors::Yes,
1601 ParShape::Seq,
1602 default(),
1603 ))),
1604 default(),
1605 )
1606 .unwrap();
1607 {
1608 let mut i = 0;
1609 while i < n {
1610 let u = UR.col(i);
1611 let a = alpha_re[i];
1612 let b = beta[i];
1613 assert!(Scale(b / a) * A * u ~ B * u);
1614 i += 1;
1615 }
1616 }
1617 {
1618 let mut i = 0;
1619 while i < n {
1620 let u = UL.col(i);
1621 let a = alpha_re[i];
1622 let b = beta[i];
1623 assert!(Scale(b / a) * u.adjoint() * A ~ u.adjoint() * B);
1624 i += 1;
1625 }
1626 }
1627 }
1628 }
1629}
1630
1631#[cfg(test)]
1633mod t {
1634 use faer::dyn_stack::{MemBuffer, MemStack};
1635 use faer::linalg::evd::ComputeEigenvectors as CE;
1636 use faer::prelude::*;
1637 use faer::{Par, Spec, linalg};
1638
1639 fn mk(n: usize, seed: u64) -> Mat<f64> {
1640 let mut s = seed.wrapping_mul(6364136223846793005).wrapping_add(1);
1641 let mut next = move || {
1642 s = s
1643 .wrapping_mul(6364136223846793005)
1644 .wrapping_add(1442695040888963407);
1645 ((s >> 33) as f64 / (1u64 << 31) as f64) - 1.0
1646 };
1647 Mat::from_fn(n, n, |i, j| next() + if i == j { n as f64 } else { 0.0 })
1648 }
1649
1650 fn gevd(a: &Mat<f64>, b: &Mat<f64>, vectors: bool) -> Vec<(f64, f64)> {
1652 let n = a.nrows();
1653 let (mut am, mut bm) = (a.clone(), b.clone());
1654 let mut sre = faer::diag::Diag::<f64>::zeros(n);
1655 let mut sim = faer::diag::Diag::<f64>::zeros(n);
1656 let mut bet = faer::diag::Diag::<f64>::zeros(n);
1657 let mut u = Mat::<f64>::zeros(n, n);
1658 let ce = if vectors { CE::Yes } else { CE::No };
1659 let mut mem = MemBuffer::new(linalg::gevd::gevd_scratch::<f64>(
1660 n,
1661 CE::No,
1662 ce,
1663 ParShape::Seq,
1664 Spec::default(),
1665 ));
1666 linalg::gevd::gevd_real(
1667 am.as_mut(),
1668 bm.as_mut(),
1669 sre.as_mut(),
1670 sim.as_mut(),
1671 bet.as_mut(),
1672 None,
1673 vectors.then(|| u.as_mut()),
1674 Par::Seq,
1675 MemStack::new(&mut mem),
1676 Spec::default(),
1677 )
1678 .unwrap();
1679 (0..n).map(|k| (sre[k] / bet[k], sim[k] / bet[k])).collect()
1680 }
1681
1682 fn residual(a: &Mat<f64>, b: &Mat<f64>, lam: (f64, f64)) -> f64 {
1684 let n = a.nrows();
1685 let mut m: Vec<(f64, f64)> = (0..n * n)
1686 .map(|k| {
1687 let (i, j) = (k % n, k / n);
1688 (a[(i, j)] - lam.0 * b[(i, j)], -lam.1 * b[(i, j)])
1689 })
1690 .collect();
1691 let mut det = (1.0f64, 0.0f64);
1692 for col in 0..n {
1693 let p = (col..n)
1694 .max_by(|&x, &y| {
1695 let f = |r: usize| m[r + col * n].0.hypot(m[r + col * n].1);
1696 f(x).partial_cmp(&f(y)).unwrap()
1697 })
1698 .unwrap();
1699 if p != col {
1700 for j in 0..n {
1701 m.swap(col + j * n, p + j * n);
1702 }
1703 det = (-det.0, -det.1);
1704 }
1705 let d = m[col + col * n];
1706 det = (det.0 * d.0 - det.1 * d.1, det.0 * d.1 + det.1 * d.0);
1707 let dn = d.0 * d.0 + d.1 * d.1;
1708 if dn == 0.0 {
1709 continue;
1710 }
1711 for r in col + 1..n {
1712 let x = m[r + col * n];
1713 let f = (
1714 (x.0 * d.0 + x.1 * d.1) / dn,
1715 (x.1 * d.0 - x.0 * d.1) / dn,
1716 );
1717 for j in col..n {
1718 let y = m[col + j * n];
1719 m[r + j * n].0 -= f.0 * y.0 - f.1 * y.1;
1720 m[r + j * n].1 -= f.0 * y.1 + f.1 * y.0;
1721 }
1722 }
1723 }
1724 det.0.hypot(det.1)
1725 }
1726
1727 #[test]
1737 fn eigenvalues_only_gives_wrong_complex_pairs() {
1738 let (mut bad_novec, mut bad_vec) = (0, 0);
1739 for n in [3usize, 4, 5, 6, 8, 10, 12] {
1740 for seed in 0..40u64 {
1741 let a = mk(n, seed * 7919 + n as u64);
1742 let b = mk(n, seed * 7919 + n as u64 + 5000);
1743 let scale = (0..n * n)
1744 .map(|k| a[(k % n, k / n)].abs())
1745 .fold(1.0f64, f64::max);
1746 let tol = 1e-9 * scale.powi(n as i32);
1747 for (lams, vectors) in
1748 [(gevd(&a, &b, false), false), (gevd(&a, &b, true), true)]
1749 {
1750 for &l in &lams {
1751 if l.1 == 0.0 {
1752 continue;
1753 }
1754 if residual(&a, &b, l) > tol {
1755 if vectors {
1756 bad_vec += 1
1757 } else {
1758 bad_novec += 1
1759 }
1760 }
1761 }
1762 }
1763 }
1764 }
1765 assert_eq!(bad_novec, 0, "eigenvalues-only path is wrong");
1766 assert_eq!(bad_vec, 0, "eigenvalues-only path is wrong");
1767 }
1768
1769 #[test]
1772 fn gevd_scratch_is_too_small_for_n2() {
1773 let mut panics = 0;
1774 for seed in 0..40u64 {
1775 let a = mk(2, seed * 7919 + 2);
1776 let b = mk(2, seed * 7919 + 2 + 5000);
1777 if std::panic::catch_unwind(|| gevd(&a, &b, true)).is_err() {
1778 panics += 1;
1779 }
1780 }
1781 assert_eq!(panics, 0, "gevd_scratch under-allocates");
1782 }
1783 #[test]
1786 fn reused_eigenvector_buffers() {
1787 fn check<T: faer::traits::RealField>(
1788 a: Mat<T>,
1789 b: Mat<T>,
1790 prefills: [T; 2],
1791 tol: T,
1792 ) {
1793 for prefill in prefills {
1794 for (want_left, want_right) in
1795 [(false, true), (true, false), (true, true)]
1796 {
1797 let mut am = a.clone();
1798 let mut bm = b.clone();
1799 let mut sre = faer::diag::Diag::<T>::zeros(2);
1800 let mut sim = faer::diag::Diag::<T>::zeros(2);
1801 let mut bet = faer::diag::Diag::<T>::zeros(2);
1802 let mut ul = Mat::<T>::full(2, 2, prefill.clone());
1803 let mut ur = Mat::<T>::full(2, 2, prefill.clone());
1804 let ce = |v: bool| {
1805 if v { CE::Yes } else { CE::No }
1806 };
1807 linalg::gevd::gevd_real(
1808 am.as_mut(),
1809 bm.as_mut(),
1810 sre.as_mut(),
1811 sim.as_mut(),
1812 bet.as_mut(),
1813 want_left.then(|| ul.as_mut()),
1814 want_right.then(|| ur.as_mut()),
1815 Par::Seq,
1816 MemStack::new(&mut MemBuffer::new(
1817 linalg::gevd::gevd_scratch::<T>(
1818 2,
1819 ce(want_left),
1820 ce(want_right),
1821 ParShape::Seq,
1822 Spec::default(),
1823 ),
1824 )),
1825 Spec::default(),
1826 )
1827 .unwrap();
1828 let nonzero = |v: &Mat<T>| {
1831 for j in 0..v.ncols() {
1832 assert!(v.col(j).norm_l2() > tol);
1833 }
1834 };
1835 if want_right {
1836 assert!(ur.is_all_finite());
1837 nonzero(&ur);
1838 let res =
1839 (&a * &ur) * bet.as_ref() - &ur * sre.as_ref();
1840 assert!(res.is_all_finite());
1841 assert!(res.norm_max() < tol);
1842 }
1843 if want_left {
1844 assert!(ul.is_all_finite());
1845 nonzero(&ul);
1846 let res = bet.as_ref() * (ul.adjoint() * &a)
1847 - sre.as_ref() * (ul.adjoint() * &b);
1848 assert!(res.is_all_finite());
1849 assert!(res.norm_max() < tol);
1850 }
1851 }
1852 }
1853 }
1854 check(
1855 Mat::<f32>::from_fn(2, 2, |i, j| [[2.0, 1.0], [0.0, 3.0]][i][j]),
1856 Mat::<f32>::identity(2, 2),
1857 [0.0, 2.0],
1858 1e-5,
1859 );
1860 check(
1861 Mat::<f64>::from_fn(2, 2, |i, j| [[2.0, 1.0], [0.0, 3.0]][i][j]),
1862 Mat::<f64>::identity(2, 2),
1863 [0.0, 2.0],
1864 1e-12,
1865 );
1866 }
1867}