Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-05-27 07:24:14

0001 // This file is part of the ACTS project.
0002 //
0003 // Copyright (C) 2016 CERN for the benefit of the ACTS project
0004 //
0005 // This Source Code Form is subject to the terms of the Mozilla Public
0006 // License, v. 2.0. If a copy of the MPL was not distributed with this
0007 // file, You can obtain one at https://mozilla.org/MPL/2.0/.
0008 
0009 #pragma once
0010 
0011 // Project include(s)
0012 #include "detray/definitions/indexing.hpp"
0013 #include "detray/definitions/math.hpp"
0014 
0015 // System include(s)
0016 #include <iterator>
0017 #include <sstream>
0018 #include <type_traits>
0019 
0020 namespace detray {
0021 
0022 /// Default hash function makes use of default node in the hash tree.
0023 template <typename value_t>
0024 struct default_hash {
0025   using hash_type = std::size_t;
0026   using data_hasher = std::hash<value_t>;
0027   using node_hasher = std::hash<hash_type>;
0028 
0029   auto operator()(const value_t &v) { return data_hasher{}(v); }
0030 
0031   auto operator()(const hash_type &left, const hash_type &right) {
0032     return node_hasher{}(left) + node_hasher{}(right);
0033   }
0034 };
0035 
0036 /// @brief Builds a hash tree from the data of an input collection.
0037 ///
0038 /// This class provides a graph algorithm that walk along the volumes of a given
0039 /// geometry and uses the portals to check reachability between the volumes.
0040 ///
0041 /// @tparam hash_function the type hashing that can be called on the collection
0042 ///                       data.
0043 /// @tparam input collection the type of data collection to be hased
0044 /// @tparam node_type how carries the hashes and links
0045 template <typename input_collection_t,
0046           typename data_t = typename input_collection_t::value_type,
0047           typename hash_function_t = default_hash<data_t>,
0048           typename hash_t =
0049               decltype(std::declval<hash_function_t>()(data_t{0})),
0050           template <typename> class vector_t = dvector>
0051   requires std::is_invocable_v<hash_function_t, data_t> &&
0052            std::is_invocable_v<hash_function_t, hash_t>
0053 class hash_tree {
0054  public:
0055   using hash_function = hash_function_t;
0056   using hash_type = hash_t;
0057 
0058   /// Default node in the hash tree.
0059   struct hashed_node {
0060     explicit hashed_node(hash_t hash) : _key(hash) {}
0061 
0062     hash_t _key;
0063     dindex _parent{detail::invalid_value<dindex>()};
0064     dindex _left_child{detail::invalid_value<dindex>()};
0065     dindex _right_child{detail::invalid_value<dindex>()};
0066 
0067     const hash_t &key() const { return _key; }
0068     hash_t &key() { return _key; }
0069 
0070     void set_parent(dindex pi) { _parent = pi; }
0071 
0072     void set_children(dindex lc, dindex rc) {
0073       _left_child = lc;
0074       _right_child = rc;
0075     }
0076   };
0077 
0078   /// No empty tree
0079   hash_tree() = delete;
0080 
0081   /// Build from existing nodes and edges, which are provide by the geometry.
0082   ///
0083   /// @param volumes geometry volumes that become the graph nodes
0084   /// @param portals geometry portals link volumes and become edges
0085   explicit hash_tree(const input_collection_t &data,
0086                      const hash_function_t & /*hf*/ = {}) {
0087     build(data);
0088   }
0089 
0090   /// Default destructor
0091   ~hash_tree() = default;
0092 
0093   /// @return the root hash of the tree, which is always the last node in the
0094   ///         node storage by way of construction. It is the fingerprint of the
0095   ///         input data.
0096   auto root() { return _tree.back().key(); }
0097 
0098   /// @returns the hash tree as a string
0099   inline std::string to_string() const {
0100     std::stringstream ss;
0101     for (const auto &n : _tree) {
0102       ss << n.key() << std::endl;
0103     }
0104     return ss.str();
0105   };
0106 
0107   /// @returns the tree data structure
0108   const vector_t<hashed_node> &tree() const { return _tree; }
0109 
0110  private:
0111   /// Go through the the input data and recursively build the tree.
0112   void build(const input_collection_t &input_data) noexcept(false) {
0113     // Build leaves from input data type
0114     for (const auto &data : input_data) {
0115       _tree.emplace_back(_hash(data));
0116     }
0117     // Need an even number of leaves to build the tree correctly
0118     if (input_data.size() % 2u != 0u) {
0119       _tree.emplace_back(_hash(0));
0120     }
0121     // Size of the tree is already known (all iterators stay valid in
0122     // recursion)
0123     // we might need to add one dummy node per level
0124     auto n_levels = static_cast<dindex>(math::log(input_data.size()));
0125     _tree.reserve(2u * _tree.size() + n_levels);
0126     // Build next level
0127     build(_tree.begin(), static_cast<dindex>(_tree.size()));
0128   }
0129 
0130   /// Build the hash tree recursively.
0131   ///
0132   /// @param first_child the beginning of the nodes for which to construct
0133   ///                    the parents in this iteration.
0134   template <typename iterator_t>
0135   void build(iterator_t &&first_child, dindex n_prev_level) {
0136     // base case
0137     if (n_prev_level <= 1u) {
0138       return;
0139     }
0140 
0141     auto last_child = first_child + static_cast<std::ptrdiff_t>(n_prev_level);
0142 
0143     // Run over previous tree level to build the next level
0144     for (auto current_child = first_child; current_child != last_child;
0145          current_child += 2u) {
0146       auto parent_digest =
0147           _hash(current_child->key(), (current_child + 1u)->key());
0148       hashed_node parent = _tree.emplace_back(parent_digest);
0149 
0150       // Parent node index is at the back of the tree
0151       current_child->set_parent(static_cast<dindex>(_tree.size()) - 1u);
0152       (current_child + 1)->set_parent(static_cast<dindex>(_tree.size()) - 1u);
0153 
0154       // Set the indices as distances in the contiguous container
0155       auto left_child_idx = std::distance(current_child, _tree.begin());
0156       parent.set_children(static_cast<dindex>(left_child_idx),
0157                           static_cast<dindex>(left_child_idx) + 1u);
0158     }
0159     dindex n_level = n_prev_level / 2u;
0160     // Need dummy leaf node for next level?
0161     if (n_level % 2u != 0u && n_level > 1u) {
0162       _tree.emplace_back(0);
0163       n_level++;
0164     }
0165     // begin next time where we ended this time
0166     build(last_child, n_level);
0167   }
0168 
0169   /// How to encode the node data
0170   hash_function_t _hash{hash_function_t{}};
0171 
0172   /// Tree nodes
0173   vector_t<hashed_node> _tree = {};
0174 };
0175 
0176 }  // namespace detray