LArSoft  v09_90_00
Liquid Argon Software toolkit - https://larsoft.org/
LArGraph.cc
Go to the documentation of this file.
1 
11 
12 #include <cmath>
13 #include <limits>
14 
15 using namespace pandora;
16 
17 namespace lar_content
18 {
19 
20 LArGraph::LArGraph(const bool fullyConnect, const int nSourceEdges, const float maxSecondaryCosine, const float maxSecondaryDistance) :
21  m_fullyConnect{fullyConnect},
22  m_nSourceEdges{nSourceEdges},
23  m_maxSecondaryCosine{maxSecondaryCosine},
24  m_maxSecondaryDistance{maxSecondaryDistance}
25 {
26 }
27 
28 //------------------------------------------------------------------------------------------------------------------------------------------
29 
31 {
32  for (const Edge *pEdge : m_edges)
33  delete pEdge;
34 }
35 
36 //------------------------------------------------------------------------------------------------------------------------------------------
37 
39 {
40  return this->m_edges;
41 }
42 
43 //------------------------------------------------------------------------------------------------------------------------------------------
44 
45 void LArGraph::MakeGraph(const CaloHitList &caloHitList)
46 {
47  Eigen::MatrixXf hitMatrix(caloHitList.size(), 2);
48  this->Vectorize(caloHitList, hitMatrix);
49  // Edges can be double-counted, so use map of maps to avoid this
50  std::map<const CaloHit *, std::map<const CaloHit *, bool>> edgeMap;
51  for (int r = 0; r < hitMatrix.rows(); ++r)
52  {
53  Eigen::RowVectorXf row(2);
54  row << hitMatrix(r, 0), hitMatrix(r, 1);
55  Eigen::MatrixXf norms((hitMatrix.rowwise() - row).array().pow(2).rowwise().sum());
56  norms(r, 0) = std::numeric_limits<float>::max();
57  Eigen::Index index;
58  norms.col(0).minCoeff(&index);
59  auto iter0{caloHitList.begin()};
60  std::advance(iter0, r);
61  auto iter1{caloHitList.begin()};
62  std::advance(iter1, index);
63  edgeMap[*iter0][*iter1] = true;
64  edgeMap[*iter1][*iter0] = true;
65  norms(index, 0) = std::numeric_limits<float>::max();
66  // Create a limited number of additional edges within a maximum distance and avoiding colinearity with existing edges
67  int nEdges{1};
68  CartesianPointVector sourceEdges({((*iter1)->GetPositionVector() - (*iter0)->GetPositionVector()).GetUnitVector()});
69  for (int i = 1; i <= std::min(2 * this->m_nSourceEdges, 5 + this->m_nSourceEdges); ++i)
70  {
71  if (nEdges >= this->m_nSourceEdges)
72  break;
73  auto val{norms.col(0).minCoeff(&index)};
74  if (val > this->m_maxSecondaryDistance)
75  continue;
76  auto iter2{caloHitList.begin()};
77  std::advance(iter2, index);
78  norms(index, 0) = std::numeric_limits<float>::max();
79  const CartesianVector &vec{((*iter2)->GetPositionVector() - (*iter0)->GetPositionVector()).GetUnitVector()};
80  bool notColinear{true};
81  for (const CartesianVector &other : sourceEdges)
82  {
83  if (vec.GetDotProduct(other) > m_maxSecondaryCosine)
84  {
85  notColinear = false;
86  break;
87  }
88  }
89  if (notColinear)
90  {
91  sourceEdges.emplace_back(vec);
92  edgeMap[*iter0][*iter2] = true;
93  edgeMap[*iter2][*iter0] = true;
94  ++nEdges;
95  }
96  }
97  }
98  std::map<const CaloHit *, std::map<const CaloHit *, bool>> usedEdges;
99  for (const auto &[pCaloHit1, map] : edgeMap)
100  {
101  for (const auto &[pCaloHit2, dummy] : map)
102  {
103  if (usedEdges.find(pCaloHit1) != usedEdges.end())
104  {
105  std::map<const CaloHit *, bool> &innerMap{usedEdges[pCaloHit1]};
106  if (innerMap.find(pCaloHit2) != innerMap.end())
107  continue;
108  }
109  usedEdges[pCaloHit1][pCaloHit2] = true;
110  usedEdges[pCaloHit2][pCaloHit1] = true;
111  this->m_edges.emplace_back(new Edge(pCaloHit1, pCaloHit2));
112  }
113  }
114  if (m_fullyConnect)
115  {
116  HitEdgeMap hitToEdgesMap;
117  for (const Edge *const pEdge : this->m_edges)
118  {
119  hitToEdgesMap[pEdge->m_v0].emplace_back(pEdge);
120  hitToEdgesMap[pEdge->m_v1].emplace_back(pEdge);
121  }
122  HitConnectionsMap graphs;
123  this->IdentifyDisconnectedRegions(hitToEdgesMap, graphs);
124  while (graphs.size() > 1)
125  {
126  this->ConnectRegions(graphs, hitToEdgesMap);
127  graphs.clear();
128  this->IdentifyDisconnectedRegions(hitToEdgesMap, graphs);
129  }
130  }
131 }
132 
133 //------------------------------------------------------------------------------------------------------------------------------------------
134 
135 void LArGraph::Walk(const CaloHit *const pRootCaloHit, const HitEdgeMap &hitToEdgesMap, CaloHitList &graph, HitUseMap &connectedHits) const
136 {
137  if (connectedHits.find(pRootCaloHit) != connectedHits.end())
138  return;
139  graph.emplace_back(pRootCaloHit);
140  connectedHits[pRootCaloHit] = true;
141  const EdgeVector &assocEdges{hitToEdgesMap.at(pRootCaloHit)};
142  for (const Edge *const edge : assocEdges)
143  {
144  if (pRootCaloHit == edge->m_v0)
145  this->Walk(edge->m_v1, hitToEdgesMap, graph, connectedHits);
146  else
147  this->Walk(edge->m_v0, hitToEdgesMap, graph, connectedHits);
148  }
149 }
150 
151 //------------------------------------------------------------------------------------------------------------------------------------------
152 
153 void LArGraph::IdentifyDisconnectedRegions(const HitEdgeMap &hitToEdgesMap, HitConnectionsMap &graphs) const
154 {
155  HitUseMap connectedHits;
156  for (const auto &[pCaloHit, assocEdges] : hitToEdgesMap)
157  {
158  if (connectedHits.find(pCaloHit) != connectedHits.end())
159  continue;
160  graphs[pCaloHit] = CaloHitList{pCaloHit};
161  connectedHits[pCaloHit] = true;
162  for (const Edge *const edge : assocEdges)
163  {
164  if (pCaloHit == edge->m_v0)
165  this->Walk(edge->m_v1, hitToEdgesMap, graphs[pCaloHit], connectedHits);
166  else
167  this->Walk(edge->m_v0, hitToEdgesMap, graphs[pCaloHit], connectedHits);
168  }
169  }
170 }
171 
172 //------------------------------------------------------------------------------------------------------------------------------------------
173 
174 void LArGraph::ConnectRegions(const HitConnectionsMap &graphs, HitEdgeMap &hitToEdgesMap)
175 {
176  std::map<int, std::vector<int>> connectedGraphMap;
177  int i{0}, idx1{0}, idx2{0};
178  for (const auto &[pCaloHit1, caloHitList1] : graphs)
179  {
180  ++i;
181  Eigen::MatrixXf subGraphMatrix(caloHitList1.size(), 2);
182  this->Vectorize(caloHitList1, subGraphMatrix);
183  float closestApproach{std::numeric_limits<float>::max()};
184  const CaloHit *pClosestHit1{nullptr};
185  const CaloHit *pClosestHit2{nullptr};
186  int j{0};
187  for (const auto &[pCaloHit2, caloHitList2] : graphs)
188  {
189  ++j;
190  auto begin{connectedGraphMap[i].begin()}, end{connectedGraphMap[i].end()};
191  if (pCaloHit1 == pCaloHit2 || std::find(begin, end, j) != end)
192  continue;
193  for (const CaloHit *const pCaloHit : caloHitList2)
194  {
195  const CartesianVector &pos{pCaloHit->GetPositionVector()};
196  Eigen::RowVectorXf row(2);
197  row << pos.GetX(), pos.GetZ();
198  Eigen::MatrixXf norms((subGraphMatrix.rowwise() - row).array().pow(2).rowwise().sum());
199  Eigen::Index index;
200  float val{norms.col(0).minCoeff(&index)};
201  if (val < closestApproach)
202  {
203  auto iter{caloHitList1.begin()};
204  std::advance(iter, index);
205  pClosestHit1 = *iter;
206  pClosestHit2 = pCaloHit;
207  closestApproach = val;
208  idx1 = i;
209  idx2 = j;
210  }
211  }
212  }
213  if (pClosestHit1 && pClosestHit2)
214  {
215  const Edge *pEdge{new Edge(pClosestHit1, pClosestHit2)};
216  this->m_edges.emplace_back(pEdge);
217  hitToEdgesMap[pEdge->m_v0].emplace_back(pEdge);
218  hitToEdgesMap[pEdge->m_v1].emplace_back(pEdge);
219  connectedGraphMap[idx1].emplace_back(idx2);
220  connectedGraphMap[idx2].emplace_back(idx1);
221  }
222  }
223 }
224 
225 //------------------------------------------------------------------------------------------------------------------------------------------
226 
227 void LArGraph::Vectorize(const CaloHitList &caloHitList, Eigen::MatrixXf &hitMatrix) const
228 {
229  int i{0};
230  for (const CaloHit *const pCaloHit : caloHitList)
231  {
232  const CartesianVector &pos{pCaloHit->GetPositionVector()};
233  hitMatrix(i, 0) = pos.GetX();
234  hitMatrix(i, 1) = pos.GetZ();
235  ++i;
236  }
237 }
238 
239 //------------------------------------------------------------------------------------------------------------------------------------------
240 //------------------------------------------------------------------------------------------------------------------------------------------
241 
242 LArGraph::Edge::Edge(const CaloHit *const pVertex0, const CaloHit *const pVertex1) :
243  m_v0{pVertex0},
244  m_v1{pVertex1}
245 {
246 }
247 
248 //------------------------------------------------------------------------------------------------------------------------------------------
249 
251 {
252  const CartesianVector &pos0{this->m_v0->GetPositionVector()}, &pos1{this->m_v1->GetPositionVector()};
253  const float dx{pos0.GetX() - pos1.GetX()}, dz{pos0.GetZ() - pos1.GetZ()};
254 
255  return dx * dx + dz * dz;
256 }
257 
258 } // namespace lar_content
int m_nSourceEdges
The number of edges to consider emerging from a source.
Definition: LArGraph.h:131
TRandom r
Definition: spectrum.C:23
const EdgeVector & GetEdges() const
Retrieve the edges for this graph.
Definition: LArGraph.cc:38
const pandora::CaloHit *const m_v1
An endpoint of the edge.
Definition: LArGraph.h:55
~LArGraph()
Destructor.
Definition: LArGraph.cc:30
std::map< const pandora::CaloHit *, bool > HitUseMap
Definition: LArGraph.h:62
Header file for the lar calo hit class.
float m_maxSecondaryCosine
The number of edges to consider emerging from a source.
Definition: LArGraph.h:132
boost::graph_traits< ModuleGraph >::edge_descriptor Edge
Definition: ModuleGraph.h:24
decltype(auto) constexpr end(T &&obj)
ADL-aware version of std::end.
Definition: StdUtils.h:77
std::vector< const Edge * > EdgeVector
Definition: LArGraph.h:57
void MakeGraph(const pandora::CaloHitList &caloHitList)
Construct a graph from a list of calo hits.
Definition: LArGraph.cc:45
auto array(Array const &a)
Returns a manipulator which will print the specified array.
Definition: DumpUtils.h:250
void ConnectRegions(const HitConnectionsMap &graphs, HitEdgeMap &hitToEdgesMap)
Identify the different disconnected sub graphs in a set of eges.
Definition: LArGraph.cc:174
float m_maxSecondaryDistance
The number of edges to consider emerging from a source.
Definition: LArGraph.h:133
Edge(const pandora::CaloHit *const pVertex0, const pandora::CaloHit *const pVertex1)
Constructor.
Definition: LArGraph.cc:242
EdgeVector m_edges
The edges defining the graph.
Definition: LArGraph.h:129
std::map< const pandora::CaloHit *, pandora::CaloHitList > HitConnectionsMap
Definition: LArGraph.h:61
const pandora::CaloHit *const m_v0
An endpoint of the edge.
Definition: LArGraph.h:52
void Walk(const pandora::CaloHit *const pCaloHit, const HitEdgeMap &hitToEdgesMap, pandora::CaloHitList &graph, HitUseMap &connectedHits) const
Walk along node edges to define a connected graph.
Definition: LArGraph.cc:135
void IdentifyDisconnectedRegions(const HitEdgeMap &hitToEdgesMap, HitConnectionsMap &graphs) const
Identify the different disconnected sub graphs in a set of eges.
Definition: LArGraph.cc:153
decltype(auto) constexpr begin(T &&obj)
ADL-aware version of std::begin.
Definition: StdUtils.h:69
float LengthSquared() const
Compute the square of the length of this edge.
Definition: LArGraph.cc:250
void Vectorize(const pandora::CaloHitList &caloHitList, Eigen::MatrixXf &hitMatrix) const
Convert a list of calo hits into an Eigen matrix.
Definition: LArGraph.cc:227
std::map< const pandora::CaloHit *, EdgeVector > HitEdgeMap
Definition: LArGraph.h:60
bool m_fullyConnect
Whether or not to connect any disconnected regions.
Definition: LArGraph.h:130