def test_bad_node_select(self, dim):
     """Tests if function raises a ``ValueError`` when input an invalid ``node_select``
     argument"""
     graph = nx.barbell_graph(dim, 0)
     s = list(range(2 * dim))
     with pytest.raises(ValueError, match="Node selection method not recognized"):
         clique.shrink(s, graph, node_select="")
    def test_weight_and_degree_ties(self, dim, monkeypatch):
        """Test that the function settles ties at random when node-weight based node selection is
        used but there is still a tie, i.e., nodes with equal weight. The starting graph is a
        complete graph with one edge removed between the first two nodes, and the subgraph is
        taken to be the whole graph. All nodes have equal weight. In this problem, the function
        can either remove the first or second node to make a clique. Either node should be
        removed at random, and this test monkeypatches the ``np.random.choice`` call to ensure
        both eventualities occur."""
        graph = nx.complete_graph(dim)
        subgraph = graph.nodes()
        graph.remove_edge(0, 1)
        weights = [1] * dim

        patch_random_choice_1 = functools.partial(patch_random_choice, element=0)
        patch_random_choice_2 = functools.partial(patch_random_choice, element=1)

        with monkeypatch.context() as m:
            m.setattr(np.random, "choice", patch_random_choice_1)
            c1 = clique.shrink(subgraph, graph, node_select=weights)
        with monkeypatch.context() as m:
            m.setattr(np.random, "choice", patch_random_choice_2)
            c2 = clique.shrink(subgraph, graph, node_select=weights)

        target1 = list(range(1, dim))
        target2 = [0] + list(range(2, dim))

        assert c1 != c2
        assert c1 == target1 and c2 == target2
 def test_bad_weights(self, dim, graph):
     """Test if function raises a ``ValueError`` when a vector of node weights input to
     ``node_select`` is not of the same dimension as the input graph."""
     s = [0, 1]
     w = np.ones(dim - 1)
     with pytest.raises(ValueError, match="Number of node weights must match number of nodes"):
         clique.shrink(s, graph, node_select=w)
    def test_input_clique_then_output_clique(self, dim):
        """Test that if the input is already a clique, then the output is the same clique."""
        graph = nx.lollipop_graph(dim, dim)
        subgraph = list(
            range(dim))  # this is a clique, the "candy" of the lollipop

        assert clique.shrink(subgraph, graph) == subgraph
 def test_is_output_clique(self, dim):
     """Test that the output subgraph is a valid clique, in this case the maximum clique
     in a lollipop graph"""
     graph = nx.lollipop_graph(dim, dim)
     subgraph = list(range(2 * dim))  # subgraph is the entire graph
     resized = clique.shrink(subgraph, graph)
     assert clique.is_clique(graph.subgraph(resized))
     assert resized == list(range(dim))
 def test_degree_relative_to_subgraph(self, dim):
     """Test that function removes nodes of small degree relative to the subgraph,
     not relative to the entire graph. This is done by creating an unbalanced barbell graph,
     with one "bell" larger than the other. The input subgraph is the small bell (a clique) plus
     a node from the larger bell. The function should remove only the node from the larger
     bell, since this has a low degree within the subgraph, despite having high degree overall"""
     g = nx.disjoint_union(nx.complete_graph(dim), nx.complete_graph(dim + 1))
     g.add_edge(dim, dim - 1)
     subgraph = list(range(dim + 1))
     assert clique.shrink(subgraph, g) == list(range(dim))
    def test_wheel_graph(self, dim):
        """Test that output is correct for a wheel graph, whose largest cliques have dimension
        3. The cliques always include the central spoke and two consecutive nodes in the outer
        wheel."""
        graph = nx.wheel_graph(dim)
        subgraph = graph.nodes()  # subgraph is the entire graph
        subgraph = clique.shrink(subgraph, graph)

        assert len(subgraph) == 3
        assert subgraph[0] == 0
        assert subgraph[1] + 1 == subgraph[2] or (subgraph[1] == 1 and subgraph[2] == dim - 1)
    def test_weight_based_ties(self, dim):
        """Test that the function returns the correct clique when using weight-based node
        selection to settle ties. The starting graph is a barbell graph and the subgraph is taken
        to be the whole graph. The weights of the first clique in the barbell are set to be less
        than the weights of the second, so that we expect the function to return the second
        clique."""
        graph = nx.barbell_graph(dim, 0)
        subgraph = graph.nodes()
        weights = [1] * dim + [2] * dim

        c = clique.shrink(subgraph, graph, node_select=weights)
        assert c == list(range(dim, 2 * dim))
    def test_wheel_graph_tie(self, dim, monkeypatch):
        """Test that output is correct for a wheel graph, whose largest cliques have dimension
        3. The cliques always include the central spoke and two consecutive nodes in the outer
        wheel. Since the function uses randomness in node selection when there is a tie (which
        occurs in the case of the wheel graph), the resultant shrunk cliques are expected to be
        different each time ``shrink`` is run. This function monkeypatches the
        ``np.random.choice`` call so that different nodes are removed during each run."""
        graph = nx.wheel_graph(dim)
        subgraph = graph.nodes()  # subgraph is the entire graph

        patch_random_choice_1 = functools.partial(patch_random_choice, element=0)
        patch_random_choice_2 = functools.partial(patch_random_choice, element=1)

        with monkeypatch.context() as m:
            m.setattr(np.random, "choice", patch_random_choice_1)
            c1 = clique.shrink(subgraph, graph)
        with monkeypatch.context() as m:
            m.setattr(np.random, "choice", patch_random_choice_2)
            c2 = clique.shrink(subgraph, graph)

        assert c1 != c2
