SymbolicNumericIntegration.jl

SymbolicNumericIntegration.jl is a hybrid symbolic/numerical integration package that works on the Julia Symbolics expressions.

SymbolicNumericIntegration.jl uses a randomized algorithm based on a hybrid of the method of undetermined coefficients and sparse regression and can solve a large subset of basic standard integrals (polynomials, exponential/logarithmic, trigonometric and hyperbolic, inverse trigonometric and hyperbolic, rational and square root). The symbolic part of the algorithm is similar (but not identical) to Risch-Bronstein's poor man's integrator and generates a list of ansatzes (candidate terms). The numerical part uses sparse regression adopted from the Sparse identification of nonlinear dynamics (SINDy) algorithm to prune down the ansatzes and find the corresponding coefficients. The basis of how it works and the theory of integration using the Symbolic-Numeric methods refer to Basis of Symbolic-Numeric Integration.

hyint is the python counterpart of SymbolicNumericIntegration.jl that works with sympy expressions.

Originally, SymbolicNumericIntegration.jl expected only univariate expression with constant real or complex coefficients as input. As of version 1.2, integrate function accepts symbolic constants for a subset of solvable integrals.

integrate returns a tuple with three values. The first one is the solved integral, the second one is the sum of the unsolved terms, and the third value is the residual error. If integrate is successful, the unsolved portion is reported as 0. If we pass detailed=false to integrate', the output is simplified to only the resulting integrals. In this case,nothing` is returned if the integral cannot be solved. Note that the simplified output will become the default in a future version.

Installation

To install SymbolicNumericIntegration.jl, use the Julia package manager:

using Pkg
Pkg.add("SymbolicNumericIntegration")

Examples:

using Symbolics
using SymbolicNumericIntegration

@variables x a b

# if `detailed = true` (default), the output is a tuple of (solution, unsolved portion, err)

