Tomographer  v4.0
Tomographer C++ Framework Documentation
mhrw.h
Go to the documentation of this file.
1 /* This file is part of the Tomographer project, which is distributed under the
2  * terms of the MIT license.
3  *
4  * The MIT License (MIT)
5  *
6  * Copyright (c) 2016 ETH Zurich, Institute for Theoretical Physics, Philippe Faist
7  * Copyright (c) 2017 Caltech, Institute for Quantum Information and Matter, Philippe Faist
8  *
9  * Permission is hereby granted, free of charge, to any person obtaining a copy
10  * of this software and associated documentation files (the "Software"), to deal
11  * in the Software without restriction, including without limitation the rights
12  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
13  * copies of the Software, and to permit persons to whom the Software is
14  * furnished to do so, subject to the following conditions:
15  *
16  * The above copyright notice and this permission notice shall be included in
17  * all copies or substantial portions of the Software.
18  *
19  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
24  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
25  * SOFTWARE.
26  */
27 
28 #ifndef _TOMOGRAPHER_MHRW_H
29 #define _TOMOGRAPHER_MHRW_H
30 
31 #include <cstddef>
32 
33 #include <limits>
34 #include <random>
35 #include <iomanip>
36 
38 #include <tomographer/tools/fmt.h>
42 
43 
44 
51 namespace Tomographer {
52 
53 
54 
55 
56 
57 
58 
59 
60 enum {
74 };
75 
76 
77 namespace tomo_internal {
78 
86  template<typename MHWalker, int UseFnSyntaxType>
87  struct MHRandomWalk_helper_decide_jump
88  {
92  typedef typename MHWalker::PointType PointType;
96  typedef typename MHWalker::FnValueType FnValueType;
97 
104  static inline FnValueType get_ptval(MHWalker & mhwalker, const PointType & curpt)
105  {
106  (void)mhwalker; (void)curpt;
107  tomographer_assert(0 && "UNKNOWN UseFnSyntaxType: Not implemented");
108  }
122  static inline double get_a_value(MHWalker & /*mhwalker*/, const PointType & /*newpt*/, FnValueType /*newptval*/,
123  const PointType & /*curpt*/, FnValueType /*curptval*/)
124  {
125  tomographer_assert(0 && "UNKNOWN UseFnSyntaxType: Not implemented");
126  return 0;
127  }
128  };
129 
133  template<typename MHWalker>
134  struct MHRandomWalk_helper_decide_jump<MHWalker, MHUseFnValue>
135  {
136  typedef typename MHWalker::PointType PointType;
137  typedef typename MHWalker::FnValueType FnValueType;
138 
139  static inline FnValueType get_ptval(MHWalker & mhwalker, const PointType & curpt)
140  {
141  return mhwalker.fnVal(curpt);
142  }
143  static inline double get_a_value(MHWalker & /*mhwalker*/, const PointType & /*newpt*/, double newptval,
144  const PointType & /*curpt*/, double curptval)
145  {
146  return (newptval / curptval);
147  }
148  };
149 
153  template<typename MHWalker>
154  struct MHRandomWalk_helper_decide_jump<MHWalker, MHUseFnLogValue>
155  {
156  typedef typename MHWalker::PointType PointType;
157  typedef typename MHWalker::FnValueType FnValueType;
158 
159  static inline FnValueType get_ptval(MHWalker & mhwalker, const PointType & curpt)
160  {
161  return mhwalker.fnLogVal(curpt);
162  }
163  static inline double get_a_value(MHWalker & /*mhwalker*/, const PointType & /*newpt*/, FnValueType newptval,
164  const PointType & /*curpt*/, FnValueType curptval)
165  {
166  return (newptval > curptval) ? 1.0 : exp(newptval - curptval);
167  }
168  };
169 
173  template<typename MHWalker>
174  struct MHRandomWalk_helper_decide_jump<MHWalker, MHUseFnRelativeValue>
175  {
176  typedef typename MHWalker::PointType PointType;
177  typedef int FnValueType; // dummy FnValueType
178 
179  static inline FnValueType get_ptval(MHWalker & /*mhwalker*/, const PointType & /*curpt*/)
180  {
181  return 0;
182  }
183  static inline double get_a_value(MHWalker & mhwalker, const PointType & newpt, FnValueType /*newptval*/,
184  const PointType & curpt, FnValueType /*curptval*/)
185  {
186  return mhwalker.fnRelVal(newpt, curpt);
187  }
188  };
189 };
190 
191 
192 // note: const implies static linkage, see http://stackoverflow.com/q/2268749/1694896
193 //
198 
199 
205 template<typename CountIntType_ = int, typename StepRealType_ = double>
206 TOMOGRAPHER_EXPORT struct MHRWParams
207 {
208  typedef CountIntType_ CountIntType;
209  typedef StepRealType_ StepRealType;
210 
211  explicit MHRWParams()
212  : step_size(0), n_sweep(0), n_therm(0), n_run(0)
213  {
214  }
215  MHRWParams(StepRealType step_size_, CountIntType n_sweep_, CountIntType n_therm_, CountIntType n_run_)
216  : step_size(step_size_), n_sweep(n_sweep_), n_therm(n_therm_), n_run(n_run_)
217  {
218  }
219 
222  StepRealType step_size;
223 
226  CountIntType n_sweep;
227 
230  CountIntType n_therm;
231 
234  CountIntType n_run;
235 };
236 
237 
238 
239 template<typename CountIntType, typename StepRealType>
240 std::ostream & operator<<(std::ostream & str, const MHRWParams<CountIntType,StepRealType> & p)
241 {
242  str << "MHRWParams(step_size=" << p.step_size << ",n_sweep=" << p.n_sweep
243  << ",n_therm=" << p.n_therm << ",n_run=" << p.n_run << ")";
244  return str;
245 }
246 
247 
248 
288 template<typename Rng_, typename MHWalker_, typename MHRWStatsCollector_, typename LoggerType_,
289  typename CountIntType_ = int>
290 TOMOGRAPHER_EXPORT class MHRandomWalk
291  : public virtual Tools::NeedOwnOperatorNew<typename MHWalker_::PointType>::ProviderType
292 {
293 public:
295  typedef Rng_ Rng;
297  typedef MHWalker_ MHWalker;
299  typedef MHRWStatsCollector_ MHRWStatsCollector;
301  typedef LoggerType_ LoggerType;
304  typedef CountIntType_ CountIntType;
305 
307  typedef typename MHWalker::PointType PointType;
309  typedef typename MHWalker::StepRealType StepRealType;
310 
313 
315 #ifndef TOMOGRAPHER_PARSED_BY_DOXYGEN
316  typedef typename tomo_internal::MHRandomWalk_helper_decide_jump<MHWalker,MHWalker::UseFnSyntaxType>::FnValueType
317  FnValueType;
318 #else
319  typedef _FnValueType FnValueType;
320 #endif
321 
322  enum {
324  UseFnSyntaxType = MHWalker::UseFnSyntaxType
325  };
326 
327 private:
328  const MHRWParamsType _n;
329 
330  Rng & _rng;
331  MHWalker & _mhwalker;
332  MHRWStatsCollector & _stats;
334 
336  PointType curpt;
340  FnValueType curptval;
341 
346  CountIntType num_accepted;
350  CountIntType num_live_points;
351 
352 public:
353 
355  MHRandomWalk(StepRealType step_size, CountIntType n_sweep, CountIntType n_therm, CountIntType n_run,
356  MHWalker & mhwalker, MHRWStatsCollector & stats,
357  Rng & rng, LoggerType & logger_)
358  : _n(step_size, n_sweep, n_therm, n_run),
359  _rng(rng),
360  _mhwalker(mhwalker),
361  _stats(stats),
362  _logger(TOMO_ORIGIN, logger_),
363  curpt(),
364  curptval(),
365  num_accepted(0),
366  num_live_points(0)
367  {
368  _logger.debug([&](std::ostream & stream) {
369  stream << "constructor(). n_sweep=" << n_sweep << ", step_size=" << step_size
370  << "n_therm=" << n_therm << ", n_run=" << n_run;
371  });
372  }
374  template<typename MHRWParamsType>
375  MHRandomWalk(MHRWParamsType&& n_rw,
376  MHWalker & mhwalker, MHRWStatsCollector & stats,
377  Rng & rng, LoggerType & logger_)
378  : _n(std::forward<MHRWParamsType>(n_rw)),
379  _rng(rng),
380  _mhwalker(mhwalker),
381  _stats(stats),
382  _logger(TOMO_ORIGIN, logger_),
383  curpt(),
384  curptval(),
385  num_accepted(0),
386  num_live_points(0)
387  {
388  _logger.debug([&](std::ostream & s) { s << "constructor(). mhrw parameters = " << _n; });
389  }
390 
391  MHRandomWalk(const MHRandomWalk & other) = delete;
392 
393 
395  inline MHRWParamsType mhrwParams() const { return _n; }
396 
398  inline StepRealType stepSize() const { return _n.step_size; }
399 
401  inline CountIntType nSweep() const { return _n.n_sweep; }
403  inline CountIntType nTherm() const { return _n.n_therm; }
405  inline CountIntType nRun() const { return _n.n_run; }
406 
407 
408 
412  inline bool hasAcceptanceRatio() const
413  {
414  return (num_live_points > 0);
415  }
418  template<typename RatioType = double>
419  inline RatioType acceptanceRatio() const
420  {
421  return RatioType(num_accepted) / RatioType(num_live_points);
422  }
423 
424 
429  inline const PointType & getCurrentPoint() const
430  {
431  return curpt;
432  }
433 
441  inline const FnValueType & getCurrentPointValue() const
442  {
443  return curptval;
444  }
445 
451  inline void setCurrentPoint(const PointType& pt)
452  {
453  curpt = pt;
454  curptval = tomo_internal::MHRandomWalk_helper_decide_jump<MHWalker,UseFnSyntaxType>::get_ptval(_mhwalker, curpt);
455  _logger.longdebug([&](std::ostream & s) {
456  s << "setCurrentPoint: set internal state. Value = " << curptval << "; Point =\n" << pt << "\n";
457  });
458  }
459 
460 
461 private:
462 
465  inline void _init()
466  {
467  num_accepted = 0;
468  num_live_points = 0;
469 
470  // starting point
471  curpt = _mhwalker.startPoint();
472  curptval = tomo_internal::MHRandomWalk_helper_decide_jump<MHWalker,UseFnSyntaxType>::get_ptval(_mhwalker, curpt);
473 
474  _mhwalker.init();
475  _stats.init();
476  _logger.longdebug("_init() done.");
477  }
480  inline void _thermalizing_done()
481  {
482  _mhwalker.thermalizingDone();
483  _stats.thermalizingDone();
484  _logger.longdebug("_thermalizing_done() done.");
485  }
488  inline void _done()
489  {
490  _mhwalker.done();
491  _stats.done();
492  _logger.longdebug("_done() done.");
493  }
494 
502  inline void _move(CountIntType k, bool is_thermalizing, bool is_live_iter)
503  {
504  _logger.longdebug("_move()");
505  // The reason `step_size` is passed to jump_fn instead of leaving jump_fn itself
506  // handle the step size, is that we might in the future want to dynamically adapt the
507  // step size according to the acceptance ratio. That would have to be done in this
508  // class.
509  PointType newpt = _mhwalker.jumpFn(curpt, _n.step_size);
510 
511  FnValueType newptval;
512 
513  newptval = tomo_internal::MHRandomWalk_helper_decide_jump<MHWalker,UseFnSyntaxType>::get_ptval(_mhwalker, newpt);
514 
515  double a = tomo_internal::MHRandomWalk_helper_decide_jump<MHWalker,UseFnSyntaxType>::get_a_value(
516  _mhwalker, newpt, newptval, curpt, curptval
517  );
518 
519  // accept move?
520  bool accept = true;
521  if (a < 1.0) {
522  accept = bool( _rng()-_rng.min() <= a*(_rng.max()-_rng.min()) );
523  }
524 
525  // track acceptance ratio, except if we are thermalizing
526  if (!is_thermalizing) {
527  num_accepted += accept ? 1 : 0;
528  ++num_live_points;
529  }
530 
531  _stats.rawMove(k, is_thermalizing, is_live_iter, accept, a, newpt, newptval, curpt, curptval, *this);
532 
533  _logger.longdebug([&](std::ostream & stream) {
534  stream << (is_thermalizing?"T":"#") << std::setw(3) << k << ": " << (accept?"AC":"RJ") << " "
535  << std::setprecision(4)
536  << "a=" << std::setw(5) << a << ", newptval=" << std::setw(5) << newptval
537  << ", curptval=" << std::setw(5) << curptval << ", accept_ratio="
538  << (!is_thermalizing ? Tools::fmts("%.2g", this->acceptanceRatio()) : std::string("N/A"))
539  << Tools::streamIfPossible(curpt, "\ncurpt = ", "", "");
540  });
541 
542  if (accept) {
543  // update the internal state of the random walk
544  curpt = newpt;
545  curptval = newptval;
546  }
547  _logger.longdebug("_move() done.");
548  }
549 
554  inline void _process_sample(CountIntType k, CountIntType n)
555  {
556  _stats.processSample(k, n, curpt, curptval, *this);
557  _logger.longdebug("_process_sample() done.");
558  }
559 
560 
561 public:
562 
569  void run()
570  {
571  _init();
572 
573  CountIntType k;
574 
575  _logger.longdebug([&](std::ostream & s) {
576  s << "Starting random walk, sweep size = " << _n.n_sweep << ", step size = " << _n.step_size
577  << ", # therm sweeps = " << _n.n_therm << ", # live sweeps = " << _n.n_run;
578  });
579 
580  const CountIntType num_thermalize = _n.n_sweep * _n.n_therm;
581 
582  for (k = 0; k < num_thermalize; ++k) {
583  // calculate a candidate jump point and see if we accept the move
584  _move(k, true, false);
585  }
586 
587  _thermalizing_done();
588 
589  _logger.longdebug("Thermalizing done, starting live runs.");
590 
591  const CountIntType num_run = _n.n_sweep * _n.n_run;
592 
593  CountIntType n = 0; // number of live samples
594 
595  for (k = 0; k < num_run; ++k) {
596 
597  bool is_live_iter = ((k+1) % _n.n_sweep == 0);
598 
599  // calculate a candidate jump point and see if we accept the move
600  _move(k, false, is_live_iter);
601 
602  if (is_live_iter) {
603  _process_sample(k, n);
604  ++n;
605  }
606 
607  }
608 
609  _done();
610 
611  _logger.longdebug("Random walk completed.");
612 
613  return;
614  }
615 };
616 
617 
618 
619 } // namespace Tomographer
620 
621 
622 
623 
624 #endif
A Metropolis-Hastings Random Walk.
Definition: mhrw.h:290
Utilities for formatting strings.
MHWalker::StepRealType StepRealType
The type of a step size of the random walk.
Definition: mhrw.h:309
CountIntType nTherm() const
Number of thermalizing sweeps.
Definition: mhrw.h:403
MHWalker::PointType PointType
The type of a point in the random walk.
Definition: mhrw.h:307
const FnValueType & getCurrentPointValue() const
Access the current function value of the random walk.
Definition: mhrw.h:441
Base namespace for the Tomographer project.
Definition: densellh.h:45
void debug(const char *fmt,...)
Generate a log message with level Logger::DEBUG (printf-like syntax)
Definition: loggers.h:1933
CountIntType n_therm
Number of thermalization sweeps.
Definition: mhrw.h:230
CountIntType n_run
Number of live sweeps.
Definition: mhrw.h:234
const double MHRWAcceptanceRatioRecommendedMin
Minimal recommended acceptance ratio.
Definition: mhrw.h:195
MHRWStatsCollector_ MHRWStatsCollector
The stats collector type (see MHRWStatsCollector Interface)
Definition: mhrw.h:299
CountIntType nSweep() const
Number of iterations in a sweep.
Definition: mhrw.h:401
Provide appropriate operator new() definitions for a structure which has a member of the given stored...
void setCurrentPoint(const PointType &pt)
Force manual state of random walk.
Definition: mhrw.h:451
STL namespace.
Provides the MH function value at each point (see Role of UseFnSyntaxType)
Definition: mhrw.h:64
MHRWParams< CountIntType, StepRealType > MHRWParamsType
The struct which can hold the parameters of this random walk.
Definition: mhrw.h:312
MHRandomWalk(MHRWParamsType &&n_rw, MHWalker &mhwalker, MHRWStatsCollector &stats, Rng &rng, LoggerType &logger_)
Simple constructor, initializes the given fields.
Definition: mhrw.h:375
MHWalker_ MHWalker
The random walker type which knows about the state space and jump function.
Definition: mhrw.h:297
StepRealType step_size
The step size of the random walk.
Definition: mhrw.h:222
#define TOMO_ORIGIN
Use this as argument for a Tomographer::Logger::LocalLogger constructor .
Definition: loggers.h:1660
_FnValueType FnValueType
The type of the Metropolis-Hastings function value. (See class documentation)
Definition: mhrw.h:319
T setw(T... args)
STL class.
CountIntType nRun() const
Number of live run sweeps.
Definition: mhrw.h:405
const PointType & getCurrentPoint() const
Access the current state of the random walk.
Definition: mhrw.h:429
MHRandomWalk(StepRealType step_size, CountIntType n_sweep, CountIntType n_therm, CountIntType n_run, MHWalker &mhwalker, MHRWStatsCollector &stats, Rng &rng, LoggerType &logger_)
Simple constructor, initializes the given fields.
Definition: mhrw.h:355
LoggerType_ LoggerType
The logger type which will be provided by user to constructor (see Logging and Loggers) ...
Definition: mhrw.h:301
const double MHRWAcceptanceRatioRecommendedMax
Maximal recommended acceptance ratio.
Definition: mhrw.h:197
void longdebug(const char *fmt,...)
Generate a log message with level Logger::LONGDEBUG (printf-like syntax)
Definition: loggers.h:1927
_Unspecified streamIfPossible(const T &obj)
Utility to stream an object, but only if "<<" overload exists.
Definition: fmt.h:308
RatioType acceptanceRatio() const
Return the acceptance ratio so far.
Definition: mhrw.h:419
Provides the logarithm MH function value at each point (see Role of UseFnSyntaxType) ...
Definition: mhrw.h:68
Rng_ Rng
Random number generator type (see C++ std::random)
Definition: mhrw.h:295
Some C++ utilities, with a tad of C++11 tricks.
void run()
Run the random walk. (pun intended)
Definition: mhrw.h:569
Provides directly the ratio of the function values for two consecutive points of the MH random walk (...
Definition: mhrw.h:73
MHRWParamsType mhrwParams() const
The parameters of the random walk.
Definition: mhrw.h:395
Managing the need for specific overrides to operator new() for some types (especially Eigen types) ...
CountIntType n_sweep
The number of individual updates to collect together in a "sweep".
Definition: mhrw.h:226
std::string fmts(const char *fmt,...)
printf- format to a std::string
Definition: fmt.h:125
Specify the parameters of a Metropolis-Hastings random walk.
Definition: mhrw.h:206
StepRealType stepSize() const
Get the step size of the random walk.
Definition: mhrw.h:398
CountIntType_ CountIntType
The type used for counting numbers of iterations (see, e.g. nSweep() or MHRWParams) ...
Definition: mhrw.h:304
T setprecision(T... args)
Binning Analysis in a Metropolis-Hastings random walk.
STL class.
bool hasAcceptanceRatio() const
Query whether we have any statistics about acceptance ratio. This is false, for example, during the thermalizing runs.
Definition: mhrw.h:412
#define tomographer_assert(...)
Assertion test macro.
Definition: cxxdefs.h:83
Utilities for logging messages.