Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2025-09-16 08:13:01

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 /// This file implements the tools for a hough transform.
0010 
0011 #pragma once
0012 
0013 #include "Acts/Utilities/Grid.hpp"
0014 
0015 #include <array>
0016 #include <span>
0017 #include <unordered_set>
0018 
0019 namespace Acts::HoughTransformUtils {
0020 
0021 /// this type is responsible for encoding the parameters of our hough space
0022 using CoordType = double;
0023 
0024 // this type is used to encode hit counts.
0025 // Floating point to allow hit weights to be applied
0026 using YieldType = float;
0027 
0028 /// @brief this function represents a mapping of a coordinate point in detector space to a line in
0029 /// hough space. Given the value of the first hough coordinate, it shall return
0030 /// the corresponding second coordinate according to the line parametrisation.
0031 /// Should be implemented by the user.
0032 /// @tparam PointType: The class representing a point in detector space (can differ between implementations)
0033 template <class PointType>
0034 using LineParametrisation =
0035     std::function<CoordType(CoordType, const PointType&)>;
0036 
0037 /// @brief struct to define the ranges of the hough histogram.
0038 /// Used to move between parameter and bin index coordinates.
0039 /// Disconnected from the hough plane binning to be able to re-use
0040 /// a plane with a given binning for several parameter ranges
0041 struct HoughAxisRanges {
0042   CoordType xMin = 0.0f;  // minimum value of the first coordinate
0043   CoordType xMax = 0.0f;  // maximum value of the first coordinate
0044   CoordType yMin = 0.0f;  // minimum value of the second coordinate
0045   CoordType yMax = 0.0f;  // maximum value of the second coordinate
0046 };
0047 
0048 /// convenience functions to link bin indices to axis coordinates
0049 
0050 /// @brief find the bin index corresponding to a certain abscissa
0051 /// of the coordinate axis, based on the axis limits and binning.
0052 /// @param min: Start of axis range
0053 /// @param max: End of axis range
0054 /// @param nSteps: Number of bins in axis
0055 /// @param val: value to find the corresponding bin for
0056 /// @return the bin number.
0057 /// No special logic to prevent over-/underflow, checking these is
0058 /// left to the caller
0059 inline int binIndex(double min, double max, unsigned nSteps, double val) {
0060   return static_cast<int>((val - min) / (max - min) * nSteps);
0061 }
0062 // Returns the lower bound of the bin specified by step
0063 /// @param min: Start of axis range
0064 /// @param max: End of axis range
0065 /// @param nSteps: Number of bins in axis
0066 /// @param binIndex: The index of the bin
0067 /// @return the parameter value at the lower bin edge.
0068 /// No special logic to prevent over-/underflow, checking these is
0069 /// left to the caller
0070 inline double lowerBinEdge(double min, double max, unsigned nSteps,
0071                            std::size_t binIndex) {
0072   return min + (max - min) * binIndex / nSteps;
0073 }
0074 // Returns the lower bound of the bin specified by step
0075 /// @param min: Start of axis range
0076 /// @param max: End of axis range
0077 /// @param nSteps: Number of bins in axis
0078 /// @param binIndex: The index of the bin
0079 /// @return the parameter value at the bin center.
0080 /// No special logic to prevent over-/underflow, checking these is
0081 /// left to the caller
0082 inline double binCenter(double min, double max, unsigned nSteps,
0083                         std::size_t binIndex) {
0084   return min + (max - min) * 0.5 * (2 * binIndex + 1) / nSteps;
0085 }
0086 
0087 /// @brief data class for the information to store for each
0088 /// cell of the hough histogram.
0089 /// @tparam identifier_t: Type of the identifier to associate to the hits
0090 ///                       Should be sortable. Used to uniquely identify each
0091 ///                       hit and to eventually return the list of hits per cell
0092 template <class identifier_t>
0093 class HoughCell {
0094  public:
0095   /// @brief construct the cell as empty
0096   HoughCell() = default;
0097   /// @brief add an entry to this cell
0098   /// @param identifier: Identifier of the hit (used to distinguish hits from another)
0099   /// @param layer: Layer of the hit (used when counting layers)
0100   /// @param weight: Optional weight to assign to the hit
0101   void fill(const identifier_t& identifier, unsigned int layer,
0102             YieldType weight = 1.);
0103   /// @brief access the number of layers with hits compatible with this cell
0104   YieldType nLayers() const { return m_nLayers; }
0105   /// @brief access the number of unique hits compatible with this cell
0106   YieldType nHits() const { return m_nHits; }
0107   /// @brief access the span of layers compatible with this cell
0108   std::span<const unsigned, std::dynamic_extent> getLayers() const;
0109   // /// @brief access the pan of unique hits compatible with this cell
0110   std::span<const identifier_t, std::dynamic_extent> getHits() const;
0111 
0112   /// @brief reset this cell, removing any existing content.
0113   void reset();
0114 
0115  private:
0116   /// (weighted) number of layers with hits on this cell
0117   YieldType m_nLayers{0};
0118   /// (weighted) number of unique hits on this cell
0119   YieldType m_nHits{0};
0120 
0121   /// index for the hits -- keeps track of vector's size
0122   std::size_t m_iHit{0};
0123   /// index for the layers -- keeps track of vector's size
0124   std::size_t m_iLayer{0};
0125 
0126   /// a batch to resize the vector of the hits or the layers
0127   std::size_t m_assignBatch{20};
0128 
0129   /// vector of layers with hits on this cell
0130   std::vector<unsigned> m_layers{std::vector<unsigned>(m_assignBatch)};
0131 
0132   /// vector of hits on this cell
0133   std::vector<identifier_t> m_hits{std::vector<identifier_t>(m_assignBatch)};
0134 };
0135 
0136 /// @brief Configuration - number of bins in each axis.
0137 /// The Hough plane is agnostic of how the bins map to
0138 /// coordinates, allowing to re-use a plane for several
0139 /// (sub) detectors of different dimensions if the bin number
0140 /// remains applicable
0141 struct HoughPlaneConfig {
0142   std::size_t nBinsX = 0;  // number of bins in the first coordinate
0143   std::size_t nBinsY = 0;  // number of bins in the second coordinate
0144 };
0145 
0146 /// @brief Representation of the hough plane - the histogram used
0147 /// for the hough transform with methods to fill and evaluate
0148 /// the histogram. Templated to a class used as identifier for the hits
0149 template <class identifier_t>
0150 class HoughPlane {
0151  public:
0152   /// @brief hough histogram representation as a 2D-indexable vector of hough cells
0153   using Axis =
0154       Acts::Axis<Acts::AxisType::Equidistant, Acts::AxisBoundaryType::Bound>;
0155   using HoughHist = Grid<HoughCell<identifier_t>, Axis, Axis>;
0156   using Index = typename HoughHist::index_t;
0157 
0158   /// @brief instantiate the (empty) hough plane
0159   /// @param cfg: configuration
0160   explicit HoughPlane(const HoughPlaneConfig& cfg);
0161 
0162   /// fill and reset methods to modify the grid content
0163 
0164   /// @brief add one measurement to the hough plane
0165   /// @tparam PointType: Type of the objects to use when adding measurements (e.g. experiment EDM object)
0166   /// @param measurement: The measurement to add
0167   /// @param axisRanges: Ranges of the hough axes, used to map the bin numbers to parameter values
0168   /// @param linePar: The function y(x) parametrising the hough space line for a given measurement
0169   /// @param widthPar: The function dy(x) parametrising the width of the y(x) curve
0170   ///                   for a given measurement
0171   /// @param identifier: The unique identifier for the given hit
0172   /// @param layer: A layer index for this hit
0173   /// @param weight: An optional weight to assign to this hit
0174   template <class PointType>
0175   void fill(const PointType& measurement, const HoughAxisRanges& axisRanges,
0176             LineParametrisation<PointType> linePar,
0177             LineParametrisation<PointType> widthPar,
0178             const identifier_t& identifier, unsigned layer = 0,
0179             YieldType weight = 1.0f);
0180   /// @brief resets the contents of the grid. Can be used to avoid reallocating the histogram
0181   /// when switching regions / (sub)detectors
0182   void reset();
0183 
0184   //// user-facing accessors
0185 
0186   /// @brief get the layers with hits in one cell of the histogram
0187   /// @param xBin: bin index in the first coordinate
0188   /// @param yBin: bin index in the second coordinate
0189   /// @return the set of layer indices that have hits for this cell
0190   std::unordered_set<unsigned> layers(std::size_t xBin,
0191                                       std::size_t yBin) const {
0192     return m_houghHist.atLocalBins({xBin, yBin}).layers();
0193   }
0194 
0195   /// @brief get the (weighted) number of layers  with hits in one cell of the histogram
0196   /// @param xBin: bin index in the first coordinate
0197   /// @param yBin: bin index in the second coordinate
0198   /// @return the (weighed) number of layers that have hits for this cell
0199   YieldType nLayers(std::size_t xBin, std::size_t yBin) const {
0200     return m_houghHist.atLocalBins({xBin, yBin}).nLayers();
0201   }
0202 
0203   /// @brief get the identifiers of all hits in one cell of the histogram
0204   /// @param xBin: bin index in the first coordinate
0205   /// @param yBin: bin index in the second coordinate
0206   /// @return the list of identifiers of the hits for this cell
0207   /// Can include duplicates if a hit was filled more than once
0208   std::span<const identifier_t, std::dynamic_extent> hitIds(
0209       std::size_t xBin, std::size_t yBin) const {
0210     return m_houghHist.atLocalBins({xBin, yBin}).getHits();
0211   }
0212   /// @brief get the identifiers of all hits in one cell of the histogram
0213   /// @param xBin: bin index in the first coordinate
0214   /// @param yBin: bin index in the second coordinate
0215   /// @return the list of identifiers of the hits for this cell
0216   /// Guaranteed to not duplicate identifiers
0217   std::unordered_set<const identifier_t> uniqueHitIds(std::size_t xBin,
0218                                                       std::size_t yBin) const {
0219     const auto hits_span = m_houghHist.atLocalBins({xBin, yBin}).getHits();
0220     return std::unordered_set<identifier_t>(hits_span.begin(), hits_span.end());
0221   }
0222   /// @brief access the (weighted) number of hits in one cell of the histogram from bin's coordinates
0223   /// @param xBin: bin index in the first coordinate
0224   /// @param yBin: bin index in the second coordinate
0225   /// @return the (weighted) number of hits for this cell
0226   YieldType nHits(std::size_t xBin, std::size_t yBin) const {
0227     return m_houghHist.atLocalBins({xBin, yBin}).nHits();
0228   }
0229 
0230   /// @brief access the (weighted) number of hits in one cell of the histogram from globalBin index
0231   /// @param globalBin: global bin index
0232   /// @return the (weighted) number of hits for this cell
0233   YieldType nHits(std::size_t globalBin) const {
0234     return m_houghHist.at(globalBin).nHits();
0235   }
0236 
0237   /// @brief get the number of bins on the first coordinate
0238   std::size_t nBinsX() const { return m_cfg.nBinsX; }
0239   /// @brief get the number of bins on the second coordinate
0240   std::size_t nBinsY() const { return m_cfg.nBinsY; }
0241 
0242   /// @brief get the maximum number of (weighted) hits seen in a single
0243   /// cell across the entire histrogram.
0244   YieldType maxHits() const { return m_maxHits; }
0245 
0246   /// @brief get the list of cells with non-zero content.
0247   /// Useful for peak-finders in sparse data
0248   /// to avoid looping over all cells
0249   const std::unordered_set<std::size_t>& getNonEmptyBins() const {
0250     return m_touchedBins;
0251   }
0252 
0253   /// @brief get the coordinates of the bin given the global bin index
0254   Index axisBins(std::size_t globalBin) const {
0255     return m_houghHist.localBinsFromGlobalBin(globalBin);
0256   }
0257 
0258   /// @brief get the globalBin index given the coordinates of the bin
0259   std::size_t globalBin(Index indexBin) const {
0260     return m_houghHist.globalBinFromLocalBins(indexBin);
0261   }
0262 
0263   /// @brief get the bin indices of the cell containing the largest number
0264   /// of (weighted) hits across the entire histogram
0265   std::pair<std::size_t, std::size_t> locMaxHits() const {
0266     return m_maxLocHits;
0267   }
0268 
0269   /// @brief get the maximum number of (weighted) layers with hits  seen
0270   /// in a single cell across the entire histrogram.
0271   YieldType maxLayers() const { return m_maxLayers; }
0272 
0273   /// @brief get the bin indices of the cell containing the largest number
0274   /// of (weighted) layers with hits across the entire histogram
0275   std::pair<std::size_t, std::size_t> locMaxLayers() const {
0276     return m_maxLocLayers;
0277   }
0278 
0279  private:
0280   /// @brief Helper method to fill a bin of the hough histogram.
0281   /// Updates the internal helper data structures (maximum tracker etc).
0282   /// @param binX: bin number along x
0283   /// @param binY: bin number along y
0284   /// @param identifier: hit identifier
0285   /// @param layer: layer index
0286   /// @param w: optional hit weight
0287   void fillBin(std::size_t binX, std::size_t binY,
0288                const identifier_t& identifier, unsigned layer, double w = 1.0f);
0289 
0290   YieldType m_maxHits = 0.0f;    // track the maximum number of hits seen
0291   YieldType m_maxLayers = 0.0f;  // track the maximum number of layers seen
0292 
0293   /// track the location of the maximum in hits
0294   std::pair<std::size_t, std::size_t> m_maxLocHits = {0, 0};
0295   /// track the location of the maximum in layers
0296   std::pair<std::size_t, std::size_t> m_maxLocLayers = {0, 0};
0297 
0298   std::size_t m_assignBatch{20};
0299 
0300   /// track the bins with non-trivial content
0301   std::unordered_set<std::size_t> m_touchedBins{};
0302 
0303   std::size_t m_iBin = 0;
0304 
0305   HoughPlaneConfig m_cfg;  // the configuration object
0306   HoughHist m_houghHist;   // the histogram data object
0307 };
0308 
0309 /// example peak finders.
0310 namespace PeakFinders {
0311 /// configuration for the LayerGuidedCombinatoric peak finder
0312 struct LayerGuidedCombinatoricConfig {
0313   YieldType threshold = 3.0f;  // min number of layers to obtain a maximum
0314   int localMaxWindowSize = 0;  // Only create candidates from a local maximum
0315 };
0316 
0317 /// @brief Peak finder inspired by ATLAS ITk event filter developments.
0318 /// Builds peaks based on layer counts and allows for subsequent resolution
0319 /// of the combinatorics by building multiple candidates per peak if needed.
0320 /// In flat regions, peak positions are moved towards smaller values of the
0321 /// second and first coordinate.
0322 /// @tparam identifier_t: The identifier type to use. Should match the one used in the hough plane.
0323 template <class identifier_t>
0324 class LayerGuidedCombinatoric {
0325  public:
0326   /// @brief data class representing the found maxima.
0327   /// Here, we just have a list of cluster identifiers
0328   struct Maximum {
0329     std::unordered_set<identifier_t> hitIdentifiers =
0330         {};  // identifiers of contributing hits
0331   };
0332   /// @brief constructor
0333   /// @param cfg: Configuration object
0334   explicit LayerGuidedCombinatoric(const LayerGuidedCombinatoricConfig& cfg);
0335 
0336   /// @brief main peak finder method.
0337   /// @param plane: Filled hough plane to search
0338   /// @return vector of found maxima
0339   std::vector<Maximum> findPeaks(const HoughPlane<identifier_t>& plane) const;
0340 
0341  private:
0342   /// @brief check if a given bin is a local maximum.
0343   /// @param plane: The filled hough plane
0344   /// @param xBin: x bin index
0345   /// @param yBin: y bin index
0346   /// @return true if a maximum, false otherwise
0347   bool passThreshold(const HoughPlane<identifier_t>& plane, std::size_t xBin,
0348                      std::size_t yBin) const;  // did we pass extensions?
0349 
0350   LayerGuidedCombinatoricConfig m_cfg;  // configuration data object
0351 };
0352 /// @brief Configuration for the IslandsAroundMax peak finder
0353 struct IslandsAroundMaxConfig {
0354   YieldType threshold =
0355       3.0f;  // min number of weigted hits required in a maximum
0356   YieldType fractionCutoff =
0357       0;  // Fraction of the global maximum at which to cut off maxima
0358   std::pair<CoordType, CoordType> minSpacingBetweenPeaks = {
0359       0.0f, 0.0f};  // minimum distance of a new peak from existing peaks in
0360                     // parameter space
0361 };
0362 /// @brief Peak finder inspired by ATLAS muon reconstruction.
0363 /// Looks for regions above a given fraction of the global maximum
0364 /// hit count and connects them into islands comprising adjacent bins
0365 /// above the threshold. Peak positions are averaged across cells in the island,
0366 /// weighted by hit counts
0367 /// @tparam identifier_t: The identifier type to use. Should match the one used in the hough plane.
0368 template <class identifier_t>
0369 class IslandsAroundMax {
0370  public:
0371   /// @brief data struct representing a local maximum.
0372   /// Comes with a position estimate and a list of hits within the island
0373   struct Maximum {
0374     CoordType x = 0;   // x value of the maximum
0375     CoordType y = 0;   // y value of the maximum
0376     CoordType wx = 0;  // x width of the maximum
0377     CoordType wy = 0;  // y width of the maximum
0378     std::unordered_set<identifier_t> hitIdentifiers =
0379         {};  // identifiers of contributing hits
0380   };
0381   /// @brief constructor.
0382   /// @param cfg: configuration object
0383   explicit IslandsAroundMax(const IslandsAroundMaxConfig& cfg);
0384 
0385   /// @brief main peak finder method.
0386   /// @param plane: The filled hough plane to search
0387   /// @param ranges: The axis ranges used for mapping between parameter space and bins.
0388   /// @return List of the found maxima
0389   std::vector<Maximum> findPeaks(const HoughPlane<identifier_t>& plane,
0390                                  const HoughAxisRanges& ranges);
0391 
0392  private:
0393   /// @brief method to incrementally grow an island by adding adjacent cells
0394   /// Performs a breadth-first search for neighbours above threshold and adds
0395   /// them to candidate. Stops when no suitable neighbours are left.
0396   /// @param houghPlane: The current hough Plane we are looking for maxima
0397   /// @param inMaximum: List of cells found in the island. Incrementally populated by calls to the method
0398   /// @param toExplore: List of the global Bin indices of neighbour cell candidates left to explore. Method will not do anything once this is empty
0399   /// @param threshold: the threshold to apply to check if a cell should be added to an island
0400   /// @param yieldMap: A map of the hit content of above-threshold cells. Used cells will be set to empty content to avoid re-use by subsequent calls
0401   void extendMaximum(const HoughPlane<identifier_t>& houghPlane,
0402                      std::vector<std::array<std::size_t, 2>>& inMaximum,
0403                      std::vector<std::size_t>& toExplore, YieldType threshold,
0404                      std::unordered_map<std::size_t, YieldType>& yieldMap);
0405 
0406   IslandsAroundMaxConfig m_cfg;  // configuration data object
0407 
0408   /// @brief array of steps to consider when exploring neighbouring cells.
0409   const std::array<std::pair<int, int>, 8> m_stepDirections{
0410       std::make_pair(-1, -1), std::make_pair(0, -1), std::make_pair(1, -1),
0411       std::make_pair(-1, 0),  std::make_pair(1, 0),  std::make_pair(-1, 1),
0412       std::make_pair(0, 1),   std::make_pair(1, 1)};
0413 };
0414 }  // namespace PeakFinders
0415 }  // namespace Acts::HoughTransformUtils
0416 
0417 #include "HoughTransformUtils.ipp"