SND@LHC Software
Loading...
Searching...
No Matches
Tools.cc
Go to the documentation of this file.
1/* Copyright 2008-2010, Technische Universitaet Muenchen,
2 Authors: Christian Hoeppner & Sebastian Neubert & Johannes Rauch
3
4 This file is part of GENFIT.
5
6 GENFIT is free software: you can redistribute it and/or modify
7 it under the terms of the GNU Lesser General Public License as published
8 by the Free Software Foundation, either version 3 of the License, or
9 (at your option) any later version.
10
11 GENFIT is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with GENFIT. If not, see <http://www.gnu.org/licenses/>.
18*/
19
20#include "Tools.h"
21
22#include <cmath>
23#include <memory>
24#include <typeinfo>
25#include <cassert>
26
27#include <TDecompChol.h>
28#include <TMatrixTSymCramerInv.h>
29#include <TMath.h>
30
31#include "Exception.h"
32
33// Use Cramer inversion for small matrices?
34static const bool useCramer = false;
35
36namespace genfit {
37
38void tools::invertMatrix(const TMatrixDSym& mat, TMatrixDSym& inv, double* determinant){
39 inv.ResizeTo(mat);
40
41 // check if numerical limits are reached (i.e at least one entry < 1E-100 and/or at least one entry > 1E100)
42 if (!(mat<1.E100) || !(mat>-1.E100)){
43 Exception e("Tools::invertMatrix() - cannot invert matrix, entries too big (>1e100)",
44 __LINE__,__FILE__);
45 e.setFatal();
46 throw e;
47 }
48 // do the trivial inversions for 1x1 and 2x2 matrices manually
49 if (mat.GetNrows() == 1){
50 if (determinant != NULL) *determinant = mat(0,0);
51 inv(0,0) = 1./mat(0,0);
52 return;
53 }
54
55 if (mat.GetNrows() == 2){
56 double det = mat(0,0)*mat(1,1) - mat(1,0)*mat(1,0);
57 if (determinant != NULL) *determinant = det;
58 if(fabs(det) < 1E-50){
59 Exception e("Tools::invertMatrix() - cannot invert matrix , determinant = 0",
60 __LINE__,__FILE__);
61 e.setFatal();
62 throw e;
63 }
64 det = 1./det;
65 inv(0,0) = det * mat(1,1);
66 inv(0,1) = inv(1,0) = -det * mat(1,0);
67 inv(1,1) = det * mat(0,0);
68 return;
69 }
70
71
72 if (useCramer && mat.GetNrows() <= 6){
73 Bool_t (*inversion)(TMatrixDSym&, Double_t*) = 0;
74 inv.ResizeTo(mat);
75 inv = mat;
76 switch (mat.GetNrows()) {
77 case 3:
78 inversion = TMatrixTSymCramerInv::Inv3x3; break;
79 case 4:
80 inversion = TMatrixTSymCramerInv::Inv4x4; break;
81 case 5:
82 inversion = TMatrixTSymCramerInv::Inv5x5; break;
83 case 6:
84 inversion = TMatrixTSymCramerInv::Inv6x6; break;
85 }
86
87 Bool_t success = inversion(inv, determinant);
88 if (!success){
89 Exception e("Tools::invertMatrix() - cannot invert matrix, determinant = 0",
90 __LINE__,__FILE__);
91 e.setFatal();
92 throw e;
93 }
94 return;
95 }
96
97 // else use TDecompChol
98 bool status = 0;
99 TDecompChol invertAlgo(mat, 1E-50);
100
101 status = invertAlgo.Invert(inv);
102 if(status == 0){
103 Exception e("Tools::invertMatrix() - cannot invert matrix, status = 0",
104 __LINE__,__FILE__);
105 e.setFatal();
106 throw e;
107 }
108
109 if (determinant != NULL) {
110 double d1, d2;
111 invertAlgo.Det(d1, d2);
112 *determinant = ldexp(d1, d2);
113 }
114}
115
116void tools::invertMatrix(TMatrixDSym& mat, double* determinant){
117 // check if numerical limits are reached (i.e at least one entry < 1E-100 and/or at least one entry > 1E100)
118 if (!(mat<1.E100) || !(mat>-1.E100)){
119 Exception e("Tools::invertMatrix() - cannot invert matrix, entries too big (>1e100)",
120 __LINE__,__FILE__);
121 e.setFatal();
122 throw e;
123 }
124 // do the trivial inversions for 1x1 and 2x2 matrices manually
125 if (mat.GetNrows() == 1){
126 if (determinant != NULL) *determinant = mat(0,0);
127 mat(0,0) = 1./mat(0,0);
128 return;
129 }
130
131 if (mat.GetNrows() == 2){
132 double *arr = mat.GetMatrixArray();
133 double det = arr[0]*arr[3] - arr[1]*arr[1];
134 if (determinant != NULL) *determinant = det;
135 if(fabs(det) < 1E-50){
136 Exception e("Tools::invertMatrix() - cannot invert matrix, determinant = 0",
137 __LINE__,__FILE__);
138 e.setFatal();
139 throw e;
140 }
141 det = 1./det;
142 double temp[3];
143 temp[0] = det * arr[3];
144 temp[1] = -det * arr[1];
145 temp[2] = det * arr[0];
146 //double *arr = mat.GetMatrixArray();
147 arr[0] = temp[0];
148 arr[1] = arr[2] = temp[1];
149 arr[3] = temp[2];
150 return;
151 }
152
153 if (useCramer && mat.GetNrows() <= 6){
154 Bool_t (*inversion)(TMatrixDSym&, Double_t*) = 0;
155 switch (mat.GetNrows()) {
156 case 3:
157 inversion = TMatrixTSymCramerInv::Inv3x3; break;
158 case 4:
159 inversion = TMatrixTSymCramerInv::Inv4x4; break;
160 case 5:
161 inversion = TMatrixTSymCramerInv::Inv5x5; break;
162 case 6:
163 inversion = TMatrixTSymCramerInv::Inv6x6; break;
164 }
165
166 Bool_t success = inversion(mat, determinant);
167 if (!success){
168 Exception e("Tools::invertMatrix() - cannot invert matrix, determinant = 0",
169 __LINE__,__FILE__);
170 e.setFatal();
171 throw e;
172 }
173 return;
174 }
175
176 // else use TDecompChol
177 bool status = 0;
178 TDecompChol invertAlgo(mat, 1E-50);
179
180 status = invertAlgo.Invert(mat);
181 if(status == 0){
182 Exception e("Tools::invertMatrix() - cannot invert matrix, status = 0",
183 __LINE__,__FILE__);
184 e.setFatal();
185 throw e;
186 }
187
188 if (determinant != NULL) {
189 double d1, d2;
190 invertAlgo.Det(d1, d2);
191 *determinant = ldexp(d1, d2);
192 }
193}
194
195
196// Solves R^T x = b, replaces b with the result x. R is assumed
197// to be upper-diagonal. This is forward substitution, but with
198// indices flipped.
199bool tools::transposedForwardSubstitution(const TMatrixD& R, TVectorD& b)
200{
201 size_t n = R.GetNrows();
202 for (unsigned int i = 0; i < n; ++i) {
203 double sum = b(i);
204 for (unsigned int j = 0; j < i; ++j) {
205 sum -= b(j)*R(j,i); // already replaced previous elements in b.
206 }
207 if (R(i,i) == 0)
208 return false;
209 b(i) = sum / R(i,i);
210 }
211 return true;
212}
213
214// Same, but for one column of the matrix b. Used by transposedInvert below
215bool tools::transposedForwardSubstitution(const TMatrixD& R, TMatrixD& b, int nCol)
216{
217 size_t n = R.GetNrows();
218 for (unsigned int i = 0; i < n; ++i) {
219 double sum = b(i, nCol);
220 for (unsigned int j = 0; j < i; ++j) {
221 sum -= b(j, nCol)*R(j,i); // already replaced previous elements in b.
222 }
223 if (R(i,i) == 0)
224 return false;
225 b(i, nCol) = sum / R(i,i);
226 }
227 return true;
228}
229
230// inv will be the inverse of the transposed of the upper-right matrix R
231bool tools::transposedInvert(const TMatrixD& R, TMatrixD& inv)
232{
233 bool result = true;
234
235 inv.ResizeTo(R);
236 for (int i = 0; i < inv.GetNrows(); ++i)
237 for (int j = 0; j < inv.GetNcols(); ++j)
238 inv(i, j) = (i == j);
239
240 for (int i = 0; i < inv.GetNcols(); ++i)
241 result = result && transposedForwardSubstitution(R, inv, i);
242
243 return result;
244}
245
246// This replaces A with an upper right matrix connected to A by a
247// orthogonal transformation. I.e., it computes the R from a QR
248// decomposition of A replacing A.
249void tools::QR(TMatrixD& A)
250{
251 int nCols = A.GetNcols();
252 int nRows = A.GetNrows();
253 assert(nRows >= nCols);
254 // This uses Businger and Golub's algorithm from Handbook for
255 // Automatical Computation, Vol. 2, Chapter 8, but without
256 // pivoting. I.e., we stop at the middle of page 112. We don't
257 // explicitly calculate the orthogonal matrix.
258
259 double *const ak = A.GetMatrixArray();
260 // No variable-length arrays in C++, alloca does the exact same thing ...
261 double *const u = (double *)alloca(sizeof(double)*nRows);
262
263 // Main loop over matrix columns.
264 for (int k = 0; k < nCols; ++k) {
265 double akk = ak[k*nCols + k];
266
267 double sum = akk*akk;
268 // Put together a housholder transformation.
269 for (int i = k + 1; i < nRows; ++i) {
270 sum += ak[i*nCols + k]*ak[i*nCols + k];
271 u[i] = ak[i*nCols + k];
272 }
273 double sigma = sqrt(sum);
274 double beta = 1/(sum + sigma*fabs(akk));
275 // The algorithm uses only the uk[i] for i >= k.
276 u[k] = copysign(sigma + fabs(akk), akk);
277
278 // Calculate y (again taking into account zero entries). This
279 // encodes how the (sub)matrix changes by the householder transformation.
280 for (int i = k; i < nCols; ++i) {
281 double y = 0;
282 for (int j = k; j < nRows; ++j)
283 y += u[j]*ak[j*nCols + i];
284 y *= beta;
285 // ... and apply the changes.
286 for (int j = k; j < nRows; ++j)
287 ak[j*nCols + i] -= u[j]*y; //y[j];
288 }
289 }
290
291 // Zero below diagonal
292 for (int i = 1; i < nCols; ++i)
293 for (int j = 0; j < i; ++j)
294 ak[i*nCols + j] = 0.;
295 for (int i = nCols; i < nRows; ++i)
296 for (int j = 0; j < nCols; ++j)
297 ak[i*nCols + j] = 0.;
298}
299
300// This averages the covariance matrices C1, C2 in a numerically
301// stable way by using matrix square roots. No optimizations
302// performed, so use with care.
303void tools::safeAverage(const TMatrixDSym& C1, const TMatrixDSym& C2,
304 TMatrixDSym& result)
305{
306 /*
307 The algorithm proceeds as follows:
308 write C1 = S1 S1' (prime for transpose),
309 C2 = S2 S2'
310 Then the inverse of the average can be written as ("." for matrix
311 multiplication)
312 C^-1 = ((S1'^-1, S2'^-1) . (S1'^-1) )
313 ( (S2'^-1) )
314 Inserting an orthogonal matrix T in the middle:
315 C^-1 = ((S1'^-1, S2'^-1) . T . T' . (S1'^-1) )
316 ( (S2'^-1) )
317 doesn't change this because T.T' = 1.
318 Now choose T s.t. T'.(S1'^-1, S2'^-1)' is an upper right matrix. We
319 use Tools::QR for the purpose, as we don't actually need T.
320
321 Then the inverse needed to obtain the covariance matrix can be
322 obtained by inverting the upper right matrix, which is squared to
323 obtained the new covariance matrix. */
324 TDecompChol dec1(C1);
325 dec1.Decompose();
326 TDecompChol dec2(C2);
327 dec2.Decompose();
328
329 const TMatrixD& S1 = dec1.GetU();
330 const TMatrixD& S2 = dec2.GetU();
331
332 TMatrixD S1inv, S2inv;
333 transposedInvert(S1, S1inv);
334 transposedInvert(S2, S2inv);
335
336 TMatrixD A(2 * S1.GetNrows(), S1.GetNcols());
337 for (int i = 0; i < S1.GetNrows(); ++i) {
338 for (int j = 0; j < S2.GetNcols(); ++j) {
339 A(i, j) = S1inv(i, j);
340 A(i + S1.GetNrows(), j) = S2inv(i, j);
341 }
342 }
343
344 QR(A);
345 A.ResizeTo(S1.GetNrows(), S1.GetNrows());
346
347 TMatrixD inv;
348 transposedInvert(A, inv);
349
350 result.ResizeTo(inv.GetNcols(), inv.GetNcols());
351 result = TMatrixDSym(TMatrixDSym::kAtA, inv);
352}
353} /* End of namespace genfit */
Exception class for error handling in GENFIT (provides storage for diagnostic information)
Definition Exception.h:48
void setFatal(bool b=true)
Set fatal flag.
Definition Exception.h:61
void QR(TMatrixD &A)
Replaces A with an upper right matrix connected to A by an orthongonal transformation....
Definition Tools.cc:249
bool transposedForwardSubstitution(const TMatrixD &R, TVectorD &b)
Solves R^t x = b, replacing b with the solution for x. R is assumed to be upper diagonal.
Definition Tools.cc:199
bool transposedInvert(const TMatrixD &R, TMatrixD &inv)
Inverts the transpose of the upper right matrix R into inv.
Definition Tools.cc:231
void invertMatrix(const TMatrixDSym &mat, TMatrixDSym &inv, double *determinant=NULL)
Invert a matrix, throwing an Exception when inversion fails. Optional calculation of determinant.
Definition Tools.cc:38
void safeAverage(const TMatrixDSym &C1, const TMatrixDSym &C2, TMatrixDSym &result)
This averages the covariance matrices C1, C2 in a numerically stable way by using matrix square roots...
Definition Tools.cc:303
Matrix inversion tools.
Definition AbsBField.h:29