ml-matrix 6.10.7 → 6.11.0
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 +458 -19
- package/matrix.mjs +4 -2
- package/matrix.umd.js +1 -1
- package/package.json +1 -1
- package/src/distanceMatrix.js +121 -0
- package/src/index.js +2 -0
- package/src/matrix.js +86 -19
- package/src/symmetricMatrix.js +248 -0
|
@@ -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';
|