@danielsimonjr/mathts-matrix 0.1.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/README.md ADDED
@@ -0,0 +1,66 @@
1
+ # @danielsimonjr/mathts-matrix
2
+
3
+ High-performance matrix operations with multi-backend support (JS/WASM/GPU).
4
+
5
+ ## Installation
6
+
7
+ ```bash
8
+ npm install @danielsimonjr/mathts-matrix
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ ```typescript
14
+ import { Matrix, DenseMatrix, backends } from '@danielsimonjr/mathts-matrix';
15
+
16
+ // Create a matrix from a 2D array
17
+ const A = Matrix.from([
18
+ [1, 2, 3],
19
+ [4, 5, 6],
20
+ [7, 8, 9]
21
+ ]);
22
+
23
+ // Basic operations (coming soon)
24
+ const det = A.determinant();
25
+ const inv = A.inverse();
26
+ const eig = A.eigenvalues();
27
+
28
+ // Configure backend
29
+ backends.configure({
30
+ backend: 'wasm', // Force WASM backend
31
+ autoBackend: true, // Or let it auto-select
32
+ wasmThreshold: 1000, // Switch to WASM above 1000 elements
33
+ gpuThreshold: 100000, // Switch to GPU above 100K elements
34
+ });
35
+ ```
36
+
37
+ ## Backends
38
+
39
+ | Backend | Trigger | Performance |
40
+ |---------|---------|-------------|
41
+ | **JS** | Default | 1x baseline |
42
+ | **WASM** | >1K elements | ~10x faster |
43
+ | **GPU** | >100K elements | ~100x faster |
44
+
45
+ ## Matrix Types
46
+
47
+ - `DenseMatrix` - Standard dense storage (row-major)
48
+ - `SparseMatrix` - CSR format for sparse data
49
+
50
+ ## Status
51
+
52
+ This package is under active development. Currently implemented:
53
+
54
+ - [x] Type definitions
55
+ - [x] Backend infrastructure
56
+ - [x] Dense matrix structure
57
+ - [x] Sparse matrix (CSR) structure
58
+ - [ ] Matrix operations (add, multiply, etc.)
59
+ - [ ] WASM backend
60
+ - [ ] GPU backend
61
+ - [ ] LU/QR/SVD decompositions
62
+ - [ ] Eigenvalue solvers
63
+
64
+ ## License
65
+
66
+ MIT
@@ -0,0 +1,497 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
3
+ var __getOwnPropNames = Object.getOwnPropertyNames;
4
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
5
+ var __esm = (fn, res) => function __init() {
6
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
7
+ };
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
21
+
22
+ // src/operations/eig.ts
23
+ var DEFAULT_MAX_ITERATIONS = 1e3;
24
+ var DEFAULT_TOLERANCE = 1e-12;
25
+ function isSymmetric(matrix, tolerance = 1e-10) {
26
+ const n = matrix.length;
27
+ if (n === 0) return true;
28
+ if (matrix[0].length !== n) return false;
29
+ for (let i = 0; i < n; i++) {
30
+ for (let j = i + 1; j < n; j++) {
31
+ if (Math.abs(matrix[i][j] - matrix[j][i]) > tolerance) {
32
+ return false;
33
+ }
34
+ }
35
+ }
36
+ return true;
37
+ }
38
+ function eye(n) {
39
+ const I = Array.from({ length: n }, () => new Array(n).fill(0));
40
+ for (let i = 0; i < n; i++) {
41
+ I[i][i] = 1;
42
+ }
43
+ return I;
44
+ }
45
+ function cloneMatrix(A) {
46
+ return A.map((row) => [...row]);
47
+ }
48
+ function householder(x) {
49
+ const n = x.length;
50
+ let sigma = 0;
51
+ for (let i = 1; i < n; i++) {
52
+ sigma += x[i] * x[i];
53
+ }
54
+ const v = [...x];
55
+ v[0] = 1;
56
+ if (sigma === 0 && x[0] >= 0) {
57
+ return { v, beta: 0 };
58
+ } else if (sigma === 0 && x[0] < 0) {
59
+ return { v, beta: -2 };
60
+ } else {
61
+ const mu = Math.sqrt(x[0] * x[0] + sigma);
62
+ if (x[0] <= 0) {
63
+ v[0] = x[0] - mu;
64
+ } else {
65
+ v[0] = -sigma / (x[0] + mu);
66
+ }
67
+ const beta = 2 * v[0] * v[0] / (sigma + v[0] * v[0]);
68
+ const v0 = v[0];
69
+ for (let i = 0; i < n; i++) {
70
+ v[i] /= v0;
71
+ }
72
+ return { v, beta };
73
+ }
74
+ }
75
+ function applyHouseholderLeft(A, v, beta, startRow, startCol) {
76
+ const n = A[0].length;
77
+ const len = v.length;
78
+ for (let j = startCol; j < n; j++) {
79
+ let dot = 0;
80
+ for (let i = 0; i < len; i++) {
81
+ dot += v[i] * A[startRow + i][j];
82
+ }
83
+ dot *= beta;
84
+ for (let i = 0; i < len; i++) {
85
+ A[startRow + i][j] -= dot * v[i];
86
+ }
87
+ }
88
+ }
89
+ function applyHouseholderRight(A, v, beta, startRow, startCol) {
90
+ const m = A.length;
91
+ const len = v.length;
92
+ for (let i = startRow; i < m; i++) {
93
+ let dot = 0;
94
+ for (let j = 0; j < len; j++) {
95
+ dot += A[i][startCol + j] * v[j];
96
+ }
97
+ dot *= beta;
98
+ for (let j = 0; j < len; j++) {
99
+ A[i][startCol + j] -= dot * v[j];
100
+ }
101
+ }
102
+ }
103
+ function hessenberg(A) {
104
+ const n = A.length;
105
+ const H = cloneMatrix(A);
106
+ let Q = eye(n);
107
+ for (let k = 0; k < n - 2; k++) {
108
+ const x = [];
109
+ for (let i = k + 1; i < n; i++) {
110
+ x.push(H[i][k]);
111
+ }
112
+ const { v, beta } = householder(x);
113
+ if (beta !== 0) {
114
+ applyHouseholderLeft(H, v, beta, k + 1, k);
115
+ applyHouseholderRight(H, v, beta, 0, k + 1);
116
+ applyHouseholderRight(Q, v, beta, 0, k + 1);
117
+ }
118
+ }
119
+ for (let i = 0; i < n; i++) {
120
+ for (let j = 0; j < i - 1; j++) {
121
+ H[i][j] = 0;
122
+ }
123
+ }
124
+ return { H, Q };
125
+ }
126
+ function givens(a, b) {
127
+ if (b === 0) {
128
+ return { c: 1, s: 0 };
129
+ } else if (Math.abs(b) > Math.abs(a)) {
130
+ const t = -a / b;
131
+ const s = 1 / Math.sqrt(1 + t * t);
132
+ return { c: s * t, s };
133
+ } else {
134
+ const t = -b / a;
135
+ const c = 1 / Math.sqrt(1 + t * t);
136
+ return { c, s: c * t };
137
+ }
138
+ }
139
+ function applyGivensLeft(A, c, s, i, k, startCol) {
140
+ const n = A[0].length;
141
+ for (let j = startCol; j < n; j++) {
142
+ const temp = c * A[i][j] - s * A[k][j];
143
+ A[k][j] = s * A[i][j] + c * A[k][j];
144
+ A[i][j] = temp;
145
+ }
146
+ }
147
+ function applyGivensRight(A, c, s, i, k, startRow) {
148
+ const m = A.length;
149
+ for (let j = startRow; j < m; j++) {
150
+ const temp = c * A[j][i] - s * A[j][k];
151
+ A[j][k] = s * A[j][i] + c * A[j][k];
152
+ A[j][i] = temp;
153
+ }
154
+ }
155
+ function qrStep(H, Q, start, end) {
156
+ const n = end - start + 1;
157
+ if (n < 2) return;
158
+ const a = H[end - 1][end - 1];
159
+ const b = H[end - 1][end];
160
+ const c = H[end][end - 1];
161
+ const d = H[end][end];
162
+ const trace = a + d;
163
+ const det = a * d - b * c;
164
+ const disc = trace * trace - 4 * det;
165
+ let shift;
166
+ if (disc >= 0) {
167
+ const sqrtDisc = Math.sqrt(disc);
168
+ const e1 = (trace + sqrtDisc) / 2;
169
+ const e2 = (trace - sqrtDisc) / 2;
170
+ shift = Math.abs(e1 - d) < Math.abs(e2 - d) ? e1 : e2;
171
+ } else {
172
+ shift = d;
173
+ }
174
+ let x = H[start][start] - shift;
175
+ let y = H[start + 1][start];
176
+ for (let k = start; k < end; k++) {
177
+ const { c: c2, s } = givens(x, y);
178
+ applyGivensLeft(H, c2, s, k, k + 1, Math.max(0, k - 1));
179
+ applyGivensRight(H, c2, s, k, k + 1, 0);
180
+ applyGivensRight(Q, c2, s, k, k + 1, 0);
181
+ if (k < end - 1) {
182
+ x = H[k + 1][k];
183
+ y = H[k + 2][k];
184
+ }
185
+ }
186
+ }
187
+ function doubleShiftQR(H, Q, start, end) {
188
+ const n = H.length;
189
+ const a = H[end - 1][end - 1];
190
+ const b = H[end - 1][end];
191
+ const c = H[end][end - 1];
192
+ const d = H[end][end];
193
+ const s = a + d;
194
+ const t = a * d - b * c;
195
+ let x = H[start][start] * H[start][start] + H[start][start + 1] * H[start + 1][start] - s * H[start][start] + t;
196
+ let y = H[start + 1][start] * (H[start][start] + H[start + 1][start + 1] - s);
197
+ let z = H[start + 1][start] * H[start + 2][start + 1];
198
+ for (let k = start; k <= end - 2; k++) {
199
+ const { v, beta } = householder([x, y, z]);
200
+ const q = Math.max(start, k - 1);
201
+ for (let j = q; j < n; j++) {
202
+ let dot = v[0] * H[k][j] + v[1] * H[k + 1][j] + v[2] * H[k + 2][j];
203
+ dot *= beta;
204
+ H[k][j] -= dot * v[0];
205
+ H[k + 1][j] -= dot * v[1];
206
+ H[k + 2][j] -= dot * v[2];
207
+ }
208
+ const r = Math.min(k + 4, end + 1);
209
+ for (let i = 0; i < r; i++) {
210
+ let dot = v[0] * H[i][k] + v[1] * H[i][k + 1] + v[2] * H[i][k + 2];
211
+ dot *= beta;
212
+ H[i][k] -= dot * v[0];
213
+ H[i][k + 1] -= dot * v[1];
214
+ H[i][k + 2] -= dot * v[2];
215
+ }
216
+ for (let i = 0; i < n; i++) {
217
+ let dot = v[0] * Q[i][k] + v[1] * Q[i][k + 1] + v[2] * Q[i][k + 2];
218
+ dot *= beta;
219
+ Q[i][k] -= dot * v[0];
220
+ Q[i][k + 1] -= dot * v[1];
221
+ Q[i][k + 2] -= dot * v[2];
222
+ }
223
+ x = H[k + 1][k];
224
+ y = H[k + 2][k];
225
+ if (k < end - 2) {
226
+ z = H[k + 3][k];
227
+ }
228
+ }
229
+ const { c: cFinal, s: sFinal } = givens(x, y);
230
+ applyGivensLeft(H, cFinal, sFinal, end - 1, end, end - 2);
231
+ applyGivensRight(H, cFinal, sFinal, end - 1, end, 0);
232
+ applyGivensRight(Q, cFinal, sFinal, end - 1, end, 0);
233
+ }
234
+ function extractEigenvalues(H, tolerance) {
235
+ const n = H.length;
236
+ const eigenvalues = [];
237
+ let i = 0;
238
+ while (i < n) {
239
+ if (i === n - 1 || Math.abs(H[i + 1][i]) <= tolerance) {
240
+ eigenvalues.push({ re: H[i][i], im: 0 });
241
+ i++;
242
+ } else {
243
+ const a = H[i][i];
244
+ const b = H[i][i + 1];
245
+ const c = H[i + 1][i];
246
+ const d = H[i + 1][i + 1];
247
+ const trace = a + d;
248
+ const det = a * d - b * c;
249
+ const disc = trace * trace - 4 * det;
250
+ if (disc >= 0) {
251
+ const sqrtDisc = Math.sqrt(disc);
252
+ eigenvalues.push({ re: (trace + sqrtDisc) / 2, im: 0 });
253
+ eigenvalues.push({ re: (trace - sqrtDisc) / 2, im: 0 });
254
+ } else {
255
+ const realPart = trace / 2;
256
+ const imagPart = Math.sqrt(-disc) / 2;
257
+ eigenvalues.push({ re: realPart, im: imagPart });
258
+ eigenvalues.push({ re: realPart, im: -imagPart });
259
+ }
260
+ i += 2;
261
+ }
262
+ }
263
+ return eigenvalues;
264
+ }
265
+ function inverseIteration(A, eigenvalue, tolerance, maxIterations) {
266
+ const n = A.length;
267
+ if (eigenvalue.im !== 0) {
268
+ return new Array(n).fill(0);
269
+ }
270
+ const lambda = eigenvalue.re;
271
+ const shifted = cloneMatrix(A);
272
+ for (let i = 0; i < n; i++) {
273
+ shifted[i][i] -= lambda;
274
+ }
275
+ for (let i = 0; i < n; i++) {
276
+ shifted[i][i] += tolerance * 1e3;
277
+ }
278
+ const LU = cloneMatrix(shifted);
279
+ const perm = Array.from({ length: n }, (_, i) => i);
280
+ for (let k = 0; k < n; k++) {
281
+ let maxVal = Math.abs(LU[k][k]);
282
+ let maxIdx = k;
283
+ for (let i = k + 1; i < n; i++) {
284
+ if (Math.abs(LU[i][k]) > maxVal) {
285
+ maxVal = Math.abs(LU[i][k]);
286
+ maxIdx = i;
287
+ }
288
+ }
289
+ if (maxIdx !== k) {
290
+ [LU[k], LU[maxIdx]] = [LU[maxIdx], LU[k]];
291
+ [perm[k], perm[maxIdx]] = [perm[maxIdx], perm[k]];
292
+ }
293
+ if (Math.abs(LU[k][k]) < tolerance) {
294
+ LU[k][k] = tolerance;
295
+ }
296
+ for (let i = k + 1; i < n; i++) {
297
+ LU[i][k] /= LU[k][k];
298
+ for (let j = k + 1; j < n; j++) {
299
+ LU[i][j] -= LU[i][k] * LU[k][j];
300
+ }
301
+ }
302
+ }
303
+ let v = new Array(n).fill(1);
304
+ let prevNorm = 0;
305
+ for (let iter = 0; iter < maxIterations; iter++) {
306
+ const y = new Array(n).fill(0);
307
+ const b = new Array(n);
308
+ for (let i = 0; i < n; i++) {
309
+ b[i] = v[perm[i]];
310
+ }
311
+ for (let i = 0; i < n; i++) {
312
+ y[i] = b[i];
313
+ for (let j = 0; j < i; j++) {
314
+ y[i] -= LU[i][j] * y[j];
315
+ }
316
+ }
317
+ const x = new Array(n).fill(0);
318
+ for (let i = n - 1; i >= 0; i--) {
319
+ x[i] = y[i];
320
+ for (let j = i + 1; j < n; j++) {
321
+ x[i] -= LU[i][j] * x[j];
322
+ }
323
+ x[i] /= LU[i][i];
324
+ }
325
+ let norm = 0;
326
+ for (let i = 0; i < n; i++) {
327
+ norm += x[i] * x[i];
328
+ }
329
+ norm = Math.sqrt(norm);
330
+ if (norm < tolerance) {
331
+ return x;
332
+ }
333
+ for (let i = 0; i < n; i++) {
334
+ v[i] = x[i] / norm;
335
+ }
336
+ if (Math.abs(norm - prevNorm) < tolerance * norm) {
337
+ break;
338
+ }
339
+ prevNorm = norm;
340
+ }
341
+ return v;
342
+ }
343
+ function eig(matrix, options = {}) {
344
+ const {
345
+ maxIterations = DEFAULT_MAX_ITERATIONS,
346
+ tolerance = DEFAULT_TOLERANCE,
347
+ computeVectors = true
348
+ } = options;
349
+ let A;
350
+ if (matrix instanceof Float64Array) {
351
+ const n2 = Math.sqrt(matrix.length);
352
+ if (n2 !== Math.floor(n2)) {
353
+ throw new Error("Float64Array length must be a perfect square");
354
+ }
355
+ A = Array.from(
356
+ { length: n2 },
357
+ (_, i) => Array.from({ length: n2 }, (_2, j) => matrix[i * n2 + j])
358
+ );
359
+ } else {
360
+ A = matrix;
361
+ }
362
+ const n = A.length;
363
+ if (n === 0) {
364
+ return { values: [], vectors: [], isSymmetric: true };
365
+ }
366
+ for (let i = 0; i < n; i++) {
367
+ if (A[i].length !== n) {
368
+ throw new Error("Matrix must be square");
369
+ }
370
+ }
371
+ const symmetric = isSymmetric(A);
372
+ if (n === 1) {
373
+ return {
374
+ values: [{ re: A[0][0], im: 0 }],
375
+ vectors: [[1]],
376
+ isSymmetric: symmetric
377
+ };
378
+ }
379
+ if (n === 2) {
380
+ const a = A[0][0], b = A[0][1], c = A[1][0], d = A[1][1];
381
+ const trace = a + d;
382
+ const det = a * d - b * c;
383
+ const disc = trace * trace - 4 * det;
384
+ let values2;
385
+ let vectors2;
386
+ if (disc >= 0) {
387
+ const sqrtDisc = Math.sqrt(disc);
388
+ const e1 = (trace + sqrtDisc) / 2;
389
+ const e2 = (trace - sqrtDisc) / 2;
390
+ values2 = [{ re: e1, im: 0 }, { re: e2, im: 0 }];
391
+ if (computeVectors) {
392
+ vectors2 = [];
393
+ for (const e of [e1, e2]) {
394
+ if (Math.abs(b) > tolerance) {
395
+ const v = [b, e - a];
396
+ const norm = Math.sqrt(v[0] * v[0] + v[1] * v[1]);
397
+ vectors2.push([v[0] / norm, v[1] / norm]);
398
+ } else if (Math.abs(c) > tolerance) {
399
+ const v = [e - d, c];
400
+ const norm = Math.sqrt(v[0] * v[0] + v[1] * v[1]);
401
+ vectors2.push([v[0] / norm, v[1] / norm]);
402
+ } else {
403
+ vectors2.push(e === a ? [1, 0] : [0, 1]);
404
+ }
405
+ }
406
+ } else {
407
+ vectors2 = eye(2);
408
+ }
409
+ } else {
410
+ const realPart = trace / 2;
411
+ const imagPart = Math.sqrt(-disc) / 2;
412
+ values2 = [
413
+ { re: realPart, im: imagPart },
414
+ { re: realPart, im: -imagPart }
415
+ ];
416
+ vectors2 = eye(2);
417
+ }
418
+ return { values: values2, vectors: vectors2, isSymmetric: symmetric };
419
+ }
420
+ const { H, Q } = hessenberg(A);
421
+ let end = n - 1;
422
+ let iter = 0;
423
+ while (end > 0 && iter < maxIterations) {
424
+ let start = end;
425
+ while (start > 0) {
426
+ const scale = Math.abs(H[start - 1][start - 1]) + Math.abs(H[start][start]);
427
+ if (Math.abs(H[start][start - 1]) <= tolerance * scale) {
428
+ H[start][start - 1] = 0;
429
+ break;
430
+ }
431
+ start--;
432
+ }
433
+ if (start === end) {
434
+ end--;
435
+ } else if (start === end - 1) {
436
+ end -= 2;
437
+ } else {
438
+ if (iter % 30 === 29) {
439
+ qrStep(H, Q, start, end);
440
+ } else if (symmetric) {
441
+ qrStep(H, Q, start, end);
442
+ } else {
443
+ doubleShiftQR(H, Q, start, end);
444
+ }
445
+ }
446
+ iter++;
447
+ }
448
+ const values = extractEigenvalues(H, tolerance);
449
+ let vectors;
450
+ if (computeVectors) {
451
+ vectors = [];
452
+ for (const eigenvalue of values) {
453
+ const v = inverseIteration(A, eigenvalue, tolerance, 100);
454
+ vectors.push(v);
455
+ }
456
+ } else {
457
+ vectors = eye(n);
458
+ }
459
+ return { values, vectors, isSymmetric: symmetric };
460
+ }
461
+ function eigvals(matrix, options) {
462
+ return eig(matrix, { ...options, computeVectors: false }).values;
463
+ }
464
+ function powerIteration(matrix, options = {}) {
465
+ const { maxIterations = 1e3, tolerance = 1e-12 } = options;
466
+ const n = matrix.length;
467
+ let v = Array.from({ length: n }, () => Math.random() - 0.5);
468
+ let norm = Math.sqrt(v.reduce((sum, x) => sum + x * x, 0));
469
+ v = v.map((x) => x / norm);
470
+ let eigenvalue = 0;
471
+ let prevEigenvalue = 0;
472
+ for (let iter = 0; iter < maxIterations; iter++) {
473
+ const w = new Array(n).fill(0);
474
+ for (let i = 0; i < n; i++) {
475
+ for (let j = 0; j < n; j++) {
476
+ w[i] += matrix[i][j] * v[j];
477
+ }
478
+ }
479
+ eigenvalue = v.reduce((sum, vi, i) => sum + vi * w[i], 0);
480
+ norm = Math.sqrt(w.reduce((sum, x) => sum + x * x, 0));
481
+ v = w.map((x) => x / norm);
482
+ if (Math.abs(eigenvalue - prevEigenvalue) < tolerance * Math.abs(eigenvalue)) {
483
+ break;
484
+ }
485
+ prevEigenvalue = eigenvalue;
486
+ }
487
+ return { value: eigenvalue, vector: v };
488
+ }
489
+
490
+ export {
491
+ __esm,
492
+ __export,
493
+ __toCommonJS,
494
+ eig,
495
+ eigvals,
496
+ powerIteration
497
+ };
@@ -0,0 +1,10 @@
1
+ import {
2
+ eig,
3
+ eigvals,
4
+ powerIteration
5
+ } from "./chunk-I6QDZ7SC.js";
6
+ export {
7
+ eig,
8
+ eigvals,
9
+ powerIteration
10
+ };