Matrix square root
A square root $\mathbf{L}$ of a covariance matrix $\mathbf{\Sigma}$ satisfies $\mathbf{L} \mathbf{L}^{\intercal} = \mathbf{\Sigma}$. A factor prior states its covariance through such a square root, and a second-order cone reads it. A positive definite matrix has a lower Cholesky factor. A singular or a nearly singular matrix has none, and the algorithms below state what to do then:
nothingtakes the plain Cholesky factor, and raises aPosDefException.RidgeCholeskySquareRootadds a small ridge to the diagonal, and grows it until the Cholesky factor exists.EigenFallbackSquareRoottakes the square root of the eigendecomposition of a positive semidefinite matrix, with no ridge.
Each estimator that takes a square root of a covariance holds the algorithm in its field mtx_sqrt: FactorPrior, CrossSectionalFactorPrior, FactorBlackLittermanPrior, Variance, StandardDeviation, DistributionValueatRisk, UncertaintySetVariance, ArithmeticReturn, RelaxedRiskBudgeting, Kurtosis, NegativeSkewness, NormBallUncertaintySetAlgorithm and OrthogonalUncertaintySet. The default of each is EigenFallbackSquareRoot: the plain Cholesky factor of a positive definite matrix, and the square root of the eigendecomposition of a singular positive semidefinite one. RidgeCholeskySquareRoot and nothing, the plain Cholesky factor that refuses a matrix that is not positive definite, are one keyword away.
PortfolioOptimisers.AbstractMatrixSquareRootAlgorithm — Type
abstract type AbstractMatrixSquareRootAlgorithm <: AbstractAlgorithmAbstract supertype for the algorithms that take a square root of a covariance matrix.
A square root $\mathbf{L}$ of a covariance matrix $\mathbf{\Sigma}$ satisfies $\mathbf{L} \mathbf{L}^{\intercal} = \mathbf{\Sigma}$. Each algorithm states what it does when $\mathbf{\Sigma}$ has no Cholesky factor. nothing in the place of an algorithm takes the plain Cholesky factor, which raises a LinearAlgebra.PosDefException on such a matrix.
Interfaces
In order to implement a new square-root algorithm that works with the library, subtype AbstractMatrixSquareRootAlgorithm with its parameters as fields, and implement the following method:
matrix_square_root
matrix_square_root(alg::MyMatrixSquareRootAlgorithm, sigma::MatNum) -> MatNum: The square root ofsigma.
Arguments
alg: The square-root algorithm.sigma: The covariance matrix,assets × assets.
Returns
L::MatNum: A square root withL * L' == sigma,assets × assets.
Examples
julia> struct MyMatrixSquareRootAlgorithm <: PortfolioOptimisers.AbstractMatrixSquareRootAlgorithm endjulia> function PortfolioOptimisers.matrix_square_root(::MyMatrixSquareRootAlgorithm, sigma::PortfolioOptimisers.MatNum) return sqrt(sigma) endjulia> matrix_square_root(MyMatrixSquareRootAlgorithm(), [4.0 0.0; 0.0 9.0])2×2 Matrix{Float64}: 2.0 0.0 0.0 3.0Related
PortfolioOptimisers.RidgeCholeskySquareRoot — Type
struct RidgeCholeskySquareRoot{__T_ridge, __T_tries} <: AbstractMatrixSquareRootAlgorithmAdds a growing ridge to the diagonal of a covariance matrix until its Cholesky factor exists.
The first try is the plain Cholesky factor of the lower triangle. Where it fails, each later try adds a ridge to the diagonal of the symmetrised matrix, so the square root reproduces the matrix to within the last ridge. A singular matrix, such as the covariance of two factors with one return series, gets a positive definite square root.
Mathematical definition
\[\begin{align} s &= \frac{1}{N} \sum_{i=1}^{N} \lvert \Sigma_{ii} \rvert\,, \\ \lambda_{k} &= 10^{k-1} \max(r s, \epsilon s)\,, \\ \mathbf{L}_{k} \mathbf{L}_{k}^{\intercal} &= \frac{\mathbf{\Sigma} + \mathbf{\Sigma}^{\intercal}}{2} + \lambda_{k} \mathbf{I}\,. \end{align}\]
Where:
- $s$: Scale of the ridge, the mean absolute variance. Where it is not a finite positive number, $s$ is the largest absolute entry of the symmetrised matrix, and at least one.
- $N$: Number of rows of $\mathbf{\Sigma}$.
- $\Sigma_{ii}$: Variance $i$ of the covariance matrix $\mathbf{\Sigma}$.
- $r$: The relative ridge
ridge. - $\epsilon$: The machine epsilon of the element type, which keeps the ridge above zero when $r$ is zero.
- $\lambda_{k}$: Ridge of try $k$, for $k = 1, \ldots,$
tries. - $\mathbf{L}_{k}$: Lower Cholesky factor of try $k$.
- $\mathbf{I}$: Identity matrix.
The ridge is relative to $s$, so the square root of $c^2 \mathbf{\Sigma}$ is $c$ times the square root of $\mathbf{\Sigma}$.
Fields
ridge: Ridge of the first try relative to the mean absolute variance, $r$. Each later try multiplies the ridge by ten.
tries: Number of tries with a ridge, after the plain Cholesky factor fails.
Constructors
RidgeCholeskySquareRoot(; ridge::Number = 1e-12, tries::Integer = 3,) -> RidgeCholeskySquareRootKeywords correspond to the struct's fields.
Validation
ridgeis finite and>= 0.tries > 0.
Examples
julia> RidgeCholeskySquareRoot()RidgeCholeskySquareRoot ridge ┼ Float64: 1.0e-12 tries ┴ Int64: 3A matrix of rank one has no Cholesky factor. The first ridge, 1e-12 of the mean variance, makes it positive definite.
julia> sigma = [1.0 1.0; 1.0 1.0];julia> L = matrix_square_root(RidgeCholeskySquareRoot(), sigma);julia> maximum(abs, L * L' - sigma) <= 2e-12trueRelated
PortfolioOptimisers.EigenFallbackSquareRoot — Type
struct EigenFallbackSquareRoot <: AbstractMatrixSquareRootAlgorithmTakes the square root of a positive semidefinite covariance matrix from its eigendecomposition where the matrix has no Cholesky factor.
A positive definite matrix gives its lower Cholesky factor. A singular positive semidefinite matrix, such as a covariance of rank one, gives the square root of its eigendecomposition, which reproduces the matrix to rounding and adds no ridge. An indefinite matrix is refused.
Mathematical definition
\[\begin{align} \mathbf{L} &= \begin{cases} \operatorname{chol}(\mathbf{\Sigma}) & \mathbf{\Sigma} \succ 0\,, \\ \mathbf{V} \max(\mathbf{\Lambda}, 0)^{1/2} & \text{otherwise, with } \mathbf{\Sigma} = \mathbf{V} \mathbf{\Lambda} \mathbf{V}^{\intercal}\,. \end{cases} \end{align}\]
Where:
- $\mathbf{L}$: Square root of $\mathbf{\Sigma}$, with $\mathbf{L} \mathbf{L}^{\intercal} = \mathbf{\Sigma}$.
- $\operatorname{chol}$: Lower Cholesky factor.
- $\mathbf{V}$, $\mathbf{\Lambda}$: Eigenvectors, and the diagonal matrix of the eigenvalues. The maximum with zero removes a negative eigenvalue of rounding.
Constructors
EigenFallbackSquareRoot() -> EigenFallbackSquareRootExamples
julia> sigma = [1.0 1.0; 1.0 1.0];julia> L = matrix_square_root(EigenFallbackSquareRoot(), sigma);julia> maximum(abs, L * L' - sigma) < 1e-15trueRelated
PortfolioOptimisers.matrix_square_root — Function
matrix_square_root(::Nothing, sigma::MatNum) -> MatNum
matrix_square_root(alg::RidgeCholeskySquareRoot, sigma::MatNum) -> MatNum
matrix_square_root(alg::EigenFallbackSquareRoot, sigma::MatNum) -> MatNumTakes a square root $\mathbf{L}$ of a covariance matrix, with $\mathbf{L} \mathbf{L}^{\intercal} = \mathbf{\Sigma}$.
nothing takes the plain lower Cholesky factor. RidgeCholeskySquareRoot takes the Cholesky factor of the matrix with the first ridge that makes it positive definite. EigenFallbackSquareRoot takes the Cholesky factor where it exists, and the square root of the eigendecomposition of a positive semidefinite matrix otherwise. A FactorPrior and a CrossSectionalFactorPrior take the square root of their factor covariance through this function.
Arguments
alg: The square-root algorithm, ornothing.sigma: The covariance matrix,assets × assets.
Validation
nothing:sigmahas a Cholesky factor. Raises aLinearAlgebra.PosDefException.RidgeCholeskySquareRoot: one try ofridge_choleskysucceeds. Raises aLinearAlgebra.PosDefExceptionwith theinfoof the plain factorisation.EigenFallbackSquareRoot: a matrix that has no Cholesky factor is Hermitian, and its smallest eigenvalue is at least $-N \epsilon \max_i \lvert \lambda_i \rvert$. Here $N$ is the number of rows and $\epsilon$ is the machine epsilon of the element type. RaisesLinearAlgebra.PosDefException(-1)andLinearAlgebra.PosDefException(1).
Returns
L::MatNum: The square root,assets × assets. It is lower triangular except for the eigen square root.
Examples
The three members differ only on a matrix that has no Cholesky factor.
julia> using LinearAlgebrajulia> sigma = [1.0 1.0; 1.0 1.0];julia> try matrix_square_root(nothing, sigma) catch err err isa PosDefException endtruejulia> L = matrix_square_root(RidgeCholeskySquareRoot(), sigma);julia> maximum(abs, L * L' - sigma - 1e-12I) < 1e-15truejulia> L = matrix_square_root(EigenFallbackSquareRoot(), sigma);julia> maximum(abs, L * L' - sigma) < 1e-15trueRelated