Skip to content
Open
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 25 additions & 1 deletion docs/datatypes/matrices.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ dense and sparse matrices.
Math.js supports two types of matrices:

- `Array`, a regular JavaScript array. A multidimensional array can be created
by nesting arrays.
by nesting arrays. If the elements of an array are of the same size, it is a
rectangular array. If the elements are all arrays but not of the same size,
it is a jagged array and if not all elements are arrays then it's an
heterogeneous array.
- `Matrix`, a matrix implementation by math.js. A `Matrix` is an object wrapped
around a regular JavaScript `Array`, providing utility functions for easy
matrix manipulation such as `subset`, `size`, `resize`, `clone`, and more.
Expand Down Expand Up @@ -224,6 +227,27 @@ If you have a matrix where the first dimension means `x` and the second
means `y`, this will look confusing since `x` is printed as _column_
(vertically) and `y` as _row_ (horizontally).

## Not rectangular arrays

By nesting arrays it is possible to have arrays that are not rectangular, for example.
```js
[[1, 2], [3, 4]] // rectangular of size [2, 2]
[[1, 2], [3]] // jagged
[[1, 2], 3] // heterogeneous
```

These types of arrays can't be converted to a matrix, but many operations are available for them.
```js
const A = [[1, 2], 3]
math.add(A, 1)
math.map(A, a => a+1)
math.forEach(A, a => console.log(a))
```
Many matrix functions expect a rectangular array and might provide unexpected results with non rectangular arrays, for example.
```js
math.size([[1, 2], [3]]) // [2, 2]
```
The process of validation for rectangularity is expensive and this provides a fast way of working with nested arrays of many kinds.

## Resizing

Expand Down
21 changes: 18 additions & 3 deletions src/function/bitwise/leftShift.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
import { createMatAlgo02xDS0 } from '../../type/matrix/utils/matAlgo02xDS0.js'
import { createMatAlgo11xS0s } from '../../type/matrix/utils/matAlgo11xS0s.js'
import { createMatAlgo14xDs } from '../../type/matrix/utils/matAlgo14xDs.js'
import { createMatAlgo15xAs } from '../../type/matrix/utils/matAlgo15xAs.js'
import { createMatAlgo01xDSid } from '../../type/matrix/utils/matAlgo01xDSid.js'
import { createMatAlgo10xSids } from '../../type/matrix/utils/matAlgo10xSids.js'
import { createMatAlgo08xS0Sid } from '../../type/matrix/utils/matAlgo08xS0Sid.js'
import { factory } from '../../utils/factory.js'
import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgorithmSuite.js'
import { createUseMatrixForArrayScalar } from './useMatrixForArrayScalar.js'
import { leftShiftNumber } from '../../plain/number/index.js'
import { leftShiftBigNumber } from '../../utils/bignumber/bitwise.js'
import { deepMap, clone } from '../../utils/array.js'

const name = 'leftShift'
const dependencies = [
Expand All @@ -27,8 +28,8 @@ export const createLeftShift = /* #__PURE__ */ factory(name, dependencies, ({ ty
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
const matAlgo15xAs = createMatAlgo15xAs()
const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, matrix })

/**
* Bitwise left logical shift of a value x by y number of bits, `x << y`.
Expand Down Expand Up @@ -78,6 +79,14 @@ export const createLeftShift = /* #__PURE__ */ factory(name, dependencies, ({ ty
return matAlgo14xDs(x, y, self, false)
}),

'Array, number | BigNumber': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(y, 0)) {
return clone(x)
}
return matAlgo15xAs(x, y, self, false)
}),

