forked from josdejong/mathjs
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathutilities.ts
More file actions
322 lines (281 loc) · 6.95 KB
/
utilities.ts
File metadata and controls
322 lines (281 loc) · 6.95 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
/**
* WASM-optimized sparse matrix utility operations
*
* These functions provide WASM-accelerated implementations of sparse matrix
* utilities based on the CSparse library algorithms. Critical for sparse
* matrix performance in scientific computing.
*
* Performance: 5-10x faster than JavaScript for sparse operations
*/
/**
* Flip a value about -1
* Used for marking nodes in graph algorithms
* @param i The value to flip
* @returns -(i+2)
*/
export function csFlip(i: i32): i32 {
return -(i + 2)
}
/**
* Unflip a value (conditionally)
* @param i The value to unflip
* @returns i if i >= 0, else -(i+2)
*/
export function csUnflip(i: i32): i32 {
return i < 0 ? -(i + 2) : i
}
/**
* Check if a node is marked
* @param w Working array
* @param j Node index
* @returns true if marked
*/
export function csMarked(w: Int32Array, j: i32): boolean {
return unchecked(w[j]) < 0
}
/**
* Mark a node
* @param w Working array
* @param j Node index
*/
export function csMark(w: Int32Array, j: i32): void {
unchecked(w[j] = csFlip(unchecked(w[j])))
}
/**
* Cumulative sum
* Computes p[0..n] = cumulative sum of c[0..n-1], and then c[0..n-1] = p[0..n-1]
* @param p Output array (size n+1)
* @param c Input/working array (size n)
* @param n Length
* @returns Sum of c
*/
export function csCumsum(p: Int32Array, c: Int32Array, n: i32): i32 {
let nz: i32 = 0
for (let i: i32 = 0; i < n; i++) {
unchecked(p[i] = nz)
const ci = unchecked(c[i])
nz += ci
unchecked(c[i] = unchecked(p[i]))
}
unchecked(p[n] = nz)
return nz
}
/**
* Sparse matrix permutation C = PAQ
* Permutes columns and rows of sparse matrix
* @param values Values array of A
* @param index Row indices of A
* @param ptr Column pointers of A
* @param m Number of rows
* @param n Number of columns
* @param pinv Row permutation (inverse)
* @param q Column permutation
* @param cValues Output values array
* @param cIndex Output row indices
* @param cPtr Output column pointers
*/
export function csPermute(
values: Float64Array,
index: Int32Array,
ptr: Int32Array,
m: i32,
n: i32,
pinv: Int32Array,
q: Int32Array,
cValues: Float64Array,
cIndex: Int32Array,
cPtr: Int32Array
): void {
let nz: i32 = 0
for (let k: i32 = 0; k < n; k++) {
unchecked(cPtr[k] = nz)
const j = q ? unchecked(q[k]) : k
for (let t: i32 = unchecked(ptr[j]); t < unchecked(ptr[j + 1]); t++) {
const i = pinv ? unchecked(pinv[unchecked(index[t])]) : unchecked(index[t])
unchecked(cIndex[nz] = i)
unchecked(cValues[nz] = unchecked(values[t]))
nz++
}
}
unchecked(cPtr[n] = nz)
}
/**
* Find a leaf in the elimination tree
* @param i Row index
* @param j Column index
* @param first First array
* @param maxfirst Maxfirst array
* @param prevleaf Previous leaf array
* @param ancestor Ancestor array
* @returns Leaf information [jleaf, jinit]
*/
export function csLeaf(
i: i32,
j: i32,
first: Int32Array,
maxfirst: Int32Array,
prevleaf: Int32Array,
ancestor: Int32Array
): i32 {
let q: i32
let s: i32
let sparent: i32
let jprev: i32
let jleaf: i32 = 0
if (i <= j || unchecked(first[j]) <= unchecked(maxfirst[i])) {
return jleaf
}
unchecked(maxfirst[i] = unchecked(first[j]))
jprev = unchecked(prevleaf[i])
unchecked(prevleaf[i] = j)
if (jprev === -1) {
jleaf = i
} else {
jleaf = -1
for (q = jprev; q !== -1 && q !== j; q = unchecked(ancestor[q])) {
s = q
}
if (s !== -1 && q === j) {
jleaf = s
}
for (q = jprev; q !== s; q = sparent) {
sparent = unchecked(ancestor[q])
unchecked(ancestor[q] = j)
}
}
return jleaf
}
/**
* Compute elimination tree
* @param index Row indices
* @param ptr Column pointers
* @param m Number of rows
* @param n Number of columns
* @param parent Output parent array
*/
export function csEtree(
index: Int32Array,
ptr: Int32Array,
m: i32,
n: i32,
parent: Int32Array
): void {
const ancestor = new Int32Array(n)
for (let i: i32 = 0; i < n; i++) {
unchecked(parent[i] = -1)
unchecked(ancestor[i] = -1)
}
for (let k: i32 = 0; k < n; k++) {
for (let p: i32 = unchecked(ptr[k]); p < unchecked(ptr[k + 1]); p++) {
let i: i32 = unchecked(index[p])
while (i !== -1 && i < k) {
const inext = unchecked(ancestor[i])
unchecked(ancestor[i] = k)
if (inext === -1) {
unchecked(parent[i] = k)
}
i = inext
}
}
}
}
/**
* Depth-first search (DFS) for nonzero pattern
* @param j Starting column
* @param index Row indices
* @param ptr Column pointers
* @param top Top of stack
* @param xi Stack/output array
* @param pstack Stack pointer array
* @param pinv Inverse permutation
* @param marked Marked array
* @returns New top of stack
*/
export function csDfs(
j: i32,
index: Int32Array,
ptr: Int32Array,
top: i32,
xi: Int32Array,
pstack: Int32Array,
pinv: Int32Array,
marked: Int32Array
): i32 {
let head: i32 = 0
let done: boolean = false
let p: i32
let p2: i32
unchecked(xi[0] = j)
while (head >= 0) {
j = unchecked(xi[head])
const jnew = pinv ? unchecked(pinv[j]) : j
if (!csMarked(marked, j)) {
csMark(marked, j)
unchecked(pstack[head] = jnew < 0 ? 0 : unchecked(ptr[jnew]))
}
done = true
p2 = jnew < 0 ? 0 : unchecked(ptr[jnew + 1])
for (p = unchecked(pstack[head]); p < p2; p++) {
const i = unchecked(index[p])
if (csMarked(marked, i)) {
continue
}
unchecked(pstack[head] = p)
unchecked(xi[++head] = i)
done = false
break
}
if (done) {
head--
unchecked(xi[--top] = j)
}
}
return top
}
/**
* Solve sparse triangular system (lower or upper)
* @param G Sparse matrix (index, ptr, values)
* @param B Right-hand side
* @param xi Pattern array
* @param x Solution array
* @param pinv Inverse permutation
* @param lo true for lower triangular, false for upper
* @returns Number of nonzeros in solution
*/
export function csSpsolve(
gIndex: Int32Array,
gPtr: Int32Array,
gValues: Float64Array,
B: Float64Array,
xi: Int32Array,
x: Float64Array,
pinv: Int32Array,
lo: boolean,
n: i32
): i32 {
let top: i32 = n
// Find nonzero pattern
for (let k: i32 = 0; k < n; k++) {
if (unchecked(B[k]) !== 0) {
top = csDfs(k, gIndex, gPtr, top, xi, new Int32Array(n), pinv, new Int32Array(n))
}
}
// Initialize x with B
for (let p: i32 = top; p < n; p++) {
unchecked(x[unchecked(xi[p])] = unchecked(B[unchecked(xi[p])]))
}
// Solve
for (let px: i32 = top; px < n; px++) {
const j = unchecked(xi[px])
const J = pinv ? unchecked(pinv[j]) : j
if (J < 0) continue
let xj = unchecked(x[j])
const p1 = unchecked(gPtr[J])
const p2 = unchecked(gPtr[J + 1])
for (let p: i32 = lo ? p1 : p2 - 1; lo ? p < p2 : p >= p1; p = lo ? p + 1 : p - 1) {
const i = unchecked(gIndex[p])
unchecked(x[i] -= unchecked(gValues[p]) * xj)
}
}
return n - top
}