Repository: nilearn/nilearn

Issue:
seeding in `_utils.data_gen`
- `utils.data_gen.generate_fake_fmri` creates a `np.random.RandomState` (mutable
  default argument) in its definition. This might actually have been intentional
  so that it returns a different output each time it is called, but it is not
  the behavior of other functions in this module, which use 0 as a fixed seed by
  default, and it means that the data seen eg by unit tests depends on the order
  in which tests are executed, which may cause spurious failures. instead, this
  function should initialize a new random generator by default, and callers can
  call it with different seeds or pass an existing generator.

- most other functions in the same module, eg `generate_timeseries`, have a
  `rand_gen` parameter that has a different behavior than the `random_state`
  used in many nilearn and scikit-learn functions. Indeed, with `rand_gen` (ie
  in `data_gen`):
     - `None` -> `RandomState(0)`
     - anything else must be a random number generator
  whereas with scikit-learn's `check_random_state`:
     - `None` -> `RandomState(None)` ie get a seed from `/dev/urandom` or the clock
     - an int -> use that as a seed
     - a random number generator -> use that as-is
  this is not a big issue but as `_utils.data_gen` is a private module making
  the api more similar to `check_random_state` is doable.
[WIP] fix for #2631 by using check_random_state to initialize rand_gen
This PR is for #2631 
I have made the following changes
1. updated `generate_fake_fmri` function's `rand_gen`  parameter to default to `None`
2. checked if `rand_gen` is None, if `None`, used `check_random_state` to get a random generator


Discussion:

Resolving patch:
diff --git a/nilearn/_utils/data_gen.py b/nilearn/_utils/data_gen.py
--- a/nilearn/_utils/data_gen.py
+++ b/nilearn/_utils/data_gen.py
@@ -21,72 +21,127 @@
 
 
 def generate_mni_space_img(n_scans=1, res=30, random_state=0, mask_dilation=2):
-    rng = check_random_state(random_state)
+    """Generate mni space img.
+
+    Parameters
+    ----------
+    n_scans : :obj:`int`, optional
+        Number of scans.
+        Default=1.
+
+    res : :obj:`int`, optional
+        Desired resolution, in mm, of output images.
+        Default=30.
+
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    mask_dilation : :obj:`int`, optional
+        The number of times the binary :term:`dilation<Dilation>` is repeated
+        on the mask.
+        Default=2.
+
+    Returns
+    -------
+    inverse_img : Niimg-like object
+        Image transformed back to mni space.
+
+    mask_img : Niimg-like object
+        Generated mask in mni space.
+
+    """
+    rand_gen = check_random_state(random_state)
     mni = datasets.load_mni152_brain_mask()
     target_affine = np.eye(3) * res
     mask_img = image.resample_img(
         mni, target_affine=target_affine, interpolation="nearest")
     masker = maskers.NiftiMasker(mask_img).fit()
     n_voxels = image.get_data(mask_img).sum()
-    data = rng.randn(n_scans, n_voxels)
+    data = rand_gen.randn(n_scans, n_voxels)
     if mask_dilation is not None and mask_dilation > 0:
         mask_img = image.new_img_like(
-            mask_img, binary_dilation(
-                image.get_data(mask_img), iterations=mask_dilation))
-    return masker.inverse_transform(data), mask_img
+            mask_img,
+            binary_dilation(image.get_data(mask_img),
+                            iterations=mask_dilation))
+    inverse_img = masker.inverse_transform(data)
+    return inverse_img, mask_img
+
 
+def generate_timeseries(n_timepoints, n_features, random_state=0):
+    """Generate some random timeseries.
 
-def generate_timeseries(n_instants, n_features,
-                        rand_gen=None):
-    """Generate some random timeseries. """
-    if rand_gen is None:
-        rand_gen = np.random.RandomState(0)
-    # TODO: add an "order" keyword
-    return rand_gen.randn(n_instants, n_features)
+    Parameters
+    ----------
+    n_timepoints : :obj:`int`
+        Number of timepoints
 
+    n_features : :obj:`int`
+        Number of features
 
-def generate_regions_ts(n_features, n_regions,
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    Returns
+    -------
+    :obj:`numpy.ndarray` of shape (n_timepoints, n_features)
+        Generated time series.
+
+    """
+    rand_gen = check_random_state(random_state)
+    return rand_gen.randn(n_timepoints, n_features)
+
+
+def generate_regions_ts(n_features,
+                        n_regions,
                         overlap=0,
-                        rand_gen=None,
+                        random_state=0,
                         window="boxcar"):
     """Generate some regions as timeseries.
 
     Parameters
     ----------
-    n_features : int
+    n_features : :obj:`int`
         Number of features.
 
-    n_regions : int
+    n_regions : :obj:`int`
         Number of regions.
 
-    overlap : int, optional
+    overlap : :obj:`int`, optional
         Number of overlapping voxels between two regions (more or less).
         Default=0.
 
-    window : str, optional
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    window : :obj:`str`, optional
         Name of a window in scipy.signal. e.g. "hamming".
         Default='boxcar'.
 
     Returns
     -------
-    regions : numpy.ndarray
-        Regions, nepresented as signals.
+    regions : :obj:`numpy.ndarray`
+        Regions, represented as signals.
         shape (n_features, n_regions)
 
     """
