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

Refactor constraint bridges with AbstractSetMapBridge #933

Merged
merged 6 commits into from
Oct 30, 2019
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
2 changes: 2 additions & 0 deletions docs/src/apireference.md
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,8 @@ Bridges.Constraint.SplitIntervalBridge
Bridges.Constraint.RSOCBridge
Bridges.Constraint.SOCRBridge
Bridges.Constraint.QuadtoSOCBridge
Bridges.Constraint.NormInfinityBridge
Bridges.Constraint.NormOneBridge
Bridges.Constraint.GeoMeanBridge
Bridges.Constraint.SquareBridge
Bridges.Constraint.RootDetBridge
Expand Down
67 changes: 3 additions & 64 deletions src/Bridges/Constraint/Constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,67 +26,10 @@ end

# Constraint bridges
# Function conversion bridges
include("function_conversion.jl")
# Transformation between a set S1 and a set S2 such that A*S1 = S2 for some linear map A
include("set_map.jl")

"""
abstract type AbstractFunctionConversionBridge <: AbstractBridge end

Bridge a constraint `F`-in-`S` into a constraint `G`-in-`S` where `F` and `G`
are equivalent representations of the same function. By convention, the
transformed function is stored in the `constraint` field.
"""
abstract type AbstractFunctionConversionBridge <: AbstractBridge end

function MOI.get(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute,
bridge::AbstractFunctionConversionBridge)
if invariant_under_function_conversion(attr)
return MOI.get(model, attr, bridge.constraint)
else
throw(ArgumentError("Bridge of type `$(typeof(bridge))` does not support accessing the attribute `$attr` because `MOIB.Constraint.invariant_under_function_conversion($attr)` returns `false`."))
end
end

function MOI.set(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute,
bridge::AbstractFunctionConversionBridge, value)
if invariant_under_function_conversion(attr)
return MOI.set(model, attr, bridge.constraint, value)
else
throw(ArgumentError("Bridge of type `$(typeof(bridge))` does not support setting the attribute `$attr` because `MOIB.Constraint.invariant_under_function_conversion($attr)` returns `false`."))
end
end

"""
invariant_under_function_conversion(attr::MOI.AbstractConstraintAttribute)

Returns whether the value of the attribute does not change if the constraint
`F`-in-`S` is transformed into a constraint `G`-in-`S` where `F` and `G` are
equivalent representations of the same function. If it returns true, then
subtypes of [`Constraint.AbstractFunctionConversionBridge`](@ref) such as
[`Constraint.ScalarFunctionizeBridge`](@ref) and
[`Constraint.VectorFunctionizeBridge`](@ref) will automatically support
[`MOI.get`](@ref) and [`MOI.set`](@ref) for `attr`.
"""
invariant_under_function_conversion(::MOI.AbstractConstraintAttribute) = false

function invariant_under_function_conversion(::Union{
MOI.ConstraintSet,
MOI.ConstraintBasisStatus,
MOI.ConstraintPrimal,
MOI.ConstraintPrimalStart,
MOI.ConstraintDual,
MOI.ConstraintDualStart})
return true
end

include("functionize.jl")
const ScalarFunctionize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{ScalarFunctionizeBridge{T}, OT}
const VectorFunctionize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorFunctionizeBridge{T}, OT}
# TODO add affine -> quadratic conversion bridge

