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 broadcast- a.S .* b, and that works fine with- Zygoteβ©οΈ