1
1
export FiniteDifferenceMethod, fdm, backward_fdm, forward_fdm, central_fdm, extrapolate_fdm
2
2
3
3
"""
4
- add_tiny( x::Union{AbstractFloat, Integer})
4
+ estimate_magitude(f, x::T) where T<:AbstractFloat
5
5
6
- Add a tiny number, 10^{-40}, to a real floating point number `x`, preserving the type. If
7
- `x` is an `Integer`, it is promoted to a suitable floating point type.
6
+ Estimate the magnitude of `f` in a neighbourhood of `x`, assuming that the outputs of `f`
7
+ have a "typical" order of magnitude. The result should be interpreted as a very rough
8
+ estimate. This function deals with the case that `f(x) = 0`.
8
9
"""
9
- add_tiny (x:: T ) where T<: AbstractFloat = x + convert (T, 1e-40 )
10
- add_tiny (x:: Integer ) = add_tiny (float (x))
10
+ function estimate_magitude (f, x:: T ) where T<: AbstractFloat
11
+ M = float (maximum (abs, f (x)))
12
+ M > 0 && (return M)
13
+ # Ouch, `f(x) = 0`. But it may not be zero around `x`. We conclude that `x` is likely a
14
+ # pathological input for `f`. Perturb `x`. Assume that the pertubed value for `x` is
15
+ # highly unlikely also a pathological value for `f`.
16
+ Δ = convert (T, 0.1 ) * max (abs (x), one (x))
17
+ return float (maximum (abs, f (x + Δ)))
18
+ end
19
+
20
+ """
21
+ estimate_roundoff_error(f, x::T) where T<:AbstractFloat
22
+
23
+ Estimate the round-off error of `f(x)`. This function deals with the case that `f(x) = 0`.
24
+ """
25
+ function estimate_roundoff_error (f, x:: T ) where T<: AbstractFloat
26
+ # Estimate the round-off error. It can happen that the function is zero around `x`, in
27
+ # which case we cannot take `eps(f(x))`. Therefore, we assume a lower bound that is
28
+ # equal to `eps(T) / 1000`, which gives `f` four orders of magnitude wiggle room.
29
+ return max (eps (estimate_magitude (f, x)), eps (T) / 1000 )
30
+ end
11
31
12
32
"""
13
33
FiniteDifferences.DEFAULT_CONDITION
209
229
210
230
# Estimate the bound on the derivative by amplifying the ∞-norm.
211
231
function _make_default_bound_estimator (; condition:: Real = DEFAULT_CONDITION)
212
- default_bound_estimator (f, x) = condition * maximum (abs, f (x) )
232
+ default_bound_estimator (f, x) = condition * estimate_magitude (f, x )
213
233
return default_bound_estimator
214
234
end
215
235
@@ -256,17 +276,18 @@ function estimate_step(
256
276
) where T<: AbstractFloat
257
277
p = length (m. coefs)
258
278
q = m. q
259
- f_x = float (f (x))
260
279
261
- # Estimate the bound and round-off error.
262
- ε = add_tiny (maximum (eps, f_x)) * factor
263
- M = add_tiny (m. bound_estimator (f, x))
280
+ # Estimate the round-off error.
281
+ ε = estimate_roundoff_error (f, x) * factor
282
+
283
+ # Estimate the bound on the derivatives.
284
+ M = m. bound_estimator (f, x)
264
285
265
286
# Set the step size by minimising an upper bound on the error of the estimate.
266
287
C₁ = ε * sum (abs, m. coefs)
267
288
C₂ = M * sum (n -> abs (m. coefs[n] * m. grid[n]^ p), eachindex (m. coefs)) / factorial (p)
268
289
# Type inference fails on this, so we annotate it, which gives big performance benefits.
269
- h:: T = convert (T, min ((q / (p - q) * C₁ / C₂)^ (1 / p), max_step))
290
+ h:: T = convert (T, min ((q / (p - q) * ( C₁ / C₂) )^ (1 / p), max_step))
270
291
271
292
# Estimate the accuracy of the method.
272
293
accuracy = h^ (- q) * C₁ + h^ (p - q) * C₂
@@ -292,7 +313,7 @@ for direction in [:forward, :central, :backward]
292
313
grid,
293
314
q,
294
315
coefs,
295
- _make_adaptive_bound_estimator ($ fdm_fun, p, adapt, condition, geom= geom),
316
+ _make_adaptive_bound_estimator ($ fdm_fun, p, q, adapt, condition, geom= geom),
296
317
)
297
318
end
298
319
@@ -327,16 +348,17 @@ end
327
348
328
349
function _make_adaptive_bound_estimator (
329
350
constructor:: Function ,
351
+ p:: Int ,
330
352
q:: Int ,
331
353
adapt:: Int ,
332
354
condition:: Int ;
333
355
kw_args...
334
356
)
335
357
if adapt >= 1
336
358
estimate_derivative = constructor (
337
- q + 1 , q , adapt= adapt - 1 , condition= condition; kw_args...
359
+ p + 1 , p , adapt= adapt - 1 , condition= condition; kw_args...
338
360
)
339
- return (f, x) -> maximum (abs, estimate_derivative (f, x) )
361
+ return (f, x) -> estimate_magitude (x′ -> estimate_derivative (f, x′), x )
340
362
else
341
363
return _make_default_bound_estimator (condition= condition)
342
364
end
0 commit comments