CMS 3D CMS Logo

getThinned_implementation.h
Go to the documentation of this file.
1 #ifndef DataFormats_Common_getThinned_implementation_h
2 #define DataFormats_Common_getThinned_implementation_h
3 
4 #include <algorithm>
5 #include <cassert>
6 #include <functional>
7 #include <optional>
8 #include <tuple>
9 #include <variant>
10 #include <limits>
11 
17 
18 namespace edm {
19  class WrapperBase;
20 
21  namespace detail {
23 
25  public:
26  ThinnedOrSlimmedProduct() = default;
27  explicit ThinnedOrSlimmedProduct(WrapperBase const* thinned, unsigned int key)
28  : thinnedProduct_{thinned}, thinnedKey_{key} {}
29  explicit ThinnedOrSlimmedProduct(ThinnedAssociation const* slimmed, unsigned int key)
30  : slimmedAssociation_{slimmed}, thinnedKey_{key} {}
31 
32  bool hasThinned() const { return thinnedProduct_ != nullptr; }
33  bool hasSlimmed() const { return slimmedAssociation_ != nullptr; }
34 
35  std::tuple<WrapperBase const*, unsigned int> thinnedProduct() const {
37  }
38 
39  std::tuple<ThinnedAssociation const*, unsigned int> slimmedAssociation() const {
41  }
42 
43  private:
44  WrapperBase const* thinnedProduct_ = nullptr;
46  unsigned int thinnedKey_ = 0;
47  };
48 
49  // This is a helper function to recursively search for a thinned
50  // product containing the parent key on the same "slimming depth". That
51  // means that when the recursion encounters a slimmed collection,
52  // the tree traversal does not proceed onto the children of the
53  // slimmed collection. Instead, the slimmed ThinnedAssociation is
54  // recorded for the case that the entire tree on a given "slimming
55  // depth" does not have any thinned-only collections.
56  //
57  // Returns
58  // - (WrapperBase, unsigned) in case a thinned collection
59  // containing the parent key was found.
60  // - (ThinnedAssociation, unsigned) in case no thinned collections
61  // were encountered, but a slimmed collection containing the
62  // parent key was found
63  // - otherwise "null" (i.e. only thinned collections without the
64  // parent key, or in absence of thinned collections the slimmed
65  // collection without the parent key, or no thinned or slimmed
66  // collections)
67  template <typename F1, typename F2, typename F3>
69  unsigned int key,
70  ThinnedAssociationsHelper const& thinnedAssociationsHelper,
71  F1 pidToBid,
72  F2 getThinnedAssociation,
73  F3 getByProductID) {
74  BranchID parent = pidToBid(pid);
75 
76  auto associatedBranches = thinnedAssociationsHelper.parentBegin(parent);
77  auto const iEnd = thinnedAssociationsHelper.parentEnd(parent);
78 
79  if (associatedBranches == iEnd) {
80  return ThinnedOrSlimmedProduct();
81  }
82  bool const slimmedAllowed = (associatedBranches + 1 == iEnd);
83  if (slimmedAllowed and associatedBranches->isSlimmed()) {
84  // Slimmed container can be considered only if it has no (thinned) siblings
85  ThinnedAssociation const* slimmedAssociation = getThinnedAssociation(associatedBranches->association());
86  if (slimmedAssociation == nullptr or
87  associatedBranches->parent() != pidToBid(slimmedAssociation->parentCollectionID())) {
88  return ThinnedOrSlimmedProduct();
89  }
90 
91  // Does this slimmed container have the element referenced by key?
92  auto slimmedIndex = slimmedAssociation->getThinnedIndex(key);
93  if (slimmedIndex.has_value()) {
94  return ThinnedOrSlimmedProduct(slimmedAssociation, *slimmedIndex);
95  } else {
96  return ThinnedOrSlimmedProduct();
97  }
98  }
99 
100  // Loop over thinned containers which were made by selecting elements from the parent container
101  for (; associatedBranches != iEnd; ++associatedBranches) {
102  if (associatedBranches->isSlimmed()) {
103  continue;
104  }
105 
106  ThinnedAssociation const* thinnedAssociation = getThinnedAssociation(associatedBranches->association());
107  if (thinnedAssociation == nullptr)
108  continue;
109 
110  if (associatedBranches->parent() != pidToBid(thinnedAssociation->parentCollectionID())) {
111  continue;
112  }
113 
114  // Does this thinned container have the element referenced by key?
115  auto thinnedIndex = thinnedAssociation->getThinnedIndex(key);
116  if (not thinnedIndex.has_value()) {
117  continue;
118  }
119 
120  // Return a pointer to thinned container if we can find it
121  ProductID const& thinnedCollectionPID = thinnedAssociation->thinnedCollectionID();
122  WrapperBase const* thinnedCollection = getByProductID(thinnedCollectionPID);
123  if (thinnedCollection != nullptr) {
124  return ThinnedOrSlimmedProduct(thinnedCollection, *thinnedIndex);
125  }
126 
127  // Thinned container is not found, try looking recursively in thinned containers
128  // which were made by selecting elements from this thinned container.
129  auto thinnedOrSlimmed = getThinnedProductOnSlimmingDepth(thinnedCollectionPID,
130  *thinnedIndex,
131  thinnedAssociationsHelper,
132  pidToBid,
133  getThinnedAssociation,
134  getByProductID);
135  if (thinnedOrSlimmed.hasThinned() or (slimmedAllowed and thinnedOrSlimmed.hasSlimmed())) {
136  return thinnedOrSlimmed;
137  }
138  }
139 
140  return ThinnedOrSlimmedProduct();
141  }
142 
143  inline auto makeThinnedIndexes(std::vector<unsigned int> const& keys,
144  std::vector<WrapperBase const*> const& foundContainers,
145  ThinnedAssociation const* thinnedAssociation) {
146  unsigned const nKeys = keys.size();
147  std::vector<unsigned int> thinnedIndexes(nKeys, kThinningDoNotLookForThisIndex);
148  bool hasAny = false;
149  for (unsigned k = 0; k < nKeys; ++k) {
150  // Already found this one
151  if (foundContainers[k] != nullptr) {
152  continue;
153  }
154  // Already know this one is not in this thinned container
156  continue;
157  }
158  // Does the thinned container hold the entry of interest?
159  if (auto thinnedIndex = thinnedAssociation->getThinnedIndex(keys[k]); thinnedIndex.has_value()) {
160  thinnedIndexes[k] = *thinnedIndex;
161  hasAny = true;
162  }
163  }
164  return std::tuple(std::move(thinnedIndexes), hasAny);
165  }
166 
167  // This is a helper function to recursive search ffor thinned
168  // collections that contain some of the parent keys on the same
169  // "slimming depth". That means that when the recursion encounters
170  // a slimmed colleciton, the tree traversal does not proceed onto
171  // the children of the slimmed collection. Instead, the slimmed
172  // ThinnedAssociation is recorded for the case that the entire
173  // tree on a given "slimming depth" does not have any thinned-only
174  // collections.
175  //
176  // Returns a (ThinnedAssociation, vector<unsigned>) in case no
177  // thinned collections were encountered, but a slimmed collection
178  // containing at least one of the parent keys was found. The
179  // returned vector contains keys to the slimmed collection, and
180  // the output arguments foundContainers and keys are not modified
181  // in this case.
182  //
183  // Otherwise returns a null optional (i.e. any thinned collection
184  // was encountered, or in absence of thinned collections the
185  // slimmed collection did not contain any parent keys, or there
186  // were no thinned or slimmed collections)
187  template <typename F1, typename F2, typename F3>
188  std::optional<std::tuple<ThinnedAssociation const*, std::vector<unsigned int>>> getThinnedProductsOnSlimmingDepth(
189  ProductID const& pid,
190  ThinnedAssociationsHelper const& thinnedAssociationsHelper,
191  F1 pidToBid,
192  F2 getThinnedAssociation,
193  F3 getByProductID,
194  std::vector<WrapperBase const*>& foundContainers,
195  std::vector<unsigned int>& keys) {
196  BranchID parent = pidToBid(pid);
197 
198  auto associatedBranches = thinnedAssociationsHelper.parentBegin(parent);
199  auto const iEnd = thinnedAssociationsHelper.parentEnd(parent);
200 
201  if (associatedBranches == iEnd) {
202  return std::nullopt;
203  }
204  bool const slimmedAllowed = associatedBranches + 1 == iEnd;
205  if (slimmedAllowed and associatedBranches->isSlimmed()) {
206  // Slimmed container can be considered only if it has no (thinned) siblings
207  ThinnedAssociation const* slimmedAssociation = getThinnedAssociation(associatedBranches->association());
208  if (slimmedAssociation == nullptr or
209  associatedBranches->parent() != pidToBid(slimmedAssociation->parentCollectionID())) {
210  return std::nullopt;
211  }
212 
213  auto [slimmedIndexes, hasAny] = makeThinnedIndexes(keys, foundContainers, slimmedAssociation);
214  // Does this slimmed container have any of the elements referenced by keys?
215  if (hasAny) {
216  return std::tuple(slimmedAssociation, std::move(slimmedIndexes));
217  } else {
218  return std::nullopt;
219  }
220  }
221 
222  // Loop over thinned containers which were made by selecting elements from the parent container
223  for (; associatedBranches != iEnd; ++associatedBranches) {
224  if (associatedBranches->isSlimmed()) {
225  continue;
226  }
227 
228  ThinnedAssociation const* thinnedAssociation = getThinnedAssociation(associatedBranches->association());
229  if (thinnedAssociation == nullptr)
230  continue;
231 
232  if (associatedBranches->parent() != pidToBid(thinnedAssociation->parentCollectionID())) {
233  continue;
234  }
235 
236  auto [thinnedIndexes, hasAny] = makeThinnedIndexes(keys, foundContainers, thinnedAssociation);
237  if (!hasAny) {
238  continue;
239  }
240 
241  // Set the pointers and indexes into the thinned container (if we can find it)
242  ProductID thinnedCollectionPID = thinnedAssociation->thinnedCollectionID();
243  WrapperBase const* thinnedCollection = getByProductID(thinnedCollectionPID);
244  unsigned const nKeys = keys.size();
245  if (thinnedCollection == nullptr) {
246  // Thinned container is not found, try looking recursively in thinned containers
247  // which were made by selecting elements from this thinned container.
248  auto slimmed = getThinnedProductsOnSlimmingDepth(thinnedCollectionPID,
249  thinnedAssociationsHelper,
250  pidToBid,
251  getThinnedAssociation,
252  getByProductID,
253  foundContainers,
254  thinnedIndexes);
255  if (slimmedAllowed and slimmed.has_value()) {
256  return slimmed;
257  }
258  for (unsigned k = 0; k < nKeys; ++k) {
259  if (foundContainers[k] == nullptr)
260  continue;
261  if (thinnedIndexes[k] == kThinningDoNotLookForThisIndex)
262  continue;
263  keys[k] = thinnedIndexes[k];
264  }
265  } else {
266  for (unsigned k = 0; k < nKeys; ++k) {
267  if (thinnedIndexes[k] == kThinningDoNotLookForThisIndex)
268  continue;
269  keys[k] = thinnedIndexes[k];
270  foundContainers[k] = thinnedCollection;
271  }
272  }
273  }
274  return std::nullopt;
275  }
276 
277  // This function provides a common implementation of
278  // EDProductGetter::getThinnedProduct() for EventPrincipal,
279  // DataGetterHelper, and BareRootProductGetter.
280  //
281  // getThinnedProduct assumes getIt was already called and failed to find
282  // the product. The input key is the index of the desired element in the
283  // container identified by ProductID (which cannot be found).
284  // If the return value is not null, then the desired element was
285  // found in a thinned container. If the desired element is not
286  // found, then an optional without a value is returned.
287  template <typename F1, typename F2, typename F3>
288  std::optional<std::tuple<WrapperBase const*, unsigned int>> getThinnedProduct(
289  ProductID const& pid,
290  unsigned int key,
291  ThinnedAssociationsHelper const& thinnedAssociationsHelper,
292  F1 pidToBid,
293  F2 getThinnedAssociation,
294  F3 getByProductID) {
295  auto thinnedOrSlimmed = getThinnedProductOnSlimmingDepth(
296  pid, key, thinnedAssociationsHelper, pidToBid, getThinnedAssociation, getByProductID);
297 
298  if (thinnedOrSlimmed.hasThinned()) {
299  return thinnedOrSlimmed.thinnedProduct();
300  } else if (thinnedOrSlimmed.hasSlimmed()) {
301  auto [slimmedAssociation, slimmedIndex] = thinnedOrSlimmed.slimmedAssociation();
302  ProductID const& slimmedCollectionPID = slimmedAssociation->thinnedCollectionID();
303  WrapperBase const* slimmedCollection = getByProductID(slimmedCollectionPID);
304  if (slimmedCollection == nullptr) {
305  // Slimmed container is not found, try looking recursively in thinned containers
306  // which were made by selecting elements from this thinned container.
307  return getThinnedProduct(slimmedCollectionPID,
308  slimmedIndex,
309  thinnedAssociationsHelper,
310  pidToBid,
311  getThinnedAssociation,
312  getByProductID);
313  }
314  return std::tuple(slimmedCollection, slimmedIndex);
315  }
316  return std::nullopt;
317  }
318 
319  // This function provides a common implementation of
320  // EDProductGetter::getThinnedProducts() for EventPrincipal,
321  // DataGetterHelper, and BareRootProductGetter.
322  //
323  // getThinnedProducts assumes getIt was already called and failed to find
324  // the product. The input keys are the indexes into the container identified
325  // by ProductID (which cannot be found). On input the WrapperBase pointers
326  // must all be set to nullptr (except when the function calls itself
327  // recursively where non-null pointers mark already found elements).
328  // Thinned containers derived from the product are searched to see
329  // if they contain the desired elements. For each that is
330  // found, the corresponding WrapperBase pointer is set and the key
331  // is modified to be the key into the container where the element
332  // was found. The WrapperBase pointers might or might not all point
333  // to the same thinned container.
334  template <typename F1, typename F2, typename F3>
335  void getThinnedProducts(ProductID const& pid,
336  ThinnedAssociationsHelper const& thinnedAssociationsHelper,
337  F1 pidToBid,
338  F2 getThinnedAssociation,
339  F3 getByProductID,
340  std::vector<WrapperBase const*>& foundContainers,
341  std::vector<unsigned int>& keys) {
342  auto slimmed = getThinnedProductsOnSlimmingDepth(
343  pid, thinnedAssociationsHelper, pidToBid, getThinnedAssociation, getByProductID, foundContainers, keys);
344  if (slimmed.has_value()) {
345  // no thinned procucts found, try out slimmed next if one is available
346  auto [slimmedAssociation, slimmedIndexes] = std::move(*slimmed);
347  ProductID const& slimmedCollectionPID = slimmedAssociation->thinnedCollectionID();
348  WrapperBase const* slimmedCollection = getByProductID(slimmedCollectionPID);
349  unsigned const nKeys = keys.size();
350  if (slimmedCollection == nullptr) {
351  getThinnedProducts(slimmedCollectionPID,
352  thinnedAssociationsHelper,
353  pidToBid,
354  getThinnedAssociation,
355  getByProductID,
356  foundContainers,
357  slimmedIndexes);
358  for (unsigned k = 0; k < nKeys; ++k) {
359  if (foundContainers[k] == nullptr)
360  continue;
361  if (slimmedIndexes[k] == kThinningDoNotLookForThisIndex)
362  continue;
363  keys[k] = slimmedIndexes[k];
364  }
365  } else {
366  for (unsigned k = 0; k < nKeys; ++k) {
367  if (slimmedIndexes[k] == kThinningDoNotLookForThisIndex)
368  continue;
369  keys[k] = slimmedIndexes[k];
370  foundContainers[k] = slimmedCollection;
371  }
372  }
373  }
374  }
375 
376  using GetThinnedKeyFromExceptionFactory = std::function<edm::Exception()>;
377 
378  // This function provides a common implementation of
379  // EDProductGetter::getThinnedKeyFrom() for EventPrincipal,
380  // DataGetterHelper, and BareRootProductGetter.
381  //
382  // The thinned ProductID must come from an existing RefCore. The
383  // input key is the index of the desired element in the container
384  // identified by the parent ProductID. Returns an std::variant
385  // whose contents can be
386  // - unsigned int for the index in the thinned collection if the
387  // desired element was found in the thinned collection
388  // - function creating an edm::Exception if parent is not a parent
389  // of any thinned collection, thinned is not really a thinned
390  // collection, or parent and thinned have no thinning
391  // relationship
392  // - std::monostate if thinned is thinned from parent, but the key
393  // is not found in the thinned collection
394  template <typename F>
395  std::variant<unsigned int, GetThinnedKeyFromExceptionFactory, std::monostate> getThinnedKeyFrom_implementation(
396  ProductID const& parentID,
397  BranchID const& parent,
398  unsigned int key,
399  ProductID const& thinnedID,
400  BranchID thinned,
401  ThinnedAssociationsHelper const& thinnedAssociationsHelper,
402  F&& getThinnedAssociation) {
403  // need to explicitly check for equality of parent BranchID,
404  // because ThinnedAssociationsHelper::parentBegin() uses
405  // std::lower_bound() that returns a valid iterator in case the
406  // parent is not found
407  if (auto iParent = thinnedAssociationsHelper.parentBegin(parent);
408  iParent == thinnedAssociationsHelper.parentEnd(parent) or iParent->parent() != parent) {
409  return [parentID]() {
411  << "Parent collection with ProductID " << parentID << " has not been thinned";
412  };
413  }
414 
415  bool foundParent = false;
416  std::vector<ThinnedAssociation const*> thinnedAssociationParentage;
417  while (not foundParent) {
418  // TODO: be smarter than linear search every time?
419  auto branchesToThinned = std::find_if(
420  thinnedAssociationsHelper.begin(), thinnedAssociationsHelper.end(), [&thinned](auto& associatedBranches) {
421  return associatedBranches.thinned() == thinned;
422  });
423  if (branchesToThinned == thinnedAssociationsHelper.end()) {
424  return [parentID, thinnedID, thinnedIsThinned = not thinnedAssociationParentage.empty()]() {
426  ex << "Requested thinned collection with ProductID " << thinnedID
427  << " is not thinned from the parent collection with ProductID " << parentID
428  << " or from any collection thinned from it.";
429  if (not thinnedIsThinned) {
430  ex << " In fact, the collection " << thinnedID
431  << " passed in as a 'thinned' collection has not been thinned at all.";
432  }
433  return ex;
434  };
435  }
436 
437  ThinnedAssociation const* thinnedAssociation = getThinnedAssociation(branchesToThinned->association());
438  if (thinnedAssociation == nullptr) {
440  if (thinnedAssociationParentage.empty()) {
441  ex << "ThinnedAssociation corresponding to thinned collection with ProductID " << thinnedID
442  << " not found.";
443  } else {
444  ex << "Intermediate ThinnedAssociation between the requested thinned ProductID " << thinnedID
445  << " and parent " << parentID << " not found.";
446  }
447  ex << " This should not happen.\nPlease contact the core framework developers.";
448  throw ex;
449  }
450 
451  thinnedAssociationParentage.push_back(thinnedAssociation);
452  if (branchesToThinned->parent() == parent) {
453  foundParent = true;
454  } else {
455  // next iteration with current parent as the thinned collection
456  thinned = branchesToThinned->parent();
457  }
458  }
459 
460  // found the parent, now need to rewind the parentage chain to
461  // find the index in the requested thinned collection
462  unsigned int thinnedIndex = key;
463  for (auto iAssociation = thinnedAssociationParentage.rbegin(), iEnd = thinnedAssociationParentage.rend();
464  iAssociation != iEnd;
465  ++iAssociation) {
466  auto optIndex = (*iAssociation)->getThinnedIndex(thinnedIndex);
467  if (optIndex) {
468  thinnedIndex = *optIndex;
469  } else {
470  return std::monostate{};
471  }
472  }
473  return thinnedIndex;
474  }
475 
476  } // namespace detail
477 } // namespace edm
478 
479 #endif
ProductID const & parentCollectionID() const
auto makeThinnedIndexes(std::vector< unsigned int > const &keys, std::vector< WrapperBase const *> const &foundContainers, ThinnedAssociation const *thinnedAssociation)
constexpr unsigned int kThinningDoNotLookForThisIndex
std::vector< ThinnedAssociationBranches >::const_iterator parentEnd(BranchID const &) const
std::optional< std::tuple< WrapperBase const *, unsigned int > > getThinnedProduct(ProductID const &pid, unsigned int key, ThinnedAssociationsHelper const &thinnedAssociationsHelper, F1 pidToBid, F2 getThinnedAssociation, F3 getByProductID)
std::variant< unsigned int, GetThinnedKeyFromExceptionFactory, std::monostate > getThinnedKeyFrom_implementation(ProductID const &parentID, BranchID const &parent, unsigned int key, ProductID const &thinnedID, BranchID thinned, ThinnedAssociationsHelper const &thinnedAssociationsHelper, F &&getThinnedAssociation)
void getThinnedProducts(ProductID const &pid, ThinnedAssociationsHelper const &thinnedAssociationsHelper, F1 pidToBid, F2 getThinnedAssociation, F3 getByProductID, std::vector< WrapperBase const *> &foundContainers, std::vector< unsigned int > &keys)
ProductID const & thinnedCollectionID() const
std::optional< unsigned int > getThinnedIndex(unsigned int parentIndex) const
The Signals That Services Can Subscribe To This is based on ActivityRegistry and is current per Services can connect to the signals distributed by the ActivityRegistry in order to monitor the activity of the application Each possible callback has some defined which we here list in angle e< void, edm::EventID const &, edm::Timestamp const & > We also list in braces which AR_WATCH_USING_METHOD_ is used for those or
Definition: Activities.doc:12
std::vector< ThinnedAssociationBranches >::const_iterator begin() const
std::vector< ThinnedAssociationBranches >::const_iterator end() const
ThinnedOrSlimmedProduct getThinnedProductOnSlimmingDepth(ProductID const &pid, unsigned int key, ThinnedAssociationsHelper const &thinnedAssociationsHelper, F1 pidToBid, F2 getThinnedAssociation, F3 getByProductID)
std::tuple< ThinnedAssociation const *, unsigned int > slimmedAssociation() const
std::optional< std::tuple< ThinnedAssociation const *, std::vector< unsigned int > > > getThinnedProductsOnSlimmingDepth(ProductID const &pid, ThinnedAssociationsHelper const &thinnedAssociationsHelper, F1 pidToBid, F2 getThinnedAssociation, F3 getByProductID, std::vector< WrapperBase const *> &foundContainers, std::vector< unsigned int > &keys)
std::vector< ThinnedAssociationBranches >::const_iterator parentBegin(BranchID const &) const
HLT enums.
ThinnedOrSlimmedProduct(ThinnedAssociation const *slimmed, unsigned int key)
std::function< edm::Exception()> GetThinnedKeyFromExceptionFactory
ThinnedOrSlimmedProduct(WrapperBase const *thinned, unsigned int key)
ThinnedAssociation const * slimmedAssociation_
static uInt32 F(BLOWFISH_CTX *ctx, uInt32 x)
Definition: blowfish.cc:163
std::tuple< WrapperBase const *, unsigned int > thinnedProduct() const
def move(src, dest)
Definition: eostools.py:511