Objective function "limit reached" status in JuMP


Can JuMP report that an objective function has reached some predefined limit?
My problem looks the following. I am looking for a Min or Max value of a variable x[1] within the bounds (lets say [1e-9,1e9]) w.r.t some non-linear constraint f(x)<=alpha:

m=Model(optimizer_with_attributes(Ipopt.Optimizer, "max_cpu_time" => 60.0))
@variable(m, x[j=1:n], start=init_x[j])
JuMP.register(m, :f, n, f, autodiff=true)
@objective(m, Min, x[1])
@NLconstraint(m, f(x...) <= alpha)

To set the bounds I either need to define variables limits:

@variable(m, 1e-9<=x[j=1:n]<=1e9, start=init_x[j])

Or to add constraints:

@constraint(m, con1, 1e-9 <= x[1] <= 1e9)

However reaching the bound (1e-9 or 1e9) may result in the following JuMP.termination_status (I am testing Ipopt and KNITRO) : LOCALLY_SOLVED, ALMOST_OPTIMAL, LOCALLY_INFEASIBLE. So it is hard to say whether an optimizer has reached the bound or a “real” minimum (maximum) was found near the bound.
Is there a way to rewrite this problem that JuMP can distinguish between those two cases? Previously I used NLopt which has some STOPVAL_REACHED option which terminates the optimization when the solver reaches some predefined value.

If Ipopt reaches the time limit, you should get termination_status(m) == MOI.TIME_LIMIT.

We can’t treat your bounds as special, because we don’t know that you intend them to be special. You would need to check the variable values of the solution and determine if they are “too close” for you.

Thanks for your response! I see that Ipopt tries to converge to bound values and report them as local optima. Indeed in this case I would prefer the solver to report that no optima has been found within the bounds. Like NLopts stopval behavior ( from NLopt docs "Another termination test that NLopt supports is that you can tell the optimization to stop when the objective function value f(x) reaches some specified value, stopval , for any feasible point x"). Is it doable with other NL optimizers in JuMP or perhaps with some reformulation of the original problem?

I don’t think Ipopt has an option to terminate based on the objective value. Here are the list of options: https://coin-or.github.io/Ipopt/OPTIONS.html#OPTIONS_REF

Presumably you could add a constraint @constraint(m, x[1] >= stopval) instead.

Indeed in this case I would prefer the solver to report that no optima has been found within the bounds.

We can’t do this for the reasons mentioned above; a locally optimal solution might exist at the bound.

Thanks, I see the reason why bounds are not treated as special points. Adding stopval as constraints @constraint(m, 1e-9 <= x[1] <= 1e9) can be a solution. I will need to set up constraint tolerance than to distinguish an optimum on the bound from “internal” optimum

@odow It seems a rule to state that Ipopt has found an optimum on the bound (constraint) can be isapprox(objective_value(m), stopval, atol=tol) where tol is Ipopt option Ipopt.Optimizer(tol=1e-8) and stopval is the bound. Ipopt also has options to set up constraint tolerances (constr_viol_tol, acceptable_constr_viol_tol). Can they also help better determine a “bound optimum” here?
For KNITRO it seems the setting I was looking for is fstopval