integrate(3x^3 + 2x - 5)
(x^2 + (3 // 4) * (x^4) - (5x), 0, 0)

integrate((5 + 2x)^-1)
((1 // 2) * log((5 // 2) + x), 0, 0.0)

# `detailed = false` simplifies the output to just the resulting integral

integrate(x^2 / (16 + x^2); detailed = false)
x + 4atan((-1 // 4) * x)

integrate(x^2 * log(x); detailed = false)
(1 // 3) * (x^3) * log(x) - (1 // 9) * (x^3)

integrate(sec(x) * tan(x); detailed = false)
sec(x)

# Symbolic integration. Here, a is a symbolic constant; therefore, we need 
# to explicitly define the independent variable (say, x). Also, we set
# `symbolic = true` to force using the symbolic solver

integrate(sin(a * x), x; detailed = false, symbolic = true)
(-cos(a * x)) / a

integrate(x^2 * cos(a * x), x; detailed = false, symbolic = true)
((a^2) * (x^2) * sin(a * x) + 2.0a * x * cos(a * x) - 2.0sin(a * x)) / (a^3)

integrate(log(log(a * x)) / x, x; detailed = false, symbolic = true)
log(a * x) * log(log(a * x)) - log(a * x)

# multiple symbolic constants

integrate(cosh(a * x) * exp(b * x), x; detailed = false, symbolic = true)
(a * sinh(a * x) * exp(b * x) - b * cosh(a * x) * exp(b * x)) / (a^2 - (b^2))

# definite integration, passing a tuple of (x, lower bound, higher bound) in the 
# second argument

integrate(x * sin(a * x), (x, 0, 1); symbolic = true, detailed = false)
(sin(a) - a * cos(a)) / (a^2)

SymbolicNumericIntegration.jl exports some special integral functions (defined over Complex numbers) and uses them in solving integrals:

  • Ei: exponential integral (define as ∫ exp(x) / x dx)
  • Si: sine integral (define as ∫ sin(x) / x dx)
  • Ci: cosine integral (define as ∫ cos(x) / x dx)
  • Li: logarithmic integral (define as ∫ 1 / log(x) dx)

For examples:

integrate(exp(x + 1) / (x + 1))
(SymbolicNumericIntegration.Ei(1 + x), 0, 0.0)

integrate(x * cos(a * x^2 - 1) / (a * x^2 - 1), x; detailed = false, symbolic = true)
((1 // 2) * SymbolicNumericIntegration.Ci(a * (x^2) - 1)) / a

integrate(1 / (x * log(log(x))), x; detailed = false, symbolic = true)
SymbolicNumericIntegration.Li(log(x))
SymbolicNumericIntegration.integrateMethod
integrate(eq, x; kwargs...)

is the main entry point to integrate a univariate expression eq with respect to `x' (optional).

julia> using Symbolics, SymbolNumericIntegration

julia> @variables x a

julia> integrate(x * sin(2x))
((1//4)*sin(2x) - (1//2)*x*cos(2x), 0, 0)

julia> integrate(x * sin(a * x), x; symbolic = true, detailed = false)
(sin(a*x) - a*x*cos(a*x)) / (a^2)

julia> integrate(x * sin(a * x), (x, 0, 1); symbolic = true, detailed = false)
(sin(a) - a*cos(a)) / (a^2)

Arguments:

  • eq: a univariate expression
  • x: independent variable (optional if eq is univariate) or a tuple of (independent variable, lower bound, upper bound) for definite integration.

Keyword Arguments:

  • abstol (default: 1e-6): the desired tolerance
  • num_steps (default: 2): the number of different steps with expanding basis to be tried
  • num_trials (default: 10): the number of trials in each step (no changes to the basis)
  • show_basis (default: false): if true, the basis (list of candidate terms) is printed
  • bypass (default: false): if true do not integrate terms separately but consider all at once
  • symbolic (default: false): try symbolic integration first (will be forced if eq has symbolic constants)
  • max_basis (default: 100): the maximum number of candidate terms to consider
  • verbose (default: false): print a detailed report
  • complex_plane (default: true): generate random test points on the complex plane (if false, the points will be on real axis)
  • radius (default: 1.0): the radius of the disk in the complex plane to generate random test points
  • opt (default: STLSQ(exp.(-10:1:0))): the sparse regression optimizer (from DataDrivenSparse)
  • homotopy (default: true): deprecated, will be removed in a future version
  • use_optim (default: false): deprecated, will be removed in a future version
  • detailed (default: true): (solved, unsolved, err) output format. If detailed=false, only the final integral is returned.

Returns a tuple of (solved, unsolved, err) if detailed == true, where

solved: the solved integral 
unsolved: the residual unsolved portion of the input
err: the numerical error in reaching the solution

Returns the resulting integral or nothing if detailed == false

source

Testing

test/runtests.jl contains a test suite of 170 easy to moderate test integrals (can be run by calling test_integrals). Currently, SymbolicNumericIntegration.jl solves more than 95% of its test suite.

Additionally, 12 test suites from the Rule-based Integrator (Rubi) are included in the /test directory. For example, we can test the first one as below. Axiom refers to the format of the test files)

using SymbolicNumericIntegration
include("test/axiom.jl")  # note, you may need to use the correct path

L = convert_axiom(:Apostle)   # can also use L = convert_axiom(1)  
test_axiom(L, false; bypass = false, verbose = false, homotopy = true)

The test suites description based on the header of the files in the Rubi directory are

nameidcomment
:Apostle1Tom M. Apostol - Calculus, Volume I, Second Edition (1967)
:Bondarenko2Vladimir Bondarenko Integration Problems
:Bronstein3Manuel Bronstein - Symbolic Integration Tutorial (1998)
:Charlwood4Kevin Charlwood - Integration on Computer Algebra Systems (2008)
:Hearn5Anthony Hearn - Reduce Integration Test Package
:Hebisch6Waldek Hebisch - email May 2013
:Jeffrey7David Jeffrey - Rectifying Transformations for Trig Integration (1997)
:Moses8Joel Moses - Symbolic Integration Ph.D. Thesis (1967)
:Stewart9James Stewart - Calculus (1987)
:Timofeev10A. F. Timofeev - Integration of Functions (1948)
:Welz11Martin Welz - posts on Sci.Math.Symbolic
:Webster12Michael Wester

Citation

If you use SymbolicNumericIntegration.jl, please cite Symbolic-Numeric Integration of Univariate Expressions based on Sparse Regression:

@article{Iravanian2022,   
   author = {Shahriar Iravanian and Carl Julius Martensen and Alessandro Cheli and Shashi Gowda and Anand Jain and Julia Computing and Yingbo Ma and Chris Rackauckas},
   doi = {10.48550/arxiv.2201.12468},
   month = {1},
   title = {Symbolic-Numeric Integration of Univariate Expressions based on Sparse Regression},
   url = {https://arxiv.org/abs/2201.12468v2},
   year = {2022},
}

Contributing

Reproducibility

The documentation of this SciML package was built using these direct dependencies,
Status `~/work/SymbolicNumericIntegration.jl/SymbolicNumericIntegration.jl/docs/Project.toml`
  [e30172f5] Documenter v1.3.0
  [78aadeae] SymbolicNumericIntegration v1.3.3 `~/work/SymbolicNumericIntegration.jl/SymbolicNumericIntegration.jl`
and using this machine and Julia version.
Julia Version 1.10.2
Commit bd47eca2c8a (2024-03-01 10:14 UTC)
Build Info:
  Official https://julialang.org/ release
Platform Info:
  OS: Linux (x86_64-linux-gnu)
  CPU: 4 × AMD EPYC 7763 64-Core Processor
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-15.0.7 (ORCJIT, znver3)
Threads: 1 default, 0 interactive, 1 GC (on 4 virtual cores)
A more complete overview of all dependencies and their versions is also provided.
Status `~/work/SymbolicNumericIntegration.jl/SymbolicNumericIntegration.jl/docs/Manifest.toml`
  [47edcb42] ADTypes v0.2.7
  [a4c015fc] ANSIColoredPrinters v0.0.1
  [1520ce14] AbstractTrees v0.4.5
  [7d9f7c33] Accessors v0.1.36
  [79e6a3ab] Adapt v4.0.4
  [dce04be8] ArgCheck v2.3.0
⌅ [ec485272] ArnoldiMethod v0.2.0
  [4fba245c] ArrayInterface v7.9.0
  [4c555306] ArrayLayouts v1.8.0
  [a9b6321e] Atomix v0.1.0
⌅ [198e06fe] BangBang v0.3.40
  [9718e550] Baselet v0.1.1
  [e2ed5e7c] Bijections v0.1.6
  [62783981] BitTwiddlingConvenienceFunctions v0.1.5
  [fa961155] CEnum v0.5.0
  [2a0fbf3d] CPUSummary v0.2.4
  [00ebfdb7] CSTParser v3.4.2
  [49dc2e85] Calculus v0.5.1
  [d360d2e6] ChainRulesCore v1.23.0
  [fb6a15b2] CloseOpenIntervals v0.1.12
  [944b1d66] CodecZlib v0.7.4
  [861a8166] Combinatorics v1.0.2
  [a80b9123] CommonMark v0.8.12
  [38540f10] CommonSolve v0.2.4
  [bbf7d656] CommonSubexpressions v0.3.0
  [34da2185] Compat v4.14.0
  [b152e2b5] CompositeTypes v0.1.3
  [a33af91c] CompositionsBase v0.1.2
  [2569d6c7] ConcreteStructs v0.2.3
  [187b0558] ConstructionBase v1.5.5
  [6add18c4] ContextVariablesX v0.1.3
  [adafc99b] CpuId v0.3.1
  [a8cc5b0e] Crayons v4.1.1
  [9a962f9c] DataAPI v1.16.0
  [2445eb08] DataDrivenDiffEq v1.4.0
  [5b588203] DataDrivenSparse v0.1.2
  [82cc6244] DataInterpolations v4.7.1
  [864edb3b] DataStructures v0.18.18
  [e2d170a0] DataValueInterfaces v1.0.0
  [244e2a9f] DefineSingletons v0.1.2
  [8bb1440f] DelimitedFiles v1.9.1
  [2b5f629d] DiffEqBase v6.149.0
  [459566f4] DiffEqCallbacks v3.4.1
  [163ba53b] DiffResults v1.1.0
  [b552c78f] DiffRules v1.15.1
  [31c24e10] Distributions v0.25.107
  [ffbed154] DocStringExtensions v0.9.3
  [e30172f5] Documenter v1.3.0
  [5b8099bc] DomainSets v0.7.10
  [fa6b7ba4] DualNumbers v0.6.8
  [7c1d4256] DynamicPolynomials v0.5.5
  [06fc5a27] DynamicQuantities v0.13.2
  [4e289a0a] EnumX v1.0.4
⌃ [f151be2c] EnzymeCore v0.6.6
  [d4d017d3] ExponentialUtilities v1.26.1
  [e2ba6199] ExprTools v0.1.10
  [cc61a311] FLoops v0.2.1
  [b9860ae5] FLoopsBase v0.1.1
  [7034ab61] FastBroadcast v0.2.8
  [9aa1b823] FastClosures v0.3.2
  [29a986be] FastLapackInterface v2.0.2
  [1a297f60] FillArrays v1.9.3
  [64ca27bc] FindFirstFunctions v1.2.0
  [6a86dc24] FiniteDiff v2.23.0
  [1fa38f19] Format v1.3.7
  [f6369f11] ForwardDiff v0.10.36
  [069b7b12] FunctionWrappers v1.1.3
  [77dc65aa] FunctionWrappersWrappers v0.1.3
  [d9f16b24] Functors v0.4.8
  [46192b85] GPUArraysCore v0.1.6
  [c145ed77] GenericSchur v0.5.4
  [d7ba0133] Git v1.3.1
  [c27321d9] Glob v1.3.1
  [86223c79] Graphs v1.9.0
  [3e5b6fbb] HostCPUFeatures v0.1.16
  [34004b35] HypergeometricFunctions v0.3.23
  [b5f81e59] IOCapture v0.2.4
  [615f187c] IfElse v0.1.1
  [d25df0c9] Inflate v0.1.4
  [22cec73e] InitialValues v0.3.1
  [8197267c] IntervalSets v0.7.10
  [3587e190] InverseFunctions v0.1.13
  [92d709cd] IrrationalConstants v0.2.2
  [82899510] IteratorInterfaceExtensions v1.0.0
  [692b3bcd] JLLWrappers v1.5.0
  [682c06a0] JSON v0.21.4
  [98e50ef6] JuliaFormatter v1.0.54
  [b14d175d] JuliaVariables v0.2.4
  [ccbc3e58] JumpProcesses v9.11.1
  [ef3ab10e] KLU v0.6.0
  [63c18a36] KernelAbstractions v0.9.18
  [ba0b0d4f] Krylov v0.9.5
  [929cbde3] LLVM v6.6.2
  [b964fa9f] LaTeXStrings v1.3.1
  [2ee39098] LabelledArrays v1.15.1
  [984bce1d] LambertW v0.4.6
  [23fbe1c1] Latexify v0.16.2
  [10f19ff3] LayoutPointers v0.1.15
  [0e77f7df] LazilyInitializedFields v1.2.2
  [5078a376] LazyArrays v1.8.3
  [d3d80556] LineSearches v7.2.0
  [7ed4a6bd] LinearSolve v2.28.0
  [2ab3a3ac] LogExpFunctions v0.3.27
  [bdcacae8] LoopVectorization v0.12.169
  [d8e11817] MLStyle v0.4.17
  [f1d291b0] MLUtils v0.4.4
  [1914dd2f] MacroTools v0.5.13
  [d125e4d3] ManualMemory v0.1.8
  [d0879d2d] MarkdownAST v0.1.2
  [a3b82374] MatrixFactorizations v2.1.0
  [bb5d69b7] MaybeInplace v0.1.2
⌅ [128add7d] MicroCollections v0.1.4
  [e1d29d7a] Missings v1.1.0
  [961ee093] ModelingToolkit v9.9.0
  [46d2c3a1] MuladdMacro v0.2.4
  [102ac46a] MultivariatePolynomials v0.5.4
  [d8a4904e] MutableArithmetics v1.4.2
  [d41bc354] NLSolversBase v7.8.3
  [872c559c] NNlib v0.9.13
  [77ba4419] NaNMath v1.0.2
  [71a1bf82] NameResolution v0.1.5
  [8913a72c] NonlinearSolve v3.9.0
  [6fe1bfb0] OffsetArrays v1.13.0
  [bac558e1] OrderedCollections v1.6.3
  [1dea7af3] OrdinaryDiffEq v6.74.1
  [90014a1f] PDMats v0.11.31
  [65ce6f38] PackageExtensionCompat v1.0.2
  [d96e819e] Parameters v0.12.3
  [69de0a69] Parsers v2.8.1
  [e409e4f3] PoissonRandom v0.4.4
  [f517fe37] Polyester v0.7.12
  [1d0040c9] PolyesterWeave v0.2.1
  [d236fae5] PreallocationTools v0.4.20
  [aea7be01] PrecompileTools v1.2.1
  [21216c6a] Preferences v1.4.3
  [8162dcfd] PrettyPrint v0.2.0
  [08abe8d2] PrettyTables v2.3.1
  [92933f4c] ProgressMeter v1.10.0
  [1fd47b50] QuadGK v2.9.4
  [e6cf234a] RandomNumbers v1.5.3
  [3cdcf5f2] RecipesBase v1.3.4
  [731186ca] RecursiveArrayTools v3.13.0
  [f2c3362d] RecursiveFactorization v0.2.21
  [189a3867] Reexport v1.2.2
  [2792f1a3] RegistryInstances v0.1.0
  [ae029012] Requires v1.3.0
  [79098fc4] Rmath v0.7.1
  [7e49a35a] RuntimeGeneratedFunctions v0.5.12
  [94e857df] SIMDTypes v0.1.0
  [476501e8] SLEEFPirates v0.6.42
  [0bca4576] SciMLBase v2.31.0
  [c0aeaf25] SciMLOperators v0.3.8
  [53ae85a6] SciMLStructures v1.1.0
  [efcf1570] Setfield v1.1.1
  [605ecd9f] ShowCases v0.1.0
  [727e6d20] SimpleNonlinearSolve v1.7.0
  [699a6c99] SimpleTraits v0.9.4
  [ce78b400] SimpleUnPack v1.1.0
  [a2af1166] SortingAlgorithms v1.2.1
  [47a9eef4] SparseDiffTools v2.17.0
  [e56a9233] Sparspak v0.3.9
  [276daf66] SpecialFunctions v2.3.1
  [171d559e] SplittablesBase v0.1.15
  [aedffcd0] Static v0.8.10
  [0d7ed370] StaticArrayInterface v1.5.0
  [90137ffa] StaticArrays v1.9.3
  [1e83bf80] StaticArraysCore v1.4.2
  [82ae8749] StatsAPI v1.7.0
  [2913bbd2] StatsBase v0.34.2
  [4c63d2b9] StatsFuns v1.3.1
  [7792a7ef] StrideArraysCore v0.5.2
  [892a3eda] StringManipulation v0.3.4
  [2efcf032] SymbolicIndexingInterface v0.3.15
  [19f23fe9] SymbolicLimits v0.2.0
  [78aadeae] SymbolicNumericIntegration v1.3.3 `~/work/SymbolicNumericIntegration.jl/SymbolicNumericIntegration.jl`
  [d1185830] SymbolicUtils v1.5.1
  [0c5d862f] Symbolics v5.27.1
  [3783bdb8] TableTraits v1.0.1
  [bd369af6] Tables v1.11.1
  [8290d209] ThreadingUtilities v0.5.2
  [a759f4b9] TimerOutputs v0.5.23
  [0796e94c] Tokenize v0.5.28
  [3bb67fe8] TranscodingStreams v0.10.7
⌃ [28d57a85] Transducers v0.4.80
  [d5829a12] TriangularSolve v0.1.21
  [410a4b4d] Tricks v0.1.8
  [781d530d] TruncatedStacktraces v1.4.0
  [5c2747f8] URIs v1.5.1
  [3a884ed6] UnPack v1.0.2
  [1986cc42] Unitful v1.19.0
  [a7c27f48] Unityper v0.1.6
  [013be700] UnsafeAtomics v0.2.1
  [d80eeb9a] UnsafeAtomicsLLVM v0.1.3
  [3d5dd08c] VectorizationBase v0.21.65
  [19fa3120] VertexSafeGraphs v0.2.0
  [2e619515] Expat_jll v2.5.0+0
  [f8c6e375] Git_jll v2.44.0+1
  [1d5cc7b8] IntelOpenMP_jll v2024.0.2+0
  [dad2f222] LLVMExtra_jll v0.0.29+0
  [94ce4f54] Libiconv_jll v1.17.0+0
  [856f044c] MKL_jll v2024.0.0+0
  [458c3c95] OpenSSL_jll v3.0.13+1
  [efe28fd5] OpenSpecFun_jll v0.5.5+0
  [f50d1b31] Rmath_jll v0.4.0+0
  [0dad84c5] ArgTools v1.1.1
  [56f22d72] Artifacts
  [2a0f44e3] Base64
  [ade2ca70] Dates
  [8ba89e20] Distributed
  [f43a241f] Downloads v1.6.0
  [7b1f6079] FileWatching
  [9fa8497b] Future
  [b77e0a4c] InteractiveUtils
  [4af54fe1] LazyArtifacts
  [b27032c2] LibCURL v0.6.4
  [76f85450] LibGit2
  [8f399da3] Libdl
  [37e2e46d] LinearAlgebra
  [56ddb016] Logging
  [d6f4376e] Markdown
  [a63ad114] Mmap
  [ca575930] NetworkOptions v1.2.0
  [44cfe95a] Pkg v1.10.0
  [de0858da] Printf
  [3fa0cd96] REPL
  [9a3f8284] Random
  [ea8e919c] SHA v0.7.0
  [9e88b42a] Serialization
  [1a1011a3] SharedArrays
  [6462fe0b] Sockets
  [2f01184e] SparseArrays v1.10.0
  [10745b16] Statistics v1.10.0
  [4607b0f0] SuiteSparse
  [fa267f1f] TOML v1.0.3
  [a4e569a6] Tar v1.10.0
  [8dfed614] Test
  [cf7118a7] UUIDs
  [4ec0a83e] Unicode
  [e66e0078] CompilerSupportLibraries_jll v1.1.0+0
  [deac9b47] LibCURL_jll v8.4.0+0
  [e37daf67] LibGit2_jll v1.6.4+0
  [29816b5a] LibSSH2_jll v1.11.0+1
  [c8ffd9c3] MbedTLS_jll v2.28.2+1
  [14a3606d] MozillaCACerts_jll v2023.1.10
  [4536629a] OpenBLAS_jll v0.3.23+4
  [05823500] OpenLibm_jll v0.8.1+2
  [efcefdf7] PCRE2_jll v10.42.0+1
  [bea87d4a] SuiteSparse_jll v7.2.1+1
  [83775a58] Zlib_jll v1.2.13+1
  [8e850b90] libblastrampoline_jll v5.8.0+1
  [8e850ede] nghttp2_jll v1.52.0+1
  [3f19e933] p7zip_jll v17.4.0+2
Info Packages marked with ⌃ and ⌅ have new versions available. Those with ⌃ may be upgradable, but those with ⌅ are restricted by compatibility constraints from upgrading. To see why use `status --outdated -m`

You can also download the manifest file and the project file.