Skip to content

Commit 30150ef

Browse files
authored
[Bridges] add CountAtLeastToCountBelongsBridge (#1920)
1 parent b1a5dc2 commit 30150ef

File tree

4 files changed

+254
-0
lines changed

4 files changed

+254
-0
lines changed

docs/src/submodules/Bridges/list_of_bridges.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ Bridges.Constraint.SemiToBinaryBridge
5353
Bridges.Constraint.ZeroOneBridge
5454
Bridges.Constraint.AllDifferentToCountDistinctBridge
5555
Bridges.Constraint.BinPackingToMILPBridge
56+
Bridges.Constraint.CountAtLeastToCountBelongsBridge
5657
Bridges.Constraint.CountBelongsToMILPBridge
5758
Bridges.Constraint.CountDistinctToMILPBridge
5859
Bridges.Constraint.TableToMILPBridge

src/Bridges/Constraint/Constraint.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ include("single_bridge_optimizer.jl")
2020

2121
include("bridges/all_different.jl")
2222
include("bridges/bin_packing.jl")
23+
include("bridges/count_at_least.jl")
2324
include("bridges/count_belongs.jl")
2425
include("bridges/count_distinct.jl")
2526
include("bridges/det.jl")
@@ -99,6 +100,7 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
99100
# TODO(odow): this reformulation assumes the bins are numbered 1..N. We
100101
# should fix this to use the variable bounds before adding automatically.
101102
# MOI.Bridges.add_bridge(bridged_model, BinPackingToMILPBridge{T})
103+
MOI.Bridges.add_bridge(bridged_model, CountAtLeastToCountBelongsBridge{T})
102104
MOI.Bridges.add_bridge(bridged_model, CountBelongsToMILPBridge{T})
103105
MOI.Bridges.add_bridge(bridged_model, CountDistinctToMILPBridge{T})
104106
MOI.Bridges.add_bridge(bridged_model, TableToMILPBridge{T})
Lines changed: 178 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,178 @@
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+
CountAtLeastToCountBelongsBridge{T,F} <: Bridges.Constraint.AbstractBridge
9+
10+
`CountAtLeastToCountBelongsBridge` implements the following reformulation:
11+
12+
* ``x \\in \\textsf{CountAtLeast}(n, d, set)`` to
13+
``(n_i, x_{d_i}) \\in \\textsf{CountBelongs}(1+d)``
14+
and ``\\sum\\limits n_i \\ge n``
15+
16+
## Source node
17+
18+
`CountAtLeastToCountBelongsBridge` supports:
19+
20+
* `F` in [`MOI.CountAtLeast`](@ref)
21+
22+
where `F` is [`MOI.VectorOfVariables`](@ref) or
23+
[`MOI.VectorAffineFunction{T}`](@ref).
24+
25+
## Target nodes
26+
27+
`CountAtLeastToCountBelongsBridge` creates:
28+
29+
* `F` in [`MOI.CountBelongs`](@ref)
30+
* [`MOI.ScalarAffineFunction{T}`](@ref) in [`MOI.GreaterThan{T}`](@ref)
31+
"""
32+
mutable struct CountAtLeastToCountBelongsBridge{
33+
T,
34+
F<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}},
35+
} <: AbstractBridge
36+
f::F
37+
s::MOI.CountAtLeast
38+
variables::Vector{MOI.VariableIndex}
39+
ci::Vector{MOI.ConstraintIndex{F,MOI.CountBelongs}}
40+
count::MOI.ConstraintIndex{MOI.ScalarAffineFunction{T},MOI.GreaterThan{T}}
41+
end
42+
43+
const CountAtLeastToCountBelongs{T,OT<:MOI.ModelLike} =
44+
SingleBridgeOptimizer{CountAtLeastToCountBelongsBridge{T},OT}
45+
46+
function bridge_constraint(
47+
::Type{CountAtLeastToCountBelongsBridge{T,F}},
48+
model::MOI.ModelLike,
49+
f::F,
50+
s::MOI.CountAtLeast,
51+
) where {T,F<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}}
52+
x = collect(MOI.Utilities.eachscalar(f))
53+
variables = MOI.VariableIndex[]
54+
cis = MOI.ConstraintIndex{F,MOI.CountBelongs}[]
55+
count_f = MOI.ScalarAffineFunction(MOI.ScalarAffineTerm{T}[], zero(T))
56+
offset = 0
57+
for p in s.partitions
58+
indices = offset .+ (1:p)
59+
y = MOI.add_variable(model)
60+
push!(variables, y)
61+
push!(count_f.terms, MOI.ScalarAffineTerm(one(T), y))
62+
ci = MOI.add_constraint(
63+
model,
64+
MOI.Utilities.operate(vcat, T, y, x[indices]...),
65+
MOI.CountBelongs(1 + p, s.set),
66+
)
67+
push!(cis, ci)
68+
offset += p
69+
end
70+
count = MOI.add_constraint(model, count_f, MOI.GreaterThan(T(s.n)))
71+
return CountAtLeastToCountBelongsBridge{T,F}(f, s, variables, cis, count)
72+
end
73+
74+
function MOI.supports_constraint(
75+
::Type{<:CountAtLeastToCountBelongsBridge{T}},
76+
::Type{<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}},
77+
::Type{MOI.CountAtLeast},
78+
) where {T}
79+
return true
80+
end
81+
82+
function MOI.Bridges.added_constrained_variable_types(
83+
::Type{<:CountAtLeastToCountBelongsBridge},
84+
)
85+
return Tuple{Type}[]
86+
end
87+
88+
function MOI.Bridges.added_constraint_types(
89+
::Type{CountAtLeastToCountBelongsBridge{T,F}},
90+
) where {T,F}
91+
return Tuple{Type,Type}[
92+
(F, MOI.CountBelongs),
93+
(MOI.ScalarAffineFunction{T}, MOI.GreaterThan{T}),
94+
]
95+
end
96+
97+
function concrete_bridge_type(
98+
::Type{<:CountAtLeastToCountBelongsBridge{T}},
99+
::Type{F},
100+
::Type{MOI.CountAtLeast},
101+
) where {T,F<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}}
102+
return CountAtLeastToCountBelongsBridge{T,F}
103+
end
104+
105+
function MOI.get(
106+
::MOI.ModelLike,
107+
::MOI.ConstraintFunction,
108+
bridge::CountAtLeastToCountBelongsBridge,
109+
)
110+
return bridge.f
111+
end
112+
113+
function MOI.get(
114+
::MOI.ModelLike,
115+
::MOI.ConstraintSet,
116+
bridge::CountAtLeastToCountBelongsBridge,
117+
)
118+
return bridge.s
119+
end
120+
121+
function MOI.delete(
122+
model::MOI.ModelLike,
123+
bridge::CountAtLeastToCountBelongsBridge,
124+
)
125+
for ci in bridge.ci
126+
MOI.delete(model, ci)
127+
end
128+
MOI.delete(model, bridge.count)
129+
for x in bridge.variables
130+
MOI.delete(model, x)
131+
end
132+
return
133+
end
134+
135+
function MOI.get(
136+
bridge::CountAtLeastToCountBelongsBridge,
137+
::MOI.NumberOfVariables,
138+
)::Int64
139+
return length(bridge.variables)
140+
end
141+
142+
function MOI.get(
143+
bridge::CountAtLeastToCountBelongsBridge,
144+
::MOI.ListOfVariableIndices,
145+
)::Vector{MOI.VariableIndex}
146+
return copy(bridge.variables)
147+
end
148+
149+
function MOI.get(
150+
bridge::CountAtLeastToCountBelongsBridge{T,F},
151+
::MOI.NumberOfConstraints{F,MOI.CountBelongs},
152+
)::Int64 where {T,F}
153+
return length(bridge.ci)
154+
end
155+
156+
function MOI.get(
157+
bridge::CountAtLeastToCountBelongsBridge{T,F},
158+
::MOI.ListOfConstraintIndices{F,MOI.CountBelongs},
159+
) where {T,F}
160+
return copy(bridge.ci)
161+
end
162+
163+
function MOI.get(
164+
::CountAtLeastToCountBelongsBridge{T,F},
165+
::MOI.NumberOfConstraints{MOI.ScalarAffineFunction{T},MOI.GreaterThan{T}},
166+
)::Int64 where {T,F}
167+
return 1
168+
end
169+
170+
function MOI.get(
171+
bridge::CountAtLeastToCountBelongsBridge{T,F},
172+
::MOI.ListOfConstraintIndices{
173+
MOI.ScalarAffineFunction{T},
174+
MOI.GreaterThan{T},
175+
},
176+
) where {T,F}
177+
return [bridge.count]
178+
end
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
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 TestConstraintCountAtLeast
8+
9+
using Test
10+
11+
using MathOptInterface
12+
const MOI = MathOptInterface
13+
14+
function runtests()
15+
for name in names(@__MODULE__; all = true)
16+
if startswith("$(name)", "test_")
17+
@testset "$(name)" begin
18+
getfield(@__MODULE__, name)()
19+
end
20+
end
21+
end
22+
return
23+
end
24+
25+
function test_runtests_VectorOfVariables()
26+
MOI.Bridges.runtests(
27+
MOI.Bridges.Constraint.CountAtLeastToCountBelongsBridge,
28+
"""
29+
variables: a, b, c
30+
[a, b, b, c] in CountAtLeast(1, [2, 2], Set([3]))
31+
a in Interval(1.0, 3.0)
32+
b in Interval(1.0, 3.0)
33+
c in Interval(1.0, 3.0)
34+
""",
35+
"""
36+
variables: a, b, c, y1, y2
37+
[y1, a, b] in CountBelongs(3, Set([3]))
38+
[y2, b, c] in CountBelongs(3, Set([3]))
39+
y1 + y2 >= 1.0
40+
a in Interval(1.0, 3.0)
41+
b in Interval(1.0, 3.0)
42+
c in Interval(1.0, 3.0)
43+
""",
44+
)
45+
return
46+
end
47+
48+
function test_runtests_VectorAffineFunction()
49+
MOI.Bridges.runtests(
50+
MOI.Bridges.Constraint.CountAtLeastToCountBelongsBridge,
51+
"""
52+
variables: a, b, c
53+
[1.0 * a, b + 1.0, b, c] in CountAtLeast(1, [2, 2], Set([3]))
54+
a in Interval(1.0, 3.0)
55+
b in Interval(1.0, 3.0)
56+
c in Interval(1.0, 3.0)
57+
""",
58+
"""
59+
variables: a, b, c, y1, y2
60+
[y1, 1.0 * a, b + 1.0] in CountBelongs(3, Set([3]))
61+
[y2, 1.0 * b, 1.0 * c] in CountBelongs(3, Set([3]))
62+
y1 + y2 >= 1.0
63+
a in Interval(1.0, 3.0)
64+
b in Interval(1.0, 3.0)
65+
c in Interval(1.0, 3.0)
66+
""",
67+
)
68+
return
69+
end
70+
71+
end # module
72+
73+
TestConstraintCountAtLeast.runtests()

0 commit comments

Comments
 (0)