Repository: networkx/networkx

Issue:
Improve testing for directed_edge_swap
There is an opportunity to improve the testing of the `directed_swap_function`.

There is a concrete suggestions [here](https://github.com/networkx/networkx/pull/5663#discussion_r906225024) for improving `test_directed_edge_swap` by validating that the input and output edges are indeed different. Upon reflection, I think this check could even be improved further by verifying that the number of swaps is correct... I think the difference in the edge sets should be 2*`nswap` - that might be one way to check.

It'd also be good to add more test cases of non-path graphs where there are only a limited number of edge swaps possible, and verify that the algorithm can recover all of them. See [the discussion](https://github.com/networkx/networkx/pull/5663#pullrequestreview-1018715617) for further details.


Discussion:
Hello
I would like to take up this issue. I will open a pull request soon.

I have made the function `test_directed_edge_swap()`  assert that the edges of the original DiGraph and the one after swapping edges are not all the same, in addition to checking the in and out degree of the graph.

Also, in order to remove the limitation of test on path graph only, I have made changes which would generate a random directed graph (which would not be a path graph) and run the tests on it.
Testing a random graph isn't all that helpful. It would be better to test a graph with a specific configuration that you are concerned will be handled correctly by the function. Random graphs give a different result every time you run them, so it is very hard to track down what the cause of the error is. You can specify a "seed" to make it the same result every time, but that is no longer random. It is just some arbitrary graph that you usually don't know much about. 

So, if you want to expand beyond a `path_graph`, I would suggest choosing a specific graph that has a different topology and working with that.  One option would be a complete graph with only one directed edge for each edge pair.
```python
G = nx.DiGraph(nx.complete_graph(N).edges)
``` 
You should think about better graphs to test edge swaps. I just made this one up as something that looks very different from a path -- not because of some feature about edge swaps. 

Thanks for taking this on! :}

Resolving patch:
diff --git a/networkx/algorithms/swap.py b/networkx/algorithms/swap.py
--- a/networkx/algorithms/swap.py
+++ b/networkx/algorithms/swap.py
@@ -56,6 +56,8 @@ def directed_edge_swap(G, *, nswap=1, max_tries=100, seed=None):
 
     The graph G is modified in place.
 
+    A later swap is allowed to undo a previous swap.
+
     References
     ----------
     .. [1] Erdős, Péter L., et al. “A Simple Havel-Hakimi Type Algorithm to Realize


Test patch:
diff --git a/networkx/algorithms/tests/test_swap.py b/networkx/algorithms/tests/test_swap.py
--- a/networkx/algorithms/tests/test_swap.py
+++ b/networkx/algorithms/tests/test_swap.py
@@ -2,14 +2,36 @@
 
 import networkx as nx
 
-
-def test_directed_edge_swap():
-    graph = nx.path_graph(200, create_using=nx.DiGraph)
-    in_degrees = sorted((n, d) for n, d in graph.in_degree())
-    out_degrees = sorted((n, d) for n, d in graph.out_degree())
-    G = nx.directed_edge_swap(graph, nswap=40, max_tries=500, seed=1)
-    assert in_degrees == sorted((n, d) for n, d in G.in_degree())
-    assert out_degrees == sorted((n, d) for n, d in G.out_degree())
+cycle = nx.cycle_graph(5, create_using=nx.DiGraph)
+tree = nx.random_tree(10, create_using=nx.DiGraph)
+path = nx.path_graph(5, create_using=nx.DiGraph)
+binomial = nx.binomial_tree(3, create_using=nx.DiGraph)
+HH = nx.directed_havel_hakimi_graph([1, 2, 1, 2, 2, 2], [3, 1, 0, 1, 2, 3])
+balanced_tree = nx.balanced_tree(2, 3, create_using=nx.DiGraph)
+
+
+@pytest.mark.parametrize("G", [path, binomial, HH, cycle, tree, balanced_tree])
+def test_directed_edge_swap(G):
+    in_degree = set(G.in_degree)
+    out_degree = set(G.out_degree)
+    edges = set(G.edges)
+    nx.directed_edge_swap(G, nswap=1, max_tries=100, seed=1)
+    assert in_degree == set(G.in_degree)
+    assert out_degree == set(G.out_degree)
+    assert edges != set(G.edges)
+    assert 3 == sum(e not in edges for e in G.edges)
+
+
+def test_directed_edge_swap_undo_previous_swap():
+    G = nx.DiGraph(nx.path_graph(4).edges)  # only 1 swap possible
+    edges = set(G.edges)
+    nx.directed_edge_swap(G, nswap=2, max_tries=100)
+    assert edges == set(G.edges)
+
+    nx.directed_edge_swap(G, nswap=1, max_tries=100, seed=1)
+    assert {(0, 2), (1, 3), (2, 1)} == set(G.edges)
+    nx.directed_edge_swap(G, nswap=1, max_tries=100, seed=1)
+    assert edges == set(G.edges)
 
 
 def test_edge_cases_directed_edge_swap():