HepLib
Loading...
Searching...
No Matches
Lib3_GaussKronrod.h
Go to the documentation of this file.
1// modified version from https://github.com/drjerry/quadpackpp
2// GaussKronrod Rules
3#pragma once
4#include "mpreal.h"
5#include <functional>
6
7/* error return codes */
8#define SUCCESS 0
9#define FAILURE 1
10
11namespace {
12 typedef mpfr::mpreal Real;
13 typedef const mpfr::mpreal & Real_t;
14
15
16 class GaussKronrod {
17 protected:
18 size_t m_; // Gauss-Legendre degree
19 size_t n_; // size of Gauss-Kronrod arrays
20 Real* xgk_; // Gauss-Kronrod abscissae
21 Real* wg_; // Gauss-Legendre weights
22 Real* wgk_; // Gauss-Kronrod weights
23
24 private:
25 Real *coefs; // Chebyshev coefficients
26 Real *zeros; // zeros of Legendre polynomial
27
28 void legendre_zeros();
29 void chebyshev_coefs();
30 void gauss_kronrod_abscissae();
31 void gauss_kronrod_weights();
32
33 Real legendre_err(int deg, Real_t x, Real& err);
34 Real legendre_deriv(int deg, Real_t x);
35 Real chebyshev_series(Real_t x, Real& err);
36 Real chebyshev_series_deriv(Real_t x);
37
38 public:
39 GaussKronrod(size_t m = 10);
40 ~GaussKronrod();
41 size_t size() { return n_; };
42 Real xgk(int k) { return (0 <= k && k < n_) ? xgk_[k] : Real(0); }
43 Real wgk(int k) { return (0 <= k && k < n_) ? wgk_[k] : Real(0); }
44 Real wg(int k) { return (0 <= k && k < n_/2) ? wg_[k] : Real(0); }
45 };
46
47 GaussKronrod::GaussKronrod(size_t m) {
48 m_ = m;
49 n_ = m_ + 1;
50 xgk_ = new Real[n_];
51 wg_ = new Real[n_ / 2];
52 wgk_ = new Real[n_];
53 coefs = new Real[n_ + 1];
54 zeros = new Real[m_ + 2];
55
56 legendre_zeros();
57 chebyshev_coefs();
58 gauss_kronrod_abscissae();
59 gauss_kronrod_weights();
60 }
61
62 GaussKronrod::~GaussKronrod() {
63 delete[] xgk_;
64 delete[] wgk_;
65 delete[] wg_;
66 delete[] coefs;
67 delete[] zeros;
68 }
69
70 void GaussKronrod::legendre_zeros() {
71 Real* temp = new Real[m_+1];
72 zeros[0] = Real(-1);
73 zeros[1] = Real(1);
74 Real delta, epsilon;
75
76 for (int k = 1; k <= m_; ++k) {
77 // loop to locate zeros of P_k interlacing z_0,...,z_k
78 for (int j = 0; j < k; ++j) {
79 // Newton's method for P_k :
80 // initialize solver at midpoint of (z_j, z_{j+1})
81 delta = 1;
82 Real x_j = (zeros[j] + zeros[j+1]) / 2;
83 Real P_k = legendre_err(k, x_j, epsilon);
84 while (abs(P_k) > epsilon && abs(delta) > mpfr::machine_epsilon()) {
85 delta = P_k / legendre_deriv(k, x_j);
86 x_j -= delta;
87 P_k = legendre_err(k, x_j, epsilon);
88 }
89 temp[j] = x_j;
90 }
91
92 // copy roots tmp_0,...,tmp_{k-1} to z_1,...z_k:
93 zeros[k+1] = zeros[k];
94 for (int j = 0; j < k; ++j) zeros[j+1] = temp[j];
95 }
96 delete[] temp;
97 }
98
99 void GaussKronrod::chebyshev_coefs() {
100 size_t ell = (m_ + 1)/2;
101 Real* alpha = new Real[ell+1];
102 Real* f = new Real[ell+1];
103
104 /* Care must be exercised in initalizing the constants in the definitions.
105 * Compilers interpret expressions like "(2*k + 1.0)/(k + 2.0)" as floating
106 * point precision, before casting to Real.
107 */
108 f[1] = Real(m_+1)/Real(2*m_ + 3);
109 alpha[0] = Real(1); // coefficient of T_{m+1}
110 alpha[1] = -f[1];
111
112 for (int k = 2; k <= ell; ++k) {
113 f[k] = f[k-1] * (2*k - 1) * (m_ + k) / (k * (2*m_ + 2*k + 1));
114 alpha[k] = -f[k];
115 for (int i = 1; i < k; ++i) alpha[k] -= f[i] * alpha[k-i];
116 }
117
118 for (int k = 0; k <= ell; ++k) {
119 coefs[m_ + 1 - 2*k] = alpha[k];
120 if (m_ >= 2*k) coefs[m_ - 2*k] = Real(0);
121 }
122
123 delete[] alpha;
124 delete[] f;
125 }
126
127 void GaussKronrod::gauss_kronrod_weights() {
128 Real err;
129 // Gauss-Legendre weights:
130 for (int k = 0; k < n_ / 2; ++k) {
131 Real x = xgk_[2*k + 1];
132 wg_[k] = (Real(-2) / ((m_ + 1) * legendre_deriv(m_, x) * legendre_err(m_+1, x, err)));
133 }
134
135 /* The ratio of leading coefficients of P_n and T_{n+1} is computed
136 * from the recursive formulae for the respective polynomials.
137 */
138 Real F_m = Real(2) / Real(2*m_ + 1);
139 for (int k = 1; k <= m_; ++k) F_m *= (Real(2*k) / Real(2*k - 1));
140
141 // Gauss-Kronrod weights:
142 for (size_t k = 0; k < n_; ++k) {
143 Real x = xgk_[k];
144 if (k % 2 == 0) {
145 wgk_[k] = F_m / (legendre_err(m_, x, err) * chebyshev_series_deriv(x));
146 } else {
147 wgk_[k] = (wg_[k/2] + F_m / (legendre_deriv(m_, x) * chebyshev_series(x, err)));
148 }
149 }
150 }
151
152 void GaussKronrod::gauss_kronrod_abscissae() {
153 Real delta, epsilon;
154
155 for (int k = 0; 2*k < n_; ++k) {
156 delta = 1;
157 // Newton's method for E_{n+1} :
158 Real x_k = (zeros[m_-k] + zeros[m_+1-k])/Real(2);
159 Real E = chebyshev_series(x_k, epsilon);
160 while (abs(E) > epsilon && abs(delta) > mpfr::machine_epsilon()) {
161 delta = E / chebyshev_series_deriv(x_k);
162 x_k -= delta;
163 E = chebyshev_series(x_k, epsilon);
164 }
165 xgk_[2*k] = x_k;
166 // copy adjacent Legendre-zero into the array:
167 if (2*k+1 < n_) xgk_[2*k+1] = zeros[m_-k];
168 }
169 }
170
171 Real GaussKronrod::legendre_err(int n, Real_t x, Real& err) {
172 if (n == 0) {
173 err = Real(0);
174 return Real(1);
175 }
176 else if (n == 1) {
177 err = Real(0);
178 return x;
179 }
180
181 Real P0 = Real(1), P1 = x, P2;
182 Real E0 = mpfr::machine_epsilon();
183 Real E1 = abs(x) * mpfr::machine_epsilon();
184 for (int k = 1; k < n; ++k) {
185 P2 = ((2*k + 1) * x * P1 - k * P0) / (k + 1);
186 err = ((2*k + 1) * abs(x) * E1 + k * E0) / (2*(k + 1));
187 P0 = P1; P1 = P2;
188 E0 = E1; E1 = err;
189 }
190 return P2;
191 }
192
193 Real GaussKronrod::legendre_deriv(int n, Real_t x) {
194 if (n == 0) return Real(0);
195 else if (n == 1) return Real(1);
196
197 Real P0 = Real(1), P1 = x, P2;
198 Real dP0 = Real(0), dP1 = Real(1), dP2;
199 for (int k = 1; k < n; ++k) {
200 P2 = ((2*k + 1) * x * P1 - k * P0) / (k + Real(1));
201 dP2 = (2*k + 1) * P1 + dP0;
202 P0 = P1; P1 = P2;
203 dP0 = dP1; dP1 = dP2;
204 }
205 return dP2;
206 }
207
208 Real GaussKronrod::chebyshev_series(Real_t x, Real& err) {
209 Real d1(0), d2(0);
210 Real absc = abs(coefs[0]); // final term for truncation error
211 Real y2 = 2 * x; // linear term for Clenshaw recursion
212
213 for (int k = n_; k >= 1; --k) {
214 Real temp = d1;
215 d1 = y2 * d1 - d2 + coefs[k];
216 d2 = temp;
217 absc += abs(coefs[k]);
218 }
219
220 err = absc * mpfr::machine_epsilon();
221 return x * d1 - d2 + coefs[0]/2;
222 }
223
224 Real GaussKronrod::chebyshev_series_deriv(Real_t x) {
225 Real d1(0), d2(0);
226 Real y2 = 2 * x; // linear term for Clenshaw recursion
227
228 for (int k = n_; k >= 2; --k) {
229 Real temp = d1;
230 d1 = y2 * d1 - d2 + k * coefs[k];
231 d2 = temp;
232 }
233
234 return y2 * d1 - d2 + coefs[1];
235 }
236
237}