Skip to content

Commit

Permalink
Merge pull request #179 from jGaboardi/dijk_maint
Browse files Browse the repository at this point in the history
Dijkstra maintenance
  • Loading branch information
jGaboardi committed Dec 2, 2018
2 parents 03ceb12 + ff9dc3c commit 59f0bd6
Show file tree
Hide file tree
Showing 3 changed files with 43 additions and 47 deletions.
11 changes: 5 additions & 6 deletions spaghetti/network.py
Original file line number Diff line number Diff line change
Expand Up @@ -993,22 +993,23 @@ def node_distance_matrix(self, n_processes, gen_tree=False):
Based on :cite:`Dijkstra1959a`.
"""

self.alldistances = {}
nnodes = len(self.node_list)
self.distancematrix = np.empty((nnodes, nnodes))

# Single-core processing
if not n_processes:
for node in self.node_list:
distance, pred = util.dijkstra(self,
self.edge_lengths,
node)
distance, pred = util.dijkstra(self, node)
pred = np.array(pred)
if gen_tree:
tree = util.generatetree(pred)
else:
tree = None
self.alldistances[node] = (distance, tree)
self.distancematrix[node] = distance

# Multiprocessing
if n_processes:
import multiprocessing as mp
Expand All @@ -1019,9 +1020,7 @@ def node_distance_matrix(self, n_processes, gen_tree=False):
cores = n_processes
p = mp.Pool(processes=cores)
distance_pred = p.map(util.dijkstra_mp,
zip(repeat(self),
repeat(self.edge_lengths),
self.node_list))
zip(repeat(self), self.node_list))
iterations = range(len(distance_pred))
distance = [distance_pred[itr][0] for itr in iterations]
pred = np.array([distance_pred[itr][1] for itr in iterations])
Expand Down
9 changes: 3 additions & 6 deletions spaghetti/tests/test_network.py
Original file line number Diff line number Diff line change
Expand Up @@ -364,20 +364,17 @@ def test_get_neighbor_distances(self):

def test_generate_tree(self):
self.known_path = [23, 22, 20, 19, 170, 2, 0]
self.distance, self.pred = util.dijkstra(self.ntw,
self.ntw.edge_lengths, 0)
self.distance, self.pred = util.dijkstra(self.ntw, 0)
self.tree = util.generatetree(self.pred)
self.assertEqual(self.tree[3], self.known_path)

def test_dijkstra(self):
self.distance, self.pred = util.dijkstra(self.ntw,
self.ntw.edge_lengths, 0)
self.distance, self.pred = util.dijkstra(self.ntw, 0)
self.assertAlmostEqual(self.distance[196], 5505.668247, places=4)
self.assertEqual(self.pred[196], 133)

def test_dijkstra_mp(self):
self.distance, self.pred = util.dijkstra_mp((self.ntw,
self.ntw.edge_lengths, 0))
self.distance, self.pred = util.dijkstra_mp((self.ntw, 0))
self.assertAlmostEqual(self.distance[196], 5505.668247, places=4)
self.assertEqual(self.pred[196], 133)

Expand Down
70 changes: 35 additions & 35 deletions spaghetti/util.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ def compute_length(v0, v1):
1.4142135623730951
"""

euc_dist = np.sqrt((v0[0] - v1[0])**2 + (v0[1] - v1[1])**2)
return euc_dist

