Skip to content

Commit fbf8eb1

Browse files
author
Alessandro
committed
added docs
1 parent afa739a commit fbf8eb1

File tree

2 files changed

+100
-2
lines changed

2 files changed

+100
-2
lines changed

README.md

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,99 @@ Its purpose is to provide a shared interface between various symbolic programmin
55
[SymbolicUtils.jl](https://github.com/JuliaSymbolics/SymbolicUtils.jl), [Symbolics.jl](https://github.com/JuliaSymbolics/Symbolics.jl) and [Metatheory.jl](https://github.com/0x0f0f0f/Metatheory.jl).
66

77
## Docs
8-
TODO
8+
You should define the following methods for an expression tree type `T` with symbol types `S` to work
9+
with TermInterface.jl, and therefore with [SymbolicUtils.jl](https://github.com/JuliaSymbolics/SymbolicUtils.jl)
10+
and [Metatheory.jl](https://github.com/0x0f0f0f/Metatheory.jl).
11+
12+
#### `isterm(x::T)` or `isterm(x::Type{T})`
13+
14+
Check if `x` represents an expression tree. If returns true,
15+
it will be assumed that `gethead(::T)` and `getargs(::T)`
16+
methods are defined. Definining these three should allow use
17+
of `SymbolicUtils.simplify` on custom types. Optionally `symtype(x)` can be
18+
defined to return the expected type of the symbolic expression.
19+
20+
#### `gethead(x::T)`
21+
22+
Returns the head (a function object) performed by an expression
23+
tree. Called only if `isterm(::T)` is true. Part of the API required
24+
for `simplify` to work. Other required methods are `getargs` and `isterm`
25+
26+
#### `getargs(x::T)`
27+
28+
Returns the arguments (a `Vector`) for an expression tree.
29+
Called only if `isterm(x)` is `true`. Part of the API required
30+
for `simplify` to work. Other required methods are `gethead` and `isterm`
31+
32+
In addition, the methods for `Base.hash` and `Base.isequal` should also be implemented by the types for the purposes of substitution and equality matching respectively.
33+
34+
#### `similarterm(t::MyType, f, args; type=T, metadata=nothing)` or `similarterm(t::Type{MyType}, f, args; type=T, metadata=nothing)`
35+
36+
Construct a new term with the operation `f` and arguments `args`, the term should be similar to `t` in type. if `t` is a `SymbolicUtils.Term` object a new Term is created with the same symtype as `t`. If not, the result is computed as `f(args...)`. Defining this method for your term type will reduce any performance loss in performing `f(args...)` (esp. the splatting, and redundant type computation). T is the symtype of the output term. You can use `SymbolicUtils.promote_symtype` to infer this type.
37+
38+
### Optional
39+
40+
#### `symtype(x)`
41+
42+
The supposed type of values in the domain of x. Tracing tools can use this type to
43+
pick the right method to run or analyse code.
44+
45+
This defaults to `typeof(x)` if `x` is numeric, or `Any` otherwise.
46+
For the types defined in this SymbolicUtils.jl, namely `T<:Symbolic{S}` it is `S`.
47+
48+
Define this for your symbolic types if you want `SymbolicUtils.simplify` to apply rules
49+
specific to numbers (such as commutativity of multiplication). Or such
50+
rules that may be implemented in the future.
51+
52+
## Example
53+
54+
Suppose you were feeling the temptations of type piracy and wanted to make a quick and dirty
55+
symbolic library built on top of Julia's `Expr` type, e.g.
56+
57+
```julia:piracy1
58+
for f ∈ [:+, :-, :*, :/, :^] #Note, this is type piracy!
59+
@eval begin
60+
Base.$f(x::Union{Expr, Symbol}, y::Number) = Expr(:call, $f, x, y)
61+
Base.$f(x::Number, y::Union{Expr, Symbol}) = Expr(:call, $f, x, y)
62+
Base.$f(x::Union{Expr, Symbol}, y::Union{Expr, Symbol}) = (Expr(:call, $f, x, y))
63+
end
64+
end
65+
66+
Base.zero(t::Expr) = 0
67+
68+
ex = 1 + (:x - 2)
69+
```
70+
71+
72+
How can we use SymbolicUtils.jl to convert `ex` to `(-)(:x, 1)`? We simply implement `isterm`,
73+
`head`, `arguments` and we'll be able to do rule-based rewriting on `Expr`s:
74+
```julia:piracy2
75+
using TermInterface
76+
using SymbolicUtils
77+
78+
79+
TermInterface.isterm(ex::Expr) = ex.head == :call
80+
TermInterface.gethead(ex::Expr) = ex.args[1]
81+
TermInterface.getargs(ex::Expr) = ex.args[2:end]
82+
TermInterface.similarterm(x::Type{Expr}, head, args; type=nothing, metadata=nothing) = Expr(:call, head, args...)
83+
84+
@rule(~x => ~x - 1)(ex)
85+
```
86+
87+
However, this is not enough to get SymbolicUtils to use its own algebraic simplification system on `Expr`s:
88+
```julia:piracy3
89+
simplify(ex)
90+
```
91+
92+
The reason that the expression was not simplified is that the expression tree is untyped, so SymbolicUtils
93+
doesn't know what rules to apply to the expression. To mimic the behaviour of most computer algebra
94+
systems, the simplest thing to do would be to assume that all `Expr`s are of type `Number`:
95+
96+
```julia:piracy4
97+
TermInterface.symtype(s::Expr) = Real
98+
TermInterface.symtype(s::Sym) = Real
99+
100+
simplify(ex)
101+
```
102+
103+
Now SymbolicUtils is able to apply the `Number` simplification rule to `Expr`.

src/TermInterface.jl

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ export isterm
1515
symtype(x)
1616
1717
Returns the symbolic type of `x`. By default this is just `typeof(x)`.
18+
Define this for your symbolic types if you want `SymbolicUtils.simplify` to apply rules
19+
specific to numbers (such as commutativity of multiplication). Or such
20+
rules that may be implemented in the future.
1821
"""
1922
function symtype(x)
2023
typeof(x)
@@ -80,7 +83,7 @@ end
8083

8184

8285
"""
83-
similarterm(x, head, args, type; metadata=nothing)
86+
similarterm(x, head, args; type=nothing, metadata=nothing)
8487
8588
Returns a term that is in the same closure of types as `typeof(x)`,
8689
with `head` as the head and `args` as the arguments, `type` as the symtype

0 commit comments

Comments
 (0)