Eclipse SUMO - Simulation of Urban MObility
CHBuilder.h
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3 // Copyright (C) 2001-2024 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials are made available under the
5 // terms of the Eclipse Public License 2.0 which is available at
6 // https://www.eclipse.org/legal/epl-2.0/
7 // This Source Code may also be made available under the following Secondary
8 // Licenses when the conditions for such availability set forth in the Eclipse
9 // Public License 2.0 are satisfied: GNU General Public License, version 2
10 // or later which is available at
11 // https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
12 // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
13 /****************************************************************************/
20 // Contraction Hierarchy Builder for the shortest path search
21 /****************************************************************************/
22 #pragma once
23 #include <config.h>
24 
25 #include <string>
26 #include <functional>
27 #include <vector>
28 #include <set>
29 #include <limits>
30 #include <algorithm>
31 #include <iterator>
32 #include <utils/common/SysUtils.h>
34 #include <utils/common/StdDefs.h>
36 #include "SPTree.h"
37 
38 //#define CHRouter_DEBUG_CONTRACTION
39 //#define CHRouter_DEBUG_CONTRACTION_WITNESSES
40 //#define CHRouter_DEBUG_CONTRACTION_QUEUE
41 //#define CHRouter_DEBUG_CONTRACTION_DEGREE
42 //#define CHRouter_DEBUG_WEIGHTS
43 
44 // ===========================================================================
45 // class definitions
46 // ===========================================================================
61 template<class E, class V>
62 class CHBuilder {
63 
64 public:
66  // forward connections are used only in forward search
67  // backward connections are used only in backwards search
68  class Connection {
69  public:
70  Connection(int t, double c, SVCPermissions p): target(t), cost(c), permissions(p) {}
71  int target;
72  double cost;
74  };
75 
76  typedef std::pair<const E*, const E*> ConstEdgePair;
77  typedef std::map<ConstEdgePair, const E*> ShortcutVia;
78  struct Hierarchy {
80  std::vector<std::vector<Connection> > forwardUplinks;
81  std::vector<std::vector<Connection> > backwardUplinks;
82  };
83 
89  CHBuilder(const std::vector<E*>& edges, bool unbuildIsWarning,
90  const SUMOVehicleClass svc,
91  bool validatePermissions):
92  myEdges(edges),
93  myErrorMsgHandler(unbuildIsWarning ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
94  mySPTree(new SPTree<CHInfo, CHConnection>(4, validatePermissions)),
95  mySVC(svc),
96  myUpdateCount(0) {
97  for (const E* const e : edges) {
98  myCHInfos.push_back(CHInfo(e));
99  }
100  }
101 
103  virtual ~CHBuilder() {
104  delete mySPTree;
105  }
106 
107 
108  Hierarchy* buildContractionHierarchy(SUMOTime time, const V* const vehicle, const SUMOAbstractRouter<E, V>* effortProvider) {
109  Hierarchy* result = new Hierarchy();
110  const int numEdges = (int)myCHInfos.size();
111  const std::string vClass = (mySPTree->validatePermissions() ?
112  "all vehicle classes " : "vClass='" + SumoVehicleClassStrings.getString(mySVC) + "' ");
113  PROGRESS_BEGIN_MESSAGE("Building Contraction Hierarchy for " + vClass
114  + "and time=" + time2string(time) + " (" + toString(numEdges) + " edges)\n");
115  const long startMillis = SysUtils::getCurrentMillis();
116  // init queue
117  std::vector<CHInfo*> queue; // max heap: edge to be contracted is front
118  // reset previous connections etc
119  for (int i = 0; i < numEdges; i++) {
120  myCHInfos[i].resetContractionState();
121  result->forwardUplinks.push_back(std::vector<Connection>());
122  result->backwardUplinks.push_back(std::vector<Connection>());
123  }
124  // copy connections from the original net
125  const double time_seconds = STEPS2TIME(time); // timelines store seconds!
126  for (int i = 0; i < numEdges; i++) {
127  synchronize(myCHInfos[i], time_seconds, vehicle, effortProvider);
128  }
129  // synchronization is finished. now we can compute priorities for the first time
130  for (int i = 0; i < numEdges; i++) {
131  myCHInfos[i].updatePriority(mySPTree);
132  queue.push_back(&(myCHInfos[i]));
133  }
134  std::make_heap(queue.begin(), queue.end(), myCmp);
135  int contractionRank = 0;
136  // contraction loop
137  while (!queue.empty()) {
138  while (tryUpdateFront(queue)) {}
139  CHInfo* max = queue.front();
140  max->rank = contractionRank;
141 #ifdef CHRouter_DEBUG_CONTRACTION
142  std::cout << "contracting '" << max->edge->getID() << "' with prio: " << max->priority << " (rank " << contractionRank << ")\n";
143 #endif
144  const E* const edge = max->edge;
145  // add outgoing connections to the forward search
146  const int edgeID = edge->getNumericalID();
147  for (const CHConnection& con : max->followers) {
148  result->forwardUplinks[edgeID].push_back(Connection(con.target->edge->getNumericalID(), con.cost, con.permissions));
149  disconnect(con.target->approaching, max);
150  con.target->updatePriority(0);
151  }
152  // add incoming connections to the backward search
153  for (const CHConnection& con : max->approaching) {
154  result->backwardUplinks[edgeID].push_back(Connection(con.target->edge->getNumericalID(), con.cost, con.permissions));
155  disconnect(con.target->followers, max);
156  con.target->updatePriority(0);
157  }
158  // add shortcuts to the net
159  for (const Shortcut& s : max->shortcuts) {
160  const ConstEdgePair& edgePair = s.edgePair;
161  result->shortcuts[edgePair] = edge;
162  CHInfo* from = getCHInfo(edgePair.first);
163  CHInfo* to = getCHInfo(edgePair.second);
164  from->followers.push_back(CHConnection(to, s.cost, s.permissions, s.underlying));
165  to->approaching.push_back(CHConnection(from, s.cost, s.permissions, s.underlying));
166  }
167  // if you need to debug the chrouter with MSVC uncomment the following line, hierarchy building will get slower and the hierarchy may change though
168  //std::make_heap(queue.begin(), queue.end(), myCmp);
169  // remove from queue
170  std::pop_heap(queue.begin(), queue.end(), myCmp);
171  queue.pop_back();
172  /*
173  if (contractionRank % 10000 == 0) {
174  // update all and rebuild queue
175  for (typename std::vector<CHInfo*>::iterator it = queue.begin(); it != queue.end(); ++it) {
176  (*it)->updatePriority(mySPTree);
177  }
178  std::make_heap(queue.begin(), queue.end(), myCmp);
179  }
180  */
181  contractionRank++;
182  }
183  // reporting
184  const long duration = SysUtils::getCurrentMillis() - startMillis;
185  WRITE_MESSAGE("Created " + toString(result->shortcuts.size()) + " shortcuts.");
186  WRITE_MESSAGE("Recomputed priority " + toString(myUpdateCount) + " times.");
187  MsgHandler::getMessageInstance()->endProcessMsg("done (" + toString(duration) + "ms).");
189  myUpdateCount = 0;
190  return result;
191  }
192 
193 private:
194  struct Shortcut {
195  Shortcut(ConstEdgePair e, double c, int u, SVCPermissions p):
196  edgePair(e), cost(c), underlying(u), permissions(p) {}
198  double cost;
201  };
202 
203 
204  class CHInfo;
205 
207  class CHConnection {
208  public:
209  CHConnection(CHInfo* t, double c, SVCPermissions p, int u):
210  target(t), cost(c), permissions(p), underlying(u) {}
212  double cost;
216  };
217 
218  typedef std::vector<CHConnection> CHConnections;
219  typedef std::pair<const CHConnection*, const CHConnection*> CHConnectionPair;
220  typedef std::vector<CHConnectionPair> CHConnectionPairs;
221 
222  /* @brief container class to use when building the contraction hierarchy.
223  * instances are reused every time the hierarchy is rebuilt (new time slice)
224  * but they must be synchronized first */
225  class CHInfo {
226  public:
228  CHInfo(const E* const e) :
229  edge(e),
230  priority(0.),
232  rank(-1),
233  level(0),
234  underlyingTotal(0),
235  visited(false),
236  traveltime(std::numeric_limits<double>::max()),
237  depth(0),
239  }
240 
243  if (spTree != nullptr) {
244  updateShortcuts(spTree);
245  updateLevel();
246  } else {
247  contractedNeighbors += 1; // called when a connected edge was contracted
248  }
249  const double oldPriority = priority;
250  // priority term as used by abraham []
251  const int edge_difference = (int)followers.size() + (int)approaching.size() - 2 * (int)shortcuts.size();
252  priority = (double)(2 * edge_difference - contractedNeighbors - underlyingTotal - 5 * level);
253  return priority != oldPriority;
254  }
255 
258  const bool validatePermissions = spTree->validatePermissions();
259 #ifdef CHRouter_DEBUG_CONTRACTION_DEGREE
260  const int degree = (int)approaching.size() + (int)followers.size();
261  std::cout << "computing shortcuts for '" + edge->getID() + "' with degree " + toString(degree) + "\n";
262 #endif
263  shortcuts.clear();
264  underlyingTotal = 0;
265  for (const CHConnection& aInfo : approaching) {
266  // build shortest path tree in a fixed neighborhood
267  spTree->rebuildFrom(aInfo.target, this);
268  for (const CHConnection& fInfo : followers) {
269  const double viaCost = aInfo.cost + fInfo.cost;
270  const SVCPermissions viaPermissions = (aInfo.permissions & fInfo.permissions);
271  if (fInfo.target->traveltime > viaCost) {
272  // found no faster path -> we need a shortcut via edge
273 #ifdef CHRouter_DEBUG_CONTRACTION_WITNESSES
274  debugNoWitness(aInfo, fInfo);
275 #endif
276  const int underlying = aInfo.underlying + fInfo.underlying;
277  underlyingTotal += underlying;
278  shortcuts.push_back(Shortcut(ConstEdgePair(aInfo.target->edge, fInfo.target->edge),
279  viaCost, underlying, viaPermissions));
280 
281  } else if (validatePermissions) {
282  if ((fInfo.target->permissions & viaPermissions) != viaPermissions) {
283  // witness has weaker restrictions. try to find another witness
284  spTree->registerForValidation(&aInfo, &fInfo);
285  } else {
286 #ifdef CHRouter_DEBUG_CONTRACTION_WITNESSES
287  debugNoWitness(aInfo, fInfo);
288 #endif
289  }
290  } else {
291 #ifdef CHRouter_DEBUG_CONTRACTION_WITNESSES
292  debugNoWitness(aInfo, fInfo);
293 #endif
294  }
295  }
296  }
297  // insert shortcuts needed due to unmet permissions
298  if (validatePermissions) {
299  for (const CHConnectionPair& chcp : spTree->getNeededShortcuts(this)) {
300  const CHConnection* aInfo = chcp.first;
301  const CHConnection* fInfo = chcp.second;
302  const double viaCost = aInfo->cost + fInfo->cost;
303  const SVCPermissions viaPermissions = (aInfo->permissions & fInfo->permissions);
304  const int underlying = aInfo->underlying + fInfo->underlying;
305  underlyingTotal += underlying;
306  shortcuts.push_back(Shortcut(ConstEdgePair(aInfo->target->edge, fInfo->target->edge),
307  viaCost, underlying, viaPermissions));
308  }
309  }
310  }
311 
312 
313  // update level as defined by Abraham
314  void updateLevel() {
315  int maxLower = std::numeric_limits<int>::min();
316  for (const CHConnection& con : approaching) {
317  if (con.target->rank < rank) {
318  maxLower = MAX2(rank, maxLower);
319  }
320  }
321  for (const CHConnection& con : followers) {
322  if (con.target->rank < rank) {
323  maxLower = MAX2(rank, maxLower);
324  }
325  }
326  if (maxLower == std::numeric_limits<int>::min()) {
327  level = 0;
328  } else {
329  level = maxLower + 1;
330  }
331  }
332 
333  // resets state before rebuilding the hierarchy
335  priority = 0.;
336  shortcuts.clear();
338  rank = -1;
339  level = 0;
340  underlyingTotal = 0;
341  followers.clear();
342  approaching.clear();
343  reset(); // just to make sure
344  }
345 
346 
348  const E* const edge;
350  double priority;
352  std::vector<Shortcut> shortcuts;
355  int rank;
356  int level;
358 
362 
365 
367  bool visited;
369  double traveltime;
371  int depth;
373  // @note: we may miss some witness paths by making traveltime the only
374  // criteria durinng search
376 
377  inline void reset() {
378  traveltime = std::numeric_limits<double>::max();
379  visited = false;
380  depth = 0;
382  }
383 
385 
387  inline void debugNoWitness(const CHConnection& aInfo, const CHConnection& fInfo) {
388  std::cout << "adding shortcut between " << aInfo.target->edge->getID() << ", " << fInfo.target->edge->getID() << " via " << edge->getID() << "\n";
389  }
390 
391  inline void debugWitness(const CHConnection& aInfo, const CHConnection& fInfo) {
392  const double viaCost = aInfo.cost + fInfo.cost;
393  std::cout << "found witness with length " << fInfo.target->traveltime << " against via " << edge->getID() << " (length " << viaCost << ") for " << aInfo.target->edge->getID() << ", " << fInfo.target->edge->getID() << "\n";
394  }
395 
396  };
397 
398 
404  public:
406  bool operator()(const CHInfo* a, const CHInfo* b) const {
407  if (a->priority == b->priority) {
408  return a->edge->getNumericalID() > b->edge->getNumericalID();
409  } else {
410  return a->priority < b->priority;
411  };
412  }
413  };
414 
415 
416  inline CHInfo* getCHInfo(const E* const edge) {
417  return &(myCHInfos[edge->getNumericalID()]);
418  }
419 
420 
422  void synchronize(CHInfo& info, double time, const V* const vehicle, const SUMOAbstractRouter<E, V>* effortProvider) {
423  // forward and backward connections are used only in forward search,
424  // thus approaching costs are those of the approaching edge and not of the edge itself
425  const bool prune = !mySPTree->validatePermissions();
426  const E* const edge = info.edge;
427  if (prune && ((edge->getPermissions() & mySVC) != mySVC)) {
428  return;
429  }
430  const double baseCost = effortProvider->getEffort(edge, vehicle, time);
431 
432  for (const std::pair<const E*, const E*>& successor : edge->getViaSuccessors(mySVC)) {
433  const E* fEdge = successor.first;
434  if (prune && ((fEdge->getPermissions() & mySVC) != mySVC)) {
435  continue;
436  }
437  CHInfo* const follower = getCHInfo(fEdge);
438  const SVCPermissions permissions = (edge->getPermissions() & fEdge->getPermissions());
439  double cost = baseCost;
440  const E* viaEdge = successor.second;
441  while (viaEdge != nullptr && viaEdge->isInternal()) {
442  cost += effortProvider->getEffort(viaEdge, vehicle, time);
443  viaEdge = viaEdge->getViaSuccessors().front().first;
444  }
445  info.followers.push_back(CHConnection(follower, cost, permissions, 1));
446  follower->approaching.push_back(CHConnection(&info, cost, permissions, 1));
447  }
448 #ifdef CHRouter_DEBUG_WEIGHTS
449  std::cout << time << ": " << edge->getID() << " baseCost: " << baseCost << "\n";
450 #endif
451  // @todo: check whether we even need to save approaching in ROEdge;
452  }
453 
454 
456  void disconnect(CHConnections& connections, CHInfo* other) {
457  for (typename CHConnections::iterator it = connections.begin(); it != connections.end(); it++) {
458  if (it->target == other) {
459  connections.erase(it);
460  return;
461  }
462  }
463  assert(false);
464  }
465 
469  bool tryUpdateFront(std::vector<CHInfo*>& queue) {
470  myUpdateCount++;
471  CHInfo* max = queue.front();
472 #ifdef CHRouter_DEBUG_CONTRACTION_QUEUE
473  std::cout << "updating '" << max->edge->getID() << "'\n";
474  debugPrintQueue(queue);
475 #endif
476  if (max->updatePriority(mySPTree)) {
477  std::pop_heap(queue.begin(), queue.end(), myCmp);
478  std::push_heap(queue.begin(), queue.end(), myCmp);
479  return true;
480  } else {
481  return false;
482  }
483  }
484 
485 #ifdef CHRouter_DEBUG_CONTRACTION_QUEUE
486  // helper method for debugging
487  void debugPrintQueue(std::vector<CHInfo*>& queue) {
488  for (const CHInfo* const chInfo : queue) {
489  std::cout << "(" << chInfo->edge->getID() << "," << chInfo->priority << ") ";
490  }
491  std::cout << "\n";
492  }
493 #endif
494 
495 private:
497  const std::vector<E*>& myEdges;
498 
501 
503  std::vector<CHInfo> myCHInfos;
504 
507 
510 
513 
516 
517 private:
520 };
long long int SUMOTime
Definition: GUI.h:35
#define WRITE_MESSAGE(msg)
Definition: MsgHandler.h:297
#define PROGRESS_DONE_MESSAGE()
Definition: MsgHandler.h:300
#define PROGRESS_BEGIN_MESSAGE(msg)
Definition: MsgHandler.h:299
std::string time2string(SUMOTime t, bool humanReadable)
convert SUMOTime to string (independently of global format setting)
Definition: SUMOTime.cpp:69
#define STEPS2TIME(x)
Definition: SUMOTime.h:55
StringBijection< SUMOVehicleClass > SumoVehicleClassStrings(sumoVehicleClassStringInitializer, SVC_CUSTOM2, false)
long long int SVCPermissions
bitset where each bit declares whether a certain SVC may use this edge/lane
SUMOVehicleClass
Definition of vehicle classes to differ between different lane usage and authority types.
@ SVC_IGNORING
vehicles ignoring classes
T MAX2(T a, T b)
Definition: StdDefs.h:82
std::string toString(const T &t, std::streamsize accuracy=gPrecision)
Definition: ToString.h:46
Forward/backward connection with associated FORWARD cost.
Definition: CHBuilder.h:207
int underlying
the number of connections underlying this connection
Definition: CHBuilder.h:215
SVCPermissions permissions
Definition: CHBuilder.h:213
CHConnection(CHInfo *t, double c, SVCPermissions p, int u)
Definition: CHBuilder.h:209
bool operator()(const CHInfo *a, const CHInfo *b) const
Comparing method.
Definition: CHBuilder.h:406
void updateShortcuts(SPTree< CHInfo, CHConnection > *spTree)
compute needed shortcuts when contracting this edge
Definition: CHBuilder.h:257
void resetContractionState()
Definition: CHBuilder.h:334
double traveltime
Effort to reach the edge.
Definition: CHBuilder.h:369
void debugWitness(const CHConnection &aInfo, const CHConnection &fInfo)
Definition: CHBuilder.h:391
CHInfo(const E *const e)
Constructor.
Definition: CHBuilder.h:228
int contractedNeighbors
priority subterms
Definition: CHBuilder.h:354
bool visited
whether the edge has been visited during shortest path search
Definition: CHBuilder.h:367
double priority
The contraction priority.
Definition: CHBuilder.h:350
const E *const edge
The current edge.
Definition: CHBuilder.h:348
bool updatePriority(SPTree< CHInfo, CHConnection > *spTree)
recompute the contraction priority and report whether it changed
Definition: CHBuilder.h:242
SVCPermissions permissions
the permissions when reaching this edge on the fastest path
Definition: CHBuilder.h:375
std::vector< Shortcut > shortcuts
The needed shortcuts.
Definition: CHBuilder.h:352
int depth
number of edges from start
Definition: CHBuilder.h:371
CHConnections followers
connections (only valid after synchronization)
Definition: CHBuilder.h:360
void updateLevel()
Definition: CHBuilder.h:314
CHConnections approaching
Definition: CHBuilder.h:361
void debugNoWitness(const CHConnection &aInfo, const CHConnection &fInfo)
debugging methods
Definition: CHBuilder.h:387
Forward/backward connection with associated forward/backward cost.
Definition: CHBuilder.h:68
SVCPermissions permissions
Definition: CHBuilder.h:73
Connection(int t, double c, SVCPermissions p)
Definition: CHBuilder.h:70
CHInfo * getCHInfo(const E *const edge)
Definition: CHBuilder.h:416
CHBuilder & operator=(const CHBuilder &s)
Invalidated assignment operator.
bool tryUpdateFront(std::vector< CHInfo * > &queue)
tries to update the priority of the first edge
Definition: CHBuilder.h:469
virtual ~CHBuilder()
Destructor.
Definition: CHBuilder.h:103
std::pair< const CHConnection *, const CHConnection * > CHConnectionPair
Definition: CHBuilder.h:219
std::map< ConstEdgePair, const E * > ShortcutVia
Definition: CHBuilder.h:77
SPTree< CHInfo, CHConnection > * mySPTree
the shortest path tree to use when searching for shortcuts
Definition: CHBuilder.h:509
CHInfoComparator myCmp
Comparator for contraction priority.
Definition: CHBuilder.h:506
Hierarchy * buildContractionHierarchy(SUMOTime time, const V *const vehicle, const SUMOAbstractRouter< E, V > *effortProvider)
Definition: CHBuilder.h:108
const std::vector< E * > & myEdges
all edges with numerical ids
Definition: CHBuilder.h:497
std::vector< CHConnection > CHConnections
Definition: CHBuilder.h:218
std::pair< const E *, const E * > ConstEdgePair
Definition: CHBuilder.h:76
CHBuilder(const std::vector< E * > &edges, bool unbuildIsWarning, const SUMOVehicleClass svc, bool validatePermissions)
Constructor.
Definition: CHBuilder.h:89
std::vector< CHConnectionPair > CHConnectionPairs
Definition: CHBuilder.h:220
std::vector< CHInfo > myCHInfos
static vector for lookup
Definition: CHBuilder.h:503
void synchronize(CHInfo &info, double time, const V *const vehicle, const SUMOAbstractRouter< E, V > *effortProvider)
copy connections from the original net (modified destructively during contraction)
Definition: CHBuilder.h:422
const SUMOVehicleClass mySVC
the permissions for which the hierarchy was constructed
Definition: CHBuilder.h:512
void disconnect(CHConnections &connections, CHInfo *other)
remove all connections to/from the given edge (assume it exists only once)
Definition: CHBuilder.h:456
MsgHandler *const myErrorMsgHandler
the handler for routing errors
Definition: CHBuilder.h:500
int myUpdateCount
counters for performance logging
Definition: CHBuilder.h:515
virtual void endProcessMsg(std::string msg)
Ends a process information.
Definition: MsgHandler.cpp:201
static MsgHandler * getMessageInstance()
Returns the instance to add normal messages to.
Definition: MsgHandler.cpp:66
Definition: SPTree.h:36
void rebuildFrom(E *start, const E *excluded)
build a shortest path tree from start to a depth of myMaxdepth. The given edge is excluded from this ...
Definition: SPTree.h:85
bool validatePermissions()
whether permissions should be validated;
Definition: SPTree.h:127
const CHConnectionPairs & getNeededShortcuts(const E *excluded)
Definition: SPTree.h:140
void registerForValidation(const C *aInfo, const C *fInfo)
save source/target pair for later validation
Definition: SPTree.h:132
double getEffort(const E *const e, const V *const v, double t) const
static long getCurrentMillis()
Returns the current time in milliseconds.
Definition: SysUtils.cpp:44
Definition: json.hpp:4471
std::vector< std::vector< Connection > > backwardUplinks
Definition: CHBuilder.h:81
std::vector< std::vector< Connection > > forwardUplinks
Definition: CHBuilder.h:80
ShortcutVia shortcuts
Definition: CHBuilder.h:79
SVCPermissions permissions
Definition: CHBuilder.h:200
Shortcut(ConstEdgePair e, double c, int u, SVCPermissions p)
Definition: CHBuilder.h:195
ConstEdgePair edgePair
Definition: CHBuilder.h:197