//! 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 Utils.QL_REQUIRE(size == matrix.columns(), () => "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 Utils.QL_REQUIRE(jd.eigenvalues()[size - 1] >= -1e-16, () => "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: Utils.QL_FAIL("unknown salvaging algorithm"); break; } 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 Utils.QL_REQUIRE(size == matrix.columns(), () => "non square matrix: " + size + " rows, " + matrix.columns() + " columns"); #endif Utils.QL_REQUIRE(componentRetainedPercentage > 0.0, () => "no eigenvalues retained"); Utils.QL_REQUIRE(componentRetainedPercentage <= 1.0, () => "percentage to be retained > 100%"); Utils.QL_REQUIRE(maxRank >= 1, () => "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 Utils.QL_REQUIRE(eigenValues[size - 1] >= -1e-16, () => "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: Utils.QL_FAIL("unknown or invalid salvaging algorithm"); break; } // factor reduction double accumulate = 0; eigenValues.ForEach((ii, vv) => accumulate += eigenValues[ii]); double enough = componentRetainedPercentage * accumulate; if (componentRetainedPercentage.IsEqual(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); }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(SalvagingAlgorithm obj) { return((obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr); }
//! 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; }
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; }
public static Matrix pseudoSqrt(Matrix m, SalvagingAlgorithm.Type a) { Matrix ret = new Matrix(NQuantLibcPINVOKE.pseudoSqrt(Matrix.getCPtr(m), (int)a), true); if (NQuantLibcPINVOKE.SWIGPendingException.Pending) throw NQuantLibcPINVOKE.SWIGPendingException.Retrieve(); return ret; }
internal static global::System.Runtime.InteropServices.HandleRef getCPtr(SalvagingAlgorithm obj) { return (obj == null) ? new global::System.Runtime.InteropServices.HandleRef(null, global::System.IntPtr.Zero) : obj.swigCPtr; }