Tomographer  v1.0a
Tomographer C++ Framework Documentation
mhrw_bin_err.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) 2015 ETH Zurich, Institute for Theoretical Physics, Philippe Faist
7  *
8  * Permission is hereby granted, free of charge, to any person obtaining a copy
9  * of this software and associated documentation files (the "Software"), to deal
10  * in the Software without restriction, including without limitation the rights
11  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
12  * copies of the Software, and to permit persons to whom the Software is
13  * furnished to do so, subject to the following conditions:
14  *
15  * The above copyright notice and this permission notice shall be included in
16  * all copies or substantial portions of the Software.
17  *
18  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
23  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
24  * SOFTWARE.
25  */
26 
27 #ifndef TOMOGRAPHER_MHRW_BIN_ERR_H
28 #define TOMOGRAPHER_MHRW_BIN_ERR_H
29 
30 
31 #include <tomographer/qit/util.h>
32 #include <tomographer/tools/util.h>
34 
35 #include <boost/math/constants/constants.hpp>
36 
37 
47 namespace Tomographer {
48 
49 namespace tomo_internal {
50 //
51 // internal helper to silence "left shift is negative" GCC warnings
52 //
53 template<int NumLevels, bool calculate>
54 struct helper_samples_size {
55  enum {
56  value = Eigen::Dynamic
57  };
58 };
59 template<int NumLevels>
60 struct helper_samples_size<NumLevels,true> {
61  enum {
62  value = (1 << NumLevels)
63  };
64 };
65 } // namespace tomo_internal
66 
67 
68 
106 template<typename ValueType_, int NumTrackValues_ = Eigen::Dynamic, int NumLevels_ = Eigen::Dynamic,
107  bool StoreBinSums_ = true, typename CountIntType_ = int>
109 {
112  typedef ValueType_ ValueType;
115  typedef CountIntType_ CountIntType;
116 
118  static constexpr int NumTrackValuesCTime = NumTrackValues_;
120  static constexpr int NumLevelsCTime = NumLevels_;
122  static constexpr int NumLevelsPlusOneCTime = (NumLevelsCTime == Eigen::Dynamic
123  ? Eigen::Dynamic
124  : (NumLevelsCTime + 1));
147  static constexpr int SamplesSizeCTime =
148  tomo_internal::helper_samples_size<NumLevelsCTime, (NumLevelsCTime > 0 && NumLevelsCTime < 7)>::value;
149 
155  static constexpr bool StoreBinSums = StoreBinSums_;
156 
165 
180 
182  enum {
184  UNKNOWN_CONVERGENCE = 0,
188  NOT_CONVERGED
189  };
190 
191 };
192 
248 template<typename Params, typename LoggerType_>
250 {
251 public:
253  typedef typename Params::ValueType ValueType;
255  typedef typename Params::CountIntType CountIntType;
256 
260  static constexpr int NumTrackValuesCTime = Params::NumTrackValuesCTime;
264  static constexpr int NumLevelsCTime = Params::NumLevelsCTime;
268  static constexpr int NumLevelsPlusOneCTime = Params::NumLevelsPlusOneCTime;
272  static constexpr int SamplesSizeCTime = Params::SamplesSizeCTime;
276  static constexpr bool StoreBinSums = Params::StoreBinSums;
277 
281  typedef typename Params::SamplesArray SamplesArray;
285  typedef typename Params::BinSumArray BinSumArray;
289  typedef typename Params::BinSumSqArray BinSumSqArray;
290 
317 
319  enum {
321  UNKNOWN_CONVERGENCE = Params::UNKNOWN_CONVERGENCE,
323  CONVERGED = Params::CONVERGED,
325  NOT_CONVERGED = Params::NOT_CONVERGED
326  };
327 
329  typedef LoggerType_ LoggerType;
330 
331 private:
332 
341  SamplesArray samples;
342 
343  // where we store the flushed values
344 
349  CountIntType n_samples;
355  CountIntType n_flushes;
376  BinSumSqArray bin_sumsq;
377 
379  LoggerType & logger;
380 
381 public:
382 
393  BinningAnalysis(int num_track_values_, int num_levels_, LoggerType & logger_)
394  : num_track_values(num_track_values_),
395  num_levels(num_levels_),
396  samples_size(1 << num_levels()),
397  samples(num_track_values(), samples_size()),
398  n_flushes(0),
399  bin_sum(BinSumArray::Zero(num_track_values())),
400  bin_sumsq(BinSumSqArray::Zero(num_track_values(), num_levels()+1)),
401  logger(logger_)
402  {
403  assert(Tools::is_positive(num_levels()));
404  assert(Tools::is_power_of_two(samples_size()));
405  assert( (1<<num_levels()) == samples_size() );
406 
407  reset();
408  }
409 
415  inline void reset()
416  {
417  n_flushes = 0;
418  n_samples = 0;
419  helper_reset_bin_sum();
420  bin_sumsq = BinSumSqArray::Zero(num_track_values(), num_levels()+1);
421  logger.longdebug("BinningAnalysis::reset()", "ready to go.");
422  }
423 
434  template<typename Derived>
436  {
437  const int ninbin = n_samples % samples_size();
438 
439  ++n_samples;
440 
441  eigen_assert(vals.rows() == num_track_values());
442  eigen_assert(vals.cols() == 1);
443 
444  // store the new values in the bins [also if ninbin == 0]
445  samples.col(ninbin) = vals;
446 
447  // add to our sum of values, if applicable.
448  helper_update_bin_sum(samples.col(ninbin));
449 
450  // see if we have to flush the bins (equivalent to `ninbin == samples_size()-1`)
451  if ( ninbin == samples_size() - 1 ) {
452 
453  // we have filled all bins. Flush them. Re-use the beginning of the samples[] array
454  // to store the reduced bins while flushing them.
455  logger.longdebug("BinningAnalysis::process_new_values()", [&](std::ostream & str) {
456  str << "n_samples is now " << n_samples << "; flushing bins. samples_size() = " << samples_size();
457  });
458 
459  // the size of the samples at the current level of binning. Starts at samples_size,
460  // and decreases by half at each higher level.
461 
462  for (int level = 0; level <= num_levels(); ++level) {
463 
464  const int binnedsize = 1 << (num_levels()-level);
465 
466  logger.longdebug("BinningAnalysis::process_new_values()", [&](std::ostream & str) {
467  str << "Processing binning level = " << level << ": binnedsize="<<binnedsize
468  << "; n_flushes=" << n_flushes << "\n";
469  str << "\tbinned samples = \n" << samples.block(0,0,num_track_values(),binnedsize);
470  });
471 
472  for (int ksample = 0; ksample < binnedsize; ++ksample) {
473  bin_sumsq.col(level) += samples.col(ksample).cwiseProduct(samples.col(ksample));
474  if (ksample % 2 == 0 && binnedsize > 1) {
475  samples.col(ksample/2) = boost::math::constants::half<ValueType>() *
476  (samples.col(ksample) + samples.col(ksample+1));
477  }
478  }
479 
480  }
481 
482  logger.longdebug("BinningAnalysis::process_new_values()", [&](std::ostream & str) {
483  str << "Flushing #" << n_flushes << " done. bin_sum is = \n" << bin_sum << "\n"
484  << "\tbin_sumsq is = \n" << bin_sumsq << "\n";
485  });
486 
487  ++n_flushes;
488  }
489 
490  }
491 
497  template<typename CalcValType, TOMOGRAPHER_ENABLED_IF_TMPL(NumTrackValuesCTime == 1)>
498  inline void process_new_value(const CalcValType val)
499  {
500  // for a single value
501  //process_new_values(Eigen::Map<const Eigen::Array<CalcValType,1,1> >(&val));
502  process_new_values(Eigen::Array<CalcValType,1,1>::Constant(val));
503  }
504 
505 
506 
507 private:
508 
509  TOMOGRAPHER_ENABLED_IF(StoreBinSums)
510  inline void helper_reset_bin_sum()
511  {
512  bin_sum.value = BinSumArray::Zero(num_track_values());
513  }
514  TOMOGRAPHER_ENABLED_IF(!StoreBinSums)
515  inline void helper_reset_bin_sum() { }
516 
517  TOMOGRAPHER_ENABLED_IF(StoreBinSums)
518  inline void helper_update_bin_sum(const Eigen::Ref<const Eigen::Array<ValueType, NumTrackValuesCTime, 1> > &
519  new_samples)
520  {
521  bin_sum.value += new_samples;
522  }
523  template<typename Derived,
524  TOMOGRAPHER_ENABLED_IF_TMPL(!StoreBinSums)>
525  inline void helper_update_bin_sum(const Eigen::DenseBase<Derived> & ) { }
526 
527 
528 public:
529 
530  // retrieve results.
531 
537  inline CountIntType get_n_flushes() const { return n_flushes; }
538 
542  TOMOGRAPHER_ENABLED_IF(StoreBinSums)
543  inline auto get_bin_means() const
544 #ifndef TOMOGRAPHER_PARSED_BY_DOXYGEN
545  -> decltype(BinSumArray() / ValueType(n_samples))
546 #endif
547  {
548  return bin_sum.value / ValueType(n_samples);
549  }
550 
557  inline auto get_bin_sqmeans() const
558 #ifndef TOMOGRAPHER_PARSED_BY_DOXYGEN
559  -> decltype(
560  bin_sumsq.cwiseQuotient(n_flushes * replicated<NumTrackValuesCTime,1>(
562  .transpose().reverse(),
563  // replicated by:
564  num_track_values(), 1
565  ))
566  )
567 #endif
568  {
569  return bin_sumsq.cwiseQuotient(n_flushes * replicated<NumTrackValuesCTime,1>(
571  .transpose().reverse(),
572  // replicated by:
573  num_track_values(), 1
574  ));
575  }
576 
577 
582  TOMOGRAPHER_ENABLED_IF(StoreBinSums)
583  inline const BinSumArray & get_bin_sum() const { return bin_sum.value; }
584 
591  inline const BinSumSqArray & get_bin_sumsq() const {
592  return bin_sumsq;
593  }
594 
595 
606  template<typename Derived>
607  inline BinSumSqArray calc_error_levels(const Eigen::ArrayBase<Derived> & means) const
608  {
609  eigen_assert(means.rows() == num_track_values());
610  eigen_assert(means.cols() == 1);
611  const int n_levels_plus_one = num_levels()+1;
612  const int n_track_values = num_track_values();
613 
618  return (
619  get_bin_sqmeans() - replicated<1,NumLevelsPlusOneCTime>(
620  means.cwiseProduct(means).template cast<ValueType>(),
621  // replicated by:
622  1, n_levels_plus_one
623  )
624  ).cwiseMax(0).cwiseQuotient(
625  // divide by the number of samples from which these bin-means were obtained, minus one.
626  replicated<NumTrackValuesCTime,1>(
628  .transpose().reverse(),
629  // replicated by:
630  n_track_values, 1
631  ) * n_flushes
633  num_track_values(), num_levels()+1,
634  1 // the constant...
635  )
636  ).cwiseSqrt();
637  }
638 
639 
651  template<typename Derived>
652  inline BinSumArray calc_error_lastlevel(const Eigen::ArrayBase<Derived> & means) const {
653  eigen_assert(means.rows() == num_track_values());
654  eigen_assert(means.cols() == 1);
655  return (
656  bin_sumsq.col(num_levels()) / ValueType(n_flushes) - means.cwiseProduct(means).template cast<ValueType>()
657  ).cwiseMax(0).cwiseSqrt() / std::sqrt(ValueType(n_flushes-1));
658  }
659 
671  TOMOGRAPHER_ENABLED_IF(StoreBinSums)
672  inline BinSumSqArray calc_error_levels() const {
673  BinSumArray means = get_bin_means();
674  return calc_error_levels(means);
675  }
676 
689  TOMOGRAPHER_ENABLED_IF(StoreBinSums)
690  inline BinSumArray calc_error_lastlevel() const {
691  BinSumArray means = get_bin_means();
692  return calc_error_lastlevel(means);
693  }
694 
707  inline Eigen::ArrayXi determine_error_convergence(const Eigen::Ref<const BinSumSqArray> & error_levels) const
708  {
709  Eigen::ArrayXi converged_status(num_track_values()); // RVO will help
710 
711  eigen_assert(error_levels.rows() == num_track_values());
712  eigen_assert(error_levels.cols() == num_levels() + 1);
713 
714  logger.longdebug("BinningAnalysis::determine_error_convergence", [&](std::ostream & str) {
715  str << "error_levels = \n" << error_levels << "\n";
716  });
717 
718  // verify that indeed the errors have converged. Inspired from ALPS code, see
719  // https://alps.comp-phys.org/svn/alps1/trunk/alps/src/alps/alea/simplebinning.h
720 
721  const int range = 4;
722  if (num_levels() < range-1) {
723 
724  converged_status = Eigen::ArrayXi::Constant(num_track_values(), UNKNOWN_CONVERGENCE);
725 
726  } else {
727 
728  converged_status = Eigen::ArrayXi::Constant(num_track_values(), CONVERGED);
729 
730  const auto & errors = error_levels.col(num_levels());
731 
732  for (int level = num_levels()+1 - range; level < num_levels(); ++level) {
733 
734  const auto & errors_thislevel = error_levels.col(level);
735 
736  logger.longdebug("BinningAnalysis::determine_error_convergence", [&](std::ostream & str) {
737  str << "About to study level " << level << ": at this point, converged_status = \n"
738  << converged_status << "\nand errors_thislevel = \n" << errors_thislevel;
739  });
740 
741  for (int val_it = 0; val_it < num_track_values(); ++val_it) {
742  if (errors_thislevel(val_it) >= errors(val_it) &&
743  converged_status(val_it) != NOT_CONVERGED) {
744  converged_status(val_it) = CONVERGED;
745  } else if (errors_thislevel(val_it) < 0.824 * errors(val_it)) {
746  converged_status(val_it) = NOT_CONVERGED;
747  } else if ((errors_thislevel(val_it) < 0.9 * errors(val_it)) &&
748  converged_status(val_it) != NOT_CONVERGED) {
749  converged_status(val_it) = UNKNOWN_CONVERGENCE;
750  }
751  }
752 
753  }
754 
755  }
756 
757  logger.longdebug("BinningAnalysis::determine_error_convergence", [&](std::ostream & str) {
758  str << "Done. converged_status [0=UNNOWN,1=CONVERGED,2=NOT CONVERGED] = \n"
759  << converged_status;
760  });
761 
762  return converged_status;
763  }
764 };
765 
766 
767 
768 } // namespace Tomographer
769 
770 
771 
772 
773 
774 
775 
776 
777 
778 
779 
780 
781 
782 
783 
784 #endif
Eigen::ArrayXi determine_error_convergence(const Eigen::Ref< const BinSumSqArray > &error_levels) const
Attempt to determine if the error bars have converged.
Definition: mhrw_bin_err.h:707
Params::BinSumSqArray BinSumSqArray
Type used to store the sum of squares of values at each binning level. See BinningAnalysisParams::Bin...
Definition: mhrw_bin_err.h:289
CountIntType get_n_flushes() const
Return the number of times the collected samples were flushed.
Definition: mhrw_bin_err.h:537
Base namespace for the Tomographer project.
Definition: dmmhrw.h:51
Group template parameters for BinningAnalysis.
Definition: mhrw_bin_err.h:108
BinSumSqArray calc_error_levels(const Eigen::ArrayBase< Derived > &means) const
Calculate the error bars of samples at different binning levels.
Definition: mhrw_bin_err.h:607
Eigen::Array< ValueType, NumTrackValuesCTime, 1 > BinSumArray
Type used to store the sum of values.
Definition: mhrw_bin_err.h:172
Params::CountIntType CountIntType
Type used to count the number of samples. See BinningAnalysisParams::CountIntType.
Definition: mhrw_bin_err.h:255
The error bars appear to have converged.
Definition: mhrw_bin_err.h:186
auto powers_of_two(IndexTypes...sizes) -> const Eigen::CwiseNullaryOp< tomo_internal::powers_of_two_generator< typename Eigen::internal::traits< Der >::Scalar >, Der >
Expression for a 1-D expression of powers of two.
Definition: util.h:357
const BinSumSqArray & get_bin_sumsq() const
Get the raw sums of the squared values observed, at each binning level.
Definition: mhrw_bin_err.h:591
std::enable_if< std::is_unsigned< X >::value, bool >::type is_positive(const X)
Test whether the given value is positive or zero.
Definition: util.h:96
Params::ValueType ValueType
Type of the value(s) for which we are calculating error bars. See BinningAnalysisParams::ValueType.
Definition: mhrw_bin_err.h:253
ColXpr col(Index i)
const CwiseBinaryOp< internal::scalar_product_op< typename Derived::Scalar, typename OtherDerived::Scalar >, const Derived, const OtherDerived > cwiseProduct(const Eigen::ArrayBase< OtherDerived > &other) const
BinSumArray calc_error_lastlevel(const Eigen::ArrayBase< Derived > &means) const
Calculate the error bar of samples (from the last binning level).
Definition: mhrw_bin_err.h:652
ValueType_ ValueType
Type of the value which we are calculating error bars for. Also the type of the error bars themselves...
Definition: mhrw_bin_err.h:112
const Tools::static_or_dynamic< int, NumTrackValuesCTime > num_track_values
The number of functions being tracked/analyzed.
Definition: mhrw_bin_err.h:300
void reset()
Reset this object and start again as if freshly constructed.
Definition: mhrw_bin_err.h:415
const Tools::static_or_dynamic< int, NumLevelsCTime > num_levels
The number of levels in the binning analysis.
Definition: mhrw_bin_err.h:310
void process_new_value(const CalcValType val)
Process a new value (if we're tracking a single function only)
Definition: mhrw_bin_err.h:498
Params::SamplesArray SamplesArray
Type of the internal buffer for the raw samples. See BinningAnalysisParams::SamplesArray.
Definition: mhrw_bin_err.h:281
Params::BinSumArray BinSumArray
Type used to store the sum of values. See BinningAnalysisParams::BinSumArray.
Definition: mhrw_bin_err.h:285
Simple binning analysis for determining error bars.
Definition: mhrw_bin_err.h:249
LoggerType_ LoggerType
Type of the logger we will be logging debugging messages to.
Definition: mhrw_bin_err.h:329
BinningAnalysis(int num_track_values_, int num_levels_, LoggerType &logger_)
Constructor.
Definition: mhrw_bin_err.h:393
Some C++ utilities, with a tad of C++11 tricks.
Eigen::Array< ValueType, NumTrackValuesCTime, NumLevelsPlusOneCTime > BinSumSqArray
Type used to store the sum of squares of values at each binning level.
Definition: mhrw_bin_err.h:179
constexpr bool is_power_of_two(int N)
Return true if the argument is a power of two, false otherwise.
Definition: util.h:277
void process_new_values(const Eigen::DenseBase< Derived > &vals)
Process new raw samples.
Definition: mhrw_bin_err.h:435
T sqrt(T...args)
Eigen::Array< ValueType, NumTrackValuesCTime, SamplesSizeCTime > SamplesArray
Type of the internal buffer for the raw samples.
Definition: mhrw_bin_err.h:164
const Tools::static_or_dynamic< CountIntType, SamplesSizeCTime > samples_size
The size of our samples buffer. (avoid using, might change in the future)
Definition: mhrw_bin_err.h:316
STL class.
CountIntType_ CountIntType
Type used to count the number of samples. Usually int is fine, except if you are really taking a LOT ...
Definition: mhrw_bin_err.h:115
Basic utilities for dealing with Eigen matrices and other types.
Utilities for logging messages.
auto get_bin_sqmeans() const
Get the raw average of the squared values observed, for each binning level.
Definition: mhrw_bin_err.h:557