# A Unified Interface for Rootfinding

**URL:** <https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698>\
**Category:** Numerics\
**Tags:** package\
**Created:** [December 2, 2016, 4:52pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698 "2016-12-02T16:52:56Z")\
**Posts on this page:** 20\
**Page:** 1

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [December 2, 2016, 4:52pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/1 "2016-12-02T16:52:56Z")

</div>

One of the most useful developments of Julia is the concept of the metapackage. A package management system coupled with multiple dispatch has lead to ecosystems like Plots.jl, DiffEqBase.jl (DifferentialEquations.jl), and MathProgBase.jl (JuMP) which allow you to use the same high level code to plug into many different packages in the same regime. This has been not only a big boost for users (there’s one interface to know!), but it’s actually been crazy amazing for package development: you can write a package which targets one interface and now all of the methods are available.

To build off of these successes, I would like to propose some kind of RootFindingBase.jl. Building off of previous interfaces, what I would like to see is something like:

```julia
find_zeros(f,interval,x0,alg(),kwargs...)

```

which would dispatch `find_zeros` using the type of `alg` to different solver methods in different packages. Candidates that I know of include:

1. Roots.jl
2. Sundials.jl (KINSOL)
3. NLsolve.jl

The idea is so that way I could so something like

```julia
find_zeros(f,(0.0,1.0),0.5,kinsol(linear_solver=banded),abstol=1e-5)

```

to call Sundials.jl and

```julia
find_zeros(f,(0.0,1.0),0.5,nlsolve(autodiff=true),abstol=1e-5)

```

to call NLsolve.jl

The reason why this is important to me is because it would allow me to write algorithms at this level, and let the user choose any nonlinear solver which fits the problem (and also easily benchmark between them!). (This will be really nice for JuliaDiffEq because then implicit methods could easily swap out rootfinding algorithms!).

The questions going forward are:

1. Are package maintainers on-board to add this kind of interface?
2. What are the problems with the current proposal?
3. How do we make it robust to the different types of rootfinding problems which exist?

Some details to discuss are:

1. The form of `f`. Allow both inplace and not inplace (allow not inplace for the univariate case, and auto-convert to an inplace function for problems on vectors?)
2. Not every method needs an interval or an initial condtion. What do we do here?
3. What should the solution type look/act like?
4. Anything else you can think of.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 3, 2016, 4:12pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/2 "2016-12-03T16:12:42Z")

</div>

