Repository: mne-tools/mne-python

Issue:
mne.source_space.add_source_space_distances not correctly populating some attributes
### Description of the problem

There is a problem with the values added by mne.source_space.add_source_space_distances.  I've run this on the sample data, using the oct-6 forward model, which contains (for the left hemisphere) 155407 vertices in the dense mesh with 3732 vertices in the subsampled mesh. 

According to the documentation, the attribute 'patch_inds' should represent, "for each vertex in the subsampled surface, the indices of the vertices in the dense surface that it represents." It is supposed to be a list of arrays. However, after running mne.source_space.add_source_space_distances, src[0]['patch_inds'] is not a list of arrays, it is a single array with length 3732. The values in the array don't even correspond to the single nearest vertex in the dense space, because the values in the array only range from 13 - 4097.

Similarly, the documentation states that 'nearest' represents, "for each vertex on the dense surface, the vertex index on the subsampled surface that its closest to." So 'nearest' should be an array with length 155407, with values between 0 and 3731. While it is an array of length 155407, the values range from 59 to 153933. 

### Steps to reproduce

```Python
import mne
from mne.datasets import sample, fetch_fsaverage
import numpy as np
# Paths to example data
sample_dir_raw = sample.data_path()
sample_dir = sample_dir_raw / 'MEG' / 'sample'
subjects_dir = sample_dir_raw / 'subjects'
fname_stc = sample_dir / 'sample_audvis-meg'

# Read the raw data
raw = mne.io.read_raw_fif(raw_fname)
fwd_fname = meg_path / 'sample_audvis-meg-oct-6-fwd.fif'
forward = mne.read_forward_solution(fwd_fname)
mne.source_space.add_source_space_distances(src,dist_limit=0.01)

# the contents of patch_inds - a single array
src[0]['patch_inds']
# if patch_inds gives the corresponding vertex in the original dense mesh, these 
# numbers should go up to the total number of vertices in the dense mesh; 155407. 
# But they max out at 4097
np.max(src[0]['patch_inds'])

# the contents of nearest. This is supposed to be the nearest vertex on the 
# subsurface for every vertex on the dense surface. So these values should only
# go up to the number of vertices on the subsurface - 3732
np.max(src[0]['nearest']
```


### Link to data

_No response_

### Expected results

The attribute 'patch_inds' should be a list of arrays (of length nuse), where each array contains the nearest vertices in the dense mesh - thus the range of values should be from 0 to np. 

The attribute 'nearest' should be an array with shape (np,) with values representing the nearest vertex in the subsampled mesh - thus the values should range from 0 to use

### Actual results

In [37]: np.shape(src[0]['patch_inds'])
Out [37]: (3732,)
In [38]: np.max(src[0]['patch_inds'])
Out [38]: 4097
In [39]: np.max(src[0]['nearest'])
Out [39]: 155330

### Additional information

Platform:         macOS-10.15.7-x86_64-i386-64bit
Python:           3.10.5 | packaged by conda-forge | (main, Jun 14 2022, 07:03:09) [Clang 13.0.1 ]
Executable:       /Users/nugenta/opt/anaconda3/envs/mne2022/bin/python
CPU:              i386: 4 cores
Memory:           16.0 GB

mne:              1.1.0
numpy:            1.22.4 {blas=NO_ATLAS_INFO, lapack=lapack}
scipy:            1.9.0
matplotlib:       3.5.3 {backend=module://matplotlib_inline.backend_inline}

sklearn:          1.1.2
numba:            0.55.2
nibabel:          4.0.1
nilearn:          0.9.1
dipy:             1.5.0
cupy:             Not found
pandas:           1.4.3
pyvista:          0.36.1 {OpenGL 4.1 INTEL-14.7.28 via Intel(R) UHD Graphics 617}
pyvistaqt:        0.9.0
ipyvtklink:       0.2.2
vtk:              


Discussion:

Resolving patch:
diff --git a/mne/source_space.py b/mne/source_space.py
--- a/mne/source_space.py
+++ b/mne/source_space.py
@@ -149,16 +149,59 @@ class SourceSpaces(list):
             surfaces) between source points.
         dist_limit : float
             The maximum distance allowed for inclusion in ``nearest``.
