UNPKG

4.96 kBJavaScriptView Raw
1import { factory } from '../../../utils/factory'
2import { createCsSpsolve } from './csSpsolve'
3
4const name = 'csLu'
5const dependencies = [
6 'abs',
7 'divideScalar',
8 'multiply',
9 'subtract',
10 'larger',
11 'largerEq',
12 'SparseMatrix'
13]
14
15export const createCsLu = /* #__PURE__ */ factory(name, dependencies, ({ abs, divideScalar, multiply, subtract, larger, largerEq, SparseMatrix }) => {
16 const csSpsolve = createCsSpsolve({ divideScalar, multiply, subtract })
17
18 /**
19 * Computes the numeric LU factorization of the sparse matrix A. Implements a Left-looking LU factorization
20 * algorithm that computes L and U one column at a tume. At the kth step, it access columns 1 to k-1 of L
21 * and column k of A. Given the fill-reducing column ordering q (see parameter s) computes L, U and pinv so
22 * L * U = A(p, q), where p is the inverse of pinv.
23 *
24 * @param {Matrix} m The A Matrix to factorize
25 * @param {Object} s The symbolic analysis from csSqr(). Provides the fill-reducing
26 * column ordering q
27 * @param {Number} tol Partial pivoting threshold (1 for partial pivoting)
28 *
29 * @return {Number} The numeric LU factorization of A or null
30 *
31 * Reference: http://faculty.cse.tamu.edu/davis/publications.html
32 */
33 return function csLu (m, s, tol) {
34 // validate input
35 if (!m) { return null }
36 // m arrays
37 const size = m._size
38 // columns
39 const n = size[1]
40 // symbolic analysis result
41 let q
42 let lnz = 100
43 let unz = 100
44 // update symbolic analysis parameters
45 if (s) {
46 q = s.q
47 lnz = s.lnz || lnz
48 unz = s.unz || unz
49 }
50 // L arrays
51 const lvalues = [] // (lnz)
52 const lindex = [] // (lnz)
53 const lptr = [] // (n + 1)
54 // L
55 const L = new SparseMatrix({
56 values: lvalues,
57 index: lindex,
58 ptr: lptr,
59 size: [n, n]
60 })
61 // U arrays
62 const uvalues = [] // (unz)
63 const uindex = [] // (unz)
64 const uptr = [] // (n + 1)
65 // U
66 const U = new SparseMatrix({
67 values: uvalues,
68 index: uindex,
69 ptr: uptr,
70 size: [n, n]
71 })
72 // inverse of permutation vector
73 const pinv = [] // (n)
74 // vars
75 let i, p
76 // allocate arrays
77 const x = [] // (n)
78 const xi = [] // (2 * n)
79 // initialize variables
80 for (i = 0; i < n; i++) {
81 // clear workspace
82 x[i] = 0
83 // no rows pivotal yet
84 pinv[i] = -1
85 // no cols of L yet
86 lptr[i + 1] = 0
87 }
88 // reset number of nonzero elements in L and U
89 lnz = 0
90 unz = 0
91 // compute L(:,k) and U(:,k)
92 for (let k = 0; k < n; k++) {
93 // update ptr
94 lptr[k] = lnz
95 uptr[k] = unz
96 // apply column permutations if needed
97 const col = q ? q[k] : k
98 // solve triangular system, x = L\A(:,col)
99 const top = csSpsolve(L, m, col, xi, x, pinv, 1)
100 // find pivot
101 let ipiv = -1
102 let a = -1
103 // loop xi[] from top -> n
104 for (p = top; p < n; p++) {
105 // x[i] is nonzero
106 i = xi[p]
107 // check row i is not yet pivotal
108 if (pinv[i] < 0) {
109 // absolute value of x[i]
110 const xabs = abs(x[i])
111 // check absoulte value is greater than pivot value
112 if (larger(xabs, a)) {
113 // largest pivot candidate so far
114 a = xabs
115 ipiv = i
116 }
117 } else {
118 // x(i) is the entry U(pinv[i],k)
119 uindex[unz] = pinv[i]
120 uvalues[unz++] = x[i]
121 }
122 }
123 // validate we found a valid pivot
124 if (ipiv === -1 || a <= 0) { return null }
125 // update actual pivot column, give preference to diagonal value
126 if (pinv[col] < 0 && largerEq(abs(x[col]), multiply(a, tol))) { ipiv = col }
127 // the chosen pivot
128 const pivot = x[ipiv]
129 // last entry in U(:,k) is U(k,k)
130 uindex[unz] = k
131 uvalues[unz++] = pivot
132 // ipiv is the kth pivot row
133 pinv[ipiv] = k
134 // first entry in L(:,k) is L(k,k) = 1
135 lindex[lnz] = ipiv
136 lvalues[lnz++] = 1
137 // L(k+1:n,k) = x / pivot
138 for (p = top; p < n; p++) {
139 // row
140 i = xi[p]
141 // check x(i) is an entry in L(:,k)
142 if (pinv[i] < 0) {
143 // save unpermuted row in L
144 lindex[lnz] = i
145 // scale pivot column
146 lvalues[lnz++] = divideScalar(x[i], pivot)
147 }
148 // x[0..n-1] = 0 for next k
149 x[i] = 0
150 }
151 }
152 // update ptr
153 lptr[n] = lnz
154 uptr[n] = unz
155 // fix row indices of L for final pinv
156 for (p = 0; p < lnz; p++) { lindex[p] = pinv[lindex[p]] }
157 // trim arrays
158 lvalues.splice(lnz, lvalues.length - lnz)
159 lindex.splice(lnz, lindex.length - lnz)
160 uvalues.splice(unz, uvalues.length - unz)
161 uindex.splice(unz, uindex.length - unz)
162 // return LU factor
163 return {
164 L: L,
165 U: U,
166 pinv: pinv
167 }
168 }
169})