RandomGamingDev 207623c931
feat: improve the performance f multiply by adding matrix type inferencing (#3149)
* added type inference

* added back accidentally removed return statement and made it so that the explicitly defined type is returned at the end

* made sure that mixed types are ignored in the process data types check

* fixed issue with undefined _data for SparseMatrix and linting issues

* simplified syntax and added type inferencing to src/type/matrix/utils and src/function/matrix/dot.js

* shortened the final part of the type inferencing and moved it to matrix creation in multiply

---------

Co-authored-by: Jos de Jong <wjosdejong@gmail.com>
2024-02-22 11:10:26 +01:00

167 lines
4.4 KiB
JavaScript

import { factory } from '../../utils/factory.js'
import { isMatrix } from '../../utils/is.js'
const name = 'dot'
const dependencies = ['typed', 'addScalar', 'multiplyScalar', 'conj', 'size']
export const createDot = /* #__PURE__ */ factory(name, dependencies, ({ typed, addScalar, multiplyScalar, conj, size }) => {
/**
* Calculate the dot product of two vectors. The dot product of
* `A = [a1, a2, ..., an]` and `B = [b1, b2, ..., bn]` is defined as:
*
* dot(A, B) = conj(a1) * b1 + conj(a2) * b2 + ... + conj(an) * bn
*
* Syntax:
*
* math.dot(x, y)
*
* Examples:
*
* math.dot([2, 4, 1], [2, 2, 3]) // returns number 15
* math.multiply([2, 4, 1], [2, 2, 3]) // returns number 15
*
* See also:
*
* multiply, cross
*
* @param {Array | Matrix} x First vector
* @param {Array | Matrix} y Second vector
* @return {number} Returns the dot product of `x` and `y`
*/
return typed(name, {
'Array | DenseMatrix, Array | DenseMatrix': _denseDot,
'SparseMatrix, SparseMatrix': _sparseDot
})
function _validateDim (x, y) {
const xSize = _size(x)
const ySize = _size(y)
let xLen, yLen
if (xSize.length === 1) {
xLen = xSize[0]
} else if (xSize.length === 2 && xSize[1] === 1) {
xLen = xSize[0]
} else {
throw new RangeError('Expected a column vector, instead got a matrix of size (' + xSize.join(', ') + ')')
}
if (ySize.length === 1) {
yLen = ySize[0]
} else if (ySize.length === 2 && ySize[1] === 1) {
yLen = ySize[0]
} else {
throw new RangeError('Expected a column vector, instead got a matrix of size (' + ySize.join(', ') + ')')
}
if (xLen !== yLen) throw new RangeError('Vectors must have equal length (' + xLen + ' != ' + yLen + ')')
if (xLen === 0) throw new RangeError('Cannot calculate the dot product of empty vectors')
return xLen
}
function _denseDot (a, b) {
const N = _validateDim(a, b)
const adata = isMatrix(a) ? a._data : a
const adt = isMatrix(a) ? a._datatype || a.getDataType() : undefined
const bdata = isMatrix(b) ? b._data : b
const bdt = isMatrix(b) ? b._datatype || b.getDataType() : undefined
// are these 2-dimensional column vectors? (as opposed to 1-dimensional vectors)
const aIsColumn = _size(a).length === 2
const bIsColumn = _size(b).length === 2
let add = addScalar
let mul = multiplyScalar
// process data types
if (adt && bdt && adt === bdt && typeof adt === 'string' && adt !== 'mixed') {
const dt = adt
// find signatures that matches (dt, dt)
add = typed.find(addScalar, [dt, dt])
mul = typed.find(multiplyScalar, [dt, dt])
}
// both vectors 1-dimensional
if (!aIsColumn && !bIsColumn) {
let c = mul(conj(adata[0]), bdata[0])
for (let i = 1; i < N; i++) {
c = add(c, mul(conj(adata[i]), bdata[i]))
}
return c
}
// a is 1-dim, b is column
if (!aIsColumn && bIsColumn) {
let c = mul(conj(adata[0]), bdata[0][0])
for (let i = 1; i < N; i++) {
c = add(c, mul(conj(adata[i]), bdata[i][0]))
}
return c
}
// a is column, b is 1-dim
if (aIsColumn && !bIsColumn) {
let c = mul(conj(adata[0][0]), bdata[0])
for (let i = 1; i < N; i++) {
c = add(c, mul(conj(adata[i][0]), bdata[i]))
}
return c
}
// both vectors are column
if (aIsColumn && bIsColumn) {
let c = mul(conj(adata[0][0]), bdata[0][0])
for (let i = 1; i < N; i++) {
c = add(c, mul(conj(adata[i][0]), bdata[i][0]))
}
return c
}
}
function _sparseDot (x, y) {
_validateDim(x, y)
const xindex = x._index
const xvalues = x._values
const yindex = y._index
const yvalues = y._values
// TODO optimize add & mul using datatype
let c = 0
const add = addScalar
const mul = multiplyScalar
let i = 0
let j = 0
while (i < xindex.length && j < yindex.length) {
const I = xindex[i]
const J = yindex[j]
if (I < J) {
i++
continue
}
if (I > J) {
j++
continue
}
if (I === J) {
c = add(c, mul(xvalues[i], yvalues[j]))
i++
j++
}
}
return c
}
// TODO remove this once #1771 is fixed
function _size (x) {
return isMatrix(x) ? x.size() : size(x)
}
})