Skip to content

Commit db7eb33

Browse files
committed
Add basic reverse-mode custom derivatives for STL iterators
This commit adds basic reverse-mode custom derivatives for STL iterators. More specifically, the commit adds the below custom derivatives: - operator_star_reverse_forw for iterators - constructor_reverse_forw for iterators - operator_plus_plus_reverse_forw and operator_plus_plus_pullback for iterators Closes vgvassilev#751
1 parent a40fa2c commit db7eb33

2 files changed

Lines changed: 144 additions & 0 deletions

File tree

include/clad/Differentiator/STLBuiltins.h

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#include <clad/Differentiator/BuiltinDerivatives.h>
77
#include <clad/Differentiator/FunctionTraits.h>
88
#include <initializer_list>
9+
#include <iterator>
910
#include <memory>
1011
#include <tuple>
12+
#include <type_traits>
1113
#include <vector>
1214

1315
namespace clad {
@@ -26,6 +28,19 @@ template <class T> void zero_init(typename std::allocator<T>&) {
2628
}
2729

2830
namespace custom_derivatives {
31+
32+
namespace helpers {
33+
template <class T, typename U = void> struct is_iterator : ::std::false_type {};
34+
35+
template <class T>
36+
struct is_iterator<
37+
T, typename ::std::enable_if<!::std::is_same<
38+
typename ::std::iterator_traits<T>::value_type, void>::value>::type>
39+
: ::std::true_type {
40+
using type = bool;
41+
};
42+
} // namespace helpers
43+
2944
namespace class_functions {
3045

3146
// vector forward mode
@@ -678,6 +693,53 @@ void operator_star_pullback(const ::std::unique_ptr<T>* u, U pullback,
678693
**d_u += pullback;
679694
}
680695

696+
template <typename It>
697+
clad::ValueAndAdjoint<typename ::std::iterator_traits<It>::reference,
698+
typename ::std::iterator_traits<It>::reference>
699+
operator_star_reverse_forw(It* it, It* d_it) {
700+
return {**it, **d_it};
701+
}
702+
703+
template <
704+
typename It,
705+
typename ::clad::custom_derivatives::helpers::is_iterator<It>::type = 1>
706+
clad::ValueAndAdjoint<It, It>
707+
constructor_reverse_forw(clad::ConstructorReverseForwTag<It>, It it, It d_it) {
708+
return {It{it}, It{d_it}};
709+
}
710+
711+
template <
712+
typename It,
713+
typename ::clad::custom_derivatives::helpers::is_iterator<It>::type = 1>
714+
clad::ValueAndAdjoint<It, It> operator_plus_plus_reverse_forw(It* it,
715+
It* d_it) {
716+
return {++*it, ++*d_it};
717+
}
718+
719+
template <
720+
typename It,
721+
typename ::clad::custom_derivatives::helpers::is_iterator<It>::type = 1>
722+
void operator_plus_plus_pullback(It* it, It pullback, It* d_it) {
723+
--*it;
724+
--*d_it;
725+
}
726+
727+
template <
728+
typename It,
729+
typename ::clad::custom_derivatives::helpers::is_iterator<It>::type = 1>
730+
clad::ValueAndAdjoint<It, It> operator_plus_plus_reverse_forw(It* it, int,
731+
It* d_it, int*) {
732+
return {++*it, ++*d_it};
733+
}
734+
735+
template <
736+
typename It,
737+
typename ::clad::custom_derivatives::helpers::is_iterator<It>::type = 1>
738+
void operator_plus_plus_pullback(It* it, int, It pullback, It* d_it, int*) {
739+
--*it;
740+
--*d_it;
741+
}
742+
681743
} // namespace class_functions
682744

683745
namespace std {

test/Gradient/STLCustomDerivatives.C

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,16 @@ double fn19(Session const *session, float *tensor_theory_params) {
255255
return out;
256256
}
257257

258+
double fn20(std::vector<double>::iterator start, std::vector<double>::iterator end) {
259+
double sum = 0;
260+
int u = 1;
261+
for (auto it = start; it != end; it++) {
262+
sum += u * *it;
263+
u += 2;
264+
}
265+
return sum;
266+
}
267+
258268
int main() {
259269
double d_i, d_j;
260270
INIT_GRADIENT(fn1);
@@ -274,6 +284,7 @@ int main() {
274284
INIT_GRADIENT(fn15);
275285
INIT_GRADIENT(fn16);
276286
INIT_GRADIENT(fn17);
287+
INIT_GRADIENT(fn20);
277288

278289
TEST_GRADIENT(fn1, /*numOfDerivativeArgs=*/2, 3, 5, &d_i, &d_j); // CHECK-EXEC: {1.00, 1.00}
279290
TEST_GRADIENT(fn2, /*numOfDerivativeArgs=*/2, 3, 5, &d_i, &d_j); // CHECK-EXEC: {2.00, 1.00}
@@ -294,6 +305,20 @@ int main() {
294305
TEST_GRADIENT(fn17, /*numOfDerivativeArgs=*/2, 1, 1, &d_i, &d_j); // CHECK-EXEC: {1.00, 3.00}
295306
auto d_fn18 = clad::gradient(fn18, "tensor_theory_params");
296307
auto d_fn19 = clad::gradient(fn19, "tensor_theory_params");
308+
309+
std::vector<double> v{1, 3, 5, 7, 9};
310+
std::vector<double> dv(5, 0);
311+
auto dbegin = dv.begin();
312+
auto dend = dv.end();
313+
fn20_grad.execute(v.begin(), v.end(), &dbegin, &dend);
314+
// CHECK-EXEC: {1.00, 3.00, 5.00, 7.00, 9.00}
315+
printf("{");
316+
for (auto i = 0; i < dv.size(); ++i) {
317+
printf("%.2f", dv[i]);
318+
if (i != dv.size() - 1)
319+
printf(", ");
320+
}
321+
printf("}\n");
297322
}
298323

299324
// CHECK: void fn1_grad(double u, double v, double *_d_u, double *_d_v) {
@@ -1269,6 +1294,63 @@ int main() {
12691294
// CHECK-NEXT: }
12701295
// CHECK-NEXT: }
12711296

1297+
// CHECK: void fn20_grad({{.*}}::iterator start, {{.*}}::iterator end, {{.*}}::iterator *_d_start, {{.*}}::iterator *_d_end) {
1298+
// CHECK-NEXT: {{.*}} it = {};
1299+
// CHECK-NEXT: {{.*}} _d_it{};
1300+
// CHECK-NEXT: clad::tape<{{.*}}> _t2 = {};
1301+
// CHECK-NEXT: clad::tape<double> _t3 = {};
1302+
// CHECK-NEXT: clad::tape<double> _t4 = {};
1303+
// CHECK-NEXT: clad::tape<clad::ValueAndAdjoint<{{.*}}>::reference, typename ::std::iterator_traits<__normal_iterator<double *, vector<double, allocator<double> > > >::reference> > _t5 = {};
1304+
// CHECK-NEXT: clad::tape<int> _t6 = {};
1305+
// CHECK-NEXT: double _d_sum = 0.;
1306+
// CHECK-NEXT: double sum = 0;
1307+
// CHECK-NEXT: int _d_u = 0;
1308+
// CHECK-NEXT: int u = 1;
1309+
// CHECK-NEXT: unsigned long _t0 = 0UL;
1310+
// CHECK-NEXT: clad::ValueAndAdjoint<{{.*}}> _t1 = clad::custom_derivatives::class_functions::constructor_reverse_forw(clad::ConstructorReverseForwTag<__normal_iterator<double *, vector<double, allocator<double> > > >(), start, (*_d_start));
1311+
// CHECK-NEXT: it = _t1.value;
1312+
// CHECK-NEXT: _d_it = _t1.adjoint;
1313+
// CHECK-NEXT: for (;; clad::push(_t2, it) , {{.*}}class_functions::operator_plus_plus_reverse_forw(&it, 0, &_d_it, 0)) {
1314+
// CHECK-NEXT: {
1315+
// CHECK-NEXT: if (!operator!=(it, end))
1316+
// CHECK-NEXT: break;
1317+
// CHECK-NEXT: }
1318+
// CHECK-NEXT: _t0++;
1319+
// CHECK-NEXT: clad::push(_t3, sum);
1320+
// CHECK-NEXT: clad::push(_t5, {{.*}}class_functions::operator_star_reverse_forw(&it, &_d_it));
1321+
// CHECK-NEXT: sum += u * clad::push(_t4, clad::back(_t5).value);
1322+
// CHECK-NEXT: clad::push(_t6, u);
1323+
// CHECK-NEXT: u += 2;
1324+
// CHECK-NEXT: }
1325+
// CHECK-NEXT: _d_sum += 1;
1326+
// CHECK-NEXT: {
1327+
// CHECK-NEXT: for (;; _t0--) {
1328+
// CHECK-NEXT: {
1329+
// CHECK-NEXT: if (!_t0)
1330+
// CHECK-NEXT: break;
1331+
// CHECK-NEXT: }
1332+
// CHECK-NEXT: {
1333+
// CHECK-NEXT: int _r0 = 0;
1334+
// CHECK-NEXT: it = clad::back(_t2);
1335+
// CHECK-NEXT: {{.*}}class_functions::operator_plus_plus_pullback(&it, 0, {}, &_d_it, &_r0);
1336+
// CHECK-NEXT: clad::pop(_t2);
1337+
// CHECK-NEXT: }
1338+
// CHECK-NEXT: {
1339+
// CHECK-NEXT: u = clad::pop(_t6);
1340+
// CHECK-NEXT: int _r_d1 = _d_u;
1341+
// CHECK-NEXT: }
1342+
// CHECK-NEXT: {
1343+
// CHECK-NEXT: sum = clad::pop(_t3);
1344+
// CHECK-NEXT: double _r_d0 = _d_sum;
1345+
// CHECK-NEXT: _d_u += _r_d0 * clad::pop(_t4);
1346+
// CHECK-NEXT: it.operator_star_pullback(u * _r_d0, &_d_it);
1347+
// CHECK-NEXT: clad::pop(_t5);
1348+
// CHECK-NEXT: }
1349+
// CHECK-NEXT: }
1350+
// CHECK-NEXT: {{.*}}constructor_pullback(start, &_d_it, &(*_d_start));
1351+
// CHECK-NEXT: }
1352+
// CHECK-NEXT: }
1353+
12721354
// CHECK: void fn18_grad_2(const Session *session, const float *tensor_x, float *tensor_theory_params, float *_d_tensor_theory_params) {
12731355
// CHECK-NEXT: int _d_id = 0;
12741356
// CHECK-NEXT: int id = 0;

0 commit comments

Comments
 (0)