lanl-ansi / Alpine.jl

A Julia/JuMP-based Global Optimization Solver for Non-convex Programs
https://lanl-ansi.github.io/Alpine.jl/latest/
Other
241 stars 39 forks source link

`Alpine` have an issue with `@NLconstraint` created from a quadratic `@expression` #221

Closed felipemarkson closed 1 year ago

felipemarkson commented 1 year ago

NOTE: This issue was created after the changes indicated on #220.

When you try to create a@NLconstraint quadratic constraint from a @expression, Alpine throws an error.

The issue is not found if using only Pavito to solve the problem, so I think the problem is not the MIP solver.

The issue is not found if the @expression is linear.

using JuMP
using Ipopt, Alpine, HiGHS, Pavito

# MILP optimizer
highs = optimizer_with_attributes(HiGHS.Optimizer,
    MOI.Silent() => true
)

# NLP optimizer
ipopt = optimizer_with_attributes(Ipopt.Optimizer,
    MOI.Silent() => true
)

# Convex MIP optimizer
pavito = optimizer_with_attributes(Pavito.Optimizer,
    "cont_solver" => ipopt,
    "mip_solver" => highs
)

# Global optimizer
alpine = optimizer_with_attributes(Alpine.Optimizer,
    "nlp_solver" => ipopt,
    "mip_solver" => pavito,
    "presolve_bt" => true,
    "apply_partitioning" => true,
)

model = Model(alpine)
@variable(model, x ≥ 0)
@expression(model, expr, x^2)
@NLconstraint(model, expr ≥ 3)
@NLobjective(model, Min, x^3)

optimize!(model)

Here you can see the Error message: image

harshangrjn commented 1 year ago

Since it is a nonlinear term and that Alpine recognizes it as an NLexpression, write the above model as follows, and it should work:

model = Model(alpine)
@variable(model, 0 <= x <=  10)
@NLexpression(model, expr, x^2)
@NLconstraint(model, expr ≥ 3)
@NLobjective(model, Min, x^3)
felipemarkson commented 1 year ago

I agree that should work! But there's situations where this is not desired, I place an example below:

model = Model(alpine)
@variable(model, 0 <= x <=  10)
@expression(model, expr, x^2)
@constraint(model, expr ≤ 4) # This is already convex, the MICP can solved it.
@NLconstraint(model, expr ≥ 3) # This is not convex, it needs convexification.
@NLobjective(model, Min, x^3)

This is important for large models, where the MICP can improve the time for solution.

harshangrjn commented 1 year ago

Good point! In the above example, since the same variable x and expression expr are shared across both convex and non-convex constraint, it wouldn't make any difference. But for cases with different variables with subset of them in convex constraints, Alpine internally detects them as convex and excludes them from partitioning to reduce the overhead. In this case, it is upon the user to use expression and NLexpression as follows:

@variable(model, 0 <= x <= 10)
@variable(model, 0 <= y <= 10)
@expression(model, expr_x, x^2)
@NLexpression(model, expr_y, y^2)
@constraint(model, expr_x <= 4) 
@NLconstraint(model, expr_y >= 3) 
@NLobjective(model, Min, x^2 + y^2)

In Alpine's output, you can observe that it detects the expr_x as convex and partitions only y variable:

PROBLEM STATISTICS
  Objective sense = Min
  # Variables = 2
  # Bin-Int Variables = 0
  # Constraints = 2
  # NL Constraints = 2
  # Linear Constraints = 0
  # Detected convex constraints = 1
  # Detected nonlinear terms = 1
  # Variables involved in nonlinear terms = 1
  # Potential variables for partitioning = 1
felipemarkson commented 1 year ago

As it is a limitation of the solver (the need to use two different expressions), this could also be in the documentation!

Thanks!

felipemarkson commented 1 year ago

@harshangrjn could you leave this issue open?

Maybe, I will try to fix it.

harshangrjn commented 1 year ago

This should be addressed in v0.5.2