@@ -4,7 +4,7 @@ Base numeric algorithms.
44Reworked part of `std.numeric`.
55
66License: $(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)
88+/
99module mir.numeric ;
1010
@@ -502,8 +502,8 @@ private @fmamath FindRootResult!T findRootImplGen(T)(
502502 {
503503 // Find the coefficients of the quadratic polynomial.
504504 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);
507507
508508 // Determine the starting point of newton steps.
509509 T c = a2.signbit != fa.signbit ? a : b;
@@ -950,7 +950,7 @@ version(mir_test) @safe unittest
950950 return pow (x, n) + double .min_normal;
951951 }
952952 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
954954 // outperforms his method, and gives total numcalls =
955955 // 921 for bisection (2.4 calls per bit), 1830 for Alefeld (4.76/bit),
956956 // 0.5f624 for brent (6.8/bit)
@@ -977,7 +977,7 @@ version(mir_test) @safe unittest
977977 ++ numCalls;
978978 real q = sin(x) - x/ 2 ;
979979 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));
981981 return q;
982982 }
983983 real alefeld1 (real x)
@@ -1020,7 +1020,7 @@ version(mir_test) @safe unittest
10201020 {
10211021 ++ numCalls;
10221022 ++ alefeldSums[7 ];
1023- return (n* x- 1 )/ ((n- 1 )* x);
1023+ return (n* x- 1 ) / ((n- 1 )* x);
10241024 }
10251025
10261026 numProblems=0 ;
@@ -1432,3 +1432,130 @@ private auto trustedAllAttr(T)(T t) @trusted
14321432 | FunctionAttribute.nothrow_;
14331433 return cast (SetFunctionAttributes! (T, functionLinkage! T, attrs)) t;
14341434}
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