AssnsAlgorithms.h
Go to the documentation of this file.
1 /**
2  * @file ForEachAssociatedGroup.h
3  * @brief Helper functions to access associations in order
4  *
5  * No additional linking is required to use these functions.
6  *
7  * Provided functions:
8  *
9  * * `art::for_each_group()` executing the provided function on each
10  * of the elements associated to the same object, for each object
11  *
12  * * `art::for_each_group_with_left()` executing the provided function
13  * on each of the elements associated to the same object, for each
14  * object, while also providing a reference to the object.
15  */
16 
17 #ifndef canvas_Persistency_Common_AssnsAlgorithms_h
18 #define canvas_Persistency_Common_AssnsAlgorithms_h
19 
21 
22 // range library
23 #include "range/v3/algorithm/for_each.hpp"
24 #include "range/v3/view/all.hpp"
25 #include "range/v3/view/group_by.hpp"
26 #include "range/v3/view/map.hpp"
27 #include "range/v3/view/transform.hpp"
28 
29 // C/C++ standard libraries
30 #include <iterator> // std::next()
31 
32 namespace art {
33  /**
34  * @brief Helper functions to access associations in order
35  * @tparam A type of association being read
36  * @tparam F type of functor to be called on each associated group
37  * @param assns the association being read
38  * @param func functor to be called on each associated group
39  *
40  * This function takes two input arguments, a constant reference to
41  * the association data product itself (`assns`), and the function
42  * (`func`) to be operated on each of the group of associated objects.
43  * This function represents the association data product as
44  * a range of ranges representing the right hand side in the
45  * collection, hence the function provided as the second argument
46  * should assume that it will be operating on a range of art::Ptr
47  * to the associated data products grouped by the data product
48  * they are associated with.
49  *
50  * Example: assuming that a module with input tag stored in `fTrackTag` has
51  * created associations of each track to its hits, the total charge for each
52  * track can be extracted by:
53  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~{.cpp}
54  * auto assns = art::getValidHandle<art::Assns<recob::Track, recob::Hit>>
55  * (fTrackTag);
56  *
57  * std::vector<double> totalCharge;
58  * art::for_each_group(*assns,
59  * [&totalCharge](auto hits)
60  * {
61  * double total = 0.;
62  * for (auto iHit = begin(hits); iHit != end(hits); ++iHit)
63  * total += (*iHit)->Integral();
64  * totalCharge.push_back(total);
65  * }
66  * );
67  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
68  * A number of important points need to be realised about this example:
69  *
70  * * the requirements of this function translate, for this example, into:
71  * * at the creation of the associations data product, for each track the
72  * _complete_ sequence of hits must be associated, one hit after the
73  * other (via `addSingle`); if the order of the hits is relevant (it is
74  * not in this specific example), hits must be associated in that order
75  * * each track must have at least one associated hit;
76  * * the original association has to be stored with `recob::Track` as
77  * _left_ key and `recob::Hit` as _right_ key;
78  * * we use here a lambda function as `func`; any object
79  * behaving as a function and able to accept the range of hits as its only
80  * argument will work just as well;
81  * * `func` will be called once for every track (but if a track has no
82  * associated hit, that track will be skipped, and if a track appears in
83  * more than one association sequence, like in (T1,H1) (T1,H2) (T2,H4)
84  * (T1,H3), then that track will appear as many times);
85  * * `func` does not return any value: the results are accumulated in a new
86  * sequence;
87  * * `func` receives a range of _art_ pointers (`art::Ptr<recob::Hit>`) which
88  * needs to be navigated with the `begin()`/`end()` free functions (don't
89  * specify their namespace: C++ will figure out!); double dereferencing
90  * is needed: the first (`*iHit`) will turn the range iterator into the
91  * pointed `art::Ptr<recob::Hit>`, and the second (`(...)->Integral()`)
92  * accesses the `recob::Hit` pointed by the _art_ pointer;
93  * * `func` does not know which track the hits are associated to, and it
94  * assumes that the first sequence of hits is associated to the first track
95  * (so that its total charge will be available in `totalCharge[0]`), the
96  * second sequence to the second track (`totalCharge[1]`) and so on.
97  *
98  * Therefore, for a `assns` sequence like
99  *
100  * (T1,H1) (T1,H2) (T2,H4) (T2,H6) (T1,H5)
101  *
102  * the function `for_each_group()` will execute two calls:
103  *
104  * func({ H1, H2 })
105  * func({ H4, H6, H5 })
106  *
107  * dealing with the hits associated to `T1` first, and `T2` next.
108  */
109  template <typename A, typename B, typename D, typename F>
110  void
112  {
113  ranges::for_each(assns | ranges::views::all |
114  ranges::views::group_by([](auto a1, auto a2) {
115  return a1.first == a2.first;
116  }) |
117  ranges::views::transform([](auto pairs) {
118  return pairs | ranges::views::values;
119  }),
120  func);
121  }
122 
123  /*
124  * @brief Helper functions to access associations in order
125  * @tparam A type of association being read
126  * @tparam F type of functor to be called on each LHS and associated RHS
127  * @param assns the association being read
128  * @param func functor to be called on each LHS and associated RHS
129  *
130  * This function performs the following:
131  * -- takes an association collection as the first argument
132  * -- calls the provided callable object
133  * For example, given an art::Assns<L, R>, it will transform this
134  * collection to a form that supports calling a function with two arguments
135  * (auto const & left, auto rights)
136  * The provided callable is invoked for each unique left.
137  *
138  */
139  template <typename A, typename B, typename D, typename F>
140  void
142  {
143  for_each_pair(assns, [&func](auto rng) {
144  auto rights = rng | ranges::views::values;
145  auto lefts = rng | ranges::views::keys;
146  auto const& left = **ranges::begin(lefts);
147  func(left, rights);
148  });
149  }
150 
151  template <typename A, typename B, typename D, typename F>
152  void
154  {
155  ranges::for_each(assns | ranges::views::all |
157  [](auto a1, auto a2) { return a1.first == a2.first; }),
158  func);
159  }
160 
161 } // namespace art
162 
163 #endif /* canvas_Persistency_Common_AssnsAlgorithms_h */
164 
165 // Local Variables:
166 // mode: c++
167 // End:
#define a2
void for_each_group_with_left(art::Assns< A, B, D > const &assns, F func)
Q_UINT16 values[128]
constexpr auto const & left(const_AssnsIter< L, R, D, Dir > const &a, const_AssnsIter< L, R, D, Dir > const &b)
Definition: AssnsIter.h:94
static QInternalList< QTextCodec > * all
Definition: qtextcodec.cpp:63
void for_each_group(art::Assns< A, B, D > const &assns, F func)
Helper functions to access associations in order.
void for_each_pair(art::Assns< A, B, D > const &assns, F func)
def func()
Definition: docstring.py:7
def group_by(rflist, field)
Definition: __init__.py:162
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:72
#define a1