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 "canvas/Persistency/Common/Assns.h"
19 #include "canvas/Persistency/Common/FindManyP.h"
20 #include "canvas/Persistency/Provenance/BranchDescription.h"
21 #include "canvas/Utilities/Exception.h"
23 // C/C++ standard library
24 #include <algorithm> // std::lower_bound(), std::sort(), std::transform()...
25 #include <iterator> // std::begin(), std::cbegin(), std::distance()...
27 #include <tuple> // std::tuple_cat(), ...
28 #include <type_traits> // std::decay_t<>, std::enable_if_t<>, ...
29 #include <utility> // std::pair<>, std::move(), std::declval()...
31 //------------------------------------------------------------------------------
32 //--- template implementation
38 //--------------------------------------------------------------------------
39 // this is a copy of things in cetlib; should be replaced by the original
40 // if they get exposed
41 template <class T, class R /* = void */>
42 struct enable_if_type_exists {
46 //--------------------------------------------------------------------------
47 /// Trait evaluating to true if H is a art Handle type.
48 template <typename H, typename = void>
49 struct is_handle : public std::false_type {};
52 struct is_handle<H, enable_if_is_handle_t<H>> : public std::true_type {};
55 constexpr bool is_handle_v = is_handle<H>::value;
57 //--------------------------------------------------------------------------
59 struct is_art_ptr_impl : public std::false_type {};
62 struct is_art_ptr_impl<art::Ptr<T>> : public std::true_type {};
64 /// Whether the specified type is an art::Ptr.
66 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;
79 template <unsigned int N, typename First, typename... Others>
80 struct get_type<N, First, Others...> {
81 static_assert(N <= sizeof...(Others), "No type in the specified position.");
82 using type = get_type_t<N - 1, Others...>;
85 template <typename First, typename... Others>
86 struct get_type<0U, First, Others...> {
90 //--------------------------------------------------------------------------
92 template <unsigned int NCopies>
93 struct TupleAppender {
94 template <typename Tuple, typename T>
95 static auto append(Tuple&& t, T const& value)
97 return TupleAppender<(NCopies - 1)>::append(
98 std::tuple_cat(std::forward<Tuple>(t), std::make_tuple(value)), value);
100 }; // TupleAppender<>
103 struct TupleAppender<0> {
104 template <typename Tuple, typename T>
105 static decltype(auto) append(Tuple&& t, T const&)
107 return std::forward<Tuple>(t);
109 }; // TupleAppender<0>
111 template <unsigned int NCopies, typename T, typename Tuple>
112 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)
122 return handle.provenance()->inputTag();
125 /// Returns the input tag of the product identified by `id`.
126 template <typename Data, typename Event>
127 art::InputTag tagFromProductID(art::ProductID const& id, Event const& event)
129 // This is not efficient for repeated queries--perhaps a map can
130 // be created that maps ProductID to input tag.
131 auto pd = event.getProductDescription(id);
132 if (pd != nullptr) { return pd->inputTag(); }
133 throw art::Exception(art::errors::ProductNotFound)
134 << "Couldn't find data product with product ID " << id << "\n";
135 } // tagFromProductID()
137 //--------------------------------------------------------------------------
138 template <typename Value>
139 class SimpleDataIndex {
141 using Value_t = Value;
143 /// Constructor: indexes pointers to data in the specified collection.
144 template <typename BeginIter, typename EndIter>
145 SimpleDataIndex(BeginIter begin, EndIter end)
150 /// Constructor: indexes pointers to data in the specified collection.
151 template <typename Coll>
152 SimpleDataIndex(Coll& data)
156 init(begin(data), end(data));
159 /// Returns a pointer to the matched data, nullptr if none.
160 std::size_t const* operator()(Value_t const& key) const
162 auto const e = index.cend();
163 auto const it = std::lower_bound(index.cbegin(), e, key, comparer());
164 return ((it == e) || (key < it->first)) ? nullptr : &(it->second);
168 using IndexElement_t = std::pair<Value_t const*, std::size_t>;
170 std::vector<IndexElement_t> index; ///< The index.
172 template <typename BeginIter, typename EndIter>
173 void init(BeginIter begin, EndIter end)
175 index.reserve(std::distance(begin, end)); // hope it's fast
177 for (auto it = begin; it != end; ++it, ++i)
178 index.emplace_back(&*it, i);
179 std::sort(index.begin(), index.end(), comparer());
183 bool operator()(Value_t const& a, Value_t const& b) const { return a < b; }
184 bool operator()(IndexElement_t const& a, Value_t const& b) const { return *(a.first) < b; }
185 bool operator()(Value_t const& a, IndexElement_t const& b) const { return a < *(b.first); }
186 bool operator()(IndexElement_t const& a, IndexElement_t const& b) const
188 return *(a.first) < *(b.first);
190 }; // struct KeyComparer
192 static auto comparer() { return KeyComparer(); }
194 }; // class SimpleDataIndex
196 template <typename Key, typename Value>
200 using Value_t = Value;
201 using ValuePtr_t = std::add_pointer_t<Value_t>;
203 /// Constructor: indexes pointers to data in the specified collection.
204 template <typename BeginIter, typename EndIter, typename KeyExtractor>
205 DataIndex(BeginIter begin, EndIter end, KeyExtractor&& getKey)
207 init(begin, end, std::forward<KeyExtractor>(getKey));
210 /// Constructor: indexes pointers to data in the specified collection.
211 template <typename Coll, typename KeyExtractor>
212 DataIndex(Coll& data, KeyExtractor&& getKey)
216 init(begin(data), end(data), std::forward<KeyExtractor>(getKey));
219 /// Returns a pointer to the matched data, nullptr if none.
220 ValuePtr_t operator()(Key_t const& key) const
222 auto const e = index.cend();
223 auto const it = std::lower_bound(index.cbegin(), e, key, comparer());
224 return ((it == e) || (key < it->first)) ? nullptr : it->second;
228 using IndexElement_t = std::pair<Key_t, Value_t*>;
230 std::vector<IndexElement_t> index; ///< The index.
232 template <typename BeginIter, typename EndIter, typename KeyExtractor>
233 void init(BeginIter begin, EndIter end, KeyExtractor&& getKey)
235 index.reserve(std::distance(begin, end)); // hope it's fast
236 for (auto it = begin; it != end; ++it)
237 index.emplace_back(getKey(*it), &*it);
238 std::sort(index.begin(), index.end(), comparer());
242 bool operator()(Key_t const& a, Key_t const& b) const { return a < b; }
243 bool operator()(IndexElement_t const& a, Key_t const& b) const { return a.first < b; }
244 bool operator()(Key_t const& a, IndexElement_t const& b) const { return a < b.first; }
245 bool operator()(IndexElement_t const& a, IndexElement_t const& b) const
247 return a.first < b.first;
249 }; // struct KeyComparer
251 static auto comparer() { return KeyComparer(); }
253 }; // class DataIndex
255 /// Returns a new index for data using the keys extracted by getKey.
256 template <typename Coll, typename KeyExtractor>
257 auto makeIndex(Coll& data, KeyExtractor&& getKey)
259 using Value_t = typename Coll::value_type;
260 using Key_t = std::decay_t<decltype(getKey(std::declval<Value_t>()))>;
261 return DataIndex<Key_t, Value_t>(data, std::forward<KeyExtractor>(getKey));
264 /// Returns a new index for data in range using the data itself as key.
265 template <typename BeginIter, typename EndIter>
266 auto makeSimpleIndex(BeginIter begin, EndIter end)
268 using Value_t = std::decay_t<decltype(*begin)>;
269 return SimpleDataIndex<Value_t>(begin, end);
272 /// Returns a new index for data using the data itself as key.
273 template <typename Coll>
274 auto makeSimpleIndex(Coll& data)
276 using Value_t = typename Coll::value_type;
277 return SimpleDataIndex<Value_t>(data);
280 //--------------------------------------------------------------------------
281 //--- finders implementation
284 namespace AssociationFinderBase {
286 /// Information on a single source: its index in the source list, and
287 /// "key" (it can be any sortable object).
288 template <typename Key = std::size_t>
289 struct SourceIDinfo_t : public std::tuple<Key, std::size_t> {
290 using Base_t = std::tuple<Key, std::size_t>;
293 /// Returns the key of the art pointer to this source element.
294 Key_t const& key() const { return std::get<0>(*this); }
295 /// Returns the index of this source element within the source list.
296 std::size_t position() const { return std::get<1>(*this); }
298 using Base_t::Base_t; // inherit constructor
300 /// Comparison functor: compares art pointer keys.
302 bool operator()(SourceIDinfo_t const& a, SourceIDinfo_t const& b) const
304 return a.key() < b.key();
306 bool operator()(SourceIDinfo_t const& a, Key_t const& b) const { return a.key() < b; }
307 bool operator()(Key_t const& a, SourceIDinfo_t const& b) const { return a < b.key(); }
308 }; // struct SourceIDinfo_t::Comparer_t
310 static Comparer_t comparer() { return {}; }
311 }; // struct SourceIDinfo_t
313 template <typename Key = std::size_t>
314 struct SourceVector_t : private std::vector<SourceIDinfo_t<Key>> {
315 using Base_t = std::vector<SourceIDinfo_t<Key>>;
316 using Info_t = typename Base_t::value_type;
317 using Key_t = typename Info_t::Key_t;
319 using const_iterator = typename Base_t::const_iterator;
324 using Base_t::reserve;
327 struct MatchConstIterator_t : public std::pair<const_iterator, bool> {
328 using Base_t = std::pair<const_iterator, bool>;
329 using Base_t::Base_t; // inherit all constructors
330 auto const& operator->() const { return std::get<0>(*this); }
331 operator bool() const { return std::get<1>(*this); }
332 bool operator!() const { return !this->operator bool(); }
333 }; // struct MatchConstIterator_t
335 /// Default constructor: starts empty.
336 SourceVector_t() = default;
338 /// Constructor: from the elements between the specified iterators.
339 template <typename BeginIter, typename EndIter>
340 SourceVector_t(BeginIter b, EndIter e)
345 /// Sorts the vector according to keys.
346 void sort() { std::sort(Base_t::begin(), Base_t::end(), Info_t::comparer()); }
348 /// Constructs a new element at the end of the vector.
349 void emplace_back(Key_t const& key, std::size_t pos) { Base_t::emplace_back(key, pos); }
351 /// Returns iterator to element with key, if it exists.
352 /// Requires the vector to be sort()ed.
353 /// If key is not present it may return end() or the wrong element.
354 auto find(Key_t const& key) const
356 auto const e = end();
357 auto it = std::lower_bound(begin(), e, key, Info_t::comparer());
358 return MatchConstIterator_t(it, (it != e) && (it->key() == key));
361 /// Resets the content to map a container delimited by the iterators.
362 template <typename BeginIter, typename EndIter>
363 void assign(BeginIter b, EndIter e)
367 for (auto it = b; it != e; ++it, ++i)
368 emplace_back(it->key(), i);
372 }; // struct SourceVector_t
374 /// Returns a tuple of NTags elements, including all specified tags and
375 /// then copies of the default one to make it N.
376 template <unsigned int NTags, typename DefaultTag, typename... InputTags>
377 auto makeTagsTuple(DefaultTag defTag, InputTags... tags)
379 return appendToTuple<(NTags - sizeof...(InputTags))>(std::make_tuple(tags...), defTag);
382 /// A named pair of a key and all its connections.
383 /// Comparisons are based solely on key.
384 template <typename A, typename B>
385 struct Connections : private std::pair<A, std::vector<B>> {
386 using Base_t = std::pair<A, std::vector<B>>;
388 using ConnectedItem_t = B;
389 using Connections_t = Connections<A, B>;
390 using Connected_t = std::tuple_element_t<1, Base_t>;
391 using Base_t::Base_t; // import pair constructors
393 /// Additional constructor: key with no connected items.
394 Connections(Key_t const& key) : Base_t(key, {}) {}
396 Key_t const& key() const { return std::get<0>(*this); }
397 Connected_t const& connectedTo() const { return std::get<1>(*this); }
398 Connected_t& connectedTo() { return std::get<1>(*this); }
401 bool operator()(Key_t const& a, Key_t const& b) const { return a < b; }
402 bool operator()(Key_t const& a, Connections_t const& b) const { return a < b.key(); }
403 bool operator()(Connections_t const& a, Key_t const& b) const { return a.key() < b; }
404 bool operator()(Connections_t const& a, Connections_t const& b) const
406 return a.key() < b.key();
410 static Comparer_t comparer() { return {}; }
412 /// Compares the connection lists by key only.
413 bool operator<(Connections_t const& b) const { return comparer()(*this, b); }
415 }; // struct Connections<>
417 struct KeysFromTag {};
418 struct KeysFromIndexTag {};
419 struct KeysFromOtherListTag {};
420 constexpr KeysFromTag KeysFrom;
421 constexpr KeysFromIndexTag KeysFromIndex;
422 constexpr KeysFromOtherListTag KeysFromOtherList;
424 /// A connections list.
425 template <typename Key, typename Connected>
426 class ConnectionList : private std::vector<Connections<Key, Connected>> {
427 using Connections_t = Connections<Key, Connected>;
428 using Data_t = std::vector<Connections_t>;
430 Data_t& data() { return *this; }
431 Data_t const& data() const { return *this; }
434 /// Type of an item connected to the key.
435 using ConnectedItem_t = typename Connections_t::ConnectedItem_t;
436 /// Type of list of connected items.
437 using Connected_t = typename Connections_t::Connected_t;
439 // import types from the base class
440 using value_type = typename Data_t::value_type;
442 /// Initializes an empty connection list.
443 ConnectionList() = default;
445 /// Initializes with no connection for each of the keys from b to e.
446 template <typename BeginIter, typename EndIter>
447 ConnectionList(KeysFromTag, BeginIter b, EndIter e)
449 data().reserve(std::distance(b, e)); // hope this is fast
450 std::transform(b, e, std::back_inserter(data()), [](auto const& key) {
451 return Connections_t(key, {});
455 /// Initializes with no connection for each of the keys from a list.
456 template <typename Other>
457 ConnectionList(KeysFromOtherListTag, ConnectionList<Key, Other> const& source)
459 data().reserve(source.size()); // hope this is fast
462 std::transform(cbegin(source),
464 std::back_inserter(data()),
465 [](auto const& conn) { return Connections_t(conn.key(), {}); });
468 /// Initializes with no connection for each of the generated keys.
469 template <typename KeyMaker>
470 ConnectionList(KeysFromIndexTag, std::size_t n, KeyMaker keyMaker)
473 for (std::size_t i = 0; i < n; ++i)
474 data().emplace_back(keyMaker(i));
477 // import methods from the base class (not the constructors)
479 using Data_t::operator[];
481 using Data_t::cbegin;
487 /// @name Special methods
489 void addConnectionAt(std::size_t index, ConnectedItem_t const& item)
491 data()[index].connectedTo().push_back(item);
493 void addConnectionAt(std::size_t index, ConnectedItem_t&& item)
495 data()[index].connectedTo().push_back(std::move(item));
498 /// Returns all connected objects on a vector, one element per key.
499 /// As a R-value reference method, the current content is lost
500 std::vector<Connected_t> values() &&
502 std::vector<Connected_t> result;
503 result.reserve(data().size());
504 std::transform(data().begin(), data().end(), std::back_inserter(result), [](auto& conn) {
505 return std::move(conn.connectedTo());
512 }; // struct ConnectionList<>
514 /// Class handling a connection list.
515 template <typename Source, typename Target>
516 class PtrConnectionManager {
519 using Manager_t = PtrConnectionManager<Source, Target>;
520 using Source_t = Source;
521 using Target_t = Target;
522 using SourcePtr_t = art::Ptr<Source_t>;
523 using TargetPtr_t = art::Ptr<Target_t>;
524 using ConnectionList_t = ConnectionList<SourcePtr_t, TargetPtr_t>;
526 /// Constructor: steals the connections from the argument.
527 PtrConnectionManager(ConnectionList_t&& data) : data(std::move(data)) {}
529 /// Returns all connected objects as a single connection of pointers.
530 std::vector<TargetPtr_t> allConnected() const
533 // this might as well be implemented with ranges
535 std::vector<TargetPtr_t> result;
540 for (std::size_t i = 0; i < data.size(); ++i) {
541 auto const& connected = data[i].connectedTo();
542 result.insert(result.end(), cbegin(connected), cend(connected));
548 /// Returns a new connection list connecting each of our sources to
549 /// FurtherPtr_t objects, using shared TargetPtr_t to map connections.
550 template <typename FurtherPtr_t>
551 ConnectionList<SourcePtr_t, FurtherPtr_t> join(
552 ConnectionList<TargetPtr_t, FurtherPtr_t>&& other) const
554 // internally, we'll call the current data A -> B, other data B -> C,
555 // and we are after A -> C
556 using APtr_t = SourcePtr_t;
557 // using BPtr_t = TargetPtr_t;
558 using CPtr_t = FurtherPtr_t;
560 auto const& AB = data;
562 ConnectionList<APtr_t, CPtr_t> AC(KeysFromOtherList, data);
564 // the match object returns the iterator to the list connected to the
565 // specified key; it does not own the data structure;
566 auto const match = makeIndex(BC, [](auto const& elem) { return elem.key(); });
568 for (std::size_t iA = 0; iA < AB.size(); iA++) {
569 auto const& connAB = AB[iA]; // connections for this A
570 auto const& BsForA = connAB.connectedTo();
572 auto& CsForA = AC[iA].connectedTo(); // connected list to fill (empty)
574 // for each B connected to this A:
575 for (auto const& key : BsForA) {
577 auto iConnBC = match(key);
578 if (!iConnBC) continue; // no C's associated to the matching B
580 // matched! move or copy the while list of Cs
581 auto& CsForB = iConnBC->connectedTo(); // (this list)
583 CsForA = std::move(CsForB); // move
585 CsForA.insert(CsForA.end(), cbegin(CsForB), cend(CsForB));
587 } // for connected Bs
594 ConnectionList_t data; ///< All connection information.
596 }; // PtrConnectionManager<>
598 /// Helper function to created the right type of manager from a temporary.
599 template <typename A, typename B>
600 auto makeConnectionManager(ConnectionList<A, B>&& data)
602 return PtrConnectionManager<typename A::value_type, typename B::value_type>(
606 } // namespace AssociationFinderBase
608 template <typename Target>
609 struct AssociationFinder {
611 using Target_t = Target;
612 using TargetPtr_t = art::Ptr<Target_t>;
614 /// Type of result of finders: one list of associated targets per source.
615 template <typename Source>
616 using Result_t = AssociationFinderBase::ConnectionList<art::Ptr<Source>, TargetPtr_t>;
618 template <typename Source>
619 using Assns_t = art::Assns<Source, Target>;
621 template <typename Assns>
622 using AssnKey_t = typename Assns::left_t;
624 template <typename Assns>
625 using AssnKeyPtr_t = art::Ptr<AssnKey_t<Assns>>;
627 template <typename Assns>
628 using ResultFromAssns_t = Result_t<AssnKey_t<Assns>>;
630 template <typename Handle>
631 using ValueFromHandle_t = typename Handle::element_type::value_type;
633 template <typename Handle>
634 using AssnsFromHandle_t = Assns_t<ValueFromHandle_t<Handle>>;
636 template <typename Handle>
637 using ResultFromHandle_t = Result_t<art::Ptr<ValueFromHandle_t<Handle>>>;
639 /// Helper finding a associations from a single complete data product.
640 template <typename Assns>
641 static ResultFromAssns_t<Assns> findFromDataProduct(art::ProductID const& sourceID,
642 std::size_t nSources,
645 using SourcePtr_t = AssnKeyPtr_t<Assns>;
647 using namespace AssociationFinderBase;
649 // as many lists in result as sources, keys created from source ID
650 ResultFromAssns_t<Assns> result(KeysFromIndex, nSources, [id = sourceID](std::size_t i) {
651 return SourcePtr_t(id, i, nullptr);
654 // get the associations, following the content of the assns data product
655 for (decltype(auto) assn : assns) {
656 auto const& sourcePtr = assn.first;
658 // does this association contain a pointer with an ID different than
659 // the one we are looking for? (it may mean our assumption that the
660 // same module produced source and its association is wrong)
661 if (sourcePtr.id() != sourceID) continue;
663 // we follow the assumption that the data product is complete with
664 // nSources elements, therefore no pointer can exist with a larger
666 assert(sourcePtr.key() < nSources);
668 // push target pointer into the result of the matched source
669 result.addConnectionAt(sourcePtr.key(), assn.second);
674 } // findFromDataProduct()
676 /// Helper finding a associations from a single list.
677 template <typename Source, typename IndexBegin, typename IndexEnd, typename Event>
678 static Result_t<Source> findSingle(art::ProductID const& sourceID,
681 Assns_t<Source> const& assns)
683 // type of the source object (hidden in PtrColl)
684 using Source_t = Source;
685 using SourcePtr_t = art::Ptr<Source_t>;
686 using namespace AssociationFinderBase;
688 std::size_t const nSources = std::distance(sbegin, send);
689 Result_t<Source_t> result(nSources);
691 // These are the source "pointers" we still have to find; they are all
692 // on the same product ID. We keep track of the original position too
693 // (unordered map may or may not be better...);
694 // given that these are all from the same product ID, the product ID is
695 // not saved. SourceVector_t provides log(N) lookup.
696 SourceVector_t<std::size_t> sourceInfos(sbegin, send);
698 // get the association, following the content of the assns data product
699 for (decltype(auto) assn : assns) {
700 SourcePtr_t const& sourcePtr = assn.first;
702 // does this association contain a pointer with an ID different than
703 // the one we are looking for? (it may mean our assumption that the
704 // same module produced source and its association is wrong)
705 if (sourcePtr.id() != sourceID) continue;
707 // is this pointer interesting?
708 auto iSrcInfo = sourceInfos.find(sourcePtr.key());
709 if (!iSrcInfo) continue; // it's not it
711 // match! push target pointer into the result of the matched source
712 result[iSrcInfo->position()].connectedTo().push_back(assn.second);
719 /// Helper finding a single degree of associations from the tag of each
720 /// source art pointer.
721 template <typename PtrCollBegin, typename PtrCollEnd, typename Event>
722 static auto findWithRange(PtrCollBegin sbegin,
725 art::InputTag const& tag)
728 * The strategy of this implementation is:
730 * 1. collect all source art pointers, sorted by key for faster lookup
731 * 2. parse all the associated pairs
732 * 1. if the source pointer of a pair is in the list of
733 * interesting source pointers, push the target pointer of the
734 * pair into the results for this source
738 using SourcePtr_t = std::decay_t<decltype(*sbegin)>;
739 static_assert(is_art_ptr<SourcePtr_t>(),
740 "Collection for AssociationFinder (FindManyInChainP) is not art pointers!");
742 // type of the source object (hidden in PtrColl)
743 using Source_t = typename SourcePtr_t::value_type;
744 using namespace AssociationFinderBase;
746 Result_t<Source_t> result(KeysFrom, sbegin, send);
747 // std::size_t const nSources = result.size();
749 // use this index for fast lookup of the sources
750 auto match = makeSimpleIndex(sbegin, send);
752 // fetch the association data product
753 auto const& assns = *(event.template getValidHandle<Assns_t<Source_t>>(tag));
755 // get the association, following the content of the assns data product
756 for (decltype(auto) assn : assns) {
757 SourcePtr_t const& sourcePtr = assn.first;
759 // is this pointer interesting?
760 auto iPos = match(sourcePtr);
761 if (!iPos) continue; // it's not it
763 // match! push target pointer into the result of the matched source
764 result[*iPos].connectedTo().push_back(assn.second);
771 /// Helper finding a single degree of associations from the tag of each
772 /// source art pointer.
773 template <typename PtrCollBegin, typename PtrCollEnd, typename Event>
774 static auto findWithRange(PtrCollBegin sbegin, PtrCollEnd send, Event const& event)
777 * The strategy of this implementation is:
779 * 1. collect all the source art pointers, grouped by product ID
780 * (and sorted by key for faster lookup)
781 * 2. for each interesting product ID:
782 * 1. fetch the association collection; this is assumed to have been
783 * created with the same input tag as the source product
784 * 2. parse all the associated pairs
785 * 1. if the source pointer of a pair is in the list of
786 * interesting source pointers, push the target pointer of the
787 * pair into the results for this source
789 * The maximum complexity of this algorithm is N log(M), where M is no
790 * larger than the maximum number of source pointers with a single
791 * product ID and N is the number of associations in each association
796 using SourcePtr_t = std::decay_t<decltype(*sbegin)>;
797 static_assert(is_art_ptr<SourcePtr_t>(),
798 "Collection for AssociationFinder (FindManyInChainP) is not art pointers!");
800 // type of the source object (hidden in PtrColl)
801 using Source_t = typename SourcePtr_t::value_type;
802 using namespace AssociationFinderBase;
804 Result_t<Source_t> result(KeysFrom, sbegin, send);
806 // These are the source pointers we still have to find,
807 // organised by product ID; we keep track of the original position too
808 // (unordered map may or may not be better...);
809 // given that these are all from the same product ID, the product ID is
811 // Also, for fast lookup the lists are sorted by pointer key.
812 std::map<art::ProductID, SourceVector_t<std::size_t>> sourcesLeft;
813 std::size_t iSource = 0;
814 for (auto it = sbegin; it != send; ++it, ++iSource)
815 sourcesLeft[it->id()].emplace_back(it->key(), iSource);
816 for (auto& sourcesWithinID : sourcesLeft)
817 sourcesWithinID.second.sort();
819 // look for all sources in each product ID
820 for (auto const& sourcesWithinID : sourcesLeft) {
822 art::ProductID sourceID = sourcesWithinID.first;
823 auto const& sourceInfos = sourcesWithinID.second;
825 // need to get the association between source and target,
826 // as produced by the same producer that produced the source itself
827 art::InputTag tag = tagFromProductID<std::vector<Source_t>>(sourceID, event);
829 // fetch the association data product
830 auto const& assns = *(event.template getValidHandle<Assns_t<Source_t>>(tag));
832 // get the association, following the content of the assns data product
833 for (decltype(auto) assn : assns) {
834 SourcePtr_t const& sourcePtr = assn.first;
836 // does this association contain a pointer with an ID different than
837 // the one we are looking for? (it may mean our assumption that the
838 // same module produced source and its association is wrong)
839 if (sourcePtr.id() != sourceID) continue;
841 // is this pointer interesting?
842 auto iSrcInfo = sourceInfos.find(sourcePtr.key());
843 if (!iSrcInfo) continue; // it's not it
845 // match! push target pointer into the result of the matched source
846 result[iSrcInfo->position()].connectedTo().push_back(assn.second);
850 } // for sourcesWithinID
855 }; // class AssociationFinder
857 /// Helper finding a single degree of associations from a specified tag.
858 template <typename Target,
861 typename = std::enable_if_t<is_handle_v<Handle>>>
862 auto findAssociations(Handle&& handle, Event const& event, art::InputTag const& assnsTag)
864 using Finder = AssociationFinder<Target>;
865 using Assns_t = typename Finder::template AssnsFromHandle_t<std::decay_t<Handle>>;
867 auto const& assns = *(event.template getValidHandle<Assns_t>(assnsTag));
869 // FIXME: this implementation is NOT canvas-compatible (Handle::id())
870 return Finder::findFromDataProduct(handle.id(), handle->size(), assns);
871 } // findAssociations(Handle, tag)
873 /// Helper finding a single degree of associations from any tag.
874 template <typename Target,
877 typename = std::enable_if_t<is_handle_v<Handle>>>
878 auto findAssociations(Handle handle, Event const& event, lar::SameAsDataTag)
881 * By definition, here all the interesting sources share the same product
882 * ID, which simplifies the implementation.
885 // need to get the association between source and target,
886 // as produced by the same producer that produced the source itself
887 // FIXME: the implementation of tagFromHandle is NOT canvas-compatible
888 art::InputTag tag = tagFromHandle(handle);
890 return findAssociations<Target>(std::forward<Handle>(handle), event, tag);
891 } // findAssociations(Handle, same tag)
893 template <typename Target,
896 typename = std::enable_if_t<!is_handle_v<PtrColl>>>
897 auto findAssociations(PtrColl const& coll, Event const& event, lar::SameAsDataTag)
901 return AssociationFinder<Target>::findWithRange(cbegin(coll), cend(coll), event);
904 /// Helper finding a single degree of associations from a specified tag.
905 template <typename Target,
908 typename = std::enable_if_t<!is_handle_v<PtrColl>>>
909 auto findAssociations(PtrColl const& coll, Event const& event, art::InputTag const& tag)
913 return AssociationFinder<Target>::findWithRange(cbegin(coll), cend(coll), event, tag);
914 } // findAssociations(Handle, tag)
916 //--------------------------------------------------------------------------
919 * @tparam Target type to be associated to the source data
920 * @tparam Tier tier: 0 -> get target, 1 -> get leftmost intermediate, ...
921 * @tparam Intermediate intermediate types, leftmost is closest to `Target`
923 template <typename Target, unsigned int Tier, typename... Intermediate>
924 struct FindManyInChainPimpl {
926 /// Total number of tiers (original source + all intermediates).
927 static constexpr unsigned int Tiers = sizeof...(Intermediate) + 1;
929 static_assert(Tier < Tiers, "Invalid tier: there is no type in position #N");
931 /// Type used for the association to source.
932 using Intermediate_t = get_type_t<(Tier - 1), Intermediate...>;
934 template <typename Source, typename Event, typename InputTags>
935 static auto find(Source&& source, Event const& event, InputTags const& tags)
937 static_assert(std::tuple_size<InputTags>() == Tiers,
938 "Wrong number of input tags specified");
940 // number of the intermediate being addressed
941 constexpr auto nTag = Tiers - 1 - Tier;
943 // find the associations between source and the next tier:
944 // Source <==> Intermediate;
945 // result is a ConnectionList object, that we store in a "manager"
946 auto iq = makeConnectionManager(
947 findAssociations<Intermediate_t>(source, event, std::get<nTag>(tags)));
949 // collapse the result for input into the next tier;
950 // this will result in a long sequence of art pointers
951 auto const intermediateData = iq.allConnected();
953 // process the next tier: Intermediate <==> Target;
954 // this is also a ConnectionList
955 auto oq = FindManyInChainPimpl<Target, (Tier - 1U), Intermediate...>::find(
956 intermediateData, event, tags);
958 // combine the result into jet another connection list:
959 // Source <==> Intermediate (+) Intermediate <==> Target
960 return iq.join(std::move(oq)); // AssociationFinderBase::combine(iq, oq);
964 }; // FindManyInChainPimpl<>
966 // Specialization for tier 0
967 // (target association to first intermediate in list)
968 template <typename Target, typename... Intermediate>
969 struct FindManyInChainPimpl<Target, 0U, Intermediate...> {
971 /// Total number of tiers (original source + all intermediates).
972 static constexpr unsigned int Tiers = sizeof...(Intermediate) + 1;
974 template <typename Source, typename Event, typename InputTags>
975 static auto find(Source&& source, Event const& event, InputTags const& tags)
977 static_assert(std::tuple_size<InputTags>() == Tiers,
978 "Wrong number of input tags specified");
979 // find the associations between the last intermediate and the target
980 // (using the last tag)
981 return findAssociations<Target>(source, event, std::get<Tiers - 1>(tags));
984 }; // FindManyInChainPimpl
986 //--------------------------------------------------------------------------
988 } // namespace details
992 //------------------------------------------------------------------------------
994 // Implementation note:
995 // - FindManyInChainP is the front-end utility with minimal template arguments;
996 // it sets up and runs FindManyInChainPimpl...
997 // - FindManyInChainPimpl is the implementation of recursion where associations
998 // are extracted for the results of the previous level ("tier") of
999 // associated objects; it juggles with tags and parameters, and relies on
1000 // findAssociations()...
1001 // - findAssociations() implements the extraction of a single association tier,
1002 // from a (flattened) collection of input objects
1004 //------------------------------------------------------------------------------
1005 template <typename Target, typename... Intermediate>
1006 template <typename Source, typename Event, typename... InputTags>
1007 auto lar::FindManyInChainP<Target, Intermediate...>::find(Source&& source,
1010 -> std::vector<TargetPtrCollection_t>
1012 constexpr auto Tiers = sizeof...(Intermediate) + 1U;
1014 // create a parameter pack with one tag per association
1015 auto const allTags = details::AssociationFinderBase::makeTagsTuple<Tiers>(
1016 SameAsData, std::forward<InputTags>(tags)...);
1018 return details::FindManyInChainPimpl<Target, (Tiers - 1), Intermediate...>::find(
1019 source, event, allTags)
1022 } // lar::FindManyInChainP<Target, Intermediate...>::FindManyInChainP()
1024 //------------------------------------------------------------------------------
1025 template <typename Target, typename... Intermediate>
1026 std::size_t lar::FindManyInChainP<Target, Intermediate...>::size() const noexcept
1028 return results.size();
1031 //------------------------------------------------------------------------------
1032 template <typename Target, typename... Intermediate>
1033 typename lar::FindManyInChainP<Target, Intermediate...>::TargetPtrCollection_t const&
1034 lar::FindManyInChainP<Target, Intermediate...>::at(std::size_t i) const
1036 return results.at(i);
1039 //------------------------------------------------------------------------------
1041 #endif // LARDATA_UTILITIES_FINDMANYINCHAINP_TCC