Esempio n. 1
0
def create_rpp_edgelist(g_strongly_connected, graph_full, edge_weight='distance', max_distance=1600):
    """
    Create the edgelist for the RPP algorithm.  This includes:
     - Required state edges (deduped)
     - Required non-state roads that connect state roads into one connected component with minimum additional distance
     - Optional roads that connect the nodes of the contracted state edges (these distances are calculated here using
       first haversine distance to filter the candidate set down using `max_distance` as a threshold, then calculating
       the true shortest path distance)
    Args:
        g_strongly_connected (networkx MultiDiGraph): of strongly connected roads
        graph_full (networkx Graph): full graph with all granular edges
        edge_weight (str): edge attribute for distance in `g_strongly_connected` and `graph_full`
        max_distance (int): max haversine distance used to add candidate optional edges for.
    Returns:
        Dataframe of edgelist described above.
    """

    dfrpp_list = []
    for e in g_strongly_connected.edges(data=True):
        if 'granular' not in e[2]:
            dfrpp_list.append({
                'start_node': e[0],
                'end_node': e[1],
                'distance_haversine': e[2]['distance'],
                'required': 1,
                'distance': e[2]['distance'],
                'path': e[2]['path'],
                'length': e[2]['length']
            })

    for n1, n2 in [comb for comb in itertools.combinations(g_strongly_connected.nodes(), 2)]:
        if n1 == n2:
            continue

        if not g_strongly_connected.has_edge(n1, n2):
            distance_haversine = haversine(g_strongly_connected.nodes[n1]['x'], g_strongly_connected.nodes[n1]['y'],
                                           g_strongly_connected.nodes[n2]['x'], g_strongly_connected.nodes[n2]['y'])

            # only add optional edges whose haversine distance is less than `max_distance`
            if distance_haversine > max_distance:
                continue

            dfrpp_list.append({
                'start_node': n1,
                'end_node': n2,
                'distance_haversine': distance_haversine,
                'required': 0,
                'distance': spm.dijkstra_path_length(graph_full, n1, n2, edge_weight),
                'path': spm.dijkstra_path(graph_full, n1, n2, edge_weight),
                'length': nx.dijkstra_path_length(graph_full, n1, n2, 'length')
            })

    # create dataframe
    dfrpp = pd.DataFrame(dfrpp_list)

    # create order
    dfrpp = dfrpp[['start_node', 'end_node', 'distance_haversine', 'distance', 'required', 'path', 'length']]

    return dfrpp
Esempio n. 2
0
def get_shortest_paths_distances(graph, pairs, edge_weight_name='distance'):
    """
    Calculate shortest distance between each pair of nodes in a graph
    Args:
        graph (networkx graph)
        pairs (list[2tuple]): List of length 2 tuples containing node pairs to calculate shortest path between
        edge_weight_name (str): edge attribute used for distance calculation
    Returns:
        dict: mapping each pair in `pairs` to the shortest path using `edge_weight_name` between them.
    """
    distances = {}
    for pair in pairs:
        distances[pair] = spm.dijkstra_path_length(graph,
                                                   pair[0],
                                                   pair[1],
                                                   weight=edge_weight_name)
    return distances
Esempio n. 3
0
def add_augmenting_path_to_graph(g_req,
                                 g_full,
                                 min_weight_pairs,
                                 edge_weight_name='distance'):
    """
    Add the min weight matching edges to the original graph
    Note the resulting graph could (and likely will) have edges that didn't exist on the original graph.  To get the
    true circuit, we must breakdown these augmented edges into the shortest path through the edges that do exist.  This
    is done with `create_eulerian_circuit`.
    Args:
        graph (networkx graph):
        min_weight_pairs (list[2tuples): output of `dedupe_matching` specifying the odd degree nodes to link together
        edge_weight_name (str): edge attribute used for distance calculation
    Returns:
        networkx graph: `graph` augmented with edges between the odd nodes specified in `min_weight_pairs`
    """
    graph_aug = g_req.copy()  # so we don't mess with the original graph
    for pair in min_weight_pairs:
        path = spm.dijkstra_path(g_full,
                                 pair[0],
                                 pair[1],
                                 weight=edge_weight_name)
        turn_length = g_full[path[0]][path[1]][0]['turn_length']
        graph_aug.add_edge(
            pair[0], pair[1], **{
                'distance':
                spm.dijkstra_path_length(g_full,
                                         pair[0],
                                         pair[1],
                                         weight=edge_weight_name),
                'augmented':
                True,
                'turn_length':
                turn_length,
                'length':
                nx.dijkstra_path_length(g_full,
                                        pair[0],
                                        pair[1],
                                        weight='length')
            })
    return graph_aug
