28 #ifndef TOMOGRAPHER_MHRW_BIN_ERR_H 29 #define TOMOGRAPHER_MHRW_BIN_ERR_H 37 #include <boost/math/constants/constants.hpp> 51 namespace tomo_internal {
55 template<
int NumLevels,
bool calculate>
56 struct helper_samples_size {
58 value = Eigen::Dynamic
61 template<
int NumLevels>
62 struct helper_samples_size<NumLevels,true> {
64 value = (1 << NumLevels)
109 Eigen::Index n_converged_ = 0,
110 Eigen::Index n_unknown_ = 0,
111 Eigen::Index n_unknown_isolated_ = 0,
112 Eigen::Index n_not_converged_ = 0)
114 n_converged(n_converged_),
115 n_unknown(n_unknown_),
116 n_unknown_isolated(n_unknown_isolated_),
117 n_not_converged(n_not_converged_)
122 Eigen::Index n_converged;
123 Eigen::Index n_unknown;
124 Eigen::Index n_unknown_isolated;
125 Eigen::Index n_not_converged;
130 static BinningErrorBarConvergenceSummary
133 const Eigen::Index n_bins = converged_status.size();
135 const Eigen::ArrayXi unkn_arr =
137 const Eigen::ArrayXi conv_arr =
140 const Eigen::Index n_unknown = unkn_arr.cast<Eigen::Index>().count();
141 const Eigen::Index n_converged = conv_arr.cast<Eigen::Index>().count();
142 const Eigen::Index n_not_converged =
148 const Eigen::Index n_unknown_isol = (
149 ( unkn_arr.segment(1,n_bins-2)
150 .cwiseProduct(conv_arr.segment(0,n_bins-2))
151 .cwiseProduct(conv_arr.segment(2,n_bins-2))
154 + unkn_arr(0)*conv_arr(1) + unkn_arr(n_bins-1)*conv_arr(n_bins-2)
157 return BinningErrorBarConvergenceSummary(n_bins, n_converged, n_unknown, n_unknown_isol, n_not_converged);
164 return stream << s.n_converged <<
" converged / " 165 << s.n_unknown <<
" maybe (" << s.n_unknown_isolated <<
" isolated) / " 166 << s.n_not_converged <<
" not converged";
210 template<
typename ValueType_,
int NumTrackValues_ = Eigen::Dynamic,
int NumLevels_ = Eigen::Dynamic,
211 bool StoreBinSums_ =
true,
typename CountIntType_ =
int>
224 static constexpr
int NumTrackValuesCTime = NumTrackValues_;
228 static constexpr
int NumLevelsCTime = NumLevels_;
232 static constexpr
int NumLevelsPlusOneCTime = (NumLevelsCTime == Eigen::Dynamic
234 : (NumLevelsCTime + 1));
257 static constexpr
int SamplesSizeCTime =
265 static constexpr
bool StoreBinSums = StoreBinSums_;
360 template<
typename Params,
typename LoggerType_>
376 static constexpr
int NumTrackValuesCTime = Params::NumTrackValuesCTime;
380 static constexpr
int NumLevelsCTime = Params::NumLevelsCTime;
384 static constexpr
int NumLevelsPlusOneCTime = Params::NumLevelsPlusOneCTime;
388 static constexpr
int SamplesSizeCTime = Params::SamplesSizeCTime;
392 static constexpr
bool StoreBinSums = Params::StoreBinSums;
466 SamplesArray samples;
474 CountIntType n_samples;
480 CountIntType n_flushes;
501 BinSumSqArray bin_sumsq;
521 : numTrackValues(num_track_values_),
522 numLevels(num_levels_),
523 samplesSize(1 << numLevels()),
524 samples((
Eigen::Index)numTrackValues(), (
Eigen::Index)samplesSize()),
526 bin_sum(BinSumArray::Zero((
Eigen::Index)numTrackValues())),
527 bin_sumsq(BinSumSqArray::Zero((
Eigen::Index)numTrackValues(), (
Eigen::Index)numLevels()+1)),
546 helper_reset_bin_sum();
547 bin_sumsq = BinSumSqArray::Zero((Eigen::Index)numTrackValues(), (Eigen::Index)numLevels()+1);
548 logger.longdebug(
"BinningAnalysis::reset()",
"ready to go.");
561 template<
typename Derived>
564 const CountIntType ninbin = n_samples % samplesSize();
572 samples.col(ninbin) = vals;
575 helper_update_bin_sum(samples.col(ninbin));
578 if ( ninbin == samplesSize() - 1 ) {
582 logger.longdebug(
"BinningAnalysis::processNewValues()", [&](
std::ostream & str) {
583 str <<
"n_samples is now " << n_samples <<
"; flushing bins. samplesSize() = " << samplesSize();
589 for (
int level = 0; level <= numLevels(); ++level) {
591 const CountIntType binnedsize = CountIntType(1) << (numLevels()-level);
593 logger.longdebug(
"BinningAnalysis::processNewValues()", [&](
std::ostream & str) {
594 str <<
"Processing binning level = " << level <<
": binnedsize="<<binnedsize
595 <<
"; n_flushes=" << n_flushes <<
"\n";
596 str <<
"\tbinned samples = \n" 597 << samples.block(0,0,(Eigen::Index)numTrackValues(),(Eigen::Index)binnedsize);
600 for (CountIntType ksample = 0; ksample < binnedsize; ++ksample) {
601 bin_sumsq.col(level) += samples.col(ksample).cwiseProduct(samples.col(ksample));
602 if (ksample % 2 == 0 && binnedsize > 1) {
603 samples.col(ksample/2) = boost::math::constants::half<ValueType>() *
604 (samples.col(ksample) + samples.col(ksample+1));
610 logger.longdebug(
"BinningAnalysis::processNewValues()", [&](
std::ostream & str) {
611 str <<
"Flushing #" << n_flushes <<
" done. bin_sum is = \n" << bin_sum <<
"\n" 612 <<
"\tbin_sumsq is = \n" << bin_sumsq <<
"\n";
625 template<
typename CalcValType, TOMOGRAPHER_ENABLED_IF_TMPL(NumTrackValuesCTime == 1)>
649 return samplesSize();
655 TOMOGRAPHER_ENABLED_IF(StoreBinSums)
656 inline void helper_reset_bin_sum()
658 bin_sum.value = BinSumArray::Zero((Eigen::Index)numTrackValues());
660 TOMOGRAPHER_ENABLED_IF(!StoreBinSums)
661 inline void helper_reset_bin_sum() { }
663 TOMOGRAPHER_ENABLED_IF(StoreBinSums)
667 bin_sum.value += new_samples;
669 template<
typename Derived,
670 TOMOGRAPHER_ENABLED_IF_TMPL(!StoreBinSums)>
688 TOMOGRAPHER_ENABLED_IF(StoreBinSums)
689 inline auto getBinMeans() const
690 #ifndef TOMOGRAPHER_PARSED_BY_DOXYGEN 691 -> decltype(BinSumArray() / ValueType(n_samples))
694 return bin_sum.value / ValueType(n_samples);
704 #ifndef TOMOGRAPHER_PARSED_BY_DOXYGEN 706 bin_sumsq.cwiseQuotient(n_flushes * Tools::replicated<NumTrackValuesCTime,1>(
708 .transpose().reverse(),
715 return bin_sumsq.cwiseQuotient(n_flushes * Tools::replicated<NumTrackValuesCTime,1>(
717 .transpose().reverse(),
728 TOMOGRAPHER_ENABLED_IF(StoreBinSums)
729 inline const BinSumArray & getBinSum()
const {
return bin_sum.value; }
752 template<
typename Derived>
757 const int n_levels_plus_one = numLevels()+1;
758 const int n_track_values = numTrackValues();
765 getBinSqmeans() - Tools::replicated<1,NumLevelsPlusOneCTime>(
770 ).cwiseMax(0).cwiseQuotient(
772 Tools::replicated<NumTrackValuesCTime,1>(
774 .transpose().reverse(),
779 (Eigen::Index)numTrackValues(), (Eigen::Index)n_levels_plus_one,
797 template<
typename Derived>
802 bin_sumsq.col((Eigen::Index)numLevels()) / ValueType(n_flushes)
804 ).cwiseMax(0).cwiseSqrt() /
std::sqrt(ValueType(n_flushes-1));
818 TOMOGRAPHER_ENABLED_IF(StoreBinSums)
819 inline BinSumSqArray calcErrorLevels()
const {
820 BinSumArray means = getBinMeans();
821 return calcErrorLevels(
std::move(means));
836 TOMOGRAPHER_ENABLED_IF(StoreBinSums)
837 inline BinSumArray calcErrorLastLevel()
const {
838 BinSumArray means = getBinMeans();
839 return calcErrorLastLevel(
std::move(means));
856 Eigen::ArrayXi converged_status(numTrackValues());
861 logger.longdebug(
"BinningAnalysis::determineErrorConvergence", [&](
std::ostream & str) {
862 str <<
"error_levels = \n" << error_levels <<
"\n";
869 if (numLevels() < range-1) {
871 converged_status = Eigen::ArrayXi::Constant((Eigen::Index)numTrackValues(), UNKNOWN_CONVERGENCE);
875 converged_status = Eigen::ArrayXi::Constant((Eigen::Index)numTrackValues(), CONVERGED);
877 const auto & errors = error_levels.col((Eigen::Index)numLevels());
879 for (
int level = numLevels()+1 - range; level < numLevels(); ++level) {
881 const auto & errors_thislevel = error_levels.col((Eigen::Index)level);
883 logger.longdebug(
"BinningAnalysis::determineErrorConvergence", [&](
std::ostream & str) {
884 str <<
"About to study level " << level <<
": at this point, converged_status = \n" 885 << converged_status <<
"\nand errors_thislevel = \n" << errors_thislevel;
888 for (Eigen::Index val_it = 0; val_it < (Eigen::Index)numTrackValues(); ++val_it) {
889 if (errors_thislevel(val_it) >= errors(val_it) &&
890 converged_status(val_it) != NOT_CONVERGED) {
891 converged_status(val_it) = CONVERGED;
892 }
else if (errors_thislevel(val_it) < 0.824 * errors(val_it)) {
893 converged_status(val_it) = NOT_CONVERGED;
894 }
else if ((errors_thislevel(val_it) < 0.9 * errors(val_it)) &&
895 converged_status(val_it) != NOT_CONVERGED) {
896 converged_status(val_it) = UNKNOWN_CONVERGENCE;
904 logger.longdebug(
"BinningAnalysis::determineErrorConvergence", [&](
std::ostream & str) {
905 str <<
"Done. converged_status [0=UNNOWN,1=CONVERGED,2=NOT CONVERGED] = \n" 909 return converged_status;
933 template<
typename IterCountIntType,
typename LocalLoggerType>
935 IterCountIntType n_run,
936 IterCountIntType samples_last_level,
937 LocalLoggerType & logger)
939 if (binning_num_levels > 0) {
941 if (binning_num_levels < 4) {
943 stream <<
"You are using few binning levels = " << binning_num_levels
944 <<
" and the resulting error bars might not be reliable." ;
950 binning_num_levels = (int)(
954 if (binning_num_levels < 1) {
955 binning_num_levels = 1;
957 if (binning_num_levels < 4) {
959 stream <<
"Because n_run is small, you will be using few binning levels = " 960 << binning_num_levels <<
", and the resulting error bars " 961 <<
"might not be reliable." ;
965 const IterCountIntType binning_last_level_num_samples =
966 (IterCountIntType)
std::ldexp((
double)n_run, - binning_num_levels);
969 stream <<
"Binning analysis: " << binning_num_levels <<
" levels, with " 970 << binning_last_level_num_samples <<
" samples at last level";
974 if (binning_last_level_num_samples < samples_last_level) {
976 stream <<
"The number of samples (" << binning_last_level_num_samples
977 <<
") at the last binning level is below the recommended value (" 978 << samples_last_level <<
"). Consider increasing n_run " 979 <<
"or decreasing binning_num_levels.";
982 return binning_num_levels;
It is unknown whether the error bar has converged or not.
void processNewValue(const CalcValType val)
Process a new value (if we're tracking a single function only)
int sanitizeBinningLevels(int binning_num_levels, IterCountIntType n_run, IterCountIntType samples_last_level, LocalLoggerType &logger)
Utility for sanitizing/determining number of binning levels.
Params::BinSumSqArray BinSumSqArray
Type used to store the sum of squares of values at each binning level. See BinningAnalysisParams::Bin...
static BinningErrorBarConvergenceSummary fromConvergedStatus(const Eigen::Ref< const Eigen::ArrayXi > &converged_status)
Construct a summary object from a list of converged status obtained from BinningAnalysis::determineEr...
auto getBinSqmeans() const
Get the raw average of the squared values observed, for each binning level.
Base namespace for the Tomographer project.
The error bar appears to have converged.
Group template parameters for BinningAnalysis.
Eigen::ArrayXi determineErrorConvergence(const Eigen::Ref< const BinSumSqArray > &error_levels) const
Attempt to determine if the error bars have converged.
Eigen::Array< ValueType, NumTrackValuesCTime, 1 > BinSumArray
Type used to store the sum of values.
Params::CountIntType CountIntType
Type used to count the number of samples. See BinningAnalysisParams::CountIntType.
Eigen::Index effectiveSampleSize() const
Number of samples required to flush all levels of the binning analysis.
void processNewValues(const Eigen::DenseBase< Derived > &vals)
Process new raw samples.
const Tools::StaticOrDynamic< int,(NumLevelsCTime==Eigen::Dynamic), NumLevelsCTime > numLevels
The number of levels in the binning analysis.
Params::ValueType ValueType
Type of the value(s) for which we are calculating error bars. See BinningAnalysisParams::ValueType.
const Tools::StaticOrDynamic< CountIntType,(SamplesSizeCTime==Eigen::Dynamic),(SamplesSizeCTime< 0 ? 0 :SamplesSizeCTime)> samplesSize
The size of our samples buffer. (avoid using, might change in the future)
CountIntType getNumFlushes() const
Return the number of times the collected samples were flushed.
const CwiseBinaryOp< internal::scalar_product_op< typename Derived::Scalar, typename OtherDerived::Scalar >, const Derived, const OtherDerived > cwiseProduct(const Eigen::ArrayBase< OtherDerived > &other) const
ValueType_ ValueType
Type of the value which we are calculating error bars for. Also the type of the error bars themselves...
void reset()
Reset this object and start again as if freshly constructed.
BinSumSqArray calcErrorLevels(const Eigen::ArrayBase< Derived > &means) const
Calculate the error bars of samples at different binning levels.
Basic utilities for dealing with Eigen matrices and other types.
Params::SamplesArray SamplesArray
Type of the internal buffer for the raw samples. See BinningAnalysisParams::SamplesArray.
Params::BinSumArray BinSumArray
Type used to store the sum of values. See BinningAnalysisParams::BinSumArray.
Some C++ utilities, with a tad of C++11 tricks.
Simple binning analysis for determining error bars.
LoggerType_ LoggerType
Type of the logger we will be logging debugging messages to.
BinningAnalysis(int num_track_values_, int num_levels_, LoggerType &logger_)
Constructor.
Managing the need for specific overrides to operator new() for some types (especially Eigen types) ...
The error bar appears NOT to have converged.
Eigen::Array< ValueType, NumTrackValuesCTime, NumLevelsPlusOneCTime > BinSumSqArray
Type used to store the sum of squares of values at each binning level.
VarValueDecoder< T >::RetType value(const Var &var)
Access the value of the given variable, as a C++ type.
Brief summary of convergence of error bars from a binning analysis.
const BinSumSqArray & getBinSumsq() const
Get the raw sums of the squared values observed, at each binning level.
Eigen::Array< ValueType, NumTrackValuesCTime, SamplesSizeCTime > SamplesArray
Type of the internal buffer for the raw samples.
BinSumArray calcErrorLastLevel(const Eigen::ArrayBase< Derived > &means) const
Calculate the error bar of samples (from the last binning level).
BinningConvergence
Convergence status of binning error bar.
CountIntType_ CountIntType
Type used to count the number of samples. Usually int is fine, except if you are really taking a LOT ...
#define tomographer_assert(...)
Assertion test macro.
const Tools::StaticOrDynamic< int,(NumTrackValuesCTime==Eigen::Dynamic), NumTrackValuesCTime > numTrackValues
The number of functions being tracked/analyzed.
Utilities for logging messages.