Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Run all tests against bridged SDPA model #2357

Merged
merged 5 commits into from
Dec 23, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
78 changes: 61 additions & 17 deletions src/Bridges/bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -697,6 +697,7 @@ function MOI.delete(b::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex)
else
MOI.delete(b.model, ci)
end
return
odow marked this conversation as resolved.
Show resolved Hide resolved
end

function MOI.delete(
Expand Down Expand Up @@ -1732,28 +1733,71 @@ function add_bridged_constraint(b, BridgeType, f, s)
return ci
end

function _throw_if_bound_already_set(b, x, ::S2, ::Type{S1}) where {S1,S2}
ErrorType = _bound_error_type(S1, S2)
if ErrorType === nothing
return
end
ci = MOI.ConstraintIndex{MOI.VariableIndex,S1}(x.value)
if (is_bridged(b, S1) && MOI.is_valid(b, ci)) || MOI.is_valid(b.model, ci)
throw(ErrorType(x))
end
return
end

function _bound_error_type(
::Type{S1},
::Type{S2},
) where {S1<:MOI.LessThan,S2<:MOI.LessThan}
return MOI.UpperBoundAlreadySet{S1,S2}
end

function _bound_error_type(::Type{S1}, ::Type{S2}) where {S1<:MOI.LessThan,S2}
return MOI.UpperBoundAlreadySet{S1,S2}
end

function _bound_error_type(::Type{S1}, ::Type{S2}) where {S1,S2<:MOI.LessThan}
return MOI.UpperBoundAlreadySet{S1,S2}
end

function _bound_error_type(::Type{S1}, ::Type{S2}) where {S1,S2}
return MOI.LowerBoundAlreadySet{S1,S2}
end

_bound_error_type(::Type{<:MOI.LessThan}, ::Type{<:MOI.GreaterThan}) = nothing

_bound_error_type(::Type{<:MOI.GreaterThan}, ::Type{<:MOI.LessThan}) = nothing

function _check_double_single_variable(
b::AbstractBridgeOptimizer,
func::MOI.VariableIndex,
set,
)
if !is_bridged(b, typeof(set))
# The variable might have been constrained in `b.model` to a set of type
# `typeof(set)` on creation.
ci = MOI.ConstraintIndex{MOI.VariableIndex,typeof(set)}(func.value)
if MOI.is_valid(b.model, ci)
error(
"The variable `$(func)` was constrained on creation ",
"to a set of type `$(typeof(set))`. Therefore, a ",
"`VariableIndex`-in-`$(typeof(set))` cannot be added on this ",
"variable. Use `MOI.set` with `MOI.ConstraintSet()` instead.",
)
end
end
x::MOI.VariableIndex,
s::S,
) where {
T,
S<:Union{
MOI.Interval{T},
MOI.EqualTo{T},
MOI.Semicontinuous{T},
MOI.Semiinteger{T},
MOI.LessThan{T},
MOI.GreaterThan{T},
},
}
# !!! warning
# The order here is _very_ important because an Interval constraint
# might get re-written into a LessThan and GreaterThan. To throw the
# appropriate error, we _must_ check sets like `Interval` and `EqualTo`
# _before_ `LessThan` and `GreaterThan`.
_throw_if_bound_already_set(b, x, s, MOI.Interval{T})
_throw_if_bound_already_set(b, x, s, MOI.EqualTo{T})
_throw_if_bound_already_set(b, x, s, MOI.Semicontinuous{T})
_throw_if_bound_already_set(b, x, s, MOI.Semiinteger{T})
_throw_if_bound_already_set(b, x, s, MOI.LessThan{T})
_throw_if_bound_already_set(b, x, s, MOI.GreaterThan{T})
return
end

_check_double_single_variable(b::AbstractBridgeOptimizer, func, set) = nothing
_check_double_single_variable(::AbstractBridgeOptimizer, ::Any, ::Any) = nothing

