Public
Edited
Feb 12, 2024
Insert cell
Insert cell
math.format(mat.evaluate(`[[0],[10],[20],[30]]+[1, 2, 3]`))
Insert cell
math.format(mat.add(mat.matrix([[0],[10],[20],[30]]), mat.matrix([1, 2, 3])))
Insert cell
Insert cell
Insert cell
{
// broadcasting examples from numpy
let results = []

results.push(
math.equal(
broadcast_shapes([8,1,6,1],[7,1,5]),
math.matrix([8,7,6,5])).toArray()
)

results.push(
math.equal(
broadcast_shapes([5,4],[1]),
math.matrix([5,4])).toArray()
)
results.push(
math.equal(
broadcast_shapes([5,4],[4]),
math.matrix([5,4])).toArray()
)

results.push(
math.equal(
broadcast_shapes([15,3,5],[15,1,5]),
math.matrix([15,3,5])).toArray()
)
results.push(
math.equal(
broadcast_shapes([15,3,5],[3,5]),
math.matrix([15,3,5])).toArray()
)

results.push(
math.equal(
broadcast_shapes([15,3,5],[3,1]),
math.matrix([15,3,5])).toArray()
)
return results
}
Insert cell
Insert cell
{
try{
return broadcast_shapes([3],[4]) // trailing dimensions do not match
}
catch(error){
return error.toString()
}
}
Insert cell
{
try {
return broadcast_shapes([2,1],[8,4,3]) // second from last dimension mismatched

} catch (error) {
return error.toString()
}
}
Insert cell
{
try {
return broadcast_shapes([4,3],[4])
} catch (error) {
return error.toString()
}
}
Insert cell
Insert cell
sizeAsArray = math.typed(
'sizeAsArray',
{
'Array': (x) => math.size(x),
'any': (x) => math.size(x).toArray()
})
Insert cell
isScalar([1])
Insert cell
isScalar(1)
Insert cell
isScalar = math.typed(
'isScalar',
{
'Array | Matrix': x => false,
'any': x => true
}
)
Insert cell
sizeOfScalar(2)
Insert cell
sizeOfScalar = (size) => math.sum(math.size(size)) == 0
Insert cell
Insert cell
broadcast_to(math.matrix([[0],[10],[20],[30]]), [4,3])
Insert cell
broadcast_to(
math.matrix([1,2,3]),
[4,3]
)
Insert cell
broadcast_to(
math.matrix([1]),
[4,3]
)
Insert cell
broadcast_to(
2,
[4,3]
)
Insert cell
Insert cell
//padLeft = (size, n, 1) => {math.resize()}
Insert cell
shapeToConcat = (Mat, dim) =>
math.resize(sizeAsArray(Mat).reverse(), [dim], 1).reverse()
Insert cell
math.sum(math.size([])) > 0
Insert cell
function broadcast_to(M, shape) {
// will broadcast multiple arrays
if (sizeOfScalar(shape))
return M

if(isScalar(M) & !sizeOfScalar(shape))
M = math.matrix([M])

const s = broadcast_shapes(sizeAsArray(M), shape) //even though the shape is already given, this checks for errors in broadcasting
const N = s.length
if(M.size().length < N)
M.reshape(shapeToConcat(M, N))

for (let i = 0; i < N ; ++i) {
if(((M.size()[i] == 1) | (M.size()[i] == 0)) & (s[i]>1))
M = math.concat(...Array(s[i]).fill(M), i)
}
return M
}
Insert cell
broadcast_to(math.matrix([1,2,3]), [4,3])
Insert cell
broadcast_matrices(math.matrix([1,2]),math.matrix([3]))
Insert cell
broadcast_matrices(
math.matrix([[0],[10],[20],[30]]),
math.matrix([1,2,3]),
)
Insert cell
math.format(
mat.evaluate(`
A = [1, 2; 3, 4; 5, 6];
A-[1,2]
# C = subtract(A, mean(A, 1)) # could yield [[-2, -2], [0, 0], [2, 2]]
`)
)
Insert cell
broadcast_shapes = math.typed(
'broadcast_shapes',
{'...Array': (shapes) => {
const dims = shapes.map(shape => shape.length)
const N = math.max(dims)
const shapes_array = shapes.map(
(shape, i) => dims[i] < N ? math.concat(
math.zeros(N - dims[i]).toArray(), // pads with zeros to the left
shape) : shape
)
const max_shapes = math.max(shapes_array,0)

shapes_array.forEach(
(shape, shapeID)=>{
shape.forEach(
(dim, dimID) => {
if ((dim < max_shapes[dimID]) & (dim > 1))
throw new Error(`shape missmatch: missmatch is found in arg ${shapeID} with shape (${shape}) not possible to broadcast dimension ${dimID} with size ${dim} to size ${max_shapes[dimID]}`)
}
)
}
)

return max_shapes
}
}
)
Insert cell
function broadcast_matrices(...matrices) {
// Broadcasts many arrays
if (matrices.every(matrix => isScalar(matrix))){
return matrices
}

const broadcastedMatrixSize = broadcast_shapes(...matrices.map(matrix => sizeAsArray(matrix)))
const N = broadcastedMatrixSize.length
const broadcasted_matrices =
matrices.map( matrix => {
let matrixSize
if(isScalar(matrix)){
matrix = [ matrix ]
}

matrixSize = sizeAsArray(matrix)

if(matrixSize.length < N){
matrix.reshape(shapeToConcat(matrix, N))
matrixSize = sizeAsArray(matrix)
}

broadcastedMatrixSize.forEach(
(size, dim) => {
if( matrixSize[dim] < size)
// the rules of broadcasting were already validated by broadcast_shapes
// thus matrix size could only be 0,1 or size
// if it's size then do noting
{
matrix = math.concat(...Array(size).fill(matrix), dim)
}
}
)
return matrix
})
return broadcasted_matrices
}
Insert cell
broadcast_matrices(math.matrix([1]),1)
Insert cell
Insert cell
Insert cell
function mapped(f, x) {
return isScalar(x) ? f(x) : math.map(x, f)
}
Insert cell
function mapLog(x, ...args) {
const base = args.length == 0 ? math.e : args[0]
return isScalar(x) ? math.log(x, base) : math.map(x, x => math.log(x, base))
}
Insert cell
mat = {
let mat = math.create()

mat.import({
add:(...Ms) => math.add(...broadcast_matrices(...Ms)),
subtract: (...Ms) => math.subtract(...broadcast_matrices(...Ms)),
dotMultiply: (...Ms) => math.multiply(...broadcast_matrices(...Ms)),
dotDivide: (...Ms) => math.divide(...broadcast_matrices(...Ms)),
exp: x => mapped(math.exp, x),
log: mapLog,
gamma: x=> mapped(math.gamma, x),
square: x=> mapped(math.square, x),
sin: x => mapped(math.sin, x),
cos: x => mapped(math.cos, x),
cube: x => mapped(math.cube, x),
//cbrt: x => mapped(math.cbrt, x),
cbrt: x => mapped(x2 => math.cbrt(x2), x), // this is a temporary fix as cbrt doesn't work with map
acos: x => mapped(math.acos, x),
acosh: x => mapped(math.acosh, x),
acot: x => mapped(math.acot, x),
acoth: x => mapped(math.acoth, x),
acsc: x => mapped(math.acsc, x),
acsch: x => mapped(math.acsch, x),
asec: x => mapped(math.asec, x),
asech: x => mapped(math.asech, x),
asin: x => mapped(math.asin, x),
asinh: x => mapped(math.asinh, x),
atan: x => mapped(math.atan, x),
atanh: x => mapped(math.atanh, x),
cosh: x => mapped(math.cosh, x),
cot: x => mapped(math.cot, x),
coth: x => mapped(math.coth, x),
csc: x => mapped(math.csc, x),
csch: x => mapped(math.csch, x),
sec: x => mapped(math.sec, x),
//'atan2(1:10, 1:10)', already works it hasn't lost it's vectorization
sech: x => mapped(math.sech, x),
sin: x => mapped(math.sin, x),
sinh: x => mapped(math.sinh, x),
tan: x => mapped(math.tan, x),
tanh: x => mapped(math.tanh, x),
},{override:true})
return mat
}
Insert cell
m = mat.parser()
Insert cell
math = require("mathjs")
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more