Core Types & Trees
Expression Nodes
All expression trees are composed of AbstractNode subtypes:
Constant(value::Float64)— literal numeric valuesVariable(name::Symbol)— variable references (untyped)Variable(name::Symbol, type)— variable references (typed)FunctionNode(func::Symbol, children...)— function applications
Building Trees Manually
x = Variable(:x)
tree = FunctionNode(:+,
FunctionNode(:sin, x),
Constant(1.0)
)Type Queries
isconstant(node) # true for Constant
isvariable(node) # true for Variable
isfunction(node) # true for FunctionNode
isterminal(node) # true for Constant or Variable
vartype(node) # type of a Variable
istyped(node) # true if Variable has a type
children(node) # child nodes of a FunctionNode
arity(node) # number of childrenTree Utilities
Size and Structure
count_nodes(node) # Total node count
tree_depth(node) # Maximum depth
tree_size_stats(node) # Comprehensive statisticsTraversal
flatten_tree(node) # All nodes in pre-order
indexed_nodes(node) # (index, node) pairs
terminals(node) # Leaf nodes only
nonterminals(node) # Internal nodes onlyCollection
collect_variables(node) # Set of variable names
collect_constants(node) # Vector of constant values
collect_functions(node) # Vector of function symbolsManipulation
copy_tree(node) # Deep copy
replace_subtree(tree, old, new) # Substitute subtrees
map_tree(f, node) # Transform all nodes
get_subtree_at_index(node, i) # Access by index
random_subtree(node) # Random selection
random_subtree_index(node) # Random indexPrinting
node_to_string(node) # Human-readable: "(sin(x) + 1)"
node_to_latex(node) # LaTeX: "\sin(x) + 1"
print_tree(node) # Hierarchical ASCII view
tree_to_string_block(node) # Block formatTypes Reference
SymbolicOptimization.AbstractNode — Type
AbstractNodeAbstract base type for all nodes in symbolic expression trees.
Concrete subtypes:
Constant: Literal numeric valuesVariable: Named variable referencesFunctionNode: Function applications with children
SymbolicOptimization.Constant — Type
Constant <: AbstractNode
Constant(value::Float64)A constant (literal) numeric value in the expression tree.
Examples
c = Constant(3.14)
c.value # 3.14SymbolicOptimization.Variable — Type
Variable{T} <: AbstractNode
Variable(name::Symbol, type::T)
Variable(name::Symbol) # untypedA variable reference in the expression tree.
The type parameter T encodes the variable's type in typed grammars (e.g., Symbol for :Scalar, :Vector), or is Nothing for untyped grammars.
Examples
# Untyped variable
x = Variable(:x)
# Typed variable
ps = Variable(:ps, :Vector)
vartype(ps) # :VectorSymbolicOptimization.FunctionNode — Type
FunctionNode <: AbstractNode
FunctionNode(func::Symbol, children::Vector{AbstractNode})
FunctionNode(func::Symbol, children::AbstractNode...)A function application node with child nodes as arguments.
Examples
# Addition: x + y
add_node = FunctionNode(:+, Variable(:x), Variable(:y))
# Nested: sin(x + 1)
nested = FunctionNode(:sin, FunctionNode(:+, Variable(:x), Constant(1.0)))SymbolicOptimization.isconstant — Function
isconstant(node::AbstractNode) -> BoolReturn true if the node is a Constant.
SymbolicOptimization.isvariable — Function
isvariable(node::AbstractNode) -> BoolReturn true if the node is a Variable.
SymbolicOptimization.isfunction — Function
isfunction(node::AbstractNode) -> BoolReturn true if the node is a FunctionNode.
SymbolicOptimization.isterminal — Function
isterminal(node::AbstractNode) -> BoolReturn true if the node is a terminal (Constant or Variable).
SymbolicOptimization.vartype — Function
vartype(v::Variable) -> TReturn the type annotation of a variable, or nothing for untyped variables.
SymbolicOptimization.istyped — Function
istyped(v::Variable) -> BoolReturn true if the variable has a type annotation.
SymbolicOptimization.children — Function
children(node::AbstractNode) -> Vector{AbstractNode}Return the children of a node. Empty for terminals.
SymbolicOptimization.arity — Function
arity(node::AbstractNode) -> IntReturn the number of children (arity) of a node.
SymbolicOptimization.copy_tree — Function
copy_tree(node::AbstractNode) -> AbstractNodeCreate a deep copy of the expression tree.
Examples
original = FunctionNode(:+, Variable(:x), Constant(1.0))
copied = copy_tree(original)
original == copied # true
original === copied # false (different objects)SymbolicOptimization.count_nodes — Function
count_nodes(node::AbstractNode) -> IntCount the total number of nodes in the tree.
Examples
tree = FunctionNode(:+, Variable(:x), Constant(1.0))
count_nodes(tree) # 3SymbolicOptimization.tree_depth — Function
tree_depth(node::AbstractNode) -> IntCompute the maximum depth of the tree. Terminals have depth 1.
Examples
tree = FunctionNode(:sin, FunctionNode(:+, Variable(:x), Constant(1.0)))
tree_depth(tree) # 3SymbolicOptimization.tree_size_stats — Function
tree_size_stats(node::AbstractNode) -> NamedTupleReturn various size statistics about the tree.
Returns
nodes: Total node countdepth: Maximum depthterminals: Number of terminal nodesfunctions: Number of function nodesconstants: Number of constant nodesvariables: Number of variable nodesunique_vars: Number of unique variable namesunique_funcs: Number of unique function symbols
SymbolicOptimization.flatten_tree — Function
flatten_tree(node::AbstractNode) -> Vector{AbstractNode}Return all nodes in the tree in pre-order traversal (root first, then children recursively).
Examples
tree = FunctionNode(:+, Variable(:x), Constant(1.0))
nodes = flatten_tree(tree) # [FunctionNode(:+, ...), Variable(:x), Constant(1.0)]
length(nodes) # 3SymbolicOptimization.indexed_nodes — Function
indexed_nodes(node::AbstractNode) -> Vector{Tuple{Int, AbstractNode}}Return (index, node) pairs for all nodes in pre-order traversal.
Examples
tree = FunctionNode(:+, Variable(:x), Constant(1.0))
for (i, n) in indexed_nodes(tree)
println("$i: $n")
endSymbolicOptimization.terminals — Function
terminals(node::AbstractNode) -> Vector{AbstractNode}Return all terminal nodes (Constants and Variables) in the tree.
SymbolicOptimization.nonterminals — Function
nonterminals(node::AbstractNode) -> Vector{AbstractNode}Return all non-terminal nodes (FunctionNodes) in the tree.
SymbolicOptimization.collect_variables — Function
collect_variables(node::AbstractNode) -> Set{Symbol}Return the set of all variable names used in the tree.
Examples
tree = FunctionNode(:+, Variable(:x), FunctionNode(:*, Variable(:x), Variable(:y)))
collect_variables(tree) # Set([:x, :y])SymbolicOptimization.collect_constants — Function
collect_constants(node::AbstractNode) -> Vector{Float64}Return all constant values in the tree (with duplicates, in pre-order).
Examples
tree = FunctionNode(:+, Constant(1.0), FunctionNode(:*, Constant(2.0), Constant(1.0)))
collect_constants(tree) # [1.0, 2.0, 1.0]SymbolicOptimization.collect_functions — Function
collect_functions(node::AbstractNode) -> Vector{Symbol}Return all function symbols used in the tree (with duplicates, in pre-order).
Examples
tree = FunctionNode(:+, Variable(:x), FunctionNode(:+, Constant(1.0), Constant(2.0)))
collect_functions(tree) # [:+, :+]SymbolicOptimization.replace_subtree — Function
replace_subtree(tree, old_node, new_node) -> AbstractNodeReplace old_node (by identity, i.e., ===) with new_node in the tree. Returns a new tree; the original is not modified.
Examples
x = Variable(:x)
tree = FunctionNode(:+, x, Constant(1.0))
new_tree = replace_subtree(tree, x, Constant(2.0))
# new_tree is now: (+ 2.0 1.0)SymbolicOptimization.map_tree — Function
map_tree(f, node::AbstractNode) -> AbstractNodeApply function f to each node in the tree, building a new tree from the results. The function f receives each node and should return an AbstractNode.
The mapping is applied bottom-up: children are mapped first, then the parent.
Examples
# Double all constants
tree = FunctionNode(:+, Constant(1.0), Constant(2.0))
doubled = map_tree(tree) do node
node isa Constant ? Constant(node.value * 2) : node
end
# doubled is now: (+ 2.0 4.0)SymbolicOptimization.get_subtree_at_index — Function
get_subtree_at_index(node::AbstractNode, idx::Int) -> AbstractNodeGet the subtree at the given index (1-based, pre-order traversal).
Examples
tree = FunctionNode(:+, Variable(:x), Constant(1.0))
get_subtree_at_index(tree, 1) # The FunctionNode itself
get_subtree_at_index(tree, 2) # Variable(:x)
get_subtree_at_index(tree, 3) # Constant(1.0)SymbolicOptimization.random_subtree — Function
random_subtree(node::AbstractNode; rng=Random.GLOBAL_RNG) -> AbstractNodeSelect a random subtree from the tree.
Examples
tree = FunctionNode(:+, Variable(:x), Constant(1.0))
subtree = random_subtree(tree) # Could be any of the 3 nodesSymbolicOptimization.random_subtree_index — Function
random_subtree_index(node::AbstractNode; rng=Random.GLOBAL_RNG) -> IntSelect a random subtree index (1-based, pre-order).
SymbolicOptimization.node_to_string — Function
node_to_string(node::AbstractNode; digits::Int=3) -> StringConvert an expression tree to a human-readable string representation.
Infix operators (+, -, *, /, ^) are printed in infix notation. Other functions use prefix notation: f(arg1, arg2, ...).
Examples
tree = FunctionNode(:+, Variable(:x), FunctionNode(:*, Constant(2.0), Variable(:y)))
node_to_string(tree) # "(x + (2.0 * y))"
tree2 = FunctionNode(:sin, Variable(:x))
node_to_string(tree2) # "sin(x)"SymbolicOptimization.node_to_latex — Function
node_to_latex(node::AbstractNode; digits::Int=3) -> StringConvert an expression tree to LaTeX math notation.
Examples
tree = FunctionNode(:/,
FunctionNode(:+, Variable(:x), Constant(1.0)),
FunctionNode(:sqrt, Variable(:y))
)
node_to_latex(tree) # "\frac{x + 1}{\sqrt{y}}"SymbolicOptimization.print_tree — Function
print_tree(node::AbstractNode; io::IO=stdout, indent::Int=0)Print a tree in a hierarchical format showing the structure.
Examples
tree = FunctionNode(:+, Variable(:x), FunctionNode(:*, Constant(2.0), Variable(:y)))
print_tree(tree)
# Output:
# +
# x
# *
# 2.0
# ySymbolicOptimization.tree_to_string_block — Function
tree_to_string_block(node::AbstractNode) -> StringReturn the tree visualization as a string.