Skip to content

Commit d893fe1

Browse files
Merge pull request #63 from shahriariravanian/main
upgrade to ver 1.2 with the addition of symbolic and definite integration.
2 parents 28428b9 + 1408768 commit d893fe1

17 files changed

+1061
-667
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "SymbolicNumericIntegration"
22
uuid = "78aadeae-fbc0-11eb-17b6-c7ec0477ba9e"
33
authors = ["Shahriar Iravanian <[email protected]>"]
4-
version = "1.1.0"
4+
version = "1.2.0"
55

66
[deps]
77
DataDrivenDiffEq = "2445eb08-9709-466a-b3fc-47e12bd697a2"

README.md

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,16 +21,19 @@ the documentation, which contains the unreleased features.
2121
## Example
2222

2323
```julia
24-
using Symbolics
25-
using SymbolicNumericIntegration
24+
julia> using SymbolicNumericIntegration
25+
julia> using Symbolics
2626

27-
@variables x
27+
julia> @variables x a b
2828

29-
integrate(3x^3 + 2x - 5)
30-
```
29+
julia> integrate(3x^3 + 2x - 5)
30+
(x^2 + (3//4)*(x^4) - (5//1)*x, 0, 0)
3131

32-
```
33-
(x^2 + (3//4)*(x^4) - (5x), 0, 0)
32+
julia> integrate(exp(a * x), x; symbolic = true)
33+
(exp(a*x) / a, 0, 0)
34+
35+
julia> integrate(sin(a * x) * cos(b * x), x; symbolic = true, detailed = false)
36+
(-a*cos(a*x)*cos(b*x) - b*sin(a*x)*sin(b*x)) / (a^2 - (b^2))
3437
```
3538

3639
# Citation

docs/make.jl

Lines changed: 9 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -6,21 +6,14 @@ using Documenter, SymbolicNumericIntegration
66
include("pages.jl")
77

88
makedocs(sitename = "SymbolicNumericIntegration.jl",
9-
authors = "Shahriar Iravanian",
10-
modules = [SymbolicNumericIntegration],
11-
clean = true, doctest = false, linkcheck = true,
12-
strict = [
13-
:doctest,
14-
:linkcheck,
15-
:parse_error,
16-
:example_block,
17-
# Other available options are
18-
# :autodocs_block, :cross_references, :docs_block, :eval_block, :example_block, :footnote, :meta_block, :missing_docs, :setup_block
19-
],
20-
format = Documenter.HTML(analytics = "UA-90474609-3",
21-
assets = ["assets/favicon.ico"],
22-
canonical = "https://docs.sciml.ai/SymbolicNumericIntegration/stable/"),
23-
pages = pages)
9+
authors = "Shahriar Iravanian",
10+
modules = [SymbolicNumericIntegration],
11+
clean = true, doctest = false, linkcheck = true,
12+
warnonly = true,
13+
format = Documenter.HTML(analytics = "UA-90474609-3",
14+
assets = ["assets/favicon.ico"],
15+
canonical = "https://docs.sciml.ai/SymbolicNumericIntegration/stable/"),
16+
pages = pages)
2417

2518
deploydocs(repo = "github.com/SciML/SymbolicNumericIntegration.jl.git";
26-
push_preview = true)
19+
push_preview = true)

docs/src/index.md

Lines changed: 41 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,13 @@
22