function MOI.add_constraint(
b::AbstractBridgeOptimizer,
Expand Down
2 changes: 2 additions & 0 deletions src/Utilities/variables_container.jl
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,8 @@
return !iszero(b.set_mask[ci.value] & _single_variable_flag(S))
end

MOI.is_valid(::VariablesContainer, ::MOI.ConstraintIndex) = false

Check warning on line 311 in src/Utilities/variables_container.jl

View check run for this annotation

Codecov / codecov/patch

src/Utilities/variables_container.jl#L311

Added line #L311 was not covered by tests

function MOI.get(
model::VariablesContainer,
::MOI.ConstraintFunction,
Expand Down
60 changes: 39 additions & 21 deletions test/Bridges/lazy_bridge_optimizer.jl
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,15 @@ end
function MOI.supports(
::StandardLPModel{T},
::MOI.ObjectiveFunction{
<:Union{MOI.VariableIndex,MOI.ScalarQuadraticFunction{T}},
<:Union{
MOI.VariableIndex,
MOI.ScalarQuadraticFunction{T},
MOI.ScalarNonlinearFunction,
MOI.VectorOfVariables,
odow marked this conversation as resolved.
Show resolved Hide resolved
MOI.VectorAffineFunction{T},
MOI.VectorQuadraticFunction{T},
MOI.VectorNonlinearFunction,
},
},
) where {T}
return false
Expand Down Expand Up @@ -295,39 +303,49 @@ function test_MOI_runtests_LPModel()
bridged = MOI.Bridges.full_bridge_optimizer(model, Float64)
MOI.Test.runtests(
bridged,
MOI.Test.Config(exclude = Any[MOI.optimize!]),
MOI.Test.Config(exclude = Any[MOI.optimize!]);
include = ["test_model_", "test_constraint_"],
exclude = [
"test_constraint_ConstraintDualStart",
"test_constraint_ConstraintPrimalStart",
"test_model_default_DualStatus",
"test_model_default_PrimalStatus",
"test_model_default_TerminationStatus",
"test_model_LowerBoundAlreadySet",
"test_model_UpperBoundAlreadySet",
],
)
return
end

function test_MOI_runtests_StandardSDPAModel()
model = StandardSDPAModel{Float64}()
bridged = MOI.Bridges.full_bridge_optimizer(model, Float64)
model =
MOI.instantiate(StandardSDPAModel{Float64}; with_bridge_type = Float64)
MOI.Test.runtests(
bridged,
MOI.Test.Config(exclude = Any[MOI.optimize!]),
include = ["ConstraintName", "VariableName"],
model,
MOI.Test.Config(
exclude = Any[MOI.optimize!, MOI.SolverName, MOI.SolverVersion],
);
exclude = String[
# Skip these tests because the bridge reformulates bound
# constraints, so there is no conflict. An error _is_ thrown if two
# sets of the same type are added.
"test_model_LowerBoundAlreadySet",
"test_model_UpperBoundAlreadySet",
# MOI.ScalarFunctionConstantNotZero is thrown, not of the original
# constraint, but of the bridged constraint. This seems okay. The
# fix would require that a bridge optimizer has a try-catch for this
# error.
"test_model_ScalarFunctionConstantNotZero",
# The error is:
# Cannot substitute `MOI.VariableIndex(1)` as it is bridged into `0.0 + 1.0 MOI.VariableIndex(-1)`.
# This seems okay. We can't get a list of variables if they are
# bridged.
"test_model_ListOfVariablesWithAttributeSet",
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This one is a maybe. I didn't look too deeply if you know better

],
)
return
end

function test_MOI_runtests_GeometricSDPAModel()
model = GeometricSDPAModel{Float64}()
bridged = MOI.Bridges.full_bridge_optimizer(model, Float64)
model =
MOI.instantiate(GeometricSDPAModel{Float64}; with_bridge_type = Float64)
MOI.Test.runtests(
bridged,
MOI.Test.Config(exclude = Any[MOI.optimize!]),
include = ["ConstraintName", "VariableName"],
model,
MOI.Test.Config(
exclude = Any[MOI.optimize!, MOI.SolverName, MOI.SolverVersion],
),
)
return
end
Expand Down
10 changes: 9 additions & 1 deletion test/Bridges/sdpa_models.jl
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,15 @@ end
function MOI.supports(
::StandardSDPAModel{T},
::MOI.ObjectiveFunction{
<:Union{MOI.VariableIndex,MOI.ScalarQuadraticFunction{T}},
<:Union{
MOI.VariableIndex,
MOI.ScalarQuadraticFunction{T},
MOI.ScalarNonlinearFunction,
MOI.VectorOfVariables,
MOI.VectorAffineFunction{T},
MOI.VectorQuadraticFunction{T},
MOI.VectorNonlinearFunction,
},
},
) where {T}
return false
Expand Down
Loading