Skip to content

Commit 70bd87e

Browse files
committed
Update README with tensor example
1 parent 4eb1bee commit 70bd87e

File tree

1 file changed

+54
-7
lines changed

1 file changed

+54
-7
lines changed

README.md

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,20 +6,19 @@
66

77
[![](https://img.shields.io/badge/docs-dev-blue.svg)](https://symbolicml.org/DynamicExpressions.jl/dev) [![CI](https://github.com/SymbolicML/DynamicExpressions.jl/actions/workflows/CI.yml/badge.svg)](https://github.com/SymbolicML/DynamicExpressions.jl/actions/workflows/CI.yml) [![Coverage Status](https://coveralls.io/repos/github/SymbolicML/DynamicExpressions.jl/badge.svg?branch=master)](https://coveralls.io/github/SymbolicML/DynamicExpressions.jl?branch=master)
88

9-
DynamicExpressions.jl is the backbone of
10-
[SymbolicRegression.jl](https://github.com/MilesCranmer/SymbolicRegression.jl) and
9+
DynamicExpressions.jl is the backbone of [SymbolicRegression.jl](https://github.com/MilesCranmer/SymbolicRegression.jl) and
1110
[PySR](https://github.com/MilesCranmer/PySR).
1211

1312
</div>
1413

1514
## Summary
1615

17-
A dynamic expression is a snippet of code that can change throughout runtime - compilation is not possible! DynamicExpressions.jl does the following:
16+
A dynamic expression is a snippet of code that can change throughout runtime - compilation is not possible! **DynamicExpressions.jl does the following:**
1817
1. Defines an enum over user-specified operators.
1918
2. Using this enum, it defines a [very lightweight and type-stable data structure](https://symbolicml.org/DynamicExpressions.jl/dev/types/#DynamicExpressions.EquationModule.Node) for arbitrary expressions.
2019
3. It then generates specialized [evaluation kernels](https://github.com/SymbolicML/DynamicExpressions.jl/blob/fe8e6dfa160d12485fb77c226d22776dd6ed697a/src/EvaluateEquation.jl#L29-L66) for the space of potential operators.
2120
4. It also generates kernels for the [first-order derivatives](https://github.com/SymbolicML/DynamicExpressions.jl/blob/fe8e6dfa160d12485fb77c226d22776dd6ed697a/src/EvaluateEquationDerivative.jl#L139-L175), using [Zygote.jl](https://github.com/FluxML/Zygote.jl).
22-
5. It can also operate on arbitrary other types (vectors, tensors, symbols, strings, etc.) - see last part below.
21+
5. DynamicExpressions.jl can also operate on arbitrary other types (vectors, tensors, symbols, strings, or even unions) - see last part below.
2322

2423
It also has import and export functionality with [SymbolicUtils.jl](https://github.com/JuliaSymbolics/SymbolicUtils.jl), so you can move your runtime expression into a CAS!
2524

@@ -151,12 +150,14 @@ result, grad, did_finish = eval_diff_tree_array(expression, X, operators, featur
151150

152151
> Does this work for only scalar operators on real numbers, or will it work for `MyCrazyType`?
153152
154-
I'm so glad you asked. `DynamicExpressions.jl` actually will work for **arbitrary types**! However, to work on operators other than real scalars, you need to use the `GenericOperatorEnum` instead of the normal `OperatorEnum`. Let's try it with strings!
153+
I'm so glad you asked. `DynamicExpressions.jl` actually will work for **arbitrary types**! However, to work on operators other than real scalars, you need to use the `GenericOperatorEnum <: AbstractOperatorEnum` instead of the normal `OperatorEnum`. Let's try it with strings!
155154

156155
```julia
157156
x1 = Node(String; feature=1)
158157
```
159-
This node, will be used to index input data (whatever it may be) with `selectdim(data, 1, feature)`. Let's now define some operators to use:
158+
159+
This node, will be used to index input data (whatever it may be) with either `data[feature]` (1D abstract arrays) or `selectdim(data, 1, feature)` (ND abstract arrays). Let's now define some operators to use:
160+
160161
```julia
161162
my_string_func(x::String) = "Hello $x"
162163

@@ -165,14 +166,60 @@ operators = GenericOperatorEnum(;
165166
unary_operators=[my_string_func],
166167
extend_user_operators=true)
167168
```
169+
168170
Now, let's create an expression:
171+
169172
```julia
170173
tree = x1 * " World!"
171174
tree(["Hello", "Me?"])
172175
# Hello World!
173176
```
177+
174178
So indeed it works for arbitrary types. It is a bit slower due to the potential for type instability, but it's not too bad:
179+
175180
```julia
176-
@btime tree(["Hello", "Me?"]
181+
@btime tree(["Hello", "Me?"])
177182
# 1738 ns
178183
```
184+
185+
## Tensors
186+
187+
> Does this work for tensors, or even unions of scalars and tensors?
188+
189+
Also yes! Let's see:
190+
191+
```julia
192+
using DynamicExpressions
193+
194+
T = Union{Float64,Vector{Float64}}
195+
196+
c1 = Node(T; val=0.0) # Scalar constant
197+
c2 = Node(T; val=[1.0, 2.0, 3.0]) # Vector constant
198+
x1 = Node(T; feature=1)
199+
200+
# Some operators on tensors (multiple dispatch can be used for different behavior!)
201+
vec_add(x, y) = x .+ y
202+
vec_square(x) = x .* x
203+
204+
# Set up an operator enum:
205+
operators = GenericOperatorEnum(;binary_operators=[vec_add], unary_operators=[vec_square], extend_user_operators=true)
206+
207+
# Construct the expression:
208+
tree = vec_add(vec_add(vec_square(x1), c2), c1)
209+
210+
X = [[-1.0, 5.2, 0.1], [0.0, 0.0, 0.0]]
211+
212+
# Evaluate!
213+
tree(X) # [2.0, 29.04, 3.01]
214+
```
215+
216+
Note that if an operator is not defined for the particular input, `nothing` will be returned instead.
217+
218+
This is all still pretty fast, too:
219+
220+
```julia
221+
@btime tree(X)
222+
# 2,949 ns
223+
@btime eval(:(vec_add(vec_add(vec_square(X[1]), [1.0, 2.0, 3.0]), 0.0)))
224+
# 115,000 ns
225+
```

0 commit comments

Comments
 (0)