/// <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> /// /// </summary> /// <param name="m">Matrix</param> private void Data(float[,] m) { this.l0 = m.GetLength(0); this.l1 = m.GetLength(1); this.radius0 = l0 >> 1; this.radius1 = l1 >> 1; this.kernel = Jagged.ToJagged(m); }
/// <summary> /// Initializes LU decomposition. /// </summary> /// <param name="A">Square matrix</param> public LU(float[,] A) { if (!Matrice.IsSquare(A)) { throw new Exception("The matrix must be square"); } // LU-decomposition: ludecomp(Jagged.ToJagged(A)); }
/// <summary> /// Initializes Cholesky decomposition. /// </summary> /// <param name="A">Square symmetric positive definite matrix</param> public Cholesky(float[,] A) { if (!Matrice.IsSquare(A)) { throw new Exception("The matrix must be square"); } // Cholesky decomposition: chol(Jagged.ToJagged(A)); }
/// <summary> /// /// </summary> /// <param name="A"></param> private void qrdecomp(float[,] A) { // params this.m = A.GetLength(0); this.n = A.GetLength(1); this.diag = new float[n]; this.qr = Jagged.ToJagged(A); float nrm, s; int k, i, j; // Main loop. for (k = 0; k < n; k++) { // Compute 2-norm of k-th column without under/overflow. nrm = 0; for (i = k; i < m; i++) { nrm = Maths.Hypotenuse(nrm, qr[i][k]); } if (nrm != 0.0) { // Form k-th Householder vector. if (qr[k][k] < 0) { nrm = -nrm; } for (i = k; i < m; i++) { qr[i][k] /= nrm; // Make v a unit vector } qr[k][k] += 1.0f; // + the (e)kth vector // Apply transformation to remaining columns. for (j = k + 1; j < n; j++) // For each column { s = 0.0f; for (i = k; i < m; i++) // For each row { s += qr[i][k] * qr[i][j]; } s = (-s) / qr[k][k]; // Unit vector product for (i = k; i < m; i++) // For each row { qr[i][j] += s * qr[i][k]; } } } diag[k] = -nrm; } }
/// <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 udldecomp(float[,] a) { int i, j, k; int n = a.GetLength(0); this.upper = new float[n, n]; this.diag = new float[n]; float[][] p = Jagged.ToJagged(a); float alpha, beta, gamma; // Mathematics in science and engineering, v.128, // Factorization methods for discrete sequential estimation, Gerald J. Bierman. // UDU* factorization aglorithm. // for (j = n - 1; j >= 1; j--) { gamma = p[j][j]; diag[j] = gamma; alpha = 1.0f / gamma; for (k = 0; k < j; k++) { beta = p[k][j]; upper[k, j] = alpha * beta; for (i = 0; i <= k; i++) { p[i][k] -= beta * upper[i, j]; } } } diag[0] = p[0][0]; // diagonal eyes: for (i = 0; i < n; i++) { upper[i, i] = 1.0f; } return; }
/// <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 tred2(float[,] a) { int i, j, k; this.matrices = Jagged.ToJagged(a); // Symmetric Householder reduction to tridiagonal form. // This is derived from the Algol procedures tred2 by Bowdler, Martin, Reinsch, and Wilkinson, // Handbook for Auto. Comp., Vol.ii-Linear Algebra, and the corresponding Fortran subroutine in EISPACK. for (j = 0; j < n; j++) { Re[j] = matrices[n - 1][j]; } float scale, h, f, g, hh; // Householder reduction to tridiagonal form. for (i = n - 1; i > 0; i--) { // Scale to avoid under/overflow. scale = 0; h = 0; for (k = 0; k < i; k++) { scale = scale + System.Math.Abs(Re[k]); } if (scale == 0) { Im[i] = Re[i - 1]; for (j = 0; j < i; j++) { Re[j] = matrices[i - 1][j]; matrices[i][j] = 0; matrices[j][i] = 0; } } else { // Generate Householder Matrice. for (k = 0; k < i; k++) { Re[k] /= scale; h += Re[k] * Re[k]; } f = Re[i - 1]; g = (float)System.Math.Sqrt(h); if (f > 0) { g = -g; } Im[i] = scale * g; h = h - f * g; Re[i - 1] = f - g; for (j = 0; j < i; j++) { Im[j] = 0; } // Apply similarity transformation to remaining columns. for (j = 0; j < i; j++) { f = Re[j]; matrices[j][i] = f; g = Im[j] + matrices[j][j] * f; for (k = j + 1; k <= i - 1; k++) { g += matrices[k][j] * Re[k]; Im[k] += matrices[k][j] * f; } Im[j] = g; } f = 0; for (j = 0; j < i; j++) { Im[j] /= h; f += Im[j] * Re[j]; } hh = f / (h + h); for (j = 0; j < i; j++) { Im[j] -= hh * Re[j]; } for (j = 0; j < i; j++) { f = Re[j]; g = Im[j]; for (k = j; k <= i - 1; k++) { matrices[k][j] -= (f * Im[k] + g * Re[k]); } Re[j] = matrices[i - 1][j]; matrices[i][j] = 0; } } Re[i] = h; } // Accumulate transformations. for (i = 0; i < n - 1; i++) { matrices[n - 1][i] = matrices[i][i]; matrices[i][i] = 1; h = Re[i + 1]; if (h != 0) { for (k = 0; k <= i; k++) { Re[k] = matrices[k][i + 1] / h; } for (j = 0; j <= i; j++) { g = 0; for (k = 0; k <= i; k++) { g += matrices[k][i + 1] * matrices[k][j]; } for (k = 0; k <= i; k++) { matrices[k][j] -= g * Re[k]; } } } for (k = 0; k <= i; k++) { matrices[k][i + 1] = 0; } } for (j = 0; j < n; j++) { Re[j] = matrices[n - 1][j]; matrices[n - 1][j] = 0; } matrices[n - 1][n - 1] = 1; Im[0] = 0; return; }
/// <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; } } } }