Repository: networkx/networkx

Issue:
`omega` gives values that are not within [-1,1] bounds
<!-- If you have a general question about NetworkX, please use the discussions tab to create a new discussion -->

<!--- Provide a general summary of the issue in the Title above -->

According to the paper this metric has been based upon, [found here](https://www.ncbi.nlm.nih.gov/pmc/articles/PMC3604768/) , omega needs to be between [-1,1].

However nx.omega gives as a result many values that are not within [-1,1] bounds

### Current Behavior
<!--- Tell us what happens instead of the expected behavior -->

values not being within the expected [-1,1] bounds

### Expected Behavior
<!--- Tell us what should happen -->

values should be within [-1,1] bounds

### Steps to Reproduce
<!--- Provide a minimal example that reproduces the bug -->

```python
import networkx as nx
G = nx.barbell_graph(5,1)
print(nx.omega(G,seed=1)) # result is -1.505815972222222
```
### Environment
<!--- Please provide details about your local environment -->
Python version: 3.7.4
NetworkX version: 2.5.1


### Additional context
<!--- Add any other context about the problem here, screenshots, etc. -->



Discussion:
Thanks for the report - I can reproduce this on the development branch (96831f99). I also tried the barbell graph example you've provided with different seeds and noticed that the result was always < -1.
I've only had a very quick look at the paper you linked to @mamonu, but one question that springs to mind is whether the [-1,1] interval is just something the authors observed for graphs they looked at rather than something that is true for all graphs. Certainly the wording "Moreover, values of [omega] are restricted to the interval -1 to 1 regardless of network size." makes it sound like a general thing, but the definition of omega makes me wonder. Is there a proof in the paper that I've missed?
You could also see if you get close to some of the paper's values for the karate graph (`nx.karate()`) from the first row of Table 1.
... I got 0.35 for the karate graph, whereas the paper says 0.08. But it might just be down to random variation.
I've had a little look at `algorithms/smallworld.py`, and noticed the following things that may be worth checking. (Unfortunately I don't have time to check them properly, and will have to stop looking any further at this issue.)

- C is calculated using transitivity in NetworkX but using the clustering coefficient in the paper. These can be very different (http://pages.stat.wisc.edu/~karlrohe/netsci/MeasuringTrianglesInGraphs.pdf)
- The `niter` and `nrand` parameters perhaps don't match the paper? I haven't checked this properly.
- I also noticed that the NetworkX implementation of *sigma* uses the `random_reference` function. It might be worth checking whether this unusual random graph generator is the standard way to calculate sigma.
thanks for looking both. 

btw @jtrim-ons I used to work at ONS some years ago :)

its a `smallworld` indeed. (ba-ddum-tch) 


I would like to look into this issue including the comments from the discussion. From the contributor guide I could not identify if issues get assigned, so I would just start? If I am missing something, please let me know.
Yes, please go ahead and start on this issue. It is much appreciated! :}


Resolving patch:
diff --git a/networkx/algorithms/smallworld.py b/networkx/algorithms/smallworld.py
--- a/networkx/algorithms/smallworld.py
+++ b/networkx/algorithms/smallworld.py
@@ -114,7 +114,7 @@ def random_reference(G, niter=1, connectivity=True, seed=None):
 @py_random_state(4)
 @not_implemented_for("directed")
 @not_implemented_for("multigraph")
-def lattice_reference(G, niter=1, D=None, connectivity=True, seed=None):
+def lattice_reference(G, niter=5, D=None, connectivity=True, seed=None):
     """Latticize the given graph by swapping edges.
 
     Parameters
@@ -181,12 +181,12 @@ def lattice_reference(G, niter=1, D=None, connectivity=True, seed=None):
             D[v, :] = D[nnodes - v - 1, :][::-1]
 
     niter = niter * nedges
-    ntries = int(nnodes * nedges / (nnodes * (nnodes - 1) / 2))
-    swapcount = 0
+    # maximal number of rewiring attempts per 'niter'
+    max_attempts = int(nnodes * nedges / (nnodes * (nnodes - 1) / 2))
 
-    for i in range(niter):
+    for _ in range(niter):
         n = 0
-        while n < ntries:
+        while n < max_attempts:
             # pick two random edges without creating edge list
             # choose source node indices from discrete distribution
             (ai, ci) = discrete_sequence(2, cdistribution=cdf, seed=seed)
@@ -220,7 +220,6 @@ def lattice_reference(G, niter=1, D=None, connectivity=True, seed=None):
                         G.add_edge(a, b)
                         G.add_edge(c, d)
                     else:
-                        swapcount += 1
                         break
             n += 1
 
@@ -298,7 +297,7 @@ def sigma(G, niter=100, nrand=10, seed=None):
 @py_random_state(3)
 @not_implemented_for("directed")
 @not_implemented_for("multigraph")
-def omega(G, niter=100, nrand=10, seed=None):
+def omega(G, niter=5, nrand=10, seed=None):
     """Returns the small-world coefficient (omega) of a graph
 
     The small-world coefficient of a graph G is:
