@danielsimonjr/mathts-matrix 0.1.13 → 0.1.14

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.
Files changed (2) hide show
  1. package/dist/index.js +163 -20
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -6544,6 +6544,84 @@ function handleZero(d, e, _U, V, zeroIdx, isDiagonal, _tolerance) {
6544
6544
  }
6545
6545
  }
6546
6546
  }
6547
+ function jacobiSVD(A) {
6548
+ const m = A.length;
6549
+ const n = A[0].length;
6550
+ const W = A.map((row2) => row2.slice());
6551
+ const V = eye(n);
6552
+ const EPS = 1e-15;
6553
+ const MAX_SWEEPS = 60;
6554
+ for (let sweep = 0; sweep < MAX_SWEEPS; sweep++) {
6555
+ let rotated = false;
6556
+ for (let p = 0; p < n - 1; p++) {
6557
+ for (let q = p + 1; q < n; q++) {
6558
+ let alpha = 0;
6559
+ let beta = 0;
6560
+ let gamma = 0;
6561
+ for (let i = 0; i < m; i++) {
6562
+ alpha += W[i][p] * W[i][p];
6563
+ gamma += W[i][q] * W[i][q];
6564
+ beta += W[i][p] * W[i][q];
6565
+ }
6566
+ if (Math.abs(beta) <= EPS * Math.sqrt(alpha * gamma) || alpha === 0 && gamma === 0) {
6567
+ continue;
6568
+ }
6569
+ rotated = true;
6570
+ const zeta = (gamma - alpha) / (2 * beta);
6571
+ const sign = zeta >= 0 ? 1 : -1;
6572
+ const t = sign / (Math.abs(zeta) + Math.sqrt(1 + zeta * zeta));
6573
+ const c = 1 / Math.sqrt(1 + t * t);
6574
+ const s = c * t;
6575
+ for (let i = 0; i < m; i++) {
6576
+ const wip = W[i][p];
6577
+ const wiq = W[i][q];
6578
+ W[i][p] = c * wip - s * wiq;
6579
+ W[i][q] = s * wip + c * wiq;
6580
+ }
6581
+ for (let i = 0; i < n; i++) {
6582
+ const vip = V[i][p];
6583
+ const viq = V[i][q];
6584
+ V[i][p] = c * vip - s * viq;
6585
+ V[i][q] = s * vip + c * viq;
6586
+ }
6587
+ }
6588
+ }
6589
+ if (!rotated) break;
6590
+ }
6591
+ const d = new Array(n).fill(0);
6592
+ const U = Array.from({ length: m }, () => new Array(n).fill(0));
6593
+ for (let j = 0; j < n; j++) {
6594
+ let norm4 = 0;
6595
+ for (let i = 0; i < m; i++) norm4 += W[i][j] * W[i][j];
6596
+ norm4 = Math.sqrt(norm4);
6597
+ d[j] = norm4;
6598
+ if (norm4 > 1e-300) {
6599
+ for (let i = 0; i < m; i++) U[i][j] = W[i][j] / norm4;
6600
+ }
6601
+ }
6602
+ for (let j = 0; j < n; j++) {
6603
+ if (d[j] > 1e-300) continue;
6604
+ const col = new Array(m).fill(0);
6605
+ for (let seed = 0; seed < m; seed++) {
6606
+ col.fill(0);
6607
+ col[seed] = 1;
6608
+ for (let c = 0; c < n; c++) {
6609
+ if (c === j) continue;
6610
+ let dot = 0;
6611
+ for (let i = 0; i < m; i++) dot += col[i] * U[i][c];
6612
+ for (let i = 0; i < m; i++) col[i] -= dot * U[i][c];
6613
+ }
6614
+ let nrm = 0;
6615
+ for (let i = 0; i < m; i++) nrm += col[i] * col[i];
6616
+ nrm = Math.sqrt(nrm);
6617
+ if (nrm > 1e-8) {
6618
+ for (let i = 0; i < m; i++) U[i][j] = col[i] / nrm;
6619
+ break;
6620
+ }
6621
+ }
6622
+ }
6623
+ return { d, U, V };
6624
+ }
6547
6625
  function svd(matrix2, options = {}) {
6548
6626
  const {
6549
6627
  maxIterations = DEFAULT_MAX_ITERATIONS,
@@ -6615,19 +6693,29 @@ function svd(matrix2, options = {}) {
6615
6693
  }
6616
6694
  svdStep(d, e, U, V, start, end);
6617
6695
  }
6618
- for (let i = 0; i < d.length; i++) {
6619
- if (d[i] < 0) {
6620
- d[i] = -d[i];
6621
- for (let j = 0; j < V.length; j++) {
6622
- V[j][i] = -V[j][i];
6623
- }
6624
- }
6625
- }
6626
- const indices = Array.from({ length: d.length }, (_, i) => i);
6627
- indices.sort((a, b) => d[b] - d[a]);
6628
- const sortedS = indices.map((i) => d[i]);
6629
- const sortedU = U.map((row2) => indices.map((i) => row2[i]));
6630
- const sortedV = V.map((row2) => indices.map((i) => row2[i]));
6696
+ let dFinal = d;
6697
+ let uFinal = U;
6698
+ let vFinal = V;
6699
+ const maxAbsD = d.reduce((mx, x) => Math.max(mx, Math.abs(x)), 0);
6700
+ if (maxAbsD > 0 && d.some((x) => Math.abs(x) <= rankTolerance * maxAbsD)) {
6701
+ const jac = jacobiSVD(A);
6702
+ dFinal = jac.d;
6703
+ uFinal = jac.U;
6704
+ vFinal = jac.V;
6705
+ }
6706
+ for (let i = 0; i < dFinal.length; i++) {
6707
+ if (dFinal[i] < 0) {
6708
+ dFinal[i] = -dFinal[i];
6709
+ for (let j = 0; j < vFinal.length; j++) {
6710
+ vFinal[j][i] = -vFinal[j][i];
6711
+ }
6712
+ }
6713
+ }
6714
+ const indices = Array.from({ length: dFinal.length }, (_, i) => i);
6715
+ indices.sort((a, b) => dFinal[b] - dFinal[a]);
6716
+ const sortedS = indices.map((i) => dFinal[i]);
6717
+ const sortedU = uFinal.map((row2) => indices.map((i) => row2[i]));
6718
+ const sortedV = vFinal.map((row2) => indices.map((i) => row2[i]));
6631
6719
  const maxS = sortedS[0] || 0;
6632
6720
  let rank = 0;
6633
6721
  for (const s of sortedS) {
@@ -7111,8 +7199,7 @@ function hessenbergReduce(A) {
7111
7199
  applyHouseholderRight(Q, v, beta, 0, k + 1);
7112
7200
  }
7113
7201
  }
7114
- for (let i = 0; i < n; i++)
7115
- for (let j = 0; j < i - 1; j++) H[i][j] = 0;
7202
+ for (let i = 0; i < n; i++) for (let j = 0; j < i - 1; j++) H[i][j] = 0;
7116
7203
  return { H, Q };
7117
7204
  }
