Skip to content

Commit

Permalink
Update merge with IIS conflits
Browse files Browse the repository at this point in the history
  • Loading branch information
odow committed Jul 11, 2019
1 parent c523cfb commit e6c869d
Show file tree
Hide file tree
Showing 2 changed files with 90 additions and 48 deletions.
98 changes: 79 additions & 19 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -2348,12 +2348,14 @@ end
"""
ConflictStatus()
Return an `MOI.TerminationStatusCode` indicating the status of the last computed conflict.
If a minimal conflict is found, it will return `MOI.OPTIMAL`. If the problem is feasible, it will
return `MOI.INFEASIBLE`. If `compute_conflict` has not been called yet, it will return
Return an `MOI.TerminationStatusCode` indicating the status of the last
computed conflict. If a minimal conflict is found, it will return
`MOI.OPTIMAL`. If the problem is feasible, it will return `MOI.INFEASIBLE`. If
`compute_conflict` has not been called yet, it will return
`MOI.OPTIMIZE_NOT_CALLED`.
"""
struct ConflictStatus <: MOI.AbstractModelAttribute end
struct ConflictStatus <: MOI.AbstractModelAttribute end

MOI.is_set_by_optimize(::ConflictStatus) = true

function MOI.get(model::Optimizer, ::ConflictStatus)
Expand Down Expand Up @@ -2398,45 +2400,103 @@ end

"""
ConstraintConflictStatus()
A Boolean constraint attribute indicating whether the constraint participates in the last computed conflict.
A Boolean constraint attribute indicating whether the constraint participates
in the last computed conflict.
"""
struct ConstraintConflictStatus <: MOI.AbstractConstraintAttribute end

MOI.is_set_by_optimize(::ConstraintConflictStatus) = true

function MOI.get(model::Optimizer, ::ConstraintConflictStatus, index::MOI.ConstraintIndex{<:MOI.SingleVariable, <:LQOI.LE})
function MOI.get(
model::Optimizer, ::ConstraintConflictStatus,
index::MOI.ConstraintIndex{MOI.SingleVariable, <:MOI.LessThan}
)
_ensure_conflict_computed(model)
return !_is_feasible(model) && Bool(get_intattrelement(model.inner, "IISUB", LQOI.get_column(model, model[index])))
if _is_feasible(model)
return false
end
return get_intattrelement(model.inner, "IISUB", _info(model, index).column) > 0
end

function MOI.get(model::Optimizer, ::ConstraintConflictStatus, index::MOI.ConstraintIndex{<:MOI.SingleVariable, <:LQOI.GE})
function MOI.get(
model::Optimizer, ::ConstraintConflictStatus,
index::MOI.ConstraintIndex{MOI.SingleVariable, <:MOI.GreaterThan}
)
_ensure_conflict_computed(model)
return !_is_feasible(model) && Bool(get_intattrelement(model.inner, "IISLB", LQOI.get_column(model, model[index])))
if _is_feasible(model)
return false
end
return get_intattrelement(model.inner, "IISLB", _info(model, index).column) > 0
end

function MOI.get(model::Optimizer, ::ConstraintConflictStatus, index::MOI.ConstraintIndex{<:MOI.SingleVariable, <:Union{LQOI.EQ, LQOI.IV}})
function MOI.get(
model::Optimizer, ::ConstraintConflictStatus,
index::MOI.ConstraintIndex{
MOI.SingleVariable, <:Union{MOI.EqualTo, MOI.Interval}
}
)
_ensure_conflict_computed(model)
return !_is_feasible(model) && (
Bool(get_intattrelement(model.inner, "IISUB", LQOI.get_column(model, model[index]))) || Bool(get_intattrelement(model.inner, "IISLB", model[index])))
if _is_feasible(model)
return false
end
if get_intattrelement(model.inner, "IISLB", _info(model, index).column) > 0
return true
end
return get_intattrelement(model.inner, "IISUB", _info(model, index).column) > 0
end

