Positive definite matrix projection
All co-moment matrices are supposed to be positive definite. However, non-positive definite matrices can arise due to collinearity, having fewer observations than observables, and floating point inacuracies. Non-positive definite matrices have zero or negative eigenvalues.
- Zero eigenvalues imply collinearity between assets, which means the system is underdetermined and therefore a linear or quadratic system involving the matrix does not have a unique solution.
- Negative eigenvalues imply numerical stability issues, which usually arise from poorly conditioned systems, which for covariance and correlation matrices arise from high collinearity.
In order to obtain unique results and improve numerical stability, these non-positive definite matrices can be projected to the nearest positive definite matrix. This keeps the underlying relationships as intact as possible, while ensuring they have the appropriate numerical characteristics.
These types and functions let us do so.
PortfolioOptimisers.AbstractPosdefEstimator — Type
abstract type AbstractPosdefEstimator <: AbstractEstimatorAbstract supertype for all positive definite matrix estimator types.
All concrete and/or abstract types that implement positive definite matrix projection or estimation should be subtypes of AbstractPosdefEstimator.
Interfaces
In order to implement a new positive definite matrix estimator which will work seamlessly with the library, subtype AbstractPosdefEstimator with all necessary parameters as part of the struct, and implement the following methods:
posdef!(pdm::AbstractPosdefEstimator, X::MatNum) -> MatNum: In-place projection of a matrix to the nearest positive definite matrix.posdef(pdm::AbstractPosdefEstimator, X::MatNum) -> MatNum: Optional out-of-place projection of a matrix to the nearest positive definite matrix. A fallback method copiesXand callsposdef!, so it is only needed if the copy can be avoided.
Arguments
pdm: Positive definite matrix estimator.X: Covariance-like or correlation-like matrixassets × assets.
Returns
X::MatNum: The projected input matrixX.
Examples
We can create a dummy positive definite estimator as follows:
julia> struct MyPosdefEstimator <: PortfolioOptimisers.AbstractPosdefEstimator endjulia> function PortfolioOptimisers.posdef!(pdm::MyPosdefEstimator, X::PortfolioOptimisers.MatNum) # Implement your in-place PD projection logic here. println("Projecting to positive definite matrix in-place...") return X endjulia> function PortfolioOptimisers.posdef(pdm::MyPosdefEstimator, X::PortfolioOptimisers.MatNum) X = copy(X) println("Copy X...") posdef!(pdm, X) return X endjulia> posdef!(MyPosdefEstimator(), [1.0 2.0; 2.0 1.0])Projecting to positive definite matrix in-place...2×2 Matrix{Float64}: 1.0 2.0 2.0 1.0julia> posdef(MyPosdefEstimator(), [1.0 2.0; 2.0 1.0])Copy X...Projecting to positive definite matrix in-place...2×2 Matrix{Float64}: 1.0 2.0 2.0 1.0Related
PortfolioOptimisers.Posdef — Type
struct Posdef{__T_alg, __T_kwargs} <: AbstractPosdefEstimatorProjects a matrix to the nearest positive definite matrix, typically used for co-moment matrices.
Posdef encapsulates all parameters required for positive definite matrix projection in posdef! and posdef to perform the nearest positive definite projection according to the estimator.
Fields
alg: The algorithm used for the nearest correlation matrix projection.
kwargs: A named tuple of keyword arguments to be passed to the algorithm.
Constructors
Posdef(; alg::Any = NearestCorrelationMatrix.Newton, kwargs::NamedTuple = (;),) -> PosdefKeywords correspond to the struct's fields.
Examples
julia> Posdef()Posdef alg ┼ UnionAll: NearestCorrelationMatrix.Newton kwargs ┴ @NamedTuple{}: NamedTuple()Related
References
- [7] N. J. Higham. Computing the nearest correlation matrix—a problem from finance. IMA Journal of Numerical Analysis 22, 329–343 (2002).
- [8] H. Qi and D. Sun. A quadratically convergent Newton method for computing the nearest correlation matrix. SIAM Journal on Matrix Analysis and Applications 28, 360–385 (2006).
PortfolioOptimisers.posdef — Function
posdef(pdm::Option{<:AbstractPosdefEstimator}, X::MatNum) -> MatNumOut-of-place version of posdef!.
Algorithm
- Copy
X. - Apply
posdef!to the copy, and return it. The input is never modified.
Arguments
pdm: Optional positive definite matrix estimator.X: Covariance-like or correlation-like matrixassets × assets.
Returns
X::MatNum: A new matrix equal to the nearest positive definite projection of the input.
Examples
julia> using LinearAlgebrajulia> X = [1.0 2.0; 2.0 1.0];julia> Xpd = posdef(Posdef(), X);julia> LinearAlgebra.isposdef(Xpd)trueRelated
PortfolioOptimisers.posdef! — Function
posdef!(pdm::Option{<:AbstractPosdefEstimator}, X::MatNum) -> MatNumIn-place projection of a matrix to the nearest positive definite matrix using the specified estimator.
For matrices without unit diagonal, the function converts them into correlation matrices i.e. matrices with unit diagonal, applies the algorithm, and rescales them back.
Mathematical definition
Solves the nearest correlation matrix problem:
\[\begin{align} \hat{\mathbf{C}} &= \underset{\mathbf{Y} \succeq 0,\; Y_{ii} = 1}{\arg\min} \lVert \mathbf{C} - \mathbf{Y} \rVert_F\,. \end{align}\]
Where:
- $\hat{\mathbf{C}}$: Nearest positive semidefinite correlation matrix.
- $\mathbf{C}$: Input correlation matrix.
- $\mathbf{Y}$: Feasible correlation matrix (positive semidefinite, unit diagonal).
- $\lVert \cdot \rVert_F$: Frobenius norm.
For covariance matrices, first standardise $\mathbf{C} = \mathrm{diag}(\mathbf{\Sigma})^{-1/2} \mathbf{\Sigma}\, \mathrm{diag}(\mathbf{\Sigma})^{-1/2}$, project, then rescale back.
Algorithm
- Return
Xunchanged when it is already positive definite. The projection has nothing to do, and the check runs before every other step. - Check that
Xis square. - Read the diagonal of
Xintos. When any entry ofsis not one,Xis a covariance matrix: replaceswith its square roots and convertXto a correlation matrix withStatsBase.cov2cor!. The test isany(!isone, s), so it is the value of the diagonal that decides, never the type ofX. - Project
Xonto the nearest correlation matrix withNearestCorrelationMatrix.nearest_cor!, under the algorithmpdm.algand the keyword argumentspdm.kwargs. - Warn when the projected
Xis still not positive definite.Xis returned either way, so the caller must check the result when it cannot tolerate an unrepaired matrix. - When step 3 converted a covariance matrix, convert
Xback withStatsBase.cor2cov!. The standard deviations are the ones read in step 3, so the original diagonal returns exactly.
Arguments
pdm: Optional positive definite matrix estimator.::Posdef: The algorithm specified inpdm.algis used to projectXto the nearest PD matrix. IfXis already positive definite, it is left unchanged.::Nothing: No-op.
X: Covariance-like or correlation-like matrixassets × assets.
Validation
Xis validated withassert_matrix_issquare. The check runs after the early return, so it is reached only whenXis not already positive definite.
Returns
X::MatNum: The input matrixXmodified in-place.
Examples
julia> using LinearAlgebrajulia> est = Posdef();julia> X = [1.0 0.9; 0.9 1.0];julia> X[1, 2] = 2.0; # Not PDjulia> posdef!(est, X)2×2 Matrix{Float64}: 1.0 1.0 1.0 1.0julia> LinearAlgebra.isposdef(X)trueRelated
References
- [7]
- N. J. Higham. Computing the nearest correlation matrix—a problem from finance. IMA Journal of Numerical Analysis 22, 329–343 (2002).
- [8]
- H. Qi and D. Sun. A quadratically convergent Newton method for computing the nearest correlation matrix. SIAM Journal on Matrix Analysis and Applications 28, 360–385 (2006).