Skip to main content
`Ctrl`+`K`

# Cython optimize root finding API

The underlying C functions for the following root finders can be accessed directly using Cython:

- `bisect`

- `ridder`

- `brenth`

- `brentq`

The Cython API for the root finding functions is similar except there is no `disp` argument. Import the root finding functions using `cimport` from `scipy.optimize.cython_optimize`.

    from scipy.optimize.cython_optimize cimport bisect, ridder, brentq, brenth

## Callback signature

The zeros functions in `cython_optimize` expect a callback that takes a double for the scalar independent variable as the 1st argument and a user defined `struct` with any extra parameters as the 2nd argument.

    double (*callback_type)(double, void*) noexcept

## Examples

Usage of `cython_optimize` requires Cython to write callbacks that are compiled into C. For more information on compiling Cython, see the Cython Documentation.

These are the basic steps:

1. Create a Cython `.pyx` file, for example: `myexample.pyx`.

2. Import the desired root finder from `cython_optimize`.

3. Write the callback function, and call the selected root finding function passing the callback, any extra arguments, and the other solver parameters.

       from scipy.optimize.cython_optimize cimport brentq

       # import math from Cython
       from libc cimport math

       myargs = {'C0': 1.0, 'C1': 0.7}  # a dictionary of extra arguments
       XLO, XHI = 0.5, 1.0  # lower and upper search boundaries
       XTOL, RTOL, MITR = 1e-3, 1e-3, 10  # other solver parameters

       # user-defined struct for extra parameters
       ctypedef struct test_params:
           double C0
           double C1

       # user-defined callback
       cdef double f(double x, void *args) noexcept:
           cdef test_params *myargs = <test_params *> args
           return myargs.C0 - math.exp(-(x - myargs.C1))

       # Cython wrapper function
       cdef double brentq_wrapper_example(dict args, double xa, double xb,
                                          double xtol, double rtol, int mitr):
           # Cython automatically casts dictionary to struct
           cdef test_params myargs = args
           return brentq(
               f, xa, xb, <test_params *> &myargs, xtol, rtol, mitr, NULL)

       # Python function
       def brentq_example(args=myargs, xa=XLO, xb=XHI, xtol=XTOL, rtol=RTOL,
                          mitr=MITR):
           '''Calls Cython wrapper from Python.'''
           return brentq_wrapper_example(args, xa, xb, xtol, rtol, mitr)

4. If you want to call your function from Python, create a Cython wrapper, and a Python function that calls the wrapper, or use `cpdef`. Then, in Python, you can import and run the example.

       from myexample import brentq_example

       x = brentq_example()
       # 0.6999942848231314

5. Create a Cython `.pxd` file if you need to export any Cython functions.

## Full output

The functions in `cython_optimize` can also copy the full output from the solver to a C `struct` that is passed as its last argument. If you don’t want the full output, just pass `NULL`. The full output `struct` must be type `zeros_full_output`, which is defined in `scipy.optimize.cython_optimize` with the following fields:

- `int funcalls`: number of function calls

- `int iterations`: number of iterations

- `int error_num`: error number

- `double root`: root of function

The root is copied by `cython_optimize` to the full output `struct`. An error number of -1 means a sign error, -2 means a convergence error, and 0 means the solver converged. Continuing from the previous example:

    from scipy.optimize.cython_optimize cimport zeros_full_output

    # cython brentq solver with full output
    cdef zeros_full_output brentq_full_output_wrapper_example(
            dict args, double xa, double xb, double xtol, double rtol,
            int mitr):
        cdef test_params myargs = args
        cdef zeros_full_output my_full_output
        # use my_full_output instead of NULL
        brentq(f, xa, xb, &myargs, xtol, rtol, mitr, &my_full_output)
        return my_full_output

    # Python function
    def brent_full_output_example(args=myargs, xa=XLO, xb=XHI, xtol=XTOL,
                                  rtol=RTOL, mitr=MITR):
        '''Returns full output'''
        return brentq_full_output_wrapper_example(args, xa, xb, xtol, rtol,
                                                  mitr)

    result = brent_full_output_example()
    # {'error_num': 0,
    #  'funcalls': 6,
    #  'iterations': 5,
    #  'root': 0.6999942848231314}