diff --git a/src/SymbolicIntegration.jl b/src/SymbolicIntegration.jl index 2279ba7..9663f75 100644 --- a/src/SymbolicIntegration.jl +++ b/src/SymbolicIntegration.jl @@ -13,4 +13,10 @@ include("coupled_differential_systems.jl") include("algebraic_functions.jl") include("frontend.jl") +# Add method dispatch system +include("methods.jl") + +# Export method interface +export AbstractIntegrationMethod, RischMethod + end # module diff --git a/src/frontend.jl b/src/frontend.jl index b90cf09..c5ba949 100644 --- a/src/frontend.jl +++ b/src/frontend.jl @@ -766,27 +766,27 @@ integrate(exp(x), x) # exp(x) integrate(log(x), x) # -x + x*log(x) ``` """ -function integrate(f::Symbolics.Num, x::Symbolics.Num; kwargs...) +function integrate_risch(f::Symbolics.Num, x::Symbolics.Num; kwargs...) # Extract SymbolicUtils expressions from Symbolics.Num wrappers - result_symbolic = integrate(f.val, x.val; kwargs...) + result_symbolic = integrate_risch(f.val, x.val; kwargs...) # Wrap result back in Symbolics.Num return Symbolics.Num(result_symbolic) end struct AlgebraicNumbersInvolved <: Exception end -function integrate(f::SymbolicUtils.Add, x::SymbolicUtils.Symbolic; useQQBar::Bool=false, +function integrate_risch(f::SymbolicUtils.Add, x::SymbolicUtils.Symbolic; useQQBar::Bool=false, catchNotImplementedError::Bool=true, catchAlgorithmFailedError::Bool=true) # For efficiency compute integral of sum as sum of integrals g = f.coeff*x for (h, c) in f.dict - g += c*integrate(h, x, useQQBar=useQQBar, catchNotImplementedError=catchNotImplementedError, + g += c*integrate_risch(h, x, useQQBar=useQQBar, catchNotImplementedError=catchNotImplementedError, catchAlgorithmFailedError=catchAlgorithmFailedError) end g end -function integrate(f::SymbolicUtils.Symbolic, x::SymbolicUtils.Symbolic; useQQBar::Bool=false, +function integrate_risch(f::SymbolicUtils.Symbolic, x::SymbolicUtils.Symbolic; useQQBar::Bool=false, catchNotImplementedError::Bool=true, catchAlgorithmFailedError::Bool=true) try p, funs, vars, args = analyze_expr(f, x) diff --git a/src/methods.jl b/src/methods.jl new file mode 100644 index 0000000..5bc8a14 --- /dev/null +++ b/src/methods.jl @@ -0,0 +1,89 @@ +# Method dispatch system for SymbolicIntegration.jl + +""" + AbstractIntegrationMethod + +Abstract supertype for all symbolic integration methods. +""" +abstract type AbstractIntegrationMethod end + +""" + RischMethod <: AbstractIntegrationMethod + +Risch algorithm for symbolic integration of elementary functions. + +# Fields +- `use_algebraic_closure::Bool`: Whether to use algebraic closure for complex roots (default: true) +- `catch_errors::Bool`: Whether to catch and handle algorithm errors gracefully (default: true) +""" +struct RischMethod <: AbstractIntegrationMethod + use_algebraic_closure::Bool + catch_errors::Bool + + function RischMethod(; use_algebraic_closure::Bool=true, catch_errors::Bool=true) + new(use_algebraic_closure, catch_errors) + end +end + +""" + integrate(f, x, method::AbstractIntegrationMethod=RischMethod(); kwargs...) + +Compute the symbolic integral of expression `f` with respect to variable `x` +using the specified integration method. + +# Arguments +- `f`: Symbolic expression to integrate (Symbolics.Num) +- `x`: Integration variable (Symbolics.Num) +- `method`: Integration method to use (AbstractIntegrationMethod, default: RischMethod()) + +# Keyword Arguments +- Method-specific keyword arguments are passed to the method implementation + +# Returns +- Symbolic expression representing the antiderivative (Symbolics.Num) + +# Examples +```julia +using SymbolicIntegration, Symbolics +@variables x + +# Using default Risch method +integrate(x^2, x) # (1//3)*(x^3) + +# Explicit method with options +integrate(1/(x^2 + 1), x, RischMethod(use_algebraic_closure=true)) # atan(x) + +# Method configuration +risch = RischMethod(use_algebraic_closure=false, catch_errors=true) +integrate(exp(x), x, risch) # exp(x) +``` +""" +function integrate(f::Symbolics.Num, x::Symbolics.Num, method::RischMethod; kwargs...) + # Call renamed Risch function with method options + return integrate_risch(f, x; + useQQBar=method.use_algebraic_closure, + catchNotImplementedError=method.catch_errors, + catchAlgorithmFailedError=method.catch_errors, + kwargs...) +end + +# Main integrate function - dispatches to RischMethod by default +function integrate(f::Symbolics.Num, x::Symbolics.Num; kwargs...) + return integrate_risch(f, x; kwargs...) +end + +""" + method_supports_rational(method::RischMethod) + +Check if the integration method supports rational function integration. +Returns `true` for RischMethod. +""" +method_supports_rational(method::RischMethod) = true + +""" + method_supports_transcendental(method::RischMethod) + +Check if the integration method supports transcendental function integration. +Returns `true` for RischMethod. +""" +method_supports_transcendental(method::RischMethod) = true \ No newline at end of file