'number | BigNumber, SparseMatrix': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(x, 0)) {
Expand All @@ -92,9 +101,15 @@ export const createLeftShift = /* #__PURE__ */ factory(name, dependencies, ({ ty
return zeros(y.size(), y.storage())
}
return matAlgo14xDs(y, x, self, true)
}),
'number | BigNumber, Array': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(x, 0)) {
return deepMap(y, () => x)
}
return matAlgo15xAs(y, x, self, true)
})
},
useMatrixForArrayScalar,
matrixAlgorithmSuite({
SS: matAlgo08xS0Sid,
DS: matAlgo01xDSid,
Expand Down
21 changes: 18 additions & 3 deletions src/function/bitwise/rightArithShift.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,14 @@ import { rightArithShiftBigNumber } from '../../utils/bignumber/bitwise.js'
import { createMatAlgo02xDS0 } from '../../type/matrix/utils/matAlgo02xDS0.js'
import { createMatAlgo11xS0s } from '../../type/matrix/utils/matAlgo11xS0s.js'
import { createMatAlgo14xDs } from '../../type/matrix/utils/matAlgo14xDs.js'
import { createMatAlgo15xAs } from '../../type/matrix/utils/matAlgo15xAs.js'
import { createMatAlgo01xDSid } from '../../type/matrix/utils/matAlgo01xDSid.js'
import { createMatAlgo10xSids } from '../../type/matrix/utils/matAlgo10xSids.js'
import { createMatAlgo08xS0Sid } from '../../type/matrix/utils/matAlgo08xS0Sid.js'
import { factory } from '../../utils/factory.js'
import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgorithmSuite.js'
import { createUseMatrixForArrayScalar } from './useMatrixForArrayScalar.js'
import { rightArithShiftNumber } from '../../plain/number/index.js'
import { clone, deepMap } from '../../utils/array.js'

const name = 'rightArithShift'
const dependencies = [
Expand All @@ -27,8 +28,8 @@ export const createRightArithShift = /* #__PURE__ */ factory(name, dependencies,
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
const matAlgo15xAs = createMatAlgo15xAs()
const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, matrix })

/**
* Bitwise right arithmetic shift of a value x by y number of bits, `x >> y`.
Expand Down Expand Up @@ -78,6 +79,14 @@ export const createRightArithShift = /* #__PURE__ */ factory(name, dependencies,
return matAlgo14xDs(x, y, self, false)
}),

'Array, number | BigNumber': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(y, 0)) {
return clone(x)
}
return matAlgo15xAs(x, y, self, false)
}),

