Skip to content

Commit 0f6cd70

Browse files
Add initial Bessel rule support
1 parent f4b85c0 commit 0f6cd70

10 files changed

Lines changed: 284 additions & 6 deletions

File tree

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ HypergeometricFunctions = "34004b35-14d8-5ef3-9330-4cdb6864b03a"
1616
Logging = "56ddb016-857b-54e1-b83d-db4d58db5568"
1717
Nemo = "2edaba10-b0f1-5616-af89-8c11ac63239a"
1818
PolyLog = "85e3b03c-9856-11eb-0374-4dc1f8670e7f"
19+
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
1920
SymbolicUtils = "d1185830-fcd6-423d-90d6-eec64667417b"
2021
Symbolics = "0c5d862f-8b57-4792-8d23-62f2024744c7"
2122

@@ -27,6 +28,7 @@ FresnelIntegrals = "0.2.0"
2728
HypergeometricFunctions = "0.3.28"
2829
Nemo = "0.51, 0.52, 0.53, 0.54, 0.55"
2930
PolyLog = "2.6.0"
31+
SpecialFunctions = "2"
3032
SymbolicUtils = "4.27"
3133
Symbolics = "7"
3234
julia = "1.10"

src/methods/rule_based/general.jl

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# ===== Special functions from a lot of packages
22

3-
# function from SpecialFunctions.jl used:
3+
# functions from SpecialFunctions.jl used:
44
# SpecialFunctions.expinti(x)
55
# SpecialFunctions.expint(nu, z)
66
# SpecialFunctions.gamma(x, y)
@@ -10,6 +10,18 @@
1010
# SpecialFunctions.loggamma
1111
# SpecialFunctions.erfi
1212
# SpecialFunctions.erf
13+
# SpecialFunctions.besselj(nu, z)
14+
# SpecialFunctions.bessely(nu, z)
15+
# SpecialFunctions.besseli(nu, z)
16+
# SpecialFunctions.besselk(nu, z)
17+
# SpecialFunctions.hankelh1(nu, z)
18+
# SpecialFunctions.hankelh2(nu, z)
19+
# SpecialFunctions.airyai(z)
20+
# SpecialFunctions.airybi(z)
21+
# SpecialFunctions.airyaiprime(z)
22+
# SpecialFunctions.airybiprime(z)
23+
24+
using SpecialFunctions
1325

