Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
eclipse
GitHub Repository: eclipse/sumo
Path: blob/main/tools/purgatory/dijkstra.py
169674 views
1
# Eclipse SUMO, Simulation of Urban MObility; see https://eclipse.dev/sumo
2
# Copyright (C) 2007-2025 German Aerospace Center (DLR) and others.
3
# This program and the accompanying materials are made available under the
4
# terms of the Eclipse Public License 2.0 which is available at
5
# https://www.eclipse.org/legal/epl-2.0/
6
# This Source Code may also be made available under the following Secondary
7
# Licenses when the conditions for such availability set forth in the Eclipse
8
# Public License 2.0 are satisfied: GNU General Public License, version 2
9
# or later which is available at
10
# https://www.gnu.org/licenses/old-licenses/gpl-2.0-standalone.html
11
# SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-or-later
12
13
# @file dijkstra.py
14
# @author Yun-Pang Floetteroed
15
# @author Daniel Krajzewicz
16
# @author Michael Behrisch
17
# @author Jakob Erdmann
18
# @date 2007-10-25
19
20
"""
21
This script is based on the script from David Eppstein, UC Irvine.
22
This script is to find the shortest path from the given origin 'start' to the other nodes in the investigated network.
23
The Dijkstra algorithm is used for searching the respective shortest paths.
24
the link information about the shortest paths and the corresponding travel times
25
will be stored in the lists P and D respectively.
26
"""
27
28
import os
29
import sys
30
from collections import defaultdict
31
from xml.sax import make_parser, handler
32
sys.path.append(os.path.join(os.path.dirname(__file__), '..'))
33
from sumolib.net import readNet # noqa
34
35
36
class priorityDictionary(dict):
37
38
def __init__(self):
39
'''Initialize priorityDictionary by creating binary heap
40
of pairs (value,key). Note that changing or removing a dict entry will
41
not remove the old pair from the heap until it is found by smallest() or
42
until the heap is rebuilt.'''
43
self.__heap = []
44
dict.__init__(self)
45
46
def smallest(self):
47
'''Find smallest item after removing deleted items from heap.'''
48
if len(self) == 0:
49
raise IndexError("smallest of empty priorityDictionary")
50
heap = self.__heap
51
while heap[0][1] not in self or self[heap[0][1]] != heap[0][0]:
52
lastItem = heap.pop()
53
insertionPoint = 0
54
while 1:
55
smallChild = 2 * insertionPoint + 1
56
if smallChild + 1 < len(heap) and \
57
heap[smallChild][0] > heap[smallChild + 1][0]:
58
smallChild += 1
59
if smallChild >= len(heap) or lastItem <= heap[smallChild]:
60
heap[insertionPoint] = lastItem
61
break
62
heap[insertionPoint] = heap[smallChild]
63
insertionPoint = smallChild
64
return heap[0][1]
65
66
def __iter__(self):
67
'''Create destructive sorted iterator of priorityDictionary.'''
68
def iterfn():
69
while len(self) > 0:
70
x = self.smallest()
71
yield x
72
del self[x]
73
return iterfn()
74
75
def __setitem__(self, key, val):
76
'''Change value stored in dictionary and add corresponding
77
pair to heap. Rebuilds the heap if the number of deleted items grows
78
too large, to avoid memory leakage.'''
79
dict.__setitem__(self, key, val)
80
heap = self.__heap
81
if len(heap) > 2 * len(self):
82
self.__heap = [(v, k) for k, v in self.items()]
83
self.__heap.sort() # builtin sort likely faster than O(n) heapify
84
else:
85
newPair = (val, key)
86
insertionPoint = len(heap)
87
heap.append(None)
88
while insertionPoint > 0 and val < heap[(insertionPoint - 1) // 2][0]:
89
heap[insertionPoint] = heap[(insertionPoint - 1) // 2]
90
insertionPoint = (insertionPoint - 1) // 2
91
heap[insertionPoint] = newPair
92
93
def setdefault(self, key, val):
94
'''Reimplement setdefault to call our customized __setitem__.'''
95
if key not in self:
96
self[key] = val
97
return self[key]
98
99
def update(self, other):
100
for key in other.keys():
101
self[key] = other[key]
102
103
104
def dijkstra(start, targets):
105
# dictionary of final distances
106
D = {}
107
# dictionary of predecessors
108
P = {}
109
# est.dist. of non-final vert.
110
Q = priorityDictionary()
111
Q[start] = 0
112
for v in Q:
113
D[v] = Q[v]
114
if targets.discard(v):
115
if len(targets) == 0:
116
return (D, P)
117
isConflictCandidate = (v != start) and (P[v].conflictlink is not None)
118
for edge in v.outEdges:
119
w = edge.target
120
vwLength = D[v] + edge.helpacttime
121
if isConflictCandidate:
122
if (edge.kind == "junction" and iter(edge.target.outEdges).next() in P[v].leftlink) or\
123
(edge.kind != "junction" and edge in P[v].leftlink):
124
vwLength += P[v].penalty
125
126
if w not in D and (w not in Q or vwLength < Q[w]):
127
Q[w] = vwLength
128
P[w] = edge
129
return (D, P)
130
131
132
def dijkstraPlain(start, targets):
133
# dictionary of final distances
134
D = {}
135
# dictionary of predecessors
136
P = {}
137
# est.dist. of non-final vert.
138
Q = priorityDictionary()
139
Q[start] = 0
140
for v in Q:
141
D[v] = Q[v]
142
if targets.discard(v):
143
if len(targets) == 0:
144
return (D, P)
145
for edge in v.getOutgoing():
146
w = edge._to
147
vwLength = D[v] + edge.helpacttime
148
149
if w not in D and (w not in Q or vwLength < Q[w]):
150
Q[w] = vwLength
151
P[w] = edge
152
return (D, P)
153
154
155
def dijkstraBoost(boostGraph, start):
156
from boost.graph import dijkstra_shortest_paths
157
dijkstra_shortest_paths(boostGraph, start,
158
distance_map=boostGraph.vertex_properties[
159
'distance'],
160
predecessor_map=boostGraph.vertex_properties[
161
'predecessor'],
162
weight_map=boostGraph.edge_properties['weight'])
163
164
# dictionary of final distances
165
D = {}
166
# dictionary of predecessors
167
P = {}
168
for v in boostGraph.vertices:
169
D[v.partner] = v.distance
170
for edge in v.partner.inEdges:
171
if edge.source == v.predecessor.partner:
172
P[v.partner] = edge
173
break
174
return (D, P)
175
176
177
class DijkstraRouter(handler.ContentHandler):
178
179
"""standalone class for routing on a sumolib network.
180
The edges from the network receive new attribute 'cost' based on
181
a loaded meanData output
182
"""
183
184
def __init__(self, netfile, weightfile=None):
185
self.net = readNet(netfile)
186
self.cost_attribute = 'traveltime'
187
self.weightfile = weightfile
188
if self.weightfile is not None:
189
self.load_weights(self.weightfile)
190
191
def load_weights(self, weightfile):
192
# reset weights before loading
193
for e in self.net.getEdges():
194
e.cost = e.getLength() / e.getSpeed()
195
self.weightfile = weightfile
196
self.intervals = 0
197
parser = make_parser()
198
parser.setContentHandler(self)
199
parser.parse(weightfile)
200
201
def startElement(self, name, attrs):
202
if name == 'interval':
203
self.intervals += 1
204
if self.intervals > 1:
205
print("ignoring weights from interval [%s,%s]" % (
206
attrs['begin'], attrs['end']))
207
if name == 'edge':
208
if self.intervals > 1:
209
return
210
if self.cost_attribute in attrs: # may be missing for some
211
self.net.getEdge(attrs['id']).cost = float(
212
attrs[self.cost_attribute])
213
214
def _route(self, start, dest, costFactors):
215
if self.weightfile is None:
216
raise Exception("not weights loaded")
217
# dictionary of final distances
218
D = {}
219
# dictionary of predecessors
220
P = {}
221
# est.dist. of non-final vert.
222
Q = priorityDictionary()
223
Q[start] = 0
224
for edge in Q:
225
D[edge] = Q[edge]
226
# print("final const to %s: %s" % (edge.getID(), D[edge]))
227
if edge == dest:
228
# print [(e.getID(), c) for e,c in Q.items()]
229
return (D, P)
230
cost = Q[edge] + edge.cost * costFactors[edge.getID()]
231
for succ in edge.getOutgoing():
232
if succ not in Q or Q[succ] > cost:
233
# print("reaching %s in %s (from %s)" % (succ.getID(), cost, edge.getID()))
234
Q[succ] = cost
235
P[succ] = edge
236
return (D, P)
237
238
def _getEdge(self, *ids):
239
"""retrieves edge objects based on their ids"""
240
result = []
241
for id in ids:
242
if isinstance(id, str):
243
if self.net.hasEdge(id):
244
result.append(self.net.getEdge(id))
245
else:
246
raise Exception("unknown edge '%s'" % id)
247
else:
248
result.append(id)
249
return result
250
251
def least_cost(self, start, dest, costFactors=defaultdict(lambda: 1.0)):
252
"""return the cost of the shortest path from start to destination edge"""
253
start, dest = self._getEdge(start, dest)
254
D, P = self._route(start, dest, costFactors)
255
if dest in D:
256
return D[dest]
257
else:
258
raise ("No path between %s and %s found" %
259
(start.getID(), dest.getID()))
260
261