Does Mathematica have a way to "fix" a correlation matrix that is not positive semi-definite?
I looked through the documentation and search the internet but could not find anything.
Does Mathematica have a way to "fix" a correlation matrix that is not positive semi-definite?
I looked through the documentation and search the internet but could not find anything.
Update: the previous method continues the iteration until all eigenvalues are at or above the threshold, that is, it does not check and stop if the current matrix is PD. The following is a version that tests for positive definiteness using PositiveDefiniteMatrixQ at every iteration:
ClearAll[covBending2];
covBending2[mat_, tol_: 1/10000] := If[PositiveDefiniteMatrixQ[mat], mat,
NestWhile[ (Eigensystem[#][[2]].DiagonalMatrix[
Max[#, tol] & /@ (Eigensystem[#][[1]])].Transpose[
Eigensystem[#][[2]]]) &, N@mat, !PositiveDefiniteMatrixQ[#]&]]
Original answer:
Here is simple (unweighted) Mma version of the Matlab implementation of Covariance Bending.
ClearAll[covBending];
covBending[mat_, tol_: 1/10000]:=If[PositiveDefiniteMatrixQ[mat], mat,
NestWhile[(Eigensystem[#][[2]].DiagonalMatrix[
Max[#, tol] & /@(Eigensystem[#][[1]])].Transpose[
Eigensystem[#][[2]]]) &, N@mat, Min[Eigensystem[#][[1]]] < tol &]]
examples:
matrices = ((1/2) (# + Transpose[#]) & /@ RandomReal[{0, 1}, {10, 4, 4}]);
{MatrixForm[#], PositiveDefiniteMatrixQ[#], MatrixForm[covBending[#]],
PositiveDefiniteMatrixQ[covBending[#]]} & /@ matrices // TableForm

Generally, the reason why matrices that were supposed to be positive semi-definite but are not, is because the constraint of working in a finite precision world often introduces a wee bit of perturbation in the lowest eigenvalues of the matrix, making it either negative or complex. These errors are generally of the order of machine precision, but is enough to return False if you use standard algorithms to check for positive semi-definiteness. This can be easily fixed with Chop. For example, something like the following:
psdMat = #1.Chop@#2.#3\[ConjugateTranspose] & @@ SingularValueDecomposition[mat];
Now if this isn't sufficient to make your matrix positive semi-definite, you should go back and take a closer look at your problem to see if there are other reasons to not expect it to be positive semi-definite.
Another example of this, is that you may have some of the correlation matrix be known but other values are not and the user needs to enter what they think is the correct correlation amount.
– Stephen Lien Aug 31 '12 at 13:53I like the approach in this paper: The most general methodology to create a valid correlation matrix for risk management and option pricing purposes. Below is a starting point for an implementation.
MakeGoodCorrelationMatrix[badCorrMat_,flag_]:=
Which[
flag==1,
Module[{LocalN=Length[badCorrMat],LocalTheta,LocalB,LocalOutput,LocalSolution},
LocalTheta=Outer[Unique[] &, Range[LocalN], Range[LocalN-1], 1, 1];
LocalB = Outer[If[#2 < LocalN, Cos[LocalTheta[[#1, #2]]] (Times @@ Sin[LocalTheta[[#1, 1 ;; #2 - 1]]]),
Times @@ Sin[LocalTheta[[#1, 1 ;; #2 - 1]]]] &, Range[LocalN], Range[LocalN], 1, 1] //Chop;
LocalOutput = Dot[LocalB, Transpose[LocalB]] //Chop;
LocalSolution = Minimize[Total[Flatten[(LocalOutput - badCorrMat)^2]], Flatten[LocalTheta]]//Chop;
LocalOutput /. LocalSolution[[2]]
],
True,
Module[{LocalLambdap = (Max[0.000001, #] & /@ Eigenvalues[badCorrMat]) , LocalS = Eigenvectors[badCorrMat] // Transpose, LocalT, LocalB, LocalOutput},
LocalT = DiagonalMatrix[Dot[#^2,LocalLambdap]^(-1) & /@ LocalS] ;
LocalB = Dot[Sqrt[LocalT],LocalS,Sqrt[DiagonalMatrix[LocalLambdap]]];
LocalOutput = Dot[LocalB,Transpose[LocalB]] ;
LocalOutput = 1/2 (LocalOutput + Transpose[LocalOutput]) (* just to make sure the output is symmetric *)
]
]
Example :
badMat = {{1, 0.05, 0.9}, {0.05, 1, 0.8}, {0.9, 0.8, 1}};
Eigenvalues[badMat]
(* {2.22926, 0.950346, -0.179603} *)
goodMat1 = MakeGoodCorrelationMatrix[badMat, 1];
Eigenvalues[goodMat1]
(* {2.11798, 0.882022, 8.32667*10^-17} *)
goodMat2 = MakeGoodCorrelationMatrix[badMat, 2];
Eigenvalues[goodMat2]
(* {2.08911, 0.910888, 9.35756*10^-7} *)
flag==1) was quite slow for matrices above 5x5 (maybe use NMinimize ?), the second approach shouldn't have a hard-coded cutoff and I did not check it will give reasonable results for large matrices. Do post your own version when ready please.
– b.gates.you.know.what
Mar 06 '18 at 08:28
! PositiveDefiniteMatrixQ[mat]? – J. M.'s missing motivation Aug 31 '12 at 07:59matis positive definite is actually much faster than checking if minimum eigenvalue ofmatless than a threshold. – kglr Aug 31 '12 at 08:15fixingarises in the context of estimation. In this connection,it always helps to distinguish between objects (BigTheta, CovMat, ...) and their estimates (the same objects with ahats). CovMat is PD but, it is never "observed", CovMatHat is seldom, if ever, guaranteed to be PD, and depending on the estimation methods (which, btw, vary from seat-of-the-pants judgmental methods to dozens of statistical methods) and numerical techniques used, may not even be symmetric. – kglr Aug 31 '12 at 23:08