1use crate::internal_prelude_sp::*;
2use crate::{assert, debug_assert};
3pub fn sort_indices<I: Index, T>(
6 col_ptr: &[I],
7 col_nnz: Option<&[I]>,
8 row_idx: &mut [I],
9 val: &mut [T],
10) {
11 assert!(col_ptr.len() > 0);
12 let n = col_ptr.len() - 1;
13 for j in 0..n {
14 let start = col_ptr[j].zx();
15 let end = col_nnz
16 .map(|nnz| start + nnz[j].zx())
17 .unwrap_or(col_ptr[j + 1].zx());
18 unsafe {
19 crate::sort::sort_indices(
20 &mut row_idx[start..end],
21 &mut val[start..end],
22 )
23 };
24 }
25}
26pub fn sort_dedup_indices<I: Index, T: ComplexField>(
29 col_ptr: &[I],
30 col_nnz: &mut [I],
31 row_idx: &mut [I],
32 val: &mut [T],
33) {
34 assert!(col_ptr.len() > 0);
35 let n = col_ptr.len() - 1;
36 for j in 0..n {
37 let start = col_ptr[j].zx();
38 let end = start + col_nnz[j].zx();
39 unsafe {
40 crate::sort::sort_indices(
41 &mut row_idx[start..end],
42 &mut val[start..end],
43 )
44 };
45 let mut prev = I::truncate(usize::MAX);
46 let mut writer = start;
47 let mut reader = start;
48 while reader < end {
49 let cur = row_idx[reader];
50 if cur == prev {
51 writer -= 1;
52 val[writer] = (&val[writer]) + (&val[reader]);
53 } else {
54 val[writer] = val[reader].copy();
55 }
56 prev = cur;
57 reader += 1;
58 writer += 1;
59 }
60 col_nnz[j] = I::truncate(writer - start);
61 }
62}
63pub fn permute_self_adjoint_scratch<I: Index>(dim: usize) -> StackReq {
66 StackReq::new::<I>(dim)
67}
68pub fn permute_dedup_self_adjoint_scratch<I: Index>(dim: usize) -> StackReq {
71 StackReq::new::<I>(dim)
72}
73pub fn permute_self_adjoint<
81 'out,
82 N: Shape,
83 I: Index,
84 T: ComplexField,
85 C: Conjugate<Canonical = T>,
86>(
87 new_val: &'out mut [T],
88 new_col_ptr: &'out mut [I],
89 new_row_idx: &'out mut [I],
90 A: SparseColMatRef<'_, I, C, N, N>,
91 perm: PermRef<'_, I, N>,
92 in_side: Side,
93 out_side: Side,
94 stack: &mut MemStack,
95) -> SparseColMatMut<'out, I, T, N, N> {
96 let n = A.nrows();
97 with_dim!(N, n.unbound());
98 permute_self_adjoint_imp(
99 new_val,
100 new_col_ptr,
101 new_row_idx,
102 A.as_shape(N, N).canonical(),
103 Conj::get::<C>(),
104 perm.as_shape(N),
105 in_side,
106 out_side,
107 true,
108 stack,
109 )
110 .as_shape_mut(n, n)
111}
112pub fn permute_self_adjoint_to_unsorted<
120 'out,
121 N: Shape,
122 I: Index,
123 T: ComplexField,
124 C: Conjugate<Canonical = T>,
125>(
126 new_val: &'out mut [T],
127 new_col_ptr: &'out mut [I],
128 new_row_idx: &'out mut [I],
129 A: SparseColMatRef<'_, I, C, N, N>,
130 perm: PermRef<'_, I, N>,
131 in_side: Side,
132 out_side: Side,
133 stack: &mut MemStack,
134) -> SparseColMatMut<'out, I, T, N, N> {
135 let n = A.nrows();
136 with_dim!(N, n.unbound());
137 permute_self_adjoint_imp(
138 new_val,
139 new_col_ptr,
140 new_row_idx,
141 A.as_shape(N, N).canonical(),
142 Conj::get::<C>(),
143 perm.as_shape(N),
144 in_side,
145 out_side,
146 false,
147 stack,
148 )
149 .as_shape_mut(n, n)
150}
151pub fn permute_dedup_self_adjoint<
160 'out,
161 N: Shape,
162 I: Index,
163 T: ComplexField,
164 C: Conjugate<Canonical = T>,
165>(
166 new_val: &'out mut [T],
167 new_col_ptr: &'out mut [I],
168 new_row_idx: &'out mut [I],
169 A: SparseColMatRef<'_, I, C, N, N>,
170 perm: PermRef<'_, I, N>,
171 in_side: Side,
172 out_side: Side,
173 stack: &mut MemStack,
174) -> SparseColMatMut<'out, I, T, N, N> {
175 let n = A.nrows();
176 with_dim!(N, n.unbound());
177 permute_dedup_self_adjoint_imp(
178 new_val,
179 new_col_ptr,
180 new_row_idx,
181 A.as_shape(N, N).canonical(),
182 Conj::get::<C>(),
183 perm.as_shape(N),
184 in_side,
185 out_side,
186 stack,
187 )
188 .as_shape_mut(n, n)
189}
190fn permute_self_adjoint_imp<'N, 'out, I: Index, T: ComplexField>(
191 new_val: &'out mut [T],
192 new_col_ptr: &'out mut [I],
193 new_row_idx: &'out mut [I],
194 A: SparseColMatRef<'_, I, T, Dim<'N>, Dim<'N>>,
195 conj_A: Conj,
196 perm: PermRef<'_, I, Dim<'N>>,
197 in_side: Side,
198 out_side: Side,
199 sort: bool,
200 stack: &mut MemStack,
201) -> SparseColMatMut<'out, I, T, Dim<'N>, Dim<'N>> {
202 let src_to_cmp = {
203 let mask = match in_side {
204 Side::Lower => 0,
205 Side::Upper => usize::MAX,
206 };
207 move |i: usize| mask ^ i
208 };
209 let dst_to_cmp = {
210 let mask = match out_side {
211 Side::Lower => 0,
212 Side::Upper => usize::MAX,
213 };
214 move |i: usize| mask ^ i
215 };
216 let conj_A = conj_A.is_conj();
217 let N = A.ncols();
218 let n = *N;
219 assert!(new_col_ptr.len() == n + 1);
220 let (_, perm_inv) = perm.bound_arrays();
221 let (mut cur_row_pos, _) = stack.collect(repeat_n!(I::truncate(0), n));
222 let cur_row_pos = Array::from_mut(&mut cur_row_pos, N);
223 let col_counts = &mut *cur_row_pos;
224 for old_j in N.indices() {
225 let new_j = perm_inv[old_j].zx();
226 let old_j_cmp = src_to_cmp(*old_j);
227 let new_j_cmp = dst_to_cmp(*new_j);
228 for old_i in A.row_idx_of_col(old_j) {
229 let new_i = perm_inv[old_i].zx();
230 let old_i_cmp = src_to_cmp(*old_i);
231 let new_i_cmp = dst_to_cmp(*new_i);
232 if old_i_cmp >= old_j_cmp {
233 let lower = new_i_cmp >= new_j_cmp;
234 let new_j = if lower { new_j } else { new_i };
235 col_counts[new_j] += I::truncate(1);
236 }
237 }
238 }
239 new_col_ptr[0] = I::truncate(0);
240 for (count, [ci0, ci1]) in iter::zip(
241 col_counts.as_mut(),
242 windows2(Cell::as_slice_of_cells(Cell::from_mut(&mut *new_col_ptr))),
243 ) {
244 let ci0 = ci0.get();
245 ci1.set(ci0 + *count);
246 *count = ci0;
247 }
248 let nnz = new_col_ptr[n].zx();
249 let new_row_idx = &mut new_row_idx[..nnz];
250 let new_val = &mut new_val[..nnz];
251 {
252 with_dim!(NNZ, nnz);
253 let new_val = Array::from_mut(new_val, NNZ);
254 let new_row_idx = Array::from_mut(new_row_idx, NNZ);
255 let conj_if = |cond: bool, x: &T| -> T {
256 if const { T::IS_REAL } {
257 x.copy()
258 } else {
259 if cond != conj_A { x.conj() } else { x.copy() }
260 }
261 };
262 for old_j in N.indices() {
263 let new_j = perm_inv[old_j].zx();
264 let old_j_cmp = src_to_cmp(*old_j);
265 let new_j_cmp = dst_to_cmp(*new_j);
266 for (old_i, val) in
267 iter::zip(A.row_idx_of_col(old_j), A.val_of_col(old_j))
268 {
269 let new_i = perm_inv[old_i].zx();
270 let old_i_cmp = src_to_cmp(*old_i);
271 let new_i_cmp = dst_to_cmp(*new_i);
272 if old_i_cmp >= old_j_cmp {
273 let lower = new_i_cmp >= new_j_cmp;
274 let (new_j, new_i) = if lower {
275 (new_j, new_i)
276 } else {
277 (new_i, new_j)
278 };
279 let cur_row_pos = &mut cur_row_pos[new_j];
280 let row_pos =
281 unsafe { Idx::new_unchecked(cur_row_pos.zx(), NNZ) };
282 *cur_row_pos += I::truncate(1);
283 new_val[row_pos] = conj_if(!lower, val);
284 new_row_idx[row_pos] = I::truncate(*new_i);
285 }
286 }
287 }
288 }
289 if sort {
290 sort_indices(new_col_ptr, None, new_row_idx, new_val);
291 }
292 unsafe {
293 SparseColMatMut::new(
294 SymbolicSparseColMatRef::new_unchecked(
295 N,
296 N,
297 new_col_ptr,
298 None,
299 new_row_idx,
300 ),
301 new_val,
302 )
303 }
304}
305fn permute_dedup_self_adjoint_imp<'N, 'out, I: Index, T: ComplexField>(
306 new_val: &'out mut [T],
307 new_col_ptr: &'out mut [I],
308 new_row_idx: &'out mut [I],
309 A: SparseColMatRef<'_, I, T, Dim<'N>, Dim<'N>>,
310 conj_A: Conj,
311 perm: PermRef<'_, I, Dim<'N>>,
312 in_side: Side,
313 out_side: Side,
314 stack: &mut MemStack,
315) -> SparseColMatMut<'out, I, T, Dim<'N>, Dim<'N>> {
316 let N = A.nrows();
317 permute_self_adjoint_imp(
318 new_val,
319 new_col_ptr,
320 new_row_idx,
321 A,
322 conj_A,
323 perm,
324 in_side,
325 out_side,
326 false,
327 stack,
328 );
329 {
330 let new_col_ptr = Cell::as_slice_of_cells(Cell::from_mut(new_col_ptr));
331 let start = Array::from_ref(&new_col_ptr[..*N], N);
332 let end = Array::from_ref(&new_col_ptr[1..], N);
333 let mut writer = 0usize;
334 for j in N.indices() {
335 let start = start[j].replace(I::truncate(writer)).zx();
336 let end = end[j].get().zx();
337 unsafe {
338 crate::sort::sort_indices(
339 &mut new_row_idx[start..end],
340 &mut new_val[start..end],
341 );
342 }
343 let mut prev = I::truncate(usize::MAX);
344 let mut reader = start;
345 while reader < end {
346 let cur = new_row_idx[reader];
347 if cur == prev {
348 writer -= 1;
349 new_val[writer] = (&new_val[writer]) + (&new_val[reader]);
350 } else {
351 new_row_idx[writer] = new_row_idx[reader];
352 new_val[writer] = new_val[reader].copy();
353 }
354 prev = cur;
355 reader += 1;
356 writer += 1;
357 }
358 }
359 new_col_ptr[*N].set(I::truncate(writer));
360 }
361 unsafe {
362 SparseColMatMut::new(
363 SymbolicSparseColMatRef::new_unchecked(
364 N,
365 N,
366 new_col_ptr,
367 None,
368 new_row_idx,
369 ),
370 new_val,
371 )
372 }
373}
374pub fn transpose_scratch<I: Index>(nrows: usize, ncols: usize) -> StackReq {
376 _ = ncols;
377 StackReq::new::<usize>(nrows)
378}
379pub fn transpose_dedup_scratch<I: Index>(
382 nrows: usize,
383 ncols: usize,
384) -> StackReq {
385 _ = ncols;
386 StackReq::new::<usize>(nrows).array(2)
387}
388pub fn transpose<'out, Rows: Shape, Cols: Shape, I: Index, T: Clone>(
396 new_val: &'out mut [T],
397 new_col_ptr: &'out mut [I],
398 new_row_idx: &'out mut [I],
399 A: SparseColMatRef<'_, I, T, Rows, Cols>,
400 stack: &mut MemStack,
401) -> SparseColMatMut<'out, I, T, Cols, Rows> {
402 let (m, n) = A.shape();
403 with_dim!(M, m.unbound());
404 with_dim!(N, n.unbound());
405 transpose_imp(
406 T::clone,
407 new_val,
408 new_col_ptr,
409 new_row_idx,
410 A.as_shape(M, N),
411 stack,
412 )
413 .as_shape_mut(n, m)
414}
415pub fn adjoint<'out, Rows: Shape, Cols: Shape, I: Index, T: ComplexField>(
423 new_val: &'out mut [T],
424 new_col_ptr: &'out mut [I],
425 new_row_idx: &'out mut [I],
426 A: SparseColMatRef<'_, I, T, Rows, Cols>,
427 stack: &mut MemStack,
428) -> SparseColMatMut<'out, I, T, Cols, Rows> {
429 let (m, n) = A.shape();
430 with_dim!(M, m.unbound());
431 with_dim!(N, n.unbound());
432 transpose_imp(
433 conj::<T>,
434 new_val,
435 new_col_ptr,
436 new_row_idx,
437 A.as_shape(M, N),
438 stack,
439 )
440 .as_shape_mut(n, m)
441}
442pub fn transpose_dedup<
450 'out,
451 Rows: Shape,
452 Cols: Shape,
453 I: Index,
454 T: ComplexField,
455 C: Conjugate<Canonical = T>,
456>(
457 new_val: &'out mut [T],
458 new_col_ptr: &'out mut [I],
459 new_row_idx: &'out mut [I],
460 A: SparseColMatRef<'_, I, C, Rows, Cols>,
461 stack: &mut MemStack,
462) -> SparseColMatMut<'out, I, T, Cols, Rows> {
463 let (m, n) = A.shape();
464 with_dim!(M, m.unbound());
465 with_dim!(N, n.unbound());
466 transpose_dedup_imp(
467 new_val,
468 new_col_ptr,
469 new_row_idx,
470 A.as_shape(M, N),
471 stack,
472 )
473 .as_shape_mut(n, m)
474}
475fn transpose_imp<'ROWS, 'COLS, 'out, I: Index, T>(
476 clone: impl Fn(&T) -> T,
477 new_val: &'out mut [T],
478 new_col_ptr: &'out mut [I],
479 new_row_idx: &'out mut [I],
480 A: SparseColMatRef<'_, I, T, Dim<'ROWS>, Dim<'COLS>>,
481 stack: &mut MemStack,
482) -> SparseColMatMut<'out, I, T, Dim<'COLS>, Dim<'ROWS>> {
483 let (M, N) = A.shape();
484 assert!(new_col_ptr.len() == *M + 1);
485 let (mut col_count, _) = stack.collect(repeat_n!(I::truncate(0), *M));
486 let col_count = Array::from_mut(&mut col_count, M);
487 for j in N.indices() {
488 for i in A.row_idx_of_col(j) {
489 col_count[i] += I::truncate(1);
490 }
491 }
492 new_col_ptr[0] = I::truncate(0);
493 for (j, [pj0, pj1]) in iter::zip(
494 M.indices(),
495 windows2(Cell::as_slice_of_cells(Cell::from_mut(new_col_ptr))),
496 ) {
497 let cj = &mut col_count[j];
498 let pj = pj0.get();
499 pj1.set(pj + *cj);
500 *cj = pj;
501 }
502 let new_row_idx = &mut new_row_idx[..new_col_ptr[*M].zx()];
503 let new_val = &mut new_val[..new_col_ptr[*M].zx()];
504 let cur_row_pos = col_count;
505 for j in N.indices() {
506 for (i, val) in iter::zip(A.row_idx_of_col(j), A.val_of_col(j)) {
507 let ci = &mut cur_row_pos[i];
508 unsafe {
509 let ci = ci.zx();
510 *new_row_idx.get_unchecked_mut(ci) = I::truncate(*j);
511 *new_val.get_unchecked_mut(ci) = clone(val);
512 }
513 *ci += I::truncate(1);
514 }
515 }
516 debug_assert!(cur_row_pos.as_ref() == &new_col_ptr[1..]);
517 unsafe {
518 SparseColMatMut::new(
519 SymbolicSparseColMatRef::new_unchecked(
520 N,
521 M,
522 new_col_ptr,
523 None,
524 new_row_idx,
525 ),
526 new_val,
527 )
528 }
529}
530fn transpose_dedup_imp<
531 'ROWS,
532 'COLS,
533 'out,
534 I: Index,
535 T: ComplexField,
536 C: Conjugate<Canonical = T>,
537>(
538 new_val: &'out mut [T],
539 new_col_ptr: &'out mut [I],
540 new_row_idx: &'out mut [I],
541 A: SparseColMatRef<'_, I, C, Dim<'ROWS>, Dim<'COLS>>,
542 stack: &mut MemStack,
543) -> SparseColMatMut<'out, I, T, Dim<'COLS>, Dim<'ROWS>> {
544 let (M, N) = A.shape();
545 assert!(new_col_ptr.len() == *M + 1);
546 let A = A.canonical();
547 let sentinel = I::truncate(usize::MAX);
548 let (mut col_count, stack) = stack.collect(repeat_n!(I::truncate(0), *M));
549 let (mut last_seen, _) = stack.collect(repeat_n!(sentinel, *M));
550 let col_count = Array::from_mut(&mut col_count, M);
551 let last_seen = Array::from_mut(&mut last_seen, M);
552 for j in N.indices() {
553 for i in A.row_idx_of_col(j) {
554 let j = I::truncate(*j);
555 if last_seen[i] == j {
556 continue;
557 }
558 last_seen[i] = j;
559 col_count[i] += I::truncate(1);
560 }
561 }
562 new_col_ptr[0] = I::truncate(0);
563 for (j, [pj0, pj1]) in iter::zip(
564 M.indices(),
565 windows2(Cell::as_slice_of_cells(Cell::from_mut(new_col_ptr))),
566 ) {
567 let cj = &mut col_count[j];
568 let pj = pj0.get();
569 pj1.set(pj + *cj);
570 *cj = pj;
571 }
572 last_seen.as_mut().fill(sentinel);
573 let new_row_idx = &mut new_row_idx[..new_col_ptr[*M].zx()];
574 let new_val = &mut new_val[..new_col_ptr[*M].zx()];
575 let cur_row_pos = col_count;
576 for j in N.indices() {
577 for (i, val) in iter::zip(A.row_idx_of_col(j), A.val_of_col(j)) {
578 let ci = &mut cur_row_pos[i];
579 let val = if Conj::get::<C>().is_conj() {
580 val.conj()
581 } else {
582 val.copy()
583 };
584 let j = I::truncate(*j);
585 unsafe {
586 if last_seen[i] == j {
587 let ci = ci.zx() - 1;
588 *new_val.get_unchecked_mut(ci) =
589 new_val.get_unchecked(ci) + (&val);
590 } else {
591 last_seen[i] = j;
592 *ci += I::truncate(1);
593 let ci = ci.zx() - 1;
594 {
595 *new_row_idx.get_unchecked_mut(ci) = j;
596 *new_val.get_unchecked_mut(ci) = val;
597 }
598 }
599 }
600 }
601 }
602 debug_assert!(cur_row_pos.as_ref() == &new_col_ptr[1..]);
603 unsafe {
604 SparseColMatMut::new(
605 SymbolicSparseColMatRef::new_unchecked(
606 N,
607 M,
608 new_col_ptr,
609 None,
610 new_row_idx,
611 ),
612 new_val,
613 )
614 }
615}
616#[cfg(test)]
617mod tests {
618 use super::*;
619 use crate::assert;
620 use crate::stats::prelude::*;
621 use dyn_stack::MemBuffer;
622 #[test]
623 fn test_transpose() {
624 let nrows = 5;
625 let ncols = 3;
626 let A = SparseColMatRef::new(
627 SymbolicSparseColMatRef::new_unsorted_checked(
628 nrows,
629 ncols,
630 &[0usize, 4, 8, 11],
631 None,
632 &[0, 0, 2, 4, 2, 1, 1, 0, 0, 1, 3],
633 ),
634 &[1.0, 2.0, 3.0, 4.0, 11.0, 12.0, 13.0, 14.0, 21.0, 22.0, 23.0],
635 );
636 let nnz = A.compute_nnz();
637 let new_col_ptr = &mut *vec![0usize; nrows + 1];
638 let new_row_idx = &mut *vec![0usize; nnz];
639 let new_val = &mut *vec![0.0; nnz];
640 {
641 let out = transpose(
642 new_val,
643 new_col_ptr,
644 new_row_idx,
645 A,
646 MemStack::new(&mut MemBuffer::new(transpose_scratch::<usize>(
647 nrows, ncols,
648 ))),
649 )
650 .into_const();
651 let target = SparseColMatRef::new(
652 SymbolicSparseColMatRef::new_unsorted_checked(
653 ncols,
654 nrows,
655 &[0usize, 4, 7, 9, 10, 11],
656 None,
657 &[0, 0, 1, 2, 1, 1, 2, 0, 1, 2, 0],
658 ),
659 &[1.0, 2.0, 14.0, 21.0, 12.0, 13.0, 22.0, 3.0, 11.0, 23.0, 4.0],
660 );
661 assert!(all(
662 out.col_ptr() == target.col_ptr(),
663 out.row_idx() == target.row_idx(),
664 out.val() == target.val()
665 ));
666 }
667 {
668 let out = transpose_dedup(
669 new_val,
670 new_col_ptr,
671 new_row_idx,
672 A,
673 MemStack::new(&mut MemBuffer::new(transpose_dedup_scratch::<
674 usize,
675 >(nrows, ncols))),
676 )
677 .into_const();
678 let target = SparseColMatRef::new(
679 SymbolicSparseColMatRef::new_unsorted_checked(
680 ncols,
681 nrows,
682 &[0usize, 3, 5, 7, 8, 9],
683 None,
684 &[0, 1, 2, 1, 2, 0, 1, 2, 0],
685 ),
686 &[3.0, 14.0, 21.0, 25.0, 22.0, 3.0, 11.0, 23.0, 4.0],
687 );
688 assert!(all(
689 out.col_ptr() == target.col_ptr(),
690 out.row_idx() == target.row_idx(),
691 out.val() == target.val()
692 ));
693 }
694 }
695 #[test]
696 fn test_permute_self_adjoint() {
697 let n = 5;
698 let rng = &mut StdRng::seed_from_u64(0);
699 let diag_rng = &mut StdRng::seed_from_u64(1);
700 let mut rand = || {
701 ComplexDistribution::new(StandardNormal, StandardNormal)
702 .rand::<c64>(rng)
703 };
704 let mut rand_diag = || c64::new(StandardNormal.rand(diag_rng), 0.0);
705 let val = &[
706 rand_diag(),
707 rand_diag(),
708 rand(),
709 rand(),
710 rand(),
711 rand_diag(),
712 rand_diag(),
713 rand(),
714 rand(),
715 rand(),
716 rand(),
717 rand(),
718 rand_diag(),
719 rand(),
720 rand_diag(),
721 rand(),
722 rand(),
723 ];
724 let A = SparseColMatRef::new(
725 SymbolicSparseColMatRef::new_unsorted_checked(
726 n,
727 n,
728 &[0usize, 4, 8, 11, 14, 17],
729 None,
730 &[0, 0, 2, 4, 2, 1, 1, 0, 0, 1, 3, 2, 3, 4, 4, 3, 2],
731 ),
732 val,
733 );
734 let nnz = A.compute_nnz();
735 let perm_fwd = &mut *vec![0, 4, 1, 3, 2usize];
736 let perm_bwd = &mut *vec![0; 5];
737 for i in 0..n {
738 perm_bwd[perm_fwd[i]] = i;
739 }
740 let perm = PermRef::new_checked(perm_fwd, perm_bwd, n);
741 let new_col_ptr = &mut *vec![0usize; n + 1];
742 let new_row_idx = &mut *vec![0usize; nnz];
743 let new_val = &mut *vec![c64::ZERO; nnz];
744 for f in [
745 permute_self_adjoint_to_unsorted,
746 permute_self_adjoint,
747 permute_dedup_self_adjoint,
748 ] {
749 for (in_side, out_side) in [
750 (Side::Lower, Side::Lower),
751 (Side::Lower, Side::Upper),
752 (Side::Upper, Side::Lower),
753 (Side::Upper, Side::Upper),
754 ] {
755 let mut out = f(
756 new_val,
757 new_col_ptr,
758 new_row_idx,
759 A,
760 perm,
761 in_side,
762 out_side,
763 MemStack::new(&mut MemBuffer::new(
764 permute_self_adjoint_scratch::<usize>(n),
765 )),
766 )
767 .to_dense();
768 let mut A = A.to_dense();
769 match in_side {
770 Side::Lower => {
771 z!(&mut A).for_each_triangular_upper(
772 linalg::zip::Diag::Skip,
773 |uz!(x)| *x = c64::ZERO,
774 );
775 for j in 0..n {
776 for i in 0..j {
777 A[(i, j)] = A[(j, i)].conj();
778 }
779 }
780 },
781 Side::Upper => {
782 z!(&mut A).for_each_triangular_lower(
783 linalg::zip::Diag::Skip,
784 |uz!(x)| *x = c64::ZERO,
785 );
786 for j in 0..n {
787 for i in j + 1..n {
788 A[(i, j)] = A[(j, i)].conj();
789 }
790 }
791 },
792 }
793 match out_side {
794 Side::Lower => {
795 for j in 0..n {
796 for i in 0..j {
797 out[(i, j)] = out[(j, i)].conj();
798 }
799 }
800 },
801 Side::Upper => {
802 for j in 0..n {
803 for i in j + 1..n {
804 out[(i, j)] = out[(j, i)].conj();
805 }
806 }
807 },
808 }
809 assert!(out == perm * &A * perm.inverse());
810 }
811 }
812 }
813}