qr.js (6422B)
1 import _extends from "@babel/runtime/helpers/extends"; 2 import { factory } from '../../../utils/factory.js'; 3 var name = 'qr'; 4 var dependencies = ['typed', 'matrix', 'zeros', 'identity', 'isZero', 'equal', 'sign', 'sqrt', 'conj', 'unaryMinus', 'addScalar', 'divideScalar', 'multiplyScalar', 'subtract', 'complex']; 5 export var createQr = /* #__PURE__ */factory(name, dependencies, _ref => { 6 var { 7 typed, 8 matrix, 9 zeros, 10 identity, 11 isZero, 12 equal, 13 sign, 14 sqrt, 15 conj, 16 unaryMinus, 17 addScalar, 18 divideScalar, 19 multiplyScalar, 20 subtract, 21 complex 22 } = _ref; 23 24 /** 25 * Calculate the Matrix QR decomposition. Matrix `A` is decomposed in 26 * two matrices (`Q`, `R`) where `Q` is an 27 * orthogonal matrix and `R` is an upper triangular matrix. 28 * 29 * Syntax: 30 * 31 * math.qr(A) 32 * 33 * Example: 34 * 35 * const m = [ 36 * [1, -1, 4], 37 * [1, 4, -2], 38 * [1, 4, 2], 39 * [1, -1, 0] 40 * ] 41 * const result = math.qr(m) 42 * // r = { 43 * // Q: [ 44 * // [0.5, -0.5, 0.5], 45 * // [0.5, 0.5, -0.5], 46 * // [0.5, 0.5, 0.5], 47 * // [0.5, -0.5, -0.5], 48 * // ], 49 * // R: [ 50 * // [2, 3, 2], 51 * // [0, 5, -2], 52 * // [0, 0, 4], 53 * // [0, 0, 0] 54 * // ] 55 * // } 56 * 57 * See also: 58 * 59 * lup, lusolve 60 * 61 * @param {Matrix | Array} A A two dimensional matrix or array 62 * for which to get the QR decomposition. 63 * 64 * @return {{Q: Array | Matrix, R: Array | Matrix}} Q: the orthogonal 65 * matrix and R: the upper triangular matrix 66 */ 67 return _extends(typed(name, { 68 DenseMatrix: function DenseMatrix(m) { 69 return _denseQR(m); 70 }, 71 SparseMatrix: function SparseMatrix(m) { 72 return _sparseQR(m); 73 }, 74 Array: function Array(a) { 75 // create dense matrix from array 76 var m = matrix(a); // lup, use matrix implementation 77 78 var r = _denseQR(m); // result 79 80 81 return { 82 Q: r.Q.valueOf(), 83 R: r.R.valueOf() 84 }; 85 } 86 }), { 87 _denseQRimpl 88 }); 89 90 function _denseQRimpl(m) { 91 // rows & columns (m x n) 92 var rows = m._size[0]; // m 93 94 var cols = m._size[1]; // n 95 96 var Q = identity([rows], 'dense'); 97 var Qdata = Q._data; 98 var R = m.clone(); 99 var Rdata = R._data; // vars 100 101 var i, j, k; 102 var w = zeros([rows], ''); 103 104 for (k = 0; k < Math.min(cols, rows); ++k) { 105 /* 106 * **k-th Household matrix** 107 * 108 * The matrix I - 2*v*transpose(v) 109 * x = first column of A 110 * x1 = first element of x 111 * alpha = x1 / |x1| * |x| 112 * e1 = tranpose([1, 0, 0, ...]) 113 * u = x - alpha * e1 114 * v = u / |u| 115 * 116 * Household matrix = I - 2 * v * tranpose(v) 117 * 118 * * Initially Q = I and R = A. 119 * * Household matrix is a reflection in a plane normal to v which 120 * will zero out all but the top right element in R. 121 * * Appplying reflection to both Q and R will not change product. 122 * * Repeat this process on the (1,1) minor to get R as an upper 123 * triangular matrix. 124 * * Reflections leave the magnitude of the columns of Q unchanged 125 * so Q remains othoganal. 126 * 127 */ 128 var pivot = Rdata[k][k]; 129 var sgn = unaryMinus(equal(pivot, 0) ? 1 : sign(pivot)); 130 var conjSgn = conj(sgn); 131 var alphaSquared = 0; 132 133 for (i = k; i < rows; i++) { 134 alphaSquared = addScalar(alphaSquared, multiplyScalar(Rdata[i][k], conj(Rdata[i][k]))); 135 } 136 137 var alpha = multiplyScalar(sgn, sqrt(alphaSquared)); 138 139 if (!isZero(alpha)) { 140 // first element in vector u 141 var u1 = subtract(pivot, alpha); // w = v * u1 / |u| (only elements k to (rows-1) are used) 142 143 w[k] = 1; 144 145 for (i = k + 1; i < rows; i++) { 146 w[i] = divideScalar(Rdata[i][k], u1); 147 } // tau = - conj(u1 / alpha) 148 149 150 var tau = unaryMinus(conj(divideScalar(u1, alpha))); 151 var s = void 0; 152 /* 153 * tau and w have been choosen so that 154 * 155 * 2 * v * tranpose(v) = tau * w * tranpose(w) 156 */ 157 158 /* 159 * -- calculate R = R - tau * w * tranpose(w) * R -- 160 * Only do calculation with rows k to (rows-1) 161 * Additionally columns 0 to (k-1) will not be changed by this 162 * multiplication so do not bother recalculating them 163 */ 164 165 for (j = k; j < cols; j++) { 166 s = 0.0; // calculate jth element of [tranpose(w) * R] 167 168 for (i = k; i < rows; i++) { 169 s = addScalar(s, multiplyScalar(conj(w[i]), Rdata[i][j])); 170 } // calculate the jth element of [tau * transpose(w) * R] 171 172 173 s = multiplyScalar(s, tau); 174 175 for (i = k; i < rows; i++) { 176 Rdata[i][j] = multiplyScalar(subtract(Rdata[i][j], multiplyScalar(w[i], s)), conjSgn); 177 } 178 } 179 /* 180 * -- calculate Q = Q - tau * Q * w * transpose(w) -- 181 * Q is a square matrix (rows x rows) 182 * Only do calculation with columns k to (rows-1) 183 * Additionally rows 0 to (k-1) will not be changed by this 184 * multiplication so do not bother recalculating them 185 */ 186 187 188 for (i = 0; i < rows; i++) { 189 s = 0.0; // calculate ith element of [Q * w] 190 191 for (j = k; j < rows; j++) { 192 s = addScalar(s, multiplyScalar(Qdata[i][j], w[j])); 193 } // calculate the ith element of [tau * Q * w] 194 195 196 s = multiplyScalar(s, tau); 197 198 for (j = k; j < rows; ++j) { 199 Qdata[i][j] = divideScalar(subtract(Qdata[i][j], multiplyScalar(s, conj(w[j]))), conjSgn); 200 } 201 } 202 } 203 } // return matrices 204 205 206 return { 207 Q: Q, 208 R: R, 209 toString: function toString() { 210 return 'Q: ' + this.Q.toString() + '\nR: ' + this.R.toString(); 211 } 212 }; 213 } 214 215 function _denseQR(m) { 216 var ret = _denseQRimpl(m); 217 218 var Rdata = ret.R._data; 219 220 if (m._data.length > 0) { 221 var zero = Rdata[0][0].type === 'Complex' ? complex(0) : 0; 222 223 for (var i = 0; i < Rdata.length; ++i) { 224 for (var j = 0; j < i && j < (Rdata[0] || []).length; ++j) { 225 Rdata[i][j] = zero; 226 } 227 } 228 } 229 230 return ret; 231 } 232 233 function _sparseQR(m) { 234 throw new Error('qr not implemented for sparse matrices yet'); 235 } 236 });