def find_mincycle(graph): # size = graph.size() # mincycle = [] # mincycle.append(np.inf) # matr = graph.to_matrix() # dist = graph.to_matrix() # for i in range(size): # for j in range(size): # if matr[i][j] == 0: # matr[i][j] = dist[i][j] = np.inf # for k in range(size): # for i in range(k): # for j in range(i): # mincycle[0] = min(mincycle[0], dist[i][j] + matr[j][k] + matr[k][i]) # # for i in range(size): # for j in range(i): # temp = dist[i][k] + dist[k][j] # if temp < dist[i][j]: # dist[i][j] = dist[j][i] = temp # if graph.oriented: # mincycle -= 1 g = nx.Graph() nx.Graph() size = graph.size() matr = graph.to_matrix() for i in range(1, size + 1): g.add_node(i) for j in range(1, size + 1): if matr[i - 1][j - 1] is not 0: g.add_edge(i, j) return nx.minimum_cycle_basis(g)[0]
def task_b(G): gr = nx.Graph(G) print("|E| = " + str(gr.number_of_edges())) print("|V| = " + str(gr.number_of_nodes())) min = 100 max = -100 gr = maximum_connected_component(gr) for i in gr.nodes: if gr.degree[i] < min: min = gr.degree[i] if gr.degree[i] > max: max = gr.degree[i] print("𝛿(G) = " + str(min)) print("Δ(G) = " + str(max)) print("rad(G) = " + str(nx.radius(gr))) print("diam(G) = " + str(nx.diameter(gr))) print("girth(G) = " + str(len(nx.minimum_cycle_basis(G)[0])) + ", " + str(nx.minimum_cycle_basis(G)[0])) print("center(G) = " + str(nx.center(gr))) print("𝜅(G) = " + str(nx.node_connectivity(gr))) print("𝜆(G) = " + str(nx.edge_connectivity(gr)))
def get_cycle_descriptors(graph): """Get descriptors for the cycles in a graph""" cycles = nx.minimum_cycle_basis(graph) lengths = count_sublist_lengths(cycles) memberships = get_cycle_memberships(graph, cycles, lengths) return { "cycles": cycles, "lengths": lengths, "memberships": memberships, }
def __init__(self, graph): """ this is the schema for molecule with fused rings :param graph: """ super().__init__(graph) self.rings = nx.minimum_cycle_basis(self.graph) # technically sssr self.rings = sorted(self.rings, key=lambda x: len(x)) self.nrings = len(self.rings) if self.nrings < 1: warnings.warn('you are init a MolGraph with no ring!') self.ring_graph = nx.Graph() for ij in itertools.combinations(range(self.nrings), 2): i, j = ij ri = self.rings[i] rj = self.rings[j] shared_nodes = set(ri) & set(rj) ni_connected_to_rj = [ ni for ni in ri if len(set(self.graph.neighbors(ni)) & set(rj)) == 1 and ni not in rj ] # we set len(shared_nodes) > 0 to avoid breaking spiro bicycle if len(shared_nodes) > 0 or len(ni_connected_to_rj) > 0: self.ring_graph.add_edge(i, j, nshare=len(shared_nodes), nconnect=len(ni_connected_to_rj)) self.connected_rings = self.get_rings( 'nconnect', 1) # for rubrene len(self.connected_rings[0]) == 8 self.fused_rings = self.get_rings( 'nshare', 2) # for rubrene len(self.fused_rings[0]) == 4 # notice if two rings share2 then they must connect1 try: self.lgcr = self.connected_rings[0] except IndexError: try: self.lgcr = [self.rings[0]] except IndexError: raise GraphError('no rings in the MolGraph!') try: self.lgfr = self.fused_rings[0] except IndexError: try: self.lgfr = [self.rings[0]] except IndexError: raise GraphError('no rings in the MolGraph!')
def test_cycle_length(self): g = HypergraphGrammar.load('hyper_grammar.pickle') for r in g.rules: if r is not None: g = r.to_nx() cycles = nx.minimum_cycle_basis(g) if len(cycles) > 0: maxlen = max([len(c) for c in cycles]) if maxlen > 7: print(maxlen) cc = nx.number_connected_components(g) if cc > 1: print(cc)
def solve_b(G): Graph = nx.Graph(G) print("|V| = " + str(len(Graph.nodes))) print("|E| = " + str(len(Graph.edges))) Graph = biggest_component(G) max_degree = -1 min_degree = 1000 for ISO in Graph.nodes: if Graph.degree[ISO] > max_degree: max_degree = Graph.degree[ISO] if Graph.degree[ISO] < min_degree: min_degree = Graph.degree[ISO] print("Min deg = " + str(min_degree)) print("Max deg = " + str(max_degree)) print("Radius = " + str(nx.radius(Graph))) print("Diameter = " + str(nx.diameter(Graph))) print("Girth = " + str(len(nx.minimum_cycle_basis(G)[0])) + ". Example: " + str(nx.minimum_cycle_basis(G)[0])) print("Center: " + str(nx.center(Graph))) print("Edge connectivity = " + str(nx.edge_connectivity(Graph))) print("Node connectivity = " + str(nx.node_connectivity(Graph)))
def _find_bonds_and_rings(self, qm_out): """Setup networkx graph """ self.graph = nx.Graph() for i_idx, i_elem in enumerate(self.elements): self.graph.add_node(i_idx, elem=i_elem, n_bonds=qm_out.n_bonds[i_idx], q=qm_out.point_charges[i_idx], lone_e=qm_out.lone_e[i_idx], coords=self.coords[i_idx]) # add bonds for j_idx, j_elem in enumerate(self.elements): b_order = qm_out.b_orders[i_idx, j_idx] if b_order > 0.3: id1, id2 = sorted([i_elem, j_elem]) b_order_half_rounded = np.round(b_order * 2) / 2 vec = self.coords[i_idx] - self.coords[j_idx] dist = np.sqrt((vec**2).sum()) self.graph.add_edge( i_idx, j_idx, vector=vec, length=dist, order=b_order, type=f'{id1}({b_order_half_rounded}){id2}', n_rings=0) if qm_out.n_bonds[i_idx] > ELE_MAXB[i_elem]: print( f"WARNING: Atom {i_idx+1} ({ATOM_SYM[i_elem]}) has too many", " ({qm_out.n_bonds[i_idx]}) bonds?") elif qm_out.n_bonds[i_idx] == 0: print( f"WARNING: Atom {i_idx+1} ({ATOM_SYM[i_elem]}) has no bonds" ) # add rings self.rings = nx.minimum_cycle_basis(self.graph) self.rings3 = [r for r in self.rings if len(r) == 3] for i in range(self.n_atoms): self.node(i)['n_ring'] = sum([i in ring for ring in self.rings]) # for atoms in self.graph.edges: ring_members = [ set(atoms).issubset(set(ring)) for ring in self.rings ] self.edge(*atoms)['n_rings'] = sum(ring_members) self.edge(*atoms)['in_ring'] = any(ring_members) self.edge(*atoms)['in_ring3'] = any( set(atoms).issubset(set(ring)) for ring in self.rings3)
def __init__(self, graph: nx.Graph, joints: dict, partition_scheme: str): """ fragment is a BasicGraph with joints I feel it's not necessary to add parent BasicGraph as an attribute, but I maybe wrong :param graph: :param joints: a dict, keys are nodes in the subgraph that have parent graph edges not in this subgraph """ super().__init__(graph) self.partition_scheme = partition_scheme self.graph = graph self.joints = joints self.rings = nx.minimum_cycle_basis(self.graph) # technically sssr self.rings = sorted(self.rings, key=lambda x: len(x))
def getCycles(graph, grid): cycles = nx.minimum_cycle_basis(graph) finalCycles = [cycles[0]] append = True for i in range(len(cycles)): append = True for j in range(len(finalCycles)): if (intersection(cycles[i], finalCycles[j]) != []): append = False if (append and cycles[i] not in finalCycles): finalCycles.append(cycles[i]) finalCycles = [ rearangeCycle(finalCycles[i], grid) for i in range(len(finalCycles)) ] return finalCycles
def get_cycle_basis(self, minimum=True): """ A wrapper for networkx cycle basis finder :param minimum: True (default), if minimum cycle basis is sought :param nxG: networkX graph :return: list of edge lists, such as [[(0, 1), (1, 2), (2, 3)]...] or [] and the number of self-loops discarded in the conversion from multi-graph to graph """ if self.multigraph: G, N = self.convert_multigraph_to_graph() else: G = self.nxGraph N = 0 # A list of cycle lists. Each cycle list is a list of nodes which forms a cycle (loop) in G. # The nodes are not necessarily returned in a order by which they appear in the cycle... if minimum: basis = nx.minimum_cycle_basis(G) else: basis = nx.cycle_basis(G) # convert into edge lists... edge_lists = [] for node_list in basis: edges = [] start = node_list[0] p = start node_list.remove(p) while node_list: p_neighbors = {nbor for a, nbor in G.edges(p)} for q in p_neighbors: if q in node_list: # edge between p and q edges.append((p, q)) node_list.remove(q) p = q break edges.append((p, start)) # return to origin edge_lists.append(edges) return edge_lists, N
def cycles(self, calculate_every=1, exclude_zeros=False): cycles_number = [] cycles_mean_length = [] for reactions in list(self.reactions.values()): print('.', end='') self._reset_graph() cycles_number.append([]) cycles_mean_length.append([]) i = 0 for reaction in reactions: node1 = (reaction[0], reaction[2]) node2 = (reaction[1], reaction[3]) self.G.add_edge(node1, node2) if i % calculate_every != 0: i += 1 continue i += 1 cycles = nx.minimum_cycle_basis(self.G) number = len(cycles) mean_length = 0 if number > 0: mean_length = sum(len(x) for x in cycles) / float(number) cycles_number[-1].append(number) cycles_mean_length[-1].append(mean_length) print('') self.cycles_number = np.array(cycles_number) self.cycles_mean_length = np.array(cycles_mean_length) # mean_number_ave = np.mean(number_ave_data, axis=0) # mean_weight_ave = np.mean(weight_ave_data, axis=0) mean_cycles_number = self._mean(cycles_number) mean_cycles_mean_length = self._mean(cycles_mean_length, exclude_zeros=True) self.topology_data["cycles_number"] = mean_cycles_number self.topology_data["cycles_mean_length"] = mean_cycles_mean_length
def compute_bonds(structures, molecules): out_name = [] out_a0 = [] out_a1 = [] out_n = [] out_dist = [] out_error = [] out_type = [] cycle_name = [] cycle_index = [] cycle_seq = [] cycle_atom_index = [] charge_name = [] charge_atom_index = [] charge_value = [] for imol, name in tqdm(list(enumerate(molecules))): molecule = structures.loc[name] error = 0 atoms = molecule.atom.values atoms_idx = molecule.atom_index.values n_avail = np.asarray([VALENCE_STD[a] for a in atoms]) n_charge = np.zeros(len(atoms), dtype=np.float16) isleaf = np.zeros( len(atoms), dtype=np.bool) # is the atom in the leafs of connection tree? coords = molecule[['x', 'y', 'z']].values kdt = KDTree( coords) # use an optimized structure for closest match query nbond = {} connected = {i: {} for i in atoms_idx} # select Hydrogen first to avoid butadyne-like ordering failures (molecule_name=dsgdb9nsd_000023) ordered_atoms_index = list(atoms_idx) ordered_atoms_index.sort(key=lambda i: BOND_ORDER[atoms[i]]) ordered_atoms_index = np.asarray(ordered_atoms_index) # STEP 1: 1-bond connect each atom with closest match # only one bond for each atom pair is done in step 1 for a0 in ordered_atoms_index: search_bonds(kdt, n_avail, nbond, connected, isleaf, coords, atoms, atoms_idx, a0, connect_once=True, VALENCE=VALENCE_STD) # STEP 2: greedy connect n-bonds, progressing from leafs of connection tree while (((n_avail > 0).sum() > 0) and isleaf).sum() > 0: progress = False for a0 in ordered_atoms_index: #print("leaF/Trunk & avail: " + ", ".join([f"{i}:{atoms[i]}={leaflabel[leaf[i]]}{n_avail[i]}" # for i in ordered_atoms_index])) if (n_avail[a0] > 0) and isleaf[a0]: for a1 in connected[a0]: if (n_avail[a0] > 0) and (n_avail[a1] > 0): add_bond(n_avail, nbond, a0, a1) progress = True if (n_avail[a0] == 0) or (n_avail[a1] == 0): isleaf[a0] = 1 isleaf[a1] = 1 if not progress: break # gather remaining multiple bonds if n_avail.sum() > 0: for key in nbond.keys(): a0, a1 = key while (n_avail[a0] > 0) and (n_avail[a1] > 0): add_bond(n_avail, nbond, a0, a1) # STEP 3: search for known ionized radicals if n_avail.sum() > 0: for (i, a) in zip(atoms_idx, atoms): if a == 'N': # NH3+ bonded_str, bonded_idx = get_bonded_atoms(atoms, nbond, i) if (bonded_str == "HHH") and (n_avail[i] == 0): # add a valence unit and search a dangling bond nearby n_avail[i] += 1 n_charge[i] += 1 if search_bonds(kdt, n_avail, nbond, connected, isleaf, coords, atoms, atoms_idx, i, connect_once=False, VALENCE=VALENCE_MAX): print(f"++ NH3+ found for {name} atom_index={i}") else: print( f"** NH3+ bonding failure for {name} atom_index={i}" ) elif (a == 'O') and (n_avail[i] == 1): # COO- bonded_str, bonded_idx = get_bonded_atoms(atoms, nbond, i) if bonded_str == "C": C_i = bonded_idx[0] C_bonded_str, C_bonded_idx = get_bonded_atoms( atoms, nbond, C_i) if ("OO" in C_bonded_str): has_2CO = False #print (C_bonded_str, C_bonded_idx, nbond, name) for a1, i1 in zip(C_bonded_str, C_bonded_idx): key = tuple(sorted((C_i, i1))) if (a1 == 'O') and (nbond[key][0] == 2): has_2CO = True if (len(C_bonded_idx) == 3) and has_2CO: # found carboxyle! n_avail[i] -= 1 print( f"** COO- found for {name} C_atom_index={C_i}" ) for a1, i1 in zip(C_bonded_str, C_bonded_idx): if a1 == 'O': n_charge[i1] = -0.5 key = tuple(sorted((C_i, i1))) nbond[key][0] = 1.5 # detect cycles : algo complexity in O(m^2 * n) # paper : https://link.springer.com/article/10.1007/s00453-007-9064-z # nx doc: https://networkx.github.io/documentation/latest/reference/algorithms/generated/networkx.algorithms.cycles.minimum_cycle_basis.html graph = nx.Graph([bond for bond in nbond.keys()]) unordered_cycles = nx.minimum_cycle_basis(graph) # index atoms by their sequential order in the cycle: i.e follow bonds # Note: this code can be written in a much cleaner way! if len(unordered_cycles) > 0: for icycle, c in enumerate(unordered_cycles): available = {i: 1 for i in c} a0 = c[0] cycle = [a0] del (available[a0]) for index in range(1, len(c)): # get atoms bonded to a0 bonded = [b for b in nbond.keys() if a0 in b] bonded = list( map(lambda b: b[0] if b[1] == a0 else b[1], bonded)) # get next atom and remove it from cycle assert (len(bonded) > 0) found = False for a1 in bonded: if (a1 in bonded) and (a1 in available): cycle.append(a1) del (available[a1]) a0 = a1 found = True break assert (found) # and add cycles found to the cycle dataframe lists cycle_name.extend([name] * len(cycle)) cycle_index.extend([icycle] * len(cycle)) cycle_seq.extend(np.arange(len(cycle))) cycle_atom_index.extend(cycle) # display info on failed molecules if n_avail.sum() > 0: error = 1 print( f" Remaining bondings={n_avail.sum()} for molecule_name={name}, atoms: " + ", ".join( [f"{i}:{atoms[i]}" for i in atoms_idx if n_avail[i] > 0])) # inputs for DataFrame bonds for (a0, a1), (n, dist) in nbond.items(): # append to python lists which is 7x faster than toa pd.DataFrame out_name.append(name) out_a0.append(a0) out_a1.append(a1) out_n.append(n) out_dist.append(dist) out_error.append(error) out_type.append(f"{n:0.1f}" + "".join(sorted(f"{atoms[a0]}{atoms[a1]}"))) # inputs for DataFrame charges charge_name.extend([name] * len(atoms)) charge_atom_index.extend(molecule.atom_index.values) charge_value.extend(n_charge) bonds = pd.DataFrame({ 'molecule_name': out_name, 'atom_index_0': out_a0, 'atom_index_1': out_a1, 'nbond': out_n, 'L2dist': out_dist, 'error': out_error, 'bond_type': out_type }) charges = pd.DataFrame({ 'molecule_name': charge_name, 'atom_index': charge_atom_index, 'charge': charge_value }) cycles = pd.DataFrame({ 'molecule_name': cycle_name, 'cycle_index': cycle_index, 'cycle_seq': cycle_seq, 'atom_index': cycle_atom_index }) return bonds, charges, cycles
def calculate(graph): if nx.is_connected(graph): return len(nx.minimum_cycle_basis(graph)) else: return 10**10
def make_graph(self): """Generate edge information to create a graph Using the intersection of each face, create edge information that makes up the face Args: self.new_line_info: List of aligned floor points self.new_line_info2: List of aligned intersections points Returns: new_edges: List of edge information pointcloud_i: Index information of PointCloud including each surface """ ceiling, floor, wall = [], [], [] self.make_line_info() if len(self.index_value) > 0: G = nx.Graph() # Making the graph using index_value list G.add_edges_from(self.index_value) # Finding the minimum cycle from the Graph cycles_list = nx.minimum_cycle_basis(G) if len(cycles_list) >= 1: delete_edges = [] temp_delete = [] # Finding the overlapping edges between generated cycles for each_cycle in cycles_list: # First making original edge information the each cycles node and self.index_value # eahc_cycle is not sorted for each_cycle_i in range(len(each_cycle) - 1): for each_edge in self.index_value: if each_cycle[each_cycle_i] in each_edge: index_node = each_edge.index( each_cycle[each_cycle_i]) for each_cycle_j in range( each_cycle_i + 1, len(each_cycle)): if each_cycle[each_cycle_j] in each_edge: if index_node == 0: temp_edge = [ each_cycle[each_cycle_i], each_cycle[each_cycle_j] ] else: temp_edge = [ each_cycle[each_cycle_j], each_cycle[each_cycle_i] ] temp_delete.append(temp_edge) delete_dup_edges = list(set(map(tuple, temp_delete))) if len(delete_dup_edges) > 0: for delete_edge in delete_dup_edges: # Second checking the duplicate edges if temp_delete.count([delete_edge[0], delete_edge[1] ]) > 1: delete_edges.append( [delete_edge[0], delete_edge[1]]) # After removing the duplicate edges, finding all cycles included from the Graph if len(delete_edges) > 0: G.remove_edges_from(delete_edges) new_cycles = nx.minimum_cycle_basis(G) ceiling, floor, wall = self.get_newGraph(G, new_cycles) else: ceiling, floor, wall = self.get_newGraph(G, cycles_list) return ceiling, floor, wall
g=np.ndarray.tolist(gnp) tr1=numTriangles(g) # print(tr1) len_trlist=len(tr1) if(len_trlist==0): print("triangles removed from graph") else: continue if(nx.is_connected(graph)==False): print("removing triangles causes disconnection!") continue # cycl_list=nx.cycle_basis(graph, 0) cycl_list=nx.minimum_cycle_basis(graph) if(len(cycl_list)): # girthObtained=len(min(cycl_list, key=len)) girthObtained=len(cycl_list[0]) girthCycle=cycl_list[0] # girthCycle=min(cycl_list, key=len) print("girth = "+str(girthObtained)) else: print("no cycles") continue if (girthObtained!=girth): print("girth doesn't match") continue else:
def __create_loop_relationships__(G, initial_cycle_weight, cycle_weight, edge_attribute): """ helper function to determine child parent relationship for loops (or any other structural components) Input networkx graph G edge_attribute str, the name of the edge weight to be considered for type cycle cycle_weight the weight based on which edge are removed max: edge with the highest edge weight min: edge with the lowest edge weight betweenness_max: edge with the highest betweenness value betweenness_min: edge with the lowest betweenness value initial_cycle_weight if true then the initial cycle basis is estimated based on edge weight with https://networkx.github.io/documentation/stable/reference/algorithms/generated/networkx.algorithms.cycles.minimum_cycle_basis.html#networkx.algorithms.cycles.minimum_cycle_basis else the initial cycles are estimated based on steps only with https://networkx.github.io/documentation/stable/reference/algorithms/generated/networkx.algorithms.cycles.cycle_basis.html#networkx.algorithms.cycles.cycle_basis Output dict of final loops dict of relationships dict of all loops """ if initial_cycle_weight: cycles = nx.minimum_cycle_basis(G) else: cycles = nx.cycle_basis(G) #print("cycles", cycles) #convert cycles into dict & edge list cycles_dict = __convert_cycles__(cycles) #print("cycles", cycles_dict) #construct dict to save cycles and give them id which is saved in tree #append each new created loop to dict save_loops = {} all_loops = {} for i in range(len(cycles_dict)): all_loops[i] = cycles_dict[i] save_loops[i] = cycles_dict[i] #2nd dict that stores loop id and child nodes in list #initialize leave nodes relationship = {} for loop in all_loops: relationship[loop] = [None, None] #merged cycles - stores cycles already merged merged_nodes = [] print("start creating hierarchical tree") while len(G.edges()) > 0: #community weight functions are used to find the most valuable edge if cycle_weight == "max": to_remove = __by_weight__(G, w_max=True, attribute=edge_attribute) elif cycle_weight == "min": to_remove = __by_weight__(G, w_max=False, attribute=edge_attribute) elif cycle_weight == "betweenness_max": to_remove = __by_centrality__(G, w_max=True, attribute=edge_attribute, type="betweenness") elif cycle_weight == "betweenness_min": to_remove = __by_centrality__(G, w_max=False, attribute=edge_attribute, type="betweenness") else: print("cycle weight not known, please select another one") return None break #remove edge from graph G.remove_edge(to_remove[0], to_remove[1]) children = [] cycle = [] for loop_ID, loop in all_loops.items(): #compare edges (independent of direction (x,y) == (y,x)) #print(loop) #print(to_remove) if ((to_remove in loop) or ((to_remove[1], to_remove[0]) in loop)): children.append(loop_ID) merged_nodes.append(loop_ID) #new cycle is made up of all child edges except the removed edge for edge in loop: if ((edge not in cycle) and ((edge[1], edge[0]) not in cycle)): if edge != to_remove and edge != (to_remove[1], to_remove[0]): cycle.append(edge) new_key = int(sorted(save_loops.keys())[-1]) + 1 all_loops[new_key] = cycle save_loops[new_key] = cycle #and remove merged loops for child in children: del all_loops[child] #add parent child connection to relationship new_stage = int(sorted(relationship.keys())[-1]) + 1 #find ID of children relationship[new_key] = children print("finished edge removal") return all_loops, relationship, save_loops
from bmpga.utils.io_utils import XYZReader reader = XYZReader() XYZs = glob("test_data/*.xyz") print(XYZs) for xyz in XYZs: print(xyz) cluster = reader.read(xyz, return_clusters=True)[0] print(cluster.molecules) graph = cluster.molecules[0].to_graph() nodes = graph.nodes for node in nodes: print(node.__dict__) print(graph.nodes) print(graph.edges) cycles = nx.cycle_basis(graph) print(cycles) print(len(cycles)) print(nx.minimum_cycle_basis(graph))
def get_cycle_lengths(graph): """Get the length of cycles in a graph""" cycles = nx.minimum_cycle_basis(graph) lengths = count_sublist_lengths(cycles) return lengths