Skip to content

Commit

Permalink
Merge 87e79a3 into 4dc265a
Browse files Browse the repository at this point in the history
  • Loading branch information
smmaurer committed Nov 17, 2020
2 parents 4dc265a + 87e79a3 commit b1e969e
Show file tree
Hide file tree
Showing 8 changed files with 102 additions and 7 deletions.
14 changes: 12 additions & 2 deletions examples/shortest_path_example.py
Expand Up @@ -71,16 +71,26 @@
print(net.shortest_path_length(nodes_a[1],nodes_b[1]))

print('Repeat with vectorized calculations:')
print(net.shortest_paths(nodes_a[0:2],nodes_b[0:2]))
print(net.shortest_path_lengths(nodes_a[0:2],nodes_b[0:2]))

# Performance comparison
print('Performance comparison for 10k distance calculations:')

t0 = time.time()
for i in range(n):
_ = net.shortest_path(nodes_a[i], nodes_b[i])
print('Route loop time = {} sec'.format(time.time() - t0))

t0 = time.time()
_ = net.shortest_paths(nodes_a, nodes_b)
print('Route vectorized time = {} sec'.format(time.time() - t0))

t0 = time.time()
for i in range(n):
_ = net.shortest_path_length(nodes_a[i], nodes_b[i])
print('Loop time = {} sec'.format(time.time() - t0))
print('Distance loop time = {} sec'.format(time.time() - t0))

t0 = time.time()
_ = net.shortest_path_lengths(nodes_a, nodes_b)
print('Vectorized time = {} sec'.format(time.time() - t0))
print('Distance vectorized time = {} sec'.format(time.time() - t0))
2 changes: 1 addition & 1 deletion pandana/__init__.py
@@ -1,3 +1,3 @@
from .network import Network

version = __version__ = '0.5'
version = __version__ = '0.6.dev0'
37 changes: 37 additions & 0 deletions pandana/network.py
Expand Up @@ -199,6 +199,43 @@ def shortest_path(self, node_a, node_b, imp_name=None):
# map back to external node ids
return self.node_ids.values[path]

def shortest_paths(self, nodes_a, nodes_b, imp_name=None):
"""
Vectorized calculation of shortest paths. Accepts a list of origins
and list of destinations and returns a corresponding list of
shortest path routes. Must provide an impedance name if more than
one is available.
Parameters
----------
nodes_a : list-like of ints
Source node ids
nodes_b : list-like of ints
Corresponding destination node ids
imp_name : string
The impedance name to use for the shortest path
Returns
-------
paths : list of np.ndarray
Nodes traversed in each shortest path
"""
if len(nodes_a) != len(nodes_b):
raise ValueError("Origin and destination counts don't match: {}, {}"
.format(len(nodes_a), len(nodes_b)))

# map to internal node indexes
nodes_a_idx = self._node_indexes(pd.Series(nodes_a)).values
nodes_b_idx = self._node_indexes(pd.Series(nodes_b)).values

imp_num = self._imp_name_to_num(imp_name)

paths = self.net.shortest_paths(nodes_a_idx, nodes_b_idx, imp_num)

# map back to external node ids
return [self.node_ids.values[p] for p in paths]

def shortest_path_length(self, node_a, node_b, imp_name=None):
"""
Return the length of the shortest path between two node ids in the
Expand Down
17 changes: 17 additions & 0 deletions pandana/tests/test_pandana.py
Expand Up @@ -268,6 +268,23 @@ def test_shortest_path(sample_osm):
assert ids[1] == path[-1]


def test_shortest_paths(sample_osm):

nodes = random_connected_nodes(sample_osm, 100)
vec_paths = sample_osm.shortest_paths(nodes[0:50], nodes[50:100])

for i in range(50):
path = sample_osm.shortest_path(nodes[i], nodes[i+50])
assert(np.array_equal(vec_paths[i], path))

# check mismatched OD lists
try:
vec_paths = sample_osm.shortest_paths(nodes[0:51], nodes[50:100])
assert 0
except ValueError as e:
pass


def test_shortest_path_length(sample_osm):

for i in range(10):
Expand Down
2 changes: 1 addition & 1 deletion setup.py
Expand Up @@ -131,7 +131,7 @@ def run(self):
## Standard setup
###############################################

version = '0.5'
version = '0.6.dev0'

packages = find_packages(exclude=["*.tests", "*.tests.*", "tests.*", "tests"])

Expand Down
23 changes: 20 additions & 3 deletions src/accessibility.cpp
Expand Up @@ -96,24 +96,41 @@ Accessibility::precomputeRangeQueries(float radius) {
}


std::vector<int>
vector<int>
Accessibility::Route(int src, int tgt, int graphno) {
vector<NodeID> ret = this->ga[graphno]->Route(src, tgt);
return vector<int> (ret.begin(), ret.end());
}


vector<vector<int>>
Accessibility::Routes(vector<long> sources, vector<long> targets, int graphno) {

int n = std::min(sources.size(), targets.size()); // in case lists don't match
vector<vector<int>> routes(n);

#pragma omp parallel
#pragma omp for schedule(guided)
for (int i = 0 ; i < n ; i++) {
vector<NodeID> ret = this->ga[graphno]->Route(sources[i], targets[i],
omp_get_thread_num());
routes[i] = vector<int> (ret.begin(), ret.end());
}
return routes;
}


double
Accessibility::Distance(int src, int tgt, int graphno) {
return this->ga[graphno]->Distance(src, tgt);
}


std::vector<double>
vector<double>
Accessibility::Distances(vector<long> sources, vector<long> targets, int graphno) {

int n = std::min(sources.size(), targets.size()); // in case lists don't match
vector<double> distances (n);
vector<double> distances(n);

#pragma omp parallel
#pragma omp for schedule(guided)
Expand Down
4 changes: 4 additions & 0 deletions src/accessibility.h
Expand Up @@ -51,6 +51,10 @@ class Accessibility {
// shortest path between two points
vector<int> Route(int src, int tgt, int graphno = 0);

// shortest path between list of origins and destinations
vector<vector<int>> Routes(vector<long> sources, vector<long> targets,
int graphno = 0);

// shortest path distance between two points
double Distance(int src, int tgt, int graphno = 0);

Expand Down
10 changes: 10 additions & 0 deletions src/cyaccess.pyx
Expand Up @@ -24,6 +24,7 @@ cdef extern from "accessibility.h" namespace "MTC::accessibility":
vector[double] getAllAggregateAccessibilityVariables(
float, string, string, string, int)
vector[int] Route(int, int, int)
vector[vector[int]] Routes(vector[long], vector[long], int)
double Distance(int, int, int)
vector[double] Distances(vector[long], vector[long], int)
void precomputeRangeQueries(double)
Expand Down Expand Up @@ -165,6 +166,15 @@ cdef class cyaccess:
"""
return self.access.Route(srcnode, destnode, impno)

def shortest_paths(self, np.ndarray[long] srcnodes,
np.ndarray[long] destnodes, int impno=0):
"""
srcnodes - node ids of origins
destnodes - node ids of destinations
impno - impedance id
"""
return self.access.Routes(srcnodes, destnodes, impno)

def shortest_path_distance(self, int srcnode, int destnode, int impno=0):
"""
srcnode - node id origin
Expand Down

0 comments on commit b1e969e

Please sign in to comment.