TMB Documentation  v1.9.11
atomic_math.hpp
1 // Copyright (C) 2013-2015 Kasper Kristensen
2 // License: GPL-2
3 
24 namespace atomic {
29 namespace Rmath {
30  /*
31  Was:
32 
33  #include <Rmath.h>
34 
35  However, Rmath defines a large number of macros that do not
36  respect the namespace limits. Some of them will conflict with
37  other TMB functions or mess up symbols in the users template
38  (e.g. change 'dt' to 'Rf_dt').
39 
40  Therefore, explicitly select the few function headers (from
41  /usr/share/R/include/Rmath.h) rather than including them all.
42  */
43 
44  extern "C" {
45  /* See 'R-API: entry points to C-code' (Writing R-extensions) */
46  double Rf_pnorm5(double, double, double, int, int);
47  double Rf_qnorm5(double, double, double, int, int);
48  double Rf_ppois(double, double, int, int);
49  double Rf_bessel_k(double, double, double);
50  double Rf_bessel_i(double, double, double);
51  double Rf_pgamma(double, double, double, int, int);
52  double Rf_qgamma(double, double, double, int, int);
53  double Rf_lgammafn(double);
54  double Rf_psigamma(double, double);
55  double Rf_fmin2(double, double);
56  /* Selected headers from <R_ext/Applic.h> */
57  typedef void integr_fn(double *x, int n, void *ex);
58  void Rdqags(integr_fn f, void *ex, double *a, double *b,
59  double *epsabs, double *epsrel,
60  double *result, double *abserr, int *neval, int *ier,
61  int *limit, int *lenw, int *last, int *iwork, double *work);
62  void Rdqagi(integr_fn f, void *ex, double *bound, int *inf,
63  double *epsabs, double *epsrel,
64  double *result, double *abserr, int *neval, int *ier,
65  int *limit, int *lenw, int *last,
66  int *iwork, double *work);
67  }
68 
69  /* Non-standard TMB special functions based on numerical
70  integration: */
71 #ifdef WITH_LIBTMB
72  void integrand_D_incpl_gamma_shape(double *x, int nx, void *ex);
73  double D_incpl_gamma_shape(double x, double shape, double n, double logc);
74  double inv_incpl_gamma(double y, double shape, double logc);
75  double D_lgamma(double x, double n);
76 #else
77  void integrand_D_incpl_gamma_shape(double *x, int nx, void *ex){
78  double* parms=(double*)ex;
79  double shape = parms[0];
80  double n = parms[1];
81  double logc = parms[2];
82  for(int i=0; i<nx; i++){
83  x[i] = exp( -exp(x[i]) + shape * x[i] + logc ) * pow(x[i], n);
84  }
85  }
86  /* n'th order derivative of (scaled) incomplete gamma wrt. shape parameter */
87  double D_incpl_gamma_shape(double x, double shape, double n, double logc){
88  if(n<.5){
89  return exp(logc + Rf_lgammafn(shape)) * Rf_pgamma(x, shape, 1.0, 1, 0);
90  }
91  double epsabs=1e-10;
92  double epsrel=1e-10;
93  double result1=0;
94  double result2=0;
95  double abserr=10000;
96  int neval=10000;
97  int ier=0;
98  int limit=100;
99  int lenw = 4 * limit;
100  int last=0;
101  int* iwork = (int*)malloc(limit * sizeof(int));
102  double* work = (double*)malloc(lenw * sizeof(double));
103  double ex[3];
104  ex[0] = shape;
105  ex[1] = n;
106  ex[2] = logc; /* Scale integrand with exp(logc) */
107  double bound; /* For indefinite integration */
108  int inf=-1; /* corresponds to (-Inf, bound) */
109  bound = log(Rf_fmin2(x,shape));
110  /* integrate -Inf...min(log(x),log(shape)) */
111  Rdqagi(integrand_D_incpl_gamma_shape, ex, &bound, &inf,
112  &epsabs, &epsrel,
113  &result1, &abserr, &neval, &ier,
114  &limit, &lenw, &last, iwork, work);
115  if(ier!=0){
116 #ifndef _OPENMP
117  Rf_warning("incpl_gamma (indef) integrate unreliable: x=%f shape=%f n=%f ier=%i", x, shape, n, ier);
118 #endif
119  }
120  /* integrate min(log(x),log(shape))...log(x) */
121  if(x>shape){
122  ier = 0;
123  double a = bound;
124  double b = log(x);
125  Rdqags(integrand_D_incpl_gamma_shape, ex, &a, &b,
126  &epsabs, &epsrel,
127  &result2, &abserr, &neval, &ier,
128  &limit, &lenw, &last, iwork, work);
129  if(ier!=0){
130 #ifndef _OPENMP
131  Rf_warning("incpl_gamma (def) integrate unreliable: x=%f shape=%f n=%f ier=%i", x, shape, n, ier);
132 #endif
133  }
134  }
135  free(iwork);
136  free(work);
137  return result1 + result2;
138  }
139  double inv_incpl_gamma(double y, double shape, double logc){
140  double logp = log(y) - Rf_lgammafn(shape) - logc;
141  double scale = 1.0;
142  return Rf_qgamma(exp(logp), shape, scale, 1, 0);
143  }
144  /* n'th order derivative of log gamma function */
145  double D_lgamma(double x, double n){
146  if(n<.5)return Rf_lgammafn(x);
147  else return Rf_psigamma(x,n-1.0);
148  }
149 #endif // #ifdef WITH_LIBTMB
150 
151 }
152 
153 #ifdef CPPAD_FRAMEWORK
154 #include "atomic_macro.hpp"
155 #endif
156 
157 #ifdef TMBAD_FRAMEWORK
158 #include "tmbad_atomic_macro.hpp"
159 #endif
160 
161 template<class Type>
162 struct TypeDefs{
163  /* The following typedefs allow us to construct matrices based on
164  already allocated memory (pointers). 'MapMatrix' gives write access
165  to the pointers while 'ConstMapMatrix' is read-only.
166  */
167  typedef Eigen::Map<Eigen::Matrix<Type,Eigen::Dynamic,Eigen::Dynamic> > MapMatrix;
168  typedef Eigen::Map<const Eigen::Matrix<Type,Eigen::Dynamic,Eigen::Dynamic> > ConstMapMatrix;
169  typedef Eigen::LDLT<Eigen::Matrix<Type, Eigen::Dynamic, Eigen::Dynamic> > LDLT;
170 };
171 
178 template<class Type>
179 typename TypeDefs<Type>::ConstMapMatrix vec2mat(const CppAD::vector<Type> &x, int m, int n, int offset=0){
180  typedef typename TypeDefs<Type>::ConstMapMatrix ConstMapMatrix_t;
181  ConstMapMatrix_t res(&x[offset], m, n);
182  return res;
183 }
184 
188 template<class Type>
189 CppAD::vector<Type> mat2vec(matrix<Type> x){
190  int n=x.size();
191  CppAD::vector<Type> res(n);
192  for(int i=0;i<n;i++)res[i]=x(i);
193  return res;
194 }
195 
199 template<class Type>
200 Type dnorm1(Type x){
201  return Type(1.0/sqrt(2.0*M_PI)) * exp(-Type(.5)*x*x);
202 }
203 
209 TMB_ATOMIC_STATIC_FUNCTION(
210  // ATOMIC_NAME
211  pnorm1
212  ,
213  // INPUT_DIM
214  1
215  ,
216  // ATOMIC_DOUBLE
217  ty[0] = Rmath::Rf_pnorm5(tx[0],0,1,1,0);
218  ,
219  // ATOMIC_REVERSE
220  px[0] = dnorm1(tx[0]) * py[0];
221  )
222 
228 TMB_ATOMIC_STATIC_FUNCTION(
229  // ATOMIC_NAME
230  qnorm1
231  ,
232  // INPUT_DIM
233  1,
234  // ATOMIC_DOUBLE
235  ty[0] = Rmath::Rf_qnorm5(tx[0],0,1,1,0);
236  ,
237  // ATOMIC_REVERSE
238  px[0] = Type(1) / dnorm1(ty[0]) * py[0];
239  )
240 
251 TMB_ATOMIC_STATIC_FUNCTION(
252  // ATOMIC_NAME
254  ,
255  // INPUT_DIM
256  4
257  ,
258  // ATOMIC_DOUBLE
259  ty[0]=Rmath::D_incpl_gamma_shape(tx[0],tx[1],tx[2],tx[3]);
260  ,
261  // ATOMIC_REVERSE
262  px[0] = exp( -tx[0] + (tx[1]-Type(1.0)) * log(tx[0]) + tx[3] ) * pow(log(tx[0]),tx[2]) * py[0];
263  Type tx_[4];
264  tx_[0] = tx[0];
265  tx_[1] = tx[1];
266  tx_[2] = tx[2] + Type(1.0); // Add one to get partial wrt. tx[1]
267  tx_[3] = tx[3];
268  px[1] = D_incpl_gamma_shape(tx_) * py[0];
269  px[2] = Type(0);
270  px[3] = ty[0] * py[0];
271  )
272 
284 TMB_ATOMIC_STATIC_FUNCTION(
285  // ATOMIC_NAME
287  ,
288  // INPUT_DIM
289  3
290  ,
291  // ATOMIC_DOUBLE
292  ty[0]=Rmath::inv_incpl_gamma(tx[0],tx[1],tx[2]);
293  ,
294  // ATOMIC_REVERSE
295  Type value = ty[0];
296  Type shape = tx[1];
297  Type logc = tx[2];
298  Type tmp = exp(-value+logc)*pow(value,shape-Type(1));
299  px[0] = 1.0 / tmp * py[0];
300  Type arg[4];
301  arg[0] = value;
302  arg[1] = shape;
303  arg[2] = Type(1); // 1st order partial wrt. shape
304  arg[3] = logc;
305  px[1] = -D_incpl_gamma_shape(arg) / tmp * py[0];
306  arg[2] = Type(0); // 0 order partial wrt. shape
307  px[2] = -D_incpl_gamma_shape(arg) / tmp * py[0];
308  )
309 
317 TMB_ATOMIC_STATIC_FUNCTION(
318  // ATOMIC_NAME
319  D_lgamma
320  ,
321  // INPUT_DIM
322  2
323  ,
324  // ATOMIC_DOUBLE
325  ty[0]=Rmath::D_lgamma(tx[0],tx[1]);
326  ,
327  // ATOMIC_REVERSE
328  Type tx_[2];
329  tx_[0]=tx[0];
330  tx_[1]=tx[1]+Type(1.0);
331  px[0] = D_lgamma(tx_) * py[0];
332  px[1] = Type(0);
333  )
334 
341 TMB_ATOMIC_STATIC_FUNCTION(
342  // ATOMIC_NAME
343  ppois
344  ,
345  // INPUT_DIM
346  2
347  ,
348  // ATOMIC_DOUBLE
349  ty[0]=Rmath::Rf_ppois(tx[0],tx[1],1,0);
350  ,
351  // ATOMIC_REVERSE
352  Type value = ty[0];
353  Type n = tx[0];
354  Type lambda = tx[1];
355  Type arg[2];
356  arg[0] = n - Type(1);
357  arg[1] = lambda;
358  px[0] = Type(0);
359  px[1] = (-value + ppois(arg)) * py[0];
360  )
361 
368 TMB_ATOMIC_STATIC_FUNCTION(
369  // ATOMIC_NAME
371  ,
372  // INPUT_DIM
373  2
374  ,
375  // ATOMIC_DOUBLE
376  ty[0] = Rmath::Rf_bessel_k(tx[0], tx[1], 1.0 /* Not scaled */);
377  ,
378  // ATOMIC_REVERSE
379  Type value = ty[0];
380  Type x = tx[0];
381  Type nu = tx[1];
382  Type arg[2];
383  arg[0] = x;
384  arg[1] = nu + Type(1);
385  px[0] = ( -bessel_k_10(arg) + value * (nu / x) ) * py[0];
386  px[1] = Type(0); /* Not implemented (!) */
387  )
388 
395 TMB_ATOMIC_STATIC_FUNCTION(
396  // ATOMIC_NAME
398  ,
399  // INPUT_DIM
400  2
401  ,
402  // ATOMIC_DOUBLE
403  ty[0] = Rmath::Rf_bessel_i(tx[0], tx[1], 1.0 /* Not scaled */);
404  ,
405  // ATOMIC_REVERSE
406  Type x = tx[0];
407  Type nu = tx[1];
408  Type arg[2];
409  arg[0] = x;
410  arg[1] = nu + Type(1);
411  Type B_right = bessel_i_10(arg);
412  arg[1] = nu - Type(1);
413  Type B_left = bessel_i_10(arg);
414  px[0] = Type(0.5) * ( B_left + B_right ) * py[0];
415  px[1] = Type(0); /* Not implemented (!) */
416 )
417 
419 template<class Type> /* Header of matmul interface */
421 template<>
423  return x*y;
424 })
436  // ATOMIC_NAME
437  matmul
438  ,
439  // OUTPUT_DIM
440  CppAD::Integer(tx[0]) * CppAD::Integer(tx[1])
441  ,
442  // ATOMIC_DOUBLE
443  typedef TypeDefs<double>::MapMatrix MapMatrix_t;
444  typedef TypeDefs<double>::ConstMapMatrix ConstMapMatrix_t;
445  int n1 = CppAD::Integer(tx[0]);
446  int n3 = CppAD::Integer(tx[1]);
447  int n2 = ( n1 + n3 > 0 ? (tx.size() - 2) / (n1 + n3) : 0 );
448  ConstMapMatrix_t X(&tx[2 ], n1, n2);
449  ConstMapMatrix_t Y(&tx[2+n1*n2], n2, n3);
450  MapMatrix_t Z(&ty[0 ], n1, n3);
451  Z = X * Y;
452  ,
453  // ATOMIC_REVERSE (W*Y^T, X^T*W)
454  typedef typename TypeDefs<Type>::MapMatrix MapMatrix_t;
455  int n1 = CppAD::Integer(tx[0]);
456  int n3 = CppAD::Integer(tx[1]);
457  int n2 = ( n1 + n3 > 0 ? (tx.size() - 2) / (n1 + n3) : 0 );
458  matrix<Type> Xt = vec2mat(tx, n1, n2, 2).transpose();
459  matrix<Type> Yt = vec2mat(tx, n2, n3, 2 + n1*n2).transpose();
460  matrix<Type> W = vec2mat(py, n1, n3);
461  MapMatrix_t res1(&px[2 ], n1, n2);
462  MapMatrix_t res2(&px[2+n1*n2], n2, n3);
463  res1 = matmul(W, Yt); // W*Y^T
464  res2 = matmul(Xt, W); // X^T*W
465  px[0] = 0; px[1] = 0;
466  )
467 
474  // ATOMIC_NAME
475  matinv
476  ,
477  // OUTPUT_DIM
478  tx.size()
479  ,
480  // ATOMIC_DOUBLE
481  typedef TypeDefs<double>::MapMatrix MapMatrix_t;
482  typedef TypeDefs<double>::ConstMapMatrix ConstMapMatrix_t;
483  int n = sqrt((double)tx.size());
484  ConstMapMatrix_t X(&tx[0], n, n);
485  MapMatrix_t Y(&ty[0], n, n);
486  Y = X.inverse(); // Use Eigen matrix inverse (LU)
487  ,
488  // ATOMIC_REVERSE (-f(X)^T*W*f(X)^T)
489  typedef typename TypeDefs<Type>::MapMatrix MapMatrix_t;
490  int n = sqrt((double)ty.size());
491  MapMatrix_t res(&px[0], n, n);
492  matrix<Type> W = vec2mat(py, n, n); // Range direction
493  matrix<Type> Y = vec2mat(ty, n, n); // f(X)
494  matrix<Type> Yt = Y.transpose(); // f(X)^T
495  matrix<Type> tmp = matmul(W, Yt); // W*f(X)^T
496  res = -matmul(Yt, tmp); // -f(X)^T*W*f(X)^T
497  )
498 
504  // ATOMIC_NAME
505  logdet
506  ,
507  // OUTPUT_DIM
508  1
509  ,
510  // ATOMIC_DOUBLE
511  int n=sqrt((double)tx.size());
512  matrix<double> X=vec2mat(tx,n,n);
513  matrix<double> LU=X.lu().matrixLU(); // Use Eigen LU decomposition
514  vector<double> LUdiag = LU.diagonal();
515  double res=LUdiag.abs().log().sum(); // TODO: currently PD only - take care of sign.
516  ty[0] = res;
517  ,
518  // ATOMIC_REVERSE (X^-1*W[0])
519  CppAD::vector<Type> invX = matinv(tx);
520  for(size_t i=0; i<tx.size(); i++) px[i] = invX[i] * py[0];
521  )
522 
529  // ATOMIC_NAME
530  invpd
531  ,
532  // OUTPUT_DIM
533  1 + tx.size()
534  ,
535  // ATOMIC_DOUBLE
536  typedef TypeDefs<double>::LDLT LDLT_t;
537  using namespace Eigen;
538  int n=sqrt((double)tx.size());
539  matrix<double> X=vec2mat(tx,n,n);
540  matrix<double> I(X.rows(),X.cols());
541  I.setIdentity();
542  LDLT_t ldlt(X);
543  matrix<double> iX = ldlt.solve(I);
544  vector<double> D = ldlt.vectorD();
545  double logdetX = D.log().sum();
546  ty[0] = logdetX;
547  for(int i=0;i<n*n;i++)ty[i+1]=iX(i);
548  ,
549  // ATOMIC_REVERSE (f2(X)*W1[0] - f2(X)^T*W2*f2(X)^T)
550  int n=sqrt((double)tx.size());
551  Type W1=py[0]; // Range direction
552  matrix<Type> W2=vec2mat(py,n,n,1); // Range direction
553  matrix<Type> Y=vec2mat(ty,n,n,1); // f2(X)
554  matrix<Type> Yt=Y.transpose(); // f2(X)^T
555  matrix<Type> tmp=matmul(W2,Yt); // W2*f2(X)^T
556  matrix<Type> res=-matmul(Yt,tmp); // -f2(X)^T*W2*f2(X)^T
557  res = res + Y*W1;
558  px=mat2vec(res);
559  )
560 
561 /* ================================== INTERFACES
562 */
563 
582 template<class Type>
584  CppAD::vector<Type> arg(2+x.size()+y.size());
585  arg[0] = x.rows(); arg[1] = y.cols();
586  for(int i=0;i<x.size();i++){arg[2+i]=x(i);}
587  for(int i=0;i<y.size();i++){arg[2+i+x.size()]=y(i);}
588  CppAD::vector<Type> res(x.rows()*y.cols());
589  matmul(arg,res);
590  return vec2mat(res,x.rows(),y.cols());
591 }
592 
610 template<class Type>
612  int n=x.rows();
613  return vec2mat(matinv(mat2vec(x)),n,n);
614 }
615 
623 template<class Type>
625  int n=x.rows();
626  CppAD::vector<Type> res = invpd(mat2vec(x));
627  logdet = res[0];
628  return vec2mat(res,n,n,1);
629 }
630 
634 template<class Type>
636  return logdet(mat2vec(x))[0];
637 }
638 
639 /* Temporary test of dmvnorm implementation based on atomic symbols.
640  Should reduce tape size from O(n^3) to O(n^2).
641 */
642 template<class Type>
643 Type nldmvnorm(vector<Type> x, matrix<Type> Sigma){
644  matrix<Type> Q=matinv(Sigma);
645  Type logdetQ = -logdet(Sigma);
646  Type quadform = (x*(Q*x)).sum();
647  return -Type(.5)*logdetQ + Type(.5)*quadform + x.size()*Type(log(sqrt(2.0*M_PI)));
648 }
649 
650 } /* End namespace atomic */
651 
652 #include "checkpoint_macro.hpp"
Vector class used by TMB.
Definition: vector.hpp:17
CppAD::vector< Type > pnorm1(CppAD::vector< Type > x)
Atomic version of standard normal distribution function. Derivative is known to be &#39;dnorm1&#39;...
CppAD::vector< Type > inv_incpl_gamma(CppAD::vector< Type > x)
Atomic version of inverse of scaled incomplete gamma function. Given find such that where the 3 in...
CppAD::vector< Type > bessel_k_10(CppAD::vector< Type > x)
Atomic version of . Valid parameter range: .
Namespace with special functions and derivatives.
Matrix class used by TMB.
Definition: vector.hpp:101
CppAD::vector< Type > matinv(CppAD::vector< Type > x)
Atomic version of matrix inversion. Inverts n-by-n matrix by LU-decomposition.
CppAD::vector< Type > bessel_i_10(CppAD::vector< Type > x)
Atomic version of . Valid parameter range: .
CppAD::vector< Type > matmul(CppAD::vector< Type > x)
Atomic version of matrix multiply. Multiplies n1-by-n2 matrix with n2-by-n3 matrix.
CppAD::vector< Type > qnorm1(CppAD::vector< Type > x)
Atomic version of standard normal quantile function. Derivative is expressed through &#39;dnorm1&#39;...
CppAD::vector< Type > invpd(CppAD::vector< Type > x)
Atomic version of log determinant and inverse of positive definite n-by-n matrix. Calculated by Chole...
CppAD::vector< Type > D_incpl_gamma_shape(CppAD::vector< Type > x)
Atomic version of scaled incomplete gamma function differentiated to any order wrt. shape parameter where the 4 input parameters are passed as a vector . Note that the normalized incomplete gamma function is obtained as the special case and . Valid parameter range: .
CppAD::vector< Type > ppois(CppAD::vector< Type > x)
Atomic version of poisson cdf . Valid parameter range: .
#define TMB_ATOMIC_VECTOR_FUNCTION( ATOMIC_NAME, OUTPUT_DIM, ATOMIC_DOUBLE, ATOMIC_REVERSE)
Construct atomic vector function based on known derivatives.
CppAD::vector< Type > D_lgamma(CppAD::vector< Type > x)
Atomic version of the n&#39;th order derivative of the log gamma function. where the 2 input parameters ...
matrix< Type > matinvpd(matrix< Type > x, Type &logdet)
Matrix inverse and determinant.
CppAD::vector< Type > logdet(CppAD::vector< Type > x)
Atomic version of log determinant of positive definite n-by-n matrix.
License: GPL v2