SymbolicIntegration.jl (my GSoC project)

Author: Mattia Micheletta Merlin; Date: 02/09/2025

Google summer of code is a summer program in which students around the workd get paid to contribute to open source projects. In Summer 2025 I was part of it, building a symbolic integrator (a program that does indefinite integrals, finds primitives) for the Julia language: SymbolicIntegration.jl.

I chose a rule-based algorithm, which uses a large number of integration rules that specify how to integrate various mathematical expressions. I chose this strategy thanks to the Mathematica package RUBI, which already contains more than 6000 integration rules and is open source. The main challenges I encountered were: As of september 2025, end of GSoC, I translated more than 3400 rules and the system can integrate a vast class of expressions, involving normal algebraic functions:
julia> integrate(sqrt(4 - 12*x + 9*x^2)+sqrt(1+x),x)
┌-------Applied rule 0_1_0 on ∫(sqrt(1 + x) + sqrt(4 - 12x + 9(x^2)), x)
| ∫( a + b + ..., x) => ∫(a,x) + ∫(b,x) + ...
└-------with result: ∫(sqrt(4 - 12x + 9(x^2)), x) + ∫(sqrt(1 + x), x)
┌-------Applied rule 1_1_1_1_4 on ∫(sqrt(1 + x), x)
| ∫((a + b * x) ^ m, x) => if 
|       !(contains_var(a, b, m, x)) &&
|       m !== -1
| (a + b * x) ^ (m + 1) / (b * (m + 1))
└-------with result: (2//3)*((1 + x)^(3//2))
┌-------Applied rule 1_2_1_1_3 on ∫(sqrt(4 - 12x + 9(x^2)), x)
| ∫((a + b * x + c * x ^ 2) ^ p, x) => if 
|       !(contains_var(a, b, c, p, x)) &&
|       (
|             b ^ 2 - 4 * a * c == 0 &&
|             p !== -1 / 2
|       )
| ((b + 2 * c * x) * (a + b * x + c * x ^ 2) ^ p) / (2 * c * (2 * p + 1))
└-------with result: (1//36)*(-12 + 18x)*((4 - 12x + 9(x^2))^(1//2))
(2//3)*((1 + x)^(3//2)) + (1//36)*(-12 + 18x)*sqrt(4 - 12x + 9(x^2))


julia> integrate((2+2x+2x^2)/(1+x^3);verbose=false)
(2//3)*log(1 + x^3) + 2.3094010767585034atan(0.14433756729740646(-4 + 8x))

julia> integrate((1 - x)^2*(1 + x)^(2.34);verbose=false)
1.1976047904191618((1 + x)^3.34) - 0.9216589861751152((1 + x)^4.34) + 0.18726591760299627((1 + x)^5.34)
also symbolic ones:
julia> integrate(1/(a+b*x^2),x;verbose=false)
(atan(x / sqrt(a / b))*sqrt(a / b)) / a

julia> integrate(x^2/(1+a*x^3),x;verbose=false)
log(1 + a*(x^3)) / (3a)
exponentials:
julia> integrate(exp(x)/(exp(2x)-1);verbose=false)
-atanh(exp(x))

julia> integrate(sqrt(x)*exp(x);verbose=false)
-0.8862269254527579SpecialFunctions.erfi(sqrt(x)) + sqrt(x)*exp(x)
logarithms:
julia> integrate(log(x)*x;verbose=false)
-(1//4)*(x^2) + (1//2)*(x^2)*log(x)

julia> integrate(log(x)/sqrt(x);verbose=false)
-(4//1)*sqrt(x) + (2//1)*sqrt(x)*log(x)

julia> integrate(log(log(x));verbose=false)
-SpecialFunctions.expinti(log(x)) + x*log(log(x))
trigonometric functions:
julia> integrate(sin(x)^3*cos(x)^2;verbose=false)
-(1//3)*(cos(x)^3) + (1//5)*(cos(x)^5)

julia> integrate(sqrt(sin(x));verbose=false)
2Elliptic.E((1//2)*(-1.5707963267948966 + x), 2)

julia> integrate(sqrt(sin(x)+1);verbose=false)
(-2cos(x)) / sqrt(1 + sin(x))

julia> integrate(sin(log(2x^2))/x;verbose=false)
(-1//2)*cos(log(2(x^2)))

julia> integrate(sin(x^2)/x;verbose=false)
(1//2)*SpecialFunctions.sinint(x^2)

julia> integrate(acosh(x+1);verbose=false)
(1 + x)*acosh(1 + x) - sqrt(x)*sqrt(2 + x)
and much more. I also added 27585 tests (integrals with their correct solution) from the RUBI package and an automated testing procedure that can be used to test the package.

If you want to learn more in detail what I did you can read the final report (is fast to read), here are instead some stats, taken from the history of my terminal (one of the few traces of my work with dates):
• 370+ commits
• 2626 terminal commands executed, 29.8 each day on average
• 1 Julia package created

Top 10 most used commands, with number of executions:
• julia: 744
• git: 596
• cd: 318
• ls: 174
• vim: 147
• code: 135
• open: 62
• cat: 47
• rm: 41
• find: 27

Top 5 git commands (I usually commit from vscode, hence the few add, commit and push):
• git reset: 125
• git push: 113
• git checkout: 110
• git log: 53
• git remote: 30

Top 5 Julia commands usage:
• julia src/rules_translator.jl: 407
• julia (no subcommand): 204
• julia --project=.: 49
• julia testset_translator.jl: 38
• julia test/runtests.jl: 25

Here are the commands on my terminal plotted, each day is a vertical line (top: midnight, bottom: midinght of the day after):


The most active day was 27 august with 154 commands. Here instead is the histogram of my work activity, taken again from the commands on the terminal: