def gradients(vals, func, releps=0.001, abseps=None, mineps=1e-09, reltol=1, epsscale=0.5):
    '\n    Calculate the partial derivatives of a function at a set of values. The\n    derivatives are calculated using the central difference, using an iterative\n    method to check that the values converge as step size decreases.\n\n    Parameters\n    ----------\n    vals: array_like\n        A set of values, that are passed to a function, at which to calculate\n        the gradient of that function\n    func:\n        A function that takes in an array of values.\n    releps: float, array_like, 1e-3\n        The initial relative step size for calculating the derivative.\n    abseps: float, array_like, None\n        The initial absolute step size for calculating the derivative.\n        This overrides `releps` if set.\n        `releps` is set then that is used.\n    mineps: float, 1e-9\n        The minimum relative step size at which to stop iterations if no\n        convergence is achieved.\n    epsscale: float, 0.5\n        The factor by which releps if scaled in each iteration.\n\n    Returns\n    -------\n    grads: array_like\n        An array of gradients for each non-fixed value.\n    '
    grads = np.zeros(len(vals))
    flipflopmax = 10.0
    if (abseps is None):
        if isinstance(releps, float):
            eps = (np.abs(vals) * releps)
            eps[(eps == 0.0)] = releps
            teps = (releps * np.ones(len(vals)))
        elif isinstance(releps, (list, np.ndarray)):
            if (len(releps) != len(vals)):
                raise ValueError('Problem with input relative step sizes')
            eps = np.multiply(np.abs(vals), releps)
            eps[(eps == 0.0)] = np.array(releps)[(eps == 0.0)]
            teps = releps
        else:
            raise RuntimeError('Relative step sizes are not a recognised type!')
    else:
        if isinstance(abseps, float):
            eps = (abseps * np.ones(len(vals)))
        elif isinstance(abseps, (list, np.ndarray)):
            if (len(abseps) != len(vals)):
                raise ValueError('Problem with input absolute step sizes')
            eps = np.array(abseps)
        else:
            raise RuntimeError('Absolute step sizes are not a recognised type!')
        teps = eps
    count = 0
    for i in range(len(vals)):
        leps = eps[i]
        cureps = teps[i]
        flipflop = 0
        fvals = np.copy(vals)
        bvals = np.copy(vals)
        fvals[i] += (0.5 * leps)
        bvals[i] -= (0.5 * leps)
        cdiff = ((func(fvals) - func(bvals)) / leps)
        while 1:
            fvals[i] -= (0.5 * leps)
            bvals[i] += (0.5 * leps)
            cureps *= epsscale
            if ((cureps < mineps) or (flipflop > flipflopmax)):
                logger.warn('Derivative calculation did not converge: setting flat derivative.')
                grads[count] = 0.0
                break
            leps *= epsscale
            fvals[i] += (0.5 * leps)
            bvals[i] -= (0.5 * leps)
            cdiffnew = ((func(fvals) - func(bvals)) / leps)
            if (cdiffnew == cdiff):
                grads[count] = cdiff
                break
            rat = (cdiff / cdiffnew)
            if (np.isfinite(rat) and (rat > 0.0)):
                if (np.abs((1.0 - rat)) < reltol):
                    grads[count] = cdiffnew
                    break
                else:
                    cdiff = cdiffnew
                    continue
            else:
                cdiff = cdiffnew
                flipflop += 1
                continue
        count += 1
    return grads
Calculate the partial derivatives of a function at a set of values. The
derivatives are calculated using the central difference, using an iterative
method to check that the values converge as step size decreases.

Parameters
----------
vals: array_like
    A set of values, that are passed to a function, at which to calculate
    the gradient of that function
func:
    A function that takes in an array of values.
releps: float, array_like, 1e-3
    The initial relative step size for calculating the derivative.
abseps: float, array_like, None
    The initial absolute step size for calculating the derivative.
    This overrides `releps` if set.
    `releps` is set then that is used.
mineps: float, 1e-9
    The minimum relative step size at which to stop iterations if no
    convergence is achieved.
epsscale: float, 0.5
    The factor by which releps if scaled in each iteration.

Returns
-------
grads: array_like
    An array of gradients for each non-fixed value.
def gradients(vals, func, releps=0.001, abseps=None, mineps=1e-09, reltol=1, epsscale=0.5):
    '\n    Calculate the partial derivatives of a function at a set of values. The\n    derivatives are calculated using the central difference, using an iterative\n    method to check that the values converge as step size decreases.\n\n    Parameters\n    ----------\n    vals: array_like\n        A set of values, that are passed to a function, at which to calculate\n        the gradient of that function\n    func:\n        A function that takes in an array of values.\n    releps: float, array_like, 1e-3\n        The initial relative step size for calculating the derivative.\n    abseps: float, array_like, None\n        The initial absolute step size for calculating the derivative.\n        This overrides `releps` if set.\n        `releps` is set then that is used.\n    mineps: float, 1e-9\n        The minimum relative step size at which to stop iterations if no\n        convergence is achieved.\n    epsscale: float, 0.5\n        The factor by which releps if scaled in each iteration.\n\n    Returns\n    -------\n    grads: array_like\n        An array of gradients for each non-fixed value.\n    '
    grads = np.zeros(len(vals))
    flipflopmax = 10.0
    if (abseps is None):
        if isinstance(releps, float):
            eps = (np.abs(vals) * releps)
            eps[(eps == 0.0)] = releps
            teps = (releps * np.ones(len(vals)))
        elif isinstance(releps, (list, np.ndarray)):
            if (len(releps) != len(vals)):
                raise ValueError('Problem with input relative step sizes')
            eps = np.multiply(np.abs(vals), releps)
            eps[(eps == 0.0)] = np.array(releps)[(eps == 0.0)]
            teps = releps
        else:
            raise RuntimeError('Relative step sizes are not a recognised type!')
    else:
        if isinstance(abseps, float):
            eps = (abseps * np.ones(len(vals)))
        elif isinstance(abseps, (list, np.ndarray)):
            if (len(abseps) != len(vals)):
                raise ValueError('Problem with input absolute step sizes')
            eps = np.array(abseps)
        else:
            raise RuntimeError('Absolute step sizes are not a recognised type!')
        teps = eps
    count = 0
    for i in range(len(vals)):
        leps = eps[i]
        cureps = teps[i]
        flipflop = 0
        fvals = np.copy(vals)
        bvals = np.copy(vals)
        fvals[i] += (0.5 * leps)
        bvals[i] -= (0.5 * leps)
        cdiff = ((func(fvals) - func(bvals)) / leps)
        while 1:
            fvals[i] -= (0.5 * leps)
            bvals[i] += (0.5 * leps)
            cureps *= epsscale
            if ((cureps < mineps) or (flipflop > flipflopmax)):
                logger.warn('Derivative calculation did not converge: setting flat derivative.')
                grads[count] = 0.0
                break
            leps *= epsscale
            fvals[i] += (0.5 * leps)
            bvals[i] -= (0.5 * leps)
            cdiffnew = ((func(fvals) - func(bvals)) / leps)
            if (cdiffnew == cdiff):
                grads[count] = cdiff
                break
            rat = (cdiff / cdiffnew)
            if (np.isfinite(rat) and (rat > 0.0)):
                if (np.abs((1.0 - rat)) < reltol):
                    grads[count] = cdiffnew
                    break
                else:
                    cdiff = cdiffnew
                    continue
            else:
                cdiff = cdiffnew
                flipflop += 1
                continue
        count += 1
    return grads