|
1 | | -# SymbolicUtils.jl |
2 | | - |
3 | | -SymbolicUtils.jl provides various utilities for symbolic computing. |
| 1 | +<h1 align="center"><a href="https://juliasymbolics.github.io/SymbolicUtils.jl/">SymbolicUtils.jl</a></h1> |
4 | 2 |
|
5 | 3 | [](https://travis-ci.com/github/JuliaSymbolics/SymbolicUtils.jl) [](https://coveralls.io/github/JuliaSymbolics/SymbolicUtils.jl?branch=master) |
| 4 | +<p align="center"> |
| 5 | + <a href="https://travis-ci.com/github/JuliaSymbolics/SymbolicUtils.jl"> |
| 6 | + <img src="https://travis-ci.org/JuliaSymbolics/SymbolicUtils.jl.svg?branch=master" |
| 7 | + alt="Build Status (Linux)"> |
| 8 | + </a> |
| 9 | + <a href="https://ci.appveyor.com/project/tlienart/Franklin-jl"> |
| 10 | + <img src="https://ci.appveyor.com/api/projects/status/github/tlienart/Franklin.jl?branch=master&svg=true" |
| 11 | + alt="Build Status (Windows)"> |
| 12 | + </a> |
| 13 | + <a href="https://coveralls.io/github/JuliaSymbolics/SymbolicUtils.jl?branch=master"> |
| 14 | + <img src="https://coveralls.io/repos/github/JuliaSymbolics/SymbolicUtils.jl/badge.svg?branch=master" |
| 15 | + alt="Coverage"> |
| 16 | + </a> |
| 17 | +</p> |
6 | 18 |
|
7 | | -## Symbols and expressions |
8 | | - |
9 | | -Symbols can be created using the `@syms` macro: |
10 | | - |
11 | | -```julia |
12 | | -julia> using SymbolicUtils |
13 | | - |
14 | | -julia> @syms a::Integer b c d x::Real y::Number |
15 | | -(a, b, c, d, x, y) |
16 | | -``` |
17 | | - |
18 | | -This macro also defines the Julia variables of the same name and each is set to the its respective symbolic object. |
19 | | - |
20 | | -The associated type `T` in the `@syms a::T` syntax, called `symtype` of the symbol, is the type the value of the symbol is supposed to be of. These types may determine the rules of symbolic simplification. |
21 | | - |
22 | | -Arithmetic and math functions are defined on symbols and return `Term` objects which represent function call expressions. |
23 | | - |
24 | | -Symbols can be defined to behave like functions. Both the input and output types for the function can be specified. Any application to that function will only admit either values of those types or symbols of the same `symtype`. |
25 | | - |
26 | | -```julia |
27 | | -julia> @syms f(x) g(x::Real, y::Real)::Real |
28 | | -(f(::Number)::Number, g(::Real, ::Real)::Real) |
29 | | - |
30 | | -julia> f(c) |
31 | | -f(c) |
32 | | - |
33 | | -julia> g(1, x) |
34 | | -g(1, x) |
35 | | -``` |
36 | | - |
37 | | -## Symbolic simplification |
38 | | - |
39 | | -Use the `simplify` function to apply a built in list of rules to simplify an expression: |
40 | | -```julia |
41 | | - |
42 | | -julia> simplify(a + b + (x * y) + c + 2 * (x * y) + d + sin(x)^2 + cos(x)^2 - y^0) |
43 | | -((3 * x * y) + a + b + c + d) |
44 | | -``` |
45 | | - |
46 | | -## Pattern matching and rewriting |
47 | | - |
48 | | -Expression rewriting rules can be created using the `@rule` macro `@rule LHS => RHS`. |
49 | | - |
50 | | -Creates a `Rule` object. A rule object is callable, and takes an expression and rewrites |
51 | | -it if it matches the LHS pattern to the RHS pattern, returns `nothing` otherwise. |
52 | | -The rule language is described below. |
53 | | - |
54 | | -LHS can be any possibly nested function call expression where any of the arugments can |
55 | | -optionally be a Slot (`~x`) or a Segment (`~~x`) (described below). |
56 | | - |
57 | | -If an expression matches LHS entirely, then it is rewritten to the pattern in the RHS |
58 | | -Segment (`~x`) and slot variables (`~~x`) on the RHS will substitute the result of the |
59 | | -matches found for these variables in the LHS. |
60 | | - |
61 | | -**Slot**: |
62 | | - |
63 | | -A Slot variable is written as `~x` and matches a single expression. `x` is the name of the variable. If a slot appears more than once in an LHS expression then expression matched at every such location must be equal (as shown by `isequal`). |
64 | | - |
65 | | -_Example:_ |
66 | | - |
67 | | -Simple rule to turn any `sin` into `cos`: |
68 | | - |
69 | | -```julia |
70 | | -julia> @syms a b c |
71 | | -(a, b, c) |
72 | | - |
73 | | -julia> r = @rule sin(~x) => cos(~x) |
74 | | -sin(~x) => cos(~x) |
75 | | - |
76 | | -julia> r(sin(1+a)) |
77 | | -cos((1 + a)) |
78 | | -``` |
79 | | - |
80 | | -A rule with 2 segment variables |
81 | | - |
82 | | -```julia |
83 | | -julia> r = @rule ~x - ~y => ~x + (-(~y)) |
84 | | -~x - ~y => ~x + -(~y) |
85 | | - |
86 | | -julia> r(a-2b) |
87 | | -(a + (-(2 * b))) |
88 | | -``` |
89 | | - |
90 | | -A rule that matches two of the same expressions: |
91 | | - |
92 | | -```julia |
93 | | -julia> r = @rule sin(~x)^2 + cos(~x)^2 => 1 |
94 | | -sin(~x) ^ 2 + cos(~x) ^ 2 => 1 |
95 | | - |
96 | | -julia> r(sin(2a)^2 + cos(2a)^2) |
97 | | -1 |
98 | | - |
99 | | -julia> r(sin(2a)^2 + cos(a)^2) |
100 | | -# nothing |
101 | | -``` |
102 | | - |
103 | | -**Segment**: |
104 | | - |
105 | | -A Segment variable is written as `~~x` and matches zero or more expressions in the |
106 | | -function call. |
107 | | - |
108 | | -_Example:_ |
109 | | - |
110 | | -This implements the distributive property of multiplication: `+(~~ys)` matches expressions |
111 | | -like `a + b`, `a+b+c` and so on. On the RHS `~~ys` presents as any old julia array. |
112 | | - |
113 | | -```julia |
114 | | -julia> r = @rule ~x * +((~~ys)) => sum(map(y-> ~x * y, ~~ys)); |
115 | | - |
116 | | -julia> r(2 * (a+b+c)) |
117 | | -((2 * a) + (2 * b) + (2 * c)) |
118 | | -``` |
119 | | - |
120 | | -**Predicates**: |
121 | | - |
122 | | -Predicates can be used on both `~x` and `~~x` by using the `~x::f` or `~~x::f`. |
123 | | -Here `f` can be any julia function. In the case of a slot the function gets a single |
124 | | -matched subexpression, in the case of segment, it gets an array of matched expressions. |
125 | | - |
126 | | -The predicate should return `true` if the current match is acceptable, and `false` |
127 | | -otherwise. |
128 | | - |
129 | | -```julia |
130 | | -julia> two_πs(x::Number) = abs(round(x/(2π)) - x/(2π)) < 10^-9 |
131 | | -two_πs (generic function with 1 method) |
132 | | - |
133 | | -julia> two_πs(x) = false |
134 | | -two_πs (generic function with 2 methods) |
135 | | - |
136 | | -julia> r = @rule sin(~~x + ~y::two_πs + ~~z) => sin(+(~~x..., ~~z...)) |
137 | | -sin(~(~x) + ~(y::two_πs) + ~(~z)) => sin(+(~(~x)..., ~(~z)...)) |
138 | | - |
139 | | -julia> r(sin(a+3π)) |
140 | | - |
141 | | -julia> r(sin(a+6π)) |
142 | | -sin(a) |
143 | | - |
144 | | -julia> r(sin(a+6π+c)) |
145 | | -sin((a + c)) |
146 | | -``` |
147 | | - |
148 | | -The predicate function gets an array of values if attached to a segment variable (`~~x`). |
149 | | - |
150 | | -### Associative-Commutative Rules |
151 | | -Given an expression `f(x, f(y, z, u), v, w)`, a `f` is said to be associative if the expression |
152 | | -is equivalent to `f(x, y, z, u, v, w)` and commutative if the order of arguments does not matter. |
153 | | -SymbolicUtils has a special `@acrule` macro meant for rules on functions which are associate |
154 | | -and commutative such as addition and multiplication of real and complex numbers. |
155 | | -```julia |
156 | | -julia> @syms x y |
157 | | -(x, y) |
158 | | - |
159 | | -julia> acr = @acrule((~y)^(~n) * ~y => (~y)^(~n+1)) |
160 | | -ACRule((~y) ^ ~n * ~y => (~y) ^ (~n + 1)) |
161 | | - |
162 | | -julia> acr(x^2 * y * x) |
163 | | -((x ^ 3) * y) |
164 | | -``` |
165 | | - |
166 | | -### RuleSets |
167 | | - |
168 | | -Rules are applied to an entire term, they do not see sub-terms |
169 | | -```julia |
170 | | -julia> using SymbolicUtils |
171 | | - |
172 | | -julia> @syms x y |
173 | | -(x, y) |
174 | | - |
175 | | -julia> r = @rule sin(~x) => cos(~x) |
176 | | -sin(~x) => cos(~x) |
177 | | - |
178 | | -julia> r(sin(sin(sin(y)))) |
179 | | -cos(sin(sin(y))) |
180 | | -``` |
181 | | -however, SymbolicUtils also defines a `RuleSet` type which stores a `Vector` of rules. `RuleSets` |
182 | | -when applied to terms will recursively walk through the expression and apply each of it's |
183 | | -consitituent rules until the term stops changing. |
184 | | -```julia |
185 | | -julia> R = RuleSet([r, @rule(~x + 1 => ~x - 1)]) |
186 | | -RuleSet(SymbolicUtils.AbstractRule[sin(~x) => cos(~x), ~x + 1 => ~x - 1]) |
187 | | - |
188 | | -julia> R(sin(sin(sin(x + 1)))) |
189 | | -cos(cos(cos((-1 + x)))) |
190 | | -``` |
191 | | -You can use the keyword argument `depth` to set a maximum number of recursions that a `RuleSet` is |
192 | | -allowed to do |
193 | | -```julia |
194 | | -julia> R(sin(sin(sin(x -1))), depth=2) |
195 | | -cos(cos(sin((x + -1)))) |
196 | | -``` |
197 | | - |
198 | | -## Interfacing with SymbolicUtils.jl |
199 | | - |
200 | | -This section is for Julia package developers who may want to use the `simplify` and rule rewriting system on their own expression types. |
201 | | - |
202 | | -Our intention is for SymbolicUtils to be useful even for packages with their own custom symbolic types which |
203 | | -differ from those offered by SymbolicUtils. To this end, SymbolicUtils provides an interface to convert expression |
204 | | -tree types which have |
205 | | -* an `operation`, (i.e. function to apply) |
206 | | -* `arguments` which the `operation` is applied to |
207 | | -* `variable` types which are the atoms from which the expression tree is built |
208 | | -* optionally, a type which should `typeof(operation(arguments...))` should return if it were to be run. |
209 | | - |
210 | | -SymbolicUtils uses a function `to_symbolic` to convert aribtrarty types to it's own internal types. |
211 | | - |
212 | | -The following methods should be defined for an expression tree type `T` with symbol types `S` to work |
213 | | -with SymbolicUtils.jl |
214 | | - |
215 | | -#### `istree(x::T)` |
216 | | - |
217 | | -Check if `x` represents an expression tree. If returns true, |
218 | | -it will be assumed that `operation(::T)` and `arguments(::T)` |
219 | | -methods are defined. Definining these three should allow use |
220 | | -of `simplify` on custom types. Optionally `symtype(x)` can be |
221 | | -defined to return the expected type of the symbolic expression. |
222 | | - |
223 | | -#### `operation(x::T)` |
224 | | - |
225 | | -Returns the operation (a function object) performed by an expression |
226 | | -tree. Called only if `istree(::T)` is true. Part of the API required |
227 | | -for `simplify` to work. Other required methods are `arguments` and `istree` |
228 | | - |
229 | | -#### `arguments(x::T)` |
230 | | - |
231 | | -Returns the arguments (a `Vector`) for an expression tree. |
232 | | -Called only if `istree(x)` is `true`. Part of the API required |
233 | | -for `simplify` to work. Other required methods are `operation` and `istree` |
234 | | - |
235 | | -#### `to_symbolic(x::S)` |
236 | | -Convert your variable type to a `SymbolicUtils.Sym`. Suppose you have |
237 | | -```julia |
238 | | -struct MySymbol |
239 | | - s::Symbol |
240 | | -end |
241 | | -``` |
242 | | -which could represent any type symbolically, then you would define |
243 | | -```julia |
244 | | -SymbolicUtils.to_symbolic(s::MySymbol) = SymbolicUtils.Sym(s.s) |
245 | | -``` |
246 | | - |
247 | | -### Optional |
248 | | - |
249 | | -#### `symtype(x)` |
250 | | - |
251 | | -The supposed type of values in the domain of x. Tracing tools can use this type to |
252 | | -pick the right method to run or analyse code. |
253 | | - |
254 | | -This defaults to `typeof(x)` if `x` is numeric, or `Any` otherwise. |
255 | | -For the types defined in this package, namely `T<:Symbolic{S}` it is `S`. |
256 | | - |
257 | | -Define this for your symbolic types if you want `simplify` to apply rules |
258 | | -specific to numbers (such as commutativity of multiplication). Or such |
259 | | -rules that may be implemented in the future. |
260 | | - |
261 | | -#### `promote_symtype(f, arg_symtypes...)` |
262 | | - |
263 | | -Returns the appropriate output type of applying `f` on arguments of type `arg_symtypes`. |
264 | | - |
265 | | -### Example |
266 | | - |
267 | | -Suppose you were feeling the temptations of type piracy and wanted to make a quick and dirty |
268 | | -symbolic library built on top of Julia's `Expr` type, e.g. |
269 | | - |
270 | | -```julia |
271 | | -for f ∈ [:+, :-, :*, :/, :^] #Note, this is type piracy! |
272 | | - @eval begin |
273 | | - Base.$f(x::Union{Expr, Symbol}, y::Number) = Expr(:call, $f, x, y) |
274 | | - Base.$f(x::Number, y::Union{Expr, Symbol}) = Expr(:call, $f, x, y) |
275 | | - Base.$f(x::Union{Expr, Symbol}, y::Union{Expr, Symbol}) = (Expr(:call, $f, x, y)) |
276 | | - end |
277 | | -end |
278 | | - |
279 | | - |
280 | | -julia> ex = 1 + (:x - 2) |
281 | | -:((+)(1, (-)(x, 2))) |
282 | | -``` |
283 | | -How can we use SymbolicUtils.jl to convert `ex` to `(-)(:x, 1)`? We simply implement `istree`, |
284 | | -`operation`, `arguments` and `to_symbolic` and we'll be off to the races: |
285 | | -```julia |
286 | | -using SymbolicUtils: Sym, istree, operation, arguments, to_symbolic |
287 | | - |
288 | | -SymbolicUtils.istree(ex::Expr) = ex.head == :call |
289 | | -SymbolicUtils.operation(ex::Expr) = ex.args[1] |
290 | | -SymbolicUtils.arguments(ex::Expr) = ex.args[2:end] |
291 | | -SymbolicUtils.to_symbolic(s::Symbol) = Sym(s) |
292 | | - |
293 | | -julia> simplify(ex) |
294 | | -(-1 + x) |
295 | | - |
296 | | -julia> dump(simplify(ex)) |
297 | | -Term{Any} |
298 | | - f: + (function of type typeof(+)) |
299 | | - arguments: Array{Any}((2,)) |
300 | | - 1: Int64 -1 |
301 | | - 2: Sym{Any} |
302 | | - name: Symbol x |
303 | | -``` |
304 | | -this thing returns a `Term{Any}`, but it's not hard to convert back to `Expr`: |
305 | | -```julia |
306 | | -to_expr(t::Term) = Expr(:call, operation(t), to_expr.(arguments(t))...) |
307 | | -to_expr(x) = x |
| 19 | +SymbolicUtils.jl provides various utilities for symbolic computing. |
308 | 20 |
|
309 | | -julia> to_expr(simplify(ex)) |
310 | | -:((+)(-1, x)) |
| 21 | +[](https://travis-ci.com/github/JuliaSymbolics/SymbolicUtils.jl) [](https://coveralls.io/github/JuliaSymbolics/SymbolicUtils.jl?branch=master) |
311 | 22 |
|
312 | | -julia> dump(ans) |
313 | | -Expr |
314 | | - head: Symbol call |
315 | | - args: Array{Any}((3,)) |
316 | | - 1: + (function of type typeof(+)) |
317 | | - 2: Int64 -1 |
318 | | - 3: Symbol x |
319 | | -``` |
| 23 | +[Symbols in SymbolicUtils](https://juliasymbolics.github.io/SymbolicUtils.jl/#creating_symbolic_expressions) carry type information. Operations on them propagate this information. [A rule-based rewriting language](https://juliasymbolics.github.io/SymbolicUtils.jl/#rule-based_rewriting) can be used to find subexpressions that satisfy arbitrary conditions and apply arbitrary transformations on the matches. The library also contains a set of useful [simplification](https://juliasymbolics.github.io/SymbolicUtils.jl/#simplification) rules for expressions of numeric symbols and numbers. These can be remixed and extended for special purposes. |
320 | 24 |
|
321 | | -Now suppose we actaully wanted all `Symbol`s to be treated as `Real` numbers. We can simply define |
322 | | -```julia |
323 | | -SymbolicUtils.symtype(s::Symbol) = Real |
| 25 | +If you are a Julia package develper in need of a rule rewriting system for your own types, have a look at the [interfacing guide](https://juliasymbolics.github.io/SymbolicUtils.jl/interface/). |
324 | 26 |
|
325 | | -julia> dump(simplify(ex)) |
326 | | -Term{Real} |
327 | | - f: + (function of type typeof(+)) |
328 | | - arguments: Array{Any}((2,)) |
329 | | - 1: Int64 -1 |
330 | | - 2: Sym{Real} |
331 | | - name: Symbol x |
332 | | -``` |
333 | | -and now all our analysis is able to figure out that the `Term`s are `Number`s. |
| 27 | +[**Go to the manual**](https://juliasymbolics.github.io/SymbolicUtils.jl/) |
334 | 28 |
|
335 | 29 | # Citations |
336 | 30 |
|
|
0 commit comments