function MOI.get(model::Optimizer, ::ConstraintConflictStatus, index::MOI.ConstraintIndex{<:MOI.ScalarAffineFunction, <:Union{LQOI.LE, LQOI.GE, LQOI.EQ}})
function MOI.get(
model::Optimizer, ::ConstraintConflictStatus,
index::MOI.ConstraintIndex{
MOI.ScalarAffineFunction{Float64},
<:Union{MOI.LessThan, MOI.GreaterThan, MOI.EqualTo}
}
)
_ensure_conflict_computed(model)
return !_is_feasible(model) && Bool(get_intattrelement(model.inner, "IISConstr", model[index]))
if _is_feasible(model)
return false
end
return get_intattrelement(model.inner, "IISConstr", _info(model, index).row) > 0
end

function MOI.get(model::Optimizer, ::ConstraintConflictStatus, index::MOI.ConstraintIndex{<:MOI.ScalarQuadraticFunction, <:Union{LQOI.LE, LQOI.GE}})
function MOI.get(
model::Optimizer, ::ConstraintConflictStatus,
index::MOI.ConstraintIndex{
MOI.ScalarQuadraticFunction{Float64},
<:Union{MOI.LessThan, MOI.GreaterThan}
}
)
_ensure_conflict_computed(model)
return !_is_feasible(model) && Bool(get_intattrelement(model.inner, "IISQConstr", model[index]))
if _is_feasible(model)
return false
end
return get_intattrelement(model.inner, "IISQConstr", _info(model, index).row) > 0
end

function MOI.supports(::Optimizer, ::ConstraintConflictStatus, ::Type{MOI.ConstraintIndex{<:MOI.SingleVariable, <:LQOI.LinSets}})
function MOI.supports(
::Optimizer, ::ConstraintConflictStatus,
::Type{<:MOI.ConstraintIndex{MOI.SingleVariable, <:SCALAR_SETS}}
)
return true
end

function MOI.supports(::Optimizer, ::ConstraintConflictStatus, ::Type{MOI.ConstraintIndex{<:MOI.ScalarAffineFunction, <:Union{LQOI.LE, LQOI.GE, LQOI.EQ}}})
function MOI.supports(
::Optimizer, ::ConstraintConflictStatus,
::Type{<:MOI.ConstraintIndex{
MOI.ScalarAffineFunction{Float64},
<:Union{MOI.LessThan, MOI.GreaterThan, MOI.EqualTo}
}}
)
return true
end

function MOI.supports(::Optimizer, ::ConstraintConflictStatus, ::Type{MOI.ConstraintIndex{<:MOI.ScalarQuadraticFunction, <:Union{LQOI.LE, LQOI.GE}}})
function MOI.supports(
::Optimizer, ::ConstraintConflictStatus,
::Type{<:MOI.ConstraintIndex{
MOI.ScalarQuadraticFunction{Float64},
<:Union{MOI.LessThan, MOI.GreaterThan}
}}
)
return true
end
40 changes: 11 additions & 29 deletions test/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -314,7 +314,7 @@ end

@testset "Conflict refiner" begin
@testset "Variable bounds (SingleVariable and LessThan/GreaterThan)" begin
model = Gurobi.Optimizer()
model = Gurobi.Optimizer(GUROBI_ENV, OutputFlag=0)
x = MOI.add_variable(model)
c1 = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(2.0))
c2 = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.LessThan(1.0))
Expand All @@ -332,7 +332,7 @@ end
end

@testset "Variable bounds (ScalarAffine)" begin
model = Gurobi.Optimizer()
model = Gurobi.Optimizer(GUROBI_ENV, OutputFlag=0)
x = MOI.add_variable(model)
c1 = MOI.add_constraint(model, MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0], [x]), 0.0), MOI.GreaterThan(2.0))
c2 = MOI.add_constraint(model, MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0], [x]), 0.0), MOI.LessThan(1.0))
Expand All @@ -348,29 +348,12 @@ end
@test MOI.get(model, Gurobi.ConstraintConflictStatus(), c2) == true
end

