Repository: scipy/scipy
File: scipy/optimize/_trustregion.py (Python)

Code before:
    warnflag = 0

    # initialize the search
    trust_radius = initial_trust_radius
    x = x0
    if return_all:
        allvecs = [x]

    subproblem_init_kw = {}
    if hasattr(subproblem, 'MAXITER_DEFAULT'):
        subproblem_init_kw['maxiter'] = subproblem_maxiter

    m = subproblem(x, fun, jac, hess, hessp, **subproblem_init_kw)
    k = 0

    # search for the function min
    # do not even start if the gradient is small enough
    while m.jac_mag >= gtol:

        # Solve the sub-problem.
        # This gives us the proposed step relative to the current position
        # and it tells us whether the proposed step
        # has reached the trust region boundary or not.
        try:
            p, hits_boundary = m.solve(trust_radius)
        except np.linalg.LinAlgError:
            warnflag = 3
            break

        # calculate the predicted value at the proposed point
        predicted_value = m(p)

        # define the local approximation at the proposed point
        x_proposed = x + p
        m_proposed = subproblem(x_proposed, fun, jac, hess, hessp, **subproblem_init_kw)

        # evaluate the ratio defined in equation (4.4)
        actual_reduction = m.fun - m_proposed.fun
        predicted_reduction = m.fun - predicted_value
        if predicted_reduction <= 0:
            warnflag = 2
            break
        rho = actual_reduction / predicted_reduction

        # update the trust radius according to the actual/predicted ratio
        if rho < 0.25:
            trust_radius *= 0.25
        elif rho > 0.75 and hits_boundary:
            trust_radius = min(2*trust_radius, max_trust_radius)

        # if the ratio is high enough then accept the proposed step

Review comment (suggestion):
This was the least intrusive way I could think of to extend the QuadraticSubproblem class. Either the extra option would go into the `__init__` or the `solve` method. Here I chose to pass it in `__init__`. Another option would be to have a generic `subproblem_options` dictionary passed to all constructors (and warn when unused options are passed?), but the call to minimize would then look like: `minimize(fun, x0, jac, options={"gtol": 1e-6, "subproblem_options": {'maxiter': 10}}`. But perhaps that's preferable. Let me know what you think.

Code after:
        hess = None

        def hessp(x, p, *args):
            return sf.hess(x).dot(p)
    else:
        raise ValueError('Either the Hessian or the Hessian-vector product '
                         'is currently required for trust-region methods')

    # ScalarFunction doesn't represent hessp
    nhessp, hessp = _wrap_function(hessp, args)

    # limit the number of iterations
    if maxiter is None:
        maxiter = len(x0)*200

    # init the search status
    warnflag = 0

    # initialize the search
    trust_radius = initial_trust_radius
    x = x0
    if return_all:
        allvecs = [x]

    subproblem_init_kw = {}
    if hasattr(subproblem, 'MAXITER_DEFAULT'):
        subproblem_init_kw['maxiter'] = subproblem_maxiter

    m = subproblem(x, fun, jac, hess, hessp, **subproblem_init_kw)
    k = 0

    # search for the function min
    # do not even start if the gradient is small enough
    while m.jac_mag >= gtol:

        # Solve the sub-problem.
        # This gives us the proposed step relative to the current position
        # and it tells us whether the proposed step
        # has reached the trust region boundary or not.
        try:
            p, hits_boundary = m.solve(trust_radius)
        except np.linalg.LinAlgError:
            warnflag = 3
            break

        # calculate the predicted value at the proposed point
        predicted_value = m(p)

        # define the local approximation at the proposed point
        x_proposed = x + p
        m_proposed = subproblem(x_proposed, fun, jac, hess, hessp, **subproblem_init_kw)