mirror of
https://github.com/josdejong/mathjs.git
synced 2025-12-08 19:46:04 +00:00
178 lines
5.6 KiB
JavaScript
178 lines
5.6 KiB
JavaScript
// 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 Moore–Penrose 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 Moore–Penrose 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 Moore–Penrose 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 Moore–Penrose 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 Moore–Penrose 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)^{+}')
|
||
})
|
||
})
|