SymbolicIntegration.jl: Best-of-Both-Worlds Symbolic Integration with Risch and 3400+ Integration Rules

We're excited to announce that SymbolicIntegration.jl has reached v3.1.0 with a powerful new feature: a hybrid integration system that combines the theoretical completeness of the Risch algorithm with the practical breadth of rule-based integration. This release brings over 3400 integration rules from the renowned RUBI (Rule-Based Integrator) system, originally from Mathematica, now available natively in Julia.

The Problem: No Single Method Rules Them All

Symbolic integration is notoriously difficult. While the Risch algorithm is theoretically complete for elementary functions, it struggles with algebraic functions like sqrt(x) and non-integer powers. On the other hand, rule-based systems excel at pattern matching and can handle a vast variety of special cases, but lack the theoretical guarantees of algorithmic completeness.

The solution? Use both. SymbolicIntegration.jl now automatically tries the Risch method first, then falls back to rule-based integration if needed. This "best-of-both-worlds" approach ensures you get the theoretical rigor when possible and practical results when necessary.

Quick Start

using Pkg
Pkg.add("SymbolicIntegration")

using SymbolicIntegration, Symbolics

@variables x

# Works with Risch
integrate(exp(2x) + 2x^2 + sin(x))
# (1//2)*exp(2x) + (2//3)*(x^3) - cos(x)

# Falls back to rule-based for sqrt
integrate(sqrt(x))
# ┌ Warning: NotImplementedError: integrand contains unsupported expression sqrt(x)
# └ @ SymbolicIntegration
#
#  > RischMethod failed returning ∫(sqrt(x), x)
#  > Trying with RuleBasedMethod...
#
# (2//3)*(x^(3//2))

The Intelligent Fallback System

When you call integrate(f, x) without specifying a method, SymbolicIntegration.jl follows a smart strategy:

  1. First attempt: Risch Method - Tries the complete algorithm for elementary transcendental functions

  2. Fallback: Rule-Based Method - If Risch fails (unsupported functions, algebraic expressions), automatically tries 3400+ integration rules

  3. Result: Maximum coverage - You get a solution whenever one is available in either system

This automatic fallback means you don't need to know which method works for which integral. Just call integrate() and let the system figure it out.

Feature Comparison

Here's what each method brings to the table:

FeatureRischRule-based
Rational functions
Non-integer powers
Exponential functions
Logarithms
Trigonometric functionsSometimes
Hyperbolic functionsSometimes
Non-elementary integralsMost of them
Multiple symbols

The complementary strengths of these methods ensure broad coverage across different integral types.

Rule-Based Integration: 3400+ Rules from RUBI

The rule-based integration system in SymbolicIntegration.jl is based on a comprehensive translation of the RUBI (Rule-Based Integrator) package from Mathematica. With over 3400 integration rules, it handles an incredibly diverse range of integrals through sophisticated pattern matching.

How It Works

Each rule is defined using SymbolicUtils pattern matching and specifies:

For example, here's a simple rule:

@rule ∫((~x)^(~!m), (~x)) =>
    !contains_var((~m), (~x)) &&
    !eq((~m), -1) ?
    (~x)^((~m) + 1) / ((~m) + 1) : nothing

When you integrate an expression, the system walks the expression tree and tries to apply rules at each node containing an integral. Rules are ordered from specific to general, ensuring the most appropriate transformation is applied.

Example: Complex Integration with Verbose Output

You can see exactly which rules are applied with the verbose option:

integrate(sqrt(4 - 12*x + 9*x^2) + sqrt(1+x), x, RuleBasedMethod(verbose=true))

# ┌-------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) => (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) => ((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))
#
# Final result: (2//3)*((1 + x)^(3//2)) + (1//36)*(-12 + 18x)*sqrt(4 - 12x + 9(x^2))

This transparency is invaluable for understanding how the integration proceeds and debugging complex cases.

Explicit Method Selection

While the automatic fallback is convenient, you can also explicitly choose which method to use:

# Use only Risch
integrate(x^2 + 1, x, RischMethod(use_algebraic_closure=false))

# Use only rule-based, skip Risch entirely
integrate(sqrt(x), x, RuleBasedMethod(verbose=false))

# Configure rule-based options
integrate(f, x, RuleBasedMethod(verbose=true, use_gamma=false))

The RuleBasedMethod accepts options for:

Risch Method: Theoretical Completeness

The Risch implementation is based on Manuel Bronstein's definitive book "Symbolic Integration I: Transcendental Functions". This provides a complete algorithm for integrating elementary transcendental functions, including:

The Risch method guarantees that if an elementary antiderivative exists within its supported function classes, it will find it. If no elementary antiderivative exists, it correctly returns the integral unevaluated.

Extensive Testing: 27,585 Verified Integrals

To ensure correctness and coverage, SymbolicIntegration.jl includes a comprehensive test suite with 27,585 solved integrals taken from the RUBI test suite:

This extensive testing ensures that the package handles both common and edge cases reliably.

Integration with the SciML Ecosystem

SymbolicIntegration.jl seamlessly integrates with the broader SciML symbolic computing stack:

This integration means symbolic integration works naturally with differential equations, optimization problems, and other SciML workflows.

Performance: Native Julia Implementation

Unlike wrappers around external computer algebra systems, SymbolicIntegration.jl is written entirely in native Julia. This provides several advantages:

Future Directions

The SymbolicIntegration.jl roadmap includes several exciting developments:

Getting Started

Install SymbolicIntegration.jl with:

using Pkg
Pkg.add("SymbolicIntegration")

Check out the documentation for detailed usage instructions, method selection guidance, and API reference.

Acknowledgments

SymbolicIntegration.jl is the result of collaborative effort from the JuliaSymbolics community:

The package builds on the theoretical foundations of Manuel Bronstein's work on the Risch algorithm and Albert Rich's comprehensive RUBI rule-based integration system.

Citation

If you use SymbolicIntegration.jl in your research, please cite:

@software{SymbolicIntegration.jl,
  author = {Harald Hofstätter and Mattia Micheletta Merlin and Chris Rackauckas},
  title = {SymbolicIntegration.jl: Symbolic Integration for Julia},
  url = {https://github.com/JuliaSymbolics/SymbolicIntegration.jl},
  year = {2023-2025}
}

Conclusion

SymbolicIntegration.jl represents a significant milestone in Julia's symbolic computing capabilities. By combining the theoretical rigor of the Risch algorithm with the practical breadth of 3400+ integration rules, it provides a powerful and user-friendly tool for symbolic integration. The intelligent fallback system ensures you get results whenever possible, while the native Julia implementation ensures smooth integration with the broader SciML ecosystem.

Try it out today and see how it can simplify your symbolic mathematics workflows!