SUMO - Simulation of Urban MObility
AStarLookupTable.h
Go to the documentation of this file.
1 /****************************************************************************/
2 // Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.org/sumo
3 // Copyright (C) 2012-2018 German Aerospace Center (DLR) and others.
4 // This program and the accompanying materials
5 // are made available under the terms of the Eclipse Public License v2.0
6 // which accompanies this distribution, and is available at
7 // http://www.eclipse.org/legal/epl-v20.html
8 // SPDX-License-Identifier: EPL-2.0
9 /****************************************************************************/
15 // Precomputed landmark distances to speed up the A* routing algorithm
16 /****************************************************************************/
17 #ifndef AStarLookupTable_h
18 #define AStarLookupTable_h
19 
20 
21 // ===========================================================================
22 // included modules
23 // ===========================================================================
24 #include <config.h>
25 
26 #include <iostream>
27 #include <fstream>
28 
29 #ifdef HAVE_FOX
31 #endif
32 
33 #define UNREACHABLE (std::numeric_limits<double>::max() / 1000.0)
34 
35 //#define ASTAR_DEBUG_LOOKUPTABLE
36 //#define ASTAR_DEBUG_LOOKUPTABLE_FROM "disabled"
37 //#define ASTAR_DEBUG_UNREACHABLE
38 
39 // ===========================================================================
40 // class definitions
41 // ===========================================================================
58 template<class E, class V>
60 public:
62  virtual double lowerBound(const E* from, const E* to, double speed, double speedFactor, double fromEffort, double toEffort) const = 0;
63 
65  virtual bool consistent() const = 0;
66 };
67 
68 
69 template<class E, class V>
70 class FullLookupTable : public AbstractLookupTable<E, V> {
71 public:
72  FullLookupTable(const std::string& filename, const int size) :
73  myTable(size) {
74  BinaryInputDevice dev(filename);
75  for (int i = 0; i < size; i++) {
76  for (int j = 0; j < size; j++) {
77  double val;
78  dev >> val;
79  myTable[i].push_back(val);
80  }
81  }
82  }
83 
84  double lowerBound(const E* from, const E* to, double /*speed*/, double speedFactor, double /*fromEffort*/, double /*toEffort*/) const {
85  return myTable[from->getNumericalID()][to->getNumericalID()] / speedFactor;
86  }
87 
88  bool consistent() const {
89  return true;
90  }
91 
92 private:
93  std::vector<std::vector<double> > myTable;
94 };
95 
96 
97 template<class E, class V>
99 public:
100  LandmarkLookupTable(const std::string& filename, const std::vector<E*>& edges, SUMOAbstractRouter<E, V>* router, const V* defaultVehicle, const std::string& outfile, const int maxNumThreads) {
101  myFirstNonInternal = -1;
102  std::map<std::string, int> numericID;
103  for (E* e : edges) {
104  if (!e->isInternal()) {
105  if (myFirstNonInternal == -1) {
106  myFirstNonInternal = e->getNumericalID();
107  }
108  numericID[e->getID()] = e->getNumericalID() - myFirstNonInternal;
109  }
110  }
111  std::ifstream strm(filename.c_str());
112  if (!strm.good()) {
113  throw ProcessError("Could not load landmark-lookup-table from '" + filename + "'.");
114  }
115  std::ofstream* ostrm = 0;
116  if (!outfile.empty()) {
117  ostrm = new std::ofstream(outfile.c_str());
118  if (!ostrm->good()) {
119  throw ProcessError("Could not open file '" + outfile + "' for writing.");
120  }
121  }
122  std::string line;
123  int numLandMarks = 0;
124  while (std::getline(strm, line)) {
125  if (line == "") {
126  break;
127  }
128  //std::cout << "'" << line << "'" << "\n";
129  StringTokenizer st(line);
130  if (st.size() == 1) {
131  const std::string lm = st.get(0);
132  myLandmarks[lm] = numLandMarks++;
133  myFromLandmarkDists.push_back(std::vector<double>(0));
134  myToLandmarkDists.push_back(std::vector<double>(0));
135  if (ostrm != 0) {
136  (*ostrm) << lm << "\n";
137  }
138  } else {
139  assert(st.size() == 4);
140  const std::string lm = st.get(0);
141  const std::string edge = st.get(1);
142  if (numericID[edge] != (int)myFromLandmarkDists[myLandmarks[lm]].size()) {
143  WRITE_WARNING("Unknown or unordered edge '" + edge + "' in landmark file.");
144  }
145  const double distFrom = TplConvert::_2double(st.get(2).c_str());
146  const double distTo = TplConvert::_2double(st.get(3).c_str());
147  myFromLandmarkDists[myLandmarks[lm]].push_back(distFrom);
148  myToLandmarkDists[myLandmarks[lm]].push_back(distTo);
149  }
150  }
151  if (myLandmarks.empty()) {
152  WRITE_WARNING("No landmarks in '" + filename + "', falling back to standard A*.");
153  delete ostrm;
154  return;
155  }
156 #ifdef HAVE_FOX
157  FXWorkerThread::Pool threadPool;
158 #endif
159  for (int i = 0; i < (int)myLandmarks.size(); ++i) {
160  if ((int)myFromLandmarkDists[i].size() != (int)edges.size() - myFirstNonInternal) {
161  const std::string landmarkID = getLandmark(i);
162  const E* landmark = 0;
163  // retrieve landmark edge
164  for (typename std::vector<E*>::const_iterator it_e = edges.begin(); it_e != edges.end(); ++it_e) {
165  if ((*it_e)->getID() == landmarkID) {
166  landmark = *it_e;
167  }
168  }
169  if (landmark == 0) {
170  WRITE_WARNING("Landmark '" + landmarkID + "' does not exist in the network.");
171  continue;
172  }
173  if (router != 0) {
174  const std::string missing = outfile.empty() ? filename + ".missing" : outfile;
175  WRITE_WARNING("Not all network edges were found in the lookup table '" + filename + "' for landmark '" + landmarkID + "'. Saving missing values to '" + missing + "'.");
176  if (ostrm == 0) {
177  ostrm = new std::ofstream(missing.c_str());
178  if (!ostrm->good()) {
179  throw ProcessError("Could not open file '" + missing + "' for writing.");
180  }
181  }
182  } else {
183  throw ProcessError("Not all network edges were found in the lookup table '" + filename + "' for landmark '" + landmarkID + "'.");
184  }
185  std::vector<const E*> routeLM(1, landmark);
186  const double lmCost = router->recomputeCosts(routeLM, defaultVehicle, 0);
187  std::vector<const E*> route;
188 #ifdef HAVE_FOX
189  if (maxNumThreads > 0) {
190  if (threadPool.size() == 0) {
191  // The CHRouter needs initialization
192  // before it gets cloned, so we do a dummy routing which is not in parallel
193  router->compute(landmark, landmark, defaultVehicle, 0, route);
194  route.clear();
195  while ((int)threadPool.size() < maxNumThreads) {
196  new WorkerThread(threadPool, router->clone(), defaultVehicle);
197  }
198  }
199  std::vector<RoutingTask*> currentTasks;
200  for (int j = (int)myFromLandmarkDists[i].size() + myFirstNonInternal; j < (int)edges.size(); ++j) {
201  const E* edge = edges[j];
202  if (landmark != edge) {
203  std::vector<const E*> routeE(1, edge);
204  const double sourceDestCost = lmCost + router->recomputeCosts(routeE, defaultVehicle, 0);
205  // compute from-distance (skip taz-sources and other unreachable edges)
206  if (edge->getPredecessors().size() > 0 && landmark->getSuccessors().size() > 0) {
207  currentTasks.push_back(new RoutingTask(landmark, edge, sourceDestCost));
208  threadPool.add(currentTasks.back());
209  }
210  // compute to-distance (skip unreachable landmarks)
211  if (landmark->getPredecessors().size() > 0 && edge->getSuccessors().size() > 0) {
212  currentTasks.push_back(new RoutingTask(edge, landmark, sourceDestCost));
213  threadPool.add(currentTasks.back());
214  }
215  }
216  }
217  threadPool.waitAll(false);
218  int taskIndex = 0;
219  for (int j = (int)myFromLandmarkDists[i].size() + myFirstNonInternal; j < (int)edges.size(); ++j) {
220  const E* edge = edges[j];
221  double distFrom = -1;
222  double distTo = -1;
223  if (landmark == edge) {
224  distFrom = 0;
225  distTo = 0;
226  } else {
227  if (edge->getPredecessors().size() > 0 && landmark->getSuccessors().size() > 0) {
228  distFrom = currentTasks[taskIndex]->getCost();
229  delete currentTasks[taskIndex++];
230  }
231  if (landmark->getPredecessors().size() > 0 && edge->getSuccessors().size() > 0) {
232  distTo = currentTasks[taskIndex]->getCost();
233  delete currentTasks[taskIndex++];
234  }
235  }
236  myFromLandmarkDists[i].push_back(distFrom);
237  myToLandmarkDists[i].push_back(distTo);
238  (*ostrm) << landmarkID << " " << edge->getID() << " " << distFrom << " " << distTo << "\n";
239  }
240  currentTasks.clear();
241  continue;
242  }
243 #endif
244  for (int j = (int)myFromLandmarkDists[i].size() + myFirstNonInternal; j < (int)edges.size(); ++j) {
245  const E* edge = edges[j];
246  double distFrom = -1;
247  double distTo = -1;
248  if (landmark == edge) {
249  distFrom = 0;
250  distTo = 0;
251  } else {
252  std::vector<const E*> routeE(1, edge);
253  const double sourceDestCost = lmCost + router->recomputeCosts(routeE, defaultVehicle, 0);
254  // compute from-distance (skip taz-sources and other unreachable edges)
255  if (edge->getPredecessors().size() > 0 && landmark->getSuccessors().size() > 0) {
256  if (router->compute(landmark, edge, defaultVehicle, 0, route)) {
257  distFrom = MAX2(0.0, router->recomputeCosts(route, defaultVehicle, 0) - sourceDestCost);
258  route.clear();
259  }
260  }
261  // compute to-distance (skip unreachable landmarks)
262  if (landmark->getPredecessors().size() > 0 && edge->getSuccessors().size() > 0) {
263  if (router->compute(edge, landmark, defaultVehicle, 0, route)) {
264  distTo = MAX2(0.0, router->recomputeCosts(route, defaultVehicle, 0) - sourceDestCost);
265  route.clear();
266  }
267  }
268  }
269  myFromLandmarkDists[i].push_back(distFrom);
270  myToLandmarkDists[i].push_back(distTo);
271  (*ostrm) << landmarkID << " " << edge->getID() << " " << distFrom << " " << distTo << "\n";
272  }
273  }
274  }
275  delete ostrm;
276  }
277 
278  double lowerBound(const E* from, const E* to, double speed, double speedFactor, double fromEffort, double toEffort) const {
279  double result = from->getDistanceTo(to) / speed;
280 #ifdef ASTAR_DEBUG_LOOKUPTABLE
281  if (from->getID() == ASTAR_DEBUG_LOOKUPTABLE_FROM) {
282  std::cout << " lowerBound to=" << to->getID() << " result1=" << result << "\n";
283  }
284 #endif
285  for (int i = 0; i < (int)myLandmarks.size(); ++i) {
286  // a cost of -1 is used to encode unreachability.
287  const double fl = myToLandmarkDists[i][from->getNumericalID() - myFirstNonInternal];
288  const double tl = myToLandmarkDists[i][to->getNumericalID() - myFirstNonInternal];
289  if (fl >= 0 && tl >= 0) {
290  const double bound = (fl - tl - toEffort) / speedFactor;
291 #ifdef ASTAR_DEBUG_LOOKUPTABLE
292  if (from->getID() == ASTAR_DEBUG_LOOKUPTABLE_FROM && result < bound) {
293  std::cout << " landmarkTo=" << getLandmark(i) << " result2=" << bound
294  << " fl=" << fl << " tl=" << tl << "\n";
295  }
296 #endif
297  result = MAX2(result, bound);
298  }
299  const double lt = myFromLandmarkDists[i][to->getNumericalID() - myFirstNonInternal];
300  const double lf = myFromLandmarkDists[i][from->getNumericalID() - myFirstNonInternal];
301  if (lt >= 0 && lf >= 0) {
302  const double bound = (lt - lf - fromEffort) / speedFactor;
303 #ifdef ASTAR_DEBUG_LOOKUPTABLE
304  if (from->getID() == ASTAR_DEBUG_LOOKUPTABLE_FROM && result < bound) {
305  std::cout << " landmarkFrom=" << getLandmark(i) << " result3=" << bound
306  << " lt=" << lt << " lf=" << lf << "\n";
307  }
308 #endif
309  result = MAX2(result, bound);
310  }
311  if ((tl >= 0 && fl < 0)
312  || (lf >= 0 && lt < 0)) {
313  // target unreachable.
314 #ifdef ASTAR_DEBUG_UNREACHABLE
315  std::cout << " unreachable: from=" << from->getID() << " to=" << to->getID() << " landmark=" << getLandmark(i) << " "
316  << ((tl >= 0 && fl < 0) ? " (toLandmark)" : " (fromLandmark)")
317  << " fl=" << fl << " tl=" << tl << " lt=" << lt << " lf=" << lf
318  << "\n";
319 #endif
320  return UNREACHABLE;
321  }
322  }
323  return result;
324  }
325 
326  bool consistent() const {
327  return false;
328  }
329 
330 private:
331  std::map<std::string, int> myLandmarks;
332  std::vector<std::vector<double> > myFromLandmarkDists;
333  std::vector<std::vector<double> > myToLandmarkDists;
335 
336 #ifdef HAVE_FOX
337 private:
338  class WorkerThread : public FXWorkerThread {
339  public:
340  WorkerThread(FXWorkerThread::Pool& pool,
341  SUMOAbstractRouter<E, V>* router, const V* vehicle)
342  : FXWorkerThread(pool), myRouter(router), myVehicle(vehicle) {}
343  virtual ~WorkerThread() {
344  delete myRouter;
345  }
346  double compute(const E* src, const E* dest, const double costOff) {
347  double result = -1.;
348  if (myRouter->compute(src, dest, myVehicle, 0, myRoute)) {
349  result = MAX2(0.0, myRouter->recomputeCosts(myRoute, myVehicle, 0) + costOff);
350  myRoute.clear();
351  }
352  return result;
353  }
354  private:
355  SUMOAbstractRouter<E, V>* myRouter;
356  const V* myVehicle;
357  std::vector<const E*> myRoute;
358  };
359 
360  class RoutingTask : public FXWorkerThread::Task {
361  public:
362  RoutingTask(const E* src, const E* dest, const double costOff)
363  : mySrc(src), myDest(dest), myCost(-costOff) {}
364  void run(FXWorkerThread* context) {
365  myCost = ((WorkerThread*)context)->compute(mySrc, myDest, myCost);
366  }
367  double getCost() {
368  return myCost;
369  }
370  private:
371  const E* const mySrc;
372  const E* const myDest;
373  double myCost;
374  private:
376  RoutingTask& operator=(const RoutingTask&);
377  };
378 
379 
380 private:
382 #endif
383 
384  std::string getLandmark(int i) const {
385  for (std::map<std::string, int>::const_iterator it = myLandmarks.begin(); it != myLandmarks.end(); ++it) {
386  if (it->second == i) {
387  return it->first;
388  }
389  }
390  return "";
391  }
392 };
393 
394 
395 
396 
397 #endif
398 
399 /****************************************************************************/
400 
void waitAll(const bool deleteFinished=true)
waits for all tasks to be finished
bool consistent() const
whether the heuristic ist consistent (found nodes are always visited on the shortest path the first t...
double lowerBound(const E *from, const E *to, double speed, double speedFactor, double fromEffort, double toEffort) const
provide a lower bound on the distance between from and to (excluding traveltime of both edges) ...
virtual bool compute(const E *from, const E *to, const V *const vehicle, SUMOTime msTime, std::vector< const E *> &into)=0
Builds the route between the given edges using the minimum effort at the given time The definition of...
std::string get(int pos) const
T MAX2(T a, T b)
Definition: StdDefs.h:76
#define WRITE_WARNING(msg)
Definition: MsgHandler.h:242
#define UNREACHABLE
int size() const
Returns the number of threads in the pool.
std::vector< std::vector< double > > myToLandmarkDists
FullLookupTable(const std::string &filename, const int size)
std::map< std::string, int > myLandmarks
double lowerBound(const E *from, const E *to, double, double speedFactor, double, double) const
provide a lower bound on the distance between from and to (excluding traveltime of both edges) ...
virtual bool consistent() const =0
whether the heuristic ist consistent (found nodes are always visited on the shortest path the first t...
void add(Task *const t, int index=-1)
Gives a number to the given task and assigns it to the worker with the given index. If the index is negative, assign to the next (round robin) one.
virtual double lowerBound(const E *from, const E *to, double speed, double speedFactor, double fromEffort, double toEffort) const =0
provide a lower bound on the distance between from and to (excluding traveltime of both edges) ...
A pool of worker threads which distributes the tasks and collects the results.
std::vector< std::vector< double > > myTable
static double _2double(const E *const data)
converts a char-type array into the double value described by it
Definition: TplConvert.h:305
std::string getLandmark(int i) const
Abstract superclass of a task to be run with an index to keep track of pending tasks.
double recomputeCosts(const std::vector< const E *> &edges, const V *const v, SUMOTime msTime) const
A thread repeatingly calculating incoming tasks.
Computes the shortest path through a network using the A* algorithm.
virtual SUMOAbstractRouter * clone()=0
LandmarkLookupTable(const std::string &filename, const std::vector< E *> &edges, SUMOAbstractRouter< E, V > *router, const V *defaultVehicle, const std::string &outfile, const int maxNumThreads)
Encapsulates binary reading operations on a file.
std::vector< std::vector< double > > myFromLandmarkDists
bool consistent() const
whether the heuristic ist consistent (found nodes are always visited on the shortest path the first t...