-        pinfo : dict
-            Information about the patch of cortex represented by a vertex in
-            the subsampled surface.
-        patch_inds : list of ndarray
+        pinfo : list of ndarray
             For each vertex in the subsampled surface, the indices of the
             vertices in the dense surface that it represents (i.e., is closest
-            to of all subsampled indices).
+            to of all subsampled indices), e.g. for the left hemisphere
+            (here constructed for ``sample`` with ``spacing='oct-6'``),
+            which vertices did we choose? Note the first is 14::
+
+                >>> src[0]['vertno']  # doctest:+SKIP
+                array([    14,     54,     59, ..., 155295, 155323, 155330])
+
+            And which dense surface verts did our vertno[0] (14 on dense) represent? ::
+
+                >>> src[0]['pinfo'][0]  # doctest:+SKIP
+                array([  6,   7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
+                        19,  20,  21,  22,  23,  24,  25,  29,  30,  31,  39, 134, 135,
+                       136, 137, 138, 139, 141, 142, 143, 144, 149, 150, 151, 152, 156,
+                       162, 163, 173, 174, 185, 448, 449, 450, 451, 452, 453, 454, 455,
+                       456, 462, 463, 464, 473, 474, 475, 485, 496, 497, 512, 864, 876,
+                       881, 889, 890, 904])
+
+        patch_inds : ndarray, shape (n_src_remaining,)
+            The patch indices that have been retained (if relevant, following
+            forward computation. After just :func:`mne.setup_source_space`,
+            this will be ``np.arange(src[0]['nuse'])``. After forward
+            computation, some vertices can be excluded, in which case this
+            tells you which patches (of the original ``np.arange(nuse)``)
+            are still in use. So if some vertices have been excluded, the
+            line above for ``pinfo`` for completeness should be (noting that
+            the first subsampled vertex ([0]) represents the following dense
+            vertices)::
+
+                >>> src[0]['pinfo'][src[0]['patch_inds'][0]]  # doctest:+SKIP
+                array([  6,   7,   8,   9,  10,  11,  12,  13,  14,  15,  16,  17,  18,
+                        19,  20,  21,  22,  23,  24,  25,  29,  30,  31,  39, 134, 135,
+                       136, 137, 138, 139, 141, 142, 143, 144, 149, 150, 151, 152, 156,
+                       162, 163, 173, 174, 185, 448, 449, 450, 451, 452, 453, 454, 455,
+                       456, 462, 463, 464, 473, 474, 475, 485, 496, 497, 512, 864, 876,
+                       881, 889, 890, 904])
+
         nearest : ndarray, shape (np,)
             For each vertex on the dense surface, this gives the vertex index
-            on the subsampled surface that it's closest to.
+            (in the dense surface) that each dense surface vertex is closest to
+            of the vertices chosen for subsampling. This is essentially the
+            reverse map off ``pinfo``, e.g.::
+
+                >>> src[0]['nearest'].shape  # doctest:+SKIP
+                (115407,)
+
+            Based on ``pinfo`` above, this should be 14:
+
+                >>> src[0]['nearest'][6]  # doctest:+SKIP
+                14
+
         nearest_dist : ndarray, shape (np,)
             The distances corresponding to ``nearest``.
 
@@ -194,7 +237,7 @@ class SourceSpaces(list):
 
     Source spaces also have some attributes that are accessible via ``.``
     access, like ``src.kind``.
-    """
+    """  # noqa: E501
 
     def __init__(self, source_spaces, info=None):  # noqa: D102
         # First check the types is actually a valid config


Test patch:
diff --git a/mne/tests/test_source_space.py b/mne/tests/test_source_space.py
--- a/mne/tests/test_source_space.py
+++ b/mne/tests/test_source_space.py
@@ -162,6 +162,39 @@ def test_add_patch_info(monkeypatch):
         _compare_source_spaces(src, src_new, 'approx')
 
 
+# We could test "src_py" here, but we can rely on our existing tests to
+# make sure the pinfo/patch_inds/nearest match
+@testing.requires_testing_data
+@pytest.mark.parametrize('src_kind', ['fwd', 'src'])
+def test_surface_source_space_doc(src_kind):
+    """Test surface source space docstring."""
+    # make sure we're correct about this stuff for both kinds!
+    if src_kind == 'fwd':
+        src = mne.read_source_spaces(fname_fwd)
+    else:
+        assert src_kind == 'src'
+        src = mne.read_source_spaces(fname_src)
+    for s in src:
+        if src_kind == 'src':  # original
+            assert len(s['pinfo']) == s['nuse']
+            assert_array_equal(s['patch_inds'], np.arange(s['nuse']))
+        else:  # pts removed
+            assert len(s['pinfo']) > s['nuse']
+        all_pinfo = np.concatenate(s['pinfo'])
+        assert_array_equal(np.sort(all_pinfo), np.arange(s['np']))
+        assert len(s['patch_inds']) == s['nuse']
+        assert len(s['vertno']) == s['nuse']
+        assert len(s['patch_inds']) == s['nuse']
+        for idx in (0, 42, 173):
+            this_dense_vertex = s['vertno'][idx]
+            # 'pinfo'
+            this_vertex_represents = s['pinfo'][s['patch_inds'][idx]]
+            assert len(this_vertex_represents) > 1
+            # 'nearest'
+            for other in this_vertex_represents:
+                assert s['nearest'][other] == this_dense_vertex
+
+
 @testing.requires_testing_data
 def test_add_source_space_distances_limited(tmp_path):
     """Test adding distances to source space with a dist_limit."""