I think that the idea is nice but it remains to be seen whether you can unify rootfinding in a single interface. Perhaps it would be best if you made a library that does this, similarly to [Plots.jl](https://github.com/tbreloff/Plots.jl) — I guess most problems will surface when you start doing this and can be dealt with then. Regarding syntax, I think you could try following [Optim.jl](http://www.juliaopt.org/Optim.jl/latest/user/config/)’s conventions, with something like

```julia
find_zeros(functionobject, # would also specify inplace, derivatives, like Optim
           initial_x_or_interval, # a point for quasi-Newton, interval for bisection
           optimizer_object, # eg kinsol(...)
           general_options_object) # tolerances, whether to do AD, ...

```

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [December 4, 2016, 5:22am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/3 "2016-12-04T05:22:00Z")

</div>

How would global rootfinders fit in, as in ApproxFun’s `roots`?

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [December 6, 2016, 10:42pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/4 "2016-12-06T22:42:44Z")

</div>

Maybe separate local and global rootfinding problems?

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [December 7, 2016, 9:53am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/5 "2016-12-07T09:53:48Z")

</div>

I wonder if a better idiom is to follow DifferentialEquations.jl, so that you’d setup up a problem on which you call roots.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [December 13, 2016, 3:24am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/6 "2016-12-13T03:24:26Z")

</div>

Yeah, what should the problem types be `LocalNonlinearSolve` and `GlobalNonlinearSolve`? Or is more necessary?

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [December 13, 2016, 3:30am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/7 "2016-12-13T03:30:18Z")

</div>

Having `Solve` in the problem name doesn’t seem right… I’d say something like `RootProblem` and `RootsProblem` to replicate `eig` vs `eigs`.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [December 13, 2016, 4:18am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/8 "2016-12-13T04:18:56Z")

</div>

I don’t like the difference between `s` and not. I think it should be a bit more explicit: `GlobalRootProblem` vs `LocalRootProblem`.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [December 13, 2016, 8:20pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/9 "2016-12-13T20:20:39Z")

</div>

This all doesn’t seem too hard to actually do and would get some really nice/usable results: would it be a good Google Summer of Code project?

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [December 14, 2016, 6:34am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/10 "2016-12-14T06:34:13Z")

</div>

After roaming around the package ecosystem a bit, here’s what I got so far.

- Two types: `LocalRootProblem` and `GlobalRootProblem`.
- Both take a general `f`. This would allow packages like Roots.jl to dispatch on `typeof(f)==Polynomial`, or ApproxFun.jl to write rootfinding dispatches on `Fun`.
- Both have an optional argument for `interval = (-Inf,Inf)`. Some methods may need to error if these values are `Inf`, but this default means “all real numbers”. (Is there a way or a need to generalize this to complex?).
- Optional argument for a Jacobian. Again, this should be left with no type-restriction. It’s an optional argument because of dispatch, with default being `nothing`.
- A `LocalRootProblem` also has an optional (or keyword?) argument for the initial condtional `x0`.

Maybe we should just go with keyword arguments because order is hard to choose, and keyword args should dispatch soon enough in Julia.

I propose usage which looks like this. We can accept two function definitions. A non-inplace version is good for univariate problems:

```julia
type LocalRootProblem{F,G,probType} <: RootProblem
  f::F
  g::G # Jacobian
  interval::Tuple{probType,probType}
  x0::probType
end

...

```

Usage:

```julia
f(x) = 2x
g(x) = 2 # The Jacobian
prob = LocalRootProblem(f,init=x0,interval=(0.0,1.0),jac=jac)

```

This makes a problem where the Jacobian is known while

```julia
prob = LocalRootProblem(f,init=x0,interval=(0.0,1.0))

```

has the second type-parameter `Void` which tells the solver that the Jacobian does not exist (a function `has_jac(prob::RootProblem{F,G,IType}) = G!=Void` can be used for this check at compile-time). Then it could be like:

```julia
find_zeros(prob,
                  nlsolve(nlsolve_specific_option=...),
                  general_options_object)

```

matching Optim.jl like @Tamas_Papp mentioned. The in-place version for functions would be

```julia
function f!(x,out)
  out[1] = 2*x[1]
end

```

which would be the same equation as above. `GlobalRootProblem` should then be similar.

One thing I noticed is that the problem type should match both `x0` and the `interval`. This would be the type that the solution is found in. Thus one can set the solvers to use `BigFloat` values by setting `interval=(big(a),big(b))` or `x0 = big(...)`. I believe any method has to either set an initial condition or an interval to solve on, so this will always work. Correct me if I’m wrong about that.

Are there any holes in this proposal? If not, I think I could make a new package in JuliaMath and put up a first draft and put some PRs out to Roots.jl, Sundials.jl, and NLsolve.jl. Then I know @dlfivefifty wants to make dispatches for ApproxFun.jl on `Fun` types, and so it’ll already gain some adoption. What’s a good name for the package? RootBase.jl?

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 14, 2016, 6:59am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/11 "2016-12-14T06:59:04Z")

</div>

@ChrisRackauckas: nice summary. I would suggest the following:

1. Embed the the function and its derivatives (particularly the Jacobian) in something like `Optim`’s `DifferentiableFunction` (or even the same thing, would be nice not to proliferate types). This would also allow approximations like from `ApproxFun` you mention above.

