Iβm writing a package to perform some physics calculations on large 3D arrays. In order to perform these calculations I need to compute a few quantities, in particular differential operators of the fields like the gradient with respect to the position (the 3 dimensions of the array correspond to the x, y and z coordinates), which I compute in Fourier space by multiplying by the wavevectors. In practice this is done by computing a FFT of the field and doing the product with a precomputed array containing the wavevectors.
Since the arrays are large and I need to perform many operations like the above, I defined mutable structs ScalarFieldCore
and VectorFieldCore
holding the fields as arrays in the field S
and V
, and definitions for basic operations such as Base.:*
which leverage LoopVectorization
to gain a significant boost in performance.
Hereβs a relevant example of one of the Base.:*
methods I implemented:
function Base.:*(
a::ScalarFieldCore{Complex{T}},
b::Array{T, 3}
)::ScalarFieldCore{Complex{T}} where T<:Real
A = StructArray(a.S)
return ScalarFieldCore{Complex{T}}(
a.L,
a.Ng,
Array(@tturbo StructArray{Complex{T}}(@. (A.re*b, A.im*b)))
)
end
Base.:*(a::Array{T, 3}, b::ScalarFieldCore{Complex{T}}) where T<:Real = Base.:*(b, a)
This turns out to work really well, and I was able to gain great leaps in performance. The issue is that now I want to make my code autodifferentiable. The initial field I feed into my code depends on some parameters, and I want to be able to compute the gradient with respect to those.
I found that Zygote
works out of the box with generic structs, so I built a simple example with my code of a function that takes the parameters and spits out a number which is the sum of the final field across the x, y and z positions. However I got the following error:
MethodError: no method matching vmaterialize(::Array{Float32, 3}, ::Val{:GridSPTCore}, ::Val{(true, 0, 0, 0, true, 0, 32, 15, 64, 0x0000000000000010, 1, true)})
The function `vmaterialize` exists, but no method is defined for this combination of argument types.
The stacktrace points to the Base.:*
method I pasted above.[1] I found this issue: Hook into Zygote.jl? Β· Issue #108 Β· JuliaSIMD/LoopVectorization.jl Β· GitHub, that looks identical to what Iβm finding, but itβs still open after 5 yearsβ¦
Unfortunately Iβm really a beginner when it comes to AD, and what Iβm finding online is too obscure for me to get a good grip on how I can make LoopVectorization
and Zygote
work together. On the other hand Iβm really reluctant in letting go of the performance improvement I got out of LoopVectorization
. How hard do you think it is to code a βadjoint of vmaterializeβ? Is it worth the effort or should I adopt some other performance-enhancement method?
Just to be sure I tried redefining
Base:.*
so that it just does a normal broadcasta.S .* b
, and that works fine withZygote
β©οΈ