You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/src/manual/rewrite.md
+21-95Lines changed: 21 additions & 95 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -8,7 +8,7 @@ Rewrite rules match and transform an expression. A rule is written using either
8
8
9
9
Here is a simple rewrite rule, that uses formula for the double angle of the sine function:
10
10
11
-
```jldoctest rewrite
11
+
```@example rewrite
12
12
using SymbolicUtils
13
13
14
14
@syms w z α::Real β::Real
@@ -18,9 +18,6 @@ using SymbolicUtils
18
18
r1 = @rule sin(2(~x)) => 2sin(~x)*cos(~x)
19
19
20
20
r1(sin(2z))
21
-
22
-
# output
23
-
2cos(z)*sin(z)
24
21
```
25
22
26
23
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_
30
27
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.
31
28
```julia
32
29
r1(sin(3z))
33
-
34
-
# output
35
-
nothing
36
30
```
37
31
38
32
Slot variable (matcher) is not necessary a single variable:
39
-
```jldoctest rewrite
33
+
```@example rewrite
40
34
r1(sin(2*(w-z)))
41
-
42
-
# output
43
-
2sin(w - z)*cos(w - z)
44
35
```
45
36
46
37
And can also match a function:
47
38
```julia
48
39
r =@rule (~f)(z+1) =>~f
49
40
50
41
r(sin(z+1))
51
-
52
-
# output
53
-
sin (generic function with 20 methods)
54
-
55
42
```
56
43
Rules are of course not limited to single slot variable
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
69
53
c2d = @rule ~a + ~b*z + ~c*z^2 => (~a, ~b, ~c)
70
54
71
-
c2d(3 + 2z + 5z^2)
72
-
# output
73
-
(3, 2, 5)
55
+
2d(3 + 2z + 5z^2)
74
56
```
75
57
Great! But if you try:
76
58
```julia
@@ -80,12 +62,10 @@ c2d(3 + 2z + z^2)
80
62
nothing
81
63
```
82
64
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
84
66
c2d = @rule ~!a + ~!b*z + ~!c*z^2 => (~a, ~b, ~c)
85
67
86
-
c2d(3 + 2z + z^2)
87
-
# output
88
-
(3, 2, 1)
68
+
2d(3 + 2z + z^2)
89
69
```
90
70
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:
91
71
@@ -97,52 +77,31 @@ addition `+` | 0
97
77
98
78
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:
99
79
100
-
```jldoctest rewrite
80
+
```@example rewrite
101
81
@syms x y z
102
82
@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
109
83
```
110
84
111
85
`~~xs` is a vector of subexpressions matched. You can use it to construct something more useful:
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.
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.
192
142
193
143
### Example of applying the rules to simplify expression
194
144
195
145
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
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.
221
165
222
-
```jldoctest rewriteex
166
+
```@example rewriteex
223
167
acpyid = @acrule sin(~x)^2 + cos(~x)^2 => 1
224
168
225
169
acpyid(cos(x)^2 + sin(x)^2 + 2cos(x)*sin(x))
226
-
227
-
# output
228
-
1 + 2sin(x)*cos(x)
229
170
```
230
171
231
172
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
270
211
271
212
To check that, we will combine rules from [previous example](#example of applying the rules to simplify expression) into a chain
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
300
235
301
-
```jldoctest composing
236
+
```@example composing
302
237
cas = Chain([acpyid, sqexpand])
303
238
304
239
cas((cos(x) + sin(x))^2)
305
-
306
-
# output
307
-
sin(x)^2 + 2sin(x)*cos(x) + cos(x)^2
308
240
```
309
241
since Pythagorean identity is applied before square expansion, so it is unable to match squares of sine and cosine.
310
242
311
243
One way to circumvent the problem of order of applying rules in chain is to use `RestartedChain`
312
244
313
-
```jldoctest composing
245
+
```@example composing
314
246
using SymbolicUtils.Rewriters: RestartedChain
315
247
316
248
rcas = RestartedChain([acpyid, sqexpand])
317
249
318
250
rcas((cos(x) + sin(x))^2)
319
-
320
-
# output
321
-
1 + 2sin(x)*cos(x)
322
251
```
323
252
324
253
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.
325
254
326
255
You can also use `Fixpoint` to apply the rules until there are no changes.
0 commit comments