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