@testset "Variable fixing (SingleVariable and EqualTo)" begin
model = Gurobi.Optimizer()
x = MOI.add_variable(model)
c1 = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.EqualTo(1.0))
c2 = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(2.0))

# Getting the results before the conflict refiner has been called must return an error.
@test MOI.get(model, Gurobi.ConflictStatus()) == MOI.OPTIMIZE_NOT_CALLED
@test_throws ErrorException MOI.get(model, Gurobi.ConstraintConflictStatus(), c1)

# Once it's called, no problem.
Gurobi.compute_conflict(model)
@test MOI.get(model, Gurobi.ConflictStatus()) == MOI.OPTIMAL
@test MOI.get(model, Gurobi.ConstraintConflictStatus(), c1) == true
@test MOI.get(model, Gurobi.ConstraintConflictStatus(), c2) == true
end

@testset "Variable bounds (SingleVariable and Interval)" begin
model = Gurobi.Optimizer()
@testset "Variable bounds (Invali Interval)" begin
model = Gurobi.Optimizer(GUROBI_ENV, OutputFlag=0)
x = MOI.add_variable(model)
c1 = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.Interval(1.0, 3.0))
c2 = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.LessThan(0.0))

c1 = MOI.add_constraint(
model, MOI.SingleVariable(x), MOI.Interval(1.0, 0.0)
)
# Getting the results before the conflict refiner has been called must return an error.
@test MOI.get(model, Gurobi.ConflictStatus()) == MOI.OPTIMIZE_NOT_CALLED
@test_throws ErrorException MOI.get(model, Gurobi.ConstraintConflictStatus(), c1)
Expand All @@ -379,11 +362,10 @@ end
Gurobi.compute_conflict(model)
@test MOI.get(model, Gurobi.ConflictStatus()) == MOI.OPTIMAL
@test MOI.get(model, Gurobi.ConstraintConflictStatus(), c1) == true
@test MOI.get(model, Gurobi.ConstraintConflictStatus(), c2) == true
end

@testset "Two conflicting constraints (GreaterThan, LessThan)" begin
model = Gurobi.Optimizer()
model = Gurobi.Optimizer(GUROBI_ENV, OutputFlag=0)
x = MOI.add_variable(model)
y = MOI.add_variable(model)
b1 = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(0.0))
Expand All @@ -407,7 +389,7 @@ end
end

@testset "Two conflicting constraints (EqualTo)" begin
model = Gurobi.Optimizer()
model = Gurobi.Optimizer(GUROBI_ENV, OutputFlag=0)
x = MOI.add_variable(model)
y = MOI.add_variable(model)
b1 = MOI.add_constraint(model, MOI.SingleVariable(x), MOI.GreaterThan(0.0))
Expand All @@ -431,7 +413,7 @@ end
end

@testset "Variables outside conflict" begin
model = Gurobi.Optimizer()
model = Gurobi.Optimizer(GUROBI_ENV, OutputFlag=0)
x = MOI.add_variable(model)
y = MOI.add_variable(model)
z = MOI.add_variable(model)
Expand All @@ -458,7 +440,7 @@ end
end

@testset "No conflict" begin
model = Gurobi.Optimizer()
model = Gurobi.Optimizer(GUROBI_ENV, OutputFlag=0)
x = MOI.add_variable(model)
c1 = MOI.add_constraint(model, MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0], [x]), 0.0), MOI.GreaterThan(1.0))
c2 = MOI.add_constraint(model, MOI.ScalarAffineFunction(MOI.ScalarAffineTerm.([1.0], [x]), 0.0), MOI.LessThan(2.0))
Expand Down

0 comments on commit e6c869d

Please sign in to comment.