Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update with HerbSpecification #59

Closed
wants to merge 1 commit into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 1 addition & 2 deletions Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,9 @@ version = "0.1.1"
DataStructures = "864edb3b-99cc-5e75-8d2d-829cb0a9cfe8"
HerbConstraints = "1fa96474-3206-4513-b4fa-23913f296dfc"
HerbCore = "2b23ba43-8213-43cb-b5ea-38c12b45bd45"
HerbData = "495a3ad3-8034-41b3-a087-aacf2fd71098"
HerbGrammar = "4ef9e186-2fe5-4b24-8de7-9f7291f24af7"
HerbInterpret = "5bbddadd-02c5-4713-84b8-97364418cca7"
HerbSpecification = "6d54aada-062f-46d8-85cf-a1ceaf058a06"
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
Expand All @@ -18,7 +18,6 @@ StatsBase = "2913bbd2-ae8a-5f71-8c99-4fb6c76f3a91"
DataStructures = "0.17,0.18"
HerbConstraints = "0.1.0"
HerbCore = "0.1.1"
HerbData = "0.1.1"
HerbGrammar = "0.1.0"
HerbInterpret = "0.1.0"
StatsBase = "0.34"
Expand Down
2 changes: 1 addition & 1 deletion src/HerbSearch.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ using DataStructures
using HerbCore
using HerbGrammar
using HerbConstraints
using HerbData
using HerbSpecification
using HerbInterpret

