-
Notifications
You must be signed in to change notification settings - Fork 63
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
RFC: Rules for real-to-complex and complex-to-real functions #176
Comments
See also this issue about why Zygote doesn't do this (tl/dr Zygote basically treats all reals as embedded in the complex numbers): FluxML/Zygote.jl#342 and an update here: FluxML/Zygote.jl#472 Also ccing @MikeInnes because this could change the behavior of Zygote. |
+1 for the pushforward / pullback of
Conversely, if we defined pushforwards / pullbacks to be complex for some functions Edit: Actually, this does not work since Of course, a similar effect could be achieved using
but this incurs some runtime penalty. In the case of |
Consider a case where we have a function
f: ℝᵐ → ℂʳ → ℂˢ → ℝⁿ = ℝᵐ → ℝⁿ
, which we can write asf = f₃ ∘ f₂ ∘ f₁
.Typically
f₁
will produce a complex output by adding, subtracting, multiplying or dividing the real by a complex numberor by calling
promote
,complex
,Complex
orcis
.Typically
f₃
will produce a real output by calling a non-holomorphic function likereal
,imag
,abs
,abs2
,hypot
, orangle
.From #167, the fact that there are complex intermediates to
f
is just an implementation detail. We could have definedf: ℝᵐ → ℝ²ʳ → ℝ²ˢ → ℝⁿ
, and the pushforwards and pullbacks of this newf
should behave the same.Since in general tangents are derivatives of a primal wrt a real, and co-tangents are derivatives of a real wrt a primal,
the pushforward through
f₁: ℝᵐ → ℂʳ
should produce a complex tangent, while the pushforward throughf₃: ℂˢ → ℝⁿ
should produce a real tangent.Conversely, the pullback through
f₃
should produce a complex cotangent, and the pullback throughf₁
should produce a real cotangent.The pushforward case is pretty easy to handle. We can 1) assume that a non-sensical tangent will not be passed and do nothing special (i.e. assume upstream AD did the right thing) or 2) define custom
frule
s that ensure that the produced tangent of unary functionsf₃(::Complex)::Real
is real.The pullback case is more complicated. Right now e.g. in Zygote, unless you create a complex number from reals by calling
complex
, you'll end up pulling back complex numbers through the initial real part of your program, which not only is wasteful but could break assumptions of therrule
s of upstream functions. I propose for the binary functionsf₁
adding customrrule
s forf₁(::Real, ::Complex)::Complex
andf₁(::Complex, ::Real)::Complex
to ensure that the co-tangent pulled back to a real primal is actually real.This came up a point of discussion in JuliaDiff/ChainRules.jl#196, and I would appreciate feedback so we can clarify our conventions here.
The text was updated successfully, but these errors were encountered: