Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/src/utils/router/AFCentralizedSPTree.h
169678 views
1
/****************************************************************************/
2
// Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
3
// Copyright (C) 2012-2025 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
/****************************************************************************/
14
/// @file AFCentralizedSPTree.h
15
/// @author Ruediger Ebendt
16
/// @date 01.12.2023
17
///
18
// Class for a label-correcting algorithm calculating multiple shortest path
19
// trees at once (centralized shortest path tree, cf. Hilger et al.)
20
// Used for setting the arc flags for the arc flag router
21
// @note Intended use is on a backward graph with flipped edges
22
/****************************************************************************/
23
#pragma once
24
#include <config.h>
25
26
#include <cassert>
27
#include <string>
28
#include <functional>
29
#include <vector>
30
#include <set>
31
#include <limits>
32
#include <algorithm>
33
#include <iterator>
34
#include <map>
35
#include <iostream>
36
#include <memory>
37
#include <stdexcept>
38
#include <cstddef>
39
#include <utils/common/MsgHandler.h>
40
#include <utils/common/StringTokenizer.h>
41
#include <utils/common/StringUtils.h>
42
#include <utils/common/StdDefs.h>
43
#include <utils/common/ToString.h>
44
#include <utils/iodevices/OutputDevice.h>
45
#include <utils/common/SUMOTime.h>
46
#include "SUMOAbstractRouter.h"
47
#include "KDTreePartition.h"
48
#include "AFInfo.h"
49
50
//#define CSPT_WRITE_QGIS_FILTERS
51
//#define CSPT_DEBUG_LEVEL_0
52
53
template<class E, class N, class V>
54
class AFCentralizedSPTree {
55
public:
56
typedef typename KDTreePartition<E, N, V>::Cell Cell;
57
typedef typename AFInfo<E>::ArcInfo ArcInfo;
58
59
class EdgeInfoComparator {
60
public:
61
/** @brief Constructor
62
* @param[in] arcInfos The arc informations
63
*/
64
EdgeInfoComparator(std::vector<ArcInfo*>& arcInfos) : myArcInfos(arcInfos) {
65
}
66
67
/** @brief Comparing method
68
* @param[in] edgeInfo1 The first edge information
69
* @param[in] edgeInfo2 The second edge information
70
* @return true iff arc info key of the first edge is greater than that of the second
71
* @note In case of ties: returns true iff the numerical id of the first edge is greater that that of the second
72
*/
73
bool operator()(const typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo1,
74
const typename SUMOAbstractRouter<E, V>::EdgeInfo* edgeInfo2) const {
75
int index1 = edgeInfo1->edge->getNumericalID();
76
int index2 = edgeInfo2->edge->getNumericalID();
77
ArcInfo* arcInfo1 = myArcInfos[index1];
78
ArcInfo* arcInfo2 = myArcInfos[index2];
79
double key1 = arcInfo1->key;
80
double key2 = arcInfo2->key;
81
82
if (key1 == key2) { // tie
83
return index1 > index2;
84
}
85
return key1 > key2;
86
}
87
private:
88
std::vector<ArcInfo*>& myArcInfos;
89
};
90
91
/** @brief Constructor
92
* @param[in] edges The edges
93
* @param[in] arcInfos The arc informations (for arc flag routing)
94
* @param[in] unbuildIsWarning The flag indicating whether network unbuilds should issue warnings or errors
95
* @param[in] effortProvider The effort provider
96
* @param[in] havePermissions The boolean flag indicating whether edge permissions need to be considered or not
97
* @param[in] haveRestrictions The boolean flag indicating whether edge restrictions need to be considered or not
98
*/
99
AFCentralizedSPTree(const std::vector<E*>& edges, std::vector<ArcInfo*>& arcInfos, bool unbuildIsWarning,
100
SUMOAbstractRouter<E, V>* effortProvider,
101
const bool havePermissions = false, const bool haveRestrictions = false) :
102
myArcInfos(arcInfos),
103
myHavePermissions(havePermissions),
104
myHaveRestrictions(haveRestrictions),
105
myErrorMsgHandler(unbuildIsWarning ? MsgHandler::getWarningInstance() : MsgHandler::getErrorInstance()),
106
myEffortProvider(effortProvider),
107
myMaxSpeed(NUMERICAL_EPS) {
108
for (const E* const edge : edges) {
109
myEdgeInfos.push_back(typename SUMOAbstractRouter<E, V>::EdgeInfo(edge));
110
myMaxSpeed = MAX2(myMaxSpeed, edge->getSpeedLimit() * MAX2(1.0, edge->getLengthGeometryFactor()));
111
}
112
myComparator = new EdgeInfoComparator(myArcInfos);
113
}
114
115
/// @brief Destructor
116
~AFCentralizedSPTree() {
117
delete myComparator;
118
}
119
120
/** @brief Returns true iff driving the given vehicle on the given edge is prohibited
121
* @param[in] edge The edge
122
* @param[in] vehicle The vehicle
123
* @return true iff driving the given vehicle on the given edge is prohibited
124
*/
125
bool isProhibited(const E* const edge, const V* const vehicle) const {
126
return (myHavePermissions && edge->prohibits(vehicle)) || (myHaveRestrictions && edge->restricts(vehicle));
127
}
128
129
/** @brief Computes a shortest path tree for each boundary edge of the given cell, returns true iff this was successful
130
* @note This is done for all such shortest path trees at once (i.e., a centralized shortest path tree is computed, see Hilger et al.)
131
* @param[in] msTime The start time of the paths/routes in milliseconds
132
* @param[in] cell The cell as a part of a k-d tree partition of the network
133
* @param[in] vehicle The vehicle
134
* @param[in] incomingEdgesOfOutgoingBoundaryEdges Maps each outgoing boundary edge to its incoming edges
135
* @param[in] silent The boolean flag indicating whether the method stays silent or puts out messages
136
* @return true iff the centralized shortest path tree could successfully be calculated
137
*/
138
bool computeCentralizedSPTree(SUMOTime msTime, const Cell* cell, const V* const vehicle,
139
const std::map<const E*, std::vector<const E*>>& incomingEdgesOfOutgoingBoundaryEdges,
140
bool silent = false) {
141
assert(cell != nullptr);
142
const std::unordered_set<const E*>& fromEdges = cell->getOutgoingBoundaryEdges();
143
if (fromEdges.empty()) { // nothing to do here
144
return false;
145
}
146
double length = 0.; // dummy for the via edge cost update
147
const SUMOVehicleClass vClass = vehicle == 0 ? SVC_IGNORING : vehicle->getVClass();
148
std::vector<const E*> fromEdgesAsVector(fromEdges.begin(), fromEdges.end());
149
init(fromEdgesAsVector, vehicle, msTime);
150
size_t numberOfVisitedFromEdges = 0;
151
bool minIsFromEdge = false;
152
#ifdef CSPT_DEBUG_LEVEL_0
153
size_t numberOfTouchedSupercellEdges = 0;
154
int num_visited = 0;
155
#endif
156
#ifdef _DEBUG
157
const Cell* supercell = cell->getSupercell();
158
#endif
159
const bool mayRevisit = true;
160
161
while (!myFrontierList.empty()) {
162
#ifdef CSPT_DEBUG_LEVEL_0
163
num_visited += 1;
164
#endif
165
// use the edge with the minimal length
166
typename SUMOAbstractRouter<E, V>::EdgeInfo* minimumInfo = myFrontierList.front();
167
const E* const minEdge = minimumInfo->edge;
168
assert(!minEdge->isInternal());
169
ArcInfo* minimumArcInfo = myArcInfos[minEdge->getNumericalID()];
170
if (minimumInfo->visited || numberOfVisitedFromEdges < fromEdges.size()) {
171
minIsFromEdge = incomingEdgesOfOutgoingBoundaryEdges.find(minEdge) != incomingEdgesOfOutgoingBoundaryEdges.end();
172
} else {
173
minIsFromEdge = false;
174
}
175
if (minIsFromEdge) {
176
if (numberOfVisitedFromEdges < fromEdges.size()) {
177
numberOfVisitedFromEdges++;
178
}
179
}
180
assert(incomingEdgesOfOutgoingBoundaryEdges.size() == fromEdges.size());
181
assert(minimumArcInfo->key != std::numeric_limits<double>::max() && minimumArcInfo->key != UNREACHABLE);
182
size_t index;
183
#ifdef CSPT_DEBUG_LEVEL_0
184
if (supercell->contains(minEdge->getToJunction())) {
185
// minEdge is a supercell edge (we check for the to-node since we work on a backward graph)
186
if (!minimumArcInfo->touched) {
187
numberOfTouchedSupercellEdges++;
188
minimumArcInfo->touched = true;
189
}
190
}
191
#endif
192
#ifdef CSPT_DEBUG_LEVEL_0
193
if (num_visited % 500 == 0) {
194
std::cout << "num_visited: " << num_visited << ", numberOfTouchedSupercellEdges: " << numberOfTouchedSupercellEdges
195
<< ", minimumArcInfo->key: " << minimumArcInfo->key << std::endl;
196
}
197
#endif
198
std::pop_heap(myFrontierList.begin(), myFrontierList.end(), *myComparator);
199
myFrontierList.pop_back();
200
myFound.push_back(minimumInfo);
201
minimumInfo->visited = true;
202
const double effortDelta = myEffortProvider->getEffort(minEdge, vehicle, minimumInfo->leaveTime);
203
const double leaveTime = minimumInfo->leaveTime + myEffortProvider->getTravelTime(minEdge, vehicle, minimumInfo->leaveTime, effortDelta);
204
205
// check all ways from the edge with the minimal length
206
for (const std::pair<const E*, const E*>& follower : minEdge->getViaSuccessors(vClass)) {
207
assert(!follower.first->isInternal());
208
bool wasPushedToHeap = false;
209
auto& followerInfo = myEdgeInfos[follower.first->getNumericalID()];
210
ArcInfo* followerArcInfo = myArcInfos[follower.first->getNumericalID()];
211
// check whether it can be used
212
if (followerInfo.prohibited || isProhibited(follower.first, vehicle)) {
213
if (!silent) {
214
myErrorMsgHandler->inform("Vehicle '" + Named::getIDSecure(vehicle) + "' is not allowed on source edge '" + followerInfo.edge->getID() + "'.");
215
}
216
continue;
217
}
218
219
if (followerArcInfo->effortsToBoundaryNodes.empty()) { // non-initialized non-supercell edge
220
assert(!supercell->contains(follower.first->getToJunction()));
221
std::fill_n(std::back_inserter(followerArcInfo->effortsToBoundaryNodes),
222
minimumArcInfo->effortsToBoundaryNodes.size(), std::numeric_limits<double>::max());
223
}
224
225
double key = std::numeric_limits<double>::max();
226
assert(followerArcInfo->effortsToBoundaryNodes.size() == minimumArcInfo->effortsToBoundaryNodes.size());
227
bool hasImproved = false;
228
// loop over all boundary nodes
229
for (index = 0; index < followerArcInfo->effortsToBoundaryNodes.size(); index++) {
230
// is minEdge a from-edge (i.e., an outgoing boundary edge of the passed cell),
231
// and 'index' not the index of its from-node (i.e., not of the 'own' boundary node)?
232
if (minIsFromEdge && (incomingEdgesOfOutgoingBoundaryEdges.at(minEdge))[index]) {
233
// if yes, assign the successor edges of the incoming edge of minEdge on the shortest route from
234
// the boundary node with the index 'index' to followersOfIncomingEdge
235
assert((incomingEdgesOfOutgoingBoundaryEdges.at(minEdge)).size()
236
== followerArcInfo->effortsToBoundaryNodes.size());
237
const std::vector<std::pair<const E*, const E*>>& followersOfIncomingEdge
238
= ((incomingEdgesOfOutgoingBoundaryEdges.at(minEdge))[index])->getViaSuccessors(vClass);
239
// is the current follower among said successor edges?
240
bool turningAllowed = false;
241
for (std::pair<const E*, const E*> followerOfIncomingEdge : followersOfIncomingEdge) {
242
if (follower.first == followerOfIncomingEdge.first) {
243
turningAllowed = true;
244
break;
245
}
246
}
247
// if not, then turning from said incoming edge to the current follower is not allowed
248
// and we mustn't propagate the distance to said boundary node
249
// instead simply skip this follower
250
if (!turningAllowed) {
251
continue;
252
}
253
}
254
// propagate distances to other boundary nodes
255
double effortToFollower = minimumArcInfo->effortsToBoundaryNodes[index] == UNREACHABLE ?
256
UNREACHABLE : minimumArcInfo->effortsToBoundaryNodes[index] + effortDelta;
257
if (effortToFollower == UNREACHABLE) {
258
continue; // no need to consider this follower
259
}
260
double time = leaveTime;
261
myEffortProvider->updateViaEdgeCost(follower.second, vehicle, time, effortToFollower, length);
262
if (effortToFollower < key) {
263
key = effortToFollower;
264
}
265
const double oldEffort = followerArcInfo->effortsToBoundaryNodes[index];
266
if (oldEffort != std::numeric_limits<double>::max()) {
267
wasPushedToHeap = true; // must have been pushed to heap during an earlier visit
268
}
269
270
if ((!followerInfo.visited || mayRevisit)
271
&& effortToFollower < oldEffort) {
272
hasImproved = true;
273
followerArcInfo->effortsToBoundaryNodes[index] = effortToFollower;
274
}
275
} // end index loop
276
277
if (!hasImproved) {
278
continue; // no need to re-enque this follower, continue w/ next one
279
}
280
followerArcInfo->key = key;
281
if (!wasPushedToHeap) {
282
myFrontierList.push_back(&followerInfo);
283
std::push_heap(myFrontierList.begin(), myFrontierList.end(), *myComparator);
284
} else {
285
auto fi = std::find(myFrontierList.begin(), myFrontierList.end(), &followerInfo);
286
if (fi == myFrontierList.end()) { // has already been expanded, reinsert into frontier
287
assert(mayRevisit);
288
myFrontierList.push_back(&followerInfo);
289
std::push_heap(myFrontierList.begin(), myFrontierList.end(), *myComparator);
290
} else {
291
std::push_heap(myFrontierList.begin(), fi + 1, *myComparator);
292
}
293
}
294
} // end follower loop
295
} // end while (!myFrontierList.empty())
296
297
#ifdef CSPT_DEBUG_LEVEL_0
298
std::cout << "centralizedSPTree finished (queue empty)." << std::endl;
299
#endif
300
return true;
301
}
302
303
protected:
304
/** @brief Initialize the arc flag router
305
* @param[in] fromEdges The container of from-/head/source edges
306
* @param[in] vehicle The vehicle
307
* @param[in] msTime The start time of the paths/routes in milliseconds
308
*/
309
void init(std::vector<const E*> fromEdges, const V* const vehicle, SUMOTime msTime);
310
/// @brief The min edge heap
311
/// @note A container for reusage of the min edge heap
312
std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo*> myFrontierList;
313
/// @brief The list of visited edges (for resetting)
314
std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo*> myFound;
315
/// The container of edge information
316
std::vector<typename SUMOAbstractRouter<E, V>::EdgeInfo> myEdgeInfos;
317
/// @brief The edge informations specific to arc flag routing
318
/// @note As opposed to the standard informations in SUMOAbstractRouter<E, V>::EdgeInfo
319
std::vector<ArcInfo*>& myArcInfos;
320
/// @brief The boolean flag indicating whether edge permissions need to be considered or not
321
const bool myHavePermissions;
322
/// @brief The boolean flag indicating whether edge restrictions need to be considered or not
323
const bool myHaveRestrictions;
324
/// @brief The handler for routing errors
325
MsgHandler* const myErrorMsgHandler;
326
/// @brief The object's operation to perform
327
SUMOAbstractRouter<E, V>* myEffortProvider;
328
/// @brief The comparator
329
EdgeInfoComparator* myComparator;
330
/// @brief The maximum speed in the network
331
double myMaxSpeed;
332
};
333
334
// ===========================================================================
335
// method definitions
336
// ===========================================================================
337
338
template<class E, class N, class V>
339
void AFCentralizedSPTree<E, N, V>::init(std::vector<const E*> fromEdges, const V* const vehicle, SUMOTime msTime) {
340
// all EdgeInfos touched in the previous query are either in myFrontierList or myFound: clean those up
341
for (auto& edgeInfo : myFrontierList) {
342
edgeInfo->reset();
343
}
344
myFrontierList.clear();
345
for (auto& edgeInfo : myFound) {
346
edgeInfo->reset();
347
}
348
for (auto& arcInfo : myArcInfos) {
349
arcInfo->reset(); // does not reset effortsToBoundaryNodes
350
}
351
myFound.clear();
352
for (const E* from : fromEdges) {
353
if (from->isInternal()) {
354
continue;
355
}
356
int edgeID = from->getNumericalID();
357
auto& fromInfo = myEdgeInfos[edgeID];
358
if (fromInfo.prohibited || isProhibited(from, vehicle)) {
359
continue;
360
}
361
fromInfo.heuristicEffort = 0.;
362
fromInfo.prev = nullptr;
363
fromInfo.leaveTime = STEPS2TIME(msTime);
364
myFrontierList.push_back(&fromInfo);
365
ArcInfo* fromArcInfo = myArcInfos[edgeID];
366
fromArcInfo->key = 0;
367
}
368
}
369
370