@@ -4,7 +4,7 @@ Base numeric algorithms.
4
4
Reworked part of `std.numeric`.
5
5
6
6
License: $(HTTP www.apache.org/licenses/LICENSE-2.0, Apache-2.0)
7
- Authors: Ilya Yaroshenko (API, findLocalMin, findRoot extension), Don Clugston (findRoot)
7
+ Authors: Ilya Yaroshenko (API, findLocalMin, findRoot extension), Don Clugston (findRoot), Lars Tandle Kyllingstad (diff)
8
8
+/
9
9
module mir.numeric ;
10
10
@@ -502,8 +502,8 @@ private @fmamath FindRootResult!T findRootImplGen(T)(
502
502
{
503
503
// Find the coefficients of the quadratic polynomial.
504
504
const T a0 = fa;
505
- const T a1 = (fb - fa)/ (b - a);
506
- const T a2 = ((fd - fb)/ (d - b) - a1)/ (d - a);
505
+ const T a1 = (fb - fa) / (b - a);
506
+ const T a2 = ((fd - fb) / (d - b) - a1) / (d - a);
507
507
508
508
// Determine the starting point of newton steps.
509
509
T c = a2.signbit != fa.signbit ? a : b;
@@ -950,7 +950,7 @@ version(mir_test) @safe unittest
950
950
return pow (x, n) + double .min_normal;
951
951
}
952
952
int [] power_nvals = [3 , 5 , 7 , 9 , 19 , 25 ];
953
- // Alefeld paper states that pow(x,n) is a very poor case, where bisection
953
+ // Alefeld paper states that pow(x, n) is a very poor case, where bisection
954
954
// outperforms his method, and gives total numcalls =
955
955
// 921 for bisection (2.4 calls per bit), 1830 for Alefeld (4.76/bit),
956
956
// 0.5f624 for brent (6.8/bit)
@@ -977,7 +977,7 @@ version(mir_test) @safe unittest
977
977
++ numCalls;
978
978
real q = sin(x) - x/ 2 ;
979
979
for (int i=1 ; i< 20 ; ++ i)
980
- q+= (2 * i- 5.0 )* (2 * i- 5.0 )/ ((x- i* i)* (x- i* i)* (x- i* i));
980
+ q+= (2 * i- 5.0 )* (2 * i- 5.0 ) / ((x- i* i)* (x- i* i)* (x- i* i));
981
981
return q;
982
982
}
983
983
real alefeld1 (real x)
@@ -1020,7 +1020,7 @@ version(mir_test) @safe unittest
1020
1020
{
1021
1021
++ numCalls;
1022
1022
++ alefeldSums[7 ];
1023
- return (n* x- 1 )/ ((n- 1 )* x);
1023
+ return (n* x- 1 ) / ((n- 1 )* x);
1024
1024
}
1025
1025
1026
1026
numProblems=0 ;
@@ -1432,3 +1432,130 @@ private auto trustedAllAttr(T)(T t) @trusted
1432
1432
| FunctionAttribute.nothrow_;
1433
1433
return cast (SetFunctionAttributes! (T, functionLinkage! T, attrs)) t;
1434
1434
}
1435
+
1436
+ /+ +
1437
+ Calculate the derivative of a function.
1438
+ This function uses Ridders' method of extrapolating the results
1439
+ of finite difference formulas for consecutively smaller step sizes,
1440
+ with an improved stopping criterion described in the Numerical Recipes
1441
+ books by Press et al.
1442
+
1443
+ This method gives a much higher degree of accuracy in the answer
1444
+ compared to a single finite difference calculation, but requires
1445
+ more function evaluations; typically 6 to 12. The maximum number
1446
+ of function evaluations is $(D 24).
1447
+
1448
+ Params:
1449
+ f = The function of which to take the derivative.
1450
+ x = The point at which to take the derivative.
1451
+ h = A "characteristic scale" over which the function changes.
1452
+ factor = Stepsize is decreased by factor at each iteration.
1453
+ safe = Return when error is SAFE worse than the best so far.
1454
+
1455
+ References:
1456
+ $(UL
1457
+ $(LI
1458
+ C. J. F. Ridders,
1459
+ $(I Accurate computation of F'(x) and F'(x)F''(x)).
1460
+ Advances in Engineering Software, vol. 4 (1982), issue 2, p. 75.)
1461
+ $(LI
1462
+ W. H. Press, S. A. Teukolsky, W. T. Vetterling, and B. P. Flannery,
1463
+ $(I Numerical Recipes in C++) (2nd ed.).
1464
+ Cambridge University Press, 2003.)
1465
+ )
1466
+ +/
1467
+ @fmamath
1468
+ DiffResult! T diff (alias f, T)(const T x, const T h, const T factor = T(2 ).sqrt, const T safe = 2 )
1469
+ {
1470
+ if (false ) // break attributes
1471
+ T y = f(T(1 ));
1472
+ scope funInst = delegate (T x) {
1473
+ return T (f(x));
1474
+ };
1475
+ scope fun = funInst.trustedAllAttr;
1476
+ return diffImpl (fun, x, h, factor, safe);
1477
+ }
1478
+
1479
+ // /ditto
1480
+ DiffResult! T diffImpl (T)
1481
+ (scope const T delegate (T) @safe pure nothrow @nogc f, const T x, const T h, const T factor = T(2 ).sqrt, const T safe = 2 )
1482
+ @safe pure nothrow @nogc
1483
+ in {
1484
+ assert (h < T.max);
1485
+ assert (h > T.min_normal);
1486
+ }
1487
+ do {
1488
+ // Set up Romberg tableau.
1489
+ import mir.ndslice.slice: sliced;
1490
+ pragma (inline, false );
1491
+
1492
+ enum tableauSize = 16 ;
1493
+ T[tableauSize ^^ 2 ] workspace = void ;
1494
+ auto tab = workspace[].sliced(tableauSize, tableauSize);
1495
+
1496
+ // From the NR book: Stop when the difference between consecutive
1497
+ // approximations is bigger than SAFE*error, where error is an
1498
+ // estimate of the absolute error in the current (best) approximation.
1499
+
1500
+ // First approximation: A_0
1501
+ T result = void ;
1502
+ T error = T.max;
1503
+ T hh = h;
1504
+
1505
+ tab[0 ,0 ] = (f(x + h) - f(x - h)) / (2 * h);
1506
+ foreach (n; 1 .. tableauSize)
1507
+ {
1508
+ // Decrease h.
1509
+ hh /= factor;
1510
+
1511
+ // Compute A_n
1512
+ tab[0 , n] = (f(x + hh) - f(x - hh)) / (2 * hh);
1513
+
1514
+ T facm = 1 ;
1515
+ foreach (m; 1 .. n + 1 )
1516
+ {
1517
+ facm *= factor ^^ 2 ;
1518
+
1519
+ // Compute B_(n-1), C_(n-2), ...
1520
+ T upLeft = tab[m - 1 , n - 1 ];
1521
+ T up = tab[m - 1 , n];
1522
+ T current = (facm * up - upLeft) / (facm - 1 );
1523
+ tab[m, n] = current;
1524
+
1525
+ // Calculate and check error.
1526
+ T currentError = fmax(fabs(current - upLeft), fabs(current - up));
1527
+ if (currentError <= error)
1528
+ {
1529
+ result = current;
1530
+ error = currentError;
1531
+ }
1532
+ }
1533
+
1534
+ if (fabs(tab[n, n] - tab[n - 1 , n - 1 ]) >= safe * error)
1535
+ break ;
1536
+ }
1537
+
1538
+ return typeof (return )(result, error);
1539
+ }
1540
+
1541
+ // /
1542
+ unittest
1543
+ {
1544
+ import mir.math.common;
1545
+
1546
+ auto f (double x) { return exp(x) / (sin(x) - x ^^ 2 ); }
1547
+ auto d (double x) { return - (exp(x) * ((x - 2 ) * x - sin(x) + cos(x)))/ (x^^ 2 - sin(x))^^ 2 ; }
1548
+ auto r = diff! f(1.0 , 0.01 );
1549
+ assert (approxEqual(r.value, d(1 )));
1550
+ }
1551
+
1552
+ /+ +
1553
+ +/
1554
+ struct DiffResult (T)
1555
+ if (__traits(isFloating, T))
1556
+ {
1557
+ // /
1558
+ T value = 0 ;
1559
+ // /
1560
+ T error = 0 ;
1561
+ }
0 commit comments