ml-matrix 6.10.8 → 6.11.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/matrix.d.ts +267 -3
- package/matrix.js +471 -24
- package/matrix.mjs +2 -0
- package/matrix.umd.js +1 -1
- package/package.json +1 -1
- package/src/distanceMatrix.js +127 -0
- package/src/index.js +2 -0
- package/src/inspect.js +4 -0
- package/src/mathOperations.js +2 -2
- package/src/matrix.js +86 -19
- package/src/stat.js +3 -3
- package/src/symmetricMatrix.js +248 -0
package/src/matrix.js
CHANGED
|
@@ -240,6 +240,16 @@ export class AbstractMatrix {
|
|
|
240
240
|
return false;
|
|
241
241
|
}
|
|
242
242
|
|
|
243
|
+
isDistance() {
|
|
244
|
+
if (!this.isSymmetric()) return false;
|
|
245
|
+
|
|
246
|
+
for (let i = 0; i < this.rows; i++) {
|
|
247
|
+
if (this.get(i, i) !== 0) return false;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
return true;
|
|
251
|
+
}
|
|
252
|
+
|
|
243
253
|
isEchelonForm() {
|
|
244
254
|
let i = 0;
|
|
245
255
|
let j = 0;
|
|
@@ -1314,13 +1324,21 @@ export class AbstractMatrix {
|
|
|
1314
1324
|
}
|
|
1315
1325
|
|
|
1316
1326
|
clone() {
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1327
|
+
return this.constructor.copy(this, new Matrix(this.rows, this.columns));
|
|
1328
|
+
}
|
|
1329
|
+
|
|
1330
|
+
/**
|
|
1331
|
+
* @template {AbstractMatrix} M
|
|
1332
|
+
* @param {AbstractMatrix} from
|
|
1333
|
+
* @param {M} to
|
|
1334
|
+
* @return {M}
|
|
1335
|
+
*/
|
|
1336
|
+
static copy(from, to) {
|
|
1337
|
+
for (const [row, column, value] of from.entries()) {
|
|
1338
|
+
to.set(row, column, value);
|
|
1322
1339
|
}
|
|
1323
|
-
|
|
1340
|
+
|
|
1341
|
+
return to;
|
|
1324
1342
|
}
|
|
1325
1343
|
|
|
1326
1344
|
sum(by) {
|
|
@@ -1504,6 +1522,36 @@ export class AbstractMatrix {
|
|
|
1504
1522
|
toString(options) {
|
|
1505
1523
|
return inspectMatrixWithOptions(this, options);
|
|
1506
1524
|
}
|
|
1525
|
+
|
|
1526
|
+
[Symbol.iterator]() {
|
|
1527
|
+
return this.entries();
|
|
1528
|
+
}
|
|
1529
|
+
|
|
1530
|
+
/**
|
|
1531
|
+
* iterator from left to right, from top to bottom
|
|
1532
|
+
* yield [row, column, value]
|
|
1533
|
+
* @returns {Generator<[number, number, number], void, *>}
|
|
1534
|
+
*/
|
|
1535
|
+
*entries() {
|
|
1536
|
+
for (let row = 0; row < this.rows; row++) {
|
|
1537
|
+
for (let col = 0; col < this.columns; col++) {
|
|
1538
|
+
yield [row, col, this.get(row, col)];
|
|
1539
|
+
}
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
|
|
1543
|
+
/**
|
|
1544
|
+
* iterator from left to right, from top to bottom
|
|
1545
|
+
* yield value
|
|
1546
|
+
* @returns {Generator<number, void, *>}
|
|
1547
|
+
*/
|
|
1548
|
+
*values() {
|
|
1549
|
+
for (let row = 0; row < this.rows; row++) {
|
|
1550
|
+
for (let col = 0; col < this.columns; col++) {
|
|
1551
|
+
yield this.get(row, col);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1507
1555
|
}
|
|
1508
1556
|
|
|
1509
1557
|
AbstractMatrix.prototype.klass = 'Matrix';
|
|
@@ -1533,21 +1581,38 @@ AbstractMatrix.prototype.tensorProduct =
|
|
|
1533
1581
|
AbstractMatrix.prototype.kroneckerProduct;
|
|
1534
1582
|
|
|
1535
1583
|
export default class Matrix extends AbstractMatrix {
|
|
1584
|
+
/**
|
|
1585
|
+
* @type {Float64Array[]}
|
|
1586
|
+
*/
|
|
1587
|
+
data;
|
|
1588
|
+
|
|
1589
|
+
/**
|
|
1590
|
+
* Init an empty matrix
|
|
1591
|
+
* @param {number} nRows
|
|
1592
|
+
* @param {number} nColumns
|
|
1593
|
+
*/
|
|
1594
|
+
#initData(nRows, nColumns) {
|
|
1595
|
+
this.data = [];
|
|
1596
|
+
|
|
1597
|
+
if (Number.isInteger(nColumns) && nColumns >= 0) {
|
|
1598
|
+
for (let i = 0; i < nRows; i++) {
|
|
1599
|
+
this.data.push(new Float64Array(nColumns));
|
|
1600
|
+
}
|
|
1601
|
+
} else {
|
|
1602
|
+
throw new TypeError('nColumns must be a positive integer');
|
|
1603
|
+
}
|
|
1604
|
+
|
|
1605
|
+
this.rows = nRows;
|
|
1606
|
+
this.columns = nColumns;
|
|
1607
|
+
}
|
|
1608
|
+
|
|
1536
1609
|
constructor(nRows, nColumns) {
|
|
1537
1610
|
super();
|
|
1538
1611
|
if (Matrix.isMatrix(nRows)) {
|
|
1539
|
-
|
|
1540
|
-
|
|
1612
|
+
this.#initData(nRows.rows, nRows.columns);
|
|
1613
|
+
Matrix.copy(nRows, this);
|
|
1541
1614
|
} else if (Number.isInteger(nRows) && nRows >= 0) {
|
|
1542
|
-
|
|
1543
|
-
this.data = [];
|
|
1544
|
-
if (Number.isInteger(nColumns) && nColumns >= 0) {
|
|
1545
|
-
for (let i = 0; i < nRows; i++) {
|
|
1546
|
-
this.data.push(new Float64Array(nColumns));
|
|
1547
|
-
}
|
|
1548
|
-
} else {
|
|
1549
|
-
throw new TypeError('nColumns must be a positive integer');
|
|
1550
|
-
}
|
|
1615
|
+
this.#initData(nRows, nColumns);
|
|
1551
1616
|
} else if (isAnyArray(nRows)) {
|
|
1552
1617
|
// Copy the values from the 2D array
|
|
1553
1618
|
const arrayData = nRows;
|
|
@@ -1559,6 +1624,7 @@ export default class Matrix extends AbstractMatrix {
|
|
|
1559
1624
|
);
|
|
1560
1625
|
}
|
|
1561
1626
|
this.data = [];
|
|
1627
|
+
|
|
1562
1628
|
for (let i = 0; i < nRows; i++) {
|
|
1563
1629
|
if (arrayData[i].length !== nColumns) {
|
|
1564
1630
|
throw new RangeError('Inconsistent array dimensions');
|
|
@@ -1568,13 +1634,14 @@ export default class Matrix extends AbstractMatrix {
|
|
|
1568
1634
|
}
|
|
1569
1635
|
this.data.push(Float64Array.from(arrayData[i]));
|
|
1570
1636
|
}
|
|
1637
|
+
|
|
1638
|
+
this.rows = nRows;
|
|
1639
|
+
this.columns = nColumns;
|
|
1571
1640
|
} else {
|
|
1572
1641
|
throw new TypeError(
|
|
1573
1642
|
'First argument must be a positive number or an array',
|
|
1574
1643
|
);
|
|
1575
1644
|
}
|
|
1576
|
-
this.rows = nRows;
|
|
1577
|
-
this.columns = nColumns;
|
|
1578
1645
|
}
|
|
1579
1646
|
|
|
1580
1647
|
set(rowIndex, columnIndex, value) {
|
package/src/stat.js
CHANGED
|
@@ -157,7 +157,7 @@ export function getScaleByRow(matrix) {
|
|
|
157
157
|
for (let i = 0; i < matrix.rows; i++) {
|
|
158
158
|
let sum = 0;
|
|
159
159
|
for (let j = 0; j < matrix.columns; j++) {
|
|
160
|
-
sum +=
|
|
160
|
+
sum += matrix.get(i, j) ** 2 / (matrix.columns - 1);
|
|
161
161
|
}
|
|
162
162
|
scale.push(Math.sqrt(sum));
|
|
163
163
|
}
|
|
@@ -177,7 +177,7 @@ export function getScaleByColumn(matrix) {
|
|
|
177
177
|
for (let j = 0; j < matrix.columns; j++) {
|
|
178
178
|
let sum = 0;
|
|
179
179
|
for (let i = 0; i < matrix.rows; i++) {
|
|
180
|
-
sum +=
|
|
180
|
+
sum += matrix.get(i, j) ** 2 / (matrix.rows - 1);
|
|
181
181
|
}
|
|
182
182
|
scale.push(Math.sqrt(sum));
|
|
183
183
|
}
|
|
@@ -197,7 +197,7 @@ export function getScaleAll(matrix) {
|
|
|
197
197
|
let sum = 0;
|
|
198
198
|
for (let j = 0; j < matrix.columns; j++) {
|
|
199
199
|
for (let i = 0; i < matrix.rows; i++) {
|
|
200
|
-
sum +=
|
|
200
|
+
sum += matrix.get(i, j) ** 2 / divider;
|
|
201
201
|
}
|
|
202
202
|
}
|
|
203
203
|
return Math.sqrt(sum);
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @typedef {0 | 1 | number | boolean} Mask
|
|
3
|
+
*/
|
|
4
|
+
import Matrix, { AbstractMatrix } from './matrix';
|
|
5
|
+
|
|
6
|
+
export class SymmetricMatrix extends AbstractMatrix {
|
|
7
|
+
/** @type {Matrix} */
|
|
8
|
+
#matrix;
|
|
9
|
+
|
|
10
|
+
get size() {
|
|
11
|
+
return this.#matrix.size;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
get rows() {
|
|
15
|
+
return this.#matrix.rows;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
get columns() {
|
|
19
|
+
return this.#matrix.columns;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
get diagonalSize() {
|
|
23
|
+
return this.rows;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* not the same as matrix.isSymmetric()
|
|
28
|
+
* Here is to check if it's instanceof SymmetricMatrix without bundling issues
|
|
29
|
+
*
|
|
30
|
+
* @param value
|
|
31
|
+
* @returns {boolean}
|
|
32
|
+
*/
|
|
33
|
+
static isSymmetricMatrix(value) {
|
|
34
|
+
return Matrix.isMatrix(value) && value.klassType === 'SymmetricMatrix';
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/**
|
|
38
|
+
* @param diagonalSize
|
|
39
|
+
* @return {SymmetricMatrix}
|
|
40
|
+
*/
|
|
41
|
+
static zeros(diagonalSize) {
|
|
42
|
+
return new this(diagonalSize);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* @param diagonalSize
|
|
47
|
+
* @return {SymmetricMatrix}
|
|
48
|
+
*/
|
|
49
|
+
static ones(diagonalSize) {
|
|
50
|
+
return new this(diagonalSize).fill(1);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* @param {number | AbstractMatrix | ArrayLike<ArrayLike<number>>} diagonalSize
|
|
55
|
+
* @return {this}
|
|
56
|
+
*/
|
|
57
|
+
constructor(diagonalSize) {
|
|
58
|
+
super();
|
|
59
|
+
|
|
60
|
+
if (Matrix.isMatrix(diagonalSize)) {
|
|
61
|
+
if (!diagonalSize.isSymmetric()) {
|
|
62
|
+
throw new TypeError('not symmetric data');
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
this.#matrix = Matrix.copy(
|
|
66
|
+
diagonalSize,
|
|
67
|
+
new Matrix(diagonalSize.rows, diagonalSize.rows),
|
|
68
|
+
);
|
|
69
|
+
} else if (Number.isInteger(diagonalSize) && diagonalSize >= 0) {
|
|
70
|
+
this.#matrix = new Matrix(diagonalSize, diagonalSize);
|
|
71
|
+
} else {
|
|
72
|
+
this.#matrix = new Matrix(diagonalSize);
|
|
73
|
+
|
|
74
|
+
if (!this.isSymmetric()) {
|
|
75
|
+
throw new TypeError('not symmetric data');
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
clone() {
|
|
81
|
+
const matrix = new SymmetricMatrix(this.diagonalSize);
|
|
82
|
+
|
|
83
|
+
for (const [row, col, value] of this.upperRightEntries()) {
|
|
84
|
+
matrix.set(row, col, value);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return matrix;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
toMatrix() {
|
|
91
|
+
return new Matrix(this);
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
get(rowIndex, columnIndex) {
|
|
95
|
+
return this.#matrix.get(rowIndex, columnIndex);
|
|
96
|
+
}
|
|
97
|
+
set(rowIndex, columnIndex, value) {
|
|
98
|
+
// symmetric set
|
|
99
|
+
this.#matrix.set(rowIndex, columnIndex, value);
|
|
100
|
+
this.#matrix.set(columnIndex, rowIndex, value);
|
|
101
|
+
|
|
102
|
+
return this;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
removeCross(index) {
|
|
106
|
+
// symmetric remove side
|
|
107
|
+
this.#matrix.removeRow(index);
|
|
108
|
+
this.#matrix.removeColumn(index);
|
|
109
|
+
|
|
110
|
+
return this;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
addCross(index, array) {
|
|
114
|
+
if (array === undefined) {
|
|
115
|
+
array = index;
|
|
116
|
+
index = this.diagonalSize;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const row = array.slice();
|
|
120
|
+
row.splice(index, 1);
|
|
121
|
+
|
|
122
|
+
this.#matrix.addRow(index, row);
|
|
123
|
+
this.#matrix.addColumn(index, array);
|
|
124
|
+
|
|
125
|
+
return this;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* @param {Mask[]} mask
|
|
130
|
+
*/
|
|
131
|
+
applyMask(mask) {
|
|
132
|
+
if (mask.length !== this.diagonalSize) {
|
|
133
|
+
throw new RangeError('Mask size do not match with matrix size');
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// prepare sides to remove from matrix from mask
|
|
137
|
+
/** @type {number[]} */
|
|
138
|
+
const sidesToRemove = [];
|
|
139
|
+
for (const [index, passthroughs] of mask.entries()) {
|
|
140
|
+
if (passthroughs) continue;
|
|
141
|
+
sidesToRemove.push(index);
|
|
142
|
+
}
|
|
143
|
+
// to remove from highest to lowest for no mutation shifting
|
|
144
|
+
sidesToRemove.reverse();
|
|
145
|
+
|
|
146
|
+
// remove sides
|
|
147
|
+
for (const sideIndex of sidesToRemove) {
|
|
148
|
+
this.removeCross(sideIndex);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return this;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Compact format upper-right corner of matrix
|
|
156
|
+
* iterate from left to right, from top to bottom.
|
|
157
|
+
*
|
|
158
|
+
* ```
|
|
159
|
+
* A B C D
|
|
160
|
+
* A 1 2 3 4
|
|
161
|
+
* B 2 5 6 7
|
|
162
|
+
* C 3 6 8 9
|
|
163
|
+
* D 4 7 9 10
|
|
164
|
+
* ```
|
|
165
|
+
*
|
|
166
|
+
* will return compact 1D array `[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]`
|
|
167
|
+
*
|
|
168
|
+
* length is S(i=0, n=sideSize) => 10 for a 4 sideSized matrix
|
|
169
|
+
*
|
|
170
|
+
* @returns {number[]}
|
|
171
|
+
*/
|
|
172
|
+
toCompact() {
|
|
173
|
+
const { diagonalSize } = this;
|
|
174
|
+
|
|
175
|
+
/** @type {number[]} */
|
|
176
|
+
const compact = new Array((diagonalSize * (diagonalSize + 1)) / 2);
|
|
177
|
+
for (let col = 0, row = 0, index = 0; index < compact.length; index++) {
|
|
178
|
+
compact[index] = this.get(row, col);
|
|
179
|
+
|
|
180
|
+
if (++col >= diagonalSize) col = ++row;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return compact;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* @param {number[]} compact
|
|
188
|
+
* @return {SymmetricMatrix}
|
|
189
|
+
*/
|
|
190
|
+
static fromCompact(compact) {
|
|
191
|
+
const compactSize = compact.length;
|
|
192
|
+
// compactSize = (sideSize * (sideSize + 1)) / 2
|
|
193
|
+
// https://mathsolver.microsoft.com/fr/solve-problem/y%20%3D%20%20x%20%60cdot%20%20%20%60frac%7B%20%20%60left(%20x%2B1%20%20%60right)%20%20%20%20%7D%7B%202%20%20%7D
|
|
194
|
+
// sideSize = (Sqrt(8 × compactSize + 1) - 1) / 2
|
|
195
|
+
const diagonalSize = (Math.sqrt(8 * compactSize + 1) - 1) / 2;
|
|
196
|
+
|
|
197
|
+
if (!Number.isInteger(diagonalSize)) {
|
|
198
|
+
throw new TypeError(
|
|
199
|
+
`This array is not a compact representation of a Symmetric Matrix, ${JSON.stringify(
|
|
200
|
+
compact,
|
|
201
|
+
)}`,
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const matrix = new SymmetricMatrix(diagonalSize);
|
|
206
|
+
for (let col = 0, row = 0, index = 0; index < compactSize; index++) {
|
|
207
|
+
matrix.set(col, row, compact[index]);
|
|
208
|
+
if (++col >= diagonalSize) col = ++row;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
return matrix;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* half iterator upper-right-corner from left to right, from top to bottom
|
|
216
|
+
* yield [row, column, value]
|
|
217
|
+
*
|
|
218
|
+
* @returns {Generator<[number, number, number], void, *>}
|
|
219
|
+
*/
|
|
220
|
+
*upperRightEntries() {
|
|
221
|
+
for (let row = 0, col = 0; row < this.diagonalSize; void 0) {
|
|
222
|
+
const value = this.get(row, col);
|
|
223
|
+
|
|
224
|
+
yield [row, col, value];
|
|
225
|
+
|
|
226
|
+
// at the end of row, move cursor to next row at diagonal position
|
|
227
|
+
if (++col >= this.diagonalSize) col = ++row;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* half iterator upper-right-corner from left to right, from top to bottom
|
|
233
|
+
* yield value
|
|
234
|
+
*
|
|
235
|
+
* @returns {Generator<[number, number, number], void, *>}
|
|
236
|
+
*/
|
|
237
|
+
*upperRightValues() {
|
|
238
|
+
for (let row = 0, col = 0; row < this.diagonalSize; void 0) {
|
|
239
|
+
const value = this.get(row, col);
|
|
240
|
+
|
|
241
|
+
yield value;
|
|
242
|
+
|
|
243
|
+
// at the end of row, move cursor to next row at diagonal position
|
|
244
|
+
if (++col >= this.diagonalSize) col = ++row;
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
SymmetricMatrix.prototype.klassType = 'SymmetricMatrix';
|