-
Notifications
You must be signed in to change notification settings - Fork 8
Expand file tree
/
Copy pathDijkstrasAlgorithm.py
More file actions
349 lines (295 loc) · 15.2 KB
/
Copy pathDijkstrasAlgorithm.py
File metadata and controls
349 lines (295 loc) · 15.2 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
import Queue
from AbortedDijkstra import aborted_dijkstra
from Map import Map
import numpy as np
# An implementation of multi-origin bidirectional_dijkstra
class DijkstrasAlgorithm:
# Gives out sequential node_id numbers to the boundary nodes in this region
# These values are used to index the node.forward_boundary_time array
@staticmethod
def init_boundary_node_ids(list_of_boundary_nodes):
i = 0
sorted_boundary_nodes = sorted(list_of_boundary_nodes,
key=lambda x: x.node_id)
for node in sorted_boundary_nodes:
node.boundary_node_id = i
i += 1
# Sets up lists of "INF" for each nodes, excluding boundary nodes
@staticmethod
def initialize_nodes(boundary_nodes_list, nyc_map):
if not boundary_nodes_list:
return
tmp_node = None
for node in boundary_nodes_list:
tmp_node = node
break
this_region_id = tmp_node.region_id
for node in nyc_map.nodes:
# Each node needs a distance from each boundary node
# all starting at infinity
node.forward_boundary_time = np.repeat(
float("INF"), len(boundary_nodes_list))
node.backward_boundary_time = np.repeat(
float("INF"), len(boundary_nodes_list))
if node.is_boundary_node and (
node.region_id == this_region_id):
# All boundary nodes in this region are 0 distance away
# from themselves
index = node.boundary_node_id
node.forward_boundary_time[index] = 0
node.backward_boundary_time[index] = 0
# Store a deep copy snapshot of forward_boundary_time for
# future comparison
# node.time_snapshot = np.copy(node.forward_boundary_time)
node.forward_predecessors = (
np.array([None] * len(boundary_nodes_list)))
node.backward_predecessors = (
np.array([None] * len(boundary_nodes_list)))
# Computes the shortest path between all pairs of boundary nodes
# If this node_id done before the main bidirectional_dijkstra() search,
# performance should be much better
@staticmethod
def initialize_boundary_nodes(boundary_nodes_list, nyc_map,
this_region_only, on_forward_graph):
visited_nodes = set()
for boundary_node in boundary_nodes_list:
partial_visited_nodes, _, _ = aborted_dijkstra(
boundary_node, boundary_nodes_list, this_region_only,
on_forward_graph)
visited_nodes.update(partial_visited_nodes)
# Update the snapshots of the boundary nodes
# for boundary_node in boundary_nodes_list:
# np.copyto(boundary_node.time_snapshot,
# boundary_node.forward_boundary_time)
return visited_nodes
# Every node in array nodes gets reset so it has no distance from anything,
# no time from anything, and came from nothing (used to reset after making
# the path)
@staticmethod
def reset_nodes(nyc_map):
for node in nyc_map.nodes:
if node is not None:
# For multi-origin bidirectional dijkstra, storing the
# time from each boundary node
node.forward_boundary_time = np.array([])
node.backward_boundary_time = np.array([])
# A snapshot of the forward_boundary_time from the
# last expansion
# node.time_snapshot = np.array([])
# For each boundary node path, shows where this
# particular node came
# from
node.forward_predecessors = np.array([])
node.backward_predecessors = np.array([])
@staticmethod
def directed_dijkstra(boundary_nodes, nyc_map, warm_start,
use_domination_value, on_forward_graph):
# if on_forward_graph:
# print("---Computing on the forward graph---")
# else:
# print("---Computing on the backward graph---")
max_queue_size = 0 # debug
expansion_count = 0 # debug
# Compute pairwise distances between boundary nodes, so only good
# information is propagated
if warm_start:
# print("Warmstarting...")
touched_nodes = DijkstrasAlgorithm.initialize_boundary_nodes(
boundary_nodes, nyc_map, False, on_forward_graph)
else:
touched_nodes = boundary_nodes
# print("Running Dijkstra with " + str(len(boundary_nodes))
# + " boundary nodes.")
# Nodes we intend to search (somehow connected to graph so far). We
# treat this as a priority queue: the one that has the potential to be
# closest (has best distance from the start_node/is closest to the
# end_node) is treated next
nodes_to_search = Queue.PriorityQueue()
for node in touched_nodes:
if not(node.region_id == boundary_nodes[0].region_id and node.is_boundary_node):
if on_forward_graph:
node.forward_boundary_time = np.repeat(
float("INF"), len(boundary_nodes))
node.forward_predecessors = (
np.array([None] * len(boundary_nodes)))
else:
node.backward_boundary_time = np.repeat(
float("INF"), len(boundary_nodes))
node.backward_predecessors = (
np.array([None] * len(boundary_nodes)))
for add_node in boundary_nodes:
nodes_to_search.put((
# times updated since it was last expanded
add_node.get_priority_key(
use_domination_value, on_forward_graph),
# minimum time from boundary node
add_node.get_min_boundary_time(on_forward_graph),
# number of infinities in the list
add_node.get_boundary_time_inf_count(
on_forward_graph),
# sum of non infinities in the list
add_node.get_boundary_time_sum(on_forward_graph),
# the actual node itself
add_node))
while not nodes_to_search.empty():
# Gets the node closest to the end node in the best case
if (nodes_to_search.qsize() > max_queue_size):
max_queue_size = nodes_to_search.qsize()
queue_item = nodes_to_search.get()
_, old_min_time, old_inf_count, old_sum = queue_item[0:4]
curr_node = queue_item[4]
# Skip if the item in queue is out-dated
if(old_min_time > curr_node.get_min_boundary_time(
on_forward_graph) or
old_inf_count > curr_node.get_boundary_time_inf_count(
on_forward_graph) or
old_sum < curr_node.get_boundary_time_sum(on_forward_graph)):
continue
# Overwrite the snapshot with a copy of the current label
# np.copyto(curr_node.time_snapshot,
# curr_node.forward_boundary_time)
# expansion of curr_node starts here
expansion_count += 1
connecting_links = None
if on_forward_graph:
connecting_links = curr_node.backward_links
else:
connecting_links = curr_node.forward_links
for connected_link in connecting_links:
connected_node = None
if on_forward_graph:
connected_node = connected_link.origin_node
else:
connected_node = connected_link.connecting_node
if connected_link.time <= 0:
continue
time_from_boundary_node = None
connected_node_time = None
if on_forward_graph:
time_from_boundary_node = curr_node.forward_boundary_time
connected_node_time = connected_node.forward_boundary_time
else:
time_from_boundary_node = curr_node.backward_boundary_time
connected_node_time = connected_node.backward_boundary_time
# The proposed distances from all boundary nodes if you go
# through curr_node i.e. curr_node's distances from the
# boundary nodes + the length of this link
proposed_label = (time_from_boundary_node
+ connected_link.time)
# Only shorter paths are accepted - perform the element-wise
# min against current values
proposed_label = np.minimum(
proposed_label, connected_node_time)
# If there were any changes, copy and note them
if not np.array_equal(proposed_label, connected_node_time):
# Update the forward_predecessors for the connected nodes
# that has updates
updated = np.nonzero(connected_node_time
- proposed_label)[0]
if on_forward_graph:
connected_node.forward_predecessors[updated] = (
curr_node)
else:
connected_node.backward_predecessors[updated] = (
curr_node)
np.copyto(connected_node_time, proposed_label)
# Put the new connected_node into the priority queue
nodes_to_search.put((
# times updated since it was last expanded
connected_node.get_priority_key(
use_domination_value, on_forward_graph),
# minimum time from boundary node
connected_node.get_min_boundary_time(on_forward_graph),
# number of infinities in the list
connected_node.get_boundary_time_inf_count(
on_forward_graph),
# sum of non infinities in the list
connected_node.get_boundary_time_sum(on_forward_graph),
# the actual node itself
connected_node))
# print("Max Queue Size: " + str(max_queue_size)) # debug
# print("Number of expansions: " + str(expansion_count)) # debug
# Basically creates a tree rooted at the boundary node where every edge in
# the tree is an arcflag
@staticmethod
def bidirectional_dijkstra(boundary_nodes, nyc_map, warm_start,
use_domination_value):
# Assign sequential IDs to the boundary nodes of this region
DijkstrasAlgorithm.init_boundary_node_ids(boundary_nodes)
# print("Initializing...")
# Gives each node a distance from the boundary nodes, which are
# initially either INF(infinity) or 0
DijkstrasAlgorithm.initialize_nodes(boundary_nodes, nyc_map)
# print "processing forward graph"
DijkstrasAlgorithm.directed_dijkstra(boundary_nodes, nyc_map,
warm_start, use_domination_value,
on_forward_graph=True)
# print "processing backward graph"
DijkstrasAlgorithm.directed_dijkstra(boundary_nodes, nyc_map,
warm_start, use_domination_value,
on_forward_graph=False)
# DijkstrasAlgorithm.independent_dijkstra(boundary_nodes, nyc_map)
DijkstrasAlgorithm.set_arc_flags(nyc_map, boundary_nodes[0].region_id)
# print
# Runs a Dijkstra search independently for each boundary node.
@staticmethod
def independent_dijkstra(boundary_nodes, nyc_map):
# Assign each boundary node an i for distance
DijkstrasAlgorithm.init_boundary_node_ids(boundary_nodes)
print("initializing")
# Gives each node a distance from the boundary nodes, which are
# initially either INF(infinity) or 0
DijkstrasAlgorithm.initialize_nodes(boundary_nodes, nyc_map)
forward_total_expanded = 0
forward_overall_max_pq_size = 0
backward_total_expanded = 0
backward_overall_max_pq_size = 0
for boundary_node in boundary_nodes:
_, forward_num_expanded, forward_max_pq_size = aborted_dijkstra(
boundary_node, None, this_region_only=False,
on_forward_graph=True)
_, backward_num_expanded, backward_max_pq_size = aborted_dijkstra(
boundary_node, None, this_region_only=False,
on_forward_graph=False)
forward_total_expanded += forward_num_expanded
forward_overall_max_pq_size = max(forward_overall_max_pq_size,
forward_max_pq_size)
backward_total_expanded += backward_num_expanded
backward_overall_max_pq_size = max(backward_overall_max_pq_size,
backward_max_pq_size)
max_pq = max(forward_overall_max_pq_size, backward_overall_max_pq_size)
max_expand = max(forward_total_expanded, backward_total_expanded)
print("Max Queue Size:", max_pq) # debug
print("Number of expansions:", max_expand) # debug
return max_expand, max_pq
# Given where the nodes came from, rebuilds the path that was taken to the
# final node
@staticmethod
def set_arc_flags(nyc_map, curr_region_id):
for node in nyc_map.nodes:
# Set forward arc flags
for predecessor_node in node.forward_predecessors:
if predecessor_node is not None:
assignLink = nyc_map.links_by_node_id[(node.node_id, predecessor_node.node_id)]
assignLink.forward_arc_flags_vector[curr_region_id] = True
# Set backward arc flags
for predecessor_node in node.backward_predecessors:
if predecessor_node is not None:
assignLink = nyc_map.links_by_node_id[(predecessor_node.node_id, node.node_id)]
assignLink.backward_arc_flags_vector[curr_region_id] = True
for link in nyc_map.links:
connect_node = nyc_map.nodes_by_id[link.connecting_node_id]
forward_region_id = connect_node.region_id
link.forward_arc_flags_vector[forward_region_id] = True
origin_node = nyc_map.nodes_by_id[link.origin_node_id]
backward_region_id = origin_node.region_id
link.backward_arc_flags_vector[backward_region_id] = True
link.forward_arc_flags_vector[backward_region_id] = True
link.backward_arc_flags_vector[forward_region_id] = True
########################################################################################
# ArcFlags | graph | Predecessor Directions
#
# Forward | Backward | With Forward Graph
# Backward | Forward | against Forward Graph
#
########################################################################################