simple-squiggle

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

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 });