File indexing completed on 2026-09-18 09:34:44
0001
0002
0003
0004
0005
0006 #ifndef YODA_BinnedDbn_h
0007 #define YODA_BinnedDbn_h
0008
0009 #include "YODA/AnalysisObject.h"
0010 #include "YODA/Fillable.h"
0011 #include "YODA/FillableStorage.h"
0012 #include "YODA/Dbn.h"
0013 #include "YODA/BinnedEstimate.h"
0014 #include "YODA/Scatter.h"
0015
0016 #ifdef HAVE_HDF5
0017 #include "YODA/Utils/H5Utils.h"
0018 #endif
0019
0020 #include <memory>
0021 #include <utility>
0022 #include <iostream>
0023 #include <iomanip>
0024
0025 namespace YODA {
0026
0027
0028
0029
0030
0031
0032
0033
0034
0035
0036
0037
0038
0039
0040
0041
0042
0043
0044
0045
0046
0047
0048
0049
0050 template <size_t DbnN, typename... AxisT>
0051 class DbnStorage;
0052
0053
0054 template <size_t DbnN, typename... AxisT>
0055 class BinnedDbn : public DbnStorage<DbnN, AxisT...> {
0056 public:
0057 using HistoT = BinnedDbn<DbnN, AxisT...>;
0058 using BaseT = DbnStorage<DbnN, AxisT...>;
0059 using FillType = typename BaseT::FillType;
0060 using BinType = typename BaseT::BinT;
0061 using Ptr = std::shared_ptr<HistoT>;
0062
0063
0064 using BaseT::BaseT;
0065
0066 BinnedDbn() = default;
0067 BinnedDbn(const HistoT&) = default;
0068 BinnedDbn(HistoT&&) = default;
0069 BinnedDbn& operator =(const HistoT&) = default;
0070 BinnedDbn& operator =(HistoT&&) = default;
0071 using AnalysisObject::operator =;
0072
0073
0074
0075
0076 BinnedDbn(const BaseT& other) : BaseT(other) {}
0077
0078 BinnedDbn(const HistoT& other, const std::string& path) : BaseT(other, path) {}
0079
0080
0081 BinnedDbn(BaseT&& other) : BaseT(std::move(other)) {}
0082
0083 BinnedDbn(HistoT&& other, const std::string& path) : BaseT(std::move(other), path) {}
0084
0085
0086 HistoT clone() const noexcept {
0087 return HistoT(*this);
0088 }
0089
0090
0091 HistoT* newclone() const noexcept {
0092 return new HistoT(*this);
0093 }
0094
0095 };
0096
0097
0098
0099
0100
0101 template <typename... AxisTypes>
0102 using BinnedHisto = BinnedDbn<sizeof...(AxisTypes), AxisTypes...>;
0103
0104 template <typename... AxisTypes>
0105 using BinnedProfile = BinnedDbn<sizeof...(AxisTypes)+1, AxisTypes...>;
0106
0107
0108
0109
0110
0111
0112
0113
0114
0115
0116
0117
0118
0119 template <size_t DbnN, typename... AxisT>
0120 class DbnStorage : public FillableStorage<DbnN, Dbn<DbnN>, AxisT...>,
0121 public AnalysisObject, public Fillable {
0122 public:
0123
0124 using BaseT = FillableStorage<DbnN, Dbn<DbnN>, AxisT...>;
0125 using BinningT = typename BaseT::BinningT;
0126 using BinT = typename BaseT::BinT;
0127 using BinType = typename BaseT::BinT;
0128 using FillType = typename BaseT::FillType;
0129 using AnalysisObject::operator =;
0130
0131
0132
0133
0134
0135
0136
0137
0138 DbnStorage() : BaseT(), AnalysisObject(mkTypeString<DbnN, AxisT...>(), "") { }
0139
0140
0141
0142
0143
0144
0145 DbnStorage(std::vector<AxisT>&&... binsEdges,
0146 const std::string& path = "", const std::string& title = "")
0147 : BaseT(Axis<AxisT>(std::move(binsEdges))...),
0148 AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0149
0150
0151
0152
0153
0154
0155 DbnStorage(const std::vector<AxisT>&... binsEdges,
0156 const std::string& path = "", const std::string& title = "")
0157 : BaseT(Axis<AxisT>(binsEdges)...),
0158 AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0159
0160
0161
0162
0163
0164
0165 DbnStorage(std::initializer_list<AxisT>&&... binsEdges,
0166 const std::string& path = "", const std::string& title = "")
0167 : BaseT(Axis<AxisT>(std::move(binsEdges))...),
0168 AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0169
0170
0171
0172
0173
0174 template <typename EdgeT = double, typename = enable_if_all_CAxisT<EdgeT, AxisT...>>
0175 DbnStorage(const std::vector<size_t>& nBins, const std::vector<std::pair<EdgeT, EdgeT>>& limitsLowUp,
0176 const std::string& path = "", const std::string& title = "")
0177 : BaseT( _mkBinning(nBins, limitsLowUp, std::make_index_sequence<sizeof...(AxisT)>{}) ),
0178 AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0179
0180
0181 DbnStorage(const Axis<AxisT>&... axes, const std::string& path = "", const std::string& title = "")
0182 : BaseT(axes...), AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0183
0184
0185 DbnStorage(Axis<AxisT>&&... axes, const std::string& path = "", const std::string& title = "")
0186 : BaseT(std::move(axes)...), AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0187
0188
0189 DbnStorage(const BinningT& binning, const std::string& path = "", const std::string& title = "")
0190 : BaseT(binning), AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0191
0192
0193 DbnStorage(BinningT&& binning, const std::string& path = "", const std::string& title = "")
0194 : BaseT(std::move(binning)), AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0195
0196
0197 template <typename EdgeT = double, typename = enable_if_all_CAxisT<EdgeT, AxisT...>>
0198 DbnStorage(const ScatterND<sizeof...(AxisT)+1>& s, const std::string& path = "", const std::string& title = "")
0199 : BaseT(_mkBinning(s, std::make_index_sequence<sizeof...(AxisT)>{})),
0200 AnalysisObject(mkTypeString<DbnN, AxisT...>(), path, title) { }
0201
0202
0203
0204
0205 DbnStorage(const DbnStorage& other, const std::string& path = "") : BaseT(other),
0206 AnalysisObject(mkTypeString<DbnN, AxisT...>(), path!=""? path : other.path(), other, other.title()) { }
0207
0208
0209
0210
0211 DbnStorage(DbnStorage&& other, const std::string& path = "") : BaseT(std::move(other)),
0212 AnalysisObject(mkTypeString<DbnN, AxisT...>(), path!=""? path : other.path(), other, other.title()) { }
0213
0214
0215 DbnStorage clone() const noexcept {
0216 return DbnStorage(*this);
0217 }
0218
0219
0220 DbnStorage* newclone() const noexcept {
0221 return new DbnStorage(*this);
0222 }
0223
0224
0225
0226
0227
0228
0229
0230
0231
0232
0233
0234 virtual int fill(FillType&& coords, const double weight = 1.0, const double fraction = 1.0) {
0235 return BaseT::fill(std::move(coords), std::make_index_sequence<sizeof...(AxisT)>{}, weight, fraction);
0236 }
0237
0238
0239 void scaleW(const double scalefactor) noexcept {
0240 setAnnotation("ScaledBy", annotation<double>("ScaledBy", 1.0) * scalefactor);
0241 for (auto& bin : BaseT::_bins) {
0242 bin.scaleW(scalefactor);
0243 }
0244 }
0245
0246
0247 void scale(const size_t i, const double scalefactor) noexcept {
0248 setAnnotation("ScaledBy", annotation<double>("ScaledBy", 1.0) * scalefactor);
0249 for (auto& bin : BaseT::_bins) {
0250 bin.scale(i, scalefactor);
0251 }
0252 }
0253
0254
0255
0256
0257
0258
0259
0260 void normalize(const double normto=1.0, const bool includeOverflows=true) {
0261 const double oldintegral = integral(includeOverflows);
0262 if (oldintegral == 0) throw WeightError("Attempted to normalize a histogram with null area");
0263 scaleW(normto / oldintegral);
0264 }
0265
0266
0267
0268
0269
0270
0271
0272
0273
0274
0275
0276
0277 template <size_t axisN, typename = std::enable_if_t<BinningT::template is_CAxis<axisN>::value>>
0278 void rebinBy(unsigned int n, size_t begin=1, size_t end=UINT_MAX) {
0279 if (n < 1) throw UserError("Rebinning requested in groups of 0!");
0280 if (!begin) throw UserError("Visible bins start with index 1!");
0281 if (end > BaseT::numBinsAt(axisN)+1) end = BaseT::numBinsAt(axisN) + 1;
0282 for (size_t m = begin; m < end; ++m) {
0283 if (m > BaseT::numBinsAt(axisN)+1) break;
0284 const size_t myend = (m+n-1 < BaseT::numBinsAt(axisN)+1) ? m+n-1 : BaseT::numBinsAt(axisN);
0285 if (myend > m) {
0286 BaseT::template mergeBins<axisN>({m, myend});
0287 end -= myend-m;
0288 }
0289 }
0290 }
0291
0292
0293
0294
0295 template <size_t axisN, typename = std::enable_if_t<BinningT::template is_CAxis<axisN>::value>>
0296 void rebin(unsigned int n, size_t begin=1, size_t end=UINT_MAX) {
0297 rebinBy<axisN>(n, begin, end);
0298 }
0299
0300
0301
0302
0303 template <size_t axisN, typename = std::enable_if_t<BinningT::template is_CAxis<axisN>::value>>
0304 void rebinTo(const std::vector<typename BinningT::template getAxisT<axisN>::EdgeT>& newedges) {
0305 if (newedges.size() < 2)
0306 throw UserError("Requested rebinning to an edge list which defines no bins");
0307 using thisAxisT = typename BinningT::template getAxisT<axisN>;
0308 using thisEdgeT = typename thisAxisT::EdgeT;
0309
0310 thisAxisT& oldAxis = BaseT::_binning.template axis<axisN>();
0311 const thisAxisT newAxis(newedges);
0312 const std::vector<thisEdgeT> eshared = oldAxis.sharedEdges(newAxis);
0313 if (eshared.size() != newAxis.edges().size())
0314 throw BinningError("Requested rebinning to incompatible edges");
0315
0316 for (size_t begin = 0; begin < eshared.size() - 1; ++begin) {
0317
0318
0319 size_t end = oldAxis.index(eshared[begin+1]) - 1;
0320
0321
0322 if (begin == newAxis.numBins(true)-1) end = oldAxis.numBins(true)-1;
0323
0324 if (end > begin) BaseT::template mergeBins<axisN>({begin, end});
0325 if (eshared.size() == oldAxis.edges().size()) break;
0326 }
0327 }
0328
0329
0330
0331
0332 template <size_t axisN, typename = std::enable_if_t<BinningT::template is_CAxis<axisN>::value>>
0333 void rebin(const std::vector<typename BinningT::template getAxisT<axisN>::EdgeT>& newedges) {
0334 rebinTo<axisN>(newedges);
0335 }
0336
0337
0338 DbnStorage& operator = (const DbnStorage& dbn) noexcept {
0339 if (this != &dbn) {
0340 AnalysisObject::operator = (dbn);
0341 BaseT::operator = (dbn);
0342 }
0343 return *this;
0344 }
0345
0346
0347 DbnStorage& operator = (DbnStorage&& dbn) noexcept {
0348 if (this != &dbn) {
0349 AnalysisObject::operator = (dbn);
0350 BaseT::operator = (std::move(dbn));
0351 }
0352 return *this;
0353 }
0354
0355
0356
0357
0358
0359
0360
0361
0362 DbnStorage& operator += (const DbnStorage& dbn) {
0363 if (*this != dbn)
0364 throw BinningError("Arithmetic operation requires compatible binning!");
0365 if (AO::hasAnnotation("ScaledBy")) AO::rmAnnotation("ScaledBy");
0366 for (size_t i = 0; i < BaseT::numBins(true, true); ++i) {
0367 BaseT::bin(i) += dbn.bin(i);
0368 }
0369 BaseT::maskBins(dbn.maskedBins(), true);
0370 return *this;
0371 }
0372
0373 DbnStorage& operator += (DbnStorage&& dbn) {
0374 if (*this != dbn)
0375 throw BinningError("Arithmetic operation requires compatible binning!");
0376 if (AO::hasAnnotation("ScaledBy")) AO::rmAnnotation("ScaledBy");
0377 for (size_t i = 0; i < BaseT::numBins(true, true); ++i) {
0378 BaseT::bin(i) += std::move(dbn.bin(i));
0379 }
0380 BaseT::maskBins(dbn.maskedBins(), true);
0381 return *this;
0382 }
0383
0384
0385
0386
0387
0388
0389 DbnStorage& operator -= (const DbnStorage& dbn) {
0390 if (*this != dbn)
0391 throw BinningError("Arithmetic operation requires compatible binning!");
0392 if (AO::hasAnnotation("ScaledBy")) AO::rmAnnotation("ScaledBy");
0393 for (size_t i = 0; i < BaseT::numBins(true, true); ++i) {
0394 BaseT::bin(i) -= dbn.bin(i);
0395 }
0396 BaseT::maskBins(dbn.maskedBins(), true);
0397 return *this;
0398 }
0399
0400 DbnStorage& operator -= (DbnStorage&& dbn) {
0401 if (*this != dbn)
0402 throw BinningError("Arithmetic operation requires compatible binning!");
0403 if (AO::hasAnnotation("ScaledBy")) AO::rmAnnotation("ScaledBy");
0404 for (size_t i = 0; i < BaseT::numBins(true, true); ++i) {
0405 BaseT::bin(i) -= std::move(dbn.bin(i));
0406 }
0407 BaseT::maskBins(dbn.maskedBins(), true);
0408 return *this;
0409 }
0410
0411
0412
0413
0414
0415
0416
0417
0418
0419 void reset() noexcept { BaseT::reset(); }
0420
0421
0422
0423
0424
0425
0426
0427 size_t fillDim() const noexcept { return BaseT::fillDim(); }
0428
0429
0430 size_t dim() const noexcept { return sizeof...(AxisT) + 1; }
0431
0432
0433 std::string _config() const noexcept { return mkAxisConfig<AxisT...>(); }
0434
0435
0436
0437
0438
0439
0440
0441
0442 double integral(const bool includeOverflows=true) const noexcept {
0443 return sumW(includeOverflows);
0444 }
0445
0446
0447 double integralError(const bool includeOverflows=true) const noexcept {
0448 return sqrt(sumW2(includeOverflows));
0449 }
0450
0451
0452 double integralTo(const size_t binIndex) const noexcept {
0453 return integralRange(0, binIndex);
0454 }
0455
0456
0457
0458 double integralRange(const size_t binIndex1, size_t binIndex2) const {
0459 assert(binIndex2 >= binIndex1);
0460 if (binIndex1 >= BaseT::numBins(true)) throw RangeError("binindex1 is out of range");
0461 if (binIndex2 >= BaseT::numBins(true)) throw RangeError("binindex2 is out of range");
0462 double sumw = 0;
0463 for (size_t idx = binIndex1; idx <= binIndex2; ++idx) {
0464 if (BaseT::bin(idx).isMasked()) continue;
0465 sumw += BaseT::bin(idx).sumW();
0466 }
0467 return sumw;
0468 }
0469
0470
0471
0472 double integralRangeError(const size_t binIndex1, size_t binIndex2) const {
0473 assert(binIndex2 >= binIndex1);
0474 if (binIndex1 >= BaseT::numBins(true)) throw RangeError("binindex1 is out of range");
0475 if (binIndex2 >= BaseT::numBins(true)) throw RangeError("binindex2 is out of range");
0476 double sumw2 = 0;
0477 for (size_t idx = binIndex1; idx <= binIndex2; ++idx) {
0478 if (BaseT::bin(idx).isMasked()) continue;
0479 sumw2 += BaseT::bin(idx).sumW2();
0480 }
0481 return sumw2;
0482 }
0483
0484
0485 double numEntries(const bool includeOverflows=true) const noexcept {
0486 double n = 0;
0487 for (const auto& b : BaseT::bins(includeOverflows)) {
0488 n += b.numEntries();
0489 }
0490 return n;
0491 }
0492
0493
0494 double effNumEntries(const bool includeOverflows=true) const noexcept {
0495 double n = 0;
0496 for (const auto& b : BaseT::bins(includeOverflows)) {
0497 n += b.effNumEntries();
0498 }
0499 return n;
0500 }
0501
0502
0503 double sumW(const bool includeOverflows=true) const noexcept {
0504 double sumw = 0;
0505 for (const auto& b : BaseT::bins(includeOverflows)) {
0506 sumw += b.sumW();
0507 }
0508 return sumw;
0509 }
0510
0511
0512 double sumW2(const bool includeOverflows=true) const noexcept {
0513 double sumw2 = 0;
0514 for (const auto& b : BaseT::bins(includeOverflows)) {
0515 sumw2 += b.sumW2();
0516 }
0517 return sumw2;
0518 }
0519
0520
0521 double sumWA(const size_t dim, const bool includeOverflows=true) const {
0522 if (dim >= DbnN) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0523 double sumwa = 0;
0524 for (const auto& b : BaseT::bins(includeOverflows)) {
0525 sumwa += b.sumW(dim+1);
0526 }
0527 return sumwa;
0528 }
0529
0530
0531 double sumWA2(const size_t dim, const bool includeOverflows=true) const {
0532 if (dim >= DbnN) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0533 double sumwa2 = 0;
0534 for (const auto& b : BaseT::bins(includeOverflows)) {
0535 sumwa2 += b.sumW2(dim+1);
0536 }
0537 return sumwa2;
0538 }
0539
0540
0541 template<size_t dim = DbnN, typename = std::enable_if_t<(dim >= 2)>>
0542 double crossTerm(const size_t A1, const size_t A2, const bool includeOverflows=true) const {
0543 if (A1 >= DbnN || A2 >= DbnN) throw RangeError("Invalid axis int, must be in range 0..dim-1");
0544 if (A1 >= A2) throw RangeError("Indices need to be different for cross term");
0545 double sumw = 0;
0546 for (const auto& b : BaseT::bins(includeOverflows)) {
0547 sumw += b.crossTerm(A1, A2);
0548 }
0549 return sumw;
0550 }
0551
0552
0553 std::vector<double> means(size_t axisN, const bool includeOverflows=true) const noexcept {
0554 std::vector<double> rtn;
0555 rtn.reserve( BaseT::numBins(includeOverflows) );
0556 for (const auto& b : BaseT::bins(includeOverflows)) {
0557 rtn.push_back(b.mean(axisN+1));
0558 }
0559 return rtn;
0560 }
0561
0562
0563 double mean(size_t axisN, const bool includeOverflows=true) const noexcept {
0564 Dbn<DbnN> dbn;
0565 for (const auto& b : BaseT::bins(includeOverflows)) {
0566 dbn += b;
0567 }
0568 return dbn.mean(axisN+1);
0569 }
0570
0571
0572 double variance(size_t axisN, const bool includeOverflows=true) const noexcept {
0573 Dbn<DbnN> dbn;
0574 for (const auto& b : BaseT::bins(includeOverflows)) {
0575 dbn += b;
0576 }
0577 return dbn.variance(axisN+1);
0578 }
0579
0580
0581 double stdDev(size_t axisN, const bool includeOverflows=true) const noexcept {
0582 return std::sqrt(variance(axisN, includeOverflows));
0583 }
0584
0585
0586 double stdErr(size_t axisN, const bool includeOverflows=true) const noexcept {
0587 Dbn<DbnN> dbn;
0588 for (const auto& b : BaseT::bins(includeOverflows)) {
0589 dbn += b;
0590 }
0591 return dbn.stdErr(axisN+1);
0592 }
0593
0594
0595 double rms(size_t axisN, const bool includeOverflows=true) const noexcept {
0596 Dbn<DbnN> dbn;
0597 for (const auto& b : BaseT::bins(includeOverflows)) {
0598 dbn += b;
0599 }
0600 return dbn.RMS(axisN+1);
0601 }
0602
0603
0604 double dVol(const bool includeOverflows=true) const noexcept {
0605 double vol = 0.0;
0606 for (const auto& b : BaseT::bins(includeOverflows)) {
0607 vol += b.dVol();
0608 }
0609 return vol;
0610 }
0611
0612
0613 double density(const bool includeOverflows=true) const noexcept {
0614 const double vol = dVol(includeOverflows);
0615 if (vol) return integral(includeOverflows) / vol;
0616 return std::numeric_limits<double>::quiet_NaN();
0617 }
0618
0619
0620 double densityError(const bool includeOverflows=true) const noexcept {
0621 const double vol = dVol(includeOverflows);
0622 if (vol) return integralError(includeOverflows) / vol;
0623 return std::numeric_limits<double>::quiet_NaN();
0624 }
0625
0626
0627 double densitySum(const bool includeOverflows=true) const noexcept {
0628 double rho = 0.0;
0629 for (const auto& b : BaseT::bins(includeOverflows)) {
0630 rho += b.sumW() / b.dVol();
0631 }
0632 return rho;
0633 }
0634
0635
0636 double maxDensity(const bool includeOverflows=true) const noexcept {
0637 std::vector<double> vals;
0638 for (auto& b : BaseT::bins(includeOverflows)) {
0639 vals.emplace_back(b.sumW() / b.dVol());
0640 }
0641 return *max_element(vals.begin(), vals.end());
0642 }
0643
0644
0645
0646
0647
0648
0649 private:
0650
0651
0652 template<size_t... Is>
0653 void _renderYODA_aux(std::ostream& os, const int width, std::index_sequence<Is...>) const noexcept {
0654
0655
0656 if ( effNumEntries(true) > 0 ) {
0657 os << "# Mean: ";
0658 if (DbnN > 1) { os << "("; }
0659 (( os << std::string(Is? ", " : "") << mean(Is, true)), ...);
0660 if (DbnN > 1) { os << ")"; }
0661 os << "\n# Integral: " << integral(true) << "\n";
0662 }
0663
0664
0665 BaseT::_binning._renderYODA(os);
0666
0667
0668 os << std::setw(width) << std::left << "# sumW" << "\t";
0669 os << std::setw(width) << std::left << "sumW2" << "\t";
0670 (( os << std::setw(width) << std::left << ("sumW(A" + std::to_string(Is+1) + ")") << "\t"
0671 << std::setw(width) << std::left << ("sumW2(A" + std::to_string(Is+1) + ")") << "\t"), ...);
0672 if constexpr (DbnN >= 2) {
0673 for (size_t i = 0; i < (DbnN-1); ++i) {
0674 for (size_t j = i+1; j < DbnN; ++j) {
0675 const std::string scross = "sumW(A" + std::to_string(i+1) + ",A" + std::to_string(j+1) + ")";
0676 os << std::setw(width) << std::left << scross << "\t";
0677 }
0678 }
0679 }
0680 os << "numEntries\n";
0681
0682 for (const auto& b : BaseT::bins(true, true)) {
0683 os << std::setw(width) << std::left << b.sumW() << "\t";
0684 os << std::setw(width) << std::left << b.sumW2() << "\t";
0685 ((os << std::setw(width) << std::left << b.sumW( Is+1) << "\t"
0686 << std::setw(width) << std::left << b.sumW2(Is+1) << "\t"), ...);
0687 if constexpr (DbnN >= 2) {
0688 for (size_t i = 0; i < (DbnN-1); ++i) {
0689 for (size_t j = i+1; j < DbnN; ++j) {
0690 os << std::setw(width) << std::left << b.crossTerm(i,j) << "\t";
0691 }
0692 }
0693 }
0694 os << std::setw(width) << std::left << b.numEntries() << "\n";
0695 }
0696 }
0697
0698 public:
0699
0700
0701 void _renderYODA(std::ostream& os, const int width = 13) const noexcept {
0702 _renderYODA_aux(os, width, std::make_index_sequence<DbnN>{});
0703 }
0704
0705
0706 void _renderFLAT(std::ostream& os, const int width = 13) const noexcept {
0707 const ScatterND<sizeof...(AxisT)+1> tmp = mkScatter();
0708 tmp._renderYODA(os, width);
0709 }
0710
0711 #ifdef HAVE_HDF5
0712
0713 void _extractEdges(std::map<std::string, EdgeHandlerBasePtr>& edges,
0714 const std::vector<std::string>&) const noexcept {
0715
0716 using lenT = EdgeHandler<size_t>;
0717 using lenPtr = EdgeHandlerPtr<size_t>;
0718 const std::string lengthID("sizeinfo");
0719 lenPtr nedges = std::static_pointer_cast<lenT>(edges.find(lengthID)->second);
0720
0721 auto extractEdges = [&edges, &binning = BaseT::_binning, &nedges](auto I) {
0722
0723 using thisEdgeT = typename BinningT::template getEdgeT<I>;
0724 using thisHandlerT = EdgeHandler<thisEdgeT>;
0725 using thisHandlerPtr = EdgeHandlerPtr<thisEdgeT>;
0726
0727 const std::string edgeID = std::string("edges_") + TypeID<thisEdgeT>::name();
0728 std::vector<thisEdgeT> tmp = binning.template edges<I>();
0729 nedges->extend({ tmp.size() });
0730
0731 auto itr = edges.find(edgeID);
0732 if (itr != edges.cend()) {
0733 thisHandlerPtr edgeset = std::static_pointer_cast<thisHandlerT>(itr->second);
0734 edgeset->extend(std::move(tmp));
0735 }
0736 else {
0737 edges[edgeID] = std::make_shared<thisHandlerT>(std::move(tmp));
0738 }
0739 };
0740 MetaUtils::staticFor<sizeof...(AxisT)>(extractEdges);
0741
0742 std::vector<size_t> masks = BaseT::_binning.maskedBins();
0743 nedges->extend({ masks.size() });
0744 nedges->extend(std::move(masks));
0745
0746 };
0747 #endif
0748
0749
0750
0751
0752
0753
0754 size_t lengthContent(bool = false) const noexcept {
0755 return BaseT::numBins(true, true) * Dbn<DbnN>::DataSize::value;
0756 }
0757
0758 std::vector<double> serializeContent(bool = false) const noexcept {
0759 std::vector<double> rtn;
0760 const size_t nBins = BaseT::numBins(true, true);
0761 rtn.reserve(nBins * Dbn<DbnN>::DataSize::value);
0762 for (size_t i = 0; i < nBins; ++i) {
0763 std::vector<double> bdata = BaseT::bin(i)._serializeContent();
0764 rtn.insert(std::end(rtn),
0765 std::make_move_iterator(std::begin(bdata)),
0766 std::make_move_iterator(std::end(bdata)));
0767 }
0768 return rtn;
0769 }
0770
0771 void deserializeContent(const std::vector<double>& data) {
0772
0773 constexpr size_t dbnSize = Dbn<DbnN>::DataSize::value;
0774 const size_t nBins = BaseT::numBins(true, true);
0775 if (data.size() != nBins * dbnSize)
0776 throw UserError("Length of serialized data should be "
0777 + std::to_string(nBins * dbnSize)+"!");
0778
0779 const auto itr = data.cbegin();
0780 for (size_t i = 0; i < nBins; ++i) {
0781 auto first = itr + i*dbnSize;
0782 auto last = first + dbnSize;
0783 BaseT::bin(i)._deserializeContent(std::vector<double>{first, last});
0784 }
0785
0786 }
0787
0788
0789
0790
0791
0792
0793
0794
0795
0796
0797
0798
0799 BinnedEstimate<AxisT...> mkEstimate(const std::string& path = "",
0800 const std::string& source = "",
0801 [[maybe_unused]] const bool divbyvol = true,
0802 const double overflowsWidth = -1.0) const {
0803
0804
0805 BinnedEstimate<AxisT...> rtn(BaseT::_binning);
0806 for (const std::string& a : annotations()) {
0807 if (a != "Type") rtn.setAnnotation(a, annotation(a));
0808 }
0809 rtn.setAnnotation("Path", path);
0810
0811 if (BaseT::nanCount()) {
0812 const double nanc = BaseT::nanCount();
0813 const double nanw = BaseT::nanSumW();
0814 const double frac = nanc / (nanc + numEntries());
0815 const double wtot = nanw + effNumEntries();
0816 rtn.setAnnotation("NanFraction", frac);
0817 if (wtot) rtn.setAnnotation("WeightedNanFraction", nanw/wtot);
0818 }
0819
0820 for (const auto& b : BaseT::bins(true, true)) {
0821 if (overflowsWidth <= 0. && !b.isVisible()) continue;
0822 if constexpr(DbnN > sizeof...(AxisT)) {
0823 rtn.bin(b.index()).setVal(b.mean(DbnN));
0824 if (b.numEntries()) {
0825 rtn.bin(b.index()).setErr(b.stdErr(DbnN), source);
0826 }
0827 }
0828 else {
0829 const double scale = divbyvol? (b.isVisible()? b.dVol() : overflowsWidth) : 1.0;
0830 rtn.bin(b.index()).setVal(b.sumW() / scale);
0831 if (b.numEntries()) {
0832 rtn.bin(b.index()).setErr(b.errW() / scale, source);
0833 }
0834 }
0835 }
0836
0837 return rtn;
0838 }
0839
0840
0841
0842
0843
0844
0845
0846
0847 template<size_t axisN, typename = std::enable_if_t< (axisN < sizeof...(AxisT)) >>
0848 auto mkEstimates(const std::string& path = "", const std::string source = "",
0849 const bool divbyvol = true, const bool includeOverflows = false,
0850 const double overflowsWidth = -1.0) {
0851
0852 BinnedEstimate<AxisT...> est = mkEstimate(path, source, divbyvol, overflowsWidth);
0853 return est.template mkEstimates<axisN>(path, includeOverflows);
0854 }
0855
0856
0857
0858
0859
0860
0861 auto mkScatter(const std::string& path="", const bool divbyvol = true,
0862 const bool usefocus = false,
0863 const bool includeOverflows = false,
0864 const bool includeMaskedBins = false,
0865 const double overflowsWidth = -1.0) const {
0866 const BinnedEstimate<AxisT...> est = mkEstimate("", "", divbyvol, overflowsWidth);
0867 ScatterND<sizeof...(AxisT)+1> rtn = est.mkScatter(path, "", includeOverflows, includeMaskedBins);
0868 if (usefocus) {
0869 size_t idx = 0;
0870 for (const auto& b : BaseT::bins(includeOverflows, includeMaskedBins)) {
0871 auto shiftIfContinuous = [&rtn, &b, &idx](auto I) {
0872 using isContinuous = typename BinningT::template is_CAxis<I>;
0873 if constexpr (isContinuous::value) {
0874 const double oldMax = rtn.point(idx).max(I);
0875 const double oldMin = rtn.point(idx).min(I);
0876 const double newVal = b.mean(I+1);
0877 rtn.point(idx).set(I, newVal, newVal - oldMin, oldMax - newVal);
0878 }
0879 };
0880 MetaUtils::staticFor<BinningT::Dimension::value>(shiftIfContinuous);
0881 ++idx;
0882 }
0883 }
0884 return rtn;
0885 }
0886
0887
0888
0889
0890
0891 template<size_t N = DbnN, typename = std::enable_if_t< (N == sizeof...(AxisT)+1) >>
0892 BinnedHisto<AxisT...> mkHisto(const std::string& path="") const {
0893
0894 BinnedHisto<AxisT...> rtn(BaseT::_binning);
0895 rtn.setNanLog(BaseT::nanCount(), BaseT::nanSumW(), BaseT::nanSumW2());
0896 for (const std::string& a : annotations()) {
0897 if (a != "Type") rtn.setAnnotation(a, annotation(a));
0898 }
0899 rtn.setAnnotation("Path", path);
0900
0901 for (const auto& b : BaseT::bins(true)) {
0902 rtn.bin(b.index()) += b.template reduce<N-1>();
0903 }
0904
0905 return rtn;
0906 }
0907
0908
0909
0910
0911
0912
0913
0914
0915
0916
0917
0918
0919 template<size_t axisN, typename = std::enable_if_t< (axisN < sizeof...(AxisT)) >>
0920 auto mkMarginalProfile(const std::string& path="") const {
0921
0922 auto rtn = BaseT::template _mkBinnedT<BinnedProfile>(BaseT::_binning.template _getAxesExcept<axisN>());
0923 rtn.setNanLog(BaseT::nanCount(), BaseT::nanSumW(), BaseT::nanSumW2());
0924 for (const std::string& a : annotations()) {
0925 if (a != "Type") rtn.setAnnotation(a, annotation(a));
0926 }
0927 rtn.setAnnotation("Path", path);
0928
0929 auto collapseStorageBins =
0930 [&oldBinning = BaseT::_binning, &oldBins = BaseT::_bins, &rtn](auto I, auto dbnRed) {
0931
0932 auto collapse = [&oldBins, &rtn](const auto& binsIndicesToMerge, auto axis) {
0933 assert(rtn.numBins(true) == binsIndicesToMerge.size());
0934
0935
0936
0937 for (size_t i = 0; i < rtn.numBins(true); ++i) {
0938 auto& pivotBin = rtn.bin(i);
0939 auto& binToAppend = oldBins[binsIndicesToMerge[i]];
0940 pivotBin += binToAppend.template reduce<axis>();
0941 }
0942 };
0943
0944
0945
0946 ssize_t nBinRowsToBeMerged = oldBinning.numBinsAt(I);
0947 while (nBinRowsToBeMerged--) {
0948
0949
0950 collapse(oldBinning.sliceIndices(I, nBinRowsToBeMerged), dbnRed);
0951 }
0952 };
0953
0954
0955
0956 auto dbnRed = std::integral_constant<size_t, (sizeof...(AxisT) == DbnN)? DbnN : axisN>();
0957 (void)collapseStorageBins(std::integral_constant<std::size_t, axisN>(), dbnRed);
0958
0959 return rtn;
0960 }
0961
0962
0963
0964
0965
0966
0967
0968
0969
0970
0971
0972
0973 template<size_t axisN, typename = std::enable_if_t< (axisN < sizeof...(AxisT)) >>
0974 auto mkMarginalHisto(const std::string& path="") const {
0975
0976 if constexpr (DbnN != sizeof...(AxisT)) {
0977
0978 return mkHisto().template mkMarginalHisto<axisN>(path);
0979 }
0980 else {
0981
0982
0983 auto rtn = BaseT::template _mkBinnedT<BinnedHisto>(BaseT::_binning.template _getAxesExcept<axisN>());
0984 rtn.setNanLog(BaseT::nanCount(), BaseT::nanSumW(), BaseT::nanSumW2());
0985 for (const std::string& a : annotations()) {
0986 if (a != "Type") rtn.setAnnotation(a, annotation(a));
0987 }
0988 rtn.setAnnotation("Path", path);
0989
0990 auto collapseStorageBins =
0991 [&oldBinning = BaseT::_binning, &oldBins = BaseT::_bins, &rtn](auto I, auto dbnRed) {
0992
0993 auto collapse = [&oldBins, &rtn](const auto& binsIndicesToMerge, auto axis) {
0994 assert(rtn.numBins(true) == binsIndicesToMerge.size());
0995
0996
0997
0998 for (size_t i = 0; i < rtn.numBins(true); ++i) {
0999 auto& pivotBin = rtn.bin(i);
1000 auto& binToAppend = oldBins[binsIndicesToMerge[i]];
1001 pivotBin += binToAppend.template reduce<axis>();
1002 }
1003 };
1004
1005
1006
1007 ssize_t nBinRowsToBeMerged = oldBinning.numBinsAt(I);
1008 while (nBinRowsToBeMerged--) {
1009
1010
1011 collapse(oldBinning.sliceIndices(I, nBinRowsToBeMerged), dbnRed);
1012 }
1013 };
1014
1015 auto dbnRed = std::integral_constant<size_t, axisN>();
1016 (void)collapseStorageBins(std::integral_constant<std::size_t, axisN>(), dbnRed);
1017
1018 return rtn;
1019 }
1020 }
1021
1022
1023
1024
1025
1026
1027 template<size_t axisN, typename = std::enable_if_t< (axisN < sizeof...(AxisT) &&
1028 sizeof...(AxisT)>=2 &&
1029 DbnN > sizeof...(AxisT)) >>
1030 auto mkProfiles(const std::string& path="", const bool includeOverflows=false) const {
1031
1032
1033 auto how2add = [](auto& pivot, const BinType& toCopy) { pivot = toCopy.template reduce<axisN>(); };
1034 auto rtn = BaseT::template mkBinnedSlices<axisN, BinnedProfile>(how2add, includeOverflows);
1035 for (const std::string& a : annotations()) {
1036 if (a == "Type") continue;
1037 for (size_t i = 0; i < rtn.size(); ++i) {
1038 rtn[i].setAnnotation(a, annotation(a));
1039 }
1040 }
1041 for (size_t i = 0; i < rtn.size(); ++i) {
1042 rtn[i].setAnnotation("Path", path);
1043 }
1044 return rtn;
1045 }
1046
1047
1048
1049
1050
1051
1052 template<size_t axisN, typename = std::enable_if_t< (axisN < sizeof...(AxisT) && sizeof...(AxisT)>=2) >>
1053 auto mkHistos(const std::string& path="", const bool includeOverflows=false) const {
1054
1055 if constexpr (DbnN != sizeof...(AxisT)) {
1056
1057 return mkHisto().template mkHistos<axisN>(path, includeOverflows);
1058 }
1059 else {
1060
1061
1062
1063 auto how2add = [](auto& pivot, const BinType& toCopy) { pivot = toCopy.template reduce<axisN>(); };
1064 auto rtn = BaseT::template mkBinnedSlices<axisN,BinnedHisto>(how2add, includeOverflows);
1065 for (const std::string& a : annotations()) {
1066 if (a == "Type") continue;
1067 for (size_t i = 0; i < rtn.size(); ++i) {
1068 rtn[i].setAnnotation(a, annotation(a));
1069 }
1070 }
1071 for (size_t i = 0; i < rtn.size(); ++i) {
1072 rtn[i].setAnnotation("Path", path);
1073 }
1074 return rtn;
1075 }
1076 }
1077
1078
1079
1080
1081
1082
1083
1084 BinnedEstimate<AxisT...> mkBinnedEffNumEntries(const std::string& path="",
1085 const std::string& source = "",
1086 const bool includeOverflows = true,
1087 const bool divbyvol = true,
1088 const double overflowsWidth = -1.0) {
1089
1090 BinnedEstimate<AxisT...> rtn = mkEstimate(path);
1091
1092 for (const auto& b : BaseT::bins(includeOverflows)) {
1093 double scale = 1.0;
1094 if (divbyvol) {
1095 scale = (overflowsWidth > 0. && !b.isVisible())? overflowsWidth : b.dVol();
1096 }
1097 const double effN = b.effNumEntries() / scale;
1098 const double err = effN * b.relErrW() / scale;
1099 rtn.bin(b.index()).set(effN, {-err, err}, source);
1100 }
1101
1102 return rtn;
1103 }
1104
1105
1106
1107 AnalysisObject* mkInert(const std::string& path = "",
1108 const std::string& source = "") const noexcept {
1109 return mkEstimate(path, source).newclone();
1110 }
1111
1112
1113
1114 private:
1115
1116
1117
1118 template<size_t... Is>
1119 BinningT _mkBinning(const std::vector<size_t>& nBins,
1120 const std::vector<std::pair<double, double>>& limitsLowUp,
1121 std::index_sequence<Is...>) const {
1122 return BinningT({((void)Is, Axis<AxisT>(nBins[Is], limitsLowUp[Is].first, limitsLowUp[Is].second))...});
1123 }
1124
1125
1126 template<size_t... Is>
1127 BinningT _mkBinning(const ScatterND<sizeof...(AxisT)+1>& s, std::index_sequence<Is...>) const {
1128 return BinningT(Axis<AxisT>(s.edges(Is))...);
1129 }
1130
1131 };
1132
1133
1134
1135
1136
1137
1138
1139 template<size_t DbnN, typename... AxisT>
1140 inline BinnedDbn<DbnN, AxisT...>
1141 operator + (BinnedDbn<DbnN, AxisT...> first, BinnedDbn<DbnN, AxisT...>&& second) {
1142 first += std::move(second);
1143 return first;
1144 }
1145
1146 template <size_t DbnN, typename... AxisT>
1147 inline BinnedDbn<DbnN, AxisT...>
1148 operator + (BinnedDbn<DbnN, AxisT...> first, const BinnedDbn<DbnN, AxisT...>& second) {
1149 first += second;
1150 return first;
1151 }
1152
1153
1154
1155 template <size_t DbnN, typename... AxisT>
1156 inline BinnedDbn<DbnN, AxisT...>
1157 operator - (BinnedDbn<DbnN, AxisT...> first, BinnedDbn<DbnN, AxisT...>&& second) {
1158 first -= std::move(second);
1159 return first;
1160 }
1161
1162 template <size_t DbnN, typename... AxisT>
1163 inline BinnedDbn<DbnN, AxisT...>
1164 operator - (BinnedDbn<DbnN, AxisT...> first, const BinnedDbn<DbnN, AxisT...>& second) {
1165 first -= second;
1166 return first;
1167 }
1168
1169
1170
1171 template <size_t DbnN, typename... AxisT>
1172 inline BinnedEstimate<AxisT...>
1173 divide(const BinnedDbn<DbnN, AxisT...>& numer, const BinnedDbn<DbnN, AxisT...>& denom) {
1174
1175 if (numer != denom) {
1176 throw BinningError("Arithmetic operation requires compatible binning!");
1177 }
1178
1179 BinnedEstimate<AxisT...> rtn = numer.mkEstimate();
1180 if (numer.path() == denom.path()) rtn.setPath(numer.path());
1181 if (rtn.hasAnnotation("ScaledBy")) rtn.rmAnnotation("ScaledBy");
1182
1183 for (const auto& b_num : numer.bins(true, true)) {
1184 const size_t idx = b_num.index();
1185 const auto& b_den = denom.bin(idx);
1186 double v, e;
1187 if (isZero(b_den.effNumEntries())) {
1188 v = std::numeric_limits<double>::quiet_NaN();
1189 e = std::numeric_limits<double>::quiet_NaN();
1190 }
1191 else {
1192 if constexpr(DbnN > sizeof...(AxisT)) {
1193 v = b_num.mean(DbnN) / b_den.mean(DbnN);
1194 const double e_num = isZero(b_num.effNumEntries())? 0 : b_num.relStdErr(DbnN);
1195 const double e_den = isZero(b_den.effNumEntries())? 0 : b_den.relStdErr(DbnN);
1196 e = fabs(v) * sqrt(sqr(e_num) + sqr(e_den));
1197 }
1198 else {
1199 v = b_num.sumW() / b_den.sumW();
1200 const double e_num = isZero(b_num.effNumEntries())? 0 : b_num.relErrW();
1201 const double e_den = isZero(b_den.effNumEntries())? 0 : b_den.relErrW();
1202 e = fabs(v) * sqrt(sqr(e_num) + sqr(e_den));
1203 }
1204 }
1205 rtn.bin(idx).set(v, {-e, e});
1206 }
1207 rtn.maskBins(denom.maskedBins(), true);
1208
1209 return rtn;
1210 }
1211
1212 template <size_t DbnN, typename... AxisT>
1213 inline BinnedEstimate<AxisT...>
1214 operator / (const BinnedDbn<DbnN, AxisT...>& numer, const BinnedDbn<DbnN, AxisT...>& denom) {
1215 return divide(numer, denom);
1216 }
1217
1218 template <size_t DbnN, typename... AxisT>
1219 inline BinnedEstimate<AxisT...>
1220 operator / (const BinnedDbn<DbnN, AxisT...>& numer, BinnedDbn<DbnN, AxisT...>&& denom) {
1221 return divide(numer, std::move(denom));
1222 }
1223
1224 template <size_t DbnN, typename... AxisT>
1225 inline BinnedEstimate<AxisT...>
1226 operator / (BinnedDbn<DbnN, AxisT...>&& numer, const BinnedDbn<DbnN, AxisT...>& denom) {
1227 return divide(std::move(numer), denom);
1228 }
1229
1230 template <size_t DbnN, typename... AxisT>
1231 inline BinnedEstimate<AxisT...>
1232 operator / (BinnedDbn<DbnN, AxisT...>&& numer, BinnedDbn<DbnN, AxisT...>&& denom) {
1233 return divide(std::move(numer), std::move(denom));
1234 }
1235
1236
1237
1238
1239
1240
1241 template <size_t DbnN, typename... AxisT>
1242 inline BinnedEstimate<AxisT...>
1243 efficiency(const BinnedDbn<DbnN, AxisT...>& accepted, const BinnedDbn<DbnN, AxisT...>& total) {
1244
1245 if (accepted != total) {
1246 throw BinningError("Arithmetic operation requires compatible binning!");
1247 }
1248
1249 BinnedEstimate<AxisT...> rtn = divide(accepted, total);
1250
1251 for (const auto& b_acc : accepted.bins(true, true)) {
1252 const auto& b_tot = total.bin(b_acc.index());
1253 auto& b_rtn = rtn.bin(b_acc.index());
1254
1255
1256
1257 if (b_acc.numEntries() > b_tot.numEntries())
1258 throw UserError("Attempt to calculate an efficiency when the numerator is not a subset of the denominator: "
1259 + Utils::toStr(b_acc.numEntries()) + " entries / " + Utils::toStr(b_tot.numEntries()) + " entries");
1260
1261
1262 double eff = std::numeric_limits<double>::quiet_NaN();
1263 double err = std::numeric_limits<double>::quiet_NaN();
1264 if (!isZero(b_tot.effNumEntries())) {
1265 eff = b_rtn.val();
1266 err = sqrt(fabs( add((1.0-2.0*eff)*b_acc.sumW2(), sqr(eff)*b_tot.sumW2()) / sqr(b_tot.sumW()) ));
1267 }
1268 b_rtn.setErr({-err, err});
1269 }
1270 return rtn;
1271 }
1272
1273
1274
1275 template <size_t DbnN, typename... AxisT>
1276 inline BinnedEstimate<AxisT...>
1277 asymm(const BinnedDbn<DbnN, AxisT...>& a, const BinnedDbn<DbnN, AxisT...>& b) {
1278 return (a-b) / (a+b);
1279 }
1280
1281
1282
1283
1284
1285
1286
1287
1288
1289
1290 template <size_t DbnN, typename... AxisT>
1291 inline BinnedEstimate<AxisT...>
1292 mkIntegral(const BinnedDbn<DbnN, AxisT...>& histo, const bool includeOverflows = true) {
1293
1294 BinnedEstimate<AxisT...> rtn = histo.mkEstimate();
1295
1296 double sumW = 0.0, sumW2 = 0.0;
1297 for (const auto& b : histo.bins(includeOverflows)) {
1298 sumW += b.sumW();
1299 sumW2 += b.sumW2();
1300 const double e = sqrt(sumW2);
1301 rtn.bin(b.index()).set(sumW, {-e, e});
1302 }
1303
1304 return rtn;
1305 }
1306
1307
1308
1309
1310
1311
1312
1313
1314
1315
1316
1317
1318
1319 template <size_t DbnN, typename... AxisT>
1320 inline BinnedEstimate<AxisT...>
1321 mkIntegralEff(const BinnedDbn<DbnN, AxisT...>& histo, const bool includeOverflows = true) {
1322
1323 BinnedEstimate<AxisT...> rtn = mkIntegral(histo, includeOverflows);
1324 const double integral = histo.integral(includeOverflows);
1325
1326
1327
1328
1329
1330 if (!integral) return rtn;
1331
1332 const double integral_err = histo.integralError(includeOverflows);
1333 for (const auto& b : rtn.bins(includeOverflows)) {
1334 const double eff = b.val() / integral;
1335 const double err = sqrt(std::abs( ((1-2*eff)*sqr(b.relTotalErrAvg()) + sqr(eff)*sqr(integral_err)) / sqr(integral) ));
1336 b.set(eff, {-err,err});
1337 }
1338
1339 return rtn;
1340 }
1341
1342
1343
1344 template <size_t DbnN, typename... AxisT>
1345 inline BinnedEstimate<AxisT...>
1346 add(const BinnedDbn<DbnN, AxisT...>& dbn, const BinnedEstimate<AxisT...>& est) {
1347 return dbn.mkEstimate() + est;
1348 }
1349
1350 template <size_t DbnN, typename... AxisT>
1351 inline BinnedEstimate<AxisT...>
1352 operator + (const BinnedDbn<DbnN, AxisT...>& dbn, const BinnedEstimate<AxisT...>& est) {
1353 return add(dbn, est);
1354 }
1355
1356 template <size_t DbnN, typename... AxisT>
1357 inline BinnedEstimate<AxisT...>
1358 operator + (BinnedDbn<DbnN, AxisT...>&& dbn, const BinnedEstimate<AxisT...>& est) {
1359 return add(std::move(dbn), est);
1360 }
1361
1362 template <size_t DbnN, typename... AxisT>
1363 inline BinnedEstimate<AxisT...>
1364 operator + (const BinnedDbn<DbnN, AxisT...>& dbn, BinnedEstimate<AxisT...>&& est) {
1365 return add(dbn, std::move(est));
1366 }
1367
1368 template <size_t DbnN, typename... AxisT>
1369 inline BinnedEstimate<AxisT...>
1370 operator + (BinnedDbn<DbnN, AxisT...>&& dbn, BinnedEstimate<AxisT...>&& est) {
1371 return add(std::move(dbn), std::move(est));
1372 }
1373
1374
1375
1376 template <size_t DbnN, typename... AxisT>
1377 inline BinnedEstimate<AxisT...>
1378 subtract(const BinnedDbn<DbnN, AxisT...>& dbn, const BinnedEstimate<AxisT...>& est) {
1379 return dbn.mkEstimate() - est;
1380 }
1381
1382 template <size_t DbnN, typename... AxisT>
1383 inline BinnedEstimate<AxisT...>
1384 operator - (const BinnedDbn<DbnN, AxisT...>& dbn, const BinnedEstimate<AxisT...>& est) {
1385 return subtract(dbn, est);
1386 }
1387
1388 template <size_t DbnN, typename... AxisT>
1389 inline BinnedEstimate<AxisT...>
1390 operator - (BinnedDbn<DbnN, AxisT...>&& dbn, const BinnedEstimate<AxisT...>& est) {
1391 return subtract(std::move(dbn), est);
1392 }
1393
1394 template <size_t DbnN, typename... AxisT>
1395 inline BinnedEstimate<AxisT...>
1396 operator - (const BinnedDbn<DbnN, AxisT...>& dbn, BinnedEstimate<AxisT...>&& est) {
1397 return subtract(dbn, std::move(est));
1398 }
1399
1400 template <size_t DbnN, typename... AxisT>
1401 inline BinnedEstimate<AxisT...>
1402 operator - (BinnedDbn<DbnN, AxisT...>&& dbn, BinnedEstimate<AxisT...>&& est) {
1403 return subtract(std::move(dbn), std::move(est));
1404 }
1405
1406
1407
1408 template <size_t DbnN, typename... AxisT>
1409 inline BinnedEstimate<AxisT...>
1410 divide(const BinnedDbn<DbnN, AxisT...>& dbn, const BinnedEstimate<AxisT...>& est) {
1411 return dbn.mkEstimate() / est;
1412 }
1413
1414 template <size_t DbnN, typename... AxisT>
1415 inline BinnedEstimate<AxisT...>
1416 operator / (const BinnedDbn<DbnN, AxisT...>& dbn, const BinnedEstimate<AxisT...>& est) {
1417 return divide(dbn, est);
1418 }
1419
1420 template <size_t DbnN, typename... AxisT>
1421 inline BinnedEstimate<AxisT...>
1422 operator / (BinnedDbn<DbnN, AxisT...>&& dbn, const BinnedEstimate<AxisT...>& est) {
1423 return divide(std::move(dbn), est);
1424 }
1425
1426 template <size_t DbnN, typename... AxisT>
1427 inline BinnedEstimate<AxisT...>
1428 operator / (const BinnedDbn<DbnN, AxisT...>& dbn, BinnedEstimate<AxisT...>&& est) {
1429 return divide(dbn, std::move(est));
1430 }
1431
1432 template <size_t DbnN, typename... AxisT>
1433 inline BinnedEstimate<AxisT...>
1434 operator / (BinnedDbn<DbnN, AxisT...>&& dbn, BinnedEstimate<AxisT...>&& est) {
1435 return divide(std::move(dbn), std::move(est));
1436 }
1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447 template<size_t DbnN, typename... AxisT, typename... Args,
1448 typename = std::enable_if_t<(DbnN == sizeof...(AxisT)+1 &&
1449 (std::is_same_v<BinnedDbn<DbnN, AxisT...>, Args> && ...))>>
1450 ScatterND<sizeof...(Args)+1> zipProfiles(const BinnedDbn<DbnN, AxisT...>& p1, Args&&... others,
1451 const std::string& path = "") {
1452
1453
1454 if ( !((p1 == others) && ...) )
1455 throw BinningError("Requested zipping of profiles with incompatible binning!");
1456
1457
1458
1459 constexpr size_t N = sizeof...(Args)+1;
1460 ScatterND<N> rtn;
1461 rtn.setAnnotation("Path", path);
1462 for (const auto& b1 : p1.bins()) {
1463 typename ScatterND<N>::NdVal vals = { b1.mean(DbnN), others.bin(b1.binIndex()).mean(DbnN) ... };
1464 typename ScatterND<N>::NdVal errs = { b1.stdErr(DbnN), others.bin(b1.binIndex()).stdErr(DbnN) ... };
1465 rtn.addPoint(vals, errs);
1466 }
1467 return rtn;
1468 }
1469
1470
1471
1472 }
1473
1474 #endif