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