Ejemplo n.º 1
0
def plot_right_panel(ax, parts):
    """Plots CHAMP domains and halfspace intersection from sampled partitions"""
    G = ig.Graph.Famous("Zachary")
    twom = 2 * G.ecount()

    domains = CHAMP_2D(G, [p.membership for p in parts], PLOT_XLIM[0], PLOT_XLIM[1])

    # Assume that all but the gamma=1.0 domain were optimal
    xs1 = [domains[0][0], domains[0][1]]
    xs3 = [domains[1][0], domains[1][1]]
    xs4 = [domains[2][0], domains[2][1]]
    ys1 = [parts[0].quality(resolution_parameter=g) / twom for g in xs1]
    ys3 = [parts[2].quality(resolution_parameter=g) / twom for g in xs3]
    ys4 = [parts[3].quality(resolution_parameter=g) / twom for g in xs4]

    # Plot halfspaces where the corresponding partition is optimal
    ax.plot(xs1, ys1, c="C0")
    ax.plot(xs3, ys3, c="C2")
    ax.plot(xs4, ys4, c="C3")

    # Project domains of optimality onto gamma axis
    ax.fill_between(xs1, [0] * len(xs1), [0.01] * len(xs1), color="C0")
    ax.fill_between(xs3, [0] * len(xs3), [0.01] * len(xs3), color="C2")
    ax.fill_between(xs4, [0] * len(xs4), [0.01] * len(xs4), color="C3")

    ax.axvline(xs1[-1], ymin=0, ymax=ys1[-1] / 0.8, c='black', linestyle='--')
    ax.axvline(xs3[-1], ymin=0, ymax=ys3[-1] / 0.8, c='black', linestyle='--')

    ax.set_xlabel(r"$\gamma$", fontsize=14)
    ax.set_title("CHAMP Optimal Partitions and Domains", fontsize=14)
    ax.set_xlim(PLOT_XLIM)
    ax.set_ylim(PLOT_YLIM)
Ejemplo n.º 2
0
def plot_gamma_estimates(all_parts):
    G = ig.Graph.Famous("Zachary")

    # Print details on the CHAMP sets when the number of communities is restricted
    # print(len(all_parts), "unique partitions in total")
    # for K in range(2, 9):
    #     restricted_parts = {p for p in all_parts if num_communities(p) == K}
    #     print(f"{len(restricted_parts)} unique partitions with {K} communities")
    #     ranges = CHAMP_2D(G, restricted_parts, GAMMA_START, GAMMA_END)
    #     print(f"{len(ranges)} unique partitions in {K}-community CHAMP set")
    #     print("=" * 50)

    ranges = CHAMP_2D(G, all_parts, GAMMA_START, GAMMA_END)
    gamma_estimates = ranges_to_gamma_estimates(G, ranges)

    # Print details on the CHAMP set when the number of communities is not restricted
    # community_counts = [0] * 9
    # for _, _, membership, _ in gamma_estimates:
    #     community_counts[num_communities(membership)] += 1
    # for k, count in enumerate(community_counts):
    #     print(f"{count} unique partitions with {k} communities in total CHAMP set")

    # Plot gamma estimates and domains of optimality when the number of communities is not restricted
    plt.rc('text', usetex=True)
    plt.rc('font', family='serif')
    plot_estimates(gamma_estimates)
    plt.title(r"Karate Club CHAMP Domains of Optimality and $\gamma$ Estimates", fontsize=14)
    plt.xlabel(r"$\gamma$", fontsize=14)
    plt.ylabel("Number of communities", fontsize=14)
    plt.savefig("karate_club_CHAMP_gamma_estimates.pdf")
Ejemplo n.º 3
0
    def test_champ_correctness_igraph_famous_louvain(self):
        """Test CHAMP correctness on various famous graphs while obtaining partitions via Louvain.

        The correctness of the CHAMP domains are checked for the original undirected and (symmetric) directed variants.
        """

        for G in generate_igraph_famous():
            gammas = generate_random_values(100, start_value=0, end_value=5)
            partitions = repeated_louvain_from_gammas(G, gammas)
            champ_ranges = CHAMP_2D(G, partitions, gamma_0=0, gamma_f=5)
            self.assert_best_partitions_match_champ_set(
                G, partitions, champ_ranges, gammas)

            G.to_directed()  # check the directed version of the graph as well
            partitions = repeated_louvain_from_gammas(G, gammas)
            champ_ranges = CHAMP_2D(G, partitions, gamma_0=0, gamma_f=5)
            self.assert_best_partitions_match_champ_set(
                G, partitions, champ_ranges, gammas)
Ejemplo n.º 4
0
    def assert_champ_correctness_unweighted_ER(self,
                                               n=100,
                                               m=1000,
                                               directed=False,
                                               num_partitions=10,
                                               num_gammas=100,
                                               K_max=5,
                                               gamma_start=0.0,
                                               gamma_end=2.0):
        G = generate_connected_ER(n=n, m=m, directed=directed)
        partitions = generate_random_partitions(num_nodes=n,
                                                num_partitions=num_partitions,
                                                K_max=K_max)
        gammas = generate_random_values(num_gammas, gamma_start, gamma_end)
        champ_ranges = CHAMP_2D(G, partitions, gamma_start, gamma_end)

        self.assert_best_partitions_match_champ_set(G, partitions,
                                                    champ_ranges, gammas)
