JuMP problem get conflicting constraints when using small values

In JuMP I’m having problems getting conflicting equality constraints when using small values

using JuMP
using Gurobi

model = Model(Gurobi.Optimizer)
@variable(model, x)
a = 5e-5
b = 3e-3

@constraint(model, c1, x == 0)
@constraint(model, c2, 1 == (a * x^2 +b*x))

@objective(model, Min, x)

optimize!(model)

compute_conflict!(model)

MOI.get.(model, MOI.ConstraintConflictStatus(), c1)

MOI.get.(model, MOI.ConstraintConflictStatus(), c2)
# Without broadcasting
MOI.get.(model, MOI.ConstraintConflictStatus(), c2)

However, this gives (sorry for long traceback)

julia> MOI.get.(model, MOI.ConstraintConflictStatus(), c1)
IN_CONFLICT::ConflictParticipationStatusCode = 1

julia> MOI.get.(model, MOI.ConstraintConflictStatus(), c2)
ERROR: ArgumentError: Gurobi.Optimizer does not support getting the attribute MathOptInterface.ConstraintConflictStatus().
Stacktrace:
  [1] get_fallback(model::Gurobi.Optimizer, attr::MathOptInterface.ConstraintConflictStatus, #unused#::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}})
    @ MathOptInterface ~/.julia/packages/MathOptInterface/IIN1o/src/attributes.jl:361
  [2] get(model::Gurobi.Optimizer, attr::MathOptInterface.ConstraintConflictStatus, args::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}})
    @ MathOptInterface ~/.julia/packages/MathOptInterface/IIN1o/src/attributes.jl:330
  [3] get(b::MathOptInterface.Bridges.LazyBridgeOptimizer{Gurobi.Optimizer}, attr::MathOptInterface.ConstraintConflictStatus, ci::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}})
    @ MathOptInterface.Bridges ~/.julia/packages/MathOptInterface/IIN1o/src/Bridges/bridge_optimizer.jl:1239
  [4] get(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{Gurobi.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, attr::MathOptInterface.ConstraintConflictStatus, index::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}})
    @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/IIN1o/src/Utilities/cachingoptimizer.jl:862
  [5] _moi_get_result(::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{Gurobi.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, ::MathOptInterface.ConstraintConflictStatus, ::Vararg{Any})
    @ JuMP ~/.julia/packages/JuMP/lnUbA/src/JuMP.jl:1226
  [6] get(model::Model, attr::MathOptInterface.ConstraintConflictStatus, cr::ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}}, ScalarShape})
    @ JuMP ~/.julia/packages/JuMP/lnUbA/src/JuMP.jl:1289
  [7] _broadcast_getindex_evalf
    @ ./broadcast.jl:670 [inlined]
  [8] _broadcast_getindex
    @ ./broadcast.jl:643 [inlined]
  [9] getindex
    @ ./broadcast.jl:597 [inlined]
 [10] copy
    @ ./broadcast.jl:875 [inlined]
 [11] materialize(bc::Base.Broadcast.Broadcasted{Base.Broadcast.DefaultArrayStyle{0}, Nothing, typeof(MathOptInterface.get), Tuple{Base.RefValue{Model}, Base.RefValue{MathOptInterface.ConstraintConflictStatus}, Base.RefValue{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}}, ScalarShape}}}})
    @ Base.Broadcast ./broadcast.jl:860
 [12] top-level scope
    @ REPL[16]:1

julia> # Without broadcasting
       MOI.get(model, MOI.ConstraintConflictStatus(), c2)
