/// <summary> /// Initializes a new instance of the <see cref="UserEvd"/> class. This object will compute the /// the eigenvalue decomposition when the constructor is called and cache it's decomposition. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If EVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception> public static UserEvd Create(Matrix<Complex32> matrix) { if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } var order = matrix.RowCount; // Initialize matricies for eigenvalues and eigenvectors var eigenVectors = DenseMatrix.CreateIdentity(order); var blockDiagonal = matrix.CreateMatrix(order, order); var eigenValues = new LinearAlgebra.Complex.DenseVector(order); var isSymmetric = true; for (var i = 0; isSymmetric && i < order; i++) { for (var j = 0; isSymmetric && j < order; j++) { isSymmetric &= matrix.At(i, j) == matrix.At(j, i).Conjugate(); } } if (isSymmetric) { var matrixCopy = matrix.ToArray(); var tau = new Complex32[order]; var d = new float[order]; var e = new float[order]; SymmetricTridiagonalize(matrixCopy, d, e, tau, order); SymmetricDiagonalize(eigenVectors, d, e, order); SymmetricUntridiagonalize(eigenVectors, matrixCopy, tau, order); for (var i = 0; i < order; i++) { eigenValues[i] = new Complex(d[i], e[i]); } } else { var matrixH = matrix.ToArray(); NonsymmetricReduceToHessenberg(eigenVectors, matrixH, order); NonsymmetricReduceHessenberToRealSchur(eigenVectors, eigenValues, matrixH, order); } for (var i = 0; i < eigenValues.Count; i++) { blockDiagonal.At(i, i, (Complex32) eigenValues[i]); } return new UserEvd(eigenVectors, eigenValues, blockDiagonal, isSymmetric); }
/// <summary> /// Initializes a new instance of the <see cref="UserEvd"/> class. This object will compute the /// the eigenvalue decomposition when the constructor is called and cache it's decomposition. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If EVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception> public UserEvd(Matrix<Complex> matrix) { if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } var order = matrix.RowCount; // Initialize matricies for eigenvalues and eigenvectors MatrixEv = DenseMatrix.Identity(order); MatrixD = matrix.CreateMatrix(order, order); VectorEv = new DenseVector(order); IsSymmetric = true; for (var i = 0; IsSymmetric && i < order; i++) { for (var j = 0; IsSymmetric && j < order; j++) { IsSymmetric &= matrix.At(i, j) == matrix.At(j, i).Conjugate(); } } if (IsSymmetric) { var matrixCopy = matrix.ToArray(); var tau = new Complex[order]; var d = new double[order]; var e = new double[order]; SymmetricTridiagonalize(matrixCopy, d, e, tau, order); SymmetricDiagonalize(d, e, order); SymmetricUntridiagonalize(matrixCopy, tau, order); for (var i = 0; i < order; i++) { VectorEv[i] = new Complex(d[i], e[i]); } } else { var matrixH = matrix.ToArray(); NonsymmetricReduceToHessenberg(matrixH, order); NonsymmetricReduceHessenberToRealSchur(matrixH, order); } MatrixD.SetDiagonal(VectorEv); }
/// <summary> /// Initializes a new instance of the <see cref="UserCholesky"/> class. This object will compute the /// Cholesky factorization when the constructor is called and cache it's factorization. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If <paramref name="matrix"/> is not a square matrix.</exception> /// <exception cref="ArgumentException">If <paramref name="matrix"/> is not positive definite.</exception> public UserCholesky(Matrix matrix) { if (matrix == null) { throw new ArgumentNullException("matrix"); } if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } // Create a new matrix for the Cholesky factor, then perform factorization (while overwriting). CholeskyFactor = matrix.Clone(); for (var j = 0; j < CholeskyFactor.RowCount; j++) { var d = 0.0; for (var k = 0; k < j; k++) { var s = 0.0; for (var i = 0; i < k; i++) { s += CholeskyFactor.At(k, i) * CholeskyFactor.At(j, i); } s = (matrix.At(j, k) - s) / CholeskyFactor.At(k, k); CholeskyFactor.At(j, k, s); d += s * s; } d = matrix.At(j, j) - d; if (d <= 0.0) { throw new ArgumentException(Resources.ArgumentMatrixPositiveDefinite); } CholeskyFactor.At(j, j, Math.Sqrt(d)); for (var k = j + 1; k < CholeskyFactor.RowCount; k++) { CholeskyFactor.At(j, k, 0.0); } } }
/// <summary> /// Initializes a new instance of the <see cref="UserEvd"/> class. This object will compute the /// the eigenvalue decomposition when the constructor is called and cache it's decomposition. /// </summary> /// <param name="matrix">The matrix to factor.</param> /// <exception cref="ArgumentNullException">If <paramref name="matrix"/> is <c>null</c>.</exception> /// <exception cref="ArgumentException">If EVD algorithm failed to converge with matrix <paramref name="matrix"/>.</exception> public static UserEvd Create(Matrix<float> matrix) { if (matrix.RowCount != matrix.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSquare); } var order = matrix.RowCount; // Initialize matricies for eigenvalues and eigenvectors var eigenVectors = Matrix<float>.Build.SameAs(matrix, order, order); var blockDiagonal = Matrix<float>.Build.SameAs(matrix, order, order); var eigenValues = new LinearAlgebra.Complex.DenseVector(order); var isSymmetric = true; for (var i = 0; isSymmetric && i < order; i++) { for (var j = 0; isSymmetric && j < order; j++) { isSymmetric &= matrix.At(i, j) == matrix.At(j, i); } } var d = new float[order]; var e = new float[order]; if (isSymmetric) { matrix.CopyTo(eigenVectors); d = eigenVectors.Row(order - 1).ToArray(); SymmetricTridiagonalize(eigenVectors, d, e, order); SymmetricDiagonalize(eigenVectors, d, e, order); } else { var matrixH = matrix.ToArray(); NonsymmetricReduceToHessenberg(eigenVectors, matrixH, order); NonsymmetricReduceHessenberToRealSchur(eigenVectors, matrixH, d, e, order); } for (var i = 0; i < order; i++) { blockDiagonal.At(i, i, d[i]); if (e[i] > 0) { blockDiagonal.At(i, i + 1, e[i]); } else if (e[i] < 0) { blockDiagonal.At(i, i - 1, e[i]); } } for (var i = 0; i < order; i++) { eigenValues[i] = new Complex(d[i], e[i]); } return new UserEvd(eigenVectors, eigenValues, blockDiagonal, isSymmetric); }
/// <summary> /// Nonsymmetric reduction from Hessenberg to real Schur form. /// </summary> /// <param name="eigenVectors">The eigen vectors to work on.</param> /// <param name="matrixH">Array for internal storage of nonsymmetric Hessenberg form.</param> /// <param name="d">Arrays for internal storage of real parts of eigenvalues</param> /// <param name="e">Arrays for internal storage of imaginary parts of eigenvalues</param> /// <param name="order">Order of initial matrix</param> /// <remarks>This is derived from the Algol procedure hqr2, /// by Martin and Wilkinson, Handbook for Auto. Comp., /// Vol.ii-Linear Algebra, and the corresponding /// Fortran subroutine in EISPACK.</remarks> static void NonsymmetricReduceHessenberToRealSchur(Matrix<float> eigenVectors, float[,] matrixH, float[] d, float[] e, int order) { // Initialize var n = order - 1; var eps = (float) Precision.SinglePrecision; var exshift = 0.0f; float p = 0, q = 0, r = 0, s = 0, z = 0, w, x, y; // Store roots isolated by balanc and compute matrix norm var norm = 0.0f; for (var i = 0; i < order; i++) { for (var j = Math.Max(i - 1, 0); j < order; j++) { norm = norm + Math.Abs(matrixH[i, j]); } } // Outer loop over eigenvalue index var iter = 0; while (n >= 0) { // Look for single small sub-diagonal element var l = n; while (l > 0) { s = Math.Abs(matrixH[l - 1, l - 1]) + Math.Abs(matrixH[l, l]); if (s == 0.0f) { s = norm; } if (Math.Abs(matrixH[l, l - 1]) < eps*s) { break; } l--; } // Check for convergence // One root found if (l == n) { matrixH[n, n] = matrixH[n, n] + exshift; d[n] = matrixH[n, n]; e[n] = 0.0f; n--; iter = 0; // Two roots found } else if (l == n - 1) { w = matrixH[n, n - 1]*matrixH[n - 1, n]; p = (matrixH[n - 1, n - 1] - matrixH[n, n])/2.0f; q = (p*p) + w; z = (float) Math.Sqrt(Math.Abs(q)); matrixH[n, n] = matrixH[n, n] + exshift; matrixH[n - 1, n - 1] = matrixH[n - 1, n - 1] + exshift; x = matrixH[n, n]; // Real pair if (q >= 0) { if (p >= 0) { z = p + z; } else { z = p - z; } d[n - 1] = x + z; d[n] = d[n - 1]; if (z != 0.0f) { d[n] = x - (w/z); } e[n - 1] = 0.0f; e[n] = 0.0f; x = matrixH[n, n - 1]; s = Math.Abs(x) + Math.Abs(z); p = x/s; q = z/s; r = (float) Math.Sqrt((p*p) + (q*q)); p = p/r; q = q/r; // Row modification for (var j = n - 1; j < order; j++) { z = matrixH[n - 1, j]; matrixH[n - 1, j] = (q*z) + (p*matrixH[n, j]); matrixH[n, j] = (q*matrixH[n, j]) - (p*z); } // Column modification for (var i = 0; i <= n; i++) { z = matrixH[i, n - 1]; matrixH[i, n - 1] = (q*z) + (p*matrixH[i, n]); matrixH[i, n] = (q*matrixH[i, n]) - (p*z); } // Accumulate transformations for (var i = 0; i < order; i++) { z = eigenVectors.At(i, n - 1); eigenVectors.At(i, n - 1, (q*z) + (p*eigenVectors.At(i, n))); eigenVectors.At(i, n, (q*eigenVectors.At(i, n)) - (p*z)); } // Complex pair } else { d[n - 1] = x + p; d[n] = x + p; e[n - 1] = z; e[n] = -z; } n = n - 2; iter = 0; // No convergence yet } else { // Form shift x = matrixH[n, n]; y = 0.0f; w = 0.0f; if (l < n) { y = matrixH[n - 1, n - 1]; w = matrixH[n, n - 1]*matrixH[n - 1, n]; } // Wilkinson's original ad hoc shift if (iter == 10) { exshift += x; for (var i = 0; i <= n; i++) { matrixH[i, i] -= x; } s = Math.Abs(matrixH[n, n - 1]) + Math.Abs(matrixH[n - 1, n - 2]); x = y = 0.75f*s; w = (-0.4375f)*s*s; } // MATLAB's new ad hoc shift if (iter == 30) { s = (y - x)/2.0f; s = (s*s) + w; if (s > 0) { s = (float) Math.Sqrt(s); if (y < x) { s = -s; } s = x - (w/(((y - x)/2.0f) + s)); for (var i = 0; i <= n; i++) { matrixH[i, i] -= s; } exshift += s; x = y = w = 0.964f; } } iter = iter + 1; // (Could check iteration count here.) // Look for two consecutive small sub-diagonal elements var m = n - 2; while (m >= l) { z = matrixH[m, m]; r = x - z; s = y - z; p = (((r*s) - w)/matrixH[m + 1, m]) + matrixH[m, m + 1]; q = matrixH[m + 1, m + 1] - z - r - s; r = matrixH[m + 2, m + 1]; s = Math.Abs(p) + Math.Abs(q) + Math.Abs(r); p = p/s; q = q/s; r = r/s; if (m == l) { break; } if (Math.Abs(matrixH[m, m - 1])*(Math.Abs(q) + Math.Abs(r)) < eps*(Math.Abs(p)*(Math.Abs(matrixH[m - 1, m - 1]) + Math.Abs(z) + Math.Abs(matrixH[m + 1, m + 1])))) { break; } m--; } for (var i = m + 2; i <= n; i++) { matrixH[i, i - 2] = 0.0f; if (i > m + 2) { matrixH[i, i - 3] = 0.0f; } } // Double QR step involving rows l:n and columns m:n for (var k = m; k <= n - 1; k++) { bool notlast = k != n - 1; if (k != m) { p = matrixH[k, k - 1]; q = matrixH[k + 1, k - 1]; r = notlast ? matrixH[k + 2, k - 1] : 0.0f; x = Math.Abs(p) + Math.Abs(q) + Math.Abs(r); if (x != 0.0f) { p = p/x; q = q/x; r = r/x; } } if (x == 0.0f) { break; } s = (float) Math.Sqrt((p*p) + (q*q) + (r*r)); if (p < 0) { s = -s; } if (s != 0.0f) { if (k != m) { matrixH[k, k - 1] = (-s)*x; } else if (l != m) { matrixH[k, k - 1] = -matrixH[k, k - 1]; } p = p + s; x = p/s; y = q/s; z = r/s; q = q/p; r = r/p; // Row modification for (var j = k; j < order; j++) { p = matrixH[k, j] + (q*matrixH[k + 1, j]); if (notlast) { p = p + (r*matrixH[k + 2, j]); matrixH[k + 2, j] = matrixH[k + 2, j] - (p*z); } matrixH[k, j] = matrixH[k, j] - (p*x); matrixH[k + 1, j] = matrixH[k + 1, j] - (p*y); } // Column modification for (var i = 0; i <= Math.Min(n, k + 3); i++) { p = (x*matrixH[i, k]) + (y*matrixH[i, k + 1]); if (notlast) { p = p + (z*matrixH[i, k + 2]); matrixH[i, k + 2] = matrixH[i, k + 2] - (p*r); } matrixH[i, k] = matrixH[i, k] - p; matrixH[i, k + 1] = matrixH[i, k + 1] - (p*q); } // Accumulate transformations for (var i = 0; i < order; i++) { p = (x*eigenVectors.At(i, k)) + (y*eigenVectors.At(i, k + 1)); if (notlast) { p = p + (z*eigenVectors.At(i, k + 2)); eigenVectors.At(i, k + 2, eigenVectors.At(i, k + 2) - (p*r)); } eigenVectors.At(i, k, eigenVectors.At(i, k) - p); eigenVectors.At(i, k + 1, eigenVectors.At(i, k + 1) - (p*q)); } } // (s != 0) } // k loop } // check convergence } // while (n >= low) // Backsubstitute to find vectors of upper triangular form if (norm == 0.0f) { return; } for (n = order - 1; n >= 0; n--) { float t; p = d[n]; q = e[n]; // Real vector if (q == 0.0f) { var l = n; matrixH[n, n] = 1.0f; for (var i = n - 1; i >= 0; i--) { w = matrixH[i, i] - p; r = 0.0f; for (var j = l; j <= n; j++) { r = r + (matrixH[i, j]*matrixH[j, n]); } if (e[i] < 0.0f) { z = w; s = r; } else { l = i; if (e[i] == 0.0f) { if (w != 0.0f) { matrixH[i, n] = (-r)/w; } else { matrixH[i, n] = (-r)/(eps*norm); } // Solve real equations } else { x = matrixH[i, i + 1]; y = matrixH[i + 1, i]; q = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]); t = ((x*s) - (z*r))/q; matrixH[i, n] = t; if (Math.Abs(x) > Math.Abs(z)) { matrixH[i + 1, n] = (-r - (w*t))/x; } else { matrixH[i + 1, n] = (-s - (y*t))/z; } } // Overflow control t = Math.Abs(matrixH[i, n]); if ((eps*t)*t > 1) { for (var j = i; j <= n; j++) { matrixH[j, n] = matrixH[j, n]/t; } } } } // Complex vector } else if (q < 0) { var l = n - 1; // Last vector component imaginary so matrix is triangular if (Math.Abs(matrixH[n, n - 1]) > Math.Abs(matrixH[n - 1, n])) { matrixH[n - 1, n - 1] = q/matrixH[n, n - 1]; matrixH[n - 1, n] = (-(matrixH[n, n] - p))/matrixH[n, n - 1]; } else { var res = Cdiv(0.0f, -matrixH[n - 1, n], matrixH[n - 1, n - 1] - p, q); matrixH[n - 1, n - 1] = res.Real; matrixH[n - 1, n] = res.Imaginary; } matrixH[n, n - 1] = 0.0f; matrixH[n, n] = 1.0f; for (var i = n - 2; i >= 0; i--) { float ra = 0.0f; float sa = 0.0f; for (var j = l; j <= n; j++) { ra = ra + (matrixH[i, j]*matrixH[j, n - 1]); sa = sa + (matrixH[i, j]*matrixH[j, n]); } w = matrixH[i, i] - p; if (e[i] < 0.0f) { z = w; r = ra; s = sa; } else { l = i; if (e[i] == 0.0f) { var res = Cdiv(-ra, -sa, w, q); matrixH[i, n - 1] = res.Real; matrixH[i, n] = res.Imaginary; } else { // Solve complex equations x = matrixH[i, i + 1]; y = matrixH[i + 1, i]; float vr = ((d[i] - p)*(d[i] - p)) + (e[i]*e[i]) - (q*q); float vi = (d[i] - p)*2.0f*q; if ((vr == 0.0f) && (vi == 0.0f)) { vr = eps*norm*(Math.Abs(w) + Math.Abs(q) + Math.Abs(x) + Math.Abs(y) + Math.Abs(z)); } var res = Cdiv((x*r) - (z*ra) + (q*sa), (x*s) - (z*sa) - (q*ra), vr, vi); matrixH[i, n - 1] = res.Real; matrixH[i, n] = res.Imaginary; if (Math.Abs(x) > (Math.Abs(z) + Math.Abs(q))) { matrixH[i + 1, n - 1] = (-ra - (w*matrixH[i, n - 1]) + (q*matrixH[i, n]))/x; matrixH[i + 1, n] = (-sa - (w*matrixH[i, n]) - (q*matrixH[i, n - 1]))/x; } else { res = Cdiv(-r - (y*matrixH[i, n - 1]), -s - (y*matrixH[i, n]), z, q); matrixH[i + 1, n - 1] = res.Real; matrixH[i + 1, n] = res.Imaginary; } } // Overflow control t = Math.Max(Math.Abs(matrixH[i, n - 1]), Math.Abs(matrixH[i, n])); if ((eps*t)*t > 1) { for (var j = i; j <= n; j++) { matrixH[j, n - 1] = matrixH[j, n - 1]/t; matrixH[j, n] = matrixH[j, n]/t; } } } } } } // Back transformation to get eigenvectors of original matrix for (var j = order - 1; j >= 0; j--) { for (var i = 0; i < order; i++) { z = 0.0f; for (var k = 0; k <= j; k++) { z = z + (eigenVectors.At(i, k)*matrixH[k, j]); } eigenVectors.At(i, j, z); } } }
/// <summary> /// Symmetric Householder reduction to tridiagonal form. /// </summary> /// <param name="eigenVectors">The eigen vectors to work on.</param> /// <param name="d">Arrays for internal storage of real parts of eigenvalues</param> /// <param name="e">Arrays for internal storage of imaginary parts of eigenvalues</param> /// <param name="order">Order of initial matrix</param> /// <remarks>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.</remarks> static void SymmetricTridiagonalize(Matrix<float> eigenVectors, float[] d, float[] e, int order) { // Householder reduction to tridiagonal form. for (var i = order - 1; i > 0; i--) { // Scale to avoid under/overflow. var scale = 0.0f; var h = 0.0f; for (var k = 0; k < i; k++) { scale = scale + Math.Abs(d[k]); } if (scale == 0.0f) { e[i] = d[i - 1]; for (var j = 0; j < i; j++) { d[j] = eigenVectors.At(i - 1, j); eigenVectors.At(i, j, 0.0f); eigenVectors.At(j, i, 0.0f); } } else { // Generate Householder vector. for (var k = 0; k < i; k++) { d[k] /= scale; h += d[k]*d[k]; } var f = d[i - 1]; var g = (float) Math.Sqrt(h); if (f > 0) { g = -g; } e[i] = scale*g; h = h - (f*g); d[i - 1] = f - g; for (var j = 0; j < i; j++) { e[j] = 0.0f; } // Apply similarity transformation to remaining columns. for (var j = 0; j < i; j++) { f = d[j]; eigenVectors.At(j, i, f); g = e[j] + (eigenVectors.At(j, j)*f); for (var k = j + 1; k <= i - 1; k++) { g += eigenVectors.At(k, j)*d[k]; e[k] += eigenVectors.At(k, j)*f; } e[j] = g; } f = 0.0f; for (var j = 0; j < i; j++) { e[j] /= h; f += e[j]*d[j]; } var hh = f/(h + h); for (var j = 0; j < i; j++) { e[j] -= hh*d[j]; } for (var j = 0; j < i; j++) { f = d[j]; g = e[j]; for (var k = j; k <= i - 1; k++) { eigenVectors.At(k, j, eigenVectors.At(k, j) - (f*e[k]) - (g*d[k])); } d[j] = eigenVectors.At(i - 1, j); eigenVectors.At(i, j, 0.0f); } } d[i] = h; } // Accumulate transformations. for (var i = 0; i < order - 1; i++) { eigenVectors.At(order - 1, i, eigenVectors.At(i, i)); eigenVectors.At(i, i, 1.0f); var h = d[i + 1]; if (h != 0.0f) { for (var k = 0; k <= i; k++) { d[k] = eigenVectors.At(k, i + 1)/h; } for (var j = 0; j <= i; j++) { var g = 0.0f; for (var k = 0; k <= i; k++) { g += eigenVectors.At(k, i + 1)*eigenVectors.At(k, j); } for (var k = 0; k <= i; k++) { eigenVectors.At(k, j, eigenVectors.At(k, j) - g*d[k]); } } } for (var k = 0; k <= i; k++) { eigenVectors.At(k, i + 1, 0.0f); } } for (var j = 0; j < order; j++) { d[j] = eigenVectors.At(order - 1, j); eigenVectors.At(order - 1, j, 0.0f); } eigenVectors.At(order - 1, order - 1, 1.0f); e[0] = 0.0f; }
/// <summary> /// Calculate Cholesky step /// </summary> /// <param name="data">Factor matrix</param> /// <param name="rowDim">Number of rows</param> /// <param name="firstCol">Column start</param> /// <param name="colLimit">Total columns</param> /// <param name="multipliers">Multipliers calculated previously</param> /// <param name="availableCores">Number of available processors</param> private static void DoCholeskyStep(Matrix<Complex32> data, int rowDim, int firstCol, int colLimit, Complex32[] multipliers, int availableCores) { var tmpColCount = colLimit - firstCol; if ((availableCores > 1) && (tmpColCount > 200)) { var tmpSplit = firstCol + (tmpColCount / 3); var tmpCores = availableCores / 2; CommonParallel.Invoke( () => DoCholeskyStep(data, rowDim, firstCol, tmpSplit, multipliers, tmpCores), () => DoCholeskyStep(data, rowDim, tmpSplit, colLimit, multipliers, tmpCores)); } else { for (var j = firstCol; j < colLimit; j++) { var tmpVal = multipliers[j]; for (var i = j; i < rowDim; i++) { data.At(i, j, data.At(i, j) - (multipliers[i] * tmpVal.Conjugate())); } } } }
/// <summary> /// Multiplies the transpose of this matrix with another matrix and places the results into the result matrix. /// </summary> /// <param name="other">The matrix to multiply with.</param> /// <param name="result">The result of the multiplication.</param> protected override void DoTransposeThisAndMultiply(Matrix<Complex> other, Matrix<Complex> result) { var diagonalOther = other as DiagonalMatrix; var diagonalResult = result as DiagonalMatrix; if (diagonalOther != null && diagonalResult != null) { var thisDataCopy = new Complex[diagonalResult._data.Length]; var otherDataCopy = new Complex[diagonalResult._data.Length]; Array.Copy(_data, thisDataCopy, (diagonalResult._data.Length > _data.Length) ? _data.Length : diagonalResult._data.Length); Array.Copy(diagonalOther._data, otherDataCopy, (diagonalResult._data.Length > diagonalOther._data.Length) ? diagonalOther._data.Length : diagonalResult._data.Length); Control.LinearAlgebraProvider.PointWiseMultiplyArrays(thisDataCopy, otherDataCopy, diagonalResult._data); return; } var denseOther = other.Storage as DenseColumnMajorMatrixStorage<Complex>; if (denseOther != null) { var dense = denseOther.Data; var diagonal = _data; var d = Math.Min(denseOther.RowCount, ColumnCount); if (d < ColumnCount) { result.ClearSubMatrix(denseOther.RowCount, ColumnCount - denseOther.RowCount, 0, denseOther.ColumnCount); } int index = 0; for (int i = 0; i < denseOther.ColumnCount; i++) { for (int j = 0; j < d; j++) { result.At(j, i, dense[index]*diagonal[j]); index++; } index += (denseOther.RowCount - d); } return; } base.DoTransposeThisAndMultiply(other, result); }
/// <summary> /// Negate each element of this matrix and place the results into the result matrix. /// </summary> /// <param name="result">The result of the negation.</param> protected override void DoNegate(Matrix<Complex> result) { var diagResult = result as DiagonalMatrix; if (diagResult != null) { Control.LinearAlgebraProvider.ScaleArray(-1, _data, diagResult._data); return; } result.Clear(); for (var i = 0; i < _data.Length; i++) { result.At(i, i, -_data[i]); } }
/// <summary> /// Adds another matrix to this matrix. /// </summary> /// <param name="other">The matrix to add to this matrix.</param> /// <param name="result">The matrix to store the result of the addition.</param> /// <exception cref="ArgumentNullException">If the other matrix is <see langword="null"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception> protected override void DoAdd(Matrix<float> other, Matrix<float> result) { var sparseOther = other as SparseMatrix; var sparseResult = result as SparseMatrix; if (sparseOther == null || sparseResult == null) { base.DoAdd(other, result); return; } if (ReferenceEquals(this, other)) { if (!ReferenceEquals(this, result)) { CopyTo(result); } Control.LinearAlgebraProvider.ScaleArray(2.0f, _storage.Values, _storage.Values); return; } SparseMatrix left; if (ReferenceEquals(sparseOther, sparseResult)) { left = this; } else if (ReferenceEquals(this, sparseResult)) { left = sparseOther; } else { CopyTo(sparseResult); left = sparseOther; } var leftStorage = left._storage; for (var i = 0; i < leftStorage.RowCount; i++) { var endIndex = leftStorage.RowPointers[i + 1]; for (var j = leftStorage.RowPointers[i]; j < endIndex; j++) { var columnIndex = leftStorage.ColumnIndices[j]; var resVal = leftStorage.Values[j] + result.At(i, columnIndex); result.At(i, columnIndex, resVal); } } }
/// <summary> /// Puts the upper triangle of this matrix into the result matrix. /// </summary> /// <param name="result">Where to store the lower triangle.</param> private void UpperTriangleImpl(Matrix<float> result) { var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var row = 0; row < result.RowCount; row++) { var endIndex = rowPointers[row + 1]; for (var j = rowPointers[row]; j < endIndex; j++) { if (row <= columnIndices[j]) { result.At(row, columnIndices[j], values[j]); } } } }
/// <summary> /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// </summary> /// <param name="other">The matrix to multiply with.</param> /// <param name="result">The result of the multiplication.</param> protected override void DoTransposeAndMultiply(Matrix<float> other, Matrix<float> result) { var otherSparse = other as SparseMatrix; var resultSparse = result as SparseMatrix; if (otherSparse == null || resultSparse == null) { base.DoTransposeAndMultiply(other, result); return; } resultSparse.Clear(); var rowPointers = _storage.RowPointers; var values = _storage.Values; var otherStorage = otherSparse._storage; for (var j = 0; j < RowCount; j++) { var startIndexOther = otherStorage.RowPointers[j]; var endIndexOther = otherStorage.RowPointers[j + 1]; if (startIndexOther == endIndexOther) { continue; } for (var i = 0; i < RowCount; i++) { // Multiply row of matrix A on row of matrix B var startIndexThis = rowPointers[i]; var endIndexThis = rowPointers[i + 1]; if (startIndexThis == endIndexThis) { continue; } var sum = 0f; for (var index = startIndexOther; index < endIndexOther; index++) { var ind = _storage.FindItem(i, otherStorage.ColumnIndices[index]); if (ind >= 0) { sum += otherStorage.Values[index]*values[ind]; } } resultSparse._storage.At(i, j, sum + result.At(i, j)); } } }
/// <summary> /// Subtracts another matrix from this matrix. /// </summary> /// <param name="other">The matrix to subtract to this matrix.</param> /// <param name="result">The matrix to store the result of subtraction.</param> /// <exception cref="ArgumentNullException">If the other matrix is <see langword="null"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception> protected override void DoSubtract(Matrix<float> other, Matrix<float> result) { var sparseOther = other as SparseMatrix; var sparseResult = result as SparseMatrix; if (sparseOther == null || sparseResult == null) { base.DoSubtract(other, result); return; } if (ReferenceEquals(this, other)) { result.Clear(); return; } var otherStorage = sparseOther._storage; if (ReferenceEquals(this, sparseResult)) { for (var i = 0; i < otherStorage.RowCount; i++) { var endIndex = otherStorage.RowPointers[i + 1]; for (var j = otherStorage.RowPointers[i]; j < endIndex; j++) { var columnIndex = otherStorage.ColumnIndices[j]; var resVal = sparseResult.At(i, columnIndex) - otherStorage.Values[j]; result.At(i, columnIndex, resVal); } } } else { if (!ReferenceEquals(sparseOther, sparseResult)) { sparseOther.CopyTo(sparseResult); } sparseResult.Negate(sparseResult); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var i = 0; i < RowCount; i++) { var endIndex = rowPointers[i + 1]; for (var j = rowPointers[i]; j < endIndex; j++) { var columnIndex = columnIndices[j]; var resVal = sparseResult.At(i, columnIndex) + values[j]; result.At(i, columnIndex, resVal); } } } }
/// <summary> /// Pointwise multiplies this matrix with another matrix and stores the result into the result matrix. /// </summary> /// <param name="other">The matrix to pointwise multiply with this one.</param> /// <param name="result">The matrix to store the result of the pointwise multiplication.</param> protected override void DoPointwiseMultiply(Matrix<float> other, Matrix<float> result) { result.Clear(); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var i = 0; i < RowCount; i++) { var endIndex = rowPointers[i + 1]; for (var j = rowPointers[i]; j < endIndex; j++) { var resVal = values[j]*other.At(i, columnIndices[j]); if (resVal != 0f) { result.At(i, columnIndices[j], resVal); } } } }
/// <summary> /// Pointwise divide this matrix by another matrix and stores the result into the result matrix. /// </summary> /// <param name="divisor">The matrix to pointwise divide this one by.</param> /// <param name="result">The matrix to store the result of the pointwise division.</param> protected override void DoPointwiseDivide(Matrix<float> divisor, Matrix<float> result) { result.Clear(); var rowPointers = _storage.RowPointers; var columnIndices = _storage.ColumnIndices; var values = _storage.Values; for (var i = 0; i < RowCount; i++) { var endIndex = rowPointers[i + 1]; for (var j = rowPointers[i]; j < endIndex; j++) { if (values[j] != 0f) { result.At(i, columnIndices[j], values[j]/divisor.At(i, columnIndices[j])); } } } }
/// <summary> /// Initializes a new instance of the <see cref="SparseMatrix"/> class, copying /// the values from the given matrix. /// </summary> /// <param name="matrix">The matrix to copy.</param> public SparseMatrix(Matrix<Complex> matrix) : this(matrix.RowCount, matrix.ColumnCount) { var sparseMatrix = matrix as SparseMatrix; var rows = matrix.RowCount; var columns = matrix.ColumnCount; if (sparseMatrix == null) { for (var i = 0; i < rows; i++) { for (var j = 0; j < columns; j++) { SetValueAt(i, j, matrix.At(i, j)); } } } else { NonZerosCount = sparseMatrix.NonZerosCount; _rowIndex = new int[rows]; _columnIndices = new int[NonZerosCount]; _nonZeroValues = new Complex[NonZerosCount]; Array.Copy(sparseMatrix._nonZeroValues, _nonZeroValues, NonZerosCount); Array.Copy(sparseMatrix._columnIndices, _columnIndices, NonZerosCount); Array.Copy(sparseMatrix._rowIndex, _rowIndex, rows); } }
/// <summary> /// Puts the upper triangle of this matrix into the result matrix. /// </summary> /// <param name="result">Where to store the lower triangle.</param> /// <exception cref="ArgumentException">If the result matrix's dimensions are not the same as this matrix.</exception> public override void UpperTriangle(Matrix<Complex> result) { if (result.RowCount != RowCount || result.ColumnCount != ColumnCount) { throw DimensionsDontMatch<ArgumentException>(this, result, "result"); } result.Clear(); for (var i = 0; i < _data.Length; i++) { result.At(i, i, _data[i]); } }
/// <summary> /// Adds another matrix to this matrix. /// </summary> /// <param name="other">The matrix to add to this matrix.</param> /// <param name="result">The matrix to store the result of the addition.</param> /// <exception cref="ArgumentNullException">If the other matrix is <see langword="null"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception> protected override void DoAdd(Matrix<Complex> other, Matrix<Complex> result) { var sparseOther = other as SparseMatrix; var sparseResult = result as SparseMatrix; if (sparseOther == null || sparseResult == null) { base.DoAdd(other, result); return; } if (ReferenceEquals(this, other)) { if (!ReferenceEquals(this, result)) { CopyTo(result); } Control.LinearAlgebraProvider.ScaleArray(2.0, _nonZeroValues, _nonZeroValues); return; } SparseMatrix left; if (ReferenceEquals(sparseOther, sparseResult)) { left = this; } else if (ReferenceEquals(this, sparseResult)) { left = sparseOther; } else { CopyTo(sparseResult); left = sparseOther; } for (var i = 0; i < left.RowCount; i++) { // Get the begin / end index for the current row var startIndex = left._rowIndex[i]; var endIndex = i < left._rowIndex.Length - 1 ? left._rowIndex[i + 1] : left.NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var columnIndex = left._columnIndices[j]; var resVal = left._nonZeroValues[j] + result.At(i, columnIndex); result.At(i, columnIndex, resVal); } } }
/// <summary> /// Subtracts another matrix from this matrix. /// </summary> /// <param name="other">The matrix to subtract.</param> /// <param name="result">The matrix to store the result of the subtraction.</param> /// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception> protected override void DoSubtract(Matrix<Complex> other, Matrix<Complex> result) { // diagonal - diagonal = diagonal var diagOther = other as DiagonalMatrix; var diagResult = result as DiagonalMatrix; if (diagOther != null && diagResult != null) { Control.LinearAlgebraProvider.SubtractArrays(_data, diagOther._data, diagResult._data); return; } other.Negate(result); for (int i = 0; i < _data.Length; i++) { result.At(i, i, result.At(i, i) + _data[i]); } }
/// <summary> /// Multiplies each element of the matrix by a scalar and places results into the result matrix. /// </summary> /// <param name="scalar">The scalar to multiply the matrix with.</param> /// <param name="result">The matrix to store the result of the multiplication.</param> protected override void DoMultiply(Complex scalar, Matrix<Complex> result) { if (scalar == 1.0) { CopyTo(result); return; } if (scalar == 0.0 || NonZerosCount == 0) { result.Clear(); return; } var sparseResult = result as SparseMatrix; if (sparseResult == null) { result.Clear(); for (var row = 0; row < RowCount; row++) { var start = _rowIndex[row]; var end = _rowIndex[row + 1]; if (start == end) { continue; } for (var index = start; index < end; index++) { var column = _columnIndices[index]; result.At(row, column, _nonZeroValues[index] * scalar); } } } else { if (!ReferenceEquals(this, result)) { CopyTo(sparseResult); } CommonParallel.For(0, NonZerosCount, index => sparseResult._nonZeroValues[index] *= scalar); } }
/// <summary> /// Solves a system of linear equations, <b>AX = B</b>, with A Cholesky factorized. /// </summary> /// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param> public override void Solve(Matrix<Complex32> input, Matrix<Complex32> result) { if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } // Check for proper dimensions. if (result.RowCount != input.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } if (result.ColumnCount != input.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } if (input.RowCount != CholeskyFactor.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixDimensions); } input.CopyTo(result); var order = CholeskyFactor.RowCount; for (var c = 0; c < result.ColumnCount; c++) { // Solve L*Y = B; Complex32 sum; for (var i = 0; i < order; i++) { sum = result.At(i, c); for (var k = i - 1; k >= 0; k--) { sum -= CholeskyFactor.At(i, k) * result.At(k, c); } result.At(i, c, sum / CholeskyFactor.At(i, i)); } // Solve L'*X = Y; for (var i = order - 1; i >= 0; i--) { sum = result.At(i, c); for (var k = i + 1; k < order; k++) { sum -= CholeskyFactor.At(k, i).Conjugate() * result.At(k, c); } result.At(i, c, sum / CholeskyFactor.At(i, i)); } } }
/// <summary> /// Multiplies this matrix with another matrix and places the results into the result matrix. /// </summary> /// <param name="other">The matrix to multiply with.</param> /// <param name="result">The result of the multiplication.</param> protected override void DoMultiply(Matrix<Complex> other, Matrix<Complex> result) { var columnVector = new DenseVector(other.RowCount); for (var row = 0; row < RowCount; row++) { // Get the begin / end index for the current row var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; if (startIndex == endIndex) { continue; } for (var column = 0; column < other.ColumnCount; column++) { // Multiply row of matrix A on column of matrix B other.Column(column, columnVector); var sum = Complex.Zero; for (var index = startIndex; index < endIndex; index++) { sum += _nonZeroValues[index] * columnVector[_columnIndices[index]]; } result.At(row, column, sum); } } }
/// <summary> /// Solves a system of linear equations, <b>AX = B</b>, with A QR factorized. /// </summary> /// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param> public override void Solve(Matrix<Complex32> input, Matrix<Complex32> result) { // Check for proper arguments. if (input == null) { throw new ArgumentNullException("input"); } if (result == null) { throw new ArgumentNullException("result"); } // The solution X should have the same number of columns as B if (input.ColumnCount != result.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows if (MatrixQ.RowCount != input.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } // The solution X row dimension is equal to the column dimension of A if (MatrixQ.ColumnCount != result.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } var inputCopy = input.Clone(); // Compute Y = transpose(Q)*B var column = new Complex32[MatrixQ.RowCount]; for (var j = 0; j < input.ColumnCount; j++) { for (var k = 0; k < MatrixQ.RowCount; k++) { column[k] = inputCopy.At(k, j); } for (var i = 0; i < MatrixQ.ColumnCount; i++) { var s = Complex32.Zero; for (var k = 0; k < MatrixQ.RowCount; k++) { s += MatrixQ.At(k, i).Conjugate() * column[k]; } inputCopy.At(i, j, s); } } // Solve R*X = Y; for (var k = MatrixQ.ColumnCount - 1; k >= 0; k--) { for (var j = 0; j < input.ColumnCount; j++) { inputCopy.At(k, j, inputCopy.At(k, j) / MatrixR.At(k, k)); } for (var i = 0; i < k; i++) { for (var j = 0; j < input.ColumnCount; j++) { inputCopy.At(i, j, inputCopy.At(i, j) - (inputCopy.At(k, j) * MatrixR.At(i, k))); } } } for (var i = 0; i < MatrixR.ColumnCount; i++) { for (var j = 0; j < input.ColumnCount; j++) { result.At(i, j, inputCopy.At(i, j)); } } }
/// <summary> /// Pointwise multiplies this matrix with another matrix and stores the result into the result matrix. /// </summary> /// <param name="other">The matrix to pointwise multiply with this one.</param> /// <param name="result">The matrix to store the result of the pointwise multiplication.</param> protected override void DoPointwiseMultiply(Matrix<Complex> other, Matrix<Complex> result) { result.Clear(); for (var i = 0; i < other.RowCount; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var resVal = _nonZeroValues[j] * other.At(i, _columnIndices[j]); if (resVal != 0.0) { result.At(i, _columnIndices[j], resVal); } } } }
/// <summary> /// Solves a system of linear equations, <b>AX = B</b>, with A SVD factorized. /// </summary> /// <param name="input">The right hand side <see cref="Matrix{T}"/>, <b>B</b>.</param> /// <param name="result">The left hand side <see cref="Matrix{T}"/>, <b>X</b>.</param> public override void Solve(Matrix<float> input, Matrix<float> result) { // The solution X should have the same number of columns as B if (input.ColumnCount != result.ColumnCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } // The dimension compatibility conditions for X = A\B require the two matrices A and B to have the same number of rows if (EigenValues.Count != input.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameRowDimension); } // The solution X row dimension is equal to the column dimension of A if (EigenValues.Count != result.RowCount) { throw new ArgumentException(Resources.ArgumentMatrixSameColumnDimension); } if (IsSymmetric) { var order = EigenValues.Count; var tmp = new float[order]; for (var k = 0; k < order; k++) { for (var j = 0; j < order; j++) { float value = 0; if (j < order) { for (var i = 0; i < order; i++) { value += EigenVectors.At(i, j)*input.At(i, k); } value /= (float) EigenValues[j].Real; } tmp[j] = value; } for (var j = 0; j < order; j++) { float value = 0; for (var i = 0; i < order; i++) { value += EigenVectors.At(j, i)*tmp[i]; } result.At(j, k, value); } } } else { throw new ArgumentException(Resources.ArgumentMatrixSymmetric); } }
/// <summary> /// Subtracts another matrix from this matrix. /// </summary> /// <param name="other">The matrix to subtract to this matrix.</param> /// <param name="result">The matrix to store the result of subtraction.</param> /// <exception cref="ArgumentNullException">If the other matrix is <see langword="null"/>.</exception> /// <exception cref="ArgumentOutOfRangeException">If the two matrices don't have the same dimensions.</exception> protected override void DoSubtract(Matrix<Complex> other, Matrix<Complex> result) { var sparseOther = other as SparseMatrix; var sparseResult = result as SparseMatrix; if (sparseOther == null || sparseResult == null) { base.DoSubtract(other, result); return; } if (ReferenceEquals(this, other)) { result.Clear(); return; } if (ReferenceEquals(this, sparseResult)) { for (var i = 0; i < sparseOther.RowCount; i++) { // Get the begin / end index for the current row var startIndex = sparseOther._rowIndex[i]; var endIndex = i < sparseOther._rowIndex.Length - 1 ? sparseOther._rowIndex[i + 1] : sparseOther.NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var columnIndex = sparseOther._columnIndices[j]; var resVal = sparseResult.At(i, columnIndex) - sparseOther._nonZeroValues[j]; result.At(i, columnIndex, resVal); } } } else { if (!ReferenceEquals(sparseOther, sparseResult)) { sparseOther.CopyTo(sparseResult); } sparseResult.Negate(sparseResult); for (var i = 0; i < RowCount; i++) { // Get the begin / end index for the current row var startIndex = _rowIndex[i]; var endIndex = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { var columnIndex = _columnIndices[j]; var resVal = sparseResult.At(i, columnIndex) + _nonZeroValues[j]; result.At(i, columnIndex, resVal); } } } }
/// <summary> /// Nonsymmetric reduction to Hessenberg form. /// </summary> /// <param name="eigenVectors">The eigen vectors to work on.</param> /// <param name="matrixH">Array for internal storage of nonsymmetric Hessenberg form.</param> /// <param name="order">Order of initial matrix</param> /// <remarks>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.</remarks> static void NonsymmetricReduceToHessenberg(Matrix<float> eigenVectors, float[,] matrixH, int order) { var ort = new float[order]; for (var m = 1; m < order - 1; m++) { // Scale column. var scale = 0.0f; for (var i = m; i < order; i++) { scale = scale + Math.Abs(matrixH[i, m - 1]); } if (scale != 0.0f) { // Compute Householder transformation. var h = 0.0f; for (var i = order - 1; i >= m; i--) { ort[i] = matrixH[i, m - 1]/scale; h += ort[i]*ort[i]; } var g = (float) Math.Sqrt(h); if (ort[m] > 0) { g = -g; } h = h - (ort[m]*g); ort[m] = ort[m] - g; // Apply Householder similarity transformation // H = (I-u*u'/h)*H*(I-u*u')/h) for (var j = m; j < order; j++) { var f = 0.0f; for (var i = order - 1; i >= m; i--) { f += ort[i]*matrixH[i, j]; } f = f/h; for (var i = m; i < order; i++) { matrixH[i, j] -= f*ort[i]; } } for (var i = 0; i < order; i++) { var f = 0.0f; for (var j = order - 1; j >= m; j--) { f += ort[j]*matrixH[i, j]; } f = f/h; for (var j = m; j < order; j++) { matrixH[i, j] -= f*ort[j]; } } ort[m] = scale*ort[m]; matrixH[m, m - 1] = scale*g; } } // Accumulate transformations (Algol's ortran). for (var i = 0; i < order; i++) { for (var j = 0; j < order; j++) { eigenVectors.At(i, j, i == j ? 1.0f : 0.0f); } } for (var m = order - 2; m >= 1; m--) { if (matrixH[m, m - 1] != 0.0f) { for (var i = m + 1; i < order; i++) { ort[i] = matrixH[i, m - 1]; } for (var j = m; j < order; j++) { var g = 0.0f; for (var i = m; i < order; i++) { g += ort[i]*eigenVectors.At(i, j); } // Double division avoids possible underflow g = (g/ort[m])/matrixH[m, m - 1]; for (var i = m; i < order; i++) { eigenVectors.At(i, j, eigenVectors.At(i, j) + g*ort[i]); } } } } }
/// <summary> /// Multiplies this matrix with transpose of another matrix and places the results into the result matrix. /// </summary> /// <param name="other">The matrix to multiply with.</param> /// <param name="result">The result of the multiplication.</param> protected override void DoTransposeAndMultiply(Matrix<Complex> other, Matrix<Complex> result) { var otherSparse = other as SparseMatrix; var resultSparse = result as SparseMatrix; if (otherSparse == null || resultSparse == null) { base.DoTransposeAndMultiply(other, result); return; } resultSparse.Clear(); for (var j = 0; j < RowCount; j++) { // Get the begin / end index for the row var startIndexOther = otherSparse._rowIndex[j]; var endIndexOther = j < otherSparse._rowIndex.Length - 1 ? otherSparse._rowIndex[j + 1] : otherSparse.NonZerosCount; if (startIndexOther == endIndexOther) { continue; } for (var i = 0; i < RowCount; i++) { // Multiply row of matrix A on row of matrix B // Get the begin / end index for the row var startIndexThis = _rowIndex[i]; var endIndexThis = i < _rowIndex.Length - 1 ? _rowIndex[i + 1] : NonZerosCount; if (startIndexThis == endIndexThis) { continue; } var sum = Complex.Zero; for (var index = startIndexOther; index < endIndexOther; index++) { var ind = FindItem(i, otherSparse._columnIndices[index]); if (ind >= 0) { sum += otherSparse._nonZeroValues[index] * _nonZeroValues[ind]; } } resultSparse.SetValueAt(i, j, sum + result.At(i, j)); } } }
/// <summary> /// Symmetric tridiagonal QL algorithm. /// </summary> /// <param name="eigenVectors">The eigen vectors to work on.</param> /// <param name="d">Arrays for internal storage of real parts of eigenvalues</param> /// <param name="e">Arrays for internal storage of imaginary parts of eigenvalues</param> /// <param name="order">Order of initial matrix</param> /// <remarks>This is derived from the Algol procedures tql2, by /// Bowdler, Martin, Reinsch, and Wilkinson, Handbook for /// Auto. Comp., Vol.ii-Linear Algebra, and the corresponding /// Fortran subroutine in EISPACK.</remarks> /// <exception cref="NonConvergenceException"></exception> static void SymmetricDiagonalize(Matrix<float> eigenVectors, float[] d, float[] e, int order) { const int maxiter = 1000; for (var i = 1; i < order; i++) { e[i - 1] = e[i]; } e[order - 1] = 0.0f; var f = 0.0f; var tst1 = 0.0f; var eps = Precision.DoublePrecision; for (var l = 0; l < order; l++) { // Find small subdiagonal element tst1 = Math.Max(tst1, Math.Abs(d[l]) + Math.Abs(e[l])); var m = l; while (m < order) { if (Math.Abs(e[m]) <= eps*tst1) { break; } m++; } // If m == l, d[l] is an eigenvalue, // otherwise, iterate. if (m > l) { var iter = 0; do { iter = iter + 1; // (Could check iteration count here.) // Compute implicit shift var g = d[l]; var p = (d[l + 1] - g)/(2.0f*e[l]); var r = SpecialFunctions.Hypotenuse(p, 1.0f); if (p < 0) { r = -r; } d[l] = e[l]/(p + r); d[l + 1] = e[l]*(p + r); var dl1 = d[l + 1]; var h = g - d[l]; for (var i = l + 2; i < order; i++) { d[i] -= h; } f = f + h; // Implicit QL transformation. p = d[m]; var c = 1.0f; var c2 = c; var c3 = c; var el1 = e[l + 1]; var s = 0.0f; var s2 = 0.0f; for (var i = m - 1; i >= l; i--) { c3 = c2; c2 = c; s2 = s; g = c*e[i]; h = c*p; r = SpecialFunctions.Hypotenuse(p, e[i]); e[i + 1] = s*r; s = e[i]/r; c = p/r; p = (c*d[i]) - (s*g); d[i + 1] = h + (s*((c*g) + (s*d[i]))); // Accumulate transformation. for (var k = 0; k < order; k++) { h = eigenVectors.At(k, i + 1); eigenVectors.At(k, i + 1, (s*eigenVectors.At(k, i)) + (c*h)); eigenVectors.At(k, i, (c*eigenVectors.At(k, i)) - (s*h)); } } p = (-s)*s2*c3*el1*e[l]/dl1; e[l] = s*p; d[l] = c*p; // Check for convergence. If too many iterations have been performed, // throw exception that Convergence Failed if (iter >= maxiter) { throw new NonConvergenceException(); } } while (Math.Abs(e[l]) > eps*tst1); } d[l] = d[l] + f; e[l] = 0.0f; } // Sort eigenvalues and corresponding vectors. for (var i = 0; i < order - 1; i++) { var k = i; var p = d[i]; for (var j = i + 1; j < order; j++) { if (d[j] < p) { k = j; p = d[j]; } } if (k != i) { d[k] = d[i]; d[i] = p; for (var j = 0; j < order; j++) { p = eigenVectors.At(j, i); eigenVectors.At(j, i, eigenVectors.At(j, k)); eigenVectors.At(j, k, p); } } } }
/// <summary> /// Puts the upper triangle of this matrix into the result matrix. /// </summary> /// <param name="result">Where to store the lower triangle.</param> private void UpperTriangleImpl(Matrix<Complex> result) { for (var row = 0; row < result.RowCount; row++) { var startIndex = _rowIndex[row]; var endIndex = row < _rowIndex.Length - 1 ? _rowIndex[row + 1] : NonZerosCount; for (var j = startIndex; j < endIndex; j++) { if (row <= _columnIndices[j]) { result.At(row, _columnIndices[j], _nonZeroValues[j]); } } } }