lanl-ansi / Juniper.jl

A JuMP-based Nonlinear Integer Program Solver
https://lanl-ansi.github.io/Juniper.jl/stable/
MIT License
179 stars 22 forks source link

Incorrect solution with conic problem #215

Closed blegat closed 2 years ago

blegat commented 3 years ago

With the following:

using JuMP

import Juniper
using MosekTools: Mosek

solver = optimizer_with_attributes(
    Juniper.Optimizer,
    "nl_solver" => Mosek.Optimizer,
)

model = Model(solver)
@variable(model, x == 1, Bin)
@constraint(model, [x, 1] ∈ SecondOrderCone())
@objective(model, Min, 2x)
optimize!(model)
@show termination_status(model)
@show primal_status(model)
@show value(x)

I get

nl_solver         : Optimizer
feasibility_pump  : false
log_levels        : [:Options, :Table, :Info]

#Variables: 1
#IntBinVar: 1
Obj Sense: Min

Problem
  Name                   :                 
  Objective sense        : min             
  Type                   : CONIC (conic optimization problem)
  Constraints            : 2               
  Cones                  : 1               
  Scalar variables       : 3               
  Matrix variables       : 0               
  Integer variables      : 0               

Optimizer started.
Presolve started.
Eliminator started.
Freed constraints in eliminator : 0
Eliminator terminated.
Eliminator started.
Freed constraints in eliminator : 0
Eliminator terminated.
Eliminator - tries                  : 2                 time                   : 0.00            
Lin. dep.  - tries                  : 0                 time                   : 0.00            
Lin. dep.  - number                 : 0               
Presolve terminated. Time: 0.00    
Optimizer terminated. Time: 0.00    

Incumbent using start values: 0.0
Problem
  Name                   :                 
  Objective sense        : min             
  Type                   : CONIC (conic optimization problem)
  Constraints            : 2               
  Cones                  : 1               
  Scalar variables       : 3               
  Matrix variables       : 0               
  Integer variables      : 0               

Optimizer started.
Optimizer terminated. Time: 0.00    

Status of relaxation: OPTIMAL
Time for relaxation: 0.008291006088256836
Relaxation Obj: 0.0
Obj: 0.0
termination_status(model) = MathOptInterface.LOCALLY_SOLVED
primal_status(model) = MathOptInterface.FEASIBLE_POINT
value(x) = 0.0

So Juniper reports that it found a feasible solution but this solution violates both the x == 1 constraint and the SOC constraint.

Wikunia commented 3 years ago

Unfortunately I don't have access to Mosek. I assume that it works when removing

@constraint(model, [x, 1] ∈ SecondOrderCone())

?

blegat commented 3 years ago

The issue is that when you give starting values, Mosek gives 3 solutions:

3-element Vector{MosekTools.MosekSolution}:
 MosekTools.MosekSolution(Mosek.MSK_SOL_ITG, Mosek.MSK_SOL_STA_UNKNOWN, Mosek.MSK_PRO_STA_UNKNOWN, Mosek.Stakey[Mosek.MSK_SK_UNK, Mosek.MSK_SK_FIX, Mosek.MSK_SK_FIX], [??????, 0.0, 0.0], Vector{Float64}[], Float64[], Float64[], Float64[], Mosek.Stakey[Mosek.MSK_SK_UNK, Mosek.MSK_SK_UNK], [0.0, 0.0], Float64[], Float64[], Float64[])
 MosekTools.MosekSolution(Mosek.MSK_SOL_BAS, Mosek.MSK_SOL_STA_UNKNOWN, Mosek.MSK_PRO_STA_UNKNOWN, Mosek.Stakey[Mosek.MSK_SK_UNK, Mosek.MSK_SK_FIX, Mosek.MSK_SK_FIX], [1.0, 0.0, 0.0], Vector{Float64}[], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], Float64[], Mosek.Stakey[Mosek.MSK_SK_BAS, Mosek.MSK_SK_BAS], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [0.0, 0.0])
 MosekTools.MosekSolution(Mosek.MSK_SOL_ITR, Mosek.MSK_SOL_STA_OPTIMAL, Mosek.MSK_PRO_STA_PRIM_AND_DUAL_FEAS, Mosek.Stakey[Mosek.MSK_SK_SUPBAS, Mosek.MSK_SK_SUPBAS, Mosek.MSK_SK_SUPBAS], [1.0, 1.0, 1.0], Vector{Float64}[], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0], [0.0, 2.0, -2.0], Mosek.Stakey[Mosek.MSK_SK_FIX, Mosek.MSK_SK_FIX], [-0.0, -1.0], [2.0, 0.0], [0.0, 2.0], [2.0, -2.0])

where ????? is the starting value you gave. So here Juniper should figure out that it should take VariablePrimal(3) by looking at PrimalStatus(1), PrimalStatus(2), PrimalStatus(3), ...

blegat commented 3 years ago

Mosek could maybe reorder its solutions to give the feasible ones first but this is also a bug in Juniper. It shouldn't assume that the feasible solution is always the first one when the status is OPTIMAL.

Wikunia commented 3 years ago

Thanks for the clarification. Why does Mosek give infeasible "solutions"? But okay so I basically check how many solutions there are and then check their primal status to pick the first optimal one. I'll try to work this out this week and might ask you regarding testing as I can only check if it works with Ipopt.

blegat commented 3 years ago

You might want to add tests with MockOptimizer to check if Juniper is robust against these. Or even better, create an MOI layer DummySolution that move the ith solution of the inner optimizer to the i+1th and create a dummy infeasible first solution. Then you can check that Juniper still works with DummySolution(Ipopt.Optimizer()) instead of Ipopt.Optimizer()

blegat commented 3 years ago

In fact you might not need to change anything in Juniper, I think you can assume that the first solution is the optimal one, see https://github.com/jump-dev/MathOptInterface.jl/issues/1372

Wikunia commented 3 years ago

Thanks I'll follow the discussion there

odow commented 2 years ago

Closing because this was a bug in Mosek.