File indexing completed on 2026-11-05 09:57:57
0001
0002
0003
0004
0005
0006
0007
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
0027
0028
0029
0030
0031
0032
0033 std::tuple<BoundVector, BoundMatrix> mergeGaussianMixture(
0034 std::span<const GsfComponent> mixture, const Surface &surface,
0035 ComponentMergeMethod method);
0036
0037
0038
0039
0040
0041
0042
0043 GsfComponent mergeTwoComponents(const GsfComponent &a, const GsfComponent &b,
0044 const Surface &surface);
0045
0046
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;
0057 };
0058
0059
0060
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
0077
0078
0079
0080
0081
0082
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
0105
0106
0107
0108
0109
0110
0111
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
0118 if (cmps.size() == 1) {
0119 const auto [weight_l, pars_l] = projector(cmps.front());
0120 return pars_l;
0121 }
0122
0123
0124
0125
0126
0127
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
0154
0155 return mean;
0156 }
0157
0158
0159
0160
0161
0162
0163
0164
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
0178
0179
0180
0181
0182
0183
0184
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;
0203
0204 BoundVector diff = pars_l - mean;
0205
0206
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
0226
0227
0228
0229
0230
0231
0232
0233
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
0253 const BoundMatrix cov =
0254 mergeGaussianMixtureCov(cmps, projector, mean, angleDesc);
0255
0256
0257 return {mean, cov};
0258 }
0259
0260
0261
0262
0263
0264
0265
0266
0267
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
0284
0285
0286
0287
0288
0289
0290
0291
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
0315
0316
0317
0318
0319
0320
0321
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
0333
0334 double computeSymmetricKlDivergence(const GsfComponent &a,
0335 const GsfComponent &b);
0336
0337
0338
0339
0340
0341
0342
0343
0344
0345
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
0356
0357
0358
0359
0360
0361
0362
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
0380 for (std::size_t i = 0ul; i < n; ++i) {
0381 m_distances[triangularIndex(n, i)] = setter(n, i);
0382 }
0383
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 }