SVD constructor
SVD(
- Array2d Arg
Construct the singular value decomposition Structure to access U, S and V.
Arg
Rectangular matrix
Implementation
SVD(Array2d Arg) {
// Derived from LINPACK code.
// Initialize.
_m = Arg.row;
_n = Arg.column;
// Apparently the failing cases are only a proper subset of (m<n),
// so let's not throw error. Correct fix to come later?
Array2d A;
var specialCaseMoreColumns = false;
var specialCaseMoreRows = false;
if (_m < _n) {
specialCaseMoreColumns = true;
_m = Arg.column;
_n = Arg.column;
A = Array2d.fixed(_m, _n);
Arg.asMap().forEach((i, row) {
row.asMap().forEach((j, elem) {
A[i][j] = elem;
});
});
} else if (_m > _n) {
specialCaseMoreRows = true;
_m = Arg.row;
_n = Arg.row;
A = Array2d.fixed(_m, _n);
Arg.asMap().forEach((i, row) {
row.asMap().forEach((j, elem) {
A[i][j] = elem;
});
});
} else {
A = Arg.copy();
}
var nu = math.min(_m, _n).toInt();
_s = Array.fixed(math.min(_m + 1, _n));
_U = Array2d.fixed(_m, nu, initialValue: 0);
_V = Array2d.fixed(_n, _n);
var e = Array.fixed(_n);
var work = Array.fixed(_m);
var wantu = true;
var wantv = true;
// Reduce A to bidiagonal form, storing the diagonal elements
// in s and the super-diagonal elements in e.
var nct = math.min(_m - 1, _n);
var nrt = math.max(0, math.min(_n - 2, _m));
for (var k = 0; k < math.max(nct, nrt); k++) {
if (k < nct) {
// Compute the transformation for the k-th column and
// place the k-th diagonal in s[k].
// Compute 2-norm of k-th column without under/overflow.
_s[k] = 0;
for (var i = k; i < _m; i++) {
_s[k] = hypotenuse(_s[k], A[i][k]);
}
if (_s[k] != 0.0) {
if (A[k][k] < 0.0) {
_s[k] = -_s[k];
}
for (var i = k; i < _m; i++) {
A[i][k] /= _s[k];
}
A[k][k] += 1.0;
}
_s[k] = -_s[k];
}
for (var j = k + 1; j < _n; j++) {
if ((k < nct) && (_s[k] != 0.0)) {
// Apply the transformation.
var t = 0.0;
for (var i = k; i < _m; i++) {
t += A[i][k] * A[i][j];
}
t = -t / A[k][k];
for (var i = k; i < _m; i++) {
A[i][j] += t * A[i][k];
}
}
// Place the k-th row of A into e for the
// subsequent calculation of the row transformation.
e[j] = A[k][j];
}
if (wantu && (k < nct)) {
// Place the transformation in U for subsequent back
// multiplication.
for (var i = k; i < _m; i++) {
_U[i][k] = A[i][k];
}
}
if (k < nrt) {
// Compute the k-th row transformation and place the
// k-th super-diagonal in e[k].
// Compute 2-norm without under/overflow.
e[k] = 0;
for (var i = k + 1; i < _n; i++) {
e[k] = hypotenuse(e[k], e[i]);
}
if (e[k] != 0.0) {
if (e[k + 1] < 0.0) {
e[k] = -e[k];
}
for (var i = k + 1; i < _n; i++) {
e[i] /= e[k];
}
e[k + 1] += 1.0;
}
e[k] = -e[k];
if ((k + 1 < _m) && (e[k] != 0.0)) {
// Apply the transformation.
for (var i = k + 1; i < _m; i++) {
work[i] = 0.0;
}
for (var j = k + 1; j < _n; j++) {
for (var i = k + 1; i < _m; i++) {
work[i] += e[j] * A[i][j];
}
}
for (var j = k + 1; j < _n; j++) {
var t = -e[j] / e[k + 1];
for (var i = k + 1; i < _m; i++) {
A[i][j] += t * work[i];
}
}
}
if (wantv) {
// Place the transformation in V for subsequent
// back multiplication.
for (var i = k + 1; i < _n; i++) {
_V[i][k] = e[i];
}
}
}
}
// Set up the final bidiagonal matrix or order p.
var p = math.min(_n, _m + 1);
if (nct < _n) {
_s[nct] = A[nct][nct];
}
if (_m < p) {
_s[p - 1] = 0.0;
}
if (nrt + 1 < p) {
e[nrt] = A[nrt][p - 1];
}
e[p - 1] = 0.0;
// If required, generate U.
if (wantu) {
for (var j = nct; j < nu; j++) {
for (var i = 0; i < _m; i++) {
_U[i][j] = 0.0;
}
_U[j][j] = 1.0;
}
for (var k = nct - 1; k >= 0; k--) {
if (_s[k] != 0.0) {
for (var j = k + 1; j < nu; j++) {
var t = 0.0;
for (var i = k; i < _m; i++) {
t += _U[i][k] * _U[i][j];
}
t = -t / _U[k][k];
for (var i = k; i < _m; i++) {
_U[i][j] += t * _U[i][k];
}
}
for (var i = k; i < _m; i++) {
_U[i][k] = -_U[i][k];
}
_U[k][k] = 1.0 + _U[k][k];
for (var i = 0; i < k - 1; i++) {
_U[i][k] = 0.0;
}
} else {
for (var i = 0; i < _m; i++) {
_U[i][k] = 0.0;
}
_U[k][k] = 1.0;
}
}
}
// If required, generate V.
if (wantv) {
for (var k = _n - 1; k >= 0; k--) {
if ((k < nrt) && (e[k] != 0.0)) {
for (var j = k + 1; j < nu; j++) {
var t = 0.0;
for (var i = k + 1; i < _n; i++) {
t += _V[i][k] * _V[i][j];
}
t = -t / _V[k + 1][k];
for (var i = k + 1; i < _n; i++) {
_V[i][j] += t * _V[i][k];
}
}
}
for (var i = 0; i < _n; i++) {
_V[i][k] = 0.0;
}
_V[k][k] = 1.0;
}
}
// Main iteration loop for the singular values.
var pp = p - 1;
var iter = 0;
var eps = math.pow(2.0, -52.0).toDouble();
var tiny = math.pow(2.0, -966.0).toDouble();
while (p > 0) {
int k, kase;
// Here is where a test for too many iterations would go.
// This section of the program inspects for
// negligible elements in the s and e arrays. On
// completion the variables kase and k are set as follows.
// kase = 1 if s(p) and e[k-1] are negligible and k<p
// kase = 2 if s(k) is negligible and k<p
// kase = 3 if e[k-1] is negligible, k<p, and
// s(k), ..., s(p) are not negligible (qr step).
// kase = 4 if e(p-1) is negligible (convergence).
for (k = p - 2; k >= -1; k--) {
if (k == -1) {
break;
}
if (e[k].abs() <= (tiny + (eps * (_s[k].abs() + _s[k + 1].abs())))) {
e[k] = 0.0;
break;
}
}
if (k == p - 2) {
kase = 4;
} else {
int ks;
for (ks = p - 1; ks >= k; ks--) {
if (ks == k) {
break;
}
var t = (ks != p ? e[ks].abs() : 0.0) +
(ks != k + 1 ? e[ks - 1].abs() : 0.0);
if (_s[ks].abs() <= (tiny + (eps * t))) {
_s[ks] = 0.0;
break;
}
}
if (ks == k) {
kase = 3;
} else if (ks == p - 1) {
kase = 1;
} else {
kase = 2;
k = ks;
}
}
k++;
// Perform the task indicated by kase.
switch (kase) {
// Deflate negligible s(p).
case 1:
{
var f = e[p - 2];
e[p - 2] = 0.0;
for (var j = p - 2; j >= k; j--) {
var t = hypotenuse(_s[j], f);
var cs = _s[j] / t;
var sn = f / t;
_s[j] = t;
if (j != k) {
f = -sn * e[j - 1];
e[j - 1] = cs * e[j - 1];
}
if (wantv) {
for (var i = 0; i < _n; i++) {
t = (cs * _V[i][j]) + (sn * _V[i][p - 1]);
_V[i][p - 1] = (-sn * _V[i][j]) + (cs * _V[i][p - 1]);
_V[i][j] = t;
}
}
}
}
break;
// Split at negligible s(k).
case 2:
{
var f = e[k - 1];
e[k - 1] = 0.0;
for (var j = k; j < p; j++) {
var t = hypotenuse(_s[j], f);
var cs = _s[j] / t;
var sn = f / t;
_s[j] = t;
f = -sn * e[j];
e[j] = cs * e[j];
if (wantu) {
for (var i = 0; i < _m; i++) {
t = (cs * _U[i][j]) + (sn * _U[i][k - 1]);
_U[i][k - 1] = (-sn * _U[i][j]) + (cs * _U[i][k - 1]);
_U[i][j] = t;
}
}
}
}
break;
// Perform one qr step.
case 3:
{
// Calculate the shift.
var scale = math.max(
math.max(
math.max(math.max(_s[p - 1].abs(), _s[p - 2].abs()),
e[p - 2].abs()),
_s[k].abs()),
e[k].abs());
var sp = _s[p - 1] / scale;
var spm1 = _s[p - 2] / scale;
var epm1 = e[p - 2] / scale;
var sk = _s[k] / scale;
var ek = e[k] / scale;
var b = ((spm1 + sp) * (spm1 - sp) + (epm1 * epm1)) / 2.0;
var c = (sp * epm1) * (sp * epm1);
var shift = 0.0;
if ((b != 0.0) | (c != 0.0)) {
shift = math.sqrt((b * b) + c);
if (b < 0.0) {
shift = -shift;
}
shift = c / (b + shift);
}
var f = ((sk + sp) * (sk - sp)) + shift;
var g = sk * ek;
// Chase zeros.
for (var j = k; j < p - 1; j++) {
var t = hypotenuse(f, g);
var cs = f / t;
var sn = g / t;
if (j != k) {
e[j - 1] = t;
}
f = (cs * _s[j]) + (sn * e[j]);
e[j] = (cs * e[j]) - (sn * _s[j]);
g = sn * _s[j + 1];
_s[j + 1] = cs * _s[j + 1];
if (wantv) {
for (var i = 0; i < _n; i++) {
t = (cs * _V[i][j]) + (sn * _V[i][j + 1]);
_V[i][j + 1] = (-sn * _V[i][j]) + (cs * _V[i][j + 1]);
_V[i][j] = t;
}
}
t = hypotenuse(f, g);
cs = f / t;
sn = g / t;
_s[j] = t;
f = (cs * e[j]) + (sn * _s[j + 1]);
_s[j + 1] = (-sn * e[j]) + (cs * _s[j + 1]);
g = sn * e[j + 1];
e[j + 1] = cs * e[j + 1];
if (wantu && (j < _m - 1)) {
for (var i = 0; i < _m; i++) {
t = (cs * _U[i][j]) + (sn * _U[i][j + 1]);
_U[i][j + 1] = (-sn * _U[i][j]) + (cs * _U[i][j + 1]);
_U[i][j] = t;
}
}
}
e[p - 2] = f;
iter = iter + 1;
}
break;
// Convergence.
case 4:
{
// Make the singular values positive.
if (_s[k] <= 0.0) {
_s[k] = (_s[k] < 0.0 ? -_s[k] : 0.0);
if (wantv) {
for (var i = 0; i <= pp; i++) {
_V[i][k] = -_V[i][k];
}
}
}
// Order the singular values.
while (k < pp) {
if (_s[k] >= _s[k + 1]) {
break;
}
var t = _s[k];
_s[k] = _s[k + 1];
_s[k + 1] = t;
if (wantv && (k < _n - 1)) {
for (var i = 0; i < _n; i++) {
t = _V[i][k + 1];
_V[i][k + 1] = _V[i][k];
_V[i][k] = t;
}
}
if (wantu && (k < _m - 1)) {
for (var i = 0; i < _m; i++) {
t = _U[i][k + 1];
_U[i][k + 1] = _U[i][k];
_U[i][k] = t;
}
}
k++;
}
iter = 0;
p--;
}
break;
}
}
if (specialCaseMoreColumns) {
_m = Arg.row;
_n = Arg.column;
array2dMultiplyToScalar(_U, -1);
array2dMultiplyToScalar(_V, -1);
} else if (specialCaseMoreRows) {
_m = Arg.row;
_n = Arg.column;
for (var i = 0; i < _U.row; i++) {
for (var j = 0; j < _U.column; j++) {
if (isOdd(j)) {
_U[i][j] *= -1;
}
}
}
for (var i = 0; i < _V.row; i++) {
for (var j = 0; j < _V.column; j++) {
if (isOdd(j)) {
_V[i][j] *= -1;
}
}
}
}
}