Belle II Software  release-08-01-10
MultivariateNormalGenerator.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 #include <framework/utilities/MultivariateNormalGenerator.h>
10 #include <framework/logging/Logger.h>
11 
12 using namespace Belle2;
13 
15 {
16  m_mean.resize(0);
17  m_transform.resize(0, 0);
18 }
19 
20 bool MultivariateNormalGenerator::setMeanCov(int n, const double* mean, const double* cov)
21 {
22  Eigen::VectorXd emean(n);
23  Eigen::MatrixXd ecov(n, n);
24  //Fill the vector and matrix from the buffers in row major layout
25  for (int i = 0; i < n; ++i) {
26  emean[i] = mean[i];
27  for (int j = 0; j < n; ++j) {
28  ecov(i, j) = cov[i * n + j];
29  }
30  }
31  //And delegate to the correct function
32  return setMeanCov(emean, ecov);
33 }
34 
35 
36 bool MultivariateNormalGenerator::setMeanCov(const Eigen::VectorXd& mean, const Eigen::MatrixXd& cov)
37 {
38  reset();
39  if (mean.rows() != cov.rows()) {
40  B2ERROR("Mean values and covariance matrix need to be of the same dimension");
41  return false;
42  }
43  if (cov.rows() != cov.cols()) {
44  B2ERROR("Covariance matrix needs to be a square matrix");
45  return false;
46  }
47 
48  // The usual way to calulate multivariate normal distributed values is to use
49  // the cholesky decomposition C = ll' and then calculate y = mean + l * x
50  // where x is a vector of standard normal distributed values. However this
51  // only works for positive definite matrices so we use the LDLT composition
52  // which gives us C = P'LDL'P and we compute L=P'Lsqrt(D) to get the same
53  // result but in a more robust way which works also for semi-definite
54  // matrices
55  auto ldlt = cov.ldlt();
56  if (ldlt.info() != Eigen::Success) {
57  B2ERROR("Cannot compute LDLT decomposition of covariance "
58  "matrix, maybe not positive semi-definite?");
59  return false;
60  }
61  Eigen::MatrixXd L = ldlt.matrixL();
62  Eigen::MatrixXd D = ldlt.vectorD().asDiagonal();
63  if (D.minCoeff() < 0) {
64  B2ERROR("MultivariateNormalGenerator: Negative values when computing LDL^T "
65  "decomposition, cannot compute M=AA^T, resulting random numbers "
66  "will not be correct");
67  return false;
68  }
69  auto P = ldlt.transpositionsP().transpose();
70  m_transform = P * L * D.cwiseSqrt();
71  //And save the mean values
72  m_mean = mean;
73  return true;
74 }
75 
Eigen::VectorXd m_mean
Member to store the mean values of the distribution.
bool setMeanCov(int n, const double *mean, const double *cov)
set the mean and covariance for the distribution with array interface: mean and covariance are passed...
void reset()
reset the generator setting the size to 0.
Eigen::MatrixXd m_transform
Member to store the transformation matrix for standard normal distributed random values.
Abstract base class for different kinds of events.