StructuredExpression
example
StructuredExpression
s allow you to specify a predefined structure for an expression that exists outside of the regular AbstractExpressionNode
objects which store expressions as trees.
Let's look at an example:
using DynamicExpressions, Random
First, we will create some normal Expression
objects.
operators = OperatorEnum(; unary_operators=(cos, exp), binary_operators=(+, -, *, /))
variable_names = ["x", "y"]
x = Expression(Node{Float64}(; feature=1); operators, variable_names)
y = Expression(Node{Float64}(; feature=2); operators, variable_names)
typeof(x)
Expression{Float64, Node{Float64}, @NamedTuple{operators::OperatorEnum{Tuple{typeof(+), typeof(-), typeof(*), typeof(/)}, Tuple{typeof(cos), typeof(exp)}}, variable_names::Vector{String}}}
Any AbstractExpression
, such as this Expression
object, can be composed together using standard Julia math operations. For example, let's some complex expressions from these:
f = x * x - cos(2.5f0 * y + -0.5f0)
g = exp(2.0 - y * y)
f, g
((x * x) - cos((2.5 * y) + -0.5), exp(2.0 - (y * y)))
We can then create a StructuredExpression
from these two expressions. This is a composite AbstractExpression
object that composes multiple expressions during evaluation.
ex = StructuredExpression(
(; f, g); structure=nt -> nt.f + nt.g, operators, variable_names
)
ex
((x * x) - cos((2.5 * y) + -0.5)) + exp(2.0 - (y * y))
Note that this is displayed as a single tree, with the +
operator used to combine them. Despite this, the expression is not actually stored with the +
operator in an AbstractExpressionNode
.
By default, using get_tree
will evaluate the result of nt.f + nt.g
. This let's us use things like the regular operations available to AbstractExpressionNode
s:
length(get_tree(ex))
17
Next, let's try to evaluate this on some random data:
rng = Random.MersenneTwister(0)
X = randn(rng, Float64, 2, 5)
X
2×5 Matrix{Float64}:
-0.758731 0.0486897 -0.645534 -1.17424 0.816649
0.0324972 0.426554 0.160479 -0.859058 -1.3611
Followed by the evaluation. Since we have stored the operators directly in the expression object, we do not need to pass the operators explicitly. Evaluation of an AbstractExpression
is set up to forward through get_tree
, so this will work automatically.
ex(X)
5-element Vector{Float64}:
7.043334231133034
5.3183714105026
6.622782405442065
5.791864223824312
2.549782401338956
Which we can verify against the individual expressions:
f(X) + g(X)
5-element Vector{Float64}:
7.043334231133034
5.3183714105026
6.622782405442065
5.791864223824312
2.549782401338956
This page was generated using Literate.jl.