diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e978604..1dcf282 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -34,7 +34,7 @@ jobs: - os: windows-latest arch: x86 steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: julia-actions/setup-julia@v1 with: version: ${{ matrix.version }} @@ -59,7 +59,7 @@ jobs: name: Documentation runs-on: ubuntu-latest steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v3 - uses: julia-actions/setup-julia@v1 with: version: "1" diff --git a/Project.toml b/Project.toml index fc831cc..53906f2 100644 --- a/Project.toml +++ b/Project.toml @@ -1,19 +1,23 @@ name = "ConstraintDomains" uuid = "5800fd60-8556-4464-8d61-84ebf7a0bedb" authors = ["Jean-François Baffier"] -version = "0.3.5" +version = "0.3.6" [deps] ConstraintCommons = "e37357d9-0691-492f-a822-e5ea6a920954" Dictionaries = "85a47980-9c8c-11e8-2b9f-f7ca1fa99fb4" Intervals = "d8418881-c3e1-53bb-8760-2df7ec849ed5" PatternFolds = "c18a7f1d-76ad-4ce4-950d-5419b888513b" +TestItemRunner = "f8b46487-2199-4994-9208-9a1283c18c0a" +TestItems = "1c621080-faea-4a02-84b6-bbd5e436b8fe" [compat] ConstraintCommons = "0.1" Dictionaries = "0.3" Intervals = "1" PatternFolds = "0.2" +TestItemRunner = "0.2" +TestItems = "0.1" julia = "1.6" [extras] diff --git a/src/ConstraintDomains.jl b/src/ConstraintDomains.jl index 2051fc2..a230b68 100644 --- a/src/ConstraintDomains.jl +++ b/src/ConstraintDomains.jl @@ -4,6 +4,8 @@ module ConstraintDomains using ConstraintCommons using Dictionaries using PatternFolds +using TestItemRunner +using TestItems # Exports export AbstractDomain diff --git a/src/common.jl b/src/common.jl index f9c33a4..74a817a 100644 --- a/src/common.jl +++ b/src/common.jl @@ -55,7 +55,8 @@ get_domain(d::D) where {D<:AbstractDomain} = d.domain """ to_domains(args...) -TODO - doc + +Convert various arguments into valid domains format. """ to_domains(domain_sizes::Vector{Int}) = map(ds -> domain(0:ds), domain_sizes) @@ -66,6 +67,11 @@ end to_domains(X, ::Nothing) = to_domains(X) +""" + Base.rand(d::Union{Vector{D},Set{D}, D}) where {D<:AbstractDomain} + +Extends `Base.rand` to (a collection of) domains. +""" Base.rand(d::AbstractDomain) = rand(get_domain(d)) Base.rand(d::Union{Vector{D},Set{D}}) where {D<:AbstractDomain} = map(rand, d) @@ -73,3 +79,11 @@ Base.rand(d::Union{Vector{D},Set{D}}) where {D<:AbstractDomain} = map(rand, d) function Base.rand(d::V) where {D<:AbstractDomain,U<:Union{Vector{D},Set{D}},V<:AbstractVector{U}} return map(rand, d) end + +## SECTION - Test Items +@testitem "EmptyDomain" tags = [:domains, :empty] begin + ed = domain() + @test domain_size(ed) == 0 == length(ed) + @test isempty(ed) + @test π ∉ ed +end diff --git a/src/continuous.jl b/src/continuous.jl index dbd7407..6d7f5d2 100644 --- a/src/continuous.jl +++ b/src/continuous.jl @@ -72,9 +72,15 @@ function domain_size(itv::Intervals) return maximum(last, get_domain(itv)) - minimum(first, get_domain(itv)) end +# TODO - implement function merge_domains(d1::D, d2::D) where {D<:ContinuousDomain} end +""" + intersect_domains(d₁, d₂) + +Compute the intersections of two domains. +""" function intersect_domains(i₁::I1, i₂::I2) where {I1<:Interval,I2<:Interval} if i₁.first > i₂.first return intersect_domains(i₂, i₁) @@ -102,4 +108,36 @@ function intersect_domains(d₁::D, d₂::D) where {T<:Real,D<:ContinuousDomain{ return Intervals(new_itvls) end + +""" + Base.size(i::I) where {I <: Interval} + +Defines the size of an interval as its `span`. +""" size(i::I) where {I <: Interval} = span(i) + +## SECTION - Test Items +@testitem "ContinuousDomain" tags = [:domains, :continuous] default_imports=false begin + using ConstraintDomains, Intervals, Test + # import Base:size + + d1 = domain(1.0..3.15) + d2 = domain(Interval{Open, Open}(-42.42, 5.0)) + + domains = [ + d1, + d2, + ] + + for d in domains + for x in [1, 2.3, π] + @test x ∈ d + end + for x in [5.1, π^π, Inf] + @test x ∉ d + end + @test rand(d) ∈ d + @test rand(d, 1) ∈ d + @test domain_size(d) > 0.0 + end +end diff --git a/src/discrete.jl b/src/discrete.jl index 87f9253..4770e73 100644 --- a/src/discrete.jl +++ b/src/discrete.jl @@ -73,6 +73,12 @@ Delete `value` from the list of points in `d`. """ Base.delete!(d::SetDomain, value) = pop!(get_domain(d), value) + +""" + merge_domains(d₁::AbstractDomain, d₂::AbstractDomain) + +Merge two domains of same nature (discrete/contiuous). +""" function merge_domains(rd₁::RangeDomain, rd₂::RangeDomain) d₁ = get_domain(rd₁) d₂ = get_domain(rd₂) @@ -99,3 +105,52 @@ function to_domains(X, d::D) where {D <: DiscreteDomain} n::Int = length(first(X)) / domain_size(d) return fill(d, n) end + +## SECTION - Test Items +@testitem "DiscreteDomain" tags = [:domain, :discrete, :set] begin + d1 = domain([4,3,2,1]) + d2 = domain(1) + foreach(i -> add!(d2, i), 2:4) + domains = [ + d1, + d2, + ] + + for d in domains + # constructors and ∈ + for x in [1,2,3,4] + @test x ∈ d + end + # length + @test length(d) == 4 + # draw and ∈ + @test rand(d) ∈ d + # add! + add!(d, 5) + @test 5 ∈ d + # delete! + delete!(d, 5) + @test 5 ∉ d + @test domain_size(d) == 3 + end +end + +@testitem "RangeDomain" tags = [:domain, :discrete, :range] begin + d1 = domain(1:5) + d2 = domain(1:.5:5) + domains = [ + d1, + d2, + ] + + for d in domains + for x in [1, 2, 3, 4, 5] + @test x ∈ d + end + for x in [42] + @test x ∉ d + end + @test rand(d) ∈ d + end + +end diff --git a/src/explore.jl b/src/explore.jl index cd60ce7..3d86781 100644 --- a/src/explore.jl +++ b/src/explore.jl @@ -5,6 +5,17 @@ struct ExploreSettings solutions_limit::Int end +""" + ExploreSettings( + domains; + complete_search_limit = 10^6, + max_samplings = sum(domain_size, domains), + search = :flexible, + solutions_limit = floor(Int, sqrt(max_samplings)), + ) + +Settings for the exploration of a search space composed by a collection of domains. +""" function ExploreSettings( domains; complete_search_limit = 10^6, @@ -15,6 +26,11 @@ function ExploreSettings( return ExploreSettings(complete_search_limit, max_samplings, search, solutions_limit) end +""" + _explore(args...) + +Internals of the `explore` function. Behavior is automatically adjusted on the kind of exploration: `:flexible`, `:complete`, `:partial`. +""" function _explore(domains, f, s, ::Val{:partial}) solutions = Set{Vector{Int}}() non_sltns = Set{Vector{Int}}() @@ -69,3 +85,11 @@ function explore( f = x -> concept(x; parameters...) return _explore(domains, f, settings, Val(settings.search)) end + +## SECTION - Test Items +@testitem "Exploration" tags = [:exploration] begin + domains = [domain([1,2,3,4]) for i in 1:4] + X, X̅ = explore(domains, allunique) + @test length(X) == factorial(4) + @test length(X̅) == 4^4 - factorial(4) +end diff --git a/src/general.jl b/src/general.jl index 60798d6..40c7b53 100644 --- a/src/general.jl +++ b/src/general.jl @@ -1,5 +1,16 @@ +""" + Base.eltype(::AbstractDomain) + +Extend `eltype` for domains. +""" Base.eltype(::D) where {T, D <: Union{DiscreteDomain{T}, ContinuousDomain{T}}} = T + +""" + Base.convert(::Type{Union{Intervals, RangeDomain}}, d::Union{Intervals, RangeDomain}) + +Extends `Base.convert` for domains. +""" function Base.convert(::Type{Intervals}, d::RangeDomain{T}) where {T <: Real} a, b = extrema(get_domain(d)) return domain(Float64(a)..Float64(b)) diff --git a/test/runtests.jl b/test/runtests.jl index 03ef0cf..b9e874d 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -1,83 +1,3 @@ -using ConstraintCommons -using ConstraintDomains -using Dictionaries -using Intervals -using Test +using TestItemRunner -@testset "ConstraintDomains.jl" begin - @testset "EmptyDomain" begin - ed = domain() - @test domain_size(ed) == 0 == length(ed) - @test isempty(ed) - @test π ∉ ed - end - - @testset "DiscreteDomain" begin - d1 = domain([4,3,2,1]) - d2 = domain(1) - foreach(i -> add!(d2, i), 2:4) - domains = Dictionary(1:2, [d1, d2]) - - for d in domains - # constructors and ∈ - for x in [1,2,3,4] - @test x ∈ d - end - # length - @test length(d) == 4 - # draw and ∈ - @test rand(d) ∈ d - # add! - add!(d, 5) - @test 5 ∈ d - # delete! - delete!(d, 5) - @test 5 ∉ d - @test domain_size(d) == 3 - end - end - - @testset "ContinuousDomain" begin - d1 = domain(1.0..3.15) - d2 = domain(Interval{Open, Open}(-42.42, 5.0)) - # d3 = domain([d1, d2]) - domains = Dictionary(1:2, [d1, d2]) - - for d in domains - for x in [1, 2.3, π] - @test x ∈ d - end - for x in [5.1, π^π, Inf] - @test x ∉ d - end - @test rand(d) ∈ d - @test rand(d, 1) ∈ d - @test domain_size(d) > 0.0 - end - - end - - @testset "RangeDomain" begin - d1 = domain(1:5) - d2 = domain(1:.5:5) - domains = Dictionary(1:2, [d1, d2]) - - for d in domains - for x in [1, 2, 3, 4, 5] - @test x ∈ d - end - for x in [42] - @test x ∉ d - end - @test rand(d) ∈ d - end - - end - - @testset "Exploration" begin - domains = [domain([1,2,3,4]) for i in 1:4] - X, X̅ = explore(domains, allunique) - @test length(X) == factorial(4) - @test length(X̅) == 4^4 - factorial(4) - end -end +@run_package_tests