Skip to content

Commit

Permalink
[Bridges] add ExponentialConeToScalarNonlinearFunctionBridge (#2587)
Browse files Browse the repository at this point in the history
  • Loading branch information
odow authored Dec 10, 2024
1 parent f88f78b commit 86d17e3
Show file tree
Hide file tree
Showing 4 changed files with 226 additions and 0 deletions.
5 changes: 5 additions & 0 deletions src/Bridges/Constraint/Constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,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

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
# 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 ExponentialConeToScalarNonlinearFunction{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)))
# We add this as a constraint to avoid conflicting with existing variable
# bounds, of which there can be at most one of.
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(
::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(
::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
2 changes: 2 additions & 0 deletions src/Utilities/parser.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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()

0 comments on commit 86d17e3

Please sign in to comment.