Constraints.jl: Streamlining Constraint Definition and Integration in Julia 
using Constraints
@info concept(:sum, [1, 2, 3, 4, 5]; op = ==, val=15)
@info concept(:sum, [1, 2, 3, 4, 5]; op = ==, val=2)
@info concept(:sum, [1, 2, 3, 4, 3]; op = ≤, val=15)
@info concept(:sum, [1, 2, 3, 4, 3]; op = ≤, val=3)using Constraints
c = x -> Constraints.xcsp_sum(
    list = x,
    condition = (>, 4),
    coeffs = [1,2,3,4]
)
@info c([1, 1, 1, 1])
@info c([0, 1, 0, 0])using CBLS, JuMP
model = Model(CBLS.Optimizer)
@variable(model, 1≤X[1:5]≤5, Int)
@variable(model, 1≤Y[1:5]≤5, Int)
@constraint(model, X in Sum(; op = ==, val = 15))
@constraint(model, Y in Sum(; op = <=, val = 10))
JuMP.optimize!(model)
@info "Sum" value.(X) value.(Y)# TODO: How to handle intention in JuMP/MOIusing Constraints
concept(:count, [1,1,1,2], vals = [1, 1, 1, 2], op = ==, val = 4) # true
concept(:count, [1,1,1,2], vals = [1, 1, 1, 2], op = ==, val = 5) # false
concept(:count, [2, 1, 4, 3]; vals=[1, 2, 3, 4], op=≥, val=2) # true
concept(:at_least, [1,1,1,2], vals = [1, 1, 1, 2], val = 4) # true
concept(:at_least, [1,1,1,2], vals = [1, 2], val = 4) # true
concept(:at_least, [1,1,1,2], vals = [1, 3], val = 4) # false
concept(:at_most, [1,1,1,2], vals = [1, 1, 1, 2], val = 4) # true
concept(:at_most, [1,1,1,2], vals = [2, 5, 3], val = 2) #true
concept(:at_most, [1,1,1,2], vals = [1, 1, 1, 3], val = 3) # true
concept(:exactly, [1,1,1,2], vals = [1, 3, 4, 2], val = 4) # true
concept(:exactly, [1,1,1,2], vals = [1, 1, 2, 3], val = 4) # true
concept(:exactly, [1,1,1,2], vals = [1, 1, 1, 3], val = 4) # falseusing Constraints
c_count = x -> Constraints.xcsp_count(
    list = x,
		condition = (≥, 4),
		values = [1, 2, 3]
)
@info c_count([1, 1, 1, 1, 5]) # true
@info c_count([0, 2, 3, 8]) # falseusing CBLS, JuMP
model = Model(CBLS.Optimizer)
@variable(model, 1≤X[1:4]≤4, Int)
@variable(model, 1≤X_at_least[1:4]≤4, Int)
@variable(model, 1≤X_at_most[1:4]≤4, Int)
@variable(model, 1≤X_exactly[1:4]≤4, Int)
@constraint(model, X in Count(vals = [1, 2, 3, 4], op = ≥, val = 2))
@constraint(model, X_at_least in AtLeast(vals = [1, 2, 3, 4], val = 2))
@constraint(model, X_at_most in AtMost(vals = [1, 2], val = 1))
@constraint(model, X_exactly in Exactly(vals = [1, 2], val = 2))
JuMP.optimize!(model)
@info "Count" value.(X) value.(X_at_least) value.(X_at_most) value.(X_exactly)# TODO: How to handle intention in JuMP/MOIusing Constraints
@info concept(:nvalues, [1, 2, 3, 4, 5]; op = ==, val = 5)
@info concept(:nvalues, [1, 2, 3, 4, 5]; op = ==, val = 2)
@info concept(:nvalues, [1, 2, 3, 4, 3]; op = <=, val = 5)
@info concept(:nvalues, [1, 2, 3, 4, 3]; op = <=, val = 3)using Constraints
c = x -> Constraints.xcsp_nvalues(
   	list = x,
		condition = (≥, 3),
		except = [1,2,3,4]
)
@info c([1, 1, 1, 1])
@info c([9, 3, 6, 8])using CBLS, JuMP
model = Model(CBLS.Optimizer)
@variable(model, 1≤X[1:5]≤5, Int)
@variable(model, 1≤Y[1:5]≤5, Int)
@variable(model, 1≤Z[1:5]≤5, Int)
@constraint(model, X in NValues(; op = ==, val = 5))
@constraint(model, Y in NValues(; op = ==, val = 2))
@constraint(model, Z in NValues(; op = <=, val = 5, vals = [1, 2]))
JuMP.optimize!(model)
@info "NValues" value.(X) value.(Y) value.(Z)# TODO: How to handle intention in JuMP/MOIusing Constraints
# [v1, v2, v3], [v1, a1, a2; v2, b1, b2; v3, c1, c2] means v1 occurs between a1 and a2 times in the first array, similar for v2 and v3.
@info concept(:cardinality, [2, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
@info concept(:cardinality, [8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3], bool=false)
@info concept(:cardinality, [8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3], bool=true)
@info concept(:cardinality, [2, 5, 10, 10]; vals=[2 1; 5 1; 10 2])
@info concept(:cardinality_closed, [8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
@info concept(:cardinality_open, [8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])using Constraints
c = x -> Constraints.xcsp_cardinality(
   	list = x,
		values = [1, 2],
		occurs = [2 3; 1 5]
)
@info c([1, 2, 1, 2])using CBLS, JuMP
model = Model(CBLS.Optimizer)
@variable(model, 1≤X[1:4]≤10, Int)
@variable(model, 1≤Y[1:4]≤10, Int)
@variable(model, 1≤Z[1:4]≤10, Int)
@constraint(model, X in Cardinality(; vals = [2 0 1; 5 1 3; 10 2 3]))
@constraint(model, Y in CardinalityOpen(; vals = [2 0 1; 5 1 3; 10 2 3]))
@constraint(model, Z in CardinalityClosed(; vals = [2 0 1; 5 1 3; 10 2 3]))
JuMP.optimize!(model)
@info "Cardinality" value.(X) value.(Y) value.(Z)# TODO: How to handle intention in JuMP/MOICounting and Summing Constraints 
Constraints.xcsp_sum Function
xcsp_sum(list, coeffs, condition)Return true if the sum of the variables in list satisfies the given condition, false otherwise.
Arguments
- list::Vector{Int}: list of values to check.
- coeffs::Vector{Int}: list of coefficients to use.
- condition: condition to satisfy.
Variants
- :sum: Global constraint ensuring that the sum of the variables in- xsatisfies a given numerical condition.
concept(:sum, x; op===, pair_vars=ones(x), val)
concept(:sum)(x; op===, pair_vars=ones(x), val)Examples
c = concept(:sum)
c([1, 2, 3, 4, 5]; op===, val=15)
c([1, 2, 3, 4, 5]; op===, val=2)
c([1, 2, 3, 4, 3]; op=≤, val=15)
c([1, 2, 3, 4, 3]; op=≤, val=3)Constraints.xcsp_count Function
xcsp_count(list, values, condition)Return true if the number of occurrences of the values in values in list satisfies the given condition, false otherwise.
Arguments
- list::Vector{Int}: list of values to check.
- values::Vector{Int}: list of values to check.
- condition: condition to satisfy.
Variants
- :count: Constraint ensuring that the number of occurrences of the values in- valsin- xsatisfies the given condition.
concept(:count, x; vals, op, val)
concept(:count)(x; vals, op, val)- :at_least: Constraint ensuring that the number of occurrences of the values in- valsin- xis at least- val.
concept(:at_least, x; vals, val)
concept(:at_least)(x; vals, val)- :at_most: Constraint ensuring that the number of occurrences of the values in- valsin- xis at most- val.
concept(:at_most, x; vals, val)
concept(:at_most)(x; vals, val)- :exactly: Constraint ensuring that the number of occurrences of the values in- valsin- xis exactly- val.
concept(:exactly, x; vals, val)
concept(:exactly)(x; vals, val)Examples
c = concept(:count)
c([2, 1, 4, 3]; vals=[1, 2, 3, 4], op=≥, val=2)
c([1, 2, 3, 4]; vals=[1, 2], op==, val=2)
c([2, 1, 4, 3]; vals=[1, 2], op=≤, val=1)Constraints.xcsp_nvalues Function
xcsp_nvalues(list, condition, except)Return true if the number of distinct values in list satisfies the given condition, false otherwise.
Arguments
- list::Vector{Int}: list of values to check.
- condition: condition to satisfy.
- except::Union{Nothing, Vector{Int}}: list of values to exclude. Default is- nothing.
Variants
- :nvalues: Ensures that the number of distinct values in- xsatisfies a given numerical condition.
The constraint is defined by the following expression: nValues(x, op, val) where x is a list of variables, op is a comparison operator, and val is an integer value.
concept(:nvalues, x; op, val)
concept(:nvalues)(x; op, val)Examples
c = concept(:nvalues)
c([1, 2, 3, 4, 5]; op = ==, val = 5)
c([1, 2, 3, 4, 5]; op = ==, val = 2)
c([1, 2, 3, 4, 3]; op = <=, val = 5)
c([1, 2, 3, 4, 3]; op = <=, val = 3)Constraints.xcsp_cardinality Function
xcsp_cardinality(list, values, occurs, closed)Return true if the number of occurrences of the values in values in list satisfies the given condition, false otherwise.
Arguments
- list::Vector{Int}: list of values to check.
- values::Vector{Int}: list of values to check.
- occurs::Vector{Int}: list of occurrences to check.
- closed::Bool: whether the constraint is closed or not.
Variants
- :cardinality: Global constraint that restricts the number of times specific values in a list- valuescan appear in- x.
concept(:cardinality, x; bool=false, vals)
concept(:cardinality)(x; bool=false, vals)- :cardinality_closed: Global constraint that restricts the number of times in a list- valuescan appear in- x. It is closed, meaning that the variables in- xcannot have values outside the ones in- list.
concept(:cardinality_closed, x; vals)
concept(:cardinality_closed)(x; vals)- :cardinality_open: Global constraint that restricts the number of times in a list- valuescan appear in- x. It is open, meaning that the variables in- xcan have values outside the ones in- list.
concept(:cardinality_open, x; vals)
concept(:cardinality_open)(x; vals)Examples
c = concept(:cardinality)
c([2, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
c([8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3], bool=false)
c([8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3], bool=true)
c([2, 5, 10, 10]; vals=[2 1; 5 1; 10 2])
c([2, 5, 10, 10]; vals=[2 0 1 42; 5 1 3 7; 10 2 3 -4])
c([2, 5, 5, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
c([2, 5, 10, 8]; vals=[2 1; 5 1; 10 2])
c([5, 5, 5, 10]; vals=[2 0 1 42; 5 1 3 7; 10 2 3 -4])
cc = concept(:cardinality_closed)
cc([8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
co = concept(:cardinality_open)
co([8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])