/// <summary> /// Gets or sets an entry of the matrix. /// </summary> /// <param name="r">The (zero-based) row number.</param> /// <param name="c">The (zero-based) column number.</param> /// <returns>The value of the specified matrix entry M<sub>r c</sub>.</returns> public override double this[int r, int c] { get { if ((r < 0) || (r >= dimension)) { throw new ArgumentOutOfRangeException("r"); } if ((c < 0) || (c >= dimension)) { throw new ArgumentOutOfRangeException("c"); } return(MatrixAlgorithms.GetEntry(store, dimension, dimension, r, c)); } set { if ((r < 0) || (r >= dimension)) { throw new ArgumentOutOfRangeException("r"); } if ((c < 0) || (c >= dimension)) { throw new ArgumentOutOfRangeException("c"); } if (IsReadOnly) { throw new InvalidOperationException(); } MatrixAlgorithms.SetEntry(store, dimension, dimension, r, c, value); } }
/// <summary> /// Computes the eigenvalues and eigenvectors of the matrix. /// </summary> /// <returns>A representation of the eigenvalues and eigenvectors of the matrix.</returns> /// <remarks> /// <para>For a generic vector v and matrix M, Mv = u will point in some direction with no particular relationship to v. /// The eigenvectors of a matrix M are vectors z that satisfy Mz = λz, i.e. multiplying an eigenvector by a /// matrix reproduces the same vector, up to a prortionality constant λ called the eigenvalue.</para> /// <para>For v to be an eigenvector of M with eigenvalue λ, (M - λI)z = 0. But for a matrix to /// anihilate any non-zero vector, that matrix must have determinant, so det(M - λI)=0. For a matrix of /// order N, this is an equation for the roots of a polynomial of order N. Since an order-N polynomial always has exactly /// N roots, an order-N matrix always has exactly N eigenvalues.</para> /// <para>Since a polynomial with real coefficients can still have complex roots, a real square matrix can nonetheless /// have complex eigenvalues (and correspondly complex eigenvectors). However, again like the complex roots of a real /// polynomial, such eigenvalues will always occurs in complex-conjugate pairs.</para> /// <para>Although the eigenvalue polynomial ensures that an order-N matrix has N eigenvalues, it can occur that there /// are not N corresponding independent eigenvectors. A matrix with fewer eigenvectors than eigenvalues is called /// defective. Like singularity, defectiveness represents a delecate balance between the elements of a matrix that can /// typically be disturbed by just an infinitesimal perturbation of elements. Because of round-off-error, then, floating-point /// algorithms cannot reliably identify defective matrices. Instead, this method will return a full set of eigenvectors, /// but some eigenvectors, corresponding to very nearly equal eigenvalues, will be very nearly parallel.</para> /// <para>While a generic square matrix can be defective, many subspecies of square matrices are guaranteed not to be. /// This includes Markov matrices, orthogonal matrices, and symmetric matrices.</para> /// <para>Determining the eigenvalues and eigenvectors of a matrix is an O(N<sup>3</sup>) operation. If you need only the /// eigenvalues of a matrix, the <see cref="Eigenvalues"/> method is more efficient.</para> /// </remarks> public ComplexEigensystem Eigensystem() { double[] aStore = MatrixAlgorithms.Copy(store, dimension, dimension); int[] perm = new int[dimension]; for (int i = 0; i < perm.Length; i++) { perm[i] = i; } SquareMatrixAlgorithms.IsolateCheapEigenvalues(aStore, perm, dimension); double[] qStore = MatrixAlgorithms.AllocateStorage(dimension, dimension); for (int i = 0; i < perm.Length; i++) { MatrixAlgorithms.SetEntry(qStore, dimension, dimension, perm[i], i, 1.0); } //double[] qStore = SquareMatrixAlgorithms.CreateUnitMatrix(dimension); // Reduce the original matrix to Hessenberg form SquareMatrixAlgorithms.ReduceToHessenberg(aStore, qStore, dimension); // Reduce the Hessenberg matrix to real Schur form SquareMatrixAlgorithms.ReduceToRealSchurForm(aStore, qStore, dimension); //MatrixAlgorithms.PrintMatrix(aStore, dimension, dimension); //SquareMatrix A = new SquareMatrix(aStore, dimension); SquareMatrix Q = new SquareMatrix(qStore, dimension); Complex[] eigenvalues; Complex[][] eigenvectors; // Extract the eigenvalues and eigenvectors of the Schur form matrix SquareMatrixAlgorithms.SchurEigensystem(aStore, dimension, out eigenvalues, out eigenvectors); // transform eigenvectors of schur form into eigenvectors of original matrix // while we are at it, normalize so largest component has value 1 for (int i = 0; i < dimension; i++) { Complex[] v = new Complex[dimension]; double norm = 0.0; for (int j = 0; j < dimension; j++) { for (int k = 0; k < dimension; k++) { v[j] += Q[j, k] * eigenvectors[i][k]; } norm = Math.Max(norm, Math.Max(Math.Abs(v[j].Re), Math.Abs(v[j].Im))); } for (int j = 0; j < dimension; j++) { v[j] = v[j] / norm; } eigenvectors[i] = v; } ComplexEigensystem eigensystem = new ComplexEigensystem(dimension, eigenvalues, eigenvectors); return(eigensystem); }
public static Complex[] ExtractEigenvalues(double[] aStore, double[] qStore, int dimension) { int count = 0; // keep track of extracted eigenvalues Complex[] lambdas = new Complex[dimension]; double sum_old = Double.PositiveInfinity; int n = dimension - 1; while (n >= 0) { //Write(aStore, dimension, dimension); // find the lowest decoupled, unreduced block int a = n; double sum = 0.0; while (a > 0) { double f = Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, a, a)) + Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, a - 1, a - 1)); double e = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, a, a - 1); if ((f + e) == f) { MatrixAlgorithms.SetEntry(aStore, dimension, dimension, a, a - 1, 0.0); break; } sum += Math.Abs(e); a--; } /* * Console.WriteLine("count = {0}", count); * double[] qt = MatrixAlgorithms.Transpose(qStore, dimension, dimension); * double[] qa = MatrixAlgorithms.Multiply(qStore, dimension, dimension, aStore, dimension, dimension); * double[] qaqt = MatrixAlgorithms.Multiply(qa, dimension, dimension, qt, dimension, dimension); * MatrixAlgorithms.PrintMatrix(qaqt, dimension, dimension); */ // check for maximum numbers of iterations without finding an eigenvalue if (count > 32) { throw new NonconvergenceException(); } // we are working between a and n, and our block is at least 3 wide // reduce if possible, otherwise do a iteration step if (a == n) { // 1 X 1 block isolated lambdas[a] = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, a, a); n -= 1; count = 0; sum_old = Double.PositiveInfinity; } else if (a == (n - 1)) { // 2 X 2 block isolated double Aaa = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, a, a); double Aba = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, a + 1, a); double Aab = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, a, a + 1); double Abb = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, a + 1, a + 1); // eigenvalues are given by the quadratic formula // e = \frac{a_11 + a22}{2} \pm q // descriminant q^2 = ( \frac{a_11 - a_22}{2} )^2 + a_21 a_1 double c = (Aaa + Abb) / 2.0; double d = Aaa * Abb - Aba * Aab; double q2 = c * c - d; if (q2 >= 0.0) { // eigenvalues are real double q = Math.Sqrt(q2); if (c >= 0.0) { lambdas[a] = c + q; } else { lambdas[a] = c - q; } // i tried to do this as c + Math.Sign(c) * q, but when c = 0, Math.Sign(c) = 0, not 1 lambdas[a + 1] = d / lambdas[a]; /* * double sn, cn; * TwoByTwoSchur(ref Aaa, ref Aab, ref Aba, ref Abb, out sn, out cn); * MatrixAlgorithms.SetEntry(aStore, dimension, dimension, a, a, Aaa); * MatrixAlgorithms.SetEntry(aStore, dimension, dimension, a, a + 1, Aab); * MatrixAlgorithms.SetEntry(aStore, dimension, dimension, a + 1, a, Aba); * MatrixAlgorithms.SetEntry(aStore, dimension, dimension, a + 1, a + 1, Abb); * * // Multiply A from left by the rotation matrix R * for (int cc = a + 2; cc < dimension; cc++) { * int i = MatrixAlgorithms.GetIndex(dimension, dimension, a, cc); * int j = MatrixAlgorithms.GetIndex(dimension, dimension, a + 1, cc); * double t = aStore[i]; * aStore[i] = cn * t + sn * aStore[j]; * aStore[j] = cn * aStore[j] - sn * t; * } * // Multiply A from the right by R^T * for (int rr = 0; rr < a; rr++) { * int i = MatrixAlgorithms.GetIndex(dimension, dimension, rr, a); * int j = MatrixAlgorithms.GetIndex(dimension, dimension, rr, a + 1); * double t = aStore[i]; * aStore[i] = cn * t + sn * aStore[j]; * aStore[j] = cn * aStore[j] - sn * t; * } * // Multiply Q^T from the left by R * if (qStore != null) { * for (int cc = 0; cc < dimension; cc++) { * int i = MatrixAlgorithms.GetIndex(dimension, dimension, a, cc); * int j = MatrixAlgorithms.GetIndex(dimension, dimension, a + 1, cc); * double t = qStore[i]; * qStore[i] = cn * t + sn * qStore[j]; * qStore[j] = cn * qStore[j] - sn * t; * } * } * * lambdas[a] = Aaa; * lambdas[a + 1] = Abb; */ } else { double q = Math.Sqrt(-q2); lambdas[a] = new Complex(c, q); lambdas[a + 1] = new Complex(c, -q); } n -= 2; count = 0; sum_old = Double.PositiveInfinity; } else { // use the lower-left 2 X 2 matrix to generate an approximate eigenvalue pair int m = n - 1; double Amm = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, m, m); double Amn = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, m, n); double Anm = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, n, m); double Ann = MatrixAlgorithms.GetEntry(aStore, dimension, dimension, n, n); // the trace of this 2 X 2 matrix is the sum of its eigenvalues, and its determinate is their product double tr = Amm + Ann; double det = Amm * Ann - Amn * Anm; // ad hoc shift if ((count == 8) || (count == 16) || (count == 24)) { double w = Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, n, n - 1)) + Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, m, m - 1)); tr = 2.0 * w; det = w * w; } FrancisTwoStep(aStore, qStore, dimension, a, n, tr, det); sum_old = sum; count++; } } return(lambdas); }
// inverts the matrix in place // the in-place-ness makes this a bit confusing public static void GaussJordanInvert(double[] store, int dimension) { // keep track of row exchanges int[] ps = new int[dimension]; // iterate over dimensions for (int k = 0; k < dimension; k++) { // look for a pivot in the kth column on any lower row int p = k; double q = MatrixAlgorithms.GetEntry(store, dimension, dimension, k, k); for (int r = k + 1; r < dimension; r++) { double s = MatrixAlgorithms.GetEntry(store, dimension, dimension, r, k); if (Math.Abs(s) > Math.Abs(q)) { p = r; q = s; } } ps[k] = p; // if no non-zero pivot is found, the matrix is singular and cannot be inverted if (q == 0.0) { throw new DivideByZeroException(); } // if the best pivot was on a lower row, swap it into the kth row if (p != k) { Blas1.dSwap(store, k, dimension, store, p, dimension, dimension); } // divide the pivot row by the pivot element, so the diagonal element becomes unity MatrixAlgorithms.SetEntry(store, dimension, dimension, k, k, 1.0); Blas1.dScal(1.0 / q, store, k, dimension, dimension); // add factors to the pivot row to zero all off-diagonal elements in the kth column for (int r = 0; r < dimension; r++) { if (r == k) { continue; } double a = MatrixAlgorithms.GetEntry(store, dimension, dimension, r, k); MatrixAlgorithms.SetEntry(store, dimension, dimension, r, k, 0.0); Blas1.dAxpy(-a, store, k, dimension, store, r, dimension, dimension); } } // unscramble exchanges for (int k = dimension - 1; k >= 0; k--) { int p = ps[k]; if (p != k) { Blas1.dSwap(store, dimension * p, 1, store, dimension * k, 1, dimension); } } }
// EIGENVALUE ALGORITHMS // public static Complex[] ReduceToRealSchurForm(double[] aStore, double[] qStore, int dimension) { // keep track of eigenvalues Complex[] eigenvalues = new Complex[dimension]; // keep track of interations int count = 0; // isolate the upper and lower boundaries of the curent subproblem // p is the upper index, n the lower index int n = dimension - 1; while (n >= 0) { // move up the matrix from endpoint n, looking for subdiagonal elements negligible compared to the neighboring diagonal elements // if we find one, that reduces the problem to the submatrix between p and n int p = n; while (p > 0) { double d = Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, p, p)) + Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, p - 1, p - 1)); double e = Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, p, p - 1)); if (d + e == d) { // double f = d * Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, p, p) - MatrixAlgorithms.GetEntry(aStore, dimension, dimension, p - 1, p - 1)); // double g = e * Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, p - 1, p)); // if (f + g == f) { MatrixAlgorithms.SetEntry(aStore, dimension, dimension, p, p - 1, 0.0); break; // } } p--; } /* * if (p == n) { * // one eigenvalue * // reduce n and re-set count * } else { * // compute m, matrix elements * // reduce n and re-set count * if (p == m) { * // two eigenvalues * } else { * // do step * } * } */ //Print(a, dimension, p, n); // get the (indexes for) the entries in the trailing 2x2 matrix int m = n - 1; int ammi = MatrixAlgorithms.GetIndex(dimension, dimension, m, m); int amni = MatrixAlgorithms.GetIndex(dimension, dimension, m, n); int anmi = MatrixAlgorithms.GetIndex(dimension, dimension, n, m); int anni = MatrixAlgorithms.GetIndex(dimension, dimension, n, n); if (n - p > 1) { count++; if (count > 32) { throw new NonconvergenceException(); } double tr = aStore[ammi] + aStore[anni]; double det = aStore[ammi] * aStore[anni] - aStore[amni] * aStore[anmi]; // ad hoc shift if ((count == 8) || (count == 16) || (count == 24)) { double w = Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, n, n - 1)) + Math.Abs(MatrixAlgorithms.GetEntry(aStore, dimension, dimension, n - 1, n - 2)); tr = 2.0 * w; det = w * w; } FrancisTwoStep(aStore, qStore, dimension, p, n, tr, det); } else { if (p == n) { eigenvalues[n] = aStore[anni]; } else if (p == m) { double sn, cn; TwoByTwoRealSchur(ref aStore[ammi], ref aStore[amni], ref aStore[anmi], ref aStore[anni], out sn, out cn, out eigenvalues[m], out eigenvalues[n]); // Multiply A from left by the rotation matrix R for (int cc = p + 2; cc < dimension; cc++) { int i = MatrixAlgorithms.GetIndex(dimension, dimension, p, cc); int j = MatrixAlgorithms.GetIndex(dimension, dimension, p + 1, cc); double t = aStore[i]; aStore[i] = cn * t + sn * aStore[j]; aStore[j] = cn * aStore[j] - sn * t; } // Multiply A from the right by R^T for (int rr = 0; rr < p; rr++) { int i = MatrixAlgorithms.GetIndex(dimension, dimension, rr, p); int j = MatrixAlgorithms.GetIndex(dimension, dimension, rr, p + 1); double t = aStore[i]; aStore[i] = cn * t + sn * aStore[j]; aStore[j] = cn * aStore[j] - sn * t; } // Multiply Q^T from the left by R if (qStore != null) { for (int rr = 0; rr < dimension; rr++) { int i = MatrixAlgorithms.GetIndex(dimension, dimension, rr, p); int j = MatrixAlgorithms.GetIndex(dimension, dimension, rr, p + 1); double t = qStore[i]; qStore[i] = cn * t + sn * qStore[j]; qStore[j] = cn * qStore[j] - sn * t; } } } n = p - 1; count = 0; } } return(eigenvalues); }