From 1142e4823cba83f4bf0966675c8d5aee446e80f6 Mon Sep 17 00:00:00 2001 From: john verzani Date: Sun, 15 May 2022 12:00:57 -0400 Subject: [PATCH] rework default bracketing method; add find_zerov (#315) --- Project.toml | 2 +- docs/src/index.md | 7 ++++--- src/Bracketing/bisection.jl | 1 - src/find_zero.jl | 34 ++++++++++++++++++++++++++++------ src/trace.jl | 15 +++++++++++++++ 5 files changed, 48 insertions(+), 11 deletions(-) diff --git a/Project.toml b/Project.toml index 96cf759f..ae209d21 100644 --- a/Project.toml +++ b/Project.toml @@ -1,6 +1,6 @@ name = "Roots" uuid = "f2b01f46-fcfa-551c-844a-d8ac1e96c665" -version = "2.0.0" +version = "2.0.1" [deps] CommonSolve = "38540f10-b2f7-11e9-35d8-d573e4eb0ff2" diff --git a/docs/src/index.md b/docs/src/index.md index 5529ed67..998f4175 100644 --- a/docs/src/index.md +++ b/docs/src/index.md @@ -20,9 +20,10 @@ specification of a method. These include: computations allows. Other methods include `Roots.A42`, `Roots.AlefeldPotraShi`, `Roots.Brent`, `Roots.Chandrapatlu`, `Roots.ITP`, `Roots.Ridders`, and ``12``-flavors of - `FalsePosition`. The default bracketing method is `Bisection`, as it - is more robust to some inputs, but `A42` and `AlefeldPotraShi` - typically converge in a few iterations and are more performant. + `FalsePosition`. The default bracketing method is `Bisection` for + the basic floating-point types, as it is more robust to some inputs, + but `A42` and `AlefeldPotraShi` typically converge in a few + iterations and are more performant. * Several derivative-free methods are implemented. These are specified diff --git a/src/Bracketing/bisection.jl b/src/Bracketing/bisection.jl index 18321291..3e5aff77 100644 --- a/src/Bracketing/bisection.jl +++ b/src/Bracketing/bisection.jl @@ -106,7 +106,6 @@ function _middle(x, y) __middle(a, b) end end - ## find middle assuming a,b same sign, finite ## Alternative "mean" definition that operates on the binary representation ## of a float. Using this definition, bisection will never take more than diff --git a/src/find_zero.jl b/src/find_zero.jl index e52da8d4..5f4177a1 100644 --- a/src/find_zero.jl +++ b/src/find_zero.jl @@ -4,6 +4,22 @@ Interface to one of several methods for finding zeros of a univariate function, e.g. solving ``f(x)=0``. +# Arguments +## Positional arguments + +* `f`: the function (univariate or `f(x,p)` with `p` holding parameters) +* `x0`: the initial condition (a value, initial values, or bracketing interval) +* `M`: some `AbstractUnivariateZeroMethod` specifying the solver +* `N`: some bracketing method, when specified creates a hybrid method + +## Keyword arguments + +* `xatol`, `xrtol`: absolute and relatative tolerance to decide if `xₙ₊₁ ≈ xₙ` +* `atol`, `rtol`: absolute and relatative tolerance to decide if `f(xₙ) ≈ 0` +* `maxiters`: specify the maximum number of iterations the algorithm can take. +* `verbose::Bool`: specifies if details about algorithm should be shown +* `tracks`: allows specification of `Tracks` objecs + # Initial starting value For most methods, `x0` is a scalar value indicating the initial value @@ -27,9 +43,9 @@ in case of failure. A method is specified to indicate which algorithm to employ: * There are methods where a bracket is specified: [`Bisection`](@ref), - [`A42`](@ref), [`AlefeldPotraShi`](@ref), - [`Roots.Brent`](@ref), among others. Bisection is the default, but - `A42` generally requires far fewer iterations. + [`A42`](@ref), [`AlefeldPotraShi`](@ref), [`Roots.Brent`](@ref), + among others. Bisection is the default for basic floating point + types`, but `A42` generally requires far fewer iterations. * There are several derivative-free methods: cf. [`Order0`](@ref), [`Order1`](@ref) (also [`Roots.Secant`](@ref)), [`Order2`](@ref) @@ -56,7 +72,9 @@ If no method is specified, the default method depends on `x0`: * If `x0` is a scalar, the default is the more robust `Order0` method. * If `x0` is a tuple, vector, or iterable with `extrema` defined - indicating a *bracketing* interval, then the `Bisection` method is used. + indicating a *bracketing* interval, then the `Bisection` method is + used for `Float64`, `Float32` or `Float16` types; otherwise the + `A42` method is used. The default methods are chosen to be robust; they may not be as efficient as some others. @@ -204,8 +222,12 @@ end # defaults when method is not specified # if a number, use Order0 # O/w use a bracketing method of an assumed iterable -find_zero(f, x0::Number; kwargs...) = find_zero(f, x0, Order0(); kwargs...) -find_zero(f, x0; kwargs...) = find_zero(f, x0, Bisection(); kwargs...) +find_zero_default_method(x0::Number) = Order0() +function find_zero_default_method(x0) + T = eltype(float.(_extrema(x0))) + T <: Union{Float16, Float32, Float64} ? Bisection() : A42() +end +find_zero(f, x0; kwargs...) = find_zero(f, x0, find_zero_default_method(x0); kwargs...) ## --------------- diff --git a/src/trace.jl b/src/trace.jl index 60123a20..78c05f2f 100644 --- a/src/trace.jl +++ b/src/trace.jl @@ -252,3 +252,18 @@ function show_tracks(io::IO, s::Tracks, M::AbstractUnivariateZeroMethod) println(io, "") end + + +## needs better name, but is this useful? +""" + find_zerov(f, x, M; kwargs...) + +Run `find_zero` return a `Tracks` object, not the value, which can be extracted via the `last` method. +""" +function find_zerov(f, x, M; verbose=nothing, kwargs...) + Z = init(ZeroProblem(f,x), M; verbose=true, kwargs...) + solve!(Z) + Z.logger +end +find_zerov(f, x; verbose=nothing, kwargs...) = + find_zerov(f, x, find_zero_default_method(x); kwargs...)