1use crate::assert;
2use crate::internal_prelude::*;
3use crate::utils::cost::{ParThreshold, rate};
4use linalg::householder;
5use linalg::matmul::triangular::BlockStructure;
6use linalg::matmul::{self, dot};
7#[derive(Copy, Clone, Debug)]
9pub struct TridiagParams {
10 pub par_threshold: ParThreshold,
14 #[doc(hidden)]
15 pub non_exhaustive: NonExhaustive,
16}
17impl<T: ComplexField> Auto<T> for TridiagParams {
18 fn auto() -> Self {
19 Self {
20 par_threshold: ParThreshold::Derived,
21 non_exhaustive: NonExhaustive(()),
22 }
23 }
24}
25pub fn tridiag_in_place_scratch<T: ComplexField>(
28 dim: usize,
29 par: ParShape,
30 params: Spec<TridiagParams, T>,
31) -> StackReq {
32 _ = par;
33 _ = params;
34 StackReq::all_of(&[
35 temp_mat_scratch::<T>(dim, 1).array(2),
36 temp_mat_scratch::<T>(dim, par.degree()),
37 ])
38}
39fn tridiag_fused_op_simd<T: ComplexField>(
40 A: MatMut<'_, T, usize, usize, ContiguousFwd>,
41 y2: ColMut<'_, T, usize>,
42 z2: ColMut<'_, T, usize, ContiguousFwd>,
43 ry2: ColRef<'_, T, usize>,
44 rz2: ColRef<'_, T, usize, ContiguousFwd>,
45 u0: ColRef<'_, T, usize, ContiguousFwd>,
46 u1: ColRef<'_, T, usize>,
47 u2: ColRef<'_, T, usize>,
48 v2: ColRef<'_, T, usize, ContiguousFwd>,
49 f: T,
50 align: usize,
51) {
52 struct Impl<'a, 'M, 'N, T: ComplexField> {
53 A: MatMut<'a, T, Dim<'M>, Dim<'N>, ContiguousFwd>,
54 y2: ColMut<'a, T, Dim<'N>>,
55 z2: ColMut<'a, T, Dim<'M>, ContiguousFwd>,
56 ry2: ColRef<'a, T, Dim<'N>>,
57 rz2: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
58 u0: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
59 u1: ColRef<'a, T, Dim<'N>>,
60 u2: ColRef<'a, T, Dim<'N>>,
61 v2: ColRef<'a, T, Dim<'M>, ContiguousFwd>,
62 f: T,
63 align: usize,
64 }
65 impl<'a, 'M, 'N, T: ComplexField> pulp::WithSimd for Impl<'a, 'M, 'N, T> {
66 type Output = ();
67
68 #[inline(always)]
69 fn with_simd<S: pulp::Simd>(self, simd: S) -> Self::Output {
70 let Self {
71 mut A,
72 mut y2,
73 mut z2,
74 ry2,
75 rz2,
76 u0,
77 u1,
78 u2,
79 v2,
80 ref f,
81 mut align,
82 } = self;
83 let simd = T::simd_ctx(simd);
84 let (m, n) = A.shape();
85 {
86 let simd = SimdCtx::<T, S>::new_align(simd, m, align);
87 simd_iter!(for i in [simd.indices()] {
88 simd.write(z2.rb_mut(), i, simd.zero());
89 });
90 }
91 for j in n.indices() {
92 let i = m.idx_inc(*j);
93 with_dim!(m, *m - *j);
94 let simd = SimdCtx::<T, S>::new_align(simd, m, align);
95 align -= 1;
96 let mut A = A.rb_mut().col_mut(j).subrows_mut(i, m);
97 let mut z = z2.rb_mut().subrows_mut(i, m);
98 let rz = rz2.subrows(i, m);
99 let ua = u0.subrows(i, m);
100 let v = v2.subrows(i, m);
101 let y = y2.rb_mut().at_mut(j);
102 let ry = simd.splat(&(-&ry2[j]));
103 let ub = simd.splat(&(-&u1[j]));
104 let uc = simd.splat(&(f * &u2[j]));
105 let mut acc = [simd.zero(); 4];
106 simd_iter!(for (IDX, i) in [simd.batch_indices(); 4] {
107 let mut a = simd.read(A.rb(), i);
108 a = simd.conj_mul_add(ry, simd.read(ua, i), a);
109 a = simd.conj_mul_add(ub, simd.read(rz, i), a);
110 simd.write(A.rb_mut(), i, a);
111 let tmp = simd.read(z.rb(), i);
112 simd.write(z.rb_mut(), i, simd.mul_add(a, uc, tmp));
113 acc[IDX] = simd.conj_mul_add(a, simd.read(v, i), acc[IDX]);
114 });
115 let acc0 = simd.add(acc[0], acc[1]);
116 let acc2 = simd.add(acc[2], acc[3]);
117 let acc0 = simd.add(acc0, acc2);
118 let acc0 = simd.reduce_sum(acc0);
119 let i0 = m.idx(0);
120 *y = f * (acc0 - &A[i0] * &v[i0]);
121 }
122 }
123 }
124 with_dim!(M, A.nrows());
125 with_dim!(N, A.ncols());
126 dispatch!(
127 Impl {
128 A: A.as_shape_mut(M, N),
129 y2: y2.as_row_shape_mut(N),
130 z2: z2.as_row_shape_mut(M),
131 ry2: ry2.as_row_shape(N),
132 rz2: rz2.as_row_shape(M),
133 u0: u0.as_row_shape(M),
134 u1: u1.as_row_shape(N),
135 u2: u2.as_row_shape(N),
136 v2: v2.as_row_shape(M),
137 f,
138 align,
139 },
140 Impl,
141 T
142 )
143}
144fn tridiag_fused_op<T: ComplexField>(
145 A: MatMut<'_, T>,
146 y2: ColMut<'_, T>,
147 z2: ColMut<'_, T>,
148 ry2: ColRef<'_, T>,
149 rz2: ColRef<'_, T>,
150 u0: ColRef<'_, T>,
151 u1: ColRef<'_, T>,
152 u2: ColRef<'_, T>,
153 v2: ColRef<'_, T>,
154 f: T,
155 align: usize,
156) {
157 let mut A = A;
158 let mut z2 = z2;
159 if const { T::SIMD_CAPABILITIES.is_simd() } {
160 if let (Some(A), Some(z2), Some(rz2), Some(u0), Some(v2)) = (
161 A.rb_mut().try_as_col_major_mut(),
162 z2.rb_mut().try_as_col_major_mut(),
163 rz2.try_as_col_major(),
164 u0.try_as_col_major(),
165 v2.try_as_col_major(),
166 ) {
167 tridiag_fused_op_simd(
168 A, y2, z2, ry2, rz2, u0, u1, u2, v2, f, align,
169 );
170 } else {
171 tridiag_fused_op_fallback(A, y2, z2, ry2, rz2, u0, u1, u2, v2, f);
172 }
173 } else {
174 tridiag_fused_op_fallback(A, y2, z2, ry2, rz2, u0, u1, u2, v2, f);
175 }
176}
177fn tridiag_fused_op_fallback<T: ComplexField>(
178 A: MatMut<'_, T>,
179 y2: ColMut<'_, T>,
180 z2: ColMut<'_, T>,
181 ry2: ColRef<'_, T>,
182 rz2: ColRef<'_, T>,
183 u0: ColRef<'_, T>,
184 u1: ColRef<'_, T>,
185 u2: ColRef<'_, T>,
186 v2: ColRef<'_, T>,
187 f: T,
188) {
189 let mut par = Par::Seq;
190 let mut A = A;
191 let mut y2 = y2;
192 let n = A.ncols();
193 let (mut A0, mut A1) = A.rb_mut().split_at_row_mut(n);
194 let (u00, u01) = u0.split_at_row(n);
195 let (v20, v21) = v2.split_at_row(n);
196 let (mut z20, mut z21) = z2.split_at_row_mut(n);
197 let (rz20, rz21) = rz2.split_at_row(n);
198 matmul::triangular::matmul(
199 A0.rb_mut(),
200 BlockStructure::TriangularLower,
201 Accum::Add,
202 u00,
203 BlockStructure::Rectangular,
204 ry2.adjoint(),
205 BlockStructure::Rectangular,
206 -one::<T>(),
207 par.reborrow(),
208 );
209 matmul::triangular::matmul(
210 A0.rb_mut(),
211 BlockStructure::TriangularLower,
212 Accum::Add,
213 rz20,
214 BlockStructure::Rectangular,
215 u1.adjoint(),
216 BlockStructure::Rectangular,
217 -one::<T>(),
218 par.reborrow(),
219 );
220 matmul::matmul(
221 A1.rb_mut(),
222 Accum::Add,
223 u01,
224 ry2.adjoint(),
225 -one::<T>(),
226 par.reborrow(),
227 );
228 matmul::matmul(
229 A1.rb_mut(),
230 Accum::Add,
231 rz21,
232 u1.adjoint(),
233 -one::<T>(),
234 par.reborrow(),
235 );
236 matmul::triangular::matmul(
237 z20.rb_mut(),
238 BlockStructure::Rectangular,
239 Accum::Replace,
240 A0.rb(),
241 BlockStructure::TriangularLower,
242 u2,
243 BlockStructure::Rectangular,
244 f.clone(),
245 par.reborrow(),
246 );
247 matmul::triangular::matmul(
248 y2.rb_mut(),
249 BlockStructure::Rectangular,
250 Accum::Replace,
251 A0.rb().adjoint(),
252 BlockStructure::StrictTriangularUpper,
253 v20,
254 BlockStructure::Rectangular,
255 f.clone(),
256 par.reborrow(),
257 );
258 matmul::matmul(z21.rb_mut(), Accum::Replace, A1.rb(), u2, f.clone(), par.reborrow());
259 matmul::matmul(
260 y2.rb_mut(),
261 Accum::Add,
262 A1.rb().adjoint(),
263 v21,
264 f.clone(),
265 par,
266 );
267}
268pub fn tridiag_in_place<T: ComplexField>(
278 A: MatMut<'_, T>,
279 householder: MatMut<'_, T>,
280 par: Par,
281 stack: &mut MemStack,
282 params: Spec<TridiagParams, T>,
283) {
284 let params = params.config;
285 let mut A = A;
286 let mut H = householder;
287 let mut par = par;
288 let n = A.nrows();
289 let b = H.nrows();
290 assert!(H.ncols() == n.saturating_sub(1));
291 if n == 0 {
292 return;
293 }
294 alloca!('stack: {
295 let mut y = unsafe { col![uninit::<T>, n] };
296 let mut w = unsafe { col![uninit::<T>, n] };
297 let mut z = unsafe { mat![uninit::<T>, n, par.degree()] };
298 });
299 {
300 let mut H = H.rb_mut().row_mut(0);
301 for k in 0..n {
302 let (_, A01, A10, A11) = A.rb_mut().split_at_mut(k, k);
303 let (_, _) = A01.split_first_col().unwrap();
304 let (_, A20) = A10.split_first_row_mut().unwrap();
305 let (mut A11, _, A21, mut A22) = A11.split_at_mut(1, 1);
306 let mut A21 = A21.col_mut(0);
307 let a11 = &mut A11[(0, 0)];
308 let (y1, mut y2) =
309 y.rb_mut().split_at_row_mut(k).1.split_at_row_mut(1);
310 if k > 0 {
311 let y1 = y1[0].copy();
312 let p = k - 1;
313 let u2 = A20.rb().col(p);
314 *a11 -= &y1 + y1.conj();
315 z!(A21.rb_mut(), u2, y2.rb()).for_each(
316 |uz!(a, u, y): Zip!(&mut T, &T, &T)| {
317 *a -= y1.conj() * u + y;
318 },
319 );
320 }
321 if k + 1 == n {
322 break;
323 }
324 let rem = n - k - 1;
325 if !params.par_threshold.region_pays::<T>(
328 par.shape(),
329 rem * rem / 2,
330 rate::tridiag(),
331 1,
332 ) {
333 par = Par::Seq;
334 }
335 let k1 = k + 1;
336 let tau_inv;
337 {
338 let (mut a11, mut x2) = A21.rb_mut().split_at_row_mut(1);
339 let a11 = &mut a11[0];
340 let householder::HouseholderInfo { tau, .. } =
341 householder::make_householder_in_place(a11, x2.rb_mut());
342 tau_inv = tau.real().recip();
343 H[k] = tau.to_cplx();
344 let mut z2 = z.rb_mut().split_at_row_mut(k + 2).1;
345 let mut w2 = w.rb_mut().split_at_row_mut(k + 2).1;
346 let (mut y1, mut y2) = y2.rb_mut().split_at_row_mut(1);
347 let y1 = &mut y1[0];
348 let (A1, A2) = A22.rb_mut().split_at_row_mut(1);
349 let A1 = A1.row_mut(0);
350 let (mut a11, _) = A1.split_at_col_mut(1);
351 let a11 = &mut a11[0];
352 let (A21, mut A22) = A2.split_at_col_mut(1);
353 let mut A21 = A21.col_mut(0);
354 if k > 0 {
355 let p = k - 1;
356 let (u1, u2) = A20.rb().col(p).split_at_row(1);
357 let ref u1 = u1[0].copy();
358 *a11 -= u1 * y1.conj() + &*y1 * u1.conj();
359 z!(A21.rb_mut(), u2.rb(), y2.rb()).for_each(
360 |uz!(a, u, y): Zip!(&mut T, &T, &T)| {
361 *a -= u * y1.conj() + &*y * u1.conj();
362 },
363 );
364 w2.copy_from(y2.rb());
365 match par {
366 Par::Seq => {
367 let mut z2 = z2.rb_mut().col_mut(0);
368 tridiag_fused_op(
369 A22.rb_mut(),
370 y2.rb_mut(),
371 z2.rb_mut(),
372 w2.rb(),
373 w2.rb(),
374 u2.rb(),
375 u2.rb(),
376 x2.rb(),
377 x2.rb(),
378 tau_inv.to_cplx(),
379 simd_align(k1 + 1),
380 );
381 z!(y2.rb_mut(), z2.rb()).for_each(
382 |uz!(y, z): Zip!(&mut T, &T)| *y += z,
383 );
384 },
385 #[cfg(feature = "rayon")]
386 _ => {
387 let nthreads = core::num::NonZeroUsize::new(par.degree()).unwrap();
388 use rayon::prelude::*;
389 let nthreads = nthreads.get();
390 let mut z2 = z2.rb_mut().subcols_mut(0, nthreads);
391 let n2 = A22.ncols();
392 assert!((n2 as u64) < (1u64 << 50));
393 let idx_to_col_start = |idx: usize| {
394 let idx_as_percent =
395 idx as f64 / nthreads as f64;
396 let col_start_percent = 1.0f64
397 - libm::sqrt(1.0f64 - idx_as_percent);
398 (col_start_percent * n2 as f64) as usize
399 };
400 {
401 let A22 = A22.rb();
402 let y2 = y2.rb();
403 let ref f = tau_inv.to_cplx::<T>();
404 crate::utils::thread::for_each(par.reborrow(),
405 nthreads,
406 z2.rb_mut().par_col_iter_mut().enumerate(),
407 |(idx, mut z2)| {
408 let first = idx_to_col_start(idx);
409 let last_col =
410 idx_to_col_start(idx + 1);
411 let nrows = n2 - first;
412 let ncols = last_col - first;
413 let mut A = unsafe {
414 A22.rb()
415 .subcols(first, ncols)
416 .subrows(first, nrows)
417 .const_cast()
418 };
419 {
420 let y2 = unsafe {
421 y2.subrows(first, ncols)
422 .const_cast()
423 };
424 let mut z2 = z2
425 .rb_mut()
426 .subrows_mut(first, nrows);
427 let ry2 =
428 w2.rb().subrows(first, ncols);
429 let rz2 =
430 w2.rb().subrows(first, nrows);
431 let u0 = u2.subrows(first, nrows);
432 let u1 = u2.subrows(first, ncols);
433 let u2 =
434 x2.rb().subrows(first, ncols);
435 let v2 =
436 x2.rb().subrows(first, nrows);
437 tridiag_fused_op(
438 A.rb_mut(),
439 y2,
440 z2.rb_mut(),
441 ry2,
442 rz2,
443 u0,
444 u1,
445 u2,
446 v2,
447 f.copy(),
448 n.next_power_of_two()
449 - (k1 + 1) - first,
450 );
451 }
452 z2.rb_mut()
453 .subrows_mut(0, first)
454 .fill(zero());
455 },
456 );
457 }
458 for z2 in z2.rb().col_iter() {
459 z!(y2.rb_mut(), z2.rb())
460 .for_each(|uz!(y, z)| *y += z);
461 }
462 },
463 }
464 } else {
465 matmul::triangular::matmul(
466 y2.rb_mut(),
467 BlockStructure::Rectangular,
468 Accum::Replace,
469 A22.rb(),
470 BlockStructure::TriangularLower,
471 x2.rb(),
472 BlockStructure::Rectangular,
473 tau_inv.to_cplx(),
474 par.reborrow(),
475 );
476 matmul::triangular::matmul(
477 y2.rb_mut(),
478 BlockStructure::Rectangular,
479 Accum::Add,
480 A22.rb().adjoint(),
481 BlockStructure::StrictTriangularUpper,
482 x2.rb(),
483 BlockStructure::Rectangular,
484 tau_inv.to_cplx(),
485 par.reborrow(),
486 );
487 }
488 z!(y2.rb_mut(), A21.rb()).for_each(
489 |uz!(y, a): Zip!(&mut T, &T)| {
490 *y += a.mul_real(&tau_inv);
491 },
492 );
493 *y1 = (&*a11
494 + dot::inner_prod(
495 A21.rb().transpose(),
496 Conj::Yes,
497 x2.rb(),
498 Conj::No,
499 ))
500 .mul_real(&tau_inv);
501 let ref b = (&*y1
502 + dot::inner_prod(
503 x2.rb().transpose(),
504 Conj::Yes,
505 y2.rb(),
506 Conj::No,
507 ))
508 .mul_pow2(from_f64::<T::Real>(0.5))
509 .mul_real(&tau_inv);
510 *y1 -= b;
511 z!(y2.rb_mut(), x2.rb()).for_each(
512 |uz!(y, u): Zip!(&mut T, &T)| {
513 *y -= b * u;
514 },
515 );
516 }
517 }
518 }
519 if n > 0 {
520 let n = n - 1;
521 let A = A.rb().submatrix(1, 0, n, n);
522 let mut H = H.rb_mut().subcols_mut(0, n);
523 let mut j = 0;
524 while j < n {
525 let b = Ord::min(b, n - j);
526 let mut H = H.rb_mut().submatrix_mut(0, j, b, b);
527 for k in 0..b {
528 H[(k, k)] = H[(0, k)].copy();
529 }
530 householder::upgrade_householder_factor(
531 H.rb_mut(),
532 A.submatrix(j, j, n - j, b),
533 b,
534 1,
535 par.reborrow(),
536 );
537 j += b;
538 }
539 }
540}
541#[cfg(test)]
542mod tests {
543 use super::*;
544 use crate::stats::prelude::*;
545 use crate::utils::approx::*;
546 use crate::{Mat, assert, c64};
547 use dyn_stack::MemBuffer;
548 #[test]
549 fn test_tridiag_real() {
550 let rng = &mut StdRng::seed_from_u64(0);
551 for n in [2, 3, 4, 8, 16] {
552 let A = CwiseMatDistribution {
553 nrows: n,
554 ncols: n,
555 dist: StandardNormal,
556 }
557 .rand::<Mat<f64>>(rng);
558 let A = A.rb() + A.adjoint();
559 let b = 3;
560 let mut H = Mat::zeros(b, n - 1);
561 let mut V = A.clone();
562 let mut V = V.as_mut();
563 tridiag_in_place(
564 V.rb_mut(),
565 H.rb_mut(),
566 Par::Seq,
567 MemStack::new(&mut MemBuffer::new(StackReq::all_of(&[
568 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<f64>(n - 1, b, n),
569 tridiag_in_place_scratch::<f64>(n, ParShape::Seq, default()),
570 ]))),
571 default(),
572 );
573 let mut A = A.clone();
574 let mut A = A.as_mut();
575 for iter in 0..2 {
576 let mut A = if iter == 0 {
577 A.rb_mut()
578 } else {
579 A.rb_mut().transpose_mut()
580 };
581 let n = n - 1;
582 let V = V.rb().submatrix(1, 0, n, n);
583 let mut A = A.rb_mut().subrows_mut(1, n);
584 let H = H.as_ref();
585 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
586 V,
587 H.as_ref(),
588 if iter == 0 { Conj::Yes } else { Conj::No },
589 A.rb_mut(),
590 Par::Seq,
591 MemStack::new(&mut MemBuffer::new(
592 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<f64>(n, b, n + 1),
593 )),
594 );
595 }
596 let approx_eq = CwiseMat(ApproxEq::<f64>::eps());
597 for j in 0..n {
598 for i in 0..n {
599 if i > j + 1 || j > i + 1 {
600 V[(i, j)] = 0.0;
601 }
602 }
603 }
604 for i in 0..n {
605 if i + 1 < n {
606 V[(i, i + 1)] = V[(i + 1, i)];
607 }
608 }
609 assert!(V ~ A);
610 }
611 }
612 #[test]
613 fn test_tridiag_cplx() {
614 let rng = &mut StdRng::seed_from_u64(0);
615 for n in [2, 3, 4, 8, 16] {
616 let A = CwiseMatDistribution {
617 nrows: n,
618 ncols: n,
619 dist: ComplexDistribution::new(StandardNormal, StandardNormal),
620 }
621 .rand::<Mat<c64>>(rng);
622 let A = A.rb() + A.adjoint();
623 let b = 3;
624 let mut H = Mat::zeros(b, n - 1);
625 let mut V = A.clone();
626 let mut V = V.as_mut();
627 tridiag_in_place(
628 V.rb_mut(),
629 H.as_mut(),
630 Par::Seq,
631 MemStack::new(&mut MemBuffer::new(tridiag_in_place_scratch::<
632 c64,
633 >(
634 n, ParShape::Seq, default()
635 ))),
636 default(),
637 );
638 let mut A = A.clone();
639 let mut A = A.as_mut();
640 for iter in 0..2 {
641 let mut A = if iter == 0 {
642 A.rb_mut()
643 } else {
644 A.rb_mut().transpose_mut()
645 };
646 let n = n - 1;
647 let V = V.rb().submatrix(1, 0, n, n);
648 let mut A = A.rb_mut().subrows_mut(1, n);
649 let H = H.as_ref();
650 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_with_conj(
651 V,
652 H.as_ref(),
653 if iter == 0 { Conj::Yes } else { Conj::No },
654 A.rb_mut(),
655 Par::Seq,
656 MemStack::new(&mut MemBuffer::new(
657 householder::apply_block_householder_sequence_transpose_on_the_left_in_place_scratch::<c64>(n, b, n + 1),
658 )),
659 );
660 }
661 let approx_eq = CwiseMat(ApproxEq::eps());
662 for j in 0..n {
663 for i in 0..n {
664 if i > j + 1 || j > i + 1 {
665 V[(i, j)] = c64::ZERO;
666 }
667 }
668 }
669 for i in 0..n {
670 if i + 1 < n {
671 V[(i, i + 1)] = V[(i + 1, i)].conj();
672 }
673 }
674 assert!(V ~ A);
675 }
676 }
677}