# Buggy code
def _minimize_newtoncg(
    fun,
    x0,
    args=(),
    jac=None,
    hess=None,
    hessp=None,
    callback=None,
    xtol=1e-5,
    eps=_epsilon,
    maxiter=None,
    disp=False,
    return_all=False,
    **unknown_options,
):
    """
    Minimization of scalar function of one or more variables using the
    Newton-CG algorithm.

    Note that the `jac` parameter (Jacobian) is required.

    Options
    -------
    disp : bool
        Set to True to print convergence messages.
    xtol : float
        Average relative error in solution `xopt` acceptable for
        convergence.
    maxiter : int
        Maximum number of iterations to perform.
    eps : float or ndarray
        If `jac` is approximated, use this value for the step size.

    """
    _check_unknown_options(unknown_options)
    if jac is None:
        raise ValueError("Jacobian is required for Newton-CG method")
    f = fun
    fprime = jac
    fhess_p = hessp
    fhess = hess
    avextol = xtol
    epsilon = eps
    retall = return_all

    def terminate(warnflag, msg):
        if disp:
            print(msg)
            print("         Current function value: %f" % old_fval)
            print("         Iterations: %d" % k)
            print("         Function evaluations: %d" % fcalls[0])
            print("         Gradient evaluations: %d" % gcalls[0])
            print("         Hessian evaluations: %d" % hcalls)
        fval = old_fval
        result = OptimizeResult(
            fun=fval,
            jac=gfk,
            nfev=fcalls[0],
            njev=gcalls[0],
            nhev=hcalls,
            status=warnflag,
            success=(warnflag == 0),
            message=msg,
            x=xk,
            nit=k,
        )
        if retall:
            result["allvecs"] = allvecs
        return result

    x0 = asarray(x0).flatten()
    fcalls, f = wrap_function(f, args)
    gcalls, fprime = wrap_function(fprime, args)
    hcalls = 0
    if maxiter is None:
        maxiter = len(x0) * 200
    cg_maxiter = 20 * len(x0)

    xtol = len(x0) * avextol
    update = [2 * xtol]
    xk = x0
    if retall:
        allvecs = [xk]
    k = 0
    gfk = None
    old_fval = f(x0)
    old_old_fval = None
    float64eps = numpy.finfo(numpy.float64).eps
    while numpy.add.reduce(numpy.abs(update)) > xtol:
        if k >= maxiter:
            msg = "Warning: " + _status_message["maxiter"]
            return terminate(1, msg)
        # Compute a search direction pk by applying the CG method to
        #  del2 f(xk) p = - grad f(xk) starting from 0.
        b = -fprime(xk)
        maggrad = numpy.add.reduce(numpy.abs(b))
        eta = numpy.min([0.5, numpy.sqrt(maggrad)])
        termcond = eta * maggrad
        xsupi = zeros(len(x0), dtype=x0.dtype)
        ri = -b
        psupi = -ri
        i = 0
        dri0 = numpy.dot(ri, ri)

        if fhess is not None:  # you want to compute Hessian once.
            A = fhess(*(xk,) + args)
            hcalls = hcalls + 1

        for k2 in xrange(cg_maxiter):
            if numpy.add.reduce(numpy.abs(ri)) <= termcond:
                break
            if fhess is None:
                if fhess_p is None:
                    Ap = approx_fhess_p(xk, psupi, fprime, epsilon)
                else:
                    Ap = fhess_p(xk, psupi, *args)
                    hcalls = hcalls + 1
            else:
                Ap = numpy.dot(A, psupi)
            # check curvature
            Ap = asarray(Ap).squeeze()  # get rid of matrices...
            curv = numpy.dot(psupi, Ap)
            if 0 <= curv <= 3 * float64eps:
                break
            elif curv < 0:
                if i > 0:
                    break
                else:
                    # fall back to steepest descent direction
                    xsupi = dri0 / (-curv) * b
                    break
            alphai = dri0 / curv
            xsupi = xsupi + alphai * psupi
            ri = ri + alphai * Ap
            dri1 = numpy.dot(ri, ri)
            betai = dri1 / dri0
            psupi = -ri + betai * psupi
            i = i + 1
            dri0 = dri1  # update numpy.dot(ri,ri) for next time.
        else:
            # curvature keeps increasing, bail out
            msg = (
                "Warning: CG iterations didn't converge. The Hessian is not "
                "positive definite."
            )
            return terminate(3, msg)

        pk = xsupi  # search direction is solution to system.
        gfk = -b  # gradient at xk

        try:
            alphak, fc, gc, old_fval, old_old_fval, gfkp1 = _line_search_wolfe12(
                f, fprime, xk, pk, gfk, old_fval, old_old_fval
            )
        except _LineSearchError:
            # Line search failed to find a better solution.
            msg = "Warning: " + _status_message["pr_loss"]
            return terminate(2, msg)

        update = alphak * pk
        xk = xk + update  # upcast if necessary
        if callback is not None:
            callback(xk)
        if retall:
            allvecs.append(xk)
        k += 1
    else:
        if np.isnan(old_fval) or np.isnan(update).any():
            return terminate(3, _status_message["nan"])

        msg = _status_message["success"]
        return terminate(0, msg)


