Eclipse SUMO - Simulation of Urban MObility
Loading...
Searching...
No Matches
AStarLookupTable.h
Go to the documentation of this file.
1/****************************************************************************/
2// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3// Copyright (C) 2012-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/****************************************************************************/
18// Precomputed landmark distances to speed up the A* routing algorithm
19/****************************************************************************/
20#pragma once
21#include <config.h>
22
23#include <iostream>
24#include <fstream>
25#ifdef HAVE_ZLIB
26#include <foreign/zstr/zstr.hpp>
27#endif
28#ifdef HAVE_FOX
30#endif
33
34#define UNREACHABLE (std::numeric_limits<double>::max() / 1000.0)
35
36//#define ASTAR_DEBUG_LOOKUPTABLE
37//#define ASTAR_DEBUG_LOOKUPTABLE_FROM "disabled"
38//#define ASTAR_DEBUG_UNREACHABLE
39
40// ===========================================================================
41// class definitions
42// ===========================================================================
59template<class E, class V>
61public:
63
65 virtual double lowerBound(const E* from, const E* to, double speed, double speedFactor, double fromEffort, double toEffort) const = 0;
66
68 virtual bool consistent() const = 0;
69};
70
71
72template<class E, class V>
74public:
75 FullLookupTable(const std::string& filename, const int size) :
76 myTable(size) {
77 std::ifstream strm(filename.c_str());
78 for (int i = 0; i < size; i++) {
79 for (int j = 0; j < size; j++) {
80 double val;
81 strm >> val;
82 myTable[i].push_back(val);
83 }
84 }
85 }
86
87 virtual ~FullLookupTable() {
88 }
89
90 double lowerBound(const E* from, const E* to, double /*speed*/, double speedFactor, double /*fromEffort*/, double /*toEffort*/) const {
91 return myTable[from->getNumericalID()][to->getNumericalID()] / speedFactor;
92 }
93
94 bool consistent() const {
95 return true;
96 }
97
98private:
99 std::vector<std::vector<double> > myTable;
100};
101
102
103template<class E, class V>
105public:
106 LandmarkLookupTable(const std::string& filename, const std::vector<E*>& edges, SUMOAbstractRouter<E, V>* router,
107 SUMOAbstractRouter<ReversedEdge<E, V>, V>* reverseRouter,
108 const V* defaultVehicle, const std::string& outfile, const int maxNumThreads) {
110 std::map<std::string, int> numericID;
111 for (E* e : edges) {
112 if (!e->isInternal()) {
113 if (myFirstNonInternal == -1) {
114 myFirstNonInternal = e->getNumericalID();
115 }
116 numericID[e->getID()] = e->getNumericalID() - myFirstNonInternal;
117 }
118 }
119#ifdef HAVE_ZLIB
120 zstr::ifstream strm(filename.c_str(), std::fstream::in | std::fstream::binary);
121#else
122 std::ifstream strm(filename.c_str());
123#endif
124 if (!strm.good()) {
125 throw ProcessError(TLF("Could not load landmark-lookup-table from '%'.", filename));
126 }
127 std::string line;
128 std::vector<const E*> landmarks;
129 int numLandMarks = 0;
130 bool haveData = false;
131 while (std::getline(strm, line)) {
132 if (line == "") {
133 break;
134 }
135 StringTokenizer st(line);
136 if (st.size() == 1) {
137 const std::string lm = st.get(0);
138 if (myLandmarks.count(lm) != 0) {
139 throw ProcessError(TLF("Duplicate edge '%' in landmark file.", lm));
140 }
141 // retrieve landmark edge
142 const auto& it = numericID.find(lm);
143 if (it == numericID.end()) {
144 throw ProcessError(TLF("Landmark edge '%' does not exist in the network.", lm));
145 }
146 myLandmarks[lm] = numLandMarks++;
147 myFromLandmarkDists.push_back(std::vector<double>(0));
148 myToLandmarkDists.push_back(std::vector<double>(0));
149 landmarks.push_back(edges[it->second + myFirstNonInternal]);
150 } else if (st.size() == 4) {
151 // legacy style landmark table
152 const std::string lm = st.get(0);
153 const std::string edge = st.get(1);
154 if (numericID[edge] != (int)myFromLandmarkDists[myLandmarks[lm]].size()) {
155 throw ProcessError(TLF("Unknown or unordered edge '%' in landmark file.", edge));
156 }
157 const double distFrom = StringUtils::toDouble(st.get(2));
158 const double distTo = StringUtils::toDouble(st.get(3));
159 myFromLandmarkDists[myLandmarks[lm]].push_back(distFrom);
160 myToLandmarkDists[myLandmarks[lm]].push_back(distTo);
161 haveData = true;
162 } else {
163 const std::string edge = st.get(0);
164 if ((int)st.size() != 2 * numLandMarks + 1) {
165 throw ProcessError(TLF("Broken landmark file, unexpected number of entries (%) for edge '%'.", st.size() - 1, edge));
166 }
167 if (numericID[edge] != (int)myFromLandmarkDists[0].size()) {
168 throw ProcessError(TLF("Unknown or unordered edge '%' in landmark file.", edge));
169 }
170 for (int i = 0; i < numLandMarks; i++) {
171 const double distFrom = StringUtils::toDouble(st.get(2 * i + 1));
172 const double distTo = StringUtils::toDouble(st.get(2 * i + 2));
173 myFromLandmarkDists[i].push_back(distFrom);
174 myToLandmarkDists[i].push_back(distTo);
175 }
176 haveData = true;
177 }
178 }
179 if (myLandmarks.empty()) {
180 WRITE_WARNINGF("No landmarks in '%', falling back to standard A*.", filename);
181 return;
182 }
183#ifdef HAVE_FOX
184 MFXWorkerThread::Pool threadPool;
185 std::vector<RoutingTask*> currentTasks;
186#endif
187 if (!haveData) {
188 WRITE_MESSAGE(TL("Calculating new lookup table."));
189 }
190 for (int i = 0; i < numLandMarks; ++i) {
191 if ((int)myFromLandmarkDists[i].size() != (int)edges.size() - myFirstNonInternal) {
192 const E* const landmark = landmarks[i];
193 if (haveData) {
194 if (myFromLandmarkDists[i].empty()) {
195 WRITE_WARNINGF(TL("No lookup table for landmark edge '%', recalculating."), landmark->getID());
196 } else {
197 throw ProcessError(TLF("Not all network edges were found in the lookup table '%' for landmark edge '%'.", filename, landmark->getID()));
198 }
199 }
200#ifdef HAVE_FOX
201 if (maxNumThreads > 0) {
202 const double lmCost = router->recomputeCosts({landmark}, defaultVehicle, 0);
203 router->setAutoBulkMode(true);
204 if (threadPool.size() == 0) {
205 if (reverseRouter == nullptr) {
206 // The CHRouter needs initialization
207 // before it gets cloned, so we do a dummy routing which is not in parallel
208 std::vector<const E*> route;
209 router->compute(landmark, landmark, defaultVehicle, 0, route);
210 } else {
211 reverseRouter->setAutoBulkMode(true);
212 }
213 while ((int)threadPool.size() < maxNumThreads) {
214 auto revClone = reverseRouter == nullptr ? nullptr : reverseRouter->clone();
215 new WorkerThread(threadPool, router->clone(), revClone, defaultVehicle);
216 }
217 }
218 for (int j = (int)myFromLandmarkDists[i].size() + myFirstNonInternal; j < (int)edges.size(); ++j) {
219 const E* const edge = edges[j];
220 if (landmark != edge) {
221 const double sourceDestCost = lmCost + router->recomputeCosts({edge}, defaultVehicle, 0);
222 currentTasks.push_back(new RoutingTask(landmark, edge, sourceDestCost));
223 threadPool.add(currentTasks.back(), i % maxNumThreads);
224 }
225 }
226 }
227#else
228 UNUSED_PARAMETER(reverseRouter);
229#endif
230 }
231 }
232#ifdef HAVE_FOX
233 threadPool.waitAll(false);
234 int taskIndex = 0;
235#endif
236 for (int i = 0; i < numLandMarks; ++i) {
237 if ((int)myFromLandmarkDists[i].size() != (int)edges.size() - myFirstNonInternal) {
238 const E* landmark = landmarks[i];
239 const double lmCost = router->recomputeCosts({landmark}, defaultVehicle, 0);
240 for (int j = (int)myFromLandmarkDists[i].size() + myFirstNonInternal; j < (int)edges.size(); ++j) {
241 const E* edge = edges[j];
242 double distFrom = -1;
243 double distTo = -1;
244 if (landmark == edge) {
245 distFrom = 0;
246 distTo = 0;
247 } else {
248 if (maxNumThreads > 0) {
249#ifdef HAVE_FOX
250 distFrom = currentTasks[taskIndex]->getFromCost();
251 distTo = currentTasks[taskIndex]->getToCost();
252 delete currentTasks[taskIndex++];
253#endif
254 } else {
255 const double sourceDestCost = lmCost + router->recomputeCosts({edge}, defaultVehicle, 0);
256 std::vector<const E*> route;
257 std::vector<const ReversedEdge<E, V>*> reversedRoute;
258 // compute from-distance (skip taz-sources and other unreachable edges)
259 if (edge->getPredecessors().size() > 0 && landmark->getSuccessors().size() > 0) {
260 if (router->compute(landmark, edge, defaultVehicle, 0, route)) {
261 distFrom = MAX2(0.0, router->recomputeCosts(route, defaultVehicle, 0) - sourceDestCost);
262 route.clear();
263 }
264 }
265 // compute to-distance (skip unreachable landmarks)
266 if (landmark->getPredecessors().size() > 0 && edge->getSuccessors().size() > 0) {
267 if (router->compute(edge, landmark, defaultVehicle, 0, route)) {
268 distTo = MAX2(0.0, router->recomputeCosts(route, defaultVehicle, 0) - sourceDestCost);
269 route.clear();
270 }
271 }
272 }
273 }
274 myFromLandmarkDists[i].push_back(distFrom);
275 myToLandmarkDists[i].push_back(distTo);
276 }
277 }
278 }
279 if (!outfile.empty()) {
280 std::ostream* ostrm = nullptr;
281#ifdef HAVE_ZLIB
282 if (StringUtils::endsWith(outfile, ".gz")) {
283 ostrm = new zstr::ofstream(outfile.c_str(), std::ios_base::out);
284 } else {
285#endif
286 ostrm = new std::ofstream(outfile.c_str());
287#ifdef HAVE_ZLIB
288 }
289#endif
290 if (!ostrm->good()) {
291 delete ostrm;
292 throw ProcessError(TLF("Could not open file '%' for writing.", outfile));
293 }
294 WRITE_MESSAGEF(TL("Saving new matrix to '%'."), outfile);
295 for (int i = 0; i < numLandMarks; ++i) {
296 (*ostrm) << getLandmark(i) << "\n";
297 }
298 for (int j = 0; j < (int)myFromLandmarkDists[0].size(); ++j) {
299 (*ostrm) << edges[j + myFirstNonInternal]->getID();
300 for (int i = 0; i < numLandMarks; ++i) {
301 (*ostrm) << " " << myFromLandmarkDists[i][j] << " " << myToLandmarkDists[i][j];
302 }
303 (*ostrm) << "\n";
304 }
305 delete ostrm;
306 }
307 }
308
310 }
311
312 double lowerBound(const E* from, const E* to, double speed, double speedFactor, double fromEffort, double toEffort) const {
313 double result = from->getDistanceTo(to) / speed;
314#ifdef ASTAR_DEBUG_LOOKUPTABLE
315 if (from->getID() == ASTAR_DEBUG_LOOKUPTABLE_FROM) {
316 std::cout << " lowerBound to=" << to->getID() << " result1=" << result << "\n";
317 }
318#endif
319 for (int i = 0; i < (int)myLandmarks.size(); ++i) {
320 // a cost of -1 is used to encode unreachability.
321 const double fl = myToLandmarkDists[i][from->getNumericalID() - myFirstNonInternal];
322 const double tl = myToLandmarkDists[i][to->getNumericalID() - myFirstNonInternal];
323 if (fl >= 0 && tl >= 0) {
324 const double bound = (fl - tl - toEffort) / speedFactor;
325#ifdef ASTAR_DEBUG_LOOKUPTABLE
326 if (from->getID() == ASTAR_DEBUG_LOOKUPTABLE_FROM && result < bound) {
327 std::cout << " landmarkTo=" << getLandmark(i) << " result2=" << bound
328 << " fl=" << fl << " tl=" << tl << "\n";
329 }
330#endif
331 result = MAX2(result, bound);
332 }
333 const double lt = myFromLandmarkDists[i][to->getNumericalID() - myFirstNonInternal];
334 const double lf = myFromLandmarkDists[i][from->getNumericalID() - myFirstNonInternal];
335 if (lt >= 0 && lf >= 0) {
336 const double bound = (lt - lf - fromEffort) / speedFactor;
337#ifdef ASTAR_DEBUG_LOOKUPTABLE
338 if (from->getID() == ASTAR_DEBUG_LOOKUPTABLE_FROM && result < bound) {
339 std::cout << " landmarkFrom=" << getLandmark(i) << " result3=" << bound
340 << " lt=" << lt << " lf=" << lf << "\n";
341 }
342#endif
343 result = MAX2(result, bound);
344 }
345 if ((tl >= 0 && fl < 0)
346 || (lf >= 0 && lt < 0)) {
347 // target unreachable.
348#ifdef ASTAR_DEBUG_UNREACHABLE
349 std::cout << " unreachable: from=" << from->getID() << " to=" << to->getID() << " landmark=" << getLandmark(i) << " "
350 << ((tl >= 0 && fl < 0) ? " (toLandmark)" : " (fromLandmark)")
351 << " fl=" << fl << " tl=" << tl << " lt=" << lt << " lf=" << lf
352 << "\n";
353#endif
354 return UNREACHABLE;
355 }
356 }
357 return result;
358 }
359
360 bool consistent() const {
361 return false;
362 }
363
364private:
365 std::map<std::string, int> myLandmarks;
366 std::vector<std::vector<double> > myFromLandmarkDists;
367 std::vector<std::vector<double> > myToLandmarkDists;
369
370#ifdef HAVE_FOX
371private:
372 class WorkerThread : public MFXWorkerThread {
373 public:
374 WorkerThread(MFXWorkerThread::Pool& pool,
376 SUMOAbstractRouter<ReversedEdge<E, V>, V>* reverseRouter, const V* vehicle)
377 : MFXWorkerThread(pool), myRouter(router), myReversedRouter(reverseRouter), myVehicle(vehicle) {}
378
379 virtual ~WorkerThread() {
380 delete myRouter;
381 delete myReversedRouter;
382 }
383
384 const std::pair<double, double> compute(const E* src, const E* dest, const double costOff) {
385 double fromResult = -1.;
386 if (myRouter->compute(src, dest, myVehicle, 0, myRoute)) {
387 fromResult = MAX2(0.0, myRouter->recomputeCosts(myRoute, myVehicle, 0) + costOff);
388 myRoute.clear();
389 }
390 double toResult = -1.;
391 if (myReversedRouter != nullptr) {
392 if (myReversedRouter->compute(src->getReversedRoutingEdge(), dest->getReversedRoutingEdge(), myVehicle, 0, myReversedRoute)) {
393 toResult = MAX2(0.0, myReversedRouter->recomputeCosts(myReversedRoute, myVehicle, 0) + costOff);
394 myReversedRoute.clear();
395 }
396 } else {
397 if (myRouter->compute(dest, src, myVehicle, 0, myRoute)) {
398 toResult = MAX2(0.0, myRouter->recomputeCosts(myRoute, myVehicle, 0) + costOff);
399 myRoute.clear();
400 }
401 }
402 return std::make_pair(fromResult, toResult);
403 }
404
405 private:
406 SUMOAbstractRouter<E, V>* myRouter;
407 SUMOAbstractRouter<ReversedEdge<E, V>, V>* myReversedRouter;
408 const V* myVehicle;
409 std::vector<const E*> myRoute;
410 std::vector<const ReversedEdge<E, V>*> myReversedRoute;
411 };
412
413 class RoutingTask : public MFXWorkerThread::Task {
414 public:
415 RoutingTask(const E* src, const E* dest, const double costOff)
416 : mySrc(src), myDest(dest), myCostOff(-costOff) {}
417 void run(MFXWorkerThread* context) {
418 myCost = ((WorkerThread*)context)->compute(mySrc, myDest, myCostOff);
419 }
420 double getFromCost() {
421 return myCost.first;
422 }
423 double getToCost() {
424 return myCost.second;
425 }
426 private:
427 const E* const mySrc;
428 const E* const myDest;
429 const double myCostOff;
430 std::pair<double, double> myCost;
431 private:
433 RoutingTask& operator=(const RoutingTask&) = delete;
434 };
435
436private:
438#endif
439
440 std::string getLandmark(int i) const {
441 for (std::map<std::string, int>::const_iterator it = myLandmarks.begin(); it != myLandmarks.end(); ++it) {
442 if (it->second == i) {
443 return it->first;
444 }
445 }
446 return "";
447 }
448};
#define UNREACHABLE
#define WRITE_WARNINGF(...)
Definition MsgHandler.h:296
#define WRITE_MESSAGEF(...)
Definition MsgHandler.h:298
#define WRITE_MESSAGE(msg)
Definition MsgHandler.h:297
#define TL(string)
Definition MsgHandler.h:315
#define TLF(string,...)
Definition MsgHandler.h:317
#define UNUSED_PARAMETER(x)
Definition StdDefs.h:30
T MAX2(T a, T b)
Definition StdDefs.h:82
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)
virtual bool consistent() const =0
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, double speedFactor, double, double) const
provide a lower bound on the distance between from and to (excluding traveltime of both edges)
bool consistent() const
whether the heuristic ist consistent (found nodes are always visited on the shortest path the first t...
std::vector< std::vector< double > > myTable
virtual ~FullLookupTable()
FullLookupTable(const std::string &filename, const int size)
Computes the shortest path through a network using the A* algorithm.
std::vector< std::vector< double > > myToLandmarkDists
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)
std::map< std::string, int > myLandmarks
std::string getLandmark(int i) const
LandmarkLookupTable(const std::string &filename, const std::vector< E * > &edges, SUMOAbstractRouter< E, V > *router, SUMOAbstractRouter< ReversedEdge< E, V >, V > *reverseRouter, const V *defaultVehicle, const std::string &outfile, const int maxNumThreads)
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...
A pool of worker threads which distributes the tasks and collects the results.
void waitAll(const bool deleteFinished=true)
waits for all tasks to be finished
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....
int size() const
Returns the number of threads in the pool.
Abstract superclass of a task to be run with an index to keep track of pending tasks.
A thread repeatingly calculating incoming tasks.
the edge type representing backward edges
virtual SUMOAbstractRouter * clone()=0
void setAutoBulkMode(const bool mode)
virtual bool compute(const E *from, const E *to, const V *const vehicle, SUMOTime msTime, std::vector< const E * > &into, bool silent=false)=0
Builds the route between the given edges using the minimum effort at the given time The definition of...
virtual double recomputeCosts(const std::vector< const E * > &edges, const V *const v, SUMOTime msTime, double *lengthp=nullptr) const
int size() const
returns the number of existing substrings
std::string get(int pos) const
returns the item at the given position
static double toDouble(const std::string &sData)
converts a string into the double value described by it by calling the char-type converter
static bool endsWith(const std::string &str, const std::string suffix)
Checks whether a given string ends with the suffix.