Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Matrix creation and conversion methods #2155

Merged
merged 14 commits into from
May 9, 2021
6 changes: 6 additions & 0 deletions src/expression/embeddedDocs/embeddedDocs.js
Original file line number Diff line number Diff line change
Expand Up @@ -202,6 +202,9 @@ import { splitUnitDocs } from './construction/splitUnit.js'
import { sparseDocs } from './construction/sparse.js'
import { numberDocs } from './construction/number.js'
import { matrixDocs } from './construction/matrix.js'
import { matrixFromFunctionDocs } from './function/matrix/matrixFromFunction.js'
import { matrixFromRowsDocs } from './function/matrix/matrixFromRows.js'
import { matrixFromColumnsDocs } from './function/matrix/matrixFromColumns.js'
import { indexDocs } from './construction/index.js'
import { fractionDocs } from './construction/fraction.js'
import { createUnitDocs } from './construction/createUnit.js'
Expand Down Expand Up @@ -424,6 +427,9 @@ export const embeddedDocs = {
inv: invDocs,
eigs: eigsDocs,
kron: kronDocs,
matrixFromFunction: matrixFromFunctionDocs,
matrixFromRows: matrixFromRowsDocs,
matrixFromColumns: matrixFromColumnsDocs,
map: mapDocs,
ones: onesDocs,
partitionSelect: partitionSelectDocs,
Expand Down
16 changes: 16 additions & 0 deletions src/expression/embeddedDocs/function/matrix/matrixFromColumns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const matrixFromColumnsDocs = {
name: 'matrixFromColumns',
category: 'Matrix',
syntax: [
'math.matrixFromColumns(...arr)',
'math.matrixFromColumns(row1, row2)',
'math.matrixFromColumns(row1, row2, row3)'
],
description: 'Create a dense matrix from vectors as individual columns.',
examples: [
'matrixFromColumns([1, 2, 3], [[4],[5],[6]])'
],
seealso: [
'matrix', 'matrixFromRows', 'matrixFromFunction', 'zeros'
]
}
22 changes: 22 additions & 0 deletions src/expression/embeddedDocs/function/matrix/matrixFromFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
export const matrixFromFunctionDocs = {
name: 'matrixFromFunction',
category: 'Matrix',
syntax: [
'math.matrixFromFunction(size, fn)',
'math.matrixFromFunction(size, fn, format)',
'math.matrixFromFunction(size, fn, format, datatype)',
'math.matrixFromFunction(size, format, fn)',
'math.matrixFromFunction(size, format, datatype, fn)'
],
description: 'Create a matrix by evaluating a generating function at each index.',
examples: [
'f(I) = I[1] - I[2]',
'matrixFromFunction([3,3], f)',
'g(I) = I[1] - I[2] == 1 ? 4 : 0',
'matrixFromFunction([100, 100], "sparse", g)',
'matrixFromFunction([5], random)'
],
seealso: [
'matrix', 'matrixFromRows', 'matrixFromColumns', 'zeros'
]
}
16 changes: 16 additions & 0 deletions src/expression/embeddedDocs/function/matrix/matrixFromRows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
export const matrixFromRowsDocs = {
name: 'matrixFromRows',
category: 'Matrix',
syntax: [
'math.matrixFromRows(...arr)',
'math.matrixFromRows(row1, row2)',
'math.matrixFromRows(row1, row2, row3)'
],
description: 'Create a dense matrix from vectors as individual rows.',
examples: [
'matrixFromRows([1, 2, 3], [[4],[5],[6]])'
],
seealso: [
'matrix', 'matrixFromColumns', 'matrixFromFunction', 'zeros'
]
}
3 changes: 3 additions & 0 deletions src/factoriesAny.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ export { createBignumber } from './type/bignumber/function/bignumber.js'
export { createComplex } from './type/complex/function/complex.js'
export { createFraction } from './type/fraction/function/fraction.js'
export { createMatrix } from './type/matrix/function/matrix.js'
export { createMatrixFromFunction } from './function/matrix/matrixFromFunction.js'
export { createMatrixFromRows } from './function/matrix/matrixFromRows.js'
export { createMatrixFromColumns } from './function/matrix/matrixFromColumns.js'
export { createSplitUnit } from './type/unit/function/splitUnit.js'
export { createUnaryMinus } from './function/arithmetic/unaryMinus.js'
export { createUnaryPlus } from './function/arithmetic/unaryPlus.js'
Expand Down
1 change: 1 addition & 0 deletions src/function/matrix/flatten.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ const dependencies = ['typed', 'matrix']
export const createFlatten = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix }) => {
/**
* Flatten a multi dimensional matrix into a single dimensional matrix.
* It is guaranteed to always return a clone of the argument.
*
* Syntax:
*
Expand Down
115 changes: 115 additions & 0 deletions src/function/matrix/matrixFromColumns.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
import { factory } from '../../utils/factory.js'

const name = 'matrixFromColumns'
const dependencies = ['typed', 'matrix', 'flatten', 'size']

export const createMatrixFromColumns = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, flatten, size }) => {
/**
* Create a dense matrix from vectors as individual columns.
* If you pass row vectors, they will be transposed (but not conjugated!)
*
* Syntax:
*
* math.matrixFromColumns(...arr)
* math.matrixFromColumns(col1, col2)
* math.matrixFromColumns(col1, col2, col3)
*
* Examples:
*
* math.matrixFromColumns([1, 2, 3], [[4],[5],[6]])
* math.matrixFromColumns(...vectors)
*
* See also:
*
* matrix, matrixFromRows, matrixFromFunction, zeros
*
* @param {...Array | ...Matrix} cols
* @return { number[][] | Matrix } if at least one of the arguments is an array, an array will be returned
*/
return typed(name, {
'...Array': function (arr) {
return _createArray(arr)
},
'...Matrix': function (arr) {
return _createMatrix(arr)
}
})

function _createArray (arr) {
if (arr.length === 0) throw new TypeError('At least one column is needed to construct a matrix.')
const N = checkVectorTypeAndReturnLength(arr[0])

// create an array with empty rows
const result = []
for (let i = 0; i < N; i++) {
result[i] = []
}

// loop columns
for (const col of arr) {
const colLength = checkVectorTypeAndReturnLength(col)

if (colLength !== N) {
throw new TypeError('The vectors had different length: ' + (N | 0) + ' ≠ ' + (colLength | 0))
}

const f = flatten(col)

// push a value to each row
for (let i = 0; i < N; i++) {
result[i].push(f[i])
}
}

return result
}

function _createMatrix (arr) {
if (arr.length === 0) throw new TypeError('At least one column is needed to construct a matrix.')
const N = checkVectorTypeAndReturnLength(arr[0])

// create an array with empty rows
const data = []
for (let i = 0; i < N; i++) {
data[i] = []
}

// loop columns
for (const col of arr) {
const colLength = checkVectorTypeAndReturnLength(col)

if (colLength !== N) {
throw new TypeError('The vectors had different length: ' + (N | 0) + ' ≠ ' + (colLength | 0))
}

// push a value to each row
let i = 0
col.forEach( value => data[i++].push(value) )
}

return matrix(data)
}

function checkVectorTypeAndReturnLength (vec) {
let s
if (Array.isArray(vec)) {
s = size(vec)
} else {
s = vec.size()
}

if (s.length === 1) { // 1D vector
return s[0]
} else if (s.length === 2) { // 2D vector
if (s[0] === 1) { // row vector
return s[1]
} else if (s[1] === 1) { // col vector
return s[0]
} else {
throw new TypeError('At least one of the arguments is not a vector.')
}
} else {
throw new TypeError('Only one- or two-dimensional vectors are supported.')
}
}
})
63 changes: 63 additions & 0 deletions src/function/matrix/matrixFromFunction.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import { factory } from '../../utils/factory.js'

