Stan  1.1
probability, sampling & optimization
 All Classes Namespaces Files Functions Variables Typedefs Enumerator Friends Macros Pages
nuts_diag.hpp
Go to the documentation of this file.
1 #ifndef __STAN__MCMC__NUTS_DIAG_H__
2 #define __STAN__MCMC__NUTS_DIAG_H__
3 
4 #include <ctime>
5 #include <cstddef>
6 #include <iostream>
7 #include <vector>
8 
9 #include <boost/random/normal_distribution.hpp>
10 #include <boost/random/mersenne_twister.hpp>
11 #include <boost/random/variate_generator.hpp>
12 #include <boost/random/uniform_01.hpp>
13 
14 #include <stan/math/util.hpp>
17 #include <stan/mcmc/hmc_base.hpp>
18 #include <stan/mcmc/util.hpp>
19 
20 namespace stan {
21 
22  namespace mcmc {
23 
32  template <class BaseRNG = boost::mt19937>
33  class nuts_diag : public hmc_base<BaseRNG> {
34  private:
35 
36  // Stop immediately if H < u - _maxchange
37  const double _maxchange;
38 
39  // Limit tree depth
40  const int _maxdepth;
41 
42  // Depth of last sample taken (-1 before any samples)
43  int _lastdepth;
44 
45  // Vector of per-parameter step sizes.
46  std::vector<double> _step_sizes;
47  // Running statistics to estimate per-coordinate std. deviations.
48  std::vector<double> _x_sum;
49  std::vector<double> _xsq_sum;
50  int _x_sum_n;
51  // Next time we should adapt the per-parameter step sizes.
52  int _next_diag_adapt;
53 
62  inline static bool compute_criterion(std::vector<double>& xplus,
63  std::vector<double>& xminus,
64  std::vector<double>& mplus,
65  std::vector<double>& mminus,
66  std::vector<double>& step_sizes) {
67  std::vector<double> total_direction;
68  stan::math::sub(xplus, xminus, total_direction);
69  // adjustment for U-turn due to step sizes
70  for (size_t i = 0; i < total_direction.size(); ++i)
71  total_direction[i] /= step_sizes[i];
72  return stan::math::dot(total_direction, mminus) > 0
73  && stan::math::dot(total_direction, mplus) > 0;
74  }
75 
76  public:
77 
105  int maxdepth = 10,
106  double epsilon = -1,
107  double epsilon_pm = 0.0,
108  bool epsilon_adapt = true,
109  double delta = 0.6,
110  double gamma = 0.05,
111  BaseRNG base_rng = BaseRNG(std::time(0)),
112  const std::vector<double>* params_r = 0,
113  const std::vector<int>* params_i = 0)
114  : hmc_base<BaseRNG>(model,
115  epsilon,
116  epsilon_pm,
117  epsilon_adapt,
118  delta,
119  gamma,
120  base_rng,
121  params_r,
122  params_i),
123 
124  _maxchange(-1000),
125  _maxdepth(maxdepth),
126  _lastdepth(-1),
127 
128  _step_sizes(model.num_params_r(), 1.0),
129  _x_sum(model.num_params_r(), 0),
130  _xsq_sum(model.num_params_r(), 0),
131  _x_sum_n(0),
132  _next_diag_adapt(10)
133  {
134  // start at 10 * epsilon because NUTS cheaper for larger epsilon
135  this->adaptation_init(10.0);
136  }
137 
144 
150  virtual sample next_impl() {
151  // Initialize the algorithm
152  std::vector<double> mminus(this->_model.num_params_r());
153  for (size_t i = 0; i < mminus.size(); ++i)
154  mminus[i] = this->_rand_unit_norm();
155  std::vector<double> mplus(mminus);
156  // The log-joint probability of the momentum and position terms, i.e.
157  // -(kinetic energy + potential energy)
158  double H0 = -0.5 * stan::math::dot_self(mminus) + this->_logp;
159 
160  std::vector<double> gradminus(this->_g);
161  std::vector<double> gradplus(this->_g);
162  std::vector<double> xminus(this->_x);
163  std::vector<double> xplus(this->_x);
164 
165  // Sample the slice variable
166  double u = log(this->_rand_uniform_01()) + H0;
167  int nvalid = 1;
168  int direction = 2 * (this->_rand_uniform_01() > 0.5) - 1;
169  bool criterion = true;
170 
171  // Repeatedly double the set of points we've visited
172  std::vector<double> newx, newgrad, dummy1, dummy2, dummy3;
173  double newlogp = -1;
174  double prob_sum = -1;
175  int newnvalid = -1;
176  int n_considered = 0;
177  // for-loop with depth outside to set lastdepth
178  int depth = 0;
179 
180  double epsilon = this->_epsilon;
181  // only vary epsilon after done adapting
182  if (!this->adapting() && this->varying_epsilon()) {
183  double low = epsilon * (1.0 - this->_epsilon_pm);
184  double high = epsilon * (1.0 + this->_epsilon_pm);
185  double range = high - low;
186  epsilon = low + (range * this->_rand_uniform_01());
187  }
188  this->_epsilon_last = epsilon; // use epsilon_last in tree build
189 
190  while (criterion && (_maxdepth < 0 || depth < _maxdepth)) {
191  direction = 2 * (this->_rand_uniform_01() > 0.5) - 1;
192  if (direction == -1)
193  build_tree(xminus, mminus, gradminus, u, direction, depth,
194  H0, xminus, mminus, gradminus, dummy1, dummy2, dummy3,
195  newx, newgrad, newlogp, newnvalid, criterion, prob_sum,
196  n_considered);
197  else
198  build_tree(xplus, mplus, gradplus, u, direction, depth,
199  H0, dummy1, dummy2, dummy3, xplus, mplus, gradplus,
200  newx, newgrad, newlogp, newnvalid, criterion, prob_sum,
201  n_considered);
202  // We can't look at the results of this last doubling if criterion==false
203  if (!criterion)
204  break;
205  criterion = compute_criterion(xplus, xminus, mplus, mminus,_step_sizes);
206  // Metropolis-Hastings to determine if we can jump to a point in
207  // the new half-tree
208  if (this->_rand_uniform_01() < float(newnvalid) / (1e-100+float(nvalid))) {
209  this->_x = newx;
210  this->_g = newgrad;
211  this->_logp = newlogp;
212  }
213  nvalid += newnvalid;
214  ++depth;
215  }
216  _lastdepth = depth;
217 
218  // Now we just have to update global (epsilon) and local
219  // (step_sizes) step sizes, if adaptation is on.
220  double adapt_stat = prob_sum / float(n_considered);
221  if (this->adapting()) {
222  // epsilon.
223  double adapt_g = adapt_stat - this->_delta;
224  std::vector<double> gvec(1, -adapt_g);
225  std::vector<double> result;
226  this->_da.update(gvec, result);
227  this->_epsilon = exp(result[0]);
228  // step_sizes. Doesn't happen every step.
229  if (this->_n_adapt_steps == _next_diag_adapt) {
230  _next_diag_adapt *= 2;
231  double step_size_sq_sum = 0;
232  for (size_t i = 0; i < _step_sizes.size(); i++) {
233  double Ex = _x_sum[i] / _x_sum_n;
234  double Exsq = _xsq_sum[i] / _x_sum_n;
235  _x_sum[i] = 0;
236  _xsq_sum[i] = 0;
237  _step_sizes[i] = sqrt(Exsq - Ex*Ex);
238  step_size_sq_sum += _step_sizes[i] * _step_sizes[i];
239  }
240  if (step_size_sq_sum > 0.0) {
241  _x_sum_n = 0;
242  double normalizer = sqrt((double)_step_sizes.size())
243  / sqrt(step_size_sq_sum);
244  for (size_t i = 0; i < _step_sizes.size(); i++)
245  _step_sizes[i] *= normalizer;
246  } else {
247  for (size_t i = 0; i < _step_sizes.size(); i++)
248  _step_sizes[i] = 1.0;
249  }
250  }
251  }
252  std::vector<double> result;
253  this->_da.xbar(result);
254  double avg_eta = 1.0 / this->n_steps();
255  this->update_mean_stat(avg_eta,adapt_stat);
256 
257  return mcmc::sample(this->_x, this->_z, this->_logp);
258  }
259 
260  virtual void write_sampler_param_names(std::ostream& o) {
261  o << "treedepth__,";
262  if (this->_epsilon_adapt || this->varying_epsilon())
263  o << "stepsize__,";
264  }
265 
266  virtual void write_sampler_params(std::ostream& o) {
267  o << _lastdepth << ',';
268  if (this->_epsilon_adapt || this->varying_epsilon())
269  o << this->_epsilon_last << ',';
270  }
271 
272  virtual void write_adaptation_params(std::ostream& o) {
273  o << "# (mcmc::nuts_diag) adaptation finished" << '\n';
274  o << "# step size=" << this->_epsilon << '\n';
275  o << "# parameter step size multipliers:\n"; // FIXME: names/delineation requires access to model
276  o << "# ";
277  for (size_t k = 0; k < _step_sizes.size(); ++k) {
278  if (k > 0) o << ',';
279  o << _step_sizes[k];
280  }
281  o << '\n';
282  }
283 
284  virtual void get_sampler_param_names(std::vector<std::string>& names) {
285  names.clear();
286  names.push_back("treedepth__");
287  if (this->_epsilon_adapt || this->varying_epsilon())
288  names.push_back("stepsize__");
289  }
290 
291  virtual void get_sampler_params(std::vector<double>& values) {
292  values.clear();
293  values.push_back(_lastdepth);
294  if (this->_epsilon_adapt || this->varying_epsilon())
295  values.push_back(this->_epsilon_last);
296  }
297 
332  void build_tree(const std::vector<double>& x,
333  const std::vector<double>& m,
334  const std::vector<double>& grad,
335  double u,
336  int direction,
337  int depth,
338  double H0,
339  std::vector<double>& xminus,
340  std::vector<double>& mminus,
341  std::vector<double>& gradminus,
342  std::vector<double>& xplus,
343  std::vector<double>& mplus,
344  std::vector<double>& gradplus,
345  std::vector<double>& newx,
346  std::vector<double>& newgrad,
347  double& newlogp,
348  int& nvalid,
349  bool& criterion,
350  double& prob_sum,
351  int& n_considered) {
352  if (depth == 0) { // base case
353  xminus = x;
354  gradminus = grad;
355  mminus = m;
356  newlogp = rescaled_leapfrog(this->_model, this->_z, _step_sizes,
357  xminus, mminus, gradminus,
358  direction * this->_epsilon_last,
359  this->_error_msgs, this->_output_msgs);
360  newx = xminus;
361  newgrad = gradminus;
362  xplus = xminus;
363  mplus = mminus;
364  gradplus = gradminus;
365  double newH = newlogp - 0.5 * stan::math::dot_self(mminus);
366  if (newH != newH) // treat nan as -inf
367  newH = -std::numeric_limits<double>::infinity();
368  nvalid = newH > u;
369  criterion = newH - u > _maxchange;
370  prob_sum = stan::math::min(1, exp(newH - H0));
371  n_considered = 1;
372  this->nfevals_plus_eq(1);
373  // Update running statistics if point is in slice
374  if (nvalid) {
375  _x_sum_n++;
376  for (size_t i = 0; i < newx.size(); i++) {
377  _x_sum[i] += newx[i];
378  _xsq_sum[i] += newx[i] * newx[i];
379  }
380  }
381  } else { // depth >= 1
382  build_tree(x, m, grad, u, direction, depth-1, H0, xminus, mminus,
383  gradminus, xplus, mplus, gradplus, newx, newgrad, newlogp,
384  nvalid, criterion, prob_sum, n_considered);
385  if (criterion) {
386  std::vector<double> dummy1, dummy2, dummy3;
387  std::vector<double> newx2;
388  std::vector<double> newgrad2;
389  double newlogp2;
390  int nvalid2;
391  bool criterion2;
392  double prob_sum2;
393  int n_considered2;
394  if (direction == -1)
395  build_tree(xminus, mminus, gradminus, u, direction, depth-1, H0,
396  xminus, mminus, gradminus, dummy1, dummy2, dummy3,
397  newx2, newgrad2, newlogp2, nvalid2, criterion2,
398  prob_sum2, n_considered2);
399  else
400  build_tree(xplus, mplus, gradplus, u, direction, depth-1, H0,
401  dummy1, dummy2, dummy3, xplus, mplus, gradplus,
402  newx2, newgrad2, newlogp2, nvalid2, criterion2,
403  prob_sum2, n_considered2);
404  if (criterion &&
405  (this->_rand_uniform_01()
406  < float(nvalid2) / float(nvalid+nvalid2))){
407  newx = newx2;
408  newgrad = newgrad2;
409  newlogp = newlogp2;
410  }
411  n_considered += n_considered2;
412  prob_sum += prob_sum2;
413  criterion &= criterion2;
414  nvalid += nvalid2;
415  }
416  criterion &= compute_criterion(xplus, xminus, mplus, mminus, _step_sizes);
417  }
418  }
419 
420 
421  };
422 
423 
424  }
425 
426 }
427 
428 #endif

     [ Stan Home Page ] © 2011–2013, Stan Development Team.