Skip to content

Commit e562979

Browse files
committed
enhance interface docs and tests
1 parent d56f132 commit e562979

File tree

2 files changed

+49
-12
lines changed

2 files changed

+49
-12
lines changed

README.md

Lines changed: 40 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -201,11 +201,16 @@ This section is for Julia package developers who may want to use the `simplify`
201201

202202
Our intention is for SymbolicUtils to be useful even for packages with their own custom symbolic types which
203203
differ from those offered by SymbolicUtils. To this end, SymbolicUtils provides an interface to convert expression
204-
trees which have a an `operation`, (i.e. function to apply), `arguments` which the `operation` is applied to, and
205-
optionally, a type which should `typeof(operation(arguments...))` should return if it were to be run.
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.
206209

210+
SymbolicUtils uses a function `to_symbolic` to convert aribtrarty types to it's own internal types.
207211

208-
The following functions should be defined for an expression tree type `T` to work with SymbolicUtils.jl
212+
The following methods should be defined for an expression tree type `T` with symbol types `S` to work
213+
with SymbolicUtils.jl
209214

210215
#### `istree(x::T)`
211216

@@ -227,6 +232,17 @@ Returns the arguments (a `Vector`) for an expression tree.
227232
Called only if `istree(x)` is `true`. Part of the API required
228233
for `simplify` to work. Other required methods are `operation` and `istree`
229234

235+
#### `to_symbolic(x::S)`
236+
Convert your variable type to a `SymbolicUtils.Variable`. 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.Variable(s.s)
245+
```
230246

231247
### Optional
232248

@@ -246,11 +262,11 @@ rules that may be implemented in the future.
246262

247263
Returns the appropriate output type of applying `f` on arguments of type `arg_symtypes`.
248264

249-
250265
### Example
251266

252267
Suppose you were feeling the temptations of type piracy and wanted to make a quick and dirty
253268
symbolic library built on top of Julia's `Expr` type, e.g.
269+
254270
```julia
255271
for f [:+, :-, :*, :/, :^] #Note, this is type piracy!
256272
@eval begin
@@ -265,24 +281,25 @@ julia> ex = 1 + (:x - 2)
265281
:((+)(1, (-)(x, 2)))
266282
```
267283
How can we use SymbolicUtils.jl to convert `ex` to `(-)(:x, 1)`? We simply implement `istree`,
268-
`operation` and `arguments` and we'll be off to the races:
284+
`operation`, `arguments` and `to_symbolic` and we'll be off to the races:
269285
```julia
270-
using SymbolicUtils: istree, operation, arguments
286+
using SymbolicUtils: Variable, istree, operation, arguments, to_symbolic
271287

272288
SymbolicUtils.istree(ex::Expr) = ex.head == :call
273289
SymbolicUtils.operation(ex::Expr) = ex.args[1]
274290
SymbolicUtils.arguments(ex::Expr) = ex.args[2:end]
275-
291+
SymbolicUtils.to_symbolic(s::Symbol) = Variable(s)
276292

277293
julia> simplify(ex)
278294
(-1 + x)
279295

280-
julia> dump(ans)
296+
julia> dump(simplify(ex))
281297
Term{Any}
282298
f: + (function of type typeof(+))
283299
arguments: Array{Any}((2,))
284300
1: Int64 -1
285-
2: Symbol x
301+
2: Variable{Any}
302+
name: Symbol x
286303
```
287304
this thing returns a `Term{Any}`, but it's not hard to convert back to `Expr`:
288305
```julia
@@ -300,3 +317,17 @@ Expr
300317
2: Int64 -1
301318
3: Symbol x
302319
```
320+
321+
Now suppose we actaully wanted all `Symbol`s to be treated as `Real` numbers. We can simply define
322+
```
323+
SymbolicUtils.symtype(s::Symbol) = Real
324+
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: Variable{Real}
331+
name: Symbol x
332+
```
333+
and now all our analysis is able to figure out that the `Term`s are `Number`s.

test/interface.jl

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
using SymbolicUtils, Test
2-
using SymbolicUtils: Term, to_symbolic, istree, operation, arguments
2+
using SymbolicUtils: Term, Variable, to_symbolic, istree, operation, arguments
33

44
SymbolicUtils.istree(ex::Expr) = ex.head == :call
55
SymbolicUtils.operation(ex::Expr) = ex.args[1]
66
SymbolicUtils.arguments(ex::Expr) = ex.args[2:end]
7+
SymbolicUtils.to_symbolic(s::Symbol) = Variable(s)
78

89
for f [:+, :-, :*, :/, :^]
910
@eval begin
@@ -15,10 +16,15 @@ end
1516

1617
ex = 1 + (:x - 2)
1718

18-
@test to_symbolic(ex) == Term{Any}(+, [1, Term{Any}(-, [:x, 2])])
19+
@test to_symbolic(ex) == Term{Any}(+, [1, Term{Any}(-, [Variable{Any}(:x), 2])])
1920
@test simplify(ex) == to_symbolic(-1 + :x)
2021

2122
to_expr(t::Term) = Expr(:call, operation(t), to_expr.(arguments(t))...)
23+
to_expr(s::Variable) = s.name
2224
to_expr(x) = x
2325

24-
to_expr(simplify(ex))
26+
@test to_expr(simplify(ex)) == Expr(:call, +, -1, :x)
27+
28+
SymbolicUtils.symtype(::Symbol) = Number
29+
30+

0 commit comments

Comments
 (0)