2. replace `interval` with `domain`, which can be ℝ (high time we had a type representing that), [a,∞), [a,b] (see the issue for IntervalSets.jl I link below), and Cartesian products of these (for box constraints along coordinates), of course for multivariate we need a type for this too.

[https://github.com/JuliaMath/IntervalSets.jl/issues/9](https://github.com/JuliaMath/IntervalSets.jl/issues/9)

---

<div class="post-metadata">

**Author:** ![dlfivefifty](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/dlfivefifty/32/1959_2.png) [@dlfivefifty](https://discourse.julialang.org/u/dlfivefifty)\
**Post date:** [December 14, 2016, 9:23am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/12 "2016-12-14T09:23:57Z")

</div>

To be clear, there’s three very different places where ApproxFun fits in:

1. `roots(f::Fun)` in ApproxFun/src/Extras/roots.jl is an algorithm (from Chebfun) for finding all roots, and could be one of the methods for solving `GlobalRootProblem`.
2. The implementation of `roots(f::Fun)` could pass through `GlobalRootProblem` and potentially use other algorithms besides the one in ApproxFun/src/Extras/roots.jl
3. A generalization of (local) root finding is solving ODEs, which is currently implemented with Newton iteration:

```julia
x = Fun()
N = u->[u(-1.)-c;u(1.);ε*u''+6*(1-x^2)*u'+u^2-1.]
u = newton(N,u0)

```

This could potentially be solved by a unified interface by constructing a `LocalRootProblem` whose `probType` is `Fun`

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [December 15, 2016, 4:36am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/13 "2016-12-15T04:36:42Z")

</div>

> [@Tamas\_Papp](#):
>
> Embed the the function and its derivatives (particularly the Jacobian) in something like Optim’s DifferentiableFunction (or even the same thing, would be nice not to proliferate types). This would also allow approximations like from ApproxFun you mention above.

What’s the purpose of the `DifferentiableFunction` type? Why does it help?

> [@Tamas\_Papp](#):
>
> replace interval with domain, which can be ℝ (high time we had a type representing that), [a,∞), [a,b] (see the issue for IntervalSets.jl I link below), and Cartesian products of these (for box constraints along coordinates), of course for multivariate we need a type for this too.

I like this idea of using domains. It’s finding the right implementation that would be the problem.

---

<div class="post-metadata">

**Author:** ![Tamas\_Papp](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/tamas_papp/32/25949_2.png) [@Tamas\_Papp](https://discourse.julialang.org/u/Tamas_Papp)\
**Post date:** [December 15, 2016, 8:36am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/14 "2016-12-15T08:36:33Z")

</div>

> [@ChrisRackauckas](#):
>
> What’s the purpose of the DifferentiableFunction type? Why does it help?

I am probably not the best person to ask about it, but see this discussion and PR:

> <https://github.com/JuliaNLSolvers/Optim.jl/issues/16>
>
> We currently have a mixed API for optimization: the functions I wrote work very …differently from the functions that Tim wrote. To unify things, I'd like to propose a new API that I hope we can all standardize on. The proposal is quite long, but I think it touches on all of the issues we need to confront. I'm opening it as an issue because I expect we'll want to debate the design for a while before implementing anything.
> 
> To simplify the discussion, let's introduce some notation.
> 
> My API worked exclusively with pure functions, which I'll refer to as:
> \- \`f\` denotes a function from R^n to R. The result is returned as a \`Real\` of some sort.
> \- \`g\` denotes the gradient of \`f\`, which makes \`g\` a function from R^n to R^n. The result is returned as a \`Vector{T}\` for some \`Real\` type \`T\`.
> \- \`h\` denotes the Hessian of \`f\`, which makes \`h\` a function from R^n to R^n\*m. The result is returned as a \`Matrix{T}\` for some \`Real\` type \`T\`.
> 
> Tim's API employed mutating functions, which I'll refer to as:
> \- \`f\` is the same as above.
> \- \`g!\` denotes the gradient of \`f\`, but one which mutates an input argument so that it is called as \`g!(storage, x)\`. I'd like to transition this over to \`g!(x, storage)\`. As will be seen, I'd also like to remove the \`nothing\` arguments being used in the current implementation. \_Because function is impure, nothing is returned\_.
> \- \`h!\` denotes the Hessian of \`f\`, but one which mutates an input argument so that it is called as \`h!(storage, x)\`. Again, I'd like to transition this over to \`h!(x, storage)\`. \_Because function is impure, nothing is returned\_.
> \- \`fg!\` denotes a coupled pair of function and gradient that get evaluated simultaneously for efficiency. This coupled pair is called as \`fg!(x, storage)\` and returns the value of \`f\` evaluated at \`x\` after mutating \`storage\`.
> 
> One could also consider functions like \`gh!\` and \`fgh!\`, but I'm not currently aware of a proposed use for those things.
> 
> Using this notation, my proposed new API is the following:
> \- We should entirely remove the use of \`g\` and \`h\` and enforce the use of mutating functions \`g!\` and \`h!\`. This may confuse some users, but I think the gains are worth the pain.
> \- We should allow automatic creation of methods for \`g!\` and \`h!\` using finite differencing. I've already added the ability to do finite-differencing by mutating an array to the Calculus package in preparation for this.
> \- We should construct ad hoc immutable types that can wrap up functions like \`f\`, \`g!\` and \`fg!\` into a single unit that permits multiple dispatch. These differentiable functions will become the core backend construct of the Optim package. End-users will not need to provide them, because we will generate these values for users automatically. But users who want to exploit forms like \`fg!\` can use these types, which will prevent the automatic creation of wrappers.
> 
> Specifically, I propose creating the following types and methods:
> 
> \`\`\`
> immutable OnceDifferentiableFunction
> f::Function
> g!::Function
> end
> 
> immutable CoupledOnceDifferentiableFunction
> f::Function
> g!::Function
> fg!::Function
> end
> 
> immutable TwiceDifferentiableFunction
> f::Function
> g!::Function
> h!::Function
> end
> \`\`\`
> 
> Using these functions, we could create methods like the following:
> 
> For pure function calls:
> \- \`callf(Function, x)\`
> \- \`callf(OnceDifferentiableFunction, x)\`
> \- \`callf(CoupledOnceDifferentiableFunction, x)\`
> \- \`callf(TwiceDifferentiableFunction, x)\`
> 
> For mutating gradient function calls:
> \- \`callg!(OnceDifferentiableFunction, x, storage)\`
> \- \`callg!(CoupledOnceDifferentiableFunction, x, storage)\`
> \- \`callg!(TwiceDifferentiableFunction, x, storage)\`
> 
> For mutating Hessian function calls:
> \- \`callh!(TwiceDifferentiableFunction, x, storage)\`
> 
> For simultaneous function and mutating gradient function calls:
> \- \`callfg!(OnceDifferentiableFunction, x, storage)\`
> \- \`callfg!(CoupledOnceDifferentiableFunction, x, storage)\`
> \- \`callfg!(TwiceDifferentiableFunction, x, storage)\`
> 
> Using these types and functions, we should be able to express all of the computations we're doing now while doing much less memory allocation. Also, the use of multiple dispatch should make it easier to do redirection by automatically creating gradients when needed.

> <https://github.com/JuliaNLSolvers/Optim.jl/pull/17>
>
> This pull request represents the current \_unfinished\_ state of my work on a new …API for unconstrained optimization in Julia. It implements the ideas described in #16.
> 
> There's still another week or so of work to do before this work will be ready for merging, but I wanted to get a demo out there to get early feedback.
> 
> It would be good to hear from @andreasnoackjensen about the changes I've made to the backtracking line search function. In my ad hoc testing, I've found that completely removing the loop in which the step-size can increase above \`1.0\` substantially lowers the number of iterations required to reach convergence. This may be an artifact of the problems I've tried out, but I'd like to make sure that we push as hard as possible on improving the performance of these functions.

In particular [this explanation](https://github.com/JuliaOpt/Optim.jl/pull/17#issuecomment-16579918).

---

<div class="post-metadata">

**Author:** ![johnmyleswhite](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/johnmyleswhite/32/31_2.png) [@johnmyleswhite](https://discourse.julialang.org/u/johnmyleswhite)\
**Post date:** [December 15, 2016, 3:40pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/15 "2016-12-15T15:40:27Z")

</div>

I don’t think `DifferentiableFunction` is that useful really (as it could clearly be replaced by tuples of functions), but I believe people intend to make more complex use of it in the future by introducing things like memoization. If they weren’t, I would propose getting rid of it. But the indirection could be put to good use when building more complex features.

---

<div class="post-metadata">

**Author:** ![simonbyrne](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/simonbyrne/32/19_2.png) [@simonbyrne](https://discourse.julialang.org/u/simonbyrne)\
**Post date:** [December 15, 2016, 4:00pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/16 "2016-12-15T16:00:44Z")

</div>

I’ve recently been thinking that this sort of thing might now be better handled using traits, e.g.

```julia
has_gradient(::Function) = false # generic fallback

function rosenbrock(x::Vector)
    return (1.0 - x[1])^2 + 100.0 * (x[2] - x[1]^2)^2
end
has_gradient(::typeof(rosenbrock)) = true
function gradient!(::typeof(rosenbrock), x::Vector, storage::Vector)
    storage[1] = -2.0 * (1.0 - x[1]) - 400.0 * (x[2] - x[1]^2) * x[1]
    storage[2] = 200.0 * (x[2] - x[1]^2)
end

```

Not sure how this would work generally though

---

<div class="post-metadata">

**Author:** ![mauro3](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mauro3/32/292_2.png) [@mauro3](https://discourse.julialang.org/u/mauro3)\
**Post date:** [December 15, 2016, 7:12pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/17 "2016-12-15T19:12:08Z")

</div>

This is a bit how we do it over in DiffEqBase: [https://github.com/JuliaDiffEq/DiffEqBase.jl/blob/6cc12def8af8e7e51b458d65f48e43d97aee4af1/src/extended\_functions.jl](https://github.com/JuliaDiffEq/DiffEqBase.jl/blob/6cc12def8af8e7e51b458d65f48e43d97aee4af1/src/extended_functions.jl)

---

<div class="post-metadata">

**Author:** ![pkofod](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/pkofod/32/2179_2.png) [@pkofod](https://discourse.julialang.org/u/pkofod)\
**Post date:** [December 18, 2016, 9:29pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/18 "2016-12-18T21:29:57Z")

</div>

Up front: I know a lot can be done using proper documentation and educational material, but…

Don’t you think the average _user_ of a general optimization package might be confused by this way of specifying it?

That being said, we really don’t use the whole `(Twice)DifferentiableFunction`-machinery for much right now, but if you look through the issues, there are some different ideas that people have for using it, and I do intend to see (soon) if we can implement some of these ideas or if we should ditch it all together.

---

<div class="post-metadata">

**Author:** ![ChrisRackauckas](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/chrisrackauckas/32/77_2.png) [@ChrisRackauckas](https://discourse.julialang.org/u/ChrisRackauckas)\
**Post date:** [January 23, 2017, 7:37pm UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/19 "2017-01-23T19:37:10Z")

</div>

Where exactly is this at now? I kind of got lost but want to revive this.

---

<div class="post-metadata">

**Author:** ![j\_verzani](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/j_verzani/32/8551_2.png) [@j\_verzani](https://discourse.julialang.org/u/j_verzani)\
**Post date:** [January 24, 2017, 12:56am UTC](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698/20 "2017-01-24T00:56:03Z")

</div>

FWIW `Roots.jl` had a rewrite to be able to support this effort once an API is decided on.

[Next page](https://discourse.julialang.org/t/a-unified-interface-for-rootfinding/698.md?page=2)
