Exemple #1
0
        // 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);
        }
Exemple #2
0
        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);
        }
Exemple #3
0
        //! 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);
        }