# Traceback (ValueError)
In [158]: minimize(func, [1.1, 1.2], method='Newton-CG', jac=False)
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
<ipython-input-158-94c7580e9508> in <module>()
----> 1 minimize(func, [1.1, 1.2], method='Newton-CG', jac=False)

~/miniconda3scipy/lib/python3.5/site-packages/scipy-1.2.0.dev0+6d455f3-py3.5-macosx-10.6-x86_64.egg/scipy/optimize/_minimize.py in minimize(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)
600     elif meth == 'newton-cg':
601         return _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback,
--> 602                                   **options)
603     elif meth == 'l-bfgs-b':
604         return _minimize_lbfgsb(fun, x0, args, jac, bounds,

~/miniconda3scipy/lib/python3.5/site-packages/scipy-1.2.0.dev0+6d455f3-py3.5-macosx-10.6-x86_64.egg/scipy/optimize/optimize.py in _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback, xtol, eps, maxiter, disp, return_all, **unknown_options)
1494     _check_unknown_options(unknown_options)
1495     if jac is None:
-> 1496         raise ValueError('Jacobian is required for Newton-CG method')
1497     f = fun
1498     fprime = jac

ValueError: Jacobian is required for Newton-CG method

# Fixed code
def _minimize_newtoncg(
    fun,
    x0,
    args=(),
    jac=None,
    hess=None,
    hessp=None,
    callback=None,
    xtol=1e-5,
    eps=_epsilon,
    maxiter=None,
    disp=False,
    return_all=False,
    **unknown_options,
):
    """
    Minimization of scalar function of one or more variables using the
    Newton-CG algorithm.

    Note that the `jac` parameter (Jacobian) is required.

    Options
    -------
    disp : bool
        Set to True to print convergence messages.
    xtol : float
        Average relative error in solution `xopt` acceptable for
        convergence.
    maxiter : int
        Maximum number of iterations to perform.
    eps : float or ndarray
        If `hessp` is approximated, use this value for the step size.
    """
    _check_unknown_options(unknown_options)
    if jac is None:
        raise ValueError("Jacobian is required for Newton-CG method")
    fhess_p = hessp
    fhess = hess
    avextol = xtol
    epsilon = eps
    retall = return_all

    x0 = asarray(x0).flatten()
    # TODO: allow hess to be approximated by FD?
    # TODO: add hessp (callable or FD) to ScalarFunction?
    sf = _prepare_scalar_function(fun, x0, jac, args=args, epsilon=eps, hess=fhess)
    f = sf.fun
    fprime = sf.grad

    def terminate(warnflag, msg):
        if disp:
            print(msg)
            print("         Current function value: %f" % old_fval)
            print("         Iterations: %d" % k)
            print("         Function evaluations: %d" % sf.nfev)
            print("         Gradient evaluations: %d" % sf.ngev)
            print("         Hessian evaluations: %d" % hcalls)
        fval = old_fval
        result = OptimizeResult(
            fun=fval,
            jac=gfk,
            nfev=sf.nfev,
            njev=sf.ngev,
            nhev=hcalls,
            status=warnflag,
            success=(warnflag == 0),
            message=msg,
            x=xk,
            nit=k,
        )
        if retall:
            result["allvecs"] = allvecs
        return result

    hcalls = 0
    if maxiter is None:
        maxiter = len(x0) * 200
    cg_maxiter = 20 * len(x0)

    xtol = len(x0) * avextol
    update = [2 * xtol]
    xk = x0
    if retall:
        allvecs = [xk]
    k = 0
    gfk = None
    old_fval = f(x0)
    old_old_fval = None
    float64eps = numpy.finfo(numpy.float64).eps
    while numpy.add.reduce(numpy.abs(update)) > xtol:
        if k >= maxiter:
            msg = "Warning: " + _status_message["maxiter"]
            return terminate(1, msg)
        # Compute a search direction pk by applying the CG method to
        #  del2 f(xk) p = - grad f(xk) starting from 0.
        b = -fprime(xk)
        maggrad = numpy.add.reduce(numpy.abs(b))
        eta = numpy.min([0.5, numpy.sqrt(maggrad)])
        termcond = eta * maggrad
        xsupi = zeros(len(x0), dtype=x0.dtype)
        ri = -b
        psupi = -ri
        i = 0
        dri0 = numpy.dot(ri, ri)

        if fhess is not None:  # you want to compute hessian once.
            A = sf.hess(xk)
            hcalls = hcalls + 1

        for k2 in xrange(cg_maxiter):
            if numpy.add.reduce(numpy.abs(ri)) <= termcond:
                break
            if fhess is None:
                if fhess_p is None:
                    Ap = approx_fhess_p(xk, psupi, fprime, epsilon)
                else:
                    Ap = fhess_p(xk, psupi, *args)
                    hcalls = hcalls + 1
            else:
                Ap = numpy.dot(A, psupi)
            # check curvature
            Ap = asarray(Ap).squeeze()  # get rid of matrices...
            curv = numpy.dot(psupi, Ap)
            if 0 <= curv <= 3 * float64eps:
                break
            elif curv < 0:
                if i > 0:
                    break
                else:
                    # fall back to steepest descent direction
                    xsupi = dri0 / (-curv) * b
                    break
            alphai = dri0 / curv
            xsupi = xsupi + alphai * psupi
            ri = ri + alphai * Ap
            dri1 = numpy.dot(ri, ri)
            betai = dri1 / dri0
            psupi = -ri + betai * psupi
            i = i + 1
            dri0 = dri1  # update numpy.dot(ri,ri) for next time.
        else:
            # curvature keeps increasing, bail out
            msg = (
                "Warning: CG iterations didn't converge. The Hessian is not "
                "positive definite."
            )
            return terminate(3, msg)

        pk = xsupi  # search direction is solution to system.
        gfk = -b  # gradient at xk

        try:
            alphak, fc, gc, old_fval, old_old_fval, gfkp1 = _line_search_wolfe12(
                f, fprime, xk, pk, gfk, old_fval, old_old_fval
            )
        except _LineSearchError:
            # Line search failed to find a better solution.
            msg = "Warning: " + _status_message["pr_loss"]
            return terminate(2, msg)

        update = alphak * pk
        xk = xk + update  # upcast if necessary
        if callback is not None:
            callback(xk)
        if retall:
            allvecs.append(xk)
        k += 1
    else:
        if np.isnan(old_fval) or np.isnan(update).any():
            return terminate(3, _status_message["nan"])

        msg = _status_message["success"]
        return terminate(0, msg)