UNPKG

4.66 kBJavaScriptView Raw
1import { csPermute } from './csPermute'
2import { csPost } from './csPost'
3import { csEtree } from './csEtree'
4import { createCsAmd } from './csAmd'
5import { createCsCounts } from './csCounts'
6import { factory } from '../../../utils/factory'
7
8const name = 'csSqr'
9const dependencies = [
10 'add',
11 'multiply',
12 'transpose'
13]
14
15export const createCsSqr = /* #__PURE__ */ factory(name, dependencies, ({ add, multiply, transpose }) => {
16 const csAmd = createCsAmd({ add, multiply, transpose })
17 const csCounts = createCsCounts({ transpose })
18
19 /**
20 * Symbolic ordering and analysis for QR and LU decompositions.
21 *
22 * @param {Number} order The ordering strategy (see csAmd for more details)
23 * @param {Matrix} a The A matrix
24 * @param {boolean} qr Symbolic ordering and analysis for QR decomposition (true) or
25 * symbolic ordering and analysis for LU decomposition (false)
26 *
27 * @return {Object} The Symbolic ordering and analysis for matrix A
28 *
29 * Reference: http://faculty.cse.tamu.edu/davis/publications.html
30 */
31 return function csSqr (order, a, qr) {
32 // a arrays
33 const aptr = a._ptr
34 const asize = a._size
35 // columns
36 const n = asize[1]
37 // vars
38 let k
39 // symbolic analysis result
40 const s = {}
41 // fill-reducing ordering
42 s.q = csAmd(order, a)
43 // validate results
44 if (order && !s.q) { return null }
45 // QR symbolic analysis
46 if (qr) {
47 // apply permutations if needed
48 const c = order ? csPermute(a, null, s.q, 0) : a
49 // etree of C'*C, where C=A(:,q)
50 s.parent = csEtree(c, 1)
51 // post order elimination tree
52 const post = csPost(s.parent, n)
53 // col counts chol(C'*C)
54 s.cp = csCounts(c, s.parent, post, 1)
55 // check we have everything needed to calculate number of nonzero elements
56 if (c && s.parent && s.cp && _vcount(c, s)) {
57 // calculate number of nonzero elements
58 for (s.unz = 0, k = 0; k < n; k++) { s.unz += s.cp[k] }
59 }
60 } else {
61 // for LU factorization only, guess nnz(L) and nnz(U)
62 s.unz = 4 * (aptr[n]) + n
63 s.lnz = s.unz
64 }
65 // return result S
66 return s
67 }
68
69 /**
70 * Compute nnz(V) = s.lnz, s.pinv, s.leftmost, s.m2 from A and s.parent
71 */
72 function _vcount (a, s) {
73 // a arrays
74 const aptr = a._ptr
75 const aindex = a._index
76 const asize = a._size
77 // rows & columns
78 const m = asize[0]
79 const n = asize[1]
80 // initialize s arrays
81 s.pinv = [] // (m + n)
82 s.leftmost = [] // (m)
83 // vars
84 const parent = s.parent
85 const pinv = s.pinv
86 const leftmost = s.leftmost
87 // workspace, next: first m entries, head: next n entries, tail: next n entries, nque: next n entries
88 const w = [] // (m + 3 * n)
89 const next = 0
90 const head = m
91 const tail = m + n
92 const nque = m + 2 * n
93 // vars
94 let i, k, p, p0, p1
95 // initialize w
96 for (k = 0; k < n; k++) {
97 // queue k is empty
98 w[head + k] = -1
99 w[tail + k] = -1
100 w[nque + k] = 0
101 }
102 // initialize row arrays
103 for (i = 0; i < m; i++) { leftmost[i] = -1 }
104 // loop columns backwards
105 for (k = n - 1; k >= 0; k--) {
106 // values & index for column k
107 for (p0 = aptr[k], p1 = aptr[k + 1], p = p0; p < p1; p++) {
108 // leftmost[i] = min(find(A(i,:)))
109 leftmost[aindex[p]] = k
110 }
111 }
112 // scan rows in reverse order
113 for (i = m - 1; i >= 0; i--) {
114 // row i is not yet ordered
115 pinv[i] = -1
116 k = leftmost[i]
117 // check row i is empty
118 if (k === -1) { continue }
119 // first row in queue k
120 if (w[nque + k]++ === 0) { w[tail + k] = i }
121 // put i at head of queue k
122 w[next + i] = w[head + k]
123 w[head + k] = i
124 }
125 s.lnz = 0
126 s.m2 = m
127 // find row permutation and nnz(V)
128 for (k = 0; k < n; k++) {
129 // remove row i from queue k
130 i = w[head + k]
131 // count V(k,k) as nonzero
132 s.lnz++
133 // add a fictitious row
134 if (i < 0) { i = s.m2++ }
135 // associate row i with V(:,k)
136 pinv[i] = k
137 // skip if V(k+1:m,k) is empty
138 if (--nque[k] <= 0) { continue }
139 // nque[k] is nnz (V(k+1:m,k))
140 s.lnz += w[nque + k]
141 // move all rows to parent of k
142 const pa = parent[k]
143 if (pa !== -1) {
144 if (w[nque + pa] === 0) { w[tail + pa] = w[tail + k] }
145 w[next + w[tail + k]] = w[head + pa]
146 w[head + pa] = w[next + i]
147 w[nque + pa] += w[nque + k]
148 }
149 }
150 for (i = 0; i < m; i++) {
151 if (pinv[i] < 0) { pinv[i] = k++ }
152 }
153 return true
154 }
155})