All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.librec.math.algorithm.SVD Maven / Gradle / Ivy

// Copyright (C) 2014-2015 Guibing Guo
//
// This file is part of LibRec.
//
// LibRec is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// LibRec is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with LibRec. If not, see .
//

package net.librec.math.algorithm;

import net.librec.math.structure.DenseMatrix;

/**
 * Singular Value Decomposition: adapted from the JAMA implementations
*

* For an m-by-n matrix A with {@code m >= n}, the singular value decomposition is an m-by-n orthogonal matrix U, an n-by-n * diagonal matrix S, and an n-by-n orthogonal matrix V so that A = U*S*V'. Note that this implementation requires {@code m>=n}. * Otherwise, you'd better use the transpose of a matrix. *

* The singular values, {@code sigma[k] = S[k][k]}, are ordered so that {@code sigma[0] >= sigma[1] >= ... >= sigma[n-1]}. */ public class SVD { /** * Arrays for internal storage of U and V. */ private double[][] U, V; /** * Array for internal storage of singular values. */ private double[] sigma; /** * Row and column dimensions. */ private int m, n; /** * Construct the singular value decomposition Structure to access U, S and V. * * @param mat Rectangular matrix */ public SVD(DenseMatrix mat) { // Derived from LINPACK code. // Initialize. DenseMatrix matClone = mat.clone(); double[][] A = matClone.data; m = matClone.numRows; n = matClone.numColumns; /* * Apparently the failing cases are only a proper subset of (m= 0; k--) { if (sigma[k] != 0.0) { for (int j = k + 1; j < nu; j++) { double t = 0; for (int i = k; i < m; i++) { t += U[i][k] * U[i][j]; } t = -t / U[k][k]; for (int i = k; i < m; i++) { U[i][j] += t * U[i][k]; } } for (int i = k; i < m; i++) { U[i][k] = -U[i][k]; } U[k][k] = 1.0 + U[k][k]; for (int i = 0; i < k - 1; i++) { U[i][k] = 0.0; } } else { for (int i = 0; i < m; i++) { U[i][k] = 0.0; } U[k][k] = 1.0; } } // Generate V for (int k = n - 1; k >= 0; k--) { if ((k < nrt) & (e[k] != 0.0)) { for (int j = k + 1; j < nu; j++) { double t = 0; for (int i = k + 1; i < n; i++) { t += V[i][k] * V[i][j]; } t = -t / V[k + 1][k]; for (int i = k + 1; i < n; i++) { V[i][j] += t * V[i][k]; } } } for (int i = 0; i < n; i++) { V[i][k] = 0.0; } V[k][k] = 1.0; } // Main iteration loop for the singular values. int pp = p - 1; int iter = 0; double eps = Math.pow(2.0, -52.0); double tiny = Math.pow(2.0, -966.0); 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

= -1; k--) { if (k == -1) { break; } if (Math.abs(e[k]) <= tiny + eps * (Math.abs(sigma[k]) + Math.abs(sigma[k + 1]))) { 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; } double t = (ks != p ? Math.abs(e[ks]) : 0.) + (ks != k + 1 ? Math.abs(e[ks - 1]) : 0.); if (Math.abs(sigma[ks]) <= tiny + eps * t) { sigma[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: { double f = e[p - 2]; e[p - 2] = 0.0; for (int j = p - 2; j >= k; j--) { double t = Maths.hypot(sigma[j], f); double cs = sigma[j] / t; double sn = f / t; sigma[j] = t; if (j != k) { f = -sn * e[j - 1]; e[j - 1] = cs * e[j - 1]; } for (int 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: { double f = e[k - 1]; e[k - 1] = 0.0; for (int j = k; j < p; j++) { double t = Maths.hypot(sigma[j], f); double cs = sigma[j] / t; double sn = f / t; sigma[j] = t; f = -sn * e[j]; e[j] = cs * e[j]; for (int 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. double scale = Math.max(Math.max( Math.max(Math.max(Math.abs(sigma[p - 1]), Math.abs(sigma[p - 2])), Math.abs(e[p - 2])), Math.abs(sigma[k])), Math.abs(e[k])); double sp = sigma[p - 1] / scale; double spm1 = sigma[p - 2] / scale; double epm1 = e[p - 2] / scale; double sk = sigma[k] / scale; double ek = e[k] / scale; double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0; double c = (sp * epm1) * (sp * epm1); double 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); } double f = (sk + sp) * (sk - sp) + shift; double g = sk * ek; // Chase zeros. for (int j = k; j < p - 1; j++) { double t = Maths.hypot(f, g); double cs = f / t; double sn = g / t; if (j != k) { e[j - 1] = t; } f = cs * sigma[j] + sn * e[j]; e[j] = cs * e[j] - sn * sigma[j]; g = sn * sigma[j + 1]; sigma[j + 1] = cs * sigma[j + 1]; for (int 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 = Maths.hypot(f, g); cs = f / t; sn = g / t; sigma[j] = t; f = cs * e[j] + sn * sigma[j + 1]; sigma[j + 1] = -sn * e[j] + cs * sigma[j + 1]; g = sn * e[j + 1]; e[j + 1] = cs * e[j + 1]; if (j < m - 1) { for (int 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 (sigma[k] <= 0.0) { sigma[k] = (sigma[k] < 0.0 ? -sigma[k] : 0.0); for (int i = 0; i <= pp; i++) { V[i][k] = -V[i][k]; } } // Order the singular values. while (k < pp) { if (sigma[k] >= sigma[k + 1]) { break; } double t = sigma[k]; sigma[k] = sigma[k + 1]; sigma[k + 1] = t; if (k < n - 1) { for (int i = 0; i < n; i++) { t = V[i][k + 1]; V[i][k + 1] = V[i][k]; V[i][k] = t; } } if (k < m - 1) { for (int 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; } } } /** * Return the left singular vectors * * @return U */ public DenseMatrix getU() { return new DenseMatrix(U, m, Math.min(m + 1, n)); } /** * Return the right singular vectors * * @return V */ public DenseMatrix getV() { return new DenseMatrix(V, n, n); } /** * Return the one-dimensional array of singular values * * @return diagonal of S. */ public double[] getSingularValues() { return sigma; } /** * Return the diagonal matrix of singular values * * @return S */ public DenseMatrix getS() { DenseMatrix res = new DenseMatrix(n, n); for (int i = 0; i < n; i++) { res.set(i, i, sigma[i]); } return res; } /** * Two norm * * @return max(S) */ public double norm2() { return sigma[0]; } /** * Two norm condition number * * @return max(S)/min(S) */ public double cond() { return sigma[0] / sigma[Math.min(m, n) - 1]; } /** * Effective numerical matrix rank * * @return Number of nonnegligible singular values. */ public int rank() { double eps = Math.pow(2.0, -52.0); double tol = Math.max(m, n) * sigma[0] * eps; int r = 0; for (int i = 0; i < sigma.length; i++) { if (sigma[i] > tol) { r++; } } return r; } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy