Prev Next atomic_mul_level.cpp

@(@\newcommand{\W}[1]{ \; #1 \; } \newcommand{\R}[1]{ {\rm #1} } \newcommand{\B}[1]{ {\bf #1} } \newcommand{\D}[2]{ \frac{\partial #1}{\partial #2} } \newcommand{\DD}[3]{ \frac{\partial^2 #1}{\partial #2 \partial #3} } \newcommand{\Dpow}[2]{ \frac{\partial^{#1}}{\partial {#2}^{#1}} } \newcommand{\dpow}[2]{ \frac{ {\rm d}^{#1}}{{\rm d}\, {#2}^{#1}} }@)@
Atomic Operations and Multiple-Levels of AD: Example and Test

Discussion
One can use checkpoint or atomic_base to code an AD<Base> operation as atomic. This means that derivative computations that use the type Base will call the corresponding atomic_base member functions. On the other hand, if Base is AD<Other> the operations recorded at the Base level will not be atomic. This is demonstrated in this example.

# include <cppad/cppad.hpp>

namespace {
     using CppAD::AD;
     typedef AD<double>                      a1double;
     typedef AD<a1double>                    a2double;
     typedef CPPAD_TESTVECTOR(a1double)      a1vector;
     typedef CPPAD_TESTVECTOR(a2double)      a2vector;

     void f_algo(const a2vector& x, a2vector& y)
     {     size_t n = x.size();
          y[0] = 0.0;
          for(size_t j = 1; j < n; j++)
               y[0] += x[j-1] * x[j];
          return;
     }
}
//
bool mul_level(void)
{     bool ok = true;
     using CppAD::checkpoint;
     using CppAD::ADFun;
     using CppAD::Independent;

     // domain dimension for this problem
     size_t n = 10;
     size_t m = 1;

     // checkpoint version of the function F(x)
     a2vector a2x(n), a2y(m);
     for(size_t j = 0; j < n; j++)
          a2x[j] = a2double(j + 1);
     //
     // could also use bool_sparsity_enum or set_sparsity_enum
     checkpoint<a1double> atom_f("atom_f", f_algo, a2x, a2y);
     //
     // Record a version of y = f(x) without checkpointing
     Independent(a2x);
     f_algo(a2x, a2y);
     ADFun<a1double> check_not(a2x, a2y);
     //
     // number of variables in a tape of f_algo that does not use checkpointing
     size_t size_not = check_not.size_var();
     //
     // Record a version of y = f(x) with checkpointing
     Independent(a2x);
     atom_f(a2x, a2y);
     ADFun<a1double> check_yes(a2x, a2y);
     //
     // f_algo is represented by one atomic operation in this tape
     ok &= check_yes.size_var() < size_not;
     //
     // now record operations at a1double level
     a1vector a1x(n), a1y(m);
     for(size_t j = 0; j < n; j++)
          a1x[j] = a1double(j + 1);
     //
     // without checkpointing
     Independent(a1x);
     a1y = check_not.Forward(0, a1x);
     ADFun<double> with_not(a1x, a1y);
     //
     // should have the same size
     ok &= with_not.size_var() == size_not;
     //
     // with checkpointing
     Independent(a1x);
     a1y = check_yes.Forward(0, a1x);
     ADFun<double> with_yes(a1x, a1y);
     //
     // f_algo is nolonger represented by one atomic operation in this tape
     ok &= with_yes.size_var() == size_not;
     //
     return ok;
}

Input File: example/atomic/mul_level.cpp