Skip to content

Commit f1150df

Browse files
symbolic integration updated
1 parent 28428b9 commit f1150df

File tree

6 files changed

+492
-107
lines changed

6 files changed

+492
-107
lines changed

src/candidates.jl

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ function closure(eq, x; max_terms = 50)
6565
end
6666

6767
function candidate_pow_minus(p, k; abstol = 1e-8)
68-
if isnan(poly_deg(p))
68+
if !is_univar_poly(p)
6969
return [p^k, p^(k + 1), log(p)]
7070
end
7171

@@ -104,10 +104,14 @@ function candidate_pow_minus(p, k; abstol = 1e-8)
104104
end
105105

106106
function candidate_sqrt(p, k)
107-
x = var(p)
108-
109107
h = Any[p^k, p^(k + 1)]
110-
108+
109+
if !is_univar_poly(p)
110+
return h
111+
end
112+
113+
x = var(p)
114+
111115
if poly_deg(p) == 2
112116
r, s = find_roots(p, x)
113117
l = leading(p, x)

src/homotopy.jl

Lines changed: 10 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,14 @@ 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)
68+
6769

6870
Symbolics.derivative(::typeof(Ei), args::NTuple{1, Any}, ::Val{1}) = exp(args[1]) / args[1]
6971
Symbolics.derivative(::typeof(Si), args::NTuple{1, Any}, ::Val{1}) = sin(args[1]) / args[1]
7072
Symbolics.derivative(::typeof(Ci), args::NTuple{1, Any}, ::Val{1}) = cos(args[1]) / args[1]
7173
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)
7275

7376
@syms 𝑥 si(𝑥) ci(𝑥) ei(𝑥) li(𝑥)
7477

