matrix-ts
fp-ts style mathematics library featuring: linear algebra, numerical methods, polynomials, and statistics
Table of Contents
- Install
- Typescript Compatibility
- Documentation
- Possible future additions
- Data types
- Typeclasses
- Examples
- Advanced Examples
Install
Uses fp-ts
as a peer dependency. Read more about peer dependencies at nodejs.org.
Yarn
yarn add fp-ts @jacob-alford/matrix-ts
NPM
npm install fp-ts @jacob-alford/matrix-ts
Typescript Compatibility
This library depends on fp-ts version ^2.9.6
(or major versions above 2.9.6
, and below 3.0.0
), and thus requires typescript version 3.5+.
Documentation
Possible future additions
- Add Cholesky Decomposition
Add QR Decomposition(Added in 1.1.0)- Add SVD Decomposition
- Add linear interpolation
- Add regression / multi-regression
- Add PCA
- Add factor analysis
- Add CCA
Data types
integer.ts
– An integral data type with typeclass instancesrational.ts
– A fractional data type with typeclass instancesnumber.ts
– Typeclass instances fornumber
complex.ts
– Complex data type with typeclass instancesquaternion.ts
– Quaternion data type with typeclass instancesComputation.ts
– Either with loggingMatrix.ts
– A type-level constrained matrix typeDecomposition.ts
– Numerical Linear algebraPolynomial.ts
– An array without trailing zerosVector.ts
– A type-level constrained vector typeinfix.ts
– A constructor for polish/reverse polish and infix notation for particular typeclassesMultivariate.ts
– Means / covariances / correlations of multivariable samplingUnivariate.ts
– Means variance / covariance / correlation of univariate and bivariate sampling
Typeclasses
Iso.ts
– Generic isomorphisms with compositionAutomorphism.ts
– Generic automorphisms with compositionAbelianGroup
– Commutative groupCommutativeRing
– A commutative ringLeftModule
– Left scalar multiplicationRightModule
– Right scalar multiplationBimodule
– Left and Right scalar multiplication
Examples
Add fractions
src/__tests__/examples.test.ts
import * as O from 'fp-ts/Option'
import * as Q from '@jacob-alford/matrix-ts/rational'
import * as Int from '@jacob-alford/matrix-ts/integer'
it('adds fractions', () => {
const { _ } = Q
const a = Q.of(Int.fromNumber(1), Int.fromNumber(2))
const b = Q.of(Int.fromNumber(1), Int.fromNumber(3))
const c = pipe(
O.Do,
O.apS('a', a),
O.apS('b', b),
O.map(({ a, b }) => _(a, '+', b))
)
const expected = Q.of(Int.fromNumber(5), Int.fromNumber(6))
expect(c).toStrictEqual(expected)
})
Vector dot product
src/__tests__/examples.test.ts
import * as N from '@jacob-alford/matrix-ts/number'
import * as V from '@jacob-alford/matrix-ts/Vector'
it('dots two vectors', () => {
const a = V.fromTuple([1, 2, 3, 4, 5, 6])
const b = V.fromTuple([4, 5, 6, 7, 8, 9])
expect(N.dot(a, b)).toBe(154)
})
Vector cross product
src/__tests__/examples.test.ts
import * as N from '@jacob-alford/matrix-ts/number'
import * as V from '@jacob-alford/matrix-ts/Vector'
it('crosses two vectors', () => {
const a = V.fromTuple([0, 2, 1])
const b = V.fromTuple([3, -1, 0])
expect(N.cross(a, b)).toStrictEqual(V.fromTuple([1, 3, -6]))
})
Multiplies two Integer Polynomials
src/__tests__examples.test.ts
import * as Poly from '@jacob-alford/matrix-ts/Polynomial'
import * as Int from '@jacob-alford/matrix-ts/integer'
it('multiples two polynomials', () => {
const fromArr = Poly.fromCoefficientArray(Int.Eq, Int.EuclideanRing)
const mul = Poly.mul(Int.Eq, Int.EuclideanRing)
const _ = Int.fromNumber
const { show } = Poly.getShow('x')(
Int.Show,
a => a === 0,
a => a === 1
)
// x + x^2
const p1 = fromArr([_(0), _(1), _(1)])
// 1 + x^4
const p2 = fromArr([_(1), _(0), _(0), _(0), _(1)])
// Expected: x + x^2 + x^5 + x^6
const result = mul(p1, p2)
expect(show(result)).toBe('x + x^2 + x^5 + x^6')
})
Advanced Examples
Gaussian Elimination with Partial Pivoting (LUP)
src/__tests__/Decomposition.test.ts
import * as D from '@jacob-alford/matrix-ts/Decomposition'
import * as M from '@jacob-alford/matrix-ts/Matrix'
import * as V from '@jacob-alford/matrix-ts/Vector'
it('solves a system of equations', () => {
const A = M.fromNestedTuples([
[2, 10, 8, 8, 6],
[1, 4, -2, 4, -1],
[0, 2, 3, 2, 1],
[3, 8, 3, 10, 9],
[1, 4, 1, 2, 1],
])
const [output] = D.LUP(A)
if (E.isLeft(output)) {
throw new Error('Unexpected result')
}
const { solve } = output.right
const b = V.fromTuple([52, 14, 12, 51, 15])
const c = V.fromTuple([50, 4, 12, 48, 12])
const x_b = solve(b)
const x_c = solve(c)
const expectedX_b = V.fromTuple([1, 2, 1, 2, 1])
const expectedX_c = V.fromTuple([2, 1, 2, 1, 2])
// ... assertions
})
it('returns a factorized matrix', () => {
const [output] = D.LUP(A)
if (E.isLeft(output)) {
throw new Error('Unexpected result')
}
const {
result: [L, U, P],
} = output.right
const expectedL = M.fromNestedTuples([
[1, 0, 0, 0, 0],
[2 / 3, 1, 0, 0, 0],
[1 / 3, 2 / 7, 1, 0, 0],
[1 / 3, 2 / 7, 4 / 11, 1, 0],
[0, 3 / 7, -1 / 11, -4 / 5, 1],
])
const expectedU = M.fromNestedTuples([
[3, 8, 3, 10, 9],
[0, 14 / 3, 6, 4 / 3, 0],
[0, 0, -33 / 7, 2 / 7, -4],
[0, 0, 0, -20 / 11, -6 / 11],
[0, 0, 0, 0, 1 / 5],
])
// ... assertions
})
Least Squares of an overdetermined system using QR Decomposition
src/__tests__/Decomposition.test.ts
it('solves a least squares problem', () => {
const A_ = M.fromNestedReadonlyArrays(
7,
3
)([
[1, -1, 1],
[1, -0.75, 0.75 ** 2],
[1, -0.5, 0.25],
[1, 0, 0],
[1, 0.25, 0.125],
[1, 0.5, 0.25],
[1, 0.75, 0.75 ** 2],
])
const A = pipe(
A_,
C.fromOption(() => 'Unexpected result'),
C.getOrThrowS
)
const { solve } = C.getOrThrowS(QR(A))
const [, x] = C.getOrThrowS(
solve(V.fromTuple([1, 0.8125, 0.75, 1, 1.3125, 1.75, 2.3125]))
)
const e = V.fromTuple([1, 1, 1])
// ... assertions
})
Covariance matrix of a multivariate sample
src/__tests__/Multivariate.test.ts
import * as RNEA from 'fp-ts/ReadonlyNonEmptyArray'
import * as V from '@jacob-alford/matrix-ts/Vector'
import * as Stat from '@jacob-alford/matrix-ts/Multivariate'
it('calculates a covariance matrix', () => {
const sample: Stat.MultivariateSample<3> = [
V.fromTuple([1, 2, 5]),
V.fromTuple([4, 1, 6]),
V.fromTuple([4, 0, 4]),
]
const cov = Stat.covariance(sample)
expect(cov).toStrictEqual([
[3, -3 / 2, 0],
[-3 / 2, 1, 1 / 2],
[0, 1 / 2, 1],
])
})
Automorphisms of polynomials
src/__tests__/examples.test.ts
import * as Poly from '@jacob-alford/matrix-ts/Polynomial'
import * as N from '@jacob-alford/matrix-ts/number'
it('differentiates and integrates polynomials', () => {
const { equals } = Poly.getPolynomialEq<number>(N.Eq)
const L = N.getDifferentialAutomorphism(1)
const thereAndBack = flow(L.get, L.reverseGet)
const hereAndThere = flow(L.reverseGet, L.get)
const a = Poly.fromCoefficientArray(N.Eq, N.Field)([1, 2, 3])
expect(equals(thereAndBack(a), a)).toBe(true)
expect(equals(hereAndThere(a), a)).toBe(true)
})
Automorphisms of matricies
src/__tests__/examples.test.ts
import * as Auto from '@jacob-alford/matrix-ts/Automorphism'
import * as N from '@jacob-alford/matrix-ts/number'
import * as V from '@jacob-alford/matrix-ts/Vector'
it('rotates a 2d vector and back 270 degrees', () => {
const rotate90Degrees = N.get2dRotation(Math.PI / 2)
const rotate180Degres = N.get2dRotation(Math.PI)
const T = Auto.compose(rotate90Degrees, rotate180Degres)
const initial = V.fromTuple([1, 0])
const rotated = T.get(initial)
const expected = V.fromTuple([0, -1])
const reversed = T.reverseGet(rotated)
// ... assertions
})
it('rotates a 3d vector and back along three axies', () => {
const rotateX45 = N.get3dXRotation(Math.PI / 4)
const rotateY180 = N.get3dYRotation(Math.PI)
const rotateZ90 = N.get3dZRotation(Math.PI / 2)
const T = Auto.compose(rotateX45, Auto.compose(rotateY180, rotateZ90))
const initial = V.fromTuple([0, 0, 1])
const rotated = T.get(initial)
const reversed = T.reverseGet(rotated)
const expected = V.fromTuple([1 / Math.sqrt(2), 0, -1 / Math.sqrt(2)])
// ... assertions
})
Quaternion automorphisms
src/__tests__/examples.test.ts
import * as H from '@jacob-alford/matrix-ts/quaternion'
import * as V from '@jacob-alford/matrix-ts/Vector'
it('rotates a 3d vector using quaternions', () => {
const T = H.getRotationAutomorphism(
// Around axis:
V.fromTuple([1, 1, 1]),
// By angle:
(2 * Math.PI) / 3
)
const initial = V.fromTuple([1, 0, 0])
const rotated = T.get(initial)
const reversed = T.reverseGet(rotated)
const expected = V.fromTuple([0, 1, 0])
// ... assertions
})