ATLAS Offline Software
CaloClusterMLGaussianMixture.h
Go to the documentation of this file.
1 /*
2  Copyright (C) 2002-2025 CERN for the benefit of the ATLAS collaboration
3 */
4 
5 #ifndef CALOCLUSTERCORRECTION_CALOCLUSTERMLCALIBGAUSSIANMIXTURE_H
6 #define CALOCLUSTERCORRECTION_CALOCLUSTERMLCALIBGAUSSIANMIXTURE_H
7 
8 #include <cmath>
9 #include <vector>
10 #include <numeric>
11 #include <algorithm>
12 #include <limits>
13 
14 namespace CaloClusterMLCalib
15 {
16  constexpr float epsilon = 1e-9; // Introduced to avoid FPE DIVBYZERO warning in std::log()
17 
18  // Helper function for logsumexp
19  float log_sum_exp(const std::vector<float> &vec)
20  {
21  if (vec.empty())
22  {
23  return -std::numeric_limits<float>::infinity();
24  }
25  float max_val = *std::max_element(vec.begin(), vec.end());
26  float sum = 0.0;
27  for (float val : vec)
28  {
29  sum += std::exp(val - max_val);
30  }
31  return max_val + std::log(sum);
32  }
33 
34  float log_likelihood(float x, const std::vector<float> &mus, const std::vector<float> &log_sigma2s, const std::vector<float> &alphas)
35  {
36  // Ensure vectors have the correct size
37  assert(mus.size() == 3 && log_sigma2s.size() == 3 && alphas.size() == 3);
38 
39  std::vector<float> log_likelihood_components(3);
40 
41  for (int i = 0; i < 3; ++i)
42  {
43  // Calculate one-dimensional negative log-Gaussian
44  float neg_log_gauss = std::pow(mus[i] - x, 2.0) / (2.0 * std::exp(log_sigma2s[i])) + 0.5 * log_sigma2s[i];
45  neg_log_gauss += 0.5 * std::log(2.0 * M_PI); // M_PI is a common constant for pi
46 
47  // log_prob_components = -neg_log_gauss_i + log(alpha_i)
48  log_likelihood_components[i] = -neg_log_gauss + std::log(alphas[i] + epsilon); // Added epsilon to avoid FPE DIVBYZERO warning
49  }
50 
51  // log_prob = log(sum_i exp(log_prob_components_i))
52  return log_sum_exp(log_likelihood_components);
53  }
54 
55  float modes(const std::vector<float> &mus, const std::vector<float> &log_sigma2s, const std::vector<float> &alphas)
56  {
57  // Select range to check for maxima
58  float x_min = 0.9 * (*std::min_element(mus.begin(), mus.end()));
59  float x_max = 1.1 * (*std::max_element(mus.begin(), mus.end()));
60 
61  // Create array of ranges to check for maxima
62  const int num_points = 1000;
63  std::vector<float> x_test(num_points);
64  float step = (x_max - x_min) / (num_points - 1);
65  for (int i = 0; i < num_points; ++i)
66  {
67  x_test[i] = x_min + i * step;
68  }
69 
70  float max_log_likelihood = -std::numeric_limits<float>::infinity();
71  float mode = x_test[0]; // Initialize to first test point instead of 0
72 
73  // Find the x-point with the largest likelihood value
74  for (float x : x_test)
75  {
76  float ll = log_likelihood(x, mus, log_sigma2s, alphas);
77  if (ll > max_log_likelihood)
78  {
79  max_log_likelihood = ll;
80  mode = x;
81  }
82  }
83 
84  return mode;
85  }
86 
87  float sigma_stoch(const std::vector<float> &mus,
88  const std::vector<float> &log_sigma2s,
89  const std::vector<float> &alphas)
90  {
91  float sigma_stoch2 = 0.0;
92  float sum_alphas_mus = 0.0;
93 
94  // This loop combines the first three lines of the Python function.
95  for (size_t i = 0; i < mus.size(); ++i)
96  {
97  // Corresponds to: np.sum(alphas * sigma2s, axis=-1)
98  sigma_stoch2 += alphas[i] * std::exp(log_sigma2s[i]);
99  // Corresponds to: np.sum(alphas * np.power(mus, 2), axis=-1)
100  sigma_stoch2 += alphas[i] * std::pow(mus[i], 2);
101  // Accumulates sum for the final term: np.sum(alphas * mus, axis=-1)
102  sum_alphas_mus += alphas[i] * mus[i];
103  }
104 
105  // Corresponds to: sigma_stoch2 -= np.power(np.sum(alphas * mus, axis=-1), 2)
106  sigma_stoch2 -= std::pow(sum_alphas_mus, 2);
107 
108  // Corresponds to: return np.sqrt(sigma_stoch2)
109  return std::sqrt(sigma_stoch2);
110  }
111 }
112 
113 #endif // CALOCLUSTERCORRECTION_CALOCLUSTERMLCALIBGAUSSIANMIXTURE_H
AllowedVariables::e
e
Definition: AsgElectronSelectorTool.cxx:37
detail::ll
long long ll
Definition: PrimitiveHelpers.h:47
CaloClusterMLCalib::epsilon
constexpr float epsilon
Definition: CaloClusterMLGaussianMixture.h:16
M_PI
#define M_PI
Definition: ActiveFraction.h:11
vec
std::vector< size_t > vec
Definition: CombinationsGeneratorTest.cxx:9
drawFromPickle.exp
exp
Definition: drawFromPickle.py:36
x
#define x
CaloClusterMLCalib::log_likelihood
float log_likelihood(float x, const std::vector< float > &mus, const std::vector< float > &log_sigma2s, const std::vector< float > &alphas)
Definition: CaloClusterMLGaussianMixture.h:34
convertTimingResiduals.sum
sum
Definition: convertTimingResiduals.py:55
lumiFormat.i
int i
Definition: lumiFormat.py:85
Preparation.mode
mode
Definition: Preparation.py:107
CaloClusterMLCalib::sigma_stoch
float sigma_stoch(const std::vector< float > &mus, const std::vector< float > &log_sigma2s, const std::vector< float > &alphas)
Definition: CaloClusterMLGaussianMixture.h:87
CaloClusterMLCalib::modes
float modes(const std::vector< float > &mus, const std::vector< float > &log_sigma2s, const std::vector< float > &alphas)
Definition: CaloClusterMLGaussianMixture.h:55
CaloClusterMLCalib::log_sum_exp
float log_sum_exp(const std::vector< float > &vec)
Definition: CaloClusterMLGaussianMixture.h:19
Pythia8_RapidityOrderMPI.val
val
Definition: Pythia8_RapidityOrderMPI.py:14
python.CaloCondTools.log
log
Definition: CaloCondTools.py:20
LArCellBinning.step
step
Definition: LArCellBinning.py:158
CaloClusterMLCalib
Definition: CaloClusterMLCalibFeatureTransform.h:16
pow
constexpr int pow(int base, int exp) noexcept
Definition: ap_fixedTest.cxx:15