Skip to content

Commit d21a13e

Browse files
docs: turn doctests into @example blocks
1 parent dc902cc commit d21a13e

File tree

3 files changed

+21
-106
lines changed

3 files changed

+21
-106
lines changed

docs/src/manual/rewrite.md

Lines changed: 21 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ Rewrite rules match and transform an expression. A rule is written using either
88

99
Here is a simple rewrite rule, that uses formula for the double angle of the sine function:
1010

11-
```jldoctest rewrite
11+
```@example rewrite
1212
using SymbolicUtils
1313
1414
@syms w z α::Real β::Real
@@ -18,9 +18,6 @@ using SymbolicUtils
1818
r1 = @rule sin(2(~x)) => 2sin(~x)*cos(~x)
1919
2020
r1(sin(2z))
21-
22-
# output
23-
2cos(z)*sin(z)
2421
```
2522

2623
The `@rule` macro takes a pair of patterns -- the _matcher_ and the _consequent_ (`@rule matcher => consequent`). If an expression matches the matcher pattern, it is rewritten to the consequent pattern. `@rule` returns a callable object that applies the rule to an expression.
@@ -30,47 +27,32 @@ The `@rule` macro takes a pair of patterns -- the _matcher_ and the _consequent_
3027
If you try to apply this rule to an expression with triple angle, it will return `nothing` -- this is the way a rule signifies failure to match.
3128
```julia
3229
r1(sin(3z))
33-
34-
# output
35-
nothing
3630
```
3731

3832
Slot variable (matcher) is not necessary a single variable:
39-
```jldoctest rewrite
33+
```@example rewrite
4034
r1(sin(2*(w-z)))
41-
42-
# output
43-
2sin(w - z)*cos(w - z)
4435
```
4536

4637
And can also match a function:
4738
```julia
4839
r = @rule (~f)(z+1) => ~f
4940

5041
r(sin(z+1))
51-
52-
# output
53-
sin (generic function with 20 methods)
54-
5542
```
5643
Rules are of course not limited to single slot variable
5744

58-
```jldoctest rewrite
45+
```@example rewrite
5946
r2 = @rule sin(~x + ~y) => sin(~x)*cos(~y) + cos(~x)*sin(~y);
6047
6148
r2(sin(α+β))
62-
63-
# output
64-
cos(β)*sin(α) + sin(β)*cos(α)
6549
```
6650

6751
Now let's say you want to catch the coefficients of a second degree polynomial in z. You can do that with:
68-
```jldoctest rewrite
52+
```@example rewrite
6953
c2d = @rule ~a + ~b*z + ~c*z^2 => (~a, ~b, ~c)
7054
71-
c2d(3 + 2z + 5z^2)
72-
# output
73-
(3, 2, 5)
55+
2d(3 + 2z + 5z^2)
7456
```
7557
Great! But if you try:
7658
```julia
@@ -80,12 +62,10 @@ c2d(3 + 2z + z^2)
8062
nothing
8163
```
8264
the rule is not applied. This is because in the input polynomial there isn't a multiplication in front of the `z^2`. For this you can use **defslot variables**, with syntax `~!a`:
83-
```jldoctest rewrite
65+
```@example rewrite
8466
c2d = @rule ~!a + ~!b*z + ~!c*z^2 => (~a, ~b, ~c)
8567
86-
c2d(3 + 2z + z^2)
87-
# output
88-
(3, 2, 1)
68+
2d(3 + 2z + z^2)
8969
```
9070
They work like normal slot variables, but if they are not present they take a default value depending on the operation they are in, in the above example `~b = 1`. Currently defslot variables can be defined in:
9171

@@ -97,52 +77,31 @@ addition `+` | 0
9777

9878
If you want to match a variable number of subexpressions at once, you will need a **segment variable**. `~~xs` in the following example is a segment variable:
9979

100-
```jldoctest rewrite
80+
```@example rewrite
10181
@syms x y z
10282
@rule(+(~~xs) => ~~xs)(x + y + z)
103-
104-
# output
105-
3-element view(::ReadOnlyArrays.ReadOnlyVector{Any, SymbolicUtils.SmallVec{Any, Vector{Any}}}, 1:3) with eltype Any:
106-
x
107-
y
108-
z
10983
```
11084

