Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
GaussianMixtureReduction.hpp
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file GaussianMixtureReduction.hpp
1 // This file is part of the Acts project.
2 //
3 // Copyright (C) 2021 CERN for the benefit of the Acts project
4 //
5 // This Source Code Form is subject to the terms of the Mozilla Public
6 // License, v. 2.0. If a copy of the MPL was not distributed with this
7 // file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 
9 #pragma once
10 
16 
17 #include <cmath>
18 #include <optional>
19 #include <tuple>
20 
21 namespace Acts {
22 namespace detail {
23 
25 template <BoundIndices Idx>
26 struct CyclicAngle {
27  constexpr static BoundIndices idx = Idx;
28  constexpr static double constant = 1.0;
29 };
30 
31 template <BoundIndices Idx>
33  constexpr static BoundIndices idx = Idx;
34  double constant = 1.0; // the radius
35 };
36 
38 template <Surface::SurfaceType type_t>
40  using Desc = std::tuple<CyclicAngle<eBoundPhi>>;
41 };
42 
43 template <>
44 struct AngleDescription<Surface::Disc> {
45  using Desc = std::tuple<CyclicAngle<eBoundLoc1>, CyclicAngle<eBoundPhi>>;
46 };
47 
48 template <>
49 struct AngleDescription<Surface::Cylinder> {
50  using Desc =
51  std::tuple<CyclicRadiusAngle<eBoundLoc0>, CyclicAngle<eBoundPhi>>;
52 };
53 
54 // Helper function that encapsulates a switch-case to select the correct angle
55 // description dependent on the surface
56 template <typename Callable>
57 auto angleDescriptionSwitch(const Surface &surface, Callable &&callable) {
58  switch (surface.type()) {
59  case Surface::Cylinder: {
61  const auto &bounds =
62  static_cast<const CylinderSurface &>(surface).bounds();
63  std::get<0>(desc).constant = bounds.get(CylinderBounds::eR);
64  return callable(desc);
65  }
66  case Surface::Disc: {
68  return callable(desc);
69  }
70  default: {
72  return callable(desc);
73  }
74  }
75 }
76 
77 template <int D, typename components_t, typename projector_t,
78  typename angle_desc_t>
80  const ActsVector<D> &mean, double sumOfWeights,
81  projector_t &&projector,
82  const angle_desc_t &angleDesc) {
84 
85  for (const auto &cmp : components) {
86  const auto &[weight_l, pars_l, cov_l] = projector(cmp);
87 
88  cov += weight_l * cov_l; // MARK: fpeMask(FLTUND, 1, #2347)
89 
90  ActsVector<D> diff = pars_l - mean;
91 
92  // Apply corrections for cyclic coordinates
93  auto handleCyclicCov = [&l = pars_l, &m = mean, &diff = diff](auto desc) {
94  diff[desc.idx] =
95  difference_periodic(l[desc.idx] / desc.constant,
96  m[desc.idx] / desc.constant, 2 * M_PI) *
97  desc.constant;
98  };
99 
100  std::apply([&](auto... dsc) { (handleCyclicCov(dsc), ...); }, angleDesc);
101 
102  cov += weight_l * diff * diff.transpose();
103  }
104 
105  cov /= sumOfWeights;
106  return cov;
107 }
108 
126 template <typename components_t, typename projector_t = Identity,
127  typename angle_desc_t = AngleDescription<Surface::Plane>::Desc>
129  projector_t &&projector = projector_t{},
130  const angle_desc_t &angleDesc = angle_desc_t{}) {
131  // Extract the first component
132  const auto &[beginWeight, beginPars, beginCov] =
133  projector(components.front());
134 
135  // Assert type properties
136  using ParsType = std::decay_t<decltype(beginPars)>;
137  using CovType = std::decay_t<decltype(beginCov)>;
138  using WeightType = std::decay_t<decltype(beginWeight)>;
139 
140  constexpr int D = ParsType::RowsAtCompileTime;
141  EIGEN_STATIC_ASSERT_VECTOR_ONLY(ParsType);
142  EIGEN_STATIC_ASSERT_MATRIX_SPECIFIC_SIZE(CovType, D, D);
143  static_assert(std::is_floating_point_v<WeightType>);
144 
145  // gcc 8 does not like this statement somehow. We must handle clang here since
146  // it defines __GNUC__ as 4.
147 #if defined(__GNUC__) && __GNUC__ < 9 && !defined(__clang__)
148  // No check
149 #else
150  std::apply(
151  [&](auto... d) { static_assert((std::less<int>{}(d.idx, D) && ...)); },
152  angleDesc);
153 #endif
154 
155  // Define the return type
156  using RetType = std::tuple<ActsVector<D>, ActsSquareMatrix<D>>;
157 
158  // Early return in case of range with length 1
159  if (components.size() == 1) {
160  return RetType{beginPars / beginWeight, beginCov / beginWeight};
161  }
162 
163  // Zero initialized values for aggregation
164  ActsVector<D> mean = ActsVector<D>::Zero();
165  WeightType sumOfWeights{0.0};
166 
167  for (const auto &cmp : components) {
168  const auto &[weight_l, pars_l, cov_l] = projector(cmp);
169 
170  sumOfWeights += weight_l;
171  mean += weight_l * pars_l;
172 
173  // Apply corrections for cyclic coordinates
174  auto handleCyclicMean = [&ref = beginPars, &pars = pars_l,
175  &weight = weight_l, &mean = mean](auto desc) {
176  const auto delta = (ref[desc.idx] - pars[desc.idx]) / desc.constant;
177 
178  if (delta > M_PI) {
179  mean[desc.idx] += (2 * M_PI) * weight * desc.constant;
180  } else if (delta < -M_PI) {
181  mean[desc.idx] -= (2 * M_PI) * weight * desc.constant;
182  }
183  };
184 
185  std::apply([&](auto... dsc) { (handleCyclicMean(dsc), ...); }, angleDesc);
186  }
187 
188  mean /= sumOfWeights;
189 
190  auto wrap = [&](auto desc) {
191  mean[desc.idx] =
192  wrap_periodic(mean[desc.idx] / desc.constant, -M_PI, 2 * M_PI) *
193  desc.constant;
194  };
195 
196  std::apply([&](auto... dsc) { (wrap(dsc), ...); }, angleDesc);
197 
198  // MARK: fpeMaskBegin(FLTUND, 1, #2347)
199  const auto cov =
200  gaussianMixtureCov(components, mean, sumOfWeights, projector, angleDesc);
201  // MARK: fpeMaskEnd(FLTUND)
202 
203  return RetType{mean, cov};
204 }
205 
206 } // namespace detail
207 
211 enum class MixtureReductionMethod { eMean, eMaxWeight };
212 
222 template <typename mixture_t, typename projector_t = Acts::Identity>
223 auto reduceGaussianMixture(const mixture_t &mixture, const Surface &surface,
224  MixtureReductionMethod method,
225  projector_t &&projector = projector_t{}) {
226  using R = std::tuple<Acts::BoundVector, Acts::BoundSquareMatrix>;
227  const auto [mean, cov] =
228  detail::angleDescriptionSwitch(surface, [&](const auto &desc) {
229  return detail::gaussianMixtureMeanCov(mixture, projector, desc);
230  });
231 
232  if (method == MixtureReductionMethod::eMean) {
233  return R{mean, cov};
234  } else {
235  const auto maxWeightIt = std::max_element(
236  mixture.begin(), mixture.end(), [&](const auto &a, const auto &b) {
237  return std::get<0>(projector(a)) < std::get<0>(projector(b));
238  });
239  const BoundVector meanMaxWeight = std::get<1>(projector(*maxWeightIt));
240 
241  return R{meanMaxWeight, cov};
242  }
243 }
244 
245 } // namespace Acts