123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906 |
- using System.Collections;
- namespace VisualMath.Accord.Math.Decompositions
- {
- using System;
- /// <summary>
- /// Singular Value Decomposition for a rectangular matrix.
- /// </summary>
- /// <remarks>
- /// <para>
- /// For an m-by-n matrix <c>A</c> with <c>m >= n</c>, the singular value decomposition
- /// is an m-by-n orthogonal matrix <c>U</c>, an n-by-n diagonal matrix <c>S</c>, and
- /// an n-by-n orthogonal matrix <c>V</c> so that <c>A = U * S * V'</c>.
- /// The singular values, <c>sigma[k] = S[k,k]</c>, are ordered so that
- /// <c>sigma[0] >= sigma[1] >= ... >= sigma[n-1]</c>.</para>
- /// <para>
- /// The singular value decomposition always exists, so the constructor will
- /// never fail. The matrix condition number and the effective numerical
- /// rank can be computed from this decomposition.</para>
- /// <para>
- /// WARNING! Please be aware that if A has less rows than columns, it is better
- /// to compute the decomposition on the transpose of A and then swap the left
- /// and right eigenvectors. If the routine is computed on A directly, the diagonal
- /// of singular values may contain one or more zeros. The identity A = U * S * V'
- /// may still hold, however. To overcome this problem, pass true to the
- /// <see cref="SingularValueDecomposition(double[,], bool, bool, bool)">autoTranspose</see> argument of the class constructor.</para>
- /// <para>
- /// This routine computes the economy decomposition of A.</para>
- ///
- /// </remarks>
- public sealed class SingularValueDecomposition
- {
- private double[,] u;
- private double[,] v;
- private double[] s; // singular values
- private int m;
- private int n;
- private bool swapped;
- /// <summary>Returns the condition number <c>max(S) / min(S)</c>.</summary>
- public double Condition
- {
- get { return s[0] / s[System.Math.Min(m, n) - 1]; }
- }
- /// <summary>Returns the singularity threshold.</summary>
- public double Threshold
- {
- get { return Double.Epsilon * System.Math.Max(m, n) * s[0]; }
- }
- /// <summary>Returns the Two norm.</summary>
- public double TwoNorm
- {
- get { return s[0]; }
- }
- /// <summary>Returns the effective numerical matrix rank.</summary>
- /// <value>Number of non-negligible singular values.</value>
- public int Rank
- {
- get
- {
- double eps = System.Math.Pow(2.0, -52.0);
- double tol = System.Math.Max(m, n) * s[0] * eps;
- int r = 0;
- for (int i = 0; i < s.Length; i++)
- {
- if (s[i] > tol)
- r++;
- }
- return r;
- }
- }
- /// <summary>Returns the one-dimensional array of singular values.</summary>
- public double[] Diagonal
- {
- get { return this.s; }
- }
- /// <summary>Returns the block diagonal matrix of singular values.</summary>
- public double[,] DiagonalMatrix
- {
- get { return Matrix.Diagonal(s); }
- }
- /// <summary>Returns the V matrix of Singular Vectors.</summary>
- public double[,] RightSingularVectors
- {
- get { return v; }
- }
- /// <summary>Return the U matrix of Singular Vectors.</summary>
- public double[,] LeftSingularVectors
- {
- get { return u; }
- }
- /// <summary>Construct singular value decomposition.</summary>
- public SingularValueDecomposition(double[,] value)
- : this(value, true, true)
- {
- }
- /// <summary>Construct singular value decomposition.</summary>
- public SingularValueDecomposition(double[,] value, bool computeLeftSingularVectors, bool computeRightSingularVectors)
- : this(value, computeLeftSingularVectors, computeRightSingularVectors, false)
- {
- }
- /// <summary>Construct singular value decomposition.</summary>
- public SingularValueDecomposition(double[,] value, bool computeLeftSingularVectors, bool computeRightSingularVectors, bool autoTranspose)
- {
- if (value == null)
- {
- throw new ArgumentNullException("value", "Matrix cannot be null.");
- }
- double[,] a;
- m = value.GetLength(0); // rows
- n = value.GetLength(1); // cols
- double eps = System.Math.Pow(2.0, -52.0);
- double tiny = System.Math.Pow(2.0, -966.0);
- if (m < n) // Check if we are violating JAMA's assumption
- {
- if (!autoTranspose) // Yes, check if we should correct it
- {
- // Warning! This routine is not guaranteed to work when A has less rows
- // than columns. If this is the case, you should compute SVD on the
- // transpose of A and then swap the left and right eigenvectors.
- // However, as the solution found can still be useful, the exception below
- // will not be thrown, and only a warning will be output in the trace.
- // throw new ArgumentException("Matrix should have more rows than columns.");
- System.Diagnostics.Trace.WriteLine(
- "WARNING: Computing SVD on a matrix with more columns than rows.");
- // Proceed anyway
- a = (double[,])value.Clone();
- }
- else
- {
- // Transposing and swapping
- a = value.Transpose();
- m = value.GetLength(1);
- n = value.GetLength(0);
- swapped = true;
- }
- }
- else
- {
- a = (double[,])value.Clone(); // Input matrix is ok
- }
- int nu = System.Math.Min(m, n);
- s = new double[System.Math.Min(m + 1, n)];
- u = new double[m, nu];
- v = new double[n, n];
- double[] e = new double[n];
- double[] work = new double[m];
- bool wantu = computeLeftSingularVectors;
- bool wantv = computeRightSingularVectors;
- // Reduce A to bidiagonal form, storing the diagonal elements in s and the super-diagonal elements in e.
- int nct = System.Math.Min(m - 1, n);
- int nrt = System.Math.Max(0, System.Math.Min(n - 2, m));
- for (int k = 0; k < System.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 (int i = k; i < m; i++)
- {
- s[k] = Accord.Math.Tools.Hypotenuse(s[k], a[i, k]);
- }
- if (s[k] != 0.0)
- {
- if (a[k, k] < 0.0)
- {
- s[k] = -s[k];
- }
- for (int i = k; i < m; i++)
- {
- a[i, k] /= s[k];
- }
- a[k, k] += 1.0;
- }
- s[k] = -s[k];
- }
- unsafe
- {
- for (int j = k + 1; j < n; j++)
- {
- fixed (double* ptr_ak = &a[k, k], ptr_aj = &a[k, j])
- {
- if ((k < nct) & (s[k] != 0.0))
- {
- // Apply the transformation.
- double t = 0;
- double* ak = ptr_ak;
- double* aj = ptr_aj;
- for (int i = k; i < m; i++)
- {
- t += (*ak) * (*aj);
- ak += n; aj += n;
- }
- t = -t / *ptr_ak;
- ak = ptr_ak;
- aj = ptr_aj;
- for (int i = k; i < m; i++)
- {
- *aj += t * (*ak);
- ak += n; aj += n;
- }
- }
- // Place the k-th row of A into e for the subsequent calculation of the row transformation.
- e[j] = *ptr_aj;
- }
- }
- }
- if (wantu & (k < nct))
- {
- // Place the transformation in U for subsequent back
- // multiplication.
- for (int 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 (int i = k + 1; i < n; i++)
- {
- e[k] = Accord.Math.Tools.Hypotenuse(e[k], e[i]);
- }
- if (e[k] != 0.0)
- {
- if (e[k + 1] < 0.0)
- e[k] = -e[k];
- for (int 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 (int i = k + 1; i < m; i++)
- work[i] = 0.0;
- unsafe
- {
- fixed (double* ptr_a = a)
- {
- int k1 = k + 1;
- for (int i = k1; i < m; i++)
- {
- double* ai = ptr_a + (i * n) + k1;
- for (int j = k1; j < n; j++, ai++)
- {
- work[i] += e[j] * (*ai);
- }
- }
- for (int j = k1; j < n; j++)
- {
- double t = -e[j] / e[k1];
- double* aj = ptr_a + (k1 * n) + j;
- for (int i = k1; i < m; i++, aj += n)
- {
- *aj += t * work[i];
- }
- }
- }
- }
- }
- if (wantv)
- {
- // Place the transformation in V for subsequent back multiplication.
- for (int i = k + 1; i < n; i++)
- v[i, k] = e[i];
- }
- }
- }
- // Set up the final bidiagonal matrix or order p.
- int p = System.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 (int j = nct; j < nu; j++)
- {
- for (int i = 0; i < m; i++)
- u[i, j] = 0.0;
- u[j, j] = 1.0;
- }
- unsafe
- {
- for (int k = nct - 1; k >= 0; k--)
- {
- if (s[k] != 0.0)
- {
- fixed (double* ptr_uk = &u[k, k])
- {
- double* uk, uj;
- for (int j = k + 1; j < nu; j++)
- {
- fixed (double* ptr_uj = &u[k, j])
- {
- double t = 0;
- uk = ptr_uk;
- uj = ptr_uj;
- for (int i = k; i < m; i++)
- {
- t += *uk * *uj;
- uk += nu; uj += nu;
- }
- t = -t / *ptr_uk;
- uk = ptr_uk;
- uj = ptr_uj;
- for (int i = k; i < m; i++)
- {
- *uj += t * *uk;
- uk += nu; uj += nu;
- }
- }
- }
- uk = ptr_uk;
- for (int i = k; i < m; i++)
- {
- *uk = -(*uk);
- uk += nu;
- }
- 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;
- }
- }
- }
- }
- // If required, generate V.
- if (wantv)
- {
- for (int k = n - 1; k >= 0; k--)
- {
- if ((k < nrt) & (e[k] != 0.0))
- {
- // TODO: The following is a pseudo correction to make SVD
- // work on matrices with n > m (less rows than columns).
- // For the proper correction, compute the decomposition of the
- // transpose of A and swap the left and right eigenvectors
- // Original line:
- // for (int j = k + 1; j < nu; j++)
- // Pseudo correction:
- // for (int j = k + 1; j < n; j++)
- for (int j = k + 1; j < n; j++) // pseudo-correction
- {
- unsafe
- {
- fixed (double* ptr_vk = &v[k + 1, k], ptr_vj = &v[k + 1, j])
- {
- double t = 0;
- double* vk = ptr_vk;
- double* vj = ptr_vj;
- for (int i = k + 1; i < n; i++)
- {
- t += *vk * *vj;
- vk += n; vj += n;
- }
- t = -t / *ptr_vk;
- vk = ptr_vk;
- vj = ptr_vj;
- for (int i = k + 1; i < n; i++)
- {
- *vj += t * *vk;
- vk += n; vj += n;
- }
- }
- }
- }
- }
- 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;
- 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 (System.Math.Abs(e[k]) <=
- tiny + eps * (System.Math.Abs(s[k]) + System.Math.Abs(s[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 ? System.Math.Abs(e[ks]) : 0.0) +
- (ks != k + 1 ? System.Math.Abs(e[ks - 1]) : 0.0);
- if (System.Math.Abs(s[ks]) <= 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:
- {
- double f = e[p - 2];
- e[p - 2] = 0.0;
- for (int j = p - 2; j >= k; j--)
- {
- double t = Accord.Math.Tools.Hypotenuse(s[j], f);
- double cs = s[j] / t;
- double sn = f / t;
- s[j] = t;
- if (j != k)
- {
- f = -sn * e[j - 1];
- e[j - 1] = cs * e[j - 1];
- }
- if (wantv)
- {
- 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 = Accord.Math.Tools.Hypotenuse(s[j], f);
- double cs = s[j] / t;
- double sn = f / t;
- s[j] = t;
- f = -sn * e[j];
- e[j] = cs * e[j];
- if (wantu)
- {
- 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 = System.Math.Max(System.Math.Max(System.Math.Max(System.Math.Max(System.Math.Abs(s[p - 1]), System.Math.Abs(s[p - 2])), System.Math.Abs(e[p - 2])), System.Math.Abs(s[k])), System.Math.Abs(e[k]));
- double sp = s[p - 1] / scale;
- double spm1 = s[p - 2] / scale;
- double epm1 = e[p - 2] / scale;
- double sk = s[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))
- {
- if (b < 0.0)
- shift = -System.Math.Sqrt(b * b + c);
- else
- shift = System.Math.Sqrt(b * b + c);
- 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 = Accord.Math.Tools.Hypotenuse(f, g);
- double cs = f / t;
- double 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)
- {
- unsafe
- {
- fixed (double* ptr_vj = &v[0, j])
- {
- double* vj = ptr_vj;
- double* vj1 = ptr_vj + 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;*/
- double vij = *vj;
- double vij1 = *vj1;
- t = cs * vij + sn * vij1;
- *vj1 = -sn * vij + cs * vij1;
- *vj = t;
- vj += n; vj1 += n;
- }
- }
- }
- }
- t = Accord.Math.Tools.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))
- {
- unsafe
- {
- fixed (double* ptr_uj = &u[0, j])
- {
- double* uj = ptr_uj;
- double* uj1 = ptr_uj + 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;*/
- double uij = *uj;
- double uij1 = *uj1;
- t = cs * uij + sn * uij1;
- *uj1 = -sn * uij + cs * uij1;
- *uj = t;
- uj += nu; uj1 += nu;
- }
- }
- }
- }
- }
- 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 (int 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;
- double t = s[k];
- s[k] = s[k + 1];
- s[k + 1] = t;
- if (wantv && (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 (wantu && (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;
- }
- }
- // If we are violating JAMA's assumption about
- // the input dimension, we need to swap u and v.
- if (swapped)
- {
- double[,] temp = this.u;
- this.u = this.v;
- this.v = temp;
- }
- }
- /// <summary>
- /// Solves a linear equation system of the form AX = B.
- /// </summary>
- /// <param name="value">Parameter B from the equation AX = B.</param>
- /// <returns>The solution X from equation AX = B.</returns>
- public double[,] Solve(double[,] value)
- {
- // Additionally an important property is that if there does not exists a solution
- // when the matrix A is singular but replacing 1/Li with 0 will provide a solution
- // that minimizes the residue |AX -Y|. SVD finds the least squares best compromise
- // solution of the linear equation system. Interestingly SVD can be also used in an
- // over-determined system where the number of equations exceeds that of the parameters.
- // L is a diagonal matrix with non-negative matrix elements having the same
- // dimension as A, Wi ? 0. The diagonal elements of L are the singular values of matrix A.
- double[,] Y = value;
- // Create L*, which is a diagonal matrix with elements
- // L*[i] = 1/L[i] if L[i] < e, else 0,
- // where e is the so-called singularity threshold.
- // In other words, if L[i] is zero or close to zero (smaller than e),
- // one must replace 1/L[i] with 0. The value of e depends on the precision
- // of the hardware. This method can be used to solve linear equations
- // systems even if the matrices are singular or close to singular.
- //singularity threshold
- double e = this.Threshold;
- int scols = s.Length;
- double[,] Ls = new double[scols, scols];
- for (int i = 0; i < s.Length; i++)
- {
- if (System.Math.Abs(s[i]) <= e)
- Ls[i, i] = 0.0;
- else Ls[i, i] = 1.0 / s[i];
- }
- //(V x L*) x Ut x Y
- double[,] VL = v.Multiply(Ls);
- //(V x L* x Ut) x Y
- int vrows = v.GetLength(0);
- int urows = u.GetLength(0);
- double[,] VLU = new double[vrows, urows];
- for (int i = 0; i < vrows; i++)
- {
- for (int j = 0; j < urows; j++)
- {
- double sum = 0;
- for (int k = 0; k < urows; k++)
- sum += VL[i, k] * u[j, k];
- VLU[i, j] = sum;
- }
- }
- //(V x L* x Ut x Y)
- return VLU.Multiply(Y);
- }
- /// <summary>
- /// Solves a linear equation system of the form Ax = b.
- /// </summary>
- /// <param name="value">The b from the equation Ax = b.</param>
- /// <returns>The x from equation Ax = b.</returns>
- public double[] Solve(double[] value)
- {
- // Additionally an important property is that if there does not exists a solution
- // when the matrix A is singular but replacing 1/Li with 0 will provide a solution
- // that minimizes the residue |AX -Y|. SVD finds the least squares best compromise
- // solution of the linear equation system. Interestingly SVD can be also used in an
- // over-determined system where the number of equations exceeds that of the parameters.
- // L is a diagonal matrix with non-negative matrix elements having the same
- // dimension as A, Wi ? 0. The diagonal elements of L are the singular values of matrix A.
- //singularity threshold
- double e = this.Threshold;
- double[] Y = value;
- // Create L*, which is a diagonal matrix with elements
- // L*i = 1/Li if Li = e, else 0,
- // where e is the so-called singularity threshold.
- // In other words, if Li is zero or close to zero (smaller than e),
- // one must replace 1/Li with 0. The value of e depends on the precision
- // of the hardware. This method can be used to solve linear equations
- // systems even if the matrices are singular or close to singular.
- int scols = s.Length;
- double[,] Ls = new double[scols, scols];
- for (int i = 0; i < s.Length; i++)
- {
- if (System.Math.Abs(s[i]) <= e)
- Ls[i, i] = 0;
- else Ls[i, i] = 1.0 / s[i];
- }
- //(V x L*) x Ut x Y
- double[,] VL = v.Multiply(Ls);
- //(V x L* x Ut) x Y
- int urows = u.GetLength(0);
- int vrows = v.GetLength(0);
- double[,] VLU = new double[vrows, urows];
- for (int i = 0; i < vrows; i++)
- {
- for (int j = 0; j < urows; j++)
- {
- double sum = 0;
- for (int k = 0; k < scols; k++)
- sum += VL[i, k] * u[j, k];
- VLU[i, j] = sum;
- }
- }
- //(V x L* x Ut x Y)
- return VLU.Multiply(Y);
- }
- /// <summary>
- /// Computes the (pseudo-)inverse of the matrix given to the Singular value decomposition.
- /// </summary>
- public double[,] Inverse()
- {
- double e = this.Threshold;
- // X = V*S^-1
- int vrows = v.GetLength(0);
- int vcols = v.GetLength(1);
- double[,] X = new double[vrows, s.Length];
- for (int i = 0; i < vrows; i++)
- {
- for (int j = 0; j < vcols; j++)
- {
- if (System.Math.Abs(s[j]) > e)
- X[i, j] = v[i, j] / s[j];
- }
- }
- // Y = X*U'
- int urows = u.GetLength(0);
- int ucols = u.GetLength(1);
- double[,] Y = new double[vrows, urows];
- for (int i = 0; i < vrows; i++)
- {
- for (int j = 0; j < urows; j++)
- {
- double sum = 0;
- for (int k = 0; k < ucols; k++)
- sum += X[i, k] * u[j, k];
- Y[i, j] = sum;
- }
- }
- return Y;
- }
- }
- }
|