UNPKG

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