# Query status of the solution when using Roots

Edited to make it more specific
I would like to query status of the solution when using the Roots package, before the function returns an answer. I can see in the source code of “find_zero.jl” that it has some functionality `state.convergence_failed = true` and also a function `assess_convergence(method::Any, state::UnivariateZeroState{T,S}, options) where {T,S}` , but there is no documentation on how to use it. I have given a MWE and my attempts at resolving it below.

My questions is:
How to query the status of the solution before it returns a value? e.g. I would like to use the Schroder method first, and if it fails to converge then it should switch to Halley method. If the roots are complex or the algorithm fails to converge then it should return NaN.

``````using Roots
function root_func(c;guess=0.01,max_evals=100)
t = collect(0:length(c)-1)
f(x) = sum(@views c[i]/(1+x)^t[i] for i in 1:length(c))
#find derivatives of f(x)
df(x) = sum(@views -t[i]*c[i]/(1+x)^(t[i]+1) for i in 1:length(c))
ddf(x) = sum(@views (t[i]^2+t[i])*c[i]/(1+x)^(t[i]+2) for i in 1:length(c))
return find_zero((f,df,ddf),guess,Roots.Schroder();maxevals=max_evals)
end
``````

Example usage:
`root_func([-100,10,100,1,1]`

My attempts at checking status of the solution before switching methods or returning a value are given here:

``````using Roots
function root_func(c;guess=0.01,max_evals=100)
t = collect(0:length(c)-1)
f(x) = sum(@views c[i]/(1+x)^t[i] for i in 1:length(c))
#find derivatives of f(x)
df(x) = sum(@views -t[i]*c[i]/(1+x)^(t[i]+1) for i in 1:length(c))
ddf(x) = sum(@views (t[i]^2+t[i])*c[i]/(1+x)^(t[i]+2) for i in 1:length(c))
r = find_zero((f,df,ddf),guess,Roots.Schroder();maxevals=max_evals)
if r.convergence_failed = false
return r
end
r = find_zero((f,df,ddf),guess,Roots.Halley();maxevals=max_evals)
if r.convergence_failed = false
return r
else return NaN
end
end
``````

The part of the code to modify would be the method here https://github.com/JuliaMath/Roots.jl/blob/master/src/find_zero.jl#L796 which implements an algorithm that checks if a bracket has occurred in which case the method is switched. For your interest, you would call `update_state`, check the conditions you wanted and if you want to switch, you would switch the method, update the `state` and continue with `update_state` until convergence or failure to converge. One caveat, I forget how much support there is for complex zeros, if I recall correctly that is limited to a few methods.

Thanks. Do I necessarily have to modify the source code of “find_zero.jl” for this? This is difficult for me as I am new to Julia.

All I want from my function is to switch from Schroder to Halley when it fails to converge rather than returning an error. And if Halley’s method also fails to converge than it should just return NaN rather than an error message.

This could be done along these lines (using a internal form of `find_zero` which returns NaN instead of an error):

``````using Roots
using ForwardDiff
D(f) = x -> ForwardDiff.derivative(f, float(x))

f(x) = (x^2+1)*(x-1)*(x-2)
a = 1.5

F = Roots.callable_function((f, D(f), D(D(f))))
M = Roots.Schroder()
state = Roots.init_state(M, F, a)
options = Roots.init_options(M, state)

val = find_zero(M, F, options, state)

if isnan(val)
M = Roots.Halley()
state = Roots.init_state(M, F, a)
val = find_zero(M, F, options, state)
end

val
``````

In this case, I’m not sure what functions will not satisfy Schroder, but will satisfy Halley, but presumably you know of one or two.

1 Like

Thank you! This solves my problem.

Schroder’s method fails for `root_func([10,10]` but Halley’s method would work using the code in the original post, though I am not sure if this a good example