simple-squiggle

A restricted subset of Squiggle
Log | Files | Refs | README

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;