@emasoft/svg-matrix 1.0.27 → 1.0.29
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/README.md +325 -0
- package/bin/svg-matrix.js +994 -378
- package/bin/svglinter.cjs +4172 -433
- package/bin/svgm.js +744 -184
- package/package.json +16 -4
- package/src/animation-references.js +71 -52
- package/src/arc-length.js +160 -96
- package/src/bezier-analysis.js +257 -117
- package/src/bezier-intersections.js +411 -148
- package/src/browser-verify.js +240 -100
- package/src/clip-path-resolver.js +350 -142
- package/src/convert-path-data.js +279 -134
- package/src/css-specificity.js +78 -70
- package/src/flatten-pipeline.js +751 -263
- package/src/geometry-to-path.js +511 -182
- package/src/index.js +191 -46
- package/src/inkscape-support.js +404 -0
- package/src/marker-resolver.js +278 -164
- package/src/mask-resolver.js +209 -98
- package/src/matrix.js +147 -67
- package/src/mesh-gradient.js +187 -96
- package/src/off-canvas-detection.js +201 -104
- package/src/path-analysis.js +187 -107
- package/src/path-data-plugins.js +628 -167
- package/src/path-simplification.js +0 -1
- package/src/pattern-resolver.js +125 -88
- package/src/polygon-clip.js +111 -66
- package/src/svg-boolean-ops.js +194 -118
- package/src/svg-collections.js +48 -19
- package/src/svg-flatten.js +282 -164
- package/src/svg-parser.js +427 -200
- package/src/svg-rendering-context.js +147 -104
- package/src/svg-toolbox.js +16411 -3298
- package/src/svg2-polyfills.js +114 -245
- package/src/transform-decomposition.js +46 -41
- package/src/transform-optimization.js +89 -68
- package/src/transforms2d.js +49 -16
- package/src/transforms3d.js +58 -22
- package/src/use-symbol-resolver.js +150 -110
- package/src/vector.js +67 -15
- package/src/vendor/README.md +110 -0
- package/src/vendor/inkscape-hatch-polyfill.js +401 -0
- package/src/vendor/inkscape-hatch-polyfill.min.js +8 -0
- package/src/vendor/inkscape-mesh-polyfill.js +843 -0
- package/src/vendor/inkscape-mesh-polyfill.min.js +8 -0
- package/src/verification.js +288 -124
package/src/matrix.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import Decimal from
|
|
2
|
-
import { Vector } from
|
|
1
|
+
import Decimal from "decimal.js";
|
|
2
|
+
import { Vector } from "./vector.js";
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Helper to convert any numeric input to Decimal.
|
|
@@ -7,7 +7,7 @@ import { Vector } from './vector.js';
|
|
|
7
7
|
* @param {number|string|Decimal} x - The value to convert
|
|
8
8
|
* @returns {Decimal} The Decimal representation
|
|
9
9
|
*/
|
|
10
|
-
const D = x => (x instanceof Decimal ? x : new Decimal(x));
|
|
10
|
+
const D = (x) => (x instanceof Decimal ? x : new Decimal(x));
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
13
|
* Matrix - Decimal-backed matrix class for arbitrary-precision matrix operations.
|
|
@@ -16,6 +16,11 @@ const D = x => (x instanceof Decimal ? x : new Decimal(x));
|
|
|
16
16
|
* Supports basic operations (add, sub, mul, transpose), linear algebra
|
|
17
17
|
* (LU, QR, determinant, inverse, solve), and matrix exponential.
|
|
18
18
|
*
|
|
19
|
+
* @class
|
|
20
|
+
* @property {Array<Array<Decimal>>} data - 2D array of Decimal matrix elements
|
|
21
|
+
* @property {number} rows - Number of rows in the matrix
|
|
22
|
+
* @property {number} cols - Number of columns in the matrix
|
|
23
|
+
*
|
|
19
24
|
* @example
|
|
20
25
|
* const M = Matrix.from([[1, 2], [3, 4]]);
|
|
21
26
|
* const I = Matrix.identity(2);
|
|
@@ -29,12 +34,14 @@ export class Matrix {
|
|
|
29
34
|
* @throws {Error} If data is not a non-empty 2D array with consistent row lengths
|
|
30
35
|
*/
|
|
31
36
|
constructor(data) {
|
|
32
|
-
if (!Array.isArray(data) || data.length === 0)
|
|
37
|
+
if (!Array.isArray(data) || data.length === 0)
|
|
38
|
+
throw new Error("Matrix requires non-empty 2D array");
|
|
33
39
|
const cols = data[0].length;
|
|
34
40
|
for (const row of data) {
|
|
35
|
-
if (!Array.isArray(row) || row.length !== cols)
|
|
41
|
+
if (!Array.isArray(row) || row.length !== cols)
|
|
42
|
+
throw new Error("All rows must have same length");
|
|
36
43
|
}
|
|
37
|
-
this.data = data.map(r => r.map(v => D(v)));
|
|
44
|
+
this.data = data.map((r) => r.map((v) => D(v)));
|
|
38
45
|
this.rows = data.length;
|
|
39
46
|
this.cols = cols;
|
|
40
47
|
}
|
|
@@ -55,7 +62,9 @@ export class Matrix {
|
|
|
55
62
|
* @returns {Matrix} New r×c zero matrix
|
|
56
63
|
*/
|
|
57
64
|
static zeros(r, c) {
|
|
58
|
-
const out = Array.from({ length: r }, () =>
|
|
65
|
+
const out = Array.from({ length: r }, () =>
|
|
66
|
+
Array.from({ length: c }, () => new Decimal(0)),
|
|
67
|
+
);
|
|
59
68
|
return new Matrix(out);
|
|
60
69
|
}
|
|
61
70
|
|
|
@@ -65,7 +74,11 @@ export class Matrix {
|
|
|
65
74
|
* @returns {Matrix} New n×n identity matrix
|
|
66
75
|
*/
|
|
67
76
|
static identity(n) {
|
|
68
|
-
const out = Array.from({ length: n }, (_, i) =>
|
|
77
|
+
const out = Array.from({ length: n }, (_, i) =>
|
|
78
|
+
Array.from({ length: n }, (_, j) =>
|
|
79
|
+
(i === j ? new Decimal(1) : new Decimal(0)),
|
|
80
|
+
),
|
|
81
|
+
);
|
|
69
82
|
return new Matrix(out);
|
|
70
83
|
}
|
|
71
84
|
|
|
@@ -74,7 +87,7 @@ export class Matrix {
|
|
|
74
87
|
* @returns {Matrix} New Matrix with copied values
|
|
75
88
|
*/
|
|
76
89
|
clone() {
|
|
77
|
-
return new Matrix(this.data.map(r => r.map(v => new Decimal(v))));
|
|
90
|
+
return new Matrix(this.data.map((r) => r.map((v) => new Decimal(v))));
|
|
78
91
|
}
|
|
79
92
|
|
|
80
93
|
/**
|
|
@@ -83,7 +96,7 @@ export class Matrix {
|
|
|
83
96
|
* @returns {string[][]} 2D array of string values
|
|
84
97
|
*/
|
|
85
98
|
toArrayOfStrings() {
|
|
86
|
-
return this.data.map(r => r.map(v => v.toString()));
|
|
99
|
+
return this.data.map((r) => r.map((v) => v.toString()));
|
|
87
100
|
}
|
|
88
101
|
|
|
89
102
|
/**
|
|
@@ -92,7 +105,7 @@ export class Matrix {
|
|
|
92
105
|
* @returns {number[][]} 2D array of number values
|
|
93
106
|
*/
|
|
94
107
|
toNumberArray() {
|
|
95
|
-
return this.data.map(r => r.map(v => v.toNumber()));
|
|
108
|
+
return this.data.map((r) => r.map((v) => v.toNumber()));
|
|
96
109
|
}
|
|
97
110
|
|
|
98
111
|
/**
|
|
@@ -113,12 +126,14 @@ export class Matrix {
|
|
|
113
126
|
let v;
|
|
114
127
|
if (vec instanceof Vector) v = vec;
|
|
115
128
|
else if (Array.isArray(vec)) v = Vector.from(vec);
|
|
116
|
-
else throw new Error(
|
|
117
|
-
if (this.cols !== v.length)
|
|
129
|
+
else throw new Error("applyToVector expects Vector or array");
|
|
130
|
+
if (this.cols !== v.length)
|
|
131
|
+
throw new Error("shape mismatch: matrix cols must equal vector length");
|
|
118
132
|
const out = [];
|
|
119
133
|
for (let i = 0; i < this.rows; i++) {
|
|
120
134
|
let sum = new Decimal(0);
|
|
121
|
-
for (let j = 0; j < this.cols; j++)
|
|
135
|
+
for (let j = 0; j < this.cols; j++)
|
|
136
|
+
sum = sum.plus(this.data[i][j].mul(v.data[j]));
|
|
122
137
|
out.push(sum);
|
|
123
138
|
}
|
|
124
139
|
return new Vector(out);
|
|
@@ -132,11 +147,14 @@ export class Matrix {
|
|
|
132
147
|
*/
|
|
133
148
|
add(other) {
|
|
134
149
|
if (other instanceof Matrix) {
|
|
135
|
-
if (this.rows !== other.rows || this.cols !== other.cols)
|
|
136
|
-
|
|
150
|
+
if (this.rows !== other.rows || this.cols !== other.cols)
|
|
151
|
+
throw new Error("shape mismatch: matrices must have same dimensions");
|
|
152
|
+
return new Matrix(
|
|
153
|
+
this.data.map((r, i) => r.map((v, j) => v.plus(other.data[i][j]))),
|
|
154
|
+
);
|
|
137
155
|
} else {
|
|
138
156
|
const s = D(other);
|
|
139
|
-
return new Matrix(this.data.map(r => r.map(v => v.plus(s))));
|
|
157
|
+
return new Matrix(this.data.map((r) => r.map((v) => v.plus(s))));
|
|
140
158
|
}
|
|
141
159
|
}
|
|
142
160
|
|
|
@@ -148,11 +166,14 @@ export class Matrix {
|
|
|
148
166
|
*/
|
|
149
167
|
sub(other) {
|
|
150
168
|
if (other instanceof Matrix) {
|
|
151
|
-
if (this.rows !== other.rows || this.cols !== other.cols)
|
|
152
|
-
|
|
169
|
+
if (this.rows !== other.rows || this.cols !== other.cols)
|
|
170
|
+
throw new Error("shape mismatch: matrices must have same dimensions");
|
|
171
|
+
return new Matrix(
|
|
172
|
+
this.data.map((r, i) => r.map((v, j) => v.minus(other.data[i][j]))),
|
|
173
|
+
);
|
|
153
174
|
} else {
|
|
154
175
|
const s = D(other);
|
|
155
|
-
return new Matrix(this.data.map(r => r.map(v => v.minus(s))));
|
|
176
|
+
return new Matrix(this.data.map((r) => r.map((v) => v.minus(s))));
|
|
156
177
|
}
|
|
157
178
|
}
|
|
158
179
|
|
|
@@ -164,19 +185,25 @@ export class Matrix {
|
|
|
164
185
|
*/
|
|
165
186
|
mul(other) {
|
|
166
187
|
if (other instanceof Matrix) {
|
|
167
|
-
if (this.cols !== other.rows)
|
|
168
|
-
|
|
188
|
+
if (this.cols !== other.rows)
|
|
189
|
+
throw new Error(
|
|
190
|
+
"shape mismatch: A.cols must equal B.rows for matrix multiplication",
|
|
191
|
+
);
|
|
192
|
+
const out = Array.from({ length: this.rows }, () =>
|
|
193
|
+
Array.from({ length: other.cols }, () => new Decimal(0)),
|
|
194
|
+
);
|
|
169
195
|
for (let i = 0; i < this.rows; i++) {
|
|
170
196
|
for (let k = 0; k < this.cols; k++) {
|
|
171
197
|
const aik = this.data[i][k];
|
|
172
198
|
if (aik.isZero()) continue;
|
|
173
|
-
for (let j = 0; j < other.cols; j++)
|
|
199
|
+
for (let j = 0; j < other.cols; j++)
|
|
200
|
+
out[i][j] = out[i][j].plus(aik.mul(other.data[k][j]));
|
|
174
201
|
}
|
|
175
202
|
}
|
|
176
203
|
return new Matrix(out);
|
|
177
204
|
} else {
|
|
178
205
|
const s = D(other);
|
|
179
|
-
return new Matrix(this.data.map(r => r.map(v => v.mul(s))));
|
|
206
|
+
return new Matrix(this.data.map((r) => r.map((v) => v.mul(s))));
|
|
180
207
|
}
|
|
181
208
|
}
|
|
182
209
|
|
|
@@ -188,8 +215,8 @@ export class Matrix {
|
|
|
188
215
|
*/
|
|
189
216
|
div(scalar) {
|
|
190
217
|
const s = D(scalar);
|
|
191
|
-
if (s.isZero()) throw new Error(
|
|
192
|
-
return new Matrix(this.data.map(r => r.map(v => v.div(s))));
|
|
218
|
+
if (s.isZero()) throw new Error("Cannot divide by zero");
|
|
219
|
+
return new Matrix(this.data.map((r) => r.map((v) => v.div(s))));
|
|
193
220
|
}
|
|
194
221
|
|
|
195
222
|
/**
|
|
@@ -197,7 +224,7 @@ export class Matrix {
|
|
|
197
224
|
* @returns {Matrix} New Matrix with negated elements
|
|
198
225
|
*/
|
|
199
226
|
negate() {
|
|
200
|
-
return new Matrix(this.data.map(r => r.map(v => v.negated())));
|
|
227
|
+
return new Matrix(this.data.map((r) => r.map((v) => v.negated())));
|
|
201
228
|
}
|
|
202
229
|
|
|
203
230
|
/**
|
|
@@ -205,7 +232,9 @@ export class Matrix {
|
|
|
205
232
|
* @returns {Matrix} New transposed Matrix
|
|
206
233
|
*/
|
|
207
234
|
transpose() {
|
|
208
|
-
const out = Array.from({ length: this.cols }, (_, i) =>
|
|
235
|
+
const out = Array.from({ length: this.cols }, (_, i) =>
|
|
236
|
+
Array.from({ length: this.rows }, (_, j) => new Decimal(this.data[j][i])),
|
|
237
|
+
);
|
|
209
238
|
return new Matrix(out);
|
|
210
239
|
}
|
|
211
240
|
|
|
@@ -216,7 +245,8 @@ export class Matrix {
|
|
|
216
245
|
* @throws {Error} If matrix is not square
|
|
217
246
|
*/
|
|
218
247
|
trace() {
|
|
219
|
-
if (!this.isSquare())
|
|
248
|
+
if (!this.isSquare())
|
|
249
|
+
throw new Error("Trace only defined for square matrices");
|
|
220
250
|
let sum = new Decimal(0);
|
|
221
251
|
for (let i = 0; i < this.rows; i++) {
|
|
222
252
|
sum = sum.plus(this.data[i][i]);
|
|
@@ -251,11 +281,14 @@ export class Matrix {
|
|
|
251
281
|
* @throws {Error} If matrix is not square or is singular
|
|
252
282
|
*/
|
|
253
283
|
lu() {
|
|
254
|
-
if (!this.isSquare())
|
|
284
|
+
if (!this.isSquare())
|
|
285
|
+
throw new Error("LU decomposition requires square matrix");
|
|
255
286
|
const n = this.rows;
|
|
256
|
-
const A = this.data.map(r => r.map(v => new Decimal(v)));
|
|
287
|
+
const A = this.data.map((r) => r.map((v) => new Decimal(v)));
|
|
257
288
|
const Pvec = Array.from({ length: n }, (_, i) => i);
|
|
258
|
-
const L = Array.from({ length: n }, () =>
|
|
289
|
+
const L = Array.from({ length: n }, () =>
|
|
290
|
+
Array.from({ length: n }, () => new Decimal(0)),
|
|
291
|
+
);
|
|
259
292
|
for (let i = 0; i < n; i++) L[i][i] = new Decimal(1);
|
|
260
293
|
|
|
261
294
|
for (let k = 0; k < n; k++) {
|
|
@@ -264,26 +297,39 @@ export class Matrix {
|
|
|
264
297
|
let maxAbs = A[k][k].abs();
|
|
265
298
|
for (let i = k + 1; i < n; i++) {
|
|
266
299
|
const aabs = A[i][k].abs();
|
|
267
|
-
if (aabs.greaterThan(maxAbs)) {
|
|
300
|
+
if (aabs.greaterThan(maxAbs)) {
|
|
301
|
+
maxAbs = aabs;
|
|
302
|
+
pivot = i;
|
|
303
|
+
}
|
|
268
304
|
}
|
|
269
|
-
if (A[pivot][k].isZero())
|
|
305
|
+
if (A[pivot][k].isZero())
|
|
306
|
+
throw new Error("Singular matrix: LU decomposition failed");
|
|
270
307
|
// Swap rows
|
|
271
308
|
if (pivot !== k) {
|
|
272
|
-
const tmp = A[k];
|
|
273
|
-
|
|
309
|
+
const tmp = A[k];
|
|
310
|
+
A[k] = A[pivot];
|
|
311
|
+
A[pivot] = tmp;
|
|
312
|
+
const tmpIdx = Pvec[k];
|
|
313
|
+
Pvec[k] = Pvec[pivot];
|
|
314
|
+
Pvec[pivot] = tmpIdx;
|
|
274
315
|
for (let j = 0; j < k; j++) {
|
|
275
|
-
const t = L[k][j];
|
|
316
|
+
const t = L[k][j];
|
|
317
|
+
L[k][j] = L[pivot][j];
|
|
318
|
+
L[pivot][j] = t;
|
|
276
319
|
}
|
|
277
320
|
}
|
|
278
321
|
// Elimination
|
|
279
322
|
for (let i = k + 1; i < n; i++) {
|
|
280
323
|
const factor = A[i][k].div(A[k][k]);
|
|
281
324
|
L[i][k] = factor;
|
|
282
|
-
for (let j = k; j < n; j++)
|
|
325
|
+
for (let j = k; j < n; j++)
|
|
326
|
+
A[i][j] = A[i][j].minus(factor.mul(A[k][j]));
|
|
283
327
|
}
|
|
284
328
|
}
|
|
285
329
|
|
|
286
|
-
const U = Array.from({ length: n }, (_, i) =>
|
|
330
|
+
const U = Array.from({ length: n }, (_, i) =>
|
|
331
|
+
Array.from({ length: n }, (_, j) => (j < i ? new Decimal(0) : A[i][j])),
|
|
332
|
+
);
|
|
287
333
|
const P = Matrix.zeros(n, n);
|
|
288
334
|
for (let i = 0; i < n; i++) P.data[i][Pvec[i]] = new Decimal(1);
|
|
289
335
|
return { L: new Matrix(L), U: new Matrix(U), P: P };
|
|
@@ -296,9 +342,10 @@ export class Matrix {
|
|
|
296
342
|
* @throws {Error} If matrix is not square
|
|
297
343
|
*/
|
|
298
344
|
determinant() {
|
|
299
|
-
if (!this.isSquare())
|
|
345
|
+
if (!this.isSquare())
|
|
346
|
+
throw new Error("Determinant only defined for square matrices");
|
|
300
347
|
const n = this.rows;
|
|
301
|
-
const { L, U, P } = this.lu();
|
|
348
|
+
const { L: _L, U, P } = this.lu();
|
|
302
349
|
let det = new Decimal(1);
|
|
303
350
|
for (let i = 0; i < n; i++) det = det.mul(U.data[i][i]);
|
|
304
351
|
// Compute permutation sign
|
|
@@ -323,13 +370,18 @@ export class Matrix {
|
|
|
323
370
|
* @throws {Error} If matrix is not square or is singular
|
|
324
371
|
*/
|
|
325
372
|
inverse() {
|
|
326
|
-
if (!this.isSquare())
|
|
373
|
+
if (!this.isSquare())
|
|
374
|
+
throw new Error("Inverse only defined for square matrices");
|
|
327
375
|
const n = this.rows;
|
|
328
376
|
// Create augmented matrix [A | I]
|
|
329
377
|
const aug = Array.from({ length: n }, (_, i) =>
|
|
330
378
|
Array.from({ length: 2 * n }, (_, j) =>
|
|
331
|
-
(j < n
|
|
332
|
-
|
|
379
|
+
(j < n
|
|
380
|
+
? new Decimal(this.data[i][j])
|
|
381
|
+
: j - n === i
|
|
382
|
+
? new Decimal(1)
|
|
383
|
+
: new Decimal(0)),
|
|
384
|
+
),
|
|
333
385
|
);
|
|
334
386
|
// Gauss-Jordan elimination
|
|
335
387
|
for (let col = 0; col < n; col++) {
|
|
@@ -338,12 +390,18 @@ export class Matrix {
|
|
|
338
390
|
let maxAbs = aug[col][col].abs();
|
|
339
391
|
for (let r = col + 1; r < n; r++) {
|
|
340
392
|
const aabs = aug[r][col].abs();
|
|
341
|
-
if (aabs.greaterThan(maxAbs)) {
|
|
393
|
+
if (aabs.greaterThan(maxAbs)) {
|
|
394
|
+
maxAbs = aabs;
|
|
395
|
+
pivot = r;
|
|
396
|
+
}
|
|
342
397
|
}
|
|
343
|
-
if (aug[pivot][col].isZero())
|
|
398
|
+
if (aug[pivot][col].isZero())
|
|
399
|
+
throw new Error("Singular matrix: inverse does not exist");
|
|
344
400
|
// Swap rows
|
|
345
401
|
if (pivot !== col) {
|
|
346
|
-
const tmp = aug[col];
|
|
402
|
+
const tmp = aug[col];
|
|
403
|
+
aug[col] = aug[pivot];
|
|
404
|
+
aug[pivot] = tmp;
|
|
347
405
|
}
|
|
348
406
|
// Scale pivot row
|
|
349
407
|
const pivval = aug[col][col];
|
|
@@ -353,11 +411,12 @@ export class Matrix {
|
|
|
353
411
|
if (r === col) continue;
|
|
354
412
|
const factor = aug[r][col];
|
|
355
413
|
if (factor.isZero()) continue;
|
|
356
|
-
for (let j = 0; j < 2 * n; j++)
|
|
414
|
+
for (let j = 0; j < 2 * n; j++)
|
|
415
|
+
aug[r][j] = aug[r][j].minus(factor.mul(aug[col][j]));
|
|
357
416
|
}
|
|
358
417
|
}
|
|
359
418
|
// Extract inverse from augmented matrix
|
|
360
|
-
const inv = aug.map(row => row.slice(n));
|
|
419
|
+
const inv = aug.map((row) => row.slice(n));
|
|
361
420
|
return new Matrix(inv);
|
|
362
421
|
}
|
|
363
422
|
|
|
@@ -372,13 +431,18 @@ export class Matrix {
|
|
|
372
431
|
let B;
|
|
373
432
|
if (b instanceof Vector) B = b;
|
|
374
433
|
else if (Array.isArray(b)) B = Vector.from(b);
|
|
375
|
-
else throw new Error(
|
|
376
|
-
if (!this.isSquare())
|
|
434
|
+
else throw new Error("b must be Vector or array");
|
|
435
|
+
if (!this.isSquare())
|
|
436
|
+
throw new Error("solve() only implemented for square matrices");
|
|
377
437
|
const n = this.rows;
|
|
378
|
-
if (B.length !== n)
|
|
438
|
+
if (B.length !== n)
|
|
439
|
+
throw new Error("dimension mismatch: b length must equal matrix rows");
|
|
379
440
|
// Create augmented matrix [A | b]
|
|
380
441
|
const aug = Array.from({ length: n }, (_, i) =>
|
|
381
|
-
Array.from(
|
|
442
|
+
Array.from(
|
|
443
|
+
{ length: n + 1 },
|
|
444
|
+
(_, j) => new Decimal(j < n ? this.data[i][j] : B.data[i]),
|
|
445
|
+
),
|
|
382
446
|
);
|
|
383
447
|
// Forward elimination
|
|
384
448
|
for (let col = 0; col < n; col++) {
|
|
@@ -386,14 +450,23 @@ export class Matrix {
|
|
|
386
450
|
let maxAbs = aug[col][col].abs();
|
|
387
451
|
for (let r = col + 1; r < n; r++) {
|
|
388
452
|
const aabs = aug[r][col].abs();
|
|
389
|
-
if (aabs.greaterThan(maxAbs)) {
|
|
453
|
+
if (aabs.greaterThan(maxAbs)) {
|
|
454
|
+
maxAbs = aabs;
|
|
455
|
+
pivot = r;
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
if (aug[pivot][col].isZero())
|
|
459
|
+
throw new Error("Singular matrix: no unique solution");
|
|
460
|
+
if (pivot !== col) {
|
|
461
|
+
const tmp = aug[col];
|
|
462
|
+
aug[col] = aug[pivot];
|
|
463
|
+
aug[pivot] = tmp;
|
|
390
464
|
}
|
|
391
|
-
if (aug[pivot][col].isZero()) throw new Error('Singular matrix: no unique solution');
|
|
392
|
-
if (pivot !== col) { const tmp = aug[col]; aug[col] = aug[pivot]; aug[pivot] = tmp; }
|
|
393
465
|
for (let r = col + 1; r < n; r++) {
|
|
394
466
|
const factor = aug[r][col].div(aug[col][col]);
|
|
395
467
|
if (factor.isZero()) continue;
|
|
396
|
-
for (let j = col; j < n + 1; j++)
|
|
468
|
+
for (let j = col; j < n + 1; j++)
|
|
469
|
+
aug[r][j] = aug[r][j].minus(factor.mul(aug[col][j]));
|
|
397
470
|
}
|
|
398
471
|
}
|
|
399
472
|
// Back substitution
|
|
@@ -412,8 +485,9 @@ export class Matrix {
|
|
|
412
485
|
* @returns {{Q: Matrix, R: Matrix}} QR decomposition components
|
|
413
486
|
*/
|
|
414
487
|
qr() {
|
|
415
|
-
const m = this.rows,
|
|
416
|
-
|
|
488
|
+
const m = this.rows,
|
|
489
|
+
n = this.cols;
|
|
490
|
+
const A = this.data.map((r) => r.map((v) => new Decimal(v)));
|
|
417
491
|
const Q = Matrix.identity(m).data;
|
|
418
492
|
|
|
419
493
|
for (let k = 0; k < Math.min(m, n); k++) {
|
|
@@ -442,21 +516,25 @@ export class Matrix {
|
|
|
442
516
|
// Apply Householder reflection to A
|
|
443
517
|
for (let j = k; j < n; j++) {
|
|
444
518
|
let dot = new Decimal(0);
|
|
445
|
-
for (let i = 0; i < v.length; i++)
|
|
446
|
-
|
|
519
|
+
for (let i = 0; i < v.length; i++)
|
|
520
|
+
dot = dot.plus(v[i].mul(A[k + i][j]));
|
|
521
|
+
for (let i = 0; i < v.length; i++)
|
|
522
|
+
A[k + i][j] = A[k + i][j].minus(new Decimal(2).mul(v[i]).mul(dot));
|
|
447
523
|
}
|
|
448
524
|
|
|
449
525
|
// Apply Householder reflection to Q
|
|
450
526
|
for (let j = 0; j < m; j++) {
|
|
451
527
|
let dot = new Decimal(0);
|
|
452
|
-
for (let i = 0; i < v.length; i++)
|
|
453
|
-
|
|
528
|
+
for (let i = 0; i < v.length; i++)
|
|
529
|
+
dot = dot.plus(v[i].mul(Q[k + i][j]));
|
|
530
|
+
for (let i = 0; i < v.length; i++)
|
|
531
|
+
Q[k + i][j] = Q[k + i][j].minus(new Decimal(2).mul(v[i]).mul(dot));
|
|
454
532
|
}
|
|
455
533
|
}
|
|
456
534
|
|
|
457
535
|
// Extract R (upper triangular part of A)
|
|
458
536
|
const R = Array.from({ length: m }, (_, i) =>
|
|
459
|
-
Array.from({ length: n }, (_, j) => (i <= j ? A[i][j] : new Decimal(0)))
|
|
537
|
+
Array.from({ length: n }, (_, j) => (i <= j ? A[i][j] : new Decimal(0))),
|
|
460
538
|
);
|
|
461
539
|
return { Q: new Matrix(Q).transpose(), R: new Matrix(R) };
|
|
462
540
|
}
|
|
@@ -472,7 +550,8 @@ export class Matrix {
|
|
|
472
550
|
*/
|
|
473
551
|
exp(options = {}) {
|
|
474
552
|
const n = this.rows;
|
|
475
|
-
if (!this.isSquare())
|
|
553
|
+
if (!this.isSquare())
|
|
554
|
+
throw new Error("Matrix exponential requires square matrix");
|
|
476
555
|
const ident = Matrix.identity(n);
|
|
477
556
|
|
|
478
557
|
// Compute infinity norm
|
|
@@ -480,7 +559,8 @@ export class Matrix {
|
|
|
480
559
|
let max = new Decimal(0);
|
|
481
560
|
for (let i = 0; i < M.rows; i++) {
|
|
482
561
|
let rowSum = new Decimal(0);
|
|
483
|
-
for (let j = 0; j < M.cols; j++)
|
|
562
|
+
for (let j = 0; j < M.cols; j++)
|
|
563
|
+
rowSum = rowSum.plus(M.data[i][j].abs());
|
|
484
564
|
if (rowSum.greaterThan(max)) max = rowSum;
|
|
485
565
|
}
|
|
486
566
|
return max;
|
|
@@ -498,7 +578,7 @@ export class Matrix {
|
|
|
498
578
|
|
|
499
579
|
// Taylor series
|
|
500
580
|
const maxIter = options.maxIter || 120;
|
|
501
|
-
const tol = new Decimal(options.tolerance ||
|
|
581
|
+
const tol = new Decimal(options.tolerance || "1e-40");
|
|
502
582
|
let term = ident.clone();
|
|
503
583
|
let result = ident.clone();
|
|
504
584
|
for (let k = 1; k < maxIter; k++) {
|