feat: vector transpose and multiply

This commit is contained in:
Glen Whitney 2025-05-04 21:12:38 -07:00
parent f398454d59
commit ed66ea7772
6 changed files with 89 additions and 8 deletions

View file

@ -27,7 +27,11 @@ export const cbrt = plain(a => {
return negate ? -result : result
})
export const invert = plain(a => 1/a)
export const multiply = plain((a, b) => a * b)
export const multiply = [
plain((a, b) => a * b),
match([Undefined, NumberT], Returns(NumberT, () => NaN)),
match([NumberT, Undefined], Returns(NumberT, () => NaN))
]
export const negate = plain(a => -a)
export const sqrt = match(NumberT, (math, _N, strategy) => {

View file

@ -24,6 +24,21 @@ describe('Vector arithmetic functions', () => {
assert.deepStrictEqual(
add([[1, 2], [4, 2]], [0, -1]), [[1, 1], [4, 1]])
})
it('multiplies vectors and matrices', () => {
const mult = math.multiply
const pyth = [3, 4, 5]
assert.deepStrictEqual(mult(pyth, 2), [6, 8, 10])
assert.deepStrictEqual(mult(-3, pyth), [-9, -12, -15])
assert.strictEqual(mult(pyth, pyth), 50)
const mat23 = [[1, 2, 3], [-3, -2, -1]]
assert.deepStrictEqual(mult(mat23, pyth), [26, -22])
const mat32 = math.transpose(mat23)
assert.deepStrictEqual(mult(pyth, mat32), [26, -22])
assert.deepStrictEqual(mult(mat23, mat32), [[14, -10], [-10, 14]])
assert.deepStrictEqual(
mult(mat32, [[1, 2], [3, 4]]),
[[-8, -10], [-4, -4], [0, 2]])
})
it('negates a vector', () => {
assert.deepStrictEqual(math.negate([-3, 4, -5]), [3, -4, 5])
})

View file

@ -15,4 +15,11 @@ describe('Vector type functions', () => {
ReturnType(vec.resolve([NumberT, BooleanT])),
Vector(Unknown))
})
it('can transpose vectors and matrices', () => {
const tsp = math.transpose
assert.deepStrictEqual(tsp([3, 4, 5]), [[3], [4], [5]])
assert.deepStrictEqual(tsp([[1, 2], [3, 4]]), [[1, 3], [2, 4]])
assert.deepStrictEqual(
tsp([[1, 2, 3], [4, 5, 6]]), [[1, 4], [2, 5], [3, 6]])
})
})

View file

@ -1,4 +1,6 @@
import {promoteBinary, promoteUnary} from './helpers.js'
import {
distributeFirst, distributeSecond, promoteBinary, promoteUnary
} from './helpers.js'
import {Vector} from './Vector.js'
import {ReturnType} from '#core/Type.js'
@ -16,6 +18,39 @@ export const normsq = match(Vector, (math, V) => {
export const abs = promoteUnary('abs')
export const add = promoteBinary('add')
export const dotMultiply = promoteBinary('multiply')
export const multiply = [
distributeFirst('multiply'),
distributeSecond('multiply'),
match([Vector, Vector], (math, [V, W], strategy) => {
const VComp = V.Component
if (W.vectorDepth === 1) {
if (V.vectorDepth === 1) {
const eltWise = math.dotMultiply.resolve([V, W], strategy)
const sum = math.sum.resolve(ReturnType(eltWise))
return ReturnsAs(sum, (v, w) => sum(eltWise(v, w)))
}
const compMult = math.multiply.resolve([VComp, W], strategy)
return ReturnsAs(
Vector(ReturnType(compMult)),
(v, w) => v.map(f => compMult(f, w)))
}
const transpose = math.transpose.resolve(W, strategy)
const wrapV = V.vectorDepth === 1
const RowV = wrapV ? V : VComp
const rowMult = math.multiply.resolve([RowV, W.Component], strategy)
let RetType = Vector(ReturnType(rowMult))
if (!wrapV) RetType = Vector(RetType)
return ReturnsAs(RetType, (v, w) => {
if (wrapV) v = [v]
w = transpose(w)
let retval = v.map(vrow => w.map(wcol => rowMult(vrow, wcol)))
if (wrapV) retval = retval[0]
return retval
})
})
]
export const negate = promoteUnary('negate')
export const subtract = promoteBinary('subtract')

View file

@ -7,17 +7,25 @@ export const promoteUnary = name => match(Vector, (math, V, strategy) => {
return Returns(Vector(ReturnType(compOp)), v => v.map(elt => compOp(elt)))
})
export const promoteBinary = name => [
match([Vector, Any], (math, [V, E], strategy) => {
export const distributeFirst = name => match(
[Vector, Any],
(math, [V, E], strategy) => {
const compOp = math.resolve(name, [V.Component, E], strategy)
return Returns(
Vector(ReturnType(compOp)), (v, e) => v.map(f => compOp(f, e)))
}),
match([Any, Vector], (math, [E, V], strategy) => {
})
export const distributeSecond = name => match(
[Any, Vector],
(math, [E, V], strategy) => {
const compOp = math.resolve(name, [E, V.Component], strategy)
return Returns(
Vector(ReturnType(compOp)), (e, v) => v.map(f => compOp(e, f)))
}),
})
export const promoteBinary = name => [
distributeFirst(name),
distributeSecond(name),
match([Vector, Vector], (math, [V, W], strategy) => {
const VComp = V.Component
const WComp = W.Component

View file

@ -9,4 +9,16 @@ export const vector = match(Multiple(Any), (math, [TV]) => {
return Returns(Vector(CompType), v => v)
})
export const transpose = match(Vector, (_math, V) => {
const wrapV = V.vectorDepth === 1
const Mat = wrapV ? Vector(V) : V
return Returns(Mat, v => {
if (wrapV) v = [v]
const cols = v.length ? v[0].length : 0
const retval = []
for (let ix = 0; ix < cols; ++ix) {
retval.push(v.map(row => row[ix]))
}
return retval
})
})