File indexing completed on 2026-05-30 08:56:54
0001
0002
0003
0004
0005
0006 #ifndef YODA_BinnedStorage_H
0007 #define YODA_BinnedStorage_H
0008
0009 #include "YODA/BinnedAxis.h"
0010 #include "YODA/Binning.h"
0011 #include "YODA/Bin.h"
0012
0013 #include <memory>
0014 #include <vector>
0015
0016 namespace YODA {
0017
0018
0019
0020
0021 template <class VecT>
0022 class BinsVecWrapper {
0023 public:
0024
0025 class myIt;
0026
0027 using BinType = std::decay_t<decltype(*std::declval<VecT>().begin())>;
0028
0029 BinsVecWrapper() = delete;
0030
0031 BinsVecWrapper(VecT& bins, const std::vector<size_t>& hiddenBins)
0032 : _bins(bins), _hiddenBins(hiddenBins) {}
0033
0034 BinsVecWrapper(BinsVecWrapper&& other)
0035 : _bins(std::move(other._bins)), _hiddenBins(std::move(other._hiddenBins)) {}
0036
0037 myIt begin() const { return myIt(_bins, _hiddenBins); }
0038 myIt end() const { return myIt(_bins); }
0039
0040 size_t size() const { return _bins.size() - _hiddenBins.size(); }
0041
0042 const BinType& operator[](size_t index) const { return _bins[index]; }
0043
0044 BinType& operator[](size_t index) { return _bins[index]; }
0045
0046 private:
0047
0048 VecT& _bins;
0049 std::vector<size_t> _hiddenBins;
0050 };
0051
0052 template<typename VecT>
0053 class BinsVecWrapper<VecT>::myIt {
0054 private:
0055 using IterT = std::conditional_t<std::is_const<VecT>::value,
0056 typename VecT::const_iterator,
0057 typename VecT::iterator>;
0058 public:
0059
0060 myIt(VecT& bins,
0061 const std::vector<size_t>& hiddenBins)
0062 : _ptr(bins.begin()),
0063 hiddenCurrPtr(hiddenBins.begin()),
0064 hiddenEndPtr(hiddenBins.end()),
0065 binsEndPtr(bins.end()) {
0066
0067 if (hiddenCurrPtr != hiddenEndPtr && 0 == *hiddenCurrPtr){
0068
0069 ++hiddenCurrPtr;
0070 ++(*this);
0071
0072 }
0073 }
0074
0075 myIt(VecT& bins)
0076 : _ptr(bins.end()),
0077
0078
0079 binsEndPtr(bins.end()) {}
0080
0081 myIt operator++() noexcept {
0082
0083
0084 ++_ptr;
0085 ++currBinIdx;
0086
0087 while (_ptr != binsEndPtr &&
0088 hiddenCurrPtr != hiddenEndPtr &&
0089 currBinIdx == *hiddenCurrPtr) {
0090
0091 ++currBinIdx;
0092 ++hiddenCurrPtr;
0093 ++_ptr;
0094 }
0095 return *this;
0096 }
0097
0098 bool operator!=(const myIt& other) const noexcept {
0099 return _ptr != other._ptr;
0100 }
0101
0102 typename std::iterator_traits<IterT>::reference operator*() noexcept { return *_ptr; }
0103
0104 private:
0105 IterT _ptr;
0106 std::vector<size_t>::const_iterator hiddenCurrPtr;
0107 std::vector<size_t>::const_iterator hiddenEndPtr;
0108 IterT binsEndPtr;
0109 size_t currBinIdx = 0;
0110 };
0111
0112
0113
0114 template <typename BinContentT, typename... AxisT>
0115 class BinnedStorage {
0116 protected:
0117
0118
0119 using BinningT = Binning<std::decay_t<decltype(std::declval<Axis<AxisT>>())>...>;
0120 using BinT = Bin<sizeof...(AxisT), BinContentT, BinningT>;
0121 using BinsVecT = std::vector<BinT>;
0122 using BaseT = BinnedStorage<BinContentT, AxisT...>;
0123
0124 public:
0125
0126 using BinningType = BinningT;
0127 using BinType = BinT;
0128 using BinDimension = std::integral_constant<size_t, sizeof...(AxisT)>;
0129
0130
0131
0132
0133
0134
0135 BinnedStorage() : _binning(std::vector<AxisT>{}...) {
0136 fillBins();
0137 }
0138
0139
0140 BinnedStorage(const BinningT& binning) : _binning(binning) {
0141 fillBins();
0142 }
0143
0144
0145 BinnedStorage(BinningT&& binning) : _binning(std::move(binning)) {
0146 fillBins();
0147 }
0148
0149
0150 BinnedStorage(const std::vector<AxisT>&... edges) : _binning(edges...) {
0151 fillBins();
0152 }
0153
0154
0155 BinnedStorage(std::vector<AxisT>&&... edges) : _binning(std::move(edges)...) {
0156 fillBins();
0157 }
0158
0159
0160 BinnedStorage(std::initializer_list<AxisT>&&... edges) : _binning(std::vector<AxisT>{edges}...) {
0161 fillBins();
0162 }
0163
0164
0165 BinnedStorage(const Axis<AxisT>&... axes) : _binning(axes...) {
0166 fillBins();
0167 }
0168
0169
0170 BinnedStorage(Axis<AxisT>&&... axes) : _binning(std::move(axes)...) {
0171 fillBins();
0172 }
0173
0174
0175 BinnedStorage(const BinnedStorage& other) : _binning(other._binning) {
0176 fillBins(other._bins);
0177 }
0178
0179
0180 BinnedStorage(BinnedStorage&& other) : _binning(std::move(other._binning)) {
0181 fillBins(std::move(other._bins));
0182 }
0183
0184
0185
0186
0187
0188
0189
0190 size_t dim() const noexcept {
0191 return sizeof...(AxisT) + 1;
0192 }
0193
0194
0195
0196
0197
0198
0199
0200
0201 BinT& bin(size_t idx) noexcept {
0202 return _bins.at(idx);
0203 }
0204
0205
0206 const BinT& bin(size_t idx) const noexcept {
0207 return _bins.at(idx);
0208 }
0209
0210
0211 BinT& bin(const std::array<size_t, sizeof...(AxisT)>& idxLocal) noexcept {
0212 return bin( _binning.localToGlobalIndex(idxLocal) );
0213 }
0214
0215
0216 const BinT& bin(const std::array<size_t, sizeof...(AxisT)>& idxLocal) const noexcept {
0217 return bin( _binning.localToGlobalIndex(idxLocal) );
0218 }
0219
0220
0221 BinT& binAt(typename BinningT::EdgeTypesTuple&& coords) noexcept {
0222 const size_t binIdx = _binning.globalIndexAt(coords);
0223 return bin(binIdx);
0224 }
0225
0226
0227 const BinT& binAt(typename BinningT::EdgeTypesTuple&& coords) const noexcept {
0228 const size_t binIdx = _binning.globalIndexAt(coords);
0229 return bin(binIdx);
0230 }
0231
0232
0233
0234
0235 void set(typename BinningT::EdgeTypesTuple&& coords, BinContentT&& content) noexcept {
0236 const size_t binIdx = _binning.globalIndexAt(coords);
0237 _bins[binIdx] = std::move(content);
0238 }
0239
0240
0241 void set(typename BinningT::EdgeTypesTuple&& coords, const BinContentT& content) noexcept {
0242 const size_t binIdx = _binning.globalIndexAt(coords);
0243 _bins[binIdx] = content;
0244 }
0245
0246
0247
0248
0249 void set(const size_t binIdx, BinContentT&& content) noexcept {
0250 _bins[binIdx] = std::move(content);
0251 }
0252
0253
0254 void set(const size_t binIdx, const BinContentT& content) noexcept {
0255 _bins[binIdx] = content;
0256 }
0257
0258
0259
0260 std::vector<size_t> calcIndicesToSkip(const bool includeOverflows, const bool includeMaskedBins) const noexcept {
0261
0262
0263 if (!_binning.numBins(!includeOverflows, !includeMaskedBins)) return {};
0264
0265 std::vector<size_t> indicesToSkip;
0266
0267
0268 auto appendIndicesVec = [&indicesToSkip](std::vector<size_t>&& indicesVec) {
0269 indicesToSkip.insert(std::end(indicesToSkip),
0270 std::make_move_iterator(std::begin(indicesVec)),
0271 std::make_move_iterator(std::end(indicesVec)));
0272 };
0273
0274
0275
0276 if(!includeOverflows) {
0277 appendIndicesVec(_binning.calcOverflowBinsIndices());
0278 }
0279
0280 if (!includeMaskedBins) {
0281 appendIndicesVec(_binning.maskedBins());
0282 }
0283
0284
0285 std::sort(indicesToSkip.begin(), indicesToSkip.end());
0286 indicesToSkip.erase( std::unique(indicesToSkip.begin(), indicesToSkip.end()),
0287 indicesToSkip.end() );
0288
0289 return indicesToSkip;
0290 }
0291
0292
0293
0294
0295
0296
0297
0298
0299 BinsVecWrapper<BinsVecT> bins(const bool includeOverflows = false,
0300 const bool includeMaskedBins = false) noexcept {
0301 return BinsVecWrapper<BinsVecT>(_bins,
0302 calcIndicesToSkip(includeOverflows, includeMaskedBins));
0303 }
0304
0305
0306
0307
0308
0309
0310 const BinsVecWrapper<const BinsVecT> bins(const bool includeOverflows = false,
0311 const bool includeMaskedBins = false) const noexcept {
0312 return BinsVecWrapper<const BinsVecT>(_bins,
0313 calcIndicesToSkip(includeOverflows, includeMaskedBins));
0314 }
0315
0316
0317
0318
0319
0320
0321
0322 const BinningT& binning() const noexcept {
0323 return _binning;
0324 }
0325
0326
0327 size_t binDim() const noexcept {
0328 return _binning.dim();
0329 }
0330
0331
0332 size_t numBins(const bool includeOverflows = false, const bool includeMaskedBins = false) const noexcept {
0333 return _binning.numBins(includeOverflows, includeMaskedBins);
0334 }
0335
0336
0337
0338
0339
0340 size_t numBinsAt(const size_t axisN, const bool includeOverflows = false) const noexcept {
0341 size_t nOverflows = includeOverflows? 0 : _binning.countOverflowBins(axisN);
0342 return _binning.numBinsAt(axisN) - nOverflows;
0343 }
0344
0345
0346 void reset() noexcept { clearBins(); }
0347
0348
0349
0350
0351 void clearBins() noexcept {
0352 _bins.clear();
0353 fillBins();
0354 }
0355
0356
0357 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0358 std::vector<E> edges(const bool includeOverflows = false) const noexcept {
0359 return BaseT::_binning.template edges<I>(includeOverflows);
0360 }
0361
0362
0363
0364
0365
0366
0367
0368 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0369 std::enable_if_t<std::is_floating_point<E>::value, std::vector<E>>
0370 widths(const bool includeOverflows = false) const noexcept {
0371 return BaseT::_binning.template widths<I>(includeOverflows);
0372 }
0373
0374
0375
0376
0377
0378
0379
0380 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0381 std::enable_if_t<std::is_floating_point<E>::value, std::vector<E>>
0382 mins(const bool includeOverflows = false) const noexcept {
0383 return BaseT::_binning.template mins<I>(includeOverflows);
0384 }
0385
0386
0387
0388
0389
0390
0391
0392 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0393 std::enable_if_t<std::is_floating_point<E>::value, std::vector<E>>
0394 maxs(const bool includeOverflows = false) const noexcept {
0395 return BaseT::_binning.template maxs<I>(includeOverflows);
0396 }
0397
0398
0399
0400
0401
0402
0403
0404 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0405 std::enable_if_t<std::is_floating_point<E>::value, std::vector<E>>
0406 mids(const bool includeOverflows = false) const noexcept {
0407 return BaseT::_binning.template mids<I>(includeOverflows);
0408 }
0409
0410
0411
0412
0413 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0414 std::enable_if_t<std::is_floating_point<E>::value, E> min() const noexcept {
0415 return BaseT::_binning.template min<I>();
0416 }
0417
0418
0419
0420
0421 template <size_t I, typename E = typename BinningT::template getEdgeT<I>>
0422 std::enable_if_t<std::is_floating_point<E>::value, E> max() const noexcept {
0423 return BaseT::_binning.template max<I>();
0424 }
0425
0426
0427
0428
0429
0430
0431
0432
0433 void maskBins(const std::vector<size_t>& indicesToMask, const bool status = true) noexcept {
0434 _binning.maskBins(indicesToMask, status);
0435 }
0436
0437
0438 void maskBin(const size_t indexToMask, const bool status = true) noexcept {
0439 _binning.maskBin(indexToMask, status);
0440 }
0441
0442
0443 void maskSlice(const size_t dim, const size_t idx, const bool status = true) {
0444 _binning.maskSlice(dim, idx, status);
0445 }
0446
0447
0448 void maskBinAt(typename BinningT::EdgeTypesTuple&& coords, const bool status = true) noexcept {
0449 _binning.maskBinAt(coords, status);
0450 }
0451
0452 bool isMasked(const size_t binIndex) const noexcept {
0453 return _binning.isMasked(binIndex);
0454 }
0455
0456 std::vector<size_t> maskedBins() const noexcept {
0457 return _binning.maskedBins();
0458 }
0459
0460 bool isVisible(const size_t binIndex) const noexcept {
0461 return _binning.isVisible(binIndex);
0462 }
0463
0464
0465
0466
0467
0468
0469
0470
0471
0472
0473
0474
0475
0476
0477
0478
0479
0480
0481 template <size_t... AxisNs, class RetT = void>
0482 auto mergeBins(
0483 std::decay_t<decltype(AxisNs, std::declval<std::pair<size_t, size_t>>())>... mergeRanges)
0484 noexcept -> std::enable_if_t<MetaUtils::is_detected_v<MetaUtils::operatorTraits::addition_assignment_t, BinContentT>, RetT>
0485 {
0486 auto mergeStorageBins =
0487 [&binning = BaseT::_binning, &binStorage = BaseT::_bins](auto I, const auto& mergeRangePair){
0488 assert(mergeRangePair.first < mergeRangePair.second);
0489
0490 auto append = [&binStorage](const auto& pivotBinsIndices, const auto& binsIndicesToMerge){
0491 assert(pivotBinsIndices.size() == binsIndicesToMerge.size());
0492
0493
0494
0495
0496
0497
0498
0499 for (size_t i = 0; i < pivotBinsIndices.size(); ++i) {
0500 auto& pivotBin = binStorage[pivotBinsIndices[i]];
0501 pivotBin += std::move(binStorage[binsIndicesToMerge[i]]);
0502 }
0503
0504 binStorage.erase(
0505 std::remove_if(binStorage.begin(), binStorage.end(), [&](const auto& b) {
0506 return std::find(binsIndicesToMerge.begin(), binsIndicesToMerge.end(), b.index()) != binsIndicesToMerge.end();
0507 }), binStorage.end());
0508 };
0509
0510 ssize_t nBinRowsToBeMerged = mergeRangePair.second - mergeRangePair.first;
0511
0512 size_t currBinRowIdx = mergeRangePair.first;
0513 size_t nextBinRowIdx = mergeRangePair.first + 1;
0514
0515
0516 while (nBinRowsToBeMerged--) {
0517
0518
0519 append(binning.sliceIndices(I, mergeRangePair.first), binning.sliceIndices(I, nextBinRowIdx));
0520 binning.template mergeBins<I>({currBinRowIdx, nextBinRowIdx});
0521 }
0522 };
0523
0524 ((void)mergeStorageBins(std::integral_constant<size_t, AxisNs>(), mergeRanges), ...);
0525
0526 }
0527
0528
0529
0530
0531
0532
0533 template<size_t axisN, template<typename...> typename BinnedT, typename Func,
0534 typename = std::enable_if_t< (axisN < sizeof...(AxisT) && sizeof...(AxisT)>=2) >>
0535 auto mkBinnedSlices(Func&& how2add, const bool includeOverflows=false) const {
0536
0537 size_t vecN = BaseT::numBinsAt(axisN, includeOverflows);
0538 auto binnedSlice = _mkBinnedT<BinnedT>(_binning.template _getAxesExcept<axisN>());
0539 std::vector<decltype(binnedSlice)> rtn(vecN, binnedSlice);
0540 for (size_t i = 0; i < vecN; ++i) {
0541
0542 auto mkSlice = [&oldBins = _bins, &how2add, &binnedSlice = rtn[i]](const auto& indicesToCopy) {
0543 assert(binnedSlice.numBins(true) == indicesToCopy.size());
0544
0545
0546
0547 for (size_t i = 0; i < binnedSlice.numBins(true); ++i) {
0548 auto& pivotBin = binnedSlice.bin(i);
0549 auto& binToCopy = oldBins[indicesToCopy[i]];
0550 how2add(pivotBin, binToCopy);
0551 }
0552 };
0553
0554
0555
0556 mkSlice(_binning.sliceIndices(axisN, i + !includeOverflows));
0557
0558 }
0559 return rtn;
0560 }
0561
0562
0563
0564
0565
0566
0567
0568
0569 BinnedStorage& operator = (const BinnedStorage& other) noexcept {
0570 if (this != &other) {
0571 _binning = other._binning;
0572 fillBins(other._bins);
0573 }
0574 return *this;
0575 }
0576
0577
0578 BinnedStorage& operator = (BinnedStorage&& other) noexcept {
0579 if (this != &other) {
0580 _binning = std::move(other._binning);
0581 fillBins(std::move(other._bins));
0582 }
0583 return *this;
0584 }
0585
0586
0587
0588
0589
0590 bool operator == (const BinnedStorage& other) const noexcept {
0591 return _binning.isCompatible(other._binning);
0592 }
0593
0594
0595
0596
0597 bool operator != (const BinnedStorage& other) const noexcept {
0598 return ! operator == (other);
0599 }
0600
0601
0602 std::vector<size_t> _global2local(size_t idx) const noexcept {
0603 const auto& indices = _binning.globalToLocalIndices(idx);
0604 return std::vector<size_t>(indices.begin(), indices.end());
0605 }
0606
0607
0608 size_t _local2global(const std::vector<size_t>& indices) const {
0609 assert(indices.size() == sizeof...(AxisT));
0610 std::array<size_t, sizeof...(AxisT)> arr;
0611 std::copy_n(std::make_move_iterator(indices.begin()), sizeof...(AxisT), arr.begin());
0612 return _binning.localToGlobalIndex(arr);
0613 }
0614
0615
0616
0617
0618 protected:
0619
0620
0621
0622
0623
0624 void fillBins() noexcept {
0625 _bins.reserve(_binning.numBins());
0626
0627 for (size_t i = 0; i < _binning.numBins(); ++i) {
0628 _bins.emplace_back(i, _binning);
0629 }
0630 }
0631
0632 void fillBins(const BinsVecT& bins) noexcept {
0633 _bins.clear();
0634 _bins.reserve(_binning.numBins());
0635 for (const auto& b : bins) {
0636 _bins.emplace_back(b, _binning);
0637 }
0638 }
0639
0640 void fillBins(BinsVecT&& bins) noexcept {
0641 _bins.clear();
0642 _bins.reserve(_binning.numBins());
0643 for (auto&& b : bins) {
0644 _bins.emplace_back(std::move(b), _binning);
0645 }
0646 }
0647
0648
0649 template<template<typename...> typename BinnedT, class... newAxes, size_t... As>
0650 auto _mkBinnedT_aux(const std::tuple<newAxes...>& t, std::index_sequence<As...>) const {
0651 return BinnedT<typename newAxes::EdgeT...>(std::get<As>(t)...);
0652 }
0653
0654
0655 template <template<typename...> typename BinnedT, class... newAxes>
0656 auto _mkBinnedT(const std::tuple<newAxes...>& t) const {
0657 return _mkBinnedT_aux<BinnedT>(t, std::make_index_sequence<sizeof...(newAxes)>{});
0658 }
0659
0660
0661
0662
0663
0664 BinsVecT _bins;
0665
0666 BinningT _binning;
0667
0668 };
0669
0670 }
0671
0672 #endif