Belle II Software  release-08-01-10
DAF.cc
1 /* Copyright 2008-2013, Technische Universitaet Muenchen, Ludwig-Maximilians-Universität München
2  Authors: Christian Hoeppner & Sebastian Neubert & Johannes Rauch & Tobias Schlüter
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 "DAF.h"
21 #include "Exception.h"
22 #include "IO.h"
23 #include "KalmanFitterInfo.h"
24 #include "KalmanFitter.h"
25 #include "KalmanFitterRefTrack.h"
26 #include "KalmanFitStatus.h"
27 #include "Tools.h"
28 #include "Track.h"
29 #include "TrackPoint.h"
30 
31 #include <assert.h>
32 #include <cmath>
33 
34 //root stuff
35 #include <TBuffer.h>
36 #include <TMath.h>
37 #include <Math/QuantFuncMathCore.h>
38 
39 
40 
41 namespace genfit {
42 
43  DAF::DAF(std::tuple<double, double, int> annealingScheme, int minIter, int maxIter, int minIterForPval, bool useRefKalman,
44  double deltaPval, double deltaWeight, double probCut)
45  : AbsKalmanFitter(10, deltaPval), deltaWeight_(deltaWeight), minIterForPval_(minIterForPval)
46  {
47  if (useRefKalman) {
48  kalman_.reset(new KalmanFitterRefTrack());
49  static_cast<KalmanFitterRefTrack*>(kalman_.get())->setRefitAll();
50  } else
51  kalman_.reset(new KalmanFitter());
52 
53  kalman_->setMultipleMeasurementHandling(weightedAverage);
54  kalman_->setMaxIterations(1);
55 
56  setAnnealingScheme(std::get<0>(annealingScheme),
57  std::get<1>(annealingScheme),
58  std::get<2>(annealingScheme),
59  minIter, maxIter); // also sets maxIterations_
60  setProbCut(probCut);
61  }
62 
63  DAF::DAF(bool useRefKalman, double deltaPval, double deltaWeight)
64  : AbsKalmanFitter(10, deltaPval), deltaWeight_(deltaWeight)
65  {
66  if (useRefKalman) {
67  kalman_.reset(new KalmanFitterRefTrack());
68  static_cast<KalmanFitterRefTrack*>(kalman_.get())->setRefitAll();
69  } else
70  kalman_.reset(new KalmanFitter());
71 
72  kalman_->setMultipleMeasurementHandling(weightedAverage);
73  kalman_->setMaxIterations(1);
74 
75  setAnnealingScheme(100, 0.1, 5); // also sets maxIterations_
76  setProbCut(0.001);
77  }
78 
79  DAF::DAF(AbsKalmanFitter* kalman, double deltaPval, double deltaWeight)
80  : AbsKalmanFitter(10, deltaPval), deltaWeight_(deltaWeight)
81  {
82  kalman_.reset(kalman);
83  kalman_->setMultipleMeasurementHandling(weightedAverage); // DAF makes no sense otherwise
84  kalman_->setMaxIterations(1);
85 
86  if (dynamic_cast<KalmanFitterRefTrack*>(kalman_.get()) != nullptr) {
87  static_cast<KalmanFitterRefTrack*>(kalman_.get())->setRefitAll();
88  }
89 
90  setAnnealingScheme(100, 0.1, 5); // also sets maxIterations_
91  setProbCut(0.01);
92  }
93 
94 
95  void DAF::processTrackWithRep(Track* tr, const AbsTrackRep* rep, bool resortHits)
96  {
97 
98  if (debugLvl_ > 0) {
99  debugOut << "DAF::processTrack //////////////////////////////////////////////////////////////// \n";
100  }
101 
102  KalmanFitStatus* status = 0;
103  bool oneLastIter = false;
104 
105  double lastPval = -1;
106 
107  for (unsigned int iBeta = 0;; ++iBeta) {
108 
109  if (debugLvl_ > 0) {
110  debugOut << "DAF::processTrack, trackRep " << rep << ", iteration " << iBeta + 1 << ", beta = " << betas_.at(iBeta) << "\n";
111  }
112 
113  kalman_->processTrackWithRep(tr, rep, resortHits);
114 
115  status = static_cast<KalmanFitStatus*>(tr->getFitStatus(rep));
116  status->setIsFittedWithDaf();
117  status->setNumIterations(iBeta + 1);
118 
119 
120  // check break conditions
121 
122  if (! status->isFitted()) {
123  if (debugLvl_ > 0) {
124  debugOut << "DAF::Kalman could not fit!\n";
125  }
126  status->setIsFitted(false);
127  break;
128  }
129 
130  if (oneLastIter == true) {
131  if (debugLvl_ > 0) {
132  debugOut << "DAF::break after one last iteration\n";
133  }
134  status->setIsFitConvergedFully(status->getNFailedPoints() == 0);
135  status->setIsFitConvergedPartially();
136  break;
137  }
138 
139  if (iBeta >= maxIterations_ - 1) {
140  status->setIsFitConvergedFully(false);
141  status->setIsFitConvergedPartially(false);
142  if (debugLvl_ > 0) {
143  debugOut << "DAF::number of max iterations reached!\n";
144  }
145  break;
146  }
147 
148 
149  // get and update weights
150  bool converged(false);
151  try {
152  converged = calcWeights(tr, rep, betas_.at(iBeta));
153  if (!converged && iBeta >= minIterForPval_ - 1 &&
154  status->getBackwardPVal() > 1e-10 && lastPval > 1e-10 && fabs(lastPval - status->getBackwardPVal()) < this->deltaPval_) {
155  if (debugLvl_ > 0) {
156  debugOut << "converged by Pval = " << status->getBackwardPVal() << " even though weights changed at iBeta = " << iBeta << std::endl;
157  }
158  converged = true;
159  }
160  lastPval = status->getBackwardPVal();
161  } catch (Exception& e) {
162  errorOut << e.what();
163  e.info();
164  //errorOut << "calc weights failed" << std::endl;
165  //mini_trk->getTrackRep(0)->setStatusFlag(1);
166  status->setIsFitted(false);
167  status->setIsFitConvergedFully(false);
168  status->setIsFitConvergedPartially(false);
169  break;
170  }
171 
172  // check if converged
173  if (iBeta >= minIterations_ - 1 && converged) {
174  if (debugLvl_ > 0) {
175  debugOut << "DAF::convergence reached in iteration " << iBeta + 1 << " -> Do one last iteration with updated weights.\n";
176  }
177  oneLastIter = true;
178  status->setIsFitConvergedFully(status->getNFailedPoints() == 0);
179  status->setIsFitConvergedPartially();
180  }
181 
182  } // end loop over betas
183 
184 
185  if (status->getForwardPVal() == 0. &&
186  status->getBackwardPVal() == 0.) {
187  status->setIsFitConvergedFully(false);
188  status->setIsFitConvergedPartially(false);
189  }
190 
191  }
192 
193 
194  void DAF::setProbCut(const double prob_cut)
195  {
196  for (int i = 1; i < 7; ++i) {
197  addProbCut(prob_cut, i);
198  }
199  }
200 
201  void DAF::addProbCut(const double prob_cut, const int measDim)
202  {
203  if (prob_cut > 1.0 || prob_cut < 0.0) {
204  Exception exc("DAF::addProbCut prob_cut is not between 0 and 1", __LINE__, __FILE__);
205  exc.setFatal();
206  throw exc;
207  }
208  if (measDim < 1 || measDim > 6) {
209  Exception exc("DAF::addProbCut measDim must be in the range [1,6]", __LINE__, __FILE__);
210  exc.setFatal();
211  throw exc;
212  }
213  chi2Cuts_[measDim] = ROOT::Math::chisquared_quantile_c(prob_cut, measDim);
214  }
215 
216  void DAF::setAnnealingScheme(double bStart, double bFinal, unsigned int nSteps)
217  {
218  // The betas are calculated as a geometric series that takes nSteps
219  // steps to go from bStart to bFinal.
220  assert(bStart > bFinal);
221  assert(bFinal > 1.E-10);
222  assert(nSteps > 1);
223 
224  minIterations_ = nSteps;
225  maxIterations_ = nSteps + 4;
226  minIterForPval_ = nSteps;
227 
228  betas_.clear();
229 
230  for (unsigned int i = 0; i < nSteps; ++i) {
231  betas_.push_back(bStart * pow(bFinal / bStart, i / (nSteps - 1.)));
232  }
233 
234  betas_.resize(maxIterations_,
235  betas_.back()); //make sure main loop has a maximum of 10 iterations and also make sure the last beta value is used for if more iterations are needed then the ones set by the user.
236 
237  }
238 
239  void DAF::setAnnealingScheme(double bStart, double bFinal, unsigned int nSteps, unsigned int minIter, unsigned int maxIter)
240  {
241  // The betas are calculated as a geometric series that takes nSteps
242  // steps to go from bStart to bFinal.
243  assert(bStart > bFinal);
244  assert(bFinal > 1.E-10);
245  assert(nSteps > 1);
246 
247  minIterations_ = minIter;
248  maxIterations_ = maxIter;
249 
250  betas_.clear();
251 
252  for (unsigned int i = 0; i < nSteps; ++i) {
253  betas_.push_back(bStart * pow(bFinal / bStart, i / (nSteps - 1.)));
254  }
255 
256  betas_.resize(maxIterations_,
257  betas_.back()); //make sure main loop has a maximum of 10 iterations and also make sure the last beta value is used for if more iterations are needed then the ones set by the user.
258 
259  }
260 
261 
262  bool DAF::calcWeights(Track* tr, const AbsTrackRep* rep, double beta)
263  {
264 
265  if (debugLvl_ > 0) {
266  debugOut << "DAF::calcWeights \n";
267  }
268 
269  bool converged(true);
270  double maxAbsChange(0);
271 
272  const std::vector< TrackPoint* >& trackPoints = tr->getPointsWithMeasurement();
273  for (std::vector< TrackPoint* >::const_iterator tp = trackPoints.begin(); tp != trackPoints.end(); ++tp) {
274  if (!(*tp)->hasFitterInfo(rep)) {
275  continue;
276  }
277  AbsFitterInfo* fi = (*tp)->getFitterInfo(rep);
278  if (dynamic_cast<KalmanFitterInfo*>(fi) == nullptr) {
279  Exception exc("DAF::getWeights ==> can only use KalmanFitterInfos", __LINE__, __FILE__);
280  throw exc;
281  }
282  KalmanFitterInfo* kfi = static_cast<KalmanFitterInfo*>(fi);
283 
284  if (kfi->areWeightsFixed()) {
285  if (debugLvl_ > 0) {
286  debugOut << "weights are fixed, continue \n";
287  }
288  continue;
289  }
290 
291  unsigned int nMeas = kfi->getNumMeasurements();
292 
293  std::vector<double> phi(nMeas, 0.);
294  double phi_sum = 0;
295  double phi_cut = 0;
296  for (unsigned int j = 0; j < nMeas; j++) {
297 
298  try {
299  const MeasurementOnPlane& residual = kfi->getResidual(j, true, true);
300  const TVectorD& resid(residual.getState());
301  TMatrixDSym Vinv(residual.getCov());
302  double detV;
303  tools::invertMatrix(Vinv, &detV); // can throw an Exception
304  int hitDim = resid.GetNrows();
305  // Needed for normalization, special cases for the two common cases,
306  // shouldn't matter, but the original code made some efforts to make
307  // this calculation faster, and it's not complex ...
308  double twoPiN = 2.*M_PI;
309  if (hitDim == 2)
310  twoPiN *= twoPiN;
311  else if (hitDim > 2)
312  twoPiN = pow(twoPiN, hitDim);
313 
314  double chi2 = Vinv.Similarity(resid);
315  if (debugLvl_ > 1) {
316  debugOut << "chi2 = " << chi2 << "\n";
317  }
318 
319  // The common factor beta is eliminated.
320  double norm = 1. / sqrt(twoPiN * detV);
321 
322  phi[j] = norm * exp(-0.5 * chi2 / beta);
323  phi_sum += phi[j];
324  //errorOut << "hitDim " << hitDim << " fchi2Cuts[hitDim] " << fchi2Cuts[hitDim] << std::endl;
325  double cutVal = chi2Cuts_[hitDim];
326  assert(cutVal > 1.E-6);
327  //the following assumes that in the competing hits could have different V otherwise calculation could be simplified
328  phi_cut += norm * exp(-0.5 * cutVal / beta);
329  } catch (Exception& e) {
330  errorOut << e.what();
331  e.info();
332  }
333  }
334 
335  for (unsigned int j = 0; j < nMeas; j++) {
336  double weight = phi[j] / (phi_sum + phi_cut);
337  //debugOut << phi_sum << " " << phi_cut << " " << weight << std::endl;
338 
339  // check convergence
340  double absChange(fabs(weight - kfi->getMeasurementOnPlane(j)->getWeight()));
341  if (converged && absChange > deltaWeight_) {
342  converged = false;
343  if (absChange > maxAbsChange)
344  maxAbsChange = absChange;
345  }
346 
347  if (debugLvl_ > 0) {
348  if (debugLvl_ > 1 || absChange > deltaWeight_) {
349  debugOut << "\t old weight: " << kfi->getMeasurementOnPlane(j)->getWeight();
350  debugOut << "\t new weight: " << weight;
351  }
352  }
353 
354  kfi->getMeasurementOnPlane(j)->setWeight(weight);
355  }
356  }
357 
358  if (debugLvl_ > 0) {
359  debugOut << "\t ";
360  debugOut << "max abs weight change = " << maxAbsChange << "\n";
361  }
362 
363  return converged;
364  }
365 
366 
367 // Customized from generated Streamer.
368  void DAF::Streamer(TBuffer& R__b)
369  {
370  // Stream an object of class genfit::DAF.
371 
372  //This works around a msvc bug and should be harmless on other platforms
373  typedef ::genfit::DAF thisClass;
374  UInt_t R__s, R__c;
375  if (R__b.IsReading()) {
376  Version_t R__v = R__b.ReadVersion(&R__s, &R__c);
377  if (R__v) { }
378  //This works around a msvc bug and should be harmless on other platforms
379  typedef genfit::AbsKalmanFitter baseClass0;
380  baseClass0::Streamer(R__b);
381  R__b >> deltaWeight_;
382  // weights_ are only of intermediate use -> not saved
383  {
384  std::vector<double>& R__stl = betas_;
385  R__stl.clear();
386  int R__i, R__n;
387  R__b >> R__n;
388  R__stl.reserve(R__n);
389  for (R__i = 0; R__i < R__n; R__i++) {
390  double R__t;
391  R__b >> R__t;
392  R__stl.push_back(R__t);
393  }
394  }
395  if (R__v == 1) {
396  // Old versions kept the chi2Cuts_ in a map.
397  // We ignore non-sensical dimensionalities when reading it again.
398  int R__i, R__n;
399  R__b >> R__n;
400  for (R__i = 0; R__i < R__n; R__i++) {
401  memset(chi2Cuts_, 0, sizeof(chi2Cuts_));
402  int R__t;
403  R__b >> R__t;
404  double R__t2;
405  R__b >> R__t2;
406  if (R__t >= 1 && R__t <= 6)
407  chi2Cuts_[R__t] = R__t2;
408  }
409  } else {
410  char n_chi2Cuts; // should be six
411  R__b >> n_chi2Cuts;
412  assert(n_chi2Cuts == 6); // Cannot be different as long as sanity prevails.
413  chi2Cuts_[0] = 0; // nonsensical.
414  R__b.ReadFastArray(&chi2Cuts_[1], n_chi2Cuts);
415  }
416  AbsKalmanFitter* p;
417  R__b >> p;
418  kalman_.reset(p);
419  R__b.CheckByteCount(R__s, R__c, thisClass::IsA());
420  } else {
421  R__c = R__b.WriteVersion(thisClass::IsA(), kTRUE);
422  //This works around a msvc bug and should be harmless on other platforms
423  typedef genfit::AbsKalmanFitter baseClass0;
424  baseClass0::Streamer(R__b);
425  R__b << deltaWeight_;
426  {
427  std::vector<double>& R__stl = betas_;
428  int R__n = int(R__stl.size());
429  R__b << R__n;
430  if (R__n) {
431  std::vector<double>::iterator R__k;
432  for (R__k = R__stl.begin(); R__k != R__stl.end(); ++R__k) {
433  R__b << (*R__k);
434  }
435  }
436  }
437  {
438  R__b << (char)6; // Number of chi2Cuts_
439  R__b.WriteFastArray(&chi2Cuts_[1], 6);
440  }
441  R__b << kalman_.get();
442  R__b.SetByteCount(R__c, kTRUE);
443  }
444  }
445 
446 } /* End of namespace genfit */
This class collects all information needed and produced by a specific AbsFitter and is specific to on...
Definition: AbsFitterInfo.h:42
Abstract base class for Kalman fitter and derived fitting algorithms.
unsigned int minIterations_
Minimum number of iterations to attempt. Forward and backward are counted as one iteration.
unsigned int maxIterations_
Maximum number of iterations to attempt. Forward and backward are counted as one iteration.
Abstract base class for a track representation.
Definition: AbsTrackRep.h:66
void processTrackWithRep(Track *tr, const AbsTrackRep *rep, bool resortHits=false) override
Process a track using the DAF.
Definition: DAF.cc:95
void setProbCut(const double prob_cut)
Set the probability cut for the weight calculation for the hits.
Definition: DAF.cc:194
void setAnnealingScheme(double bStart, double bFinal, unsigned int nSteps)
Configure the annealing scheme.
Definition: DAF.cc:216
void addProbCut(const double prob_cut, const int measDim)
Set the probability cut for the weight calculation for the hits for a specific measurement dimensiona...
Definition: DAF.cc:201
bool calcWeights(Track *trk, const AbsTrackRep *rep, double beta)
Calculate and set the weights for the next fitting pass.
Definition: DAF.cc:262
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
FitStatus for use with AbsKalmanFitter implementations.
Collects information needed and produced by a AbsKalmanFitter implementations and is specific to one ...
MeasurementOnPlane getResidual(unsigned int iMeasurement=0, bool biased=false, bool onlyMeasurementErrors=true) const override
Get unbiased (default) or biased residual from ith measurement.
bool areWeightsFixed() const
Are the weights fixed?
Kalman filter implementation with linearization around a reference track.
Simple Kalman filter implementation.
Definition: KalmanFitter.h:40
Measured coordinates on a plane.
Collection of TrackPoint objects, AbsTrackRep objects and FitStatus objects.
Definition: Track.h:71
FitStatus * getFitStatus(const AbsTrackRep *rep=nullptr) const
Get FitStatus for a AbsTrackRep. Per default, return FitStatus for cardinalRep.
Definition: Track.h:154
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
Defines for I/O streams used for error and debug printing.
std::ostream debugOut
Default stream for debug output.
@ weightedAverage
weighted average between measurements; used by DAF
std::ostream errorOut
Default stream for error output.