ERROR: ArgumentError: Gurobi.Optimizer does not support getting the attribute MathOptInterface.ConstraintConflictStatus().
Stacktrace:
 [1] get_fallback(model::Gurobi.Optimizer, attr::MathOptInterface.ConstraintConflictStatus, #unused#::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}})
   @ MathOptInterface ~/.julia/packages/MathOptInterface/IIN1o/src/attributes.jl:361
 [2] get(model::Gurobi.Optimizer, attr::MathOptInterface.ConstraintConflictStatus, args::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}})
   @ MathOptInterface ~/.julia/packages/MathOptInterface/IIN1o/src/attributes.jl:330
 [3] get(b::MathOptInterface.Bridges.LazyBridgeOptimizer{Gurobi.Optimizer}, attr::MathOptInterface.ConstraintConflictStatus, ci::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}})
   @ MathOptInterface.Bridges ~/.julia/packages/MathOptInterface/IIN1o/src/Bridges/bridge_optimizer.jl:1239
 [4] get(model::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{Gurobi.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, attr::MathOptInterface.ConstraintConflictStatus, index::MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}})
   @ MathOptInterface.Utilities ~/.julia/packages/MathOptInterface/IIN1o/src/Utilities/cachingoptimizer.jl:862
 [5] _moi_get_result(::MathOptInterface.Utilities.CachingOptimizer{MathOptInterface.Bridges.LazyBridgeOptimizer{Gurobi.Optimizer}, MathOptInterface.Utilities.UniversalFallback{MathOptInterface.Utilities.Model{Float64}}}, ::MathOptInterface.ConstraintConflictStatus, ::Vararg{Any})
   @ JuMP ~/.julia/packages/JuMP/lnUbA/src/JuMP.jl:1226
 [6] get(model::Model, attr::MathOptInterface.ConstraintConflictStatus, cr::ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarQuadraticFunction{Float64}, MathOptInterface.EqualTo{Float64}}, ScalarShape})
   @ JuMP ~/.julia/packages/JuMP/lnUbA/src/JuMP.jl:1289
 [7] top-level scope
   @ REPL[17]:2

As you can see c1 is fine, but c2 encounters an error, I’ve also tried this with Ipopt.

Versions tried:
Julia 1.7.2 (2022-02-06)
[2e9cd046] Gurobi v0.10.1
[4076af6c] JuMP v0.22.2

Julia 1.8.0-beta3 (2022-03-29)
[2e9cd046] Gurobi v0.11.1
[4076af6c] JuMP v1.0.0

It seems that the problem is not with small values, but with the fact that the equality constraint is quadratic. This works:

using JuMP
using Gurobi

model = Model(Gurobi.Optimizer)
@variable(model, x)
a = 5e-5
b = 3e-3

@constraint(model, c1, x == 0)
@constraint(model, c2, 1 == (a * x +b*x))

@objective(model, Min, x)

optimize!(model)

compute_conflict!(model)

MOI.get.(model, MOI.ConstraintConflictStatus(), c1)

MOI.get.(model, MOI.ConstraintConflictStatus(), c2)

Gives:
image

Can Gurobi always support quadratic equality constraints? The example works with inequality:

using JuMP
using Gurobi

model = Model(Gurobi.Optimizer)
@variable(model, x)
a = 5e-5
b = 3e-3

@constraint(model, c1, x == 0)
@constraint(model, c2, 1 <= (a * x^2 +b*x))

@objective(model, Min, x)

optimize!(model)

compute_conflict!(model)

# MOI.get.(model, MOI.ConstraintConflictStatus(), c1)

MOI.get.(model, MOI.ConstraintConflictStatus(), c2)
# # Without broadcasting
MOI.get.(model, MOI.ConstraintConflictStatus(), c2)

and gives:

I am unfortunately not familiar with Gurobi enough to offer more help.

Thanks for your response. Given the error message I hadn’t considered that it could be a Gurobi limitation. Reviewing but after reviewing the Gurobi documentation they state “Quadratic equality constraints are always non-convex; they will give a GRB_ERROR_QCP_EQUALITY_CONSTRAINT error with default settings” so it’s likely that’s causing the error. But why is JuMP giving the error message ArgumentError: Gurobi.Optimizer does not support getting the attribute MathOptInterface.ConstraintConflictStatus().

To further support your point if I set c2 to a linear equality constraint @constraint(model, c2, 1 == (a * x + b * x)) I’m able to get the conflict status.

It seems to me that my problem is I’m trying to do something Gurobi isn’t able to and that JuMP is giving a misleading error message.

After some digging - it seems that JuMP doesn’t handle this particular conflict code. For your problem:
image

But JuMP seems to check:
image

Then it throws an error that we see. Maybe open an issue with JuMP? Although someone more experienced may be more helpful

Thanks, I’ll look at opening a ticket tomorrow.