diff --git a/CHANGELOG.rst b/CHANGELOG.rst index eadf0ecd..9b4105d7 100644 --- a/CHANGELOG.rst +++ b/CHANGELOG.rst @@ -7,6 +7,13 @@ Changelog ========= +3.2.2 - 2026-03-17 +------------------ + +**Bug fix:** + +- Fixed incorrect formula in :meth:`~glum.CloglogLink.inverse_derivative2`. This affected observed information matrix computation and robust/clustered standard errors for models using the complementary log-log link. + 3.2.1 - 2026-03-16 ------------------ diff --git a/src/glum/_link.py b/src/glum/_link.py index 5dd6de47..7fc3e962 100644 --- a/src/glum/_link.py +++ b/src/glum/_link.py @@ -253,7 +253,7 @@ def inverse_derivative2(self, lin_pred): # noqa D class CloglogLink(Link): - """The complementary log-log link function ``log(-log(-p))``.""" + """The complementary log-log link function ``log(-log(1-mu))``.""" def __eq__(self, other): # noqa D return isinstance(other, self.__class__) @@ -279,5 +279,4 @@ def inverse_derivative(self, lin_pred): # noqa D return np.exp(lin_pred - np.exp(lin_pred)) def inverse_derivative2(self, lin_pred): # noqa D - # TODO: check if numerical stability can be improved - return np.exp(np.exp(lin_pred) - lin_pred) * np.expm1(lin_pred) + return -np.exp(lin_pred - np.exp(lin_pred)) * np.expm1(lin_pred) diff --git a/tests/glm/test_link.py b/tests/glm/test_link.py index 7c4095d5..dc795791 100644 --- a/tests/glm/test_link.py +++ b/tests/glm/test_link.py @@ -32,6 +32,15 @@ def test_link_properties(link): assert link.inverse_derivative2(x).shape == link.inverse_derivative(x).shape + # Check inverse_derivative2 against numerical second derivative + h = 1e-7 + numerical_d2 = (link.inverse_derivative(x + h) - link.inverse_derivative(x - h)) / ( + 2 * h + ) + np.testing.assert_allclose( + link.inverse_derivative2(x), numerical_d2, rtol=1e-4, atol=1e-8 + ) + def test_equality(): assert IdentityLink() == IdentityLink()