@@ -1216,6 +1216,52 @@ end
12161216
12171217Base.:(== )(x:: Among , y:: Among ) = x. dimension == y. dimension && x. set == y. set
12181218
1219+ """
1220+ CountAtLeast(n::Int, d::Vector{Int}, set::Set{Int})
1221+
1222+ The set ``\\ {x \\ in \\ mathbb{Z}^{d_1 + d_2 + \\ ldots d_N}\\ }``, where `x` is
1223+ partitioned into `N` subsets (``\\ {x_1, \\ ldots, x_{d_1}\\ }``,
1224+ ``\\ {x_{d_1, 1}, \\ ldots, x_{d_1 + d_2}\\ }`` and so on), and at least ``n``
1225+ elements of each subset take one of the values in `set`.
1226+
1227+ ## Also known as
1228+
1229+ This constraint is called `at_least` in MiniZinc.
1230+
1231+ ## Example
1232+
1233+ ```julia
1234+ model = Utilities.Model{Float64}()
1235+ a, _ = add_constrained_variable(model, Integer())
1236+ b, _ = add_constrained_variable(model, Integer())
1237+ c, _ = add_constrained_variable(model, Integer())
1238+ # To ensure that `3` appears at least once in each of the subsets {a, b}, {b, c}
1239+ x, d, set = [a, b, b, c], [2, 2], [3]
1240+ add_constraint(model, VectorOfVariables(x), CountAtLeast(1, d, Set(set)))
1241+ ```
1242+ """
1243+ struct CountAtLeast <: AbstractVectorSet
1244+ n:: Int
1245+ partitions:: Vector{Int}
1246+ set:: Set{Int}
1247+ function CountAtLeast (
1248+ n:: Base.Integer ,
1249+ partitions:: Vector{Int} ,
1250+ set:: Set{Int} ,
1251+ )
1252+ if any (p <= 0 for p in partitions)
1253+ throw (DimensionMismatch (" Invalid partition dimension." ))
1254+ end
1255+ return new (n, partitions, set)
1256+ end
1257+ end
1258+
1259+ dimension (s:: CountAtLeast ) = sum (s. partitions)
1260+
1261+ function Base.:(== )(x:: CountAtLeast , y:: CountAtLeast )
1262+ return x. n == y. n && x. partitions == y. partitions && x. set == y. set
1263+ end
1264+
12191265# isbits types, nothing to copy
12201266function Base. copy (
12211267 set:: Union {
@@ -1253,13 +1299,18 @@ function Base.copy(
12531299 AllDifferent,
12541300 CountDistinct,
12551301 Among,
1302+ CountAtLeast,
12561303 },
12571304)
12581305 return set
12591306end
12601307Base. copy (set:: S ) where {S<: Union{SOS1,SOS2} } = S (copy (set. weights))
12611308Base. copy (set:: Among ) = Among (set. dimension, copy (set. set))
12621309
1310+ function Base. copy (set:: CountAtLeast )
1311+ return CountAtLeast (set. n, copy (set. partitions), copy (set. set))
1312+ end
1313+
12631314"""
12641315 supports_dimension_update(S::Type{<:MOI.AbstractVectorSet})
12651316
0 commit comments