|
| 1 | +""" |
| 2 | +```julia |
| 3 | +Halley(; chunk_size = Val{0}(), autodiff = Val{true}(), |
| 4 | + diff_type = Val{:forward}) |
| 5 | +``` |
| 6 | +
|
| 7 | +A low-overhead implementation of Halley's Method. This method is non-allocating on scalar |
| 8 | +and static array problems. |
| 9 | +
|
| 10 | +!!! note |
| 11 | +
|
| 12 | + As part of the decreased overhead, this method omits some of the higher level error |
| 13 | + catching of the other methods. Thus, to see better error messages, use one of the other |
| 14 | + methods like `NewtonRaphson` |
| 15 | +
|
| 16 | +### Keyword Arguments |
| 17 | +
|
| 18 | +- `chunk_size`: the chunk size used by the internal ForwardDiff.jl automatic differentiation |
| 19 | + system. This allows for multiple derivative columns to be computed simultaneously, |
| 20 | + improving performance. Defaults to `0`, which is equivalent to using ForwardDiff.jl's |
| 21 | + default chunk size mechanism. For more details, see the documentation for |
| 22 | + [ForwardDiff.jl](https://juliadiff.org/ForwardDiff.jl/stable/). |
| 23 | +- `autodiff`: whether to use forward-mode automatic differentiation for the Jacobian. |
| 24 | + Note that this argument is ignored if an analytical Jacobian is passed; as that will be |
| 25 | + used instead. Defaults to `Val{true}`, which means ForwardDiff.jl is used by default. |
| 26 | + If `Val{false}`, then FiniteDiff.jl is used for finite differencing. |
| 27 | +- `diff_type`: the type of finite differencing used if `autodiff = false`. Defaults to |
| 28 | + `Val{:forward}` for forward finite differences. For more details on the choices, see the |
| 29 | + [FiniteDiff.jl](https://github.com/JuliaDiff/FiniteDiff.jl) documentation. |
| 30 | +""" |
| 31 | +struct Halley{CS, AD, FDT} <: AbstractNewtonAlgorithm{CS, AD, FDT} |
| 32 | + function Halley(; chunk_size = Val{0}(), autodiff = Val{true}(), |
| 33 | + diff_type = Val{:forward}) |
| 34 | + new{SciMLBase._unwrap_val(chunk_size), SciMLBase._unwrap_val(autodiff), |
| 35 | + SciMLBase._unwrap_val(diff_type)}() |
| 36 | + end |
| 37 | +end |
| 38 | + |
| 39 | +function SciMLBase.__solve(prob::NonlinearProblem, |
| 40 | + alg::Halley, args...; abstol = nothing, |
| 41 | + reltol = nothing, |
| 42 | + maxiters = 1000, kwargs...) |
| 43 | + f = Base.Fix2(prob.f, prob.p) |
| 44 | + x = float(prob.u0) |
| 45 | + fx = f(x) |
| 46 | + # fx = float(prob.u0) |
| 47 | + if !isa(fx, Number) || !isa(x, Number) |
| 48 | + error("Halley currently only supports scalar-valued single-variable functions") |
| 49 | + end |
| 50 | + T = typeof(x) |
| 51 | + |
| 52 | + if SciMLBase.isinplace(prob) |
| 53 | + error("Halley currently only supports out-of-place nonlinear problems") |
| 54 | + end |
| 55 | + |
| 56 | + atol = abstol !== nothing ? abstol : |
| 57 | + real(oneunit(eltype(T))) * (eps(real(one(eltype(T)))))^(4 // 5) |
| 58 | + rtol = reltol !== nothing ? reltol : eps(real(one(eltype(T))))^(4 // 5) |
| 59 | + |
| 60 | + if typeof(x) <: Number |
| 61 | + xo = oftype(one(eltype(x)), Inf) |
| 62 | + else |
| 63 | + xo = map(x -> oftype(one(eltype(x)), Inf), x) |
| 64 | + end |
| 65 | + |
| 66 | + for i in 1:maxiters |
| 67 | + if alg_autodiff(alg) |
| 68 | + fx = f(x) |
| 69 | + dfdx(x) = ForwardDiff.derivative(f, x) |
| 70 | + dfx = dfdx(x) |
| 71 | + d2fx = ForwardDiff.derivative(dfdx, x) |
| 72 | + else |
| 73 | + fx = f(x) |
| 74 | + dfx = FiniteDiff.finite_difference_derivative(f, x, diff_type(alg), eltype(x), |
| 75 | + fx) |
| 76 | + d2fx = FiniteDiff.finite_difference_derivative(x -> FiniteDiff.finite_difference_derivative(f, x), |
| 77 | + x, diff_type(alg), eltype(x), fx) |
| 78 | + end |
| 79 | + iszero(fx) && |
| 80 | + return SciMLBase.build_solution(prob, alg, x, fx; retcode = ReturnCode.Success) |
| 81 | + Δx = (2*dfx^2 - fx*d2fx) \ (2fx*dfx) |
| 82 | + x -= Δx |
| 83 | + if isapprox(x, xo, atol = atol, rtol = rtol) |
| 84 | + return SciMLBase.build_solution(prob, alg, x, fx; retcode = ReturnCode.Success) |
| 85 | + end |
| 86 | + xo = x |
| 87 | + end |
| 88 | + |
| 89 | + return SciMLBase.build_solution(prob, alg, x, fx; retcode = ReturnCode.MaxIters) |
| 90 | +end |
0 commit comments