1use crate::assert;
3use crate::internal_prelude_sp::*;
4impl<I: Index> ColamdCol<I> {
5 fn is_dead_principal(&self) -> bool {
6 self.start == I::Signed::truncate(NONE)
7 }
8
9 fn is_dead(&self) -> bool {
10 self.start < I::Signed::truncate(0)
11 }
12
13 fn is_alive(&self) -> bool {
14 !self.is_dead()
15 }
16
17 fn kill_principal(&mut self) {
18 self.start = I::Signed::truncate(NONE);
19 }
20
21 fn kill_non_principal(&mut self) {
22 self.start = I::Signed::truncate(NONE - 1);
23 }
24}
25impl<I: Index> ColamdRow<I> {
26 fn is_dead(&self) -> bool {
27 self.shared2 < I::Signed::truncate(0)
28 }
29
30 fn is_alive(&self) -> bool {
31 !self.is_dead()
32 }
33
34 fn kill(&mut self) {
35 self.shared2 = I::Signed::truncate(NONE);
36 }
37}
38fn clear_mark<I: Index>(
39 tag_mark: I,
40 max_mark: I,
41 row: &mut [ColamdRow<I>],
42) -> I {
43 let I = I::truncate;
44 let SI = I::Signed::truncate;
45 if tag_mark == I(0) || tag_mark >= max_mark {
46 for r in row {
47 if r.is_alive() {
48 r.shared2 = SI(0);
49 }
50 }
51 I(1)
52 } else {
53 tag_mark
54 }
55}
56pub fn order_scratch<I: Index>(
59 nrows: usize,
60 ncols: usize,
61 A_nnz: usize,
62) -> StackReq {
63 let m = nrows;
64 let n = ncols;
65 let n_scratch = StackReq::new::<I>(n);
66 let m_scratch = StackReq::new::<I>(m);
67 let np1_scratch = StackReq::new::<I>(n + 1);
68 let size = StackReq::new::<I>(
69 A_nnz
70 .checked_mul(2)
71 .and_then(|x| x.checked_add(A_nnz / 5))
72 .and_then(|p| p.checked_add(n))
73 .unwrap_or(usize::MAX),
74 );
75 StackReq::or(
76 StackReq::all_of(&[
77 StackReq::new::<ColamdCol<I>>(n + 1),
78 StackReq::new::<ColamdRow<I>>(m + 1),
79 np1_scratch,
80 size,
81 ]),
82 StackReq::all_of(&[
83 n_scratch,
84 n_scratch,
85 StackReq::or(
86 StackReq::and(n_scratch, m_scratch),
87 StackReq::all_of(&[n_scratch; 3]),
88 ),
89 ]),
90 )
91}
92pub fn order<I: Index>(
99 perm: &mut [I],
100 perm_inv: &mut [I],
101 A: SymbolicSparseColMatRef<'_, I>,
102 control: Control,
103 stack: &mut MemStack,
104) -> Result<(), FaerError> {
105 let m = A.nrows();
106 let n = A.ncols();
107 let I = I::truncate;
108 let SI = I::Signed::truncate;
109 {
110 let (col, stack) = unsafe { stack.make_raw::<ColamdCol<I>>(n + 1) };
111 let (row, stack) = unsafe { stack.make_raw::<ColamdRow<I>>(m + 1) };
112 let nnz = A.compute_nnz();
113 let (p, stack) = unsafe { stack.make_raw::<I>(n + 1) };
114 let size = (2 * nnz)
115 .checked_add(nnz / 5)
116 .and_then(|p| p.checked_add(n));
117 let (new_row_idx, _) = unsafe {
118 stack.make_raw::<I>(size.ok_or(FaerError::IndexOverflow)?)
119 };
120 p[0] = I(0);
121 for j in 0..n {
122 let row_idx = A.row_idx_of_col_raw(j);
123 p[j + 1] = p[j] + I(row_idx.len());
124 new_row_idx[p[j].zx()..p[j + 1].zx()].copy_from_slice(&*row_idx);
125 }
126 let A = new_row_idx;
127 for c in 0..n {
128 col[c] = ColamdCol::<I> {
129 start: p[c].to_signed(),
130 length: p[c + 1].to_signed() - p[c].to_signed(),
131 shared1: SI(1),
132 shared2: SI(0),
133 shared3: SI(NONE),
134 shared4: SI(NONE),
135 };
136 }
137 col[n] = ColamdCol::<I> {
138 start: SI(NONE),
139 length: SI(NONE),
140 shared1: SI(NONE),
141 shared2: SI(NONE),
142 shared3: SI(NONE),
143 shared4: SI(NONE),
144 };
145 for r in 0..m {
146 row[r] = ColamdRow::<I> {
147 start: SI(0),
148 length: SI(0),
149 shared1: SI(NONE),
150 shared2: SI(NONE),
151 };
152 }
153 row[m] = ColamdRow::<I> {
154 start: SI(NONE),
155 length: SI(NONE),
156 shared1: SI(NONE),
157 shared2: SI(NONE),
158 };
159 let mut jumbled = false;
160 for c in 0..n {
161 let mut last_row = SI(NONE);
162 let mut cp = p[c].zx();
163 let cp_end = p[c + 1].zx();
164 while cp < cp_end {
165 let r = A[cp].zx();
166 cp += 1;
167 if SI(r) <= last_row || row[r].shared2 == SI(c) {
168 jumbled = true;
169 }
170 if row[r].shared2 != SI(c) {
171 row[r].length += SI(1);
172 } else {
173 col[c].length -= SI(1);
174 }
175 row[r].shared2 = SI(c);
176 last_row = SI(r);
177 }
178 }
179 row[0].start = p[n].to_signed();
180 row[0].shared1 = row[0].start;
181 row[0].shared2 = -SI(1);
182 for r in 1..m {
183 row[r].start = row[r - 1].start + row[r - 1].length;
184 row[r].shared1 = row[r].start;
185 row[r].shared2 = -SI(1);
186 }
187 if jumbled {
188 for c in 0..n {
189 let mut cp = p[c].zx();
190 let cp_end = p[c + 1].zx();
191 while cp < cp_end {
192 let r = A[cp].zx();
193 cp += 1;
194 if row[r].shared2 != SI(c) {
195 A[row[r].shared1.zx()] = I(c);
196 row[r].shared1 += SI(1);
197 row[r].shared2 = SI(c);
198 }
199 }
200 }
201 } else {
202 for c in 0..n {
203 let mut cp = p[c].zx();
204 let cp_end = p[c + 1].zx();
205 while cp < cp_end {
206 let r = A[cp].zx();
207 cp += 1;
208 A[row[r].shared1.zx()] = I(c);
209 row[r].shared1 += SI(1);
210 }
211 }
212 }
213 for r in 0..m {
214 row[r].shared2 = SI(0);
215 row[r].shared1 = row[r].length;
216 }
217 if jumbled {
218 col[0].start = SI(0);
219 p[0] = I::from_signed(col[0].start);
220 for c in 1..n {
221 col[c].start = col[c - 1].start + col[c - 1].length;
222 p[c] = I::from_signed(col[c].start);
223 }
224 for r in 0..m {
225 let mut rp = row[r].start.zx();
226 let rp_end = rp + row[r].length.zx();
227 while rp < rp_end {
228 A[p[rp].zx()] = I(r);
229 p[rp] += I(1);
230 rp += 1;
231 }
232 }
233 }
234 let dense_row_count = if control.dense_row < 0.0 {
235 n - 1
236 } else {
237 Ord::max(16, (control.dense_row * n as f64) as usize)
238 };
239 let dense_col_count = if control.dense_col < 0.0 {
240 m - 1
241 } else {
242 Ord::max(16, (control.dense_col * Ord::min(m, n) as f64) as usize)
243 };
244 let mut max_deg = 0;
245 let mut ncol2 = n;
246 let head = &mut *p;
249 for c in (0..n).rev() {
250 let deg = col[c].length;
251 if deg == SI(0) {
252 ncol2 -= 1;
253 col[c].shared2 = SI(ncol2);
254 col[c].kill_principal();
255 }
256 }
257 for c in (0..n).rev() {
258 if col[c].is_dead() {
259 continue;
260 }
261 let deg = col[c].length;
262 if deg.zx() > dense_col_count {
263 ncol2 -= 1;
264 col[c].shared2 = SI(ncol2);
265 let mut cp = col[c].start.zx();
266 let cp_end = cp + col[c].length.zx();
267 while cp < cp_end {
268 row[A[cp].zx()].shared1 -= SI(1);
269 cp += 1;
270 }
271 col[c].kill_principal();
272 }
273 }
274 for r in 0..m {
275 let deg = row[r].shared1.zx();
276 assert!(deg <= n);
277 if deg > dense_row_count || deg == 0 {
278 row[r].kill();
279 } else {
281 max_deg = Ord::max(deg, max_deg);
282 }
283 }
284 for c in (0..n).rev() {
285 if col[c].is_dead() {
286 continue;
287 }
288 let mut score = 0;
289 let mut cp = col[c].start.zx();
290 let mut new_cp = cp;
291 let cp_end = cp + col[c].length.zx();
292 while cp < cp_end {
293 let r = A[cp].zx();
294 cp += 1;
295 if row[r].is_dead() {
296 continue;
297 }
298 A[new_cp] = I(r);
299 new_cp += 1;
300 score += row[r].shared1.zx() - 1;
301 score = Ord::min(score, n);
302 }
303 let col_length = new_cp - col[c].start.zx();
304 if col_length == 0 {
305 ncol2 -= 1;
306 col[c].shared2 = SI(ncol2);
307 col[c].kill_principal();
308 } else {
309 assert!(score <= n);
310 col[c].length = SI(col_length);
311 col[c].shared2 = SI(score);
312 }
313 }
314 head.fill(I(NONE));
315 let mut min_score = n;
316 for c in (0..n).rev() {
317 if col[c].is_alive() {
318 let score = col[c].shared2.zx();
319 assert!(min_score <= n);
320 assert!(score <= n);
321 assert!(head[score].to_signed() >= SI(NONE));
322 let next_col = head[score];
323 col[c].shared3 = SI(NONE);
324 col[c].shared4 = next_col.to_signed();
325 if next_col != I(NONE) {
326 col[next_col.zx()].shared3 = SI(c);
327 }
328 head[score] = I(c);
329 min_score = Ord::min(score, min_score);
330 }
331 }
332 let max_mark = I::from_signed(I::Signed::MAX) - I(n);
333 let mut tag_mark = clear_mark(I(0), max_mark, row);
334 let mut min_score = 0;
335 let mut pfree = 2 * nnz;
336 let mut k = 0;
337 while k < ncol2 {
338 assert!(min_score <= n);
339 assert!(head[min_score].to_signed() >= SI(NONE));
340 while head[min_score] == I(NONE) && min_score < n {
341 min_score += 1;
342 }
343 let pivot_col = head[min_score].zx();
344 let mut next_col = col[pivot_col].shared4;
345 head[min_score] = I::from_signed(next_col);
346 if next_col != SI(NONE) {
347 col[next_col.zx()].shared3 = SI(NONE);
348 }
349 assert!(!col[pivot_col].is_dead());
350 let pivot_col_score = col[pivot_col].shared2;
351 col[pivot_col].shared2 = SI(k);
352 let pivot_col_thickness = col[pivot_col].shared1;
353 assert!(pivot_col_thickness > SI(0));
354 k += pivot_col_thickness.zx();
355 let needed_memory = Ord::min(pivot_col_score, SI(n - k));
356 if pfree as isize + needed_memory.sx() as isize >= A.len() as isize
357 {
358 pfree = garbage_collection(row, col, A, pfree);
359 assert!(
360 (pfree as isize + needed_memory.sx() as isize)
361 < A.len() as isize
362 );
363 tag_mark = clear_mark(I(0), max_mark, row);
364 }
365 let pivot_row_start = pfree;
366 let mut pivot_row_degree = 0;
367 col[pivot_col].shared1 = -pivot_col_thickness;
368 let mut cp = col[pivot_col].start.zx();
369 let cp_end = cp + col[pivot_col].length.zx();
370 while cp < cp_end {
371 let r = A[cp].zx();
372 cp += 1;
373 if row[r].is_alive() {
374 let mut rp = row[r].start.zx();
375 let rp_end = rp + row[r].length.zx();
376 while rp < rp_end {
377 let c = A[rp].zx();
378 rp += 1;
379 let col_thickness = col[c].shared1;
380 if col_thickness > SI(0) && col[c].is_alive() {
381 col[c].shared1 = -col_thickness;
382 A[pfree] = I(c);
383 pfree += 1;
384 pivot_row_degree += col_thickness.zx();
385 }
386 }
387 }
388 }
389 col[pivot_col].shared1 = pivot_col_thickness;
390 max_deg = Ord::max(max_deg, pivot_row_degree);
391 let mut cp = col[pivot_col].start.zx();
392 let cp_end = cp + col[pivot_col].length.zx();
393 while cp < cp_end {
394 let r = A[cp].zx();
395 cp += 1;
396 row[r].kill();
397 }
398 let pivot_row_length = pfree - pivot_row_start;
399 let pivot_row = if pivot_row_length > 0 {
400 A[col[pivot_col].start.zx()]
401 } else {
402 I(NONE)
403 };
404 let mut rp = pivot_row_start;
405 let rp_end = rp + pivot_row_length;
406 while rp < rp_end {
407 let c = A[rp].zx();
408 rp += 1;
409 assert!(col[c].is_alive());
410 let col_thickness = -col[c].shared1;
411 assert!(col_thickness > SI(0));
412 col[c].shared1 = col_thickness;
413 let cur_score = col[c].shared2.zx();
414 let prev_col = col[c].shared3;
415 let next_col = col[c].shared4;
416 assert!(cur_score <= n);
417 if prev_col == SI(NONE) {
418 head[cur_score] = I::from_signed(next_col);
419 } else {
420 col[prev_col.zx()].shared4 = next_col;
421 }
422 if next_col != SI(NONE) {
423 col[next_col.zx()].shared3 = prev_col;
424 }
425 let mut cp = col[c].start.zx();
426 let cp_end = cp + col[c].length.zx();
427 while cp < cp_end {
428 let r = A[cp].zx();
429 cp += 1;
430 let row_mark = row[r].shared2;
431 if row[r].is_dead() {
432 continue;
433 }
434 assert!(I(r) != pivot_row);
435 let mut set_difference = row_mark - tag_mark.to_signed();
436 if set_difference < SI(0) {
437 assert!(row[r].shared1 <= SI(max_deg));
438 set_difference = row[r].shared1;
439 }
440 set_difference -= col_thickness;
441 assert!(set_difference >= SI(0));
442 if set_difference == SI(0) && control.aggressive {
443 row[r].kill();
444 } else {
445 row[r].shared2 = set_difference + tag_mark.to_signed();
446 }
447 }
448 }
449 let mut rp = pivot_row_start;
450 let rp_end = rp + pivot_row_length;
451 while rp < rp_end {
452 let c = A[rp].zx();
453 rp += 1;
454 assert!(col[c].is_alive());
455 let mut hash = 0;
456 let mut cur_score = 0;
457 let mut cp = col[c].start.zx();
458 let mut new_cp = cp;
459 let cp_end = cp + col[c].length.zx();
460 while cp < cp_end {
461 let r = A[cp].zx();
462 cp += 1;
463 let row_mark = row[r].shared2;
464 if row[r].is_dead() {
465 continue;
466 }
467 assert!(row_mark >= tag_mark.to_signed());
468 A[new_cp] = I(r);
469 new_cp += 1;
470 hash += r;
471 cur_score += (row_mark - tag_mark.to_signed()).zx();
472 cur_score = Ord::min(cur_score, n);
473 }
474 col[c].length = SI(new_cp - col[c].start.zx());
475 if col[c].length.zx() == 0 {
476 col[c].kill_principal();
477 pivot_row_degree -= col[c].shared1.zx();
478 col[c].shared2 = SI(k);
479 k += col[c].shared1.zx();
480 } else {
481 col[c].shared2 = SI(cur_score);
482 hash %= n + 1;
483 let head_column = head[hash];
484 let first_col;
485 if head_column.to_signed() > SI(NONE) {
486 first_col = col[head_column.zx()].shared3;
487 col[head_column.zx()].shared3 = SI(c);
488 } else {
489 first_col = -(head_column.to_signed() + SI(2));
490 head[hash] = I::from_signed(-SI(c + 2));
491 }
492 col[c].shared4 = first_col;
493 col[c].shared3 = SI(hash);
494 assert!(col[c].is_alive());
495 }
496 }
497 detect_super_cols(col, A, head, pivot_row_start, pivot_row_length);
498 col[pivot_col].kill_principal();
499 tag_mark = clear_mark(tag_mark + I(max_deg) + I(1), max_mark, row);
500 let mut rp = pivot_row_start;
501 let mut new_rp = rp;
502 let rp_end = rp + pivot_row_length;
503 while rp < rp_end {
504 let c = A[rp].zx();
505 rp += 1;
506 if col[c].is_dead() {
507 continue;
508 }
509 A[new_rp] = I(c);
510 new_rp += 1;
511 A[(col[c].start + col[c].length).zx()] = pivot_row;
512 col[c].length += SI(1);
513 let mut cur_score = col[c].shared2.zx() + pivot_row_degree;
514 let max_score = n - k - col[c].shared1.zx();
515 cur_score -= col[c].shared1.zx();
516 cur_score = Ord::min(cur_score, max_score);
517 col[c].shared2 = SI(cur_score);
518 next_col = head[cur_score].to_signed();
519 col[c].shared4 = next_col;
520 col[c].shared3 = SI(NONE);
521 if next_col != SI(NONE) {
522 col[next_col.zx()].shared3 = SI(c);
523 }
524 head[cur_score] = I(c);
525 min_score = Ord::min(min_score, cur_score);
526 }
527 if pivot_row_degree > 0 {
528 let pivot_row = pivot_row.zx();
529 row[pivot_row].start = SI(pivot_row_start);
530 row[pivot_row].length = SI(new_rp - pivot_row_start);
531 row[pivot_row].shared1 = SI(pivot_row_degree);
532 row[pivot_row].shared2 = SI(0);
533 }
534 }
535 for i in 0..n {
536 assert!(col[i].is_dead());
537 if !col[i].is_dead_principal() && col[i].shared2 == SI(NONE) {
538 let mut parent = i;
539 loop {
540 parent = col[parent].shared1.zx();
541 if col[parent].is_dead_principal() {
542 break;
543 }
544 }
545 let mut c = i;
546 let mut order = col[parent].shared2.zx();
547 loop {
548 assert!(col[c].shared2 == SI(NONE));
549 col[c].shared2 = SI(order);
550 order += 1;
551 col[c].shared1 = SI(parent);
552 c = col[c].shared1.zx();
553 if col[c].shared2 != SI(NONE) {
554 break;
555 }
556 }
557 col[parent].shared2 = SI(order);
558 }
559 }
560 for c in 0..n {
561 perm[col[c].shared2.zx()] = I(c);
562 }
563 for c in 0..n {
564 perm_inv[perm[c].zx()] = I(c);
565 }
566 }
567 let mut etree = alloc::vec![I(0); n];
568 let mut post = alloc::vec![I(0); n];
569 let etree = super::qr::col_etree::<I>(
570 A,
571 Some(PermRef::<'_, I>::new_checked(perm, perm_inv, n)),
572 &mut etree,
573 stack,
574 );
575 super::qr::postorder(&mut post, etree, stack);
576 for i in 0..n {
577 perm[post[i].zx()] = I(i);
578 }
579 for i in 0..n {
580 perm_inv[i] = perm[perm_inv[i].zx()];
581 }
582 for i in 0..n {
583 perm[perm_inv[i].zx()] = I(i);
584 }
585 Ok(())
586}
587fn detect_super_cols<I: Index>(
588 col: &mut [ColamdCol<I>],
589 A: &mut [I],
590 head: &mut [I],
591 row_start: usize,
592 row_length: usize,
593) {
594 let I = I::truncate;
595 let SI = I::Signed::truncate;
596 let mut rp = row_start;
597 let rp_end = rp + row_length;
598 while rp < rp_end {
599 let c = A[rp].zx();
600 rp += 1;
601 if col[c].is_dead() {
602 continue;
603 }
604 let hash = col[c].shared3.zx();
605 let head_column = head[hash].to_signed();
606 let first_col = if head_column > SI(NONE) {
607 col[head_column.zx()].shared3
608 } else {
609 -(head_column + SI(2))
610 };
611 let mut super_c_ = first_col;
612 while super_c_ != SI(NONE) {
613 let super_c = super_c_.zx();
614 let length = col[super_c].length;
615 let mut prev_c = super_c;
616 let mut c_ = col[super_c].shared4;
617 while c_ != SI(NONE) {
618 let c = c_.zx();
619 if col[c].length != length
620 || col[c].shared2 != col[super_c].shared2
621 {
622 prev_c = c;
623 c_ = col[c].shared4;
624 continue;
625 }
626 let mut cp1 = col[super_c].start.zx();
627 let mut cp2 = col[c].start.zx();
628 let mut i = 0;
629 while i < length.zx() {
630 if A[cp1] != A[cp2] {
631 break;
632 }
633 cp1 += 1;
634 cp2 += 1;
635 i += 1;
636 }
637 if i != length.zx() {
638 prev_c = c;
639 c_ = col[c].shared4;
640 continue;
641 }
642 col[super_c].shared1 += col[c].shared1;
643 col[c].shared1 = SI(super_c);
644 col[c].kill_non_principal();
645 col[c].shared2 = SI(NONE);
646 col[prev_c].shared4 = col[c].shared4;
647 c_ = col[c].shared4;
648 }
649 super_c_ = col[super_c].shared4;
650 }
651 if head_column > SI(NONE) {
652 col[head_column.zx()].shared3 = SI(NONE);
653 } else {
654 head[hash] = I(NONE);
655 }
656 }
657}
658fn garbage_collection<I: Index>(
659 row: &mut [ColamdRow<I>],
660 col: &mut [ColamdCol<I>],
661 A: &mut [I],
662 pfree: usize,
663) -> usize {
664 let I = I::truncate;
665 let SI = I::Signed::truncate;
666 let mut pdest = 0usize;
667 let m = row.len() - 1;
668 let n = col.len() - 1;
669 for c in 0..n {
670 if !col[c].is_dead() {
671 let mut psrc = col[c].start.zx();
672 col[c].start = SI(pdest);
673 let length = col[c].length.zx();
674 for _ in 0..length {
675 let r = A[psrc].zx();
676 psrc += 1;
677 if !row[r].is_dead() {
678 A[pdest] = I(r);
679 pdest += 1;
680 }
681 }
682 col[c].length = SI(pdest) - col[c].start;
683 }
684 }
685 for r in 0..m {
686 if row[r].is_dead() || row[r].length == SI(0) {
687 row[r].kill();
688 } else {
689 let psrc = row[r].start.zx();
690 row[r].shared2 = A[psrc].to_signed();
691 A[psrc] = !I(r);
692 }
693 }
694 let mut psrc = pdest;
695 while psrc < pfree {
696 let psrc_ = psrc;
697 psrc += 1;
698 if A[psrc_].to_signed() < SI(0) {
699 psrc -= 1;
700 let r = (!A[psrc]).zx();
701 A[psrc] = I::from_signed(row[r].shared2);
702 row[r].start = SI(pdest);
703 let length = row[r].length.zx();
704 for _ in 0..length {
705 let c = A[psrc].zx();
706 psrc += 1;
707 if !col[c].is_dead() {
708 A[pdest] = I(c);
709 pdest += 1;
710 }
711 }
712 row[r].length = SI(pdest) - row[r].start;
713 }
714 }
715 pdest
716}
717#[derive(Copy, Clone, Debug)]
718#[repr(C)]
719struct ColamdRow<I: Index> {
720 start: I::Signed,
721 length: I::Signed,
722 shared1: I::Signed,
723 shared2: I::Signed,
724}
725#[derive(Copy, Clone, Debug)]
726#[repr(C)]
727struct ColamdCol<I: Index> {
728 start: I::Signed,
729 length: I::Signed,
730 shared1: I::Signed,
731 shared2: I::Signed,
732 shared3: I::Signed,
733 shared4: I::Signed,
734}
735unsafe impl<I: Index> bytemuck::Zeroable for ColamdCol<I> {}
736unsafe impl<I: Index> bytemuck::Pod for ColamdCol<I> {}
737unsafe impl<I: Index> bytemuck::Zeroable for ColamdRow<I> {}
738unsafe impl<I: Index> bytemuck::Pod for ColamdRow<I> {}
739#[derive(Debug, Copy, Clone, PartialEq)]
741pub struct Control {
742 pub dense_row: f64,
744 pub dense_col: f64,
746 pub aggressive: bool,
748}
749impl Default for Control {
750 #[inline]
751 fn default() -> Self {
752 Self {
753 dense_row: 0.5,
754 dense_col: 0.5,
755 aggressive: true,
756 }
757 }
758}