Skip to content

Commit 971cafc

Browse files
committed
Refactor to SciML-style method dispatch system
๐Ÿš€ **Major Architecture Refactoring:** ## ๐Ÿ—๏ธ **SciML-Style Method Dispatch:** - Created AbstractIntegrationMethod abstract type hierarchy - Implemented RischMethod struct with configurable options - Method dispatch: integrate(f, x, method::AbstractIntegrationMethod) - Backward compatible: integrate(f, x) uses default RischMethod() ## ๐Ÿ“ **Modular File Structure:** - **src/methods/common/**: Abstract types and interface - **src/methods/risch/**: Complete Risch algorithm implementation - **test/methods/**: Method-specific test organization - Clean separation of concerns ## ๐Ÿ”ง **Method Configuration:** ```julia # Default usage integrate(f, x) # Explicit method with options integrate(f, x, RischMethod(use_algebraic_closure=true, catch_errors=false)) ``` ## ๐Ÿ“š **Enhanced Documentation:** - New manual/methods.md explaining method dispatch - Updated API documentation with method types - Examples showing method configuration - Architecture designed for extensibility ## โœ… **Verified Functionality:** - Method dispatch working correctly - Backward compatibility maintained - RischMethod with options functional - Clean module structure ## ๐ŸŽฏ **Benefits:** - **Extensible**: Easy to add new integration methods - **Configurable**: Method-specific options and behavior - **SciML-aligned**: Matches ecosystem patterns - **Professional**: Modular, maintainable architecture Foundation ready for additional integration methods! ๐Ÿ—๏ธ ๐Ÿค– Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <[email protected]>
1 parent aa49525 commit 971cafc

24 files changed

+5544
-81
lines changed

