Skip to content

Commit 33266b9

Browse files
authored
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
1 parent d48492a commit 33266b9

File tree

6 files changed

+122
-29
lines changed

6 files changed

+122
-29
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:

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
@@ -19,48 +19,137 @@ Settings for the exploration of a search space composed by a collection of domai
1919
function ExploreSettings(
2020
domains;
2121
complete_search_limit = 10^6,
22-
max_samplings = sum(domain_size, domains),
22+
max_samplings = sum(domain_size, domains; init = 0),
2323
search = :flexible,
2424
solutions_limit = floor(Int, sqrt(max_samplings)),
2525
)
2626
return ExploreSettings(complete_search_limit, max_samplings, search, solutions_limit)
2727
end
2828

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

38-
sl = s.solutions_limit
123+
solutions = explorer.state.solutions
124+
non_sltns = explorer.state.non_solutions
125+
domains = explorer.domains |> values
39126

40-
for _ = 1:s.max_samplings
127+
for _ = 1:ms
41128
length(solutions) sl && length(non_sltns) sl && break
42129
config = map(rand, domains)
43-
c = f(config) ? solutions : non_sltns
44-
length(c) < sl && push!(c, config)
130+
update_exploration!(explorer, f, config)
45131
end
46-
return solutions, non_sltns
132+
return nothing
47133
end
48134

49-
function _explore(domains, f, ::ExploreSettings, ::Val{:complete})
50-
solutions = Set{Vector{Int}}()
51-
non_sltns = Set{Vector{Int}}()
52-
53-
configurations = Base.Iterators.product(map(d -> get_domain(d), domains)...)
54-
foreach(
55-
c -> (cv = collect(c); push!(f(cv) ? solutions : non_sltns, cv)),
56-
configurations,
57-
)
58-
return solutions, non_sltns
135+
function _explore!(explorer, f, ::Val{:complete})
136+
C = Base.Iterators.product(map(d -> get_domain(d), explorer.domains |> values)...)
137+
foreach(c -> update_exploration!(explorer, f, c, :complete), C)
138+
return nothing
59139
end
60140

61-
function _explore(domains, f, s, ::Val{:flexible})
62-
search = s.max_samplings < s.complete_search_limit ? :complete : :partial
63-
return _explore(domains, f, s, Val(search))
141+
function explore!(explorer::Explorer)
142+
c =
143+
x -> all([
144+
f(isempty(vars) ? x : @view x[vars]) for
145+
(f, vars) in explorer.concepts |> values
146+
])
147+
s = explorer.settings
148+
search = s.search
149+
if search == :flexible
150+
search = s.max_samplings < s.complete_search_limit ? :complete : :partial
151+
end
152+
return _explore!(explorer, c, Val(search))
64153
end
65154

66155
"""
@@ -78,7 +167,9 @@ Beware that if the density of the solutions in the search space is low, `solutio
78167
"""
79168
function explore(domains, concept; settings = ExploreSettings(domains), parameters...)
80169
f = x -> concept(x; parameters...)
81-
return _explore(domains, f, settings, Val(settings.search))
170+
explorer = Explorer([(f, Vector{Int}())], domains; settings)
171+
explore!(explorer)
172+
return explorer.state.solutions, explorer.state.non_solutions
82173
end
83174

84175
## SECTION - Test Items
@@ -87,4 +178,6 @@ end
87178
X, X̅ = explore(domains, allunique)
88179
@test length(X) == factorial(4)
89180
@test length(X̅) == 4^4 - factorial(4)
181+
182+
explorer = ConstraintDomains.Explorer()
90183
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)