Expand Down Expand Up @@ -74,12 +75,12 @@ def get_neighbor_distances(ntw, v0, l):
>>> import spaghetti as spgh
>>> from libpysal import examples
>>> ntw = spgh.Network(examples.get_path('streets.shp'))
>>> neighs = spgh.util.get_neighbor_distances(ntw, 0,
... ntw.edge_lengths)
>>> neighs = spgh.util.get_neighbor_distances(ntw, 0, ntw.edge_lengths)
>>> neighs[1]
102.62353453439829
"""

edges = ntw.enum_links_node(v0)
neighbors = {}
for e in edges:
Expand Down Expand Up @@ -111,7 +112,7 @@ def generatetree(pred):
>>> import spaghetti as spgh
>>> from libpysal import examples
>>> ntw = spgh.Network(examples.get_path('streets.shp'))
>>> distance, pred = spgh.util.dijkstra(ntw, ntw.edge_lengths, 0)
>>> distance, pred = spgh.util.dijkstra(ntw, 0)
>>> tree = spgh.util.generatetree(pred)
>>> tree[3]
[23, 22, 20, 19, 170, 2, 0]
Expand All @@ -134,7 +135,7 @@ def generatetree(pred):
return tree


def dijkstra(ntw, cost, v0, n=float('inf')):
def dijkstra(ntw, v0, initial_dist=np.inf):
"""Compute the shortest path between a start node and all other
nodes in an origin-destination matrix.
Expand All @@ -144,16 +145,12 @@ def dijkstra(ntw, cost, v0, n=float('inf')):
ntw : spaghetti.Network
spaghetti Network object.
cost : dict
key is tuple (start node, end node); value is ``float``.
Cost per edge to travel, e.g. distance.
v0 : int
Start node ID
n : float
initial_dist : float
integer break point to stop iteration and return n neighbors.
Default is ``'inf'``.
Default is ``numpy.inf``.
Returns
-------
Expand All @@ -175,42 +172,47 @@ def dijkstra(ntw, cost, v0, n=float('inf')):
>>> import spaghetti as spgh
>>> from libpysal import examples
>>> ntw = spgh.Network(examples.get_path('streets.shp'))
>>> distance, pred = spgh.util.dijkstra(ntw, ntw.edge_lengths, 0)
>>> distance, pred = spgh.util.dijkstra(ntw, 0)
>>> round(distance[196], 4)
5505.6682
>>> pred[196]
133
"""

distance = [n for x in ntw.node_list]
# Cost per edge to travel, e.g. distance.
cost = ntw.edge_lengths

distance = [initial_dist for x in ntw.node_list]
idx = ntw.node_list.index(v0)
distance[ntw.node_list.index(v0)] = 0
pred = [-1 for x in ntw.node_list]
a = set()
a.add(v0)
while len(a) > 0:
unvisited, pred = set([v0]), [-1 for x in ntw.node_list]

while len(unvisited) > 0:
# Get node with the lowest value from distance.
dist = n
for node in a:
dist = initial_dist
for node in unvisited:
if distance[node] < dist:
dist = distance[node]
v = node
dist, current = distance[node], node

# Remove that node from the set.
a.remove(v)
last = v
# 4. Get the neighbors to the current node.
neighbors = get_neighbor_distances(ntw, v, cost)
unvisited.remove(current)

# Get the neighbors to the current node.
neighbors = get_neighbor_distances(ntw, current, cost)
for v1, indiv_cost in neighbors.items():
if distance[v1] > distance[v] + indiv_cost:
distance[v1] = distance[v] + indiv_cost
pred[v1] = v
a.add(v1)
if distance[v1] > distance[current] + indiv_cost:
distance[v1] = distance[current] + indiv_cost
pred[v1] = current
unvisited.add(v1)

pred = np.array(pred, dtype=np.int)

return distance, pred


def dijkstra_mp(ntw_cost_node):
def dijkstra_mp(ntw_node):
"""
Compute the shortest path between a start node and all other
nodes in the web utilizing multiple cores upon request.
Expand All @@ -221,9 +223,7 @@ def dijkstra_mp(ntw_cost_node):
ntw_cost_node : tuple
tuple of arguments to pass into dijkstra as
(1) ntw - ``spaghetti.Network; spaghetti Network object``;
(2) cost - ``dict`` keyed by tuple (start node, end node)
with values is ``float`` - Cost per edge to travel, e.g. dist.;
(3) node - ``int``; Start node ID
(2) node - ``int``; Start node ID
Returns
-------
Expand All @@ -245,16 +245,16 @@ def dijkstra_mp(ntw_cost_node):
>>> import spaghetti as spgh
>>> from libpysal import examples
>>> ntw = spgh.Network(examples.get_path('streets.shp'))
>>> distance, pred = spgh.util.dijkstra(ntw, ntw.edge_lengths, 0)
>>> distance, pred = spgh.util.dijkstra(ntw, 0)
>>> round(distance[196], 4)
5505.6682
>>> pred[196]
133
"""

ntw, cost, node = ntw_cost_node
distance, pred = dijkstra(ntw, cost, node)
ntw, node = ntw_node
distance, pred = dijkstra(ntw, node)
return distance, pred


Expand Down

0 comments on commit 59f0bd6

Please sign in to comment.