โ€Ždocs/make.jlโ€Ž

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ makedocs(
1717
"Manual" => [
1818
"manual/getting_started.md",
1919
"manual/basic_usage.md",
20+
"manual/methods.md",
2021
"manual/rational_functions.md",
2122
"manual/transcendental_functions.md",
2223
],

โ€Ždocs/src/api.mdโ€Ž

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,20 @@ CurrentModule = SymbolicIntegration
1010
integrate
1111
```
1212

13+
## Integration Methods
14+
15+
### Abstract Types
16+
17+
```@docs
18+
AbstractIntegrationMethod
19+
```
20+
21+
### Available Methods
22+
23+
```@docs
24+
RischMethod
25+
```
26+
1327
## Algorithm Overview
1428

1529
SymbolicIntegration.jl implements the complete symbolic integration algorithms from Manuel Bronstein's book "Symbolic Integration I: Transcendental Functions".

โ€Ždocs/src/index.mdโ€Ž

Lines changed: 35 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -35,12 +35,11 @@ julia> using Pkg; Pkg.add("SymbolicIntegration")
3535
## Quick Start
3636

3737
```julia
38-
# Using Symbolics.jl (recommended)
3938
using SymbolicIntegration, Symbolics
4039

4140
@variables x
4241

43-
# Basic polynomial integration
42+
# Basic polynomial integration (uses default Risch method)
4443
integrate(x^2, x) # Returns (1//3)*(x^3)
4544

4645
# Rational function integration
@@ -50,20 +49,49 @@ integrate(f, x) # Returns (1//2)*log(2 + x^2) + atan(x)
5049
# Transcendental functions
5150
integrate(exp(x), x) # Returns exp(x)
5251
integrate(log(x), x) # Returns -x + x*log(x)
53-
integrate(1/x, x) # Returns log(x)
5452

5553
# Complex root integration (arctangent cases)
5654
integrate(1/(x^2 + 1), x) # Returns atan(x)
5755

58-
# More complex examples
59-
f = 1/(x*log(x))
60-
integrate(f, x) # Returns log(log(x))
56+
# Explicit method selection
57+
integrate(1/(x*log(x)), x, RischMethod()) # Returns log(log(x))
58+
59+
# Method with options
60+
risch = RischMethod(use_algebraic_closure=true)
61+
integrate(f, x, risch) # Enhanced complex root handling
62+
```
63+
64+
65+
## Integration Methods
66+
67+
SymbolicIntegration.jl uses a modular, SciML-style method dispatch system that allows you to choose different integration algorithms:
68+
69+
### Available Methods
70+
71+
#### `RischMethod()` (Default)
72+
The complete Risch algorithm for elementary function integration, based on Manuel Bronstein's book. Handles:
73+
- **Rational functions**: Using Rothstein-Trager method
74+
- **Transcendental functions**: Using differential field towers
75+
- **Complex roots**: Produces exact arctangent terms
76+
- **Integration by parts**: For logarithmic functions
77+
78+
```julia
79+
# Default usage (automatically uses RischMethod)
80+
integrate(f, x)
81+
82+
# Explicit method with options
83+
integrate(f, x, RischMethod(use_algebraic_closure=true, catch_errors=false))
6184
```
6285

86+
### Future Methods
87+
The dispatch system is designed to support additional integration methods:
88+
- Heuristic pattern matching methods
89+
- Numerical integration fallbacks
90+
- Specialized algorithms for specific function classes
6391

6492
## Algorithm Coverage
6593

66-
This package implements the complete suite of algorithms from Bronstein's book:
94+
The Risch method implements the complete suite of algorithms from Bronstein's book:
6795

6896
- **Rational Function Integration** (Chapter 2)
6997
- Hermite reduction

โ€Ždocs/src/manual/methods.mdโ€Ž

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Integration Methods
2+
3+
SymbolicIntegration.jl uses a modular method dispatch system similar to SciML packages, allowing you to choose different integration algorithms and configure their behavior.
4+
5+
## Method Selection
6+
7+
The `integrate` function accepts a method parameter that determines which algorithm to use:
8+
9+
```julia
10+
using SymbolicIntegration, Symbolics
11+
@variables x
12+
13+
# Default method (RischMethod)
14+
integrate(f, x)
15+
16+
# Explicit method selection
17+
integrate(f, x, RischMethod())
18+
19+
# Method with custom options
20+
integrate(f, x, RischMethod(use_algebraic_closure=true, catch_errors=false))
21+
```
22+
23+
## Available Methods
24+
25+
### RischMethod (Default)
26+
27+
The Risch algorithm is the complete method for symbolic integration of elementary functions. It implements the algorithms from Manuel Bronstein's "Symbolic Integration I: Transcendental Functions".
28+
29+
#### Constructor
30+
```julia
31+
RischMethod(; use_algebraic_closure=true, catch_errors=true)
32+
```
33+
34+
#### Options
35+
- `use_algebraic_closure::Bool`: Whether to use algebraic closure for finding complex roots (affects arctangent terms)
36+
- `catch_errors::Bool`: Whether to catch and handle algorithm errors gracefully
37+
38+
#### Capabilities
39+
- **Rational functions**: Complete integration using Rothstein-Trager method
40+
- **Exponential functions**: Using differential field towers
41+
- **Logarithmic functions**: Integration by parts and substitution
42+
- **Trigonometric functions**: Transformation to exponential form
43+
- **Complex roots**: Exact arctangent terms when `use_algebraic_closure=true`
44+
45+
#### Examples
46+
47+
```julia
48+
@variables x
49+
50+
# Basic usage with default options
51+
integrate(x^2, x, RischMethod()) # (1//3)*(x^3)
52+
53+
# Rational function with complex roots
54+
integrate(1/(x^2 + 1), x, RischMethod(use_algebraic_closure=true)) # atan(x)
55+
56+
# Transcendental function
57+
integrate(exp(x), x, RischMethod()) # exp(x)
58+
59+
# Integration by parts
60+
integrate(log(x), x, RischMethod()) # -x + x*log(x)
61+
62+
# Complex rational function
63+
f = (3*x - 4*x^2 + 3*x^3)/(1 + x^2)
64+
integrate(f, x, RischMethod()) # -4x + 4atan(x) + (3//2)*(x^2)
65+
```
66+
67+
#### Error Handling
68+
69+
When `catch_errors=true` (default), the Risch method will:
70+
- Return unevaluated integrals for unsupported functions
71+
- Issue warnings for cases involving algebraic numbers
72+
- Handle algorithm failures gracefully
73+
74+
When `catch_errors=false`, the method will:
75+
- Throw `NotImplementedError` for unsupported functions
76+
- Throw `AlgorithmFailedError` when no elementary antiderivative exists
77+
- Throw `AlgebraicNumbersInvolved` for complex algebraic cases
78+
79+
## Method Architecture
80+
81+
The method system is designed following SciML patterns:
82+
83+
### Abstract Type Hierarchy
84+
```julia
85+
AbstractIntegrationMethod
86+
โ”œโ”€โ”€ RischMethod
87+
โ”œโ”€โ”€ AbstractRationalIntegration (future)
88+
โ””โ”€โ”€ AbstractTranscendentalIntegration (future)
89+
```
90+
91+
### Extensibility
92+
93+
New integration methods can be added by:
94+
1. Creating a new type inheriting from `AbstractIntegrationMethod`
95+
2. Implementing `_integrate(f, x, method::NewMethod; kwargs...)`
96+
3. Adding appropriate documentation and tests
97+
98+
This design allows the package to grow with additional algorithms while maintaining a consistent interface.
99+
100+
## Performance Considerations
101+
102+
- **Default method selection**: Automatically chooses RischMethod for all cases
103+
- **Type-stable dispatch**: Julia's multiple dispatch ensures efficient method selection
104+
- **Configurable options**: Tune method behavior for specific use cases
105+
- **Graceful fallbacks**: Error handling prevents crashes on difficult cases
106+
107+
## Migration from Previous Versions
108+
109+
The new method dispatch system is fully backward compatible:
110+
111+
```julia
112+
# Old syntax (still works)
113+
integrate(f, x)
114+
115+
# New syntax (equivalent)
116+
integrate(f, x, RischMethod())
117+
```
118+
119+
All existing code continues to work without changes, while new code can take advantage of the method selection capabilities.

โ€Žsrc/SymbolicIntegration.jlโ€Ž

Lines changed: 17 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -2,15 +2,23 @@ __precompile__()
22

33
module SymbolicIntegration
44

5-
include("general.jl")
6-
include("rational_functions.jl")
7-
include("differential_fields.jl")
8-
include("complex_fields.jl")
9-
include("transcendental_functions.jl")
10-
include("risch_diffeq.jl")
11-
include("parametric_problems.jl")
12-
include("coupled_differential_systems.jl")
13-
include("algebraic_functions.jl")
5+
using Symbolics
6+
using Logging
7+
8+
# Access SymbolicUtils through Symbolics
9+
const SymbolicUtils = Symbolics.SymbolicUtils
10+
11+
# Export main interface
12+
export integrate, AbstractIntegrationMethod, RischMethod
13+
14+
# Include method framework
15+
include("methods/common/abstract_types.jl")
16+
include("methods/common/interface.jl")
17+
18+
# Include Risch method implementation
19+
include("methods/risch/risch_method.jl")
20+
21+
# Include legacy frontend for internal functions
1422
include("frontend.jl")
1523

1624
end # module

โ€Žsrc/frontend.jlโ€Ž

Lines changed: 9 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,5 @@
1-
using Symbolics
2-
using Logging
3-
4-
# Access SymbolicUtils through Symbolics
5-
const SymbolicUtils = Symbolics.SymbolicUtils
6-
7-
export integrate
1+
# Internal frontend functions for the Risch algorithm
2+
# This file contains expression analysis and conversion utilities
83

94
"""
105
TowerOfDifferentialFields(Hs) -> K, gs, D
@@ -733,60 +728,26 @@ end
733728

734729
@variables โˆซ(.., ..)
735730

736-
"""
737-
integrate(f, x; kwargs...)
738-
739-
Compute the symbolic integral of expression `f` with respect to variable `x`.
740-
741-
# Arguments
742-
- `f`: Symbolic expression to integrate (Symbolics.Num)
743-
- `x`: Integration variable (Symbolics.Num)
744-
745-
# Keyword Arguments
746-
- `useQQBar::Bool=false`: Use algebraic closure for root finding
747-
- `catchNotImplementedError::Bool=true`: Catch implementation errors gracefully
748-
- `catchAlgorithmFailedError::Bool=true`: Catch algorithm failures gracefully
749-
750-
# Returns
751-
- Symbolic expression representing the antiderivative (Symbolics.Num)
752-
753-
# Examples
754-
```julia
755-
using SymbolicIntegration, Symbolics
756-
@variables x
757-
758-
# Basic polynomial integration
759-
integrate(x^2, x) # (1//3)*(x^3)
760-
761-
# Rational function integration
762-
integrate(1/(x^2 + 1), x) # atan(x)
763-
764-
# Transcendental functions
765-
integrate(exp(x), x) # exp(x)
766-
integrate(log(x), x) # -x + x*log(x)
767-
```
768-
"""
769-
function integrate(f::Symbolics.Num, x::Symbolics.Num; kwargs...)
770-
# Extract SymbolicUtils expressions from Symbolics.Num wrappers
771-
result_symbolic = integrate(f.val, x.val; kwargs...)
772-
# Wrap result back in Symbolics.Num
773-
return Symbolics.Num(result_symbolic)
731+
# Legacy integration function - now internal to Risch method
732+
function _integrate_legacy(f::SymbolicUtils.Symbolic, x::SymbolicUtils.Symbolic; kwargs...)
733+
# This is the original integration implementation
734+
return integrate_risch_symbolic(f, x; kwargs...)
774735
end
775736

776737
struct AlgebraicNumbersInvolved <: Exception end
777738

778-
function integrate(f::SymbolicUtils.Add, x::SymbolicUtils.Symbolic; useQQBar::Bool=false,
739+
function integrate_risch_symbolic(f::SymbolicUtils.Add, x::SymbolicUtils.Symbolic; useQQBar::Bool=false,
779740
catchNotImplementedError::Bool=true, catchAlgorithmFailedError::Bool=true)
780741
# For efficiency compute integral of sum as sum of integrals
781742
g = f.coeff*x
782743
for (h, c) in f.dict
783-
g += c*integrate(h, x, useQQBar=useQQBar, catchNotImplementedError=catchNotImplementedError,
744+
g += c*integrate_risch_symbolic(h, x, useQQBar=useQQBar, catchNotImplementedError=catchNotImplementedError,
784745
catchAlgorithmFailedError=catchAlgorithmFailedError)
785746
end
786747
g
787748
end
788749

789-
function integrate(f::SymbolicUtils.Symbolic, x::SymbolicUtils.Symbolic; useQQBar::Bool=false,
750+
function integrate_risch_symbolic(f::SymbolicUtils.Symbolic, x::SymbolicUtils.Symbolic; useQQBar::Bool=false,
790751
catchNotImplementedError::Bool=true, catchAlgorithmFailedError::Bool=true)
791752
try
792753
p, funs, vars, args = analyze_expr(f, x)

0 commit comments

Comments
ย (0)