diff --git a/include/clad/Differentiator/STLBuiltins.h b/include/clad/Differentiator/STLBuiltins.h index 11192b16d..fc240d11e 100644 --- a/include/clad/Differentiator/STLBuiltins.h +++ b/include/clad/Differentiator/STLBuiltins.h @@ -12,6 +12,8 @@ namespace clad { namespace custom_derivatives { namespace class_functions { +// vector forward mode + template void clear_pushforward(::std::vector* v, ::std::vector* d_v) { d_v->clear(); @@ -131,6 +133,181 @@ operator_subscript_pushforward(const ::std::vector* v, unsigned idx, return {(*v)[idx], (*d_v)[idx]}; } +template +ValueAndPushforward at_pushforward(::std::vector* v, unsigned idx, + ::std::vector* d_v, + unsigned d_idx) { + return {(*v)[idx], (*d_v)[idx]}; +} + +template +ValueAndPushforward +at_pushforward(const ::std::vector* v, unsigned idx, + const ::std::vector* d_v, unsigned d_idx) { + return {(*v)[idx], (*d_v)[idx]}; +} + +template +clad::ValueAndPushforward<::std::vector&, ::std::vector&> +operator_equal_pushforward(::std::vector* a, const ::std::vector& param, + ::std::vector* d_a, + const ::std::vector& d_param) noexcept { + (*a) = param; + (*d_a) = d_param; + return {*a, *d_a}; +} + +template +inline clad::ValueAndPushforward +front_pushforward(const ::std::vector* a, + const ::std::vector* d_a) noexcept { + return {a->front(), d_a->front()}; +} + +template +inline clad::ValueAndPushforward +front_pushforward(::std::vector* a, ::std::vector* d_a) noexcept { + return {a->front(), d_a->front()}; +} + +template +inline clad::ValueAndPushforward +back_pushforward(const ::std::vector* a, + const ::std::vector* d_a) noexcept { + return {a->back(), d_a->back()}; +} + +template +inline clad::ValueAndPushforward +back_pushforward(::std::vector* a, ::std::vector* d_a) noexcept { + return {a->back(), d_a->back()}; +} + +template +ValueAndPushforward::iterator, + typename ::std::vector::iterator> +begin_pushforward(::std::vector* v, ::std::vector* d_v) { + return {v->begin(), d_v->begin()}; +} + +template +ValueAndPushforward::iterator, + typename ::std::vector::iterator> +end_pushforward(::std::vector* v, ::std::vector* d_v) { + return {v->end(), d_v->end()}; +} + +template +ValueAndPushforward::iterator, + typename ::std::vector::iterator> +erase_pushforward(::std::vector* v, + typename ::std::vector::const_iterator pos, + ::std::vector* d_v, + typename ::std::vector::const_iterator d_pos) { + return {v->erase(pos), d_v->erase(d_pos)}; +} + +template +ValueAndPushforward::iterator, + typename ::std::vector::iterator> +insert_pushforward(::std::vector* v, + typename ::std::vector::const_iterator pos, U u, + ::std::vector* d_v, + typename ::std::vector::const_iterator d_pos, U d_u) { + return {v->insert(pos, u), d_v->insert(d_pos, d_u)}; +} + +template +ValueAndPushforward::iterator, + typename ::std::vector::iterator> +insert_pushforward(::std::vector* v, + typename ::std::vector::const_iterator pos, + ::std::initializer_list list, ::std::vector* d_v, + typename ::std::vector::const_iterator d_pos, + ::std::initializer_list d_list) { + return {v->insert(pos, list), d_v->insert(d_pos, d_list)}; +} + +template +ValueAndPushforward::iterator, + typename ::std::vector::iterator> +insert_pushforward(::std::vector* v, + typename ::std::vector::const_iterator pos, U first, + U last, ::std::vector* d_v, + typename ::std::vector::const_iterator d_pos, U d_first, + U d_last) { + return {v->insert(pos, first, last), d_v->insert(d_pos, d_first, d_last)}; +} + +template +void assign_pushforward(::std::vector* v, + typename ::std::vector::size_type n, const U& val, + ::std::vector* d_v, + typename ::std::vector::size_type /*d_n*/, + const U& d_val) { + v->assign(n, val); + d_v->assign(n, d_val); +} + +template +void assign_pushforward(::std::vector* v, U first, U last, + ::std::vector* d_v, U d_first, U d_last) { + v->assign(first, last); + d_v->assign(d_first, d_last); +} + +template +void assign_pushforward(::std::vector* v, ::std::initializer_list list, + ::std::vector* d_v, + ::std::initializer_list d_list) { + v->assign(list); + d_v->assign(d_list); +} + +template +void reserve_pushforward(::std::vector* v, + typename ::std::vector::size_type n, + ::std::vector* d_v, + typename ::std::vector::size_type /*d_n*/) { + v->reserve(n); + d_v->reserve(n); +} + +template +void shrink_to_fit_pushforward(::std::vector* v, ::std::vector* d_v) { + v->shrink_to_fit(); + d_v->shrink_to_fit(); +} + +template +void push_back_pushforward(::std::vector* v, U val, ::std::vector* d_v, + U d_val) { + v->push_back(val); + d_v->push_back(d_val); +} + +template +void pop_back_pushforward(::std::vector* v, ::std::vector* d_v) noexcept { + v->pop_back(); + d_v->pop_back(); +} + +template +clad::ValueAndPushforward<::std::size_t, ::std::size_t> +size_pushforward(const ::std::vector* v, + const ::std::vector* d_v) noexcept { + return {v->size(), 0}; +} + +template +clad::ValueAndPushforward<::std::size_t, ::std::size_t> +capacity_pushforward(const ::std::vector* v, + const ::std::vector* d_v) noexcept { + return {v->capacity(), 0}; +} + +// array forward mode + template constexpr clad::ValueAndPushforward operator_subscript_pushforward(::std::array* a, ::std::size_t i, @@ -198,13 +375,23 @@ back_pushforward(::std::array* a, ::std::array* d_a) noexcept { return {a->back(), d_a->back()}; } -template -void fill_pushforward(::std::array* a, const T& u, - ::std::array* d_a, const T& d_u) { +template +void fill_pushforward(::std::array* a, const U& u, + ::std::array* d_a, const U& d_u) { a->fill(u); d_a->fill(d_u); } +template +clad::ValueAndPushforward<::std::size_t, ::std::size_t> +size_pushforward(const ::std::array* a, + const ::std::array* d_a) noexcept { + return {a->size(), 0}; +} + +// vector reverse mode +// more can be found in tests: test/Gradient/STLCustomDerivatives.C + template void push_back_reverse_forw(::std::vector* v, U val, ::std::vector* d_v, U d_val) { @@ -256,6 +443,8 @@ void constructor_pullback(::std::vector* v, S count, U val, d_v->clear(); } +// array reverse mode + template clad::ValueAndAdjoint operator_subscript_reverse_forw( ::std::array* arr, typename ::std::array::size_type idx, @@ -341,6 +530,8 @@ void constructor_pullback(::std::array* a, const ::std::array& arr, (*d_arr)[i] += (*d_a)[i]; } +// tuple forward mode + template clad::ValueAndPushforward<::std::tuple, ::std::tuple> operator_equal_pushforward(::std::tuple* tu, @@ -356,6 +547,8 @@ operator_equal_pushforward(::std::tuple* tu, namespace std { +// tie and maketuple forward mode + // Helper functions for selecting subtuples template <::std::size_t shift_amount, ::std::size_t... Is> constexpr auto shift_sequence(IndexSequence) { diff --git a/test/ForwardMode/STLCustomDerivatives.C b/test/ForwardMode/STLCustomDerivatives.C index 7d441fa0f..7ee45affa 100644 --- a/test/ForwardMode/STLCustomDerivatives.C +++ b/test/ForwardMode/STLCustomDerivatives.C @@ -116,6 +116,201 @@ double fnVec4(double u, double v) { // CHECK-NEXT: return _t1.pushforward * _t4 + _t3 * _t2.pushforward; // CHECK-NEXT: } +double fnVec5(double x, double y) { + std::vector v; + + v.reserve(10); + + double res = x*v.capacity(); + + v.push_back(x); + v.shrink_to_fit(); + res += x*v.capacity(); + + return res; // 11x +} + +// CHECK: double fnVec5_darg0(double x, double y) { +// CHECK-NEXT: double _d_x = 1; +// CHECK-NEXT: double _d_y = 0; +// CHECK-NEXT: std::vector _d_v; +// CHECK-NEXT: std::vector v; +// CHECK-NEXT: {{.*}}reserve_pushforward(&v, 10, &_d_v, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t0 = {{.*}}capacity_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}} &_t1 = _t0.value; +// CHECK-NEXT: double _d_res = _d_x * _t1 + x * _t0.pushforward; +// CHECK-NEXT: double res = x * _t1; +// CHECK-NEXT: {{.*}}push_back_pushforward(&v, x, &_d_v, _d_x); +// CHECK-NEXT: {{.*}}shrink_to_fit_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t2 = {{.*}}capacity_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}} &_t3 = _t2.value; +// CHECK-NEXT: _d_res += _d_x * _t3 + x * _t2.pushforward; +// CHECK-NEXT: res += x * _t3; +// CHECK-NEXT: return _d_res; +// CHECK-NEXT: } + +double fnVec6(double x, double y) { + std::vector v(3, y); + + v.pop_back(); + double res = v.size()*x; // res = 2x + + v.erase(v.begin()); + res += v.size()*x; // res = 3x + + std::vector w; + w = v; + w.clear(); + res += w.size()*x + v.size()*x; // res = 4x + + w.insert(w.end(), 5); + res += w.size()*x; // res = 5x + + w.insert(w.end(), {y, x, y}); + w.insert(w.end(), v.begin(), v.end()); + if (w[0] == 5 && w[1] == y && w[2] == x && w[3] == y && v.back() == w.back()) { // should always be true + res += w[2]; // res = 6x + } + + w.assign(2, y); + res += (w[0] == y && w[1] == y)*x; // res = 7x + + v[0] = x; + w.assign(v.begin(), v.end()); + res += w[0]; // res = 8x; + + w.assign({3*x, 2*x, 4*x}); + res += w[1]; // res = 10x; + + return res; // 10x +} + +// CHECK: double fnVec6_darg0(double x, double y) { +// CHECK-NEXT: double _d_x = 1; +// CHECK-NEXT: double _d_y = 0; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector, ::std::vector > _t0 = {{.*}}constructor_pushforward(clad::ConstructorPushforwardTag >(), 3, y{{.*}}, 0, _d_y{{.*}}); +// CHECK-NEXT: std::vector _d_v(_t0.pushforward); +// CHECK-NEXT: std::vector v(_t0.value); +// CHECK-NEXT: {{.*}}pop_back_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t1 = {{.*}}size_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}} &_t2 = _t1.value; +// CHECK-NEXT: double _d_res = _t1.pushforward * x + _t2 * _d_x; +// CHECK-NEXT: double res = _t2 * x; +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t3 = {{.*}}begin_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t4 = {{.*}}erase_pushforward(&v, {{.*}}_t3.value{{.*}}, &_d_v, {{.*}}_t3.pushforward{{.*}}); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t5 = {{.*}}size_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}} &_t6 = _t5.value; +// CHECK-NEXT: _d_res += _t5.pushforward * x + _t6 * _d_x; +// CHECK-NEXT: res += _t6 * x; +// CHECK-NEXT: std::vector _d_w; +// CHECK-NEXT: std::vector w; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::vector &, ::std::vector &> _t7 = {{.*}}operator_equal_pushforward(&w, v, &_d_w, _d_v); +// CHECK-NEXT: {{.*}}clear_pushforward(&w, &_d_w); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t8 = {{.*}}size_pushforward(&w, &_d_w); +// CHECK-NEXT: {{.*}} &_t9 = _t8.value; +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t10 = {{.*}}size_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}} &_t11 = _t10.value; +// CHECK-NEXT: _d_res += _t8.pushforward * x + _t9 * _d_x + _t10.pushforward * x + _t11 * _d_x; +// CHECK-NEXT: res += _t9 * x + _t11 * x; +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t12 = {{.*}}end_pushforward(&w, &_d_w); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t13 = {{.*}}insert_pushforward(&w, {{.*}}_t12.value{{.*}}, 5, &_d_w, {{.*}}_t12.pushforward{{.*}}, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t14 = {{.*}}size_pushforward(&w, &_d_w); +// CHECK-NEXT: {{.*}} &_t15 = _t14.value; +// CHECK-NEXT: _d_res += _t14.pushforward * x + _t15 * _d_x; +// CHECK-NEXT: res += _t15 * x; +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t16 = {{.*}}end_pushforward(&w, &_d_w); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t17 = {{.*}}insert_pushforward(&w, {{.*}}_t16.value{{.*}}, {y, x, y}, &_d_w, {{.*}}_t16.pushforward{{.*}}, {_d_y, _d_x, _d_y}); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t18 = {{.*}}end_pushforward(&w, &_d_w); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t19 = {{.*}}begin_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t20 = {{.*}}end_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t21 = {{.*}}insert_pushforward(&w, {{.*}}_t18.value{{.*}}, {{.*}}_t19.value{{.*}}, {{.*}}_t20.value{{.*}}, &_d_w, {{.*}}_t18.pushforward{{.*}}, {{.*}}_t19.pushforward{{.*}}, {{.*}}_t20.pushforward{{.*}}); +// CHECK-NEXT: if (w[0] == 5 && w[1] == y && w[2] == x && w[3] == y && v.back() == w.back()) { +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t22 = {{.*}}operator_subscript_pushforward(&w, 2, &_d_w, 0); +// CHECK-NEXT: _d_res += _t22.pushforward; +// CHECK-NEXT: res += _t22.value; +// CHECK-NEXT: } +// CHECK-NEXT: {{.*}}assign_pushforward(&w, 2, y, &_d_w, 0, _d_y); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t23 = {{.*}}operator_subscript_pushforward(&w, 0, &_d_w, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t24 = {{.*}}operator_subscript_pushforward(&w, 1, &_d_w, 0); +// CHECK-NEXT: bool _t25 = ((_t23.value == y) && (_t24.value == y)); +// CHECK-NEXT: _d_res += false * x + _t25 * _d_x; +// CHECK-NEXT: res += _t25 * x; +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t26 = {{.*}}operator_subscript_pushforward(&v, 0, &_d_v, 0); +// CHECK-NEXT: _t26.pushforward = _d_x; +// CHECK-NEXT: _t26.value = x; +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t27 = {{.*}}begin_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t28 = {{.*}}end_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}assign_pushforward(&w, {{.*}}_t27.value{{.*}}, {{.*}}_t28.value{{.*}}, &_d_w, {{.*}}_t27.pushforward{{.*}}, {{.*}}_t28.pushforward{{.*}}); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t29 = {{.*}}operator_subscript_pushforward(&w, 0, &_d_w, 0); +// CHECK-NEXT: _d_res += _t29.pushforward; +// CHECK-NEXT: res += _t29.value; +// CHECK-NEXT: {{.*}}assign_pushforward(&w, {3 * x, 2 * x, 4 * x}, &_d_w, {0 * x + 3 * _d_x, 0 * x + 2 * _d_x, 0 * x + 4 * _d_x}); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t30 = {{.*}}operator_subscript_pushforward(&w, 1, &_d_w, 0); +// CHECK-NEXT: _d_res += _t30.pushforward; +// CHECK-NEXT: res += _t30.value; +// CHECK-NEXT: return _d_res; +// CHECK-NEXT: } + +double fnVec7(double x, double y) { + std::vector v; + for (size_t i = 0; i < 3; ++i) { + float fx = x; + v.push_back(fx); + } + double res = 0; + for (size_t i = 0; i < v.size(); ++i) { + v[i] = i * v.at(i); + res += v.at(i); + } + + const std::vector v2 = v; + + // result is the same as res, that is: 3x + return res + v.front() + v.back() - v[v.size()-1] + v2.at(0) + v2.front() + v2.back() - v2[v2.size()-1]; +} + +// CHECK: double fnVec7_darg0(double x, double y) { +// CHECK-NEXT: double _d_x = 1; +// CHECK-NEXT: double _d_y = 0; +// CHECK-NEXT: std::vector _d_v; +// CHECK-NEXT: std::vector v; +// CHECK-NEXT: { +// CHECK-NEXT: size_t _d_i = 0; +// CHECK-NEXT: for (size_t i = 0; i < 3; ++i) { +// CHECK-NEXT: float _d_fx = _d_x; +// CHECK-NEXT: float fx = x; +// CHECK-NEXT: {{.*}}push_back_pushforward(&v, static_cast(fx), &_d_v, static_cast(_d_fx)); +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: double _d_res = 0; +// CHECK-NEXT: double res = 0; +// CHECK-NEXT: { +// CHECK-NEXT: size_t _d_i = 0; +// CHECK-NEXT: for (size_t i = 0; i < v.size(); ++i) { +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t0 = {{.*}}operator_subscript_pushforward(&v, i, &_d_v, _d_i); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t1 = {{.*}}at_pushforward(&v, i, &_d_v, _d_i); +// CHECK-NEXT: double &_t2 = _t1.value; +// CHECK-NEXT: _t0.pushforward = _d_i * _t2 + i * _t1.pushforward; +// CHECK-NEXT: _t0.value = i * _t2; +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t3 = {{.*}}at_pushforward(&v, i, &_d_v, _d_i); +// CHECK-NEXT: _d_res += _t3.pushforward; +// CHECK-NEXT: res += _t3.value; +// CHECK-NEXT: } +// CHECK-NEXT: } +// CHECK-NEXT: const std::vector _d_v2 = _d_v; +// CHECK-NEXT: const std::vector v2 = v; +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t4 = {{.*}}front_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t5 = {{.*}}back_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t6 = {{.*}}size_pushforward(&v, &_d_v); +// CHECK-NEXT: {{.*}}ValueAndPushforward<{{.*}}> _t7 = {{.*}}operator_subscript_pushforward(&v, _t6.value - 1, &_d_v, _t6.pushforward - 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t8 = {{.*}}at_pushforward(&v2, 0, &_d_v2, 0); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t9 = {{.*}}front_pushforward(&v2, &_d_v2); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t10 = {{.*}}back_pushforward(&v2, &_d_v2); +// CHECK-NEXT: {{.*}}ValueAndPushforward< ::std::size_t, ::std::size_t> _t11 = {{.*}}size_pushforward(&v2, &_d_v2); +// CHECK-NEXT: {{.*}}ValueAndPushforward _t12 = {{.*}}operator_subscript_pushforward(&v2, _t11.value - 1, &_d_v2, _t11.pushforward - 0); +// CHECK-NEXT: return _d_res + _t4.pushforward + _t5.pushforward - _t7.pushforward + _t8.pushforward + _t9.pushforward + _t10.pushforward - _t12.pushforward; +// CHECK-NEXT: } + double fnArr1(double x) { std::array a; a.fill(x); @@ -214,6 +409,9 @@ int main() { INIT_DIFFERENTIATE(fnVec2, "u"); INIT_DIFFERENTIATE(fnVec3, "u"); INIT_DIFFERENTIATE(fnVec4, "u"); + INIT_DIFFERENTIATE(fnVec5, "x"); + INIT_DIFFERENTIATE(fnVec6, "x"); + INIT_DIFFERENTIATE(fnVec7, "x"); INIT_DIFFERENTIATE(fnArr1, "x"); INIT_DIFFERENTIATE(fnArr2, "x"); INIT_DIFFERENTIATE(fnTuple1, "x"); @@ -222,6 +420,9 @@ int main() { TEST_DIFFERENTIATE(fnVec2, 3, 5); // CHECK-EXEC: {5.00} TEST_DIFFERENTIATE(fnVec3, 3, 5); // CHECK-EXEC: {2.00} TEST_DIFFERENTIATE(fnVec4, 3, 5); // CHECK-EXEC: {30.00} + TEST_DIFFERENTIATE(fnVec5, 3, 4); // CHECK-EXEC: {11.00} + TEST_DIFFERENTIATE(fnVec6, 3, 4); // CHECK-EXEC: {10.00} + TEST_DIFFERENTIATE(fnVec7, 3, 4); // CHECK-EXEC: {3.00} TEST_DIFFERENTIATE(fnArr1, 3); // CHECK-EXEC: {3.00} TEST_DIFFERENTIATE(fnArr2, 3); // CHECK-EXEC: {108.00} TEST_DIFFERENTIATE(fnTuple1, 3, 4); // CHECK-EXEC: {2.00}