TMB Documentation  v1.9.11
compois.hpp
1 namespace compois_utils {
2 //#define TRACE(x) std::cout << #x << "=" << x << "\n";
3 
6 template<class Type>
7 Type calc_logZ(Type loglambda, Type nu){
8  using atomic::tiny_ad::isfinite;
9  bool ok = (0 < nu && isfinite(loglambda) && isfinite(nu) );
10  if (!ok) return NAN;
13  int iter_max = 1e4;
14  Type logZ = 0.;
15  Type logmu = loglambda / nu;
16  Type mu = exp(logmu); // mode: lambda^(1/nu)
17  if (false) {
18  // Asymptotic expansion for large mu (dropped because inaccurate)
19  logZ =
20  nu * mu - 0.5 * log(nu) -
21  ((nu - 1) / 2) * log(mu) -
22  ((nu - 1) / 2) * log(2 * M_PI);
23  } else if ( (mu > 100) && (mu * nu > 200) && (nu < 2 * mu) ) {
25  // Laplace approximation when nu=1 (Poisson case)
26  Type jhat = mu - .5;
27  Type H1 = lgamma<2>(jhat + 1.);
28  Type fhat1 = jhat * logmu - lgamma(jhat + 1.);
29  Type logL1 = log(sqrt(2. * M_PI)) - .5 * log(H1) + fhat1;
30  // Laplace approximation error nu=1
31  Type err1 = logL1 - mu;
32  // Laplace approximation general nu
33  Type H = nu * H1;
34  Type fhat = nu * fhat1;
35  Type logL = log(sqrt(2. * M_PI)) - .5 * log(H) + fhat;
36  // Apply correction
37  logL -= err1 / nu;
38  logZ = logL;
39  }
40  else {
41  // Series summation
42  int index; // Current index
43  Type logT; // Current log term
44  Type dlogT; // logT(index) - logT(index-1)
45  double reltol = 1e-12; // Break if Term_current / Sum_current < reltol
46  int i;
47  // Initialize largest term and sum
48  int index_mode = floor(mu);
49  Type logT_mode = index_mode * loglambda - nu * lgamma(index_mode + 1.);
50  logZ = logT_mode;
51  // Left tail. FIXME: Use half gaussian approximation for mu >~ maxit.
52  logT = logT_mode; // Initialize
53  for(i = 1; i<iter_max; i++) {
54  index = index_mode - i;
55  if (index < 0) break;
56  dlogT = loglambda - nu * log( (double) index + 1);
57  logT -= dlogT;
58  logZ = logspace_add(logZ, logT);
59  if ( logT - logZ < log(reltol) ) break;
60  }
61  // Right tail
62  logT = logT_mode; // Initialize
63  for(i = 1; i<iter_max; i++) {
64  index = index_mode + i;
65  dlogT = loglambda - nu * log( (double) index);
66  logT += dlogT;
67  logZ = logspace_add(logZ, logT);
68  if ( logT - logZ < log(reltol) ) break;
69  }
70  // Tail upper bound via geometric series
71  // T_j = T_i * exp( dlogT_i * j ) , j>=i
72  // sum( rho^j , j=i,...) = rho^i * 1/(1-rho)
73  // T_i * rho^i * 1/(1-rho)
74  // logT_tail = log(T_i) + i * log(rho) - log(1-rho)
75  Type logT_tail = logT + index * dlogT - logspace_sub(Type(0), dlogT);
76  logZ = logspace_add(logZ, logT_tail);
77  }
78  return logZ;
79 }
80 
82 template<class Type>
83 Type calc_mean(Type loglambda, Type nu){
84  typedef atomic::tiny_ad::variable<1, 1, Type> ADType;
85  ADType loglambda_ (loglambda, 0);
86  ADType ans = calc_logZ<ADType>(loglambda_, nu);
87  return ans.getDeriv()[0];
88 }
89 
92 template<class Type>
93 Type calc_loglambda(Type logmean, Type nu) {
94  using atomic::tiny_ad::isfinite;
95  bool ok = (0 < nu && isfinite(logmean) && isfinite(nu) );
96  if (!ok) return NAN;
97  int iter_max = 100; double reltol = 1e-9, abstol = 1e-12;
98  typedef atomic::tiny_ad::variable<1, 1, Type> ADType;
99  ADType x = nu * logmean; // Initial guess
100  ADType step = 0;
101  ADType f_previous = INFINITY;
102  int i=0;
103  for ( ; i < iter_max ; i++ ) {
104  x.deriv[0] = 1.; // Seed
105  ADType y = calc_mean<ADType>(x, nu);
106  if( ! isfinite(y) ) {
107  if (i==0) return NAN; // Undefined initial value
108  // Step halfway back
109  step = step / 2;
110  x -= step;
111  continue;
112  }
113  ADType f = ( y > 0 ?
114  log(y) - ADType( logmean ) :
115  y - ADType( exp(logmean) ) );
116  if( fabs(f) > fabs(f_previous) ) {
117  // Step halfway back
118  step = step / 2;
119  x -= step;
120  continue;
121  }
122  step = ( f.deriv[0] != 0 ?
123  -f.value / f.deriv[0] : 0 ); // Newton step
124  x += step; f_previous = f;
125  if(fabs(step) <= reltol * fabs(x))
126  break;
127  if(fabs(step) <= abstol)
128  break;
129  }
130  if (i == iter_max)
131  Rf_warning("calc_loglambda: Maximum number of iterations exceeded");
132  return x.value;
133 }
134 
135 extern "C" {
136  double Rf_lgammafn(double);
137  double Rf_psigamma(double, double);
138  double Rf_expm1(double);
139  double Rf_pgeom(double x, double p, int lower_tail, int log_p);
140  double Rf_runif(double a, double b);
141  double Rf_qgeom(double p, double prob, int lower_tail, int log_p);
142  double Rf_rgeom(double p);
143 }
144 
154 #ifdef WITH_LIBTMB
155 double simulate(double loglambda, double nu);
156 #else
157 double simulate(double loglambda, double nu) {
158 #define logf_target(x) ( nu * ( x * logmu - Rf_lgammafn(x+1) ) )
159 #define logf_propose(x) ( x < jhat ? logf0(x) : logf1(x) )
160 #define logf0(x) ( v0 + slope0 * (x - j0) )
161 #define logf1(x) ( v1 + slope1 * (x - j1) )
162  double logmu = loglambda / nu;
163  double mu = exp(logmu);
164  double jhat = (mu > 1 ? mu - .5 : 1);
165  double sd = 1. / sqrt(nu * Rf_psigamma(jhat+1, 1)); // Approx Gaussian
166  double j0 = (mu > 1 ? jhat - fmin(sd, .5 * jhat) : 0); // left tangent point
167  double j1 = jhat + sd; // right tangent point
168  double slope0 = (mu > 1 ? nu * ( logmu - Rf_psigamma(j0+1, 0) ) : 0);
169  double slope1 = nu * ( logmu - Rf_psigamma(j1+1, 0) );
170  double v0 = nu * ( j0 * logmu - Rf_lgammafn(j0+1) );
171  double v1 = nu * ( j1 * logmu - Rf_lgammafn(j1+1) );
172  // Geometric success probabilities
173  double prob0 = (mu > 1 ? -expm1(-slope0) : 1);
174  double prob1 = -expm1(slope1);
175  double mu0 = (mu > 1 ? floor(jhat) : 0); // Left offset
176  double mu1 = mu0 + 1; // right offset
177  double p0 = Rf_pgeom(mu0, prob0, 1, 0); // Left tail is truncated
178  double w0 = p0 * exp(logf0(mu0)) / prob0; // Mass of left tail proposal
179  double w1 = 1 * exp(logf1(mu1)) / prob1; // Mass of right tail proposal
180  double samp = NAN;
181  if(false) { // Debug
182  // Calculate accept probs
183  double St=0, Sp=0;
184  for(int i=0; i<1e6; i++) {
185  St += exp(logf_target(i));
186  Sp += exp(logf_propose(i));
187  }
188  double paccept = St / Sp;
189  return paccept;
190  }
191  int i=0, iter_max = 1e4;
192  for ( ; i < iter_max; i++ ) {
193  // Draw proposal sample
194  if( Rf_runif(0, 1) < w0 / (w0 + w1) ) {
195  samp = mu0 - Rf_qgeom(Rf_runif(0, p0), prob0, 1, 0);
196  } else {
197  samp = mu1 + Rf_rgeom(prob1);
198  }
199  double paccept = exp(logf_target(samp) - logf_propose(samp));
200  if(paccept > 1) {
201  samp = NAN;
202  Rf_warning("compois sampler failed (probably overflow: paccept = %f)", paccept);
203  break;
204  }
205  if( Rf_runif(0, 1) < paccept ) break;
206  }
207  if (i == iter_max) {
208  samp = NAN;
209  Rf_warning("compois sampler failed (iteration limit exceeded)");
210  }
211  if (samp != samp) { // NAN
212  Rf_warning("compois sampler returned NaN for mu=%f nu=%f", mu, nu);
213  }
214 #undef logf_target
215 #undef logf_propose
216 #undef logf0
217 #undef logf1
218  return samp;
219 }
220 #endif
221 
222 } // compois_utils
Type lgamma(Type x)
Logarithm of gamma function (following R argument convention).
Definition: lgamma.hpp:12
Type logspace_add(Type logx, Type logy)
Addition in log-space.
Type logspace_sub(Type logx, Type logy)
Subtraction in log-space.
License: GPL v2