// Take a matrix and make all the eigenvalues non-negative private static Matrix projectToPositiveSemidefiniteMatrix(Matrix M) { int size = M.rows(); Utils.QL_REQUIRE(size == M.columns(), () => "matrix not square"); Matrix diagonal = new Matrix(size, size); SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(M); for (int i = 0; i < size; ++i) { diagonal[i, i] = Math.Max(jd.eigenvalues()[i], 0.0); } Matrix result = jd.eigenvectors() * diagonal * Matrix.transpose(jd.eigenvectors()); return(result); }
public void testEigenvectors() { //("Testing eigenvalues and eigenvectors calculation..."); setup(); Matrix[] testMatrices = { M1, M2 }; for (int k=0; k<testMatrices.Length; k++) { Matrix M = testMatrices[k]; SymmetricSchurDecomposition dec = new SymmetricSchurDecomposition(M); Vector eigenValues = dec.eigenvalues(); Matrix eigenVectors = dec.eigenvectors(); double minHolder = double.MaxValue; for (int i=0; i<N; i++) { Vector v = new Vector(N); for (int j = 0; j < N; j++) v[j] = eigenVectors[j,i]; // check definition Vector a = M * v; Vector b = eigenValues[i] * v; if (norm(a-b) > 1.0e-15) Assert.Fail("Eigenvector definition not satisfied"); // check decreasing ordering if (eigenValues[i] >= minHolder) { Assert.Fail("Eigenvalues not ordered: " + eigenValues); } else minHolder = eigenValues[i]; } // check normalization Matrix m = eigenVectors * Matrix.transpose(eigenVectors); if (norm(m-I) > 1.0e-15) Assert.Fail("Eigenvector not normalized"); } }
public static Matrix rankReducedSqrt(Matrix matrix, int maxRank, double componentRetainedPercentage, SalvagingAlgorithm sa) { int size = matrix.rows(); #if QL_EXTRA_SAFETY_CHECKS checkSymmetry(matrix); #else if (size != matrix.columns()) { throw new Exception("non square matrix: " + size + " rows, " + matrix.columns() + " columns"); } #endif if (!(componentRetainedPercentage > 0.0)) { throw new Exception("no eigenvalues retained"); } if (!(componentRetainedPercentage <= 1.0)) { throw new Exception("percentage to be retained > 100%"); } if (!(maxRank >= 1)) { throw new Exception("max rank required < 1"); } // spectral (a.k.a Principal Component) analysis SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(matrix); Vector eigenValues = jd.eigenvalues(); // salvaging algorithm switch (sa) { case SalvagingAlgorithm.None: // eigenvalues are sorted in decreasing order if (!(eigenValues[size - 1] >= -1e-16)) { throw new Exception("negative eigenvalue(s) (" + eigenValues[size - 1] + ")"); } break; case SalvagingAlgorithm.Spectral: // negative eigenvalues set to zero for (int i = 0; i < size; ++i) { eigenValues[i] = Math.Max(eigenValues[i], 0.0); } break; case SalvagingAlgorithm.Higham: { int maxIterations = 40; double tolerance = 1e-6; Matrix adjustedMatrix = highamImplementation(matrix, maxIterations, tolerance); jd = new SymmetricSchurDecomposition(adjustedMatrix); eigenValues = jd.eigenvalues(); } break; default: throw new Exception("unknown or invalid salvaging algorithm"); } // factor reduction /*std::accumulate(eigenValues.begin(), * eigenValues.end(), 0.0);*/ double accumulate = 0; eigenValues.ForEach((ii, vv) => accumulate += eigenValues[ii]); double enough = componentRetainedPercentage * accumulate; if (componentRetainedPercentage == 1.0) { // numerical glitches might cause some factors to be discarded enough *= 1.1; } // retain at least one factor double components = eigenValues[0]; int retainedFactors = 1; for (int i = 1; components < enough && i < size; ++i) { components += eigenValues[i]; retainedFactors++; } // output is granted to have a rank<=maxRank retainedFactors = Math.Min(retainedFactors, maxRank); Matrix diagonal = new Matrix(size, retainedFactors, 0.0); for (int i = 0; i < retainedFactors; ++i) { diagonal[i, i] = Math.Sqrt(eigenValues[i]); } Matrix result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); return(result); }
//! Returns the pseudo square root of a real symmetric matrix /*! Given a matrix \f$ M \f$, the result \f$ S \f$ is defined * as the matrix such that \f$ S S^T = M. \f$ * If the matrix is not positive semi definite, it can * return an approximation of the pseudo square root * using a (user selected) salvaging algorithm. * * For more information see: "The most general methodology to create * a valid correlation matrix for risk management and option pricing * purposes", by R. Rebonato and P. Jдckel. * The Journal of Risk, 2(2), Winter 1999/2000 * http://www.rebonato.com/correlationmatrix.pdf * * Revised and extended in "Monte Carlo Methods in Finance", * by Peter Jдckel, Chapter 6. * * \pre the given matrix must be symmetric. * * \relates Matrix * * \warning Higham algorithm only works for correlation matrices. * * \test * - the correctness of the results is tested by reproducing * known good data. * - the correctness of the results is tested by checking * returned values against numerical calculations. */ public static Matrix pseudoSqrt(Matrix matrix, SalvagingAlgorithm sa) { int size = matrix.rows(); #if QL_EXTRA_SAFETY_CHECKS checkSymmetry(matrix); #else if (size != matrix.columns()) { throw new Exception("non square matrix: " + size + " rows, " + matrix.columns() + " columns"); } #endif // spectral (a.k.a Principal Component) analysis SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(matrix); Matrix diagonal = new Matrix(size, size, 0.0); // salvaging algorithm Matrix result = new Matrix(size, size); bool negative; switch (sa) { case SalvagingAlgorithm.None: // eigenvalues are sorted in decreasing order if (!(jd.eigenvalues()[size - 1] >= -1e-16)) { throw new Exception("negative eigenvalue(s) (" + jd.eigenvalues()[size - 1] + ")"); } result = MatrixUtilities.CholeskyDecomposition(matrix, true); break; case SalvagingAlgorithm.Spectral: // negative eigenvalues set to zero for (int i = 0; i < size; i++) { diagonal[i, i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); } result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); break; case SalvagingAlgorithm.Hypersphere: // negative eigenvalues set to zero negative = false; for (int i = 0; i < size; ++i) { diagonal[i, i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); if (jd.eigenvalues()[i] < 0.0) { negative = true; } } result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); if (negative) { result = hypersphereOptimize(matrix, result, false); } break; case SalvagingAlgorithm.LowerDiagonal: // negative eigenvalues set to zero negative = false; for (int i = 0; i < size; ++i) { diagonal[i, i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); if (jd.eigenvalues()[i] < 0.0) { negative = true; } } result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); if (negative) { result = hypersphereOptimize(matrix, result, true); } break; case SalvagingAlgorithm.Higham: int maxIterations = 40; double tol = 1e-6; result = highamImplementation(matrix, maxIterations, tol); result = MatrixUtilities.CholeskyDecomposition(result, true); break; default: throw new Exception("unknown salvaging algorithm"); } return(result); }
//! Returns the pseudo square root of a real symmetric matrix /*! Given a matrix \f$ M \f$, the result \f$ S \f$ is defined as the matrix such that \f$ S S^T = M. \f$ If the matrix is not positive semi definite, it can return an approximation of the pseudo square root using a (user selected) salvaging algorithm. For more information see: "The most general methodology to create a valid correlation matrix for risk management and option pricing purposes", by R. Rebonato and P. Jдckel. The Journal of Risk, 2(2), Winter 1999/2000 http://www.rebonato.com/correlationmatrix.pdf Revised and extended in "Monte Carlo Methods in Finance", by Peter Jдckel, Chapter 6. \pre the given matrix must be symmetric. \relates Matrix \warning Higham algorithm only works for correlation matrices. \test - the correctness of the results is tested by reproducing known good data. - the correctness of the results is tested by checking returned values against numerical calculations. */ public static Matrix pseudoSqrt(Matrix matrix, SalvagingAlgorithm sa) { int size = matrix.rows(); #if QL_EXTRA_SAFETY_CHECKS checkSymmetry(matrix); #else if (size != matrix.columns()) throw new ApplicationException("non square matrix: " + size + " rows, " + matrix.columns() + " columns"); #endif // spectral (a.k.a Principal Component) analysis SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(matrix); Matrix diagonal = new Matrix(size, size, 0.0); // salvaging algorithm Matrix result = new Matrix(size, size); bool negative; switch (sa) { case SalvagingAlgorithm.None: // eigenvalues are sorted in decreasing order if (!(jd.eigenvalues()[size-1]>=-1e-16)) throw new ApplicationException("negative eigenvalue(s) (" + jd.eigenvalues()[size-1] + ")"); result = MatrixUtilities.CholeskyDecomposition(matrix, true); break; case SalvagingAlgorithm.Spectral: // negative eigenvalues set to zero for (int i=0; i<size; i++) diagonal[i,i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); break; case SalvagingAlgorithm.Hypersphere: // negative eigenvalues set to zero negative=false; for (int i=0; i<size; ++i){ diagonal[i,i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); if (jd.eigenvalues()[i]<0.0) negative=true; } result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); if (negative) result = hypersphereOptimize(matrix, result, false); break; case SalvagingAlgorithm.LowerDiagonal: // negative eigenvalues set to zero negative=false; for (int i=0; i<size; ++i){ diagonal[i,i] = Math.Sqrt(Math.Max(jd.eigenvalues()[i], 0.0)); if (jd.eigenvalues()[i]<0.0) negative=true; } result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); if (negative) result = hypersphereOptimize(matrix, result, true); break; case SalvagingAlgorithm.Higham: int maxIterations = 40; double tol = 1e-6; result = highamImplementation(matrix, maxIterations, tol); result = MatrixUtilities.CholeskyDecomposition(result, true); break; default: throw new ApplicationException("unknown salvaging algorithm"); } return result; }
// Take a matrix and make all the eigenvalues non-negative private static Matrix projectToPositiveSemidefiniteMatrix(Matrix M) { int size = M.rows(); if (size != M.columns()) throw new ApplicationException("matrix not square"); Matrix diagonal = new Matrix(size, size); SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(M); for (int i=0; i<size; ++i) diagonal[i,i] = Math.Max(jd.eigenvalues()[i], 0.0); Matrix result = jd.eigenvectors()*diagonal*Matrix.transpose(jd.eigenvectors()); return result; }
public static Matrix rankReducedSqrt(Matrix matrix, int maxRank, double componentRetainedPercentage, SalvagingAlgorithm sa) { int size = matrix.rows(); #if QL_EXTRA_SAFETY_CHECKS checkSymmetry(matrix); #else if (size != matrix.columns()) throw new ApplicationException("non square matrix: " + size + " rows, " + matrix.columns() + " columns"); #endif if (!(componentRetainedPercentage > 0.0)) throw new ApplicationException("no eigenvalues retained"); if (!(componentRetainedPercentage <= 1.0)) throw new ApplicationException("percentage to be retained > 100%"); if (!(maxRank >= 1)) throw new ApplicationException("max rank required < 1"); // spectral (a.k.a Principal Component) analysis SymmetricSchurDecomposition jd = new SymmetricSchurDecomposition(matrix); Vector eigenValues = jd.eigenvalues(); // salvaging algorithm switch (sa) { case SalvagingAlgorithm.None: // eigenvalues are sorted in decreasing order if (!(eigenValues[size - 1] >= -1e-16)) throw new ApplicationException("negative eigenvalue(s) (" + eigenValues[size - 1] + ")"); break; case SalvagingAlgorithm.Spectral: // negative eigenvalues set to zero for (int i = 0; i < size; ++i) eigenValues[i] = Math.Max(eigenValues[i], 0.0); break; case SalvagingAlgorithm.Higham: { int maxIterations = 40; double tolerance = 1e-6; Matrix adjustedMatrix = highamImplementation(matrix, maxIterations, tolerance); jd = new SymmetricSchurDecomposition(adjustedMatrix); eigenValues = jd.eigenvalues(); } break; default: throw new ApplicationException("unknown or invalid salvaging algorithm"); } // factor reduction /*std::accumulate(eigenValues.begin(), eigenValues.end(), 0.0);*/ double accumulate = 0; eigenValues.ForEach((ii, vv) => accumulate += eigenValues[ii]); double enough = componentRetainedPercentage * accumulate; if (componentRetainedPercentage == 1.0) { // numerical glitches might cause some factors to be discarded enough *= 1.1; } // retain at least one factor double components = eigenValues[0]; int retainedFactors = 1; for (int i = 1; components < enough && i < size; ++i) { components += eigenValues[i]; retainedFactors++; } // output is granted to have a rank<=maxRank retainedFactors = Math.Min(retainedFactors, maxRank); Matrix diagonal = new Matrix(size, retainedFactors, 0.0); for (int i = 0; i < retainedFactors; ++i) diagonal[i, i] = Math.Sqrt(eigenValues[i]); Matrix result = jd.eigenvectors() * diagonal; normalizePseudoRoot(matrix, result); return result; }