Package: pyroots
pyrootsAbstractA Python library implementing various root finding methods for
single-variable functions.Currently the following methods have been implemented:Thebisectmethod.Theriddermethod.Thebrentmethod.With regard toBrent's method, there are two implementations, the
first one uses inverse quadratic extrapolation (Brentq) while the
other ones uses hyperbolic extrapolation (Brenth).If you don't know which method to use, you should probably useBrentq.
That being said,Bisectmethod is safe and slow (i.e. lots of iterations).Example# define the function whose root you are searchingdeff(x,a):returnx**2-a+1# Create the Solver object (instead of Brentq you could also import Brenth/Ridder/Bisect)frompyrootsimportBrentqbrent=Brentq(epsilon=1e-5)# solve the function in `[-3, 0]` while `a` is equal to 2result=brent(f,-3,0,a=2)print(result)will output:converged : True
   message : Solution converged.
iterations :   6
func calls :   9
        x0 :    -1.0000000748530762
      xtol :     0.0000000000000002
     f(x0) :     0.0000001497061579
   epsilon :     0.0000100000000000
   x_steps : [-3, 0, -0.3333333333333333, -1.6666666666666665, -0.7777777777777779, -1.0686868686868687, -0.9917335278385606, -0.9997244260982788, -1.0000000748530762]
  fx_steps : [8, -1, -0.8888888888888888, 1.7777777777777772, -0.3950617283950615, 0.14209162330374459, -0.01646460976088293, -0.0005510718624670563, 1.4970615791476405e-07]RationaleThe functionality ofpyrootsis already implemented inscipy, so the
natural question is why rediscover the wheel?Well, the main reason is thatscipyis a huge dependency.Pyrootson
the other hand is just a single package that is easily installed and
that you can easily bundle withpy2exeor similar projects. It doesn't
even need to get installed, just throw thepyrootsfolder in your
project and you are ready to go.Apart from that, the API used byscipy's functions is not very
user-friendly. For example you can't use keyword arguments for your
functions. Moreover, inscipythere is no reliable way to define how
many digits of accuracy you want in the obtained root. For example, you
may ask for 6 digits, but scipy may calculate up to 14 (or 12 or
whatever) digits. The main implication of this "glitch" is that scipy's
method may evaluate the function more times than those really needed. If
the function calculates something trivial like the functions in the
following examples, then these extra function calls are no big deal, but
if your functions take significant time to evaluate ,e.g. more than
seconds, then this can quickly become annoying, or even, simply
unacceptable, e.g. the function takes some minutes to return a value.Installationwith pip:pip install pyrootsUsageAll the solvers share the same API, so you can easily switch between the
various methods.FunctionThe function whose root you are searching must take at least a single
argument and return a single number. This first argument is also the
dependent variable and, apart from that, the function can also take any
number of positional/keyword arguments. For example the following
functions are totally valid ones:deff(x,a):returnx**2-a+1defg(x,a,b,c=3):returnx**2+a**b-cSolver ObjectsThe first thing you have to do is to create aSolverobject for the
method you want to use:frompyrootsimportBrentqbrent=Brentq()When you create theSolverobject, you can specify several parameters
that will affect the convergence. The most important are:epsilon which specifies the number of digits that will be taken
under consideration when checking for convergence. It defaults to1e-6.raise_on_failwhich will raise an exception if convergence failed.
It defaults to True.Using the above function definitions, in order to find the root offyou must first define an interval that contains the root. Let's say that
this interval is defined as[xa, xb]. In this case you will call the
solver like this:deff(x,a):returnx**2-a+1solver=Brentq()result=solver(f,xa,xb,a=3)Result ObjectsAll the methods return aResultobject that has the following
attributes:result.x0               # the root
result.fx0              # the value of ``f(x0)`
result.convergence      # True/False
result.iterations       # the number of iterations
result.func_calls       # the number of function evaluations.
result.msg              # a descriptive message regarding the convergence (or the failure of convergence)
result.x_steps          # a list containing the x values  that have been tried while the solver run
result.fx_steps         # a list containing the f(x) values that have been calculated while the solver runIf, for some reason, convergence cannot be achieved, then aConvergenceErroris raised. If you don't want that to happen, then you
have to passFalseas the value ofraise_on_failargument:deff(x):returnx**2-1result=brent(f,xa=-10,xb=-5,raise_on_fail=False)print(result)APIEach solver factory has the following signature:SolverFactory(epsilon=1e-6,xtol=EPS,max_iter=500,raise_on_fail=True,debug_precision=10)where:epsilonis the required precision of the solution, i.e. a solution
is achieved when|f(x0)|is smaller thanepsilon.max_iteris the maximum allowed number of iterations.raise_on_failis a boolean flag indicating whether or not an
exception should be raised if convergence fails. It defaults to TrueEach solver object has the following signature:solver_object(f,xa,xb,*args,**kwargs)where:fis the function whose root we are searching.xais the lower bracket of the interval of the solution we search.xbis the upper bracket of the interval of the solution we search.*argsare passed as positional arguments whenfis evaluated.**kwargsare passed as keyword arguments whenfis evaluated.DocumentationFor the time being documentation is not yet ready, but the examples in
the README should be enough to get your feet wet.The source code repository of pyroots can be found at:https://github.com/pmav99/pyrootsFeedback and contributions are greatly appreciated.pmav99 <gmail>https://www.embeddedrelated.com/showarticle/855.php