Author: Mattia Micheletta Merlin; Date: 25/12/2025
This summer I created a Julia package that solves indefinite integrals (more on that in my other blog post) that was great but really slow. This blog post is a (quite technical but understandable) description of how I improved the "time to the first integral" from ~14 minutes to ~12 seconds, while making it capable of solving more integrals.julia> tree_print(y*exp(y^2-1)+1)
└─+ (2 arguments)
├─1
└─* (2 arguments)
├─exp (1 arguments)
│ └─+ (2 arguments)
│ ├─-1
│ └─^ (2 arguments)
│ ├─y
│ └─2
└─y
The previous system to recognize if an input expression matched a rule, worked by creating a function for the top node of the tree of the symbolic expression of the rule, that called other functions created for the children nodes, let them be both end leaves (symbols, numbers) or other operations. Those functions then called other functions created for their children nodes and so on. So just creating a rule was a computationally expensive task involving many nested functions.
Exprs, which is a Julia data type used to represent Julia code itself, basically a parsed string of code, that is also represented as a tree.
julia> ans = Meta.parse("1+(1+y)^2")
:(1 + (1 + y) ^ 2)
julia> dump(ans)
Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol ^
2: Expr
head: Symbol call
args: Array{Any}((3,))
1: Symbol +
2: Int64 1
3: Symbol y
3: Int64 2
So at initialization no function needs to be created. Then I use a recursive function that traverses both trees at the same time (rule and input expression), until a difference is found or the whole tree is traversed. This approach is much simpler and surprisingly much faster! This effectively solved the type inference time as well, because there is now only one function to be compiled.
using Symbolics, SymbolicIntegration
@variables x
result = integrate(asinh(x), RuleBasedMethod(verbose=true))
println(result)
With the old system I had:
julia --project=. prova.jl 850.31s user 72.69s system 111% cpu 13:50.71 total
With the new system I have:
julia --project=. prova.jl 11.95s user 0.49s system 101% cpu 12.244 total
That is a 67x speedup.