Chris Chudzicki 13a3d4c198
Linting: StandardJS for src/, test/; Prettier for types/ (#2544)
* setup linting with eslint-config-standard, prettier

* [autofix] npm run lint -- --fix with new setup

* [manual] fix types/ directory errors

* [manual] fix linting errors in test/ directory

* [manual] fix single linting error in src/

* revert ts-expect-error comment change

* error on .only in mocha tests

* fix test description typo

* move some short objects to single line

* add and gitignore eslintcache

* individually suppress ts any

* set --max-warnings to 0

* extract matrices to constants

* update ts-expect-error comments
2022-04-29 12:04:01 +02:00

177 lines
5.6 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// test pinv
import assert from 'assert'
import approx from '../../../../tools/approx.js'
import math from '../../../../src/defaultInstance.js'
const pinv = math.pinv
/**
* Tests whether `A_` is MoorePenrose inverse of `A`.
*
* @param {Matrix | Array} A A two dimensional matrix or array.
* @param {Matrix | Array} A_ A two dimensional matrix or array.
*
* @return {Boolean} Returns true if `A_` is a valid MoorePenrose inverse of `A`
*/
function assertValidPinv (A, A_) {
const Asize = math.size(A).valueOf()
const rows = Asize[0]
const cols = Asize[1]
// sizes match
assert.deepStrictEqual(math.size(A_).valueOf(), [cols, rows])
// A A_ A = A
approx.deepEqual(math.add(math.multiply(A, math.multiply(A_, A)), math.Complex(1, 1)).valueOf(), math.add(A, math.Complex(1, 1)).valueOf())
// A_ A A_ = A_
approx.deepEqual(math.add(math.multiply(A_, math.multiply(A, A_)), math.Complex(1, 1)).valueOf(), math.add(A_, math.Complex(1, 1)).valueOf())
// (A A_)* = A A_
approx.deepEqual(math.add(math.ctranspose(math.multiply(A, A_)), math.Complex(1, 1)).valueOf(), math.add(math.multiply(A, A_), math.Complex(1, 1)).valueOf())
// (A_ A)* = A_ A
approx.deepEqual(math.add(math.ctranspose(math.multiply(A_, A)), math.Complex(1, 1)).valueOf(), math.add(math.multiply(A_, A), math.Complex(1, 1)).valueOf())
}
describe('pinv', function () {
function check (A, A_, strict = false) {
const pinvA = pinv(A)
if (A_) {
if (strict) assert.deepStrictEqual(pinvA, A_)
else approx.deepEqual(pinvA, A_)
}
switch (math.size(A).valueOf().length) {
case 0:
assertValidPinv([[A]], [[pinvA]])
break
case 1:
assertValidPinv(math.reshape(A, [1, -1]), math.reshape(pinvA, [-1, 1]))
break
case 2:
assertValidPinv(A, pinvA)
break
default:
assert.ok(math.size(A).valueOf().length < 3)
break
}
}
it('should return the inverse of a number', function () {
check(4, 1 / 4, true)
check(math.bignumber(4), math.bignumber(1 / 4), true)
})
it('should return the inverse of a matrix with just one value', function () {
check([4], [1 / 4], true)
check([[4]], [[1 / 4]], true)
})
it('should return the inverse for each element in an array', function () {
check([4], [1 / 4], true)
check([[4]], [[1 / 4]], true)
check([
[1, 4, 7],
[3, 0, 5],
[-1, 9, 11]
], [
[5.625, -2.375, -2.5],
[4.75, -2.25, -2],
[-3.375, 1.625, 1.5]
])
check([
[2, -1, 0],
[-1, 2, -1],
[0, -1, 2]
], [
[3 / 4, 1 / 2, 1 / 4],
[1 / 2, 1, 1 / 2],
[1 / 4, 1 / 2, 3 / 4]
])
check([
[1, 0, 0],
[0, 0, 1],
[0, 1, 0]
], [
[1, 0, 0],
[0, 0, 1],
[0, 1, 0]
])
check([
[1, 0, 0],
[0, -1, 1],
[0, 0, 1]
], [
[1, 0, 0],
[0, -1, 1],
[0, 0, 1]
])
})
it('should return the inverse for each element in a matrix', function () {
check(math.matrix([4]), math.matrix([1 / 4]), true)
check(math.matrix([[4]]), math.matrix([[1 / 4]]), true)
check(math.matrix([[4]], 'sparse'), math.matrix([[1 / 4]], 'sparse'), true)
check(math.matrix([[1, 2], [3, 4]], 'sparse'), math.matrix([[-2, 1], [1.5, -0.5]], 'sparse'), true)
})
it('should return the MoorePenrose inverse of complex matrices', function () {
check(
math.evaluate(`[
[0.4032 + 0.0876i, 0.1678 + 0.0390i, 0.5425 + 0.5118i],
[0.3174 + 0.3352i, 0.9784 + 0.4514i, -0.4416 - 1.3188i],
[0.4008 - 0.0504i, 0.0979 - 0.2558i, 0.2983 + 0.7800i]
]`)
// , math.evaluate(`[
// [0.4318 - 0.1398i, 0.2869 - 0.1360i, 0.3897 - 0.0370i],
// [0.3507 - 0.0725i, 0.4354 - 0.1329i, 0.2877 + 0.0565i],
// [0.3116 - 0.2626i, 0.0042 + 0.2584i, 0.2388 - 0.2806i]
// ]`)
)
})
it('should return the MoorePenrose inverse of non-square matrices', function () {
check([[0, 0]], [[0], [0]], true)
check([1, 2, 3], [1 / 14, 2 / 14, 3 / 14])
check([[1, 2, 3], [4, 5, 6]], [[-17 / 18, 8 / 18], [-2 / 18, 2 / 18], [13 / 18, -4 / 18]])
check([[1, 4], [2, 5], [3, 6]], [[-17 / 18, -2 / 18, 13 / 18], [8 / 18, 2 / 18, -4 / 18]])
check([
[64, 2, 3, 61, 60, 6],
[9, 55, 54, 12, 13, 51],
[17, 47, 46, 20, 21, 43],
[40, 26, 27, 37, 36, 30],
[32, 34, 35, 29, 28, 38],
[41, 23, 22, 44, 45, 19],
[49, 15, 14, 52, 53, 11],
[8, 58, 59, 5, 4, 62]
])
})
it('should throw an error in case of multi dimensional matrices', function () {
assert.throws(function () { pinv([[[1, 2, 3], [4, 5, 6]]]) }, /Matrix must be two dimensional/)
})
it('should return the MoorePenrose inverse of non-invertable matrices', function () {
check([[0]], [[0]], true)
check([[1, 0], [0, 0]], [[1, 0], [0, 0]])
check([[1, 1, 1], [1, 0, 0], [0, 0, 0]], [[0, 1, 0], [0.5, -0.5, 0], [0.5, -0.5, 0]])
})
it('should throw an error in case of wrong number of arguments', function () {
assert.throws(function () { pinv() }, /TypeError: Too few arguments/)
assert.throws(function () { pinv([], []) }, /TypeError: Too many arguments/)
})
it('should throw an error in case of invalid type of arguments', function () {
assert.throws(function () { math.concat(pinv(new Date())) }, /TypeError: Unexpected type of argument/)
})
it('should LaTeX pinv', function () {
const expression = math.parse('pinv([[1,2],[3,4]])')
assert.strictEqual(expression.toTex(), '\\left(\\begin{bmatrix}1&2\\\\3&4\\end{bmatrix}\\right)^{+}')
})
})