[Derivatives] Implement forward-mode derivative for std::comp_ellint_1#1674
[Derivatives] Implement forward-mode derivative for std::comp_ellint_1#1674ayush4874 wants to merge 16 commits intovgvassilev:masterfrom
Conversation
|
Hi! I checked the README to find the community chat, but I couldn't find a Gitter or Discord link. Is there an active channel I should join for development discussions? |
| using std::pow_pullback; | ||
| using std::pow_pushforward; | ||
| using std::sqrt_pushforward; | ||
| using std::comp_ellint_1_pushforward; |
There was a problem hiding this comment.
We will need a pullback for this and also tests.
There was a problem hiding this comment.
Thanks for the review!
I replaced the manual pullback with comp_ellint_1_darg0 (derivative w.r.t arg 0). This allows Clad to automatically derive the reverse-mode pullback, resolving the signature ambiguity.
I also added a regression test (clad/test/Differentiator/EllipticTest.cpp) and verified the gradients against the analytical values locally.
Verification Output:
Forward Derivative: 0.541732
Reverse Derivative: 0.541732
| // ------------------------------------------------------------------------- | ||
| // [Ayush GSoC 2026 Contribution] | ||
| // Feature: Added support for Complete Elliptic Integral of the First Kind | ||
| // Target: std::comp_ellint_1(k) | ||
| // Formula: dK/dk = (E(k) - (1-k^2)*K(k)) / (k * (1-k^2)) |
There was a problem hiding this comment.
Remove the redundant comments.
test/Differentiator/EllipticTest.cpp
Outdated
| @@ -0,0 +1,33 @@ | |||
| // RUN: %clangxx -fplugin=%clad_plugin -std=c++17 %s -o %t | |||
There was a problem hiding this comment.
This test should be part of test/Features/stl-cmath.cpp
There was a problem hiding this comment.
Done. I've moved the test case into test/Features/stl-cmath.cpp and removed the verbose comments from the header file.
test/Features/stl-cmath.cpp
Outdated
| // | ||
| DEFINE_FUNCTIONS(erf) // x in (-inf,+inf) | ||
|
|
||
| double comp_ellint_1_wrapper(double k) { |
There was a problem hiding this comment.
You need to make this look the same as the other functions.
There was a problem hiding this comment.
Done. Since comp_ellint_1 is a C++17 addition and does not have a legacy C equivalent (like comp_ellint_1f), I could not use the DEFINE_FUNCTIONS macro directly.
However, I have manually defined a templated wrapper f_comp_ellint_1 that follows the exact same naming convention and pattern as the macro, ensuring consistency with the rest of the file.
test/Features/stl-cmath.cpp
Outdated
| double comp_ellint_1_wrapper(double k) { | ||
| return std::comp_ellint_1(k); | ||
| } | ||
| // Manually defined to match DEFINE_FUNCTIONS pattern (comp_ellint_1f does not exist) |
There was a problem hiding this comment.
Still does not look as the surroundings. Please make sure you understand this file and its comments.
There was a problem hiding this comment.
My apologies for the oversight. I realized upon closer inspection that DEFINE_FUNCTIONS generates suffix variants (f_...f, f_...l) that my previous template missed.
I have now added f_comp_ellint_1f and f_comp_ellint_1l manually to fully replicate the macro's behavior as requested.
test/Features/stl-cmath.cpp
Outdated
| inline float f_comp_ellint_1f(float x) { return std::comp_ellint_1(x); } | ||
| inline long double f_comp_ellint_1l(long double x) { return std::comp_ellint_1(x); } |
There was a problem hiding this comment.
Why we cannot use DEFINE_FUNCTIONS here? Note this is missing the pullbacks and updating the section with the proper comment // ---- special fns. Also misses updating the table in the beginning of the file. Misses the range...
As I've said -- needs to look like the other functions tests in the same file.
There was a problem hiding this comment.
I have updated the file to fully match the surrounding style and structure as requested:
- Added
comp_ellint_1to the feature table at the top of the file. - Moved the implementation to the "Special Functions" section with the
// k in (-1, 1)range comment. - Defined
comp_ellint_1fandcomp_ellint_1lwrappers so I can useDEFINE_FUNCTIONS(comp_ellint_1)directly. - Updated the test case in
main()to use the standardCHECK_ALL_RANGEmacro.
There was a problem hiding this comment.
@vgvassilev it's ready for review, can you have a look?
There was a problem hiding this comment.
Why adding only this special function and not the rest?
There was a problem hiding this comment.
I initially isolated comp_ellint_1 to ensure the testing pattern for special functions was correct before expanding.
Since comp_ellint_2 (Complete Elliptic Integral of the Second Kind) is actually required for the derivative of comp_ellint_1, it is logical to group them. I will update this PR to include std::comp_ellint_2 and std::comp_ellint_3 so they can be reviewed together. I'll push those changes shortly.
| } | ||
|
|
||
| // This allows Clad to auto-generate both Forward and Reverse mode. | ||
| CUDA_HOST_DEVICE double comp_ellint_1_darg0(double k) { |
There was a problem hiding this comment.
warning: function 'comp_ellint_1_darg0' defined in a header file; function definitions in header files can lead to ODR violations [misc-definitions-in-headers]
CUDA_HOST_DEVICE double comp_ellint_1_darg0(double k) {
^Additional context
include/clad/Differentiator/BuiltinDerivatives.h:1079: make as 'inline'
CUDA_HOST_DEVICE double comp_ellint_1_darg0(double k) {
^| *d_y += (y / h) * d_z; | ||
| } | ||
|
|
||
| CUDA_HOST_DEVICE inline double comp_ellint_1_darg0(double k) { |
There was a problem hiding this comment.
Do we need the _darg versions here?
There was a problem hiding this comment.
You're right, we don't need them. I've removed the _darg helpers and moved the derivative logic directly inside the pushforward templates.
test/Features/stl-cmath.cpp
Outdated
| inline float comp_ellint_1f(float x) { return std::comp_ellint_1(x); } | ||
| inline long double comp_ellint_1l(long double x) { return std::comp_ellint_1(x); } | ||
|
|
||
| DEFINE_FUNCTIONS(comp_ellint_1) |
There was a problem hiding this comment.
Done. I've added comments specifying the domain is k in (-1, 1) for these integrals.
There was a problem hiding this comment.
look how was done for the other functions.
test/Features/stl-cmath.cpp
Outdated
| double k = 0.5, nu = 0.3; | ||
|
|
||
| // Test differentiation w.r.t 'k' (Arg 0) | ||
| auto d_ellint3_k = clad::differentiate(std::comp_ellint_3, 0); | ||
| double res_k = d_ellint3_k.execute(k, nu); | ||
|
|
||
| // Test differentiation w.r.t 'nu' (Arg 1) | ||
| auto d_ellint3_nu = clad::differentiate(std::comp_ellint_3, 1); | ||
| double res_nu = d_ellint3_nu.execute(k, nu); | ||
|
|
||
| // Simple check to ensure we got numbers back (prevents unused var warning) | ||
| (void)res_k; | ||
| (void)res_nu; |
There was a problem hiding this comment.
Here we should compare against the numerical, right? See what the CHECK macro does.
There was a problem hiding this comment.
You are right. I updated the test to compare the symbolic result against a Finite Difference numerical approximation. The test will now fail (return 1) if the derivative deviates from the numerical reference.
test/Features/stl-cmath.cpp
Outdated
| inline float comp_ellint_1f(float x) { return std::comp_ellint_1(x); } | ||
| inline long double comp_ellint_1l(long double x) { return std::comp_ellint_1(x); } | ||
|
|
||
| DEFINE_FUNCTIONS(comp_ellint_1) |
There was a problem hiding this comment.
look how was done for the other functions.
test/Features/stl-cmath.cpp
Outdated
| double res_k = d_ellint3_k.execute(k, nu); | ||
| double sym_k = d_ellint3_k.execute(k, nu); | ||
| // Numerical approximation: (f(k+h) - f(k-h)) / 2h | ||
| double num_k = (std::comp_ellint_3(k + h, nu) - std::comp_ellint_3(k - h, nu)) / (2 * h); |
There was a problem hiding this comment.
There is an api in this file that gets you the numerical part.
There was a problem hiding this comment.
I see the pattern now. I updated the tests to match atan2 and pow by using the f_ wrappers.
For comp_ellint_3, I fixed the second arg in the wrapper so I can just use CHECK_ALL_RANGE, which handles the numerical verification automatically.
3944d20 to
2190f7b
Compare
|
(assuming the bots are green) |
|
clang-tidy review says "All clean, LGTM! 👍" |
|
CI failures were caused by I’ve added |
Implemented the derivative of the Complete Elliptic Integral of the First Kind (std::comp_ellint_1) using the standard formula involving std::comp_ellint_2. Verified against numerical differentiation.
523878c to
c686d61
Compare
|
clang-tidy review says "All clean, LGTM! 👍" |
|
clang-tidy review says "All clean, LGTM! 👍" |
|
clang-tidy review says "All clean, LGTM! 👍" |
|
Hi @ayush4874, what is the state of this PR -- it seems to be failing on a lot of platforms. |
|
Hi @vgvassilev, apologies, I was busy with semester exams. I just pushed the fix for the CI failures. The root cause was a type promotion mismatch. I fixed it by updating the templates with Tests are passing cleanly locally now. |
|
clang-tidy review says "All clean, LGTM! 👍" |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
…ibility and fix clang-format
|
Pushed a fix for the Mac CI failures. Older versions of |
Summary
This PR implements the exact symbolic differentiation rule for
std::comp_ellint_1(Complete Elliptic Integral of the First Kind), resolving the fallback to numerical differentiation.Fixes #1673
Mathematical Context
The derivative of the First Kind Elliptic Integral$K(k)$ is defined using the Second Kind Integral $E(k)$ as:
where:
std::comp_ellint_1(k)std::comp_ellint_2(k)Implementation Details
clad/Differentiator/BuiltinDerivatives.hcomp_ellint_1_pushforwardin theclad::custom_derivativesnamespace.usingdeclaration to register the function in the global lookup scope.Verification
I tested the implementation locally by differentiating a wrapper function for
std::comp_ellint_1.1. Compilation Check:
The compilation warning
falling back to numerical differentiationis gone, confirming Clad is now using the symbolic rule.2. Numerical Accuracy:$k = 0.5$ :
I verified the derivative output at
0.541733...0.541732This confirms the implementation produces the correct gradients for physics applications.