Repository: networkx/networkx

Issue:
Unable to get the same network from `soft_random_geometric_graph` on different machines
I'm unable, with version 2.5 of networkx, to get the same graph from `soft_random_geometric_graph` (with same parameters of course, and same seed of random number generator)

A cause of the problem is the fact that `scipy.spatial.cKDTree` doesn't return the edges in the same order. A simple pass of "sorted(...)" solves the probelm

### Current Behavior
The generated graph has different edges on different machines, even with the same seed

### Expected Behavior
The generated graph should have the same edges, given the same seed

### Steps to Reproduce
```
import numpy as np
import networkx as nx

rng = np.random.RandomState()
rng.seed(4)
N =100
scale = 2
x_pos = np.sqrt(N)*rng.random(N)
y_pos = np.sqrt(N)*rng.random(N)
# for soft geometric graph generation
pos  = {i: (x, y) for i, (x, y) in enumerate(zip(x_pos,y_pos))}

rng = np.random.RandomState()
rng.seed(2)
G = nx.generators.soft_random_geometric_graph(N,4, pos=pos, seed=rng )
```
This snippet is sufficient to generate graphs with different edges on different machines, provided that scipy is installed

### Environment

Python version: 3.8, 3.7
NetworkX version: 2.5




Discussion:
Thanks very much for this -- I would have had a hard time identifying that there even was a problem here... :}

Two quick questions while it is fresh in your head and before I dive into it.  
1) You say that `sorted()` can fix the problem -- Did you sort the results of the `query_pair()` method, or later on, and would that work if the nodes (and thus the node-pairs) are not sortable? 
2) Do you have suggestions for how I can check that this is working or not? How different do you "two machines" have to be?

[Edit:  What version(s) of scipy are you using on the two machines?]
1. I have fixed the problem with sorting the edges that come from the cKDTree routine, as I have found that was this algorithm which gave different results on different machines.
2. I have found the problem on two different x86_64 machines, with same versions of the packages, but different versions of python (one 3.7, the other one 3.8). 
3. I have two different versions of scipy, one is 1.5.2 (python 3.7) and the other is 1.6.2 (python 3.8). 

Edit: Regardless, sorting the edges makes the generated graphs identical. I can put the code that works, too
Unfortunately sorting the edges isn't always possible because nodes (and thus node-pairs) are not generally sortable -- only hashable.  If you provide a number of nodes to the function all nodes are integers, but if you provide a list of nodes, the nodes are not necessarily sortable.

I also find that scipy 1.5.1 and 1.6.0 give different results. I'll try to track it down from there.  It might be that to get the same results you have to use the same version of scipy.
Sorted can work here...  The return value of sp.spatial.cKDTree(coords) is a **set** of 2-tuple edges.  So order is arbitrary and potentially different in different versions of Python.  But it is not a set of 2-tuples of **nodes**, it is a set of 2-tuples of **node_indexes**.  The index is the index within the list `coords` used as input.

So, there are integers and can be sorted before what is now the next line where they get mapped to nodes.
All we need is to add `sorted` inside the list comprehension where we loop over `edge_indexes` (line 55).

Is this where you had it?  Maybe I should just say:  Your suggestion looks like it will work fine!!  I'd love it if you could make a PR, or indicate if this fix matches what you were suggesting.     Thanks!
Yeah, I wrapped the result of sp.spatial.cKDTree(coords). If you can tell
me on which branch I have to commit the change, I will gladly make a Pull
Request. (Branch master doesn't seem to have the call to cKDTree)


Il Gio 29 Apr 2021, 20:12 Dan Schult ***@***.***> ha scritto:

> Sorted can work here... The return value of sp.spatial.cKDTree(coords) is
> a *set* of 2-tuple edges. So order is arbitrary and potentially different
> in different versions of Python. But it is not a set of 2-tuples of
> *nodes*, it is a set of 2-tuples of *node_indexes*. The index is the
> index within the list coords used as input.
>
> So, there are integers and can be sorted before what is now the next line
> where they get mapped to nodes.
> All we need is to add sorted inside the list comprehension where we loop
> over edge_indexes (line 55).
>
> Is this where you had it? Maybe I should just say: Your suggestion looks
> like it will work fine!! I'd love it if you could make a PR, or indicate if
> this fix matches what you were suggesting. Thanks!
>
> —
> You are receiving this because you authored the thread.
> Reply to this email directly, view it on GitHub
> <https://github.com/networkx/networkx/issues/4767#issuecomment-829479852>,
> or unsubscribe
> <https://github.com/notifications/unsubscribe-auth/ADMONLNKQVDOBUZLZVSJAOTTLGOQ3ANCNFSM43ZUP5TQ>
> .
>
 
Hmmm...   We changed the branch name to `main` so there shouldn't be a `master` branch anymore.
If you make a fork into your set of repositories you can name the branch whatever you want and then
create a pull request from that branch to networkx/main
I can do it pretty easily so if your setup isn't working smoothly just say so and I'll make it and ask you to check it.


Resolving patch:
diff --git a/networkx/generators/geometric.py b/networkx/generators/geometric.py
--- a/networkx/generators/geometric.py
+++ b/networkx/generators/geometric.py
@@ -52,7 +52,7 @@ def geometric_edges(G, radius, p):
     nodes, coords = list(zip(*nodes_pos))
     kdtree = sp.spatial.cKDTree(coords)  # Cannot provide generator.
     edge_indexes = kdtree.query_pairs(radius, p)
-    edges = [(nodes[u], nodes[v]) for u, v in edge_indexes]
+    edges = [(nodes[u], nodes[v]) for u, v in sorted(edge_indexes)]
     return edges
 
 


Test patch: