Skip to content

Commit 7419fd7

Browse files
authored
DPL Analysis: add support for generating multiple combinations (#2933)
1 parent 6a37cb4 commit 7419fd7

File tree

5 files changed

+1214
-22
lines changed

5 files changed

+1214
-22
lines changed

Framework/Core/ANALYSIS.md

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -332,6 +332,30 @@ struct MyTask : AnalysisTask {
332332
};
333333
```
334334
335+
### Getting combinations (pairs, triplets, ...)
336+
To get combinations of distinct tracks, CombinationsGenerator from `ASoAHelpers.h` can be used. It is possible to specify a predicate for a combination as a whole, and only matching combinations are then outputed. Example:
337+
338+
```cpp
339+
struct MyTask : AnalysisTask {
340+
341+
void process(Tracks const& tracks) {
342+
auto condition = [](const auto& tracksCombination) {
343+
return std::all_of(tracksCombination.begin(), tracksCombination.end(),
344+
[](const auto& t) { return track.eta() < 0; });
345+
});
346+
auto combGenerator = CombinationsGenerator<Tracks, 3>(tracks, condition);
347+
for (auto& comb : combGenerator) {
348+
// all tracks have eta < 0
349+
Track t0 = comb[0];
350+
Track t1 = comb[1];
351+
Track t2 = comb[2];
352+
...
353+
}
354+
}
355+
};
356+
```
357+
358+
335359
### Possible ideas
336360

337361
We could add a template `<typename C...> reshuffle()` method to the Table class which allows you to reduce the number of columns or attach new dynamic columns. A template wrapper could

Framework/Core/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -228,6 +228,7 @@ foreach(b
228228
TableBuilder
229229
WorkflowHelpers
230230
ASoA
231+
ASoAHelpers
231232
HistogramRegistry
232233
TableToTree
233234
TreeToTable

Framework/Core/include/Framework/ASoAHelpers.h

Lines changed: 129 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -13,24 +13,142 @@
1313

1414
#include "Framework/ASoA.h"
1515

16+
#include <iterator>
17+
#include <tuple>
18+
#include <utility>
19+
1620
namespace o2::soa
1721
{
1822

19-
/// @return a vector of pairs with all the possible
20-
/// combinations of the rows of the table T.
23+
template <typename T2, std::size_t K2>
24+
void addOne(std::array<T2, K2>& array, const T2& maxOffset, bool& isEnd)
25+
{
26+
for (int i = 0; i < K2; i++) {
27+
array[K2 - i - 1]++;
28+
if (array[K2 - i - 1] != maxOffset + K2 - i - 1) { // no < operator for RowViewBase
29+
for (int j = K2 - i; j < K2; j++) {
30+
array[j].setCursor(array[j - 1].mRowIndex + 1);
31+
}
32+
isEnd = false;
33+
return;
34+
}
35+
}
36+
isEnd = true;
37+
}
38+
39+
/// @return next K-combination of the rows of the table T.
2140
/// FIXME: move to coroutines once we have C++20
22-
template <typename T>
23-
std::vector<std::pair<typename T::iterator, typename T::iterator>>
24-
combinations(T const& table)
41+
template <typename T, int K>
42+
class CombinationsGenerator
2543
{
26-
std::vector<std::pair<typename T::iterator, typename T::iterator>> result;
27-
result.reserve((table.size() + 1) * table.size() / 2);
28-
for (auto t0 = table.begin(); t0 + 1 != table.end(); ++t0) {
29-
for (auto t1 = t0 + 1; t1 != table.end(); ++t1) {
30-
result.push_back(std::make_pair(t0, t1));
44+
public:
45+
using IteratorType = typename T::iterator;
46+
using CombinationType = std::array<IteratorType, K>;
47+
using FunctionType = std::function<bool(const CombinationType&)>;
48+
49+
class CombinationsIterator : public std::iterator<std::forward_iterator_tag, CombinationType>
50+
{
51+
public:
52+
using reference = CombinationType&;
53+
using value_type = CombinationType;
54+
using pointer = CombinationType*;
55+
using iterator_category = std::forward_iterator_tag;
56+
57+
CombinationsIterator() = delete;
58+
59+
CombinationsIterator(const IteratorType& begin, int n, const FunctionType& condition)
60+
: mN(n), mIsEnd(false), mTableBegin(begin), mMaxOffset(begin + n - K + 1), mCondition(condition)
61+
{
62+
initIterators();
63+
}
64+
65+
~CombinationsIterator() = default;
66+
67+
// prefix increment
68+
CombinationsIterator& operator++()
69+
{
70+
if (!mIsEnd) {
71+
addOne(mCurrent, mMaxOffset, mIsEnd);
72+
}
73+
while (!mIsEnd && !mCondition(mCurrent)) {
74+
addOne(mCurrent, mMaxOffset, mIsEnd);
75+
}
76+
return *this;
77+
}
78+
// postfix increment
79+
CombinationsIterator operator++(int /*unused*/)
80+
{
81+
CombinationsIterator copy(*this);
82+
operator++();
83+
return copy;
84+
}
85+
// return reference
86+
reference operator*()
87+
{
88+
return mCurrent;
3189
}
90+
bool operator==(const CombinationsIterator& rh)
91+
{
92+
return (mIsEnd && rh.mIsEnd) || (mCurrent == rh.mCurrent);
93+
}
94+
bool operator!=(const CombinationsIterator& rh)
95+
{
96+
return !(*this == rh);
97+
}
98+
99+
void initIterators()
100+
{
101+
for (int i = 0; i < K; i++) {
102+
mCurrent[i] = mTableBegin + i;
103+
}
104+
if (!mCondition(mCurrent)) {
105+
operator++();
106+
}
107+
}
108+
109+
void goToEnd()
110+
{
111+
for (int i = 0; i < K; i++) {
112+
mCurrent[i].setCursor(mN - K + i);
113+
}
114+
operator++();
115+
}
116+
117+
private:
118+
CombinationType mCurrent;
119+
int mN; // number of elements
120+
bool mIsEnd; // whether there are any more tuples available
121+
FunctionType mCondition; // only tuples satisfying the condition will be outputed
122+
IteratorType mTableBegin; // start of the table for which tuples are generated
123+
IteratorType mMaxOffset; // one position past maximum acceptable position for 0th element of combination
124+
};
125+
126+
using iterator = CombinationsIterator;
127+
using const_iterator = CombinationsIterator;
128+
129+
inline iterator begin()
130+
{
131+
return iterator(mTableBegin, mN, mCondition);
132+
}
133+
inline iterator end()
134+
{
135+
auto it = iterator(mTableBegin, mN, mCondition);
136+
it.goToEnd();
137+
return it;
32138
}
33-
return result;
139+
140+
CombinationsGenerator() = delete;
141+
CombinationsGenerator(const T& table, const FunctionType& condition)
142+
: mTableBegin(table.begin()), mN(table.size()), mCondition(condition)
143+
{
144+
static_assert(K > 0);
145+
}
146+
~CombinationsGenerator() = default;
147+
148+
private:
149+
IteratorType mTableBegin;
150+
int mN;
151+
FunctionType mCondition;
34152
};
35153

36154
} // namespace o2::soa

0 commit comments

Comments
 (0)