diff --git a/tools.py b/tools.py new file mode 100644 index 0000000000000000000000000000000000000000..1cc91f6caaaa812d4ec4fddb95294fdc044d14a0 --- /dev/null +++ b/tools.py @@ -0,0 +1,436 @@ + +import networkx as nx +from math import cos,sin,pi,ceil +import random as rd + +def measure_visibility(S,all_qclqs): + "measure the min and average visibility of the quasi-cliques of all_qclqs with respect to S" + + def visibility(Q): + "measure the visibility of Q with respect to S" + + maxi = 0 + + for Q2 in S: + vis_Q2_Q = len(set(Q2) & set(Q))/len(set(Q)) # definition of the visibility + if vis_Q2_Q > 0.999: + return 1 + elif vis_Q2_Q > maxi: + maxi = vis_Q2_Q + + return maxi + + if len(all_qclqs) == 0: + return 1,1 + + min_vis = 1 + avg_vis = 0 + + for Q in all_qclqs: + vis_S_Q = visibility(Q) + avg_vis += vis_S_Q + min_vis = min(min_vis,vis_S_Q) + + return avg_vis/len(all_qclqs), min_vis + + +def load_graph(path2file): + "load a graph from a .txt file, each line of the .txt file corresponds to an edge" + + G = nx.Graph() + + file = open(path2file,'r') + + line = file.readline().rstrip('\n\r') + + while line: + l = line.split() + u = int(l[0]) + v = int(l[1]) + + G.add_edge(u,v) + + line = file.readline().rstrip('\n\r') + + file.close() + + return G + +def random_subgraph(G,n,n_0): + """ return H=(V',E') a random induced subgraph of G, H is generated according to the following rules: + - n_0 first vertices are added to V' + - add vertices from the neighbours of V' until |V'|=n + rk : this function uses the random package, use rd.seed(new_seed) to modify the seed + """ + + V_prime = list(G.nodes) + rd.shuffle(V_prime) + V_prime = set(V_prime[:n_0]) + initials = V_prime.copy() + + candidates = set() + for u in V_prime: + candidates = candidates | set(G[u]) + + candidates -= V_prime + candidates = list(candidates) + + while len(V_prime)<n: + if len(candidates)>0: + i = rd.randrange(0,len(candidates)) + v = candidates[i] + + else: # assuming here that V-V' is not the empty set + l = list(set(G.nodes)-set(V_prime)) + i = rd.randrange(0,len(l)) + v = l[i] + + V_prime.add(v) + neigh_v = set(G[v])-set(V_prime)-set(candidates) + candidates = candidates + list(neigh_v) + candidates.remove(v) + + H = G.subgraph(V_prime) + H2 = nx.Graph() + H2.add_nodes_from(H.nodes) + H2.add_edges_from(H.edges) + + return H2,initials + + + +def add_rd_edges(G, n_edges): + "add n_edges new random edges to G" + + n = len(G.nodes) + non_edges = [(j,i) for i in range(1,n) for j in range(i)] + + for (u,v) in G.edges: + non_edges.remove((u,v)) + + rd.shuffle(non_edges) + + G.add_edges_from(non_edges[:n_edges]) + + + +def rd_nodes_to_supernodes(G, min_supernode_size=10, max_supernode_size=10, proba_clique=1): + "transforme les sommets d'un graphe en ensemble de sommets (cliques ou anticliques)" + + n = len(G.nodes) + supernodes = [] + last_node = -1 # on a deja utilise tous les sommets de 0 a last_node + + for i in range(n): + supernode_size = rd.randrange(min_supernode_size, max_supernode_size+1) + supernodes.append([last_node+1+j for j in range(supernode_size)]) + last_node += supernode_size + + G2 = nx.Graph() + G2.add_nodes_from(range(last_node+1)) + + for i_U in range(1,n): + SU = supernodes[i_U] + for i_V in range(i_U): + if G.has_edge(i_U,i_V): + SV = supernodes[i_V] + G2.add_edges_from([(u,v) for u in SU for v in SV]) + + if rd.random()<=proba_clique: + G2.add_edges_from([(SU[i],SU[j]) for i in range(1,len(SU)) for j in range(i)]) + + return G2,supernodes + + +def compare_graphe(G1,G2): + "renvoie la taille de la difference symetrique des arretes de G1 et G2" + diff = len(set(G1.edges)-set(G2.edges)) + len(set(G2.edges)-set(G1.edges)) + return diff + + +def position_affichage(G,list_comm): + "calcule une position d'affichage pour les sommets d'un graphe de communautes" + pos = {} + + distance = 30 + distance_au_centre = 10 + angle = 2*pi/len(list_comm) + + pas_encore_vu = set(G.nodes) + + for i,C in enumerate(list_comm): + centre_x = distance*cos(i*angle) + centre_y = distance*sin(i*angle) + + copy_pas_encore_vu = pas_encore_vu.copy() + + for u in (copy_pas_encore_vu & set(C)): + pas_encore_vu.remove(u) + x = (2*rd.random()-1)*distance_au_centre + centre_x + y = (2*rd.random()-1)*distance_au_centre + centre_y + pos[u]=(x,y) + + for u in pas_encore_vu: + x = (2*rd.random()-1)*distance_au_centre + y = (2*rd.random()-1)*distance_au_centre + pos[u] = (x,y) + + return pos + +def barycentre(l): + "calcule le barycentre d'une liste de points" + if len(l)==0: + return (0,0) + + S_x = 0 + S_y = 0 + + for (x,y) in l: + S_x += x + S_y += y + + return (S_x/len(l), S_y/len(l)) + +def position_affichage2(G,list_comm): + "calcule une position d'affichage pour les sommets d'un graphe de communautes" + pos = {} + + distance = 50 + distance_au_centre = 10 + angle = 2*pi/len(list_comm) + + centres = [[] for i in range(len(G.nodes))] + + for i,C in enumerate(list_comm): + centre_x = distance*cos(i*angle) + centre_y = distance*sin(i*angle) + + for u in C: + centres[u].append((centre_x,centre_y)) + + for u in G.nodes: + + (x,y) = barycentre(centres[u]) + + dx = (2*rd.random()-1)*distance_au_centre + dy = (2*rd.random()-1)*distance_au_centre + + pos[u] = (x+dx,y+dy) + + return pos + +def position_affichage3(list_comm): + "calcule une position d'affichage pour les sommets d'un graphe de communautes" + pos = {} + + distance = 50 + distance_au_centre = 10 + angle = 2*pi/len(list_comm) + + for i,C in enumerate(list_comm): + centre_x = distance*cos(i*angle) + centre_y = distance*sin(i*angle) + + angle_2 = 2*pi/len(C) + + for j,u in enumerate(C): + dx = distance_au_centre*cos(j*angle_2) + dy = distance_au_centre*sin(j*angle_2) + pos[u] = (centre_x+dx,centre_y+dy) + + return pos + +def position_affichage4(list_comm, pos_centres): + "calcule une position d'affichage pour les sommets d'un graphe de communautes" + pos = {} + + distance_au_centre = 1 + + for i,C in enumerate(list_comm): + centre_x = pos_centres[i][0] + centre_y = pos_centres[i][1] + + angle_2 = 2*pi/len(C) + + for j,u in enumerate(C): + dx = distance_au_centre*cos(j*angle_2) + dy = distance_au_centre*sin(j*angle_2) + pos[u] = (centre_x+dx,centre_y+dy) + + return pos + +def node_in_comm(G,list_comm): + dico = {} + for i in range(len(list_comm)): + C = list_comm[i] + for u in C: + dico[u] = i + return dico + +def non_edges_inside_comms(G,list_comm): + missing_edges = [] + for C in list_comm: + for i in range(1,len(C)): + for j in range(i): + u = C[i] + v = C[j] + #print(u,v,G[u]) + if not (v in G[u]): + missing_edges.append((u,v)) + return missing_edges + +def edges_outside_comms(G,list_comm): + edges = [] + node_comm = node_in_comm(G,list_comm) + + for (i,j) in G.edges: + if node_comm[i] != node_comm[j]: + edges.append((i,j)) + + return edges + +def calcule_gamma(G,X): + gamma_deg = 1 + gamma_density = 0 + for u in X: + gamma_u = len(set(X) & set(G[u]))/(len(X)-1) + gamma_deg = min(gamma_deg, gamma_u) + gamma_density += gamma_u + gamma_density /= len(X) + return gamma_deg, gamma_density + +def est_deg_qclq(G,X,gamma): + "verifie que X est une gamma deg-qclq de G" + for u in X: + if len(set(X) & set(G[u])) < gamma*(len(X)-1): + return False + return True + +def est_density_qclq(G,X,gamma): + "verifie que X est une gamma density-qclq de G" + H = G.subgraph(X) + return (2*len(H.edges)>gamma*len(X)*(len(X)-1)) + +def deleted_edges_in_comm(list_comm,edges_del): + "renvoie les arretes supprimees lors de edge_reduction qui sont dans une communaute" + l = [] + for (u,v) in edges_del: + for X in list_comm: + if (u in X) and (v in X): + l.append((u,v)) + return l + +def deleted_nodes_in_comm(list_comm,nodes_del): + "renvoie les sommets supprimes lors de la reduction qui sont dans une communaute" + l = set() + nodes_del = set(nodes_del) + + for X in list_comm: + l = l|(set(X) & nodes_del) + + return l + +def reduction(G,gamma,min_size,edge_red=False): + + "delete the nodes (and edges) of G not contained in any quasi-cliques" + + def edge_reduction(): + nb_deleted = 0 + list_edges = list(G.edges).copy() + bound = ceil(2*gamma*(min_size-1))-min_size + for (u,v) in list_edges: + if len(set(G[u]) & set(G[v])) < bound: # condition of the lemma + G.remove_edge(u,v) + nb_deleted += 1 + edges_deleted.append((u,v)) + + return nb_deleted + + def node_reduction(): + nb_deleted = 0 + list_nodes = list(G.nodes).copy() + for u in list_nodes: + if len(G[u])< ceil(gamma*(min_size-1)): + G.remove_node(u) + nb_deleted += 1 + nodes_deleted.append(u) + + return nb_deleted + + flag_reduction = True + + while flag_reduction: + nb_edges_deleted = 0 + if edge_red: + nb_edges_deleted = edge_reduction() + nb_nodes_deleted = node_reduction() + flag_reduction = ((nb_edges_deleted+nb_nodes_deleted)>0) + + +def only_maximal(Quasi_Cliques): + + max_Quasi_Cliques = [] + # If C is not maximal then there is C' before C such that C c C' + + for C in Quasi_Cliques: + + is_maximal = True + + for max_C in max_Quasi_Cliques: + if set(C).issubset(set(max_C)): + is_maximal = False + break + + if is_maximal: + max_Quasi_Cliques.append(list(C)) + + return max_Quasi_Cliques + +def connected_components(G): + "compute the connected components of G" + + n = len(G.nodes) + nodes = list(G.nodes) + + colors = {} + + for i in range(n): + u = nodes[i] + colors[u] = i + + for i in range(n): + for (u,v) in G.edges: + coul = min(colors[u],colors[v]) + colors[u] = coul + colors[v] = coul + + + components = {} + comp_color = [] + + for u in G.nodes: + if colors[u] in comp_color: + components[colors[u]].append(u) + else: + comp_color.append(colors[u]) + components[colors[u]] = [u] + + return list(components.values()) + +edges_deleted = [] +nodes_deleted = [] + + + + + + + + + + + + + + +