1 /** Functions for numerical integration (quadrature) and differentiation. 2 3 It is recommended to read the following introduction before using 4 any of the functions in this module, as it discusses features and 5 issues that are common to several of them. 6 7 Integration: 8 9 This module contains several different integration methods, suitable 10 for a wide range of problems. The choice of which method to use 11 depends on several things, such as whether the integration 12 interval is finite or infinite, whether the integrand oscillates, or 13 whether it exhibits other difficult behaviour such as singularities or 14 discontinuities in the integration interval. Choosing the right method 15 can both speed up the calculation and make it more accurate. 16 Therefore, each method below has a short description of which types of 17 integral it is suitable for. 18 19 When you just want to "get things done" without any fuss, 20 use the general-purpose $(D integrate()) function. It tries to select 21 the method which is most likely to succeed for the given interval. 22 23 All the integration functions have a set of input parameters in common: 24 The function to integrate, the limits of the integral, and some accuracy 25 requirements. All of these are described in the documentation for 26 $(D integrate()). 27 28 Most of the quadrature routines in SciD are ported from from 29 $(LINK2 http://www.netlib.org/quadpack,QUADPACK) 30 which is a well-known FORTRAN package for numerical evaluation of 31 integrals using 32 $(LINK2 http://en.wikipedia.org/wiki/Gaussian_quadrature,Gaussian quadrature). 33 Gaussian quadrature is 34 $(LINK2 http://mathworld.wolfram.com/NumericalIntegration.html,considered) 35 to be the most accurate method available for numerical integration of 36 smooth functions. The QUADPACK integrators are named $(D integrateQ*()) below. 37 38 In addition, SciD has support for 39 $(LINK2 http://mathworld.wolfram.com/DoubleExponentialIntegration.html,double exponential integration). 40 The DE algorithms are ported from Takuya Ooura's 41 $(LINK2 http://www.kurims.kyoto-u.ac.jp/~ooura/intde.html,DE-Quadrature package), 42 and are available in this module through the $(D integrateDE*()) functions. 43 44 Differentiation: 45 46 The simplest, but fastest, form of numerical differentiation is using 47 finite differences. The functions in this module use three different 48 finite-difference formulas: Forward differences, 49 --- 50 df f(x+h) - f(x) 51 -- = ------------- 52 dx h 53 --- 54 backward differences, 55 --- 56 df f(x) - f(x-h) 57 -- = ------------- 58 dx h 59 --- 60 and central differences, 61 --- 62 df f(x+h) - f(x-h) 63 -- = --------------- 64 dx 2 h 65 --- 66 Of these three, the latter is the most accurate, but in the cases where 67 $(D f(x)) is already known it requires one more function evaluation compared 68 to the forward-/backward-difference formulas. This is of particular 69 importance when one needs to evaluate several derivatives, as is the 70 case with e.g. gradients and Jacobians. 71 72 Some functions in this module, like $(D diff()), use another method by Ridders, 73 which extrapolates the results of finite-difference formulas like the 74 above to make the approximation more accurate – usually a lot more so. 75 This requires several function evaluations and is therefore also a lot 76 slower than simple finite differences. 77 78 All the differentiation methods in this module take an optional 79 $(D scale) parameter. This is a "characteristic scale" for the function, 80 i.e. a scale over which the function changes significantly. It is 81 worth experimenting a bit with this parameter, as it can have a 82 drastic impact on accuracy. (For an extreme example, see the "Example" 83 section for the $(D diff()) function below.) For practical reasons, $(D 1.0) is 84 chosen as the default scale in this module. Another, probably more 85 common, choice that sometimes works well is to set $(D scale = x), where 86 $(D x) is the point at which the derivative is taken. 87 88 89 Authors: Lars Tandle Kyllingstad 90 Copyright: Copyright (c) 2009-2010, Lars T. Kyllingstad. All rights reserved. 91 License: Boost License 1.0 92 Macros: 93 D = <b><code>$0</code></b> 94 INFTY = ∞ 95 SUP = <sup>$0</sup> 96 */ 97 module scid.calculus; 98 99 100 import std.algorithm; 101 import std.conv; 102 import std.math; 103 import std.traits; 104 105 import scid.core.fortran; 106 import scid.core.memory; 107 import scid.core.traits; 108 import scid.internal.calculus.integrate_qng; 109 import scid.ports.intde.intde1; 110 import scid.ports.quadpack.qage; 111 import scid.ports.quadpack.qagie; 112 import scid.ports.quadpack.qagpe; 113 import scid.ports.quadpack.qagse; 114 import scid.ports.quadpack.qawce; 115 import scid.ports.quadpack.qawfe; 116 import scid.ports.quadpack.qawoe; 117 import scid.ports.quadpack.qawse; 118 import scid.matrix; 119 import scid.types; 120 import scid.util; 121 122 version(unittest) 123 { 124 import std.range; 125 import scid.core.testing; 126 } 127 128 129 130 131 /** Integrate a function from $(D a) to $(D b). 132 133 This module contains a lot of different integration routines, and 134 $(D _integrate()) aims to be a simple interface to some of 135 these. Currently, it only calls the $(D integrateQAGS()) and 136 $(D integrateQAGI()) routines, but this may change in the future. 137 In particular, it should take into account whether a function 138 is oscillatory, if it has discontinuities and singularities in 139 the integration interval, and so on. 140 141 Params: 142 f = The function to _integrate. This can be given as a 143 function pointer, a delegate, or a struct/class 144 which defines $(D opCall()). 145 a = The lower limit of integration. 146 b = The upper limit of integration. 147 epsRel = (optional) The requested relative accuracy. 148 epsAbs = (optional) The requested absolute accuracy. 149 150 Note: 151 This function is different from the other $(D integrateXXX()) routines 152 in this module in that it allows $(D a) and $(D b) to take on the special 153 values $(D Real.infinity) and $(D -Real.infinity), where $(D Real) is the 154 floating-point type being used. 155 156 Example: 157 --- 158 // Let's integrate the function 159 // 160 // log(x) 161 // f(x) = ---------- 162 // 2 163 // 1 + 100 x 164 // 165 // from x=0 to x=infinity. 166 167 real f(real x) { return log(x)/(1 + 100*x*x); } 168 auto result = integrate(&f, 0.0L, real.infinity, 0.001L); 169 170 real exact = -PI * log(10.0)/20.0; 171 assert (abs(result.value - exact) < result.error); 172 --- 173 */ 174 Result!Real integrate (Func, Real)(scope Func f, Real a, Real b, 175 Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 176 { 177 if (isFinite(a)) 178 { 179 if (isFinite(b)) 180 return integrateQAGS(f, a, b, epsRel, epsAbs); 181 182 else if (b == Real.infinity) 183 return integrateQAGI(f, a, Infinite.upper, epsRel, epsAbs); 184 185 else if (b == -Real.infinity) 186 return -integrateQAGI(f, a, Infinite.lower, epsRel, epsAbs); 187 } 188 else if (a == -Real.infinity) 189 { 190 if (isFinite(b)) 191 return integrateQAGI(f, b, Infinite.lower, epsRel, epsAbs); 192 193 else if (b == Real.infinity) 194 return integrateQAGI(f, epsRel, epsAbs); 195 196 else if (b == -Real.infinity) 197 return Result!Real(0.0, 0.0); 198 } 199 else if (a == Real.infinity) 200 { 201 if (isFinite(b)) 202 return -integrateQAGI(f, b, Infinite.upper, epsRel, epsAbs); 203 204 else if (b == -Real.infinity) 205 return -integrateQAGI(f, epsRel, epsAbs); 206 207 else if (b == Real.infinity) 208 return Result!Real(0.0, 0.0); 209 } 210 211 // a or b is NaN. 212 return Result!Real(Real.nan, Real.nan); 213 } 214 215 216 unittest 217 { 218 // Tests for when both limits are finite. 219 real logsqrt(real x) { return (x <= 0.0 ? 0.0 : log(x)/sqrt(x)); } 220 auto logsqrtResult = integrate(&logsqrt, 0.0L, 1.0L, 0.0L, 1e-10L); 221 assert (approxEqual(logsqrtResult.value, -4.0L, 0.0L, logsqrtResult.error)); 222 assert (logsqrtResult.error < abs(logsqrtResult.value*1e-10L)); 223 224 225 // Tests for when both limits are infinite. 226 real gauss(real x) 227 { 228 return exp(-x*x); 229 } 230 231 auto gaussResult1 = 232 integrate(&gauss, -real.infinity, real.infinity, 1e-10L); 233 assert (approxEqual(gaussResult1.value, sqrt(PI), 0.0L, gaussResult1.error)); 234 assert (gaussResult1.error < abs(gaussResult1.value*1e-10)); 235 236 auto gaussResult2 = 237 integrate(&gauss, real.infinity, -real.infinity, 1e-10L); 238 assert (approxEqual(gaussResult2.value,-sqrt(PI), 0.0L, gaussResult2.error)); 239 240 auto gaussResult3 = 241 integrate(&gauss, -real.infinity, -real.infinity, 1e-10L); 242 assert (gaussResult3.value == 0.0); 243 244 auto gaussResult4 = 245 integrate(&gauss, real.infinity, real.infinity, 1e-10L); 246 assert (gaussResult4.value == 0.0); 247 248 249 // Tests for when one limit is infinite. 250 real u(real x) { return log( x)/(1 + 100*x*x); } 251 real l(real x) { return log(-x)/(1 + 100*x*x); } 252 253 real ulExact = -PI * log(10.0)/20.0; 254 auto uResult = integrate(&u, 0.0L, real.infinity, 0.001L); 255 auto muResult = integrate(&u, real.infinity, 0.0L, 0.001L); 256 auto lResult = integrate(&l, -real.infinity, 0.0L, 0.001L); 257 auto mlResult = integrate(&l, 0.0L, -real.infinity, 0.001L); 258 259 assert (approxEqual(uResult.value, ulExact, 0.0L, uResult.error)); 260 assert (approxEqual(lResult.value, ulExact, 0.0L, lResult.error)); 261 assert (uResult.value == -muResult.value); 262 assert (lResult.value == -mlResult.value); 263 } 264 265 266 267 268 /** Calculate the integral of $(D f(x)) over the finite interval ($(D a),$(D b)) 269 using a simple 270 non-adaptive automatic integrator, based on a sequence of rules with 271 increasing degree of algebraic precision. 272 273 This method should only be used for well-behaved integrands, or when 274 speed is a lot more important than accuracy. 275 276 Example: 277 --- 278 // Despite the statement above, integrateQNG() can handle some 279 // difficulties, such as the endpoint singularity in the following 280 // example: 281 double f(double x) { return x^^2 * log(1/x); } 282 auto i = integrateQNG(&f, 0.0, 1.0); 283 --- 284 */ 285 Result!Real integrateQNG(Func, Real)(scope Func f, Real a, Real b, 286 Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 287 in 288 { 289 assert (epsAbs > 0 || epsRel >= 50*Real.epsilon, 290 "Requested accuracy is too small."); 291 } 292 body 293 { 294 return scid.internal.calculus.integrate_qng.qng(f, a, b, epsRel, epsAbs); 295 } 296 297 298 unittest 299 { 300 double f(double x) { return x^^2 * log(1/x); } 301 auto result = integrateQNG(&f, 0.0, 1.0); 302 assert (abs(result.value - 1.0/9) <= result.error); 303 } 304 305 306 307 308 // Integration rules for integrateQAG() 309 enum GaussKronrod { rule15 = 1, rule21, rule31, rule41, rule51, rule61 } 310 311 312 313 314 /** Calculate the integral of $(D f(x)) over the finite interval 315 ($(D a),$(D b)) using a simple globally adaptive integrator. 316 317 This method is suitable for functions without singularities 318 or discontinuities which are too difficult for $(D integrateQNG()), 319 and, in particular, for functions with oscillating behaviour of a 320 non-specific type. 321 322 It is possible to choose between 6 pairs of 323 $(LINK2 http://en.wikipedia.org/wiki/Gauss–Kronrod_quadrature_formula, 324 Gauss–Kronrod quadrature formulae) 325 for the rule evaluation component. The pairs of high 326 degree of precision are suitable for handling integration 327 difficulties due to a strongly oscillating integrand, whereas 328 the lower-order rules are more efficient for well-behaved integrands. 329 The $(D rule) parameter may take on the following values, corresponding to 330 15-, 21-, 31-, 41-, 51-, and 61-point Gauss–Kronrod rules: 331 --- 332 enum GaussKronrod { rule15, rule21, rule31, rule41, rule51, rule61 } 333 --- 334 335 Example: 336 --- 337 // Strongly oscillating integrand, better use highest-order rule. 338 double f(double x) { return cos(100 * sin(x)); } 339 auto i = integrateQAG(&f, 0.0, cast(double) PI, GaussKronrod.rule61); 340 --- 341 */ 342 Result!Real integrateQAG(Func, Real) 343 (scope Func f, Real a, Real b, GaussKronrod rule = GaussKronrod.rule31, 344 Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 345 in 346 { 347 assert (epsAbs > 0 || epsRel >= 50*Real.epsilon, 348 "Requested accuracy is too small."); 349 } 350 body 351 { 352 Real result, abserr; 353 int neval, ier, last; 354 355 enum int limit = 500; 356 Real[limit] alist, blist, rlist, elist; 357 int[limit] iord; 358 359 qage(f, a, b, epsAbs, epsRel, rule, limit, result, abserr, 360 neval, ier, alist.ptr, blist.ptr, rlist.ptr, elist.ptr, 361 iord.ptr, last); 362 checkQuadpackStatus(ier, result, abserr); 363 364 return typeof(return)(result, abserr); 365 } 366 367 368 unittest 369 { 370 double f(double x) { return cos(100 * sin(x)); } 371 auto i = integrateQAG(&f, 0.0, cast(double) PI, GaussKronrod.rule61); 372 assert (isAccurate(i.value, i.error, 0.062787400491492695655, 1e-6)); 373 } 374 375 376 377 378 /** Calculate the integral of $(D f(x)) over the finite interval 379 ($(D a),$(D b)) using a general-purpose integration algorithm. 380 381 $(D _integrateQAGS()) is an integrator based on globally adaptive interval 382 subdivision in connection with extrapolation by the Epsilon 383 algorithm, which eliminates the effects of integrand singularities 384 of several types. 385 386 This is the most "intelligent" of the general-purpose finite-interval 387 integration methods, and the one that best handles bad integrand 388 behaviour. It is fairly expensive in terms of processing time, though, 389 so if that is an issue you may want to investigate the integrand 390 in more detail and try one or a combination of the more specialised 391 integration methods. 392 393 Example: 394 --- 395 // This function has an internal singularity at x = 1/3. 396 double f(double x) { return 1/sqrt(abs(x-1.0/3)); } 397 auto i = integrateQAGS(&f, 0.0, 1.0); 398 --- 399 */ 400 Result!Real integrateQAGS(Func, Real)(scope Func f, Real a, Real b, 401 Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 402 in 403 { 404 assert (epsAbs > 0 || epsRel >= 50*Real.epsilon, 405 "Requested accuracy is too small."); 406 } 407 body 408 { 409 Real result, abserr; 410 int neval, ier; 411 int last; 412 413 enum int limit = 500; 414 Real[limit] alist, blist, rlist, elist; 415 int[limit] iord; 416 417 qagse(f, a, b, epsAbs, epsRel, limit, result, abserr, neval, ier, 418 alist.ptr, blist.ptr, rlist.ptr, elist.ptr, iord.ptr, last); 419 checkQuadpackStatus(ier, result, abserr); 420 421 return typeof(return)(result, abserr); 422 } 423 424 425 unittest 426 { 427 double f(double x) { return 1/sqrt(abs(x-1.0/3)); } 428 auto i = integrateQAGS(&f, 0.0, 1.0); 429 double expected = 2 * (sqrt(2.0/3) + sqrt(1.0/3)); 430 assert (isAccurate(i, expected, 1e-8)); 431 } 432 433 434 435 436 /** Calculate the integral of $(D f(x)) over the finite interval ($(D a),$(D b)), 437 taking into account known points of special difficulty 438 inside the interval. 439 440 This routine uses the same integration method as $(D integrateQAGS()), 441 but allows you to specify an array of points where the 442 integrand has internal singularities, discontinuities or 443 other types of bad behaviour. 444 */ 445 Result!Real integrateQAGP(Func, Real)(scope Func f, Real a, Real b, 446 Real[] trouble, Real epsRel = cast(Real) 1e-6, 447 Real epsAbs = cast(Real) 0) 448 in 449 { 450 assert (epsAbs > 0 || epsRel >= 50*Real.epsilon, 451 "Requested accuracy is too small."); 452 } 453 body 454 { 455 if (trouble.length == 0) return integrateQAGS(f, a, b, epsRel, epsAbs); 456 457 mixin(newFrame); 458 459 Real result, abserr; 460 int neval, ier; 461 int last; 462 463 enum int limit = 500; 464 Real[limit] alist, blist, rlist, elist; 465 int[limit] iord, level; 466 467 // The array passed to qagp must have two unused slots. 468 // Unfortunately, this will sometimes cause an allocation. 469 int npts2 = toInt(trouble.length) + 2; 470 trouble.length = npts2; 471 auto pts = newStack!Real(npts2); 472 auto ndin = newStack!int(npts2); 473 474 qagpe(f, a, b, npts2, trouble.ptr, epsAbs, epsRel, limit, 475 result, abserr, neval, ier, 476 alist.ptr, blist.ptr, rlist.ptr, elist.ptr, pts.ptr, 477 iord.ptr, level.ptr, ndin.ptr, last); 478 checkQuadpackStatus(ier, result, abserr); 479 480 return typeof(return)(result, abserr); 481 } 482 483 484 unittest 485 { 486 real f(real x) { return abs(x - PI/4)^^(-0.4L); } 487 auto i = integrateQAGP(&f, 0.0L, 1.0L, [PI/4], 1e-8L); 488 auto expect = ((1-PI/4)^^0.6L + (PI/4)^^0.6L)/0.6L; 489 assert (isAccurate(i, expect, 1e-8L)); 490 } 491 492 493 494 495 // Integration ranges for integrateQAGI() 496 enum Infinite { upper = 1, lower = -1 } 497 498 499 500 501 /** Calculate the integral of $(D f(x)) over an infinite interval. 502 503 The infinite range is mapped onto a finite interval and subsequently 504 the same strategy as in $(D integrateQAGS()) is applied. 505 506 To integrate $(D f(x)) over the interval (-$(INFTY),$(INFTY)), use the 507 first form. To integrate $(D f(x)) over the interval (-$(INFTY),$(D a)) or 508 ($(D a),$(INFTY)) use the second form with $(D inf=Infinite.lower) or 509 $(D inf=Infinite.upper), respectively. 510 511 Example: 512 --- 513 // Slowly convergent integral over infinite interval, 514 // integrand with endpoint singularity. 515 double f(double x) { return (1 + 10*x)^^(-2) / sqrt(x); } 516 auto i = integrateQAGI(&f, 0.0, Infinite.upper, 1e-8); 517 --- 518 */ 519 Result!Real integrateQAGI(Func, Real) 520 (scope Func f, Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 521 { 522 return integrateQAGI(f, Real.init, cast(Infinite) 2, epsRel, epsAbs); 523 } 524 525 526 /// ditto 527 Result!Real integrateQAGI(Func, Real)(scope Func f, Real a, Infinite inf, 528 Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 529 in 530 { 531 assert (epsAbs > 0 || epsRel >= 50*Real.epsilon, 532 "Requested accuracy is too small."); 533 } 534 body 535 { 536 Real result, abserr; 537 int neval, ier, last; 538 539 enum int limit = 500; 540 Real[limit] alist, blist, rlist, elist; 541 int[limit] iord; 542 543 qagie(f, a, inf, epsAbs, epsRel, limit, result, abserr, neval, ier, 544 alist.ptr, blist.ptr, rlist.ptr, elist.ptr, iord.ptr, last); 545 checkQuadpackStatus(ier, result, abserr); 546 547 return typeof(return)(result, abserr); 548 } 549 550 551 unittest 552 { 553 double f(double x) { return (1 + 10*x)^^(-2) / sqrt(x); } 554 auto i = integrateQAGI(&f, 0.0, Infinite.upper); 555 double expected = PI / (2 * sin(PI/2)) / sqrt(10.0); 556 assert (isAccurate(i.value, i.error, expected, 1e-6)); 557 } 558 559 560 561 562 /** Calculate the integral of an oscillatory function over the 563 finite interval ($(D a),$(D b)). 564 565 Use this to calculate the integral of $(D f(x)*cos(omega*x)) 566 or $(D f(x)*sin(omega*x)) 567 where $(D f(x)) is the (possibly singular) user-specified function 568 and $(D omega) is a known constant. The weight function is specified 569 by setting $(D weight) to $(D Oscillation.cos) or $(D Oscillation.sin). 570 571 The rule evaluation component is based on the modified 572 $(LINK2 http://en.wikipedia.org/wiki/Clenshaw%E2%80%93Curtis_quadrature, 573 Clenshaw–Curtis technique). 574 An adaptive subdivision scheme is used in connection with 575 an extrapolation procedure, which is a modification of that in 576 $(D integrateQAGS()) and allows the algorithm to deal with 577 singularities in $(D f(x)). 578 579 See_Also: 580 $(D integrateQAWF()), for similar integrals over an infinite interval. 581 582 Example: 583 --- 584 // Integrate exp(20*(x-1))*sin(256*x) over the interval (0,1) 585 real f(real x) { return exp(20*(x-1)); } 586 auto i = integrateQAWO(&f, 0.0L, 1.0L, 256.0L, Oscillation.sin, 1e-15L); 587 --- 588 */ 589 Result!Real integrateQAWO(Func, Real)(scope Func f, Real a, Real b, Real omega, 590 Oscillation weight, Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 591 in 592 { 593 assert (epsAbs > 0 || epsRel >= 50*Real.epsilon, 594 "Requested accuracy is too small."); 595 } 596 body 597 { 598 Real result, abserr; 599 int neval, ier, last, momcom; 600 601 enum limit = 500; 602 enum icall = 1; 603 enum maxp1 = 21; 604 Real[limit] alist, blist, rlist, elist; 605 int[limit] iord, nnlog; 606 Real[maxp1*25] chebmo; 607 608 qawoe!(Real, Func)(f, a, b, omega, weight, epsAbs, epsRel, limit, 609 icall, maxp1, result, abserr, neval, ier, last, 610 alist.ptr, blist.ptr, rlist.ptr, elist.ptr, iord.ptr, nnlog.ptr, 611 momcom, chebmo.ptr); 612 checkQuadpackStatus(ier, result, abserr); 613 614 return typeof(return)(result, abserr); 615 } 616 617 618 enum Oscillation { cos = 1, sin = 2 } 619 620 621 unittest 622 { 623 enum eps = 1e-15L; 624 real f(real x) { return exp(20*(x-1)); } 625 auto i = integrateQAWO(&f, 0.0L, 1.0L, 256.0L, Oscillation.sin, eps); 626 real expect = (20*sin(256.0L) - 256*cos(256.0L) + 256*exp(-20.0L))/(400+65536); 627 assert (isAccurate(i, expect, eps)); 628 } 629 630 631 632 633 /** Calculates a Fourier transform integral. 634 635 Use this to calculate the integral of $(D f(x)*cos(omega*x)) 636 or $(D f(x)*sin(omega*x)) 637 on the interval ($(D a),$(INFTY)). The weight function is specified 638 by setting $(D weight) to $(D Oscillation.cos) or $(D Oscillation.sin). 639 640 The procedure of $(D integrateQAWO()) is applied on successive finite 641 intervals, and convergence acceleration by means of the Epsilon 642 algorithm is applied to the series of integral approximations. 643 644 Note: 645 This function is unique among the QUADPACK integrators in that 646 you must specify an absolute accuracy, not a relative one. 647 648 See_Also: 649 $(D integrateQAWO()), for similar integrals over a finite interval. 650 651 Example: 652 --- 653 // Calculate the integral of cos(x)*exp(-x/64)/sqrt(x) over 654 // the interval (0, infinity). 655 real f(real x) { return x <= 0 ? 0 : exp(-x/64) / sqrt(x); } 656 auto i = integrateQAWF(&f, 0.0L, 1.0L, Oscillation.cos, 1e-15L); 657 --- 658 */ 659 Result!Real integrateQAWF(Func, Real)(scope Func f, Real a, Real omega, 660 Oscillation weight, Real epsAbs) 661 in 662 { 663 assert (epsAbs > 0, "Requested accuracy is too small."); 664 } 665 body 666 { 667 Real result, abserr; 668 int neval, ier, lst; 669 670 enum limit = 500; 671 enum limlst = 50; 672 enum maxp1 = 21; 673 Real[limit] alist, blist, rlist, elist; 674 Real[limlst] rslst, erlst; 675 int[limit] iord, nnlog; 676 int[limlst] ierlst; 677 Real[maxp1*25] chebmo; 678 679 qawfe!(Real, Func)(f, a, omega, weight, epsAbs, limlst, limit, 680 maxp1, result, abserr, neval, ier, rslst.ptr, erlst.ptr, ierlst.ptr, 681 lst, alist.ptr, blist.ptr, rlist.ptr, elist.ptr, iord.ptr, nnlog.ptr, 682 chebmo.ptr); 683 684 // QAWF has slightly different error codes, so we can't 685 // use checkQuadpackStatus() 686 string msg, extra; 687 switch(ier) 688 { 689 case 0: 690 return typeof(return)(result, abserr); 691 case 1: 692 msg = "The maximum number of cycles allowed has been reached"; 693 break; 694 case 4: 695 msg = "Convergence acceleration algorithm failed to reach the " 696 ~"desired accuracy"; 697 break; 698 case 6: 699 msg = "Invalid input"; 700 extra = "If you get this error message, it is a bug in SciD. " 701 ~ "Please report."; 702 break; 703 case 7: 704 msg = "Bad integrand behaviour within one or more of the cycles."; 705 break; 706 default: 707 msg = "Invalid QUADPACK error code"; 708 extra = "If you get this error message, it is a bug in SciD. " 709 ~ "Please report."; 710 } 711 if (extra.length == 0) 712 { 713 extra = "There may be local integration difficulties " 714 ~"(singularities, discontinuities, etc.). If the position " 715 ~"of such a difficulty can be determined, try to split " 716 ~"up the interval at this point and call the appropriate " 717 ~"integrators on the subintervals."; 718 } 719 throw new IntegrationException(msg, extra, result, abserr); 720 } 721 722 723 unittest 724 { 725 enum eps = 1e-14L; 726 real f(real x) { return x <= 0 ? 0 : exp(-x/64) / sqrt(x); } 727 auto i = integrateQAWF(&f, 0.0L, 1.0L, Oscillation.cos, eps); 728 real expect = sqrt(PI) * (1 + 1.0L/4096)^^(-0.25L) * cos(atan(64.0L)/2); 729 assert (isAccurate(i, expect, 0.0L, eps)); 730 } 731 732 733 734 735 // Weight functions for integrateQAWS() 736 enum Weight { unity = 1, logxa = 2, logbx = 3, logab = 4 } 737 738 739 /** Calculate an integral over the finite interval ($(D a),$(D b)), 740 where the integrand has algebraic and/or logarithmic endpoint 741 singularities of a known type. 742 743 The integrand is taken to be on the form 744 --- 745 alpha beta 746 f(x) (x - a) (b - x) w(x) 747 --- 748 where $(D f(x)) is the given function and $(D w(x)) is 749 specified by setting the $(D weight) parameter to one of the following: 750 --- 751 Weight.unity: w(x) = 1 752 Weight.logxa: w(x) = log(x-a) 753 Weight.logbx: w(x) = log(b-x) 754 Weight.logab: w(x) = log(x-a) log(b-x) 755 --- 756 757 A globally adaptive subdivision strategy is applied, 758 with modified 759 $(LINK2 http://en.wikipedia.org/wiki/Clenshaw%E2%80%93Curtis_quadrature, 760 Clenshaw–Curtis integration) on those subintervals 761 which contain $(D a) or $(D b). 762 763 Example: 764 --- 765 // Calculate the integral of 1/(sqrt(1-x^^2) * (x+1.5)). 766 // Another way to write this integrand is 767 // (x-(-1))^^(-0.5) * (1-x)^^0.5 / (x+1.5), 768 // so we set alpha = beta = -0.5. 769 real f(real x) { return 1/(x + 1.5L); } 770 auto i = integrateQAWS(&f, -1.0L, 1.0L, -0.5L, -0.5L, Weight.unity, 1e-15L); 771 --- 772 */ 773 Result!Real integrateQAWS(Func, Real)(scope Func f, Real a, Real b, 774 Real alpha, Real beta, Weight weight, 775 Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 776 in 777 { 778 assert (epsAbs > 0 || epsRel >= 50*Real.epsilon, 779 "Requested accuracy is too small."); 780 } 781 body 782 { 783 Real result, abserr; 784 int neval, ier, last; 785 786 enum int limit = 500; 787 Real[limit] alist, blist, rlist, elist; 788 int[limit] iord; 789 790 int sign = 1; 791 if (b <= a) 792 { 793 if (b == a) return typeof(return)(0, 0); 794 swap(a, b); 795 sign = -1; 796 } 797 798 qawse(f, a, b, alpha, beta, weight, epsAbs, epsRel, limit, 799 result, abserr, neval, ier, 800 alist.ptr, blist.ptr, rlist.ptr, elist.ptr, iord.ptr, last); 801 checkQuadpackStatus(ier, result, abserr); 802 803 return typeof(return)(sign*result, abserr); 804 } 805 806 807 unittest 808 { 809 enum eps = 1e-15L; 810 real f(real x) { return 1/(x + 1.5L); } 811 auto i = integrateQAWS(&f, 1.0L, -1.0L, -0.5L, -0.5L, Weight.unity, eps); 812 auto expected = -PI / sqrt(1.5L^^2 - 1); 813 assert (isAccurate(i, expected, eps)); 814 } 815 816 817 818 819 /** Calculate a Cauchy principal value integral. 820 821 Use this to calculate the integral of $(D f(x)/(x-c)) over the finite 822 interval ($(D a),$(D b)), where $(D f(x)) is smooth on the entire 823 interval and $(D c) is not one of the endpoints. 824 825 The strategy is globally adaptive. Modified 826 $(LINK2 http://en.wikipedia.org/wiki/Clenshaw%E2%80%93Curtis_quadrature, 827 Clenshaw–Curtis integration) is used on those intervals containing the point 828 $(D x = c). 829 830 Example: 831 --- 832 // Integrate cos(x-1)/(x-1) over the interval (0,3) 833 real f(real x) { return cos(x-1); } 834 auto i = integrateQAWC(&f, 0.0L, 3.0L, 1.0L, 1e-15L); 835 --- 836 */ 837 Result!Real integrateQAWC(Func, Real)(scope Func f, Real a, Real b, Real c, 838 Real epsRel = cast(Real) 1e-6, Real epsAbs = cast(Real) 0) 839 in 840 { 841 assert (epsAbs > 0 || epsRel >= 50*Real.epsilon, 842 "Requested accuracy is too small."); 843 assert (c != a && c != b, "Singularity at interval endpoint."); 844 } 845 body 846 { 847 Real result, abserr; 848 int neval, ier, last; 849 850 enum limit = 500; 851 Real[limit] alist, blist, rlist, elist; 852 int[limit] iord; 853 854 qawce!(Real, Func)(f, a, b, c, epsAbs, epsRel, limit, result, abserr, 855 neval, ier, alist.ptr, blist.ptr, rlist.ptr, elist.ptr, iord.ptr, 856 last); 857 checkQuadpackStatus(ier, result, abserr); 858 859 return typeof(return)(result, abserr); 860 } 861 862 863 unittest 864 { 865 // This is just a simple test of basic functionality. 866 // More thorough unittests are in the scid.ports.quadpack.qawc module. 867 enum eps = 1e-15L; 868 enum expect = 0.085576905873896861036L; 869 static real f(real x) { return cos(x-1); } 870 auto i = integrateQAWC(&f, 0.0L, 3.0L, 1.0L, eps); 871 assert (abs(i.value-expect) < eps*expect && i.error <= eps); 872 } 873 874 875 876 877 /** Calculate the integral of $(D f(x)) over the finite interval 878 ($(D a),$(D b)) using double exponential integration. 879 880 Example: 881 --- 882 double f(double x) { return x^^2 * log(1/x); } 883 auto i = integrateDE(&f, 0.0, 1.0); 884 --- 885 */ 886 Result!Real integrateDE(Func, Real)(scope Func f, Real a, Real b, 887 Real epsRel = cast(Real) 1e-6) 888 { 889 Real result, error; 890 intde(f, a, b, epsRel, &result, &error); 891 if (error < 0) throw new IntegrationException( 892 "Integration failed", 893 "The integrand may have discontinuities, singularities or " 894 ~"oscillatory behaviour in the integration interval.", 895 result, 896 real.nan); 897 return typeof(return)(result, error); 898 } 899 900 901 unittest 902 { 903 double f(double x) { return x^^2 * log(1/x); } 904 auto result = integrateDE(&f, 0.0, 1.0, 1e-7); 905 assert (isAccurate(result.value, result.error, 1.0/9, 1e-6)); 906 } 907 908 909 910 911 /** Calculate the integral of $(D f(x)) over the infinite interval 912 ($(D a),$(INFTY)) using double exponential integration. 913 914 Example: 915 --- 916 double f(double x) { return (1 + 10*x)^^(-2) / sqrt(x); } 917 auto i = integrateDEI(&f, 0.0); 918 --- 919 */ 920 Result!Real integrateDEI(Func, Real)(scope Func f, Real a, 921 Real epsRel = cast(Real) 1e-6) 922 { 923 Real result, error; 924 intdei!Real(f, a, epsRel, &result, &error); 925 if (error < 0) throw new IntegrationException( 926 "Integration failed", 927 "The integrand may have discontinuities, singularities or " 928 ~"oscillatory behaviour in the integration interval.", 929 result, 930 real.nan); 931 return typeof(return)(result, error); 932 } 933 934 935 unittest 936 { 937 double f(double x) { return (1 + 10*x)^^(-2) / sqrt(x); } 938 auto result = integrateDEI(&f, 0.0); 939 double expected = PI / (2 * sin(PI/2)) / sqrt(10.0); 940 assert (matchDigits(result.value, expected, 6)); 941 } 942 943 944 945 946 /** Calculate the integral of an oscillating function $(D f(x)) 947 over the infinite interval ($(D a),$(INFTY)) using double exponential 948 integration. 949 950 $(D f(x)) is assumed to take the form 951 --- 952 f(x) = g(x) * sin(omega * x + theta) 953 --- 954 as $(D x) goes to infinity ($(D theta) is not specified). 955 956 Example: 957 --- 958 real f(real x) { return x <= 0 ? 0 : cos(x) * exp(-x/64) / sqrt(x); } 959 auto i = integrateDEO(&f, 0.0L, 1.0L, 1e-18L); 960 --- 961 */ 962 Result!Real integrateDEO(Func, Real)(scope Func f, Real a, 963 Real omega, Real epsRel = cast(Real) 1e-6) 964 { 965 Real result, error; 966 intdeo(f, a, omega, epsRel, &result, &error); 967 if (error < 0) throw new IntegrationException( 968 "Integration failed", 969 "The integrand may have discontinuities, singularities or " 970 ~"oscillatory behaviour in the integration interval.", 971 result, 972 real.nan); 973 return typeof(return)(result, error); 974 } 975 976 977 unittest 978 { 979 real f(real x) { return x <= 0 ? 0 : cos(x) * exp(-x/64) / sqrt(x); } 980 auto i = integrateDEO(&f, 0.0L, 1.0L, 1e-18L); 981 real expect = sqrt(PI) * (1 + 1.0L/4096)^^(-0.25L) * cos(atan(64.0L)/2); 982 assert (matchDigits(i.value, expect, 18)); 983 } 984 985 986 987 988 /** Exception thrown when an integration routine fails. */ 989 class IntegrationException : Exception 990 { 991 /** The current estimates of the integration result and 992 absolute error. 993 994 Depending on the type of error, these may or may not be 995 close to the correct values. They are, however, very often 996 useful for figuring out what has gone wrong. 997 */ 998 immutable real resultEstimate; 999 immutable real errorEstimate; /// ditto 1000 1001 1002 /** This value will sometimes contain more detailed information 1003 about the error. 1004 */ 1005 immutable string extra; 1006 1007 1008 this(string msg, real resultEstimate, real errorEstimate, 1009 string file = __FILE__, size_t line = __LINE__) 1010 { 1011 this(msg, null, resultEstimate, errorEstimate, file, line); 1012 } 1013 1014 1015 this(string msg, string extra, real resultEstimate, real errorEstimate, 1016 string file = __FILE__, size_t line = __LINE__) 1017 { 1018 super (msg, file, line); 1019 1020 this.extra = extra; 1021 this.resultEstimate = resultEstimate; 1022 this.errorEstimate = errorEstimate; 1023 } 1024 1025 1026 override string toString() 1027 { 1028 return super.toString() 1029 ~ (extra.length > 0 ? "\n"~extra : "") 1030 ~ text("\nCurrent estimate: ", resultEstimate, " ± ", errorEstimate); 1031 } 1032 } 1033 1034 1035 /* Verify the value of 'ier' returned by QUADPACK routines, and 1036 throw an appropriate exception if it is not zero. 1037 */ 1038 void checkQuadpackStatus(Real)(int ier, Real result, Real abserr, 1039 string file = __FILE__, size_t line = __LINE__) 1040 { 1041 string msg, extra; 1042 switch(ier) 1043 { 1044 case 0: return; 1045 case 1: 1046 msg = "Reached the maximum number of iterations or subdivisions"; 1047 extra = "If possible, try using a more special-purpose integrator."; 1048 break; 1049 case 2: 1050 msg = "Roundoff error detected"; 1051 extra = "The occurrence of roundoff error is detected, which " 1052 ~ "prevents the requested tolerance from being achieved. " 1053 ~ "The error may be under-estimated."; 1054 break; 1055 case 3: 1056 msg = "Bad integrand behaviour"; 1057 extra = "Extremely bad integrand behaviour occurs at at some " 1058 ~ "points of the integration interval."; 1059 break; 1060 case 4: 1061 msg = "Roundoff error detected."; 1062 extra = "The algorithm does not converge. Roundoff error is " 1063 ~ "detected in the extrapolation table. It is assumed that " 1064 ~ "the requested tolerance cannot be achieved, and that the " 1065 ~ "returned result is the best that can be obtained."; 1066 break; 1067 case 5: 1068 msg = "Integral is divergent or converges too slowly"; 1069 break; 1070 case 6: 1071 msg = "Invalid input"; 1072 extra = "If you get this error message, it is a bug in SciD. " 1073 ~ "Please report."; 1074 break; 1075 default: 1076 msg = "Invalid QUADPACK error code"; 1077 extra = "If you get this error message, it is a bug in SciD. " 1078 ~ "Please report."; 1079 } 1080 throw new IntegrationException(msg, extra, result, abserr, file, line); 1081 } 1082 1083 1084 1085 1086 /** Calculate the derivative of a function. 1087 1088 This function uses Ridders' method of extrapolating the results 1089 of finite difference formulas for consecutively smaller step sizes, 1090 with an improved stopping criterion described in the Numerical Recipes 1091 books by Press et al. 1092 1093 This method gives a much higher degree of accuracy in the answer 1094 compared to a single finite difference calculation, but requires 1095 more function evaluations; typically 6 to 12. The maximum number 1096 of function evaluations is $(D 2*tableauSize). 1097 1098 Params: 1099 f = The function of which to take the derivative. 1100 x = The point at which to take the derivative. 1101 scale = A "characteristic scale" over which the function 1102 changes. (optional) 1103 tableauSize = The values of the consecutive approximations 1104 are stored in a $(D tableauSize)-by-$(D tableauSize) triangular 1105 matrix. Sometimes this number can be reduced to limit 1106 the possible number of function evaluations (see above) 1107 and thus greater speed, and sometimes it can be increased 1108 to allow for higher accuracy. (optional) 1109 1110 Example: 1111 This example is from Ridders' paper: 1112 --- 1113 // Let's take the derivative of 1114 // x 1115 // e 1116 // f(x) = ---------- 1117 // 2 1118 // sin x - x 1119 // at x=1. 1120 real f(real x) { return exp(x)/(sin(x)-x*x); } 1121 real dfdxAt1 = 140.73773557129660339; 1122 1123 // scale=0.01 is appropriate for this function. 1124 auto r = diff(&f, 1.0, 0.01); 1125 assert (abs(r.value - dfdxAt1) <= r.error); 1126 1127 // Note that if we use the default scale, it won't work: 1128 auto s = diff(&f, 1.0); 1129 writeln(s.error); // prints "inf" 1130 --- 1131 1132 References: 1133 $(UL 1134 $(LI 1135 C. J. F. Ridders, 1136 $(I Accurate computation of F'(x) and F'(x)F''(x)). 1137 Advances in Engineering Software, vol. 4 (1982), issue 2, p. 75.) 1138 $(LI 1139 W. H. Press, S. A. Teukolsky, W. T. Vetterling, and B. P. Flannery, 1140 $(I Numerical Recipes in C++) (2nd ed.). 1141 Cambridge University Press, 2003.) 1142 ) 1143 */ 1144 Result!real diff (Func) 1145 (scope Func f, real x, real scale = 1.0, size_t tableauSize = 10) 1146 in 1147 { 1148 assert (tableauSize > 0); 1149 } 1150 body 1151 { 1152 mixin(newFrame); 1153 1154 static assert (__traits(compiles, { real y; Func g; real z = g(y); }), 1155 "diff: Invalid function type: "~Func.stringof); 1156 1157 // Set up Romberg tableau. 1158 auto workspace = newStack!real((tableauSize*(tableauSize+1))/2); 1159 auto tab = 1160 MatrixView!(real, Storage.Triangular)(workspace, tableauSize); 1161 1162 // Let's keep the notation in order. 1163 real h = scale == 0.0 ? 1.0 : abs(scale); 1164 1165 // Divide h by the factor FAC for each finite-difference 1166 // approximation. Ridders uses FAC=2.0 in his paper, but 1167 // the NR book uses FAC=1.4. 1168 // TODO: Experiment and see which value is best. 1169 enum real FAC = 2.0; 1170 enum real FACSQ = FAC*FAC; 1171 enum real INVFAC = 1.0/FAC; 1172 1173 // From the NR book: Stop when the difference between consecutive 1174 // approximations is bigger than SAFE*error, where error is an 1175 // estimate of the absolute error in the current (best) approximation. 1176 enum int SAFE = 2; 1177 1178 // First approximation: A_0 1179 tab[0,0] = (f(x+h) - f(x-h))/(2.0*h); 1180 1181 real result, error = real.infinity; 1182 for (size_t n=1; n<tableauSize; n++) 1183 { 1184 // Decrease h. 1185 h *= INVFAC; 1186 1187 // Compute A_n 1188 tab[0,n] = (f(x+h) - f(x-h))/(2.0*h); 1189 1190 real facm = 1.0; 1191 for (size_t m=1; m<=n; m++) 1192 { 1193 facm *= FACSQ; 1194 1195 // Compute B_(n-1), C_(n-2), ... 1196 real upLeft = tab[m-1, n-1]; 1197 real up = tab[m-1, n]; 1198 real current = (facm*up - upLeft)/(facm-1); 1199 tab[m,n] = current; 1200 1201 // Calculate and check error. 1202 real currentError = max(abs(current - upLeft), abs(current - up)); 1203 if (currentError <= error) 1204 { 1205 result = current; 1206 error = currentError; 1207 } 1208 } 1209 1210 if (abs(tab[n,n]-tab[n-1,n-1]) >= SAFE*error) break; 1211 } 1212 1213 return Result!real(result, error); 1214 } 1215 1216 unittest 1217 { 1218 alias diff!(float function(float)) diff_float; 1219 alias diff!(double function(double)) diff_double; 1220 alias diff!(real function(real)) diff_real; 1221 1222 real f(real x) { return exp(x)/(sin(x)-x*x); } 1223 auto r = diff(&f, 1.0, 0.01, 5); 1224 assert (approxEqual(r.value, 140.73773557129660339L, 1225 0.0L, min(r.error, sqrt(real.epsilon)))); 1226 } 1227 1228 1229 1230 1231 /** Calculate the derivative of a function using a two-point 1232 formula, i.e. a forward- or backward-difference formula. 1233 1234 This method only evaluates the function once (in addition 1235 to the function value provided by the user), and is therefore 1236 the fastest way to compute a derivative. However, it is also 1237 the least accurate method, and should only be used if the 1238 function is very expensive to compute $(I and) you have 1239 already calculated $(D f(x)). 1240 1241 Params: 1242 f = The function to differentiate. 1243 x = The point at which to take the derivative. 1244 fx = The function value at $(D x), i.e. $(D f(x)) 1245 scale = A characteristic scale for $(D f). When this 1246 is positive, the forward-difference formula 1247 is used, and when it is negative, the 1248 backward-difference formula is used. 1249 1250 Returns: 1251 An approximation to the derivative of $(D f) at the point 1252 $(D x). The relative error in the result is $(I at best) 1253 on the order of $(D sqrt(real.epsilon)). 1254 Usually it is much higher. 1255 */ 1256 real diff2(Func)(scope Func f, real x, real fx, real scale = 1.0) 1257 in { assert (scale != 0); } 1258 body 1259 { 1260 enum sqrtEpsilon = sqrt(real.epsilon); 1261 immutable xph = x + sqrtEpsilon*scale; 1262 immutable h = xph - x; 1263 return (f(xph) - fx) / (xph - x); 1264 } 1265 1266 unittest 1267 { 1268 real f(real x) { return sin(x) * log(x); } 1269 real df(real x) { return cos(x) * log(x) + sin(x) / x; } 1270 1271 foreach (x; iota(0.1, 1.0, 0.1)) 1272 { 1273 assert (matchDigits(diff2(&f, x, f(x)), df(x), 8)); 1274 } 1275 } 1276 1277 1278 1279 1280 /** Calculate the derivative of a function using a three-point 1281 formula, a.k.a. a central difference formula. 1282 1283 The function is evaluated twice, at points just below and 1284 just above $(D x). 1285 1286 Params: 1287 f = The function to differentiate. 1288 x = The point at which to take the derivative. 1289 scale = A characteristic scale for $(D f). 1290 1291 Returns: 1292 An approximation to the derivative of $(D f) at the point 1293 $(D x). The relative error in the result is at best 1294 on the order of $(D (real.epsilon)^(2/3)), roughly three 1295 orders of magnitude more accurate than $(D diff2()). 1296 */ 1297 real diff3(Func)(scope Func f, real x, real scale = 1.0) 1298 in { assert (scale != 0); } 1299 body 1300 { 1301 immutable h = cbrt(real.epsilon) * scale; 1302 immutable xmh = x - h; 1303 immutable xph = x + h; 1304 return (f(xph) - f(xmh))/(xph - xmh); 1305 } 1306 1307 unittest 1308 { 1309 real f(real x) { return sin(x) * log(x); } 1310 real df(real x) { return cos(x) * log(x) + sin(x) / x; } 1311 1312 foreach (x; iota(0.1, 1.0, 0.1)) 1313 { 1314 assert (matchDigits(diff3(&f, x), df(x), 11)); 1315 } 1316 } 1317 1318 1319 1320 1321 /** Calculate the Jacobian matrix associated with a set of $(I m) functions 1322 in $(I n) variables using a central-difference approximation to the 1323 Jacobian. 1324 1325 This method requires 2$(I n) function evaluations, but is more 1326 accurate than the faster $(D jacobian2()) function. The relative 1327 accuracy in the result is, at best, on the order of 1328 $(D (real.epsilon)^(2/3)). 1329 1330 Params: 1331 f = The set of functions. This is typically a function or delegate 1332 which takes a vector as input and returns a vector. If the 1333 function takes a second vector parameter, this is assumed to 1334 be a buffer for the return value, and will be used as such. 1335 1336 m = The number of functions in $(D f). 1337 1338 x = The point at which to take the derivative. 1339 1340 scale = A "characteristic scale" over which the function changes. 1341 (optional) 1342 1343 buffer = A buffer of length at least $(I m)*$(I n), for storing the calculated 1344 Jacobian matrix. (optional) 1345 1346 Examples: 1347 --- 1348 // Let's say we want to find the Jacobian of the function 1349 // f(x,y) = (xy, x-y) 1350 // at the point p = (1, 2). 1351 real[] p = [ 1.0, 2.0 ]; 1352 1353 // This is the simplest way to do it: 1354 real[] f(real[] a) 1355 { 1356 auto r = new real[2]; 1357 r[0] = a[0] * a[1]; 1358 r[1] = a[0] - a[1]; 1359 return r; 1360 } 1361 1362 auto j = jacobian(&f, 2, p); 1363 1364 // However, if we need to perform this operation many times, 1365 // we may want to speed things up a bit. To avoid unnecessary 1366 // repeated allocations, we can add a buffer parameter to the 1367 // function: 1368 real[] g(real[] a, real[] r) 1369 { 1370 r[0] = a[0] * a[1]; 1371 r[1] = a[0] - a[1]; 1372 return r[0 .. 2]; 1373 } 1374 1375 auto jFaster = jacobian(&g, 2, p); 1376 --- 1377 1378 See_also: 1379 $(UL $(LI Wikipedia: 1380 $(LINK2 http://en.wikipedia.org/wiki/Jacobian_matrix_and_determinant,Jacobian matrix and determinant). 1381 )) 1382 */ 1383 MatrixView!Real jacobian (Real, Func) (scope Func f, int m, Real[] x, 1384 real scale=1.0, Real[] buffer=null) 1385 { 1386 mixin (newFrame); 1387 1388 static assert (isFloatingPoint!Real, 1389 "jacobian: Not a floating-point type: "~Real.stringof); 1390 static assert (isVectorField!(Func, Real), 1391 "jacobian: Invalid function type ("~Func.stringof~"), or " 1392 ~"function type doesn't match parameter type ("~Real.stringof~")"); 1393 assert (m > 0); 1394 1395 immutable size_t n = x.length; 1396 buffer.length = m*n; 1397 auto jaco = MatrixView!Real(buffer, m, n); 1398 1399 // Allocate workspace. 1400 auto workspace = newStack!Real(2*m); 1401 auto fxph = workspace[0 .. m]; 1402 auto fxmh = workspace[m .. $]; 1403 1404 // Determine step length. 1405 scale = scale == 0.0 ? 1.0 : abs(scale); 1406 immutable real CBRT_EPSILON = cbrt(real.epsilon); // TODO: make enum 1407 real h = CBRT_EPSILON * scale; 1408 1409 foreach (j; 0 .. n) 1410 { 1411 // We don't want to change x. 1412 real save = x[j]; 1413 1414 // Ensure 2h is exactly (x+h)-(x-h) 1415 real xph = save + h; 1416 real xmh = save - h; 1417 real twh = xph - xmh; 1418 1419 // Evaluate function at both points, provide buffer if possible. 1420 x[j] = xph; 1421 static if (isBufferVectorField!(Func,Real)) fxph = f(x, fxph); 1422 else fxph = f(x); 1423 1424 x[j] = xmh; 1425 static if (isBufferVectorField!(Func,Real)) fxmh = f(x, fxmh); 1426 else fxmh = f(x); 1427 1428 // Restore x. 1429 x[j] = save; 1430 1431 // Calculate one column of the Jacobian. 1432 foreach (i; 0 .. m) 1433 jaco[i, j] = (fxph[i] - fxmh[i])/twh; 1434 } 1435 1436 return jaco; 1437 } 1438 1439 unittest 1440 { 1441 alias jacobian!(float, float[] function(float[])) jacobian_float; 1442 alias jacobian!(double, double[] function(double[])) jacobian_double; 1443 alias jacobian!(real, real[] function(real[])) jacobian_real; 1444 1445 // Note: This is the example from the doc comment. 1446 // There are more unittests for jacobian() below jacobian2(). 1447 double[] f(double[] a, double[] r=null) 1448 { 1449 if (r.length < 2) r = new double[2]; 1450 auto x = a[0], y = a[1]; 1451 1452 r[0] = x*y; 1453 r[1] = x-y; 1454 return r[0 .. 2]; 1455 } 1456 1457 double[] x = [ 1.0, 2.0 ]; 1458 double[4] buffer; 1459 auto j1 = jacobian(&f, 2, x); 1460 auto j2 = jacobian(&f, 2, x, 1.0, buffer); 1461 1462 double e = 1e-6; 1463 assert (approxEqual(j1[0,0], 2.0, e) && approxEqual(j1[0,1], 1.0, e) 1464 && approxEqual(j1[1,0], 1.0, e) && approxEqual(j1[1,1], -1.0, e)); 1465 1466 assert (approxEqual(j2[0,0], 2.0, e) && approxEqual(j2[0,1], 1.0, e) 1467 && approxEqual(j2[1,0], 1.0, e) && approxEqual(j2[1,1], -1.0, e)); 1468 } 1469 1470 1471 1472 1473 /** Calculate the Jacobian using forward-/backward-difference 1474 methods, also known as 2-point formulas. 1475 1476 This is less accurate than the central-difference method 1477 used in the $(D jacobian()) function, but requires only half 1478 as many function evaluations. The relative accuracy is, 1479 at best, on the order of $(D sqrt(real.epsilon)). 1480 1481 Params: 1482 f = The set of functions. This is typically a function or delegate 1483 which takes a vector as input and returns a vector. If the 1484 function takes a second vector parameter, this is assumed to 1485 be a buffer for the return value, and will be used as such. 1486 1487 m = The number of functions in $(D f). 1488 1489 x = The point at which to take the derivative. 1490 1491 scale = $(D abs(scale)) is the characteristic scale of the 1492 function, and the sign of scale determines which 1493 differentiation method is used. If $(D scale) is 1494 negative, the backward difference method is used, and 1495 if it is positive, forward differences are used. 1496 (optional) 1497 1498 fx = The result of evaluating the function at the point $(D x). 1499 Providing this saves one function evaluation if you 1500 for some reason have already calculated the value. (optional) 1501 1502 buffer = A buffer of length at least $(I m)*$(I n), for storing the calculated 1503 Jacobian matrix. (optional) 1504 1505 Examples: 1506 --- 1507 // Continuing with the example for the jacobian() function, 1508 // we make it even faster by using a 2-point formula. 1509 auto gp = g(p); 1510 auto jFastest = jacobian2(&g, p, 1.0, gp, buffer, workspace); 1511 --- 1512 */ 1513 MatrixView!Real jacobian2 (Real, Func) (scope Func f, int m, Real[] x, 1514 real scale=1.0, Real[] fx=null, Real[] buffer=null) 1515 { 1516 mixin (newFrame); 1517 1518 static assert (isFloatingPoint!Real, 1519 "jacobian2: Not a floating-point type: "~Real.stringof); 1520 static assert (isVectorField!(Func, Real), 1521 "jacobian2: Invalid function type ("~Func.stringof~"), or " 1522 ~"function type doesn't match parameter type ("~Real.stringof~")"); 1523 assert (m > 0); 1524 1525 immutable size_t n = x.length; 1526 buffer.length = m*n; 1527 auto jaco = MatrixView!Real(buffer, m, n); 1528 1529 // Allocate workspace. 1530 auto fxph = newStack!Real(m); 1531 1532 // Calculate f(x) if it's not provided. 1533 if (fx.length == 0) 1534 { 1535 static if (isBufferVectorField!(Func, Real)) 1536 { 1537 fx = newStack!Real(m); 1538 fx = f(x, fx); 1539 } 1540 else fx = f(x); 1541 } 1542 else assert (fx.length == m); 1543 1544 // Determine step length. 1545 scale = scale == 0.0 ? 1.0 : abs(scale); 1546 real h = sqrt(real.epsilon) * scale; 1547 1548 for (int j=0; j<n; j++) 1549 { 1550 // We don't want to change x. 1551 real save = x[j]; 1552 1553 // Ensure h is exactly (x+h)-x 1554 real xph = save + h; 1555 real hExact = xph - save; 1556 1557 // Evaluate function at x+h, provide buffer if possible. 1558 x[j] = xph; 1559 static if (isBufferVectorField!(Func,Real)) fxph = f(x, fxph); 1560 else fxph = f(x); 1561 1562 // Restore x. 1563 x[j] = save; 1564 1565 // Calculate one column of the Jacobian. 1566 for (int i=0; i<m; i++) 1567 jaco[i, j] = (fxph[i] - fx[i])/hExact; 1568 } 1569 1570 return jaco; 1571 } 1572 1573 unittest 1574 { 1575 alias jacobian2!(float, float[] function(float[])) jacobian2_float; 1576 alias jacobian2!(double, double[] function(double[])) jacobian2_double; 1577 alias jacobian2!(real, real[] function(real[])) jacobian2_real; 1578 1579 real[] f(real[] x, real[] buf=null) 1580 { 1581 if (buf.length < 4) buf = new real[4]; 1582 real x1 = x[0], x2 = x[1], x3 = x[2]; 1583 1584 buf[0] = x1; 1585 buf[1] = 5*x3; 1586 buf[2] = 4*x2*x2 - 2*x3; 1587 buf[3] = x3*sin(x1); 1588 1589 return buf[0 .. 4]; 1590 } 1591 1592 real[] p = [0.0L, 1, 2]; 1593 real[] answer = [1.0L, 0, 0, p[2]*cos(p[0]), 1594 0, 0, 8*p[1], 0, 1595 0, 5, -2, sin(p[0])]; 1596 real[12] buffer; 1597 1598 1599 // Central difference 1600 real e = 1e-10; 1601 auto j = jacobian(&f, 4, p); 1602 assert (approxEqual(j.array, answer, e, e)); 1603 1604 j = jacobian(&f, 4, p, 1.0, buffer); 1605 assert (approxEqual(j.array, answer, e, e)); 1606 1607 1608 // The next ones are less accurate. 1609 e = 1e-6; 1610 1611 // Forward difference 1612 j = jacobian2(&f, 4, p); 1613 assert (approxEqual(j.array, answer, e, e)); 1614 1615 // Backward difference 1616 j = jacobian2(&f, 4, p, -1.0, f(p), buffer); 1617 assert (approxEqual(j.array, answer, e, e)); 1618 } 1619 1620 1621 1622 1623 /** Calculate the _gradient of a function of several variables. 1624 1625 This function calculates a central-difference approximation to 1626 the _gradient of a function $(D f). The error in the result is, at best, 1627 on the order of $(D sqrt(real.epsilon)). The function $(D f) is evaluated 1628 2$(I n) times, where $(I n) is the length of the vector $(D x). 1629 1630 Params: 1631 f = The function of which to find the _gradient. 1632 x = The point at which to find the _gradient. 1633 scale = A "characteristic scale" over which the function 1634 changes significantly. (optional) 1635 buffer = A buffer of the same length as $(D x), for the returned 1636 _gradient vector. (optional) 1637 1638 Example: 1639 --- 1640 // Let's find the gradient of f(x,y) = x exp(y) at 1641 // the point p = (2,1). 1642 real f(real[] x) { return x[0] * exp(x[1]); } 1643 real[] p = [2.0L, 1.0L]; 1644 1645 auto g = gradient(&f, p); 1646 --- 1647 1648 See_also: 1649 $(UL $(LI Wikipedia: 1650 $(LINK2 http://en.wikipedia.org/wiki/Gradient,Gradient). 1651 )) 1652 */ 1653 Real[] gradient (Real, Func) 1654 (scope Func f, Real[] x, real scale=1.0, Real[] buffer=null) 1655 { 1656 static assert (isFloatingPoint!Real, 1657 "gradient: Not a floating-point type: "~Real.stringof); 1658 static assert (__traits(compiles, { Real[] u; Func g; Real v = g(u); }), 1659 "gradient: Invalid function type ("~Func.stringof~"), or " 1660 ~"function type doesn't match parameter type ("~Real.stringof~")"); 1661 1662 immutable size_t n = x.length; 1663 buffer.length = n; 1664 1665 // Determine step length. 1666 scale = scale == 0.0 ? 1.0 : abs(scale); 1667 real h = cbrt(real.epsilon) * scale; 1668 1669 foreach (i; 0 .. n) 1670 { 1671 // We don't want to modify the vector x. 1672 real save = x[i]; 1673 1674 // Ensure the difference between x+h and x-h is exactly 2h. 1675 real xph = save + h; 1676 real xmh = save - h; 1677 real twh = xph - xmh; 1678 1679 // Calculate central difference and divide by 2h. 1680 x[i] = xph; 1681 real sum = f(x); 1682 x[i] = xmh; 1683 sum -= f(x); 1684 buffer[i] = sum/twh; 1685 1686 // Restore vector. 1687 x[i] = save; 1688 } 1689 1690 return buffer[0 .. n]; 1691 } 1692 1693 unittest 1694 { 1695 alias gradient!(float, float function(float[])) gradient_float; 1696 alias gradient!(double, double function(double[])) gradient_double; 1697 alias gradient!(real, real function(real[])) gradient_real; 1698 1699 real f(real[] x) { return x[0] * exp(x[1]); } 1700 real[] p = [2.0L, 1.0L]; 1701 1702 auto g = gradient(&f, p); 1703 1704 real[] fgrad(real[] x) { return [exp(x[1]), x[0]*exp(x[1])]; } 1705 auto gtest = fgrad(p); 1706 assert (approxEqual(g, gtest, 1e-10, 1e-10)); 1707 } 1708 1709 1710 1711 /** Calculate the gradient of a function of several variables 1712 using Ridders' method. 1713 1714 This function uses $(D diff()) to calculate the derivative 1715 in each direction. It is therefore more accurate, but slower, than 1716 the $(D gradient()) function. The function $(D f) is typically evaluated 1717 between 6$(I n) and 12$(I n) times, where $(I n) is the length of $(D x). 1718 See the documentation for $(D diff()) for more information on this method. 1719 1720 This function is used in the same way as $(D gradient()). 1721 */ 1722 Real[] gradientR (Real, Func) 1723 (scope Func f, Real[] x, real scale=1.0, Real[] buffer=null) 1724 { 1725 static assert (isFloatingPoint!Real, 1726 "gradientR: Not a floating-point type: "~Real.stringof); 1727 static assert (__traits(compiles, { Real[] u; Func g; Real v = g(u); }), 1728 "gradientR: Invalid function type ("~Func.stringof~"), or " 1729 ~"function type doesn't match parameter type ("~Real.stringof~")"); 1730 1731 immutable size_t n = x.length; 1732 buffer.length = n; 1733 1734 // Calculate the derivative in each direction using Ridders' method. 1735 foreach (i; 0 .. n) 1736 { 1737 real save = x[i]; 1738 1739 // Provide a real->real proxy function. 1740 real g(real y) 1741 { 1742 x[i] = y; 1743 return f(x); 1744 } 1745 1746 // Use Ridders' method to differentiate. 1747 buffer[i] = diff(&g, save, scale, 10); 1748 x[i] = save; 1749 } 1750 1751 return buffer; 1752 } 1753 1754 unittest 1755 { 1756 alias gradientR!(float, float function(float[])) gradientR_float; 1757 alias gradientR!(double, double function(double[])) gradientR_double; 1758 alias gradientR!(real, real function(real[])) gradientR_real; 1759 1760 real f(real[] v) 1761 { 1762 real x = v[0], y = v[1], z = v[2]; 1763 return exp(x) + log(y) + z*z; 1764 } 1765 1766 real[] df(real[] v) 1767 { 1768 real x = v[0], y = v[1], z = v[2]; 1769 real[] g = new real[3]; 1770 g[0] = exp(x); 1771 g[1] = 1/y; 1772 g[2] = 2*z; 1773 return g; 1774 } 1775 1776 real[] p = [ 0.0L, 2.0, 4.0 ]; 1777 real[] ans = df(p); 1778 auto g = gradient(&f, p); 1779 1780 assert (approxEqual(g, ans, sqrt(real.epsilon))); 1781 } 1782 1783 1784 1785 1786 /** Calculate the Hessian matrix of a function of several variables 1787 using a central-difference approximation. 1788 1789 This function stores its results in an $(I n)-by-$(I n) symmetric matrix, 1790 where $(I n) is the number of variables (i.e. the length of $(D x)). 1791 The function $(D f) is evaluated 1+2$(I n)$(SUP 2) times. 1792 1793 Params: 1794 f = The function of which to calculate the Hessian. 1795 x = The point at which to calculate the Hessian. 1796 scale = A "characteristic scale" over which the function 1797 changes significantly. (optional) 1798 buffer = A buffer of size at least $(I n)($(I n)+1)/2, for storing 1799 the Hessian matrix. (optional) 1800 1801 Example: 1802 --- 1803 // Above, we found the gradient of f(x,y) at the point p. 1804 // Finding the Hessian matrix is just as simple: 1805 auto h = hessian(&f, p); 1806 --- 1807 1808 See_also: 1809 $(UL $(LI Wikipedia: 1810 $(LINK2 http://en.wikipedia.org/wiki/Hessian_matrix,Hessian matrix). 1811 )) 1812 */ 1813 MatrixView!(Real, Storage.Symmetric) hessian(Real, Func) 1814 (scope Func f, Real[] x, real scale=1.0, Real[] buffer=null) 1815 { 1816 static assert (isFloatingPoint!Real, 1817 "hessian: Not a floating-point type: "~Real.stringof); 1818 static assert (__traits(compiles, { Real[] u; Func g; Real v = g(u); }), 1819 "hessian: Invalid function type ("~Func.stringof~"), or " 1820 ~"function type doesn't match parameter type ("~Real.stringof~")"); 1821 1822 immutable size_t n = x.length; 1823 1824 buffer.length = (n*n + n)/2; 1825 auto hess = MatrixView!(Real, Storage.Symmetric)(buffer, n, n); 1826 1827 // Determine step length. 1828 enum real SQRT_EPSILON = sqrt(real.epsilon); 1829 enum real QDRT_EPSILON = sqrt(SQRT_EPSILON); 1830 scale = scale == 0.0 ? 1.0 : abs(scale); 1831 real hOffdiag = QDRT_EPSILON*scale; 1832 real hDiag = cbrt(real.epsilon*(scale^^4)); 1833 1834 // We need the function at x for the diagonal elements. 1835 real m2fx = -2.0*f(x); 1836 1837 // Loop over columns. 1838 foreach (j; 0 .. n) 1839 { 1840 // Loop over rows, calculate offdiagonal elements. 1841 foreach (i; 0 .. j) 1842 { 1843 // Don't want to change x. 1844 real savex = x[i]; 1845 real savey = x[j]; 1846 1847 // Make hx and hy exactly representable numbers. 1848 real xph = savex + hOffdiag; 1849 real xmh = savex - hOffdiag; 1850 real twhx = xph - xmh; 1851 1852 real yph = savey + hOffdiag; 1853 real ymh = savey - hOffdiag; 1854 real twhy = yph - ymh; 1855 1856 // Sum function value at all four points. 1857 x[i] = xph; 1858 x[j] = yph; 1859 real sum = f(x); 1860 1861 x[j] = ymh; 1862 sum -= f(x); 1863 1864 x[i] = xmh; 1865 sum += f(x); 1866 1867 x[j] = yph; 1868 sum -= f(x); 1869 1870 // Restore x and save one element of the Hessian. 1871 x[i] = savex; 1872 x[j] = savey; 1873 hess[i, j] = sum/(twhx*twhy); 1874 } 1875 1876 // Calculate the diagonal element. 1877 { 1878 // Don't want to change x. 1879 real save = x[j]; 1880 1881 // Make h an exactly representable number. 1882 real xph = save + hDiag; 1883 real h = xph - save; 1884 1885 // We have already calculated -2f(x). 1886 real sum = m2fx; 1887 1888 x[j] = xph; 1889 sum += f(x); 1890 1891 x[j] = save - hDiag; 1892 sum += f(x); 1893 1894 // Reset x and calculate diagonal element. 1895 x[j] = save; 1896 hess[j, j] = sum/(h*h); 1897 } 1898 } 1899 1900 return hess; 1901 } 1902 1903 unittest 1904 { 1905 alias hessian!(float, float function(float[])) hessian_float; 1906 alias hessian!(double, double function(double[])) hessian_double; 1907 alias hessian!(real, real function(real[])) hessian_real; 1908 1909 real f(real[] x) { return cos(x[0]+x[1]) + sin(x[0]*x[1]); } 1910 real fxx(real[] x) { return -cos(x[0]+x[1]) - x[1]*x[1]*sin(x[0]*x[1]); } 1911 real fxy(real[] x) { return -cos(x[0]+x[1]) - x[0]*x[1]*sin(x[0]*x[1]) 1912 + cos(x[0]*x[1]); } 1913 real fyx(real[] x) { return fxy(x); } 1914 real fyy(real[] x) { return -cos(x[0]+x[1]) - x[0]*x[0]*sin(x[0]*x[1]); } 1915 1916 real[] x = [ 10., 100.0 ]; 1917 real[3] buffer; 1918 1919 auto h = hessian(&f, x, PI*0.25, buffer); 1920 1921 assert (approxEqual(h[0,0], fxx(x), 1e-6L)); 1922 assert (approxEqual(h[0,1], fxy(x), 1e-6L)); 1923 assert (approxEqual(h[1,0], fyx(x), 1e-6L)); 1924 assert (approxEqual(h[1,1], fyy(x), 1e-6L)); 1925 }