feat: implement negative integer exponents for square matrices (#2517)

Resolves #2463

Co-authored-by: Glen Whitney <glen@studioinfinity.org>
This commit is contained in:
HanchaiN 2022-04-09 02:15:54 +07:00 committed by GitHub
parent 36e7e46289
commit 7480c11bf7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 51 additions and 7 deletions

View File

@ -187,5 +187,6 @@ Hjortur Jonasson <hjorturjonasson@gmail.com>
Abraham <abraham@abranhe.com>
Sam Estep <sam@samestep.com>
Sinan <43215895+SinanAkkoyun@users.noreply.github.com>
HanchaiN <85230240+HanchaiN@users.noreply.github.com>
# Generated by tools/update-authors.js

View File

@ -1,5 +1,9 @@
# History
# unpublished changes since 10.4.3:
- Implement #2463: allow negative integer powers of invertible square matrices
(#2517). Thanks @HanchaiN.
# 2022-04-08, version 10.4.3

View File

@ -10,7 +10,9 @@ export const powDocs = {
examples: [
'2^3',
'2*2*2',
'1 + e ^ (pi * i)'
'1 + e ^ (pi * i)',
'math.pow([[1, 2], [4, 3]], 2)',
'math.pow([[1, 2], [4, 3]], -1)'
],
seealso: [
'multiply',

View File

@ -10,16 +10,20 @@ const dependencies = [
'identity',
'multiply',
'matrix',
'inv',
'fraction',
'number',
'Complex'
]
export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, identity, multiply, matrix, number, fraction, Complex }) => {
export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, config, identity, multiply, matrix, inv, number, fraction, Complex }) => {
/**
* Calculates the power of x to y, `x ^ y`.
* Matrix exponentiation is supported for square matrices `x`, and positive
* integer exponents `y`.
*
* Matrix exponentiation is supported for square matrices `x` and integers `y`:
* when `y` is nonnegative, `x` may be any square matrix; and when `y` is
* negative, `x` must be invertible, and then this function returns
* inv(x)^(-y).
*
* For cubic roots of negative numbers, the function returns the principal
* root by default. In order to let the function return the real root,
@ -40,6 +44,9 @@ export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, c
* const b = [[1, 2], [4, 3]]
* math.pow(b, 2) // returns Array [[9, 8], [16, 17]]
*
* const c = [[1, 2], [4, 3]]
* math.pow(c, -1) // returns Array [[-0.6, 0.4], [0.8, -0.2]]
*
* See also:
*
* multiply, sqrt, cbrt, nthRoot
@ -150,13 +157,13 @@ export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, c
/**
* Calculate the power of a 2d array
* @param {Array} x must be a 2 dimensional, square matrix
* @param {number} y a positive, integer value
* @param {number} y a integer value (positive if `x` is not invertible)
* @returns {Array}
* @private
*/
function _powArray (x, y) {
if (!isInteger(y) || y < 0) {
throw new TypeError('For A^b, b must be a positive integer (value is ' + y + ')')
if (!isInteger(y)) {
throw new TypeError('For A^b, b must be an integer (value is ' + y + ')')
}
// verify that A is a 2 dimensional square matrix
const s = size(x)
@ -166,6 +173,16 @@ export const createPow = /* #__PURE__ */ factory(name, dependencies, ({ typed, c
if (s[0] !== s[1]) {
throw new Error('For A^b, A must be square (size is ' + s[0] + 'x' + s[1] + ')')
}
if (y < 0) {
try {
return _powArray(inv(x), -y)
} catch (error) {
if (error.message === 'Cannot calculate inverse, determinant is zero') {
throw new TypeError('For A^b, when A is not invertible, b must be a positive integer (value is ' + y + ')')
}
throw error
}
}
let res = identity(s[0]).valueOf()
let px = x

View File

@ -241,6 +241,21 @@ describe('pow', function () {
approx.deepEqual(pow(matrix(a), 2), matrix(res))
})
it('should raise an inverted matrix for power -1', function () {
const a = [
[2, -1, 0],
[-1, 2, -1],
[0, -1, 2]
]
const res = [
[3 / 4, 1 / 2, 1 / 4],
[1 / 2, 1, 1 / 2],
[1 / 4, 1 / 2, 3 / 4]
]
approx.deepEqual(pow(a, -1), res)
approx.deepEqual(pow(matrix(a), -1), matrix(res))
})
it('should return identity matrix for power 0', function () {
const a = [[1, 2], [3, 4]]
const res = [[1, 0], [0, 1]]
@ -266,6 +281,11 @@ describe('pow', function () {
assert.throws(function () { pow(a, [2, 3]) })
})
it('should throw an error when raising a non-invertible matrix to a negative integer power', function () {
const a = [[1, 1, 1], [1, 0, 0], [0, 0, 0]]
assert.throws(function () { pow(a, -1) })
})
it('should LaTeX pow', function () {
const expression = math.parse('pow(2,10)')
assert.strictEqual(expression.toTex(), '\\left(2\\right)^{10}')