mathjs
Version:
Math.js is an extensive math library for JavaScript and Node.js. It features a flexible expression parser with support for symbolic computation, comes with a large set of built-in functions and constants, and offers an integrated solution to work with dif
170 lines (166 loc) • 5.13 kB
JavaScript
import { factory } from '../../../utils/factory'
import { createCsSpsolve } from './csSpsolve'
const name = 'csLu'
const dependencies = [
'abs',
'divideScalar',
'multiply',
'subtract',
'larger',
'largerEq',
'SparseMatrix'
]
export const createCsLu = /* #__PURE__ */ factory(name, dependencies, ({ abs, divideScalar, multiply, subtract, larger, largerEq, SparseMatrix }) => {
const csSpsolve = createCsSpsolve({ divideScalar, multiply, subtract })
/**
* Computes the numeric LU factorization of the sparse matrix A. Implements a Left-looking LU factorization
* algorithm that computes L and U one column at a tume. At the kth step, it access columns 1 to k-1 of L
* and column k of A. Given the fill-reducing column ordering q (see parameter s) computes L, U and pinv so
* L * U = A(p, q), where p is the inverse of pinv.
*
* @param {Matrix} m The A Matrix to factorize
* @param {Object} s The symbolic analysis from csSqr(). Provides the fill-reducing
* column ordering q
* @param {Number} tol Partial pivoting threshold (1 for partial pivoting)
*
* @return {Number} The numeric LU factorization of A or null
*
* Reference: http://faculty.cse.tamu.edu/davis/publications.html
*/
return function csLu (m, s, tol) {
// validate input
if (!m) { return null }
// m arrays
const size = m._size
// columns
const n = size[1]
// symbolic analysis result
let q
let lnz = 100
let unz = 100
// update symbolic analysis parameters
if (s) {
q = s.q
lnz = s.lnz || lnz
unz = s.unz || unz
}
// L arrays
const lvalues = [] // (lnz)
const lindex = [] // (lnz)
const lptr = [] // (n + 1)
// L
const L = new SparseMatrix({
values: lvalues,
index: lindex,
ptr: lptr,
size: [n, n]
})
// U arrays
const uvalues = [] // (unz)
const uindex = [] // (unz)
const uptr = [] // (n + 1)
// U
const U = new SparseMatrix({
values: uvalues,
index: uindex,
ptr: uptr,
size: [n, n]
})
// inverse of permutation vector
const pinv = [] // (n)
// vars
let i, p
// allocate arrays
const x = [] // (n)
const xi = [] // (2 * n)
// initialize variables
for (i = 0; i < n; i++) {
// clear workspace
x[i] = 0
// no rows pivotal yet
pinv[i] = -1
// no cols of L yet
lptr[i + 1] = 0
}
// reset number of nonzero elements in L and U
lnz = 0
unz = 0
// compute L(:,k) and U(:,k)
for (let k = 0; k < n; k++) {
// update ptr
lptr[k] = lnz
uptr[k] = unz
// apply column permutations if needed
const col = q ? q[k] : k
// solve triangular system, x = L\A(:,col)
const top = csSpsolve(L, m, col, xi, x, pinv, 1)
// find pivot
let ipiv = -1
let a = -1
// loop xi[] from top -> n
for (p = top; p < n; p++) {
// x[i] is nonzero
i = xi[p]
// check row i is not yet pivotal
if (pinv[i] < 0) {
// absolute value of x[i]
const xabs = abs(x[i])
// check absoulte value is greater than pivot value
if (larger(xabs, a)) {
// largest pivot candidate so far
a = xabs
ipiv = i
}
} else {
// x(i) is the entry U(pinv[i],k)
uindex[unz] = pinv[i]
uvalues[unz++] = x[i]
}
}
// validate we found a valid pivot
if (ipiv === -1 || a <= 0) { return null }
// update actual pivot column, give preference to diagonal value
if (pinv[col] < 0 && largerEq(abs(x[col]), multiply(a, tol))) { ipiv = col }
// the chosen pivot
const pivot = x[ipiv]
// last entry in U(:,k) is U(k,k)
uindex[unz] = k
uvalues[unz++] = pivot
// ipiv is the kth pivot row
pinv[ipiv] = k
// first entry in L(:,k) is L(k,k) = 1
lindex[lnz] = ipiv
lvalues[lnz++] = 1
// L(k+1:n,k) = x / pivot
for (p = top; p < n; p++) {
// row
i = xi[p]
// check x(i) is an entry in L(:,k)
if (pinv[i] < 0) {
// save unpermuted row in L
lindex[lnz] = i
// scale pivot column
lvalues[lnz++] = divideScalar(x[i], pivot)
}
// x[0..n-1] = 0 for next k
x[i] = 0
}
}
// update ptr
lptr[n] = lnz
uptr[n] = unz
// fix row indices of L for final pinv
for (p = 0; p < lnz; p++) { lindex[p] = pinv[lindex[p]] }
// trim arrays
lvalues.splice(lnz, lvalues.length - lnz)
lindex.splice(lnz, lindex.length - lnz)
uvalues.splice(unz, uvalues.length - unz)
uindex.splice(unz, uindex.length - unz)
// return LU factor
return {
L: L,
U: U,
pinv: pinv
}
}
})