const name = 'matrixFromFunction'
const dependencies = ['typed', 'matrix', 'isZero']

export const createMatrixFromFunction = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, isZero }) => {
/**
* Create a matrix by evaluating a generating function at each index.
*
* Syntax:
*
* math.matrixFromFunction(size, fn)
* math.matrixFromFunction(size, fn, format)
* math.matrixFromFunction(size, fn, format, datatype)
* math.matrixFromFunction(size, format, fn)
* math.matrixFromFunction(size, format, datatype, fn)
*
* Examples:
*
* math.matrixFromFunction([3,3], i => i[0] - i[1]) // an antisymmetric matrix
* math.matrixFromFunction([100, 100], 'sparse', i => i[0] - i[1] === 1 ? 4 : 0) // a sparse subdiagonal matrix
* math.matrixFromFunction([5], i => math.random()) // a random vector
*
* See also:
*
* matrix, zeros
*/
return typed(name, {
'Array | Matrix, function, string, string': function (size, fn, format, datatype) {
return _create(size, fn, format, datatype)
},
'Array | Matrix, function, string': function (size, fn, format) {
return _create(size, fn, format)
},
'Array | Matrix, function': function (size, fn) {
return _create(size, fn, 'dense')
},
'Array | Matrix, string, function': function (size, format, fn) {
return _create(size, fn, format)
},
'Array | Matrix, string, string, function': function (size, format, datatype, fn) {
return _create(size, fn, format, datatype)
}
})

function _create (size, fn, format, datatype) {
let m
if (datatype !== undefined) {
m = matrix(format, datatype)
} else {
m = matrix(format)
}

m.resize(size)
m.forEach(function(_, index) {
const val = fn(index)
if (isZero(val)) return
m.set(index, val)
})

return m
}
})
100 changes: 100 additions & 0 deletions src/function/matrix/matrixFromRows.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { factory } from '../../utils/factory.js'

