rsparse/lib.rs
1//! <span style="color:#c45508">rs</span>parse
2//!
3//! A Rust library for solving sparse linear systems using direct methods.
4//!
5//!
6//!  [](https://crates.io/crates/rsparse) [](https://crates.io/crates/rsparse)
7//!
8//! ---
9//!
10//! ## Data structures
11//! - CSC matrix (`Sprs`)
12//! - Triplet matrix (`Trpl`)
13//!
14//! ## Features
15//! - Convert from dense `[Vec<f64>]` or `Vec<Vec<64>>` matrix to CSC sparse matrix `Sprs`
16//! - Convert from sparse to dense `Vec<Vec<f64>>`
17//! - Convert from a triplet format matrix `Trpl` to CSC `Sprs`
18//! - Sparse matrix addition [C=A+B]
19//! - Sparse matrix multiplication [C=A*B]
20//! - Transpose sparse matrices
21//! - Solve sparse linear systems
22//!
23//! ### Solvers
24//! - **lsolve**: Solve a lower triangular system. Solves Lx=b where x and b are dense.
25//! - **ltsolve**: Solve L’x=b where x and b are dense.
26//! - **usolve**: Solve an upper triangular system. Solves Ux=b where x and b are dense
27//! - **utsolve**: Solve U’x=b where x and b are dense
28//! - **cholsol**: A\b solver using Cholesky factorization. Where A is a defined positive `Sprs` matrix and b is a dense vector
29//! - **lusol**: A\b solver using LU factorization. Where A is a square `Sprs` matrix and b is a dense vector
30//! - **qrsol**: A\b solver using QR factorization. Where A is a rectangular `Sprs` matrix and b is a dense vector
31//!
32//! ## Examples
33//! ### Basic matrix operations
34//! ```rust
35//! // Create a CSC sparse matrix A
36//! let a = rsparse::data::Sprs{
37//! // Maximum number of entries
38//! nzmax: 5,
39//! // number of rows
40//! m: 3,
41//! // number of columns
42//! n: 3,
43//! // Values
44//! x: vec![1., 9., 9., 2., 9.],
45//! // Indices
46//! i: vec![1, 2, 2, 0, 2],
47//! // Pointers
48//! p: vec![0, 2, 3, 5]
49//! };
50//!
51//! // Import the same matrix from a dense structure
52//! let mut a2 = rsparse::data::Sprs::new_from_vec(
53//! &[
54//! vec![0., 0., 2.],
55//! vec![1., 0., 0.],
56//! vec![9., 9., 9.]
57//! ]
58//! );
59//!
60//! // Check if they are the same
61//! assert_eq!(a.nzmax, a2.nzmax);
62//! assert_eq!(a.m,a2.m);
63//! assert_eq!(a.n,a2.n);
64//! assert_eq!(a.x,a2.x);
65//! assert_eq!(a.i,a2.i);
66//! assert_eq!(a.p,a2.p);
67//!
68//! // Transform A to dense and print result
69//! println!("\nA");
70//! print_matrix(&a.to_dense());
71//!
72//! // Transpose A
73//! let at = rsparse::transpose(&a);
74//! // Transform to dense and print result
75//! println!("\nAt");
76//! print_matrix(&at.to_dense());
77//!
78//! // B = A + A'
79//! let b = &a + &at;
80//! // Transform to dense and print result
81//! println!("\nB");
82//! print_matrix(&b.to_dense());
83//!
84//! // C = A * B
85//! let c = &a * &b;
86//! // Transform to dense and print result
87//! println!("\nC");
88//! print_matrix(&c.to_dense());
89//!
90//! fn print_matrix(vec: &[Vec<f64>]) {
91//! for row in vec {
92//! println!("{:?}", row);
93//! }
94//! }
95//! ```
96//!
97//! Output:
98//!
99//! ```result
100//! A
101//! 0 0 2
102//! 1 0 0
103//! 9 9 9
104//!
105//! At
106//! 0 1 9
107//! 0 0 9
108//! 2 0 9
109//!
110//! B
111//! 0 1 11
112//! 1 0 9
113//! 11 9 18
114//!
115//! C
116//! 22 18 36
117//! 0 1 11
118//! 108 90 342
119//! ```
120//!
121//!
122//! ### Solve a linear system
123//! ```rust
124//! // Arbitrary A matrix (dense)
125//! let a = [
126//! vec![8.2541e-01, 9.5622e-01, 4.6698e-01, 8.4410e-03, 6.3193e-01, 7.5741e-01, 5.3584e-01, 3.9448e-01],
127//! vec![7.4808e-01, 2.0403e-01, 9.4649e-01, 2.5086e-01, 2.6931e-01, 5.5866e-01, 3.1827e-01, 2.9819e-02],
128//! vec![6.3980e-01, 9.1615e-01, 8.5515e-01, 9.5323e-01, 7.8323e-01, 8.6003e-01, 7.5761e-01, 8.9255e-01],
129//! vec![1.8726e-01, 8.9339e-01, 9.9796e-01, 5.0506e-01, 6.1439e-01, 4.3617e-01, 7.3369e-01, 1.5565e-01],
130//! vec![2.8015e-02, 6.3404e-01, 8.4771e-01, 8.6419e-01, 2.7555e-01, 3.5909e-01, 7.6644e-01, 8.9905e-02],
131//! vec![9.1817e-01, 8.6629e-01, 5.9917e-01, 1.9346e-01, 2.1960e-01, 1.8676e-01, 8.7020e-01, 2.7891e-01],
132//! vec![3.1999e-01, 5.9988e-01, 8.7402e-01, 5.5710e-01, 2.4707e-01, 7.5652e-01, 8.3682e-01, 6.3145e-01],
133//! vec![9.3807e-01, 7.5985e-02, 7.8758e-01, 3.6881e-01, 4.4553e-01, 5.5005e-02, 3.3908e-01, 3.4573e-01],
134//! ];
135//!
136//! // Convert A to sparse
137//! let mut a_sparse = rsparse::data::Sprs::new_from_vec(&a);
138//!
139//! // Generate arbitrary b vector
140//! let mut b = [
141//! 0.4377,
142//! 0.7328,
143//! 0.1227,
144//! 0.1817,
145//! 0.2634,
146//! 0.6876,
147//! 0.8711,
148//! 0.4201
149//! ];
150//!
151//! // Known solution:
152//! /*
153//! 0.264678,
154//! -1.228118,
155//! -0.035452,
156//! -0.676711,
157//! -0.066194,
158//! 0.761495,
159//! 1.852384,
160//! -0.282992
161//! */
162//!
163//! // A*x=b -> solve for x -> place x in b
164//! rsparse::lusol(&a_sparse, &mut b, 1, 1e-6);
165//! println!("\nX");
166//! println!("{:?}", &b);
167//! ```
168//!
169//! Output:
170//!
171//! ```result
172//! X
173//! [0.2646806068156303, -1.2280777288645675, -0.035491404094236435, -0.6766064748053932, -0.06619898266432682, 0.7615102544801993, 1.8522970972589123, -0.2830302118359591]
174//! ```
175//!
176//! ## Sources
177//! - Davis, T. (2006). Direct Methods for Sparse Linear Systems. Society for Industrial and Applied Mathematics. [https://doi.org/10.1137/1.9780898718881](https://doi.org/10.1137/1.9780898718881)
178//! - [CSparse](https://people.math.sc.edu/Burkardt/c_src/csparse/csparse.html): A Concise Sparse Matrix Package in C
179//!
180//! MIT License
181//! Copyright (c) 2023 Ricard Lado
182
183pub mod data;
184use data::{Nmrc, Sprs, Symb};
185
186#[derive(Copy, Clone, Debug)]
187pub enum Error {
188 /// Cholesky factorization failed (not positive definite)
189 NotPositiveDefinite,
190 /// LU factorization failed (no pivot found)
191 NoPivot,
192}
193
194impl std::fmt::Display for Error {
195 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
196 match self {
197 Self::NoPivot => write!(f, "Could not find a pivot"),
198 Self::NotPositiveDefinite => write!(f, "Could not complete Cholesky factorization. Please provide a positive definite matrix"),
199 }
200 }
201}
202
203impl std::error::Error for Error {}
204
205// --- Public functions --------------------------------------------------------
206
207/// C = alpha * A + beta * B
208///
209/// # Example:
210/// ```rust
211/// let a = [
212/// vec![2., 2., 4., 4., 1.],
213/// vec![3., 4., 5., 8., 3.],
214/// vec![2., 6., 3., 9., 3.],
215/// vec![5., 7., 6., 7., 1.],
216/// vec![7., 1., 8., 9., 2.],
217/// ];
218/// let mut a_sparse = rsparse::data::Sprs::new();
219/// a_sparse.from_vec(&a);
220///
221/// let b = [
222/// vec![8., 8., 6., 6., 2.],
223/// vec![4., 9., 7., 5., 9.],
224/// vec![2., 3., 8., 4., 1.],
225/// vec![4., 7., 6., 8., 9.],
226/// vec![9., 1., 8., 7., 1.],
227/// ];
228/// let mut b_sparse = rsparse::data::Sprs::new();
229/// b_sparse.from_vec(&b);
230///
231/// let r = [
232/// vec![10., 10., 10., 10., 3.],
233/// vec![7., 13., 12., 13., 12.],
234/// vec![4., 9., 11., 13., 4.],
235/// vec![9., 14., 12., 15., 10.],
236/// vec![16., 2., 16., 16., 3.],
237/// ];
238/// let mut r_sparse = rsparse::data::Sprs::new();
239/// r_sparse.from_vec(&r);
240///
241/// // Check as dense
242/// assert_eq!(rsparse::add(&a_sparse, &b_sparse, 1., 1.).to_dense(), r);
243/// ```
244///
245pub fn add(a: &Sprs, b: &Sprs, alpha: f64, beta: f64) -> Sprs {
246 let mut nz = 0;
247 let m = a.m;
248 let n = b.n;
249 let anz = a.p[a.n] as usize;
250 let bnz = b.p[n] as usize;
251 let mut w = vec![0; m];
252 let mut x = vec![0.0; m];
253 let mut c = Sprs::zeros(m, n, anz + bnz);
254
255 for j in 0..n {
256 c.p[j] = nz as isize; // column j of C starts here
257 nz = scatter(a, j, alpha, &mut w[..], &mut x[..], j + 1, &mut c, nz); // alpha*A(:,j)
258 nz = scatter(b, j, beta, &mut w[..], &mut x[..], j + 1, &mut c, nz); // beta*B(:,j)
259
260 for p in c.p[j] as usize..nz {
261 c.x[p] = x[c.i[p]];
262 }
263 }
264 c.p[n] = nz as isize; // finalize the last column of C
265
266 c.quick_trim();
267
268 c
269}
270
271/// L = chol (A, [Pinv parent cp]), Pinv is optional
272///
273/// See: `schol(...)`
274///
275pub fn chol(a: &Sprs, s: &mut Symb) -> Result<Nmrc, Error> {
276 let mut top;
277 let mut d;
278 let mut lki;
279 let mut i;
280 let n = a.n;
281
282 let mut n_mat = Nmrc::new();
283 let mut w = vec![0; 3 * n];
284 let ws = n; // pointer of w
285 let wc = 2 * n; // pointer of w
286 let mut x = vec![0.; n];
287
288 let c = match s.pinv {
289 Some(_) => symperm(a, &s.pinv),
290 None => a.clone(),
291 };
292 n_mat.l = Sprs::zeros(n, n, s.cp[n] as usize);
293 for k in 0..n {
294 // --- Nonzero pattern of L(k,:) ------------------------------------
295 w[wc + k] = s.cp[k]; // column k of L starts here
296 n_mat.l.p[k] = w[wc + k];
297 x[k] = 0.; // x (0:k) is now zero
298 w[k] = k as isize; // mark node k as visited
299 top = ereach(&c, k, &s.parent[..], ws, &mut w[..], &mut x[..], n); // find row k of L
300 d = x[k]; // d = C(k,k)
301 x[k] = 0.; // clear workspace for k+1st iteration
302
303 // --- Triangular solve ---------------------------------------------
304 while top < n {
305 // solve L(0:k-1,0:k-1) * x = C(:,k)
306 i = w[ws + top] as usize; // s [top..n-1] is pattern of L(k,:)
307 lki = x[i] / n_mat.l.x[n_mat.l.p[i] as usize]; // L(k,i) = x (i) / L(i,i)
308 x[i] = 0.; // clear workspace for k+1st iteration
309 for p in (n_mat.l.p[i] + 1)..w[wc + i] as isize {
310 x[n_mat.l.i[p as usize]] -= n_mat.l.x[p as usize] * lki;
311 }
312 d -= lki * lki; // d = d - L(k,i)*L(k,i)
313 let p = w[wc + i] as usize;
314 w[wc + i] += 1;
315 n_mat.l.i[p] = k; // store L(k,i) in column i
316 n_mat.l.x[p] = lki;
317
318 // increment statement
319 top += 1;
320 }
321 // --- Compute L(k,k) -----------------------------------------------
322 if d <= 0. {
323 // not pos def
324 return Err(Error::NotPositiveDefinite);
325 }
326 let p = w[wc + k];
327 w[wc + k] += 1;
328 n_mat.l.i[p as usize] = k; // store L(k,k) = sqrt (d) in column k
329 n_mat.l.x[p as usize] = f64::powf(d, 0.5);
330 }
331 n_mat.l.p[n] = s.cp[n]; // finalize L
332
333 Ok(n_mat)
334}
335
336/// A\b solver using Cholesky factorization.
337///
338/// x=A\b where A is symmetric positive definite; b overwritten with solution
339///
340/// # Parameters:
341///
342/// Input, i8 ORDER:
343/// - -1:natural,
344/// - 0:Cholesky,
345/// - 1:LU,
346/// - 2:QR
347///
348/// # Example:
349/// ```rust
350/// let c = [
351/// vec![5.0, 0.0, 0.0, 0.0, 0.0],
352/// vec![0.0, 5.0, 0.0, 0.0, 0.017856],
353/// vec![0.0, 0.0, 5.0, 0.0, 0.0],
354/// vec![0.0, 0.0, 0.0, 5.0, 0.479746],
355/// vec![0.0, 0.017856, 0.0, 0.479746, 5.0]
356/// ];
357/// let mut c_sparse = rsparse::data::Sprs::new();
358/// c_sparse.from_vec(&c);
359///
360/// let mut b = [
361/// 0.2543,
362/// 0.8143,
363/// 0.2435,
364/// 0.9293,
365/// 0.3500
366/// ];
367///
368/// rsparse::cholsol(&mut c_sparse, &mut b, 0);
369/// println!("\nX");
370/// println!("{:?}", &b);
371/// ```
372///
373pub fn cholsol(a: &Sprs, b: &mut [f64], order: i8) -> Result<(), Error> {
374 let n = a.n;
375 let mut s = schol(a, order); // ordering and symbolic analysis
376 let n_mat = chol(a, &mut s)?; // numeric Cholesky factorization
377 let mut x = vec![0.; n];
378
379 ipvec(n, &s.pinv, b, &mut x[..]); // x = P*b
380 lsolve(&n_mat.l, &mut x); // x = L\x
381 ltsolve(&n_mat.l, &mut x); // x = L'\x
382 pvec(n, &s.pinv, &x[..], &mut b[..]); // b = P'*x
383
384 Ok(())
385}
386
387/// Generalized A times X Plus Y
388///
389/// r = A * x + y
390///
391/// # Example
392/// ```rust
393/// let a = [
394/// vec![0., 0., 2.],
395/// vec![1., 0., 0.],
396/// vec![9., 9., 9.]
397/// ];
398/// let mut a_sparse = rsparse::data::Sprs::new();
399/// a_sparse.from_vec(&a);
400///
401/// let x = vec![1., 2., 3.];
402/// let y = vec![3., 2., 1.];
403///
404/// assert_eq!(rsparse::gaxpy(&a_sparse, &x, &y), vec![9., 3., 55.]);
405/// ```
406///
407pub fn gaxpy(a_mat: &Sprs, x: &[f64], y: &[f64]) -> Vec<f64> {
408 let mut r = y.to_vec();
409 for (j, &x_j) in x.iter().enumerate().take(a_mat.n) {
410 for p in a_mat.p[j]..a_mat.p[j + 1] {
411 let p_u = p as usize;
412 r[a_mat.i[p_u]] += a_mat.x[p_u] * x_j;
413 }
414 }
415
416 r
417}
418
419/// Solves a lower triangular system. Solves Lx=b. Where x and b are dense.
420///
421/// The lsolve function assumes that the diagonal entry of L is always present
422/// and is the first entry in each column. Otherwise, the row indices in each
423/// column of L can appear in any order.
424///
425/// On input, X contains the right hand side, and on output, the solution.
426///
427/// # Example:
428/// ```rust
429/// let l = [
430/// vec![1.0000, 0., 0., 0., 0., 0., 0., 0., 0., 0.],
431/// vec![0.4044, 1.0000, 0., 0., 0., 0., 0., 0., 0., 0.],
432/// vec![0.3465, 0.0122, 1.0000, 0., 0., 0., 0., 0., 0., 0.],
433/// vec![0.7592, -0.3591, -0.1154, 1.0000, 0., 0., 0., 0., 0., 0.],
434/// vec![0.6868, 0.1135, 0.2113, 0.6470, 1.0000, 0., 0., 0., 0., 0.],
435/// vec![0.7304, -0.1453, 0.1755, 0.0585, -0.7586, 1.0000, 0., 0., 0., 0.],
436/// vec![0.8362, 0.0732, 0.7601, -0.1107, 0.1175, -0.5406, 1.0000, 0., 0., 0.],
437/// vec![0.0390, 0.8993, 0.3428, 0.1639, 0.4246, -0.5861, 0.7790, 1.0000, 0., 0.],
438/// vec![0.8079, -0.4437, 0.8271, 0.2583, -0.2238, 0.0544, 0.2360, -0.7387, 1.0000, 0.],
439/// vec![0.1360, 0.9532, -0.1212, -0.1943, 0.4311, 0.1069, 0.3717, 0.7176, -0.6053, 1.0000]
440/// ];
441/// let mut l_sparse = rsparse::data::Sprs::new();
442/// l_sparse.from_vec(&l);
443///
444/// let mut b = [
445/// 0.8568,
446/// 0.3219,
447/// 0.9263,
448/// 0.4635,
449/// 0.8348,
450/// 0.1339,
451/// 0.8444,
452/// 0.7000,
453/// 0.7947,
454/// 0.5552
455/// ];
456///
457/// rsparse::lsolve(&l_sparse, &mut b);
458/// ```
459///
460pub fn lsolve(l: &Sprs, x: &mut [f64]) {
461 for j in 0..l.n {
462 x[j] /= l.x[l.p[j] as usize];
463 for p in (l.p[j] + 1) as usize..l.p[j + 1] as usize {
464 x[l.i[p]] -= l.x[p] * x[j];
465 }
466 }
467}
468
469/// Solves L'x=b. Where x and b are dense.
470///
471/// On input, X contains the right hand side, and on output, the solution.
472///
473/// # Example:
474/// ```rust
475/// let l = [
476/// vec![1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
477/// vec![0.3376, 1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000],
478/// vec![0.8260, 0.2762, 1.0000, 0.0000, 0.0000, 0.0000, 0.0000],
479/// vec![0.5710, 0.1764, 0.5430, 1.0000, 0.0000, 0.0000, 0.0000],
480/// vec![0.9194, 0.3583, 0.6850, 0.6594, 1.0000, 0.0000, 0.0000],
481/// vec![0.2448, 0.5015, -0.2830, 0.2239, 0.4723, 1.0000, 0.0000],
482/// vec![0.2423, 0.2332, -0.8355, 0.7522, -0.3700, 0.1985, 1.0000]
483/// ];
484/// let mut l_sparse = rsparse::data::Sprs::new();
485/// l_sparse.from_vec(&l);
486///
487/// let mut b = [
488/// 0.444841,
489/// 0.528773,
490/// 0.988345,
491/// 0.097749,
492/// 0.996166,
493/// 0.068040,
494/// 0.844511
495/// ];
496///
497/// rsparse::ltsolve(&l_sparse, &mut b);
498/// ```
499
500///
501pub fn ltsolve(l: &Sprs, x: &mut [f64]) {
502 for j in (0..l.n).rev() {
503 for p in (l.p[j] + 1) as usize..l.p[j + 1] as usize {
504 x[j] -= l.x[p] * x[l.i[p]];
505 }
506 x[j] /= l.x[l.p[j] as usize];
507 }
508}
509
510/// (L,U,Pinv) = lu(A, (Q lnz unz)). lnz and unz can be guess
511///
512/// See: `sqr(...)`
513///
514pub fn lu(a: &Sprs, s: &mut Symb, tol: f64) -> Result<Nmrc, Error> {
515 let n = a.n;
516 let mut col;
517 let mut top;
518 let mut ipiv;
519 let mut a_f;
520 let mut t;
521 let mut pivot;
522 let mut x = vec![0.; n];
523 let mut xi = vec![0; 2 * n];
524 let mut n_mat = Nmrc {
525 l: Sprs::zeros(n, n, s.lnz), // initial L and U
526 u: Sprs::zeros(n, n, s.unz),
527 pinv: Some(vec![0; n]),
528 b: Vec::new(),
529 };
530
531 x[0..n].fill(0.); // clear workspace
532 n_mat.pinv.as_mut().unwrap()[0..n].fill(-1); // no rows pivotal yet
533 n_mat.l.p[0..n + 1].fill(0); // no cols of L yet
534
535 s.lnz = 0;
536 s.unz = 0;
537 for k in 0..n {
538 // compute L(:,k) and U(:,k)
539 // --- Triangular solve ---------------------------------------------
540 n_mat.l.p[k] = s.lnz as isize; // L(:,k) starts here
541 n_mat.u.p[k] = s.unz as isize; // L(:,k) starts here
542
543 // Resize L and U
544 if s.lnz + n > n_mat.l.nzmax {
545 let nsz = 2 * n_mat.l.nzmax + n;
546 n_mat.l.nzmax = nsz;
547 n_mat.l.i.resize(nsz, 0);
548 n_mat.l.x.resize(nsz, 0.);
549 }
550 if s.unz + n > n_mat.u.nzmax {
551 let nsz = 2 * n_mat.u.nzmax + n;
552 n_mat.u.nzmax = nsz;
553 n_mat.u.i.resize(nsz, 0);
554 n_mat.u.x.resize(nsz, 0.);
555 }
556
557 col = s.q.as_ref().map_or(k, |q| q[k] as usize);
558 top = splsolve(&mut n_mat.l, a, col, &mut xi[..], &mut x[..], &n_mat.pinv); // x = L\A(:,col)
559
560 // --- Find pivot ---------------------------------------------------
561 ipiv = -1;
562 a_f = -1.;
563 for &i in xi[top..n].iter() {
564 let i = i as usize;
565 if n_mat.pinv.as_ref().unwrap()[i] < 0 {
566 // row i is not pivotal
567 t = f64::abs(x[i]);
568 if t > a_f {
569 a_f = t; // largest pivot candidate so far
570 ipiv = i as isize;
571 }
572 } else {
573 // x(i) is the entry U(Pinv[i],k)
574 n_mat.u.i[s.unz] = n_mat.pinv.as_ref().unwrap()[i] as usize;
575 n_mat.u.x[s.unz] = x[i];
576 s.unz += 1;
577 }
578 }
579 if ipiv == -1 || a_f <= 0. {
580 return Err(Error::NoPivot);
581 }
582 if n_mat.pinv.as_ref().unwrap()[col] < 0 && f64::abs(x[col]) >= a_f * tol {
583 ipiv = col as isize;
584 }
585
586 // --- Divide by pivot ----------------------------------------------
587 pivot = x[ipiv as usize]; // the chosen pivot
588 n_mat.u.i[s.unz] = k; // last entry in U(:,k) is U(k,k)
589 n_mat.u.x[s.unz] = pivot;
590 s.unz += 1;
591 n_mat.pinv.as_mut().unwrap()[ipiv as usize] = k as isize; // ipiv is the kth pivot row
592 n_mat.l.i[s.lnz] = ipiv as usize; // first entry in L(:,k) is L(k,k) = 1
593 n_mat.l.x[s.lnz] = 1.;
594 s.lnz += 1;
595 for &i in xi[top..n].iter() {
596 let i = i as usize;
597 if n_mat.pinv.as_ref().unwrap()[i] < 0 {
598 // x(i) is an entry in L(:,k)
599 n_mat.l.i[s.lnz] = i; // save unpermuted row in L
600 n_mat.l.x[s.lnz] = x[i] / pivot; // scale pivot column
601 s.lnz += 1
602 }
603 x[i] = 0.; // x [0..n-1] = 0 for next k
604 }
605 }
606 // --- Finalize L and U -------------------------------------------------
607 n_mat.l.p[n] = s.lnz as isize;
608 n_mat.u.p[n] = s.unz as isize;
609 // fix row indices of L for final Pinv
610 for p in 0..s.lnz {
611 n_mat.l.i[p] = n_mat.pinv.as_ref().unwrap()[n_mat.l.i[p]] as usize;
612 }
613 n_mat.l.quick_trim();
614 n_mat.u.quick_trim();
615
616 Ok(n_mat)
617}
618
619/// A\b solver using LU factorization.
620///
621/// x=A\b where A is asymmetric; b (dense) overwritten with solution
622///
623/// Input, i8 ORDER:
624/// - -1:natural,
625/// - 0:Cholesky,
626/// - 1:LU,
627/// - 2:QR
628///
629/// # Example:
630/// ```rust
631/// // Arbitrary A matrix (dense)
632/// let a = [
633/// vec![8.2541e-01, 9.5622e-01, 4.6698e-01, 8.4410e-03, 6.3193e-01, 7.5741e-01, 5.3584e-01, 3.9448e-01],
634/// vec![7.4808e-01, 2.0403e-01, 9.4649e-01, 2.5086e-01, 2.6931e-01, 5.5866e-01, 3.1827e-01, 2.9819e-02],
635/// vec![6.3980e-01, 9.1615e-01, 8.5515e-01, 9.5323e-01, 7.8323e-01, 8.6003e-01, 7.5761e-01, 8.9255e-01],
636/// vec![1.8726e-01, 8.9339e-01, 9.9796e-01, 5.0506e-01, 6.1439e-01, 4.3617e-01, 7.3369e-01, 1.5565e-01],
637/// vec![2.8015e-02, 6.3404e-01, 8.4771e-01, 8.6419e-01, 2.7555e-01, 3.5909e-01, 7.6644e-01, 8.9905e-02],
638/// vec![9.1817e-01, 8.6629e-01, 5.9917e-01, 1.9346e-01, 2.1960e-01, 1.8676e-01, 8.7020e-01, 2.7891e-01],
639/// vec![3.1999e-01, 5.9988e-01, 8.7402e-01, 5.5710e-01, 2.4707e-01, 7.5652e-01, 8.3682e-01, 6.3145e-01],
640/// vec![9.3807e-01, 7.5985e-02, 7.8758e-01, 3.6881e-01, 4.4553e-01, 5.5005e-02, 3.3908e-01, 3.4573e-01],
641/// ];
642///
643/// // Convert A to sparse
644/// let mut a_sparse = rsparse::data::Sprs::new();
645/// a_sparse.from_vec(&a);
646///
647/// // Generate arbitrary b vector
648/// let mut b = [
649/// 0.4377,
650/// 0.7328,
651/// 0.1227,
652/// 0.1817,
653/// 0.2634,
654/// 0.6876,
655/// 0.8711,
656/// 0.4201
657/// ];
658///
659/// // A*x=b -> solve for x -> place x in b
660/// rsparse::lusol(&a_sparse, &mut b, 1, 1e-6);
661/// println!("\nX");
662/// println!("{:?}", &b);
663/// ```
664
665///
666pub fn lusol(a: &Sprs, b: &mut [f64], order: i8, tol: f64) -> Result<(), Error> {
667 let mut x = vec![0.; a.n];
668 let mut s;
669 s = sqr(a, order, false); // ordering and symbolic analysis
670 let n = lu(a, &mut s, tol)?; // numeric LU factorization
671
672 ipvec(a.n, &n.pinv, b, &mut x[..]); // x = P*b
673 lsolve(&n.l, &mut x); // x = L\x
674 usolve(&n.u, &mut x[..]); // x = U\x
675 ipvec(a.n, &s.q, &x[..], &mut b[..]); // b = Q*x
676 Ok(())
677}
678
679/// C = A * B
680///
681/// # Example
682/// ```rust
683/// let a = [
684/// vec![0., 0., 2.],
685/// vec![1., 0., 0.],
686/// vec![9., 9., 9.]
687/// ];
688/// let mut a_sparse = rsparse::data::Sprs::new();
689/// a_sparse.from_vec(&a);
690///
691/// let b = [
692/// vec![0., 0., 2.],
693/// vec![1., 0., 0.],
694/// vec![9., 1., 9.]
695/// ];
696/// let mut b_sparse = rsparse::data::Sprs::new();
697/// b_sparse.from_vec(&b);
698///
699/// let c = rsparse::multiply(&a_sparse, &b_sparse);
700///
701/// assert_eq!(
702/// c.to_dense(),
703/// vec![vec![18., 2., 18.], vec![0., 0., 2.], vec![90., 9., 99.]]
704/// );
705/// ```
706///
707pub fn multiply(a: &Sprs, b: &Sprs) -> Sprs {
708 let mut nz = 0;
709 let mut w = vec![0; a.m];
710 let mut x = vec![0.0; a.m];
711 let mut c = Sprs::zeros(a.m, b.n, 2 * (a.p[a.n] + b.p[b.n]) as usize + a.m);
712
713 for j in 0..b.n {
714 if nz + a.m > c.nzmax {
715 // change the max # of entries of C
716 let nsz = 2 * c.nzmax + a.m;
717 c.nzmax = nsz;
718 c.i.resize(nsz, 0);
719 c.x.resize(nsz, 0.);
720 }
721 c.p[j] = nz as isize; // column j of C starts here
722 for p in b.p[j]..b.p[j + 1] {
723 nz = scatter(
724 a,
725 b.i[p as usize],
726 b.x[p as usize],
727 &mut w[..],
728 &mut x[..],
729 j + 1,
730 &mut c,
731 nz,
732 );
733 }
734 for p in c.p[j] as usize..nz {
735 c.x[p] = x[c.i[p]];
736 }
737 }
738 c.p[b.n] = nz as isize;
739 c.quick_trim();
740
741 c
742}
743
744/// Computes the 1-norm of a sparse matrix
745///
746/// 1-norm of a sparse matrix = max (sum (abs (A))), largest column sum
747///
748/// # Example:
749/// ```rust
750/// let a = [
751/// vec![0.947046, 0.107385, 0.414713, 0.829759, 0.184515, 0.915179],
752/// vec![0.731729, 0.256865, 0.57665, 0.808786, 0.975115, 0.853119],
753/// vec![0.241559, 0.76349, 0.561508, 0.726358, 0.418349, 0.089947],
754/// vec![0.056867, 0.612998, 0.933199, 0.834696, 0.831912, 0.077548],
755/// vec![0.080079, 0.350149, 0.930013, 0.482766, 0.808863, 0.152294],
756/// vec![0.486605, 0.215417, 0.446327, 0.737579, 0.141593, 0.472575]
757/// ];
758///
759/// let mut a_sparse = rsparse::data::Sprs::new();
760/// a_sparse.from_vec(&a);
761///
762/// assert!(f64::abs(rsparse::norm(&a_sparse) - 4.4199) < 1e-3);
763/// ```
764///
765pub fn norm(a: &Sprs) -> f64 {
766 let mut norm_r = 0.;
767 for j in 0..a.n {
768 let mut s = 0.;
769 for p in a.p[j] as usize..a.p[j + 1] as usize {
770 s += a.x[p].abs();
771 }
772 norm_r = f64::max(norm_r, s);
773 }
774
775 norm_r
776}
777
778/// Sparse QR factorization (V,beta,p,R) = qr (A)
779///
780/// See: `sqr(...)`
781///
782pub fn qr(a: &Sprs, s: &Symb) -> Nmrc {
783 let mut p1;
784 let mut top;
785 let mut col;
786 let mut i;
787
788 let m = a.m;
789 let n = a.n;
790 let mut vnz = s.lnz;
791 let mut rnz = s.unz;
792
793 let mut v = Sprs::zeros(s.m2, n, vnz); // equivalent to n_mat.l
794 let mut r = Sprs::zeros(s.m2, n, rnz); // equivalent to n_mat.u
795
796 let leftmost_p = m + n; // pointer of s.pinv
797 let mut w = vec![0; s.m2 + n];
798 let ws = s.m2; // pointer of w // size n
799 let mut x = vec![0.; s.m2];
800 let mut n_mat = Nmrc::new();
801 let mut beta = vec![0.; n]; // equivalent to n_mat.b
802
803 w[0..s.m2].fill(-1); // clear w, to mark nodes
804 rnz = 0;
805 vnz = 0;
806 for k in 0..n {
807 // compute V and R
808 r.p[k] = rnz as isize; // R(:,k) starts here
809 v.p[k] = vnz as isize; // V(:,k) starts here
810 p1 = vnz;
811 w[k] = k as isize; // add V(k,k) to pattern of V
812 v.i[vnz] = k;
813 vnz += 1;
814 top = n;
815 col = s.q.as_ref().map_or(k, |q| q[k] as usize);
816
817 for p in a.p[col]..a.p[col + 1] {
818 // find R(:,k) pattern
819 i = s.pinv.as_ref().unwrap()[leftmost_p + a.i[p as usize]]; // i = min(find(A(i,Q)))
820 let mut len = 0;
821 while w[i as usize] != k as isize {
822 // traverse up to k
823 w[ws + len] = i;
824 len += 1;
825 w[i as usize] = k as isize;
826 // increment statement
827 i = s.parent[i as usize];
828 }
829 for j in 1..(len + 1) {
830 top -= 1;
831 w[ws + top] = w[ws + len - j]; // push path on stack
832 }
833 i = s.pinv.as_ref().unwrap()[a.i[p as usize]]; // i = permuted row of A(:,col)
834 x[i as usize] = a.x[p as usize]; // x (i) = A(.,col)
835 if i > k as isize && w[i as usize] < k as isize {
836 // pattern of V(:,k) = x (k+1:m)
837 v.i[vnz] = i as usize; // add i to pattern of V(:,k)
838 vnz += 1;
839 w[i as usize] = k as isize;
840 }
841 }
842 for p in top..n {
843 // for each i in pattern of R(:,k)
844 i = w[ws + p]; // R(i,k) is nonzero
845 happly(&v, i as usize, beta[i as usize], &mut x[..]); // apply (V(i),Beta(i)) to x
846 r.i[rnz] = i as usize; // R(i,k) = x(i)
847 r.x[rnz] = x[i as usize];
848 rnz += 1;
849 x[i as usize] = 0.;
850 if s.parent[i as usize] == k as isize {
851 vnz = scatter_no_x(i as usize, &mut w[..], k, &mut v, vnz);
852 }
853 }
854 for p in p1..vnz {
855 // gather V(:,k) = x
856 v.x[p] = x[v.i[p]];
857 x[v.i[p]] = 0.;
858 }
859 r.i[rnz] = k; // R(k,k) = norm (x)
860 r.x[rnz] = house(&mut v.x[..], Some(p1), &mut beta[..], Some(k), vnz - p1); // [v,beta]=house(x)
861 rnz += 1;
862 }
863 r.p[n] = rnz as isize; // finalize R
864 v.p[n] = vnz as isize; // finalize V
865
866 n_mat.l = v;
867 n_mat.u = r;
868 n_mat.b = beta;
869
870 n_mat
871}
872
873/// A\b solver using QR factorization.
874///
875/// x=A\b where A can be rectangular; b overwritten with solution
876///
877/// # Parameters:
878///
879/// Input, i8 ORDER:
880/// - -1:natural,
881/// - 0:Cholesky,
882/// - 1:LU,
883/// - 2:QR
884///
885/// # Example:
886/// ```rust
887/// // Arbitrary A matrix (dense)
888/// let a = [
889/// vec![8.2541e-01, 9.5622e-01, 4.6698e-01, 8.4410e-03, 6.3193e-01, 7.5741e-01, 5.3584e-01, 3.9448e-01],
890/// vec![7.4808e-01, 2.0403e-01, 9.4649e-01, 2.5086e-01, 2.6931e-01, 5.5866e-01, 3.1827e-01, 2.9819e-02],
891/// vec![6.3980e-01, 9.1615e-01, 8.5515e-01, 9.5323e-01, 7.8323e-01, 8.6003e-01, 7.5761e-01, 8.9255e-01],
892/// vec![1.8726e-01, 8.9339e-01, 9.9796e-01, 5.0506e-01, 6.1439e-01, 4.3617e-01, 7.3369e-01, 1.5565e-01],
893/// vec![2.8015e-02, 6.3404e-01, 8.4771e-01, 8.6419e-01, 2.7555e-01, 3.5909e-01, 7.6644e-01, 8.9905e-02],
894/// vec![9.1817e-01, 8.6629e-01, 5.9917e-01, 1.9346e-01, 2.1960e-01, 1.8676e-01, 8.7020e-01, 2.7891e-01],
895/// vec![3.1999e-01, 5.9988e-01, 8.7402e-01, 5.5710e-01, 2.4707e-01, 7.5652e-01, 8.3682e-01, 6.3145e-01],
896/// vec![9.3807e-01, 7.5985e-02, 7.8758e-01, 3.6881e-01, 4.4553e-01, 5.5005e-02, 3.3908e-01, 3.4573e-01],
897/// ];
898///
899/// // Convert A to sparse
900/// let mut a_sparse = rsparse::data::Sprs::new();
901/// a_sparse.from_vec(&a);
902///
903/// // Generate arbitrary b vector
904/// let mut b = [
905/// 0.4377,
906/// 0.7328,
907/// 0.1227,
908/// 0.1817,
909/// 0.2634,
910/// 0.6876,
911/// 0.8711,
912/// 0.4201
913/// ];
914///
915/// // A*x=b -> solve for x -> place x in b
916/// rsparse::qrsol(&a_sparse, &mut b, 2);
917/// println!("\nX");
918/// println!("{:?}", &b);
919/// ```
920///
921pub fn qrsol(a: &Sprs, b: &mut [f64], order: i8) {
922 let n = a.n;
923 let m = a.m;
924
925 if m >= n {
926 let s = sqr(a, order, true); // ordering and symbolic analysis
927 let n_mat = qr(a, &s); // numeric QR factorization
928 let mut x = vec![0.; s.m2];
929
930 ipvec(m, &s.pinv, b, &mut x[..]); // x(0:m-1) = P*b(0:m-1)
931 for k in 0..n {
932 // apply Householder refl. to x
933 happly(&n_mat.l, k, n_mat.b[k], &mut x[..]);
934 }
935 usolve(&n_mat.u, &mut x[..]); // x = R\x
936 ipvec(n, &s.q, &x[..], &mut b[..]); // b(0:n-1) = Q*x (permutation)
937 } else {
938 let at = transpose(a); // Ax=b is underdetermined
939 let s = sqr(&at, order, true); // ordering and symbolic analysis
940 let n_mat = qr(&at, &s); // numeric QR factorization of A'
941 let mut x = vec![0.; s.m2];
942
943 pvec(m, &s.q, b, &mut x[..]); // x(0:m-1) = Q'*b (permutation)
944 utsolve(&n_mat.u, &mut x[..]); // x = R'\x
945 for k in (0..m).rev() {
946 happly(&n_mat.l, k, n_mat.b[k], &mut x[..]);
947 }
948 pvec(n, &s.pinv, &x[..], &mut b[..]); // b (0:n-1) = P'*x
949 }
950}
951
952/// Ordering and symbolic analysis for a Cholesky factorization
953///
954/// # Parameters:
955///
956/// Input, i8 ORDER:
957/// - -1:natural,
958/// - 0:Cholesky,
959/// - 1:LU,
960/// - 2:QR
961///
962pub fn schol(a: &Sprs, order: i8) -> Symb {
963 let n = a.n;
964 let mut s = Symb::new(); // allocate symbolic analysis
965 let p = amd(a, order); // P = amd(A+A'), or natural
966 s.pinv = pinvert(&p, n); // find inverse permutation
967 drop(p);
968 let c_mat = symperm(a, &s.pinv); // C = spones(triu(A(P,P)))
969 s.parent = etree(&c_mat, false); // find e tree of C
970 let post = post(n, &s.parent[..]); // postorder the etree
971 let mut c = counts(&c_mat, &s.parent[..], &post[..], false); // find column counts of chol(C)
972 drop(post);
973 drop(c_mat);
974 s.cp = vec![0; n + 1]; // find column pointers for L
975 s.unz = cumsum(&mut s.cp[..], &mut c[..], n);
976 s.lnz = s.unz;
977 drop(c);
978
979 s
980}
981
982/// Scalar plus sparse matrix. C = alpha + A
983///
984/// # Example:
985/// ```rust
986/// let a = [
987/// vec![8., 8., 6., 6., 2.],
988/// vec![4., 9., 7., 5., 9.],
989/// vec![2., 3., 8., 4., 1.],
990/// vec![4., 7., 6., 8., 9.],
991/// vec![9., 1., 8., 7., 1.],
992/// ];
993/// let mut a_sparse = rsparse::data::Sprs::new();
994/// a_sparse.from_vec(&a);
995///
996/// let r = [
997/// vec![10., 10., 8., 8., 4.],
998/// vec![6., 11., 9., 7., 11.],
999/// vec![4., 5., 10., 6., 3.],
1000/// vec![6., 9., 8., 10., 11.],
1001/// vec![11., 3., 10., 9., 3.],
1002/// ];
1003/// let mut r_sparse = rsparse::data::Sprs::new();
1004/// r_sparse.from_vec(&r);
1005///
1006/// // Add 2
1007/// assert_eq!(
1008/// rsparse::scpmat(2., &a_sparse).to_dense(),
1009/// r_sparse.to_dense()
1010/// );
1011/// ```
1012///
1013pub fn scpmat(alpha: f64, a: &Sprs) -> Sprs {
1014 let mut c = Sprs::new();
1015 c.m = a.m;
1016 c.n = a.n;
1017 c.nzmax = a.nzmax;
1018 c.p = a.p.clone();
1019 c.i = a.i.clone();
1020 c.x = a.x.iter().map(|x| x + alpha).collect();
1021
1022 c
1023}
1024
1025/// Scalar times sparse matrix. C = alpha * A
1026///
1027/// # Example:
1028/// ```rust
1029/// let a = [
1030/// vec![8., 8., 6., 6., 2.],
1031/// vec![4., 9., 7., 5., 9.],
1032/// vec![2., 3., 8., 4., 1.],
1033/// vec![4., 7., 6., 8., 9.],
1034/// vec![9., 1., 8., 7., 1.],
1035/// ];
1036/// let mut a_sparse = rsparse::data::Sprs::new();
1037/// a_sparse.from_vec(&a);
1038///
1039/// let r = [
1040/// vec![16., 16., 12., 12., 4.],
1041/// vec![8., 18., 14., 10., 18.],
1042/// vec![4., 6., 16., 8., 2.],
1043/// vec![8., 14., 12., 16., 18.],
1044/// vec![18., 2., 16., 14., 2.],
1045/// ];
1046/// let mut r_sparse = rsparse::data::Sprs::new();
1047/// r_sparse.from_vec(&r);
1048///
1049/// // Multiply a by 2
1050/// assert_eq!(
1051/// rsparse::scxmat(2., &a_sparse).to_dense(),
1052/// r_sparse.to_dense()
1053/// );
1054/// ```
1055///
1056pub fn scxmat(alpha: f64, a: &Sprs) -> Sprs {
1057 let mut c = Sprs::new();
1058 c.m = a.m;
1059 c.n = a.n;
1060 c.nzmax = a.nzmax;
1061 c.p = a.p.clone();
1062 c.i = a.i.clone();
1063 c.x = a.x.iter().map(|x| x * alpha).collect();
1064
1065 c
1066}
1067
1068/// Print a sparse matrix
1069///
1070pub fn sprs_print(a: &Sprs, brief: bool) {
1071 let m = a.m;
1072 let n = a.n;
1073 let nzmax = a.nzmax;
1074
1075 println!(
1076 "{}-by-{}, nzmax: {} nnz: {}, 1-norm: {}",
1077 m,
1078 n,
1079 nzmax,
1080 a.p[n],
1081 norm(a)
1082 );
1083 for j in 0..n {
1084 println!(
1085 " col {} : locations {} to {}",
1086 j,
1087 a.p[j],
1088 a.p[j + 1] - 1
1089 );
1090 for p in a.p[j]..a.p[j + 1] {
1091 println!(" {} : {}", a.i[p as usize], a.x[p as usize]);
1092 if brief && p > 20 {
1093 println!(" ...");
1094 return;
1095 }
1096 }
1097 }
1098}
1099
1100/// Symbolic analysis for QR or LU
1101///
1102/// Input, i8 ORDER:
1103/// - -1:natural,
1104/// - 0:Cholesky,
1105/// - 1:LU,
1106/// - 2:QR
1107///
1108pub fn sqr(a: &Sprs, order: i8, qr: bool) -> Symb {
1109 let mut s = Symb::new();
1110 let pst;
1111
1112 s.q = amd(a, order); // fill-reducing ordering
1113 if qr {
1114 // QR symbolic analysis
1115 let c = if order >= 0 {
1116 permute(a, &None, &s.q)
1117 } else {
1118 a.clone()
1119 };
1120 s.parent = etree(&c, true); // etree of C'*C, where C=A(:,Q)
1121 pst = post(a.n, &s.parent[..]);
1122 s.cp = counts(&c, &s.parent[..], &pst[..], true); // col counts chol(C'*C)
1123 s.pinv = vcount(&c, &s.parent[..], &mut s.m2, &mut s.lnz);
1124 s.unz = 0;
1125 for k in 0..a.n {
1126 s.unz += s.cp[k] as usize;
1127 }
1128 } else {
1129 s.unz = 4 * a.p[a.n] as usize + a.n; // for LU factorization only
1130 s.lnz = s.unz; // guess nnz(L) and nnz(U)
1131 }
1132
1133 s
1134}
1135
1136/// C = A'
1137///
1138/// The algorithm for transposing a sparse matrix (C = A^T) it can be viewed not
1139/// just as a linear algebraic function but as a method for converting a
1140/// compressed-column sparse matrix into a compressed-row sparse matrix as well.
1141/// The algorithm computes the row counts of A, computes the cumulative sum to
1142/// obtain the row pointers, and then iterates over each nonzero entry in A,
1143/// placing the entry in its appropriate row vector. If the resulting sparse
1144/// matrix C is interpreted as a matrix in compressed-row form, then C is equal
1145/// to A, just in a different format. If C is viewed as a compressed-column
1146/// matrix, then C contains A^T.
1147///
1148/// # Example
1149/// ```rust
1150/// let a = [
1151/// vec![2.1615, 2.0044, 2.1312, 0.8217, 2.2074],
1152/// vec![2.2828, 1.9089, 1.9295, 0.9412, 2.0017],
1153/// vec![2.2156, 1.8776, 1.9473, 1.0190, 1.8352],
1154/// vec![1.0244, 0.8742, 0.9177, 0.7036, 0.7551],
1155/// vec![2.0367, 1.5642, 1.4313, 0.8668, 1.7571],
1156/// ];
1157/// let mut a_sparse = rsparse::data::Sprs::new();
1158/// a_sparse.from_vec(&a);
1159///
1160/// assert_eq!(
1161/// rsparse::transpose(&a_sparse).to_dense(),
1162/// vec![
1163/// vec![2.1615, 2.2828, 2.2156, 1.0244, 2.0367],
1164/// vec![2.0044, 1.9089, 1.8776, 0.8742, 1.5642],
1165/// vec![2.1312, 1.9295, 1.9473, 0.9177, 1.4313],
1166/// vec![0.8217, 0.9412, 1.0190, 0.7036, 0.8668],
1167/// vec![2.2074, 2.0017, 1.8352, 0.7551, 1.7571]
1168/// ]
1169/// )
1170/// ```
1171///
1172pub fn transpose(a: &Sprs) -> Sprs {
1173 let mut q;
1174 let mut w = vec![0; a.m];
1175 let mut c = Sprs::zeros(a.n, a.m, a.p[a.n] as usize);
1176
1177 for p in 0..a.p[a.n] as usize {
1178 w[a.i[p]] += 1; // row counts
1179 }
1180 cumsum(&mut c.p[..], &mut w[..], a.m); // row pointers
1181 for j in 0..a.n {
1182 for p in a.p[j] as usize..a.p[j + 1] as usize {
1183 q = w[a.i[p]] as usize;
1184 c.i[q] = j; // place A(i,j) as entry C(j,i)
1185 c.x[q] = a.x[p];
1186 w[a.i[p]] += 1;
1187 }
1188 }
1189
1190 c
1191}
1192
1193/// Solves an upper triangular system. Solves U*x=b.
1194///
1195/// Solve Ux=b where x and b are dense. x=b on input, solution on output.
1196///
1197/// # Example:
1198/// ```rust
1199/// let u = [
1200/// vec![0.7824, 0.4055, 0.0827, 0.9534, 0.9713, 0.1418, 0.0781],
1201/// vec![0.0, 0.7766, 0.2981, 0.2307, -0.3172, 0.6819, 0.5979],
1202/// vec![0.0, 0.0, 0.2986, -0.5576, 0.5928, -0.2759, -0.1672],
1203/// vec![0.0, 0.0, 0.0, 0.6393, -0.4245, 0.1277, 0.5842],
1204/// vec![0.0, 0.0, 0.0, 0.0, -1.277, 1.1435, 1.0631],
1205/// vec![0.0, 0.0, 0.0, 0.0, 0.0, 1.2096, 0.7268],
1206/// vec![0.0, 0.0, 0.0, 0.0, 0.0, 0.0, -0.4574]
1207/// ];
1208/// let mut u_sparse = rsparse::data::Sprs::new();
1209/// u_sparse.from_vec(&u);
1210///
1211/// let mut b = [
1212/// 0.189772,
1213/// 0.055761,
1214/// 0.030676,
1215/// 0.181620,
1216/// 0.526924,
1217/// 0.744179,
1218/// 0.078005
1219/// ];
1220///
1221/// rsparse::usolve(&u_sparse, &mut b);
1222/// ```
1223///
1224pub fn usolve(u: &Sprs, x: &mut [f64]) {
1225 for j in (0..u.n).rev() {
1226 x[j] /= u.x[(u.p[j + 1] - 1) as usize];
1227 for p in u.p[j]..u.p[j + 1] - 1 {
1228 x[u.i[p as usize]] -= u.x[p as usize] * x[j];
1229 }
1230 }
1231}
1232
1233/// Solves U'x=b where x and b are dense.
1234///
1235/// x=b on input, solution on output.
1236///
1237/// # Example:
1238/// ```rust
1239/// let u = [
1240/// vec![0.9842, 0.1720, 0.9948, 0.2766, 0.4560, 0.1462, 0.8124],
1241/// vec![0.0000, 0.6894, 0.1043, 0.4486, 0.5217, 0.7157, 0.4132],
1242/// vec![0.0000, 0.0000, -0.5500, -0.2340, 0.0822, 0.2176, -0.1996],
1243/// vec![0.0000, 0.0000, 0.0000, 0.6554, -0.1564, -0.0287, 0.2107],
1244/// vec![0.0000, 0.0000, 0.0000, 0.0000, -0.4127, -0.4652, -0.6993],
1245/// vec![0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.6881, 0.3037],
1246/// vec![0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, -0.7740]
1247/// ];
1248/// let mut u_sparse = rsparse::data::Sprs::new();
1249/// u_sparse.from_vec(&u);
1250///
1251/// let mut b = [
1252/// 0.444841,
1253/// 0.528773,
1254/// 0.988345,
1255/// 0.097749,
1256/// 0.996166,
1257/// 0.068040,
1258/// 0.844511
1259/// ];
1260///
1261/// rsparse::utsolve(&u_sparse, &mut b);
1262/// ```
1263
1264///
1265pub fn utsolve(u: &Sprs, x: &mut [f64]) {
1266 for j in 0..u.n {
1267 for p in u.p[j] as usize..(u.p[j + 1] - 1) as usize {
1268 x[j] -= u.x[p] * x[u.i[p]];
1269 }
1270 x[j] /= u.x[(u.p[j + 1] - 1) as usize];
1271 }
1272}
1273
1274// --- Private functions -------------------------------------------------------
1275
1276/// amd(...) carries out the approximate minimum degree algorithm.
1277/// p = amd(A+A') if symmetric is true, or amd(A'A) otherwise
1278/// # Parameters:
1279///
1280/// Input, i8 ORDER:
1281/// - -1:natural,
1282/// - 0:Cholesky,
1283/// - 1:LU,
1284/// - 2:QR
1285///
1286fn amd(a: &Sprs, order: i8) -> Option<Vec<isize>> {
1287 let mut dense;
1288 let mut c;
1289 let mut nel = 0;
1290 let mut mindeg = 0;
1291 let mut elenk;
1292 let mut nvk;
1293 let mut p;
1294 let mut p2;
1295 let mut pk1;
1296 let mut pk2;
1297 let mut e;
1298 let mut pj;
1299 let mut ln;
1300 let mut nvi;
1301 let mut i;
1302 let mut mark_v;
1303 let mut lemax = 0;
1304 let mut eln;
1305 let mut wnvi;
1306 let mut p1;
1307 let mut pn;
1308 let mut h;
1309 let mut d;
1310 let mut dext;
1311 let mut p3;
1312 let mut p4;
1313 let mut j;
1314 let mut nvj;
1315 let mut jlast;
1316
1317 // --- Construct matrix C -----------------------------------------------
1318 if order < 0 {
1319 return None;
1320 }
1321
1322 let mut at = transpose(a); // compute A'
1323 let m = a.m;
1324 let n = a.n;
1325 dense = std::cmp::max(16, (10. * f32::sqrt(n as f32)) as isize); // find dense threshold
1326 dense = std::cmp::min((n - 2) as isize, dense);
1327
1328 if order == 0 && n == m {
1329 c = add(a, &at, 0., 0.); // C = A+A'
1330 } else if order == 1 {
1331 p2 = 0; // drop dense columns from AT
1332 for j in 0..m {
1333 p = at.p[j]; // column j of AT starts here
1334 at.p[j] = p2; // new column j starts here
1335 if at.p[j + 1] - p > dense {
1336 continue; // skip dense col j
1337 }
1338 while p < at.p[j + 1] {
1339 at.i[p2 as usize] = at.i[p as usize];
1340 p2 += 1;
1341 p += 1;
1342 }
1343 }
1344 at.p[m] = p2; // finalize AT
1345 let a2 = transpose(&at); // A2 = AT'
1346 c = multiply(&at, &a2); // C=A'*A with no dense rows
1347 } else {
1348 c = multiply(&at, a); // C=A'*A
1349 }
1350 drop(at);
1351
1352 let mut p_v = vec![0; n + 1]; // allocate result
1353 let mut ww = vec![0; 8 * (n + 1)]; // get workspace
1354 // offsets of ww (pointers in csparse)
1355 let len = 0; // of ww
1356 let nv = n + 1; // of ww
1357 let next = 2 * (n + 1); // of ww
1358 let head = 3 * (n + 1); // of ww
1359 let elen = 4 * (n + 1); // of ww
1360 let degree = 5 * (n + 1); // of ww
1361 let w = 6 * (n + 1); // of ww
1362 let hhead = 7 * (n + 1); // of ww
1363 let last = 0; // of p_v // use P as workspace for last
1364
1365 fkeep(&mut c, &diag); // drop diagonal entries
1366 let mut cnz = c.p[n];
1367 // change the max # of entries of C
1368 let nsz = cnz as usize + cnz as usize / 5 + 2 * n;
1369 c.nzmax = nsz;
1370 c.i.resize(nsz, 0);
1371 c.x.resize(nsz, 0.);
1372
1373 // --- Initialize quotient graph ----------------------------------------
1374 for k in 0..n {
1375 ww[len + k] = c.p[k + 1] - c.p[k];
1376 }
1377 ww[len + n] = 0;
1378 for i in 0..=n {
1379 ww[head + i] = -1; // degree list i is empty
1380 p_v[last + i] = -1;
1381 ww[next + i] = -1;
1382 ww[hhead + i] = -1; // hash list i is empty
1383 ww[nv + i] = 1; // node i is just one node
1384 ww[w + i] = 1; // node i is alive
1385 ww[elen + i] = 0; // Ek of node i is empty
1386 ww[degree + i] = ww[len + i]; // degree of node i
1387 }
1388 mark_v = wclear(0, 0, &mut ww[..], w, n); // clear w ALERT!!! C implementation passes w (pointer to ww)
1389 ww[elen + n] = -2; // n is a dead element
1390 c.p[n] = -1; // n is a root of assembly tree
1391 ww[w + n] = 0; // n is a dead element
1392
1393 // --- Initialize degree lists ------------------------------------------
1394 for i in 0..n {
1395 let d = ww[degree + i];
1396 if d == 0 {
1397 // node i is empty
1398 ww[elen + i] = -2; // element i is dead
1399 nel += 1;
1400 c.p[i] = -1; // i is a root of assembly tree
1401 ww[w + i] = 0;
1402 } else if d > dense {
1403 // node i is dense
1404 ww[nv + i] = 0; // absorb i into element n
1405 ww[elen + i] = -1; // node i is dead
1406 nel += 1;
1407 c.p[i] = flip(n as isize);
1408 ww[nv + n] += 1;
1409 } else {
1410 if ww[(head as isize + d) as usize] != -1 {
1411 let wt = ww[(head as isize + d) as usize];
1412 p_v[(last as isize + wt) as usize] = i as isize;
1413 }
1414 ww[next + i] = ww[(head as isize + d) as usize]; // put node i in degree list d
1415 ww[(head as isize + d) as usize] = i as isize;
1416 }
1417 }
1418
1419 while nel < n {
1420 // while (selecting pivots) do
1421 // --- Select node of minimum approximate degree --------------------
1422 let mut k;
1423 loop {
1424 k = ww[head + mindeg];
1425 if !(mindeg < n && k == -1) {
1426 break;
1427 }
1428 mindeg += 1;
1429 }
1430
1431 if ww[(next as isize + k) as usize] != -1 {
1432 let wt = ww[(next as isize + k) as usize];
1433 p_v[(last as isize + wt) as usize] = -1;
1434 }
1435 ww[head + mindeg] = ww[(next as isize + k) as usize]; // remove k from degree list
1436 elenk = ww[(elen as isize + k) as usize]; // elenk = |Ek|
1437 nvk = ww[(nv as isize + k) as usize]; // # of nodes k represents
1438 nel += nvk as usize; // nv[k] nodes of A eliminated
1439
1440 // --- Garbage collection -------------------------------------------
1441 if elenk > 0 && (cnz + mindeg as isize) as usize >= c.nzmax {
1442 for j in 0..n {
1443 p = c.p[j];
1444 if p >= 0 {
1445 // j is a live node or element
1446 c.p[j] = c.i[p as usize] as isize; // save first entry of object
1447 c.i[p as usize] = flip(j as isize) as usize; // first entry is now FLIP(j)
1448 }
1449 }
1450 let mut q = 0;
1451 p = 0;
1452 while p < cnz {
1453 // scan all of memory
1454 let j = flip(c.i[p as usize] as isize);
1455 p += 1;
1456 if j >= 0 {
1457 // found object j
1458 c.i[q] = c.p[j as usize] as usize; // restore first entry of object
1459 c.p[j as usize] = q as isize; // new pointer to object j
1460 q += 1;
1461 for _ in 0..ww[(len as isize + j) as usize] - 1 {
1462 c.i[q] = c.i[p as usize];
1463 q += 1;
1464 p += 1;
1465 }
1466 }
1467 }
1468 cnz = q as isize; // Ci [cnz...nzmax-1] now free
1469 }
1470 // --- Construct new element ----------------------------------------
1471 let mut dk = 0;
1472
1473 ww[(nv as isize + k) as usize] = -nvk; // flag k as in Lk
1474 p = c.p[k as usize];
1475 if elenk == 0 {
1476 // do in place if elen[k] == 0
1477 pk1 = p;
1478 } else {
1479 pk1 = cnz;
1480 }
1481 pk2 = pk1;
1482 for k1 in 1..=(elenk + 1) as usize {
1483 if k1 > elenk as usize {
1484 e = k; // search the nodes in k
1485 pj = p; // list of nodes starts at Ci[pj]
1486 ln = ww[(len as isize + k) as usize] - elenk; // length of list of nodes in k
1487 } else {
1488 e = c.i[p as usize] as isize; // search the nodes in e
1489 p += 1;
1490 pj = c.p[e as usize];
1491 ln = ww[(len as isize + e) as usize]; // length of list of nodes in e
1492 }
1493 for _ in 1..=ln {
1494 i = c.i[pj as usize] as isize;
1495 pj += 1;
1496 nvi = ww[(nv as isize + i) as usize];
1497 if nvi <= 0 {
1498 continue; // node i dead, or seen
1499 }
1500 dk += nvi; // degree[Lk] += size of node i
1501 ww[(nv as isize + i) as usize] = -nvi; // negate nv[i] to denote i in Lk
1502 c.i[pk2 as usize] = i as usize; // place i in Lk
1503 pk2 += 1;
1504 if ww[(next as isize + i) as usize] != -1 {
1505 let wt = ww[(next as isize + i) as usize];
1506 p_v[(last as isize + wt) as usize] = p_v[last + i as usize];
1507 }
1508 if p_v[(last as isize + i) as usize] != -1 {
1509 // remove i from degree list
1510 let wt = p_v[(last as isize + i) as usize];
1511 ww[(next as isize + wt) as usize] = ww[(next as isize + i) as usize];
1512 } else {
1513 let wt = ww[degree + i as usize];
1514 ww[(head as isize + wt) as usize] = ww[next + i as usize];
1515 }
1516 }
1517 if e != k {
1518 c.p[e as usize] = flip(k); // absorb e into k
1519 ww[(w as isize + e) as usize] = 0; // e is now a dead element
1520 }
1521 }
1522 if elenk != 0 {
1523 cnz = pk2; // Ci [cnz...nzmax] is free
1524 }
1525 ww[(degree as isize + k) as usize] = dk; // external degree of k - |Lk\i|
1526 c.p[k as usize] = pk1; // element k is in Ci[pk1..pk2-1]
1527 ww[(len as isize + k) as usize] = pk2 - pk1;
1528 ww[(elen as isize + k) as usize] = -2; // k is now an element
1529
1530 // --- Find set differences -----------------------------------------
1531 mark_v = wclear(mark_v, lemax, &mut ww[..], w, n); // clear w if necessary
1532 for pk in pk1..pk2 {
1533 // scan1: find |Le\Lk|
1534 i = c.i[pk as usize] as isize;
1535 eln = ww[(elen as isize + i) as usize];
1536 if eln <= 0 {
1537 continue; // skip if elen[i] empty
1538 }
1539 nvi = -ww[(nv as isize + i) as usize]; // nv [i] was negated
1540 wnvi = mark_v - nvi;
1541 for p in c.p[i as usize] as usize..=(c.p[i as usize] + eln - 1) as usize {
1542 // scan Ei
1543 e = c.i[p] as isize;
1544 if ww[(w as isize + e) as usize] >= mark_v {
1545 ww[(w as isize + e) as usize] -= nvi; // decrement |Le\Lk|
1546 } else if ww[(w as isize + e) as usize] != 0 {
1547 // ensure e is a live element
1548 ww[(w as isize + e) as usize] = ww[(degree as isize + e) as usize] + wnvi;
1549 // 1st time e seen in scan 1
1550 }
1551 }
1552 }
1553
1554 // --- Degree update ------------------------------------------------
1555 for pk in pk1..pk2 {
1556 // scan2: degree update
1557 i = c.i[pk as usize] as isize; // consider node i in Lk
1558 p1 = c.p[i as usize];
1559 p2 = p1 + ww[(elen as isize + i) as usize] - 1;
1560 pn = p1;
1561 h = 0;
1562 d = 0;
1563 for p in p1..=p2 {
1564 // scan Ei
1565 e = c.i[p as usize] as isize;
1566 if ww[(w as isize + e) as usize] != 0 {
1567 // e is an unabsorbed element
1568 dext = ww[(w as isize + e) as usize] - mark_v; // dext = |Le\Lk|
1569 if dext > 0 {
1570 d += dext; // sum up the set differences
1571 c.i[pn as usize] = e as usize; // keep e in Ei
1572 pn += 1;
1573 h += e as usize; // compute the hash of node i
1574 } else {
1575 c.p[e as usize] = flip(k); // aggressive absorb. e->k
1576 ww[(w as isize + e) as usize] = 0; // e is a dead element
1577 }
1578 }
1579 }
1580 ww[(elen as isize + i) as usize] = pn - p1 + 1; // elen[i] = |Ei|
1581 p3 = pn;
1582 p4 = p1 + ww[(len as isize + i) as usize];
1583 for p in p2 + 1..p4 {
1584 // prune edges in Ai
1585 j = c.i[p as usize] as isize;
1586 nvj = ww[(nv as isize + j) as usize];
1587 if nvj <= 0 {
1588 continue; // node j dead or in Lk
1589 }
1590 d += nvj; // degree(i) += |j|
1591 c.i[pn as usize] = j as usize; // place j in node list of i
1592 pn += 1;
1593 h += j as usize; // compute hash for node i
1594 }
1595 if d == 0 {
1596 // check for mass elimination
1597 c.p[i as usize] = flip(k); // absorb i into k
1598 nvi = -ww[(nv as isize + i) as usize];
1599 dk -= nvi; // |Lk| -= |i|
1600 nvk += nvi; // |k| += nv[i]
1601 nel += nvi as usize;
1602 ww[(nv as isize + i) as usize] = 0;
1603 ww[(elen as isize + i) as usize] = -1; // node i is dead
1604 } else {
1605 ww[(degree as isize + i) as usize] =
1606 std::cmp::min(ww[(degree as isize + i) as usize], d); // update degree(i)
1607 c.i[pn as usize] = c.i[p3 as usize]; // move first node to end
1608 c.i[p3 as usize] = c.i[p1 as usize]; // move 1st el. to end of Ei
1609 c.i[p1 as usize] = k as usize; // add k as 1st element in of Ei
1610 ww[(len as isize + i) as usize] = pn - p1 + 1; // new len of adj. list of node i
1611 h %= n; // finalize hash of i
1612 ww[(next as isize + i) as usize] = ww[hhead + h]; // place i in hash bucket
1613 ww[hhead + h] = i;
1614 p_v[(last as isize + i) as usize] = h as isize; // save hash of i in last[i]
1615 }
1616 } // scan2 is done
1617 ww[(degree as isize + k) as usize] = dk; // finalize |Lk|
1618 lemax = std::cmp::max(lemax, dk);
1619 mark_v = wclear(mark_v + lemax, lemax, &mut ww[..], w, n); // clear w
1620
1621 // --- Supernode detection ------------------------------------------
1622 for pk in pk1..pk2 {
1623 i = c.i[pk as usize] as isize;
1624 if ww[(nv as isize + i) as usize] >= 0 {
1625 continue; // skip if i is dead
1626 }
1627 h = p_v[(last as isize + i) as usize] as usize; // scan hash bucket of node i
1628 i = ww[hhead + h];
1629 ww[hhead + h] = -1; // hash bucket will be empty
1630
1631 while i != -1 && ww[(next as isize + i) as usize] != -1 {
1632 ln = ww[(len as isize + i) as usize];
1633 eln = ww[(elen as isize + i) as usize];
1634 for p in c.p[i as usize] + 1..=c.p[i as usize] + ln - 1 {
1635 ww[w + c.i[p as usize]] = mark_v;
1636 }
1637 jlast = i;
1638
1639 let mut ok;
1640 j = ww[(next as isize + i) as usize];
1641 while j != -1 {
1642 // compare i with all j
1643 ok = ww[(len as isize + j) as usize] == ln
1644 && ww[(elen as isize + j) as usize] == eln;
1645
1646 p = c.p[j as usize] + 1;
1647 while ok && p < c.p[j as usize] + ln {
1648 if ww[w + c.i[p as usize]] != mark_v {
1649 // compare i and j
1650 ok = false;
1651 }
1652
1653 p += 1;
1654 }
1655 if ok {
1656 // i and j are identical
1657 c.p[j as usize] = flip(i); // absorb j into i
1658 ww[(nv as isize + i) as usize] += ww[(nv as isize + j) as usize];
1659 ww[(nv as isize + j) as usize] = 0;
1660 ww[(elen as isize + j) as usize] = -1; // node j is dead
1661 j = ww[(next as isize + j) as usize]; // delete j from hash bucket
1662 ww[(next as isize + jlast) as usize] = j;
1663 } else {
1664 jlast = j; // j and i are different
1665 j = ww[(next as isize + j) as usize];
1666 }
1667 }
1668
1669 // increment while loop
1670 i = ww[(next as isize + i) as usize];
1671 mark_v += 1;
1672 }
1673 }
1674
1675 // --- Finalize new element------------------------------------------
1676 p = pk1;
1677 for pk in pk1..pk2 {
1678 // finalize Lk
1679 i = c.i[pk as usize] as isize;
1680 nvi = -ww[(nv as isize + i) as usize];
1681 if nvi <= 0 {
1682 continue; // skip if i is dead
1683 }
1684 ww[(nv as isize + i) as usize] = nvi; // restore nv[i]
1685 d = ww[(degree as isize + i) as usize] + dk - nvi; // compute external degree(i)
1686 d = std::cmp::min(d, n as isize - nel as isize - nvi);
1687 if ww[(head as isize + d) as usize] != -1 {
1688 let wt = ww[(head as isize + d) as usize];
1689 p_v[(last as isize + wt) as usize] = i;
1690 }
1691 ww[(next as isize + i) as usize] = ww[(head as isize + d) as usize]; // put i back in degree list
1692 p_v[(last as isize + i) as usize] = -1;
1693 ww[(head as isize + d) as usize] = i;
1694 mindeg = std::cmp::min(mindeg, d as usize); // find new minimum degree
1695 ww[(degree as isize + i) as usize] = d;
1696 c.i[p as usize] = i as usize; // place i in Lk
1697 p += 1;
1698 }
1699 ww[(nv as isize + k) as usize] = nvk; // # nodes absorbed into k
1700 ww[(len as isize + k) as usize] = p - pk1;
1701 if ww[(len as isize + k) as usize] == 0 {
1702 // length of adj list of element k
1703 c.p[k as usize] = -1; // k is a root of the tree
1704 ww[(w as isize + k) as usize] = 0; // k is now a dead element
1705 }
1706 if elenk != 0 {
1707 cnz = p; // free unused space in Lk
1708 }
1709 }
1710
1711 // --- Post-ordering ----------------------------------------------------
1712 for i in 0..n {
1713 c.p[i] = flip(c.p[i]); // fix assembly tree
1714 }
1715 for j in 0..=n {
1716 ww[head + j] = -1;
1717 }
1718 for j in (0..=n).rev() {
1719 // place unordered nodes in lists
1720 if ww[nv + j] > 0 {
1721 continue; // skip if j is an element
1722 }
1723 ww[next + j] = ww[(head as isize + c.p[j]) as usize]; // place j in list of its parent
1724 ww[(head as isize + c.p[j]) as usize] = j as isize;
1725 }
1726 for e in (0..=n).rev() {
1727 // place elements in lists
1728 if ww[nv + e] <= 0 {
1729 continue; // skip unless e is an element
1730 }
1731 if c.p[e] != -1 {
1732 ww[next + e] = ww[(head as isize + c.p[e]) as usize]; // place e in list of its parent
1733 ww[(head as isize + c.p[e]) as usize] = e as isize;
1734 }
1735 }
1736 let mut k = 0;
1737 for i in 0..=n {
1738 // postorder the assembly tree
1739 if c.p[i] == -1 {
1740 k = tdfs(i as isize, k, &mut ww[..], head, next, &mut p_v[..], w); // Note that CSparse passes the pointers of ww
1741 }
1742 }
1743
1744 Some(p_v)
1745}
1746
1747/// process edge (j,i) of the matrix
1748///
1749fn cedge(
1750 j: isize,
1751 i: isize,
1752 w: &mut [isize],
1753 first: usize,
1754 maxfirst: usize,
1755 delta_colcount: &mut [isize],
1756 prevleaf: usize,
1757 ancestor: usize,
1758) {
1759 let mut q;
1760 let mut s;
1761 let mut sparent;
1762
1763 if i <= j || w[(first as isize + j) as usize] <= w[(maxfirst as isize + i) as usize] {
1764 return;
1765 }
1766 w[(maxfirst as isize + i) as usize] = w[(first as isize + j) as usize]; // update max first[j] seen so far
1767 let jprev = w[(prevleaf as isize + i) as usize]; // j is a leaf of the ith subtree
1768 delta_colcount[j as usize] += 1; // A(i,j) is in the skeleton matrix
1769 if jprev != -1 {
1770 // q = least common ancestor of jprev and j
1771 q = jprev;
1772 while q != w[(ancestor as isize + q) as usize] {
1773 // increment
1774 q = w[(ancestor as isize + q) as usize];
1775 }
1776 s = jprev;
1777 while s != q {
1778 sparent = w[(ancestor as isize + s) as usize]; // path compression
1779 w[(ancestor as isize + s) as usize] = q;
1780 // increment
1781 s = sparent;
1782 }
1783 delta_colcount[q as usize] -= 1; // decrement to account for overlap in q
1784 }
1785 w[(prevleaf as isize + i) as usize] = j; // j is now previous leaf of ith subtree
1786}
1787
1788/// colcount = column counts of LL'=A or LL'=A'A, given parent & post ordering
1789///
1790fn counts(a: &Sprs, parent: &[isize], post: &[isize], ata: bool) -> Vec<isize> {
1791 let m = a.m;
1792 let n = a.n;
1793
1794 let s = if ata { 4 * n + n + m + 1 } else { 4 * n };
1795
1796 let mut w = vec![0; s]; // get workspace
1797 let first = 3 * n; // pointer for w
1798 let ancestor = 0; // pointer for w
1799 let maxfirst = n; // pointer for w
1800 let prevleaf = 2 * n; // pointer for w
1801 let head = 4 * n; // pointer for w
1802 let next = 5 * n + 1; // pointer for w
1803 let mut delta_colcount = vec![0; n]; // allocate result || CSparse: delta = colcount = cs_malloc (n, sizeof (int)) ;
1804 let mut j;
1805 let mut k;
1806
1807 let at = transpose(a);
1808 w[0..s].fill(-1); // clear workspace [0..s-1]
1809 for (k, &p) in post.iter().enumerate() {
1810 // find first [j]
1811 j = p;
1812 delta_colcount[j as usize] = match w[(first as isize + j) as usize] {
1813 -1 => 1,
1814 _ => 0,
1815 };
1816 while j != -1 && w[(first as isize + j) as usize] == -1 {
1817 w[(first as isize + j) as usize] = k as isize;
1818
1819 // increment
1820 j = parent[j as usize];
1821 }
1822 }
1823
1824 if ata {
1825 for k in 0..n {
1826 w[post[k] as usize] = k as isize; // invert post
1827 }
1828 for i in 0..m {
1829 k = n; // k = least post-ordered column in row i
1830 for p in at.p[i]..at.p[i + 1] {
1831 k = std::cmp::min(k, w[at.i[p as usize]] as usize);
1832 }
1833 w[next + i] = w[head + k]; // place row i in link list k
1834 w[head + k] = i as isize;
1835 }
1836 }
1837 for i in 0..n {
1838 w[ancestor + i] = i as isize; // each node in its own set
1839 }
1840 for k in 0..n {
1841 j = post[k]; // j is the kth node in post-ordered etree
1842 if parent[j as usize] != -1 {
1843 delta_colcount[parent[j as usize] as usize] -= 1; // j is not a root
1844 }
1845 if ata {
1846 let mut ii = w[head + k];
1847 while ii != -1 {
1848 for p in at.p[ii as usize]..at.p[ii as usize + 1] {
1849 cedge(
1850 j,
1851 at.i[p as usize] as isize,
1852 &mut w[..],
1853 first,
1854 maxfirst,
1855 &mut delta_colcount[..],
1856 prevleaf,
1857 ancestor,
1858 );
1859 }
1860
1861 // increment
1862 ii = w[(next as isize + ii) as usize];
1863 }
1864 } else {
1865 for p in at.p[j as usize]..at.p[j as usize + 1] {
1866 cedge(
1867 j,
1868 at.i[p as usize] as isize,
1869 &mut w[..],
1870 first,
1871 maxfirst,
1872 &mut delta_colcount[..],
1873 prevleaf,
1874 ancestor,
1875 );
1876 }
1877 }
1878 if parent[j as usize] != -1 {
1879 w[(ancestor as isize + j) as usize] = parent[j as usize];
1880 }
1881 }
1882 for j in 0..n {
1883 // sum up delta's of each child
1884 if parent[j] != -1 {
1885 delta_colcount[parent[j] as usize] += delta_colcount[j];
1886 }
1887 }
1888
1889 delta_colcount
1890}
1891
1892/// p [0..n] = cumulative sum of c [0..n-1], and then copy p [0..n-1] into c
1893///
1894fn cumsum(p: &mut [isize], c: &mut [isize], n: usize) -> usize {
1895 let mut nz = 0;
1896 for (p_i, c_i) in p.iter_mut().zip(c.iter_mut()).take(n) {
1897 *p_i = nz;
1898 nz += *c_i;
1899 *c_i = *p_i;
1900 }
1901 p[n] = nz;
1902
1903 nz as usize
1904}
1905
1906/// depth-first-search of the graph of a matrix, starting at node j
1907/// if pstack_i is used for pstack=xi[pstack_i]
1908///
1909fn dfs(
1910 j: usize,
1911 l: &mut Sprs,
1912 top: usize,
1913 xi: &mut [isize],
1914 pstack_i: &usize,
1915 pinv: &Option<Vec<isize>>,
1916) -> usize {
1917 let mut i;
1918 let mut j = j;
1919 let mut jnew;
1920 let mut head = 0;
1921 let mut done;
1922 let mut p2;
1923 let mut top = top;
1924
1925 xi[0] = j as isize; // initialize the recursion stack
1926 while head >= 0 {
1927 j = xi[head as usize] as usize; // get j from the top of the recursion stack
1928 if pinv.is_some() {
1929 jnew = pinv.as_ref().unwrap()[j];
1930 } else {
1931 jnew = j as isize;
1932 }
1933 if !marked(&l.p[..], j) {
1934 mark(&mut l.p[..], j); // mark node j as visited
1935 if jnew < 0 {
1936 xi[pstack_i + head as usize] = 0;
1937 } else {
1938 xi[pstack_i + head as usize] = unflip(l.p[jnew as usize]);
1939 }
1940 }
1941 done = true; // node j done if no unvisited neighbors
1942 if jnew < 0 {
1943 p2 = 0;
1944 } else {
1945 p2 = unflip(l.p[(jnew + 1) as usize]);
1946 }
1947 for p in xi[pstack_i + head as usize]..p2 {
1948 // examine all neighbors of j
1949 i = l.i[p as usize]; // consider neighbor node i
1950 if marked(&l.p[..], i) {
1951 continue; // skip visited node i
1952 }
1953 xi[pstack_i + head as usize] = p; // pause depth-first search of node j
1954 head += 1;
1955 xi[head as usize] = i as isize; // start dfs at node i
1956 done = false; // node j is not done
1957 break; // break, to start dfs (i)
1958 }
1959 if done {
1960 // depth-first search at node j is done
1961 head -= 1; // remove j from the recursion stack
1962 top -= 1;
1963 xi[top] = j as isize; // and place in the output stack
1964 }
1965 }
1966
1967 top
1968}
1969
1970/// keep off-diagonal entries; drop diagonal entries
1971///
1972fn diag(i: isize, j: isize, _: f64) -> bool {
1973 i != j
1974}
1975
1976/// compute nonzero pattern of L(k,:)
1977///
1978fn ereach(
1979 a: &Sprs,
1980 k: usize,
1981 parent: &[isize],
1982 s: usize,
1983 w: &mut [isize],
1984 x: &mut [f64],
1985 top: usize,
1986) -> usize {
1987 let mut top = top;
1988 let mut i;
1989 let mut len;
1990 for p in a.p[k]..a.p[k + 1] {
1991 // get pattern of L(k,:)
1992 i = a.i[p as usize] as isize; // A(i,k) is nonzero
1993 if i > k as isize {
1994 continue; // only use upper triangular part of A
1995 }
1996 x[i as usize] = a.x[p as usize]; // x(i) = A(i,k)
1997 len = 0;
1998 while w[i as usize] != k as isize {
1999 // traverse up etree
2000 w[s + len] = i; // L(k,i) is nonzero
2001 len += 1;
2002 w[i as usize] = k as isize; // mark i as visited
2003
2004 // increment statement
2005 i = parent[i as usize];
2006 }
2007
2008 for j in 1..(len + 1) {
2009 top -= 1;
2010 w[s + top] = w[s + len - j]; // push path on stack
2011 }
2012 }
2013
2014 top // s [top..n-1] contains pattern of L(k,:)
2015}
2016
2017/// compute the etree of A (using triu(A), or A'A without forming A'A
2018///
2019fn etree(a: &Sprs, ata: bool) -> Vec<isize> {
2020 let mut parent = vec![0; a.n];
2021 let mut i;
2022 let mut inext;
2023
2024 let mut w = if ata {
2025 vec![0; a.n + a.m]
2026 } else {
2027 vec![0; a.n]
2028 };
2029
2030 let ancestor = 0;
2031 let prev = ancestor + a.n;
2032
2033 if ata {
2034 w[prev..prev + a.m].fill(-1);
2035 }
2036
2037 for k in 0..a.n {
2038 parent[k] = -1; // node k has no parent yet
2039 w[ancestor + k] = -1; // nor does k have an ancestor
2040 for p in a.p[k] as usize..a.p[k + 1] as usize {
2041 if ata {
2042 i = w[prev + a.i[p]];
2043 } else {
2044 i = a.i[p] as isize;
2045 }
2046 while i != -1 && i < k as isize {
2047 // traverse from i to k
2048 inext = w[(ancestor as isize + i) as usize]; // inext = ancestor of i
2049 w[(ancestor as isize + i) as usize] = k as isize; // path compression
2050 if inext == -1 {
2051 parent[i as usize] = k as isize; // no anc., parent is k
2052 }
2053
2054 // increment
2055 i = inext
2056 }
2057 if ata {
2058 w[prev + a.i[p]] = k as isize;
2059 }
2060 }
2061 }
2062
2063 parent
2064}
2065
2066/// drop entries for which fkeep(A(i,j)) is false; return nz if OK, else -1
2067///
2068fn fkeep(a: &mut Sprs, f: &dyn Fn(isize, isize, f64) -> bool) -> isize {
2069 let mut p;
2070 let mut nz = 0;
2071 let n = a.n;
2072 for j in 0..n {
2073 p = a.p[j]; // get current location of col j
2074 a.p[j] = nz; // record new location of col j
2075 while p < a.p[j + 1] {
2076 if f(a.i[p as usize] as isize, j as isize, a.x[p as usize]) {
2077 // a.x always exists
2078 a.x[nz as usize] = a.x[p as usize]; // keep a(i,j)
2079 a.i[nz as usize] = a.i[p as usize];
2080 nz += 1;
2081 }
2082 p += 1;
2083 }
2084 }
2085 a.p[n] = nz;
2086
2087 nz
2088}
2089
2090/// apply the ith Householder vector to x
2091///
2092fn happly(v: &Sprs, i: usize, beta: f64, x: &mut [f64]) {
2093 let mut tau = 0.;
2094
2095 for p in v.p[i]..v.p[i + 1] {
2096 // tau = v'*x
2097 tau += v.x[p as usize] * x[v.i[p as usize]];
2098 }
2099 tau *= beta; // tau = beta*(v'*x)
2100 for p in v.p[i]..v.p[i + 1] {
2101 // x = x - v*tau
2102 x[v.i[p as usize]] -= v.x[p as usize] * tau;
2103 }
2104}
2105
2106/// create a Householder reflection [v,beta,s]=house(x), overwrite x with v,
2107/// where (I-beta*v*v')*x = s*x. See Algo 5.1.1, Golub & Van Loan, 3rd ed.
2108///
2109fn house(
2110 x: &mut [f64],
2111 xp: Option<usize>,
2112 beta: &mut [f64],
2113 betap: Option<usize>,
2114 n: usize,
2115) -> f64 {
2116 let s;
2117 let xp = xp.unwrap_or(0);
2118 let betap = betap.unwrap_or(0);
2119
2120 let sigma: f64 = (1..n).map(|i| x[i + xp] * x[i + xp]).sum();
2121 // FIXME: float comparison with bit-wise not equals
2122 if sigma != 0. {
2123 s = (x[xp] * x[xp] + sigma).sqrt(); // s = norm (x)
2124 x[xp] = if x[xp] <= 0. {
2125 x[xp] - s
2126 } else {
2127 -sigma / (x[xp] + s)
2128 };
2129 beta[betap] = (-s * x[xp]).recip();
2130 } else {
2131 s = x[xp].abs(); // s = |x(0)|
2132 beta[betap] = if x[xp] <= 0. { 2. } else { 0. };
2133 x[xp] = 1.;
2134 }
2135
2136 s
2137}
2138
2139/// x(P) = b, for dense vectors x and b; P=None denotes identity
2140///
2141fn ipvec(n: usize, p: &Option<Vec<isize>>, b: &[f64], x: &mut [f64]) {
2142 for k in 0..n {
2143 if p.is_some() {
2144 x[p.as_ref().unwrap()[k] as usize] = b[k];
2145 } else {
2146 x[k] = b[k];
2147 }
2148 }
2149}
2150
2151/// C = A(P,Q) where P and Q are permutations of 0..m-1 and 0..n-1
2152///
2153fn permute(a: &Sprs, pinv: &Option<Vec<isize>>, q: &Option<Vec<isize>>) -> Sprs {
2154 let mut j;
2155 let mut nz = 0;
2156 let mut c = Sprs::zeros(a.m, a.n, a.p[a.n] as usize);
2157
2158 for k in 0..a.n {
2159 c.p[k] = nz as isize; // column k of C is column Q[k] of A
2160 if q.is_some() {
2161 j = q.as_ref().unwrap()[k] as usize;
2162 } else {
2163 j = k;
2164 }
2165 for p in a.p[j] as usize..a.p[j + 1] as usize {
2166 c.x[nz] = a.x[p]; // row i of A is row Pinv[i] of C
2167 if pinv.is_some() {
2168 c.i[nz] = pinv.as_ref().unwrap()[a.i[p]] as usize;
2169 } else {
2170 c.i[nz] = a.i[p];
2171 }
2172 nz += 1;
2173 }
2174 }
2175 c.p[a.n] = nz as isize;
2176
2177 c
2178}
2179
2180/// Pinv = P', or P = Pinv'
2181///
2182fn pinvert(p: &Option<Vec<isize>>, n: usize) -> Option<Vec<isize>> {
2183 // pinv
2184 if p.is_none() {
2185 // p = None denotes identity
2186 return None;
2187 }
2188
2189 let mut pinv = vec![0; n]; // allocate result
2190 for k in 0..n {
2191 pinv[p.as_ref().unwrap()[k] as usize] = k as isize; // invert the permutation
2192 }
2193
2194 Some(pinv)
2195}
2196
2197/// post order a forest
2198///
2199fn post(n: usize, parent: &[isize]) -> Vec<isize> {
2200 let mut k = 0;
2201 let mut post = vec![0; n]; // allocate result
2202 let mut w = vec![0; 3 * n]; // 3*n workspace
2203 let head = 0; // pointer for w
2204 let next = head + n; // pointer for w
2205 let stack = head + 2 * n; // pointer for w
2206
2207 for j in 0..n {
2208 w[head + j] = -1; // empty link lists
2209 }
2210 for j in (0..n).rev() {
2211 // traverse nodes in reverse order
2212 if parent[j] == -1 {
2213 continue; // j is a root
2214 }
2215 w[next + j] = w[(head as isize + parent[j]) as usize]; // add j to list of its parent
2216 w[(head as isize + parent[j]) as usize] = j as isize;
2217 }
2218 for (j, par) in parent.iter().enumerate().take(n) {
2219 if *par != -1 {
2220 continue; // skip j if it is not a root
2221 }
2222 k = tdfs(j as isize, k, &mut w[..], head, next, &mut post[..], stack);
2223 }
2224
2225 post
2226}
2227
2228/// x = b(P), for dense vectors x and b; P=None denotes identity
2229///
2230fn pvec(n: usize, p: &Option<Vec<isize>>, b: &[f64], x: &mut [f64]) {
2231 for (k, x_k) in x.iter_mut().enumerate().take(n) {
2232 *x_k = match p {
2233 Some(p) => b[p[k] as usize],
2234 None => b[k],
2235 };
2236 }
2237}
2238
2239/// xi [top...n-1] = nodes reachable from graph of L*P' via nodes in B(:,k).
2240/// xi [n...2n-1] used as workspace.
2241///
2242fn reach(l: &mut Sprs, b: &Sprs, k: usize, xi: &mut [isize], pinv: &Option<Vec<isize>>) -> usize {
2243 let mut top = l.n;
2244
2245 for p in b.p[k] as usize..b.p[k + 1] as usize {
2246 if !marked(&l.p[..], b.i[p]) {
2247 // start a dfs at unmarked node i
2248 let n = l.n;
2249 top = dfs(b.i[p], l, top, &mut xi[..], &n, pinv);
2250 }
2251 }
2252 for i in xi.iter().take(l.n).skip(top) {
2253 mark(&mut l.p[..], *i as usize); // restore L
2254 }
2255
2256 top
2257}
2258
2259/// x = x + beta * A(:,j), where x is a dense vector and A(:,j) is sparse
2260///
2261fn scatter(
2262 a: &Sprs,
2263 j: usize,
2264 beta: f64,
2265 w: &mut [isize],
2266 x: &mut [f64],
2267 mark: usize,
2268 c: &mut Sprs,
2269 nz: usize,
2270) -> usize {
2271 let mut i;
2272 let mut nzo = nz;
2273 for p in a.p[j] as usize..a.p[j + 1] as usize {
2274 i = a.i[p]; // A(i,j) is nonzero
2275 if w[i] < mark as isize {
2276 w[i] = mark as isize; // i is new entry in column j
2277 c.i[nzo] = i; // add i to pattern of C(:,j)
2278 nzo += 1;
2279 x[i] = beta * a.x[p]; // x(i) = beta*A(i,j)
2280 } else {
2281 x[i] += beta * a.x[p]; // i exists in C(:,j) already
2282 }
2283 }
2284
2285 nzo
2286}
2287
2288/// beta * A(:,j), where A(:,j) is sparse. For QR decomposition
2289///
2290fn scatter_no_x(j: usize, w: &mut [isize], mark: usize, c: &mut Sprs, nz: usize) -> usize {
2291 let mut i;
2292 let mut nzo = nz;
2293 for p in c.p[j] as usize..c.p[j + 1] as usize {
2294 i = c.i[p]; // A(i,j) is nonzero
2295 if w[i] < mark as isize {
2296 w[i] = mark as isize; // i is new entry in column j
2297 c.i[nzo] = i; // add i to pattern of C(:,j)
2298 nzo += 1;
2299 }
2300 }
2301
2302 nzo
2303}
2304
2305/// Solve Lx=b(:,k), leaving pattern in xi[top..n-1], values scattered in x.
2306///
2307fn splsolve(
2308 l: &mut Sprs,
2309 b: &Sprs,
2310 k: usize,
2311 xi: &mut [isize],
2312 x: &mut [f64],
2313 pinv: &Option<Vec<isize>>,
2314) -> usize {
2315 let mut jnew;
2316 let top = reach(l, b, k, &mut xi[..], pinv); // xi[top..n-1]=Reach(B(:,k))
2317
2318 for p in top..l.n {
2319 x[xi[p] as usize] = 0.; // clear x
2320 }
2321 for p in b.p[k] as usize..b.p[k + 1] as usize {
2322 x[b.i[p]] = b.x[p]; // scatter B
2323 }
2324 for j in xi.iter().take(l.n).skip(top) {
2325 let j_u = *j as usize; // x(j) is nonzero
2326 jnew = match pinv {
2327 Some(pinv) => pinv[j_u], // j is column jnew of L
2328 None => *j, // j is column jnew of L
2329 };
2330 if jnew < 0 {
2331 continue; // column jnew is empty
2332 }
2333 for p in (l.p[jnew as usize] + 1) as usize..l.p[jnew as usize + 1] as usize {
2334 x[l.i[p]] -= l.x[p] * x[j_u]; // x(i) -= L(i,j) * x(j)
2335 }
2336 }
2337
2338 top // return top of stack
2339}
2340
2341/// C = A(p,p) where A and C are symmetric the upper part stored, Pinv not P
2342///
2343fn symperm(a: &Sprs, pinv: &Option<Vec<isize>>) -> Sprs {
2344 let n = a.n;
2345 let mut i;
2346 let mut i2;
2347 let mut j2;
2348 let mut q;
2349 let mut c = Sprs::zeros(n, n, a.p[n] as usize);
2350 let mut w = vec![0; n];
2351
2352 for j in 0..n {
2353 //count entries in each column of C
2354 j2 = pinv.as_ref().map_or(j, |pinv| pinv[j] as usize); // column j of A is column j2 of C
2355
2356 for p in a.p[j]..a.p[j + 1] {
2357 i = a.i[p as usize];
2358 if i > j {
2359 continue; // skip lower triangular part of A
2360 }
2361 i2 = pinv.as_ref().map_or(i, |pinv| pinv[i] as usize); // row i of A is row i2 of C
2362 w[std::cmp::max(i2, j2)] += 1; // column count of C
2363 }
2364 }
2365 cumsum(&mut c.p[..], &mut w[..], n); // compute column pointers of C
2366 for j in 0..n {
2367 j2 = pinv.as_ref().map_or(j, |pinv| pinv[j] as usize); // column j of A is column j2 of C
2368 for p in a.p[j]..a.p[j + 1] {
2369 i = a.i[p as usize];
2370 if i > j {
2371 continue; // skip lower triangular part of A
2372 }
2373 i2 = pinv.as_ref().map_or(i, |pinv| pinv[i] as usize); // row i of A is row i2 of C
2374 q = w[std::cmp::max(i2, j2)] as usize;
2375 w[std::cmp::max(i2, j2)] += 1;
2376 c.i[q] = std::cmp::min(i2, j2);
2377 c.x[q] = a.x[p as usize];
2378 }
2379 }
2380
2381 c
2382}
2383
2384/// depth-first search and postorder of a tree rooted at node j (for fn amd())
2385///
2386fn tdfs(
2387 j: isize,
2388 k: isize,
2389 ww: &mut [isize],
2390 head: usize,
2391 next: usize,
2392 post: &mut [isize],
2393 stack: usize,
2394) -> isize {
2395 let mut i;
2396 let mut p;
2397 let mut top = 0;
2398 let mut k = k;
2399
2400 ww[stack] = j; // place j on the stack
2401 while top >= 0 {
2402 // while (stack is not empty)
2403 p = ww[(stack as isize + top) as usize]; // p = top of stack
2404 i = ww[(head as isize + p) as usize]; // i = youngest child of p
2405 match i {
2406 -1 => {
2407 top -= 1; // p has no unordered children left
2408 post[k as usize] = p; // node p is the kth post-ordered node
2409 k += 1;
2410 }
2411 _ => {
2412 ww[(head as isize + p) as usize] = ww[(next as isize + i) as usize]; // remove i from children of p
2413 top += 1;
2414 ww[(stack as isize + top) as usize] = i; // start dfs on child node i
2415 }
2416 }
2417 }
2418
2419 k
2420}
2421
2422/// compute vnz, Pinv, leftmost, m2 from A and parent
2423///
2424fn vcount(a: &Sprs, parent: &[isize], m2: &mut usize, vnz: &mut usize) -> Option<Vec<isize>> {
2425 let n = a.n;
2426 let m = a.m;
2427
2428 let mut pinv: Vec<isize> = vec![0; 2 * m + n];
2429 let leftmost = m + n; // pointer of pinv
2430 let mut w = vec![0; m + 3 * n];
2431
2432 // pointers for w
2433 let next = 0;
2434 let head = m;
2435 let tail = m + n;
2436 let nque = m + 2 * n;
2437
2438 w[head..head + n].fill(-1); // queue k is empty
2439 w[tail..tail + n].fill(-1);
2440 w[nque..nque + n].fill(0);
2441 pinv[leftmost..leftmost + m].fill(-1);
2442 for k in (0..n).rev() {
2443 for p in a.p[k]..a.p[k + 1] {
2444 pinv[leftmost + a.i[p as usize]] = k as isize; // leftmost[i] = min(find(A(i,:)))
2445 }
2446 }
2447 let mut k;
2448 for i in (0..m).rev() {
2449 // scan rows in reverse order
2450 pinv[i] = -1; // row i is not yet ordered
2451 k = pinv[leftmost + i];
2452 if k == -1 {
2453 continue; // row i is empty
2454 }
2455 if w[(nque as isize + k) as usize] == 0 {
2456 w[(tail as isize + k) as usize] = i as isize; // first row in queue k
2457 }
2458 w[(nque as isize + k) as usize] += 1;
2459 w[next + i] = w[(head as isize + k) as usize]; // put i at head of queue k
2460 w[(head as isize + k) as usize] = i as isize;
2461 }
2462 *vnz = 0;
2463 *m2 = m;
2464 let mut i;
2465 for k in 0..n {
2466 // find row permutation and nnz(V)
2467 i = w[head + k]; // remove row i from queue k
2468 *vnz += 1; // count V(k,k) as nonzero
2469 if i < 0 {
2470 i = *m2 as isize; // add a fictitious row
2471 *m2 += 1;
2472 }
2473 pinv[i as usize] = k as isize; // associate row i with V(:,k)
2474 w[nque + k] -= 1;
2475 if w[nque + k] <= 0 {
2476 continue; // skip if V(k+1:m,k) is empty
2477 }
2478 *vnz += w[nque + k] as usize; // nque [k] = nnz (V(k+1:m,k))
2479 let pa = parent[k]; // move all rows to parent of k
2480 if pa != -1 {
2481 if w[(nque as isize + pa) as usize] == 0 {
2482 w[(tail as isize + pa) as usize] = w[tail + k];
2483 }
2484 let tw = w[tail + k];
2485 w[(next as isize + tw) as usize] = w[(head as isize + pa) as usize];
2486 w[(head as isize + pa) as usize] = w[(next as isize + i) as usize];
2487 w[(nque as isize + pa) as usize] += w[nque + k];
2488 }
2489 }
2490 let mut k = n;
2491 for p in pinv.iter_mut().take(m) {
2492 if *p < 0 {
2493 *p = k as isize;
2494 k += 1;
2495 }
2496 }
2497
2498 Some(pinv)
2499}
2500
2501/// clears W
2502///
2503fn wclear(mark_v: isize, lemax: isize, ww: &mut [isize], w: usize, n: usize) -> isize {
2504 let mut mark = mark_v;
2505 if mark < 2 || (mark + lemax < 0) {
2506 for k in 0..n {
2507 if ww[w + k] != 0 {
2508 ww[w + k] = 1;
2509 }
2510 }
2511 mark = 2;
2512 }
2513
2514 mark // at this point, w [0..n-1] < mark holds
2515}
2516
2517// --- Inline functions --------------------------------------------------------
2518
2519#[inline]
2520fn flip(i: isize) -> isize {
2521 -(i) - 2
2522}
2523
2524#[inline]
2525fn unflip(i: isize) -> isize {
2526 if i < 0 {
2527 flip(i)
2528 } else {
2529 i
2530 }
2531}
2532
2533#[inline]
2534fn marked(ap: &[isize], j: usize) -> bool {
2535 ap[j] < 0
2536}
2537
2538#[inline]
2539fn mark(ap: &mut [isize], j: usize) {
2540 ap[j] = flip(ap[j])
2541}