33
**SymbolicNumericIntegration.jl** is a hybrid symbolic/numerical integration package that works on the [Julia Symbolics](https://docs.sciml.ai/Symbolics/stable/) expressions.
44

5-
**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).
6-
The basis of how it works and the theory of integration using the Symbolic-Numeric methods refer to [Basis of Symbolic-Numeric Integration](https://github.com/SciML/SymbolicNumericIntegration.jl/blob/main/docs/theory.ipynb).
5+
**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](https://github.com/SciML/SymbolicNumericIntegration.jl/blob/main/docs/theory.ipynb).
76

8-
Function `integrate` returns the integral of a univariate expression with *constant* real or complex coefficients. `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.
7+
[hyint](https://github.com/siravan/hyint) is the python counterpart of **SymbolicNumericIntegration.jl** that works with **sympy** expressions.
8+
9+
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.
10+
11+
`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.
912

1013
## Installation
1114

@@ -19,57 +22,53 @@ Pkg.add("SymbolicNumericIntegration")
1922
Examples:
2023

2124
```julia
22-
using Symbolics
23-
using SymbolicNumericIntegration
25+
julia> using SymbolicNumericIntegration
26+
julia> using Symbolics
2427

25-
@variables x
26-
```
28+
julia> @variables x a b
29+
30+
# if `detailed = true` (default), the output is a tuple of (solution, unsolved portion, err)
2731

28-
```julia
2932
julia> integrate(3x^3 + 2x - 5)
3033
(x^2 + (3//4)*(x^4) - (5x), 0, 0)
3134

3235
julia> integrate((5 + 2x)^-1)
3336
((1//2)*log((5//2) + x), 0, 0.0)
3437

35-
julia> integrate(1 / (6 + x^2 - (5x)))
36-
(log(x - 3) - log(x - 2), 0, 3.339372764128952e-16)
38+
# `detailed = false` simplifies the output to just the resulting integral
3739

38-
julia> integrate(1 / (x^2 - 16))
39-
((1//8)*log(x - 4) - ((1//8)*log(4 + x)), 0, 1.546926788028958e-16)
40+
julia> integrate(x^2 / (16 + x^2); detailed = false)
41+
x + 4atan((-1//4)*x)
4042

41-
julia> integrate(x^2 / (16 + x^2))
42-
(x + 4atan((-1//4)*x), 0, 1.3318788420751984e-16)
43+
julia> integrate(x^2 * log(x); detailed = false)
44+
(1//3)*(x^3)*log(x) - (1//9)*(x^3)
4345

44-
julia> integrate(x^2 / sqrt(4 + x^2))
45-
((1//2)*x*((4 + x^2)^0.5) - ((2//1)*log(x + sqrt(4 + x^2))), 0, 8.702422633074313e-17)
46+
julia> integrate(sec(x) * tan(x); detailed = false)
47+
sec(x)
4648

47-
julia> integrate(x^2 * log(x))
48-
((1//3)*log(x)*(x^3) - ((1//9)*(x^3)), 0, 0)
49+
# Symbolic integration. Here, a is a symbolic constant; therefore, we need
50+
# to explicitly define the independent variable (say, x). Also, we set
51+
# `symbolic = true` to force using the symbolic solver
4952

50-
julia> integrate(x^2 * exp(x))
51-
(2exp(x) + exp(x)*(x^2) - (2x*exp(x)), 0, 0)
53+
julia> integrate(sin(a * x), x; detailed = false, symbolic = true)
54+
(-cos(a*x)) / a
5255

53-
julia> integrate(tan(2x))
54-
((-1//2)*log(cos(2x)), 0, 0)
56+
julia> integrate(x^2 * cos(a * x), x; detailed = false, symbolic = true)
57+
((a^2)*(x^2)*sin(a*x) + 2.0a*x*cos(a*x) - 2.0sin(a*x)) / (a^3)
5558

56-
julia> integrate(sec(x) * tan(x))
57-
(cos(x)^-1, 0, 0)
59+
julia> integrate(log(log(a * x)) / x, x; detailed = false, symbolic = true)
60+
log(a*x)*log(log(a*x)) - log(a*x)
5861

59-
julia> integrate(cosh(2x) * exp(x))
60-
((2//3)*exp(x)*sinh(2x) - ((1//3)*exp(x)*cosh(2x)), 0, 7.073930088880992e-8)
62+
# multiple symbolic constants
6163

62-
julia> integrate(cosh(x) * sin(x))
63-
((1//2)*sin(x)*sinh(x) - ((1//2)*cos(x)*cosh(x)), 0, 4.8956233716268386e-17)
64+
julia> integrate(cosh(a * x) * exp(b * x), x; detailed = false, symbolic = true)
65+
(a*sinh(a*x)*exp(b*x) - b*cosh(a*x)*exp(b*x)) / (a^2 - (b^2))
6466

65-
julia> integrate(cosh(2x) * sin(3x))
66-
(0.153845sinh(2x)*sin(3x) - (0.23077cosh(2x)*cos(3x)), 0, 4.9807620877373405e-6)
67+
# definite integration, passing a tuple of (x, lower bound, higher bound) in the
68+
# second argument
6769

68-
julia> integrate(log(log(x)) * (x^-1))
69-
(log(x)*log(log(x)) - log(x), 0, 0)
70-
71-
julia> integrate(exp(x^2))
72-
(0, exp(x^2), Inf) # as expected!
70+
julia> integrate(x * sin(a * x), (x, 0, 1); symbolic = true, detailed = false)
71+
(sin(a) - a*cos(a)) / (a^2)
7372
```
7473

7574
SymbolicNumericIntegration.jl exports some special integral functions (defined over Complex numbers) and uses them in solving integrals:
@@ -81,15 +80,15 @@ SymbolicNumericIntegration.jl exports some special integral functions (defined o
8180

8281
For examples:
8382

84-
```
83+
```julia
8584
julia> integrate(exp(x + 1) / (x + 1))
86-
(SymbolicNumericIntegration.Ei(1 + x), 0, 1.1796119636642288e-16)
85+
(SymbolicNumericIntegration.Ei(1 + x), 0, 0.0)
8786

88-
julia> integrate(x * cos(x^2 - 1) / (x^2 - 1))
89-
((1//2)*SymbolicNumericIntegration.Ci(x^2 - 1), 0, 2.7755575615628914e-17)
87+
julia> integrate(x * cos(a*x^2 - 1) / (a*x^2 - 1), x; detailed=false, symbolic=true)
88+
((1//2)*SymbolicNumericIntegration.Ci(a*(x^2) - 1)) / a
9089

91-
julia> integrate(1 / (x*log(log(x))))
92-
(SymbolicNumericIntegration.Li(log(x)), 0, 1.1102230246251565e-16)
90+
julia> integrate(1 / (x*log(log(x))), x; detailed=false, symbolic=true)
91+
SymbolicNumericIntegration.Li(log(x))
9392
```
9493

9594
```@docs
@@ -102,7 +101,7 @@ integrate(eq, x; kwargs...)
102101

103102
Additionally, 12 test suites from the *Rule-based Integrator* ([Rubi](https://rulebasedintegration.org/)) 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)
104103

105-
```julia
104+
```
106105
using SymbolicNumericIntegration
107106
include("test/axiom.jl") # note, you may need to use the correct path
108107
@@ -198,33 +197,3 @@ Pkg.status(; mode = PKGMODE_MANIFEST) # hide
198197
```@raw html
199198
</details>
200199
```
201-
202-
```@raw html
203-
You can also download the
204-
<a href="
205-
```
206-
207-
```@eval
208-
using TOML
209-
version = TOML.parse(read("../../Project.toml", String))["version"]
210-
name = TOML.parse(read("../../Project.toml", String))["name"]
211-
link = "https://github.com/SciML/" * name * ".jl/tree/gh-pages/v" * version *
212-
"/assets/Manifest.toml"
213-
```
214-
215-
```@raw html
216-
">manifest</a> file and the
217-
<a href="
218-
```
219-
220-
```@eval
221-
using TOML
222-
version = TOML.parse(read("../../Project.toml", String))["version"]
223-
name = TOML.parse(read("../../Project.toml", String))["name"]
224-
link = "https://github.com/SciML/" * name * ".jl/tree/gh-pages/v" * version *
225-
"/assets/Project.toml"
226-
```
227-
228-
```@raw html
229-
">project</a> file.
230-
```

src/SymbolicNumericIntegration.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ using SymbolicUtils: exprtype, BasicSymbolic
1010
using DataDrivenDiffEq, DataDrivenSparse
1111

1212
include("utils.jl")
13+
include("tree.jl")
1314
include("special.jl")
1415

1516
include("cache.jl")
@@ -29,6 +30,5 @@ include("integral.jl")
2930
export integrate, generate_basis
3031

3132
include("symbolic.jl")
32-
include("logger.jl")
3333

3434
end # module

src/candidates.jl

Lines changed: 0 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -64,72 +64,6 @@ function closure(eq, x; max_terms = 50)
6464
unique([[s for s in S]; [s * x for s in S]])
6565
end
6666

67-
function candidate_pow_minus(p, k; abstol = 1e-8)
68-
if isnan(poly_deg(p))
69-
return [p^k, p^(k + 1), log(p)]
70-
end
71-
72-
x = var(p)
73-
d = poly_deg(p)
74-
75-
for j in 1:10 # will try 10 times to find the roots
76-
r, s = find_roots(p, x)
77-
if length(r) + length(s) >= d
78-
break
79-
end
80-
end
81-
s = s[1:2:end]
82-
r = nice_parameter.(r)
83-
s = nice_parameter.(s)
84-
85-
# ∫ 1 / ((x-z₁)(x-z₂)) dx = ... + c₁ * log(x-z₁) + c₂ * log(x-z₂)
86-
q = Any[log(x - u) for u in r]
87-
for i in eachindex(s)
88-
β = s[i]
89-
if abs(imag(β)) > abstol
90-
push!(q, atan((x - real(β)) / imag(β)))
91-
push!(q, (1 + x) * log(x^2 - 2 * real(β) * x + abs2(β)))
92-
else
93-
push!(q, log(x - real(β)))
94-
end
95-
end
96-
q = unique(q)
97-
98-
# return [[p^k, p^(k+1)]; candidates(q₁, x)]
99-
if k -1
100-
return [[p^k]; q]
101-
else
102-
return [[p^k, p^(k + 1)]; q]
103-
end
104-
end
105-
106-
function candidate_sqrt(p, k)
107-
x = var(p)
108-
109-
h = Any[p^k, p^(k + 1)]
110-
111-
if poly_deg(p) == 2
112-
r, s = find_roots(p, x)
113-
l = leading(p, x)
114-
if length(r) == 2
115-
if sum(r) 0
116-
r₁ = abs(r[1])
117-
if l > 0
118-
push!(h, acosh(x / r₁))
119-
else
120-
push!(h, asin(x / r₁))
121-
end
122-
end
123-
elseif real(s[1]) 0
124-
push!(h, asinh(x / imag.(s[1])))
125-
end
126-
end
127-
128-
Δ = expand_derivatives(Differential(x)(p))
129-
push!(h, log(0.5 * Δ + sqrt(p)))
130-
return h
131-
end
132-
13367
###############################################################################
13468

13569
enqueue_expr!(S, q, eq, x) = enqueue_expr!!(S, q, ops(eq)..., x)

0 commit comments

Comments
 (0)