LArPcaHelper.cc
Go to the documentation of this file.
1 /**
2  * @file larpandoracontent/LArHelpers/LArPcaHelper.cc
3  *
4  * @brief Implementation of the principal curve analysis helper class.
5  *
6  * $Log: $
7  */
8 
12 
13 #include <Eigen/Dense>
14 
15 using namespace pandora;
16 
17 namespace lar_content
18 {
19 
20 template <typename T>
21 void LArPcaHelper::RunPca(const T &t, CartesianVector &centroid, EigenValues &outputEigenValues, EigenVectors &outputEigenVectors)
22 {
23  WeightedPointVector weightedPointVector;
24 
25  for (const auto &point : t)
26  weightedPointVector.push_back(std::make_pair(LArObjectHelper::TypeAdaptor::GetPosition(point), 1.));
27 
28  return LArPcaHelper::RunPca(weightedPointVector, centroid, outputEigenValues, outputEigenVectors);
29 }
30 
31 //------------------------------------------------------------------------------------------------------------------------------------------
32 
33 void LArPcaHelper::RunPca(const WeightedPointVector &pointVector, CartesianVector &centroid, EigenValues &outputEigenValues, EigenVectors &outputEigenVectors)
34 {
35  // The steps are:
36  // 1) do a mean normalization of the input vec points
37  // 2) compute the covariance matrix
38  // 3) run the SVD
39  // 4) extract the eigen vectors and values
40 
41  // Run through the point vector and get the mean position of all points
42  if (pointVector.empty())
43  {
44  std::cout << "LArPcaHelper::RunPca - no three dimensional hits provided" << std::endl;
45  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
46  }
47 
48  double meanPosition[3] = {0., 0., 0.};
49  double sumWeight(0.);
50 
51  for (const WeightedPoint &weightedPoint : pointVector)
52  {
53  const CartesianVector &point(weightedPoint.first);
54  const double weight(weightedPoint.second);
55 
56  if (weight < 0.)
57  {
58  std::cout << "LArPcaHelper::RunPca - negative weight found" << std::endl;
59  throw StatusCodeException(STATUS_CODE_NOT_ALLOWED);
60  }
61 
62  meanPosition[0] += static_cast<double>(point.GetX()) * weight;
63  meanPosition[1] += static_cast<double>(point.GetY()) * weight;
64  meanPosition[2] += static_cast<double>(point.GetZ()) * weight;
65  sumWeight += weight;
66  }
67 
68  if (std::fabs(sumWeight) < std::numeric_limits<double>::epsilon())
69  {
70  std::cout << "LArPcaHelper::RunPca - sum of weights is zero" << std::endl;
71  throw StatusCodeException(STATUS_CODE_NOT_FOUND);
72  }
73 
74  meanPosition[0] /= sumWeight;
75  meanPosition[1] /= sumWeight;
76  meanPosition[2] /= sumWeight;
77  centroid = CartesianVector(meanPosition[0], meanPosition[1], meanPosition[2]);
78 
79  // Define elements of our covariance matrix
80  double xi2(0.);
81  double xiyi(0.);
82  double xizi(0.);
83  double yi2(0.);
84  double yizi(0.);
85  double zi2(0.);
86 
87  for (const WeightedPoint &weightedPoint : pointVector)
88  {
89  const CartesianVector &point(weightedPoint.first);
90  const double weight(weightedPoint.second);
91  const double x(static_cast<double>((point.GetX()) - meanPosition[0]));
92  const double y(static_cast<double>((point.GetY()) - meanPosition[1]));
93  const double z(static_cast<double>((point.GetZ()) - meanPosition[2]));
94 
95  xi2 += x * x * weight;
96  xiyi += x * y * weight;
97  xizi += x * z * weight;
98  yi2 += y * y * weight;
99  yizi += y * z * weight;
100  zi2 += z * z * weight;
101  }
102 
103  // Using Eigen package
104  Eigen::Matrix3f sig;
105 
106  sig << xi2, xiyi, xizi, xiyi, yi2, yizi, xizi, yizi, zi2;
107 
108  sig *= 1. / sumWeight;
109 
110  Eigen::SelfAdjointEigenSolver<Eigen::Matrix3f> eigenMat(sig);
111 
112  if (eigenMat.info() != Eigen::ComputationInfo::Success)
113  {
114  std::cout << "LArPcaHelper::RunPca - decomposition failure, nThreeDHits = " << pointVector.size() << std::endl;
115  throw StatusCodeException(STATUS_CODE_INVALID_PARAMETER);
116  }
117 
118  typedef std::pair<float, size_t> EigenValColPair;
119  typedef std::vector<EigenValColPair> EigenValColVector;
120 
121  EigenValColVector eigenValColVector;
122  const auto &resultEigenMat(eigenMat.eigenvalues());
123  eigenValColVector.emplace_back(resultEigenMat(0), 0);
124  eigenValColVector.emplace_back(resultEigenMat(1), 1);
125  eigenValColVector.emplace_back(resultEigenMat(2), 2);
126 
127  std::sort(eigenValColVector.begin(), eigenValColVector.end(),
128  [](const EigenValColPair &left, const EigenValColPair &right) { return left.first > right.first; });
129 
130  // Get the eigen values
131  outputEigenValues = CartesianVector(eigenValColVector.at(0).first, eigenValColVector.at(1).first, eigenValColVector.at(2).first);
132 
133  // Get the principal axes
134  const Eigen::Matrix3f &eigenVecs(eigenMat.eigenvectors());
135 
136  for (const EigenValColPair &pair : eigenValColVector)
137  outputEigenVectors.emplace_back(eigenVecs(0, pair.second), eigenVecs(1, pair.second), eigenVecs(2, pair.second));
138 }
139 
140 //------------------------------------------------------------------------------------------------------------------------------------------
141 
142 template void LArPcaHelper::RunPca(const CartesianPointVector &, CartesianVector &, EigenValues &, EigenVectors &);
143 template void LArPcaHelper::RunPca(const CaloHitList &, CartesianVector &, EigenValues &, EigenVectors &);
144 
145 } // namespace lar_content
pandora::CartesianVector EigenValues
Definition: LArPcaHelper.h:24
std::pair< const pandora::CartesianVector, double > WeightedPoint
Definition: LArPcaHelper.h:26
std::vector< WeightedPoint > WeightedPointVector
Definition: LArPcaHelper.h:27
Header file for the principal curve analysis helper class.
weight
Definition: test.py:257
Header file for the object helper class.
Header file for the cluster helper class.
std::vector< pandora::CartesianVector > EigenVectors
Definition: LArPcaHelper.h:25
list x
Definition: train.py:276
QTextStream & endl(QTextStream &s)