1426
using Elliptic
1527
@register_symbolic Elliptic.F(phi, m) # incomplete first kind
@@ -230,4 +242,6 @@ all_rules_paths = [
230242
"7 Inverse hyperbolic functions/7.3 Inverse hyperbolic tangent/7.3.1 (a+b arctanh(c x^n))^p.jl"
231243
"7 Inverse hyperbolic functions/7.3 Inverse hyperbolic tangent/7.3.2 (d x)^m (a+b arctanh(c x^n))^p.jl"
232244
"7 Inverse hyperbolic functions/7.3 Inverse hyperbolic tangent/7.3.3 (d+e x)^m (a+b arctanh(c x^n))^p.jl"
245+
246+
"8 Special functions/8.1 Bessel functions.jl"
233247
]
Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
file_rules = [
2+
#(* ::Subsection::Closed:: *)
3+
#(* 8.1 Bessel functions *)
4+
5+
(
6+
"8_1_1",
7+
:((~x)^(~!nu) * besselj((~!mu), (~!a) * (~x))) => :(
8+
!contains_var((~a), (~mu), (~nu), (~x)) &&
9+
eq((~mu), (~nu) - 1) &&
10+
!eq((~a), 0) ?
11+
(~x)^(~nu) * SpecialFunctions.besselj((~nu), (~a) * (~x)) (~a) : nothing
12+
),
13+
)
14+
15+
(
16+
"8_1_2",
17+
:((~x)^(~!nu) * bessely((~!mu), (~!a) * (~x))) => :(
18+
!contains_var((~a), (~mu), (~nu), (~x)) &&
19+
eq((~mu), (~nu) - 1) &&
20+
!eq((~a), 0) ?
21+
(~x)^(~nu) * SpecialFunctions.bessely((~nu), (~a) * (~x)) (~a) : nothing
22+
),
23+
)
24+
25+
(
26+
"8_1_3",
27+
:((~x)^(~!nu) * besseli((~!mu), (~!a) * (~x))) => :(
28+
!contains_var((~a), (~mu), (~nu), (~x)) &&
29+
eq((~mu), (~nu) - 1) &&
30+
!eq((~a), 0) ?
31+
(~x)^(~nu) * SpecialFunctions.besseli((~nu), (~a) * (~x)) (~a) : nothing
32+
),
33+
)
34+
35+
(
36+
"8_1_4",
37+
:((~x)^(~!nu) * besselk((~!mu), (~!a) * (~x))) => :(
38+
!contains_var((~a), (~mu), (~nu), (~x)) &&
39+
eq((~mu), (~nu) - 1) &&
40+
!eq((~a), 0) ?
41+
-(~x)^(~nu) * SpecialFunctions.besselk((~nu), (~a) * (~x)) (~a) : nothing
42+
),
43+
)
44+
45+
(
46+
"8_1_5",
47+
:((~x)^(~!m) * besselj((~!nu), (~!a) * (~x))) => :(
48+
!contains_var((~a), (~m), (~nu), (~x)) &&
49+
igt(simplify((~m) - (~nu) - 1), 0) &&
50+
ext_iseven(simplify((~m) - (~nu) - 1)) &&
51+
!eq((~a), 0) ?
52+
(~x)^(~m) * SpecialFunctions.besselj((~nu) + 1, (~a) * (~x)) (~a) -
53+
simplify((~m) - (~nu) - 1) (~a) * ((~x)^((~m) - 1) * SpecialFunctions.besselj((~nu) + 1, (~a) * (~x)), (~x)) : nothing
54+
),
55+
)
56+
57+
(
58+
"8_1_6",
59+
:((~x)^(~!m) * bessely((~!nu), (~!a) * (~x))) => :(
60+
!contains_var((~a), (~m), (~nu), (~x)) &&
61+
igt(simplify((~m) - (~nu) - 1), 0) &&
62+
ext_iseven(simplify((~m) - (~nu) - 1)) &&
63+
!eq((~a), 0) ?
64+
(~x)^(~m) * SpecialFunctions.bessely((~nu) + 1, (~a) * (~x)) (~a) -
65+
simplify((~m) - (~nu) - 1) (~a) * ((~x)^((~m) - 1) * SpecialFunctions.bessely((~nu) + 1, (~a) * (~x)), (~x)) : nothing
66+
),
67+
)
68+
69+
(
70+
"8_1_7",
71+
:((~x)^(~!m) * besseli((~!nu), (~!a) * (~x))) => :(
72+
!contains_var((~a), (~m), (~nu), (~x)) &&
73+
igt(simplify((~m) - (~nu) - 1), 0) &&
74+
ext_iseven(simplify((~m) - (~nu) - 1)) &&
75+
!eq((~a), 0) ?
76+
(~x)^(~m) * SpecialFunctions.besseli((~nu) + 1, (~a) * (~x)) (~a) -
77+
simplify((~m) - (~nu) - 1) (~a) * ((~x)^((~m) - 1) * SpecialFunctions.besseli((~nu) + 1, (~a) * (~x)), (~x)) : nothing
78+
),
79+
)
80+
81+
(
82+
"8_1_8",
83+
:((~x)^(~!m) * besselk((~!nu), (~!a) * (~x))) => :(
84+
!contains_var((~a), (~m), (~nu), (~x)) &&
85+
igt(simplify((~m) - (~nu) - 1), 0) &&
86+
ext_iseven(simplify((~m) - (~nu) - 1)) &&
87+
!eq((~a), 0) ?
88+
-(~x)^(~m) * SpecialFunctions.besselk((~nu) + 1, (~a) * (~x)) (~a) +
89+
simplify((~m) - (~nu) - 1) (~a) * ((~x)^((~m) - 1) * SpecialFunctions.besselk((~nu) + 1, (~a) * (~x)), (~x)) : nothing
90+
),
91+
)
92+
93+
]

