qxLib
integration.inl
Go to the documentation of this file.
1 /**
2 
3  @file integration.inl
4  @author Khrapov
5  @date 29.04.2023
6  @copyright (c) Nick Khrapov, 2023. All right reserved.
7 
8 **/
9 
10 namespace qx
11 {
12 
13 namespace details
14 {
15 
16 template<class count_t>
17 inline std::optional<count_t> integration_sample_count(double fExtent, size_t nSamplesPerUnit) noexcept
18 {
19  if (!std::isfinite(fExtent) || nSamplesPerUnit == 0)
20  return std::nullopt;
21 
22  // Floating-point arithmetic is required; validate the resulting count below.
23  const double fCount = std::ceil(std::abs(fExtent) * static_cast<double>(nSamplesPerUnit)); //-V203
24 
25  // A power of two is exact in double, unlike the largest size_t value.
26  const double fCountLimit = std::ldexp(1.0, std::numeric_limits<count_t>::digits);
27  if (!std::isfinite(fCount) || fCount >= fCountLimit)
28  return std::nullopt;
29 
30  return static_cast<count_t>(fCount);
31 }
32 
33 } // namespace details
34 
35 template<class function_2d_t>
36 inline double integrate_rectangle_rule(const function_2d_t& func, double x0, double x1, size_t nIntervalsPer1) noexcept(
37  noexcept(static_cast<double>(func(0.0))))
38 {
39  const auto optIntervals = details::integration_sample_count<size_t>(x1 - x0, nIntervalsPer1);
40  if (!optIntervals)
41  return std::numeric_limits<double>::quiet_NaN();
42 
43  const size_t nIntervals = *optIntervals;
44  if (nIntervals == 0)
45  return 0.0;
46 
47  const double dx = (x1 - x0) / static_cast<double>(nIntervals); //-V203
48  double fTotalArea = 0.0;
49  double x = x0;
50 
51  for (size_t i = 0; i < nIntervals; ++i)
52  {
53  fTotalArea += dx * func(x);
54  x += dx;
55  }
56 
57  return fTotalArea;
58 }
59 
60 template<class function_2d_t>
61 double integrate_trapezoid_rule(const function_2d_t& func, double x0, double x1, size_t nIntervalsPer1) noexcept(
62  noexcept(static_cast<double>(func(0.0))))
63 {
64  const auto optIntervals = details::integration_sample_count<size_t>(x1 - x0, nIntervalsPer1);
65  if (!optIntervals)
66  return std::numeric_limits<double>::quiet_NaN();
67 
68  const size_t nIntervals = *optIntervals;
69  if (nIntervals == 0)
70  return 0.0;
71 
72  const double dx = (x1 - x0) / static_cast<double>(nIntervals); //-V203
73  double fTotalArea = 0.0;
74  double x = x0;
75 
76  for (size_t i = 0; i < nIntervals; ++i)
77  {
78  fTotalArea += dx * (func(x) + func(x + dx)) / 2;
79  x += dx;
80  }
81 
82  return fTotalArea;
83 }
84 
85 template<class function_2d_t>
87  const function_2d_t& func,
88  double x0,
89  double x1,
90  double fMaxSliceError,
91  size_t nIntervalsPer1,
92  size_t nMaxRecursion) noexcept(noexcept(static_cast<double>(func(0.0))))
93 {
94  const auto optIntervals = details::integration_sample_count<size_t>(x1 - x0, nIntervalsPer1);
95  if (!optIntervals)
96  return std::numeric_limits<double>::quiet_NaN();
97 
98  const size_t nIntervals = *optIntervals;
99  if (nIntervals == 0)
100  return 0.0;
101 
102  const double dx = (x1 - x0) / static_cast<double>(nIntervals); //-V203
103  double fTotalArea = 0.0;
104  double x = x0;
105 
106  auto slice_area = make_recursive_lambda(
107  [nMaxRecursion](
108  const auto& slice_area,
109  const function_2d_t& func,
110  double x0,
111  double x1,
112  double max_slice_error,
113  size_t recursionLevel)
114  {
115  const double y0 = func(x0);
116  const double y1 = func(x1);
117  const double xm = (x0 + x1) / 2;
118  const double ym = func(xm);
119 
120  const double fArea12 = (x1 - x0) * (y0 + y1) / 2.0;
121  const double fArea1m = (xm - x0) * (y0 + ym) / 2.0;
122  const double fAream2 = (x1 - xm) * (ym + y1) / 2.0;
123  const double fArea1m2 = fArea1m + fAream2;
124 
125  const double fError = (fArea1m2 - fArea12) / fArea12;
126 
127  ++recursionLevel;
128  if (recursionLevel > nMaxRecursion || std::abs(fError) < max_slice_error)
129  {
130  return fArea1m2;
131  }
132  else
133  {
134  return slice_area(func, x0, xm, max_slice_error, recursionLevel)
135  + slice_area(func, xm, x1, max_slice_error, recursionLevel);
136  }
137  });
138 
139  for (size_t i = 0; i < nIntervals; ++i)
140  {
141  fTotalArea += slice_area(func, x, x + dx, fMaxSliceError, 0);
142  x += dx;
143  }
144 
145  return fTotalArea;
146 }
147 
148 template<class function_2d_t>
150  const function_2d_t& funcIsInside,
151  glm::dvec2 pos0,
152  glm::dvec2 pos1,
153  size_t nPointsPerOneSquare) noexcept(noexcept(static_cast<int>(funcIsInside(0.0, 0.0))))
154 {
155  const double fArea = std::abs(pos1.x - pos0.x) * std::abs(pos1.y - pos0.y);
156 
157  const auto optTotalPoints = details::integration_sample_count<int>(fArea, nPointsPerOneSquare);
158  if (!optTotalPoints)
159  return std::numeric_limits<double>::quiet_NaN();
160 
161  const int nTotalPoints = *optTotalPoints;
162  if (nTotalPoints == 0)
163  return 0.0;
164 
165  int points_inside = 0;
166 
167  std::default_random_engine generator(static_cast<unsigned>(std::time(nullptr)));
168 
169  std::uniform_real_distribution<double> x_dist(std::min(pos0.x, pos1.x), std::max(pos0.x, pos1.x));
170  std::uniform_real_distribution<double> y_dist(std::min(pos0.y, pos1.y), std::max(pos0.y, pos1.y));
171 
172  for (int i = 0; i < nTotalPoints; ++i)
173  {
174  int f = funcIsInside(x_dist(generator), y_dist(generator));
175  if (f > 0)
176  points_inside++;
177  else if (f < 0)
178  points_inside--;
179  }
180 
181  return (static_cast<double>(points_inside) / nTotalPoints) * fArea;
182 }
183 } // namespace qx
double integrate_monte_carlo(const function_2d_t &funcIsInside, glm::dvec2 pos0, glm::dvec2 pos1, size_t nPointsPerOneSquare=1000) noexcept(noexcept(static_cast< int >(funcIsInside(0.0, 0.0))))
Integrate using probabilistic algorithm Monte Carlo.
double integrate_rectangle_rule(const function_2d_t &func, double x0, double x1, size_t nIntervalsPer1=10) noexcept(noexcept(static_cast< double >(func(0.0))))
Integrate using rectangle rule.
Definition: integration.inl:36
double integrate_adaptive_midpoint(const function_2d_t &func, double x0, double x1, double fMaxSliceError, size_t nIntervalsPer1=10, size_t nMaxRecursion=300) noexcept(noexcept(static_cast< double >(func(0.0))))
Integrate using adaptive midpoint.
Definition: integration.inl:86
double integrate_trapezoid_rule(const function_2d_t &func, double x0, double x1, size_t nIntervalsPer1=10) noexcept(noexcept(static_cast< double >(func(0.0))))
Integrate using trapezoid rule.
Definition: integration.inl:61
recursive_lambda< std::decay_t< lambda_t > > make_recursive_lambda(lambda_t &&lambda)
Create lambda that can be called recursively.