Skip to content

Commit 622bb27

Browse files
committed
Add CountGreaterThan set (#1829)
1 parent 0a37d93 commit 622bb27

File tree

6 files changed

+88
-0
lines changed

6 files changed

+88
-0
lines changed

docs/src/reference/standard_form.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ AllDifferent
103103
Among
104104
CountAtLeast
105105
CountDistinct
106+
CountGreaterThan
106107
```
107108

108109
## Matrix sets

src/Test/test_basic_constraint.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -113,6 +113,7 @@ _set(::Type{MOI.AllDifferent}) = MOI.AllDifferent(3)
113113
_set(::Type{MOI.CountDistinct}) = MOI.CountDistinct(4)
114114
_set(::Type{MOI.Among}) = MOI.Among(4, Set([3, 4]))
115115
_set(::Type{MOI.CountAtLeast}) = MOI.CountAtLeast(1, [2, 2], Set([3]))
116+
_set(::Type{MOI.CountGreaterThan}) = MOI.CountGreaterThan(5)
116117

117118
function _set(
118119
::Type{MOI.Indicator{MOI.ACTIVATE_ON_ONE,MOI.LessThan{T}}},
@@ -280,6 +281,7 @@ for s in [
280281
:CountDistinct,
281282
:Among,
282283
:CountAtLeast,
284+
:CountGreaterThan,
283285
]
284286
S = getfield(MOI, s)
285287
functions = if S <: MOI.AbstractScalarSet

src/Test/test_cpsat.jl

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -172,3 +172,51 @@ function setup_test(
172172
)
173173
return
174174
end
175+
176+
"""
177+
test_cpsat_CountGreaterThan(model::MOI.ModelLike, config::Config)
178+
179+
Add a VectorOfVariables-in-CountGreaterThan constraint.
180+
"""
181+
function test_cpsat_CountGreaterThan(
182+
model::MOI.ModelLike,
183+
config::Config{T},
184+
) where {T}
185+
@requires MOI.supports_constraint(
186+
model,
187+
MOI.VectorOfVariables,
188+
MOI.CountGreaterThan,
189+
)
190+
@requires MOI.supports_add_constrained_variable(model, MOI.Integer)
191+
@requires _supports(config, MOI.optimize!)
192+
c, _ = MOI.add_constrained_variable(model, MOI.Integer())
193+
y, _ = MOI.add_constrained_variable(model, MOI.Integer())
194+
x = [MOI.add_constrained_variable(model, MOI.Integer())[1] for _ in 1:3]
195+
MOI.add_constraint(
196+
model,
197+
MOI.VectorOfVariables([c; y; x]),
198+
MOI.CountGreaterThan(5),
199+
)
200+
MOI.optimize!(model)
201+
c_val = round(Int, MOI.get(model, MOI.VariablePrimal(), c))
202+
y_val = round(Int, MOI.get(model, MOI.VariablePrimal(), y))
203+
x_val = round.(Int, MOI.get.(model, MOI.VariablePrimal(), x))
204+
@test c_val > sum(x_val[i] == y_val for i in 1:length(x))
205+
return
206+
end
207+
208+
function setup_test(
209+
::typeof(test_cpsat_CountGreaterThan),
210+
model::MOIU.MockOptimizer,
211+
::Config{T},
212+
) where {T}
213+
MOIU.set_mock_optimize!(
214+
model,
215+
(mock::MOIU.MockOptimizer) -> MOIU.mock_optimize!(
216+
mock,
217+
MOI.OPTIMAL,
218+
(MOI.FEASIBLE_POINT, T[2, 4, 0, 4, 0]),
219+
),
220+
)
221+
return
222+
end

src/Utilities/model.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -791,6 +791,7 @@ const LessThanIndicatorZero{T} =
791791
MOI.CountDistinct,
792792
MOI.Among,
793793
MOI.CountAtLeast,
794+
MOI.CountGreaterThan,
794795
),
795796
(
796797
MOI.PowerCone,

src/sets.jl

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1267,6 +1267,40 @@ function Base.:(==)(x::CountAtLeast, y::CountAtLeast)
12671267
return x.n == y.n && x.partitions == y.partitions && x.set == y.set
12681268
end
12691269

1270+
"""
1271+
CountGreaterThan(dimension::Int)
1272+
1273+
The set ``\\{(c, y, x) \\in \\mathbb{Z}^{1+1+d}\\}`` such that `c` is strictly
1274+
greater than the number of occurances of `y` in `x`.
1275+
1276+
## Also known as
1277+
1278+
This constraint is called `count_gt` in MiniZinc.
1279+
1280+
## Example
1281+
1282+
```julia
1283+
model = Utilities.Model{Float64}()
1284+
c, _ = add_constrained_variable(model, Integer())
1285+
y, _ = add_constrained_variable(model, Integer())
1286+
x = [add_constrained_variable(model, Integer())[1] for _ in 1:3]
1287+
add_constraint(model, VectorOfVariables([c; y; x]), CountGreaterThan(5))
1288+
```
1289+
"""
1290+
struct CountGreaterThan <: AbstractVectorSet
1291+
dimension::Int
1292+
function CountGreaterThan(dimension::Base.Integer)
1293+
if dimension < 2
1294+
throw(
1295+
DimensionMismatch(
1296+
"Dimension of CountGreaterThan must be >= 2.",
1297+
),
1298+
)
1299+
end
1300+
return new(dimension)
1301+
end
1302+
end
1303+
12701304
# isbits types, nothing to copy
12711305
function Base.copy(
12721306
set::Union{
@@ -1305,6 +1339,7 @@ function Base.copy(
13051339
CountDistinct,
13061340
Among,
13071341
CountAtLeast,
1342+
CountGreaterThan,
13081343
},
13091344
)
13101345
return set

test/sets.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -141,6 +141,7 @@ function test_sets_DimensionMismatch()
141141
(MOI.RootDetConeSquare, 0),
142142
(MOI.AllDifferent, 0),
143143
(MOI.CountDistinct, 1),
144+
(MOI.CountGreaterThan, 2),
144145
)
145146
@test_throws DimensionMismatch S(min_dimension - 1)
146147
@test S(min_dimension) isa S

0 commit comments

Comments
 (0)