Ejemplo n.º 5
0
def plot_stable_partitions(all_parts):
    G = ig.Graph.Famous("Zachary")

    # Store shared force-directed layout to make later plotting layouts consistent
    layout = G.layout_fruchterman_reingold(niter=1000)

    # Plot stable partitions when the number of communities is restricted to 2-4
    for K in range(2, 5):
        restricted_parts = {p for p in all_parts if num_communities(p) == K}

        if len(restricted_parts) > 0:
            ranges = CHAMP_2D(G, restricted_parts, GAMMA_START, GAMMA_END)
            gamma_estimates = ranges_to_gamma_estimates(G, ranges)
            stable_parts = gamma_estimates_to_stable_partitions(gamma_estimates)

            for i, p in enumerate(stable_parts):
                ig.plot(louvain.RBConfigurationVertexPartition(G, initial_membership=p),
                        f"karate_club_{K}_stable{i}.png", bbox=(1000, 1000), layout=layout)
Ejemplo n.º 6
0
def test(gamma_0=0.0, gamma_f=2.0, louvain_iterations=100, timeout=60):
    """Runs a simple benchmark on the Karate Club of our parallel louvain implementation vs. CHAMP's

    :param num_processors: number of CPUs to use
    :param gamma_0: starting gamma
    :param gamma_f: final gamma
    :param louvain_iterations: number of initial louvain iterations
    :param timeout: maximum allowed time (in seconds) for a set of louvain runs
    """

    xs = []
    ys1 = []
    ys2 = []
    our_duration = 0
    champ_duration = 0

    num_processors = cpu_count()
    print(f"Test with {num_processors} CPUs")
    print(f'{"# partitions":>15} {"Our time (s)":>15} {"CHAMP time (s)":>15}')

    while our_duration < timeout:
        xs.append(louvain_iterations)
        print(f"{louvain_iterations:>15} ", end='', flush=True)

        start = time()
        all_parts = repeated_parallel_louvain_from_gammas(G, gammas=np.linspace(gamma_0, gamma_f, louvain_iterations),
                                                          show_progress=False)
        _ = CHAMP_2D(G, all_parts, gamma_0, gamma_f)
        our_duration = time() - start

        ys1.append(our_duration)
        print(f"{our_duration:>15.2f} ", end='', flush=True)

        if champ_duration < timeout:
            start = time()
            _ = parallel_louvain(G, gamma_0, gamma_f, numruns=louvain_iterations, numprocesses=num_processors)
            champ_duration = time() - start
            ys2.append(champ_duration)
            print(f"{champ_duration:>15.2f}")
        else:
            print(f"{0:>15.2f}")

        # the number of louvain iterations roughly increases by 1.5x each iteration
        louvain_iterations = louvain_iterations + louvain_iterations // 2
def run_CHAMP(G, all_parts, gamma_start=GAMMA_START, gamma_end=GAMMA_END):
    ranges = CHAMP_2D(G, all_parts, gamma_start, gamma_end)
    gamma_estimates = ranges_to_gamma_estimates(G, ranges)
    return gamma_estimates
        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")

                ranges = CHAMP_2D(G, [p1, p2], 0, 2)
                gamma_estimates = ranges_to_gamma_estimates(G, ranges)

                plt.rc('text', usetex=True)
                plt.rc('font', family='serif')
                plot_estimates(gamma_estimates)
                plt.title(r"Domains of Optimality with Loop in $\gamma$ Estimates", fontsize=14)
                plt.ylabel("Number of communities", fontsize=14)
                plt.xlabel(r"$\gamma$", fontsize=14)
                plt.savefig("estimation_loop_estimates.pdf")
                break
Ejemplo n.º 9
0
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)
Ejemplo n.º 10
0
        except ig.InternalError:
            continue

        if not G.is_connected():
            continue

        gammas = np.linspace(GAMMA_START, GAMMA_END, GAMMA_ITERS)
        all_parts = [singlelayer_louvain(G, g) for g in gammas]

        if len(all_parts) == 0:
            continue

        # Print details on the CHAMP set when the number of communities is not restricted
        ranges = CHAMP_2D(G,
                          all_parts,
                          GAMMA_START,
                          GAMMA_END,
                          single_threaded=True)
        gamma_estimates = ranges_to_gamma_estimates(G, ranges)

        def gamma_to_domain(gamma):
            for gamma_start, gamma_end, part, gamma_est in gamma_estimates:
                if gamma_start <= gamma <= gamma_end:
                    return part, gamma_est
            return None, None

        for i in range(len(gamma_estimates)):
            _, _, old_membership, old_estimate = gamma_estimates[i]

            for update_iteration in range(1000):
                if old_estimate is None: