def bisect(func, a, b, xtol=1e-6, errorcontrol=True,
           testkwargs=dict(), outside='extrapolate',
           ascending=None,
           disp=False):
    """Find root by bysection search.

    If the function evaluation is noisy then use `errorcontrol=True` for adaptive
    sampling of the function during the bisection search.

    Parameters
    ----------
    func: callable
        Function of which the root should be found. If `errorcontrol=True`
        then the function should be derived from `AverageBase`.
    a, b: float
        initial interval
    xtol: float
        target tolerance for interval size
    errorcontrol: boolean
        if true, assume that function is derived from `AverageBase`.
    testkwargs: only for `errorcontrol=True`
        see `AverageBase.test0`
    outside: ['extrapolate', 'raise']
        How to handle the case where f(a) and f(b) have same sign,
        i.e. where the root lies outside of the interval.
        If 'raise' throws a `BisectException`.
    ascending: allow passing in directly whether function is ascending or not
        if ascending=True then it is assumed without check that f(a) < 0 and f(b) > 0
        if ascending=False then it is assumed without check that f(a) > 0 and f(b) < 0

    Returns
    -------
    float, root of function
    """
    search = True
    # check whether function is ascending or not
    if ascending is None:
        if errorcontrol:
            testkwargs.update(dict(type_='smaller', force=True))
            fa = func.test0(a, **testkwargs)
            fb = func.test0(b, **testkwargs)
        else:
            fa = func(a) < 0
            fb = func(b) < 0
        if fa and not fb:
            ascending = True
        elif fb and not fa:
            ascending =  False
        else:
            if disp:
                print('Warning: func(a) and func(b) do not have opposing signs -> no search done')
            if outside == 'raise':
                raise BisectException()
            search = False

    # refine interval until it has reached size xtol, except if root outside
    while (b-a > xtol) and search:
        mid = (a+b)/2.0
        if ascending:
            if ((not errorcontrol) and (func(mid) < 0)) or \
                    (errorcontrol and func.test0(mid, **testkwargs)):
                a = mid 
            else:
                b = mid
        else:
            if ((not errorcontrol) and (func(mid) < 0)) or \
                    (errorcontrol and func.test0(mid, **testkwargs)):
                b = mid 
            else:
                a = mid
        if disp:
            print('bisect bounds', a, b)
    # interpolate linearly to get zero
    if errorcontrol:
        ya, yb = func(a)[0], func(b)[0]
    else:
        ya, yb = func(a), func(b)
    m = (yb-ya) / (b-a)
    res = a-ya/m
    if disp:
        print('bisect final value', res)
    return res
def bisect ( func , a , b , xtol = 1e-6 , errorcontrol = True , testkwargs = dict ( ) , outside = 'extrapolate' , ascending = None , disp = False ) : search = True if ascending is None : if errorcontrol : testkwargs . update ( dict ( type_ = 'smaller' , force = True ) ) fa = func . test0 ( a , ** testkwargs ) fb = func . test0 ( b , ** testkwargs ) else : fa = func ( a ) < 0 fb = func ( b ) < 0 if fa and not fb : ascending = True elif fb and not fa : ascending = False else : if disp : print ( 'Warning: func(a) and func(b) do not have opposing signs -> no search done' ) if outside == 'raise' : raise BisectException ( ) search = False while ( b - a > xtol ) and search : mid = ( a + b ) / 2.0 if ascending : if ( ( not errorcontrol ) and ( func ( mid ) < 0 ) ) or ( errorcontrol and func . test0 ( mid , ** testkwargs ) ) : a = mid else : b = mid else : if ( ( not errorcontrol ) and ( func ( mid ) < 0 ) ) or ( errorcontrol and func . test0 ( mid , ** testkwargs ) ) : b = mid else : a = mid if disp : print ( 'bisect bounds' , a , b ) if errorcontrol : ya , yb = func ( a ) [ 0 ] , func ( b ) [ 0 ] else : ya , yb = func ( a ) , func ( b ) m = ( yb - ya ) / ( b - a ) res = a - ya / m if disp : print ( 'bisect final value' , res ) return res
Find root by bysection search.

    If the function evaluation is noisy then use `errorcontrol=True` for adaptive
    sampling of the function during the bisection search.

    Parameters
    ----------
    func: callable
        Function of which the root should be found. If `errorcontrol=True`
        then the function should be derived from `AverageBase`.
    a, b: float
        initial interval
    xtol: float
        target tolerance for interval size
    errorcontrol: boolean
        if true, assume that function is derived from `AverageBase`.
    testkwargs: only for `errorcontrol=True`
        see `AverageBase.test0`
    outside: ['extrapolate', 'raise']
        How to handle the case where f(a) and f(b) have same sign,
        i.e. where the root lies outside of the interval.
        If 'raise' throws a `BisectException`.
    ascending: allow passing in directly whether function is ascending or not
        if ascending=True then it is assumed without check that f(a) < 0 and f(b) > 0
        if ascending=False then it is assumed without check that f(a) > 0 and f(b) < 0

    Returns
    -------
    float, root of function