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