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 in PortfolioOptimisers.jl.
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.
Arguments
pdm: Positive definite matrix estimator.X: Covariance-like or correlation-like matrixfeatures × features.
Returns
X::MatNum: The projected input matrixX.
Examples
We can create a dummy positive definite estimator as follows:
julia> struct MyPosdefEstimator <: PortfolioOptimisers.AbstractPosdefEstimator end
julia> 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
end
julia> function PortfolioOptimisers.posdef(pdm::MyPosdefEstimator, X::PortfolioOptimisers.MatNum)
X = copy(X)
println("Copy X...")
posdef!(pdm, X)
return X
end
julia> 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.0
julia> 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
sourcePortfolioOptimisers.Posdef Type
struct Posdef{__T_alg, __T_kwargs} <: AbstractPosdefEstimatorA concrete estimator type for projecting 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
sourcePortfolioOptimisers.posdef! Function
posdef!(pdm::Option{<:Posdef}, 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.
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 matrixfeatures × features.
Validation
Xis validated withassert_matrix_issquare.
Returns
X::MatNum: The input matrixXmodified in-place.
Details
If
pdmis::Nothing, orXis already positive definite, the function returnsXwithout modification.If
Xis already positive definite, it is left unchanged.If
Xis not a correlation matrix, it is converted to one before applying the algorithm.Calls
NearestCorrelationMatrix.nearest_cor!(X, pdm.alg; pdm.kwargs...)to perform the projection.If the algorithm fails to converge, a warning is emitted.
If
Xis not a correlation matrix, it is converted back.Returns
X.
Examples
julia> using LinearAlgebra
julia> est = Posdef();
julia> X = [1.0 0.9; 0.9 1.0];
julia> X[1, 2] = 2.0; # Not PD
julia> posdef!(est, X)
2×2 Matrix{Float64}:
1.0 1.0
1.0 1.0
julia> LinearAlgebra.isposdef(X)
trueRelated
source