def _simplify_graph(g: nx.MultiDiGraph, check_lanes): for n, _ in tqdm(list(g.adjacency()), desc="simplifying oneways"): if g.out_degree(n) == 1 and g.in_degree(n) == 1: # oneways simplify_oneways(n, g, check_lanes) for n, _ in tqdm(list(g.adjacency()), desc="simplifying bidirectional"): if g.out_degree(n) == 2 and g.in_degree( n) == 2: # both directions in highway simplify_twoways(n, g, check_lanes)
def simplify_graph(g: nx.MultiDiGraph, check_lanes): for n, _ in list(g.adjacency()): if g.out_degree(n) == 1 and g.in_degree(n) == 1: # oneways simplify_oneways(n, g, check_lanes) for n, _ in list(g.adjacency()): if g.out_degree(n) == 2 and g.in_degree( n) == 2: # both directions in highway simplify_twoways(n, g, check_lanes)
def convert_multidi_to_weighted_undir_graph(in_graph: nx.MultiDiGraph, agg_function: Any) -> nx.Graph: """Convert a graph with multiple edges to a graph with no multiple edges. Arguments: in_graph: graph with (potentially) multiple directed edges per node pair. agg_function: a function that takes an iterable of floats and returns a number. Applied to weights on multiple edges to produce one weight. Returns: out_graph: graph with weighted undirected edges. """ edges = collections.defaultdict(dict) for node, neighbor_dict in in_graph.adjacency(): for neighbor, edges_dict in neighbor_dict.items(): id_pair = tuple(sorted([node, neighbor])) if 'weight' not in edges[id_pair]: edges[id_pair]['weight'] = [] for _, edge_data in edges_dict.items(): # Some input edges have artificially high weight [1] in to prevent # an agent from visiting the center of a POI. So, the input is also # given a "true_length" attribute [2] so that the graph construction # can use the actual distance. # [1] See cabby.geo.map_processing.map_structure.ADD_POI_DISTANCE # [2] See cabby.geo.map_processing.edge if 'true_length' in edge_data: weight = edge_data['true_length'] else: weight = edge_data['length'] edges[id_pair]['weight'].append(weight) for id_pair in edges: edges[id_pair]['weight'] = agg_function(edges[id_pair]['weight']) out_graph = nx.Graph() out_graph.add_edges_from( [id_pair + (data, ) for id_pair, data in edges.items()]) return out_graph
def _get_max_id_in_graph(g: nx.MultiDiGraph) -> int: max_id = -1 for n, nbrdict in g.adjacency(): for nbr, attrs in nbrdict.items(): for i in range(len(attrs)): if max_id < attrs[i]['id']: max_id = attrs[i]['id'] return max_id
def create_digraph(graph: nx.MultiDiGraph) -> nx.DiGraph: if not _is_multidigraph(graph): return nx.DiGraph(graph) # id_counter = _get_max_id_in_graph(graph) + 1 new_graph = nx.DiGraph() for n, nbrdict in graph.adjacency(): for nbr, attributes in nbrdict.items(): if not type(attributes) is dict: id_counter = 0 for i in range(len(attributes)): if attributes[i]['others'] == [[]]: new_graph.add_edge(n, nbr, id='{}_{}'.format( attributes[i]['id'], id_counter), lanes=attributes[i]['lanes'], others=attributes[i]['others']) id_counter += 1 else: temp_nbr = get_node(attributes[i]['others'][0]) new_graph.add_edge(n, temp_nbr, id='{}_{}'.format( attributes[i]['id'], id_counter), lanes=attributes[i]['lanes'], others=[[]]) id_counter += 1 new_graph.add_edge(temp_nbr, nbr, id='{}_{}'.format( attributes[i]['id'], id_counter), lanes=attributes[i]['lanes'], others=attributes[i]['others'][1:]) id_counter += 1 else: new_graph.add_edge(n, nbr, id=attributes['id'], lanes=attributes['lanes'], others=attributes['others']) return new_graph
def graph_from_prov_networkx_graph(prov_graph: nx.MultiDiGraph): graph_object = dict() node_labels = dict() edge_labels = dict() renamed_nodes = {node: i for i, node in enumerate(prov_graph.nodes)} for n, nbrsdict in prov_graph.adjacency(): u = renamed_nodes[n] graph_object[u] = dict() node_labels[u] = str(n.get_type()) for nbr, keydict in nbrsdict.items(): v = renamed_nodes[nbr] for key, eattr in keydict.items(): if v not in graph_object[u]: # only add the first edge graph_object[u][v] = 1.0 edge_labels[(u, v)] = str(eattr["relation"].get_type()) return (graph_object, node_labels, edge_labels)
def plot_graph(graph: nx.MultiDiGraph) -> None: """ Plot the Multiple Directed graph using the plotly library. """ # Choosing the spring layout to position the vertices of the graph. pos = nx.spring_layout(graph) # Creating the edge trace. edge_x = [] edge_y = [] xtext = [] ytext = [] edge_values_text = [] for edge in graph.edges(): # Determine the start and end coordinates of the edge on the graph. x0, y0 = pos[edge[0]] x1, y1 = pos[edge[1]] # Add all x coordinates to list of x_edge data. edge_x.append(x0) edge_x.append(x1) edge_x.append(None) # Add all y coordinates to list of y_edge data. edge_y.append(y0) edge_y.append(y1) edge_y.append(None) # Add x midpoint coordinates to list of xtext data. xtext.append((x0 + x1) / 2) # Add y midpoint coordinates to list of ytext data. ytext.append((y0 + y1) / 2) # Add transaction value to list of edge_values data. value = graph.get_edge_data(edge[0], edge[1])[0]['weight'] edge_values_text.append(f"Transaction Value: {value}") # Plotting the edges. edge_trace = go.Scatter(x=edge_x, y=edge_y, line=dict(width=1, color='black'), mode='lines') # Plotting the edge transaction text. edge_values_trace = go.Scatter(x=xtext, y=ytext, mode='none', text=edge_values_text, textposition='top center', hovertemplate='%{text}<extra></extra>') # Creating the node trace. node_x = [] node_y = [] node_size = [] for node in graph.nodes(): # Determine the coordinates of each node (using the spring layout defined earlier) x, y = pos[node] node_x.append(x) node_y.append(y) size = 10 if graph.nodes[node] != {}: size = graph.nodes[node]['size'] node_size.append(size) node_trace = go.Scatter(x=node_x, y=node_y, mode='markers', hoverinfo='text', marker=dict(showscale=True, colorscale='Hot', color=[], size=node_size, colorbar=dict( thickness=10, title='# of Transactions (degree)', xanchor='left', titleside='right'), line_width=2)) # Setting the text of each node to its address. node_text = [] for node in graph.nodes(): node_desc = f"Address: {node}" # If the account doesn't have an empty representation # in the graph, get its balance. if graph.nodes[node] != {}: balance = graph.nodes[node]['balance'] node_desc = f"Address: {node}\nBalance: {balance}" # Add the description of the node to the list (which # will get added to the trace, updating it). node_text.append(node_desc) # Update the text and size attributes of the node trace. node_trace.text = node_text node_neighbours = [] for node in graph.adjacency(): # To find the neighbours of this node (accounts who either # sent or received transactions from this current account) # we must access the second item of a tuple, which contains # a dictionary representation of its neighbours (addresses # mapped to neighbours = len(node[1]) node_neighbours.append(neighbours) node_trace.marker.color = node_neighbours # Setting up the layout here. layout = go.Layout( title='Ethereum Transaction Graph', showlegend=False, hovermode='closest', xaxis=dict(showgrid=False, zeroline=False), yaxis=dict(showgrid=False, zeroline=False), margin=dict(b=20, l=15, r=15, t=50), # Setting up the margins around the graph ) # Plot the graph figure. fig = go.Figure(data=[edge_trace, node_trace, edge_values_trace], layout=layout) # update layout fig.update_layout(title_font_size=15) fig.show()
def _is_multidigraph(g: nx.MultiDiGraph) -> bool: for node, nbrdict in g.adjacency(): for nbr, attributes in nbrdict.items(): if len(attributes) > 1: return True return False