TMB Documentation  v1.9.11
checkpoint_macro.hpp
1 // Copyright (C) 2013-2015 Kasper Kristensen
2 // License: GPL-2
3 
4 /*
5  Given function f0. Define recursively higher order reverse
6  mode derivatives:
7 
8  f0: R^(n) -> R^(m) ( x -> f0 (x) )
9  f1: R^(n+m) -> R^(n) ( (x,w1) -> f0'(x)*w1 )
10  f2: R^(n+m+n) -> R^(n+m) ( (x,w1,w2) -> f1'(x)*w2 )
11  f3: R^(n+m+n+n+m) -> R^(n+m+n) ( (x,w1,w2,w3) -> f2'(x)*w3 )
12 
13  1. We define a 'generalized symbol' to represent all of these.
14  _Reverse_mode_AD_ is trivially obtained for this symbol by calling
15  itself on a higher level. Each occurance on the tape will occupy
16  O(n+m) memory units independent on the number of flops performed
17  by f0.
18 
19  2. _Double_versions_ of the generalized symbol are obtained using
20  nested AD types to tape f0, then recursively tape forward and
21  reverse mode sweeps.
22 
23  Finally, given (1) and (2) the macro TMB_ATOMIC_VECTOR_FUNCTION
24  will generate the atomic symbol.
25 */
26 
27 /* general helper functions */
28 #ifdef CPPAD_FRAMEWORK
29 namespace atomic{
61  template <class Base, class Func>
62  CppAD::ADFun<Base>* generate_tape(Func f, vector<double> x_){
63  std::cout << "Generating tape\n";
64  int n=x_.size();
65  vector<AD<Base> > x(n);
66  for(int i=0;i<n;i++)x[i]=AD<Base>(x_[i]);
67  CppAD::Independent(x);
68  vector<AD<Base> > y=f(x);
69  vector<AD<Base> > y2(y.size());
70  for(int i=0;i<y.size();i++)y2[i]=y[i];
71  CppAD::ADFun<Base>* padf=new CppAD::ADFun<Base>(x,y2);
72  return padf;
73  }
78  template <class Base>
79  CppAD::ADFun<Base>* forrev(CppAD::ADFun<AD<Base> >* padf, vector<double> x_){
80  size_t n=padf->Domain();
81  size_t m=padf->Range();
82  vector<AD<Base> > x(n+m);
83  vector<AD<Base> > y(n);
84  for(int i=0;i<x_.size();i++)x[i]=AD<Base>(x_[i]);
85  for(int i=x_.size();i<x.size();i++)x[i]=AD<Base>(0);
86  vector<AD<Base> > tmp1(n);
87  vector<AD<Base> > tmp2(m);
88  CppAD::Independent(x);
89  for(size_t i=0;i<n;i++)tmp1[i]=x[i];
90  for(size_t i=0;i<m;i++)tmp2[i]=x[i+n];
91  padf->Forward(0,tmp1);
92  y = padf->Reverse(1,tmp2);
93  CppAD::ADFun<Base>* padf2=new CppAD::ADFun<Base>(x,y);
94  delete padf;
95  return padf2;
96  }
98  template <class ADBase>
99  CppAD::ADFun<double>* multi_forrev(CppAD::ADFun<ADBase>* padf, vector<double> x_){
100  return multi_forrev(forrev(padf, x_), x_);
101  }
102  template <>
103  CppAD::ADFun<double>* multi_forrev<double>(CppAD::ADFun<double>* padf, vector<double> x_) CSKIP({
104  return padf;
105  })
107  template<class Func>
108  CppAD::ADFun<double>* tape_symbol(Func f, vector<double> x){
109  typedef typename Func::ScalarType::value_type Base;
110  CppAD::ADFun<Base>* f0=generate_tape<Base>(f,x);
111  CppAD::ADFun<double>* fn=multi_forrev(f0,x);
112  return fn;
113  }
114 #ifdef _OPENMP
115 #define NTHREADS config.nthreads
116 #define THREAD omp_get_thread_num()
117 #else
118 #define NTHREADS 1
119 #define THREAD 0
120 #endif
121 
123  template<template<class> class UserFunctor>
124  struct forrev_derivatives{
125  bool initialized;
126  int n,m;
127  forrev_derivatives(){
128  initialized=false;
129  }
130  /* ADFun pointers used by the double versions
131  indexed as vpf[thread][level] */
132  CppAD::vector<CppAD::vector<CppAD::ADFun<double>* > > vpf;
133  void cpyADfunPointer(CppAD::ADFun<double>* padf, int i){
134  padf->optimize();
135  vpf[0][i] = padf;
136  /* Copy object for other threads */
137  for(int thread=1;thread<NTHREADS;thread++){
138  vpf[thread][i]=new CppAD::ADFun<double>();
139  vpf[thread][i]->operator=(*padf);
140  }
141  }
142  void do_init(vector<double> x){
143  UserFunctor<double> f;
144  n=x.size();
145  m=f(x).size();
146  UserFunctor<AD<double> > f0;
147  UserFunctor<AD<AD<double> > > f1;
148  UserFunctor<AD<AD<AD<double> > > > f2;
149  UserFunctor<AD<AD<AD<AD<double> > > > > f3;
150  vpf.resize(NTHREADS);
151  for(int thread=0;thread<NTHREADS;thread++){
152  vpf[thread].resize(4);
153  }
154  cpyADfunPointer(tape_symbol(f0,x), 0);
155  cpyADfunPointer(tape_symbol(f1,x), 1);
156  cpyADfunPointer(tape_symbol(f2,x), 2);
157  cpyADfunPointer(tape_symbol(f3,x), 3);
158  }
159  void init(vector<double> x){
160  if(!initialized){
161  do_init(x);
162  initialized=true;
163  }
164  }
165  int get_output_dim(int input_dim){
166  int output_dim=-1;
167  // Fibonacci type recursion for each 'column'
168  if (input_dim == n) output_dim = m;
169  else if (input_dim == n+m) output_dim = n;
170  else if (input_dim == n+m+n) output_dim = n+m;
171  else if (input_dim == n+m+n+n+m) output_dim = n+m+n;
172  else Rf_error("get_output_dim failed");
173  return output_dim;
174  }
175  // Calculate level from input dimension
176  int get_level(int input_dim){
177  int level=-1;
178  if (input_dim == n) level = 0;
179  else if (input_dim == n+m) level = 1;
180  else if (input_dim == n+m+n) level = 2;
181  else if (input_dim == n+m+n+n+m) level = 3;
182  else Rf_error("get_level failed");
183  return level;
184  }
185  // Evaluate
186  CppAD::vector<double> operator()(CppAD::vector<double> tx){
187  int level = get_level(tx.size());
188  return vpf[THREAD][level]->Forward(0,tx);
189  }
190  }; /* end class forrev_derivatives */
191 #undef NTHREADS
192 #undef THREAD
193 
196 #define REGISTER_ATOMIC(USERFUNCTION) \
197 namespace USERFUNCTION##NAMESPACE{ \
198  template<class Type> \
199  struct UserFunctor{ \
200  typedef Type ScalarType; \
201  vector<Type> operator()(vector<Type> x){ \
202  return USERFUNCTION(x); \
203  } \
204  }; \
205  atomic::forrev_derivatives<UserFunctor> double_version; \
206  TMB_ATOMIC_VECTOR_FUNCTION( \
207  generalized_symbol \
208  , \
209  double_version.get_output_dim(tx.size()) \
210  , \
211  ty = double_version(tx); \
212  , \
213  CppAD::vector<Type> concat(tx.size() + py.size()); \
214  for(size_t i=0; i < tx.size(); i++) concat[i] = tx[i]; \
215  for(size_t i=0; i < py.size(); i++) concat[tx.size()+i] = py[i]; \
216  px = generalized_symbol(concat); \
217  ) \
218  template<class Base> \
219  vector<Base> generalized_symbol(vector<Base> x){ \
220  CppAD::vector<Base> xx(x.size()); \
221  for(int i=0;i<x.size();i++)xx[i]=x[i]; \
222  CppAD::vector<Base> yy=generalized_symbol(xx); \
223  vector<Base> y(yy.size()); \
224  for(int i=0;i<y.size();i++)y[i]=yy[i]; \
225  return y; \
226  } \
227 } \
228 vector<double> USERFUNCTION(vector<double> x){ \
229  USERFUNCTION##NAMESPACE::double_version.init(x); \
230  return USERFUNCTION##NAMESPACE::generalized_symbol(x); \
231 } \
232 vector<AD<double> > USERFUNCTION(vector<AD<double> > x){ \
233  return USERFUNCTION##NAMESPACE::generalized_symbol(x); \
234 } \
235 vector<AD<AD<double> > > USERFUNCTION(vector<AD<AD<double> > > x){ \
236  return USERFUNCTION##NAMESPACE::generalized_symbol(x); \
237 } \
238 vector<AD<AD<AD<double> > > > USERFUNCTION(vector<AD<AD<AD<double> > > > x){ \
239  return USERFUNCTION##NAMESPACE::generalized_symbol(x); \
240 }
241 
245 } /* end namespace atomic */
246 #endif // CPPAD_FRAMEWORK
247 
248 #ifdef TMBAD_FRAMEWORK
249 namespace atomic {
250 
266 template<class Functor>
267 struct AtomicLocal {
269  Functor F;
270  TMBad::ADFun<> Tape;
271  AtomicLocal(const Functor &F) : F(F) {}
272  template<class Type>
273  vector<Type> operator()(const vector<Type> &x) {
274  if ( (size_t) x.size() != Tape.Domain() ) {
275  Tape = TMBad::ADFun<>( StdWrapFunctor(F), x).atomic();
276  }
277  std::vector<Type> x_(x.data(), x.data() + x.size());
278  std::vector<Type> y_ = Tape(x_);
279  vector<Type> y(y_);
280  return y;
281  }
282  vector<double> operator()(const vector<double> &x) {
283  return F(x);
284  }
285 };
286 
294 template<class Functor>
295 struct AtomicGlobal {
296 #ifdef _OPENMP
297 #define NTHREADS config.nthreads
298 #define THREAD omp_get_thread_num()
299 #else
300 #define NTHREADS 1
301 #define THREAD 0
302 #endif
303  std::vector< AtomicLocal<Functor> >* p_;
304  AtomicGlobal() {
305  static std::vector< AtomicLocal<Functor> >* p =
306  new std::vector< AtomicLocal<Functor> > (NTHREADS, Functor() );
307  p_ = p;
308  }
309  template<class Type>
310  vector<Type> operator()(const vector<Type> &x) {
311  return ((*p_)[THREAD])(x);
312  }
313 #undef NTHREADS
314 #undef THREAD
315 };
316 
317 #define REGISTER_ATOMIC(USERFUNCTION) \
318  namespace USERFUNCTION##NAMESPACE { \
319  template<class Type> \
320  struct UserFunctor { \
321  typedef Type ScalarType; \
322  vector<Type> operator()(const vector<Type> &x) { \
323  return USERFUNCTION(x); \
324  } \
325  }; \
326  } \
327  vector<double> USERFUNCTION(const vector<double> &x) { \
328  typedef USERFUNCTION##NAMESPACE::UserFunctor<double> Functor; \
329  return atomic::AtomicGlobal<Functor>()(x); \
330  } \
331  vector<TMBad::ad_aug> USERFUNCTION(const vector<TMBad::ad_aug> &x) { \
332  typedef USERFUNCTION##NAMESPACE::UserFunctor<TMBad::ad_aug> Functor; \
333  return atomic::AtomicGlobal<Functor>()(x); \
334  }
335 
336 } // End namespace atomic
337 
338 #endif // TMBAD_FRAMEWORK
Vector class used by TMB.
Definition: vector.hpp:17
For backwards compatibility with CppAD.
Namespace with special functions and derivatives.
Interoperability with other vector classes.
Definition: TMBad.hpp:57
User interface to checkpointing using TMBad.
License: GPL v2