Skip to content

Commit cf4fe62

Browse files
authored
Tag new release for ConstraintExplorer.jl (#61)
* Fixes for tests * Fix format * Update Project.toml * Make an Explorer to prepare a JuMP interface and broader search space exploration (#60) * New Explorer structure * Makes the explorer adaptive * Updates for ConstraintExplorer * Fix in explore * Format * Update ci.yml * Update Project.toml
1 parent 7e8f095 commit cf4fe62

File tree

7 files changed

+124
-31
lines changed

7 files changed

+124
-31
lines changed

.github/workflows/ci.yml

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,6 @@ jobs:
5050
fail-fast: false
5151
matrix:
5252
version:
53-
- "1.8"
5453
- "1" # automatically expands to the latest stable 1.x release of Julia
5554
- "pre"
5655
os:

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "ConstraintDomains"
22
uuid = "5800fd60-8556-4464-8d61-84ebf7a0bedb"
33
authors = ["Jean-François Baffier"]
4-
version = "0.3.13"
4+
version = "0.3.14"
55

66
[deps]
77
ConstraintCommons = "e37357d9-0691-492f-a822-e5ea6a920954"
@@ -16,7 +16,7 @@ Intervals = "1"
1616
PatternFolds = "0.2"
1717
StatsBase = "0.34"
1818
TestItems = "0.1, 1"
19-
julia = "1.8"
19+
julia = "1.10"
2020

2121
[extras]
2222
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"

src/ConstraintDomains.jl

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ module ConstraintDomains
22

33
# SECTION - Imports
44
import ConstraintCommons: ConstraintCommons, δ_extrema
5-
# import Dictionaries
65
import PatternFolds: PatternFolds, Interval, Closed
76
import StatsBase: sample
87
import TestItems: @testitem
@@ -11,15 +10,15 @@ import TestItems: @testitem
1110
export AbstractDomain
1211
export ContinuousDomain
1312
export DiscreteDomain
14-
export ExploreSettings
13+
export Explorer, ExploreSettings
1514
export RangeDomain
1615
export SetDomain
1716

1817
export add!
1918
export delete!
2019
export domain
2120
export domain_size
22-
export explore
21+
export explore, explore!
2322
export generate_parameters
2423
export get_domain
2524
export intersect_domains

src/common.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ function Base.string(D::Vector{<:AbstractDomain})
9898
end
9999
Base.string(d::AbstractDomain) = replace(string(d.domain), " " => "")
100100

101+
merge_domains(::EmptyDomain, d::D) where {D<:AbstractDomain} = d
102+
merge_domains(d::D, ::EmptyDomain) where {D<:AbstractDomain} = d
103+
101104
## SECTION - Test Items
102105
@testitem "EmptyDomain" tags = [:domains, :empty] begin
103106
ed = domain()

src/continuous.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,6 @@ function intersect_domains(
114114
return Intervals(new_itvls)
115115
end
116116

117-
118117
"""
119118
Base.size(i::I) where {I <: Interval}
120119

src/explore.jl

Lines changed: 116 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -27,48 +27,137 @@ Create an `ExploreSettings` object to configure the exploration of a search spac
2727
function ExploreSettings(
2828
domains;
2929
complete_search_limit = 10^6,
30-
max_samplings = sum(domain_size, domains),
30+
max_samplings = sum(domain_size, domains; init = 0),
3131
search = :flexible,
3232
solutions_limit = floor(Int, sqrt(max_samplings)),
3333
)
3434
return ExploreSettings(complete_search_limit, max_samplings, search, solutions_limit)
3535
end
3636

37+
struct ExplorerState{T}
38+
best::Vector{T}
39+
solutions::Set{Vector{T}}
40+
non_solutions::Set{Vector{T}}
41+
42+
ExplorerState{T}() where {T} = new{T}([], Set{Vector{T}}(), Set{Vector{T}}())
43+
end
44+
45+
ExplorerState(domains) = ExplorerState{Union{map(eltype, domains)...}}()
46+
47+
mutable struct Explorer{F1<:Function,D<:AbstractDomain,F2<:Union{Function,Nothing},T}
48+
concepts::Dict{Int,Tuple{F1,Vector{Int}}}
49+
domains::Dict{Int,D}
50+
objective::F2
51+
settings::ExploreSettings
52+
state::ExplorerState{T}
53+
54+
function Explorer(
55+
concepts,
56+
domains,
57+
objective = nothing;
58+
settings = ExploreSettings(domains),
59+
)
60+
F1 = isempty(concepts) ? Function : Union{map(c -> typeof(c[1]), concepts)...}
61+
D = isempty(domains) ? AbstractDomain : Union{map(typeof, domains)...}
62+
F2 = typeof(objective)
63+
T = isempty(domains) ? Real : Union{map(eltype, domains)...}
64+
d_c = Dict(enumerate(concepts))
65+
d_d = Dict(enumerate(domains))
66+
return new{F1,D,F2,T}(d_c, d_d, objective, settings, ExplorerState{T}())
67+
end
68+
end
69+
70+
function Explorer()
71+
concepts = Vector{Tuple{Function,Vector{Int}}}()
72+
domains = Vector{AbstractDomain}()
73+
objective = nothing
74+
settings = ExploreSettings(domains)
75+
return Explorer(concepts, domains, objective; settings)
76+
end
77+
78+
function Base.push!(explorer::Explorer, concept::Tuple{Function,Vector{Int}})
79+
max_key = maximum(keys(explorer.concepts); init = 0)
80+
explorer.concepts[max_key+1] = concept
81+
return max_key + 1
82+
end
83+
84+
function delete_concept!(explorer::Explorer, key::Int)
85+
delete!(explorer.concepts, key)
86+
return nothing
87+
end
88+
89+
function Base.push!(explorer::Explorer, domain::AbstractDomain)
90+
max_key = maximum(keys(explorer.domains); init = 0)
91+
explorer.domains[max_key+1] = domain
92+
return max_key + 1
93+
end
94+
95+
function delete_domain!(explorer::Explorer, key::Int)
96+
delete!(explorer.domains, key)
97+
return nothing
98+
end
99+
100+
set!(explorer::Explorer, objective::Function) = explorer.objective = objective
101+
102+
function update_exploration!(explorer, f, c, search = explorer.settings.search)
103+
solutions = explorer.state.solutions
104+
non_sltns = explorer.state.non_solutions
105+
obj = explorer.objective
106+
sl = search == :complete ? Inf : explorer.settings.solutions_limit
107+
108+
cv = collect(c)
109+
if f(cv)
110+
if length(solutions) < sl
111+
push!(solutions, cv)
112+
obj !== nothing && (explorer.state.best = argmin(obj, solutions))
113+
end
114+
else
115+
if length(non_sltns) < sl
116+
push!(non_sltns, cv)
117+
end
118+
end
119+
return nothing
120+
end
121+
37122
"""
38123
_explore(args...)
39124
40125
Internals of the `explore` function. Behavior is automatically adjusted on the kind of exploration: `:flexible`, `:complete`, `:partial`.
41126
"""
42-
function _explore(domains, f, s, ::Val{:partial})
43-
solutions = Set{Vector{Int}}()
44-
non_sltns = Set{Vector{Int}}()
127+
function _explore!(explorer, f, ::Val{:partial})
128+
sl = explorer.settings.solutions_limit
129+
ms = explorer.settings.max_samplings
45130

46-
sl = s.solutions_limit
131+
solutions = explorer.state.solutions
132+
non_sltns = explorer.state.non_solutions
133+
domains = explorer.domains |> values
47134

48-
for _ = 1:s.max_samplings
135+
for _ = 1:ms
49136
length(solutions) sl && length(non_sltns) sl && break
50137
config = map(rand, domains)
51-
c = f(config) ? solutions : non_sltns
52-
length(c) < sl && push!(c, config)
138+
update_exploration!(explorer, f, config)
53139
end
54-
return solutions, non_sltns
140+
return nothing
55141
end
56142

57-
function _explore(domains, f, ::ExploreSettings, ::Val{:complete})
58-
solutions = Set{Vector{Int}}()
59-
non_sltns = Set{Vector{Int}}()
60-
61-
configurations = Base.Iterators.product(map(d -> get_domain(d), domains)...)
62-
foreach(
63-
c -> (cv = collect(c); push!(f(cv) ? solutions : non_sltns, cv)),
64-
configurations,
65-
)
66-
return solutions, non_sltns
143+
function _explore!(explorer, f, ::Val{:complete})
144+
C = Base.Iterators.product(map(d -> get_domain(d), explorer.domains |> values)...)
145+
foreach(c -> update_exploration!(explorer, f, c, :complete), C)
146+
return nothing
67147
end
68148

69-
function _explore(domains, f, s, ::Val{:flexible})
70-
search = s.max_samplings < s.complete_search_limit ? :complete : :partial
71-
return _explore(domains, f, s, Val(search))
149+
function explore!(explorer::Explorer)
150+
c =
151+
x -> all([
152+
f(isempty(vars) ? x : @view x[vars]) for
153+
(f, vars) in explorer.concepts |> values
154+
])
155+
s = explorer.settings
156+
search = s.search
157+
if search == :flexible
158+
search = s.max_samplings < s.complete_search_limit ? :complete : :partial
159+
end
160+
return _explore!(explorer, c, Val(search))
72161
end
73162

74163
"""
@@ -87,7 +176,9 @@ Search (a part of) a search space and return a pair of vectors of configurations
87176
"""
88177
function explore(domains, concept; settings = ExploreSettings(domains), parameters...)
89178
f = x -> concept(x; parameters...)
90-
return _explore(domains, f, settings, Val(settings.search))
179+
explorer = Explorer([(f, Vector{Int}())], domains; settings)
180+
explore!(explorer)
181+
return explorer.state.solutions, explorer.state.non_solutions
91182
end
92183

93184
## SECTION - Test Items
@@ -96,4 +187,6 @@ end
96187
X, X̅ = explore(domains, allunique)
97188
@test length(X) == factorial(4)
98189
@test length(X̅) == 4^4 - factorial(4)
190+
191+
explorer = ConstraintDomains.Explorer()
99192
end

src/general.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ function Base.convert(::Type{Intervals}, d::RangeDomain{T}) where {T<:Real}
1616
return domain(Interval{T,Closed,Closed}(a, b))
1717
end
1818

19-
function Base.convert(::Type{RangeDomain}, d::Intervals{T}) where {T<:Real}
19+
function Base.convert(::Type{RangeDomain}, d::Intervals)
2020
i = get_domain(d)[1]
2121
a = Int(i.first)
2222
b = Int(i.last)

0 commit comments

Comments
 (0)