/// <summary> /// Initializes eigenvalue decomposition. /// </summary> /// <param name="A">Square matrix</param> /// <param name="eps">Epsilon [0, 1]</param> public EVD(float[,] A, float eps = 1e-16f) { if (!Matrice.IsSquare(A)) { throw new Exception("The matrix must be square"); } this.n = A.GetLength(0); this.Re = new float[n]; this.Im = new float[n]; this.eps = Maths.Float(eps); // for symmetric matrices eigen-value decomposition // without Hessenberg form. if (Matrice.IsSymmetric(A)) { hessenberg = Jagged.Zero(n, n); matrices = Jagged.ToJagged(A); tred2(); // Tridiagonalize. tql2(); // Diagonalize. } // with Hessenberg form. else { matrices = Jagged.Zero(n, n); hessenberg = Jagged.ToJagged(A); orthogonal = new float[n]; orthes(); // Reduce to Hessenberg form. hqr2(); // Reduce Hessenberg to real Schur form. } }
/// <summary> /// Initializes generalized eigenvalue decomposition. /// </summary> /// <param name="a">Matrix A</param> /// <param name="b">Matrix B</param> /// <param name="eps">Epsilon [0, 1]</param> public GEVD(float[,] a, float[,] b, float eps = 1e-16f) { if (a.GetLength(0) != a.GetLength(1)) { throw new ArgumentException("The matrix must be square"); } if (b.GetLength(0) != b.GetLength(1)) { throw new ArgumentException("The matrix must be square"); } if (a.GetLength(0) != b.GetLength(0) || a.GetLength(1) != b.GetLength(1)) { throw new ArgumentException("Matrices should be the same size"); } // params this.n = a.GetLength(0); this.ar = new float[n]; this.ai = new float[n]; this.beta = new float[n]; this.Z = Jagged.Zero(n, n); var A = Jagged.ToJagged(a); var B = Jagged.ToJagged(b); bool matz = true; int ierr = 0; // reduces A to upper Hessenberg form and B to upper // triangular form using orthogonal transformations qzhes(n, A, B, matz, Z); // reduces the Hessenberg matrix A to quasi-triangular form // using orthogonal transformations while maintaining the // triangular form of the B matrix. qzit(n, A, B, Maths.Float(eps), matz, Z, ref ierr); // reduces the quasi-triangular matrix further, so that any // remaining 2-by-2 blocks correspond to pairs of complex // eigenvalues, and returns quantities whose ratios give the // generalized eigenvalues. qzval(n, A, B, ar, ai, beta, matz, Z); // computes the eigenvectors of the triangular problem and // transforms the results back to the original coordinate system. qzvec(n, A, B, ar, ai, beta, Z); }
/// <summary> /// /// </summary> /// <param name="a"></param> private void ludecomp(float[][] a) { int i, j, k; int n = a.GetLength(0); float alpha, beta; this.upper = Jagged.Zero(n, n); this.lower = Jagged.Zero(n, n); for (i = 0; i < n; i++) { this.upper[i][i] = 1; } for (j = 0; j < n; j++) { for (i = j; i < n; i++) { alpha = 0; for (k = 0; k < j; k++) { alpha = alpha + this.lower[i][k] * this.upper[k][j]; } this.lower[i][j] = a[i][j] - alpha; } beta = lower[j][j]; for (i = j; i < n; i++) { alpha = 0; for (k = 0; k < j; k++) { alpha = alpha + this.lower[j][k] * this.upper[k][i]; } if (beta != 0) { this.upper[j][i] = (a[j][i] - alpha) / beta; } } } }
/// <summary> /// /// </summary> /// <param name="A"></param> private void svdcmp(float[,] A) { this.Ur = Jagged.ToJagged(A); this.Sr = new float[m]; this.Vr = Jagged.Zero(m, m); float[] rv1 = new float[m]; int flag, i, its, j, jj, k, l = 0, nm = 0; float anorm, c, f, g, h, e, scale, x, y, z; // householder reduction to bidiagonal form g = scale = anorm = 0.0f; for (i = 0; i < m; i++) { l = i + 1; rv1[i] = scale * g; g = e = scale = 0; if (i < n) { for (k = i; k < n; k++) { scale += Math.Abs(Ur[k][i]); } if (scale != 0.0) { for (k = i; k < n; k++) { Ur[k][i] /= scale; e += Ur[k][i] * Ur[k][i]; } f = Ur[i][i]; g = -Sign((float)Math.Sqrt(e), f); h = f * g - e; Ur[i][i] = f - g; if (i != m - 1) { for (j = l; j < m; j++) { for (e = 0.0f, k = i; k < n; k++) { e += Ur[k][i] * Ur[k][j]; } f = e / h; for (k = i; k < n; k++) { Ur[k][j] += f * Ur[k][i]; } } } for (k = i; k < n; k++) { Ur[k][i] *= scale; } } } Sr[i] = scale * g; g = e = scale = 0.0f; if ((i < n) && (i != m - 1)) { for (k = l; k < m; k++) { scale += Math.Abs(Ur[i][k]); } if (scale != 0.0) { for (k = l; k < m; k++) { Ur[i][k] /= scale; e += Ur[i][k] * Ur[i][k]; } f = Ur[i][l]; g = -Sign((float)Math.Sqrt(e), f); h = f * g - e; Ur[i][l] = f - g; for (k = l; k < m; k++) { rv1[k] = Ur[i][k] / h; } if (i != n - 1) { for (j = l; j < n; j++) { for (e = 0.0f, k = l; k < m; k++) { e += Ur[j][k] * Ur[i][k]; } for (k = l; k < m; k++) { Ur[j][k] += e * rv1[k]; } } } for (k = l; k < m; k++) { Ur[i][k] *= scale; } } } anorm = Math.Max(anorm, (Math.Abs(Sr[i]) + Math.Abs(rv1[i]))); } // accumulation of right-hand transformations for (i = m - 1; i >= 0; i--) { if (i < m - 1) { if (g != 0.0) { for (j = l; j < m; j++) { Vr[j][i] = (Ur[i][j] / Ur[i][l]) / g; } for (j = l; j < m; j++) { for (e = 0, k = l; k < m; k++) { e += Ur[i][k] * Vr[k][j]; } for (k = l; k < m; k++) { Vr[k][j] += e * Vr[k][i]; } } } for (j = l; j < m; j++) { Vr[i][j] = Vr[j][i] = 0; } } Vr[i][i] = 1; g = rv1[i]; l = i; } // accumulation of left-hand transformations for (i = m - 1; i >= 0; i--) { l = i + 1; g = Sr[i]; if (i < m - 1) { for (j = l; j < m; j++) { Ur[i][j] = 0.0f; } } if (g != 0) { g = 1.0f / g; if (i != m - 1) { for (j = l; j < m; j++) { for (e = 0, k = l; k < n; k++) { e += Ur[k][i] * Ur[k][j]; } f = (e / Ur[i][i]) * g; for (k = i; k < n; k++) { Ur[k][j] += f * Ur[k][i]; } } } for (j = i; j < n; j++) { Ur[j][i] *= g; } } else { for (j = i; j < n; j++) { Ur[j][i] = 0; } } ++Ur[i][i]; } // diagonalization of the bidiagonal form: Loop over singular values // and over allowed iterations for (k = m - 1; k >= 0; k--) { for (its = 1; its <= iterations; its++) { flag = 1; for (l = k; l >= 0; l--) { // test for splitting nm = l - 1; if (Math.Abs(rv1[l]) + anorm == anorm) { flag = 0; break; } if (Math.Abs(Sr[nm]) + anorm == anorm) { break; } } if (flag != 0) { c = 0.0f; e = 1.0f; for (i = l; i <= k; i++) { f = e * rv1[i]; if (Math.Abs(f) + anorm != anorm) { g = Sr[i]; h = Maths.Hypotenuse(f, g); Sr[i] = h; h = 1.0f / h; c = g * h; e = -f * h; //for (j = 1; j <= m; j++) for (j = 1; j < n; j++) { y = Ur[j][nm]; z = Ur[j][i]; Ur[j][nm] = y * c + z * e; Ur[j][i] = z * c - y * e; } } } } z = Sr[k]; if (l == k) { // convergence if (z < 0.0) { // singular value is made nonnegative Sr[k] = -z; for (j = 0; j < m; j++) { Vr[j][k] = -Vr[j][k]; } } break; } if (its == iterations) { throw new ApplicationException("No convergence in " + iterations.ToString() + " iterations of singular decomposition"); } // shift from bottom 2-by-2 minor x = Sr[l]; nm = k - 1; y = Sr[nm]; g = rv1[nm]; h = rv1[k]; f = ((y - z) * (y + z) + (g - h) * (g + h)) / (2.0f * h * y); g = Maths.Hypotenuse(f, 1.0f); f = ((x - z) * (x + z) + h * ((y / (f + Sign(g, f))) - h)) / x; // next QR transformation c = e = 1.0f; for (j = l; j <= nm; j++) { i = j + 1; g = rv1[i]; y = Sr[i]; h = e * g; g = c * g; z = Maths.Hypotenuse(f, h); rv1[j] = z; c = f / z; e = h / z; f = x * c + g * e; g = g * c - x * e; h = y * e; y *= c; for (jj = 0; jj < m; jj++) { x = Vr[jj][j]; z = Vr[jj][i]; Vr[jj][j] = x * c + z * e; Vr[jj][i] = z * c - x * e; } z = Maths.Hypotenuse(f, h); Sr[j] = z; if (z != 0) { z = 1.0f / z; c = f * z; e = h * z; } f = c * g + e * y; x = c * y - e * g; for (jj = 0; jj < n; jj++) { y = Ur[jj][j]; z = Ur[jj][i]; Ur[jj][j] = y * c + z * e; Ur[jj][i] = z * c - y * e; } } rv1[l] = 0.0f; rv1[k] = f; Sr[k] = x; } } }
/// <summary> /// /// </summary> /// <param name="A"></param> private void orthes(float[,] A) { // Properties int n = A.GetLength(0); this.matrices = Jagged.Zero(n, n); this.hessenberg = Jagged.ToJagged(A); float[] orthogonal = new float[n]; // Nonsymmetric reduction to Hessenberg form. // This is derived from the Algol procedures orthes and ortran, by Martin and Wilkinson, // Handbook for Auto. Comp., Vol.ii-Linear Algebra, and the corresponding Fortran subroutines in EISPACK. int low = 0; int high = n - 1; int m, i, j; float scale, h, g, f; for (m = low + 1; m <= high - 1; m++) { // Scale column. scale = 0; for (i = m; i <= high; i++) { scale = scale + System.Math.Abs(hessenberg[i][m - 1]); } if (scale != 0) { // Compute Householder transformation. h = 0; for (i = high; i >= m; i--) { orthogonal[i] = hessenberg[i][m - 1] / scale; h += orthogonal[i] * orthogonal[i]; } g = (float)System.Math.Sqrt(h); if (orthogonal[m] > 0) { g = -g; } h = h - orthogonal[m] * g; orthogonal[m] = orthogonal[m] - g; // Apply Householder similarity transformation // H = (I - u * u' / h) * H * (I - u * u') / h) for (j = m; j < n; j++) { f = 0; for (i = high; i >= m; i--) { f += orthogonal[i] * hessenberg[i][j]; } f = f / h; for (i = m; i <= high; i++) { hessenberg[i][j] -= f * orthogonal[i]; } } for (i = 0; i <= high; i++) { f = 0; for (j = high; j >= m; j--) { f += orthogonal[j] * hessenberg[i][j]; } f = f / h; for (j = m; j <= high; j++) { hessenberg[i][j] -= f * orthogonal[j]; } } orthogonal[m] = scale * orthogonal[m]; hessenberg[m][m - 1] = scale * g; } } // Accumulate transformations (Algol's ortran). for (i = 0; i < n; i++) { for (j = 0; j < n; j++) { matrices[i][j] = (i == j ? 1 : 0); } } for (m = high - 1; m >= low + 1; m--) { if (hessenberg[m][m - 1] != 0) { for (i = m + 1; i <= high; i++) { orthogonal[i] = hessenberg[i][m - 1]; } for (j = m; j <= high; j++) { g = 0; for (i = m; i <= high; i++) { g += orthogonal[i] * matrices[i][j]; } // float division avoids possible underflow. g = (g / orthogonal[m]) / hessenberg[m][m - 1]; for (i = m; i <= high; i++) { matrices[i][j] += g * orthogonal[i]; } } } } // final reduction: if (n > 2) { for (i = 0; i < n - 2; i++) { for (j = i + 2; j < n; j++) { hessenberg[j][i] = 0; } } } }