|
| 1 | +struct ComplexPlane{T,R<:AbstractRange{T}} <: AbstractMatrix{Complex{T}} |
| 2 | + re::R |
| 3 | + im::R |
| 4 | +end |
| 5 | + |
| 6 | +Base.length(p::ComplexPlane) = length(p.re)*length(p.im) |
| 7 | +Base.size(p::ComplexPlane) = (length(p.im), length(p.re)) |
| 8 | +Base.eltype(p::ComplexPlane) = complex(eltype(p.re)) |
| 9 | +Base.getindex(p::ComplexPlane, i::CartesianIndex) = p.re[i[2]] + im*p.im[i[1]] |
| 10 | + |
| 11 | +function primitive_integral(f, a, b, c, w) |
| 12 | + μ = (a+b)/2 |
| 13 | + δ = (b-a)/2 |
| 14 | + v = zero(f(a)) |
| 15 | + for (c,w) in zip(c,w) |
| 16 | + v += w*f(δ*c + μ) |
| 17 | + end |
| 18 | + δ*v |
| 19 | +end |
| 20 | + |
| 21 | +interval(x::AbstractRange, r::UnitRange) = x[r[1]]..x[r[end]] |
| 22 | + |
| 23 | +function split_range(r::UnitRange) |
| 24 | + n = length(r) |
| 25 | + n < 1 && error("Too small range") |
| 26 | + n == 1 && return (r, r[1]:(r[1]-1)) |
| 27 | + |
| 28 | + s = n ÷ 2 |
| 29 | + r[1]:r[s], r[s+1]:r[end] |
| 30 | +end |
| 31 | + |
| 32 | +function find_closest(x::AbstractRange, x₀::Real) |
| 33 | + cur = firstindex(x):lastindex(x) |
| 34 | + i = 0 |
| 35 | + while length(cur) > 1 && i < ceil(Int, log2(length(x))) |
| 36 | + left,right = split_range(cur) |
| 37 | + if x₀ ∈ interval(x, left) |
| 38 | + cur = left |
| 39 | + continue |
| 40 | + elseif x₀ ∈ interval(x, right) |
| 41 | + cur = right |
| 42 | + continue |
| 43 | + end |
| 44 | + x₀ < x[left[1]] && return left[1] |
| 45 | + x₀ > x[right[end]] && return right[end] |
| 46 | + return norm(x[left[end]]-x₀) < norm(x[right[1]]-x₀) ? left[end] : right[1] |
| 47 | + i += 1 |
| 48 | + end |
| 49 | + cur[1] |
| 50 | +end |
| 51 | + |
| 52 | +function find_closest(x::ComplexPlane, x₀::Number) |
| 53 | + j = find_closest(x.re, real(x₀)) |
| 54 | + i = find_closest(x.im, imag(x₀)) |
| 55 | + CartesianIndex((i,j)) |
| 56 | +end |
| 57 | + |
| 58 | +struct AutomaticIntegration{Func, Grid, Xt, GridIntegrals, |
| 59 | + Roots, Weights} |
| 60 | + f::Func |
| 61 | + x::Grid |
| 62 | + x₀::Xt |
| 63 | + ∫f::GridIntegrals |
| 64 | + c::Roots |
| 65 | + w::Weights |
| 66 | +end |
| 67 | + |
| 68 | +function integrate!(ai::AutomaticIntegration{<:Any,<:AbstractRange}, i) |
| 69 | + a,b = firstindex(ai.x),lastindex(ai.x) |
| 70 | + for j = i+1:b |
| 71 | + ai.∫f[j] = ai(ai.x[j],i=j-1) |
| 72 | + end |
| 73 | + for j = i-1:-1:a |
| 74 | + ai.∫f[j] = ai(ai.x[j],i=j+1) |
| 75 | + end |
| 76 | +end |
| 77 | + |
| 78 | +function integrate!(ai::AutomaticIntegration{<:Any,<:ComplexPlane}, i) |
| 79 | + s = size(ai.x) |
| 80 | + ci = CartesianIndices(s) |
| 81 | + |
| 82 | + north(I::CartesianIndex) = CartesianIndex(I[1]-1,I[2]) |
| 83 | + south(I::CartesianIndex) = CartesianIndex(I[1]+1,I[2]) |
| 84 | + west(I::CartesianIndex) = CartesianIndex(I[1],I[2]-1) |
| 85 | + east(I::CartesianIndex) = CartesianIndex(I[1],I[2]+1) |
| 86 | + |
| 87 | + # First, we integrate parallel to the real axis, starting from |
| 88 | + # index i, then we integrate parallel to the imaginary axis, |
| 89 | + # starting from the real line-parallel we just created. |
| 90 | + for (r,dir) in [(reverse(ci[i[1],1:i[2]-1]), east), |
| 91 | + (ci[i[1],i[2]+1:end], west), |
| 92 | + (ci[i[1]+1:end,:], north), |
| 93 | + (ci[i[1]-1:-1:1,:], south)] |
| 94 | + for I in r |
| 95 | + # @show I, dir(I) ai.∫f[I], ai.∫f[dir(I)] |
| 96 | + ai.∫f[I] = ai(ai.x[I],i=dir(I)) |
| 97 | + end |
| 98 | + end |
| 99 | +end |
| 100 | + |
| 101 | +function AutomaticIntegration(f, x, x₀; k=3, init=zero(f(x₀))) |
| 102 | + Xt = eltype(x) |
| 103 | + s = size(x) |
| 104 | + Ft = typeof(f(Xt(x₀))) |
| 105 | + ∫f = zeros(Ft, s) |
| 106 | + |
| 107 | + c,w = gausslegendre(k) |
| 108 | + ai = AutomaticIntegration(f, x, x₀, ∫f, c, w) |
| 109 | + |
| 110 | + # We start by integrating from the point on the grid closest to x₀ |
| 111 | + # to x₀, which is minus the integral value desired, since the |
| 112 | + # integral originates at x₀. |
| 113 | + i = find_closest(x, x₀) |
| 114 | + ∫f[i] = init - ai(x₀, i=i) |
| 115 | + integrate!(ai, i) |
| 116 | + |
| 117 | + ai |
| 118 | +end |
| 119 | + |
| 120 | +(ai::AutomaticIntegration)(x; i=find_closest(ai.x, x)) = |
| 121 | + ai.∫f[i] + primitive_integral(ai.f, ai.x[i], x, ai.c, ai.w) |
| 122 | + |
| 123 | +(ai::AutomaticIntegration)(a, b) = ai(b) - ai(a) |
0 commit comments