Repository: networkx/networkx

Issue:
Documentation and code in `floyd_warshall_numpy` are inconsistent
### Current Behavior
<!--- Tell us what happens instead of the expected behavior -->

Using `floyd_warshall_numpy` with a specified set of nodes will only find paths that are confined to that subset of nodes.  I'm not sure I agree with that choice, and certainly the documentation does not make it clear.

### Expected Behavior
<!--- Tell us what should happen -->

Based on the documentation, I would expect it to find a path that starts at one node and ends at another, even if that path must go through additional nodes not in the provided list.

### Steps to Reproduce
<!--- Provide a minimal example that reproduces the bug -->

https://stackoverflow.com/q/65771537/2966723

### Environment
<!--- Please provide details about your local environment -->
Python version:  3.9
NetworkX version:  2.5


### Additional context
<!--- Add any other context about the problem here, screenshots, etc. -->



Discussion:
Can you explain why you thought the function would allow paths outside of the listed nodes? 
The current documentation says `The rows and columns are ordered by the nodes in nodelist.`

Looking further, the `nodelist` is passed to `to_numpy_array` which says in the doc_string:
```
When `nodelist` does not contain every node in `G`, the adjacency matrix is
built from the subgraph of `G` that is induced by the nodes in `nodelist`.
```
Perhaps we should copy that documentation to `floyd_warshall_numpy`.

But it's supposed to return:

>**distance** – A matrix of shortest path distances between nodes. If there is no path between to nodes the corresponding matrix entry will be Inf.     

**[note typo to->two]**

The fact that the documentation says the results showing distances between two nodes are ordered based on the nodelist only suggests to me that the order depends on the nodelist. 
 Not that the numerical value of the distances found will vary based on the nodelist, 

So in principle if I just want the distance between 2 nodes in the Graph, I could call it with the nodelist `[node1, node2]` and I should get the shortest path distance between the nodes.  There's nothing that suggests to me that we're only interested in the subgraph induced by the nodelist.  So I would expect `Inf` to only happen if there is no path in the Graph.  But in this case, I'll get `Inf` if they are not adjacent.
Unfortunately (or fortunately) this interpretation would preclude taking advantage of the dense matrix data structure that this function takes advantage of. Or, perhaps it could use the full matrix and then manipulate it after-the-fact to remove all computed path lengths except those between nodes in nodelist. I don't think that is a good design for this function.

But the current implementation makes it hard to track errors where users provide a subset of the nodes. The SO original post looks like such a difficulty. Our current implementation hides the fact that an induced subgraph has been created. That induced subgraph could easily be constructed by the user before passing into this function. I see little to be gained by providing an automated subgraph feature and it makes providing a nodelist error prone. 

I think the best solution is to raise an error if `nodelist` does not specify all nodes in G and no others.  One possible test of such could be:

```python
if nodelist is None:
    nodelist = list(G)
elif len(nodelist) == len(G) == len(set(nodelist)):
    pass
else:
    raise nx.NetworkXError("nodelist should provide an ordering of all nodes in G")
```

Does this make sense? Is it s good idea?
This would work.

An alternative would be to do the entire network, and then subsample the matrix based on the input nodelist.  However, this would change the output of the algorithm for the same inputs.  

I think it would be better for the algorithm to break than to change what it returns.  So I think I agree with your suggestion.

The users can subsample the rows and columns themselves...
agreed

