Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
4871734
Prototype arena-backed ArenaNode
MilesCranmerBot Feb 1, 2026
5291356
Add reusable ArenaCursor preorder traversal
MilesCranmerBot Feb 1, 2026
93c2422
Add postfix stack-based utilities inspired by symbolic_regression.rs
MilesCranmerBot Feb 1, 2026
b5b4116
Add postfix validity check
MilesCranmerBot Feb 1, 2026
79b6fe1
ArenaNode: postfix roundtrip + minimal commutative rewrite
MilesCranmerBot Feb 1, 2026
9285357
docs/tests: clarify postfix utilities are for roundtrips only
MilesCranmerBot Feb 1, 2026
3e36294
ArenaNode: allow cross-arena set_child!/set_children! via copy
MilesCranmerBot Feb 1, 2026
7dcd653
ArenaNode: traversal-based postfix emit + cursor-based rewrite
MilesCranmerBot Feb 2, 2026
c6f37f3
fix: stabilize ArenaNode prototype traversal and tests
MilesCranmerBot Feb 20, 2026
04e763d
fix: stabilize ArenaNode follow-up
MilesCranmerBot Jun 9, 2026
6b91ec7
fix: evaluate ArenaNode directly
MilesCranmerBot Jun 10, 2026
1427d4a
fix: stabilize ArenaNode CI follow-up
MilesCranmerBot Jun 10, 2026
fb799dd
test: expand ArenaNode interface coverage
MilesCranmerBot Jun 10, 2026
a4b8649
chore: restore compat bounds
MilesCranmerBot Jun 10, 2026
6b2aaed
style: format ArenaNode follow-up
MilesCranmerBot Jun 10, 2026
b790948
fix: revert is_constant rewrite
MilesCranmerBot Jun 10, 2026
3a062a9
test(ArenaNode): add Expression interface and derivative tests
MilesCranmerBot Jun 10, 2026
694d46a
fix: stabilize ArenaNode follow-up CI
MilesCranmerBot Jun 11, 2026
f90e4f6
fix: address ArenaNode review cleanup
MilesCranmerBot Jun 11, 2026
a6f8a01
fix: stabilize ArenaNode CI dependencies
MilesCranmerBot Jun 11, 2026
92d4eef
fix: remove ArenaNode postfix debug utilities
MilesCranmerBot Jun 11, 2026
66a04c6
fix: keep LoweredCodeUtils test bound tight
MilesCranmerBot Jun 11, 2026
39658a7
fix: allow Julia 1.10 JET test compat
MilesCranmerBot Jun 11, 2026
1ad9560
fix: stabilize ArenaNode CI checks
MilesCranmerBot Jun 11, 2026
0f5e5f1
fix: raise SymbolicUtils lower bound
MilesCranmerBot Jun 11, 2026
f91cc9b
fix: constrain downgrade test dependencies
MilesCranmerBot Jun 11, 2026
1dbe254
fix: exclude uninstrumented extension coverage
MilesCranmerBot Jun 11, 2026
cd36acf
fix: address ArenaNode review comments
MilesCranmerBot Jun 11, 2026
782c962
fix: restore default Aqua checks
MilesCranmerBot Jun 11, 2026
811dffd
fix: simplify get_child integer dispatch
MilesCranmerBot Jun 11, 2026
1879638
feat: optimize ArenaNode storage and copies
MilesCranmerBot Jun 11, 2026
7b32c7b
fix: speed up ArenaNode traversals
MilesCranmerBot Jun 12, 2026
70c81a6
feat: add iterative ArenaNode eval
MilesCranmerBot Jun 12, 2026
c426828
fix: stabilize ArenaNode tests
MilesCranmerBot Jun 12, 2026
e6133a3
feat: add buffered ArenaNode plan evaluator
MilesCranmerBot Jun 12, 2026
74438bd
refactor(ArenaNode): generalize buffered plan evaluation to arbitrary…
MilesCranmer Jun 12, 2026
bd86ba9
test: revert Aqua workaround (belongs in a separate PR)
MilesCranmer Jun 12, 2026
5534208
fix(ArenaNode): close review findings on safety and Node parity
MilesCranmer Jun 12, 2026
3118155
refactor(ArenaNode): bundle plan-eval state into _PlanState/_PlanRegs
MilesCranmer Jun 12, 2026
4311a33
refactor(ArenaNode): self-documenting variable names
MilesCranmer Jun 12, 2026
61a5c3d
refactor(ArenaNode): address review comments
MilesCranmer Jun 12, 2026
f4d7ddd
refactor(ArenaNode): flatten _exec_op! into guard-clause helpers
MilesCranmer Jun 12, 2026
fc73362
refactor(ArenaNode): replace out() thunk with a plain output view bin…
MilesCranmer Jun 12, 2026
622a787
refactor(ArenaNode): share child attachment via _resolve_child_index!
MilesCranmer Jun 12, 2026
90cf0ea
refactor(ArenaNode): single-source the planner/executor slot policy
MilesCranmer Jun 12, 2026
b6f6970
refactor(ArenaNode): named raw accessors instead of getfield; restore…
MilesCranmer Jun 12, 2026
106db88
refactor(ArenaNode): name the descriptor bit operations
MilesCranmer Jun 12, 2026
6188223
perf(ArenaNode): recover base validation cost without losing ok parity
MilesCranmer Jun 12, 2026
bd0a2c7
refactor(ArenaNode): OperandKind enum for descriptor kinds
MilesCranmer Jun 12, 2026
7d95417
Revert "refactor(ArenaNode): OperandKind enum for descriptor kinds"
MilesCranmer Jun 12, 2026
8fb31a2
docs(ArenaNode): plain comments instead of docstrings on internals
MilesCranmer Jun 12, 2026
243a5f1
style(ArenaNode): drop ceremonial @inline; flatten repeated returns
MilesCranmer Jun 12, 2026
6695e1c
perf(ArenaNode): keep @inline on setproperty!
MilesCranmer Jun 12, 2026
512a544
bench(ArenaNode): also measure the unbuffered paths
MilesCranmer Jun 12, 2026
5fb4f9e
refactor(ArenaNode): split the plan evaluator into ArenaNodeEval.jl
MilesCranmer Jun 12, 2026
a3fdbbd
fix(ArenaNode): CI fixes, override deletion, facade poison guard, fas…
MilesCranmer Jun 12, 2026
1bf85b2
test(ArenaNode): Supposition invariants
MilesCranmer Jun 12, 2026
2f86a2a
perf(ArenaNode): direct entry recursion for hash; flat count_constant…
MilesCranmer Jun 12, 2026
5280bb5
perf(ArenaNode): entry-level tree_mapreduce skeleton
MilesCranmer Jun 12, 2026
ade5015
perf(ArenaNode): entry-level recursion for non-compact any
MilesCranmer Jun 12, 2026
087a0ad
docs(ArenaNode): record why tree_mapreduce keeps recursion
MilesCranmer Jun 12, 2026
02b2aed
refactor(ArenaNode): one body for compact and non-compact any
MilesCranmer Jun 12, 2026
9bb37c6
perf(ArenaNode): _foreach_node iteration driver; flat ==; faster cons…
MilesCranmer Jun 12, 2026
6fda350
test(ArenaNode): avoid Data.OneOf in the mutation generator
MilesCranmer Jun 12, 2026
25276ff
test: raise downgraded test dependency bounds
MilesCranmerBot Jun 13, 2026
60d0023
fix: address ArenaNode API regressions
MilesCranmerBot Jun 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ Optim = "1, 2"
NLSolversBase = "7, 8"
PrecompileTools = "1.2.1"
Reexport = "1.2.2"
SymbolicUtils = "4"
SymbolicUtils = "4.35"
Zygote = "0.7"
julia = "1.10"
Random = "1"
Expand Down
12 changes: 8 additions & 4 deletions ext/DynamicExpressionsLoopVectorizationExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -211,12 +211,16 @@ function deg2_r0_eval(
end

# Interface with Bumper.jl
function bumper_kern!(
@generated function bumper_kern!(
op::F, cumulators::Tuple{Vararg{Any,degree}}, ::EvalOptions{true,true,early_exit}
) where {F,degree,early_exit}
cumulator_1 = first(cumulators)
@turbo @. cumulator_1 = op(cumulators...)
return cumulator_1
quote
Base.Cartesian.@nexprs($degree, i -> cumulator_i = cumulators[i])
@turbo for j in eachindex(cumulator_1)
cumulator_1[j] = Base.Cartesian.@ncall($degree, op, i -> cumulator_i[j])
end
return cumulator_1
end
end

end
89 changes: 89 additions & 0 deletions scripts/bench_arenanode_eval.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
using DynamicExpressions
using DynamicExpressions: EvalOptions, ArrayBuffer
using Random: MersenneTwister

include(joinpath(@__DIR__, "..", "test", "tree_gen_utils.jl"))

const AN = DynamicExpressions.ArenaNodeModule

operators = OperatorEnum(1 => (cos, exp), 2 => (+, -, *, /))
const T = Float64
nfeat = 5
n = 1_000
rng = MersenneTwister(0)
X = randn(rng, T, nfeat, n)
buf = zeros(T, 64, n)

function bench(trees, X, operators, buffer; reps=300)
best = Inf
for _ in 1:reps
t0 = time_ns()
for tree in trees
buffer.index[] = 0
eval_tree_array(tree, X, operators; eval_options=EvalOptions(; buffer))
end
best = min(best, (time_ns() - t0) / length(trees))
end
return best
end

function allocs_per_eval(tree, X, operators, buffer)
@allocated(eval_tree_array(tree, X, operators; eval_options=EvalOptions(; buffer)))
end

for treesize in (7, 15, 31)
trees = [gen_random_tree_fixed_size(treesize, operators, nfeat, T) for _ in 1:50]
atrees = [convert(AN.ArenaNode{T,2}, t) for t in trees]
buffer = ArrayBuffer(buf, Ref(0))

# correctness sanity: both paths must agree
for (t, a) in zip(trees, atrees)
buffer.index[] = 0
yn, okn = eval_tree_array(t, X, operators; eval_options=EvalOptions(; buffer))
buffer.index[] = 0
ya, oka = eval_tree_array(a, X, operators; eval_options=EvalOptions(; buffer))
okn == oka || error("ok mismatch at size $treesize")
okn && (yn ≈ ya || error("value mismatch at size $treesize"))
end

# warmup
bench(trees, X, operators, buffer; reps=3)
bench(atrees, X, operators, buffer; reps=3)

t_node = bench(trees, X, operators, buffer)
t_arena = bench(atrees, X, operators, buffer)
buffer.index[] = 0
a_node = allocs_per_eval(trees[1], X, operators, buffer)
buffer.index[] = 0
a_arena = allocs_per_eval(atrees[1], X, operators, buffer)
println(
"n=$treesize Node: $(round(t_node/1e3; digits=2))us " *
"ArenaNode: $(round(t_arena/1e3; digits=2))us " *
"ratio=$(round(t_arena/t_node; digits=3)) " *
"allocs/eval Node=$a_node Arena=$a_arena",
)

bench_nobuf(trees) = begin
best = Inf
for _ in 1:300
t0 = time_ns()
for tree in trees
eval_tree_array(tree, X, operators)
end
best = min(best, (time_ns() - t0) / length(trees))
end
best
end
bench_nobuf(trees[1:2])
bench_nobuf(atrees[1:2]) # warmup
tn_nb = bench_nobuf(trees)
ta_nb = bench_nobuf(atrees)
an_nb = @allocated(eval_tree_array(trees[1], X, operators))
aa_nb = @allocated(eval_tree_array(atrees[1], X, operators))
println(
"n=$treesize unbuffered: Node: $(round(tn_nb/1e3; digits=2))us " *
"ArenaNode: $(round(ta_nb/1e3; digits=2))us " *
"ratio=$(round(ta_nb/tn_nb; digits=3)) " *
"allocs/eval Node=$an_nb Arena=$aa_nb",
)
end
Loading
Loading