npm package: @kkitahara/linear-algebra
Description: ECMAScript modules for exactly manipulating vectors and matrices of which elements are real or complex numbers of the form (p / q)sqrt(b).
Author: Koichi Kitahara
Latest version: 2.1.3

[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
[![license](https://img.shields.io/npm/l/@kkitahara/linear-algebra.svg)](https://www.apache.org/licenses/LICENSE-2.0)
[![pipeline status](https://gitlab.com/kkitahara/linear-algebra/badges/v2.1.3/pipeline.svg)](https://gitlab.com/kkitahara/linear-algebra/commits/v2.1.3)
[![coverage report](https://gitlab.com/kkitahara/linear-algebra/badges/v2.1.3/coverage.svg)](https://gitlab.com/kkitahara/linear-algebra/commits/v2.1.3)
[![version](https://img.shields.io/npm/v/@kkitahara/linear-algebra/latest.svg)](https://www.npmjs.com/package/@kkitahara/linear-algebra)
[![bundle size](https://img.shields.io/bundlephobia/min/@kkitahara/linear-algebra.svg)](https://www.npmjs.com/package/@kkitahara/linear-algebra)
[![downloads per week](https://img.shields.io/npm/dw/@kkitahara/linear-algebra.svg)](https://www.npmjs.com/package/@kkitahara/linear-algebra)
[![downloads per month](https://img.shields.io/npm/dm/@kkitahara/linear-algebra.svg)](https://www.npmjs.com/package/@kkitahara/linear-algebra)
[![downloads per year](https://img.shields.io/npm/dy/@kkitahara/linear-algebra.svg)](https://www.npmjs.com/package/@kkitahara/linear-algebra)
[![downloads total](https://img.shields.io/npm/dt/@kkitahara/linear-algebra.svg)](https://www.npmjs.com/package/@kkitahara/linear-algebra)

# LinearAlgebra

ECMAScript modules for exactly manipulating vectors and matrices
of which elements are real or complex numbers of the form
(*p* / *q*)sqrt(*b*),
where *p* is an integer, *q* is a positive (non-zero) integer,
and *b* is a positive, square-free integer.

## Installation
```
npm install @kkitahara/linear-algebra @kkitahara/complex-algebra @kkitahara/real-algebra
```

## Examples
### Complex linear algebra
```javascript
import { ExactRealAlgebra as RealAlgebra } from '@kkitahara/real-algebra'
import { ComplexAlgebra } from '@kkitahara/complex-algebra'
import { LinearAlgebra } from '@kkitahara/linear-algebra'
let r = new RealAlgebra()
let c = new ComplexAlgebra(r)
let l = new LinearAlgebra(c)
let m1, m2, m3
```
Generate a new matrix (since v2.0.0)
```javascript
m1 = l.$(1, 0, 0, 1)
m1.toString() // '(1, 0, 0, 1)'

m1 = l.$(r.$(1, 2, 5), c.$(0, 1), c.$(0, -1), 1)
m1.toString() // '((1 / 2)sqrt(5), i(1), i(-1), 1)'

// Some Array methods can be used
m1 = l.$(r.$(1, 2, 5), c.$(0, 1), c.$(0, -1), 1)
m1.push(c.$(3))
m1.toString() // '((1 / 2)sqrt(5), i(1), i(-1), 1, 3)'
```
Set and get the dimension
```javascript
m1 = l.$(1, 0, 0, 1)

// 2 x 2 matix
m1.setDim(2, 2)
// number of rows
m1.getDim()[0] // 2
// number of columns
m1.getDim()[1] // 2
// elements are stored in row-major order
m1.toString() // '(1, 0,\n 0, 1)'

// 1 x 4 matix
m1.setDim(1, 4)
m1.getDim()[0] // 1
m1.getDim()[1] // 4
m1.toString() // '(1, 0, 0, 1)'

// 4 x 1 matix
m1.setDim(4, 1)
m1.getDim()[0] // 4
m1.getDim()[1] // 1
m1.toString() // '(1,\n 0,\n 0,\n 1)'

// 3 x 1 matix (inconsistent dimension)
m1.setDim(3, 1)
// throws an Error when getDim is called
m1.getDim() // Error

// 2 x 0 (0 means auto)
m1.setDim(2, 0)
m1.getDim()[0] // 2
m1.getDim()[1] // 2

// 0 x 1 (0 means auto)
m1.setDim(0, 1)
m1.getDim()[0] // 4
m1.getDim()[1] // 1

// 0 x 0 (this is an adaptive matrix, since v2.0.0)
m1.setDim(0, 0)
m1.isAdaptive() // true
m1.getDim() // Error

// matrices are adaptive by default
m1 = l.$(1, 0, 0, 1)
m1.isAdaptive() // true
```
:warning: matrix elements are stored in row-major order.

Copy (generate a new object)
```javascript
m1 = l.$(1, 0, 0, 1).setDim(2, 2)
m2 = l.copy(m1)
m2.toString() // '(1, 0,\n 0, 1)'
m2.getDim()[0] // 2
m2.getDim()[1] // 2
```
Equality
```javascript
m1 = l.$(1, 0, 0, 1).setDim(2, 2)
m2 = l.$(1, 0, 0, 1).setDim(2, 2)
m3 = l.$(1, 0, 0, -1).setDim(2, 2)
l.eq(m1, m2) // true
l.eq(m1, m3) // false

// matrices of different dimension are considered to be not equal
m1 = l.$(1, 0, 0, 1).setDim(2, 2)
m2 = l.$(1, 0, 0, 1).setDim(1, 4)
l.eq(m1, m2) // false

// here, `m1` is adaptive
m1 = l.$(1, 0, 0, 1)
m2 = l.$(1, 0, 0, 1).setDim(1, 4)
m3 = l.$(1, 0, 0, 1).setDim(4, 1)
l.eq(m1, m2) // true
l.eq(m1, m3) // true
l.eq(m2, m3) // false
```
Inequality
```javascript
m1 = l.$(1, 0, 0, 1).setDim(2, 2)
m2 = l.$(1, 0, 0, 1).setDim(2, 2)
m3 = l.$(1, 0, 0, -1).setDim(2, 2)
l.ne(m1, m2) // false
l.ne(m1, m3) // true

// matrices of different dimension are considered to be not equal
m1 = l.$(1, 0, 0, 1).setDim(2, 2)
m2 = l.$(1, 0, 0, 1).setDim(1, 4)
l.ne(m1, m2) // true

// here, `m1` is adaptive
m1 = l.$(1, 0, 0, 1)
m2 = l.$(1, 0, 0, 1).setDim(1, 4)
m3 = l.$(1, 0, 0, 1).setDim(4, 1)
l.ne(m1, m2) // false
l.ne(m1, m3) // false
l.ne(m2, m3) // true
```
isZero
```javascript
m1 = l.$(1, 0, 0, 1).setDim(2, 2)
m2 = l.$(0, 0, 0, 0).setDim(2, 2)
l.isZero(m1) // false
l.isZero(m2) // true
```
isInteger (since v1.1.0)
```javascript
m1 = l.$(1, r.$(4, 2), -3, 4).setDim(2, 2)
m2 = l.$(1, r.$(1, 2), -3, 4).setDim(2, 2)
l.isInteger(m1) // true
l.isInteger(m2) // false
```
Element-wise addition
```javascript
m1 = l.$(1, 2, 3, 4)
m2 = l.$(1, 3, 1, 3)
// new object is generated
m3 = l.add(m1, m2)
m3.toString() // '(2, 5, 4, 7)'
```
In-place element-wise addition
```javascript
m1 = l.$(1, 2, 3, 4)
m2 = l.$(1, 3, 1, 3)
// new object is not generated
m1 = l.iadd(m1, m2)
m1.toString() // '(2, 5, 4, 7)'
```
Element-wise subtraction
```javascript
m1 = l.$(1, 2, 3, 4)
m2 = l.$(1, 3, 1, 3)
// new object is generated
m3 = l.sub(m1, m2)
m3.toString() // '(0, -1, 2, 1)'
```
In-place element-wise subtraction
```javascript
m1 = l.$(1, 2, 3, 4)
m2 = l.$(1, 3, 1, 3)
// new object is not generated
m1 = l.isub(m1, m2)
m1.toString() // '(0, -1, 2, 1)'
```
Element-wise multiplication
```javascript
m1 = l.$(1, 2, 3, 4)
m2 = l.$(1, 3, 1, 3)
// new object is generated
m3 = l.mul(m1, m2)
m3.toString() // '(1, 6, 3, 12)'
```
In-place element-wise multiplication
```javascript
m1 = l.$(1, 2, 3, 4)
m2 = l.$(1, 3, 1, 3)
// new object is not generated
m1 = l.imul(m1, m2)
m1.toString() // '(1, 6, 3, 12)'
```
Element-wise division
```javascript
m1 = l.$(1, 2, 3, 4)
m2 = l.$(1, 3, 1, 3)
// new object is generated
m3 = l.div(m1, m2)
m3.toString() // '(1, 2 / 3, 3, 4 / 3)'
```
In-place element-wise division
```javascript
m1 = l.$(1, 2, 3, 4)
m2 = l.$(1, 3, 1, 3)
// new object is not generated
m1 = l.idiv(m1, m2)
m1.toString() // '(1, 2 / 3, 3, 4 / 3)'
```
Scalar multiplication
```javascript
m1 = l.$(1, 2, 3, 4)
// new object is generated
m2 = l.smul(m1, r.$(1, 2))
m2.toString() // '(1 / 2, 1, 3 / 2, 2)'
```
In-place scalar multiplication
```javascript
m1 = l.$(1, 2, 3, 4)
// new object is not generated
m1 = l.ismul(m1, r.$(1, 2))
m1.toString() // '(1 / 2, 1, 3 / 2, 2)'
```
Scalar multiplication by -1
```javascript
m1 = l.$(1, 2, 3, 4)
// new object is generated
m2 = l.neg(m1)
m2.toString() // '(-1, -2, -3, -4)'
```
In-place scalar multiplication by -1
```javascript
m1 = l.$(1, 2, 3, 4)
// new object is not generated
m1 = l.ineg(m1)
m1.toString() // '(-1, -2, -3, -4)'
```
Scalar division (since v2.0.0)
```javascript
m1 = l.$(1, 2, 3, 4)
// new object is generated
m2 = l.sdiv(m1, 2)
m2.toString() // '(1 / 2, 1, 3 / 2, 2)'
```
In-place scalar division (since v2.0.0)
```javascript
m1 = l.$(1, 2, 3, 4)
// new object is not generated
m1 = l.isdiv(m1, 2)
m1.toString() // '(1 / 2, 1, 3 / 2, 2)'
```
Complex conjugate
```javascript
m1 = l.$(1, c.$(0, 2), 3, c.$(0, 4))
// new object is generated
m2 = l.cjg(m1)
m2.toString() // '(1, i(-2), 3, i(-4))'
```
In-place evaluation of the complex conjugate
```javascript
m1 = l.$(1, c.$(0, 2), 3, c.$(0, 4))
// new object is not generated
m1 = l.icjg(m1)
m1.toString() // '(1, i(-2), 3, i(-4))'
```
Transpose
```javascript
m1 = l.$(1, 2, 3, 4).setDim(2, 2)
// new object is generated
m2 = l.transpose(m1)
m2.toString() // '(1, 3,\n 2, 4)'
```
In-place evaluation of the transpose
```javascript
m1 = l.$(1, 2, 3, 4).setDim(2, 2)
// new object is not generated
m1 = l.itranspose(m1)
m1.toString() // '(1, 3,\n 2, 4)'
```
Conjugate transpose (Hermitian transpose)
```javascript
m1 = l.$(1, c.$(0, 2), 3, c.$(0, 4)).setDim(2, 2)
// new object is generated
m2 = l.cjgTranspose(m1)
m2.toString() // '(1, 3,\n i(-2), i(-4))'
```
In-place evaluation of the conjugate transpose
```javascript
m1 = l.$(1, c.$(0, 2), 3, c.$(0, 4)).setDim(2, 2)
// new object is not generated
m1 = l.icjgTranspose(m1)
m1.toString() // '(1, 3,\n i(-2), i(-4))'
```
Dot product
```javascript
m1 = l.$(1, c.$(0, 2))
m2 = l.$(c.$(0, 3), 2)
l.dot(m1, m2).toString() // 'i(-1)'
```
Square of the absolute value (Frobenius norm)
```javascript
m1 = l.$(1, c.$(0, 2))
let a = l.abs2(m1)
a.toString() // '5'
// return value is not a complex number (but a real number)
a.re // undefined
a.im // undefined
```
Matrix multiplication
```javascript
m1 = l.$(1, 2, 3, 4).setDim(2, 2)
m2 = l.$(1, 3, 1, 3).setDim(2, 2)
m3 = l.mmul(m1, m2)
m3.toString() // '(3, 9,\n 7, 21)'
```
LU-factorisation
```javascript
m1 = l.$(1, 2, 3, 4).setDim(2, 2)
// new object is generated
m2 = l.lup(m1)
```
In-place LU-factorisation
```javascript
m1 = l.$(1, 2, 3, 4).setDim(2, 2)
// new object is not generated
m1 = l.ilup(m1)
```
Solving a linear equation
```javascript
m1 = l.$(1, 2, 3, 4).setDim(2, 2)
m2 = l.$(1, 2, 3, 4).setDim(2, 2)

// m1 m3 = m2, new object is generated
m3 = l.solve(l.lup(m1), m2)
m3.toString() // '(1, 0,\n 0, 1)'

// m3 m1 = m2, new object is generated
m3 = l.solve(m2, l.lup(m1))
m3.toString() // '(1, 0,\n 0, 1)'
```
Solving a linear equation in-place
```javascript
m1 = l.$(1, 2, 3, 4).setDim(2, 2)
m2 = l.$(1, 2, 3, 4).setDim(2, 2)

// m1 m3 = m2, new object is not generated
m2 = l.isolve(l.lup(m1), m2)
m2.toString() // '(1, 0,\n 0, 1)'

m2 = l.$(1, 2, 3, 4).setDim(2, 2)

// m3 m1 = m2, new object is not generated
m2 = l.isolve(m2, l.lup(m1))
m2.toString() // '(1, 0,\n 0, 1)'
```
Determinant
```javascript
m1 = l.$(1, 2, 3, 4).setDim(2, 2)
m2 = l.lup(m1)
// det method supports only LU-factorised matrices
let det = l.det(m2)
det.toString() // '-2'
```
JSON (stringify and parse)
```javascript
m1 = l.$(1, r.$(2, 3, 5), 3, c.$(0, r.$(4, 5, 3))).setDim(2, 2)
let str = JSON.stringify(m1)
m2 = JSON.parse(str, l.reviver)
l.eq(m1, m2) // true
```

### Real linear algebra
If complex numbers are not necessary, you can use RealAlgebra
instead of ComplexAlgebra.
```javascript
import { ExactRealAlgebra as RealAlgebra } from '@kkitahara/real-algebra'
import { LinearAlgebra } from '@kkitahara/linear-algebra'
let r = new RealAlgebra()
let l = new LinearAlgebra(r)
```

### Numerical real/complex linear algebra
You can work with built-in numbers if you use
```javascript
import { RealAlgebra } from '@kkitahara/real-algebra'
```
instead of ExactRealAlgebra.
See the documents of @kkitahara/real-algebra for more details.

### ESDoc documents
For more examples, see ESDoc documents:
```
cd node_modules/@kkitahara/linear-algebra
npm install --only=dev
npm run doc
```
and open `doc/index.html` in your browser.

## LICENSE
&copy; 2019 Koichi Kitahara  
[Apache 2.0](LICENSE)