Resolving patch:
diff --git a/networkx/algorithms/shortest_paths/dense.py b/networkx/algorithms/shortest_paths/dense.py
--- a/networkx/algorithms/shortest_paths/dense.py
+++ b/networkx/algorithms/shortest_paths/dense.py
@@ -13,37 +13,57 @@
 def floyd_warshall_numpy(G, nodelist=None, weight="weight"):
     """Find all-pairs shortest path lengths using Floyd's algorithm.
 
+    This algorithm for finding shortest paths takes advantage of
+    matrix representations of a graph and works well for dense
+    graphs where all-pairs shortest path lengths are desired.
+    The results are returned as a NumPy array, distance[i, j],
+    where i and j are the indexes of two nodes in nodelist.
+    The entry distance[i, j] is the distance along a shortest
+    path from i to j. If no path exists the distance is Inf.
+
     Parameters
     ----------
     G : NetworkX graph
 
-    nodelist : list, optional
+    nodelist : list, optional (default=G.nodes)
        The rows and columns are ordered by the nodes in nodelist.
-       If nodelist is None then the ordering is produced by G.nodes().
+       If nodelist is None then the ordering is produced by G.nodes.
+       Nodelist should include all nodes in G.
 
-    weight: string, optional (default= 'weight')
+    weight: string, optional (default='weight')
        Edge data key corresponding to the edge weight.
 
     Returns
     -------
     distance : NumPy matrix
         A matrix of shortest path distances between nodes.
-        If there is no path between to nodes the corresponding matrix entry
-        will be Inf.
+        If there is no path between two nodes the value is Inf.
 
     Notes
     -----
     Floyd's algorithm is appropriate for finding shortest paths in
     dense graphs or graphs with negative weights when Dijkstra's
     algorithm fails. This algorithm can still fail if there are negative
-    cycles.  It has running time $O(n^3)$ with running space of $O(n^2)$.
+    cycles. It has running time $O(n^3)$ with running space of $O(n^2)$.
+
+    Raises
+    ------
+    NetworkXError
+        If nodelist is not a list of the nodes in G.
     """
     import numpy as np
 
+    if nodelist is not None:
+        if not (len(nodelist) == len(G) == len(set(nodelist))):
+            raise nx.NetworkXError(
+                "nodelist must contain every node in G with no repeats."
+                "If you wanted a subgraph of G use G.subgraph(nodelist)"
+            )
+
     # To handle cases when an edge has weight=0, we must make sure that
     # nonedges are not given the value 0 as well.
     A = nx.to_numpy_array(
-        G, nodelist=nodelist, multigraph_weight=min, weight=weight, nonedge=np.inf
+        G, nodelist, multigraph_weight=min, weight=weight, nonedge=np.inf
     )
     n, m = A.shape
     np.fill_diagonal(A, 0)  # diagonal elements should be zero


Test patch:
diff --git a/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py b/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py
--- a/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py
+++ b/networkx/algorithms/shortest_paths/tests/test_dense_numpy.py
@@ -6,70 +6,84 @@
 import networkx as nx
 
 
