You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
wp.sqrt() gradient is inf at x == 0, unlike wp.length() #2290
The adjoint of wp.sqrt() computes 0.5 / sqrt(x) with no guard, so its gradient is inf at x == 0. Downstream of that, the inf usually turns into NaN. For example, in a hand-written norm wp.sqrt(wp.dot(v, v)) at v == 0, the inf is multiplied by the zero gradient of dot:
Expected: v.grad[1] == [0, 0, 0], which is what wp.length(v) already returns. adj_length skips the gradient update when the result is at or below kEps, but adj_sqrt has no such guard. With wp.length() the gradient at zero is a valid subgradient (0); with an equivalent wp.sqrt() it is inf/NaN.
Proposed fix: guard adj_sqrt the same way as adj_length, giving a zero gradient at x == 0 (and -0), while still propagating NaN for x < 0, where the forward result is already NaN.
Application / Workflow Impact
MuJoCo Warp's differentiability work (google-deepmind/mujoco_warp#1727) hits this. Its enable_grad() currently monkey-patches wp.sqrt with a safe_sqrt that has a custom zero-at-origin gradient (adjoint.py). In the review discussion it was suggested that this be upstreamed to Warp instead (comment, replying to this comment). The tape-control half of that discussion is tracked separately in #2285.
Python 3.12, Linux x86_64. Reproduced on CPU. The adjoint is shared C++ code in warp/native/builtin.h, so CUDA should behave the same, but I haven't tested it on CUDA.
Activity
Guard adj_sqrt against division by zero at x == 0 (NVIDIAGH-2290)
Guard adj_sqrt against division by zero at x == 0 (NVIDIAGH-2290)
Guard adj_sqrt against division by zero at x == 0 (NVIDIAGH-2290)