@@ -77,8 +80,12 @@ Symbolics.derivative(::typeof(Li), args::NTuple{1, Any}, ::Val{1}) = 1 / log(arg
7780
guard_zero(x) = isequal(x, 0) ? one(x) : x
7881

7982
function generate_homotopy(eq, x)
80-
eq = eq isa Num ? eq.val : eq
81-
x = x isa Num ? x.val : x
83+
eq = value(eq)
84+
x = value(x)
85+
86+
if is_add(eq)
87+
return unique(union([generate_homotopy(t,x) for t in args(eq)]...))
88+
end
8289

8390
p = transform(eq, x)
8491
q, sub, ks = rename_factors(p, (si => Si, ci => Ci, ei => Ei, li => Li))
@@ -107,7 +114,7 @@ partial_int_rules = [
107114
# trigonometric functions
108115
@rule 𝛷(sin(~x)) => (cos(~x) + si(~x), ~x)
109116
@rule 𝛷(cos(~x)) => (sin(~x) + ci(~x), ~x)
110-
@rule 𝛷(tan(~x)) => (log(cos(~x)), ~x)
117+
@rule 𝛷(tan(~x)) => (log(cos(~x)) + ~x*tan(~x), ~x)
111118
@rule 𝛷(csc(~x)) => (log(csc(~x) + cot(~x)) + log(sin(~x)), ~x)
112119
@rule 𝛷(sec(~x)) => (log(sec(~x) + tan(~x)) + log(cos(~x)), ~x)
113120
@rule 𝛷(cot(~x)) => (log(sin(~x)), ~x)

src/integral.jl

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ 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
32+
- `symbolic` (default: `false`): try symbolic integration first (will be forced if `eq` has constant parameters)
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)
@@ -47,7 +47,7 @@ Output:
4747
function integrate(eq, x = nothing; abstol = 1e-6, num_steps = 2, num_trials = 10,
4848
radius = 1.0,
4949
show_basis = false, opt = STLSQ(exp.(-10:1:0)), bypass = false,
50-
symbolic = true, max_basis = 100, verbose = false, complex_plane = true,
50+
symbolic = false, max_basis = 100, verbose = false, complex_plane = true,
5151
homotopy = true, use_optim = false)
5252
eq = expand(eq)
5353

@@ -165,6 +165,25 @@ function integrate_term(eq, x, l; kwargs...)
165165
result(l, "Successful", y)
166166
return y, 0, 0
167167
end
168+
169+
params = const_params(eq, x)
170+
171+
if !isempty(params) && !symbolic
172+
@warn("The input expression has constant parameters: [$(join(params, ", "))], forcing `symbolic = true`")
173+
symbolic = true
174+
end
175+
176+
if symbolic
177+
y = integrate_symbolic(eq, x; abstol, radius)
178+
if y == nothing
179+
if !isempty(params)
180+
@warn("Symbolic integration failed. Try changing constant parameters ([$(join(params, ", "))]) to numerical values.")
181+
return 0, eq, Inf
182+
end
183+
else
184+
return y, 0, 0
185+
end
186+
end
168187

169188
eq = cache(eq)
170189
basis1 = generate_basis(eq, x, true)
@@ -190,19 +209,7 @@ function integrate_term(eq, x, l; kwargs...)
190209
for i in 1:num_steps
191210
if length(basis1) > max_basis
192211
break
193-
end
194-
195-
if symbolic
196-
y, ϵ = try_symbolic(Float64, expr(eq), x, expr.(basis1), deriv!.(basis1, x);
197-
kwargs...)
198-
199-
if !isequal(y, 0) && accept_solution(eq, x, y, radius) < abstol
200-
result(l, "Successful symbolic", y)
201-
return y, 0, 0
202-
else
203-
inform(l, "Failed symbolic")
204-
end
205-
end
212+
end
206213

207214
for j in 1:num_trials
208215
basis = isodd(j) ? basis1 : basis2

src/sparse.jl

Lines changed: 60 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22
function solve_sparse(eq, x, basis, radius; kwargs...)
33
args = Dict(kwargs)
44
abstol, opt, complex_plane = args[:abstol], args[:opt], args[:complex_plane]
5-
5+
66
A, X = init_basis_matrix(eq, x, basis, radius, complex_plane; abstol)
77

88
# find a linearly independent subset of the basis
@@ -52,8 +52,14 @@ function init_basis_matrix(eq, x, basis, radius, complex_plane; abstol = 1e-6)
5252

5353
# A is an nxn matrix holding the values of the fragments at n random points
5454
A = zeros(Complex{Float64}, (n, n))
55-
X = zeros(Complex{Float64}, n)
56-
55+
X = zeros(Complex{Float64}, n)
56+
57+
S = subs_symbols(eq, x)
58+
if !isempty(S)
59+
eq = substitute(eq, S)
60+
basis = [substitute(y, S) for y in basis]
61+
end
62+
5763
eq_fun = fun!(eq, x)
5864
Δbasis_fun = deriv_fun!.(basis, x)
5965

@@ -162,3 +168,54 @@ function find_dense(A, basis; abstol = 1e-6)
162168
end
163169
return nothing, Inf
164170
end
171+
172+
173+
###############################################################################
174+
175+
176+
function hints(eq, x, basis; radius=5.0, abstol=1e-6, opt=STLSQ(exp.(-10:1:0)), complex_plane=true)
177+
A, X = init_basis_matrix(eq, x, basis, radius, complex_plane; abstol)
178+
179+
# find a linearly independent subset of the basis
180+
l = find_independent_subset(A; abstol)
181+
A, basis = A[l, l], basis[l]
182+
183+
n, m = size(A)
184+
185+
try
186+
b = ones((1, n))
187+
solver = SparseLinearSolver(opt,
188+
options = DataDrivenCommonOptions(verbose = false,
189+
maxiters = 1000))
190+
res, _... = solver(A', b)
191+
q = DataDrivenSparse.coef(first(res))
192+
err = abs(rms(A * q' .- 1))
193+
if err < abstol
194+
sel = abs.(q) .> abstol
195+
h = [basis[i] for i in 1:length(basis) if sel[i]]
196+
else
197+
h = []
198+
end
199+
return h, err
200+
catch e
201+
println("Error from hints", e)
202+
end
203+
204+
return 0, Inf
205+
end
206+
207+
208+
function best_hints(eq, x, basis; radius=5.0, abstol=1e-6, opt=STLSQ(exp.(-10:1:0)), complex_plane=true, num_trials=10)
209+
H = []
210+
L = Int[]
211+
212+
for _ in 1:num_trials
213+
h, err = hints(eq, x, basis; radius, abstol, opt, complex_plane)
214+
push!(H, h)
215+
push!(L, err < abstol ? length(h) : length(basis))
216+
end
217+
218+
_, idx = findmin(L)
219+
return H[idx]
220+
end
221+

0 commit comments

Comments
 (0)