Skip to content

Commit 9263978

Browse files
authored
[Bridges] add SplitComplexIndicatorEqualToBridge (#2921)
1 parent b1341b5 commit 9263978

3 files changed

Lines changed: 319 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: 232 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,232 @@
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} <:
9+
Bridges.Constraint.AbstractBridge
10+
11+
`SplitComplexIndicatorEqualToBridge` implements the following reformulation:
12+
13+
* ``z \\implies f(x) + g(x) * im = a + b * im`` into ``z \\implies f(x) = a``
14+
and ``z \\implies g(x) = b``
15+
16+
## Source node
17+
18+
`SplitComplexIndicatorEqualToBridge` supports:
19+
20+
* `G` in `MOI.Indicator{A,MOI.EqualTo{Complex{T}}`
21+
22+
where `G` is a function with `Complex` coefficients.
23+
24+
## Target nodes
25+
26+
`SplitComplexIndicatorEqualToBridge` creates:
27+
28+
* `F` in `MOI.Indicator{A,MOI.EqualTo{T}}`
29+
30+
where `F` is the type of the real/imaginary part of `G`.
31+
"""
32+
struct SplitComplexIndicatorEqualToBridge{
33+
T,
34+
F<:MOI.Utilities.TypedVectorLike{T},
35+
G<:MOI.Utilities.TypedVectorLike{Complex{T}},
36+
A,
37+
} <: AbstractBridge
38+
real_constraint::Union{
39+
Nothing,
40+
MOI.ConstraintIndex{F,MOI.Indicator{A,MOI.EqualTo{T}}},
41+
}
42+
imag_constraint::Union{
43+
Nothing,
44+
MOI.ConstraintIndex{F,MOI.Indicator{A,MOI.EqualTo{T}}},
45+
}
46+
end
47+
48+
const SplitComplexIndicatorEqualTo{T,OT<:MOI.ModelLike} =
49+
SingleBridgeOptimizer{SplitComplexIndicatorEqualToBridge{T},OT}
50+
51+
function _add_constraint_if_nonzero(model, A, f, x, rhs::T) where {T}
52+
if iszero(f) && iszero(rhs)
53+
return nothing
54+
end
55+
g = MOI.Utilities.operate(vcat, T, x, f)
56+
return MOI.add_constraint(model, g, MOI.Indicator{A}(MOI.EqualTo(rhs)))
57+
end
58+
59+
function bridge_constraint(
60+
::Type{SplitComplexIndicatorEqualToBridge{T,F,G,A}},
61+
model::MOI.ModelLike,
62+
func::G,
63+
set::MOI.Indicator{A,MOI.EqualTo{Complex{T}}},
64+
) where {T,F,G,A}
65+
@assert MOI.output_dimension(func) == 2
66+
scalars = MOI.Utilities.scalarize(func)
67+
x, f = convert(MOI.VariableIndex, scalars[1]), scalars[2]
68+
rhs = set.set.value
69+
real_ci = _add_constraint_if_nonzero(model, A, real(f), x, real(rhs))
70+
imag_f = MOI.Utilities.operate(imag, T, f)
71+
imag_ci = _add_constraint_if_nonzero(model, A, imag_f, x, imag(rhs))
72+
return SplitComplexIndicatorEqualToBridge{T,F,G,A}(real_ci, imag_ci)
73+
end
74+
75+
function MOI.supports_constraint(
76+
::Type{<:SplitComplexIndicatorEqualToBridge{T}},
77+
::Type{<:MOI.Utilities.TypedVectorLike{Complex{T}}},
78+
::Type{MOI.Indicator{A,MOI.EqualTo{Complex{T}}}},
79+
) where {T,A}
80+
return true
81+
end
82+
83+
function MOI.Bridges.added_constrained_variable_types(
84+
::Type{<:SplitComplexIndicatorEqualToBridge},
85+
)
86+
return Tuple{Type}[]
87+
end
88+
89+
function MOI.Bridges.added_constraint_types(
90+
::Type{SplitComplexIndicatorEqualToBridge{T,F,G,A}},
91+
) where {T,F,G,A}
92+
return Tuple{Type,Type}[(F, MOI.Indicator{A,MOI.EqualTo{T}})]
93+
end
94+
95+
function concrete_bridge_type(
96+
::Type{<:SplitComplexIndicatorEqualToBridge{T}},
97+
G::Type{<:MOI.Utilities.TypedLike},
98+
::Type{MOI.Indicator{A,MOI.EqualTo{Complex{T}}}},
99+
) where {T,A}
100+
F = MA.promote_operation(imag, G)
101+
return SplitComplexIndicatorEqualToBridge{T,F,G,A}
102+
end
103+
104+
function MOI.get(
105+
model::MOI.ModelLike,
106+
attr::MOI.ConstraintFunction,
107+
bridge::SplitComplexIndicatorEqualToBridge{T,F,G,A},
108+
) where {T,F,G,A}
109+
H = MOI.Utilities.scalar_type(G)
110+
h = zero(H)
111+
x = nothing
112+
if bridge.real_constraint !== nothing
113+
f = MOI.get(model, attr, bridge.real_constraint)
114+
f_scalars = MOI.Utilities.scalarize(f)
115+
x = convert(MOI.VariableIndex, f_scalars[1])
116+
MOI.Utilities.operate!(+, Complex{T}, h, convert(H, f_scalars[2]))
117+
end
118+
if bridge.imag_constraint !== nothing
119+
f = MOI.get(model, attr, bridge.imag_constraint)
120+
f_scalars = MOI.Utilities.scalarize(f)
121+
x = convert(MOI.VariableIndex, f_scalars[1])
122+
MOI.Utilities.operate!(+, Complex{T}, h, im * f_scalars[2])
123+
end
124+
return MOI.Utilities.operate(vcat, Complex{T}, x, h)
125+
end
126+
127+
function MOI.get(
128+
model::MOI.ModelLike,
129+
::MOI.ConstraintSet,
130+
bridge::SplitComplexIndicatorEqualToBridge{T,F,G,A},
131+
) where {T,F,G,A}
132+
rhs = zero(T) + zero(T) * im
133+
if bridge.real_constraint !== nothing
134+
set = MOI.get(model, MOI.ConstraintSet(), bridge.real_constraint)
135+
rhs += set.set.value
136+
end
137+
if bridge.imag_constraint !== nothing
138+
set = MOI.get(model, MOI.ConstraintSet(), bridge.imag_constraint)
139+
rhs += set.set.value * im
140+
end
141+
return MOI.Indicator{A}(MOI.EqualTo(rhs))
142+
end
143+
144+
function MOI.get(
145+
bridge::SplitComplexIndicatorEqualToBridge{T,F,G,A},
146+
::MOI.NumberOfConstraints{F,MOI.Indicator{A,MOI.EqualTo{T}}},
147+
)::Int64 where {T,F,G,A}
148+
return Int64(bridge.real_constraint !== nothing) +
149+
Int64(bridge.imag_constraint !== nothing)
150+
end
151+
152+
function MOI.get(
153+
bridge::SplitComplexIndicatorEqualToBridge{T,F,G,A},
154+
::MOI.ListOfConstraintIndices{F,MOI.Indicator{A,MOI.EqualTo{T}}},
155+
) where {T,F,G,A}
156+
list = MOI.ConstraintIndex{F,MOI.Indicator{A,MOI.EqualTo{T}}}[]
157+
if bridge.real_constraint !== nothing
158+
push!(list, bridge.real_constraint)
159+
end
160+
if bridge.imag_constraint !== nothing
161+
push!(list, bridge.imag_constraint)
162+
end
163+
return list
164+
end
165+
166+
function MOI.delete(
167+
model::MOI.ModelLike,
168+
bridge::SplitComplexIndicatorEqualToBridge,
169+
)
170+
if bridge.real_constraint !== nothing
171+
MOI.delete(model, bridge.real_constraint)
172+
end
173+
if bridge.imag_constraint !== nothing
174+
MOI.delete(model, bridge.imag_constraint)
175+
end
176+
return
177+
end
178+
179+
function MOI.supports(
180+
model::MOI.ModelLike,
181+
attr::MOI.ConstraintPrimalStart,
182+
::Type{SplitComplexIndicatorEqualToBridge{T,F,G,A}},
183+
) where {T,F,G,A}
184+
return MOI.supports(
185+
model,
186+
attr,
187+
MOI.ConstraintIndex{F,MOI.Indicator{A,MOI.EqualTo{T}}},
188+
)
189+
end
190+
191+
function MOI.get(
192+
model::MOI.ModelLike,
193+
attr::Union{MOI.ConstraintPrimal,MOI.ConstraintPrimalStart},
194+
bridge::SplitComplexIndicatorEqualToBridge{T},
195+
) where {T}
196+
ret = zeros(Complex{T}, 2)
197+
if bridge.real_constraint !== nothing
198+
value = MOI.get(model, attr, bridge.real_constraint)
199+
if value == nothing
200+
return nothing
201+
end
202+
ret[1] = value[1]
203+
ret[2] += value[2]
204+
end
205+
if bridge.imag_constraint !== nothing
206+
value = MOI.get(model, attr, bridge.imag_constraint)
207+
if value == nothing
208+
return nothing
209+
end
210+
ret[1] = value[1]
211+
ret[2] += value[2] * im
212+
end
213+
return ret
214+
end
215+
216+
_pass_nothing(f::Function, x::AbstractVector) = [x[1], f(x[2])]
217+
_pass_nothing(::Any, ::Nothing) = nothing
218+
219+
function MOI.set(
220+
model::MOI.ModelLike,
221+
attr::MOI.ConstraintPrimalStart,
222+
bridge::SplitComplexIndicatorEqualToBridge{T},
223+
value,
224+
) where {T}
225+
if bridge.real_constraint !== nothing
226+
MOI.set(model, attr, bridge.real_constraint, _pass_nothing(real, value))
227+
end
228+
if bridge.imag_constraint !== nothing
229+
MOI.set(model, attr, bridge.imag_constraint, _pass_nothing(imag, value))
230+
end
231+
return
232+
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)