lup.js 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379
  1. import { clone } from '../../../utils/object.js';
  2. import { factory } from '../../../utils/factory.js';
  3. var name = 'lup';
  4. var dependencies = ['typed', 'matrix', 'abs', 'addScalar', 'divideScalar', 'multiplyScalar', 'subtract', 'larger', 'equalScalar', 'unaryMinus', 'DenseMatrix', 'SparseMatrix', 'Spa'];
  5. export var createLup = /* #__PURE__ */factory(name, dependencies, _ref => {
  6. var {
  7. typed,
  8. matrix,
  9. abs,
  10. addScalar,
  11. divideScalar,
  12. multiplyScalar,
  13. subtract,
  14. larger,
  15. equalScalar,
  16. unaryMinus,
  17. DenseMatrix,
  18. SparseMatrix,
  19. Spa
  20. } = _ref;
  21. /**
  22. * Calculate the Matrix LU decomposition with partial pivoting. Matrix `A` is decomposed in two matrices (`L`, `U`) and a
  23. * row permutation vector `p` where `A[p,:] = L * U`
  24. *
  25. * Syntax:
  26. *
  27. * math.lup(A)
  28. *
  29. * Example:
  30. *
  31. * const m = [[2, 1], [1, 4]]
  32. * const r = math.lup(m)
  33. * // r = {
  34. * // L: [[1, 0], [0.5, 1]],
  35. * // U: [[2, 1], [0, 3.5]],
  36. * // P: [0, 1]
  37. * // }
  38. *
  39. * See also:
  40. *
  41. * slu, lsolve, lusolve, usolve
  42. *
  43. * @param {Matrix | Array} A A two dimensional matrix or array for which to get the LUP decomposition.
  44. *
  45. * @return {{L: Array | Matrix, U: Array | Matrix, P: Array.<number>}} The lower triangular matrix, the upper triangular matrix and the permutation matrix.
  46. */
  47. return typed(name, {
  48. DenseMatrix: function DenseMatrix(m) {
  49. return _denseLUP(m);
  50. },
  51. SparseMatrix: function SparseMatrix(m) {
  52. return _sparseLUP(m);
  53. },
  54. Array: function Array(a) {
  55. // create dense matrix from array
  56. var m = matrix(a);
  57. // lup, use matrix implementation
  58. var r = _denseLUP(m);
  59. // result
  60. return {
  61. L: r.L.valueOf(),
  62. U: r.U.valueOf(),
  63. p: r.p
  64. };
  65. }
  66. });
  67. function _denseLUP(m) {
  68. // rows & columns
  69. var rows = m._size[0];
  70. var columns = m._size[1];
  71. // minimum rows and columns
  72. var n = Math.min(rows, columns);
  73. // matrix array, clone original data
  74. var data = clone(m._data);
  75. // l matrix arrays
  76. var ldata = [];
  77. var lsize = [rows, n];
  78. // u matrix arrays
  79. var udata = [];
  80. var usize = [n, columns];
  81. // vars
  82. var i, j, k;
  83. // permutation vector
  84. var p = [];
  85. for (i = 0; i < rows; i++) {
  86. p[i] = i;
  87. }
  88. // loop columns
  89. for (j = 0; j < columns; j++) {
  90. // skip first column in upper triangular matrix
  91. if (j > 0) {
  92. // loop rows
  93. for (i = 0; i < rows; i++) {
  94. // min i,j
  95. var min = Math.min(i, j);
  96. // v[i, j]
  97. var s = 0;
  98. // loop up to min
  99. for (k = 0; k < min; k++) {
  100. // s = l[i, k] - data[k, j]
  101. s = addScalar(s, multiplyScalar(data[i][k], data[k][j]));
  102. }
  103. data[i][j] = subtract(data[i][j], s);
  104. }
  105. }
  106. // row with larger value in cvector, row >= j
  107. var pi = j;
  108. var pabsv = 0;
  109. var vjj = 0;
  110. // loop rows
  111. for (i = j; i < rows; i++) {
  112. // data @ i, j
  113. var v = data[i][j];
  114. // absolute value
  115. var absv = abs(v);
  116. // value is greater than pivote value
  117. if (larger(absv, pabsv)) {
  118. // store row
  119. pi = i;
  120. // update max value
  121. pabsv = absv;
  122. // value @ [j, j]
  123. vjj = v;
  124. }
  125. }
  126. // swap rows (j <-> pi)
  127. if (j !== pi) {
  128. // swap values j <-> pi in p
  129. p[j] = [p[pi], p[pi] = p[j]][0];
  130. // swap j <-> pi in data
  131. DenseMatrix._swapRows(j, pi, data);
  132. }
  133. // check column is in lower triangular matrix
  134. if (j < rows) {
  135. // loop rows (lower triangular matrix)
  136. for (i = j + 1; i < rows; i++) {
  137. // value @ i, j
  138. var vij = data[i][j];
  139. if (!equalScalar(vij, 0)) {
  140. // update data
  141. data[i][j] = divideScalar(data[i][j], vjj);
  142. }
  143. }
  144. }
  145. }
  146. // loop columns
  147. for (j = 0; j < columns; j++) {
  148. // loop rows
  149. for (i = 0; i < rows; i++) {
  150. // initialize row in arrays
  151. if (j === 0) {
  152. // check row exists in upper triangular matrix
  153. if (i < columns) {
  154. // U
  155. udata[i] = [];
  156. }
  157. // L
  158. ldata[i] = [];
  159. }
  160. // check we are in the upper triangular matrix
  161. if (i < j) {
  162. // check row exists in upper triangular matrix
  163. if (i < columns) {
  164. // U
  165. udata[i][j] = data[i][j];
  166. }
  167. // check column exists in lower triangular matrix
  168. if (j < rows) {
  169. // L
  170. ldata[i][j] = 0;
  171. }
  172. continue;
  173. }
  174. // diagonal value
  175. if (i === j) {
  176. // check row exists in upper triangular matrix
  177. if (i < columns) {
  178. // U
  179. udata[i][j] = data[i][j];
  180. }
  181. // check column exists in lower triangular matrix
  182. if (j < rows) {
  183. // L
  184. ldata[i][j] = 1;
  185. }
  186. continue;
  187. }
  188. // check row exists in upper triangular matrix
  189. if (i < columns) {
  190. // U
  191. udata[i][j] = 0;
  192. }
  193. // check column exists in lower triangular matrix
  194. if (j < rows) {
  195. // L
  196. ldata[i][j] = data[i][j];
  197. }
  198. }
  199. }
  200. // l matrix
  201. var l = new DenseMatrix({
  202. data: ldata,
  203. size: lsize
  204. });
  205. // u matrix
  206. var u = new DenseMatrix({
  207. data: udata,
  208. size: usize
  209. });
  210. // p vector
  211. var pv = [];
  212. for (i = 0, n = p.length; i < n; i++) {
  213. pv[p[i]] = i;
  214. }
  215. // return matrices
  216. return {
  217. L: l,
  218. U: u,
  219. p: pv,
  220. toString: function toString() {
  221. return 'L: ' + this.L.toString() + '\nU: ' + this.U.toString() + '\nP: ' + this.p;
  222. }
  223. };
  224. }
  225. function _sparseLUP(m) {
  226. // rows & columns
  227. var rows = m._size[0];
  228. var columns = m._size[1];
  229. // minimum rows and columns
  230. var n = Math.min(rows, columns);
  231. // matrix arrays (will not be modified, thanks to permutation vector)
  232. var values = m._values;
  233. var index = m._index;
  234. var ptr = m._ptr;
  235. // l matrix arrays
  236. var lvalues = [];
  237. var lindex = [];
  238. var lptr = [];
  239. var lsize = [rows, n];
  240. // u matrix arrays
  241. var uvalues = [];
  242. var uindex = [];
  243. var uptr = [];
  244. var usize = [n, columns];
  245. // vars
  246. var i, j, k;
  247. // permutation vectors, (current index -> original index) and (original index -> current index)
  248. var pvCo = [];
  249. var pvOc = [];
  250. for (i = 0; i < rows; i++) {
  251. pvCo[i] = i;
  252. pvOc[i] = i;
  253. }
  254. // swap indices in permutation vectors (condition x < y)!
  255. var swapIndeces = function swapIndeces(x, y) {
  256. // find pv indeces getting data from x and y
  257. var kx = pvOc[x];
  258. var ky = pvOc[y];
  259. // update permutation vector current -> original
  260. pvCo[kx] = y;
  261. pvCo[ky] = x;
  262. // update permutation vector original -> current
  263. pvOc[x] = ky;
  264. pvOc[y] = kx;
  265. };
  266. // loop columns
  267. var _loop = function _loop() {
  268. // sparse accumulator
  269. var spa = new Spa();
  270. // check lower triangular matrix has a value @ column j
  271. if (j < rows) {
  272. // update ptr
  273. lptr.push(lvalues.length);
  274. // first value in j column for lower triangular matrix
  275. lvalues.push(1);
  276. lindex.push(j);
  277. }
  278. // update ptr
  279. uptr.push(uvalues.length);
  280. // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1]
  281. var k0 = ptr[j];
  282. var k1 = ptr[j + 1];
  283. // copy column j into sparse accumulator
  284. for (k = k0; k < k1; k++) {
  285. // row
  286. i = index[k];
  287. // copy column values into sparse accumulator (use permutation vector)
  288. spa.set(pvCo[i], values[k]);
  289. }
  290. // skip first column in upper triangular matrix
  291. if (j > 0) {
  292. // loop rows in column j (above diagonal)
  293. spa.forEach(0, j - 1, function (k, vkj) {
  294. // loop rows in column k (L)
  295. SparseMatrix._forEachRow(k, lvalues, lindex, lptr, function (i, vik) {
  296. // check row is below k
  297. if (i > k) {
  298. // update spa value
  299. spa.accumulate(i, unaryMinus(multiplyScalar(vik, vkj)));
  300. }
  301. });
  302. });
  303. }
  304. // row with larger value in spa, row >= j
  305. var pi = j;
  306. var vjj = spa.get(j);
  307. var pabsv = abs(vjj);
  308. // loop values in spa (order by row, below diagonal)
  309. spa.forEach(j + 1, rows - 1, function (x, v) {
  310. // absolute value
  311. var absv = abs(v);
  312. // value is greater than pivote value
  313. if (larger(absv, pabsv)) {
  314. // store row
  315. pi = x;
  316. // update max value
  317. pabsv = absv;
  318. // value @ [j, j]
  319. vjj = v;
  320. }
  321. });
  322. // swap rows (j <-> pi)
  323. if (j !== pi) {
  324. // swap values j <-> pi in L
  325. SparseMatrix._swapRows(j, pi, lsize[1], lvalues, lindex, lptr);
  326. // swap values j <-> pi in U
  327. SparseMatrix._swapRows(j, pi, usize[1], uvalues, uindex, uptr);
  328. // swap values in spa
  329. spa.swap(j, pi);
  330. // update permutation vector (swap values @ j, pi)
  331. swapIndeces(j, pi);
  332. }
  333. // loop values in spa (order by row)
  334. spa.forEach(0, rows - 1, function (x, v) {
  335. // check we are above diagonal
  336. if (x <= j) {
  337. // update upper triangular matrix
  338. uvalues.push(v);
  339. uindex.push(x);
  340. } else {
  341. // update value
  342. v = divideScalar(v, vjj);
  343. // check value is non zero
  344. if (!equalScalar(v, 0)) {
  345. // update lower triangular matrix
  346. lvalues.push(v);
  347. lindex.push(x);
  348. }
  349. }
  350. });
  351. };
  352. for (j = 0; j < columns; j++) {
  353. _loop();
  354. }
  355. // update ptrs
  356. uptr.push(uvalues.length);
  357. lptr.push(lvalues.length);
  358. // return matrices
  359. return {
  360. L: new SparseMatrix({
  361. values: lvalues,
  362. index: lindex,
  363. ptr: lptr,
  364. size: lsize
  365. }),
  366. U: new SparseMatrix({
  367. values: uvalues,
  368. index: uindex,
  369. ptr: uptr,
  370. size: usize
  371. }),
  372. p: pvCo,
  373. toString: function toString() {
  374. return 'L: ' + this.L.toString() + '\nU: ' + this.U.toString() + '\nP: ' + this.p;
  375. }
  376. };
  377. }
  378. });