Use a buffer

We already illustrated the use of Stopping for optimization algorithm, however, in the case where one algorithm/solver is not Stopping-compatible, a buffer solver is required to unify the formalism. We illustrate this situation here with the Ipopt solver.

Remark in the buffer function: in case the solver stops with success but the stopping condition is not satisfied, one option is to iterate and reduce the various tolerances.

Documentation for Ipopt options can be found here: https://coin-or.github.io/Ipopt/OPTIONS.html#OPTIONS_REF

The Julia file corresponding to this tutorial can be found here.

using Ipopt, NLPModels, NLPModelsIpopt, Stopping

include("../test-stopping/rosenbrock.jl")
x0  = 1.5 * ones(6)
nlp = ADNLPModel(rosenbrock,  x0)

The traditional way to solve an optimization problem using NLPModelsIpopt https://github.com/JuliaSmoothOptimizers/NLPModelsIpopt.jl

printstyled("Oth scenario:\n")

stats = ipopt(nlp, print_level = 0, x0 = x0)

Use y0 (general), zL (lower bound), zU (upper bound) for initial guess of Lagrange multipliers.

@show stats.solution, stats.status

Using Stopping, the idea is to create a buffer function

function solveIpopt(stp :: NLPStopping)

 #xk = solveIpopt(stop.pb, stop.current_state.x)
 stats = ipopt(nlp, print_level     = 0,
                    tol             = stp.meta.rtol,
                    x0              = stp.current_state.x,
                    max_iter        = stp.meta.max_iter,
                    max_cpu_time    = stp.meta.max_time,
                    dual_inf_tol    = stp.meta.atol,
                    constr_viol_tol = stp.meta.atol,
                    compl_inf_tol   = stp.meta.atol)

 #Update the meta boolean with the output message
 if stats.status == :first_order stp.meta.suboptimal      = true end
 if stats.status == :acceptable  stp.meta.suboptimal      = true end
 if stats.status == :infeasible  stp.meta.infeasible      = true end
 if stats.status == :small_step  stp.meta.stalled         = true end
 if stats.status == :max_iter    stp.meta.iteration_limit = true end
 if stats.status == :max_time    stp.meta.tired           = true end

 stp.meta.nb_of_stop = stats.iter
 #stats.elapsed_time

 x = stats.solution

 #Not mandatory, but in case some entries of the State are used to stop
 fill_in!(stp, x)

 stop!(stp)

 return stp
end
nlp_at_x = NLPAtX(x0)
stop = NLPStopping(nlp, unconstrained_check, nlp_at_x)

1st scenario, we solve again the problem with the buffer solver

printstyled("1st scenario:\n")
solveIpopt(stop)
@show stop.current_state.x, status(stop)
nbiter = stop.meta.nb_of_stop

2nd scenario: we check that we control the maximum iterations.

printstyled("2nd scenario:\n")
#rstate is set as true to allow reinit! modifying the State
reinit!(stop, rstate = true, x = x0)
stop.meta.max_iter = max(nbiter-4,1)

solveIpopt(stop)
#Final status is :IterationLimit
@show stop.current_state.x, status(stop)