include("sampling_grammar.jl")
Expand Down
8 changes: 4 additions & 4 deletions src/genetic_enumerators.jl
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
"""
get_genetic_enumerator(examples; fitness_function = HerbSearch.default_fitness, initial_population_size = 10, maximum_initial_population_depth = 3, mutation_probability = 0.1, cross_over = HerbSearch.crossover_swap_children_2, select_parents = HerbSearch.select_fitness_proportional_parents, evaluation_function::Function=HerbInterpret.test_with_input)
get_genetic_enumerator(spec; fitness_function = HerbSearch.default_fitness, initial_population_size = 10, maximum_initial_population_depth = 3, mutation_probability = 0.1, cross_over = HerbSearch.crossover_swap_children_2, select_parents = HerbSearch.select_fitness_proportional_parents, evaluation_function::Function=HerbInterpret.execute_on_input)

Returns a [`GeneticSearchIterator`](@ref) given a grammar. The iterator is fitted against the examples provided evaluated using the fitness function. All other arguments are hyperparameters for the genetic search procedure.
"""
function get_genetic_enumerator(examples;
function get_genetic_enumerator(spec;
fitness_function = HerbSearch.default_fitness,
initial_population_size = 10,
maximum_initial_population_depth = 3,
mutation_probability = 0.1,
cross_over = HerbSearch.crossover_swap_children_2,
select_parents = HerbSearch.select_fitness_proportional_parents,
evaluation_function::Function=HerbInterpret.test_with_input)
evaluation_function::Function=execute_on_input)
return (grammar, max_depth, max_size, start_symbol) -> begin
return GeneticSearchIterator(
grammar = grammar,
examples = examples,
spec = spec,
fitness = fitness_function,
cross_over = cross_over,
mutation! = mutate_random!,
Expand Down
8 changes: 4 additions & 4 deletions src/genetic_search_iterator.jl
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Defines an [`ExpressionIterator`](@ref) using genetic search.
Consists of:

- `grammar::ContextSensitiveGrammar`: the grammar to search over
- `examples::Vector{<:Example}`: a collection of examples defining the specification
- `spec::AbstractSpecification`: a collection of examples defining the specification

- `fitness::FitnessFunction`: assigns a numerical value (fitness score) to each individual based on how closely it meets the desired objective
- `cross_over::CrossOverFunction`: combines the program from two parent individuals to create one or more offspring individuals
Expand All @@ -30,7 +30,7 @@ end
"""
Base.@kwdef struct GeneticSearchIterator{FitnessFunction,CrossOverFunction,MutationFunction,SelectParentsFunction,EvaluationFunction} <: ExpressionIterator
grammar::ContextSensitiveGrammar
examples::Vector{<:Example}
spec::AbstractSpecification

fitness::FitnessFunction
cross_over::CrossOverFunction
Expand Down Expand Up @@ -96,7 +96,7 @@ function get_best_program(population::Array{RuleNode}, iter:: GeneticSearchItera
best_fitness = 0
for index ∈ eachindex(population)
chromosome = population[index]
fitness_value = iter.fitness(chromosome, HerbInterpret.evaluate_program(chromosome, iter.examples, iter.grammar, iter.evaluation_function))
fitness_value = iter.fitness(chromosome, HerbInterpret.evaluate_program(chromosome, iter.spec, iter.grammar, iter.evaluation_function))
if isnothing(best_program)
best_fitness = fitness_value
best_program = chromosome
Expand Down Expand Up @@ -140,7 +140,7 @@ function Base.iterate(iter::GeneticSearchIterator, current_state::GeneticIterato
current_population = current_state.population

# Calculate fitness
fitness_array = [iter.fitness(chromosome, HerbInterpret.evaluate_program(chromosome, iter.examples, iter.grammar, iter.evaluation_function)) for chromosome in current_population]
fitness_array = [iter.fitness(chromosome, HerbInterpret.evaluate_program(chromosome, iter.spec, iter.grammar, iter.evaluation_function)) for chromosome in current_population]

new_population = Vector{RuleNode}(undef,iter.population_size)

Expand Down
28 changes: 14 additions & 14 deletions src/search_procedure.jl
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ Base.showerror(io::IO, e::EvaluationError) = print(io, "An exception was thrown


"""
search_rulenode(g::Grammar, problem::Problem, start::Symbol; evaluator::Function=test_with_input, enumerator::Function=get_bfs_enumerator, max_depth::Union{Int, Nothing}=nothing, max_size::Union{Int, Nothing}=nothing, max_time::Union{Int, Nothing}=nothing, max_enumerations::Union{Int, Nothing}=nothing, allow_evaluation_errors::Bool=false, mod::Module=Main)::Union{Tuple{RuleNode, Any}, Nothing}
search_rulenode(g::Grammar, problem::Problem, start::Symbol; evaluator::Function=execute_on_input, enumerator::Function=get_bfs_enumerator, max_depth::Union{Int, Nothing}=nothing, max_size::Union{Int, Nothing}=nothing, max_time::Union{Int, Nothing}=nothing, max_enumerations::Union{Int, Nothing}=nothing, allow_evaluation_errors::Bool=false, mod::Module=Main)::Union{Tuple{RuleNode, Any}, Nothing}

Searches the grammar for the program that satisfies the maximum number of examples in the problem.

- g - The grammar that defines the search space
- problem - The problem definition with IO examples
- problem - The problem definition with an AbstractSpecification
- start - The start symbol in the grammar
- evaluator - The evaluation function. Takes a SymbolTable, expression and a dictionary with
input variable assignments and returns the output of the expression.
Expand All @@ -32,7 +32,7 @@ function search_rulenode(
g::Grammar,
problem::Problem,
start::Symbol;
evaluator::Function=test_with_input,
evaluator::Function=execute_on_input,
enumerator::Function=get_bfs_enumerator,
max_depth::Union{Int, Nothing}=nothing,
max_size::Union{Int, Nothing}=nothing,
Expand All @@ -58,12 +58,12 @@ function search_rulenode(
# Create expression from rulenode representation of AST
expr = rulenode2expr(h, g)

# Evaluate the examples.
# Evaluate the specifcation.
# # `all` shortcircuits, so not every example will be evaluated in every iteration.
# if all(example.out == evaluator(symboltable, expr, example.in) for example ∈ problem.examples)
# if all(example.out == evaluator(symboltable, expr, example.in) for example ∈ problem.spec)
# return (h, expr)
falsified = false
for example ∈ problem.examples
for example ∈ problem.spec
# Evaluate the example, making sure that any exceptions are caught
try
output = evaluator(symboltable, expr, example.in)
Expand Down Expand Up @@ -93,12 +93,12 @@ end


"""
search(g::Grammar, problem::Problem, start::Symbol; evaluator::Function=test_with_input, enumerator::Function=get_bfs_enumerator, max_depth::Union{Int, Nothing}=nothing, max_size::Union{Int, Nothing}=nothing, max_time::Union{Int, Nothing}=nothing, max_enumerations::Union{Int, Nothing}=nothing, allow_evaluation_errors::Bool=false, mod::Module=Main)::Union{Any, Nothing}
search(g::Grammar, problem::Problem, start::Symbol; evaluator::Function=execute_on_input, enumerator::Function=get_bfs_enumerator, max_depth::Union{Int, Nothing}=nothing, max_size::Union{Int, Nothing}=nothing, max_time::Union{Int, Nothing}=nothing, max_enumerations::Union{Int, Nothing}=nothing, allow_evaluation_errors::Bool=false, mod::Module=Main)::Union{Any, Nothing}

Searches for a program by calling [`search_rulenode`](@ref) starting from [`Symbol`](@ref) `start` guided by `enumerator` and [`Grammar`](@ref) trying to satisfy the higher-order constraints in form of input/output examples defined in the [`Problem`](@ref).

- g - The grammar that defines the search space
- problem - The problem definition with IO examples
- problem - The problem definition with an AbstractSpecification
- start - The start symbol in the grammar
- evaluator - The evaluation function. Takes a SymbolTable, expression and a dictionary with
input variable assignments and returns the output of the expression.
Expand All @@ -117,7 +117,7 @@ function search(
g::Grammar,
problem::Problem,
start::Symbol;
evaluator::Function=test_with_input,
evaluator::Function=execute_on_input,
enumerator::Function=get_bfs_enumerator,
max_depth::Union{Int, Nothing}=nothing,
max_size::Union{Int, Nothing}=nothing,
Expand Down Expand Up @@ -172,14 +172,14 @@ mse_error_function(old_error, output, expected_output) = old_error + (output - e


"""
search_best(g::Grammar, problem::Problem, start::Symbol; evaluator::Function=test_with_input, enumerator::Function=get_bfs_enumerator, error_function::Function=default_error_function, max_depth::Union{Int, Nothing}=nothing, max_size::Union{Int, Nothing}=nothing, max_time::Union{Int, Nothing}=nothing, max_enumerations::Union{Int, Nothing}=nothing, allow_evaluation_errors::Bool=false, mod::Module=Main)::Tuple{Any, Real}
search_best(g::Grammar, problem::Problem, start::Symbol; evaluator::Function=execute_on_input, enumerator::Function=get_bfs_enumerator, error_function::Function=default_error_function, max_depth::Union{Int, Nothing}=nothing, max_size::Union{Int, Nothing}=nothing, max_time::Union{Int, Nothing}=nothing, max_enumerations::Union{Int, Nothing}=nothing, allow_evaluation_errors::Bool=false, mod::Module=Main)::Tuple{Any, Real}

Searches the grammar for the program that satisfies the maximum number of examples in the problem.
The evaluator should be a function that takes a SymbolTable, expression and a dictionary with
input variable assignments and returns the output of the expression.

- g - The grammar that defines the search space
- problem - The problem definition with IO examples
- problem - The problem definition with an AbstractSpecification
- start - The start symbol in the grammar
- evaluator - The evaluation function. Takes a SymbolTable, expression and a dictionary with
input variable assignments and returns the output of the expression.
Expand All @@ -196,9 +196,9 @@ Can be considerably slower than `search` due to having to evaluate each expressi
"""
function search_best(
g::Grammar,
problem::Problem,
problem::HerbSpecification.Problem,
start::Symbol;
evaluator::Function=test_with_input,
evaluator::Function=execute_on_input,
enumerator::Function=get_bfs_enumerator,
error_function::Function=default_error_function,
max_depth::Union{Int, Nothing}=nothing,
Expand Down Expand Up @@ -230,7 +230,7 @@ function search_best(
# Evaluate the expression on the examples
total_error = 0
crashed = false
for example ∈ problem.examples
for example ∈ problem.spec
try
output = evaluator(symboltable, expr, example.in)
total_error = error_function(total_error, output, example.out)
Expand Down
24 changes: 12 additions & 12 deletions src/stochastic_enumerators.jl
Original file line number Diff line number Diff line change
@@ -1,18 +1,18 @@
"""
get_mh_enumerator(examples::AbstractArray{<:Example}, cost_function::Function, evaluation_function::Function=HerbInterpret.test_with_input)
get_mh_enumerator(spec::AbstractSpecification, cost_function::Function, evaluation_function::Function=HerbInterpret.execute_on_input)

Returns an enumerator that runs according to the Metropolis Hastings algorithm.
- `examples` : array of examples
- `spec` : AbstractSpecification, could be an array of IOexamples
- `cost_function` : cost function to evaluate the programs proposed
- `evaluation_function` : evaluation function that evaluates the program generated and produces an output
The propose function is random_fill_propose and the accept function is probabilistic.
The temperature value of the algorithm remains constant over time.
"""
function get_mh_enumerator(examples::AbstractArray{<:Example}, cost_function::Function, evaluation_function::Function=HerbInterpret.test_with_input)
function get_mh_enumerator(spec::AbstractSpecification, cost_function::Function, evaluation_function::Function=execute_on_input)
return (grammar, max_depth, max_size, start_symbol) -> begin
return StochasticSearchEnumerator(
grammar=grammar,
examples=examples,
spec=spec,
max_depth=max_depth,
neighbourhood=constructNeighbourhood,
propose=random_fill_propose,
Expand All @@ -27,22 +27,22 @@ function get_mh_enumerator(examples::AbstractArray{<:Example}, cost_function::Fu
end

"""
get_vlsn_enumerator(examples, cost_function, enumeration_depth = 2, evaluation_function::Function=HerbInterpret.test_with_input)
get_vlsn_enumerator(spec, cost_function, enumeration_depth = 2, evaluation_function::Function=HerbInterpret.execute_on_input)

Returns an enumerator that runs according to the Very Large Scale Neighbourhood Search algorithm.
- `examples` : array of examples
- `spec` :AbstractSpecification, could be an array of IOexamples
- `cost_function` : cost function to evaluate the programs proposed
- `enumeration_depth` : the enumeration depth to search for a best program at a time
- `evaluation_function` : evaluation function that evaluates the program generated and produces an output
The propose function consists of all possible programs of the given `enumeration_depth`. The accept function accepts the program
with the lowest cost according to the `cost_function`.
The temperature value of the algorithm remains constant over time.
"""
function get_vlsn_enumerator(examples, cost_function, enumeration_depth = 2, evaluation_function::Function=HerbInterpret.test_with_input)
function get_vlsn_enumerator(spec, cost_function, enumeration_depth = 2, evaluation_function::Function=execute_on_input)
return (grammar, max_depth, max_size, start_symbol) -> begin
return StochasticSearchEnumerator(
grammar=grammar,
examples=examples,
spec=spec,
max_depth=max_depth,
neighbourhood=constructNeighbourhood,
propose=enumerate_neighbours_propose(enumeration_depth),
Expand All @@ -56,22 +56,22 @@ function get_vlsn_enumerator(examples, cost_function, enumeration_depth = 2, eva
end

"""
get_sa_enumerator(examples, cost_function, initial_temperature=1, temperature_decreasing_factor = 0.99, evaluation_function::Function=HerbInterpret.test_with_input)
get_sa_enumerator(spec, cost_function, initial_temperature=1, temperature_decreasing_factor = 0.99, evaluation_function::Function=HerbInterpret.execute_on_input)

Returns an enumerator that runs according to the Very Large Scale Neighbourhood Search algorithm.
- `examples` : array of examples
- `spec` : AbstractSpecification, could be an array of IOexamples
- `cost_function` : cost function to evaluate the programs proposed
- `initial_temperature` : the starting temperature of the algorithm
- `temperature_decreasing_factor` : the decreasing factor of the temperature of the time
- `evaluation_function` : evaluation function that evaluates the program generated and produces an output
The propose function is `random_fill_propose` (the same as for Metropolis Hastings). The accept function is probabilistic
but takes into account the tempeerature too.
"""
function get_sa_enumerator(examples, cost_function, initial_temperature=1, temperature_decreasing_factor = 0.99, evaluation_function::Function=HerbInterpret.test_with_input)
function get_sa_enumerator(spec, cost_function, initial_temperature=1, temperature_decreasing_factor = 0.99, evaluation_function::Function=execute_on_input)
return (grammar, max_depth, max_size, start_symbol) -> begin
return StochasticSearchEnumerator(
grammar=grammar,
examples=examples,
spec=spec,
max_depth=max_depth,
neighbourhood=constructNeighbourhood,
propose=random_fill_propose,
Expand Down
Loading
Loading