|
45 | 45 | # these are the exitsing bounds implemented here |
46 | 46 |
|
47 | 47 |
|
| 48 | +def _slogdet_checked(matrix): |
| 49 | + """ |
| 50 | + Compute the log-determinant and check that it's positive. |
| 51 | +
|
| 52 | + Parameters |
| 53 | + ---------- |
| 54 | + matrix : array_like |
| 55 | + Matrix to compute determinant for. |
| 56 | +
|
| 57 | + Returns |
| 58 | + ------- |
| 59 | + detln : float |
| 60 | + Natural log of the absolute value of the determinant. |
| 61 | +
|
| 62 | + Raises |
| 63 | + ------ |
| 64 | + ValueError |
| 65 | + If the determinant is not positive. |
| 66 | + """ |
| 67 | + detsign, detln = linalg.slogdet(matrix) |
| 68 | + if detsign <= 0: |
| 69 | + raise ValueError( |
| 70 | + f"Invalid ellipsoid: covariance matrix has non-positive determinant " |
| 71 | + f"(sign={detsign}). This may indicate degenerate live points or " |
| 72 | + f"numerical instability. Matrix shape: {matrix.shape}" |
| 73 | + ) |
| 74 | + return detln |
| 75 | + |
| 76 | + |
48 | 77 | class Bound: |
49 | 78 | """ |
50 | 79 | This is is master class for the all the bounds listing |
@@ -729,8 +758,7 @@ def __init__(self, ndim, cov=None): |
729 | 758 | self.axes = lalg.sqrtm(self.cov) |
730 | 759 | self.axes_inv = lalg.pinvh(self.axes) |
731 | 760 |
|
732 | | - detsign, detln = linalg.slogdet(self.am) |
733 | | - assert detsign > 0 |
| 761 | + detln = _slogdet_checked(self.am) |
734 | 762 | self.logvol = logvol_prefactor(self.ndim) - 0.5 * detln |
735 | 763 | self.funit = 1 |
736 | 764 | self.ctrs = [] # placeholder |
@@ -922,8 +950,7 @@ def update(self, |
922 | 950 | self.ctrs = points |
923 | 951 |
|
924 | 952 | # Compute volume. |
925 | | - detsign, detln = linalg.slogdet(self.am) |
926 | | - assert detsign > 0 |
| 953 | + detln = _slogdet_checked(self.am) |
927 | 954 | self.logvol = (logvol_prefactor(self.ndim) - 0.5 * detln) |
928 | 955 |
|
929 | 956 | # Estimate the volume and fractional overlap with the unit cube |
@@ -997,8 +1024,7 @@ def __init__(self, ndim, cov=None): |
997 | 1024 | self.axes = lalg.sqrtm(self.cov) |
998 | 1025 | self.axes_inv = lalg.pinvh(self.axes) |
999 | 1026 |
|
1000 | | - detsign, detln = linalg.slogdet(self.am) |
1001 | | - assert detsign > 0 |
| 1027 | + detln = _slogdet_checked(self.am) |
1002 | 1028 | self.logvol = self.ndim * np.log(2.) - 0.5 * detln |
1003 | 1029 | self.funit = 1 |
1004 | 1030 | self.ctrs = [] |
@@ -1190,8 +1216,7 @@ def update(self, |
1190 | 1216 | self.axes *= hsmax |
1191 | 1217 | self.axes_inv /= hsmax |
1192 | 1218 |
|
1193 | | - detsign, detln = linalg.slogdet(self.am) |
1194 | | - assert detsign > 0 |
| 1219 | + detln = _slogdet_checked(self.am) |
1195 | 1220 | self.logvol = (self.ndim * np.log(2.) - 0.5 * detln) |
1196 | 1221 |
|
1197 | 1222 | # Estimate the volume and fractional overlap with the unit cube |
|
0 commit comments