csLu.js 4.9 KB

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