Skip to content

Commit b1a5dc2

Browse files
authored
[Bridges] add AllDifferentToCountDistinctBridge (#1923)
1 parent 90d7dcf commit b1a5dc2

File tree

5 files changed

+282
-0
lines changed

5 files changed

+282
-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
@@ -51,6 +51,7 @@ Bridges.Constraint.IndicatorActiveOnFalseBridge
5151
Bridges.Constraint.IndicatorSOS1Bridge
5252
Bridges.Constraint.SemiToBinaryBridge
5353
Bridges.Constraint.ZeroOneBridge
54+
Bridges.Constraint.AllDifferentToCountDistinctBridge
5455
Bridges.Constraint.BinPackingToMILPBridge
5556
Bridges.Constraint.CountBelongsToMILPBridge
5657
Bridges.Constraint.CountDistinctToMILPBridge

src/Bridges/Constraint/Constraint.jl

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

21+
include("bridges/all_different.jl")
2122
include("bridges/bin_packing.jl")
2223
include("bridges/count_belongs.jl")
2324
include("bridges/count_distinct.jl")
@@ -94,6 +95,7 @@ function add_all_bridges(bridged_model, ::Type{T}) where {T}
9495
MOI.Bridges.add_bridge(bridged_model, SemiToBinaryBridge{T})
9596
MOI.Bridges.add_bridge(bridged_model, ZeroOneBridge{T})
9697
# Constraint programming bridges
98+
MOI.Bridges.add_bridge(bridged_model, AllDifferentToCountDistinctBridge{T})
9799
# TODO(odow): this reformulation assumes the bins are numbered 1..N. We
98100
# should fix this to use the variable bounds before adding automatically.
99101
# MOI.Bridges.add_bridge(bridged_model, BinPackingToMILPBridge{T})
Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
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+
AllDifferentToCountDistinctBridge{T,F} <: Bridges.Constraint.AbstractBridge
9+
10+
`AllDifferentToCountDistinctBridge` implements the following reformulations:
11+
12+
* ``x \\in \\textsf{AllDifferent}(d)`` to ``(n, x) \\in \\textsf{CountDistinct}(1+d)``
13+
and ``n = d``
14+
* ``f(x) \\in \\textsf{AllDifferent}(d)`` to ``(d, f(x)) \\in \\textsf{CountDistinct}(1+d)``
15+
16+
## Source node
17+
18+
`AllDifferentToCountDistinctBridge` supports:
19+
20+
* `F` in [`MOI.AllDifferent`](@ref)
21+
22+
where `F` is [`MOI.VectorOfVariables`](@ref) or
23+
[`MOI.VectorAffineFunction{T}`](@ref).
24+
25+
## Target nodes
26+
27+
`AllDifferentToCountDistinctBridge` creates:
28+
29+
* `F` in [`MOI.CountDistinct`](@ref)
30+
* [`MOI.VariableIndex`](@ref) in [`MOI.EqualTo{T}`](@ref)
31+
"""
32+
mutable struct AllDifferentToCountDistinctBridge{
33+
T,
34+
F<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}},
35+
} <: AbstractBridge
36+
f::F
37+
y::Union{Nothing,MOI.VariableIndex}
38+
ci::MOI.ConstraintIndex{F,MOI.CountDistinct}
39+
40+
function AllDifferentToCountDistinctBridge{T}(
41+
f::MOI.VectorOfVariables,
42+
y::MOI.VariableIndex,
43+
ci::MOI.ConstraintIndex{MOI.VectorOfVariables,MOI.CountDistinct},
44+
) where {T}
45+
return new{T,MOI.VectorOfVariables}(f, y, ci)
46+
end
47+
48+
function AllDifferentToCountDistinctBridge{T}(
49+
f::MOI.VectorAffineFunction{T},
50+
ci::MOI.ConstraintIndex{MOI.VectorAffineFunction{T},MOI.CountDistinct},
51+
) where {T}
52+
return new{T,MOI.VectorAffineFunction{T}}(f, nothing, ci)
53+
end
54+
end
55+
56+
const AllDifferentToCountDistinct{T,OT<:MOI.ModelLike} =
57+
SingleBridgeOptimizer{AllDifferentToCountDistinctBridge{T},OT}
58+
59+
function bridge_constraint(
60+
::Type{AllDifferentToCountDistinctBridge{T,F}},
61+
model::MOI.ModelLike,
62+
f::F,
63+
s::MOI.AllDifferent,
64+
) where {T,F<:MOI.VectorOfVariables}
65+
d = MOI.output_dimension(f)
66+
y, _ = MOI.add_constrained_variable(model, MOI.EqualTo(T(d)))
67+
ci = MOI.add_constraint(
68+
model,
69+
MOI.Utilities.operate(vcat, T, y, f),
70+
MOI.CountDistinct(d + 1),
71+
)
72+
return AllDifferentToCountDistinctBridge{T}(f, y, ci)
73+
end
74+
75+
function bridge_constraint(
76+
::Type{AllDifferentToCountDistinctBridge{T,F}},
77+
model::MOI.ModelLike,
78+
f::F,
79+
s::MOI.AllDifferent,
80+
) where {T,F<:MOI.VectorAffineFunction{T}}
81+
d = MOI.output_dimension(f)
82+
ci = MOI.add_constraint(
83+
model,
84+
MOI.Utilities.operate(vcat, T, T(d), f),
85+
MOI.CountDistinct(d + 1),
86+
)
87+
return AllDifferentToCountDistinctBridge{T}(f, ci)
88+
end
89+
90+
function MOI.supports_constraint(
91+
::Type{<:AllDifferentToCountDistinctBridge{T}},
92+
::Type{<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}},
93+
::Type{MOI.AllDifferent},
94+
) where {T}
95+
return true
96+
end
97+
98+
function MOI.Bridges.added_constrained_variable_types(
99+
::Type{AllDifferentToCountDistinctBridge{T,MOI.VectorOfVariables}},
100+
) where {T}
101+
return Tuple{Type}[(MOI.EqualTo{T},)]
102+
end
103+
104+
function MOI.Bridges.added_constrained_variable_types(
105+
::Type{AllDifferentToCountDistinctBridge{T,MOI.VectorAffineFunction{T}}},
106+
) where {T}
107+
return Tuple{Type}[]
108+
end
109+
110+
function MOI.Bridges.added_constraint_types(
111+
::Type{AllDifferentToCountDistinctBridge{T,F}},
112+
) where {T,F}
113+
return Tuple{Type,Type}[(F, MOI.CountDistinct),]
114+
end
115+
116+
function concrete_bridge_type(
117+
::Type{<:AllDifferentToCountDistinctBridge{T}},
118+
::Type{F},
119+
::Type{MOI.AllDifferent},
120+
) where {T,F<:Union{MOI.VectorOfVariables,MOI.VectorAffineFunction{T}}}
121+
return AllDifferentToCountDistinctBridge{T,F}
122+
end
123+
124+
function MOI.get(
125+
::MOI.ModelLike,
126+
::MOI.ConstraintFunction,
127+
bridge::AllDifferentToCountDistinctBridge,
128+
)
129+
return bridge.f
130+
end
131+
132+
function MOI.get(
133+
::MOI.ModelLike,
134+
::MOI.ConstraintSet,
135+
bridge::AllDifferentToCountDistinctBridge,
136+
)
137+
return MOI.AllDifferent(MOI.output_dimension(bridge.f))
138+
end
139+
140+
function MOI.delete(
141+
model::MOI.ModelLike,
142+
bridge::AllDifferentToCountDistinctBridge,
143+
)
144+
MOI.delete(model, bridge.ci)
145+
if bridge.y !== nothing
146+
MOI.delete(model, bridge.y)
147+
end
148+
return
149+
end
150+
151+
function MOI.get(
152+
bridge::AllDifferentToCountDistinctBridge,
153+
::MOI.NumberOfVariables,
154+
)::Int64
155+
if bridge.y === nothing
156+
return 0
157+
end
158+
return 1
159+
end
160+
161+
function MOI.get(
162+
bridge::AllDifferentToCountDistinctBridge,
163+
::MOI.ListOfVariableIndices,
164+
)::Vector{MOI.VariableIndex}
165+
if bridge.y === nothing
166+
return MOI.VariableIndex[]
167+
end
168+
return [bridge.y]
169+
end
170+
171+
function MOI.get(
172+
bridge::AllDifferentToCountDistinctBridge{T},
173+
::MOI.NumberOfConstraints{MOI.VariableIndex,MOI.EqualTo{T}},
174+
)::Int64 where {T}
175+
if bridge.y === nothing
176+
return 0
177+
end
178+
return 1
179+
end
180+
181+
function MOI.get(
182+
bridge::AllDifferentToCountDistinctBridge{T},
183+
::MOI.ListOfConstraintIndices{MOI.VariableIndex,MOI.EqualTo{T}},
184+
) where {T}
185+
if bridge.y === nothing
186+
return MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{T}}[]
187+
end
188+
ci = MOI.ConstraintIndex{MOI.VariableIndex,MOI.EqualTo{T}}(bridge.y.value)
189+
return [ci]
190+
end
191+
192+
function MOI.get(
193+
::AllDifferentToCountDistinctBridge{T,F},
194+
::MOI.NumberOfConstraints{F,MOI.CountDistinct},
195+
)::Int64 where {T,F}
196+
return 1
197+
end
198+
199+
function MOI.get(
200+
bridge::AllDifferentToCountDistinctBridge{T,F},
201+
::MOI.ListOfConstraintIndices{F,MOI.CountDistinct},
202+
) where {T,F}
203+
return [bridge.ci]
204+
end

src/Test/test_cpsat.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ function test_cpsat_AllDifferent(
2222
@requires _supports(config, MOI.optimize!)
2323
y = [MOI.add_constrained_variable(model, MOI.Integer()) for _ in 1:3]
2424
x = first.(y)
25+
MOI.add_constraint.(model, x, MOI.Interval(zero(T), T(2)))
2526
MOI.add_constraint(model, MOI.VectorOfVariables(x), MOI.AllDifferent(3))
2627
MOI.optimize!(model)
2728
x_val = MOI.get.(model, MOI.VariablePrimal(), x)
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
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 TestConstraintAllDifferent
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.AllDifferentToCountDistinctBridge,
28+
"""
29+
variables: x, y, z
30+
[x, y, z] in AllDifferent(3)
31+
x in Interval(1.0, 2.0)
32+
y >= 2.0
33+
y <= 3.0
34+
z == 2.0
35+
""",
36+
"""
37+
variables: x, y, z, n
38+
[n, x, y, z] in CountDistinct(4)
39+
x in Interval(1.0, 2.0)
40+
y >= 2.0
41+
y <= 3.0
42+
z == 2.0
43+
n == 3.0
44+
""",
45+
)
46+
return
47+
end
48+
49+
function test_runtests_VectorAffineFunction()
50+
MOI.Bridges.runtests(
51+
MOI.Bridges.Constraint.AllDifferentToCountDistinctBridge,
52+
"""
53+
variables: x, y, z
54+
[2.0 * x + -1.0, y, z] in AllDifferent(3)
55+
x in Interval(1.0, 2.0)
56+
y >= 2.0
57+
y <= 3.0
58+
z == 2.0
59+
""",
60+
"""
61+
variables: x, y, z
62+
[3.0, 2.0 * x + -1.0, y, z] in CountDistinct(4)
63+
x in Interval(1.0, 2.0)
64+
y >= 2.0
65+
y <= 3.0
66+
z == 2.0
67+
""",
68+
)
69+
return
70+
end
71+
72+
end # module
73+
74+
TestConstraintAllDifferent.runtests()

0 commit comments

Comments
 (0)