Link Search Menu Expand Document

matrix-ts

fp-ts style mathematics library featuring: linear algebra, numerical methods, polynomials, and statistics

Table of Contents

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 instances
  • rational.ts – A fractional data type with typeclass instances
  • number.ts – Typeclass instances for number
  • complex.ts – Complex data type with typeclass instances
  • quaternion.ts – Quaternion data type with typeclass instances
  • Computation.ts – Either with logging
  • Matrix.ts – A type-level constrained matrix type
  • Decomposition.ts – Numerical Linear algebra
  • Polynomial.ts – An array without trailing zeros
  • Vector.ts – A type-level constrained vector type
  • infix.ts – A constructor for polish/reverse polish and infix notation for particular typeclasses
  • Multivariate.ts – Means / covariances / correlations of multivariable sampling
  • Univariate.ts – Means variance / covariance / correlation of univariate and bivariate sampling

Typeclasses

  • Iso.ts – Generic isomorphisms with composition
  • Automorphism.ts – Generic automorphisms with composition
  • AbelianGroup – Commutative group
  • CommutativeRing – A commutative ring
  • LeftModule – Left scalar multiplication
  • RightModule – Right scalar multiplation
  • Bimodule – 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
})