Skip to content

Commit

Permalink
Update the "more verbose" tutorial example
Browse files Browse the repository at this point in the history
There are two changes here, one of which may be a bit contentious. I
really enjoyed working through this, but I was thrown by the final
comments concerning 167. What's so different about 167 compared to 168?
I found the program generator was quite happy to come up with a program
for both.

I thought it might be interesting therefore if the 167 example was
something that the program generator was not able to solve. This, I
think, makes it more interesting and also emphasises that the software
is doing what the user would expect (i.e. not coming up with a solution
that doesn't exist).

This change therefore constrains the values to being even, so that the
167 result can't be obtained.

The second, smaller change, is to use 'Number# instead of 'Real'. I was
uncomfortable with the latter simply because, even in the original
version, they are in fact all integers.

I understand if you feel these changes are making things overly
complicated. I just thought they'd make an interesting example.
  • Loading branch information
llewelld committed Oct 18, 2024
1 parent fd0a9d4 commit 2e0ffc9
Showing 1 changed file with 20 additions and 16 deletions.
36 changes: 20 additions & 16 deletions docs/src/tutorials/getting_started_with_herb.jl
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,20 @@ Specifically, we will look at example-based search, where the goal is to find a
# ╔═╡ 841f097d-a389-4dd2-9ad3-1a2292568634
md"""
### Setup
First, we start with the setup. We need to access to all the function in the Herb.jl framework.
First, we start with the setup. We need access to all the function in the Herb.jl framework.
"""

# ╔═╡ db7fe47b-ab3e-4705-b6ac-2733b9e81434
md"""
### Defining the program space
Next, we start by creating a grammar. We define a context-free grammar as a [`HerbGrammar.ContextSpecificGrammar`](@ref) without any constraints. A context-free grammar is just a simple set of production rules for defining combinations of terminal symbols (in our case real numbers).
Next, we start by creating a grammar. We define a context-free grammar as a [`HerbGrammar.ContextSpecificGrammar`](@ref) 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).
Contrary, we could define a context-sensitive grammar, when the production rules only hold in a certain context. However, for more information on this, please see our tutorial on [defining grammars](defining_grammars.md).
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](defining_grammars.md).
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 interval `[0:9]` on real numbers and also constrain them to be integer.
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.
Expand All @@ -42,11 +42,11 @@ If you run this cell, you can see all the rules rolled out.

# ╔═╡ 763b378b-66f9-481e-a3da-ca37825eb255
g = HerbGrammar.@csgrammar begin
Real = |(0:9)
Real = x
Real = Real + Real
Real = Real - Real
Real = Real * Real
Number = 0|2|4|6|8
Number = x
Number = Number + Number
Number = Number - Number
Number = Number * Number
end

# ╔═╡ 6d01dfe8-9048-4696-916c-b33fbc97268b
Expand All @@ -69,7 +69,7 @@ In the cell below we automatically generate some examples for `x` assigning valu

# ╔═╡ 8bf48b7a-0ff5-4015-81d3-ed2eeeceff1c
# Create input-output examples
examples = [HerbSpecification.IOExample(Dict(:x => x), 3x + 5) for x 1:5]
examples = [HerbSpecification.IOExample(Dict(:x => x), 4x + 6) for x 1:5]

# ╔═╡ 2baa7f33-c86d-40e2-9253-720ec19e4c43
md"""
Expand Down Expand Up @@ -99,11 +99,11 @@ 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 `Real`.
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`.
"""

# ╔═╡ d553f37b-bc8a-4426-a98b-fb195ed994d9
iterator_1 = BFSIterator(g, :Real)
iterator_1 = BFSIterator(g, :Number)

# ╔═╡ e1910236-9783-4989-a014-c3f7ccdf33d3
synth(problem_1, iterator_1)
Expand All @@ -119,17 +119,19 @@ md"""
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 `167`. We immediately pass the examples to the problem and then set up the new search.
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.
"""

# ╔═╡ cdab3f55-37e4-4aee-bae1-14d3475cbdcd
begin
problem_2 = HerbSpecification.Problem("example2", [HerbSpecification.IOExample(Dict(:x => x), 168) for x 1:5])
iterator_2 = HerbSearch.BFSIterator(g, :Real, max_depth=4, max_size=30)
expr_2 = HerbSearch.synth(problem_2, iterator_2)
iterator_2 = HerbSearch.BFSIterator(g, :Number, max_depth=4, max_size=30)
expr_2, flag = HerbSearch.synth(problem_2, iterator_2)
print(expr_2)
program_2 = rulenode2expr(expr_2, g)
println(program_2)
end

# ╔═╡ 5ad86beb-eb25-4bae-b0c2-a33d1a38581a
Expand All @@ -142,8 +144,10 @@ In any case, this concludes our first introduction to the `Herb.jl` program synt
# ╔═╡ c06d09a5-138a-4821-8a60-074fa7ec026d
begin
problem_3 = HerbSpecification.Problem("example3", [HerbSpecification.IOExample(Dict(:x => x), 167) for x 1:5])
expr_3 = HerbSearch.synth(problem_3, iterator_2)
expr_3, flag = HerbSearch.synth(problem_3, iterator_2)
print(expr_3)
program_3 = rulenode2expr(expr_3, g)
println(program_3)
end

# ╔═╡ 00000000-0000-0000-0000-000000000001
Expand Down

0 comments on commit 2e0ffc9

Please sign in to comment.