@@ -310,22 +309,22 @@ def omega(G, niter=100, nrand=10, seed=None):
     of an equivalent random graph and Cl is the average clustering coefficient
     of an equivalent lattice graph.
 
-    The small-world coefficient (omega) ranges between -1 and 1. Values close
-    to 0 means the G features small-world characteristics. Values close to -1
-    means G has a lattice shape whereas values close to 1 means G is a random
-    graph.
+    The small-world coefficient (omega) measures how much G is like a lattice
+    or a random graph. Negative values mean G is similar to a lattice whereas
+    positive values mean G is a random graph.
+    Values close to 0 mean that G has small-world characteristics.
 
     Parameters
     ----------
     G : NetworkX graph
         An undirected graph.
 
-    niter: integer (optional, default=100)
+    niter: integer (optional, default=5)
         Approximate number of rewiring per edge to compute the equivalent
         random graph.
 
     nrand: integer (optional, default=10)
-        Number of random graphs generated to compute the average clustering
+        Number of random graphs generated to compute the maximal clustering
         coefficient (Cr) and average shortest path length (Lr).
 
     seed : integer, random_state, or None (default)
@@ -354,15 +353,31 @@ def omega(G, niter=100, nrand=10, seed=None):
     # Compute the mean clustering coefficient and average shortest path length
     # for an equivalent random graph
     randMetrics = {"C": [], "L": []}
-    for i in range(nrand):
-        Gr = random_reference(G, niter=niter, seed=seed)
-        Gl = lattice_reference(G, niter=niter, seed=seed)
-        randMetrics["C"].append(nx.transitivity(Gl))
+
+    # Calculate initial average clustering coefficient which potentially will
+    # get replaced by higher clustering coefficients from generated lattice
+    # reference graphs
+    Cl = nx.average_clustering(G)
+
+    niter_lattice_reference = niter
+    niter_random_reference = niter * 2
+
+    for _ in range(nrand):
+        # Generate random graph
+        Gr = random_reference(G, niter=niter_random_reference, seed=seed)
         randMetrics["L"].append(nx.average_shortest_path_length(Gr))
 
-    C = nx.transitivity(G)
+        # Generate lattice graph
+        Gl = lattice_reference(G, niter=niter_lattice_reference, seed=seed)
+
+        # Replace old clustering coefficient, if clustering is higher in
+        # generated lattice reference
+        Cl_temp = nx.average_clustering(Gl)
+        if Cl_temp > Cl:
+            Cl = Cl_temp
+
+    C = nx.average_clustering(G)
     L = nx.average_shortest_path_length(G)
-    Cl = np.mean(randMetrics["C"])
     Lr = np.mean(randMetrics["L"])
 
     omega = (Lr / L) - (C / Cl)


Test patch:
diff --git a/networkx/algorithms/tests/test_smallworld.py b/networkx/algorithms/tests/test_smallworld.py
--- a/networkx/algorithms/tests/test_smallworld.py
+++ b/networkx/algorithms/tests/test_smallworld.py
@@ -55,6 +55,16 @@ def test_omega():
     omegal = omega(Gl, niter=1, nrand=1, seed=rng)
     omegar = omega(Gr, niter=1, nrand=1, seed=rng)
     omegas = omega(Gs, niter=1, nrand=1, seed=rng)
-    print("omegas, omegal, omegar")
-    print(omegas, omegal, omegar)
     assert omegal < omegas and omegas < omegar
+
+    # Test that omega lies within the [-1, 1] bounds
+    G_barbell = nx.barbell_graph(5, 1)
+    G_karate = nx.karate_club_graph()
+
+    omega_barbell = nx.omega(G_barbell)
+    omega_karate = nx.omega(G_karate, nrand=2)
+
+    omegas = (omegal, omegar, omegas, omega_barbell, omega_karate)
+
+    for o in omegas:
+        assert -1 <= o <= 1