11185
`~~xs` is a vector of subexpressions matched. You can use it to construct something more useful:
11286

113-
```jldoctest rewrite
87+
```@example rewrite
11488
r3 = @rule ~x * +(~~ys) => sum(map(y-> ~x * y, ~~ys));
11589
11690
r3(2 * (w+w+α+β))
117-
118-
# output
119-
4w + 2α + 2β
12091
```
12192

12293
Notice that the expression was autosimplified before application of the rule.
12394

124-
```jldoctest rewrite
95+
```@example rewrite
12596
2 * (w+w+α+β)
126-
127-
# output
128-
2(2w + α + β)
12997
```
13098

13199
Note that writing a single tilde `~` as consequent, will make the rule return a dictionary of [slot variable, expression matched].
132100

133-
```jldoctest rewrite
101+
```@example rewrite
134102
r = @rule (~x + (~y)^(~m)) => ~
135103
136104
r(z+w^α)
137-
138-
# output
139-
Base.ImmutableDict{Symbol, Any} with 5 entries:
140-
:MATCH => z + w^α
141-
:m => α
142-
:y => w
143-
:x => z
144-
:____ => nothing
145-
146105
```
147106

148107
### Predicates for matching
@@ -153,7 +112,7 @@ Similarly `~~x::g` is a way of attaching a predicate `g` to a segment variable.
153112

154113
For example,
155114

156-
```jldoctest pred
115+
```@example pred
157116
using SymbolicUtils
158117
@syms a b c d
159118
@@ -163,69 +122,51 @@ r = @rule ~x + ~~y::(ys->iseven(length(ys))) => "odd terms";
163122
@show r(b + c + d)
164123
@show r(b + c + b)
165124
@show r(a + b)
166-
167-
# output
168-
r(a + b + c + d) = nothing
169-
r(b + c + d) = "odd terms"
170-
r(b + c + b) = nothing
171-
r(a + b) = nothing
172125
```
173126

174127

175128
### Associative-Commutative Rules
176129

177130
Given an expression `f(x, f(y, z, u), v, w)`, a `f` is said to be associative if the expression is equivalent to `f(x, y, z, u, v, w)` and commutative if the order of arguments does not matter. SymbolicUtils has a special `@acrule` macro meant for rules on functions which are associate and commutative such as addition and multiplication of real and complex numbers.
178131

179-
```jldoctest acr
132+
```@example acr
180133
using SymbolicUtils
181134
@syms x y z
182135
183136
acr = @acrule((~a)^(~x) * (~a)^(~y) => (~a)^(~x + ~y))
184137
185138
acr(x^y * x^z)
186-
187-
# output
188-
x^(y + z)
189139
```
190140

191141
although in case of `Number` it also works the same way with regular `@rule` since autosimplification orders and applies associativity and commutativity to the expression.
192142

193143
### Example of applying the rules to simplify expression
194144

195145
Consider expression `(cos(x) + sin(x))^2` that we would like simplify by applying some trigonometric rules. First, we need rule to expand square of `cos(x) + sin(x)`. First we try the simplest rule to expand square of the sum and try it on simple expression
196-
```jldoctest rewriteex
146+
```@example rewriteex
197147
using SymbolicUtils
198148
199149
@syms x::Real y::Real
200150
201151
sqexpand = @rule (~x + ~y)^2 => (~x)^2 + (~y)^2 + 2 * ~x * ~y
202152
203153
sqexpand((cos(x) + sin(x))^2)
204-
205-
# output
206-
sin(x)^2 + 2sin(x)*cos(x) + cos(x)^2
207154
```
208155

209156
It works. This can be further simplified using Pythagorean identity and check it
210157

