Skip to content

Commit f7ab23d

Browse files
committed
moved information from redmen to documentation
1 parent 81826c2 commit f7ab23d

File tree

6 files changed

+206
-266
lines changed

6 files changed

+206
-266
lines changed

README.md

Lines changed: 6 additions & 220 deletions
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,17 @@
33
[![Build Status](https://github.com/JuliaSymbolics/SymbolicIntegration.jl/actions/workflows/CI.yml/badge.svg?branch=main)](https://github.com/JuliaSymbolics/SymbolicIntegration.jl/actions/workflows/CI.yml?query=branch%3Amain)
44
[![Spell Check](https://github.com/JuliaSymbolics/SymbolicIntegration.jl/actions/workflows/spellcheck.yml/badge.svg?branch=main)](https://github.com/JuliaSymbolics/SymbolicIntegration.jl/actions/workflows/spellcheck.yml)
55

6-
- [SymbolicIntegration.jl](#symbolicintegrationjl)
7-
- [Installation](#installation)
8-
- [Usage](#usage)
9-
- [Basic Integration](#basic-integration)
10-
- [Method Selection](#method-selection)
11-
- [Integration Methods](#integration-methods)
12-
- [Risch Method](#rischmethod)
13-
- [Rule Based Method](#rulebasedmethod)
14-
- [Documentation](#documentation)
15-
- [Contributing](#contributing)
16-
- [Citation](#citation)
17-
186

197
SymbolicIntegration.jl provides a flexible, extensible framework for symbolic integration with multiple algorithm choices.
208

21-
# Installation
9+
10+
# Usage
11+
12+
### Installation
2213
```julia
2314
julia> using Pkg; Pkg.add("SymbolicIntegration")
2415
```
2516

26-
# Usage
27-
2817
### Basic Integration
2918

3019
```julia
@@ -48,26 +37,14 @@ The first argument is the expression to integrate, second argument is the variab
4837
You can explicitly choose a integration method like this:
4938
```julia
5039
# Explicit method choice
51-
integrate(f, x, RischMethod())
52-
53-
# ...with special configuration
5440
risch = RischMethod(use_algebraic_closure=true, catch_errors=false)
5541
integrate(f, x, risch)
5642
```
57-
where:
58-
- `use_algebraic_closure` TODO does what?
59-
- `catch_errors` TODO does what?
60-
6143
or
6244
```julia
63-
integrate(f, x, RuleBasedMethod())
64-
6545
rbm = RuleBasedMethod(verbose=true, use_gamma=false)
6646
integrate(f, x, rbm)
6747
```
68-
where:
69-
- `verbsoe` specifies whether to print or not the integration rules applied (default true)
70-
- `use_gamma` specifies whether to use rules with the gamma function in the result, or not (default false)
7148

7249
If no method is specified, first RischMethod will be tried, then RuleBasedMethod:
7350
```julia
@@ -118,215 +95,24 @@ Complete symbolic integration using the Risch algorithm from Manuel Bronstein's
11895
- ✅ **Trigonometric functions**: Via transformation to exponential form
11996
- ❌ **More than one symbolic variable**: Integration w.r.t. one variable, with other symbolic variables present in the expression
12097
121-
**Function Classes:**
122-
- Polynomial functions: `∫x^n dx`, `∫(ax^2 + bx + c) dx`
123-
- Rational functions: `∫P(x)/Q(x) dx` → logarithmic and arctangent terms
124-
- Exponential functions: `∫exp(f(x)) dx`, `∫x*exp(x) dx`
125-
- Logarithmic functions: `∫log(x) dx`, `∫1/(x*log(x)) dx`
126-
- Trigonometric functions: `∫sin(x) dx`, `∫cos(x) dx`, `∫tan(x) dx`
127-
12898
## RuleBasedMethod
12999
130100
[![Rules](https://img.shields.io/badge/dynamic/json?url=https://raw.githubusercontent.com/JuliaSymbolics/SymbolicIntegration.jl/main/.github/badges/rules-count.json&query=$.message&label=Total%20rules&color=blue)](https://github.com/JuliaSymbolics/SymbolicIntegration.jl)
131101
132102
This method uses a rule based approach to integrate a vast class of functions, and it's built using the rules from the Mathematica package [RUBI](https://rulebasedintegration.org/).
133103
134104
**Capabilities:**
135-
- ✅ TODO add others
105+
- ✅ Fast convergence for a large base of recognized cases
106+
- ✅ Algebraic functions like `sqrt` and non-integer powers are supported
136107
- ✅ **More than one symbolic variable**: Integration w.r.t. one variable, with other symbolic variables present in the expression
137108
138-
### How it works internally
139-
The rules are defined using the SymbolicUtils [rule macro](https://symbolicutils.juliasymbolics.org/rewrite/#rule-based_rewriting) and are of this form:
140-
```julia
141-
# rule 1_1_1_1_2
142-
@rule ((~x)^(~!m),(~x)) =>
143-
!contains_var((~m), (~x)) &&
144-
!eq((~m), -1) ?
145-
(~x)^((~m) + 1)((~m) + 1) : nothing
146-
```
147-
The rule left hand side pattern is the symbolic function `∫(var1, var2)` where first variable is the integrand and second is the integration variable. After the => there are some conditions to determine if the rules are applicable, and after the ? there is the transformation. Note that this may still contain a integral, so a walk in pre order of the tree representing the symbolic expression is done, applying rules to each node containing the integral.
148-
149-
The infix operator `⨸` is used to represent a custom division function, if called on integers returns a rational and if called on floats returns a float. This is done because // operator does not support floats. This specific character was chosen because it resembles the division symbol and because it has the same precedence as /.
150-
151-
Not all rules are yet translated, I am each day translating more of them. If you want to know how to help translating rules and improving the package read the [contributing](#contributing) section. If you enconunter any issues using the package, please write me or open a issue on the repo.
152-
153-
# Test
154-
To test the package run
155-
```
156-
julia --project=. test/runtests.jl
157-
```
158-
or in a Repl:
159-
```
160-
julia> using Symbolics, SymbolicIntegration
161-
162-
julia> include("test/runtests.jl")
163-
164-
```
165109
166110
# Documentation
167111
168112
Complete documentation with method selection guidance, algorithm details, and examples is available at:
169113
**[https://symbolicintegration.juliasymbolics.org](https://symbolicintegration.juliasymbolics.org)**
170114
171115
172-
173-
174-
175-
# Contributing
176-
In this repo there is also some software that serves the sole purpose of helping with the translation of rules from Mathematica syntax, and not for the actual package working. The important ones are:
177-
- translator_of_rules.jl is a script that with regex and other string manipulations translates from Mathematica syntax to julia syntax
178-
- translator_of_testset.jl is a script that translates the testsets into julia syntax (much simpler than translator_of_rules.jl)
179-
- `reload_rules` function in rules_loader.jl. When developing the package using Revise is not enough because rules are defined with a macro. So this function reloads rules from a specific .jl file or from all files if called without arguments.
180-
181-
my typical workflow is:
182-
- translate a rule file with translator_of_rules.jl. In the resulting file there could be some problems:
183-
- - maybe a Mathematica function that i never encountered before and therefore not included in the translation script (and in rules_utility_functions.jl)
184-
- - maybe a Mathematica syntax that I never encountered before and not included in the translation script
185-
- - others, see [Common problems when translating rules](#common-problems-when-translating-rules)
186-
- If the problem is quite common in other rules: implement in the translation script and translate the rule again, otherwise fix it manually in the .jl file
187-
188-
The rules not yet translated are mainly those from sections 4 to 8
189-
190-
## Common problems when translating rules
191-
### function not translated
192-
If you encounter a normal function that is not translated by the script, it will stay untranslated, with square brackets, like this:
193-
```
194-
sqrt(Sign[(~b)]*sin((~e) + (~f)*(~x)))sqrt((~d)*sin((~e) + (~f)*(~x)))* (1(sqrt((~a) + (~b)*sin((~e) + (~f)*(~x)))*sqrt(Sign[(~b)]*sin((~e) + (~f)*(~x)))), (~x)) : nothing)
195-
```
196-
a trick to find them fast is to search the regex pattern `(?<=^[^#]).*\[` in all the file. If you find them and they are already presen in julia or you implement them in rules_utility_functions.jl, you can simply add the to the smart_replace list in the translator and translate the script again.
197-
198-
### Sum function translation
199-
the `Sum[...]` function gets translated with this regex:
200-
```
201-
(r"Sum\[(.*?),\s*\{(.*?),(.*?),(.*?)\}\]", s"sum([\1 for \2 in (\3):(\4)])"),
202-
```
203-
its quite common that the \1 is a <=2 letter variable, and so will get translated from the translator into a slot variable, appending ~.
204-
205-
For example
206-
```
207-
Sum[Int[1/(1 - Sin[e + f*x]^2/((-1)^(4*k/n)*Rt[-a/b, n/2])), x], {k, 1, n/2}]
208-
```
209-
gets translated to
210-
```
211-
sum([∫(1⨸(1 - sin((~e) + (~f)*(~x))^2⨸((-1)^(4*(~k)⨸(~n))*rt(-(~a)⨸(~b), (~n)⨸2))), (~x)) for (~k) in ( 1):( (~n)⨸2)]
212-
```
213-
while it should be
214-
```
215-
sum([∫(1⨸(1 - sin((~e) + (~f)*(~x))^2⨸((-1)^(4*k⨸(~n))*rt(-(~a)⨸(~b), (~n)⨸2))), (~x)) for k in ( 1):( (~n)⨸2)]),
216-
```
217-
so what I usually do is to change the "index of the summation" variable to a >2 letters name in the Mathematica file, like this
218-
```
219-
Sum[Int[1/(1 - Sin[e + f*x]^2/((-1)^(4*iii/n)*Rt[-a/b, n/2])), x], {iii, 1, n/2}]
220-
```
221-
so that will not be translated into slot variable.
222-
```
223-
sum([∫(1⨸(1 - sin((~e) + (~f)*(~x))^2⨸((-1)^(4*iii⨸(~n))*rt(-(~a)⨸(~b), (~n)⨸2))), (~x)) for iii in ( 1):( (~n)⨸2)]),
224-
```
225-
### Module syntax translation
226-
The `Module` Syntax is similar to the `With` syntax, but a bit different and for now is not handled by the script
227-
228-
### * not present or present as \[Star]
229-
in Mathematica if you write `a b` or `a \[Star] b` is interpreted as `a*b`. So sometimes in the rules is written like that. When it happens i usually add the * in the mathematica file, and then i translate it
230-
231-
## Description of the script `src/translator_of_rules.jl`
232-
This script is used to translate integration rules from Mathematica syntax
233-
to julia Syntax.
234-
235-
### How to use it
236-
``` bash
237-
julia src/translator_of_rules.jl "src/rules/4 Trig functions/4.1 Sine/4.1.8 trig^m (a+b cos^p+c sin^q)^n.m"
238-
```
239-
and will produce the julia file at the path `src/rules/4 Trig functions/4.1 Sine/4.1.8 trig^m (a+b cos^p+c sin^q)^n.jl`
240-
241-
### How it works internally (useful to know if you have to debug it)
242-
It processes line per line, so the integration rule must be all on only one
243-
line. Let's say we translate this (fictional) rule:
244-
```
245-
Int[x_^m_./(a_ + b_. + c_.*x_^4), x_Symbol] := With[{q = Rt[a/c, 2], r = Rt[2*q - b/c, 2]}, 1/(2*c*r)*Int[x^(m - 3), x] - 1/(2*c*r) /; OddQ[r]] /; FreeQ[{a, b, c}, x] && (NeQ[b^2 - 4*a*c, 0] || (GeQ[m, 3] && LtQ[m, 4])) && NegQ[b^2 - 4*a*c]
246-
```
247-
#### With syntax
248-
for each line it first check if there is the With syntax, a syntax in Mathematica
249-
that enables to define variables in a local scope. If yes it can do two things:
250-
In the new method translates the block using the let syntax, like this:
251-
```julia
252-
@rule ((~x)^(~!m)/((~a) + (~!b) + (~!c)*(~x)^4),(~x)) =>
253-
!contains_var((~a), (~b), (~c), (~x)) &&
254-
(
255-
!eq((~b)^2 - 4*(~a)*(~c), 0) ||
256-
(
257-
ge((~m), 3) &&
258-
lt((~m), 4)
259-
)
260-
) &&
261-
neg((~b)^2 - 4*(~a)*(~c)) ?
262-
let
263-
q = rt((~a)(~c), 2)
264-
r = rt(2*q - (~b)(~c), 2)
265-
266-
ext_isodd(r) ?
267-
1(2*(~c)*r)*((~x)^((~m) - 3), (~x)) - 1(2*(~c)*r) : nothing
268-
end : nothing
269-
```
270-
The old method was to finds the defined variables and substitute them with their
271-
definition. Also there could be conditions inside the With block (OddQ in the example),
272-
that were bought outside.
273-
```
274-
1/(2*c*Rt[2*q - b/c, 2])*Int[x^(m - 3), x] - 1/(2*c*Rt[2*q - b/c, 2])/; FreeQ[{a, b, c}, x] && (NeQ[b^2 - 4*a*c, 0] || (GeQ[m, 3] && LtQ[m, 4])) && NegQ[b^2 - 4*a*c] && OddQ[Rt[2*q - b/c, 2]]
275-
```
276-
#### replace and smart_replace applications
277-
Then the line is split into integral, result, and conditions:
278-
```
279-
Int[x_^m_./(a_ + b_. + c_.*x_^4), x_Symbol]
280-
```
281-
```
282-
1/(2*c*Rt[2*q - b/c, 2])*Int[x^(m - 3), x] - 1/(2*c*Rt[2*q - b/c, 2])
283-
```
284-
```
285-
FreeQ[{a, b, c}, x] && (NeQ[b^2 - 4*a*c, 0] || (GeQ[m, 3] && LtQ[m, 4])) && NegQ[b^2 - 4*a*c] && OddQ[Rt[2*q - b/c, 2]]
286-
```
287-
288-
Each one of them is translated using the appropriate function, but the three
289-
all work the same. They first apply a number of times the smart_replace function,
290-
that replaces functions names without messing the nested brackets (like normal regex do)
291-
```
292-
smart_replace("ArcTan[Rt[b, 2]*x/Rt[a, 2]] + Log[x]", "ArcTan", "atan")
293-
# output
294-
"atan(Rt[b, 2]*x/Rt[a, 2]) + Log[x]"
295-
```
296-
Then also the normal replace function is applied a number of times, for more
297-
complex patterns. For example, every two letter word, optionally followed by
298-
numbers, that is not a function call (so not followed by open parenthesis), and
299-
that is not the "in" word, is prefixed with a tilde `~`. This is because in
300-
Mathematica you can reference the slot variables without any prefix, and in
301-
julia you need ~.
302-
303-
#### Pretty indentation
304-
Then they are all put together following the julia rules syntax
305-
@rule integrand => conditions ? result : nothing
306-
```
307-
@rule ∫((~x)^(~!m)/((~a) + (~!b) + (~!c)*(~x)^4),(~x)) => !contains_var((~a), (~b), (~c), (~x)) && (!eq((~b)^2 - 4*(~a)*(~c), 0) || (ge((~m), 3) && lt((~m), 4))) && neg((~b)^2 - 4*(~a)*(~c)) && ext_isodd(rt(2*(~q) - (~b)/(~c), 2)) ? 1⨸(2*(~c)*rt(2*(~q) - (~b)⨸(~c), 2))*∫((~x)^((~m) - 3), (~x)) - 1⨸(2*(~c)*rt(2*(~q) - (~b)⨸(~c), 2)) : nothing
308-
```
309-
Usually the conditions are a lot of && and ||, so a pretty indentation is
310-
applied automatically that rewrites the rule like this:
311-
```
312-
@rule ∫((~x)^(~!m)/((~a) + (~!b) + (~!c)*(~x)^4),(~x)) =>
313-
!contains_var((~a), (~b), (~c), (~x)) &&
314-
(
315-
!eq((~b)^2 - 4*(~a)*(~c), 0) ||
316-
(
317-
ge((~m), 3) &&
318-
lt((~m), 4)
319-
)
320-
) &&
321-
neg((~b)^2 - 4*(~a)*(~c)) &&
322-
ext_isodd(rt(2*(~q) - (~b)/(~c), 2)) ?
323-
1⨸(2*(~c)*rt(2*(~q) - (~b)⨸(~c), 2))*∫((~x)^((~m) - 3), (~x)) - 1⨸(2*(~c)*rt(2*(~q) - (~b)⨸(~c), 2)) : nothing
324-
```
325-
326-
#### end
327-
finally the rule is placed in a tuple (index, rule), and all the
328-
tuples are put into a array, ready to be included by load_rules
329-
330116
# Citation
331117
332118
If you use SymbolicIntegration.jl in your research, please cite:

docs/src/index.md

Lines changed: 28 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -4,27 +4,7 @@
44
CurrentModule = SymbolicIntegration
55
```
66

7-
SymbolicIntegration.jl provides Julia implementations of symbolic integration algorithms.
8-
9-
The front-end (i.e., the user interface) uses [Symbolics.jl](https://docs.sciml.ai/Symbolics/stable/).
10-
The actual integration algorithms are implemented in a generic way using [AbstractAlgebra.jl](https://nemocas.github.io/AbstractAlgebra.jl/dev/).
11-
Some algorithms require [Nemo.jl](https://nemocas.github.io/Nemo.jl/dev/) for calculations with algebraic numbers.
12-
13-
SymbolicIntegration.jl is based on the algorithms from the book
14-
15-
> Manuel Bronstein, [Symbolic Integration I: Transcentental Functions](https://link.springer.com/book/10.1007/b138171), 2nd ed, Springer 2005,
16-
17-
for which a pretty complete set of reference implementations is provided.
18-
19-
Currently, SymbolicIntegration.jl can integrate:
20-
- Rational functions
21-
- Integrands involving transcendental elementary functions like `exp`, `log`, `sin`, etc.
22-
23-
As in the book, integrands involving algebraic functions like `sqrt` and non-integer powers are not treated.
24-
25-
!!! note
26-
SymbolicIntegration.jl is still in an early stage of development and should not be expected to run stably in all situations.
27-
It comes with absolutely no warranty whatsoever.
7+
SymbolicIntegration.jl lets you solve indefinite integrals (finds primitives) in Julia [Symbolics.jl](https://docs.sciml.ai/Symbolics/stable/). It does so using two symbolic integration algorithms: Risch algorithm and Rule based algorithm.
288

299
## Installation
3010

@@ -37,22 +17,20 @@ julia> using Pkg; Pkg.add("SymbolicIntegration")
3717
```julia
3818
using SymbolicIntegration, Symbolics
3919

40-
@variables x
20+
@variables x a
4121

4222
# Basic polynomial integration (uses default RischMethod)
4323
integrate(x^2, x) # Returns (1//3)*(x^3)
4424

45-
# Rational function integration with complex roots
25+
# Rational function integration
26+
integrate(1/(x^2 + 1), x) # Returns atan(x)
4627
f = (x^3 + x^2 + x + 2)/(x^4 + 3*x^2 + 2)
4728
integrate(f, x) # Returns (1//2)*log(2 + x^2) + atan(x)
4829

4930
# Transcendental functions
5031
integrate(exp(x), x) # Returns exp(x)
5132
integrate(log(x), x) # Returns -x + x*log(x)
5233

53-
# Complex root integration (arctangent cases)
54-
integrate(1/(x^2 + 1), x) # Returns atan(x)
55-
5634
# Method selection and configuration
5735
integrate(f, x, RischMethod()) # Explicit method choice
5836
integrate(f, x, RischMethod(use_algebraic_closure=true)) # With options
@@ -63,7 +41,7 @@ integrate(f, x, RischMethod(use_algebraic_closure=true)) # With options
6341

6442
SymbolicIntegration.jl provides multiple integration algorithms through a flexible method dispatch system:
6543

66-
### RischMethod (Default)
44+
### RischMethod
6745
The complete Risch algorithm for elementary function integration:
6846
- **Exact results**: Guaranteed correct symbolic integration
6947
- **Complex roots**: Produces exact arctangent terms
@@ -78,11 +56,26 @@ integrate(f, x)
7856
integrate(f, x, RischMethod(use_algebraic_closure=true))
7957
```
8058

81-
### Future Methods
82-
The framework supports additional integration algorithms:
83-
- **HeuristicMethod**: Fast pattern-matching integration
84-
- **NumericalMethod**: Numerical integration fallbacks
85-
- **SymPyMethod**: SymPy backend compatibility
59+
Is implemented in a generic way using [AbstractAlgebra.jl](https://nemocas.github.io/AbstractAlgebra.jl/dev/). Some algorithms require [Nemo.jl](https://nemocas.github.io/Nemo.jl/dev/) for calculations with algebraic numbers.
60+
61+
Is based on the algorithms from the book:
62+
63+
> Manuel Bronstein, [Symbolic Integration I: Transcentental Functions](https://link.springer.com/book/10.1007/b138171), 2nd ed, Springer 2005,
64+
65+
for which a pretty complete set of reference implementations is provided.
66+
67+
Currently, RischMethod can integrate:
68+
- Rational functions
69+
- Integrands involving transcendental elementary functions like `exp`, `log`, `sin`, etc.
70+
71+
As in the book, integrands involving algebraic functions like `sqrt` and non-integer powers are not treated.
72+
73+
!!! note
74+
SymbolicIntegration.jl is still in an early stage of development and should not be expected to run stably in all situations.
75+
It comes with absolutely no warranty whatsoever.
76+
77+
### RuleBased
78+
TODO add
8679

8780
[→ See complete methods documentation](methods/overview.md)
8881

@@ -105,7 +98,7 @@ The **RischMethod** implements the complete suite of algorithms from Bronstein's
10598

10699
## Contributing
107100

108-
We welcome contributions! Please see the [Symbolics.jl contributing guidelines](https://docs.sciml.ai/Symbolics/stable/contributing/).
101+
We welcome contributions! Please see the [contributing](manual/contributing.md) page and the [Symbolics.jl contributing guidelines](https://docs.sciml.ai/Symbolics/stable/contributing/).
109102

110103
## Citation
111104

@@ -131,4 +124,5 @@ Pages = [
131124
"api.md"
132125
]
133126
Depth = 2
134-
```
127+
```
128+

0 commit comments

Comments
 (0)