Skip to content

Commit f5b2d14

Browse files
committed
Add pushforwards for the Kokkos:: math functions.
Kokkos ships its own math functions (Kokkos::cos, Kokkos::sqrt, ...) so a kernel differentiates identically on host and device. clad has no registered derivatives for them, so it descends into the wrapper's body and trips an internal assertion instead of differentiating the call. Register pushforwards for cos, sin, sqrt, and fabs in KokkosBuiltins.h, mirroring how BuiltinDerivatives.h handles the std:: math functions: clad now emits a call to the known derivative rather than cloning the wrapper. Reverse mode is synthesized from the same pushforward, so no pullback is added. The change is header-only: dispatch to clad::custom_derivatives::Kokkos::* is the path the View pushforwards in this header already use.
1 parent 02cd313 commit f5b2d14

3 files changed

Lines changed: 71 additions & 0 deletions

File tree

include/clad/Differentiator/KokkosBuiltins.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -322,6 +322,31 @@ void operator_call_pullback(const ::Kokkos::View<DataType, ViewParams...>* v,
322322

323323
/// Kokkos functions (view utils)
324324
namespace Kokkos {
325+
/// Pushforwards for the Kokkos math functions -- without a registered
326+
/// derivative clad differentiates the wrapper body itself and asserts.
327+
/// Pushforward only, as std:: math is in BuiltinDerivatives.h: reverse mode is
328+
/// synthesized from it, so a hand-written _pullback would be rejected.
329+
template <typename T, typename dT>
330+
KOKKOS_INLINE_FUNCTION clad::ValueAndPushforward<T, dT>
331+
cos_pushforward(T x, dT d_x) {
332+
return {::Kokkos::cos(x), (-1) * ::Kokkos::sin(x) * d_x};
333+
}
334+
template <typename T, typename dT>
335+
KOKKOS_INLINE_FUNCTION clad::ValueAndPushforward<T, dT>
336+
sin_pushforward(T x, dT d_x) {
337+
return {::Kokkos::sin(x), ::Kokkos::cos(x) * d_x};
338+
}
339+
template <typename T, typename dT>
340+
KOKKOS_INLINE_FUNCTION clad::ValueAndPushforward<T, dT>
341+
sqrt_pushforward(T x, dT d_x) {
342+
return {::Kokkos::sqrt(x), d_x / (((T)2) * ::Kokkos::sqrt(x))};
343+
}
344+
template <typename T, typename dT>
345+
KOKKOS_INLINE_FUNCTION clad::ValueAndPushforward<T, dT>
346+
fabs_pushforward(T x, dT d_x) {
347+
return {::Kokkos::fabs(x), (x < 0) ? ((-1) * d_x) : d_x};
348+
}
349+
325350
template <typename View1, typename View2, typename T>
326351
inline void deep_copy_pushforward(const View1& dst, const View2& src, T param,
327352
const View1& d_dst, const View2& d_src,

unittests/Kokkos/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ add_clad_unittest(KokkosTests
44
ViewBasics.cpp
55
ParallelReduce.cpp
66
ParallelFor.cpp
7+
MathFunctions.cpp
78
)
89

910
# If llvm does not require rtti, kokkos does.

unittests/Kokkos/MathFunctions.cpp

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// Pins the Kokkos math pushforwards (cos/sin/sqrt/fabs, KokkosBuiltins.h)
2+
// against a finite-difference tangent, forward and reverse.
3+
4+
#include "TestUtils.h"
5+
#include "clad/Differentiator/Differentiator.h"
6+
#include "clad/Differentiator/KokkosBuiltins.h"
7+
#include <Kokkos_Core.hpp>
8+
#include "gtest/gtest.h"
9+
10+
// Exercises all four functions in one expression. sqrt(fabs(x)) keeps the test
11+
// valid for both signs of x (the fabs kink is only at 0, which we avoid).
12+
double math_fn(double x, double y) {
13+
return Kokkos::sqrt(Kokkos::fabs(x)) * Kokkos::cos(y) + Kokkos::sin(x * y);
14+
}
15+
16+
TEST(KokkosMath, Forward) {
17+
const double eps = 1e-6, tol = 1e-5;
18+
auto fn_x = clad::differentiate(math_fn, "x");
19+
auto fn_y = clad::differentiate(math_fn, "y");
20+
for (double x : {1.3, -1.3}) {
21+
std::function<double(double)> gx = [x](double t) {
22+
return math_fn(t, 0.7);
23+
};
24+
std::function<double(double)> gy = [x](double t) { return math_fn(x, t); };
25+
EXPECT_NEAR(fn_x.execute(x, 0.7), finite_difference_tangent(gx, x, eps),
26+
tol);
27+
EXPECT_NEAR(fn_y.execute(x, 0.7), finite_difference_tangent(gy, 0.7, eps),
28+
tol);
29+
}
30+
}
31+
32+
TEST(KokkosMath, Reverse) {
33+
const double eps = 1e-6, tol = 1e-5;
34+
auto fn_grad = clad::gradient(math_fn);
35+
for (double x : {1.3, -1.3}) {
36+
double dx = 0, dy = 0;
37+
fn_grad.execute(x, 0.7, &dx, &dy);
38+
std::function<double(double)> gx = [x](double t) {
39+
return math_fn(t, 0.7);
40+
};
41+
std::function<double(double)> gy = [x](double t) { return math_fn(x, t); };
42+
EXPECT_NEAR(dx, finite_difference_tangent(gx, x, eps), tol);
43+
EXPECT_NEAR(dy, finite_difference_tangent(gy, 0.7, eps), tol);
44+
}
45+
}

0 commit comments

Comments
 (0)