Skip to content
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
3 changes: 1 addition & 2 deletions src/Bridges/Bridges.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ import MathOptInterface as MOI
using ..MathOptComplements:
ComplementsWithSetType,
AbstractComplementarityRelaxation,
ComplementarityReformulation,
_remove_bounds!
ComplementarityReformulation

include("VerticalBridge.jl")
include("SpecifySetTypeBridge.jl")
Expand Down
24 changes: 9 additions & 15 deletions src/Bridges/ComplementsVectorizeBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -40,15 +40,11 @@ struct ComplementsVectorizeBridge{T,F,S,SV} <:
set_constant::T
end

function _vector_set_type(::Type{<:MOI.GreaterThan})
return MOI.Nonnegatives
end
function _vector_set_type(::Type{<:MOI.LessThan})
return MOI.Nonpositives
end
function _vector_set_type(::Type{<:MOI.EqualTo})
return MOI.Zeros
end
_vector_set_type(::Type{<:MOI.GreaterThan}) = MOI.Nonnegatives

_vector_set_type(::Type{<:MOI.LessThan}) = MOI.Nonpositives

_vector_set_type(::Type{<:MOI.EqualTo}) = MOI.Zeros

function _set_constant(
::Type{T},
Expand All @@ -58,6 +54,7 @@ function _set_constant(
) where {T}
return MOI.Utilities.get_bounds(model, T, x2)[1]
end

function _set_constant(
::Type{T},
model,
Expand All @@ -66,6 +63,7 @@ function _set_constant(
) where {T}
return MOI.Utilities.get_bounds(model, T, x2)[2]
end

function _set_constant(
::Type{T},
model,
Expand Down Expand Up @@ -94,12 +92,8 @@ end
function MOI.supports_constraint(
::Type{<:ComplementsVectorizeBridge},
::Type{MOI.VectorOfVariables},
::Type{
<:ComplementsWithSetType{
<:Union{MOI.GreaterThan,MOI.LessThan,MOI.EqualTo},
},
},
)
::Type{ComplementsWithSetType{S}},
) where {S<:Union{MOI.GreaterThan,MOI.LessThan,MOI.EqualTo}}
return true
end

Expand Down
19 changes: 7 additions & 12 deletions src/Bridges/FlipSignBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,18 +38,13 @@ struct FlipSignBridge{T,F,S1,S2,G} <: MOI.Bridges.Constraint.AbstractBridge
original_func::G
end

function _flip_set_type(::Type{MOI.Nonnegatives})
return MOI.Nonpositives
end
function _flip_set_type(::Type{MOI.Nonpositives})
return MOI.Nonnegatives
end
function _flip_set_type(::Type{MOI.GreaterThan{T}}) where {T}
return MOI.LessThan{T}
end
function _flip_set_type(::Type{MOI.LessThan{T}}) where {T}
return MOI.GreaterThan{T}
end
_flip_set_type(::Type{MOI.Nonnegatives}) = MOI.Nonpositives

_flip_set_type(::Type{MOI.Nonpositives}) = MOI.Nonnegatives

_flip_set_type(::Type{MOI.GreaterThan{T}}) where {T} = MOI.LessThan{T}

_flip_set_type(::Type{MOI.LessThan{T}}) where {T} = MOI.GreaterThan{T}

const _FlippableSets =
Union{MOI.Nonnegatives,MOI.Nonpositives,MOI.GreaterThan,MOI.LessThan}
Expand Down
31 changes: 26 additions & 5 deletions src/Bridges/NonlinearBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ function _complementarity_bounds(
) where {T}
return (zero(T), T(Inf))
end

function _complementarity_bounds(
::Type{MOI.Nonpositives},
model,
Expand All @@ -163,6 +164,7 @@ function _complementarity_bounds(
) where {T}
return (T(-Inf), zero(T))
end

function _complementarity_bounds(
::Type{MOI.Zeros},
model,
Expand All @@ -171,6 +173,7 @@ function _complementarity_bounds(
) where {T}
return (zero(T), zero(T))
end

function _complementarity_bounds(
::Type{<:MOI.GreaterThan},
model,
Expand All @@ -179,6 +182,7 @@ function _complementarity_bounds(
) where {T}
return (MOI.Utilities.get_bounds(model, T, x2)[1], T(Inf))
end

function _complementarity_bounds(
::Type{<:MOI.LessThan},
model,
Expand All @@ -187,6 +191,7 @@ function _complementarity_bounds(
) where {T}
return (T(-Inf), MOI.Utilities.get_bounds(model, T, x2)[2])
end

function _complementarity_bounds(
::Type{<:MOI.Interval},
model,
Expand All @@ -197,11 +202,15 @@ function _complementarity_bounds(
end

"""
reformulate_as_nonlinear_program!(model, relaxation, fun, set::ComplementsWithSetType{S})
reformulate_as_nonlinear_program!(
model,
relaxation,
fun,
set::ComplementsWithSetType{S},
)

Reformulate complementarity constraints as a nonlinear program using the given
relaxation. The set type `S` determines which bound case to use.

"""
function reformulate_as_nonlinear_program!(
model::MOI.ModelLike,
Expand Down Expand Up @@ -334,7 +343,6 @@ For `epsilon ≥ 0`, the complementarity constraint `0 ≤ a ⟂ b ≥ 0` is ref
0 ≤ b
a + b - sqrt((a + b)^2 + epsilon) ≤ 0
```

"""
struct FischerBurmeisterRelaxation{T} <: AbstractComplementarityRelaxation
epsilon::T
Expand Down Expand Up @@ -439,13 +447,27 @@ For `epsilon ≥ 0`, the complementarity constraint `0 ≤ a ⟂ b ≥ 0` is ref
```
a . b ≤ epsilon^2
(a + epsilon) . (b + epsilon) ≥ epsilon^2

```
"""
struct LiuFukushimaRelaxation{T} <: AbstractComplementarityRelaxation
epsilon::T
end

function _remove_bounds!(model::MOI.ModelLike, x::MOI.VariableIndex)
for cidx in [
MOI.ConstraintIndex{MOI.VariableIndex,MOI.Interval{Float64}}(x.value),
MOI.ConstraintIndex{MOI.VariableIndex,MOI.LessThan{Float64}}(x.value),
MOI.ConstraintIndex{MOI.VariableIndex,MOI.GreaterThan{Float64}}(
x.value,
),
]
if MOI.is_valid(model, cidx)
MOI.delete(model, cidx)
end
end
return
end

function _relax_complementarity_lower_bound!(
model::MOI.ModelLike,
relaxation::LiuFukushimaRelaxation,
Expand Down Expand Up @@ -528,7 +550,6 @@ with the function `ϕ`:
-0.5 ((a -epsilon)^2 + (b - epsilon)^2) otherwise

```

"""
struct KanzowSchwarzRelaxation{T} <: AbstractComplementarityRelaxation
epsilon::T
Expand Down
10 changes: 5 additions & 5 deletions src/Bridges/VerticalBridge.jl
Original file line number Diff line number Diff line change
Expand Up @@ -187,6 +187,7 @@ function _is_single_variable(func::MOI.ScalarAffineFunction)
func.terms[1].coefficient == 1.0 &&
iszero(func.constant)
end

function _is_single_variable(func::MOI.ScalarQuadraticFunction)
return (
length(func.quadratic_terms) == 0 &&
Expand All @@ -195,13 +196,17 @@ function _is_single_variable(func::MOI.ScalarQuadraticFunction)
iszero(func.constant)
)
end

function _is_single_variable(func::MOI.ScalarNonlinearFunction)
return func.head == :+ &&
length(func.args) == 1 &&
isa(func.args[1], MOI.VariableIndex)
end

_get_variable(func::MOI.ScalarAffineFunction) = func.terms[1].variable

_get_variable(func::MOI.ScalarQuadraticFunction) = func.affine_terms[1].variable

_get_variable(func::MOI.ScalarNonlinearFunction) = func.args[1]

# TODO: add support for ScalarNonlinearTerm
Expand All @@ -211,10 +216,8 @@ function _parse_complementarity_constraint(
)
exprs = MOI.Utilities.scalarize(fun)
@assert length(exprs) == 2*n_comp

cc_lhs = MOI.AbstractScalarFunction[]
cc_rhs = MOI.VariableIndex[]

for i in 1:n_comp
# Parse LHS
t1 = exprs[i]
Expand All @@ -224,7 +227,6 @@ function _parse_complementarity_constraint(
else
push!(cc_lhs, t1)
end

# Parse RHS
isvar2 = _is_single_variable(t2)
if !isvar2
Expand All @@ -237,7 +239,6 @@ function _parse_complementarity_constraint(
end
push!(cc_rhs, _get_variable(t2))
end

return cc_lhs, cc_rhs
end

Expand All @@ -250,7 +251,6 @@ expressions are rewritten with a slack. `T` is the coefficient type used for
the generated equality constraints.

Once reformulated, the complementarity constraints involve only single variables.

"""
function reformulate_to_vertical!(
model::MOI.ModelLike,
Expand Down
16 changes: 13 additions & 3 deletions src/MOI_wrapper.jl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ mutable struct Optimizer{T,O<:MOI.ModelLike} <:
constraint_map::MOI.Bridges.Constraint.Map
con_to_name::Dict{MOI.ConstraintIndex,String}
name_to_con::Union{Dict{String,MOI.ConstraintIndex},Nothing}

function Optimizer{T}(model::MOI.ModelLike) where {T}
return new{T,typeof(model)}(
model,
Expand Down Expand Up @@ -45,12 +46,14 @@ MOI.Bridges.is_bridged(::Optimizer, ::Type{<:MOI.AbstractSet}) = false

# Complements and ComplementsWithSetType are bridged
MOI.Bridges.is_bridged(::Optimizer, ::Type{MOI.Complements}) = true

function MOI.Bridges.supports_bridging_constrained_variable(
::Optimizer,
::Type{MOI.Complements},
)
return true
end

function MOI.Bridges.bridge_type(
::Optimizer{T},
::Type{MOI.Complements},
Expand All @@ -59,6 +62,7 @@ function MOI.Bridges.bridge_type(
end

MOI.Bridges.is_bridged(::Optimizer, ::Type{<:ComplementsWithSetType}) = true

function MOI.Bridges.supports_bridging_constrained_variable(
::Optimizer,
::Type{<:ComplementsWithSetType},
Expand Down Expand Up @@ -86,13 +90,15 @@ function MOI.Bridges.is_bridged(
)
return true
end

function MOI.Bridges.supports_bridging_constraint(
::Optimizer,
::Type{<:MOI.AbstractVectorFunction},
::Type{MOI.Complements},
)
return true
end

function MOI.Bridges.bridge_type(
::Optimizer{T},
::Type{<:MOI.AbstractVectorFunction},
Expand All @@ -118,6 +124,7 @@ function MOI.Bridges.is_bridged(
)
return true
end

function MOI.Bridges.supports_bridging_constraint(
::Optimizer,
::Type{<:MOI.AbstractVectorFunction},
Expand Down Expand Up @@ -251,8 +258,10 @@ function _additional_arguments(
return (model.reformulation,)
end

# TODO it would be nice if MOI was defining this `MOI.Bridges.additional_arguments` function and
# already had this implementation of `add_bridged_constraint` so that I don't have to reimplement it
# TODO(blegat): it would be nice if MOI was defining this
# `MOI.Bridges.additional_arguments` function and already had this
# implementation of `add_bridged_constraint` so that I don't have to reimplement
# it
function MOI.Bridges.add_bridged_constraint(b::Optimizer, BridgeType, f, s)
bridge = MOI.Bridges.Constraint.Constraint.bridge_constraint(
BridgeType,
Expand All @@ -261,7 +270,8 @@ function MOI.Bridges.add_bridged_constraint(b::Optimizer, BridgeType, f, s)
s,
_additional_arguments(b, BridgeType)...,
)
# The rest is copy-pasted from the default implementation of `add_bridged_constraint` in MOI
# The rest is copy-pasted from the default implementation of
# `add_bridged_constraint` in MOI
ci = MOI.Bridges.Constraint.add_key_for_bridge(
MOI.Bridges.Constraint.bridges(b)::MOI.Bridges.Constraint.Map,
bridge,
Expand Down
Loading
Loading