UNPKG

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

384 lines (370 loc) 10.7 kB
'use strict' const util = require('../../../utils/index') const object = util.object function factory (type, config, load, typed) { const matrix = load(require('../../../type/matrix/function/matrix')) const abs = load(require('../../arithmetic/abs')) const addScalar = load(require('../../arithmetic/addScalar')) const divideScalar = load(require('../../arithmetic/divideScalar')) const multiplyScalar = load(require('../../arithmetic/multiplyScalar')) const subtract = load(require('../../arithmetic/subtract')) const larger = load(require('../../relational/larger')) const equalScalar = load(require('../../relational/equalScalar')) const unaryMinus = load(require('../../arithmetic/unaryMinus')) const SparseMatrix = type.SparseMatrix const DenseMatrix = type.DenseMatrix const Spa = type.Spa /** * Calculate the Matrix LU decomposition with partial pivoting. Matrix `A` is decomposed in two matrices (`L`, `U`) and a * row permutation vector `p` where `A[p,:] = L * U` * * Syntax: * * math.lup(A) * * Example: * * const m = [[2, 1], [1, 4]] * const r = math.lup(m) * // r = { * // L: [[1, 0], [0.5, 1]], * // U: [[2, 1], [0, 3.5]], * // P: [0, 1] * // } * * See also: * * slu, lsolve, lusolve, usolve * * @param {Matrix | Array} A A two dimensional matrix or array for which to get the LUP decomposition. * * @return {{L: Array | Matrix, U: Array | Matrix, P: Array.<number>}} The lower triangular matrix, the upper triangular matrix and the permutation matrix. */ const lup = typed('lup', { 'DenseMatrix': function (m) { return _denseLUP(m) }, 'SparseMatrix': function (m) { return _sparseLUP(m) }, 'Array': function (a) { // create dense matrix from array const m = matrix(a) // lup, use matrix implementation const r = _denseLUP(m) // result return { L: r.L.valueOf(), U: r.U.valueOf(), p: r.p } } }) function _denseLUP (m) { // rows & columns const rows = m._size[0] const columns = m._size[1] // minimum rows and columns let n = Math.min(rows, columns) // matrix array, clone original data const data = object.clone(m._data) // l matrix arrays const ldata = [] const lsize = [rows, n] // u matrix arrays const udata = [] const usize = [n, columns] // vars let i, j, k // permutation vector const p = [] for (i = 0; i < rows; i++) { p[i] = i } // loop columns for (j = 0; j < columns; j++) { // skip first column in upper triangular matrix if (j > 0) { // loop rows for (i = 0; i < rows; i++) { // min i,j const min = Math.min(i, j) // v[i, j] let s = 0 // loop up to min for (k = 0; k < min; k++) { // s = l[i, k] - data[k, j] s = addScalar(s, multiplyScalar(data[i][k], data[k][j])) } data[i][j] = subtract(data[i][j], s) } } // row with larger value in cvector, row >= j let pi = j let pabsv = 0 let vjj = 0 // loop rows for (i = j; i < rows; i++) { // data @ i, j const v = data[i][j] // absolute value const absv = abs(v) // value is greater than pivote value if (larger(absv, pabsv)) { // store row pi = i // update max value pabsv = absv // value @ [j, j] vjj = v } } // swap rows (j <-> pi) if (j !== pi) { // swap values j <-> pi in p p[j] = [p[pi], p[pi] = p[j]][0] // swap j <-> pi in data DenseMatrix._swapRows(j, pi, data) } // check column is in lower triangular matrix if (j < rows) { // loop rows (lower triangular matrix) for (i = j + 1; i < rows; i++) { // value @ i, j const vij = data[i][j] if (!equalScalar(vij, 0)) { // update data data[i][j] = divideScalar(data[i][j], vjj) } } } } // loop columns for (j = 0; j < columns; j++) { // loop rows for (i = 0; i < rows; i++) { // initialize row in arrays if (j === 0) { // check row exists in upper triangular matrix if (i < columns) { // U udata[i] = [] } // L ldata[i] = [] } // check we are in the upper triangular matrix if (i < j) { // check row exists in upper triangular matrix if (i < columns) { // U udata[i][j] = data[i][j] } // check column exists in lower triangular matrix if (j < rows) { // L ldata[i][j] = 0 } continue } // diagonal value if (i === j) { // check row exists in upper triangular matrix if (i < columns) { // U udata[i][j] = data[i][j] } // check column exists in lower triangular matrix if (j < rows) { // L ldata[i][j] = 1 } continue } // check row exists in upper triangular matrix if (i < columns) { // U udata[i][j] = 0 } // check column exists in lower triangular matrix if (j < rows) { // L ldata[i][j] = data[i][j] } } } // l matrix const l = new DenseMatrix({ data: ldata, size: lsize }) // u matrix const u = new DenseMatrix({ data: udata, size: usize }) // p vector const pv = [] for (i = 0, n = p.length; i < n; i++) { pv[p[i]] = i } // return matrices return { L: l, U: u, p: pv, toString: function () { return 'L: ' + this.L.toString() + '\nU: ' + this.U.toString() + '\nP: ' + this.p } } } function _sparseLUP (m) { // rows & columns const rows = m._size[0] const columns = m._size[1] // minimum rows and columns const n = Math.min(rows, columns) // matrix arrays (will not be modified, thanks to permutation vector) const values = m._values const index = m._index const ptr = m._ptr // l matrix arrays const lvalues = [] const lindex = [] const lptr = [] const lsize = [rows, n] // u matrix arrays const uvalues = [] const uindex = [] const uptr = [] const usize = [n, columns] // vars let i, j, k // permutation vectors, (current index -> original index) and (original index -> current index) const pvCo = [] const pvOc = [] for (i = 0; i < rows; i++) { pvCo[i] = i pvOc[i] = i } // swap indices in permutation vectors (condition x < y)! const swapIndeces = function (x, y) { // find pv indeces getting data from x and y const kx = pvOc[x] const ky = pvOc[y] // update permutation vector current -> original pvCo[kx] = y pvCo[ky] = x // update permutation vector original -> current pvOc[x] = ky pvOc[y] = kx } // loop columns for (j = 0; j < columns; j++) { // sparse accumulator const spa = new Spa() // check lower triangular matrix has a value @ column j if (j < rows) { // update ptr lptr.push(lvalues.length) // first value in j column for lower triangular matrix lvalues.push(1) lindex.push(j) } // update ptr uptr.push(uvalues.length) // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1] const k0 = ptr[j] const k1 = ptr[j + 1] // copy column j into sparse accumulator for (k = k0; k < k1; k++) { // row i = index[k] // copy column values into sparse accumulator (use permutation vector) spa.set(pvCo[i], values[k]) } // skip first column in upper triangular matrix if (j > 0) { // loop rows in column j (above diagonal) spa.forEach(0, j - 1, function (k, vkj) { // loop rows in column k (L) SparseMatrix._forEachRow(k, lvalues, lindex, lptr, function (i, vik) { // check row is below k if (i > k) { // update spa value spa.accumulate(i, unaryMinus(multiplyScalar(vik, vkj))) } }) }) } // row with larger value in spa, row >= j let pi = j let vjj = spa.get(j) let pabsv = abs(vjj) // loop values in spa (order by row, below diagonal) spa.forEach(j + 1, rows - 1, function (x, v) { // absolute value const absv = abs(v) // value is greater than pivote value if (larger(absv, pabsv)) { // store row pi = x // update max value pabsv = absv // value @ [j, j] vjj = v } }) // swap rows (j <-> pi) if (j !== pi) { // swap values j <-> pi in L SparseMatrix._swapRows(j, pi, lsize[1], lvalues, lindex, lptr) // swap values j <-> pi in U SparseMatrix._swapRows(j, pi, usize[1], uvalues, uindex, uptr) // swap values in spa spa.swap(j, pi) // update permutation vector (swap values @ j, pi) swapIndeces(j, pi) } // loop values in spa (order by row) spa.forEach(0, rows - 1, function (x, v) { // check we are above diagonal if (x <= j) { // update upper triangular matrix uvalues.push(v) uindex.push(x) } else { // update value v = divideScalar(v, vjj) // check value is non zero if (!equalScalar(v, 0)) { // update lower triangular matrix lvalues.push(v) lindex.push(x) } } }) } // update ptrs uptr.push(uvalues.length) lptr.push(lvalues.length) // return matrices return { L: new SparseMatrix({ values: lvalues, index: lindex, ptr: lptr, size: lsize }), U: new SparseMatrix({ values: uvalues, index: uindex, ptr: uptr, size: usize }), p: pvCo, toString: function () { return 'L: ' + this.L.toString() + '\nU: ' + this.U.toString() + '\nP: ' + this.p } } } return lup } exports.name = 'lup' exports.factory = factory