2 * @file lardata/Utilities/FindManyInChainP.tcc
3 * @brief Template implementation for `FindManyInChainP.h`.
4 * @author Gianluca Petrillo (petrillo@fnal.gov)
9 #ifndef LARDATA_UTILITIES_FINDMANYINCHAINP_TCC
10 #define LARDATA_UTILITIES_FINDMANYINCHAINP_TCC
12 #ifndef LARDATA_UTILITIES_FINDMANYINCHAINP_H
13 #error "FindManyInChainP.tcc must not be included directly. Include FindManyInChainP.h instead."
14 #endif // LARDATA_UTILITIES_FINDMANYINCHAINP_H
17 #include "art/Framework/Principal/Handle.h"
18 #include "art/Persistency/Provenance/ProductMetaData.h"
19 #include "canvas/Persistency/Common/FindManyP.h"
20 #include "canvas/Persistency/Common/Assns.h"
21 #include "canvas/Utilities/Exception.h"
23 // C/C++ standard library
25 #include <iterator> // std::begin(), std::cbegin(), std::distance()...
26 #include <tuple> // std::tuple_cat(), ...
27 #include <algorithm> // std::lower_bound(), std::sort(), std::transform()...
28 #include <utility> // std::pair<>, std::move(), std::declval()...
29 #include <type_traits> // std::decay_t<>, std::enable_if_t<>, ...
32 //------------------------------------------------------------------------------
33 //--- template implementation
39 //--------------------------------------------------------------------------
40 // this is a copy of things in cetlib; should be replaced by the original
41 // if they get exposed
42 template <class T, class R /* = void */>
43 struct enable_if_type_exists { using type = R; };
45 //--------------------------------------------------------------------------
46 /// Trait evaluating to true if H is a art Handle type.
47 template <typename H, typename = void>
48 struct is_handle: public std::false_type {};
51 struct is_handle<H, enable_if_is_handle_t<H>>: public std::true_type {};
54 constexpr bool is_handle_v = is_handle<H>::value;
56 //--------------------------------------------------------------------------
58 struct is_art_ptr_impl: public std::false_type {};
61 struct is_art_ptr_impl<art::Ptr<T>>: public std::true_type {};
63 /// Whether the specified type is an art::Ptr.
65 using is_art_ptr = is_art_ptr_impl<std::decay_t<T>>;
68 //--------------------------------------------------------------------------
70 /// Hosts as `type` the type in position `N` (first is `0`).
71 // this might be implemented with std::tuple_element;
72 // here we get control on error messages
73 template <unsigned int N, typename... Args>
76 template <unsigned int N, typename... Args>
77 using get_type_t = typename get_type<N, Args...>::type;
80 template <unsigned int N, typename First, typename... Others>
81 struct get_type<N, First, Others...> {
83 (N <= sizeof...(Others), "No type in the specified position.");
84 using type = get_type_t<N-1, Others...>;
87 template <typename First, typename... Others>
88 struct get_type<0U, First, Others...> { using type = First; };
91 //--------------------------------------------------------------------------
93 template <unsigned int NCopies>
94 struct TupleAppender {
95 template <typename Tuple, typename T>
96 static auto append(Tuple&& t, T const& value)
98 return TupleAppender<(NCopies - 1)>::append(
99 std::tuple_cat(std::forward<Tuple>(t), std::make_tuple(value)),
103 }; // TupleAppender<>
106 struct TupleAppender<0> {
107 template <typename Tuple, typename T>
108 static auto append(Tuple&& t, T const&) { return t; }
109 }; // TupleAppender<0>
112 template <unsigned int NCopies, typename T, typename Tuple>
113 auto appendToTuple(Tuple&& t, T value)
114 { return TupleAppender<NCopies>::append(std::forward<Tuple>(t), value); }
117 //--------------------------------------------------------------------------
118 /// Returns the input tag of the product identified by the handle.
119 template <typename Handle>
120 art::InputTag tagFromHandle(Handle&& handle)
121 { return handle.provenance()->inputTag(); }
123 /// Returns the input tag of the product identified by `id`.
124 template <typename Data, typename Event>
125 art::InputTag tagFromProductID
126 (art::ProductID const& id, Event const& /* event */)
129 // This implementation will need to be revisited with art 3.0 and in
130 // general with multithreading (note the singleton product list).
132 // Also note that this is not efficient for repeated queries, in which
133 // case the product list should be resorted into some map with product ID
136 auto const& products = art::ProductMetaData::instance().productList();
137 for (auto const& productInfoPair: products) {
138 auto const& bd = productInfoPair.second; // branch description here
139 if (bd.productID() != id) continue;
140 return { bd.moduleLabel(), bd.productInstanceName(), bd.processName() };
142 throw art::Exception(art::errors::ProductNotFound)
143 << "Couldn't find data product with product ID " << id << "\n";
144 } // tagFromProductID()
147 //--------------------------------------------------------------------------
148 template <typename Value>
149 class SimpleDataIndex {
151 using Value_t = Value;
153 /// Constructor: indexes pointers to data in the specified collection.
154 template <typename BeginIter, typename EndIter>
155 SimpleDataIndex(BeginIter begin, EndIter end) { init(begin, end); }
157 /// Constructor: indexes pointers to data in the specified collection.
158 template <typename Coll>
159 SimpleDataIndex(Coll& data)
160 { using std::begin; using std::end; init(begin(data), end(data)); }
162 /// Returns a pointer to the matched data, nullptr if none.
163 std::size_t const* operator() (Value_t const& key) const
165 auto const e = index.cend();
166 auto const it = std::lower_bound(index.cbegin(), e, key, comparer());
167 return ((it == e) || (key < it->first))? nullptr: &(it->second);
172 using IndexElement_t = std::pair<Value_t const*, std::size_t>;
174 std::vector<IndexElement_t> index; ///< The index.
176 template <typename BeginIter, typename EndIter>
177 void init(BeginIter begin, EndIter end)
179 index.reserve(std::distance(begin, end)); // hope it's fast
181 for (auto it = begin; it != end; ++it, ++i)
182 index.emplace_back(&*it, i);
183 std::sort(index.begin(), index.end(), comparer());
187 bool operator() (Value_t const& a, Value_t const& b) const
189 bool operator() (IndexElement_t const& a, Value_t const& b) const
190 { return *(a.first) < b; }
191 bool operator() (Value_t const& a, IndexElement_t const& b) const
192 { return a < *(b.first); }
193 bool operator() (IndexElement_t const& a, IndexElement_t const& b) const
194 { return *(a.first) < *(b.first); }
195 }; // struct KeyComparer
197 static auto comparer() { return KeyComparer(); }
199 }; // class SimpleDataIndex
202 template <typename Key, typename Value>
206 using Value_t = Value;
207 using ValuePtr_t = std::add_pointer_t<Value_t>;
209 /// Constructor: indexes pointers to data in the specified collection.
210 template <typename BeginIter, typename EndIter, typename KeyExtractor>
211 DataIndex(BeginIter begin, EndIter end, KeyExtractor&& getKey)
212 { init(begin, end, std::forward<KeyExtractor>(getKey)); }
214 /// Constructor: indexes pointers to data in the specified collection.
215 template <typename Coll, typename KeyExtractor>
216 DataIndex(Coll& data, KeyExtractor&& getKey)
220 init(begin(data), end(data), std::forward<KeyExtractor>(getKey));
223 /// Returns a pointer to the matched data, nullptr if none.
224 ValuePtr_t operator() (Key_t const& key) const
226 auto const e = index.cend();
227 auto const it = std::lower_bound(index.cbegin(), e, key, comparer());
228 return ((it == e) || (key < it->first))? nullptr: it->second;
233 using IndexElement_t = std::pair<Key_t, Value_t*>;
235 std::vector<IndexElement_t> index; ///< The index.
237 template <typename BeginIter, typename EndIter, typename KeyExtractor>
238 void init(BeginIter begin, EndIter end, KeyExtractor&& getKey)
240 index.reserve(std::distance(begin, end)); // hope it's fast
241 for (auto it = begin; it != end; ++it)
242 index.emplace_back(getKey(*it), &*it);
243 std::sort(index.begin(), index.end(), comparer());
247 bool operator() (Key_t const& a, Key_t const& b) const
249 bool operator() (IndexElement_t const& a, Key_t const& b) const
250 { return a.first < b; }
251 bool operator() (Key_t const& a, IndexElement_t const& b) const
252 { return a < b.first; }
253 bool operator() (IndexElement_t const& a, IndexElement_t const& b) const
254 { return a.first < b.first; }
255 }; // struct KeyComparer
257 static auto comparer() { return KeyComparer(); }
259 }; // class DataIndex
262 /// Returns a new index for data using the keys extracted by getKey.
263 template <typename Coll, typename KeyExtractor>
264 auto makeIndex(Coll& data, KeyExtractor&& getKey)
266 using Value_t = typename Coll::value_type;
267 using Key_t = std::decay_t<decltype(getKey(std::declval<Value_t>()))>;
269 DataIndex<Key_t, Value_t>(data, std::forward<KeyExtractor>(getKey));
272 /// Returns a new index for data in range using the data itself as key.
273 template <typename BeginIter, typename EndIter>
274 auto makeSimpleIndex(BeginIter begin, EndIter end)
276 using Value_t = std::decay_t<decltype(*begin)>;
277 return SimpleDataIndex<Value_t>(begin, end);
280 /// Returns a new index for data using the data itself as key.
281 template <typename Coll>
282 auto makeSimpleIndex(Coll& data)
284 using Value_t = typename Coll::value_type;
285 return SimpleDataIndex<Value_t>(data);
289 //--------------------------------------------------------------------------
290 //--- finders implementation
293 namespace AssociationFinderBase {
295 /// Information on a single source: its index in the source list, and
296 /// "key" (it can be any sortable object).
297 template <typename Key = std::size_t>
298 struct SourceIDinfo_t: public std::tuple<Key, std::size_t> {
299 using Base_t = std::tuple<Key, std::size_t>;
302 /// Returns the key of the art pointer to this source element.
303 Key_t const& key() const { return std::get<0>(*this); }
304 /// Returns the index of this source element within the source list.
305 std::size_t position() const { return std::get<1>(*this); }
307 using Base_t::Base_t; // inherit constructor
309 /// Comparison functor: compares art pointer keys.
312 (SourceIDinfo_t const& a, SourceIDinfo_t const& b) const
313 { return a.key() < b.key(); }
314 bool operator() (SourceIDinfo_t const& a, Key_t const& b) const
315 { return a.key() < b; }
316 bool operator() (Key_t const& a, SourceIDinfo_t const& b) const
317 { return a < b.key(); }
318 }; // struct SourceIDinfo_t::Comparer_t
320 static Comparer_t comparer() { return {}; }
321 }; // struct SourceIDinfo_t
323 template <typename Key = std::size_t>
324 struct SourceVector_t: private std::vector<SourceIDinfo_t<Key>> {
325 using Base_t = std::vector<SourceIDinfo_t<Key>>;
326 using Info_t = typename Base_t::value_type;
327 using Key_t = typename Info_t::Key_t;
329 using const_iterator = typename Base_t::const_iterator;
335 using Base_t::reserve;
337 struct MatchConstIterator_t: public std::pair<const_iterator, bool> {
338 using Base_t = std::pair<const_iterator, bool>;
339 using Base_t::Base_t; // inherit all constructors
340 auto const& operator->() const { return std::get<0>(*this); }
341 operator bool() const { return std::get<1>(*this); }
342 bool operator!() const { return !this->operator bool(); }
343 }; // struct MatchConstIterator_t
345 /// Default constructor: starts empty.
346 SourceVector_t() = default;
348 /// Constructor: from the elements between the specified iterators.
349 template <typename BeginIter, typename EndIter>
350 SourceVector_t(BeginIter b, EndIter e) { assign(b, e); }
352 /// Sorts the vector according to keys.
354 { std::sort(Base_t::begin(), Base_t::end(), Info_t::comparer()); }
356 /// Constructs a new element at the end of the vector.
357 void emplace_back(Key_t const& key, std::size_t pos)
358 { Base_t::emplace_back(key, pos); }
360 /// Returns iterator to element with key, if it exists.
361 /// Requires the vector to be sort()ed.
362 /// If key is not present it may return end() or the wrong element.
363 auto find(Key_t const& key) const
365 auto const e = end();
366 auto it = std::lower_bound(begin(), e, key, Info_t::comparer());
367 return MatchConstIterator_t(it, (it != e) && (it->key() == key));
370 /// Resets the content to map a container delimited by the iterators.
371 template <typename BeginIter, typename EndIter>
372 void assign(BeginIter b, EndIter e)
376 for (auto it = b; it != e; ++it, ++i) emplace_back(it->key(), i);
380 }; // struct SourceVector_t
383 /// Returns a tuple of NTags elements, including all specified tags and
384 /// then copies of the default one to make it N.
385 template <unsigned int NTags, typename DefaultTag, typename... InputTags>
386 auto makeTagsTuple(DefaultTag defTag, InputTags... tags) {
387 return appendToTuple<(NTags - sizeof...(InputTags))>
388 (std::make_tuple(tags...), defTag);
392 /// A named pair of a key and all its connections.
393 /// Comparisons are based solely on key.
394 template <typename A, typename B>
395 struct Connections: private std::pair<A, std::vector<B>> {
396 using Base_t = std::pair<A, std::vector<B>>;
398 using ConnectedItem_t = B;
399 using Connections_t = Connections<A, B>;
400 using Connected_t = std::tuple_element_t<1, Base_t>;
401 using Base_t::Base_t; // import pair constructors
403 /// Additional constructor: key with no connected items.
404 Connections(Key_t const& key): Base_t(key, {}) {}
406 Key_t const& key() const { return std::get<0>(*this); }
407 Connected_t const& connectedTo() const { return std::get<1>(*this); }
408 Connected_t& connectedTo() { return std::get<1>(*this); }
411 bool operator() (Key_t const& a, Key_t const& b) const
413 bool operator() (Key_t const& a, Connections_t const& b) const
414 { return a < b.key(); }
415 bool operator() (Connections_t const& a, Key_t const& b) const
416 { return a.key() < b; }
417 bool operator() (Connections_t const& a, Connections_t const& b) const
418 { return a.key() < b.key(); }
421 static Comparer_t comparer() { return {}; }
423 /// Compares the connection lists by key only.
424 bool operator< (Connections_t const& b) const
425 { return comparer()(*this, b); }
427 }; // struct Connections<>
430 struct KeysFromTag {};
431 struct KeysFromIndexTag {};
432 struct KeysFromOtherListTag {};
433 constexpr KeysFromTag KeysFrom;
434 constexpr KeysFromIndexTag KeysFromIndex;
435 constexpr KeysFromOtherListTag KeysFromOtherList;
437 /// A connections list.
438 template <typename Key, typename Connected>
439 class ConnectionList: private std::vector<Connections<Key, Connected>> {
440 using Connections_t = Connections<Key, Connected>;
441 using Data_t = std::vector<Connections_t>;
443 Data_t& data() { return *this; }
444 Data_t const& data() const { return *this; }
447 /// Type of an item connected to the key.
448 using ConnectedItem_t = typename Connections_t::ConnectedItem_t;
449 /// Type of list of connected items.
450 using Connected_t = typename Connections_t::Connected_t;
452 // import types from the base class
453 using value_type = typename Data_t::value_type;
456 /// Initializes an empty connection list.
457 ConnectionList() = default;
459 /// Initializes with no connection for each of the keys from b to e.
460 template <typename BeginIter, typename EndIter>
461 ConnectionList(KeysFromTag, BeginIter b, EndIter e)
463 data().reserve(std::distance(b, e)); // hope this is fast
464 std::transform(b, e, std::back_inserter(data()),
465 [](auto const& key) { return Connections_t(key, {}); }
469 /// Initializes with no connection for each of the keys from a list.
470 template <typename Other>
472 (KeysFromOtherListTag, ConnectionList<Key, Other> const& source)
474 data().reserve(source.size()); // hope this is fast
478 cbegin(source), cend(source), std::back_inserter(data()),
479 [](auto const& conn) { return Connections_t(conn.key(), {}); }
483 /// Initializes with no connection for each of the generated keys.
484 template <typename KeyMaker>
485 ConnectionList(KeysFromIndexTag, std::size_t n, KeyMaker keyMaker)
488 for (std::size_t i = 0; i < n; ++i)
489 data().emplace_back(keyMaker(i));
492 // import methods from the base class (not the constructors)
494 using Data_t::operator[];
496 using Data_t::cbegin;
502 /// @name Special methods
504 void addConnectionAt(std::size_t index, ConnectedItem_t const& item)
505 { data()[index].connectedTo().push_back(item); }
506 void addConnectionAt(std::size_t index, ConnectedItem_t&& item)
507 { data()[index].connectedTo().push_back(std::move(item)); }
509 /// Returns all connected objects on a vector, one element per key.
510 /// As a R-value reference method, the current content is lost
511 std::vector<Connected_t> values() &&
513 std::vector<Connected_t> result;
514 result.reserve(data().size());
516 data().begin(), data().end(), std::back_inserter(result),
517 [](auto& conn){ return std::move(conn.connectedTo()); }
524 }; // struct ConnectionList<>
527 /// Class handling a connection list.
528 template <typename Source, typename Target>
529 class PtrConnectionManager {
532 using Manager_t = PtrConnectionManager<Source, Target>;
533 using Source_t = Source;
534 using Target_t = Target;
535 using SourcePtr_t = art::Ptr<Source_t>;
536 using TargetPtr_t = art::Ptr<Target_t>;
537 using ConnectionList_t = ConnectionList<SourcePtr_t, TargetPtr_t>;
539 /// Constructor: steals the connections from the argument.
540 PtrConnectionManager(ConnectionList_t&& data): data(std::move(data)) {}
543 /// Returns all connected objects as a single connection of pointers.
544 std::vector<TargetPtr_t> allConnected() const
547 // this might as well be implemented with ranges
549 std::vector<TargetPtr_t> result;
554 for (std::size_t i = 0; i < data.size(); ++i) {
555 auto const& connected = data[i].connectedTo();
556 result.insert(result.end(), cbegin(connected), cend(connected));
562 /// Returns a new connection list connecting each of our sources to
563 /// FurtherPtr_t objects, using shared TargetPtr_t to map connections.
564 template <typename FurtherPtr_t>
565 ConnectionList<SourcePtr_t, FurtherPtr_t> join
566 (ConnectionList<TargetPtr_t, FurtherPtr_t>&& other) const
568 // internally, we'll call the current data A -> B, other data B -> C,
569 // and we are after A -> C
570 using APtr_t = SourcePtr_t;
571 // using BPtr_t = TargetPtr_t;
572 using CPtr_t = FurtherPtr_t;
574 auto const& AB = data;
576 ConnectionList<APtr_t, CPtr_t> AC(KeysFromOtherList, data);
578 // the match object returns the iterator to the list connected to the
579 // specified key; it does not own the data structure;
581 = makeIndex(BC, [](auto const& elem){ return elem.key(); });
583 for (std::size_t iA = 0; iA < AB.size(); iA++) {
584 auto const& connAB = AB[iA]; // connections for this A
585 auto const& BsForA = connAB.connectedTo();
587 auto& CsForA = AC[iA].connectedTo(); // connected list to fill (empty)
589 // for each B connected to this A:
590 for (auto const& key: BsForA) {
592 auto iConnBC = match(key);
593 if (!iConnBC) continue; // no C's associated to the matching B
595 // matched! move or copy the while list of Cs
596 auto& CsForB = iConnBC->connectedTo(); // (this list)
597 if (CsForA.empty()) CsForA = std::move(CsForB); // move
598 else CsForA.insert(CsForA.end(), cbegin(CsForB), cend(CsForB));
600 } // for connected Bs
607 ConnectionList_t data; ///< All connection information.
609 }; // PtrConnectionManager<>
611 /// Helper function to created the right type of manager from a temporary.
612 template <typename A, typename B>
613 auto makeConnectionManager(ConnectionList<A, B>&& data)
615 return PtrConnectionManager
616 <typename A::value_type, typename B::value_type>(std::move(data));
619 } // namespace AssociationFinderBase
622 template <typename Target>
623 struct AssociationFinder {
625 using Target_t = Target;
626 using TargetPtr_t = art::Ptr<Target_t>;
628 /// Type of result of finders: one list of associated targets per source.
629 template <typename Source>
631 = AssociationFinderBase::ConnectionList<art::Ptr<Source>, TargetPtr_t>;
633 template <typename Source>
634 using Assns_t = art::Assns<Source, Target>;
636 template <typename Assns>
637 using AssnKey_t = typename Assns::left_t;
639 template <typename Assns>
640 using AssnKeyPtr_t = art::Ptr<AssnKey_t<Assns>>;
642 template <typename Assns>
643 using ResultFromAssns_t = Result_t<AssnKey_t<Assns>>;
645 template <typename Handle>
646 using ValueFromHandle_t = typename Handle::element_type::value_type;
648 template <typename Handle>
649 using AssnsFromHandle_t = Assns_t<ValueFromHandle_t<Handle>>;
651 template <typename Handle>
652 using ResultFromHandle_t = Result_t<art::Ptr<ValueFromHandle_t<Handle>>>;
654 /// Helper finding a associations from a single complete data product.
655 template <typename Assns>
656 static ResultFromAssns_t<Assns> findFromDataProduct(
657 art::ProductID const& sourceID, std::size_t nSources,
661 using SourcePtr_t = AssnKeyPtr_t<Assns>;
663 using namespace AssociationFinderBase;
665 // as many lists in result as sources, keys created from source ID
666 ResultFromAssns_t<Assns> result(
667 KeysFromIndex, nSources,
668 [id=sourceID](std::size_t i){ return SourcePtr_t(id, i, nullptr); }
671 // get the associations, following the content of the assns data product
672 for (decltype(auto) assn: assns) {
673 auto const& sourcePtr = assn.first;
675 // does this association contain a pointer with an ID different than
676 // the one we are looking for? (it may mean our assumption that the
677 // same module produced source and its association is wrong)
678 if (sourcePtr.id() != sourceID) continue;
680 // we follow the assumption that the data product is complete with
681 // nSources elements, therefore no pointer can exist with a larger
683 assert(sourcePtr.key() < nSources);
685 // push target pointer into the result of the matched source
686 result.addConnectionAt(sourcePtr.key(), assn.second);
691 } // findFromDataProduct()
694 /// Helper finding a associations from a single list.
696 typename Source, typename IndexBegin, typename IndexEnd, typename Event
698 static Result_t<Source> findSingle(
699 art::ProductID const& sourceID,
700 IndexBegin sbegin, IndexEnd send,
701 Assns_t<Source> const& assns
704 // type of the source object (hidden in PtrColl)
705 using Source_t = Source;
706 using SourcePtr_t = art::Ptr<Source_t>;
707 using namespace AssociationFinderBase;
709 std::size_t const nSources = std::distance(sbegin, send);
710 Result_t<Source_t> result(nSources);
712 // These are the source "pointers" we still have to find; they are all
713 // on the same product ID. We keep track of the original position too
714 // (unordered map may or may not be better...);
715 // given that these are all from the same product ID, the product ID is
716 // not saved. SourceVector_t provides log(N) lookup.
717 SourceVector_t<std::size_t> sourceInfos(sbegin, send);
719 // get the association, following the content of the assns data product
720 for (decltype(auto) assn: assns) {
721 SourcePtr_t const& sourcePtr = assn.first;
723 // does this association contain a pointer with an ID different than
724 // the one we are looking for? (it may mean our assumption that the
725 // same module produced source and its association is wrong)
726 if (sourcePtr.id() != sourceID) continue;
728 // is this pointer interesting?
729 auto iSrcInfo = sourceInfos.find(sourcePtr.key());
730 if (!iSrcInfo) continue; // it's not it
732 // match! push target pointer into the result of the matched source
733 result[iSrcInfo->position()].connectedTo().push_back(assn.second);
741 /// Helper finding a single degree of associations from the tag of each
742 /// source art pointer.
743 template <typename PtrCollBegin, typename PtrCollEnd, typename Event>
744 static auto findWithRange(
745 PtrCollBegin sbegin, PtrCollEnd send, Event const& event,
746 art::InputTag const& tag
749 * The strategy of this implementation is:
751 * 1. collect all source art pointers, sorted by key for faster lookup
752 * 2. parse all the associated pairs
753 * 1. if the source pointer of a pair is in the list of
754 * interesting source pointers, push the target pointer of the
755 * pair into the results for this source
759 using SourcePtr_t = std::decay_t<decltype(*sbegin)>;
760 static_assert(is_art_ptr<SourcePtr_t>(),
761 "Collection for AssociationFinder (FindManyInChainP) is not art pointers!"
764 // type of the source object (hidden in PtrColl)
765 using Source_t = typename SourcePtr_t::value_type;
766 using namespace AssociationFinderBase;
768 Result_t<Source_t> result(KeysFrom, sbegin, send);
769 // std::size_t const nSources = result.size();
771 // use this index for fast lookup of the sources
772 auto match = makeSimpleIndex(sbegin, send);
774 // fetch the association data product
776 = *(event.template getValidHandle<Assns_t<Source_t>>(tag));
778 // get the association, following the content of the assns data product
779 for (decltype(auto) assn: assns) {
780 SourcePtr_t const& sourcePtr = assn.first;
782 // is this pointer interesting?
783 auto iPos = match(sourcePtr);
784 if (!iPos) continue; // it's not it
786 // match! push target pointer into the result of the matched source
787 result[*iPos].connectedTo().push_back(assn.second);
795 /// Helper finding a single degree of associations from the tag of each
796 /// source art pointer.
797 template <typename PtrCollBegin, typename PtrCollEnd, typename Event>
798 static auto findWithRange
799 (PtrCollBegin sbegin, PtrCollEnd send, Event const& event)
802 * The strategy of this implementation is:
804 * 1. collect all the source art pointers, grouped by product ID
805 * (and sorted by key for faster lookup)
806 * 2. for each interesting product ID:
807 * 1. fetch the association collection; this is assumed to have been
808 * created with the same input tag as the source product
809 * 2. parse all the associated pairs
810 * 1. if the source pointer of a pair is in the list of
811 * interesting source pointers, push the target pointer of the
812 * pair into the results for this source
814 * The maximum complexity of this algorithm is N log(M), where M is no
815 * larger than the maximum number of source pointers with a single
816 * product ID and N is the number of associations in each association
821 using SourcePtr_t = std::decay_t<decltype(*sbegin)>;
822 static_assert(is_art_ptr<SourcePtr_t>(),
823 "Collection for AssociationFinder (FindManyInChainP) is not art pointers!"
826 // type of the source object (hidden in PtrColl)
827 using Source_t = typename SourcePtr_t::value_type;
828 using namespace AssociationFinderBase;
830 Result_t<Source_t> result(KeysFrom, sbegin, send);
832 // These are the source pointers we still have to find,
833 // organised by product ID; we keep track of the original position too
834 // (unordered map may or may not be better...);
835 // given that these are all from the same product ID, the product ID is
837 // Also, for fast lookup the lists are sorted by pointer key.
838 std::map<art::ProductID, SourceVector_t<std::size_t>>
840 std::size_t iSource = 0;
841 for (auto it = sbegin; it != send; ++it, ++iSource)
842 sourcesLeft[it->id()].emplace_back(it->key(), iSource);
843 for (auto& sourcesWithinID: sourcesLeft)
844 sourcesWithinID.second.sort();
846 // look for all sources in each product ID
847 for (auto const& sourcesWithinID: sourcesLeft) {
849 art::ProductID sourceID = sourcesWithinID.first;
850 auto const& sourceInfos = sourcesWithinID.second;
852 // need to get the association between source and target,
853 // as produced by the same producer that produced the source itself
855 = tagFromProductID<std::vector<Source_t>>(sourceID, event);
857 // fetch the association data product
859 = *(event.template getValidHandle<Assns_t<Source_t>>(tag));
861 // get the association, following the content of the assns data product
862 for (decltype(auto) assn: assns) {
863 SourcePtr_t const& sourcePtr = assn.first;
865 // does this association contain a pointer with an ID different than
866 // the one we are looking for? (it may mean our assumption that the
867 // same module produced source and its association is wrong)
868 if (sourcePtr.id() != sourceID) continue;
870 // is this pointer interesting?
871 auto iSrcInfo = sourceInfos.find(sourcePtr.key());
872 if (!iSrcInfo) continue; // it's not it
874 // match! push target pointer into the result of the matched source
875 result[iSrcInfo->position()].connectedTo().push_back(assn.second);
879 } // for sourcesWithinID
885 }; // class AssociationFinder
889 /// Helper finding a single degree of associations from a specified tag.
891 typename Target, typename Handle, typename Event,
892 typename = std::enable_if_t<is_handle_v<Handle>>
894 auto findAssociations
895 (Handle&& handle, Event const& event, art::InputTag const& assnsTag)
897 using Finder = AssociationFinder<Target>;
899 = typename Finder::template AssnsFromHandle_t<std::decay_t<Handle>>;
901 auto const& assns = *(event.template getValidHandle<Assns_t>(assnsTag));
903 // FIXME: this implementation is NOT canvas-compatible (Handle::id())
904 return Finder::findFromDataProduct(handle.id(), handle->size(), assns);
905 } // findAssociations(Handle, tag)
908 /// Helper finding a single degree of associations from any tag.
910 typename Target, typename Handle, typename Event,
911 typename = std::enable_if_t<is_handle_v<Handle>>
913 auto findAssociations(Handle handle, Event const& event, lar::SameAsDataTag)
916 * By definition, here all the interesting sources share the same product
917 * ID, which simplifies the implementation.
920 // need to get the association between source and target,
921 // as produced by the same producer that produced the source itself
922 // FIXME: the implementation of tagFromHandle is NOT canvas-compatible
923 art::InputTag tag = tagFromHandle(handle);
925 return findAssociations<Target>
926 (std::forward<Handle>(handle), event, tag);
927 } // findAssociations(Handle, same tag)
931 typename Target, typename PtrColl, typename Event,
932 typename = std::enable_if_t<!is_handle_v<PtrColl>>
934 auto findAssociations
935 (PtrColl const& coll, Event const& event, lar::SameAsDataTag)
939 return AssociationFinder<Target>::findWithRange
940 (cbegin(coll), cend(coll), event);
944 /// Helper finding a single degree of associations from a specified tag.
946 typename Target, typename PtrColl, typename Event,
947 typename = std::enable_if_t<!is_handle_v<PtrColl>>
949 auto findAssociations
950 (PtrColl const& coll, Event const& event, art::InputTag const& tag)
954 return AssociationFinder<Target>::findWithRange
955 (cbegin(coll), cend(coll), event, tag);
956 } // findAssociations(Handle, tag)
959 //--------------------------------------------------------------------------
962 * @tparam Target type to be associated to the source data
963 * @tparam Tier tier: 0 -> get target, 1 -> get leftmost intermediate, ...
964 * @tparam Intermediate intermediate types, leftmost is closest to `Target`
966 template <typename Target, unsigned int Tier, typename... Intermediate>
967 struct FindManyInChainPimpl {
969 /// Total number of tiers (original source + all intermediates).
970 static constexpr unsigned int Tiers = sizeof...(Intermediate) + 1;
973 (Tier < Tiers, "Invalid tier: there is no type in position #N");
975 /// Type used for the association to source.
976 using Intermediate_t = get_type_t<(Tier - 1), Intermediate...>;
978 template <typename Source, typename Event, typename InputTags>
980 (Source&& source, Event const& event, InputTags const& tags)
982 static_assert(std::tuple_size<InputTags>() == Tiers,
983 "Wrong number of input tags specified");
985 // number of the intermediate being addressed
986 constexpr auto nTag = Tiers - 1 - Tier;
988 // find the associations between source and the next tier:
989 // Source <==> Intermediate;
990 // result is a ConnectionList object, that we store in a "manager"
991 auto iq = makeConnectionManager(
992 findAssociations<Intermediate_t>
993 (source, event, std::get<nTag>(tags))
996 // collapse the result for input into the next tier;
997 // this will result in a long sequence of art pointers
998 auto const intermediateData = iq.allConnected();
1000 // process the next tier: Intermediate <==> Target;
1001 // this is also a ConnectionList
1003 = FindManyInChainPimpl<Target, (Tier - 1U), Intermediate...>::find
1004 (intermediateData, event, tags);
1006 // combine the result into jet another connection list:
1007 // Source <==> Intermediate (+) Intermediate <==> Target
1008 return iq.join(std::move(oq)); // AssociationFinderBase::combine(iq, oq);
1013 }; // FindManyInChainPimpl<>
1016 // Specialization for tier 0
1017 // (target association to first intermediate in list)
1018 template <typename Target, typename... Intermediate>
1019 struct FindManyInChainPimpl<Target, 0U, Intermediate...> {
1021 /// Total number of tiers (original source + all intermediates).
1022 static constexpr unsigned int Tiers = sizeof...(Intermediate) + 1;
1024 template <typename Source, typename Event, typename InputTags>
1026 (Source&& source, Event const& event, InputTags const& tags)
1028 static_assert(std::tuple_size<InputTags>() == Tiers,
1029 "Wrong number of input tags specified");
1030 // find the associations between the last intermediate and the target
1031 // (using the last tag)
1032 return findAssociations<Target>
1033 (source, event, std::get<Tiers - 1>(tags));
1036 }; // FindManyInChainPimpl
1040 //--------------------------------------------------------------------------
1042 } // namespace details
1046 //------------------------------------------------------------------------------
1048 // Implementation note:
1049 // - FindManyInChainP is the front-end utility with minimal template arguments;
1050 // it sets up and runs FindManyInChainPimpl...
1051 // - FindManyInChainPimpl is the implementation of recursion where associations
1052 // are extracted for the results of the previous level ("tier") of
1053 // associated objects; it juggles with tags and parameters, and relies on
1054 // findAssociations()...
1055 // - findAssociations() implements the extraction of a single association tier,
1056 // from a (flattened) collection of input objects
1058 //------------------------------------------------------------------------------
1059 template <typename Target, typename... Intermediate>
1060 template <typename Source, typename Event, typename... InputTags>
1061 auto lar::FindManyInChainP<Target, Intermediate...>::find
1062 (Source&& source, Event const& event, InputTags... tags)
1063 -> std::vector<TargetPtrCollection_t>
1065 constexpr auto Tiers = sizeof...(Intermediate) + 1U;
1067 // create a parameter pack with one tag per association
1069 = details::AssociationFinderBase::makeTagsTuple<Tiers>
1070 (SameAsData, std::forward<InputTags>(tags)...);
1072 return details::FindManyInChainPimpl<Target, (Tiers - 1), Intermediate...>
1073 ::find(source, event, allTags).values();
1075 } // lar::FindManyInChainP<Target, Intermediate...>::FindManyInChainP()
1078 //------------------------------------------------------------------------------
1079 template <typename Target, typename... Intermediate>
1081 lar::FindManyInChainP<Target, Intermediate...>::size() const noexcept
1082 { return results.size(); }
1085 //------------------------------------------------------------------------------
1086 template <typename Target, typename... Intermediate>
1087 typename lar::FindManyInChainP<Target, Intermediate...>::TargetPtrCollection_t const&
1088 lar::FindManyInChainP<Target, Intermediate...>::at(std::size_t i) const
1089 { return results.at(i); }
1092 //------------------------------------------------------------------------------
1095 #endif // LARDATA_UTILITIES_FINDMANYINCHAINP_TCC