211-
```jldoctest rewriteex
158+
```@example rewriteex
212159
pyid = @rule sin(~x)^2 + cos(~x)^2 => 1
213160
214161
pyid(sin(x)^2 + 2sin(x)*cos(x) + cos(x)^2)===nothing
215-
216-
# output
217-
true
218162
```
219163

220164
Why does it return `nothing`? If we look at the expression, we see that we have an additional addend `+ 2sin(x)*cos(x)`. Therefore, in order to work, the rule needs to be associative-commutative.
221165

222-
```jldoctest rewriteex
166+
```@example rewriteex
223167
acpyid = @acrule sin(~x)^2 + cos(~x)^2 => 1
224168
225169
acpyid(cos(x)^2 + sin(x)^2 + 2cos(x)*sin(x))
226-
227-
# output
228-
1 + 2sin(x)*cos(x)
229170
```
230171

231172
It has been some work. Fortunately rules may be [chained together](#chaining rewriters) into more sophisticated rewriters to avoid manual application of the rules.
@@ -270,7 +211,7 @@ Several rules may be chained to give chain of rules. Chain is an array of rules
270211

271212
To check that, we will combine rules from [previous example](#example of applying the rules to simplify expression) into a chain
272213

273-
```jldoctest composing
214+
```@example composing
274215
using SymbolicUtils
275216
using SymbolicUtils.Rewriters
276217
@@ -282,52 +223,37 @@ acpyid = @acrule sin(~x)^2 + cos(~x)^2 => 1
282223
csa = Chain([sqexpand, acpyid])
283224
284225
csa((cos(x) + sin(x))^2)
285-
286-
# output
287-
1 + 2sin(x)*cos(x)
288226
```
289227

290228
Important feature of `Chain` is that it returns the expression instead of `nothing` if it doesn't change the expression
291229

292-
```jldoctest composing
230+
```@example composing
293231
Chain([@acrule sin(~x)^2 + cos(~x)^2 => 1])((cos(x) + sin(x))^2)
294-
295-
# output
296-
(sin(x) + cos(x))^2
297232
```
298233

299234
it's important to notice, that chain is ordered, so if rules are in different order it wouldn't work the same as in earlier example
300235

301-
```jldoctest composing
236+
```@example composing
302237
cas = Chain([acpyid, sqexpand])
303238
304239
cas((cos(x) + sin(x))^2)
305-
306-
# output
307-
sin(x)^2 + 2sin(x)*cos(x) + cos(x)^2
308240
```
309241
since Pythagorean identity is applied before square expansion, so it is unable to match squares of sine and cosine.
310242

311243
One way to circumvent the problem of order of applying rules in chain is to use `RestartedChain`
312244

313-
```jldoctest composing
245+
```@example composing
314246
using SymbolicUtils.Rewriters: RestartedChain
315247
316248
rcas = RestartedChain([acpyid, sqexpand])
317249
318250
rcas((cos(x) + sin(x))^2)
319-
320-
# output
321-
1 + 2sin(x)*cos(x)
322251
```
323252

324253
It restarts the chain after each successful application of a rule, so after `sqexpand` is hit it (re)starts again and successfully applies `acpyid` to resulting expression.
325254

326255
You can also use `Fixpoint` to apply the rules until there are no changes.
327256

328-
```jldoctest composing
257+
```@example composing
329258
Fixpoint(cas)((cos(x) + sin(x))^2)
330-
331-
# output
332-
1 + 2sin(x)*cos(x)
333259
```

test/doctest.jl

Lines changed: 0 additions & 10 deletions
This file was deleted.

test/runtests.jl

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ using Pkg, Test, SafeTestsets
44
if haskey(ENV, "SU_BENCHMARK_ONLY")
55
@safetestset "Benchmark" begin include("benchmark.jl") end
66
else
7-
@safetestset "Doc" begin include("doctest.jl") end
87
@safetestset "Basics" begin include("basics.jl") end
98
@safetestset "Basics" begin include("arrayop.jl") end
109
@safetestset "Order" begin include("order.jl") end

0 commit comments

Comments
 (0)