Coverage Report

Created: 2025-09-04 07:11

/src/quantlib/ql/math/integrals/kronrodintegral.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- mode: c++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
3
/*
4
 Copyright (C) 2006 François du Vignaud
5
6
 This file is part of QuantLib, a free-software/open-source library
7
 for financial quantitative analysts and developers - http://quantlib.org/
8
9
 QuantLib is free software: you can redistribute it and/or modify it
10
 under the terms of the QuantLib license.  You should have received a
11
 copy of the license along with this program; if not, please email
12
 <quantlib-dev@lists.sf.net>. The license is also available online at
13
 <https://www.quantlib.org/license.shtml>.
14
15
 This program is distributed in the hope that it will be useful, but WITHOUT
16
 ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
17
 FOR A PARTICULAR PURPOSE.  See the license for more details.
18
*/
19
20
#include <ql/math/integrals/kronrodintegral.hpp>
21
#include <ql/types.hpp>
22
23
namespace QuantLib {
24
25
    static Real rescaleError(Real err,
26
                             const Real resultAbs,
27
0
                             const Real resultAsc) {
28
0
        err = std::fabs(err) ;
29
0
        if (resultAsc != 0 && err != 0){
30
0
            Real scale = std::pow((200 * err / resultAsc), 1.5) ;
31
0
            if (scale < 1)
32
0
                err = resultAsc * scale ;
33
0
            else
34
0
                err = resultAsc ;
35
0
            }
36
0
        if (resultAbs > QL_MIN_POSITIVE_REAL / (50 * QL_EPSILON )){
37
0
            Real min_err = 50 * QL_EPSILON  * resultAbs ;
38
0
            if (min_err > err)
39
0
                err = min_err ;
40
0
            }
41
0
        return err ;
42
0
    }
43
44
    /* Gauss-Kronrod-Patterson quadrature coefficients for use in
45
    quadpack routine qng. These coefficients were calculated with
46
    101 decimal digit arithmetic by L. W. Fullerton, Bell Labs, Nov
47
    1981. */
48
49
    /* x1, abscissae common to the 10-, 21-, 43- and 87-point rule */
50
    static const Real x1[5] = {
51
        0.973906528517171720077964012084452,
52
        0.865063366688984510732096688423493,
53
        0.679409568299024406234327365114874,
54
        0.433395394129247190799265943165784,
55
        0.148874338981631210884826001129720
56
        } ;
57
58
    /* w10, weights of the 10-point formula */
59
    static const Real w10[5] = {
60
        0.066671344308688137593568809893332,
61
        0.149451349150580593145776339657697,
62
        0.219086362515982043995534934228163,
63
        0.269266719309996355091226921569469,
64
        0.295524224714752870173892994651338
65
        } ;
66
67
    /* x2, abscissae common to the 21-, 43- and 87-point rule */
68
    static const Real x2[5] = {
69
        0.995657163025808080735527280689003,
70
        0.930157491355708226001207180059508,
71
        0.780817726586416897063717578345042,
72
        0.562757134668604683339000099272694,
73
        0.294392862701460198131126603103866
74
        } ;
75
76
    /* w21a, weights of the 21-point formula for abscissae x1 */
77
    static const Real w21a[5] = {
78
        0.032558162307964727478818972459390,
79
        0.075039674810919952767043140916190,
80
        0.109387158802297641899210590325805,
81
        0.134709217311473325928054001771707,
82
        0.147739104901338491374841515972068
83
        } ;
84
85
    /* w21b, weights of the 21-point formula for abscissae x2 */
86
    static const Real w21b[6] = {
87
        0.011694638867371874278064396062192,
88
        0.054755896574351996031381300244580,
89
        0.093125454583697605535065465083366,
90
        0.123491976262065851077958109831074,
91
        0.142775938577060080797094273138717,
92
        0.149445554002916905664936468389821
93
        } ;
94
95
    /* x3, abscissae common to the 43- and 87-point rule */
96
    static const Real x3[11] = {
97
        0.999333360901932081394099323919911,
98
        0.987433402908088869795961478381209,
99
        0.954807934814266299257919200290473,
100
        0.900148695748328293625099494069092,
101
        0.825198314983114150847066732588520,
102
        0.732148388989304982612354848755461,
103
        0.622847970537725238641159120344323,
104
        0.499479574071056499952214885499755,
105
        0.364901661346580768043989548502644,
106
        0.222254919776601296498260928066212,
107
        0.074650617461383322043914435796506
108
        } ;
109
110
    /* w43a, weights of the 43-point formula for abscissae x1, x3 */
111
    static const Real w43a[10] = {
112
        0.016296734289666564924281974617663,
113
        0.037522876120869501461613795898115,
114
        0.054694902058255442147212685465005,
115
        0.067355414609478086075553166302174,
116
        0.073870199632393953432140695251367,
117
        0.005768556059769796184184327908655,
118
        0.027371890593248842081276069289151,
119
        0.046560826910428830743339154433824,
120
        0.061744995201442564496240336030883,
121
        0.071387267268693397768559114425516
122
        } ;
123
124
    /* w43b, weights of the 43-point formula for abscissae x3 */
125
    static const Real w43b[12] = {
126
        0.001844477640212414100389106552965,
127
        0.010798689585891651740465406741293,
128
        0.021895363867795428102523123075149,
129
        0.032597463975345689443882222526137,
130
        0.042163137935191811847627924327955,
131
        0.050741939600184577780189020092084,
132
        0.058379395542619248375475369330206,
133
        0.064746404951445885544689259517511,
134
        0.069566197912356484528633315038405,
135
        0.072824441471833208150939535192842,
136
        0.074507751014175118273571813842889,
137
        0.074722147517403005594425168280423
138
        } ;
139
140
    /* x4, abscissae of the 87-point rule */
141
    static const Real x4[22] = {
142
        0.999902977262729234490529830591582,
143
        0.997989895986678745427496322365960,
144
        0.992175497860687222808523352251425,
145
        0.981358163572712773571916941623894,
146
        0.965057623858384619128284110607926,
147
        0.943167613133670596816416634507426,
148
        0.915806414685507209591826430720050,
149
        0.883221657771316501372117548744163,
150
        0.845710748462415666605902011504855,
151
        0.803557658035230982788739474980964,
152
        0.757005730685495558328942793432020,
153
        0.706273209787321819824094274740840,
154
        0.651589466501177922534422205016736,
155
        0.593223374057961088875273770349144,
156
        0.531493605970831932285268948562671,
157
        0.466763623042022844871966781659270,
158
        0.399424847859218804732101665817923,
159
        0.329874877106188288265053371824597,
160
        0.258503559202161551802280975429025,
161
        0.185695396568346652015917141167606,
162
        0.111842213179907468172398359241362,
163
        0.037352123394619870814998165437704
164
        } ;
165
166
    /* w87a, weights of the 87-point formula for abscissae x1, x2, x3 */
167
    static const Real w87a[21] = {
168
        0.008148377384149172900002878448190,
169
        0.018761438201562822243935059003794,
170
        0.027347451050052286161582829741283,
171
        0.033677707311637930046581056957588,
172
        0.036935099820427907614589586742499,
173
        0.002884872430211530501334156248695,
174
        0.013685946022712701888950035273128,
175
        0.023280413502888311123409291030404,
176
        0.030872497611713358675466394126442,
177
        0.035693633639418770719351355457044,
178
        0.000915283345202241360843392549948,
179
        0.005399280219300471367738743391053,
180
        0.010947679601118931134327826856808,
181
        0.016298731696787335262665703223280,
182
        0.021081568889203835112433060188190,
183
        0.025370969769253827243467999831710,
184
        0.029189697756475752501446154084920,
185
        0.032373202467202789685788194889595,
186
        0.034783098950365142750781997949596,
187
        0.036412220731351787562801163687577,
188
        0.037253875503047708539592001191226
189
        } ;
190
191
    /* w87b, weights of the 87-point formula for abscissae x4    */
192
    static const Real w87b[23] = {
193
        0.000274145563762072350016527092881,
194
        0.001807124155057942948341311753254,
195
        0.004096869282759164864458070683480,
196
        0.006758290051847378699816577897424,
197
        0.009549957672201646536053581325377,
198
        0.012329447652244853694626639963780,
199
        0.015010447346388952376697286041943,
200
        0.017548967986243191099665352925900,
201
        0.019938037786440888202278192730714,
202
        0.022194935961012286796332102959499,
203
        0.024339147126000805470360647041454,
204
        0.026374505414839207241503786552615,
205
        0.028286910788771200659968002987960,
206
        0.030052581128092695322521110347341,
207
        0.031646751371439929404586051078883,
208
        0.033050413419978503290785944862689,
209
        0.034255099704226061787082821046821,
210
        0.035262412660156681033782717998428,
211
        0.036076989622888701185500318003895,
212
        0.036698604498456094498018047441094,
213
        0.037120549269832576114119958413599,
214
        0.037334228751935040321235449094698,
215
        0.037361073762679023410321241766599
216
        } ;
217
218
0
     void GaussKronrodNonAdaptive::setRelativeAccuracy(Real relativeAccuracy) { 
219
0
        relativeAccuracy_ = relativeAccuracy;
220
0
    }
221
 
222
 
223
0
    Real GaussKronrodNonAdaptive::relativeAccuracy() const {
224
0
        return relativeAccuracy_;
225
0
    }
226
227
    GaussKronrodNonAdaptive::GaussKronrodNonAdaptive(Real absoluteAccuracy,
228
                                                     Size maxEvaluations,
229
                                                     Real relativeAccuracy)
230
0
    : Integrator(absoluteAccuracy, maxEvaluations),
231
0
      relativeAccuracy_(relativeAccuracy) {}
232
233
    Real
234
    GaussKronrodNonAdaptive::integrate(const std::function<Real (Real)>& f,
235
                                       Real a,
236
0
                                       Real b) const {
237
0
        Real result;
238
        //Size neval;
239
0
        Real fv1[5], fv2[5], fv3[5], fv4[5];
240
0
        Real savfun[21];  /* array of function values which have been computed */
241
0
        Real res10, res21, res43, res87;    /* 10, 21, 43 and 87 point results */
242
0
        Real err;
243
0
        Real resAbs; /* approximation to the integral of abs(f) */
244
0
        Real resasc; /* approximation to the integral of abs(f-i/(b-a)) */
245
0
        int k ;
246
247
0
        QL_REQUIRE(a<b, "b must be greater than a)");
248
249
0
        const Real halfLength = 0.5 * (b - a);
250
0
        const Real center = 0.5 * (b + a);
251
0
        const Real fCenter = f(center);
252
253
        // Compute the integral using the 10- and 21-point formula.
254
255
0
        res10 = 0;
256
0
        res21 = w21b[5] * fCenter;
257
0
        resAbs = w21b[5] * std::fabs(fCenter);
258
259
0
        for (k = 0; k < 5; k++) {
260
0
            Real abscissa = halfLength * x1[k];
261
0
            Real fval1 = f(center + abscissa);
262
0
            Real fval2 = f(center - abscissa);
263
0
            Real fval = fval1 + fval2;
264
0
            res10 += w10[k] * fval;
265
0
            res21 += w21a[k] * fval;
266
0
            resAbs += w21a[k] * (std::fabs(fval1) + std::fabs(fval2));
267
0
            savfun[k] = fval;
268
0
            fv1[k] = fval1;
269
0
            fv2[k] = fval2;
270
0
        }
271
272
0
        for (k = 0; k < 5; k++) {
273
0
            Real abscissa = halfLength * x2[k];
274
0
            Real fval1 = f(center + abscissa);
275
0
            Real fval2 = f(center - abscissa);
276
0
            Real fval = fval1 + fval2;
277
0
            res21 += w21b[k] * fval;
278
0
            resAbs += w21b[k] * (std::fabs(fval1) + std::fabs(fval2));
279
0
            savfun[k + 5] = fval;
280
0
            fv3[k] = fval1;
281
0
            fv4[k] = fval2;
282
0
        }
283
284
0
        result = res21 * halfLength;
285
0
        resAbs *= halfLength ;
286
0
        Real mean = 0.5 * res21;
287
0
        resasc = w21b[5] * std::fabs(fCenter - mean);
288
289
0
        for (k = 0; k < 5; k++)
290
0
            resasc += (w21a[k] * (std::fabs(fv1[k] - mean)
291
0
                        + std::fabs(fv2[k] - mean))
292
0
                        + w21b[k] * (std::fabs(fv3[k] - mean)
293
0
                        + std::fabs(fv4[k] - mean)));
294
295
0
        err = rescaleError ((res21 - res10) * halfLength, resAbs, resasc) ;
296
0
        resasc *= halfLength ;
297
298
        // test for convergence.
299
0
        if (err < absoluteAccuracy() || err < relativeAccuracy() * std::fabs(result)){
300
0
            setAbsoluteError(err);
301
0
            setNumberOfEvaluations(21);
302
0
            return result;
303
0
        }
304
305
        /* compute the integral using the 43-point formula. */
306
307
0
        res43 = w43b[11] * fCenter;
308
309
0
        for (k = 0; k < 10; k++)
310
0
            res43 += savfun[k] * w43a[k];
311
312
0
        for (k = 0; k < 11; k++){
313
0
            Real abscissa = halfLength * x3[k];
314
0
            Real fval = (f(center + abscissa)
315
0
                + f(center - abscissa));
316
0
            res43 += fval * w43b[k];
317
0
            savfun[k + 10] = fval;
318
0
            }
319
320
        // test for convergence.
321
322
0
        result = res43 * halfLength;
323
0
        err = rescaleError ((res43 - res21) * halfLength, resAbs, resasc);
324
325
0
       if (err < absoluteAccuracy() || err < relativeAccuracy() * std::fabs(result)){
326
0
            setAbsoluteError(err);
327
0
            setNumberOfEvaluations(43);
328
0
            return result;
329
0
        }
330
331
        /* compute the integral using the 87-point formula. */
332
333
0
        res87 = w87b[22] * fCenter;
334
335
0
        for (k = 0; k < 21; k++)
336
0
            res87 += savfun[k] * w87a[k];
337
338
0
        for (k = 0; k < 22; k++){
339
0
            Real abscissa = halfLength * x4[k];
340
0
            res87 += w87b[k] * (f(center + abscissa)
341
0
                + f(center - abscissa));
342
0
        }
343
344
        // test for convergence.
345
0
        result = res87 * halfLength ;
346
0
        err = rescaleError ((res87 - res43) * halfLength, resAbs, resasc);
347
348
0
        setAbsoluteError(err);
349
0
        setNumberOfEvaluations(87);
350
0
        return result;
351
0
    }
352
353
    Real
354
    GaussKronrodAdaptive::integrate(const std::function<Real (Real)>& f,
355
                                    Real a,
356
0
                                    Real b) const {
357
0
        return integrateRecursively(f, a, b, absoluteAccuracy());
358
0
    }
359
360
    // weights for 7-point Gauss-Legendre integration
361
    // (only 4 values out of 7 are given as they are symmetric)
362
    static const Real g7w[] = { 0.417959183673469,
363
                                0.381830050505119,
364
                                0.279705391489277,
365
                                0.129484966168870 };
366
    // weights for 15-point Gauss-Kronrod integration
367
    static const Real k15w[] = { 0.209482141084728,
368
                                 0.204432940075298,
369
                                 0.190350578064785,
370
                                 0.169004726639267,
371
                                 0.140653259715525,
372
                                 0.104790010322250,
373
                                 0.063092092629979,
374
                                 0.022935322010529 };
375
    // abscissae (evaluation points)
376
    // for 15-point Gauss-Kronrod integration
377
    static const Real k15t[] = { 0.000000000000000,
378
                                 0.207784955007898,
379
                                 0.405845151377397,
380
                                 0.586087235467691,
381
                                 0.741531185599394,
382
                                 0.864864423359769,
383
                                 0.949107912342758,
384
                                 0.991455371120813 };
385
386
    Real GaussKronrodAdaptive::integrateRecursively(
387
                                    const std::function<Real (Real)>& f,
388
                                    Real a,
389
                                    Real b,
390
0
                                    Real tolerance) const {
391
392
0
            Real halflength = (b - a) / 2;
393
0
            Real center = (a + b) / 2;
394
395
0
            Real g7; // will be result of G7 integral
396
0
            Real k15; // will be result of K15 integral
397
398
0
            Real t, fsum; // t (abscissa) and f(t)
399
0
            Real fc = f(center);
400
0
            g7 = fc * g7w[0];
401
0
            k15 = fc * k15w[0];
402
403
            // calculate g7 and half of k15
404
0
            Integer j, j2;
405
0
            for (j = 1, j2 = 2; j < 4; j++, j2 += 2) {
406
0
                t = halflength * k15t[j2];
407
0
                fsum = f(center - t) + f(center + t);
408
0
                g7  += fsum * g7w[j];
409
0
                k15 += fsum * k15w[j2];
410
0
            }
411
412
            // calculate other half of k15
413
0
            for (j2 = 1; j2 < 8; j2 += 2) {
414
0
                t = halflength * k15t[j2];
415
0
                fsum = f(center - t) + f(center + t);
416
0
                k15 += fsum * k15w[j2];
417
0
            }
418
419
            // multiply by (a - b) / 2
420
0
            g7 = halflength * g7;
421
0
            k15 = halflength * k15;
422
423
            // 15 more function evaluations have been used
424
0
            increaseNumberOfEvaluations(15);
425
426
            // error is <= k15 - g7
427
            // if error is larger than tolerance then split the interval
428
            // in two and integrate recursively
429
0
            if (std::fabs(k15 - g7) < tolerance) {
430
0
                return k15;
431
0
            } else {
432
0
                QL_REQUIRE(numberOfEvaluations()+30 <=
433
0
                           maxEvaluations(),
434
0
                           "maximum number of function evaluations "
435
0
                           "exceeded");
436
0
                return integrateRecursively(f, a, center, tolerance/2)
437
0
                    + integrateRecursively(f, center, b, tolerance/2);
438
0
            }
439
0
        }
440
441
442
    GaussKronrodAdaptive::GaussKronrodAdaptive(Real absoluteAccuracy,
443
                                               Size maxEvaluations)
444
0
    : Integrator(absoluteAccuracy, maxEvaluations) {
445
0
        QL_REQUIRE(maxEvaluations >= 15,
446
0
                   "required maxEvaluations (" << maxEvaluations <<
447
0
                   ") not allowed. It must be >= 15");
448
0
    }
449
}