Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-11-05 09:57:57

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