Conversation
The derivative of fmod(x, y) = x - y * floor(x/y) w.r.t. y is -floor(x/y), not 0. Updated both forward and backward derivative implementations in diff.meta.slang to include the correct y gradient. Forward: d(fmod) = x.d - floor(x.p/y.p) * y.d (was just x.d) Backward: y.d = -floor(x.p/y.p) * dOut (was 0) Fixes #10071 Co-authored-by: Harsh Aggarwal (NVIDIA) <szihs@users.noreply.github.com>
|
No actionable comments were generated in the recent review. 🎉 📝 WalkthroughWalkthroughThis PR updates the fmod function's backward differentiation implementation to properly handle derivatives from both operands, with both a forward-reverse mode and in-place variant. Accompanying test suite validates derivative correctness across multiple scenarios. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Poem
🚥 Pre-merge checks | ✅ 2 | ❌ 1❌ Failed checks (1 inconclusive)
✅ Passed checks (2 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Pull request overview
This PR aims to fix autodiff derivatives for fmod(x, y) so the gradient w.r.t. y is correctly propagated, and adds a regression test under tests/autodiff/.
Changes:
- Update
diff.meta.slangforward/backward derivative implementations forfmod. - Add a new autodiff regression test for
fmodand its expected output.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 5 comments.
| File | Description |
|---|---|
source/slang/diff.meta.slang |
Adjusts fmod forward/backward derivative propagation logic. |
tests/autodiff/autodiff-fmod.slang |
Adds a compute test covering forward and backward autodiff for fmod. |
tests/autodiff/autodiff-fmod.slang.expected.txt |
Adds golden output for the new fmod autodiff test. |
| // fmod(x, y) = x - y * floor(x / y) | ||
| // d/dx fmod(x, y) = 1 | ||
| // d/dy fmod(x, y) = -floor(x / y) |
There was a problem hiding this comment.
The added comment describes fmod(x, y) using floor(x/y), but Slang’s fmod matches C/HLSL semantics: fmod(x,y) = x - y * trunc(x/y) (toward zero). This matters for negative inputs, where floor gives a different quotient and would make the documented derivative incorrect.
| T.Differential dx = x.d; | ||
| T.Differential dy = __mul_p_d(-floor(x.p / y.p), y.d); | ||
| return DifferentialPair<T>(fmod(x.p, y.p), T.dadd(dx, dy)); |
There was a problem hiding this comment.
__d_fmod uses floor(x.p / y.p) to compute the y contribution. For fmod (float remainder), the correct quotient is trunc(x/y) toward zero; using floor gives wrong gradients for negative values (e.g., x=-7, y=4). Please switch to trunc(x.p / y.p) (or an equivalent helper consistent with the actual fmod implementation).
| x = diffPair(x.p, dOut); | ||
| y = diffPair(y.p); | ||
| y = diffPair(y.p, __mul_p_d(-floor(x.p / y.p), dOut)); |
There was a problem hiding this comment.
The backward derivative also uses floor(x.p / y.p) for the propagated derivative to y. To match fmod remainder semantics (truncation toward zero), this should use trunc(x.p / y.p); otherwise gradients w.r.t. y will be incorrect for negative inputs.
| // fmod(5.5, 2.0) = 1.5 | ||
| // d/dx fmod(x,y) = 1 | ||
| // d/dy fmod(x,y) = -floor(x/y) = -floor(5.5/2.0) = -floor(2.75) = -2 | ||
| { |
There was a problem hiding this comment.
The test comments use -floor(x/y) for the y-derivative, but Slang fmod uses truncation toward zero (trunc(x/y)). The current inputs are all positive so floor==trunc; adding a negative-input case (e.g., x=-7, y=4) would catch the difference.
| RWStructuredBuffer<float> outputBuffer; | ||
|
|
||
| typedef DifferentialPair<float> dpfloat; | ||
| typedef float.Differential dfloat; |
There was a problem hiding this comment.
typedef float.Differential dfloat; is declared but not used in this test. Removing it would avoid dead code and keep the test focused on the fmod derivative behavior.
| typedef float.Differential dfloat; |
This PR addresses issue #10071
Generated with Claude Code