'number | BigNumber, SparseMatrix': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(x, 0)) {
Expand All @@ -92,9 +101,15 @@ export const createRightArithShift = /* #__PURE__ */ factory(name, dependencies,
return zeros(y.size(), y.storage())
}
return matAlgo14xDs(y, x, self, true)
}),
'number | BigNumber, Array': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(x, 0)) {
return deepMap(y, () => x)
}
return matAlgo15xAs(y, x, self, true)
})
},
useMatrixForArrayScalar,
matrixAlgorithmSuite({
SS: matAlgo08xS0Sid,
DS: matAlgo01xDSid,
Expand Down
22 changes: 19 additions & 3 deletions src/function/bitwise/rightLogShift.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ import { createMatAlgo14xDs } from '../../type/matrix/utils/matAlgo14xDs.js'
import { createMatAlgo01xDSid } from '../../type/matrix/utils/matAlgo01xDSid.js'
import { createMatAlgo10xSids } from '../../type/matrix/utils/matAlgo10xSids.js'
import { createMatAlgo08xS0Sid } from '../../type/matrix/utils/matAlgo08xS0Sid.js'
import { createMatAlgo15xAs } from '../../type/matrix/utils/matAlgo15xAs.js'
import { factory } from '../../utils/factory.js'
import { createMatrixAlgorithmSuite } from '../../type/matrix/utils/matrixAlgorithmSuite.js'
import { rightLogShiftNumber } from '../../plain/number/index.js'
import { createUseMatrixForArrayScalar } from './useMatrixForArrayScalar.js'
import { deepMap, clone } from '../../utils/array.js'

const name = 'rightLogShift'
const dependencies = [
Expand All @@ -26,8 +27,8 @@ export const createRightLogShift = /* #__PURE__ */ factory(name, dependencies, (
const matAlgo10xSids = createMatAlgo10xSids({ typed, DenseMatrix })
const matAlgo11xS0s = createMatAlgo11xS0s({ typed, equalScalar })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
const matAlgo15xAs = createMatAlgo15xAs()
const matrixAlgorithmSuite = createMatrixAlgorithmSuite({ typed, matrix, concat })
const useMatrixForArrayScalar = createUseMatrixForArrayScalar({ typed, matrix })

/**
* Bitwise right logical shift of value x by y number of bits, `x >>> y`.
Expand Down Expand Up @@ -76,6 +77,14 @@ export const createRightLogShift = /* #__PURE__ */ factory(name, dependencies, (
return matAlgo14xDs(x, y, self, false)
}),

'Array, number | BigNumber': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(y, 0)) {
return clone(x)
}
return matAlgo15xAs(x, y, self, false)
}),

'number | BigNumber, SparseMatrix': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(x, 0)) {
Expand All @@ -90,9 +99,16 @@ export const createRightLogShift = /* #__PURE__ */ factory(name, dependencies, (
return zeros(y.size(), y.storage())
}
return matAlgo14xDs(y, x, self, true)
}),

'number | BigNumber, Array': typed.referToSelf(self => (x, y) => {
// check scalar
if (equalScalar(x, 0)) {
return deepMap(y, () => x)
}
return matAlgo15xAs(y, x, self, true)
})
},
useMatrixForArrayScalar,
matrixAlgorithmSuite({
SS: matAlgo08xS0Sid,
DS: matAlgo01xDSid,
Expand Down
15 changes: 0 additions & 15 deletions src/function/bitwise/useMatrixForArrayScalar.js

This file was deleted.

42 changes: 3 additions & 39 deletions src/type/matrix/utils/matAlgo14xDs.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { factory } from '../../../utils/factory.js'
import { clone } from '../../../utils/object.js'

const name = 'matAlgo14xDs'
const dependencies = ['typed']
Expand All @@ -22,54 +21,19 @@ export const createMatAlgo14xDs = /* #__PURE__ */ factory(name, dependencies, ({
*/
return function matAlgo14xDs (a, b, callback, inverse) {
// a arrays
const adata = a._data
const asize = a._size
const adt = a._datatype

// datatype
let dt
// callback signature to use
let cf = callback

// process data types
if (typeof adt === 'string') {
// datatype
dt = adt
// convert b to the same datatype
b = typed.convert(b, dt)
b = typed.convert(b, adt)
// callback
cf = typed.find(callback, [dt, dt])
cf = typed.find(callback, [adt, adt])
}

// populate cdata, iterate through dimensions
const cdata = asize.length > 0 ? _iterate(cf, 0, asize, asize[0], adata, b, inverse) : []

// c matrix
return a.createDenseMatrix({
data: cdata,
size: clone(asize),
datatype: dt
})
}

// recursive function
function _iterate (f, level, s, n, av, bv, inverse) {
// initialize array for this level
const cv = []
// check we reach the last level
if (level === s.length - 1) {
// loop arrays in last level
for (let i = 0; i < n; i++) {
// invoke callback and store value
cv[i] = inverse ? f(bv, av[i]) : f(av[i], bv)
}
} else {
// iterate current level
for (let j = 0; j < n; j++) {
// iterate next level
cv[j] = _iterate(f, level + 1, s, s[level + 1], av[j], bv, inverse)
}
}
return cv
return inverse ? a.map(v => cf(b, v)) : a.map(v => cf(v, b))
}
})
26 changes: 26 additions & 0 deletions src/type/matrix/utils/matAlgo15xAs.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { factory } from '../../../utils/factory.js'
import { deepMap as map } from '../../../utils/array.js'

const name = 'matAlgo15xAs'
const dependencies = []

export const createMatAlgo15xAs = /* #__PURE__ */ factory(name, dependencies, () => {
/**
* Iterates over Array items and invokes the callback function f(Aij..z, b).
* Callback function invoked MxN times.
*
* C(i,j,...z) = f(Aij..z, b)
*
* @param {Array} a The Array instance (A)
* @param {Scalar} b The Scalar value
* @param {Function} cf The f(Aij..z,b) operation to invoke
* @param {boolean} inverse A true value indicates callback should be invoked f(b,Aij..z)
*
* @return {Array} Array (C)
*
* https://github.com/josdejong/mathjs/pull/346#issuecomment-97659042
*/
return function matAlgo15xAs (a, b, cf, inverse) {
return inverse ? map(a, v => cf(b, v)) : map(a, v => cf(v, b))
}
})
10 changes: 6 additions & 4 deletions src/type/matrix/utils/matrixAlgorithmSuite.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { factory } from '../../../utils/factory.js'
import { extend } from '../../../utils/object.js'
import { createMatAlgo13xDD } from './matAlgo13xDD.js'
import { createMatAlgo14xDs } from './matAlgo14xDs.js'
import { createMatAlgo15xAs } from './matAlgo15xAs.js'
import { broadcast } from './broadcast.js'

const name = 'matrixAlgorithmSuite'
Expand All @@ -11,6 +12,7 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
name, dependencies, ({ typed, matrix }) => {
const matAlgo13xDD = createMatAlgo13xDD({ typed })
const matAlgo14xDs = createMatAlgo14xDs({ typed })
const matAlgo15xAs = createMatAlgo15xAs()

/**
* Return a signatures object with the usual boilerplate of
Expand Down Expand Up @@ -115,9 +117,9 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
matrixSignatures[scalar + ', DenseMatrix'] =
(x, y) => matAlgo14xDs(y, x, elop, true)
matrixSignatures['Array,' + scalar] =
(x, y) => matAlgo14xDs(matrix(x), y, elop, false).valueOf()
(x, y) => matAlgo15xAs(x, y, elop, false)
matrixSignatures[scalar + ', Array'] =
(x, y) => matAlgo14xDs(matrix(y), x, elop, true).valueOf()
(x, y) => matAlgo15xAs(y, x, elop, true)
} else {
matrixSignatures['DenseMatrix,' + scalar] =
typed.referToSelf(self => (x, y) => {
Expand All @@ -129,11 +131,11 @@ export const createMatrixAlgorithmSuite = /* #__PURE__ */ factory(
})
matrixSignatures['Array,' + scalar] =
typed.referToSelf(self => (x, y) => {
return matAlgo14xDs(matrix(x), y, self, false).valueOf()
return matAlgo15xAs(x, y, self, false)
})
matrixSignatures[scalar + ', Array'] =
typed.referToSelf(self => (x, y) => {
return matAlgo14xDs(matrix(y), x, self, true).valueOf()
return matAlgo15xAs(y, x, self, true)
})
}
}
Expand Down
5 changes: 5 additions & 0 deletions test/unit-tests/function/arithmetic/add.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ describe('add', function () {
assert.deepStrictEqual(add([3, 4], 2), [5, 6])
})

it('should add a scalar and a jagged array correctly', function () {
assert.deepStrictEqual(add(2, [[3, 4], 5]), [[5, 6], 7])
assert.deepStrictEqual(add([[3, 4], 5], 2), [[5, 6], 7])
})

it('should add broadcastable arrays correctly', function () {
const a2 = [1, 2]
const a3 = [[3], [4]]
Expand Down
3 changes: 3 additions & 0 deletions test/unit-tests/function/arithmetic/dotDivide.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,9 @@ describe('dotDivide', function () {
const a = [[1, 2], [3, 4]]
assert.deepStrictEqual(dotDivide(a, 2), [[0.5, 1], [1.5, 2]])
assert.deepStrictEqual(dotDivide([], 2), [])
// jagged array
const ja = [[1, 2, 3], [4], [5, 6]]
assert.deepStrictEqual(dotDivide(ja, 2), [[0.5, 1, 1.5], [2], [2.5, 3]])
})

it('should divide 1 over a array element-wise', function () {
Expand Down
4 changes: 4 additions & 0 deletions test/unit-tests/function/arithmetic/dotMultiply.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,10 @@ describe('dotMultiply', function () {
approxDeepEqual(dotMultiply(3, a), [[3, 0], [9, 12]])
approxDeepEqual(dotMultiply([1, 2, 3, 4], 2), [2, 4, 6, 8])
approxDeepEqual(dotMultiply(2, [1, 2, 3, 4]), [2, 4, 6, 8])
// jagged array
const ja = [[1, 2, 3], [4], [5, 6]]
approxDeepEqual(dotMultiply(ja, 2), [[2, 4, 6], [8], [10, 12]])
approxDeepEqual(dotMultiply(2, ja), [[2, 4, 6], [8], [10, 12]])
})

it('should multiply broadcastable arrays element-wise', function () {
Expand Down
Loading