Hello all,
I am an Applied Mathematics PhD student at CU Boulder, and I want to implement fast matrix free finite difference methods using Julia. This appeared at first glance to be quite simple task, but when I compare the method to simply forming a sparse matrix, my method is slower.
I believe that I have followed the advice laid out in the Multidimensional algorithms and iteration post that @tim.holy put together. I am a bit stumped at this point and think that I am missing something obvious.
I am running Julia 1.8 on an Intel MacBook, if that is helpful.
using SparseArrays, LinearAlgebra, BenchmarkTools
function D_matFree(u::AbstractArray{T,N}, h::Real, order::Real, dim::Int) where {T<:Real,N}
du = similar(u)
if order == 0
du[:] .= u[:]
return
elseif order == 1
fwd = 1/ h * [-11/6, 3, -3/2, 1/3] # 3rd order
# fwd = 1/ h * [3/2,-2, 1/2] # 2nd order
cnt = 1 / h * [-1/2, 0, 1/2]
elseif order == 2
fwd = 1/h^2 * [35/12, -26/3, 19/2, -14/3, 11/12] # 3rd order
# fwd = 1/h^2 * [2,-5,4,-1] # 2nd order
cnt = 1/h^2 * [1, -2, 1]
elseif order == 3
fwd = 1/h^3 *[-17/4,71/4,-59/2,49/2,-41/4,7/4] # 3rd order
# fwd = 1/h^3 * [-5/2,9,-12,7,-3/2] # 2nd order
cnt = 1/h^3 * [-1/2, 1, 0, -1, 1/2]
else
@error "Not implemented for order $order"
end
bwd = mod(order,2) == 0 ? fwd : -fwd
sz = size(u)
@assert all(size(du) .== sz)
J = length(fwd)
bnd = Int(floor(length(cnt)/2))
left = CartesianIndices(sz[1:dim-1])
right = CartesianIndices(sz[dim+1:N])
@inbounds for i in 1:sz[dim], l in left, r in right
if i <= bnd
du[l,i,r] = dot((@view u[l,i:i+J-1,r]), fwd)
elseif sz[dim]-bnd <= i
du[l,i,r] = dot((@view u[l,i:-1:i-J+1,r]), bwd)
else
du[l,i,r] = dot((@view u[l,i-bnd:i+bnd,r]), cnt)
end
end
du
end
function D_sparse(u::AbstractArray{T,N}, h::Real, order::Real, dim::Int) where {T<:Real,N}
du = similar(u)
if order == 0
du[:] .= u[:]
return
elseif order == 1
fwd = 1/ h * [-11/6, 3, -3/2, 1/3] # 3rd order
# fwd = 1/ h * [3/2,-2, 1/2] # 2nd order
cnt = 1 / h * [-1/2, 0, 1/2]
elseif order == 2
fwd = 1/h^2 * [35/12, -26/3, 19/2, -14/3, 11/12] # 3rd order
# fwd = 1/h^2 * [2,-5,4,-1] # 2nd order
cnt = 1/h^2 * [1, -2, 1]
elseif order == 3
fwd = 1/h^3 *[-17/4,71/4,-59/2,49/2,-41/4,7/4] # 3rd order
# fwd = 1/h^3 * [-5/2,9,-12,7,-3/2] # 2nd order
cnt = 1/h^3 * [-1/2, 1, 0, -1, 1/2]
else
@error "Not implemented for order $order"
end
bwd = mod(order,2) == 0 ? fwd : -fwd
sz = size(u)
n = sz[dim]
I = Int[]
J = Int[]
val = Float64[]
bnd = Int(floor(length(cnt)/2))
for i = 1:n
if i <= bnd
k = length(fwd)
I = vcat(I, i*ones(k))
J = vcat(J, i:i+k-1)
val = vcat(val, fwd)
elseif n-bnd <= i
k = length(bwd)
I = vcat(I, i*ones(k))
J = vcat(J, i:-1:i-k+1)
val = vcat(val, bwd)
else
k = length(cnt)
k2 = Int(floor(k/2))
I = vcat(I, i*ones(k))
J = vcat(J, i-k2:i+k2)
val = vcat(val, cnt)
end
ix = findall(1 .> J .|| n .< J )
@assert isempty(ix) "$i = i, ix = $(J[ix])"
end
# return J
DD = sparse(I,J,val,n,n)
left = CartesianIndices(sz[1:dim-1])
right = CartesianIndices(sz[dim+1:N])
for l in left, r in right
du[l,:,r] = DD * u[l,:,r]
end
return du
end
##
u = rand(1000, 5000)
h = 0.1
order = 1
dim = 1
@info "Matrix Free"
@btime du1 = D_matFree(u, h, order, dim)
@info "Sparse Implementation"
@btime du2 = D_sparse(u, h, order, dim);
relErr = norm(du1-du2)/norm(du1)
@info "Relative Error $relErr"
nothing