Belle II Software  release-06-02-00
BaseHardConstraint.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * Forked from https://github.com/iLCSoft/MarlinKinfit *
6  * *
7  * Further information about the fit engine and the user interface *
8  * provided in MarlinKinfit can be found at *
9  * https://www.desy.de/~blist/kinfit/doc/html/ *
10  * and in the LCNotes LC-TOOL-2009-001 and LC-TOOL-2009-004 available *
11  * from http://www-flc.desy.de/lcnotes/ *
12  * *
13  * See git log for contributors and copyright holders. *
14  * This file is licensed under LGPL-3.0, see LICENSE.md. *
15  **************************************************************************/
16 
17 #include "analysis/OrcaKinFit/BaseHardConstraint.h"
18 #include <framework/logging/Logger.h>
19 
20 #undef NDEBUG
21 #include <cassert>
22 
23 #include <cstring>
24 #include <iostream>
25 #include <cmath>
26 
27 namespace Belle2 {
32  namespace OrcaKinFit {
33 
35 
36 
37  void BaseHardConstraint::add1stDerivativesToMatrix(double* M, int idim) const
38  {
39  double dgdpi[BaseDefs::MAXINTERVARS];
40  for (unsigned int i = 0; i < fitobjects.size(); ++i) {
41  const BaseFitObject* foi = fitobjects[i];
42  assert(foi);
43  if (firstDerivatives(i, dgdpi)) {
44  foi->addTo1stDerivatives(M, idim, dgdpi, getGlobalNum(), getVarBasis());
45  }
46  }
47  }
48 
49 
75  void BaseHardConstraint::add2ndDerivativesToMatrix(double* M, int idim, double lambda) const
76  {
77 
84  // Derivatives $\frac{\partial ^2 g}{\partial P_i \partial P_j}$ at fixed i, j
85  // d2GdPidPj[4*ii+jj] is derivative w.r.t. P_i,ii and P_j,jj, where ii=0,1,2,3 for E,px,py,pz
86  double d2GdPidPj[BaseDefs::MAXINTERVARS * BaseDefs::MAXINTERVARS];
87 
88  // Derivatives $\frac {\partial P_i}{\partial a_k}$ for all i;
89  // k is local parameter number
90  // dPidAk[KMAX*4*i + 4*k + ii] is $\frac {\partial P_{i,ii}}{\partial a_k}$,
91  // with ii=0, 1, 2, 3 for E, px, py, pz
92  const int n = fitobjects.size();
93  auto* dPidAk = new double[n * BaseDefs::MAXPAR * BaseDefs::MAXINTERVARS];
94  bool* dPidAkval = new bool[n];
95 
96  for (int i = 0; i < n; ++i) dPidAkval[i] = false;
97 
98  // Derivatives $\frac{\partial ^2 g}{\partial P_i \partial a_l}$ at fixed i
99  // d2GdPdAl[4*l + ii] is $\frac{\partial ^2 g}{\partial P_{i,ii} \partial a_l}$
100  double d2GdPdAl[BaseDefs::MAXINTERVARS * BaseDefs::MAXPAR];
101  // Derivatives $\frac{\partial ^2 g}{\partial a_k \partial a_l}$
102  double d2GdAkdAl[BaseDefs::MAXPAR * BaseDefs::MAXPAR] = {0};
103 
104  // Global parameter numbers: parglobal[BaseDefs::MAXPAR*i+klocal]
105  // is global parameter number of local parameter klocal of i-th Fit object
106  int* parglobal = new int[BaseDefs::MAXPAR * n];
107 
108  for (int i = 0; i < n; ++i) {
109  const BaseFitObject* foi = fitobjects[i];
110  assert(foi);
111  for (int klocal = 0; klocal < foi->getNPar(); ++klocal) {
112  parglobal [BaseDefs::MAXPAR * i + klocal] = foi->getGlobalParNum(klocal);
113  }
114  }
115 
116 
117  for (int i = 0; i < n; ++i) {
118  const BaseFitObject* foi = fitobjects[i];
119  assert(foi);
120  for (int j = 0; j < n; ++j) {
121  const BaseFitObject* foj = fitobjects[j];
122  assert(foj);
123  if (secondDerivatives(i, j, d2GdPidPj)) {
124  if (!dPidAkval[i]) {
125  foi->getDerivatives(dPidAk + i * (BaseDefs::MAXPAR * BaseDefs::MAXINTERVARS), BaseDefs::MAXPAR * BaseDefs::MAXINTERVARS);
126  dPidAkval[i] = true;
127  }
128  if (!dPidAkval[j]) {
129  foj->getDerivatives(dPidAk + j * (BaseDefs::MAXPAR * BaseDefs::MAXINTERVARS), BaseDefs::MAXPAR * BaseDefs::MAXINTERVARS);
130  dPidAkval[j] = true;
131  }
132  // Now sum over E/px/Py/Pz for object j:
133  // $$\frac{\partial ^2 g}{\partial P_{i,ii} \partial a_l}
134  // = (sum_{j}) sum_{jj} frac{\partial ^2 g}{\partial P_{i,ii} \partial P_{j,jj}}
135  // \cdot \frac{\partial P_{j,jj}}{\partial a_l}
136  // We're summing over jj here
137  for (int llocal = 0; llocal < foj->getNPar(); ++llocal) {
138  for (int ii = 0; ii < BaseDefs::MAXINTERVARS; ++ii) {
139  int ind1 = BaseDefs::MAXINTERVARS * ii;
140  int ind2 = (BaseDefs::MAXPAR * BaseDefs::MAXINTERVARS) * j + BaseDefs::MAXINTERVARS * llocal;
141  double& r = d2GdPdAl[BaseDefs::MAXINTERVARS * llocal + ii];
142  r = d2GdPidPj[ ind1] * dPidAk[ ind2]; // E
143  r += d2GdPidPj[++ind1] * dPidAk[++ind2]; // px
144  r += d2GdPidPj[++ind1] * dPidAk[++ind2]; // py
145  r += d2GdPidPj[++ind1] * dPidAk[++ind2]; // pz
146  }
147  }
148  // Now sum over E/px/Py/Pz for object i, i.e. sum over ii:
149  // $$
150  // \frac{\partial ^2 g}{\partial a_k \partial a_l}
151  // = \sum_{ii} \frac{\partial ^2 g}{\partial P_{i,ii} \partial a_l} \cdot
152  // \frac{\partial P_{i,ii}}{\partial a_k}
153  // $$
154  for (int klocal = 0; klocal < foi->getNPar(); ++klocal) {
155  for (int llocal = 0; llocal < foj->getNPar(); ++llocal) {
156  int ind1 = BaseDefs::MAXINTERVARS * llocal;
157  int ind2 = (BaseDefs::MAXPAR * BaseDefs::MAXINTERVARS) * i + BaseDefs::MAXINTERVARS * klocal;
158  double& r = d2GdAkdAl[BaseDefs::MAXPAR * klocal + llocal];
159  r = d2GdPdAl[ ind1] * dPidAk[ ind2]; //E
160  r += d2GdPdAl[++ind1] * dPidAk[++ind2]; // px
161  r += d2GdPdAl[++ind1] * dPidAk[++ind2]; // py
162  r += d2GdPdAl[++ind1] * dPidAk[++ind2]; // pz
163  }
164  }
165  // Now expand the local parameter numbers to global ones
166  for (int klocal = 0; klocal < foi->getNPar(); ++klocal) {
167  int kglobal = parglobal [BaseDefs::MAXPAR * i + klocal];
168  for (int llocal = 0; llocal < foj->getNPar(); ++llocal) {
169  int lglobal = parglobal [BaseDefs::MAXPAR * j + llocal];
170  M [idim * kglobal + lglobal] += lambda * d2GdAkdAl[BaseDefs::MAXPAR * klocal + llocal];
171  }
172  }
173  }
174  }
175  }
185  double dgdpi[BaseDefs::MAXINTERVARS];
186  for (int i = 0; i < n; ++i) {
187  const BaseFitObject* foi = fitobjects[i];
188  assert(foi);
189  if (firstDerivatives(i, dgdpi)) {
190  foi->addTo2ndDerivatives(M, idim, lambda, dgdpi, getVarBasis());
191  }
192  }
193 
194  delete[] dPidAk;
195  delete[] dPidAkval;
196  delete[] parglobal;
197  }
198 
199  void BaseHardConstraint::addToGlobalChi2DerVector(double* y, int idim, double lambda) const
200  {
201  double dgdpi[BaseDefs::MAXINTERVARS];
202  for (unsigned int i = 0; i < fitobjects.size(); ++i) {
203  const BaseFitObject* foi = fitobjects[i];
204  assert(foi);
205  if (firstDerivatives(i, dgdpi)) {
206  foi->addToGlobalChi2DerVector(y, idim, lambda, dgdpi, getVarBasis());
207  }
208  }
209  }
210 
211 
213  {
214  double dgdpi[BaseDefs::MAXINTERVARS];
215  double error2 = 0;
216  for (unsigned int i = 0; i < fitobjects.size(); ++i) {
217  const BaseFitObject* foi = fitobjects[i];
218  assert(foi);
219  if (firstDerivatives(i, dgdpi)) {
220  error2 += foi->getError2(dgdpi, getVarBasis());
221  }
222  }
223  return std::sqrt(std::abs(error2));
224  }
225 
226 
227  double BaseHardConstraint::dirDer(double* p, double* w, int idim, double mu)
228  {
229  double* pw, *pp;
230  for (pw = w; pw < w + idim; * (pw++) = 0);
231  addToGlobalChi2DerVector(w, idim, mu);
232  double result = 0;
233  for (pw = w, pp = p; pw < w + idim; result += *(pp++) * *(pw++));
234  return mu * result;
235  }
236 
237  double BaseHardConstraint::dirDerAbs(double* p, double* w, int idim, double mu)
238  {
239  double val = getValue();
240  if (val == 0) return mu * std::fabs(dirDer(p, w, idim, 1));
241  else if (val > 0) return mu * dirDer(p, w, idim, 1);
242  else return -mu * dirDer(p, w, idim, 1);
243  }
244 
245 
246  void BaseHardConstraint::test1stDerivatives()
247  {
248  B2INFO("BaseConstraint::test1stDerivatives for " << getName());
249  double y[100];
250  for (double& i : y) i = 0;
251  addToGlobalChi2DerVector(y, 100, 1);
252  double eps = 0.00001;
253  for (unsigned int ifo = 0; ifo < fitobjects.size(); ++ifo) {
254  BaseFitObject* fo = fitobjects[ifo];
255  assert(fo);
256  for (int ilocal = 0; ilocal < fo->getNPar(); ++ilocal) {
257  int iglobal = fo->getGlobalParNum(ilocal);
258  double calc = y[iglobal];
259  double num = num1stDerivative(ifo, ilocal, eps);
260  B2INFO("fo: " << fo->getName() << " par " << ilocal << "/"
261  << iglobal << " (" << fo->getParamName(ilocal)
262  << ") calc: " << calc << " - num: " << num << " = " << calc - num);
263  }
264  }
265  }
266 
267  void BaseHardConstraint::test2ndDerivatives()
268  {
269  B2INFO("BaseConstraint::test2ndDerivatives for " << getName());
270  const int idim = 100;
271  auto* M = new double[idim * idim];
272  for (int i = 0; i < idim * idim; ++i) M[i] = 0;
273  add2ndDerivativesToMatrix(M, idim, 1);
274  double eps = 0.0001;
275  B2INFO("eps=" << eps);
276 
277  for (unsigned int ifo1 = 0; ifo1 < fitobjects.size(); ++ifo1) {
278  BaseFitObject* fo1 = fitobjects[ifo1];
279  assert(fo1);
280  for (unsigned int ifo2 = ifo1; ifo2 < fitobjects.size(); ++ifo2) {
281  BaseFitObject* fo2 = fitobjects[ifo2];
282  assert(fo2);
283  for (int ilocal1 = 0; ilocal1 < fo1->getNPar(); ++ilocal1) {
284  int iglobal1 = fo1->getGlobalParNum(ilocal1);
285  for (int ilocal2 = (ifo1 == ifo2 ? ilocal1 : 0); ilocal2 < fo2->getNPar(); ++ilocal2) {
286  int iglobal2 = fo2->getGlobalParNum(ilocal2);
287  double calc = M[idim * iglobal1 + iglobal2];
288  double num = num2ndDerivative(ifo1, ilocal1, eps, ifo2, ilocal2, eps);
289  B2INFO("fo1: " << fo1->getName() << " par " << ilocal1 << "/"
290  << iglobal1 << " (" << fo1->getParamName(ilocal1)
291  << "), fo2: " << fo2->getName() << " par " << ilocal2 << "/"
292  << iglobal2 << " (" << fo2->getParamName(ilocal2)
293  << ") calc: " << calc << " - num: " << num << " = " << calc - num);
294  }
295  }
296  }
297  }
298  delete[] M;
299  }
300 
301 
302  double BaseHardConstraint::num1stDerivative(int ifo, int ilocal, double eps)
303  {
304  BaseFitObject* fo = fitobjects[ifo];
305  assert(fo);
306  double save = fo->getParam(ilocal);
307  fo->setParam(ilocal, save + eps);
308  double v1 = getValue();
309  fo->setParam(ilocal, save - eps);
310  double v2 = getValue();
311  double result = (v1 - v2) / (2 * eps);
312  fo->setParam(ilocal, save);
313  return result;
314  }
315 
316  double BaseHardConstraint::num2ndDerivative(int ifo1, int ilocal1, double eps1,
317  int ifo2, int ilocal2, double eps2)
318  {
319  double result;
320 
321  if (ifo1 == ifo2 && ilocal1 == ilocal2) {
322  BaseFitObject* fo = fitobjects[ifo1];
323  assert(fo);
324  double save = fo->getParam(ilocal1);
325  double v0 = getValue();
326  fo->setParam(ilocal1, save + eps1);
327  double v1 = getValue();
328  fo->setParam(ilocal1, save - eps1);
329  double v2 = getValue();
330  result = (v1 + v2 - 2 * v0) / (eps1 * eps1);
331  fo->setParam(ilocal1, save);
332  } else {
333  BaseFitObject* fo1 = fitobjects[ifo1];
334  assert(fo1);
335  BaseFitObject* fo2 = fitobjects[ifo2];
336  assert(fo2);
337  double save1 = fo1->getParam(ilocal1);
338  double save2 = fo2->getParam(ilocal2);
339  fo1->setParam(ilocal1, save1 + eps1);
340  fo2->setParam(ilocal2, save2 + eps2);
341  double v11 = getValue();
342  fo2->setParam(ilocal2, save2 - eps2);
343  double v12 = getValue();
344  fo1->setParam(ilocal1, save1 - eps1);
345  double v22 = getValue();
346  fo2->setParam(ilocal2, save2 + eps2);
347  double v21 = getValue();
348  result = (v11 + v22 - v12 - v21) / (4 * eps1 * eps2);
349  fo1->setParam(ilocal1, save1);
350  fo2->setParam(ilocal2, save2);
351  }
352  return result;
353  }
354 
355  void BaseHardConstraint::printFirstDerivatives() const
356  {
357 
358  B2INFO("BaseHardConstraint::printFirstDerivatives " << fitobjects.size());
359 
360  double dgdpi[BaseDefs::MAXINTERVARS];
361  for (unsigned int i = 0; i < fitobjects.size(); ++i) {
362  const BaseFitObject* foi = fitobjects[i];
363  assert(foi);
364  if (firstDerivatives(i, dgdpi)) {
365  B2INFO("first derivs for obj " << i << " : ");
366  for (double j : dgdpi)
367  B2INFO(j << " ");
368  }
369  }
370 
371  return;
372  }
373 
374  void BaseHardConstraint::printSecondDerivatives() const
375  {
376 
377  double d2GdPidPj[BaseDefs::MAXINTERVARS * BaseDefs::MAXINTERVARS];
378 
379  int n = fitobjects.size();
380 
381  for (int i = 0; i < n; ++i) {
382  const BaseFitObject* foi = fitobjects[i];
383  assert(foi);
384  for (int j = 0; j < n; ++j) {
385  const BaseFitObject* foj = fitobjects[j];
386  assert(foj);
387  if (secondDerivatives(i, j, d2GdPidPj)) {
388 
389  B2INFO("second derivs for objs " << i << " " << j);
390 
391  int k(0);
392  for (int k1 = 0; k1 < BaseDefs::MAXINTERVARS; k1++) {
393  for (int k2 = 0; k2 < BaseDefs::MAXINTERVARS; k2++) {
394  B2INFO(d2GdPidPj[k++] << " ");
395  }
396  }
397  }
398  }
399  }
400 
401  return;
402  }
403 
404  }// end OrcaKinFit namespace
406 } // end Belle2 namespace
virtual const char * getName() const
Returns the name of the constraint.
virtual int getNPar() const =0
Get total number of parameters of this FitObject.
virtual const char * getParamName(int ilocal) const =0
Get name of parameter ilocal.
virtual bool setParam(int ilocal, double par_, bool measured_, bool fixed_=false)
Set value and measured flag of parameter i; return: significant change.
virtual int getGlobalParNum(int ilocal) const
Get global parameter number of parameter ilocal.
virtual const char * getName() const
Get object's name.
virtual double getParam(int ilocal) const
Get current value of parameter ilocal.
virtual int addToGlobalChi2DerVector(double *y, int idim) const
Add derivatives of chi squared to global derivative vector.
virtual ~BaseHardConstraint()
Virtual destructor.
virtual int getGlobalNum() const
Accesses position of constraint in global constraint list.
virtual double getValue() const override=0
Returns the value of the constraint.
FitObjectContainer fitobjects
The FitObjectContainer.
virtual void add1stDerivativesToMatrix(double *M, int idim) const
Adds first order derivatives to global covariance matrix M.
virtual double getError() const override
Returns the error on the value of the constraint.
virtual void addToGlobalChi2DerVector(double *y, int idim, double lambda) const
Add lambda times derivatives of chi squared to global derivative vector.
virtual double dirDerAbs(double *p, double *w, int idim, double mu=1)
Calculate directional derivative for abs(c)
virtual double dirDer(double *p, double *w, int idim, double mu=1)
Calculate directional derivative.
virtual bool secondDerivatives(int i, int j, double *derivatives) const =0
Second derivatives with respect to the meta-variables of Fit objects i and j; result false if all der...
virtual bool firstDerivatives(int i, double *derivatives) const =0
First derivatives with respect to the meta-variables of Fit objects i; result false if all derivative...
virtual void add2ndDerivativesToMatrix(double *M, int idim, double lambda) const
Adds second order derivatives to global covariance matrix M.
virtual double num2ndDerivative(int ifo1, int ilocal1, double eps1, int ifo2, int ilocal2, double eps2)
Evaluates numerically the 2nd derivative w.r.t. 2 parameters.
virtual double num1stDerivative(int ifo, int ilocal, double eps)
Evaluates numerically the 1st derivative w.r.t. a parameter.
Abstract base class for different kinds of events.