def quad_switch(graph: nx.Graph, u1, v1, u2, v2) -> bool: if not graph.has_edge(u1, v1) or not graph.has_edge(u2, v2): return False if graph.has_edge(u1, u2) or graph.has_edge(v1, v2): return False num_connected = nx.number_connected_components(graph) graph.remove_edge(u1, v1) graph.remove_edge(u2, v2) graph.add_edge(u1, u2) graph.add_edge(v1, v2) if nx.number_connected_components(graph) != num_connected: graph.remove_edge(u1, u2) graph.remove_edge(v1, v2) graph.add_edge(u1, v1) graph.add_edge(u2, v2) return False return True
def to_networkx(self, graph: nx.Graph = None) -> nx.Graph: """ Return networkx graph of entities. Parameters ---------- graph : nx.Graph, optional Graph to add entities to. If not supplied the function creates and returns a new graph. By default None Returns ------- nx.Graph Graph with entity and any connected entities. """ graph = graph or nx.Graph() if not graph.has_node(self): graph.add_node(self, **self.node_properties) for edge in self.edges: if graph.has_edge(edge.source, edge.target): continue graph.add_edge(edge.source, edge.target, **edge.attrs) for node in (edge.source, edge.target): # If this node has edges that are not in our graph # call to_networkx recursively on that node. if any(edge for edge in node.edges if not graph.has_edge(edge.source, edge.target)): ent_node = typing.cast(Entity, node) ent_node.to_networkx(graph) return graph
def count_graph_distance(g1: nx.Graph, g2: nx.Graph, mapping: dict, reverse_mapping: dict, sub: bool): d_vertex = 0 d_edge = 0 symbols1 = nx.get_node_attributes(g1, 'symbol') symbols2 = nx.get_node_attributes(g2, 'symbol') # count distance of vertices for node1 in g1.nodes(): if mapping[node1] == -1: d_vertex += 1 elif symbols1[node1] != symbols2[mapping[node1]]: d_vertex += 1 if not sub: # not sub graph distance for node2 in g2.nodes(): if reverse_mapping[node2] == -1: d_vertex += 1 # count distance of edges for first, second in g1.edges(): if mapping[first] == -1 or mapping[second] == -1 or not g2.has_edge( mapping[first], mapping[second]): d_edge += 1 if not sub: for first, second in g2.edges(): if reverse_mapping[first] == -1 or reverse_mapping[second] == -1 \ or not g1.has_edge(reverse_mapping[first], reverse_mapping[second]): d_edge += 1 return d_vertex + d_edge
def update_environment_edge(rules: Rules, graph: Graph, final_actions: Actions) -> None: for edge in final_actions.values(): u, v = edge if not graph.has_edge(*edge): graph.add_edge(u, v) elif graph.has_edge(*edge): graph.remove_edge(u, v) return None
def remove_intra_edges(graph: nx.Graph, group: Union[GraphSignalData, Mapping[str, GraphSignalData]]): if isinstance(group, collections.abc.Mapping): for actual_group in group.values(): remove_intra_edges(graph, actual_group) else: for v in group: for u in group: if graph.has_edge(v, u) or graph.has_edge(u, v): graph.remove_edge(v, u)
def two_join_combinations(graph : nx.Graph): for (a1, a2), (b1, b2) in combinations(graph.edges, 2): if graph.has_edge(a1, b2) or graph.has_edge(a2, b1): continue for u in vertex_set(graph) - (complete_verts(graph, {a2, b2}) ): if distinct((a1, a2, b1, b2, u)): yield (a1, a2, b1, b2, u)
def clustering_and_average_clustering(graph: nx.Graph) -> (List[float], float): """ Returns the clustering coefficient of each node of network and the average clustering coefficient. Parameters ---------- graph: a NetworkX graph object Returns ------- clustering: list clustering coefficient of each network node in a average_clustering: floating point number average clustering coefficient """ clustering = [] for node in graph.nodes_iter(): neighbors = nx.neighbors(G=graph, n=node) clustering_coef = 0 n_links = 0 if len(neighbors) > 1: for neighbor1 in neighbors: for neighbor2 in neighbors: if graph.has_edge(u=neighbor1, v=neighbor2): n_links += 1 clustering_coef = n_links / (len(neighbors) * (len(neighbors) - 1)) clustering.append(clustering_coef) average_clustering = sum(clustering) / float(len(clustering)) return clustering, average_clustering
def add_interacting_proteins(G: nx.Graph, df: pd.DataFrame, kind: str) -> nx.Graph: """ Generic function for adding interaction edges to PPI Graph. You can use this function to additional interactions using a dataframe with columns ``"p1"`` and ``"p2"``. :param G: PPI Graph to populate with edges. :type G: nx.Graph :param df: Dataframe containing edgelist. :type df: pd.DataFrame :param kind: name of interaction type. :type kind: str :returns: PPI Graph with pre-computed edges added. :rtype: nx.Graph """ protein_1 = df["p1"].values protein_2 = df["p2"].values interacting_proteins = set(list(zip(protein_1, protein_2))) for p1, p2 in interacting_proteins: if G.has_edge(p1, p2): G.edges[p1, p2]["kind"].add(kind) else: G.add_edge(p1, p2, kind={kind}) log.debug(f"Added {len(df)} {kind} interaction edges") return G
def L_P_AA(network: nx.Graph): num_add = 0 # the number of egdes to be added nodes_pair_without_edge = [] # the pairs of nodes without edges probability_add = [] # the probabilities of the pairs of nodes to be added score = 0.0 # the score of each pair of nodes in link prediction model total_score_without_edge = 0.0 # the sum of scores of pairs of nodes without edge # calculate the score of each pair of nodes for i, elei in enumerate(list(network.nodes(), 1)): for j, elej in enumerate(list(network.nodes(), 1)): score = 0.0 if i >= j: continue if not network.has_edge(elei, elej): try: pre = nx.admic_adar_index(network, [(elei, elej)]) for u, v, s in pre: score = s except: continue total_score_without_edge += score nodes_pair_without_edge.append((elei, elej, score)) for a, b, c in nodes_pair_without_edge: probability_add.append(c / total_score_without_edge) # calculate the probabilities of edges to be added return nodes_pair_without_edge, probability_add
def L_P_CN(network: nx.Graph): num_add = 0 # the number of egdes to be added nodes_pair_without_edge = [] # the pairs of nodes without edges probability_add = [] # the probabilities of the pairs of nodes to be added score = 0 # the score of each pair of nodes in link prediction model total_score_without_edge = 0.0 # the sum of scores of pairs of nodes without edge # calculate the score of each pair of nodes for i, elei in enumerate(list(network.nodes()), 1): for j, elej in enumerate(list(network.nodes()), 1): score = 0 if i >= j: continue if not network.has_edge(elei, elej): try: score = len(nx.common_neighbors(network, elei, elej)) except: continue total_score_without_edge += score nodes_pair_without_edge.append((elei, elej, score)) for a, b, c in nodes_pair_without_edge: probability_add.append(c / total_score_without_edge) # calculate the probabilities of edges to be added return nodes_pair_without_edge, probability_add
def shortest_path_sets(graph : nx.Graph, A : set, B : set): # Find a shortest path between two connected sets of verts for a in A: for b in B: if graph.has_edge(a, b): return [a, b] F = nx.Graph(graph.subgraph(vertex_set(graph) - (A | B))) F.add_nodes_from(("a", "b")) for (u, v) in edge_set(graph): if u in A: F.add_edge("a", v) elif v in A: F.add_edge(u, "a") if u in B: F.add_edge("b", v) elif v in B: F.add_edge(u, "b") P = shortest_path(F, "a", "b") if P is None: return None new_a = (A & neighbourhood(graph, P[ 1])).pop() new_b = (B & neighbourhood(graph, P[-2])).pop() return [new_a] + P[1:-1] + [new_b]
def add_delaunay_triangulation( G: nx.Graph, allowable_nodes: Optional[List[str]] = None ): """ Compute the Delaunay triangulation of the protein structure. This has been used in prior work. References: Harrison, R. W., Yu, X. & Weber, I. T. Using triangulation to include target structure improves drug resistance prediction accuracy. in 1–1 (IEEE, 2013). doi:10.1109/ICCABS.2013.6629236 Yu, X., Weber, I. T. & Harrison, R. W. Prediction of HIV drug resistance from genotype with encoded three-dimensional protein structure. BMC Genomics 15 Suppl 5, S1 (2014). Notes: 1. We do not use the add_interacting_resis function, because this interaction is computed on the ``CA`` atoms. Therefore, there is code duplication. For now, I have chosen to leave this code duplication in. :param G: The networkx graph to add the triangulation to. :type G: nx.Graph :param allowable_nodes: The nodes to include in the triangulation. If ``None`` (default), no filtering is done. This parameter is used to filter out nodes that are not desired in the triangulation. Eg if you wanted to construct a delaunay triangulation of the CA atoms of an atomic graph. :type allowable_nodes: List[str], optional """ if allowable_nodes is None: coords = np.array([d["coords"] for _, d in G.nodes(data=True)]) node_map: Dict[int, str] = dict(enumerate(G.nodes())) else: coords = np.array( [ d["coords"] for _, d in G.nodes(data=True) if d["atom_type"] in allowable_nodes ] ) node_map: Dict[int, str] = { i: n for i, (n, d) in enumerate(G.nodes(data=True)) if d["atom_type"] in allowable_nodes } node_map: Dict[int, str] = dict(enumerate(node_map.values())) tri = Delaunay(coords) # this is the triangulation log.debug( f"Detected {len(tri.simplices)} simplices in the Delaunay Triangulation." ) for simplex in tri.simplices: nodes = [node_map[s] for s in simplex] for n1, n2 in combinations(nodes, 2): if n1 not in G.nodes or n2 not in G.nodes: continue if G.has_edge(n1, n2): G.edges[n1, n2]["kind"].add("delaunay") else: G.add_edge(n1, n2, kind={"delaunay"})
def add_cation_pi_interactions( G: nx.Graph, rgroup_df: Optional[pd.DataFrame] = None ): """Add cation-pi interactions.""" if rgroup_df is None: rgroup_df = G.graph["rgroup_df"] cation_pi_df = filter_dataframe( rgroup_df, "residue_name", CATION_PI_RESIS, True ) cation_pi_df = filter_dataframe( cation_pi_df, "node_id", list(G.nodes()), True ) distmat = compute_distmat(cation_pi_df) interacting_atoms = get_interacting_atoms(6, distmat) interacting_atoms = list(zip(interacting_atoms[0], interacting_atoms[1])) for (a1, a2) in interacting_atoms: resi1 = cation_pi_df.loc[a1, "node_id"] resi2 = cation_pi_df.loc[a2, "node_id"] condition1 = resi1 in CATION_RESIS and resi2 in PI_RESIS condition2 = resi1 in PI_RESIS and resi2 in CATION_RESIS if (condition1 or condition2) and resi1 != resi2: if G.has_edge(resi1, resi2): G.edges[resi1, resi2]["kind"].add("cation_pi") else: G.add_edge(resi1, resi2, kind={"cation_pi"})
def add_interacting_genes(G: nx.Graph, df: pd.DataFrame, kind: str) -> nx.Graph: """ Generic function for adding interaction edges to GRNGraph :param G: GRNGraph to populate with edges :type G: nx.Graph :param df: DataFrame containing edgelist :type df: pd.DataFrame :param kind: name of interaction type :type kind: str :returns: Graph with edges added :rtype: nx.Graph """ gene_1 = df["g1"].values gene_2 = df["g2"].values reg_type = df["regtype"].values interacting_genes = set(list(zip(gene_1, gene_2, reg_type))) for g1, g2, r in interacting_genes: if G.has_edge(g1, g2): G.edges[g1, g2]["kind"].add(kind) G.edges[g1, g2]["regtype"].add(r) else: G.add_edge(g1, g2, kind={kind}, regtype={r}) log.debug(f"Added {len(df)} {kind} interaction edges") return G
def hide_edges(graph: nx.Graph, percentage: int) -> nx.Graph: graph = copy.deepcopy(graph) edge_usages = np.zeros(graph.number_of_nodes()) edges = [] for u, v, w in graph.edges(data='weight', default=1): edge_usages[u] += 1 edge_usages[v] += 1 edges.append((u, v, w)) edges = np.array(edges) to_hide = (percentage / 100.0) * graph.number_of_edges() while to_hide > 0: e = np.random.randint(len(edges)) u, v, w = edges[e] u, v = int(u), int(v) if not graph.has_edge(u, v): continue if edge_usages[u] > 0 and edge_usages[v] > 0: edge_usages[u] -= 1 edge_usages[v] -= 1 graph.remove_edge(u, v) to_hide -= 1 return graph
def contact_neighborhood(network: nx.Graph, subreddit: str, deepcopy=True, detach=True) -> nx.Graph: ''' Obtain the contact neighborhood of the network (snapshot) by only including nodes with activity on the subreddit of interest that are connected to the user. detached - remove edges from central user to other nodes to avoid making the entire graph weakly connected. ''' if deepcopy: network = copy.deepcopy(network) user = network.graph['user'] nodes_to_remove = [] for (n, m) in network.nodes(data=True): # Remove nodes without activity in specified subreddit if not m['membership'].contains(subreddit): nodes_to_remove.append(n) # Remove nodes not connected to central user if not network.has_edge(n, user) and n != user: nodes_to_remove.append(n) # Remove user node if detach: nodes_to_remove.append(user) network.remove_nodes_from(nodes_to_remove) return network
def test_self_loop(graph: nx.Graph, verbose=False): selfloops = False for node in graph.nodes: if graph.has_edge(node, node): selfloops = True if verbose: print(node)
def add_distance_threshold(G: nx.Graph, long_interaction_threshold: int, threshold: float = 5.0): """ Adds edges to any nodes within a given distance of each other. Long interaction threshold is used to specify minimum separation in sequence to add an edge between networkx nodes within the distance threshold :param G: Protein Structure graph to add distance edges to :param long_interaction_threshold: minimum distance in sequence for two nodes to be connected :param threshold: Distance in angstroms, below which two nodes are connected :return: Graph with distance-based edges added """ dist_mat = compute_distmat(G.graph["pdb_df"]) interacting_nodes = get_interacting_atoms(threshold, distmat=dist_mat) interacting_nodes = zip(interacting_nodes[0], interacting_nodes[1]) for a1, a2 in interacting_nodes: n1 = G.graph["pdb_df"].loc[a1, "node_id"] n2 = G.graph["pdb_df"].loc[a2, "node_id"] n1_chain = G.graph["pdb_df"].loc[a1, "chain_id"] n2_chain = G.graph["pdb_df"].loc[a2, "chain_id"] n1_position = G.graph["pdb_df"].loc[a1, "residue_number"] n2_position = G.graph["pdb_df"].loc[a2, "residue_number"] condition_1 = n1_chain != n2_chain condition_2 = (abs(n1_position - n2_position) > long_interaction_threshold) if condition_1 or (condition_2 and not condition_1): if G.has_edge(n1, n2): G.edges[n1, n2]["kind"].add("distance_threshold") else: G.add_edge(n1, n2, kind={"distance_threshold"})
def create_src_tokengraph(dataset, vocab, G: nx.Graph = None, window_size: int = 2): """ Given a corpus create a token Graph. Append to graph G if provided. :param edge_attr: Name of the edge attribute, should match with param name when calling add_edge(). :param window_size: Sliding window size :param G: :param dataset: TorchText dataset :param vocab: TorchText field containing vocab. :return: """ ## Create graph if not exist: if G is None: G = nx.Graph() ## Add token's id as node to the graph for token_txt, token_id in vocab['str2idx_map'].items(): # try: # token_emb = glove_embs[token_txt] # except KeyError: # emb_shape = glove_embs[list(glove_embs.keys())[0]].shape # glove_embs['<UNK>'] = np.random.uniform(low=0.5, high=0.5, # size=emb_shape) # token_emb = glove_embs['<UNK>'] # G.add_node(token_id, node_txt=token_txt, s_co=field.vocab.freqs[ # token_txt], t_co=0, emb=token_emb) G.add_node(token_id, node_txt=token_txt, s_co=vocab['freqs'][token_txt], t_co=0) ## Add edges based on token co-occurrence within a sliding window: for txt_toks in dataset: j = 0 txt_len = len(txt_toks) if window_size is None or window_size > txt_len: window_size = txt_len slide = txt_len - window_size + 1 for k in range(slide): txt_window = txt_toks[j:j + window_size] ## Co-occurrence in tweet: occurrences = find_cooccurrences(txt_window) ## Add edges with attribute: for token_pair, wt in occurrences.items(): node1 = vocab['str2idx_map'][token_pair[0]] node2 = vocab['str2idx_map'][token_pair[1]] if G.has_edge(node1, node2): wt = G.get_edge_data(node1, node2)['s_pair'] + wt G.add_edge(node1, node2, s_pair=wt, t_pair=0) j = j + 1 return G
def get_unconnected_pairs_(G: nx.Graph, cutoff=2, n_limit=None): edges_len = dict(nx.all_pairs_shortest_path_length(G, cutoff=cutoff)) unconnected_pairs = [] appended_pairs = {} nodes = list(G.nodes()) for u in edges_len.keys(): if u not in appended_pairs: appended_pairs[u] = {} for v in edges_len[u].keys(): if v not in appended_pairs: appended_pairs[v] = {} if u != v and v not in appended_pairs[u] and not G.has_edge(u, v): unconnected_pairs.append({'node_1': u, 'node_2': v}) appended_pairs[u][v] = True appended_pairs[v][u] = True if n_limit is not None: unconnected_pairs = np.random.choice(unconnected_pairs, size=min(n_limit, len(unconnected_pairs)), replace=False) unconnected_pairs = list(unconnected_pairs) return unconnected_pairs
def add_delaunay_triangulation(G: nx.Graph): """ Compute the Delaunay triangulation of the protein structure. This has been used in prior work. References: - Harrison, R. W., Yu, X. & Weber, I. T. Using triangulation to include target structure improves drug resistance prediction accuracy. in 1–1 (IEEE, 2013). doi:10.1109/ICCABS.2013.6629236 - Yu, X., Weber, I. T. & Harrison, R. W. Prediction of HIV drug resistance from genotype with encoded three-dimensional protein structure. BMC Genomics 15 Suppl 5, S1 (2014). Notes: 1. We do not use the add_interacting_resis function, because this interaction is computed on the CA atoms. Therefore, there is code duplication. For now, I have chosen to leave this code duplication in. """ ca_coords = G.graph["pdb_df"].query("atom_name == 'CA'") tri = Delaunay(ca_coords[["x_coord", "y_coord", "z_coord"]]) # this is the triangulation for simplex in tri.simplices: nodes = ca_coords.reset_index(drop=True).loc[simplex, "node_id"] for n1, n2 in combinations(nodes, 2): if G.has_edge(n1, n2): G.edges[n1, n2]["kind"].add("delaunay") else: G.add_edge(n1, n2, kind={"delaunay"})
def add_aromatic_sulphur_interactions(G: nx.Graph, rgroup_df: Optional[pd.DataFrame] = None ): """Find all aromatic-sulphur interactions.""" if rgroup_df is None: rgroup_df = G.graph["rgroup_df"] RESIDUES = ["MET", "CYS", "PHE", "TYR", "TRP"] SULPHUR_RESIS = ["MET", "CYS"] AROMATIC_RESIS = ["PHE", "TYR", "TRP"] aromatic_sulphur_df = filter_dataframe(rgroup_df, "residue_name", RESIDUES, True) distmat = compute_distmat(aromatic_sulphur_df) interacting_atoms = get_interacting_atoms(5.3, distmat) interacting_atoms = zip(interacting_atoms[0], interacting_atoms[1]) for (a1, a2) in interacting_atoms: resi1 = aromatic_sulphur_df.loc[a1, "node_id"] resi2 = aromatic_sulphur_df.loc[a2, "node_id"] condition1 = resi1 in SULPHUR_RESIS and resi2 in AROMATIC_RESIS condition2 = resi1 in AROMATIC_RESIS and resi2 in SULPHUR_RESIS if (condition1 or condition2) and resi1 != resi2: if G.has_edge(resi1, resi2): G.edges[resi1, resi2]["kind"].add("aromatic_sulphur") else: G.add_edge(resi1, resi2, kind={"aromatic_sulphur"})
def edmondskarp(G: nx.Graph, s, t): RG = G.copy() for u,v in G.edges_iter(): G.edge[u][v]['flow'] = 0 path = isthereapath(RG,s,t) while len(path) > 0: path_cp = min([RG.edge[u][v]['capacity'] for u,v in path]) for u,v in path: if G.has_edge(u,v): G.edge[u][v]['flow'] += path_cp RG.edge[u][v]['capacity'] -= path_cp if RG.edge[u][v]['capacity'] == 0: RG.remove_edge(u,v) if RG.has_edge(v,u): RG.edge[v][u]['capacity'] += path_cp else: RG.add_edge(v,u,capacity=path_cp) else: # then this edge is a "cancelling" flow # residue should go up and cancelling "capacity" should go down G.edge[v][u]['flow'] -= path_cp RG.edge[v][u]['capacity'] += path_cp RG.edge[u][v]['capacity'] -= path_cp if RG.edge[u][v]['capacity'] == 0: RG.remove_edge(u,v) path = isthereapath(RG,s,t) return RG
def print_pheromone(graph: nx.Graph): for i in graph.nodes: for j in graph.nodes: if graph.has_edge(i, j): print(i, end="\t") print(j, end="\t") print(graph[i][j]['pheromone'])
def __connect_spokes(graph: nx.Graph, men_spokes: List[int], women_spokes: List[int], is_invalid_graph: Callable, condom_weight: Optional[float] = None): men_spokes_copy = [i for i in men_spokes] women_spokes_copy = [i for i in women_spokes] random.shuffle(men_spokes_copy) while len(men_spokes_copy) > 0 and len(len(women_spokes_copy)) > 0: man_id = men_spokes_copy.pop(0) women = [i for i in women_spokes_copy] random.shuffle(women) while len(women) > 0: success = False woman_id = women.pop(0) if graph.has_edge(man_id, woman_id): continue if condom_weight is None: graph.add_edge(man_id, woman_id) else: graph.add_edge(man_id, woman_id, weight=condom_weight) if is_invalid_graph(graph): graph.remove_edge(man_id, woman_id) else: success = True break if success: women_spokes_copy.remove(woman_id)
def maximal_independent_sets(mat): g = Graph(mat) for u in g.nodes(): if g.has_edge(u, u): g.remove_edge(u, u) cg = complement(g) isets = list(find_cliques(cg)) return isets
def _getEdges(self, G: nx.Graph, player: Player, depth=0, parent: Player = None): if player.username not in G.nodes: self._addNode(G, player) for p in player.peers: p1 = p.player.username p2 = p.player2.username if p.player2.username not in G.nodes: self._addNode(G, p.player2) if not G.has_edge(p1, p2) and not G.has_edge(p2, p1): self._addEdge(G, player, p) if depth < Config.MAX_RECURSION_DEPTH: self._getEdges(G, p.player2, depth=depth + 1, parent=player)
def ops_are_consistent_with_device_graph(ops: Iterable[ops.Operation], device_graph: nx.Graph) -> bool: for op in ops: if not set(op.qubits).issubset(device_graph): return False if len(op.qubits) >= 2 and not device_graph.has_edge(*op.qubits): return False return True
def test_happy_path(self): graph = Graph() e1 = gen_name() e2 = gen_name() e3 = gen_name() graph.add_node(e1, layer=1, position=(1.0, 2.0), label='E') graph.add_node(e2, layer=1, position=(1.0, 1.0), label='E') graph.add_node(e3, layer=1, position=(2.0, 1.0), label='E') graph.add_edge(e1, e2) graph.add_edge(e1, e3) graph.add_edge(e2, e3) i = add_interior(graph, e1, e2, e3) if visualize_tests: visualize_graph_3d(graph) pyplot.show() [i1] = P9().apply(graph, [i]) # if correct number of nodes and edges self.assertEqual(len(graph.nodes()), 8) self.assertEqual(len(graph.edges()), 13) # if cross-layer interior connections self.assertEqual(graph.nodes[i]['label'], 'i') self.assertTrue(graph.has_edge(i, i1)) # if new interior has correct label and layer self.assertEqual(graph.nodes[i1]['label'], 'I') self.assertEqual(graph.nodes[i1]['layer'], graph.nodes[i]['layer'] + 1) # if new interior has 3 neighbors i1_neighbors = get_neighbors_at(graph, i1, graph.nodes[i1]['layer']) self.assertEqual(len(i1_neighbors), 3) # if new nodes are in correct positions new_e1 = get_node_at(graph, 2, (1.0, 2.0)) new_e2 = get_node_at(graph, 2, (1.0, 1.0)) new_e3 = get_node_at(graph, 2, (2.0, 1.0)) self.assertIsNotNone(new_e1) self.assertIsNotNone(new_e2) self.assertIsNotNone(new_e3) # if each vertex has correct label for n in i1_neighbors: self.assertEqual(graph.nodes[n]['label'], 'E') # if each vertex has correct number of neighbors for n in i1_neighbors: node_neighbors = get_neighbors_at(graph, n, graph.nodes[n]['layer']) self.assertEqual(len(node_neighbors), 3) if visualize_tests: visualize_graph_3d(graph) pyplot.show()
def test_happy_path(self): graph = Graph() initial_node = gen_name() graph.add_node(initial_node, layer=0, position=(0.5, 0.5), label='E') if visualize_tests: visualize_graph_3d(graph) pyplot.show() P1().apply(graph, [initial_node]) nodes_data = graph.nodes(data=True) self.assertEqual(len(graph.nodes()), 7) self.assertEqual(len(graph.edges()), 13) # check the initial node initial_node_data = nodes_data[initial_node] self.assertEqual(initial_node_data['layer'], 0) self.assertEqual(initial_node_data['position'], (0.5, 0.5)) self.assertEqual(initial_node_data['label'], 'e') # check other nodes vx_bl = get_node_at(graph, 1, (0, 0)) vx_br = get_node_at(graph, 1, (1, 0)) vx_tl = get_node_at(graph, 1, (0, 1)) vx_tr = get_node_at(graph, 1, (1, 1)) self.assertIsNotNone(vx_bl) self.assertIsNotNone(vx_br) self.assertIsNotNone(vx_tl) self.assertIsNotNone(vx_tr) self.assertEqual(nodes_data[vx_bl]['label'], 'E') self.assertEqual(nodes_data[vx_br]['label'], 'E') self.assertEqual(nodes_data[vx_tl]['label'], 'E') self.assertEqual(nodes_data[vx_tr]['label'], 'E') vx_i1 = get_node_at(graph, 1, (2 / 3, 1 / 3)) vx_i2 = get_node_at(graph, 1, (1 / 3, 2 / 3)) self.assertIsNotNone(vx_i1) self.assertIsNotNone(vx_i2) self.assertEqual(nodes_data[vx_i1]['label'], 'I') self.assertEqual(nodes_data[vx_i2]['label'], 'I') self.assertTrue(graph.has_edge(initial_node, vx_i1)) self.assertTrue(graph.has_edge(initial_node, vx_i2)) self.assertTrue(graph.has_edge(vx_tl, vx_tr)) self.assertTrue(graph.has_edge(vx_tr, vx_br)) self.assertTrue(graph.has_edge(vx_br, vx_bl)) self.assertTrue(graph.has_edge(vx_bl, vx_tl)) self.assertTrue(graph.has_edge(vx_bl, vx_tr)) self.assertTrue(graph.has_edge(vx_i1, vx_bl)) self.assertTrue(graph.has_edge(vx_i1, vx_br)) self.assertTrue(graph.has_edge(vx_i1, vx_tr)) self.assertTrue(graph.has_edge(vx_i2, vx_bl)) self.assertTrue(graph.has_edge(vx_i2, vx_tl)) self.assertTrue(graph.has_edge(vx_i2, vx_tr)) if visualize_tests: visualize_graph_3d(graph) pyplot.show()
def edges_between(graph : nx.Graph, us, vs): edges = set() for u in us: for v in vs: if graph.has_edge(u, v): edges |= {(u, v)} return edges
def recalculate_edges(self, nodes=[]): """ Recalculate edges for given nodes or for all self.nodes(). Edge between nodes n1 and n2 are added if both are ChannelType.in_comm_range of each other""" if(not nodes): nodes = self.nodes() for n1 in nodes: for n2 in self.nodes(): if (n1 != n2): if (self.channelType.in_comm_range(self, n1, n2)): Graph.add_edge(self, n1, n2) elif (Graph.has_edge(self, n1, n2)): Graph.remove_edge(self, n1, n2)
class LangGraph(object): """ A graph of all the relationships in a document and/or sentence """ def __init__(self, directed=False): """ Builds a graph out of the given document """ self.isDirected = directed #a graph that is meant to be full of class Instance if self.isDirected: self.graph = DiGraph() else: self.graph = Graph() self.start = None #an Instance #keep the graph also according to temporal, redundant probably needs #refactoring self.temporal = None self.temporalMap = None def setStart(self, start): """ Sets the starting instance, also builds the temporal ordering of the graph """ self.start = start self.temporal = self.narrativeOrder() self.temporalMap = self.narrativeMapping() def indexToInst(self, index): """ Returns the instance corresponding to the given index """ result = index #if the index is an int, lookup the instance associated with it if type(index) == int: result = self.temporal[index] return result def instToIndex(self, instance): """ Return the index associated with the instance """ return self.temporalMap[instance] def narrativeOrder(self): """ Returns the instances in narrative order """ results = [] node = self.start prev = None #while there are more nodes, keep adding them while node is not None: #record the current node results.append(node) #get the connected nodes fringe = [n for n in self.adj(node, WORD_EDGE) if n != prev] nextNode = fringe[0] if fringe else None #advance to the next node prev = node node = nextNode return results def narrativeMapping(self): """ Makes the mapping from instances to their narrative index """ return {inst:i for i,inst in enumerate(self.temporal)} def addNode(self, node): """ Adds a node to the graph """ self.graph.add_node(node) def addEdge(self, start, end, type): """ Adds an edge between the two instances """ #if the edge exists, just add the type if self.graph.has_edge(start, end): self.addType(start, end, type) else: self.graph.add_edge(start, end, TYPES=set([type])) def removeEdge(self, start, end, edgeType): """ Removes an edge with a given type from the edge type """ #remove the type self.removeType(start, end, edgeType) #if there are no types, remove the edge itself types = self.edgeTypes(start, end) #remove the edge if not len(types) and self.graph.has_edge(start, end): self.graph.remove_edge(start, end) def addType(self, start, end, type): """ Adds a type between the edges """ #look for existing types types = self.graph[start][end].get(TYPES, set()) #add the new type types.add(type) self.graph[start][end][TYPES] = types def removeType(self, start, end, edgeType): """ Removes the type on the edge """ for prefix in [PARENT, CHILD]: edgeType = removePrefix(prefix, edgeType) types = self.graph[start][end][TYPES] #if the types contains the edge, remove if edgeType in types: types.remove(edgeType) def hasType(self, start, end, type): """ Returns true if the edge between the two nodes has the given type """ return type in self.edgeTypes(start, end) def singleEdgeTypes(self, start, end): """ Returns the types on the edge if any, or an empty set is returned """ #make sure we are using instances rather than indexes start = self.indexToInst(start) end = self.indexToInst(end) data = self.graph.get_edge_data(start,end) result = set() #if there is data, get the types if data is not None: result = data.get(TYPES, set()) return result def edgeTypes(self, start, end): """ Returns the types on the edge if any, or an empty set is returned """ if self.isDirected: parent = addPrefixes(PARENT, self.singleEdgeTypes(end, start)) child = addPrefixes(CHILD, self.singleEdgeTypes(start, end)) types = parent.union(child) else: types = self.singleEdgeTypes(start, end) return types def allEdgeTypes(self): """ Returns all the edge types """ results = set() #collect all the edges with all the types for s,e,types in self.allEdges(): #look up the edge types to make sure everything is covered for edgeType in types: results.add(edgeType) #add in the reverse types for edgeType in self.edgeTypes(e,s): results.add(edgeType) return results def allEdges(self): """ Yield all the edges in the graph """ for start, end in self.graph.edges(): yield start, end, self.edgeTypes(start, end) def contains(self, instance): """ Returns true if the graph contains the instance """ return self.graph.has_node(instance) def instances(self): """ Return all the instances in the graph """ return self.graph.nodes() def edges(self, instance): """ Returns all the edges connected to this instance """ inst = self.indexToInst(instance) #make get the directed edges if self.isDirected: results = [t for _, t in self.graph.out_edges(inst)] + [t for t, _ in self.graph.in_edges(inst)] else: results = self.graph.adj[inst] return results def docType(self): """ Returns the document type (String) """ return self.temporal[0].event.docType def adj(self, instance, type=None): """ Returns the adjancent node with a given type """ return [other for other in self.edges(instance) if self.hasType(instance, other, type) or type is None] def nonNarrativeAdj(self, instance, returnIndex=False): """ Returns the nodes that are not adjancent to the given instance """ results = [] #add each node if it has a non-narrative (temporal) connection for node in self.edges(instance): #get the non narrative types edgeTypes = nonNarrativeTypes(self.edgeTypes(instance, node)) #if there is a non-narrative edge, add it if edgeTypes: #lookup the index of the node nodeMarker = self.instToIndex(node) if returnIndex else node results.append((nodeMarker, edgeTypes)) return results def words(self): """ Returns the words in narrative order """ return [t.word for t in self.tokens()] def tokens(self): """ Returns the tokens in narrative order """ return [i.token for i in self.temporal] def labels(self): """ Returns the sequence of labels for the instances """ return [i.event.type for i in self.temporal] def removeAny(self, blackList): """ Removes any nodes/tokens/instances that match the words in the blacklist """ #if a token or its lemma match any of the words in the blacklist #mark it for removal toRemove = {inst.token for inst in self.temporal if inst.token.word.lower() in blackList or inst.token.lemma.lower() in blackList} self.removeNodes(toRemove) def removeNodes(self, tokens): """ Removes the token from the graph """ startLen = len(self) #mark all the instances/indexes to remove instances = {inst:i for inst,i in self.temporalMap.items() if inst.token in tokens} #determine the remaining nodes remaining = sorted(list(set(range(startLen)) - {i for i in instances.values()})) #add in all the bypasses for startIndex, endIndex in iterPairs(remaining): start = self.temporal[startIndex] end = self.temporal[endIndex] self.addEdge(start, end, WORD_EDGE) #remove the edges for inst in instances: self.graph.remove_node(inst) #if there are remaining nodes then reset the temporal mapping if remaining: startIndex = min(remaining) self.start = self.temporal[startIndex] #redo narrative order self.temporal = self.narrativeOrder() self.temporalMap = self.narrativeMapping() else: self.start = None self.temporal = [] self.temporalMap = {} def copy(self): """ Performs a shallow copy of the graph """ newGraph = LangGraph(self.isDirected, self.entEdges) #create new instances newInst = {i:me.Instance(copy(i.token), i.event) for i in self.temporal} #add in all the edges for start, end in self.graph.edges(): for eType in self.edgeTypes(start, end): newGraph.addEdge(newInst[start], newInst[end], eType) newGraph.setStart(newInst[self.start]) return newGraph def graphString(self): """ Returns the graph as a string """ return " ".join([t.word for t in self.tokens()]) def __len__(self): """ Returns the number of nodes (tokens) in the graph """ return len(self.graph) def __repr__(self): """ Returns a summary string of the graph """ return "LangGraph {} nodes, {} edges".format(len(self.graph.nodes()), len(self.graph.edges()))