src/methods/rule_based/rules2/9 Miscellaneous/0.1 Integrand simplification rules.jl

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,14 @@
11
file_rules = [
22
("0_1_0", :(+(~~a)) => :(sum(map(f -> (f,~x), arguments(~a)))) )
33

4+
("0_1_1",
5+
:(*(~~a)) => :(
6+
let
7+
terms = distribute_special_function_product(arguments(~a), ~x)
8+
terms === nothing ? nothing : sum(map(term -> (term, ~x), terms))
9+
end
10+
))
11+
412
("0_1_6",
513
:((~!u)*((~!a)*(~v) + (~!b)*(~v) + (~!w))^(~!p)) => :(
614
!contains_var((~a), (~b), (~x)) &&
@@ -172,4 +180,4 @@ end
172180
ext_isinteger((~p)) ?
173181
1(~c)^(~p)*((~u)*((~b)2 + (~c)*(~x)^(~n))^(2*(~p)), (~x)) : nothing))
174182

175-
]
183+
]

src/methods/rule_based/rules_utility_functions.jl

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -478,6 +478,59 @@ function dist(exp1, exp2, x)
478478
end
479479
end
480480

481+
const SPECIAL_FUNCTION_OPERATIONS = (
482+
SpecialFunctions.expinti,
483+
SpecialFunctions.expint,
484+
SpecialFunctions.gamma,
485+
SpecialFunctions.sinint,
486+
SpecialFunctions.cosint,
487+
SpecialFunctions.loggamma,
488+
SpecialFunctions.erfi,
489+
SpecialFunctions.erf,
490+
SpecialFunctions.besselj,
491+
SpecialFunctions.bessely,
492+
SpecialFunctions.besseli,
493+
SpecialFunctions.besselk,
494+
SpecialFunctions.hankelh1,
495+
SpecialFunctions.hankelh2,
496+
SpecialFunctions.airyai,
497+
SpecialFunctions.airybi,
498+
SpecialFunctions.airyaiprime,
499+
SpecialFunctions.airybiprime,
500+
Elliptic.F,
501+
Elliptic.E,
502+
Elliptic.Pi,
503+
hypergeometric2f1,
504+
hypergeometricpFq,
505+
PolyLog.reli,
506+
FresnelIntegrals.fresnelc,
507+
FresnelIntegrals.fresnels,
508+
sinhintegral,
509+
coshintegral,
510+
)
511+
512+
function special_function_operation(op)
513+
return any(special_op -> op === special_op, SPECIAL_FUNCTION_OPERATIONS)
514+
end
515+
516+
function contains_special_function(u)
517+
u = Symbolics.unwrap(u)
518+
!iscall(u) && return false
519+
special_function_operation(operation(u)) && return true
520+
return any(contains_special_function(arg) for arg in arguments(u))
521+
end
522+
523+
function distribute_special_function_product(factors, x)
524+
sum_index = findfirst(factor -> issum(factor) && contains_var(factor, x), factors)
525+
sum_index === nothing && return nothing
526+
any(contains_special_function, factors) || return nothing
527+
528+
sum_factor = Symbolics.unwrap(factors[sum_index])
529+
return map(arguments(sum_factor)) do term
530+
prod(i == sum_index ? term : factor for (i, factor) in pairs(factors))
531+
end
532+
end
533+
481534
# linear(a+3x,x) true
482535
# linear((x+1)^2 - x^2 - 1,x) true
483536
function linear(args...)
@@ -900,4 +953,4 @@ function nonfree_addends(u, x)
900953
return contains_var(u, x) ? 1 : u
901954
end
902955

903-
# TODO are all this unwrap needed?
956+
# TODO are all this unwrap needed?

src/methods/rule_based/translator_of_rules.jl

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -280,6 +280,16 @@ function translate_integrand(integrand)
280280
("ArcTanh", "atanh"),
281281
("ArcCoth", "acoth"),
282282

