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