1use crate::assert;
2use crate::internal_prelude::*;
3use linalg::householder::{self, HouseholderInfo};
4use linalg::matmul::triangular::BlockStructure;
5use linalg::matmul::{self, dot, matmul};
6use linalg::triangular_solve;
7#[derive(Copy, Clone, Debug)]
9pub struct HessenbergParams {
10 pub par_threshold: usize,
12 pub blocking_threshold: usize,
14 #[doc(hidden)]
15 pub non_exhaustive: NonExhaustive,
16}
17impl<T: ComplexField> Auto<T> for HessenbergParams {
18 fn auto() -> Self {
19 Self {
20 par_threshold: 192 * 256,
21 blocking_threshold: 256 * 256,
22 non_exhaustive: NonExhaustive(()),
23 }
24 }
25}
26pub fn hessenberg_in_place_scratch<T: ComplexField>(
29 dim: usize,
30 block_size: usize,
31 par: Par,
32 params: Spec<HessenbergParams, T>,
33) -> StackReq {
34 let params = params.config;
35 let _ = par;
36 let n = dim;
37 if n * n < params.blocking_threshold {
38 StackReq::any_of(&[StackReq::all_of(&[
39 temp_mat_scratch::<T>(n, 1).array(3),
40 temp_mat_scratch::<T>(n, par.degree()),
41 ])])
42 } else {
43 StackReq::all_of(&[
44 temp_mat_scratch::<T>(n, block_size),
45 temp_mat_scratch::<T>(block_size, 1),
46 StackReq::any_of(&[
47 StackReq::all_of(&[
48 temp_mat_scratch::<T>(n, 1),
49 temp_mat_scratch::<T>(n, par.degree()),
50 ]),
51 temp_mat_scratch::<T>(n, block_size),
52 ]),
53 ])
54 }
55}
56fn hessenberg_fused_op_simd<T: ComplexField>(
57 A: MatMut<'_, T, usize, usize, ContiguousFwd>,
58 l_out: RowMut<'_, T, usize>,
59 r_out: ColMut<'_, T, usize, ContiguousFwd>,
60 l_in: RowRef<'_, T, usize, ContiguousFwd>,
61 r_in: ColRef<'_, T, usize>,
62 l0: ColRef<'_, T, usize, ContiguousFwd>,
63 l1: ColRef<'_, T, usize, ContiguousFwd>,
64 r0: RowRef<'_, T, usize>,
65 r1: RowRef<'_, T, usize>,
66 align: usize,
67) {
68 struct Impl<'a, 'M, 'N, T: ComplexField> {
69 A: MatMut<'a, T, Dim<'M>, Dim<'N>, ContiguousFwd>,
70 l_out: RowMut<'a, T, Dim<'N>>,
71 r_out: ColMut<'a, T, Dim<'M>, ContiguousFwd>,
72 l_in: RowRef<'a, T, Dim<'M>, ContiguousFwd>,
73 r_in: ColRef<'a, T, Dim<'N>>,
74 l0: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
75 l1: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
76 r0: RowRef<'a, T, Dim<'N>>,
77 r1: RowRef<'a, T, Dim<'N>>,
78 align: usize,
79 }
80 impl<'a, 'M, 'N, T: ComplexField> pulp::WithSimd for Impl<'a, 'M, 'N, T> {
81 type Output = ();
82
83 #[inline(always)]
84 fn with_simd<S: pulp::Simd>(self, simd: S) -> Self::Output {
85 let Self {
86 mut A,
87 mut l_out,
88 mut r_out,
89 l_in,
90 r_in,
91 l0,
92 l1,
93 r0,
94 r1,
95 align,
96 } = self;
97 let (m, n) = A.shape();
98 let simd = SimdCtx::<T, S>::new_align(T::simd_ctx(simd), m, align);
99
100 simd_iter!(for i in [simd.indices()] {
101 simd.write(r_out.rb_mut(), i, simd.zero());
102 });
103
104 let indices = simd.batch_indices::<4>();
105 let l_in = l_in.transpose();
106 for j in n.indices() {
107 let mut A = A.rb_mut().col_mut(j);
108 let r_in = simd.splat(r_in.at(j));
109 let r0 = simd.splat(-&r0[j]);
110 let r1 = simd.splat(-&r1[j]);
111 let mut acc = [simd.zero(); 4];
112 simd_iter!(for (IDX, i) in [indices; 4] {
113 let mut a = simd.read(A.rb(), i);
114 a = simd.mul_add(simd.read(l0, i), r0, a);
115 a = simd.conj_mul_add(r1, simd.read(l1, i), a);
116 simd.write(A.rb_mut(), i, a);
117 acc[IDX] =
118 simd.conj_mul_add(simd.read(l_in, i), a, acc[IDX]);
119 let tmp = simd.read(r_out.rb(), i);
120 simd.write(r_out.rb_mut(), i, simd.mul_add(a, r_in, tmp));
121 });
122 let acc0 = simd.add(acc[0], acc[1]);
123 let acc2 = simd.add(acc[2], acc[3]);
124 let acc0 = simd.add(acc0, acc2);
125 let l_out = l_out.rb_mut().at_mut(j);
126 *l_out = simd.reduce_sum(acc0);
127 }
128 }
129 }
130 with_dim!(M, A.nrows());
131 with_dim!(N, A.ncols());
132 dispatch!(
133 Impl {
134 A: A.as_shape_mut(M, N),
135 l_out: l_out.as_col_shape_mut(N),
136 r_out: r_out.as_row_shape_mut(M),
137 l_in: l_in.as_col_shape(M),
138 r_in: r_in.as_row_shape(N),
139 l0: l0.as_row_shape(M),
140 l1: l1.as_row_shape(M),
141 r0: r0.as_col_shape(N),
142 r1: r1.as_col_shape(N),
143 align,
144 },
145 Impl,
146 T
147 )
148}
149fn hessenberg_fused_op_fallback<T: ComplexField>(
150 A: MatMut<'_, T>,
151 l_out: RowMut<'_, T>,
152 r_out: ColMut<'_, T>,
153 l_in: RowRef<'_, T>,
154 r_in: ColRef<'_, T>,
155 l0: ColRef<'_, T>,
156 l1: ColRef<'_, T>,
157 r0: RowRef<'_, T>,
158 r1: RowRef<'_, T>,
159) {
160 let mut A = A;
161 matmul(
162 A.rb_mut(),
163 Accum::Add,
164 l0.as_mat(),
165 r0.as_mat(),
166 -one::<T>(),
167 Par::Seq,
168 );
169 matmul(
170 A.rb_mut(),
171 Accum::Add,
172 l1.as_mat(),
173 r1.as_mat().conjugate(),
174 -one::<T>(),
175 Par::Seq,
176 );
177 matmul(
178 r_out.as_mat_mut(),
179 Accum::Replace,
180 A.rb(),
181 r_in.as_mat(),
182 one(),
183 Par::Seq,
184 );
185 matmul(
186 l_out.as_mat_mut(),
187 Accum::Replace,
188 l_in.as_mat().conjugate(),
189 A.rb(),
190 one(),
191 Par::Seq,
192 );
193}
194fn hessenberg_fused_op<T: ComplexField>(
195 A: MatMut<'_, T>,
196 l_out: RowMut<'_, T>,
197 r_out: ColMut<'_, T>,
198 l_in: RowRef<'_, T>,
199 r_in: ColRef<'_, T>,
200 l0: ColRef<'_, T>,
201 l1: ColRef<'_, T>,
202 r0: RowRef<'_, T>,
203 r1: RowRef<'_, T>,
204 align: usize,
205) {
206 let mut A = A;
207 let mut r_out = r_out;
208 if const { T::SIMD_CAPABILITIES.is_simd() } {
209 if let (Some(A), Some(r_out), Some(l_in), Some(l0), Some(l1)) = (
210 A.rb_mut().try_as_col_major_mut(),
211 r_out.rb_mut().try_as_col_major_mut(),
212 l_in.try_as_row_major(),
213 l0.try_as_col_major(),
214 l1.try_as_col_major(),
215 ) {
216 hessenberg_fused_op_simd(
217 A, l_out, r_out, l_in, r_in, l0, l1, r0, r1, align,
218 );
219 } else {
220 hessenberg_fused_op_fallback(
221 A, l_out, r_out, l_in, r_in, l0, l1, r0, r1,
222 );
223 }
224 } else {
225 hessenberg_fused_op_fallback(
226 A, l_out, r_out, l_in, r_in, l0, l1, r0, r1,
227 );
228 }
229}
230fn hessenberg_rearranged_unblocked<T: ComplexField>(
231 A: MatMut<'_, T>,
232 H: MatMut<'_, T>,
233 par: Par,
234 stack: &mut MemStack,
235 params: HessenbergParams,
236) {
237 assert!(all(
238 A.nrows() == A.ncols(),
239 H.ncols() == A.ncols().saturating_sub(1)
240 ));
241 let n = A.nrows();
242 let b = H.nrows();
243 if n == 0 {
244 return;
245 }
246 let mut A = A;
247 let mut H = H;
248 let mut par = par;
249 {
250 let mut H = H.rb_mut().row_mut(0);
251 alloca!('stack: {
252 let mut y = unsafe { row![uninit::<T>, n] };
253 let mut z = unsafe { col![uninit::<T>, n] };
254 let mut v = unsafe { row![uninit::<T>, n] };
255 let mut w = unsafe { mat![uninit::<T>, n, par.degree()] };
256 });
257 for k in 0..n {
258 let (_, A01, A10, A11) = A.rb_mut().split_at_mut(k, k);
259 let (_, mut A02) = A01.split_first_col_mut().unwrap();
260 let (_, A20) = A10.split_first_row_mut().unwrap();
261 let (mut A11, A12, A21, mut A22) = A11.split_at_mut(1, 1);
262 let mut A12 = A12.row_mut(0);
263 let mut A21 = A21.col_mut(0);
264 let A11 = &mut A11[(0, 0)];
265 let (y1, mut y2) =
266 y.rb_mut().split_at_col_mut(k).1.split_at_col_mut(1);
267 let (z1, mut z2) =
268 z.rb_mut().split_at_row_mut(k).1.split_at_row_mut(1);
269 let (_, mut v2) =
270 v.rb_mut().split_at_col_mut(k).1.split_at_col_mut(1);
271 let (mut w0, w12) = w.rb_mut().split_at_row_mut(k);
272 let (_, mut w2) = w12.split_at_row_mut(1);
273 if k > 0 {
274 let y1 = y1[0].copy();
275 let z1 = z1[0].copy();
276
277 let p = k - 1;
278 let u2 = A20.rb().col(p);
279 *A11 -= &y1 + &z1;
280 z!(&mut A12, &y2, u2.rb().transpose()).for_each(
281 |uz!(a, y, u): Zip!(&mut T, &T, &T)| {
282 *a -= y + &z1 * u.conj();
283 },
284 );
285 z!(&mut A21, &u2, &z2).for_each(
286 |uz!(a, u, z): Zip!(&mut T, &T, &T)| *a -= u * &y1 + z,
287 );
288 }
289 {
290 let n = n - k - 1;
291 if n * n < params.par_threshold {
292 par = Par::Seq;
293 }
294 }
295 if k + 1 == n {
296 break;
297 }
298 let beta;
299 let tau_inv;
300 {
301 let (mut A11, mut A21) = A21.rb_mut().split_at_row_mut(1);
302 let A11 = &mut A11[0];
303 let HouseholderInfo { tau, .. } =
304 householder::make_householder_in_place(A11, A21.rb_mut());
305 tau_inv = tau.recip();
306 beta = A11.copy();
307 *A11 = one();
308 H[k] = tau.to_cplx();
309 }
310 let x2 = A21.rb();
311 if k > 0 {
312 let p = k - 1;
313 let u2 = A20.rb().col(p);
314 hessenberg_fused_op(
315 A22.rb_mut(),
316 v2.rb_mut(),
317 w2.rb_mut().col_mut(0),
318 x2.transpose(),
319 x2,
320 u2,
321 z2.rb(),
322 y2.rb(),
323 u2.transpose(),
324 simd_align(k + 1),
325 );
326 y2.copy_from(v2.rb());
327 z2.copy_from(w2.rb().col(0));
328 } else {
329 matmul(
330 z2.rb_mut().as_mat_mut(),
331 Accum::Replace,
332 A22.rb(),
333 x2.as_mat(),
334 one(),
335 par,
336 );
337 matmul(
338 y2.rb_mut().as_mat_mut(),
339 Accum::Replace,
340 x2.adjoint().as_mat(),
341 A22.rb(),
342 one(),
343 par,
344 );
345 }
346 let u2 = x2;
347 let ref b = dot::inner_prod(
348 u2.rb().transpose(),
349 Conj::Yes,
350 z2.rb(),
351 Conj::No,
352 )
353 .mul_pow2(from_f64::<T::Real>(0.5))
354 .mul_real(&tau_inv);
355 z!(&mut y2, u2.transpose()).for_each(
356 |uz!(y, u): Zip!(&mut T, &T)| {
357 *y = (&*y - b * u.conj()).mul_real(&tau_inv);
358 },
359 );
360 z!(&mut z2, u2).for_each(|uz!(z, u): Zip!(&mut T, &T)| {
361 *z = (&*z - b * u).mul_real(&tau_inv);
362 });
363 let ref dot =
364 dot::inner_prod(A12.rb(), Conj::No, u2.rb(), Conj::No)
365 .mul_real(&tau_inv);
366 z!(&mut A12, u2.transpose())
367 .for_each(|uz!(a, u): Zip!(&mut T, &T)| *a -= dot * u.conj());
368 matmul(
369 w0.rb_mut().col_mut(0).as_mat_mut(),
370 Accum::Replace,
371 A02.rb(),
372 u2.as_mat(),
373 one(),
374 par,
375 );
376 matmul(
377 A02.rb_mut(),
378 Accum::Add,
379 w0.rb().col(0).as_mat(),
380 u2.adjoint().as_mat(),
381 (-&tau_inv).to_cplx(),
382 par,
383 );
384 A21[0] = beta;
385 }
386 }
387 if n > 0 {
388 let n = n - 1;
389 let A = A.rb().submatrix(1, 0, n, n);
390 let mut H = H.rb_mut().subcols_mut(0, n);
391 let mut j = 0;
392 while j < n {
393 let b = Ord::min(b, n - j);
394 let mut H = H.rb_mut().submatrix_mut(0, j, b, b);
395 for k in 0..b {
396 H[(k, k)] = H[(0, k)].copy();
397 }
398 householder::upgrade_householder_factor(
399 H.rb_mut(),
400 A.submatrix(j, j, n - j, b),
401 b,
402 1,
403 par,
404 );
405 j += b;
406 }
407 }
408}
409fn hessenberg_gqvdg_unblocked<T: ComplexField>(
410 A: MatMut<'_, T>,
411 Z: MatMut<'_, T>,
412 H: MatMut<'_, T>,
413 beta: ColMut<'_, T>,
414 par: Par,
415 stack: &mut MemStack,
416 params: HessenbergParams,
417) {
418 let n = A.nrows();
419 let b = H.nrows();
420 let mut A = A;
421 let mut H = H;
422 let mut Z = Z;
423 _ = params;
424 let (mut x, _) = unsafe { temp_mat_uninit(n, 1, stack) };
425 let mut x = x.as_mat_mut().col_mut(0);
426 let mut beta = beta;
427 for k in 0..b {
428 let mut x0 = x.rb_mut().subrows_mut(0, k);
429 let (T00, T01, _, T11) = H.rb_mut().split_at_mut(k, k);
430 let (mut T01, _) = T01.split_first_col_mut().unwrap();
431 let (mut T11, _, _, _) = T11.split_at_mut(1, 1);
432 let T11 = &mut T11[(0, 0)];
433 let (U0, A12) = A.rb_mut().split_at_col_mut(k);
434 let (mut A1, A2) = A12.split_first_col_mut().unwrap();
435 let (Z0, Z12) = Z.rb_mut().split_at_col_mut(k);
436 let (mut Z1, _) = Z12.split_first_col_mut().unwrap();
437 let U0 = U0.rb();
438 let Z0 = Z0.rb();
439 let T00 = T00.rb();
440 let (U00, U10) = U0.split_at_row(k);
441 let (U10, U20) = U10.split_first_row().unwrap();
442 x0.copy_from(U10.adjoint());
443 triangular_solve::solve_upper_triangular_in_place(
444 T00,
445 x0.rb_mut().as_mat_mut(),
446 par,
447 );
448 matmul::matmul(
449 A1.rb_mut().as_mat_mut(),
450 Accum::Add,
451 Z0,
452 x0.rb().as_mat(),
453 -one::<T>(),
454 par,
455 );
456 let (mut A01, A11) = A1.rb_mut().split_at_row_mut(k);
457 let (mut A11, mut A21) = A11.split_at_row_mut(1);
458 let A11 = &mut A11[0];
459 {
460 matmul::triangular::matmul(
461 x0.rb_mut().as_mat_mut(),
462 BlockStructure::Rectangular,
463 Accum::Replace,
464 U00.adjoint(),
465 BlockStructure::StrictTriangularUpper,
466 A01.rb().as_mat(),
467 BlockStructure::Rectangular,
468 one(),
469 par,
470 );
471 z!(x0.rb_mut(), U10.transpose())
472 .for_each(|uz!(x, u): Zip!(&mut T, &T)| *x += &*A11 * u.conj());
473 matmul::matmul(
474 x0.rb_mut().as_mat_mut(),
475 Accum::Add,
476 U20.adjoint(),
477 A21.rb().as_mat(),
478 one(),
479 par,
480 );
481 }
482 {
483 triangular_solve::solve_lower_triangular_in_place(
484 T00.adjoint(),
485 x0.rb_mut().as_mat_mut(),
486 par,
487 );
488 }
489 {
490 matmul::triangular::matmul(
491 A01.rb_mut().as_mat_mut(),
492 BlockStructure::Rectangular,
493 Accum::Add,
494 U00,
495 BlockStructure::StrictTriangularLower,
496 x0.rb().as_mat(),
497 BlockStructure::Rectangular,
498 -one::<T>(),
499 par,
500 );
501 *A11 -= dot::inner_prod(U10, Conj::No, x0.rb(), Conj::No);
502 matmul::matmul(
503 A21.rb_mut().as_mat_mut(),
504 Accum::Add,
505 U20,
506 x0.rb().as_mat(),
507 -one::<T>(),
508 par,
509 );
510 }
511 if k + 1 < n {
512 let (mut A11, mut A21) = A21.rb_mut().split_at_row_mut(1);
513 let A11 = &mut A11[0];
514 let HouseholderInfo { tau, .. } =
515 householder::make_householder_in_place(A11, A21.rb_mut());
516 beta[k] = A11.copy();
517 *A11 = one();
518 *T11 = tau.to_cplx();
519 } else {
520 *T11 = infinity();
521 }
522 matmul::matmul(
523 Z1.rb_mut().as_mat_mut(),
524 Accum::Replace,
525 A2.rb(),
526 A21.rb().as_mat(),
527 one(),
528 par,
529 );
530 matmul::matmul(
531 T01.rb_mut().as_mat_mut(),
532 Accum::Replace,
533 U20.adjoint(),
534 A21.rb().as_mat(),
535 one(),
536 par,
537 );
538 }
539}
540#[track_caller]
549pub fn hessenberg_in_place<T: ComplexField>(
550 A: MatMut<'_, T>,
551 householder: MatMut<'_, T>,
552 par: Par,
553 stack: &mut MemStack,
554 params: Spec<HessenbergParams, T>,
555) {
556 let params = params.config;
557 assert!(all(
558 A.nrows() == A.ncols(),
559 householder.ncols() == A.ncols().saturating_sub(1)
560 ));
561 let n = A.nrows().unbound();
562 if n * n < params.blocking_threshold {
563 hessenberg_rearranged_unblocked(A, householder, par, stack, params);
564 } else {
565 hessenberg_gqvdg_blocked(A, householder, par, stack, params);
566 }
567}
568fn hessenberg_gqvdg_blocked<T: ComplexField>(
569 A: MatMut<'_, T>,
570 H: MatMut<'_, T>,
571 par: Par,
572 stack: &mut MemStack,
573 params: HessenbergParams,
574) {
575 let n = A.nrows();
576 let b = H.nrows();
577 let mut A = A;
578 let mut H = H;
579 let (mut Z, stack) = unsafe { temp_mat_uninit(n, b, stack) };
580 let mut Z = Z.as_mat_mut();
581 let mut j = 0;
582 while j < n {
583 let bs = Ord::min(b, n - j);
584 let bs_u = Ord::min(bs, n - j - 1);
585 let (mut beta, stack) = unsafe { temp_mat_uninit(bs, 1, stack) };
586 let mut beta = beta.as_mat_mut().col_mut(0);
587 {
588 let mut T11 = H.rb_mut().submatrix_mut(0, j, bs_u, bs_u);
589 {
590 let A11 = A.rb_mut().submatrix_mut(j, j, n - j, n - j);
591 let Z1 = Z.rb_mut().submatrix_mut(j, 0, n - j, bs);
592 hessenberg_gqvdg_unblocked(
593 A11,
594 Z1,
595 T11.rb_mut(),
596 beta.rb_mut(),
597 par,
598 stack,
599 params,
600 );
601 }
602 let (mut X, _) = unsafe { temp_mat_uninit(n, bs_u, stack) };
603 let mut X = X.as_mat_mut();
604 let (mut X0, X12) = X.rb_mut().split_at_row_mut(j);
605 let (_, mut X2) = X12.split_at_row_mut(bs_u);
606 let (_, Z12) = Z.rb_mut().subcols_mut(0, bs_u).split_at_row_mut(j);
607 let (mut Z1, mut Z2) = Z12.split_at_row_mut(bs_u);
608 let (_, A01, _, A11) = A.rb_mut().split_at_mut(j, j);
609 let (mut A01, mut A02) = A01.split_at_col_mut(bs_u);
610 let (A11, mut A12, A21, mut A22) = A11.split_at_mut(bs_u, bs_u);
611 let U1 = A11.rb();
612 let U2 = A21.rb();
613 let T1 = T11.rb();
614 matmul::triangular::matmul(
615 X0.rb_mut(),
616 BlockStructure::Rectangular,
617 Accum::Replace,
618 A01.rb(),
619 BlockStructure::Rectangular,
620 U1,
621 BlockStructure::StrictTriangularLower,
622 one(),
623 par,
624 );
625 matmul::matmul(X0.rb_mut(), Accum::Add, A02.rb(), U2, one(), par);
626 triangular_solve::solve_lower_triangular_in_place(
627 T1.transpose(),
628 X0.rb_mut().transpose_mut(),
629 par,
630 );
631 matmul::triangular::matmul(
632 A01.rb_mut(),
633 BlockStructure::Rectangular,
634 Accum::Add,
635 X0.rb(),
636 BlockStructure::Rectangular,
637 U1.adjoint(),
638 BlockStructure::StrictTriangularUpper,
639 -one::<T>(),
640 par,
641 );
642 matmul::matmul(
643 A02.rb_mut(),
644 Accum::Add,
645 X0.rb(),
646 U2.adjoint(),
647 -one::<T>(),
648 par,
649 );
650 triangular_solve::solve_lower_triangular_in_place(
651 T1.transpose(),
652 Z1.rb_mut().transpose_mut(),
653 par,
654 );
655 triangular_solve::solve_lower_triangular_in_place(
656 T1.transpose(),
657 Z2.rb_mut().transpose_mut(),
658 par,
659 );
660 matmul::matmul(
661 A12.rb_mut(),
662 Accum::Add,
663 Z1.rb(),
664 U2.adjoint(),
665 -one::<T>(),
666 par,
667 );
668 matmul::matmul(
669 A22.rb_mut(),
670 Accum::Add,
671 Z2.rb(),
672 U2.adjoint(),
673 -one::<T>(),
674 par,
675 );
676 let mut X = X2.rb_mut().transpose_mut();
677 matmul::triangular::matmul(
678 X.rb_mut(),
679 BlockStructure::Rectangular,
680 Accum::Replace,
681 U1.adjoint(),
682 BlockStructure::StrictTriangularUpper,
683 A12.rb(),
684 BlockStructure::Rectangular,
685 one(),
686 par,
687 );
688 matmul::matmul(
689 X.rb_mut(),
690 Accum::Add,
691 U2.adjoint(),
692 A22.rb(),
693 one(),
694 par,
695 );
696 triangular_solve::solve_lower_triangular_in_place(
697 T1.adjoint(),
698 X.rb_mut(),
699 par,
700 );
701 matmul::triangular::matmul(
702 A12.rb_mut(),
703 BlockStructure::Rectangular,
704 Accum::Add,
705 U1,
706 BlockStructure::StrictTriangularLower,
707 X.rb(),
708 BlockStructure::Rectangular,
709 -one::<T>(),
710 par,
711 );
712 matmul::matmul(
713 A22.rb_mut(),
714 Accum::Add,
715 U2,
716 X.rb(),
717 -one::<T>(),
718 par,
719 );
720 }
721 let n = n - j;
722 let mut A = A.rb_mut().submatrix_mut(j, j, n, bs);
723 for k in 0..bs {
724 if k + 1 < n {
725 A[(k + 1, k)] = beta[k].copy();
726 }
727 }
728 j += bs;
729 }
730}
731#[cfg(test)]
732mod tests {
733 use super::*;
734 use crate::stats::prelude::*;
735 use crate::utils::approx::*;
736 use crate::{Mat, assert, c64};
737 use dyn_stack::MemBuffer;
738 use std::mem::MaybeUninit;
739 #[test]
740 fn test_hessenberg_real() {
741 let rng = &mut StdRng::seed_from_u64(0);
742 for n in [3, 4, 8, 16] {
743 let A = CwiseMatDistribution {
744 nrows: n,
745 ncols: n,
746 dist: StandardNormal,
747 }
748 .rand::<Mat<f64>>(rng);
749 let b = 3;
750 let mut H = Mat::zeros(b, n - 1);
751 let mut V = A.clone();
752 let mut V = V.as_mut();
753 hessenberg_rearranged_unblocked(
754 V.rb_mut(),
755 H.as_mut(),
756 Par::Seq,
757 MemStack::new(&mut [MaybeUninit::uninit(); 1024]),
758 auto!(f64),
759 );
760 let mut A = A.clone();
761 let mut A = A.as_mut();
762 for iter in 0..2 {
763 let mut A = if iter == 0 {
764 A.rb_mut()
765 } else {
766 A.rb_mut().transpose_mut()
767 };
768 let n = n - 1;
769 let V = V.rb().submatrix(1, 0, n, n);
770 let mut A = A.rb_mut().subrows_mut(1, n);
771 let H = H.as_ref();
772 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
773 V,
774 H.as_ref(),
775 if iter == 0 { Conj::Yes } else { Conj::No },
776 A.rb_mut(),
777 Par::Seq,
778 MemStack::new(&mut MemBuffer::new(
779 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<f64>(n, b, n + 1),
780 )),
781 );
782 }
783 let approx_eq = CwiseMat(ApproxEq::<f64>::eps());
784 for j in 0..n {
785 for i in 0..n {
786 if i > j + 1 {
787 V[(i, j)] = 0.0;
788 }
789 }
790 }
791 assert!(V ~ A);
792 }
793 }
794 #[test]
795 fn test_hessenberg_cplx() {
796 let rng = &mut StdRng::seed_from_u64(0);
797 for n in [1, 2, 3, 4, 8, 16] {
798 for par in [Par::Seq, Par::rayon(4)] {
799 let A = CwiseMatDistribution {
800 nrows: n,
801 ncols: n,
802 dist: ComplexDistribution::new(
803 StandardNormal,
804 StandardNormal,
805 ),
806 }
807 .rand::<Mat<c64>>(rng);
808 let b = 3;
809 let mut H = Mat::zeros(b, n - 1);
810 let mut V = A.clone();
811 let mut V = V.as_mut();
812 hessenberg_rearranged_unblocked(
813 V.rb_mut(),
814 H.as_mut(),
815 par,
816 MemStack::new(&mut [MaybeUninit::uninit(); 8 * 1024]),
817 HessenbergParams {
818 par_threshold: 0,
819 ..auto!(c64)
820 }
821 .into(),
822 );
823 let mut A = A.clone();
824 let mut A = A.as_mut();
825 for iter in 0..2 {
826 let mut A = if iter == 0 {
827 A.rb_mut()
828 } else {
829 A.rb_mut().transpose_mut()
830 };
831 let n = n - 1;
832 let V = V.rb().submatrix(1, 0, n, n);
833 let mut A = A.rb_mut().subrows_mut(1, n);
834 let H = H.as_ref();
835 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
836 V,
837 H.as_ref(),
838 if iter == 0 { Conj::Yes } else { Conj::No },
839 A.rb_mut(),
840 Par::Seq,
841 MemStack::new(&mut MemBuffer::new(
842 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<c64>(n, b, n + 1),
843 )),
844 );
845 }
846 let approx_eq = CwiseMat(ApproxEq::eps());
847 for j in 0..n {
848 for i in 0..n {
849 if i > j + 1 {
850 V[(i, j)] = c64::ZERO;
851 }
852 }
853 }
854 assert!(V ~ A);
855 }
856 }
857 }
858 #[test]
859 fn test_hessenberg_cplx_gqvdg() {
860 let rng = &mut StdRng::seed_from_u64(0);
861 for n in [2, 3, 4, 8, 16, 21] {
862 for par in [Par::Seq, Par::rayon(4)] {
863 let b = 4;
864 let A = CwiseMatDistribution {
865 nrows: n,
866 ncols: n,
867 dist: ComplexDistribution::new(
868 StandardNormal,
869 StandardNormal,
870 ),
871 }
872 .rand::<Mat<c64, _, _>>(rng);
873 let mut H = Mat::zeros(b, n - 1);
874 let mut V = A.clone();
875 let mut V = V.as_mut();
876 hessenberg_gqvdg_blocked(
877 V.rb_mut(),
878 H.as_mut(),
879 par,
880 MemStack::new(&mut [MaybeUninit::uninit(); 16 * 1024]),
881 HessenbergParams {
882 par_threshold: 0,
883 ..auto!(c64)
884 }
885 .into(),
886 );
887 let mut A = A.clone();
888 let mut A = A.as_mut();
889 for iter in 0..2 {
890 let mut A = if iter == 0 {
891 A.rb_mut()
892 } else {
893 A.rb_mut().transpose_mut()
894 };
895 let n = n - 1;
896 let V = V.rb().submatrix(1, 0, n, n);
897 let mut A = A.rb_mut().subrows_mut(1, n);
898 let H = H.as_ref().subcols(0, n);
899 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
900 V,
901 H.as_ref(),
902 if iter == 0 { Conj::Yes } else { Conj::No },
903 A.rb_mut(),
904 Par::Seq,
905 MemStack::new(&mut MemBuffer::new(
906 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<c64>(n, b, n + 1),
907 )),
908 );
909 }
910 let approx_eq = CwiseMat(ApproxEq::eps());
911 for j in 0..n {
912 for i in 0..n {
913 if i > j + 1 {
914 V[(i, j)] = c64::ZERO;
915 }
916 }
917 }
918 assert!(V ~ A);
919 }
920 }
921 }
922}