7118
7205
  function givensParams(a, b) {
@@ -7217,6 +7304,57 @@ function qrStepDouble(H, Q, start, end) {
7217
7304
  applyGivensRight(H, gc, gs, end - 1, end, 0);
7218
7305
  applyGivensRight(Q, gc, gs, end - 1, end, 0);
7219
7306
  }
7307
+ function qrStepSingleShift(H, Q, start, end, shift) {
7308
+ let x = H[start][start] - shift;
7309
+ let y = H[start + 1][start];
7310
+ for (let k = start; k < end; k++) {
7311
+ const { c: gc, s: gs } = givensParams(x, y);
7312
+ applyGivensLeft(H, gc, gs, k, k + 1, Math.max(0, k - 1));
7313
+ applyGivensRight(H, gc, gs, k, k + 1, 0);
7314
+ applyGivensRight(Q, gc, gs, k, k + 1, 0);
7315
+ if (k < end - 1) {
7316
+ x = H[k + 1][k];
7317
+ y = H[k + 2][k];
7318
+ }
7319
+ }
7320
+ }
7321
+ function exceptionalShift(H, start, end) {
7322
+ const sub1 = Math.abs(H[end][end - 1]);
7323
+ const sub2 = end - 1 > start ? Math.abs(H[end - 1][end - 2]) : 0;
7324
+ return H[end][end] + 0.75 * (sub1 + sub2);
7325
+ }
7326
+ function standardize2x2Block(H, Q, p) {
7327
+ const q = p + 1;
7328
+ const a = H[p][p];
7329
+ const b = H[p][q];
7330
+ const c = H[q][p];
7331
+ const d = H[q][q];
7332
+ if (c === 0) return;
7333
+ const half = (a - d) / 2;
7334
+ const disc = half * half + b * c;
7335
+ if (disc < 0) return;
7336
+ const z = Math.sqrt(disc);
7337
+ const lambda = (a + d) / 2 + (half >= 0 ? z : -z);
7338
+ const r0 = Math.hypot(a - lambda, b);
7339
+ const r1 = Math.hypot(c, d - lambda);
7340
+ let u0;
7341
+ let u1;
7342
+ if (r0 >= r1) {
7343
+ u0 = b;
7344
+ u1 = lambda - a;
7345
+ } else {
7346
+ u0 = lambda - d;
7347
+ u1 = c;
7348
+ }
7349
+ const norm4 = Math.hypot(u0, u1);
7350
+ if (norm4 === 0) return;
7351
+ const cs = u0 / norm4;
7352
+ const sn = -u1 / norm4;
7353
+ applyGivensLeft(H, cs, sn, p, q, 0);
7354
+ applyGivensRight(H, cs, sn, p, q, 0);
7355
+ applyGivensRight(Q, cs, sn, p, q, 0);
7356
+ H[q][p] = 0;
7357
+ }
7220
7358
  function schurRaw(A, maxIterations, tolerance) {
7221
7359
  const n = A.length;
7222
7360
  if (n === 1) {
@@ -7225,7 +7363,10 @@ function schurRaw(A, maxIterations, tolerance) {
7225
7363
  const { H, Q } = hessenbergReduce(A);
7226
7364
  let end = n - 1;
7227
7365
  let iter = 0;
7366
+ let iterSinceDeflation = 0;
7367
+ const EXCEPTIONAL_EVERY = 10;
7228
7368
  while (end > 0 && iter < maxIterations) {
7369
+ const endBefore = end;
7229
7370
  let start = end;
7230
7371
  while (start > 0) {
7231
7372
  const scale2 = Math.abs(H[start - 1][start - 1]) + Math.abs(H[start][start]);
@@ -7238,14 +7379,16 @@ function schurRaw(A, maxIterations, tolerance) {
7238
7379
  if (start === end) {
7239
7380
  end--;
7240
7381
  } else if (start === end - 1) {
7382
+ standardize2x2Block(H, Q, start);
7241
7383
  end -= 2;
7384
+ } else if (iterSinceDeflation > 0 && iterSinceDeflation % EXCEPTIONAL_EVERY === 0) {
7385
+ qrStepSingleShift(H, Q, start, end, exceptionalShift(H, start, end));
7386
+ } else if (end - start >= 2) {
7387
+ qrStepDouble(H, Q, start, end);
7242
7388
  } else {
7243
- if (end - start >= 2) {
7244
- qrStepDouble(H, Q, start, end);
7245
- } else {
7246
- qrStepSingle(H, Q, start, end);
7247
- }
7389
+ qrStepSingle(H, Q, start, end);
7248
7390
  }
7391
+ iterSinceDeflation = end < endBefore ? 0 : iterSinceDeflation + 1;
7249
7392
  iter++;
7250
7393
  }
7251
7394
  return { H, Q };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@danielsimonjr/mathts-matrix",
3
- "version": "0.1.13",
3
+ "version": "0.1.14",
4
4
  "description": "Matrix operations for MathTS with WASM/WebGPU backend support",
5
5
  "author": "Daniel Simon Jr.",
6
6
  "license": "MIT",
@@ -32,7 +32,7 @@
32
32
  "build:prod": "tsup src/index.ts --format esm --dts --clean --minify --treeshake && node scripts/copy-wasm.mjs"
33
33
  },
34
34
  "dependencies": {
35
- "@danielsimonjr/mathts-core": "^0.3.0",
35
+ "@danielsimonjr/mathts-core": "^0.4.0",
36
36
  "@danielsimonjr/mathts-parallel": "^0.3.0"
37
37
  },
38
38
  "devDependencies": {