Skip to content

[MLIR] fix: complex.create reverse mode flips imaginary-operand gradient sign - #2899

Merged
mofeing merged 6 commits into
EnzymeAD:mainfrom
kimjune01:complex-create-im-coverage
Jun 26, 2026
Merged

mofeing merged 6 commits into
EnzymeAD:mainfrom
kimjune01:complex-create-im-coverage

Conversation

@kimjune01

@kimjune01 kimjune01 commented Jun 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #2900.

complex.create in reverse mode negates the gradient on its imaginary operand. The reverse generator conjugates the cotangent it fetches for each complex-result op (createConjOp on the diffe), and the other complex rules account for that; CreateOp does not, so its rule reads ComplexIm(DiffeRet) = Im(conj(z_bar)) = -Im(z_bar). Its contributions are real f32 (where createConjOp is identity), so nothing downstream compensates. Full reasoning and the emitted-IR receipt are in #2900.

Change

  • Negate the imaginary-operand contribution in the CreateOp rule, matching the NegF the ImOp rule already uses, so CreateOp is consistent with the cotangent convention the other complex rules follow. complex.re is unaffected. Only complex.create in primal position changes; complex.mlir uses create only in the adjoint, so nothing there moves.
  • Adds test/MLIR/ReverseMode/complex_create_im.mlir: differentiates f(t) = im(create(0, t)) = t and checks the returned cotangent is +seed. The corrected adjoint chain is an identity, so it canonicalizes to return %arg1; the buggy -seed chain cannot.

Verification

Developed against your CI (no local MLIR build here). The test-only commit's MLIR run went red on the sign, which is the bug. The post-fix run currently needs maintainer approval to start (action_required), so a green-after-fix is pending your approval rather than confirmed by me. Happy to adjust a CHECK line against the exact canonicalized IR once it runs.

f(t) = im(create(0, t)) = t is a smooth real identity, so df/dt must be +1.
The existing complex.mlir covers re/im standalone and create in the forward
position, but not the consumer path where create feeds im. Adds that case;
asserts no arith.negf survives (im's adjoint negation cancels the Wirtinger
conj wrap when the gradient is correct).
The prior CHECK-NOT: arith.negf was a poor discriminator: im's adjoint
legitimately emits a negf even when correct. Assert the actual return is the
positive seed (df/dt=+1) so a failure pins the sign flip and dumps the return.
The MLIR reverse wrap conjugates the incoming cotangent of every
complex-result op (enzyme-tblgen.cpp, createConjOp on diffe). For holomorphic
ops this cancels against the per-contribution conj; for complex.create (R^2->C)
it does not: the rule extracted ComplexIm(DiffeRet) = Im(conj(z_bar)) = -Im(z_bar),
negating the gradient flowing to the imaginary operand. So f(t) = im(create(0,t))
= t differentiated to -1 instead of +1.

Negate the imaginary-operand contribution to compensate, mirroring the existing
NegF compensation in the ImOp rule. Only complex.create in primal position is
affected (no existing lit test exercises it; complex.mlir uses create only in
the adjoint). Covered by complex_create_im.mlir.
@kimjune01 kimjune01 changed the title [MLIR] test: reverse-mode coverage for complex.create -> complex.im [MLIR] fix: complex.create reverse mode flips imaginary-operand gradient sign Jun 24, 2026
CI confirmed the fix flips the gradient to +seed, but canonicalize does not
fold the corrected chain to 'return %arg1' (it leaves the negf/conj chain and
returns %8 = +seed). Assert the fix's signature instead: a negf of the
complex.im result. Without the fix, complex.im is the returned cotangent (-seed)
with no following negf, so this still discriminates the bug.
@kimjune01
kimjune01 marked this pull request as ready for review June 25, 2026 00:05

@mofeing mofeing left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@mofeing
mofeing added this pull request to the merge queue Jun 26, 2026
Merged via the queue into EnzymeAD:main with commit 8244d9c Jun 26, 2026
29 of 30 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reverse mode: complex.create flips the imaginary-operand gradient sign

2 participants