283+
("BesselJ", "besselj", 2),
284+
("BesselY", "bessely", 2),
285+
("BesselI", "besseli", 2),
286+
("BesselK", "besselk", 2),
287+
("HankelH1", "hankelh1", 2),
288+
("HankelH2", "hankelh2", 2),
289+
("AiryAiPrime", "airyaiprime", 1),
290+
("AiryBiPrime", "airybiprime", 1),
291+
("AiryAi", "airyai", 1),
292+
("AiryBi", "airybi", 1),
283293
("PolyLog", "PolyLog.reli", 2),
284294
("Gamma", "SymbolicUtils.gamma"),
285295
]
@@ -354,6 +364,16 @@ function result_substitutions(result, vardefs)
354364
("Erf", "SymbolicUtils.erf"),
355365
("SinIntegral", "SymbolicUtils.sinint"),
356366
("CosIntegral", "SymbolicUtils.cosint"),
367+
("BesselJ", "SpecialFunctions.besselj", 2),
368+
("BesselY", "SpecialFunctions.bessely", 2),
369+
("BesselI", "SpecialFunctions.besseli", 2),
370+
("BesselK", "SpecialFunctions.besselk", 2),
371+
("HankelH1", "SpecialFunctions.hankelh1", 2),
372+
("HankelH2", "SpecialFunctions.hankelh2", 2),
373+
("AiryAiPrime", "SpecialFunctions.airyaiprime", 1),
374+
("AiryBiPrime", "SpecialFunctions.airybiprime", 1),
375+
("AiryAi", "SpecialFunctions.airyai", 1),
376+
("AiryBi", "SpecialFunctions.airybi", 1),
357377
# taken from other julia packages
358378
("EllipticE", "elliptic_e", (1,2)),
359379
("EllipticF", "elliptic_f", 2),
@@ -655,4 +675,4 @@ try
655675
catch e
656676
println("Error during translation: $e")
657677
exit(1)
658-
end
678+
end

test/methods/rule_based/test_rule2.jl

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using Test
2+
import SymbolicIntegration
3+
using SpecialFunctions
24
using Symbolics
35
using SymbolicIntegration: eq
46

@@ -53,6 +55,19 @@ end
5355
rs = :(exp(~x)) => :(~x)
5456
@test eq(SymbolicIntegration.rule2(rs, exp(x+1)), x+1)
5557
@test eq(SymbolicIntegration.rule2(rs, ℯ^x), x)
58+
59+
rbj = :(besselj(~nu, ~z)) => :(~nu, ~z)
60+
besselj_match = SymbolicUtils.arguments(SymbolicIntegration.rule2(rbj, SpecialFunctions.besselj(0, x)))
61+
@test SymbolicUtils.unwrap_const(besselj_match[1]) == 0
62+
@test isequal(besselj_match[2], x)
63+
64+
rbi = :(besseli(~nu, ~z)) => :(~nu, ~z)
65+
besseli_match = SymbolicUtils.arguments(SymbolicIntegration.rule2(rbi, SpecialFunctions.besseli(1, x)))
66+
@test SymbolicUtils.unwrap_const(besseli_match[1]) == 1
67+
@test isequal(besseli_match[2], x)
68+
69+
rai = :(airyai(~z)) => :(~z)
70+
@test eq(SymbolicIntegration.rule2(rai, SpecialFunctions.airyai(x)), x)
5671
end
5772

