Repository: networkx/networkx

Issue:
directed_laplacian_matrix sometimes returns nans
<!-- 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 -->

The function `directed_laplacian_matrix` returns a matrix with `nan` entries on specific input graphs. The entries of the directed Laplacian matrix are always well-defined, so `nan`s should not appear. This behavior does not produce an error, but the code of the function produces a `RuntimeWarning`, and seems to come from the attempt to take the square root (with `numpy`) of an array with negative entries (produced by `scipy`).  

### Current Behavior

<!--- Tell us what happens instead of the expected behavior -->

The function `directed_laplacian_matrix` in line `257` calls `scipy.sparse.linalg.eigs` to get the unique eigenvector whose eigenvalue is 1. This eigenvector should have all positive or all negative values,  but it seems that `scipy` sometimes returns some positive and some negative entries. The existence of a unique left eigenvector with all positive entries (corresponding to the eigenvalue 1) is guaranteed by the Perron--Frobenius theorem. The correct eigenvector is selected by `sparse.linalg.eigs` from `scipy`, and even if all the entries are negative (still a legitimate eigenvector), normalizing on line `259` should ensure all entries are positive. However, if the signs of entries are both positve and negative, then that will stay the case after this line.  When taking the square root later in line `260` with `numpy.sqrt`, a `RuntimeWarning` occurs, because the input is not necessarily positive. The warning is not produced when executing the function `directed_laplacian_matrix` directly, only when `np.sqrt(p)` is called on line `260` by itself. 

### Expected Behavior

<!--- Tell us what should happen -->

It should be that `directed_laplacian_matrix` returns a matrix that does not have any `nan`s. Or, perhaps it should give an error if the vector `evecs` returned from `scipy` on line `257` does not have all positive or all negative entries. 

### Steps to Reproduce

<!--- Provide a minimal example that reproduces the bug -->

I was trying to compute the directed Laplacian matrix for a directed graph with 408 nodes, accessible in `adjlist` format in [theattached file](https://github.com/networkx/networkx/files/12395264/G.txt) file (and also at a [pastebin link](https://pastebin.com/ET1k7vBq)) . This is the largest strongly connected component of a larger directed graph with 440 nodes (so the node indices go up to 440). The following code demonstrates that sometimes `nan` values are returned. The linked file is saved as `G.txt`.

````
import networkx as nx
G = nx.read_adjlist('G.txt', create_using=nx.DiGraph)
tries = 100
no_nan,has_nan = (0,0)
for _ in range(tries):
    mat = nx.directed_laplacian_matrix(G)
    nans = np.where(np.isnan(np.array(mat)))
    if len(nans[0] == 0):
        no_nan += 1
    else:
        has_nan += 1
print('In '+str(tries)+' attempts, '+str(no_nan)+' attempts with no nan values, '+str(has_nan)+' attempts with nan values', flush=True)

>>> In 100 attempts, 69 attempts with no nan values, 31 attempts with nan values
````

The following code of lines `251` to `260` shows that the `RuntimeWarning` happens on the `np.sqrt` line.

````
import numpy as np
import scipy as sp
import warnings
warnings.filterwarnings("error")
 
tries = 100
success,fail = (0,0)
for _ in range(tries):
    P = nx.linalg.laplacianmatrix._transition_matrix(G)
    evals,evecs = sp.sparse.linalg.eigs(P.T,k=1)
    v = evecs.flatten().real
    p = v/v.sum()
    try:
        sqrtp = np.sqrt(p)
        success += 1
    except:
        fail += 1
print('In '+str(tries)+' attempts, '+str(success)+' successes and '+str(fail)+' failures', flush=True)

>>> In 100 attempts, 27 successes and 73 failures
````

The precise warning is as follows:

````
RuntimeWarning: invalid value encountered in sqrt
  sqrtp = np.sqrt(p)
````

### Environment

<!--- Please provide details about your local environment -->

Python version: 3.8.3
NetworkX version: 3.1
NumPy version: 1.23.1
SciPy version: 1.8.0

### Other comments

My guess is the issue is with rounding in `scipy`, so perhaps it is better to have this discussion in that project. However, it would be nice for `networkx` to provide some indication (error, warning) if this happens - now I only get an error if I try to do something with the returned matrix. There is no check made in `directed_laplacian_matrix` to see if the entries of `p` (the vector passed to the square root function) are positive, perhaps checking there and rasing error would better inform the user if there is a problem.


Discussion:

Resolving patch:
diff --git a/networkx/linalg/laplacianmatrix.py b/networkx/linalg/laplacianmatrix.py
--- a/networkx/linalg/laplacianmatrix.py
+++ b/networkx/linalg/laplacianmatrix.py
@@ -257,7 +257,8 @@ def directed_laplacian_matrix(
     evals, evecs = sp.sparse.linalg.eigs(P.T, k=1)
     v = evecs.flatten().real
     p = v / v.sum()
-    sqrtp = np.sqrt(p)
+    # p>=0 by Perron-Frobenius Thm. Use abs() to fix roundoff across zero gh-6865
+    sqrtp = np.sqrt(np.abs(p))
     Q = (
         # TODO: rm csr_array wrapper when spdiags creates arrays
         sp.sparse.csr_array(sp.sparse.spdiags(sqrtp, 0, n, n))


Test patch: