def subgraph_isomorphism_vertex_counts(edge_index, **kwargs): ##### vertex structural identifiers ##### subgraph_dict, induced, num_nodes = kwargs['subgraph_dict'], kwargs['induced'], kwargs['num_nodes'] directed = kwargs['directed'] if 'directed' in kwargs else False G_gt = gt.Graph(directed=directed) G_gt.add_edge_list(list(edge_index.transpose(1,0).cpu().numpy())) gt.stats.remove_self_loops(G_gt) gt.stats.remove_parallel_edges(G_gt) # compute all subgraph isomorphisms sub_iso = gt_topology.subgraph_isomorphism(subgraph_dict['subgraph'], G_gt, induced=induced, subgraph=True, generator=True) ## num_nodes should be explicitly set for the following edge case: ## when there is an isolated vertex whose index is larger ## than the maximum available index in the edge_index counts = np.zeros((num_nodes, len(subgraph_dict['orbit_partition']))) for sub_iso_curr in sub_iso: for i,node in enumerate(sub_iso_curr): # increase the count for each orbit counts[node, subgraph_dict['orbit_membership'][i]] +=1 counts = counts/subgraph_dict['aut_count'] counts = torch.tensor(counts) return counts
def matching_subgraph(self, sub): """Find all indices of atoms which match the given graph. Args: sub (graph_tool.Graph): the subgraph Returns: List: list of lists of atomic indices matching the atoms in sub to those in this molecule """ try: import graph_tool.topology as top except ImportError: raise RuntimeError( "Please install the graph_tool library for graph operations") g = self.bond_graph() matches = top.subgraph_isomorphism( sub, g, vertex_label=( sub.vertex_properties["element"], g.vertex_properties["element"], ), ) return [tuple(x.a) for x in matches]
def matching_fragments(self, fragment, method="connectivity"): """ Find the indices of a matching fragment to the given molecular fragment Args: fragment (Molecule): Molecule object containing the desired fragment method (str, optional): the method for matching Returns: List[dict]: List of maps between matching indices in this molecule and those in the fragment """ try: import graph_tool.topology as top except ImportError: raise RuntimeError( "Please install the graph_tool library for graph operations") sub = fragment.bond_graph() g = self.bond_graph() matches = top.subgraph_isomorphism( sub, g, vertex_label=( sub.vertex_properties["element"], g.vertex_properties["element"], ), ) return [list(x.a) for x in matches]
def match_graphs(G1, G2) -> List[Tuple[List[int], List[int]]]: """ Compute graph isomorphisms. Parameters ---------- G1: Graph 1 G2: Graph 2 Returns ------- List[Tuple[List[int],List[int]]] All possible mappings between nodes of graph 1 and graph 2 (isomorphisms) Raises ------ ValueError If the graphs `G1` and `G2` are not isomorphic """ try: maps = topology.subgraph_isomorphism( G1, G2, vertex_label=( G1.vertex_properties["atomicnum"], G2.vertex_properties["atomicnum"], ), subgraph=False, ) except KeyError: # No "atomicnum" vertex property warnings.warn("No atomic number information stored on nodes. " + "Node matching is not performed...") maps = topology.subgraph_isomorphism(G1, G2, subgraph=False) # Check if graphs are actually isomorphic if len(maps) == 0: # TODO: Create a new exception raise ValueError(f"Graphs {G1} and {G2} are not isomorphic.") n = num_vertices(G1) # Extract all isomorphisms in a list return [(np.arange(0, n, dtype=int), m.a) for m in maps]
def automorphism_orbits(edge_list, print_msgs=True, **kwargs): ##### vertex automorphism orbits ##### directed = kwargs['directed'] if 'directed' in kwargs else False graph = gt.Graph(directed=directed) graph.add_edge_list(edge_list) gt.stats.remove_self_loops(graph) gt.stats.remove_parallel_edges(graph) # compute the vertex automorphism group aut_group = gt_topology.subgraph_isomorphism(graph, graph, induced=False, subgraph=True, generator=False) orbit_membership = {} for v in graph.get_vertices(): orbit_membership[v] = v # whenever two nodes can be mapped via some automorphism, they are assigned the same orbit for aut in aut_group: for original, vertex in enumerate(aut): role = min(original, orbit_membership[vertex]) orbit_membership[vertex] = role orbit_membership_list = [[], []] for vertex, om_curr in orbit_membership.items(): orbit_membership_list[0].append(vertex) orbit_membership_list[1].append(om_curr) # make orbit list contiguous (i.e. 0,1,2,...O) _, contiguous_orbit_membership = np.unique(orbit_membership_list[1], return_inverse=True) orbit_membership = { vertex: contiguous_orbit_membership[i] for i, vertex in enumerate(orbit_membership_list[0]) } orbit_partition = {} for vertex, orbit in orbit_membership.items(): orbit_partition[orbit] = [ vertex ] if orbit not in orbit_partition else orbit_partition[orbit] + [ vertex ] aut_count = len(aut_group) if print_msgs: print('Orbit partition of given substructure: {}'.format( orbit_partition)) print('Number of orbits: {}'.format(len(orbit_partition))) print('Automorphism count: {}'.format(aut_count)) return graph, orbit_partition, orbit_membership, aut_count
def match_graphs(G1, G2) -> List[Tuple[List[int], List[int]]]: """ Compute graph isomorphisms. Parameters ---------- G1: Graph 1 G2: Graph 2 Returns ------- List[Tuple[List[int],List[int]]] All possible mappings between nodes of graph 1 and graph 2 (isomorphisms) Raises ------ NonIsomorphicGraphs If the graphs `G1` and `G2` are not isomorphic """ try: maps = topology.subgraph_isomorphism( G1, G2, vertex_label=( G1.vertex_properties["aprops"], G2.vertex_properties["aprops"], ), subgraph=False, ) except KeyError: warnings.warn(warn_no_atomic_properties) maps = topology.subgraph_isomorphism(G1, G2, subgraph=False) # Check if graphs are actually isomorphic if len(maps) == 0: raise NonIsomorphicGraphs(error_non_isomorphic_graphs) n = num_vertices(G1) # Extract all isomorphisms in a list return [(np.arange(0, n, dtype=int), m.a) for m in maps]
def subgraph_isomorphism_edge_counts(edge_index, **kwargs): ##### edge structural identifiers ##### subgraph_dict, induced = kwargs['subgraph_dict'], kwargs['induced'] directed = kwargs['directed'] if 'directed' in kwargs else False edge_index = edge_index.transpose(1, 0).cpu().numpy() edge_dict = {} for i, edge in enumerate(edge_index): edge_dict[tuple(edge)] = i if not directed: subgraph_edges = to_undirected( torch.tensor( subgraph_dict['subgraph'].get_edges().tolist()).transpose( 1, 0)).transpose(1, 0).tolist() G_gt = gt.Graph(directed=directed) G_gt.add_edge_list(list(edge_index)) gt.stats.remove_self_loops(G_gt) gt.stats.remove_parallel_edges(G_gt) # compute all subgraph isomorphisms sub_iso = gt_topology.subgraph_isomorphism(subgraph_dict['subgraph'], G_gt, induced=induced, subgraph=True, generator=True) counts = np.zeros( (edge_index.shape[0], len(subgraph_dict['orbit_partition']))) for sub_iso_curr in sub_iso: mapping = sub_iso_curr.get_array() # import pdb;pdb.set_trace() for i, edge in enumerate(subgraph_edges): # for every edge in the graph H, find the edge in the subgraph G_S to which it is mapped # (by finding where its endpoints are matched). # Then, increase the count of the matched edge w.r.t. the corresponding orbit # Repeat for the reverse edge (the one with the opposite direction) edge_orbit = subgraph_dict['orbit_membership'][i] mapped_edge = tuple([mapping[edge[0]], mapping[edge[1]]]) counts[edge_dict[mapped_edge], edge_orbit] += 1 counts = counts / subgraph_dict['aut_count'] counts = torch.tensor(counts) return counts
def check_claim(graphs, n, g): for m in range(n - 1, 3, -1): for _, h in graphs.get(m, []): if subgraph_isomorphism(h, g, max_n=1, induced=True): return m return None
def edge_automorphism_orbits(edge_list, **kwargs): ##### edge automorphism orbits according to the line graph ##### directed=kwargs['directed'] if 'directed' in kwargs else False graph_nx = nx.from_edgelist(edge_list) graph = gt.Graph(directed=directed) graph.add_edge_list(edge_list) gt.stats.remove_self_loops(graph) gt.stats.remove_parallel_edges(graph) aut_group = gt_topology.subgraph_isomorphism(graph, graph, induced=False, subgraph=True, generator=False) aut_count = len(aut_group) ##### compute line graph vertex automorphism orbits ##### graph_nx_line = nx.line_graph(graph_nx) mapping = {node: i for i,node in enumerate(graph_nx_line.nodes)} inverse_mapping = {i: node for i,node in enumerate(graph_nx_line.nodes)} graph_nx_line = nx.relabel_nodes(graph_nx_line, mapping) line_graph = gt.Graph(directed=directed) line_graph.add_edge_list(list(graph_nx_line.edges)) gt.stats.remove_self_loops(line_graph) gt.stats.remove_parallel_edges(line_graph) aut_group_edges = gt_topology.subgraph_isomorphism(line_graph, line_graph, induced=False, subgraph=True, generator=False) orbit_membership = {} for v in line_graph.get_vertices(): orbit_membership[v] = v for aut in aut_group_edges: for original, vertex in enumerate(aut): role = min(original, orbit_membership[vertex]) orbit_membership[vertex] = role orbit_membership_list = [[],[]] for vertex, om_curr in orbit_membership.items(): orbit_membership_list[0].append(vertex) orbit_membership_list[1].append(om_curr) _, contiguous_orbit_membership = np.unique(orbit_membership_list[1], return_inverse = True) orbit_membership = {vertex: contiguous_orbit_membership[i] for i,vertex in enumerate(orbit_membership_list[0])} orbit_partition= {} for vertex, orbit in orbit_membership.items(): orbit_partition[orbit] = [inverse_mapping[vertex]] if orbit not in orbit_partition else orbit_partition[orbit]+[inverse_mapping[vertex]] ##### transfer line graph vertex automorphism orbits to original edges ##### orbit_membership_new = {} for i,edge in enumerate(graph.get_edges()): mapped_edge = mapping[tuple(edge)] if tuple(edge) in mapping else mapping[tuple([edge[1],edge[0]])] orbit_membership_new[i] = orbit_membership[mapped_edge] print('Edge orbit partition of given substructure: {}'.format(orbit_partition)) print('Number of edge orbits: {}'.format(len(orbit_partition))) print('Graph (vertex) automorphism count: {}'.format(aut_count)) return graph, orbit_partition, orbit_membership_new, aut_count
def main(): # vocab_file = 'vocabulary.subgraph' # subgraph_process(get_vocabulary(vocab_file)) print subgraph_isomorphism(vocab_subgraph_0.gexf, graph_of_words_0.gexf)