simple-squiggle

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

multiply.js (24069B)


      1 import { factory } from '../../utils/factory.js';
      2 import { isMatrix } from '../../utils/is.js';
      3 import { extend } from '../../utils/object.js';
      4 import { arraySize } from '../../utils/array.js';
      5 import { createAlgorithm11 } from '../../type/matrix/utils/algorithm11.js';
      6 import { createAlgorithm14 } from '../../type/matrix/utils/algorithm14.js';
      7 var name = 'multiply';
      8 var dependencies = ['typed', 'matrix', 'addScalar', 'multiplyScalar', 'equalScalar', 'dot'];
      9 export var createMultiply = /* #__PURE__ */factory(name, dependencies, _ref => {
     10   var {
     11     typed,
     12     matrix,
     13     addScalar,
     14     multiplyScalar,
     15     equalScalar,
     16     dot
     17   } = _ref;
     18   var algorithm11 = createAlgorithm11({
     19     typed,
     20     equalScalar
     21   });
     22   var algorithm14 = createAlgorithm14({
     23     typed
     24   });
     25 
     26   function _validateMatrixDimensions(size1, size2) {
     27     // check left operand dimensions
     28     switch (size1.length) {
     29       case 1:
     30         // check size2
     31         switch (size2.length) {
     32           case 1:
     33             // Vector x Vector
     34             if (size1[0] !== size2[0]) {
     35               // throw error
     36               throw new RangeError('Dimension mismatch in multiplication. Vectors must have the same length');
     37             }
     38 
     39             break;
     40 
     41           case 2:
     42             // Vector x Matrix
     43             if (size1[0] !== size2[0]) {
     44               // throw error
     45               throw new RangeError('Dimension mismatch in multiplication. Vector length (' + size1[0] + ') must match Matrix rows (' + size2[0] + ')');
     46             }
     47 
     48             break;
     49 
     50           default:
     51             throw new Error('Can only multiply a 1 or 2 dimensional matrix (Matrix B has ' + size2.length + ' dimensions)');
     52         }
     53 
     54         break;
     55 
     56       case 2:
     57         // check size2
     58         switch (size2.length) {
     59           case 1:
     60             // Matrix x Vector
     61             if (size1[1] !== size2[0]) {
     62               // throw error
     63               throw new RangeError('Dimension mismatch in multiplication. Matrix columns (' + size1[1] + ') must match Vector length (' + size2[0] + ')');
     64             }
     65 
     66             break;
     67 
     68           case 2:
     69             // Matrix x Matrix
     70             if (size1[1] !== size2[0]) {
     71               // throw error
     72               throw new RangeError('Dimension mismatch in multiplication. Matrix A columns (' + size1[1] + ') must match Matrix B rows (' + size2[0] + ')');
     73             }
     74 
     75             break;
     76 
     77           default:
     78             throw new Error('Can only multiply a 1 or 2 dimensional matrix (Matrix B has ' + size2.length + ' dimensions)');
     79         }
     80 
     81         break;
     82 
     83       default:
     84         throw new Error('Can only multiply a 1 or 2 dimensional matrix (Matrix A has ' + size1.length + ' dimensions)');
     85     }
     86   }
     87   /**
     88    * C = A * B
     89    *
     90    * @param {Matrix} a            Dense Vector   (N)
     91    * @param {Matrix} b            Dense Vector   (N)
     92    *
     93    * @return {number}             Scalar value
     94    */
     95 
     96 
     97   function _multiplyVectorVector(a, b, n) {
     98     // check empty vector
     99     if (n === 0) {
    100       throw new Error('Cannot multiply two empty vectors');
    101     }
    102 
    103     return dot(a, b);
    104   }
    105   /**
    106    * C = A * B
    107    *
    108    * @param {Matrix} a            Dense Vector   (M)
    109    * @param {Matrix} b            Matrix         (MxN)
    110    *
    111    * @return {Matrix}             Dense Vector   (N)
    112    */
    113 
    114 
    115   function _multiplyVectorMatrix(a, b) {
    116     // process storage
    117     if (b.storage() !== 'dense') {
    118       throw new Error('Support for SparseMatrix not implemented');
    119     }
    120 
    121     return _multiplyVectorDenseMatrix(a, b);
    122   }
    123   /**
    124    * C = A * B
    125    *
    126    * @param {Matrix} a            Dense Vector   (M)
    127    * @param {Matrix} b            Dense Matrix   (MxN)
    128    *
    129    * @return {Matrix}             Dense Vector   (N)
    130    */
    131 
    132 
    133   function _multiplyVectorDenseMatrix(a, b) {
    134     // a dense
    135     var adata = a._data;
    136     var asize = a._size;
    137     var adt = a._datatype; // b dense
    138 
    139     var bdata = b._data;
    140     var bsize = b._size;
    141     var bdt = b._datatype; // rows & columns
    142 
    143     var alength = asize[0];
    144     var bcolumns = bsize[1]; // datatype
    145 
    146     var dt; // addScalar signature to use
    147 
    148     var af = addScalar; // multiplyScalar signature to use
    149 
    150     var mf = multiplyScalar; // process data types
    151 
    152     if (adt && bdt && adt === bdt && typeof adt === 'string') {
    153       // datatype
    154       dt = adt; // find signatures that matches (dt, dt)
    155 
    156       af = typed.find(addScalar, [dt, dt]);
    157       mf = typed.find(multiplyScalar, [dt, dt]);
    158     } // result
    159 
    160 
    161     var c = []; // loop matrix columns
    162 
    163     for (var j = 0; j < bcolumns; j++) {
    164       // sum (do not initialize it with zero)
    165       var sum = mf(adata[0], bdata[0][j]); // loop vector
    166 
    167       for (var i = 1; i < alength; i++) {
    168         // multiply & accumulate
    169         sum = af(sum, mf(adata[i], bdata[i][j]));
    170       }
    171 
    172       c[j] = sum;
    173     } // return matrix
    174 
    175 
    176     return a.createDenseMatrix({
    177       data: c,
    178       size: [bcolumns],
    179       datatype: dt
    180     });
    181   }
    182   /**
    183    * C = A * B
    184    *
    185    * @param {Matrix} a            Matrix         (MxN)
    186    * @param {Matrix} b            Dense Vector   (N)
    187    *
    188    * @return {Matrix}             Dense Vector   (M)
    189    */
    190 
    191 
    192   var _multiplyMatrixVector = typed('_multiplyMatrixVector', {
    193     'DenseMatrix, any': _multiplyDenseMatrixVector,
    194     'SparseMatrix, any': _multiplySparseMatrixVector
    195   });
    196   /**
    197    * C = A * B
    198    *
    199    * @param {Matrix} a            Matrix         (MxN)
    200    * @param {Matrix} b            Matrix         (NxC)
    201    *
    202    * @return {Matrix}             Matrix         (MxC)
    203    */
    204 
    205 
    206   var _multiplyMatrixMatrix = typed('_multiplyMatrixMatrix', {
    207     'DenseMatrix, DenseMatrix': _multiplyDenseMatrixDenseMatrix,
    208     'DenseMatrix, SparseMatrix': _multiplyDenseMatrixSparseMatrix,
    209     'SparseMatrix, DenseMatrix': _multiplySparseMatrixDenseMatrix,
    210     'SparseMatrix, SparseMatrix': _multiplySparseMatrixSparseMatrix
    211   });
    212   /**
    213    * C = A * B
    214    *
    215    * @param {Matrix} a            DenseMatrix  (MxN)
    216    * @param {Matrix} b            Dense Vector (N)
    217    *
    218    * @return {Matrix}             Dense Vector (M)
    219    */
    220 
    221 
    222   function _multiplyDenseMatrixVector(a, b) {
    223     // a dense
    224     var adata = a._data;
    225     var asize = a._size;
    226     var adt = a._datatype; // b dense
    227 
    228     var bdata = b._data;
    229     var bdt = b._datatype; // rows & columns
    230 
    231     var arows = asize[0];
    232     var acolumns = asize[1]; // datatype
    233 
    234     var dt; // addScalar signature to use
    235 
    236     var af = addScalar; // multiplyScalar signature to use
    237 
    238     var mf = multiplyScalar; // process data types
    239 
    240     if (adt && bdt && adt === bdt && typeof adt === 'string') {
    241       // datatype
    242       dt = adt; // find signatures that matches (dt, dt)
    243 
    244       af = typed.find(addScalar, [dt, dt]);
    245       mf = typed.find(multiplyScalar, [dt, dt]);
    246     } // result
    247 
    248 
    249     var c = []; // loop matrix a rows
    250 
    251     for (var i = 0; i < arows; i++) {
    252       // current row
    253       var row = adata[i]; // sum (do not initialize it with zero)
    254 
    255       var sum = mf(row[0], bdata[0]); // loop matrix a columns
    256 
    257       for (var j = 1; j < acolumns; j++) {
    258         // multiply & accumulate
    259         sum = af(sum, mf(row[j], bdata[j]));
    260       }
    261 
    262       c[i] = sum;
    263     } // return matrix
    264 
    265 
    266     return a.createDenseMatrix({
    267       data: c,
    268       size: [arows],
    269       datatype: dt
    270     });
    271   }
    272   /**
    273    * C = A * B
    274    *
    275    * @param {Matrix} a            DenseMatrix    (MxN)
    276    * @param {Matrix} b            DenseMatrix    (NxC)
    277    *
    278    * @return {Matrix}             DenseMatrix    (MxC)
    279    */
    280 
    281 
    282   function _multiplyDenseMatrixDenseMatrix(a, b) {
    283     // a dense
    284     var adata = a._data;
    285     var asize = a._size;
    286     var adt = a._datatype; // b dense
    287 
    288     var bdata = b._data;
    289     var bsize = b._size;
    290     var bdt = b._datatype; // rows & columns
    291 
    292     var arows = asize[0];
    293     var acolumns = asize[1];
    294     var bcolumns = bsize[1]; // datatype
    295 
    296     var dt; // addScalar signature to use
    297 
    298     var af = addScalar; // multiplyScalar signature to use
    299 
    300     var mf = multiplyScalar; // process data types
    301 
    302     if (adt && bdt && adt === bdt && typeof adt === 'string') {
    303       // datatype
    304       dt = adt; // find signatures that matches (dt, dt)
    305 
    306       af = typed.find(addScalar, [dt, dt]);
    307       mf = typed.find(multiplyScalar, [dt, dt]);
    308     } // result
    309 
    310 
    311     var c = []; // loop matrix a rows
    312 
    313     for (var i = 0; i < arows; i++) {
    314       // current row
    315       var row = adata[i]; // initialize row array
    316 
    317       c[i] = []; // loop matrix b columns
    318 
    319       for (var j = 0; j < bcolumns; j++) {
    320         // sum (avoid initializing sum to zero)
    321         var sum = mf(row[0], bdata[0][j]); // loop matrix a columns
    322 
    323         for (var x = 1; x < acolumns; x++) {
    324           // multiply & accumulate
    325           sum = af(sum, mf(row[x], bdata[x][j]));
    326         }
    327 
    328         c[i][j] = sum;
    329       }
    330     } // return matrix
    331 
    332 
    333     return a.createDenseMatrix({
    334       data: c,
    335       size: [arows, bcolumns],
    336       datatype: dt
    337     });
    338   }
    339   /**
    340    * C = A * B
    341    *
    342    * @param {Matrix} a            DenseMatrix    (MxN)
    343    * @param {Matrix} b            SparseMatrix   (NxC)
    344    *
    345    * @return {Matrix}             SparseMatrix   (MxC)
    346    */
    347 
    348 
    349   function _multiplyDenseMatrixSparseMatrix(a, b) {
    350     // a dense
    351     var adata = a._data;
    352     var asize = a._size;
    353     var adt = a._datatype; // b sparse
    354 
    355     var bvalues = b._values;
    356     var bindex = b._index;
    357     var bptr = b._ptr;
    358     var bsize = b._size;
    359     var bdt = b._datatype; // validate b matrix
    360 
    361     if (!bvalues) {
    362       throw new Error('Cannot multiply Dense Matrix times Pattern only Matrix');
    363     } // rows & columns
    364 
    365 
    366     var arows = asize[0];
    367     var bcolumns = bsize[1]; // datatype
    368 
    369     var dt; // addScalar signature to use
    370 
    371     var af = addScalar; // multiplyScalar signature to use
    372 
    373     var mf = multiplyScalar; // equalScalar signature to use
    374 
    375     var eq = equalScalar; // zero value
    376 
    377     var zero = 0; // process data types
    378 
    379     if (adt && bdt && adt === bdt && typeof adt === 'string') {
    380       // datatype
    381       dt = adt; // find signatures that matches (dt, dt)
    382 
    383       af = typed.find(addScalar, [dt, dt]);
    384       mf = typed.find(multiplyScalar, [dt, dt]);
    385       eq = typed.find(equalScalar, [dt, dt]); // convert 0 to the same datatype
    386 
    387       zero = typed.convert(0, dt);
    388     } // result
    389 
    390 
    391     var cvalues = [];
    392     var cindex = [];
    393     var cptr = []; // c matrix
    394 
    395     var c = b.createSparseMatrix({
    396       values: cvalues,
    397       index: cindex,
    398       ptr: cptr,
    399       size: [arows, bcolumns],
    400       datatype: dt
    401     }); // loop b columns
    402 
    403     for (var jb = 0; jb < bcolumns; jb++) {
    404       // update ptr
    405       cptr[jb] = cindex.length; // indeces in column jb
    406 
    407       var kb0 = bptr[jb];
    408       var kb1 = bptr[jb + 1]; // do not process column jb if no data exists
    409 
    410       if (kb1 > kb0) {
    411         // last row mark processed
    412         var last = 0; // loop a rows
    413 
    414         for (var i = 0; i < arows; i++) {
    415           // column mark
    416           var mark = i + 1; // C[i, jb]
    417 
    418           var cij = void 0; // values in b column j
    419 
    420           for (var kb = kb0; kb < kb1; kb++) {
    421             // row
    422             var ib = bindex[kb]; // check value has been initialized
    423 
    424             if (last !== mark) {
    425               // first value in column jb
    426               cij = mf(adata[i][ib], bvalues[kb]); // update mark
    427 
    428               last = mark;
    429             } else {
    430               // accumulate value
    431               cij = af(cij, mf(adata[i][ib], bvalues[kb]));
    432             }
    433           } // check column has been processed and value != 0
    434 
    435 
    436           if (last === mark && !eq(cij, zero)) {
    437             // push row & value
    438             cindex.push(i);
    439             cvalues.push(cij);
    440           }
    441         }
    442       }
    443     } // update ptr
    444 
    445 
    446     cptr[bcolumns] = cindex.length; // return sparse matrix
    447 
    448     return c;
    449   }
    450   /**
    451    * C = A * B
    452    *
    453    * @param {Matrix} a            SparseMatrix    (MxN)
    454    * @param {Matrix} b            Dense Vector (N)
    455    *
    456    * @return {Matrix}             SparseMatrix    (M, 1)
    457    */
    458 
    459 
    460   function _multiplySparseMatrixVector(a, b) {
    461     // a sparse
    462     var avalues = a._values;
    463     var aindex = a._index;
    464     var aptr = a._ptr;
    465     var adt = a._datatype; // validate a matrix
    466 
    467     if (!avalues) {
    468       throw new Error('Cannot multiply Pattern only Matrix times Dense Matrix');
    469     } // b dense
    470 
    471 
    472     var bdata = b._data;
    473     var bdt = b._datatype; // rows & columns
    474 
    475     var arows = a._size[0];
    476     var brows = b._size[0]; // result
    477 
    478     var cvalues = [];
    479     var cindex = [];
    480     var cptr = []; // datatype
    481 
    482     var dt; // addScalar signature to use
    483 
    484     var af = addScalar; // multiplyScalar signature to use
    485 
    486     var mf = multiplyScalar; // equalScalar signature to use
    487 
    488     var eq = equalScalar; // zero value
    489 
    490     var zero = 0; // process data types
    491 
    492     if (adt && bdt && adt === bdt && typeof adt === 'string') {
    493       // datatype
    494       dt = adt; // find signatures that matches (dt, dt)
    495 
    496       af = typed.find(addScalar, [dt, dt]);
    497       mf = typed.find(multiplyScalar, [dt, dt]);
    498       eq = typed.find(equalScalar, [dt, dt]); // convert 0 to the same datatype
    499 
    500       zero = typed.convert(0, dt);
    501     } // workspace
    502 
    503 
    504     var x = []; // vector with marks indicating a value x[i] exists in a given column
    505 
    506     var w = []; // update ptr
    507 
    508     cptr[0] = 0; // rows in b
    509 
    510     for (var ib = 0; ib < brows; ib++) {
    511       // b[ib]
    512       var vbi = bdata[ib]; // check b[ib] != 0, avoid loops
    513 
    514       if (!eq(vbi, zero)) {
    515         // A values & index in ib column
    516         for (var ka0 = aptr[ib], ka1 = aptr[ib + 1], ka = ka0; ka < ka1; ka++) {
    517           // a row
    518           var ia = aindex[ka]; // check value exists in current j
    519 
    520           if (!w[ia]) {
    521             // ia is new entry in j
    522             w[ia] = true; // add i to pattern of C
    523 
    524             cindex.push(ia); // x(ia) = A
    525 
    526             x[ia] = mf(vbi, avalues[ka]);
    527           } else {
    528             // i exists in C already
    529             x[ia] = af(x[ia], mf(vbi, avalues[ka]));
    530           }
    531         }
    532       }
    533     } // copy values from x to column jb of c
    534 
    535 
    536     for (var p1 = cindex.length, p = 0; p < p1; p++) {
    537       // row
    538       var ic = cindex[p]; // copy value
    539 
    540       cvalues[p] = x[ic];
    541     } // update ptr
    542 
    543 
    544     cptr[1] = cindex.length; // return sparse matrix
    545 
    546     return a.createSparseMatrix({
    547       values: cvalues,
    548       index: cindex,
    549       ptr: cptr,
    550       size: [arows, 1],
    551       datatype: dt
    552     });
    553   }
    554   /**
    555    * C = A * B
    556    *
    557    * @param {Matrix} a            SparseMatrix      (MxN)
    558    * @param {Matrix} b            DenseMatrix       (NxC)
    559    *
    560    * @return {Matrix}             SparseMatrix      (MxC)
    561    */
    562 
    563 
    564   function _multiplySparseMatrixDenseMatrix(a, b) {
    565     // a sparse
    566     var avalues = a._values;
    567     var aindex = a._index;
    568     var aptr = a._ptr;
    569     var adt = a._datatype; // validate a matrix
    570 
    571     if (!avalues) {
    572       throw new Error('Cannot multiply Pattern only Matrix times Dense Matrix');
    573     } // b dense
    574 
    575 
    576     var bdata = b._data;
    577     var bdt = b._datatype; // rows & columns
    578 
    579     var arows = a._size[0];
    580     var brows = b._size[0];
    581     var bcolumns = b._size[1]; // datatype
    582 
    583     var dt; // addScalar signature to use
    584 
    585     var af = addScalar; // multiplyScalar signature to use
    586 
    587     var mf = multiplyScalar; // equalScalar signature to use
    588 
    589     var eq = equalScalar; // zero value
    590 
    591     var zero = 0; // process data types
    592 
    593     if (adt && bdt && adt === bdt && typeof adt === 'string') {
    594       // datatype
    595       dt = adt; // find signatures that matches (dt, dt)
    596 
    597       af = typed.find(addScalar, [dt, dt]);
    598       mf = typed.find(multiplyScalar, [dt, dt]);
    599       eq = typed.find(equalScalar, [dt, dt]); // convert 0 to the same datatype
    600 
    601       zero = typed.convert(0, dt);
    602     } // result
    603 
    604 
    605     var cvalues = [];
    606     var cindex = [];
    607     var cptr = []; // c matrix
    608 
    609     var c = a.createSparseMatrix({
    610       values: cvalues,
    611       index: cindex,
    612       ptr: cptr,
    613       size: [arows, bcolumns],
    614       datatype: dt
    615     }); // workspace
    616 
    617     var x = []; // vector with marks indicating a value x[i] exists in a given column
    618 
    619     var w = []; // loop b columns
    620 
    621     for (var jb = 0; jb < bcolumns; jb++) {
    622       // update ptr
    623       cptr[jb] = cindex.length; // mark in workspace for current column
    624 
    625       var mark = jb + 1; // rows in jb
    626 
    627       for (var ib = 0; ib < brows; ib++) {
    628         // b[ib, jb]
    629         var vbij = bdata[ib][jb]; // check b[ib, jb] != 0, avoid loops
    630 
    631         if (!eq(vbij, zero)) {
    632           // A values & index in ib column
    633           for (var ka0 = aptr[ib], ka1 = aptr[ib + 1], ka = ka0; ka < ka1; ka++) {
    634             // a row
    635             var ia = aindex[ka]; // check value exists in current j
    636 
    637             if (w[ia] !== mark) {
    638               // ia is new entry in j
    639               w[ia] = mark; // add i to pattern of C
    640 
    641               cindex.push(ia); // x(ia) = A
    642 
    643               x[ia] = mf(vbij, avalues[ka]);
    644             } else {
    645               // i exists in C already
    646               x[ia] = af(x[ia], mf(vbij, avalues[ka]));
    647             }
    648           }
    649         }
    650       } // copy values from x to column jb of c
    651 
    652 
    653       for (var p0 = cptr[jb], p1 = cindex.length, p = p0; p < p1; p++) {
    654         // row
    655         var ic = cindex[p]; // copy value
    656 
    657         cvalues[p] = x[ic];
    658       }
    659     } // update ptr
    660 
    661 
    662     cptr[bcolumns] = cindex.length; // return sparse matrix
    663 
    664     return c;
    665   }
    666   /**
    667    * C = A * B
    668    *
    669    * @param {Matrix} a            SparseMatrix      (MxN)
    670    * @param {Matrix} b            SparseMatrix      (NxC)
    671    *
    672    * @return {Matrix}             SparseMatrix      (MxC)
    673    */
    674 
    675 
    676   function _multiplySparseMatrixSparseMatrix(a, b) {
    677     // a sparse
    678     var avalues = a._values;
    679     var aindex = a._index;
    680     var aptr = a._ptr;
    681     var adt = a._datatype; // b sparse
    682 
    683     var bvalues = b._values;
    684     var bindex = b._index;
    685     var bptr = b._ptr;
    686     var bdt = b._datatype; // rows & columns
    687 
    688     var arows = a._size[0];
    689     var bcolumns = b._size[1]; // flag indicating both matrices (a & b) contain data
    690 
    691     var values = avalues && bvalues; // datatype
    692 
    693     var dt; // addScalar signature to use
    694 
    695     var af = addScalar; // multiplyScalar signature to use
    696 
    697     var mf = multiplyScalar; // process data types
    698 
    699     if (adt && bdt && adt === bdt && typeof adt === 'string') {
    700       // datatype
    701       dt = adt; // find signatures that matches (dt, dt)
    702 
    703       af = typed.find(addScalar, [dt, dt]);
    704       mf = typed.find(multiplyScalar, [dt, dt]);
    705     } // result
    706 
    707 
    708     var cvalues = values ? [] : undefined;
    709     var cindex = [];
    710     var cptr = []; // c matrix
    711 
    712     var c = a.createSparseMatrix({
    713       values: cvalues,
    714       index: cindex,
    715       ptr: cptr,
    716       size: [arows, bcolumns],
    717       datatype: dt
    718     }); // workspace
    719 
    720     var x = values ? [] : undefined; // vector with marks indicating a value x[i] exists in a given column
    721 
    722     var w = []; // variables
    723 
    724     var ka, ka0, ka1, kb, kb0, kb1, ia, ib; // loop b columns
    725 
    726     for (var jb = 0; jb < bcolumns; jb++) {
    727       // update ptr
    728       cptr[jb] = cindex.length; // mark in workspace for current column
    729 
    730       var mark = jb + 1; // B values & index in j
    731 
    732       for (kb0 = bptr[jb], kb1 = bptr[jb + 1], kb = kb0; kb < kb1; kb++) {
    733         // b row
    734         ib = bindex[kb]; // check we need to process values
    735 
    736         if (values) {
    737           // loop values in a[:,ib]
    738           for (ka0 = aptr[ib], ka1 = aptr[ib + 1], ka = ka0; ka < ka1; ka++) {
    739             // row
    740             ia = aindex[ka]; // check value exists in current j
    741 
    742             if (w[ia] !== mark) {
    743               // ia is new entry in j
    744               w[ia] = mark; // add i to pattern of C
    745 
    746               cindex.push(ia); // x(ia) = A
    747 
    748               x[ia] = mf(bvalues[kb], avalues[ka]);
    749             } else {
    750               // i exists in C already
    751               x[ia] = af(x[ia], mf(bvalues[kb], avalues[ka]));
    752             }
    753           }
    754         } else {
    755           // loop values in a[:,ib]
    756           for (ka0 = aptr[ib], ka1 = aptr[ib + 1], ka = ka0; ka < ka1; ka++) {
    757             // row
    758             ia = aindex[ka]; // check value exists in current j
    759 
    760             if (w[ia] !== mark) {
    761               // ia is new entry in j
    762               w[ia] = mark; // add i to pattern of C
    763 
    764               cindex.push(ia);
    765             }
    766           }
    767         }
    768       } // check we need to process matrix values (pattern matrix)
    769 
    770 
    771       if (values) {
    772         // copy values from x to column jb of c
    773         for (var p0 = cptr[jb], p1 = cindex.length, p = p0; p < p1; p++) {
    774           // row
    775           var ic = cindex[p]; // copy value
    776 
    777           cvalues[p] = x[ic];
    778         }
    779       }
    780     } // update ptr
    781 
    782 
    783     cptr[bcolumns] = cindex.length; // return sparse matrix
    784 
    785     return c;
    786   }
    787   /**
    788    * Multiply two or more values, `x * y`.
    789    * For matrices, the matrix product is calculated.
    790    *
    791    * Syntax:
    792    *
    793    *    math.multiply(x, y)
    794    *    math.multiply(x, y, z, ...)
    795    *
    796    * Examples:
    797    *
    798    *    math.multiply(4, 5.2)        // returns number 20.8
    799    *    math.multiply(2, 3, 4)       // returns number 24
    800    *
    801    *    const a = math.complex(2, 3)
    802    *    const b = math.complex(4, 1)
    803    *    math.multiply(a, b)          // returns Complex 5 + 14i
    804    *
    805    *    const c = [[1, 2], [4, 3]]
    806    *    const d = [[1, 2, 3], [3, -4, 7]]
    807    *    math.multiply(c, d)          // returns Array [[7, -6, 17], [13, -4, 33]]
    808    *
    809    *    const e = math.unit('2.1 km')
    810    *    math.multiply(3, e)          // returns Unit 6.3 km
    811    *
    812    * See also:
    813    *
    814    *    divide, prod, cross, dot
    815    *
    816    * @param  {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} x First value to multiply
    817    * @param  {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} y Second value to multiply
    818    * @return {number | BigNumber | Fraction | Complex | Unit | Array | Matrix} Multiplication of `x` and `y`
    819    */
    820 
    821 
    822   return typed(name, extend({
    823     // we extend the signatures of multiplyScalar with signatures dealing with matrices
    824     'Array, Array': function ArrayArray(x, y) {
    825       // check dimensions
    826       _validateMatrixDimensions(arraySize(x), arraySize(y)); // use dense matrix implementation
    827 
    828 
    829       var m = this(matrix(x), matrix(y)); // return array or scalar
    830 
    831       return isMatrix(m) ? m.valueOf() : m;
    832     },
    833     'Matrix, Matrix': function MatrixMatrix(x, y) {
    834       // dimensions
    835       var xsize = x.size();
    836       var ysize = y.size(); // check dimensions
    837 
    838       _validateMatrixDimensions(xsize, ysize); // process dimensions
    839 
    840 
    841       if (xsize.length === 1) {
    842         // process y dimensions
    843         if (ysize.length === 1) {
    844           // Vector * Vector
    845           return _multiplyVectorVector(x, y, xsize[0]);
    846         } // Vector * Matrix
    847 
    848 
    849         return _multiplyVectorMatrix(x, y);
    850       } // process y dimensions
    851 
    852 
    853       if (ysize.length === 1) {
    854         // Matrix * Vector
    855         return _multiplyMatrixVector(x, y);
    856       } // Matrix * Matrix
    857 
    858 
    859       return _multiplyMatrixMatrix(x, y);
    860     },
    861     'Matrix, Array': function MatrixArray(x, y) {
    862       // use Matrix * Matrix implementation
    863       return this(x, matrix(y));
    864     },
    865     'Array, Matrix': function ArrayMatrix(x, y) {
    866       // use Matrix * Matrix implementation
    867       return this(matrix(x, y.storage()), y);
    868     },
    869     'SparseMatrix, any': function SparseMatrixAny(x, y) {
    870       return algorithm11(x, y, multiplyScalar, false);
    871     },
    872     'DenseMatrix, any': function DenseMatrixAny(x, y) {
    873       return algorithm14(x, y, multiplyScalar, false);
    874     },
    875     'any, SparseMatrix': function anySparseMatrix(x, y) {
    876       return algorithm11(y, x, multiplyScalar, true);
    877     },
    878     'any, DenseMatrix': function anyDenseMatrix(x, y) {
    879       return algorithm14(y, x, multiplyScalar, true);
    880     },
    881     'Array, any': function ArrayAny(x, y) {
    882       // use matrix implementation
    883       return algorithm14(matrix(x), y, multiplyScalar, false).valueOf();
    884     },
    885     'any, Array': function anyArray(x, y) {
    886       // use matrix implementation
    887       return algorithm14(matrix(y), x, multiplyScalar, true).valueOf();
    888     },
    889     'any, any': multiplyScalar,
    890     'any, any, ...any': function anyAnyAny(x, y, rest) {
    891       var result = this(x, y);
    892 
    893       for (var i = 0; i < rest.length; i++) {
    894         result = this(result, rest[i]);
    895       }
    896 
    897       return result;
    898     }
    899   }, multiplyScalar.signatures));
    900 });