-class TestFloydNumpy:
-    def test_cycle_numpy(self):
-        dist = nx.floyd_warshall_numpy(nx.cycle_graph(7))
-        assert dist[0, 3] == 3
-        assert dist[0, 4] == 3
-
-    def test_weighted_numpy_three_edges(self):
-        XG3 = nx.Graph()
-        XG3.add_weighted_edges_from(
-            [[0, 1, 2], [1, 2, 12], [2, 3, 1], [3, 4, 5], [4, 5, 1], [5, 0, 10]]
-        )
-        dist = nx.floyd_warshall_numpy(XG3)
-        assert dist[0, 3] == 15
-
-    def test_weighted_numpy_two_edges(self):
-        XG4 = nx.Graph()
-        XG4.add_weighted_edges_from(
-            [
-                [0, 1, 2],
-                [1, 2, 2],
-                [2, 3, 1],
-                [3, 4, 1],
-                [4, 5, 1],
-                [5, 6, 1],
-                [6, 7, 1],
-                [7, 0, 1],
-            ]
-        )
-        dist = nx.floyd_warshall_numpy(XG4)
-        assert dist[0, 2] == 4
-
-    def test_weight_parameter_numpy(self):
-        XG4 = nx.Graph()
-        XG4.add_edges_from(
-            [
-                (0, 1, {"heavy": 2}),
-                (1, 2, {"heavy": 2}),
-                (2, 3, {"heavy": 1}),
-                (3, 4, {"heavy": 1}),
-                (4, 5, {"heavy": 1}),
-                (5, 6, {"heavy": 1}),
-                (6, 7, {"heavy": 1}),
-                (7, 0, {"heavy": 1}),
-            ]
-        )
-        dist = nx.floyd_warshall_numpy(XG4, weight="heavy")
-        assert dist[0, 2] == 4
-
-    def test_directed_cycle_numpy(self):
-        G = nx.DiGraph()
-        nx.add_cycle(G, [0, 1, 2, 3])
-        pred, dist = nx.floyd_warshall_predecessor_and_distance(G)
-        D = nx.utils.dict_to_numpy_array(dist)
-        np.testing.assert_equal(nx.floyd_warshall_numpy(G), D)
-
-    def test_zero_weight(self):
-        G = nx.DiGraph()
-        edges = [(1, 2, -2), (2, 3, -4), (1, 5, 1), (5, 4, 0), (4, 3, -5), (2, 5, -7)]
-        G.add_weighted_edges_from(edges)
-        dist = nx.floyd_warshall_numpy(G)
-        assert int(np.min(dist)) == -14
-
-        G = nx.MultiDiGraph()
-        edges.append((2, 5, -7))
-        G.add_weighted_edges_from(edges)
-        dist = nx.floyd_warshall_numpy(G)
-        assert int(np.min(dist)) == -14
+def test_cycle_numpy():
+    dist = nx.floyd_warshall_numpy(nx.cycle_graph(7))
+    assert dist[0, 3] == 3
+    assert dist[0, 4] == 3
+
+
+def test_weighted_numpy_three_edges():
+    XG3 = nx.Graph()
+    XG3.add_weighted_edges_from(
+        [[0, 1, 2], [1, 2, 12], [2, 3, 1], [3, 4, 5], [4, 5, 1], [5, 0, 10]]
+    )
+    dist = nx.floyd_warshall_numpy(XG3)
+    assert dist[0, 3] == 15
+
+
+def test_weighted_numpy_two_edges():
+    XG4 = nx.Graph()
+    XG4.add_weighted_edges_from(
+        [
+            [0, 1, 2],
+            [1, 2, 2],
+            [2, 3, 1],
+            [3, 4, 1],
+            [4, 5, 1],
+            [5, 6, 1],
+            [6, 7, 1],
+            [7, 0, 1],
+        ]
+    )
+    dist = nx.floyd_warshall_numpy(XG4)
+    assert dist[0, 2] == 4
+
+
+def test_weight_parameter_numpy():
+    XG4 = nx.Graph()
+    XG4.add_edges_from(
+        [
+            (0, 1, {"heavy": 2}),
+            (1, 2, {"heavy": 2}),
+            (2, 3, {"heavy": 1}),
+            (3, 4, {"heavy": 1}),
+            (4, 5, {"heavy": 1}),
+            (5, 6, {"heavy": 1}),
+            (6, 7, {"heavy": 1}),
+            (7, 0, {"heavy": 1}),
+        ]
+    )
+    dist = nx.floyd_warshall_numpy(XG4, weight="heavy")
+    assert dist[0, 2] == 4
+
+
+def test_directed_cycle_numpy():
+    G = nx.DiGraph()
+    nx.add_cycle(G, [0, 1, 2, 3])
+    pred, dist = nx.floyd_warshall_predecessor_and_distance(G)
+    D = nx.utils.dict_to_numpy_array(dist)
+    np.testing.assert_equal(nx.floyd_warshall_numpy(G), D)
+
+
+def test_zero_weight():
+    G = nx.DiGraph()
+    edges = [(1, 2, -2), (2, 3, -4), (1, 5, 1), (5, 4, 0), (4, 3, -5), (2, 5, -7)]
+    G.add_weighted_edges_from(edges)
+    dist = nx.floyd_warshall_numpy(G)
+    assert int(np.min(dist)) == -14
+
+    G = nx.MultiDiGraph()
+    edges.append((2, 5, -7))
+    G.add_weighted_edges_from(edges)
+    dist = nx.floyd_warshall_numpy(G)
+    assert int(np.min(dist)) == -14
+
+
+def test_nodelist():
+    G = nx.path_graph(7)
+    dist = nx.floyd_warshall_numpy(G, nodelist=[3, 5, 4, 6, 2, 1, 0])
+    assert dist[0, 3] == 3
+    assert dist[0, 1] == 2
+    assert dist[6, 2] == 4
+    pytest.raises(nx.NetworkXError, nx.floyd_warshall_numpy, G, [1, 3])
+    pytest.raises(nx.NetworkXError, nx.floyd_warshall_numpy, G, list(range(9)))