def test_invalid_max_size(self): """Test if function raises a ``ValueError`` when an invalid max_size is requested""" dim = 5 with pytest.raises( ValueError, match="max_size must be less than number of nodes in graph"): subgraph.resize([0, 1], nx.complete_graph(dim), 2, dim)
def test_target_big(self, graph): """Test if function raises a ``ValueError`` when a too large number is given for ``target`` """ with pytest.raises( ValueError, match="target must be greater than two and less than"): subgraph.resize(subgraphs=[[0, 1]], graph=graph, target=5)
def test_tie(self, monkeypatch): """Test if function correctly settles ties with random selection. This test starts with the problem of a 6-dimensional complete graph and a 4-node subgraph, with the objective of shrinking down to 3 nodes. In this case, any of the 4 nodes in the subgraph is an equally good candidate for removal since all nodes have the same degree. The test monkeypatches the ``np.random.shuffle`` function to simply permute the list according to an offset, e.g. for an offset of 2 the list [0, 1, 2, 3] gets permuted to [2, 3, 0, 1]. Using this we expect the node to be removed by ``resize`` in this case to have label equal to the offset.""" dim = 6 g = nx.complete_graph(dim) s = [0, 1, 2, 3] def permute(l, offset): d = len(l) ll = l.copy() for _i in range(d): l[_i] = ll[(_i + offset) % d] for i in range(4): permute_i = functools.partial(permute, offset=i) with monkeypatch.context() as m: m.setattr(np.random, "shuffle", permute_i) resized = subgraph.resize(s, g, min_size=3, max_size=3) resized_subgraph = resized[3] removed_node = list(set(s) - set(resized_subgraph))[0] assert removed_node == i
def test_full_range(self, dim, min_size, max_size): """Test if function correctly resizes to full range of requested sizes""" g = nx.complete_graph(dim) resized = subgraph.resize([0, 1, 2], g, min_size, max_size) r = range(min_size, max_size + 1) assert set(resized.keys()) == set(r) subgraph_sizes = [len(resized[i]) for i in r] assert subgraph_sizes == list(r)
def test_callable_input(self, graph): """Tests if function returns the correct output given a custom method set by the user""" objective_return = (0, [0]) def custom_method(*args, **kwargs): """Mockup of custom-method function fed to ``search``""" return objective_return result = subgraph.resize(subgraphs=[[0, 1]], graph=graph, target=4, resize_options={"method": custom_method}) assert result == objective_return
def test_valid_input(self, graph, monkeypatch, methods): """Tests if function returns the correct output under normal conditions. The resizing method is here monkey patched to return a known result.""" objective_return = (0, [0]) def custom_method(*args, **kwargs): """Mockup of custom-method function fed to ``resize``""" return objective_return with monkeypatch.context() as m: m.setattr(subgraph, "RESIZE_DICT", {methods: custom_method}) result = subgraph.resize(subgraphs=[[0, 1]], graph=graph, target=4, resize_options={"method": methods}) assert result == objective_return
def test_resize_integration(graph, target, methods): """Test if function returns resized subgraphs of the correct form given an input list of variable sized subgraphs specified by ``subgraphs``. The output should be a list of ``len( 10)``, each element containing itself a list corresponding to a subgraph that should be of ``len(target)``. Each element in the subraph list should correspond to a node of the input graph. Note that graph nodes are numbered in this test as [0, 1, 4, 9, ...] (i.e., squares of the usual list) as a simple mapping to explore that the resized subgraph returned is still a valid subgraph.""" graph = nx.relabel_nodes(graph, lambda x: x**2) graph_nodes = set(graph.nodes) s_relabeled = [(np.array(s)**2).tolist() for s in subgraphs] resized = subgraph.resize(subgraphs=s_relabeled, graph=graph, target=target, resize_options={"method": methods}) resized = np.array(resized) dims = resized.shape assert len(dims) == 2 assert dims[0] == len(s_relabeled) assert dims[1] == target assert all(set(sample).issubset(graph_nodes) for sample in resized)
def test_correct_resize(self): """Test if function correctly resizes on a fixed example where the ideal resizing is known. The example is a lollipop graph of 6 fully connected nodes and 2 nodes on the lollipop stick. An edge between node zero and node ``dim - 1`` (the lollipop node connecting to the stick) is removed and a starting subgraph of nodes ``[2, 3, 4, 5, 6]`` is selected. The objective is to add-on 1 and 2 nodes and remove 1 node.""" dim = 6 g = nx.lollipop_graph(dim, 2) g.remove_edge(0, dim - 1) s = [2, 3, 4, 5, 6] min_size = 4 max_size = 7 ideal = { 4: [2, 3, 4, 5], 5: [2, 3, 4, 5, 6], 6: [1, 2, 3, 4, 5, 6], 7: [0, 1, 2, 3, 4, 5, 6], } resized = subgraph.resize(s, g, min_size, max_size) assert ideal == resized
def test_target_wrong_type(self, graph): """Test if function raises a ``TypeError`` when incorrect type given for ``target`` """ with pytest.raises(TypeError, match="target must be an integer"): subgraph.resize(subgraphs=[[0, 1]], graph=graph, target=[])
def test_invalid_max_vs_min(self): """Test if function raises a ``ValueError`` when max_size is less than min_size""" dim = 5 with pytest.raises(ValueError, match="max_size must not be less than min_size"): subgraph.resize([0, 1], nx.complete_graph(dim), 4, 3)
def test_invalid_min_size(self): """Test if function raises a ``ValueError`` when an invalid min_size is requested""" dim = 5 with pytest.raises(ValueError, match="min_size must be at least 1"): subgraph.resize([0, 1], nx.complete_graph(dim), 0, 3)
def test_input_not_subgraph(self): """Test if function raises a ``ValueError`` when input is not a subgraph""" dim = 5 with pytest.raises(ValueError, match="Input is not a valid subgraph"): subgraph.resize([dim + 1], nx.complete_graph(dim), 2, 3)