@thi.ng/tensors
Version:
1D/2D/3D/4D tensors with extensible polymorphic operations and customizable storage
247 lines (246 loc) • 6.21 kB
JavaScript
import { swap } from "@thi.ng/arrays/swap";
import { illegalArgs } from "@thi.ng/errors/illegal-arguments";
import { illegalState } from "@thi.ng/errors/illegal-state";
import { zeroes } from "./tensor.js";
const { abs, sqrt } = Math;
const svd = (mat, maxIter = 50) => {
const [M, N] = mat.shape;
if (M < N) illegalArgs("need more rows than columns");
const $u = mat.pack();
const $q = zeroes([N]);
const $v = zeroes([N, N]);
const u = $u.data;
const q = $q.data;
const v = $v.data;
const e = new Array(N).fill(0);
let prec = 2 ** -52;
const tolerance = 1e-64 / prec;
let c = 0;
let i = 0;
let j = 0;
let k = 0;
let l = 0;
let f = 0;
let g = 0;
let h = 0;
let x = 0;
let y = 0;
let z = 0;
let scale = 0;
let $i;
let $j;
let $k;
for (i = 0, $i = 0; i < N; i++, $i += N) {
e[i] = g;
scale = 0;
l = i + 1;
for (j = i, $j = $i + i; j < M; j++, $j += N) {
scale += u[$j] ** 2;
}
if (scale <= tolerance) g = 0;
else {
f = u[$i + i];
g = sqrt(scale);
if (f >= 0) g *= -1;
h = f * g - scale;
u[$i + i] = f - g;
for (j = l; j < N; j++) {
scale = 0;
for (k = i, $k = $i; k < M; k++, $k += N) {
scale += u[$k + i] * u[$k + j];
}
f = scale / h;
for (k = i, $k = $i; k < M; k++, $k += N) {
u[$k + j] += f * u[$k + i];
}
}
}
q[i] = g;
scale = 0;
for (j = l; j < N; j++) {
scale += u[$i + j] ** 2;
}
if (scale <= tolerance) g = 0;
else {
f = u[$i + i + 1];
g = sqrt(scale);
if (f >= 0) g *= -1;
h = f * g - scale;
u[$i + i + 1] = f - g;
for (j = l; j < N; j++) e[j] = u[$i + j] / h;
for (j = l, $j = j * N; j < M; j++, $j += N) {
scale = 0;
for (k = l; k < N; k++) {
scale += u[$j + k] * u[$i + k];
}
for (k = l; k < N; k++) {
u[$j + k] += scale * e[k];
}
}
}
y = abs(q[i]) + abs(e[i]);
if (y > x) x = y;
}
for (i = N; i-- > 0; ) {
$i = i * N;
if (g !== 0) {
h = g * u[$i + i + 1];
for (j = l, $j = j * N + i; j < N; j++, $j += N) {
v[$j] = u[$i + j] / h;
}
for (j = l; j < N; j++) {
scale = 0;
for (k = l, $k = k * N + j; k < N; k++, $k += N) {
scale += u[$i + k] * v[$k];
}
for (k = l, $k = k * N; k < N; k++, $k += N) {
v[$k + j] += scale * v[$k + i];
}
}
}
for (j = l, $j = j * N + i; j < N; j++, $j += N) {
v[$i + j] = 0;
v[$j] = 0;
}
v[$i + i] = 1;
g = e[i];
l = i;
}
for (i = N; i-- > 0; ) {
$i = i * N;
l = i + 1;
g = q[i];
for (j = l; j < N; j++) u[$i + j] = 0;
if (g !== 0) {
h = u[$i + i] * g;
for (j = l; j < N; j++) {
scale = 0;
for (k = l, $k = k * N; k < M; k++, $k += N) {
scale += u[$k + i] * u[$k + j];
}
f = scale / h;
for (k = i, $k = k * N; k < M; k++, $k += N) {
u[$k + j] += f * u[$k + i];
}
}
for (j = i, $j = $i + i; j < M; j++, $j += N) {
u[$j] /= g;
}
} else {
for (j = i, $j = $i + i; j < M; j++, $j += N) u[$j] = 0;
}
u[$i + i]++;
}
prec *= x;
for (k = N; k-- > 0; ) {
for (let iter = 0; iter <= maxIter; iter++) {
let testConvergance = false;
for (l = k; l >= 0; l--) {
if (abs(e[l]) <= prec) {
testConvergance = true;
break;
}
if (abs(q[l - 1]) <= prec) break;
}
if (!testConvergance) {
c = 0;
scale = 1;
const l1 = l - 1;
for (i = l; i <= k; i++) {
f = scale * e[i];
e[i] *= c;
if (abs(f) <= prec) break;
g = q[i];
h = __pythag(f, g);
q[i] = h;
c = g / h;
scale = -f / h;
for (j = 0, $j = 0; j < M; j++, $j += N) {
y = u[$j + l1];
z = u[$j + i];
u[$j + l1] = y * c + z * scale;
u[$j + i] = -y * scale + z * c;
}
}
}
z = q[k];
if (l === k) {
if (z < 0) {
q[k] = -z;
for (j = 0, $j = k; j < N; j++, $j += N) {
v[$j] *= -1;
}
}
break;
}
if (iter >= maxIter) illegalState("no convergence");
x = q[l];
y = q[k - 1];
g = e[k - 1];
h = e[k];
f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2 * h * y);
g = __pythag(f, 1);
if (f < 0) f = ((x - z) * (x + z) + h * (y / (f - g) - h)) / x;
else f = ((x - z) * (x + z) + h * (y / (f + g) - h)) / x;
c = 1;
scale = 1;
for (i = l + 1; i <= k; i++) {
g = e[i];
y = q[i];
h = scale * g;
g = c * g;
z = __pythag(f, h);
e[i - 1] = z;
c = f / z;
scale = h / z;
f = x * c + g * scale;
g = -x * scale + g * c;
h = y * scale;
y = y * c;
for (j = 0, $j = i; j < N; j++, $j += N) {
x = v[$j - 1];
z = v[$j];
v[$j - 1] = x * c + z * scale;
v[$j] = -x * scale + z * c;
}
z = __pythag(f, h);
q[i - 1] = z;
c = f / z;
scale = h / z;
f = c * g + scale * y;
x = -scale * g + c * y;
for (j = 0, $j = i; j < M; j++, $j += N) {
y = u[$j - 1];
z = u[$j];
u[$j - 1] = y * c + z * scale;
u[$j] = -y * scale + z * c;
}
}
e[l] = 0;
e[k] = f;
q[k] = x;
}
}
for (i = 0; i < N; i++) if (q[i] < prec) q[i] = 0;
for (i = 0; i < N; i++) {
for (j = i; j-- > 0; ) {
if (q[j] < q[i]) {
swap(q, i, j);
for (k = 0, $k = 0; k < M; k++, $k += N)
swap(u, $k + i, $k + j);
for (k = 0, $k = 0; k < N; k++, $k += N)
swap(v, $k + i, $k + j);
i = j;
}
}
}
return { u: $u, v: $v, d: $q };
};
const __pythag = (a, b) => {
a = abs(a);
b = abs(b);
return a > b ? a * sqrt(1 + b * b / a / a) : b !== 0 ? b * sqrt(1 + a * a / b / b) : a;
};
export {
svd
};