Skip to content

Commit 1dd6117

Browse files
symbolic integration improved
1 parent f1150df commit 1dd6117

File tree

11 files changed

+375
-411
lines changed

11 files changed

+375
-411
lines changed

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 & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -64,76 +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 !is_univar_poly(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-
h = Any[p^k, p^(k + 1)]
108-
109-
if !is_univar_poly(p)
110-
return h
111-
end
112-
113-
x = var(p)
114-
115-
if poly_deg(p) == 2
116-
r, s = find_roots(p, x)
117-
l = leading(p, x)
118-
if length(r) == 2
119-
if sum(r) 0
120-
r₁ = abs(r[1])
121-
if l > 0
122-
push!(h, acosh(x / r₁))
123-
else
124-
push!(h, asin(x / r₁))
125-
end
126-
end
127-
elseif real(s[1]) 0
128-
push!(h, asinh(x / imag.(s[1])))
129-
end
130-
end
131-
132-
Δ = expand_derivatives(Differential(x)(p))
133-
push!(h, log(0.5 * Δ + sqrt(p)))
134-
return h
135-
end
136-
13767
###############################################################################
13868

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

src/homotopy.jl

Lines changed: 96 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -64,16 +64,16 @@ Symbolics.@register_symbolic Ei(z)
6464
Symbolics.@register_symbolic Si(z)
6565
Symbolics.@register_symbolic Ci(z)
6666
Symbolics.@register_symbolic Li(z)
67-
Symbolics.@register_symbolic erfi(z)
67+
Symbolics.@register_symbolic Erfi(z)
6868

6969

7070
Symbolics.derivative(::typeof(Ei), args::NTuple{1, Any}, ::Val{1}) = exp(args[1]) / args[1]
7171
Symbolics.derivative(::typeof(Si), args::NTuple{1, Any}, ::Val{1}) = sin(args[1]) / args[1]
7272
Symbolics.derivative(::typeof(Ci), args::NTuple{1, Any}, ::Val{1}) = cos(args[1]) / args[1]
7373
Symbolics.derivative(::typeof(Li), args::NTuple{1, Any}, ::Val{1}) = 1 / log(args[1])
74-
Symbolics.derivative(::typeof(erfi), args::NTuple{1, Any}, ::Val{1}) = 2 / sqrt(2) * exp(args[1]^2)
74+
Symbolics.derivative(::typeof(Erfi), args::NTuple{1, Any}, ::Val{1}) = 2 / sqrt(2) * exp(args[1]^2)
7575

76-
@syms 𝑥 si(𝑥) ci(𝑥) ei(𝑥) li(𝑥)
76+
@syms 𝑥 si(𝑥) ci(𝑥) ei(𝑥) li(𝑥) erfi_(𝑥)
7777

7878
##############################################################################
7979

@@ -88,7 +88,7 @@ function generate_homotopy(eq, x)
8888
end
8989

9090
p = transform(eq, x)
91-
q, sub, ks = rename_factors(p, (si => Si, ci => Ci, ei => Ei, li => Li))
91+
q, sub, ks = rename_factors(p, (si => Si, ci => Ci, ei => Ei, li => Li, erfi_ => Erfi))
9292
S = 0
9393

9494
for i in 1:length(ks)
@@ -114,7 +114,7 @@ partial_int_rules = [
114114
# trigonometric functions
115115
@rule 𝛷(sin(~x)) => (cos(~x) + si(~x), ~x)
116116
@rule 𝛷(cos(~x)) => (sin(~x) + ci(~x), ~x)
117-
@rule 𝛷(tan(~x)) => (log(cos(~x)) + ~x*tan(~x), ~x)
117+
@rule 𝛷(tan(~x)) => (log(cos(~x)), ~x)
118118
@rule 𝛷(csc(~x)) => (log(csc(~x) + cot(~x)) + log(sin(~x)), ~x)
119119
@rule 𝛷(sec(~x)) => (log(sec(~x) + tan(~x)) + log(cos(~x)), ~x)
120120
@rule 𝛷(cot(~x)) => (log(sin(~x)), ~x)
@@ -154,37 +154,105 @@ partial_int_rules = [
154154
@rule 𝛷(asech(~x)) => (asech(~x), ~x)
155155
@rule 𝛷(acoth(~x)) => (~x * acot(~x) + log(~x + 1), ~x)
156156
# logarithmic and exponential functions
157-
@rule 𝛷(log(~x)) => (~x + ~x * log(~x) +
158-
sum(candidate_pow_minus(~x, -1); init = one(~x)),
159-
~x)
157+
@rule 𝛷(log(~x)) => (~x + ~x * log(~x) + sum(pow_minus_rule(~x, -1); init = one(~x)), ~x)
160158
@rule 𝛷(1 / log(~x)) => (log(log(~x)) + li(~x), ~x)
161-
@rule 𝛷(exp(~x)) => (exp(~x) + ei(~x), ~x)
159+
@rule 𝛷(exp(~x)) => (exp(~x) + ei(~x) + erfi_rule(~x), ~x)
162160
@rule 𝛷(^(exp(~x), ~k::is_neg)) => (^(exp(-~x), -~k), ~x)
163161
# square-root functions
164-
@rule 𝛷(^(~x, ~k::is_abs_half)) => (sum(candidate_sqrt(~x, ~k);
165-
init = one(~x)), ~x);
166-
@rule 𝛷(sqrt(~x)) => (sum(candidate_sqrt(~x, 0.5); init = one(~x)), ~x);
167-
@rule 𝛷(1 / sqrt(~x)) => (sum(candidate_sqrt(~x, -0.5);
168-
init = one(~x)), ~x);
162+
@rule 𝛷(^(~x, ~k::is_abs_half)) => (sum(sqrt_rule(~x, ~k); init = one(~x)), ~x);
163+
@rule 𝛷(sqrt(~x)) => (sum(sqrt_rule(~x, 0.5); init = one(~x)), ~x);
164+
@rule 𝛷(1 / sqrt(~x)) => (sum(sqrt_rule(~x, -0.5); init = one(~x)), ~x);
169165
# rational functions
170-
@rule 𝛷(1 / ^(~x::is_poly, ~k::is_pos_int)) => (sum(candidate_pow_minus(~x,
171-
-~k);
172-
init = one(~x)),
173-
~x)
174-
@rule 𝛷(1 / ~x::is_poly) => (sum(candidate_pow_minus(~x, -1);
175-
init = one(~x)), ~x);
166+
@rule 𝛷(1 / ^(~x::is_univar_poly, ~k::is_pos_int)) => (sum(pow_minus_rule(~x,-~k); init = one(~x)), ~x)
167+
@rule 𝛷(1 / ~x::is_univar_poly) => (sum(pow_minus_rule(~x, -1); init = one(~x)), ~x);
176168
@rule 𝛷(^(~x, -1)) => (log(~x), ~x)
177-
@rule 𝛷(^(~x, ~k::is_neg_int)) => (sum(^(~x, i) for i in (~k + 1):-1),
178-
~x)
169+
@rule 𝛷(^(~x, ~k::is_neg_int)) => (sum(^(~x, i) for i in (~k + 1):-1), ~x)
179170
@rule 𝛷(1 / ~x) => (log(~x), ~x)
180-
@rule 𝛷(^(~x, ~k::is_pos_int)) => (sum(^(~x, i + 1)
181-
for i in 1:(~k + 1)),
182-
~x)
171+
@rule 𝛷(^(~x, ~k::is_pos_int)) => (sum(^(~x, i + 1) for i in 1:(~k + 1)), ~x)
183172
@rule 𝛷(1) => (𝑥, 1)
184173
@rule 𝛷(~x) => ((~x + ^(~x, 2)), ~x)]
185174

186175
function apply_partial_int_rules(eq, x)
187-
y, dy = expand(Fixpoint(Prewalk(Chain(partial_int_rules))))(𝛷(value(eq)))
188-
D = Differential(x)
189-
return y, guard_zero(expand_derivatives(D(dy)))
176+
y, dy = Chain(partial_int_rules)(𝛷(value(eq)))
177+
return y, guard_zero(diff(dy, x))
190178
end
179+
180+
################################################################
181+
182+
function erfi_rule(eq)
183+
if is_univar_poly(eq)
184+
x = var(eq)
185+
return erfi_(x)
186+
end
187+
return 0
188+
end
189+
190+
function pow_minus_rule(p, k; abstol = 1e-8)
191+
if !is_univar_poly(p)
192+
return [p^k, p^(k + 1), log(p)]
193+
end
194+
195+
x = var(p)
196+
d = poly_deg(p)
197+
198+
for j in 1:10 # will try 10 times to find the roots
199+
r, s = find_roots(p, x)
200+
if length(r) + length(s) >= d
201+
break
202+
end
203+
end
204+
s = s[1:2:end]
205+
r = nice_parameter.(r)
206+
s = nice_parameter.(s)
207+
208+
# ∫ 1 / ((x-z₁)(x-z₂)) dx = ... + c₁ * log(x-z₁) + c₂ * log(x-z₂)
209+
q = Any[log(x - u) for u in r]
210+
for i in eachindex(s)
211+
β = s[i]
212+
if abs(imag(β)) > abstol
213+
push!(q, atan((x - real(β)) / imag(β)))
214+
push!(q, (1 + x) * log(x^2 - 2 * real(β) * x + abs2(β)))
215+
else
216+
push!(q, log(x - real(β)))
217+
end
218+
end
219+
q = unique(q)
220+
221+
if k -1
222+
return [[p^k]; q]
223+
else
224+
return [[p^k, p^(k + 1)]; q]
225+
end
226+
end
227+
228+
function sqrt_rule(p, k)
229+
h = Any[p^k, p^(k + 1)]
230+
231+
if !is_univar_poly(p)
232+
return h
233+
end
234+
235+
x = var(p)
236+
237+
if poly_deg(p) == 2
238+
r, s = find_roots(p, x)
239+
l = leading(p, x)
240+
if length(r) == 2
241+
if sum(r) 0
242+
r₁ = abs(r[1])
243+
if l > 0
244+
push!(h, acosh(x / r₁))
245+
else
246+
push!(h, asin(x / r₁))
247+
end
248+
end
249+
elseif real(s[1]) 0
250+
push!(h, asinh(x / imag.(s[1])))
251+
end
252+
end
253+
254+
Δ = expand_derivatives(Differential(x)(p))
255+
push!(h, log(0.5 * Δ + sqrt(p)))
256+
return h
257+
end
258+

0 commit comments

Comments
 (0)