diff --git a/docs/src/submodules/Bridges/overview.md b/docs/src/submodules/Bridges/overview.md index 2de7370646..6ae572967b 100644 --- a/docs/src/submodules/Bridges/overview.md +++ b/docs/src/submodules/Bridges/overview.md @@ -167,10 +167,8 @@ with inner model MOIU.GenericModel{Float64,MOIU.ModelFunctionConstraints{Float64 Then use [`Bridges.add_bridge`](@ref) to add individual bridges: ```jldoctest lazy_bridge_optimizer julia> MOI.Bridges.add_bridge(optimizer, MOI.Bridges.Constraint.SplitIntervalBridge{Float64}) -Dict{Any,DataType} with 0 entries julia> MOI.Bridges.add_bridge(optimizer, MOI.Bridges.Objective.FunctionizeBridge{Float64}) -Dict{Any,DataType} with 0 entries ``` Now the constraints will be bridged only if needed: diff --git a/src/Bridges/Bridges.jl b/src/Bridges/Bridges.jl index bed06cdb8c..5d72a1975a 100644 --- a/src/Bridges/Bridges.jl +++ b/src/Bridges/Bridges.jl @@ -9,11 +9,8 @@ const CI = MOI.ConstraintIndex include("bridge.jl") include("bridge_optimizer.jl") -# Variable bridges include("Variable/Variable.jl") -# Constraint bridges include("Constraint/Constraint.jl") -# Objective bridges include("Objective/Objective.jl") include("lazy_bridge_optimizer.jl") @@ -34,25 +31,33 @@ function full_bridge_optimizer(model::MOI.ModelLike, ::Type{T}) where {T} end print_num_bridges(io::IO, ::Variable.EmptyMap) = nothing + print_num_bridges(io::IO, ::Constraint.EmptyMap) = nothing + print_num_bridges(io::IO, ::Objective.EmptyMap) = nothing + function print_num_bridges(io::IO, B::Variable.Map) s(n) = n == 1 ? "" : "s" indent = " "^get(io, :indent, 0) n = length(B) - return print(io, "\n$(indent)with $(n) variable bridge$(s(n))") + print(io, "\n$(indent)with $(n) variable bridge$(s(n))") + return end + function print_num_bridges(io::IO, B::Constraint.Map) s(n) = n == 1 ? "" : "s" indent = " "^get(io, :indent, 0) n = length(B) - return print(io, "\n$(indent)with $(n) constraint bridge$(s(n))") + print(io, "\n$(indent)with $(n) constraint bridge$(s(n))") + return end + function print_num_bridges(io::IO, B::Objective.Map) s(n) = n == 1 ? "" : "s" indent = " "^get(io, :indent, 0) n = length(B) - return print(io, "\n$(indent)with $(n) objective bridge$(s(n))") + print(io, "\n$(indent)with $(n) objective bridge$(s(n))") + return end function Base.show(io::IO, B::AbstractBridgeOptimizer) @@ -65,6 +70,7 @@ function Base.show(io::IO, B::AbstractBridgeOptimizer) print(io, "\n$(indent)with inner model ") show(IOContext(io, :indent => get(io, :indent, 0) + 2), B.model) end + return end -end # module +end diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index c65e1f3d57..6402342bd5 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -35,59 +35,77 @@ include("set_map.jl") include("vectorize.jl") const Vectorize{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorizeBridge{T},OT} + include("scalarize.jl") const Scalarize{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{ScalarizeBridge{T},OT} + include("slack.jl") const ScalarSlack{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{ScalarSlackBridge{T},OT} const VectorSlack{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorSlackBridge{T},OT} + include("interval.jl") const SplitInterval{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SplitIntervalBridge{T},OT} + include("quad_to_soc.jl") const QuadtoSOC{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{QuadtoSOCBridge{T},OT} + include("soc_to_nonconvex_quad.jl") # do not add these bridges by default const SOCtoNonConvexQuad{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoNonConvexQuadBridge{T},OT} const RSOCtoNonConvexQuad{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoNonConvexQuadBridge{T},OT} + include("norm_to_lp.jl") const NormInfinity{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormInfinityBridge{T},OT} const NormOne{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormOneBridge{T},OT} + include("geomean_to_relentr.jl") const GeoMeantoRelEntr{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeantoRelEntrBridge{T},OT} + include("geomean.jl") const GeoMean{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{GeoMeanBridge{T},OT} + include("relentr_to_exp.jl") const RelativeEntropy{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RelativeEntropyBridge{T},OT} + include("norm_spec_nuc_to_psd.jl") const NormSpectral{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormSpectralBridge{T},OT} const NormNuclear{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{NormNuclearBridge{T},OT} + include("square.jl") const Square{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SquareBridge{T},OT} + include("det.jl") const LogDet{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{LogDetBridge{T},OT} const RootDet{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RootDetBridge{T},OT} + include("soc_to_psd.jl") const SOCtoPSD{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoPSDBridge{T},OT} const RSOCtoPSD{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoPSDBridge{T},OT} + include("indicator_activate_on_zero.jl") +# TODO(odow): should something go here? + include("indicator_sos.jl") const IndicatortoSOS1{T,BC<:MOI.AbstractScalarSet,MaybeBC} = SingleBridgeOptimizer{IndicatorSOS1Bridge{T,BC,MaybeBC}} + include("semi_to_binary.jl") const SemiToBinary{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SemiToBinaryBridge{T},OT} + include("zero_one.jl") const ZeroOne{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{ZeroOneBridge{T},OT} @@ -102,7 +120,6 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T} MOIB.add_bridge(bridged_model, GreaterToIntervalBridge{T}) MOIB.add_bridge(bridged_model, LessToIntervalBridge{T}) end - MOIB.add_bridge(bridged_model, GreaterToLessBridge{T}) MOIB.add_bridge(bridged_model, LessToGreaterBridge{T}) MOIB.add_bridge(bridged_model, NonnegToNonposBridge{T}) diff --git a/src/Bridges/Constraint/bridge.jl b/src/Bridges/Constraint/bridge.jl index f28b94171e..682a0ead9e 100644 --- a/src/Bridges/Constraint/bridge.jl +++ b/src/Bridges/Constraint/bridge.jl @@ -17,8 +17,12 @@ struct IndexInVector end """ - bridge_constraint(BT::Type{<:AbstractBridge}, model::MOI.ModelLike, - func::AbstractFunction, set::MOI.AbstractSet) + bridge_constraint( + BT::Type{<:AbstractBridge}, + model::MOI.ModelLike, + func::AbstractFunction, + set::MOI.AbstractSet, + ) Bridge the constraint `func`-in-`set` using bridge `BT` to `model` and returns a bridge object of type `BT`. The bridge type `BT` should be a concrete type, @@ -43,9 +47,14 @@ The list of variables created by the bridge `b` in the model. MOI.get(::AbstractBridge, ::MOI.ListOfVariableIndices) = MOI.VariableIndex[] """ - MOI.supports_constraint(BT::Type{<:AbstractBridge}, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet})::Bool + MOI.supports_constraint( + BT::Type{<:AbstractBridge}, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + )::Bool -Return a `Bool` indicating whether the bridges of type `BT` support bridging `F`-in-`S` constraints. +Return a `Bool` indicating whether the bridges of type `BT` support bridging +`F`-in-`S` constraints. """ function MOI.supports_constraint( ::Type{<:AbstractBridge}, @@ -56,14 +65,16 @@ function MOI.supports_constraint( end """ - added_constrained_variable_types(BT::Type{<:MOI.Bridges.Constraint.AbstractBridge}, - F::Type{<:MOI.AbstractFunction}, - S::Type{<:MOI.AbstractSet}) + added_constrained_variable_types( + BT::Type{<:MOI.Bridges.Constraint.AbstractBridge}, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + ) Return a list of the types of constrained variables that bridges of type `BT` add for bridging `F`-in-`S` constraints. This falls back to -`added_constrained_variable_types(concrete_bridge_type(BT, F, S))` -so bridges should not implement this method. +`added_constrained_variable_types(concrete_bridge_type(BT, F, S))` so bridges +should not implement this method. """ function MOIB.added_constrained_variable_types( BT::Type{<:AbstractBridge}, @@ -74,9 +85,11 @@ function MOIB.added_constrained_variable_types( end """ - added_constraint_types(BT::Type{<:MOI.Bridges.Constraint.AbstractBridge}, - F::Type{<:MOI.AbstractFunction}, - S::Type{<:MOI.AbstractSet}) + added_constraint_types( + BT::Type{<:MOI.Bridges.Constraint.AbstractBridge}, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + ) Return a list of the types of constraints that bridges of type `BT` add for bridging `F`-in-`S` constraints. This falls back to diff --git a/src/Bridges/Constraint/det.jl b/src/Bridges/Constraint/det.jl index 71eab504d6..3b98160f22 100644 --- a/src/Bridges/Constraint/det.jl +++ b/src/Bridges/Constraint/det.jl @@ -1,3 +1,4 @@ +# TODO(odow): this appears elsewhere function trimap(i::Integer, j::Integer) if i < j trimap(j, i) @@ -7,13 +8,20 @@ function trimap(i::Integer, j::Integer) end """ - extract_eigenvalues(model, f::MOI.VectorAffineFunction{T}, d::Int, offset::Int) where T + extract_eigenvalues( + model, + f::MOI.VectorAffineFunction{T}, + d::Int, + offset::Int + ), where {T} The vector `f` contains `t` (if `offset = 1`) or `(t, u)` (if `offset = 2`) followed by the matrix `X` of dimension `d`. -This functions extracts the eigenvalues of `X` and returns a vector containing `t` or `(t, u)`, -a vector `MOI.VariableIndex` containing the eigenvalues of `X`, -the variables created and the index of the constraint created to extract the eigenvalues. + +This functions extracts the eigenvalues of `X` and returns a vector containing +`t` or `(t, u)`, a vector `MOI.VariableIndex` containing the eigenvalues of `X`, +the variables created and the index of the constraint created to extract the +eigenvalues. """ function extract_eigenvalues( model, @@ -65,9 +73,15 @@ end """ LogDetBridge{T} -The `LogDetConeTriangle` is representable by a `PositiveSemidefiniteConeTriangle` and `ExponentialCone` constraints. -Indeed, ``\\log\\det(X) = \\log(\\delta_1) + \\cdots + \\log(\\delta_n)`` where ``\\delta_1``, ..., ``\\delta_n`` are the eigenvalues of ``X``. -Adapting the method from [1, p. 149], we see that ``t \\le u \\log(\\det(X/u))`` for ``u > 0`` if and only if there exists a lower triangular matrix ``Δ`` such that +The `LogDetConeTriangle` is representable by a +`PositiveSemidefiniteConeTriangle` and `ExponentialCone` constraints. + +Indeed, ``\\log\\det(X) = \\log(\\delta_1) + \\cdots + \\log(\\delta_n)`` where +``\\delta_1``, ..., ``\\delta_n`` are the eigenvalues of ``X``. + +Adapting the method from [1, p. 149], we see that ``t \\le u \\log(\\det(X/u))`` +for ``u > 0`` if and only if there exists a lower triangular matrix ``Δ`` such +that ```math \\begin{align*} \\begin{pmatrix} @@ -78,7 +92,9 @@ Adapting the method from [1, p. 149], we see that ``t \\le u \\log(\\det(X/u))`` \\end{align*} ``` -[1] Ben-Tal, Aharon, and Arkadi Nemirovski. *Lectures on modern convex optimization: analysis, algorithms, and engineering applications*. Society for Industrial and Applied Mathematics, 2001. +[1] Ben-Tal, Aharon, and Arkadi Nemirovski. *Lectures on modern convex + optimization: analysis, algorithms, and engineering applications*. Society + for Industrial and Applied Mathematics, 2001. ``` """ struct LogDetBridge{T} <: AbstractBridge @@ -91,6 +107,7 @@ struct LogDetBridge{T} <: AbstractBridge lcindex::Vector{CI{MOI.VectorAffineFunction{T},MOI.ExponentialCone}} tlindex::CI{MOI.ScalarAffineFunction{T},MOI.LessThan{T}} end + function bridge_constraint( ::Type{LogDetBridge{T}}, model, @@ -104,6 +121,7 @@ function bridge_constraint( s, ) end + function bridge_constraint( ::Type{LogDetBridge{T}}, model, @@ -126,9 +144,11 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:LogDetBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types(::Type{LogDetBridge{T}}) where {T} return [ (MOI.VectorAffineFunction{T}, MOI.PositiveSemidefiniteConeTriangle), @@ -138,7 +158,13 @@ function MOIB.added_constraint_types(::Type{LogDetBridge{T}}) where {T} end """ - sublog(model, x::MOI.VariableIndex, y::MOI.VariableIndex, z::MOI.VariableIndex, ::Type{T}) where T + sublog( + model, + x::MOI.VariableIndex, + y::MOI.VariableIndex, + z::MOI.VariableIndex, + ::Type{T}, + ) where {T} Constrains ``x \\le y \\log(z/y)`` and returns the constraint index. """ @@ -157,9 +183,15 @@ function sublog( end """ - subsum(model, t::MOI.ScalarAffineFunction, l::Vector{MOI.VariableIndex}, ::Type{T}) where T + subsum( + model, + t::MOI.ScalarAffineFunction, + l::Vector{MOI.VariableIndex}, + ::Type{T} + ) where {T} -Constrains ``t \\le l_1 + \\cdots + l_n`` where `n` is the length of `l` and returns the constraint index. +Constrains ``t \\le l_1 + \\cdots + l_n`` where `n` is the length of `l` and +returns the constraint index. """ function subsum( model, @@ -179,9 +211,11 @@ end # Attributes, Bridge acting as a model MOI.get(b::LogDetBridge, ::MOI.NumberOfVariables) = length(b.Δ) + length(b.l) + MOI.get(b::LogDetBridge, ::MOI.ListOfVariableIndices) = [b.Δ; b.l] + function MOI.get( - b::LogDetBridge{T}, + ::LogDetBridge{T}, ::MOI.NumberOfConstraints{ MOI.VectorAffineFunction{T}, MOI.PositiveSemidefiniteConeTriangle, @@ -189,18 +223,21 @@ function MOI.get( ) where {T} return 1 end + function MOI.get( b::LogDetBridge{T}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T},MOI.ExponentialCone}, ) where {T} return length(b.lcindex) end + function MOI.get( - b::LogDetBridge{T}, + ::LogDetBridge{T}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T},MOI.LessThan{T}}, ) where {T} return 1 end + function MOI.get( b::LogDetBridge{T}, ::MOI.ListOfConstraintIndices{ @@ -210,6 +247,7 @@ function MOI.get( ) where {T} return [b.sdindex] end + function MOI.get( b::LogDetBridge{T}, ::MOI.ListOfConstraintIndices{ @@ -219,6 +257,7 @@ function MOI.get( ) where {T} return b.lcindex end + function MOI.get( b::LogDetBridge{T}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T},MOI.LessThan{T}}, @@ -232,7 +271,8 @@ function MOI.delete(model::MOI.ModelLike, bridge::LogDetBridge) MOI.delete(model, bridge.lcindex) MOI.delete(model, bridge.sdindex) MOI.delete(model, bridge.l) - return MOI.delete(model, bridge.Δ) + MOI.delete(model, bridge.Δ) + return end # Attributes, Bridge acting as a constraint @@ -250,6 +290,7 @@ function MOI.get( x = MOI.get(model, attr, bridge.sdindex)[1:length(bridge.Δ)] return vcat(t, u, x) end + # [X Δ; Δ' Diag(Δ)] in PSD # t - sum(l) >= 0 # (l_i, u, Δ_ii) in Exp @@ -285,8 +326,12 @@ end """ RootDetBridge{T} -The `RootDetConeTriangle` is representable by a `PositiveSemidefiniteConeTriangle` and an `GeometricMeanCone` constraints; see [1, p. 149]. -Indeed, ``t \\le \\det(X)^{1/n}`` if and only if there exists a lower triangular matrix ``Δ`` such that +The `RootDetConeTriangle` is representable by a +`PositiveSemidefiniteConeTriangle` and an `GeometricMeanCone` constraints; see +[1, p. 149]. + +Indeed, ``t \\le \\det(X)^{1/n}`` if and only if there exists a lower triangular +matrix ``Δ`` such that: ```math \\begin{align*} \\begin{pmatrix} @@ -297,7 +342,9 @@ Indeed, ``t \\le \\det(X)^{1/n}`` if and only if there exists a lower triangular \\end{align*} ``` -[1] Ben-Tal, Aharon, and Arkadi Nemirovski. *Lectures on modern convex optimization: analysis, algorithms, and engineering applications*. Society for Industrial and Applied Mathematics, 2001. +[1] Ben-Tal, Aharon, and Arkadi Nemirovski. *Lectures on modern convex + optimization: analysis, algorithms, and engineering applications*. Society + for Industrial and Applied Mathematics, 2001. """ struct RootDetBridge{T} <: AbstractBridge Δ::Vector{MOI.VariableIndex} @@ -307,6 +354,7 @@ struct RootDetBridge{T} <: AbstractBridge } gmindex::CI{MOI.VectorAffineFunction{T},MOI.GeometricMeanCone} end + function bridge_constraint( ::Type{RootDetBridge{T}}, model, @@ -320,6 +368,7 @@ function bridge_constraint( s, ) end + function bridge_constraint( ::Type{RootDetBridge{T}}, model, @@ -335,7 +384,6 @@ function bridge_constraint( MOIU.operate(vcat, T, t, DF), MOI.GeometricMeanCone(d + 1), ) - return RootDetBridge(Δ, sdindex, gmindex) end @@ -346,9 +394,11 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:RootDetBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types(::Type{RootDetBridge{T}}) where {T} return [ (MOI.VectorAffineFunction{T}, MOI.PositiveSemidefiniteConeTriangle), @@ -358,9 +408,11 @@ end # Attributes, Bridge acting as a model MOI.get(b::RootDetBridge, ::MOI.NumberOfVariables) = length(b.Δ) + MOI.get(b::RootDetBridge, ::MOI.ListOfVariableIndices) = b.Δ + function MOI.get( - b::RootDetBridge{T}, + ::RootDetBridge{T}, ::MOI.NumberOfConstraints{ MOI.VectorAffineFunction{T}, MOI.PositiveSemidefiniteConeTriangle, @@ -368,8 +420,9 @@ function MOI.get( ) where {T} return 1 end + function MOI.get( - b::RootDetBridge{T}, + ::RootDetBridge{T}, ::MOI.NumberOfConstraints{ MOI.VectorAffineFunction{T}, MOI.GeometricMeanCone, @@ -377,6 +430,7 @@ function MOI.get( ) where {T} return 1 end + function MOI.get( b::RootDetBridge{T}, ::MOI.ListOfConstraintIndices{ @@ -386,6 +440,7 @@ function MOI.get( ) where {T} return [b.sdindex] end + function MOI.get( b::RootDetBridge{T}, ::MOI.ListOfConstraintIndices{ @@ -400,7 +455,8 @@ end function MOI.delete(model::MOI.ModelLike, bridge::RootDetBridge) MOI.delete(model, bridge.gmindex) MOI.delete(model, bridge.sdindex) - return MOI.delete(model, bridge.Δ) + MOI.delete(model, bridge.Δ) + return end # Attributes, Bridge acting as a constraint @@ -413,6 +469,7 @@ function MOI.get( x = MOI.get(model, attr, bridge.sdindex)[1:length(bridge.Δ)] return vcat(t, x) end + # (t, x) in RootDet <=> exists Δ such that At + Bx + CΔ in (PSD, GeoMean) # so RootDet* = [A'; B'] (PSD, GeoMean)* # and 0 = [C'] (PSD, GeoMean)* diff --git a/src/Bridges/Constraint/flip_sign.jl b/src/Bridges/Constraint/flip_sign.jl index 4b95cb5cd7..bdfcd94f34 100644 --- a/src/Bridges/Constraint/flip_sign.jl +++ b/src/Bridges/Constraint/flip_sign.jl @@ -17,10 +17,13 @@ abstract type FlipSignBridge{ function map_function(::Type{<:FlipSignBridge{T}}, func) where {T} return MOIU.operate(-, T, func) end + # 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 function inverse_adjoint_map_function(BT::Type{<:FlipSignBridge}, func) return map_function(BT, func) @@ -37,7 +40,8 @@ function MOI.delete( set = MOI.get(model, MOI.ConstraintSet(), bridge.constraint) new_set = MOI.update_dimension(set, MOI.dimension(set) - 1) MOI.delete(model, bridge.constraint) - return bridge.constraint = MOI.add_constraint(model, new_func, new_set) + bridge.constraint = MOI.add_constraint(model, new_func, new_set) + return end function MOI.modify( @@ -45,12 +49,14 @@ function MOI.modify( bridge::FlipSignBridge, change::MOI.ScalarCoefficientChange, ) - return MOI.modify( + MOI.modify( model, bridge.constraint, MOI.ScalarCoefficientChange(change.variable, -change.new_coefficient), ) + return end + function MOI.modify( model::MOI.ModelLike, bridge::FlipSignBridge, @@ -59,16 +65,20 @@ function MOI.modify( new_coefficients = Tuple{Int64,T}[ (index, -coef) for (index, coef) in change.new_coefficients ] - return MOI.modify( + MOI.modify( model, bridge.constraint, MOI.MultirowChange(change.variable, new_coefficients), ) + return end """ - GreaterToLessBridge{T, F<:MOI.AbstractScalarFunction, G<:MOI.AbstractScalarFunction} <: - FlipSignBridge{T, MOI.GreaterThan{T}, MOI.LessThan{T}, F, G} + GreaterToLessBridge{ + T, + F<:MOI.AbstractScalarFunction, + G<:MOI.AbstractScalarFunction + } <: FlipSignBridge{T, MOI.GreaterThan{T}, MOI.LessThan{T}, F, G} Transforms a `G`-in-`GreaterThan{T}` constraint into an `F`-in-`LessThan{T}` constraint. @@ -80,12 +90,15 @@ struct GreaterToLessBridge{ } <: FlipSignBridge{T,MOI.GreaterThan{T},MOI.LessThan{T},F,G} constraint::CI{F,MOI.LessThan{T}} end + function map_set(::Type{<:GreaterToLessBridge}, set::MOI.GreaterThan) return MOI.LessThan(-set.lower) end + function inverse_map_set(::Type{<:GreaterToLessBridge}, set::MOI.LessThan) return MOI.GreaterThan(-set.upper) end + function concrete_bridge_type( ::Type{<:GreaterToLessBridge{T}}, G::Type{<:MOI.AbstractScalarFunction}, @@ -96,8 +109,11 @@ function concrete_bridge_type( end """ - LessToGreaterBridge{T, F<:MOI.AbstractScalarFunction, G<:MOI.AbstractScalarFunction} <: - FlipSignBridge{T, MOI.LessThan{T}, MOI.GreaterThan{T}, F, G} + LessToGreaterBridge{ + T, + F<:MOI.AbstractScalarFunction, + G<:MOI.AbstractScalarFunction + } <: FlipSignBridge{T, MOI.LessThan{T}, MOI.GreaterThan{T}, F, G} Transforms a `G`-in-`LessThan{T}` constraint into an `F`-in-`GreaterThan{T}` constraint. @@ -109,12 +125,15 @@ struct LessToGreaterBridge{ } <: FlipSignBridge{T,MOI.LessThan{T},MOI.GreaterThan{T},F,G} constraint::CI{F,MOI.GreaterThan{T}} end + function map_set(::Type{<:LessToGreaterBridge}, set::MOI.LessThan) return MOI.GreaterThan(-set.upper) end + function inverse_map_set(::Type{<:LessToGreaterBridge}, set::MOI.GreaterThan) return MOI.LessThan(-set.lower) end + function concrete_bridge_type( ::Type{<:LessToGreaterBridge{T}}, G::Type{<:MOI.AbstractScalarFunction}, @@ -125,8 +144,11 @@ function concrete_bridge_type( end """ - NonnegToNonposBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <: - FlipSignBridge{T, MOI.Nonnegatives, MOI.Nonpositives, F, G} + NonnegToNonposBridge{ + T, + F<:MOI.AbstractVectorFunction, + G<:MOI.AbstractVectorFunction + } <: FlipSignBridge{T, MOI.Nonnegatives, MOI.Nonpositives, F, G} Transforms a `G`-in-`Nonnegatives` constraint into a `F`-in-`Nonpositives` constraint. @@ -138,12 +160,15 @@ mutable struct NonnegToNonposBridge{ } <: FlipSignBridge{T,MOI.Nonnegatives,MOI.Nonpositives,F,G} constraint::CI{F,MOI.Nonpositives} end + function map_set(::Type{<:NonnegToNonposBridge}, set::MOI.Nonnegatives) return MOI.Nonpositives(set.dimension) end + function inverse_map_set(::Type{<:NonnegToNonposBridge}, set::MOI.Nonpositives) return MOI.Nonnegatives(set.dimension) end + function concrete_bridge_type( ::Type{<:NonnegToNonposBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, @@ -154,8 +179,11 @@ function concrete_bridge_type( end """ - NonposToNonnegBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <: - FlipSignBridge{T, MOI.Nonpositives, MOI.Nonnegatives, F, G} + NonposToNonnegBridge{ + T, + F<:MOI.AbstractVectorFunction, + G<:MOI.AbstractVectorFunction, + } <: FlipSignBridge{T, MOI.Nonpositives, MOI.Nonnegatives, F, G} Transforms a `G`-in-`Nonpositives` constraint into a `F`-in-`Nonnegatives` constraint. @@ -167,12 +195,15 @@ mutable struct NonposToNonnegBridge{ } <: FlipSignBridge{T,MOI.Nonpositives,MOI.Nonnegatives,F,G} constraint::CI{F,MOI.Nonnegatives} end + function map_set(::Type{<:NonposToNonnegBridge}, set::MOI.Nonpositives) return MOI.Nonnegatives(set.dimension) end + function inverse_map_set(::Type{<:NonposToNonnegBridge}, set::MOI.Nonnegatives) return MOI.Nonpositives(set.dimension) end + function concrete_bridge_type( ::Type{<:NonposToNonnegBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, diff --git a/src/Bridges/Constraint/function_conversion.jl b/src/Bridges/Constraint/function_conversion.jl index f04f87a33e..184c9bccc7 100644 --- a/src/Bridges/Constraint/function_conversion.jl +++ b/src/Bridges/Constraint/function_conversion.jl @@ -31,21 +31,22 @@ function MOI.supports( 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 + if !invariant_under_function_conversion(attr) 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 + MOI.set(model, attr, bridge.constraint, value) + return end """ diff --git a/src/Bridges/Constraint/functionize.jl b/src/Bridges/Constraint/functionize.jl index 6b35d590a9..dcd5d6c6c4 100644 --- a/src/Bridges/Constraint/functionize.jl +++ b/src/Bridges/Constraint/functionize.jl @@ -10,6 +10,7 @@ struct ScalarFunctionizeBridge{T,S} <: AbstractFunctionConversionBridge{MOI.ScalarAffineFunction{T},S} constraint::CI{MOI.ScalarAffineFunction{T},S} end + function bridge_constraint( ::Type{ScalarFunctionizeBridge{T,S}}, model, @@ -28,16 +29,19 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types( ::Type{<:ScalarFunctionizeBridge}, ) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{ScalarFunctionizeBridge{T,S}}, ) where {T,S} return [(MOI.ScalarAffineFunction{T}, S)] end + function concrete_bridge_type( ::Type{<:ScalarFunctionizeBridge{T}}, ::Type{MOI.SingleVariable}, @@ -48,11 +52,12 @@ end # Attributes, Bridge acting as a model function MOI.get( - b::ScalarFunctionizeBridge{T,S}, + ::ScalarFunctionizeBridge{T,S}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T},S}, ) where {T,S} return 1 end + function MOI.get( b::ScalarFunctionizeBridge{T,S}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T},S}, @@ -73,13 +78,15 @@ function MOI.set( c::ScalarFunctionizeBridge{T}, f::MOI.SingleVariable, ) where {T} - return MOI.set( + MOI.set( model, MOI.ConstraintFunction(), c.constraint, MOI.ScalarAffineFunction{T}(f), ) + return end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintFunction, @@ -101,6 +108,7 @@ mutable struct VectorFunctionizeBridge{T,S} <: AbstractFunctionConversionBridge{MOI.VectorAffineFunction{T},S} constraint::CI{MOI.VectorAffineFunction{T},S} end + function bridge_constraint( ::Type{VectorFunctionizeBridge{T,S}}, model, @@ -118,16 +126,19 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types( ::Type{<:VectorFunctionizeBridge}, ) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{VectorFunctionizeBridge{T,S}}, ) where {T,S} return [(MOI.VectorAffineFunction{T}, S)] end + function concrete_bridge_type( ::Type{<:VectorFunctionizeBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, @@ -138,11 +149,12 @@ end # Attributes, Bridge acting as a model function MOI.get( - b::VectorFunctionizeBridge{T,S}, + ::VectorFunctionizeBridge{T,S}, ::MOI.NumberOfConstraints{MOI.VectorAffineFunction{T},S}, ) where {T,S} return 1 end + function MOI.get( b::VectorFunctionizeBridge{T,S}, ::MOI.ListOfConstraintIndices{MOI.VectorAffineFunction{T},S}, @@ -155,6 +167,7 @@ function MOI.delete(model::MOI.ModelLike, bridge::VectorFunctionizeBridge) MOI.delete(model, bridge.constraint) return end + function MOI.delete( model::MOI.ModelLike, bridge::VectorFunctionizeBridge, @@ -166,7 +179,8 @@ function MOI.delete( set = MOI.get(model, MOI.ConstraintSet(), bridge.constraint) new_set = MOI.update_dimension(set, MOI.dimension(set) - 1) MOI.delete(model, bridge.constraint) - return bridge.constraint = MOI.add_constraint(model, new_func, new_set) + bridge.constraint = MOI.add_constraint(model, new_func, new_set) + return end # Constraints @@ -176,13 +190,15 @@ function MOI.set( bridge::VectorFunctionizeBridge{T}, func::MOI.VectorOfVariables, ) where {T} - return MOI.set( + MOI.set( model, MOI.ConstraintFunction(), bridge.constraint, MOI.VectorAffineFunction{T}(func), ) + return end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintFunction, diff --git a/src/Bridges/Constraint/geomean.jl b/src/Bridges/Constraint/geomean.jl index 1b60810697..47f35d4d97 100644 --- a/src/Bridges/Constraint/geomean.jl +++ b/src/Bridges/Constraint/geomean.jl @@ -5,6 +5,7 @@ function _ilog2(n, i) _ilog2(n, i + 1) end end + function ilog2(n::Integer) @assert n > zero(n) return _ilog2(n, zero(n)) @@ -14,20 +15,23 @@ end GeoMeanBridge{T, F, G, H} The `GeometricMeanCone` is `SecondOrderCone` representable; see [1, p. 105]. + The reformulation is best described in an example. -Consider the cone of dimension 4 + +Consider the cone of dimension 4: ```math t \\le \\sqrt[3]{x_1 x_2 x_3} ``` -This can be rewritten as ``\\exists x_{21} \\ge 0`` such that +This can be rewritten as ``\\exists x_{21} \\ge 0`` such that: ```math \\begin{align*} t & \\le x_{21},\\\\ x_{21}^4 & \\le x_1 x_2 x_3 x_{21}. \\end{align*} ``` -Note that we need to create ``x_{21}`` and not use ``t^4`` directly as ``t`` is allowed to be negative. -Now, this is equivalent to + +Note that we need to create ``x_{21}`` and not use ``t^4`` directly as ``t`` is +allowed to be negative. Now, this is equivalent to: ```math \\begin{align*} t & \\le x_{21}/\\sqrt{4},\\\\ @@ -36,7 +40,9 @@ Now, this is equivalent to \\end{align*} ``` -[1] Ben-Tal, Aharon, and Arkadi Nemirovski. *Lectures on modern convex optimization: analysis, algorithms, and engineering applications*. Society for Industrial and Applied Mathematics, 2001. +[1] Ben-Tal, Aharon, and Arkadi Nemirovski. *Lectures on modern convex + optimization: analysis, algorithms, and engineering applications*. Society + for Industrial and Applied Mathematics, 2001. """ struct GeoMeanBridge{T,F,G,H} <: AbstractBridge # Initially, (t, x) is of dimension d so x is dimension (d-1) @@ -50,6 +56,7 @@ struct GeoMeanBridge{T,F,G,H} <: AbstractBridge socrc::Vector{CI{G,MOI.RotatedSecondOrderCone}} nonneg::Union{Nothing,CI{H,MOI.Nonnegatives}} end + function bridge_constraint( ::Type{GeoMeanBridge{T,F,G,H}}, model, @@ -133,9 +140,11 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:GeoMeanBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{<:GeoMeanBridge{T,F,G}}, ) where {T,F,G} @@ -145,6 +154,7 @@ function MOIB.added_constraint_types( (G, MOI.Nonnegatives), ] end + function concrete_bridge_type( ::Type{<:GeoMeanBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, @@ -159,37 +169,44 @@ end # Attributes, Bridge acting as a model MOI.get(b::GeoMeanBridge, ::MOI.NumberOfVariables) = length(b.xij) + MOI.get(b::GeoMeanBridge, ::MOI.ListOfVariableIndices) = b.xij + function MOI.get( - b::GeoMeanBridge{T,F}, + ::GeoMeanBridge{T,F}, ::MOI.NumberOfConstraints{F,MOI.LessThan{T}}, ) where {T,F} return 1 # t ≤ x_{l1}/sqrt(N) end + function MOI.get( b::GeoMeanBridge{T,F,G}, ::MOI.NumberOfConstraints{G,MOI.RotatedSecondOrderCone}, ) where {T,F,G} return length(b.socrc) end + function MOI.get( b::GeoMeanBridge{T,F,G}, ::MOI.NumberOfConstraints{G,MOI.Nonnegatives}, ) where {T,F,G} return (b.d > 2 ? 0 : 1) end + function MOI.get( b::GeoMeanBridge{T,F}, ::MOI.ListOfConstraintIndices{F,MOI.LessThan{T}}, ) where {T,F} return [b.tubc] end + function MOI.get( b::GeoMeanBridge{T,F,G}, ::MOI.ListOfConstraintIndices{G,MOI.RotatedSecondOrderCone}, ) where {T,F,G} return b.socrc end + function MOI.get( b::GeoMeanBridge{T,F,G,H}, ::MOI.ListOfConstraintIndices{H,MOI.Nonnegatives}, @@ -205,12 +222,14 @@ function MOI.delete(model::MOI.ModelLike, bridge::GeoMeanBridge) if bridge.d == 2 MOI.delete(model, bridge.nonneg) end + return end # Attributes, Bridge acting as a constraint function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet, bridge::GeoMeanBridge) return MOI.GeometricMeanCone(bridge.d) end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintFunction, @@ -245,6 +264,7 @@ function MOI.get( end return MOIU.vectorize(f_scalars) end + function _getconstrattr(model, attr, bridge::GeoMeanBridge{T}) where {T} output = Vector{T}(undef, bridge.d) output[1] = MOI.get(model, attr, bridge.tubc) @@ -262,6 +282,7 @@ function _getconstrattr(model, attr, bridge::GeoMeanBridge{T}) where {T} end return output end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintPrimal, @@ -278,6 +299,7 @@ function MOI.get( end return output end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintDual, diff --git a/src/Bridges/Constraint/geomean_to_relentr.jl b/src/Bridges/Constraint/geomean_to_relentr.jl index b3c642598a..3ed6b5a72a 100644 --- a/src/Bridges/Constraint/geomean_to_relentr.jl +++ b/src/Bridges/Constraint/geomean_to_relentr.jl @@ -1,20 +1,26 @@ """ GeoMeantoRelEntrBridge{T} -The `geometric mean cone` is representable with a relative entropy constraint and a -nonnegative auxiliary variable, since ``u \\le \\prod_{i=1}^n w_i^{1/n}`` is equivalent -to ``y \\ge 0`` and ``0 \\le u + y \\le \\prod_{i=1}^n w_i^{1/n}``, and the latter -inequality is equivalent to ``1 \\le \\prod_{i=1}^n (\\frac{w_i}{u + y})^{1/n}``, which -is equivalent to ``0 \\le \\sum_{i=1}^n \\log (\\frac{w_i}{u + y})^{1/n}``, which is -equivalent to ``0 \\ge \\sum_{i=1}^n (u + y) \\log (\\frac{u + y}{w_i})``. Thus -``(u, w) \\in GeometricMeanCone(1 + n)`` is representable as ``y \\ge 0``, -``(0, w, (u + y) e) \\in RelativeEntropyCone(1 + 2n)``, where ``e`` is a vector of ones. +The `geometric mean cone` is representable with a relative entropy constraint +and a nonnegative auxiliary variable. + +This is because ``u \\le \\prod_{i=1}^n w_i^{1/n}`` is equivalent to +``y \\ge 0`` and ``0 \\le u + y \\le \\prod_{i=1}^n w_i^{1/n}``, +and the latter inequality is equivalent to +``1 \\le \\prod_{i=1}^n (\\frac{w_i}{u + y})^{1/n}``, which is equivalent to +``0 \\le \\sum_{i=1}^n \\log (\\frac{w_i}{u + y})^{1/n}``, which is +equivalent to ``0 \\ge \\sum_{i=1}^n (u + y) \\log (\\frac{u + y}{w_i})``. + +Thus ``(u, w) \\in GeometricMeanCone(1 + n)`` is representable as ``y \\ge 0``, +``(0, w, (u + y) e) \\in RelativeEntropyCone(1 + 2n)``, where ``e`` is a vector +of ones. """ struct GeoMeantoRelEntrBridge{T,F,G,H} <: AbstractBridge y::MOI.VariableIndex nn_index::CI{F,MOI.Nonnegatives} # for y >= 0 relentr_index::CI{G,MOI.RelativeEntropyCone} end + function bridge_constraint( ::Type{GeoMeantoRelEntrBridge{T,F,G,H}}, model::MOI.ModelLike, @@ -51,14 +57,17 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:GeoMeantoRelEntrBridge}) return [(MOI.Nonnegatives,)] end + function MOIB.added_constraint_types( ::Type{<:GeoMeantoRelEntrBridge{T,F,G}}, ) where {T,F,G} return [(G, MOI.RelativeEntropyCone)] end + function concrete_bridge_type( ::Type{<:GeoMeantoRelEntrBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, @@ -78,27 +87,32 @@ end # Attributes, Bridge acting as a model MOI.get(bridge::GeoMeantoRelEntrBridge, ::MOI.NumberOfVariables) = 1 + function MOI.get(bridge::GeoMeantoRelEntrBridge, ::MOI.ListOfVariableIndices) return [bridge.y] end + function MOI.get( - bridge::GeoMeantoRelEntrBridge{T,F}, + ::GeoMeantoRelEntrBridge{T,F}, ::MOI.NumberOfConstraints{F,MOI.Nonnegatives}, ) where {T,F} return 1 end + function MOI.get( - bridge::GeoMeantoRelEntrBridge{T,F,G}, + ::GeoMeantoRelEntrBridge{T,F,G}, ::MOI.NumberOfConstraints{G,MOI.RelativeEntropyCone}, ) where {T,F,G} return 1 end + function MOI.get( bridge::GeoMeantoRelEntrBridge{T,F}, ::MOI.ListOfConstraintIndices{F,MOI.Nonnegatives}, ) where {T,F} return [bridge.nn_index] end + function MOI.get( bridge::GeoMeantoRelEntrBridge{T,F,G}, ::MOI.ListOfConstraintIndices{G,MOI.RelativeEntropyCone}, @@ -110,7 +124,8 @@ end function MOI.delete(model::MOI.ModelLike, bridge::GeoMeantoRelEntrBridge) MOI.delete(model, bridge.relentr_index) MOI.delete(model, bridge.nn_index) - return MOI.delete(model, bridge.y) + MOI.delete(model, bridge.y) + return end # Attributes, Bridge acting as a constraint @@ -130,6 +145,7 @@ function MOI.get( w_func = relentr_func[2:(1+d)] return MOIU.convert_approx(H, MOIU.operate(vcat, T, u_func, w_func)) end + function MOI.get( model::MOI.ModelLike, ::MOI.ConstraintSet, @@ -144,6 +160,7 @@ function MOI.get( ), ) end + function MOI.supports( ::MOI.ModelLike, ::Union{MOI.ConstraintPrimalStart,MOI.ConstraintDualStart}, @@ -151,6 +168,7 @@ function MOI.supports( ) return true end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, @@ -162,6 +180,7 @@ function MOI.get( u_val = sum(relentr_primal[(2+d):end]) / d - y_val return vcat(u_val, relentr_primal[2:(1+d)]) end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, @@ -181,9 +200,10 @@ function MOI.set( ) return end -# Given a is dual on y >= 0 and (b, c, d) is dual on RelativeEntropyCone constraint, -# dual on (u, w) in GeometricMeanCone is (-a, c). Note that sum(d) = -a, so we could -# instead use (sum(d), c). + +# Given a is dual on y >= 0 and (b, c, d) is dual on RelativeEntropyCone +# constraint, dual on (u, w) in GeometricMeanCone is (-a, c). Note that +# sum(d) = -a, so we could instead use (sum(d), c). function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart}, @@ -195,6 +215,7 @@ function MOI.get( w_dual = relentr_dual[2:(d+1)] return vcat(u_dual, w_dual) end + # Given GeometricMeanCone constraint dual start of (u, w), constraint dual on y >= 0 is -u # and on RelativeEntropyCone constraint is (-u/n, w, u/n * (log.(w/geomean(w)) .+ 1)). # Note log.(w/geomean(w)) = log.(w) .- sum(log, w) / n. diff --git a/src/Bridges/Constraint/indicator_activate_on_zero.jl b/src/Bridges/Constraint/indicator_activate_on_zero.jl index 62e97785d2..f787e1f86b 100644 --- a/src/Bridges/Constraint/indicator_activate_on_zero.jl +++ b/src/Bridges/Constraint/indicator_activate_on_zero.jl @@ -63,6 +63,7 @@ function MOIB.added_constrained_variable_types( ) return [(MOI.ZeroOne,)] end + function MOIB.added_constraint_types( ::Type{IndicatorActiveOnFalseBridge{T,F,S}}, ) where {T,F,S} diff --git a/src/Bridges/Constraint/indicator_sos.jl b/src/Bridges/Constraint/indicator_sos.jl index f582a7b043..2f74789e7a 100644 --- a/src/Bridges/Constraint/indicator_sos.jl +++ b/src/Bridges/Constraint/indicator_sos.jl @@ -84,8 +84,8 @@ function MOI.get( end function MOI.get( - model::MOI.ModelLike, - attr::MOI.ConstraintFunction, + ::MOI.ModelLike, + ::MOI.ConstraintFunction, b::IndicatorSOS1Bridge{T}, ) where {T} z = b.z_variable @@ -261,10 +261,6 @@ function MOI.set( MOI.set(model, MOI.VariablePrimalStart(), bridge.z_variable, zvalue) wstart = MOI.get(model, MOI.VariablePrimalStart(), bridge.w_variable) wstart = wstart === nothing ? zero(T) : wstart - return MOI.set( - model, - attr, - bridge.linear_constraint_index, - lin_start + wstart, - ) + MOI.set(model, attr, bridge.linear_constraint_index, lin_start + wstart) + return end diff --git a/src/Bridges/Constraint/interval.jl b/src/Bridges/Constraint/interval.jl index 29e99313f4..402be7ad43 100644 --- a/src/Bridges/Constraint/interval.jl +++ b/src/Bridges/Constraint/interval.jl @@ -28,6 +28,7 @@ struct SplitIntervalBridge{ lower::CI{F,LS} upper::CI{F,US} end + function bridge_constraint( ::Type{SplitIntervalBridge{T,F,S,LS,US}}, model::MOI.ModelLike, @@ -46,6 +47,7 @@ function MOI.supports_constraint( ) where {T} return true end + function MOI.supports_constraint( ::Type{SplitIntervalBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, @@ -53,14 +55,17 @@ function MOI.supports_constraint( ) where {T} return MOIU.is_coefficient_type(F, T) end + function MOIB.added_constrained_variable_types(::Type{<:SplitIntervalBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{SplitIntervalBridge{T,F,S,LS,US}}, ) where {T,F,S,LS,US} return [(F, LS), (F, US)] end + function concrete_bridge_type( ::Type{<:SplitIntervalBridge}, F::Type{<:MOI.AbstractScalarFunction}, @@ -68,6 +73,7 @@ function concrete_bridge_type( ) where {T} return SplitIntervalBridge{T,F,S,MOI.GreaterThan{T},MOI.LessThan{T}} end + function concrete_bridge_type( ::Type{<:SplitIntervalBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, @@ -83,18 +89,21 @@ function MOI.get( ) where {T,F,S,LS} return 1 end + function MOI.get( ::SplitIntervalBridge{T,F,S,LS,US}, ::MOI.NumberOfConstraints{F,US}, ) where {T,F,S,LS,US} return 1 end + function MOI.get( bridge::SplitIntervalBridge{T,F,S,LS}, ::MOI.ListOfConstraintIndices{F,LS}, ) where {T,F,S,LS} return [bridge.lower] end + function MOI.get( bridge::SplitIntervalBridge{T,F,S,LS,US}, ::MOI.ListOfConstraintIndices{F,US}, @@ -105,7 +114,8 @@ end # Indices function MOI.delete(model::MOI.ModelLike, bridge::SplitIntervalBridge) MOI.delete(model, bridge.lower) - return MOI.delete(model, bridge.upper) + MOI.delete(model, bridge.upper) + return end # Attributes, Bridge acting as a constraint @@ -116,6 +126,7 @@ function MOI.supports( ) return true end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, @@ -124,6 +135,7 @@ function MOI.get( # lower and upper should give the same value return MOI.get(model, attr, bridge.lower) end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, @@ -131,7 +143,8 @@ function MOI.set( value, ) MOI.set(model, attr, bridge.lower, value) - return MOI.set(model, attr, bridge.upper, value) + MOI.set(model, attr, bridge.upper, value) + return end # The map is: # x ∈ S <=> [1 1]' * x ∈ LS × US @@ -147,6 +160,7 @@ function MOI.get( return MOI.get(model, attr, bridge.lower) + MOI.get(model, attr, bridge.upper) end + function _split_dual_start(value) if value < 0 return zero(value), value @@ -154,6 +168,7 @@ function _split_dual_start(value) return value, zero(value) end end + function _split_dual_start(value::Vector) lower = similar(value) upper = similar(value) @@ -161,6 +176,7 @@ function _split_dual_start(value::Vector) lower[i], upper[i] = _split_dual_start(value[i]) end end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintDualStart, @@ -169,7 +185,8 @@ function MOI.set( ) where {T} lower, upper = _split_dual_start(value) MOI.set(model, attr, bridge.lower, lower) - return MOI.set(model, attr, bridge.upper, upper) + MOI.set(model, attr, bridge.upper, upper) + return end function MOI.get( @@ -213,7 +230,8 @@ function MOI.modify( change::MOI.AbstractFunctionModification, ) MOI.modify(model, bridge.lower, change) - return MOI.modify(model, bridge.upper, change) + MOI.modify(model, bridge.upper, change) + return end function MOI.set( @@ -223,7 +241,8 @@ function MOI.set( func::F, ) where {T,F} MOI.set(model, MOI.ConstraintFunction(), bridge.lower, func) - return MOI.set(model, MOI.ConstraintFunction(), bridge.upper, func) + MOI.set(model, MOI.ConstraintFunction(), bridge.upper, func) + return end function MOI.set( @@ -233,7 +252,8 @@ function MOI.set( change::S, ) where {T,F,S} MOI.set(model, MOI.ConstraintSet(), bridge.lower, _lower_set(change)) - return MOI.set(model, MOI.ConstraintSet(), bridge.upper, _upper_set(change)) + MOI.set(model, MOI.ConstraintSet(), bridge.upper, _upper_set(change)) + return end function MOI.get( @@ -243,6 +263,7 @@ function MOI.get( ) return MOI.get(model, attr, bridge.lower) end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintSet, @@ -253,6 +274,7 @@ function MOI.get( MOI.get(model, attr, bridge.upper).upper, ) end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintSet, @@ -260,6 +282,7 @@ function MOI.get( ) where {T,F} return MOI.EqualTo(MOI.get(model, attr, bridge.lower).lower) end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintSet, diff --git a/src/Bridges/Constraint/ltgt_to_interval.jl b/src/Bridges/Constraint/ltgt_to_interval.jl index 8dc548ed49..232ae96d89 100644 --- a/src/Bridges/Constraint/ltgt_to_interval.jl +++ b/src/Bridges/Constraint/ltgt_to_interval.jl @@ -31,14 +31,17 @@ function MOI.modify( bridge::AbstractToIntervalBridge, change::MOI.ScalarCoefficientChange, ) - return MOI.modify(model, bridge.constraint, change) + MOI.modify(model, bridge.constraint, change) + return end + function MOI.modify( model::MOI.ModelLike, bridge::AbstractToIntervalBridge, change::MOI.MultirowChange{T}, ) where {T} - return MOI.modify(model, bridge.constraint, change) + MOI.modify(model, bridge.constraint, change) + return end """ @@ -52,12 +55,15 @@ struct GreaterToIntervalBridge{T,F<:MOI.AbstractScalarFunction} <: AbstractToIntervalBridge{T,MOI.GreaterThan{T},F} constraint::CI{F,MOI.Interval{T}} end + function map_set(::Type{<:GreaterToIntervalBridge}, set::MOI.GreaterThan) return MOI.Interval(set.lower, typemax(set.lower)) end + function inverse_map_set(::Type{<:GreaterToIntervalBridge}, set::MOI.Interval) return MOI.GreaterThan(set.lower) end + function concrete_bridge_type( ::Type{<:GreaterToIntervalBridge{T}}, F::Type{<:MOI.AbstractScalarFunction}, @@ -77,12 +83,15 @@ struct LessToIntervalBridge{T,F<:MOI.AbstractScalarFunction} <: AbstractToIntervalBridge{T,MOI.LessThan{T},F} constraint::CI{F,MOI.Interval{T}} end + function map_set(::Type{<:LessToIntervalBridge}, set::MOI.LessThan) return MOI.Interval(typemin(set.upper), set.upper) end + function inverse_map_set(::Type{<:LessToIntervalBridge}, set::MOI.Interval) return MOI.LessThan(set.upper) end + function concrete_bridge_type( ::Type{<:LessToIntervalBridge{T}}, F::Type{<:MOI.AbstractScalarFunction}, diff --git a/src/Bridges/Constraint/map.jl b/src/Bridges/Constraint/map.jl index 471fd6eadf..133ebd53f5 100644 --- a/src/Bridges/Constraint/map.jl +++ b/src/Bridges/Constraint/map.jl @@ -18,6 +18,7 @@ struct Map <: AbstractDict{MOI.ConstraintIndex,AbstractBridge} AbstractBridge, } end + function Map() return Map( Union{Nothing,AbstractBridge}[], @@ -32,12 +33,14 @@ function Base.isempty(map::Map) return all(bridge -> bridge === nothing, map.bridges) && isempty(map.single_variable_constraints) end + function Base.empty!(map::Map) empty!(map.bridges) empty!(map.constraint_types) empty!(map.single_variable_constraints) return map end + function Base.length(map::Map) return count(bridge -> bridge !== nothing, map.bridges) + length(map.single_variable_constraints) @@ -49,25 +52,30 @@ function Base.haskey(map::Map, ci::MOI.ConstraintIndex{F,S}) where {F,S} map.bridges[_index(ci)] !== nothing && (F, S) == map.constraint_types[_index(ci)] end + function Base.getindex(map::Map, ci::MOI.ConstraintIndex) return map.bridges[_index(ci)] end + function Base.haskey( map::Map, ci::MOI.ConstraintIndex{MOI.SingleVariable,S}, ) where {S} return haskey(map.single_variable_constraints, (ci.value, S)) end + function Base.getindex( map::Map, ci::MOI.ConstraintIndex{MOI.SingleVariable,S}, ) where {S} return map.single_variable_constraints[(ci.value, S)] end + function Base.delete!(map::Map, ci::MOI.ConstraintIndex) map.bridges[_index(ci)] = nothing return map end + function Base.delete!( map::Map, ci::MOI.ConstraintIndex{MOI.SingleVariable,S}, @@ -75,6 +83,7 @@ function Base.delete!( delete!(map.single_variable_constraints, (ci.value, S)) return map end + function Base.values(map::Map) return Base.Iterators.flatten(( # See comment in `values(::Variable.Map)`. @@ -98,12 +107,15 @@ function _iterate_sv( return ci => bridge, (2, elem_state[2]) end end + function _index(index, F, S) return MOI.ConstraintIndex{F,S}(index) end + function _index(index, F::Type{MOI.VectorOfVariables}, S) return MOI.ConstraintIndex{F,S}(-index) end + function _iterate(map::Map, state = 1) while state ≤ length(map.bridges) && map.bridges[state] === nothing state += 1 @@ -148,18 +160,21 @@ function keys_of_type end function number_of_type(map::Map, ::Type{MOI.ConstraintIndex{F,S}}) where {F,S} return count(i -> haskey(map, _index(i, F, S)), eachindex(map.bridges)) end + function keys_of_type(map::Map, C::Type{MOI.ConstraintIndex{F,S}}) where {F,S} return Base.Iterators.Filter( ci -> haskey(map, ci), MOIU.LazyMap{C}(i -> _index(i, F, S), eachindex(map.bridges)), ) end + function number_of_type( map::Map, C::Type{MOI.ConstraintIndex{MOI.SingleVariable,S}}, ) where {S} return count(key -> key[2] == S, keys(map.single_variable_constraints)) end + function keys_of_type( map::Map, C::Type{MOI.ConstraintIndex{MOI.SingleVariable,S}}, @@ -256,6 +271,7 @@ function add_key_for_bridge( push!(map.constraint_types, (typeof(func), typeof(set))) return _index(length(map.bridges), typeof(func), typeof(set)) end + function add_key_for_bridge( map::Map, bridge::AbstractBridge, diff --git a/src/Bridges/Constraint/norm_spec_nuc_to_psd.jl b/src/Bridges/Constraint/norm_spec_nuc_to_psd.jl index a730e7a7d5..2284aa3e84 100644 --- a/src/Bridges/Constraint/norm_spec_nuc_to_psd.jl +++ b/src/Bridges/Constraint/norm_spec_nuc_to_psd.jl @@ -9,6 +9,7 @@ struct NormSpectralBridge{T,F,G} <: AbstractBridge column_dim::Int # column dimension of X psd_index::CI{F,MOI.PositiveSemidefiniteConeTriangle} end + function bridge_constraint( ::Type{NormSpectralBridge{T,F,G}}, model::MOI.ModelLike, @@ -47,14 +48,17 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:NormSpectralBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{NormSpectralBridge{T,F,G}}, ) where {T,F,G} return [(F, MOI.PositiveSemidefiniteConeTriangle)] end + function concrete_bridge_type( ::Type{<:NormSpectralBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, @@ -71,6 +75,7 @@ function MOI.get( ) where {T,F,G} return 1 end + function MOI.get( bridge::NormSpectralBridge{T,F,G}, ::MOI.ListOfConstraintIndices{F,MOI.PositiveSemidefiniteConeTriangle}, @@ -80,7 +85,8 @@ end # References function MOI.delete(model::MOI.ModelLike, bridge::NormSpectralBridge) - return MOI.delete(model, bridge.psd_index) + MOI.delete(model, bridge.psd_index) + return end # Attributes, Bridge acting as a constraint @@ -100,6 +106,7 @@ function MOI.get( ]] return MOIU.convert_approx(G, MOIU.operate(vcat, T, t, X)) end + function MOI.get( model::MOI.ModelLike, ::MOI.ConstraintSet, @@ -107,6 +114,7 @@ function MOI.get( ) return MOI.NormSpectralCone(bridge.row_dim, bridge.column_dim) end + function MOI.supports( ::MOI.ModelLike, ::MOI.ConstraintPrimalStart, @@ -114,6 +122,7 @@ function MOI.supports( ) return true end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, @@ -128,6 +137,7 @@ function MOI.get( ]] return vcat(t, X) end + function MOI.set( model::MOI.ModelLike, ::MOI.ConstraintPrimalStart, @@ -181,6 +191,7 @@ struct NormNuclearBridge{T,F,G,H} <: AbstractBridge ge_index::CI{F,MOI.GreaterThan{T}} psd_index::CI{G,MOI.PositiveSemidefiniteConeTriangle} end + function bridge_constraint( ::Type{NormNuclearBridge{T,F,G,H}}, model::MOI.ModelLike, @@ -241,14 +252,17 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:NormNuclearBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{NormNuclearBridge{T,F,G,H}}, ) where {T,F,G,H} return [(F, MOI.GreaterThan{T}), (G, MOI.PositiveSemidefiniteConeTriangle)] end + function concrete_bridge_type( ::Type{<:NormNuclearBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, @@ -269,27 +283,32 @@ end function MOI.get(bridge::NormNuclearBridge, ::MOI.NumberOfVariables) return length(bridge.U) + length(bridge.V) end + function MOI.get(bridge::NormNuclearBridge, ::MOI.ListOfVariableIndices) return vcat(bridge.U, bridge.V) end + function MOI.get( bridge::NormNuclearBridge{T,F,G,H}, ::MOI.NumberOfConstraints{F,MOI.GreaterThan{T}}, ) where {T,F,G,H} return 1 end + function MOI.get( bridge::NormNuclearBridge{T,F,G,H}, ::MOI.NumberOfConstraints{G,MOI.PositiveSemidefiniteConeTriangle}, ) where {T,F,G,H} return 1 end + function MOI.get( bridge::NormNuclearBridge{T,F,G,H}, ::MOI.ListOfConstraintIndices{F,MOI.GreaterThan{T}}, ) where {T,F,G,H} return [bridge.ge_index] end + function MOI.get( bridge::NormNuclearBridge{T,F,G,H}, ::MOI.ListOfConstraintIndices{G,MOI.PositiveSemidefiniteConeTriangle}, @@ -302,7 +321,8 @@ function MOI.delete(model::MOI.ModelLike, bridge::NormNuclearBridge) MOI.delete(model, bridge.ge_index) MOI.delete(model, bridge.psd_index) MOI.delete(model, bridge.U) - return MOI.delete(model, bridge.V) + MOI.delete(model, bridge.V) + return end # Attributes, Bridge acting as a constraint @@ -335,6 +355,7 @@ function MOI.get( ]] return MOIU.convert_approx(H, MOIU.operate(vcat, T, t, X)) end + function MOI.get( model::MOI.ModelLike, ::MOI.ConstraintSet, @@ -342,6 +363,7 @@ function MOI.get( ) return MOI.NormNuclearCone(bridge.row_dim, bridge.column_dim) end + function MOI.supports( ::MOI.ModelLike, ::MOI.ConstraintDualStart, @@ -349,6 +371,7 @@ function MOI.supports( ) return true end + function MOI.get( model::MOI.ModelLike, ::MOI.ConstraintPrimal, @@ -381,6 +404,7 @@ function MOI.get( ]] return vcat(t, X) end + function MOI.set( model::MOI.ModelLike, ::MOI.ConstraintDualStart, diff --git a/src/Bridges/Constraint/norm_to_lp.jl b/src/Bridges/Constraint/norm_to_lp.jl index 90d02fb2c2..41e8559ebc 100644 --- a/src/Bridges/Constraint/norm_to_lp.jl +++ b/src/Bridges/Constraint/norm_to_lp.jl @@ -9,6 +9,7 @@ struct NormInfinityBridge{T,F,G} <: SetMapBridge{T,MOI.Nonnegatives,MOI.NormInfinityCone,F,G} constraint::CI{F,MOI.Nonnegatives} end + function concrete_bridge_type( ::Type{<:NormInfinityBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, @@ -21,6 +22,7 @@ end function map_set(::Type{<:NormInfinityBridge}, set::MOI.NormInfinityCone) return MOI.Nonnegatives(2 * (MOI.dimension(set) - 1)) end + function inverse_map_set(::Type{<:NormInfinityBridge}, set::MOI.Nonnegatives) return MOI.NormInfinityCone(div(MOI.dimension(set), 2) + 1) end @@ -36,6 +38,7 @@ function map_function(::Type{<:NormInfinityBridge{T}}, func) where {T} end return f_new end + function inverse_map_function(::Type{<:NormInfinityBridge{T}}, func) where {T} scalars = MOIU.eachscalar(func) t = MOIU.operate!(/, T, sum(scalars), T(length(scalars))) @@ -58,6 +61,7 @@ function adjoint_map_function(::Type{<:NormInfinityBridge}, func) x = (scalars[(d+1):end] - scalars[1:d]) return vcat(t, x) end + function inverse_adjoint_map_function( ::Type{<:NormInfinityBridge{T}}, func::AbstractVector{T}, @@ -87,6 +91,7 @@ struct NormOneBridge{T,F,G} <: AbstractBridge y::Vector{MOI.VariableIndex} nn_index::CI{F,MOI.Nonnegatives} end + function bridge_constraint( ::Type{NormOneBridge{T,F,G}}, model::MOI.ModelLike, @@ -113,12 +118,15 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:NormOneBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types(::Type{<:NormOneBridge{T,F}}) where {T,F} return [(F, MOI.Nonnegatives)] end + function concrete_bridge_type( ::Type{<:NormOneBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, @@ -143,6 +151,7 @@ function MOI.get( ) where {T,F} return 1 end + function MOI.get( b::NormOneBridge{T,F}, ::MOI.ListOfConstraintIndices{F,MOI.Nonnegatives}, @@ -153,7 +162,8 @@ end # References function MOI.delete(model::MOI.ModelLike, c::NormOneBridge) MOI.delete(model, c.nn_index) - return MOI.delete(model, c.y) + MOI.delete(model, c.y) + return end # Attributes, Bridge acting as a constraint @@ -177,6 +187,7 @@ function MOI.get( MOIU.remove_variable(MOIU.operate(vcat, T, t, x), c.y), ) end + function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormOneBridge) dim = div( MOI.dimension(MOI.get(model, MOI.ConstraintSet(), c.nn_index)) + 1, @@ -184,6 +195,7 @@ function MOI.get(model::MOI.ModelLike, ::MOI.ConstraintSet, c::NormOneBridge) ) return MOI.NormOneCone(dim) end + function MOI.supports( ::MOI.ModelLike, ::Union{MOI.ConstraintPrimalStart,MOI.ConstraintDualStart}, @@ -191,6 +203,7 @@ function MOI.supports( ) return true end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, @@ -210,6 +223,7 @@ function MOI.set( MOI.set(model, attr, bridge.nn_index, nn_value) return end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, diff --git a/src/Bridges/Constraint/quad_to_soc.jl b/src/Bridges/Constraint/quad_to_soc.jl index a5dbda95d4..445f065c91 100644 --- a/src/Bridges/Constraint/quad_to_soc.jl +++ b/src/Bridges/Constraint/quad_to_soc.jl @@ -60,6 +60,7 @@ struct QuadtoSOCBridge{T} <: AbstractBridge less_than::Bool # whether the constraint was ≤ or ≥ set_constant::T # the constant that was on the set end + function bridge_constraint( ::Type{QuadtoSOCBridge{T}}, model, @@ -161,12 +162,15 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:QuadtoSOCBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types(::Type{QuadtoSOCBridge{T}}) where {T} return [(MOI.VectorAffineFunction{T}, MOI.RotatedSecondOrderCone)] end + function concrete_bridge_type( ::Type{<:QuadtoSOCBridge{T}}, ::Type{MOI.ScalarQuadraticFunction{T}}, @@ -185,6 +189,7 @@ function MOI.get( ) where {T} return 1 end + function MOI.get( bridge::QuadtoSOCBridge{T}, ::MOI.ListOfConstraintIndices{ @@ -197,7 +202,8 @@ end # References function MOI.delete(model::MOI.ModelLike, bridge::QuadtoSOCBridge) - return MOI.delete(model, bridge.soc) + MOI.delete(model, bridge.soc) + return end # Attributes, Bridge acting as a constraint @@ -270,6 +276,7 @@ function MOI.get( return MOI.GreaterThan(b.set_constant) end end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintFunction, diff --git a/src/Bridges/Constraint/relentr_to_exp.jl b/src/Bridges/Constraint/relentr_to_exp.jl index 94a2518fbf..14f367c7d5 100644 --- a/src/Bridges/Constraint/relentr_to_exp.jl +++ b/src/Bridges/Constraint/relentr_to_exp.jl @@ -12,6 +12,7 @@ struct RelativeEntropyBridge{T,F,G,H} <: AbstractBridge ge_index::CI{F,MOI.GreaterThan{T}} exp_indices::Vector{CI{G,MOI.ExponentialCone}} end + function bridge_constraint( ::Type{RelativeEntropyBridge{T,F,G,H}}, model::MOI.ModelLike, @@ -52,14 +53,17 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:RelativeEntropyBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{RelativeEntropyBridge{T,F,G,H}}, ) where {T,F,G,H} return [(F, MOI.GreaterThan{T}), (G, MOI.ExponentialCone)] end + function concrete_bridge_type( ::Type{<:RelativeEntropyBridge{T}}, H::Type{<:MOI.AbstractVectorFunction}, @@ -87,18 +91,21 @@ function MOI.get( ) where {T,F} return 1 end + function MOI.get( bridge::RelativeEntropyBridge{T,F,G}, ::MOI.NumberOfConstraints{G,MOI.ExponentialCone}, ) where {T,F,G} return length(bridge.y) end + function MOI.get( bridge::RelativeEntropyBridge{T,F}, ::MOI.ListOfConstraintIndices{F,MOI.GreaterThan{T}}, ) where {T,F} return [bridge.ge_index] end + function MOI.get( bridge::RelativeEntropyBridge{T,F,G}, ::MOI.ListOfConstraintIndices{G,MOI.ExponentialCone}, @@ -112,7 +119,8 @@ function MOI.delete(model::MOI.ModelLike, bridge::RelativeEntropyBridge) MOI.delete(model, exp_index_i) end MOI.delete(model, bridge.ge_index) - return MOI.delete(model, bridge.y) + MOI.delete(model, bridge.y) + return end # Attributes, Bridge acting as a constraint @@ -140,6 +148,7 @@ function MOI.get( end return MOIU.convert_approx(H, MOIU.remove_variable(func, bridge.y)) end + function MOI.get( model::MOI.ModelLike, ::MOI.ConstraintSet, @@ -147,6 +156,7 @@ function MOI.get( ) return MOI.RelativeEntropyCone(1 + 2 * length(bridge.y)) end + function MOI.supports( ::MOI.ModelLike, ::Union{MOI.ConstraintPrimalStart,MOI.ConstraintDualStart}, @@ -154,6 +164,7 @@ function MOI.supports( ) return true end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, @@ -170,6 +181,7 @@ function MOI.get( end return primal end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, diff --git a/src/Bridges/Constraint/rsoc.jl b/src/Bridges/Constraint/rsoc.jl index 6b943f5b5b..da7a4a25a1 100644 --- a/src/Bridges/Constraint/rsoc.jl +++ b/src/Bridges/Constraint/rsoc.jl @@ -39,6 +39,7 @@ end function map_set(::Type{<:RSOCtoSOCBridge}, set::MOI.RotatedSecondOrderCone) return MOI.SecondOrderCone(MOI.dimension(set)) end + function inverse_map_set(::Type{<:RSOCtoSOCBridge}, set::MOI.SecondOrderCone) return MOI.RotatedSecondOrderCone(MOI.dimension(set)) end @@ -65,6 +66,7 @@ end function map_set(::Type{<:SOCtoRSOCBridge}, set::MOI.SecondOrderCone) return MOI.RotatedSecondOrderCone(MOI.dimension(set)) end + function inverse_map_set( ::Type{<:SOCtoRSOCBridge}, set::MOI.RotatedSecondOrderCone, diff --git a/src/Bridges/Constraint/scalarize.jl b/src/Bridges/Constraint/scalarize.jl index b36886f147..bfc73b3b62 100644 --- a/src/Bridges/Constraint/scalarize.jl +++ b/src/Bridges/Constraint/scalarize.jl @@ -8,6 +8,7 @@ mutable struct ScalarizeBridge{T,F,S} <: AbstractBridge scalar_constraints::Vector{CI{F,S}} constants::Vector{T} end + function bridge_constraint( ::Type{ScalarizeBridge{T,F,S}}, model::MOI.ModelLike, @@ -34,14 +35,17 @@ function MOI.supports_constraint( # we only support it if the coefficient type of `F` is `T`. return MOIU.is_coefficient_type(F, T) end + function MOIB.added_constrained_variable_types(::Type{<:ScalarizeBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{ScalarizeBridge{T,F,S}}, ) where {T,F,S} return [(F, S)] end + function concrete_bridge_type( ::Type{<:ScalarizeBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, @@ -57,6 +61,7 @@ function MOI.get( ) where {T,F,S} return length(bridge.scalar_constraints) end + function MOI.get( bridge::ScalarizeBridge{T,F,S}, ::MOI.ListOfConstraintIndices{F,S}, @@ -70,6 +75,7 @@ function MOI.delete(model::MOI.ModelLike, bridge::ScalarizeBridge) MOI.delete(model, ci) end end + function MOI.delete( model::MOI.ModelLike, bridge::ScalarizeBridge, @@ -103,6 +109,7 @@ function MOI.get( end return func end + function MOI.get( ::MOI.ModelLike, ::MOI.ConstraintSet, @@ -118,6 +125,7 @@ function MOI.supports( ) return true end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, @@ -125,6 +133,7 @@ function MOI.get( ) return MOI.get.(model, attr, bridge.scalar_constraints) .+ bridge.constants end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, @@ -132,13 +141,10 @@ function MOI.set( value, ) # TODO do no add constant if the primal status is a ray like in Vectorize - return MOI.set.( - model, - attr, - bridge.scalar_constraints, - value .- bridge.constants, - ) + MOI.set.(model, attr, bridge.scalar_constraints, value .- bridge.constants) + return end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart}, @@ -146,27 +152,32 @@ function MOI.get( ) return MOI.get.(model, attr, bridge.scalar_constraints) end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintDualStart, bridge::ScalarizeBridge, value, ) - return MOI.set.(model, attr, bridge.scalar_constraints, value) + MOI.set.(model, attr, bridge.scalar_constraints, value) + return end + function MOI.modify( model::MOI.ModelLike, bridge::ScalarizeBridge{T,F,S}, change::MOI.VectorConstantChange{T}, ) where {T,F,S} bridge.constants = change.new_constant - return MOI.set.( + MOI.set.( model, MOI.ConstraintSet(), bridge.scalar_constraints, S.(-change.new_constant), ) + return end + function MOI.modify( model::MOI.ModelLike, bridge::ScalarizeBridge, @@ -181,6 +192,7 @@ function MOI.modify( end return nothing end + function MOI.set( model::MOI.ModelLike, ::MOI.ConstraintFunction, diff --git a/src/Bridges/Constraint/set_map.jl b/src/Bridges/Constraint/set_map.jl index af07793bfd..a7528e08f4 100644 --- a/src/Bridges/Constraint/set_map.jl +++ b/src/Bridges/Constraint/set_map.jl @@ -18,6 +18,7 @@ function MOI.supports_constraint( ) where {T,S2,S1<:MOI.AbstractScalarSet} return true end + function MOI.supports_constraint( ::Type{<:SetMapBridge{T,S2,S1}}, ::Type{<:MOI.AbstractVectorFunction}, @@ -25,9 +26,11 @@ function MOI.supports_constraint( ) where {T,S2,S1<:MOI.AbstractVectorSet} return true end + function MOIB.added_constrained_variable_types(::Type{<:SetMapBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{<:SetMapBridge{T,S2,S1,F}}, ) where {T,S2,S1,F} @@ -41,6 +44,7 @@ function MOI.get( ) where {T,S2,S1,F} return 1 end + function MOI.get( bridge::SetMapBridge{T,S2,S1,F}, ::MOI.ListOfConstraintIndices{F,S2}, @@ -50,7 +54,8 @@ end # References function MOI.delete(model::MOI.ModelLike, bridge::SetMapBridge) - return MOI.delete(model, bridge.constraint) + MOI.delete(model, bridge.constraint) + return end # Attributes, Bridge acting as a constraint @@ -63,6 +68,7 @@ function MOI.get( func = inverse_map_function(typeof(bridge), mapped_func) return MOIU.convert_approx(G, func) end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintSet, @@ -71,18 +77,15 @@ function MOI.get( set = MOI.get(model, attr, bridge.constraint) return inverse_map_set(typeof(bridge), set) end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintSet, bridge::SetMapBridge{T,S2,S1}, new_set::S1, ) where {T,S2,S1} - return MOI.set( - model, - attr, - bridge.constraint, - map_set(typeof(bridge), new_set), - ) + MOI.set(model, attr, bridge.constraint, map_set(typeof(bridge), new_set)) + return end function MOI.supports( @@ -92,6 +95,7 @@ function MOI.supports( ) where {T,S2,S1,F} return MOI.supports(model, attr, MOI.ConstraintIndex{F,S2}) end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, @@ -100,6 +104,7 @@ function MOI.get( value = MOI.get(model, attr, bridge.constraint) return inverse_map_function(typeof(bridge), value) end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, @@ -107,8 +112,10 @@ function MOI.set( value, ) mapped_value = map_function(typeof(bridge), value) - return MOI.set(model, attr, bridge.constraint, mapped_value) + MOI.set(model, attr, bridge.constraint, mapped_value) + return end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintDual,MOI.ConstraintDualStart}, @@ -117,6 +124,7 @@ function MOI.get( value = MOI.get(model, attr, bridge.constraint) return adjoint_map_function(typeof(bridge), value) end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintDualStart, @@ -124,7 +132,8 @@ function MOI.set( value, ) mapped_value = inverse_adjoint_map_function(typeof(bridge), value) - return MOI.set(model, attr, bridge.constraint, mapped_value) + MOI.set(model, attr, bridge.constraint, mapped_value) + return end function MOI.modify( @@ -134,11 +143,8 @@ function MOI.modify( ) # By linearity of the map, we can just change the constant constant = map_function(typeof(bridge), change.new_constant) - return MOI.modify( - model, - bridge.constraint, - MOI.VectorConstantChange(constant), - ) + MOI.modify(model, bridge.constraint, MOI.VectorConstantChange(constant)) + return end include("flip_sign.jl") diff --git a/src/Bridges/Constraint/single_bridge_optimizer.jl b/src/Bridges/Constraint/single_bridge_optimizer.jl index 60e10bdac4..290144961b 100644 --- a/src/Bridges/Constraint/single_bridge_optimizer.jl +++ b/src/Bridges/Constraint/single_bridge_optimizer.jl @@ -1,5 +1,6 @@ """ - SingleBridgeOptimizer{BT<:AbstractBridge, OT<:MOI.ModelLike} <: AbstractBridgeOptimizer + SingleBridgeOptimizer{BT<:AbstractBridge, OT<:MOI.ModelLike} <: + AbstractBridgeOptimizer The `SingleBridgeOptimizer` bridges any constraint supported by the bridge `BT`. This is in contrast with the [`MathOptInterface.Bridges.LazyBridgeOptimizer`](@ref) @@ -13,6 +14,7 @@ mutable struct SingleBridgeOptimizer{BT<:AbstractBridge,OT<:MOI.ModelLike} <: con_to_name::Dict{MOI.ConstraintIndex,String} name_to_con::Union{Dict{String,MOI.ConstraintIndex},Nothing} end + function SingleBridgeOptimizer{BT}(model::OT) where {BT,OT<:MOI.ModelLike} return SingleBridgeOptimizer{BT,OT}( model, @@ -25,19 +27,23 @@ end function bridges(bridge::MOI.Bridges.AbstractBridgeOptimizer) return EmptyMap() end + function bridges(bridge::SingleBridgeOptimizer) return bridge.map end MOIB.supports_constraint_bridges(::SingleBridgeOptimizer) = true + # If `BT` bridges `MOI.Reals` (such as `Constraint.FunctionizeBridge` bridge, # without this method, it creates a `StackOverflow` with # `is_bridged`, `supports_bridging_constrained_variable` # and `supports_add_constrained_variables`. MOIB.is_bridged(::SingleBridgeOptimizer, ::Type{MOI.Reals}) = false + function MOIB.is_bridged(b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractSet}) return MOIB.supports_bridging_constrained_variable(b, S) end + function MOIB.supports_bridging_constrained_variable( b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractSet}, @@ -48,6 +54,7 @@ function MOIB.supports_bridging_constrained_variable( S, ) && MOI.supports_add_constrained_variables(b, MOI.Reals) end + function MOIB.supports_bridging_constraint( ::SingleBridgeOptimizer{BT}, F::Type{<:MOI.AbstractFunction}, @@ -55,6 +62,7 @@ function MOIB.supports_bridging_constraint( ) where {BT} return MOI.supports_constraint(BT, F, S) end + function MOIB.is_bridged( b::SingleBridgeOptimizer, F::Type{<:MOI.AbstractFunction}, @@ -62,12 +70,14 @@ function MOIB.is_bridged( ) return MOIB.supports_bridging_constraint(b, F, S) end + function MOIB.is_bridged( ::SingleBridgeOptimizer, ::Type{<:MOI.AbstractScalarFunction}, ) return false end + function MOIB.bridge_type( ::SingleBridgeOptimizer{BT}, ::Type{<:MOI.AbstractFunction}, @@ -75,4 +85,5 @@ function MOIB.bridge_type( ) where {BT} return BT end + MOIB.bridging_cost(::SingleBridgeOptimizer, args...) = 1.0 diff --git a/src/Bridges/Constraint/slack.jl b/src/Bridges/Constraint/slack.jl index f9b2ac9d26..919935fe29 100644 --- a/src/Bridges/Constraint/slack.jl +++ b/src/Bridges/Constraint/slack.jl @@ -5,6 +5,7 @@ function MOIB.added_constrained_variable_types( ) where {T,VF,ZS,F,S} return [(S,)] end + function MOIB.added_constraint_types( ::Type{<:AbstractSlackBridge{T,VF,ZS,F}}, ) where {T,VF,ZS,F} @@ -17,18 +18,21 @@ function MOI.get( ) where {T,VF,ZS,F} return 1 end + function MOI.get( ::AbstractSlackBridge{T,VF,ZS,F,S}, ::MOI.NumberOfConstraints{VF,S}, ) where {T,VF,ZS,F,S} return 1 end + function MOI.get( bridge::AbstractSlackBridge{T,VF,ZS,F}, ::MOI.ListOfConstraintIndices{F,ZS}, ) where {T,VF,ZS,F} return [bridge.equality] end + function MOI.get( bridge::AbstractSlackBridge{T,VF,ZS,F,S}, ::MOI.ListOfConstraintIndices{VF,S}, @@ -51,6 +55,7 @@ function MOI.supports( ) return true end + function MOI.get( model::MOI.ModelLike, attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart}, @@ -59,6 +64,7 @@ function MOI.get( # due to equality, slack should have the same value as original affine function return MOI.get(model, attr, bridge.slack_in_set) end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintPrimalStart, @@ -71,8 +77,10 @@ function MOI.set( MOI.set.(model, MOI.VariablePrimalStart(), bridge.slack, value) end MOI.set(model, attr, bridge.slack_in_set, value) - return MOI.set(model, attr, bridge.equality, zero(value)) + MOI.set(model, attr, bridge.equality, zero(value)) + return end + function MOI.get( model::MOI.ModelLike, a::Union{MOI.ConstraintDual,MOI.ConstraintDualStart}, @@ -83,6 +91,7 @@ function MOI.get( # equal and we can return either one of them. return MOI.get(model, a, bridge.slack_in_set) end + function MOI.set( model::MOI.ModelLike, attr::MOI.ConstraintDualStart, @@ -94,7 +103,8 @@ function MOI.set( # Giving `value` to `bridge.equality` will put the function in the # lagrangian as expected. MOI.set(model, attr, bridge.slack_in_set, value) - return MOI.set(model, attr, bridge.equality, value) + MOI.set(model, attr, bridge.equality, value) + return end function MOI.modify( @@ -102,7 +112,8 @@ function MOI.modify( bridge::AbstractSlackBridge, change::MOI.AbstractFunctionModification, ) - return MOI.modify(model, bridge.equality, change) + MOI.modify(model, bridge.equality, change) + return end function MOI.set( @@ -111,7 +122,8 @@ function MOI.set( bridge::AbstractSlackBridge{T,VF,ZS,F,S}, change::S, ) where {T,VF,ZS,F,S} - return MOI.set(model, MOI.ConstraintSet(), bridge.slack_in_set, change) + MOI.set(model, MOI.ConstraintSet(), bridge.slack_in_set, change) + return end function MOI.get( @@ -124,6 +136,7 @@ function MOI.get( bridge.slack, ) end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintSet, @@ -148,6 +161,7 @@ struct ScalarSlackBridge{T,F,S} <: slack_in_set::CI{MOI.SingleVariable,S} equality::CI{F,MOI.EqualTo{T}} end + function bridge_constraint( ::Type{ScalarSlackBridge{T,F,S}}, model, @@ -176,6 +190,7 @@ function MOI.supports_constraint( ) where {T} return false end + function MOI.supports_constraint( ::Type{ScalarSlackBridge{T}}, ::Type{<:MOI.SingleVariable}, @@ -183,6 +198,7 @@ function MOI.supports_constraint( ) where {T} return false end + function MOI.supports_constraint( ::Type{ScalarSlackBridge{T}}, ::Type{<:MOI.AbstractScalarFunction}, @@ -190,6 +206,7 @@ function MOI.supports_constraint( ) where {T} return false end + function concrete_bridge_type( ::Type{<:ScalarSlackBridge{T}}, F::Type{<:MOI.AbstractScalarFunction}, @@ -219,7 +236,8 @@ function MOI.set( func::F, ) where {T,F,S} new_func = MOIU.operate(-, T, func, MOI.SingleVariable(bridge.slack)) - return MOI.set(model, MOI.ConstraintFunction(), bridge.equality, new_func) + MOI.set(model, MOI.ConstraintFunction(), bridge.equality, new_func) + return end # vector version @@ -238,6 +256,7 @@ struct VectorSlackBridge{T,F,S} <: slack_in_set::CI{MOI.VectorOfVariables,S} equality::CI{F,MOI.Zeros} end + function bridge_constraint( ::Type{VectorSlackBridge{T,F,S}}, model, @@ -258,6 +277,7 @@ function MOI.supports_constraint( ) where {T} return MOIU.is_coefficient_type(F, T) end + function MOI.supports_constraint( ::Type{VectorSlackBridge{T}}, ::Type{<:MOI.VectorOfVariables}, @@ -265,6 +285,7 @@ function MOI.supports_constraint( ) where {T} return false end + function MOI.supports_constraint( ::Type{VectorSlackBridge{T}}, ::Type{<:MOI.AbstractVectorFunction}, @@ -272,6 +293,7 @@ function MOI.supports_constraint( ) where {T} return false end + function MOI.supports_constraint( ::Type{VectorSlackBridge{T}}, ::Type{<:MOI.VectorOfVariables}, @@ -279,6 +301,7 @@ function MOI.supports_constraint( ) where {T} return false end + function concrete_bridge_type( ::Type{<:VectorSlackBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, @@ -305,5 +328,6 @@ function MOI.set( func, MOI.VectorAffineFunction{T}(MOI.VectorOfVariables(bridge.slack)), ) - return MOI.set(model, MOI.ConstraintFunction(), bridge.equality, new_func) + MOI.set(model, MOI.ConstraintFunction(), bridge.equality, new_func) + return end diff --git a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl index e01c0d81e3..ac20291343 100644 --- a/src/Bridges/Constraint/soc_to_nonconvex_quad.jl +++ b/src/Bridges/Constraint/soc_to_nonconvex_quad.jl @@ -29,6 +29,7 @@ struct SOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} var_pos::Vector{CI{MOI.ScalarAffineFunction{T},MOI.GreaterThan{T}}} vars::Vector{MOI.VariableIndex} end + function bridge_constraint( ::Type{SOCtoNonConvexQuadBridge{T}}, model, @@ -87,6 +88,7 @@ struct RSOCtoNonConvexQuadBridge{T} <: AbstractSOCtoNonConvexQuadBridge{T} var_pos::Vector{CI{MOI.ScalarAffineFunction{T},MOI.GreaterThan{T}}} vars::Vector{MOI.VariableIndex} end + function bridge_constraint( ::Type{RSOCtoNonConvexQuadBridge{T}}, model, @@ -126,6 +128,7 @@ function MOI.supports_constraint( ) where {T} return true end + function MOI.supports_constraint( ::Type{RSOCtoNonConvexQuadBridge{T}}, ::Type{MOI.VectorOfVariables}, @@ -139,6 +142,7 @@ function MOIB.added_constrained_variable_types( ) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{<:AbstractSOCtoNonConvexQuadBridge{T}}, ) where {T} @@ -155,6 +159,7 @@ function concrete_bridge_type( ) where {T} return SOCtoNonConvexQuadBridge{T} end + function concrete_bridge_type( ::Type{RSOCtoNonConvexQuadBridge{T}}, ::Type{MOI.VectorOfVariables}, @@ -204,7 +209,8 @@ function MOI.delete( bridge::AbstractSOCtoNonConvexQuadBridge, ) MOI.delete(model, bridge.quad) - return MOI.delete.(model, bridge.var_pos) + MOI.delete.(model, bridge.var_pos) + return end # Attributes, Bridge acting as a constraint @@ -224,6 +230,7 @@ function MOI.get( ) where {T} return MOI.SecondOrderCone(length(b.vars)) end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintSet, diff --git a/src/Bridges/Constraint/soc_to_psd.jl b/src/Bridges/Constraint/soc_to_psd.jl index c2f5806655..461d638758 100644 --- a/src/Bridges/Constraint/soc_to_psd.jl +++ b/src/Bridges/Constraint/soc_to_psd.jl @@ -59,6 +59,7 @@ struct SOCtoPSDBridge{T,F,G} <: SetMapBridge{ } constraint::MOI.ConstraintIndex{F,MOI.PositiveSemidefiniteConeTriangle} end + function concrete_bridge_type( ::Type{<:SOCtoPSDBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, @@ -71,6 +72,7 @@ end function map_set(::Type{<:SOCtoPSDBridge}, set::MOI.SecondOrderCone) return MOI.PositiveSemidefiniteConeTriangle(MOI.dimension(set)) end + function inverse_map_set( ::Type{<:SOCtoPSDBridge}, set::MOI.PositiveSemidefiniteConeTriangle, @@ -81,17 +83,20 @@ end function map_function(::Type{<:SOCtoPSDBridge{T}}, func) where {T} return _SOCtoPSDaff(T, func, MOIU.eachscalar(func)[1]) end + function inverse_map_function(::Type{<:SOCtoPSDBridge}, func) scalars = MOIU.eachscalar(func) dim = MOIU.side_dimension_for_vectorized_dimension(length(scalars)) return scalars[trimap.(1, 1:dim)] end + function adjoint_map_function(::Type{<:SOCtoPSDBridge{T}}, func) where {T} scalars = MOIU.eachscalar(func) dim = MOIU.side_dimension_for_vectorized_dimension(length(scalars)) tdual = sum(i -> func[trimap(i, i)], 1:dim) return MOIU.operate(vcat, T, tdual, func[trimap.(2:dim, 1)] * 2) end + function inverse_adjoint_map_function( ::Type{<:SOCtoPSDBridge{T}}, func, @@ -144,6 +149,7 @@ struct RSOCtoPSDBridge{T,F,G} <: SetMapBridge{ } constraint::MOI.ConstraintIndex{F,MOI.PositiveSemidefiniteConeTriangle} end + function concrete_bridge_type( ::Type{<:RSOCtoPSDBridge{T}}, G::Type{<:MOI.AbstractVectorFunction}, @@ -158,6 +164,7 @@ end function map_set(::Type{<:RSOCtoPSDBridge}, set::MOI.RotatedSecondOrderCone) return MOI.PositiveSemidefiniteConeTriangle(MOI.dimension(set) - 1) end + function inverse_map_set( ::Type{<:RSOCtoPSDBridge}, set::MOI.PositiveSemidefiniteConeTriangle, @@ -170,6 +177,7 @@ function map_function(::Type{<:RSOCtoPSDBridge{T}}, func) where {T} h = MOIU.operate!(*, T, scalars[2], convert(T, 2)) return _SOCtoPSDaff(T, scalars[[1; 3:length(scalars)]], h) end + function inverse_map_function(::Type{<:RSOCtoPSDBridge{T}}, func) where {T} scalars = MOIU.eachscalar(func) dim = MOIU.side_dimension_for_vectorized_dimension(length(scalars)) @@ -178,12 +186,14 @@ function inverse_map_function(::Type{<:RSOCtoPSDBridge{T}}, func) where {T} u = MOIU.operate!(/, T, scalars[3], convert(T, 2)) return MOIU.operate(vcat, T, t, u, scalars[[trimap(1, i) for i in 2:dim]]) end + function adjoint_map_function(::Type{<:RSOCtoPSDBridge{T}}, func) where {T} scalars = MOIU.eachscalar(func) dim = MOIU.side_dimension_for_vectorized_dimension(length(scalars)) udual = sum(i -> func[trimap(i, i)], 2:dim) return MOIU.operate(vcat, T, func[1], 2udual, func[trimap.(2:dim, 1)] * 2) end + function inverse_adjoint_map_function( ::Type{<:RSOCtoPSDBridge{T}}, func, diff --git a/src/Bridges/Constraint/square.jl b/src/Bridges/Constraint/square.jl index 9d3a7a89a4..93740fe9e5 100644 --- a/src/Bridges/Constraint/square.jl +++ b/src/Bridges/Constraint/square.jl @@ -77,6 +77,7 @@ struct SquareBridge{ triangle::CI{F,TT} sym::Vector{Pair{Tuple{Int,Int},CI{G,MOI.EqualTo{T}}}} end + function bridge_constraint( ::Type{SquareBridge{T,F,G,TT,ST}}, model::MOI.ModelLike, @@ -140,14 +141,17 @@ function MOI.supports_constraint( ) where {T} return true end + function MOIB.added_constrained_variable_types(::Type{<:SquareBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types( ::Type{SquareBridge{T,F,G,TT,ST}}, ) where {T,F,G,TT,ST} return [(F, TT), (G, MOI.EqualTo{T})] end + function concrete_bridge_type( ::Type{<:SquareBridge{T}}, F::Type{<:MOI.AbstractVectorFunction}, @@ -166,18 +170,21 @@ function MOI.get( ) where {T,F,G,TT} return 1 end + function MOI.get( bridge::SquareBridge{T,F,G}, ::MOI.NumberOfConstraints{G,MOI.EqualTo{T}}, ) where {T,F,G} return length(bridge.sym) end + function MOI.get( bridge::SquareBridge{T,F,G,TT}, ::MOI.ListOfConstraintIndices{F,TT}, ) where {T,F,G,TT} return [bridge.triangle] end + function MOI.get( bridge::SquareBridge{T,F,G}, ::MOI.ListOfConstraintIndices{G,MOI.EqualTo{T}}, @@ -222,9 +229,11 @@ function MOI.get( end return MOIU.vectorize(sqr) end + function MOI.get(::MOI.ModelLike, ::MOI.ConstraintSet, bridge::SquareBridge) return bridge.square_set end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintPrimal, @@ -242,6 +251,7 @@ function MOI.get( end return sqr end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintDual, diff --git a/src/Bridges/Constraint/vectorize.jl b/src/Bridges/Constraint/vectorize.jl index 5a1e278d5b..0426d7c8c3 100644 --- a/src/Bridges/Constraint/vectorize.jl +++ b/src/Bridges/Constraint/vectorize.jl @@ -86,7 +86,8 @@ function MOI.get( end function MOI.delete(model::MOI.ModelLike, bridge::VectorizeBridge) - return MOI.delete(model, bridge.vector_constraint) + MOI.delete(model, bridge.vector_constraint) + return end # Attributes, Bridge acting as a constraint diff --git a/src/Bridges/Constraint/zero_one.jl b/src/Bridges/Constraint/zero_one.jl index 98747e6cad..030afdece2 100644 --- a/src/Bridges/Constraint/zero_one.jl +++ b/src/Bridges/Constraint/zero_one.jl @@ -68,7 +68,8 @@ end function MOI.delete(model::MOI.ModelLike, bridge::ZeroOneBridge) MOI.delete(model, bridge.interval_index) - return MOI.delete(model, bridge.integer_index) + MOI.delete(model, bridge.integer_index) + return end function MOI.get( @@ -94,7 +95,8 @@ function MOI.set( value, ) where {T} MOI.set(model, attr, bridge.integer_index, value) - return MOI.set(model, attr, bridge.interval_index, value) + MOI.set(model, attr, bridge.interval_index, value) + return end # Attributes, Bridge acting as a model diff --git a/src/Bridges/Objective/Objective.jl b/src/Bridges/Objective/Objective.jl index 16621c5bb0..e10c925ad1 100644 --- a/src/Bridges/Objective/Objective.jl +++ b/src/Bridges/Objective/Objective.jl @@ -18,6 +18,7 @@ include("single_bridge_optimizer.jl") include("functionize.jl") const Functionize{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{FunctionizeBridge{T},OT} + include("slack.jl") const Slack{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SlackBridge{T},OT} diff --git a/src/Bridges/Objective/bridge.jl b/src/Bridges/Objective/bridge.jl index f0d1707156..16e42060b1 100644 --- a/src/Bridges/Objective/bridge.jl +++ b/src/Bridges/Objective/bridge.jl @@ -7,9 +7,11 @@ bridges. abstract type AbstractBridge <: MOIB.AbstractBridge end """ - bridge_objective(BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, - model::MOI.ModelLike, - func::MOI.AbstractScalarFunction) + bridge_objective( + BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, + model::MOI.ModelLike, + func::MOI.AbstractScalarFunction, + ) Bridge the objective function `func` using bridge `BT` to `model` and returns a bridge object of type `BT`. The bridge type `BT` should be a concrete type, @@ -28,9 +30,12 @@ function bridge_objective( end """ - function MOI.set(model::MOI.ModelLike, ::MOI.ObjectiveSense, - bridge::MOI.Bridges.Objective.AbstractBridge, - sense::MOI.ObjectiveSense) + function MOI.set( + model::MOI.ModelLike, + ::MOI.ObjectiveSense, + bridge::MOI.Bridges.Objective.AbstractBridge, + sense::MOI.ObjectiveSense, + ) Informs `bridge` that the objective sense is changed to `sense`. If changing the objective sense is not supported, the bridge should not implement this @@ -53,11 +58,13 @@ function MOI.set( end """ - function MOI.get(model::MOI.ModelLike, attr::MOI.ObjectiveFunction, - bridge::MOI.Bridges.Objective.AbstractBridge) + function MOI.get( + model::MOI.ModelLike, + attr::MOI.ObjectiveFunction, + bridge::MOI.Bridges.Objective.AbstractBridge, + ) -Return the objective function object bridged by `bridge` for the model -`model`. +Return the objective function object bridged by `bridge` for the model `model`. """ function MOI.get( ::MOI.ModelLike, @@ -73,7 +80,10 @@ function MOI.get( end """ - function MOI.delete(model::MOI.ModelLike, bridge::MOI.Bridges.Objective.AbstractBridge) + function MOI.delete( + model::MOI.ModelLike, + bridge::MOI.Bridges.Objective.AbstractBridge, + ) Delete any variable or constraint added by `bridge`. """ @@ -88,7 +98,8 @@ end """ supports_objective_function( BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, - F::Type{<:MOI.AbstractScalarFunction})::Bool + F::Type{<:MOI.AbstractScalarFunction}, + )::Bool Return a `Bool` indicating whether the bridges of type `BT` support bridging objective functions of type `F`. @@ -101,8 +112,10 @@ function supports_objective_function( end """ - added_constrained_variable_types(BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, - F::Type{<:MOI.AbstractScalarFunction}) + added_constrained_variable_types( + BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, + F::Type{<:MOI.AbstractScalarFunction}, + ) Return a list of the types of constrained variables that bridges of type `BT` add for bridging objective functions of type `F`. This fallbacks to @@ -118,13 +131,15 @@ function MOIB.added_constrained_variable_types( end """ - added_constraint_types(BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, - F::Type{<:MOI.AbstractScalarFunction}) + added_constraint_types( + BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, + F::Type{<:MOI.AbstractScalarFunction}, + ) -Return a list of the types of constraints that bridges of type `BT` add -for bridging objective functions of type `F`. This fallbacks to -`added_constraint_types(concrete_bridge_type(BT, S))` -so bridges should not implement this method. +Return a list of the types of constraints that bridges of type `BT` add for +bridging objective functions of type `F`. This fallbacks to +`added_constraint_types(concrete_bridge_type(BT, S))` so bridges should not +implement this method. """ function MOIB.added_constraint_types( BT::Type{<:AbstractBridge}, @@ -134,13 +149,15 @@ function MOIB.added_constraint_types( end """ - set_objective_function_type(BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, - F::Type{<:MOI.AbstractScalarFunction}) + set_objective_function_type( + BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, + F::Type{<:MOI.AbstractScalarFunction}, + ) -Return the type of objective function that bridges of type `BT` set -for bridging objective functions of type `F`. This fallbacks to -`set_objective_function_type(concrete_bridge_type(BT, F))` -so bridges should not implement this method. +Return the type of objective function that bridges of type `BT` set for bridging +objective functions of type `F`. This fallbacks to +`set_objective_function_type(concrete_bridge_type(BT, F))` so bridges should not +implement this method. """ function MOIB.set_objective_function_type( BT::Type{<:AbstractBridge}, @@ -150,8 +167,10 @@ function MOIB.set_objective_function_type( end """ - concrete_bridge_type(BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, - F::Type{<:MOI.AbstractScalarFunction})::DataType + concrete_bridge_type( + BT::Type{<:MOI.Bridges.Objective.AbstractBridge}, + F::Type{<:MOI.AbstractScalarFunction}, + )::DataType Return the concrete type of the bridge supporting objective functions of type `F`. This function can only be called if `MOI.supports_objective_function(BT, F)` diff --git a/src/Bridges/Objective/functionize.jl b/src/Bridges/Objective/functionize.jl index f86c984c99..79749f1e97 100644 --- a/src/Bridges/Objective/functionize.jl +++ b/src/Bridges/Objective/functionize.jl @@ -5,6 +5,7 @@ The `FunctionizeBridge` converts a `SingleVariable` objective into a `ScalarAffineFunction{T}` objective. """ struct FunctionizeBridge{T} <: AbstractBridge end + function bridge_objective( ::Type{FunctionizeBridge{T}}, model::MOI.ModelLike, @@ -21,12 +22,15 @@ function supports_objective_function( ) return true end + function MOIB.added_constrained_variable_types(::Type{<:FunctionizeBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types(::Type{<:FunctionizeBridge}) return Tuple{DataType,DataType}[] end + function MOIB.set_objective_function_type( ::Type{FunctionizeBridge{T}}, ) where {T} @@ -34,16 +38,17 @@ function MOIB.set_objective_function_type( end # Attributes, Bridge acting as a model -function MOI.get(bridge::FunctionizeBridge, ::MOI.NumberOfVariables) +function MOI.get(::FunctionizeBridge, ::MOI.NumberOfVariables) return 0 end -function MOI.get(bridge::FunctionizeBridge, ::MOI.ListOfVariableIndices) + +function MOI.get(::FunctionizeBridge, ::MOI.ListOfVariableIndices) return MOI.VariableIndex[] end # No variables or constraints are created in this bridge so there is nothing to # delete. -function MOI.delete(model::MOI.ModelLike, bridge::FunctionizeBridge) end +MOI.delete(model::MOI.ModelLike, bridge::FunctionizeBridge) = nothing function MOI.set( ::MOI.ModelLike, @@ -53,19 +58,22 @@ function MOI.set( ) # `FunctionizeBridge` is sense agnostic, therefore, we don't need to change # anything. + return end + function MOI.get( model::MOI.ModelLike, attr::MOIB.ObjectiveFunctionValue{MOI.SingleVariable}, - bridge::FunctionizeBridge{T}, + ::FunctionizeBridge{T}, ) where {T} F = MOI.ScalarAffineFunction{T} return MOI.get(model, MOIB.ObjectiveFunctionValue{F}(attr.result_index)) end + function MOI.get( model::MOI.ModelLike, - attr::MOI.ObjectiveFunction{MOI.SingleVariable}, - bridge::FunctionizeBridge{T}, + ::MOI.ObjectiveFunction{MOI.SingleVariable}, + ::FunctionizeBridge{T}, ) where {T} F = MOI.ScalarAffineFunction{T} func = MOI.get(model, MOI.ObjectiveFunction{F}()) diff --git a/src/Bridges/Objective/map.jl b/src/Bridges/Objective/map.jl index d6e854e935..a2e148b64b 100644 --- a/src/Bridges/Objective/map.jl +++ b/src/Bridges/Objective/map.jl @@ -8,6 +8,7 @@ mutable struct Map <: AbstractDict{MOI.ObjectiveFunction,AbstractBridge} bridges::Dict{MOI.ObjectiveFunction,AbstractBridge} function_type::Union{Nothing,Type{<:MOI.AbstractScalarFunction}} end + function Map() return Map(Dict{MOI.ObjectiveFunction,AbstractBridge}(), nothing) end @@ -15,18 +16,25 @@ end # Implementation of `AbstractDict` interface. Base.isempty(map::Map) = isempty(map.bridges) + function Base.empty!(map::Map) empty!(map.bridges) - return map.function_type = nothing + map.function_type = nothing + return map end + function Base.haskey(map::Map, attr::MOI.ObjectiveFunction) return haskey(map.bridges, attr) end + function Base.getindex(map::Map, attr::MOI.ObjectiveFunction) return map.bridges[attr] end + Base.length(map::Map) = length(map.bridges) + Base.values(map::Map) = values(map.bridges) + Base.iterate(map::Map, args...) = iterate(map.bridges, args...) # Custom interface for information needed by `AbstractBridgeOptimizer`s that is @@ -51,10 +59,13 @@ function root_bridge(map::Map) end """ - add_key_for_bridge(map::Map, bridge::AbstractBridge, - func::MOI.AbstractScalarFunction) + add_key_for_bridge( + map::Map, + bridge::AbstractBridge, + func::MOI.AbstractScalarFunction, + ) -Stores the mapping `atttr => bridge` where `attr` is +Stores the mapping `attr => bridge` where `attr` is `MOI.ObjectiveFunction{typeof(func)}()` and set [`function_type`](@ref) to `typeof(func)`. """ @@ -77,10 +88,17 @@ Empty version of [`Map`](@ref). It is used by not bridge any objective function. """ struct EmptyMap <: AbstractDict{MOI.ObjectiveFunction,AbstractBridge} end + Base.isempty(::EmptyMap) = true + function Base.empty!(::EmptyMap) end + Base.length(::EmptyMap) = 0 + Base.haskey(::EmptyMap, ::MOI.ObjectiveFunction) = false + Base.values(::EmptyMap) = MOIU.EmptyVector{AbstractBridge}() + Base.iterate(::EmptyMap) = nothing + function_type(::EmptyMap) = nothing diff --git a/src/Bridges/Objective/single_bridge_optimizer.jl b/src/Bridges/Objective/single_bridge_optimizer.jl index fd4fa09e04..8762843de7 100644 --- a/src/Bridges/Objective/single_bridge_optimizer.jl +++ b/src/Bridges/Objective/single_bridge_optimizer.jl @@ -11,19 +11,23 @@ mutable struct SingleBridgeOptimizer{BT<:AbstractBridge,OT<:MOI.ModelLike} <: model::OT map::Map # `MOI.ObjectiveFunction` -> objective bridge end + function SingleBridgeOptimizer{BT}(model::OT) where {BT,OT<:MOI.ModelLike} return SingleBridgeOptimizer{BT,OT}(model, Map()) end -function bridges(bridge::MOI.Bridges.AbstractBridgeOptimizer) +function bridges(::MOI.Bridges.AbstractBridgeOptimizer) return EmptyMap() end + bridges(bridge::SingleBridgeOptimizer) = bridge.map MOIB.supports_constraint_bridges(::SingleBridgeOptimizer) = false + function MOIB.is_bridged(::SingleBridgeOptimizer, ::Type{<:MOI.AbstractSet}) return false end + function MOIB.is_bridged( ::SingleBridgeOptimizer, ::Type{<:MOI.AbstractFunction}, @@ -31,18 +35,21 @@ function MOIB.is_bridged( ) return false end + function MOIB.supports_bridging_objective_function( ::SingleBridgeOptimizer{BT}, F::Type{<:MOI.AbstractScalarFunction}, ) where {BT} return supports_objective_function(BT, F) end + function MOIB.is_bridged( b::SingleBridgeOptimizer, F::Type{<:MOI.AbstractScalarFunction}, ) return MOIB.supports_bridging_objective_function(b, F) end + function MOIB.bridge_type( ::SingleBridgeOptimizer{BT}, ::Type{<:MOI.AbstractScalarFunction}, diff --git a/src/Bridges/Objective/slack.jl b/src/Bridges/Objective/slack.jl index 44307e4503..a395c40b29 100644 --- a/src/Bridges/Objective/slack.jl +++ b/src/Bridges/Objective/slack.jl @@ -21,6 +21,7 @@ struct SlackBridge{ MOI.ConstraintIndex{F,MOI.GreaterThan{T}}, } end + function bridge_objective( ::Type{SlackBridge{T,F,G}}, model::MOI.ModelLike, @@ -50,19 +51,24 @@ function supports_objective_function( ) return false end + function supports_objective_function( ::Type{<:SlackBridge}, ::Type{<:MOI.AbstractScalarFunction}, ) return true end + MOIB.added_constrained_variable_types(::Type{<:SlackBridge}) = Tuple{DataType}[] + function MOIB.added_constraint_types(::Type{<:SlackBridge{T,F}}) where {T,F} return [(F, MOI.GreaterThan{T}), (F, MOI.LessThan{T})] end + function MOIB.set_objective_function_type(::Type{<:SlackBridge}) return MOI.SingleVariable end + function concrete_bridge_type( ::Type{<:SlackBridge{T}}, G::Type{<:MOI.AbstractScalarFunction}, @@ -72,18 +78,21 @@ function concrete_bridge_type( end # Attributes, Bridge acting as a model -function MOI.get(bridge::SlackBridge, ::MOI.NumberOfVariables) +function MOI.get(::SlackBridge, ::MOI.NumberOfVariables) return 1 end + function MOI.get(bridge::SlackBridge, ::MOI.ListOfVariableIndices) return [bridge.slack] end + function MOI.get( bridge::SlackBridge{T,F}, ::MOI.NumberOfConstraints{F,S}, ) where {T,F,S<:Union{MOI.GreaterThan{T},MOI.LessThan{T}}} return bridge.constraint isa MOI.ConstraintIndex{F,S} ? 1 : 0 end + function MOI.get( bridge::SlackBridge{T,F}, ::MOI.ListOfConstraintIndices{F,S}, @@ -97,7 +106,8 @@ end function MOI.delete(model::MOI.ModelLike, bridge::SlackBridge) MOI.delete(model, bridge.constraint) - return MOI.delete(model, bridge.slack) + MOI.delete(model, bridge.slack) + return end function MOI.get( @@ -120,9 +130,10 @@ function MOI.get( MOI.constant(MOI.get(model, MOI.ConstraintSet(), bridge.constraint)) return obj_slack_constant + slack - constant end + function MOI.get( model::MOI.ModelLike, - attr::MOI.ObjectiveFunction{G}, + ::MOI.ObjectiveFunction{G}, bridge::SlackBridge{T,F,G}, ) where {T,F,G<:MOI.AbstractScalarFunction} func = MOI.get(model, MOI.ConstraintFunction(), bridge.constraint) diff --git a/src/Bridges/Variable/Variable.jl b/src/Bridges/Variable/Variable.jl index 7a8b602f01..7981b78e99 100644 --- a/src/Bridges/Variable/Variable.jl +++ b/src/Bridges/Variable/Variable.jl @@ -30,20 +30,26 @@ end # Variable bridges include("zeros.jl") const Zeros{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{ZerosBridge{T},OT} + include("free.jl") const Free{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{FreeBridge{T},OT} + include("flip_sign.jl") const NonposToNonneg{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{NonposToNonnegBridge{T},OT} + include("vectorize.jl") const Vectorize{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{VectorizeBridge{T},OT} + include("soc_to_rsoc.jl") const SOCtoRSOC{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{SOCtoRSOCBridge{T},OT} + include("rsoc_to_soc.jl") const RSOCtoSOC{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoSOCBridge{T},OT} + include("rsoc_to_psd.jl") const RSOCtoPSD{T,OT<:MOI.ModelLike} = SingleBridgeOptimizer{RSOCtoPSDBridge{T},OT} diff --git a/src/Bridges/Variable/bridge.jl b/src/Bridges/Variable/bridge.jl index f3d9089be5..87adf91ca7 100644 --- a/src/Bridges/Variable/bridge.jl +++ b/src/Bridges/Variable/bridge.jl @@ -16,22 +16,28 @@ struct IndexInVector end """ - bridge_constrained_variable(BT::Type{<:AbstractBridge}, model::MOI.ModelLike, - set::MOI.AbstractSet) + bridge_constrained_variable( + BT::Type{<:AbstractBridge}, + model::MOI.ModelLike, + set::MOI.AbstractSet, + ) -Bridge the constrained variable in `set` using bridge `BT` to `model` and returns -a bridge object of type `BT`. The bridge type `BT` should be a concrete type, -that is, all the type parameters of the bridge should be set. Use +Bridge the constrained variable in `set` using bridge `BT` to `model` and +returns a bridge object of type `BT`. The bridge type `BT` should be a concrete +type, that is, all the type parameters of the bridge should be set. Use [`concrete_bridge_type`](@ref) to obtain a concrete type for given set types. """ function bridge_constrained_variable end """ - function MOI.get(model::MOI.ModelLike, attr::MOI.AbstractVariableAttribute, - bridge::AbstractBridge) + function MOI.get( + model::MOI.ModelLike, + attr::MOI.AbstractVariableAttribute, + bridge::AbstractBridge, + ) -Return the value of the attribute `attr` of the model `model` for the -variable bridged by `bridge`. +Return the value of the attribute `attr` of the model `model` for the variable +bridged by `bridge`. """ function MOI.get( ::MOI.ModelLike, @@ -46,11 +52,15 @@ function MOI.get( end """ - function MOI.get(model::MOI.ModelLike, attr::MOI.AbstractVariableAttribute, - bridge::AbstractBridge, i::IndexInVector) + function MOI.get( + model::MOI.ModelLike, + attr::MOI.AbstractVariableAttribute, + bridge::AbstractBridge, + i::IndexInVector, + ) -Return the value of the attribute `attr` of the model `model` for the -variable at index `i` in the vector of variables bridged by `bridge`. +Return the value of the attribute `attr` of the model `model` for the variable +at index `i` in the vector of variables bridged by `bridge`. """ function MOI.get( ::MOI.ModelLike, @@ -66,8 +76,11 @@ function MOI.get( end """ - MOI.supports(model::MOI.ModelLike, attr::MOI.AbstractVariableAttribute, - BT::Type{<:AbstractBridge}) + MOI.supports( + model::MOI.ModelLike, + attr::MOI.AbstractVariableAttribute, + BT::Type{<:AbstractBridge}, + ) Return a `Bool` indicating whether `BT` supports setting `attr` to `model`. """ @@ -80,11 +93,16 @@ function MOI.supports( end """ - function MOI.set(model::MOI.ModelLike, attr::MOI.AbstractVariableAttribute, - bridge::AbstractBridge, value[, ::IndexInVector]) + function MOI.set( + model::MOI.ModelLike, + attr::MOI.AbstractVariableAttribute, + bridge::AbstractBridge, + value[, + ::IndexInVector], + ) -Return the value of the attribute `attr` of the model `model` for the -variable bridged by `bridge`. +Return the value of the attribute `attr` of the model `model` for the variable +bridged by `bridge`. """ function MOI.set( model::MOI.ModelLike, @@ -101,8 +119,10 @@ function MOI.set( end """ - supports_constrained_variable(::Type{<:AbstractBridge}, - ::Type{<:MOI.AbstractSet})::Bool + supports_constrained_variable( + ::Type{<:AbstractBridge}, + ::Type{<:MOI.AbstractSet}, + )::Bool Return a `Bool` indicating whether the bridges of type `BT` support bridging constrained variables in `S`. @@ -229,23 +249,30 @@ function concrete_bridge_type( end """ - unbridged_map(bridge::MOI.Bridges.Variable.AbstractBridge, - vi::MOI.VariableIndex) + unbridged_map( + bridge::MOI.Bridges.Variable.AbstractBridge, + vi::MOI.VariableIndex, + ) For a bridged variable in a scalar set, return a tuple of pairs mapping the variables created by the bridge to an affine expression in terms of the bridged variable `vi`. - unbridged_map(bridge::MOI.Bridges.Variable.AbstractBridge, - vis::Vector{MOI.VariableIndex}) + unbridged_map( + bridge::MOI.Bridges.Variable.AbstractBridge, + vis::Vector{MOI.VariableIndex}, + ) For a bridged variable in a vector set, return a tuple of pairs mapping the variables created by the bridge to an affine expression in terms of the bridged variable `vis`. If this method is not implemented, it falls back to calling the following method for every variable of `vis`. - unbridged_map(bridge::MOI.Bridges.Variable.AbstractBridge, - vi::MOI.VariableIndex, i::IndexInVector) + unbridged_map( + bridge::MOI.Bridges.Variable.AbstractBridge, + vi::MOI.VariableIndex, + i::IndexInVector, + ) For a bridged variable in a vector set, return a tuple of pairs mapping the variables created by the bridge to an affine expression in terms of the bridged @@ -262,7 +289,7 @@ function unbridged_map(bridge::AbstractBridge, vis::Vector{MOI.VariableIndex}) for (i, vi) in enumerate(vis) vi_mappings = unbridged_map(bridge, vi, IndexInVector(i)) if vi_mappings === nothing - return nothing + return end for mapping in vi_mappings push!(mappings, mapping) diff --git a/src/Bridges/Variable/flip_sign.jl b/src/Bridges/Variable/flip_sign.jl index 12869a791d..e2cf917f06 100644 --- a/src/Bridges/Variable/flip_sign.jl +++ b/src/Bridges/Variable/flip_sign.jl @@ -15,11 +15,13 @@ function supports_constrained_variable( ) where {T,S1<:MOI.AbstractVectorSet} return true end + function MOIB.added_constrained_variable_types( ::Type{<:FlipSignBridge{T,S1,S2}}, ) where {T,S1,S2} return [(S2,)] end + function MOIB.added_constraint_types(::Type{<:FlipSignBridge}) return Tuple{DataType,DataType}[] end @@ -28,15 +30,18 @@ end function MOI.get(bridge::FlipSignBridge, ::MOI.NumberOfVariables) return length(bridge.flipped_variables) end + function MOI.get(bridge::FlipSignBridge, ::MOI.ListOfVariableIndices) return bridge.flipped_variables end + function MOI.get( ::FlipSignBridge{T,S1,S2}, ::MOI.NumberOfConstraints{MOI.VectorOfVariables,S2}, ) where {T,S1,S2<:MOI.AbstractVectorSet} return 1 end + function MOI.get( bridge::FlipSignBridge{T,S1,S2}, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,S2}, @@ -46,7 +51,8 @@ end # References function MOI.delete(model::MOI.ModelLike, bridge::FlipSignBridge) - return MOI.delete(model, bridge.flipped_variables) + MOI.delete(model, bridge.flipped_variables) + return end function MOI.delete( @@ -55,13 +61,14 @@ function MOI.delete( i::IndexInVector, ) MOI.delete(model, bridge.flipped_variables[i.value]) - return deleteat!(bridge.flipped_variables, i.value) + deleteat!(bridge.flipped_variables, i.value) + return end # Attributes, Bridge acting as a constraint function MOI.get( - model::MOI.ModelLike, - attr::MOI.ConstraintSet, + ::MOI.ModelLike, + ::MOI.ConstraintSet, bridge::FlipSignBridge{T,S1}, ) where {T,S1<:MOI.AbstractVectorSet} return S1(length(bridge.flipped_variables)) @@ -91,6 +98,7 @@ function MOIB.bridged_function( func = MOI.SingleVariable(bridge.flipped_variables[i.value]) return MOIU.operate(-, T, func) end + function unbridged_map( bridge::FlipSignBridge{T}, vi::MOI.VariableIndex, @@ -107,6 +115,7 @@ function MOI.supports( ) return MOI.supports(model, attr, MOI.VariableIndex) end + function MOI.set( model::MOI.ModelLike, attr::MOI.VariablePrimalStart, @@ -114,12 +123,13 @@ function MOI.set( value, i::IndexInVector, ) - return MOI.set(model, attr, bridge.flipped_variables[i.value], -value) + MOI.set(model, attr, bridge.flipped_variables[i.value], -value) + return end """ - NonposToNonnegBridge{T, F<:MOI.AbstractVectorFunction, G<:MOI.AbstractVectorFunction} <: - FlipSignBridge{T, MOI.Nonpositives, MOI.Nonnegatives, F, G} + NonposToNonnegBridge{T} <: + FlipSignBridge{T, MOI.Nonpositives, MOI.Nonnegatives} Transforms constrained variables in `Nonpositives` into constrained variables in `Nonnegatives`. @@ -132,6 +142,7 @@ struct NonposToNonnegBridge{T} <: MOI.Nonnegatives, } end + function bridge_constrained_variable( ::Type{NonposToNonnegBridge{T}}, model::MOI.ModelLike, diff --git a/src/Bridges/Variable/free.jl b/src/Bridges/Variable/free.jl index d564d96832..5c22dbb7ab 100644 --- a/src/Bridges/Variable/free.jl +++ b/src/Bridges/Variable/free.jl @@ -8,6 +8,7 @@ struct FreeBridge{T} <: AbstractBridge variables::Vector{MOI.VariableIndex} constraint::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.Nonnegatives} end + function bridge_constrained_variable( ::Type{FreeBridge{T}}, model::MOI.ModelLike, @@ -23,9 +24,11 @@ end function supports_constrained_variable(::Type{<:FreeBridge}, ::Type{MOI.Reals}) return true end + function MOIB.added_constrained_variable_types(::Type{<:FreeBridge}) return [(MOI.Nonnegatives,)] end + function MOIB.added_constraint_types(::Type{FreeBridge{T}}) where {T} return Tuple{DataType,DataType}[] end @@ -34,15 +37,18 @@ end function MOI.get(bridge::FreeBridge, ::MOI.NumberOfVariables) return length(bridge.variables) end + function MOI.get(bridge::FreeBridge, ::MOI.ListOfVariableIndices) return vcat(bridge.variables) end + function MOI.get( - bridge::FreeBridge, + ::FreeBridge, ::MOI.NumberOfConstraints{MOI.VectorOfVariables,MOI.Nonnegatives}, ) return 1 end + function MOI.get( bridge::FreeBridge, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.Nonnegatives}, @@ -52,14 +58,16 @@ end # References function MOI.delete(model::MOI.ModelLike, bridge::FreeBridge) - return MOI.delete(model, bridge.variables) + MOI.delete(model, bridge.variables) + return end function MOI.delete(model::MOI.ModelLike, bridge::FreeBridge, i::IndexInVector) n = div(length(bridge.variables), 2) MOI.delete(model, bridge.variables[i.value]) MOI.delete(model, bridge.variables[n+i.value]) - return deleteat!(bridge.variables, [i.value, n + i.value]) + deleteat!(bridge.variables, [i.value, n + i.value]) + return end # Attributes, Bridge acting as a constraint @@ -73,6 +81,7 @@ function MOI.get( primal = MOI.get(model, attr, bridge.constraint) return primal[1:n] - primal[n.+(1:n)] end + # The transformation is x_free = [I -I] * x # so the transformation of the dual is # y = [I; -I] * y_free @@ -111,6 +120,7 @@ function MOIB.bridged_function( MOI.SingleVariable(bridge.variables[n+i.value]), ) end + # x_free has been replaced by x[i] - x[n + i]. # To undo it we replace x[i] by x_free and x[n + i] by 0. function unbridged_map( @@ -125,6 +135,7 @@ function unbridged_map( return bridge.variables[i.value] => func, bridge.variables[n+i.value] => zero(MOI.ScalarAffineFunction{T}) end + function MOI.supports( model::MOI.ModelLike, attr::MOI.VariablePrimalStart, @@ -132,6 +143,7 @@ function MOI.supports( ) return MOI.supports(model, attr, MOI.VariableIndex) end + function MOI.set( model::MOI.ModelLike, attr::MOI.VariablePrimalStart, @@ -148,5 +160,6 @@ function MOI.set( end n = div(length(bridge.variables), 2) MOI.set(model, attr, bridge.variables[i.value], nonneg) - return MOI.set(model, attr, bridge.variables[n+i.value], nonpos) + MOI.set(model, attr, bridge.variables[n+i.value], nonpos) + return end diff --git a/src/Bridges/Variable/map.jl b/src/Bridges/Variable/map.jl index 534ab22874..beed3c2c89 100644 --- a/src/Bridges/Variable/map.jl +++ b/src/Bridges/Variable/map.jl @@ -31,6 +31,7 @@ mutable struct Map <: AbstractDict{MOI.VariableIndex,AbstractBridge} # Context of constraint bridged by constraint bridges constraint_context::Dict{MOI.ConstraintIndex,Int64} end + function Map() return Map( Int64[], @@ -47,6 +48,7 @@ end # Implementation of `AbstractDict` interface. Base.isempty(map::Map) = all(bridge -> bridge === nothing, map.bridges) + function Base.empty!(map::Map) empty!(map.info) empty!(map.index_in_vector) @@ -63,6 +65,7 @@ function Base.empty!(map::Map) empty!(map.constraint_context) return map end + function bridge_index(map::Map, vi::MOI.VariableIndex) index = map.info[-vi.value] if index ≤ 0 @@ -71,14 +74,17 @@ function bridge_index(map::Map, vi::MOI.VariableIndex) return -vi.value - index + 1 end end + function Base.haskey(map::Map, vi::MOI.VariableIndex) return -length(map.bridges) ≤ vi.value ≤ -1 && map.bridges[bridge_index(map, vi)] !== nothing && map.index_in_vector[-vi.value] != -1 end + function Base.getindex(map::Map, vi::MOI.VariableIndex) return map.bridges[bridge_index(map, vi)] end + function Base.delete!(map::Map, vi::MOI.VariableIndex) if iszero(map.info[-vi.value]) # Delete scalar variable @@ -103,22 +109,23 @@ function Base.delete!(map::Map, vi::MOI.VariableIndex) map.index_in_vector[-vi.value] = -1 return map end + function Base.delete!(map::Map, vis::Vector{MOI.VariableIndex}) - if has_keys(map, vis) - for vi in vis - map.index_in_vector[-vi.value] = -1 - end - map.bridges[bridge_index(map, first(vis))] = nothing - map.sets[bridge_index(map, first(vis))] = nothing - return - else + if !has_keys(map, vis) throw( ArgumentError( "`$vis` is not a valid key vector as returned by `add_keys_for_bridge`.", ), ) end + for vi in vis + map.index_in_vector[-vi.value] = -1 + end + map.bridges[bridge_index(map, first(vis))] = nothing + map.sets[bridge_index(map, first(vis))] = nothing + return map end + function Base.keys(map::Map) return Base.Iterators.Filter( vi -> haskey(map, vi), @@ -128,7 +135,9 @@ function Base.keys(map::Map) ), ) end + Base.length(map::Map) = count(bridge -> bridge !== nothing, map.bridges) + function number_of_variables(map::Map) num = 0 for i in eachindex(map.bridges) @@ -143,11 +152,13 @@ function number_of_variables(map::Map) end return num end + function Base.values(map::Map) # We don't use `filter` as it would compute the resulting array which # is not necessary if the caller just wants to iterater over `values`. return Base.Iterators.Filter(bridge -> bridge !== nothing, map.bridges) end + function Base.iterate(map::Map, state = 1) while state ≤ length(map.bridges) && map.bridges[state] === nothing state += 1 @@ -325,43 +336,41 @@ function add_keys_for_bridge( if iszero(MOI.dimension(set)) return MOI.VariableIndex[], MOI.ConstraintIndex{MOI.VectorOfVariables,typeof(set)}(0) - else - push!(map.parent_index, map.current_context) - bridge_index = Int64(length(map.parent_index)) - push!(map.info, -MOI.dimension(set)) - push!(map.index_in_vector, 1) + end + push!(map.parent_index, map.current_context) + bridge_index = Int64(length(map.parent_index)) + push!(map.info, -MOI.dimension(set)) + push!(map.index_in_vector, 1) + push!(map.bridges, nothing) + push!(map.sets, typeof(set)) + for i in 2:MOI.dimension(set) + push!(map.parent_index, 0) + push!(map.info, i) + push!(map.index_in_vector, i) push!(map.bridges, nothing) - push!(map.sets, typeof(set)) - for i in 2:MOI.dimension(set) - push!(map.parent_index, 0) - push!(map.info, i) - push!(map.index_in_vector, i) - push!(map.bridges, nothing) - push!(map.sets, nothing) - end - map.bridges[bridge_index] = - call_in_context(map, bridge_index, bridge_fun) - variables = MOI.VariableIndex[ - MOI.VariableIndex(-(bridge_index - 1 + i)) for - i in 1:MOI.dimension(set) - ] - if map.unbridged_function !== nothing - mappings = unbridged_map(map.bridges[bridge_index], variables) - if mappings === nothing - map.unbridged_function = nothing - else - for mapping in mappings - push!( - map.unbridged_function, - mapping.first => (bridge_index, mapping.second), - ) - end + push!(map.sets, nothing) + end + map.bridges[bridge_index] = call_in_context(map, bridge_index, bridge_fun) + variables = MOI.VariableIndex[ + MOI.VariableIndex(-(bridge_index - 1 + i)) for + i in 1:MOI.dimension(set) + ] + if map.unbridged_function !== nothing + mappings = unbridged_map(map.bridges[bridge_index], variables) + if mappings === nothing + map.unbridged_function = nothing + else + for mapping in mappings + push!( + map.unbridged_function, + mapping.first => (bridge_index, mapping.second), + ) end end - index = first(variables).value - return variables, - MOI.ConstraintIndex{MOI.VectorOfVariables,typeof(set)}(index) end + index = first(variables).value + return variables, + MOI.ConstraintIndex{MOI.VectorOfVariables,typeof(set)}(index) end """ @@ -370,7 +379,7 @@ end Return `MOI.SingleVariable(vi)` where `vi` is the bridged variable corresponding to `ci`. """ -function function_for(map::Map, ci::MOI.ConstraintIndex{MOI.SingleVariable}) +function function_for(::Map, ci::MOI.ConstraintIndex{MOI.SingleVariable}) return MOI.SingleVariable(MOI.VariableIndex(ci.value)) end @@ -420,19 +429,18 @@ function unbridged_function(map::Map, vi::MOI.VariableIndex) context_func = get(map.unbridged_function, vi, nothing) if context_func === nothing return nothing - else - bridge_index, func = context_func - # If the bridge bridging `vi` has index `bridge_index` or directly or - # indirectly created this bridge then we don't unbridge the variable. - context = map.current_context - while !iszero(context) - if bridge_index == context - return nothing - end - context = map.parent_index[context] + end + bridge_index, func = context_func + # If the bridge bridging `vi` has index `bridge_index` or directly or + # indirectly created this bridge then we don't unbridge the variable. + context = map.current_context + while !iszero(context) + if bridge_index == context + return nothing end - return func + context = map.parent_index[context] end + return func end """ @@ -503,16 +511,27 @@ Empty version of [`Map`](@ref). It is used by not bridge any variable. """ struct EmptyMap <: AbstractDict{MOI.VariableIndex,AbstractBridge} end + Base.isempty(::EmptyMap) = true + function Base.empty!(::EmptyMap) end + Base.length(::EmptyMap) = 0 + Base.keys(::EmptyMap) = MOIU.EmptyVector{MOI.VariableIndex}() + Base.values(::EmptyMap) = MOIU.EmptyVector{AbstractBridge}() + has_bridges(::EmptyMap) = false + number_of_variables(::EmptyMap) = 0 + number_with_set(::EmptyMap, ::Type{<:MOI.AbstractSet}) = 0 + function constraints_with_set(::EmptyMap, S::Type{<:MOI.AbstractSet}) return MOI.ConstraintIndex{MOIU.variable_function_type(S),S}[] end + register_context(::EmptyMap, ::MOI.ConstraintIndex) = nothing + call_in_context(::EmptyMap, ::MOI.ConstraintIndex, f::Function) = f() diff --git a/src/Bridges/Variable/rsoc_to_psd.jl b/src/Bridges/Variable/rsoc_to_psd.jl index 51c8319489..d8d062c308 100644 --- a/src/Bridges/Variable/rsoc_to_psd.jl +++ b/src/Bridges/Variable/rsoc_to_psd.jl @@ -1,8 +1,10 @@ """ RSOCtoPSDBridge{T} <: Bridges.Variable.AbstractBridge -Transforms constrained variables in [`MathOptInterface.RotatedSecondOrderCone`](@ref) -to constrained variables in [`MathOptInterface.PositiveSemidefiniteConeTriangle`](@ref). +Transforms constrained variables in +[`MathOptInterface.RotatedSecondOrderCone`](@ref) +to constrained variables in +[`MathOptInterface.PositiveSemidefiniteConeTriangle`](@ref). """ struct RSOCtoPSDBridge{T} <: AbstractBridge # `t` is `variables[1]` @@ -21,6 +23,7 @@ struct RSOCtoPSDBridge{T} <: AbstractBridge MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}, } end + function bridge_constrained_variable( ::Type{RSOCtoPSDBridge{T}}, model::MOI.ModelLike, @@ -70,9 +73,11 @@ function supports_constrained_variable( ) return true end + function MOIB.added_constrained_variable_types(::Type{<:RSOCtoPSDBridge}) return [(MOI.PositiveSemidefiniteConeTriangle,), (MOI.Nonnegatives,)] end + function MOIB.added_constraint_types(::Type{RSOCtoPSDBridge{T}}) where {T} return [ (MOI.SingleVariable, MOI.EqualTo{T}), @@ -84,15 +89,18 @@ end function MOI.get(bridge::RSOCtoPSDBridge, ::MOI.NumberOfVariables) return length(bridge.variables) end + function MOI.get(bridge::RSOCtoPSDBridge, ::MOI.ListOfVariableIndices) return bridge.variables end + function MOI.get( bridge::RSOCtoPSDBridge, ::MOI.NumberOfConstraints{MOI.VectorOfVariables,S}, ) where {S<:Union{MOI.PositiveSemidefiniteConeTriangle,MOI.Nonnegatives}} return bridge.psd isa MOI.ConstraintIndex{MOI.VectorOfVariables,S} ? 1 : 0 end + function MOI.get( bridge::RSOCtoPSDBridge, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,S}, @@ -103,24 +111,28 @@ function MOI.get( return MOI.ConstraintIndex{MOI.VectorOfVariables,S}[] end end + function MOI.get( bridge::RSOCtoPSDBridge{T}, ::MOI.NumberOfConstraints{MOI.SingleVariable,MOI.EqualTo{T}}, ) where {T} return length(bridge.off_diag) end + function MOI.get( bridge::RSOCtoPSDBridge{T}, ::MOI.ListOfConstraintIndices{MOI.SingleVariable,MOI.EqualTo{T}}, ) where {T} return bridge.off_diag end + function MOI.get( bridge::RSOCtoPSDBridge{T}, ::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}, ) where {T} return length(bridge.diag) end + function MOI.get( bridge::RSOCtoPSDBridge{T}, ::MOI.ListOfConstraintIndices{MOI.ScalarAffineFunction{T},MOI.EqualTo{T}}, @@ -133,7 +145,8 @@ function MOI.delete(model::MOI.ModelLike, bridge::RSOCtoPSDBridge) for ci in bridge.diag MOI.delete(model, ci) end - return MOI.delete(model, bridge.variables) + MOI.delete(model, bridge.variables) + return end # Attributes, Bridge acting as a constraint @@ -155,20 +168,20 @@ function trimap(i::Integer, j::Integer) return div((i - 1) * i, 2) + j end end + function _variable_map(bridge::RSOCtoPSDBridge, i::IndexInVector) if bridge.psd isa MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.Nonnegatives} return i.value + elseif i.value == 1 + return 1 + elseif i.value == 2 + return 3 else - if i.value == 1 - return 1 - elseif i.value == 2 - return 3 - else - return trimap(1, i.value - 1) - end + return trimap(1, i.value - 1) end end + function _variable(bridge::RSOCtoPSDBridge, i::IndexInVector) return bridge.variables[_variable_map(bridge, i)] end @@ -186,6 +199,7 @@ function MOI.get( end return mapped end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintDual, @@ -231,6 +245,7 @@ function MOIB.bridged_function( return convert(MOI.ScalarAffineFunction{T}, func) end end + function unbridged_map( bridge::RSOCtoPSDBridge{T}, vi::MOI.VariableIndex, diff --git a/src/Bridges/Variable/rsoc_to_soc.jl b/src/Bridges/Variable/rsoc_to_soc.jl index 05b104929b..2f6da50d51 100644 --- a/src/Bridges/Variable/rsoc_to_soc.jl +++ b/src/Bridges/Variable/rsoc_to_soc.jl @@ -75,6 +75,7 @@ struct RSOCtoSOCBridge{T} <: AbstractBridge variables::Vector{MOI.VariableIndex} constraint::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.SecondOrderCone} end + function bridge_constrained_variable( ::Type{RSOCtoSOCBridge{T}}, model::MOI.ModelLike, @@ -93,9 +94,11 @@ function supports_constrained_variable( ) return true end + function MOIB.added_constrained_variable_types(::Type{<:RSOCtoSOCBridge}) return [(MOI.SecondOrderCone,)] end + function MOIB.added_constraint_types(::Type{<:RSOCtoSOCBridge}) return Tuple{DataType,DataType}[] end @@ -104,15 +107,18 @@ end function MOI.get(bridge::RSOCtoSOCBridge, ::MOI.NumberOfVariables) return length(bridge.variables) end + function MOI.get(bridge::RSOCtoSOCBridge, ::MOI.ListOfVariableIndices) return bridge.variables end + function MOI.get( - bridge::RSOCtoSOCBridge, + ::RSOCtoSOCBridge, ::MOI.NumberOfConstraints{MOI.VectorOfVariables,MOI.SecondOrderCone}, ) return 1 end + function MOI.get( bridge::RSOCtoSOCBridge, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,MOI.SecondOrderCone}, @@ -122,7 +128,8 @@ end # References function MOI.delete(model::MOI.ModelLike, bridge::RSOCtoSOCBridge) - return MOI.delete(model, bridge.variables) + MOI.delete(model, bridge.variables) + return end # Attributes, Bridge acting as a constraint @@ -158,6 +165,7 @@ function MOIB.bridged_function( ) where {T} return rotate_bridged_function(T, bridge.variables, i) end + function unbridged_map( bridge::RSOCtoSOCBridge{T}, vis::Vector{MOI.VariableIndex}, diff --git a/src/Bridges/Variable/single_bridge_optimizer.jl b/src/Bridges/Variable/single_bridge_optimizer.jl index 8b023b4738..01b3bd35f6 100644 --- a/src/Bridges/Variable/single_bridge_optimizer.jl +++ b/src/Bridges/Variable/single_bridge_optimizer.jl @@ -1,10 +1,11 @@ """ - SingleBridgeOptimizer{BT<:AbstractBridge, OT<:MOI.ModelLike} <: AbstractBridgeOptimizer + SingleBridgeOptimizer{BT<:AbstractBridge, OT<:MOI.ModelLike} <: + AbstractBridgeOptimizer The `SingleBridgeOptimizer` bridges any constrained variables supported by the bridge `BT`. This is in contrast with the [`MathOptInterface.Bridges.LazyBridgeOptimizer`](@ref) -which only bridges the constrained variables that are unsupported by the internal model, -even if they are supported by one of its bridges. +which only bridges the constrained variables that are unsupported by the +internal model, even if they are supported by one of its bridges. !!! note Two bridge optimizers using variable bridges cannot be used together as both @@ -20,6 +21,7 @@ mutable struct SingleBridgeOptimizer{BT<:AbstractBridge,OT<:MOI.ModelLike} <: con_to_name::Dict{MOI.ConstraintIndex,String} name_to_con::Union{Dict{String,MOI.ConstraintIndex},Nothing} end + function SingleBridgeOptimizer{BT}(model::OT) where {BT,OT<:MOI.ModelLike} return SingleBridgeOptimizer{BT,OT}( model, @@ -31,27 +33,32 @@ function SingleBridgeOptimizer{BT}(model::OT) where {BT,OT<:MOI.ModelLike} ) end -function bridges(bridge::MOI.Bridges.AbstractBridgeOptimizer) +function bridges(::MOI.Bridges.AbstractBridgeOptimizer) return EmptyMap() end + bridges(bridge::SingleBridgeOptimizer) = bridge.map MOIB.supports_constraint_bridges(::SingleBridgeOptimizer) = false + function MOIB.supports_bridging_constrained_variable( ::SingleBridgeOptimizer{BT}, S::Type{<:MOI.AbstractSet}, ) where {BT} return supports_constrained_variable(BT, S) end + function MOIB.is_variable_bridged( ::SingleBridgeOptimizer, ::Type{<:MOI.AbstractSet}, ) return true end + function MOIB.is_bridged(b::SingleBridgeOptimizer, S::Type{<:MOI.AbstractSet}) return MOIB.supports_bridging_constrained_variable(b, S) end + function MOIB.is_bridged( ::SingleBridgeOptimizer, ::Type{<:MOI.AbstractFunction}, @@ -59,16 +66,19 @@ function MOIB.is_bridged( ) return false end + function MOIB.is_bridged( ::SingleBridgeOptimizer, ::Type{<:MOI.AbstractScalarFunction}, ) return false end + function MOIB.bridge_type( ::SingleBridgeOptimizer{BT}, ::Type{<:MOI.AbstractSet}, ) where {BT} return BT end + MOIB.bridging_cost(::SingleBridgeOptimizer, args...) = 1.0 diff --git a/src/Bridges/Variable/soc_to_rsoc.jl b/src/Bridges/Variable/soc_to_rsoc.jl index 4b1f9295b6..230b052038 100644 --- a/src/Bridges/Variable/soc_to_rsoc.jl +++ b/src/Bridges/Variable/soc_to_rsoc.jl @@ -10,6 +10,7 @@ struct SOCtoRSOCBridge{T} <: AbstractBridge MOI.RotatedSecondOrderCone, } end + function bridge_constrained_variable( ::Type{SOCtoRSOCBridge{T}}, model::MOI.ModelLike, @@ -28,9 +29,11 @@ function supports_constrained_variable( ) return true end + function MOIB.added_constrained_variable_types(::Type{<:SOCtoRSOCBridge}) return [(MOI.RotatedSecondOrderCone,)] end + function MOIB.added_constraint_types(::Type{<:SOCtoRSOCBridge}) return Tuple{DataType,DataType}[] end @@ -39,15 +42,18 @@ end function MOI.get(bridge::SOCtoRSOCBridge, ::MOI.NumberOfVariables) return length(bridge.variables) end + function MOI.get(bridge::SOCtoRSOCBridge, ::MOI.ListOfVariableIndices) return bridge.variables end + function MOI.get( bridge::SOCtoRSOCBridge, ::MOI.NumberOfConstraints{MOI.VectorOfVariables,MOI.RotatedSecondOrderCone}, ) return 1 end + function MOI.get( bridge::SOCtoRSOCBridge, ::MOI.ListOfConstraintIndices{ @@ -60,7 +66,8 @@ end # References function MOI.delete(model::MOI.ModelLike, bridge::SOCtoRSOCBridge) - return MOI.delete(model, bridge.variables) + MOI.delete(model, bridge.variables) + return end # Attributes, Bridge acting as a constraint @@ -96,6 +103,7 @@ function MOIB.bridged_function( ) where {T} return rotate_bridged_function(T, bridge.variables, i) end + function unbridged_map( bridge::SOCtoRSOCBridge{T}, vis::Vector{MOI.VariableIndex}, diff --git a/src/Bridges/Variable/vectorize.jl b/src/Bridges/Variable/vectorize.jl index ae8c1a2243..adf5cd5bc6 100644 --- a/src/Bridges/Variable/vectorize.jl +++ b/src/Bridges/Variable/vectorize.jl @@ -12,6 +12,7 @@ mutable struct VectorizeBridge{T,S} <: AbstractBridge vector_constraint::MOI.ConstraintIndex{MOI.VectorOfVariables,S} set_constant::T # constant in scalar set end + function bridge_constrained_variable( ::Type{VectorizeBridge{T,S}}, model::MOI.ModelLike, @@ -28,14 +29,17 @@ function supports_constrained_variable( ) where {T} return true end + function MOIB.added_constrained_variable_types( ::Type{VectorizeBridge{T,S}}, ) where {T,S} return [(S,)] end + function MOIB.added_constraint_types(::Type{<:VectorizeBridge}) return Tuple{DataType,DataType}[] end + function concrete_bridge_type( ::Type{<:VectorizeBridge{T}}, S::Type{<:MOIU.ScalarLinearSet{T}}, @@ -44,18 +48,21 @@ function concrete_bridge_type( end # Attributes, Bridge acting as a model -function MOI.get(bridge::VectorizeBridge, ::MOI.NumberOfVariables) +function MOI.get(::VectorizeBridge, ::MOI.NumberOfVariables) return 1 end + function MOI.get(bridge::VectorizeBridge, ::MOI.ListOfVariableIndices) return [bridge.variable] end + function MOI.get( ::VectorizeBridge{T,S}, ::MOI.NumberOfConstraints{MOI.VectorOfVariables,S}, ) where {T,S} return 1 end + function MOI.get( bridge::VectorizeBridge{T,S}, ::MOI.ListOfConstraintIndices{MOI.VectorOfVariables,S}, @@ -65,13 +72,14 @@ end # References function MOI.delete(model::MOI.ModelLike, bridge::VectorizeBridge) - return MOI.delete(model, bridge.variable) + MOI.delete(model, bridge.variable) + return end # Attributes, Bridge acting as a constraint function MOI.get( - model::MOI.ModelLike, + ::MOI.ModelLike, ::MOI.ConstraintSet, bridge::VectorizeBridge{T,S}, ) where {T,S} @@ -79,10 +87,10 @@ function MOI.get( end function MOI.set( - model::MOI.ModelLike, + ::MOI.ModelLike, attr::MOI.ConstraintSet, bridge::VectorizeBridge, - new_set::MOIU.ScalarLinearSet, + ::MOIU.ScalarLinearSet, ) # This would require modifing any constraint which uses the bridged # variable. @@ -112,6 +120,7 @@ function MOI.get( end return y end + function MOI.get( model::MOI.ModelLike, attr::MOI.ConstraintDual, @@ -136,6 +145,7 @@ function MOI.get( end return value end + function MOI.supports( model::MOI.ModelLike, attr::MOI.VariablePrimalStart, @@ -143,19 +153,22 @@ function MOI.supports( ) return MOI.supports(model, attr, MOI.VariableIndex) end + function MOI.set( model::MOI.ModelLike, attr::MOI.VariablePrimalStart, bridge::VectorizeBridge, value, ) - return MOI.set(model, attr, bridge.variable, value - bridge.set_constant) + MOI.set(model, attr, bridge.variable, value - bridge.set_constant) + return end function MOIB.bridged_function(bridge::VectorizeBridge{T}) where {T} func = MOI.SingleVariable(bridge.variable) return MOIU.operate(+, T, func, bridge.set_constant) end + function unbridged_map( bridge::VectorizeBridge{T}, vi::MOI.VariableIndex, diff --git a/src/Bridges/Variable/zeros.jl b/src/Bridges/Variable/zeros.jl index 9be276edb5..c44e94fcb9 100644 --- a/src/Bridges/Variable/zeros.jl +++ b/src/Bridges/Variable/zeros.jl @@ -3,11 +3,13 @@ Transforms constrained variables in [`MathOptInterface.Zeros`](@ref) to zeros, which ends up creating no variables in the underlying model. + The bridged variables are therefore similar to parameters with zero values. Parameters with non-zero value can be created with constrained variables in [`MOI.EqualTo`](@ref) by combining a [`VectorizeBridge`](@ref) and this bridge. The functions cannot be unbridged, given a function, we cannot determine, if the bridged variables were used. + The dual values cannot be determined by the bridge but they can be determined by the bridged optimizer using [`MathOptInterface.Utilities.get_fallback`](@ref) if a `CachingOptimizer` is used (since `ConstraintFunction` cannot be got @@ -16,6 +18,7 @@ as functions cannot be unbridged). struct ZerosBridge{T} <: AbstractBridge n::Int # Number of variables end + function bridge_constrained_variable( ::Type{ZerosBridge{T}}, model::MOI.ModelLike, @@ -27,21 +30,24 @@ end function supports_constrained_variable(::Type{<:ZerosBridge}, ::Type{MOI.Zeros}) return true end + function MOIB.added_constrained_variable_types(::Type{<:ZerosBridge}) return Tuple{DataType}[] end + function MOIB.added_constraint_types(::Type{<:ZerosBridge}) return Tuple{DataType,DataType}[] end # Attributes, Bridge acting as a model MOI.get(bridge::ZerosBridge, ::MOI.NumberOfVariables) = 0 + function MOI.get(bridge::ZerosBridge, ::MOI.ListOfVariableIndices) return MOI.VariableIndex[] end # References -function MOI.delete(::MOI.ModelLike, ::ZerosBridge) end +MOI.delete(::MOI.ModelLike, ::ZerosBridge) = nothing # Attributes, Bridge acting as a constraint @@ -69,6 +75,5 @@ end function MOIB.bridged_function(::ZerosBridge{T}, ::IndexInVector) where {T} return zero(MOI.ScalarAffineFunction{T}) end -function unbridged_map(::ZerosBridge, ::MOI.VariableIndex, ::IndexInVector) - return nothing -end + +unbridged_map(::ZerosBridge, ::MOI.VariableIndex, ::IndexInVector) = nothing diff --git a/src/Bridges/bridge.jl b/src/Bridges/bridge.jl index 6a844cb89e..b5c638e85b 100644 --- a/src/Bridges/bridge.jl +++ b/src/Bridges/bridge.jl @@ -22,7 +22,8 @@ abstract type AbstractBridge end """ MOI.get(b::AbstractBridge, ::MOI.NumberOfConstraints{F, S}) where {F, S} -The number of constraints of the type `F`-in-`S` created by the bridge `b` in the model. +The number of constraints of the type `F`-in-`S` created by the bridge `b` in +the model. """ MOI.get(::AbstractBridge, ::MOI.NumberOfConstraints) = 0 @@ -41,8 +42,11 @@ function MOI.get( end """ - MOI.supports(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute, - BT::Type{<:AbstractBridge}) + MOI.supports( + model::MOI.ModelLike, + attr::MOI.AbstractConstraintAttribute, + BT::Type{<:AbstractBridge}, + ) Return a `Bool` indicating whether `BT` supports setting `attr` to `model`. """ @@ -55,8 +59,11 @@ function MOI.supports( end """ - function MOI.get(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute, - bridge::AbstractBridge) + function MOI.get( + model::MOI.ModelLike, + attr::MOI.AbstractConstraintAttribute, + bridge::AbstractBridge, + ) Return the value of the attribute `attr` of the model `model` for the constraint bridged by `bridge`. @@ -84,8 +91,12 @@ function MOI.get( end """ - function MOI.set(model::MOI.ModelLike, attr::MOI.AbstractConstraintAttribute, - bridge::AbstractBridge, value) + function MOI.set( + model::MOI.ModelLike, + attr::MOI.AbstractConstraintAttribute, + bridge::AbstractBridge, + value, + ) Set the value of the attribute `attr` of the model `model` for the constraint bridged by `bridge`. @@ -104,7 +115,9 @@ function MOI.set( end """ - added_constrained_variable_types(BT::Type{<:Variable.AbstractBridge})::Vector{Tuple{DataType}} + added_constrained_variable_types( + BT::Type{<:Variable.AbstractBridge}, + )::Vector{Tuple{DataType}} Return a list of the types of constrained variables that bridges of concrete type `BT` add. This is used by the [`LazyBridgeOptimizer`](@ref). @@ -112,7 +125,9 @@ type `BT` add. This is used by the [`LazyBridgeOptimizer`](@ref). function added_constrained_variable_types end """ - added_constraint_types(BT::Type{<:Constraint.AbstractBridge})::Vector{Tuple{DataType, DataType}} + added_constraint_types( + BT::Type{<:Constraint.AbstractBridge}, + )::Vector{Tuple{DataType, DataType}} Return a list of the types of constraints that bridges of concrete type `BT` add. This is used by the [`LazyBridgeOptimizer`](@ref). @@ -120,7 +135,9 @@ add. This is used by the [`LazyBridgeOptimizer`](@ref). function added_constraint_types end """ - set_objective_function_type(BT::Type{<:Objective.AbstractBridge})::Type{<:MOI.AbstractScalarFunction} + set_objective_function_type( + BT::Type{<:Objective.AbstractBridge}, + )::Type{<:MOI.AbstractScalarFunction} Return the type of objective function that bridges of concrete type `BT` set. This is used by the [`LazyBridgeOptimizer`](@ref). diff --git a/src/Bridges/bridge_optimizer.jl b/src/Bridges/bridge_optimizer.jl index 8277799b49..d412e6e420 100644 --- a/src/Bridges/bridge_optimizer.jl +++ b/src/Bridges/bridge_optimizer.jl @@ -18,8 +18,11 @@ abstract type AbstractBridgeOptimizer <: MOI.AbstractOptimizer end function supports_constraint_bridges end """ - is_bridged(b::AbstractBridgeOptimizer, F::Type{<:MOI.AbstractFunction}, - S::Type{<:MOI.AbstractSet})::Bool + is_bridged( + b::AbstractBridgeOptimizer, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + )::Bool Return a `Bool` indicating whether `b` tries to bridge `F`-in-`S` constraints instead of passing it as is to its internal model. @@ -29,7 +32,10 @@ instead of passing it as is to its internal model. Return a `Bool` indicating whether `b` tries to bridge constrained variables in `S` instead of passing it as is to its internal model. - is_bridged(b::AbstractBridgeOptimizer, F::Type{<:MOI.AbstractFunction})::Bool + is_bridged( + b::AbstractBridgeOptimizer, + F::Type{<:MOI.AbstractFunction}, + )::Bool Return a `Bool` indicating whether `b` tries to bridge objective functions of type `F` instead of passing it as is to its internal model. @@ -56,6 +62,7 @@ function is_bridged( ) where {F,S} return is_bridged(b, F, S) end + function is_bridged( b::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex{MOI.SingleVariable,S}, @@ -88,16 +95,16 @@ function is_bridged( (is_bridged(b, S) || haskey(Constraint.bridges(b), ci)) ) end + function is_bridged( - b::AbstractBridgeOptimizer, + ::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex{MOI.VectorOfVariables,S}, ) where {S} return ci.value < 0 end """ - is_bridged(b::AbstractBridgeOptimizer, - attr::MOI.ObjectiveFunction) + is_bridged(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveFunction) Return a `Bool` indicating whether `attr` is bridged. The objective function is said to be bridged the objective function attribute passed to `b.model` is @@ -112,7 +119,9 @@ const ObjectiveAttribute = """ supports_bridging_constrained_variable( - ::AbstractBridgeOptimizer, ::Type{<:MOI.AbstractSet}) + ::AbstractBridgeOptimizer, + ::Type{<:MOI.AbstractSet}, + ) Return a `Bool` indicating whether `b` supports bridging constrained variable in `S`. @@ -126,7 +135,9 @@ end """ is_variable_bridged( - b::AbstractBridgeOptimizer, S::Type{<:MOI.AbstractSet}) + b::AbstractBridgeOptimizer, + S::Type{<:MOI.AbstractSet}, + ) Return a `Bool` indicating whether `b` bridges constrained variable in `S` using a variable bridge, assuming `is_bridged(b, S)`. If it returns `false`, @@ -141,8 +152,7 @@ function is_variable_bridged( end """ - is_variable_bridged(b::AbstractBridgeOptimizer, - ci::MOI.ConstraintIndex) + is_variable_bridged(b::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex) Returns whether `ci` is the constraint of a bridged constrained variable. That is, if it was returned by `Variable.add_key_for_bridge` or @@ -150,6 +160,7 @@ is, if it was returned by `Variable.add_key_for_bridge` or `ci.value < 0` as, it can also simply be a constraint on a bridged variable. """ is_variable_bridged(::AbstractBridgeOptimizer, ::MOI.ConstraintIndex) = false + function is_variable_bridged( b::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex{<:Union{MOI.SingleVariable,MOI.VectorOfVariables}}, @@ -164,7 +175,8 @@ end supports_bridging_constraint( b::AbstractBridgeOptimizer, F::Type{<:MOI.AbstractFunction}, - S::Type{<:MOI.AbstractSet})::Bool + S::Type{<:MOI.AbstractSet}, + )::Bool Return a `Bool` indicating whether `b` supports bridging `F`-in-`S` constraints. """ @@ -179,7 +191,8 @@ end """ supports_bridging_objective_function( b::AbstractBridgeOptimizer, - F::Type{<:MOI.AbstractScalarFunction})::Bool + F::Type{<:MOI.AbstractScalarFunction}, + )::Bool Return a `Bool` indicating whether `b` supports bridging objective functions of type `F`. @@ -192,9 +205,11 @@ function supports_bridging_objective_function( end """ - bridge_type(b::AbstractBridgeOptimizer, - F::Type{<:MOI.AbstractFunction}, - S::Type{<:MOI.AbstractSet}) + bridge_type( + b::AbstractBridgeOptimizer, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + ) Return the `AbstractBridge` type to be used to bridge `F`-in-`S` constraints. This function should only be called if `is_bridged(b, F, S)`. @@ -235,7 +250,11 @@ function bridge(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveFunction) end """ - call_in_context(b::AbstractBridgeOptimizer, vi::MOI.VariableIndex, f::Function) + call_in_context( + b::AbstractBridgeOptimizer, + vi::MOI.VariableIndex, + f::Function, + ) Call `f(bridge)` where `vi` is bridged by `bridge` in its context, see [`Variable.call_in_context`](@ref). @@ -249,7 +268,11 @@ function call_in_context( end """ - call_in_context(b::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex, f::Function) + call_in_context( + b::AbstractBridgeOptimizer, + ci::MOI.ConstraintIndex, + f::Function, + ) Call `f(bridge)` where `ci` is bridged by `bridge` in its context, see [`Variable.call_in_context`](@ref). @@ -278,12 +301,15 @@ function _functionize_bridge(b::AbstractBridgeOptimizer, bridge_type) " `$(typeof(b))`.", ) end + function constraint_scalar_functionize_bridge(b::AbstractBridgeOptimizer) return _functionize_bridge(b, Constraint.ScalarFunctionizeBridge) end + function constraint_vector_functionize_bridge(b::AbstractBridgeOptimizer) return _functionize_bridge(b, Constraint.VectorFunctionizeBridge) end + function objective_functionize_bridge(b::AbstractBridgeOptimizer) return _functionize_bridge(b, Objective.FunctionizeBridge) end @@ -298,6 +324,7 @@ function MOI.is_empty(b::AbstractBridgeOptimizer) isempty(Constraint.bridges(b)) && MOI.is_empty(b.model) end + function MOI.empty!(b::AbstractBridgeOptimizer) MOI.empty!(b.model) if Variable.has_bridges(Variable.bridges(b)) @@ -311,8 +338,10 @@ function MOI.empty!(b::AbstractBridgeOptimizer) end empty!(Variable.bridges(b)) empty!(Constraint.bridges(b)) - return empty!(Objective.bridges(b)) + empty!(Objective.bridges(b)) + return end + function MOI.supports( b::AbstractBridgeOptimizer, attr::Union{MOI.AbstractModelAttribute,MOI.AbstractOptimizerAttribute}, @@ -345,7 +374,7 @@ function MOIU.pass_nonvariable_constraints( pass_cons; filter_constraints = filter_constraints, ) - return MOIU.pass_nonvariable_constraints_fallback( + MOIU.pass_nonvariable_constraints_fallback( dest, src, idxmap, @@ -353,11 +382,13 @@ function MOIU.pass_nonvariable_constraints( pass_cons; filter_constraints = filter_constraints, ) + return end -function MOI.copy_to(mock::AbstractBridgeOptimizer, src::MOI.ModelLike; kws...) - return MOIU.automatic_copy_to(mock, src; kws...) +function MOI.copy_to(b::AbstractBridgeOptimizer, src::MOI.ModelLike; kwargs...) + return MOIU.automatic_copy_to(b, src; kwargs...) end + function MOI.supports_incremental_interface( b::AbstractBridgeOptimizer, copy_names::Bool, @@ -373,6 +404,7 @@ function MOI.is_valid(b::AbstractBridgeOptimizer, vi::MOI.VariableIndex) return MOI.is_valid(b.model, vi) end end + function MOI.is_valid( b::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex{F,S}, @@ -389,6 +421,7 @@ function MOI.is_valid( return MOI.is_valid(b.model, ci) end end + function _delete_variables_in_vector_of_variables_constraint( b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex}, @@ -416,6 +449,7 @@ function _delete_variables_in_vector_of_variables_constraint( end end end + function _delete_variables_in_variables_constraints( b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex}, @@ -445,6 +479,7 @@ function _delete_variables_in_variables_constraints( end end end + function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex}) if Constraint.has_bridges(Constraint.bridges(b)) _delete_variables_in_variables_constraints(b, vis) @@ -473,6 +508,7 @@ function MOI.delete(b::AbstractBridgeOptimizer, vis::Vector{MOI.VariableIndex}) MOI.delete(b.model, vis) end end + function MOI.delete(b::AbstractBridgeOptimizer, vi::MOI.VariableIndex) if Constraint.has_bridges(Constraint.bridges(b)) _delete_variables_in_variables_constraints(b, [vi]) @@ -504,6 +540,7 @@ function MOI.delete(b::AbstractBridgeOptimizer, vi::MOI.VariableIndex) MOI.delete(b.model, vi) end end + function MOI.delete(b::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex) if is_bridged(b, ci) MOI.throw_if_not_valid(b, ci) @@ -533,31 +570,35 @@ end """ function reduce_bridged( - b::AbstractBridgeOptimizer, args, + b::AbstractBridgeOptimizer, + args, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, - init, operate_variable_bridges!, - operate_constraint_bridges!) - -If `F`-in-`S` constraints may be added to `b.model`, -starts with `value = MOI.get(b.model, args...)`, otherwise, starts with -`value = init()`. Then -* if `F`-in-`S` constraints may correspond to - bridged variables, modify it with `operate_variable_bridges!`; -* if `F`-in-`S` constraints may correspond to - bridged constraints, modify it with `operate_constraint_bridges!`; + init, + operate_variable_bridges!, + operate_constraint_bridges!, + ) + +If `F`-in-`S` constraints may be added to `b.model`, starts with +`value = MOI.get(b.model, args...)`, otherwise, starts with `value = init()`. + +Then: + * if `F`-in-`S` constraints may correspond to bridged variables, modify it with + `operate_variable_bridges!` + * if `F`-in-`S` constraints may correspond to bridged constraints, modify it + with `operate_constraint_bridges!` then return the final `value`. -For instance, [`MOI.supports`](@ref) calls this function with -`init = () -> true`, `operate_variable_bridges(ok) = -ok && MOI.supports(b, attr, Variable.concrete_bridge_type(b, S))` and -`operate_constraint_bridges(ok) = ok && -MOI.supports(b, attr, Constraint.concrete_bridge_type(b, F, S))`. +For example, [`MOI.supports`](@ref) calls this function with + * `init = () -> true` + * `operate_variable_bridges(ok) = ok && MOI.supports(b, attr, Variable.concrete_bridge_type(b, S))` + * `operate_constraint_bridges(ok) = ok && MOI.supports(b, attr, Constraint.concrete_bridge_type(b, F, S))`. """ function reduce_bridged( b::AbstractBridgeOptimizer, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}, + # TODO(odow): why is init a function and not a constant? init, model_value, operate_variable_bridges!, @@ -591,10 +632,12 @@ function get_all_including_bridged( ) list = MOI.get(b.model, attr) if !isempty(Variable.bridges(b)) + # TODO(odow): Fix this! list = append!(copy(list), keys(Variable.bridges(b))) end return list end + function get_all_including_bridged( b::AbstractBridgeOptimizer, attr::MOI.ListOfConstraintIndices{F,S}, @@ -618,15 +661,19 @@ function get_all_including_bridged( ), ) end + # Remove constraints bridged by `bridge` from `list` function _remove_bridged(list, bridge, attr) for c in MOI.get(bridge, attr) + # TODO(odow): fix this i = something(findfirst(isequal(c), list), 0) if !iszero(i) MOI.deleteat!(list, i) end end + return end + function MOI.get( b::AbstractBridgeOptimizer, attr::Union{MOI.ListOfConstraintIndices,MOI.ListOfVariableIndices}, @@ -643,6 +690,7 @@ function MOI.get( end return list end + function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.NumberOfVariables) s = MOI.get(b.model, attr) + @@ -678,6 +726,7 @@ function get_all_including_bridged( ), ) end + function MOI.get( b::AbstractBridgeOptimizer, attr::MOI.NumberOfConstraints{F,S}, @@ -695,6 +744,7 @@ function MOI.get( end return s end + function MOI.get( b::AbstractBridgeOptimizer, attr::MOI.ListOfConstraintTypesPresent, @@ -757,18 +807,21 @@ function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ListOfModelAttributesSet) end return unbridged_function(b, list) end + function MOI.get( b::AbstractBridgeOptimizer, attr::Union{MOI.AbstractModelAttribute,MOI.AbstractOptimizerAttribute}, ) return unbridged_function(b, MOI.get(b.model, attr)) end + function MOI.set( b::AbstractBridgeOptimizer, attr::Union{MOI.AbstractModelAttribute,MOI.AbstractOptimizerAttribute}, value, ) - return MOI.set(b.model, attr, bridged_function(b, value)) + MOI.set(b.model, attr, bridged_function(b, value)) + return end """ @@ -777,9 +830,14 @@ end Return the cost of bridging variables constrained in `S` on creation, `is_bridged(b, S)` is assumed to be `true`. - bridging_cost(b::AbstractBridgeOptimizer, F::Type{<:MOI.AbstractFunction, S::Type{<:MOI.AbstractSet}}) + bridging_cost( + b::AbstractBridgeOptimizer, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}, + ) + +Return the cost of bridging `F`-in-`S` constraints. -Return the cost of bridging `F`-in-`S` constraints, `is_bridged(b, S)` is assumed to be `true`. """ function bridging_cost end @@ -794,6 +852,7 @@ function MOI.get( return MOI.get(b.model, attr) end end + function MOI.get( b::AbstractBridgeOptimizer, attr::MOI.ConstraintBridgingCost{F,S}, @@ -815,9 +874,11 @@ said to be bridged if the value of `MOI.ObjectiveFunctionType` is different for `b` and `b.model`. """ is_objective_bridged(b) = !isempty(Objective.bridges(b)) + function _delete_objective_bridges(b) MOI.delete(b, Objective.root_bridge(Objective.bridges(b))) - return empty!(Objective.bridges(b)) + empty!(Objective.bridges(b)) + return end function MOI.supports( @@ -842,6 +903,7 @@ underlying model. struct ObjectiveFunctionValue{F<:MOI.AbstractScalarFunction} result_index::Int end + function MOI.get( b::AbstractBridgeOptimizer, attr::ObjectiveFunctionValue{F}, @@ -853,6 +915,7 @@ function MOI.get( return MOI.get(b.model, MOI.ObjectiveValue(attr.result_index)) end end + function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveValue) if is_objective_bridged(b) F = Objective.function_type(Objective.bridges(b)) @@ -861,6 +924,7 @@ function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveValue) return MOI.get(b.model, attr) end end + function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveFunctionType) if is_objective_bridged(b) return Objective.function_type(Objective.bridges(b)) @@ -868,9 +932,11 @@ function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveFunctionType) return MOI.get(b.model, attr) end end + function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveSense) return MOI.get(b.model, attr) end + function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveFunction) value = if is_bridged(b, attr) MOI.get(b, attr, bridge(b, attr)) @@ -879,6 +945,7 @@ function MOI.get(b::AbstractBridgeOptimizer, attr::MOI.ObjectiveFunction) end return unbridged_function(b, value) end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.ObjectiveSense, @@ -894,11 +961,15 @@ function MOI.set( end end end + return end + function _bridge_objective(b, BridgeType, func) bridge = Objective.bridge_objective(BridgeType, b, func) - return Objective.add_key_for_bridge(Objective.bridges(b), bridge, func) + Objective.add_key_for_bridge(Objective.bridges(b), bridge, func) + return end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.ObjectiveFunction, @@ -936,7 +1007,9 @@ function MOI.set( else MOI.set(b.model, attr, func) end + return end + function MOI.modify( b::AbstractBridgeOptimizer, obj::MOI.ObjectiveFunction, @@ -944,13 +1017,12 @@ function MOI.modify( ) if is_bridged(b, change) modify_bridged_change(b, obj, change) + elseif is_bridged(b, obj) + MOI.modify(b, bridge(b, obj), change) else - if is_bridged(b, obj) - MOI.modify(b, bridge(b, obj), change) - else - MOI.modify(b.model, obj, change) - end + MOI.modify(b.model, obj, change) end + return end # Variable attributes @@ -979,6 +1051,7 @@ function MOI.get( end return unbridged_function(b, value) end + function MOI.get( b::AbstractBridgeOptimizer, attr::MOI.AbstractVariableAttribute, @@ -990,6 +1063,7 @@ function MOI.get( return unbridged_function.(b, MOI.get(b.model, attr, indices)) end end + function MOI.supports( b::AbstractBridgeOptimizer, attr::MOI.AbstractVariableAttribute, @@ -997,6 +1071,7 @@ function MOI.supports( ) return MOI.supports(b.model, attr, MOI.VariableIndex) end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.AbstractVariableAttribute, @@ -1005,15 +1080,17 @@ function MOI.set( ) value = bridged_function(b, value) if is_bridged(b, index) - return call_in_context( + call_in_context( b, index, bridge -> MOI.set(b, attr, bridge, value, _index(b, index)...), ) else - return MOI.set(b.model, attr, index, value) + MOI.set(b.model, attr, index, value) end + return end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.AbstractVariableAttribute, @@ -1021,10 +1098,11 @@ function MOI.set( values::Vector, ) if any(index -> is_bridged(b, index), indices) - return MOI.set.(b, attr, indices, values) + MOI.set.(b, attr, indices, values) else - return MOI.set(b.model, attr, indices, bridged_function.(b, values)) + MOI.set(b.model, attr, indices, bridged_function.(b, values)) end + return end # Constraint attributes @@ -1041,8 +1119,10 @@ function MOI.set( _set_substituted(b, MOI.ConstraintSet(), ci, new_set) end end - return _set_substituted(b, attr, ci, func) + _set_substituted(b, attr, ci, func) + return end + function MOI.get( b::AbstractBridgeOptimizer, attr::MOI.ConstraintFunction, @@ -1054,32 +1134,36 @@ function MOI.get( return Variable.function_for(Variable.bridges(b), ci) else func = call_in_context(b, ci, br -> MOI.get(b, attr, br)) + return unbridged_constraint_function(b, func) end else - func = MOI.get(b.model, attr, ci) + return unbridged_constraint_function(b, MOI.get(b.model, attr, ci)) end - return unbridged_constraint_function(b, func) end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.ConstraintSet, ci::MOI.ConstraintIndex{MOI.SingleVariable,S}, value::S, ) where {S<:MOI.AbstractScalarSet} - return _set_substituted(b, attr, ci, value) + _set_substituted(b, attr, ci, value) + return end + function MOI.get( b::AbstractBridgeOptimizer, attr::MOI.ConstraintSet, ci::MOI.ConstraintIndex{MOI.SingleVariable}, ) - return if is_bridged(b, ci) + if is_bridged(b, ci) MOI.throw_if_not_valid(b, ci) - call_in_context(b, ci, bridge -> MOI.get(b, attr, bridge)) + return call_in_context(b, ci, bridge -> MOI.get(b, attr, bridge)) else - MOI.get(b.model, attr, ci) + return MOI.get(b.model, attr, ci) end end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.ConstraintSet, @@ -1090,8 +1174,10 @@ function MOI.set( func = MOI.get(b, MOI.ConstraintFunction(), ci) new_func, set = bridged_constraint_function(b, func, set) end - return _set_substituted(b, attr, ci, set) + _set_substituted(b, attr, ci, set) + return end + function MOI.get( b::AbstractBridgeOptimizer, attr::MOI.ConstraintSet, @@ -1120,6 +1206,7 @@ function MOI.get( end return set end + function MOI.get( b::AbstractBridgeOptimizer, attr::MOI.AbstractConstraintAttribute, @@ -1133,6 +1220,7 @@ function MOI.get( end return unbridged_function(b, func) end + function MOI.supports( b::AbstractBridgeOptimizer, attr::MOI.AbstractConstraintAttribute, @@ -1163,15 +1251,19 @@ function _set_substituted( else MOI.set(b.model, attr, ci, value) end + return end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.AbstractConstraintAttribute, ci::MOI.ConstraintIndex, value, ) - return _set_substituted(b, attr, ci, bridged_function(b, value)) + _set_substituted(b, attr, ci, bridged_function(b, value)) + return end + ## Getting and Setting names function MOI.get( b::AbstractBridgeOptimizer, @@ -1184,6 +1276,7 @@ function MOI.get( return MOI.get(b.model, attr, vi) end end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.VariableName, @@ -1196,6 +1289,7 @@ function MOI.set( else MOI.set(b.model, attr, vi, name) end + return end function MOI.get( @@ -1209,6 +1303,7 @@ function MOI.get( return MOI.get(b.model, attr, constraint_index) end end + function MOI.supports( b::AbstractBridgeOptimizer, attr::MOI.ConstraintName, @@ -1216,6 +1311,7 @@ function MOI.supports( ) where {F,S} return is_bridged(b, F, S) || MOI.supports(b.model, attr, Index) end + function MOI.set( b::AbstractBridgeOptimizer, attr::MOI.ConstraintName, @@ -1228,6 +1324,7 @@ function MOI.set( else MOI.set(b.model, attr, constraint_index, name) end + return end # Query index from name (similar to `UniversalFallback`) @@ -1237,22 +1334,22 @@ function MOI.get( name::String, ) vi = MOI.get(b.model, MOI.VariableIndex, name) - if Variable.has_bridges(Variable.bridges(b)) - if b.name_to_var === nothing - b.name_to_var = MOIU.build_name_to_var_map(b.var_to_name) - end - vi_bridged = get(b.name_to_var, name, nothing) - MOIU.throw_if_multiple_with_name(vi_bridged, name) - return MOIU.check_type_and_multiple_names( - MOI.VariableIndex, - vi_bridged, - vi, - name, - ) - else + if !Variable.has_bridges(Variable.bridges(b)) return vi end + if b.name_to_var === nothing + b.name_to_var = MOIU.build_name_to_var_map(b.var_to_name) + end + vi_bridged = get(b.name_to_var, name, nothing) + MOIU.throw_if_multiple_with_name(vi_bridged, name) + return MOIU.check_type_and_multiple_names( + MOI.VariableIndex, + vi_bridged, + vi, + name, + ) end + function MOI.get( b::AbstractBridgeOptimizer, IdxT::Type{MOI.ConstraintIndex{F,S}}, @@ -1277,6 +1374,7 @@ function MOI.get( MOIU.throw_if_multiple_with_name(ci_bridged, name) return MOIU.check_type_and_multiple_names(IdxT, ci_bridged, ci, name) end + function MOI.get( b::AbstractBridgeOptimizer, IdxT::Type{MOI.ConstraintIndex}, @@ -1312,12 +1410,14 @@ function MOI.supports_constraint( return MOI.supports_constraint(b.model, F, S) end end + function add_bridged_constraint(b, BridgeType, f, s) bridge = Constraint.bridge_constraint(BridgeType, b, f, s) ci = Constraint.add_key_for_bridge(Constraint.bridges(b), bridge, f, s) Variable.register_context(Variable.bridges(b), ci) return ci end + function _check_double_single_variable( b::AbstractBridgeOptimizer, func::MOI.SingleVariable, @@ -1338,8 +1438,11 @@ function _check_double_single_variable( ) end end + return end -function _check_double_single_variable(b::AbstractBridgeOptimizer, func, set) end + +_check_double_single_variable(b::AbstractBridgeOptimizer, func, set) = nothing + function MOI.add_constraint( b::AbstractBridgeOptimizer, f::MOI.AbstractFunction, @@ -1393,6 +1496,7 @@ function MOI.add_constraint( return MOI.add_constraint(b.model, f, s) end end + function MOI.add_constraints( b::AbstractBridgeOptimizer, f::Vector{F}, @@ -1400,35 +1504,37 @@ function MOI.add_constraints( ) where {F<:MOI.AbstractFunction,S<:MOI.AbstractSet} if is_bridged(b, F, S) return MOI.add_constraint.(b, f, s) - else - if Variable.has_bridges(Variable.bridges(b)) - if F == MOI.SingleVariable - if any(func -> is_bridged(b, func.variable), f) - return MOI.add_constraint.(b, f, s) - end - elseif F == MOI.VectorOfVariables - if any(func -> any(vi -> is_bridged(b, vi), func.variables), f) - return MOI.add_constraint.(b, f, s) - end - else - f = F[bridged_function(b, func)::F for func in f] + end + if Variable.has_bridges(Variable.bridges(b)) + if F == MOI.SingleVariable + if any(func -> is_bridged(b, func.variable), f) + return MOI.add_constraint.(b, f, s) + end + elseif F == MOI.VectorOfVariables + if any(func -> any(vi -> is_bridged(b, vi), func.variables), f) + return MOI.add_constraint.(b, f, s) end + else + f = F[bridged_function(b, func)::F for func in f] end - return MOI.add_constraints(b.model, f, s) end + return MOI.add_constraints(b.model, f, s) end + function is_bridged( ::AbstractBridgeOptimizer, ::Union{MOI.ScalarConstantChange,MOI.VectorConstantChange}, ) return false end + function is_bridged( b::AbstractBridgeOptimizer, change::Union{MOI.ScalarCoefficientChange,MOI.MultirowChange}, ) return is_bridged(b, change.variable) end + function modify_bridged_change( b::AbstractBridgeOptimizer, obj, @@ -1452,7 +1558,9 @@ function modify_bridged_change( [(i, coef * t.coefficient) for (i, coef) in change.new_coefficients] MOI.modify(b, obj, MOI.MultirowChange(t.variable, coefs)) end + return end + function modify_bridged_change( b::AbstractBridgeOptimizer, obj, @@ -1475,7 +1583,9 @@ function modify_bridged_change( coef = t.coefficient * change.new_coefficient MOI.modify(b, obj, MOI.ScalarCoefficientChange(t.variable, coef)) end + return end + function MOI.modify( b::AbstractBridgeOptimizer, ci::MOI.ConstraintIndex, @@ -1490,6 +1600,7 @@ function MOI.modify( MOI.modify(b.model, ci, change) end end + return end # Variables @@ -1502,6 +1613,7 @@ function MOI.add_variable(b::AbstractBridgeOptimizer) return MOI.add_variable(b.model) end end + function MOI.add_variables(b::AbstractBridgeOptimizer, n) if is_bridged(b, MOI.Reals) variables, constraint = MOI.add_constrained_variables(b, MOI.Reals(n)) @@ -1522,6 +1634,7 @@ function MOI.supports_add_constrained_variables( return MOI.supports_add_constrained_variables(b.model, MOI.Reals) end end + function MOI.supports_add_constrained_variables( b::AbstractBridgeOptimizer, S::Type{<:MOI.AbstractVectorSet}, @@ -1532,28 +1645,29 @@ function MOI.supports_add_constrained_variables( return MOI.supports_add_constrained_variables(b.model, S) end end + function MOI.add_constrained_variables( b::AbstractBridgeOptimizer, set::MOI.AbstractVectorSet, ) - if is_bridged(b, typeof(set)) - if set isa MOI.Reals || is_variable_bridged(b, typeof(set)) - BridgeType = Variable.concrete_bridge_type(b, typeof(set)) - return Variable.add_keys_for_bridge( - Variable.bridges(b), - () -> Variable.bridge_constrained_variable(BridgeType, b, set), - set, - ) - else - variables = MOI.add_variables(b, MOI.dimension(set)) - constraint = - MOI.add_constraint(b, MOI.VectorOfVariables(variables), set) - return variables, constraint - end - else + if !is_bridged(b, typeof(set)) return MOI.add_constrained_variables(b.model, set) end + if set isa MOI.Reals || is_variable_bridged(b, typeof(set)) + BridgeType = Variable.concrete_bridge_type(b, typeof(set)) + return Variable.add_keys_for_bridge( + Variable.bridges(b), + () -> Variable.bridge_constrained_variable(BridgeType, b, set), + set, + ) + else + variables = MOI.add_variables(b, MOI.dimension(set)) + constraint = + MOI.add_constraint(b, MOI.VectorOfVariables(variables), set) + return variables, constraint + end end + function MOI.supports_add_constrained_variable( b::AbstractBridgeOptimizer, S::Type{<:MOI.AbstractScalarSet}, @@ -1564,32 +1678,32 @@ function MOI.supports_add_constrained_variable( return MOI.supports_add_constrained_variable(b.model, S) end end + function MOI.add_constrained_variable( b::AbstractBridgeOptimizer, set::MOI.AbstractScalarSet, ) - if is_bridged(b, typeof(set)) - if is_variable_bridged(b, typeof(set)) - BridgeType = Variable.concrete_bridge_type(b, typeof(set)) - return Variable.add_key_for_bridge( - Variable.bridges(b), - () -> Variable.bridge_constrained_variable(BridgeType, b, set), - set, - ) - else - variable = MOI.add_variable(b) - constraint = - MOI.add_constraint(b, MOI.SingleVariable(variable), set) - return variable, constraint - end - else + if !is_bridged(b, typeof(set)) return MOI.add_constrained_variable(b.model, set) end + if is_variable_bridged(b, typeof(set)) + BridgeType = Variable.concrete_bridge_type(b, typeof(set)) + return Variable.add_key_for_bridge( + Variable.bridges(b), + () -> Variable.bridge_constrained_variable(BridgeType, b, set), + set, + ) + else + variable = MOI.add_variable(b) + constraint = MOI.add_constraint(b, MOI.SingleVariable(variable), set) + return variable, constraint + end end function MOI.supports(b::AbstractBridgeOptimizer, sub::MOI.AbstractSubmittable) return MOI.supports(b.model, sub) end + function MOI.submit( b::AbstractBridgeOptimizer, sub::MOI.AbstractSubmittable, @@ -1599,8 +1713,10 @@ function MOI.submit( end """ - bridged_variable_function(b::AbstractBridgeOptimizer, - vi::MOI.VariableIndex) + bridged_variable_function( + b::AbstractBridgeOptimizer, + vi::MOI.VariableIndex, + ) Return a `MOI.AbstractScalarFunction` of variables of `b.model` that equals `vi`. That is, if the variable `vi` is bridged, it returns its expression in @@ -1641,6 +1757,7 @@ function bridged_function(bridge::AbstractBridgeOptimizer, value) value, )::typeof(value) end + function bridged_function(b::AbstractBridgeOptimizer, func::MOI.SingleVariable) # Should not be called by `add_constraint` as it force-bridges it # but could be called by attributes @@ -1650,6 +1767,7 @@ function bridged_function(b::AbstractBridgeOptimizer, func::MOI.SingleVariable) end return func end + # Shortcut to avoid `Variable.throw_if_cannot_unbridge(Variable.bridges(b))` function bridge_function( ::AbstractBridgeOptimizer, @@ -1659,13 +1777,15 @@ function bridge_function( end """ - unbridged_variable_function(b::AbstractBridgeOptimizer, - vi::MOI.VariableIndex) + unbridged_variable_function( + b::AbstractBridgeOptimizer, + vi::MOI.VariableIndex, + ) Return a `MOI.AbstractScalarFunction` of variables of `b` that equals `vi`. -That is, if the variable `vi` is an internal variable of `b.model` created by a bridge -but not visible to the user, it returns its expression in terms of the variables -of bridged variables. Otherwise, it returns `MOI.SingleVariable(vi)`. +That is, if the variable `vi` is an internal variable of `b.model` created by a +bridge but not visible to the user, it returns its expression in terms of the +variables of bridged variables. Otherwise, it returns `MOI.SingleVariable(vi)`. """ function unbridged_variable_function( b::AbstractBridgeOptimizer, @@ -1701,12 +1821,14 @@ function unbridged_function(b::AbstractBridgeOptimizer, value) value, )::typeof(value) end + function unbridged_function( - bridge::AbstractBridgeOptimizer, + ::AbstractBridgeOptimizer, func::Union{MOI.SingleVariable,MOI.VectorOfVariables}, ) return func # bridged variables are not allowed in non-bridged constraints end + # Shortcut to avoid `Variable.throw_if_cannot_unbridge(Variable.bridges(b))` function unbridged_function( ::AbstractBridgeOptimizer, @@ -1734,6 +1856,7 @@ function bridged_constraint_function( ) return bridged_function(b, func), set end + function bridged_constraint_function( b::AbstractBridgeOptimizer, func::MOI.AbstractScalarFunction, @@ -1749,6 +1872,7 @@ function bridged_constraint_function( f = bridged_function(b, func)::typeof(func) return MOIU.normalize_constant(f, set) end + function bridged_constraint_function( b::AbstractBridgeOptimizer, func::MOI.SingleVariable, @@ -1774,6 +1898,7 @@ function unbridged_constraint_function( ) return unbridged_function(b, func) end + function unbridged_constraint_function( b::AbstractBridgeOptimizer, func::MOI.AbstractScalarFunction, @@ -1788,6 +1913,7 @@ function unbridged_constraint_function( end return f end + function unbridged_constraint_function( b::AbstractBridgeOptimizer, func::MOI.SingleVariable, diff --git a/src/Bridges/debug.jl b/src/Bridges/debug.jl index 7a659899fc..c3c19904d7 100644 --- a/src/Bridges/debug.jl +++ b/src/Bridges/debug.jl @@ -1,24 +1,27 @@ function print_node(io::IO, b::LazyBridgeOptimizer, node::VariableNode) S, = b.variable_types[node.index] - return MOIU.print_with_acronym( + MOIU.print_with_acronym( io, "[$(node.index)] constrained variables in `$S` are", ) + return end + function print_node(io::IO, b::LazyBridgeOptimizer, node::ConstraintNode) F, S = b.constraint_types[node.index] - return MOIU.print_with_acronym( - io, - "($(node.index)) `$F`-in-`$S` constraints are", - ) + MOIU.print_with_acronym(io, "($(node.index)) `$F`-in-`$S` constraints are") + return end + function print_node(io::IO, b::LazyBridgeOptimizer, node::ObjectiveNode) F, = b.objective_types[node.index] - return MOIU.print_with_acronym( + MOIU.print_with_acronym( io, "|$(node.index)| objective function of type `$F` is", ) + return end + function print_node_info( io::IO, b::LazyBridgeOptimizer, @@ -51,38 +54,45 @@ function print_node_info( println(io, ".") end end + return end -function print_graph(b::LazyBridgeOptimizer; kws...) - return print_graph(Base.stdout, b; kws...) + +function print_graph(b::LazyBridgeOptimizer; kwargs...) + print_graph(Base.stdout, b; kwargs...) + return end -function print_graph(io::IO, b::LazyBridgeOptimizer; kws...) + +function print_graph(io::IO, b::LazyBridgeOptimizer; kwargs...) println(io, b.graph) - return print_nodes( + print_nodes( io, b, variable_nodes(b.graph), constraint_nodes(b.graph), objective_nodes(b.graph); - kws..., + kwargs..., ) + return end + function print_nodes( io::IO, b::LazyBridgeOptimizer, variable_nodes, constraint_nodes, objective_nodes; - kws..., + kwargs..., ) for node in variable_nodes - print_node_info(io, b, node; kws...) + print_node_info(io, b, node; kwargs...) end for node in constraint_nodes - print_node_info(io, b, node; kws...) + print_node_info(io, b, node; kwargs...) end for node in objective_nodes - print_node_info(io, b, node; kws...) + print_node_info(io, b, node; kwargs...) end + return end function print_if_unsupported( @@ -95,8 +105,10 @@ function print_if_unsupported( end print(io, " ") print_node(io, b, node) - return print(io, " not supported\n") + print(io, " not supported\n") + return end + function print_unsupported(io::IO, b::LazyBridgeOptimizer, edge::Edge) for node in edge.added_variables print_if_unsupported(io, b, node) @@ -104,7 +116,9 @@ function print_unsupported(io::IO, b::LazyBridgeOptimizer, edge::Edge) for node in edge.added_constraints print_if_unsupported(io, b, node) end + return end + function print_unsupported(io::IO, b::LazyBridgeOptimizer, edge::ObjectiveEdge) for node in edge.added_variables print_if_unsupported(io, b, node) @@ -112,8 +126,10 @@ function print_unsupported(io::IO, b::LazyBridgeOptimizer, edge::ObjectiveEdge) for node in edge.added_constraints print_if_unsupported(io, b, node) end - return print_if_unsupported(io, b, edge.added_objective) + print_if_unsupported(io, b, edge.added_objective) + return end + function print_unsupported( io::IO, b::LazyBridgeOptimizer, @@ -135,7 +151,9 @@ function print_unsupported( if no_bridge println(io, " no added bridge supports bridging it.") end + return end + function _bridge_type( b::LazyBridgeOptimizer, node::VariableNode, @@ -146,6 +164,7 @@ function _bridge_type( b.variable_types[node.index]..., ) end + function _bridge_type( b::LazyBridgeOptimizer, node::ConstraintNode, @@ -156,6 +175,7 @@ function _bridge_type( b.constraint_types[node.index]..., ) end + function _bridge_type( b::LazyBridgeOptimizer, node::ObjectiveNode, @@ -166,6 +186,7 @@ function _bridge_type( b.objective_types[node.index]..., ) end + function print_unsupported(io::IO, b::LazyBridgeOptimizer, node::VariableNode) print_unsupported( io, @@ -184,22 +205,27 @@ function print_unsupported(io::IO, b::LazyBridgeOptimizer, node::VariableNode) println(io, ":") print_if_unsupported(io, b, constraint_node) end + return end + function print_unsupported(io::IO, b::LazyBridgeOptimizer, node::ConstraintNode) - return print_unsupported( + print_unsupported( io, b, b.graph.constraint_edges[node.index], bridge_index -> _bridge_type(b, node, bridge_index), ) + return end + function print_unsupported(io::IO, b::LazyBridgeOptimizer, node::ObjectiveNode) - return print_unsupported( + print_unsupported( io, b, b.graph.objective_edges[node.index], bridge_index -> _bridge_type(b, node, bridge_index), ) + return end function add_unsupported( @@ -217,7 +243,9 @@ function add_unsupported( add_unsupported(graph, node, variables, constraints, objectives) end end + return end + function add_unsupported( graph::Graph, edges::Vector{ObjectiveEdge}, @@ -240,7 +268,9 @@ function add_unsupported( objectives, ) end + return end + function add_unsupported( graph::Graph, node::VariableNode, @@ -269,7 +299,9 @@ function add_unsupported( objectives, ) end + return end + function add_unsupported( graph::Graph, node::ConstraintNode, @@ -281,14 +313,16 @@ function add_unsupported( return end push!(constraints, node) - return add_unsupported( + add_unsupported( graph, graph.constraint_edges[node.index], variables, constraints, objectives, ) + return end + function add_unsupported( graph::Graph, node::ObjectiveNode, @@ -300,25 +334,28 @@ function add_unsupported( return end push!(objectives, node) - return add_unsupported( + add_unsupported( graph, graph.objective_edges[node.index], variables, constraints, objectives, ) + return end + function _sort(nodes::Set) vector = collect(nodes) sort!(vector, by = node -> node.index) return vector end + function debug_unsupported(io::IO, b::LazyBridgeOptimizer, node::AbstractNode) variables = Set{VariableNode}() constraints = Set{ConstraintNode}() objectives = Set{ObjectiveNode}() add_unsupported(b.graph, node, variables, constraints, objectives) - return print_nodes( + print_nodes( io, b, _sort(variables), @@ -326,6 +363,7 @@ function debug_unsupported(io::IO, b::LazyBridgeOptimizer, node::AbstractNode) _sort(objectives), debug_unsupported = true, ) + return end const UNSUPPORTED_MESSAGE = @@ -353,6 +391,7 @@ function debug( println(io, UNSUPPORTED_MESSAGE) debug_unsupported(io, b, node(b, S)) end + return end """ @@ -368,9 +407,10 @@ Prints to `io` explanations for the value of function debug_supports_add_constrained_variable( b::LazyBridgeOptimizer, S::Type{<:MOI.AbstractSet}; - kws..., + kwargs..., ) - return debug(b, S; kws...) + debug(b, S; kwargs...) + return end function debug( @@ -386,12 +426,16 @@ function debug( println(io, UNSUPPORTED_MESSAGE) debug_unsupported(io, b, node(b, F, S)) end + return end """ debug_supports_constraint( - b::LazyBridgeOptimizer, F::Type{<:MOI.AbstractFunction}, - S::Type{<:MOI.AbstractSet}; io::IO = Base.stdout) + b::LazyBridgeOptimizer, + F::Type{<:MOI.AbstractFunction}, + S::Type{<:MOI.AbstractSet}; + io::IO = Base.stdout, + ) Prints to `io` explanations for the value of [`MOI.supports_constraint`](@ref) with the same arguments. @@ -400,9 +444,9 @@ function debug_supports_constraint( b::LazyBridgeOptimizer, F::Type{<:MOI.AbstractFunction}, S::Type{<:MOI.AbstractSet}; - kws..., + kwargs..., ) - return debug(b, F, S; kws...) + return debug(b, F, S; kwargs...) end function debug( @@ -422,10 +466,15 @@ function debug( ) debug_unsupported(io, b, node(b, F)) end + return end """ - debug_supports(b::LazyBridgeOptimizer, ::MOI.ObjectiveFunction{F}; io::IO = Base.stdout) where F + debug_supports( + b::LazyBridgeOptimizer, + ::MOI.ObjectiveFunction{F}; + io::IO = Base.stdout, + ) where F Prints to `io` explanations for the value of [`MOI.supports`](@ref) with the same arguments. @@ -433,7 +482,8 @@ same arguments. function debug_supports( b::LazyBridgeOptimizer, ::MOI.ObjectiveFunction{F}; - kws..., + kwargs..., ) where {F} - return debug(b, F; kws...) + debug(b, F; kwargs...) + return end diff --git a/src/Bridges/graph.jl b/src/Bridges/graph.jl index 699341d534..b44f384e33 100644 --- a/src/Bridges/graph.jl +++ b/src/Bridges/graph.jl @@ -2,22 +2,27 @@ const INFINITY = -1 const INVALID_NODE_INDEX = -1 abstract type AbstractNode end + struct VariableNode <: AbstractNode index::Int end + struct ConstraintNode <: AbstractNode index::Int end + struct ObjectiveNode <: AbstractNode index::Int end abstract type AbstractEdge end + struct Edge <: AbstractEdge bridge_index::Int added_variables::Vector{VariableNode} added_constraints::Vector{ConstraintNode} end + struct ObjectiveEdge <: AbstractEdge bridge_index::Int added_variables::Vector{VariableNode} @@ -47,6 +52,7 @@ mutable struct Graph objective_best::Vector{Int} objective_last_correct::Int end + function Graph() return Graph( Vector{Edge}[], @@ -72,18 +78,21 @@ function Base.show(io::IO, graph::Graph) print(io, length(graph.constraint_best), " constraint nodes and ") return print(io, length(graph.objective_best), " objective nodes.") end + function variable_nodes(graph::Graph) return MOIU.LazyMap{VariableNode}( i -> VariableNode(i), eachindex(graph.variable_best), ) end + function constraint_nodes(graph::Graph) return MOIU.LazyMap{ConstraintNode}( i -> ConstraintNode(i), eachindex(graph.constraint_best), ) end + function objective_nodes(graph::Graph) return MOIU.LazyMap{ObjectiveNode}( i -> ObjectiveNode(i), @@ -115,18 +124,25 @@ function Base.empty!(graph::Graph) empty!(graph.objective_edges) empty!(graph.objective_dist) empty!(graph.objective_best) - return graph.objective_last_correct = 0 + graph.objective_last_correct = 0 + return end function add_edge(graph::Graph, node::VariableNode, edge::Edge) - return push!(graph.variable_edges[node.index], edge) + push!(graph.variable_edges[node.index], edge) + return end + function add_edge(graph::Graph, node::ConstraintNode, edge::Edge) - return push!(graph.constraint_edges[node.index], edge) + push!(graph.constraint_edges[node.index], edge) + return end + function add_edge(graph::Graph, node::ObjectiveNode, edge::ObjectiveEdge) - return push!(graph.objective_edges[node.index], edge) + push!(graph.objective_edges[node.index], edge) + return end + function add_variable_node(graph::Graph) push!(graph.variable_edges, Edge[]) # Use an invalid index so that the code errors instead return something @@ -137,6 +153,7 @@ function add_variable_node(graph::Graph) push!(graph.variable_best, 0) return VariableNode(length(graph.variable_best)) end + function set_variable_constraint_node( graph::Graph, variable_node::VariableNode, @@ -144,14 +161,17 @@ function set_variable_constraint_node( cost::Int, ) graph.variable_constraint_node[variable_node.index] = constraint_node - return graph.variable_constraint_cost[variable_node.index] = cost + graph.variable_constraint_cost[variable_node.index] = cost + return end + function add_constraint_node(graph::Graph) push!(graph.constraint_edges, Edge[]) push!(graph.constraint_dist, INFINITY) push!(graph.constraint_best, 0) return ConstraintNode(length(graph.constraint_best)) end + function add_objective_node(graph::Graph) push!(graph.objective_edges, ObjectiveEdge[]) push!(graph.objective_dist, INFINITY) @@ -169,10 +189,12 @@ function bridge_index(graph::Graph, node::VariableNode) bellman_ford!(graph) return graph.variable_best[node.index] end + function bridge_index(graph::Graph, node::ConstraintNode) bellman_ford!(graph) return graph.constraint_best[node.index] end + function bridge_index(graph::Graph, node::ObjectiveNode) bellman_ford!(graph) return graph.objective_best[node.index] @@ -278,9 +300,11 @@ function _dist(graph::Graph, node::VariableNode) end end end + function _dist(graph::Graph, node::ConstraintNode) return iszero(node.index) ? 0 : graph.constraint_dist[node.index] end + function _dist(graph::Graph, node::ObjectiveNode) return iszero(node.index) ? 0 : graph.objective_dist[node.index] end @@ -288,10 +312,12 @@ end function _sum_dist(graph::Graph, nodes::Vector{<:AbstractNode}) return mapreduce(node -> _dist(graph, node), +, nodes, init = 0) end + function added_dist(graph::Graph, edge::Edge) return _sum_dist(graph, edge.added_variables) + _sum_dist(graph, edge.added_constraints) end + function added_dist(graph::Graph, edge::ObjectiveEdge) return _sum_dist(graph, edge.added_variables) + _sum_dist(graph, edge.added_constraints) + @@ -301,13 +327,16 @@ end function supports_no_update(graph::Graph, node::AbstractNode) return iszero(node.index) || _dist(graph, node) != INFINITY end + function _supports_all_no_update(graph::Graph, nodes::Vector{<:AbstractNode}) return all(node -> supports_no_update(graph, node), nodes) end + function supports_added_no_update(graph::Graph, edge::Edge) return _supports_all_no_update(graph, edge.added_variables) && _supports_all_no_update(graph, edge.added_constraints) end + function supports_added_no_update(graph::Graph, edge::ObjectiveEdge) return _supports_all_no_update(graph, edge.added_variables) && _supports_all_no_update(graph, edge.added_constraints) && diff --git a/src/Bridges/lazy_bridge_optimizer.jl b/src/Bridges/lazy_bridge_optimizer.jl index 5c896d7919..a026a6aa99 100644 --- a/src/Bridges/lazy_bridge_optimizer.jl +++ b/src/Bridges/lazy_bridge_optimizer.jl @@ -5,13 +5,20 @@ include("graph.jl") """ LazyBridgeOptimizer{OT<:MOI.ModelLike} <: AbstractBridgeOptimizer -The `LazyBridgeOptimizer` combines several bridges, which are added using the [`add_bridge`](@ref) function. -Whenever a constraint is added, it only attempts to bridge it if it is not supported by the internal model (hence its name `Lazy`). +The `LazyBridgeOptimizer` combines several bridges, which are added using the +[`add_bridge`](@ref) function. + +Whenever a constraint is added, it only attempts to bridge it if it is not +supported by the internal model (hence its name `Lazy`). + When bridging a constraint, it selects the minimal number of bridges needed. -For instance, a constraint `F`-in-`S` can be bridged into a constraint `F1`-in-`S1` (supported by the internal model) using bridge 1 or -bridged into a constraint `F2`-in-`S2` (unsupported by the internal model) using bridge 2 which can then be -bridged into a constraint `F3`-in-`S3` (supported by the internal model) using bridge 3, -it will choose bridge 1 as it allows to bridge `F`-in-`S` using only one bridge instead of two if it uses bridge 2 and 3. + +For example, if a constraint `F`-in-`S` can be bridged into a constraint +`F1`-in-`S1` (supported by the internal model) using bridge 1 or bridged into a +constraint `F2`-in-`S2` (unsupported by the internal model) using bridge 2 which +can then be bridged into a constraint `F3`-in-`S3` (supported by the internal +model) using bridge 3, it will choose bridge 1 as it allows to bridge `F`-in-`S +using only one bridge instead of two if it uses bridge 2 and 3. """ mutable struct LazyBridgeOptimizer{OT<:MOI.ModelLike} <: AbstractBridgeOptimizer # Internal model @@ -44,6 +51,7 @@ mutable struct LazyBridgeOptimizer{OT<:MOI.ModelLike} <: AbstractBridgeOptimizer # `concrete_bridge_type` at runtime, which is slow. cached_bridge_type::Dict{Any,DataType} end + function LazyBridgeOptimizer(model::MOI.ModelLike) return LazyBridgeOptimizer{typeof(model)}( model, @@ -71,23 +79,26 @@ end function Variable.bridges(bridge::LazyBridgeOptimizer) return bridge.variable_map end + function Constraint.bridges(bridge::LazyBridgeOptimizer) return bridge.constraint_map end + function Objective.bridges(b::LazyBridgeOptimizer) return b.objective_map end # After `add_bridge(b, BT)`, some constrained variables `(S,)` in # `keys(b.variable_best)` or constraints `(F, S)` in `keys(b.constraint_best)` -# or `(F,)` in `keys(b.objective_best)` may be bridged -# with less bridges than `b.variable_dist[(S,)]`, -# `b.constraint_dist[(F, S)]` or `b.objective_dist[(F,)]` using `BT`. -# We could either recompute the distance from every node or clear the -# dictionary so that the distance is computed lazily at the next -# `supports_constraint` call. We prefer clearing the dictionaries so as this is -# called for each bridge added and recomputing the distance for each bridge -# would be a waste if several bridges are added consecutively. +# or `(F,)` in `keys(b.objective_best)` may be bridged with less bridges than +# `b.variable_dist[(S,)]`, `b.constraint_dist[(F, S)]` or +# `b.objective_dist[(F,)]` using `BT`. +# +# We could either recompute the distance from every node or clear the dictionary +# so that the distance is computed lazily at the next `supports_constraint` +# call. We prefer clearing the dictionaries so as this is called for each bridge +# added and recomputing the distance for each bridge would be a waste if several +# bridges are added consecutively. function _reset_dist(b::LazyBridgeOptimizer) empty!(b.variable_node) empty!(b.variable_types) @@ -96,7 +107,8 @@ function _reset_dist(b::LazyBridgeOptimizer) empty!(b.objective_node) empty!(b.objective_types) empty!(b.graph) - return empty!(b.cached_bridge_type) + empty!(b.cached_bridge_type) + return end function edge(b::LazyBridgeOptimizer, bridge_index, BT::Type{<:AbstractBridge}) @@ -108,6 +120,7 @@ function edge(b::LazyBridgeOptimizer, bridge_index, BT::Type{<:AbstractBridge}) ConstraintNode[node(b, C[1], C[2]) for C in added_constraint_types(BT)], ) end + function edge( b::LazyBridgeOptimizer, bridge_index, @@ -124,16 +137,19 @@ function edge( end functionized_type(::Nothing) = nothing + function functionized_type( ::Type{<:Constraint.ScalarFunctionizeBridge{T}}, ) where {T} return MOI.ScalarAffineFunction{T} end + function functionized_type( ::Type{<:Constraint.VectorFunctionizeBridge{T}}, ) where {T} return MOI.VectorAffineFunction{T} end + function functionized_type(b::LazyBridgeOptimizer, ::Type{MOI.SingleVariable}) return functionized_type( _first_functionize_bridge( @@ -142,6 +158,7 @@ function functionized_type(b::LazyBridgeOptimizer, ::Type{MOI.SingleVariable}) ), ) end + function functionized_type( b::LazyBridgeOptimizer, ::Type{MOI.VectorOfVariables}, @@ -264,12 +281,14 @@ function _bridge_types( ) return b.variable_bridge_types end + function _bridge_types( b::LazyBridgeOptimizer, ::Type{<:Constraint.AbstractBridge}, ) return b.constraint_bridge_types end + function _bridge_types( b::LazyBridgeOptimizer, ::Type{<:Objective.AbstractBridge}, @@ -287,9 +306,12 @@ function add_bridge(b::LazyBridgeOptimizer, BT::Type{<:AbstractBridge}) _add_bridge(b, BT) _reset_dist(b) end + return end + function _add_bridge(b::LazyBridgeOptimizer, BT::Type{<:AbstractBridge}) - return push!(_bridge_types(b, BT), BT) + push!(_bridge_types(b, BT), BT) + return end """