Mantid
Loading...
Searching...
No Matches
EigenFunctions.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2026 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
7//----------------------------------------------------------------------
8// Includes
9//----------------------------------------------------------------------
11
12namespace Mantid::CurveFitting {
13
19Eigen::MatrixXd covar_from_jacobian(const map_type &J, double epsrel) {
20 if (epsrel < 0.0) {
21 throw std::invalid_argument("epsrel must be non-negative");
22 }
23
24 const Eigen::Index nr = J.rows(); // nr = num rows
25 const Eigen::Index nc = J.cols(); // nc = num cols
26 if ((nc == 0) || (nr == 0))
27 return Eigen::MatrixXd{};
28
29 // Pivoted QR Decomposition: JP = QR
30 Eigen::ColPivHouseholderQR<Eigen::MatrixXd> qr(J);
31
32 // R is (nc x np) in general; the useful part for J^T J is (nc x nc) block
33 Eigen::MatrixXd R = qr.matrixR().topLeftCorner(nc, nc).template triangularView<Eigen::Upper>().toDenseMatrix();
34
35 // Determine rank using the same rule as GSL: compare diag entries to |R_11|
36 const double r11 = std::abs(R(0, 0));
37 Eigen::Index rank = 0;
38 if (r11 > 0.0) {
39 for (Eigen::Index k = 0; k < nc; ++k) {
40 if (std::abs(R(k, k)) > epsrel * r11) { // column considered linerally independant
41 ++rank;
42 } else {
43 break; // with pivoting, following first dependent subsequent cols should also be dependant
44 }
45 }
46 } else {
47 // R11 == 0, everything dependent
48 rank = 0;
49 }
50
51 // Build covariance in the pivoted parameter order
52 Eigen::MatrixXd cov_pivot = Eigen::MatrixXd::Zero(nc, nc);
53
54 if (rank > 0) {
55 // cov = (R^T R)^{-1} for the independent cols = R^{-1} R^{-T}
56 const Eigen::MatrixXd R1 = R.topLeftCorner(rank, rank).template triangularView<Eigen::Upper>();
57 Eigen::MatrixXd invR1 = R1.inverse();
58 Eigen::MatrixXd cov1 = invR1 * invR1.transpose();
59 cov_pivot.topLeftCorner(rank, rank) = cov1;
60 }
61
62 // Unpivot back to original parameter order:
63 // cov = (J^T J)^{-1} = P (R^T R)^{-1} P^T
64 const Eigen::PermutationMatrix<Eigen::Dynamic, Eigen::Dynamic> P = qr.colsPermutation();
65 Eigen::MatrixXd cov = P * cov_pivot * P.transpose();
66
67 return cov;
68}
69
70} // namespace Mantid::CurveFitting
Eigen::Map< Eigen::MatrixXd, 0, dynamic_stride > map_type
MANTID_CURVEFITTING_DLL Eigen::MatrixXd covar_from_jacobian(const map_type &J, double epsrel)
Mimics gsl_multifit_covar(J, epsrel, covar)