Automatic differentiation of complex functions: simple workaround

We had a big thread about this a while ago: Taking Complex Autodiff Seriously in ChainRules

The TL:DR is that holomorphic functions are not the only functions which exist and are widely used, they’re actually a very very small subset of the functions that exist and are used. It would be a really bad idea if AD packages automatically assumed that functions are holomorphic. Derivatives of complex functions are still perfectly well defined even if the function is not holomorphic.

The approach that won out was just computing pullbacks correctly, so that users could either construct the full jacobian from the pullback, or a wirtinger derivative ( that is, \partial \over \partial z) if they prefer. (explanation of the math here: Taking Complex Autodiff Seriously in ChainRules - #52 by Mason)

If you do indeed want to just deal with \partial \over \partial z alone, you can do that like so:

using Zygote

function ∂z(f, z)
  _, back = Zygote.pullback(f, z)
  du, dv = back(1)[1], back(im)[1]
  (du' + im*dv')/2
end
julia> ∂z(f, 2+2im)
0.12000000000000002 + 0.16000000000000003im

Zygote.jl has docs on this here: Complex Differentiation · Zygote
And ChainRules.jl has docs on it here: Complex numbers · ChainRules

2 Likes