def test_build_adhoc_network(): import functools same_edge_p = functools.partial(Edge.same_edge, precision=0.0000001) # It should simply do it right edge_locations = ((Edge(id=1, start_node=1, end_node=10, cost=100, reverse_cost=1000), 0.5),) adhoc_nodes, adhoc_network = build_adhoc_network(edge_locations) assert len(adhoc_nodes) == 1 node = adhoc_nodes[0] assert isinstance(node, AdHocNode) backward_edge, forward_edge = adhoc_network[node] backward_edge = backward_edge.reversed_edge() assert same_edge_p(backward_edge, Edge(id=1, start_node=1, end_node=node, cost=50, reverse_cost=500)) assert same_edge_p(forward_edge, Edge(id=1, start_node=node, end_node=10, cost=50, reverse_cost=500)) assert backward_edge == adhoc_network[1][0] assert backward_edge.reversed_edge() == adhoc_network[node][0] assert forward_edge == adhoc_network[node][1] assert forward_edge.reversed_edge() == adhoc_network[10][0] # It should do it simply right for 2 edge locations edge_locations = ((Edge(id=1, start_node=1, end_node=10, cost=100, reverse_cost=1000), 0.5), (Edge(id=2, start_node=3, end_node=5, cost=100, reverse_cost=1000), 0.4)) adhoc_nodes, adhoc_network = build_adhoc_network(edge_locations) assert len(adhoc_nodes) == 2 # It should do it right at 3 locations at the same edge edge = Edge(id=1, start_node=1, end_node=10, cost=100, reverse_cost=1000) edge_locations = ((edge, 0.5), (edge.reversed_edge(), 0.4), (edge.reversed_edge(), 0)) adhoc_nodes, adhoc_network = build_adhoc_network(edge_locations) # 1 -------------> n0 --> n1 -----------> n2 (10) n0, n1, n2 = adhoc_nodes assert same_edge_p(adhoc_network[1][0], Edge(id=1, start_node=1, end_node=n0, cost=50, reverse_cost=500)) b0, f0 = adhoc_network[n0] assert b0 == adhoc_network[1][0].reversed_edge() assert same_edge_p(f0, Edge(id=1, start_node=n0, end_node=n1, cost=10, reverse_cost=100)) b1, f1 = adhoc_network[n1] assert b1 == f0.reversed_edge() assert same_edge_p(f1, Edge(id=1, start_node=n1, end_node=n2, cost=40, reverse_cost=400)) assert n2 == 10 assert same_edge_p(adhoc_network[n2][0], Edge(id=1, start_node=n1, end_node=n2, cost=40, reverse_cost=400).reversed_edge())
def test_split_edge(): import functools same_edge_p = functools.partial(Edge.same_edge, precision=0.0000001) edge = Edge(id=1, start_node=2, end_node=10, cost=100, reverse_cost=1000) # It should simply do it right adhoc_node_edges = split_edge(edge, [0.5]) assert len(adhoc_node_edges) == 1 n, b, f = adhoc_node_edges[0] assert n == AdHocNode(edge_id=edge.id, location= 0.5) assert same_edge_p(b, Edge(id=edge.id, start_node=edge.start_node, end_node=n, cost=edge.cost * 0.5, reverse_cost=edge.reverse_cost * 0.5)) assert same_edge_p(f, Edge(id=edge.id, start_node=n, end_node=10, cost=edge.cost * 0.5, reverse_cost=edge.reverse_cost * 0.5)) assert not b.reversed and not f.reversed # It should split reversed edge redge = edge.reversed_edge() adhoc_node_edges = split_edge(redge, [0.5]) n, b, f = adhoc_node_edges[0] assert b.reversed and f.reversed # It should split the edge by 2 locations adhoc_node_edges = split_edge(edge, [0.5, 0.4]) assert len(adhoc_node_edges) == 2 (n2, b2, f2), (n1, b1, f1) = adhoc_node_edges assert same_edge_p(b1, Edge(id=edge.id, start_node=edge.start_node, end_node=n1, cost=edge.cost * 0.4, reverse_cost=edge.reverse_cost * 0.4)) assert same_edge_p(f1, Edge(id=edge.id, start_node=n1, end_node=n2, cost=edge.cost * 0.1, reverse_cost=edge.reverse_cost * 0.1)) assert b2 == f1 assert same_edge_p(f2, Edge(id=edge.id, start_node=n2, end_node=edge.end_node, cost=edge.cost * 0.5, reverse_cost=edge.reverse_cost * 0.5)) # It should split the edge at starting location adhoc_node_edges = split_edge(edge, [0]) assert len(adhoc_node_edges) == 1 n, b, f = adhoc_node_edges[0] assert n == edge.start_node assert b is None assert f == edge # It should split the edge at ending location adhoc_node_edges = split_edge(edge, [1]) assert len(adhoc_node_edges) == 1 n, b, f = adhoc_node_edges[0] assert n == edge.end_node assert b == edge assert f is None # It should do all right adhoc_node_edges = split_edge(edge, [1, 0.4, 0, 0.4, 0, 0.5]) assert len(adhoc_node_edges) == 6 # Do this because Python gurantees stable sort n0, b0, f0 = adhoc_node_edges[2] n1, b1, f1 = adhoc_node_edges[4] n2, b2, f2 = adhoc_node_edges[1] n3, b3, f3 = adhoc_node_edges[3] n4, b4, f4 = adhoc_node_edges[5] n5, b5, f5 = adhoc_node_edges[0] assert n0 == edge.start_node and b0 is None and f0 is None assert n1 == edge.start_node and b1 is None assert same_edge_p(f1, Edge(id=edge.id, start_node=n1, end_node=n2, cost=edge.cost * 0.4, reverse_cost=edge.reverse_cost * 0.4)) assert isinstance(n2, AdHocNode) assert b2 == f1 assert same_edge_p(f2, Edge(id=edge.id, start_node=n2, end_node=n3, cost=0, reverse_cost=0)) assert isinstance(n3, AdHocNode) assert b3 == f2 assert same_edge_p(f3, Edge(id=edge.id, start_node=n3, end_node=n4, cost=edge.cost * 0.1, reverse_cost=edge.reverse_cost * 0.1)) assert isinstance(n4, AdHocNode) assert b4 == f3 assert same_edge_p(f4, Edge(id=edge.id, start_node=n4, end_node=n5, cost=edge.cost * 0.5, reverse_cost=edge.reverse_cost * 0.5)) assert n5 == edge.end_node assert b5 == f4 assert f5 is None
def test_road_network_route(): # The example from http://en.wikipedia.org/wiki/Dijkstra's_algorithm e12 = Edge('12', 1, 2, 7, 7) e13 = Edge('13', 1, 3, 9, 9) e16 = Edge('16', 1, 6, 14, 14) e23 = Edge('23', 2, 3, 10, 10) e24 = Edge('24', 2, 4, 15, 15) e34 = Edge('34', 3, 4, 11, 11) e36 = Edge('36', 3, 6, 2, 2) e45 = Edge('45', 4, 5, 6, 6) e56 = Edge('56', 5, 6, 9, 9) # Extra isolated edge e89 = Edge('89', 8, 9, 2, 1000) ecircle = Edge('cc', 'c', 'c', 100000, 1) edges = (e12, e13, e16, e23, e24, e34, e36, e45, e56, e89) road_network = { 1: (e12, e13, e16), 2: (e12.reversed_edge(), e23, e24), 3: (e13.reversed_edge(), e23.reversed_edge(), e34, e36), 4: (e24.reversed_edge(), e34.reversed_edge(), e45), 5: (e45.reversed_edge(), e56), 6: (e16.reversed_edge(), e36.reversed_edge(), e56.reversed_edge()), # Extra isolated edges 8: (e89, ), 9: (e89.reversed_edge(),)} def _get_edges(node): return road_network.get(node, []) _AHN = collections.namedtuple('AdhocNodeForTest', 'edge_id, location, reversed') def _assert_path(path, nodes): if path or nodes: assert len(nodes) == len(path) + 1, 'count not matched' else: return path = reversed(path) nodes = iter(nodes) last_edge = None for edge in path: node = next(nodes) if isinstance(node, _AHN): assert node.edge_id == edge.id assert node.location == edge.start_node.location assert node.reversed == edge.reversed else: assert node == edge.start_node if last_edge: assert last_edge.end_node == edge.start_node last_edge = edge # Last node node = next(nodes) if isinstance(node, _AHN): assert node.edge_id == edge.id assert node.location == edge.end_node.location assert node.reversed == edge.reversed else: assert node == edge.end_node # It should route between 2 locations at different edges path, cost = road_network_route((e13, 0.5), (e56, 0.5), _get_edges) _assert_path(path, [_AHN('13', 0.5, False), 3, 6, _AHN('56', 0.5, True)]) assert abs(cost - 11) <= 0.000001 # It should route between 2 locations at the same edge path, cost = road_network_route((e13, 0.1), (e13, 0.9), _get_edges) _assert_path(path, [_AHN('13', 0.1, False), _AHN('13', 0.9, False)]) assert abs(cost - 9 * 0.8) <= 0.000001 # It should route between 2 locations at a circle edge (start node == end node) in a reverse way path1, cost1 = road_network_route((ecircle, 0.2), (ecircle, 0.7), _get_edges) path2, cost2 = road_network_route((ecircle, 0.2), (ecircle.reversed_edge(), 0.3), _get_edges) assert path1 == path2 and cost1 == cost2 _assert_path(path1, [_AHN('cc', 0.2, True), 'c', _AHN('cc', 0.7, True)]) assert abs(cost1 - 0.5) <= 0.0000001 # It should give 0 cost if source and target are same location path, cost = road_network_route((e13, 0.1), (e13.reversed_edge(), 0.9), _get_edges) _assert_path(path, [_AHN('13', 0.1, True), _AHN('13', 1 - 0.9, True)]) assert abs(cost) <= 0.000001 assert cost == path[0].cost # It should route for locations at intersections path, cost = road_network_route((e13, 0), (e13, 1), _get_edges) _assert_path(path, [1, 3]) assert path[0] == e13 assert cost == e13.cost # It should not find a path from nose.tools import assert_raises assert_raises(sp.PathNotFound, road_network_route, (e13, 0.2), (e24, 0.9), _get_edges, 10) assert_raises(sp.PathNotFound, road_network_route, (e13, 0), (e89, 0.5), _get_edges) assert_raises(sp.PathNotFound, road_network_route, (e89, 0.9), (e89, 0.2), _get_edges, 10) # It should return multiple paths targets = [(e16, 0.6), (e13, 0.3), (e34, 0.5), (e56, 1)] results = road_network_route_many((e16, 0.1), targets, _get_edges) path, cost = results[0] assert abs(cost - 7) < 0.000001 _assert_path(path, [_AHN('16', 0.1, False), _AHN('16', 0.6, False)]) path, cost = results[1] assert abs(cost - 4.1) < 0.000001 _assert_path(path, [_AHN('16', 0.1, True), 1, _AHN('13', 0.3, False)]) path, cost = results[2] assert abs(cost - 15.9) < 0.000001 _assert_path(path, [_AHN('16', 0.1, True), 1, 3, _AHN('34', 0.5, False)]) path, cost = results[3] assert abs(cost - 12.4) < 0.000001 _assert_path(path, [_AHN('16', 0.1, True), 1, 3, 6]) # It should find paths when multiple targets are on the same edge with the source targets = [(e16, 0.2), (e16, 0.4), (e16, 1), (e16, 0)] results = road_network_route_many((e16, 0.8), targets, _get_edges) path, cost = results[0] _assert_path(path, [_AHN('16', 0.8, True), _AHN('16', 0.4, True), _AHN('16', 0.2, True)]) assert abs(cost - 8.4) < 0.000001 path, cost = results[1] _assert_path(path, [_AHN('16', 0.8, True), _AHN('16', 0.4, True)]) assert abs(cost - 5.6) < 0.000001 path, cost = results[2] _assert_path(path, [_AHN('16', 0.8, False), 6]) assert abs(cost - 2.8) < 0.000001 path, cost = results[3] _assert_path(path, [_AHN('16', 0.8, True), _AHN('16', 0.4, True), _AHN('16', 0.2, True), 1]) assert abs(cost - 11.2) < 0.000001 # It should find paths on the circle edge targets = [(ecircle, 0.8), (ecircle, 0.7), (ecircle, 0.1)] results = road_network_route_many((ecircle, 0.2), targets, _get_edges) path, cost = results[0] assert (cost - 0.4) < 0.0000001 _assert_path(path, [_AHN('cc', 0.2, True), _AHN('cc', 0.1, True), 'c', _AHN('cc', 0.8, True)]) path, cost = results[1] assert (cost - 0.5) < 0.0000001 _assert_path(path, [_AHN('cc', 0.2, True), _AHN('cc', 0.1, True), 'c', _AHN('cc', 0.8, True), _AHN('cc', 0.7, True)]) path, cost = results[2] assert (cost - 0.1) < 0.0000001 _assert_path(path, [_AHN('cc', 0.2, True), _AHN('cc', 0.1, True)]) # It should not find a path to the isolated edge targets = [(e13, 0.3), (e89, 0.2), (e34, 0.5)] results = road_network_route_many((e13, 0.3), targets, _get_edges) assert results[0][1] >= 0 and results[2][1] >= 0 assert results[1] == (None, -1) # It should not find a path if the cost exceeds the max_path_cost results = road_network_route_many((e89, 0.9), [(e89, 0.8), (e89, 0.1)], _get_edges, 200) path, cost = results[0] assert abs(cost - 100) < 0.00001 assert results[1] == (None, -1) # One-to-many routing should be the same as calling one-to-one # multiple times import random source = (e13, random.random()) # Generate 20 locations at each edge targets = [(edge, random.random()) for edge in edges for _ in range(20)] def _route_many_hard_way(source, targets): route_distances = [] for target in targets: try: _, route_distance = road_network_route(source, target, _get_edges) except sp.PathNotFound: route_distance = -1 route_distances.append(route_distance) return route_distances hard_ways = _route_many_hard_way(source, targets) # Get costs in the second column easy_ways = list(zip(*road_network_route_many(source, targets, _get_edges)))[1] for hard_way, easy_way in zip(hard_ways, easy_ways): assert abs(hard_way - easy_way) < 0.0000000001