@@ -14,6 +14,7 @@ def __init__(
1414 u_y_rand : Optional [np .ndarray ] = None ,
1515 u_y_syst : Optional [np .ndarray ] = None ,
1616 corr_y : Optional [Union [str , np .ndarray ]] = None ,
17+ skip_invcov : bool = False ,
1718 ) -> None :
1819 """
1920 Container class for measurement variable data.
@@ -29,6 +30,7 @@ def __init__(
2930 Accepted values: ``None``, ``"rand"`` (random), ``"syst"``
3031 (systematic), or a square matrix whose side length equals the
3132 length of ``y``.
33+ :param skip_invcov: If ``True``, skip the computation of the inverse covariance matrix (which is only needed for certain retrieval methods like optimal estimation).
3234 """
3335
3436 u_y_total , corr_y = self ._format_uncertainty (
@@ -43,9 +45,13 @@ def __init__(
4345
4446 self .corr_y = util .format_correlation (self .y_flat , corr_y )
4547
48+ self .corr_y , self .cholesky , self .W = self .return_corr_cholesky_whitening (
49+ self .corr_y
50+ )
51+
4652 self ._check_shapes (self .y_flat , self .u_y_flat , self .corr_y )
4753
48- if corr_y is not None :
54+ if corr_y is not None and not skip_invcov :
4955 self .invcov = self .calculate_inv_cov (self .u_y_flat , self .corr_y )
5056 else :
5157 self .invcov = None
@@ -136,6 +142,30 @@ def _format_uncertainty(u_total, u_rand, u_syst, corr):
136142 tot_corr = cm .convert_cov_to_corr (tot_cov , tot )
137143 return tot , tot_corr
138144
145+ @staticmethod
146+ def return_corr_cholesky_whitening (corr : Optional [np .ndarray ]) -> tuple :
147+ """
148+ Return the correlation matrix, its Cholesky decomposition, and the whitening matrix.
149+
150+ :param corr: Correlation matrix, or ``None``.
151+ :returns: Tuple of ``(corr, cholesky, W)`` where ``cholesky`` is
152+ the Cholesky decomposition of the correlation matrix, or
153+ ``None`` if ``corr`` is ``None``, and ``W`` is the whitening matrix.
154+ """
155+ if corr is not None :
156+ try :
157+ cholesky = np .linalg .cholesky (corr )
158+ W = np .linalg .solve (cholesky , np .eye (cholesky .shape [0 ]))
159+ return corr , cholesky , W
160+ except np .linalg .LinAlgError :
161+ # If the correlation matrix is not positive definite, use the nearest positive definite matrix
162+ corr_pd = cm .nearestPD_cholesky (corr , return_cholesky = False , corr = True )
163+ cholesky = np .linalg .cholesky (corr_pd )
164+ W = np .linalg .solve (cholesky , np .eye (cholesky .shape [0 ]))
165+ return corr_pd , cholesky , W
166+ else :
167+ return None , None , None
168+
139169 @staticmethod
140170 def calculate_inv_cov (unc : np .ndarray , corr : np .ndarray ) -> np .ndarray :
141171 """
@@ -151,5 +181,4 @@ def calculate_inv_cov(unc: np.ndarray, corr: np.ndarray) -> np.ndarray:
151181 if np .array_equal (cov , np .diag (np .diag (cov ))):
152182 return np .diag (1 / np .diag (cov ))
153183 else :
154- # might need a check for PD here
155184 return np .linalg .inv (cov )
0 commit comments