public void EpsilonTest() { double x = 0.5; double expected = 0.00000000000000011102230246251565; double actual = Special.Epslon(x); Assert.AreEqual(expected, actual); x = 0.0; expected = 0.0; actual = Special.Epslon(x); Assert.AreEqual(expected, actual); x = 1.0; expected = 0.00000000000000022204460492503131; actual = Special.Epslon(x); Assert.AreEqual(expected, actual); }
/// <summary> /// Adaptation of the original Fortran QZIT routine from EISPACK. /// </summary> /// <remarks> /// This subroutine is the second step of the qz algorithm /// for solving generalized matrix eigenvalue problems, /// siam j. numer. anal. 10, 241-256(1973) by moler and stewart, /// as modified in technical note nasa tn d-7305(1973) by ward. /// /// This subroutine accepts a pair of real matrices, one of them /// in upper hessenberg form and the other in upper triangular form. /// it reduces the hessenberg matrix to quasi-triangular form using /// orthogonal transformations while maintaining the triangular form /// of the other matrix. it is usually preceded by qzhes and /// followed by qzval and, possibly, qzvec. /// /// For the full documentation, please check the original function. /// </remarks> private static int qzit(int n, double[,] a, double[,] b, double eps1, bool matz, double[,] z, ref int ierr) { int i, j, k, l = 0; double r, s, t, a1, a2, a3 = 0; int k1, k2, l1, ll; double u1, u2, u3; double v1, v2, v3; double a11, a12, a21, a22, a33, a34, a43, a44; double b11, b12, b22, b33, b34, b44; int na, en, ld; double ep; double sh = 0; int km1, lm1 = 0; double ani, bni; int ish, itn, its, enm2, lor1; double epsa, epsb, anorm = 0, bnorm = 0; int enorn; bool notlas; ierr = 0; #region Compute epsa and epsb for (i = 0; i < n; ++i) { ani = 0.0; bni = 0.0; if (i != 0) { ani = (Math.Abs(a[i, (i - 1)])); } for (j = i; j < n; ++j) { ani += Math.Abs(a[i, j]); bni += Math.Abs(b[i, j]); } if (ani > anorm) { anorm = ani; } if (bni > bnorm) { bnorm = bni; } } if (anorm == 0.0) { anorm = 1.0; } if (bnorm == 0.0) { bnorm = 1.0; } ep = eps1; if (ep == 0.0) { // Use roundoff level if eps1 is zero ep = Special.Epslon(1.0); } epsa = ep * anorm; epsb = ep * bnorm; #endregion // Reduce a to quasi-triangular form, while keeping b triangular lor1 = 0; enorn = n; en = n - 1; itn = n * 30; // Begin QZ step L60: if (en <= 1) { goto L1001; } if (!matz) { enorn = en + 1; } its = 0; na = en - 1; enm2 = na; L70: ish = 2; // Check for convergence or reducibility. for (ll = 0; ll <= en; ++ll) { lm1 = en - ll - 1; l = lm1 + 1; if (l + 1 == 1) { goto L95; } if ((Math.Abs(a[l, lm1])) <= epsa) { break; } } L90: a[l, lm1] = 0.0; if (l < na) { goto L95; } // 1-by-1 or 2-by-2 block isolated en = lm1; goto L60; // Check for small top of b L95: ld = l; L100: l1 = l + 1; b11 = b[l, l]; if (Math.Abs(b11) > epsb) { goto L120; } b[l, l] = 0.0; s = (Math.Abs(a[l, l]) + Math.Abs(a[l1, l])); u1 = a[l, l] / s; u2 = a[l1, l] / s; r = Special.Sign(Math.Sqrt(u1 * u1 + u2 * u2), u1); v1 = -(u1 + r) / r; v2 = -u2 / r; u2 = v2 / v1; for (j = l; j < enorn; ++j) { t = a[l, j] + u2 * a[l1, j]; a[l, j] += t * v1; a[l1, j] += t * v2; t = b[l, j] + u2 * b[l1, j]; b[l, j] += t * v1; b[l1, j] += t * v2; } if (l != 0) { a[l, lm1] = -a[l, lm1]; } lm1 = l; l = l1; goto L90; L120: a11 = a[l, l] / b11; a21 = a[l1, l] / b11; if (ish == 1) { goto L140; } // Iteration strategy if (itn == 0) { goto L1000; } if (its == 10) { goto L155; } // Determine type of shift b22 = b[l1, l1]; if (Math.Abs(b22) < epsb) { b22 = epsb; } b33 = b[na, na]; if (Math.Abs(b33) < epsb) { b33 = epsb; } b44 = b[en, en]; if (Math.Abs(b44) < epsb) { b44 = epsb; } a33 = a[na, na] / b33; a34 = a[na, en] / b44; a43 = a[en, na] / b33; a44 = a[en, en] / b44; b34 = b[na, en] / b44; t = (a43 * b34 - a33 - a44) * .5; r = t * t + a34 * a43 - a33 * a44; if (r < 0.0) { goto L150; } // Determine single shift zeroth column of a ish = 1; r = Math.Sqrt(r); sh = -t + r; s = -t - r; if (Math.Abs(s - a44) < Math.Abs(sh - a44)) { sh = s; } // Look for two consecutive small sub-diagonal elements of a. for (ll = ld; ll + 1 <= enm2; ++ll) { l = enm2 + ld - ll - 1; if (l == ld) { goto L140; } lm1 = l - 1; l1 = l + 1; t = a[l + 1, l + 1]; if (Math.Abs(b[l, l]) > epsb) { t -= sh * b[l, l]; } if (Math.Abs(a[l, lm1]) <= (Math.Abs(t / a[l1, l])) * epsa) { goto L100; } } L140: a1 = a11 - sh; a2 = a21; if (l != ld) { a[l, lm1] = -a[l, lm1]; } goto L160; // Determine double shift zeroth column of a L150: a12 = a[l, l1] / b22; a22 = a[l1, l1] / b22; b12 = b[l, l1] / b22; a1 = ((a33 - a11) * (a44 - a11) - a34 * a43 + a43 * b34 * a11) / a21 + a12 - a11 * b12; a2 = a22 - a11 - a21 * b12 - (a33 - a11) - (a44 - a11) + a43 * b34; a3 = a[l1 + 1, l1] / b22; goto L160; // Ad hoc shift L155: a1 = 0.0; a2 = 1.0; a3 = 1.1605; L160: ++its; --itn; if (!matz) { lor1 = ld; } // Main loop for (k = l; k <= na; ++k) { notlas = k != na && ish == 2; k1 = k + 1; k2 = k + 2; km1 = Math.Max(k, l + 1) - 1; // Computing MAX ll = Math.Min(en, k1 + ish); // Computing MIN if (notlas) { goto L190; } // Zero a(k+1,k-1) if (k == l) { goto L170; } a1 = a[k, km1]; a2 = a[k1, km1]; L170: s = Math.Abs(a1) + Math.Abs(a2); if (s == 0.0) { goto L70; } u1 = a1 / s; u2 = a2 / s; r = Special.Sign(Math.Sqrt(u1 * u1 + u2 * u2), u1); v1 = -(u1 + r) / r; v2 = -u2 / r; u2 = v2 / v1; for (j = km1; j < enorn; ++j) { t = a[k, j] + u2 * a[k1, j]; a[k, j] += t * v1; a[k1, j] += t * v2; t = b[k, j] + u2 * b[k1, j]; b[k, j] += t * v1; b[k1, j] += t * v2; } if (k != l) { a[k1, km1] = 0.0; } goto L240; // Zero a(k+1,k-1) and a(k+2,k-1) L190: if (k == l) { goto L200; } a1 = a[k, km1]; a2 = a[k1, km1]; a3 = a[k2, km1]; L200: s = Math.Abs(a1) + Math.Abs(a2) + Math.Abs(a3); if (s == 0.0) { goto L260; } u1 = a1 / s; u2 = a2 / s; u3 = a3 / s; r = Special.Sign(Math.Sqrt(u1 * u1 + u2 * u2 + u3 * u3), u1); v1 = -(u1 + r) / r; v2 = -u2 / r; v3 = -u3 / r; u2 = v2 / v1; u3 = v3 / v1; for (j = km1; j < enorn; ++j) { t = a[k, j] + u2 * a[k1, j] + u3 * a[k2, j]; a[k, j] += t * v1; a[k1, j] += t * v2; a[k2, j] += t * v3; t = b[k, j] + u2 * b[k1, j] + u3 * b[k2, j]; b[k, j] += t * v1; b[k1, j] += t * v2; b[k2, j] += t * v3; } if (k == l) { goto L220; } a[k1, km1] = 0.0; a[k2, km1] = 0.0; // Zero b(k+2,k+1) and b(k+2,k) L220: s = (Math.Abs(b[k2, k2])) + (Math.Abs(b[k2, k1])) + (Math.Abs(b[k2, k])); if (s == 0.0) { goto L240; } u1 = b[k2, k2] / s; u2 = b[k2, k1] / s; u3 = b[k2, k] / s; r = Special.Sign(Math.Sqrt(u1 * u1 + u2 * u2 + u3 * u3), u1); v1 = -(u1 + r) / r; v2 = -u2 / r; v3 = -u3 / r; u2 = v2 / v1; u3 = v3 / v1; for (i = lor1; i < ll + 1; ++i) { t = a[i, k2] + u2 * a[i, k1] + u3 * a[i, k]; a[i, k2] += t * v1; a[i, k1] += t * v2; a[i, k] += t * v3; t = b[i, k2] + u2 * b[i, k1] + u3 * b[i, k]; b[i, k2] += t * v1; b[i, k1] += t * v2; b[i, k] += t * v3; } b[k2, k] = 0.0; b[k2, k1] = 0.0; if (matz) { for (i = 0; i < n; ++i) { t = z[i, k2] + u2 * z[i, k1] + u3 * z[i, k]; z[i, k2] += t * v1; z[i, k1] += t * v2; z[i, k] += t * v3; } } // Zero b(k+1,k) L240: s = (Math.Abs(b[k1, k1])) + (Math.Abs(b[k1, k])); if (s == 0.0) { goto L260; } u1 = b[k1, k1] / s; u2 = b[k1, k] / s; r = Special.Sign(Math.Sqrt(u1 * u1 + u2 * u2), u1); v1 = -(u1 + r) / r; v2 = -u2 / r; u2 = v2 / v1; for (i = lor1; i < ll + 1; ++i) { t = a[i, k1] + u2 * a[i, k]; a[i, k1] += t * v1; a[i, k] += t * v2; t = b[i, k1] + u2 * b[i, k]; b[i, k1] += t * v1; b[i, k] += t * v2; } b[k1, k] = 0.0; if (matz) { for (i = 0; i < n; ++i) { t = z[i, k1] + u2 * z[i, k]; z[i, k1] += t * v1; z[i, k] += t * v2; } } L260: ; } goto L70; // End QZ step // Set error -- all eigenvalues have not converged after 30*n iterations L1000: ierr = en + 1; // Save epsb for use by qzval and qzvec L1001: if (n > 1) { b[n - 1, 0] = epsb; } return(0); }
/// <summary> /// Single non-negative matrix factorization. /// </summary> private double nnmf(double[,] value, ref double[,] w0, ref double[,] h0, NonnegativeFactorizationAlgorithm alg, int maxIterations, double normChangeThreshold, double maxFactorChangeThreshold) { double[,] v = value; double[,] h = h0; double[,] w = w0; double[,] z = null; double dnorm0 = 0.0; // previous iteration norm // Main loop for (int iteration = 0; iteration < maxIterations; iteration++) { // Check which algorithm should be used if (alg == NonnegativeFactorizationAlgorithm.MultiplicativeUpdate) { // Multiplicative update formula h = new double[k, m]; w = new double[n, k]; if (z == null) { z = new double[k, k]; } // Update H for (int i = 0; i < k; i++) { for (int j = i; j < k; j++) { double s = 0.0; for (int l = 0; l < n; l++) { s += w0[l, i] * w0[l, j]; } z[i, j] = z[j, i] = s; } for (int j = 0; j < m; j++) { double d = 0.0; for (int l = 0; l < k; l++) { d += z[i, l] * h0[l, j]; } double s = 0.0; for (int l = 0; l < n; l++) { s += w0[l, i] * v[l, j]; } h[i, j] = h0[i, j] * s / (d + Special.Epslon(s)); } } // Update W for (int j = 0; j < k; j++) { for (int i = j; i < k; i++) { double s = 0.0; for (int l = 0; l < m; l++) { s += h[i, l] * h[j, l]; } z[i, j] = z[j, i] = s; } for (int i = 0; i < n; i++) { double d = 0.0; for (int l = 0; l < k; l++) { d += w0[i, l] * z[j, l]; } double s = 0.0; for (int l = 0; l < m; l++) { s += v[i, l] * h[j, l]; } w[i, j] = w0[i, j] * s / (d + Special.Epslon(s)); } } } else { // Alternating least squares update h = w0.Solve(v); makepositive(h); w = v.Divide(h); makepositive(w); } // Reconstruct matrix A using W and H double[,] r = w.Multiply(h); // Get norm of difference double dnorm = normdiff(v, r); // Get maximum change in factors double dw = maxdiff(w0, w) / (sqrteps + maxabs(w0)); double dh = maxdiff(h0, h) / (sqrteps + maxabs(h0)); double delta = System.Math.Max(dw, dh); if (iteration > 0) // Check for convergence { if (delta <= maxFactorChangeThreshold || dnorm <= normChangeThreshold * dnorm0) { break; } } // Remember previous iteration results dnorm0 = dnorm; w0 = w; h0 = h; } return(dnorm0); }