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