Skip to main content

faer/sparse/linalg/
amd.rs

1//! approximate minimum degree ordering.
2use crate::internal_prelude_sp::*;
3use crate::{assert, debug_assert};
4#[inline]
5fn post_tree<'n, I: Index>(
6	root: Idx<'n, usize>,
7	k: usize,
8	child: &mut Array<'n, MaybeIdx<'n, I>>,
9	sibling: &Array<'n, MaybeIdx<'n, I>>,
10	order: &mut Array<'n, I::Signed>,
11	stack: &mut Array<'n, I::Signed>,
12) -> usize {
13	let N = child.len();
14	let I = I::Signed::truncate;
15	let mut top = 1usize;
16	stack[N.check(0)] = I(*root);
17	let mut k = k;
18	while top > 0 {
19		let i = N.check(stack[N.check(top - 1)].zx());
20		if let Some(child_) = child[i].idx() {
21			let mut f = child_.zx();
22			loop {
23				top += 1;
24				if let Some(new_f) = sibling[f].idx() {
25					f = new_f.zx();
26				} else {
27					break;
28				}
29			}
30			let mut t = top;
31			let mut f = child_.zx();
32			loop {
33				t -= 1;
34				stack[N.check(t)] = I(*f);
35				if let Some(new_f) = sibling[f].idx() {
36					f = new_f.zx();
37				} else {
38					break;
39				}
40			}
41			child[i] = MaybeIdx::none();
42		} else {
43			top -= 1;
44			order[i] = I(k);
45			k += 1;
46		}
47	}
48	k
49}
50#[inline]
51fn postorder<'n, 'out, I: Index>(
52	order: &'out mut Array<'n, I::Signed>,
53	etree: &Array<'n, MaybeIdx<'n, I>>,
54	nv: &Array<'n, I::Signed>,
55	f_size: &Array<'n, I::Signed>,
56	stack: &mut MemStack,
57) {
58	let N = order.len();
59	let n = *N;
60	if n == 0 {
61		return;
62	}
63	let I = I::Signed::truncate;
64	let zero = I(0);
65	let none = I(NONE);
66	let (ref mut child, stack) =
67		stack.collect(repeat_n!(MaybeIdx::<'_, I>::none(), n));
68	let (ref mut sibling, stack) =
69		stack.collect(repeat_n!(MaybeIdx::<'_, I>::none(), n));
70	let (ref mut stack, _) = stack.collect(repeat_n!(I(0), n));
71	let child = Array::from_mut(child, N);
72	let sibling = Array::from_mut(sibling, N);
73	let stack = Array::from_mut(stack, N);
74	for j in N.indices().rev() {
75		if nv[j] > zero {
76			if let Some(parent) = etree[j].idx() {
77				let parent = parent.zx();
78				sibling[j] = child[parent];
79				child[parent] = MaybeIdx::from_index(j.truncate());
80			}
81		}
82	}
83	for i in N.indices() {
84		if nv[i] > zero {
85			if let Some(child_) = child[i].idx() {
86				let child_ = child_.zx();
87				let mut fprev = MaybeIdx::<'n>::none();
88				let mut bigfprev = MaybeIdx::<'n>::none();
89				let mut bigf = MaybeIdx::<'n>::none();
90				let mut maxfrsize = none;
91				let mut f = MaybeIdx::from_index(child_);
92				while let Some(f_) = f.idx() {
93					let frsize = f_size[f_];
94					if frsize >= maxfrsize {
95						maxfrsize = frsize;
96						bigfprev = fprev;
97						bigf = f;
98					}
99					fprev = f;
100					f = sibling[f_].sx();
101				}
102				let bigf = bigf.idx().unwrap();
103				let fnext = sibling[bigf];
104				if let Some(fnext) = fnext.idx() {
105					if let Some(bigfprev) = bigfprev.idx() {
106						sibling[bigfprev] = MaybeIdx::from_index(fnext);
107					} else {
108						child[i] = MaybeIdx::from_index(fnext);
109					}
110					let fprev = fprev.idx().unwrap();
111					sibling[bigf] = MaybeIdx::none();
112					sibling[fprev] = MaybeIdx::from_index(bigf.truncate());
113				}
114			}
115		}
116	}
117	order.as_mut().fill(none);
118	let mut k = 0usize;
119	for i in N.indices() {
120		if etree[i].idx().is_none() && nv[i] > zero {
121			k = post_tree(i, k, child, sibling, order, stack);
122		}
123	}
124}
125#[inline(always)]
126fn flip<I: SignedIndex>(i: I) -> I {
127	-I::truncate(2) - i
128}
129#[inline]
130fn clear_flag<I: SignedIndex>(wflg: I, wbig: I, w: &mut [I]) -> I {
131	let I = I::truncate;
132	let zero = I(0);
133	let one = I(1);
134	if wflg < I(2) || wflg >= wbig {
135		for x in w {
136			if *x != zero {
137				*x = one;
138			}
139		}
140		return I(2);
141	}
142	wflg
143}
144#[allow(clippy::comparison_chain)]
145fn amd_2<I: Index>(
146	pe: &mut [I::Signed],
147	iw: &mut [I::Signed],
148	len: &mut [I::Signed],
149	pfree: usize,
150	next: &mut [I::Signed],
151	last: &mut [I::Signed],
152	control: Control,
153	stack: &mut MemStack,
154) -> FlopCount {
155	let n = pe.len();
156	assert!(n < I::Signed::MAX.zx());
157	let mut pfree = pfree;
158	let iwlen = iw.len();
159	let I = I::Signed::truncate;
160	let none = I(NONE);
161	let zero = I(0);
162	let one = I(1);
163	let alpha = control.dense;
164	let aggressive = control.aggressive;
165	let mut mindeg = 0usize;
166	let mut ncmpa = 0usize;
167	let mut lemax = 0usize;
168	let mut ndiv = 0.0;
169	let mut nms_lu = 0.0;
170	let mut nms_ldl = 0.0;
171	let mut nel = 0usize;
172	let mut me = none;
173	let dense = if alpha < 0.0 {
174		n - 2
175	} else {
176		(alpha * ComplexFieldExt::sqrt(&(n as f64))) as usize
177	};
178	let dense = Ord::max(dense, 16);
179	let dense = Ord::min(dense, n);
180	let (mut w, stack) = stack.collect(repeat_n!(one, n));
181	let (mut nv, stack) = stack.collect(repeat_n!(one, n));
182	let (mut elen, stack) = stack.collect(repeat_n!(zero, n));
183	let nv = &mut *nv;
184	let elen = &mut *elen;
185	let w = &mut *w;
186	let wbig = I::Signed::MAX - I(n);
187	let mut wflg = I(0);
188	let mut ndense = zero;
189	{
190		let (mut head, stack) = stack.collect(repeat_n!(none, n));
191		let (mut degree, _) = stack.collect(repeat_n!(none, n));
192		let head = &mut *head;
193		let degree = &mut *degree;
194		last.fill(none);
195		head.fill(none);
196		next.fill(none);
197		degree.copy_from_slice(len);
198		for i in 0..n {
199			let deg = degree[i].zx();
200			if deg == 0 {
201				elen[i] = flip(one);
202				nel += 1;
203				pe[i] = none;
204				w[i] = zero;
205			} else if deg > dense {
206				ndense += one;
207				nv[i] = zero;
208				elen[i] = none;
209				pe[i] = none;
210				nel += 1;
211			} else {
212				let inext = head[deg];
213				if inext != none {
214					last[inext.zx()] = I(i);
215				}
216				next[i] = inext;
217				head[deg] = I(i);
218			}
219		}
220		while nel < n {
221			let mut deg = mindeg;
222			while deg < n {
223				me = head[deg];
224				if me != none {
225					break;
226				}
227				deg += 1;
228			}
229			mindeg = deg;
230			let me = me.zx();
231			let inext = next[me];
232			if inext != none {
233				last[inext.zx()] = none;
234			}
235			head[deg] = inext;
236			let elenme = elen[me];
237			let mut nvpiv = nv[me];
238			nel += nvpiv.zx();
239			nv[me] = -nvpiv;
240			let mut degme = 0usize;
241			let mut pme1;
242			let mut pme2;
243			if elenme == zero {
244				pme1 = pe[me];
245				pme2 = pme1 - one;
246				for p in pme1.zx()..(pme1 + len[me]).zx() {
247					let i = iw[p].zx();
248					let nvi = nv[i];
249					if nvi > zero {
250						degme += nvi.zx();
251						nv[i] = -nvi;
252						pme2 += one;
253						iw[pme2.zx()] = I(i);
254						let ilast = last[i];
255						let inext = next[i];
256						if inext != none {
257							last[inext.zx()] = ilast;
258						}
259						if ilast != none {
260							next[ilast.zx()] = inext;
261						} else {
262							head[degree[i].zx()] = inext;
263						}
264					}
265				}
266			} else {
267				let mut p = pe[me].zx();
268				pme1 = I(pfree);
269				let slenme = (len[me] - elenme).zx();
270				for knt1 in 1..elenme.zx() + 2 {
271					let e;
272					let mut pj;
273					let ln;
274					if I(knt1) > elenme {
275						e = me;
276						pj = I(p);
277						ln = slenme;
278					} else {
279						e = iw[p].zx();
280						p += 1;
281						pj = pe[e];
282						ln = len[e].zx();
283					}
284					for knt2 in 1..ln + 1 {
285						let i = iw[pj.zx()].zx();
286						pj += one;
287						let nvi = nv[i];
288						if nvi > zero {
289							if pfree >= iwlen {
290								pe[me] = I(p);
291								len[me] -= I(knt1);
292								if len[me] == zero {
293									pe[me] = none;
294								}
295								pe[e] = pj;
296								len[e] = I(ln - knt2);
297								if len[e] == zero {
298									pe[e] = none;
299								}
300								ncmpa += 1;
301								for (j, pe) in pe.iter_mut().enumerate() {
302									let pn = *pe;
303									if pn >= zero {
304										let pn = pn.zx();
305										*pe = iw[pn];
306										iw[pn] = flip(I(j));
307									}
308								}
309								let mut psrc = 0usize;
310								let mut pdst = 0usize;
311								let pend = pme1.zx();
312								while psrc < pend {
313									let j = flip(iw[psrc]);
314									psrc += 1;
315									if j >= zero {
316										let j = j.zx();
317										iw[pdst] = pe[j];
318										pe[j] = I(pdst);
319										pdst += 1;
320										let lenj = len[j].zx();
321										if lenj > 0 {
322											iw.copy_within(
323												psrc..psrc + lenj - 1,
324												pdst,
325											);
326											psrc += lenj - 1;
327											pdst += lenj - 1;
328										}
329									}
330								}
331								let p1 = pdst;
332								iw.copy_within(pme1.zx()..pfree, pdst);
333								pdst += pfree - pme1.zx();
334								pme1 = I(p1);
335								pfree = pdst;
336								pj = pe[e];
337								p = pe[me].zx();
338							}
339							degme += nvi.zx();
340							nv[i] = -nvi;
341							iw[pfree] = I(i);
342							pfree += 1;
343							let ilast = last[i];
344							let inext = next[i];
345							if inext != none {
346								last[inext.zx()] = ilast;
347							}
348							if ilast != none {
349								next[ilast.zx()] = inext;
350							} else {
351								head[degree[i].zx()] = inext;
352							}
353						}
354					}
355					if e != me {
356						pe[e] = flip(I(me));
357						w[e] = zero;
358					}
359				}
360				pme2 = I(pfree - 1);
361			}
362			degree[me] = I(degme);
363			pe[me] = pme1;
364			len[me] = pme2 - pme1 + one;
365			elen[me] = flip(nvpiv + I(degme));
366			wflg = clear_flag(wflg, wbig, w);
367			assert!(pme1 >= zero);
368			for pme in pme1.zx()..(pme2 + one).zx() {
369				let i = iw[pme].zx();
370				let eln = elen[i];
371				if eln > zero {
372					let nvi = -nv[i];
373					let wnvi = wflg - nvi;
374					for iw in iw[pe[i].zx()..][..eln.zx()].iter() {
375						let e = iw.zx();
376						let mut we = w[e];
377						if we >= wflg {
378							we -= nvi;
379						} else if we != zero {
380							we = degree[e] + wnvi;
381						}
382						w[e] = we;
383					}
384				}
385			}
386			for pme in pme1.zx()..(pme2 + one).zx() {
387				let i = iw[pme].zx();
388				let p1 = pe[i].zx();
389				let p2 = p1 + elen[i].zx();
390				let mut pn = p1;
391				let mut hash = 0usize;
392				deg = 0usize;
393				if aggressive {
394					for p in p1..p2 {
395						let e = iw[p].zx();
396						let we = w[e];
397						if we != zero {
398							let dext = we - wflg;
399							if dext > zero {
400								deg += dext.zx();
401								iw[pn] = I(e);
402								pn += 1;
403								hash = hash.wrapping_add(e);
404							} else {
405								pe[e] = flip(I(me));
406								w[e] = zero;
407							}
408						}
409					}
410				} else {
411					for p in p1..p2 {
412						let e = iw[p].zx();
413						let we = w[e];
414						if we != zero {
415							let dext = (we - wflg).zx();
416							deg += dext;
417							iw[pn] = I(e);
418							pn += 1;
419							hash = hash.wrapping_add(e);
420						}
421					}
422				}
423				elen[i] = I(pn - p1 + 1);
424				let p3 = pn;
425				let p4 = p1 + len[i].zx();
426				for p in p2..p4 {
427					let j = iw[p].zx();
428					let nvj = nv[j];
429					if nvj > zero {
430						deg += nvj.zx();
431						iw[pn] = I(j);
432						pn += 1;
433						hash = hash.wrapping_add(j);
434					}
435				}
436				if elen[i] == one && p3 == pn {
437					pe[i] = flip(I(me));
438					let nvi = -nv[i];
439					assert!(nvi >= zero);
440					degme -= nvi.zx();
441					nvpiv += nvi;
442					nel += nvi.zx();
443					nv[i] = zero;
444					elen[i] = none;
445				} else {
446					degree[i] = Ord::min(degree[i], I(deg));
447					iw[pn] = iw[p3];
448					iw[p3] = iw[p1];
449					iw[p1] = I(me);
450					len[i] = I(pn - p1 + 1);
451					hash %= n;
452					let j = head[hash];
453					if j <= none {
454						next[i] = flip(j);
455						head[hash] = flip(I(i));
456					} else {
457						next[i] = last[j.zx()];
458						last[j.zx()] = I(i);
459					}
460					last[i] = I(hash);
461				}
462			}
463			degree[me] = I(degme);
464			lemax = Ord::max(lemax, degme);
465			wflg += I(lemax);
466			wflg = clear_flag(wflg, wbig, w);
467			for pme in pme1.zx()..(pme2 + one).zx() {
468				let mut i = iw[pme].zx();
469				if nv[i] < zero {
470					let hash = last[i].zx();
471					let j = head[hash];
472					if j == none {
473						i = NONE;
474					} else if j < none {
475						i = flip(j).zx();
476						head[hash] = none;
477					} else {
478						i = last[j.zx()].sx();
479						last[j.zx()] = none;
480					}
481					while i != NONE && next[i] != none {
482						let ln = len[i];
483						let eln = elen[i];
484						for p in (pe[i] + one).zx()..(pe[i] + ln).zx() {
485							w[iw[p].zx()] = wflg;
486						}
487						let mut jlast = i;
488						let mut j = next[i].sx();
489						while j != NONE {
490							let mut ok = len[j] == ln && elen[j] == eln;
491							if ok {
492								for p in (pe[j] + one).zx()..(pe[j] + ln).zx() {
493									if w[iw[p].zx()] != wflg {
494										ok = false;
495										break;
496									}
497								}
498							}
499							if ok {
500								pe[j] = flip(I(i));
501								nv[i] += nv[j];
502								nv[j] = zero;
503								elen[j] = none;
504								j = next[j].sx();
505								next[jlast] = I(j);
506							} else {
507								jlast = j;
508								j = next[j].sx();
509							}
510						}
511						wflg += one;
512						i = next[i].sx();
513					}
514				}
515			}
516			let mut p = pme1.zx();
517			let nleft = n - nel;
518			for pme in pme1.zx()..(pme2 + one).zx() {
519				let i = iw[pme].zx();
520				let nvi = -nv[i];
521				if nvi > zero {
522					nv[i] = nvi;
523					deg = degree[i].zx() + degme - nvi.zx();
524					deg = Ord::min(deg, nleft - nvi.zx());
525					let inext = head[deg];
526					if inext != none {
527						last[inext.zx()] = I(i);
528					}
529					next[i] = inext;
530					last[i] = none;
531					head[deg] = I(i);
532					mindeg = Ord::min(mindeg, deg);
533					degree[i] = I(deg);
534					iw[p] = I(i);
535					p += 1;
536				}
537			}
538			nv[me] = nvpiv;
539			len[me] = I(p) - pme1;
540			if len[me] == zero {
541				pe[me] = none;
542				w[me] = zero;
543			}
544			if elenme != zero {
545				pfree = p;
546			}
547			{
548				let f = nvpiv.sx() as isize as f64;
549				let r = degme as f64 + ndense.sx() as isize as f64;
550				let lnzme = f * r + (f - 1.0) * f / 2.0;
551				ndiv += lnzme;
552				let s = f * r * r
553					+ r * (f - 1.0) * f
554					+ (f - 1.0) * f * (2.0 * f - 1.0) / 6.0;
555				nms_lu += s;
556				nms_ldl += (s + lnzme) / 2.0;
557			}
558		}
559		{
560			let f = ndense.sx() as isize as f64;
561			let lnzme = (f - 1.0) * f / 2.0;
562			ndiv += lnzme;
563			let s = (f - 1.0) * f * (2.0 * f - 1.0) / 6.0;
564			nms_lu += s;
565			nms_ldl += (s + lnzme) / 2.0;
566		}
567		for pe in pe.iter_mut() {
568			*pe = flip(*pe);
569		}
570		for elen in elen.iter_mut() {
571			*elen = flip(*elen);
572		}
573		for i in 0..n {
574			if nv[i] == zero {
575				let mut j = pe[i].sx();
576				if j == NONE {
577					continue;
578				}
579				while nv[j] == zero {
580					j = pe[j].zx();
581				}
582				let e = I(j);
583				let mut j = i;
584				while nv[j] == zero {
585					let jnext = pe[j];
586					pe[j] = e;
587					j = jnext.zx();
588				}
589			}
590		}
591	}
592	with_dim!(N, n);
593	postorder(
594		Array::from_mut(w, N),
595		Array::from_ref(MaybeIdx::<'_, I>::from_slice_ref_checked(pe, N), N),
596		Array::from_ref(nv, N),
597		Array::from_ref(elen, N),
598		stack,
599	);
600	let (ref mut head, _) = stack.collect(repeat_n!(none, n));
601	next.fill(none);
602	for (e, &k) in w.iter().enumerate() {
603		if k != none {
604			head[k.zx()] = I(e);
605		}
606	}
607	nel = 0;
608	for &e in head.iter() {
609		if e == none {
610			break;
611		}
612		let e = e.zx();
613		next[e] = I(nel);
614		nel += nv[e].zx();
615	}
616	assert!(nel == n - ndense.zx());
617	for i in 0..n {
618		if nv[i] == zero {
619			let e = pe[i];
620			if e != none {
621				let e = e.zx();
622				next[i] = next[e];
623				next[e] += one;
624			} else {
625				next[i] = I(nel);
626				nel += 1;
627			}
628		}
629	}
630	assert!(nel == n);
631	for i in 0..n {
632		last[next[i].zx()] = I(i);
633	}
634	let _ = ncmpa;
635	FlopCount {
636		n_div: ndiv,
637		n_mult_subs_ldl: nms_ldl,
638		n_mult_subs_lu: nms_lu,
639	}
640}
641#[allow(clippy::comparison_chain)]
642fn amd_1<I: Index>(
643	perm: &mut [I::Signed],
644	perm_inv: &mut [I::Signed],
645	A: SymbolicSparseColMatRef<'_, I>,
646	len: &mut [I::Signed],
647	iwlen: usize,
648	control: Control,
649	stack: &mut MemStack,
650) -> FlopCount {
651	let n = perm.len();
652	let I = I::Signed::truncate;
653	let zero = I(0);
654	let one = I(1);
655	let (p_e, stack) = unsafe { stack.make_raw::<I::Signed>(n) };
656	let (s_p, stack) = unsafe { stack.make_raw::<I::Signed>(n) };
657	let (i_w, stack) = unsafe { stack.make_raw::<I::Signed>(iwlen) };
658	let mut pfree = zero;
659	for j in 0..n {
660		p_e[j] = pfree;
661		s_p[j] = pfree;
662		pfree += len[j];
663	}
664	let pfree = pfree.zx();
665	assert!(iwlen >= pfree + n);
666	{
667		with_dim!(N, n);
668		let (t_p, _) = unsafe { stack.make_raw::<I::Signed>(n) };
669		let A = A.as_shape(N, N);
670		let s_p = Array::from_mut(s_p, N);
671		let t_p = Array::from_mut(t_p, N);
672		for k in N.indices() {
673			let mut seen = zero;
674			let mut j_prev = usize::MAX;
675			for j in A.row_idx_of_col(k) {
676				if j_prev == *j {
677					j_prev = *j;
678					continue;
679				}
680				j_prev = *j;
681				if j < k {
682					i_w[s_p[j].zx()] = I(*k);
683					s_p[j] += one;
684					i_w[s_p[k].zx()] = I(*j);
685					s_p[k] += one;
686					seen += one;
687				} else {
688					if j == k {
689						seen += one;
690					}
691					break;
692				}
693				let mut seen_j = zero;
694				let mut i_prev = usize::MAX;
695				for i in &A.row_idx_of_col_raw(j)[t_p[j].zx()..] {
696					if i_prev == *i.zx() {
697						i_prev = *i.zx();
698						continue;
699					}
700					i_prev = *i.zx();
701					let i = i.zx();
702					if i < k {
703						i_w[s_p[i].zx()] = I(*j);
704						s_p[i] += one;
705						i_w[s_p[j].zx()] = I(*i);
706						s_p[j] += one;
707						seen_j += one;
708					} else {
709						if i == k {
710							seen_j += one;
711						}
712						break;
713					}
714				}
715				t_p[j] += seen_j;
716			}
717			t_p[k] = seen;
718		}
719		for j in N.indices() {
720			let mut i_prev = usize::MAX;
721			for i in &A.row_idx_of_col_raw(j)[t_p[j].zx()..] {
722				if i_prev == *i.zx() {
723					i_prev = *i.zx();
724					continue;
725				}
726				i_prev = *i.zx();
727				let i = i.zx();
728				i_w[s_p[i].zx()] = I(*j);
729				s_p[i] += one;
730				i_w[s_p[j].zx()] = I(*i);
731				s_p[j] += one;
732			}
733		}
734	}
735	debug_assert!(s_p[n - 1] == I(pfree));
736	amd_2::<I>(p_e, i_w, len, pfree, perm_inv, perm, control, stack)
737}
738fn preprocess<'out, I: Index>(
739	new_col_ptr: &'out mut [I],
740	new_row_idx: &'out mut [I],
741	A: SymbolicSparseColMatRef<'_, I>,
742	stack: &mut MemStack,
743) -> SymbolicSparseColMatRef<'out, I> {
744	let n = A.nrows();
745	with_dim!(N, n);
746	let I = I::Signed::truncate;
747	let zero = I(0);
748	let one = I(1);
749	let (ref mut w, stack) = stack.collect(repeat_n!(I(0), n));
750	let (ref mut flag, _) = stack.collect(repeat_n!(I(NONE), n));
751	let w = Array::from_mut(w, N);
752	let flag = Array::from_mut(flag, N);
753	let A = A.as_shape(N, N);
754	for j in N.indices() {
755		let j_ = I(*j);
756		for i in A.row_idx_of_col(j) {
757			if flag[i] != j_ {
758				w[i] += one;
759				flag[i] = j_;
760			}
761		}
762	}
763	new_col_ptr[0] = I::from_signed(zero);
764	for (i, [r, r_next]) in iter::zip(
765		N.indices(),
766		windows2(Cell::as_slice_of_cells(Cell::from_mut(new_col_ptr))),
767	) {
768		r_next.set(r.get() + I::from_signed(w[i]));
769	}
770	w.as_mut()
771		.copy_from_slice(bytemuck::cast_slice(&new_col_ptr[..n]));
772	flag.as_mut().fill(I(NONE));
773	for j in N.indices() {
774		let j_ = I(*j);
775		for i in A.row_idx_of_col(j) {
776			if flag[i] != j_ {
777				new_row_idx[w[i].zx()] = I::from_signed(j_);
778				w[i] += one;
779				flag[i] = j_;
780			}
781		}
782	}
783	unsafe {
784		SymbolicSparseColMatRef::new_unchecked(
785			n,
786			n,
787			&*new_col_ptr,
788			None,
789			&new_row_idx[..new_col_ptr[n].zx()],
790		)
791	}
792}
793#[allow(clippy::comparison_chain)]
794fn aat<I: Index>(
795	len: &mut [I::Signed],
796	A: SymbolicSparseColMatRef<'_, I>,
797	stack: &mut MemStack,
798) -> Result<usize, FaerError> {
799	{
800		with_dim!(N, A.nrows());
801		let I = I::Signed::truncate;
802		let zero = I(0);
803		let one = I(1);
804		let A = A.as_shape(N, N);
805		let n = *N;
806		let t_p = &mut *unsafe { stack.make_raw::<I::Signed>(n).0 };
807		let len = Array::from_mut(len, N);
808		let t_p = Array::from_mut(t_p, N);
809		for k in N.indices() {
810			let mut seen = zero;
811			let mut j_prev = usize::MAX;
812			for j in A.row_idx_of_col(k) {
813				if j_prev == *j {
814					j_prev = *j;
815					continue;
816				}
817				j_prev = *j;
818				if j < k {
819					seen += one;
820					len[j] += one;
821					len[k] += one;
822				} else {
823					if j == k {
824						seen += one;
825					}
826					break;
827				}
828				let mut seen_j = zero;
829				let mut i_prev = usize::MAX;
830				for i in &A.row_idx_of_col_raw(j)[t_p[j].zx()..] {
831					if i_prev == *i.zx() {
832						i_prev = *i.zx();
833						continue;
834					}
835					i_prev = *i.zx();
836					let i = i.zx();
837					if i < k {
838						len[i] += one;
839						len[j] += one;
840						seen_j += one;
841					} else {
842						if i == k {
843							seen_j += one;
844						}
845						break;
846					}
847				}
848				t_p[j] += seen_j;
849			}
850			t_p[k] = seen;
851		}
852		for j in N.indices() {
853			let mut i_prev = usize::MAX;
854			for i in &A.row_idx_of_col_raw(j)[t_p[j].zx()..] {
855				if i_prev == *i.zx() {
856					i_prev = *i.zx();
857					continue;
858				}
859				i_prev = *i.zx();
860				len[i.zx()] += one;
861				len[j] += one;
862			}
863		}
864	}
865	let nzaat = I::Signed::sum_nonnegative(len).map(I::from_signed);
866	nzaat.ok_or(FaerError::IndexOverflow).map(I::zx)
867}
868/// computes the layout of required workspace for computing the amd ordering of
869/// a sorted matrix
870pub fn order_scratch<I: Index>(n: usize, nnz_upper: usize) -> StackReq {
871	let n_scratch = StackReq::new::<I>(n);
872	let nzaat = nnz_upper.checked_mul(2).unwrap_or(usize::MAX);
873	StackReq::all_of(&[
874		n_scratch,
875		StackReq::new::<I>(nzaat.checked_add(nzaat / 5).unwrap_or(usize::MAX)),
876		n_scratch,
877		n_scratch,
878		n_scratch,
879		n_scratch,
880		n_scratch,
881		n_scratch,
882		n_scratch,
883		n_scratch,
884		n_scratch,
885		n_scratch,
886	])
887}
888/// computes the layout of required workspace for computing the amd ordering of
889/// an unsorted matrix
890pub fn order_maybe_unsorted_scratch<I: Index>(
891	n: usize,
892	nnz_upper: usize,
893) -> StackReq {
894	StackReq::all_of(&[
895		order_scratch::<I>(n, nnz_upper),
896		StackReq::new::<I>(n + 1),
897		StackReq::new::<I>(nnz_upper),
898	])
899}
900/// computes the approximate minimum degree ordering for reducing the fill-in
901/// during the sparse cholesky factorization of a matrix with the sparsity
902/// pattern of $A + A^\top$
903pub fn order<I: Index>(
904	perm: &mut [I],
905	perm_inv: &mut [I],
906	A: SymbolicSparseColMatRef<'_, I>,
907	control: Control,
908	stack: &mut MemStack,
909) -> Result<FlopCount, FaerError> {
910	let n = perm.len();
911	if n == 0 {
912		return Ok(FlopCount {
913			n_div: 0.0,
914			n_mult_subs_ldl: 0.0,
915			n_mult_subs_lu: 0.0,
916		});
917	}
918	let (ref mut len, stack) =
919		stack.collect(repeat_n!(I::Signed::truncate(0), n));
920	let nzaat = aat(len, A, stack)?;
921	let iwlen = nzaat
922		.checked_add(nzaat / 5)
923		.and_then(|x| x.checked_add(n))
924		.ok_or(FaerError::IndexOverflow)?;
925	Ok(amd_1::<I>(
926		bytemuck::cast_slice_mut(perm),
927		bytemuck::cast_slice_mut(perm_inv),
928		A,
929		len,
930		iwlen,
931		control,
932		stack,
933	))
934}
935/// computes the approximate minimum degree ordering for reducing the fill-in
936/// during the sparse cholesky factorization of a matrix with the sparsity
937/// pattern of $A + A^\top$
938///
939/// # note
940/// allows unsorted matrices
941pub fn order_maybe_unsorted<I: Index>(
942	perm: &mut [I],
943	perm_inv: &mut [I],
944	A: SymbolicSparseColMatRef<'_, I>,
945	control: Control,
946	stack: &mut MemStack,
947) -> Result<FlopCount, FaerError> {
948	let n = perm.len();
949	if n == 0 {
950		return Ok(FlopCount {
951			n_div: 0.0,
952			n_mult_subs_ldl: 0.0,
953			n_mult_subs_lu: 0.0,
954		});
955	}
956	let nnz = A.compute_nnz();
957	let (new_col_ptr, stack) = unsafe { stack.make_raw::<I>(n + 1) };
958	let (new_row_idx, stack) = unsafe { stack.make_raw::<I>(nnz) };
959	let A = preprocess(new_col_ptr, new_row_idx, A, stack);
960	order(perm, perm_inv, A, control, stack)
961}
962/// tuning parameters for the amd implementation
963#[derive(Debug, Copy, Clone, PartialEq)]
964pub struct Control {
965	/// "dense" if degree > dense * sqrt(n)
966	pub dense: f64,
967	/// do aggressive absorption
968	pub aggressive: bool,
969}
970impl Default for Control {
971	#[inline]
972	fn default() -> Self {
973		Self {
974			dense: 10.0,
975			aggressive: true,
976		}
977	}
978}
979/// flop count of the ldlt and lu factorizations if the provided ordering is
980/// used
981#[derive(Default, Debug, Copy, Clone, PartialEq)]
982pub struct FlopCount {
983	/// number of division
984	pub n_div: f64,
985	/// number of multiplications and subtractions for the ldlt factorization
986	pub n_mult_subs_ldl: f64,
987	/// number of multiplications and subtractions for the lu factorization
988	pub n_mult_subs_lu: f64,
989}
990
991#[cfg(test)]
992mod tests {
993	use super::*;
994	use alloc::alloc::Layout;
995	use core::slice;
996	use std::alloc::System;
997
998	struct GlobalAlloc;
999	#[global_allocator]
1000	static GLOBAL_ALLOC: GlobalAlloc = GlobalAlloc;
1001	unsafe impl core::alloc::GlobalAlloc for GlobalAlloc {
1002		unsafe fn alloc(&self, layout: Layout) -> *mut u8 {
1003			let result = unsafe { System.alloc(layout) };
1004			// Fill the memory with garbage.
1005			unsafe {
1006				slice::from_raw_parts_mut(result, layout.size()).fill(0x7F)
1007			};
1008			result
1009		}
1010
1011		unsafe fn realloc(
1012			&self,
1013			ptr: *mut u8,
1014			layout: Layout,
1015			new_size: usize,
1016		) -> *mut u8 {
1017			unsafe { System.realloc(ptr, layout, new_size) }
1018		}
1019
1020		unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) {
1021			unsafe { System.dealloc(ptr, layout) }
1022		}
1023	}
1024
1025	#[test]
1026	fn test_gh_258() {
1027		use faer::Side;
1028		use faer::sparse::linalg::cholesky::{
1029			SymmetricOrdering, factorize_symbolic_cholesky,
1030		};
1031		use faer::sparse::{SparseColMat, Triplet};
1032		use std::fs::read_to_string;
1033
1034		let data =
1035			read_to_string("test_data/sparse_cholesky/gh_258.txt").unwrap();
1036		let mut triplets = Vec::<Triplet<u32, u32, f64>>::new();
1037		for line in data.lines() {
1038			let [i, j, a] = Vec::from_iter(line.split(" ")).try_into().unwrap();
1039			triplets.push(Triplet::new(
1040				i.parse().unwrap(),
1041				j.parse().unwrap(),
1042				a.parse().unwrap(),
1043			));
1044		}
1045		let mat =
1046			SparseColMat::try_new_from_triplets(52, 52, &triplets).unwrap();
1047		_ = factorize_symbolic_cholesky(
1048			mat.symbolic(),
1049			Side::Lower,
1050			SymmetricOrdering::Amd,
1051			<_>::default(),
1052		);
1053	}
1054}