11export FiniteDifferenceMethod, fdm, backward_fdm, forward_fdm, central_fdm, extrapolate_fdm
22
33"""
4- add_tiny( x::Union{AbstractFloat, Integer})
4+ estimate_magitude(f, x::T) where T<:AbstractFloat
55
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`.
89"""
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
1131
1232"""
1333 FiniteDifferences.DEFAULT_CONDITION
209229
210230# Estimate the bound on the derivative by amplifying the ∞-norm.
211231function _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 )
213233 return default_bound_estimator
214234end
215235
@@ -256,17 +276,18 @@ function estimate_step(
256276) where T<: AbstractFloat
257277 p = length (m. coefs)
258278 q = m. q
259- f_x = float (f (x))
260279
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)
264285
265286 # Set the step size by minimising an upper bound on the error of the estimate.
266287 C₁ = ε * sum (abs, m. coefs)
267288 C₂ = M * sum (n -> abs (m. coefs[n] * m. grid[n]^ p), eachindex (m. coefs)) / factorial (p)
268289 # 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))
270291
271292 # Estimate the accuracy of the method.
272293 accuracy = h^ (- q) * C₁ + h^ (p - q) * C₂
@@ -292,7 +313,7 @@ for direction in [:forward, :central, :backward]
292313 grid,
293314 q,
294315 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),
296317 )
297318 end
298319
@@ -327,16 +348,17 @@ end
327348
328349function _make_adaptive_bound_estimator (
329350 constructor:: Function ,
351+ p:: Int ,
330352 q:: Int ,
331353 adapt:: Int ,
332354 condition:: Int ;
333355 kw_args...
334356)
335357 if adapt >= 1
336358 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...
338360 )
339- return (f, x) -> maximum (abs, estimate_derivative (f, x) )
361+ return (f, x) -> estimate_magitude (x′ -> estimate_derivative (f, x′), x )
340362 else
341363 return _make_default_bound_estimator (condition= condition)
342364 end
0 commit comments