-    if rand_gen is None:
-        rand_gen = np.random.RandomState(0)
+    rand_gen = check_random_state(random_state)
     if window is None:
         window = "boxcar"
 
-    assert(n_features > n_regions)
+    assert n_features > n_regions
 
     # Compute region boundaries indices.
     # Start at 1 to avoid getting an empty region
     boundaries = np.zeros(n_regions + 1)
     boundaries[-1] = n_features
-    boundaries[1:-1] = rand_gen.permutation(np.arange(1, n_features)
-                                            )[:n_regions - 1]
+    boundaries[1:-1] = rand_gen.permutation(np.arange(
+        1, n_features))[:n_regions - 1]
     boundaries.sort()
 
     regions = np.zeros((n_regions, n_features), order="C")
@@ -102,78 +157,98 @@ def generate_regions_ts(n_features, n_regions,
     return regions
 
 
-def generate_maps(shape, n_regions, overlap=0, border=1,
-                  window="boxcar", rand_gen=None, affine=np.eye(4)):
+def generate_maps(shape,
+                  n_regions,
+                  overlap=0,
+                  border=1,
+                  window="boxcar",
+                  random_state=0,
+                  affine=np.eye(4)):
     """Generate a 4D volume containing several maps.
 
     Parameters
     ----------
-    n_regions : int
+    n_regions : :obj:`int`
         Number of regions to generate.
 
-    overlap : int, optional
+    overlap : :obj:`int`, optional
         Approximate number of voxels common to two neighboring regions.
         Default=0.
 
-    window : str, optional
+    window : :obj:`str`, optional
         Name of a window in scipy.signal. Used to get non-uniform regions.
         Default='boxcar'.
 
-    ran_gen : None or numpy.random.RandomState, optional
-        Random generator to use for generation. Default=None.
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
 
-    affine : Numpy array, optional
-        Affine transformation to use. Default=np.eye(4).
+    affine : :obj:`numpy.ndarray`, optional
+        Affine transformation to use.
+        Default=np.eye(4).
 
-    border : int, optional
+    border : :obj:`int`, optional
         Number of background voxels on each side of the 3D volumes.
         Default=1.
 
     Returns
     -------
-    maps : nibabel.Nifti1Image
-        4D array, containing maps.
+    maps : Niimg-like object
+        4D image object containing the maps.
 
-    mask_img : nibabel.Nifti1Image
-        3D, The mask outside of which maps are 0.
+    mask_img : Niimg-like object
+        3D mask giving non-zero voxels.
 
     """
     mask = np.zeros(shape, dtype=np.int8)
     mask[border:-border, border:-border, border:-border] = 1
-    ts = generate_regions_ts(mask.sum(), n_regions, overlap=overlap,
-                             rand_gen=rand_gen, window=window)
+    ts = generate_regions_ts(mask.sum(),
+                             n_regions,
+                             overlap=overlap,
+                             random_state=random_state,
+                             window=window)
     mask_img = Nifti1Image(mask, affine)
     return masking.unmask(ts, mask_img), mask_img
 
 
-def generate_labeled_regions(shape, n_regions, rand_gen=None, labels=None,
-                             affine=np.eye(4), dtype="int32"):
+def generate_labeled_regions(shape,
+                             n_regions,
+                             random_state=0,
+                             labels=None,
+                             affine=np.eye(4),
+                             dtype="int32"):
     """Generate a 3D volume with labeled regions.
 
     Parameters
     ----------
-    shape : tuple
+    shape : :obj:`tuple`
         Shape of returned array.
 
-    n_regions : int
+    n_regions : :obj:`int`
         Number of regions to generate. By default (if "labels" is None),
         add a background with value zero.
+        Default=None.
+
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
 
     labels : iterable, optional
         Labels to use for each zone. If provided, n_regions is unused.
 
-    rand_gen : numpy.random.RandomState
-        Random generator to use for generation.
+    affine : :obj:`numpy.ndarray`, optional
+        Affine of returned image.
+        Default=np.eye(4).
 
-    affine : numpy.ndarray, optional
-        Affine of returned image. Default=np.eye(4).
-
-    dtype : type, optional
-        Data type of image. Default=np.int32.
+    dtype : :obj:`type`, optional
+        Data type of image.
+        Default='int32'.
 
     Returns
     -------
-    regions : nibabel.Nifti1Image
+    Niimg-like object
         Data has shape "shape", containing region labels.
 
     """
@@ -184,7 +259,9 @@ def generate_labeled_regions(shape, n_regions, rand_gen=None, labels=None,
     else:
         n_regions = len(labels)
 
-    regions = generate_regions_ts(n_voxels, n_regions, rand_gen=rand_gen)
+    regions = generate_regions_ts(n_voxels,
+                                  n_regions,
+                                  random_state=random_state)
     # replace weights with labels
     for n, row in zip(labels, regions):
         row[row > 0] = n
@@ -193,28 +270,61 @@ def generate_labeled_regions(shape, n_regions, rand_gen=None, labels=None,
     return Nifti1Image(data, affine)
 
 
-def generate_labeled_regions_large(shape, n_regions, rand_gen=None,
-                                   affine=np.eye(4), dtype="int32"):
+def generate_labeled_regions_large(shape,
+                                   n_regions,
+                                   random_state=0,
+                                   affine=np.eye(4),
+                                   dtype="int32"):
     """Similar to generate_labeled_regions, but suitable for a large number of
     regions.
 
     See generate_labeled_regions for details.
+
+    Parameters
+    ----------
+    shape : :obj:`tuple` of :obj:`int`
+        Shape of returned array.
+
+    n_regions : :obj:`int`
+        Number of regions to generate.
+
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    affine : :obj:`numpy.ndarray`, optional
+        Affine of returned image.
+        Default=np.eye(4).
+
+    dtype : :obj:`type`, optional
+        Data type of image.
+        Default='int32'.
+
+    Returns
+    -------
+    Niimg-like object
+        Data has shape "shape", containing region labels.
+
     """
-    if rand_gen is None:
-        rand_gen = np.random.RandomState(0)
+    rand_gen = check_random_state(random_state)
     data = rand_gen.randint(n_regions + 1, size=shape, dtype=dtype)
     if len(np.unique(data)) != n_regions + 1:
         raise ValueError("Some labels are missing. Maybe shape is too small.")
     return Nifti1Image(data, affine)
 
 
-def generate_fake_fmri(shape=(10, 11, 12), length=17, kind="noise",
-                       affine=np.eye(4), n_blocks=None, block_size=None,
+def generate_fake_fmri(shape=(10, 11, 12),
+                       length=17,
+                       kind="noise",
+                       affine=np.eye(4),
+                       n_blocks=None,
+                       block_size=None,
                        block_type='classification',
-                       rand_gen=np.random.RandomState(0)):
+                       random_state=0):
     """Generate a signal which can be used for testing.
 
-    The return value is a 4D array, representing 3D volumes along time.
+    The return value is a 4D image, representing 3D volumes along time.
     Only the voxels in the center are non-zero, to mimic the presence of
     brain voxels in real signals. Setting n_blocks to an integer generates
     condition blocks, the remaining of the timeseries corresponding
@@ -222,49 +332,55 @@ def generate_fake_fmri(shape=(10, 11, 12), length=17, kind="noise",
 
     Parameters
     ----------
-    shape : tuple, optional
-        Shape of 3D volume. Default=(10, 11, 12).
+    shape : :obj:`tuple`, optional
+        Shape of 3D volume.
+        Default=(10, 11, 12).
 
-    length : int, optional
-        Number of time instants. Default=17.
+    length : :obj:`int`, optional
+        Number of time instants.
+        Default=17.
 
-    kind : string, optional
+    kind : :obj:`str`, optional
         Kind of signal used as timeseries.
         "noise": uniformly sampled values in [0..255]
         "step": 0.5 for the first half then 1.
         Default='noise'.
 
-    affine : numpy.ndarray, optional
-        Affine of returned images. Default=np.eye(4).
+    affine : :obj:`numpy.ndarray`, optional
+        Affine of returned images.
+        Default=np.eye(4).
 
-    n_blocks : int or None, optional
+    n_blocks : :obj:`int` or None, optional
         Number of condition blocks.
+        Default=None.
 
-    block_size : int or None, optional
+    block_size : :obj:`int` or None, optional
         Number of timepoints in a block. Used only if n_blocks is not
         None. Defaults to 3 if n_blocks is not None.
 
-    block_type : str, optional
+    block_type : :obj:`str`, optional
         Defines if the returned target should be used for
         'classification' or 'regression'.
         Default='classification'.
 
-    rand_gen : numpy.random.RandomState, optional
-        Random generator to use for generation.
-        Default=np.random.RandomState(0).
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
 
     Returns
     -------
-    fmri : nibabel.Nifti1Image
+    Niimg-like object
         Fake fmri signal.
         shape: shape + (length,)
 
-    mask : nibabel.Nifti1Image
+    Niimg-like object
         Mask giving non-zero voxels.
 
-    target : numpy.ndarray
-        Classification or regression target. Shape of number of
-        time points (length). Returned only if n_blocks is not None.
+    target : :obj:`numpy.ndarray`
+        Classification or regression target.
+        A 1D array with one element for each time point.
+        Returned only if ``n_blocks`` is not None.
 
     """
     full_shape = shape + (length, )
@@ -273,6 +389,7 @@ def generate_fake_fmri(shape=(10, 11, 12), length=17, kind="noise",
     width = [s // 2 for s in shape]
     shift = [s // 4 for s in shape]
 
+    rand_gen = check_random_state(random_state)
     if kind == "noise":
         signals = rand_gen.randint(256, size=(width + [length]))
     elif kind == "step":
@@ -281,14 +398,11 @@ def generate_fake_fmri(shape=(10, 11, 12), length=17, kind="noise",
     else:
         raise ValueError("Unhandled value for parameter 'kind'")
 
-    fmri[shift[0]:shift[0] + width[0],
-         shift[1]:shift[1] + width[1],
-         shift[2]:shift[2] + width[2],
-         :] = signals
+    fmri[shift[0]:shift[0] + width[0], shift[1]:shift[1] + width[1],
+         shift[2]:shift[2] + width[2], :] = signals
 
     mask = np.zeros(shape)
-    mask[shift[0]:shift[0] + width[0],
-         shift[1]:shift[1] + width[1],
+    mask[shift[0]:shift[0] + width[0], shift[1]:shift[1] + width[1],
          shift[2]:shift[2] + width[2]] = 1
 
     if n_blocks is None:
@@ -301,10 +415,9 @@ def generate_fake_fmri(shape=(10, 11, 12), length=17, kind="noise",
     target = np.zeros(length, dtype=int)
     rest_max_size = (length - (n_blocks * block_size)) // n_blocks
     if rest_max_size < 0:
-        raise ValueError(
-            '%s is too small '
-            'to put %s blocks of size %s' % (
-                length, n_blocks, block_size))
+        raise ValueError('%s is too small '
+                         'to put %s blocks of size %s' %
+                         (length, n_blocks, block_size))
     t_start = 0
     if rest_max_size > 0:
         t_start = rand_gen.randint(0, rest_max_size, 1)[0]
@@ -317,8 +430,7 @@ def generate_fake_fmri(shape=(10, 11, 12), length=17, kind="noise",
             # Select the voxel in the image center and add some signal
             # that increases with each block
             voxel_idx = flat_fmri.shape[0] // 2
-            trials_effect = (
-                rand_gen.random_sample(block_size) + 1) * block
+            trials_effect = (rand_gen.random_sample(block_size) + 1) * block
         t_rest = 0
         if rest_max_size > 0:
             t_rest = rand_gen.randint(0, rest_max_size, 1)[0]
@@ -333,47 +445,155 @@ def generate_fake_fmri(shape=(10, 11, 12), length=17, kind="noise",
             Nifti1Image(mask, affine), target)
 
 
-def generate_fake_fmri_data_and_design(shapes, rk=3, affine=np.eye(4)):
+def generate_fake_fmri_data_and_design(shapes,
+                                       rk=3,
+                                       affine=np.eye(4),
+                                       random_state=0):
+    """Generate random :term:`fMRI` time series and design matrices of given
+    shapes.
+
+    Parameters
+    ----------
+    shapes : :obj:`list` of length-4 :obj:`tuple`s of :obj:`int`
+        Shapes of the fmri data to be generated.
+
+    rk : :obj:`int`, optional
+        Number of columns in the design matrix to be generated.
+        Default=3.
+
+    affine : :obj:`numpy.ndarray`, optional
+        Affine of returned images. Must be a 4x4 array.
+        Default=np.eye(4).
+
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    Returns
+    -------
+    mask : Niimg-like object
+        3D mask giving non-zero voxels.
+
+    fmri_data : :obj:`list`
+        A 4D volume represented by a list of 3D Niimg-like objects.
+
+    design_matrices : :obj:`list`
+        A list of pd.DataFrame
+
+    """
     fmri_data = []
     design_matrices = []
+    rand_gen = check_random_state(random_state)
     for i, shape in enumerate(shapes):
-        data = np.random.randn(*shape)
+        data = rand_gen.randn(*shape)
         data[1:-1, 1:-1, 1:-1] += 100
         fmri_data.append(Nifti1Image(data, affine))
-        columns = np.random.choice(list(string.ascii_lowercase),
-                                   size=rk,
-                                   replace=False)
-        design_matrices.append(pd.DataFrame(np.random.randn(shape[3], rk),
-                                            columns=columns))
-    mask = Nifti1Image((np.random.rand(*shape[:3]) > .5).astype(np.int8),
+        columns = rand_gen.choice(list(string.ascii_lowercase),
+                                  size=rk,
+                                  replace=False)
+        design_matrices.append(
+            pd.DataFrame(rand_gen.randn(shape[3], rk), columns=columns))
+    mask = Nifti1Image((rand_gen.rand(*shape[:3]) > .5).astype(np.int8),
                        affine)
     return mask, fmri_data, design_matrices
 
 
-def write_fake_fmri_data_and_design(shapes, rk=3, affine=np.eye(4)):
+def write_fake_fmri_data_and_design(shapes,
+                                    rk=3,
+                                    affine=np.eye(4),
+                                    random_state=0):
+    """Generate random :term:`fMRI` data and design matrices and write them to
+    disk.
+
+    Parameters
+    ----------
+    shapes : :obj:`list` of :obj:`tuple`s of :obj:`int`
+        A list of shapes in tuple format.
+
+    rk : :obj:`int`, optional
+        Number of columns in the design matrix to be generated.
+        Default=3.
+
+    affine : :obj:`numpy.ndarray`, optional
+        Affine of returned images.
+        Default=np.eye(4).
+
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    Returns
+    -------
+    mask_file : :obj:`str`
+        Path to mask file.
+
+    fmri_files : :obj:`list` of :obj:`str`
+        A list of paths to the generated fmri files.
+
+    design_files : :obj:`list` of :obj:`str`
+        A list of paths to the generated design matrix files.
+
+    See Also
+    ------
+    nilearn._utils.data_gen.generate_fake_fmri_data_and_design
+
+    """
     mask_file, fmri_files, design_files = 'mask.nii', [], []
+    rand_gen = check_random_state(random_state)
     for i, shape in enumerate(shapes):
         fmri_files.append('fmri_run%d.nii' % i)
-        data = np.random.randn(*shape)
+        data = rand_gen.randn(*shape)
         data[1:-1, 1:-1, 1:-1] += 100
         Nifti1Image(data, affine).to_filename(fmri_files[-1])
         design_files.append('dmtx_%d.csv' % i)
-        pd.DataFrame(np.random.randn(shape[3], rk),
+        pd.DataFrame(rand_gen.randn(shape[3], rk),
                      columns=['', '', '']).to_csv(design_files[-1])
-    Nifti1Image((np.random.rand(*shape[:3]) > .5).astype(np.int8),
+    Nifti1Image((rand_gen.rand(*shape[:3]) > .5).astype(np.int8),
                 affine).to_filename(mask_file)
     return mask_file, fmri_files, design_files
 
 
-def write_fake_bold_img(file_path, shape, rk=3, affine=np.eye(4)):
-    data = np.random.randn(*shape)
+def write_fake_bold_img(file_path,
+                        shape,
+                        affine=np.eye(4),
+                        random_state=0):
+    """Generate a random image of given shape and write it to disk.
+
+    Parameters
+    ----------
+    file_path : :obj:`str`
+        Output file path.
+
+    shape : :obj:`tuple` of :obj:`int`
+        Shape of output array. Should be at least 3D.
+
+    affine : :obj:`numpy.ndarray`, optional
+        Affine of returned images.
+        Default=np.eye(4).
+
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    Returns
+    -------
+    file_path : :obj:`str`
+        Output file path.
+
+    """
+    rand_gen = check_random_state(random_state)
+    data = rand_gen.randn(*shape)
     data[1:-1, 1:-1, 1:-1] += 100
     Nifti1Image(data, affine).to_filename(file_path)
     return file_path
 
 
 def generate_signals_from_precisions(precisions,
-                                     min_n_samples=50, max_n_samples=100,
+                                     min_n_samples=50,
+                                     max_n_samples=100,
                                      random_state=0):
     """Generate timeseries according to some given precision matrices.
 
@@ -381,88 +601,103 @@ def generate_signals_from_precisions(precisions,
 
     Parameters
     ----------
-    precisions : list of numpy.ndarray
-        List of precision matrices. Every matrix must be square (with the same
-        size) and positive definite. The output of
+    precisions : :obj:`list` of :obj:`numpy.ndarray`
+        A list of precision matrices. Every matrix must be square (with the
+        same size) and positive definite. The output of
         generate_group_sparse_gaussian_graphs() can be used here.
 
-    min_samples, max_samples : int, optional
+    min_samples, max_samples : :obj:`int`, optional
         The number of samples drawn for each timeseries is taken at random
         between these two numbers. Defaults are 50 and 100.
 
-    random_state : int, optional
-        Random state. Default=0.
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
 
     Returns
     -------
-    signals : list of numpy.ndarray
+    signals : :obj:`list` of :obj:`numpy.ndarray`
         Output signals. signals[n] corresponds to precisions[n], and has shape
         (sample number, precisions[n].shape[0]).
 
     """
-    random_state = check_random_state(random_state)
+    rand_gen = check_random_state(random_state)
 
     signals = []
-    n_samples = random_state.randint(min_n_samples, high=max_n_samples,
-                                     size=len(precisions))
+    n_samples = rand_gen.randint(min_n_samples,
+                                 high=max_n_samples,
+                                 size=len(precisions))
 
     mean = np.zeros(precisions[0].shape[0])
     for n, prec in zip(n_samples, precisions):
-        signals.append(random_state.multivariate_normal(mean,
-                                                    np.linalg.inv(prec),
-                                                    (n,)))
+        signals.append(
+            rand_gen.multivariate_normal(mean, np.linalg.inv(prec), (n, )))
     return signals
 
 
-def generate_group_sparse_gaussian_graphs(
-        n_subjects=5, n_features=30, min_n_samples=30, max_n_samples=50,
-        density=0.1, random_state=0, verbose=0):
+def generate_group_sparse_gaussian_graphs(n_subjects=5,
+                                          n_features=30,
+                                          min_n_samples=30,
+                                          max_n_samples=50,
+                                          density=0.1,
+                                          random_state=0,
+                                          verbose=0):
     """Generate signals drawn from a sparse Gaussian graphical model.
 
     Parameters
     ----------
-    n_subjects : int, optional
-        Number of subjects. Default=5.
-
-    n_features : int, optional
-        Number of signals per subject to generate. Default=30.
+    n_subjects : :obj:`int`, optional
+        Number of subjects.
+        Default=5.
 
-    density : float, optional
-        Density of edges in graph topology. Default=0.1.
+    n_features : :obj:`int`, optional
+        Number of signals per subject to generate.
+        Default=30.
 
-    min_n_samples, max_n_samples : int, optional
+    min_n_samples, max_n_samples : :obj:`int`, optional
         Each subject have a different number of samples, between these two
         numbers. All signals for a given subject have the same number of
         samples. Defaults are 30 and 50.
 
-    random_state : int or numpy.random.RandomState instance, optional
-        Random number generator, or seed. Default=0.
+    density : :obj:`float`, optional
+        Density of edges in graph topology.
+        Default=0.1.
 
-    verbose : int, optional
-        Verbosity level (0 means no message). Default=0.
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    verbose : :obj:`int`, optional
+        Verbosity level (0 means no message).
+        Default=0.
 
     Returns
     -------
-    subjects : list of numpy.ndarray, shape for each (n_samples, n_features)
-        subjects[n] is the signals for subject n. They are provided as a numpy
-        len(subjects) = n_subjects. n_samples varies according to the subject.
+    signals : :obj:`list` of :obj:`numpy.ndarray`, shape for each \
+        (n_samples, n_features) signals[n] is the signals for subject n.
+        They are provided as a numpy len(signals) = n_subjects.
+        n_samples varies according to the subject.
 
-    precisions : list of numpy.ndarray
+    precisions : :obj:`list` of :obj:`numpy.ndarray`
         Precision matrices.
 
-    topology : numpy.ndarray
+    topology : :obj:`numpy.ndarray`
         Binary array giving the graph topology used for generating covariances
         and signals.
 
     """
-    random_state = check_random_state(random_state)
+    rand_gen = check_random_state(random_state)
     # Generate topology (upper triangular binary matrix, with zeros on the
     # diagonal)
     topology = np.empty((n_features, n_features))
-    topology[:, :] = np.triu((
-        random_state.randint(0, high=int(1. / density),
-                             size=n_features * n_features)
-    ).reshape(n_features, n_features) == 0, k=1)
+    topology[:, :] = np.triu(
+        (rand_gen.randint(0,
+                          high=int(1. / density),
+                          size=n_features * n_features)).reshape(
+                              n_features, n_features) == 0,
+        k=1)
 
     # Generate edges weights on topology
     precisions = []
@@ -471,7 +706,7 @@ def generate_group_sparse_gaussian_graphs(
 
         # See also sklearn.datasets.samples_generator.make_sparse_spd_matrix
         prec = topology.copy()
-        prec[mask] = random_state.uniform(low=.1, high=.8, size=(mask.sum()))
+        prec[mask] = rand_gen.uniform(low=.1, high=.8, size=(mask.sum()))
         prec += np.eye(prec.shape[0])
         prec = np.dot(prec.T, prec)
 
@@ -488,16 +723,16 @@ def generate_group_sparse_gaussian_graphs(
     topology += np.eye(*topology.shape)
     topology = np.dot(topology.T, topology)
     topology = topology > 0
-    assert(np.all(topology == topology.T))
-    logger.log("Sparsity: {0:f}".format(
-        1. * topology.sum() / (topology.shape[0] ** 2)),
+    assert (np.all(topology == topology.T))
+    logger.log(
+        "Sparsity: {0:f}".format(1. * topology.sum() / (topology.shape[0]**2)),
         verbose=verbose)
 
     # Generate temporal signals
     signals = generate_signals_from_precisions(precisions,
                                                min_n_samples=min_n_samples,
                                                max_n_samples=max_n_samples,
-                                               random_state=random_state)
+                                               random_state=rand_gen)
     return signals, precisions, topology
 
 
@@ -530,67 +765,106 @@ def basic_paradigm(condition_names_have_spaces=False):
     return events
 
 
-def basic_confounds(length):
+def basic_confounds(length, random_state=0):
+    """Generate random motion parameters (3 translation directions, 3 rotation
+    directions).
+
+    Parameters
+    ----------
+    length : :obj:`int`
+        Length of basic confounds.
+
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
+    Returns
+    -------
+    confounds : :obj:`pandas.DataFrame`.
+        Basic confounds.
+        This DataFrame will have six columns: "RotX", "RotY", "RotZ", "X", "Y",
+        and "Z".
+
+    """
+    rand_gen = check_random_state(random_state)
     columns = ['RotX', 'RotY', 'RotZ', 'X', 'Y', 'Z']
-    data = np.random.rand(length, 6)
+    data = rand_gen.rand(length, 6)
     confounds = pd.DataFrame(data, columns=columns)
     return confounds
 
 
-def create_fake_bids_dataset(base_dir='', n_sub=10, n_ses=2,
+def create_fake_bids_dataset(base_dir='',
+                             n_sub=10,
+                             n_ses=2,
                              tasks=['localizer', 'main'],
-                             n_runs=[1, 3], with_derivatives=True,
+                             n_runs=[1, 3],
+                             with_derivatives=True,
                              with_confounds=True,
                              confounds_tag="desc-confounds_timeseries",
-                             no_session=False):
+                             no_session=False,
+                             random_state=0):
     """Creates a fake :term:`bids<BIDS>` dataset directory with dummy files.
+
     Returns fake dataset directory name.
 
     Parameters
     ----------
-    base_dir : string (Absolute path), optional
-        Absolute directory path in which to create the fake :term:`BIDS` dataset dir.
+    base_dir : :obj:`str` (Absolute path), optional
+        Absolute directory path in which to create the fake :term:`BIDS`
+        dataset dir.
         Default: Current directory.
 
-    n_sub : int, optional
-        Number of subject to be simulated in the dataset.
+    n_sub : :obj:`int`, optional
+        Number of subjects to be simulated in the dataset.
         Default=10.
 
-    n_ses : int, optional
+    n_ses : :obj:`int`, optional
         Number of sessions to be simulated in the dataset.
         Ignored if no_session=True.
         Default=2.
 
-    tasks : List of str, optional
-        List of tasks. Default=["localizer", "main"].
+    tasks : :obj:`list` of :obj:`str`, optional
+        List of tasks to be simulated in the dataset.
+        Default=["localizer", "main"].
 
-    n_runs : List of int, optional
+    n_runs : :obj:`list` of :obj:`int`, optional
+        Number of runs to create, where each element indicates the
+        number of runs for the corresponding task.
+        The length of this list must match the number of items in ``tasks``.
+        Each run creates 100 volumes.
         Default=[1, 3].
 
-    with_derivatives : bool, optional
+    with_derivatives : :obj:`bool`, optional
         In the case derivatives are included, they come with two spaces and
         descriptions. Spaces are 'MNI' and 'T1w'. Descriptions are 'preproc'
         and :term:`fMRIPrep`. Only space 'T1w' include both descriptions.
         Default=True.
 
-    with_confounds : bool, optional
+    with_confounds : :obj:`bool`, optional
+        Whether to generate associated confounds files or not.
         Default=True.
 
-    confounds_tag : string (filename suffix), optional
+    confounds_tag : :obj:`str` (filename suffix), optional
         If generating confounds, what path should they have? Defaults to
         `desc-confounds_timeseries` as in :term:`fMRIPrep` >= 20.2
         but can be other values (e.g. "desc-confounds_regressors" as
         in :term:`fMRIPrep` < 20.2).
         Default="desc-confounds_timeseries".
 
-    no_session : bool, optional
+    no_session : :obj:`bool`, optional
         Specifying no_sessions will only produce runs and files without the
         optional session field. In this case n_ses will be ignored.
         Default=False.
 
+    random_state : :obj:`int` or :obj:`numpy.random.RandomState` instance, \
+                   optional
+        Random number generator, or seed.
+        Default=0.
+
     Returns
     -------
-    dataset directory name : string
+    dataset directory name : :obj:`str`
         'bids_dataset'.
 
     Notes
@@ -600,6 +874,7 @@ def create_fake_bids_dataset(base_dir='', n_sub=10, n_ses=2,
     """
     bids_path = os.path.join(base_dir, 'bids_dataset')
     os.makedirs(bids_path)
+    rand_gen = check_random_state(random_state)
     # Create surface bids dataset
     open(os.path.join(bids_path, 'README.txt'), 'w')
     vox = 4
@@ -618,7 +893,8 @@ def create_fake_bids_dataset(base_dir='', n_sub=10, n_ses=2,
             os.makedirs(func_path)
             for task, n_run in zip(tasks, n_runs):
                 run_labels = [
-                    'run-%02d' % label for label in range(1, n_run + 1)]
+                    'run-%02d' % label for label in range(1, n_run + 1)
+                ]
                 for run in run_labels:
                     fields = [subject, session, 'task-' + task]
                     if '' in fields:
@@ -628,12 +904,11 @@ def create_fake_bids_dataset(base_dir='', n_sub=10, n_ses=2,
                         file_id += '_' + run
                     bold_path = os.path.join(func_path,
                                              file_id + '_bold.nii.gz')
-                    write_fake_bold_img(bold_path, [vox, vox, vox, 100])
+                    write_fake_bold_img(bold_path, [vox, vox, vox, 100],
+                                        random_state=rand_gen)
                     events_path = os.path.join(func_path,
                                                file_id + '_events.tsv')
-                    basic_paradigm().to_csv(events_path,
-                                            sep='\t',
-                                            index=None)
+                    basic_paradigm().to_csv(events_path, sep='\t', index=None)
                     param_path = os.path.join(func_path,
                                               file_id + '_bold.json')
                     with open(param_path, 'w') as param_file:
@@ -649,9 +924,10 @@ def create_fake_bids_dataset(base_dir='', n_sub=10, n_ses=2,
                 func_path = os.path.join(subses_dir, 'func')
                 os.makedirs(func_path)
                 for task, n_run in zip(tasks, n_runs):
-                    for run in ['run-%02d' % label
-                                for label in range(1, n_run + 1)
-                                ]:
+                    for run in [
+                            'run-%02d' % label
+                            for label in range(1, n_run + 1)
+                    ]:
                         fields = [subject, session, 'task-' + task]
                         if '' in fields:
                             fields.remove('')
@@ -659,31 +935,25 @@ def create_fake_bids_dataset(base_dir='', n_sub=10, n_ses=2,
                         if n_run > 1:
                             file_id += '_' + run
                         preproc = (
-                            file_id + '_space-MNI_desc-preproc_bold.nii.gz'
-                        )
+                            file_id + '_space-MNI_desc-preproc_bold.nii.gz')
                         preproc_path = os.path.join(func_path, preproc)
-                        write_fake_bold_img(preproc_path,
-                                            [vox, vox, vox, 100]
-                                            )
+                        write_fake_bold_img(preproc_path, [vox, vox, vox, 100],
+                                            random_state=rand_gen)
                         preproc = (
-                            file_id + '_space-T1w_desc-preproc_bold.nii.gz'
-                        )
+                            file_id + '_space-T1w_desc-preproc_bold.nii.gz')
                         preproc_path = os.path.join(func_path, preproc)
-                        write_fake_bold_img(preproc_path,
-                                            [vox, vox, vox, 100]
-                                            )
+                        write_fake_bold_img(preproc_path, [vox, vox, vox, 100],
+                                            random_state=rand_gen)
                         preproc = (
-                            file_id + '_space-T1w_desc-fmriprep_bold.nii.gz'
-                        )
+                            file_id + '_space-T1w_desc-fmriprep_bold.nii.gz')
                         preproc_path = os.path.join(func_path, preproc)
-                        write_fake_bold_img(preproc_path,
-                                            [vox, vox, vox, 100]
-                                            )
+                        write_fake_bold_img(preproc_path, [vox, vox, vox, 100],
+                                            random_state=rand_gen)
                         if with_confounds:
                             confounds_path = os.path.join(
                                 func_path,
                                 file_id + '_' + confounds_tag + '.tsv',
                             )
-                            basic_confounds(100).to_csv(confounds_path,
-                                                        sep='\t', index=None)
+                            basic_confounds(100, random_state=rand_gen).to_csv(
+                                confounds_path, sep='\t', index=None)
     return 'bids_dataset'


Test patch:
diff --git a/nilearn/glm/tests/test_utils.py b/nilearn/glm/tests/test_utils.py
--- a/nilearn/glm/tests/test_utils.py
+++ b/nilearn/glm/tests/test_utils.py
@@ -100,7 +100,7 @@ def test_z_score():
 
 def test_z_score_opposite_contrast():
     fmri, mask = generate_fake_fmri(shape=(50, 20, 50), length=96,
-                                    rand_gen=np.random.RandomState(42))
+                                    random_state=np.random.RandomState(42))
 
     nifti_masker = NiftiMasker(mask_img=mask)
     data = nifti_masker.fit_transform(fmri)
diff --git a/nilearn/plotting/tests/test_img_plotting/test_plot_img_comparisons.py b/nilearn/plotting/tests/test_img_plotting/test_plot_img_comparisons.py
--- a/nilearn/plotting/tests/test_img_plotting/test_plot_img_comparisons.py
+++ b/nilearn/plotting/tests/test_img_plotting/test_plot_img_comparisons.py
@@ -15,12 +15,12 @@ def test_plot_img_comparison():
     axes = axes.ravel()
     kwargs = {"shape": (3, 2, 4), "length": 5}
     query_images, mask_img = generate_fake_fmri(
-        rand_gen=np.random.RandomState(0), **kwargs
+        random_state=np.random.RandomState(0), **kwargs
     )
     # plot_img_comparison doesn't handle 4d images ATM
     query_images = list(iter_img(query_images))
     target_images, _ = generate_fake_fmri(
-        rand_gen=np.random.RandomState(1), **kwargs
+        random_state=np.random.RandomState(1), **kwargs
     )
     target_images = list(iter_img(target_images))
     target_images[0] = query_images[0]
diff --git a/nilearn/tests/test_testing.py b/nilearn/tests/test_testing.py
--- a/nilearn/tests/test_testing.py
+++ b/nilearn/tests/test_testing.py
@@ -53,13 +53,13 @@ def test_generate_fake_fmri():
                 shape=shape, length=length, kind=kind,
                 n_blocks=n_block, block_size=bsize,
                 block_type=btype,
-                rand_gen=rand_gen)
+                random_state=rand_gen)
         else:
             fmri, mask, target = generate_fake_fmri(
                 shape=shape, length=length, kind=kind,
                 n_blocks=n_block, block_size=bsize,
                 block_type=btype,
-                rand_gen=rand_gen)
+                random_state=rand_gen)
 
         assert fmri.shape[:-1] == shape
         assert fmri.shape[-1] == length
@@ -68,7 +68,7 @@ def test_generate_fake_fmri():
             assert target.size == length
 
     pytest.raises(ValueError, generate_fake_fmri, length=10, n_blocks=10,
-                  block_size=None, rand_gen=rand_gen)
+                  block_size=None, random_state=rand_gen)
 
 
 def test_int64_niftis(tmp_path):