Skip to content

Commit 3a333be

Browse files
committed
[Minuit2] Document why covariance transform omits 2nd-derivative term
Int2extCovariance/Ext2intCovariance transform the error matrix between internal and external coordinates using only the first-order Jacobian (dPext/dPint), even on the diagonal. This is intentional and contrasts with the Hessian/G2 transformation in AnalyticalGradientCalculator, which carries an extra diagonal term d^2Pext/dPint^2 * gradient. The Hessian needs that non-tensorial term because it is evaluated at arbitrary, non-stationary points where the external gradient is nonzero. The covariance matrix is the inverse Hessian evaluated at the minimum, where the gradient vanishes; the term is then identically zero and the covariance transforms as a genuine (2,0) tensor with the Jacobian alone. Adding it would also break the exact round-trip between Int2extCovariance and Ext2intCovariance.
1 parent f384a05 commit 3a333be

2 files changed

Lines changed: 18 additions & 0 deletions

File tree

math/minuit2/src/Minuit2Minimizer.cxx

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -787,6 +787,12 @@ bool Minuit2Minimizer::GetHessianMatrix(double *hess) const
787787
{
788788
// get value of Hessian matrix
789789
// this is the second derivative matrices
790+
//
791+
// Note: for parameters with limits, the returned external Hessian is obtained by inverting the
792+
// external covariance matrix, which is transformed from the internal one with the Jacobian of the
793+
// int<->ext transformation only (see MnUserTransformation::Int2extCovariance). This is correct only
794+
// at the minimum, where the external gradient vanishes. Away from the minimum the transformation
795+
// would need an additional second-derivative term and the result would be inaccurate.
790796
if (!fState.HasCovariance())
791797
return false; // no info available when minimization has failed
792798
for (unsigned int i = 0; i < fDim; ++i) {

math/minuit2/src/MnUserTransformation.cxx

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,15 @@ MnUserTransformation::Int2extCovariance(const MnAlgebraicVector &vec, const MnAl
140140
// return the external covariance matrix from the internal error matrix and the internal parameter value
141141
// the vector of internal parameter is needed for the derivatives (Jacobian of the transformation)
142142
// Vext(i,j) = Vint(i,j) * dPext(i)/dPint(i) * dPext(j)/dPint(j)
143+
//
144+
// Note: only the first derivative (Jacobian) of the int<->ext transformation enters here, even on the
145+
// diagonal. Unlike the Hessian/G2 transformation (see AnalyticalGradientCalculator), there is no
146+
// d^2Pext/dPint^2 * gradient term. The Hessian needs that extra non-tensorial term because it is
147+
// evaluated at arbitrary, non-stationary points where the external gradient is nonzero. The covariance
148+
// matrix, by contrast, is the inverse Hessian evaluated at the minimum, where the external gradient
149+
// vanishes and that term is identically zero; the covariance then transforms as a genuine (2,0) tensor
150+
// with the Jacobian alone. (Adding the term here would also break the exact round-trip between
151+
// Int2extCovariance and Ext2intCovariance.)
143152

144153
MnUserCovariance result(cov.Nrow());
145154
for (unsigned int i = 0; i < vec.size(); i++) {
@@ -171,6 +180,9 @@ MnUserTransformation::Ext2intCovariance(const MnAlgebraicVector &vec, const MnAl
171180
// return the internal covariance matrix from the external error matrix and the internal parameter value
172181
// the vector of internal parameter is needed for the derivatives (Jacobian of the transformation)
173182
// Vint(i,j) = Vext(i,j) * dPint(i)/dPext(i) * dPint(j)/dPext(j)
183+
//
184+
// As in Int2extCovariance, only the Jacobian enters: no second-derivative (d^2Pint/dPext^2 * gradient)
185+
// term is needed, since the covariance is the inverse Hessian at the minimum where the gradient is zero.
174186

175187
MnUserCovariance result(cov.Nrow());
176188
for (unsigned int i = 0; i < vec.size(); i++) {

0 commit comments

Comments
 (0)