Skip to content

Commit e2b153a

Browse files
merged with hyint for symbolic integration
1 parent de34d8c commit e2b153a

File tree

7 files changed

+60
-98
lines changed

7 files changed

+60
-98
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: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,13 +24,16 @@ the documentation, which contains the unreleased features.
2424
using Symbolics
2525
using SymbolicNumericIntegration
2626

27-
@variables x
27+
@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: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,14 +9,7 @@ makedocs(sitename = "SymbolicNumericIntegration.jl",
99
authors = "Shahriar Iravanian",
1010
modules = [SymbolicNumericIntegration],
1111
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-
],
12+
warnonly = true,
2013
format = Documenter.HTML(analytics = "UA-90474609-3",
2114
assets = ["assets/favicon.ico"],
2215
canonical = "https://docs.sciml.ai/SymbolicNumericIntegration/stable/"),

docs/src/index.md

Lines changed: 31 additions & 71 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

@@ -22,7 +25,7 @@ Examples:
2225
using Symbolics
2326
using SymbolicNumericIntegration
2427

25-
@variables x
28+
@variables x a b
2629
```
2730

2831
```julia
@@ -32,44 +35,31 @@ julia> integrate(3x^3 + 2x - 5)
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)
37-
38-
julia> integrate(1 / (x^2 - 16))
39-
((1//8)*log(x - 4) - ((1//8)*log(4 + x)), 0, 1.546926788028958e-16)
40-
41-
julia> integrate(x^2 / (16 + x^2))
42-
(x + 4atan((-1//4)*x), 0, 1.3318788420751984e-16)
43-
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-
47-
julia> integrate(x^2 * log(x))
48-
((1//3)*log(x)*(x^3) - ((1//9)*(x^3)), 0, 0)
38+
# detailed simplifies the output to just the resulting integral
39+
julia> integrate(x^2 / (16 + x^2); detailed=false)
40+
x + 4atan((-1//4)*x)
4941

50-
julia> integrate(x^2 * exp(x))
51-
(2exp(x) + exp(x)*(x^2) - (2x*exp(x)), 0, 0)
42+
julia> integrate(x^2 * log(x); detailed=false)
43+
(1//3)*(x^3)*log(x) - (1//9)*(x^3)
5244

53-
julia> integrate(tan(2x))
54-
((-1//2)*log(cos(2x)), 0, 0)
45+
julia> integrate(sec(x) * tan(x); detailed=false)
46+
sec(x)
5547

56-
julia> integrate(sec(x) * tan(x))
57-
(cos(x)^-1, 0, 0)
48+
# Here, a is a symbolic constant; therefore, we need to explicitly
49+
# define the independent variable (say, x). Also, we set
50+
# `symbolic=true` to force using the symbolic solver
51+
julia> integrate(sin(a*x), x; detailed=false, symbolic=true)
52+
(-cos(a*x)) / a
5853

59-
julia> integrate(cosh(2x) * exp(x))
60-
((2//3)*exp(x)*sinh(2x) - ((1//3)*exp(x)*cosh(2x)), 0, 7.073930088880992e-8)
54+
julia> integrate(x^2 * cos(a*x), x; detailed=false, symbolic=true)
55+
((a^2)*(x^2)*sin(a*x) + 2.0a*x*cos(a*x) - 2.0sin(a*x)) / (a^3)
6156

62-
julia> integrate(cosh(x) * sin(x))
63-
((1//2)*sin(x)*sinh(x) - ((1//2)*cos(x)*cosh(x)), 0, 4.8956233716268386e-17)
57+
# multiple symbolic constants
58+
julia> integrate(cosh(a*x) * exp(b*x), x; detailed=false, symbolic=true)
59+
(a*sinh(a*x)*exp(b*x) - b*cosh(a*x)*exp(b*x)) / (a^2 - (b^2))
6460

65-
julia> integrate(cosh(2x) * sin(3x))
66-
(0.153845sinh(2x)*sin(3x) - (0.23077cosh(2x)*cos(3x)), 0, 4.9807620877373405e-6)
67-
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!
61+
julia> integrate(log(log(a*x)) / x, x; detailed=false, symbolic=true)
62+
log(a*x)*log(log(a*x)) - log(a*x)
7363
```
7464

7565
SymbolicNumericIntegration.jl exports some special integral functions (defined over Complex numbers) and uses them in solving integrals:
@@ -83,13 +73,13 @@ For examples:
8373

8474
```
8575
julia> integrate(exp(x + 1) / (x + 1))
86-
(SymbolicNumericIntegration.Ei(1 + x), 0, 1.1796119636642288e-16)
76+
(SymbolicNumericIntegration.Ei(1 + x), 0, 0.0)
8777
88-
julia> integrate(x * cos(x^2 - 1) / (x^2 - 1))
89-
((1//2)*SymbolicNumericIntegration.Ci(x^2 - 1), 0, 2.7755575615628914e-17)
78+
julia> integrate(x * cos(a*x^2 - 1) / (a*x^2 - 1), x; detailed=false, symbolic=true)
79+
((1//2)*SymbolicNumericIntegration.Ci(a*(x^2) - 1)) / a
9080
91-
julia> integrate(1 / (x*log(log(x))))
92-
(SymbolicNumericIntegration.Li(log(x)), 0, 1.1102230246251565e-16)
81+
julia> integrate(1 / (x*log(log(x))), x; detailed=false, symbolic=true)
82+
SymbolicNumericIntegration.Li(log(x))
9383
```
9484

9585
```@docs
@@ -198,33 +188,3 @@ Pkg.status(; mode = PKGMODE_MANIFEST) # hide
198188
```@raw html
199189
</details>
200190
```
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/integral.jl

Lines changed: 7 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -29,14 +29,15 @@ Keyword Arguments:
2929
- `num_trials` (default: `10`): the number of trials in each step (no changes to the basis)
3030
- `show_basis` (default: `false`): if true, the basis (list of candidate terms) is printed
3131
- `bypass` (default: `false`): if true do not integrate terms separately but consider all at once
32-
- `symbolic` (default: `false`): try symbolic integration first (will be forced if `eq` has constant parameters)
32+
- `symbolic` (default: `false`): try symbolic integration first (will be forced if `eq` has symbolic constants)
3333
- `max_basis` (default: `100`): the maximum number of candidate terms to consider
3434
- `verbose` (default: `false`): print a detailed report
3535
- `complex_plane` (default: `true`): generate random test points on the complex plane (if false, the points will be on real axis)
3636
- `radius` (default: `1.0`): the radius of the disk in the complex plane to generate random test points
3737
- `opt` (default: `STLSQ(exp.(-10:1:0))`): the sparse regression optimizer (from DataDrivenSparse)
3838
- `homotopy` (default: `true`): use the homotopy algorithm to generate the basis (*deprecated*, will be removed in a future version)
3939
- `use_optim` (default: `false`): use Optim.jl `minimize` function instead of the STLSQ algorithm (*experimental*)
40+
- `detailed` (default: `true`): `(solved, unsolved, err)` output format. If `detailed=false`, only the final integral is returned.
4041
4142
Output:
4243
-------
@@ -52,11 +53,12 @@ function integrate(eq, x = nothing; abstol = 1e-6, num_steps = 2, num_trials = 1
5253
eq = expand(eq)
5354

5455
if x == nothing
55-
if !isempty(sym_consts(eq, x))
56+
vars = get_variables(eq)
57+
if length(vars) > 1
5658
error("Multiple symbolic variables detect. Please pass the independent variable to `integrate`")
57-
end
58-
x = var(eq)
59-
if x == nothing
59+
elseif length(vars) == 1
60+
x = vars[1]
61+
else
6062
@syms 𝑥
6163
x = 𝑥
6264
end

src/sparse.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,7 @@ function hints(eq, x, basis; radius=5.0, abstol=1e-6, opt=STLSQ(exp.(-10:1:0)),
198198
end
199199
return h, err
200200
catch e
201-
println("Error from hints", e)
201+
# println("Error from hints: ", e)
202202
end
203203

204204
return 0, Inf
@@ -207,7 +207,7 @@ end
207207

208208
function best_hints(eq, x, basis; radius=5.0, abstol=1e-6, opt=STLSQ(exp.(-10:1:0)), complex_plane=true, num_trials=10)
209209
H = []
210-
L = Int[]
210+
L = Int[]
211211

212212
for _ in 1:num_trials
213213
try

test/runtests.jl

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ include("axiom.jl")
1111

1212
##############################################################################
1313

14-
@variables x a b β
14+
@variables x a b c β
1515

1616
"""
1717
a list of basic standard integral tests
@@ -209,6 +209,7 @@ basic_integrals = [
209209
sym_integrals = [
210210
# Basic Forms
211211
a*x^2,
212+
a*x + b*x^2 - c*x^3,
212213
a / x,
213214
1 / (a*x + 5),
214215
1 / (x + a)^2,
@@ -238,20 +239,23 @@ sym_integrals = [
238239
sec(a*x),
239240
x * cos(a*x),
240241
x^2 * cos(a*x),
241-
sin(a*x)^2 * cos(b*x)^3,
242242
exp(a*x) * sin(b*x),
243243
x * exp(a*x) * sin(a*x),
244244
x * exp(a*x) * cos(b*x),
245245
cosh(a*x),
246246
exp(a*x) * cosh(b*x),
247247
cos(a*x) * cosh(b*x),
248+
sin(a*x) * cos(b*x) * exp(c*x),
249+
sin(a*x) * sinh(b*x) * exp(c*x),
248250
sec(a*x)^2 * tan(a*x),
249251
exp(a*x) / (1 + exp(a*x)),
252+
exp(a*x) / exp(b*x),
250253
cos(exp(a*x)) * sin(exp(a*x)) * exp(a*x),
251254
1 / (x * log(a*x)),
252255
log(log(a*x)) / x,
253256
sin(log(a*x)),
254-
x / (exp(a*x) - b),
257+
x / (exp(a*x) - b),
258+
exp(a*x) / (b*exp(a*x) + c),
255259
exp(a*x) * exp(exp(a*x)),
256260
log(cos(a*x)) * tan(a*x),
257261
1 / (x^3 + a),
@@ -266,14 +270,14 @@ sym_integrals = [
266270
]
267271

268272

269-
function test_integrals(basis=true, subs=nothing; kw...)
273+
function test_integrals(basic=true, subs=nothing; kw...)
270274
args = isempty(kw) ? Dict() : Dict(kw)
271275
args[:detailed] = false
272276
misses = []
273277
k = 1
274278

275-
integrals = basis ? basic_integrals : sym_integrals
276-
args[:symbolic] = !basis
279+
integrals = basic ? basic_integrals : sym_integrals
280+
args[:symbolic] = !basic
277281

278282
for (i, eq) in enumerate(integrals)
279283
if isequal(eq, β)

0 commit comments

Comments
 (0)