Skip to content

Commit e2d10cb

Browse files
committed
[Bridges] add SplitComplexIndicatorEqualToBridge
1 parent 476851b commit e2d10cb

3 files changed

Lines changed: 320 additions & 0 deletions

File tree

src/Bridges/Constraint/Constraint.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,7 @@ function add_all_bridges(model, ::Type{T}) where {T}
127127
MOI.Bridges.add_bridge(model, SOS1ToMILPBridge{T})
128128
MOI.Bridges.add_bridge(model, SOS2ToMILPBridge{T})
129129
MOI.Bridges.add_bridge(model, SplitComplexEqualToBridge{T})
130+
MOI.Bridges.add_bridge(model, SplitComplexIndicatorEqualToBridge{T})
130131
MOI.Bridges.add_bridge(model, SplitComplexZerosBridge{T})
131132
MOI.Bridges.add_bridge(model, SplitHyperRectangleBridge{T})
132133
MOI.Bridges.add_bridge(model, SplitIntervalBridge{T})
Lines changed: 233 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,233 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
"""
8+
SplitComplexIndicatorEqualToBridge{T,F,G,A} <: Bridges.Constraint.AbstractBridge
9+
10+
`SplitComplexIndicatorEqualToBridge` implements the following reformulation:
11+
12+
* ``z \\implies f(x) + g(x) * im = a + b * im`` into ``z \\implies f(x) = a``
13+
and ``z \\implies g(x) = b``
14+
15+
## Source node
16+
17+
`SplitComplexIndicatorEqualToBridge` supports:
18+
19+
* `G` in [`MOI.Indicator{A,MOI.EqualTo{Complex{T}}`](@ref)
20+
21+
where `G` is a function with `Complex` coefficients.
22+
23+
## Target nodes
24+
25+
`SplitComplexIndicatorEqualToBridge` creates:
26+
27+
* `F` in [`MOI.Indicator{A,MOI.EqualTo{T}}`](@ref)
28+
29+
where `F` is the type of the real/imaginary part of `G`.
30+
"""
31+
struct SplitComplexIndicatorEqualToBridge{
32+
T,
33+
F<:MOI.Utilities.TypedVectorLike{T},
34+
G<:MOI.Utilities.TypedVectorLike{Complex{T}},
35+
A,
36+
} <: AbstractBridge
37+
real_constraint::Union{
38+
Nothing,
39+
MOI.ConstraintIndex{F,MOI.Indicator{A,MOI.EqualTo{T}}},
40+
}
41+
imag_constraint::Union{
42+
Nothing,
43+
MOI.ConstraintIndex{F,MOI.Indicator{A,MOI.EqualTo{T}}},
44+
}
45+
end
46+
47+
const SplitComplexIndicatorEqualTo{T,OT<:MOI.ModelLike} =
48+
SingleBridgeOptimizer{SplitComplexIndicatorEqualToBridge{T},OT}
49+
50+
function _add_constraint_if_nonzero(model, A, f, x, rhs::T) where {T}
51+
if iszero(f) && iszero(rhs)
52+
return nothing
53+
end
54+
g = MOI.Utilities.operate(vcat, T, x, f)
55+
return MOI.add_constraint(model, g, MOI.Indicator{A}(MOI.EqualTo(rhs)))
56+
end
57+
58+
function bridge_constraint(
59+
::Type{SplitComplexIndicatorEqualToBridge{T,F,G,A}},
60+
model::MOI.ModelLike,
61+
func::G,
62+
set::MOI.Indicator{A,MOI.EqualTo{Complex{T}}},
63+
) where {T,F,G,A}
64+
@assert MOI.output_dimension(func) == 2
65+
scalars = MOI.Utilities.scalarize(func)
66+
x, f = convert(MOI.VariableIndex, scalars[1]), scalars[2]
67+
rhs = set.set.value
68+
real_ci = _add_constraint_if_nonzero(model, A, real(f), x, real(rhs))
69+
imag_f = MOI.Utilities.operate(imag, T, f)
70+
imag_ci = _add_constraint_if_nonzero(model, A, imag_f, x, imag(rhs))
71+
return SplitComplexIndicatorEqualToBridge{T,F,G,A}(real_ci, imag_ci)
72+
end
73+
74+
# We don't support `MOI.VariableIndex` as it would be a self-loop in the bridge
75+
# graph.
76+
function MOI.supports_constraint(
77+
::Type{<:SplitComplexIndicatorEqualToBridge{T}},
78+
::Type{<:MOI.Utilities.TypedLike{Complex{T}}},
79+
::Type{MOI.Indicator{A,MOI.EqualTo{Complex{T}}}},
80+
) where {T,A}
81+
return true
82+
end
83+
84+
function MOI.Bridges.added_constrained_variable_types(
85+
::Type{<:SplitComplexIndicatorEqualToBridge},
86+
)
87+
return Tuple{Type}[]
88+
end
89+
90+
function MOI.Bridges.added_constraint_types(
91+
::Type{SplitComplexIndicatorEqualToBridge{T,F,G,A}},
92+
) where {T,F,G,A}
93+
return Tuple{Type,Type}[(F, MOI.Indicator{A,MOI.EqualTo{T}})]
94+
end
95+
96+
function concrete_bridge_type(
97+
::Type{<:SplitComplexIndicatorEqualToBridge{T}},
98+
G::Type{<:MOI.Utilities.TypedLike},
99+
::Type{MOI.Indicator{A,MOI.EqualTo{Complex{T}}}},
100+
) where {T,A}
101+
F = MA.promote_operation(imag, G)
102+
return SplitComplexIndicatorEqualToBridge{T,F,G,A}
103+
end
104+
105+
function MOI.get(
106+
model::MOI.ModelLike,
107+
attr::MOI.ConstraintFunction,
108+
bridge::SplitComplexIndicatorEqualToBridge{T,F,G,A},
109+
) where {T,F,G,A}
110+
H = MOI.Utilities.scalar_type(G)
111+
h = zero(H)
112+
x = nothing
113+
if bridge.real_constraint !== nothing
114+
f = MOI.get(model, attr, bridge.real_constraint)
115+
f_scalars = MOI.Utilities.scalarize(f)
116+
x = convert(MOI.VariableIndex, f_scalars[1])
117+
MOI.Utilities.operate!(+, Complex{T}, h, convert(H, f_scalars[2]))
118+
end
119+
if bridge.imag_constraint !== nothing
120+
f = MOI.get(model, attr, bridge.imag_constraint)
121+
f_scalars = MOI.Utilities.scalarize(f)
122+
x = convert(MOI.VariableIndex, f_scalars[1])
123+
MOI.Utilities.operate!(+, Complex{T}, h, im * f_scalars[2])
124+
end
125+
return MOI.Utilities.operate(vcat, Complex{T}, x, h)
126+
end
127+
128+
function MOI.get(
129+
model::MOI.ModelLike,
130+
::MOI.ConstraintSet,
131+
bridge::SplitComplexIndicatorEqualToBridge{T,F,G,A},
132+
) where {T,F,G,A}
133+
rhs = zero(T) + zero(T) * im
134+
if bridge.real_constraint !== nothing
135+
set = MOI.get(model, MOI.ConstraintSet(), bridge.real_constraint)
136+
rhs += set.set.value
137+
end
138+
if bridge.imag_constraint !== nothing
139+
set = MOI.get(model, MOI.ConstraintSet(), bridge.imag_constraint)
140+
rhs += set.set.value * im
141+
end
142+
return MOI.Indicator{A}(MOI.EqualTo(rhs))
143+
end
144+
145+
function MOI.get(
146+
bridge::SplitComplexIndicatorEqualToBridge{T,F,G,A},
147+
::MOI.NumberOfConstraints{F,MOI.Indicator{A,MOI.EqualTo{T}}},
148+
)::Int64 where {T,F,G,A}
149+
return Int64(bridge.real_constraint !== nothing) +
150+
Int64(bridge.imag_constraint !== nothing)
151+
end
152+
153+
function MOI.get(
154+
bridge::SplitComplexIndicatorEqualToBridge{T,F,G,A},
155+
::MOI.ListOfConstraintIndices{F,MOI.Indicator{A,MOI.EqualTo{T}}},
156+
) where {T,F,G,A}
157+
list = MOI.ConstraintIndex{F,MOI.Indicator{A,MOI.EqualTo{T}}}[]
158+
if bridge.real_constraint !== nothing
159+
push!(list, bridge.real_constraint)
160+
end
161+
if bridge.imag_constraint !== nothing
162+
push!(list, bridge.imag_constraint)
163+
end
164+
return list
165+
end
166+
167+
function MOI.delete(
168+
model::MOI.ModelLike,
169+
bridge::SplitComplexIndicatorEqualToBridge,
170+
)
171+
if bridge.real_constraint !== nothing
172+
MOI.delete(model, bridge.real_constraint)
173+
end
174+
if bridge.imag_constraint !== nothing
175+
MOI.delete(model, bridge.imag_constraint)
176+
end
177+
return
178+
end
179+
180+
function MOI.supports(
181+
model::MOI.ModelLike,
182+
attr::MOI.ConstraintPrimalStart,
183+
::Type{SplitComplexIndicatorEqualToBridge{T,F,G,A}},
184+
) where {T,F,G,A}
185+
return MOI.supports(
186+
model,
187+
attr,
188+
MOI.ConstraintIndex{F,MOI.Indicator{A,MOI.EqualTo{T}}},
189+
)
190+
end
191+
192+
function MOI.get(
193+
model::MOI.ModelLike,
194+
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
195+
bridge::SplitComplexIndicatorEqualToBridge{T},
196+
) where {T}
197+
ret = zeros(Complex{T}, 2)
198+
if bridge.real_constraint !== nothing
199+
value = MOI.get(model, attr, bridge.real_constraint)
200+
if value == nothing
201+
return nothing
202+
end
203+
ret[1] = value[1]
204+
ret[2] += value[2]
205+
end
206+
if bridge.imag_constraint !== nothing
207+
value = MOI.get(model, attr, bridge.imag_constraint)
208+
if value == nothing
209+
return nothing
210+
end
211+
ret[1] = value[1]
212+
ret[2] += value[2] * im
213+
end
214+
return ret
215+
end
216+
217+
_pass_nothing(f::Function, x::AbstractVector) = [x[1], f(x[2])]
218+
_pass_nothing(::Any, ::Nothing) = nothing
219+
220+
function MOI.set(
221+
model::MOI.ModelLike,
222+
attr::MOI.ConstraintPrimalStart,
223+
bridge::SplitComplexIndicatorEqualToBridge{T},
224+
value,
225+
) where {T}
226+
if bridge.real_constraint !== nothing
227+
MOI.set(model, attr, bridge.real_constraint, _pass_nothing(real, value))
228+
end
229+
if bridge.imag_constraint !== nothing
230+
MOI.set(model, attr, bridge.imag_constraint, _pass_nothing(imag, value))
231+
end
232+
return
233+
end
Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
# Copyright (c) 2017: Miles Lubin and contributors
2+
# Copyright (c) 2017: Google Inc.
3+
#
4+
# Use of this source code is governed by an MIT-style license that can be found
5+
# in the LICENSE.md file or at https://opensource.org/licenses/MIT.
6+
7+
module TestConstraintSplitComplexIndicatorEqualTo
8+
9+
using Test
10+
11+
import MathOptInterface as MOI
12+
13+
function runtests()
14+
for name in names(@__MODULE__; all = true)
15+
if startswith("$(name)", "test_")
16+
@testset "$(name)" begin
17+
getfield(@__MODULE__, name)()
18+
end
19+
end
20+
end
21+
return
22+
end
23+
24+
function test_runtests()
25+
MOI.Bridges.runtests(
26+
MOI.Bridges.Constraint.SplitComplexIndicatorEqualToBridge,
27+
"""
28+
variables: x, z
29+
::Complex{Float64}: [z, (1.0 + 2.0im) * x] in Indicator{ACTIVATE_ON_ONE}(EqualTo(3.0 + 4.0im))
30+
z in ZeroOne()
31+
""",
32+
"""
33+
variables: x, z
34+
::Float64: [z, 1.0 * x] in Indicator{ACTIVATE_ON_ONE}(EqualTo(3.0))
35+
::Float64: [z, 2.0 * x] in Indicator{ACTIVATE_ON_ONE}(EqualTo(4.0))
36+
z in ZeroOne()
37+
""",
38+
)
39+
MOI.Bridges.runtests(
40+
MOI.Bridges.Constraint.SplitComplexIndicatorEqualToBridge,
41+
"""
42+
variables: x, z
43+
::Complex{Float64}: [z, (1.0 + 2.0im) * x] in Indicator{ACTIVATE_ON_ZERO}(EqualTo(3.0 + 4.0im))
44+
z in ZeroOne()
45+
""",
46+
"""
47+
variables: x, z
48+
::Float64: [z, 1.0 * x] in Indicator{ACTIVATE_ON_ZERO}(EqualTo(3.0))
49+
::Float64: [z, 2.0 * x] in Indicator{ACTIVATE_ON_ZERO}(EqualTo(4.0))
50+
z in ZeroOne()
51+
""",
52+
)
53+
MOI.Bridges.runtests(
54+
MOI.Bridges.Constraint.SplitComplexIndicatorEqualToBridge,
55+
"""
56+
variables: x, z
57+
::Complex{Float64}: [z, (0.0 + 2.0im) * x] in Indicator{ACTIVATE_ON_ZERO}(EqualTo(0.0 + 4.0im))
58+
z in ZeroOne()
59+
""",
60+
"""
61+
variables: x, z
62+
::Float64: [z, 2.0 * x] in Indicator{ACTIVATE_ON_ZERO}(EqualTo(4.0))
63+
z in ZeroOne()
64+
""";
65+
constraint_start = [1.0, 0.0 + 1.2im],
66+
)
67+
MOI.Bridges.runtests(
68+
MOI.Bridges.Constraint.SplitComplexIndicatorEqualToBridge,
69+
"""
70+
variables: x, z
71+
::Complex{Float64}: [z, (1.0 + 0.0im) * x] in Indicator{ACTIVATE_ON_ZERO}(EqualTo(3.0 + 0.0im))
72+
z in ZeroOne()
73+
""",
74+
"""
75+
variables: x, z
76+
::Float64: [z, 1.0 * x] in Indicator{ACTIVATE_ON_ZERO}(EqualTo(3.0))
77+
z in ZeroOne()
78+
""";
79+
constraint_start = [1.0, 1.2 + 0.0im],
80+
)
81+
return
82+
end
83+
84+
end # module
85+
86+
TestConstraintSplitComplexIndicatorEqualTo.runtests()

0 commit comments

Comments
 (0)