From e44e83a7adfa11e790c9fa82b2bfb02b874532b8 Mon Sep 17 00:00:00 2001 From: Oscar Dowson Date: Wed, 4 Dec 2024 12:56:16 +1300 Subject: [PATCH] [Bridges] add ExponentialConeToScalarNonlinearFunctionBridge --- src/Bridges/Constraint/Constraint.jl | 6 + ...tialConeToScalarNonlinearFunctionBridge.jl | 162 ++++++++++++++++++ src/Utilities/parser.jl | 2 + ...tialConeToScalarNonlinearFunctionBridge.jl | 58 +++++++ 4 files changed, 228 insertions(+) create mode 100644 src/Bridges/Constraint/bridges/ExponentialConeToScalarNonlinearFunctionBridge.jl create mode 100644 test/Bridges/Constraint/ExponentialConeToScalarNonlinearFunctionBridge.jl diff --git a/src/Bridges/Constraint/Constraint.jl b/src/Bridges/Constraint/Constraint.jl index d9d3c84074..f91ed33959 100644 --- a/src/Bridges/Constraint/Constraint.jl +++ b/src/Bridges/Constraint/Constraint.jl @@ -68,6 +68,7 @@ include("bridges/zero_one.jl") include("bridges/sos1_to_milp.jl") include("bridges/sos2_to_milp.jl") include("bridges/indicator_to_milp.jl") +include("bridges/ExponentialConeToScalarNonlinearFunctionBridge.jl") """ add_all_bridges(bridged_model, ::Type{T}) where {T} @@ -156,6 +157,11 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T} MOI.Bridges.add_bridge(bridged_model, SOS1ToMILPBridge{T}) MOI.Bridges.add_bridge(bridged_model, SOS2ToMILPBridge{T}) MOI.Bridges.add_bridge(bridged_model, IndicatorToMILPBridge{T}) + + MOI.Bridges.add_bridge( + bridged_model, + ExponentialConeToScalarNonlinearFunctionBridge{T}, + ) return end diff --git a/src/Bridges/Constraint/bridges/ExponentialConeToScalarNonlinearFunctionBridge.jl b/src/Bridges/Constraint/bridges/ExponentialConeToScalarNonlinearFunctionBridge.jl new file mode 100644 index 0000000000..3f7477db9a --- /dev/null +++ b/src/Bridges/Constraint/bridges/ExponentialConeToScalarNonlinearFunctionBridge.jl @@ -0,0 +1,162 @@ +# Copyright (c) 2017: Miles Lubin and contributors +# Copyright (c) 2017: Google Inc. +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +""" + ExponentialConeToScalarNonlinearFunctionBridge{T,F} <: + Bridges.Constraint.AbstractBridge + +`ExponentialConeToScalarNonlinearFunctionBridge` implements the following +reformulation: + + * ``(x, y, z) \\in \\textsf{ExponentialCone}()`` to + ``y \\cdot exp(x / y)) - z \\le 0``, ``y \\ge 0``. + +## Source node + +`ExponentialConeToScalarNonlinearFunctionBridge` supports: + + * `F` in [`MOI.ExponentialCone`](@ref) + +## Target nodes + +`ExponentialConeToScalarNonlinearFunctionBridge` creates: + + * [`MOI.ScalarNonlinearFunction`](@ref) in [`MOI.LessThan{T}`](@ref) + * [`MOI.ScalarAffineFunction`](@ref) in [`MOI.GreaterThan{T}`](@ref) +""" +mutable struct ExponentialConeToScalarNonlinearFunctionBridge{ + T, + F<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}, +} <: AbstractBridge + f::F + ci::MOI.ConstraintIndex{MOI.ScalarNonlinearFunction,MOI.LessThan{T}} + ci_y::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.GreaterThan{T}} +end + +const ExponentialToScalarNonlinearFunction{T,OT<:MOI.ModelLike} = + SingleBridgeOptimizer{ExponentialConeToScalarNonlinearFunctionBridge{T},OT} + +function bridge_constraint( + ::Type{ExponentialConeToScalarNonlinearFunctionBridge{T,F}}, + model::MOI.ModelLike, + f::F, + s::MOI.ExponentialCone, +) where {T,F<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}} + x, y, z = MOI.Utilities.scalarize(f) + g_x_div_y = MOI.ScalarNonlinearFunction(:/, Any[x, y]) + g_exp_x_div_y = MOI.ScalarNonlinearFunction(:exp, Any[g_x_div_y]) + g = MOI.ScalarNonlinearFunction( + :-, + Any[MOI.ScalarNonlinearFunction(:*, Any[y, g_exp_x_div_y]), z], + ) + ci = MOI.add_constraint(model, g, MOI.LessThan(zero(T))) + ci_y = MOI.add_constraint(model, one(T) * y, MOI.GreaterThan(zero(T))) + return ExponentialConeToScalarNonlinearFunctionBridge{T,F}(f, ci, ci_y) +end + +function MOI.supports_constraint( + ::Type{<:ExponentialConeToScalarNonlinearFunctionBridge{T}}, + ::Type{<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}}, + ::Type{MOI.ExponentialCone}, +) where {T} + return true +end + +function MOI.Bridges.added_constrained_variable_types( + ::Type{ExponentialConeToScalarNonlinearFunctionBridge{T,F}}, +) where {T,F} + return Tuple{Type}[] +end + +function MOI.Bridges.added_constraint_types( + ::Type{ExponentialConeToScalarNonlinearFunctionBridge{T,F}}, +) where {T,F} + return Tuple{Type,Type}[ + (MOI.ScalarNonlinearFunction, MOI.LessThan{T}), + (MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}), + ] +end + +function concrete_bridge_type( + ::Type{<:ExponentialConeToScalarNonlinearFunctionBridge{T}}, + ::Type{F}, + ::Type{MOI.ExponentialCone}, +) where {T,F<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}} + return ExponentialConeToScalarNonlinearFunctionBridge{T,F} +end + +function MOI.get( + ::MOI.ModelLike, + ::MOI.ConstraintFunction, + bridge::ExponentialConeToScalarNonlinearFunctionBridge, +) + return copy(bridge.f) +end + +function MOI.get( + ::MOI.ModelLike, + ::MOI.ConstraintSet, + ::ExponentialConeToScalarNonlinearFunctionBridge, +) + return MOI.ExponentialCone() +end + +function MOI.delete( + model::MOI.ModelLike, + bridge::ExponentialConeToScalarNonlinearFunctionBridge, +) + MOI.delete(model, bridge.ci) + MOI.delete(model, bridge.ci_y) + return +end + +function MOI.get( + ::ExponentialConeToScalarNonlinearFunctionBridge, + ::MOI.NumberOfVariables, +)::Int64 + return 0 +end + +function MOI.get( + ::ExponentialConeToScalarNonlinearFunctionBridge, + ::MOI.ListOfVariableIndices, +)::Vector{MOI.VariableIndex} + return MOI.VariableIndex[] +end + +function MOI.get( + bridge::ExponentialConeToScalarNonlinearFunctionBridge{T}, + ::MOI.NumberOfConstraints{MOI.ScalarNonlinearFunction,MOI.LessThan{T}}, +)::Int64 where {T} + return 1 +end + +function MOI.get( + bridge::ExponentialConeToScalarNonlinearFunctionBridge{T}, + ::MOI.ListOfConstraintIndices{MOI.ScalarNonlinearFunction,MOI.LessThan{T}}, +) where {T} + return [bridge.ci] +end + +function MOI.get( + bridge::ExponentialConeToScalarNonlinearFunctionBridge{T}, + ::MOI.NumberOfConstraints{ + MOI.ScalarAffineFunction{T}, + MOI.GreaterThan{T}, + }, +)::Int64 where {T} + return 1 +end + +function MOI.get( + bridge::ExponentialConeToScalarNonlinearFunctionBridge{T}, + ::MOI.ListOfConstraintIndices{ + MOI.ScalarAffineFunction{T}, + MOI.GreaterThan{T}, + }, +) where {T} + return [bridge.ci_y] +end diff --git a/src/Utilities/parser.jl b/src/Utilities/parser.jl index 0ec3f5aa96..47e8f672ac 100644 --- a/src/Utilities/parser.jl +++ b/src/Utilities/parser.jl @@ -244,6 +244,8 @@ function _parsed_to_moi(model, s::Expr) return _parsed_scalar_to_moi(model, s.args[2]) elseif Meta.isexpr(s, :call, 2) && s.args[1] == :VectorNonlinearFunction return _parsed_vector_to_moi(model, s.args[2]) + elseif Meta.isexpr(s, :call, 2) && s.args[1] == :esc + return _parsed_to_moi(model, _parse_function(s.args[2], Float64)) end args = Any[_parsed_to_moi(model, arg) for arg in s.args[2:end]] return MOI.ScalarNonlinearFunction(s.args[1], args) diff --git a/test/Bridges/Constraint/ExponentialConeToScalarNonlinearFunctionBridge.jl b/test/Bridges/Constraint/ExponentialConeToScalarNonlinearFunctionBridge.jl new file mode 100644 index 0000000000..f6ca20493c --- /dev/null +++ b/test/Bridges/Constraint/ExponentialConeToScalarNonlinearFunctionBridge.jl @@ -0,0 +1,58 @@ +# Copyright (c) 2017: Miles Lubin and contributors +# Copyright (c) 2017: Google Inc. +# +# Use of this source code is governed by an MIT-style license that can be found +# in the LICENSE.md file or at https://opensource.org/licenses/MIT. + +module TestConstraintExponentialConeToScalarNonlinearFunctionBridge + +using Test + +import MathOptInterface as MOI + +function runtests() + for name in names(@__MODULE__; all = true) + if startswith("$(name)", "test_") + @testset "$(name)" begin + getfield(@__MODULE__, name)() + end + end + end + return +end + +function test_runtests_VectorOfVariables() + MOI.Bridges.runtests( + MOI.Bridges.Constraint.ExponentialConeToScalarNonlinearFunctionBridge, + """ + variables: x, y, z + [x, y, z] in ExponentialCone() + """, + """ + variables: x, y, z + ScalarNonlinearFunction(y * exp(x / y) - z) <= 0.0 + 1.0 * y >= 0.0 + """, + ) + return +end + +function test_runtests_VectorAffineFunction() + MOI.Bridges.runtests( + MOI.Bridges.Constraint.ExponentialConeToScalarNonlinearFunctionBridge, + """ + variables: x, y, z + [1.0 * x, 2.0 * y, 3.0 * z + 1.0] in ExponentialCone() + """, + """ + variables: x, y, z + ScalarNonlinearFunction(esc(2.0 * y) * exp(esc(1.0 * x) / esc(2.0 * y)) - esc(3.0 * z + 1.0)) <= 0.0 + 2.0 * y >= 0.0 + """, + ) + return +end + +end # module + +TestConstraintExponentialConeToScalarNonlinearFunctionBridge.runtests()