Skip to content

Commit

Permalink
Bench updates (#36)
Browse files Browse the repository at this point in the history
* Save for benchs

* Tests for circuit count cumulative element extension instantiation

* Tests for intention maximum mdd minimum

* Tests for nvalues nooverlap ordered regular sum

* Update no_overlap to match pairvars parameter generation

* Fixes channel and element signatures

* Versions bump in toml and ci

* Versions bump in ci

* Remove push activation for CI
  • Loading branch information
Azzaare authored Oct 6, 2023
1 parent 5369229 commit 3c48c33
Show file tree
Hide file tree
Showing 23 changed files with 509 additions and 75 deletions.
3 changes: 1 addition & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
name: CI
on:
- push
- pull_request
jobs:
test:
Expand All @@ -10,7 +9,7 @@ jobs:
fail-fast: false
matrix:
version:
- "1.8"
- "1.9"
- 'nightly'
os:
- ubuntu-latest
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Constraints"
uuid = "30f324ab-b02d-43f0-b619-e131c61659f7"
authors = ["Jean-François Baffier"]
version = "0.5.2"
version = "0.5.3"

[deps]
CompositionalNetworks = "4b67e4b5-442d-4ef5-b760-3f5df3a57537"
Expand Down
10 changes: 6 additions & 4 deletions src/constraint.jl
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,16 @@ mutable struct Constraint{FConcept<:Function,FError<:Function}
concept::FConcept
description::String
error::FError
params::Vector{Dict{Symbol, Bool}}
params::Vector{Dict{Symbol,Bool}}
symmetries::Set{Symbol}

function Constraint(;
args=0,
concept=x -> true,
description="No given description!",
error=(x; param=0, dom_size=0) -> Float64(!concept(x)),
params=Vector{Dict{Symbol, Bool}}(),
syms=Set{Symbol}(),
params=Vector{Dict{Symbol,Bool}}(),
syms=Set{Symbol}()
)
return new{typeof(concept),typeof(error)}(
args, concept, description, error, params, syms
Expand Down Expand Up @@ -85,7 +85,9 @@ Simply delete the `concept_` part of symbol or string starting with it. TODO: ad
"""
shrink_concept(s) = Symbol(string(s)[9:end])

## SECTION - Test Items
function concept_vs_error(c, e, args...; kargs...)
return c(args...; kargs...) (e(args...; kargs...) > 0.0)
end
@testitem "Empty constraint" tags = [:constraint, :empty] begin
c = Constraint()
@test concept(c, []) == true
Expand Down
19 changes: 18 additions & 1 deletion src/constraints/all_different.jl
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,28 @@
xcsp_all_different(list, ::Nothing) = allunique(list)

function xcsp_all_different(list, except)
return xcsp_all_different(list=Iterators.filter(x -> x except, list))
return xcsp_all_different(list=Iterators.filter(x -> x except[:, 1], list))
end

xcsp_all_different(; list, except=nothing) = xcsp_all_different(list, except)

const description_all_different = """Global constraint ensuring that all the values of a given configuration are unique"""

@usual concept_all_different(x; vals=nothing) = xcsp_all_different(list=x, except=vals)

## SECTION - Test Items
@testitem "All Different" tags = [:usual, :constraints, :all_different] begin
c = USUAL_CONSTRAINTS[:all_different] |> concept
e = USUAL_CONSTRAINTS[:all_different] |> error_f
vs = Constraints.concept_vs_error

@test c([1, 2, 3, 4])
@test !c([1, 2, 3, 1])
@test c([1, 0, 0, 4]; vals=[0])
@test !c([1, 0, 0, 1]; vals=[0])

@test vs(c, e, [1, 2, 3, 4])
@test vs(c, e, [1, 2, 3, 1])
@test vs(c, e, [1, 0, 0, 4]; vals=[0])
@test vs(c, e, [1, 0, 0, 1]; vals=[0])
end
30 changes: 28 additions & 2 deletions src/constraints/all_equal.jl
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,33 @@ concept_all_equal(x, val) = all(y -> y == val, x)

xcsp_all_equal(; list) = concept_all_equal(list; val=first(list))

@usual function concept_all_equal(x; val=nothing, pair_vars=zero(x))
return concept_all_equal(x+pair_vars, val)
@usual function concept_all_equal(x; val=nothing, pair_vars=zero(x), op=+)
if iszero(pair_vars)
return concept_all_equal(x, val)
else
aux = map(t -> op(t...), Iterators.zip(x, pair_vars))
return concept_all_equal(aux, val)
end
end
concept_all_equal(x, ::Nothing) = xcsp_all_equal(list=x)

## SECTION - Test Items
@testitem "All Equal" tags = [:usual, :constraints, :all_equal] begin
c = USUAL_CONSTRAINTS[:all_equal] |> concept
e = USUAL_CONSTRAINTS[:all_equal] |> error_f
vs = Constraints.concept_vs_error

@test c([0, 0, 0, 0])
@test !c([1, 2, 3, 4])
@test c([3, 2, 1, 0]; pair_vars=[0, 1, 2, 3])
@test !c([0, 1, 2, 3]; pair_vars=[0, 1, 2, 3])
@test c([1, 2, 3, 4]; op=/, val=1, pair_vars=[1, 2, 3, 4])
@test !c([1, 2, 3, 4]; op=*, val=1, pair_vars=[1, 2, 3, 4])

@test vs(c, e, [0, 0, 0, 0])
@test vs(c, e, [1, 2, 3, 4])
@test vs(c, e, [3, 2, 1, 0]; pair_vars=[0, 1, 2, 3])
@test vs(c, e, [0, 1, 2, 3]; pair_vars=[0, 1, 2, 3])
@test vs(c, e, [1, 2, 3, 4]; op=/, val=1, pair_vars=[1, 2, 3, 4])
@test vs(c, e, [1, 2, 3, 4]; op=*, val=1, pair_vars=[1, 2, 3, 4])
end
47 changes: 43 additions & 4 deletions src/constraints/cardinality.jl
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,53 @@ function xcsp_cardinality(; list, values, occurs, closed=false)
end

@usual function concept_cardinality(x; bool=false, vals)
values = vals[1]
occurs = if length(vals) 3
map(i -> vals[2][i]:(vals[2][i] vals[3][i] ? 1 : -1):(vals[3][i]), 1:length(vals[1]))
values = vals[:, 1]
occurs = if size(vals, 2) == 1
ones(Int, size(vals, 1))
elseif size(vals, 2) 3
map(i -> vals[i, 2]:(vals[i, 2] vals[i, 3] ? 1 : -1):(vals[i, 3]), 1:size(vals, 1))
else
vals[2]
vals[:, 2]
end
return xcsp_cardinality(; list=x, values, occurs, closed=bool)
end

@usual concept_cardinality_closed(x; vals) = concept_cardinality(x; bool=true, vals)
@usual concept_cardinality_open(x; vals) = concept_cardinality(x; bool=false, vals)

## SECTION - Test Items
@testitem "Cardinality" tags = [:usual, :constraints, :cardinality] begin
c = USUAL_CONSTRAINTS[:cardinality] |> concept
e = USUAL_CONSTRAINTS[:cardinality] |> error_f
vs = Constraints.concept_vs_error

@test c([2, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
@test c([8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3], bool=false)
@test !c([8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3], bool=true)
@test c([2, 5, 10, 10]; vals=[2 1; 5 1; 10 2])
@test c([2, 5, 10, 10]; vals=[2 0 1 42; 5 1 3 7; 10 2 3 -4])
@test !c([2, 5, 5, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
@test !c([2, 5, 10, 8]; vals=[2 1; 5 1; 10 2])
@test !c([5, 5, 5, 10]; vals=[2 0 1 42; 5 1 3 7; 10 2 3 -4])


@test vs(c, e, [2, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
@test vs(c, e, [8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3], bool=false)
@test vs(c, e, [8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3], bool=true)
@test vs(c, e, [2, 5, 10, 10]; vals=[2 1; 5 1; 10 2])
@test vs(c, e, [2, 5, 10, 10]; vals=[2 0 1 42; 5 1 3 7; 10 2 3 -4])
@test vs(c, e, [2, 5, 5, 10]; vals=[2 0 1; 5 1 3; 10 2 3])
@test vs(c, e, [2, 5, 10, 8]; vals=[2 1; 5 1; 10 2])
@test vs(c, e, [5, 5, 5, 10]; vals=[2 0 1 42; 5 1 3 7; 10 2 3 -4])

cc = USUAL_CONSTRAINTS[:cardinality_closed] |> concept
ec = USUAL_CONSTRAINTS[:cardinality_closed] |> error_f
@test cc([8, 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=true)
@test vs(cc, ec, [8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])

co = USUAL_CONSTRAINTS[:cardinality_open] |> concept
eo = USUAL_CONSTRAINTS[:cardinality_open] |> error_f
@test co([8, 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)
@test vs(co, eo, [8, 5, 10, 10]; vals=[2 0 1; 5 1 3; 10 2 3])

end
44 changes: 37 additions & 7 deletions src/constraints/channel.jl
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
function xcsp_channel(list)
function xcsp_channel(list::AbstractVector)
for (i, j) in enumerate(list)
if i j
list[j] == i || return false
Expand All @@ -7,17 +7,24 @@ function xcsp_channel(list)
return true
end

function xcsp_channel(l1, l2)
for (i, j) in enumerate(l2)
function xcsp_channel(list::Tuple)
l1, l2 = list
l = length(l1)
for j in l2
# @info "debug in" l1 l2 k j list
0 < j l || (return false)
i = l2[j]
0 < i l || (return false)
if l1[i] j || l2[j] i
return false
end
end
return true
end

xcsp_channel(; list...) = xcsp_channel(values(list)...)

function xcsp_channel(; list)
xcsp_channel(values(list))
end
function concept_channel(x, ::Val{2})
mid = length(x) ÷ 2
return xcsp_channel(list=(@view(x[1:mid]), @view(x[mid+1:end])))
Expand All @@ -27,6 +34,29 @@ concept_channel(x, ::Val) = xcsp_channel(list=x)

const description_channel = """Global constraint ensuring that all ...`"""

@usual concept_channel(x; dim=1) = concept_channel(x, Val(dim))
concept_channel(x, id) = count(!iszero, x) == 1 == x[id]

@usual function concept_channel(x; dim=1, id = nothing)
return isnothing(id) ? concept_channel(x, Val(dim)) : concept_channel(x, id)
end
@testitem "Channel" tags = [:usual, :constraints, :channel] begin
c = USUAL_CONSTRAINTS[:channel] |> concept
e = USUAL_CONSTRAINTS[:channel] |> error_f
vs = Constraints.concept_vs_error

@test c([2, 1, 4, 3])
@test c([1, 2, 3, 4])
@test !c([2, 3, 1, 4])
@test c([2, 1, 5, 3, 4, 2, 1, 4, 5, 3]; dim=2)
@test !c([2, 1, 4, 3, 5, 2, 1, 4, 5, 3]; dim=2)
@test c([false, false, true, false]; id=3)
@test !c([false, false, true, false]; id=1)

@usual concept_channel(x::AbstractVector{Bool}; id) = count(x) == 1 == x[id]
@test vs(c, e, [2, 1, 4, 3])
@test vs(c, e, [1, 2, 3, 4])
@test vs(c, e, [2, 3, 1, 4])
@test vs(c, e, [2, 1, 5, 3, 4, 2, 1, 4, 5, 3]; dim=2)
@test vs(c, e, [2, 1, 4, 3, 5, 2, 1, 4, 5, 3]; dim=2)
@test vs(c, e, [false, false, true, false]; id=3)
@test vs(c, e, [false, false, true, false]; id=1)
end
51 changes: 34 additions & 17 deletions src/constraints/circuit.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,24 +9,41 @@ end
const description_circuit = "Global constraint ensuring that the values of `x` form a circuit. If the indices of the variables are not `1:length(x)`, the indices can be indicated as the `param` collection"

# circuit (full circuit)
@usual function concept_circuit(x; op = , val = 1)
s = Set(1:length(x))
d = Dict{Int, Int}()
while !isempty(s)
u = pop!(s)
push!(d, u => 0)
l = 0
while !isempty(s)
v = pop!(s, x[u], 0)
if iszero(v)
break
end
l += 1
δ = get!(d, v, l) - l
!iszero(δ) && op(δ, val) && (return true)
u = v
@usual function concept_circuit(x; op = , val = length(x))
V = Set(1:length(x))
S = Vector{eltype(x)}()
D = Set{Int}()

while !isempty(V)
v = isempty(S) ? pop!(V) : pop!(V, x[last(S)])
push!(S, v)
notvalid = false
(cycle = x[v] S) || (notvalid = (v == x[v] || x[v] V || x[v] D))
if cycle
λ = length(S) - findfirst(u -> u == x[v], S) + 1
op(λ, val) && return true
end
if cycle || notvalid
union!(D, S)
empty!(S)
end
setdiff!(s, keys(d))
end
return false
end

## SECTION - Test Items
@testitem "Circuit" tags = [:usual, :constraints, :circuit] begin
c = USUAL_CONSTRAINTS[:circuit] |> concept
e = USUAL_CONSTRAINTS[:circuit] |> error_f
vs = Constraints.concept_vs_error

@test !c([1, 2, 3, 4])
@test c([2, 3, 4, 1])
@test c([2, 3, 1, 4]; op = ==, val = 3)
@test c([4, 3, 1, 3]; op = >, val = 0)

@test vs(c, e, [1, 2, 3, 4])
@test vs(c, e, [2, 3, 4, 1])
@test vs(c, e, [2, 3, 1, 4]; op = ==, val = 3)
@test vs(c, e, [4, 3, 1, 3]; op = >, val = 0)
end
27 changes: 27 additions & 0 deletions src/constraints/count.jl
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,30 @@ const description_at_most = """Constraint ensuring that ...`"""
const description_exactly = """Constraint ensuring that ...`"""

@usual concept_exactly(x; vals, val) = concept_count(x; vals, op = ==, val)

## SECTION - Test Items
@testitem "Count" tags = [:usual, :constraints, :count] begin
c = USUAL_CONSTRAINTS[:count] |> concept
e = USUAL_CONSTRAINTS[:count] |> error_f
vs = Constraints.concept_vs_error

c_at_least = USUAL_CONSTRAINTS[:at_least] |> concept
e_at_least = USUAL_CONSTRAINTS[:at_least] |> error_f

c_at_most = USUAL_CONSTRAINTS[:at_most] |> concept
e_at_most = USUAL_CONSTRAINTS[:at_most] |> error_f

c_exactly = USUAL_CONSTRAINTS[:exactly] |> concept
e_exactly = USUAL_CONSTRAINTS[:exactly] |> error_f

@test c([2, 1, 4, 3]; vals = [1, 2, 3, 4], op = , val = 2) && c_at_least([2, 1, 4, 3]; vals = [1, 2, 3, 4], val = 2)
@test c([1, 2, 3, 4]; vals = [1, 2], op = ==, val = 2) && c_exactly([1, 2, 3, 4]; vals = [1, 2], val = 2)
@test !c([2, 1, 4, 3]; vals = [1, 2], op = , val = 1) && !c_at_most([2, 1, 4, 3]; vals = [1, 2], val = 1)

@test vs(c, e, [2, 1, 4, 3]; vals = [1, 2, 3, 4], op = , val = 2)
@test vs(c_at_least, e_at_least, [2, 1, 4, 3]; vals = [1, 2, 3, 4], val = 2)
@test vs(c, e, [1, 2, 3, 4]; vals = [1, 2], op = ==, val = 2)
@test vs(c_exactly, e_exactly, [1, 2, 3, 4]; vals = [1, 2], val = 2)
@test vs(c, e, [2, 1, 4, 3]; vals = [1, 2], op = , val = 2)
@test vs(c_at_most, e_at_most, [2, 1, 4, 3]; vals = [1, 2], val = 1)
end
34 changes: 27 additions & 7 deletions src/constraints/cumulative.jl
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
function xcsp_cumulative(; origins, lengths, heights, condition)
acc = Dict{eltype(origins),Int}()
for t in sort(collect(zip(origins, lengths, heights)))
foreach(t -> t t[1] && delete!(acc, t), keys(acc))
incsert!(acc, t[1] + t[2], t[3])
condition[1](sum(values(acc)), condition[2]) || return false
tasks = Vector{Tuple{eltype(origins),eltype(origins)}}()
for t in Iterators.zip(origins, lengths, heights)
push!(tasks, (t[1], t[3]))
push!(tasks,(t[1]+t[2], -t[3]))
end
η = zero(eltype(origins))
for t in sort!(tasks)
η += t[2]
condition[1](η, condition[2]) || return false
end
return true
end

function concept_cumulative(x, pair_vars, op, val)
return xcsp_cumulative(
origins=x, lengths=pair_vars[1], heights=pair_vars[2], condition=(op, val)
origins=x, lengths=pair_vars[1,:], heights=pair_vars[2, :], condition=(op, val)
)
end

Expand All @@ -20,6 +24,22 @@ end

const description_cumulative = """Global constraint ensuring that all ...`"""

@usual function concept_cumulative(x; pair_vars=ones(eltype(x), length(x)), op=, val)
@usual function concept_cumulative(x; pair_vars=ones(eltype(x), (2, length(x))), op=, val)
return concept_cumulative(x, pair_vars, op, val)
end

@testitem "Cumulative" tags = [:usual, :constraints, :cumulative] begin
c = USUAL_CONSTRAINTS[:cumulative] |> concept
e = USUAL_CONSTRAINTS[:cumulative] |> error_f
vs = Constraints.concept_vs_error

@test c([1, 2, 3, 4, 5]; val=1)
@test !c([1, 2, 2, 4, 5]; val=1)
@test c([1, 2, 3, 4, 5]; pair_vars=[3 2 5 4 2; 1 2 1 1 3], op= , val=5)
@test !c([1, 2, 3, 4, 5]; pair_vars=[3 2 5 4 2; 1 2 1 1 3], op= <, val=5)

@test vs(c, e, [1, 2, 3, 4, 5]; val=1)
@test vs(c, e, [1, 2, 2, 4, 5]; val=1)
@test vs(c, e, [1, 2, 3, 4, 5]; pair_vars=[3 2 5 4 2; 1 2 1 1 3], op= , val=5)
@test vs(c, e, [1, 2, 3, 4, 5]; pair_vars=[3 2 5 4 2; 1 2 1 1 3], op= <, val=5)
end
Loading

0 comments on commit 3c48c33

Please sign in to comment.