From f3e7ffa6bd166c5318becbbc12334d4a28e9bdcd Mon Sep 17 00:00:00 2001 From: "Documenter.jl" Date: Thu, 14 Nov 2024 12:18:55 +0000 Subject: [PATCH] build based on 186d84e --- previews/PR115/.documenter-siteinfo.json | 2 +- previews/PR115/HerbConstraints/index.html | 2 +- previews/PR115/HerbCore/index.html | 2 +- previews/PR115/HerbGrammar/index.html | 2 +- previews/PR115/HerbInterpret/index.html | 2 +- previews/PR115/HerbSearch/index.html | 6 +++--- previews/PR115/HerbSpecification/index.html | 2 +- previews/PR115/concepts/index.html | 2 +- previews/PR115/get_started/index.html | 2 +- previews/PR115/index.html | 2 +- previews/PR115/install/index.html | 2 +- previews/PR115/search_index.js | 2 +- previews/PR115/tutorials/TopDown/index.html | 2 +- previews/PR115/tutorials/abstract_syntax_trees/index.html | 2 +- previews/PR115/tutorials/advanced_search/index.html | 8 ++++---- previews/PR115/tutorials/defining_grammars/index.html | 2 +- .../tutorials/getting_started_with_constraints/index.html | 2 +- .../PR115/tutorials/getting_started_with_herb/index.html | 2 +- .../PR115/tutorials/working_with_interpreters/index.html | 2 +- 19 files changed, 24 insertions(+), 24 deletions(-) diff --git a/previews/PR115/.documenter-siteinfo.json b/previews/PR115/.documenter-siteinfo.json index 3541ed9..8f9c741 100644 --- a/previews/PR115/.documenter-siteinfo.json +++ b/previews/PR115/.documenter-siteinfo.json @@ -1 +1 @@ -{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-14T12:17:20","documenter_version":"1.8.0"}} \ No newline at end of file +{"documenter":{"julia_version":"1.11.1","generation_timestamp":"2024-11-14T12:18:48","documenter_version":"1.8.0"}} \ No newline at end of file diff --git a/previews/PR115/HerbConstraints/index.html b/previews/PR115/HerbConstraints/index.html index 18db4f2..947384f 100644 --- a/previews/PR115/HerbConstraints/index.html +++ b/previews/PR115/HerbConstraints/index.html @@ -23,4 +23,4 @@ set_value!(a, 9) #backup value 10 set_value!(a, 8) #no need to backup again set_value!(a, 3) #no need to backup again -restore!(sm) #restores a to value 10source
HerbConstraints.check_treeMethod
check_tree(c::Contains, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the Contains constraint.

source
HerbConstraints.check_treeMethod
check_tree(c::ContainsSubtree, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the ContainsSubtree constraint.

source
HerbConstraints.check_treeMethod
check_tree(c::Forbidden, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the Forbidden constraint.

source
HerbConstraints.check_treeMethod
check_tree(c::ForbiddenSequence, tree::AbstractRuleNode; sequence_started=false)::Bool

Checks if the given AbstractRuleNode tree abides the ForbiddenSequence constraint.

source
HerbConstraints.check_treeMethod
check_tree(c::Ordered, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the Ordered constraint.

source
HerbConstraints.check_treeMethod
function check_tree(c::Unique, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the Unique constraint.

source
HerbConstraints.contains_varnodeMethod
contains_varnode(rn::AbstractRuleNode, name::Symbol)

Checks if an AbstractRuleNode tree contains a VarNode with the given name.

source
HerbConstraints.deactivate!Method
deactivate!(solver::GenericSolver, constraint::AbstractLocalConstraint)

Function that should be called whenever the constraint is already satisfied and never has to be repropagated.

source
HerbConstraints.deactivate!Method
deactivate!(solver::UniformSolver, constraint::AbstractLocalConstraint)

Function that should be called whenever the constraint is already satisfied and never has to be repropagated.

source
HerbConstraints.decrement!Method

Decrease the value of the integer by 1

source
HerbConstraints.fix_point!Method
fix_point!(solver::Solver)

Propagate constraints in the current state until no further dedecutions can be made

source
HerbConstraints.freeze_stateMethod
freeze_state(hole::StateHole)::RuleNode

Converts a [StateHole])(@ref) to a [RuleNode]@(ref). The hole and its children are assumed to be filled.

source
HerbConstraints.get_grammarMethod
function get_grammar(solver::GenericSolver)::AbstractGrammar

Get the grammar.

source
HerbConstraints.get_grammarMethod
function get_grammar(solver::UniformSolver)::AbstractGrammar

Get the grammar.

source
HerbConstraints.get_hole_at_locationMethod
get_hole_at_location(solver::GenericSolver, location::Vector{Int})::AbstractHole

Get the node at path location and assert it is a AbstractHole.

source
HerbConstraints.get_hole_at_locationMethod
get_hole_at_location(solver::UniformSolver, path::Vector{Int})

Get the hole that is located at the provided path.

source
HerbConstraints.get_intersectionMethod
get_intersection(domain1::BitVector, domain2::BitVector)::Bool

Returns all the values that are in both domain1 and domain2

source
HerbConstraints.get_max_depthMethod
function get_max_depth(solver::GenericSolver)::SolverState

Get the maximum depth of the tree.

source
HerbConstraints.get_max_sizeMethod
function get_max_depth(solver::GenericSolver)::SolverState

Get the maximum number of AbstractRuleNodes allowed inside the tree.

source
HerbConstraints.get_nodesMethod
get_nodes(solver)

Return an iterator over all nodes in the tree

source
HerbConstraints.get_nodes_on_pathMethod
function get_nodes_on_path(root::AbstractRuleNode, path::Vector{Int})::Vector{AbstractRuleNode}

Gets a list of nodes on the path, starting (and including) the root.

source
HerbConstraints.get_priorityMethod
function get_priority(::AbstractLocalConstraint)

Used to determine which constraint to propagate first in fix_point!. Constraints with fast propagators and/or strong inference should be propagated first.

source
HerbConstraints.get_starting_symbolMethod
function get_starting_symbol(solver::GenericSolver)::Symbol

Get the symbol from the solver.

source
HerbConstraints.get_stateMethod
function get_state(solver::GenericSolver)::SolverState

Get the current [SolverState]@(ref) of the solver.

source
HerbConstraints.get_treeMethod
function get_tree(solver::GenericSolver)::AbstractRuleNode

Returns the number of AbstractRuleNodes in the tree.

source
HerbConstraints.get_treeMethod
function get_tree(solver::UniformSolver)::AbstractRuleNode

Get the root of the tree. This remains the same instance throughout the entire search.

source
HerbConstraints.get_tree_sizeMethod
function get_tree_size(solver::GenericSolver)::Int

Returns the number of AbstractRuleNodes in the tree.

source
HerbConstraints.get_valueMethod

Get the value of the stateful integer

source
HerbConstraints.increment!Method

Increase the value of the integer by 1

source
HerbConstraints.is_subdomainMethod
is_subdomain(specific_tree::AbstractRuleNode, general_tree::AbstractRuleNode)

Checks if the specific_tree can be obtained by repeatedly removing values from the general_tree

source
HerbConstraints.is_subdomainMethod
 is_subdomain(subdomain::BitVector, domain::BitVector)

Checks if subdomain is a subdomain of domain. Example: [0, 0, 1, 0] is a subdomain of [0, 1, 1, 1]

source
HerbConstraints.isfeasibleMethod
isfeasible(solver::GenericSolver)

Returns true if no inconsistency has been detected. Used in several ways:

  • Iterators should check for infeasibility to discard infeasible states
  • After any tree manipulation with the possibility of an inconsistency (e.g. remove_below!, remove_above!, remove!)
  • fix_point! should check for infeasibility to clear its schedule and return
  • Some GenericSolver functions assert a feasible state for debugging purposes @assert isfeasible(solver)
  • Some GenericSolver functions have a guard that skip the function on an infeasible state: if !isfeasible(solver) return end
source
HerbConstraints.isfeasibleMethod
isfeasible(solver::UniformSolver)

Returns true if no inconsistency has been detected.

source
HerbConstraints.load_state!Method
load_state!(solver::GenericSolver, state::SolverState)

Overwrites the current state with the given state

source
HerbConstraints.make_equal!Method
function make_equal!(solver::Solver, node1::AbstractRuleNode, node2::AbstractRuleNode)::MakeEqualResult

Tree manipulation that enforces node1 == node2 if unambiguous.

source
HerbConstraints.make_less_than_or_equal!Method
function make_less_than_or_equal!(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole}, guards::Vector{Tuple{AbstractHole, Int}})::LessThanOrEqualResult

Helper function that keeps track of the guards

source
HerbConstraints.make_less_than_or_equal!Method
function make_less_than_or_equal!(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole})::LessThanOrEqualResult

Ensures that n1<=n2 by removing impossible values from holes. Returns one of the following results:

source
HerbConstraints.make_less_than_or_equal!Method
function make_less_than_or_equal!(solver::Solver, nodes1::Vector{AbstractRuleNode}, nodes2::Vector{AbstractRuleNode}, guards::Vector{Tuple{AbstractHole, Int}})::LessThanOrEqualResult

Helper function that tiebreaks on children.

source
HerbConstraints.new_state!Method
new_state!(solver::GenericSolver, tree::AbstractRuleNode)

Overwrites the current state and propagates constraints on the tree from the ground up

source
HerbConstraints.notify_new_nodeMethod
notify_new_node(solver::GenericSolver, event_path::Vector{Int})

Notify all constraints that a new node has appeared at the event_path by calling their respective on_new_node function.

Warning

This does not notify the solver about nodes below the event_path. In that case, call notify_new_nodes instead.

source
HerbConstraints.notify_new_nodesMethod
notify_new_nodes(solver::GenericSolver, node::AbstractRuleNode, path::Vector{Int})

Notify all grammar constraints about the new node and its (grand)children

source
HerbConstraints.notify_new_nodesMethod
notify_new_nodes(solver::UniformSolver, node::AbstractRuleNode, path::Vector{Int})

Notify all grammar constraints about the new node and its (grand)children

source
HerbConstraints.notify_tree_manipulationMethod
notify_tree_manipulation(solver::GenericSolver, event_path::Vector{Int})

Notify subscribed constraints that a tree manipulation has occured at the event_path by scheduling them for propagation

source
HerbConstraints.notify_tree_manipulationMethod
notify_tree_manipulation(solver::UniformSolver, event_path::Vector{Int})

Notify subscribed constraints that a tree manipulation has occured at the event_path by scheduling them for propagation

source
HerbConstraints.partitionMethod
partition(hole::Hole, grammar::ContextSensitiveGrammar)::Vector{BitVector}

Partition a Hole into subdomains grouped by childtypes

source
HerbConstraints.pattern_matchMethod

Generic fallback function for commutativity. Swaps arguments 1 and 2, then dispatches to a more specific signature. If this gets stuck in an infinite loop, the implementation of an AbstractRuleNode type pair is missing.

source
HerbConstraints.pattern_matchMethod
pattern_match(rn::AbstractRuleNode, mn::AbstractRuleNode)::PatternMatchResult

Recursively tries to match AbstractRuleNode rn with AbstractRuleNode mn. Returns a PatternMatchResult that describes if the pattern was matched.

source
HerbConstraints.pattern_matchMethod
pattern_match(node::AbstractRuleNode, domainrulenode::DomainRuleNode, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult

Comparing any AbstractRuleNode with a DomainRuleNode

source
HerbConstraints.pattern_matchMethod
pattern_match(rn::AbstractRuleNode, var::VarNode, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult

Comparing any AbstractRuleNode with a named VarNode

source
HerbConstraints.pattern_matchMethod
pattern_match(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole}, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult

Comparing any pair of Rulenode and/or AbstractHole. It is important to note that some AbstractHoles are already filled and should be treated as RuleNode. This is why this function is dispatched on (isfilled(h1), isfilled(h2)). The '(RuleNode, AbstractHole)' case could still include two nodes of type AbstractHole, but one of them should be treated as a rulenode.

source
HerbConstraints.pattern_matchMethod
pattern_match(rns::Vector{AbstractRuleNode}, mns::Vector{AbstractRuleNode}, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult

Pairwise tries to match two ordered lists of AbstractRuleNodes. Typically, this function is used to pattern match the children two AbstractRuleNodes.

source
HerbConstraints.post!Method
post!(solver::GenericSolver, constraint::AbstractLocalConstraint)

Imposes the constraint to the current state. By default, the constraint will be scheduled for its initial propagation. Constraints can overload this method to add themselves to notify lists or triggers.

source
HerbConstraints.post!Method
post!(solver::UniformSolver, constraint::AbstractLocalConstraint)

Post a new local constraint. Converts the constraint to a state constraint and schedules it for propagation.

source
HerbConstraints.propagate!Method
function propagate!(::GenericSolver, ::LocalContainsSubtree)

!!! warning: LocalContainsSubtree uses stateful properties and can therefore not be propagated in the GenericSolver. (The GenericSolver shares constraints among different states, so they cannot use stateful properties)

source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalContains)

Enforce that the rule appears at or below the path at least once. Uses a helper function to retrieve a list of holes that can potentially hold the target rule. If there is only a single hole that can potentially hold the target rule, that hole will be filled with that rule.

source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalForbiddenSequence)
source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalForbidden)

Enforce that the forbidden tree does not occur at the path. The forbidden tree is matched against the AbstractRuleNode located at the path. Deductions are based on the type of the PatternMatchResult returned by the pattern_match function.

source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalOrdered)

Enforce that the VarNodes in the tree are in the specified order. First the node located at the path is matched to see if the ordered constraint applies here. The nodes matching the variables are stored in the vars dictionary. Then the order is enforced within the make_less_than_or_equal! tree manipulation.

source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalUnique)

Enforce that the rule appears at or below the path at least once. Uses a helper function to retrieve a list of holes that can potentially hold the target rule. If there is only a single hole that can potentially hold the target rule, that hole will be filled with that rule.

source
HerbConstraints.propagate!Method
function propagate!(solver::UniformSolver, c::LocalContainsSubtree)

Enforce that the tree appears at or below the path at least once. Nodes that can potentially become the target sub-tree are considered candidates. In case of multiple candidates, a stateful set of indices is used to keep track of active candidates.

source
HerbConstraints.remove!Method
remove!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)

Remove rule_index from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.

source
HerbConstraints.remove!Method
remove!(solver::GenericSolver, path::Vector{Int}, rules::Vector{Int})

Remove all rules from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.

source
HerbConstraints.remove!Method
remove!(set::StateSparseSet, val::Int)

Removes value val from StateSparseSet set. Returns true if val was in set.

source
HerbConstraints.remove!Method
remove!(solver::Solver, path::Vector{Int}, rule_index::Int)

Remove rule_index from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.

source
HerbConstraints.remove!Method
remove!(solver::UniformSolver, path::Vector{Int}, rules::Vector{Int})

Remove all rules from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.

source
HerbConstraints.remove_above!Method
remove_above!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)

Reduce the domain of the hole located at the path by removing all rules indices above rule_index Example: rule_index = 2. hole with domain [1, 1, 0, 1] gets reduced to [1, 0, 0, 0] and gets simplified to a RuleNode

source
HerbConstraints.remove_above!Method

Remove all the values greater than val from the set

source
HerbConstraints.remove_above!Method
remove_above!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)

Reduce the domain of the hole located at the path by removing all rules indices above rule_index Example: rule_index = 2. hole with domain {1, 2, 4} gets reduced to {1}

source
HerbConstraints.remove_all_but!Method
remove_all_but!(solver::GenericSolver, path::Vector{Int}, new_domain::BitVector)

Reduce the domain of the hole located at the path, to the new_domain. It is assumed the path points to a hole, otherwise an exception will be thrown. It is assumed new_domain ⊆ domain. For example: [1, 0, 1, 0] ⊆ [1, 0, 1, 1]

source
HerbConstraints.remove_all_but!Method
remove_all_but!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)

Fill in the hole located at the path with rule rule_index. It is assumed the path points to a hole, otherwise an exception will be thrown. It is assumed rule_index ∈ hole.domain.

!!! warning: If the hole is known to be in the current tree, the hole can be passed directly. The caller has to make sure that the hole instance is actually present at the provided path.

source
HerbConstraints.remove_all_but!Method
remove_all_but!(set::StateSparseSet, val::Int)::Bool

Removes all values from StateSparseSet set, except val

source
HerbConstraints.remove_all_but!Method
remove_all_but!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)

Fill in the hole located at the path with rule rule_index.

source
HerbConstraints.remove_below!Method
remove_below!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)

Reduce the domain of the hole located at the path by removing all rules indices below rule_index Example: rule_index = 2. hole with domain [1, 1, 0, 1] gets reduced to [0, 1, 0, 1]

source
HerbConstraints.remove_below!Method

Remove all the values less than val from the set

source
HerbConstraints.remove_below!Method
remove_below!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)

Reduce the domain of the hole located at the path by removing all rules indices below rule_index Example: rule_index = 2. hole with domain {1, 2, 4} gets reduced to {2, 4}

source
HerbConstraints.remove_node!Method
function remove_node!(solver::GenericSolver, path::Vector{Int})

Remove the node at the given path by substituting it with a hole of the same symbol.

source
HerbConstraints.restore!Method

Restores the StateInt stored in the StateIntBackup to its original value

source
HerbConstraints.restore!Method

Reverts all the backups since the last save_state!.

source
HerbConstraints.restore!Method

Restore state of the solver until the last save_state!

source
HerbConstraints.save_state!Method
save_state!(solver::GenericSolver)

Returns a copy of the current state that can be restored by calling load_state!(solver, state)

source
HerbConstraints.save_state!Method

Make a backup of the current state. Return to this state by calling restore!.

source
HerbConstraints.save_state!Method

Save the current state of the solver, can restored using restore!

source
HerbConstraints.schedule!Method
schedule(solver::GenericSolver, constraint::AbstractLocalConstraint)

Schedules the constraint for propagation.

source
HerbConstraints.set_infeasible!Method
set_infeasible!(solver::GenericSolver)

Function to be called if any inconsistency has been detected

source
HerbConstraints.set_infeasible!Method
set_infeasible!(solver::Solver)

Function to be called if any inconsistency has been detected

source
HerbConstraints.set_value!Method

Set the value of the integer to the given val

source
HerbConstraints.shouldscheduleMethod
shouldschedule(solver::Solver, constraint::AbstractLocalConstraint, path::Vector{Int})::Bool

Function that is called when a tree manipulation occured at the path. Returns true if the constraint should be scheduled for propagation.

Default behavior: return true iff the manipulation happened at or below the constraint path.

source
HerbConstraints.shouldscheduleMethod
shouldschedule(::Solver, constraint::LocalForbiddenSequence, path::Vector{Int})::Bool

Return true iff the manipulation happened at or above the constraint path.

source
HerbConstraints.simplify_hole!Method
simplify_hole!(solver::GenericSolver, path::Vector{Int})

Takes a Hole and tries to simplify it to a UniformHole or RuleNode. If the domain of the hole is empty, the state will be marked as infeasible

source
HerbConstraints.substitute!Method
substitute!(solver::GenericSolver, path::Vector{Int}, new_node::AbstractRuleNode; is_domain_increasing::Union{Nothing, Bool}=nothing)

Substitute the node at the path, with a new_node.

  • is_domain_increasing: indicates if all grammar constraints should be repropagated from the ground up.

Domain increasing substitutions are substitutions that cannot be achieved by repeatedly removing values from domains. Example of an domain increasing event: hole[{3, 4, 5}] -> hole[{1, 2}]. Example of an domain decreasing event: hole[{3, 4, 5}] -> rulenode(4, [hole[{1, 2}], rulenode(1)]).

source
HerbCore.contains_holeMethod
contains_hole(hole::StateHole)::Bool

Returns true if the hole or any of its (grand)children are not filled.

source
HerbCore.get_node_at_locationMethod
HerbCore.get_node_at_location(solver::GenericSolver, location::Vector{Int})::AbstractRuleNode

Get the node at path location.

source
HerbCore.get_node_at_locationMethod
get_node_at_location(solver::UniformSolver, path::Vector{Int})

Get the node that is located at the provided path.

source
HerbCore.get_pathMethod
get_path(solver::GenericSolver, node::AbstractRuleNode)

Get the path at which the node is located.

source
HerbCore.get_pathMethod
get_path(solver::UniformSolver, node::AbstractRuleNode)

Get the path at which the node is located.

source
HerbCore.get_ruleMethod
get_rule(hole::StateHole)::Int

Assuming the hole has domain size 1, get the rule it is currently assigned to.

source
HerbCore.isfilledMethod
isfilled(hole::StateHole)::Bool

Holes with domain size 1 are fixed to a rule. Returns whether the hole has domain size 1. (holes with an empty domain are not considered to be fixed)

source

Index

+restore!(sm) #restores a to value 10source
HerbConstraints.check_treeMethod
check_tree(c::Contains, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the Contains constraint.

source
HerbConstraints.check_treeMethod
check_tree(c::ContainsSubtree, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the ContainsSubtree constraint.

source
HerbConstraints.check_treeMethod
check_tree(c::Forbidden, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the Forbidden constraint.

source
HerbConstraints.check_treeMethod
check_tree(c::ForbiddenSequence, tree::AbstractRuleNode; sequence_started=false)::Bool

Checks if the given AbstractRuleNode tree abides the ForbiddenSequence constraint.

source
HerbConstraints.check_treeMethod
check_tree(c::Ordered, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the Ordered constraint.

source
HerbConstraints.check_treeMethod
function check_tree(c::Unique, tree::AbstractRuleNode)::Bool

Checks if the given AbstractRuleNode tree abides the Unique constraint.

source
HerbConstraints.contains_varnodeMethod
contains_varnode(rn::AbstractRuleNode, name::Symbol)

Checks if an AbstractRuleNode tree contains a VarNode with the given name.

source
HerbConstraints.deactivate!Method
deactivate!(solver::GenericSolver, constraint::AbstractLocalConstraint)

Function that should be called whenever the constraint is already satisfied and never has to be repropagated.

source
HerbConstraints.deactivate!Method
deactivate!(solver::UniformSolver, constraint::AbstractLocalConstraint)

Function that should be called whenever the constraint is already satisfied and never has to be repropagated.

source
HerbConstraints.decrement!Method

Decrease the value of the integer by 1

source
HerbConstraints.fix_point!Method
fix_point!(solver::Solver)

Propagate constraints in the current state until no further dedecutions can be made

source
HerbConstraints.freeze_stateMethod
freeze_state(hole::StateHole)::RuleNode

Converts a [StateHole])(@ref) to a [RuleNode]@(ref). The hole and its children are assumed to be filled.

source
HerbConstraints.get_grammarMethod
function get_grammar(solver::GenericSolver)::AbstractGrammar

Get the grammar.

source
HerbConstraints.get_grammarMethod
function get_grammar(solver::UniformSolver)::AbstractGrammar

Get the grammar.

source
HerbConstraints.get_hole_at_locationMethod
get_hole_at_location(solver::GenericSolver, location::Vector{Int})::AbstractHole

Get the node at path location and assert it is a AbstractHole.

source
HerbConstraints.get_hole_at_locationMethod
get_hole_at_location(solver::UniformSolver, path::Vector{Int})

Get the hole that is located at the provided path.

source
HerbConstraints.get_intersectionMethod
get_intersection(domain1::BitVector, domain2::BitVector)::Bool

Returns all the values that are in both domain1 and domain2

source
HerbConstraints.get_max_depthMethod
function get_max_depth(solver::GenericSolver)::SolverState

Get the maximum depth of the tree.

source
HerbConstraints.get_max_sizeMethod
function get_max_depth(solver::GenericSolver)::SolverState

Get the maximum number of AbstractRuleNodes allowed inside the tree.

source
HerbConstraints.get_nodesMethod
get_nodes(solver)

Return an iterator over all nodes in the tree

source
HerbConstraints.get_nodes_on_pathMethod
function get_nodes_on_path(root::AbstractRuleNode, path::Vector{Int})::Vector{AbstractRuleNode}

Gets a list of nodes on the path, starting (and including) the root.

source
HerbConstraints.get_priorityMethod
function get_priority(::AbstractLocalConstraint)

Used to determine which constraint to propagate first in fix_point!. Constraints with fast propagators and/or strong inference should be propagated first.

source
HerbConstraints.get_starting_symbolMethod
function get_starting_symbol(solver::GenericSolver)::Symbol

Get the symbol from the solver.

source
HerbConstraints.get_stateMethod
function get_state(solver::GenericSolver)::SolverState

Get the current [SolverState]@(ref) of the solver.

source
HerbConstraints.get_treeMethod
function get_tree(solver::GenericSolver)::AbstractRuleNode

Returns the number of AbstractRuleNodes in the tree.

source
HerbConstraints.get_treeMethod
function get_tree(solver::UniformSolver)::AbstractRuleNode

Get the root of the tree. This remains the same instance throughout the entire search.

source
HerbConstraints.get_tree_sizeMethod
function get_tree_size(solver::GenericSolver)::Int

Returns the number of AbstractRuleNodes in the tree.

source
HerbConstraints.get_valueMethod

Get the value of the stateful integer

source
HerbConstraints.increment!Method

Increase the value of the integer by 1

source
HerbConstraints.is_subdomainMethod
is_subdomain(specific_tree::AbstractRuleNode, general_tree::AbstractRuleNode)

Checks if the specific_tree can be obtained by repeatedly removing values from the general_tree

source
HerbConstraints.is_subdomainMethod
 is_subdomain(subdomain::BitVector, domain::BitVector)

Checks if subdomain is a subdomain of domain. Example: [0, 0, 1, 0] is a subdomain of [0, 1, 1, 1]

source
HerbConstraints.isfeasibleMethod
isfeasible(solver::GenericSolver)

Returns true if no inconsistency has been detected. Used in several ways:

  • Iterators should check for infeasibility to discard infeasible states
  • After any tree manipulation with the possibility of an inconsistency (e.g. remove_below!, remove_above!, remove!)
  • fix_point! should check for infeasibility to clear its schedule and return
  • Some GenericSolver functions assert a feasible state for debugging purposes @assert isfeasible(solver)
  • Some GenericSolver functions have a guard that skip the function on an infeasible state: if !isfeasible(solver) return end
source
HerbConstraints.isfeasibleMethod
isfeasible(solver::UniformSolver)

Returns true if no inconsistency has been detected.

source
HerbConstraints.load_state!Method
load_state!(solver::GenericSolver, state::SolverState)

Overwrites the current state with the given state

source
HerbConstraints.make_equal!Method
function make_equal!(solver::Solver, node1::AbstractRuleNode, node2::AbstractRuleNode)::MakeEqualResult

Tree manipulation that enforces node1 == node2 if unambiguous.

source
HerbConstraints.make_less_than_or_equal!Method
function make_less_than_or_equal!(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole}, guards::Vector{Tuple{AbstractHole, Int}})::LessThanOrEqualResult

Helper function that keeps track of the guards

source
HerbConstraints.make_less_than_or_equal!Method
function make_less_than_or_equal!(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole})::LessThanOrEqualResult

Ensures that n1<=n2 by removing impossible values from holes. Returns one of the following results:

source
HerbConstraints.make_less_than_or_equal!Method
function make_less_than_or_equal!(solver::Solver, nodes1::Vector{AbstractRuleNode}, nodes2::Vector{AbstractRuleNode}, guards::Vector{Tuple{AbstractHole, Int}})::LessThanOrEqualResult

Helper function that tiebreaks on children.

source
HerbConstraints.new_state!Method
new_state!(solver::GenericSolver, tree::AbstractRuleNode)

Overwrites the current state and propagates constraints on the tree from the ground up

source
HerbConstraints.notify_new_nodeMethod
notify_new_node(solver::GenericSolver, event_path::Vector{Int})

Notify all constraints that a new node has appeared at the event_path by calling their respective on_new_node function.

Warning

This does not notify the solver about nodes below the event_path. In that case, call notify_new_nodes instead.

source
HerbConstraints.notify_new_nodesMethod
notify_new_nodes(solver::GenericSolver, node::AbstractRuleNode, path::Vector{Int})

Notify all grammar constraints about the new node and its (grand)children

source
HerbConstraints.notify_new_nodesMethod
notify_new_nodes(solver::UniformSolver, node::AbstractRuleNode, path::Vector{Int})

Notify all grammar constraints about the new node and its (grand)children

source
HerbConstraints.notify_tree_manipulationMethod
notify_tree_manipulation(solver::GenericSolver, event_path::Vector{Int})

Notify subscribed constraints that a tree manipulation has occured at the event_path by scheduling them for propagation

source
HerbConstraints.notify_tree_manipulationMethod
notify_tree_manipulation(solver::UniformSolver, event_path::Vector{Int})

Notify subscribed constraints that a tree manipulation has occured at the event_path by scheduling them for propagation

source
HerbConstraints.partitionMethod
partition(hole::Hole, grammar::ContextSensitiveGrammar)::Vector{BitVector}

Partition a Hole into subdomains grouped by childtypes

source
HerbConstraints.pattern_matchMethod

Generic fallback function for commutativity. Swaps arguments 1 and 2, then dispatches to a more specific signature. If this gets stuck in an infinite loop, the implementation of an AbstractRuleNode type pair is missing.

source
HerbConstraints.pattern_matchMethod
pattern_match(rn::AbstractRuleNode, mn::AbstractRuleNode)::PatternMatchResult

Recursively tries to match AbstractRuleNode rn with AbstractRuleNode mn. Returns a PatternMatchResult that describes if the pattern was matched.

source
HerbConstraints.pattern_matchMethod
pattern_match(node::AbstractRuleNode, domainrulenode::DomainRuleNode, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult

Comparing any AbstractRuleNode with a DomainRuleNode

source
HerbConstraints.pattern_matchMethod
pattern_match(rn::AbstractRuleNode, var::VarNode, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult

Comparing any AbstractRuleNode with a named VarNode

source
HerbConstraints.pattern_matchMethod
pattern_match(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole}, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult

Comparing any pair of Rulenode and/or AbstractHole. It is important to note that some AbstractHoles are already filled and should be treated as RuleNode. This is why this function is dispatched on (isfilled(h1), isfilled(h2)). The '(RuleNode, AbstractHole)' case could still include two nodes of type AbstractHole, but one of them should be treated as a rulenode.

source
HerbConstraints.pattern_matchMethod
pattern_match(rns::Vector{AbstractRuleNode}, mns::Vector{AbstractRuleNode}, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult

Pairwise tries to match two ordered lists of AbstractRuleNodes. Typically, this function is used to pattern match the children two AbstractRuleNodes.

source
HerbConstraints.post!Method
post!(solver::GenericSolver, constraint::AbstractLocalConstraint)

Imposes the constraint to the current state. By default, the constraint will be scheduled for its initial propagation. Constraints can overload this method to add themselves to notify lists or triggers.

source
HerbConstraints.post!Method
post!(solver::UniformSolver, constraint::AbstractLocalConstraint)

Post a new local constraint. Converts the constraint to a state constraint and schedules it for propagation.

source
HerbConstraints.propagate!Method
function propagate!(::GenericSolver, ::LocalContainsSubtree)

!!! warning: LocalContainsSubtree uses stateful properties and can therefore not be propagated in the GenericSolver. (The GenericSolver shares constraints among different states, so they cannot use stateful properties)

source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalContains)

Enforce that the rule appears at or below the path at least once. Uses a helper function to retrieve a list of holes that can potentially hold the target rule. If there is only a single hole that can potentially hold the target rule, that hole will be filled with that rule.

source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalForbiddenSequence)
source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalForbidden)

Enforce that the forbidden tree does not occur at the path. The forbidden tree is matched against the AbstractRuleNode located at the path. Deductions are based on the type of the PatternMatchResult returned by the pattern_match function.

source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalOrdered)

Enforce that the VarNodes in the tree are in the specified order. First the node located at the path is matched to see if the ordered constraint applies here. The nodes matching the variables are stored in the vars dictionary. Then the order is enforced within the make_less_than_or_equal! tree manipulation.

source
HerbConstraints.propagate!Method
function propagate!(solver::Solver, c::LocalUnique)

Enforce that the rule appears at or below the path at least once. Uses a helper function to retrieve a list of holes that can potentially hold the target rule. If there is only a single hole that can potentially hold the target rule, that hole will be filled with that rule.

source
HerbConstraints.propagate!Method
function propagate!(solver::UniformSolver, c::LocalContainsSubtree)

Enforce that the tree appears at or below the path at least once. Nodes that can potentially become the target sub-tree are considered candidates. In case of multiple candidates, a stateful set of indices is used to keep track of active candidates.

source
HerbConstraints.remove!Method
remove!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)

Remove rule_index from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.

source
HerbConstraints.remove!Method
remove!(solver::GenericSolver, path::Vector{Int}, rules::Vector{Int})

Remove all rules from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.

source
HerbConstraints.remove!Method
remove!(set::StateSparseSet, val::Int)

Removes value val from StateSparseSet set. Returns true if val was in set.

source
HerbConstraints.remove!Method
remove!(solver::Solver, path::Vector{Int}, rule_index::Int)

Remove rule_index from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.

source
HerbConstraints.remove!Method
remove!(solver::UniformSolver, path::Vector{Int}, rules::Vector{Int})

Remove all rules from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.

source
HerbConstraints.remove_above!Method
remove_above!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)

Reduce the domain of the hole located at the path by removing all rules indices above rule_index Example: rule_index = 2. hole with domain [1, 1, 0, 1] gets reduced to [1, 0, 0, 0] and gets simplified to a RuleNode

source
HerbConstraints.remove_above!Method

Remove all the values greater than val from the set

source
HerbConstraints.remove_above!Method
remove_above!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)

Reduce the domain of the hole located at the path by removing all rules indices above rule_index Example: rule_index = 2. hole with domain {1, 2, 4} gets reduced to {1}

source
HerbConstraints.remove_all_but!Method
remove_all_but!(solver::GenericSolver, path::Vector{Int}, new_domain::BitVector)

Reduce the domain of the hole located at the path, to the new_domain. It is assumed the path points to a hole, otherwise an exception will be thrown. It is assumed new_domain ⊆ domain. For example: [1, 0, 1, 0] ⊆ [1, 0, 1, 1]

source
HerbConstraints.remove_all_but!Method
remove_all_but!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)

Fill in the hole located at the path with rule rule_index. It is assumed the path points to a hole, otherwise an exception will be thrown. It is assumed rule_index ∈ hole.domain.

!!! warning: If the hole is known to be in the current tree, the hole can be passed directly. The caller has to make sure that the hole instance is actually present at the provided path.

source
HerbConstraints.remove_all_but!Method
remove_all_but!(set::StateSparseSet, val::Int)::Bool

Removes all values from StateSparseSet set, except val

source
HerbConstraints.remove_all_but!Method
remove_all_but!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)

Fill in the hole located at the path with rule rule_index.

source
HerbConstraints.remove_below!Method
remove_below!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)

Reduce the domain of the hole located at the path by removing all rules indices below rule_index Example: rule_index = 2. hole with domain [1, 1, 0, 1] gets reduced to [0, 1, 0, 1]

source
HerbConstraints.remove_below!Method

Remove all the values less than val from the set

source
HerbConstraints.remove_below!Method
remove_below!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)

Reduce the domain of the hole located at the path by removing all rules indices below rule_index Example: rule_index = 2. hole with domain {1, 2, 4} gets reduced to {2, 4}

source
HerbConstraints.remove_node!Method
function remove_node!(solver::GenericSolver, path::Vector{Int})

Remove the node at the given path by substituting it with a hole of the same symbol.

source
HerbConstraints.restore!Method

Restores the StateInt stored in the StateIntBackup to its original value

source
HerbConstraints.restore!Method

Reverts all the backups since the last save_state!.

source
HerbConstraints.restore!Method

Restore state of the solver until the last save_state!

source
HerbConstraints.save_state!Method
save_state!(solver::GenericSolver)

Returns a copy of the current state that can be restored by calling load_state!(solver, state)

source
HerbConstraints.save_state!Method

Make a backup of the current state. Return to this state by calling restore!.

source
HerbConstraints.save_state!Method

Save the current state of the solver, can restored using restore!

source
HerbConstraints.schedule!Method
schedule(solver::GenericSolver, constraint::AbstractLocalConstraint)

Schedules the constraint for propagation.

source
HerbConstraints.set_infeasible!Method
set_infeasible!(solver::GenericSolver)

Function to be called if any inconsistency has been detected

source
HerbConstraints.set_infeasible!Method
set_infeasible!(solver::Solver)

Function to be called if any inconsistency has been detected

source
HerbConstraints.set_value!Method

Set the value of the integer to the given val

source
HerbConstraints.shouldscheduleMethod
shouldschedule(solver::Solver, constraint::AbstractLocalConstraint, path::Vector{Int})::Bool

Function that is called when a tree manipulation occured at the path. Returns true if the constraint should be scheduled for propagation.

Default behavior: return true iff the manipulation happened at or below the constraint path.

source
HerbConstraints.shouldscheduleMethod
shouldschedule(::Solver, constraint::LocalForbiddenSequence, path::Vector{Int})::Bool

Return true iff the manipulation happened at or above the constraint path.

source
HerbConstraints.simplify_hole!Method
simplify_hole!(solver::GenericSolver, path::Vector{Int})

Takes a Hole and tries to simplify it to a UniformHole or RuleNode. If the domain of the hole is empty, the state will be marked as infeasible

source
HerbConstraints.substitute!Method
substitute!(solver::GenericSolver, path::Vector{Int}, new_node::AbstractRuleNode; is_domain_increasing::Union{Nothing, Bool}=nothing)

Substitute the node at the path, with a new_node.

  • is_domain_increasing: indicates if all grammar constraints should be repropagated from the ground up.

Domain increasing substitutions are substitutions that cannot be achieved by repeatedly removing values from domains. Example of an domain increasing event: hole[{3, 4, 5}] -> hole[{1, 2}]. Example of an domain decreasing event: hole[{3, 4, 5}] -> rulenode(4, [hole[{1, 2}], rulenode(1)]).

source
HerbCore.contains_holeMethod
contains_hole(hole::StateHole)::Bool

Returns true if the hole or any of its (grand)children are not filled.

source
HerbCore.get_node_at_locationMethod
HerbCore.get_node_at_location(solver::GenericSolver, location::Vector{Int})::AbstractRuleNode

Get the node at path location.

source
HerbCore.get_node_at_locationMethod
get_node_at_location(solver::UniformSolver, path::Vector{Int})

Get the node that is located at the provided path.

source
HerbCore.get_pathMethod
get_path(solver::GenericSolver, node::AbstractRuleNode)

Get the path at which the node is located.

source
HerbCore.get_pathMethod
get_path(solver::UniformSolver, node::AbstractRuleNode)

Get the path at which the node is located.

source
HerbCore.get_ruleMethod
get_rule(hole::StateHole)::Int

Assuming the hole has domain size 1, get the rule it is currently assigned to.

source
HerbCore.isfilledMethod
isfilled(hole::StateHole)::Bool

Holes with domain size 1 are fixed to a rule. Returns whether the hole has domain size 1. (holes with an empty domain are not considered to be fixed)

source

Index

diff --git a/previews/PR115/HerbCore/index.html b/previews/PR115/HerbCore/index.html index d9d464a..c27fa3e 100644 --- a/previews/PR115/HerbCore/index.html +++ b/previews/PR115/HerbCore/index.html @@ -1,2 +1,2 @@ -HerbCore.jl · Herb.jl

HerbCore.jl Documentation

HerbCore.AbstractGrammarType
AbstractGrammar

Abstract type representing all grammars. It is assumed that all grammar structs have at least the following attributes:

  • rules::Vector{Any}: A list of RHS of rules (subexpressions).
  • types::Vector{Symbol}: A list of LHS of rules (types, all symbols).
  • isterminal::BitVector: A bitvector where bit i represents whether rule i is terminal.
  • iseval::BitVector: A bitvector where bit i represents whether rule i is an eval rule.
  • bytype::Dict{Symbol,Vector{Int}}: A dictionary that maps a type to all rules of said type.
  • domains::Dict{Symbol, BitVector}: A dictionary that maps a type to a domain bitvector. The domain bitvector has bit i set to true iff the ith rule is of this type.
  • childtypes::Vector{Vector{Symbol}}: A list of types of the children for each rule.

If a rule is terminal, the corresponding list is empty.

  • log_probabilities::Union{Vector{Real}, Nothing}: A list of probabilities for each rule.

If the grammar is non-probabilistic, the list can be nothing.

For concrete types, see ContextSensitiveGrammar within the HerbGrammar module.

source
HerbCore.AbstractHoleType
AbstractHole <: AbstractRuleNode

A AbstractHole is a placeholder where certain rules from the grammar can still be applied. The domain of a AbstractHole defines which rules can be applied. The domain is a bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied.

source
HerbCore.AbstractRuleNodeType
abstract type AbstractRuleNode end

Abstract type for representing expression trees. An AbstractRuleNode is expected to implement the following functions:

  • isfilled(::AbstractRuleNode)::Bool. True iff the grammar rule this node holds is not ambiguous, i.e. has domain size 1.
  • isuniform(::AbstractRuleNode)::Bool. True iff the children of this node are known.
  • get_rule(::AbstractRuleNode)::Int. Returns the index of the grammar rule it represents.
  • get_children(::AbstractRuleNode)::Vector{AbstractRuleNode}. Returns the children of this node.

Expression trees consist of RuleNodes and AbstractHoles.

source
HerbCore.HoleType

Hole <: AbstractHole

  • domain: A bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied.
source
HerbCore.RuleNodeType
RuleNode <: AbstractRuleNode

A RuleNode represents a node in an expression tree. Each node corresponds to a certain rule in the AbstractGrammar. A RuleNode consists of:

  • ind: The index of the rule in the AbstractGrammar which this node is representing.
  • _val: Field for caching evaluations of RuleNodes, preventing multiple unnecessary evaluations. The field can be used to store any needed infromation.
  • children: The children of this node in the expression tree
Compat

Evaluate immediately functionality is not yet supported by most of Herb.jl.

source
HerbCore.RuleNodeMethod
RuleNode(ind::Int, _val::Any)

Create a RuleNode for the AbstractGrammar rule with index ind, _val as immediately evaluated value and no children

Warning

Only use this constructor if you are absolutely certain that a rule is terminal and cannot have children. Use [RuleNode(ind::Int, grammar::AbstractGrammar)] for rules that might have children. In general, AbstractHoles should be used as a placeholder when the children of a node are not yet known.

Compat

Evaluate immediately functionality is not yet supported by most of Herb.jl.

source
HerbCore.UniformHoleType
UniformHole <: AbstractHole
  • domain: A bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied. All rules in the domain are required to have the same childtypes.
  • children: The children of this hole in the expression tree.
source
Base.islessMethod
Base.isless(rn₁::AbstractRuleNode, rn₂::AbstractRuleNode)::Bool

Compares two RuleNodes. Returns true if the left RuleNode is less than the right RuleNode. Order is determined from the index of the RuleNodes. If both RuleNodes have the same index, a depth-first search is performed in both RuleNodes until nodes with a different index are found.

source
Base.lengthMethod
Base.length(root::RuleNode)

Return the number of nodes in the tree rooted at root.

source
HerbCore.get_node_at_locationMethod
get_node_at_location(root::Hole, location::Vector{Int})

Retrieves the current hole, if location is this very hole. Throws error otherwise.

source
HerbCore.get_pathMethod
get_path(root::AbstractRuleNode, node::AbstractRuleNode)

Returns the path from the root to the targetnode. Returns nothing if no path exists.

source
HerbCore.get_rulesequenceMethod
get_rulesequence(node::RuleNode, path::Vector{Int})

Extract the derivation sequence from a path (sequence of child indices) and an AbstractRuleNode. If the path is deeper than the deepest node, it returns what it has.

source
HerbCore.have_same_shapeMethod
have_same_shape(node1::AbstractRuleNode, node2::AbstractRuleNode)

Returns true iff node1 and node2 have the same shape Example: RuleNode(3, [ RuleNode(1), RuleNode(1) ]) and RuleNode(9, [ RuleNode(2), Hole(domain) ]) have the same shape: 1 root with 2 children.

source
HerbCore.isfilledMethod
isfilled(node::AbstractRuleNode)::Bool

Returns whether the [AbstractRuleNode] holds a single rule. This is always the case for RuleNodes. Holes are considered to be "filled" iff their domain size is exactly 1.

source
HerbCore.node_depthMethod
node_depth(root::AbstractRuleNode, node::AbstractRuleNode)::Int

Return the depth of node for an AbstractRuleNode tree rooted at root. Depth is 1 when root == node.

Warning

node must be a subtree of root in order for this function to work.

source
HerbCore.rulesonleftMethod
rulesonleft(expr::RuleNode, path::Vector{Int})::Set{Int}

Finds all rules that are used in the left subtree defined by the path.

source
HerbCore.swap_nodeMethod
swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int})

Replace a node in expr, specified by path, with new_expr. Path is a sequence of child indices, starting from the root node.

source
HerbCore.swap_nodeMethod
swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode)

Replace child i of a node, a part of larger expr, with new_expr.

source

Index

+HerbCore.jl · Herb.jl

HerbCore.jl Documentation

HerbCore.AbstractGrammarType
AbstractGrammar

Abstract type representing all grammars. It is assumed that all grammar structs have at least the following attributes:

  • rules::Vector{Any}: A list of RHS of rules (subexpressions).
  • types::Vector{Symbol}: A list of LHS of rules (types, all symbols).
  • isterminal::BitVector: A bitvector where bit i represents whether rule i is terminal.
  • iseval::BitVector: A bitvector where bit i represents whether rule i is an eval rule.
  • bytype::Dict{Symbol,Vector{Int}}: A dictionary that maps a type to all rules of said type.
  • domains::Dict{Symbol, BitVector}: A dictionary that maps a type to a domain bitvector. The domain bitvector has bit i set to true iff the ith rule is of this type.
  • childtypes::Vector{Vector{Symbol}}: A list of types of the children for each rule.

If a rule is terminal, the corresponding list is empty.

  • log_probabilities::Union{Vector{Real}, Nothing}: A list of probabilities for each rule.

If the grammar is non-probabilistic, the list can be nothing.

For concrete types, see ContextSensitiveGrammar within the HerbGrammar module.

source
HerbCore.AbstractHoleType
AbstractHole <: AbstractRuleNode

A AbstractHole is a placeholder where certain rules from the grammar can still be applied. The domain of a AbstractHole defines which rules can be applied. The domain is a bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied.

source
HerbCore.AbstractRuleNodeType
abstract type AbstractRuleNode end

Abstract type for representing expression trees. An AbstractRuleNode is expected to implement the following functions:

  • isfilled(::AbstractRuleNode)::Bool. True iff the grammar rule this node holds is not ambiguous, i.e. has domain size 1.
  • isuniform(::AbstractRuleNode)::Bool. True iff the children of this node are known.
  • get_rule(::AbstractRuleNode)::Int. Returns the index of the grammar rule it represents.
  • get_children(::AbstractRuleNode)::Vector{AbstractRuleNode}. Returns the children of this node.

Expression trees consist of RuleNodes and AbstractHoles.

source
HerbCore.HoleType

Hole <: AbstractHole

  • domain: A bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied.
source
HerbCore.RuleNodeType
RuleNode <: AbstractRuleNode

A RuleNode represents a node in an expression tree. Each node corresponds to a certain rule in the AbstractGrammar. A RuleNode consists of:

  • ind: The index of the rule in the AbstractGrammar which this node is representing.
  • _val: Field for caching evaluations of RuleNodes, preventing multiple unnecessary evaluations. The field can be used to store any needed infromation.
  • children: The children of this node in the expression tree
Compat

Evaluate immediately functionality is not yet supported by most of Herb.jl.

source
HerbCore.RuleNodeMethod
RuleNode(ind::Int, _val::Any)

Create a RuleNode for the AbstractGrammar rule with index ind, _val as immediately evaluated value and no children

Warning

Only use this constructor if you are absolutely certain that a rule is terminal and cannot have children. Use [RuleNode(ind::Int, grammar::AbstractGrammar)] for rules that might have children. In general, AbstractHoles should be used as a placeholder when the children of a node are not yet known.

Compat

Evaluate immediately functionality is not yet supported by most of Herb.jl.

source
HerbCore.UniformHoleType
UniformHole <: AbstractHole
  • domain: A bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied. All rules in the domain are required to have the same childtypes.
  • children: The children of this hole in the expression tree.
source
Base.islessMethod
Base.isless(rn₁::AbstractRuleNode, rn₂::AbstractRuleNode)::Bool

Compares two RuleNodes. Returns true if the left RuleNode is less than the right RuleNode. Order is determined from the index of the RuleNodes. If both RuleNodes have the same index, a depth-first search is performed in both RuleNodes until nodes with a different index are found.

source
Base.lengthMethod
Base.length(root::RuleNode)

Return the number of nodes in the tree rooted at root.

source
HerbCore.get_node_at_locationMethod
get_node_at_location(root::Hole, location::Vector{Int})

Retrieves the current hole, if location is this very hole. Throws error otherwise.

source
HerbCore.get_pathMethod
get_path(root::AbstractRuleNode, node::AbstractRuleNode)

Returns the path from the root to the targetnode. Returns nothing if no path exists.

source
HerbCore.get_rulesequenceMethod
get_rulesequence(node::RuleNode, path::Vector{Int})

Extract the derivation sequence from a path (sequence of child indices) and an AbstractRuleNode. If the path is deeper than the deepest node, it returns what it has.

source
HerbCore.have_same_shapeMethod
have_same_shape(node1::AbstractRuleNode, node2::AbstractRuleNode)

Returns true iff node1 and node2 have the same shape Example: RuleNode(3, [ RuleNode(1), RuleNode(1) ]) and RuleNode(9, [ RuleNode(2), Hole(domain) ]) have the same shape: 1 root with 2 children.

source
HerbCore.isfilledMethod
isfilled(node::AbstractRuleNode)::Bool

Returns whether the [AbstractRuleNode] holds a single rule. This is always the case for RuleNodes. Holes are considered to be "filled" iff their domain size is exactly 1.

source
HerbCore.node_depthMethod
node_depth(root::AbstractRuleNode, node::AbstractRuleNode)::Int

Return the depth of node for an AbstractRuleNode tree rooted at root. Depth is 1 when root == node.

Warning

node must be a subtree of root in order for this function to work.

source
HerbCore.rulesonleftMethod
rulesonleft(expr::RuleNode, path::Vector{Int})::Set{Int}

Finds all rules that are used in the left subtree defined by the path.

source
HerbCore.swap_nodeMethod
swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int})

Replace a node in expr, specified by path, with new_expr. Path is a sequence of child indices, starting from the root node.

source
HerbCore.swap_nodeMethod
swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode)

Replace child i of a node, a part of larger expr, with new_expr.

source

Index

diff --git a/previews/PR115/HerbGrammar/index.html b/previews/PR115/HerbGrammar/index.html index 8f02152..efdc3ea 100644 --- a/previews/PR115/HerbGrammar/index.html +++ b/previews/PR115/HerbGrammar/index.html @@ -19,4 +19,4 @@ 0.3 : R = 1 | 2 0.2 : R = R + R end -)source
HerbGrammar.get_childtypesMethod
get_childtypes(rule::Any, types::AbstractVector{Symbol})

Returns the child types/nonterminals of a production rule.

source
HerbGrammar.get_domainMethod
get_domain(g::AbstractGrammar, type::Symbol)::BitVector

Returns the domain for the hole of a certain type as a BitVector of the same length as the number of rules in the grammar. Bit i is set to true iff rule i is of type type.

Info

Since this function can be intensively used when exploring a program space defined by a grammar, the outcomes of this function are precomputed and stored in the domains field in a AbstractGrammar.

source
HerbGrammar.get_domainMethod
get_domain(g::AbstractGrammar, rules::Vector{Int})::BitVector

Takes a domain rules defined as a vector of ints and converts it to a domain defined as a BitVector.

source
HerbGrammar.get_rulesequenceMethod
get_rulesequence(node::RuleNode, path::Vector{Int})

Extract the derivation sequence from a path (sequence of child indices) and an AbstractRuleNode. If the path is deeper than the deepest node, it returns what it has.

source
HerbGrammar.iscompleteMethod
iscomplete(grammar::AbstractGrammar, node::RuleNode)

Returns true if the expression represented by the RuleNode is a complete expression, meaning that it is fully defined and doesn't have any Holes.

source
HerbGrammar.isevalMethod
iseval(grammar::AbstractGrammar, index::Int)::Bool

Returns true if the production rule at rule_index contains the special _() eval function.

Compat

evaluate immediately functionality is not yet supported by most of Herb.jl

source
HerbGrammar.isevalMethod
iseval(grammar::AbstractGrammar)::Bool

Returns true if any production rules in grammar contain the special _() eval function.

Compat

evaluate immediately functionality is not yet supported by most of Herb.jl

source
HerbGrammar.isevalMethod
iseval(rule)

Returns true if the rule is the special evaluate immediately function, i.e., _()

Compat

evaluate immediately functionality is not yet supported by most of Herb.jl

source
HerbGrammar.isprobabilisticMethod
isprobabilistic(grammar::AbstractGrammar)::Bool

Function returns whether a AbstractGrammar is probabilistic.

source
HerbGrammar.isterminalMethod
isterminal(grammar::AbstractGrammar, node::AbstractRuleNode)::Bool

Returns true if the production rule used by node is terminal, i.e., does not contain any nonterminal symbols.

source
HerbGrammar.isterminalMethod
isterminal(grammar::AbstractGrammar, rule_index::Int)::Bool

Returns true if the production rule at rule_index is terminal, i.e., does not contain any nonterminal symbols.

source
HerbGrammar.isterminalMethod
isterminal(rule::Any, types::AbstractVector{Symbol})

Returns true if the rule is terminal, i.e., it does not contain any of the types in the provided vector. For example, :(x) is terminal, and :(1+1) is terminal, but :(Real + Real) is typically not.

source
HerbGrammar.isvariableMethod
isvariable(grammar::AbstractGrammar, ind::Int, mod::Module)::Bool

Return true if the rule with index ind represents a variable.

Taking into account the symbols defined in the given module(s).

source
HerbGrammar.isvariableMethod
isvariable(grammar::AbstractGrammar, ind::Int)::Bool

Return true if the rule with index ind represents a variable.

source
HerbGrammar.isvariableMethod
isvariable(grammar::AbstractGrammar, node::RuleNode, mod::Module)::Bool

Return true if the rule used by node represents a variable.

Taking into account the symbols defined in the given module(s).

source
HerbGrammar.isvariableMethod
isvariable(grammar::AbstractGrammar, node::RuleNode)::Bool

Return true if the rule used by node represents a variable in a program (essentially, an input to the program)

source
HerbGrammar.log_probabilityMethod
log_probability(grammar::AbstractGrammar, index::Int)::Real

Returns the log probability for the rule at index in the grammar.

Warning

If the grammar is not probabilistic, a warning is displayed, and a uniform probability is assumed.

source
HerbGrammar.max_arityMethod
max_arity(grammar::AbstractGrammar)::Int

Returns the maximum arity (number of children) over all production rules in the AbstractGrammar.

source
HerbGrammar.merge_grammars!Method
merge_grammars!(merge_to::AbstractGrammar, merge_from::AbstractGrammar)

Adds all rules and constraints from merge_from to merge_to.

source
HerbGrammar.mindepthMethod
mindepth(grammar::AbstractGrammar, typ::Symbol, dmap::AbstractVector{Int})

Returns the minimum depth achievable for a given nonterminal symbol. The minimum depth is the depth of the lowest tree that can be made using typ as a start symbol. dmap can be obtained from mindepth_map.

source
HerbGrammar.mindepth_mapMethod
mindepth_map(grammar::AbstractGrammar)

Returns the minimum depth achievable for each production rule in the AbstractGrammar. In other words, this function finds the depths of the lowest trees that can be made using each of the available production rules as a root.

source
HerbGrammar.nchildrenMethod
nchildren(grammar::AbstractGrammar, rule_index::Int)::Int

Returns the number of children (nonterminals) of the production rule at rule_index.

source
HerbGrammar.nchildrenMethod
nchildren(grammar::AbstractGrammar, node::RuleNode)::Int

Returns the number of children in the production rule used by node.

source
HerbGrammar.nonterminalsMethod
nonterminals(grammar::AbstractGrammar)::Vector{Symbol}

Returns a list of the nonterminals or types in the AbstractGrammar.

source
HerbGrammar.normalize!Function

A function for normalizing the probabilities of a probabilistic ContextSensitiveGrammar. If the optional type argument is provided, only the rules of that type are normalized.

source
HerbGrammar.parse_probabilistic_ruleMethod

Parses a single (potentially shorthand) derivation rule of a probabilistic ContextSensitiveGrammar. Returns nothing if the rule is not probabilistic, otherwise a Tuple of its type and a Vector of probability-rule pairs it expands into.

source
HerbGrammar.probabilityMethod
probability(grammar::AbstractGrammar, index::Int)::Real

Return the probability for a rule in the grammar. Use log_probability whenever possible.

Warning

If the grammar is not probabilistic, a warning is displayed, and a uniform probability is assumed.

source
HerbGrammar.read_csgFunction
read_csg(grammarpath::AbstractString, constraintspath::OptionalPath=nothing)::ContextSensitiveGrammar

Reads a ContextSensitiveGrammar from the files at grammarpath and constraintspath.

Danger

Only open trusted grammars. Parts of the grammar can be passed to Julia's eval function.

source
HerbGrammar.read_pcsgFunction
read_pcsg(grammarpath::AbstractString, constraintspath::OptionalPath=nothing)::ContextSensitiveGrammar

Reads a probabilistic ContextSensitiveGrammar from the files at grammarpath and constraintspath.

Danger

Only open trusted grammars. Parts of the grammar can be passed to Julia's eval function.

source
HerbGrammar.remove_rule!Method
remove_rule!(g::AbstractGrammar, idx::Int)

Removes the rule corresponding to idx from the grammar. In order to avoid shifting indices, the rule is replaced with nothing, and all other data structures are updated accordingly.

source
HerbGrammar.return_typeMethod
return_type(grammar::AbstractGrammar, rule_index::Int)::Symbol

Returns the type of the production rule at rule_index.

source
HerbGrammar.return_typeMethod
return_type(grammar::AbstractGrammar, node::RuleNode)

Gives the return type or nonterminal symbol in the production rule used by node.

source
HerbGrammar.return_typeMethod
return_type(grammar::AbstractGrammar, hole::UniformHole)

Gives the return type or nonterminal symbol in the production rule used by hole.

source
HerbGrammar.root_node_locMethod

rootnodeloc(root::RuleNode) Returns a NodeLoc pointing to the root node.

source
HerbGrammar.rulenode2exprMethod
rulenode2expr(rulenode::AbstractRuleNode, grammar::AbstractGrammar)

Converts an AbstractRuleNode into a Julia expression corresponding to the rule definitions in the grammar. The returned expression can be evaluated with Julia semantics using eval().

source
HerbGrammar.rulenode_log_probabilityMethod

Calculates the log probability associated with a rulenode in a probabilistic grammar.

source
HerbGrammar.rulesoftypeMethod
rulesoftype(node::RuleNode, grammar::AbstractGrammar, ruletype::Symbol, ignoreNode::RuleNode)

Returns every rule of nonterminal symbol ruletype that is also used in the AbstractRuleNode tree, but not in the ignoreNode subtree.

Warning

The ignoreNode must be a subtree of node for it to have an effect.

source
HerbGrammar.rulesoftypeMethod
rulesoftype(node::RuleNode, grammar::AbstractGrammar, ruletype::Symbol)

Returns every rule of nonterminal symbol ruletype that is also used in the AbstractRuleNode tree.

source
HerbGrammar.rulesoftypeMethod
rulesoftype(node::RuleNode, ruleset::Set{Int}, ignoreNode::RuleNode)

Returns every rule in the ruleset that is also used in the AbstractRuleNode tree, but not in the ignoreNode subtree.

Warning

The ignoreNode must be a subtree of node for it to have an effect.

source
HerbGrammar.rulesonleftMethod
rulesonleft(node::RuleNode, path::Vector{Int})::Set{Int}

Finds all rules that are used in the left subtree defined by the path.

source
HerbGrammar.store_csgFunction
store_csg(g::ContextSensitiveGrammar, grammarpath::AbstractString, constraintspath::OptionalPath=nothing)

Writes a ContextSensitiveGrammar to the files at grammarpath and constraintspath. The grammarpath file will contain a ContextSensitiveGrammar definition, and the constraintspath file will contain the AbstractConstraints of the ContextSensitiveGrammar.

source
HerbGrammar.subsequenceofMethod
subsequenceof(vec1::Vector{Int}, vec2::Vector{Int})

Checks if vec1 is a subsequence of vec2.

source
HerbGrammar.swap_nodeMethod
swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int})

Replace a node in expr, specified by path, with new_expr. Path is a sequence of child indices, starting from the root node.

source
HerbGrammar.swap_nodeMethod
swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode)

Replace child i of a node, a part of larger expr, with new_expr.

source

Index

+)source
HerbGrammar.get_childtypesMethod
get_childtypes(rule::Any, types::AbstractVector{Symbol})

Returns the child types/nonterminals of a production rule.

source
HerbGrammar.get_domainMethod
get_domain(g::AbstractGrammar, type::Symbol)::BitVector

Returns the domain for the hole of a certain type as a BitVector of the same length as the number of rules in the grammar. Bit i is set to true iff rule i is of type type.

Info

Since this function can be intensively used when exploring a program space defined by a grammar, the outcomes of this function are precomputed and stored in the domains field in a AbstractGrammar.

source
HerbGrammar.get_domainMethod
get_domain(g::AbstractGrammar, rules::Vector{Int})::BitVector

Takes a domain rules defined as a vector of ints and converts it to a domain defined as a BitVector.

source
HerbGrammar.get_rulesequenceMethod
get_rulesequence(node::RuleNode, path::Vector{Int})

Extract the derivation sequence from a path (sequence of child indices) and an AbstractRuleNode. If the path is deeper than the deepest node, it returns what it has.

source
HerbGrammar.iscompleteMethod
iscomplete(grammar::AbstractGrammar, node::RuleNode)

Returns true if the expression represented by the RuleNode is a complete expression, meaning that it is fully defined and doesn't have any Holes.

source
HerbGrammar.isevalMethod
iseval(grammar::AbstractGrammar, index::Int)::Bool

Returns true if the production rule at rule_index contains the special _() eval function.

Compat

evaluate immediately functionality is not yet supported by most of Herb.jl

source
HerbGrammar.isevalMethod
iseval(grammar::AbstractGrammar)::Bool

Returns true if any production rules in grammar contain the special _() eval function.

Compat

evaluate immediately functionality is not yet supported by most of Herb.jl

source
HerbGrammar.isevalMethod
iseval(rule)

Returns true if the rule is the special evaluate immediately function, i.e., _()

Compat

evaluate immediately functionality is not yet supported by most of Herb.jl

source
HerbGrammar.isprobabilisticMethod
isprobabilistic(grammar::AbstractGrammar)::Bool

Function returns whether a AbstractGrammar is probabilistic.

source
HerbGrammar.isterminalMethod
isterminal(grammar::AbstractGrammar, node::AbstractRuleNode)::Bool

Returns true if the production rule used by node is terminal, i.e., does not contain any nonterminal symbols.

source
HerbGrammar.isterminalMethod
isterminal(grammar::AbstractGrammar, rule_index::Int)::Bool

Returns true if the production rule at rule_index is terminal, i.e., does not contain any nonterminal symbols.

source
HerbGrammar.isterminalMethod
isterminal(rule::Any, types::AbstractVector{Symbol})

Returns true if the rule is terminal, i.e., it does not contain any of the types in the provided vector. For example, :(x) is terminal, and :(1+1) is terminal, but :(Real + Real) is typically not.

source
HerbGrammar.isvariableMethod
isvariable(grammar::AbstractGrammar, ind::Int, mod::Module)::Bool

Return true if the rule with index ind represents a variable.

Taking into account the symbols defined in the given module(s).

source
HerbGrammar.isvariableMethod
isvariable(grammar::AbstractGrammar, ind::Int)::Bool

Return true if the rule with index ind represents a variable.

source
HerbGrammar.isvariableMethod
isvariable(grammar::AbstractGrammar, node::RuleNode, mod::Module)::Bool

Return true if the rule used by node represents a variable.

Taking into account the symbols defined in the given module(s).

source
HerbGrammar.isvariableMethod
isvariable(grammar::AbstractGrammar, node::RuleNode)::Bool

Return true if the rule used by node represents a variable in a program (essentially, an input to the program)

source
HerbGrammar.log_probabilityMethod
log_probability(grammar::AbstractGrammar, index::Int)::Real

Returns the log probability for the rule at index in the grammar.

Warning

If the grammar is not probabilistic, a warning is displayed, and a uniform probability is assumed.

source
HerbGrammar.max_arityMethod
max_arity(grammar::AbstractGrammar)::Int

Returns the maximum arity (number of children) over all production rules in the AbstractGrammar.

source
HerbGrammar.merge_grammars!Method
merge_grammars!(merge_to::AbstractGrammar, merge_from::AbstractGrammar)

Adds all rules and constraints from merge_from to merge_to.

source
HerbGrammar.mindepthMethod
mindepth(grammar::AbstractGrammar, typ::Symbol, dmap::AbstractVector{Int})

Returns the minimum depth achievable for a given nonterminal symbol. The minimum depth is the depth of the lowest tree that can be made using typ as a start symbol. dmap can be obtained from mindepth_map.

source
HerbGrammar.mindepth_mapMethod
mindepth_map(grammar::AbstractGrammar)

Returns the minimum depth achievable for each production rule in the AbstractGrammar. In other words, this function finds the depths of the lowest trees that can be made using each of the available production rules as a root.

source
HerbGrammar.nchildrenMethod
nchildren(grammar::AbstractGrammar, rule_index::Int)::Int

Returns the number of children (nonterminals) of the production rule at rule_index.

source
HerbGrammar.nchildrenMethod
nchildren(grammar::AbstractGrammar, node::RuleNode)::Int

Returns the number of children in the production rule used by node.

source
HerbGrammar.nonterminalsMethod
nonterminals(grammar::AbstractGrammar)::Vector{Symbol}

Returns a list of the nonterminals or types in the AbstractGrammar.

source
HerbGrammar.normalize!Function

A function for normalizing the probabilities of a probabilistic ContextSensitiveGrammar. If the optional type argument is provided, only the rules of that type are normalized.

source
HerbGrammar.parse_probabilistic_ruleMethod

Parses a single (potentially shorthand) derivation rule of a probabilistic ContextSensitiveGrammar. Returns nothing if the rule is not probabilistic, otherwise a Tuple of its type and a Vector of probability-rule pairs it expands into.

source
HerbGrammar.probabilityMethod
probability(grammar::AbstractGrammar, index::Int)::Real

Return the probability for a rule in the grammar. Use log_probability whenever possible.

Warning

If the grammar is not probabilistic, a warning is displayed, and a uniform probability is assumed.

source
HerbGrammar.read_csgFunction
read_csg(grammarpath::AbstractString, constraintspath::OptionalPath=nothing)::ContextSensitiveGrammar

Reads a ContextSensitiveGrammar from the files at grammarpath and constraintspath.

Danger

Only open trusted grammars. Parts of the grammar can be passed to Julia's eval function.

source
HerbGrammar.read_pcsgFunction
read_pcsg(grammarpath::AbstractString, constraintspath::OptionalPath=nothing)::ContextSensitiveGrammar

Reads a probabilistic ContextSensitiveGrammar from the files at grammarpath and constraintspath.

Danger

Only open trusted grammars. Parts of the grammar can be passed to Julia's eval function.

source
HerbGrammar.remove_rule!Method
remove_rule!(g::AbstractGrammar, idx::Int)

Removes the rule corresponding to idx from the grammar. In order to avoid shifting indices, the rule is replaced with nothing, and all other data structures are updated accordingly.

source
HerbGrammar.return_typeMethod
return_type(grammar::AbstractGrammar, rule_index::Int)::Symbol

Returns the type of the production rule at rule_index.

source
HerbGrammar.return_typeMethod
return_type(grammar::AbstractGrammar, node::RuleNode)

Gives the return type or nonterminal symbol in the production rule used by node.

source
HerbGrammar.return_typeMethod
return_type(grammar::AbstractGrammar, hole::UniformHole)

Gives the return type or nonterminal symbol in the production rule used by hole.

source
HerbGrammar.root_node_locMethod

rootnodeloc(root::RuleNode) Returns a NodeLoc pointing to the root node.

source
HerbGrammar.rulenode2exprMethod
rulenode2expr(rulenode::AbstractRuleNode, grammar::AbstractGrammar)

Converts an AbstractRuleNode into a Julia expression corresponding to the rule definitions in the grammar. The returned expression can be evaluated with Julia semantics using eval().

source
HerbGrammar.rulenode_log_probabilityMethod

Calculates the log probability associated with a rulenode in a probabilistic grammar.

source
HerbGrammar.rulesoftypeMethod
rulesoftype(node::RuleNode, grammar::AbstractGrammar, ruletype::Symbol, ignoreNode::RuleNode)

Returns every rule of nonterminal symbol ruletype that is also used in the AbstractRuleNode tree, but not in the ignoreNode subtree.

Warning

The ignoreNode must be a subtree of node for it to have an effect.

source
HerbGrammar.rulesoftypeMethod
rulesoftype(node::RuleNode, grammar::AbstractGrammar, ruletype::Symbol)

Returns every rule of nonterminal symbol ruletype that is also used in the AbstractRuleNode tree.

source
HerbGrammar.rulesoftypeMethod
rulesoftype(node::RuleNode, ruleset::Set{Int}, ignoreNode::RuleNode)

Returns every rule in the ruleset that is also used in the AbstractRuleNode tree, but not in the ignoreNode subtree.

Warning

The ignoreNode must be a subtree of node for it to have an effect.

source
HerbGrammar.rulesonleftMethod
rulesonleft(node::RuleNode, path::Vector{Int})::Set{Int}

Finds all rules that are used in the left subtree defined by the path.

source
HerbGrammar.store_csgFunction
store_csg(g::ContextSensitiveGrammar, grammarpath::AbstractString, constraintspath::OptionalPath=nothing)

Writes a ContextSensitiveGrammar to the files at grammarpath and constraintspath. The grammarpath file will contain a ContextSensitiveGrammar definition, and the constraintspath file will contain the AbstractConstraints of the ContextSensitiveGrammar.

source
HerbGrammar.subsequenceofMethod
subsequenceof(vec1::Vector{Int}, vec2::Vector{Int})

Checks if vec1 is a subsequence of vec2.

source
HerbGrammar.swap_nodeMethod
swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int})

Replace a node in expr, specified by path, with new_expr. Path is a sequence of child indices, starting from the root node.

source
HerbGrammar.swap_nodeMethod
swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode)

Replace child i of a node, a part of larger expr, with new_expr.

source

Index

diff --git a/previews/PR115/HerbInterpret/index.html b/previews/PR115/HerbInterpret/index.html index 199548c..d9f7ae4 100644 --- a/previews/PR115/HerbInterpret/index.html +++ b/previews/PR115/HerbInterpret/index.html @@ -1,4 +1,4 @@ HerbInterpret.jl · Herb.jl

HerbInterpret.jl Documentation

HerbInterpret.evaluate_programMethod
evaluate_program(program::RuleNode, examples::Vector{<:IOExample}, grammar::AbstractGrammar, evaluation_function::Function)

Runs a program on the examples and returns tuples of actual desired output and the program's output

source
HerbInterpret.execute_on_inputMethod
execute_on_input(grammar::AbstractGrammar, program::RuleNode, input::Dict{Symbol, T})::Any where T

Converts a RuleNode program into an expression using a given grammar, then evaluates this expression with a single input dictionary input and a symbol table derived from the grammar using execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T}).

Arguments

  • grammar::AbstractGrammar: A grammar object used to convert the RuleNode into an executable expression.
  • program::RuleNode: The program, represented as a RuleNode, to be converted and evaluated.
  • input::Dict{Symbol, T}: A dictionary providing input values for symbols used in the generated expression.

Returns

  • Any: The result of evaluating the generated expression with the given input dictionary.
source
HerbInterpret.execute_on_inputMethod
execute_on_input(grammar::AbstractGrammar, program::RuleNode, input::Vector{T})::Vector{Any} where T <: Dict{Symbol, <:Any}

Converts a RuleNode program into an expression using a given grammar, then evaluates this expression for each input dictionary in a vector input and a symbol table derived from the grammar using execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T}).

Arguments

  • grammar::AbstractGrammar: A grammar object used to convert the RuleNode into an executable expression.
  • program::RuleNode: The program, represented as a RuleNode, to be converted and evaluated.
  • input::Vector{T}: A vector of dictionaries, each providing input values for symbols used in the generated expression.

Returns

  • Vector{Any}: A vector containing the results of evaluating the generated expression for each input dictionary.
source
HerbInterpret.execute_on_inputMethod
execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T})::Any where T

Evaluates an expression expr within the context of a symbol table tab and a single input dictionary input. The input dictionary keys should match the symbols used in the expression, and their values are used during the expression's evaluation.

Arguments

  • tab::SymbolTable: A symbol table containing predefined symbols and their associated values or functions.
  • expr::Any: The expression to be evaluated. Can be any Julia expression that is valid within the context of the provided symbol table and input.
  • input::Dict{Symbol, T}: A dictionary where each key is a symbol used in the expression, and the value is the corresponding value to be used in the expression's evaluation. The type T can be any type.

Returns

  • Any: The result of evaluating the expression with the given symbol table and input dictionary.
Warning

This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.

source
HerbInterpret.execute_on_inputMethod
execute_on_input(tab::SymbolTable, expr::Any, input::Vector{T})::Vector{<:Any} where T <: Dict{Symbol, <:Any}

Wrapper around execute_on_input to execute all inputs given as an array.

Arguments

  • tab::SymbolTable: A symbol table containing predefined symbols and their associated values or functions.
  • expr::Any: The expression to be evaluated for each input dictionary.
  • inputs::Vector{T}: A vector of dictionaries, each serving as an individual set of inputs for the expression's evaluation.

Returns

  • Vector{<:Any}: A vector containing the results of evaluating the expression for each input dictionary.
source
HerbInterpret.interpretMethod
interpret(tab::SymbolTable, ex::Expr)

Evaluates an expression without compiling it. Uses AST and symbol lookups. Only supports :call and :(=) expressions at the moment.

Example usage:

tab = SymbolTable(:f => f, :x => x)
 ex = :(f(x))
-interpret(tab, ex)

WARNING: This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.

source
HerbInterpret.test_all_examplesMethod
test_all_examples(tab::SymbolTable, expr::Any, examples::Vector{IOExample})::Vector{Bool}
Warning

This function is deprecated. Please use execute_on_input instead.

Runs the interpreter on all examples with the given input table and expression. The symbol table defines everything (functions, symbols) that are not input variables to the program to be synthesised. Returns a list of true/false values indicating if the expression satisfies the corresponding example. WARNING: This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.

source
HerbInterpret.test_examplesMethod
test_examples(tab::SymbolTable, expr::Any, examples::Vector{IOExample})::Bool
Warning

This function is deprecated. Please use execute_on_input instead.

Evaluates all examples and returns true iff all examples pass. Shortcircuits as soon as an example is found for which the program doesn't work. Returns false if one of the examples produces an error.

source

Index

+interpret(tab, ex)

WARNING: This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.

source
HerbInterpret.test_all_examplesMethod
test_all_examples(tab::SymbolTable, expr::Any, examples::Vector{IOExample})::Vector{Bool}
Warning

This function is deprecated. Please use execute_on_input instead.

Runs the interpreter on all examples with the given input table and expression. The symbol table defines everything (functions, symbols) that are not input variables to the program to be synthesised. Returns a list of true/false values indicating if the expression satisfies the corresponding example. WARNING: This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.

source
HerbInterpret.test_examplesMethod
test_examples(tab::SymbolTable, expr::Any, examples::Vector{IOExample})::Bool
Warning

This function is deprecated. Please use execute_on_input instead.

Evaluates all examples and returns true iff all examples pass. Shortcircuits as soon as an example is found for which the program doesn't work. Returns false if one of the examples produces an error.

source

Index

diff --git a/previews/PR115/HerbSearch/index.html b/previews/PR115/HerbSearch/index.html index 4c30f4d..9077d79 100644 --- a/previews/PR115/HerbSearch/index.html +++ b/previews/PR115/HerbSearch/index.html @@ -1,8 +1,8 @@ -HerbSearch.jl · Herb.jl

HerbSearch.jl Documentation

HerbSearch.BFSIteratorType
@programiterator BFSIterator() <: TopDownIterator

Returns a breadth-first iterator given a grammar and a starting symbol. Returns trees in the grammar in increasing order of size. Inherits all stop-criteria from TopDownIterator.

source
HerbSearch.DFSIteratorType
@programiterator DFSIterator() <: TopDownIterator

Returns a depth-first search enumerator given a grammar and a starting symbol. Returns trees in the grammar in decreasing order of size. Inherits all stop-criteria from TopDownIterator.

source
HerbSearch.ExpandFailureReasonType
@enum ExpandFailureReason limit_reached=1 already_complete=2

Representation of the different reasons why expanding a partial tree failed. Currently, there are two possible causes of the expansion failing:

  • limit_reached: The depth limit or the size limit of the partial tree would be violated by the expansion
  • already_complete: There is no hole left in the tree, so nothing can be expanded.
source
HerbSearch.FixedShapedIteratorType
@programiterator FixedShapedIterator()

Enumerates all programs that extend from the provided fixed shaped tree. The Solver is required to be in a state without any Holes.

!!! warning: this iterator is used as a baseline for the constraint propagation thesis. After the thesis, this iterator can (and should) be deleted.

source
HerbSearch.GeneticSearchIteratorType
GeneticSearchIterator{FitnessFunction,CrossOverFunction,MutationFunction,SelectParentsFunction,EvaluationFunction} <: ProgramIterator

Defines an ProgramIterator using genetic search.

Consists of:

  • examples::Vector{<:IOExample}: a collection of examples defining the specification

  • evaluation_function::EvaluationFunction: interpreter to evaluate the individual programs

  • population_size::Int64: number of inviduals in the population

  • mutation_probability::Float64: probability of mutation for each individual

  • maximum_initial_population_depth::Int64: maximum depth of trees when population is initialized

end

source
HerbSearch.MHSearchIteratorType
MHSearchIterator(examples::AbstractArray{<:IOExample}, cost_function::Function, evaluation_function::Function=HerbInterpret.execute_on_input)

Returns an enumerator that runs according to the Metropolis Hastings algorithm.

  • spec : array of examples
  • 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 randomfillpropose and the accept function is probabilistic. The temperature value of the algorithm remains constant over time.

source
HerbSearch.MLFSIteratorType
@programiterator MLFSIterator() <: TopDownIterator

Iterator that enumerates expressions in the grammar in decreasing order of probability (Only use this iterator with probabilistic grammars). Inherits all stop-criteria from TopDownIterator.

source
HerbSearch.ProgramIteratorType
abstract type ProgramIterator

Generic iterator for all possible search strategies. All iterators are expected to have the following fields:

  • grammar::ContextSensitiveGrammar: the grammar to search over
  • sym::Symbol: defines the start symbol from which the search should be started
  • max_depth::Int: maximum depth of program trees
  • max_size::Int: maximum number of AbstractRuleNodes of program trees
  • max_time::Int: maximum time the iterator may take
  • max_enumerations::Int: maximum number of enumerations
source
HerbSearch.SASearchIteratorType
SASearchIterator(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 Simulated Annealing Search algorithm.

  • spec : array of examples
  • 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.

source
HerbSearch.StochasticSearchIteratorType
abstract type StochasticSearchIterator <: ProgramIterator

A unified abstract type for the algorithms Metropolis Hastings, Very Large Scale Neighbourhood and Simulated Annealing. Each algorithm implements neighbourhood, propose, accept and temperature functions. Below the signatures of each function is shown.

Signatures


Returns a node location from the program that is the neighbourhood. It can also return other information using dict

neighbourhood(iter::T, current_program::RuleNode) where T <: StochasticSearchIterator -> (loc::NodeLocation, dict::Dict)

Proposes a list of programs using the location provided by neighbourhood and the dict.

propose(iter::T, current_program::RuleNode, neighbourhood_node_loc::NodeLoc, dmap::AbstractVector{Int}, dict::Union{Nothing,Dict{String,Any}}) where T <: StochasticSearchIterator -> Iter[RuleNode]

Based on the current program and possible cost and temperature it accepts the program or not. Usually we would always want to accept better programs but we might get stuck if we do so. That is why some implementations of the accept function accept with a probability costs that are worse. cost means how different are the outcomes of the program compared to the correct outcomes. The lower the cost the better the program performs on the examples. The cost is provided by the cost_function

accept(::T, currentcost::Real, nextcost::Real, temperature::Real) where T <: StochasticSearchIterator -> Bool

Returns the new temperature based on the previous temperature. Higher the temperature means that the algorithm will explore more.

temperature(::T, current_temperature::Real) where T <: StochasticSearchIterator -> Real

Returns the cost of the current program. It receives a list of tuples (expected, found) and gives back a cost.

cost_function(outcomes::Tuple{<:Number,<:Number}[]) -> Real

Fields

  • examples::Vector{IOExample} example used to check the program
  • cost_function::Function
  • initial_temperature::Real = 1
  • evaluation_function::Function that evaluates the julia expressions

An iterator over all possible expressions of a grammar up to maxdepth with start symbol sym. Also inherits all stop criteria like `maxdepthfromProgramIterator`.

source
HerbSearch.SynthResultType
@enum SynthResult optimal_program=1 suboptimal_program=2

Representation of the possible results of the synth procedure. At the moment there are two possible outcomes:

  • optimal_program: The synthesized program satisfies the entire program specification.
  • suboptimal_program: The synthesized program does not satisfy the entire program specification, but got the best score from the evaluator.
source
HerbSearch.TopDownIteratorType
mutable struct TopDownIterator <: ProgramIterator

Enumerates a context-free grammar starting at Symbol sym with respect to the grammar up to a given depth and a given size. The exploration is done using the given priority function for derivations, and the expand function for discovered nodes. Concrete iterators may overload the following methods:

  • priority_function
  • derivation_heuristic
  • hole_heuristic
source
HerbSearch.UniformIteratorType
mutable struct UniformIterator

Inner iterator that enumerates all candidate programs of a uniform tree.

  • solver: the uniform solver.
  • outeriter: outer iterator that is responsible for producing uniform trees. This field is used to dispatch on the derivation_heuristic.
  • unvisited_branches: for each search-node from the root to the current search-node, a list of unviisted branches.
  • nsolutions: number of solutions found so far.
source
HerbSearch.VLSNSearchIteratorType
VLSNSearchIterator(spec, cost_function, enumeration_depth = 2, evaluation_function::Function=HerbInterpret.execute_on_input) = StochasticSearchIterator(

Returns an iterator that runs according to the Very Large Scale Neighbourhood Search algorithm.

  • spec : array of examples
  • cost_function : cost function to evaluate the programs proposed
  • vlsn_neighbourhood_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.

source
HerbSearch.@programiteratorMacro
@programiterator

Canonical way of creating a program iterator. The macro automatically declares the expected fields listed in the ProgramIterator documentation. Syntax accepted by the macro is as follows (anything enclosed in square brackets is optional): @programiterator [mutable] <IteratorName>( <arg₁>, ..., <argₙ> ) [<: <SupertypeIterator>] Note that the macro emits an assertion that the SupertypeIterator is a subtype of ProgramIterator which otherwise throws an ArgumentError. If no supertype is given, the new iterator extends ProgramIterator directly. Each <argᵢ> may be (almost) any expression valid in a struct declaration, and they must be comma separated. One known exception is that an inner constructor must always be given using the extended function <name>(...) ... end syntax. The mutable keyword determines whether the declared struct is mutable.

source
Base.collectMethod
function Base.collect(iter::TopDownIterator)

Return an array of all programs in the TopDownIterator.

Warning

This requires deepcopying programs from type StateHole to type RuleNode. If it is not needed to save all programs, iterate over the iterator manually.

source
Base.iterateMethod
Base.iterate(iter::FixedShapedIterator, pq::DataStructures.PriorityQueue)

Describes the iteration for a given TopDownIterator and a PriorityQueue over the grammar without enqueueing new items to the priority queue. Recursively returns the result for the priority queue.

source
Base.iterateMethod
Base.iterate(iter::FixedShapedIterator)

Describes the iteration for a given TopDownIterator over the grammar. The iteration constructs a PriorityQueue first and then prunes it propagating the active constraints. Recursively returns the result for the priority queue.

source
Base.iterateMethod
Base.iterate(iter::GeneticSearchIterator, current_state::GeneticIteratorState)

Iterates the search space using a genetic algorithm. Takes the iterator and the current state to mutate and crossover random inviduals. Returns the best program-so-far and the state of the iterator.

source
Base.iterateMethod
Base.iterate(iter::GeneticSearchIterator)

Iterates the search space using a genetic algorithm. First generates a population sampling random programs. Returns the best program-so-far, and the state of the iterator.

source
Base.iterateMethod
Base.iterate(iter::StochasticSearchIterator, current_state::IteratorState)

The algorithm that constructs the iterator of StochasticSearchIterator. It has the following structure:

  1. get a random node location -> location,dict = neighbourhood(current_program)
  2. call propose on the current program getting a list of full programs
  3. iterate through all the proposals and check if the proposed program is "better" than the previous one
  4. "accept" the new program by calling the accept
  5. return the new next_program
source
Base.iterateMethod
Base.iterate(iter::TopDownIterator, pq::DataStructures.PriorityQueue)

Describes the iteration for a given TopDownIterator and a PriorityQueue over the grammar without enqueueing new items to the priority queue. Recursively returns the result for the priority queue.

source
Base.iterateMethod
Base.iterate(iter::TopDownIterator)

Describes the iteration for a given TopDownIterator over the grammar. The iteration constructs a PriorityQueue first and then prunes it propagating the active constraints. Recursively returns the result for the priority queue.

source
Base.lengthMethod
Base.length(iter::ProgramIterator)

Counts and returns the number of possible programs without storing all the programs. !!! warning: modifies and exhausts the iterator

source
Base.lengthMethod
Base.length(iter::UniformIterator)

Counts and returns the number of programs without storing all the programs. !!! warning: modifies and exhausts the iterator

source
Base.randFunction
rand(::Type{RuleNode}, grammar::AbstractGrammar, max_depth::Int=10)

Generates a random RuleNode of arbitrary type and maximum depth max_depth.

source
Base.randFunction
rand(::Type{RuleNode}, grammar::AbstractGrammar, typ::Symbol, max_depth::Int=10)

Generates a random RuleNode of return type typ and maximum depth max_depth.

source
Base.randFunction
rand(::Type{RuleNode}, grammar::AbstractGrammar, typ::Symbol, dmap::AbstractVector{Int}, max_depth::Int=10)

Generates a random RuleNode, i.e. an expression tree, of root type typ and maximum depth max_depth guided by a depth map dmap if possible.

source
HerbSearch._calculate_costMethod
_calculate_cost(program::RuleNode, cost_function::Function, spec::AbstractVector{IOExample}, grammar::AbstractGrammar, evaluation_function::Function)

Returns the cost of the program using the examples and the cost_function. It first convert the program to an expression and evaluates it on all the examples.

source
HerbSearch._find_next_complete_treeMethod
_find_next_complete_tree(solver::Solver, pq::PriorityQueue, iter::FixedShapedIterator)::Union{Tuple{RuleNode, PriorityQueue}, Nothing}

Takes a priority queue and returns the smallest AST from the grammar it can obtain from the queue or by (repeatedly) expanding trees that are in the queue. Returns nothing if there are no trees left within the depth limit.

source
HerbSearch._find_next_complete_treeMethod
_find_next_complete_tree(solver::Solver, pq::PriorityQueue, iter::TopDownIterator)::Union{Tuple{RuleNode, Tuple{Vector{AbstractRuleNode}, PriorityQueue}}, Nothing}

Takes a priority queue and returns the smallest AST from the grammar it can obtain from the queue or by (repeatedly) expanding trees that are in the queue. Returns nothing if there are no trees left within the depth limit.

source
HerbSearch.best_acceptMethod
best_accept(current_cost::Real, next_cost::Real, temperature::Real)

Returns true if the cost of the proposed program is smaller than the cost of the current program. Otherwise, returns false.

Arguments

  • current_cost::Real: the cost of the current program.
  • next_cost::Real: the cost of the proposed program.
  • temperature::Real: the temperature; not used.
source
HerbSearch.const_temperatureMethod
const_temperature(current_temperature::Real)

Returns the temperature unchanged. This function is used by Metropolis Hastings and Very Large Neighbourhood Search algorithms.

Arguments

  • current_temperature::Real: the current temperature of the search.
source
HerbSearch.constructNeighbourhoodMethod
constructNeighbourhood(current_program::RuleNode, grammar::AbstractGrammar)

The neighbourhood node location is chosen at random. The dictionary is nothing.

Arguments

  • current_program::RuleNode: the current program.
  • grammar::AbstractGrammar: the grammar.
source
HerbSearch.constructNeighbourhoodRuleSubsetMethod
constructNeighbourhoodRuleSubset(current_program::RuleNode, grammar::AbstractGrammar)

The neighbourhood node location is chosen at random. The dictionary is contains one entry with key "rule_subset" and value of type Vector{Any} being a random subset of grammar rules.

Arguments

  • current_program::RuleNode: the current program.
  • grammar::AbstractGrammar: the grammar.
source
HerbSearch.cross_overMethod
cross_over(::GeneticSearchIterator, parent_1::RuleNode, parent_2::RuleNode)

Combines the program from two parent individuals to create one or more offspring individuals.

source
HerbSearch.crossover_swap_children_1Method
crossover_swap_children_1(parent_1::RuleNode, parent_2::RuleNode)

Performs a random crossover of two parents of type RuleNode. The subprograms are swapped and only one altered parent program is returned.

source
HerbSearch.crossover_swap_children_2Method
crossover_swap_children_2(parent_1::RuleNode, parent_2::RuleNode)

Performs a random crossover of two parents of type RuleNode. The subprograms are swapped and both altered parent programs are returned.

source
HerbSearch.decreasing_temperatureMethod
decreasing_temperature(percentage::Real)

Returns a function that produces a temperature decreased by percentage%. This function is used by the Simmulated Annealing algorithm.

Arguments

  • percentage::Real: the percentage to decrease the temperature by.
source
HerbSearch.default_fitnessMethod
default_fitness(program, results)

Defines the default fitness function taking the program and its results. Results are a vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}. As we are looking for individuals with the highest fitness function, the error is inverted.

source
HerbSearch.derivation_heuristicMethod
function derivation_heuristic(::TopDownIterator, indices::Vector{Int})

Returns a sorted sublist of the indices, based on which rules are most promising to fill a hole. By default, this is the identity function.

source
HerbSearch.enumerate_neighbours_proposeMethod
enumerate_neighbours_propose(enumeration_depth::Int64)

The return function is a function that produces a list with all the subprograms with depth at most enumeration_depth.

source
HerbSearch.evaluateMethod
evaluate(problem::Problem{Vector{IOExample}}, expr::Any, tab::SymbolTable; allow_evaluation_errors::Bool=false)

Evaluate the expression on the examples.

Optional parameters:

- `shortcircuit` - Whether to stop evaluating after finding single example fails, to speed up the [synth](@ref) procedure. If true, the returned score is an underapproximation of the actual score.
+HerbSearch.jl · Herb.jl

HerbSearch.jl Documentation

HerbSearch.BFSIteratorType
@programiterator BFSIterator() <: TopDownIterator

Returns a breadth-first iterator given a grammar and a starting symbol. Returns trees in the grammar in increasing order of size. Inherits all stop-criteria from TopDownIterator.

source
HerbSearch.DFSIteratorType
@programiterator DFSIterator() <: TopDownIterator

Returns a depth-first search enumerator given a grammar and a starting symbol. Returns trees in the grammar in decreasing order of size. Inherits all stop-criteria from TopDownIterator.

source
HerbSearch.ExpandFailureReasonType
@enum ExpandFailureReason limit_reached=1 already_complete=2

Representation of the different reasons why expanding a partial tree failed. Currently, there are two possible causes of the expansion failing:

  • limit_reached: The depth limit or the size limit of the partial tree would be violated by the expansion
  • already_complete: There is no hole left in the tree, so nothing can be expanded.
source
HerbSearch.FixedShapedIteratorType
@programiterator FixedShapedIterator()

Enumerates all programs that extend from the provided fixed shaped tree. The Solver is required to be in a state without any Holes.

!!! warning: this iterator is used as a baseline for the constraint propagation thesis. After the thesis, this iterator can (and should) be deleted.

source
HerbSearch.GeneticSearchIteratorType
GeneticSearchIterator{FitnessFunction,CrossOverFunction,MutationFunction,SelectParentsFunction,EvaluationFunction} <: ProgramIterator

Defines an ProgramIterator using genetic search.

Consists of:

  • examples::Vector{<:IOExample}: a collection of examples defining the specification

  • evaluation_function::EvaluationFunction: interpreter to evaluate the individual programs

  • population_size::Int64: number of inviduals in the population

  • mutation_probability::Float64: probability of mutation for each individual

  • maximum_initial_population_depth::Int64: maximum depth of trees when population is initialized

end

source
HerbSearch.MHSearchIteratorType
MHSearchIterator(examples::AbstractArray{<:IOExample}, cost_function::Function, evaluation_function::Function=HerbInterpret.execute_on_input)

Returns an enumerator that runs according to the Metropolis Hastings algorithm.

  • spec : array of examples
  • 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 randomfillpropose and the accept function is probabilistic. The temperature value of the algorithm remains constant over time.

source
HerbSearch.MLFSIteratorType
@programiterator MLFSIterator() <: TopDownIterator

Iterator that enumerates expressions in the grammar in decreasing order of probability (Only use this iterator with probabilistic grammars). Inherits all stop-criteria from TopDownIterator.

source
HerbSearch.ProgramIteratorType
abstract type ProgramIterator

Generic iterator for all possible search strategies. All iterators are expected to have the following fields:

  • grammar::ContextSensitiveGrammar: the grammar to search over
  • sym::Symbol: defines the start symbol from which the search should be started
  • max_depth::Int: maximum depth of program trees
  • max_size::Int: maximum number of AbstractRuleNodes of program trees
  • max_time::Int: maximum time the iterator may take
  • max_enumerations::Int: maximum number of enumerations
source
HerbSearch.SASearchIteratorType
SASearchIterator(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 Simulated Annealing Search algorithm.

  • spec : array of examples
  • 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.

source
HerbSearch.StochasticSearchIteratorType
abstract type StochasticSearchIterator <: ProgramIterator

A unified abstract type for the algorithms Metropolis Hastings, Very Large Scale Neighbourhood and Simulated Annealing. Each algorithm implements neighbourhood, propose, accept and temperature functions. Below the signatures of each function is shown.

Signatures


Returns a node location from the program that is the neighbourhood. It can also return other information using dict

neighbourhood(iter::T, current_program::RuleNode) where T <: StochasticSearchIterator -> (loc::NodeLocation, dict::Dict)

Proposes a list of programs using the location provided by neighbourhood and the dict.

propose(iter::T, current_program::RuleNode, neighbourhood_node_loc::NodeLoc, dmap::AbstractVector{Int}, dict::Union{Nothing,Dict{String,Any}}) where T <: StochasticSearchIterator -> Iter[RuleNode]

Based on the current program and possible cost and temperature it accepts the program or not. Usually we would always want to accept better programs but we might get stuck if we do so. That is why some implementations of the accept function accept with a probability costs that are worse. cost means how different are the outcomes of the program compared to the correct outcomes. The lower the cost the better the program performs on the examples. The cost is provided by the cost_function

accept(::T, currentcost::Real, nextcost::Real, temperature::Real) where T <: StochasticSearchIterator -> Bool

Returns the new temperature based on the previous temperature. Higher the temperature means that the algorithm will explore more.

temperature(::T, current_temperature::Real) where T <: StochasticSearchIterator -> Real

Returns the cost of the current program. It receives a list of tuples (expected, found) and gives back a cost.

cost_function(outcomes::Tuple{<:Number,<:Number}[]) -> Real

Fields

  • examples::Vector{IOExample} example used to check the program
  • cost_function::Function
  • initial_temperature::Real = 1
  • evaluation_function::Function that evaluates the julia expressions

An iterator over all possible expressions of a grammar up to maxdepth with start symbol sym. Also inherits all stop criteria like `maxdepthfromProgramIterator`.

source
HerbSearch.SynthResultType
@enum SynthResult optimal_program=1 suboptimal_program=2

Representation of the possible results of the synth procedure. At the moment there are two possible outcomes:

  • optimal_program: The synthesized program satisfies the entire program specification.
  • suboptimal_program: The synthesized program does not satisfy the entire program specification, but got the best score from the evaluator.
source
HerbSearch.TopDownIteratorType
mutable struct TopDownIterator <: ProgramIterator

Enumerates a context-free grammar starting at Symbol sym with respect to the grammar up to a given depth and a given size. The exploration is done using the given priority function for derivations, and the expand function for discovered nodes. Concrete iterators may overload the following methods:

  • priority_function
  • derivation_heuristic
  • hole_heuristic
source
HerbSearch.UniformIteratorType
mutable struct UniformIterator

Inner iterator that enumerates all candidate programs of a uniform tree.

  • solver: the uniform solver.
  • outeriter: outer iterator that is responsible for producing uniform trees. This field is used to dispatch on the derivation_heuristic.
  • unvisited_branches: for each search-node from the root to the current search-node, a list of unviisted branches.
  • nsolutions: number of solutions found so far.
source
HerbSearch.VLSNSearchIteratorType
VLSNSearchIterator(spec, cost_function, enumeration_depth = 2, evaluation_function::Function=HerbInterpret.execute_on_input) = StochasticSearchIterator(

Returns an iterator that runs according to the Very Large Scale Neighbourhood Search algorithm.

  • spec : array of examples
  • cost_function : cost function to evaluate the programs proposed
  • vlsn_neighbourhood_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.

source
HerbSearch.@programiteratorMacro
@programiterator

Canonical way of creating a program iterator. The macro automatically declares the expected fields listed in the ProgramIterator documentation. Syntax accepted by the macro is as follows (anything enclosed in square brackets is optional): @programiterator [mutable] <IteratorName>( <arg₁>, ..., <argₙ> ) [<: <SupertypeIterator>] Note that the macro emits an assertion that the SupertypeIterator is a subtype of ProgramIterator which otherwise throws an ArgumentError. If no supertype is given, the new iterator extends ProgramIterator directly. Each <argᵢ> may be (almost) any expression valid in a struct declaration, and they must be comma separated. One known exception is that an inner constructor must always be given using the extended function <name>(...) ... end syntax. The mutable keyword determines whether the declared struct is mutable.

source
Base.collectMethod
function Base.collect(iter::TopDownIterator)

Return an array of all programs in the TopDownIterator.

Warning

This requires deepcopying programs from type StateHole to type RuleNode. If it is not needed to save all programs, iterate over the iterator manually.

source
Base.iterateMethod
Base.iterate(iter::FixedShapedIterator, pq::DataStructures.PriorityQueue)

Describes the iteration for a given TopDownIterator and a PriorityQueue over the grammar without enqueueing new items to the priority queue. Recursively returns the result for the priority queue.

source
Base.iterateMethod
Base.iterate(iter::FixedShapedIterator)

Describes the iteration for a given TopDownIterator over the grammar. The iteration constructs a PriorityQueue first and then prunes it propagating the active constraints. Recursively returns the result for the priority queue.

source
Base.iterateMethod
Base.iterate(iter::GeneticSearchIterator, current_state::GeneticIteratorState)

Iterates the search space using a genetic algorithm. Takes the iterator and the current state to mutate and crossover random inviduals. Returns the best program-so-far and the state of the iterator.

source
Base.iterateMethod
Base.iterate(iter::GeneticSearchIterator)

Iterates the search space using a genetic algorithm. First generates a population sampling random programs. Returns the best program-so-far, and the state of the iterator.

source
Base.iterateMethod
Base.iterate(iter::StochasticSearchIterator, current_state::IteratorState)

The algorithm that constructs the iterator of StochasticSearchIterator. It has the following structure:

  1. get a random node location -> location,dict = neighbourhood(current_program)
  2. call propose on the current program getting a list of full programs
  3. iterate through all the proposals and check if the proposed program is "better" than the previous one
  4. "accept" the new program by calling the accept
  5. return the new next_program
source
Base.iterateMethod
Base.iterate(iter::TopDownIterator, pq::DataStructures.PriorityQueue)

Describes the iteration for a given TopDownIterator and a PriorityQueue over the grammar without enqueueing new items to the priority queue. Recursively returns the result for the priority queue.

source
Base.iterateMethod
Base.iterate(iter::TopDownIterator)

Describes the iteration for a given TopDownIterator over the grammar. The iteration constructs a PriorityQueue first and then prunes it propagating the active constraints. Recursively returns the result for the priority queue.

source
Base.lengthMethod
Base.length(iter::ProgramIterator)

Counts and returns the number of possible programs without storing all the programs. !!! warning: modifies and exhausts the iterator

source
Base.lengthMethod
Base.length(iter::UniformIterator)

Counts and returns the number of programs without storing all the programs. !!! warning: modifies and exhausts the iterator

source
Base.randFunction
rand(::Type{RuleNode}, grammar::AbstractGrammar, typ::Symbol, max_depth::Int=10)

Generates a random RuleNode of return type typ and maximum depth max_depth.

source
Base.randFunction
rand(::Type{RuleNode}, grammar::AbstractGrammar, max_depth::Int=10)

Generates a random RuleNode of arbitrary type and maximum depth max_depth.

source
Base.randFunction
rand(::Type{RuleNode}, grammar::AbstractGrammar, typ::Symbol, dmap::AbstractVector{Int}, max_depth::Int=10)

Generates a random RuleNode, i.e. an expression tree, of root type typ and maximum depth max_depth guided by a depth map dmap if possible.

source
HerbSearch._calculate_costMethod
_calculate_cost(program::RuleNode, cost_function::Function, spec::AbstractVector{IOExample}, grammar::AbstractGrammar, evaluation_function::Function)

Returns the cost of the program using the examples and the cost_function. It first convert the program to an expression and evaluates it on all the examples.

source
HerbSearch._find_next_complete_treeMethod
_find_next_complete_tree(solver::Solver, pq::PriorityQueue, iter::FixedShapedIterator)::Union{Tuple{RuleNode, PriorityQueue}, Nothing}

Takes a priority queue and returns the smallest AST from the grammar it can obtain from the queue or by (repeatedly) expanding trees that are in the queue. Returns nothing if there are no trees left within the depth limit.

source
HerbSearch._find_next_complete_treeMethod
_find_next_complete_tree(solver::Solver, pq::PriorityQueue, iter::TopDownIterator)::Union{Tuple{RuleNode, Tuple{Vector{AbstractRuleNode}, PriorityQueue}}, Nothing}

Takes a priority queue and returns the smallest AST from the grammar it can obtain from the queue or by (repeatedly) expanding trees that are in the queue. Returns nothing if there are no trees left within the depth limit.

source
HerbSearch.best_acceptMethod
best_accept(current_cost::Real, next_cost::Real, temperature::Real)

Returns true if the cost of the proposed program is smaller than the cost of the current program. Otherwise, returns false.

Arguments

  • current_cost::Real: the cost of the current program.
  • next_cost::Real: the cost of the proposed program.
  • temperature::Real: the temperature; not used.
source
HerbSearch.const_temperatureMethod
const_temperature(current_temperature::Real)

Returns the temperature unchanged. This function is used by Metropolis Hastings and Very Large Neighbourhood Search algorithms.

Arguments

  • current_temperature::Real: the current temperature of the search.
source
HerbSearch.constructNeighbourhoodMethod
constructNeighbourhood(current_program::RuleNode, grammar::AbstractGrammar)

The neighbourhood node location is chosen at random. The dictionary is nothing.

Arguments

  • current_program::RuleNode: the current program.
  • grammar::AbstractGrammar: the grammar.
source
HerbSearch.constructNeighbourhoodRuleSubsetMethod
constructNeighbourhoodRuleSubset(current_program::RuleNode, grammar::AbstractGrammar)

The neighbourhood node location is chosen at random. The dictionary is contains one entry with key "rule_subset" and value of type Vector{Any} being a random subset of grammar rules.

Arguments

  • current_program::RuleNode: the current program.
  • grammar::AbstractGrammar: the grammar.
source
HerbSearch.cross_overMethod
cross_over(::GeneticSearchIterator, parent_1::RuleNode, parent_2::RuleNode)

Combines the program from two parent individuals to create one or more offspring individuals.

source
HerbSearch.crossover_swap_children_1Method
crossover_swap_children_1(parent_1::RuleNode, parent_2::RuleNode)

Performs a random crossover of two parents of type RuleNode. The subprograms are swapped and only one altered parent program is returned.

source
HerbSearch.crossover_swap_children_2Method
crossover_swap_children_2(parent_1::RuleNode, parent_2::RuleNode)

Performs a random crossover of two parents of type RuleNode. The subprograms are swapped and both altered parent programs are returned.

source
HerbSearch.decreasing_temperatureMethod
decreasing_temperature(percentage::Real)

Returns a function that produces a temperature decreased by percentage%. This function is used by the Simmulated Annealing algorithm.

Arguments

  • percentage::Real: the percentage to decrease the temperature by.
source
HerbSearch.default_fitnessMethod
default_fitness(program, results)

Defines the default fitness function taking the program and its results. Results are a vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}. As we are looking for individuals with the highest fitness function, the error is inverted.

source
HerbSearch.derivation_heuristicMethod
function derivation_heuristic(::TopDownIterator, indices::Vector{Int})

Returns a sorted sublist of the indices, based on which rules are most promising to fill a hole. By default, this is the identity function.

source
HerbSearch.enumerate_neighbours_proposeMethod
enumerate_neighbours_propose(enumeration_depth::Int64)

The return function is a function that produces a list with all the subprograms with depth at most enumeration_depth.

source
HerbSearch.evaluateMethod
evaluate(problem::Problem{Vector{IOExample}}, expr::Any, tab::SymbolTable; allow_evaluation_errors::Bool=false)

Evaluate the expression on the examples.

Optional parameters:

- `shortcircuit` - Whether to stop evaluating after finding single example fails, to speed up the [synth](@ref) procedure. If true, the returned score is an underapproximation of the actual score.
 - `allow_evaluation_errors` - Whether the search should continue if an exception is thrown in the evaluation or throw the error

Returns a score in the interval [0, 1]

source
HerbSearch.extract_name_from_argumentMethod
extract_name_from_argument(ex)

Extracts the name of a field declaration, otherwise throws an ArgumentError. A field declaration is either a simple field name with possible a type attached to it or a keyword argument.

Example

x::Int -> x hello -> hello x = 4 -> x x::Int = 3 -> x

source
HerbSearch.fitnessMethod
fitness(::GeneticSearchIterator, program, results)

Assigns a numerical value (fitness score) to each individual based on how closely it meets the desired objective.

source
HerbSearch.generate_branchesMethod

Returns a vector of disjoint branches to expand the search tree at its current state. Example:

# pseudo code
 Hole(domain=[2, 4, 5], children=[
     Hole(domain=[1, 6]), 
     Hole(domain=[1, 6])
-])

If we split on the first hole, this function will create three branches.

  • (firsthole, 2)
  • (firsthole, 4)
  • (firsthole, 5)
source
HerbSearch.get_best_programMethod
get_best_program(population::Array{RuleNode}, iter::GeneticSearchIterator)::RuleNode

Returns the best program within the population with respect to the fitness function.

source
HerbSearch.heuristic_leftmostMethod
heuristic_leftmost(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over holes, where the left-most hole always gets considered first. Returns a HoleReference once a hole is found. This is the default option for enumerators.

source
HerbSearch.heuristic_randomMethod
heuristic_random(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over holes, where random holes get chosen randomly using random exploration. Returns a HoleReference once a hole is found.

source
HerbSearch.heuristic_rightmostMethod
heuristic_rightmost(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over holes, where the right-most hole always gets considered first. Returns a HoleReference once a hole is found.

source
HerbSearch.heuristic_smallest_domainMethod
heuristic_smallest_domain(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over all available holes in the unfinished AST, by considering the size of their respective domains. A domain here describes the number of possible derivations with respect to the constraints. Returns a HoleReference once a hole is found.

source
HerbSearch.hole_heuristicMethod
hole_heuristic(::FixedShapedIterator, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over fixed shaped holes. Returns a HoleReference once a hole is found.

source
HerbSearch.hole_heuristicMethod
hole_heuristic(::TopDownIterator, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over variable shaped holes. Returns a HoleReference once a hole is found.

source
HerbSearch.is_kwdefMethod
is_kwdeg(ex)

Checks if a field declaration is a keyword argument or not. This is called when filtering if the user arguments to the program iteartor are keyword arguments or not.

source
HerbSearch.mean_squared_errorMethod
mean_squared_error(results::AbstractVector{Tuple{<:Number,<:Number}})

Returns the mean squared error of results.

Arguments

  • results<:AbstractVector{<:Tuple{Number,Number}}: the vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}.
source
HerbSearch.misclassificationMethod
misclassification(results::AbstractVector{Tuple{<:Number,<:Number}})

Returns the amount of misclassified examples, i.e. how many tuples with non-matching entries are there in results.

Arguments

  • results<:AbstractVector{<:Tuple{Number,Number}}: the vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}.
source
HerbSearch.mutate!Function
mutate!(::GeneticSearchIterator, program::RuleNode, grammar::AbstractGrammar, max_depth::Int = 2)

Mutates the program of an invididual.

source
HerbSearch.mutate_random!Function
mutate_random!(program::RuleNode, grammar::AbstractGrammar, max_depth::Int64 = 2)

Mutates the given program by inserting a randomly generated sub-program at a random location.

source
HerbSearch.next_solution!Method
next_solution!(iter::UniformIterator)::Union{RuleNode, StateHole, Nothing}

Searches for the next unvisited solution. Returns nothing if all solutions have been found already.

source
HerbSearch.priority_functionMethod
priority_function(::BFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Assigns priority such that the search tree is traversed like in a BFS manner

source
HerbSearch.priority_functionMethod
priority_function(::DFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Assigns priority such that the search tree is traversed like in a DFS manner

source
HerbSearch.priority_functionMethod
priority_function(::FixedShapedIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}})

Assigns a priority value to a tree that needs to be considered later in the search. Trees with the lowest priority value are considered first.

source
HerbSearch.priority_functionMethod
priority_function(::MLFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Calculates logit for all possible derivations for a node in a tree and returns them.

source
HerbSearch.priority_functionMethod
priority_function(::RandomIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Assigns a random priority to each state.

source
HerbSearch.priority_functionMethod
priority_function(::TopDownIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Assigns a priority value to a tree that needs to be considered later in the search. Trees with the lowest priority value are considered first.

  • ``: The first argument is a dispatch argument and is only used to dispatch to the correct priority function
  • g: The grammar used for enumeration
  • tree: The tree that is about to be stored in the priority queue
  • parent_value: The priority value of the parent SolverState
  • isrequeued: The same tree shape will be requeued. The next time this tree shape is considered, the UniformSolver will produce the next complete program deriving from this shape.
source
HerbSearch.probabilistic_acceptMethod
probabilistic_accept(current_cost::Real, next_cost::Real, temperature::Real)

Probabilistically decides whether to accept the new program (next) based on the ratio of costs (smaller is better) between the previous and new program. Returns True if the new program is accepted, False otherwise.

Arguments

  • current_cost::Real: the cost of the current program.
  • next_cost::Real: the cost of the proposed program.
  • temperature::Real: the temperature; not used.
source
HerbSearch.probabilistic_accept_with_temperatureMethod
probabilistic_accept_with_temperature(current_cost::Real, next_cost::Real, temperature::Real)

Returns true if the cost of the proposed program is smaller than the cost of the current program. Otherwise, returns true with the probability equal to:

\[1 / (1 + exp(delta / temperature))\]

In any other case, returns false.

Arguments

  • current_cost::Real: the cost of the current program.
  • next_cost::Real: the cost of the proposed program.
  • temperature::Real: the temperature of the search.
source
HerbSearch.probabilistic_accept_with_temperature_fractionMethod
probabilistic_accept_with_temperature_fraction(current_cost::Real, program_to_consider_cost::Real, temperature::Real)

Probabilistically decides whether to accept the new program (next) based on the ratio of costs (smaller is better) between the previous and new program multiplied by the temperature. Returns True if the new program is accepted, False otherwise.

Arguments

  • current_cost::Real: the cost of the current program.
  • next_cost::Real: the cost of the proposed program.
  • temperature::Real: the current temperature
source
HerbSearch.processkwarg!Method
processkwarg!(keywords::Vector{Expr}, ex::Union{Expr, Symbol})

Checks if ex has a default value specified, if so it returns only the field declaration, and pushes ex to keywords. Otherwise it returns ex

source
HerbSearch.random_fill_proposeFunction
random_fill_propose(solver::Solver, path::Vector{Int}, dict::Union{Nothing,Dict{String,Any}}, nr_random=5)

Returns a list with only one proposed, completely random, subprogram.

Arguments

  • solver::solver: solver
  • path::Vector{Int}: path to the location to be filled.
  • dict::Dict{String, Any}: the dictionary with additional arguments; not used.
  • nr_random=1 : the number of random subprograms to be generated.
source
HerbSearch.select_chromosomeMethod
select_chromosome(population::Array{RuleNode}, fitness_array::Array{<:Real})::RuleNode

Selects a chromosome (individual) from the population based on a fitness array. The function uses a fitness-proportionate selection strategy, often referred to as "roulette wheel" selection. Assumes fitness_array to be normalized already.

source
HerbSearch.select_fitness_proportional_parentsMethod
select_fitness_proportional_parents(population::Array{RuleNode}, fitness_array::Array{<:Real})::Tuple{RuleNode,RuleNode}

Selects two parent chromosomes (individuals) from a population based on fitness-proportionate selection. The selected parents can be used for genetic crossover in the next steps of the algorithm.

source
HerbSearch.select_parentsMethod
select_parents(::GeneticSearchIterator, population::Array{RuleNode}, fitness_array::Array{<:Real})

Selects two parents for the crossover.

source
HerbSearch.set_stateholes!Method
function set_stateholes!(iter::UniformIterator, node::Union{StateHole, RuleNode})::Vector{StateHole}

Does a dfs to retrieve all unfilled state holes in the program tree and stores them in the stateholes vector.

source
HerbSearch.synthMethod
synth(problem::Problem, iterator::ProgramIterator; shortcircuit::Bool=true, allow_evaluation_errors::Bool=false, mod::Module=Main)::Union{Tuple{RuleNode, SynthResult}, Nothing}

Synthesize a program that satisfies the maximum number of examples in the problem. - problem - The problem definition with IO examples - iterator - The iterator that will be used - shortcircuit - Whether to stop evaluating after finding a single example that fails, to speed up the synth procedure. If true, the returned score is an underapproximation of the actual score. - allowevaluationerrors - Whether the search should crash if an exception is thrown in the evaluation - maxtime - Maximum time that the iterator will run - maxenumerations - Maximum number of iterations that the iterator will run - mod - A module containing definitions for the functions in the grammar that do not exist in Main

Returns a tuple of the rulenode representing the solution program and a synthresult that indicates if that program is optimal. synth uses evaluate which returns a score in the interval [0, 1] and checks whether that score reaches 1. If not it will return the best program so far, with the proper flag

source
StatsBase.sampleFunction
sample(root::RuleNode, typ::Symbol, grammar::AbstractGrammar, maxdepth::Int=typemax(Int))

Uniformly samples a random node from the tree limited to maxdepth.

source
StatsBase.sampleFunction
sample(::Type{NodeLoc}, root::RuleNode, maxdepth::Int=typemax(Int))

Uniformly selects a random node in the tree no deeper than maxdepth using reservoir sampling. Returns a NodeLoc that specifies the location using its parent so that the subtree can be replaced.

source
StatsBase.sampleFunction
sample(root::RuleNode, typ::Symbol, grammar::AbstractGrammar,
-                      maxdepth::Int=typemax(Int))

Uniformly selects a random node of the given return type typ limited by maxdepth.

source
StatsBase.sampleFunction
StatsBase.sample(::Type{NodeLoc}, root::RuleNode, typ::Symbol, grammar::AbstractGrammar, maxdepth::Int=typemax(Int))

Uniformly selects a random node in the tree of a given type, specified using its parent such that the subtree can be replaced. Returns a NodeLoc.

source

The HerbSearch package takes care of all operations related to searching for the desired program. This includes

  • the functionality to sample a certain program given a grammar,
  • the implementation of several heuristic functions,
  • searching for a program that satisfies the specification, and
  • implementations of several search algorithms in terms of how they enumerate the search space
    • Breadth-First Search
    • Depth-First Search
    • Metropolis Hastings
    • Very Large Scale Neighbourhood Search
    • Simulated Annealing
    • Genetic Search

Index

+])

If we split on the first hole, this function will create three branches.

  • (firsthole, 2)
  • (firsthole, 4)
  • (firsthole, 5)
source
HerbSearch.get_best_programMethod
get_best_program(population::Array{RuleNode}, iter::GeneticSearchIterator)::RuleNode

Returns the best program within the population with respect to the fitness function.

source
HerbSearch.heuristic_leftmostMethod
heuristic_leftmost(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over holes, where the left-most hole always gets considered first. Returns a HoleReference once a hole is found. This is the default option for enumerators.

source
HerbSearch.heuristic_randomMethod
heuristic_random(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over holes, where random holes get chosen randomly using random exploration. Returns a HoleReference once a hole is found.

source
HerbSearch.heuristic_rightmostMethod
heuristic_rightmost(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over holes, where the right-most hole always gets considered first. Returns a HoleReference once a hole is found.

source
HerbSearch.heuristic_smallest_domainMethod
heuristic_smallest_domain(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over all available holes in the unfinished AST, by considering the size of their respective domains. A domain here describes the number of possible derivations with respect to the constraints. Returns a HoleReference once a hole is found.

source
HerbSearch.hole_heuristicMethod
hole_heuristic(::FixedShapedIterator, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over fixed shaped holes. Returns a HoleReference once a hole is found.

source
HerbSearch.hole_heuristicMethod
hole_heuristic(::TopDownIterator, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}

Defines a heuristic over variable shaped holes. Returns a HoleReference once a hole is found.

source
HerbSearch.is_kwdefMethod
is_kwdeg(ex)

Checks if a field declaration is a keyword argument or not. This is called when filtering if the user arguments to the program iteartor are keyword arguments or not.

source
HerbSearch.mean_squared_errorMethod
mean_squared_error(results::AbstractVector{Tuple{<:Number,<:Number}})

Returns the mean squared error of results.

Arguments

  • results<:AbstractVector{<:Tuple{Number,Number}}: the vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}.
source
HerbSearch.misclassificationMethod
misclassification(results::AbstractVector{Tuple{<:Number,<:Number}})

Returns the amount of misclassified examples, i.e. how many tuples with non-matching entries are there in results.

Arguments

  • results<:AbstractVector{<:Tuple{Number,Number}}: the vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}.
source
HerbSearch.mutate!Function
mutate!(::GeneticSearchIterator, program::RuleNode, grammar::AbstractGrammar, max_depth::Int = 2)

Mutates the program of an invididual.

source
HerbSearch.mutate_random!Function
mutate_random!(program::RuleNode, grammar::AbstractGrammar, max_depth::Int64 = 2)

Mutates the given program by inserting a randomly generated sub-program at a random location.

source
HerbSearch.next_solution!Method
next_solution!(iter::UniformIterator)::Union{RuleNode, StateHole, Nothing}

Searches for the next unvisited solution. Returns nothing if all solutions have been found already.

source
HerbSearch.priority_functionMethod
priority_function(::BFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Assigns priority such that the search tree is traversed like in a BFS manner

source
HerbSearch.priority_functionMethod
priority_function(::DFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Assigns priority such that the search tree is traversed like in a DFS manner

source
HerbSearch.priority_functionMethod
priority_function(::FixedShapedIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}})

Assigns a priority value to a tree that needs to be considered later in the search. Trees with the lowest priority value are considered first.

source
HerbSearch.priority_functionMethod
priority_function(::MLFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Calculates logit for all possible derivations for a node in a tree and returns them.

source
HerbSearch.priority_functionMethod
priority_function(::RandomIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Assigns a random priority to each state.

source
HerbSearch.priority_functionMethod
priority_function(::TopDownIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)

Assigns a priority value to a tree that needs to be considered later in the search. Trees with the lowest priority value are considered first.

  • ``: The first argument is a dispatch argument and is only used to dispatch to the correct priority function
  • g: The grammar used for enumeration
  • tree: The tree that is about to be stored in the priority queue
  • parent_value: The priority value of the parent SolverState
  • isrequeued: The same tree shape will be requeued. The next time this tree shape is considered, the UniformSolver will produce the next complete program deriving from this shape.
source
HerbSearch.probabilistic_acceptMethod
probabilistic_accept(current_cost::Real, next_cost::Real, temperature::Real)

Probabilistically decides whether to accept the new program (next) based on the ratio of costs (smaller is better) between the previous and new program. Returns True if the new program is accepted, False otherwise.

Arguments

  • current_cost::Real: the cost of the current program.
  • next_cost::Real: the cost of the proposed program.
  • temperature::Real: the temperature; not used.
source
HerbSearch.probabilistic_accept_with_temperatureMethod
probabilistic_accept_with_temperature(current_cost::Real, next_cost::Real, temperature::Real)

Returns true if the cost of the proposed program is smaller than the cost of the current program. Otherwise, returns true with the probability equal to:

\[1 / (1 + exp(delta / temperature))\]

In any other case, returns false.

Arguments

  • current_cost::Real: the cost of the current program.
  • next_cost::Real: the cost of the proposed program.
  • temperature::Real: the temperature of the search.
source
HerbSearch.probabilistic_accept_with_temperature_fractionMethod
probabilistic_accept_with_temperature_fraction(current_cost::Real, program_to_consider_cost::Real, temperature::Real)

Probabilistically decides whether to accept the new program (next) based on the ratio of costs (smaller is better) between the previous and new program multiplied by the temperature. Returns True if the new program is accepted, False otherwise.

Arguments

  • current_cost::Real: the cost of the current program.
  • next_cost::Real: the cost of the proposed program.
  • temperature::Real: the current temperature
source
HerbSearch.processkwarg!Method
processkwarg!(keywords::Vector{Expr}, ex::Union{Expr, Symbol})

Checks if ex has a default value specified, if so it returns only the field declaration, and pushes ex to keywords. Otherwise it returns ex

source
HerbSearch.random_fill_proposeFunction
random_fill_propose(solver::Solver, path::Vector{Int}, dict::Union{Nothing,Dict{String,Any}}, nr_random=5)

Returns a list with only one proposed, completely random, subprogram.

Arguments

  • solver::solver: solver
  • path::Vector{Int}: path to the location to be filled.
  • dict::Dict{String, Any}: the dictionary with additional arguments; not used.
  • nr_random=1 : the number of random subprograms to be generated.
source
HerbSearch.select_chromosomeMethod
select_chromosome(population::Array{RuleNode}, fitness_array::Array{<:Real})::RuleNode

Selects a chromosome (individual) from the population based on a fitness array. The function uses a fitness-proportionate selection strategy, often referred to as "roulette wheel" selection. Assumes fitness_array to be normalized already.

source
HerbSearch.select_fitness_proportional_parentsMethod
select_fitness_proportional_parents(population::Array{RuleNode}, fitness_array::Array{<:Real})::Tuple{RuleNode,RuleNode}

Selects two parent chromosomes (individuals) from a population based on fitness-proportionate selection. The selected parents can be used for genetic crossover in the next steps of the algorithm.

source
HerbSearch.select_parentsMethod
select_parents(::GeneticSearchIterator, population::Array{RuleNode}, fitness_array::Array{<:Real})

Selects two parents for the crossover.

source
HerbSearch.set_stateholes!Method
function set_stateholes!(iter::UniformIterator, node::Union{StateHole, RuleNode})::Vector{StateHole}

Does a dfs to retrieve all unfilled state holes in the program tree and stores them in the stateholes vector.

source
HerbSearch.synthMethod
synth(problem::Problem, iterator::ProgramIterator; shortcircuit::Bool=true, allow_evaluation_errors::Bool=false, mod::Module=Main)::Union{Tuple{RuleNode, SynthResult}, Nothing}

Synthesize a program that satisfies the maximum number of examples in the problem. - problem - The problem definition with IO examples - iterator - The iterator that will be used - shortcircuit - Whether to stop evaluating after finding a single example that fails, to speed up the synth procedure. If true, the returned score is an underapproximation of the actual score. - allowevaluationerrors - Whether the search should crash if an exception is thrown in the evaluation - maxtime - Maximum time that the iterator will run - maxenumerations - Maximum number of iterations that the iterator will run - mod - A module containing definitions for the functions in the grammar that do not exist in Main

Returns a tuple of the rulenode representing the solution program and a synthresult that indicates if that program is optimal. synth uses evaluate which returns a score in the interval [0, 1] and checks whether that score reaches 1. If not it will return the best program so far, with the proper flag

source
StatsBase.sampleFunction
sample(root::RuleNode, typ::Symbol, grammar::AbstractGrammar, maxdepth::Int=typemax(Int))

Uniformly samples a random node from the tree limited to maxdepth.

source
StatsBase.sampleFunction
sample(root::RuleNode, typ::Symbol, grammar::AbstractGrammar,
+                      maxdepth::Int=typemax(Int))

Uniformly selects a random node of the given return type typ limited by maxdepth.

source
StatsBase.sampleFunction
sample(::Type{NodeLoc}, root::RuleNode, maxdepth::Int=typemax(Int))

Uniformly selects a random node in the tree no deeper than maxdepth using reservoir sampling. Returns a NodeLoc that specifies the location using its parent so that the subtree can be replaced.

source
StatsBase.sampleFunction
StatsBase.sample(::Type{NodeLoc}, root::RuleNode, typ::Symbol, grammar::AbstractGrammar, maxdepth::Int=typemax(Int))

Uniformly selects a random node in the tree of a given type, specified using its parent such that the subtree can be replaced. Returns a NodeLoc.

source

The HerbSearch package takes care of all operations related to searching for the desired program. This includes

  • the functionality to sample a certain program given a grammar,
  • the implementation of several heuristic functions,
  • searching for a program that satisfies the specification, and
  • implementations of several search algorithms in terms of how they enumerate the search space
    • Breadth-First Search
    • Depth-First Search
    • Metropolis Hastings
    • Very Large Scale Neighbourhood Search
    • Simulated Annealing
    • Genetic Search

Index

diff --git a/previews/PR115/HerbSpecification/index.html b/previews/PR115/HerbSpecification/index.html index 3dcaf6a..26d7936 100644 --- a/previews/PR115/HerbSpecification/index.html +++ b/previews/PR115/HerbSpecification/index.html @@ -1,2 +1,2 @@ -HerbSpecification.jl · Herb.jl

HerbSpecification.jl Documentation

HerbSpecification.IOExampleType
struct IOExample

An input-output example. in is a Dict of {Symbol,Any} where the symbol represents a variable in a program. out can be anything.

source
HerbSpecification.MetricProblemType
struct MetricProblem{T <: Vector{IOExample}}

Program synthesis problem defined by an specification and a metric. The specification has to be based on input/output examples, while the function needs to return a numerical value.

source
HerbSpecification.ProblemType
struct Problem

Program synthesis problem defined by an AbstractSpecifications. Has a name and a specification of type T.

Warning

Please care that concrete Problem types with different values of T are never subtypes of each other.

source
Base.getindexMethod
Base.getindex(p::Problem{Vector{IOExample}}, indices)

Overwrite Base.getindex to allow for slicing of input/output-based problems.

source

Index

+HerbSpecification.jl · Herb.jl

HerbSpecification.jl Documentation

HerbSpecification.IOExampleType
struct IOExample

An input-output example. in is a Dict of {Symbol,Any} where the symbol represents a variable in a program. out can be anything.

source
HerbSpecification.MetricProblemType
struct MetricProblem{T <: Vector{IOExample}}

Program synthesis problem defined by an specification and a metric. The specification has to be based on input/output examples, while the function needs to return a numerical value.

source
HerbSpecification.ProblemType
struct Problem

Program synthesis problem defined by an AbstractSpecifications. Has a name and a specification of type T.

Warning

Please care that concrete Problem types with different values of T are never subtypes of each other.

source
Base.getindexMethod
Base.getindex(p::Problem{Vector{IOExample}}, indices)

Overwrite Base.getindex to allow for slicing of input/output-based problems.

source

Index

diff --git a/previews/PR115/concepts/index.html b/previews/PR115/concepts/index.html index f3599d7..b6735e9 100644 --- a/previews/PR115/concepts/index.html +++ b/previews/PR115/concepts/index.html @@ -1,2 +1,2 @@ -Architecture and core concepts · Herb.jl
+Architecture and core concepts · Herb.jl
diff --git a/previews/PR115/get_started/index.html b/previews/PR115/get_started/index.html index c55adbe..2c5f49c 100644 --- a/previews/PR115/get_started/index.html +++ b/previews/PR115/get_started/index.html @@ -30,4 +30,4 @@ output = execute_on_input(SymbolTable(g), program, Dict(:x => 6)) println(output) - + diff --git a/previews/PR115/index.html b/previews/PR115/index.html index 4de492a..8f0e975 100644 --- a/previews/PR115/index.html +++ b/previews/PR115/index.html @@ -1,2 +1,2 @@ -Herb.jl · Herb.jl

Herb.jl

A library for defining and efficiently solving program synthesis tasks in Julia.

Why Herb.jl?

When writing research software we almost always investigate highly specific properties or algorithms of our domain, leading to us building the tools from scratch over and over again. The very same holds for the field of program synthesis: Tools are hard to run, benchmarks are hard to get and prepare, and its hard to adapt our existing code to a novel idea.

Herb.jl will take care of this for you and helps you defining, solving and extending your program synthesis problems.

Herb.jl provides...

  • a unified and universal framework for program synthesis
  • Herb.jl allows you to describe all sorts of program synthesis problems using context-free grammars
  • a number of state-of-the-art benchmarks and solvers already implemented and usable out-of-the-box

Herb.jl's sub-packages provide fast and easily extendable implementations of

  • various static and dynamic search strategies,
  • learning search strategies, sampling techniques and more,
  • constraint formulation and propagation,
  • easy grammar formulation and usage,
  • wide-range of usable program interpreters and languages + the possibility to use your own, and
  • efficient data formulation.

Why Julia?

Julia is a perfect fit for program synthesis due to numerous reasons. Starting from scientific reasons like speed of execution and composability over to practical reasons like speed of writing Julia code. For a full ode on why to use Julia, please see the WhyJulia manifesto.

Sub-Modules

Herb's functionality is distributed among several sub-packages:

Basics

Advanced content

+Herb.jl · Herb.jl

Herb.jl

A library for defining and efficiently solving program synthesis tasks in Julia.

Why Herb.jl?

When writing research software we almost always investigate highly specific properties or algorithms of our domain, leading to us building the tools from scratch over and over again. The very same holds for the field of program synthesis: Tools are hard to run, benchmarks are hard to get and prepare, and its hard to adapt our existing code to a novel idea.

Herb.jl will take care of this for you and helps you defining, solving and extending your program synthesis problems.

Herb.jl provides...

  • a unified and universal framework for program synthesis
  • Herb.jl allows you to describe all sorts of program synthesis problems using context-free grammars
  • a number of state-of-the-art benchmarks and solvers already implemented and usable out-of-the-box

Herb.jl's sub-packages provide fast and easily extendable implementations of

  • various static and dynamic search strategies,
  • learning search strategies, sampling techniques and more,
  • constraint formulation and propagation,
  • easy grammar formulation and usage,
  • wide-range of usable program interpreters and languages + the possibility to use your own, and
  • efficient data formulation.

Why Julia?

Julia is a perfect fit for program synthesis due to numerous reasons. Starting from scientific reasons like speed of execution and composability over to practical reasons like speed of writing Julia code. For a full ode on why to use Julia, please see the WhyJulia manifesto.

Sub-Modules

Herb's functionality is distributed among several sub-packages:

Basics

Advanced content

diff --git a/previews/PR115/install/index.html b/previews/PR115/install/index.html index 35877a3..a932de3 100644 --- a/previews/PR115/install/index.html +++ b/previews/PR115/install/index.html @@ -1,4 +1,4 @@ Installation Guide · Herb.jl

Installation Guide

Before installing Herb.jl, ensure that you have a running Julia distribution installed (Julia version 1.7 and above were tested).

Thanks to Julia's package management, installing Herb.jl is very straighforward. Activate the default Julia REPL using

julia

or from within one of your projects using

julia --project=.

From the Julia REPL run

]
 add Herb

or instead running

import Pkg
-Pkg.add("Herb")

which will both install all dependencies automatically.

For later convenience we can also add the respective dependencies to our project, so that we do not have to write Herb.HerbGrammar every time.

] add HerbConstraints HerbCore HerbSpecification HerbInterpret HerbGrammar HerbSearch

And just like this you are done! Welcome to Herb.jl!

+Pkg.add("Herb")

which will both install all dependencies automatically.

For later convenience we can also add the respective dependencies to our project, so that we do not have to write Herb.HerbGrammar every time.

] add HerbConstraints HerbCore HerbSpecification HerbInterpret HerbGrammar HerbSearch

And just like this you are done! Welcome to Herb.jl!

diff --git a/previews/PR115/search_index.js b/previews/PR115/search_index.js index 0213dbd..df97cee 100644 --- a/previews/PR115/search_index.js +++ b/previews/PR115/search_index.js @@ -1,3 +1,3 @@ var documenterSearchIndex = {"docs": -[{"location":"install/#Installation-Guide","page":"Installation Guide","title":"Installation Guide","text":"","category":"section"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"Before installing Herb.jl, ensure that you have a running Julia distribution installed (Julia version 1.7 and above were tested). ","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"Thanks to Julia's package management, installing Herb.jl is very straighforward. Activate the default Julia REPL using","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"julia","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"or from within one of your projects using","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"julia --project=.","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"From the Julia REPL run ","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"]\nadd Herb","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"or instead running","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"import Pkg\nPkg.add(\"Herb\")","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"which will both install all dependencies automatically.","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"For later convenience we can also add the respective dependencies to our project, so that we do not have to write Herb.HerbGrammar every time.","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"] add HerbConstraints HerbCore HerbSpecification HerbInterpret HerbGrammar HerbSearch","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"And just like this you are done! Welcome to Herb.jl!","category":"page"},{"location":"concepts/#Architecture-and-core-concepts","page":"Architecture and core concepts","title":"Architecture and core concepts","text":"","category":"section"},{"location":"HerbSearch/#HerbSearch_docs","page":"HerbSearch.jl","title":"HerbSearch.jl Documentation","text":"","category":"section"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"CurrentModule=HerbSearch","category":"page"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"Modules = [HerbSearch]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbSearch/#HerbSearch.BFSIterator","page":"HerbSearch.jl","title":"HerbSearch.BFSIterator","text":"@programiterator BFSIterator() <: TopDownIterator\n\nReturns a breadth-first iterator given a grammar and a starting symbol. Returns trees in the grammar in increasing order of size. Inherits all stop-criteria from TopDownIterator.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.DFSIterator","page":"HerbSearch.jl","title":"HerbSearch.DFSIterator","text":"@programiterator DFSIterator() <: TopDownIterator\n\nReturns a depth-first search enumerator given a grammar and a starting symbol. Returns trees in the grammar in decreasing order of size. Inherits all stop-criteria from TopDownIterator.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.ExpandFailureReason","page":"HerbSearch.jl","title":"HerbSearch.ExpandFailureReason","text":"@enum ExpandFailureReason limit_reached=1 already_complete=2\n\nRepresentation of the different reasons why expanding a partial tree failed. Currently, there are two possible causes of the expansion failing:\n\nlimit_reached: The depth limit or the size limit of the partial tree would be violated by the expansion\nalready_complete: There is no hole left in the tree, so nothing can be expanded.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.FixedShapedIterator","page":"HerbSearch.jl","title":"HerbSearch.FixedShapedIterator","text":"@programiterator FixedShapedIterator()\n\nEnumerates all programs that extend from the provided fixed shaped tree. The Solver is required to be in a state without any Holes.\n\n!!! warning: this iterator is used as a baseline for the constraint propagation thesis. After the thesis, this iterator can (and should) be deleted.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.GeneticSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.GeneticSearchIterator","text":"GeneticSearchIterator{FitnessFunction,CrossOverFunction,MutationFunction,SelectParentsFunction,EvaluationFunction} <: ProgramIterator\n\nDefines an ProgramIterator using genetic search. \n\nConsists of:\n\nexamples::Vector{<:IOExample}: a collection of examples defining the specification \nevaluation_function::EvaluationFunction: interpreter to evaluate the individual programs\npopulation_size::Int64: number of inviduals in the population\nmutation_probability::Float64: probability of mutation for each individual\nmaximum_initial_population_depth::Int64: maximum depth of trees when population is initialized \n\nend\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.MHSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.MHSearchIterator","text":"MHSearchIterator(examples::AbstractArray{<:IOExample}, cost_function::Function, evaluation_function::Function=HerbInterpret.execute_on_input)\n\nReturns an enumerator that runs according to the Metropolis Hastings algorithm.\n\nspec : array of examples\ncost_function : cost function to evaluate the programs proposed\nevaluation_function : evaluation function that evaluates the program generated and produces an output\n\nThe propose function is randomfillpropose and the accept function is probabilistic. The temperature value of the algorithm remains constant over time.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.MLFSIterator","page":"HerbSearch.jl","title":"HerbSearch.MLFSIterator","text":"@programiterator MLFSIterator() <: TopDownIterator\n\nIterator that enumerates expressions in the grammar in decreasing order of probability (Only use this iterator with probabilistic grammars). Inherits all stop-criteria from TopDownIterator.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.ProgramIterator","page":"HerbSearch.jl","title":"HerbSearch.ProgramIterator","text":"abstract type ProgramIterator\n\nGeneric iterator for all possible search strategies. All iterators are expected to have the following fields:\n\ngrammar::ContextSensitiveGrammar: the grammar to search over\nsym::Symbol: defines the start symbol from which the search should be started \nmax_depth::Int: maximum depth of program trees\nmax_size::Int: maximum number of AbstractRuleNodes of program trees\nmax_time::Int: maximum time the iterator may take\nmax_enumerations::Int: maximum number of enumerations\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.RandomIterator","page":"HerbSearch.jl","title":"HerbSearch.RandomIterator","text":"@programiterator RandomIterator() <: TopDownIterator\n\nIterates trees in the grammar in a random order.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.SASearchIterator","page":"HerbSearch.jl","title":"HerbSearch.SASearchIterator","text":"SASearchIterator(spec, cost_function, initial_temperature=1, temperature_decreasing_factor = 0.99, evaluation_function::Function=HerbInterpret.execute_on_input)\n\nReturns an enumerator that runs according to the Simulated Annealing Search algorithm.\n\nspec : array of examples\ncost_function : cost function to evaluate the programs proposed\ninitial_temperature : the starting temperature of the algorithm\ntemperature_decreasing_factor : the decreasing factor of the temperature of the time\nevaluation_function : evaluation function that evaluates the program generated and produces an output\n\nThe propose function is random_fill_propose (the same as for Metropolis Hastings). The accept function is probabilistic but takes into account the tempeerature too.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.StochasticSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.StochasticSearchIterator","text":"abstract type StochasticSearchIterator <: ProgramIterator\n\nA unified abstract type for the algorithms Metropolis Hastings, Very Large Scale Neighbourhood and Simulated Annealing. Each algorithm implements neighbourhood, propose, accept and temperature functions. Below the signatures of each function is shown.\n\nSignatures\n\n\n\nReturns a node location from the program that is the neighbourhood. It can also return other information using dict\n\nneighbourhood(iter::T, current_program::RuleNode) where T <: StochasticSearchIterator -> (loc::NodeLocation, dict::Dict)\n\nProposes a list of programs using the location provided by neighbourhood and the dict.\n\npropose(iter::T, current_program::RuleNode, neighbourhood_node_loc::NodeLoc, dmap::AbstractVector{Int}, dict::Union{Nothing,Dict{String,Any}}) where T <: StochasticSearchIterator -> Iter[RuleNode]\n\n\n\nBased on the current program and possible cost and temperature it accepts the program or not. Usually we would always want to accept better programs but we might get stuck if we do so. That is why some implementations of the accept function accept with a probability costs that are worse. cost means how different are the outcomes of the program compared to the correct outcomes. The lower the cost the better the program performs on the examples. The cost is provided by the cost_function\n\naccept(::T, currentcost::Real, nextcost::Real, temperature::Real) where T <: StochasticSearchIterator -> Bool\n\nReturns the new temperature based on the previous temperature. Higher the temperature means that the algorithm will explore more.\n\ntemperature(::T, current_temperature::Real) where T <: StochasticSearchIterator -> Real\n\nReturns the cost of the current program. It receives a list of tuples (expected, found) and gives back a cost.\n\ncost_function(outcomes::Tuple{<:Number,<:Number}[]) -> Real\n\n\n\nFields\n\nexamples::Vector{IOExample} example used to check the program\ncost_function::Function\ninitial_temperature::Real = 1 \nevaluation_function::Function that evaluates the julia expressions\n\nAn iterator over all possible expressions of a grammar up to maxdepth with start symbol sym. Also inherits all stop criteria like `maxdepthfromProgramIterator`.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.SynthResult","page":"HerbSearch.jl","title":"HerbSearch.SynthResult","text":"@enum SynthResult optimal_program=1 suboptimal_program=2\n\nRepresentation of the possible results of the synth procedure. At the moment there are two possible outcomes:\n\noptimal_program: The synthesized program satisfies the entire program specification.\nsuboptimal_program: The synthesized program does not satisfy the entire program specification, but got the best score from the evaluator.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.TopDownIterator","page":"HerbSearch.jl","title":"HerbSearch.TopDownIterator","text":"mutable struct TopDownIterator <: ProgramIterator\n\nEnumerates a context-free grammar starting at Symbol sym with respect to the grammar up to a given depth and a given size. The exploration is done using the given priority function for derivations, and the expand function for discovered nodes. Concrete iterators may overload the following methods:\n\npriority_function\nderivation_heuristic\nhole_heuristic\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.UniformIterator","page":"HerbSearch.jl","title":"HerbSearch.UniformIterator","text":"mutable struct UniformIterator\n\nInner iterator that enumerates all candidate programs of a uniform tree.\n\nsolver: the uniform solver.\nouteriter: outer iterator that is responsible for producing uniform trees. This field is used to dispatch on the derivation_heuristic.\nunvisited_branches: for each search-node from the root to the current search-node, a list of unviisted branches.\nnsolutions: number of solutions found so far.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.UniformIterator-Tuple{UniformSolver, Union{Nothing, ProgramIterator}}","page":"HerbSearch.jl","title":"HerbSearch.UniformIterator","text":"UniformIterator(solver::UniformSolver, outeriter::ProgramIterator)\n\nConstructs a new UniformIterator that traverses solutions of the UniformSolver and is an inner iterator of an outer ProgramIterator.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.VLSNSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.VLSNSearchIterator","text":"VLSNSearchIterator(spec, cost_function, enumeration_depth = 2, evaluation_function::Function=HerbInterpret.execute_on_input) = StochasticSearchIterator(\n\nReturns an iterator that runs according to the Very Large Scale Neighbourhood Search algorithm.\n\nspec : array of examples\ncost_function : cost function to evaluate the programs proposed\nvlsn_neighbourhood_depth : the enumeration depth to search for a best program at a time\nevaluation_function : evaluation function that evaluates the program generated and produces an output\n\nThe 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.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.@programiterator-Tuple{Any, Any}","page":"HerbSearch.jl","title":"HerbSearch.@programiterator","text":"@programiterator\n\nCanonical way of creating a program iterator. The macro automatically declares the expected fields listed in the ProgramIterator documentation. Syntax accepted by the macro is as follows (anything enclosed in square brackets is optional): @programiterator [mutable] ( , ..., ) [<: ] Note that the macro emits an assertion that the SupertypeIterator is a subtype of ProgramIterator which otherwise throws an ArgumentError. If no supertype is given, the new iterator extends ProgramIterator directly. Each may be (almost) any expression valid in a struct declaration, and they must be comma separated. One known exception is that an inner constructor must always be given using the extended function (...) ... end syntax. The mutable keyword determines whether the declared struct is mutable.\n\n\n\n\n\n","category":"macro"},{"location":"HerbSearch/#Base.collect-Tuple{TopDownIterator}","page":"HerbSearch.jl","title":"Base.collect","text":"function Base.collect(iter::TopDownIterator)\n\nReturn an array of all programs in the TopDownIterator. \n\nwarning: Warning\nThis requires deepcopying programs from type StateHole to type RuleNode. If it is not needed to save all programs, iterate over the iterator manually.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{FixedShapedIterator, DataStructures.PriorityQueue}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::FixedShapedIterator, pq::DataStructures.PriorityQueue)\n\nDescribes the iteration for a given TopDownIterator and a PriorityQueue over the grammar without enqueueing new items to the priority queue. Recursively returns the result for the priority queue.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{FixedShapedIterator}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::FixedShapedIterator)\n\nDescribes the iteration for a given TopDownIterator over the grammar. The iteration constructs a PriorityQueue first and then prunes it propagating the active constraints. Recursively returns the result for the priority queue.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{GeneticSearchIterator, HerbSearch.GeneticIteratorState}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::GeneticSearchIterator, current_state::GeneticIteratorState)\n\nIterates the search space using a genetic algorithm. Takes the iterator and the current state to mutate and crossover random inviduals. Returns the best program-so-far and the state of the iterator.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{GeneticSearchIterator}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::GeneticSearchIterator)\n\nIterates the search space using a genetic algorithm. First generates a population sampling random programs. Returns the best program-so-far, and the state of the iterator.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{HerbSearch.StochasticSearchIterator, HerbSearch.IteratorState}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::StochasticSearchIterator, current_state::IteratorState)\n\nThe algorithm that constructs the iterator of StochasticSearchIterator. It has the following structure:\n\nget a random node location -> location,dict = neighbourhood(current_program)\ncall propose on the current program getting a list of full programs\niterate through all the proposals and check if the proposed program is \"better\" than the previous one\n\"accept\" the new program by calling the accept\nreturn the new next_program\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{TopDownIterator, Tuple{Vector{<:AbstractRuleNode}, DataStructures.PriorityQueue}}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::TopDownIterator, pq::DataStructures.PriorityQueue)\n\nDescribes the iteration for a given TopDownIterator and a PriorityQueue over the grammar without enqueueing new items to the priority queue. Recursively returns the result for the priority queue.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{TopDownIterator}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::TopDownIterator)\n\nDescribes the iteration for a given TopDownIterator over the grammar. The iteration constructs a PriorityQueue first and then prunes it propagating the active constraints. Recursively returns the result for the priority queue.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.length-Tuple{ProgramIterator}","page":"HerbSearch.jl","title":"Base.length","text":"Base.length(iter::ProgramIterator)\n\nCounts and returns the number of possible programs without storing all the programs. !!! warning: modifies and exhausts the iterator\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.length-Tuple{UniformIterator}","page":"HerbSearch.jl","title":"Base.length","text":"Base.length(iter::UniformIterator)\n\nCounts and returns the number of programs without storing all the programs. !!! warning: modifies and exhausts the iterator\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.rand","page":"HerbSearch.jl","title":"Base.rand","text":"rand(::Type{RuleNode}, grammar::AbstractGrammar, max_depth::Int=10)\n\nGenerates a random RuleNode of arbitrary type and maximum depth max_depth.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#Base.rand-2","page":"HerbSearch.jl","title":"Base.rand","text":"rand(::Type{RuleNode}, grammar::AbstractGrammar, typ::Symbol, max_depth::Int=10)\n\nGenerates a random RuleNode of return type typ and maximum depth max_depth.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#Base.rand-3","page":"HerbSearch.jl","title":"Base.rand","text":"rand(::Type{RuleNode}, grammar::AbstractGrammar, typ::Symbol, dmap::AbstractVector{Int}, max_depth::Int=10)\n\nGenerates a random RuleNode, i.e. an expression tree, of root type typ and maximum depth max_depth guided by a depth map dmap if possible.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#HerbSearch._calculate_cost-Tuple{Union{StateHole, RuleNode}, Function, AbstractVector{IOExample}, AbstractGrammar, Function}","page":"HerbSearch.jl","title":"HerbSearch._calculate_cost","text":"_calculate_cost(program::RuleNode, cost_function::Function, spec::AbstractVector{IOExample}, grammar::AbstractGrammar, evaluation_function::Function)\n\nReturns the cost of the program using the examples and the cost_function. It first convert the program to an expression and evaluates it on all the examples.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch._find_next_complete_tree-Tuple{Solver, DataStructures.PriorityQueue, FixedShapedIterator}","page":"HerbSearch.jl","title":"HerbSearch._find_next_complete_tree","text":"_find_next_complete_tree(solver::Solver, pq::PriorityQueue, iter::FixedShapedIterator)::Union{Tuple{RuleNode, PriorityQueue}, Nothing}\n\nTakes a priority queue and returns the smallest AST from the grammar it can obtain from the queue or by (repeatedly) expanding trees that are in the queue. Returns nothing if there are no trees left within the depth limit.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch._find_next_complete_tree-Tuple{Solver, DataStructures.PriorityQueue, TopDownIterator}","page":"HerbSearch.jl","title":"HerbSearch._find_next_complete_tree","text":"_find_next_complete_tree(solver::Solver, pq::PriorityQueue, iter::TopDownIterator)::Union{Tuple{RuleNode, Tuple{Vector{AbstractRuleNode}, PriorityQueue}}, Nothing}\n\nTakes a priority queue and returns the smallest AST from the grammar it can obtain from the queue or by (repeatedly) expanding trees that are in the queue. Returns nothing if there are no trees left within the depth limit.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.best_accept-Tuple{Real, Real, Real}","page":"HerbSearch.jl","title":"HerbSearch.best_accept","text":"best_accept(current_cost::Real, next_cost::Real, temperature::Real)\n\nReturns true if the cost of the proposed program is smaller than the cost of the current program. Otherwise, returns false.\n\nArguments\n\ncurrent_cost::Real: the cost of the current program.\nnext_cost::Real: the cost of the proposed program.\ntemperature::Real: the temperature; not used.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.calculate_cost-Union{Tuple{T}, Tuple{T, Union{StateHole, RuleNode}}} where T<:HerbSearch.StochasticSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.calculate_cost","text":"calculate_cost(iter::T, program::Union{RuleNode, StateHole}) where T <: StochasticSearchIterator\n\nWrapper around _calculate_cost.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.const_temperature-Tuple{Real}","page":"HerbSearch.jl","title":"HerbSearch.const_temperature","text":"const_temperature(current_temperature::Real)\n\nReturns the temperature unchanged. This function is used by Metropolis Hastings and Very Large Neighbourhood Search algorithms.\n\nArguments\n\ncurrent_temperature::Real: the current temperature of the search.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.constructNeighbourhood-Tuple{RuleNode, AbstractGrammar}","page":"HerbSearch.jl","title":"HerbSearch.constructNeighbourhood","text":"constructNeighbourhood(current_program::RuleNode, grammar::AbstractGrammar)\n\nThe neighbourhood node location is chosen at random. The dictionary is nothing.\n\nArguments\n\ncurrent_program::RuleNode: the current program.\ngrammar::AbstractGrammar: the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.constructNeighbourhoodRuleSubset-Tuple{RuleNode, AbstractGrammar}","page":"HerbSearch.jl","title":"HerbSearch.constructNeighbourhoodRuleSubset","text":"constructNeighbourhoodRuleSubset(current_program::RuleNode, grammar::AbstractGrammar)\n\nThe neighbourhood node location is chosen at random. The dictionary is contains one entry with key \"rule_subset\" and value of type Vector{Any} being a random subset of grammar rules.\n\nArguments\n\ncurrent_program::RuleNode: the current program.\ngrammar::AbstractGrammar: the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.cross_over-Tuple{GeneticSearchIterator, RuleNode, RuleNode}","page":"HerbSearch.jl","title":"HerbSearch.cross_over","text":"cross_over(::GeneticSearchIterator, parent_1::RuleNode, parent_2::RuleNode)\n\nCombines the program from two parent individuals to create one or more offspring individuals.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.crossover_swap_children_1-Tuple{RuleNode, RuleNode}","page":"HerbSearch.jl","title":"HerbSearch.crossover_swap_children_1","text":"crossover_swap_children_1(parent_1::RuleNode, parent_2::RuleNode)\n\nPerforms a random crossover of two parents of type RuleNode. The subprograms are swapped and only one altered parent program is returned.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.crossover_swap_children_2-Tuple{RuleNode, RuleNode}","page":"HerbSearch.jl","title":"HerbSearch.crossover_swap_children_2","text":"crossover_swap_children_2(parent_1::RuleNode, parent_2::RuleNode)\n\nPerforms a random crossover of two parents of type RuleNode. The subprograms are swapped and both altered parent programs are returned.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.decreasing_temperature-Tuple{Real}","page":"HerbSearch.jl","title":"HerbSearch.decreasing_temperature","text":"decreasing_temperature(percentage::Real)\n\nReturns a function that produces a temperature decreased by percentage%. This function is used by the Simmulated Annealing algorithm.\n\nArguments\n\npercentage::Real: the percentage to decrease the temperature by.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.default_fitness-Tuple{Any, Any}","page":"HerbSearch.jl","title":"HerbSearch.default_fitness","text":"default_fitness(program, results)\n\nDefines the default fitness function taking the program and its results. Results are a vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}. As we are looking for individuals with the highest fitness function, the error is inverted. \n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.derivation_heuristic-Tuple{RandomIterator, Vector{Int64}}","page":"HerbSearch.jl","title":"HerbSearch.derivation_heuristic","text":"function derivation_heuristic(::RandomIterator, indices::Vector{Int})\n\nRandomly shuffles the rules.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.derivation_heuristic-Tuple{TopDownIterator, Vector{Int64}}","page":"HerbSearch.jl","title":"HerbSearch.derivation_heuristic","text":"function derivation_heuristic(::TopDownIterator, indices::Vector{Int})\n\nReturns a sorted sublist of the indices, based on which rules are most promising to fill a hole. By default, this is the identity function.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.enumerate_neighbours_propose-Tuple{Int64}","page":"HerbSearch.jl","title":"HerbSearch.enumerate_neighbours_propose","text":"enumerate_neighbours_propose(enumeration_depth::Int64)\n\nThe return function is a function that produces a list with all the subprograms with depth at most enumeration_depth.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.evaluate-Tuple{Problem{Vector{IOExample}}, Any, Dict{Symbol, Any}}","page":"HerbSearch.jl","title":"HerbSearch.evaluate","text":"evaluate(problem::Problem{Vector{IOExample}}, expr::Any, tab::SymbolTable; allow_evaluation_errors::Bool=false)\n\nEvaluate the expression on the examples.\n\nOptional parameters:\n\n- `shortcircuit` - Whether to stop evaluating after finding single example fails, to speed up the [synth](@ref) procedure. If true, the returned score is an underapproximation of the actual score.\n- `allow_evaluation_errors` - Whether the search should continue if an exception is thrown in the evaluation or throw the error\n\nReturns a score in the interval [0, 1]\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.extract_name_from_argument-Tuple{Any}","page":"HerbSearch.jl","title":"HerbSearch.extract_name_from_argument","text":"extract_name_from_argument(ex)\n\nExtracts the name of a field declaration, otherwise throws an ArgumentError. A field declaration is either a simple field name with possible a type attached to it or a keyword argument.\n\nExample\n\nx::Int -> x hello -> hello x = 4 -> x x::Int = 3 -> x\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.fitness-Tuple{GeneticSearchIterator, RuleNode, AbstractVector{<:Tuple{Any, Any}}}","page":"HerbSearch.jl","title":"HerbSearch.fitness","text":"fitness(::GeneticSearchIterator, program, results)\n\nAssigns a numerical value (fitness score) to each individual based on how closely it meets the desired objective.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.generate_branches-Tuple{UniformIterator}","page":"HerbSearch.jl","title":"HerbSearch.generate_branches","text":"Returns a vector of disjoint branches to expand the search tree at its current state. Example:\n\n# pseudo code\nHole(domain=[2, 4, 5], children=[\n Hole(domain=[1, 6]), \n Hole(domain=[1, 6])\n])\n\nIf we split on the first hole, this function will create three branches.\n\n(firsthole, 2)\n(firsthole, 4)\n(firsthole, 5)\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.get_best_program-Tuple{Array{RuleNode}, GeneticSearchIterator}","page":"HerbSearch.jl","title":"HerbSearch.get_best_program","text":"get_best_program(population::Array{RuleNode}, iter::GeneticSearchIterator)::RuleNode\n\nReturns the best program within the population with respect to the fitness function.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_leftmost-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_leftmost","text":"heuristic_leftmost(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over holes, where the left-most hole always gets considered first. Returns a HoleReference once a hole is found. This is the default option for enumerators.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_leftmost_fixed_shaped_hole-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_leftmost_fixed_shaped_hole","text":"heuristic_leftmost_fixed_shaped_hole(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over FixedShapeHoles, where the left-most hole always gets considered first. Returns a HoleReference once a hole is found. This is the default option for enumerators.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_random-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_random","text":"heuristic_random(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over holes, where random holes get chosen randomly using random exploration. Returns a HoleReference once a hole is found.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_rightmost-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_rightmost","text":"heuristic_rightmost(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over holes, where the right-most hole always gets considered first. Returns a HoleReference once a hole is found. \n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_smallest_domain-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_smallest_domain","text":"heuristic_smallest_domain(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over all available holes in the unfinished AST, by considering the size of their respective domains. A domain here describes the number of possible derivations with respect to the constraints. Returns a HoleReference once a hole is found. \n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.hole_heuristic-Tuple{FixedShapedIterator, AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.hole_heuristic","text":"hole_heuristic(::FixedShapedIterator, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over fixed shaped holes. Returns a HoleReference once a hole is found.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.hole_heuristic-Tuple{TopDownIterator, AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.hole_heuristic","text":"hole_heuristic(::TopDownIterator, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over variable shaped holes. Returns a HoleReference once a hole is found.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.is_field_decl-Tuple{Any}","page":"HerbSearch.jl","title":"HerbSearch.is_field_decl","text":"is_field_decl(ex)\n\nCheck if extractname(ex) returns a name.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.is_kwdef-Tuple{Any}","page":"HerbSearch.jl","title":"HerbSearch.is_kwdef","text":"is_kwdeg(ex)\n\nChecks if a field declaration is a keyword argument or not. This is called when filtering if the user arguments to the program iteartor are keyword arguments or not.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.mean_squared_error-Tuple{AbstractVector{<:Tuple{Number, Number}}}","page":"HerbSearch.jl","title":"HerbSearch.mean_squared_error","text":"mean_squared_error(results::AbstractVector{Tuple{<:Number,<:Number}})\n\nReturns the mean squared error of results.\n\nArguments\n\nresults<:AbstractVector{<:Tuple{Number,Number}}: the vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.misclassification-Tuple{T} where T<:(AbstractVector{<:Tuple{Number, Number}})","page":"HerbSearch.jl","title":"HerbSearch.misclassification","text":"misclassification(results::AbstractVector{Tuple{<:Number,<:Number}})\n\nReturns the amount of misclassified examples, i.e. how many tuples with non-matching entries are there in results.\n\nArguments\n\nresults<:AbstractVector{<:Tuple{Number,Number}}: the vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.mutate!","page":"HerbSearch.jl","title":"HerbSearch.mutate!","text":"mutate!(::GeneticSearchIterator, program::RuleNode, grammar::AbstractGrammar, max_depth::Int = 2)\n\nMutates the program of an invididual.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#HerbSearch.mutate_random!","page":"HerbSearch.jl","title":"HerbSearch.mutate_random!","text":"mutate_random!(program::RuleNode, grammar::AbstractGrammar, max_depth::Int64 = 2)\n\nMutates the given program by inserting a randomly generated sub-program at a random location.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#HerbSearch.next_solution!-Tuple{UniformIterator}","page":"HerbSearch.jl","title":"HerbSearch.next_solution!","text":"next_solution!(iter::UniformIterator)::Union{RuleNode, StateHole, Nothing}\n\nSearches for the next unvisited solution. Returns nothing if all solutions have been found already.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{BFSIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::BFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nAssigns priority such that the search tree is traversed like in a BFS manner\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{DFSIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::DFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nAssigns priority such that the search tree is traversed like in a DFS manner\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{FixedShapedIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::FixedShapedIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}})\n\nAssigns a priority value to a tree that needs to be considered later in the search. Trees with the lowest priority value are considered first.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{MLFSIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::MLFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nCalculates logit for all possible derivations for a node in a tree and returns them.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{RandomIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::RandomIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nAssigns a random priority to each state.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{TopDownIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::TopDownIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nAssigns a priority value to a tree that needs to be considered later in the search. Trees with the lowest priority value are considered first.\n\n``: The first argument is a dispatch argument and is only used to dispatch to the correct priority function\ng: The grammar used for enumeration\ntree: The tree that is about to be stored in the priority queue\nparent_value: The priority value of the parent SolverState\nisrequeued: The same tree shape will be requeued. The next time this tree shape is considered, the UniformSolver will produce the next complete program deriving from this shape.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.probabilistic_accept-Tuple{Real, Real, Real}","page":"HerbSearch.jl","title":"HerbSearch.probabilistic_accept","text":"probabilistic_accept(current_cost::Real, next_cost::Real, temperature::Real)\n\nProbabilistically decides whether to accept the new program (next) based on the ratio of costs (smaller is better) between the previous and new program. Returns True if the new program is accepted, False otherwise.\n\nArguments\n\ncurrent_cost::Real: the cost of the current program.\nnext_cost::Real: the cost of the proposed program.\ntemperature::Real: the temperature; not used.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.probabilistic_accept_with_temperature-Tuple{Real, Real, Real}","page":"HerbSearch.jl","title":"HerbSearch.probabilistic_accept_with_temperature","text":"probabilistic_accept_with_temperature(current_cost::Real, next_cost::Real, temperature::Real)\n\nReturns true if the cost of the proposed program is smaller than the cost of the current program. Otherwise, returns true with the probability equal to: \n\n1 (1 + exp(delta temperature))\n\nIn any other case, returns false.\n\nArguments\n\ncurrent_cost::Real: the cost of the current program.\nnext_cost::Real: the cost of the proposed program.\ntemperature::Real: the temperature of the search.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.probabilistic_accept_with_temperature_fraction-Tuple{Real, Real, Real}","page":"HerbSearch.jl","title":"HerbSearch.probabilistic_accept_with_temperature_fraction","text":"probabilistic_accept_with_temperature_fraction(current_cost::Real, program_to_consider_cost::Real, temperature::Real)\n\nProbabilistically decides whether to accept the new program (next) based on the ratio of costs (smaller is better) between the previous and new program multiplied by the temperature. Returns True if the new program is accepted, False otherwise.\n\nArguments\n\ncurrent_cost::Real: the cost of the current program.\nnext_cost::Real: the cost of the proposed program.\ntemperature::Real: the current temperature \n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.processkwarg!-Tuple{Vector{Expr}, Union{Expr, Symbol}}","page":"HerbSearch.jl","title":"HerbSearch.processkwarg!","text":"processkwarg!(keywords::Vector{Expr}, ex::Union{Expr, Symbol})\n\nChecks if ex has a default value specified, if so it returns only the field declaration, and pushes ex to keywords. Otherwise it returns ex\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.random_fill_propose","page":"HerbSearch.jl","title":"HerbSearch.random_fill_propose","text":"random_fill_propose(solver::Solver, path::Vector{Int}, dict::Union{Nothing,Dict{String,Any}}, nr_random=5)\n\nReturns a list with only one proposed, completely random, subprogram.\n\nArguments\n\nsolver::solver: solver\npath::Vector{Int}: path to the location to be filled.\ndict::Dict{String, Any}: the dictionary with additional arguments; not used.\nnr_random=1 : the number of random subprograms to be generated.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#HerbSearch.select_chromosome-Tuple{Array{RuleNode}, Array{<:Real}}","page":"HerbSearch.jl","title":"HerbSearch.select_chromosome","text":"select_chromosome(population::Array{RuleNode}, fitness_array::Array{<:Real})::RuleNode\n\nSelects a chromosome (individual) from the population based on a fitness array. The function uses a fitness-proportionate selection strategy, often referred to as \"roulette wheel\" selection. Assumes fitness_array to be normalized already.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.select_fitness_proportional_parents-Tuple{Array{RuleNode}, Array{<:Real}}","page":"HerbSearch.jl","title":"HerbSearch.select_fitness_proportional_parents","text":"select_fitness_proportional_parents(population::Array{RuleNode}, fitness_array::Array{<:Real})::Tuple{RuleNode,RuleNode}\n\nSelects two parent chromosomes (individuals) from a population based on fitness-proportionate selection. The selected parents can be used for genetic crossover in the next steps of the algorithm.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.select_parents-Tuple{GeneticSearchIterator, Array{RuleNode}, Array{<:Real}}","page":"HerbSearch.jl","title":"HerbSearch.select_parents","text":"select_parents(::GeneticSearchIterator, population::Array{RuleNode}, fitness_array::Array{<:Real})\n\nSelects two parents for the crossover.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.set_stateholes!-Tuple{UniformIterator, Union{StateHole, RuleNode}}","page":"HerbSearch.jl","title":"HerbSearch.set_stateholes!","text":"function set_stateholes!(iter::UniformIterator, node::Union{StateHole, RuleNode})::Vector{StateHole}\n\nDoes a dfs to retrieve all unfilled state holes in the program tree and stores them in the stateholes vector.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.synth-Tuple{Problem, ProgramIterator}","page":"HerbSearch.jl","title":"HerbSearch.synth","text":"synth(problem::Problem, iterator::ProgramIterator; shortcircuit::Bool=true, allow_evaluation_errors::Bool=false, mod::Module=Main)::Union{Tuple{RuleNode, SynthResult}, Nothing}\n\nSynthesize a program that satisfies the maximum number of examples in the problem. - problem - The problem definition with IO examples - iterator - The iterator that will be used - shortcircuit - Whether to stop evaluating after finding a single example that fails, to speed up the synth procedure. If true, the returned score is an underapproximation of the actual score. - allowevaluationerrors - Whether the search should crash if an exception is thrown in the evaluation - maxtime - Maximum time that the iterator will run - maxenumerations - Maximum number of iterations that the iterator will run - mod - A module containing definitions for the functions in the grammar that do not exist in Main\n\nReturns a tuple of the rulenode representing the solution program and a synthresult that indicates if that program is optimal. synth uses evaluate which returns a score in the interval [0, 1] and checks whether that score reaches 1. If not it will return the best program so far, with the proper flag\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.validate_iterator-Tuple{Any}","page":"HerbSearch.jl","title":"HerbSearch.validate_iterator","text":"validate_iterator(iter)\n\nValidates the parameters of the iterator\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#StatsBase.sample","page":"HerbSearch.jl","title":"StatsBase.sample","text":"sample(root::RuleNode, typ::Symbol, grammar::AbstractGrammar, maxdepth::Int=typemax(Int))\n\nUniformly samples a random node from the tree limited to maxdepth.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#StatsBase.sample-2","page":"HerbSearch.jl","title":"StatsBase.sample","text":"sample(::Type{NodeLoc}, root::RuleNode, maxdepth::Int=typemax(Int))\n\nUniformly selects a random node in the tree no deeper than maxdepth using reservoir sampling. Returns a NodeLoc that specifies the location using its parent so that the subtree can be replaced.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#StatsBase.sample-3","page":"HerbSearch.jl","title":"StatsBase.sample","text":"sample(root::RuleNode, typ::Symbol, grammar::AbstractGrammar,\n maxdepth::Int=typemax(Int))\n\nUniformly selects a random node of the given return type typ limited by maxdepth.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#StatsBase.sample-4","page":"HerbSearch.jl","title":"StatsBase.sample","text":"StatsBase.sample(::Type{NodeLoc}, root::RuleNode, typ::Symbol, grammar::AbstractGrammar, maxdepth::Int=typemax(Int))\n\nUniformly selects a random node in the tree of a given type, specified using its parent such that the subtree can be replaced. Returns a NodeLoc.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"The HerbSearch package takes care of all operations related to searching for the desired program. This includes","category":"page"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"the functionality to sample a certain program given a grammar,\nthe implementation of several heuristic functions,\nsearching for a program that satisfies the specification, and\nimplementations of several search algorithms in terms of how they enumerate the search space\nBreadth-First Search \nDepth-First Search \nMetropolis Hastings \nVery Large Scale Neighbourhood Search \nSimulated Annealing\nGenetic Search","category":"page"},{"location":"HerbSearch/#Index","page":"HerbSearch.jl","title":"Index","text":"","category":"section"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"","category":"page"},{"location":"tutorials/working_with_interpreters/","page":"Working with custom interpreters","title":"Working with custom interpreters","text":"\n\n\n\n
begin\n    using HerbGrammar\n    using HerbInterpret\nend
\n\n\n\n

Using the Julia interpreter

To know how good a candidate program is, program synthesisers execute them. The easiest way to execute a program is to rely on Julia itself. To leverage the Julia interpreter, you only have to ensure that your programs are valid Julia expressions.

For example, assume the following grammar.

\n\n
g = @csgrammar begin\n    Number = |(1:2)\n    Number = x\n    Number = Number + Number\n    Number = Number * Number\nend
\n
1: Number = 1\n2: Number = 2\n3: Number = x\n4: Number = Number + Number\n5: Number = Number * Number\n
\n\n\n

Let's construct a program x+3, which would correspond to the following RuleNode representation

\n\n
myprog = RuleNode(4, [RuleNode(3), RuleNode(1)])
\n
4{3,1}
\n\n\n

To run this program, we have to convert it into a Julia expression, which we can do in the following way:

\n\n
myprog_julia = rulenode2expr(myprog, g)
\n
:(x + 1)
\n\n\n

Now we have a valid Julia expression, but we are still missing one key ingredient: we have to inform the interpreter about the special symbols. In our case, these are :x and :+. To do so, we need to create a symbol table, which is nothing more than a dictionary mapping symbols to their values:

\n\n
symboltable = Dict{Symbol,Any}(:x => 2, :+ => +)
\n
Dict{Symbol, Any} with 2 entries:\n  :+ => +\n  :x => 2
\n\n\n

Now we can execute our program through the defaul interpreter available in HerbInterpret:

\n\n
interpret(symboltable, myprog_julia)
\n
3
\n\n\n

And that's it!

\n\n\n

Defining a custom interpreter

A disadvantage of the default Julia interpreter is that it needs to traverse abstract syntax tree twice – once to convert it into a Julia expression, and the second time to execute that expression. Program execution is regularly the most consuming part of the entire pipeline and, by eliminating one of these steps, we can cut the runtime in half.

We can define an interpreter that works directly over RuleNodes. Consider the scenario in which we want to write programs for robot navigation: imagine a 2D world in which the robot can move around and pick up a ball. The programs we could write direct the robot to go up, down, left, and right. For convenience, the programming language also offers conditionals and loops:

\n\n
grammar_robots = @csgrammar begin\n    Start = Sequence                   #1\n\n    Sequence = Operation                #2\n    Sequence = (Operation; Sequence)    #3\n    Operation = Transformation          #4\n    Operation = ControlStatement        #5\n\n    Transformation = moveRight() | moveDown() | moveLeft() | moveUp() | drop() | grab()     #6\n    ControlStatement = IF(Condition, Sequence, Sequence)        #12\n    ControlStatement = WHILE(Condition, Sequence)               #13\n\n    Condition = atTop() | atBottom() | atLeft() | atRight() | notAtTop() | notAtBottom() | notAtLeft() | notAtRight()      #14\nend
\n
1: Start = Sequence\n2: Sequence = Operation\n3: Sequence = begin\n    Operation\n    Sequence\nend\n4: Operation = Transformation\n5: Operation = ControlStatement\n6: Transformation = moveRight()\n7: Transformation = moveDown()\n8: Transformation = moveLeft()\n9: Transformation = moveUp()\n10: Transformation = drop()\n11: Transformation = grab()\n12: ControlStatement = IF(Condition, Sequence, Sequence)\n13: ControlStatement = WHILE(Condition, Sequence)\n14: Condition = atTop()\n15: Condition = atBottom()\n16: Condition = atLeft()\n17: Condition = atRight()\n18: Condition = notAtTop()\n19: Condition = notAtBottom()\n20: Condition = notAtLeft()\n21: Condition = notAtRight()\n
\n\n\n

This grammar specifies a simple sequential program with instructions for the robot. A couple of example programs:

  • moveRight(); moveLeft(); drop()

  • WHILE(notAtTop(), moveUp())

The idea behind this programming language is that the program specifies a set of transformations over a state of the robot world. Thus, a program can only be executed over a particular state. In this case, the state represents the size of the 2D world, the current position of a robot, the current position of a ball, and whether the robot is currently holding a ball. The execution of a particular instruction acts as a state transformation: each instruction takes a state as an input, transforms it, and passes it to the subsequent instruction. For example, execution of the program moveRight(); moveLeft(); drop() would proceed as:

  1. take an input state,

  2. pass it to the moveRight() instruction,

  3. pass the output of moveRight() to moveLeft() instructions,

  4. pass the output of moveLeft() to drop(),

  5. return the output of drop().

The following is only one possible way to implement a custom interpreter, but it demonstrates a general template that can always be followed.

We want to implement the following function, which would take in a program in the form of a RuleNode, a grammar, and a starting state, and return the state obtained after executing the program:

    interpret(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, state::RobotState)::RobotState

As RuleNodes only store indices of derivation rules from the grammar, not the functions themselves, we will first pull the function call associated with every derivation rule. In Julia, this is indicated by the top-level symbol of the rules. For example, the top-level symbol for the derivation rule 6 is :moveRight; for rule 12, that is :IF.

\n\n\n\n\n\n\n\n\n\n\n\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n

The remaining functions follow a similar idea. (You can see the full implementation of this interpreter here).

\n\n","category":"page"},{"location":"tutorials/working_with_interpreters/","page":"Working with custom interpreters","title":"Working with custom interpreters","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/working_with_interpreters.jl\"","category":"page"},{"location":"HerbCore/#HerbCore_docs","page":"HerbCore.jl","title":"HerbCore.jl Documentation","text":"","category":"section"},{"location":"HerbCore/","page":"HerbCore.jl","title":"HerbCore.jl","text":"CurrentModule=HerbCore","category":"page"},{"location":"HerbCore/","page":"HerbCore.jl","title":"HerbCore.jl","text":"Modules = [HerbCore]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbCore/#HerbCore.AbstractConstraint","page":"HerbCore.jl","title":"HerbCore.AbstractConstraint","text":"Represents a constraint for a AbstractGrammar. Concrete implementations can be found in HerbConstraints.jl.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.AbstractGrammar","page":"HerbCore.jl","title":"HerbCore.AbstractGrammar","text":"AbstractGrammar\n\nAbstract type representing all grammars. It is assumed that all grammar structs have at least the following attributes:\n\nrules::Vector{Any}: A list of RHS of rules (subexpressions).\ntypes::Vector{Symbol}: A list of LHS of rules (types, all symbols).\nisterminal::BitVector: A bitvector where bit i represents whether rule i is terminal.\niseval::BitVector: A bitvector where bit i represents whether rule i is an eval rule.\nbytype::Dict{Symbol,Vector{Int}}: A dictionary that maps a type to all rules of said type.\ndomains::Dict{Symbol, BitVector}: A dictionary that maps a type to a domain bitvector. The domain bitvector has bit i set to true iff the ith rule is of this type.\nchildtypes::Vector{Vector{Symbol}}: A list of types of the children for each rule. \n\nIf a rule is terminal, the corresponding list is empty.\n\nlog_probabilities::Union{Vector{Real}, Nothing}: A list of probabilities for each rule. \n\nIf the grammar is non-probabilistic, the list can be nothing.\n\nFor concrete types, see ContextSensitiveGrammar within the HerbGrammar module.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.AbstractHole","page":"HerbCore.jl","title":"HerbCore.AbstractHole","text":"AbstractHole <: AbstractRuleNode\n\nA AbstractHole is a placeholder where certain rules from the grammar can still be applied. The domain of a AbstractHole defines which rules can be applied. The domain is a bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.AbstractRuleNode","page":"HerbCore.jl","title":"HerbCore.AbstractRuleNode","text":"abstract type AbstractRuleNode end\n\nAbstract type for representing expression trees. An AbstractRuleNode is expected to implement the following functions:\n\nisfilled(::AbstractRuleNode)::Bool. True iff the grammar rule this node holds is not ambiguous, i.e. has domain size 1.\nisuniform(::AbstractRuleNode)::Bool. True iff the children of this node are known.\nget_rule(::AbstractRuleNode)::Int. Returns the index of the grammar rule it represents.\nget_children(::AbstractRuleNode)::Vector{AbstractRuleNode}. Returns the children of this node.\n\nExpression trees consist of RuleNodes and AbstractHoles.\n\nA RuleNode represents a certain production rule in the AbstractGrammar.\nA AbstractHole is a placeholder where certain rules in the grammar still can be applied. \n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.AbstractUniformHole","page":"HerbCore.jl","title":"HerbCore.AbstractUniformHole","text":"Hole <: AbstractHole\n\nAn AbstractUniformHole is a placeholder where certain rules from the grammar can still be applied, but all rules in the domain are required to have the same childtypes.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.Hole","page":"HerbCore.jl","title":"HerbCore.Hole","text":"Hole <: AbstractHole\n\ndomain: A bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.HoleReference","page":"HerbCore.jl","title":"HerbCore.HoleReference","text":"HoleReference\n\nContains a hole and the path to the hole from the root of the tree.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.RuleNode","page":"HerbCore.jl","title":"HerbCore.RuleNode","text":"RuleNode <: AbstractRuleNode\n\nA RuleNode represents a node in an expression tree. Each node corresponds to a certain rule in the AbstractGrammar. A RuleNode consists of:\n\nind: The index of the rule in the AbstractGrammar which this node is representing.\n_val: Field for caching evaluations of RuleNodes, preventing multiple unnecessary evaluations. The field can be used to store any needed infromation.\nchildren: The children of this node in the expression tree\n\ncompat: Compat\nEvaluate immediately functionality is not yet supported by most of Herb.jl.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.RuleNode-Tuple{Int64, Any}","page":"HerbCore.jl","title":"HerbCore.RuleNode","text":"RuleNode(ind::Int, _val::Any)\n\nCreate a RuleNode for the AbstractGrammar rule with index ind, _val as immediately evaluated value and no children\n\nwarning: Warning\nOnly use this constructor if you are absolutely certain that a rule is terminal and cannot have children. Use [RuleNode(ind::Int, grammar::AbstractGrammar)] for rules that might have children. In general, AbstractHoles should be used as a placeholder when the children of a node are not yet known. \n\ncompat: Compat\nEvaluate immediately functionality is not yet supported by most of Herb.jl.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.RuleNode-Tuple{Int64, Vector{<:AbstractRuleNode}}","page":"HerbCore.jl","title":"HerbCore.RuleNode","text":"RuleNode(ind::Int, children::Vector{AbstractRuleNode})\n\nCreate a RuleNode for the AbstractGrammar rule with index ind and children as subtrees.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.UniformHole","page":"HerbCore.jl","title":"HerbCore.UniformHole","text":"UniformHole <: AbstractHole\n\ndomain: A bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied. All rules in the domain are required to have the same childtypes.\nchildren: The children of this hole in the expression tree.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#Base.isless-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbCore.jl","title":"Base.isless","text":"Base.isless(rn₁::AbstractRuleNode, rn₂::AbstractRuleNode)::Bool\n\nCompares two RuleNodes. Returns true if the left RuleNode is less than the right RuleNode. Order is determined from the index of the RuleNodes. If both RuleNodes have the same index, a depth-first search is performed in both RuleNodes until nodes with a different index are found.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#Base.length-Tuple{AbstractRuleNode}","page":"HerbCore.jl","title":"Base.length","text":"Base.length(root::RuleNode)\n\nReturn the number of nodes in the tree rooted at root.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.contains_hole-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.contains_hole","text":"contains_hole(rn::RuleNode) = any(contains_hole(c) for c ∈ rn.children)\n\nChecks if an AbstractRuleNode tree contains a AbstractHole.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.contains_nonuniform_hole-Tuple{AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.contains_nonuniform_hole","text":"contains_nonuniform_hole(rn::RuleNode)\n\nChecks if an AbstractRuleNode tree contains a Hole.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.depth-Tuple{AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.depth","text":"depth(root::RuleNode)::Int\n\nReturn the depth of the AbstractRuleNode tree rooted at root. Holes do count towards the depth.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_children-Tuple{AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.get_children","text":"get_children(rn::AbstractRuleNode)\n\nReturns the children of the given AbstractRuleNode\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_node_at_location-Tuple{AbstractRuleNode, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.get_node_at_location","text":"get_node_at_location(root::AbstractRuleNode, location::Vector{Int})\n\nRetrieves a RuleNode at the given location by reference.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_node_at_location-Tuple{Hole, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.get_node_at_location","text":"get_node_at_location(root::Hole, location::Vector{Int})\n\nRetrieves the current hole, if location is this very hole. Throws error otherwise.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_path-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.get_path","text":"get_path(root::AbstractRuleNode, node::AbstractRuleNode)\n\nReturns the path from the root to the targetnode. Returns nothing if no path exists.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_rule-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.get_rule","text":"get_rule(rn::AbstractRuleNode)\n\nReturns the index of the rule that this AbstractRuleNode represents\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_rulesequence-Tuple{RuleNode, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.get_rulesequence","text":"get_rulesequence(node::RuleNode, path::Vector{Int})\n\nExtract the derivation sequence from a path (sequence of child indices) and an AbstractRuleNode. If the path is deeper than the deepest node, it returns what it has.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.hasdynamicvalue-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.hasdynamicvalue","text":"function hasdynamicvalue(rn::AbstractRuleNode)::Bool\n\nReturns true iff the rule has a _val field set up.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.have_same_shape-Tuple{Any, Any}","page":"HerbCore.jl","title":"HerbCore.have_same_shape","text":"have_same_shape(node1::AbstractRuleNode, node2::AbstractRuleNode)\n\nReturns true iff node1 and node2 have the same shape Example: RuleNode(3, [ \tRuleNode(1), \tRuleNode(1) ]) and RuleNode(9, [ \tRuleNode(2), \tHole(domain) ]) have the same shape: 1 root with 2 children.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.isfilled-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.isfilled","text":"isfilled(node::AbstractRuleNode)::Bool\n\nReturns whether the [AbstractRuleNode] holds a single rule. This is always the case for RuleNodes. Holes are considered to be \"filled\" iff their domain size is exactly 1.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.isuniform-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.isuniform","text":"isuniform(rn::AbstractRuleNode)\n\nReturns true iff the children of the AbstractRuleNode are known.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.node_depth-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.node_depth","text":"node_depth(root::AbstractRuleNode, node::AbstractRuleNode)::Int\n\nReturn the depth of node for an AbstractRuleNode tree rooted at root. Depth is 1 when root == node.\n\nwarning: Warning\nnode must be a subtree of root in order for this function to work.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.number_of_holes-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.number_of_holes","text":"number_of_holes(rn::AbstractRuleNode)::Int\n\nRecursively counts the number of holes in an AbstractRuleNode\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.rulesoftype-Tuple{RuleNode, Set{Int64}}","page":"HerbCore.jl","title":"HerbCore.rulesoftype","text":"rulesoftype(node::RuleNode, ruleset::Set{Int})\n\nReturns every rule in the ruleset that is also used in the AbstractRuleNode tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.rulesonleft-Tuple{RuleNode, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.rulesonleft","text":"rulesonleft(expr::RuleNode, path::Vector{Int})::Set{Int}\n\nFinds all rules that are used in the left subtree defined by the path.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.swap_node-Tuple{AbstractRuleNode, AbstractRuleNode, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.swap_node","text":"swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int})\n\nReplace a node in expr, specified by path, with new_expr. Path is a sequence of child indices, starting from the root node.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.swap_node-Tuple{RuleNode, RuleNode, Int64, RuleNode}","page":"HerbCore.jl","title":"HerbCore.swap_node","text":"swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode)\n\nReplace child i of a node, a part of larger expr, with new_expr.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#Index","page":"HerbCore.jl","title":"Index","text":"","category":"section"},{"location":"HerbCore/","page":"HerbCore.jl","title":"HerbCore.jl","text":"","category":"page"},{"location":"HerbGrammar/#HerbGrammar_docs","page":"HerbGrammar.jl","title":"HerbGrammar.jl Documentation","text":"","category":"section"},{"location":"HerbGrammar/","page":"HerbGrammar.jl","title":"HerbGrammar.jl","text":"CurrentModule=HerbGrammar","category":"page"},{"location":"HerbGrammar/","page":"HerbGrammar.jl","title":"HerbGrammar.jl","text":"Modules = [HerbGrammar]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbGrammar/#HerbGrammar.ContextSensitiveGrammar","page":"HerbGrammar.jl","title":"HerbGrammar.ContextSensitiveGrammar","text":"ContextSensitiveGrammar <: AbstractGrammar\n\nRepresents a context-sensitive grammar. Extends AbstractGrammar with constraints.\n\nConsists of:\n\nrules::Vector{Any}: A list of RHS of rules (subexpressions).\ntypes::Vector{Symbol}: A list of LHS of rules (types, all symbols).\nisterminal::BitVector: A bitvector where bit i represents whether rule i is terminal.\niseval::BitVector: A bitvector where bit i represents whether rule i is an eval rule.\nbytype::Dict{Symbol,Vector{Int}}: A dictionary that maps a type to all rules of said type.\ndomains::Dict{Symbol, BitVector}: A dictionary that maps a type to a domain bitvector. The domain bitvector has bit i set to true iff the ith rule is of this type.\nchildtypes::Vector{Vector{Symbol}}: A list of types of the children for each rule. If a rule is terminal, the corresponding list is empty.\nbychildtypes::Vector{BitVector}: A bitvector of rules that share the same childtypes for each rule\nlog_probabilities::Union{Vector{Real}, Nothing}: A list of probabilities for each rule. If the grammar is non-probabilistic, the list can be nothing.\nconstraints::Vector{AbstractConstraint}: A list of constraints that programs in this grammar have to abide.\n\nUse the @csgrammar macro to create a ContextSensitiveGrammar object. Use the @pcsgrammar macro to create a ContextSensitiveGrammar object with probabilities.\n\n\n\n\n\n","category":"type"},{"location":"HerbGrammar/#HerbGrammar.NodeLoc","page":"HerbGrammar.jl","title":"HerbGrammar.NodeLoc","text":"NodeLoc A helper struct that points to a node in the tree via its parent such that the child can be easily swapped out. If i is 0 the node pointed to is the root node and parent is the node itself.\n\n\n\n\n\n","category":"type"},{"location":"HerbGrammar/#HerbGrammar.SymbolTable","page":"HerbGrammar.jl","title":"HerbGrammar.SymbolTable","text":"SymbolTable(grammar::AbstractGrammar, mod::Module=Main)\n\nReturns a SymbolTable populated with a mapping from symbols in the AbstractGrammar to symbols in module mod or Main, if defined.\n\n\n\n\n\n","category":"type"},{"location":"HerbGrammar/#HerbGrammar.SymbolTable-2","page":"HerbGrammar.jl","title":"HerbGrammar.SymbolTable","text":"SymbolTable\n\nData structure for mapping terminal symbols in the AbstractGrammar to their Julia interpretation.\n\n\n\n\n\n","category":"type"},{"location":"HerbGrammar/#HerbGrammar.@cfgrammar-Tuple{Any}","page":"HerbGrammar.jl","title":"HerbGrammar.@cfgrammar","text":"@cfgrammar\n\nThis macro is deprecated and will be removed in future versions. Use @csgrammar instead.\n\n\n\n\n\n","category":"macro"},{"location":"HerbGrammar/#HerbGrammar.@csgrammar-Tuple{Any}","page":"HerbGrammar.jl","title":"HerbGrammar.@csgrammar","text":"@csgrammar\n\nA macro for defining a ContextSensitiveGrammar. AbstractConstraints can be added afterwards using the addconstraint! function.\n\nExample usage:\n\ngrammar = @csgrammar begin\n\tR = x\n\tR = 1 | 2\n\tR = R + R\nend\n\nSyntax:\n\nLiterals: Symbols that are already defined in Julia are considered literals, such as 1, 2, or π. For example: R = 1.\nVariables: A variable is a symbol that is not a nonterminal symbol and not already defined in Julia. For example: R = x.\nFunctions: Functions and infix operators that are defined in Julia or the Main module can be used with the default evaluator. For example: R = R + R, R = f(a, b).\nCombinations: Multiple rules can be defined on a single line in the grammar definition using the | symbol. For example: R = 1 | 2 | 3.\nIterators: Another way to define multiple rules is by providing a Julia iterator after a | symbol. For example: R = |(1:9).\n\nRelated:\n\n@pcsgrammar uses a similar syntax to create probabilistic ContextSensitiveGrammars.\n\n\n\n\n\n","category":"macro"},{"location":"HerbGrammar/#HerbGrammar.@pcsgrammar-Tuple{Any}","page":"HerbGrammar.jl","title":"HerbGrammar.@pcsgrammar","text":"@pcsgrammar\n\nA macro for defining a probabilistic ContextSensitiveGrammar. \n\nExample usage:\n\ngrammar = @pcsgrammar begin\n\t0.5 : R = x\n\t0.3 : R = 1 | 2\n\t0.2 : R = R + R\nend\n\nSyntax:\n\nThe syntax of rules is identical to the syntax used by @csgrammar:\n\nLiterals: Symbols that are already defined in Julia are considered literals, such as 1, 2, or π. For example: R = 1.\nVariables: A variable is a symbol that is not a nonterminal symbol and not already defined in Julia. For example: R = x.\nFunctions: Functions and infix operators that are defined in Julia or the Main module can be used with the default evaluator. For example: R = R + R, R = f(a, b).\nCombinations: Multiple rules can be defined on a single line in the grammar definition using the | symbol. For example: R = 1 | 2 | 3.\nIterators: Another way to define multiple rules is by providing a Julia iterator after a | symbol. For example: R = |(1:9).\n\nEvery rule is also prefixed with a probability. Rules and probabilities are separated using the : symbol. If multiple rules are defined on a single line, the probability is equally divided between the rules. The sum of probabilities for all rules of a certain non-terminal symbol should be equal to 1. The probabilities are automatically scaled if this isn't the case.\n\nRelated:\n\n@csgrammar uses a similar syntax to create non-probabilistic ContextSensitiveGrammars.\n\n\n\n\n\n","category":"macro"},{"location":"HerbGrammar/#Base.get-Tuple{AbstractRuleNode, NodeLoc}","page":"HerbGrammar.jl","title":"Base.get","text":"get(root::AbstractRuleNode, loc::NodeLoc) Obtain the node pointed to by loc.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#Base.insert!-Tuple{RuleNode, NodeLoc, RuleNode}","page":"HerbGrammar.jl","title":"Base.insert!","text":"insert!(loc::NodeLoc, rulenode::RuleNode) Replaces the subtree pointed to by loc with the given rulenode.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.add_rule!-Tuple{AbstractGrammar, Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.add_rule!","text":"add_rule!(g::AbstractGrammar, e::Expr)\n\nAdds a rule to the grammar. \n\nUsage:\n\n add_rule!(grammar, :(\"Real = Real + Real\"))\n\nThe syntax is identical to the syntax of @csgrammar and @cfgrammar, but only single rules are supported.\n\nwarning: Warning\nCalls to this function are ignored if a rule is already in the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.add_rule!-Tuple{AbstractGrammar, Real, Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.add_rule!","text":"Adds a probabilistic derivation rule.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.addconstraint!-Tuple{ContextSensitiveGrammar, AbstractConstraint}","page":"HerbGrammar.jl","title":"HerbGrammar.addconstraint!","text":"addconstraint!(grammar::ContextSensitiveGrammar, c::AbstractConstraint)\n\nAdds a AbstractConstraint to a ContextSensitiveGrammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.child_types-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.child_types","text":"child_types(grammar::AbstractGrammar, rule_index::Int)\n\nReturns the types of the children (nonterminals) of the production rule at rule_index.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.child_types-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.child_types","text":"child_types(grammar::AbstractGrammar, node::RuleNode)\n\nReturns the list of child types (nonterminal symbols) in the production rule used by node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.cleanup_removed_rules!-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.cleanup_removed_rules!","text":"cleanup_removed_rules!(g::AbstractGrammar)\n\nRemoves any placeholders for previously deleted rules. This means that indices get shifted.\n\nwarning: Warning\nWhen indices are shifted, this grammar can no longer be used to interpret AbstractRuleNode trees created before the call to this function. These trees become meaningless. \n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.clearconstraints!-Tuple{ContextSensitiveGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.clearconstraints!","text":"Clear all constraints from the grammar\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.containedin-Tuple{Vector, Vector}","page":"HerbGrammar.jl","title":"HerbGrammar.containedin","text":"containedin(vec1::Vector, vec2::Vector)\n\nChecks if elements of vec1 are contained in vec2 in the same order (possibly with elements in between)\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.contains_returntype","page":"HerbGrammar.jl","title":"HerbGrammar.contains_returntype","text":"contains_returntype(node::RuleNode, grammar::AbstractGrammar, sym::Symbol, maxdepth::Int=typemax(Int))\n\nReturns true if the tree rooted at node contains at least one node at depth less than maxdepth with the given return type or nonterminal symbol.\n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.expr2csgrammar-Tuple{Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.expr2csgrammar","text":"expr2csgrammar(ex::Expr)::ContextSensitiveGrammar\n\nA function for converting an Expr to a ContextSensitiveGrammar. If the expression is hardcoded, you should use the @csgrammar macro. Only expressions in the correct format (see @csgrammar) can be converted.\n\nExample usage:\n\ngrammar = expr2csgrammar(\n\tbegin\n\t\tR = x\n\t\tR = 1 | 2\n\t\tR = R + R\n\tend\n)\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.expr2pcsgrammar-Tuple{Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.expr2pcsgrammar","text":"Function for converting an Expr to a ContextSensitiveGrammar with probabilities. If the expression is hardcoded, you should use the @pcsgrammar macro. Only expressions in the correct format (see @pcsgrammar) can be converted.\n\nExample usage:\n\ngrammar = expr2pcsgrammar(\n\tbegin\n\t\t0.5 : R = x\n\t\t0.3 : R = 1 | 2\n\t\t0.2 : R = R + R\n\tend\n)\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.get_childtypes-Tuple{Any, AbstractVector{Symbol}}","page":"HerbGrammar.jl","title":"HerbGrammar.get_childtypes","text":"get_childtypes(rule::Any, types::AbstractVector{Symbol})\n\nReturns the child types/nonterminals of a production rule.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.get_domain-Tuple{AbstractGrammar, Symbol}","page":"HerbGrammar.jl","title":"HerbGrammar.get_domain","text":"get_domain(g::AbstractGrammar, type::Symbol)::BitVector\n\nReturns the domain for the hole of a certain type as a BitVector of the same length as the number of rules in the grammar. Bit i is set to true iff rule i is of type type.\n\ninfo: Info\nSince this function can be intensively used when exploring a program space defined by a grammar, the outcomes of this function are precomputed and stored in the domains field in a AbstractGrammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.get_domain-Tuple{AbstractGrammar, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.get_domain","text":"get_domain(g::AbstractGrammar, rules::Vector{Int})::BitVector\n\nTakes a domain rules defined as a vector of ints and converts it to a domain defined as a BitVector.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.get_rulesequence-Tuple{RuleNode, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.get_rulesequence","text":"get_rulesequence(node::RuleNode, path::Vector{Int})\n\nExtract the derivation sequence from a path (sequence of child indices) and an AbstractRuleNode. If the path is deeper than the deepest node, it returns what it has.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.iscomplete-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.iscomplete","text":"iscomplete(grammar::AbstractGrammar, node::RuleNode)\n\nReturns true if the expression represented by the RuleNode is a complete expression, meaning that it is fully defined and doesn't have any Holes.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.iseval-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.iseval","text":"iseval(grammar::AbstractGrammar, index::Int)::Bool\n\nReturns true if the production rule at rule_index contains the special _() eval function.\n\ncompat: Compat\nevaluate immediately functionality is not yet supported by most of Herb.jl\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.iseval-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.iseval","text":"iseval(grammar::AbstractGrammar)::Bool\n\nReturns true if any production rules in grammar contain the special _() eval function.\n\ncompat: Compat\nevaluate immediately functionality is not yet supported by most of Herb.jl\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.iseval-Tuple{Any}","page":"HerbGrammar.jl","title":"HerbGrammar.iseval","text":"iseval(rule)\n\nReturns true if the rule is the special evaluate immediately function, i.e., _()\n\ncompat: Compat\nevaluate immediately functionality is not yet supported by most of Herb.jl\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isprobabilistic-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.isprobabilistic","text":"isprobabilistic(grammar::AbstractGrammar)::Bool\n\nFunction returns whether a AbstractGrammar is probabilistic.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isterminal-Tuple{AbstractGrammar, AbstractRuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.isterminal","text":"isterminal(grammar::AbstractGrammar, node::AbstractRuleNode)::Bool\n\nReturns true if the production rule used by node is terminal, i.e., does not contain any nonterminal symbols.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isterminal-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.isterminal","text":"isterminal(grammar::AbstractGrammar, rule_index::Int)::Bool\n\nReturns true if the production rule at rule_index is terminal, i.e., does not contain any nonterminal symbols.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isterminal-Tuple{Any, AbstractVector{Symbol}}","page":"HerbGrammar.jl","title":"HerbGrammar.isterminal","text":"isterminal(rule::Any, types::AbstractVector{Symbol})\n\nReturns true if the rule is terminal, i.e., it does not contain any of the types in the provided vector. For example, :(x) is terminal, and :(1+1) is terminal, but :(Real + Real) is typically not.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isvariable-Tuple{AbstractGrammar, Int64, Vararg{Module}}","page":"HerbGrammar.jl","title":"HerbGrammar.isvariable","text":"isvariable(grammar::AbstractGrammar, ind::Int, mod::Module)::Bool\n\nReturn true if the rule with index ind represents a variable.\n\nTaking into account the symbols defined in the given module(s).\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isvariable-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.isvariable","text":"isvariable(grammar::AbstractGrammar, ind::Int)::Bool\n\nReturn true if the rule with index ind represents a variable.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isvariable-Tuple{AbstractGrammar, RuleNode, Vararg{Module}}","page":"HerbGrammar.jl","title":"HerbGrammar.isvariable","text":"isvariable(grammar::AbstractGrammar, node::RuleNode, mod::Module)::Bool\n\nReturn true if the rule used by node represents a variable.\n\nTaking into account the symbols defined in the given module(s).\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isvariable-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.isvariable","text":"isvariable(grammar::AbstractGrammar, node::RuleNode)::Bool\n\nReturn true if the rule used by node represents a variable in a program (essentially, an input to the program)\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.log_probability-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.log_probability","text":"log_probability(grammar::AbstractGrammar, index::Int)::Real\n\nReturns the log probability for the rule at index in the grammar.\n\nwarning: Warning\nIf the grammar is not probabilistic, a warning is displayed, and a uniform probability is assumed.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.max_arity-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.max_arity","text":"max_arity(grammar::AbstractGrammar)::Int\n\nReturns the maximum arity (number of children) over all production rules in the AbstractGrammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.merge_grammars!-Tuple{AbstractGrammar, AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.merge_grammars!","text":"merge_grammars!(merge_to::AbstractGrammar, merge_from::AbstractGrammar)\n\nAdds all rules and constraints from merge_from to merge_to.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.mindepth-Tuple{AbstractGrammar, Symbol, AbstractVector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.mindepth","text":"mindepth(grammar::AbstractGrammar, typ::Symbol, dmap::AbstractVector{Int})\n\nReturns the minimum depth achievable for a given nonterminal symbol. The minimum depth is the depth of the lowest tree that can be made using typ as a start symbol. dmap can be obtained from mindepth_map.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.mindepth_map-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.mindepth_map","text":"mindepth_map(grammar::AbstractGrammar)\n\nReturns the minimum depth achievable for each production rule in the AbstractGrammar. In other words, this function finds the depths of the lowest trees that can be made using each of the available production rules as a root.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.nchildren-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.nchildren","text":"nchildren(grammar::AbstractGrammar, rule_index::Int)::Int\n\nReturns the number of children (nonterminals) of the production rule at rule_index.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.nchildren-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.nchildren","text":"nchildren(grammar::AbstractGrammar, node::RuleNode)::Int\n\nReturns the number of children in the production rule used by node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.nonterminals-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.nonterminals","text":"nonterminals(grammar::AbstractGrammar)::Vector{Symbol}\n\nReturns a list of the nonterminals or types in the AbstractGrammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.normalize!","page":"HerbGrammar.jl","title":"HerbGrammar.normalize!","text":"A function for normalizing the probabilities of a probabilistic ContextSensitiveGrammar. If the optional type argument is provided, only the rules of that type are normalized.\n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.parse_probabilistic_rule-Tuple{Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.parse_probabilistic_rule","text":"Parses a single (potentially shorthand) derivation rule of a probabilistic ContextSensitiveGrammar. Returns nothing if the rule is not probabilistic, otherwise a Tuple of its type and a Vector of probability-rule pairs it expands into.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.probability-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.probability","text":"probability(grammar::AbstractGrammar, index::Int)::Real\n\nReturn the probability for a rule in the grammar. Use log_probability whenever possible.\n\nwarning: Warning\nIf the grammar is not probabilistic, a warning is displayed, and a uniform probability is assumed.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.read_csg","page":"HerbGrammar.jl","title":"HerbGrammar.read_csg","text":"read_csg(grammarpath::AbstractString, constraintspath::OptionalPath=nothing)::ContextSensitiveGrammar\n\nReads a ContextSensitiveGrammar from the files at grammarpath and constraintspath.\n\ndanger: Danger\nOnly open trusted grammars. Parts of the grammar can be passed to Julia's eval function. \n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.read_pcsg","page":"HerbGrammar.jl","title":"HerbGrammar.read_pcsg","text":"read_pcsg(grammarpath::AbstractString, constraintspath::OptionalPath=nothing)::ContextSensitiveGrammar\n\nReads a probabilistic ContextSensitiveGrammar from the files at grammarpath and constraintspath.\n\ndanger: Danger\nOnly open trusted grammars. Parts of the grammar can be passed to Julia's eval function. \n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.remove_rule!-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.remove_rule!","text":"remove_rule!(g::AbstractGrammar, idx::Int)\n\nRemoves the rule corresponding to idx from the grammar. In order to avoid shifting indices, the rule is replaced with nothing, and all other data structures are updated accordingly.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.return_type-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.return_type","text":"return_type(grammar::AbstractGrammar, rule_index::Int)::Symbol\n\nReturns the type of the production rule at rule_index.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.return_type-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.return_type","text":"return_type(grammar::AbstractGrammar, node::RuleNode)\n\nGives the return type or nonterminal symbol in the production rule used by node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.return_type-Tuple{AbstractGrammar, UniformHole}","page":"HerbGrammar.jl","title":"HerbGrammar.return_type","text":"return_type(grammar::AbstractGrammar, hole::UniformHole)\n\nGives the return type or nonterminal symbol in the production rule used by hole.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.root_node_loc-Tuple{RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.root_node_loc","text":"rootnodeloc(root::RuleNode) Returns a NodeLoc pointing to the root node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulenode2expr-Tuple{AbstractRuleNode, AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.rulenode2expr","text":"rulenode2expr(rulenode::AbstractRuleNode, grammar::AbstractGrammar)\n\nConverts an AbstractRuleNode into a Julia expression corresponding to the rule definitions in the grammar. The returned expression can be evaluated with Julia semantics using eval().\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulenode_log_probability-Tuple{RuleNode, AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.rulenode_log_probability","text":"Calculates the log probability associated with a rulenode in a probabilistic grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulesoftype-Tuple{RuleNode, AbstractGrammar, Symbol, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.rulesoftype","text":"rulesoftype(node::RuleNode, grammar::AbstractGrammar, ruletype::Symbol, ignoreNode::RuleNode)\n\nReturns every rule of nonterminal symbol ruletype that is also used in the AbstractRuleNode tree, but not in the ignoreNode subtree.\n\nwarning: Warning\nThe ignoreNode must be a subtree of node for it to have an effect.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulesoftype-Tuple{RuleNode, AbstractGrammar, Symbol}","page":"HerbGrammar.jl","title":"HerbGrammar.rulesoftype","text":"rulesoftype(node::RuleNode, grammar::AbstractGrammar, ruletype::Symbol)\n\nReturns every rule of nonterminal symbol ruletype that is also used in the AbstractRuleNode tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulesoftype-Tuple{RuleNode, Set{Int64}, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.rulesoftype","text":"rulesoftype(node::RuleNode, ruleset::Set{Int}, ignoreNode::RuleNode)\n\nReturns every rule in the ruleset that is also used in the AbstractRuleNode tree, but not in the ignoreNode subtree.\n\nwarning: Warning\nThe ignoreNode must be a subtree of node for it to have an effect.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulesonleft-Tuple{RuleNode, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.rulesonleft","text":"rulesonleft(node::RuleNode, path::Vector{Int})::Set{Int}\n\nFinds all rules that are used in the left subtree defined by the path.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.store_csg","page":"HerbGrammar.jl","title":"HerbGrammar.store_csg","text":"store_csg(g::ContextSensitiveGrammar, grammarpath::AbstractString, constraintspath::OptionalPath=nothing)\n\nWrites a ContextSensitiveGrammar to the files at grammarpath and constraintspath. The grammarpath file will contain a ContextSensitiveGrammar definition, and the constraintspath file will contain the AbstractConstraints of the ContextSensitiveGrammar.\n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.subsequenceof-Tuple{Vector{Int64}, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.subsequenceof","text":"subsequenceof(vec1::Vector{Int}, vec2::Vector{Int})\n\nChecks if vec1 is a subsequence of vec2.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.swap_node-Tuple{AbstractRuleNode, AbstractRuleNode, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.swap_node","text":"swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int})\n\nReplace a node in expr, specified by path, with new_expr. Path is a sequence of child indices, starting from the root node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.swap_node-Tuple{RuleNode, RuleNode, Int64, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.swap_node","text":"swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode)\n\nReplace child i of a node, a part of larger expr, with new_expr.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#Index","page":"HerbGrammar.jl","title":"Index","text":"","category":"section"},{"location":"HerbGrammar/","page":"HerbGrammar.jl","title":"HerbGrammar.jl","text":"","category":"page"},{"location":"HerbSpecification/#HerbSpecification_docs","page":"HerbSpecification.jl","title":"HerbSpecification.jl Documentation","text":"","category":"section"},{"location":"HerbSpecification/","page":"HerbSpecification.jl","title":"HerbSpecification.jl","text":"CurrentModule=HerbSpecification","category":"page"},{"location":"HerbSpecification/","page":"HerbSpecification.jl","title":"HerbSpecification.jl","text":"Modules = [HerbSpecification]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbSpecification/#HerbSpecification.AbstractDependentTypeSpecification","page":"HerbSpecification.jl","title":"HerbSpecification.AbstractDependentTypeSpecification","text":"struct AbstractDependentTypeSpecification <: AbstractTypeSpecification\n\nDefines a specification through dependent types. Needs a concrete type checker as oracle.\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.AgdaSpecification","page":"HerbSpecification.jl","title":"HerbSpecification.AgdaSpecification","text":"struct AgdaSpecification <: AbstractDependentTypeSpecification\n\nDefines a specification \n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.IOExample","page":"HerbSpecification.jl","title":"HerbSpecification.IOExample","text":"struct IOExample\n\nAn input-output example. in is a Dict of {Symbol,Any} where the symbol represents a variable in a program. out can be anything.\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.MetricProblem","page":"HerbSpecification.jl","title":"HerbSpecification.MetricProblem","text":"struct MetricProblem{T <: Vector{IOExample}}\n\nProgram synthesis problem defined by an specification and a metric. The specification has to be based on input/output examples, while the function needs to return a numerical value.\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.Problem","page":"HerbSpecification.jl","title":"HerbSpecification.Problem","text":"struct Problem\n\nProgram synthesis problem defined by an AbstractSpecifications. Has a name and a specification of type T.\n\nwarning: Warning\nPlease care that concrete Problem types with different values of T are never subtypes of each other. \n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.SMTSpecification","page":"HerbSpecification.jl","title":"HerbSpecification.SMTSpecification","text":"struct SMTSpecification <: AbstractFormalSpecification\n\nA specification based on a logical formula defined by a SMT solver.\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.Trace","page":"HerbSpecification.jl","title":"HerbSpecification.Trace","text":"struct Trace\n\nA trace defining a wanted program execution for program synthesis. @TODO combine with Gen.jl\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#Base.getindex-Tuple{Problem{Vector{IOExample}}, Any}","page":"HerbSpecification.jl","title":"Base.getindex","text":"Base.getindex(p::Problem{Vector{IOExample}}, indices)\n\nOverwrite Base.getindex to allow for slicing of input/output-based problems.\n\n\n\n\n\n","category":"method"},{"location":"HerbSpecification/#Index","page":"HerbSpecification.jl","title":"Index","text":"","category":"section"},{"location":"HerbSpecification/","page":"HerbSpecification.jl","title":"HerbSpecification.jl","text":"","category":"page"},{"location":"HerbConstraints/#HerbConstraints_docs","page":"HerbConstraints.jl","title":"HerbConstraints.jl Documentation","text":"","category":"section"},{"location":"HerbConstraints/","page":"HerbConstraints.jl","title":"HerbConstraints.jl","text":"CurrentModule=HerbConstraints","category":"page"},{"location":"HerbConstraints/","page":"HerbConstraints.jl","title":"HerbConstraints.jl","text":"Modules = [HerbConstraints]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbConstraints/#HerbConstraints.AbstractGrammarConstraint","page":"HerbConstraints.jl","title":"HerbConstraints.AbstractGrammarConstraint","text":"abstract type AbstractGrammarConstraint <: AbstractConstraint\n\nAbstract type representing all user-defined constraints. Each grammar constraint has a related AbstractLocalConstraint that is responsible for propagating the constraint at a specific location in the tree. Grammar constraints should implement on_new_node to post a AbstractLocalConstraint at that new node\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.AbstractLocalConstraint","page":"HerbConstraints.jl","title":"HerbConstraints.AbstractLocalConstraint","text":"abstract type AbstractLocalConstraint <: AbstractConstraint\n\nAbstract type representing all local constraints. Each local constraint contains a path that points to a specific location in the tree at which the constraint applies.\n\nEach local constraint should implement a propagate!-function. Inside the propagate! function, the constraint can use the following solver functions:\n\nremove!: Elementary tree manipulation. Removes a value from a domain. (other tree manipulations are: remove_above!, remove_below!, remove_all_but!)\ndeactivate!: Prevent repropagation. Call this as soon as the constraint is satisfied.\nset_infeasible!: Report a non-trivial inconsistency. Call this if the constraint can never be satisfied. An empty domain is considered a trivial inconsistency, such inconsistencies are already handled by tree manipulations.\nisfeasible: Check if the current tree is still feasible. Return from the propagate function, as soon as infeasibility is detected.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.AbstractStateManager","page":"HerbConstraints.jl","title":"HerbConstraints.AbstractStateManager","text":"Manages all changes made to StateInts using StateIntBackups. Support the following functions:\n\nStateInt Creates a new stateful integer\nsave_state! Creates a checkpoint for all stateful integers\nrestore! Restores the values to the latest checkpoint\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.Contains","page":"HerbConstraints.jl","title":"HerbConstraints.Contains","text":"Contains <: AbstractGrammarConstraint This [AbstractGrammarConstraint] enforces that a given rule appears in the program tree at least once.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.ContainsSubtree","page":"HerbConstraints.jl","title":"HerbConstraints.ContainsSubtree","text":"ContainsSubtree <: AbstractGrammarConstraint\n\nThis [AbstractGrammarConstraint] enforces that a given subtree appears in the program tree at least once.\n\n!!! warning: This constraint can only be propagated by the UniformSolver\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.DomainRuleNode","page":"HerbConstraints.jl","title":"HerbConstraints.DomainRuleNode","text":"struct DomainRuleNode <: AbstractRuleNode\n\nMatches any 1 rule in its domain. Example usage:\n\nDomainRuleNode(Bitvector((0, 0, 1, 1)), [RuleNode(1), RuleNode(1)])\n\nThis matches RuleNode(3, [RuleNode(1), RuleNode(1)]) and RuleNode(4, [RuleNode(1), RuleNode(1)]) and UniformHole({3, 4}, [RuleNode(1), RuleNode(1)])\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.Forbidden","page":"HerbConstraints.jl","title":"HerbConstraints.Forbidden","text":"Forbidden <: AbstractGrammarConstraint\n\nThis [AbstractGrammarConstraint] forbids any subtree that matches the pattern given by tree to be generated. A pattern is a tree of AbstractRuleNodes. Such a node can either be a RuleNode, which contains a rule index corresponding to the rule index in the AbstractGrammar and the appropriate number of children, similar to RuleNodes. It can also contain a VarNode, which contains a single identifier symbol. A VarNode can match any subtree, but if there are multiple instances of the same variable in the pattern, the matched subtrees must be identical. Any rule in the domain that makes the match attempt successful is removed.\n\nFor example, consider the tree 1(a, 2(b, 3(c, 4)))):\n\nForbidden(RuleNode(3, [RuleNode(5), RuleNode(4)])) forbids c to be filled with 5.\nForbidden(RuleNode(3, [VarNode(:v), RuleNode(4)])) forbids c to be filled, since a [VarNode] can match any rule, thus making the match attempt successful for the entire domain of c. Therefore, this tree invalid.\nForbidden(RuleNode(3, [VarNode(:v), VarNode(:v)])) forbids c to be filled with 4, since that would make both assignments to v equal, which causes a successful match.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.ForbiddenSequence","page":"HerbConstraints.jl","title":"HerbConstraints.ForbiddenSequence","text":"ForbiddenPath <: AbstractGrammarConstraint\n\nThis [AbstractGrammarConstraint] forbids the given sequence of rule nodes. Sequences are strictly vertical and may include gaps. Consider the tree 1(a, 2(b, 3(c, d)))):\n\n[2, 3, d] is a sequence\n[1, 3, d] is a sequence\n[3, c, d] is not a sequence\n\nExamples:\n\nForbiddenSequence([3, 4]) enforces that rule 4 cannot be applied at c or d.\nForbiddenSequence([1, 2, 4]) enforces that rule 4 cannot be applied at b, c or d.\nForbiddenSequence([1, 4]) enforces that rule 4 cannot be applied anywhere.\n\nIf any of the rules in ignore_if appears in the sequence, the constraint is ignored. Suppose the forbidden sequence = [1, 2, 3] and ignore_if = [99] Consider the following paths from the root:\n\n[1, 2, 2, 3] is forbidden, as the sequence does not contain 99\n[1, 99, 2, 3] is NOT forbidden, as the sequence does contain 99\n[1, 99, 1, 2, 3] is forbidden, as there is a subsequence that does not contain 99\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.GenericSolver","page":"HerbConstraints.jl","title":"HerbConstraints.GenericSolver","text":"GenericSolver\n\nMaintains a feasible partial program in a SolverState. A ProgramIterator may manipulate the partial tree with the following tree manipulations:\n\nsubstitute!\nremove!\nremove_below!\nremove_above!\nremove_all_but!\n\nEach SolverState holds an independent propagation program. Program iterators can freely move back and forth between states using:\n\nnew_state!\nsave_state!\nload_state!\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.GenericSolver-Tuple{AbstractGrammar, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.GenericSolver","text":"GenericSolver(grammar::AbstractGrammar, init_node::AbstractRuleNode)\n\nConstructs a new solver, with an initial state of the provided AbstractRuleNode.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.GenericSolver-Tuple{AbstractGrammar, Symbol}","page":"HerbConstraints.jl","title":"HerbConstraints.GenericSolver","text":"GenericSolver(grammar::AbstractGrammar, sym::Symbol)\n\nConstructs a new solver, with an initial state using starting symbol sym\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualHardFail","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualHardFail","text":"struct LessThanOrEqualHardFail <: LessThanOrEqualResult end\n\nnode1 > node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualResult","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualResult","text":"abstract type LessThanOrEqualResult end\n\nA result of the less_than_or_equal function. Can be one of 3 cases:\n\nLessThanOrEqualSuccess\nLessThanOrEqualHardFail\nLessThanOrEqualSoftFail\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSoftFail","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSoftFail","text":"struct LessThanOrEqualSoftFail <: LessThanOrEqualResult\n\nnode1 <= node2 and node1 > node2 are both possible depending on the assignment of hole1 and hole2. Includes two cases:\n\nhole2::AbstractHole: A failed AbstractHole-AbstractHole comparison. (e.g. AbstractHole(BitVector((1, 0, 1))) vs AbstractHole(BitVector((0, 1, 1))))\nhole2::Nothing: A failed AbstractHole-RuleNode comparison. (e.g. AbstractHole(BitVector((1, 0, 1))) vs RuleNode(2))\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSuccess","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSuccess","text":"abstract type LessThanOrEqualSuccess <: LessThanOrEqualResult\n\nnode1 <= node2 is guaranteed under all possible assignments of the holes involved. The strictness of a LessThanOrEqualSuccess is specified by 1 of 2 concrete cases:\n\nLessThanOrEqualSuccessLessThan: node1 < node2\nLessThanOrEqualSuccessEquality: node1 == node2\nLessThanOrEqualSuccessWithHoles: node1 <= node2. Unable to specific.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSuccessEquality","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSuccessEquality","text":"struct LessThanOrEqualSuccessEquality <: LessThanOrEqualSuccess end\n\nnode1 == node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSuccessLessThan","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSuccessLessThan","text":"struct LessThanOrEqualSuccessEquality <: LessThanOrEqualSuccess end\n\nnode1 < node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSuccessWithHoles","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSuccessWithHoles","text":"struct LessThanOrEqualSuccessWithHoles <: LessThanOrEqualSuccess end\n\nnode1 <= node2 is guaranteed under all possible assignments of the holes involved. Because of the holes involved, it is not possible to specify '<' or '=='.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalContains","page":"HerbConstraints.jl","title":"HerbConstraints.LocalContains","text":"LocalContains\n\nEnforces that a given rule appears at or below the given path at least once.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalContainsSubtree","page":"HerbConstraints.jl","title":"HerbConstraints.LocalContainsSubtree","text":"LocalContains\n\nEnforces that a given tree appears at or below the given path at least once.\n\n!!! warning: This is a stateful constraint can only be propagated by the UniformSolver. The indices and candidates fields should not be set by the user.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalContainsSubtree-Tuple{Vector{Int64}, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.LocalContainsSubtree","text":"LocalContainsSubtree(path::Vector{Int}, tree::AbstractRuleNode)\n\nEnforces that a given tree appears at or below the given path at least once.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.LocalForbidden","page":"HerbConstraints.jl","title":"HerbConstraints.LocalForbidden","text":"LocalForbidden\n\nForbids the a subtree that matches the tree to be generated at the location provided by the path. Use a Forbidden constraint for enforcing this throughout the entire search space.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalForbiddenSequence","page":"HerbConstraints.jl","title":"HerbConstraints.LocalForbiddenSequence","text":"LocalForbiddenSequence <: AbstractLocalConstraint\n\nForbids the given sequence of rule nodes ending at the node at the path. If any of the rules in ignore_if appears in the sequence, the constraint is ignored.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalOrdered","page":"HerbConstraints.jl","title":"HerbConstraints.LocalOrdered","text":"Enforces an order over two or more subtrees that fill the variables specified in order when the pattern is applied at the location given by path. Use an Ordered constraint for enforcing this throughout the entire search space.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalUnique","page":"HerbConstraints.jl","title":"HerbConstraints.LocalUnique","text":"LocalUnique <: AbstractLocalConstraint\n\nEnforces that a given rule appears at or below the given path at most once. In case of the UniformSolver, cache the list of holes, since no new holes can appear.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.MakeEqualHardFail","page":"HerbConstraints.jl","title":"HerbConstraints.MakeEqualHardFail","text":"struct MakeEqualHardFail <: MakeEqualResult end\n\nnode1 != node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.MakeEqualResult","page":"HerbConstraints.jl","title":"HerbConstraints.MakeEqualResult","text":"abstract type MakeEqualResult end\n\nA result of the make_equal! function. Can be one of 3 cases:\n\nMakeEqualSuccess\nMakeEqualHardFail\nMakeEqualSoftFail\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.MakeEqualSoftFail","page":"HerbConstraints.jl","title":"HerbConstraints.MakeEqualSoftFail","text":"struct MakeEqualSoftFail <: MakeEqualResult end\n\nMaking node1 == node2 is ambiguous. Examples:\n\nRuleNode(1, [Hole({1, 2, 3})]) == RuleNode(1, [VarNode(:a)]). The hole can be filled with any rule.\nHole({1, 2, 3}) == DomainRuleNode({1, 2, 3}). The hole can be filled with any rule.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.MakeEqualSuccess","page":"HerbConstraints.jl","title":"HerbConstraints.MakeEqualSuccess","text":"struct MakeEqualSuccess <: MakeEqualResult end\n\nnode1 == node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.Ordered","page":"HerbConstraints.jl","title":"HerbConstraints.Ordered","text":"Ordered <: AbstractGrammarConstraint\n\nA AbstractGrammarConstraint that enforces a specific order in MatchVar assignments in the pattern defined by tree. Nodes in the pattern can either be a RuleNode, which contains a rule index corresponding to the rule index in the AbstractGrammar and the appropriate number of children. It can also contain a VarNode, which contains a single identifier symbol. A VarNode can match any subtree. If there are multiple instances of the same variable in the pattern, the matched subtrees must be identical.\n\nThe order defines an order between the variable assignments. For example, if the order is [x, y], the constraint will require the assignment to x to be less than or equal to the assignment to y. The order is recursively defined by RuleNode indices. For more information, see Base.isless(rn₁::AbstractRuleNode, rn₂::AbstractRuleNode).\n\nFor example, consider the tree 1(a, 2(b, 3(c, 4)))):\n\nOrdered(RuleNode(3, [VarNode(:v), VarNode(:w)]), [:v, :w]) removes every rule with an index of 5 or greater from the domain of c, since that would make the index of the assignment to v greater than the index of the assignment to w, violating the order.\nOrdered(RuleNode(3, [VarNode(:v), VarNode(:w)]), [:w, :v]) removes every rule with an index of 4 or less from the domain of c, since that would make the index of the assignment to v less than the index of the assignment to w, violating the order.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchHardFail","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchHardFail","text":"The pattern is not matched and can never be matched by filling in holes\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchResult","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchResult","text":"abstract type PatternMatchResult end\n\nA result of the pattern_match function. Can be one of 4 cases:\n\nPatternMatchSuccess\nPatternMatchSuccessWhenHoleAssignedTo\nPatternMatchHardFail\nPatternMatchSoftFail\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchSoftFail","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchSoftFail","text":"The pattern can still be matched in a non-trivial way. Includes two cases:\n\nmultiple holes are involved. this result stores a reference to one of them\na single hole is involved, but needs to be filled with a node of size >= 2\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchSuccess","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchSuccess","text":"The pattern is exactly matched and does not involve any holes at all\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchSuccessWhenHoleAssignedTo","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchSuccessWhenHoleAssignedTo","text":"The pattern can be matched when the hole is filled with any of the given ind(s).\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.Solver","page":"HerbConstraints.jl","title":"HerbConstraints.Solver","text":"abstract type Solver\n\nAbstract constraint solver. Each solver should have at least the following fields:\n\nstatistics::SolverStatistics\nfix_point_running::Bool\nschedule::PriorityQueue{AbstractLocalConstraint, Int}\n\nEach solver should implement at least:\n\npost!\nget_tree\nget_grammar\nset_infeasible!\nisfeasible\nHerbCore.get_node_at_location\nget_hole_at_location\nnotify_tree_manipulation\ndeactivate!\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.SolverState","page":"HerbConstraints.jl","title":"HerbConstraints.SolverState","text":"mutable struct SolverState\n\nA state to be solved by the GenericSolver. A state contains of:\n\ntree: A partial AST\nactive_constraints: The local constraints that apply to this tree. These constraints are enforced each time the tree is modified.\nisfeasible: Flag to indicate if this state is still feasible. When a propagator spots an inconsistency, this field will be set to false. Tree manipulations and further propagations are not allowed on infeasible states\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.SolverStatistics","page":"HerbConstraints.jl","title":"HerbConstraints.SolverStatistics","text":"Temporary struct to track! the number of several function calls centered around the Solver\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateHole","page":"HerbConstraints.jl","title":"HerbConstraints.StateHole","text":"StateHole <: AbstractUniformHole\n\nStateHoles are uniform holes used by the UniformSolver. Domain manipulations are tracked for backpropagation.\n\ndomain: A StateSparseSet representing the rule nodes this hole can take. If size(domain) == 1, this hole should act like a RuleNode\nchildren: The children of this hole in the expression tree.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateHole-Tuple{HerbConstraints.StateManager, RuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.StateHole","text":"Converts a RuleNode to a StateHole\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateHole-Tuple{HerbConstraints.StateManager, UniformHole}","page":"HerbConstraints.jl","title":"HerbConstraints.StateHole","text":"Converts a UniformHole to a StateHole\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateInt","page":"HerbConstraints.jl","title":"HerbConstraints.StateInt","text":"Stateful integer that can be saved and restored by the StateManager. Supports the following functions:\n\nget_value\nset_value!\nincrement!\ndecrement!\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateIntBackup","page":"HerbConstraints.jl","title":"HerbConstraints.StateIntBackup","text":"Backup entry for the given StateInt\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateManager","page":"HerbConstraints.jl","title":"HerbConstraints.StateManager","text":"Manages all changes made to StateInts using StateIntBackups\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateSparseSet-Tuple{HerbConstraints.StateManager, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.StateSparseSet","text":"Converts a BitVector domain representation to a StateSparseSet Example:\n\nset = StateSparseSet(sm, BitVector((1, 1, 0, 0, 1, 0, 0))) #{1, 2, 5}\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateSparseSet-Tuple{HerbConstraints.StateManager, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.StateSparseSet","text":"Create a new StateSparseSet with values [1, 2, ..., n]\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateStack","page":"HerbConstraints.jl","title":"HerbConstraints.StateStack","text":"Simple stack that can only increase in size. Supports backtracking by decreasing the size to the saved size.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateStack-Union{Tuple{HerbConstraints.AbstractStateManager}, Tuple{T}} where T","page":"HerbConstraints.jl","title":"HerbConstraints.StateStack","text":"function StateStack{T}(sm::AbstractStateManager) where T\n\nCreate an empty StateStack supporting elements of type T\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateStack-Union{Tuple{T}, Tuple{HerbConstraints.AbstractStateManager, Vector{T}}} where T","page":"HerbConstraints.jl","title":"HerbConstraints.StateStack","text":"function StateStack{T}(sm::AbstractStateManager, vec::Vector{T}) where T\n\nCreate a StateStack for the provided vec\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.UniformSolver","page":"HerbConstraints.jl","title":"HerbConstraints.UniformSolver","text":"A DFS-based solver that uses StateHoles that support backtracking.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.UniformSolver-Tuple{AbstractGrammar, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.UniformSolver","text":"UniformSolver(grammar::AbstractGrammar, fixed_shaped_tree::AbstractRuleNode)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.Unique","page":"HerbConstraints.jl","title":"HerbConstraints.Unique","text":"Unique <: AbstractGrammarConstraint\n\nThis [AbstractGrammarConstraint] enforces that a given rule appears in the program tree at most once.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.VarNode","page":"HerbConstraints.jl","title":"HerbConstraints.VarNode","text":"struct VarNode <: AbstractRuleNode\n\nMatches any subtree and assigns it to a variable name. The LocalForbidden constraint will not match if identical variable symbols match to different trees. Example usage:\n\nRuleNode(3, [VarNode(:x), VarNode(:x)])\n\nThis matches RuleNode(3, [RuleNode(1), RuleNode(1)]), RuleNode(3, [RuleNode(2), RuleNode(2)]), etc. but also larger subtrees such as RuleNode(3, [RuleNode(4, [RuleNode(1)]), RuleNode(4, [RuleNode(1)])])\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.@csgrammar_annotated-Tuple{Any}","page":"HerbConstraints.jl","title":"HerbConstraints.@csgrammar_annotated","text":"@csgrammar_annotated Define an annotated grammar and return it as a ContextSensitiveGrammar. Allows for adding optional annotations per rule. As well as that, allows for adding optional labels per rule, which can be referenced in annotations. Syntax is backwards-compatible with @csgrammar. Examples:\n\ng₁ = @csgrammar_annotated begin\n Element = 1\n Element = x\n Element = Element + Element := commutative\n Element = Element * Element := (commutative, transitive)\nend\n\ng₁ = @csgrammar_annotated begin\n Element = 1\n Element = x\n Element = Element + Element := forbidden_path([3, 1])\n Element = Element * Element := (commutative, transitive)\nend\n\ng₁ = @csgrammar_annotated begin\n one:: Element = 1\n variable:: Element = x\n addition:: Element = Element + Element := (\n commutative,\n transitive,\n forbidden_path([:addition, :one]) || forbidden_path([:one, :variable])\n )\n multiplication:: Element = Element * Element := (commutative, transitive)\nend\n\n\n\n\n\n","category":"macro"},{"location":"HerbConstraints/#Base.collect-Tuple{HerbConstraints.StateStack}","page":"HerbConstraints.jl","title":"Base.collect","text":"function Base.collect(stack::StateStack)\n\nReturn the internal Vector representation of the stack. !!! warning: The returned vector is read-only.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.findall-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.findall","text":"Returns all elements in the set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.findfirst-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.findfirst","text":"Returns the minimum value in the set. This function name is used instead of min to allow code reuse for domains of type BitVector and StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.findlast-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.findlast","text":"Returns the maximum value in the set. This function name is used instead of min to allow code reuse for domains of type BitVector and StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.getindex-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"Base.getindex","text":"Checks if value val is in StateSparseSet s. !!! warning: This allows a StateSparseSet to be used as if it were a BitVector representation of a set\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.in-Tuple{Int64, HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.in","text":"Checks if value val is in StateSparseSet s.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.in-Union{Tuple{T}, Tuple{HerbConstraints.StateStack{T}, T}} where T","page":"HerbConstraints.jl","title":"Base.in","text":"function Base.in(stack::StateStack, value)::Bool\n\nChecks whether the value is in the stack.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.length-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.length","text":"Returns the number of values in the StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.push!-Tuple{HerbConstraints.StateStack, Any}","page":"HerbConstraints.jl","title":"Base.push!","text":"function Base.push!(stack::StateStack, item)\n\nPlace an item on top of the stack.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.show-Tuple{IO, HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.show","text":"Pretty print the StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.size-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.size","text":"Returns the number of values in the StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.size-Tuple{HerbConstraints.StateStack}","page":"HerbConstraints.jl","title":"Base.size","text":"function Base.size(stack::StateStack)\n\nGet the current size of the stack.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.sum-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.sum","text":"Returns the number of values in the StateSparseSet. !!! warning: This is not actually the sum of the set. It is the length of the set. This allows a StateSparseSet to be used as if it were a BitVector representation of a set\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._contains-Tuple{AbstractRuleNode, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._contains","text":"_contains(node::AbstractRuleNode, rule::Int)::Bool\n\nRecursive helper function for the LocalContains constraint Returns one of the following:\n\ntrue, if the node does contains the rule\nfalse, if the node does not contain the rule\nVector{AbstractHole}, if the node contains the rule if one the holes gets filled with the target rule\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._count_occurrences!-Tuple{AbstractRuleNode, Int64, Vector{AbstractHole}}","page":"HerbConstraints.jl","title":"HerbConstraints._count_occurrences!","text":"function _count_occurrences!(node::AbstractRuleNode, rule::Int, holes::Vector{AbstractHole})::Int\n\nRecursive helper function for the LocalUnique constraint. Returns the number of certain occurrences of the rule in the tree. All holes that potentially can hold the target rule are stored in the holes vector.\n\n!!! warning: Stops counting if the rule occurs more than once. Counting beyond 2 is not needed for LocalUnique. \n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._count_occurrences-Tuple{AbstractRuleNode, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._count_occurrences","text":"function _count_occurrences(rule::Int, node::AbstractRuleNode)::Int\n\nRecursively counts the number of occurrences of the rule in the node.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._count_occurrences-Tuple{Vector{AbstractHole}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._count_occurrences","text":"function _count_occurrences(holes::Vector{AbstractHole}, rule::Int)\n\nCounts the occurences of the rule in the cached list of holes.\n\n!!! warning: Stops counting if the rule occurs more than once. Counting beyond 2 is not needed for LocalUnique. \n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._exchange_positions!-Tuple{HerbConstraints.StateSparseSet, Int64, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._exchange_positions!","text":"Exchanges the positions in the internal representation of the StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._update_bounds_val_removed!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._update_bounds_val_removed!","text":"This function should be called whenever the minimum or maximum value from the set might have been removed. The minimum and maximum value of the set will be updated to the actual bounds of the set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._update_max_val_removed!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._update_max_val_removed!","text":"This function should be called whenever the maximum value from the set might have been removed. The maximum value of the set will be updated to the actual maximum of the set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._update_min_val_removed!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._update_min_val_removed!","text":"This function should be called whenever the minimum value from the set might have been removed. The minimum value of the set will be updated to the actual minimum of the set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.annotation2constraint-Tuple{Any, Int64, Vector{String}}","page":"HerbConstraints.jl","title":"HerbConstraints.annotation2constraint","text":"Converts an annotation to a constraint. commutative: creates an Ordered constraint transitive: creates an (incorrect) Forbidden constraint forbidden_path(path::Vector{Union{Symbol, Int}}): creates a ForbiddenPath constraint with the original rule included ... || ...: creates a OneOf constraint (also works with ... || ... || ... et cetera, though not very performant)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.are_disjoint-Tuple{BitVector, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.are_disjoint","text":"are_disjoint(domain1::BitVector, domain2::BitVector)::Bool\n\nReturns true if there is no overlap in values between domain1 and domain2\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.are_disjoint-Tuple{HerbConstraints.StateSparseSet, HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"HerbConstraints.are_disjoint","text":"are_disjoint(set1::StateSparseSet, set2::StateSparseSet)\n\nReturns true if there is no overlap in values between set1 and set2\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.backup!-Tuple{StateInt}","page":"HerbConstraints.jl","title":"HerbConstraints.backup!","text":"Should be called whenever the state of a StateInt is modified. Creates a StateIntBackup for the given StateInt. Only backup the value if this integer has not been stored during this state before Example usecase:\n\na = StateInt(sm, 10)\nsave_state!(sm)\nset_value!(a, 9) #backup value 10\nset_value!(a, 8) #no need to backup again\nset_value!(a, 3) #no need to backup again\nrestore!(sm) #restores a to value 10\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{Contains, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::Contains, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the Contains constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{ContainsSubtree, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::ContainsSubtree, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the ContainsSubtree constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{Forbidden, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::Forbidden, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the Forbidden constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{ForbiddenSequence, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::ForbiddenSequence, tree::AbstractRuleNode; sequence_started=false)::Bool\n\nChecks if the given AbstractRuleNode tree abides the ForbiddenSequence constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{Ordered, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::Ordered, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the Ordered constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{Unique, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"function check_tree(c::Unique, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the Unique constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.contains_varnode-Tuple{AbstractRuleNode, Symbol}","page":"HerbConstraints.jl","title":"HerbConstraints.contains_varnode","text":"contains_varnode(rn::AbstractRuleNode, name::Symbol)\n\nChecks if an AbstractRuleNode tree contains a VarNode with the given name.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.deactivate!-Tuple{GenericSolver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.deactivate!","text":"deactivate!(solver::GenericSolver, constraint::AbstractLocalConstraint)\n\nFunction that should be called whenever the constraint is already satisfied and never has to be repropagated.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.deactivate!-Tuple{UniformSolver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.deactivate!","text":"deactivate!(solver::UniformSolver, constraint::AbstractLocalConstraint)\n\nFunction that should be called whenever the constraint is already satisfied and never has to be repropagated.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.decrement!-Tuple{StateInt}","page":"HerbConstraints.jl","title":"HerbConstraints.decrement!","text":"Decrease the value of the integer by 1\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.fix_point!-Tuple{Solver}","page":"HerbConstraints.jl","title":"HerbConstraints.fix_point!","text":"fix_point!(solver::Solver)\n\nPropagate constraints in the current state until no further dedecutions can be made\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.freeze_state-Tuple{StateHole}","page":"HerbConstraints.jl","title":"HerbConstraints.freeze_state","text":"freeze_state(hole::StateHole)::RuleNode\n\nConverts a [StateHole])(@ref) to a [RuleNode]@(ref). The hole and its children are assumed to be filled.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_grammar-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_grammar","text":"function get_grammar(solver::GenericSolver)::AbstractGrammar\n\nGet the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_grammar-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_grammar","text":"function get_grammar(solver::UniformSolver)::AbstractGrammar\n\nGet the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_hole_at_location-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.get_hole_at_location","text":"get_hole_at_location(solver::GenericSolver, location::Vector{Int})::AbstractHole\n\nGet the node at path location and assert it is a AbstractHole.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_hole_at_location-Tuple{UniformSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.get_hole_at_location","text":"get_hole_at_location(solver::UniformSolver, path::Vector{Int})\n\nGet the hole that is located at the provided path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_intersection-Tuple{BitVector, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.get_intersection","text":"get_intersection(domain1::BitVector, domain2::BitVector)::Bool\n\nReturns all the values that are in both domain1 and domain2\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_max_depth-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_max_depth","text":"function get_max_depth(solver::GenericSolver)::SolverState\n\nGet the maximum depth of the tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_max_size-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_max_size","text":"function get_max_depth(solver::GenericSolver)::SolverState\n\nGet the maximum number of AbstractRuleNodes allowed inside the tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_nodes-Tuple{Any}","page":"HerbConstraints.jl","title":"HerbConstraints.get_nodes","text":"get_nodes(solver)\n\nReturn an iterator over all nodes in the tree\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_nodes_on_path-Tuple{AbstractRuleNode, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.get_nodes_on_path","text":"function get_nodes_on_path(root::AbstractRuleNode, path::Vector{Int})::Vector{AbstractRuleNode}\n\nGets a list of nodes on the path, starting (and including) the root.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_priority-Tuple{AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.get_priority","text":"function get_priority(::AbstractLocalConstraint)\n\nUsed to determine which constraint to propagate first in fix_point!. Constraints with fast propagators and/or strong inference should be propagated first.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_starting_symbol-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_starting_symbol","text":"function get_starting_symbol(solver::GenericSolver)::Symbol\n\nGet the symbol from the solver.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_state-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_state","text":"function get_state(solver::GenericSolver)::SolverState\n\nGet the current [SolverState]@(ref) of the solver.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_tree-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_tree","text":"function get_tree(solver::GenericSolver)::AbstractRuleNode\n\nReturns the number of AbstractRuleNodes in the tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_tree-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_tree","text":"function get_tree(solver::UniformSolver)::AbstractRuleNode\n\nGet the root of the tree. This remains the same instance throughout the entire search.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_tree_size-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_tree_size","text":"function get_tree_size(solver::GenericSolver)::Int\n\nReturns the number of AbstractRuleNodes in the tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_value-Tuple{StateInt}","page":"HerbConstraints.jl","title":"HerbConstraints.get_value","text":"Get the value of the stateful integer\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.increment!-Tuple{StateInt}","page":"HerbConstraints.jl","title":"HerbConstraints.increment!","text":"Increase the value of the integer by 1\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.is_subdomain-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.is_subdomain","text":"is_subdomain(specific_tree::AbstractRuleNode, general_tree::AbstractRuleNode)\n\nChecks if the specific_tree can be obtained by repeatedly removing values from the general_tree\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.is_subdomain-Tuple{BitVector, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.is_subdomain","text":" is_subdomain(subdomain::BitVector, domain::BitVector)\n\nChecks if subdomain is a subdomain of domain. Example: [0, 0, 1, 0] is a subdomain of [0, 1, 1, 1]\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.isfeasible-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.isfeasible","text":"isfeasible(solver::GenericSolver)\n\nReturns true if no inconsistency has been detected. Used in several ways:\n\nIterators should check for infeasibility to discard infeasible states\nAfter any tree manipulation with the possibility of an inconsistency (e.g. remove_below!, remove_above!, remove!)\nfix_point! should check for infeasibility to clear its schedule and return\nSome GenericSolver functions assert a feasible state for debugging purposes @assert isfeasible(solver)\nSome GenericSolver functions have a guard that skip the function on an infeasible state: if !isfeasible(solver) return end\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.isfeasible-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.isfeasible","text":"isfeasible(solver::UniformSolver)\n\nReturns true if no inconsistency has been detected.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.load_state!-Tuple{GenericSolver, SolverState}","page":"HerbConstraints.jl","title":"HerbConstraints.load_state!","text":"load_state!(solver::GenericSolver, state::SolverState)\n\nOverwrites the current state with the given state\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.make_equal!-Tuple{Solver, Union{AbstractHole, RuleNode}, Union{DomainRuleNode, AbstractHole, RuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.make_equal!","text":"function make_equal!(solver::Solver, node1::AbstractRuleNode, node2::AbstractRuleNode)::MakeEqualResult\n\nTree manipulation that enforces node1 == node2 if unambiguous.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.make_less_than_or_equal!-Tuple{Solver, Union{AbstractHole, RuleNode}, Union{AbstractHole, RuleNode}, Vector{Tuple{AbstractHole, Int64}}}","page":"HerbConstraints.jl","title":"HerbConstraints.make_less_than_or_equal!","text":"function make_less_than_or_equal!(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole}, guards::Vector{Tuple{AbstractHole, Int}})::LessThanOrEqualResult\n\nHelper function that keeps track of the guards\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.make_less_than_or_equal!-Tuple{Solver, Union{AbstractHole, RuleNode}, Union{AbstractHole, RuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.make_less_than_or_equal!","text":"function make_less_than_or_equal!(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole})::LessThanOrEqualResult\n\nEnsures that n1<=n2 by removing impossible values from holes. Returns one of the following results:\n\nLessThanOrEqualSuccess. When [n1<=n2].\nLessThanOrEqualHardFail. When [n1>n2] or when the solver state is infeasible.\nLessThanOrEqualSoftFail. When no further deductions can be made, but [n1<=n2] and [n1>n2] are still possible.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.make_less_than_or_equal!-Tuple{Solver, Vector{AbstractRuleNode}, Vector{AbstractRuleNode}, Vector{Tuple{AbstractHole, Int64}}}","page":"HerbConstraints.jl","title":"HerbConstraints.make_less_than_or_equal!","text":"function make_less_than_or_equal!(solver::Solver, nodes1::Vector{AbstractRuleNode}, nodes2::Vector{AbstractRuleNode}, guards::Vector{Tuple{AbstractHole, Int}})::LessThanOrEqualResult\n\nHelper function that tiebreaks on children.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.new_state!-Tuple{GenericSolver, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.new_state!","text":"new_state!(solver::GenericSolver, tree::AbstractRuleNode)\n\nOverwrites the current state and propagates constraints on the tree from the ground up\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_new_node-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_new_node","text":"notify_new_node(solver::GenericSolver, event_path::Vector{Int})\n\nNotify all constraints that a new node has appeared at the event_path by calling their respective on_new_node function.\n\nwarning: Warning\nThis does not notify the solver about nodes below the event_path. In that case, call notify_new_nodes instead.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_new_nodes-Tuple{GenericSolver, AbstractRuleNode, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_new_nodes","text":"notify_new_nodes(solver::GenericSolver, node::AbstractRuleNode, path::Vector{Int})\n\nNotify all grammar constraints about the new node and its (grand)children\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_new_nodes-Tuple{UniformSolver, AbstractRuleNode, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_new_nodes","text":"notify_new_nodes(solver::UniformSolver, node::AbstractRuleNode, path::Vector{Int})\n\nNotify all grammar constraints about the new node and its (grand)children\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_tree_manipulation-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_tree_manipulation","text":"notify_tree_manipulation(solver::GenericSolver, event_path::Vector{Int})\n\nNotify subscribed constraints that a tree manipulation has occured at the event_path by scheduling them for propagation\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_tree_manipulation-Tuple{UniformSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_tree_manipulation","text":"notify_tree_manipulation(solver::UniformSolver, event_path::Vector{Int})\n\nNotify subscribed constraints that a tree manipulation has occured at the event_path by scheduling them for propagation\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.partition-Tuple{Hole, ContextSensitiveGrammar}","page":"HerbConstraints.jl","title":"HerbConstraints.partition","text":"partition(hole::Hole, grammar::ContextSensitiveGrammar)::Vector{BitVector}\n\nPartition a Hole into subdomains grouped by childtypes\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{AbstractRuleNode, AbstractRuleNode, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"Generic fallback function for commutativity. Swaps arguments 1 and 2, then dispatches to a more specific signature. If this gets stuck in an infinite loop, the implementation of an AbstractRuleNode type pair is missing.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(rn::AbstractRuleNode, mn::AbstractRuleNode)::PatternMatchResult\n\nRecursively tries to match AbstractRuleNode rn with AbstractRuleNode mn. Returns a PatternMatchResult that describes if the pattern was matched.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{AbstractRuleNode, DomainRuleNode, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(node::AbstractRuleNode, domainrulenode::DomainRuleNode, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult\n\nComparing any AbstractRuleNode with a DomainRuleNode\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{AbstractRuleNode, VarNode, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(rn::AbstractRuleNode, var::VarNode, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult\n\nComparing any AbstractRuleNode with a named VarNode\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{Union{AbstractHole, RuleNode}, Union{AbstractHole, RuleNode}, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole}, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult\n\nComparing any pair of Rulenode and/or AbstractHole. It is important to note that some AbstractHoles are already filled and should be treated as RuleNode. This is why this function is dispatched on (isfilled(h1), isfilled(h2)). The '(RuleNode, AbstractHole)' case could still include two nodes of type AbstractHole, but one of them should be treated as a rulenode.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{Vector{AbstractRuleNode}, Vector{AbstractRuleNode}, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(rns::Vector{AbstractRuleNode}, mns::Vector{AbstractRuleNode}, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult\n\nPairwise tries to match two ordered lists of AbstractRuleNodes. Typically, this function is used to pattern match the children two AbstractRuleNodes.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.post!-Tuple{GenericSolver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.post!","text":"post!(solver::GenericSolver, constraint::AbstractLocalConstraint)\n\nImposes the constraint to the current state. By default, the constraint will be scheduled for its initial propagation. Constraints can overload this method to add themselves to notify lists or triggers.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.post!-Tuple{UniformSolver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.post!","text":"post!(solver::UniformSolver, constraint::AbstractLocalConstraint)\n\nPost a new local constraint. Converts the constraint to a state constraint and schedules it for propagation.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{GenericSolver, LocalContainsSubtree}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(::GenericSolver, ::LocalContainsSubtree)\n\n!!! warning: LocalContainsSubtree uses stateful properties and can therefore not be propagated in the GenericSolver. (The GenericSolver shares constraints among different states, so they cannot use stateful properties)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalContains}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalContains)\n\nEnforce that the rule appears at or below the path at least once. Uses a helper function to retrieve a list of holes that can potentially hold the target rule. If there is only a single hole that can potentially hold the target rule, that hole will be filled with that rule.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalForbiddenSequence}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalForbiddenSequence)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalForbidden}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalForbidden)\n\nEnforce that the forbidden tree does not occur at the path. The forbidden tree is matched against the AbstractRuleNode located at the path. Deductions are based on the type of the PatternMatchResult returned by the pattern_match function.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalOrdered}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalOrdered)\n\nEnforce that the VarNodes in the tree are in the specified order. First the node located at the path is matched to see if the ordered constraint applies here. The nodes matching the variables are stored in the vars dictionary. Then the order is enforced within the make_less_than_or_equal! tree manipulation. \n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalUnique}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalUnique)\n\nEnforce that the rule appears at or below the path at least once. Uses a helper function to retrieve a list of holes that can potentially hold the target rule. If there is only a single hole that can potentially hold the target rule, that hole will be filled with that rule.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{UniformSolver, LocalContainsSubtree}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::UniformSolver, c::LocalContainsSubtree)\n\nEnforce that the tree appears at or below the path at least once. Nodes that can potentially become the target sub-tree are considered candidates. In case of multiple candidates, a stateful set of indices is used to keep track of active candidates.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{GenericSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)\n\nRemove rule_index from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{GenericSolver, Vector{Int64}, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(solver::GenericSolver, path::Vector{Int}, rules::Vector{Int})\n\nRemove all rules from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(set::StateSparseSet, val::Int)\n\nRemoves value val from StateSparseSet set. Returns true if val was in set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{UniformSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(solver::Solver, path::Vector{Int}, rule_index::Int)\n\nRemove rule_index from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{UniformSolver, Vector{Int64}, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(solver::UniformSolver, path::Vector{Int}, rules::Vector{Int})\n\nRemove all rules from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_above!-Tuple{GenericSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_above!","text":"remove_above!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)\n\nReduce the domain of the hole located at the path by removing all rules indices above rule_index Example: rule_index = 2. hole with domain [1, 1, 0, 1] gets reduced to [1, 0, 0, 0] and gets simplified to a RuleNode\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_above!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_above!","text":"Remove all the values greater than val from the set\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_above!-Tuple{UniformSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_above!","text":"remove_above!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)\n\nReduce the domain of the hole located at the path by removing all rules indices above rule_index Example: rule_index = 2. hole with domain {1, 2, 4} gets reduced to {1}\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_all_but!-Tuple{GenericSolver, Vector{Int64}, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_all_but!","text":"remove_all_but!(solver::GenericSolver, path::Vector{Int}, new_domain::BitVector)\n\nReduce the domain of the hole located at the path, to the new_domain. It is assumed the path points to a hole, otherwise an exception will be thrown. It is assumed new_domain ⊆ domain. For example: [1, 0, 1, 0] ⊆ [1, 0, 1, 1]\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_all_but!-Tuple{GenericSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_all_but!","text":"remove_all_but!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)\n\nFill in the hole located at the path with rule rule_index. It is assumed the path points to a hole, otherwise an exception will be thrown. It is assumed rule_index ∈ hole.domain.\n\n!!! warning: If the hole is known to be in the current tree, the hole can be passed directly. The caller has to make sure that the hole instance is actually present at the provided path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_all_but!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_all_but!","text":"remove_all_but!(set::StateSparseSet, val::Int)::Bool\n\nRemoves all values from StateSparseSet set, except val\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_all_but!-Tuple{UniformSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_all_but!","text":"remove_all_but!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)\n\nFill in the hole located at the path with rule rule_index.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_below!-Tuple{GenericSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_below!","text":"remove_below!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)\n\nReduce the domain of the hole located at the path by removing all rules indices below rule_index Example: rule_index = 2. hole with domain [1, 1, 0, 1] gets reduced to [0, 1, 0, 1]\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_below!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_below!","text":"Remove all the values less than val from the set\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_below!-Tuple{UniformSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_below!","text":"remove_below!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)\n\nReduce the domain of the hole located at the path by removing all rules indices below rule_index Example: rule_index = 2. hole with domain {1, 2, 4} gets reduced to {2, 4}\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_node!-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_node!","text":"function remove_node!(solver::GenericSolver, path::Vector{Int})\n\nRemove the node at the given path by substituting it with a hole of the same symbol.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.restore!-Tuple{HerbConstraints.StateIntBackup}","page":"HerbConstraints.jl","title":"HerbConstraints.restore!","text":"Restores the StateInt stored in the StateIntBackup to its original value\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.restore!-Tuple{HerbConstraints.StateManager}","page":"HerbConstraints.jl","title":"HerbConstraints.restore!","text":"Reverts all the backups since the last save_state!.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.restore!-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.restore!","text":"Restore state of the solver until the last save_state!\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.save_state!-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.save_state!","text":"save_state!(solver::GenericSolver)\n\nReturns a copy of the current state that can be restored by calling load_state!(solver, state)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.save_state!-Tuple{HerbConstraints.StateManager}","page":"HerbConstraints.jl","title":"HerbConstraints.save_state!","text":"Make a backup of the current state. Return to this state by calling restore!.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.save_state!-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.save_state!","text":"Save the current state of the solver, can restored using restore!\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.schedule!-Tuple{Solver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.schedule!","text":"schedule(solver::GenericSolver, constraint::AbstractLocalConstraint)\n\nSchedules the constraint for propagation.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.set_infeasible!-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.set_infeasible!","text":"set_infeasible!(solver::GenericSolver)\n\nFunction to be called if any inconsistency has been detected\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.set_infeasible!-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.set_infeasible!","text":"set_infeasible!(solver::Solver)\n\nFunction to be called if any inconsistency has been detected\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.set_value!-Tuple{StateInt, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.set_value!","text":"Set the value of the integer to the given val\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.shouldschedule-Tuple{Solver, AbstractLocalConstraint, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.shouldschedule","text":"shouldschedule(solver::Solver, constraint::AbstractLocalConstraint, path::Vector{Int})::Bool\n\nFunction that is called when a tree manipulation occured at the path. Returns true if the constraint should be scheduled for propagation.\n\nDefault behavior: return true iff the manipulation happened at or below the constraint path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.shouldschedule-Tuple{Solver, LocalForbiddenSequence, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.shouldschedule","text":"shouldschedule(::Solver, constraint::LocalForbiddenSequence, path::Vector{Int})::Bool\n\nReturn true iff the manipulation happened at or above the constraint path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.simplify_hole!-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.simplify_hole!","text":"simplify_hole!(solver::GenericSolver, path::Vector{Int})\n\nTakes a Hole and tries to simplify it to a UniformHole or RuleNode. If the domain of the hole is empty, the state will be marked as infeasible\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.substitute!-Tuple{GenericSolver, Vector{Int64}, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.substitute!","text":"substitute!(solver::GenericSolver, path::Vector{Int}, new_node::AbstractRuleNode; is_domain_increasing::Union{Nothing, Bool}=nothing)\n\nSubstitute the node at the path, with a new_node.\n\nis_domain_increasing: indicates if all grammar constraints should be repropagated from the ground up.\n\nDomain increasing substitutions are substitutions that cannot be achieved by repeatedly removing values from domains. Example of an domain increasing event: hole[{3, 4, 5}] -> hole[{1, 2}]. Example of an domain decreasing event: hole[{3, 4, 5}] -> rulenode(4, [hole[{1, 2}], rulenode(1)]).\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.contains_hole-Tuple{StateHole}","page":"HerbConstraints.jl","title":"HerbCore.contains_hole","text":"contains_hole(hole::StateHole)::Bool\n\nReturns true if the hole or any of its (grand)children are not filled.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_node_at_location-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbCore.get_node_at_location","text":"HerbCore.get_node_at_location(solver::GenericSolver, location::Vector{Int})::AbstractRuleNode\n\nGet the node at path location.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_node_at_location-Tuple{UniformSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbCore.get_node_at_location","text":"get_node_at_location(solver::UniformSolver, path::Vector{Int})\n\nGet the node that is located at the provided path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_path-Tuple{GenericSolver, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbCore.get_path","text":"get_path(solver::GenericSolver, node::AbstractRuleNode)\n\nGet the path at which the node is located.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_path-Tuple{UniformSolver, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbCore.get_path","text":"get_path(solver::UniformSolver, node::AbstractRuleNode)\n\nGet the path at which the node is located.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_rule-Tuple{StateHole}","page":"HerbConstraints.jl","title":"HerbCore.get_rule","text":"get_rule(hole::StateHole)::Int\n\nAssuming the hole has domain size 1, get the rule it is currently assigned to.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.isfilled-Tuple{StateHole}","page":"HerbConstraints.jl","title":"HerbCore.isfilled","text":"isfilled(hole::StateHole)::Bool\n\nHoles with domain size 1 are fixed to a rule. Returns whether the hole has domain size 1. (holes with an empty domain are not considered to be fixed)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Index","page":"HerbConstraints.jl","title":"Index","text":"","category":"section"},{"location":"HerbConstraints/","page":"HerbConstraints.jl","title":"HerbConstraints.jl","text":"","category":"page"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"\n\n\n\n\n

Advanced Search Procedures in Herb.jl

A more verbose getting started with Herb.jl described the concept of a program space and showed how to search it with Herb.jl, using a simple breadth-first-search (BFS) iterator for the search. This tutorial takes a closer look at advanced search procedures hat can be employed to find a solution program to a program synthesis problem.

More specifically, you will learn about

  • Parameters that can be specified and their effect on the search procedure.

  • Deterministic search methods BFS and DFS.

  • Stochastic search methods, which introduce randomness to search the program space. We will look at Metropolis-Hastings, Very Large Scale Neighbourhood Search, Simulated Annealing and Genetic Search.

\n\n
using PlutoUI
\n\n\n
TableOfContents()
\n\n\n\n

Let's import all the Herb modules that we will use throughout the tutorial.

\n\n
using HerbGrammar, HerbSpecification, HerbSearch, HerbInterpret, HerbConstraints, HerbCore\n
\n\n\n\n

We start with a simple grammar:.

\n\n
g_1 = @csgrammar begin\n    Number = |(1:2)\n    Number = x\n    Number = Number + Number\n    Number = Number * Number\nend
\n
1: Number = 1\n2: Number = 2\n3: Number = x\n4: Number = Number + Number\n5: Number = Number * Number\n
\n\n\n

Let's use the simple program 2x+1 as our problem and generate some input-output examples for the problem specification.

\n\n
problem_1 = Problem([IOExample(Dict(:x => x), 2x+1) for x ∈ 1:5])
\n
Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 3), IOExample(Dict{Symbol, Any}(:x => 2), 5), IOExample(Dict{Symbol, Any}(:x => 3), 7), IOExample(Dict{Symbol, Any}(:x => 4), 9), IOExample(Dict{Symbol, Any}(:x => 5), 11)])
\n\n","category":"page"},{"location":"tutorials/advanced_search/#Parameters","page":"Advanced Search Procedures","title":"Parameters","text":"","category":"section"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"
\n

Search procedures typically have some hyperparameters that you can configure.

max_depth

max_depth controls the maximum depth of the program trees that are explored during the search, effectively limiting the size and complexity of the synthesized program. The parameter is configured as part of the iterator.

In the following example, we consider two different values for max_depth.

\n\n
iterator_1 = BFSIterator(g_1, :Number, max_depth=3)
\n
BFSIterator(GenericSolver(1: Number = 1\n2: Number = 2\n3: Number = x\n4: Number = Number + Number\n5: Number = Number * Number\n, SolverState(hole[Bool[1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 3))
\n\n
iterator_2 = BFSIterator(g_1, :Number, max_depth=6)
\n
BFSIterator(GenericSolver(1: Number = 1\n2: Number = 2\n3: Number = x\n4: Number = Number + Number\n5: Number = Number * Number\n, SolverState(hole[Bool[1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 6))
\n\n\n

To see the effect max_depth has on the number of memory allocations made during the program synthesis process, we use the @time macro.

\n\n
begin\n    println(\"Solution for max_depth = 3:\")\n    solution_1 = @time synth(problem_1, iterator_1)\n    println(solution_1)\n    println(\"----------------------\")\n    println(\"Solution for max_depth = 6:\")\n    solution_2 = @time synth(problem_1, iterator_2)\n    println(solution_2)\nend
\n\n\n\n

While increasing max_depth allows us to explore more complex and deeper program trees, which may lead to a better solution, it also requires more memory allocation and can increase the execution time.

\n\n\n

max_enumerations

max_enumerations defines the maximum number of candidate programs that can be evaluated before the search is terminated.

Let's explore how many enumerations are necessary to solve our simple problem.

\n\n
for i in range(1, 50)\n        println(i, \" enumerations\")\n        iterator = BFSIterator(g_1, :Number, max_depth=i)\n        solution = @time synth(problem_1, iterator)\n        println(solution)\nend
\n\n\n\n

At i = 3, we observe that an optimal program is found. Increasing the number of enumerations beyond that does not affect the solution or the number of memory allocations.

\n\n\n

allow_evaluation_errors

A final parameter we consider here is allow_evaluation_errors, which is false by default. When true, the search continues even if an exception occurs during the evaluation of a candidate program. This allows the search process to handle faulty candidate programs and explore other ones, instead of throwing an error and terminating prematurely.

We will use a new example to see the effect of allow_evaluation_errors. We begin defining a new simple grammar. We then create some input-output examples to specify the problem we want to solve. This time, we choose a problem that we cannot solve with the provided grammar.

\n\n
g_2 = @csgrammar begin\n    Number = 1\n    List = []\n    Index = List[Number]\nend
\n
1: Number = 1\n2: List = []\n3: Index = List[Number]\n
\n\n
problem_2 = Problem([IOExample(Dict(), x) for x ∈ 1:5])
\n
Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(), 1), IOExample(Dict{Symbol, Any}(), 2), IOExample(Dict{Symbol, Any}(), 3), IOExample(Dict{Symbol, Any}(), 4), IOExample(Dict{Symbol, Any}(), 5)])
\n\n
iterator_3 = BFSIterator(g_2, :Index, max_depth=2)
\n
BFSIterator(GenericSolver(1: Number = 1\n2: List = []\n3: Index = List[Number]\n, SolverState(3{2,1}, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2))
\n\n
using Test
\n\n\n
Test.@test_throws HerbSearch.EvaluationError synth(problem_2, iterator_3)
\n
Test Passed\n      Thrown: HerbSearch.EvaluationError
\n\n\n

As expected, an exception occurs during the synthesis process. Now we try the same again, with allow_evaluation_errors=true.

\n\n
solution_4 = synth(problem_2, iterator_3, allow_evaluation_errors=true)
\n
(3{2,1}, suboptimal_program)
\n\n
println(\"solution: \", solution_4)
\n\n\n\n

\"This time we find a solution, although a suboptimal one.

\n\n","category":"page"},{"location":"tutorials/advanced_search/#Top-down-search","page":"Advanced Search Procedures","title":"Top-down search","text":"","category":"section"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"
\n

Herb.jl provides already implemented, ready-to-use search methods. The core building block of the search is the program iterator, which represents a walk through the program space. All program iterators share the top-level abstract type ProgramIterator. For more information on iterators and how to customize them, see this tutorial.

First, we explore two fundamental deterministic top-down search algorithms: breadth-first search (BFS) and depth-first search (DFS). Both algorithms are implemented using the abstract type TopDownIterator, which can be customized through the functions

  • priority_function

  • derivation_heuristic

  • hole_heuristic

\n\n\n

First, we explore two fundamental deterministic top-down search algorithms: breadth-first search (BFS) and depth-first search (DFS). Both algorithms are implemented using the abstract type TopDownIterator, which can be customized through the functions priorityfunction, derivationheuristic, and hole_heuristic.

Breadth-First Search

The BFSIterator enumerates all possible programs at a given depth before progressing to the next level, ensuring that trees are explored in increasing order of size. This guarantees that smaller programs are evaluated first, and larger, more complex ones are considered only after all smaller ones have been processed.

To explore BFSIterator, we define another very simple grammar.

\n\n
g_3 = @csgrammar begin\n        Real = 1 | 2\n        Real = Real * Real\nend
\n
1: Real = 1\n2: Real = 2\n3: Real = Real * Real\n
\n\n\n

Next, we define a BFSIterator with a max_depth of 2 and a max_size of infinite (which we approximate with the maximum value of Int), and a starting symbol of type Real. By default, BFSIterator uses the heuristic 'left-most first', i.e., the left-most child in the tree is always explored first.

\n\n
iterator_bfs = BFSIterator(g_3, :Real, max_depth=2, max_size=typemax(Int))
\n
BFSIterator(GenericSolver(1: Real = 1\n2: Real = 2\n3: Real = Real * Real\n, SolverState(hole[Bool[1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2))
\n\n\n

To see all possible solution programs the iterator explores, we use collect. It returs a list of the programs, ordered by increasing size and depth.

\n\n
programs_bfs = collect(iterator_bfs)
\n
6-element Vector{RuleNode}:\n 1,\n 2,\n 3{1,1}\n 3{1,2}\n 3{2,2}\n 3{2,1}
\n\n
println(programs_bfs)
\n\n\n\n

Let's verify that the iterator returns the programs we expect (keep in mind we use a leftmost-first heuristic).

\n\n
answer_programs = [\n    RuleNode(1),\n    RuleNode(2),\n    RuleNode(3, [RuleNode(1), RuleNode(1)]),\n    RuleNode(3, [RuleNode(1), RuleNode(2)]),\n    RuleNode(3, [RuleNode(2), RuleNode(1)]),\n    RuleNode(3, [RuleNode(2), RuleNode(2)])\n]
\n
6-element Vector{RuleNode}:\n 1,\n 2,\n 3{1,1}\n 3{1,2}\n 3{2,1}\n 3{2,2}
\n\n
println(all(p ∈ programs_bfs for p ∈ answer_programs))
\n\n\n\n

Depth-First Search

The DFSIterator explores one branch of the search tree at a time, fully traversing it unitl a correct program is found or the specified max_depth is reached. Only after completing the current branch, it proceeds to the next branch.

As before, we collect the candidate programs using the same grammar, but a DFSIterator.

\n\n
iterator_dfs = DFSIterator(g_3, :Real, max_depth=2, max_size=typemax(Int))
\n
DFSIterator(GenericSolver(1: Real = 1\n2: Real = 2\n3: Real = Real * Real\n, SolverState(hole[Bool[1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2))
\n\n
programs_dfs = collect(iterator_dfs)
\n
6-element Vector{RuleNode}:\n 1,\n 3{1,1}\n 3{1,2}\n 3{2,2}\n 3{2,1}\n 2,
\n\n
println(programs_dfs)
\n\n\n\n

DFSIterator also uses by default a leftmost-first heuristic. If we want to use a rightmost-first heuristic instead, we can create our own iterator DFSIteratorRightmost as a sub-type of TopDownIterator, using the @programiterator macro. Then we implement the functions priority_function and hole_heuristic. Also see the tutorial Top Down Iterator for how to build iterators is Herb.jl.

\n\n
@programiterator DFSIteratorRightmost() <: TopDownIterator
\n
DFSIteratorRightmost
\n\n\n

By default, priority_function for a TopDownIterator is that of a BFS iterator. Hence, we need to provide a new implementation.

\n\n
function priority_function(\n    ::DFSIteratorRightmost, \n    ::AbstractGrammar, \n    ::AbstractRuleNode, \n    parent_value::Union{Real, Tuple{Vararg{Real}}},\n    isrequeued::Bool\n)\n    if isrequeued\n        return parent_value;\n    end\n    return parent_value - 1;\nend
\n
priority_function (generic function with 1 method)
\n\n\n

Next, we need to implement the hole_heuristic to be rightmost-first.

\n\n
function hole_heuristic(::DFSIteratorRightmost, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n    return heuristic_rightmost(node, max_depth);\nend
\n
hole_heuristic (generic function with 1 method)
\n\n
iteratordfs_rightmost = DFSIteratorRightmost(g_3, :Real, max_depth=2, max_size=typemax(Int))
\n
DFSIteratorRightmost(GenericSolver(1: Real = 1\n2: Real = 2\n3: Real = Real * Real\n, SolverState(hole[Bool[1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2))
\n\n
programs_dfs_rightmost = collect(iteratordfs_rightmost)
\n
6-element Vector{RuleNode}:\n 1,\n 2,\n 3{1,1}\n 3{1,2}\n 3{2,2}\n 3{2,1}
\n\n
println(programs_dfs_rightmost)
\n\n\n\n

We observe that the order of programs has changed. We can also test if both DFS iterators return the same programs:

\n\n
Set(programs_dfs)==Set(programs_dfs_rightmost)
\n
true
\n\n","category":"page"},{"location":"tutorials/advanced_search/#Stochastic-search","page":"Advanced Search Procedures","title":"Stochastic search","text":"","category":"section"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"
\n

While deterministic search methods explore the search space in a predictable way, stochastic ones introduce randomness to allow for more flexibility.

In this section, we will look at the stochastic search algorithms: Metropolis-Hastings (MH), Very Large Scale Neighbourhood Search (VLSNS), and Simulated Annealing (SA). In Herb.jl, all of these search methodsthe share a common supertype StochasticSearchIterator, which defines the following fields

  • examples

  • cost_function

  • initial_temperature

  • evaluation_function.

They are customized by overriding the functions neighbourhood, propose, accept and temperature as required.

We start with a simple grammar and a helper function to create the input-output examples for the problem we want to solve.

\n\n
g_4 = @csgrammar begin\n    X = |(1:5)\n    X = X * X\n    X = X + X\n    X = X - X\n    X = x\nend
\n
1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n
\n\n
function create_problem(f, range=20)\n    examples = [IOExample(Dict(:x => x), f(x)) for x ∈ 1:range]\n    return Problem(examples), examples\nend
\n
create_problem (generic function with 2 methods)
\n\n\n

Throughout the stochastic search examples, we will use mean-squared-error as cost function. The cost function helps to guide the search by evaluating how well a candidate program solves the given task. This is used to decide whether a proposed program should be accepted or rejected.

\n\n
cost_function = mean_squared_error
\n
mean_squared_error (generic function with 1 method)
\n\n\n

Metropolis-Hastings

Metropolis-Hastings (MH) is a method to produce samples from a distribution that may otherwise be difficult to sample. In the context of program synthesis, we sample from a distribution of programs defined by the grammar.

For more information on MH, see for example this webpage.

To illustrate MH, we use a simple arithmetic example.

\n\n
e_mh = x -> x * x + 4
\n
#7 (generic function with 1 method)
\n\n
problem_mh, examples_mh = create_problem(e_mh)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)])
\n\n\n

Run the following code block to define the iterator and perform the program synthesis multiple times. Since the search process is stochastic, you will likely see different solution programs with each run.

\n\n
begin\n    iterator_mh = MHSearchIterator(g_4, :X, examples_mh, cost_function, max_depth=3) \n    program_mh = synth(problem_mh, iterator_mh)\n    println(\"Sollution using MH: \", program_mh)\nend
\n\n\n\n

Very Large Scale Neighbourhood Search

The second stochastic search method we consider is Very Large Scale Neighbourhood Search (VLSN). In each iteration, the algorithm searches the neighbourhood of the current candidate program for a local optimum, aiming to find a better candidate solution.

For more information, see this article.

Given the same grammar as before, we can try it with some simple examples.

\n\n
e_vlsn = x -> 10
\n
#9 (generic function with 1 method)
\n\n
problem_vlsn1, examples_vlsn1 = create_problem(e_vlsn)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 10), IOExample(Dict{Symbol, Any}(:x => 2), 10), IOExample(Dict{Symbol, Any}(:x => 3), 10), IOExample(Dict{Symbol, Any}(:x => 4), 10), IOExample(Dict{Symbol, Any}(:x => 5), 10), IOExample(Dict{Symbol, Any}(:x => 6), 10), IOExample(Dict{Symbol, Any}(:x => 7), 10), IOExample(Dict{Symbol, Any}(:x => 8), 10), IOExample(Dict{Symbol, Any}(:x => 9), 10), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 10), IOExample(Dict{Symbol, Any}(:x => 12), 10), IOExample(Dict{Symbol, Any}(:x => 13), 10), IOExample(Dict{Symbol, Any}(:x => 14), 10), IOExample(Dict{Symbol, Any}(:x => 15), 10), IOExample(Dict{Symbol, Any}(:x => 16), 10), IOExample(Dict{Symbol, Any}(:x => 17), 10), IOExample(Dict{Symbol, Any}(:x => 18), 10), IOExample(Dict{Symbol, Any}(:x => 19), 10), IOExample(Dict{Symbol, Any}(:x => 20), 10)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 10), IOExample(Dict{Symbol, Any}(:x => 2), 10), IOExample(Dict{Symbol, Any}(:x => 3), 10), IOExample(Dict{Symbol, Any}(:x => 4), 10), IOExample(Dict{Symbol, Any}(:x => 5), 10), IOExample(Dict{Symbol, Any}(:x => 6), 10), IOExample(Dict{Symbol, Any}(:x => 7), 10), IOExample(Dict{Symbol, Any}(:x => 8), 10), IOExample(Dict{Symbol, Any}(:x => 9), 10), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 10), IOExample(Dict{Symbol, Any}(:x => 12), 10), IOExample(Dict{Symbol, Any}(:x => 13), 10), IOExample(Dict{Symbol, Any}(:x => 14), 10), IOExample(Dict{Symbol, Any}(:x => 15), 10), IOExample(Dict{Symbol, Any}(:x => 16), 10), IOExample(Dict{Symbol, Any}(:x => 17), 10), IOExample(Dict{Symbol, Any}(:x => 18), 10), IOExample(Dict{Symbol, Any}(:x => 19), 10), IOExample(Dict{Symbol, Any}(:x => 20), 10)])
\n\n
iterator_vlsn1 = VLSNSearchIterator(g_4, :X, examples_vlsn1, cost_function, max_depth=2) 
\n
VLSNSearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 10), IOExample(Dict{Symbol, Any}(:x => 2), 10), IOExample(Dict{Symbol, Any}(:x => 3), 10), IOExample(Dict{Symbol, Any}(:x => 4), 10), IOExample(Dict{Symbol, Any}(:x => 5), 10), IOExample(Dict{Symbol, Any}(:x => 6), 10), IOExample(Dict{Symbol, Any}(:x => 7), 10), IOExample(Dict{Symbol, Any}(:x => 8), 10), IOExample(Dict{Symbol, Any}(:x => 9), 10), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 10), IOExample(Dict{Symbol, Any}(:x => 12), 10), IOExample(Dict{Symbol, Any}(:x => 13), 10), IOExample(Dict{Symbol, Any}(:x => 14), 10), IOExample(Dict{Symbol, Any}(:x => 15), 10), IOExample(Dict{Symbol, Any}(:x => 16), 10), IOExample(Dict{Symbol, Any}(:x => 17), 10), IOExample(Dict{Symbol, Any}(:x => 18), 10), IOExample(Dict{Symbol, Any}(:x => 19), 10), IOExample(Dict{Symbol, Any}(:x => 20), 10)], mean_squared_error, 2, 1, execute_on_input)
\n\n
program_vlsn1 = synth(problem_vlsn1, iterator_vlsn1)
\n
(6{2,5}, optimal_program)
\n\n
println(\"Solution: \", program_vlsn1)
\n\n\n
e_vlsn2 = x -> x
\n
#11 (generic function with 1 method)
\n\n
problem_vlsn2, examples_vlsn2 = create_problem(e_vlsn2)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 1), IOExample(Dict{Symbol, Any}(:x => 2), 2), IOExample(Dict{Symbol, Any}(:x => 3), 3), IOExample(Dict{Symbol, Any}(:x => 4), 4), IOExample(Dict{Symbol, Any}(:x => 5), 5), IOExample(Dict{Symbol, Any}(:x => 6), 6), IOExample(Dict{Symbol, Any}(:x => 7), 7), IOExample(Dict{Symbol, Any}(:x => 8), 8), IOExample(Dict{Symbol, Any}(:x => 9), 9), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 11), IOExample(Dict{Symbol, Any}(:x => 12), 12), IOExample(Dict{Symbol, Any}(:x => 13), 13), IOExample(Dict{Symbol, Any}(:x => 14), 14), IOExample(Dict{Symbol, Any}(:x => 15), 15), IOExample(Dict{Symbol, Any}(:x => 16), 16), IOExample(Dict{Symbol, Any}(:x => 17), 17), IOExample(Dict{Symbol, Any}(:x => 18), 18), IOExample(Dict{Symbol, Any}(:x => 19), 19), IOExample(Dict{Symbol, Any}(:x => 20), 20)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 1), IOExample(Dict{Symbol, Any}(:x => 2), 2), IOExample(Dict{Symbol, Any}(:x => 3), 3), IOExample(Dict{Symbol, Any}(:x => 4), 4), IOExample(Dict{Symbol, Any}(:x => 5), 5), IOExample(Dict{Symbol, Any}(:x => 6), 6), IOExample(Dict{Symbol, Any}(:x => 7), 7), IOExample(Dict{Symbol, Any}(:x => 8), 8), IOExample(Dict{Symbol, Any}(:x => 9), 9), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 11), IOExample(Dict{Symbol, Any}(:x => 12), 12), IOExample(Dict{Symbol, Any}(:x => 13), 13), IOExample(Dict{Symbol, Any}(:x => 14), 14), IOExample(Dict{Symbol, Any}(:x => 15), 15), IOExample(Dict{Symbol, Any}(:x => 16), 16), IOExample(Dict{Symbol, Any}(:x => 17), 17), IOExample(Dict{Symbol, Any}(:x => 18), 18), IOExample(Dict{Symbol, Any}(:x => 19), 19), IOExample(Dict{Symbol, Any}(:x => 20), 20)])
\n\n
iterator_vlsn2 = VLSNSearchIterator(g_4, :X, examples_vlsn2, cost_function, max_depth=1) 
\n
VLSNSearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 1), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 1), IOExample(Dict{Symbol, Any}(:x => 2), 2), IOExample(Dict{Symbol, Any}(:x => 3), 3), IOExample(Dict{Symbol, Any}(:x => 4), 4), IOExample(Dict{Symbol, Any}(:x => 5), 5), IOExample(Dict{Symbol, Any}(:x => 6), 6), IOExample(Dict{Symbol, Any}(:x => 7), 7), IOExample(Dict{Symbol, Any}(:x => 8), 8), IOExample(Dict{Symbol, Any}(:x => 9), 9), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 11), IOExample(Dict{Symbol, Any}(:x => 12), 12), IOExample(Dict{Symbol, Any}(:x => 13), 13), IOExample(Dict{Symbol, Any}(:x => 14), 14), IOExample(Dict{Symbol, Any}(:x => 15), 15), IOExample(Dict{Symbol, Any}(:x => 16), 16), IOExample(Dict{Symbol, Any}(:x => 17), 17), IOExample(Dict{Symbol, Any}(:x => 18), 18), IOExample(Dict{Symbol, Any}(:x => 19), 19), IOExample(Dict{Symbol, Any}(:x => 20), 20)], mean_squared_error, 2, 1, execute_on_input)
\n\n
program_vlsn2 = synth(problem_vlsn2, iterator_vlsn2)
\n
(9,, optimal_program)
\n\n
println(\"Solution: \", program_vlsn2)
\n\n\n\n

Simulated Annealing

Simulated Annealing (SA) explores smaller, incremental changes to the candidate program in each iteration, gradually refining the solution. It is a variation of the hill-climbing algorithm: Instead of always selecting the best move, SA picks a random move. If the move improves the solution (i.e., the candidate program), it is accepted.

Occasionally, SA will accept a move that worsens the solution. This allows the algorithm to escape local optima and explore more of the solution space. However, this strategy follows a cooling (annealing) schedule: at the beginning (high temperature), the algorithm explores more broadly and is more likely to accept worse solutions. As the temperature decreases, it becomes more selective, accepting worse solutions less often.

For more information, see this page.

\n\n\n

We use the same example as for MH. SA additionally has the option to specify the initial_temperature for the annealing (default initial_temperature=1). Let's see what effect changing the temperature from 1 to 2 has on the solution program.

\n\n
problem_sa, examples_sa = create_problem(e_mh)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)])
\n\n
initial_temperature1 = 1
\n
1
\n\n
iterator_sa1 = SASearchIterator(g_4, :X, examples_sa, cost_function, max_depth=3, initial_temperature = initial_temperature1) 
\n
SASearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 3), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)], mean_squared_error, 1, 0.99, execute_on_input)
\n\n
program_sa1 = synth(problem_sa, iterator_sa1)
\n
(7{4,6{9,9}}, optimal_program)
\n\n
initial_temperature2 = 2
\n
2
\n\n
iterator_sa2 = SASearchIterator(g_4, :X, examples_sa, cost_function, max_depth=3, initial_temperature = initial_temperature2) 
\n
SASearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 3), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)], mean_squared_error, 2, 0.99, execute_on_input)
\n\n","category":"page"},{"location":"tutorials/advanced_search/#Genetic-Search","page":"Advanced Search Procedures","title":"Genetic Search","text":"","category":"section"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"
\n

Genetic search is a type of evolutionary algorithm, which simulates the process of natural selection. It evolves a population of candidate programs through operations like mutation, crossover (recombination), and selection. Then, the fitness of each program is assessed (i.e., how well it satisfies the given specifications). Only the 'fittest' programs are selected for the next generation, thus gradually refining the population of candidate programs.

For more information, see here.

We show the example of finding a lambda function. Try varying the parameters of the genetic search to see what happens.

\n\n
e_gs = x -> 3 * x * x + (x + 2)
\n
#13 (generic function with 1 method)
\n\n
problem_gs, examples_gs = create_problem(e_gs)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 6), IOExample(Dict{Symbol, Any}(:x => 2), 16), IOExample(Dict{Symbol, Any}(:x => 3), 32), IOExample(Dict{Symbol, Any}(:x => 4), 54), IOExample(Dict{Symbol, Any}(:x => 5), 82), IOExample(Dict{Symbol, Any}(:x => 6), 116), IOExample(Dict{Symbol, Any}(:x => 7), 156), IOExample(Dict{Symbol, Any}(:x => 8), 202), IOExample(Dict{Symbol, Any}(:x => 9), 254), IOExample(Dict{Symbol, Any}(:x => 10), 312), IOExample(Dict{Symbol, Any}(:x => 11), 376), IOExample(Dict{Symbol, Any}(:x => 12), 446), IOExample(Dict{Symbol, Any}(:x => 13), 522), IOExample(Dict{Symbol, Any}(:x => 14), 604), IOExample(Dict{Symbol, Any}(:x => 15), 692), IOExample(Dict{Symbol, Any}(:x => 16), 786), IOExample(Dict{Symbol, Any}(:x => 17), 886), IOExample(Dict{Symbol, Any}(:x => 18), 992), IOExample(Dict{Symbol, Any}(:x => 19), 1104), IOExample(Dict{Symbol, Any}(:x => 20), 1222)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 6), IOExample(Dict{Symbol, Any}(:x => 2), 16), IOExample(Dict{Symbol, Any}(:x => 3), 32), IOExample(Dict{Symbol, Any}(:x => 4), 54), IOExample(Dict{Symbol, Any}(:x => 5), 82), IOExample(Dict{Symbol, Any}(:x => 6), 116), IOExample(Dict{Symbol, Any}(:x => 7), 156), IOExample(Dict{Symbol, Any}(:x => 8), 202), IOExample(Dict{Symbol, Any}(:x => 9), 254), IOExample(Dict{Symbol, Any}(:x => 10), 312), IOExample(Dict{Symbol, Any}(:x => 11), 376), IOExample(Dict{Symbol, Any}(:x => 12), 446), IOExample(Dict{Symbol, Any}(:x => 13), 522), IOExample(Dict{Symbol, Any}(:x => 14), 604), IOExample(Dict{Symbol, Any}(:x => 15), 692), IOExample(Dict{Symbol, Any}(:x => 16), 786), IOExample(Dict{Symbol, Any}(:x => 17), 886), IOExample(Dict{Symbol, Any}(:x => 18), 992), IOExample(Dict{Symbol, Any}(:x => 19), 1104), IOExample(Dict{Symbol, Any}(:x => 20), 1222)])
\n\n
iterator_gs = GeneticSearchIterator(g_4, :X, examples_gs, population_size = 10, mutation_probability = 0.8, maximum_initial_population_depth = 3) 
\n
GeneticSearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 9223372036854775807), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 6), IOExample(Dict{Symbol, Any}(:x => 2), 16), IOExample(Dict{Symbol, Any}(:x => 3), 32), IOExample(Dict{Symbol, Any}(:x => 4), 54), IOExample(Dict{Symbol, Any}(:x => 5), 82), IOExample(Dict{Symbol, Any}(:x => 6), 116), IOExample(Dict{Symbol, Any}(:x => 7), 156), IOExample(Dict{Symbol, Any}(:x => 8), 202), IOExample(Dict{Symbol, Any}(:x => 9), 254), IOExample(Dict{Symbol, Any}(:x => 10), 312), IOExample(Dict{Symbol, Any}(:x => 11), 376), IOExample(Dict{Symbol, Any}(:x => 12), 446), IOExample(Dict{Symbol, Any}(:x => 13), 522), IOExample(Dict{Symbol, Any}(:x => 14), 604), IOExample(Dict{Symbol, Any}(:x => 15), 692), IOExample(Dict{Symbol, Any}(:x => 16), 786), IOExample(Dict{Symbol, Any}(:x => 17), 886), IOExample(Dict{Symbol, Any}(:x => 18), 992), IOExample(Dict{Symbol, Any}(:x => 19), 1104), IOExample(Dict{Symbol, Any}(:x => 20), 1222)], execute_on_input, 10, 0.8, 3)
\n\n
program_gs, error_gs = synth(problem_gs, iterator_gs)
\n
(6{1,7{7{8{5,4}7{7{6{7{6{9,1}1}6{9,1}}6{8{2,2}6{1,7{7{3,7{7{3,6{3,6{1,7{7{2,7{7{6{1,6{5,3}}6{8{2,2}7{3,8{6{7{7{6{7{5,7{7{9,2}9}}9}6{9,7{3,9}}}6{7{8{3,1}9}6{8{2,6{3,6{1,7{7{2,7{2,6{7{8{1,5}9}9}}}1}}}}7{3,8{6{5,3}6{1,8{5,3}}}}}}}7{6{7{9,9}9}8{1,6{7{7{2,1}7{7{6{7{5,5}9}7{5,6{8{1,6{3,7{7{7{7{4,8{9,6{1,7{7{6{5,3}2}6{3,7{7{2,7{7{8{3,7{2,1}}5}6{1,9}}}6{8{2,2}7{3,8{3,3}}}}}}}}}6{3,6{1,7{7{2,7{7{9,6{8{2,2}7{3,8{3,6{1,7{7{6{2,3}6{9,4}}9}}}}}}6{7{8{1,7{7{6{4,3}6{7{1,8{1,8{9,3}}}7{3,6{3,7{7{2,5}7{7{2,1}7{7{6{7{5,5}9}6{9,3}}6{7{7{1,6{8{2,6{3,6{1,7{7{2,7{1,6{7{8{1,7{8{7{7{5,2}1}6{7{8{6{4,1}9}9}4}}9}}9}9}}}6{7{4,3}7{7{3,6{3,1}}1}}}}}}7{3,8{6{8{2,9}8{2,4}}6{1,7{7{6{2,8{9,2}}6{7{1,9}7{3,6{3,9}}}}9}}}}}}9}9}}}}}}}}9}}9}9}}}1}}}}6{7{8{6{4,1}2}9}9}}6{3,7{6{7{6{9,1}1}6{9,8{4,7{7{7{3,6{3,6{1,7{7{7{3,6{3,1}}7{7{3,6{3,1}}1}}1}}}}6{2,9}}6{8{2,2}6{7{4,3}7{7{3,6{3,1}}1}}}}}}}9}}}}}7{3,8{7{3,5}6{7{9,9}9}}}}}}6{7{8{3,1}9}9}}}7{7{6{2,3}4}6{3,7{6{7{6{6{7{7{1,1}9}9}1}1}9}1}}}}}}}6{1,7{7{6{8{6{4,1}9}7{4,1}}6{7{1,3}7{9,6{3,7{7{2,7{7{3,6{3,1}}1}}7{7{2,9}7{7{6{7{5,7{7{9,2}9}}9}6{9,7{3,7{6{9,1}1}}}}6{7{8{3,1}9}6{8{2,6{3,6{1,7{7{2,7{7{3,6{8{2,8{2,3}}7{3,8{6{8{7{1,2}9}7{6{7{9,9}9}1}}6{1,7{7{6{2,3}6{7{7{8{2,2}1}3}7{3,6{3,1}}}}9}}}}}}6{7{8{1,7{8{7{7{5,2}1}6{7{8{6{4,1}9}9}4}}9}}9}9}}}1}}}}7{3,8{6{8{2,9}8{2,6{7{8{7{6{2,1}7{1,2}}5}9}9}}}6{1,7{7{6{2,8{9,2}}6{7{1,9}7{3,6{3,9}}}}9}}}}}}}}}}}}}9}}}}}}6{7{8{1,1}7{7{6{6{1,3}9}6{8{1,6{3,7{7{6{2,3}1}6{3,7{6{7{6{9,1}1}1}9}}}}}7{3,8{7{3,5}6{7{9,9}9}}}}}6{3,9}}}9}}}5}}}}6{7{8{7{3,2}1}9}9}}}3}}}}6{7{7{9,6{2,8{5,5}}}9}9}}}1}}, optimal_program)
\n\n","category":"page"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/advanced_search.jl\"","category":"page"},{"location":"tutorials/getting_started_with_constraints/","page":"Getting started with Constraints","title":"Getting started with Constraints","text":"\n\n\n\n\n

Getting started with HerbConstraints

When enumerating programs using a grammar, we will encounter many redundant programs. For example, x, -(-x) and 1 * x are syntactically different programs, but they have the same semantics. Grammar constraints aim to speed up synthesis by eliminating such redundant programs and thereby reducing the size of the program space.

\n\n\n

Setup

For this tutorial, we need to import the following modules of the Herb.jl framework:

  • HerbCore for the necessary data strucutes, like Holes and RuleNodes

  • HerbGrammar to define the grammar

  • HerbConstraints to define the constraints

  • HerbSearch to execute a constrained enumeration

We will also redefine the simple arithmetic grammar from the previous tutorial.

\n\n
using HerbCore, HerbGrammar, HerbConstraints, HerbSearch
\n\n\n
grammar = @csgrammar begin\n    Int = 1\n    Int = x\n    Int = - Int\n    Int = Int + Int\n    Int = Int * Int\nend
\n
1: Int = 1\n2: Int = x\n3: Int = -Int\n4: Int = Int + Int\n5: Int = Int * Int\n
\n\n\n

Working with constraints

To show the effects of constraints, we will first enumerate all programs without constraints (up to a maximum size of 3 AST nodes).

(To make sure the grammar doesn't have any constraints, we can clear the constraints using clearconstraints!. This is not needed at this point, but could come in handy if your REPL holds a reference to a constrained version of the grammar)

\n\n
begin\n    clearconstraints!(grammar)\n    iter_1 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_1\n        println(rulenode2expr(program, grammar))\n    end\n    \nend
\n\n\n\n

Upon inspection, we can already see some redundant programs, like 1 * 1 and -(-1). To eliminate these redundant programs, we will set up some constraints that prevent these patterns from appearing. Then we will create another iteratator to enumerate all programs that satisfy the defined grammar constraints.

To make the forbidden pattern constraint general, we will use a special type of rulenode: VarNode(:A). This node matches with any subtree and can be used to forbid multiple forbidden patterns using a single constraint. For example, Forbidden(RuleNode(minus, [RuleNode(minus, [VarNode(:A)])]))) forbids:

  • -(-1)

  • -(-X)

  • -(-(1 + 1))

  • 1 + -(-(1 + 1))

  • etc

\n\n
begin\n    one = 1\n    x = 2\n    minus = 3\n    plus = 4\n    times = 5\n    \n    addconstraint!(grammar, Forbidden(RuleNode(times, [RuleNode(one), VarNode(:A)])))        # forbid 1*A\n    addconstraint!(grammar, Forbidden(RuleNode(minus, [RuleNode(minus, [VarNode(:A)])])))    # forbid -(-A)\n    \n    iter_2 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_2\n        println(rulenode2expr(program, grammar))\n    end\nend
\n\n\n\n

Forbidden Constraint

The Forbidden constraint forbids any subtree in the program that matches a given template tree. Such a template tree can consist of 3 node types:

  • RuleNode(1). Matches exactly the given rule.

  • DomainRuleNode(BitVector((0, 0, 0, 1, 1)), children). Matches any rule in its bitvector domain. In this case, rule 4 and 5.

  • VarNode(:A). Matches any subtree. If another VarNode of the same name is used, the subtrees have to be the same.

\n\n
begin\n    #this constraint forbids A+A and A*A\n    constraint_1 = Forbidden(DomainRuleNode(BitVector((0, 0, 0, 1, 1)), [VarNode(:A), VarNode(:A)]))\n    \n    # Without this constraint, we encounter 154 programs\n    clearconstraints!(grammar)\n    iter_3 = BFSIterator(grammar, :Int, max_size=5)\n    println(length(iter_3))\n    \n    # With this constraint, we encounter 106 programs\n    clearconstraints!(grammar)\n    addconstraint!(grammar, constraint_1)\n    iter_4 = BFSIterator(grammar, :Int, max_size=5)\n    println(length(iter_4))\n    \nend
\n\n\n\n

Contains Constraint

The Contains constraint enforces that a given rule appears in the program tree at least once.

In the arithmetic grammar, this constraint can be used to ensure the input symbol x is used in the program. Otherwise, the program is just a constant.

\n\n
begin\n    clearconstraints!(grammar)\n    addconstraint!(grammar, Contains(2)) #rule 2 should be used in the program\n    iter_5 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_5\n        println(rulenode2expr(program, grammar))\n    end\nend
\n\n\n\n

Contains Subtree Constraint

Similarly to the Contains constraint, the ContainsSubtree can be used to enforce a given template tree is used in the program at least once.

\n\n
begin\n    clearconstraints!(grammar)\n    addconstraint!(grammar, ContainsSubtree(RuleNode(times, [RuleNode(x), RuleNode(x)]))) #x*x should be in the program tree\n    iter_6 = BFSIterator(grammar, :Int, max_size=4)\n    \n    for program ∈ iter_6\n        println(rulenode2expr(program, grammar))\n    end\nend
\n\n\n\n

Ordered Constraint

The Ordered constraint enforces an <= ordering on a provided list of variables. With this constraint, we can break symmetries based on commutativity. For example, 1+x and x+1 are semantically equivalent. By imposing an Ordered constraint, we can eliminate one of the symmetric variants.

To define an Ordered constraint, we need to provide it with a template tree including at least two differently named VarNodes. And additionally, an ordering of the variables in the tree.

In the upcoming example we will set up a template tree representing a+b and a*b. Then, we will impose an ordering a<=b on all the subtrees that match the template.

The result is that our iterator skips the redundant programs x+1 and x*1, as they are already represented by 1+x and 1*x.

\n\n
begin\n    clearconstraints!(grammar)\n    \n    template_tree = DomainRuleNode(BitVector((0, 0, 0, 1, 1)), [VarNode(:a), VarNode(:b)])\n    order = [:a, :b]\n    \n    addconstraint!(grammar, Ordered(template_tree, order))\n    iter_7 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_7\n        println(rulenode2expr(program, grammar))\n    end\n    \nend
\n\n\n\n

Forbidden Sequence Constraint

The ForbiddenSequence constraints forbids a given sequence of rule nodes in a vertical path of the tree.

An optional second argument, ignore_if, can be used to overrule the constraint in case any of the rules on the ignore_if list are present.

Below we will define the constraint ForbiddenSequence([plus, one], ignore_if=[times]). It forbids an 1 after an + unless an * disrupts the sequence.

This constraint will forbid the following programs:

  • x + 1

  • x + -1

  • x + -(-1)

  • x + (x + 1)

  • x * (x + 1)

But it will allow the following program (as * disrupts the sequence):

  • x + (x * 1)

\n\n
begin\n    constraint_2 = ForbiddenSequence([plus, one], ignore_if=[times])\n    addconstraint!(grammar, constraint_2)\n    iter_8 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_8\n        println(rulenode2expr(program, grammar))\n    end\n    \nend
\n\n\n\n

Custom Constraint

To implement a new constraint, we need to define two structs: an AbstractGrammarConstraint and an AbstractLocalConstraint.

A grammar constraint is a high-level constraint on the grammar itself and does not refer to a location in the tree. For example, the Forbidden constraint is responsible for forbidding a template tree everywhere in the tree. To divide the work of constraint propagation, the grammar constraint will post several local constraints that are responsible for propagating the constraint at each particular location.

A local constraint is a rooted version of a grammar constraint. Each local constraint holds a path field that points to a location in the tree where this constraint applies.

\n\n\n

Suppose we want to implement a simple custom constraint that forbids a given rule twice in a row.

Each time a new AST node is added to a tree, the on_new_node function is called to notify that an unseen node has been added to the tree at path path. Our grammar constraint has the opportunity to react to this event. In this example, we will post a new local constraint at the new location using the post! function.

(Don't worry about the HerbConstraints. prefixes. Normally, constraints are defined within the HerbConstraints repository, so there is no need to specify the namespace)

\n\n
begin\n    \"\"\"\n    Forbids the consecutive application of the specified rule.\n    For example, CustomConstraint(4) forbids the tree 4(1, 4(1, 1)) as it applies rule 4 twice in a row.\n    \"\"\"\n    struct ForbidConsecutive <: AbstractGrammarConstraint\n        rule::Int\n    end\n    \n    \"\"\"\n    Post a local constraint on each new node that appears in the tree\n    \"\"\"\n    function HerbConstraints.on_new_node(solver::Solver, constraint::ForbidConsecutive, path::Vector{Int})\n        HerbConstraints.post!(solver, LocalForbidConsecutive(path, constraint.rule))\n    end\nend
\n\n\n\n

Next, we will define our local constraint. This constraint is responsible for propagating the constraint at a given path. The propagate! method can use several solver functions to manipulate the tree. The following tree manipulations can be used to remove rules from the domain of a hole at a given path:

  • remove!(solver::Solver, path::Vector{Int}, rule_index::Int)

  • remove!(solver::Solver, path::Vector{Int}, rules::Vector{Int})

  • remove_all_but!(solver::Solver, path::Vector{Int}, new_domain::BitVector)

  • remove_above!(solver::Solver, path::Vector{Int}, rule_index::Int)

  • remove_below!(solver::Solver, path::Vector{Int}, rule_index::Int)

  • make_equal!(solver::Solver, node1::AbstractRuleNode, node2::AbstractRuleNode) (a high level manipulation that requires node1 and node2 to be in the tree)

In addition to tree manipulations, the following solver functions can be used to communicate new information to the solver:

  • set_infeasible!(solver). If a propagator detects an inconsistency, the solver should be notified and cancel any other scheduled propagators.

  • deactivate!(solver, constraint). If a constraint is satisfied, it should deactivate itself to prevent re-propagation.

  • post!(solver, constraint) A constraint is allowed to post new local constraints. This might be helpful if a constraint can be reduced to a smaller constraint.

The solver manages all constraints and the program tree we propagate on. Applying tree manipulations might cause a chain reaction of other propagators, so the shape of the tree might update as we propagate. The get the latest information about the tree, we should use the following getter functions:

  • get_tree(solver) returns the root node of the current (partial) program tree

  • isfeasible(solver) returns the a flag indicating if the solver is not violating any (other) constriants.

  • get_path(solver, node) returns the path at which the node is located.

  • get_node_at_location(solver, path) returns the node that is currently at the given path (be aware that this instance might be replaced by manipulations).

  • get_hole_at_location(solver, path) same as get node at location, but asserts the node is a hole (domain size >= 2).

To get information about a node, we can use the following getter functions:

  • isfilled(node). Returns true if the node is a RuleNode or has domain size 1.

  • get_rule(node). Get the rule of a filled node.

  • get_children(node). Get the children of a node.

  • node.domain[rule]. Given the node is a hole, return true if rule is in the domain.

Finally, another useful function for propagators is pattern_match(node1, node2). This function compares two trees and returns a PatternMatchResult that indicates if the nodes match, and potentially indicate which holes need to be filled to complete the match.

\n\n
begin\n    \"\"\"\n    Forbids the consecutive application of the specified rule at path `path`.\n    \"\"\"\n    struct LocalForbidConsecutive <: AbstractLocalConstraint\n        path::Vector{Int}\n        rule::Int\n    end\n    \n    \"\"\"\n    Propagates the constraints by preventing a consecutive application of the specified rule.\n    \"\"\"\n    function HerbConstraints.propagate!(solver::Solver, constraint::LocalForbidConsecutive)\n        node = get_node_at_location(solver, constraint.path)\n        if isfilled(node)\n            if get_rule(node) == constraint.rule\n                #the specified rule is used, make sure the rule will not be used by any of the children\n                for (i, child) ∈ enumerate(get_children(node))\n                    if isfilled(child)\n                        if get_rule(child) == constraint.rule\n                            #the specified rule was used twice in a row, which is violating the constraint\n                            set_infeasible!(solver)\n                            return\n                        end\n                    elseif child.domain[constraint.rule]\n                        child_path = push!(copy(constraint.path), i)\n                        remove!(solver, child_path, constraint.rule) # remove the rule from the domain of the child\n                    end\n                end\n            end\n        elseif node.domain[constraint.rule]\n            #our node is a hole with the specified rule in its domain\n            #we will now check if any of the children already uses the specified rule\n            softfail = false\n            for (i, child) ∈ enumerate(get_children(node))\n                if isfilled(child)\n                    if get_rule(child) == constraint.rule\n                        #the child holds the specified rule, so the parent cannot have this rule\n                        remove!(solver, constraint.path, constraint.rule)\n                    end\n                elseif child.domain[constraint.rule]\n                    #the child is a hole and contains the specified node. since there are 2 holes involved, we will softfail.\n                    softfail = true\n                end\n            end\n            if softfail\n                #we cannot deactivate the constraint, because it needs to be repropagated\n                return\n            end\n        end\n    \n        #the constraint is satisfied and can be deactivated\n        HerbConstraints.deactivate!(solver, constraint)\n    end\nend
\n\n\n\n

Posting a local constraint will trigger the initial propagation. To re-propagate, the constraint needs to be rescheduled for propagation.

Whenever the tree is manipulated, we will make a shouldschedule check to see if our constraint needs to be rescheduled for propagation based on the manipulation.

In our case, we want to repropagate if either:

  • a tree manipulation occured at the constraint.path

  • a tree manipulation occured at the child of the constraint.path

\n\n
\n\"\"\"\nGets called whenever an tree manipulation occurs at the given `path`.\nReturns true iff the `constraint` should be rescheduled for propagation.\n\"\"\"\nfunction HerbConstraints.shouldschedule(solver::Solver, constraint::LocalForbidConsecutive, path::Vector{Int})::Bool\n    return (path == constraint.path) || (path == constraint.path[1:end-1])\nend\n
\n\n\n\n

With all the components implemented, we can do a constrained enumeration using our new ForbidConsecutive constraint.

\n\n
begin\n    clearconstraints!(grammar)\n    \n    addconstraint!(grammar, ForbidConsecutive(minus))\n    addconstraint!(grammar, ForbidConsecutive(plus))\n    addconstraint!(grammar, ForbidConsecutive(times))\n    \n    iter = BFSIterator(grammar, :Int, max_size=6)\n    \n    for program ∈ iter\n        println(rulenode2expr(program, grammar))\n    end\nend
\n\n\n","category":"page"},{"location":"tutorials/getting_started_with_constraints/","page":"Getting started with Constraints","title":"Getting started with Constraints","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/getting_started_with_constraints.jl\"","category":"page"},{"location":"HerbInterpret/#HerbInterpret_docs","page":"HerbInterpret.jl","title":"HerbInterpret.jl Documentation","text":"","category":"section"},{"location":"HerbInterpret/","page":"HerbInterpret.jl","title":"HerbInterpret.jl","text":"CurrentModule=HerbInterpret","category":"page"},{"location":"HerbInterpret/","page":"HerbInterpret.jl","title":"HerbInterpret.jl","text":"Modules = [HerbInterpret]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbInterpret/#HerbInterpret.evaluate_program-Tuple{RuleNode, Vector{<:IOExample}, AbstractGrammar, Function}","page":"HerbInterpret.jl","title":"HerbInterpret.evaluate_program","text":"evaluate_program(program::RuleNode, examples::Vector{<:IOExample}, grammar::AbstractGrammar, evaluation_function::Function)\n\nRuns a program on the examples and returns tuples of actual desired output and the program's output\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.execute_on_input-Union{Tuple{T}, Tuple{AbstractGrammar, RuleNode, Dict{Symbol, T}}} where T","page":"HerbInterpret.jl","title":"HerbInterpret.execute_on_input","text":"execute_on_input(grammar::AbstractGrammar, program::RuleNode, input::Dict{Symbol, T})::Any where T\n\nConverts a RuleNode program into an expression using a given grammar, then evaluates this expression with a single input dictionary input and a symbol table derived from the grammar using execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T}).\n\nArguments\n\ngrammar::AbstractGrammar: A grammar object used to convert the RuleNode into an executable expression.\nprogram::RuleNode: The program, represented as a RuleNode, to be converted and evaluated.\ninput::Dict{Symbol, T}: A dictionary providing input values for symbols used in the generated expression.\n\nReturns\n\nAny: The result of evaluating the generated expression with the given input dictionary.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.execute_on_input-Union{Tuple{T}, Tuple{AbstractGrammar, RuleNode, Vector{T}}} where T<:(Dict{Symbol})","page":"HerbInterpret.jl","title":"HerbInterpret.execute_on_input","text":"execute_on_input(grammar::AbstractGrammar, program::RuleNode, input::Vector{T})::Vector{Any} where T <: Dict{Symbol, <:Any}\n\nConverts a RuleNode program into an expression using a given grammar, then evaluates this expression for each input dictionary in a vector input and a symbol table derived from the grammar using execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T}).\n\nArguments\n\ngrammar::AbstractGrammar: A grammar object used to convert the RuleNode into an executable expression.\nprogram::RuleNode: The program, represented as a RuleNode, to be converted and evaluated.\ninput::Vector{T}: A vector of dictionaries, each providing input values for symbols used in the generated expression.\n\nReturns\n\nVector{Any}: A vector containing the results of evaluating the generated expression for each input dictionary.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.execute_on_input-Union{Tuple{T}, Tuple{Dict{Symbol, Any}, Any, Dict{Symbol, T}}} where T","page":"HerbInterpret.jl","title":"HerbInterpret.execute_on_input","text":"execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T})::Any where T\n\nEvaluates an expression expr within the context of a symbol table tab and a single input dictionary input. The input dictionary keys should match the symbols used in the expression, and their values are used during the expression's evaluation.\n\nArguments\n\ntab::SymbolTable: A symbol table containing predefined symbols and their associated values or functions.\nexpr::Any: The expression to be evaluated. Can be any Julia expression that is valid within the context of the provided symbol table and input.\ninput::Dict{Symbol, T}: A dictionary where each key is a symbol used in the expression, and the value is the corresponding value to be used in the expression's evaluation. The type T can be any type.\n\nReturns\n\nAny: The result of evaluating the expression with the given symbol table and input dictionary.\n\nwarning: Warning\nThis function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.execute_on_input-Union{Tuple{T}, Tuple{Dict{Symbol, Any}, Any, Vector{T}}} where T<:(Dict{Symbol})","page":"HerbInterpret.jl","title":"HerbInterpret.execute_on_input","text":"execute_on_input(tab::SymbolTable, expr::Any, input::Vector{T})::Vector{<:Any} where T <: Dict{Symbol, <:Any}\n\nWrapper around execute_on_input to execute all inputs given as an array.\n\nArguments\n\ntab::SymbolTable: A symbol table containing predefined symbols and their associated values or functions.\nexpr::Any: The expression to be evaluated for each input dictionary.\ninputs::Vector{T}: A vector of dictionaries, each serving as an individual set of inputs for the expression's evaluation.\n\nReturns\n\nVector{<:Any}: A vector containing the results of evaluating the expression for each input dictionary.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.interpret-Tuple{Dict{Symbol, Any}, Any}","page":"HerbInterpret.jl","title":"HerbInterpret.interpret","text":"interpret(tab::SymbolTable, ex::Expr)\n\nEvaluates an expression without compiling it. Uses AST and symbol lookups. Only supports :call and :(=) expressions at the moment.\n\nExample usage:\n\ntab = SymbolTable(:f => f, :x => x)\nex = :(f(x))\ninterpret(tab, ex)\n\nWARNING: This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.test_all_examples-Tuple{Dict{Symbol, Any}, Any, Vector{IOExample}}","page":"HerbInterpret.jl","title":"HerbInterpret.test_all_examples","text":"test_all_examples(tab::SymbolTable, expr::Any, examples::Vector{IOExample})::Vector{Bool}\n\nwarning: Warning\nThis function is deprecated. Please use execute_on_input instead.\n\nRuns the interpreter on all examples with the given input table and expression. The symbol table defines everything (functions, symbols) that are not input variables to the program to be synthesised. Returns a list of true/false values indicating if the expression satisfies the corresponding example. WARNING: This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.test_examples-Tuple{Dict{Symbol, Any}, Any, Vector{IOExample}}","page":"HerbInterpret.jl","title":"HerbInterpret.test_examples","text":"test_examples(tab::SymbolTable, expr::Any, examples::Vector{IOExample})::Bool\n\nwarning: Warning\nThis function is deprecated. Please use execute_on_input instead.\n\nEvaluates all examples and returns true iff all examples pass. Shortcircuits as soon as an example is found for which the program doesn't work. Returns false if one of the examples produces an error.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#Index","page":"HerbInterpret.jl","title":"Index","text":"","category":"section"},{"location":"HerbInterpret/","page":"HerbInterpret.jl","title":"HerbInterpret.jl","text":"","category":"page"},{"location":"tutorials/getting_started_with_herb/","page":"A more verbose getting started with Herb.jl","title":"A more verbose getting started with Herb.jl","text":"\n\n\n\n\n

Search

This notebook describes how you can search a program space as defined by a grammar. Specifically, we will look at example-based search, where the goal is to find a program that is able to transform the inputs of every example to the corresponding output.

\n\n\n

Setup

First, we start with the setup. We need access to all the function in the Herb.jl framework.

\n\n
using HerbGrammar, HerbSpecification, HerbSearch, HerbInterpret, HerbConstraints
\n\n\n\n

Defining the program space

Next, we start by creating a grammar. We define a context-free grammar as a HerbGrammar.ContextSpecificGrammar without any constraints. A context-free grammar is just a simple set of production rules for defining combinations of terminal symbols (in our case integers).

Alternatively we could define a context-sensitive grammar, when the production rules only hold in a certain context. For more information on this, please see our tutorial on defining grammars.

For now, we specify a simple grammar (using the @csgrammar macro) for dealing with integers and explain all the rules individually:

  1. First, we specify our number values and constrain them to being positive even integers.

  2. Then, we can also use the variable x to hold an integer.

  3. The third rule determines we can add two integers.

  4. The fourth rule determines we can subtract an integer from another.

  5. Finally, we also allow the multiplication of two integers.

If you run this cell, you can see all the rules rolled out.

\n\n
g = HerbGrammar.@csgrammar begin\n    Number = 0|2|4|6|8\n    Number = x\n    Number = Number + Number\n    Number = Number - Number\n    Number = Number * Number\nend
\n
1: Number = 0\n2: Number = 2\n3: Number = 4\n4: Number = 6\n5: Number = 8\n6: Number = x\n7: Number = Number + Number\n8: Number = Number - Number\n9: Number = Number * Number\n
\n\n\n

Defining the problem

\n\n\n

As mentioned before, we are looking at example-based search. This means that the problem is defined by a set of input-output examples. A single example hence consists of an input and an output. The input is defined as a dictionary, with a value assigned to each variable in the grammar. It is important to write the variable name as a Symbol instead of a string. A Symbol in Julia is written with a colon prefix, i.e. :x. The output of the input-output example is just a single value for this specific grammar, but could possibly relate to e.g. arrays of values, too.

In the cell below we automatically generate some examples for x assigning values 1-5.

\n\n
# Create input-output examples\nexamples = [HerbSpecification.IOExample(Dict(:x => x), 4x + 6) for x ∈ 1:5]
\n
5-element Vector{IOExample}:\n IOExample(Dict{Symbol, Any}(:x => 1), 10)\n IOExample(Dict{Symbol, Any}(:x => 2), 14)\n IOExample(Dict{Symbol, Any}(:x => 3), 18)\n IOExample(Dict{Symbol, Any}(:x => 4), 22)\n IOExample(Dict{Symbol, Any}(:x => 5), 26)
\n\n\n

Now that we have some input-output examples, we can define the problem. Next to the examples, a problem also contains a name meant to link to the file path, which can be used to keep track of current examples. For now, this is irrelevant, and you can give the program any name you like.

\n\n
problem_1 = HerbSpecification.Problem(\"example\", examples)
\n
Problem{Vector{IOExample}}(\"example\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 10), IOExample(Dict{Symbol, Any}(:x => 2), 14), IOExample(Dict{Symbol, Any}(:x => 3), 18), IOExample(Dict{Symbol, Any}(:x => 4), 22), IOExample(Dict{Symbol, Any}(:x => 5), 26)])
\n\n\n

Searching

Now that we have defined the search space and the goal of the search, we can start the search.

Of course, our problem is underdefined as there might be multiple programs that satisfy our examples. Let us consider the case where we also have a ternary if-then-else operator and standard boolean operators in our grammar: we could synthesize the program x ≤ 5 ? 3x+5 : 0. This program satisfies all our examples, but we don't expect it to generalize very well.

To search through a program space, we first need to define a HerbSearch.ProgramIterator, which can be instantiated with different iterators, for now we use a simple HerbSearch.BFSIterator. For more advanced search methods check out our tutorial on advanced search. For more information about iterators, check out our tutorial on working with interpreters.

In general, we assume that a smaller program is more general than a larger program. Therefore, we search for the smallest program in our grammar that satisfies our examples. This can be done using a breadth-first search over the program/search space.

This search is very basic; it makes use of an enumeration technique, where we enumerate programs one-by-one until we find a program that matches our examples. The search procedure has a built-in default evaluator to verify the candidate programs with the given input. The search procedure also has a built-in search procedure using breadth-first search.

So, we only need to give our grammar and the problem to our search procedure, along with a starting Symbol, in our case a Number.

\n\n
iterator_1 = BFSIterator(g, :Number)
\n
BFSIterator(GenericSolver(1: Number = 0\n2: Number = 2\n3: Number = 4\n4: Number = 6\n5: Number = 8\n6: Number = x\n7: Number = Number + Number\n8: Number = Number - Number\n9: Number = Number * Number\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 9223372036854775807))
\n\n
synth(problem_1, iterator_1)
\n
(7{9{6,3}4}, optimal_program)
\n\n\n

As you can see, the search procedure found the correct program!

\n\n\n

Defining the search procedure

In the previous case, we used the built-ins of the search procedure. However, we can also give a custom enumerator to the search procedure and define a few more values.

We first define a new problem to test with, we are looking for the programs that can compute the value 168. We immediately pass the examples to the problem and then set up the new search.

Search is done by passing the grammar, the problem and the starting point like before. We now also specify the enumeration function to be used, and now we use depth-first search. Then, we give the maximum depth of the programs we want to search for (3), the maximum number of nodes in the Abstract Syntax Tree that exists during search (10), and the maximum time in seconds allowed for the search.

\n\n
begin\n    problem_2 = HerbSpecification.Problem(\"example2\", [HerbSpecification.IOExample(Dict(:x => x), 168) for x ∈ 1:5])\n    iterator_2 = HerbSearch.BFSIterator(g, :Number, max_depth=4, max_size=30)\n    expr_2, flag_2 = HerbSearch.synth(problem_2, iterator_2)\n    print(expr_2)\n    program_2 = rulenode2expr(expr_2, g)\n    println(program_2)\nend
\n\n\n\n

We see that our synthesizer can find a program to construct the value 168, though a fun experiment would be trying to get the value 167, what do you think would happen? You can try below, using the same iterator.

In any case, this concludes our first introduction to the Herb.jl program synthesis framework. You can see more examples in this repository, or explore yourself. Enjoy!

\n\n
begin\n    problem_3 = HerbSpecification.Problem(\"example3\", [HerbSpecification.IOExample(Dict(:x => x), 167) for x ∈ 1:5])\n    expr_3, flag_3 = HerbSearch.synth(problem_3, iterator_2)\n    print(expr_3)\n    program_3 = rulenode2expr(expr_3, g)\n    println(program_3)\nend
\n\n\n","category":"page"},{"location":"tutorials/getting_started_with_herb/","page":"A more verbose getting started with Herb.jl","title":"A more verbose getting started with Herb.jl","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/getting_started_with_herb.jl\"","category":"page"},{"location":"get_started/#Getting-Started","page":"Getting Started","title":"Getting Started","text":"","category":"section"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"You can either paste this code into the Julia REPL or into a seperate file, e.g. get_started.jl. If using a separate file you can execute using julia get_started.jl or julia --project=. get_started.jl depending on whether you installed Herb.jl globally or in a project.","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"To begin, we need to import all needed packages using","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"using HerbGrammar, HerbSpecification, HerbSearch, HerbInterpret","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"To define a program synthesis problem, we need a grammar and specification. ","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"First, a grammar can be constructed using the @csgrammar macro included in HerbGrammar. ","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"Here, we describe a simple integer arithmetic example, that can add and multiply an input variable x or the integers 1,2, using","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"g = @csgrammar begin\n Number = |(1:2)\n Number = x\n Number = Number + Number\n Number = Number * Number\nend","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"Second, the problem specification can be provided using e.g. input/output examples using HerbSpecification. Inputs are provided as a Dict assigning values to variables, and outputs as arbitrary values. The problem itself is then a list of IOExamples using","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"problem = Problem([IOExample(Dict(:x => x), 2x+1) for x ∈ 1:5])","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"The problem is given now, let us search for a solution with HerbSearch. For now we will just use the default parameters searching for a satisfying program over the grammar, given the problem and a starting symbol using","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"iterator = BFSIterator(g, :Number, max_depth=5)\nsolution, flag = synth(problem, iterator)\nprintln(solution)","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"There are various ways to adapt the search technique to your needs. Please have a look at the synth documentation.","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"Eventually, we want to test our solution on some other inputs using HerbInterpret. We transform our grammar g to a Julia expression with Symboltable(g), add our solution and the input, assigning the value 6 to the variable x.","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"program = rulenode2expr(solution, g) # should yield 2*6+1\nprintln(program)\n\noutput = execute_on_input(SymbolTable(g), program, Dict(:x => 6)) \nprintln(output)","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"If you run the completed code it will output both the generated Julia expression and the result from assigning value.","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"Just like that we tackled (almost) all modules of Herb.jl.","category":"page"},{"location":"get_started/#Where-to-go-from-here?","page":"Getting Started","title":"Where to go from here?","text":"","category":"section"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"See our other tutorials!","category":"page"},{"location":"get_started/#The-full-code-example","page":"Getting Started","title":"The full code example","text":"","category":"section"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"using HerbSearch, HerbSpecification, HerbInterpret, HerbGrammar\n\n# define our very simple context-free grammar\n# Can add and multiply an input variable x or the integers 1,2.\ng = @csgrammar begin\n Number = |(1:2)\n Number = x\n Number = Number + Number\n Number = Number * Number\nend\n\nproblem = Problem([IOExample(Dict(:x => x), 2x+1) for x ∈ 1:5])\niterator = BFSIterator(g, :Number, max_depth=5)\n\nsolution, flag = synth(problem, iterator)\nprogram = rulenode2expr(solution, g) # should yield 2*6 +1 \nprintln(program)\n\noutput = execute_on_input(SymbolTable(g), program, Dict(:x => 6)) \nprintln(output)\n","category":"page"},{"location":"tutorials/defining_grammars/","page":"Defining Grammars in Herb.jl","title":"Defining Grammars in Herb.jl","text":"\n\n\n\n\n

Defining Grammars in Herb.jl using HerbGrammar

The program space in Herb.jl is defined using a grammar. This notebook demonstrates how such a grammar can be created. There are multiple kinds of grammars, but they can all be defined in a very similar way.

\n\n\n

Setup

First, we import the necessary Herb packages.

\n\n
using HerbGrammar, HerbConstraints
\n\n\n\n

Creating a simple grammar

This cell contains a very simple arithmetic grammar. The grammar is defined using the @csgrammar macro. This macro converts the grammar definition in the form of a Julia expression into Herb's internal grammar representation. Macro's are executed during compilation. If you want to load a grammar during execution, have a look at the HerbGrammar.expr2csgrammar function.

\n\n
g₁ = HerbGrammar.@csgrammar begin\n    Int = 1\n    Int = 2\n    Int = 3\n    Int = Int * Int\n    Int = Int + Int\nend
\n
1: Int = 1\n2: Int = 2\n3: Int = 3\n4: Int = Int * Int\n5: Int = Int + Int\n
\n\n\n

Defining every integer one-by-one can be quite tedious. Therefore, it is also possible to use the following syntax that makes use of a Julia iterator:

\n\n
g₂ = HerbGrammar.@csgrammar begin\n    Int = |(0:9)\n    Int = Int * Int\n    Int = Int + Int\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = Int + Int\n
\n\n\n

You can do the same with lists:

\n\n
g₃ = HerbGrammar.@csgrammar begin\n    Int = |([0, 2, 4, 6, 8])\n    Int = Int * Int\n    Int = Int + Int\nend
\n
1: Int = 0\n2: Int = 2\n3: Int = 4\n4: Int = 6\n5: Int = 8\n6: Int = Int * Int\n7: Int = Int + Int\n
\n\n\n

Variables can also be added to the grammar by just using the variable name:

\n\n
g₄ = HerbGrammar.@csgrammar begin\n    Int = |(0:9)\n    Int = Int * Int\n    Int = Int + Int\n    Int = x\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = Int + Int\n13: Int = x\n
\n\n\n

Grammars can also work with functions. After all, + and * are just infix operators for Julia's identically-named functions. You can use functions that are provided by Julia, or functions that you wrote yourself:

\n\n
begin\n    f(a) = a + 1\n    \n    g₅ = HerbGrammar.@csgrammar begin\n        Int = |(0:9)\n        Int = Int * Int\n        Int = Int + Int\n        Int = f(Int)\n        Int = x\n    end\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = Int + Int\n13: Int = f(Int)\n14: Int = x\n
\n\n\n

Similarly, we can also define the operator times (x) manually.

\n\n
begin\n    ×(a, b) = a * b\n    \n    g₆ = HerbGrammar.@csgrammar begin\n        Int = |(0:9)\n        Int = a\n        Int = Int + Int\n        Int = Int × Int\n    end\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = a\n12: Int = Int + Int\n13: Int = Int × Int\n
\n\n\n

Working with grammars

If you want to implement something using these grammars, it is useful to know about the functions that you can use to manipulate grammars and extract information. This section is not complete, but it aims to give an overview of the most important functions.

It is recommended to also read up on Julia metaprogramming if you are not already familiar with the concept.

One of the most important things about grammars is that each rule has an index associated with it:

\n\n
begin\n    g₇ = HerbGrammar.@csgrammar begin\n        Int = |(0:9)\n        Int = Int + Int\n        Int = Int * Int\n        Int = x\n    end\n    \n    collect(enumerate(g₇.rules))\nend
\n
13-element Vector{Tuple{Int64, Any}}:\n (1, 0)\n (2, 1)\n (3, 2)\n (4, 3)\n (5, 4)\n (6, 5)\n (7, 6)\n (8, 7)\n (9, 8)\n (10, 9)\n (11, :(Int + Int))\n (12, :(Int * Int))\n (13, :x)
\n\n\n

We can use this index to extract information from the grammar.

\n\n\n

isterminal

isterminal returns true if a rule is terminal, i.e. it cannot be expanded. For example, rule 1 is terminal, but rule 11 is not, since it contains the non-terminal symbol :Int.

\n\n
HerbGrammar.isterminal(g₇, 1)
\n
true
\n\n
HerbGrammar.isterminal(g₇, 11)
\n
false
\n\n\n

return_type

This function is rather obvious; it returns the non-terminal symbol that corresponds to a certain rule. The return type for all rules in our grammar is :Int.

\n\n
HerbGrammar.return_type(g₇, 11)
\n
:Int
\n\n\n

child_types

child_types returns the types of the nonterminal children of a rule in a vector. If you just want to know how many children a rule has, and not necessarily which types they have, you can use nchildren

\n\n
HerbGrammar.child_types(g₇, 11)
\n
2-element Vector{Symbol}:\n :Int\n :Int
\n\n
HerbGrammar.nchildren(g₇, 11)
\n
2
\n\n\n

nonterminals

The nonterminals function can be used to obtain a list of all nonterminals in the grammar.

\n\n
HerbGrammar.nonterminals(g₇)
\n
1-element Vector{Symbol}:\n :Int
\n\n\n

Adding rules

It is also possible to add rules to a grammar during execution. This can be done using the add_rule! function. The exclamatin mark is a Julia convention and is appended to name if a function modifies its arguments (in our example the grammar).

A rule can be provided in the same syntax as is used in the grammar definition. The rule should be of the Expr type, which is a built-in type for representing expressions. An easy way of creating Expr values in Julia is to encapsulate it in brackets and use a colon as prefix:

\n\n
HerbGrammar.add_rule!(g₇, :(Int = Int - Int))
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n14: Int = Int - Int\n
\n\n\n

Removing rules

It is also possible to remove rules in Herb.jl, however, this is a bit more involved. As said before, rules have an index associated with them. The internal representation of programs that are defined by the grammar makes use of those indices for efficiency. Blindly removing a rule would shift the indices of other rules, and this could mean that existing programs get a different meaning or become invalid.

Therefore, there are two functions for removing rules:

  • remove_rule! removes a rule from the grammar, but fills its place with a placeholder. Therefore, the indices stay the same, and only programs that use the removed rule become invalid.

  • cleanup_removed_rules! removes all placeholders and shifts the indices of the other rules.

\n\n
HerbGrammar.remove_rule!(g₇, 11)
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: nothing = nothing\n12: Int = Int * Int\n13: Int = x\n14: Int = Int - Int\n
\n\n
HerbGrammar.cleanup_removed_rules!(g₇)
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = x\n13: Int = Int - Int\n
\n\n","category":"page"},{"location":"tutorials/defining_grammars/#Context-sensitive-grammars","page":"Defining Grammars in Herb.jl","title":"Context-sensitive grammars","text":"","category":"section"},{"location":"tutorials/defining_grammars/","page":"Defining Grammars in Herb.jl","title":"Defining Grammars in Herb.jl","text":"
\n

Context-sensitive grammars introduce additional constraints compared to context-free grammars (like the simple grammar examples above). As before, we use the @csgrammar macro:

\n\n
g₈ = HerbGrammar.@csgrammar begin\n    Int = |(0:9)\n    Int = Int + Int\n    Int = Int * Int\n    Int = x\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n
\n\n\n

Constraints can be added using the addconstraint! function, which takes a context-sensitive grammar and a constraint and adds the constraint to the grammar.

For example, we can add a `constraint to enforce that the input symbolx` (rule 13) appears at least once in the program, to avoid programs that are just a constant.

\n\n
HerbGrammar.addconstraint!(g₈, Contains(13))
\n
1-element Vector{HerbCore.AbstractConstraint}:\n Contains(13)
\n\n\n

There is a dedicated tutorial for constraints in Herb.jl and how to work with them.

\n\n\n

Probabilistic grammars

Herb.jl also supports probabilistic grammars. These grammars allow the user to assign a probability to each rule in the grammar. A probabilistic grammar can be defined in a very similar way to a standard grammar, but has some slightly different syntax:

\n\n
begin\n    g₉ = HerbGrammar.@pcsgrammar begin\n        0.4 : Int = |(0:9)\n        0.2 : Int = Int + Int\n        0.1 : Int = Int * Int\n        0.3 : Int = x\n    end\n    \n    for r ∈ 1:length(g₃.rules)\n        p = HerbGrammar.probability(g₈, r)\n    \n        println(\"$p : $r\")\n    end\nend
\n\n\n\n

The numbers before each rule represent the probability assigned to that rule. The total probability for each return type should add up to 1.0. If this isn't the case, Herb.jl will normalize the probabilities.

If a single line in the grammar definition represents multiple rules, such as 0.4 : Int = |(0:9), the probability will be evenly divided over all these rules.

\n\n","category":"page"},{"location":"tutorials/defining_grammars/#File-writing","page":"Defining Grammars in Herb.jl","title":"File writing","text":"","category":"section"},{"location":"tutorials/defining_grammars/","page":"Defining Grammars in Herb.jl","title":"Defining Grammars in Herb.jl","text":"
\n

Saving & loading context-free grammars

If you want to store a grammar on the disk, you can use the store_csg, read_csg and functions to store and read grammars respectively. The store_csg grammar can also be used to store probabilistic grammars. To read probabilistic grammars, use read_pcsg. The stored grammar files can also be opened using a text editor to be modified, as long as the contents of the file doesn't violate the syntax for defining grammars.

\n\n
HerbGrammar.store_csg(g₇, \"demo.txt\")
\n\n\n
HerbGrammar.read_csg(\"demo.txt\")
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = x\n13: Int = Int - Int\n
\n\n\n

Saving & loading context-sensitive grammars

Saving and loading context-sensitive grammars is very similar to how it is done with context-free grammars. The only difference is that an additional file is created for the constraints. The file that contains the grammars can be edited and can also be read using the reader for context-free grammars. The file that contains the constraints cannot be edited.

\n\n
HerbGrammar.store_csg( g₈, \"demo.grammar\", \"demo.constraints\")
\n\n\n
g₈, g₈.constraints
\n
(1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n, HerbCore.AbstractConstraint[Contains(13)])
\n\n
g₁₀  = HerbGrammar.read_csg(\"demo.grammar\", \"demo.constraints\")
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n
\n\n
g₁₀, g₁₀.constraints
\n
(1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n, HerbCore.AbstractConstraint[Contains(13)])
\n\n","category":"page"},{"location":"tutorials/defining_grammars/","page":"Defining Grammars in Herb.jl","title":"Defining Grammars in Herb.jl","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/defining_grammars.jl\"","category":"page"},{"location":"tutorials/TopDown/#Building-Herb-Iterators","page":"Top Down Iterator","title":"Building Herb Iterators","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The core building block in Herb is a program iterator. A program iterator represents a walk through the program space; different iterators provide different ways of iterating through program space. From the program synthesis point of view, program iterators actaully represent program spaces.","category":"page"},{"location":"tutorials/TopDown/#Iterator-hierarchy","page":"Top Down Iterator","title":"Iterator hierarchy","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Program iterators are organised in a hierarchy. The top-level abstract type is ProgramIterator. At the next level of the hierarchy lie commonly used search families:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"TopDownIterator for top-down traversals\nStochasticSearachIterator for traversals with stochastic search\nBottomUpIterator for bottom-up search","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Stochastic search further provides specific iterators:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"MHSearchIterator for program traversal with Metropolis-Hastings algorithm\nVLNSearchIterator for traversals with Very Large Neighbourhood Search\nSASearchIterator for Simulated Annealing","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"We provide generic and customiseable implementations of each of these iterators, so that users can easily tweak them by through multiple dispatch. Keep reading!","category":"page"},{"location":"tutorials/TopDown/#Iterator-design","page":"Top Down Iterator","title":"Iterator design","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Program iterators follow the standard Julia Iterator interface. That is, every iterator should implement two functions:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"iterate(<:ProgramIterator)::(RuleNode,Any) to get the first program. The function takes a program iterator as an input, returning the first program and a state (which can be anything)\niterate(<:ProgramIterator,Any)::(RuleNode,Any) to get the consequtive programs. The function takes the progrma iterator and the state from the previous iteration, and return the next program and the next state.","category":"page"},{"location":"tutorials/TopDown/#Top-Down-iterator","page":"Top Down Iterator","title":"Top Down iterator","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"We illustarate how to build iterators with a Top Down iterator. The top Down iterator is build as a best-first iterator: it maintains a priority queue of programs and always pops the first element of the queue. The iterator is customiseable through the following functions:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"priority_function: dictating the order of programs in the priority queue\nderivation_heuristic: dictating in which order to explore the derivations rules within a single hole\nhole_heuristic: dictating which hole to expand next","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The first call to iterate(iter::TopDownIterator):","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"function Base.iterate(iter::TopDownIterator)\n # Priority queue with `SolverState`s (for variable shaped trees) and `UniformIterator`s (for fixed shaped trees)\n pq :: PriorityQueue{Union{SolverState, UniformIterator}, Union{Real, Tuple{Vararg{Real}}}} = PriorityQueue()\n\n solver = iter.solver\n\n if isfeasible(solver)\n enqueue!(pq, get_state(solver), priority_function(iter, get_grammar(solver), get_tree(solver), 0, false))\n end\n return _find_next_complete_tree(iter.solver, pq, iter)\nend","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The first call steps everything up: it initiates the priority queue, the constraint solver (more on that later), and return the first program. The function _find_next_complete_tree(iter.solver, pq, iter) does a lot of heavy lifting here; we will cover it later, but the only important thing is that it finds the next complete program in the priority queue (because, in case of top down enumeration, the queue also contains partial programs which we only want to expand, but not return to the user).","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The subsequent call to iterate(iter::TopDownIterator, pq::DataStructures.PriorityQueue) are quite simple: all that is needed is to find the next complete program in the priority queue:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"function Base.iterate(iter::TopDownIterator, pq::DataStructures.PriorityQueue)\n return _find_next_complete_tree(iter.solver, pq, iter)\nend","category":"page"},{"location":"tutorials/TopDown/#Modifying-the-provided-iterator","page":"Top Down Iterator","title":"Modifying the provided iterator","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"If you would like to, for example, modify the priority function, you don't have to implement the iterator from scratch. You simply need to create a new type and inherit from the TopDownIterator:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"abstract type MyTopDown <: TopDownIterator end.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"What is left is to implement the priority function, multiple-dispatching it over the new type. For example, to do a random order:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"function priority_function(\n ::MyTopDown, \n ::AbstractGrammar, \n ::AbstractRuleNode, \n ::Union{Real, Tuple{Vararg{Real}}},\n ::Bool\n)\n Random.rand();\nend","category":"page"},{"location":"tutorials/TopDown/#A-note-on-data-structures","page":"Top Down Iterator","title":"A note on data structures","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"As you have probably noticed, the priority queue some strange data structures: SolverState and UniformIterator; the top down iterator never puts RuleNodes into the queue. In fact, the iterator never directly manipulates RuleNodes itself, but that is rather delegated to the constraint solver. The constraint solver will do a lot of work to reduce the number of programs we have to consider. The SolverState and UniformIterator are specialised data structure to improve the efficiency and memory usage. ","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Herb uses a data structure of UniformTrees to represent all programs with an AST of the same shape, where each node has the same type. the UniformIterator is an iterator efficiently iterating over that structure.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The SolverState represents non-uniform trees – ASTs whose shape we haven't compeltely determined yet. SolverState is used as an intermediate representation betfore we reach UniformTrees on which partial constraint propagation is done.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"In principle, you should never construct ASTs yourself directly; you should leave that to the constraint solver.","category":"page"},{"location":"tutorials/TopDown/#Extra:-Find-Next-Complete-Tree-/-Program","page":"Top Down Iterator","title":"Extra: Find Next Complete Tree / Program","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"This function pops an element from the priority queue whilst it is not empty, and then checks what kind of iterator it is.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"function _find_next_complete_tree(\n solver::Solver,\n pq::PriorityQueue,\n iter::TopDownIterator\n)\n while length(pq) ≠ 0\n (item, priority_value) = dequeue_pair!(pq)\n","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"If it is a Uniform Iterator, that is an interator where all the holes have the same shape, then it iterates over the solutions.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"\n if item isa UniformIterator\n #the item is a fixed shaped solver, we should get the next solution and re-enqueue it with a new priority value\n uniform_iterator = item\n solution = next_solution!(uniform_iterator)\n if !isnothing(solution)\n enqueue!(pq, uniform_iterator, priority_function(iter, get_grammar(solver), solution, priority_value, true))\n return (solution, pq)\n end\n","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"If it is not a Uniform Iterator, we find a hole to branch on. If the holes are all uniform, a Uniform Iterator is created, and is enqueued. If iterating on the holes would exceed a maximum depth, nothing new is enqueued. Lastly, if the holes aren't the same shape, we branch / partition on the holes, to create new partial domains to enqueue.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":" elseif item isa SolverState\n #the item is a solver state, we should find a variable shaped hole to branch on\n state = item\n load_state!(solver, state)\n\n hole_res = hole_heuristic(iter, get_tree(solver), get_max_depth(solver))\n if hole_res ≡ already_complete\n uniform_solver = UniformSolver(get_grammar(solver), get_tree(solver), with_statistics=solver.statistics)\n uniform_iterator = UniformIterator(uniform_solver, iter)\n solution = next_solution!(uniform_iterator)\n if !isnothing(solution)\n enqueue!(pq, uniform_iterator, priority_function(iter, get_grammar(solver), solution, priority_value, true))\n return (solution, pq)\n end\n elseif hole_res ≡ limit_reached\n # The maximum depth is reached\n continue\n elseif hole_res isa HoleReference\n # Variable Shaped Hole was found\n (; hole, path) = hole_res\n \n partitioned_domains = partition(hole, get_grammar(solver))\n number_of_domains = length(partitioned_domains)\n for (i, domain) ∈ enumerate(partitioned_domains)\n if i < number_of_domains\n state = save_state!(solver)\n end\n @assert isfeasible(solver) \"Attempting to expand an infeasible tree: $(get_tree(solver))\"\n remove_all_but!(solver, path, domain)\n if isfeasible(solver)\n enqueue!(pq, get_state(solver), priority_function(iter, get_grammar(solver), get_tree(solver), priority_value, false))\n end\n if i < number_of_domains\n load_state!(solver, state)\n end\n end\n end\n\n","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Otherwise, throw an exception, because we came across an unexpected iterator type.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":" else\n throw(\"BadArgument: PriorityQueue contains an item of unexpected type '$(typeof(item))'\")\n end\n end\n return nothing\nend","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"CurrentModule=Herb","category":"page"},{"location":"#[Herb.jl](https://github.com/Herb-AI/Herb.jl)","page":"Herb.jl","title":"Herb.jl","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"A library for defining and efficiently solving program synthesis tasks in Julia.","category":"page"},{"location":"#Why-Herb.jl?","page":"Herb.jl","title":"Why Herb.jl?","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"When writing research software we almost always investigate highly specific properties or algorithms of our domain, leading to us building the tools from scratch over and over again. The very same holds for the field of program synthesis: Tools are hard to run, benchmarks are hard to get and prepare, and its hard to adapt our existing code to a novel idea.","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Herb.jl will take care of this for you and helps you defining, solving and extending your program synthesis problems.","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Herb.jl provides...","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"a unified and universal framework for program synthesis\nHerb.jl allows you to describe all sorts of program synthesis problems using context-free grammars\na number of state-of-the-art benchmarks and solvers already implemented and usable out-of-the-box","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Herb.jl's sub-packages provide fast and easily extendable implementations of ","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"various static and dynamic search strategies,\nlearning search strategies, sampling techniques and more,\nconstraint formulation and propagation, \neasy grammar formulation and usage,\nwide-range of usable program interpreters and languages + the possibility to use your own, and \nefficient data formulation.","category":"page"},{"location":"#Why-Julia?","page":"Herb.jl","title":"Why Julia?","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Julia is a perfect fit for program synthesis due to numerous reasons. Starting from scientific reasons like speed of execution and composability over to practical reasons like speed of writing Julia code. For a full ode on why to use Julia, please see the WhyJulia manifesto.","category":"page"},{"location":"#Sub-Modules","page":"Herb.jl","title":"Sub-Modules","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Herb's functionality is distributed among several sub-packages:","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"HerbCore.jl: The core of Herb.jl defining abstract concepts,\nHerbGrammar.jl: Functionality for declaring grammars,\nHerbSpecification.jl: For describing user intent as specifications,\nHerbInterpret.jl: For running programs in different languages and environments,\nHerbConstraints.jl: For defining and effectively propagating and managing constraints during search, and\nHerbSearch.jl: For actually searching for solutions.","category":"page"},{"location":"#Basics","page":"Herb.jl","title":"Basics","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Pages = [\"install.md\", \"get_started.md\", \"concepts.md\"]","category":"page"},{"location":"#Advanced-content","page":"Herb.jl","title":"Advanced content","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"","category":"page"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"\n\n\n\n\n

Herb tutorial: Abstract syntax trees

\n\n\n

In this tutorial, you will learn

  • How to represent a computer program as an abstract syntax tree in Herb.

  • How to replace parts of the tree to modify the program.

\n\n","category":"page"},{"location":"tutorials/abstract_syntax_trees/#Abstract-syntax-trees","page":"Abstract Syntax Trees","title":"Abstract syntax trees","text":"","category":"section"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"
\n

The syntactic structure of a computer program can be represented in a hierarchical tree structure, a so-called Abstract Syntax Tree (AST). The syntax of a programming language is typically defined using a formal grammar, a set of rules on how valid programs can be constructed. ASTs are derived from the grammar, but are abstractions in the sense that they omit details such as parenthesis, semicolons, etc. and only retain what's necessary to capture the program structure.

In the context of program synthesis, ASTs are often used to define the space of all possible programs which is searched to find one that satisfies the given specifications. During the search process, different ASTs, each corresponding to a different program, are generated and evaluated until a suitable one is found.

Each node of the AST represents a construct in the program (e.g., a variable, an operator, a statement, or a function) and this construct corresponds to a rule in the formal grammar. An edge describes the relationship between constructs, and the tree structure captures the nesting of constructs.

\n\n","category":"page"},{"location":"tutorials/abstract_syntax_trees/#A-simple-example-program","page":"Abstract Syntax Trees","title":"A simple example program","text":"","category":"section"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"
\n

We first consider the simple program 5*(x+3). We will define a grammar that is sufficient to represent this program and use it to construct a AST for our program.

\n\n\n

Define the grammar

\n\n
begin \n    using PlutoUI\n    \n    using HerbCore\n    using HerbGrammar\n    using HerbInterpret\nend
\n\n\n
grammar = @csgrammar begin\n        Number = |(0:9)\n        Number = x\n        Number = Number + Number\n        Number = Number * Number\n    end
\n
1: Number = 0\n2: Number = 1\n3: Number = 2\n4: Number = 3\n5: Number = 4\n6: Number = 5\n7: Number = 6\n8: Number = 7\n9: Number = 8\n10: Number = 9\n11: Number = x\n12: Number = Number + Number\n13: Number = Number * Number\n
\n\n\n

Construct the syntax tree

\n\n\n

The AST of this program is shown in the diagram below. The number in each node refers to the index of the corresponding rule in our grammar.

\n\n\n
    flowchart \n    id1((13)) ---\n    id2((6))\n    id1 --- id3((12))\n    id4((11))\n    id5((4))\n    id3 --- id4\n    id3 --- id5
\n\n\n
    flowchart \n    id1((13)) ---\n    id2((6))\n    id1 --- id3((12))\n    id4((11))\n    id5((4))\n    id3 --- id4\n    id3 --- id5
\n\n\n

In Herb.jl, the HerbCore.RuleNode is used to represent both an individual node, but also entire ASTs or sub-trees. This is achieved by nesting instances of RuleNode. A RuleNode can be instantiated by providing the index of the grammar rule that the node represents and a vector of child nodes.

\n\n
syntaxtree = RuleNode(13, [RuleNode(6), RuleNode(12, [RuleNode(11), RuleNode(4)])])
\n
13{6,12{11,4}}
\n\n\n

We can confirm that our AST is correct by displaying it in a more human-readable way, using HerbGrammar.rulenode2expr and by testing it on a few input examples using HerbInterpret.execute_on_input.

\n\n
rulenode2expr(syntaxtree, grammar)
\n
:(5 * (x + 3))
\n\n
# test solution on inputs\nexecute_on_input(grammar, syntaxtree, Dict(:x => 10))
\n
65
\n\n","category":"page"},{"location":"tutorials/abstract_syntax_trees/#Another-example:-FizzBuzz","page":"Abstract Syntax Trees","title":"Another example: FizzBuzz","text":"","category":"section"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"
\n

Let's look at a more interesting example. The program fizbuzz() is based on the popular FizzBuzz problem. Given an integer number, the program simply returns a String of that number, but replace numbers divisible by 3 with \"Fizz\", numbers divisible by 5 with \"Buzz\", and number divisible by both 3 and 5 with \"FizzBuzz\".

\n\n
function fizzbuzz(x)\n    if x % 5 == 0 && x % 3 == 0\n        return \"FizzBuzz\"\n    else\n        if x % 3 == 0\n            return  \"Fizz\"\n        else\n            if x % 5 == 0\n                return \"Buzz\"\n            else\n                return string(x)\n            end\n        end\n    end\nend
\n
fizzbuzz (generic function with 1 method)
\n\n\n

Define the grammar

Let's define a grammar with all the rules that we need.

\n\n
grammar_fizzbuzz = @csgrammar begin\n    Int = input1\n    Int = 0 | 3 | 5\n    String = \"Fizz\" | \"Buzz\" | \"FizzBuzz\"\n    String = string(Int)\n    Return = String\n    Int = Int % Int\n    Bool = Int == Int\n    Int = Bool ? Int : Int\n    Bool = Bool && Bool\nend
\n
1: Int = input1\n2: Int = 0\n3: Int = 3\n4: Int = 5\n5: String = Fizz\n6: String = Buzz\n7: String = FizzBuzz\n8: String = string(Int)\n9: Return = String\n10: Int = Int % Int\n11: Bool = Int == Int\n12: Int = if Bool\n    Int\nelse\n    Int\nend\n13: Bool = Bool && Bool\n
\n\n\n

Construct the syntax tree

\n\n\n

Given the grammar, the AST of fizzbuzz() looks like this:

\n\n\n
flowchart \n    id1((12)) --- id21((13))\n    id1--- id22((9))\n    id1--- id23((12))\n\n    id21 --- id31((11))\n    id21 --- id32((11))\n\n    id31 --- id41((10))\n    id31 --- id42((2))\n\n    id41 --- id51((1))\n    id41 --- id52((4))\n\n    id32 --- id43((10)) \n    id32 --- id44((2))\n\n    id43 --- id53((1))\n    id43 --- id54((3))\n\n    id22 --- id33((7))\n    id23 --- id34((11))\n\n    id34 --- id45((10))\n    id34 --- id46((2))\n\n    id45 --- id55((1))\n    id45 --- id56((3))\n\n    id23 --- id35((9))\n    id35 --- id47((5))\n\n    id23 --- id36((12))\n    id36 --- id48((11))\n    id48 --- id57((10))\n    id57 --- id61((1))\n    id57 --- id62((4))\n    id48 --- id58((2))\n\n    id36 --- id49((9))\n    id49 --- id59((6))\n\n    id36 --- id410((9))\n    id410 --- id510((8))\n    id510 --- id63((1))\n    \n    \n    
\n\n\n

As before, we use nest instanced of RuleNode to implement the AST.

\n\n
fizzbuzz_syntaxtree = RuleNode(12, [\n               RuleNode(13, [\n                   RuleNode(11, [\n                       RuleNode(10, [\n                           RuleNode(1),\n                           RuleNode(4)\n                       ]),\n                       RuleNode(2)\n                   ]),\n                   RuleNode(11, [\n                       RuleNode(10, [\n                           RuleNode(1),\n                           RuleNode(3)\n                       ]),\n                       RuleNode(2)\n                   ])\n               ]),\n               RuleNode(9, [\n                   RuleNode(7)\n               \n               ]),\n               RuleNode(12, [\n                   RuleNode(11, [\n                       RuleNode(10, [\n                           RuleNode(1),\n                           RuleNode(3),\n                       ]),\n                       RuleNode(2)\n                   ]),\n                   RuleNode(9, [\n                       RuleNode(5)\n                   ]),\n                   RuleNode(12, [\n                       RuleNode(11, [\n                           RuleNode(10, [\n                               RuleNode(1),\n                               RuleNode(4)\n                           ]),\n                           RuleNode(2)\n                       ]),\n                       RuleNode(9, [\n                           RuleNode(6)\n                       ]),\n                       RuleNode(9, [\n                           RuleNode(8, [\n                                RuleNode(1)\n                            ])\n                       ])\n                   ])\n               ]) \n    ])
\n
12{13{11{10{1,4}2}11{10{1,3}2}}9{7}12{11{10{1,3}2}9{5}12{11{10{1,4}2}9{6}9{8{1}}}}}
\n\n\n

And we check our syntax tree is correct:

\n\n
rulenode2expr(fizzbuzz_syntaxtree, grammar_fizzbuzz)
\n
:(if input1 % 5 == 0 && input1 % 3 == 0\n      \"FizzBuzz\"\n  else\n      if input1 % 3 == 0\n          \"Fizz\"\n      else\n          if input1 % 5 == 0\n              \"Buzz\"\n          else\n              string(input1)\n          end\n      end\n  end)
\n\n
begin\n    # test solution on inputs\n    input = [Dict(:input1 => 3), Dict(:input1 => 5), Dict(:input1 =>15), Dict(:input1 => 22)]\n    output1 = execute_on_input(grammar_fizzbuzz, fizzbuzz_syntaxtree, input)\n    output1\nend
\n
4-element Vector{Any}:\n \"Fizz\"\n \"Buzz\"\n \"FizzBuzz\"\n \"22\"
\n\n\n

Modify the AST/program

There are several ways to modify an AST and hence, a program. You can

  • directly replace a node with HerbCore.swap_node()

  • insert a rule node with insert!

Let's modify our example such that if the input number is divisible by 3, the program returns \"Buzz\" instead of \"Fizz\". We use swap_node() to replace the node of the AST that corresponds to rule 5 in the grammar (String = Fizz) with rule 6 (String = Buzz). To do so, swap_node() needs the tree that contains the node we want to modify, the new node we want to replace the node with, and the path to that node.

Note that swap_node() modifies the tree, hence we make a deep copy of it first.

\n\n
begin\n    modified_fizzbuzz_syntaxtree = deepcopy(fizzbuzz_syntaxtree)\n    newnode = RuleNode(6)\n    path = [3, 2, 1]\n    swap_node(modified_fizzbuzz_syntaxtree, newnode, path)\n    rulenode2expr(modified_fizzbuzz_syntaxtree, grammar_fizzbuzz)\nend
\n
:(if input1 % 5 == 0 && input1 % 3 == 0\n      \"FizzBuzz\"\n  else\n      if input1 % 3 == 0\n          \"Buzz\"\n      else\n          if input1 % 5 == 0\n              \"Buzz\"\n          else\n              string(input1)\n          end\n      end\n  end)
\n\n\n

Let's confirm that we modified the AST, and hence the program, correctly:

\n\n
# test solution on same inputs as before\nexecute_on_input(grammar_fizzbuzz, modified_fizzbuzz_syntaxtree, input)
\n
4-element Vector{Any}:\n \"Buzz\"\n \"Buzz\"\n \"FizzBuzz\"\n \"22\"
\n\n\n

An alternative way to modify the AST is by using insert!(). This requires to provide the location of the node that we want to as NodeLoc. NodeLoc points to a node in the tree and consists of the parent and the child index of the node. Again, we make a deep copy of the original AST first.

\n\n
begin\n    anothermodified_fizzbuzz_syntaxtree = deepcopy(fizzbuzz_syntaxtree)\n    # get the node we want to modify and instantiate a NodeLoc from it.\n    node = get_node_at_location(anothermodified_fizzbuzz_syntaxtree, [3, 2, 1])\n    nodeloc = NodeLoc(node, 0)\n    # replace the node\n    insert!(node, nodeloc, newnode)\n    rulenode2expr(anothermodified_fizzbuzz_syntaxtree, grammar_fizzbuzz)\nend
\n
:(if input1 % 5 == 0 && input1 % 3 == 0\n      \"FizzBuzz\"\n  else\n      if input1 % 3 == 0\n          \"Buzz\"\n      else\n          if input1 % 5 == 0\n              \"Buzz\"\n          else\n              string(input1)\n          end\n      end\n  end)
\n\n\n

Again, we check that we modified the program as intended:

\n\n
# test on same inputs as before\nexecute_on_input(grammar_fizzbuzz, anothermodified_fizzbuzz_syntaxtree, input)
\n
4-element Vector{Any}:\n \"Buzz\"\n \"Buzz\"\n \"FizzBuzz\"\n \"22\"
\n\n","category":"page"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/abstract_syntax_trees.jl\"","category":"page"}] +[{"location":"install/#Installation-Guide","page":"Installation Guide","title":"Installation Guide","text":"","category":"section"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"Before installing Herb.jl, ensure that you have a running Julia distribution installed (Julia version 1.7 and above were tested). ","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"Thanks to Julia's package management, installing Herb.jl is very straighforward. Activate the default Julia REPL using","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"julia","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"or from within one of your projects using","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"julia --project=.","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"From the Julia REPL run ","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"]\nadd Herb","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"or instead running","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"import Pkg\nPkg.add(\"Herb\")","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"which will both install all dependencies automatically.","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"For later convenience we can also add the respective dependencies to our project, so that we do not have to write Herb.HerbGrammar every time.","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"] add HerbConstraints HerbCore HerbSpecification HerbInterpret HerbGrammar HerbSearch","category":"page"},{"location":"install/","page":"Installation Guide","title":"Installation Guide","text":"And just like this you are done! Welcome to Herb.jl!","category":"page"},{"location":"concepts/#Architecture-and-core-concepts","page":"Architecture and core concepts","title":"Architecture and core concepts","text":"","category":"section"},{"location":"HerbSearch/#HerbSearch_docs","page":"HerbSearch.jl","title":"HerbSearch.jl Documentation","text":"","category":"section"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"CurrentModule=HerbSearch","category":"page"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"Modules = [HerbSearch]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbSearch/#HerbSearch.BFSIterator","page":"HerbSearch.jl","title":"HerbSearch.BFSIterator","text":"@programiterator BFSIterator() <: TopDownIterator\n\nReturns a breadth-first iterator given a grammar and a starting symbol. Returns trees in the grammar in increasing order of size. Inherits all stop-criteria from TopDownIterator.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.DFSIterator","page":"HerbSearch.jl","title":"HerbSearch.DFSIterator","text":"@programiterator DFSIterator() <: TopDownIterator\n\nReturns a depth-first search enumerator given a grammar and a starting symbol. Returns trees in the grammar in decreasing order of size. Inherits all stop-criteria from TopDownIterator.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.ExpandFailureReason","page":"HerbSearch.jl","title":"HerbSearch.ExpandFailureReason","text":"@enum ExpandFailureReason limit_reached=1 already_complete=2\n\nRepresentation of the different reasons why expanding a partial tree failed. Currently, there are two possible causes of the expansion failing:\n\nlimit_reached: The depth limit or the size limit of the partial tree would be violated by the expansion\nalready_complete: There is no hole left in the tree, so nothing can be expanded.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.FixedShapedIterator","page":"HerbSearch.jl","title":"HerbSearch.FixedShapedIterator","text":"@programiterator FixedShapedIterator()\n\nEnumerates all programs that extend from the provided fixed shaped tree. The Solver is required to be in a state without any Holes.\n\n!!! warning: this iterator is used as a baseline for the constraint propagation thesis. After the thesis, this iterator can (and should) be deleted.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.GeneticSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.GeneticSearchIterator","text":"GeneticSearchIterator{FitnessFunction,CrossOverFunction,MutationFunction,SelectParentsFunction,EvaluationFunction} <: ProgramIterator\n\nDefines an ProgramIterator using genetic search. \n\nConsists of:\n\nexamples::Vector{<:IOExample}: a collection of examples defining the specification \nevaluation_function::EvaluationFunction: interpreter to evaluate the individual programs\npopulation_size::Int64: number of inviduals in the population\nmutation_probability::Float64: probability of mutation for each individual\nmaximum_initial_population_depth::Int64: maximum depth of trees when population is initialized \n\nend\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.MHSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.MHSearchIterator","text":"MHSearchIterator(examples::AbstractArray{<:IOExample}, cost_function::Function, evaluation_function::Function=HerbInterpret.execute_on_input)\n\nReturns an enumerator that runs according to the Metropolis Hastings algorithm.\n\nspec : array of examples\ncost_function : cost function to evaluate the programs proposed\nevaluation_function : evaluation function that evaluates the program generated and produces an output\n\nThe propose function is randomfillpropose and the accept function is probabilistic. The temperature value of the algorithm remains constant over time.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.MLFSIterator","page":"HerbSearch.jl","title":"HerbSearch.MLFSIterator","text":"@programiterator MLFSIterator() <: TopDownIterator\n\nIterator that enumerates expressions in the grammar in decreasing order of probability (Only use this iterator with probabilistic grammars). Inherits all stop-criteria from TopDownIterator.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.ProgramIterator","page":"HerbSearch.jl","title":"HerbSearch.ProgramIterator","text":"abstract type ProgramIterator\n\nGeneric iterator for all possible search strategies. All iterators are expected to have the following fields:\n\ngrammar::ContextSensitiveGrammar: the grammar to search over\nsym::Symbol: defines the start symbol from which the search should be started \nmax_depth::Int: maximum depth of program trees\nmax_size::Int: maximum number of AbstractRuleNodes of program trees\nmax_time::Int: maximum time the iterator may take\nmax_enumerations::Int: maximum number of enumerations\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.RandomIterator","page":"HerbSearch.jl","title":"HerbSearch.RandomIterator","text":"@programiterator RandomIterator() <: TopDownIterator\n\nIterates trees in the grammar in a random order.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.SASearchIterator","page":"HerbSearch.jl","title":"HerbSearch.SASearchIterator","text":"SASearchIterator(spec, cost_function, initial_temperature=1, temperature_decreasing_factor = 0.99, evaluation_function::Function=HerbInterpret.execute_on_input)\n\nReturns an enumerator that runs according to the Simulated Annealing Search algorithm.\n\nspec : array of examples\ncost_function : cost function to evaluate the programs proposed\ninitial_temperature : the starting temperature of the algorithm\ntemperature_decreasing_factor : the decreasing factor of the temperature of the time\nevaluation_function : evaluation function that evaluates the program generated and produces an output\n\nThe propose function is random_fill_propose (the same as for Metropolis Hastings). The accept function is probabilistic but takes into account the tempeerature too.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.StochasticSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.StochasticSearchIterator","text":"abstract type StochasticSearchIterator <: ProgramIterator\n\nA unified abstract type for the algorithms Metropolis Hastings, Very Large Scale Neighbourhood and Simulated Annealing. Each algorithm implements neighbourhood, propose, accept and temperature functions. Below the signatures of each function is shown.\n\nSignatures\n\n\n\nReturns a node location from the program that is the neighbourhood. It can also return other information using dict\n\nneighbourhood(iter::T, current_program::RuleNode) where T <: StochasticSearchIterator -> (loc::NodeLocation, dict::Dict)\n\nProposes a list of programs using the location provided by neighbourhood and the dict.\n\npropose(iter::T, current_program::RuleNode, neighbourhood_node_loc::NodeLoc, dmap::AbstractVector{Int}, dict::Union{Nothing,Dict{String,Any}}) where T <: StochasticSearchIterator -> Iter[RuleNode]\n\n\n\nBased on the current program and possible cost and temperature it accepts the program or not. Usually we would always want to accept better programs but we might get stuck if we do so. That is why some implementations of the accept function accept with a probability costs that are worse. cost means how different are the outcomes of the program compared to the correct outcomes. The lower the cost the better the program performs on the examples. The cost is provided by the cost_function\n\naccept(::T, currentcost::Real, nextcost::Real, temperature::Real) where T <: StochasticSearchIterator -> Bool\n\nReturns the new temperature based on the previous temperature. Higher the temperature means that the algorithm will explore more.\n\ntemperature(::T, current_temperature::Real) where T <: StochasticSearchIterator -> Real\n\nReturns the cost of the current program. It receives a list of tuples (expected, found) and gives back a cost.\n\ncost_function(outcomes::Tuple{<:Number,<:Number}[]) -> Real\n\n\n\nFields\n\nexamples::Vector{IOExample} example used to check the program\ncost_function::Function\ninitial_temperature::Real = 1 \nevaluation_function::Function that evaluates the julia expressions\n\nAn iterator over all possible expressions of a grammar up to maxdepth with start symbol sym. Also inherits all stop criteria like `maxdepthfromProgramIterator`.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.SynthResult","page":"HerbSearch.jl","title":"HerbSearch.SynthResult","text":"@enum SynthResult optimal_program=1 suboptimal_program=2\n\nRepresentation of the possible results of the synth procedure. At the moment there are two possible outcomes:\n\noptimal_program: The synthesized program satisfies the entire program specification.\nsuboptimal_program: The synthesized program does not satisfy the entire program specification, but got the best score from the evaluator.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.TopDownIterator","page":"HerbSearch.jl","title":"HerbSearch.TopDownIterator","text":"mutable struct TopDownIterator <: ProgramIterator\n\nEnumerates a context-free grammar starting at Symbol sym with respect to the grammar up to a given depth and a given size. The exploration is done using the given priority function for derivations, and the expand function for discovered nodes. Concrete iterators may overload the following methods:\n\npriority_function\nderivation_heuristic\nhole_heuristic\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.UniformIterator","page":"HerbSearch.jl","title":"HerbSearch.UniformIterator","text":"mutable struct UniformIterator\n\nInner iterator that enumerates all candidate programs of a uniform tree.\n\nsolver: the uniform solver.\nouteriter: outer iterator that is responsible for producing uniform trees. This field is used to dispatch on the derivation_heuristic.\nunvisited_branches: for each search-node from the root to the current search-node, a list of unviisted branches.\nnsolutions: number of solutions found so far.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.UniformIterator-Tuple{UniformSolver, Union{Nothing, ProgramIterator}}","page":"HerbSearch.jl","title":"HerbSearch.UniformIterator","text":"UniformIterator(solver::UniformSolver, outeriter::ProgramIterator)\n\nConstructs a new UniformIterator that traverses solutions of the UniformSolver and is an inner iterator of an outer ProgramIterator.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.VLSNSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.VLSNSearchIterator","text":"VLSNSearchIterator(spec, cost_function, enumeration_depth = 2, evaluation_function::Function=HerbInterpret.execute_on_input) = StochasticSearchIterator(\n\nReturns an iterator that runs according to the Very Large Scale Neighbourhood Search algorithm.\n\nspec : array of examples\ncost_function : cost function to evaluate the programs proposed\nvlsn_neighbourhood_depth : the enumeration depth to search for a best program at a time\nevaluation_function : evaluation function that evaluates the program generated and produces an output\n\nThe 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.\n\n\n\n\n\n","category":"type"},{"location":"HerbSearch/#HerbSearch.@programiterator-Tuple{Any, Any}","page":"HerbSearch.jl","title":"HerbSearch.@programiterator","text":"@programiterator\n\nCanonical way of creating a program iterator. The macro automatically declares the expected fields listed in the ProgramIterator documentation. Syntax accepted by the macro is as follows (anything enclosed in square brackets is optional): @programiterator [mutable] ( , ..., ) [<: ] Note that the macro emits an assertion that the SupertypeIterator is a subtype of ProgramIterator which otherwise throws an ArgumentError. If no supertype is given, the new iterator extends ProgramIterator directly. Each may be (almost) any expression valid in a struct declaration, and they must be comma separated. One known exception is that an inner constructor must always be given using the extended function (...) ... end syntax. The mutable keyword determines whether the declared struct is mutable.\n\n\n\n\n\n","category":"macro"},{"location":"HerbSearch/#Base.collect-Tuple{TopDownIterator}","page":"HerbSearch.jl","title":"Base.collect","text":"function Base.collect(iter::TopDownIterator)\n\nReturn an array of all programs in the TopDownIterator. \n\nwarning: Warning\nThis requires deepcopying programs from type StateHole to type RuleNode. If it is not needed to save all programs, iterate over the iterator manually.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{FixedShapedIterator, DataStructures.PriorityQueue}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::FixedShapedIterator, pq::DataStructures.PriorityQueue)\n\nDescribes the iteration for a given TopDownIterator and a PriorityQueue over the grammar without enqueueing new items to the priority queue. Recursively returns the result for the priority queue.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{FixedShapedIterator}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::FixedShapedIterator)\n\nDescribes the iteration for a given TopDownIterator over the grammar. The iteration constructs a PriorityQueue first and then prunes it propagating the active constraints. Recursively returns the result for the priority queue.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{GeneticSearchIterator, HerbSearch.GeneticIteratorState}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::GeneticSearchIterator, current_state::GeneticIteratorState)\n\nIterates the search space using a genetic algorithm. Takes the iterator and the current state to mutate and crossover random inviduals. Returns the best program-so-far and the state of the iterator.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{GeneticSearchIterator}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::GeneticSearchIterator)\n\nIterates the search space using a genetic algorithm. First generates a population sampling random programs. Returns the best program-so-far, and the state of the iterator.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{HerbSearch.StochasticSearchIterator, HerbSearch.IteratorState}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::StochasticSearchIterator, current_state::IteratorState)\n\nThe algorithm that constructs the iterator of StochasticSearchIterator. It has the following structure:\n\nget a random node location -> location,dict = neighbourhood(current_program)\ncall propose on the current program getting a list of full programs\niterate through all the proposals and check if the proposed program is \"better\" than the previous one\n\"accept\" the new program by calling the accept\nreturn the new next_program\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{TopDownIterator, Tuple{Vector{<:AbstractRuleNode}, DataStructures.PriorityQueue}}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::TopDownIterator, pq::DataStructures.PriorityQueue)\n\nDescribes the iteration for a given TopDownIterator and a PriorityQueue over the grammar without enqueueing new items to the priority queue. Recursively returns the result for the priority queue.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.iterate-Tuple{TopDownIterator}","page":"HerbSearch.jl","title":"Base.iterate","text":"Base.iterate(iter::TopDownIterator)\n\nDescribes the iteration for a given TopDownIterator over the grammar. The iteration constructs a PriorityQueue first and then prunes it propagating the active constraints. Recursively returns the result for the priority queue.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.length-Tuple{ProgramIterator}","page":"HerbSearch.jl","title":"Base.length","text":"Base.length(iter::ProgramIterator)\n\nCounts and returns the number of possible programs without storing all the programs. !!! warning: modifies and exhausts the iterator\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.length-Tuple{UniformIterator}","page":"HerbSearch.jl","title":"Base.length","text":"Base.length(iter::UniformIterator)\n\nCounts and returns the number of programs without storing all the programs. !!! warning: modifies and exhausts the iterator\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#Base.rand","page":"HerbSearch.jl","title":"Base.rand","text":"rand(::Type{RuleNode}, grammar::AbstractGrammar, typ::Symbol, max_depth::Int=10)\n\nGenerates a random RuleNode of return type typ and maximum depth max_depth.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#Base.rand-2","page":"HerbSearch.jl","title":"Base.rand","text":"rand(::Type{RuleNode}, grammar::AbstractGrammar, max_depth::Int=10)\n\nGenerates a random RuleNode of arbitrary type and maximum depth max_depth.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#Base.rand-3","page":"HerbSearch.jl","title":"Base.rand","text":"rand(::Type{RuleNode}, grammar::AbstractGrammar, typ::Symbol, dmap::AbstractVector{Int}, max_depth::Int=10)\n\nGenerates a random RuleNode, i.e. an expression tree, of root type typ and maximum depth max_depth guided by a depth map dmap if possible.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#HerbSearch._calculate_cost-Tuple{Union{StateHole, RuleNode}, Function, AbstractVector{IOExample}, AbstractGrammar, Function}","page":"HerbSearch.jl","title":"HerbSearch._calculate_cost","text":"_calculate_cost(program::RuleNode, cost_function::Function, spec::AbstractVector{IOExample}, grammar::AbstractGrammar, evaluation_function::Function)\n\nReturns the cost of the program using the examples and the cost_function. It first convert the program to an expression and evaluates it on all the examples.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch._find_next_complete_tree-Tuple{Solver, DataStructures.PriorityQueue, FixedShapedIterator}","page":"HerbSearch.jl","title":"HerbSearch._find_next_complete_tree","text":"_find_next_complete_tree(solver::Solver, pq::PriorityQueue, iter::FixedShapedIterator)::Union{Tuple{RuleNode, PriorityQueue}, Nothing}\n\nTakes a priority queue and returns the smallest AST from the grammar it can obtain from the queue or by (repeatedly) expanding trees that are in the queue. Returns nothing if there are no trees left within the depth limit.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch._find_next_complete_tree-Tuple{Solver, DataStructures.PriorityQueue, TopDownIterator}","page":"HerbSearch.jl","title":"HerbSearch._find_next_complete_tree","text":"_find_next_complete_tree(solver::Solver, pq::PriorityQueue, iter::TopDownIterator)::Union{Tuple{RuleNode, Tuple{Vector{AbstractRuleNode}, PriorityQueue}}, Nothing}\n\nTakes a priority queue and returns the smallest AST from the grammar it can obtain from the queue or by (repeatedly) expanding trees that are in the queue. Returns nothing if there are no trees left within the depth limit.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.best_accept-Tuple{Real, Real, Real}","page":"HerbSearch.jl","title":"HerbSearch.best_accept","text":"best_accept(current_cost::Real, next_cost::Real, temperature::Real)\n\nReturns true if the cost of the proposed program is smaller than the cost of the current program. Otherwise, returns false.\n\nArguments\n\ncurrent_cost::Real: the cost of the current program.\nnext_cost::Real: the cost of the proposed program.\ntemperature::Real: the temperature; not used.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.calculate_cost-Union{Tuple{T}, Tuple{T, Union{StateHole, RuleNode}}} where T<:HerbSearch.StochasticSearchIterator","page":"HerbSearch.jl","title":"HerbSearch.calculate_cost","text":"calculate_cost(iter::T, program::Union{RuleNode, StateHole}) where T <: StochasticSearchIterator\n\nWrapper around _calculate_cost.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.const_temperature-Tuple{Real}","page":"HerbSearch.jl","title":"HerbSearch.const_temperature","text":"const_temperature(current_temperature::Real)\n\nReturns the temperature unchanged. This function is used by Metropolis Hastings and Very Large Neighbourhood Search algorithms.\n\nArguments\n\ncurrent_temperature::Real: the current temperature of the search.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.constructNeighbourhood-Tuple{RuleNode, AbstractGrammar}","page":"HerbSearch.jl","title":"HerbSearch.constructNeighbourhood","text":"constructNeighbourhood(current_program::RuleNode, grammar::AbstractGrammar)\n\nThe neighbourhood node location is chosen at random. The dictionary is nothing.\n\nArguments\n\ncurrent_program::RuleNode: the current program.\ngrammar::AbstractGrammar: the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.constructNeighbourhoodRuleSubset-Tuple{RuleNode, AbstractGrammar}","page":"HerbSearch.jl","title":"HerbSearch.constructNeighbourhoodRuleSubset","text":"constructNeighbourhoodRuleSubset(current_program::RuleNode, grammar::AbstractGrammar)\n\nThe neighbourhood node location is chosen at random. The dictionary is contains one entry with key \"rule_subset\" and value of type Vector{Any} being a random subset of grammar rules.\n\nArguments\n\ncurrent_program::RuleNode: the current program.\ngrammar::AbstractGrammar: the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.cross_over-Tuple{GeneticSearchIterator, RuleNode, RuleNode}","page":"HerbSearch.jl","title":"HerbSearch.cross_over","text":"cross_over(::GeneticSearchIterator, parent_1::RuleNode, parent_2::RuleNode)\n\nCombines the program from two parent individuals to create one or more offspring individuals.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.crossover_swap_children_1-Tuple{RuleNode, RuleNode}","page":"HerbSearch.jl","title":"HerbSearch.crossover_swap_children_1","text":"crossover_swap_children_1(parent_1::RuleNode, parent_2::RuleNode)\n\nPerforms a random crossover of two parents of type RuleNode. The subprograms are swapped and only one altered parent program is returned.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.crossover_swap_children_2-Tuple{RuleNode, RuleNode}","page":"HerbSearch.jl","title":"HerbSearch.crossover_swap_children_2","text":"crossover_swap_children_2(parent_1::RuleNode, parent_2::RuleNode)\n\nPerforms a random crossover of two parents of type RuleNode. The subprograms are swapped and both altered parent programs are returned.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.decreasing_temperature-Tuple{Real}","page":"HerbSearch.jl","title":"HerbSearch.decreasing_temperature","text":"decreasing_temperature(percentage::Real)\n\nReturns a function that produces a temperature decreased by percentage%. This function is used by the Simmulated Annealing algorithm.\n\nArguments\n\npercentage::Real: the percentage to decrease the temperature by.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.default_fitness-Tuple{Any, Any}","page":"HerbSearch.jl","title":"HerbSearch.default_fitness","text":"default_fitness(program, results)\n\nDefines the default fitness function taking the program and its results. Results are a vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}. As we are looking for individuals with the highest fitness function, the error is inverted. \n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.derivation_heuristic-Tuple{RandomIterator, Vector{Int64}}","page":"HerbSearch.jl","title":"HerbSearch.derivation_heuristic","text":"function derivation_heuristic(::RandomIterator, indices::Vector{Int})\n\nRandomly shuffles the rules.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.derivation_heuristic-Tuple{TopDownIterator, Vector{Int64}}","page":"HerbSearch.jl","title":"HerbSearch.derivation_heuristic","text":"function derivation_heuristic(::TopDownIterator, indices::Vector{Int})\n\nReturns a sorted sublist of the indices, based on which rules are most promising to fill a hole. By default, this is the identity function.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.enumerate_neighbours_propose-Tuple{Int64}","page":"HerbSearch.jl","title":"HerbSearch.enumerate_neighbours_propose","text":"enumerate_neighbours_propose(enumeration_depth::Int64)\n\nThe return function is a function that produces a list with all the subprograms with depth at most enumeration_depth.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.evaluate-Tuple{Problem{Vector{IOExample}}, Any, Dict{Symbol, Any}}","page":"HerbSearch.jl","title":"HerbSearch.evaluate","text":"evaluate(problem::Problem{Vector{IOExample}}, expr::Any, tab::SymbolTable; allow_evaluation_errors::Bool=false)\n\nEvaluate the expression on the examples.\n\nOptional parameters:\n\n- `shortcircuit` - Whether to stop evaluating after finding single example fails, to speed up the [synth](@ref) procedure. If true, the returned score is an underapproximation of the actual score.\n- `allow_evaluation_errors` - Whether the search should continue if an exception is thrown in the evaluation or throw the error\n\nReturns a score in the interval [0, 1]\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.extract_name_from_argument-Tuple{Any}","page":"HerbSearch.jl","title":"HerbSearch.extract_name_from_argument","text":"extract_name_from_argument(ex)\n\nExtracts the name of a field declaration, otherwise throws an ArgumentError. A field declaration is either a simple field name with possible a type attached to it or a keyword argument.\n\nExample\n\nx::Int -> x hello -> hello x = 4 -> x x::Int = 3 -> x\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.fitness-Tuple{GeneticSearchIterator, RuleNode, AbstractVector{<:Tuple{Any, Any}}}","page":"HerbSearch.jl","title":"HerbSearch.fitness","text":"fitness(::GeneticSearchIterator, program, results)\n\nAssigns a numerical value (fitness score) to each individual based on how closely it meets the desired objective.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.generate_branches-Tuple{UniformIterator}","page":"HerbSearch.jl","title":"HerbSearch.generate_branches","text":"Returns a vector of disjoint branches to expand the search tree at its current state. Example:\n\n# pseudo code\nHole(domain=[2, 4, 5], children=[\n Hole(domain=[1, 6]), \n Hole(domain=[1, 6])\n])\n\nIf we split on the first hole, this function will create three branches.\n\n(firsthole, 2)\n(firsthole, 4)\n(firsthole, 5)\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.get_best_program-Tuple{Array{RuleNode}, GeneticSearchIterator}","page":"HerbSearch.jl","title":"HerbSearch.get_best_program","text":"get_best_program(population::Array{RuleNode}, iter::GeneticSearchIterator)::RuleNode\n\nReturns the best program within the population with respect to the fitness function.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_leftmost-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_leftmost","text":"heuristic_leftmost(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over holes, where the left-most hole always gets considered first. Returns a HoleReference once a hole is found. This is the default option for enumerators.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_leftmost_fixed_shaped_hole-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_leftmost_fixed_shaped_hole","text":"heuristic_leftmost_fixed_shaped_hole(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over FixedShapeHoles, where the left-most hole always gets considered first. Returns a HoleReference once a hole is found. This is the default option for enumerators.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_random-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_random","text":"heuristic_random(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over holes, where random holes get chosen randomly using random exploration. Returns a HoleReference once a hole is found.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_rightmost-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_rightmost","text":"heuristic_rightmost(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over holes, where the right-most hole always gets considered first. Returns a HoleReference once a hole is found. \n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.heuristic_smallest_domain-Tuple{AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.heuristic_smallest_domain","text":"heuristic_smallest_domain(node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over all available holes in the unfinished AST, by considering the size of their respective domains. A domain here describes the number of possible derivations with respect to the constraints. Returns a HoleReference once a hole is found. \n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.hole_heuristic-Tuple{FixedShapedIterator, AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.hole_heuristic","text":"hole_heuristic(::FixedShapedIterator, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over fixed shaped holes. Returns a HoleReference once a hole is found.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.hole_heuristic-Tuple{TopDownIterator, AbstractRuleNode, Int64}","page":"HerbSearch.jl","title":"HerbSearch.hole_heuristic","text":"hole_heuristic(::TopDownIterator, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n\nDefines a heuristic over variable shaped holes. Returns a HoleReference once a hole is found.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.is_field_decl-Tuple{Any}","page":"HerbSearch.jl","title":"HerbSearch.is_field_decl","text":"is_field_decl(ex)\n\nCheck if extractname(ex) returns a name.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.is_kwdef-Tuple{Any}","page":"HerbSearch.jl","title":"HerbSearch.is_kwdef","text":"is_kwdeg(ex)\n\nChecks if a field declaration is a keyword argument or not. This is called when filtering if the user arguments to the program iteartor are keyword arguments or not.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.mean_squared_error-Tuple{AbstractVector{<:Tuple{Number, Number}}}","page":"HerbSearch.jl","title":"HerbSearch.mean_squared_error","text":"mean_squared_error(results::AbstractVector{Tuple{<:Number,<:Number}})\n\nReturns the mean squared error of results.\n\nArguments\n\nresults<:AbstractVector{<:Tuple{Number,Number}}: the vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.misclassification-Tuple{T} where T<:(AbstractVector{<:Tuple{Number, Number}})","page":"HerbSearch.jl","title":"HerbSearch.misclassification","text":"misclassification(results::AbstractVector{Tuple{<:Number,<:Number}})\n\nReturns the amount of misclassified examples, i.e. how many tuples with non-matching entries are there in results.\n\nArguments\n\nresults<:AbstractVector{<:Tuple{Number,Number}}: the vector of tuples, where each tuple is in the form Tuple{expected_output, actual_output}.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.mutate!","page":"HerbSearch.jl","title":"HerbSearch.mutate!","text":"mutate!(::GeneticSearchIterator, program::RuleNode, grammar::AbstractGrammar, max_depth::Int = 2)\n\nMutates the program of an invididual.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#HerbSearch.mutate_random!","page":"HerbSearch.jl","title":"HerbSearch.mutate_random!","text":"mutate_random!(program::RuleNode, grammar::AbstractGrammar, max_depth::Int64 = 2)\n\nMutates the given program by inserting a randomly generated sub-program at a random location.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#HerbSearch.next_solution!-Tuple{UniformIterator}","page":"HerbSearch.jl","title":"HerbSearch.next_solution!","text":"next_solution!(iter::UniformIterator)::Union{RuleNode, StateHole, Nothing}\n\nSearches for the next unvisited solution. Returns nothing if all solutions have been found already.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{BFSIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::BFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nAssigns priority such that the search tree is traversed like in a BFS manner\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{DFSIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::DFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nAssigns priority such that the search tree is traversed like in a DFS manner\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{FixedShapedIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::FixedShapedIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}})\n\nAssigns a priority value to a tree that needs to be considered later in the search. Trees with the lowest priority value are considered first.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{MLFSIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::MLFSIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nCalculates logit for all possible derivations for a node in a tree and returns them.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{RandomIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::RandomIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nAssigns a random priority to each state.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.priority_function-Tuple{TopDownIterator, AbstractGrammar, AbstractRuleNode, Union{Real, Tuple{Vararg{Real}}}, Bool}","page":"HerbSearch.jl","title":"HerbSearch.priority_function","text":"priority_function(::TopDownIterator, g::AbstractGrammar, tree::AbstractRuleNode, parent_value::Union{Real, Tuple{Vararg{Real}}}, isrequeued::Bool)\n\nAssigns a priority value to a tree that needs to be considered later in the search. Trees with the lowest priority value are considered first.\n\n``: The first argument is a dispatch argument and is only used to dispatch to the correct priority function\ng: The grammar used for enumeration\ntree: The tree that is about to be stored in the priority queue\nparent_value: The priority value of the parent SolverState\nisrequeued: The same tree shape will be requeued. The next time this tree shape is considered, the UniformSolver will produce the next complete program deriving from this shape.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.probabilistic_accept-Tuple{Real, Real, Real}","page":"HerbSearch.jl","title":"HerbSearch.probabilistic_accept","text":"probabilistic_accept(current_cost::Real, next_cost::Real, temperature::Real)\n\nProbabilistically decides whether to accept the new program (next) based on the ratio of costs (smaller is better) between the previous and new program. Returns True if the new program is accepted, False otherwise.\n\nArguments\n\ncurrent_cost::Real: the cost of the current program.\nnext_cost::Real: the cost of the proposed program.\ntemperature::Real: the temperature; not used.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.probabilistic_accept_with_temperature-Tuple{Real, Real, Real}","page":"HerbSearch.jl","title":"HerbSearch.probabilistic_accept_with_temperature","text":"probabilistic_accept_with_temperature(current_cost::Real, next_cost::Real, temperature::Real)\n\nReturns true if the cost of the proposed program is smaller than the cost of the current program. Otherwise, returns true with the probability equal to: \n\n1 (1 + exp(delta temperature))\n\nIn any other case, returns false.\n\nArguments\n\ncurrent_cost::Real: the cost of the current program.\nnext_cost::Real: the cost of the proposed program.\ntemperature::Real: the temperature of the search.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.probabilistic_accept_with_temperature_fraction-Tuple{Real, Real, Real}","page":"HerbSearch.jl","title":"HerbSearch.probabilistic_accept_with_temperature_fraction","text":"probabilistic_accept_with_temperature_fraction(current_cost::Real, program_to_consider_cost::Real, temperature::Real)\n\nProbabilistically decides whether to accept the new program (next) based on the ratio of costs (smaller is better) between the previous and new program multiplied by the temperature. Returns True if the new program is accepted, False otherwise.\n\nArguments\n\ncurrent_cost::Real: the cost of the current program.\nnext_cost::Real: the cost of the proposed program.\ntemperature::Real: the current temperature \n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.processkwarg!-Tuple{Vector{Expr}, Union{Expr, Symbol}}","page":"HerbSearch.jl","title":"HerbSearch.processkwarg!","text":"processkwarg!(keywords::Vector{Expr}, ex::Union{Expr, Symbol})\n\nChecks if ex has a default value specified, if so it returns only the field declaration, and pushes ex to keywords. Otherwise it returns ex\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.random_fill_propose","page":"HerbSearch.jl","title":"HerbSearch.random_fill_propose","text":"random_fill_propose(solver::Solver, path::Vector{Int}, dict::Union{Nothing,Dict{String,Any}}, nr_random=5)\n\nReturns a list with only one proposed, completely random, subprogram.\n\nArguments\n\nsolver::solver: solver\npath::Vector{Int}: path to the location to be filled.\ndict::Dict{String, Any}: the dictionary with additional arguments; not used.\nnr_random=1 : the number of random subprograms to be generated.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#HerbSearch.select_chromosome-Tuple{Array{RuleNode}, Array{<:Real}}","page":"HerbSearch.jl","title":"HerbSearch.select_chromosome","text":"select_chromosome(population::Array{RuleNode}, fitness_array::Array{<:Real})::RuleNode\n\nSelects a chromosome (individual) from the population based on a fitness array. The function uses a fitness-proportionate selection strategy, often referred to as \"roulette wheel\" selection. Assumes fitness_array to be normalized already.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.select_fitness_proportional_parents-Tuple{Array{RuleNode}, Array{<:Real}}","page":"HerbSearch.jl","title":"HerbSearch.select_fitness_proportional_parents","text":"select_fitness_proportional_parents(population::Array{RuleNode}, fitness_array::Array{<:Real})::Tuple{RuleNode,RuleNode}\n\nSelects two parent chromosomes (individuals) from a population based on fitness-proportionate selection. The selected parents can be used for genetic crossover in the next steps of the algorithm.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.select_parents-Tuple{GeneticSearchIterator, Array{RuleNode}, Array{<:Real}}","page":"HerbSearch.jl","title":"HerbSearch.select_parents","text":"select_parents(::GeneticSearchIterator, population::Array{RuleNode}, fitness_array::Array{<:Real})\n\nSelects two parents for the crossover.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.set_stateholes!-Tuple{UniformIterator, Union{StateHole, RuleNode}}","page":"HerbSearch.jl","title":"HerbSearch.set_stateholes!","text":"function set_stateholes!(iter::UniformIterator, node::Union{StateHole, RuleNode})::Vector{StateHole}\n\nDoes a dfs to retrieve all unfilled state holes in the program tree and stores them in the stateholes vector.\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.synth-Tuple{Problem, ProgramIterator}","page":"HerbSearch.jl","title":"HerbSearch.synth","text":"synth(problem::Problem, iterator::ProgramIterator; shortcircuit::Bool=true, allow_evaluation_errors::Bool=false, mod::Module=Main)::Union{Tuple{RuleNode, SynthResult}, Nothing}\n\nSynthesize a program that satisfies the maximum number of examples in the problem. - problem - The problem definition with IO examples - iterator - The iterator that will be used - shortcircuit - Whether to stop evaluating after finding a single example that fails, to speed up the synth procedure. If true, the returned score is an underapproximation of the actual score. - allowevaluationerrors - Whether the search should crash if an exception is thrown in the evaluation - maxtime - Maximum time that the iterator will run - maxenumerations - Maximum number of iterations that the iterator will run - mod - A module containing definitions for the functions in the grammar that do not exist in Main\n\nReturns a tuple of the rulenode representing the solution program and a synthresult that indicates if that program is optimal. synth uses evaluate which returns a score in the interval [0, 1] and checks whether that score reaches 1. If not it will return the best program so far, with the proper flag\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#HerbSearch.validate_iterator-Tuple{Any}","page":"HerbSearch.jl","title":"HerbSearch.validate_iterator","text":"validate_iterator(iter)\n\nValidates the parameters of the iterator\n\n\n\n\n\n","category":"method"},{"location":"HerbSearch/#StatsBase.sample","page":"HerbSearch.jl","title":"StatsBase.sample","text":"sample(root::RuleNode, typ::Symbol, grammar::AbstractGrammar, maxdepth::Int=typemax(Int))\n\nUniformly samples a random node from the tree limited to maxdepth.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#StatsBase.sample-2","page":"HerbSearch.jl","title":"StatsBase.sample","text":"sample(root::RuleNode, typ::Symbol, grammar::AbstractGrammar,\n maxdepth::Int=typemax(Int))\n\nUniformly selects a random node of the given return type typ limited by maxdepth.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#StatsBase.sample-3","page":"HerbSearch.jl","title":"StatsBase.sample","text":"sample(::Type{NodeLoc}, root::RuleNode, maxdepth::Int=typemax(Int))\n\nUniformly selects a random node in the tree no deeper than maxdepth using reservoir sampling. Returns a NodeLoc that specifies the location using its parent so that the subtree can be replaced.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/#StatsBase.sample-4","page":"HerbSearch.jl","title":"StatsBase.sample","text":"StatsBase.sample(::Type{NodeLoc}, root::RuleNode, typ::Symbol, grammar::AbstractGrammar, maxdepth::Int=typemax(Int))\n\nUniformly selects a random node in the tree of a given type, specified using its parent such that the subtree can be replaced. Returns a NodeLoc.\n\n\n\n\n\n","category":"function"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"The HerbSearch package takes care of all operations related to searching for the desired program. This includes","category":"page"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"the functionality to sample a certain program given a grammar,\nthe implementation of several heuristic functions,\nsearching for a program that satisfies the specification, and\nimplementations of several search algorithms in terms of how they enumerate the search space\nBreadth-First Search \nDepth-First Search \nMetropolis Hastings \nVery Large Scale Neighbourhood Search \nSimulated Annealing\nGenetic Search","category":"page"},{"location":"HerbSearch/#Index","page":"HerbSearch.jl","title":"Index","text":"","category":"section"},{"location":"HerbSearch/","page":"HerbSearch.jl","title":"HerbSearch.jl","text":"","category":"page"},{"location":"tutorials/working_with_interpreters/","page":"Working with custom interpreters","title":"Working with custom interpreters","text":"\n\n\n\n
begin\n    using HerbGrammar\n    using HerbInterpret\nend
\n\n\n\n

Using the Julia interpreter

To know how good a candidate program is, program synthesisers execute them. The easiest way to execute a program is to rely on Julia itself. To leverage the Julia interpreter, you only have to ensure that your programs are valid Julia expressions.

For example, assume the following grammar.

\n\n
g = @csgrammar begin\n    Number = |(1:2)\n    Number = x\n    Number = Number + Number\n    Number = Number * Number\nend
\n
1: Number = 1\n2: Number = 2\n3: Number = x\n4: Number = Number + Number\n5: Number = Number * Number\n
\n\n\n

Let's construct a program x+3, which would correspond to the following RuleNode representation

\n\n
myprog = RuleNode(4, [RuleNode(3), RuleNode(1)])
\n
4{3,1}
\n\n\n

To run this program, we have to convert it into a Julia expression, which we can do in the following way:

\n\n
myprog_julia = rulenode2expr(myprog, g)
\n
:(x + 1)
\n\n\n

Now we have a valid Julia expression, but we are still missing one key ingredient: we have to inform the interpreter about the special symbols. In our case, these are :x and :+. To do so, we need to create a symbol table, which is nothing more than a dictionary mapping symbols to their values:

\n\n
symboltable = Dict{Symbol,Any}(:x => 2, :+ => +)
\n
Dict{Symbol, Any} with 2 entries:\n  :+ => +\n  :x => 2
\n\n\n

Now we can execute our program through the defaul interpreter available in HerbInterpret:

\n\n
interpret(symboltable, myprog_julia)
\n
3
\n\n\n

And that's it!

\n\n\n

Defining a custom interpreter

A disadvantage of the default Julia interpreter is that it needs to traverse abstract syntax tree twice – once to convert it into a Julia expression, and the second time to execute that expression. Program execution is regularly the most consuming part of the entire pipeline and, by eliminating one of these steps, we can cut the runtime in half.

We can define an interpreter that works directly over RuleNodes. Consider the scenario in which we want to write programs for robot navigation: imagine a 2D world in which the robot can move around and pick up a ball. The programs we could write direct the robot to go up, down, left, and right. For convenience, the programming language also offers conditionals and loops:

\n\n
grammar_robots = @csgrammar begin\n    Start = Sequence                   #1\n\n    Sequence = Operation                #2\n    Sequence = (Operation; Sequence)    #3\n    Operation = Transformation          #4\n    Operation = ControlStatement        #5\n\n    Transformation = moveRight() | moveDown() | moveLeft() | moveUp() | drop() | grab()     #6\n    ControlStatement = IF(Condition, Sequence, Sequence)        #12\n    ControlStatement = WHILE(Condition, Sequence)               #13\n\n    Condition = atTop() | atBottom() | atLeft() | atRight() | notAtTop() | notAtBottom() | notAtLeft() | notAtRight()      #14\nend
\n
1: Start = Sequence\n2: Sequence = Operation\n3: Sequence = begin\n    Operation\n    Sequence\nend\n4: Operation = Transformation\n5: Operation = ControlStatement\n6: Transformation = moveRight()\n7: Transformation = moveDown()\n8: Transformation = moveLeft()\n9: Transformation = moveUp()\n10: Transformation = drop()\n11: Transformation = grab()\n12: ControlStatement = IF(Condition, Sequence, Sequence)\n13: ControlStatement = WHILE(Condition, Sequence)\n14: Condition = atTop()\n15: Condition = atBottom()\n16: Condition = atLeft()\n17: Condition = atRight()\n18: Condition = notAtTop()\n19: Condition = notAtBottom()\n20: Condition = notAtLeft()\n21: Condition = notAtRight()\n
\n\n\n

This grammar specifies a simple sequential program with instructions for the robot. A couple of example programs:

  • moveRight(); moveLeft(); drop()

  • WHILE(notAtTop(), moveUp())

The idea behind this programming language is that the program specifies a set of transformations over a state of the robot world. Thus, a program can only be executed over a particular state. In this case, the state represents the size of the 2D world, the current position of a robot, the current position of a ball, and whether the robot is currently holding a ball. The execution of a particular instruction acts as a state transformation: each instruction takes a state as an input, transforms it, and passes it to the subsequent instruction. For example, execution of the program moveRight(); moveLeft(); drop() would proceed as:

  1. take an input state,

  2. pass it to the moveRight() instruction,

  3. pass the output of moveRight() to moveLeft() instructions,

  4. pass the output of moveLeft() to drop(),

  5. return the output of drop().

The following is only one possible way to implement a custom interpreter, but it demonstrates a general template that can always be followed.

We want to implement the following function, which would take in a program in the form of a RuleNode, a grammar, and a starting state, and return the state obtained after executing the program:

    interpret(prog::AbstractRuleNode, grammar::ContextSensitiveGrammar, state::RobotState)::RobotState

As RuleNodes only store indices of derivation rules from the grammar, not the functions themselves, we will first pull the function call associated with every derivation rule. In Julia, this is indicated by the top-level symbol of the rules. For example, the top-level symbol for the derivation rule 6 is :moveRight; for rule 12, that is :IF.

\n\n\n\n\n\n\n\n\n\n\n\n
\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n

The remaining functions follow a similar idea. (You can see the full implementation of this interpreter here).

\n\n","category":"page"},{"location":"tutorials/working_with_interpreters/","page":"Working with custom interpreters","title":"Working with custom interpreters","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/working_with_interpreters.jl\"","category":"page"},{"location":"HerbCore/#HerbCore_docs","page":"HerbCore.jl","title":"HerbCore.jl Documentation","text":"","category":"section"},{"location":"HerbCore/","page":"HerbCore.jl","title":"HerbCore.jl","text":"CurrentModule=HerbCore","category":"page"},{"location":"HerbCore/","page":"HerbCore.jl","title":"HerbCore.jl","text":"Modules = [HerbCore]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbCore/#HerbCore.AbstractConstraint","page":"HerbCore.jl","title":"HerbCore.AbstractConstraint","text":"Represents a constraint for a AbstractGrammar. Concrete implementations can be found in HerbConstraints.jl.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.AbstractGrammar","page":"HerbCore.jl","title":"HerbCore.AbstractGrammar","text":"AbstractGrammar\n\nAbstract type representing all grammars. It is assumed that all grammar structs have at least the following attributes:\n\nrules::Vector{Any}: A list of RHS of rules (subexpressions).\ntypes::Vector{Symbol}: A list of LHS of rules (types, all symbols).\nisterminal::BitVector: A bitvector where bit i represents whether rule i is terminal.\niseval::BitVector: A bitvector where bit i represents whether rule i is an eval rule.\nbytype::Dict{Symbol,Vector{Int}}: A dictionary that maps a type to all rules of said type.\ndomains::Dict{Symbol, BitVector}: A dictionary that maps a type to a domain bitvector. The domain bitvector has bit i set to true iff the ith rule is of this type.\nchildtypes::Vector{Vector{Symbol}}: A list of types of the children for each rule. \n\nIf a rule is terminal, the corresponding list is empty.\n\nlog_probabilities::Union{Vector{Real}, Nothing}: A list of probabilities for each rule. \n\nIf the grammar is non-probabilistic, the list can be nothing.\n\nFor concrete types, see ContextSensitiveGrammar within the HerbGrammar module.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.AbstractHole","page":"HerbCore.jl","title":"HerbCore.AbstractHole","text":"AbstractHole <: AbstractRuleNode\n\nA AbstractHole is a placeholder where certain rules from the grammar can still be applied. The domain of a AbstractHole defines which rules can be applied. The domain is a bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.AbstractRuleNode","page":"HerbCore.jl","title":"HerbCore.AbstractRuleNode","text":"abstract type AbstractRuleNode end\n\nAbstract type for representing expression trees. An AbstractRuleNode is expected to implement the following functions:\n\nisfilled(::AbstractRuleNode)::Bool. True iff the grammar rule this node holds is not ambiguous, i.e. has domain size 1.\nisuniform(::AbstractRuleNode)::Bool. True iff the children of this node are known.\nget_rule(::AbstractRuleNode)::Int. Returns the index of the grammar rule it represents.\nget_children(::AbstractRuleNode)::Vector{AbstractRuleNode}. Returns the children of this node.\n\nExpression trees consist of RuleNodes and AbstractHoles.\n\nA RuleNode represents a certain production rule in the AbstractGrammar.\nA AbstractHole is a placeholder where certain rules in the grammar still can be applied. \n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.AbstractUniformHole","page":"HerbCore.jl","title":"HerbCore.AbstractUniformHole","text":"Hole <: AbstractHole\n\nAn AbstractUniformHole is a placeholder where certain rules from the grammar can still be applied, but all rules in the domain are required to have the same childtypes.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.Hole","page":"HerbCore.jl","title":"HerbCore.Hole","text":"Hole <: AbstractHole\n\ndomain: A bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.HoleReference","page":"HerbCore.jl","title":"HerbCore.HoleReference","text":"HoleReference\n\nContains a hole and the path to the hole from the root of the tree.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.RuleNode","page":"HerbCore.jl","title":"HerbCore.RuleNode","text":"RuleNode <: AbstractRuleNode\n\nA RuleNode represents a node in an expression tree. Each node corresponds to a certain rule in the AbstractGrammar. A RuleNode consists of:\n\nind: The index of the rule in the AbstractGrammar which this node is representing.\n_val: Field for caching evaluations of RuleNodes, preventing multiple unnecessary evaluations. The field can be used to store any needed infromation.\nchildren: The children of this node in the expression tree\n\ncompat: Compat\nEvaluate immediately functionality is not yet supported by most of Herb.jl.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#HerbCore.RuleNode-Tuple{Int64, Any}","page":"HerbCore.jl","title":"HerbCore.RuleNode","text":"RuleNode(ind::Int, _val::Any)\n\nCreate a RuleNode for the AbstractGrammar rule with index ind, _val as immediately evaluated value and no children\n\nwarning: Warning\nOnly use this constructor if you are absolutely certain that a rule is terminal and cannot have children. Use [RuleNode(ind::Int, grammar::AbstractGrammar)] for rules that might have children. In general, AbstractHoles should be used as a placeholder when the children of a node are not yet known. \n\ncompat: Compat\nEvaluate immediately functionality is not yet supported by most of Herb.jl.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.RuleNode-Tuple{Int64, Vector{<:AbstractRuleNode}}","page":"HerbCore.jl","title":"HerbCore.RuleNode","text":"RuleNode(ind::Int, children::Vector{AbstractRuleNode})\n\nCreate a RuleNode for the AbstractGrammar rule with index ind and children as subtrees.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.UniformHole","page":"HerbCore.jl","title":"HerbCore.UniformHole","text":"UniformHole <: AbstractHole\n\ndomain: A bitvector, where the ith bit is set to true if the ith rule in the grammar can be applied. All rules in the domain are required to have the same childtypes.\nchildren: The children of this hole in the expression tree.\n\n\n\n\n\n","category":"type"},{"location":"HerbCore/#Base.isless-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbCore.jl","title":"Base.isless","text":"Base.isless(rn₁::AbstractRuleNode, rn₂::AbstractRuleNode)::Bool\n\nCompares two RuleNodes. Returns true if the left RuleNode is less than the right RuleNode. Order is determined from the index of the RuleNodes. If both RuleNodes have the same index, a depth-first search is performed in both RuleNodes until nodes with a different index are found.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#Base.length-Tuple{AbstractRuleNode}","page":"HerbCore.jl","title":"Base.length","text":"Base.length(root::RuleNode)\n\nReturn the number of nodes in the tree rooted at root.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.contains_hole-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.contains_hole","text":"contains_hole(rn::RuleNode) = any(contains_hole(c) for c ∈ rn.children)\n\nChecks if an AbstractRuleNode tree contains a AbstractHole.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.contains_nonuniform_hole-Tuple{AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.contains_nonuniform_hole","text":"contains_nonuniform_hole(rn::RuleNode)\n\nChecks if an AbstractRuleNode tree contains a Hole.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.depth-Tuple{AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.depth","text":"depth(root::RuleNode)::Int\n\nReturn the depth of the AbstractRuleNode tree rooted at root. Holes do count towards the depth.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_children-Tuple{AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.get_children","text":"get_children(rn::AbstractRuleNode)\n\nReturns the children of the given AbstractRuleNode\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_node_at_location-Tuple{AbstractRuleNode, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.get_node_at_location","text":"get_node_at_location(root::AbstractRuleNode, location::Vector{Int})\n\nRetrieves a RuleNode at the given location by reference.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_node_at_location-Tuple{Hole, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.get_node_at_location","text":"get_node_at_location(root::Hole, location::Vector{Int})\n\nRetrieves the current hole, if location is this very hole. Throws error otherwise.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_path-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.get_path","text":"get_path(root::AbstractRuleNode, node::AbstractRuleNode)\n\nReturns the path from the root to the targetnode. Returns nothing if no path exists.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_rule-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.get_rule","text":"get_rule(rn::AbstractRuleNode)\n\nReturns the index of the rule that this AbstractRuleNode represents\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.get_rulesequence-Tuple{RuleNode, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.get_rulesequence","text":"get_rulesequence(node::RuleNode, path::Vector{Int})\n\nExtract the derivation sequence from a path (sequence of child indices) and an AbstractRuleNode. If the path is deeper than the deepest node, it returns what it has.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.hasdynamicvalue-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.hasdynamicvalue","text":"function hasdynamicvalue(rn::AbstractRuleNode)::Bool\n\nReturns true iff the rule has a _val field set up.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.have_same_shape-Tuple{Any, Any}","page":"HerbCore.jl","title":"HerbCore.have_same_shape","text":"have_same_shape(node1::AbstractRuleNode, node2::AbstractRuleNode)\n\nReturns true iff node1 and node2 have the same shape Example: RuleNode(3, [ \tRuleNode(1), \tRuleNode(1) ]) and RuleNode(9, [ \tRuleNode(2), \tHole(domain) ]) have the same shape: 1 root with 2 children.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.isfilled-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.isfilled","text":"isfilled(node::AbstractRuleNode)::Bool\n\nReturns whether the [AbstractRuleNode] holds a single rule. This is always the case for RuleNodes. Holes are considered to be \"filled\" iff their domain size is exactly 1.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.isuniform-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.isuniform","text":"isuniform(rn::AbstractRuleNode)\n\nReturns true iff the children of the AbstractRuleNode are known.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.node_depth-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbCore.jl","title":"HerbCore.node_depth","text":"node_depth(root::AbstractRuleNode, node::AbstractRuleNode)::Int\n\nReturn the depth of node for an AbstractRuleNode tree rooted at root. Depth is 1 when root == node.\n\nwarning: Warning\nnode must be a subtree of root in order for this function to work.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.number_of_holes-Tuple{RuleNode}","page":"HerbCore.jl","title":"HerbCore.number_of_holes","text":"number_of_holes(rn::AbstractRuleNode)::Int\n\nRecursively counts the number of holes in an AbstractRuleNode\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.rulesoftype-Tuple{RuleNode, Set{Int64}}","page":"HerbCore.jl","title":"HerbCore.rulesoftype","text":"rulesoftype(node::RuleNode, ruleset::Set{Int})\n\nReturns every rule in the ruleset that is also used in the AbstractRuleNode tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.rulesonleft-Tuple{RuleNode, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.rulesonleft","text":"rulesonleft(expr::RuleNode, path::Vector{Int})::Set{Int}\n\nFinds all rules that are used in the left subtree defined by the path.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.swap_node-Tuple{AbstractRuleNode, AbstractRuleNode, Vector{Int64}}","page":"HerbCore.jl","title":"HerbCore.swap_node","text":"swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int})\n\nReplace a node in expr, specified by path, with new_expr. Path is a sequence of child indices, starting from the root node.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#HerbCore.swap_node-Tuple{RuleNode, RuleNode, Int64, RuleNode}","page":"HerbCore.jl","title":"HerbCore.swap_node","text":"swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode)\n\nReplace child i of a node, a part of larger expr, with new_expr.\n\n\n\n\n\n","category":"method"},{"location":"HerbCore/#Index","page":"HerbCore.jl","title":"Index","text":"","category":"section"},{"location":"HerbCore/","page":"HerbCore.jl","title":"HerbCore.jl","text":"","category":"page"},{"location":"HerbGrammar/#HerbGrammar_docs","page":"HerbGrammar.jl","title":"HerbGrammar.jl Documentation","text":"","category":"section"},{"location":"HerbGrammar/","page":"HerbGrammar.jl","title":"HerbGrammar.jl","text":"CurrentModule=HerbGrammar","category":"page"},{"location":"HerbGrammar/","page":"HerbGrammar.jl","title":"HerbGrammar.jl","text":"Modules = [HerbGrammar]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbGrammar/#HerbGrammar.ContextSensitiveGrammar","page":"HerbGrammar.jl","title":"HerbGrammar.ContextSensitiveGrammar","text":"ContextSensitiveGrammar <: AbstractGrammar\n\nRepresents a context-sensitive grammar. Extends AbstractGrammar with constraints.\n\nConsists of:\n\nrules::Vector{Any}: A list of RHS of rules (subexpressions).\ntypes::Vector{Symbol}: A list of LHS of rules (types, all symbols).\nisterminal::BitVector: A bitvector where bit i represents whether rule i is terminal.\niseval::BitVector: A bitvector where bit i represents whether rule i is an eval rule.\nbytype::Dict{Symbol,Vector{Int}}: A dictionary that maps a type to all rules of said type.\ndomains::Dict{Symbol, BitVector}: A dictionary that maps a type to a domain bitvector. The domain bitvector has bit i set to true iff the ith rule is of this type.\nchildtypes::Vector{Vector{Symbol}}: A list of types of the children for each rule. If a rule is terminal, the corresponding list is empty.\nbychildtypes::Vector{BitVector}: A bitvector of rules that share the same childtypes for each rule\nlog_probabilities::Union{Vector{Real}, Nothing}: A list of probabilities for each rule. If the grammar is non-probabilistic, the list can be nothing.\nconstraints::Vector{AbstractConstraint}: A list of constraints that programs in this grammar have to abide.\n\nUse the @csgrammar macro to create a ContextSensitiveGrammar object. Use the @pcsgrammar macro to create a ContextSensitiveGrammar object with probabilities.\n\n\n\n\n\n","category":"type"},{"location":"HerbGrammar/#HerbGrammar.NodeLoc","page":"HerbGrammar.jl","title":"HerbGrammar.NodeLoc","text":"NodeLoc A helper struct that points to a node in the tree via its parent such that the child can be easily swapped out. If i is 0 the node pointed to is the root node and parent is the node itself.\n\n\n\n\n\n","category":"type"},{"location":"HerbGrammar/#HerbGrammar.SymbolTable","page":"HerbGrammar.jl","title":"HerbGrammar.SymbolTable","text":"SymbolTable(grammar::AbstractGrammar, mod::Module=Main)\n\nReturns a SymbolTable populated with a mapping from symbols in the AbstractGrammar to symbols in module mod or Main, if defined.\n\n\n\n\n\n","category":"type"},{"location":"HerbGrammar/#HerbGrammar.SymbolTable-2","page":"HerbGrammar.jl","title":"HerbGrammar.SymbolTable","text":"SymbolTable\n\nData structure for mapping terminal symbols in the AbstractGrammar to their Julia interpretation.\n\n\n\n\n\n","category":"type"},{"location":"HerbGrammar/#HerbGrammar.@cfgrammar-Tuple{Any}","page":"HerbGrammar.jl","title":"HerbGrammar.@cfgrammar","text":"@cfgrammar\n\nThis macro is deprecated and will be removed in future versions. Use @csgrammar instead.\n\n\n\n\n\n","category":"macro"},{"location":"HerbGrammar/#HerbGrammar.@csgrammar-Tuple{Any}","page":"HerbGrammar.jl","title":"HerbGrammar.@csgrammar","text":"@csgrammar\n\nA macro for defining a ContextSensitiveGrammar. AbstractConstraints can be added afterwards using the addconstraint! function.\n\nExample usage:\n\ngrammar = @csgrammar begin\n\tR = x\n\tR = 1 | 2\n\tR = R + R\nend\n\nSyntax:\n\nLiterals: Symbols that are already defined in Julia are considered literals, such as 1, 2, or π. For example: R = 1.\nVariables: A variable is a symbol that is not a nonterminal symbol and not already defined in Julia. For example: R = x.\nFunctions: Functions and infix operators that are defined in Julia or the Main module can be used with the default evaluator. For example: R = R + R, R = f(a, b).\nCombinations: Multiple rules can be defined on a single line in the grammar definition using the | symbol. For example: R = 1 | 2 | 3.\nIterators: Another way to define multiple rules is by providing a Julia iterator after a | symbol. For example: R = |(1:9).\n\nRelated:\n\n@pcsgrammar uses a similar syntax to create probabilistic ContextSensitiveGrammars.\n\n\n\n\n\n","category":"macro"},{"location":"HerbGrammar/#HerbGrammar.@pcsgrammar-Tuple{Any}","page":"HerbGrammar.jl","title":"HerbGrammar.@pcsgrammar","text":"@pcsgrammar\n\nA macro for defining a probabilistic ContextSensitiveGrammar. \n\nExample usage:\n\ngrammar = @pcsgrammar begin\n\t0.5 : R = x\n\t0.3 : R = 1 | 2\n\t0.2 : R = R + R\nend\n\nSyntax:\n\nThe syntax of rules is identical to the syntax used by @csgrammar:\n\nLiterals: Symbols that are already defined in Julia are considered literals, such as 1, 2, or π. For example: R = 1.\nVariables: A variable is a symbol that is not a nonterminal symbol and not already defined in Julia. For example: R = x.\nFunctions: Functions and infix operators that are defined in Julia or the Main module can be used with the default evaluator. For example: R = R + R, R = f(a, b).\nCombinations: Multiple rules can be defined on a single line in the grammar definition using the | symbol. For example: R = 1 | 2 | 3.\nIterators: Another way to define multiple rules is by providing a Julia iterator after a | symbol. For example: R = |(1:9).\n\nEvery rule is also prefixed with a probability. Rules and probabilities are separated using the : symbol. If multiple rules are defined on a single line, the probability is equally divided between the rules. The sum of probabilities for all rules of a certain non-terminal symbol should be equal to 1. The probabilities are automatically scaled if this isn't the case.\n\nRelated:\n\n@csgrammar uses a similar syntax to create non-probabilistic ContextSensitiveGrammars.\n\n\n\n\n\n","category":"macro"},{"location":"HerbGrammar/#Base.get-Tuple{AbstractRuleNode, NodeLoc}","page":"HerbGrammar.jl","title":"Base.get","text":"get(root::AbstractRuleNode, loc::NodeLoc) Obtain the node pointed to by loc.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#Base.insert!-Tuple{RuleNode, NodeLoc, RuleNode}","page":"HerbGrammar.jl","title":"Base.insert!","text":"insert!(loc::NodeLoc, rulenode::RuleNode) Replaces the subtree pointed to by loc with the given rulenode.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.add_rule!-Tuple{AbstractGrammar, Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.add_rule!","text":"add_rule!(g::AbstractGrammar, e::Expr)\n\nAdds a rule to the grammar. \n\nUsage:\n\n add_rule!(grammar, :(\"Real = Real + Real\"))\n\nThe syntax is identical to the syntax of @csgrammar and @cfgrammar, but only single rules are supported.\n\nwarning: Warning\nCalls to this function are ignored if a rule is already in the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.add_rule!-Tuple{AbstractGrammar, Real, Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.add_rule!","text":"Adds a probabilistic derivation rule.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.addconstraint!-Tuple{ContextSensitiveGrammar, AbstractConstraint}","page":"HerbGrammar.jl","title":"HerbGrammar.addconstraint!","text":"addconstraint!(grammar::ContextSensitiveGrammar, c::AbstractConstraint)\n\nAdds a AbstractConstraint to a ContextSensitiveGrammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.child_types-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.child_types","text":"child_types(grammar::AbstractGrammar, rule_index::Int)\n\nReturns the types of the children (nonterminals) of the production rule at rule_index.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.child_types-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.child_types","text":"child_types(grammar::AbstractGrammar, node::RuleNode)\n\nReturns the list of child types (nonterminal symbols) in the production rule used by node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.cleanup_removed_rules!-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.cleanup_removed_rules!","text":"cleanup_removed_rules!(g::AbstractGrammar)\n\nRemoves any placeholders for previously deleted rules. This means that indices get shifted.\n\nwarning: Warning\nWhen indices are shifted, this grammar can no longer be used to interpret AbstractRuleNode trees created before the call to this function. These trees become meaningless. \n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.clearconstraints!-Tuple{ContextSensitiveGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.clearconstraints!","text":"Clear all constraints from the grammar\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.containedin-Tuple{Vector, Vector}","page":"HerbGrammar.jl","title":"HerbGrammar.containedin","text":"containedin(vec1::Vector, vec2::Vector)\n\nChecks if elements of vec1 are contained in vec2 in the same order (possibly with elements in between)\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.contains_returntype","page":"HerbGrammar.jl","title":"HerbGrammar.contains_returntype","text":"contains_returntype(node::RuleNode, grammar::AbstractGrammar, sym::Symbol, maxdepth::Int=typemax(Int))\n\nReturns true if the tree rooted at node contains at least one node at depth less than maxdepth with the given return type or nonterminal symbol.\n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.expr2csgrammar-Tuple{Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.expr2csgrammar","text":"expr2csgrammar(ex::Expr)::ContextSensitiveGrammar\n\nA function for converting an Expr to a ContextSensitiveGrammar. If the expression is hardcoded, you should use the @csgrammar macro. Only expressions in the correct format (see @csgrammar) can be converted.\n\nExample usage:\n\ngrammar = expr2csgrammar(\n\tbegin\n\t\tR = x\n\t\tR = 1 | 2\n\t\tR = R + R\n\tend\n)\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.expr2pcsgrammar-Tuple{Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.expr2pcsgrammar","text":"Function for converting an Expr to a ContextSensitiveGrammar with probabilities. If the expression is hardcoded, you should use the @pcsgrammar macro. Only expressions in the correct format (see @pcsgrammar) can be converted.\n\nExample usage:\n\ngrammar = expr2pcsgrammar(\n\tbegin\n\t\t0.5 : R = x\n\t\t0.3 : R = 1 | 2\n\t\t0.2 : R = R + R\n\tend\n)\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.get_childtypes-Tuple{Any, AbstractVector{Symbol}}","page":"HerbGrammar.jl","title":"HerbGrammar.get_childtypes","text":"get_childtypes(rule::Any, types::AbstractVector{Symbol})\n\nReturns the child types/nonterminals of a production rule.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.get_domain-Tuple{AbstractGrammar, Symbol}","page":"HerbGrammar.jl","title":"HerbGrammar.get_domain","text":"get_domain(g::AbstractGrammar, type::Symbol)::BitVector\n\nReturns the domain for the hole of a certain type as a BitVector of the same length as the number of rules in the grammar. Bit i is set to true iff rule i is of type type.\n\ninfo: Info\nSince this function can be intensively used when exploring a program space defined by a grammar, the outcomes of this function are precomputed and stored in the domains field in a AbstractGrammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.get_domain-Tuple{AbstractGrammar, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.get_domain","text":"get_domain(g::AbstractGrammar, rules::Vector{Int})::BitVector\n\nTakes a domain rules defined as a vector of ints and converts it to a domain defined as a BitVector.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.get_rulesequence-Tuple{RuleNode, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.get_rulesequence","text":"get_rulesequence(node::RuleNode, path::Vector{Int})\n\nExtract the derivation sequence from a path (sequence of child indices) and an AbstractRuleNode. If the path is deeper than the deepest node, it returns what it has.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.iscomplete-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.iscomplete","text":"iscomplete(grammar::AbstractGrammar, node::RuleNode)\n\nReturns true if the expression represented by the RuleNode is a complete expression, meaning that it is fully defined and doesn't have any Holes.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.iseval-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.iseval","text":"iseval(grammar::AbstractGrammar, index::Int)::Bool\n\nReturns true if the production rule at rule_index contains the special _() eval function.\n\ncompat: Compat\nevaluate immediately functionality is not yet supported by most of Herb.jl\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.iseval-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.iseval","text":"iseval(grammar::AbstractGrammar)::Bool\n\nReturns true if any production rules in grammar contain the special _() eval function.\n\ncompat: Compat\nevaluate immediately functionality is not yet supported by most of Herb.jl\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.iseval-Tuple{Any}","page":"HerbGrammar.jl","title":"HerbGrammar.iseval","text":"iseval(rule)\n\nReturns true if the rule is the special evaluate immediately function, i.e., _()\n\ncompat: Compat\nevaluate immediately functionality is not yet supported by most of Herb.jl\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isprobabilistic-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.isprobabilistic","text":"isprobabilistic(grammar::AbstractGrammar)::Bool\n\nFunction returns whether a AbstractGrammar is probabilistic.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isterminal-Tuple{AbstractGrammar, AbstractRuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.isterminal","text":"isterminal(grammar::AbstractGrammar, node::AbstractRuleNode)::Bool\n\nReturns true if the production rule used by node is terminal, i.e., does not contain any nonterminal symbols.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isterminal-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.isterminal","text":"isterminal(grammar::AbstractGrammar, rule_index::Int)::Bool\n\nReturns true if the production rule at rule_index is terminal, i.e., does not contain any nonterminal symbols.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isterminal-Tuple{Any, AbstractVector{Symbol}}","page":"HerbGrammar.jl","title":"HerbGrammar.isterminal","text":"isterminal(rule::Any, types::AbstractVector{Symbol})\n\nReturns true if the rule is terminal, i.e., it does not contain any of the types in the provided vector. For example, :(x) is terminal, and :(1+1) is terminal, but :(Real + Real) is typically not.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isvariable-Tuple{AbstractGrammar, Int64, Vararg{Module}}","page":"HerbGrammar.jl","title":"HerbGrammar.isvariable","text":"isvariable(grammar::AbstractGrammar, ind::Int, mod::Module)::Bool\n\nReturn true if the rule with index ind represents a variable.\n\nTaking into account the symbols defined in the given module(s).\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isvariable-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.isvariable","text":"isvariable(grammar::AbstractGrammar, ind::Int)::Bool\n\nReturn true if the rule with index ind represents a variable.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isvariable-Tuple{AbstractGrammar, RuleNode, Vararg{Module}}","page":"HerbGrammar.jl","title":"HerbGrammar.isvariable","text":"isvariable(grammar::AbstractGrammar, node::RuleNode, mod::Module)::Bool\n\nReturn true if the rule used by node represents a variable.\n\nTaking into account the symbols defined in the given module(s).\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.isvariable-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.isvariable","text":"isvariable(grammar::AbstractGrammar, node::RuleNode)::Bool\n\nReturn true if the rule used by node represents a variable in a program (essentially, an input to the program)\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.log_probability-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.log_probability","text":"log_probability(grammar::AbstractGrammar, index::Int)::Real\n\nReturns the log probability for the rule at index in the grammar.\n\nwarning: Warning\nIf the grammar is not probabilistic, a warning is displayed, and a uniform probability is assumed.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.max_arity-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.max_arity","text":"max_arity(grammar::AbstractGrammar)::Int\n\nReturns the maximum arity (number of children) over all production rules in the AbstractGrammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.merge_grammars!-Tuple{AbstractGrammar, AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.merge_grammars!","text":"merge_grammars!(merge_to::AbstractGrammar, merge_from::AbstractGrammar)\n\nAdds all rules and constraints from merge_from to merge_to.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.mindepth-Tuple{AbstractGrammar, Symbol, AbstractVector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.mindepth","text":"mindepth(grammar::AbstractGrammar, typ::Symbol, dmap::AbstractVector{Int})\n\nReturns the minimum depth achievable for a given nonterminal symbol. The minimum depth is the depth of the lowest tree that can be made using typ as a start symbol. dmap can be obtained from mindepth_map.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.mindepth_map-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.mindepth_map","text":"mindepth_map(grammar::AbstractGrammar)\n\nReturns the minimum depth achievable for each production rule in the AbstractGrammar. In other words, this function finds the depths of the lowest trees that can be made using each of the available production rules as a root.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.nchildren-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.nchildren","text":"nchildren(grammar::AbstractGrammar, rule_index::Int)::Int\n\nReturns the number of children (nonterminals) of the production rule at rule_index.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.nchildren-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.nchildren","text":"nchildren(grammar::AbstractGrammar, node::RuleNode)::Int\n\nReturns the number of children in the production rule used by node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.nonterminals-Tuple{AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.nonterminals","text":"nonterminals(grammar::AbstractGrammar)::Vector{Symbol}\n\nReturns a list of the nonterminals or types in the AbstractGrammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.normalize!","page":"HerbGrammar.jl","title":"HerbGrammar.normalize!","text":"A function for normalizing the probabilities of a probabilistic ContextSensitiveGrammar. If the optional type argument is provided, only the rules of that type are normalized.\n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.parse_probabilistic_rule-Tuple{Expr}","page":"HerbGrammar.jl","title":"HerbGrammar.parse_probabilistic_rule","text":"Parses a single (potentially shorthand) derivation rule of a probabilistic ContextSensitiveGrammar. Returns nothing if the rule is not probabilistic, otherwise a Tuple of its type and a Vector of probability-rule pairs it expands into.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.probability-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.probability","text":"probability(grammar::AbstractGrammar, index::Int)::Real\n\nReturn the probability for a rule in the grammar. Use log_probability whenever possible.\n\nwarning: Warning\nIf the grammar is not probabilistic, a warning is displayed, and a uniform probability is assumed.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.read_csg","page":"HerbGrammar.jl","title":"HerbGrammar.read_csg","text":"read_csg(grammarpath::AbstractString, constraintspath::OptionalPath=nothing)::ContextSensitiveGrammar\n\nReads a ContextSensitiveGrammar from the files at grammarpath and constraintspath.\n\ndanger: Danger\nOnly open trusted grammars. Parts of the grammar can be passed to Julia's eval function. \n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.read_pcsg","page":"HerbGrammar.jl","title":"HerbGrammar.read_pcsg","text":"read_pcsg(grammarpath::AbstractString, constraintspath::OptionalPath=nothing)::ContextSensitiveGrammar\n\nReads a probabilistic ContextSensitiveGrammar from the files at grammarpath and constraintspath.\n\ndanger: Danger\nOnly open trusted grammars. Parts of the grammar can be passed to Julia's eval function. \n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.remove_rule!-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.remove_rule!","text":"remove_rule!(g::AbstractGrammar, idx::Int)\n\nRemoves the rule corresponding to idx from the grammar. In order to avoid shifting indices, the rule is replaced with nothing, and all other data structures are updated accordingly.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.return_type-Tuple{AbstractGrammar, Int64}","page":"HerbGrammar.jl","title":"HerbGrammar.return_type","text":"return_type(grammar::AbstractGrammar, rule_index::Int)::Symbol\n\nReturns the type of the production rule at rule_index.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.return_type-Tuple{AbstractGrammar, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.return_type","text":"return_type(grammar::AbstractGrammar, node::RuleNode)\n\nGives the return type or nonterminal symbol in the production rule used by node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.return_type-Tuple{AbstractGrammar, UniformHole}","page":"HerbGrammar.jl","title":"HerbGrammar.return_type","text":"return_type(grammar::AbstractGrammar, hole::UniformHole)\n\nGives the return type or nonterminal symbol in the production rule used by hole.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.root_node_loc-Tuple{RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.root_node_loc","text":"rootnodeloc(root::RuleNode) Returns a NodeLoc pointing to the root node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulenode2expr-Tuple{AbstractRuleNode, AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.rulenode2expr","text":"rulenode2expr(rulenode::AbstractRuleNode, grammar::AbstractGrammar)\n\nConverts an AbstractRuleNode into a Julia expression corresponding to the rule definitions in the grammar. The returned expression can be evaluated with Julia semantics using eval().\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulenode_log_probability-Tuple{RuleNode, AbstractGrammar}","page":"HerbGrammar.jl","title":"HerbGrammar.rulenode_log_probability","text":"Calculates the log probability associated with a rulenode in a probabilistic grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulesoftype-Tuple{RuleNode, AbstractGrammar, Symbol, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.rulesoftype","text":"rulesoftype(node::RuleNode, grammar::AbstractGrammar, ruletype::Symbol, ignoreNode::RuleNode)\n\nReturns every rule of nonterminal symbol ruletype that is also used in the AbstractRuleNode tree, but not in the ignoreNode subtree.\n\nwarning: Warning\nThe ignoreNode must be a subtree of node for it to have an effect.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulesoftype-Tuple{RuleNode, AbstractGrammar, Symbol}","page":"HerbGrammar.jl","title":"HerbGrammar.rulesoftype","text":"rulesoftype(node::RuleNode, grammar::AbstractGrammar, ruletype::Symbol)\n\nReturns every rule of nonterminal symbol ruletype that is also used in the AbstractRuleNode tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulesoftype-Tuple{RuleNode, Set{Int64}, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.rulesoftype","text":"rulesoftype(node::RuleNode, ruleset::Set{Int}, ignoreNode::RuleNode)\n\nReturns every rule in the ruleset that is also used in the AbstractRuleNode tree, but not in the ignoreNode subtree.\n\nwarning: Warning\nThe ignoreNode must be a subtree of node for it to have an effect.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.rulesonleft-Tuple{RuleNode, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.rulesonleft","text":"rulesonleft(node::RuleNode, path::Vector{Int})::Set{Int}\n\nFinds all rules that are used in the left subtree defined by the path.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.store_csg","page":"HerbGrammar.jl","title":"HerbGrammar.store_csg","text":"store_csg(g::ContextSensitiveGrammar, grammarpath::AbstractString, constraintspath::OptionalPath=nothing)\n\nWrites a ContextSensitiveGrammar to the files at grammarpath and constraintspath. The grammarpath file will contain a ContextSensitiveGrammar definition, and the constraintspath file will contain the AbstractConstraints of the ContextSensitiveGrammar.\n\n\n\n\n\n","category":"function"},{"location":"HerbGrammar/#HerbGrammar.subsequenceof-Tuple{Vector{Int64}, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.subsequenceof","text":"subsequenceof(vec1::Vector{Int}, vec2::Vector{Int})\n\nChecks if vec1 is a subsequence of vec2.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.swap_node-Tuple{AbstractRuleNode, AbstractRuleNode, Vector{Int64}}","page":"HerbGrammar.jl","title":"HerbGrammar.swap_node","text":"swap_node(expr::AbstractRuleNode, new_expr::AbstractRuleNode, path::Vector{Int})\n\nReplace a node in expr, specified by path, with new_expr. Path is a sequence of child indices, starting from the root node.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#HerbGrammar.swap_node-Tuple{RuleNode, RuleNode, Int64, RuleNode}","page":"HerbGrammar.jl","title":"HerbGrammar.swap_node","text":"swap_node(expr::RuleNode, node::RuleNode, child_index::Int, new_expr::RuleNode)\n\nReplace child i of a node, a part of larger expr, with new_expr.\n\n\n\n\n\n","category":"method"},{"location":"HerbGrammar/#Index","page":"HerbGrammar.jl","title":"Index","text":"","category":"section"},{"location":"HerbGrammar/","page":"HerbGrammar.jl","title":"HerbGrammar.jl","text":"","category":"page"},{"location":"HerbSpecification/#HerbSpecification_docs","page":"HerbSpecification.jl","title":"HerbSpecification.jl Documentation","text":"","category":"section"},{"location":"HerbSpecification/","page":"HerbSpecification.jl","title":"HerbSpecification.jl","text":"CurrentModule=HerbSpecification","category":"page"},{"location":"HerbSpecification/","page":"HerbSpecification.jl","title":"HerbSpecification.jl","text":"Modules = [HerbSpecification]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbSpecification/#HerbSpecification.AbstractDependentTypeSpecification","page":"HerbSpecification.jl","title":"HerbSpecification.AbstractDependentTypeSpecification","text":"struct AbstractDependentTypeSpecification <: AbstractTypeSpecification\n\nDefines a specification through dependent types. Needs a concrete type checker as oracle.\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.AgdaSpecification","page":"HerbSpecification.jl","title":"HerbSpecification.AgdaSpecification","text":"struct AgdaSpecification <: AbstractDependentTypeSpecification\n\nDefines a specification \n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.IOExample","page":"HerbSpecification.jl","title":"HerbSpecification.IOExample","text":"struct IOExample\n\nAn input-output example. in is a Dict of {Symbol,Any} where the symbol represents a variable in a program. out can be anything.\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.MetricProblem","page":"HerbSpecification.jl","title":"HerbSpecification.MetricProblem","text":"struct MetricProblem{T <: Vector{IOExample}}\n\nProgram synthesis problem defined by an specification and a metric. The specification has to be based on input/output examples, while the function needs to return a numerical value.\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.Problem","page":"HerbSpecification.jl","title":"HerbSpecification.Problem","text":"struct Problem\n\nProgram synthesis problem defined by an AbstractSpecifications. Has a name and a specification of type T.\n\nwarning: Warning\nPlease care that concrete Problem types with different values of T are never subtypes of each other. \n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.SMTSpecification","page":"HerbSpecification.jl","title":"HerbSpecification.SMTSpecification","text":"struct SMTSpecification <: AbstractFormalSpecification\n\nA specification based on a logical formula defined by a SMT solver.\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#HerbSpecification.Trace","page":"HerbSpecification.jl","title":"HerbSpecification.Trace","text":"struct Trace\n\nA trace defining a wanted program execution for program synthesis. @TODO combine with Gen.jl\n\n\n\n\n\n","category":"type"},{"location":"HerbSpecification/#Base.getindex-Tuple{Problem{Vector{IOExample}}, Any}","page":"HerbSpecification.jl","title":"Base.getindex","text":"Base.getindex(p::Problem{Vector{IOExample}}, indices)\n\nOverwrite Base.getindex to allow for slicing of input/output-based problems.\n\n\n\n\n\n","category":"method"},{"location":"HerbSpecification/#Index","page":"HerbSpecification.jl","title":"Index","text":"","category":"section"},{"location":"HerbSpecification/","page":"HerbSpecification.jl","title":"HerbSpecification.jl","text":"","category":"page"},{"location":"HerbConstraints/#HerbConstraints_docs","page":"HerbConstraints.jl","title":"HerbConstraints.jl Documentation","text":"","category":"section"},{"location":"HerbConstraints/","page":"HerbConstraints.jl","title":"HerbConstraints.jl","text":"CurrentModule=HerbConstraints","category":"page"},{"location":"HerbConstraints/","page":"HerbConstraints.jl","title":"HerbConstraints.jl","text":"Modules = [HerbConstraints]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbConstraints/#HerbConstraints.AbstractGrammarConstraint","page":"HerbConstraints.jl","title":"HerbConstraints.AbstractGrammarConstraint","text":"abstract type AbstractGrammarConstraint <: AbstractConstraint\n\nAbstract type representing all user-defined constraints. Each grammar constraint has a related AbstractLocalConstraint that is responsible for propagating the constraint at a specific location in the tree. Grammar constraints should implement on_new_node to post a AbstractLocalConstraint at that new node\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.AbstractLocalConstraint","page":"HerbConstraints.jl","title":"HerbConstraints.AbstractLocalConstraint","text":"abstract type AbstractLocalConstraint <: AbstractConstraint\n\nAbstract type representing all local constraints. Each local constraint contains a path that points to a specific location in the tree at which the constraint applies.\n\nEach local constraint should implement a propagate!-function. Inside the propagate! function, the constraint can use the following solver functions:\n\nremove!: Elementary tree manipulation. Removes a value from a domain. (other tree manipulations are: remove_above!, remove_below!, remove_all_but!)\ndeactivate!: Prevent repropagation. Call this as soon as the constraint is satisfied.\nset_infeasible!: Report a non-trivial inconsistency. Call this if the constraint can never be satisfied. An empty domain is considered a trivial inconsistency, such inconsistencies are already handled by tree manipulations.\nisfeasible: Check if the current tree is still feasible. Return from the propagate function, as soon as infeasibility is detected.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.AbstractStateManager","page":"HerbConstraints.jl","title":"HerbConstraints.AbstractStateManager","text":"Manages all changes made to StateInts using StateIntBackups. Support the following functions:\n\nStateInt Creates a new stateful integer\nsave_state! Creates a checkpoint for all stateful integers\nrestore! Restores the values to the latest checkpoint\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.Contains","page":"HerbConstraints.jl","title":"HerbConstraints.Contains","text":"Contains <: AbstractGrammarConstraint This [AbstractGrammarConstraint] enforces that a given rule appears in the program tree at least once.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.ContainsSubtree","page":"HerbConstraints.jl","title":"HerbConstraints.ContainsSubtree","text":"ContainsSubtree <: AbstractGrammarConstraint\n\nThis [AbstractGrammarConstraint] enforces that a given subtree appears in the program tree at least once.\n\n!!! warning: This constraint can only be propagated by the UniformSolver\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.DomainRuleNode","page":"HerbConstraints.jl","title":"HerbConstraints.DomainRuleNode","text":"struct DomainRuleNode <: AbstractRuleNode\n\nMatches any 1 rule in its domain. Example usage:\n\nDomainRuleNode(Bitvector((0, 0, 1, 1)), [RuleNode(1), RuleNode(1)])\n\nThis matches RuleNode(3, [RuleNode(1), RuleNode(1)]) and RuleNode(4, [RuleNode(1), RuleNode(1)]) and UniformHole({3, 4}, [RuleNode(1), RuleNode(1)])\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.Forbidden","page":"HerbConstraints.jl","title":"HerbConstraints.Forbidden","text":"Forbidden <: AbstractGrammarConstraint\n\nThis [AbstractGrammarConstraint] forbids any subtree that matches the pattern given by tree to be generated. A pattern is a tree of AbstractRuleNodes. Such a node can either be a RuleNode, which contains a rule index corresponding to the rule index in the AbstractGrammar and the appropriate number of children, similar to RuleNodes. It can also contain a VarNode, which contains a single identifier symbol. A VarNode can match any subtree, but if there are multiple instances of the same variable in the pattern, the matched subtrees must be identical. Any rule in the domain that makes the match attempt successful is removed.\n\nFor example, consider the tree 1(a, 2(b, 3(c, 4)))):\n\nForbidden(RuleNode(3, [RuleNode(5), RuleNode(4)])) forbids c to be filled with 5.\nForbidden(RuleNode(3, [VarNode(:v), RuleNode(4)])) forbids c to be filled, since a [VarNode] can match any rule, thus making the match attempt successful for the entire domain of c. Therefore, this tree invalid.\nForbidden(RuleNode(3, [VarNode(:v), VarNode(:v)])) forbids c to be filled with 4, since that would make both assignments to v equal, which causes a successful match.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.ForbiddenSequence","page":"HerbConstraints.jl","title":"HerbConstraints.ForbiddenSequence","text":"ForbiddenPath <: AbstractGrammarConstraint\n\nThis [AbstractGrammarConstraint] forbids the given sequence of rule nodes. Sequences are strictly vertical and may include gaps. Consider the tree 1(a, 2(b, 3(c, d)))):\n\n[2, 3, d] is a sequence\n[1, 3, d] is a sequence\n[3, c, d] is not a sequence\n\nExamples:\n\nForbiddenSequence([3, 4]) enforces that rule 4 cannot be applied at c or d.\nForbiddenSequence([1, 2, 4]) enforces that rule 4 cannot be applied at b, c or d.\nForbiddenSequence([1, 4]) enforces that rule 4 cannot be applied anywhere.\n\nIf any of the rules in ignore_if appears in the sequence, the constraint is ignored. Suppose the forbidden sequence = [1, 2, 3] and ignore_if = [99] Consider the following paths from the root:\n\n[1, 2, 2, 3] is forbidden, as the sequence does not contain 99\n[1, 99, 2, 3] is NOT forbidden, as the sequence does contain 99\n[1, 99, 1, 2, 3] is forbidden, as there is a subsequence that does not contain 99\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.GenericSolver","page":"HerbConstraints.jl","title":"HerbConstraints.GenericSolver","text":"GenericSolver\n\nMaintains a feasible partial program in a SolverState. A ProgramIterator may manipulate the partial tree with the following tree manipulations:\n\nsubstitute!\nremove!\nremove_below!\nremove_above!\nremove_all_but!\n\nEach SolverState holds an independent propagation program. Program iterators can freely move back and forth between states using:\n\nnew_state!\nsave_state!\nload_state!\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.GenericSolver-Tuple{AbstractGrammar, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.GenericSolver","text":"GenericSolver(grammar::AbstractGrammar, init_node::AbstractRuleNode)\n\nConstructs a new solver, with an initial state of the provided AbstractRuleNode.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.GenericSolver-Tuple{AbstractGrammar, Symbol}","page":"HerbConstraints.jl","title":"HerbConstraints.GenericSolver","text":"GenericSolver(grammar::AbstractGrammar, sym::Symbol)\n\nConstructs a new solver, with an initial state using starting symbol sym\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualHardFail","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualHardFail","text":"struct LessThanOrEqualHardFail <: LessThanOrEqualResult end\n\nnode1 > node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualResult","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualResult","text":"abstract type LessThanOrEqualResult end\n\nA result of the less_than_or_equal function. Can be one of 3 cases:\n\nLessThanOrEqualSuccess\nLessThanOrEqualHardFail\nLessThanOrEqualSoftFail\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSoftFail","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSoftFail","text":"struct LessThanOrEqualSoftFail <: LessThanOrEqualResult\n\nnode1 <= node2 and node1 > node2 are both possible depending on the assignment of hole1 and hole2. Includes two cases:\n\nhole2::AbstractHole: A failed AbstractHole-AbstractHole comparison. (e.g. AbstractHole(BitVector((1, 0, 1))) vs AbstractHole(BitVector((0, 1, 1))))\nhole2::Nothing: A failed AbstractHole-RuleNode comparison. (e.g. AbstractHole(BitVector((1, 0, 1))) vs RuleNode(2))\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSuccess","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSuccess","text":"abstract type LessThanOrEqualSuccess <: LessThanOrEqualResult\n\nnode1 <= node2 is guaranteed under all possible assignments of the holes involved. The strictness of a LessThanOrEqualSuccess is specified by 1 of 2 concrete cases:\n\nLessThanOrEqualSuccessLessThan: node1 < node2\nLessThanOrEqualSuccessEquality: node1 == node2\nLessThanOrEqualSuccessWithHoles: node1 <= node2. Unable to specific.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSuccessEquality","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSuccessEquality","text":"struct LessThanOrEqualSuccessEquality <: LessThanOrEqualSuccess end\n\nnode1 == node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSuccessLessThan","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSuccessLessThan","text":"struct LessThanOrEqualSuccessEquality <: LessThanOrEqualSuccess end\n\nnode1 < node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LessThanOrEqualSuccessWithHoles","page":"HerbConstraints.jl","title":"HerbConstraints.LessThanOrEqualSuccessWithHoles","text":"struct LessThanOrEqualSuccessWithHoles <: LessThanOrEqualSuccess end\n\nnode1 <= node2 is guaranteed under all possible assignments of the holes involved. Because of the holes involved, it is not possible to specify '<' or '=='.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalContains","page":"HerbConstraints.jl","title":"HerbConstraints.LocalContains","text":"LocalContains\n\nEnforces that a given rule appears at or below the given path at least once.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalContainsSubtree","page":"HerbConstraints.jl","title":"HerbConstraints.LocalContainsSubtree","text":"LocalContains\n\nEnforces that a given tree appears at or below the given path at least once.\n\n!!! warning: This is a stateful constraint can only be propagated by the UniformSolver. The indices and candidates fields should not be set by the user.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalContainsSubtree-Tuple{Vector{Int64}, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.LocalContainsSubtree","text":"LocalContainsSubtree(path::Vector{Int}, tree::AbstractRuleNode)\n\nEnforces that a given tree appears at or below the given path at least once.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.LocalForbidden","page":"HerbConstraints.jl","title":"HerbConstraints.LocalForbidden","text":"LocalForbidden\n\nForbids the a subtree that matches the tree to be generated at the location provided by the path. Use a Forbidden constraint for enforcing this throughout the entire search space.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalForbiddenSequence","page":"HerbConstraints.jl","title":"HerbConstraints.LocalForbiddenSequence","text":"LocalForbiddenSequence <: AbstractLocalConstraint\n\nForbids the given sequence of rule nodes ending at the node at the path. If any of the rules in ignore_if appears in the sequence, the constraint is ignored.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalOrdered","page":"HerbConstraints.jl","title":"HerbConstraints.LocalOrdered","text":"Enforces an order over two or more subtrees that fill the variables specified in order when the pattern is applied at the location given by path. Use an Ordered constraint for enforcing this throughout the entire search space.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.LocalUnique","page":"HerbConstraints.jl","title":"HerbConstraints.LocalUnique","text":"LocalUnique <: AbstractLocalConstraint\n\nEnforces that a given rule appears at or below the given path at most once. In case of the UniformSolver, cache the list of holes, since no new holes can appear.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.MakeEqualHardFail","page":"HerbConstraints.jl","title":"HerbConstraints.MakeEqualHardFail","text":"struct MakeEqualHardFail <: MakeEqualResult end\n\nnode1 != node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.MakeEqualResult","page":"HerbConstraints.jl","title":"HerbConstraints.MakeEqualResult","text":"abstract type MakeEqualResult end\n\nA result of the make_equal! function. Can be one of 3 cases:\n\nMakeEqualSuccess\nMakeEqualHardFail\nMakeEqualSoftFail\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.MakeEqualSoftFail","page":"HerbConstraints.jl","title":"HerbConstraints.MakeEqualSoftFail","text":"struct MakeEqualSoftFail <: MakeEqualResult end\n\nMaking node1 == node2 is ambiguous. Examples:\n\nRuleNode(1, [Hole({1, 2, 3})]) == RuleNode(1, [VarNode(:a)]). The hole can be filled with any rule.\nHole({1, 2, 3}) == DomainRuleNode({1, 2, 3}). The hole can be filled with any rule.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.MakeEqualSuccess","page":"HerbConstraints.jl","title":"HerbConstraints.MakeEqualSuccess","text":"struct MakeEqualSuccess <: MakeEqualResult end\n\nnode1 == node2 is guaranteed under all possible assignments of the holes involved.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.Ordered","page":"HerbConstraints.jl","title":"HerbConstraints.Ordered","text":"Ordered <: AbstractGrammarConstraint\n\nA AbstractGrammarConstraint that enforces a specific order in MatchVar assignments in the pattern defined by tree. Nodes in the pattern can either be a RuleNode, which contains a rule index corresponding to the rule index in the AbstractGrammar and the appropriate number of children. It can also contain a VarNode, which contains a single identifier symbol. A VarNode can match any subtree. If there are multiple instances of the same variable in the pattern, the matched subtrees must be identical.\n\nThe order defines an order between the variable assignments. For example, if the order is [x, y], the constraint will require the assignment to x to be less than or equal to the assignment to y. The order is recursively defined by RuleNode indices. For more information, see Base.isless(rn₁::AbstractRuleNode, rn₂::AbstractRuleNode).\n\nFor example, consider the tree 1(a, 2(b, 3(c, 4)))):\n\nOrdered(RuleNode(3, [VarNode(:v), VarNode(:w)]), [:v, :w]) removes every rule with an index of 5 or greater from the domain of c, since that would make the index of the assignment to v greater than the index of the assignment to w, violating the order.\nOrdered(RuleNode(3, [VarNode(:v), VarNode(:w)]), [:w, :v]) removes every rule with an index of 4 or less from the domain of c, since that would make the index of the assignment to v less than the index of the assignment to w, violating the order.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchHardFail","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchHardFail","text":"The pattern is not matched and can never be matched by filling in holes\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchResult","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchResult","text":"abstract type PatternMatchResult end\n\nA result of the pattern_match function. Can be one of 4 cases:\n\nPatternMatchSuccess\nPatternMatchSuccessWhenHoleAssignedTo\nPatternMatchHardFail\nPatternMatchSoftFail\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchSoftFail","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchSoftFail","text":"The pattern can still be matched in a non-trivial way. Includes two cases:\n\nmultiple holes are involved. this result stores a reference to one of them\na single hole is involved, but needs to be filled with a node of size >= 2\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchSuccess","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchSuccess","text":"The pattern is exactly matched and does not involve any holes at all\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.PatternMatchSuccessWhenHoleAssignedTo","page":"HerbConstraints.jl","title":"HerbConstraints.PatternMatchSuccessWhenHoleAssignedTo","text":"The pattern can be matched when the hole is filled with any of the given ind(s).\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.Solver","page":"HerbConstraints.jl","title":"HerbConstraints.Solver","text":"abstract type Solver\n\nAbstract constraint solver. Each solver should have at least the following fields:\n\nstatistics::SolverStatistics\nfix_point_running::Bool\nschedule::PriorityQueue{AbstractLocalConstraint, Int}\n\nEach solver should implement at least:\n\npost!\nget_tree\nget_grammar\nset_infeasible!\nisfeasible\nHerbCore.get_node_at_location\nget_hole_at_location\nnotify_tree_manipulation\ndeactivate!\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.SolverState","page":"HerbConstraints.jl","title":"HerbConstraints.SolverState","text":"mutable struct SolverState\n\nA state to be solved by the GenericSolver. A state contains of:\n\ntree: A partial AST\nactive_constraints: The local constraints that apply to this tree. These constraints are enforced each time the tree is modified.\nisfeasible: Flag to indicate if this state is still feasible. When a propagator spots an inconsistency, this field will be set to false. Tree manipulations and further propagations are not allowed on infeasible states\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.SolverStatistics","page":"HerbConstraints.jl","title":"HerbConstraints.SolverStatistics","text":"Temporary struct to track! the number of several function calls centered around the Solver\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateHole","page":"HerbConstraints.jl","title":"HerbConstraints.StateHole","text":"StateHole <: AbstractUniformHole\n\nStateHoles are uniform holes used by the UniformSolver. Domain manipulations are tracked for backpropagation.\n\ndomain: A StateSparseSet representing the rule nodes this hole can take. If size(domain) == 1, this hole should act like a RuleNode\nchildren: The children of this hole in the expression tree.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateHole-Tuple{HerbConstraints.StateManager, RuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.StateHole","text":"Converts a RuleNode to a StateHole\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateHole-Tuple{HerbConstraints.StateManager, UniformHole}","page":"HerbConstraints.jl","title":"HerbConstraints.StateHole","text":"Converts a UniformHole to a StateHole\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateInt","page":"HerbConstraints.jl","title":"HerbConstraints.StateInt","text":"Stateful integer that can be saved and restored by the StateManager. Supports the following functions:\n\nget_value\nset_value!\nincrement!\ndecrement!\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateIntBackup","page":"HerbConstraints.jl","title":"HerbConstraints.StateIntBackup","text":"Backup entry for the given StateInt\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateManager","page":"HerbConstraints.jl","title":"HerbConstraints.StateManager","text":"Manages all changes made to StateInts using StateIntBackups\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateSparseSet-Tuple{HerbConstraints.StateManager, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.StateSparseSet","text":"Converts a BitVector domain representation to a StateSparseSet Example:\n\nset = StateSparseSet(sm, BitVector((1, 1, 0, 0, 1, 0, 0))) #{1, 2, 5}\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateSparseSet-Tuple{HerbConstraints.StateManager, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.StateSparseSet","text":"Create a new StateSparseSet with values [1, 2, ..., n]\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateStack","page":"HerbConstraints.jl","title":"HerbConstraints.StateStack","text":"Simple stack that can only increase in size. Supports backtracking by decreasing the size to the saved size.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.StateStack-Union{Tuple{HerbConstraints.AbstractStateManager}, Tuple{T}} where T","page":"HerbConstraints.jl","title":"HerbConstraints.StateStack","text":"function StateStack{T}(sm::AbstractStateManager) where T\n\nCreate an empty StateStack supporting elements of type T\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.StateStack-Union{Tuple{T}, Tuple{HerbConstraints.AbstractStateManager, Vector{T}}} where T","page":"HerbConstraints.jl","title":"HerbConstraints.StateStack","text":"function StateStack{T}(sm::AbstractStateManager, vec::Vector{T}) where T\n\nCreate a StateStack for the provided vec\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.UniformSolver","page":"HerbConstraints.jl","title":"HerbConstraints.UniformSolver","text":"A DFS-based solver that uses StateHoles that support backtracking.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.UniformSolver-Tuple{AbstractGrammar, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.UniformSolver","text":"UniformSolver(grammar::AbstractGrammar, fixed_shaped_tree::AbstractRuleNode)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.Unique","page":"HerbConstraints.jl","title":"HerbConstraints.Unique","text":"Unique <: AbstractGrammarConstraint\n\nThis [AbstractGrammarConstraint] enforces that a given rule appears in the program tree at most once.\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.VarNode","page":"HerbConstraints.jl","title":"HerbConstraints.VarNode","text":"struct VarNode <: AbstractRuleNode\n\nMatches any subtree and assigns it to a variable name. The LocalForbidden constraint will not match if identical variable symbols match to different trees. Example usage:\n\nRuleNode(3, [VarNode(:x), VarNode(:x)])\n\nThis matches RuleNode(3, [RuleNode(1), RuleNode(1)]), RuleNode(3, [RuleNode(2), RuleNode(2)]), etc. but also larger subtrees such as RuleNode(3, [RuleNode(4, [RuleNode(1)]), RuleNode(4, [RuleNode(1)])])\n\n\n\n\n\n","category":"type"},{"location":"HerbConstraints/#HerbConstraints.@csgrammar_annotated-Tuple{Any}","page":"HerbConstraints.jl","title":"HerbConstraints.@csgrammar_annotated","text":"@csgrammar_annotated Define an annotated grammar and return it as a ContextSensitiveGrammar. Allows for adding optional annotations per rule. As well as that, allows for adding optional labels per rule, which can be referenced in annotations. Syntax is backwards-compatible with @csgrammar. Examples:\n\ng₁ = @csgrammar_annotated begin\n Element = 1\n Element = x\n Element = Element + Element := commutative\n Element = Element * Element := (commutative, transitive)\nend\n\ng₁ = @csgrammar_annotated begin\n Element = 1\n Element = x\n Element = Element + Element := forbidden_path([3, 1])\n Element = Element * Element := (commutative, transitive)\nend\n\ng₁ = @csgrammar_annotated begin\n one:: Element = 1\n variable:: Element = x\n addition:: Element = Element + Element := (\n commutative,\n transitive,\n forbidden_path([:addition, :one]) || forbidden_path([:one, :variable])\n )\n multiplication:: Element = Element * Element := (commutative, transitive)\nend\n\n\n\n\n\n","category":"macro"},{"location":"HerbConstraints/#Base.collect-Tuple{HerbConstraints.StateStack}","page":"HerbConstraints.jl","title":"Base.collect","text":"function Base.collect(stack::StateStack)\n\nReturn the internal Vector representation of the stack. !!! warning: The returned vector is read-only.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.findall-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.findall","text":"Returns all elements in the set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.findfirst-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.findfirst","text":"Returns the minimum value in the set. This function name is used instead of min to allow code reuse for domains of type BitVector and StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.findlast-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.findlast","text":"Returns the maximum value in the set. This function name is used instead of min to allow code reuse for domains of type BitVector and StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.getindex-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"Base.getindex","text":"Checks if value val is in StateSparseSet s. !!! warning: This allows a StateSparseSet to be used as if it were a BitVector representation of a set\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.in-Tuple{Int64, HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.in","text":"Checks if value val is in StateSparseSet s.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.in-Union{Tuple{T}, Tuple{HerbConstraints.StateStack{T}, T}} where T","page":"HerbConstraints.jl","title":"Base.in","text":"function Base.in(stack::StateStack, value)::Bool\n\nChecks whether the value is in the stack.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.length-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.length","text":"Returns the number of values in the StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.push!-Tuple{HerbConstraints.StateStack, Any}","page":"HerbConstraints.jl","title":"Base.push!","text":"function Base.push!(stack::StateStack, item)\n\nPlace an item on top of the stack.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.show-Tuple{IO, HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.show","text":"Pretty print the StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.size-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.size","text":"Returns the number of values in the StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.size-Tuple{HerbConstraints.StateStack}","page":"HerbConstraints.jl","title":"Base.size","text":"function Base.size(stack::StateStack)\n\nGet the current size of the stack.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Base.sum-Tuple{HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"Base.sum","text":"Returns the number of values in the StateSparseSet. !!! warning: This is not actually the sum of the set. It is the length of the set. This allows a StateSparseSet to be used as if it were a BitVector representation of a set\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._contains-Tuple{AbstractRuleNode, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._contains","text":"_contains(node::AbstractRuleNode, rule::Int)::Bool\n\nRecursive helper function for the LocalContains constraint Returns one of the following:\n\ntrue, if the node does contains the rule\nfalse, if the node does not contain the rule\nVector{AbstractHole}, if the node contains the rule if one the holes gets filled with the target rule\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._count_occurrences!-Tuple{AbstractRuleNode, Int64, Vector{AbstractHole}}","page":"HerbConstraints.jl","title":"HerbConstraints._count_occurrences!","text":"function _count_occurrences!(node::AbstractRuleNode, rule::Int, holes::Vector{AbstractHole})::Int\n\nRecursive helper function for the LocalUnique constraint. Returns the number of certain occurrences of the rule in the tree. All holes that potentially can hold the target rule are stored in the holes vector.\n\n!!! warning: Stops counting if the rule occurs more than once. Counting beyond 2 is not needed for LocalUnique. \n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._count_occurrences-Tuple{AbstractRuleNode, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._count_occurrences","text":"function _count_occurrences(rule::Int, node::AbstractRuleNode)::Int\n\nRecursively counts the number of occurrences of the rule in the node.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._count_occurrences-Tuple{Vector{AbstractHole}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._count_occurrences","text":"function _count_occurrences(holes::Vector{AbstractHole}, rule::Int)\n\nCounts the occurences of the rule in the cached list of holes.\n\n!!! warning: Stops counting if the rule occurs more than once. Counting beyond 2 is not needed for LocalUnique. \n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._exchange_positions!-Tuple{HerbConstraints.StateSparseSet, Int64, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._exchange_positions!","text":"Exchanges the positions in the internal representation of the StateSparseSet.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._update_bounds_val_removed!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._update_bounds_val_removed!","text":"This function should be called whenever the minimum or maximum value from the set might have been removed. The minimum and maximum value of the set will be updated to the actual bounds of the set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._update_max_val_removed!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._update_max_val_removed!","text":"This function should be called whenever the maximum value from the set might have been removed. The maximum value of the set will be updated to the actual maximum of the set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints._update_min_val_removed!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints._update_min_val_removed!","text":"This function should be called whenever the minimum value from the set might have been removed. The minimum value of the set will be updated to the actual minimum of the set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.annotation2constraint-Tuple{Any, Int64, Vector{String}}","page":"HerbConstraints.jl","title":"HerbConstraints.annotation2constraint","text":"Converts an annotation to a constraint. commutative: creates an Ordered constraint transitive: creates an (incorrect) Forbidden constraint forbidden_path(path::Vector{Union{Symbol, Int}}): creates a ForbiddenPath constraint with the original rule included ... || ...: creates a OneOf constraint (also works with ... || ... || ... et cetera, though not very performant)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.are_disjoint-Tuple{BitVector, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.are_disjoint","text":"are_disjoint(domain1::BitVector, domain2::BitVector)::Bool\n\nReturns true if there is no overlap in values between domain1 and domain2\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.are_disjoint-Tuple{HerbConstraints.StateSparseSet, HerbConstraints.StateSparseSet}","page":"HerbConstraints.jl","title":"HerbConstraints.are_disjoint","text":"are_disjoint(set1::StateSparseSet, set2::StateSparseSet)\n\nReturns true if there is no overlap in values between set1 and set2\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.backup!-Tuple{StateInt}","page":"HerbConstraints.jl","title":"HerbConstraints.backup!","text":"Should be called whenever the state of a StateInt is modified. Creates a StateIntBackup for the given StateInt. Only backup the value if this integer has not been stored during this state before Example usecase:\n\na = StateInt(sm, 10)\nsave_state!(sm)\nset_value!(a, 9) #backup value 10\nset_value!(a, 8) #no need to backup again\nset_value!(a, 3) #no need to backup again\nrestore!(sm) #restores a to value 10\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{Contains, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::Contains, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the Contains constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{ContainsSubtree, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::ContainsSubtree, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the ContainsSubtree constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{Forbidden, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::Forbidden, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the Forbidden constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{ForbiddenSequence, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::ForbiddenSequence, tree::AbstractRuleNode; sequence_started=false)::Bool\n\nChecks if the given AbstractRuleNode tree abides the ForbiddenSequence constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{Ordered, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"check_tree(c::Ordered, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the Ordered constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.check_tree-Tuple{Unique, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.check_tree","text":"function check_tree(c::Unique, tree::AbstractRuleNode)::Bool\n\nChecks if the given AbstractRuleNode tree abides the Unique constraint.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.contains_varnode-Tuple{AbstractRuleNode, Symbol}","page":"HerbConstraints.jl","title":"HerbConstraints.contains_varnode","text":"contains_varnode(rn::AbstractRuleNode, name::Symbol)\n\nChecks if an AbstractRuleNode tree contains a VarNode with the given name.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.deactivate!-Tuple{GenericSolver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.deactivate!","text":"deactivate!(solver::GenericSolver, constraint::AbstractLocalConstraint)\n\nFunction that should be called whenever the constraint is already satisfied and never has to be repropagated.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.deactivate!-Tuple{UniformSolver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.deactivate!","text":"deactivate!(solver::UniformSolver, constraint::AbstractLocalConstraint)\n\nFunction that should be called whenever the constraint is already satisfied and never has to be repropagated.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.decrement!-Tuple{StateInt}","page":"HerbConstraints.jl","title":"HerbConstraints.decrement!","text":"Decrease the value of the integer by 1\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.fix_point!-Tuple{Solver}","page":"HerbConstraints.jl","title":"HerbConstraints.fix_point!","text":"fix_point!(solver::Solver)\n\nPropagate constraints in the current state until no further dedecutions can be made\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.freeze_state-Tuple{StateHole}","page":"HerbConstraints.jl","title":"HerbConstraints.freeze_state","text":"freeze_state(hole::StateHole)::RuleNode\n\nConverts a [StateHole])(@ref) to a [RuleNode]@(ref). The hole and its children are assumed to be filled.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_grammar-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_grammar","text":"function get_grammar(solver::GenericSolver)::AbstractGrammar\n\nGet the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_grammar-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_grammar","text":"function get_grammar(solver::UniformSolver)::AbstractGrammar\n\nGet the grammar.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_hole_at_location-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.get_hole_at_location","text":"get_hole_at_location(solver::GenericSolver, location::Vector{Int})::AbstractHole\n\nGet the node at path location and assert it is a AbstractHole.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_hole_at_location-Tuple{UniformSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.get_hole_at_location","text":"get_hole_at_location(solver::UniformSolver, path::Vector{Int})\n\nGet the hole that is located at the provided path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_intersection-Tuple{BitVector, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.get_intersection","text":"get_intersection(domain1::BitVector, domain2::BitVector)::Bool\n\nReturns all the values that are in both domain1 and domain2\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_max_depth-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_max_depth","text":"function get_max_depth(solver::GenericSolver)::SolverState\n\nGet the maximum depth of the tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_max_size-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_max_size","text":"function get_max_depth(solver::GenericSolver)::SolverState\n\nGet the maximum number of AbstractRuleNodes allowed inside the tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_nodes-Tuple{Any}","page":"HerbConstraints.jl","title":"HerbConstraints.get_nodes","text":"get_nodes(solver)\n\nReturn an iterator over all nodes in the tree\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_nodes_on_path-Tuple{AbstractRuleNode, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.get_nodes_on_path","text":"function get_nodes_on_path(root::AbstractRuleNode, path::Vector{Int})::Vector{AbstractRuleNode}\n\nGets a list of nodes on the path, starting (and including) the root.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_priority-Tuple{AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.get_priority","text":"function get_priority(::AbstractLocalConstraint)\n\nUsed to determine which constraint to propagate first in fix_point!. Constraints with fast propagators and/or strong inference should be propagated first.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_starting_symbol-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_starting_symbol","text":"function get_starting_symbol(solver::GenericSolver)::Symbol\n\nGet the symbol from the solver.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_state-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_state","text":"function get_state(solver::GenericSolver)::SolverState\n\nGet the current [SolverState]@(ref) of the solver.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_tree-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_tree","text":"function get_tree(solver::GenericSolver)::AbstractRuleNode\n\nReturns the number of AbstractRuleNodes in the tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_tree-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_tree","text":"function get_tree(solver::UniformSolver)::AbstractRuleNode\n\nGet the root of the tree. This remains the same instance throughout the entire search.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_tree_size-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.get_tree_size","text":"function get_tree_size(solver::GenericSolver)::Int\n\nReturns the number of AbstractRuleNodes in the tree.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.get_value-Tuple{StateInt}","page":"HerbConstraints.jl","title":"HerbConstraints.get_value","text":"Get the value of the stateful integer\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.increment!-Tuple{StateInt}","page":"HerbConstraints.jl","title":"HerbConstraints.increment!","text":"Increase the value of the integer by 1\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.is_subdomain-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.is_subdomain","text":"is_subdomain(specific_tree::AbstractRuleNode, general_tree::AbstractRuleNode)\n\nChecks if the specific_tree can be obtained by repeatedly removing values from the general_tree\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.is_subdomain-Tuple{BitVector, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.is_subdomain","text":" is_subdomain(subdomain::BitVector, domain::BitVector)\n\nChecks if subdomain is a subdomain of domain. Example: [0, 0, 1, 0] is a subdomain of [0, 1, 1, 1]\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.isfeasible-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.isfeasible","text":"isfeasible(solver::GenericSolver)\n\nReturns true if no inconsistency has been detected. Used in several ways:\n\nIterators should check for infeasibility to discard infeasible states\nAfter any tree manipulation with the possibility of an inconsistency (e.g. remove_below!, remove_above!, remove!)\nfix_point! should check for infeasibility to clear its schedule and return\nSome GenericSolver functions assert a feasible state for debugging purposes @assert isfeasible(solver)\nSome GenericSolver functions have a guard that skip the function on an infeasible state: if !isfeasible(solver) return end\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.isfeasible-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.isfeasible","text":"isfeasible(solver::UniformSolver)\n\nReturns true if no inconsistency has been detected.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.load_state!-Tuple{GenericSolver, SolverState}","page":"HerbConstraints.jl","title":"HerbConstraints.load_state!","text":"load_state!(solver::GenericSolver, state::SolverState)\n\nOverwrites the current state with the given state\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.make_equal!-Tuple{Solver, Union{AbstractHole, RuleNode}, Union{DomainRuleNode, AbstractHole, RuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.make_equal!","text":"function make_equal!(solver::Solver, node1::AbstractRuleNode, node2::AbstractRuleNode)::MakeEqualResult\n\nTree manipulation that enforces node1 == node2 if unambiguous.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.make_less_than_or_equal!-Tuple{Solver, Union{AbstractHole, RuleNode}, Union{AbstractHole, RuleNode}, Vector{Tuple{AbstractHole, Int64}}}","page":"HerbConstraints.jl","title":"HerbConstraints.make_less_than_or_equal!","text":"function make_less_than_or_equal!(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole}, guards::Vector{Tuple{AbstractHole, Int}})::LessThanOrEqualResult\n\nHelper function that keeps track of the guards\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.make_less_than_or_equal!-Tuple{Solver, Union{AbstractHole, RuleNode}, Union{AbstractHole, RuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.make_less_than_or_equal!","text":"function make_less_than_or_equal!(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole})::LessThanOrEqualResult\n\nEnsures that n1<=n2 by removing impossible values from holes. Returns one of the following results:\n\nLessThanOrEqualSuccess. When [n1<=n2].\nLessThanOrEqualHardFail. When [n1>n2] or when the solver state is infeasible.\nLessThanOrEqualSoftFail. When no further deductions can be made, but [n1<=n2] and [n1>n2] are still possible.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.make_less_than_or_equal!-Tuple{Solver, Vector{AbstractRuleNode}, Vector{AbstractRuleNode}, Vector{Tuple{AbstractHole, Int64}}}","page":"HerbConstraints.jl","title":"HerbConstraints.make_less_than_or_equal!","text":"function make_less_than_or_equal!(solver::Solver, nodes1::Vector{AbstractRuleNode}, nodes2::Vector{AbstractRuleNode}, guards::Vector{Tuple{AbstractHole, Int}})::LessThanOrEqualResult\n\nHelper function that tiebreaks on children.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.new_state!-Tuple{GenericSolver, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.new_state!","text":"new_state!(solver::GenericSolver, tree::AbstractRuleNode)\n\nOverwrites the current state and propagates constraints on the tree from the ground up\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_new_node-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_new_node","text":"notify_new_node(solver::GenericSolver, event_path::Vector{Int})\n\nNotify all constraints that a new node has appeared at the event_path by calling their respective on_new_node function.\n\nwarning: Warning\nThis does not notify the solver about nodes below the event_path. In that case, call notify_new_nodes instead.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_new_nodes-Tuple{GenericSolver, AbstractRuleNode, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_new_nodes","text":"notify_new_nodes(solver::GenericSolver, node::AbstractRuleNode, path::Vector{Int})\n\nNotify all grammar constraints about the new node and its (grand)children\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_new_nodes-Tuple{UniformSolver, AbstractRuleNode, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_new_nodes","text":"notify_new_nodes(solver::UniformSolver, node::AbstractRuleNode, path::Vector{Int})\n\nNotify all grammar constraints about the new node and its (grand)children\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_tree_manipulation-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_tree_manipulation","text":"notify_tree_manipulation(solver::GenericSolver, event_path::Vector{Int})\n\nNotify subscribed constraints that a tree manipulation has occured at the event_path by scheduling them for propagation\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.notify_tree_manipulation-Tuple{UniformSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.notify_tree_manipulation","text":"notify_tree_manipulation(solver::UniformSolver, event_path::Vector{Int})\n\nNotify subscribed constraints that a tree manipulation has occured at the event_path by scheduling them for propagation\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.partition-Tuple{Hole, ContextSensitiveGrammar}","page":"HerbConstraints.jl","title":"HerbConstraints.partition","text":"partition(hole::Hole, grammar::ContextSensitiveGrammar)::Vector{BitVector}\n\nPartition a Hole into subdomains grouped by childtypes\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{AbstractRuleNode, AbstractRuleNode, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"Generic fallback function for commutativity. Swaps arguments 1 and 2, then dispatches to a more specific signature. If this gets stuck in an infinite loop, the implementation of an AbstractRuleNode type pair is missing.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{AbstractRuleNode, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(rn::AbstractRuleNode, mn::AbstractRuleNode)::PatternMatchResult\n\nRecursively tries to match AbstractRuleNode rn with AbstractRuleNode mn. Returns a PatternMatchResult that describes if the pattern was matched.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{AbstractRuleNode, DomainRuleNode, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(node::AbstractRuleNode, domainrulenode::DomainRuleNode, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult\n\nComparing any AbstractRuleNode with a DomainRuleNode\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{AbstractRuleNode, VarNode, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(rn::AbstractRuleNode, var::VarNode, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult\n\nComparing any AbstractRuleNode with a named VarNode\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{Union{AbstractHole, RuleNode}, Union{AbstractHole, RuleNode}, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(h1::Union{RuleNode, AbstractHole}, h2::Union{RuleNode, AbstractHole}, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult\n\nComparing any pair of Rulenode and/or AbstractHole. It is important to note that some AbstractHoles are already filled and should be treated as RuleNode. This is why this function is dispatched on (isfilled(h1), isfilled(h2)). The '(RuleNode, AbstractHole)' case could still include two nodes of type AbstractHole, but one of them should be treated as a rulenode.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.pattern_match-Tuple{Vector{AbstractRuleNode}, Vector{AbstractRuleNode}, Dict{Symbol, AbstractRuleNode}}","page":"HerbConstraints.jl","title":"HerbConstraints.pattern_match","text":"pattern_match(rns::Vector{AbstractRuleNode}, mns::Vector{AbstractRuleNode}, vars::Dict{Symbol, AbstractRuleNode})::PatternMatchResult\n\nPairwise tries to match two ordered lists of AbstractRuleNodes. Typically, this function is used to pattern match the children two AbstractRuleNodes.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.post!-Tuple{GenericSolver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.post!","text":"post!(solver::GenericSolver, constraint::AbstractLocalConstraint)\n\nImposes the constraint to the current state. By default, the constraint will be scheduled for its initial propagation. Constraints can overload this method to add themselves to notify lists or triggers.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.post!-Tuple{UniformSolver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.post!","text":"post!(solver::UniformSolver, constraint::AbstractLocalConstraint)\n\nPost a new local constraint. Converts the constraint to a state constraint and schedules it for propagation.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{GenericSolver, LocalContainsSubtree}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(::GenericSolver, ::LocalContainsSubtree)\n\n!!! warning: LocalContainsSubtree uses stateful properties and can therefore not be propagated in the GenericSolver. (The GenericSolver shares constraints among different states, so they cannot use stateful properties)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalContains}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalContains)\n\nEnforce that the rule appears at or below the path at least once. Uses a helper function to retrieve a list of holes that can potentially hold the target rule. If there is only a single hole that can potentially hold the target rule, that hole will be filled with that rule.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalForbiddenSequence}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalForbiddenSequence)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalForbidden}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalForbidden)\n\nEnforce that the forbidden tree does not occur at the path. The forbidden tree is matched against the AbstractRuleNode located at the path. Deductions are based on the type of the PatternMatchResult returned by the pattern_match function.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalOrdered}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalOrdered)\n\nEnforce that the VarNodes in the tree are in the specified order. First the node located at the path is matched to see if the ordered constraint applies here. The nodes matching the variables are stored in the vars dictionary. Then the order is enforced within the make_less_than_or_equal! tree manipulation. \n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{Solver, LocalUnique}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::Solver, c::LocalUnique)\n\nEnforce that the rule appears at or below the path at least once. Uses a helper function to retrieve a list of holes that can potentially hold the target rule. If there is only a single hole that can potentially hold the target rule, that hole will be filled with that rule.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.propagate!-Tuple{UniformSolver, LocalContainsSubtree}","page":"HerbConstraints.jl","title":"HerbConstraints.propagate!","text":"function propagate!(solver::UniformSolver, c::LocalContainsSubtree)\n\nEnforce that the tree appears at or below the path at least once. Nodes that can potentially become the target sub-tree are considered candidates. In case of multiple candidates, a stateful set of indices is used to keep track of active candidates.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{GenericSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)\n\nRemove rule_index from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{GenericSolver, Vector{Int64}, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(solver::GenericSolver, path::Vector{Int}, rules::Vector{Int})\n\nRemove all rules from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(set::StateSparseSet, val::Int)\n\nRemoves value val from StateSparseSet set. Returns true if val was in set.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{UniformSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(solver::Solver, path::Vector{Int}, rule_index::Int)\n\nRemove rule_index from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove!-Tuple{UniformSolver, Vector{Int64}, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.remove!","text":"remove!(solver::UniformSolver, path::Vector{Int}, rules::Vector{Int})\n\nRemove all rules from the domain of the hole located at the path. It is assumed the path points to a hole, otherwise an exception will be thrown.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_above!-Tuple{GenericSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_above!","text":"remove_above!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)\n\nReduce the domain of the hole located at the path by removing all rules indices above rule_index Example: rule_index = 2. hole with domain [1, 1, 0, 1] gets reduced to [1, 0, 0, 0] and gets simplified to a RuleNode\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_above!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_above!","text":"Remove all the values greater than val from the set\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_above!-Tuple{UniformSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_above!","text":"remove_above!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)\n\nReduce the domain of the hole located at the path by removing all rules indices above rule_index Example: rule_index = 2. hole with domain {1, 2, 4} gets reduced to {1}\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_all_but!-Tuple{GenericSolver, Vector{Int64}, BitVector}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_all_but!","text":"remove_all_but!(solver::GenericSolver, path::Vector{Int}, new_domain::BitVector)\n\nReduce the domain of the hole located at the path, to the new_domain. It is assumed the path points to a hole, otherwise an exception will be thrown. It is assumed new_domain ⊆ domain. For example: [1, 0, 1, 0] ⊆ [1, 0, 1, 1]\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_all_but!-Tuple{GenericSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_all_but!","text":"remove_all_but!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)\n\nFill in the hole located at the path with rule rule_index. It is assumed the path points to a hole, otherwise an exception will be thrown. It is assumed rule_index ∈ hole.domain.\n\n!!! warning: If the hole is known to be in the current tree, the hole can be passed directly. The caller has to make sure that the hole instance is actually present at the provided path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_all_but!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_all_but!","text":"remove_all_but!(set::StateSparseSet, val::Int)::Bool\n\nRemoves all values from StateSparseSet set, except val\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_all_but!-Tuple{UniformSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_all_but!","text":"remove_all_but!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)\n\nFill in the hole located at the path with rule rule_index.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_below!-Tuple{GenericSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_below!","text":"remove_below!(solver::GenericSolver, path::Vector{Int}, rule_index::Int)\n\nReduce the domain of the hole located at the path by removing all rules indices below rule_index Example: rule_index = 2. hole with domain [1, 1, 0, 1] gets reduced to [0, 1, 0, 1]\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_below!-Tuple{HerbConstraints.StateSparseSet, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_below!","text":"Remove all the values less than val from the set\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_below!-Tuple{UniformSolver, Vector{Int64}, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_below!","text":"remove_below!(solver::UniformSolver, path::Vector{Int}, rule_index::Int)\n\nReduce the domain of the hole located at the path by removing all rules indices below rule_index Example: rule_index = 2. hole with domain {1, 2, 4} gets reduced to {2, 4}\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.remove_node!-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.remove_node!","text":"function remove_node!(solver::GenericSolver, path::Vector{Int})\n\nRemove the node at the given path by substituting it with a hole of the same symbol.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.restore!-Tuple{HerbConstraints.StateIntBackup}","page":"HerbConstraints.jl","title":"HerbConstraints.restore!","text":"Restores the StateInt stored in the StateIntBackup to its original value\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.restore!-Tuple{HerbConstraints.StateManager}","page":"HerbConstraints.jl","title":"HerbConstraints.restore!","text":"Reverts all the backups since the last save_state!.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.restore!-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.restore!","text":"Restore state of the solver until the last save_state!\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.save_state!-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.save_state!","text":"save_state!(solver::GenericSolver)\n\nReturns a copy of the current state that can be restored by calling load_state!(solver, state)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.save_state!-Tuple{HerbConstraints.StateManager}","page":"HerbConstraints.jl","title":"HerbConstraints.save_state!","text":"Make a backup of the current state. Return to this state by calling restore!.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.save_state!-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.save_state!","text":"Save the current state of the solver, can restored using restore!\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.schedule!-Tuple{Solver, AbstractLocalConstraint}","page":"HerbConstraints.jl","title":"HerbConstraints.schedule!","text":"schedule(solver::GenericSolver, constraint::AbstractLocalConstraint)\n\nSchedules the constraint for propagation.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.set_infeasible!-Tuple{GenericSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.set_infeasible!","text":"set_infeasible!(solver::GenericSolver)\n\nFunction to be called if any inconsistency has been detected\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.set_infeasible!-Tuple{UniformSolver}","page":"HerbConstraints.jl","title":"HerbConstraints.set_infeasible!","text":"set_infeasible!(solver::Solver)\n\nFunction to be called if any inconsistency has been detected\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.set_value!-Tuple{StateInt, Int64}","page":"HerbConstraints.jl","title":"HerbConstraints.set_value!","text":"Set the value of the integer to the given val\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.shouldschedule-Tuple{Solver, AbstractLocalConstraint, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.shouldschedule","text":"shouldschedule(solver::Solver, constraint::AbstractLocalConstraint, path::Vector{Int})::Bool\n\nFunction that is called when a tree manipulation occured at the path. Returns true if the constraint should be scheduled for propagation.\n\nDefault behavior: return true iff the manipulation happened at or below the constraint path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.shouldschedule-Tuple{Solver, LocalForbiddenSequence, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.shouldschedule","text":"shouldschedule(::Solver, constraint::LocalForbiddenSequence, path::Vector{Int})::Bool\n\nReturn true iff the manipulation happened at or above the constraint path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.simplify_hole!-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbConstraints.simplify_hole!","text":"simplify_hole!(solver::GenericSolver, path::Vector{Int})\n\nTakes a Hole and tries to simplify it to a UniformHole or RuleNode. If the domain of the hole is empty, the state will be marked as infeasible\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbConstraints.substitute!-Tuple{GenericSolver, Vector{Int64}, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbConstraints.substitute!","text":"substitute!(solver::GenericSolver, path::Vector{Int}, new_node::AbstractRuleNode; is_domain_increasing::Union{Nothing, Bool}=nothing)\n\nSubstitute the node at the path, with a new_node.\n\nis_domain_increasing: indicates if all grammar constraints should be repropagated from the ground up.\n\nDomain increasing substitutions are substitutions that cannot be achieved by repeatedly removing values from domains. Example of an domain increasing event: hole[{3, 4, 5}] -> hole[{1, 2}]. Example of an domain decreasing event: hole[{3, 4, 5}] -> rulenode(4, [hole[{1, 2}], rulenode(1)]).\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.contains_hole-Tuple{StateHole}","page":"HerbConstraints.jl","title":"HerbCore.contains_hole","text":"contains_hole(hole::StateHole)::Bool\n\nReturns true if the hole or any of its (grand)children are not filled.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_node_at_location-Tuple{GenericSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbCore.get_node_at_location","text":"HerbCore.get_node_at_location(solver::GenericSolver, location::Vector{Int})::AbstractRuleNode\n\nGet the node at path location.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_node_at_location-Tuple{UniformSolver, Vector{Int64}}","page":"HerbConstraints.jl","title":"HerbCore.get_node_at_location","text":"get_node_at_location(solver::UniformSolver, path::Vector{Int})\n\nGet the node that is located at the provided path.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_path-Tuple{GenericSolver, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbCore.get_path","text":"get_path(solver::GenericSolver, node::AbstractRuleNode)\n\nGet the path at which the node is located.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_path-Tuple{UniformSolver, AbstractRuleNode}","page":"HerbConstraints.jl","title":"HerbCore.get_path","text":"get_path(solver::UniformSolver, node::AbstractRuleNode)\n\nGet the path at which the node is located.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.get_rule-Tuple{StateHole}","page":"HerbConstraints.jl","title":"HerbCore.get_rule","text":"get_rule(hole::StateHole)::Int\n\nAssuming the hole has domain size 1, get the rule it is currently assigned to.\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#HerbCore.isfilled-Tuple{StateHole}","page":"HerbConstraints.jl","title":"HerbCore.isfilled","text":"isfilled(hole::StateHole)::Bool\n\nHoles with domain size 1 are fixed to a rule. Returns whether the hole has domain size 1. (holes with an empty domain are not considered to be fixed)\n\n\n\n\n\n","category":"method"},{"location":"HerbConstraints/#Index","page":"HerbConstraints.jl","title":"Index","text":"","category":"section"},{"location":"HerbConstraints/","page":"HerbConstraints.jl","title":"HerbConstraints.jl","text":"","category":"page"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"\n\n\n\n\n

Advanced Search Procedures in Herb.jl

A more verbose getting started with Herb.jl described the concept of a program space and showed how to search it with Herb.jl, using a simple breadth-first-search (BFS) iterator for the search. This tutorial takes a closer look at advanced search procedures hat can be employed to find a solution program to a program synthesis problem.

More specifically, you will learn about

  • Parameters that can be specified and their effect on the search procedure.

  • Deterministic search methods BFS and DFS.

  • Stochastic search methods, which introduce randomness to search the program space. We will look at Metropolis-Hastings, Very Large Scale Neighbourhood Search, Simulated Annealing and Genetic Search.

\n\n
using PlutoUI
\n\n\n
TableOfContents()
\n\n\n\n

Let's import all the Herb modules that we will use throughout the tutorial.

\n\n
using HerbGrammar, HerbSpecification, HerbSearch, HerbInterpret, HerbConstraints, HerbCore\n
\n\n\n\n

We start with a simple grammar:.

\n\n
g_1 = @csgrammar begin\n    Number = |(1:2)\n    Number = x\n    Number = Number + Number\n    Number = Number * Number\nend
\n
1: Number = 1\n2: Number = 2\n3: Number = x\n4: Number = Number + Number\n5: Number = Number * Number\n
\n\n\n

Let's use the simple program 2x+1 as our problem and generate some input-output examples for the problem specification.

\n\n
problem_1 = Problem([IOExample(Dict(:x => x), 2x+1) for x ∈ 1:5])
\n
Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 3), IOExample(Dict{Symbol, Any}(:x => 2), 5), IOExample(Dict{Symbol, Any}(:x => 3), 7), IOExample(Dict{Symbol, Any}(:x => 4), 9), IOExample(Dict{Symbol, Any}(:x => 5), 11)])
\n\n","category":"page"},{"location":"tutorials/advanced_search/#Parameters","page":"Advanced Search Procedures","title":"Parameters","text":"","category":"section"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"
\n

Search procedures typically have some hyperparameters that you can configure.

max_depth

max_depth controls the maximum depth of the program trees that are explored during the search, effectively limiting the size and complexity of the synthesized program. The parameter is configured as part of the iterator.

In the following example, we consider two different values for max_depth.

\n\n
iterator_1 = BFSIterator(g_1, :Number, max_depth=3)
\n
BFSIterator(GenericSolver(1: Number = 1\n2: Number = 2\n3: Number = x\n4: Number = Number + Number\n5: Number = Number * Number\n, SolverState(hole[Bool[1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 3))
\n\n
iterator_2 = BFSIterator(g_1, :Number, max_depth=6)
\n
BFSIterator(GenericSolver(1: Number = 1\n2: Number = 2\n3: Number = x\n4: Number = Number + Number\n5: Number = Number * Number\n, SolverState(hole[Bool[1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 6))
\n\n\n

To see the effect max_depth has on the number of memory allocations made during the program synthesis process, we use the @time macro.

\n\n
begin\n    println(\"Solution for max_depth = 3:\")\n    solution_1 = @time synth(problem_1, iterator_1)\n    println(solution_1)\n    println(\"----------------------\")\n    println(\"Solution for max_depth = 6:\")\n    solution_2 = @time synth(problem_1, iterator_2)\n    println(solution_2)\nend
\n\n\n\n

While increasing max_depth allows us to explore more complex and deeper program trees, which may lead to a better solution, it also requires more memory allocation and can increase the execution time.

\n\n\n

max_enumerations

max_enumerations defines the maximum number of candidate programs that can be evaluated before the search is terminated.

Let's explore how many enumerations are necessary to solve our simple problem.

\n\n
for i in range(1, 50)\n        println(i, \" enumerations\")\n        iterator = BFSIterator(g_1, :Number, max_depth=i)\n        solution = @time synth(problem_1, iterator)\n        println(solution)\nend
\n\n\n\n

At i = 3, we observe that an optimal program is found. Increasing the number of enumerations beyond that does not affect the solution or the number of memory allocations.

\n\n\n

allow_evaluation_errors

A final parameter we consider here is allow_evaluation_errors, which is false by default. When true, the search continues even if an exception occurs during the evaluation of a candidate program. This allows the search process to handle faulty candidate programs and explore other ones, instead of throwing an error and terminating prematurely.

We will use a new example to see the effect of allow_evaluation_errors. We begin defining a new simple grammar. We then create some input-output examples to specify the problem we want to solve. This time, we choose a problem that we cannot solve with the provided grammar.

\n\n
g_2 = @csgrammar begin\n    Number = 1\n    List = []\n    Index = List[Number]\nend
\n
1: Number = 1\n2: List = []\n3: Index = List[Number]\n
\n\n
problem_2 = Problem([IOExample(Dict(), x) for x ∈ 1:5])
\n
Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(), 1), IOExample(Dict{Symbol, Any}(), 2), IOExample(Dict{Symbol, Any}(), 3), IOExample(Dict{Symbol, Any}(), 4), IOExample(Dict{Symbol, Any}(), 5)])
\n\n
iterator_3 = BFSIterator(g_2, :Index, max_depth=2)
\n
BFSIterator(GenericSolver(1: Number = 1\n2: List = []\n3: Index = List[Number]\n, SolverState(3{2,1}, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2))
\n\n
using Test
\n\n\n
Test.@test_throws HerbSearch.EvaluationError synth(problem_2, iterator_3)
\n
Test Passed\n      Thrown: HerbSearch.EvaluationError
\n\n\n

As expected, an exception occurs during the synthesis process. Now we try the same again, with allow_evaluation_errors=true.

\n\n
solution_4 = synth(problem_2, iterator_3, allow_evaluation_errors=true)
\n
(3{2,1}, suboptimal_program)
\n\n
println(\"solution: \", solution_4)
\n\n\n\n

\"This time we find a solution, although a suboptimal one.

\n\n","category":"page"},{"location":"tutorials/advanced_search/#Top-down-search","page":"Advanced Search Procedures","title":"Top-down search","text":"","category":"section"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"
\n

Herb.jl provides already implemented, ready-to-use search methods. The core building block of the search is the program iterator, which represents a walk through the program space. All program iterators share the top-level abstract type ProgramIterator. For more information on iterators and how to customize them, see this tutorial.

First, we explore two fundamental deterministic top-down search algorithms: breadth-first search (BFS) and depth-first search (DFS). Both algorithms are implemented using the abstract type TopDownIterator, which can be customized through the functions

  • priority_function

  • derivation_heuristic

  • hole_heuristic

\n\n\n

First, we explore two fundamental deterministic top-down search algorithms: breadth-first search (BFS) and depth-first search (DFS). Both algorithms are implemented using the abstract type TopDownIterator, which can be customized through the functions priorityfunction, derivationheuristic, and hole_heuristic.

Breadth-First Search

The BFSIterator enumerates all possible programs at a given depth before progressing to the next level, ensuring that trees are explored in increasing order of size. This guarantees that smaller programs are evaluated first, and larger, more complex ones are considered only after all smaller ones have been processed.

To explore BFSIterator, we define another very simple grammar.

\n\n
g_3 = @csgrammar begin\n        Real = 1 | 2\n        Real = Real * Real\nend
\n
1: Real = 1\n2: Real = 2\n3: Real = Real * Real\n
\n\n\n

Next, we define a BFSIterator with a max_depth of 2 and a max_size of infinite (which we approximate with the maximum value of Int), and a starting symbol of type Real. By default, BFSIterator uses the heuristic 'left-most first', i.e., the left-most child in the tree is always explored first.

\n\n
iterator_bfs = BFSIterator(g_3, :Real, max_depth=2, max_size=typemax(Int))
\n
BFSIterator(GenericSolver(1: Real = 1\n2: Real = 2\n3: Real = Real * Real\n, SolverState(hole[Bool[1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2))
\n\n\n

To see all possible solution programs the iterator explores, we use collect. It returs a list of the programs, ordered by increasing size and depth.

\n\n
programs_bfs = collect(iterator_bfs)
\n
6-element Vector{RuleNode}:\n 1,\n 2,\n 3{1,1}\n 3{1,2}\n 3{2,2}\n 3{2,1}
\n\n
println(programs_bfs)
\n\n\n\n

Let's verify that the iterator returns the programs we expect (keep in mind we use a leftmost-first heuristic).

\n\n
answer_programs = [\n    RuleNode(1),\n    RuleNode(2),\n    RuleNode(3, [RuleNode(1), RuleNode(1)]),\n    RuleNode(3, [RuleNode(1), RuleNode(2)]),\n    RuleNode(3, [RuleNode(2), RuleNode(1)]),\n    RuleNode(3, [RuleNode(2), RuleNode(2)])\n]
\n
6-element Vector{RuleNode}:\n 1,\n 2,\n 3{1,1}\n 3{1,2}\n 3{2,1}\n 3{2,2}
\n\n
println(all(p ∈ programs_bfs for p ∈ answer_programs))
\n\n\n\n

Depth-First Search

The DFSIterator explores one branch of the search tree at a time, fully traversing it unitl a correct program is found or the specified max_depth is reached. Only after completing the current branch, it proceeds to the next branch.

As before, we collect the candidate programs using the same grammar, but a DFSIterator.

\n\n
iterator_dfs = DFSIterator(g_3, :Real, max_depth=2, max_size=typemax(Int))
\n
DFSIterator(GenericSolver(1: Real = 1\n2: Real = 2\n3: Real = Real * Real\n, SolverState(hole[Bool[1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2))
\n\n
programs_dfs = collect(iterator_dfs)
\n
6-element Vector{RuleNode}:\n 1,\n 3{1,1}\n 3{1,2}\n 3{2,2}\n 3{2,1}\n 2,
\n\n
println(programs_dfs)
\n\n\n\n

DFSIterator also uses by default a leftmost-first heuristic. If we want to use a rightmost-first heuristic instead, we can create our own iterator DFSIteratorRightmost as a sub-type of TopDownIterator, using the @programiterator macro. Then we implement the functions priority_function and hole_heuristic. Also see the tutorial Top Down Iterator for how to build iterators is Herb.jl.

\n\n
@programiterator DFSIteratorRightmost() <: TopDownIterator
\n
DFSIteratorRightmost
\n\n\n

By default, priority_function for a TopDownIterator is that of a BFS iterator. Hence, we need to provide a new implementation.

\n\n
function priority_function(\n    ::DFSIteratorRightmost, \n    ::AbstractGrammar, \n    ::AbstractRuleNode, \n    parent_value::Union{Real, Tuple{Vararg{Real}}},\n    isrequeued::Bool\n)\n    if isrequeued\n        return parent_value;\n    end\n    return parent_value - 1;\nend
\n
priority_function (generic function with 1 method)
\n\n\n

Next, we need to implement the hole_heuristic to be rightmost-first.

\n\n
function hole_heuristic(::DFSIteratorRightmost, node::AbstractRuleNode, max_depth::Int)::Union{ExpandFailureReason, HoleReference}\n    return heuristic_rightmost(node, max_depth);\nend
\n
hole_heuristic (generic function with 1 method)
\n\n
iteratordfs_rightmost = DFSIteratorRightmost(g_3, :Real, max_depth=2, max_size=typemax(Int))
\n
DFSIteratorRightmost(GenericSolver(1: Real = 1\n2: Real = 2\n3: Real = Real * Real\n, SolverState(hole[Bool[1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2))
\n\n
programs_dfs_rightmost = collect(iteratordfs_rightmost)
\n
6-element Vector{RuleNode}:\n 1,\n 2,\n 3{1,1}\n 3{1,2}\n 3{2,2}\n 3{2,1}
\n\n
println(programs_dfs_rightmost)
\n\n\n\n

We observe that the order of programs has changed. We can also test if both DFS iterators return the same programs:

\n\n
Set(programs_dfs)==Set(programs_dfs_rightmost)
\n
true
\n\n","category":"page"},{"location":"tutorials/advanced_search/#Stochastic-search","page":"Advanced Search Procedures","title":"Stochastic search","text":"","category":"section"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"
\n

While deterministic search methods explore the search space in a predictable way, stochastic ones introduce randomness to allow for more flexibility.

In this section, we will look at the stochastic search algorithms: Metropolis-Hastings (MH), Very Large Scale Neighbourhood Search (VLSNS), and Simulated Annealing (SA). In Herb.jl, all of these search methodsthe share a common supertype StochasticSearchIterator, which defines the following fields

  • examples

  • cost_function

  • initial_temperature

  • evaluation_function.

They are customized by overriding the functions neighbourhood, propose, accept and temperature as required.

We start with a simple grammar and a helper function to create the input-output examples for the problem we want to solve.

\n\n
g_4 = @csgrammar begin\n    X = |(1:5)\n    X = X * X\n    X = X + X\n    X = X - X\n    X = x\nend
\n
1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n
\n\n
function create_problem(f, range=20)\n    examples = [IOExample(Dict(:x => x), f(x)) for x ∈ 1:range]\n    return Problem(examples), examples\nend
\n
create_problem (generic function with 2 methods)
\n\n\n

Throughout the stochastic search examples, we will use mean-squared-error as cost function. The cost function helps to guide the search by evaluating how well a candidate program solves the given task. This is used to decide whether a proposed program should be accepted or rejected.

\n\n
cost_function = mean_squared_error
\n
mean_squared_error (generic function with 1 method)
\n\n\n

Metropolis-Hastings

Metropolis-Hastings (MH) is a method to produce samples from a distribution that may otherwise be difficult to sample. In the context of program synthesis, we sample from a distribution of programs defined by the grammar.

For more information on MH, see for example this webpage.

To illustrate MH, we use a simple arithmetic example.

\n\n
e_mh = x -> x * x + 4
\n
#7 (generic function with 1 method)
\n\n
problem_mh, examples_mh = create_problem(e_mh)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)])
\n\n\n

Run the following code block to define the iterator and perform the program synthesis multiple times. Since the search process is stochastic, you will likely see different solution programs with each run.

\n\n
begin\n    iterator_mh = MHSearchIterator(g_4, :X, examples_mh, cost_function, max_depth=3) \n    program_mh = synth(problem_mh, iterator_mh)\n    println(\"Sollution using MH: \", program_mh)\nend
\n\n\n\n

Very Large Scale Neighbourhood Search

The second stochastic search method we consider is Very Large Scale Neighbourhood Search (VLSN). In each iteration, the algorithm searches the neighbourhood of the current candidate program for a local optimum, aiming to find a better candidate solution.

For more information, see this article.

Given the same grammar as before, we can try it with some simple examples.

\n\n
e_vlsn = x -> 10
\n
#9 (generic function with 1 method)
\n\n
problem_vlsn1, examples_vlsn1 = create_problem(e_vlsn)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 10), IOExample(Dict{Symbol, Any}(:x => 2), 10), IOExample(Dict{Symbol, Any}(:x => 3), 10), IOExample(Dict{Symbol, Any}(:x => 4), 10), IOExample(Dict{Symbol, Any}(:x => 5), 10), IOExample(Dict{Symbol, Any}(:x => 6), 10), IOExample(Dict{Symbol, Any}(:x => 7), 10), IOExample(Dict{Symbol, Any}(:x => 8), 10), IOExample(Dict{Symbol, Any}(:x => 9), 10), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 10), IOExample(Dict{Symbol, Any}(:x => 12), 10), IOExample(Dict{Symbol, Any}(:x => 13), 10), IOExample(Dict{Symbol, Any}(:x => 14), 10), IOExample(Dict{Symbol, Any}(:x => 15), 10), IOExample(Dict{Symbol, Any}(:x => 16), 10), IOExample(Dict{Symbol, Any}(:x => 17), 10), IOExample(Dict{Symbol, Any}(:x => 18), 10), IOExample(Dict{Symbol, Any}(:x => 19), 10), IOExample(Dict{Symbol, Any}(:x => 20), 10)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 10), IOExample(Dict{Symbol, Any}(:x => 2), 10), IOExample(Dict{Symbol, Any}(:x => 3), 10), IOExample(Dict{Symbol, Any}(:x => 4), 10), IOExample(Dict{Symbol, Any}(:x => 5), 10), IOExample(Dict{Symbol, Any}(:x => 6), 10), IOExample(Dict{Symbol, Any}(:x => 7), 10), IOExample(Dict{Symbol, Any}(:x => 8), 10), IOExample(Dict{Symbol, Any}(:x => 9), 10), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 10), IOExample(Dict{Symbol, Any}(:x => 12), 10), IOExample(Dict{Symbol, Any}(:x => 13), 10), IOExample(Dict{Symbol, Any}(:x => 14), 10), IOExample(Dict{Symbol, Any}(:x => 15), 10), IOExample(Dict{Symbol, Any}(:x => 16), 10), IOExample(Dict{Symbol, Any}(:x => 17), 10), IOExample(Dict{Symbol, Any}(:x => 18), 10), IOExample(Dict{Symbol, Any}(:x => 19), 10), IOExample(Dict{Symbol, Any}(:x => 20), 10)])
\n\n
iterator_vlsn1 = VLSNSearchIterator(g_4, :X, examples_vlsn1, cost_function, max_depth=2) 
\n
VLSNSearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 2), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 10), IOExample(Dict{Symbol, Any}(:x => 2), 10), IOExample(Dict{Symbol, Any}(:x => 3), 10), IOExample(Dict{Symbol, Any}(:x => 4), 10), IOExample(Dict{Symbol, Any}(:x => 5), 10), IOExample(Dict{Symbol, Any}(:x => 6), 10), IOExample(Dict{Symbol, Any}(:x => 7), 10), IOExample(Dict{Symbol, Any}(:x => 8), 10), IOExample(Dict{Symbol, Any}(:x => 9), 10), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 10), IOExample(Dict{Symbol, Any}(:x => 12), 10), IOExample(Dict{Symbol, Any}(:x => 13), 10), IOExample(Dict{Symbol, Any}(:x => 14), 10), IOExample(Dict{Symbol, Any}(:x => 15), 10), IOExample(Dict{Symbol, Any}(:x => 16), 10), IOExample(Dict{Symbol, Any}(:x => 17), 10), IOExample(Dict{Symbol, Any}(:x => 18), 10), IOExample(Dict{Symbol, Any}(:x => 19), 10), IOExample(Dict{Symbol, Any}(:x => 20), 10)], mean_squared_error, 2, 1, execute_on_input)
\n\n
program_vlsn1 = synth(problem_vlsn1, iterator_vlsn1)
\n
(7{5,5}, optimal_program)
\n\n
println(\"Solution: \", program_vlsn1)
\n\n\n
e_vlsn2 = x -> x
\n
#11 (generic function with 1 method)
\n\n
problem_vlsn2, examples_vlsn2 = create_problem(e_vlsn2)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 1), IOExample(Dict{Symbol, Any}(:x => 2), 2), IOExample(Dict{Symbol, Any}(:x => 3), 3), IOExample(Dict{Symbol, Any}(:x => 4), 4), IOExample(Dict{Symbol, Any}(:x => 5), 5), IOExample(Dict{Symbol, Any}(:x => 6), 6), IOExample(Dict{Symbol, Any}(:x => 7), 7), IOExample(Dict{Symbol, Any}(:x => 8), 8), IOExample(Dict{Symbol, Any}(:x => 9), 9), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 11), IOExample(Dict{Symbol, Any}(:x => 12), 12), IOExample(Dict{Symbol, Any}(:x => 13), 13), IOExample(Dict{Symbol, Any}(:x => 14), 14), IOExample(Dict{Symbol, Any}(:x => 15), 15), IOExample(Dict{Symbol, Any}(:x => 16), 16), IOExample(Dict{Symbol, Any}(:x => 17), 17), IOExample(Dict{Symbol, Any}(:x => 18), 18), IOExample(Dict{Symbol, Any}(:x => 19), 19), IOExample(Dict{Symbol, Any}(:x => 20), 20)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 1), IOExample(Dict{Symbol, Any}(:x => 2), 2), IOExample(Dict{Symbol, Any}(:x => 3), 3), IOExample(Dict{Symbol, Any}(:x => 4), 4), IOExample(Dict{Symbol, Any}(:x => 5), 5), IOExample(Dict{Symbol, Any}(:x => 6), 6), IOExample(Dict{Symbol, Any}(:x => 7), 7), IOExample(Dict{Symbol, Any}(:x => 8), 8), IOExample(Dict{Symbol, Any}(:x => 9), 9), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 11), IOExample(Dict{Symbol, Any}(:x => 12), 12), IOExample(Dict{Symbol, Any}(:x => 13), 13), IOExample(Dict{Symbol, Any}(:x => 14), 14), IOExample(Dict{Symbol, Any}(:x => 15), 15), IOExample(Dict{Symbol, Any}(:x => 16), 16), IOExample(Dict{Symbol, Any}(:x => 17), 17), IOExample(Dict{Symbol, Any}(:x => 18), 18), IOExample(Dict{Symbol, Any}(:x => 19), 19), IOExample(Dict{Symbol, Any}(:x => 20), 20)])
\n\n
iterator_vlsn2 = VLSNSearchIterator(g_4, :X, examples_vlsn2, cost_function, max_depth=1) 
\n
VLSNSearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 1), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 1), IOExample(Dict{Symbol, Any}(:x => 2), 2), IOExample(Dict{Symbol, Any}(:x => 3), 3), IOExample(Dict{Symbol, Any}(:x => 4), 4), IOExample(Dict{Symbol, Any}(:x => 5), 5), IOExample(Dict{Symbol, Any}(:x => 6), 6), IOExample(Dict{Symbol, Any}(:x => 7), 7), IOExample(Dict{Symbol, Any}(:x => 8), 8), IOExample(Dict{Symbol, Any}(:x => 9), 9), IOExample(Dict{Symbol, Any}(:x => 10), 10), IOExample(Dict{Symbol, Any}(:x => 11), 11), IOExample(Dict{Symbol, Any}(:x => 12), 12), IOExample(Dict{Symbol, Any}(:x => 13), 13), IOExample(Dict{Symbol, Any}(:x => 14), 14), IOExample(Dict{Symbol, Any}(:x => 15), 15), IOExample(Dict{Symbol, Any}(:x => 16), 16), IOExample(Dict{Symbol, Any}(:x => 17), 17), IOExample(Dict{Symbol, Any}(:x => 18), 18), IOExample(Dict{Symbol, Any}(:x => 19), 19), IOExample(Dict{Symbol, Any}(:x => 20), 20)], mean_squared_error, 2, 1, execute_on_input)
\n\n
program_vlsn2 = synth(problem_vlsn2, iterator_vlsn2)
\n
(9,, optimal_program)
\n\n
println(\"Solution: \", program_vlsn2)
\n\n\n\n

Simulated Annealing

Simulated Annealing (SA) explores smaller, incremental changes to the candidate program in each iteration, gradually refining the solution. It is a variation of the hill-climbing algorithm: Instead of always selecting the best move, SA picks a random move. If the move improves the solution (i.e., the candidate program), it is accepted.

Occasionally, SA will accept a move that worsens the solution. This allows the algorithm to escape local optima and explore more of the solution space. However, this strategy follows a cooling (annealing) schedule: at the beginning (high temperature), the algorithm explores more broadly and is more likely to accept worse solutions. As the temperature decreases, it becomes more selective, accepting worse solutions less often.

For more information, see this page.

\n\n\n

We use the same example as for MH. SA additionally has the option to specify the initial_temperature for the annealing (default initial_temperature=1). Let's see what effect changing the temperature from 1 to 2 has on the solution program.

\n\n
problem_sa, examples_sa = create_problem(e_mh)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)])
\n\n
initial_temperature1 = 1
\n
1
\n\n
iterator_sa1 = SASearchIterator(g_4, :X, examples_sa, cost_function, max_depth=3, initial_temperature = initial_temperature1) 
\n
SASearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 3), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)], mean_squared_error, 1, 0.99, execute_on_input)
\n\n
program_sa1 = synth(problem_sa, iterator_sa1)
\n
(7{6{9,9}4}, optimal_program)
\n\n
initial_temperature2 = 2
\n
2
\n\n
iterator_sa2 = SASearchIterator(g_4, :X, examples_sa, cost_function, max_depth=3, initial_temperature = initial_temperature2) 
\n
SASearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 3), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 5), IOExample(Dict{Symbol, Any}(:x => 2), 8), IOExample(Dict{Symbol, Any}(:x => 3), 13), IOExample(Dict{Symbol, Any}(:x => 4), 20), IOExample(Dict{Symbol, Any}(:x => 5), 29), IOExample(Dict{Symbol, Any}(:x => 6), 40), IOExample(Dict{Symbol, Any}(:x => 7), 53), IOExample(Dict{Symbol, Any}(:x => 8), 68), IOExample(Dict{Symbol, Any}(:x => 9), 85), IOExample(Dict{Symbol, Any}(:x => 10), 104), IOExample(Dict{Symbol, Any}(:x => 11), 125), IOExample(Dict{Symbol, Any}(:x => 12), 148), IOExample(Dict{Symbol, Any}(:x => 13), 173), IOExample(Dict{Symbol, Any}(:x => 14), 200), IOExample(Dict{Symbol, Any}(:x => 15), 229), IOExample(Dict{Symbol, Any}(:x => 16), 260), IOExample(Dict{Symbol, Any}(:x => 17), 293), IOExample(Dict{Symbol, Any}(:x => 18), 328), IOExample(Dict{Symbol, Any}(:x => 19), 365), IOExample(Dict{Symbol, Any}(:x => 20), 404)], mean_squared_error, 2, 0.99, execute_on_input)
\n\n","category":"page"},{"location":"tutorials/advanced_search/#Genetic-Search","page":"Advanced Search Procedures","title":"Genetic Search","text":"","category":"section"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"
\n

Genetic search is a type of evolutionary algorithm, which simulates the process of natural selection. It evolves a population of candidate programs through operations like mutation, crossover (recombination), and selection. Then, the fitness of each program is assessed (i.e., how well it satisfies the given specifications). Only the 'fittest' programs are selected for the next generation, thus gradually refining the population of candidate programs.

For more information, see here.

We show the example of finding a lambda function. Try varying the parameters of the genetic search to see what happens.

\n\n
e_gs = x -> 3 * x * x + (x + 2)
\n
#13 (generic function with 1 method)
\n\n
problem_gs, examples_gs = create_problem(e_gs)
\n
(Problem{Vector{IOExample}}(\"\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 6), IOExample(Dict{Symbol, Any}(:x => 2), 16), IOExample(Dict{Symbol, Any}(:x => 3), 32), IOExample(Dict{Symbol, Any}(:x => 4), 54), IOExample(Dict{Symbol, Any}(:x => 5), 82), IOExample(Dict{Symbol, Any}(:x => 6), 116), IOExample(Dict{Symbol, Any}(:x => 7), 156), IOExample(Dict{Symbol, Any}(:x => 8), 202), IOExample(Dict{Symbol, Any}(:x => 9), 254), IOExample(Dict{Symbol, Any}(:x => 10), 312), IOExample(Dict{Symbol, Any}(:x => 11), 376), IOExample(Dict{Symbol, Any}(:x => 12), 446), IOExample(Dict{Symbol, Any}(:x => 13), 522), IOExample(Dict{Symbol, Any}(:x => 14), 604), IOExample(Dict{Symbol, Any}(:x => 15), 692), IOExample(Dict{Symbol, Any}(:x => 16), 786), IOExample(Dict{Symbol, Any}(:x => 17), 886), IOExample(Dict{Symbol, Any}(:x => 18), 992), IOExample(Dict{Symbol, Any}(:x => 19), 1104), IOExample(Dict{Symbol, Any}(:x => 20), 1222)]), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 6), IOExample(Dict{Symbol, Any}(:x => 2), 16), IOExample(Dict{Symbol, Any}(:x => 3), 32), IOExample(Dict{Symbol, Any}(:x => 4), 54), IOExample(Dict{Symbol, Any}(:x => 5), 82), IOExample(Dict{Symbol, Any}(:x => 6), 116), IOExample(Dict{Symbol, Any}(:x => 7), 156), IOExample(Dict{Symbol, Any}(:x => 8), 202), IOExample(Dict{Symbol, Any}(:x => 9), 254), IOExample(Dict{Symbol, Any}(:x => 10), 312), IOExample(Dict{Symbol, Any}(:x => 11), 376), IOExample(Dict{Symbol, Any}(:x => 12), 446), IOExample(Dict{Symbol, Any}(:x => 13), 522), IOExample(Dict{Symbol, Any}(:x => 14), 604), IOExample(Dict{Symbol, Any}(:x => 15), 692), IOExample(Dict{Symbol, Any}(:x => 16), 786), IOExample(Dict{Symbol, Any}(:x => 17), 886), IOExample(Dict{Symbol, Any}(:x => 18), 992), IOExample(Dict{Symbol, Any}(:x => 19), 1104), IOExample(Dict{Symbol, Any}(:x => 20), 1222)])
\n\n
iterator_gs = GeneticSearchIterator(g_4, :X, examples_gs, population_size = 10, mutation_probability = 0.8, maximum_initial_population_depth = 3) 
\n
GeneticSearchIterator(GenericSolver(1: X = 1\n2: X = 2\n3: X = 3\n4: X = 4\n5: X = 5\n6: X = X * X\n7: X = X + X\n8: X = X - X\n9: X = x\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 9223372036854775807), IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 6), IOExample(Dict{Symbol, Any}(:x => 2), 16), IOExample(Dict{Symbol, Any}(:x => 3), 32), IOExample(Dict{Symbol, Any}(:x => 4), 54), IOExample(Dict{Symbol, Any}(:x => 5), 82), IOExample(Dict{Symbol, Any}(:x => 6), 116), IOExample(Dict{Symbol, Any}(:x => 7), 156), IOExample(Dict{Symbol, Any}(:x => 8), 202), IOExample(Dict{Symbol, Any}(:x => 9), 254), IOExample(Dict{Symbol, Any}(:x => 10), 312), IOExample(Dict{Symbol, Any}(:x => 11), 376), IOExample(Dict{Symbol, Any}(:x => 12), 446), IOExample(Dict{Symbol, Any}(:x => 13), 522), IOExample(Dict{Symbol, Any}(:x => 14), 604), IOExample(Dict{Symbol, Any}(:x => 15), 692), IOExample(Dict{Symbol, Any}(:x => 16), 786), IOExample(Dict{Symbol, Any}(:x => 17), 886), IOExample(Dict{Symbol, Any}(:x => 18), 992), IOExample(Dict{Symbol, Any}(:x => 19), 1104), IOExample(Dict{Symbol, Any}(:x => 20), 1222)], execute_on_input, 10, 0.8, 3)
\n\n
program_gs, error_gs = synth(problem_gs, iterator_gs)
\n
(7{2,7{6{9,7{7{9,9}9}}9}}, optimal_program)
\n\n","category":"page"},{"location":"tutorials/advanced_search/","page":"Advanced Search Procedures","title":"Advanced Search Procedures","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/advanced_search.jl\"","category":"page"},{"location":"tutorials/getting_started_with_constraints/","page":"Getting started with Constraints","title":"Getting started with Constraints","text":"\n\n\n\n\n

Getting started with HerbConstraints

When enumerating programs using a grammar, we will encounter many redundant programs. For example, x, -(-x) and 1 * x are syntactically different programs, but they have the same semantics. Grammar constraints aim to speed up synthesis by eliminating such redundant programs and thereby reducing the size of the program space.

\n\n\n

Setup

For this tutorial, we need to import the following modules of the Herb.jl framework:

  • HerbCore for the necessary data strucutes, like Holes and RuleNodes

  • HerbGrammar to define the grammar

  • HerbConstraints to define the constraints

  • HerbSearch to execute a constrained enumeration

We will also redefine the simple arithmetic grammar from the previous tutorial.

\n\n
using HerbCore, HerbGrammar, HerbConstraints, HerbSearch
\n\n\n
grammar = @csgrammar begin\n    Int = 1\n    Int = x\n    Int = - Int\n    Int = Int + Int\n    Int = Int * Int\nend
\n
1: Int = 1\n2: Int = x\n3: Int = -Int\n4: Int = Int + Int\n5: Int = Int * Int\n
\n\n\n

Working with constraints

To show the effects of constraints, we will first enumerate all programs without constraints (up to a maximum size of 3 AST nodes).

(To make sure the grammar doesn't have any constraints, we can clear the constraints using clearconstraints!. This is not needed at this point, but could come in handy if your REPL holds a reference to a constrained version of the grammar)

\n\n
begin\n    clearconstraints!(grammar)\n    iter_1 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_1\n        println(rulenode2expr(program, grammar))\n    end\n    \nend
\n\n\n\n

Upon inspection, we can already see some redundant programs, like 1 * 1 and -(-1). To eliminate these redundant programs, we will set up some constraints that prevent these patterns from appearing. Then we will create another iteratator to enumerate all programs that satisfy the defined grammar constraints.

To make the forbidden pattern constraint general, we will use a special type of rulenode: VarNode(:A). This node matches with any subtree and can be used to forbid multiple forbidden patterns using a single constraint. For example, Forbidden(RuleNode(minus, [RuleNode(minus, [VarNode(:A)])]))) forbids:

  • -(-1)

  • -(-X)

  • -(-(1 + 1))

  • 1 + -(-(1 + 1))

  • etc

\n\n
begin\n    one = 1\n    x = 2\n    minus = 3\n    plus = 4\n    times = 5\n    \n    addconstraint!(grammar, Forbidden(RuleNode(times, [RuleNode(one), VarNode(:A)])))        # forbid 1*A\n    addconstraint!(grammar, Forbidden(RuleNode(minus, [RuleNode(minus, [VarNode(:A)])])))    # forbid -(-A)\n    \n    iter_2 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_2\n        println(rulenode2expr(program, grammar))\n    end\nend
\n\n\n\n

Forbidden Constraint

The Forbidden constraint forbids any subtree in the program that matches a given template tree. Such a template tree can consist of 3 node types:

  • RuleNode(1). Matches exactly the given rule.

  • DomainRuleNode(BitVector((0, 0, 0, 1, 1)), children). Matches any rule in its bitvector domain. In this case, rule 4 and 5.

  • VarNode(:A). Matches any subtree. If another VarNode of the same name is used, the subtrees have to be the same.

\n\n
begin\n    #this constraint forbids A+A and A*A\n    constraint_1 = Forbidden(DomainRuleNode(BitVector((0, 0, 0, 1, 1)), [VarNode(:A), VarNode(:A)]))\n    \n    # Without this constraint, we encounter 154 programs\n    clearconstraints!(grammar)\n    iter_3 = BFSIterator(grammar, :Int, max_size=5)\n    println(length(iter_3))\n    \n    # With this constraint, we encounter 106 programs\n    clearconstraints!(grammar)\n    addconstraint!(grammar, constraint_1)\n    iter_4 = BFSIterator(grammar, :Int, max_size=5)\n    println(length(iter_4))\n    \nend
\n\n\n\n

Contains Constraint

The Contains constraint enforces that a given rule appears in the program tree at least once.

In the arithmetic grammar, this constraint can be used to ensure the input symbol x is used in the program. Otherwise, the program is just a constant.

\n\n
begin\n    clearconstraints!(grammar)\n    addconstraint!(grammar, Contains(2)) #rule 2 should be used in the program\n    iter_5 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_5\n        println(rulenode2expr(program, grammar))\n    end\nend
\n\n\n\n

Contains Subtree Constraint

Similarly to the Contains constraint, the ContainsSubtree can be used to enforce a given template tree is used in the program at least once.

\n\n
begin\n    clearconstraints!(grammar)\n    addconstraint!(grammar, ContainsSubtree(RuleNode(times, [RuleNode(x), RuleNode(x)]))) #x*x should be in the program tree\n    iter_6 = BFSIterator(grammar, :Int, max_size=4)\n    \n    for program ∈ iter_6\n        println(rulenode2expr(program, grammar))\n    end\nend
\n\n\n\n

Ordered Constraint

The Ordered constraint enforces an <= ordering on a provided list of variables. With this constraint, we can break symmetries based on commutativity. For example, 1+x and x+1 are semantically equivalent. By imposing an Ordered constraint, we can eliminate one of the symmetric variants.

To define an Ordered constraint, we need to provide it with a template tree including at least two differently named VarNodes. And additionally, an ordering of the variables in the tree.

In the upcoming example we will set up a template tree representing a+b and a*b. Then, we will impose an ordering a<=b on all the subtrees that match the template.

The result is that our iterator skips the redundant programs x+1 and x*1, as they are already represented by 1+x and 1*x.

\n\n
begin\n    clearconstraints!(grammar)\n    \n    template_tree = DomainRuleNode(BitVector((0, 0, 0, 1, 1)), [VarNode(:a), VarNode(:b)])\n    order = [:a, :b]\n    \n    addconstraint!(grammar, Ordered(template_tree, order))\n    iter_7 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_7\n        println(rulenode2expr(program, grammar))\n    end\n    \nend
\n\n\n\n

Forbidden Sequence Constraint

The ForbiddenSequence constraints forbids a given sequence of rule nodes in a vertical path of the tree.

An optional second argument, ignore_if, can be used to overrule the constraint in case any of the rules on the ignore_if list are present.

Below we will define the constraint ForbiddenSequence([plus, one], ignore_if=[times]). It forbids an 1 after an + unless an * disrupts the sequence.

This constraint will forbid the following programs:

  • x + 1

  • x + -1

  • x + -(-1)

  • x + (x + 1)

  • x * (x + 1)

But it will allow the following program (as * disrupts the sequence):

  • x + (x * 1)

\n\n
begin\n    constraint_2 = ForbiddenSequence([plus, one], ignore_if=[times])\n    addconstraint!(grammar, constraint_2)\n    iter_8 = BFSIterator(grammar, :Int, max_size=3)\n    \n    for program ∈ iter_8\n        println(rulenode2expr(program, grammar))\n    end\n    \nend
\n\n\n\n

Custom Constraint

To implement a new constraint, we need to define two structs: an AbstractGrammarConstraint and an AbstractLocalConstraint.

A grammar constraint is a high-level constraint on the grammar itself and does not refer to a location in the tree. For example, the Forbidden constraint is responsible for forbidding a template tree everywhere in the tree. To divide the work of constraint propagation, the grammar constraint will post several local constraints that are responsible for propagating the constraint at each particular location.

A local constraint is a rooted version of a grammar constraint. Each local constraint holds a path field that points to a location in the tree where this constraint applies.

\n\n\n

Suppose we want to implement a simple custom constraint that forbids a given rule twice in a row.

Each time a new AST node is added to a tree, the on_new_node function is called to notify that an unseen node has been added to the tree at path path. Our grammar constraint has the opportunity to react to this event. In this example, we will post a new local constraint at the new location using the post! function.

(Don't worry about the HerbConstraints. prefixes. Normally, constraints are defined within the HerbConstraints repository, so there is no need to specify the namespace)

\n\n
begin\n    \"\"\"\n    Forbids the consecutive application of the specified rule.\n    For example, CustomConstraint(4) forbids the tree 4(1, 4(1, 1)) as it applies rule 4 twice in a row.\n    \"\"\"\n    struct ForbidConsecutive <: AbstractGrammarConstraint\n        rule::Int\n    end\n    \n    \"\"\"\n    Post a local constraint on each new node that appears in the tree\n    \"\"\"\n    function HerbConstraints.on_new_node(solver::Solver, constraint::ForbidConsecutive, path::Vector{Int})\n        HerbConstraints.post!(solver, LocalForbidConsecutive(path, constraint.rule))\n    end\nend
\n\n\n\n

Next, we will define our local constraint. This constraint is responsible for propagating the constraint at a given path. The propagate! method can use several solver functions to manipulate the tree. The following tree manipulations can be used to remove rules from the domain of a hole at a given path:

  • remove!(solver::Solver, path::Vector{Int}, rule_index::Int)

  • remove!(solver::Solver, path::Vector{Int}, rules::Vector{Int})

  • remove_all_but!(solver::Solver, path::Vector{Int}, new_domain::BitVector)

  • remove_above!(solver::Solver, path::Vector{Int}, rule_index::Int)

  • remove_below!(solver::Solver, path::Vector{Int}, rule_index::Int)

  • make_equal!(solver::Solver, node1::AbstractRuleNode, node2::AbstractRuleNode) (a high level manipulation that requires node1 and node2 to be in the tree)

In addition to tree manipulations, the following solver functions can be used to communicate new information to the solver:

  • set_infeasible!(solver). If a propagator detects an inconsistency, the solver should be notified and cancel any other scheduled propagators.

  • deactivate!(solver, constraint). If a constraint is satisfied, it should deactivate itself to prevent re-propagation.

  • post!(solver, constraint) A constraint is allowed to post new local constraints. This might be helpful if a constraint can be reduced to a smaller constraint.

The solver manages all constraints and the program tree we propagate on. Applying tree manipulations might cause a chain reaction of other propagators, so the shape of the tree might update as we propagate. The get the latest information about the tree, we should use the following getter functions:

  • get_tree(solver) returns the root node of the current (partial) program tree

  • isfeasible(solver) returns the a flag indicating if the solver is not violating any (other) constriants.

  • get_path(solver, node) returns the path at which the node is located.

  • get_node_at_location(solver, path) returns the node that is currently at the given path (be aware that this instance might be replaced by manipulations).

  • get_hole_at_location(solver, path) same as get node at location, but asserts the node is a hole (domain size >= 2).

To get information about a node, we can use the following getter functions:

  • isfilled(node). Returns true if the node is a RuleNode or has domain size 1.

  • get_rule(node). Get the rule of a filled node.

  • get_children(node). Get the children of a node.

  • node.domain[rule]. Given the node is a hole, return true if rule is in the domain.

Finally, another useful function for propagators is pattern_match(node1, node2). This function compares two trees and returns a PatternMatchResult that indicates if the nodes match, and potentially indicate which holes need to be filled to complete the match.

\n\n
begin\n    \"\"\"\n    Forbids the consecutive application of the specified rule at path `path`.\n    \"\"\"\n    struct LocalForbidConsecutive <: AbstractLocalConstraint\n        path::Vector{Int}\n        rule::Int\n    end\n    \n    \"\"\"\n    Propagates the constraints by preventing a consecutive application of the specified rule.\n    \"\"\"\n    function HerbConstraints.propagate!(solver::Solver, constraint::LocalForbidConsecutive)\n        node = get_node_at_location(solver, constraint.path)\n        if isfilled(node)\n            if get_rule(node) == constraint.rule\n                #the specified rule is used, make sure the rule will not be used by any of the children\n                for (i, child) ∈ enumerate(get_children(node))\n                    if isfilled(child)\n                        if get_rule(child) == constraint.rule\n                            #the specified rule was used twice in a row, which is violating the constraint\n                            set_infeasible!(solver)\n                            return\n                        end\n                    elseif child.domain[constraint.rule]\n                        child_path = push!(copy(constraint.path), i)\n                        remove!(solver, child_path, constraint.rule) # remove the rule from the domain of the child\n                    end\n                end\n            end\n        elseif node.domain[constraint.rule]\n            #our node is a hole with the specified rule in its domain\n            #we will now check if any of the children already uses the specified rule\n            softfail = false\n            for (i, child) ∈ enumerate(get_children(node))\n                if isfilled(child)\n                    if get_rule(child) == constraint.rule\n                        #the child holds the specified rule, so the parent cannot have this rule\n                        remove!(solver, constraint.path, constraint.rule)\n                    end\n                elseif child.domain[constraint.rule]\n                    #the child is a hole and contains the specified node. since there are 2 holes involved, we will softfail.\n                    softfail = true\n                end\n            end\n            if softfail\n                #we cannot deactivate the constraint, because it needs to be repropagated\n                return\n            end\n        end\n    \n        #the constraint is satisfied and can be deactivated\n        HerbConstraints.deactivate!(solver, constraint)\n    end\nend
\n\n\n\n

Posting a local constraint will trigger the initial propagation. To re-propagate, the constraint needs to be rescheduled for propagation.

Whenever the tree is manipulated, we will make a shouldschedule check to see if our constraint needs to be rescheduled for propagation based on the manipulation.

In our case, we want to repropagate if either:

  • a tree manipulation occured at the constraint.path

  • a tree manipulation occured at the child of the constraint.path

\n\n
\n\"\"\"\nGets called whenever an tree manipulation occurs at the given `path`.\nReturns true iff the `constraint` should be rescheduled for propagation.\n\"\"\"\nfunction HerbConstraints.shouldschedule(solver::Solver, constraint::LocalForbidConsecutive, path::Vector{Int})::Bool\n    return (path == constraint.path) || (path == constraint.path[1:end-1])\nend\n
\n\n\n\n

With all the components implemented, we can do a constrained enumeration using our new ForbidConsecutive constraint.

\n\n
begin\n    clearconstraints!(grammar)\n    \n    addconstraint!(grammar, ForbidConsecutive(minus))\n    addconstraint!(grammar, ForbidConsecutive(plus))\n    addconstraint!(grammar, ForbidConsecutive(times))\n    \n    iter = BFSIterator(grammar, :Int, max_size=6)\n    \n    for program ∈ iter\n        println(rulenode2expr(program, grammar))\n    end\nend
\n\n\n","category":"page"},{"location":"tutorials/getting_started_with_constraints/","page":"Getting started with Constraints","title":"Getting started with Constraints","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/getting_started_with_constraints.jl\"","category":"page"},{"location":"HerbInterpret/#HerbInterpret_docs","page":"HerbInterpret.jl","title":"HerbInterpret.jl Documentation","text":"","category":"section"},{"location":"HerbInterpret/","page":"HerbInterpret.jl","title":"HerbInterpret.jl","text":"CurrentModule=HerbInterpret","category":"page"},{"location":"HerbInterpret/","page":"HerbInterpret.jl","title":"HerbInterpret.jl","text":"Modules = [HerbInterpret]\nOrder = [:type, :const, :macro, :function]","category":"page"},{"location":"HerbInterpret/#HerbInterpret.evaluate_program-Tuple{RuleNode, Vector{<:IOExample}, AbstractGrammar, Function}","page":"HerbInterpret.jl","title":"HerbInterpret.evaluate_program","text":"evaluate_program(program::RuleNode, examples::Vector{<:IOExample}, grammar::AbstractGrammar, evaluation_function::Function)\n\nRuns a program on the examples and returns tuples of actual desired output and the program's output\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.execute_on_input-Union{Tuple{T}, Tuple{AbstractGrammar, RuleNode, Dict{Symbol, T}}} where T","page":"HerbInterpret.jl","title":"HerbInterpret.execute_on_input","text":"execute_on_input(grammar::AbstractGrammar, program::RuleNode, input::Dict{Symbol, T})::Any where T\n\nConverts a RuleNode program into an expression using a given grammar, then evaluates this expression with a single input dictionary input and a symbol table derived from the grammar using execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T}).\n\nArguments\n\ngrammar::AbstractGrammar: A grammar object used to convert the RuleNode into an executable expression.\nprogram::RuleNode: The program, represented as a RuleNode, to be converted and evaluated.\ninput::Dict{Symbol, T}: A dictionary providing input values for symbols used in the generated expression.\n\nReturns\n\nAny: The result of evaluating the generated expression with the given input dictionary.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.execute_on_input-Union{Tuple{T}, Tuple{AbstractGrammar, RuleNode, Vector{T}}} where T<:(Dict{Symbol})","page":"HerbInterpret.jl","title":"HerbInterpret.execute_on_input","text":"execute_on_input(grammar::AbstractGrammar, program::RuleNode, input::Vector{T})::Vector{Any} where T <: Dict{Symbol, <:Any}\n\nConverts a RuleNode program into an expression using a given grammar, then evaluates this expression for each input dictionary in a vector input and a symbol table derived from the grammar using execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T}).\n\nArguments\n\ngrammar::AbstractGrammar: A grammar object used to convert the RuleNode into an executable expression.\nprogram::RuleNode: The program, represented as a RuleNode, to be converted and evaluated.\ninput::Vector{T}: A vector of dictionaries, each providing input values for symbols used in the generated expression.\n\nReturns\n\nVector{Any}: A vector containing the results of evaluating the generated expression for each input dictionary.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.execute_on_input-Union{Tuple{T}, Tuple{Dict{Symbol, Any}, Any, Dict{Symbol, T}}} where T","page":"HerbInterpret.jl","title":"HerbInterpret.execute_on_input","text":"execute_on_input(tab::SymbolTable, expr::Any, input::Dict{Symbol, T})::Any where T\n\nEvaluates an expression expr within the context of a symbol table tab and a single input dictionary input. The input dictionary keys should match the symbols used in the expression, and their values are used during the expression's evaluation.\n\nArguments\n\ntab::SymbolTable: A symbol table containing predefined symbols and their associated values or functions.\nexpr::Any: The expression to be evaluated. Can be any Julia expression that is valid within the context of the provided symbol table and input.\ninput::Dict{Symbol, T}: A dictionary where each key is a symbol used in the expression, and the value is the corresponding value to be used in the expression's evaluation. The type T can be any type.\n\nReturns\n\nAny: The result of evaluating the expression with the given symbol table and input dictionary.\n\nwarning: Warning\nThis function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.execute_on_input-Union{Tuple{T}, Tuple{Dict{Symbol, Any}, Any, Vector{T}}} where T<:(Dict{Symbol})","page":"HerbInterpret.jl","title":"HerbInterpret.execute_on_input","text":"execute_on_input(tab::SymbolTable, expr::Any, input::Vector{T})::Vector{<:Any} where T <: Dict{Symbol, <:Any}\n\nWrapper around execute_on_input to execute all inputs given as an array.\n\nArguments\n\ntab::SymbolTable: A symbol table containing predefined symbols and their associated values or functions.\nexpr::Any: The expression to be evaluated for each input dictionary.\ninputs::Vector{T}: A vector of dictionaries, each serving as an individual set of inputs for the expression's evaluation.\n\nReturns\n\nVector{<:Any}: A vector containing the results of evaluating the expression for each input dictionary.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.interpret-Tuple{Dict{Symbol, Any}, Any}","page":"HerbInterpret.jl","title":"HerbInterpret.interpret","text":"interpret(tab::SymbolTable, ex::Expr)\n\nEvaluates an expression without compiling it. Uses AST and symbol lookups. Only supports :call and :(=) expressions at the moment.\n\nExample usage:\n\ntab = SymbolTable(:f => f, :x => x)\nex = :(f(x))\ninterpret(tab, ex)\n\nWARNING: This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.test_all_examples-Tuple{Dict{Symbol, Any}, Any, Vector{IOExample}}","page":"HerbInterpret.jl","title":"HerbInterpret.test_all_examples","text":"test_all_examples(tab::SymbolTable, expr::Any, examples::Vector{IOExample})::Vector{Bool}\n\nwarning: Warning\nThis function is deprecated. Please use execute_on_input instead.\n\nRuns the interpreter on all examples with the given input table and expression. The symbol table defines everything (functions, symbols) that are not input variables to the program to be synthesised. Returns a list of true/false values indicating if the expression satisfies the corresponding example. WARNING: This function throws exceptions that are caused in the given expression. These exceptions have to be handled by the caller of this function.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#HerbInterpret.test_examples-Tuple{Dict{Symbol, Any}, Any, Vector{IOExample}}","page":"HerbInterpret.jl","title":"HerbInterpret.test_examples","text":"test_examples(tab::SymbolTable, expr::Any, examples::Vector{IOExample})::Bool\n\nwarning: Warning\nThis function is deprecated. Please use execute_on_input instead.\n\nEvaluates all examples and returns true iff all examples pass. Shortcircuits as soon as an example is found for which the program doesn't work. Returns false if one of the examples produces an error.\n\n\n\n\n\n","category":"method"},{"location":"HerbInterpret/#Index","page":"HerbInterpret.jl","title":"Index","text":"","category":"section"},{"location":"HerbInterpret/","page":"HerbInterpret.jl","title":"HerbInterpret.jl","text":"","category":"page"},{"location":"tutorials/getting_started_with_herb/","page":"A more verbose getting started with Herb.jl","title":"A more verbose getting started with Herb.jl","text":"\n\n\n\n\n

Search

This notebook describes how you can search a program space as defined by a grammar. Specifically, we will look at example-based search, where the goal is to find a program that is able to transform the inputs of every example to the corresponding output.

\n\n\n

Setup

First, we start with the setup. We need access to all the function in the Herb.jl framework.

\n\n
using HerbGrammar, HerbSpecification, HerbSearch, HerbInterpret, HerbConstraints
\n\n\n\n

Defining the program space

Next, we start by creating a grammar. We define a context-free grammar as a HerbGrammar.ContextSpecificGrammar without any constraints. A context-free grammar is just a simple set of production rules for defining combinations of terminal symbols (in our case integers).

Alternatively we could define a context-sensitive grammar, when the production rules only hold in a certain context. For more information on this, please see our tutorial on defining grammars.

For now, we specify a simple grammar (using the @csgrammar macro) for dealing with integers and explain all the rules individually:

  1. First, we specify our number values and constrain them to being positive even integers.

  2. Then, we can also use the variable x to hold an integer.

  3. The third rule determines we can add two integers.

  4. The fourth rule determines we can subtract an integer from another.

  5. Finally, we also allow the multiplication of two integers.

If you run this cell, you can see all the rules rolled out.

\n\n
g = HerbGrammar.@csgrammar begin\n    Number = 0|2|4|6|8\n    Number = x\n    Number = Number + Number\n    Number = Number - Number\n    Number = Number * Number\nend
\n
1: Number = 0\n2: Number = 2\n3: Number = 4\n4: Number = 6\n5: Number = 8\n6: Number = x\n7: Number = Number + Number\n8: Number = Number - Number\n9: Number = Number * Number\n
\n\n\n

Defining the problem

\n\n\n

As mentioned before, we are looking at example-based search. This means that the problem is defined by a set of input-output examples. A single example hence consists of an input and an output. The input is defined as a dictionary, with a value assigned to each variable in the grammar. It is important to write the variable name as a Symbol instead of a string. A Symbol in Julia is written with a colon prefix, i.e. :x. The output of the input-output example is just a single value for this specific grammar, but could possibly relate to e.g. arrays of values, too.

In the cell below we automatically generate some examples for x assigning values 1-5.

\n\n
# Create input-output examples\nexamples = [HerbSpecification.IOExample(Dict(:x => x), 4x + 6) for x ∈ 1:5]
\n
5-element Vector{IOExample}:\n IOExample(Dict{Symbol, Any}(:x => 1), 10)\n IOExample(Dict{Symbol, Any}(:x => 2), 14)\n IOExample(Dict{Symbol, Any}(:x => 3), 18)\n IOExample(Dict{Symbol, Any}(:x => 4), 22)\n IOExample(Dict{Symbol, Any}(:x => 5), 26)
\n\n\n

Now that we have some input-output examples, we can define the problem. Next to the examples, a problem also contains a name meant to link to the file path, which can be used to keep track of current examples. For now, this is irrelevant, and you can give the program any name you like.

\n\n
problem_1 = HerbSpecification.Problem(\"example\", examples)
\n
Problem{Vector{IOExample}}(\"example\", IOExample[IOExample(Dict{Symbol, Any}(:x => 1), 10), IOExample(Dict{Symbol, Any}(:x => 2), 14), IOExample(Dict{Symbol, Any}(:x => 3), 18), IOExample(Dict{Symbol, Any}(:x => 4), 22), IOExample(Dict{Symbol, Any}(:x => 5), 26)])
\n\n\n

Searching

Now that we have defined the search space and the goal of the search, we can start the search.

Of course, our problem is underdefined as there might be multiple programs that satisfy our examples. Let us consider the case where we also have a ternary if-then-else operator and standard boolean operators in our grammar: we could synthesize the program x ≤ 5 ? 3x+5 : 0. This program satisfies all our examples, but we don't expect it to generalize very well.

To search through a program space, we first need to define a HerbSearch.ProgramIterator, which can be instantiated with different iterators, for now we use a simple HerbSearch.BFSIterator. For more advanced search methods check out our tutorial on advanced search. For more information about iterators, check out our tutorial on working with interpreters.

In general, we assume that a smaller program is more general than a larger program. Therefore, we search for the smallest program in our grammar that satisfies our examples. This can be done using a breadth-first search over the program/search space.

This search is very basic; it makes use of an enumeration technique, where we enumerate programs one-by-one until we find a program that matches our examples. The search procedure has a built-in default evaluator to verify the candidate programs with the given input. The search procedure also has a built-in search procedure using breadth-first search.

So, we only need to give our grammar and the problem to our search procedure, along with a starting Symbol, in our case a Number.

\n\n
iterator_1 = BFSIterator(g, :Number)
\n
BFSIterator(GenericSolver(1: Number = 0\n2: Number = 2\n3: Number = 4\n4: Number = 6\n5: Number = 8\n6: Number = x\n7: Number = Number + Number\n8: Number = Number - Number\n9: Number = Number * Number\n, SolverState(hole[Bool[1, 1, 1, 1, 1, 1, 1, 1, 1]],, Set{AbstractLocalConstraint}(), true), DataStructures.PriorityQueue{AbstractLocalConstraint, Int64, Base.Order.ForwardOrdering}(), nothing, true, false, 9223372036854775807, 9223372036854775807))
\n\n
synth(problem_1, iterator_1)
\n
(7{9{6,3}4}, optimal_program)
\n\n\n

As you can see, the search procedure found the correct program!

\n\n\n

Defining the search procedure

In the previous case, we used the built-ins of the search procedure. However, we can also give a custom enumerator to the search procedure and define a few more values.

We first define a new problem to test with, we are looking for the programs that can compute the value 168. We immediately pass the examples to the problem and then set up the new search.

Search is done by passing the grammar, the problem and the starting point like before. We now also specify the enumeration function to be used, and now we use depth-first search. Then, we give the maximum depth of the programs we want to search for (3), the maximum number of nodes in the Abstract Syntax Tree that exists during search (10), and the maximum time in seconds allowed for the search.

\n\n
begin\n    problem_2 = HerbSpecification.Problem(\"example2\", [HerbSpecification.IOExample(Dict(:x => x), 168) for x ∈ 1:5])\n    iterator_2 = HerbSearch.BFSIterator(g, :Number, max_depth=4, max_size=30)\n    expr_2, flag_2 = HerbSearch.synth(problem_2, iterator_2)\n    print(expr_2)\n    program_2 = rulenode2expr(expr_2, g)\n    println(program_2)\nend
\n\n\n\n

We see that our synthesizer can find a program to construct the value 168, though a fun experiment would be trying to get the value 167, what do you think would happen? You can try below, using the same iterator.

In any case, this concludes our first introduction to the Herb.jl program synthesis framework. You can see more examples in this repository, or explore yourself. Enjoy!

\n\n
begin\n    problem_3 = HerbSpecification.Problem(\"example3\", [HerbSpecification.IOExample(Dict(:x => x), 167) for x ∈ 1:5])\n    expr_3, flag_3 = HerbSearch.synth(problem_3, iterator_2)\n    print(expr_3)\n    program_3 = rulenode2expr(expr_3, g)\n    println(program_3)\nend
\n\n\n","category":"page"},{"location":"tutorials/getting_started_with_herb/","page":"A more verbose getting started with Herb.jl","title":"A more verbose getting started with Herb.jl","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/getting_started_with_herb.jl\"","category":"page"},{"location":"get_started/#Getting-Started","page":"Getting Started","title":"Getting Started","text":"","category":"section"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"You can either paste this code into the Julia REPL or into a seperate file, e.g. get_started.jl. If using a separate file you can execute using julia get_started.jl or julia --project=. get_started.jl depending on whether you installed Herb.jl globally or in a project.","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"To begin, we need to import all needed packages using","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"using HerbGrammar, HerbSpecification, HerbSearch, HerbInterpret","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"To define a program synthesis problem, we need a grammar and specification. ","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"First, a grammar can be constructed using the @csgrammar macro included in HerbGrammar. ","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"Here, we describe a simple integer arithmetic example, that can add and multiply an input variable x or the integers 1,2, using","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"g = @csgrammar begin\n Number = |(1:2)\n Number = x\n Number = Number + Number\n Number = Number * Number\nend","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"Second, the problem specification can be provided using e.g. input/output examples using HerbSpecification. Inputs are provided as a Dict assigning values to variables, and outputs as arbitrary values. The problem itself is then a list of IOExamples using","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"problem = Problem([IOExample(Dict(:x => x), 2x+1) for x ∈ 1:5])","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"The problem is given now, let us search for a solution with HerbSearch. For now we will just use the default parameters searching for a satisfying program over the grammar, given the problem and a starting symbol using","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"iterator = BFSIterator(g, :Number, max_depth=5)\nsolution, flag = synth(problem, iterator)\nprintln(solution)","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"There are various ways to adapt the search technique to your needs. Please have a look at the synth documentation.","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"Eventually, we want to test our solution on some other inputs using HerbInterpret. We transform our grammar g to a Julia expression with Symboltable(g), add our solution and the input, assigning the value 6 to the variable x.","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"program = rulenode2expr(solution, g) # should yield 2*6+1\nprintln(program)\n\noutput = execute_on_input(SymbolTable(g), program, Dict(:x => 6)) \nprintln(output)","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"If you run the completed code it will output both the generated Julia expression and the result from assigning value.","category":"page"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"Just like that we tackled (almost) all modules of Herb.jl.","category":"page"},{"location":"get_started/#Where-to-go-from-here?","page":"Getting Started","title":"Where to go from here?","text":"","category":"section"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"See our other tutorials!","category":"page"},{"location":"get_started/#The-full-code-example","page":"Getting Started","title":"The full code example","text":"","category":"section"},{"location":"get_started/","page":"Getting Started","title":"Getting Started","text":"using HerbSearch, HerbSpecification, HerbInterpret, HerbGrammar\n\n# define our very simple context-free grammar\n# Can add and multiply an input variable x or the integers 1,2.\ng = @csgrammar begin\n Number = |(1:2)\n Number = x\n Number = Number + Number\n Number = Number * Number\nend\n\nproblem = Problem([IOExample(Dict(:x => x), 2x+1) for x ∈ 1:5])\niterator = BFSIterator(g, :Number, max_depth=5)\n\nsolution, flag = synth(problem, iterator)\nprogram = rulenode2expr(solution, g) # should yield 2*6 +1 \nprintln(program)\n\noutput = execute_on_input(SymbolTable(g), program, Dict(:x => 6)) \nprintln(output)\n","category":"page"},{"location":"tutorials/defining_grammars/","page":"Defining Grammars in Herb.jl","title":"Defining Grammars in Herb.jl","text":"\n\n\n\n\n

Defining Grammars in Herb.jl using HerbGrammar

The program space in Herb.jl is defined using a grammar. This notebook demonstrates how such a grammar can be created. There are multiple kinds of grammars, but they can all be defined in a very similar way.

\n\n\n

Setup

First, we import the necessary Herb packages.

\n\n
using HerbGrammar, HerbConstraints
\n\n\n\n

Creating a simple grammar

This cell contains a very simple arithmetic grammar. The grammar is defined using the @csgrammar macro. This macro converts the grammar definition in the form of a Julia expression into Herb's internal grammar representation. Macro's are executed during compilation. If you want to load a grammar during execution, have a look at the HerbGrammar.expr2csgrammar function.

\n\n
g₁ = HerbGrammar.@csgrammar begin\n    Int = 1\n    Int = 2\n    Int = 3\n    Int = Int * Int\n    Int = Int + Int\nend
\n
1: Int = 1\n2: Int = 2\n3: Int = 3\n4: Int = Int * Int\n5: Int = Int + Int\n
\n\n\n

Defining every integer one-by-one can be quite tedious. Therefore, it is also possible to use the following syntax that makes use of a Julia iterator:

\n\n
g₂ = HerbGrammar.@csgrammar begin\n    Int = |(0:9)\n    Int = Int * Int\n    Int = Int + Int\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = Int + Int\n
\n\n\n

You can do the same with lists:

\n\n
g₃ = HerbGrammar.@csgrammar begin\n    Int = |([0, 2, 4, 6, 8])\n    Int = Int * Int\n    Int = Int + Int\nend
\n
1: Int = 0\n2: Int = 2\n3: Int = 4\n4: Int = 6\n5: Int = 8\n6: Int = Int * Int\n7: Int = Int + Int\n
\n\n\n

Variables can also be added to the grammar by just using the variable name:

\n\n
g₄ = HerbGrammar.@csgrammar begin\n    Int = |(0:9)\n    Int = Int * Int\n    Int = Int + Int\n    Int = x\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = Int + Int\n13: Int = x\n
\n\n\n

Grammars can also work with functions. After all, + and * are just infix operators for Julia's identically-named functions. You can use functions that are provided by Julia, or functions that you wrote yourself:

\n\n
begin\n    f(a) = a + 1\n    \n    g₅ = HerbGrammar.@csgrammar begin\n        Int = |(0:9)\n        Int = Int * Int\n        Int = Int + Int\n        Int = f(Int)\n        Int = x\n    end\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = Int + Int\n13: Int = f(Int)\n14: Int = x\n
\n\n\n

Similarly, we can also define the operator times (x) manually.

\n\n
begin\n    ×(a, b) = a * b\n    \n    g₆ = HerbGrammar.@csgrammar begin\n        Int = |(0:9)\n        Int = a\n        Int = Int + Int\n        Int = Int × Int\n    end\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = a\n12: Int = Int + Int\n13: Int = Int × Int\n
\n\n\n

Working with grammars

If you want to implement something using these grammars, it is useful to know about the functions that you can use to manipulate grammars and extract information. This section is not complete, but it aims to give an overview of the most important functions.

It is recommended to also read up on Julia metaprogramming if you are not already familiar with the concept.

One of the most important things about grammars is that each rule has an index associated with it:

\n\n
begin\n    g₇ = HerbGrammar.@csgrammar begin\n        Int = |(0:9)\n        Int = Int + Int\n        Int = Int * Int\n        Int = x\n    end\n    \n    collect(enumerate(g₇.rules))\nend
\n
13-element Vector{Tuple{Int64, Any}}:\n (1, 0)\n (2, 1)\n (3, 2)\n (4, 3)\n (5, 4)\n (6, 5)\n (7, 6)\n (8, 7)\n (9, 8)\n (10, 9)\n (11, :(Int + Int))\n (12, :(Int * Int))\n (13, :x)
\n\n\n

We can use this index to extract information from the grammar.

\n\n\n

isterminal

isterminal returns true if a rule is terminal, i.e. it cannot be expanded. For example, rule 1 is terminal, but rule 11 is not, since it contains the non-terminal symbol :Int.

\n\n
HerbGrammar.isterminal(g₇, 1)
\n
true
\n\n
HerbGrammar.isterminal(g₇, 11)
\n
false
\n\n\n

return_type

This function is rather obvious; it returns the non-terminal symbol that corresponds to a certain rule. The return type for all rules in our grammar is :Int.

\n\n
HerbGrammar.return_type(g₇, 11)
\n
:Int
\n\n\n

child_types

child_types returns the types of the nonterminal children of a rule in a vector. If you just want to know how many children a rule has, and not necessarily which types they have, you can use nchildren

\n\n
HerbGrammar.child_types(g₇, 11)
\n
2-element Vector{Symbol}:\n :Int\n :Int
\n\n
HerbGrammar.nchildren(g₇, 11)
\n
2
\n\n\n

nonterminals

The nonterminals function can be used to obtain a list of all nonterminals in the grammar.

\n\n
HerbGrammar.nonterminals(g₇)
\n
1-element Vector{Symbol}:\n :Int
\n\n\n

Adding rules

It is also possible to add rules to a grammar during execution. This can be done using the add_rule! function. The exclamatin mark is a Julia convention and is appended to name if a function modifies its arguments (in our example the grammar).

A rule can be provided in the same syntax as is used in the grammar definition. The rule should be of the Expr type, which is a built-in type for representing expressions. An easy way of creating Expr values in Julia is to encapsulate it in brackets and use a colon as prefix:

\n\n
HerbGrammar.add_rule!(g₇, :(Int = Int - Int))
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n14: Int = Int - Int\n
\n\n\n

Removing rules

It is also possible to remove rules in Herb.jl, however, this is a bit more involved. As said before, rules have an index associated with them. The internal representation of programs that are defined by the grammar makes use of those indices for efficiency. Blindly removing a rule would shift the indices of other rules, and this could mean that existing programs get a different meaning or become invalid.

Therefore, there are two functions for removing rules:

  • remove_rule! removes a rule from the grammar, but fills its place with a placeholder. Therefore, the indices stay the same, and only programs that use the removed rule become invalid.

  • cleanup_removed_rules! removes all placeholders and shifts the indices of the other rules.

\n\n
HerbGrammar.remove_rule!(g₇, 11)
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: nothing = nothing\n12: Int = Int * Int\n13: Int = x\n14: Int = Int - Int\n
\n\n
HerbGrammar.cleanup_removed_rules!(g₇)
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = x\n13: Int = Int - Int\n
\n\n","category":"page"},{"location":"tutorials/defining_grammars/#Context-sensitive-grammars","page":"Defining Grammars in Herb.jl","title":"Context-sensitive grammars","text":"","category":"section"},{"location":"tutorials/defining_grammars/","page":"Defining Grammars in Herb.jl","title":"Defining Grammars in Herb.jl","text":"
\n

Context-sensitive grammars introduce additional constraints compared to context-free grammars (like the simple grammar examples above). As before, we use the @csgrammar macro:

\n\n
g₈ = HerbGrammar.@csgrammar begin\n    Int = |(0:9)\n    Int = Int + Int\n    Int = Int * Int\n    Int = x\nend
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n
\n\n\n

Constraints can be added using the addconstraint! function, which takes a context-sensitive grammar and a constraint and adds the constraint to the grammar.

For example, we can add a `constraint to enforce that the input symbolx` (rule 13) appears at least once in the program, to avoid programs that are just a constant.

\n\n
HerbGrammar.addconstraint!(g₈, Contains(13))
\n
1-element Vector{HerbCore.AbstractConstraint}:\n Contains(13)
\n\n\n

There is a dedicated tutorial for constraints in Herb.jl and how to work with them.

\n\n\n

Probabilistic grammars

Herb.jl also supports probabilistic grammars. These grammars allow the user to assign a probability to each rule in the grammar. A probabilistic grammar can be defined in a very similar way to a standard grammar, but has some slightly different syntax:

\n\n
begin\n    g₉ = HerbGrammar.@pcsgrammar begin\n        0.4 : Int = |(0:9)\n        0.2 : Int = Int + Int\n        0.1 : Int = Int * Int\n        0.3 : Int = x\n    end\n    \n    for r ∈ 1:length(g₃.rules)\n        p = HerbGrammar.probability(g₈, r)\n    \n        println(\"$p : $r\")\n    end\nend
\n\n\n\n

The numbers before each rule represent the probability assigned to that rule. The total probability for each return type should add up to 1.0. If this isn't the case, Herb.jl will normalize the probabilities.

If a single line in the grammar definition represents multiple rules, such as 0.4 : Int = |(0:9), the probability will be evenly divided over all these rules.

\n\n","category":"page"},{"location":"tutorials/defining_grammars/#File-writing","page":"Defining Grammars in Herb.jl","title":"File writing","text":"","category":"section"},{"location":"tutorials/defining_grammars/","page":"Defining Grammars in Herb.jl","title":"Defining Grammars in Herb.jl","text":"
\n

Saving & loading context-free grammars

If you want to store a grammar on the disk, you can use the store_csg, read_csg and functions to store and read grammars respectively. The store_csg grammar can also be used to store probabilistic grammars. To read probabilistic grammars, use read_pcsg. The stored grammar files can also be opened using a text editor to be modified, as long as the contents of the file doesn't violate the syntax for defining grammars.

\n\n
HerbGrammar.store_csg(g₇, \"demo.txt\")
\n\n\n
HerbGrammar.read_csg(\"demo.txt\")
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int * Int\n12: Int = x\n13: Int = Int - Int\n
\n\n\n

Saving & loading context-sensitive grammars

Saving and loading context-sensitive grammars is very similar to how it is done with context-free grammars. The only difference is that an additional file is created for the constraints. The file that contains the grammars can be edited and can also be read using the reader for context-free grammars. The file that contains the constraints cannot be edited.

\n\n
HerbGrammar.store_csg( g₈, \"demo.grammar\", \"demo.constraints\")
\n\n\n
g₈, g₈.constraints
\n
(1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n, HerbCore.AbstractConstraint[Contains(13)])
\n\n
g₁₀  = HerbGrammar.read_csg(\"demo.grammar\", \"demo.constraints\")
\n
1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n
\n\n
g₁₀, g₁₀.constraints
\n
(1: Int = 0\n2: Int = 1\n3: Int = 2\n4: Int = 3\n5: Int = 4\n6: Int = 5\n7: Int = 6\n8: Int = 7\n9: Int = 8\n10: Int = 9\n11: Int = Int + Int\n12: Int = Int * Int\n13: Int = x\n, HerbCore.AbstractConstraint[Contains(13)])
\n\n","category":"page"},{"location":"tutorials/defining_grammars/","page":"Defining Grammars in Herb.jl","title":"Defining Grammars in Herb.jl","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/defining_grammars.jl\"","category":"page"},{"location":"tutorials/TopDown/#Building-Herb-Iterators","page":"Top Down Iterator","title":"Building Herb Iterators","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The core building block in Herb is a program iterator. A program iterator represents a walk through the program space; different iterators provide different ways of iterating through program space. From the program synthesis point of view, program iterators actaully represent program spaces.","category":"page"},{"location":"tutorials/TopDown/#Iterator-hierarchy","page":"Top Down Iterator","title":"Iterator hierarchy","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Program iterators are organised in a hierarchy. The top-level abstract type is ProgramIterator. At the next level of the hierarchy lie commonly used search families:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"TopDownIterator for top-down traversals\nStochasticSearachIterator for traversals with stochastic search\nBottomUpIterator for bottom-up search","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Stochastic search further provides specific iterators:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"MHSearchIterator for program traversal with Metropolis-Hastings algorithm\nVLNSearchIterator for traversals with Very Large Neighbourhood Search\nSASearchIterator for Simulated Annealing","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"We provide generic and customiseable implementations of each of these iterators, so that users can easily tweak them by through multiple dispatch. Keep reading!","category":"page"},{"location":"tutorials/TopDown/#Iterator-design","page":"Top Down Iterator","title":"Iterator design","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Program iterators follow the standard Julia Iterator interface. That is, every iterator should implement two functions:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"iterate(<:ProgramIterator)::(RuleNode,Any) to get the first program. The function takes a program iterator as an input, returning the first program and a state (which can be anything)\niterate(<:ProgramIterator,Any)::(RuleNode,Any) to get the consequtive programs. The function takes the progrma iterator and the state from the previous iteration, and return the next program and the next state.","category":"page"},{"location":"tutorials/TopDown/#Top-Down-iterator","page":"Top Down Iterator","title":"Top Down iterator","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"We illustarate how to build iterators with a Top Down iterator. The top Down iterator is build as a best-first iterator: it maintains a priority queue of programs and always pops the first element of the queue. The iterator is customiseable through the following functions:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"priority_function: dictating the order of programs in the priority queue\nderivation_heuristic: dictating in which order to explore the derivations rules within a single hole\nhole_heuristic: dictating which hole to expand next","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The first call to iterate(iter::TopDownIterator):","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"function Base.iterate(iter::TopDownIterator)\n # Priority queue with `SolverState`s (for variable shaped trees) and `UniformIterator`s (for fixed shaped trees)\n pq :: PriorityQueue{Union{SolverState, UniformIterator}, Union{Real, Tuple{Vararg{Real}}}} = PriorityQueue()\n\n solver = iter.solver\n\n if isfeasible(solver)\n enqueue!(pq, get_state(solver), priority_function(iter, get_grammar(solver), get_tree(solver), 0, false))\n end\n return _find_next_complete_tree(iter.solver, pq, iter)\nend","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The first call steps everything up: it initiates the priority queue, the constraint solver (more on that later), and return the first program. The function _find_next_complete_tree(iter.solver, pq, iter) does a lot of heavy lifting here; we will cover it later, but the only important thing is that it finds the next complete program in the priority queue (because, in case of top down enumeration, the queue also contains partial programs which we only want to expand, but not return to the user).","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The subsequent call to iterate(iter::TopDownIterator, pq::DataStructures.PriorityQueue) are quite simple: all that is needed is to find the next complete program in the priority queue:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"function Base.iterate(iter::TopDownIterator, pq::DataStructures.PriorityQueue)\n return _find_next_complete_tree(iter.solver, pq, iter)\nend","category":"page"},{"location":"tutorials/TopDown/#Modifying-the-provided-iterator","page":"Top Down Iterator","title":"Modifying the provided iterator","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"If you would like to, for example, modify the priority function, you don't have to implement the iterator from scratch. You simply need to create a new type and inherit from the TopDownIterator:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"abstract type MyTopDown <: TopDownIterator end.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"What is left is to implement the priority function, multiple-dispatching it over the new type. For example, to do a random order:","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"function priority_function(\n ::MyTopDown, \n ::AbstractGrammar, \n ::AbstractRuleNode, \n ::Union{Real, Tuple{Vararg{Real}}},\n ::Bool\n)\n Random.rand();\nend","category":"page"},{"location":"tutorials/TopDown/#A-note-on-data-structures","page":"Top Down Iterator","title":"A note on data structures","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"As you have probably noticed, the priority queue some strange data structures: SolverState and UniformIterator; the top down iterator never puts RuleNodes into the queue. In fact, the iterator never directly manipulates RuleNodes itself, but that is rather delegated to the constraint solver. The constraint solver will do a lot of work to reduce the number of programs we have to consider. The SolverState and UniformIterator are specialised data structure to improve the efficiency and memory usage. ","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Herb uses a data structure of UniformTrees to represent all programs with an AST of the same shape, where each node has the same type. the UniformIterator is an iterator efficiently iterating over that structure.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"The SolverState represents non-uniform trees – ASTs whose shape we haven't compeltely determined yet. SolverState is used as an intermediate representation betfore we reach UniformTrees on which partial constraint propagation is done.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"In principle, you should never construct ASTs yourself directly; you should leave that to the constraint solver.","category":"page"},{"location":"tutorials/TopDown/#Extra:-Find-Next-Complete-Tree-/-Program","page":"Top Down Iterator","title":"Extra: Find Next Complete Tree / Program","text":"","category":"section"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"This function pops an element from the priority queue whilst it is not empty, and then checks what kind of iterator it is.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"function _find_next_complete_tree(\n solver::Solver,\n pq::PriorityQueue,\n iter::TopDownIterator\n)\n while length(pq) ≠ 0\n (item, priority_value) = dequeue_pair!(pq)\n","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"If it is a Uniform Iterator, that is an interator where all the holes have the same shape, then it iterates over the solutions.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"\n if item isa UniformIterator\n #the item is a fixed shaped solver, we should get the next solution and re-enqueue it with a new priority value\n uniform_iterator = item\n solution = next_solution!(uniform_iterator)\n if !isnothing(solution)\n enqueue!(pq, uniform_iterator, priority_function(iter, get_grammar(solver), solution, priority_value, true))\n return (solution, pq)\n end\n","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"If it is not a Uniform Iterator, we find a hole to branch on. If the holes are all uniform, a Uniform Iterator is created, and is enqueued. If iterating on the holes would exceed a maximum depth, nothing new is enqueued. Lastly, if the holes aren't the same shape, we branch / partition on the holes, to create new partial domains to enqueue.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":" elseif item isa SolverState\n #the item is a solver state, we should find a variable shaped hole to branch on\n state = item\n load_state!(solver, state)\n\n hole_res = hole_heuristic(iter, get_tree(solver), get_max_depth(solver))\n if hole_res ≡ already_complete\n uniform_solver = UniformSolver(get_grammar(solver), get_tree(solver), with_statistics=solver.statistics)\n uniform_iterator = UniformIterator(uniform_solver, iter)\n solution = next_solution!(uniform_iterator)\n if !isnothing(solution)\n enqueue!(pq, uniform_iterator, priority_function(iter, get_grammar(solver), solution, priority_value, true))\n return (solution, pq)\n end\n elseif hole_res ≡ limit_reached\n # The maximum depth is reached\n continue\n elseif hole_res isa HoleReference\n # Variable Shaped Hole was found\n (; hole, path) = hole_res\n \n partitioned_domains = partition(hole, get_grammar(solver))\n number_of_domains = length(partitioned_domains)\n for (i, domain) ∈ enumerate(partitioned_domains)\n if i < number_of_domains\n state = save_state!(solver)\n end\n @assert isfeasible(solver) \"Attempting to expand an infeasible tree: $(get_tree(solver))\"\n remove_all_but!(solver, path, domain)\n if isfeasible(solver)\n enqueue!(pq, get_state(solver), priority_function(iter, get_grammar(solver), get_tree(solver), priority_value, false))\n end\n if i < number_of_domains\n load_state!(solver, state)\n end\n end\n end\n\n","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":"Otherwise, throw an exception, because we came across an unexpected iterator type.","category":"page"},{"location":"tutorials/TopDown/","page":"Top Down Iterator","title":"Top Down Iterator","text":" else\n throw(\"BadArgument: PriorityQueue contains an item of unexpected type '$(typeof(item))'\")\n end\n end\n return nothing\nend","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"CurrentModule=Herb","category":"page"},{"location":"#[Herb.jl](https://github.com/Herb-AI/Herb.jl)","page":"Herb.jl","title":"Herb.jl","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"A library for defining and efficiently solving program synthesis tasks in Julia.","category":"page"},{"location":"#Why-Herb.jl?","page":"Herb.jl","title":"Why Herb.jl?","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"When writing research software we almost always investigate highly specific properties or algorithms of our domain, leading to us building the tools from scratch over and over again. The very same holds for the field of program synthesis: Tools are hard to run, benchmarks are hard to get and prepare, and its hard to adapt our existing code to a novel idea.","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Herb.jl will take care of this for you and helps you defining, solving and extending your program synthesis problems.","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Herb.jl provides...","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"a unified and universal framework for program synthesis\nHerb.jl allows you to describe all sorts of program synthesis problems using context-free grammars\na number of state-of-the-art benchmarks and solvers already implemented and usable out-of-the-box","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Herb.jl's sub-packages provide fast and easily extendable implementations of ","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"various static and dynamic search strategies,\nlearning search strategies, sampling techniques and more,\nconstraint formulation and propagation, \neasy grammar formulation and usage,\nwide-range of usable program interpreters and languages + the possibility to use your own, and \nefficient data formulation.","category":"page"},{"location":"#Why-Julia?","page":"Herb.jl","title":"Why Julia?","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Julia is a perfect fit for program synthesis due to numerous reasons. Starting from scientific reasons like speed of execution and composability over to practical reasons like speed of writing Julia code. For a full ode on why to use Julia, please see the WhyJulia manifesto.","category":"page"},{"location":"#Sub-Modules","page":"Herb.jl","title":"Sub-Modules","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Herb's functionality is distributed among several sub-packages:","category":"page"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"HerbCore.jl: The core of Herb.jl defining abstract concepts,\nHerbGrammar.jl: Functionality for declaring grammars,\nHerbSpecification.jl: For describing user intent as specifications,\nHerbInterpret.jl: For running programs in different languages and environments,\nHerbConstraints.jl: For defining and effectively propagating and managing constraints during search, and\nHerbSearch.jl: For actually searching for solutions.","category":"page"},{"location":"#Basics","page":"Herb.jl","title":"Basics","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"Pages = [\"install.md\", \"get_started.md\", \"concepts.md\"]","category":"page"},{"location":"#Advanced-content","page":"Herb.jl","title":"Advanced content","text":"","category":"section"},{"location":"","page":"Herb.jl","title":"Herb.jl","text":"","category":"page"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"\n\n\n\n\n

Herb tutorial: Abstract syntax trees

\n\n\n

In this tutorial, you will learn

  • How to represent a computer program as an abstract syntax tree in Herb.

  • How to replace parts of the tree to modify the program.

\n\n","category":"page"},{"location":"tutorials/abstract_syntax_trees/#Abstract-syntax-trees","page":"Abstract Syntax Trees","title":"Abstract syntax trees","text":"","category":"section"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"
\n

The syntactic structure of a computer program can be represented in a hierarchical tree structure, a so-called Abstract Syntax Tree (AST). The syntax of a programming language is typically defined using a formal grammar, a set of rules on how valid programs can be constructed. ASTs are derived from the grammar, but are abstractions in the sense that they omit details such as parenthesis, semicolons, etc. and only retain what's necessary to capture the program structure.

In the context of program synthesis, ASTs are often used to define the space of all possible programs which is searched to find one that satisfies the given specifications. During the search process, different ASTs, each corresponding to a different program, are generated and evaluated until a suitable one is found.

Each node of the AST represents a construct in the program (e.g., a variable, an operator, a statement, or a function) and this construct corresponds to a rule in the formal grammar. An edge describes the relationship between constructs, and the tree structure captures the nesting of constructs.

\n\n","category":"page"},{"location":"tutorials/abstract_syntax_trees/#A-simple-example-program","page":"Abstract Syntax Trees","title":"A simple example program","text":"","category":"section"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"
\n

We first consider the simple program 5*(x+3). We will define a grammar that is sufficient to represent this program and use it to construct a AST for our program.

\n\n\n

Define the grammar

\n\n
begin \n    using PlutoUI\n    \n    using HerbCore\n    using HerbGrammar\n    using HerbInterpret\nend
\n\n\n
grammar = @csgrammar begin\n        Number = |(0:9)\n        Number = x\n        Number = Number + Number\n        Number = Number * Number\n    end
\n
1: Number = 0\n2: Number = 1\n3: Number = 2\n4: Number = 3\n5: Number = 4\n6: Number = 5\n7: Number = 6\n8: Number = 7\n9: Number = 8\n10: Number = 9\n11: Number = x\n12: Number = Number + Number\n13: Number = Number * Number\n
\n\n\n

Construct the syntax tree

\n\n\n

The AST of this program is shown in the diagram below. The number in each node refers to the index of the corresponding rule in our grammar.

\n\n\n
    flowchart \n    id1((13)) ---\n    id2((6))\n    id1 --- id3((12))\n    id4((11))\n    id5((4))\n    id3 --- id4\n    id3 --- id5
\n\n\n
    flowchart \n    id1((13)) ---\n    id2((6))\n    id1 --- id3((12))\n    id4((11))\n    id5((4))\n    id3 --- id4\n    id3 --- id5
\n\n\n

In Herb.jl, the HerbCore.RuleNode is used to represent both an individual node, but also entire ASTs or sub-trees. This is achieved by nesting instances of RuleNode. A RuleNode can be instantiated by providing the index of the grammar rule that the node represents and a vector of child nodes.

\n\n
syntaxtree = RuleNode(13, [RuleNode(6), RuleNode(12, [RuleNode(11), RuleNode(4)])])
\n
13{6,12{11,4}}
\n\n\n

We can confirm that our AST is correct by displaying it in a more human-readable way, using HerbGrammar.rulenode2expr and by testing it on a few input examples using HerbInterpret.execute_on_input.

\n\n
rulenode2expr(syntaxtree, grammar)
\n
:(5 * (x + 3))
\n\n
# test solution on inputs\nexecute_on_input(grammar, syntaxtree, Dict(:x => 10))
\n
65
\n\n","category":"page"},{"location":"tutorials/abstract_syntax_trees/#Another-example:-FizzBuzz","page":"Abstract Syntax Trees","title":"Another example: FizzBuzz","text":"","category":"section"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"
\n

Let's look at a more interesting example. The program fizbuzz() is based on the popular FizzBuzz problem. Given an integer number, the program simply returns a String of that number, but replace numbers divisible by 3 with \"Fizz\", numbers divisible by 5 with \"Buzz\", and number divisible by both 3 and 5 with \"FizzBuzz\".

\n\n
function fizzbuzz(x)\n    if x % 5 == 0 && x % 3 == 0\n        return \"FizzBuzz\"\n    else\n        if x % 3 == 0\n            return  \"Fizz\"\n        else\n            if x % 5 == 0\n                return \"Buzz\"\n            else\n                return string(x)\n            end\n        end\n    end\nend
\n
fizzbuzz (generic function with 1 method)
\n\n\n

Define the grammar

Let's define a grammar with all the rules that we need.

\n\n
grammar_fizzbuzz = @csgrammar begin\n    Int = input1\n    Int = 0 | 3 | 5\n    String = \"Fizz\" | \"Buzz\" | \"FizzBuzz\"\n    String = string(Int)\n    Return = String\n    Int = Int % Int\n    Bool = Int == Int\n    Int = Bool ? Int : Int\n    Bool = Bool && Bool\nend
\n
1: Int = input1\n2: Int = 0\n3: Int = 3\n4: Int = 5\n5: String = Fizz\n6: String = Buzz\n7: String = FizzBuzz\n8: String = string(Int)\n9: Return = String\n10: Int = Int % Int\n11: Bool = Int == Int\n12: Int = if Bool\n    Int\nelse\n    Int\nend\n13: Bool = Bool && Bool\n
\n\n\n

Construct the syntax tree

\n\n\n

Given the grammar, the AST of fizzbuzz() looks like this:

\n\n\n
flowchart \n    id1((12)) --- id21((13))\n    id1--- id22((9))\n    id1--- id23((12))\n\n    id21 --- id31((11))\n    id21 --- id32((11))\n\n    id31 --- id41((10))\n    id31 --- id42((2))\n\n    id41 --- id51((1))\n    id41 --- id52((4))\n\n    id32 --- id43((10)) \n    id32 --- id44((2))\n\n    id43 --- id53((1))\n    id43 --- id54((3))\n\n    id22 --- id33((7))\n    id23 --- id34((11))\n\n    id34 --- id45((10))\n    id34 --- id46((2))\n\n    id45 --- id55((1))\n    id45 --- id56((3))\n\n    id23 --- id35((9))\n    id35 --- id47((5))\n\n    id23 --- id36((12))\n    id36 --- id48((11))\n    id48 --- id57((10))\n    id57 --- id61((1))\n    id57 --- id62((4))\n    id48 --- id58((2))\n\n    id36 --- id49((9))\n    id49 --- id59((6))\n\n    id36 --- id410((9))\n    id410 --- id510((8))\n    id510 --- id63((1))\n    \n    \n    
\n\n\n

As before, we use nest instanced of RuleNode to implement the AST.

\n\n
fizzbuzz_syntaxtree = RuleNode(12, [\n               RuleNode(13, [\n                   RuleNode(11, [\n                       RuleNode(10, [\n                           RuleNode(1),\n                           RuleNode(4)\n                       ]),\n                       RuleNode(2)\n                   ]),\n                   RuleNode(11, [\n                       RuleNode(10, [\n                           RuleNode(1),\n                           RuleNode(3)\n                       ]),\n                       RuleNode(2)\n                   ])\n               ]),\n               RuleNode(9, [\n                   RuleNode(7)\n               \n               ]),\n               RuleNode(12, [\n                   RuleNode(11, [\n                       RuleNode(10, [\n                           RuleNode(1),\n                           RuleNode(3),\n                       ]),\n                       RuleNode(2)\n                   ]),\n                   RuleNode(9, [\n                       RuleNode(5)\n                   ]),\n                   RuleNode(12, [\n                       RuleNode(11, [\n                           RuleNode(10, [\n                               RuleNode(1),\n                               RuleNode(4)\n                           ]),\n                           RuleNode(2)\n                       ]),\n                       RuleNode(9, [\n                           RuleNode(6)\n                       ]),\n                       RuleNode(9, [\n                           RuleNode(8, [\n                                RuleNode(1)\n                            ])\n                       ])\n                   ])\n               ]) \n    ])
\n
12{13{11{10{1,4}2}11{10{1,3}2}}9{7}12{11{10{1,3}2}9{5}12{11{10{1,4}2}9{6}9{8{1}}}}}
\n\n\n

And we check our syntax tree is correct:

\n\n
rulenode2expr(fizzbuzz_syntaxtree, grammar_fizzbuzz)
\n
:(if input1 % 5 == 0 && input1 % 3 == 0\n      \"FizzBuzz\"\n  else\n      if input1 % 3 == 0\n          \"Fizz\"\n      else\n          if input1 % 5 == 0\n              \"Buzz\"\n          else\n              string(input1)\n          end\n      end\n  end)
\n\n
begin\n    # test solution on inputs\n    input = [Dict(:input1 => 3), Dict(:input1 => 5), Dict(:input1 =>15), Dict(:input1 => 22)]\n    output1 = execute_on_input(grammar_fizzbuzz, fizzbuzz_syntaxtree, input)\n    output1\nend
\n
4-element Vector{Any}:\n \"Fizz\"\n \"Buzz\"\n \"FizzBuzz\"\n \"22\"
\n\n\n

Modify the AST/program

There are several ways to modify an AST and hence, a program. You can

  • directly replace a node with HerbCore.swap_node()

  • insert a rule node with insert!

Let's modify our example such that if the input number is divisible by 3, the program returns \"Buzz\" instead of \"Fizz\". We use swap_node() to replace the node of the AST that corresponds to rule 5 in the grammar (String = Fizz) with rule 6 (String = Buzz). To do so, swap_node() needs the tree that contains the node we want to modify, the new node we want to replace the node with, and the path to that node.

Note that swap_node() modifies the tree, hence we make a deep copy of it first.

\n\n
begin\n    modified_fizzbuzz_syntaxtree = deepcopy(fizzbuzz_syntaxtree)\n    newnode = RuleNode(6)\n    path = [3, 2, 1]\n    swap_node(modified_fizzbuzz_syntaxtree, newnode, path)\n    rulenode2expr(modified_fizzbuzz_syntaxtree, grammar_fizzbuzz)\nend
\n
:(if input1 % 5 == 0 && input1 % 3 == 0\n      \"FizzBuzz\"\n  else\n      if input1 % 3 == 0\n          \"Buzz\"\n      else\n          if input1 % 5 == 0\n              \"Buzz\"\n          else\n              string(input1)\n          end\n      end\n  end)
\n\n\n

Let's confirm that we modified the AST, and hence the program, correctly:

\n\n
# test solution on same inputs as before\nexecute_on_input(grammar_fizzbuzz, modified_fizzbuzz_syntaxtree, input)
\n
4-element Vector{Any}:\n \"Buzz\"\n \"Buzz\"\n \"FizzBuzz\"\n \"22\"
\n\n\n

An alternative way to modify the AST is by using insert!(). This requires to provide the location of the node that we want to as NodeLoc. NodeLoc points to a node in the tree and consists of the parent and the child index of the node. Again, we make a deep copy of the original AST first.

\n\n
begin\n    anothermodified_fizzbuzz_syntaxtree = deepcopy(fizzbuzz_syntaxtree)\n    # get the node we want to modify and instantiate a NodeLoc from it.\n    node = get_node_at_location(anothermodified_fizzbuzz_syntaxtree, [3, 2, 1])\n    nodeloc = NodeLoc(node, 0)\n    # replace the node\n    insert!(node, nodeloc, newnode)\n    rulenode2expr(anothermodified_fizzbuzz_syntaxtree, grammar_fizzbuzz)\nend
\n
:(if input1 % 5 == 0 && input1 % 3 == 0\n      \"FizzBuzz\"\n  else\n      if input1 % 3 == 0\n          \"Buzz\"\n      else\n          if input1 % 5 == 0\n              \"Buzz\"\n          else\n              string(input1)\n          end\n      end\n  end)
\n\n\n

Again, we check that we modified the program as intended:

\n\n
# test on same inputs as before\nexecute_on_input(grammar_fizzbuzz, anothermodified_fizzbuzz_syntaxtree, input)
\n
4-element Vector{Any}:\n \"Buzz\"\n \"Buzz\"\n \"FizzBuzz\"\n \"22\"
\n\n","category":"page"},{"location":"tutorials/abstract_syntax_trees/","page":"Abstract Syntax Trees","title":"Abstract Syntax Trees","text":"EditURL = \"https://github.com/Herb-AI/Herb.jl/blob/main/docs/src/tutorials/abstract_syntax_trees.jl\"","category":"page"}] } diff --git a/previews/PR115/tutorials/TopDown/index.html b/previews/PR115/tutorials/TopDown/index.html index b8faa71..c2209f0 100644 --- a/previews/PR115/tutorials/TopDown/index.html +++ b/previews/PR115/tutorials/TopDown/index.html @@ -78,4 +78,4 @@ end end return nothing -end +end diff --git a/previews/PR115/tutorials/abstract_syntax_trees/index.html b/previews/PR115/tutorials/abstract_syntax_trees/index.html index fc976fc..f0fe395 100644 --- a/previews/PR115/tutorials/abstract_syntax_trees/index.html +++ b/previews/PR115/tutorials/abstract_syntax_trees/index.html @@ -382,4 +382,4 @@

- + diff --git a/previews/PR115/tutorials/advanced_search/index.html b/previews/PR115/tutorials/advanced_search/index.html index 3c4fd06..7967597 100644 --- a/previews/PR115/tutorials/advanced_search/index.html +++ b/previews/PR115/tutorials/advanced_search/index.html @@ -368,7 +368,7 @@