include("flip_sign.jl")
const GreaterToLess{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{GreaterToLessBridge{T}, OT}
const LessToGreater{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{LessToGreaterBridge{T}, OT}
const NonnegToNonpos{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NonnegToNonposBridge{T}, OT}
const NonposToNonneg{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{NonposToNonnegBridge{T}, OT}
include("vectorize.jl")
const Vectorize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorizeBridge{T}, OT}
include("scalarize.jl")
Expand All @@ -96,10 +39,6 @@ const ScalarSlack{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{ScalarSlackBridg
const VectorSlack{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorSlackBridge{T}, OT}
include("interval.jl")
const SplitInterval{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SplitIntervalBridge{T}, OT}
include("rsoc.jl")
const RSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCBridge{T}, OT}
include("socr.jl")
const SOCR{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCRBridge{T}, OT}
include("quad_to_soc.jl")
const QuadtoSOC{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T}, OT}
include("norm_to_lp.jl")
Expand Down
154 changes: 27 additions & 127 deletions src/Bridges/Constraint/flip_sign.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,86 +3,45 @@

Bridge a `G`-in-`S1` constraint into an `F`-in-`S2` constraint by multiplying
the function by `-1` and taking the point reflection of the set across the
origin. The flipped `F`-in-`S` constraint is stored in the `flipped_constraint`
origin. The flipped `F`-in-`S` constraint is stored in the `constraint`
field by convention.
"""
abstract type FlipSignBridge{
T, S1<:MOI.AbstractSet, S2<:MOI.AbstractSet,
F<:MOI.AbstractFunction, G<:MOI.AbstractFunction} <: AbstractBridge end
F<:MOI.AbstractFunction, G<:MOI.AbstractFunction} <: SetMapBridge{T, S2, S1, F, G} end

function MOI.supports_constraint(
::Type{<:FlipSignBridge{T, S1}}, ::Type{<:MOI.AbstractScalarFunction},
::Type{S1}) where {T, S1<:MOI.AbstractScalarSet}
return true
end
function MOI.supports_constraint(
::Type{<:FlipSignBridge{T, S1}}, ::Type{<:MOI.AbstractVectorFunction},
::Type{S1}) where {T, S1<:MOI.AbstractVectorSet}
return true
end
MOIB.added_constrained_variable_types(::Type{<:FlipSignBridge}) = Tuple{DataType}[]
function MOIB.added_constraint_types(
::Type{<:FlipSignBridge{T, S1, S2, F}}) where {T, S1, S2, F}
return [(F, S2)]
end
map_function(::Type{<:FlipSignBridge{T}}, func) where {T} = MOIU.operate(-, T, func)
# The map is an involution
inverse_map_function(BT::Type{<:FlipSignBridge}, func) = map_function(BT, func)
# The map is symmetric
adjoint_map_function(BT::Type{<:FlipSignBridge}, func) = map_function(BT, func)
# The map is a symmetric involution
inverse_adjoint_map_function(BT::Type{<:FlipSignBridge}, func) = map_function(BT, func)

# Attributes, Bridge acting as a model
function MOI.get(::FlipSignBridge{T, S1, S2, F},
::MOI.NumberOfConstraints{F, S2}) where {T, S1, S2, F}
return 1
end
function MOI.get(bridge::FlipSignBridge{T, S1, S2, F},
::MOI.ListOfConstraintIndices{F, S2}) where {T, S1, S2, F}
return [bridge.flipped_constraint]
end

# References
function MOI.delete(model::MOI.ModelLike, bridge::FlipSignBridge)
MOI.delete(model, bridge.flipped_constraint)
end
function MOI.delete(model::MOI.ModelLike, bridge::FlipSignBridge, i::IndexInVector)
func = MOI.get(model, MOI.ConstraintFunction(), bridge.flipped_constraint)
func = MOI.get(model, MOI.ConstraintFunction(), bridge.constraint)
idx = setdiff(1:MOI.output_dimension(func), i.value)
new_func = MOIU.eachscalar(func)[idx]
set = MOI.get(model, MOI.ConstraintSet(), bridge.flipped_constraint)
set = MOI.get(model, MOI.ConstraintSet(), bridge.constraint)
new_set = MOI.update_dimension(set, MOI.dimension(set) - 1)
MOI.delete(model, bridge.flipped_constraint)
bridge.flipped_constraint = MOI.add_constraint(model, new_func, new_set)
end

# Attributes, Bridge acting as a constraint
function MOI.get(model::MOI.ModelLike,
attr::Union{MOI.ConstraintPrimal, MOI.ConstraintDual},
bridge::FlipSignBridge)
return -MOI.get(model, attr, bridge.flipped_constraint)
end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintFunction,
bridge::FlipSignBridge{T, S1, S2, F, G}) where {T, S1, S2, F, G}
func = MOIU.operate(-, T, MOI.get(model, attr, bridge.flipped_constraint))
return MOIU.convert_approx(G, func)
MOI.delete(model, bridge.constraint)
bridge.constraint = MOI.add_constraint(model, new_func, new_set)
end

function MOI.modify(model::MOI.ModelLike, bridge::FlipSignBridge,
change::MOI.ScalarCoefficientChange)
MOI.modify(
model, bridge.flipped_constraint,
model, bridge.constraint,
MOI.ScalarCoefficientChange(change.variable, -change.new_coefficient))
end

function MOI.modify(model::MOI.ModelLike, bridge::FlipSignBridge,
change::MOI.MultirowChange{T}) where T
new_coefficients = Tuple{Int64, T}[
(index, -coef) for (index, coef) in change.new_coefficients]
MOI.modify(model, bridge.flipped_constraint,
MOI.modify(model, bridge.constraint,
MOI.MultirowChange(change.variable,
new_coefficients))
end
function MOI.modify(model::MOI.ModelLike, bridge::FlipSignBridge,
change::MOI.VectorConstantChange)
MOI.modify(model, bridge.flipped_constraint,
MOI.VectorConstantChange(-change.new_constant))
end

"""
GreaterToLessBridge{T, F<:MOI.AbstractScalarFunction, G<:MOI.AbstractScalarFunction} <:
Expand All @@ -93,34 +52,17 @@ constraint.
"""
struct GreaterToLessBridge{T, F<:MOI.AbstractScalarFunction, G<:MOI.AbstractScalarFunction} <:
FlipSignBridge{T, MOI.GreaterThan{T}, MOI.LessThan{T}, F, G}
flipped_constraint::CI{F, MOI.LessThan{T}}
end
function bridge_constraint(::Type{GreaterToLessBridge{T, F, G}},
model::MOI.ModelLike,
g::MOI.AbstractScalarFunction,
s::MOI.GreaterThan) where {T, F, G}
f = MOIU.operate(-, T, g)
flipped_constraint = MOI.add_constraint(model, f, MOI.LessThan(-s.lower))
return GreaterToLessBridge{T, F, G}(flipped_constraint)
constraint::CI{F, MOI.LessThan{T}}
end
map_set(::Type{<:GreaterToLessBridge}, set::MOI.GreaterThan) = MOI.LessThan(-set.lower)
inverse_map_set(::Type{<:GreaterToLessBridge}, set::MOI.LessThan) = MOI.GreaterThan(-set.upper)
function concrete_bridge_type(::Type{<:GreaterToLessBridge{T}},
G::Type{<:MOI.AbstractScalarFunction},
::Type{MOI.GreaterThan{T}}) where T
F = MOIU.promote_operation(-, T, G)
return GreaterToLessBridge{T, F, G}
end

function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintSet,
bridge::GreaterToLessBridge, new_set::MOI.GreaterThan)
MOI.set(model, attr, bridge.flipped_constraint,
MOI.LessThan(-new_set.lower))
end
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
bridge::GreaterToLessBridge)
set = MOI.get(model, attr, bridge.flipped_constraint)
return MOI.GreaterThan(-set.upper)
end

"""
LessToGreaterBridge{T, F<:MOI.AbstractScalarFunction, G<:MOI.AbstractScalarFunction} <:
FlipSignBridge{T, MOI.LessThan{T}, MOI.GreaterThan{T}, F, G}
Expand All @@ -130,33 +72,17 @@ constraint.
"""
struct LessToGreaterBridge{T, F<:MOI.AbstractScalarFunction, G<:MOI.AbstractScalarFunction} <:
FlipSignBridge{T, MOI.LessThan{T}, MOI.GreaterThan{T}, F, G}
flipped_constraint::CI{F, MOI.GreaterThan{T}}
end
function bridge_constraint(::Type{LessToGreaterBridge{T, F, G}},
model::MOI.ModelLike,
g::MOI.AbstractScalarFunction,
s::MOI.LessThan) where {T, F, G}
f = MOIU.operate(-, T, g)
flipped_constraint = MOI.add_constraint(model, f, MOI.GreaterThan(-s.upper))
return LessToGreaterBridge{T, F, G}(flipped_constraint)
constraint::CI{F, MOI.GreaterThan{T}}
end
map_set(::Type{<:LessToGreaterBridge}, set::MOI.LessThan) = MOI.GreaterThan(-set.upper)
inverse_map_set(::Type{<:LessToGreaterBridge}, set::MOI.GreaterThan) = MOI.LessThan(-set.lower)
function concrete_bridge_type(::Type{<:LessToGreaterBridge{T}},
G::Type{<:MOI.AbstractScalarFunction},
::Type{MOI.LessThan{T}}) where T
F = MOIU.promote_operation(-, T, G)
return LessToGreaterBridge{T, F, G}
end

function MOI.set(model::MOI.ModelLike, attr::MOI.ConstraintSet,
bridge::LessToGreaterBridge, new_set::MOI.LessThan)
MOI.set(model, attr, bridge.flipped_constraint,
MOI.GreaterThan(-new_set.upper))
end
function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
bridge::LessToGreaterBridge)
set = MOI.get(model, attr, bridge.flipped_constraint)
return MOI.LessThan(-set.lower)
end

"""
NonnegToNonposBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <:
Expand All @@ -167,30 +93,17 @@ constraint.
"""
mutable struct NonnegToNonposBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <:
FlipSignBridge{T, MOI.Nonnegatives, MOI.Nonpositives, F, G}
flipped_constraint::CI{F, MOI.Nonpositives}
end
function bridge_constraint(::Type{NonnegToNonposBridge{T, F, G}},
model::MOI.ModelLike,
g::MOI.AbstractVectorFunction,
s::MOI.Nonnegatives) where {T, F, G}
f = MOIU.operate(-, T, g)
flipped_constraint = MOI.add_constraint(model, f,
MOI.Nonpositives(s.dimension))
return NonnegToNonposBridge{T, F, G}(flipped_constraint)
constraint::CI{F, MOI.Nonpositives}
end
map_set(::Type{<:NonnegToNonposBridge}, set::MOI.Nonnegatives) = MOI.Nonpositives(set.dimension)
inverse_map_set(::Type{<:NonnegToNonposBridge}, set::MOI.Nonpositives) = MOI.Nonnegatives(set.dimension)
function concrete_bridge_type(::Type{<:NonnegToNonposBridge{T}},
G::Type{<:MOI.AbstractVectorFunction},
::Type{MOI.Nonnegatives}) where T
F = MOIU.promote_operation(-, T, G)
return NonnegToNonposBridge{T, F, G}
end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
bridge::NonnegToNonposBridge)
set = MOI.get(model, attr, bridge.flipped_constraint)
return MOI.Nonnegatives(MOI.dimension(set))
end

"""
NonposToNonnegBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <:
FlipSignBridge{T, MOI.Nonpositives, MOI.Nonnegatives, F, G}
Expand All @@ -200,26 +113,13 @@ constraint.
"""
mutable struct NonposToNonnegBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <:
FlipSignBridge{T, MOI.Nonpositives, MOI.Nonnegatives, F, G}
flipped_constraint::CI{F, MOI.Nonnegatives}
end
function bridge_constraint(::Type{NonposToNonnegBridge{T, F, G}},
model::MOI.ModelLike,
g::MOI.AbstractVectorFunction,
s::MOI.Nonpositives) where {T, F, G}
f = MOIU.operate(-, T, g)
flipped_constraint = MOI.add_constraint(model, f,
MOI.Nonnegatives(s.dimension))
return NonposToNonnegBridge{T, F, G}(flipped_constraint)
constraint::CI{F, MOI.Nonnegatives}
end
map_set(::Type{<:NonposToNonnegBridge}, set::MOI.Nonpositives) = MOI.Nonnegatives(set.dimension)
inverse_map_set(::Type{<:NonposToNonnegBridge}, set::MOI.Nonnegatives) = MOI.Nonpositives(set.dimension)
function concrete_bridge_type(::Type{<:NonposToNonnegBridge{T}},
G::Type{<:MOI.AbstractVectorFunction},
::Type{MOI.Nonpositives}) where T
F = MOIU.promote_operation(-, T, G)
return NonposToNonnegBridge{T, F, G}
end

function MOI.get(model::MOI.ModelLike, attr::MOI.ConstraintSet,
bridge::NonposToNonnegBridge)
set = MOI.get(model, attr, bridge.flipped_constraint)
return MOI.Nonpositives(MOI.dimension(set))
end
59 changes: 59 additions & 0 deletions src/Bridges/Constraint/function_conversion.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
"""
abstract type AbstractFunctionConversionBridge{F, S} <: AbstractBridge end

Bridge a constraint `G`-in-`S` into a constraint `F`-in-`S` where `F` and `G`
are equivalent representations of the same function. By convention, the
transformed function is stored in the `constraint` field.
"""
abstract type AbstractFunctionConversionBridge{F, S} <: AbstractBridge end

function MOI.get(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute,
bridge::AbstractFunctionConversionBridge)
if invariant_under_function_conversion(attr)
return MOI.get(model, attr, bridge.constraint)
else
throw(ArgumentError("Bridge of type `$(typeof(bridge))` does not support accessing the attribute `$attr` because `MOIB.Constraint.invariant_under_function_conversion($attr)` returns `false`."))
end
end

function MOI.supports(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute,
::Type{<:AbstractFunctionConversionBridge{F, S}}) where {F, S}
return invariant_under_function_conversion(attr) &&
MOI.supports(model, attr, MOI.ConstraintIndex{F, S})
end
function MOI.set(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute,
bridge::AbstractFunctionConversionBridge, value)
if invariant_under_function_conversion(attr)
return MOI.set(model, attr, bridge.constraint, value)
else
throw(ArgumentError("Bridge of type `$(typeof(bridge))` does not support setting the attribute `$attr` because `MOIB.Constraint.invariant_under_function_conversion($attr)` returns `false`."))
end
end

"""
invariant_under_function_conversion(attr::MOI.AbstractConstraintAttribute)

Returns whether the value of the attribute does not change if the constraint
`F`-in-`S` is transformed into a constraint `G`-in-`S` where `F` and `G` are
equivalent representations of the same function. If it returns true, then
subtypes of [`Constraint.AbstractFunctionConversionBridge`](@ref) such as
[`Constraint.ScalarFunctionizeBridge`](@ref) and
[`Constraint.VectorFunctionizeBridge`](@ref) will automatically support
[`MOI.get`](@ref) and [`MOI.set`](@ref) for `attr`.
"""
invariant_under_function_conversion(::MOI.AbstractConstraintAttribute) = false

function invariant_under_function_conversion(::Union{
MOI.ConstraintSet,
MOI.ConstraintBasisStatus,
MOI.ConstraintPrimal,
MOI.ConstraintPrimalStart,
MOI.ConstraintDual,
MOI.ConstraintDualStart})
return true
end

include("functionize.jl")
const ScalarFunctionize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{ScalarFunctionizeBridge{T}, OT}
const VectorFunctionize{T, OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorFunctionizeBridge{T}, OT}
# TODO add affine -> quadratic conversion bridge
Loading