Math.cc
1 /*
2  * MoMEMta: a modular implementation of the Matrix Element Method
3  * Copyright (C) 2016 Universite catholique de Louvain (UCL), Belgium
4  *
5  * This program is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, either version 3 of the License, or
8  * (at your option) any later version.
9  *
10  * This program is distributed in the hope that it will be useful,
11  * but WITHOUT ANY WARRANTY; without even the implied warranty of
12  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  * GNU General Public License for more details.
14  *
15  * You should have received a copy of the GNU General Public License
16  * along with this program. If not, see <http://www.gnu.org/licenses/>.
17  */
18 
19 #include <momemta/Math.h>
20 
21 #include <iostream>
22 #include <limits>
23 
24 using namespace std;
25 
26 bool solveQuadratic(const double a, const double b, const double c, std::vector<double>& roots,
27  bool verbose) {
28 
29  if (!a) {
30  if (!b) {
31  cout << "No solution to equation " << a << " x^2 + " << b << " x + " << c << endl
32  << endl;
33  return false;
34  }
35  roots.push_back(-c / b);
36  if (verbose)
37  cout << "Solution of " << b << " x + " << c << ": " << roots[0]
38  << ", test = " << b * roots[0] + c << endl
39  << endl;
40  return true;
41  }
42 
43  const double rho = SQ(b) - 4. * a * c;
44 
45  if (rho >= 0.) {
46  if (b == 0.) {
47  roots.push_back(sqrt(rho) / (2. * a));
48  roots.push_back(-sqrt(rho) / (2. * a));
49  } else {
50  const double x = -0.5 * (b + sign(b) * sqrt(rho));
51  roots.push_back(x / a);
52  roots.push_back(c / x);
53  }
54  if (verbose) {
55  cout << "Solutions of " << a << " x^2 + " << b << " x + " << c << ":" << endl;
56  for (unsigned short i = 0; i < roots.size(); i++)
57  cout << "x" << i << " = " << roots[i]
58  << ", test = " << a * SQ(roots[i]) + b * roots[i] + c << endl;
59  cout << endl;
60  }
61  return true;
62  } else {
63  if (verbose)
64  cout << "No real solutions to " << a << " x^2 + " << b << " x + " << c << endl << endl;
65  return false;
66  }
67 }
68 
69 bool solveCubic(const double a, const double b, const double c, const double d,
70  std::vector<double>& roots, bool verbose) {
71 
72  if (a == 0)
73  return solveQuadratic(b, c, d, roots, verbose);
74 
75  const double an = b / a;
76  const double bn = c / a;
77  const double cn = d / a;
78 
79  const double Q = SQ(an) / 9. - bn / 3.;
80  const double R = CB(an) / 27. - an * bn / 6. + cn / 2.;
81 
82  if (SQ(R) < CB(Q)) {
83  const double theta = acos(R / sqrt(CB(Q))) / 3.;
84 
85  roots.push_back(-2. * sqrt(Q) * cos(theta) - an / 3.);
86  roots.push_back(-2. * sqrt(Q) * cosXpm2PI3(theta, 1.) - an / 3.);
87  roots.push_back(-2. * sqrt(Q) * cosXpm2PI3(theta, -1.) - an / 3.);
88  } else {
89  const double A = -sign(R) * cbrt(abs(R) + sqrt(SQ(R) - CB(Q)));
90 
91  double B;
92 
93  if (A == 0.)
94  B = 0.;
95  else
96  B = Q / A;
97 
98  const double x = A + B - an / 3.;
99 
100  roots.push_back(x);
101  roots.push_back(x);
102  roots.push_back(x);
103  }
104 
105  if (verbose) {
106  cout << "Solutions of " << a << " x^3 + " << b << " x^2 + " << c << " x + " << d << ":"
107  << endl;
108  for (unsigned short i = 0; i < roots.size(); i++)
109  cout << "x" << i << " = " << roots[i]
110  << ", test = " << a * CB(roots[i]) + b * SQ(roots[i]) + c * roots[i] + d << endl;
111  cout << endl;
112  }
113 
114  return true;
115 }
116 
117 bool solveQuartic(const double a, const double b, const double c, const double d, const double e,
118  std::vector<double>& roots, bool verbose) {
119 
120  if (!a)
121  return solveCubic(b, c, d, e, roots, verbose);
122 
123  if (!b && !c && !d && !e) {
124  roots.push_back(0.);
125  roots.push_back(0.);
126  roots.push_back(0.);
127  roots.push_back(0.);
128  } else if (!b && !d) {
129  std::vector<double> sq_sol;
130  solveQuadratic(a, c, e, sq_sol, verbose);
131  for (unsigned short i = 0; i < sq_sol.size(); ++i) {
132  if (sq_sol[i] < 0)
133  continue;
134  roots.push_back(sqrt(sq_sol[i]));
135  roots.push_back(-sqrt(sq_sol[i]));
136  }
137  } else {
138  const double an = b / a;
139  const double bn = c / a - (3. / 8.) * SQ(b / a);
140  const double cn = CB(0.5 * b / a) - 0.5 * b * c / SQ(a) + d / a;
141  const double dn =
142  -3. * QU(0.25 * b / a) + e / a - 0.25 * b * d / SQ(a) + c * SQ(b / 4.) / CB(a);
143 
144  std::vector<double> res;
145  solveCubic(1., 2. * bn, SQ(bn) - 4. * dn, -SQ(cn), res, verbose);
146  short pChoice = -1;
147 
148  for (unsigned short i = 0; i < res.size(); ++i) {
149  if (res[i] > 0) {
150  pChoice = i;
151  break;
152  }
153  }
154 
155  if (pChoice < 0) {
156  if (verbose)
157  cout << "No real solution to " << a << " x^4 + " << b << " x^3 + " << c << " x^2 + "
158  << d << " x + " << e << " (no positive root for the resolvent cubic)." << endl
159  << endl;
160  return false;
161  }
162 
163  const double p = sqrt(res[pChoice]);
164  solveQuadratic(p, SQ(p), 0.5 * (p * (bn + res[pChoice]) - cn), roots, verbose);
165  solveQuadratic(p, -SQ(p), 0.5 * (p * (bn + res[pChoice]) + cn), roots, verbose);
166 
167  for (unsigned short i = 0; i < roots.size(); ++i)
168  roots[i] -= an / 4.;
169  }
170 
171  size_t nRoots = roots.size();
172 
173  if (verbose) {
174  if (nRoots) {
175  cout << "Solutions of " << a << " x^4 + " << b << " x^3 + " << c << " x^2 + " << d
176  << " x + " << e << ":" << endl;
177  for (unsigned short i = 0; i < nRoots; i++)
178  cout << "x" << i << " = " << roots[i] << ", test = "
179  << a * QU(roots[i]) + b * CB(roots[i]) + c * SQ(roots[i]) + d * roots[i] + e
180  << endl;
181  cout << endl;
182  } else {
183  cout << "No real solution to " << a << " x^4 + " << b << " x^3 + " << c << " x^2 + "
184  << d << " x + " << e << endl
185  << endl;
186  }
187  }
188 
189  return nRoots > 0;
190 }
191 
192 bool solve2Quads(const double a20, const double a02, const double a11, const double a10,
193  const double a01, const double a00, const double b20, const double b02,
194  const double b11, const double b10, const double b01, const double b00,
195  std::vector<double>& E1, std::vector<double>& E2, bool verbose) {
196 
197  // The procedure used in this function relies on a20 != 0 or b20 != 0
198  if (a20 == 0. && b20 == 0.) {
199 
200  if (a02 != 0. || b02 != 0.) {
201  // Swapping E1 <-> E2 should suffice!
202  return solve2Quads(a02, a20, a11, a01, a10, a00, b02, b20, b11, b01, b10, b00, E2, E1,
203  verbose);
204  } else {
205  return solve2QuadsDeg(a11, a10, a01, a00, b11, b10, b01, b00, E1, E2, verbose);
206  }
207  }
208 
209  const double alpha = b20 * a02 - a20 * b02;
210  const double beta = b20 * a11 - a20 * b11;
211  const double gamma = b20 * a10 - a20 * b10;
212  const double delta = b20 * a01 - a20 * b01;
213  const double omega = b20 * a00 - a20 * b00;
214 
215  const double a = a20 * SQ(alpha) + a02 * SQ(beta) - a11 * alpha * beta;
216  const double b = 2. * a20 * alpha * delta - a11 * (alpha * gamma + delta * beta) -
217  a10 * alpha * beta + 2. * a02 * beta * gamma + a01 * SQ(beta);
218  const double c = a20 * SQ(delta) + 2. * a20 * alpha * omega -
219  a11 * (delta * gamma + omega * beta) - a10 * (alpha * gamma + delta * beta) +
220  a02 * SQ(gamma) + 2. * a01 * beta * gamma + a00 * SQ(beta);
221  const double d = 2. * a20 * delta * omega - a11 * omega * gamma -
222  a10 * (delta * gamma + omega * beta) + a01 * SQ(gamma) +
223  2. * a00 * beta * gamma;
224  const double e = a20 * SQ(omega) - a10 * omega * gamma + a00 * SQ(gamma);
225 
226  solveQuartic(a, b, c, d, e, E2, verbose);
227 
228  for (unsigned short i = 0; i < E2.size(); ++i) {
229 
230  const double e2 = E2[i];
231 
232  if (beta * e2 + gamma != 0.) {
233  // Everything OK
234 
235  const double e1 = -(alpha * SQ(e2) + delta * e2 + omega) / (beta * e2 + gamma);
236  E1.push_back(e1);
237 
238  } else if (alpha * SQ(e2) + delta * e2 + omega == 0.) {
239  // Up to two solutions for e1
240 
241  std::vector<double> e1;
242 
243  if (!solveQuadratic(a20, a11 * e2 + a10, a02 * SQ(e2) + a01 * e2 + a00, e1, verbose)) {
244 
245  if (!solveQuadratic(b20, b11 * e2 + b10, b02 * SQ(e2) + b01 * e2 + b00, e1,
246  verbose)) {
247  cout << "Error in solve2Quads: there should be at least one solution for e1!"
248  << endl;
249  E1.clear();
250  E2.clear();
251  return false;
252  }
253 
254  } else {
255  // We have either a double, or two roots for e1
256  // In this case, e2 must be twice degenerate!
257  // Since in E2 degenerate roots are grouped, E2[i+1] shoud exist and be equal to e2
258  // We then go straight for i+2.
259 
260  if (i < E2.size() - 1) {
261 
262  if (e2 != E2[i + 1]) {
263  cout << "Error in solve2Quads: if there are two solutions for e1, e2 "
264  "should be degenerate!"
265  << endl;
266  E1.clear();
267  E2.clear();
268  return false;
269  }
270 
271  E1.push_back(e1[0]);
272  E1.push_back(e1[1]);
273  ++i;
274  continue;
275 
276  } else {
277  cout << "Error in solve2Quads: if there are two solutions for e1, e2 should be "
278  "degenerate!"
279  << endl;
280  E1.clear();
281  E2.clear();
282  return false;
283  }
284  }
285 
286  } else {
287  // There is no solution given this e2
288  E2.erase(E2.begin() + i);
289  --i;
290  }
291  }
292 
293  return true;
294 }
295 
296 bool solve2QuadsDeg(const double a11, const double a10, const double a01, const double a00,
297  const double b11, const double b10, const double b01, const double b00,
298  vector<double>& E1, vector<double>& E2, bool verbose) {
299 
300  if (a11 == 0. && b11 == 0.)
301  return solve2Linear(a10, a01, a00, b10, b01, b00, E1, E2, verbose);
302 
303  bool result = solveQuadratic(a11 * (b11 * a10 - a11 * b10),
304  a01 * (b11 * a10 - a11 * b10) - a01 * (b11 * a01 - a11 * b01) +
305  a11 * (b11 * a00 - a11 * b00),
306  a01 * (b11 * a00 - a11 * b00), E1, verbose);
307 
308  if (!result) {
309  if (verbose) {
310  cout << "No solution to the system:" << endl;
311  cout << " " << a11 << "*E1*E2 + " << a10 << "*E1 + " << a01 << "*E2 + " << a00 << " = 0"
312  << endl;
313  cout << " " << b11 << "*E1*E2 + " << b10 << "*E1 + " << b01 << "*E2 + " << b00 << " = 0"
314  << endl;
315  cout << endl;
316  }
317  return false;
318  }
319 
320  for (unsigned short i = 0; i < E1.size(); ++i) {
321 
322  double denom = a11 * E1[i] + a01;
323 
324  if (denom != 0) {
325  E2.push_back(-(a10 * E1[i] + a00) / denom);
326 
327  } else {
328  denom = b11 * a01 - a11 * b01;
329  if (denom != 0.) {
330  E2.push_back(-((b11 * a10 - a11 * b10) * E1[i] + b11 * a00 - a11 * b00) / denom);
331  } else {
332  denom = b11 * E1[i] + b01;
333  if (denom != 0.) {
334  E2.push_back(-(b10 * E1[i] + b00) / denom);
335  } else {
336  E1.erase(E1.begin() + i);
337  --i;
338  }
339  }
340  }
341  }
342 
343  if (verbose) {
344  cout << "Solutions to the system:" << endl;
345  cout << " " << a11 << "*E1*E2 + " << a10 << "*E1 + " << a01 << "*E2 + " << a00 << " = 0"
346  << endl;
347  cout << " " << b11 << "*E1*E2 + " << b10 << "*E1 + " << b01 << "*E2 + " << b00 << " = 0"
348  << endl;
349  for (unsigned short i = 0; i < E1.size(); ++i)
350  cout << " E1 = " << E1[i] << ", E2 = " << E2[i] << endl;
351  cout << endl;
352  }
353 
354  return E1.size();
355 }
356 
357 bool solve2Linear(const double a10, const double a01, const double a00, const double b10,
358  const double b01, const double b00, std::vector<double>& E1,
359  std::vector<double>& E2, bool verbose) {
360 
361  const double det = a10 * b01 - b10 * a01;
362 
363  if (det == 0.) {
364  if (a00 != 0. || b00 != 0.) {
365  if (verbose) {
366  cout << "No solution to the system:" << endl;
367  cout << " " << a10 << "*E1 + " << a01 << "*E2 + " << a00 << " = 0" << endl;
368  cout << " " << b10 << "*E1 + " << b01 << "*E2 + " << b00 << " = 0" << endl;
369  cout << endl;
370  }
371  return false;
372  } else {
373  cout << "Error in solve2Linear: indeterminate system:" << endl;
374  cout << " " << a10 << "*E1 + " << a01 << "*E2 + " << a00 << " = 0" << endl;
375  cout << " " << b10 << "*E1 + " << b01 << "*E2 + " << b00 << " = 0" << endl;
376  cout << endl;
377  return false;
378  }
379  }
380 
381  const double e2 = (b10 * a00 - a10 * b00) / det;
382  E2.push_back(e2);
383  double e1;
384  if (a10 == 0.)
385  e1 = -(b00 + b01 * e2) / b10;
386  else
387  e1 = -(a00 + a01 * e2) / a10;
388  E1.push_back(e1);
389 
390  if (verbose) {
391  cout << "Solution to the system:" << endl;
392  cout << " " << a10 << "*E1 + " << a01 << "*E2 + " << a00 << " = 0" << endl;
393  cout << " " << b10 << "*E1 + " << b01 << "*E2 + " << b00 << " = 0" << endl;
394  cout << " E1 = " << e1 << ", E2 = " << e2 << endl << endl;
395  }
396 
397  return true;
398 }
399 
400 double BreitWigner(const double s, const double m, const double g) {
401  double k = m * g;
402  return k / (std::pow(s - m * m, 2.) + std::pow(m * g, 2.));
403 }
404 
405 /*
406  * Copied from Catch:
407  * https://github.com/philsquared/Catch/blob/7a22bad76340f8b48094b46bc586e76ac9ea93ac/include/internal/catch_approx.hpp#L55-L60
408  */
409 bool ApproxComparison(double value, double expected) {
410  static const double epsilon = std::numeric_limits<float>::epsilon() * 100;
411  static const double margin = 1.0;
412  static const double scale = 1.0;
413 
414  bool relativeOK = std::fabs(value - expected) < epsilon * (scale + (std::max)(std::fabs(value), std::fabs(expected)));
415  if (relativeOK) {
416  return true;
417  }
418  return std::fabs(value - expected) < margin;
419 }
bool solveQuartic(const double a, const double b, const double c, const double d, const double e, std::vector< double > &roots, bool verbose=false)
Finds the real solutions to .
Definition: Math.cc:117
bool ApproxComparison(double value, double expected)
Compare two doubles and return true if they are approximatively equal.
Definition: Math.cc:409
Mathematical functions.
bool solveQuadratic(const double a, const double b, const double c, std::vector< double > &roots, bool verbose=false)
Finds the real solutions to .
Definition: Math.cc:26
#define QU(x)
Compute .
Definition: Math.h:29
Definition: optional.h:869
T sign(const T &x)
Sign function.
Definition: Math.h:41
bool solve2Linear(const double a10, const double a01, const double a00, const double b10, const double b01, const double b00, std::vector< double > &E1, std::vector< double > &E2, bool verbose=false)
Solve a system of two linear equations.
Definition: Math.cc:357
#define SQ(x)
Compute .
Definition: Math.h:25
double BreitWigner(const double s, const double m, const double g)
A relativist Breit-Wigner distribution.
Definition: Math.cc:400
#define CB(x)
Compute .
Definition: Math.h:27
bool solve2QuadsDeg(const double a11, const double a10, const double a01, const double a00, const double b11, const double b10, const double b01, const double b00, std::vector< double > &E1, std::vector< double > &E2, bool verbose=false)
Solve a system of two degenerated quadratic equations.
bool solve2Quads(const double a20, const double a02, const double a11, const double a10, const double a01, const double a00, const double b20, const double b02, const double b11, const double b10, const double b01, const double b00, std::vector< double > &E1, std::vector< double > &E2, bool verbose=false)
Solve a system of two quadratic equations.
Definition: Math.cc:192
bool solveCubic(const double a, const double b, const double c, const double d, std::vector< double > &roots, bool verbose=false)
Finds the real solutions to .
Definition: Math.cc:69