Esempio n. 4
0
def find_minimum_weight_edges_to_connect_components(dfsp, graph, edge_weight='distance', top=10):
    """
    Given a dataframe of haversine distances between many pairs of nodes, calculate the min weight way to connect all
    the components in `graph`.  At each iteration, the true shortest path (dijkstra_path_length) is calculated for the
     top `top` closest node pairs using haversine distance.  This heuristic improves efficiency at the cost of
     potentially not finding the true min weight connectors.  If this is a concern, increase `top`.
    Args:
        dfsp (dataframe): calculated with `shortest_paths_between_components` with haversine distance between all node
                          candidate node pairs
        graph (networkx graph): used for the true shortest path calculation
        edge_weight (str): edge attribute used shortest path calculation in `graph`
        top (int): number of pairs for which shortest path calculation is performed at each iteration
    Returns:
        list[tuple3] containing just the connectors needed to connect all the components in `graph`.
    """

    # find shortest edges to add to make one big connected component
    dfsp = dfsp.copy()
    new_required_edges = []
    while sum(dfsp.index[dfsp['start_comp'] != dfsp['end_comp']]) > 0:

        # calculate path distance for top 10 shortest
        dfsp['path_distance'] = None
        dfsp['path'] = dfsp['path2'] = [[]] * len(dfsp)

        for i in dfsp.index[dfsp['start_comp'] != dfsp['end_comp']][0:top]:
            if dfsp.loc[i]['path_distance'] is None:
                dfsp.loc[i, 'path_distance'] = spm.dijkstra_path_length(graph,
                                                                       dfsp.loc[i, 'start_node'],
                                                                       dfsp.loc[i, 'end_node'],
                                                                       edge_weight)
                dfsp.at[i, 'path'] = spm.dijkstra_path(graph,
                                                      dfsp.loc[i, 'start_node'],
                                                      dfsp.loc[i, 'end_node'],
                                                      edge_weight)
                dfsp.at[i, 'length'] = nx.dijkstra_path_length(graph,
                                                      dfsp.loc[i, 'start_node'],
                                                      dfsp.loc[i, 'end_node'],
                                                      'length')

        dfsp.sort_values('path_distance', inplace=True)

        # The first index where start and end comps are different is the index containing the shortest connecting path between start comp and end comp
        first_index = dfsp.index[dfsp['start_comp'] != dfsp['end_comp']][0]
        start_comp = dfsp.loc[first_index]['start_comp']
        end_comp = dfsp.loc[first_index]['end_comp']
        start_node = dfsp.loc[first_index]['start_node']
        end_node = dfsp.loc[first_index]['end_node']
        path_distance = dfsp.loc[first_index]['path_distance']
        path = dfsp.loc[first_index]['path']
        length = dfsp.loc[first_index]['length']
        
        dfsp_rev_pair = dfsp[dfsp['start_comp'] == end_comp] 
        dfsp_rev_pair = dfsp_rev_pair[dfsp_rev_pair['end_comp'] == start_comp]
        for i in dfsp_rev_pair.index[0:top]: 
             if dfsp_rev_pair.loc[i]['path_distance'] is None:
                dfsp_rev_pair.loc[i, 'path_distance'] = spm.dijkstra_path_length(graph,
                                                                       dfsp_rev_pair.loc[i, 'start_node'],
                                                                       dfsp_rev_pair.loc[i, 'end_node'],
                                                                       edge_weight)
                dfsp_rev_pair.at[i, 'path'] = spm.dijkstra_path(graph,
                                                      dfsp_rev_pair.loc[i, 'start_node'],
                                                      dfsp_rev_pair.loc[i, 'end_node'],
                                                      edge_weight)
                dfsp_rev_pair.at[i, 'length'] = nx.dijkstra_path_length(graph,
                                                      dfsp.loc[i, 'start_node'],
                                                      dfsp.loc[i, 'end_node'],
                                                      'length')

        dfsp_rev_pair.sort_values('path_distance', inplace=True)

        first_index_rev = dfsp_rev_pair.index[0]
        start_node_rev = dfsp_rev_pair.loc[first_index_rev]['start_node']
        end_node_rev = dfsp_rev_pair.loc[first_index_rev]['end_node']
        path_distance_rev = dfsp_rev_pair.loc[first_index_rev]['path_distance']
        path_rev = dfsp_rev_pair.loc[first_index_rev]['path']
        length_rev = dfsp_rev_pair.loc[first_index_rev]['length']
           
        dfsp.loc[dfsp['end_comp'] == end_comp, 'end_comp'] = start_comp
        dfsp.loc[dfsp['start_comp'] == end_comp, 'start_comp'] = start_comp
        dfsp.sort_values('haversine_distance', inplace=True)
        new_required_edges.append((start_node, end_node, {'distance': path_distance, 'path': path, 'length': length, 'start_comp': start_comp, 'end_comp': end_comp}))
        new_required_edges.append((start_node_rev, end_node_rev, {'distance': path_distance_rev, 'length': length_rev, 'path': path_rev, 'start_comp': end_comp, 'end_comp': start_comp}))

    return new_required_edges