-
-
Notifications
You must be signed in to change notification settings - Fork 30
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
add Trapezoidal rule #173
add Trapezoidal rule #173
Changes from 6 commits
32e438e
881804b
afc0f12
ade9f6b
7265f24
c4a9208
efaf2e1
913f7f7
aa677c4
e7aefc9
608af76
2eb0090
346c0ac
9ab8ab2
6ba2e3c
813a460
7c3d710
65cf88d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -147,5 +147,107 @@ | |||||
SciMLBase.build_solution(prob, alg, val, err, chi = chi, retcode = ReturnCode.Success) | ||||||
end | ||||||
|
||||||
export QuadGKJL, HCubatureJL, VEGAS, GaussLegendre | ||||||
is_sampled_problem(prob::IntegralProblem) = prob.f isa AbstractArray | ||||||
import SciMLBase.IntegralProblem # this is type piracy, and belongs in SciMLBase | ||||||
function IntegralProblem(y::AbstractArray, lb, ub, args...; kwargs...) | ||||||
IntegralProblem{false}(y, lb, ub, args...; kwargs...) | ||||||
end | ||||||
function construct_grid(prob, alg, lb, ub, dim) | ||||||
x = alg.spec | ||||||
@assert length(ub) == length(lb) == 1 "Multidimensional integration is not supported with the Trapezoidal method" | ||||||
if x isa Integer | ||||||
grid = range(lb[1], ub[1], length=x) | ||||||
else | ||||||
grid = x | ||||||
@assert ndims(grid) == 1 "Multidimensional integration is not supported with the Trapezoidal method" | ||||||
end | ||||||
|
||||||
@assert lb[1] ≈ grid[begin] "Lower bound in `IntegralProblem` must coincide with that of the grid" | ||||||
@assert ub[1] ≈ grid[end] "Upper bound in `IntegralProblem` must coincide with that of the grid" | ||||||
if is_sampled_problem(prob) | ||||||
@assert size(prob.f, dim) == length(grid) "Integrand and grid must be of equal length along the integrated dimension" | ||||||
@assert axes(prob.f, dim) == axes(grid,1) "Grid and integrand array must use same indexing along integrated dimension" | ||||||
end | ||||||
return grid | ||||||
end | ||||||
|
||||||
@inline myselectdim(y::AbstractArray{T,dims}, d, i) where {T,dims} = selectdim(y, d, i) | ||||||
@inline myselectdim(y::AbstractArray{T,1}, _, i) where {T} = @inbounds y[i] | ||||||
|
||||||
@inline dimension(::Val{D}) where D = D | ||||||
function __solvebp_call(prob::IntegralProblem, alg::Trapezoidal{S, D}, sensealg, lb, ub, p; kwargs...) where {S,D} | ||||||
# since all AbstractRange types are equidistant by design, we can rely on that | ||||||
@assert prob.batch == 0 | ||||||
# using `Val`s for dimensionality is required to make `selectdim` not allocate | ||||||
dim = dimension(D) | ||||||
p = p | ||||||
if is_sampled_problem(prob) | ||||||
@assert alg.spec isa AbstractArray "For pre-sampled problems where the integrand is an array, the integration grid must also be specified by an array." | ||||||
end | ||||||
|
||||||
grid = construct_grid(prob, alg, lb, ub, dim) | ||||||
|
||||||
err = Inf64 | ||||||
if is_sampled_problem(prob) | ||||||
data = prob.f | ||||||
# inlining is required in order to not allocate | ||||||
@inline function integrand(i) | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
It should be an anonymous function if in an if statement |
||||||
# integrate along dimension `dim`, returning a n-1 dimensional array, or scalar if n=1 | ||||||
myselectdim(data, dim, i) | ||||||
end | ||||||
else | ||||||
if isinplace(prob) | ||||||
y = zeros(eltype(lb), prob.nout) | ||||||
integrand = i -> @inbounds (prob.f(y, grid[i], p); y) | ||||||
else | ||||||
integrand = i -> @inbounds prob.f(grid[i], p) | ||||||
end | ||||||
end | ||||||
|
||||||
firstidx, lastidx = firstindex(grid), lastindex(grid) | ||||||
|
||||||
out = integrand(firstidx) | ||||||
|
||||||
if isbits(out) | ||||||
# fast path for equidistant grids | ||||||
if grid isa AbstractRange | ||||||
dx = grid[begin+1] - grid[begin] | ||||||
out /= 2 | ||||||
for i in (firstidx+1):(lastidx-1) | ||||||
out += integrand(i) | ||||||
end | ||||||
out += integrand(lastidx)/2 | ||||||
out *= dx | ||||||
# irregular grids: | ||||||
else | ||||||
out *= (grid[firstidx + 1] - grid[firstidx]) | ||||||
for i in (firstidx+1):(lastidx-1) | ||||||
@inbounds out += integrand(i) * (grid[i + 1] - grid[i-1]) | ||||||
end | ||||||
out += integrand(lastidx) * (grid[lastidx] - grid[lastidx-1]) | ||||||
out /= 2 | ||||||
end | ||||||
else # same, but inplace, broadcasted | ||||||
out = copy(out) # to prevent aliasing | ||||||
if grid isa AbstractRange | ||||||
dx = grid[begin+1] - grid[begin] | ||||||
out ./= 2 | ||||||
for i in (firstidx+1):(lastidx-1) | ||||||
out .+= integrand(i) | ||||||
end | ||||||
out .+= integrand(lastidx) ./ 2 | ||||||
out .*= dx | ||||||
else | ||||||
out .*= (grid[firstidx + 1] - grid[firstidx]) | ||||||
for i in (firstidx+1):(lastidx-1) | ||||||
@inbounds out .+= integrand(i) .* (grid[i + 1] - grid[i-1]) | ||||||
end | ||||||
out .+= integrand(lastidx) .* (grid[lastidx] - grid[lastidx-1]) | ||||||
out ./= 2 | ||||||
end | ||||||
end | ||||||
return SciMLBase.build_solution(prob, alg, out, err, retcode = ReturnCode.Success) | ||||||
end | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This implementation of Trapezoid should be in a separate file trapezoid.jl that implements the method, and is then |
||||||
|
||||||
export QuadGKJL, HCubatureJL, VEGAS, GaussLegendre, Trapezoidal | ||||||
end # module |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
using Integrals, Test | ||
@testset "Sampled Integration" begin | ||
lb = 0.0 | ||
ub = 1.0 | ||
npoints = 1000 | ||
for method = [Trapezoidal] # Simpson's later | ||
nouts = [1,2,1,2] | ||
for (i,f) = enumerate([(x,p) -> x^5, (x,p) -> [x^5, x^5], (out, x,p) -> (out[1] = x^5; out), (out, x, p) -> (out[1] = x^5; out[2] = x^5; out)]) | ||
|
||
exact = 1/6 | ||
prob = IntegralProblem(f, lb, ub, nout=nouts[i]) | ||
|
||
# AbstractRange | ||
error1 = solve(prob, method(npoints)).u .- exact | ||
@test all(error1 .< 10^-4) | ||
|
||
# AbstractVector equidistant | ||
error2 = solve(prob, method(collect(range(lb, ub, length=npoints)))).u .- exact | ||
@test all(error2 .< 10^-4) | ||
|
||
# AbstractVector irregular | ||
grid = rand(npoints) | ||
grid = [lb; sort(grid); ub] | ||
error3 = solve(prob, method(grid)).u .- exact | ||
@test all(error3 .< 10^-4) | ||
|
||
|
||
end | ||
exact = 1/6 | ||
|
||
grid = rand(npoints) | ||
grid = [lb; sort(grid); ub] | ||
# single dimensional y | ||
y = grid .^ 5 | ||
prob = IntegralProblem(y, lb, ub) | ||
error4 = solve(prob, method(grid, dim=1)).u .- exact | ||
@test all(error4 .< 10^-4) | ||
|
||
# along dim=2 | ||
y = ([grid grid]') .^ 5 | ||
prob = IntegralProblem(y, lb, ub) | ||
error5 = solve(prob, method(grid, dim=2)).u .- exact | ||
@test all(error5 .< 10^-4) | ||
end | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah move this over to SciMLBase