Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-06-04 08:11:44

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 #include "Acts/Definitions/TrackParametrization.hpp"
0012 #include "Acts/Surfaces/CylinderSurface.hpp"
0013 #include "Acts/TrackFitting/GsfComponent.hpp"
0014 #include "Acts/TrackFitting/GsfOptions.hpp"
0015 #include "Acts/Utilities/detail/periodic.hpp"
0016 
0017 #include <iosfwd>
0018 #include <stdexcept>
0019 #include <tuple>
0020 
0021 namespace Acts::detail::Gsf {
0022 
0023 /// Reduce Gaussian mixture into a single parameter vector and covariance, using
0024 /// the specified method to reduce the mixture.
0025 ///
0026 /// @param mixture The mixture iterable
0027 /// @param surface The surface, on which the bound state is
0028 /// @param method How to reduce the mixture
0029 /// @return parameters and covariance as tuple
0030 std::tuple<BoundVector, BoundMatrix> mergeGaussianMixture(
0031     std::span<const GsfComponent> mixture, const Surface &surface,
0032     ComponentMergeMethod method);
0033 
0034 /// Merge two Gaussian mixture components into one
0035 ///
0036 /// @param a First component
0037 /// @param b Second component
0038 /// @param surface The surface, on which the bound state is
0039 /// @return merged component
0040 GsfComponent mergeTwoComponents(const GsfComponent &a, const GsfComponent &b,
0041                                 const Surface &surface);
0042 
0043 /// Angle descriptions for the combineBoundGaussianMixture function
0044 template <BoundIndices Idx>
0045 struct CyclicAngle {
0046   constexpr static BoundIndices idx = Idx;
0047   constexpr static double constant = 1.0;
0048 };
0049 
0050 template <BoundIndices Idx>
0051 struct CyclicRadiusAngle {
0052   constexpr static BoundIndices idx = Idx;
0053   double constant = 1.0;  // the radius
0054 };
0055 
0056 /// A compile time map to provide angle descriptions for different surfaces
0057 template <Surface::SurfaceType type_t>
0058 struct AngleDescription {
0059   using Desc = std::tuple<CyclicAngle<eBoundPhi>>;
0060 };
0061 
0062 template <>
0063 struct AngleDescription<Surface::Disc> {
0064   using Desc = std::tuple<CyclicAngle<eBoundLoc1>, CyclicAngle<eBoundPhi>>;
0065 };
0066 
0067 template <>
0068 struct AngleDescription<Surface::Cylinder> {
0069   using Desc =
0070       std::tuple<CyclicRadiusAngle<eBoundLoc0>, CyclicAngle<eBoundPhi>>;
0071 };
0072 
0073 /// Helper function that encapsulates a switch-case to select the correct angle
0074 /// description dependent on the surface
0075 /// @param surface The surface, which the bound state is on
0076 /// @param callable A callable that takes the angle description as argument
0077 /// @return The return value of the callable
0078 template <typename Callable>
0079 auto angleDescriptionSwitch(const Surface &surface, Callable &&callable) {
0080   switch (surface.type()) {
0081     case Surface::Cylinder: {
0082       AngleDescription<Surface::Cylinder>::Desc desc{};
0083       const auto &bounds =
0084           static_cast<const CylinderSurface &>(surface).bounds();
0085       std::get<0>(desc).constant = bounds.get(CylinderBounds::eR);
0086       return callable(desc);
0087     }
0088     case Surface::Disc: {
0089       AngleDescription<Surface::Disc>::Desc desc{};
0090       return callable(desc);
0091     }
0092     default: {
0093       AngleDescription<Surface::Plane>::Desc desc{};
0094       return callable(desc);
0095     }
0096   }
0097 }
0098 
0099 /// Combine multiple components into one representative parameter object. The
0100 /// function takes a range and a projector to allow for arbitrary input to be
0101 /// combined.
0102 ///
0103 /// @param cmps The component range to merge
0104 /// @param projector A projector to extract the weight and parameters from the components
0105 /// @param angleDesc The angle description object
0106 /// @return parameters
0107 template <typename component_range_t, typename projector_t,
0108           typename angle_desc_t>
0109 BoundVector mergeGaussianMixtureMean(const component_range_t &cmps,
0110                                      const projector_t &projector,
0111                                      const angle_desc_t &angleDesc) {
0112   // Early return in case of range with length 1
0113   if (cmps.size() == 1) {
0114     const auto [weight_l, pars_l] = projector(cmps.front());
0115     return pars_l;
0116   }
0117 
0118   // Do the (circular) mean with complex arithmetic.
0119   // For normal values, just keep the real values. For angles, use the complex
0120   // phase. Weighting then transparently happens by multiplying a real-valued
0121   // weight.
0122   using CVec = Eigen::Matrix<std::complex<double>, eBoundSize, 1>;
0123   CVec cMean = CVec::Zero();
0124   double sumOfWeights = 0;
0125 
0126   for (const auto &cmp : cmps) {
0127     const auto [weight_l, pars_l] = projector(cmp);
0128     CVec cPars_l = pars_l;
0129 
0130     const auto setPolar = [&](const auto &desc) {
0131       cPars_l[desc.idx] = std::polar(1.0, pars_l[desc.idx] / desc.constant);
0132     };
0133     std::apply([&](auto... dsc) { (setPolar(dsc), ...); }, angleDesc);
0134 
0135     sumOfWeights += weight_l;
0136     cMean += weight_l * cPars_l;
0137   }
0138 
0139   cMean /= sumOfWeights;
0140 
0141   BoundVector mean = cMean.real();
0142 
0143   const auto getArg = [&](const auto &desc) {
0144     mean[desc.idx] = desc.constant * std::arg(cMean[desc.idx]);
0145   };
0146   std::apply([&](auto... dsc) { (getArg(dsc), ...); }, angleDesc);
0147 
0148   return mean;
0149 }
0150 
0151 /// Combine multiple components into one representative parameter object. The
0152 /// function takes a range and a projector to allow for arbitrary input to be
0153 /// combined.
0154 ///
0155 /// @param cmps The component range to merge
0156 /// @param projector A projector to extract the weight and parameters from the components
0157 /// @return parameters
0158 template <typename component_range_t, typename projector_t>
0159 BoundVector mergeGaussianMixtureMaxWeight(const component_range_t &cmps,
0160                                           const projector_t &projector) {
0161   const auto maxWeightIt =
0162       std::ranges::max_element(cmps, {}, [&](const auto &cmp) {
0163         const auto [weight_l, pars_l] = projector(cmp);
0164         return weight_l;
0165       });
0166   const auto [weight_max, pars_max] = projector(*maxWeightIt);
0167   return pars_max;
0168 }
0169 
0170 /// Compute the covariance of a Gaussian mixture given the mean. The function
0171 /// takes a range and a projector to allow for arbitrary input to be combined.
0172 ///
0173 /// @param cmps The component range to merge
0174 /// @param projector A projector to extract the weight and parameters from the components
0175 /// @param mean The precomputed mean of the mixture
0176 /// @param angleDesc The angle description object
0177 /// @return covariance
0178 template <typename component_range_t, typename projector_t,
0179           typename angle_desc_t>
0180 BoundMatrix mergeGaussianMixtureCov(const component_range_t &cmps,
0181                                     const projector_t &projector,
0182                                     const BoundVector &mean,
0183                                     const angle_desc_t &angleDesc) {
0184   if (cmps.size() == 1) {
0185     const auto [weight_l, pars_l, cov_l] = projector(cmps.front());
0186     return cov_l;
0187   }
0188 
0189   BoundMatrix cov = BoundMatrix::Zero();
0190   double sumOfWeights = 0;
0191 
0192   for (const auto &cmp : cmps) {
0193     const auto [weight_l, pars_l, cov_l] = projector(cmp);
0194 
0195     cov += weight_l * cov_l;  // MARK: fpeMask(FLTUND, 1, #2347)
0196 
0197     BoundVector diff = pars_l - mean;
0198 
0199     // Apply corrections for cyclic coordinates
0200     const auto handleCyclicCov = [&l = pars_l, &m = mean,
0201                                   &diff = diff](const auto &desc) {
0202       diff[desc.idx] = difference_periodic(l[desc.idx] / desc.constant,
0203                                            m[desc.idx] / desc.constant,
0204                                            2 * std::numbers::pi) *
0205                        desc.constant;
0206     };
0207     std::apply([&](auto... dsc) { (handleCyclicCov(dsc), ...); }, angleDesc);
0208 
0209     sumOfWeights += weight_l;
0210     cov += weight_l * diff * diff.transpose();
0211   }
0212 
0213   cov /= sumOfWeights;
0214 
0215   return cov;
0216 }
0217 
0218 /// Combine multiple components into one representative parameter object. The
0219 /// function takes a range and a projector to allow for arbitrary input to be
0220 /// combined.
0221 ///
0222 /// @param cmps The component range to merge
0223 /// @param projector A projector to extract the weight, parameters and covariance
0224 ///        from the components
0225 /// @param angleDesc The angle description object
0226 /// @return parameters and covariance
0227 template <typename component_range_t, typename projector_t,
0228           typename angle_desc_t>
0229 std::tuple<BoundVector, BoundMatrix> mergeGaussianMixtureMeanCov(
0230     const component_range_t &cmps, const projector_t &projector,
0231     const angle_desc_t &angleDesc) {
0232   if (cmps.size() == 1) {
0233     const auto [weight_l, pars_l, cov_l] = projector(cmps.front());
0234     return {pars_l, cov_l};
0235   }
0236 
0237   const BoundVector mean = mergeGaussianMixtureMean(
0238       cmps,
0239       [&](const auto &c) {
0240         const auto [weight_l, pars_l, cov_l] = projector(c);
0241         return std::tuple(weight_l, pars_l);
0242       },
0243       angleDesc);
0244 
0245   // MARK: fpeMaskBegin(FLTUND, 1, #2347)
0246   const BoundMatrix cov =
0247       mergeGaussianMixtureCov(cmps, projector, mean, angleDesc);
0248   // MARK: fpeMaskEnd(FLTUND)
0249 
0250   return {mean, cov};
0251 }
0252 
0253 /// Reduce Gaussian mixture into a single parameter vector, using the specified
0254 /// method to reduce the mixture.
0255 ///
0256 /// @param cmps The component range to merge
0257 /// @param projector A projector to extract the weight and parameters from the components
0258 /// @param angleDesc The angle description object
0259 /// @param method How to reduce the mixture
0260 /// @return parameters
0261 template <typename component_range_t, typename projector_t,
0262           typename angle_desc_t>
0263 BoundVector mergeGaussianMixtureParams(const component_range_t &cmps,
0264                                        const projector_t &projector,
0265                                        const angle_desc_t &angleDesc,
0266                                        ComponentMergeMethod method) {
0267   if (method == ComponentMergeMethod::eMean) {
0268     return mergeGaussianMixtureMean(cmps, projector, angleDesc);
0269   } else if (method == ComponentMergeMethod::eMaxWeight) {
0270     return mergeGaussianMixtureMaxWeight(cmps, projector);
0271   } else {
0272     throw std::logic_error("Invalid component merge method");
0273   }
0274 }
0275 
0276 /// Reduce Gaussian mixture into a single parameter vector and covariance, using
0277 /// the specified method to reduce the mixture.
0278 ///
0279 /// @param cmps The component range to merge
0280 /// @param projector A projector to extract the weight, parameters and covariance
0281 ///        from the components
0282 /// @param angleDesc The angle description object
0283 /// @param method How to reduce the mixture
0284 /// @return parameters and covariance
0285 template <typename component_range_t, typename projector_t,
0286           typename angle_desc_t>
0287 std::tuple<BoundVector, BoundMatrix> mergeGaussianMixture(
0288     const component_range_t &cmps, const projector_t &projector,
0289     const angle_desc_t &angleDesc, ComponentMergeMethod method) {
0290   const auto [mean, cov] =
0291       mergeGaussianMixtureMeanCov(cmps, projector, angleDesc);
0292 
0293   if (method == ComponentMergeMethod::eMean) {
0294     return {mean, cov};
0295   } else if (method == ComponentMergeMethod::eMaxWeight) {
0296     const BoundVector mode =
0297         mergeGaussianMixtureMaxWeight(cmps, [&](const auto &c) {
0298           const auto [weight_l, pars_l, cov_l] = projector(c);
0299           return std::tuple(weight_l, pars_l);
0300         });
0301     return {mode, cov};
0302   } else {
0303     throw std::logic_error("Invalid component merge method");
0304   }
0305 }
0306 
0307 /// Reduce Gaussian mixture into a single parameter vector, using the specified
0308 /// method to reduce the mixture.
0309 ///
0310 /// @param cmps The component range to merge
0311 /// @param projector A projector to extract the weight and parameters from the components
0312 /// @param surface The surface, which the bound state is on
0313 /// @param method How to reduce the mixture
0314 /// @return parameters
0315 template <typename component_range_t, typename projector_t>
0316 BoundVector mergeGaussianMixtureParams(const component_range_t &cmps,
0317                                        const projector_t &projector,
0318                                        const Surface &surface,
0319                                        ComponentMergeMethod method) {
0320   return angleDescriptionSwitch(surface, [&](const auto &desc) {
0321     return mergeGaussianMixtureParams(cmps, projector, desc, method);
0322   });
0323 }
0324 
0325 /// Reduce Gaussian mixture into a single parameter vector and covariance, using
0326 /// the specified method to reduce the mixture.
0327 ///
0328 /// @param cmps The component range to merge
0329 /// @param projector A projector to extract the weight, parameters and covariance
0330 ///        from the components
0331 /// @param surface The surface, which the bound state is on
0332 /// @param method How to reduce the mixture
0333 /// @return parameters and covariance
0334 template <typename component_range_t, typename projector_t>
0335 std::tuple<BoundVector, BoundMatrix> mergeGaussianMixture(
0336     const component_range_t &cmps, const projector_t &projector,
0337     const Surface &surface, ComponentMergeMethod method) {
0338   return angleDescriptionSwitch(surface, [&](const auto &desc) {
0339     return mergeGaussianMixture(cmps, projector, desc, method);
0340   });
0341 }
0342 
0343 /// @brief Class representing a symmetric distance matrix
0344 class SymmetricKLDistanceMatrix {
0345   using Array = Eigen::Array<double, Eigen::Dynamic, 1>;
0346   using Mask = Eigen::Array<bool, Eigen::Dynamic, 1>;
0347 
0348   Array m_distances;
0349   Mask m_mask;
0350   std::vector<std::pair<std::size_t, std::size_t>> m_mapToPair;
0351   std::size_t m_numberComponents{};
0352 
0353   template <typename array_t, typename setter_t>
0354   void setAssociated(std::size_t n, array_t &array, const setter_t &setter) {
0355     const std::size_t indexConst = (n - 1) * n / 2;
0356     // Rows
0357     for (std::size_t i = 0ul; i < n; ++i) {
0358       array[indexConst + i] = setter(n, i);
0359     }
0360     // Columns
0361     for (std::size_t i = n + 1; i < m_numberComponents; ++i) {
0362       array[(i - 1) * i / 2 + n] = setter(n, i);
0363     }
0364   }
0365 
0366   /// Computes the Kullback-Leibler distance between two components as shown in
0367   /// https://arxiv.org/abs/2001.00727v1 but ignoring the weights
0368   static double computeSymmetricKlDivergence(const GsfComponent &a,
0369                                              const GsfComponent &b);
0370 
0371  public:
0372   explicit SymmetricKLDistanceMatrix(std::span<const GsfComponent> cmps);
0373 
0374   double at(std::size_t i, std::size_t j) const;
0375 
0376   void recomputeAssociatedDistances(std::size_t n,
0377                                     std::span<const GsfComponent> cmps);
0378 
0379   void maskAssociatedDistances(std::size_t n);
0380 
0381   std::pair<std::size_t, std::size_t> minDistancePair() const;
0382 
0383   std::ostream &toStream(std::ostream &os) const;
0384 
0385   friend std::ostream &operator<<(std::ostream &os,
0386                                   const SymmetricKLDistanceMatrix &m) {
0387     return m.toStream(os);
0388   }
0389 };
0390 
0391 }  // namespace Acts::detail::Gsf