coin-or/Ipopt

Unsure if bound constraints serve the purpose as inequality constraints for my use case?

a-jp opened this issue · 4 comments

a-jp commented

Hi,
In my use case all my variables are non-negative reals. My use case makes no sense if the variables are ever negative, not even during the iterations. Due to std::log the code will fail if this is ever the case, in addition to it being non-physical.

When constructing the problem I place a lower bound of zero (specifically 0.0) and an upper bound that is outside of the range that is possible but still not infinite (it's normally set to 1.0 or 10.0 when setting the variable bounds and is much higher than the value any variable will get to). I have seen my problem defined also in the literature that the variables must satisfy >= 0 inequality constraints. As such I use both in my code, bounds and the inequality constraints.

Is this over-specified/over-constrained? Should I just rely on the bounds on the variables to ensure I have non-negative reals? Or is it correct to have the inequality constraints also in addition to the bounds?

Could I just confirm the value of the upper bound I should set to provide the above inequality if it's correct to use it, should it be +inf, std::numeric_limits::max(), or something specifically in the IPOPT namespace?

Related to +inf on the upper bound for inequality constraints, if I know due to the bound constraints used above that my variables never get to a value of 1.0 or 10.0, why would it not be a good idea just to use equality constraints rather than inequality constraints?

Seems to me there is three ways to do this and I'm confused.

My question boils down to whether the bounds are guaranteed to ensure the variables remain non-negative. My settings set honor_original_bounds=yes and bound_relax_factor=0.0. These were both used in an attempt to ensure the variables never go negative.

Any help really appreciated. My code runs, I just want to make sure I'm setting it up properly.
Cheers,

Setting bound_relax_factor to 0 should ensure that Ipopt obeys your variable bounds (there is a rare exception regarding slacks becoming too small that was discussed somewhere here before). Adding variable bounds also as constraints is usually not beneficial.

If you have log(), then you should set the lower bound for the argument to a small positive value, to avoid log(0).
If you don't need an upper bound on a variable, then omit it by setting it to a value equal or larger than nlp_upper_bound_inf.

I don't understand the part about "use equality constraints rather than inequality constraints".

a-jp commented

Thank you for this. I will try this, I know my code nans without the constraints and I suspect this may be the 0.0 lower bound, I will try with std::numeric_limits::min(). Ignore the equality constraint question I was in error there.

Is there any notion of it being easier on IpOPT to solve with lower bounds only rather than lower and upper? I can set the upper to be nlp_upper_bound_inf just wondering if that has an additional benefit to the solver? Same question for inequality constraints if in reality there is only a lower bound, is that beneficial to the solver too just to set one?

All my equality constraints are of the form of A.x=b, were both A and b are known ahead of the calculation and are constants. b are the constraints I set, and x are the ipopt variables. Can I benefit from this? If so, what and how do I tell IpOPT this is the case? Will it make a big difference?

An upper bound that isn't attained shouldn't be useful. The same for constraint sides. They just add barrier terms that aren't helpful.

You can tell Ipopt that all your equality constraints are linear via option jac_c_constant. It's not going to make much of a difference. It may just avoid Jacobian evaluations in some less likely case, I think.

a-jp commented

Ok thanks, I will give that a go.

I can confirm that my code runs as expect with all inequality constraints removed that were enforcing the bounds on the variables. Moreover, the upper variable bound is now std::numeric_limits::max(). It was essential to making the code work to have std::numeric_limits::min() as the lower variable bound. Using 0.0 caused code failures and for some reason the constraint prevented the code failure. I believe the code is now more correct (although I would note the answers are not meaningfully different).