Skip to content

Commit 982c596

Browse files
bernatguillendextorious
authored andcommitted
Added Romberg's method (#14)
* Added Romberg's method, no support for SVector yet * Changed RombergEven so that it is compatible with static vectors * Changed README to add Romberg * Added warning in case final step reached without reaching accuracy, added test for warning
1 parent 190d82c commit 982c596

File tree

3 files changed

+58
-7
lines changed

3 files changed

+58
-7
lines changed

README.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66

77
[![codecov.io](http://codecov.io/github/dextorious/NumericalIntegration.jl/coverage.svg?branch=master)](http://codecov.io/github/dextorious/NumericalIntegration.jl?branch=master)
88

9-
This is a simple package to provide functionality for numerically integrating presampled data (meaning you can't choose arbitrary nodes). If you have the ability to evaluate your integrand at arbitrary points, please consider using better tools for the job (such as the excellent [FastGaussQuadrature.jl](https://github.com/ajt60gaibb/FastGaussQuadrature.jl)).
9+
This is a simple package to provide functionality for numerically integrating presampled data (meaning you can't choose arbitrary nodes). If you have the ability to evaluate your integrand at arbitrary points, please consider using better tools for the job (such as the excellent [FastGaussQuadrature.jl](https://github.com/ajt60gaibb/FastGaussQuadrature.jl)).
1010

1111
Do note that while the code is trivial, it has not been extensively tested and does not focus on numerical precision. Issues, suggestions and pull requests are welcome.
1212

@@ -32,5 +32,6 @@ The currently available methods are:
3232
- TrapezoidalEvenFast
3333
- SimpsonEven
3434
- SimpsonEvenFast
35+
- RombergEven
3536

36-
All methods containing "Even" in the name assume evenly spaced data. All methods containing "Fast" omit basic correctness checks and focus on performance. Consequently, the fast methods will segfault or produce incorrect results if you supply incorrect data (vectors of different lengths, etc.).
37+
All methods containing "Even" in the name assume evenly spaced data. All methods containing "Fast" omit basic correctness checks and focus on performance. Consequently, the fast methods will segfault or produce incorrect results if you supply incorrect data (vectors of different lengths, etc.). RombergEven needs a power of 2 + 1 points (so 9, 17, 33, 65, 129, 257, 513, 1025...) evenly spaced for it to work. Useful when control over accuracy is needed.

src/NumericalIntegration.jl

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,12 @@
11
module NumericalIntegration
22

3+
using LinearAlgebra
4+
using Logging
5+
36
export integrate
47
export Trapezoidal, TrapezoidalEven, TrapezoidalFast, TrapezoidalEvenFast
58
export SimpsonEven, SimpsonEvenFast
9+
export RombergEven
610
export IntegrationMethod
711

812
abstract type IntegrationMethod end
@@ -13,6 +17,10 @@ struct TrapezoidalFast <: IntegrationMethod end
1317
struct TrapezoidalEvenFast <: IntegrationMethod end
1418
struct SimpsonEven <: IntegrationMethod end # https://en.wikipedia.org/wiki/Simpson%27s_rule#Alternative_extended_Simpson.27s_rule
1519
struct SimpsonEvenFast <: IntegrationMethod end
20+
struct RombergEven{T<:AbstractFloat} <: IntegrationMethod
21+
acc::T
22+
end # https://en.wikipedia.org/wiki/Romberg%27s_method
23+
RombergEven() = RombergEven(1e-12)
1624

1725
const HALF = 1//2
1826

@@ -71,6 +79,40 @@ function integrate(x::AbstractVector, y::AbstractVector, ::SimpsonEvenFast)
7179
@inbounds return (x[2] - x[1]) * retval
7280
end
7381

82+
function integrate(x::AbstractVector, y::AbstractVector, m::RombergEven)
83+
@assert length(x) == length(y) "x and y vectors must be of the same length!"
84+
@assert ((length(x) - 1) & (length(x) - 2)) == 0 "Need length of vector to be 2^n + 1"
85+
maxsteps::Integer = Int(log2(length(x)-1))
86+
rombaux = zeros(eltype(y), maxsteps, 2)
87+
prevrow = 1
88+
currrow = 2
89+
@inbounds h = x[end] - x[1]
90+
@inbounds rombaux[prevrow, 1] = (y[1] + y[end])*h*HALF
91+
@inbounds for i in 1 : (maxsteps-1)
92+
h *= HALF
93+
npoints = 1 << (i-1)
94+
jumpsize = div(length(x)-1, 2*npoints)
95+
c = 0.0
96+
for j in 1 : npoints
97+
c += y[1 + (2*j-1)*jumpsize]
98+
end
99+
rombaux[1, currrow] = h*c + HALF*rombaux[1, prevrow]
100+
for j in 2 : (i+1)
101+
n_k = 4^(j-1)
102+
rombaux[j, currrow] = (n_k*rombaux[j-1, currrow] - rombaux[j-1, prevrow])/(n_k - 1)
103+
end
104+
105+
if i > maxsteps//3 && norm(rombaux[i, prevrow] - rombaux[i+1, currrow], Inf) < m.acc
106+
return rombaux[i+1, currrow]
107+
end
108+
109+
prevrow, currrow = currrow, prevrow
110+
end
111+
finalerr = norm(rombaux[maxsteps-1, prevrow] - rombaux[maxsteps, currrow], Inf)
112+
@warn "RombergEven :: final step reached, but accuracy not: $finalerr > $(m.acc)"
113+
@inbounds return rombaux[maxsteps, prevrow]
114+
end
115+
74116
integrate(x::AbstractVector, y::AbstractVector) = integrate(x, y, TrapezoidalFast())
75117

76118
end

test/runtests.jl

Lines changed: 13 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ using InteractiveUtils # for subtypes
44
using StaticArrays
55

66
@testset "compare with analytic result" begin
7-
x = collect(-π : π/1000 : π)
7+
x = collect(-π : 2*π/2048 : π)
88
y = sin.(x)
9-
p = collect(0 : π/1000 : π)
9+
p = collect(0 : π/1024 : π)
1010
q = sin.(p)
1111
for M in subtypes(IntegrationMethod)
1212
for T in [Float32, Float64, BigFloat]
@@ -23,9 +23,9 @@ using StaticArrays
2323
end
2424

2525
@testset "SVector" begin
26-
xs = range(0, stop=1, length=10)
27-
ys1 = randn(10)
28-
ys2 = randn(10)
26+
xs = range(0, stop=1, length=9)
27+
ys1 = randn(9)
28+
ys2 = randn(9)
2929
ys = map(SVector, ys1, ys2)
3030
for M in subtypes(IntegrationMethod)
3131
m = M()
@@ -35,3 +35,11 @@ end
3535
@test res @SVector [res1, res2]
3636
end
3737
end
38+
39+
@testset "Raising Warnings" begin
40+
xs = collect(-1.0 : 0.5 : 1.0)
41+
ys = xs.^2
42+
m = RombergEven()
43+
expwarn = "RombergEven :: final step reached, but accuracy not: 1.0 > 1.0e-12"
44+
@test_logs (:warn, expwarn) integrate(xs, ys, m)
45+
end

0 commit comments

Comments
 (0)