2024-05-22 08:46:14 +02:00

178 lines
5.6 KiB
JavaScript
Raw Permalink 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 { approxDeepEqual } 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
approxDeepEqual(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_
approxDeepEqual(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_
approxDeepEqual(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
approxDeepEqual(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 approxDeepEqual(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)^{+}')
})
})