Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:18:22

0001 /* Copyright 2008-2010, Technische Universitaet Muenchen,
0002    Authors: Christian Hoeppner & Sebastian Neubert & Johannes Rauch
0003 
0004    This file is part of GENFIT.
0005 
0006    GENFIT is free software: you can redistribute it and/or modify
0007    it under the terms of the GNU Lesser General Public License as published
0008    by the Free Software Foundation, either version 3 of the License, or
0009    (at your option) any later version.
0010 
0011    GENFIT is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014    GNU Lesser General Public License for more details.
0015 
0016    You should have received a copy of the GNU Lesser General Public License
0017    along with GENFIT.  If not, see <http://www.gnu.org/licenses/>.
0018 */
0019 
0020 #include "Tools.h"
0021 
0022 #include <cmath>
0023 #include <memory>
0024 #include <typeinfo>
0025 #include <cassert>
0026 
0027 #include <TDecompChol.h>
0028 #include <TMatrixDSymEigen.h>
0029 #include <TMatrixTSymCramerInv.h>
0030 #include <TMath.h>
0031 
0032 #include "AbsHMatrix.h"
0033 #include "Exception.h"
0034 
0035 
0036 namespace genfit {
0037 
0038 void tools::invertMatrix(const TMatrixDSym& mat, TMatrixDSym& inv, double* determinant){
0039   inv.ResizeTo(mat);
0040 
0041   // check if numerical limits are reached (i.e at least one entry < 1E-100 and/or at least one entry > 1E100)
0042   if (!(mat<1.E100) || !(mat>-1.E100)){
0043     Exception e("Tools::invertMatrix() - cannot invert matrix, entries too big (>1e100)",
0044         __LINE__,__FILE__);
0045     e.setFatal();
0046     throw e;
0047   }
0048   // do the trivial inversions for 1x1 and 2x2 matrices manually
0049   if (mat.GetNrows() == 1){
0050     if (determinant != nullptr) *determinant = mat(0,0);
0051     inv(0,0) = 1./mat(0,0);
0052     return;
0053   }
0054 
0055   if (mat.GetNrows() == 2){
0056     double det = mat(0,0)*mat(1,1) - mat(1,0)*mat(1,0);
0057     if (determinant != nullptr) *determinant = det;
0058     if(fabs(det) < 1E-50){
0059       Exception e("Tools::invertMatrix() - cannot invert matrix , determinant = 0",
0060           __LINE__,__FILE__);
0061       e.setFatal();
0062       throw e;
0063     }
0064     det = 1./det;
0065     inv(0,0) =             det * mat(1,1);
0066     inv(0,1) = inv(1,0) = -det * mat(1,0);
0067     inv(1,1) =             det * mat(0,0);
0068     return;
0069   }
0070 
0071   // else use TDecompChol
0072   bool status = 0;
0073   TDecompChol invertAlgo(mat, 1E-50);
0074 
0075   status = invertAlgo.Invert(inv);
0076   if(status == 0){
0077     Exception e("Tools::invertMatrix() - cannot invert matrix, status = 0",
0078         __LINE__,__FILE__);
0079     e.setFatal();
0080     throw e;
0081   }
0082 
0083   if (determinant != nullptr) {
0084     double d1, d2;
0085     invertAlgo.Det(d1, d2);
0086     *determinant = ldexp(d1, d2);
0087   }
0088 }
0089 
0090 void tools::invertMatrix(TMatrixDSym& mat, double* determinant){
0091   // check if numerical limits are reached (i.e at least one entry < 1E-100 and/or at least one entry > 1E100)
0092   if (!(mat<1.E100) || !(mat>-1.E100)){
0093     Exception e("Tools::invertMatrix() - cannot invert matrix, entries too big (>1e100)",
0094         __LINE__,__FILE__);
0095     e.setFatal();
0096     throw e;
0097   }
0098   // do the trivial inversions for 1x1 and 2x2 matrices manually
0099   if (mat.GetNrows() == 1){
0100     if (determinant != nullptr) *determinant = mat(0,0);
0101     mat(0,0) = 1./mat(0,0);
0102     return;
0103   }
0104 
0105   if (mat.GetNrows() == 2){
0106     double *arr = mat.GetMatrixArray();
0107     double det = arr[0]*arr[3] - arr[1]*arr[1];
0108     if (determinant != nullptr) *determinant = det;
0109     if(fabs(det) < 1E-50){
0110       Exception e("Tools::invertMatrix() - cannot invert matrix, determinant = 0",
0111           __LINE__,__FILE__);
0112       e.setFatal();
0113       throw e;
0114     }
0115     det = 1./det;
0116     double temp[3];
0117     temp[0] =  det * arr[3];
0118     temp[1] = -det * arr[1];
0119     temp[2] =  det * arr[0];
0120     //double *arr = mat.GetMatrixArray();
0121     arr[0] = temp[0];
0122     arr[1] = arr[2] = temp[1];
0123     arr[3] = temp[2];
0124     return;
0125   }
0126 
0127   // else use TDecompChol
0128   bool status = 0;
0129   TDecompChol invertAlgo(mat, 1E-50);
0130 
0131   status = invertAlgo.Invert(mat);
0132   if(status == 0){
0133     Exception e("Tools::invertMatrix() - cannot invert matrix, status = 0",
0134         __LINE__,__FILE__);
0135     e.setFatal();
0136     throw e;
0137   }
0138 
0139   if (determinant != nullptr) {
0140     double d1, d2;
0141     invertAlgo.Det(d1, d2);
0142     *determinant = ldexp(d1, d2);
0143   }
0144 }
0145 
0146 
0147 // Solves R^T x = b, replaces b with the result x.  R is assumed
0148 // to be upper-diagonal.  This is forward substitution, but with
0149 // indices flipped.
0150 bool tools::transposedForwardSubstitution(const TMatrixD& R, TVectorD& b)
0151 {
0152   size_t n = R.GetNrows();
0153   double *const bk = b.GetMatrixArray();
0154   const double *const Rk = R.GetMatrixArray();
0155   for (unsigned int i = 0; i < n; ++i) {
0156     double sum = bk[i];
0157     for (unsigned int j = 0; j < i; ++j) {
0158       sum -= bk[j]*Rk[j*n + i];  // already replaced previous elements in b.
0159     }
0160     if (Rk[i*n+i] == 0)
0161       return false;
0162     bk[i] = sum / Rk[i*n + i];
0163   }
0164   return true;
0165 }
0166 
0167 
0168 // Same, but for one column of the matrix b.  Used by transposedInvert below
0169 // assumes b(i,j) == (i == j)
0170 bool tools::transposedForwardSubstitution(const TMatrixD& R, TMatrixD& b, int nCol)
0171 {
0172   size_t n = R.GetNrows();
0173   double *const bk = b.GetMatrixArray() + nCol;
0174   const double *const Rk = R.GetMatrixArray();
0175   for (unsigned int i = nCol; i < n; ++i) {
0176     double sum = (i == (size_t)nCol);
0177     for (unsigned int j = 0; j < i; ++j) {
0178       sum -= bk[j*n]*Rk[j*n + i];  // already replaced previous elements in b.
0179     }
0180     if (Rk[i*n+i] == 0)
0181       return false;
0182     bk[i*n] = sum / Rk[i*n + i];
0183   }
0184   return true;
0185 }
0186 
0187 
0188 // inv will be the inverse of the transposed of the upper-right matrix R
0189 bool tools::transposedInvert(const TMatrixD& R, TMatrixD& inv)
0190 {
0191   bool result = true;
0192 
0193   inv.ResizeTo(R);
0194   double *const invk = inv.GetMatrixArray();
0195   int nRows = inv.GetNrows();
0196   for (int i = 0; i < nRows; ++i)
0197     for (int j = 0; j < nRows; ++j)
0198       invk[i*nRows + j] = (i == j);
0199 
0200   for (int i = 0; i < inv.GetNcols(); ++i)
0201     result = result && transposedForwardSubstitution(R, inv, i);
0202 
0203   return result;
0204 }
0205 
0206 // This replaces A with an upper right matrix connected to A by a
0207 // orthogonal transformation.  I.e., it computes the R from a QR
0208 // decomposition of A replacing A.
0209 void tools::QR(TMatrixD& A)
0210 {
0211   int nCols = A.GetNcols();
0212   int nRows = A.GetNrows();
0213   assert(nRows >= nCols);
0214   // This uses Businger and Golub's algorithm from Handbook for
0215   // Automatical Computation, Vol. 2, Chapter 8, but without
0216   // pivoting.  I.e., we stop at the middle of page 112.  We don't
0217   // explicitly calculate the orthogonal matrix.
0218 
0219   double *const ak = A.GetMatrixArray();
0220   // No variable-length arrays in C++, alloca does the exact same thing ...
0221   double *const u = (double *)alloca(sizeof(double)*nRows);
0222 
0223   // Main loop over matrix columns.
0224   for (int k = 0; k < nCols; ++k) {
0225     double akk = ak[k*nCols + k];
0226 
0227     double sum = akk*akk;
0228     // Put together a housholder transformation.
0229     for (int i = k + 1; i < nRows; ++i) {
0230       sum += ak[i*nCols + k]*ak[i*nCols + k];
0231       u[i] = ak[i*nCols + k];
0232     }
0233     double sigma = sqrt(sum);
0234     double beta = 1/(sum + sigma*fabs(akk));
0235     // The algorithm uses only the uk[i] for i >= k.
0236     u[k] = copysign(sigma + fabs(akk), akk);
0237 
0238     // Calculate y (again taking into account zero entries).  This
0239     // encodes how the (sub)matrix changes by the householder transformation.
0240     for (int i = k; i < nCols; ++i) {
0241       double y = 0;
0242       for (int j = k; j < nRows; ++j)
0243     y += u[j]*ak[j*nCols + i];
0244       y *= beta;
0245       // ... and apply the changes.
0246       for (int j = k; j < nRows; ++j)
0247     ak[j*nCols + i] -= u[j]*y; //y[j];
0248     }
0249   }
0250 
0251   // Zero below diagonal
0252   for (int i = 1; i < nCols; ++i)
0253     for (int j = 0; j < i; ++j)
0254       ak[i*nCols + j] = 0.;
0255   for (int i = nCols; i < nRows; ++i)
0256     for (int j = 0; j < nCols; ++j)
0257       ak[i*nCols + j] = 0.;
0258 }
0259 
0260 // This replaces A with an upper right matrix connected to A by a
0261 // orthogonal transformation.  I.e., it computes the R from a QR
0262 // decomposition of A replacing A.  Simultaneously it transforms b by
0263 // the inverse orthogonal transformation.
0264 // 
0265 // The purpose is this: the least-squared problem
0266 //   ||Ax - b|| = min
0267 // is equivalent to
0268 //   ||QRx - b|| = ||Rx - Q'b|| = min
0269 // where Q' denotes the transposed (i.e. inverse).
0270 void tools::QR(TMatrixD& A, TVectorD& b)
0271 {
0272   int nCols = A.GetNcols();
0273   int nRows = A.GetNrows();
0274   assert(nRows >= nCols);
0275   assert(b.GetNrows() == nRows);
0276   // This uses Businger and Golub's algorithm from Handbook for
0277   // Automatic Computation, Vol. 2, Chapter 8, but without pivoting.
0278   // I.e., we stop at the middle of page 112.  We don't explicitly
0279   // calculate the orthogonal matrix, but Q'b which is not done
0280   // explicitly in Businger et al.
0281   // Also in Numer. Math. 7, 269-276 (1965)
0282 
0283   double *const ak = A.GetMatrixArray();
0284   double *const bk = b.GetMatrixArray();
0285   // No variable-length arrays in C++, alloca does the exact same thing ...
0286   //double * u = (double *)alloca(sizeof(double)*nRows);
0287   double u[500];
0288 
0289   // Main loop over matrix columns.
0290   for (int k = 0; k < nCols; ++k) {
0291     double akk = ak[k*nCols + k];
0292 
0293     double sum = akk*akk;
0294     // Put together a housholder transformation.
0295     for (int i = k + 1; i < nRows; ++i) {
0296       sum += ak[i*nCols + k]*ak[i*nCols + k];
0297       u[i] = ak[i*nCols + k];
0298     }
0299     double sigma = sqrt(sum);
0300     double beta = 1/(sum + sigma*fabs(akk));
0301     // The algorithm uses only the uk[i] for i >= k.
0302     u[k] = copysign(sigma + fabs(akk), akk);
0303 
0304     // Calculate b (again taking into account zero entries).  This
0305     // encodes how the (sub)vector changes by the householder transformation.
0306     double yb = 0;
0307     for (int j = k; j < nRows; ++j)
0308       yb += u[j]*bk[j];
0309     yb *= beta;
0310     // ... and apply the changes.
0311     for (int j = k; j < nRows; ++j)
0312       bk[j] -= u[j]*yb;
0313 
0314     // Calculate y (again taking into account zero entries).  This
0315     // encodes how the (sub)matrix changes by the householder transformation.
0316     for (int i = k; i < nCols; ++i) {
0317       double y = 0;
0318       for (int j = k; j < nRows; ++j)
0319     y += u[j]*ak[j*nCols + i];
0320       y *= beta;
0321       // ... and apply the changes.
0322       for (int j = k; j < nRows; ++j)
0323     ak[j*nCols + i] -= u[j]*y;
0324     }
0325   }
0326 
0327   // Zero below diagonal
0328   for (int i = 1; i < nCols; ++i)
0329     for (int j = 0; j < i; ++j)
0330       ak[i*nCols + j] = 0.;
0331   for (int i = nCols; i < nRows; ++i)
0332     for (int j = 0; j < nCols; ++j)
0333       ak[i*nCols + j] = 0.;
0334 }
0335 
0336 
0337 void
0338 tools::noiseMatrixSqrt(const TMatrixDSym& noise,
0339                TMatrixD& noiseSqrt)
0340 {
0341   // This is the slowest part of the whole Sqrt Kalman.  Using an LDLt
0342   // transform is probably the easiest way of remedying this.
0343   TMatrixDSymEigen eig(noise);
0344   noiseSqrt.ResizeTo(noise);
0345   noiseSqrt = eig.GetEigenVectors();
0346   double* pNoiseSqrt = noiseSqrt.GetMatrixArray();
0347   const TVectorD& evs(eig.GetEigenValues());
0348   const double* pEvs = evs.GetMatrixArray();
0349   // GetEigenVectors is such that noise = noiseSqrt * evs * noiseSqrt'
0350   // We're evaluating the first product with the eigenvalues replaced
0351   // by their square roots, so we're multiplying with a diagonal
0352   // matrix from the right.
0353   int iCol = 0;
0354   for (; iCol < noiseSqrt.GetNrows(); ++iCol) {
0355     double ev = pEvs[iCol] > 0 ? sqrt(pEvs[iCol]) : 0;
0356     // if (ev == 0)
0357     //  break;
0358     for (int j = 0; j < noiseSqrt.GetNrows(); ++j) {
0359       pNoiseSqrt[j*noiseSqrt.GetNcols() + iCol] *= ev;
0360     }
0361   }
0362   if (iCol < noiseSqrt.GetNcols()) {
0363     // Hit zero eigenvalue, resize matrix
0364     noiseSqrt.ResizeTo(noiseSqrt.GetNrows(), iCol);
0365   }
0366 
0367   // noiseSqrt * noiseSqrt' = noise
0368 }
0369 
0370 
0371 // Transports the square root of the covariance matrix using a
0372 // square-root formalism
0373 //
0374 // With covariance square root S, transport matrix F and noise matrix
0375 // square root Q.
0376 void
0377 tools::kalmanPredictionCovSqrt(const TMatrixD& S,
0378                    const TMatrixD& F, const TMatrixD& Q,
0379                    TMatrixD& Snew)
0380 {
0381   Snew.ResizeTo(S.GetNrows() + Q.GetNcols(),
0382         S.GetNcols());
0383 
0384   // This overwrites all elements, no precautions necessary
0385   Snew.SetSub(0, 0, TMatrixD(S, TMatrixD::kMultTranspose, F));
0386   if (Q.GetNcols() != 0)
0387     Snew.SetSub(S.GetNrows(), 0, TMatrixD(TMatrixD::kTransposed, Q));
0388 
0389   tools::QR(Snew);
0390 
0391   // The result is in the upper right corner of the matrix.
0392   Snew.ResizeTo(S.GetNrows(), S.GetNrows());
0393 }
0394 
0395 
0396 // Kalman measurement update (no transport)
0397 // x, S : state prediction, covariance square root
0398 // res, R, H : residual, measurement covariance square root, H matrix of the measurement
0399 // gives the update (new state = x + update) and the updated covariance square root.
0400 // S and Snew are allowed to refer to the same object.
0401 void
0402 tools::kalmanUpdateSqrt(const TMatrixD& S,
0403             const TVectorD& res, const TMatrixD& R,
0404             const AbsHMatrix* H,
0405             TVectorD& update, TMatrixD& SNew)
0406 {
0407   TMatrixD pre(S.GetNrows() + R.GetNrows(),
0408            S.GetNcols() + R.GetNcols());
0409   pre.SetSub(0,            0,         R); /* Zeros in upper right block */
0410   pre.SetSub(R.GetNrows(), 0, H->MHt(S)); pre.SetSub(R.GetNrows(), R.GetNcols(), S);
0411 
0412   tools::QR(pre);
0413   const TMatrixD& r = pre;
0414 
0415   const TMatrixD& a(r.GetSub(0, R.GetNrows()-1,
0416                  0, R.GetNcols()-1));
0417   TMatrixD K(TMatrixD::kTransposed, r.GetSub(0, R.GetNrows()-1, R.GetNcols(), pre.GetNcols()-1));
0418   SNew = r.GetSub(R.GetNrows(), pre.GetNrows()-1, R.GetNcols(), pre.GetNcols()-1);
0419 
0420   update.ResizeTo(res);
0421   update = res;
0422   tools::transposedForwardSubstitution(a, update);
0423   update *= K;
0424 }
0425 
0426 } /* End of namespace genfit */