Skip to content

Commit

Permalink
Add mpow to compute matrix power using exponentiation by squaring
Browse files Browse the repository at this point in the history
  • Loading branch information
rotu committed Oct 14, 2024
1 parent a16f8b1 commit 9fec6f0
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 0 deletions.
6 changes: 6 additions & 0 deletions matrix.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -616,6 +616,12 @@ export abstract class AbstractMatrix {
*/
mmul(other: MaybeMatrix): Matrix;

/**
* Returns the square matrix raised to the given power
* @param scalar - the non-negative integer power to raise this matrix to
*/
mpow(scalar: number): Matrix;

strassen2x2(other: MaybeMatrix): Matrix;

strassen3x3(other: MaybeMatrix): Matrix;
Expand Down
46 changes: 46 additions & 0 deletions src/__tests__/matrix/mpow.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import { describe, it, expect } from 'vitest';

import { Matrix } from '../..';

Check failure on line 3 in src/__tests__/matrix/mpow.test.js

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Expected 1 empty line after import statement not followed by another import
describe('matrix power', () => {
it('power of a non-square matrix is not defined', () => {
let x = new Matrix(3, 5);
expect(() => x.mpow(2)).toThrowError();
});
it('negative power is rejected', () => {
let x = new Matrix(3, 3);
expect(() => x.mpow(-2)).toThrowError();
});
it('A matrix to the power 0 is identity', () => {
expect(
new Matrix([
[1, 2, 3],
[4, 5, 6],
[7, 8, 9],
])
.mpow(0)
.to2DArray(),
).toStrictEqual([
[1, 0, 0],
[0, 1, 0],
[0, 0, 1],
]);
});
it('Matches scalar exponentiation', () => {
let x = new Matrix([[1.5]]).mpow(50);
expect(x.size).toBe(1);
expect(x.get(0, 0)).toBeCloseTo(1.5 ** 50);
});
it('Matches scalar exponentiation', () => {
expect(
new Matrix([
[1, 2],
[3, -1],
])
.mpow(21)
.to2DArray(),
).toStrictEqual([
[282475249, 564950498],
[847425747, -282475249],
]);
});
});
20 changes: 20 additions & 0 deletions src/matrix.js
Original file line number Diff line number Diff line change
Expand Up @@ -874,6 +874,26 @@ export class AbstractMatrix {
return result;
}

mpow(scalar) {
if (!this.isSquare()) {
throw new RangeError('Matrix must be square');
}
if (!Number.isInteger(scalar) || scalar < 0) {
throw new RangeError('Exponent must be a non-negative integer');
}
// Russian Peasant exponentiation, i.e. exponentiation by squaring
let result = Matrix.eye(this.rows);
let bb = this;
// Note: Don't bit shift. In JS, that would truncate at 32 bits
for (let e = scalar; 1 < e; e /= 2) {

Check failure on line 888 in src/matrix.js

View workflow job for this annotation

GitHub Actions / nodejs / lint-eslint

Expected literal to be on the right side of <
if (e & (1 !== 0)) {
result = result.mmul(bb);
}
bb = bb.mmul(bb);
}
return result;
}

strassen2x2(other) {
other = Matrix.checkMatrix(other);
let result = new Matrix(2, 2);
Expand Down

0 comments on commit 9fec6f0

Please sign in to comment.