Skip to content

Commit 9ee9473

Browse files
committed
[RF] Implement batch data access for RooCompositeDataStore
Implement getBatches() and getCategoryBatches() for RooCompositeDataStore, which so far threw an exception. Like the existing getWeightBatch(), the implementation lazily concatenates the columns of the component datasets into internal buffers by loading the composite rows one by one. The index category, which is not stored in any of the component datasets, is synthesized from the row lookup. This is needed so that combined datasets backed by a composite storage (e.g. the output of generating from a RooSimultaneous with AllBinned()) can be loaded by the generic RooFit::Evaluator data path, which so far only worked when the dataset was split into its channel components first.
1 parent 06e9f11 commit 9ee9473

2 files changed

Lines changed: 101 additions & 16 deletions

File tree

roofit/roofitcore/inc/RooCompositeDataStore.h

Lines changed: 17 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -92,26 +92,27 @@ class RooCompositeDataStore : public RooAbsDataStore {
9292
void loadValues(const RooAbsDataStore *tds, const RooFormulaVar* select=nullptr, const char* rangeName=nullptr,
9393
std::size_t nStart=0, std::size_t nStop = std::numeric_limits<std::size_t>::max()) override;
9494

95-
RooAbsData::RealSpans getBatches(std::size_t first, std::size_t len) const override {
96-
//TODO
97-
std::cerr << "This functionality is not yet implemented for composite data stores." << std::endl;
98-
throw std::logic_error("getBatches() not implemented for RooCompositeDataStore.");
99-
(void)first; (void)len;
100-
return {};
101-
}
95+
RooAbsData::RealSpans getBatches(std::size_t first, std::size_t len) const override;
96+
RooAbsData::CategorySpans getCategoryBatches(std::size_t first, std::size_t len) const override;
10297
std::span<const double> getWeightBatch(std::size_t first, std::size_t len) const override;
10398

10499

105100
protected:
106-
107-
std::map<Int_t,RooAbsDataStore*> _dataMap ;
108-
RooCategory* _indexCat = nullptr;
109-
mutable RooAbsDataStore* _curStore = nullptr; ///<! Datastore associated with current event
110-
mutable Int_t _curIndex = 0; ///<! Index associated with current event
111-
mutable std::unique_ptr<std::vector<double>> _weightBuffer; ///<! Buffer for weights in case a batch of values is requested.
112-
bool _ownComps = false; ///<!
113-
114-
ClassDefOverride(RooCompositeDataStore,1) // Composite Data Storage class
101+
void fillBatchBuffers() const;
102+
103+
std::map<Int_t, RooAbsDataStore *> _dataMap;
104+
RooCategory *_indexCat = nullptr;
105+
mutable RooAbsDataStore *_curStore = nullptr; ///<! Datastore associated with current event
106+
mutable Int_t _curIndex = 0; ///<! Index associated with current event
107+
mutable std::unique_ptr<std::vector<double>>
108+
_weightBuffer; ///<! Buffer for weights in case a batch of values is requested.
109+
mutable std::map<RooAbsArg const *, std::vector<double>>
110+
_realBatchBuffers; ///<! Buffers for real-valued columns in case batches of values are requested.
111+
mutable std::map<RooAbsArg const *, std::vector<RooAbsCategory::value_type>>
112+
_catBatchBuffers; ///<! Buffers for category columns in case batches of values are requested.
113+
bool _ownComps = false; ///<!
114+
115+
ClassDefOverride(RooCompositeDataStore, 1) // Composite Data Storage class
115116
};
116117

117118

roofit/roofitcore/src/RooCompositeDataStore.cxx

Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ When iterated from start to finish, datasets will be traversed in the order of t
3535
#include "RooRealVar.h"
3636
#include "RooCategory.h"
3737

38+
#include <algorithm>
3839
#include <iomanip>
3940
#include <iostream>
4041