5873
@testset "Segment" begin
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using Test
2+
using SymbolicIntegration
3+
using SpecialFunctions
4+
using Symbolics
5+
6+
@testset "RuleBased special-function integrals" begin
7+
@variables x a
8+
method = RuleBasedMethod()
9+
10+
@test isequal(
11+
integrate(x * SpecialFunctions.besselj(0, a * x), x, method),
12+
x * SpecialFunctions.besselj(1, a * x) / a,
13+
)
14+
@test isequal(
15+
integrate(x^2 * SpecialFunctions.besselj(1, a * x), x, method),
16+
x^2 * SpecialFunctions.besselj(2, a * x) / a,
17+
)
18+
@test isequal(
19+
integrate(x * SpecialFunctions.bessely(0, a * x), x, method),
20+
x * SpecialFunctions.bessely(1, a * x) / a,
21+
)
22+
@test isequal(
23+
integrate(x * SpecialFunctions.besseli(0, a * x), x, method),
24+
x * SpecialFunctions.besseli(1, a * x) / a,
25+
)
26+
@test isequal(
27+
integrate(x * SpecialFunctions.besselk(0, a * x), x, method),
28+
-x * SpecialFunctions.besselk(1, a * x) / a,
29+
)
30+
31+
@test isequal(
32+
integrate(x^3 * SpecialFunctions.besselj(0, a * x), x, method),
33+
x^3 * SpecialFunctions.besselj(1, a * x) / a -
34+
2 * x^2 * SpecialFunctions.besselj(2, a * x) / a^2,
35+
)
36+
@test isequal(
37+
integrate((x + x^3) * SpecialFunctions.besselj(0, a * x), x, method),
38+
x * SpecialFunctions.besselj(1, a * x) / a +
39+
x^3 * SpecialFunctions.besselj(1, a * x) / a -
40+
2 * x^2 * SpecialFunctions.besselj(2, a * x) / a^2,
41+
)
42+
@test isequal(
43+
integrate(x * (1 + x^2) * SpecialFunctions.besselj(0, a * x), x, method),
44+
x * SpecialFunctions.besselj(1, a * x) / a +
45+
x^3 * SpecialFunctions.besselj(1, a * x) / a -
46+
2 * x^2 * SpecialFunctions.besselj(2, a * x) / a^2,
47+
)
48+
@test isequal(
49+
integrate((1 + x) * SpecialFunctions.airyai(x), x, method),
50+
SymbolicIntegration.(SpecialFunctions.airyai(x), x) +
51+
SymbolicIntegration.(x * SpecialFunctions.airyai(x), x),
52+
)
53+
@test isequal(
54+
integrate(x^3 * SpecialFunctions.besseli(0, a * x), x, method),
55+
x^3 * SpecialFunctions.besseli(1, a * x) / a -
56+
2 * x^2 * SpecialFunctions.besseli(2, a * x) / a^2,
57+
)
58+
@test isequal(
59+
integrate(x^3 * SpecialFunctions.besselk(0, a * x), x, method),
60+
-x^3 * SpecialFunctions.besselk(1, a * x) / a -
61+
2 * x^2 * SpecialFunctions.besselk(2, a * x) / a^2,
62+
)
63+
end

test/methods/rule_based/translator_of_testset.jl

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,16 @@ function translate_mathematica_to_julia(expr::String)
1616
("Erf", "SymbolicUtils.erf"),
1717
("SinIntegral", "SymbolicUtils.sinint"),
1818
("CosIntegral", "SymbolicUtils.cosint"),
19+
("BesselJ", "SymbolicIntegration.SpecialFunctions.besselj", 2),
20+
("BesselY", "SymbolicIntegration.SpecialFunctions.bessely", 2),
21+
("BesselI", "SymbolicIntegration.SpecialFunctions.besseli", 2),
22+
("BesselK", "SymbolicIntegration.SpecialFunctions.besselk", 2),
23+
("HankelH1", "SymbolicIntegration.SpecialFunctions.hankelh1", 2),
24+
("HankelH2", "SymbolicIntegration.SpecialFunctions.hankelh2", 2),
25+
("AiryAiPrime", "SymbolicIntegration.SpecialFunctions.airyaiprime", 1),
26+
("AiryBiPrime", "SymbolicIntegration.SpecialFunctions.airybiprime", 1),
27+
("AiryAi", "SymbolicIntegration.SpecialFunctions.airyai", 1),
28+
("AiryBi", "SymbolicIntegration.SpecialFunctions.airybi", 1),
1929
# taken from other julia packages
2030
("EllipticE", "SymbolicIntegration.elliptic_e", (1,2)),
2131
("EllipticF", "SymbolicIntegration.elliptic_f", 2),
@@ -180,4 +190,3 @@ catch e
180190
println("Error during translation: $e")
181191
exit(1)
182192
end
183-

0 commit comments

Comments
 (0)