AmpGen 2.1
Loading...
Searching...
No Matches
NumericalIntegration.h
Go to the documentation of this file.
1#ifndef AMPGEN_NUMERICALINTEGRATION_H
2#define AMPGEN_NUMERICALINTEGRATION_H 1
3#include <iostream>
4#include <queue>
5
6#include <gsl/gsl_integration.h>
7
8#include "AmpGen/MetaUtils.h"
9#include "AmpGen/simd/utils.h"
11
12namespace AmpGen {
13 template <typename function_type>
14 double integrate_1d(const function_type &fcn, const double &min, const double &max, gsl_integration_workspace *ws = nullptr) {
15 gsl_integration_workspace *w = (ws == nullptr) ? gsl_integration_workspace_alloc(1000) : ws;
16 double result = 0;
17 double error = 0;
18 gsl_function F;
19 // static_assert( is_functor<function_type, double( const double& )>::value == std::true_type , "function is of wrong type");
20 if constexpr(is_functor<function_type, double(const double &)>::value) {
21 F.function = [](double x, void *p) -> double { return (*static_cast<function_type *>(p))(x); };
22 } else if constexpr(is_functor<function_type, double(const std::array<double, 1> &)>::value) {
23 F.function = [](double x, void *p) -> double { return (*static_cast<function_type *>(p))(std::array<double, 1>{x}); };
24 } else
25 static_assert(true, "function matches no signature!");
26
27 F.params = const_cast<function_type *>(&fcn);
28 gsl_integration_qags(&F, min, max, 0, 1e-5, 1000, w, &result, &error);
29 if(ws == nullptr) gsl_integration_workspace_free(w);
30 return result;
31 }
32 template <typename function_type> double integrate_1d_inf(const function_type &fcn, gsl_integration_workspace *ws = nullptr) {
33 gsl_integration_workspace *w = (ws == nullptr) ? gsl_integration_workspace_alloc(1000) : ws;
34 double result = 0;
35 double error = 0;
36 gsl_function F;
37 F.function = [](double x, void *p) -> double { return (*static_cast<function_type *>(p))(x); };
38 F.params = const_cast<function_type *>(&fcn);
39 gsl_integration_qagi(&F, 0, 1e-5, 1000, w, &result, &error);
40 if(ws == nullptr) gsl_integration_workspace_free(w);
41 // std::cout << result << " +/- " << error << std::endl;
42 return result;
43 }
44
45 template <typename function_type>
46 double integrate_1d_cauchy(const function_type &fcn, const double &x0, const double &min, const double &max, gsl_integration_workspace *ws = nullptr) {
47 gsl_integration_workspace *w = (ws == nullptr) ? gsl_integration_workspace_alloc(1000) : ws;
48 double result = 0;
49 double error = 0;
50 gsl_function F;
51 F.function = [](double x, void *p) -> double { return (*static_cast<function_type *>(p))(x); };
52 F.params = const_cast<function_type *>(&fcn);
53 gsl_integration_qawc(&F, min, max, x0, 0, 1e-8, 1000, w, &result, &error);
54 if(ws == nullptr) gsl_integration_workspace_free(w);
55 return result;
56 }
57
58 template <unsigned dim> struct integral {
59 double value = {0};
60 double var = {0};
61 int index = {0};
62 std::array<double, dim> a = {0};
63 std::array<double, dim> b = {0};
64 bool operator<(const integral<dim> &other) const { return var < other.var; }
65 };
66
67 template <unsigned dim, typename fcn>
68 std::tuple<double, double, unsigned> integrate_fp(const fcn &F, const std::array<double, dim> &ctr, const std::array<double, dim> &wth) {
69#if INSTRUCTION_SET == INSTRUCTION_SET_AVX2d
70 return integrate_fp<dim, 4>(F, ctr, wth);
71#endif
72 if constexpr(is_functor<fcn, double(const std::array<double, dim> &)>::value) { return integrate_fp_scalar<dim>(F, ctr, wth); }
73 }
74
75 template <unsigned dim, typename fcn> double integrate(const fcn &F, const std::array<double, dim> xmin, const std::array<double, dim> &xmax) {
76 // Based off of ROOT Math/IntegratorMultiDim
77 // With improved speed and safety:
78 // - No dynamic memory allocation
79 // - Static dimension of integrals
80 // - Supports vectorised integrands
81 // References to actual method [again, from ROOT Math/IntegratorMultiDim]
82 // 1.A.C. Genz and A.A. Malik, Remarks on algorithm 006:
83 // An adaptive algorithm for numerical integration over
84 // an N-dimensional rectangular region, J. Comput. Appl. Math. 6 (1980) 295-302.
85 // 2.A. van Doren and L. de Ridder, An adaptive algorithm for numerical
86 // integration over an n-dimensional cube, J.Comput. Appl. Math. 2 (1976) 207-217.
87 if constexpr(dim == 1) {
88 if constexpr(is_functor<fcn, double(const double &)>::value) {
89 integrate_1d(F, xmin[0], xmax[0]);
90 } else if constexpr(is_functor<fcn, double(const std::array<double, 1> &)>::value) {
91 return integrate_1d([&F](const double &x) { return F(std::array<double, 1>{x}); }, xmin[0], xmax[0]);
92 } else
93 static_assert(true, "1D function doesn't have recognised signature");
94 return 0;
95 } else {
96 double epsrel = 1e-10; // specified relative accuracy
97 double epsabs = 0.; // specified relative accuracy
98 // output parameters
99 double relerr = 0; // an estimation of the relative accuracy of the result
100
101 double result = 0;
102 double abserr = 0;
103 auto status = 3;
104
105 unsigned int ifncls = 0;
106 bool ldv = false;
107 unsigned isbrgn = 1;
108 unsigned isbrgs = 1;
109
110 constexpr unsigned irlcls = get_power<2, dim>::value + 2 * dim * (dim + 1) + 1; // number of function evaluations per iteration
111 constexpr unsigned minpts = get_power<2, dim>::value + 2 * dim * (dim + 1) + 1; // minimum number of function evaluations
112 constexpr unsigned maxpts = 1000000; // maximum number of function evaluations
113
114 std::array<integral<dim>, (1 + maxpts / irlcls) / 2> partial_integrals;
115
116 integral<dim> *current_integral = &(partial_integrals[0]);
117
118 for(unsigned j = 0; j < dim; j++) {
119 current_integral->a[j] = (xmax[j] + xmin[j]) * 0.5;
120 current_integral->b[j] = (xmax[j] - xmin[j]) * 0.5;
121 }
122
123 unsigned int idvax0 = 0;
124 integral<dim> tmp;
125
126 do {
127 auto [rgnval, rgnerr, idvaxn] = integrate_fp<dim>(F, current_integral->a, current_integral->b);
128 result += rgnval;
129 abserr += rgnerr;
130 ifncls += irlcls;
131
132 double aresult = std::abs(result);
133 tmp.var = rgnerr;
134 tmp.value = rgnval;
135 tmp.index = idvaxn;
136 tmp.a = current_integral->a;
137 tmp.b = current_integral->b;
138
139 if(ldv) {
140 unsigned isbtmp = 0;
141 while(true) {
142 isbtmp = 2 * isbrgn;
143 if(isbtmp > isbrgs) break;
144 if(isbtmp < isbrgs && partial_integrals[isbtmp].var < partial_integrals[isbtmp + 1].var) isbtmp++;
145 if(rgnerr >= partial_integrals[isbtmp].var) break;
146 partial_integrals[isbrgn] = partial_integrals[isbtmp];
147 isbrgn = isbtmp;
148 }
149 } else {
150 unsigned isbtmp = 0;
151 do {
152 isbtmp = isbrgn / 2;
153 if(isbtmp >= 1 && rgnerr > partial_integrals[isbtmp].var) {
154 partial_integrals[isbrgn] = partial_integrals[isbtmp];
155 isbrgn = isbtmp;
156 }
157 } while(isbtmp >= 1 && rgnerr > partial_integrals[isbtmp].var);
158 }
159
160 partial_integrals[isbrgn] = tmp;
161 if(ldv) { // divison along chosen coordinate
162 ldv = false;
163 current_integral = &(partial_integrals[isbrgn]);
164 isbrgs += 1;
165 isbrgn = isbrgs;
166 partial_integrals[isbrgn].a = current_integral->a;
167 partial_integrals[isbrgn].b = current_integral->b;
168 partial_integrals[isbrgn].a[idvax0] += 2 * current_integral->b[idvax0];
169 current_integral = &(partial_integrals[isbrgn]);
170 continue;
171 }
172 // if no divisions to be made..
173 relerr = std::abs(result) == 0 ? abserr : abserr / std::abs(result);
174 if((relerr < epsrel && aresult < epsabs) or ((relerr < epsrel || abserr < epsabs) && ifncls > minpts)) {
175 status = 0;
176 break;
177 }
178 if((isbrgs >= partial_integrals.size() - 1) or (ifncls + 2 * irlcls > maxpts)) {
179 status = 2;
180 break;
181 }
182 // std::cout << "#calls: " << ifncls << ", #cycles: " << isbrgs << " / " << ( 1 + maxpts/irlcls)/2 << " " << abserr << std::endl;
183 ldv = true;
184 isbrgn = 1;
185 current_integral = &(partial_integrals[isbrgn]);
186
187 abserr -= current_integral->var;
188 result -= current_integral->value;
189 idvax0 = current_integral->index;
190
191 current_integral->b[idvax0] *= 0.5;
192 current_integral->a[idvax0] -= current_integral->b[idvax0];
193 } while(status == 3);
194 // std::cout << "IFNCLS: " << ifncls << " ISBRGN: " << isbrgn << " ISBRGS: " << isbrgs << " " << abserr << " " << relerr << std::endl;
195 return result; // an approximate value of the integral
196 }
197 }
198}
199
200#endif
double integrate_1d(const function_type &fcn, const double &min, const double &max, gsl_integration_workspace *ws=nullptr)
double integrate_1d_inf(const function_type &fcn, gsl_integration_workspace *ws=nullptr)
double integrate(const fcn &F, const std::array< double, dim > xmin, const std::array< double, dim > &xmax)
std::tuple< double, double, unsigned > integrate_fp(const fcn &F, const std::array< double, dim > &ctr, const std::array< double, dim > &wth)
double integrate_1d_cauchy(const function_type &fcn, const double &x0, const double &min, const double &max, gsl_integration_workspace *ws=nullptr)
std::tuple< double, double, unsigned > integrate_fp_scalar(const fcn &F, const std::array< double, dim > &ctr, const std::array< double, dim > &wth)
std::array< double, dim > a
bool operator<(const integral< dim > &other) const
std::array< double, dim > b