u_dens = []

for s in samples:
    uniform = list(np.random.choice(24, 8,
                                    replace=False))  # generates uniform sample
    GBS_dens.append(nx.density(TA_graph.subgraph(s)))
    u_dens.append(nx.density(TA_graph.subgraph(uniform)))

print("GBS mean density = {:.4f}".format(np.mean(GBS_dens)))
print("Uniform mean density = {:.4f}".format(np.mean(u_dens)))

##############################################################################
# Those look like great GBS samples 💪! To obtain cliques, we shrink the samples by greedily
# removing nodes with low degree until a clique is found.

shrunk = [clique.shrink(s, TA_graph) for s in samples]
print(clique.is_clique(TA_graph.subgraph(shrunk[0])))

##############################################################################
# Let's take a look at some of these cliques. What are the clique sizes in the first ten samples?
# What is the average clique size? How about the largest and smallest clique size?

clique_sizes = [len(s) for s in shrunk]
print("First ten clique sizes = ", clique_sizes[:10])
print("Average clique size = {:.3f}".format(np.mean(clique_sizes)))
print("Maximum clique size = ", np.max(clique_sizes))
print("Minimum clique size = ", np.min(clique_sizes))

##############################################################################
# Even in the first few samples, we've already identified larger cliques than the 4-node clique
# we studied before. Awesome! Indeed, this simple shrinking strategy gives cliques with average
u_dens = []

for s in samples:
    uniform = list(np.random.choice(16, 20))  
    GBS_dens.append(nx.density(PH_graph.subgraph(s)))
    u_dens.append(nx.density(PH_graph.subgraph(uniform)))

print("GBS mean density = {:.4f}".format(np.mean(GBS_dens)))
print("Uniform mean density = {:.4f}".format(np.mean(u_dens)))
# GBS mean density = 0.3714
# Uniform mean density = 0.1673

---------------------------------------------------------------------

# Perform greedy shrinking until the subgraph is a maxclique
shrunk = [clique.shrink(s, PH_graph) for s in samples]
print(clique.is_clique(PH_graph.subgraph(shrunk[0])))

# Find the average clique size
searched = [clique.search(s, PH_graph, 10) for s in shrunk]
clique_sizes = [len(s) for s in searched]
print("Average clique size = {:.3f}".format(np.mean(clique_sizes)))
# Average clique size = 6.664

---------------------------------------------------------------------

# Plot one of the samples of the average sized cliques
clique_fig = plot.graph(PH_graph, searched[0])
clique_fig.show()

# Find the largest clique (aka the maxclique) and plot it!
GBS_dens = []
u_dens = []

for s in samples:
    uniform = list(np.random.choice(24, 8, replace=False))  # generates uniform sample
    GBS_dens.append(nx.density(TA_graph.subgraph(s)))
    u_dens.append(nx.density(TA_graph.subgraph(uniform)))

print("GBS mean density = {:.4f}".format(np.mean(GBS_dens)))
print("Uniform mean density = {:.4f}".format(np.mean(u_dens)))

##############################################################################
# Those look like great GBS samples 💪! To obtain cliques, we shrink the samples by greedily
# removing nodes with low degree until a clique is found.

shrunk = [clique.shrink(s, TA_graph) for s in samples]
print(clique.is_clique(TA_graph.subgraph(shrunk[0])))

##############################################################################
# Let's take a look at some of these cliques. What are the clique sizes in the first ten samples?
# What is the average clique size? How about the largest and smallest clique size?

clique_sizes = [len(s) for s in shrunk]
print("First ten clique sizes = ", clique_sizes[:10])
print("Average clique size = {:.3f}".format(np.mean(clique_sizes)))
print("Maximum clique size = ", np.max(clique_sizes))
print("Minimum clique size = ", np.min(clique_sizes))

##############################################################################
# Even in the first few samples, we've already identified larger cliques than the 4-node clique
# we studied before. Awesome! Indeed, this simple shrinking strategy gives cliques with average