def test_directed_consistency_igraph_famous(self): """Test gamma estimate consistency on undirected and (symmetric) directed versions of various famous graphs.""" for G in generate_igraph_famous(): random_membership = generate_random_partition(G.vcount(), 5) gamma_undirected = gamma_estimate(G, random_membership) G.to_directed() gamma_directed = gamma_estimate(G, random_membership) self.assertAlmostEqual(gamma_undirected, gamma_directed, places=10)
def run_method(G, ground_truth_communities, num_louvain_runs, method, gamma_sweep_min=0.5, gamma_sweep_max=2.0): """ Run one trial of comparing our benchmark to typical Louvain strategies :param G: graph of interest :param ground_truth_communities: ground truth community vector :param method: "modularity pruning", "modularity pruning ground truth K", "gamma sweep" or "ground truth gamma" :return: list of NMIs compared to the ground truth communities """ ground_truth_gamma = gamma_estimate(G, ground_truth_communities) if ground_truth_gamma > gamma_sweep_max: print(f"Ground truth gamma {ground_truth_gamma:.2f} is large") if ground_truth_gamma is None: raise ValueError( "Cannot use a graph with degenerate ground truth communities") if method == "modularity pruning" or method == "modularity pruning ground truth K" or method == "gamma sweep": gammas = np.linspace(gamma_sweep_min, gamma_sweep_max, num_louvain_runs) elif method == "ground truth gamma": gammas = np.linspace(ground_truth_gamma, ground_truth_gamma, num_louvain_runs) else: raise ValueError(f"Option {method} is not valid") parts = repeated_louvain_from_gammas(G, gammas) if method == "modularity pruning": stable_parts = prune_to_stable_partitions(G, parts, gamma_start=gammas[0], gamma_end=gammas[-1], single_threaded=True) nmis = [nmi(ground_truth_communities, p) for p in stable_parts] elif method == "modularity pruning ground truth K": ground_truth_K = num_communities(ground_truth_communities) stable_parts = prune_to_stable_partitions( G, parts, gamma_start=gammas[0], gamma_end=gammas[-1], restrict_num_communities=ground_truth_K, single_threaded=True) nmis = [nmi(ground_truth_communities, p) for p in stable_parts] else: # method == "gamma sweep" or method == "ground truth gamma": nmis = [nmi(ground_truth_communities, p) for p in parts] return nmis
def generate_boxplot_results(): results = [] for i in range(len(Gs)): for p in pickle.load(open(f"parts{i}.p", "rb")): K = num_communities(p) g_est = gamma_estimate(Gs[i], p) if g_est is not None and 2 <= K < K_MAX: assert g_est < 15 results.append((K, g_est)) pickle.dump(results, open("boxplot_results.p", "wb"))
if __name__ == "__main__": N = 600 B = N // 3 p_in1 = 10 / 99 p_in2 = p_in1 * 0.75 # 5/66 p_out1 = 0.25 / 40 # 1/160 for i, p_out2 in enumerate([0.02, 0.035, 0.05]): # delta pref_matrix = [[p_in1, p_out1, p_out1], [p_out1, p_in2, p_out2], [p_out1, p_out2, p_in2]] block_sizes = [B] * 3 G = ig.Graph.SBM(N, pref_matrix, block_sizes) assert G.is_connected() ground_truth = tuple(i // B for i in range(N)) true_gamma = gamma_estimate(G, ground_truth) ground_truth2 = tuple(min(1, i // B) for i in range(N)) true_gamma2 = gamma_estimate(G, ground_truth2) # Store shared force-directed layout to make later plotting layouts consistent layout = G.layout_fruchterman_reingold(niter=1000) out2 = ig.plot(louvain.RBConfigurationVertexPartition(G, ground_truth), f"bistable_sbm_delta{i}_2-community.png", bbox=(1000, 1000), layout=layout) out3 = ig.plot(louvain.RBConfigurationVertexPartition( G, ground_truth2), f"bistable_sbm_delta{i}_3-community.png", bbox=(1000, 1000), layout=layout)
from modularitypruning.parameter_estimation_utilities import gamma_estimate, ranges_to_gamma_estimates from modularitypruning.plotting import plot_estimates from random import randint import matplotlib.pyplot as plt import numpy as np if __name__ == "__main__": while True: G = ig.Graph.Erdos_Renyi(n=10, m=20) while not G.is_connected(): G = ig.Graph.Erdos_Renyi(n=10, m=20) p1 = sorted_tuple(tuple(randint(0, 2) for _ in range(G.vcount()))) p2 = sorted_tuple(tuple(randint(0, 2) for _ in range(G.vcount()))) g1 = gamma_estimate(G, p1) g2 = gamma_estimate(G, p2) if g1 is None or g2 is None or np.isnan(g1) or np.isnan(g2): continue part1 = louvain_part_with_membership(G, p1) part2 = louvain_part_with_membership(G, p2) if part1.quality(resolution_parameter=g2) > part2.quality(resolution_parameter=g2): if part2.quality(resolution_parameter=g1) > part1.quality(resolution_parameter=g1): layout = G.layout_fruchterman_reingold(niter=1000) out = ig.plot(part1, layout=layout) out.save("estimation_loop1.png") out = ig.plot(part2, layout=layout) out.save("estimation_loop2.png")
def generate_bistable_SBM_test_output(): print("Running bistable SBM test...") N = 2400 p2s = (np.linspace(0.01, 0.02, 5).tolist() + np.linspace(0.02, 0.025, 15).tolist() + np.linspace(0.025, 0.0475, 5).tolist() + np.linspace(0.0475, 0.0525, 15).tolist() + np.linspace(0.0525, 0.06, 5).tolist()) c2s = [] # probability of K=2 stability c3s = [] # probability of K=3 stability cboths = [] # probability of bistability progress = Progress(TRIALS_PER_DELTA * len(p2s)) for p_out2 in p2s: total = 0 count_2stable = 0 count_3stable = 0 count_both_stable = 0 while total < TRIALS_PER_DELTA: p_in1 = 10 / 99 # m_in = 10/3 p_in2 = p_in1 * 0.75 # m_in = 5/2 for each block p_out1 = 0.25 / 40 # m_out = 1.25 # p_out2 in outer loop pref_matrix = [[p_in1, p_out1, p_out1], [p_out1, p_in2, p_out2], [p_out1, p_out2, p_in2]] block_sizes = [N // 3] * 3 G = ig.Graph.SBM(N, pref_matrix, block_sizes) if not G.is_connected(): print("\rDisconnected graph. Skipping...", end='', flush=True) continue ground_truth = tuple(i // block_sizes[0] for i in range(N)) ground_truth2 = tuple( min(1, i // block_sizes[0]) for i in range(N)) true_gamma = gamma_estimate(G, ground_truth) true_gamma2 = gamma_estimate(G, ground_truth2) if true_gamma is None or true_gamma2 is None: print("\rDegenerate ground truth estimate. Skipping...", end='', flush=True) continue all_parts = repeated_parallel_louvain_from_gammas( G, gammas=np.linspace(GAMMA_START, GAMMA_END, LOUVAIN_ITERATIONS_PER_DELTA), show_progress=False) ranges = CHAMP_2D(G, all_parts, GAMMA_START, GAMMA_END) gamma_estimates = ranges_to_gamma_estimates(G, ranges) stable2, stable3 = False, False for g_start, g_end, membership, g_est in gamma_estimates: if g_est is not None and g_start <= g_est <= g_end: if num_communities(membership) == 2: stable2 = True elif num_communities(membership) == 3: stable3 = True if stable2: count_2stable += 1 if stable3: count_3stable += 1 if stable2 and stable3: count_both_stable += 1 total += 1 progress.increment() c2s.append(count_2stable / total) c3s.append(count_3stable / total) cboths.append(count_both_stable / total) progress.done() with open("SBM_constant_probs.out", "w") as f: print(N, file=f) print(p2s, file=f) print(c2s, file=f) print(c3s, file=f) print(cboths, file=f)