|
| 1 | +module MultivariateSingularIntegrals |
| 2 | +using LinearAlgebra, ClassicalOrthogonalPolynomials, SingularIntegrals, FillArrays |
| 3 | +import Base: size |
| 4 | + |
| 5 | +export logkernelsquare, logkernelsquare! |
| 6 | + |
| 7 | +""" |
| 8 | + L₀₀(z) = ∫_{-1}^1∫_{-1}^1 log(z-(s+im*t))dsdt |
| 9 | +""" |
| 10 | +L₀₀(z) = (1-z)*M0(z-1) + im*M1(z-1) + (1+z)*M0(z+1) - im*M1(z+1) - 4 |
| 11 | + |
| 12 | +#computes [a b; c d] \ [1,0] |
| 13 | +@inline function twobytwosolve_1(a, b, c, d) |
| 14 | + dt = inv(muladd(a,d,-b*c)) |
| 15 | + d*dt, -c*dt |
| 16 | +end |
| 17 | + |
| 18 | +#computes [a b; c d] \ [0, 1] |
| 19 | +@inline function twobytwosolve_2(a, b, c, d) |
| 20 | + dt = inv(muladd(a,d,-b*c)) |
| 21 | + -b*dt, a*dt |
| 22 | +end |
| 23 | + |
| 24 | + |
| 25 | + |
| 26 | + |
| 27 | +""" |
| 28 | + biforwardsub!(M, A, B, R1, R2) |
| 29 | +
|
| 30 | +finds `X` such that `A*X + im*X*B' = R1` and `B*X + im*X*A' = R2` |
| 31 | +""" |
| 32 | +function biforwardsub!(M, z, A, B, R1, R2) |
| 33 | + m,n = size(M) |
| 34 | + begin |
| 35 | + # fill out first row |
| 36 | + a,b = twobytwosolve_2(A[1,2], B[1,2], im*B[1,2], im*A[1,2]) |
| 37 | + M[1,2] = ((a+b)*z- (a+im*b)*(A[1,1]-im*B[1,1]))*M[1,1] - a*R1[1,1] - b*R2[1,1] |
| 38 | + for j = 2:n-1 |
| 39 | + a,b = twobytwosolve_2(A[1,2], B[1,2], im*B[j,j+1], im*A[j,j+1]) |
| 40 | + M[1,j+1] = ((a+b)*z - a*A[1,1] + b*B[1,1])*M[1,j] - im*(a*B[j,j-1] + b*A[j,j-1])*M[1,j-1] - a*R1[1,j] - b*R2[1,j] |
| 41 | + end |
| 42 | + |
| 43 | + # fill out first column |
| 44 | + a,b = twobytwosolve_1(A[1,2], B[1,2], im*B[1,2], im*A[1,2]) |
| 45 | + M[2,1] = ((a+b)*z- (a+im*b)*(A[1,1]-im*B[1,1]))*M[1,1] - a*R1[1,1] - b*R2[1,1] |
| 46 | + for k = 2:m-1 |
| 47 | + a,b = twobytwosolve_1(A[k,k+1], B[k,k+1], im*B[1,2], im*A[1,2]) |
| 48 | + M[k+1,1] = ((a+b)*z - im*(a*B[1,1] + b*A[1,1]))*M[k,1] - (a*A[k,k-1] + b*B[k,k-1])*M[k-1,1] - a*R1[k,1] - b*R2[k,1] |
| 49 | + end |
| 50 | + |
| 51 | + # fill out 2nd column from first |
| 52 | + for k = 2:m |
| 53 | + a,b = twobytwosolve_2(A[k,k+1], B[k,k+1], im*B[1,2], im*A[1,2]) |
| 54 | + M[k,2] = (a+b)*z*M[k,1] - (a*A[k,k-1] + b*B[k,k-1])*M[k-1,1] - (im*a*B[1,1] + im*b*A[1,1])*M[k,1] - a*R1[k,1] - b*R2[k,1] |
| 55 | + end |
| 56 | + |
| 57 | + # fill out rest, column-by-column |
| 58 | + for k = 2:m, j = 2:n-1 |
| 59 | + a,b = twobytwosolve_2(A[k,k+1], B[k,k+1], im*B[j,j+1], im*A[j,j+1]) |
| 60 | + M[k,j+1] = (a+b)*z*M[k,j] - (a*A[k,k-1] + b*B[k,k-1])*M[k-1,j] - (im*a*B[j,j-1] + im*b*A[j,j-1])*M[k,j-1]- a*R1[k,j] - b*R2[k,j] |
| 61 | + end |
| 62 | + end |
| 63 | + |
| 64 | + M |
| 65 | +end |
| 66 | + |
| 67 | +function m_const_vec(n, z) |
| 68 | + (x,y) = reim(z) |
| 69 | + T = float(real(typeof(z))) |
| 70 | + if x > 0 || y ≥ 1 || (x == 0 && !signbit(x) && y < 0) # need to treat im*y+0.0 differently |
| 71 | + [im*convert(T,π); Zeros{T}(n-1)] |
| 72 | + elseif y ≤ -1 |
| 73 | + [-3im*convert(T,π); Zeros{T}(n-1)] |
| 74 | + else # x ≤ 0 && -1 ≤ y ≤ 1 |
| 75 | + r = Weighted(Jacobi{T}(1,1))[y,1:n-1] |
| 76 | + [((1 + y) - 3*(1-y))/2*im*convert(T,π); -im .* convert(T,π) .* r ./ (1:(n-1))] |
| 77 | + end |
| 78 | +end |
| 79 | + |
| 80 | +function imlogkernel_vec(n, z) |
| 81 | + T = float(real(typeof(z))) |
| 82 | + transpose(complexlogkernel(Legendre{T}(), -im*float(z)))[1:n] + m_const_vec(n, float(z)) |
| 83 | +end |
| 84 | + |
| 85 | +function rec_rhs_k!(R, z) |
| 86 | + n = size(R,1) |
| 87 | + T = float(real(typeof(z))) |
| 88 | + P = Legendre{T}() |
| 89 | + x = axes(P,1) |
| 90 | + Mm1 = imlogkernel_vec(n+1, z+1) |
| 91 | + fill!(R, zero(T)) |
| 92 | + R[1,1] = -2im*Mm1[2] +2*(z+1)*Mm1[1]-4 |
| 93 | + R[2,1] = -convert(T,4)/3 |
| 94 | + for j = 1:n-1 |
| 95 | + R[1,j+1] = - 2im * convert(T,j+1)/(2j+1) * Mm1[j+2] + 2*(1+z)*Mm1[j+1] - 2im * convert(T,j)/(2j+1) * Mm1[j] |
| 96 | + end |
| 97 | + R |
| 98 | +end |
| 99 | + |
| 100 | +rec_rhs_j!(R, z) = -1 ≤ real(z) ≤ 1 && -1 ≤ imag(z) ≤ 1 ? rec_rhs_j_insquare!(R, z) : rec_rhs_j_offsquare!(R, z) |
| 101 | + |
| 102 | + |
| 103 | +zlog(z) = iszero(z) ? zero(z) : z*log(z) |
| 104 | + |
| 105 | +L0(z) = zlog(1 + complex(z)) - zlog(complex(z)-1) - 2one(z) |
| 106 | +L1(z, r0=L0(z)) = (z+1)*r0/2 + 1 - zlog(complex(z)+1) |
| 107 | + |
| 108 | +M0(z) = L0(-im*float(z)) + m_const(0, float(z)) |
| 109 | +M1(z, r0=L0(-im*z)) = L1(-im*float(z), r0) + m_const(1, float(z)) |
| 110 | + |
| 111 | +function m_const(k, z) |
| 112 | + (x,y) = reim(z) |
| 113 | + T = float(real(typeof(z))) |
| 114 | + im*convert(T,π) * if k == 0 |
| 115 | + if x > 0 || y ≥ 1 || (x == 0 && !signbit(x) && y < 0) # need to treat im*y+0.0 differently |
| 116 | + convert(T, 1) |
| 117 | + elseif y ≤ -1 # x ≤ 0 |
| 118 | + convert(T, -3) |
| 119 | + else # x ≤ 0 and -1 < y < 1 |
| 120 | + ((1 + y) - 3*(1-y))/2 |
| 121 | + end |
| 122 | + elseif -1 ≤ y ≤ 1 && x ≤ 0 # k ≠ 0 |
| 123 | + -Weighted(Jacobi{T}(1,1))[y,k]/k |
| 124 | + else |
| 125 | + zero(T) |
| 126 | + end |
| 127 | +end |
| 128 | + |
| 129 | + |
| 130 | +function rec_rhs_j_offsquare!(R, z) |
| 131 | + n = size(R,1) |
| 132 | + x,y = reim(z) |
| 133 | + T = float(real(typeof(z))) |
| 134 | + P = Legendre{T}() |
| 135 | + Lm1 = transpose(complexlogkernel(P, z+im))[1:n+1] # transpose makes col-vector, works around slowess |
| 136 | + μ = m_const_vec(n, z) |
| 137 | + fill!(R, zero(T)) |
| 138 | + R[1,1] = - 2Lm1[2] - 4im*m_const(1,z)+ 2*(z+im)*(M0(1-im*z) + m_const0(z)) - 4im |
| 139 | + R[1,2] = -(convert(T,4)*im)/3 + 2x*m_const(1, z) |
| 140 | + for j = 3:n |
| 141 | + R[1,j] = 2x*μ[j] |
| 142 | + end |
| 143 | + R[2,1] = - convert(T,4)/3 * Lm1[3] - convert(T,2)/3 *M0(1-im*z) - convert(T,2)/3 *m_const0(z) + 2*(z+im)*Lm1[2] |
| 144 | + for j = 2:n |
| 145 | + R[2,j] = -2μ[j]/3 |
| 146 | + end |
| 147 | + for k = 2:n-1 |
| 148 | + R[k+1,1] = 2(im+z)*Lm1[k+1] - convert(T,2k)/(2k+1)*Lm1[k] - convert(T,2(k+1))/(2k+1)*Lm1[k+2] |
| 149 | + end |
| 150 | + R |
| 151 | +end |
| 152 | + |
| 153 | + |
| 154 | + |
| 155 | +# averages the difference between M(k,z-s) and L(k,-im*(z-s)) |
| 156 | +function m_const_square_0_vec(z, Wx) |
| 157 | + x,y = reim(z) |
| 158 | + T = float(typeof(x)) |
| 159 | + n = length(Wx) |
| 160 | + [im*convert(T,π)*((1+x) + (1-x) * ((1 + y) - 3*(1-y))/2); |
| 161 | + (im*convert(T,π)* (-1 + ((1 + y) - 3*(1-y))/2)) .* Wx ./ (2*(1:n))] |
| 162 | +end |
| 163 | + |
| 164 | + |
| 165 | +function rec_rhs_j_insquare!(R, z) |
| 166 | + n = size(R,1) |
| 167 | + T = float(real(typeof(z))) |
| 168 | + x,y = reim(z) |
| 169 | + W = Weighted(Jacobi{T}(1,1)) |
| 170 | + P = Legendre{T}() |
| 171 | + Wy = W[y,1:n-1] |
| 172 | + Wx = W[x,1:n] |
| 173 | + Vx = Weighted(Jacobi{T}(2,2))[x,1:n-2] |
| 174 | + Lm1 = transpose(complexlogkernel(P, z+im))[1:n+1] # transpose makes col-vector, works around slowess |
| 175 | + πT = convert(T, π) |
| 176 | + μ0 = m_const_square_0_vec(z, Wx) |
| 177 | + |
| 178 | + R[3:end,2:end] .= (im * πT) .*Vx ./ (2 .* (2:n-1) .* (1:(n-2))) .* (Wy ./ (2 .* (1:n-1)))' |
| 179 | + for k = 2:n-1 |
| 180 | + R[k+1,1] = - convert(T,2k)/(2k+1) * Lm1[k] + 2im*Lm1[k+1] - convert(T,2(k+1))/(2k+1) * Lm1[k+2] + |
| 181 | + - convert(T,k+1)/(2k+1) * μ0[k+2] - convert(T,k)/(2k+1) * μ0[k] + |
| 182 | + 2z*Lm1[k+1] + (z+im)*μ0[k+1] - Wx[k]/k * πT*Wy[1] |
| 183 | + end |
| 184 | + for j = 2:n |
| 185 | + R[2,j] = (im*πT/6 * (x^2 + x - 2)*(x-1)) * Wy[j-1] / (j-1) |
| 186 | + end |
| 187 | + for j = 3:n |
| 188 | + R[1,j] = (im*πT*(x-1)^2) * Wy[j-1] / (2*(j-1)) |
| 189 | + end |
| 190 | + R[1,2] = -convert(T,4)im/3 + im*πT*(1-x)^2/2 * Wy[1] |
| 191 | + R[2,1] = -4Lm1[3]/3 - 2μ0[3]/3 - 2M0(1-im*z)/3 - μ0[1]/3 - πT*Wx[1] * Wy[1] + 2*(z+im)*Lm1[2] + (z+im)*μ0[2] |
| 192 | + R[1,1] = -2Lm1[2] - μ0[2] + 2(z+im)*M0(1-im*z) - 2*πT*(1-x)*W[y,1] + (z+im)*μ0[1] - 4im |
| 193 | + R |
| 194 | +end |
| 195 | + |
| 196 | + |
| 197 | + |
| 198 | +struct LogKernelSquareData{T} |
| 199 | + A::Tridiagonal{T, Vector{T}} |
| 200 | + B::Tridiagonal{T, Vector{T}} |
| 201 | + X::Matrix{Complex{T}} |
| 202 | + F::Matrix{Complex{T}} # A*X + im*X*B' == F |
| 203 | + G::Matrix{Complex{T}} # B*X + im*X*A' == G |
| 204 | +end |
| 205 | + |
| 206 | +size(L::LogKernelSquareData, k...) = size(L.X, k...) |
| 207 | + |
| 208 | +function LogKernelSquareData{T}(n) where T |
| 209 | + A = Tridiagonal((zero(T):n-1) ./ (3:2:(2n+1)), [-1; zeros(T, n)], (convert(T,2):(1+n)) ./ (1:2:(2n))) |
| 210 | + B = Tridiagonal((one(T):n) ./ (3:2:(2n+1)), zeros(T, n+1), (one(T):n) ./ (1:2:(2n))) |
| 211 | + X = Matrix{Complex{T}}(undef, n, n) |
| 212 | + F = Matrix{Complex{T}}(undef, n, n) |
| 213 | + G = Matrix{Complex{T}}(undef, n, n) |
| 214 | + LogKernelSquareData(A, B, X, F, G) |
| 215 | +end |
| 216 | + |
| 217 | +LogKernelSquareData(n) = LogKernelSquareData{Float64}(n) |
| 218 | + |
| 219 | +logkernelsquare(z, n) = logkernelsquare!(LogKernelSquareData{float(real(typeof(z)))}(n), z) |
| 220 | +function logkernelsquare!(data, z) |
| 221 | + R1 = rec_rhs_k!(data.F, z) |
| 222 | + R2 = rec_rhs_j!(data.G, z) |
| 223 | + |
| 224 | + data.X[1,1] = L₀₀(z) |
| 225 | + biforwardsub!(data.X, z, data.A, data.B, R1, R2) |
| 226 | +end |
| 227 | + |
| 228 | +end # module MultivariateSingularIntegrals |
0 commit comments