Skip to content

Commit 1115b6d

Browse files
committed
Add mir.numeric.diff
1 parent 64cfcbf commit 1115b6d

File tree

1 file changed

+133
-6
lines changed

1 file changed

+133
-6
lines changed

source/mir/numeric.d

Lines changed: 133 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ Base numeric algorithms.
44
Reworked part of `std.numeric`.
55
66
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)
88
+/
99
module 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

Comments
 (0)