simple-squiggle

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

norm.js (7488B)


      1 import { factory } from '../../utils/factory.js';
      2 var name = 'norm';
      3 var dependencies = ['typed', 'abs', 'add', 'pow', 'conj', 'sqrt', 'multiply', 'equalScalar', 'larger', 'smaller', 'matrix', 'ctranspose', 'eigs'];
      4 export var createNorm = /* #__PURE__ */factory(name, dependencies, _ref => {
      5   var {
      6     typed,
      7     abs,
      8     add,
      9     pow,
     10     conj,
     11     sqrt,
     12     multiply,
     13     equalScalar,
     14     larger,
     15     smaller,
     16     matrix,
     17     ctranspose,
     18     eigs
     19   } = _ref;
     20 
     21   /**
     22    * Calculate the norm of a number, vector or matrix.
     23    *
     24    * The second parameter p is optional. If not provided, it defaults to 2.
     25    *
     26    * Syntax:
     27    *
     28    *    math.norm(x)
     29    *    math.norm(x, p)
     30    *
     31    * Examples:
     32    *
     33    *    math.abs(-3.5)                         // returns 3.5
     34    *    math.norm(-3.5)                        // returns 3.5
     35    *
     36    *    math.norm(math.complex(3, -4))         // returns 5
     37    *
     38    *    math.norm([1, 2, -3], Infinity)        // returns 3
     39    *    math.norm([1, 2, -3], -Infinity)       // returns 1
     40    *
     41    *    math.norm([3, 4], 2)                   // returns 5
     42    *
     43    *    math.norm([[1, 2], [3, 4]], 1)          // returns 6
     44    *    math.norm([[1, 2], [3, 4]], 'inf')     // returns 7
     45    *    math.norm([[1, 2], [3, 4]], 'fro')     // returns 5.477225575051661
     46    *
     47    * See also:
     48    *
     49    *    abs, hypot
     50    *
     51    * @param  {number | BigNumber | Complex | Array | Matrix} x
     52    *            Value for which to calculate the norm
     53    * @param  {number | BigNumber | string} [p=2]
     54    *            Vector space.
     55    *            Supported numbers include Infinity and -Infinity.
     56    *            Supported strings are: 'inf', '-inf', and 'fro' (The Frobenius norm)
     57    * @return {number | BigNumber} the p-norm
     58    */
     59   return typed(name, {
     60     number: Math.abs,
     61     Complex: function Complex(x) {
     62       return x.abs();
     63     },
     64     BigNumber: function BigNumber(x) {
     65       // norm(x) = abs(x)
     66       return x.abs();
     67     },
     68     boolean: function boolean(x) {
     69       // norm(x) = abs(x)
     70       return Math.abs(x);
     71     },
     72     Array: function Array(x) {
     73       return _norm(matrix(x), 2);
     74     },
     75     Matrix: function Matrix(x) {
     76       return _norm(x, 2);
     77     },
     78     'number | Complex | BigNumber | boolean, number | BigNumber | string': function numberComplexBigNumberBooleanNumberBigNumberString(x) {
     79       // ignore second parameter, TODO: remove the option of second parameter for these types
     80       return this(x);
     81     },
     82     'Array, number | BigNumber | string': function ArrayNumberBigNumberString(x, p) {
     83       return _norm(matrix(x), p);
     84     },
     85     'Matrix, number | BigNumber | string': function MatrixNumberBigNumberString(x, p) {
     86       return _norm(x, p);
     87     }
     88   });
     89   /**
     90    * Calculate the plus infinity norm for a vector
     91    * @param {Matrix} x
     92    * @returns {number} Returns the norm
     93    * @private
     94    */
     95 
     96   function _vectorNormPlusInfinity(x) {
     97     // norm(x, Infinity) = max(abs(x))
     98     var pinf = 0; // skip zeros since abs(0) === 0
     99 
    100     x.forEach(function (value) {
    101       var v = abs(value);
    102 
    103       if (larger(v, pinf)) {
    104         pinf = v;
    105       }
    106     }, true);
    107     return pinf;
    108   }
    109   /**
    110    * Calculate the minus infinity norm for a vector
    111    * @param {Matrix} x
    112    * @returns {number} Returns the norm
    113    * @private
    114    */
    115 
    116 
    117   function _vectorNormMinusInfinity(x) {
    118     // norm(x, -Infinity) = min(abs(x))
    119     var ninf; // skip zeros since abs(0) === 0
    120 
    121     x.forEach(function (value) {
    122       var v = abs(value);
    123 
    124       if (!ninf || smaller(v, ninf)) {
    125         ninf = v;
    126       }
    127     }, true);
    128     return ninf || 0;
    129   }
    130   /**
    131    * Calculate the norm for a vector
    132    * @param {Matrix} x
    133    * @param {number | string} p
    134    * @returns {number} Returns the norm
    135    * @private
    136    */
    137 
    138 
    139   function _vectorNorm(x, p) {
    140     // check p
    141     if (p === Number.POSITIVE_INFINITY || p === 'inf') {
    142       return _vectorNormPlusInfinity(x);
    143     }
    144 
    145     if (p === Number.NEGATIVE_INFINITY || p === '-inf') {
    146       return _vectorNormMinusInfinity(x);
    147     }
    148 
    149     if (p === 'fro') {
    150       return _norm(x, 2);
    151     }
    152 
    153     if (typeof p === 'number' && !isNaN(p)) {
    154       // check p != 0
    155       if (!equalScalar(p, 0)) {
    156         // norm(x, p) = sum(abs(xi) ^ p) ^ 1/p
    157         var n = 0; // skip zeros since abs(0) === 0
    158 
    159         x.forEach(function (value) {
    160           n = add(pow(abs(value), p), n);
    161         }, true);
    162         return pow(n, 1 / p);
    163       }
    164 
    165       return Number.POSITIVE_INFINITY;
    166     } // invalid parameter value
    167 
    168 
    169     throw new Error('Unsupported parameter value');
    170   }
    171   /**
    172    * Calculate the Frobenius norm for a matrix
    173    * @param {Matrix} x
    174    * @returns {number} Returns the norm
    175    * @private
    176    */
    177 
    178 
    179   function _matrixNormFrobenius(x) {
    180     // norm(x) = sqrt(sum(diag(x'x)))
    181     var fro = 0;
    182     x.forEach(function (value, index) {
    183       fro = add(fro, multiply(value, conj(value)));
    184     });
    185     return abs(sqrt(fro));
    186   }
    187   /**
    188    * Calculate the norm L1 for a matrix
    189    * @param {Matrix} x
    190    * @returns {number} Returns the norm
    191    * @private
    192    */
    193 
    194 
    195   function _matrixNormOne(x) {
    196     // norm(x) = the largest column sum
    197     var c = []; // result
    198 
    199     var maxc = 0; // skip zeros since abs(0) == 0
    200 
    201     x.forEach(function (value, index) {
    202       var j = index[1];
    203       var cj = add(c[j] || 0, abs(value));
    204 
    205       if (larger(cj, maxc)) {
    206         maxc = cj;
    207       }
    208 
    209       c[j] = cj;
    210     }, true);
    211     return maxc;
    212   }
    213   /**
    214    * Calculate the norm L2 for a matrix
    215    * @param {Matrix} x
    216    * @returns {number} Returns the norm
    217    * @private
    218    */
    219 
    220 
    221   function _matrixNormTwo(x) {
    222     // norm(x) = sqrt( max eigenvalue of A*.A)
    223     var sizeX = x.size();
    224 
    225     if (sizeX[0] !== sizeX[1]) {
    226       throw new RangeError('Invalid matrix dimensions');
    227     }
    228 
    229     var tx = ctranspose(x);
    230     var squaredX = multiply(tx, x);
    231     var eigenVals = eigs(squaredX).values.toArray();
    232     var rho = eigenVals[eigenVals.length - 1];
    233     return abs(sqrt(rho));
    234   }
    235   /**
    236    * Calculate the infinity norm for a matrix
    237    * @param {Matrix} x
    238    * @returns {number} Returns the norm
    239    * @private
    240    */
    241 
    242 
    243   function _matrixNormInfinity(x) {
    244     // norm(x) = the largest row sum
    245     var r = []; // result
    246 
    247     var maxr = 0; // skip zeros since abs(0) == 0
    248 
    249     x.forEach(function (value, index) {
    250       var i = index[0];
    251       var ri = add(r[i] || 0, abs(value));
    252 
    253       if (larger(ri, maxr)) {
    254         maxr = ri;
    255       }
    256 
    257       r[i] = ri;
    258     }, true);
    259     return maxr;
    260   }
    261   /**
    262    * Calculate the norm for a 2D Matrix (M*N)
    263    * @param {Matrix} x
    264    * @param {number | string} p
    265    * @returns {number} Returns the norm
    266    * @private
    267    */
    268 
    269 
    270   function _matrixNorm(x, p) {
    271     // check p
    272     if (p === 1) {
    273       return _matrixNormOne(x);
    274     }
    275 
    276     if (p === Number.POSITIVE_INFINITY || p === 'inf') {
    277       return _matrixNormInfinity(x);
    278     }
    279 
    280     if (p === 'fro') {
    281       return _matrixNormFrobenius(x);
    282     }
    283 
    284     if (p === 2) {
    285       return _matrixNormTwo(x);
    286     } // invalid parameter value
    287 
    288 
    289     throw new Error('Unsupported parameter value ' + p);
    290   }
    291   /**
    292    * Calculate the norm for an array
    293    * @param {Matrix} x
    294    * @param {number | string} p
    295    * @returns {number} Returns the norm
    296    * @private
    297    */
    298 
    299 
    300   function _norm(x, p) {
    301     // size
    302     var sizeX = x.size(); // check if it is a vector
    303 
    304     if (sizeX.length === 1) {
    305       return _vectorNorm(x, p);
    306     } // MxN matrix
    307 
    308 
    309     if (sizeX.length === 2) {
    310       if (sizeX[0] && sizeX[1]) {
    311         return _matrixNorm(x, p);
    312       } else {
    313         throw new RangeError('Invalid matrix dimensions');
    314       }
    315     }
    316   }
    317 });