Skip to content

Commit dd35ec8

Browse files
committed
refactor: remove configurable super class and circular dependencies
Fixes the library for transpiled browser builds. BREAKING CHANGE: * It is no longer possible to make a Matrix class that extends a custom constructor * `matrix.det()` was moved to a standalone function: `determinant(matrix)` * `matrix.pseudoInverse()` was moved to a standalone function: `pseudoInverse(matrix)` * `matrix.linearDependencies()` was moved to a standalone function: `linearDependencies(matrix)` * Matrix views must be created using their constructors instead of Matrix methods. For example, `matrix.transposeView()` becomes `new MatrixTransposeView(matrix)`
1 parent 7cdb3a9 commit dd35ec8

30 files changed

+1917
-2054
lines changed

matrix.d.ts

Lines changed: 36 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -4,66 +4,38 @@ declare module 'ml-matrix' {
44
type ScalarOrMatrix = number | Matrix;
55
type MatrixDimension = 'row' | 'column';
66

7-
class BaseView extends Matrix {}
8-
class MatrixColumnView extends BaseView {
9-
set(rowIndex: number, columnIndex: number, value: number): MatrixColumnView;
10-
get(rowIndex: number): number;
7+
export class MatrixColumnView extends Matrix {
8+
constructor(matrix: Matrix, column: number);
119
}
12-
class MatrixColumnSelectionView extends BaseView {
13-
set(
14-
rowIndex: number,
15-
columnIndex: number,
16-
value: number
17-
): MatrixColumnSelectionView;
18-
get(rowIndex: number, columnIndex: number): number;
10+
export class MatrixColumnSelectionView extends Matrix {
11+
constructor(matrix: Matrix, columnIndices: number[]);
1912
}
20-
class MatrixFlipColumnView extends BaseView {
21-
set(
22-
rowIndex: number,
23-
columnIndex: number,
24-
value: number
25-
): MatrixFlipColumnView;
26-
get(rowIndex: number, columnIndex: number): number;
13+
export class MatrixFlipColumnView extends Matrix {
14+
constructor(matrix: Matrix);
2715
}
28-
class MatrixFlipRowView extends BaseView {
29-
set(
30-
rowIndex: number,
31-
columnIndex: number,
32-
value: number
33-
): MatrixFlipRowView;
34-
get(rowIndex: number, columnIndex: number): number;
16+
export class MatrixFlipRowView extends Matrix {
17+
constructor(matrix: Matrix);
3518
}
36-
class MatrixRowView extends BaseView {
37-
set(rowIndex: number, columnIndex: number, value: number): MatrixRowView;
38-
get(rowIndex: number, columnIndex: number): number;
19+
export class MatrixRowView extends Matrix {
20+
constructor(matrix: Matrix, row: number);
3921
}
40-
class MatrixRowSelectionView extends BaseView {
41-
set(
42-
rowIndex: number,
43-
columnIndex: number,
44-
value: number
45-
): MatrixRowSelectionView;
46-
get(rowIndex: number, columnIndex: number): number;
22+
export class MatrixRowSelectionView extends Matrix {
23+
constructor(matrix: Matrix, rowIndices: number[]);
4724
}
48-
class MatrixSelectionView extends BaseView {
49-
set(
50-
rowIndex: number,
51-
columnIndex: number,
52-
value: number
53-
): MatrixSelectionView;
54-
get(rowIndex: number, columnIndex: number): number;
25+
export class MatrixSelectionView extends Matrix {
26+
constructor(matrix: Matrix, rowIndices: number[], columnIndices: number[]);
5527
}
56-
class MatrixSubView extends BaseView {
57-
set(rowIndex: number, columnIndex: number, value: number): MatrixSubView;
58-
get(rowIndex: number, columnIndex: number): number;
28+
export class MatrixSubView extends Matrix {
29+
constructor(
30+
matrix: Matrix,
31+
startRow: number,
32+
endRow: number,
33+
startColumn: number,
34+
endColumn: number
35+
);
5936
}
60-
class MatrixTransposeView extends BaseView {
61-
set(
62-
rowIndex: number,
63-
columnIndex: number,
64-
value: number
65-
): MatrixTransposeView;
66-
get(rowIndex: number, columnIndex: number): number;
37+
export class MatrixTransposeView extends BaseView {
38+
constructor(matrix: Matrix);
6739
}
6840
export class Matrix {
6941
readonly size: number;
@@ -100,7 +72,7 @@ declare module 'ml-matrix' {
10072
static checkMatrix(value: any): Matrix;
10173
static isMatrix(value: any): value is Matrix;
10274

103-
apply(callback: Function): Matrix;
75+
apply(callback: Function): this;
10476
to1DArray(): number[];
10577
to2DArray(): number[][];
10678
isRowVector(): boolean;
@@ -110,7 +82,7 @@ declare module 'ml-matrix' {
11082
isSymmetric(): boolean;
11183
isEchelonForm(): boolean;
11284
isReducedEchelonForm(): boolean;
113-
set(rowIndex: number, columnIndex: number, value: number): Matrix;
85+
set(rowIndex: number, columnIndex: number, value: number): this;
11486
get(rowIndex: number, columnIndex: number): number;
11587
repeat(rowRep: number, colRep: number): Matrix;
11688
fill(value: number): this;
@@ -224,26 +196,6 @@ declare module 'ml-matrix' {
224196
): Matrix;
225197
selection(rowIndices: number[], columnIndices: number[]): Matrix;
226198
trace(): number;
227-
transposeView(): MatrixTransposeView;
228-
rowView(row: number): MatrixRowView;
229-
columnView(column: number): MatrixColumnView;
230-
flipRowView(): MatrixFlipRowView;
231-
flipColumnView(): MatrixFlipColumnView;
232-
subMatrixView(
233-
startRow: number,
234-
endRow: number,
235-
startColumn: number,
236-
endColumn: number
237-
): MatrixSubView;
238-
selectionView(
239-
rowIndices: number[],
240-
columnIndices: number[]
241-
): MatrixSelectionView;
242-
rowSelectionView(rowIndices: number[]): MatrixRowSelectionView;
243-
columnSelectionView(columnIndices: number[]): MatrixColumnSelectionView;
244-
det(): number;
245-
determinant(): number;
246-
pseudoInverse(threshold?: number): Matrix;
247199
clone(): Matrix;
248200
entropy(eps?: number): number;
249201
variance(unbiased?: boolean, means?: number[]): number[];
@@ -421,10 +373,17 @@ declare module 'ml-matrix' {
421373
export { QrDecomposition, QrDecomposition as QR };
422374

423375
export function solve(
424-
leftHandSide: Matrix,
425-
rightHandSide: Matrix,
376+
leftHandSide: MaybeMatrix,
377+
rightHandSide: MaybeMatrix,
426378
useSVD?: boolean
427379
): Matrix;
428380

429-
export function inverse(matrix: Matrix, useSVD?: boolean): Matrix;
381+
export function inverse(matrix: MaybeMatrix, useSVD?: boolean): Matrix;
382+
383+
export function determinant(matrix: MaybeMatrix): number;
384+
385+
export function pseudoInverse(
386+
matrix: MaybeMatrix,
387+
threshold?: number
388+
): Matrix;
430389
}

src/__tests__/matrix/creation.js

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,7 +150,7 @@ describe('Matrix creation', () => {
150150

151151
it('Symbol.species should work for views', () => {
152152
var matrix = new Matrix([[1, 1, 1], [1, 1, 1]]);
153-
var view = matrix.transposeView();
153+
var view = new MatrixTransposeView(matrix);
154154
expect(matrix.transpose().mmul(matrix)).toStrictEqual(view.mmul(matrix));
155155
});
156156

@@ -168,7 +168,9 @@ describe('Matrix creation', () => {
168168
expect(JSON.stringify(matrix)).toBe(json);
169169
const mat1D = wrap(matrix.to1DArray(), { rows: 2 });
170170
expect(JSON.stringify(mat1D)).toBe(json);
171-
const transposeTwice = matrix.transposeView().transposeView();
171+
const transposeTwice = new MatrixTransposeView(
172+
new MatrixTransposeView(matrix)
173+
);
172174
expect(JSON.stringify(transposeTwice)).toBe(json);
173175
});
174176
});

src/__tests__/matrix/utility.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Matrix } from '../..';
1+
import { Matrix, pseudoInverse, determinant } from '../..';
22
import * as util from '../../../testUtils';
33

44
describe('utility methods', () => {
@@ -87,27 +87,23 @@ describe('utility methods', () => {
8787
});
8888

8989
it('determinant', () => {
90-
var determinant = matrix.det();
91-
expect(determinant).toBe(-18);
90+
var det = determinant(matrix);
91+
expect(det).toBe(-18);
9292
var subMatrix = matrix.selection([1, 2], [1, 2]);
93-
determinant = subMatrix.det();
94-
expect(determinant).toBe(-9);
95-
});
96-
97-
it('determinant synonym', () => {
98-
expect(matrix.det()).toBe(matrix.determinant());
93+
det = determinant(subMatrix);
94+
expect(det).toBe(-9);
9995
});
10096

10197
it('determinant n>3', () => {
10298
var m = Matrix.rand(5, 5);
103-
expect(m.det()).toBeCloseTo(0, -1);
99+
expect(determinant(m)).toBeCloseTo(0, -1);
104100
});
105101

106102
it('determinant wrong size', () => {
107103
var m1 = Matrix.ones(3, 5);
108104
var m2 = Matrix.ones(5, 3);
109-
expect(() => m1.det()).toThrow(/square/);
110-
expect(() => m2.det()).toThrow(/square/);
105+
expect(() => determinant(m1)).toThrow(/square/);
106+
expect(() => determinant(m2)).toThrow(/square/);
111107
});
112108

113109
it('norm Frobenius', () => {
@@ -242,23 +238,23 @@ describe('utility methods', () => {
242238
// Actual values calculated by the Numpy library
243239

244240
var matrix = new Matrix([[2, 4], [7, 1]]);
245-
var result = matrix.pseudoInverse().to2DArray();
241+
var result = pseudoInverse(matrix).to2DArray();
246242

247243
expect(result[0][0]).toBeCloseTo(-0.03846153846153843, 5);
248244
expect(result[0][1]).toBeCloseTo(0.15384615384615374, 5);
249245
expect(result[1][0]).toBeCloseTo(0.2692307692307691, 5);
250246
expect(result[1][1]).toBeCloseTo(-0.076923076923077, 5);
251247

252248
matrix = new Matrix([[4, 7], [2, 6]]);
253-
result = matrix.pseudoInverse().to2DArray();
249+
result = pseudoInverse(matrix).to2DArray();
254250
expect(result[0][0]).toBeCloseTo(0.6, 5);
255251
expect(result[0][1]).toBeCloseTo(-0.7, 5);
256252
expect(result[1][0]).toBeCloseTo(-0.2, 5);
257253
expect(result[1][1]).toBeCloseTo(0.4, 5);
258254

259255
// Example from http://reference.wolfram.com/language/ref/PseudoInverse.html
260256
matrix = new Matrix([[1, 2, 3], [4, 5, 6], [7, 8, 9]]);
261-
result = matrix.pseudoInverse().to2DArray();
257+
result = pseudoInverse(matrix).to2DArray();
262258

263259
expect(result[0][0]).toBeCloseTo(-6.38888889e-1, 5);
264260
expect(result[0][1]).toBeCloseTo(-1.66666667e-1, 5);
@@ -274,7 +270,7 @@ describe('utility methods', () => {
274270

275271
// non-square matrix.
276272
matrix = new Matrix([[1, 0, 1], [2, 4, 5]]);
277-
result = matrix.pseudoInverse().to2DArray();
273+
result = pseudoInverse(matrix).to2DArray();
278274

279275
expect(result[0][0]).toBeCloseTo(0.75609756, 5);
280276
expect(result[0][1]).toBeCloseTo(-0.07317073, 5);

src/__tests__/views/column.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { Matrix } from '../..';
1+
import { Matrix, MatrixColumnView } from '../..';
22

33
describe('Column view', () => {
44
it('should set and get row values', () => {
55
const m = Matrix.ones(5, 8);
6-
const mcv1 = m.columnView(2);
7-
const mcv2 = m.columnView(3);
6+
const mcv1 = new MatrixColumnView(m, 2);
7+
const mcv2 = new MatrixColumnView(m, 3);
88

99
m.set(2, 2, 12);
1010
m.set(0, 3, 5);

src/__tests__/views/columnSelection.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Matrix } from '../..';
1+
import { Matrix, MatrixColumnSelectionView } from '../..';
22

33
describe('Selection column view', () => {
44
it('should correctly remap coordinates', () => {
55
const m = Matrix.ones(5, 8);
6-
const msv = m.columnSelectionView([1, 2]);
6+
const msv = new MatrixColumnSelectionView(m, [1, 2]);
77

88
expect(m.get(0, 2)).toBe(1);
99
msv.set(0, 1, 5);
@@ -12,10 +12,10 @@ describe('Selection column view', () => {
1212

1313
it('should throw when wrong arguments or range', () => {
1414
const m = Matrix.ones(2, 2);
15-
expect(() => m.columnSelectionView([1, 1, 2])).toThrow(
15+
expect(() => new MatrixColumnSelectionView(m, [1, 1, 2])).toThrow(
1616
'column indices are out of range'
1717
);
18-
expect(() => m.columnSelectionView(1)).toThrow(
18+
expect(() => new MatrixColumnSelectionView(m, 1)).toThrow(
1919
'unexpected type for column indices'
2020
);
2121
});

src/__tests__/views/flipColumn.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Matrix } from '../..';
1+
import { Matrix, MatrixFlipColumnView } from '../..';
22

33
describe('Flip column view', () => {
44
it('should set and get values', () => {
55
const m = Matrix.ones(5, 8);
6-
const view = m.flipColumnView();
6+
const view = new MatrixFlipColumnView(m);
77

88
m.set(0, 3, 5);
99
expect(view.get(0, 4)).toBe(5);

src/__tests__/views/flipRow.js

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Matrix } from '../..';
1+
import { Matrix, MatrixFlipRowView } from '../..';
22

33
describe('Flip row view', () => {
44
it('should set and get values', () => {
55
const m = Matrix.ones(5, 8);
6-
const view = m.flipRowView();
6+
const view = new MatrixFlipRowView(m);
77

88
m.set(0, 3, 5);
99
expect(view.get(4, 3)).toBe(5);

src/__tests__/views/row.js

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
1-
import { Matrix } from '../..';
1+
import { Matrix, MatrixRowView } from '../..';
22

33
describe('Row view', () => {
44
it('should set and get column values', () => {
55
const m = Matrix.ones(5, 8);
6-
const mrv1 = m.rowView(0);
7-
const mrv2 = m.rowView(3);
6+
const mrv1 = new MatrixRowView(m, 0);
7+
const mrv2 = new MatrixRowView(m, 3);
88

99
m.set(0, 3, 5);
1010
m.set(2, 2, 12);

src/__tests__/views/rowSelection.js

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Matrix } from '../..';
1+
import { Matrix, MatrixRowSelectionView } from '../..';
22

33
describe('Selection view', () => {
44
it('should correctly remap coordinates', () => {
55
const m = Matrix.ones(5, 8);
6-
const msv = m.rowSelectionView([1, 2]);
6+
const msv = new MatrixRowSelectionView(m, [1, 2]);
77

88
expect(m.get(1, 0)).toBe(1);
99
msv.set(0, 0, 5);
@@ -12,10 +12,10 @@ describe('Selection view', () => {
1212

1313
it('should throw when wrong arguments or range', () => {
1414
const m = Matrix.ones(2, 2);
15-
expect(() => m.rowSelectionView([1, 1, 2])).toThrow(
15+
expect(() => new MatrixRowSelectionView(m, [1, 1, 2])).toThrow(
1616
'row indices are out of range'
1717
);
18-
expect(() => m.rowSelectionView(1)).toThrow(
18+
expect(() => new MatrixRowSelectionView(m, 1)).toThrow(
1919
'unexpected type for row indices'
2020
);
2121
});

src/__tests__/views/selection.js

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
1-
import { Matrix } from '../..';
1+
import { Matrix, MatrixSelectionView } from '../..';
22

33
describe('Selection view', () => {
44
it('should correctly remap coordinates', () => {
55
const m = Matrix.ones(5, 8);
6-
const msv = m.selectionView([1, 2], [2, 1]);
6+
const msv = new MatrixSelectionView(m, [1, 2], [2, 1]);
77

88
expect(m.get(1, 2)).toBe(1);
99
msv.set(0, 0, 5);
@@ -16,7 +16,11 @@ describe('Selection view', () => {
1616

1717
it('should handle typed arrays', () => {
1818
const m = Matrix.ones(5, 8);
19-
const msv = m.selectionView(Int8Array.from([1, 2]), Int8Array.from([2, 1]));
19+
const msv = new MatrixSelectionView(
20+
m,
21+
Int8Array.from([1, 2]),
22+
Int8Array.from([2, 1])
23+
);
2024

2125
expect(m.get(1, 2)).toBe(1);
2226
msv.set(0, 0, 5);
@@ -29,7 +33,9 @@ describe('Selection view', () => {
2933

3034
it('should throw when wrong arguments or range', () => {
3135
const m = Matrix.ones(2, 2);
32-
expect(() => m.selectionView([1, 1, 2], [0, 2])).toThrow(RangeError);
33-
expect(() => m.selectionView([1, 1])).toThrow(TypeError);
36+
expect(() => new MatrixSelectionView(m, [1, 1, 2], [0, 2])).toThrow(
37+
RangeError
38+
);
39+
expect(() => new MatrixSelectionView(m, [1, 1])).toThrow(TypeError);
3440
});
3541
});

0 commit comments

Comments
 (0)