@@ -408,3 +409,86 @@ std::span<const double> RooCompositeDataStore::getWeightBatch(std::size_t first,
408409

409410
return {_weightBuffer->data() + first, len};
410411
}
412+
413+
////////////////////////////////////////////////////////////////////////////////
414+
/// Fill the internal per-column buffers for batch access by loading the
415+
/// composite rows one by one, like getWeightBatch() does for the weights.
416+
/// This also synthesizes a column for the index category, which is not
417+
/// stored in any of the component datasets.
418+
void RooCompositeDataStore::fillBatchBuffers() const
419+
{
420+
const auto n = static_cast<std::size_t>(numEntries());
421+
422+
if (!_realBatchBuffers.empty() || !_catBatchBuffers.empty()) {
423+
// Refill from scratch if entries were added or removed since the last
424+
// fill. Value mutations that keep the number of entries are not
425+
// detected, like for the weight buffer above.
426+
const std::size_t nFilled = !_realBatchBuffers.empty() ? _realBatchBuffers.begin()->second.size()
427+
: _catBatchBuffers.begin()->second.size();
428+
if (nFilled == n)
429+
return;
430+
_realBatchBuffers.clear();
431+
_catBatchBuffers.clear();
432+
}
433+
434+
std::vector<std::pair<RooAbsReal const *, std::vector<double> *>> realCols;
435+
std::vector<std::pair<RooAbsCategory const *, std::vector<RooAbsCategory::value_type> *>> catCols;
436+
437+
for (RooAbsArg const *arg : _vars) {
438+
if (auto cat = dynamic_cast<RooAbsCategory const *>(arg)) {
439+
auto &buf = _catBatchBuffers[arg];
440+
buf.reserve(n);
441+
catCols.emplace_back(cat, &buf);
442+
} else if (auto real = dynamic_cast<RooAbsReal const *>(arg)) {
443+
auto &buf = _realBatchBuffers[arg];
444+
buf.reserve(n);
445+
realCols.emplace_back(real, &buf);
446+
}
447+
}
448+
449+
for (std::size_t i = 0; i < n; ++i) {
450+
get(i);
451+
for (auto &col : realCols)
452+
col.second->push_back(col.first->getVal());
453+
for (auto &col : catCols)
454+
col.second->push_back(col.first->getCurrentIndex());
455+
}
456+
}
457+
458+
////////////////////////////////////////////////////////////////////////////////
459+
/// Get the batches of the real-valued columns in the range [first, first+len).
460+
/// The columns of the component datasets are lazily concatenated into
461+
/// internal buffers in composite row order.
462+
RooAbsData::RealSpans RooCompositeDataStore::getBatches(std::size_t first, std::size_t len) const
463+
{
464+
fillBatchBuffers();
465+
466+
// Clamp against the actual number of entries, because the default
467+
// arguments of RooAbsData::getBatches() ask for the maximum length.
468+
first = std::min(first, static_cast<std::size_t>(numEntries()));
469+
len = std::min(len, static_cast<std::size_t>(numEntries()) - first);
470+
471+
RooAbsData::RealSpans out;
472+
for (auto const &item : _realBatchBuffers) {
473+
out.emplace(item.first, std::span<const double>{item.second.data() + first, len});
474+
}
475+
return out;
476+
}
477+
478+
////////////////////////////////////////////////////////////////////////////////
479+
/// Get the batches of the category columns in the range [first, first+len),
480+
/// including the index category. See getBatches().
481+
RooAbsData::CategorySpans RooCompositeDataStore::getCategoryBatches(std::size_t first, std::size_t len) const
482+
{
483+
fillBatchBuffers();
484+
485+
// See getBatches() for the clamping rationale.
486+
first = std::min(first, static_cast<std::size_t>(numEntries()));
487+
len = std::min(len, static_cast<std::size_t>(numEntries()) - first);
488+
489+
RooAbsData::CategorySpans out;
490+
for (auto const &item : _catBatchBuffers) {
491+
out.emplace(item.first, std::span<const RooAbsCategory::value_type>{item.second.data() + first, len});
492+
}
493+
return out;
494+
}

0 commit comments

Comments
 (0)