const name = 'matrixFromRows'
const dependencies = ['typed', 'matrix', 'flatten', 'size']

export const createMatrixFromRows = /* #__PURE__ */ factory(name, dependencies, ({ typed, matrix, flatten, size }) => {
/**
* Create a dense matrix from vectors as individual rows.
* If you pass column vectors, they will be transposed (but not conjugated!)
*
* Syntax:
*
* math.matrixFromRows(...arr)
* math.matrixFromRows(row1, row2)
* math.matrixFromRows(row1, row2, row3)
*
* Examples:
*
* math.matrixFromRows([1, 2, 3], [[4],[5],[6]])
* math.matrixFromRows(...vectors)
*
* See also:
*
* matrix, matrixFromColumns, matrixFromFunction, zeros
*
* @param {...Array | ...Matrix} rows
* @return { number[][] | Matrix } if at least one of the arguments is an array, an array will be returned
*/
return typed(name, {
'...Array': function (arr) {
return _createArray(arr)
},
'...Matrix': function (arr) {
return _createMatrix(arr)
}
})

function _createArray (arr) {
if (arr.length === 0) throw new TypeError('At least one row is needed to construct a matrix.')
const N = checkVectorTypeAndReturnLength(arr[0])

const result = []
for (const row of arr) {
const rowLength = checkVectorTypeAndReturnLength(row)

if (rowLength !== N) {
throw new TypeError('The vectors had different length: ' + (N | 0) + ' ≠ ' + (rowLength | 0))
}

result.push(flatten(row))
}

return result
}

function _createMatrix (arr) {
if (arr.length === 0) throw new TypeError('At least one row is needed to construct a matrix.')
const N = checkVectorTypeAndReturnLength(arr[0])

const data = []
for (const row of arr) {
const rowLength = checkVectorTypeAndReturnLength(row)

if (rowLength !== N) {
throw new TypeError('The vectors had different length: ' + (N | 0) + ' ≠ ' + (rowLength | 0))
}

if (row.storage() === 'dense') {
data.push(flatten(row._data))
} else {
data.push(flatten(row.toArray()))
}
}

return matrix(data)
}

function checkVectorTypeAndReturnLength (vec) {
let s
if (Array.isArray(vec)) {
s = size(vec)
} else {
s = vec.size()
}

if (s.length === 1) { // 1D vector
return s[0]
} else if (s.length === 2) { // 2D vector
if (s[0] === 1) { // row vector
return s[1]
} else if (s[1] === 1) { // col vector
return s[0]
} else {
throw new TypeError('At least one of the arguments is not a vector.')
}
} else {
throw new TypeError('Only one- or two-dimensional vectors are supported.')
}
}
})
Loading