-
-
Notifications
You must be signed in to change notification settings - Fork 30
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
add trapezoidal rule for sampled data, attempt 2
- Loading branch information
1 parent
913f7f7
commit aa677c4
Showing
8 changed files
with
167 additions
and
55 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
# Integrating pre-sampled data | ||
|
||
In some cases, instead of a function that acts as integrand, | ||
one only possesses a list of data points `y` at a set of sampling | ||
locations `x`, that must be integrated. This package contains functionality | ||
for doing that. | ||
|
||
## Example | ||
|
||
Say, by some means we have generated a dataset `x` and `y`: | ||
```example 1 | ||
using Integrals # hide | ||
f = x -> x^2 | ||
x = range(0, 1, length=20) | ||
y = f.(x) | ||
``` | ||
|
||
Now, we can integrate this data set as follows: | ||
|
||
```example 1 | ||
problem = SampledIntegralProblem(y, x) | ||
method = TrapezoidalRule() | ||
solve(problem, method) | ||
``` | ||
|
||
The exact aswer is of course \$ 1/3 \$. | ||
|
||
## Details | ||
|
||
### Non-equidistant grids | ||
|
||
If the sampling points `x` are provided as an `AbstractRange` | ||
(constructed with the `range` function for example), faster methods are used that take advantage of | ||
the fact that the points are equidistantly spaced. Otherwise, general methods are used for | ||
non-uniform grids. | ||
|
||
Example: | ||
|
||
```example 2 | ||
using Integrals # hide | ||
f = x -> x^7 | ||
x = [0.0; sort(rand(1000)); 1.0] | ||
y = f.(x) | ||
problem = SampledIntegralProblem(y, x) | ||
method = TrapezoidalRule() | ||
solve(problem, method) | ||
``` | ||
|
||
### Evaluating multiple integrals at once | ||
|
||
If the provided data set `y` is a multidimensional array, the integrals are evaluated across only one | ||
of its axes. For performance reasons, the last axis of the array `y` is chosen by default, but this can be modified with the `dim` | ||
keyword argument to the problem definition. | ||
|
||
```example 3 | ||
using Integrals # hide | ||
f1 = x -> x^2 | ||
f2 = x -> x^3 | ||
f3 = x -> x^4 | ||
x = range(0, 1, length=20) | ||
y = [f1.(x) f2.(x) f3.(x)] | ||
problem = SampledIntegralProblem(y, x; dim=1) | ||
method = TrapezoidalRule() | ||
solve(problem, method) | ||
``` | ||
|
||
### Supported methods | ||
|
||
Right now, only the `TrapezoidalRule` is supported, [see wikipedia](https://en.wikipedia.org/wiki/Trapezoidal_rule). | ||
|
||
```@docs | ||
TrapezoidalRule | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
abstract type AbstractWeights end | ||
|
||
# must have field `n` for length, and a field `h` for stepsize | ||
abstract type UniformWeights <: AbstractWeights end | ||
@inline Base.iterate(w::UniformWeights) = (0 == w.n) ? nothing : (w[1], 1) | ||
@inline Base.iterate(w::UniformWeights, i) = (i == w.n) ? nothing : (w[i+1], i+1) | ||
Base.length(w::UniformWeights) = w.n | ||
Base.eltype(w::UniformWeights) = typeof(w.h) | ||
Base.size(w::UniformWeights) = (length(w), ) | ||
|
||
# must contain field `x` which are the sampling points | ||
abstract type NonuniformWeights <: AbstractWeights end | ||
@inline Base.iterate(w::NonuniformWeights) = (0 == length(w.x)) ? nothing : (w[firstindex(w.x)], firstindex(w.x)) | ||
@inline Base.iterate(w::NonuniformWeights, i) = (i == lastindex(w.x)) ? nothing : (w[i+1], i+1) | ||
Base.length(w::NonuniformWeights) = length(w.x) | ||
Base.eltype(w::NonuniformWeights) = eltype(w.x) | ||
Base.size(w::NonuniformWeights) = (length(w), ) | ||
|
||
_eachslice(data::AbstractArray; dims=ndims(data)) = eachslice(data; dims=dims) | ||
_eachslice(data::AbstractArray{T, 1}; dims=ndims(data)) where T = data | ||
|
||
|
||
# these can be removed when the Val(dim) is removed from SciMLBase | ||
dimension(::Val{D}) where {D} = D | ||
dimension(D::Int) = D | ||
|
||
|
||
function evalrule(data::AbstractArray, weights, dim) | ||
f = _eachslice(data, dims=dim) | ||
firstidx, lastidx = firstindex(f), lastindex(f) | ||
out = f[firstidx]*weights[firstidx] | ||
if isbits(out) | ||
for i in firstidx+1:lastidx | ||
@inbounds out += f[i]*weights[i] | ||
end | ||
else | ||
for i in firstidx+1:lastidx | ||
@inbounds out .+= f[i] .* weights[i] | ||
end | ||
end | ||
return out | ||
|
||
end | ||
|
||
|
||
# can be reused for other sampled rules | ||
function __solvebp_call(prob::SampledIntegralProblem, alg::TrapezoidalRule; kwargs...) | ||
dim = dimension(prob.dim) | ||
err = nothing | ||
data = prob.y | ||
grid = prob.x | ||
weights = find_weights(grid, alg) | ||
I = evalrule(data, weights, dim) | ||
return SciMLBase.build_solution(prob, alg, I, err, retcode = ReturnCode.Success) | ||
end | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,55 +1,23 @@ | ||
function __solvebp_call(prob::SampledIntegralProblem, alg::TrapezoidalRule; kwargs...) | ||
dim = dimension(prob.dim) | ||
err = Inf64 | ||
data = prob.y | ||
grid = prob.x | ||
# inlining is required in order to not allocate | ||
integrand = @inline function (i) | ||
# integrate along dimension `dim`, returning a n-1 dimensional array, or scalar if n=1 | ||
_selectdim(data, dim, i) | ||
end | ||
struct TrapezoidalUniformWeights <: UniformWeights | ||
n::Int | ||
h::Float64 | ||
end | ||
|
||
@inline Base.getindex(w::TrapezoidalUniformWeights, i) = ifelse((i == 1) || (i == w.n), w.h*0.5 , w.h) | ||
|
||
firstidx, lastidx = firstindex(grid), lastindex(grid) | ||
|
||
out = integrand(firstidx) | ||
struct TrapezoidalNonuniformWeights{X<:AbstractArray} <: NonuniformWeights | ||
x::X | ||
end | ||
|
||
if isbits(out) | ||
# fast path for equidistant grids | ||
if grid isa AbstractRange | ||
dx = step(grid) | ||
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) | ||
@inline function Base.getindex(w::TrapezoidalNonuniformWeights, i) | ||
x = w.x | ||
(i == firstindex(x)) && return (x[i + 1] - x[i])*0.5 | ||
(i == lastindex(x)) && return (x[i] - x[i - 1])*0.5 | ||
return (x[i + 1] - x[i - 1])*0.5 | ||
end | ||
|
||
function find_weights(x::AbstractVector, ::TrapezoidalRule) | ||
x isa AbstractRange && return TrapezoidalUniformWeights(length(x), step(x)) | ||
return TrapezoidalNonuniformWeights(x) | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters