Repository: deephyper/deephyper (language: python)

Issue:
Check docstrings with ruff to improve code documentation
Using ruff, we will check the docstrings of the public code. If docstrings are not present or do not follow "google style" then it will raise an error and fail the test.


Discussion / hints:
Here is the `pyproject.toml` configuration settings for using ruff to check docstrings: 
https://docs.astral.sh/ruff/settings/#lintpydocstyle
Here are the pydocstyle rules used by ruff for checking docstrings:
https://docs.astral.sh/ruff/rules/#pydocstyle-d

Fix patch:
diff --git a/docs/conf.py b/docs/conf.py
index f4fe8158..7fb5df25 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -31,7 +31,7 @@
 
 # The short X.Y version
 about = {}
-with open(f"../deephyper/__version__.py") as f:
+with open("../src/deephyper/__version__.py") as f:
     exec(f.read(), about)
 
 version = about["__version__"]
diff --git a/pyproject.toml b/pyproject.toml
index 84a0b01a..f0c3e83d 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -108,4 +108,18 @@ markers = [
 ]
 
 [tool.ruff]
-pydocstyle.convention = "google"
\ No newline at end of file
+exclude = [
+    "docs",
+    "examples",
+    "src/deephyper/skopt",
+    "src/deephyper/analysis/hpo/_paxplot.py",
+    "tests",
+]
+line-length = 100
+
+[tool.ruff.lint]
+extend-select = ["D", "E501"]
+ignore = ["D107"]
+
+[tool.ruff.lint.pydocstyle]
+convention = "google"
diff --git a/src/deephyper/__init__.py b/src/deephyper/__init__.py
index 1954faa3..ee047bb0 100644
--- a/src/deephyper/__init__.py
+++ b/src/deephyper/__init__.py
@@ -1,11 +1,22 @@
-"""DeepHyper's software architecture is designed to be modular and extensible. It is built on top of the following main subpackages:
+"""DeepHyper's software architecture is designed to be modular and extensible.
+
+It is organized around the following subpackages:
 
 * :mod:`deephyper.analysis`: Tools to analyse results from DeepHyper.
-* :mod:`deephyper.ensemble`: Tools to build ensembles of neural networks with uncertainty quantification.
-* :mod:`deephyper.evaluator` : Tools to distribute the evaluation of tasks (e.g., neural network trainings).
-* :mod:`deephyper.hpo`: Tools to define and run hyperparameter optimization (HPO) and neural architecture search (NAS).
+
+* :mod:`deephyper.ensemble`: Tools to build ensembles of neural networks with
+   uncertainty quantification.
+
+* :mod:`deephyper.evaluator` : Tools to distribute the evaluation of tasks
+   (e.g., neural network trainings).
+
+* :mod:`deephyper.hpo`: Tools to define and run hyperparameter optimization
+   (HPO) and neural architecture search (NAS).
+
 * :mod:`deephyper.predictor`: Tools to wrap pure predictive models (i.e., that can only predict).
-* :mod:`deephyper.stopper`: Tools to define multi-fidelity strategies for hyperparameter optimization (HPO) and neural architecture search (NAS).
+
+* :mod:`deephyper.stopper`: Tools to define multi-fidelity strategies for
+   hyperparameter optimization (HPO) and neural architecture search (NAS).
 
 
 DeepHyper installation requires **Python >= 3.10**.
diff --git a/src/deephyper/analysis/__init__.py b/src/deephyper/analysis/__init__.py
index ed1b2c66..421fa0fc 100644
--- a/src/deephyper/analysis/__init__.py
+++ b/src/deephyper/analysis/__init__.py
@@ -1,5 +1,4 @@
-"""This analysis subpackage contains modules to analyze results returned by deephyper.
-"""
+"""This analysis subpackage contains modules to analyze results returned by deephyper."""
 
 from ._rank import rank
 from ._matplotlib import figure_size, update_matplotlib_rc
diff --git a/src/deephyper/analysis/_matplotlib.py b/src/deephyper/analysis/_matplotlib.py
index 9887e9bc..6be96280 100644
--- a/src/deephyper/analysis/_matplotlib.py
+++ b/src/deephyper/analysis/_matplotlib.py
@@ -6,7 +6,7 @@ def figure_size(width, fraction=1):
 
     Args:
         width (float): Document textwidth or columnwidth in pts.
-        fraction (float, optional) Fraction of the width which you wish the figure to occupy.
+        fraction (float, optional): Fraction of the width which you wish the figure to occupy.
 
     Returns:
         tuple: Dimensions of figure in inches.
diff --git a/src/deephyper/analysis/_rank.py b/src/deephyper/analysis/_rank.py
index 23ab20b5..4cc940dd 100644
--- a/src/deephyper/analysis/_rank.py
+++ b/src/deephyper/analysis/_rank.py
@@ -11,14 +11,24 @@ def rank(
     axis=None,
     nan_policy="propagate",
 ):
-    """Returns the ranking from a list of scores given a tolerance epsilon (wrapper around ``scipy.stats.rankdata``, see `Scipy Documentation <https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.rankdata.html>`_). Lower scores corresponds to lower ranks.
+    """Returns the ranking from a list of scores given a tolerance epsilon.
+
+    This function is a wrapper around ``scipy.stats.rankdata``, see `Scipy Documentation
+    <https://docs.scipy.org/doc/scipy/reference/generated/scipy.stats.rankdata.html>`_).
+
+    Lower scores corresponds to lower ranks.
 
     Args:
         a (array): List of scores.
-        method (str, optional): The method used to assign ranks to tied elements. The options are ``"average"``, ``"min"``, ``"max"``, ``"dense"`` and ``'ordinal'``. Defaults to ``"min"``.
-        decimals (int, optional): The number of decimal at which rounding is performed. Defaults to ``3``.
-        axis (int, optional): The axis along which the elements of ``a`` are ranked. Defaults to ``None`` to rank the elements after flattening the array.
-        nan_policy (str, optional): Defines how to handle when input contains nan. The options are ``"propagate"``, ``"raise"``, ``"omit"``. Defaults to ``"propagate"``.
+        method (str, optional): The method used to assign ranks to tied elements. The options are
+            ``"average"``, ``"min"``, ``"max"``, ``"dense"`` and ``'ordinal'``. Defaults to
+            ``"min"``.
+        decimals (int, optional): The number of decimal at which rounding is performed. Defaults to
+            ``3``.
+        axis (int, optional): The axis along which the elements of ``a`` are ranked. Defaults to
+            ``None`` to rank the elements after flattening the array.
+        nan_policy (str, optional): Defines how to handle when input contains nan. The options are
+            ``"propagate"``, ``"raise"``, ``"omit"``. Defaults to ``"propagate"``.
 
     Returns:
         array: The ranking of the scores.
diff --git a/src/deephyper/analysis/hpo/_hpo.py b/src/deephyper/analysis/hpo/_hpo.py
index 2aa0e49f..6dc1e49f 100644
--- a/src/deephyper/analysis/hpo/_hpo.py
+++ b/src/deephyper/analysis/hpo/_hpo.py
@@ -33,7 +33,9 @@ def filter_failed_objectives(
         df (pd.DataFrame): the results of a Hyperparameter Search.
 
     Returns:
-        Tuple[pd.DataFrame, pd.DataFrame]: ``df_without_failures, df_with_failures`` the first are results of a Hyperparameter Search without failed objectives and the second are results of Hyperparameter search with failed objectives.
+        Tuple[pd.DataFrame, pd.DataFrame]: ``df_without_failures, df_with_failures`` the first are
+        results of a Hyperparameter Search without failed objectives and the second are results of
+        Hyperparameter search with failed objectives.
     """
     # Single-Objective
     if "objective" in df.columns:
@@ -59,12 +61,11 @@ def filter_failed_objectives(
 
         df_with_failures = df[mask]
         df_without_failures = df[~mask]
-        df_without_failures = df_without_failures.astype(
-            {objcol_i: float for objcol_i in objcol}
-        )
+        df_without_failures = df_without_failures.astype({objcol_i: float for objcol_i in objcol})
     else:
         raise ValueError(
-            "The DataFrame does not contain neither a column named 'objective' nor columns named 'objective_<int>'."
+            "The DataFrame does not contain neither a column named 'objective' nor columns named "
+            "'objective_<int>'."
         )
 
     return df_without_failures, df_with_failures
@@ -82,9 +83,7 @@ def parameters_from_row(row: pd.Series) -> dict:
     return {k[2:]: v for k, v in row.to_dict().items() if k.startswith("p:")}
 
 
-def parameters_at_max(
-    df: pd.DataFrame, column: str = "objective"
-) -> Tuple[dict, float]:
+def parameters_at_max(df: pd.DataFrame, column: str = "objective") -> Tuple[dict, float]:
     """Return the parameters at the maximum of the given ``column`` function.
 
     Args:
@@ -92,7 +91,8 @@ def parameters_at_max(
         column (str, optional): the column to use for the maximization. Defaults to ``"objective"``.
 
     Returns:
-        Tuple[dict, float]: the parameters at the maximum of the ``column`` and its corresponding value.
+        Tuple[dict, float]: the parameters at the maximum of the ``column`` and its corresponding
+            value.
     """
     df, _ = filter_failed_objectives(df)
     idx = df[column].argmax()
@@ -112,7 +112,8 @@ def parameters_at_topk(
         k (int, optional): the number of top-k to return. Defaults to ``1``.
 
     Returns:
-        List[Tuple[dict, float]]: the parameters at the maximum of the ``column`` and its corresponding value.
+        List[Tuple[dict, float]]: the parameters at the maximum of the ``column`` and its
+            corresponding value.
     """
     df, _ = filter_failed_objectives(df)
     df = df.nlargest(k, columns=column)
@@ -133,10 +134,14 @@ def plot_search_trajectory_single_objective_hpo(
     Args:
         results (pd.DataFrame): the results of Hyperparameter Optimization.
         show_failures (bool, optional): whether to show the failed objectives. Defaults to ``True``.
-        column (str, optional): the column to use for the y-axis of the plot. Defaults to ``"objective"``.
-        mode (str, optional): if the plot should be made for minimization ``"min"`` or maximization ``"max"``. Defaults to ``"max"``.
-        x_units (str, optional): if the plot should be made with respect to evaluations ``"evaluations"`` or time ``"seconds"``. Defaults to ``"evaluations"``.
+        column (str, optional): the column to use for the y-axis of the plot. Defaults to
+            ``"objective"``.
+        mode (str, optional): if the plot should be made for minimization ``"min"`` or maximization
+            ``"max"``. Defaults to ``"max"``.
+        x_units (str, optional): if the plot should be made with respect to evaluations
+            ``"evaluations"`` or time ``"seconds"``. Defaults to ``"evaluations"``.
         ax (matplotlib.pyplot.axes): the axes to use for the plot.
+        kwargs (dict): other keywords arguments passed to``ax.scatter(...)``.
 
     Returns:
         (matplotlib.pyplot.figure, matplotlib.pyplot.axes): the figure and axes of the plot.
@@ -224,10 +229,12 @@ def compile_worker_activity(results, profile_type="submit/gather"):
 
     Args:
         results (pd.DataFrame): the results of a Hyperparameter Search.
-        profile_type (str, optional): the type of profile to build. It can be `"submit/gather"` or `"start/end"`. Defaults to "submit/gather".
+        profile_type (str, optional): the type of profile to build. It can be `"submit/gather"` or
+            `"start/end"`. Defaults to "submit/gather".
 
     Returns:
-        timestamps, n_jobs_active: a list of timestamps and a list of the number of active jobs at each timestamp.
+        timestamps, n_jobs_active: a list of timestamps and a list of the number of active jobs at
+            each timestamp.
     """
     if profile_type == "submit/gather":
         key_start, key_end = "m:timestamp_submit", "m:timestamp_gather"
@@ -235,13 +242,12 @@ def compile_worker_activity(results, profile_type="submit/gather"):
         key_start, key_end = "m:timestamp_start", "m:timestamp_end"
     else:
         raise ValueError(
-            f"Unknown profile_type='{profile_type}' it should be one of ['submit/gather', 'start/end']."
+            f"Unknown profile_type='{profile_type}' it should be one of "
+            "['submit/gather', 'start/end']"
         )
 
     if key_start not in results.columns or key_end not in results.columns:
-        raise ValueError(
-            f"Columns '{key_start}' and '{key_end}' are not present in the DataFrame."
-        )
+        raise ValueError(f"Columns '{key_start}' and '{key_end}' are not present in the DataFrame.")
 
     results = results.sort_values(by=[key_start], ascending=True)
 
@@ -274,9 +280,13 @@ def plot_worker_utilization(
 
     Args:
         results (pd.DataFrame): the results of a Hyperparameter Search.
-        num_workers (int, optional): the number of workers. If passed the normalized utilization will be shown (/num_workers). Otherwise, the raw number of active workers is shown. Defaults to ``None``.
-        profile_type (str, optional): the type of profile to build. It can be `"submit/gather"` or `"start/end"`. Defaults to "submit/gather".
+        num_workers (int, optional): the number of workers. If passed the normalized utilization
+            will be shown (/num_workers). Otherwise, the raw number of active workers is shown.
+            Defaults to ``None``.
+        profile_type (str, optional): the type of profile to build. It can be `"submit/gather"`
+            or `"start/end"`. Defaults to "submit/gather".
         ax (matplotlib.pyplot.axes): the axes to use for the plot.
+        kwargs (dict): other keywords arguments passed to the ``ax.step(...)``.
 
     Returns:
         (matplotlib.pyplot.figure, matplotlib.pyplot.axes): the figure and axes of the plot.
@@ -337,12 +347,8 @@ def add_colorbar_px(paxfig, data, cmap="viridis", colorbar_kwargs={}):
     ax_colorbar = paxfig.add_subplot(gs[0, n_axes])
 
     # Create colorbar
-    sm = plt.cm.ScalarMappable(
-        norm=plt.Normalize(vmin=data.min(), vmax=data.max()), cmap=cmap
-    )
-    cbar = paxfig.colorbar(
-        sm, orientation="vertical", ax=ax_colorbar, **colorbar_kwargs
-    )
+    sm = plt.cm.ScalarMappable(norm=plt.Normalize(vmin=data.min(), vmax=data.max()), cmap=cmap)
+    cbar = paxfig.colorbar(sm, orientation="vertical", ax=ax_colorbar, **colorbar_kwargs)
     cbar.locator = MaxNLocator(integer=True)
     cbar.update_ticks()
 
@@ -378,9 +384,10 @@ def plot_parallel_coordinate(
         results (pd.DataFrame): the results of a Hyperparameter Search.
         parameters_columns (list, optional): list of columns to include in the plot.
         objective_column (str): name of the objective column
-        rank_mode (str, optional): mode of ranking. Defaults to "min".
-        highlight (bool, optional): whether to highlight the best solutions. Defaults to True.
-        constant_predictor (float, optional): value to compare the objective to. Defaults to 0.035726056.
+        rank_mode (str, optional): mode of ranking. Defaults to ``"min"``.
+        highlight (bool, optional): whether to highlight the best solutions. Defaults to ``True``.
+        constant_predictor (float, optional): value to compare the objective to. Defaults to
+            ``0.035726056``.
 
     Returns:
         fig: figure of the parallel coordinate plot
@@ -473,9 +480,7 @@ def convertCols(cols):
 
         # Add colorbar
         color_col = len(cols) - 1
-        paxfig.add_colorbar(
-            ax_idx=color_col, cmap=cmap, colorbar_kwargs={"label": "Rank"}
-        )
+        paxfig.add_colorbar(ax_idx=color_col, cmap=cmap, colorbar_kwargs={"label": "Rank"})
 
     fig = plt.gcf()
     ax = fig.gca()
diff --git a/src/deephyper/analysis/hpo/_paxplot.py b/src/deephyper/analysis/hpo/_paxplot.py
index 2bca6f4b..e5d17a7f 100644
--- a/src/deephyper/analysis/hpo/_paxplot.py
+++ b/src/deephyper/analysis/hpo/_paxplot.py
@@ -15,8 +15,7 @@ class PaxFigure(Figure):
     _safe_inherited_functions = ["savefig", "set_size_inches", "draw", "show"]
 
     def __init__(self, *args, data=[], **kwargs):
-        """Paxplot extension of Matplot Figure
-        """
+        """Paxplot extension of Matplot Figure"""
         # Setup
         super().__init__(*args, **kwargs)
         self._show_unsafe_warning = True
@@ -35,8 +34,7 @@ def __init__(self, *args, data=[], **kwargs):
     def _scale_vals(self, data, lower=None, upper=None):
         """Scale `data` between lower and upper
 
-        Parameters
-        ----------
+        Args:
         data : array-like
             Data to be scalled
         lower : numeric, optional
@@ -45,7 +43,6 @@ def _scale_vals(self, data, lower=None, upper=None):
             Upper value for scaling, by default None
 
         Returns:
-        -------
         array-like
             Scaled data
         """
@@ -64,8 +61,7 @@ def _scale_vals(self, data, lower=None, upper=None):
     def _get_color_gradient(self, val, lower, upper, colormap):
         """Get color gradient values for the `val`
 
-        Parameters
-        ----------
+        Args:
         val : float
             value to get color for scaling
         lower : float
@@ -76,7 +72,6 @@ def _get_color_gradient(self, val, lower, upper, colormap):
             Matplotlib colormap to use for coloring
 
         Returns:
-        -------
         color: str
             string color code
         """
@@ -88,8 +83,7 @@ def _get_color_gradient(self, val, lower, upper, colormap):
     def _update_plot_lines(self, ax_idx):
         """Update plotted lines based on scaled data (_pax_data_scale)
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Axis index to update line data
         """
@@ -117,8 +111,7 @@ def _update_plot_ticks(self, ax_idx):
         """Update ticks based on tick labels (_pax_ticks_labels) and scaled tick
         location data (_pax_ticks_scale)
 
-        Parameters
-        ----------
+        Args:
         ax_idx : _type_
             _description_
 
@@ -137,8 +130,7 @@ def _update_plot_ticks(self, ax_idx):
         self.axes[ax_idx].set_ylim([0.0, 1.0])
 
     def _default_format(self):
-        """Set the default format of a Paxplot Figure
-        """
+        """Set the default format of a Paxplot Figure"""
         # Set attributes
         def_vals = [[0, 1]] * len(self.axes)
         def_bools = [False] * len(self.axes)
@@ -178,8 +170,7 @@ def _default_lim(self, ax_idx):
         """Set the default limits for an axis. Default limits are between the
         minimum and maximum values.
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         """
@@ -198,8 +189,7 @@ def _default_ticks(self, ax_idx):
         """Set the default ticks for an axis. Default ticks are six labels
         between the current limits of the axis.
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         """
@@ -218,13 +208,11 @@ def _default_ticks(self, ax_idx):
     def _convert_string_data(self, data: list):
         """Convert string input data to numerical data
 
-        Parameters
-        ----------
+        Args:
         data : list
             Data to be plotted from `plot`
 
         Returns:
-        -------
         data : list
             Converted `data`
         """
@@ -255,8 +243,7 @@ def _convert_string_data(self, data: list):
     def plot(self, data: list, line_kwargs={}):
         """Plot the supplied data
 
-        Parameters
-        ----------
+        Args:
         data : array-like
             Data to be plotted
 
@@ -331,8 +318,7 @@ def plot(self, data: list, line_kwargs={}):
     def set_lim(self, ax_idx: int, bottom: float, top: float):
         """Set custom limits on axis
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         bottom : numeric
@@ -349,8 +335,7 @@ def set_lim(self, ax_idx: int, bottom: float, top: float):
     def _set_lim(self, ax_idx: int, bottom: float, top: float):
         """Private function to set custom limits on axis
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         bottom : numeric
@@ -403,8 +388,7 @@ def _set_lim(self, ax_idx: int, bottom: float, top: float):
     def set_ticks(self, ax_idx: int, ticks: list, labels=None):
         """Set the axis tick locations and optionally labels.
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         ticks : list of floats
@@ -421,8 +405,7 @@ def set_ticks(self, ax_idx: int, ticks: list, labels=None):
     def _set_ticks(self, ax_idx: int, ticks: list, labels=None):
         """Private function to set the axis tick locations and optionally labels.
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         ticks : list of floats
@@ -488,8 +471,7 @@ def set_even_ticks(
         no minimum and maximum values are specified, the limits of the
         underlying plotted data are assumed.
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         n_ticks : int
@@ -538,8 +520,7 @@ def set_even_ticks(
     def set_label(self, ax_idx: int, label: str):
         """Set the label for the axis
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         label : str
@@ -562,8 +543,7 @@ def set_label(self, ax_idx: int, label: str):
     def set_labels(self, labels: list):
         """Set labels for all axes. A wrapper for set_label
 
-        Parameters
-        ----------
+        Args:
         labels : list
             Labels for each axis. Must be same length as number of axes.
         """
@@ -578,8 +558,7 @@ def set_labels(self, labels: list):
     def invert_axis(self, ax_idx: int):
         """Invert axis
 
-        Parameters
-        ----------
+        Args:
         ax_idx : int
             Index of matplotlib axes
         """
@@ -614,8 +593,7 @@ def invert_axis(self, ax_idx: int):
     def add_legend(self, labels=[]):
         """Create a legend for a specified figure
 
-        Parameters
-        ----------
+        Args:
         labels : list
             List of data labels
         """
@@ -657,8 +635,7 @@ def add_legend(self, labels=[]):
     def add_colorbar(self, ax_idx: int, cmap="viridis", colorbar_kwargs={}):
         """Add colorbar to paxfigure
 
-        Parameters
-        ----------
+        Args:
         ax : int
             axes index
         data : array-like
@@ -723,8 +700,7 @@ def add_colorbar(self, ax_idx: int, cmap="viridis", colorbar_kwargs={}):
 
 
 def add_unsafe_warning(func, fig):
-    """Generate warning if not supported by Paxplot
-    """
+    """Generate warning if not supported by Paxplot"""
 
     @functools.wraps(func)
     def wrapper(*args, **kwargs):
diff --git a/src/deephyper/core/__init__.py b/src/deephyper/core/__init__.py
index e69de29b..0dc1a9f2 100644
--- a/src/deephyper/core/__init__.py
+++ b/src/deephyper/core/__init__.py
@@ -0,0 +1,1 @@
+"""Subpackage with code reused accross the package."""
diff --git a/src/deephyper/core/cli/__init__.py b/src/deephyper/core/cli/__init__.py
index 0d37948e..ec65bf2f 100644
--- a/src/deephyper/core/cli/__init__.py
+++ b/src/deephyper/core/cli/__init__.py
@@ -1,7 +1,6 @@
-# For the documentation
-from . import _cli, _hps
+from . import _cli, _hpo  # noqa: D104
 
-commands = [_cli, _hps]
+commands = [_cli, _hpo]
 
 __doc__ = ""
 
diff --git a/src/deephyper/core/cli/_cli.py b/src/deephyper/core/cli/_cli.py
index 172724a1..49145336 100644
--- a/src/deephyper/core/cli/_cli.py
+++ b/src/deephyper/core/cli/_cli.py
@@ -20,24 +20,28 @@
 
 import argparse
 
-from deephyper.core.cli import _hps
+from deephyper.core.cli import _hpo
 
 
 def create_parser():
-    """:meta private:
+    """Creates the deephyper CL parser.
+
+    :meta private:
     """
     parser = argparse.ArgumentParser(description="DeepHyper command line.")
 
     subparsers = parser.add_subparsers()
 
     # hyper-parameter search
-    _hps.add_subparser(subparsers)
+    _hpo.add_subparser(subparsers)
 
     return parser
 
 
 def main():
-    """:meta private:
+    """Entry point of the CL.
+
+    :meta private:
     """
     parser = create_parser()
 
diff --git a/src/deephyper/core/cli/_hps.py b/src/deephyper/core/cli/_hpo.py
similarity index 51%
rename from src/deephyper/core/cli/_hps.py
rename to src/deephyper/core/cli/_hpo.py
index e93e75bf..e6c1e8fa 100644
--- a/src/deephyper/core/cli/_hps.py
+++ b/src/deephyper/core/cli/_hpo.py
@@ -1,55 +1,11 @@
-"""Hyperparameter Search
----------------------
+"""Command line for hyperparameter optimization.
 
-Command line to execute hyperparameter search.
+Use the command line help option to get more information.
 
 .. code-block:: bash
 
-    $ deephyper hps ambs --help
-
-    usage: deephyper hps ambs [-h] --problem PROBLEM --evaluator EVALUATOR [--random-state RANDOM_STATE] [--log-dir LOG_DIR] [--verbose VERBOSE] [--surrogate-model SURROGATE_MODEL] [--acq-func ACQ_FUNC]
-                            [--kappa KAPPA] [--xi XI] [--liar-strategy LIAR_STRATEGY] [--n-jobs N_JOBS] [--max-evals MAX_EVALS] [--timeout TIMEOUT] --run-function RUN_FUNCTION [--num-workers NUM_WORKERS]
-                            [--callbacks CALLBACKS] [--ray-address RAY_ADDRESS] [--ray-password RAY_PASSWORD] [--ray-num-cpus RAY_NUM_CPUS] [--ray-num-gpus RAY_NUM_GPUS]
-                            [--ray-num-cpus-per-task RAY_NUM_CPUS_PER_TASK] [--ray-num-gpus-per-task RAY_NUM_GPUS_PER_TASK] [--ray-ray-kwargs RAY_RAY_KWARGS]
-
-    optional arguments:
-    -h, --help            show this help message and exit
-    --problem PROBLEM
-    --evaluator EVALUATOR
-    --random-state RANDOM_STATE
-                            Type[int]. Defaults to 'None'.
-    --log-dir LOG_DIR     Type[str]. Defaults to '.'.
-    --verbose VERBOSE     Type[int]. Defaults to '0'.
-    --surrogate-model SURROGATE_MODEL
-                            Type[str]. Defaults to 'RF'.
-    --acq-func ACQ_FUNC   Type[str]. Defaults to 'LCB'.
-    --kappa KAPPA         Type[float]. Defaults to '1.96'.
-    --xi XI               Type[float]. Defaults to '0.001'.
-    --liar-strategy LIAR_STRATEGY
-                            Type[str]. Defaults to 'cl_min'.
-    --n-jobs N_JOBS       Type[int]. Defaults to '1'.
-    --max-evals MAX_EVALS
-                            Type[int]. Defaults to '-1' when an number of evaluations is not imposed.
-    --timeout TIMEOUT     Type[int]. Number of seconds before killing the search. Defaults to 'None' when a time budget is not imposed.
-    --run-function RUN_FUNCTION
-    --num-workers NUM_WORKERS
-                            Type[int]. Defaults to '1'.
-    --callbacks CALLBACKS
-                            Defaults to 'None'.
-    --ray-address RAY_ADDRESS
-                            Type[str]. Defaults to 'None'.
-    --ray-password RAY_PASSWORD
-                            Type[str]. Defaults to 'None'.
-    --ray-num-cpus RAY_NUM_CPUS
-                            Type[int]. Defaults to 'None'.
-    --ray-num-gpus RAY_NUM_GPUS
-                            Type[int]. Defaults to 'None'.
-    --ray-num-cpus-per-task RAY_NUM_CPUS_PER_TASK
-                            Type[float]. Defaults to '1'.
-    --ray-num-gpus-per-task RAY_NUM_GPUS_PER_TASK
-                            Type[float]. Defaults to 'None'.
-    --ray-ray-kwargs RAY_RAY_KWARGS
-                            Type[dict]. Defaults to '{}'.
+   $ deephyper hps ambs --help
+
 """
 
 import argparse
@@ -61,13 +17,17 @@
 from deephyper.evaluator import EVALUATORS, Evaluator
 
 HPS_SEARCHES = {
-    "ambs": "deephyper.hpo.AMBS",
     "cbo": "deephyper.hpo.CBO",
+    "random": "deephyper.hpo.RandomSearch",
+    "regevo": "deephyper.hpo.RegularizedEvolution",
+    "eds": "deephyper.hpo.ExperimentalDesignSearch",
 }
 
 
 def build_parser_from(cls):
-    """:meta private:
+    """Build the parser automatically from the classes.
+
+    :meta private:
     """
     parser = argparse.ArgumentParser(conflict_handler="resolve")
 
@@ -79,13 +39,13 @@ def build_parser_from(cls):
         "--max-evals",
         default=-1,
         type=int,
-        help="Type[int]. Defaults to '-1' when an number of evaluations is not imposed.",
+        help="Defaults to '-1' when number of evaluations is not imposed.",
     )
     parser.add_argument(
         "--timeout",
         default=None,
         type=int,
-        help="Type[int]. Number of seconds before killing the search. Defaults to 'None' when a time budget is not imposed.",
+        help="Number of seconds before killing search, defaults to 'None'.",
     )
 
     # add arguments for evaluators
@@ -104,12 +64,14 @@ def build_parser_from(cls):
 
 
 def add_subparser(parsers):
-    """:meta private:
+    """Definition of the parser of the command.
+
+    :meta private:
     """
-    parser_name = "hps"
+    parser_name = "hpo"
 
     parser = parsers.add_parser(
-        parser_name, help="Command line to run hyperparameter search."
+        parser_name, help="Command line to run hyperparameter optimization."
     )
 
     subparsers = parser.add_subparsers()
@@ -127,7 +89,9 @@ def add_subparser(parsers):
 
 
 def main(**kwargs):
-    """:meta private:
+    """Entry point of the command.
+
+    :meta private:
     """
     sys.path.insert(0, ".")
 
@@ -157,17 +121,13 @@ def main(**kwargs):
     # remove the arguments from unused evaluator
     for method in EVALUATORS.keys():
         evaluator_method_kwargs = {
-            k[len(evaluator_method) + 1 :]: kwargs.pop(k)
-            for k in kwargs.copy()
-            if method in k
+            k[len(evaluator_method) + 1 :]: kwargs.pop(k) for k in kwargs.copy() if method in k
         }
         if method == evaluator_method:
             evaluator_kwargs = {**evaluator_kwargs, **evaluator_method_kwargs}
 
     # create evaluator
-    logging.info(
-        f"Evaluator(method={evaluator_method}, method_kwargs={evaluator_kwargs}"
-    )
+    logging.info(f"Evaluator(method={evaluator_method}, method_kwargs={evaluator_kwargs}")
     evaluator = Evaluator.create(
         run_function, method=evaluator_method, method_kwargs=evaluator_kwargs
     )
diff --git a/src/deephyper/core/exceptions/__init__.py b/src/deephyper/core/exceptions/__init__.py
index b7dd9a96..35920a1a 100644
--- a/src/deephyper/core/exceptions/__init__.py
+++ b/src/deephyper/core/exceptions/__init__.py
@@ -1,6 +1,4 @@
-"""Deephyper exceptions"""
-
-# ! Root exceptions
+"""Set of root exceptions for the package."""
 
 
 class DeephyperError(Exception):
@@ -8,7 +6,10 @@ class DeephyperError(Exception):
 
 
 class DeephyperRuntimeError(RuntimeError):
-    """Raised when an error is detected in deephyper and that doesn’t fall in any of the other categories. The associated value is a string indicating what precisely went wrong."""
+    """Raised when for error that doesn't fall in other categories.
+
+    The associated value is a string indicating what precisely went wrong.
+    """
 
 
 class SearchTerminationError(RuntimeError):
@@ -24,12 +25,12 @@ class TimeoutReached(SearchTerminationError):
 
 
 class RunFunctionError(RuntimeError):
-    """Raised when error occurs in run-function"""
+    """Raised when error occurs in run-function."""
 
     def __init__(self, msg: str = None) -> None:
         self.msg = msg
 
-    def __str__(self) -> str:
+    def __str__(self) -> str:  # noqa: D105
         return self.msg
 
 
diff --git a/src/deephyper/core/exceptions/loading.py b/src/deephyper/core/exceptions/loading.py
index 6ed21f73..4738eb89 100644
--- a/src/deephyper/core/exceptions/loading.py
+++ b/src/deephyper/core/exceptions/loading.py
@@ -12,7 +12,7 @@ def __init__(self, target, attr, error_source, custom_msg=""):
         self.error_source = error_source
         self.custom_msg = custom_msg
 
-    def __str__(self):
+    def __str__(self):  # noqa: D105
         error = (
             f"{self.error_source}\n"
             f"{self.custom_msg}"
diff --git a/src/deephyper/core/exceptions/problem.py b/src/deephyper/core/exceptions/problem.py
deleted file mode 100644
index ce9d1ce3..00000000
--- a/src/deephyper/core/exceptions/problem.py
+++ /dev/null
@@ -1,94 +0,0 @@
-"""Exceptions related with problem definition."""
-
-from deephyper.core.exceptions import DeephyperError
-
-
-class SpaceDimNameOfWrongType(DeephyperError):
-    """Raised when a dimension name of the space is not a string."""
-
-    def __init__(self, value):
-        self.value = value
-
-    def __str__(self):
-        return f"Dimension name: '{self.value}' is of type == {type(self.value)} when should be 'str'!"
-
-
-# ! NaProblemErrors
-
-
-class NaProblemError(DeephyperError):
-    """Raise when an error occurs in a NaProblem instance."""
-
-    def __init__(self, msg: str):
-        self.msg = msg
-
-    def __str__(self):
-        return self.msg
-
-
-class SearchSpaceBuilderIsNotCallable(NaProblemError):
-    """Raised when a search space builder is not a callable."""
-
-    def __init__(self, parameter):
-        self.parameter = parameter
-
-    def __str__(self):
-        raise f"The search space builder {self.parameter} should be a callable when it is not!"
-
-
-class SearchSpaceBuilderMissingParameter(NaProblemError):
-    """Raised when a missing parameter is detected in a callable which creates a Structure.
-
-    Args:
-        missing_parameter (str): name of the missing parameter.
-    """
-
-    def __init__(self, missing_parameter):
-        self.missing_parameter = missing_parameter
-
-    def __str__(self):
-        return f"The callable which creates a Structure is missing a '{self.missing_parameter}' parameter!"
-
-
-class SearchSpaceBuilderMissingDefaultParameter(NaProblemError):
-    """Raised when a parameter of a search space builder is missing a default value."""
-
-    def __init__(self, parameter):
-        self.parameter = parameter
-
-    def __str__(self):
-        return f"The parameter {self.parameter} must have a default value!"
-
-
-class ProblemPreprocessingIsNotCallable(NaProblemError):
-    """Raised when the preprocessing parameter is not callable."""
-
-    def __init__(self, parameter):
-        self.parameter = parameter
-
-    def __str__(self):
-        return f"The parameter {self.parameter} must be a callable."
-
-
-class ProblemLoadDataIsNotCallable(NaProblemError):
-    """Raised when the load_data parameter is not callable."""
-
-    def __init__(self, parameter):
-        self.parameter = parameter
-
-    def __str__(self):
-        return f"The parameter {self.parameter} must be a callable."
-
-
-class WrongProblemObjective(NaProblemError):
-    """Raised when the objective parameter is neither a callable nor a string."""
-
-    def __init__(self, objective, possible_names=None):
-        self.objective = objective
-        self.possible_names = possible_names
-
-    def __str__(self):
-        output = f"The objective: {str(self.objective)} is not valid."
-        if self.possible_names is not None:
-            output += f" Possible objectives are: {self.possible_names}"
-        return output
diff --git a/src/deephyper/core/parser.py b/src/deephyper/core/parser.py
index 04d0d0d8..9f848496 100644
--- a/src/deephyper/core/parser.py
+++ b/src/deephyper/core/parser.py
@@ -1,3 +1,5 @@
+"""Module containing code to build automatically build the CL parser."""
+
 import argparse
 import inspect
 from inspect import signature
@@ -11,6 +13,8 @@ def add_arguments_from_signature(parser, obj, prefix="", exclude=[]):
     Args:
         parser (ArgumentParser)): the argument parser to which we want to add arguments.
         obj (type): the class from which we want to extract default parameters for the constructor.
+        prefix (str, Optional): prefix to add to created parser arguments.
+        exclude (list, Optional): list of arguments to be excluded.
     """
     sig = signature(obj)
     prefix = f"{prefix}-" if len(prefix) > 0 else ""
@@ -45,7 +49,9 @@ def add_arguments_from_signature(parser, obj, prefix="", exclude=[]):
 
 
 def str2bool(v):
-    """:meta private:
+    """Convert input string values to boolean.
+
+    :meta private:
     """
     if isinstance(v, bool):
         return v
diff --git a/src/deephyper/core/utils/__init__.py b/src/deephyper/core/utils/__init__.py
index d81230d7..dc3eb64e 100644
--- a/src/deephyper/core/utils/__init__.py
+++ b/src/deephyper/core/utils/__init__.py
@@ -1,3 +1,5 @@
+"""Set of utilities."""
+
 from ._import import load_attr
 from ._timeout import terminate_on_timeout
 from ._capture_std import CaptureSTD
diff --git a/src/deephyper/core/utils/_introspection.py b/src/deephyper/core/utils/_introspection.py
index 6a93d46d..ba443d7d 100644
--- a/src/deephyper/core/utils/_introspection.py
+++ b/src/deephyper/core/utils/_introspection.py
@@ -3,7 +3,7 @@
 
 
 def _get_init_param_names(cls):
-    """Get parameter names for the estimator"""
+    """Get parameter names for the estimator."""
     # fetch the constructor
     init = cls.__init__
     if init is object.__init__:
diff --git a/src/deephyper/core/utils/joblib_utils.py b/src/deephyper/core/utils/joblib_utils.py
index 3c83c37d..634de51c 100644
--- a/src/deephyper/core/utils/joblib_utils.py
+++ b/src/deephyper/core/utils/joblib_utils.py
@@ -1,4 +1,6 @@
-"""Necessary to pass the global configuration to the joblib workers until
+"""Utility for JobLib.
+
+Necessary to pass the global configuration to the joblib workers until
 https://github.com/joblib/joblib/issues/1071 is fixed.
 """
 
@@ -7,9 +9,9 @@
 
 # if sklearn >= 1.2.2 then use Parallel/delayed from there
 # otherwise use from joblib
-if isinstance(sklearn.__version__, str) and parse_version(
-    sklearn.__version__
-) >= parse_version("1.2.2"):
+if isinstance(sklearn.__version__, str) and parse_version(sklearn.__version__) >= parse_version(
+    "1.2.2"
+):
     from sklearn.utils.parallel import Parallel, delayed  # noqa: F401
 else:
     from joblib import Parallel, delayed  # noqa: F401
diff --git a/src/deephyper/core/warnings.py b/src/deephyper/core/warnings.py
index 5d912269..ed41320e 100644
--- a/src/deephyper/core/warnings.py
+++ b/src/deephyper/core/warnings.py
@@ -1,3 +1,5 @@
+"""Submodule containing code related to warnings."""
+
 import warnings
 
 # Used to enforce the warnings to be displayed
@@ -5,4 +7,5 @@
 
 
 def deprecated_api(msg: str) -> None:
+    """Utility function to throw a deprecation warning for some section of the API."""
     warnings.warn(msg, DeprecationWarning, stacklevel=2)
diff --git a/src/deephyper/ensemble/__init__.py b/src/deephyper/ensemble/__init__.py
index 2ebdaf24..e6549bd4 100644
--- a/src/deephyper/ensemble/__init__.py
+++ b/src/deephyper/ensemble/__init__.py
@@ -1,6 +1,7 @@
 """Subpackage to build ensemble of predicting models.
 
-Ensembling can be useful to minimize the loss if the learner has variance (i.e., its performance metric varies when trained multiple times) or to estimate the uncertainty of the predictions.
+Ensembling can be useful to minimize the loss if the learner has variance (i.e., its performance
+metric varies when trained multiple times) or to estimate the uncertainty of the predictions.
 """
 
 from deephyper.ensemble._ensemble import EnsemblePredictor
diff --git a/src/deephyper/ensemble/_ensemble.py b/src/deephyper/ensemble/_ensemble.py
index adc5528d..e26a243a 100644
--- a/src/deephyper/ensemble/_ensemble.py
+++ b/src/deephyper/ensemble/_ensemble.py
@@ -25,13 +25,22 @@ class EnsemblePredictor(Predictor):
     """A predictor that is itself an ensemble of multiple predictors.
 
     Args:
-        predictors (Sequence[Predictor | PredictorLoader]): the list of predictors to put in the ensemble. The sequence can be composed of ``Predictor`` (i.e., the model is already loaded in memory) or ``PredictorLoader`` to perform the loading remotely and in parallel. In the later case, the ``.load()`` function is called for each inference.
+        predictors (Sequence[Predictor | PredictorLoader]): the list of predictors to put in the
+            ensemble. The sequence can be composed of ``Predictor`` (i.e., the model is already
+            loaded in memory) or ``PredictorLoader`` to perform the loading remotely and in
+            parallel. In the later case, the ``.load()`` function is called for each inference.
 
-        aggregator (Aggregator): the aggregation function to fuse the predictions of the predictors into one prediction.
+        aggregator (Aggregator): the aggregation function to fuse the predictions of the predictors
+            into one prediction.
 
-        weights (Sequence[float], optional): the weights of the predictors in the aggregation. Defaults to ``None``.
+        weights (Sequence[float], optional): the weights of the predictors in the aggregation.
+            Defaults to ``None``.
 
-        evaluator (str | Dict, optional): The parallel strategy to compute predictions from the list of predictions. If it is a ``str`` it must be a possible ``method`` of ``Evaluator.create(..., method=...)``. If it is a ``dict`` it must have two keys ``method`` and ``method_kwargs`` such as ``Evaluator.create(...)``. Defaults to ``None`` which is equivalent to ``evaluator="serial"`` for serial evaluations.
+        evaluator (str | Dict, optional): The parallel strategy to compute predictions from the
+            list of predictions. If it is a ``str`` it must be a possible ``method`` of
+            ``Evaluator.create(..., method=...)``. If it is a ``dict`` it must have two keys
+            ``method`` and ``method_kwargs`` such as ``Evaluator.create(...)``. Defaults to
+            ``None`` which is equivalent to ``evaluator="serial"`` for serial evaluations.
 
     Raises:
         ValueError: when the type of the ``evaluator`` argument is not ``str`` or ``dict``.
@@ -59,9 +68,7 @@ def __init__(
             self.evaluator_method = evaluator.get("method", "serial")
             self.evaluator_method_kwargs = evaluator.get("method_kwargs", {})
         else:
-            raise ValueError(
-                f"evaluator must be either None or str or dict, got {type(evaluator)}"
-            )
+            raise ValueError(f"evaluator must be either None or str or dict, got {type(evaluator)}")
         self.init_evaluator()
 
     def init_evaluator(self):
@@ -105,7 +112,8 @@ def predictions_from_predictors(
             predictors (Sequence[Predictor]): the list of predictors to compute the predictions.
 
         Returns:
-            List[np.ndarray]: the sequence of predictions in the same order that the list of predictors.
+            List[np.ndarray]: the sequence of predictions in the same order that the list of
+            predictors.
         """
         self._evaluator.submit(
             [
diff --git a/src/deephyper/ensemble/aggregator/_aggregator.py b/src/deephyper/ensemble/aggregator/_aggregator.py
index 36373391..a80156d1 100644
--- a/src/deephyper/ensemble/aggregator/_aggregator.py
+++ b/src/deephyper/ensemble/aggregator/_aggregator.py
@@ -4,14 +4,15 @@
 
 
 class Aggregator(abc.ABC):
-    """Base class that represents an aggregation function for the predictions of a set of predictors."""
+    """Base class that represents an aggregation function of a set of predictors."""
 
     @abc.abstractmethod
     def aggregate(self, y: List, weights: List = None):
         """Aggregate the predictions from different predictors.
 
         Args:
-            y (List): List of predictions from different models. It should be of shape ``(n_predictors, ...)``.
+            y (List): List of predictions from different models. It should be of shape
+                ``(n_predictors, ...)``.
 
             weights (list, optional): Weights of the predictors. Default is ``None``.
 
diff --git a/src/deephyper/ensemble/aggregator/_mean.py b/src/deephyper/ensemble/aggregator/_mean.py
index 14538650..cb6f63f7 100644
--- a/src/deephyper/ensemble/aggregator/_mean.py
+++ b/src/deephyper/ensemble/aggregator/_mean.py
@@ -18,7 +18,8 @@ class MeanAggregator(Aggregator):
           - ✅
 
     Args:
-        with_uncertainty (bool, optional): a boolean that sets if the uncertainty should be returned when calling the aggregator. Defaults to ``False``.
+        with_uncertainty (bool, optional): a boolean that sets if the uncertainty should be returned
+         when calling the aggregator. Defaults to ``False``.
     """
 
     def __init__(self, with_uncertainty: bool = False):
@@ -45,15 +46,11 @@ def aggregate(
             ValueError: If `weights` length does not match the number of predictors in `y`.
         """
         if weights is not None and len(weights) != len(y):
-            raise ValueError(
-                "The length of `weights` must match the number of predictors in `y`."
-            )
+            raise ValueError("The length of `weights` must match the number of predictors in `y`.")
 
         # Ensure `y` is a valid list of arrays
         if not all(isinstance(pred, (np.ndarray, np.ma.MaskedArray)) for pred in y):
-            raise TypeError(
-                "All elements of `y` must be numpy.ndarray or numpy.ma.MaskedArray."
-            )
+            raise TypeError("All elements of `y` must be numpy.ndarray or numpy.ma.MaskedArray.")
 
         self._np = np
         if all(isinstance(pred, np.ma.MaskedArray) for pred in y):
@@ -66,8 +63,6 @@ def aggregate(
         if not self.with_uncertainty:
             return avg
 
-        uncertainty = self._np.average(
-            self._np.square(stacked_y - avg), axis=0, weights=weights
-        )
+        uncertainty = self._np.average(self._np.square(stacked_y - avg), axis=0, weights=weights)
 
         return {"loc": avg, "uncertainty": uncertainty}
diff --git a/src/deephyper/ensemble/aggregator/_mixed_categorical.py b/src/deephyper/ensemble/aggregator/_mixed_categorical.py
index 6b2c63bc..a245c61e 100644
--- a/src/deephyper/ensemble/aggregator/_mixed_categorical.py
+++ b/src/deephyper/ensemble/aggregator/_mixed_categorical.py
@@ -24,7 +24,8 @@ class MixedCategoricalAggregator(Aggregator):
             - ``"entropy"``: Uncertainty is computed as the entropy of the categorical distribution.
 
         decomposed_uncertainty (bool, optional):
-            If ``True``, decomposes uncertainty into aleatoric and epistemic components. Default is ``False``.
+            If ``True``, decomposes uncertainty into aleatoric and epistemic components. Default is
+            ``False``.
     """
 
     VALID_UNCERTAINTY_METHODS = {"confidence", "entropy"}
@@ -55,7 +56,8 @@ def aggregate(
 
         Returns:
             Dict[str, Union[np.ndarray, float]]: Aggregated results, including:
-                - ``"loc"``: Aggregated categorical probabilities of shape ``(n_samples, ..., n_classes)``.
+                - ``"loc"``: Aggregated categorical probabilities of shape ``(n_samples, ...,
+                 n_classes)``.
                 - ``"uncertainty"``: (Optional) Total uncertainty.
                 - ``"uncertainty_aleatoric"``: (Optional) Aleatoric uncertainty.
                 - ``"uncertainty_epistemic"``: (Optional) Epistemic uncertainty.
@@ -67,9 +69,7 @@ def aggregate(
             raise TypeError("Input `y` must be a list of numpy.ndarray.")
 
         if weights is not None and len(weights) != len(y):
-            raise ValueError(
-                "The length of `weights` must match the number of predictors in `y`."
-            )
+            raise ValueError("The length of `weights` must match the number of predictors in `y`.")
 
         self._np = np
         if all(isinstance(pred, np.ma.MaskedArray) for pred in y):
@@ -85,13 +85,9 @@ def aggregate(
 
         # Compute uncertainty
         if self.uncertainty_method == "confidence":
-            self._compute_confidence_uncertainty(
-                agg, y_proba_models, y_proba_ensemble, weights
-            )
+            self._compute_confidence_uncertainty(agg, y_proba_models, y_proba_ensemble, weights)
         elif self.uncertainty_method == "entropy":
-            self._compute_entropy_uncertainty(
-                agg, y_proba_models, y_proba_ensemble, weights
-            )
+            self._compute_entropy_uncertainty(agg, y_proba_models, y_proba_ensemble, weights)
 
         return agg
 
@@ -111,9 +107,7 @@ def _compute_confidence_uncertainty(
             uncertainty_aleatoric = self._np.average(
                 1 - self._np.max(y_proba_models, axis=-1), weights=weights, axis=0
             )
-            uncertainty_epistemic = self._np.maximum(
-                0, uncertainty - uncertainty_aleatoric
-            )
+            uncertainty_epistemic = self._np.maximum(0, uncertainty - uncertainty_aleatoric)
 
             agg["uncertainty_aleatoric"] = uncertainty_aleatoric
             agg["uncertainty_epistemic"] = uncertainty_epistemic
diff --git a/src/deephyper/ensemble/aggregator/_mixed_normal.py b/src/deephyper/ensemble/aggregator/_mixed_normal.py
index f759fee7..f9fa2618 100644
--- a/src/deephyper/ensemble/aggregator/_mixed_normal.py
+++ b/src/deephyper/ensemble/aggregator/_mixed_normal.py
@@ -19,7 +19,8 @@ class MixedNormalAggregator(Aggregator):
           - ❌
 
     Args:
-        decomposed_scale (bool, optional): If ``True``, the scale of the mixture distribution is decomposed into aleatoric and epistemic components. Default is ``False``.
+        decomposed_scale (bool, optional): If ``True``, the scale of the mixture distribution is
+        decomposed into aleatoric and epistemic components. Default is ``False``.
     """
 
     def __init__(self, decomposed_scale: bool = False):
@@ -29,12 +30,16 @@ def aggregate(self, y: List, weights: List = None):
         """Aggregate the predictions using the mean.
 
         Args:
-            y (Dict): Predictions dictionary with keys ``loc`` for the mean and ``scale`` for the standard-deviation of each normal distribution, each with shape ``(n_predictors, n_samples, ..., n_outputs)``.
+            y (Dict): Predictions dictionary with keys ``loc`` for the mean and ``scale`` for the
+            standard-deviation of each normal distribution, each with shape ``(n_predictors,
+            n_samples, ..., n_outputs)``.
 
             weights (list, optional): Weights of the predictors. Default is ``None``.
 
         Returns:
-            Dict: Aggregated predictions with keys ``loc`` for the mean and ``scale`` for the standard-deviation of the mixture distribution, each with shape ``(n_samples, ..., n_outputs)``.
+            Dict: Aggregated predictions with keys ``loc`` for the mean and ``scale`` for the
+            standard-deviation of the mixture distribution, each with shape ``(n_samples, ...,
+            n_outputs)``.
         """
         y_dict = {k: list() for k in y[0].keys()}
         for yi in y:
@@ -56,10 +61,10 @@ def aggregate(self, y: List, weights: List = None):
             agg["scale"] = mean_scale
 
         else:
-            # Here we assume that the mixture distribution is a normal distribution with a scale that is the sum of the aleatoric and epistemic scales. This is a significant approximation that could be improved by returning the true GMM.
-            scale_aleatoric = np.sqrt(
-                average(np.square(scale), weights=weights, axis=0)
-            )
+            # Here we assume that the mixture distribution is a normal distribution with a scale
+            # that is the sum of the aleatoric and epistemic scales. This is a significant
+            # approximation that could be improved by returning the true GMM.
+            scale_aleatoric = np.sqrt(average(np.square(scale), weights=weights, axis=0))
             scale_epistemic = np.sqrt(np.square(np.std(loc, axis=0)))
             agg["scale_aleatoric"] = scale_aleatoric
             agg["scale_epistemic"] = scale_epistemic
diff --git a/src/deephyper/ensemble/aggregator/_mode.py b/src/deephyper/ensemble/aggregator/_mode.py
index 9f3411e9..22263226 100644
--- a/src/deephyper/ensemble/aggregator/_mode.py
+++ b/src/deephyper/ensemble/aggregator/_mode.py
@@ -6,7 +6,7 @@
 
 
 class ModeAggregator(Aggregator):
-    """Aggregate predictions using the mode of categorical distributions from each predictor in the ensemble.
+    """Aggregate predictions using the mode of categorical distributions from predictors.
 
     .. list-table::
         :widths: 25 25
@@ -17,11 +17,13 @@ class ModeAggregator(Aggregator):
         * - ✅
           - ✅
 
-    This aggregator is useful when the ensemble is composed of predictors that output categorical distributions.
-    The mode of the ensemble is the mode of the modes of the predictors, minimizing the 0-1 loss.
+    This aggregator is useful when the ensemble is composed of predictors that output categorical
+    distributions. The mode of the ensemble is the mode of the modes of the predictors, minimizing
+    the 0-1 loss.
 
     Args:
-        with_uncertainty (bool, optional): a boolean that sets if the uncertainty should be returned when calling the aggregator. Defaults to ``False``.
+        with_uncertainty (bool, optional): a boolean that sets if the uncertainty should be
+        returned when calling the aggregator. Defaults to ``False``.
     """
 
     def __init__(self, with_uncertainty: bool = False):
@@ -43,8 +45,9 @@ def aggregate(
             weights (Optional[List[float]]): Weights for the predictors. Default is ``None``.
 
         Returns:
-            Union[Union[np.ndarray, np.ma.MaskedArray], Dict[str, Union[np.ndarray, np.ma.MaskedArray]]]: Aggregated results, as an array
-             corresponding to the mode when ``with_uncertainty=False`` and as a dict otherwise including:
+            Union[Union[np.ndarray, np.ma.MaskedArray], Dict[str, Union[np.ndarray,
+            np.ma.MaskedArray]]]: Aggregated results, as an array corresponding to the mode when
+            ``with_uncertainty=False`` and as a dict otherwise including:
                 - ``"loc"``: Aggregated mode of shape ``(n_samples, ...)``.
                 - ``"uncertainty"``: Uncertainty values of shape ``(n_samples, ...)`` `.
 
@@ -54,9 +57,7 @@ def aggregate(
         if not isinstance(y, list) or not all(
             isinstance(arr, (np.ndarray, np.ma.MaskedArray)) for arr in y
         ):
-            raise TypeError(
-                "Input `y` must be a list of numpy.ndarray or numpy.ma.MaskedArray."
-            )
+            raise TypeError("Input `y` must be a list of numpy.ndarray or numpy.ma.MaskedArray.")
 
         self._np = np
         is_masked = False
@@ -72,9 +73,7 @@ def aggregate(
         # Mode of the ensemble (n_samples, ...)
         y_mode_models = self._np.argmax(y_proba_models, axis=-1)
 
-        weighted_counts = self._np.zeros_like(y_proba_models, dtype=np.float64).sum(
-            axis=0
-        )
+        weighted_counts = self._np.zeros_like(y_proba_models, dtype=np.float64).sum(axis=0)
         eye_arr = np.eye(num_classes, dtype=np.float64)
         for i in range(n_predictors):
             if weights is None:
diff --git a/src/deephyper/ensemble/aggregator/utils.py b/src/deephyper/ensemble/aggregator/utils.py
index 300e75dc..91a197fc 100644
--- a/src/deephyper/ensemble/aggregator/utils.py
+++ b/src/deephyper/ensemble/aggregator/utils.py
@@ -1,3 +1,5 @@
+"""Module of utility functions for ensemble aggregators."""
+
 import numpy as np
 from typing import Optional, Union, Sequence
 
diff --git a/src/deephyper/ensemble/loss.py b/src/deephyper/ensemble/loss.py
index 4b60f73d..695dd101 100644
--- a/src/deephyper/ensemble/loss.py
+++ b/src/deephyper/ensemble/loss.py
@@ -18,9 +18,7 @@ def _check_is_array(y: Any, y_name: str) -> None:
         ValueError: when ``y`` is not an np.ndarray.
     """
     if not isinstance(y, np.ndarray):
-        raise ValueError(
-            f"{y_name} should be of type np.ndarray but is of type {type(y)}"
-        )
+        raise ValueError(f"{y_name} should be of type np.ndarray but is of type {type(y)}")
 
 
 def _check_is_array_or_dict(y: Any, y_name: str):
@@ -34,13 +32,14 @@ def _check_is_array_or_dict(y: Any, y_name: str):
         ValueError: when ``y`` is not an np.ndarray and not a dict.
     """
     if not isinstance(y, np.ndarray) and not isinstance(y, dict):
-        raise ValueError(
-            f"{y_name} should be of type np.ndarray or dict but is of type {type(y)}"
-        )
+        raise ValueError(f"{y_name} should be of type np.ndarray or dict but is of type {type(y)}")
 
 
 class Loss(abc.ABC):
-    """Base class that represents the loss function of an ensemble. Losses represent functions that should be minimized."""
+    """Base class that represents the loss function of an ensemble.
+
+    Losses represent functions that should be minimized.
+    """
 
     @abc.abstractmethod
     def __call__(
@@ -67,7 +66,8 @@ def __call__(
 
         Args:
             y_true (np.ndarray): the true target values.
-            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``.
+            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a
+            _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``.
 
         Returns:
             np.ndarray: the loss value with first dimension ``n_samples``.
@@ -75,8 +75,8 @@ def __call__(
         _check_is_array(y_true, "y_true")
         _check_is_array_or_dict(y_pred, "y_pred")
 
-        # If the prediction is a dictionary, we assume that the prediction are distribution parameters
-        # and we take the mean estimate that should correspond to the 'loc' key
+        # If the prediction is a dictionary, we assume that the prediction are distribution
+        # parameters and we take the mean estimate that should correspond to the 'loc' key
         if isinstance(y_pred, dict):
             if "loc" not in y_pred:
                 raise ValueError("y_pred should contain a 'loc' key when it is a dict")
@@ -87,7 +87,10 @@ def __call__(
 
 
 class AbsoluteError(Loss):
-    """The usual absolute loss ``(y_true - y_pred)**2`` used to estimate the median of ``P(Y|X=x)``."""
+    """The usual absolute loss ``(y_true - y_pred)**2``.
+
+    It is used to estimate the median of ``P(Y|X=x)``.
+    """
 
     def __call__(
         self, y_true: np.ndarray, y_pred: np.ndarray | Dict[str, np.ndarray]
@@ -96,7 +99,8 @@ def __call__(
 
         Args:
             y_true (np.ndarray): the true target values.
-            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``.
+            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a
+            _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``.
 
         Returns:
             np.ndarray: the loss value with first dimension ``n_samples``.
@@ -104,8 +108,8 @@ def __call__(
         _check_is_array(y_true, "y_true")
         _check_is_array_or_dict(y_pred, "y_pred")
 
-        # If the prediction is a dictionary, we assume that the prediction are distribution parameters
-        # and we take the median estimate that should correspond to the 'loc' key
+        # If the prediction is a dictionary, we assume that the prediction are distribution
+        # parameters and we take the median estimate that should correspond to the 'loc' key
         if isinstance(y_pred, dict):
             if "loc" not in y_pred:
                 raise ValueError("y_pred should contain a 'loc' key when it is a dict.")
@@ -116,7 +120,11 @@ def __call__(
 
 
 class NormalNegLogLikelihood(Loss):
-    """The negative log-likelihood of observed data ``y_true`` given predicted parameters of a normal distribution ``y_pred["loc"], y_pred["scale"]``."""
+    """The negative log-likelihood of a normal distribution.
+
+    Given observed data ``y_true`` and the predicted parameters of the normal distribution
+    ``y_pred["loc"], y_pred["scale"]``.
+    """
 
     def __init__(self):
         self.dist = ss.norm
@@ -128,7 +136,8 @@ def __call__(
 
         Args:
             y_true (np.ndarray): the true target values.
-            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``.
+            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a
+                _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``.
 
         Returns:
             np.ndarray: the loss value with first dimension ``n_samples``.
@@ -139,7 +148,8 @@ def __call__(
         if isinstance(y_pred, np.ndarray):
             if np.shape(y_pred)[0] != 2:
                 raise ValueError(
-                    f"The first dimension of y_pred should be equal to 2 but it is {np.shape(y_pred)[0]}."
+                    "The first dimension of y_pred should be equal to 2 but it is "
+                    f"{np.shape(y_pred)[0]}."
                 )
 
             if np.shape(y_pred)[1:] != np.shape(y_true):
@@ -155,9 +165,7 @@ def __call__(
                 raise ValueError("y_pred should contain a 'loc' key when it is a dict.")
 
             if "scale" not in y_pred:
-                raise ValueError(
-                    "y_pred should contain a 'scale' key when it is a dict."
-                )
+                raise ValueError("y_pred should contain a 'scale' key when it is a dict.")
 
             y_pred_loc = y_pred["loc"]
             y_pred_scale = y_pred["scale"]
@@ -166,10 +174,13 @@ def __call__(
 
 
 class ZeroOneLoss(Loss):
-    """Zero-One loss for classification (a.k.a, error rate) which has value ``1`` if the prediction is wrong and ``0`` if it is correct.
+    """Zero-One loss for classification (a.k.a, error rate).
+
+    It has value ``1`` if the prediction is wrong and ``0`` if it is correct.
 
     Args:
-        predict_proba (bool, optional): A boolean indicating if ``y_pred`` contains predicted categorical probabilities. Defaults to ``False`` for label predictions.
+        predict_proba (bool, optional): A boolean indicating if ``y_pred`` contains predicted
+            categorical probabilities. Defaults to ``False`` for label predictions.
     """
 
     def __init__(self, predict_proba: bool = False):
@@ -181,8 +192,10 @@ def __call__(
         """Compute the zero-one loss.
 
         Args:
-            y_true (np.ndarray): the true target values. It should be an array of integers representing the true class labels.
-            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``.
+            y_true (np.ndarray): the true target values. It should be an array of integers
+                representing the true class labels.
+            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a
+                _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``.
 
         Returns:
             np.ndarray: the loss value with first dimension ``n_samples``.
@@ -214,8 +227,11 @@ def __call__(
         """Compute the categorical crossentropy loss.
 
         Args:
-            y_true (np.ndarray): the true target values. It should be an array of labels or one-hot encoded labels representing the true class labels.
-            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``. It is an array of predicted categorical probabilities.
+            y_true (np.ndarray): the true target values. It should be an array of labels or one-hot
+                encoded labels representing the true class labels.
+            y_pred (np.ndarray or Dict[str, np.ndarray]): the predicted target values. If it is a
+                _Dict[str, np.ndarray]_ then it should contain a key ``"loc"``. It is an array of
+                predicted categorical probabilities.
 
         Returns:
             np.ndarray: the loss value with first dimension ``n_samples``.
diff --git a/src/deephyper/ensemble/selector/__init__.py b/src/deephyper/ensemble/selector/__init__.py
index 17e228aa..cf1d3214 100644
--- a/src/deephyper/ensemble/selector/__init__.py
+++ b/src/deephyper/ensemble/selector/__init__.py
@@ -1,4 +1,8 @@
-"""Subpackage providing methods that select a subset of predictors from a set of available predictors in order to build an ensemble."""
+"""Subpackage providing ensemble selection algorithms.
+
+It contains methods that select a subset of predictors from a set of available predictors
+in order to build an ensemble.
+"""
 
 from deephyper.ensemble.selector._greedy import GreedySelector
 from deephyper.ensemble.selector._online_selector import OnlineSelector
diff --git a/src/deephyper/ensemble/selector/_greedy.py b/src/deephyper/ensemble/selector/_greedy.py
index e50653b2..b218d6e3 100644
--- a/src/deephyper/ensemble/selector/_greedy.py
+++ b/src/deephyper/ensemble/selector/_greedy.py
@@ -14,24 +14,24 @@ class GreedySelector(Selector):
     the loss when aggregated together.
 
     Args:
-        loss_func (Callable or Loss):
-            a loss function that takes two arguments: the true target values and the predicted target values.
-        aggregator (Aggregator):
-            The aggregator to use to combine the predictions of the selected predictors.
-        k (int, optional):
-            The number of unique predictors to select for the ensemble. Defaults to ``5``.
-        k_init (int, optional):
-            Regularization parameter for greedy selection. It is the number of predictors to select in the initialization step. Defaults to ``1``.
-        max_it (int, optional):
-            Maximum number of iterations which also corresponds to the number of non-unique predictors added to the ensemble. Defaults to ``-1``.
-        eps_tol (float, optional):
-            Tolerance for the stopping criterion. Defaults to ``1e-3``.
-        with_replacement (bool, optional):
-            Performs greedy selection with replacement of models already selected. Defaults to ``True``.
-        early_stopping (bool, optional):
-            Stops the ensemble selection as soon as the loss stops improving. Defaults to ``True``.
-        bagging (bool, optional):
-            Performanced boostrap resampling of available predictors at each iteration. This can be particularly useful when the dataset used for selection is small. Defaults to ``False``.
+        loss_func (Callable or Loss): a loss function that takes two arguments: the true target
+            values and the predicted target values.
+        aggregator (Aggregator): The aggregator to use to combine the predictions of the selected
+            predictors.
+        k (int, optional): The number of unique predictors to select for the ensemble. Defaults to
+            ``5``.
+        k_init (int, optional): Regularization parameter for greedy selection. It is the number of
+            predictors to select in the initialization step. Defaults to ``1``.
+        max_it (int, optional): Maximum number of iterations which also corresponds to the number
+            of non-unique predictors added to the ensemble. Defaults to ``-1``.
+        eps_tol (float, optional): Tolerance for the stopping criterion. Defaults to ``1e-3``.
+        with_replacement (bool, optional): Performs greedy selection with replacement of models
+            already selected. Defaults to ``True``.
+        early_stopping (bool, optional): Stops the ensemble selection as soon as the loss stops
+            improving. Defaults to ``True``.
+        bagging (bool, optional): Performs boostrap resampling of available predictors at each
+            iteration. This can be particularly useful when the dataset used for selection is
+            small. Defaults to ``False``.
         verbose (bool, optional):
             Turns on the verbose mode. Defaults to ``False``.
     """
@@ -70,9 +70,7 @@ def select(self, y, y_predictors) -> Sequence[int]:
         losses = [self._evaluate(y, y_pred_i) for y_pred_i in y_predictors]
         selected_indices = np.argsort(losses)[: self.k_init].tolist()
         selected_indices_weights = [1 / self.k_init] * self.k_init
-        loss_min = self._evaluate(
-            y, self._aggregate([y_predictors[i] for i in selected_indices])
-        )
+        loss_min = self._evaluate(y, self._aggregate([y_predictors[i] for i in selected_indices]))
         n_predictors = len(y_predictors)
         bagged_predictors = None
 
@@ -81,16 +79,12 @@ def select(self, y, y_predictors) -> Sequence[int]:
 
         # Greedy steps
         it = 0
-        while (self.max_it < 0 or it < self.max_it) and len(
-            np.unique(selected_indices)
-        ) < self.k:
+        while (self.max_it < 0 or it < self.max_it) and len(np.unique(selected_indices)) < self.k:
             losses = []
 
             if self.bagging:
                 bagged_predictors = np.unique(
-                    self.random_state.randint(
-                        low=0, high=n_predictors, size=n_predictors
-                    )
+                    self.random_state.randint(low=0, high=n_predictors, size=n_predictors)
                 )
 
             for i in range(n_predictors):
@@ -130,16 +124,13 @@ def select(self, y, y_predictors) -> Sequence[int]:
             if self.verbose:
                 print(f"Step {it}, ensemble is {selected_indices}")
 
-        selected_indices, selected_indices_weights = np.unique(
-            selected_indices, return_counts=True
-        )
-        selected_indices_weights = selected_indices_weights / np.sum(
-            selected_indices_weights
-        )
+        selected_indices, selected_indices_weights = np.unique(selected_indices, return_counts=True)
+        selected_indices_weights = selected_indices_weights / np.sum(selected_indices_weights)
 
         if self.verbose:
             print(
-                f"After {it} steps, the final ensemble is {selected_indices} with weights {selected_indices_weights}"
+                f"After {it} steps, the final ensemble is {selected_indices} with "
+                f"weights {selected_indices_weights}"
             )
 
         return selected_indices.tolist(), selected_indices_weights.tolist()
diff --git a/src/deephyper/ensemble/selector/_online_selector.py b/src/deephyper/ensemble/selector/_online_selector.py
index 589cd49a..52eb9d55 100644
--- a/src/deephyper/ensemble/selector/_online_selector.py
+++ b/src/deephyper/ensemble/selector/_online_selector.py
@@ -101,9 +101,9 @@ def on_done(self, job):
         y_pred_mask[job.output["online_selector"]["y_pred_idx"]] = 0
 
         y_pred = np.zeros_like(self.y, dtype=float)
-        y_pred[job.output["online_selector"]["y_pred_idx"]] = job.output[
-            "online_selector"
-        ]["y_pred"]
+        y_pred[job.output["online_selector"]["y_pred_idx"]] = job.output["online_selector"][
+            "y_pred"
+        ]
 
         m_y_pred = np.ma.masked_array(y_pred, mask=y_pred_mask)
         self.y_predictors.append(m_y_pred)
@@ -120,19 +120,20 @@ def on_done_other(self, job):
     @property
     def selected_predictors_job_ids(self) -> List[str]:
         """List of ``job.id`` corresponding to the selected set of predictors."""
-        return [
-            self.y_predictors_job_ids[idx] for idx in self.selected_predictors_indexes
-        ]
+        return [self.y_predictors_job_ids[idx] for idx in self.selected_predictors_indexes]
 
     @property
     def ensemble(self):
-        """The ``ensemble`` with adapted ``.predictors`` and ``.weights`` from the latest selection."""
+        """The ensemble with its weights.
+
+        It will provide the ``ensemble`` with adapted ``.predictors`` and ``.weights`` from the
+        latest selection.
+        """
         # TODO: the following deepcopy can create issues with the evaluator used by the ensemble
         # ensemble = copy.deepcopy(self._ensemble)
         ensemble = copy.copy(self._ensemble)
         ensemble.predictors = [
-            self._load_predictor_func(job_id)
-            for job_id in self.selected_predictors_job_ids
+            self._load_predictor_func(job_id) for job_id in self.selected_predictors_job_ids
         ]
         ensemble.weights = self.selected_predictors_weights
         return ensemble
diff --git a/src/deephyper/ensemble/selector/_selector.py b/src/deephyper/ensemble/selector/_selector.py
index dc6ad3f3..d8f97738 100644
--- a/src/deephyper/ensemble/selector/_selector.py
+++ b/src/deephyper/ensemble/selector/_selector.py
@@ -7,10 +7,14 @@
 
 
 class Selector(abc.ABC):
-    """Base class that represents an algorithm that select a subset of predictors from a set of available predictors in order to build an ensemble.
+    """Base class that represents an selection algorithm.
+
+    It selects a subset of predictors from a set of available predictors in order to build
+    an ensemble.
 
     Args:
-        loss_func (Callable or Loss): a loss function that takes two arguments: the true target values and the predicted target values.
+        loss_func (Callable or Loss): a loss function that takes two arguments: the true target
+            values and the predicted target values.
     """
 
     def __init__(self, loss_func: Callable | Loss):
@@ -22,7 +26,9 @@ def select(self, y, y_predictors) -> Tuple[Sequence[int], Sequence[float]]:
 
         Args:
             y (np.ndarray): the true target values.
-            y_predictors (_type_): a sequence of predictions from available predictors. It should be a list of length ``n_predictors`` with each element being the prediction of a predictor.
+            y_predictors (_type_): a sequence of predictions from available predictors. It should
+                be a list of length ``n_predictors`` with each element being the prediction of a
+                predictor.
 
         Returns:
             Sequence[int]: the sequence of selected predictors.
diff --git a/src/deephyper/ensemble/selector/_topk.py b/src/deephyper/ensemble/selector/_topk.py
index 793d7a18..2af28a68 100644
--- a/src/deephyper/ensemble/selector/_topk.py
+++ b/src/deephyper/ensemble/selector/_topk.py
@@ -7,10 +7,13 @@
 
 
 class TopKSelector(Selector):
-    """Selection method implementing Top-K selection. This method selects the K predictors with the lowest loss.
+    """Selection method implementing Top-K selection.
+
+    This method selects the K predictors with the lowest loss.
 
     Args:
-        loss_func (Callable or Loss): a loss function that takes two arguments: the true target values and the predicted target values.
+        loss_func (Callable or Loss): a loss function that takes two arguments: the true target
+            values and the predicted target values.
         k (int, optional): The number of predictors to select. Defaults to ``5``.
     """
 
diff --git a/src/deephyper/evaluator/__init__.py b/src/deephyper/evaluator/__init__.py
index beb42453..532233b5 100644
--- a/src/deephyper/evaluator/__init__.py
+++ b/src/deephyper/evaluator/__init__.py
@@ -1,11 +1,14 @@
-"""This evaluator subpackage provides a common interface to execute isolated
-tasks with different parallel backends and system properties. This interface
-is used by search algorithm to perform black-box optimization (the black-box
-being represented by the ``run``-function). An ``Evaluator``, when
-instanciated, is bound to a ``run``-function which takes as first argument a
-dictionnary and optionally has other keyword-arguments. The ``run``-function
-has to return a Python serializable value (under ``pickle`` protocol). In
-it's most basic form the return value is a ``float``.
+"""This evaluator subpackage.
+
+This provides a common interface to execute isolated tasks with different
+parallel backends and system properties.
+
+This interface is used by search algorithm to perform black-box optimization
+(the black-box being represented by the ``run``-function). An ``Evaluator``,
+when instanciated, is bound to a ``run``-function which takes as first
+argument a dictionnary and optionally has other keyword-arguments. The
+``run``-function has to return a Python serializable value (under ``pickle``
+protocol). In it's most basic form the return value is a ``float``.
 
 An example ``run``-function is:
 
@@ -34,10 +37,9 @@ def run(job: RunningJob) -> Union[float, str, Dict]:
     return {"objective": 42.0, "metadata": {"num_epochs_trained": 25, "num_parameters": 420000}}
     # dict with reserved keywords (when @profile decorator is used)
     return {"objective": 42.0, "metadata": {"timestamp_start": ..., "timestamp_end": ...}"
-    # tuple of float for multi-objective optimization (will appear as "objective_0" and "objective_1" in the resulting dataframe)
+    # tuple of float for multi-objective optimization, appears as "objective_0" and "objective_1"
     return 42.0, 0.42
 
-
 """
 
 from deephyper.evaluator._decorator import profile
diff --git a/src/deephyper/evaluator/_decorator.py b/src/deephyper/evaluator/_decorator.py
index 05b9f0c9..e678f136 100644
--- a/src/deephyper/evaluator/_decorator.py
+++ b/src/deephyper/evaluator/_decorator.py
@@ -9,8 +9,10 @@
 
 
 def register_inner_function_for_pickle(func):
-    """Register former decorated function under a new name to be called in subprocess within
-    the decorator.
+    """Register former decorated function under a new name.
+
+    This is to be called in subprocess within the decorator.
+
     See: https://stackoverflow.com/questions/73146709/python-process-inside-decorator
     """
     prefix = "profiled_"
@@ -22,8 +24,11 @@ def register_inner_function_for_pickle(func):
     func.__qualname__ = saved_name
 
 
-# Example from https://github.com/dabeaz/python-cookbook/blob/master/src/9/defining_a_decorator_that_takes_an_optional_argument/example.py
-def profile(
+# Example from
+# https://github.com/dabeaz/python-cookbook/blob/master/src/9/defining_a_decorator_that_takes_an_optional_argument/example.py
+
+
+def profile(  # noqa: D417
     _func=None,
     *,
     memory: bool = False,
@@ -65,10 +70,22 @@ def run(config):
             return y
 
     Args:
-        memory (bool): If ``True``, the memory usage is measured. The measured memory, in bytes, accounts for the whole process. Defaults to ``False``.
-        memory_limit (int): In bytes, if set to a positive integer, the memory usage is measured at regular intervals and the function is interrupted if the memory usage exceeds the limit. If set to ``-1``, only the peak memory is measured. If the executed function is busy outside of the Python interpretor, this mechanism will not work properly. Defaults to ``-1``.
-        memory_tracing_interval (float): In seconds, the interval at which the memory usage is measured. Defaults to ``0.1``.
-        register (bool): Register the called function to be pickalable and executed in a subprocess when the we use as decorator ``@profile``.
+        memory (bool):
+            If ``True``, the memory usage is measured. The measured memory, in
+            bytes, accounts for the whole process. Defaults to ``False``.
+        memory_limit (int):
+            In bytes, if set to a positive integer, the memory usage is
+            measured at regular intervals and the function is interrupted if
+            the memory usage exceeds the limit. If set to ``-1``, only the
+            peak memory is measured. If the executed function is busy outside
+            of the Python interpretor, this mechanism will not work properly.
+            Defaults to ``-1``.
+        memory_tracing_interval (float):
+            In seconds, the interval at which the memory usage is measured.
+            Defaults to ``0.1``.
+        register (bool):
+            Register the called function to be pickalable and executed in a
+            subprocess when the we use as decorator ``@profile``.
 
     Returns:
         function: a decorated function.
@@ -200,7 +217,7 @@ def sync_wrapper_profile(*args, **kwargs):
 
 
 def slow_down(_func=None, *, rate=1):
-    """Sleep given amount of seconds before calling the function"""
+    """Sleep given amount of seconds before calling the function."""
 
     def decorator_slow_down(func):
         @functools.wraps(func)
diff --git a/src/deephyper/evaluator/_encoder.py b/src/deephyper/evaluator/_encoder.py
index 5b9fa843..0aa4f688 100644
--- a/src/deephyper/evaluator/_encoder.py
+++ b/src/deephyper/evaluator/_encoder.py
@@ -12,8 +12,7 @@
 
 
 class Encoder(json.JSONEncoder):
-    """Enables JSON dump of numpy data, python functions.
-    """
+    """Enables JSON dump of numpy data, python functions."""
 
     def default(self, obj):
         if isinstance(obj, uuid.UUID):
@@ -58,9 +57,11 @@ def parse_subprocess_result(result):
     except AttributeError:
         error = stderr.decode("utf-8")
         raise RuntimeError(
-            f"{error}\n\n Could not collect any result from the run_function in the main process because an error happened in the subprocess."
+            f"{error}\n\n Could not collect any result from the run_function in the main process \
+            because an error happened in the subprocess."
         )
-    # Finally, parse whether the return value from the user-defined function is a scalar, a list, or a dictionary.
+    # Finally, parse whether the return value from the user-defined function
+    # is a scalar, a list, or a dictionary.
     retval = retval_bytes.replace(
         b"'", b'"'
     )  # For dictionaries, replace single quotes with double quotes!
diff --git a/src/deephyper/evaluator/_evaluator.py b/src/deephyper/evaluator/_evaluator.py
index 41356492..667783dc 100644
--- a/src/deephyper/evaluator/_evaluator.py
+++ b/src/deephyper/evaluator/_evaluator.py
@@ -30,7 +30,9 @@
 def _test_ipython_interpretor() -> bool:
     """Test if the current Python interpretor is IPython or not.
 
-    Suggested by: https://stackoverflow.com/questions/15411967/how-can-i-check-if-code-is-executed-in-the-ipython-notebook
+    Suggested by
+    https://stackoverflow.com/questions/15411967/
+    how-can-i-check-if-code-is-executed-in-the-ipython-notebook
     """
     # names of shells/modules using jupyter
     notebooks_shells = ["ZMQInteractiveShell"]
@@ -52,15 +54,27 @@ def _test_ipython_interpretor() -> bool:
 
 
 class Evaluator(abc.ABC):
-    """This ``Evaluator`` class asynchronously manages a series of Job objects to help execute given HPS or NAS tasks on various environments with differing system settings and properties.
+    """This ``Evaluator`` class asynchronously manages a series of Job objects.
+
+    It helps to execute given HPS or NAS tasks on various environments with
+    differing system settings and properties.
 
     Args:
-        run_function (callable): functions to be executed by the ``Evaluator``.
-        num_workers (int, optional): Number of parallel workers available for the ``Evaluator``. Defaults to 1.
-        callbacks (list, optional): A list of callbacks to trigger custom actions at the creation or completion of jobs. Defaults to None.
-        run_function_kwargs (dict, optional): Static keyword arguments to pass to the ``run_function`` when executed.
-        storage (Storage, optional): Storage used by the evaluator. Defaults to ``MemoryStorage``.
-        search_id (Hashable, optional): The id of the search to use in the corresponding storage. If ``None`` it will create a new search identifier when initializing the search.
+        run_function (callable):
+            Functions to be executed by the ``Evaluator``.
+        num_workers (int, optional):
+            Number of parallel workers available for the ``Evaluator``. Defaults to 1.
+        callbacks (list, optional):
+            A list of callbacks to trigger custom actions at the creation or
+            completion of jobs. Defaults to None.
+        run_function_kwargs (dict, optional):
+            Static keyword arguments to pass to the ``run_function`` when executed.
+        storage (Storage, optional):
+            Storage used by the evaluator. Defaults to ``MemoryStorage``.
+        search_id (Hashable, optional):
+            The id of the search to use in the corresponding storage. If
+            ``None`` it will create a new search identifier when initializing
+            the search.
     """
 
     FAIL_RETURN_VALUE = OBJECTIVE_VALUE_FAILURE
@@ -79,15 +93,14 @@ def __init__(
     ):
         if hasattr(run_function, "__name__") and hasattr(run_function, "__module__"):
             logging.info(
-                f"{type(self).__name__} will execute {run_function.__name__}() from module {run_function.__module__}"
+                f"{type(self).__name__} will execute {run_function.__name__}() from module "
+                f"{run_function.__module__}"
             )
         else:
             logging.info(f"{type(self).__name__} will execute {run_function}")
 
         self.run_function = run_function  # User-defined run function.
-        self.run_function_kwargs = (
-            {} if run_function_kwargs is None else run_function_kwargs
-        )
+        self.run_function_kwargs = {} if run_function_kwargs is None else run_function_kwargs
 
         # Number of parallel workers available
         self.num_workers = num_workers
@@ -97,9 +110,7 @@ def __init__(
         self._tasks_pending = []  # Temp list to hold pending tasks from asyncio.
         self.jobs_done = []  # List used to store all jobs completed by the evaluator.
         self.job_id_gathered = []  # List of jobs'id gathered by the evaluator.
-        self.timestamp = (
-            time.time()
-        )  # Recorded time of when this evaluator interface was created.
+        self.timestamp = time.time()  # Recorded time of when this evaluator interface was created.
         self.maximum_num_jobs_submitted = -1  # Maximum number of jobs to spawn.
         self._num_jobs_offset = 0
         self.loop = None  # Event loop for asyncio.
@@ -134,9 +145,7 @@ def __init__(
 
         # to avoid "RuntimeError: This event loop is already running"
         if not (Evaluator.NEST_ASYNCIO_PATCHED) and _test_ipython_interpretor():
-            warnings.warn(
-                "Applying nest-asyncio patch for IPython Shell!", category=UserWarning
-            )
+            warnings.warn("Applying nest-asyncio patch for IPython Shell!", category=UserWarning)
             import deephyper.evaluator._nest_asyncio as nest_asyncio
 
             nest_asyncio.apply()
@@ -157,8 +166,10 @@ def timeout(self):
 
     @timeout.setter
     def timeout(self, value):
-        """Set a timeout for the Evaluator. It will create task with a "time budget" and will kill the the task if this budget
-        is exhausted.
+        """Set a timeout for the Evaluator.
+
+        It will create new tasks with a "time budget" and it will cancel the
+        the task if this budget is exhausted.
         """
         self._time_timeout_set = time.time()
         self._timeout = value
@@ -200,9 +211,16 @@ def create(run_function, method="serial", method_kwargs={}):
         """Create evaluator with a specific backend and configuration.
 
         Args:
-            run_function (function): the function to execute in parallel.
-            method (str, optional): the backend to use in ``["serial", "thread", "process", "ray", "mpicomm"]``. Defaults to ``"serial"``.
-            method_kwargs (dict, optional): configuration dictionnary of the corresponding backend. Keys corresponds to the keyword arguments of the corresponding implementation. Defaults to "{}".
+            run_function (function):
+                The function to execute in parallel.
+            method (str, optional):
+                The backend to use in ``
+                ["serial", "thread", "process", "ray", "mpicomm"]``. Defaults
+                to ``"serial"``.
+            method_kwargs (dict, optional):
+                Configuration dictionnary of the corresponding backend. Keys
+                corresponds to the keyword arguments of the corresponding
+                implementation. Defaults to "{}".
 
         Raises:
             ValueError: if the ``method is`` not acceptable.
@@ -219,7 +237,8 @@ def create(run_function, method="serial", method_kwargs={}):
             )
 
         logging.info(
-            f"Creating {EVALUATORS[method].split('.')[-1]} of {method=} for {run_function=} with {method_kwargs=}"
+            f"Creating {EVALUATORS[method].split('.')[-1]} of {method=} for "
+            f"{run_function=} with {method_kwargs=}"
         )
 
         # create the evaluator
@@ -236,10 +255,13 @@ def _create_job(self, job_id, args, run_function, storage) -> Job:
         return self._job_class(job_id, args, run_function, storage)
 
     async def _await_at_least_n_tasks(self, n):
-        # If a user requests a batch size larger than the number of currently-running tasks, set n to the number of tasks running.
+        # If a user requests a batch size larger than the number of
+        # currently-running tasks, set n to the number of tasks running.
         if n > len(self._tasks_running):
             warnings.warn(
-                f"Requested a batch size ({n}) larger than currently running tasks ({len(self._tasks_running)}). Batch size has been set to the count of currently running tasks."
+                "Requested a batch size ({n}) larger than currently running tasks "
+                f"({len(self._tasks_running)}). Batch size has been set to the count of currently "
+                "running tasks."
             )
             n = len(self._tasks_running)
 
@@ -281,7 +303,7 @@ def _create_tasks(self, args_list: list) -> int:
 
             # Set the context of the job
             # TODO: the notion of `search` in the storage should probably be updated to something
-            # TODO: like `group` or `campaign` because it can be used in a different context than the search
+            # TODO: like `group` or `campaign` because can be used in different context than search
             new_job.context.search = self.search
 
             self.jobs.append(new_job)
@@ -328,7 +350,8 @@ async def _execute(self, job):
 
         if isinstance(job, HPOJob) and not (isinstance(job.output, dict)):
             raise ValueError(
-                "The output of the job is not standard. Check if `job.set_output(output) was correctly used when defining the Evaluator class."
+                "The output of the job is not standard. Check if `job.set_output(output) was "
+                "correctly used when defining the Evaluator class."
             )
 
         return job
@@ -357,7 +380,8 @@ def submit(self, args_list: List[Dict]):
         """Send configurations to be evaluated by available workers.
 
         Args:
-            args_list (List[Dict]): A list of dict which will be passed to the run function to be executed.
+            args_list (List[Dict]):
+                A list of dict which will be passed to the run function to be executed.
         """
         logging.info(f"submit {len(args_list)} job(s) starts...")
         self.set_event_loop()
@@ -374,8 +398,13 @@ def gather(self, type, size=1):
                     ``"ALL"``
                         Block until all jobs submitted to the evaluator are completed.
                     ``"BATCH"``
-                        Specify a minimum batch size of jobs to collect from the evaluator. The method will block until at least ``size`` evaluations are completed.
-            size (int, optional): The minimum batch size that we want to collect from the evaluator. Defaults to 1.
+                        Specify a minimum batch size of jobs to collect from
+                        the evaluator. The method will block until at least
+                        ``size`` evaluations are completed.
+
+            size (int, optional):
+                The minimum batch size that we want to collect from the
+                evaluator. Defaults to 1.
 
         Raises:
             Exception: Raised when a gather operation other than "ALL" or "BATCH" is provided.
@@ -403,7 +432,8 @@ def gather(self, type, size=1):
             return local_results
         else:
             logging.info(
-                f"gather done - {len(local_results)} local(s) and {len(other_results)} other(s) job(s), "
+                f"gather done - {len(local_results)} local(s) and {len(other_results)} "
+                "other(s) job(s)"
             )
 
             return local_results, other_results
@@ -449,7 +479,9 @@ def decode(self, key):
         return x
 
     def convert_for_csv(self, val):
-        """Convert an input value to an accepted format to be saved as a value of a CSV file (e.g., a list becomes it's str representation).
+        """Convert an input value to an accepted format.
+
+        This is to be saved as a value of a CSV file (e.g., a list becomes it's str representation).
 
         Args:
             val (Any): The input value to convert.
@@ -528,12 +560,17 @@ def dump_jobs_done_to_csv(
         filename="results.csv",
         flush: bool = False,
     ):
-        """Dump completed jobs to a CSV file. This will reset the ``Evaluator.jobs_done`` attribute to an empty list.
+        """Dump completed jobs to a CSV file.
+
+        This will reset the ``Evaluator.jobs_done`` attribute to an empty list.
 
         Args:
-            log_dir (str): directory where to dump the CSV file.
-            filename (str): name of the file where to write the data.
-            flush (bool): a boolean indicating if the results should be flushed (i.e., forcing the dumping).
+            log_dir (str):
+                Directory where to dump the CSV file.
+            filename (str):
+                Name of the file where to write the data.
+            flush (bool):
+                A boolean indicating if the results should be flushed (i.e., forcing the dumping).
         """
         logging.info("Dumping completed jobs to CSV...")
 
@@ -582,9 +619,7 @@ def _dump_jobs_done_to_csv_as_regular_format(
                     self._columns_dumped = records_list[0].keys()
 
                 if self._columns_dumped is not None:
-                    writer = csv.DictWriter(
-                        fp, self._columns_dumped, extrasaction="ignore"
-                    )
+                    writer = csv.DictWriter(fp, self._columns_dumped, extrasaction="ignore")
 
                     if not (self._start_dumping):
                         writer.writeheader()
@@ -596,12 +631,17 @@ def _dump_jobs_done_to_csv_as_regular_format(
     def _dump_jobs_done_to_csv_as_hpo_format(
         self, log_dir: str = ".", filename="results.csv", flush: bool = False
     ):
-        """Dump completed jobs to a CSV file. This will reset the ``Evaluator.jobs_done`` attribute to an empty list.
+        """Dump completed jobs to a CSV file.
+
+        This will reset the ``Evaluator.jobs_done`` attribute to an empty list.
 
         Args:
-            log_dir (str): directory where to dump the CSV file.
-            filename (str): name of the file where to write the data.
-            flush (bool): a boolean indicating if the results should be flushed (i.e., forcing the dumping).
+            log_dir (str):
+                Directory where to dump the CSV file.
+            filename (str):
+                Name of the file where to write the data.
+            flush (bool):
+                A boolean indicating if the results should be flushed (i.e., forcing the dumping).
         """
         resultsList = []
 
@@ -615,9 +655,7 @@ def _dump_jobs_done_to_csv_as_hpo_format(
             result["objective"] = job.objective
 
             # when the objective is a tuple (multi-objective) we create 1 column per tuple-element
-            if isinstance(result["objective"], tuple) or isinstance(
-                result["objective"], list
-            ):
+            if isinstance(result["objective"], tuple) or isinstance(result["objective"], list):
                 obj = result.pop("objective")
 
                 if self.num_objective is None:
@@ -655,26 +693,18 @@ def _dump_jobs_done_to_csv_as_hpo_format(
                     for result in resultsList:
                         # Waiting to start receiving non-failed jobs before dumping results
                         is_single_obj_and_has_success = (
-                            "objective" in result
-                            and type(result["objective"]) is not str
+                            "objective" in result and type(result["objective"]) is not str
                         )
                         is_multi_obj_and_has_success = (
-                            "objective_0" in result
-                            and type(result["objective_0"]) is not str
+                            "objective_0" in result and type(result["objective_0"]) is not str
                         )
-                        if (
-                            is_single_obj_and_has_success
-                            or is_multi_obj_and_has_success
-                            or flush
-                        ):
+                        if is_single_obj_and_has_success or is_multi_obj_and_has_success or flush:
                             self._columns_dumped = result.keys()
 
                             break
 
                 if self._columns_dumped is not None:
-                    writer = csv.DictWriter(
-                        fp, self._columns_dumped, extrasaction="ignore"
-                    )
+                    writer = csv.DictWriter(fp, self._columns_dumped, extrasaction="ignore")
 
                     if not (self._start_dumping):
                         writer.writeheader()
@@ -683,10 +713,9 @@ def _dump_jobs_done_to_csv_as_hpo_format(
                     writer.writerows(resultsList)
                     self.jobs_done = []
 
-    def dump_evals(
-        self, log_dir: str = ".", filename="results.csv", flush: bool = False
-    ):
+    def dump_evals(self, log_dir: str = ".", filename="results.csv", flush: bool = False):
         deprecated_api(
-            "The ``Evaluator.dump_evals(...)`` method is deprecated and will be removed. The ``Evaluator.dump_jobs_done_to_csv(...)`` method should be used instead."
+            "The ``Evaluator.dump_evals(...)`` method is deprecated and will be removed. The "
+            "``Evaluator.dump_jobs_done_to_csv(...)`` method should be used instead."
         )
         self.dump_jobs_done_to_csv(log_dir, filename, flush)
diff --git a/src/deephyper/evaluator/_job.py b/src/deephyper/evaluator/_job.py
index eccfb3e3..eabd9efd 100644
--- a/src/deephyper/evaluator/_job.py
+++ b/src/deephyper/evaluator/_job.py
@@ -121,7 +121,8 @@ def standardize_output(
                 output = {"objective": float(output)}
             else:
                 raise TypeError(
-                    f"When a scalar type, the output of the run-function cannot be of type {type(output)} it should either be a string or a number."
+                    "When a scalar type, the output of run-function cannot be of type "
+                    f"{type(output)} it should either be a string or a number."
                 )
 
         # output only returned objective values as tuple or list
@@ -132,19 +133,19 @@ def standardize_output(
             other_metadata = output.pop("metadata", dict())
             if not isinstance(other_metadata, dict):
                 TypeError(
-                    f"The metadata returned by the run-function should be a dictionnary but are: {other_metadata}."
+                    "The metadata returned by the run-function should be a dictionnary but are: "
+                    f"{other_metadata}."
                 )
             metadata.update(other_metadata)
 
             if "objective" not in output:
                 raise ValueError(
-                    "The output of the run-function should have a key 'objective' when it is a dictionnary."
+                    "The output of the run-function should have a key 'objective' when it is a "
+                    "dictionnary."
                 )
 
         else:
-            raise TypeError(
-                f"The output of the run-function cannot be of type {type(output)}"
-            )
+            raise TypeError(f"The output of the run-function cannot be of type {type(output)}")
 
         return output, metadata
 
@@ -213,8 +214,9 @@ def status(self):
         return JobStatus(self.storage.load_job_status(self.id))
 
     def record(self, budget: float, objective: float):
-        """Records the current ``budget`` and ``objective`` values in the object and
-        pass it to the stopper if one is being used.
+        """Records the current ``budget`` and ``objective`` values in the object.
+
+        These are passed to the stopper if one is being used.
 
         Args:
             budget (float): the budget used.
@@ -226,7 +228,10 @@ def record(self, budget: float, objective: float):
             self.obs = objective
 
     def stopped(self) -> bool:
-        """Returns True if the RunningJob is using a Stopper and it is stopped. Otherwise it will return False."""
+        """Returns True if the RunningJob is using a Stopper and it is stopped.
+
+        Otherwise it will return False.
+        """
         if self.stopper:
             return self.stopper.stop()
         else:
@@ -234,7 +239,10 @@ def stopped(self) -> bool:
 
     @property
     def objective(self):
-        """If the RunningJob is using a Stopper then it will return observations from the it. Otherwise it will simply return the last objective value recorded."""
+        """If the RunningJob is using a Stopper then it will return observations from the it.
+
+        Otherwise it will simply return the last objective value recorded.
+        """
         if self.stopper:
             return self.stopper.objective
         else:
diff --git a/src/deephyper/evaluator/_mochi_process_pool.py b/src/deephyper/evaluator/_mochi_process_pool.py
index eca1e737..b78c1258 100644
--- a/src/deephyper/evaluator/_mochi_process_pool.py
+++ b/src/deephyper/evaluator/_mochi_process_pool.py
@@ -45,9 +45,13 @@ class MochiEvaluator(Evaluator):
     """This evaluator uses the ``ProcessPoolExecutor`` as backend.
 
     Args:
-        run_function (callable): functions to be executed by the ``Evaluator``.
-        num_workers (int, optional): Number of parallel processes used to compute the ``run_function``. Defaults to 1.
-        callbacks (list, optional): A list of callbacks to trigger custom actions at the creation or completion of jobs. Defaults to None.
+        run_function (callable):
+            Functions to be executed by the ``Evaluator``.
+        num_workers (int, optional):
+            Number of parallel processes used to compute the ``run_function``. Defaults to 1.
+        callbacks (list, optional):
+            A list of callbacks to trigger custom actions at the creation or
+            completion of jobs. Defaults to None.
     """
 
     def __init__(
@@ -75,11 +79,10 @@ def __init__(
             # !creating the exector once here is crutial to avoid repetitive overheads
             self.executor = ProcessPoolExecutor(max_workers=num_workers)
 
-            if hasattr(run_function, "__name__") and hasattr(
-                run_function, "__module__"
-            ):
+            if hasattr(run_function, "__name__") and hasattr(run_function, "__module__"):
                 logger.info(
-                    f"Mochi Evaluator will execute {self.run_function.__name__}() from module {self.run_function.__module__}"
+                    f"Mochi Evaluator will execute {self.run_function.__name__}() from module "
+                    f"{self.run_function.__module__}"
                 )
             else:
                 logger.info(f"Mochi Evaluator will execute {self.run_function}")
diff --git a/src/deephyper/evaluator/_mpi_comm.py b/src/deephyper/evaluator/_mpi_comm.py
index f199f2d3..ac71a767 100644
--- a/src/deephyper/evaluator/_mpi_comm.py
+++ b/src/deephyper/evaluator/_mpi_comm.py
@@ -13,7 +13,10 @@
 
 
 def catch_exception(run_func):
-    """A wrapper function to execute the ``run_func`` passed by the user. This way we can catch remote exception"""
+    """A wrapper function to execute the ``run_func`` passed by the user.
+
+    This is used to catch remote exception.
+    """
     try:
         code = 0
         result = run_func()
@@ -27,24 +30,32 @@ def catch_exception(run_func):
 class MPICommEvaluator(Evaluator):
     """This evaluator uses the ``mpi4py`` library as backend.
 
-    This evaluator consider an already existing MPI-context (with running processes), therefore it has less overhead than ``MPIPoolEvaluator`` which spawn processes dynamically.
+    This evaluator consider an already existing MPI-context (with running
+    processes), therefore it has less overhead than ``MPIPoolEvaluator``
+    which spawn processes dynamically.
 
     Args:
-        run_function (callable): functions to be executed by the ``Evaluator``.
-
-        num_workers (int, optional): Number of parallel Ray-workers used to compute the ``run_function``. Defaults to ``None`` which consider 1 rank as a worker (minus the master rank).
-
-        callbacks (list, optional): A list of callbacks to trigger custom actions at the creation or completion of jobs. Defaults to ``None``.
-
-        run_function_kwargs (dict, optional): Keyword-arguments to pass to the ``run_function``. Defaults to ``None``.
-
-        storage (Storage, optional): Storage used by the evaluator. Defaults to ``SharedMemoryStorage``.
-
-        search_id (Hashable, optional): The id of the search to use in the corresponding storage. If ``None`` it will create a new search identifier when initializing the search.
-
-        comm (optional): A MPI communicator, if ``None`` it will use ``MPI.COMM_WORLD``. Defaults to ``None``.
-
-        rank (int, optional): The rank of the master process. Defaults to ``0``.
+        run_function (callable):
+            Functions to be executed by the ``Evaluator``.
+        num_workers (int, optional):
+            Number of parallel Ray-workers used to compute the
+            ``run_function``. Defaults to ``None`` which consider 1 rank as a
+            worker (minus the master rank).
+        callbacks (list, optional):
+            A list of callbacks to trigger custom actions at the creation or
+            completion of jobs. Defaults to ``None``.
+        run_function_kwargs (dict, optional):
+            Keyword-arguments to pass to the ``run_function``. Defaults to ``None``.
+        storage (Storage, optional):
+            Storage used by the evaluator. Defaults to ``SharedMemoryStorage``.
+        search_id (Hashable, optional):
+            The id of the search to use in the corresponding storage. If
+            ``None`` it will create a new search identifier when initializing
+            the search.
+        comm (optional):
+            A MPI communicator, if ``None`` it will use ``MPI.COMM_WORLD``. Defaults to ``None``.
+        rank (int, optional):
+            The rank of the master process. Defaults to ``0``.
     """
 
     def __init__(
@@ -94,7 +105,8 @@ def __init__(
 
         if self.num_workers == 0 and self.comm.Get_size() <= 1:
             raise RuntimeError(
-                "No workers was detected because there was only 1 MPI rank. The number of MPI ranks must be greater than 1."
+                "No workers was detected because there was only 1 MPI rank. The number of MPI "
+                "ranks must be greater than 1."
             )
 
         self.executor = MPICommExecutor(comm=self.comm, root=self.root)
@@ -124,9 +136,7 @@ async def execute(self, job: Job) -> Job:
                 job.run_function, running_job, **self.run_function_kwargs
             )
 
-            run_function_future = self.loop.run_in_executor(
-                self.master_executor, run_function
-            )
+            run_function_future = self.loop.run_in_executor(self.master_executor, run_function)
 
             if self.timeout is not None:
                 try:
diff --git a/src/deephyper/evaluator/_nest_asyncio.py b/src/deephyper/evaluator/_nest_asyncio.py
index 8f87e262..85276e47 100644
--- a/src/deephyper/evaluator/_nest_asyncio.py
+++ b/src/deephyper/evaluator/_nest_asyncio.py
@@ -1,4 +1,7 @@
-"""From https://github.com/erdewit/nest_asyncio"""
+"""Utility code to use asyncio feature inside jupyter notebooks/ipython kernels.
+
+The code is from: https://github.com/erdewit/nest_asyncio
+"""
 
 import asyncio
 import asyncio.events as events
@@ -20,8 +23,9 @@ def apply(loop=None):
 
 
 def _patch_asyncio():
-    """Patch asyncio module to use pure Python tasks and futures,
-    use module level _current_tasks, all_tasks and patch run method.
+    """Patch asyncio module to use pure Python tasks and futures.
+
+    Use module level _current_tasks, all_tasks and patch run method.
     """
 
     def run(main, *, debug=False):
@@ -58,9 +62,7 @@ def _get_event_loop(stacklevel=3):
         asyncio.tasks._current_tasks = asyncio.tasks.Task._current_tasks
         asyncio.all_tasks = asyncio.tasks.Task.all_tasks
     if sys.version_info >= (3, 9, 0):
-        events._get_event_loop = events.get_event_loop = asyncio.get_event_loop = (
-            _get_event_loop
-        )
+        events._get_event_loop = events.get_event_loop = asyncio.get_event_loop = _get_event_loop
         _get_event_loop
     asyncio.run = run
     asyncio._nest_patched = True
@@ -91,8 +93,9 @@ def run_until_complete(self, future):
             return f.result()
 
     def _run_once(self):
-        """Simplified re-implementation of asyncio's _run_once that
-        runs handles as they become ready.
+        """Simplified re-implementation of asyncio's _run_once.
+
+        This runs handles as they become ready.
         """
         ready = self._ready
         scheduled = self._scheduled
@@ -141,10 +144,7 @@ def manage_run(self):
             events._set_running_loop(old_running_loop)
             self._num_runs_pending -= 1
             if self._is_proactorloop:
-                if (
-                    self._num_runs_pending == 0
-                    and self._self_reading_future is not None
-                ):
+                if self._num_runs_pending == 0 and self._self_reading_future is not None:
                     ov = self._self_reading_future._ov
                     self._self_reading_future.cancel()
                     if ov is not None:
@@ -185,9 +185,7 @@ def _check_running(self):
     cls._check_running = _check_running
     cls._check_runnung = _check_running  # typo in Python 3.7 source
     cls._num_runs_pending = 0
-    cls._is_proactorloop = os.name == "nt" and issubclass(
-        cls, asyncio.ProactorEventLoop
-    )
+    cls._is_proactorloop = os.name == "nt" and issubclass(cls, asyncio.ProactorEventLoop)
     if sys.version_info < (3, 7, 0):
         cls._set_coroutine_origin_tracking = cls._set_coroutine_wrapper
     cls._nest_patched = True
@@ -230,9 +228,7 @@ def leave_task(loop, task):
 
 
 def _patch_tornado():
-    """If tornado is imported before nest_asyncio, make tornado aware of
-    the pure-Python asyncio Future.
-    """
+    """If tornado imported before nest_asyncio, make tornado aware of pure-Python asyncio Future."""
     if "tornado" in sys.modules:
         import tornado.concurrent as tc
 
diff --git a/src/deephyper/evaluator/_process_pool.py b/src/deephyper/evaluator/_process_pool.py
index 4c21bb84..7a4dd560 100644
--- a/src/deephyper/evaluator/_process_pool.py
+++ b/src/deephyper/evaluator/_process_pool.py
@@ -11,17 +11,21 @@ class ProcessPoolEvaluator(Evaluator):
     """This evaluator uses the ``ProcessPoolExecutor`` as backend.
 
     Args:
-        run_function (callable): functions to be executed by the ``Evaluator``.
-
-        num_workers (int, optional): Number of parallel processes used to compute the ``run_function``. Defaults to 1.
-
-        callbacks (list, optional): A list of callbacks to trigger custom actions at the creation or completion of jobs. Defaults to None.
-
-        run_function_kwargs (dict, optional): Static keyword arguments to pass to the ``run_function`` when executed.
-
-        storage (Storage, optional): Storage used by the evaluator. Defaults to ``SharedMemoryStorage``.
-
-        search_id (Hashable, optional): The id of the search to use in the corresponding storage. If ``None`` it will create a new search identifier when initializing the search.
+        run_function (callable):
+            Functions to be executed by the ``Evaluator``.
+        num_workers (int, optional):
+            Number of parallel processes used to compute the ``run_function``. Defaults to 1.
+        callbacks (list, optional):
+            A list of callbacks to trigger custom actions at the creation or
+            completion of jobs. Defaults to None.
+        run_function_kwargs (dict, optional):
+            Static keyword arguments to pass to the ``run_function`` when executed.
+        storage (Storage, optional):
+            Storage used by the evaluator. Defaults to ``SharedMemoryStorage``.
+        search_id (Hashable, optional):
+            The id of the search to use in the corresponding storage. If
+            ``None`` it will create a new search identifier when initializing
+            the search.
     """
 
     def __init__(
diff --git a/src/deephyper/evaluator/_queued.py b/src/deephyper/evaluator/_queued.py
index 5ffb8266..5df0653c 100644
--- a/src/deephyper/evaluator/_queued.py
+++ b/src/deephyper/evaluator/_queued.py
@@ -1,7 +1,7 @@
 import collections
 
 
-def queued(evaluator_class):
+def queued(evaluator_class):  # noqa: D417
     """Decorator transforming an Evaluator into a ``Queued{Evaluator}``.
 
     The ``run_function`` used with a ``Queued{Evaluator}`` needs to have a
@@ -9,13 +9,14 @@ def queued(evaluator_class):
     will be passed.
 
     Args:
-        queue (list): A list of queued resources.
-        queue_pop_per_task (int, optional): The number of resources popped out of the queue each time a task is submitted. Defaults to ``1``.
+        queue (list):
+            A list of queued resources.
+        queue_pop_per_task (int, optional):
+            The number of resources popped out of the queue each time a task
+            is submitted. Defaults to ``1``.
     """
 
-    def __init__(
-        self, *args, queue: list = None, queue_pop_per_task: int = 1, **kwargs
-    ):
+    def __init__(self, *args, queue: list = None, queue_pop_per_task: int = 1, **kwargs):
         evaluator_class.__init__(self, *args, **kwargs)
 
         self.queue = collections.deque(queue[:])
diff --git a/src/deephyper/evaluator/_ray.py b/src/deephyper/evaluator/_ray.py
index cde70258..322019a3 100644
--- a/src/deephyper/evaluator/_ray.py
+++ b/src/deephyper/evaluator/_ray.py
@@ -13,19 +13,43 @@ class RayEvaluator(Evaluator):
     """This evaluator uses the ``ray`` library as backend.
 
     Args:
-        run_function (callable): functions to be executed by the ``Evaluator``.
-        callbacks (list, optional): A list of callbacks to trigger custom actions at the creation or completion of jobs. Defaults to None.
-        run_function_kwargs (dict, optional): Static keyword arguments to pass to the ``run_function`` when executed.
-        storage (Storage, optional): Storage used by the evaluator. Defaults to ``RayStorage``.
-        search_id (Hashable, optional): The id of the search to use in the corresponding storage. If ``None`` it will create a new search identifier when initializing the search.
-        address (str, optional): address of the Ray-head. Defaults to None, if no Ray-head was started.
-        password (str, optional): password to connect ot the Ray-head. Defaults to None, if the default Ray-password is used.
-        num_cpus (int, optional): number of CPUs available in the Ray-cluster. Defaults to None, if the Ray-cluster was already started it will be automatically computed.
-        num_gpus (int, optional): number of GPUs available in the Ray-cluster. Defaults to None, if the Ray-cluster was already started it will be automatically computed.
-        num_cpus_per_task (float, optional): number of CPUs used per remote task. Defaults to 1.
-        num_gpus_per_task (float, optional): number of GPUs used per remote task. Defaults to None.
-        ray_kwargs (dict, optional): other ray keyword arguments passed to ``ray.init(...)``. Defaults to {}.
-        num_workers (int, optional): number of workers available to compute remote-tasks in parallel. Defaults to ``None``, or if it is ``-1`` it is automatically computed based with ``num_workers = int(num_cpus // num_cpus_per_task)``.
+        run_function (callable):
+            Functions to be executed by the ``Evaluator``.
+        callbacks (list, optional):
+            A list of callbacks to trigger custom actions at the creation or
+            completion of jobs. Defaults to None.
+        run_function_kwargs (dict, optional):
+            Static keyword arguments to pass to the ``run_function`` when executed.
+        storage (Storage, optional):
+            Storage used by the evaluator. Defaults to ``RayStorage``.
+        search_id (Hashable, optional):
+            The id of the search to use in the corresponding storage. If
+            ``None`` it will create a new search identifier when initializing
+            the search.
+        address (str, optional):
+            address of the Ray-head. Defaults to None, if no Ray-head was started.
+        password (str, optional):
+            Password to connect ot the Ray-head. Defaults to None, if the
+            default Ray-password is used.
+        num_cpus (int, optional):
+            Number of CPUs available in the Ray-cluster. Defaults to None, if
+            the Ray-cluster was already started it will be automatically
+            computed.
+        num_gpus (int, optional):
+            Number of GPUs available in the Ray-cluster. Defaults to None, if
+            the Ray-cluster was already started it will be automatically
+            computed.
+        num_cpus_per_task (float, optional):
+            Number of CPUs used per remote task. Defaults to 1.
+        num_gpus_per_task (float, optional):
+            Number of GPUs used per remote task. Defaults to None.
+        ray_kwargs (dict, optional):
+            Other ray keyword arguments passed to ``ray.init(...)``. Defaults to {}.
+        num_workers (int, optional):
+            Number of workers available to compute remote-tasks in parallel.
+            Defaults to ``None``, or if it is ``-1`` it is automatically
+            computed based with ``num_workers = int
+            (num_cpus // num_cpus_per_task)``.
     """
 
     def __init__(
@@ -77,16 +101,12 @@ def __init__(
         self.num_gpus_per_task = num_gpus_per_task
 
         if num_cpus is None:
-            self.num_cpus = int(
-                sum([node["Resources"].get("CPU", 0) for node in ray.nodes()])
-            )
+            self.num_cpus = int(sum([node["Resources"].get("CPU", 0) for node in ray.nodes()]))
         else:
             self.num_cpus = num_cpus
 
         if num_gpus is None:
-            self.num_gpus = int(
-                sum([node["Resources"].get("GPU", 0) for node in ray.nodes()])
-            )
+            self.num_gpus = int(sum([node["Resources"].get("GPU", 0) for node in ray.nodes()]))
         else:
             self.num_gpus = num_gpus
 
diff --git a/src/deephyper/evaluator/_serial.py b/src/deephyper/evaluator/_serial.py
index f50a4714..d4baee8b 100644
--- a/src/deephyper/evaluator/_serial.py
+++ b/src/deephyper/evaluator/_serial.py
@@ -10,12 +10,21 @@ class SerialEvaluator(Evaluator):
     """This evaluator run evaluations one after the other (not parallel).
 
     Args:
-        run_function (callable): functions to be executed by the ``Evaluator``.
-        num_workers (int, optional): Number of parallel Ray-workers used to compute the ``run_function``. Defaults to 1.
-        callbacks (list, optional): A list of callbacks to trigger custom actions at the creation or completion of jobs. Defaults to None.
-        run_function_kwargs (dict, optional): Static keyword arguments to pass to the ``run_function`` when executed.
-        storage (Storage, optional): Storage used by the evaluator. Defaults to ``MemoryStorage``.
-        search_id (Hashable, optional): The id of the search to use in the corresponding storage. If ``None`` it will create a new search identifier when initializing the search.
+        run_function (callable):
+            Functions to be executed by the ``Evaluator``.
+        num_workers (int, optional):
+            Number of parallel Ray-workers used to compute the ``run_function``. Defaults to 1.
+        callbacks (list, optional):
+            A list of callbacks to trigger custom actions at the creation or
+            completion of jobs. Defaults to None.
+        run_function_kwargs (dict, optional):
+            Static keyword arguments to pass to the ``run_function`` when executed.
+        storage (Storage, optional):
+            Storage used by the evaluator. Defaults to ``MemoryStorage``.
+        search_id (Hashable, optional):
+            The id of the search to use in the corresponding storage. If
+            ``None`` it will create a new search identifier when initializing
+            the search.
     """
 
     def __init__(
@@ -29,7 +38,9 @@ def __init__(
     ):
         if not asyncio.iscoroutinefunction(run_function):
             raise ValueError(
-                f"The {run_function=} passed to {type(self).__name__} is not a coroutine (e.g., with 'async def' in its definition) either make it a coroutine function or use the ThreadEvaluator instead."
+                f"The {run_function=} passed to {type(self).__name__} is not a coroutine "
+                "(e.g., with 'async def' in its definition) either make it a coroutine function "
+                "or use the ThreadEvaluator instead."
             )
 
         super().__init__(
diff --git a/src/deephyper/evaluator/_thread_pool.py b/src/deephyper/evaluator/_thread_pool.py
index 99d082dd..97a3597b 100644
--- a/src/deephyper/evaluator/_thread_pool.py
+++ b/src/deephyper/evaluator/_thread_pool.py
@@ -13,15 +13,28 @@
 class ThreadPoolEvaluator(Evaluator):
     """This evaluator uses the ``ThreadPoolExecutor`` as backend.
 
-    .. warning:: This evaluator is interesting with I/O intensive tasks, do not expect a speed-up with compute intensive tasks.
+    .. warning::
+
+       This evaluator is interesting with I/O intensive tasks, do not expect a
+       speed-up with compute intensive tasks.
 
     Args:
-        run_function (callable): functions to be executed by the ``Evaluator``.
-        num_workers (int, optional): Number of concurrent threads used to compute the ``run_function``. Defaults to 1.
-        callbacks (list, optional): A list of callbacks to trigger custom actions at the creation or completion of jobs. Defaults to None.
-        run_function_kwargs (dict, optional): Static keyword arguments to pass to the ``run_function`` when executed.
-        storage (Storage, optional): Storage used by the evaluator. Defaults to ``MemoryStorage``.
-        search_id (Hashable, optional): The id of the search to use in the corresponding storage. If ``None`` it will create a new search identifier when initializing the search.
+        run_function (callable):
+            Functions to be executed by the ``Evaluator``.
+        num_workers (int, optional):
+            Number of concurrent threads used to compute the ``run_function``.
+            Defaults to 1.
+        callbacks (list, optional):
+            A list of callbacks to trigger custom actions at the creation or
+            completion of jobs. Defaults to None.
+        run_function_kwargs (dict, optional):
+            Static keyword arguments to pass to the ``run_function`` when executed.
+        storage (Storage, optional):
+            Storage used by the evaluator. Defaults to ``MemoryStorage``.
+        search_id (Hashable, optional):
+            The id of the search to use in the corresponding storage. If
+            ``None`` it will create a new search identifier when initializing
+            the search.
     """
 
     def __init__(
diff --git a/src/deephyper/evaluator/callback.py b/src/deephyper/evaluator/callback.py
index 8ba09723..2056cd55 100644
--- a/src/deephyper/evaluator/callback.py
+++ b/src/deephyper/evaluator/callback.py
@@ -1,10 +1,11 @@
 """The callback module contains sub-classes of the ``Callback`` class.
 
-This ``Callback`` class is used to trigger custom actions on the start and
+The ``Callback`` class is used to trigger custom actions on the start and
 completion of jobs by the ``Evaluator``. Callbacks can be used with any
-Evaluator implementation.
+``Evaluator`` implementation.
 """
 
+import abc
 import deephyper.core.exceptions
 import numpy as np
 import pandas as pd
@@ -16,7 +17,9 @@
     from tqdm import tqdm
 
 
-class Callback:
+class Callback(abc.ABC):
+    """Callback interface."""
+
     def on_launch(self, job):
         """Called each time a ``Job`` is created by the ``Evaluator``.
 
@@ -25,14 +28,14 @@ def on_launch(self, job):
         """
 
     def on_done(self, job):
-        """Called each time a Job is completed by the Evaluator.
+        """Called each time a local ``Job`` has been gathered by the Evaluator.
 
         Args:
             job (Job): The completed job.
         """
 
     def on_done_other(self, job):
-        """Called each time a Job is collected from an other process.
+        """Called after local ``Job`` have been gathered for each remote ``Job`` that is done.
 
         Args:
             job (Job): The completed Job.
@@ -40,7 +43,12 @@ def on_done_other(self, job):
 
 
 class ProfilingCallback(Callback):
-    """Collect profiling data. Each time a ``Job`` is completed by the ``Evaluator`` a the different timestamps corresponding to the submit and gather (and run function start and end if the ``profile`` decorator is used on the run function) are collected.
+    """Collect profiling data.
+
+    Each time a ``Job`` is completed by the ``Evaluator`` a the different
+    timestamps corresponding to the submit and gather (and run function start
+    and end if the ``profile`` decorator is used on the run function) are
+    collected.
 
     An example usage can be:
 
@@ -53,9 +61,8 @@ class ProfilingCallback(Callback):
     def __init__(self):
         self.history = []
 
-    def on_launch(self, job): ...
-
     def on_done(self, job):
+        """Called when a local job has been gathered."""
         start = job.timestamp_submit
         end = job.timestamp_gather
         if job.timestamp_start is not None and job.timestamp_end is not None:
@@ -66,6 +73,7 @@ def on_done(self, job):
 
     @property
     def profile(self):
+        """The profile processed as a ``pd.DataFrame``."""
         n_jobs = 0
         profile = []
         for t, incr in sorted(self.history):
@@ -89,9 +97,11 @@ def __init__(self):
         self._n_done = 0
 
     def on_done_other(self, job):
+        """Called after gathering local jobs on available remote jobs that are done."""
         self.on_done(job)
 
     def on_done(self, job):
+        """Called when a local job has been gathered."""
         self._n_done += 1
         # Test if multi objectives are received
         if np.ndim(job.objective) > 0:
@@ -99,12 +109,11 @@ def on_done(self, job):
                 if self._best_objective is None:
                     self._best_objective = np.sum(job.objective)
                 else:
-                    self._best_objective = max(
-                        np.sum(job.objective), self._best_objective
-                    )
+                    self._best_objective = max(np.sum(job.objective), self._best_objective)
 
                 print(
-                    f"[{self._n_done:05d}] -- best sum(objective): {self._best_objective:.5f} -- received sum(objective): {np.sum(job.objective):.5f}"
+                    f"[{self._n_done:05d}] -- best sum(objective): {self._best_objective:.5f} -- "
+                    f"received sum(objective): {np.sum(job.objective):.5f}"
                 )
             elif np.any(type(res) is str and "F" == res[0] for res in job.objective):
                 print(f"[{self._n_done:05d}] -- received failure: {job.objective}")
@@ -115,7 +124,8 @@ def on_done(self, job):
                 self._best_objective = max(job.objective, self._best_objective)
 
             print(
-                f"[{self._n_done:05d}] -- best objective: {self._best_objective:.5f} -- received objective: {job.objective:.5f}"
+                f"[{self._n_done:05d}] -- best objective: {self._best_objective:.5f} -- "
+                f"received objective: {job.objective:.5f}"
             )
         elif type(job.objective) is str and "F" == job.objective[0]:
             print(f"[{self._n_done:05d}] -- received failure: {job.objective}")
@@ -137,13 +147,19 @@ def __init__(self):
         self._tqdm = None
 
     def set_max_evals(self, max_evals):
+        """Setter for the maximum number of evaluations.
+
+        It is used to initialize the tqdm progressbar.
+        """
         self._max_evals = max_evals
         self._tqdm = None
 
     def on_done_other(self, job):
+        """Called after gathering local jobs on available remote jobs that are done."""
         self.on_done(job)
 
     def on_done(self, job):
+        """Called when a local job has been gathered."""
         if self._tqdm is None:
             if self._max_evals:
                 self._tqdm = tqdm(total=self._max_evals)
@@ -158,9 +174,7 @@ def on_done(self, job):
                 if self._best_objective is None:
                     self._best_objective = np.sum(job.objective)
                 else:
-                    self._best_objective = max(
-                        np.sum(job.objective), self._best_objective
-                    )
+                    self._best_objective = max(np.sum(job.objective), self._best_objective)
             else:
                 self._n_failures += 1
             self._tqdm.set_postfix(
@@ -174,18 +188,24 @@ def on_done(self, job):
                     self._best_objective = max(job.objective, self._best_objective)
             else:
                 self._n_failures += 1
-            self._tqdm.set_postfix(
-                objective=self._best_objective, failures=self._n_failures
-            )
+            self._tqdm.set_postfix(objective=self._best_objective, failures=self._n_failures)
 
 
 class SearchEarlyStopping(Callback):
     """Stop the search gracefully when it does not improve for a given number of evaluations.
 
     Args:
-        patience (int, optional): The number of not improving evaluations to wait for before stopping the search. Defaults to 10.
-        objective_func (callable, optional): A function that takes a ``Job`` has input and returns the maximized scalar value monitored by this callback. Defaults to ``lambda j: j.result``.
-        threshold (float, optional): The threshold to reach before activating the patience to stop the search. Defaults to ``None``, patience is reinitialized after each improving observation.
+        patience (int, optional):
+            The number of not improving evaluations to wait for before
+            stopping the search. Defaults to 10.
+        objective_func (callable, optional):
+            A function that takes a ``Job`` has input and returns the
+            maximized scalar value monitored by this callback. Defaults to
+            ``lambda j: j.result``.
+        threshold (float, optional):
+            The threshold to reach before activating the patience to stop the
+            search. Defaults to ``None``, patience is reinitialized after
+            each improving observation.
         verbose (bool, optional): Activation or deactivate the verbose mode. Defaults to ``True``.
     """
 
@@ -204,9 +224,11 @@ def __init__(
         self._verbose = verbose
 
     def on_done_other(self, job):
+        """Called after gathering local jobs on available remote jobs that are done."""
         self.on_done(job)
 
     def on_done(self, job):
+        """Called when a local job has been gathered."""
         job_objective = self._objective_func(job)
         # if multi objectives are received
         if np.ndim(job_objective) > 0:
@@ -217,7 +239,8 @@ def on_done(self, job):
             if job_objective > self._best_objective:
                 if self._verbose:
                     print(
-                        f"Objective has improved from {self._best_objective:.5f} -> {job_objective:.5f}"
+                        "Objective has improved from "
+                        f"{self._best_objective:.5f} -> {job_objective:.5f}"
                     )
                 self._best_objective = job_objective
                 self._n_lower = 0
@@ -228,13 +251,15 @@ def on_done(self, job):
             if self._threshold is None:
                 if self._verbose:
                     print(
-                        f"Stopping the search because it did not improve for the last {self._patience} evaluations!"
+                        "Stopping the search because it did not improve for the last "
+                        f"{self._patience} evaluations!"
                     )
                 raise deephyper.core.exceptions.SearchTerminationError
             else:
                 if self._best_objective > self._threshold:
                     if self._verbose:
                         print(
-                            f"Stopping the search because it did not improve for the last {self._patience} evaluations!"
+                            "Stopping the search because it did not improve for the last "
+                            f"{self._patience} evaluations!"
                         )
                     raise deephyper.core.exceptions.SearchTerminationError
diff --git a/src/deephyper/evaluator/mpi.py b/src/deephyper/evaluator/mpi.py
index 4c693245..b514b48e 100644
--- a/src/deephyper/evaluator/mpi.py
+++ b/src/deephyper/evaluator/mpi.py
@@ -1,3 +1,5 @@
+"""Submodule from where ``MPI`` should always be imported within the package."""
+
 import mpi4py
 
 # The following configuration avoid initializing MPI during the import
diff --git a/src/deephyper/evaluator/storage/_memory_storage.py b/src/deephyper/evaluator/storage/_memory_storage.py
index b9872089..15e49a5c 100644
--- a/src/deephyper/evaluator/storage/_memory_storage.py
+++ b/src/deephyper/evaluator/storage/_memory_storage.py
@@ -72,9 +72,7 @@ def store_job(self, job_id: Hashable, key: Hashable, value: Any) -> None:
         search_id, partial_id = job_id.split(".")
         self._data[search_id]["data"][partial_id][key] = value
 
-    def store_job_in(
-        self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None
-    ) -> None:
+    def store_job_in(self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None) -> None:
         """Stores the input arguments of the executed job.
 
         Args:
@@ -150,9 +148,7 @@ def load_job(self, job_id: Hashable) -> dict:
         data = self._data[search_id]["data"][partial_id]
         return copy.deepcopy(data)
 
-    def store_search_value(
-        self, search_id: Hashable, key: Hashable, value: Any
-    ) -> None:
+    def store_search_value(self, search_id: Hashable, key: Hashable, value: Any) -> None:
         """Stores the value corresponding to key for search_id.
 
         Args:
@@ -171,9 +167,7 @@ def load_search_value(self, search_id: Hashable, key: Hashable) -> Any:
         """
         return self._data[search_id][key]
 
-    def load_metadata_from_all_jobs(
-        self, search_id: Hashable, key: Hashable
-    ) -> List[Any]:
+    def load_metadata_from_all_jobs(self, search_id: Hashable, key: Hashable) -> List[Any]:
         """Loads a given metadata value from all jobs.
 
         Args:
diff --git a/src/deephyper/evaluator/storage/_mpi_win_mutable_mapping.py b/src/deephyper/evaluator/storage/_mpi_win_mutable_mapping.py
index 7cc1dec4..38a76458 100644
--- a/src/deephyper/evaluator/storage/_mpi_win_mutable_mapping.py
+++ b/src/deephyper/evaluator/storage/_mpi_win_mutable_mapping.py
@@ -16,14 +16,15 @@ class MPIWinMutableMapping(MutableMapping):
     """Dict like object shared between MPI processes using one-sided communication.
 
     Args:
-        default_value (dict): The default value of the mutable mapping at initialization. Defaults to ``None`` for empty
-        dict.
-
-        comm (MPI.Comm): An MPI communicator.
-
-        size (int): The total size of the shared memory in bytes. Defaults to ``104857600`` for 100MB.
-
-        root (int): The MPI rank where the shared memory window is hosted.
+        default_value (dict):
+            The default value of the mutable mapping at initialization.
+            Defaults to ``None`` for empty dict.
+        comm (MPI.Comm):
+            An MPI communicator.
+        size (int):
+            The total size of the shared memory in bytes. Defaults to ``104857600`` for 100MB.
+        root (int):
+            The MPI rank where the shared memory window is hosted.
     """
 
     HEADER_SIZE = 8  # Reserve 8 bytes for size header
@@ -75,13 +76,9 @@ def _read_dict(self):
         """Read the dictionnary state from the shared memory."""
         # Deserialize the dictionary from shared memory
         try:
-            size = int.from_bytes(
-                self.shared_memory[: self.HEADER_SIZE], byteorder="big"
-            )
+            size = int.from_bytes(self.shared_memory[: self.HEADER_SIZE], byteorder="big")
             if size > 0:
-                raw_data = self.shared_memory[
-                    self.HEADER_SIZE : self.HEADER_SIZE + size
-                ].tobytes()
+                raw_data = self.shared_memory[self.HEADER_SIZE : self.HEADER_SIZE + size].tobytes()
                 self.local_dict = pickle.loads(raw_data)
             else:
                 self.local_dict = {}
@@ -173,9 +170,7 @@ def unlock(self):
 
     def session_start(self, ready_only: bool = False):
         if self._session_is_started:
-            raise RuntimeError(
-                "A session has already been started without being finished!"
-            )
+            raise RuntimeError("A session has already been started without being finished!")
 
         self._session_is_started = True
         self._session_is_ready_only = ready_only
diff --git a/src/deephyper/evaluator/storage/_mpi_win_storage.py b/src/deephyper/evaluator/storage/_mpi_win_storage.py
index 05470cac..92b65cfb 100644
--- a/src/deephyper/evaluator/storage/_mpi_win_storage.py
+++ b/src/deephyper/evaluator/storage/_mpi_win_storage.py
@@ -8,9 +8,7 @@
 
 
 class MPIWinStorage(Storage):
-    """Storage based on MPIWinMutableMapping that uses one-sided communication for
-    shared memory.
-    """
+    """Storage based on MPIWinMutableMapping that uses one-sided communication for shared memory."""
 
     def __init__(self, comm, size: int = 104857600, root: int = 0) -> None:
         logging.info("Creating MPIWinStorage ...")
@@ -86,9 +84,7 @@ def store_job(self, job_id: Hashable, key: Hashable, value: Any) -> None:
         with self._mapping():
             self._mapping["data"][search_id]["data"][partial_id][key] = value
 
-    def store_job_in(
-        self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None
-    ) -> None:
+    def store_job_in(self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None) -> None:
         """Stores the input arguments of the executed job.
 
         Args:
@@ -117,9 +113,7 @@ def store_job_metadata(self, job_id: Hashable, key: Hashable, value: Any) -> Non
         """
         search_id, partial_id = job_id.split(".")
         with self._mapping():
-            self._mapping["data"][search_id]["data"][partial_id]["metadata"][key] = (
-                value
-            )
+            self._mapping["data"][search_id]["data"][partial_id]["metadata"][key] = value
 
     def load_all_search_ids(self) -> List[Hashable]:
         """Loads the identifiers of all recorded searches.
@@ -167,9 +161,7 @@ def load_job(self, job_id: Hashable) -> dict:
         data = self._mapping["data"][search_id]["data"][partial_id]
         return copy.deepcopy(data)
 
-    def store_search_value(
-        self, search_id: Hashable, key: Hashable, value: Any
-    ) -> None:
+    def store_search_value(self, search_id: Hashable, key: Hashable, value: Any) -> None:
         """Stores the value corresponding to key for search_id.
 
         Args:
@@ -189,9 +181,7 @@ def load_search_value(self, search_id: Hashable, key: Hashable) -> Any:
         """
         return self._mapping["data"][search_id][key]
 
-    def load_metadata_from_all_jobs(
-        self, search_id: Hashable, key: Hashable
-    ) -> List[Any]:
+    def load_metadata_from_all_jobs(self, search_id: Hashable, key: Hashable) -> List[Any]:
         """Loads a given metadata value from all jobs.
 
         Args:
diff --git a/src/deephyper/evaluator/storage/_null_storage.py b/src/deephyper/evaluator/storage/_null_storage.py
index 7cc20ef4..04227240 100644
--- a/src/deephyper/evaluator/storage/_null_storage.py
+++ b/src/deephyper/evaluator/storage/_null_storage.py
@@ -47,9 +47,7 @@ def store_job(self, job_id: Hashable, key: Hashable, value: Any) -> None:
             value (Any): The value to store.
         """
 
-    def store_job_in(
-        self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None
-    ) -> None:
+    def store_job_in(self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None) -> None:
         """Stores the input arguments of the executed job.
 
         Args:
@@ -116,9 +114,7 @@ def load_job(self, job_id: Hashable) -> dict:
         """
         return None
 
-    def store_search_value(
-        self, search_id: Hashable, key: Hashable, value: Any
-    ) -> None:
+    def store_search_value(self, search_id: Hashable, key: Hashable, value: Any) -> None:
         """Stores the value corresponding to key for search_id.
 
         Args:
@@ -136,9 +132,7 @@ def load_search_value(self, search_id: Hashable, key: Hashable) -> Any:
         """
         return None
 
-    def load_metadata_from_all_jobs(
-        self, search_id: Hashable, key: Hashable
-    ) -> List[Any]:
+    def load_metadata_from_all_jobs(self, search_id: Hashable, key: Hashable) -> List[Any]:
         """Loads a given metadata value from all jobs.
 
         Args:
diff --git a/src/deephyper/evaluator/storage/_ray_storage.py b/src/deephyper/evaluator/storage/_ray_storage.py
index b270cf9d..bd653910 100644
--- a/src/deephyper/evaluator/storage/_ray_storage.py
+++ b/src/deephyper/evaluator/storage/_ray_storage.py
@@ -7,10 +7,14 @@
 
 
 class RayStorage(Storage):
-    """Storage class using Ray actors. The RayStorage is wrapping the MemoryStorage class to be a Ray actor.
+    """Storage class using Ray actors.
+
+    The RayStorage is wrapping the MemoryStorage class to be a Ray actor.
 
     Args:
-        address (str, optional): address of the Ray-head. Defaults to ``"auto"``, to connect to the local head node.
+        address (str, optional):
+            Address of the Ray-head. Defaults to ``"auto"``, to connect to the
+            local head node.
     """
 
     ray_storage_counter = 0
@@ -48,9 +52,7 @@ def __setstate__(self, newstate):
         if not ray.is_initialized():
             ray.init(address=self.address)
 
-        self.memory_storage_actor = ray.get_actor(
-            self.actor_name, namespace="deephyper"
-        )
+        self.memory_storage_actor = ray.get_actor(self.actor_name, namespace="deephyper")
 
     def create_new_search(self) -> Hashable:
         """Create a new search in the store and returns its identifier.
@@ -72,9 +74,7 @@ def create_new_job(self, search_id: Hashable) -> Hashable:
         """
         return ray.get(self.memory_storage_actor.create_new_job.remote(search_id))
 
-    def store_search_value(
-        self, search_id: Hashable, key: Hashable, value: Any
-    ) -> None:
+    def store_search_value(self, search_id: Hashable, key: Hashable, value: Any) -> None:
         """Stores the value corresponding to key for search_id.
 
         Args:
@@ -82,9 +82,7 @@ def store_search_value(
             key (Hashable): A key to use to store the value.
             value (Any): The value to store.
         """
-        ray.get(
-            self.memory_storage_actor.store_search_value.remote(search_id, key, value)
-        )
+        ray.get(self.memory_storage_actor.store_search_value.remote(search_id, key, value))
 
     def load_search_value(self, search_id: Hashable, key: Hashable) -> Any:
         """Loads the value corresponding to key for search_id.
@@ -93,9 +91,7 @@ def load_search_value(self, search_id: Hashable, key: Hashable) -> Any:
             search_id (Hashable): The identifier of the job.
             key (Hashable): A key to use to access the value.
         """
-        return ray.get(
-            self.memory_storage_actor.load_search_value.remote(search_id, key)
-        )
+        return ray.get(self.memory_storage_actor.load_search_value.remote(search_id, key))
 
     def store_job(self, job_id: Hashable, key: Hashable, value: Any) -> None:
         """Stores the value corresponding to key for job_id.
@@ -107,9 +103,7 @@ def store_job(self, job_id: Hashable, key: Hashable, value: Any) -> None:
         """
         ray.get(self.memory_storage_actor.store_job.remote(job_id, key, value))
 
-    def store_job_in(
-        self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None
-    ) -> None:
+    def store_job_in(self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None) -> None:
         """Stores the input arguments of the executed job.
 
         Args:
@@ -179,9 +173,7 @@ def load_job(self, job_id: Hashable) -> dict:
         """
         return ray.get(self.memory_storage_actor.load_job.remote(job_id))
 
-    def load_metadata_from_all_jobs(
-        self, search_id: Hashable, key: Hashable
-    ) -> List[Any]:
+    def load_metadata_from_all_jobs(self, search_id: Hashable, key: Hashable) -> List[Any]:
         """Loads a given metadata value from all jobs.
 
         Args:
@@ -191,9 +183,7 @@ def load_metadata_from_all_jobs(
         Returns:
             List[Any]: A list of all the retrieved metadata values.
         """
-        return ray.get(
-            self.memory_storage_actor.load_metadata_from_all_jobs.remote(search_id, key)
-        )
+        return ray.get(self.memory_storage_actor.load_metadata_from_all_jobs.remote(search_id, key))
 
     def load_out_from_all_jobs(self, search_id: Hashable) -> List[Any]:
         """Loads the output value from all jobs.
@@ -204,9 +194,7 @@ def load_out_from_all_jobs(self, search_id: Hashable) -> List[Any]:
         Returns:
             List[Any]: A list of all the retrieved output values.
         """
-        return ray.get(
-            self.memory_storage_actor.load_out_from_all_jobs.remote(search_id)
-        )
+        return ray.get(self.memory_storage_actor.load_out_from_all_jobs.remote(search_id))
 
     def load_jobs(self, job_ids: List[Hashable]) -> dict:
         """Load all data from a given list of jobs' identifiers.
diff --git a/src/deephyper/evaluator/storage/_redis_storage.py b/src/deephyper/evaluator/storage/_redis_storage.py
index dbe78ee0..2d13b164 100644
--- a/src/deephyper/evaluator/storage/_redis_storage.py
+++ b/src/deephyper/evaluator/storage/_redis_storage.py
@@ -79,9 +79,7 @@ def create_new_job(self, search_id: Hashable) -> Hashable:
         Returns:
             Hashable: The created identifier of the job.
         """
-        partial_id = (
-            self._redis.incr(f"search:{search_id}.job_id_counter", amount=1) - 1
-        )
+        partial_id = self._redis.incr(f"search:{search_id}.job_id_counter", amount=1) - 1
         partial_id = f"{partial_id}"  # converting to str
         job_id = f"{search_id}.{partial_id}"
         self._redis.rpush(f"search:{search_id}.job_id_list", job_id)
@@ -108,9 +106,7 @@ def store_job(self, job_id: Hashable, key: Hashable, value: Any) -> None:
         """
         self._redis.json().set(f"job:{job_id}", f".{key}", value)
 
-    def store_job_in(
-        self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None
-    ) -> None:
+    def store_job_in(self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None) -> None:
         """Stores the input arguments of the executed job.
 
         Args:
@@ -143,9 +139,7 @@ def store_job_metadata(self, job_id: Hashable, key: Hashable, value: Any) -> Non
         """
         if isinstance(value, Number) and math.isnan(value):
             value = "NaN"
-        logging.info(
-            f"Storing metadata for job:{job_id} with key:{key} and value:{value}"
-        )
+        logging.info(f"Storing metadata for job:{job_id} with key:{key} and value:{value}")
         self._redis.json().set(f"job:{job_id}", f".metadata.{key}", value)
 
     def load_all_search_ids(self) -> List[Hashable]:
@@ -199,9 +193,7 @@ def load_job(self, job_id: Hashable) -> dict:
         data = self._redis.json().get(f"job:{job_id}", ".")
         return data
 
-    def store_search_value(
-        self, search_id: Hashable, key: Hashable, value: Any
-    ) -> None:
+    def store_search_value(self, search_id: Hashable, key: Hashable, value: Any) -> None:
         """Stores the value corresponding to key for search_id.
 
         Args:
@@ -225,9 +217,7 @@ def load_search_value(self, search_id: Hashable, key: Hashable) -> Any:
         value = pickle.loads(value)
         return value
 
-    def load_metadata_from_all_jobs(
-        self, search_id: Hashable, key: Hashable
-    ) -> List[Any]:
+    def load_metadata_from_all_jobs(self, search_id: Hashable, key: Hashable) -> List[Any]:
         """Loads a given metadata value from all jobs.
 
         Args:
diff --git a/src/deephyper/evaluator/storage/_shared_memory_storage.py b/src/deephyper/evaluator/storage/_shared_memory_storage.py
index b9afac57..3df63881 100644
--- a/src/deephyper/evaluator/storage/_shared_memory_storage.py
+++ b/src/deephyper/evaluator/storage/_shared_memory_storage.py
@@ -7,8 +7,11 @@
 
 
 def SharedMemoryStorage():
-    """Creates a server process managing a MemoryStorage class and provides Proxy classes
-    to processes to which it is passed. It is made to be used in combination with :class:`deephyper.evaluator.ProcessPoolEvaluator`.
+    """Creates a server process managing a MemoryStorage class.
+
+    This also provides Proxy classes to processes to which it is passed. This
+    function, is made to be used in combination
+    with :class:`deephyper.evaluator.ProcessPoolEvaluator`.
     """
     manager = BaseManager()
     manager.start()
diff --git a/src/deephyper/evaluator/storage/_storage.py b/src/deephyper/evaluator/storage/_storage.py
index b7297e6f..ab42bd65 100644
--- a/src/deephyper/evaluator/storage/_storage.py
+++ b/src/deephyper/evaluator/storage/_storage.py
@@ -32,8 +32,10 @@ def create(method: str = "memory", method_kwargs: Dict = None) -> StorageType:
         """Static method allowing the creation of a storage client.
 
         Args:
-            method (str, optional): the type of storage client in ``["memory", "redis"]``. Defaults to "memory".
-            method_kwargs (Dict, optional): the client keyword-arguments parameters. Defaults to None.
+            method (str, optional):
+                The type of storage client in ``["memory", "redis"]``. Defaults to "memory".
+            method_kwargs (Dict, optional):
+                The client keyword-arguments parameters. Defaults to None.
 
         Raises:
             ValueError: if the type of requested storage client is not valid.
@@ -43,9 +45,7 @@ def create(method: str = "memory", method_kwargs: Dict = None) -> StorageType:
         """
         method_kwargs = method_kwargs if method_kwargs else {}
 
-        logging.info(
-            f"Creating Storage(method={method}, method_kwargs={method_kwargs}..."
-        )
+        logging.info(f"Creating Storage(method={method}, method_kwargs={method_kwargs}...")
 
         if method not in STORAGES.keys():
             val = ", ".join(STORAGES)
@@ -90,9 +90,7 @@ def create_new_job(self, search_id: Hashable) -> Hashable:
         """
 
     @abc.abstractmethod
-    def store_search_value(
-        self, search_id: Hashable, key: Hashable, value: Any
-    ) -> None:
+    def store_search_value(self, search_id: Hashable, key: Hashable, value: Any) -> None:
         """Stores the value corresponding to key for search_id.
 
         Args:
@@ -121,9 +119,7 @@ def store_job(self, job_id: Hashable, key: Hashable, value: Any) -> None:
         """
 
     @abc.abstractmethod
-    def store_job_in(
-        self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None
-    ) -> None:
+    def store_job_in(self, job_id: Hashable, args: Tuple = None, kwargs: Dict = None) -> None:
         """Stores the input arguments of the executed job.
 
         Args:
@@ -193,9 +189,7 @@ def load_job(self, job_id: Hashable) -> dict:
         """
 
     @abc.abstractmethod
-    def load_metadata_from_all_jobs(
-        self, search_id: Hashable, key: Hashable
-    ) -> List[Any]:
+    def load_metadata_from_all_jobs(self, search_id: Hashable, key: Hashable) -> List[Any]:
         """Loads a given metadata value from all jobs.
 
         Args:
diff --git a/src/deephyper/hpo/_cbo.py b/src/deephyper/hpo/_cbo.py
index 32412f5b..e02fc56c 100644
--- a/src/deephyper/hpo/_cbo.py
+++ b/src/deephyper/hpo/_cbo.py
@@ -50,7 +50,8 @@ def scheduler_periodic_exponential_decay(i, eta_0, num_dim, period, rate, delay)
         delay (int): delay of the decay (decaying starts after ``delay`` iterations).
 
     Returns:
-        tuple: an iterable of length 2 with the updated values at iteration ``i`` for ``[kappa, xi]``.
+        tuple: an iterable of length 2 with the updated values at iteration ``i`` for
+        ``[kappa, xi]``.
     """
     eta_i = eta_0 * np.exp(-rate * ((i - 1 - delay) % period))
     return eta_i
@@ -68,7 +69,8 @@ def scheduler_bandit(i, eta_0, num_dim, delta=0.05, lamb=0.2, delay=0):
         delay (int): delay of the scheduler (decaying starts after ``delay`` iterations).
 
     Returns:
-        tuple: an iterable of length 2 with the updated values at iteration ``i`` for ``[kappa, xi]``.
+        tuple: an iterable of length 2 with the updated values at iteration ``i`` for
+        ``[kappa, xi]``.
     """
     i = np.maximum(i + 1 - delay, 1)
     beta_i = 2 * np.log(num_dim * i**2 * np.pi**2 / (6 * delta)) * lamb
@@ -79,7 +81,10 @@ def scheduler_bandit(i, eta_0, num_dim, delta=0.05, lamb=0.2, delay=0):
 
 
 class CBO(Search):
-    """Centralized Bayesian Optimisation Search. It follows a manager-workers architecture where the manager runs the Bayesian optimization loop and workers execute parallel evaluations of the black-box function.
+    """Centralized Bayesian Optimisation Search.
+
+    It follows a manager-workers architecture where the manager runs the Bayesian optimization
+    loop and workers execute parallel evaluations of the black-box function.
 
     .. list-table::
         :widths: 25 25 25
@@ -104,57 +109,129 @@ class CBO(Search):
 
         random_state (int, optional): Random seed. Defaults to ``None``.
 
-        log_dir (str, optional): Log directory where search's results are saved. Defaults to ``"."``.
+        log_dir (str, optional): Log directory where search's results are saved. Defaults to
+            ``"."``.
 
         verbose (int, optional): Indicate the verbosity level of the search. Defaults to ``0``.
 
-        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the function. Defaults to ``None`` which does not use any stopper.
-
-        surrogate_model (Union[str,sklearn.base.RegressorMixin], optional): Surrogate model used by the Bayesian optimization. Can be a value in ``["RF", "GP", "ET", "MF", "GBRT", "DUMMY"]`` or a sklearn regressor. ``"ET"`` is for Extremely Randomized Trees which is the best compromise between speed and quality when performing a lot of parallel evaluations, i.e., reaching more than hundreds of evaluations. ``"GP"`` is for Gaussian-Process which is the best choice when maximizing the quality of iteration but quickly slow down when reaching hundreds of evaluations, also it does not support conditional search space. ``"RF"`` is for Random-Forest, slower than extremely randomized trees but with better mean estimate and worse epistemic uncertainty quantification capabilities. ``"GBRT"`` is for Gradient-Boosting Regression Tree, it has better mean estimate than other tree-based method worse uncertainty quantification capabilities and slower than ``"RF"``. Defaults to ``"ET"``.
-
-        surrogate_model_kwargs (dict, optional): Additional parameters to pass to the surrogate model. Defaults to ``None``.
-
-        acq_func (str, optional): Acquisition function used by the Bayesian optimization. Can be a value in ``["UCB", "EI", "PI", "gp_hedge"]``. Defaults to ``"UCB"``.
-
-        acq_optimizer (str, optional): Method used to minimze the acquisition function. Can be a value in ``["sampling", "lbfgs", "ga", "mixedga"]``. Defaults to ``"auto"``.
-
-        acq_optimizer_freq (int, optional): Frequency of optimization calls for the acquisition function. Defaults to ``10``, using optimizer every ``10`` surrogate model updates.
-
-        kappa (float, optional): Manage the exploration/exploitation tradeoff for the "UCB" acquisition function. Defaults to ``1.96`` which corresponds to 95% of the confidence interval.
-
-        xi (float, optional): Manage the exploration/exploitation tradeoff of ``"EI"`` and ``"PI"`` acquisition function. Defaults to ``0.001``.
-
-        n_points (int, optional): The number of configurations sampled from the search space to infer each batch of new evaluated configurations.
-
-        filter_duplicated (bool, optional): Force the optimizer to sample unique points until the search space is "exhausted" in the sens that no new unique points can be found given the sampling size ``n_points``. Defaults to ``True``.
-
-        update_prior (bool, optional): Update the prior of the surrogate model with the new evaluated points. Defaults to ``False``. Should be set to ``True`` when all objectives and parameters are continuous.
-
-        update_prior_quantile (float, optional): The quantile used to update the prior. Defaults to ``0.1``.
-
-        multi_point_strategy (str, optional): Definition of the constant value use for the Liar strategy. Can be a value in ``["cl_min", "cl_mean", "cl_max", "qUCB"]``. All ``"cl_..."`` strategies follow the constant-liar scheme, where if $N$ new points are requested, the surrogate model is re-fitted $N-1$ times with lies (respectively, the minimum, mean and maximum objective found so far; for multiple objectives, these are the minimum, mean and maximum of the individual objectives) to infer the acquisition function. Constant-Liar strategy have poor scalability because of this repeated re-fitting. The ``"qUCB"`` strategy is much more efficient by sampling a new $kappa$ value for each new requested point without re-fitting the model, but it is only compatible with ``acq_func == "UCB"``. Defaults to ``"cl_max"``.
-
-        n_jobs (int, optional): Number of parallel processes used to fit the surrogate model of the Bayesian optimization. A value of ``-1`` will use all available cores. Not used in ``surrogate_model`` if passed as own sklearn regressor. Defaults to ``1``.
-
-        n_initial_points (int, optional): Number of collected objectives required before fitting the surrogate-model. Defaults to ``10``.
-
-        initial_point_generator (str, optional): Sets an initial points generator. Can be either ``["random", "sobol", "halton", "hammersly", "lhs", "grid"]``. Defaults to ``"random"``.
-
-        initial_points (List[Dict], optional): A list of initial points to evaluate where each point is a dictionnary where keys are names of hyperparameters and values their corresponding choice. Defaults to ``None`` for them to be generated randomly from the search space.
-
-        filter_failures (str, optional): Replace objective of failed configurations by ``"min"`` or ``"mean"``. If ``"ignore"`` is passed then failed configurations will be filtered-out and not passed to the surrogate model. For multiple objectives, failure of any single objective will lead to treating that configuration as failed and each of these multiple objective will be replaced by their individual ``"min"`` or ``"mean"`` of past configurations. Defaults to ``"min"`` to replace failed configurations objectives by the running min of all objectives.
-
-        max_failures (int, optional): Maximum number of failed configurations allowed before observing a valid objective value when ``filter_failures`` is not equal to ``"ignore"``. Defaults to ``100``.
-
-        moo_lower_bounds (list, optional): List of lower bounds on the interesting range of objective values. Must be the same length as the number of obejctives. Defaults to ``None``, i.e., no bounds. Can bound only a single objective by providing ``None`` for all other values. For example, ``moo_lower_bounds=[None, 0.5, None]`` will explore all tradeoffs for the objectives at index 0 and 2, but only consider scores for objective 1 that exceed 0.5.
-
-        moo_scalarization_strategy (str, optional): Scalarization strategy used in multiobjective optimization. Can be a value in ``["Linear", "Chebyshev", "AugChebyshev", "PBI", "Quadratic"]``. Defaults to ``"Chebyshev"``. Typically, randomized methods should be used to capture entire Pareto front, unless there is a known target solution a priori. Additional details on each scalarization can be found in :mod:`deephyper.skopt.moo`.
-
-        moo_scalarization_weight (list, optional): Scalarization weights to be used in multiobjective optimization with length equal to the number of objective functions. Defaults to ``None`` for randomized weights. Only set if you want to fix the scalarization weights for a multiobjective HPS.
-
-        scheduler (dict, callable, optional): a function to manage the value of ``kappa, xi`` with iterations. Defaults to ``None`` which does not use any scheduler. The periodic exponential decay scheduler can be used with  ``scheduler={"type": "periodic-exp-decay", "period": 30, "rate": 0.1}``. The scheduler can also be a callable function with signature ``scheduler(i, eta_0, **kwargs)`` where ``i`` is the current iteration, ``eta_0`` is the initial value of ``[kappa, xi]`` and ``kwargs`` are other fixed parameters of the function. Instead of fixing the decay ``"rate"`` the final ``kappa`` or ``xi`` can be used ``{"type": "periodic-exp-decay", "period": 25, "kappa_final": 1.96}``.
-
-        objective_scaler (str, optional): a way to map the objective space to some other support for example to normalize it. Defaults to ``"auto"`` which automatically set it to "identity" for any surrogate model except "RF" which will use "quantile-uniform".
+        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the
+            function. Defaults to ``None`` which does not use any stopper.
+
+        surrogate_model (Union[str,sklearn.base.RegressorMixin], optional): Surrogate model used by
+            the Bayesian optimization. Can be a value in ``["RF", "GP", "ET", "MF", "GBRT",
+            "DUMMY"]`` or a sklearn regressor. ``"ET"`` is for Extremely Randomized Trees which is
+            the best compromise between speed and quality when performing a lot of parallel
+            evaluations, i.e., reaching more than hundreds of evaluations. ``"GP"`` is for Gaussian-
+            Process which is the best choice when maximizing the quality of iteration but quickly
+            slow down when reaching hundreds of evaluations, also it does not support conditional
+            search space. ``"RF"`` is for Random-Forest, slower than extremely randomized trees but
+            with better mean estimate and worse epistemic uncertainty quantification capabilities.
+            ``"GBRT"`` is for Gradient-Boosting Regression Tree, it has better mean estimate than
+            other tree-based method worse uncertainty quantification capabilities and slower than
+            ``"RF"``. Defaults to ``"ET"``.
+
+        surrogate_model_kwargs (dict, optional): Additional parameters to pass to the surrogate
+            model. Defaults to ``None``.
+
+        acq_func (str, optional): Acquisition function used by the Bayesian optimization. Can be a
+            value in ``["UCB", "EI", "PI", "gp_hedge"]``. Defaults to ``"UCB"``.
+
+        acq_optimizer (str, optional): Method used to minimze the acquisition function. Can be a
+            value in ``["sampling", "lbfgs", "ga", "mixedga"]``. Defaults to ``"auto"``.
+
+        acq_optimizer_freq (int, optional): Frequency of optimization calls for the acquisition
+            function. Defaults to ``10``, using optimizer every ``10`` surrogate model updates.
+
+        kappa (float, optional): Manage the exploration/exploitation tradeoff for the "UCB"
+            acquisition function. Defaults to ``1.96`` which corresponds to 95% of the confidence
+            interval.
+
+        xi (float, optional): Manage the exploration/exploitation tradeoff of ``"EI"`` and ``"PI"``
+            acquisition function. Defaults to ``0.001``.
+
+        n_points (int, optional): The number of configurations sampled from the search space to
+            infer each batch of new evaluated configurations.
+
+        filter_duplicated (bool, optional): Force the optimizer to sample unique points until the
+            search space is "exhausted" in the sens that no new unique points can be found given
+            the sampling size ``n_points``. Defaults to ``True``.
+
+        update_prior (bool, optional): Update the prior of the surrogate model with the new
+            evaluated points. Defaults to ``False``. Should be set to ``True`` when all objectives
+            and parameters are continuous.
+
+        update_prior_quantile (float, optional): The quantile used to update the prior.
+            Defaults to ``0.1``.
+
+        multi_point_strategy (str, optional): Definition of the constant value use for the Liar
+            strategy. Can be a value in ``["cl_min", "cl_mean", "cl_max", "qUCB"]``. All
+            ``"cl_..."`` strategies follow the constant-liar scheme, where if $N$ new points are
+            requested, the surrogate model is re-fitted $N-1$ times with lies (respectively, the
+            minimum, mean and maximum objective found so far; for multiple objectives, these are
+            the minimum, mean and maximum of the individual objectives) to infer the acquisition
+            function. Constant-Liar strategy have poor scalability because of this repeated re-
+            fitting. The ``"qUCB"`` strategy is much more efficient by sampling a new $kappa$ value
+            for each new requested point without re-fitting the model, but it is only compatible
+            with ``acq_func == "UCB"``. Defaults to ``"cl_max"``.
+
+        n_jobs (int, optional): Number of parallel processes used to fit the surrogate model of the
+            Bayesian optimization. A value of ``-1`` will use all available cores. Not used in
+            ``surrogate_model`` if passed as own sklearn regressor. Defaults to ``1``.
+
+        n_initial_points (int, optional): Number of collected objectives required before fitting
+            the surrogate-model. Defaults to ``10``.
+
+        initial_point_generator (str, optional): Sets an initial points generator. Can be either
+            ``["random", "sobol", "halton", "hammersly", "lhs", "grid"]``. Defaults to ``"random"``.
+
+        initial_points (List[Dict], optional): A list of initial points to evaluate where each
+            point is a dictionnary where keys are names of hyperparameters and values their
+            corresponding choice. Defaults to ``None`` for them to be generated randomly from
+            the search space.
+
+        filter_failures (str, optional): Replace objective of failed configurations by ``"min"``
+            or ``"mean"``. If ``"ignore"`` is passed then failed configurations will be
+            filtered-out and not passed to the surrogate model. For multiple objectives, failure of
+            any single objective will lead to treating that configuration as failed and each of
+            these multiple objective will be replaced by their individual ``"min"`` or ``"mean"``
+            of past configurations. Defaults to ``"min"`` to replace failed configurations
+            objectives by the running min of all objectives.
+
+        max_failures (int, optional): Maximum number of failed configurations allowed before
+            observing a valid objective value when ``filter_failures`` is not equal to
+            ``"ignore"``. Defaults to ``100``.
+
+        moo_lower_bounds (list, optional): List of lower bounds on the interesting range of
+            objective values. Must be the same length as the number of obejctives. Defaults to
+            ``None``, i.e., no bounds. Can bound only a single objective by providing ``None``
+            for all other values. For example, ``moo_lower_bounds=[None, 0.5, None]`` will explore
+            all tradeoffs for the objectives at index 0 and 2, but only consider scores for
+            objective 1 that exceed 0.5.
+
+        moo_scalarization_strategy (str, optional): Scalarization strategy used in multiobjective
+            optimization. Can be a value in ``["Linear", "Chebyshev", "AugChebyshev", "PBI",
+            "Quadratic"]``. Defaults to ``"Chebyshev"``. Typically, randomized methods should be
+            used to capture entire Pareto front, unless there is a known target solution a priori.
+            Additional details on each scalarization can be found in :mod:`deephyper.skopt.moo`.
+
+        moo_scalarization_weight (list, optional): Scalarization weights to be used in
+            multiobjective optimization with length equal to the number of objective functions.
+            Defaults to ``None`` for randomized weights. Only set if you want to fix the
+            scalarization weights for a multiobjective HPS.
+
+        scheduler (dict, callable, optional): a function to manage the value of ``kappa, xi`` with
+            iterations. Defaults to ``None`` which does not use any scheduler. The periodic
+            exponential decay scheduler can be used with  ``scheduler={"type":
+            "periodic-exp-decay", "period": 30, "rate": 0.1}``. The scheduler can also be a
+            callable function with signature ``scheduler(i, eta_0, **kwargs)`` where ``i`` is the
+            current iteration, ``eta_0`` is the initial value of ``[kappa, xi]`` and ``kwargs`` are
+            other fixed parameters of the function. Instead of fixing the decay ``"rate"`` the
+            final ``kappa`` or ``xi`` can be used ``{"type": "periodic-exp-decay", "period": 25,
+            "kappa_final": 1.96}``.
+
+        objective_scaler (str, optional): a way to map the objective space to some other support
+            for example to normalize it. Defaults to ``"auto"`` which automatically set it to
+            "identity" for any surrogate model except "RF" which will use "quantile-uniform".
     """
 
     def __init__(
@@ -223,7 +300,8 @@ def __init__(
             base_estimator = surrogate_model
         else:
             raise ValueError(
-                f"Parameter 'surrogate_model={surrogate_model}' should have a value in {surrogate_model_allowed}, or be a sklearn regressor!"
+                f"Parameter 'surrogate_model={surrogate_model}' should have a value in "
+                f"{surrogate_model_allowed}, or be a sklearn regressor!"
             )
 
         acq_func_allowed = [
@@ -258,9 +336,7 @@ def __init__(
             )
 
         if type(max_failures) is not int:
-            raise ValueError(
-                f"Parameter max_failures={max_failures} should be an integer value!"
-            )
+            raise ValueError(f"Parameter max_failures={max_failures} should be an integer value!")
 
         # Initialize lower bounds for objectives
         if moo_lower_bounds is None:
@@ -269,12 +345,12 @@ def __init__(
             [isinstance(lbi, numbers.Number) or lbi is None for lbi in moo_lower_bounds]
         ):
             self._moo_upper_bounds = [
-                -lbi if isinstance(lbi, numbers.Number) else None
-                for lbi in moo_lower_bounds
+                -lbi if isinstance(lbi, numbers.Number) else None for lbi in moo_lower_bounds
             ]
         else:
             raise ValueError(
-                f"Parameter 'moo_lower_bounds={moo_lower_bounds}' is invalid. Must be None or a list"
+                f"Parameter 'moo_lower_bounds={moo_lower_bounds}' is invalid. Must be None or "
+                f"a list"
             )
 
         moo_scalarization_strategy_allowed = list(moo_functions.keys())
@@ -283,7 +359,9 @@ def __init__(
             or isinstance(moo_scalarization_strategy, MoScalarFunction)
         ):
             raise ValueError(
-                f"Parameter 'moo_scalarization_strategy={moo_scalarization_strategy}' should have a value in {moo_scalarization_strategy_allowed} or be a subclass of deephyper.skopt.moo.MoScalarFunction!"
+                f"Parameter 'moo_scalarization_strategy={moo_scalarization_strategy}' should have a"
+                f" value in {moo_scalarization_strategy_allowed} or be a subclass of "
+                f"deephyper.skopt.moo.MoScalarFunction!"
             )
         self._moo_scalarization_strategy = moo_scalarization_strategy
         self._moo_scalarization_weight = moo_scalarization_weight
@@ -299,7 +377,8 @@ def __init__(
         ]
         if multi_point_strategy not in multi_point_strategy_allowed:
             raise ValueError(
-                f"Parameter multi_point_strategy={multi_point_strategy} should have a value in {multi_point_strategy_allowed}!"
+                f"Parameter multi_point_strategy={multi_point_strategy} should have a value "
+                f"in {multi_point_strategy_allowed}!"
             )
 
         self._n_initial_points = n_initial_points
@@ -340,9 +419,7 @@ def __init__(
                 "update_prior": update_prior,
                 "update_prior_quantile": 1 - update_prior_quantile,
                 "n_jobs": n_jobs,
-                "filter_failures": MAP_filter_failures.get(
-                    filter_failures, filter_failures
-                ),
+                "filter_failures": MAP_filter_failures.get(filter_failures, filter_failures),
                 "max_failures": max_failures,
                 "acq_optimizer_freq": acq_optimizer_freq,
             },
@@ -424,9 +501,7 @@ def _apply_scheduler(self, i):
         if self.scheduler is not None:
             kappa, xi = self.scheduler(i)
             values = {"kappa": kappa, "xi": xi}
-            logging.info(
-                f"Updated exploration-exploitation policy with {values} from scheduler"
-            )
+            logging.info(f"Updated exploration-exploitation policy with {values} from scheduler")
             self._opt.acq_func_kwargs.update(values)
 
     def _ask(self, n: int = 1) -> List[Dict]:
@@ -447,7 +522,7 @@ def _tell(self, results: List[HPOJob]):
         """Tell the search the results of the evaluations.
 
         Args:
-            observations (dict): a dictionary containing the results of the evaluations.
+            results (List[HPOJob]): a dictionary containing the results of the evaluations.
         """
         # Transform configurations to list to fit optimizer
         logging.info("Transforming received configurations to list...")
@@ -468,10 +543,7 @@ def _tell(self, results: List[HPOJob]):
             elif (type(obj) is str and "F" == obj[0]) or any(
                 type(obj_i) is str and "F" == obj_i[0] for obj_i in obj
             ):
-                if (
-                    self._opt_kwargs["acq_optimizer_kwargs"]["filter_failures"]
-                    == "ignore"
-                ):
+                if self._opt_kwargs["acq_optimizer_kwargs"]["filter_failures"] == "ignore":
                     continue
                 else:
                     opt_X.append(x)
@@ -505,7 +577,8 @@ def _get_surrogate_model(
 
         Args:
             name (str): name of the surrogate model.
-            n_jobs (int): number of parallel processes to distribute the computation of the surrogate model.
+            n_jobs (int): number of parallel processes to distribute the computation of the
+                surrogate model.
             random_state (int): random seed.
             surrogate_model_kwargs (dict): additional parameters to pass to the surrogate model.
 
@@ -591,7 +664,8 @@ def _get_surrogate_model(
                 )
             except AttributeError:
                 raise deephyper.core.exceptions.MissingRequirementError(
-                    "Installing 'deephyper/scikit-garden' is required to use MondrianForest (MF) regressor as a surrogate model!"
+                    "Installing 'deephyper/scikit-garden' is required to use MondrianForest (MF) "
+                    "regressor as a surrogate model!"
                 )
         # Model: Gradient Boosting Regression Tree (based on quantiles)
         # https://scikit-learn.org/stable/modules/generated/sklearn.ensemble.GradientBoostingRegressor.html
@@ -697,16 +771,20 @@ def fit_surrogate(self, df):
             else:
                 y = df.filter(regex=r"^objective_\d+$").values.tolist()
         except KeyError:
-            raise ValueError(
-                "Incompatible dataframe 'df' to fit surrogate model of CBO."
-            )
+            raise ValueError("Incompatible dataframe 'df' to fit surrogate model of CBO.")
 
         self._opt.tell(x, [np.negative(yi).tolist() for yi in y])
 
     def fit_generative_model(
         self, df, q=0.90, n_samples=100, verbose=False, **generative_model_kwargs
     ):
-        """Learn the distribution of hyperparameters for the top-``(1-q)x100%`` configurations and sample from this distribution. It can be used for transfer learning. For multiobjective problems, this function computes the top-``(1-q)x100%`` configurations in terms of their ranking with respect to pareto efficiency: all points on the first non-dominated pareto front have rank 1 and in general, points on the k'th non-dominated front have rank k.
+        """Fits a generative model for sampling during BO.
+
+        Learn the distribution of hyperparameters for the top-``(1-q)x100%`` configurations and
+        sample from this distribution. It can be used for transfer learning. For multiobjective
+        problems, this function computes the top-``(1-q)x100%`` configurations in terms of their
+        ranking with respect to pareto efficiency: all points on the first non-dominated pareto
+        front have rank 1 and in general, points on the k'th non-dominated front have rank k.
 
         Example Usage:
 
@@ -715,20 +793,30 @@ def fit_generative_model(
 
         Args:
             df (str|DataFrame): a dataframe or path to CSV from a previous search.
-            q (float, optional): the quantile defined the set of top configurations used to bias the search. Defaults to ``0.90`` which select the top-10% configurations from ``df``.
+
+            q (float, optional): the quantile defined the set of top configurations used to bias
+                the search. Defaults to ``0.90`` which select the top-10% configurations from
+                ``df``.
+
             n_samples (int, optional): the number of samples used to score the generative model.
-            verbose (bool, optional): If set to ``True`` it will print the score of the generative model. Defaults to ``False``.
-            generative_model_kwargs (dict, optional): additional parameters to pass to the generative model.
+
+            verbose (bool, optional): If set to ``True`` it will print the score of the generative
+                model. Defaults to ``False``.
+
+            generative_model_kwargs (dict, optional): additional parameters to pass to the
+                generative model.
 
         Returns:
-            tuple: ``score, model`` which are a metric which measures the quality of the learned generated-model and the generative model respectively.
+            tuple: ``score, model`` which are a metric which measures the quality of the learned
+                generated-model and the generative model respectively.
         """
         # to make sdv optional
         try:
             import sdv
         except ModuleNotFoundError:
             raise deephyper.core.exceptions.MissingRequirementError(
-                "Installing 'sdv' is required to use 'fit_generative_model' please run 'pip install \"deephyper[sdv]\"'"
+                "Installing 'sdv' is required to use 'fit_generative_model' please run "
+                "'pip install \"deephyper[sdv]\"'"
             )
 
         from sdv.evaluation.single_table import evaluate_quality
@@ -740,14 +828,16 @@ def fit_generative_model(
 
         if len(df) < 10:
             raise ValueError(
-                f"The passed DataFrame contains only {len(df)} results when a minimum of 10 is required!"
+                f"The passed DataFrame contains only {len(df)} results when a minimum of "
+                f"10 is required!"
             )
 
         # !avoid error linked to `n_components=10` a parameter of generative model used
         q_max = 1 - 10 / len(df)
         if q_max < q:
             warnings.warn(
-                f"The value of q={q} is replaced by q_max={q_max} because a minimum of 10 samples are required to perform transfer-learning!",
+                f"The value of q={q} is replaced by q_max={q_max} because a minimum of 10 samples "
+                f"sare required to perform transfer-learning!",
                 category=UserWarning,
             )
             q = q_max
@@ -774,9 +864,7 @@ def fit_generative_model(
             req_df = df.loc[top]
 
         req_df = req_df[["job_id"] + hp_cols]
-        req_df = req_df.rename(
-            columns={k: k[2:] for k in hp_cols if k.startswith("p:")}
-        )
+        req_df = req_df.rename(columns={k: k[2:] for k in hp_cols if k.startswith("p:")})
 
         # constraints
         scalar_constraints = []
@@ -786,8 +874,8 @@ def fit_generative_model(
                 hp = self._problem.space[hp_name]
 
                 # TODO: Categorical and Ordinal are both considered non-ordered for SDV
-                # TODO: it could be useful to use the "category"  type of Pandas and the ordered=True/False argument
-                # TODO: to extend the capability of SDV
+                # TODO: it could be useful to use the "category"  type of Pandas and the
+                # TODO: ordered=True/False argument to extend the capability of SDV
                 if isinstance(hp, csh.CategoricalHyperparameter) or isinstance(
                     hp, csh.OrdinalHyperparameter
                 ):
@@ -856,8 +944,15 @@ def fit_search_space(self, df, fac_numerical=0.125, fac_categorical=10):
 
         Args:
             df (str|DataFrame): a checkpoint from a previous search.
-            fac_numerical (float): the factor used to compute the sigma of a truncated normal distribution based on ``sigma = max(1.0, (upper - lower) * fac_numerical)``. A small large factor increase exploration while a small factor increase exploitation around the best-configuration from the ``df`` parameter.
-            fac_categorical (float): the weight given to a categorical feature part of the best configuration. A large weight ``> 1`` increase exploitation while a small factor close to ``1`` increase exploration.
+
+            fac_numerical (float): the factor used to compute the sigma of a truncated normal
+                distribution based on ``sigma = max(1.0, (upper - lower) * fac_numerical)``. A
+                small large factor increase exploration while a small factor increase exploitation
+                around the best-configuration from the ``df`` parameter.
+
+            fac_categorical (float): the weight given to a categorical feature part of the best
+                configuration. A large weight ``> 1`` increase exploitation while a small factor
+                close to ``1`` increase exploration.
         """
         if type(df) is str and df[-4:] == ".csv":
             df = pd.read_csv(df)
@@ -887,9 +982,7 @@ def fit_search_space(self, df, fac_numerical=0.125, fac_categorical=10):
             best_index = np.argmax(res_df["objective"].values)
             best_param = res_df.iloc[best_index]
         else:
-            best_index = non_dominated_set(
-                -np.asarray(res_df[objcol]), return_mask=False
-            )[0]
+            best_index = non_dominated_set(-np.asarray(res_df[objcol]), return_mask=False)[0]
             best_param = res_df.iloc[best_index]
 
         cst_new = CS.ConfigurationSpace(seed=self._random_state.randint(0, 2**31))
@@ -944,9 +1037,7 @@ def fit_search_space(self, df, fac_numerical=0.125, fac_categorical=10):
                     logging.warning(f"Not fitting {hp} because it is not supported!")
                     cst_new.add(hp)
             else:
-                logging.warning(
-                    f"Not fitting {hp} because it was not found in the dataframe!"
-                )
+                logging.warning(f"Not fitting {hp} because it was not found in the dataframe!")
                 cst_new.add(hp)
 
         # For conditions
@@ -972,10 +1063,7 @@ def fit_search_space(self, df, fac_numerical=0.125, fac_categorical=10):
                 for comp in cond.components:
                     cond_list.append(self._return_forbid(comp, cst_new))
                 cond_new = CS.ForbiddenAndConjunction(*cond_list)
-            elif (
-                type(cond) is CS.ForbiddenEqualsClause
-                or type(cond) is CS.ForbiddenInClause
-            ):
+            elif type(cond) is CS.ForbiddenEqualsClause or type(cond) is CS.ForbiddenInClause:
                 cond_new = self._return_forbid(cond, cst_new)
             else:
                 logging.warning(f"Forbidden {type(cond)} is not implemented!")
@@ -984,7 +1072,9 @@ def fit_search_space(self, df, fac_numerical=0.125, fac_categorical=10):
         self._opt_kwargs["dimensions"] = cst_new
 
     def _to_dict(self, x: list) -> dict:
-        """Transform a list of hyperparameter values to a ``dict`` where keys are hyperparameters names and values are hyperparameters values.
+        """Transform a list of hyperparameter values to a ``dict``.
+
+        The keys are hyperparameters names and values are hyperparameters values.
 
         Args:
             x (list): a list of hyperparameter values.
diff --git a/src/deephyper/hpo/_eds.py b/src/deephyper/hpo/_eds.py
index e7bbf884..6edd7506 100644
--- a/src/deephyper/hpo/_eds.py
+++ b/src/deephyper/hpo/_eds.py
@@ -2,7 +2,10 @@
 
 
 class ExperimentalDesignSearch(CBO):
-    """Centralized Experimental Design Search. It follows a manager-workers architecture where the manager runs the sampling process and workers execute parallel evaluations of the black-box function.
+    """Centralized Experimental Design Search.
+
+    It follows a manager-workers architecture where the manager runs the sampling process and
+    workers execute parallel evaluations of the black-box function.
 
     .. list-table::
         :widths: 25 25 25
@@ -28,11 +31,13 @@ class ExperimentalDesignSearch(CBO):
 
         random_state (int, optional): Random seed. Defaults to ``None``.
 
-        log_dir (str, optional): Log directory where search's results are saved. Defaults to ``"."``.
+        log_dir (str, optional): Log directory where search's results are saved. Defaults to
+            ``"."``.
 
         verbose (int, optional): Indicate the verbosity level of the search. Defaults to ``0``.
 
-        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the function. Defaults to ``None`` which does not use any stopper.
+        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the
+            function. Defaults to ``None`` which does not use any stopper.
 
         n_points (int, optional): Number of points to sample. Defaults to ``None``.
 
@@ -60,9 +65,7 @@ def __init__(
         initial_points=None,
     ):
         if n_points is None:
-            raise ValueError(
-                "n_points must be specified for the ExperimentalDesignSearch."
-            )
+            raise ValueError("n_points must be specified for the ExperimentalDesignSearch.")
         super().__init__(
             problem,
             evaluator,
diff --git a/src/deephyper/hpo/_mpi_dbo.py b/src/deephyper/hpo/_mpi_dbo.py
index 2b197021..398edfcb 100644
--- a/src/deephyper/hpo/_mpi_dbo.py
+++ b/src/deephyper/hpo/_mpi_dbo.py
@@ -41,55 +41,124 @@ class MPIDistributedBO(CBO):
 
         random_state (int, optional): Random seed. Defaults to ``None``.
 
-        log_dir (str, optional): Log directory where search's results are saved. Defaults to ``"."``.
+        log_dir (str, optional): Log directory where search's results are saved.
+            Defaults to ``"."``.
 
         verbose (int, optional): Indicate the verbosity level of the search. Defaults to ``0``.
 
-        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the function. Defaults to ``None`` which does not use any stopper.
-
-        surrogate_model (Union[str,sklearn.base.RegressorMixin], optional): Surrogate model used by the Bayesian optimization. Can be a value in ``["RF", "GP", "ET", "MF", "GBRT", "DUMMY"]`` or a sklearn regressor. ``"ET"`` is for Extremely Randomized Trees which is the best compromise between speed and quality when performing a lot of parallel evaluations, i.e., reaching more than hundreds of evaluations. ``"GP"`` is for Gaussian-Process which is the best choice when maximizing the quality of iteration but quickly slow down when reaching hundreds of evaluations, also it does not support conditional search space. ``"RF"`` is for Random-Forest, slower than extremely randomized trees but with better mean estimate and worse epistemic uncertainty quantification capabilities. ``"GBRT"`` is for Gradient-Boosting Regression Tree, it has better mean estimate than other tree-based method worse uncertainty quantification capabilities and slower than ``"RF"``. Defaults to ``"ET"``.
-
-        acq_func (str, optional): Acquisition function used by the Bayesian optimization. Can be a value in ``["UCB", "EI", "PI", "gp_hedge"]``. Defaults to ``"UCB"``.
-
-        acq_optimizer (str, optional): Method used to minimze the acquisition function. Can be a value in ``["sampling", "lbfgs"]``. Defaults to ``"auto"``.
-
-        acq_optimizer_freq (int, optional): Frequency of optimization calls for the acquisition function. Defaults to ``10``, using optimizer every ``10`` surrogate model updates.
-
-        kappa (float, optional): Manage the exploration/exploitation tradeoff for the "UCB" acquisition function. Defaults to ``1.96`` which corresponds to 95% of the confidence interval.
-
-        xi (float, optional): Manage the exploration/exploitation tradeoff of ``"EI"`` and ``"PI"`` acquisition function. Defaults to ``0.001``.
-
-        n_points (int, optional): The number of configurations sampled from the search space to infer each batch of new evaluated configurations.
-
-        filter_duplicated (bool, optional): Force the optimizer to sample unique points until the search space is "exhausted" in the sens that no new unique points can be found given the sampling size ``n_points``. Defaults to ``True``.
-
-        update_prior (bool, optional): Update the prior of the surrogate model with the new evaluated points. Defaults to ``False``. Should be set to ``True`` when all objectives and parameters are continuous.
-
-        update_prior_quantile (float, optional): The quantile used to update the prior. Defaults to ``0.1``.
-
-        multi_point_strategy (str, optional): Definition of the constant value use for the Liar strategy. Can be a value in ``["cl_min", "cl_mean", "cl_max", "qUCB"]``. All ``"cl_..."`` strategies follow the constant-liar scheme, where if $N$ new points are requested, the surrogate model is re-fitted $N-1$ times with lies (respectively, the minimum, mean and maximum objective found so far; for multiple objectives, these are the minimum, mean and maximum of the individual objectives) to infer the acquisition function. Constant-Liar strategy have poor scalability because of this repeated re-fitting. The ``"qUCB"`` strategy is much more efficient by sampling a new $kappa$ value for each new requested point without re-fitting the model, but it is only compatible with ``acq_func == "UCB"``. Defaults to ``"cl_max"``.
-
-        n_jobs (int, optional): Number of parallel processes used to fit the surrogate model of the Bayesian optimization. A value of ``-1`` will use all available cores. Not used in ``surrogate_model`` if passed as own sklearn regressor. Defaults to ``1``.
-
-        n_initial_points (int, optional): Number of collected objectives required before fitting the surrogate-model. Defaults to ``10``.
-
-        initial_point_generator (str, optional): Sets an initial points generator. Can be either ``["random", "sobol", "halton", "hammersly", "lhs", "grid"]``. Defaults to ``"random"``.
-
-        initial_points (List[Dict], optional): A list of initial points to evaluate where each point is a dictionnary where keys are names of hyperparameters and values their corresponding choice. Defaults to ``None`` for them to be generated randomly from the search space.
-
-        filter_failures (str, optional): Replace objective of failed configurations by ``"min"`` or ``"mean"``. If ``"ignore"`` is passed then failed configurations will be filtered-out and not passed to the surrogate model. For multiple objectives, failure of any single objective will lead to treating that configuration as failed and each of these multiple objective will be replaced by their individual ``"min"`` or ``"mean"`` of past configurations. Defaults to ``"mean"`` to replace by failed configurations by the running mean of objectives.
-
-        max_failures (int, optional): Maximum number of failed configurations allowed before observing a valid objective value when ``filter_failures`` is not equal to ``"ignore"``. Defaults to ``100``.
-
-        moo_lower_bounds (list, optional): List of lower bounds on the interesting range of objective values. Must be the same length as the number of obejctives. Defaults to ``None``, i.e., no bounds. Can bound only a single objective by providing ``None`` for all other values. For example, ``moo_lower_bounds=[None, 0.5, None]`` will explore all tradeoffs for the objectives at index 0 and 2, but only consider scores for objective 1 that exceed 0.5.
-
-        moo_scalarization_strategy (str, optional): Scalarization strategy used in multiobjective optimization. Can be a value in ``["Linear", "Chebyshev", "AugChebyshev", "PBI", "Quadratic"]``. Defaults to ``"Chebyshev"``. Typically, randomized methods should be used to capture entire Pareto front, unless there is a known target solution a priori. Additional details on each scalarization can be found in :mod:`deephyper.skopt.moo`.
-
-        moo_scalarization_weight (list, optional): Scalarization weights to be used in multiobjective optimization with length equal to the number of objective functions. Defaults to ``None`` for randomized weights. Only set if you want to fix the scalarization weights for a multiobjective HPS.
-
-        scheduler (dict, callable, optional): a function to manage the value of ``kappa, xi`` with iterations. Defaults to ``None`` which does not use any scheduler. The periodic exponential decay scheduler can be used with  ``scheduler={"type": "periodic-exp-decay", "period": 30, "rate": 0.1}``. The scheduler can also be a callable function with signature ``scheduler(i, eta_0, **kwargs)`` where ``i`` is the current iteration, ``eta_0`` is the initial value of ``[kappa, xi]`` and ``kwargs`` are other fixed parameters of the function.
-
-        objective_scaler (str, optional): a way to map the objective space to some other support for example to normalize it. Defaults to ``"auto"`` which automatically set it to "identity" for any surrogate model except "RF" which will use "quantile-uniform".
+        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the
+            function. Defaults to ``None`` which does not use any stopper.
+
+        surrogate_model (Union[str,sklearn.base.RegressorMixin], optional): Surrogate model used by
+            the Bayesian optimization. Can be a value in ``["RF", "GP", "ET", "MF", "GBRT",
+            "DUMMY"]`` or a sklearn regressor. ``"ET"`` is for Extremely Randomized Trees which is
+            the best compromise between speed and quality when performing a lot of parallel
+            evaluations, i.e., reaching more than hundreds of evaluations. ``"GP"`` is for Gaussian-
+            Process which is the best choice when maximizing the quality of iteration but quickly
+            slow down when reaching hundreds of evaluations, also it does not support conditional
+            search space. ``"RF"`` is for Random-Forest, slower than extremely randomized trees but
+            with better mean estimate and worse epistemic uncertainty quantification capabilities.
+            ``"GBRT"`` is for Gradient-Boosting Regression Tree, it has better mean estimate than
+            other tree-based method worse uncertainty quantification capabilities and slower than
+            ``"RF"``. Defaults to ``"ET"``.
+
+        acq_func (str, optional): Acquisition function used by the Bayesian optimization. Can be a
+            value in ``["UCB", "EI", "PI", "gp_hedge"]``. Defaults to ``"UCB"``.
+
+        acq_optimizer (str, optional): Method used to minimze the acquisition function. Can be a
+            value in ``["sampling", "lbfgs"]``. Defaults to ``"auto"``.
+
+        acq_optimizer_freq (int, optional): Frequency of optimization calls for the acquisition
+            function. Defaults to ``10``, using optimizer every ``10`` surrogate model updates.
+
+        kappa (float, optional): Manage the exploration/exploitation tradeoff for the "UCB"
+            acquisition function. Defaults to ``1.96`` which corresponds to 95% of the confidence
+            interval.
+
+        xi (float, optional): Manage the exploration/exploitation tradeoff of ``"EI"`` and
+            ``"PI"`` acquisition function. Defaults to ``0.001``.
+
+        n_points (int, optional): The number of configurations sampled from the search space to
+            infer each batch of new evaluated configurations.
+
+        filter_duplicated (bool, optional): Force the optimizer to sample unique points until the
+            search space is "exhausted" in the sens that no new unique points can be found given
+            the sampling size ``n_points``. Defaults to ``True``.
+
+        update_prior (bool, optional): Update the prior of the surrogate model with the new
+            evaluated points. Defaults to ``False``. Should be set to ``True`` when all objectives
+            and parameters are continuous.
+
+        update_prior_quantile (float, optional): The quantile used to update the prior.
+            Defaults to ``0.1``.
+
+        multi_point_strategy (str, optional): Definition of the constant value use for the Liar
+            strategy. Can be a value in ``["cl_min", "cl_mean", "cl_max", "qUCB"]``. All
+            ``"cl_..."`` strategies follow the constant-liar scheme, where if $N$ new points are
+            requested, the surrogate model is re-fitted $N-1$ times with lies (respectively, the
+            minimum, mean and maximum objective found so far; for multiple objectives, these are
+            the minimum, mean and maximum of the individual objectives) to infer the acquisition
+            function. Constant-Liar strategy have poor scalability because of this repeated re-
+            fitting. The ``"qUCB"`` strategy is much more efficient by sampling a new $kappa$
+            value for each new requested point without re-fitting the model, but it is only
+            compatible with ``acq_func == "UCB"``. Defaults to ``"cl_max"``.
+
+        n_jobs (int, optional): Number of parallel processes used to fit the surrogate model of
+            the Bayesian optimization. A value of ``-1`` will use all available cores. Not used
+            in ``surrogate_model`` if passed as own sklearn regressor. Defaults to ``1``.
+
+        n_initial_points (int, optional): Number of collected objectives required before fitting
+            the surrogate-model. Defaults to ``10``.
+
+        initial_point_generator (str, optional): Sets an initial points generator. Can be either
+            ``["random", "sobol", "halton", "hammersly", "lhs", "grid"]``. Defaults to ``"random"``.
+
+        initial_points (List[Dict], optional): A list of initial points to evaluate where each
+            point is a dictionnary where keys are names of hyperparameters and values their
+            corresponding choice. Defaults to ``None`` for them to be generated randomly from
+            the search space.
+
+        filter_failures (str, optional): Replace objective of failed configurations by ``"min"`` or
+            ``"mean"``. If ``"ignore"`` is passed then failed configurations will be filtered-out
+            and not passed to the surrogate model. For multiple objectives, failure of any single
+            objective will lead to treating that configuration as failed and each of these multiple
+            objective will be replaced by their individual ``"min"`` or ``"mean"`` of past
+            configurations. Defaults to ``"mean"`` to replace by failed configurations by the
+            running mean of objectives.
+
+        max_failures (int, optional): Maximum number of failed configurations allowed before
+            observing a valid objective value when ``filter_failures`` is not equal to
+            ``"ignore"``. Defaults to ``100``.
+
+        moo_lower_bounds (list, optional): List of lower bounds on the interesting range of
+            objective values. Must be the same length as the number of obejctives. Defaults to
+            ``None``, i.e., no bounds. Can bound only a single objective by providing ``None``
+            for all other values. For example, ``moo_lower_bounds=[None, 0.5, None]`` will explore
+            all tradeoffs for the objectives at index 0 and 2, but only consider scores for
+            objective 1 that exceed 0.5.
+
+        moo_scalarization_strategy (str, optional): Scalarization strategy used in multiobjective
+            optimization. Can be a value in ``["Linear", "Chebyshev", "AugChebyshev", "PBI",
+            "Quadratic"]``. Defaults to ``"Chebyshev"``. Typically, randomized methods should be
+            used to capture entire Pareto front, unless there is a known target solution a priori.
+            Additional details on each scalarization can be found in :mod:`deephyper.skopt.moo`.
+
+        moo_scalarization_weight (list, optional): Scalarization weights to be used in
+            multiobjective optimization with length equal to the number of objective functions.
+            Defaults to ``None`` for randomized weights. Only set if you want to fix the
+            scalarization weights for a multiobjective HPS.
+
+        scheduler (dict, callable, optional): a function to manage the value of ``kappa, xi`` with
+            iterations. Defaults to ``None`` which does not use any scheduler. The periodic
+            exponential decay scheduler can be used with  ``scheduler={"type":
+            "periodic-exp-decay", "period": 30, "rate": 0.1}``. The scheduler can also be a
+            callable function with signature ``scheduler(i, eta_0, **kwargs)`` where ``i`` is the
+            current iteration, ``eta_0`` is the initial value of ``[kappa, xi]`` and ``kwargs``
+            are other fixed parameters of the function.
+
+        objective_scaler (str, optional): a way to map the objective space to some other support
+            for example to normalize it. Defaults to ``"auto"`` which automatically set it to
+            "identity" for any surrogate model except "RF" which will use "quantile-uniform".
 
         comm (Comm, optional): communicator used with MPI. Defaults to ``None`` for  ``COMM_WORLD``.
     """
@@ -147,12 +216,12 @@ def __init__(
             random_state = np.random.RandomState()
 
         if acq_func[0] == "q":
-            kappa = scipy.stats.expon.rvs(
-                size=self.size, scale=kappa, random_state=random_state
-            )[self.rank]
-            xi = scipy.stats.expon.rvs(
-                size=self.size, scale=xi, random_state=random_state
-            )[self.rank]
+            kappa = scipy.stats.expon.rvs(size=self.size, scale=kappa, random_state=random_state)[
+                self.rank
+            ]
+            xi = scipy.stats.expon.rvs(size=self.size, scale=xi, random_state=random_state)[
+                self.rank
+            ]
             acq_func = acq_func[1:]
         elif acq_func[0] == "b":
             acq_func[0] = acq_func[1:]
@@ -251,7 +320,8 @@ def check_evaluator(self, evaluator):
 
             else:
                 raise TypeError(
-                    f"The evaluator shoud be an instance of deephyper.evaluator.Evaluator by is {type(evaluator)}!"
+                    f"The evaluator shoud be an instance of deephyper.evaluator.Evaluator "
+                    f"by is {type(evaluator)}!"
                 )
         else:
             self._evaluator = evaluator
@@ -311,7 +381,9 @@ def dumps_evals(*args, **kwargs):
         return evaluator
 
     def extend_results_with_pareto_efficient(self, df_path: str):
-        """Extend the results DataFrame with a column ``pareto_efficient`` which is ``True`` if the point is Pareto efficient.
+        """Extend the results DataFrame with the Pareto-Front.
+
+        It adds a column ``pareto_efficient`` which is ``True`` if the point is Pareto efficient.
 
         Args:
             df_path (pd.DataFrame): the path to results DataFrame.
diff --git a/src/deephyper/hpo/_problem.py b/src/deephyper/hpo/_problem.py
index e297f069..481ab104 100644
--- a/src/deephyper/hpo/_problem.py
+++ b/src/deephyper/hpo/_problem.py
@@ -5,7 +5,6 @@
 import ConfigSpace.hyperparameters as csh
 import numpy as np
 
-import deephyper.core.exceptions as dh_exceptions
 import deephyper.skopt
 
 
@@ -67,7 +66,8 @@ def convert_to_skopt_space(cs_space, surrogate_model=None):
 
     Args:
         cs_space (ConfigurationSpace): the ``ConfigurationSpace`` to convert.
-        surrogate_model (str, optional): the type of surrogate model/base estimator used to perform Bayesian optimization. Defaults to None.
+        surrogate_model (str, optional): the type of surrogate model/base estimator used to
+            perform Bayesian optimization. Defaults to ``None``.
 
     Raises:
         TypeError: if the input space is not a ConfigurationSpace.
@@ -79,9 +79,7 @@ def convert_to_skopt_space(cs_space, surrogate_model=None):
     if not (isinstance(cs_space, cs.ConfigurationSpace)):
         raise TypeError("Input space should be of type ConfigurationSpace")
 
-    sample_with_config_space = (
-        len(cs_space.conditions) > 0 or len(cs_space.forbidden_clauses) > 0
-    )
+    sample_with_config_space = len(cs_space.conditions) > 0 or len(cs_space.forbidden_clauses) > 0
 
     # convert the ConfigSpace to deephyper.skopt.space.Space
     dimensions = []
@@ -100,12 +98,15 @@ def check_hyperparameter(parameter, name=None, default_value=None):
     :meta private:
 
     Args:
-        parameter (str|Hyperparameter): an instance of ``ConfigSpace.hyperparameters.hyperparameter`` or a synthetic description (e.g., ``list``, ``tuple``).
-        parameter (str): the name of the hyperparameter. Only required when the parameter is not a ``ConfigSpace.hyperparameters.hyperparameter``.
+        parameter (str|Hyperparameter): an instance of ``ConfigSpace.hyperparameters.
+            hyperparameter`` or a synthetic description (e.g., ``list``, ``tuple``).
+        name (str): the name of the hyperparameter. Only required when the parameter is not a
+            ``ConfigSpace.hyperparameters.hyperparameter``.
         default_value: a default value for the hyperparameter.
 
     Returns:
-        Hyperparameter: the ConfigSpace hyperparameter instance corresponding to the ``parameter`` description.
+        Hyperparameter: the ConfigSpace hyperparameter instance corresponding to the ``parameter``
+        description.
     """
     if isinstance(parameter, csh.Hyperparameter):
         return parameter
@@ -115,7 +116,8 @@ def check_hyperparameter(parameter, name=None, default_value=None):
             return csh.Constant(name=name, value=parameter)
 
         raise ValueError(
-            "Shortcut definition of an hyper-parameter has to be a type in [list, tuple, array, dict, float, int, str]."
+            "Shortcut definition of an hyper-parameter has to be a type in [list, tuple, array, "
+            "dict, float, int, str]."
         )
 
     if type(name) is not str:
@@ -130,13 +132,13 @@ def check_hyperparameter(parameter, name=None, default_value=None):
             prior = "uniform"
         elif len(parameter) == 3:
             prior = parameter[2]
-            assert (
-                prior
-                in [
-                    "uniform",
-                    "log-uniform",
-                ]
-            ), f"Prior has to be 'uniform' or 'log-uniform' when {prior} was given for parameter '{name}'"
+            assert prior in [
+                "uniform",
+                "log-uniform",
+            ], (
+                f"Prior has to be 'uniform' or 'log-uniform' when {prior} "
+                f"was given for parameter '{name}'"
+            )
             parameter = parameter[:2]
 
         log = prior == "log-uniform"
@@ -150,9 +152,7 @@ def check_hyperparameter(parameter, name=None, default_value=None):
                 name=name, lower=parameter[0], upper=parameter[1], log=log, **kwargs
             )
     elif type(parameter) is list:  # Categorical
-        if any(
-            [isinstance(p, (str, bool)) or isinstance(p, np.bool_) for p in parameter]
-        ):
+        if any([isinstance(p, (str, bool)) or isinstance(p, np.bool_) for p in parameter]):
             return csh.CategoricalHyperparameter(name, choices=parameter, **kwargs)
         elif all([isinstance(p, (int, float)) for p in parameter]):
             return csh.OrdinalHyperparameter(name, sequence=parameter, **kwargs)
@@ -169,8 +169,7 @@ def check_hyperparameter(parameter, name=None, default_value=None):
                 )
 
     raise ValueError(
-        f"Invalid dimension {name}: {parameter}. Read the documentation for"
-        f" supported types."
+        f"Invalid dimension {name}: {parameter}. Read the documentation for" f" supported types."
     )
 
 
@@ -181,13 +180,12 @@ class HpProblem:
     >>> problem = HpProblem()
 
     Args:
-        config_space (ConfigurationSpace, optional): In case the ``HpProblem`` is defined from a `ConfigurationSpace`.
+        config_space (ConfigurationSpace, optional): In case the ``HpProblem`` is defined from a
+        `ConfigurationSpace`.
     """
 
     def __init__(self, config_space=None):
-        if config_space is not None and not (
-            isinstance(config_space, cs.ConfigurationSpace)
-        ):
+        if config_space is not None and not (isinstance(config_space, cs.ConfigurationSpace)):
             raise ValueError(
                 "Parameter 'config_space' should be an instance of ConfigurationSpace!"
             )
@@ -208,9 +206,7 @@ def __repr__(self):
     def __len__(self):
         return len(self.hyperparameter_names)
 
-    def add_hyperparameter(
-        self, value, name: str = None, default_value=None
-    ) -> csh.Hyperparameter:
+    def add_hyperparameter(self, value, name: str = None, default_value=None) -> csh.Hyperparameter:
         """Add an hyperparameter to the ``HpProblem``.
 
         Hyperparameters can be added to a ``HpProblem`` with a short syntax:
@@ -223,7 +219,8 @@ def add_hyperparameter(
 
         >>> problem.add_hyperparameter((0.0, 10.0, "log-uniform"), "real", default_value=5.0)
 
-        It is also possible to use `ConfigSpace <https://automl.github.io/ConfigSpace/master/API-Doc.html#hyperparameters>`_ ``Hyperparameters``:
+        It is also possible to use `ConfigSpace Hyperparameters
+        <https://automl.github.io/ConfigSpace/master/API-Doc.html#hyperparameters>`_:
 
         >>> import ConfigSpace.hyperparameters as csh
         >>> csh_hp = csh.UniformIntegerHyperparameter(
@@ -233,19 +230,26 @@ def add_hyperparameter(
         Args:
             value (tuple or list or ConfigSpace.Hyperparameter): a valid hyperparametr description.
             name (str): The name of the hyperparameter to add.
-            default_value (float or int or str): A default value for the corresponding hyperparameter.
+            default_value (float or int or str): A default value for the corresponding
+                hyperparameter.
 
         Returns:
-            ConfigSpace.Hyperparameter: a ConfigSpace ``Hyperparameter`` object corresponding to the ``(value, name, default_value)``.
+            ConfigSpace.Hyperparameter: a ConfigSpace ``Hyperparameter`` object corresponding to
+            the ``(value, name, default_value)``.
         """
         if not (type(name) is str or name is None):
-            raise dh_exceptions.problem.SpaceDimNameOfWrongType(name)
+            raise TypeError(
+                f"Dimension name: '{name}' is of type == {type(name)} when should be 'str'!"
+            )
         csh_parameter = check_hyperparameter(value, name, default_value=default_value)
         self._space.add(csh_parameter)
         return csh_parameter
 
     def add_hyperparameters(self, hp_list):
-        """Add a list of hyperparameters. It can be useful when a list of ``ConfigSpace.Hyperparameter`` are defined and we need to add them to the ``HpProblem``.
+        """Add a list of hyperparameters.
+
+        It can be useful when a list of ``ConfigSpace.Hyperparameter`` are defined and we need to
+        add them to the ``HpProblem``.
 
         Args:
             hp_list (ConfigSpace.Hyperparameter): a list of ConfigSpace hyperparameters.
@@ -256,9 +260,12 @@ def add_hyperparameters(self, hp_list):
         return [self.add_hyperparameter(hp) for hp in hp_list]
 
     def add_forbidden_clause(self, clause):
-        """Add a `forbidden clause <https://automl.github.io/ConfigSpace/master/API-Doc.html#forbidden-clauses>`_ to the ``HpProblem``.
+        r"""Add a forbidden clause to the problem.
 
-        For example if we want to optimize :math:`\\frac{1}{x}` where :math:`x` cannot be equal to 0:
+        Add a `forbidden clause <https://automl.github.io/ConfigSpace/master/API-Doc.html#forbidden-clauses>`_
+        to the ``HpProblem``.
+
+        For example if we want to optimize :math:`\frac{1}{x}` where :math:`x` cannot be equal to 0:
 
         >>> from deephyper.hpo import HpProblem
         >>> import ConfigSpace as cs
@@ -272,7 +279,10 @@ def add_forbidden_clause(self, clause):
         self._space.add(clause)
 
     def add_condition(self, condition):
-        """Add a `condition <https://automl.github.io/ConfigSpace/master/API-Doc.html#conditions>`_ to the ``HpProblem``.
+        """Add a condition to the problem.
+
+        Add a `condition <https://automl.github.io/ConfigSpace/master/API-Doc.html#conditions>`_ to
+        the ``HpProblem``.
 
                 >>> from deephyper.hpo import HpProblem
                 >>> import ConfigSpace as cs
@@ -287,7 +297,7 @@ def add_condition(self, condition):
         self._space.add(condition)
 
     def add_conditions(self, conditions: list) -> None:
-        """Add a list of `condition <https://automl.github.io/ConfigSpace/master/API-Doc.html#conditions>`_ to the ``HpProblem``.
+        """Add a list of conditions to the problem.
 
         Args:
             conditions (list): A list of ConfigSpace conditions.
diff --git a/src/deephyper/hpo/_random.py b/src/deephyper/hpo/_random.py
index 7426dab7..cd159e3a 100644
--- a/src/deephyper/hpo/_random.py
+++ b/src/deephyper/hpo/_random.py
@@ -43,16 +43,14 @@ class RandomSearch(Search):
 
     Args:
         problem: object describing the search/optimization problem.
-
         evaluator: object describing the evaluation process.
-
-        random_state (np.random.RandomState, optional): Initial random state of the search. Defaults to ``None``.
-
-        log_dir (str, optional): Path to the directoy where results of the search are stored. Defaults to ``"."``.
-
+        random_state (np.random.RandomState, optional): Initial random state of the search.
+            Defaults to ``None``.
+        log_dir (str, optional): Path to the directoy where results of the search are stored.
+            Defaults to ``"."``.
         verbose (int, optional): Use verbose mode. Defaults to ``0``.
-
-        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the function. Defaults to ``None`` which does not use any stopper.
+        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the
+            function. Defaults to ``None`` which does not use any stopper.
     """
 
     def __init__(
@@ -106,7 +104,7 @@ def _tell(self, results: List[HPOJob]):
         """Tell the search the results of the evaluations.
 
         Args:
-            observations (dict): a dictionary containing the results of the evaluations.
+            results (List[HPOJob]): a dictionary containing the results of the evaluations.
         """
         for config, obj in results:
             pass
diff --git a/src/deephyper/hpo/_regevo.py b/src/deephyper/hpo/_regevo.py
index 5a4db45b..bd5acba3 100644
--- a/src/deephyper/hpo/_regevo.py
+++ b/src/deephyper/hpo/_regevo.py
@@ -29,7 +29,9 @@ def get_inactive_value_of_hyperparameter(hp):
 
 
 class RegularizedEvolution(Search):
-    """Regularized evolution algorithm used as an example for the API to implement new search algorithms.
+    """Regularized evolution algorithm.
+
+    This implementation is an example for the Search API to implement new search algorithms.
 
     .. list-table::
         :widths: 25 25 25
@@ -44,20 +46,17 @@ class RegularizedEvolution(Search):
 
     Args:
         problem: object describing the search/optimization problem.
-
         evaluator: object describing the evaluation process.
-
-        random_state (np.random.RandomState, optional): Initial random state of the search. Defaults to ``None``.
-
-        log_dir (str, optional): Path to the directoy where results of the search are stored. Defaults to ``"."``.
-
+        random_state (np.random.RandomState, optional): Initial random state of the search.
+            Defaults to ``None``.
+        log_dir (str, optional): Path to the directoy where results of the search are stored.
+            Defaults to ``"."``.
         verbose (int, optional): Use verbose mode. Defaults to ``0``.
-
-        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the function. Defaults to ``None`` which does not use any stopper.
-
+        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the
+            function. Defaults to ``None`` which does not use any stopper.
         population_size (int, optional): The size of the population. Defaults to ``100``.
-
-        sample_size (int, optional): The number of samples to draw from the population. Defaults to ``10``.
+        sample_size (int, optional): The number of samples to draw from the population. Defaults
+            to ``10``.
     """
 
     def __init__(
@@ -73,9 +72,7 @@ def __init__(
     ):
         super().__init__(problem, evaluator, random_state, log_dir, verbose, stopper)
         self._problem.space.seed(self._random_state.randint(0, 2**31))
-        assert (
-            population_size > sample_size
-        ), "population_size must be greater than sample_size"
+        assert population_size > sample_size, "population_size must be greater than sample_size"
         self.population_size = population_size
         self.sample_size = sample_size
         self._population = deque(maxlen=self.population_size)
@@ -109,9 +106,7 @@ def _ask(self, n: int = 1) -> List[Dict]:
                     # If the parameter is inactive due to some conditions then we attribute the
                     # lower bound value to break symmetries and enforce the same representation.
                     if hp_name not in sample:
-                        sample[hp_name] = get_inactive_value_of_hyperparameter(
-                            space[hp_name]
-                        )
+                        sample[hp_name] = get_inactive_value_of_hyperparameter(space[hp_name])
 
                     # Make sure to have JSON serializable values
                     if type(sample[hp_name]).__module__ == np.__name__:
@@ -142,9 +137,7 @@ def _ask(self, n: int = 1) -> List[Dict]:
                 hp_value = hp.rvs(size=None, random_state=space.random)
 
                 child_sample[hp_name] = hp_value
-                child_sample = dict(
-                    deactivate_inactive_hyperparameters(child_sample, space)
-                )
+                child_sample = dict(deactivate_inactive_hyperparameters(child_sample, space))
 
                 for hp_name in self._problem.hyperparameter_names:
                     # If the parameter is inactive due to some conditions then we attribute the
@@ -166,7 +159,7 @@ def _tell(self, results: List[HPOJob]):
         """Tell the search the results of the evaluations.
 
         Args:
-            observations (dict): a dictionary containing the results of the evaluations.
+            results (List[HPOJob]): a dictionary containing the results of the evaluations.
         """
         for config, obj in results:
             # Do not add failures to population
diff --git a/src/deephyper/hpo/_search.py b/src/deephyper/hpo/_search.py
index 7d62e1fc..6f5d7ae4 100644
--- a/src/deephyper/hpo/_search.py
+++ b/src/deephyper/hpo/_search.py
@@ -26,16 +26,14 @@ class Search(abc.ABC):
 
     Args:
         problem: object describing the search/optimization problem.
-
         evaluator: object describing the evaluation process.
-
-        random_state (np.random.RandomState, optional): Initial random state of the search. Defaults to ``None``.
-
-        log_dir (str, optional): Path to the directoy where results of the search are stored. Defaults to ``"."``.
-
+        random_state (np.random.RandomState, optional): Initial random state of the search.
+            Defaults to ``None``.
+        log_dir (str, optional): Path to the directoy where results of the search are stored.
+            Defaults to ``"."``.
         verbose (int, optional): Use verbose mode. Defaults to ``0``.
-
-        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the function. Defaults to ``None`` which does not use any stopper.
+        stopper (Stopper, optional): a stopper to leverage multi-fidelity when evaluating the
+            function. Defaults to ``None`` which does not use any stopper.
     """
 
     def __init__(
@@ -80,9 +78,7 @@ def __init__(
         self._path_results = os.path.join(self._log_dir, "results.csv")
         if os.path.exists(self._path_results):
             str_current_time = time.strftime("%Y%m%d-%H%M%S")
-            path_results_renamed = self._path_results.replace(
-                ".", f"_{str_current_time}."
-            )
+            path_results_renamed = self._path_results.replace(".", f"_{str_current_time}.")
             logging.warning(
                 f"Results file already exists, it will be renamed to {path_results_renamed}"
             )
@@ -113,13 +109,12 @@ def check_evaluator(self, evaluator):
                 self._evaluator = Evaluator.create(
                     evaluator,
                     method=method,
-                    method_kwargs={
-                        "callbacks": [TqdmCallback()] if self._verbose else []
-                    },
+                    method_kwargs={"callbacks": [TqdmCallback()] if self._verbose else []},
                 )
             else:
                 raise TypeError(
-                    f"The evaluator shoud be of type deephyper.evaluator.Evaluator or Callable but it is  {type(evaluator)}!"
+                    f"The evaluator shoud be of type deephyper.evaluator.Evaluator or Callable but "
+                    f"it is  {type(evaluator)}!"
                 )
         else:
             self._evaluator = evaluator
@@ -145,10 +140,14 @@ def dump_context(self):
             yaml.dump(context, file)
 
     def _set_timeout(self, timeout=None):
-        """If the `timeout` parameter is valid. Run the search in an other thread and trigger a timeout when this thread exhaust the allocated time budget.
+        """Set the timeout for the evaluator used by the search.
+
+        If the `timeout` parameter is valid. Run the search in an other thread and trigger a
+        timeout when this thread exhaust the allocated time budget.
 
         Args:
-            timeout (int, optional): The time budget (in seconds) of the search before stopping. Defaults to ``None``, will not impose a time budget.
+            timeout (int, optional): The time budget (in seconds) of the search before stopping.
+                Defaults to ``None``, will not impose a time budget.
         """
         if timeout is not None:
             if type(timeout) is not int:
@@ -163,18 +162,20 @@ def _set_timeout(self, timeout=None):
         #         terminate_on_timeout, timeout + 1, self._search
         #     )
 
-    def search(
-        self, max_evals: int = -1, timeout: int = None, max_evals_strict: bool = False
-    ):
+    def search(self, max_evals: int = -1, timeout: int = None, max_evals_strict: bool = False):
         """Execute the search algorithm.
 
         Args:
-            max_evals (int, optional): The maximum number of evaluations of the run function to perform before stopping the search. Defaults to ``-1``, will run indefinitely.
-            timeout (int, optional): The time budget (in seconds) of the search before stopping. Defaults to ``None``, will not impose a time budget.
-            max_evals_strict (bool, optional): If ``True`` the search will not spawn more than ``max_evals`` jobs. Defaults to ``False``.
+            max_evals (int, optional): The maximum number of evaluations of the run function to
+                perform before stopping the search. Defaults to ``-1``, will run indefinitely.
+            timeout (int, optional): The time budget (in seconds) of the search before stopping.
+                Defaults to ``None``, will not impose a time budget.
+            max_evals_strict (bool, optional): If ``True`` the search will not spawn more than
+                ``max_evals`` jobs. Defaults to ``False``.
 
         Returns:
-            DataFrame: a pandas DataFrame containing the evaluations performed or ``None`` if the search could not evaluate any configuration.
+            DataFrame: a pandas DataFrame containing the evaluations performed or ``None`` if the
+                search could not evaluate any configuration.
         """
         import asyncio
 
@@ -208,13 +209,12 @@ def search(
         except TimeoutReached:
             self.stopped = True
             wait_all_running_jobs = False
-            logging.warning(
-                "Search is being stopped because the allowed timeout has been reached."
-            )
+            logging.warning("Search is being stopped because the allowed timeout has been reached.")
         except MaximumJobsSpawnReached:
             self.stopped = True
             logging.warning(
-                "Search is being stopped because the maximum number of spawned jobs has been reached."
+                "Search is being stopped because the maximum number of spawned jobs has been "
+                "reached."
             )
         except SearchTerminationError:
             self.stopped = True
@@ -257,10 +257,13 @@ def search_id(self):
         return self._evaluator._search_id
 
     def extend_results_with_pareto_efficient(self, df_path: str):
-        """Extend the results DataFrame with a column ``pareto_efficient`` which is ``True`` if the point is Pareto efficient.
+        """Extend the results DataFrame with Pareto-Front.
+
+        A column ``pareto_efficient`` is added to the dataframe. It is ``True`` if the
+        point is Pareto efficient.
 
         Args:
-            df (pd.DataFrame): the input results DataFrame.
+            df_path (str): the path to the input DataFrame.
         """
         df = pd.read_csv(df_path)
 
@@ -272,21 +275,22 @@ def extend_results_with_pareto_efficient(self, df_path: str):
                 mask_no_failures = ~df[objective_columns[0]].str.startswith("F")
             else:
                 mask_no_failures = np.ones(len(df), dtype=bool)
-            objectives = -df.loc[mask_no_failures, objective_columns].values.astype(
-                float
-            )
+            objectives = -df.loc[mask_no_failures, objective_columns].values.astype(float)
             mask_pareto_front = non_dominated_set(objectives)
             df["pareto_efficient"] = False
             df.loc[mask_no_failures, "pareto_efficient"] = mask_pareto_front
             df.to_csv(df_path, index=False)
 
     def _search(self, max_evals, timeout, max_evals_strict=False):
-        """Search algorithm to be implemented.
+        """Search algorithm logic.
 
         Args:
-            max_evals (int): The maximum number of evaluations of the run function to perform before stopping the search. Defaults to -1, will run indefinitely.
-            timeout (int): The time budget of the search before stopping.Defaults to None, will not impose a time budget.
-            max_evals_strict (bool, optional): ...
+            max_evals (int): The maximum number of evaluations of the run function to perform
+                before stopping the search. Defaults to -1, will run indefinitely.
+            timeout (int): The time budget of the search before stopping. Defaults to ``None``,
+                will not impose a time budget.
+            max_evals_strict (bool, optional): Wether the number of submitted jobs should be
+            strictly equal to ``max_evals``.
         """
         if max_evals_strict:
 
@@ -311,9 +315,7 @@ def num_evals():
             logging.info("Gathering jobs...")
             t1 = time.time()
 
-            new_results = self._evaluator.gather(
-                self.gather_type, self.gather_batch_size
-            )
+            new_results = self._evaluator.gather(self.gather_type, self.gather_batch_size)
 
             # Check if results are received from other search instances
             # connected to the same storage
@@ -321,12 +323,11 @@ def num_evals():
                 local_results, other_results = new_results
                 new_results = local_results + other_results
                 logging.info(
-                    f"Gathered {len(local_results)} local job(s) and {len(other_results)} other job(s) in {time.time() - t1:.4f} sec."
+                    f"Gathered {len(local_results)} local job(s) and {len(other_results)} other "
+                    f"job(s) in {time.time() - t1:.4f} sec."
                 )
             else:
-                logging.info(
-                    f"Gathered {len(new_results)} job(s) in {time.time() - t1:.4f} sec."
-                )
+                logging.info(f"Gathered {len(new_results)} job(s) in {time.time() - t1:.4f} sec.")
 
             logging.info("Dumping evaluations...")
             t1 = time.time()
@@ -376,7 +377,8 @@ def tell(self, results: List[HPOJob]):
         """Tell the search the results of the evaluations.
 
         Args:
-            results (List[HPOJob]): a list of HPOJobs from which hyperparameters and objectives can be retrieved.
+            results (List[HPOJob]): a list of HPOJobs from which hyperparameters and objectives can
+            be retrieved.
         """
         self._tell(results)
 
@@ -385,5 +387,6 @@ def _tell(self, results: List[HPOJob]):
         """Tell the search the results of the evaluations.
 
         Args:
-            observations (List[HPOJob]): a list of HPOJobs from which hyperparameters and objectives can be retrieved.
+            results (List[HPOJob]): a list of HPOJobs from which hyperparameters and objectives can
+                be retrieved.
         """
diff --git a/src/deephyper/predictor/sklearn/_predictor_sklearn.py b/src/deephyper/predictor/sklearn/_predictor_sklearn.py
index 67c4ce90..d0f99b53 100644
--- a/src/deephyper/predictor/sklearn/_predictor_sklearn.py
+++ b/src/deephyper/predictor/sklearn/_predictor_sklearn.py
@@ -12,9 +12,7 @@ class SklearnPredictor(Predictor):
     def __init__(self, model: BaseEstimator):
         self.model = model
         self._predict_func = "predict"
-        if isinstance(self.model, ClassifierMixin) and hasattr(
-            self.model, "predict_proba"
-        ):
+        if isinstance(self.model, ClassifierMixin) and hasattr(self.model, "predict_proba"):
             self._predict_func = "predict_proba"
 
     def predict(self, X):
@@ -41,9 +39,7 @@ def load(self) -> SklearnPredictor:
         return SklearnPredictor(model)
 
     @staticmethod
-    def find_predictor_files(
-        path_directory: str, file_extension: str = "pkl"
-    ) -> List[str]:
+    def find_predictor_files(path_directory: str, file_extension: str = "pkl") -> List[str]:
         """Finds the predictor files in a directory given a specific extension.
 
         Args:
diff --git a/src/deephyper/predictor/tf_keras2/_predictor_tf_keras2.py b/src/deephyper/predictor/tf_keras2/_predictor_tf_keras2.py
index 6db19d69..70693898 100644
--- a/src/deephyper/predictor/tf_keras2/_predictor_tf_keras2.py
+++ b/src/deephyper/predictor/tf_keras2/_predictor_tf_keras2.py
@@ -47,15 +47,11 @@ def __init__(self, path_predictor_file: str):
         super().__init__(path_predictor_file)
 
     def load(self) -> TFKeras2Predictor:
-        model = tfk.models.load_model(
-            self.path_predictor_file, compile=False, safe_mode=False
-        )
+        model = tfk.models.load_model(self.path_predictor_file, compile=False, safe_mode=False)
         return TFKeras2Predictor(model)
 
     @staticmethod
-    def find_predictor_files(
-        path_directory: str, file_extension: str = "keras"
-    ) -> List[str]:
+    def find_predictor_files(path_directory: str, file_extension: str = "keras") -> List[str]:
         """Finds the predictor files in a directory given a specific extension.
 
         Args:
diff --git a/src/deephyper/predictor/torch/_predictor_torch.py b/src/deephyper/predictor/torch/_predictor_torch.py
index 0b7bbf53..90bef125 100644
--- a/src/deephyper/predictor/torch/_predictor_torch.py
+++ b/src/deephyper/predictor/torch/_predictor_torch.py
@@ -11,7 +11,8 @@ class TorchPredictor(Predictor):
     def __init__(self, module: torch.nn.Module):
         if not isinstance(module, torch.nn.Module):
             raise ValueError(
-                f"The given module is of type {type(module)} when it should be of type torch.nn.Module!"
+                f"The given module is of type {type(module)} when it should be of type "
+                f"torch.nn.Module!"
             )
 
         self.module = module
@@ -60,9 +61,7 @@ def load(self) -> TorchPredictor:
         return TorchPredictor(model)
 
     @staticmethod
-    def find_predictor_files(
-        path_directory: str, file_extension: str = "pt"
-    ) -> List[str]:
+    def find_predictor_files(path_directory: str, file_extension: str = "pt") -> List[str]:
         """Finds the predictor files in a directory given a specific extension.
 
         Args:
diff --git a/src/deephyper/skopt/callbacks.py b/src/deephyper/skopt/callbacks.py
index d1c0ee1a..333fe228 100644
--- a/src/deephyper/skopt/callbacks.py
+++ b/src/deephyper/skopt/callbacks.py
@@ -18,8 +18,7 @@
 
 
 def check_callback(callback):
-    """Check if callback is a callable or a list of callables.
-    """
+    """Check if callback is a callable or a list of callables."""
     if callback is not None:
         if isinstance(callback, Callable):
             return [callback]
@@ -163,13 +162,11 @@ def _criterion(self, result):
         Classes inheriting from `EarlyStop` should use this method to
         implement their decision logic.
 
-        Parameters
-        ----------
+        Args:
         result : `OptimizeResult`, scipy object
             The optimization as a OptimizeResult object.
 
         Returns:
-        -------
         decision : boolean or None
             Return True/False if the criterion can make a decision or `None` if
             there is not enough data yet to make a decision.
@@ -228,8 +225,7 @@ def _criterion(self, result):
 
 
 class HollowIterationsStopper(EarlyStopper):
-    """Stop if the improvement over the last n iterations is below a threshold.
-    """
+    """Stop if the improvement over the last n iterations is below a threshold."""
 
     def __init__(self, n_iterations, threshold=0):
         super(HollowIterationsStopper, self).__init__()
diff --git a/src/deephyper/skopt/learning/forest.py b/src/deephyper/skopt/learning/forest.py
index a4606492..a452351f 100644
--- a/src/deephyper/skopt/learning/forest.py
+++ b/src/deephyper/skopt/learning/forest.py
@@ -364,8 +364,7 @@ def __init__(
     def predict(self, X, return_std=False, disentangled_std=False):
         """Predict continuous output for X.
 
-        Parameters
-        ----------
+        Args:
         X : array of shape = (n_samples, n_features)
             Input data.
 
@@ -373,7 +372,6 @@ def predict(self, X, return_std=False, disentangled_std=False):
             Whether or not to return the standard deviation.
 
         Returns:
-        -------
         predictions : array-like of shape = (n_samples,)
             Predicted values for X. If criterion is set to "mse",
             then `predictions[i] ~= mean(y | X[i])`.
@@ -591,8 +589,7 @@ def __init__(
     def predict(self, X, return_std=False, disentangled_std=False):
         """Predict continuous output for X.
 
-        Parameters
-        ----------
+        Args:
         X : array-like of shape=(n_samples, n_features)
             Input data.
 
@@ -600,7 +597,6 @@ def predict(self, X, return_std=False, disentangled_std=False):
             Whether or not to return the standard deviation.
 
         Returns:
-        -------
         predictions : array-like of shape=(n_samples,)
             Predicted values for X. If criterion is set to "squared_error",
             then `predictions[i] ~= mean(y | X[i])`.
diff --git a/src/deephyper/skopt/learning/gaussian_process/gpr.py b/src/deephyper/skopt/learning/gaussian_process/gpr.py
index 7698ce4e..99ce80cd 100644
--- a/src/deephyper/skopt/learning/gaussian_process/gpr.py
+++ b/src/deephyper/skopt/learning/gaussian_process/gpr.py
@@ -175,8 +175,7 @@ def __init__(
     def fit(self, X, y):
         """Fit Gaussian process regression model.
 
-        Parameters
-        ----------
+        Args:
         X : array-like, shape = (n_samples, n_features)
             Training data
 
@@ -184,7 +183,6 @@ def fit(self, X, y):
             Target values
 
         Returns:
-        -------
         self
             Returns an instance of self.
         """
@@ -263,8 +261,7 @@ def predict(
         the gradient of the mean and the standard-deviation with respect to X
         can be optionally provided.
 
-        Parameters
-        ----------
+        Args:
         X : array-like, shape = (n_samples, n_features)
             Query points where the GP is evaluated.
 
@@ -285,7 +282,6 @@ def predict(
             Only valid when X is a single point.
 
         Returns:
-        -------
         y_mean : array, shape = (n_samples, [n_output_dims])
             Mean of predictive distribution a query points
 
diff --git a/src/deephyper/skopt/learning/gaussian_process/kernels.py b/src/deephyper/skopt/learning/gaussian_process/kernels.py
index af129d91..a90f8568 100644
--- a/src/deephyper/skopt/learning/gaussian_process/kernels.py
+++ b/src/deephyper/skopt/learning/gaussian_process/kernels.py
@@ -52,8 +52,7 @@ def __pow__(self, b):
     def gradient_x(self, x, X_train):
         """Computes gradient of K(x, X_train) with respect to x
 
-        Parameters
-        ----------
+        Args:
         x: array-like, shape=(n_features,)
             A single test point.
 
@@ -61,7 +60,6 @@ def gradient_x(self, x, X_train):
             Training data used to fit the gaussian process.
 
         Returns:
-        -------
         gradient_x: array-like, shape=(n_samples, n_features)
             Gradient of K(x, X_train) with respect to x.
         """
@@ -338,8 +336,7 @@ def hyperparameter_length_scale(self):
     def __call__(self, X, Y=None, eval_gradient=False):
         """Return the kernel k(X, Y) and optionally its gradient.
 
-        Parameters
-        ----------
+        Args:
         * `X` [array-like, shape=(n_samples_X, n_features)]
             Left argument of the returned kernel k(X, Y)
 
@@ -352,7 +349,6 @@ def __call__(self, X, Y=None, eval_gradient=False):
             hyperparameter is determined. Only supported when Y is None.
 
         Returns:
-        -------
         * `K` [array-like, shape=(n_samples_X, n_samples_Y)]
             Kernel k(X, Y)
 
diff --git a/src/deephyper/skopt/learning/gbrt.py b/src/deephyper/skopt/learning/gbrt.py
index 3fb5f6c3..2eaa1a00 100644
--- a/src/deephyper/skopt/learning/gbrt.py
+++ b/src/deephyper/skopt/learning/gbrt.py
@@ -52,8 +52,7 @@ def __init__(
     def fit(self, X, y):
         """Fit one regressor for each quantile.
 
-        Parameters
-        ----------
+        Args:
         X : array-like, shape=(n_samples, n_features)
             Training vectors, where `n_samples` is the number of samples
             and `n_features` is the number of features.
@@ -111,8 +110,7 @@ def predict(self, X, return_std=False, return_quantiles=False):
         and the predicted standard deviation, which is approximated as
         the (0.84th quantile - 0.16th quantile) divided by 2.0
 
-        Parameters
-        ----------
+        Args:
         X : array-like, shape=(n_samples, n_features)
             where `n_samples` is the number of samples
             and `n_features` is the number of features.
diff --git a/src/deephyper/skopt/optimizer/optimizer.py b/src/deephyper/skopt/optimizer/optimizer.py
index db5361c3..e926df83 100644
--- a/src/deephyper/skopt/optimizer/optimizer.py
+++ b/src/deephyper/skopt/optimizer/optimizer.py
@@ -484,8 +484,7 @@ def __init__(
     def copy(self, random_state=None):
         """Create a shallow copy of an instance of the optimizer.
 
-        Parameters
-        ----------
+        Args:
         random_state : int, RandomState instance, or None (default)
             Set the random state of the copy.
         """
@@ -887,8 +886,7 @@ def tell(self, x, y, fit=True):
         To add multiple observations in a batch pass a list-of-lists for `x`
         and a list of scalars for `y`.
 
-        Parameters
-        ----------
+        Args:
         x : list or list-of-lists
             Point at which objective was evaluated.
 
@@ -1349,7 +1347,6 @@ def get_result(self):
         but without calling tell
 
         Returns:
-        -------
         res : `OptimizeResult`, scipy object
             OptimizeResult instance with the required information.
 
diff --git a/src/deephyper/skopt/sampler/base.py b/src/deephyper/skopt/sampler/base.py
index 4ce94e79..dc9441d0 100644
--- a/src/deephyper/skopt/sampler/base.py
+++ b/src/deephyper/skopt/sampler/base.py
@@ -5,13 +5,11 @@ def generate(self, dimensions, n_samples, random_state=None):
     def set_params(self, **params):
         """Set the parameters of this initial point generator.
 
-        Parameters
-        ----------
+        Args:
         **params : dict
             Generator parameters.
 
         Returns:
-        -------
         self : object
             Generator instance.
         """
diff --git a/src/deephyper/skopt/sampler/grid.py b/src/deephyper/skopt/sampler/grid.py
index f975a722..242907be 100644
--- a/src/deephyper/skopt/sampler/grid.py
+++ b/src/deephyper/skopt/sampler/grid.py
@@ -84,8 +84,7 @@ def __init__(self, border="exclude", use_full_layout=True, append_border="only")
     def generate(self, dimensions, n_samples, random_state=None):
         """Creates samples from a regular grid.
 
-        Parameters
-        ----------
+        Args:
         dimensions : list, shape (n_dims,)
             List of search space dimensions.
             Each search dimension can be defined either as
@@ -105,7 +104,6 @@ def generate(self, dimensions, n_samples, random_state=None):
             results.
 
         Returns:
-        -------
         np.array, shape=(n_dim, n_samples)
             grid set
         """
diff --git a/src/deephyper/skopt/sampler/halton.py b/src/deephyper/skopt/sampler/halton.py
index 86fd2e6c..96cd6eb9 100644
--- a/src/deephyper/skopt/sampler/halton.py
+++ b/src/deephyper/skopt/sampler/halton.py
@@ -43,8 +43,7 @@ def __init__(self, min_skip=0, max_skip=0, primes=None):
     def generate(self, dimensions, n_samples, random_state=None):
         """Creates samples from Halton set.
 
-        Parameters
-        ----------
+        Args:
         dimensions : list, shape (n_dims,)
             List of search space dimensions.
             Each search dimension can be defined either as
@@ -63,7 +62,6 @@ def generate(self, dimensions, n_samples, random_state=None):
             results.
 
         Returns:
-        -------
         np.array, shape=(n_dim, n_samples)
             Halton set.
 
diff --git a/src/deephyper/skopt/sampler/hammersly.py b/src/deephyper/skopt/sampler/hammersly.py
index eae2d428..11786ff0 100644
--- a/src/deephyper/skopt/sampler/hammersly.py
+++ b/src/deephyper/skopt/sampler/hammersly.py
@@ -46,8 +46,7 @@ def __init__(self, min_skip=0, max_skip=0, primes=None):
     def generate(self, dimensions, n_samples, random_state=None):
         """Creates samples from Hammersly set.
 
-        Parameters
-        ----------
+        Args:
         dimensions : list, shape (n_dims,)
             List of search space dimensions.
             Each search dimension can be defined either as
@@ -67,7 +66,6 @@ def generate(self, dimensions, n_samples, random_state=None):
             results.
 
         Returns:
-        -------
         np.array, shape=(n_dim, n_samples)
             Hammersley set.
 
diff --git a/src/deephyper/skopt/sampler/lhs.py b/src/deephyper/skopt/sampler/lhs.py
index 265ff606..87703bb9 100644
--- a/src/deephyper/skopt/sampler/lhs.py
+++ b/src/deephyper/skopt/sampler/lhs.py
@@ -49,8 +49,7 @@ def __init__(self, lhs_type="classic", criterion="maximin", iterations=1000):
     def generate(self, dimensions, n_samples, random_state=None):
         """Creates latin hypercube samples.
 
-        Parameters
-        ----------
+        Args:
         dimensions : list, shape (n_dims,)
             List of search space dimensions.
             Each search dimension can be defined either as
@@ -70,7 +69,6 @@ def generate(self, dimensions, n_samples, random_state=None):
             results.
 
         Returns:
-        -------
         np.array, shape=(n_dim, n_samples)
             LHS set
         """
diff --git a/src/deephyper/skopt/sampler/sobol.py b/src/deephyper/skopt/sampler/sobol.py
index f7341c4d..6e453fc5 100644
--- a/src/deephyper/skopt/sampler/sobol.py
+++ b/src/deephyper/skopt/sampler/sobol.py
@@ -491,8 +491,7 @@ def init(self, dim_num):
     def generate(self, dimensions, n_samples, random_state=None):
         """Creates samples from Sobol' set.
 
-        Parameters
-        ----------
+        Args:
         dimensions : list, shape (n_dims,)
             List of search space dimensions.
             Each search dimension can be defined either as
@@ -511,7 +510,6 @@ def generate(self, dimensions, n_samples, random_state=None):
             results.
 
         Returns:
-        -------
         sample : array_like (n_samples, dim)
             Sobol' set.
 
@@ -557,8 +555,7 @@ def generate(self, dimensions, n_samples, random_state=None):
     def _sobol(self, dim_num, seed):
         """Generates a new quasirandom Sobol' vector with each call.
 
-        Parameters
-        ----------
+        Args:
         dim_num : int
           Number of spatial dimensions.
           `dim_num` must satisfy 1 <= DIM_NUM <= 40.
@@ -573,7 +570,6 @@ def _sobol(self, dim_num, seed):
           the sequence.
 
         Returns:
-        -------
         vector, seed : np.array (n_dim,), int
             The next quasirandom vector and the seed of its next vector.
 
diff --git a/src/deephyper/skopt/searchcv.py b/src/deephyper/skopt/searchcv.py
index e7f7fd0d..1a63c38c 100644
--- a/src/deephyper/skopt/searchcv.py
+++ b/src/deephyper/skopt/searchcv.py
@@ -378,15 +378,13 @@ def optimizer_results_(self):
     def _make_optimizer(self, params_space):
         """Instantiate skopt Optimizer class.
 
-        Parameters
-        ----------
+        Args:
         params_space : dict
             Represents parameter search space. The keys are parameter
             names (strings) and values are deephyper.skopt.space.Dimension instances,
             one of Real, Integer or Categorical.
 
         Returns:
-        -------
         optimizer: Instance of the `Optimizer` class used for for search
             in some parameter space.
 
@@ -424,7 +422,6 @@ def total_iterations(self):
         all subspaces with `fit` method.
 
         Returns:
-        -------
         max_iter: int, total number of iterations to explore
         """
         total_iter = 0
@@ -443,8 +440,7 @@ def total_iterations(self):
     def fit(self, X, y=None, *, groups=None, callback=None, **fit_params):
         """Run fit on the estimator with randomly drawn parameters.
 
-        Parameters
-        ----------
+        Args:
         X : array-like or sparse matrix, shape = [n_samples, n_features]
             The training input samples.
 
diff --git a/src/deephyper/skopt/space/space.py b/src/deephyper/skopt/space/space.py
index ca80149e..70792d57 100644
--- a/src/deephyper/skopt/space/space.py
+++ b/src/deephyper/skopt/space/space.py
@@ -1,3 +1,5 @@
+"""Submodule for Skopt space definition."""
+
 import numbers
 
 import ConfigSpace as CS
@@ -30,7 +32,7 @@ def __repr__(self):
 
 
 def _transpose_list_array(x):
-    """Transposes a list matrix"""
+    """Utility to transpose a list matrix."""
     n_dims = len(x)
     assert n_dims > 0
     n_samples = len(x[0])
@@ -160,8 +162,7 @@ class Dimension:
     def rvs(self, n_samples=1, random_state=None):
         """Draw random samples.
 
-        Parameters
-        ----------
+        Args:
         n_samples : int or None
             The number of samples to be drawn.
 
@@ -178,36 +179,41 @@ def transform(self, X):
         return self.transformer.transform(X)
 
     def inverse_transform(self, Xt):
-        """Inverse transform samples from the warped space back into the
-        original space.
-        """
+        """Inverse transform samples from the warped space back into the original space."""
         return self.transformer.inverse_transform(Xt)
 
     def set_transformer(self):
+        """Setter for the transformer."""
         raise NotImplementedError
 
     @property
     def size(self):
+        """Dimensionality of sampel from the dimension before the transform/preprocessing."""
         return 1
 
     @property
     def transformed_size(self):
+        """Dimensionality of samples from the dimension after the transform/preprocessing."""
         return 1
 
     @property
     def bounds(self):
+        """Bounds before the transform/preprocessing."""
         raise NotImplementedError
 
     @property
     def is_constant(self):
+        """Test if the dimension is a constant."""
         raise NotImplementedError
 
     @property
     def transformed_bounds(self):
+        """Bounds after the transform/preprocessing."""
         raise NotImplementedError
 
     @property
     def name(self):
+        """Name of the dimension."""
         return self._name
 
     @name.setter
@@ -333,8 +339,7 @@ def __init__(
     def set_transformer(self, transform="identity"):
         """Define rvs and transformer spaces.
 
-        Parameters
-        ----------
+        Args:
         transform : str
            Can be 'normalize' or 'identity'
 
@@ -386,6 +391,7 @@ def set_transformer(self, transform="identity"):
                 self.transformer = LogN(self.base)
 
     def __eq__(self, other):
+        """Test if the dimension is equal to an other by testing if types, bounds, prior, and other parameters are equal."""
         return (
             type(self) is type(other)
             and np.allclose([self.low], [other.low])
@@ -397,14 +403,13 @@ def __eq__(self, other):
         )
 
     def __repr__(self):
+        """String representation of the dimension."""
         return "Real(low={}, high={}, prior='{}', transform='{}', loc='{}', scale='{}')".format(
             self.low, self.high, self.prior, self.transform_, self.loc, self.scale
         )
 
     def inverse_transform(self, Xt):
-        """Inverse transform samples from the warped space back into the
-        original space.
-        """
+        """Inverse transform samples from the warped space back into the original space."""
         inv_transform = super(Real, self).inverse_transform(Xt)
         if isinstance(inv_transform, list):
             inv_transform = np.array(inv_transform)
@@ -417,13 +422,16 @@ def inverse_transform(self, Xt):
 
     @property
     def bounds(self):
+        """Bounds before the transform/preprocessing."""
         return (self.low, self.high)
 
     @property
     def is_constant(self):
+        """Tet if the dimension is constant."""
         return self.low == self.high
 
     def __contains__(self, point):
+        """Test if a value is contained in the support of the dimension."""
         if isinstance(point, list):
             point = np.array(point)
         if point == np.nan:
@@ -433,6 +441,7 @@ def __contains__(self, point):
 
     @property
     def transformed_bounds(self):
+        """Bounds after the transform/preprocessing."""
         if self.transform_ == "normalize":
             return 0.0, 1.0
         else:
@@ -447,8 +456,7 @@ def transformed_bounds(self):
     def distance(self, a, b):
         """Compute distance between point `a` and `b`.
 
-        Parameters
-        ----------
+        Args:
         a : float
             First point.
 
@@ -482,13 +490,13 @@ def update_prior(self, X, y, q=0.9):
 
     def rvs(self, n_samples=1, random_state=None):
         """Draw random samples.
-        Parameters
-        ----------
-        n_samples : int or None
-            The number of samples to be drawn.
-        random_state : int, RandomState instance, or None (default)
-            Set random state to something other than None for reproducible
-            results.
+
+        Args:
+            n_samples : int or None
+                The number of samples to be drawn.
+            random_state : int, RandomState instance, or None (default)
+                Set random state to something other than None for reproducible
+                results.
         """
         rng = check_random_state(random_state)
 
@@ -504,46 +512,44 @@ def rvs(self, n_samples=1, random_state=None):
 class Integer(Dimension):
     """Search space dimension that can take on integer values.
 
-    Parameters
-    ----------
-    low : int
-        Lower bound (inclusive).
-
-    high : int
-        Upper bound (inclusive).
+    Args:
+        low : int
+            Lower bound (inclusive).
 
-    prior : "uniform" or "log-uniform", default="uniform"
-        Distribution to use when sampling random integers for
-        this dimension.
+        high : int
+            Upper bound (inclusive).
 
-        - If `"uniform"`, integers are sampled uniformly between the lower
-          and upper bounds.
-        - If `"log-uniform"`, integers are sampled uniformly between
-          `log(lower, base)` and `log(upper, base)` where log
-          has base `base`.
+        prior : "uniform" or "log-uniform", default="uniform"
+            Distribution to use when sampling random integers for
+            this dimension.
 
-    base : int
-        The logarithmic base to use for a log-uniform prior.
+            - If `"uniform"`, integers are sampled uniformly between the lower
+            and upper bounds.
+            - If `"log-uniform"`, integers are sampled uniformly between
+            `log(lower, base)` and `log(upper, base)` where log
+            has base `base`.
 
-        - Default 10, otherwise commonly 2.
+        base : int
+            The logarithmic base to use for a log-uniform prior.
 
-    transform : "identity", "normalize", optional
-        The following transformations are supported.
+            - Default 10, otherwise commonly 2.
 
-        - "identity", (default) the transformed space is the same as the
-          original space.
-        - "normalize", the transformed space is scaled to be between
-          0 and 1.
+        transform : "identity", "normalize", optional
+            The following transformations are supported.
 
-    name : str or None
-        Name associated with dimension, e.g., "number of trees".
+            - "identity", (default) the transformed space is the same as the
+            original space.
+            - "normalize", the transformed space is scaled to be between
+            0 and 1.
 
-    dtype : str or dtype, default=np.int64
-        integer type which will be used in inverse_transform,
-        can be int, np.int16, np.uint32, np.int32, np.int64 (default).
-        When set to int, `inverse_transform` returns a list instead of
-        a numpy array
+        name : str or None
+            Name associated with dimension, e.g., "number of trees".
 
+        dtype : str or dtype, default=np.int64
+            integer type which will be used in inverse_transform,
+            can be int, np.int16, np.uint32, np.int32, np.int64 (default).
+            When set to int, `inverse_transform` returns a list instead of
+            a numpy array
     """
 
     def __init__(
@@ -623,8 +629,7 @@ def __init__(
     def set_transformer(self, transform="identity"):
         """Define _rvs and transformer spaces.
 
-        Parameters
-        ----------
+        Args:
         transform : str
            Can be 'normalize' or 'identity'
 
@@ -671,6 +676,7 @@ def set_transformer(self, transform="identity"):
                 self.transformer = LogN(self.base)
 
     def __eq__(self, other):
+        """Test if the dimension is equal to an other by testing if types and bounds are all equal."""
         return (
             type(self) is type(other)
             and np.allclose([self.low], [other.low])
@@ -678,14 +684,13 @@ def __eq__(self, other):
         )
 
     def __repr__(self):
+        """String representation of the dimension."""
         return "Integer(low={}, high={}, prior='{}', transform='{}')".format(
             self.low, self.high, self.prior, self.transform_
         )
 
     def inverse_transform(self, Xt):
-        """Inverse transform samples from the warped space back into the
-        original space.
-        """
+        """Inverse transform samples from the warped space back into the original space."""
         # The concatenation of all transformed dimensions makes Xt to be
         # of type float, hence the required cast back to int.
         inv_transform = super(Integer, self).inverse_transform(Xt)
@@ -707,13 +712,16 @@ def inverse_transform(self, Xt):
 
     @property
     def bounds(self):
+        """Bounds before transform/preprocessing."""
         return (self.low, self.high)
 
     @property
     def is_constant(self):
+        """Test if the dimension is a constant."""
         return self.low == self.high
 
     def __contains__(self, point):
+        """Test if the value is contained in the support of the dimension."""
         if isinstance(point, list):
             point = np.array(point)
         if point == np.nan:
@@ -723,6 +731,7 @@ def __contains__(self, point):
 
     @property
     def transformed_bounds(self):
+        """Bounds after the transform/preprocessing."""
         if self.transform_ == "normalize":
             return 0.0, 1.0
         else:
@@ -737,8 +746,7 @@ def transformed_bounds(self):
     def distance(self, a, b):
         """Compute distance between point `a` and `b`.
 
-        Parameters
-        ----------
+        Args:
         a : int
             First point.
 
@@ -756,29 +764,26 @@ def distance(self, a, b):
 class Categorical(Dimension):
     """Search space dimension that can take on categorical values.
 
-    Parameters
-    ----------
-    categories : list, shape=(n_categories,)
-        Sequence of possible categories.
-
-    prior : list, shape=(categories,), default=None
-        Prior probabilities for each category. By default all categories
-        are equally likely.
-
-    transform : "onehot", "string", "identity", "label", default="onehot"
-
-        - "identity", the transformed space is the same as the original
-          space.
-        - "string",  the transformed space is a string encoded
-          representation of the original space.
-        - "label", the transformed space is a label encoded
-          representation (integer) of the original space.
-        - "onehot", the transformed space is a one-hot encoded
-          representation of the original space.
-
-    name : str or None
-        Name associated with dimension, e.g., "colors".
-
+    Args:
+        categories (list, shape=(n_categories,)):
+            Sequence of possible categories.
+
+        prior (list, shape=(categories,), default=None):
+            Prior probabilities for each category. By default all categories
+            are equally likely.
+
+        transform (str, Optional):
+            - ``"identity"``, the transformed space is the same as the original
+            space.
+            - ``"string"``,  the transformed space is a string encoded
+            representation of the original space.
+            - ``"label"``, the transformed space is a label encoded
+            representation (integer) of the original space.
+            - ``"onehot"``, the transformed space is a one-hot encoded
+            representation of the original space.
+
+        name (str, Optional):
+            Name associated with dimension, e.g., ``"colors"``.
     """
 
     def __init__(self, categories, prior=None, transform=None, name=None):
@@ -802,10 +807,8 @@ def __init__(self, categories, prior=None, transform=None, name=None):
     def set_transformer(self, transform="onehot"):
         """Define _rvs and transformer spaces.
 
-        Parameters
-        ----------
-        transform : str
-           Can be 'normalize', 'onehot', 'string', 'label', or 'identity'
+        Args:
+            transform (str): Can be a value in ``['normalize', 'onehot', 'string', 'label', 'identity']``.
 
         """
         self.transform_ = transform
@@ -845,6 +848,7 @@ def set_transformer(self, transform="onehot"):
             )
 
     def __eq__(self, other):
+        """Test if the dimension is equal to an other by checking if types, categories and priors are equal."""
         return (
             type(self) is type(other)
             and self.categories == other.categories
@@ -852,6 +856,7 @@ def __eq__(self, other):
         )
 
     def __repr__(self):
+        """String representation of the dimension."""
         if len(self.categories) > 7:
             cats = self.categories[:3] + (_Ellipsis(),) + self.categories[-3:]
         else:
@@ -867,9 +872,7 @@ def __repr__(self):
         )
 
     def inverse_transform(self, Xt):
-        """Inverse transform samples from the warped space back into the
-        original space.
-        """
+        """Inverse transform samples from the warped space back into the original space."""
         # The concatenation of all transformed dimensions makes Xt to be
         # of type float, hence the required cast back to int.
         inv_transform = super(Categorical, self).inverse_transform(Xt)
@@ -878,6 +881,7 @@ def inverse_transform(self, Xt):
         return inv_transform
 
     def rvs(self, n_samples=None, random_state=None):
+        """Sample elements from the dimension."""
         choices = self._rvs.rvs(size=n_samples, random_state=random_state)
 
         if isinstance(choices, numbers.Integral):
@@ -891,6 +895,7 @@ def rvs(self, n_samples=None, random_state=None):
 
     @property
     def transformed_size(self):
+        """Cardinality of the dimension after applying transform/preprocessing."""
         if self.transform_ == "onehot":
             size = len(self.categories)
             # when len(categories) == 2, CategoricalEncoder outputs a
@@ -900,17 +905,21 @@ def transformed_size(self):
 
     @property
     def bounds(self):
+        """Bounds of before applying transform/preprocessing."""
         return self.categories
 
     @property
     def is_constant(self):
+        """Test if the current dimension is a constant (with only 1 element it its support)."""
         return len(self.categories) <= 1
 
     def __contains__(self, point):
+        """Test if a value is contained among current categories."""
         return point in self.categories
 
     @property
     def transformed_bounds(self):
+        """Bounds after applying transform/preprocessing."""
         if self.transformed_size == 1:
             N = len(self.categories)
             if self.transform_ == "label":
@@ -928,8 +937,7 @@ def distance(self, a, b):
         As categories have no order the distance between two points is one
         if a != b and zero otherwise.
 
-        Parameters
-        ----------
+        Args:
         a : category
             First category.
 
@@ -982,9 +990,11 @@ def __init__(self, dimensions, model_sdv=None, config_space=None):
         self.dimensions = [check_dimension(dim) for dim in dimensions]
 
     def __eq__(self, other):
+        """Check if a space is equal to an other by checking if their dimensions are all equal."""
         return all([a == b for a, b in zip(self.dimensions, other.dimensions)])
 
     def __repr__(self):
+        """String representation of the space."""
         if len(self.dimensions) > 31:
             dims = self.dimensions[:15] + [_Ellipsis()] + self.dimensions[-15:]
         else:
@@ -992,12 +1002,12 @@ def __repr__(self):
         return "Space([{}])".format(",\n       ".join(map(str, dims)))
 
     def __iter__(self):
+        """Iter over the ``dimensions`` of the ``Space``."""
         return iter(self.dimensions)
 
     @property
     def dimension_names(self):
-        """Names of all the dimensions in the search-space.
-        """
+        """Names of all the dimensions in the search-space."""
         index = 0
         names = []
         for dim in self.dimensions:
@@ -1010,41 +1020,35 @@ def dimension_names(self):
 
     @property
     def is_real(self):
-        """Returns true if all dimensions are Real
-        """
+        """Returns true if all dimensions are Real."""
         return all([isinstance(dim, Real) for dim in self.dimensions])
 
     @classmethod
     def from_yaml(cls, yml_path, namespace=None):
-        """Create Space from yaml configuration file
-
-        Parameters
-        ----------
-        yml_path : str
-            Full path to yaml configuration file, example YaML below:
-            Space:
-
-            - Integer:
-              low: -5
-              high: 5
-            - Categorical:
-              categories:
-              - a
-              - b
-            - Real:
-              low: 1.0
-              high: 5.0
-              prior: log-uniform
-
-        namespace : str, default=None
-           Namespace within configuration file to use, will use first
-           namespace if not provided
+        """Create a ``Space`` from yaml configuration file.
+
+        Args:
+            yml_path (str): Full path to yaml configuration file, example YaML below:
+                Space:
+
+                - Integer:
+                low: -5
+                high: 5
+                - Categorical:
+                categories:
+                - a
+                - b
+                - Real:
+                low: 1.0
+                high: 5.0
+                prior: log-uniform
+
+            namespace : str, default=None
+            Namespace within configuration file to use, will use first
+            namespace if not provided
 
         Returns:
-        -------
-        space : Space
-           Instantiated Space object
-
+            space (Space): Instantiated Space object.
         """
         with open(yml_path, "rb") as f:
             config = yaml.safe_load(f)
@@ -1086,21 +1090,15 @@ def rvs(self, n_samples=1, random_state=None, n_jobs=1):
         """Draw random samples.
 
         The samples are in the original space. They need to be transformed
-        before being passed to a model or minimizer by `space.transform()`.
+        before being passed to a model or minimizer by ``space.transform()``.
 
-        Parameters
-        ----------
-        n_samples : int, default=1
-            Number of samples to be drawn from the space.
-
-        random_state : int, RandomState instance, or None (default)
-            Set random state to something other than None for reproducible
-            results.
+        Args:
+            n_samples (int, default=1): Number of samples to be drawn from the space.
+            random_state (int or RandomState, Optional): Set random state to something other than None for reproducible results.
+            n_jobs (int): the number of parallel processes to use to perform sampling.
 
         Returns:
-        -------
-        points : list of lists, shape=(n_points, n_dims)
-           Points sampled from the space.
+            points (list of lists, shape=(n_points, n_dims)): Points sampled from the space.
         """
         rng = check_random_state(random_state)
         if self.config_space:
@@ -1187,14 +1185,10 @@ def rvs(self, n_samples=1, random_state=None, n_jobs=1):
                 return _transpose_list_array(columns)
 
     def set_transformer(self, transform):
-        """Sets the transformer of all dimension objects to `transform`
-
-        Parameters
-        ----------
-        transform : str or list of str
-           Sets all transformer,, when `transform`  is a string.
-           Otherwise, transform must be a list with strings with
-           the same length as `dimensions`
+        """Sets the transformer of all dimension objects to ``transform``.
+
+        Args:
+            transform (str or list of str): Sets all transformer,, when `transform`  is a string. Otherwise, transform must be a list with strings with the same length as `dimensions`
         """
         # Transform
         for j in range(self.n_dims):
@@ -1204,15 +1198,11 @@ def set_transformer(self, transform):
                 self.dimensions[j].set_transformer(transform)
 
     def set_transformer_by_type(self, transform, dim_type):
-        """Sets the transformer of `dim_type` objects to `transform`
+        """Sets the transformer of ``dim_type`` objects to ``transform``.
 
-        Parameters
-        ----------
-        transform : str
-           Sets all transformer of type `dim_type` to `transform`
-        dim_type : type
-            Can be `deephyper.skopt.space.Real`, `deephyper.skopt.space.Integer` or
-             `deephyper.skopt.space.Categorical`
+        Args:
+            transform (str): Sets all transformer of type ``dim_type`` to ``transform``
+            dim_type (type): Can be ``deephyper.skopt.space.Real``, ``deephyper.skopt.space.Integer`` or ``deephyper.skopt.space.Categorical``.
         """
         # Transform
         for j in range(self.n_dims):
@@ -1220,7 +1210,7 @@ def set_transformer_by_type(self, transform, dim_type):
                 self.dimensions[j].set_transformer(transform)
 
     def get_transformer(self):
-        """Returns all transformers as list"""
+        """Returns all transformers as list."""
         return [self.dimensions[j].transform_ for j in range(self.n_dims)]
 
     def transform(self, X):
@@ -1229,15 +1219,11 @@ def transform(self, X):
         Note: this transformation is expected to be used to project samples
               into a suitable space for numerical optimization.
 
-        Parameters
-        ----------
-        X : list of lists, shape=(n_samples, n_dims)
-            The samples to transform.
+        Args:
+            X (list of lists, shape=(n_samples, n_dims)): The samples to transform.
 
         Returns:
-        -------
-        Xt : array of floats, shape=(n_samples, transformed_n_dims)
-            The transformed samples.
+            Xt (array of floats, shape=(n_samples, transformed_n_dims)): The transformed samples.
         """
         # Pack by dimension
         columns = [list() for _ in self.dimensions]
@@ -1256,18 +1242,13 @@ def transform(self, X):
         return Xt
 
     def inverse_transform(self, Xt):
-        """Inverse transform samples from the warped space back to the
-           original space.
+        """Inverse transform samples from the warped space back to the original space.
 
-        Parameters
-        ----------
-        Xt : array of floats, shape=(n_samples, transformed_n_dims)
-            The samples to inverse transform.
+        Args:
+            Xt (array of floats, shape=(n_samples, transformed_n_dims)): The samples to inverse transform.
 
         Returns:
-        -------
-        X : list of lists, shape=(n_samples, n_dims)
-            The original samples.
+            X (list of lists, shape=(n_samples, n_dims)): The original samples.
         """
         # Inverse transform
         columns = []
@@ -1328,14 +1309,12 @@ def __getitem__(self, dimension_names):
         `space[['foo', 'bar']]` returns the two dimensions named
         'foo' and 'bar' if they exist.
 
-        Parameters
-        ----------
+        Args:
         dimension_names : str or list(str)
             Name of a single search-space dimension (str).
             List of names for search-space dimensions (list(str)).
 
         Returns:
-        -------
         dims tuple (index, Dimension), list(tuple(index, Dimension)), \
                 (None, None)
             A single search-space dimension with the given name,
@@ -1385,19 +1364,17 @@ def transformed_bounds(self):
 
     @property
     def is_categorical(self):
-        """Space contains exclusively categorical dimensions"""
+        """Space contains exclusively categorical dimensions."""
         return all([isinstance(dim, Categorical) for dim in self.dimensions])
 
     @property
     def is_partly_categorical(self):
-        """Space contains any categorical dimensions"""
+        """Space contains any categorical dimensions."""
         return any([isinstance(dim, Categorical) for dim in self.dimensions])
 
     @property
     def n_constant_dimensions(self):
-        """Returns the number of constant dimensions which have zero degree of
-        freedom, e.g. an Integer dimensions with (0., 0.) as bounds.
-        """
+        """Returns the number of constant dimensions which have zero degree of freedom, e.g. an Integer dimensions with (0., 0.) as bounds."""
         n = 0
         for dim in self.dimensions:
             if dim.is_constant:
@@ -1407,8 +1384,7 @@ def n_constant_dimensions(self):
     def distance(self, point_a, point_b):
         """Compute distance between two points in this space.
 
-        Parameters
-        ----------
+        Args:
         point_a : array
             First point.
 
@@ -1422,7 +1398,9 @@ def distance(self, point_a, point_b):
         return distance
 
     def update_prior(self, X, y, q=0.9):
-        """Update the prior of the dimensions. Instead of doing random-sampling, a kernel density estimation is fit on the region of interest and
+        """Update the prior of the dimensions.
+
+        Instead of doing random-sampling, a kernel density estimation is fit on the region of interest and
         sampling is performed from this distribution.
         """
         y = np.array(y)
@@ -1433,6 +1411,7 @@ def update_prior(self, X, y, q=0.9):
                 dim.update_prior(Xi, y, q=q)
 
     def deactivate_inactive_dimensions(self, x):
+        """When ConfigSpace is used, it will return the "lower" bound of inactive parameters."""
         x = x[:]
         if self.config_space is not None:
             x_dict = {k: v for k, v in zip(self.dimension_names, x)}
diff --git a/src/deephyper/skopt/space/transformers.py b/src/deephyper/skopt/space/transformers.py
index d33feaaf..3af78b4d 100644
--- a/src/deephyper/skopt/space/transformers.py
+++ b/src/deephyper/skopt/space/transformers.py
@@ -1,4 +1,7 @@
+"""Submodule of transformer (i.e., preprocessing pipelines) for Skopt."""
+
 from __future__ import division
+
 import numpy as np
 from sklearn.preprocessing import LabelBinarizer
 
@@ -7,12 +10,15 @@ class Transformer(object):
     """Base class for all 1-D transformers."""
 
     def fit(self, X):
+        """Initialize the transformer with current ``X``."""
         return self
 
     def transform(self, X):
+        """Applies the transformation."""
         raise NotImplementedError
 
     def inverse_transform(self, X):
+        """Reverse the transformation."""
         raise NotImplementedError
 
 
@@ -23,9 +29,11 @@ def __init__(self, type_func=None) -> None:
         self.type_func = type_func
 
     def transform(self, X):
+        """Returns the identity."""
         return X
 
     def inverse_transform(self, Xt):
+        """Returns the identity."""
         if self.type_func:
             return [self.type_func(Xt[0])]
         else:
@@ -36,16 +44,18 @@ class ToInteger(Transformer):
     """Identity transform."""
 
     def transform(self, X):
+        """Apply the integer transform."""
         return int(X)
 
     def inverse_transform(self, Xt):
+        """Returns the identity as the operation is not reversable."""
         return Xt
 
 
 class StringEncoder(Transformer):
     """StringEncoder transform.
-    The transform will cast everything to a
-    string and the inverse transform will cast to the type defined in dtype.
+
+    The transform will cast everything to a string and the inverse transform will cast to the type defined in dtype.
     """
 
     def __init__(self, dtype=str):
@@ -53,11 +63,9 @@ def __init__(self, dtype=str):
         self.dtype = dtype
 
     def fit(self, X):
-        """Fit a list or array of categories. All elements must be from the
-        same type.
+        """Fit a list or array of categories. All elements must be from the same type.
 
-        Parameters
-        ----------
+        Args:
         X : array-like, shape=(n_categories,)
             List of categories.
         """
@@ -67,31 +75,22 @@ def fit(self, X):
     def transform(self, X):
         """Transform an array of categories to a string encoded representation.
 
-        Parameters
-        ----------
-        X : array-like, shape=(n_samples,)
-            List of categories.
+        Args:
+            X (array-like, shape=(n_samples,)): List of categories.
 
         Returns:
-        -------
-        Xt : array-like, shape=(n_samples,)
-            The string encoded categories.
+            Xt (array-like, shape=(n_samples,)): The string encoded categories.
         """
         return [str(x) for x in X]
 
     def inverse_transform(self, Xt):
-        """Inverse transform string encoded categories back to their original
-           representation.
+        """Inverse transform string encoded categories back to their original representation.
 
-        Parameters
-        ----------
-        Xt : array-like, shape=(n_samples,)
-            String encoded categories.
+        Args:
+            Xt (array-like, shape=(n_samples,)): String encoded categories.
 
         Returns:
-        -------
-        X : array-like, shape=(n_samples,)
-            The original categories.
+            X (array-like, shape=(n_samples,)): The original categories.
         """
         return [self.dtype(x) for x in Xt]
 
@@ -103,9 +102,11 @@ def __init__(self, base):
         self._base = base
 
     def transform(self, X):
+        """Apply the log transform."""
         return np.log10(np.asarray(X, dtype=float)) / np.log10(self._base)
 
     def inverse_transform(self, Xt):
+        """Reverse the log transform."""
         return self._base ** np.asarray(Xt, dtype=float)
 
 
@@ -119,10 +120,8 @@ def __init__(self):
     def fit(self, X):
         """Fit a list or array of categories.
 
-        Parameters
-        ----------
-        X : array-like, shape=(n_categories,)
-            List of categories.
+        Args:
+            X (array-like, shape=(n_categories,)): List of categories.
         """
         self.mapping_ = {v: i for i, v in enumerate(X)}
         self.inverse_mapping_ = {i: v for v, i in self.mapping_.items()}
@@ -134,31 +133,22 @@ def fit(self, X):
     def transform(self, X):
         """Transform an array of categories to a one-hot encoded representation.
 
-        Parameters
-        ----------
-        X : array-like, shape=(n_samples,)
-            List of categories.
+        Args:
+            X (array-like, shape=(n_samples,)): List of categories.
 
         Returns:
-        -------
-        Xt : array-like, shape=(n_samples, n_categories)
-            The one-hot encoded categories.
+            Xt (array-like, shape=(n_samples, n_categories)): The one-hot encoded categories.
         """
         return self._lb.transform([self.mapping_[v] for v in X])
 
     def inverse_transform(self, Xt):
-        """Inverse transform one-hot encoded categories back to their original
-           representation.
+        """Inverse transform one-hot encoded categories back to their original representation.
 
-        Parameters
-        ----------
-        Xt : array-like, shape=(n_samples, n_categories)
-            One-hot encoded categories.
+        Args:
+            Xt (array-like, shape=(n_samples, n_categories)): One-hot encoded categories.
 
         Returns:
-        -------
-        X : array-like, shape=(n_samples,)
-            The original categories.
+            X (array-like, shape=(n_samples,)): The original categories.
         """
         Xt = np.asarray(Xt)
         return [self.inverse_mapping_[i] for i in self._lb.inverse_transform(Xt)]
@@ -174,10 +164,8 @@ def __init__(self, X=None):
     def fit(self, X):
         """Fit a list or array of categories.
 
-        Parameters
-        ----------
-        X : array-like, shape=(n_categories,)
-            List of categories.
+        Args:
+            X (array-like, shape=(n_categories,)): List of categories.
         """
         X = np.asarray(X)
         if X.dtype == object:
@@ -192,35 +180,25 @@ def fit(self, X):
         return self
 
     def transform(self, X):
-        """Transform an array of categories to a one-hot encoded
-        representation.
+        """Transform an array of categories to a one-hot encoded representation.
 
-        Parameters
-        ----------
-        X : array-like, shape=(n_samples,)
-            List of categories.
+        Args:
+            X (array-like, shape=(n_samples,)): List of categories.
 
         Returns:
-        -------
-        Xt : array-like, shape=(n_samples, n_categories)
-            The integer categories.
+            Xt (array-like, shape=(n_samples, n_categories)): The integer categories.
         """
         X = np.asarray(X)
         return [self.mapping_[v] for v in X]
 
     def inverse_transform(self, Xt):
-        """Inverse transform integer categories back to their original
-           representation.
+        """Inverse transform integer categories back to their original representation.
 
-        Parameters
-        ----------
-        Xt : array-like, shape=(n_samples, n_categories)
-            Integer categories.
+        Args:
+            Xt (array-like, shape=(n_samples, n_categories)): Integer categories.
 
         Returns:
-        -------
-        X : array-like, shape=(n_samples,)
-            The original categories.
+            X (array-like, shape=(n_samples,)): The original categories.
         """
         if isinstance(Xt, (float, np.float64)):
             Xt = [Xt]
@@ -232,17 +210,11 @@ def inverse_transform(self, Xt):
 class Normalize(Transformer):
     """Scales each dimension into the interval [0, 1].
 
-    Parameters
-    ----------
-    low : float
-        Lower bound.
-
-    high : float
-        Higher bound.
-
-    is_int : bool, default=False
-        Round and cast the return value of `inverse_transform` to integer. Set
-        to `True` when applying this transform to integers.
+    Args:
+        low (float): Lower bound.
+        high (float): Higher bound.
+        is_int (bool): Round and cast the return value of `inverse_transform` to integer. Set
+            to `True` when applying this transform to integers. Defaults to ``False``.
     """
 
     def __init__(self, low, high, is_int=False):
@@ -252,6 +224,7 @@ def __init__(self, low, high, is_int=False):
         self._eps = 1e-8
 
     def transform(self, X):
+        """Apply the normalization."""
         X = np.asarray(X)
         if self.is_int:
             if np.any(np.round(X) > self.high):
@@ -275,6 +248,7 @@ def transform(self, X):
             return (X - self.low) / (self.high - self.low)
 
     def inverse_transform(self, X):
+        """Reverse the normalization."""
         X = np.asarray(X)
         if np.any(X > 1.0 + self._eps):
             raise ValueError("All values should be less than 1.0")
@@ -289,10 +263,8 @@ def inverse_transform(self, X):
 class Pipeline(Transformer):
     """A lightweight pipeline to chain transformers.
 
-    Parameters
-    ----------
-    transformers : list
-        A list of Transformer instances.
+    Args:
+        transformers (list): A list of Transformer instances.
     """
 
     def __init__(self, transformers):
@@ -305,16 +277,19 @@ def __init__(self, transformers):
                 )
 
     def fit(self, X):
+        """Initialize all transformers with current ``X``."""
         for transformer in self.transformers:
             transformer.fit(X)
         return self
 
     def transform(self, X):
+        """Apply the preprocessing mapping for all elements of ``X``."""
         for transformer in self.transformers:
             X = transformer.transform(X)
         return X
 
     def inverse_transform(self, X):
+        """Reverse the preprocessing mapping for all elements of ``X``."""
         for transformer in self.transformers[::-1]:
             X = transformer.inverse_transform(X)
         return X
diff --git a/src/deephyper/skopt/utils.py b/src/deephyper/skopt/utils.py
index 89726167..24e4995e 100644
--- a/src/deephyper/skopt/utils.py
+++ b/src/deephyper/skopt/utils.py
@@ -1,3 +1,5 @@
+"""Submodule of utility functions for skopt."""
+
 from collections import OrderedDict
 from copy import deepcopy
 from functools import wraps
@@ -166,26 +168,19 @@ def dump(res, filename, store_objective=True, **kwargs):
 
 
 def load(filename, **kwargs):
-    """Reconstruct a skopt optimization result from a file
-    persisted with deephyper.skopt.dump.
+    """Reconstruct a skopt optimization result from a file persisted with ``deephyper.skopt.dump``.
 
     .. note::
         Notice that the loaded optimization result can be missing
-        the objective function (`.specs['args']['func']`) if `deephyper.skopt.dump`
+        the objective function (``.specs['args']['func']``) if ``deephyper.skopt.dump``
         was called with `store_objective=False`.
 
-    Parameters
-    ----------
-    filename : string or `pathlib.Path`
-        The path of the file from which to load the optimization result.
-
-    **kwargs : other keyword arguments
-        All other keyword arguments will be passed to `joblib.load`.
+    Args:
+        filename (str or pathlib.Path): The path of the file from which to load the optimization result.
+        **kwargs : All other keyword arguments will be passed to `joblib.load`.
 
     Returns:
-    -------
-    res : `OptimizeResult`, scipy object
-        Reconstructed OptimizeResult instance.
+        res (OptimizeResult scipy object): Reconstructed OptimizeResult instance.
     """
     return load_(filename, **kwargs)
 
@@ -218,33 +213,21 @@ def check_x_in_space(x, space):
 
 
 def expected_minimum(res, n_random_starts=20, random_state=None):
-    """Compute the minimum over the predictions of the last surrogate model.
-    Uses `expected_minimum_random_sampling` with `n_random_starts` = 100000,
-    when the space contains any categorical values.
+    """Compute the minimum over the predictions of the last surrogate model. Uses `expected_minimum_random_sampling` with `n_random_starts` = 100000, when the space contains any categorical values.
 
     .. note::
         The returned minimum may not necessarily be an accurate
         prediction of the minimum of the true objective function.
 
-    Parameters
-    ----------
-    res : `OptimizeResult`, scipy object
-        The optimization result returned by a `skopt` minimizer.
-
-    n_random_starts : int, default=20
-        The number of random starts for the minimization of the surrogate
-        model.
-
-    random_state : int, RandomState instance, or None (default)
-        Set random state to something other than None for reproducible
-        results.
+    Args:
+        res (OptimizeResult scipy object): The optimization result returned by a `skopt` minimizer.
+        n_random_starts (int): The number of random starts for the minimization of the surrogate
+            model. Defaults to ``20``.
+        random_state (int or RandomState, Optional): Set random state to something other than None for reproducible results.
 
     Returns:
-    -------
-    x : list
-        location of the minimum.
-    fun : float
-        the surrogate function value at the minimum.
+        x (list): location of the minimum.
+        fun (float): the surrogate function value at the minimum.
     """
     if res.space.is_partly_categorical:
         return expected_minimum_random_sampling(
@@ -274,33 +257,22 @@ def func(x):
 
 
 def expected_minimum_random_sampling(res, n_random_starts=100000, random_state=None):
-    """Minimum search by doing naive random sampling, Returns the parameters
-    that gave the minimum function value. Can be used when the space
-    contains any categorical values.
+    """Minimum search by doing naive random sampling, Returns the parameters that gave the minimum function value. Can be used when the space contains any categorical values.
 
     .. note::
         The returned minimum may not necessarily be an accurate
         prediction of the minimum of the true objective function.
 
-    Parameters
-    ----------
-    res : `OptimizeResult`, scipy object
-        The optimization result returned by a `skopt` minimizer.
-
-    n_random_starts : int, default=100000
-        The number of random starts for the minimization of the surrogate
-        model.
-
-    random_state : int, RandomState instance, or None (default)
-        Set random state to something other than None for reproducible
+    Args:
+        res (OptimizeResult scipy object): The optimization result returned by a `skopt` minimizer.
+        n_random_starts (int): The number of random starts for the minimization of the surrogate
+        model. Defaults to ``100000``.
+        random_state (int or RandomState, Optional): Set random state to something other than None for reproducible
         results.
 
     Returns:
-    -------
-    x : list
-        location of the minimum.
-    fun : float
-        the surrogate function value at the minimum.
+        x (list):  location of the minimum.
+        fun (float): the surrogate function value at the minimum.
     """
     # sample points from search space
     random_samples = res.space.rvs(n_random_starts, random_state=random_state)
@@ -478,20 +450,13 @@ def cook_initial_point_generator(generator, **kwargs):
 
 
 def dimensions_aslist(search_space):
-    """Convert a dict representation of a search space into a list of
-    dimensions, ordered by sorted(search_space.keys()).
+    """Convert a dict representation of a search space into a list of dimensions, ordered by sorted(search_space.keys()).
 
-    Parameters
-    ----------
-    search_space : dict
-        Represents search space. The keys are dimension names (strings)
-        and values are instances of classes that inherit from the class
-        :class:`deephyper.skopt.space.Dimension` (Real, Integer or Categorical)
+    Args:
+        search_space (dict): Represents search space. The keys are dimension names (strings) and values are instances of classes that inherit from the class :class:`deephyper.skopt.space.Dimension` (Real, Integer or Categorical)
 
     Returns:
-    -------
-    params_space_list: list
-        list of deephyper.skopt.space.Dimension instances.
+        params_space_list (list): list of deephyper.skopt.space.Dimension instances.
 
     Examples:
     --------
@@ -512,28 +477,16 @@ def dimensions_aslist(search_space):
 
 
 def point_asdict(search_space, point_as_list):
-    """Convert the list representation of a point from a search space
-    to the dictionary representation, where keys are dimension names
-    and values are corresponding to the values of dimensions in the list.
+    """Convert the list representation of a point from a search space to the dictionary representation, where keys are dimension names and values are corresponding to the values of dimensions in the list.
 
     .. seealso:: :class:`deephyper.skopt.utils.point_aslist`
 
-    Parameters
-    ----------
-    search_space : dict
-        Represents search space. The keys are dimension names (strings)
-        and values are instances of classes that inherit from the class
-        :class:`deephyper.skopt.space.Dimension` (Real, Integer or Categorical)
-
-    point_as_list : list
-        list with parameter values.The order of parameters in the list
-        is given by sorted(params_space.keys()).
+    Args:
+        search_space (dict): Represents search space. The keys are dimension names (strings) and values are instances of classes that inherit from the class :class:`deephyper.skopt.space.Dimension` (Real, Integer or Categorical)
+        point_as_list (list): list with parameter values.The order of parameters in the list is given by sorted(params_space.keys()).
 
     Returns:
-    -------
-    params_dict : OrderedDict
-        dictionary with parameter names as keys to which
-        corresponding parameter values are assigned.
+        params_dict (OrderedDict): dictionary with parameter names as keys to which corresponding parameter values are assigned.
 
     Examples:
     --------
@@ -552,28 +505,16 @@ def point_asdict(search_space, point_as_list):
 
 
 def point_aslist(search_space, point_as_dict):
-    """Convert a dictionary representation of a point from a search space to
-    the list representation. The list of values is created from the values of
-    the dictionary, sorted by the names of dimensions used as keys.
+    """Convert a dictionary representation of a point from a search space to the list representation. The list of values is created from the values of the dictionary, sorted by the names of dimensions used as keys.
 
     .. seealso:: :class:`deephyper.skopt.utils.point_asdict`
 
-    Parameters
-    ----------
-    search_space : dict
-        Represents search space. The keys are dimension names (strings)
-        and values are instances of classes that inherit from the class
-        :class:`deephyper.skopt.space.Dimension` (Real, Integer or Categorical)
-
-    point_as_dict : dict
-        dict with parameter names as keys to which corresponding
-        parameter values are assigned.
+    Args:
+        search_space (dict): Represents search space. The keys are dimension names (strings) and values are instances of classes that inherit from the class :class:`deephyper.skopt.space.Dimension` (Real, Integer or Categorical).
+        point_as_dict (dict): dict with parameter names as keys to which corresponding parameter values are assigned.
 
     Returns:
-    -------
-    point_as_list : list
-        list with point values.The order of
-        parameters in the list is given by sorted(params_space.keys()).
+        point_as_list (list): list with point values.The order of parameters in the list is given by sorted(params_space.keys()).
 
     Examples:
     --------
@@ -627,8 +568,7 @@ def normalize_dimensions(dimensions):
 
 
 def check_list_types(x, types):
-    """Check whether all elements of a list `x` are of the correct type(s)
-    and raise a ValueError if they are not.
+    """Check whether all elements of a list `x` are of the correct type(s) and raise a ValueError if they are not.
 
     Note that `types` can be either a single object-type or a tuple
     of object-types.
@@ -636,14 +576,9 @@ def check_list_types(x, types):
     Raises `ValueError`, If one or more element in the list `x` is
     not of the correct type(s).
 
-    Parameters
-    ----------
-    x : list
-        List of objects.
-
-    types : object or list(object)
-        Either a single object-type or a tuple of object-types.
-
+    Args:
+        x (list): List of objects.
+        types (object or list(object)): Either a single object-type or a tuple of object-types.
     """
     # List of the elements in the list that are incorrectly typed.
     err = list(filter(lambda a: not isinstance(a, types), x))
@@ -656,14 +591,10 @@ def check_list_types(x, types):
 
 
 def check_dimension_names(dimensions):
-    """Check whether all dimensions have names. Raises `ValueError`,
-    if one or more dimensions are unnamed.
-
-    Parameters
-    ----------
-    dimensions : list(Dimension)
-        List of Dimension-objects.
+    """Check whether all dimensions have names. Raises `ValueError`, if one or more dimensions are unnamed.
 
+    Args:
+        dimensions (list(Dimension)): List of Dimension-objects.
     """
     # List of the dimensions that have no names.
     err_dims = list(filter(lambda dim: dim.name is None, dimensions))
@@ -676,8 +607,7 @@ def check_dimension_names(dimensions):
 
 
 def use_named_args(dimensions):
-    """Wrapper / decorator for an objective function that uses named arguments
-    to make it compatible with optimizers that use a single list of parameters.
+    """Wrapper / decorator for an objective function that uses named arguments to make it compatible with optimizers that use a single list of parameters.
 
     Your objective function can be defined as being callable using named
     arguments: `func(foo=123, bar=3.0, baz='hello')` for a search-space
@@ -733,29 +663,22 @@ def use_named_args(dimensions):
     >>> print("Best parameters: {}".format(result.x))
     Best parameters: [0.44134853091052617, 0.06570954323368307, 0.17586123323419825]
 
-    Parameters
-    ----------
-    dimensions : list(Dimension)
-        List of `Dimension`-objects for the search-space dimensions.
+
+    Args:
+        dimensions (list(Dimension)): List of `Dimension`-objects for the search-space dimensions.
 
     Returns:
-    -------
-    wrapped_func : callable
-        Wrapped objective function.
+        wrapped_func (callable): Wrapped objective function.
     """
 
     def decorator(func):
-        """This uses more advanced Python features to wrap `func` using a
-        function-decorator, which are not explained so well in the
-        official Python documentation.
+        """This uses more advanced Python features to wrap ``func`` using a function-decorator, which are not explained so well in the official Python documentation.
 
         A good video tutorial explaining how this works is found here:
         https://www.youtube.com/watch?v=KlBPCzcQNU8
 
-        Parameters
-        ----------
-        func : callable
-            Function to minimize. Should take *named arguments*
+        Args:
+            func (callable): Function to minimize. Should take *named arguments*
             and return the objective value.
         """
         # Ensure all dimensions are correctly typed.
@@ -766,22 +689,18 @@ def decorator(func):
 
         @wraps(func)
         def wrapper(x):
-            """This is the code that will be executed every time the
-            wrapped / decorated `func` is being called.
+            """This is the code that will be executed every time the wrapped / decorated `func` is being called.
+
             It takes `x` as a single list of parameters and
             converts them to named arguments and calls `func` with them.
 
-            Parameters
-            ----------
-            x : list
-                A single list of parameters e.g. `[123, 3.0, 'linear']`
+            Args:
+                x (list): A single list of parameters e.g. `[123, 3.0, 'linear']`
                 which will be converted to named arguments and passed
                 to `func`.
 
             Returns:
-            -------
-            objective_value
-                The objective value returned by `func`.
+                objective_value: The objective value returned by `func`.
             """
             # Ensure the number of dimensions match
             # the number of parameters in the list x.
diff --git a/src/deephyper/stopper/__init__.py b/src/deephyper/stopper/__init__.py
index 1a3424ca..e14a5580 100644
--- a/src/deephyper/stopper/__init__.py
+++ b/src/deephyper/stopper/__init__.py
@@ -1,8 +1,20 @@
-"""The ``stopper`` module provides features to observe intermediate performances of iterative algorithm and decide dynamically if its evaluation should be stopped or continued.
+"""The ``stopper`` module.
 
-This module was inspired from the Pruner interface and implementation of `Optuna <https://optuna.readthedocs.io/en/stable/reference/pruners.html>`_.
+This module provides features to observe intermediate performances of
+iterative algorithm and decide dynamically if its evaluation should be
+stopped or continued.
 
-The ``Stopper`` class is the base class for all stoppers. It provides the interface for the ``observe`` and ``stop`` methods that should be implemented by all stoppers. The ``observe`` method is called at each iteration of the iterative algorithm and the ``stop`` method is called at the end of each iteration to decide if the evaluation should be stopped or continued. The stopper object is not used directly but through the ``RunningJob`` received by the ``run``-function. In the following example we demonstrate with a simulation how it can be used:
+This module was inspired from the Pruner interface and implementation of
+`Optuna <https://optuna.readthedocs.io/en/stable/reference/pruners.html>`_.
+
+The ``Stopper`` class is the base class for all stoppers. It provides the
+interface for the ``observe`` and ``stop`` methods that should be implemented
+by all stoppers. The ``observe`` method is called at each iteration of the
+iterative algorithm and the ``stop`` method is called at the end of each
+iteration to decide if the evaluation should be stopped or continued. The
+stopper object is not used directly but through the ``RunningJob`` received
+by the ``run``-function. In the following example we demonstrate with a
+simulation how it can be used:
 
 .. code-block:: python
 
@@ -44,10 +56,11 @@ def run(job):
     results = search.search(timeout=10)
 
 
-As it can be observed in the following results many evaluation stopped after the first iteration which saved
-a lot of computation time. If evaluated fully, each configuration would take about 1 seconds and we would be able
-to compute only a maximum of 10 configurations (because we set a timeout of 10). However, with the stopper we managed
-to perform 15 evaluations instead.
+As it can be observed in the following results many evaluation stopped after
+the first iteration which saved a lot of computation time. If evaluated
+fully, each configuration would take about 1 seconds and we would be able to
+compute only a maximum of 10 configurations (because we set a timeout of 10).
+However, with the stopper we managed to perform 15 evaluations instead.
 
 .. code-block:: console
 
diff --git a/src/deephyper/stopper/_asha_stopper.py b/src/deephyper/stopper/_asha_stopper.py
index 55d18d58..4d5cbcd7 100644
--- a/src/deephyper/stopper/_asha_stopper.py
+++ b/src/deephyper/stopper/_asha_stopper.py
@@ -19,17 +19,36 @@ class SuccessiveHalvingStopper(Stopper):
           - ❌
           - ✅
 
-    The Successive Halving (SHA) was proposed in `Non-stochastic Best Arm IdentiÞcation and Hyperparameter Optimization <http://proceedings.mlr.press/v51/jamieson16.pdf>`_ in the context of a fixed number of hyperparameter configurations. The SHA algorithm was synchronous at the time and therefore not efficient when using parallel ressources. The Sucessive Halving algorithm was then extended to be asynchronous in `A System for Massively Parallel Hyperparameter Tuning <https://arxiv.org/abs/1810.05934>`_.
-
-    Halving is a technique to reduce the number of configurations to evaluate by a factor of ``reduction_factor``. The halving schedule is following a geometric progression. The first halving step is done after ``min_steps`` steps. The next halving step is done after ``min_steps * reduction_factor`` steps. The next halving step is done after ``min_steps * reduction_factor^2`` steps. And so on.
+    The Successive Halving (SHA) was proposed in `Non-stochastic Best Arm
+    IdentiÞcation and Hyperparameter Optimization
+    <http://proceedings.mlr.press/v51/jamieson16.pdf>`_ in the context of a
+    fixed number of hyperparameter configurations. The SHA algorithm was
+    synchronous at the time and therefore not efficient when using parallel
+    ressources. The Sucessive Halving algorithm was then extended to be
+    asynchronous in `A System for Massively Parallel Hyperparameter Tuning
+    <https://arxiv.org/abs/1810.05934>`_.
+
+    Halving is a technique to reduce the number of configurations to evaluate
+    by a factor of ``reduction_factor``. The halving schedule is following a
+    geometric progression. The first halving step is done after ``min_steps``
+    steps. The next halving step is done after ``min_steps *
+    reduction_factor`` steps. The next halving step is done after
+    ``min_steps * reduction_factor^2`` steps. And so on.
 
     Args:
-        max_steps (int): The maximum number of steps to run the evaluation (e.g., number of epochs).
-        min_steps (float, optional): The minimum number of steps to run the evaluation. Defaults to 1.
-        reduction_factor (float, optional): At each halving step the current model is kept only if among the top-``1/reduction_factor*100``%. Defaults to 3.
-        min_early_stopping_rate (float, optional): A parameter to delay the halving schedule. Defaults to 0.
-        min_competing (int, optional): The minimum number of competitors necessary to check the top-k condition. Defaults to 0.
-        min_fully_completed (int, optional): The minimum number of evaluation evaluated with ``max_steps``. Defaults to 1.
+        max_steps (int):
+            The maximum number of steps to run the evaluation (e.g., number of epochs).
+        min_steps (float, optional):
+            The minimum number of steps to run the evaluation. Defaults to 1.
+        reduction_factor (float, optional):
+            At each halving step the current model is kept only if among the
+            top-``1/reduction_factor*100``%. Defaults to 3.
+        min_early_stopping_rate (float, optional):
+            A parameter to delay the halving schedule. Defaults to 0.
+        min_competing (int, optional):
+            The minimum number of competitors necessary to check the top-k condition. Defaults to 0.
+        min_fully_completed (int, optional):
+            The minimum number of evaluation evaluated with ``max_steps``. Defaults to 1.
     """
 
     def __init__(
diff --git a/src/deephyper/stopper/_const_stopper.py b/src/deephyper/stopper/_const_stopper.py
index 488572da..200da4a5 100644
--- a/src/deephyper/stopper/_const_stopper.py
+++ b/src/deephyper/stopper/_const_stopper.py
@@ -16,8 +16,11 @@ class ConstantStopper(Stopper):
           - ✅
 
     Args:
-        max_steps (int): the maximum number of steps which should be performed to evaluate the configuration fully.
-        stop_step (int): the step at which to stop the evaluation.
+        max_steps (int):
+            The maximum number of steps which should be performed to evaluate
+            the configuration fully.
+        stop_step (int):
+            The step at which to stop the evaluation.
     """
 
     def __init__(self, max_steps: int, stop_step: int) -> None:
diff --git a/src/deephyper/stopper/_lcmodel_stopper.py b/src/deephyper/stopper/_lcmodel_stopper.py
index 411f2fc9..8b189e61 100644
--- a/src/deephyper/stopper/_lcmodel_stopper.py
+++ b/src/deephyper/stopper/_lcmodel_stopper.py
@@ -19,8 +19,9 @@ def area_learning_curve(z, f, z_max) -> float:
 
 
 class LCModelStopper(Stopper):
-    """Stopper based on learning curve extrapolation (LCE) to evaluate if the iterations of the learning algorithm
-    should be stopped.
+    """Stopper based on learning curve extrapolation (LCE).
+
+    This is to evaluate if the iterations of the learning algorithm should be stopped.
 
     .. list-table::
         :widths: 25 25 25
@@ -33,13 +34,28 @@ class LCModelStopper(Stopper):
           - ❌
           - ❌
 
-    The LCE is based on a parametric learning curve model (LCM) which is modeling the score as a function of the number of training steps. Training steps can correspond to the number of training epochs, the number of training batches, the number of observed samples or any other quantity that is iterated through during the training process. The LCE is based on the following steps:
-
-    1. An early stopping condition is always checked first. If the early stopping condition is met, the LCE is not applied.
-    2. Then, some safeguard conditions are checked to ensure that the LCE can be applied (number of observed steps must be greater or equal to the number of parameters of the LCM).
-    3. If the LCM cannot be fitted (number of observed steps is less than number of parameters of the model), then the last observed step is compared to hitorical performance of others at the same step to check if it is a low-performing outlier (outlier in the direction of performing worse!) using the IQR criterion.
-    4. If the LCM can be fitted, a least square fit is performed to estimate the parameters of the LCM.
-    5. The probability of the current LC to perform worse than the best observed score at the maximum iteration is computed using Monte-Carlo Markov Chain (MCMC).
+    The LCE is based on a parametric learning curve model (LCM) which is
+    modeling the score as a function of the number of training steps.
+    Training steps can correspond to the number of training epochs, the
+    number of training batches, the number of observed samples or any other
+    quantity that is iterated through during the training process. The LCE is
+    based on the following steps:
+
+    1. An early stopping condition is always checked first. If the early
+       stopping condition is met, the LCE is not applied.
+    2. Then, some safeguard conditions are checked to ensure that the LCE can
+       be applied (number of observed steps must be greater or equal to the
+       number of parameters of the LCM).
+    3. If the LCM cannot be fitted (number of observed steps is less than
+       number of parameters of the model), then the last observed step is
+       compared to hitorical performance of others at the same step to check if
+       it is a low-performing outlier (outlier in the direction of performing
+       worse!) using the IQR criterion.
+    4. If the LCM can be fitted, a least square fit is performed to estimate
+       the parameters of the LCM.
+    5. The probability of the current LC to perform worse than the best
+       observed score at the maximum iteration is computed using Monte-Carlo
+       Markov Chain (MCMC).
 
     To use this stopper, you need to install the following dependencies:
 
@@ -49,17 +65,56 @@ class LCModelStopper(Stopper):
         $ numpyro
 
     Args:
-        max_steps (int): The maximum number of training steps which can be performed.
-        min_steps (int, optional): The minimum number of training steps which can be performed. Defaults to ``4``. It is better to have at least as many steps as the number of parameters of the fitted learning curve model. For example, if ``lc_model="mmf4"`` then ``min_steps`` should be at least ``4``.
-        lc_model (str, optional): The parameteric learning model to use. It should be a string in the following list: ``["lin2", "loglin2", "loglin3", "loglin4", "pow3","mmf4", "vapor3", "logloglin2", "hill3", "logpow3", "pow4", "exp4", "janoschek4", "weibull4", "ilog2"]``. The number in the name corresponds to the number of parameters of the parametric model. Defaults to ``"mmf4"``.
-        min_obs_to_fit_lc_model (int, optional): The minimum number of observed scores to fit the learning curve model. Defaults to ``4`` because the ``"mmf4"`` model has 4 parameters.
-        min_done_for_outlier_detection (int, optional): The minimum number of observed scores at the same step to check for if it is a lower-bound outlier. Defaults to ``10``.
-        iqr_factor_for_outlier_detection (float, optional): The IQR factor for outlier detection. The higher it is the more inclusive the condition will be (i.e. if set very large it is likely not going to detect any outliers). Defaults to ``1.5``.
-        prob_promotion (float, optional): The threshold probabily to stop the iterations. If the current learning curve has a probability greater than ``prob_promotion`` to be worse that the best observed score accross all evaluations then the current iterations are stopped. Defaults to ``0.9`` (i.e. probability of 0.9 of being worse).
-        early_stopping_patience (float, optional): The patience of the early stopping condition. If it is an ``int`` it is directly corresponding to a number of iterations. If it is a ``float`` then it is corresponding to a proportion between [0,1] w.r.t. ``max_steps``. Defaults to ``0.25`` (i.e. 25% of ``max_steps``).
-        reduction_factor (int, optional): The reduction factor of the number of steps to wait before stopping the evaluation. Defaults to ``1`` to extrapolate the learning curve at every step.
-        objective_returned (str, optional): The returned objective. It can be a value in ``["last", "max", "alc"]`` where ``"last"`` corresponds to the last observed score, ``"max"`` corresponds to the maximum observed score and ``"alc"`` corresponds to the area under the learning curve. Defaults to "last".
-        random_state (int or np.RandomState, optional): The random state of estimation process. Defaults to ``None``.
+        max_steps (int):
+            The maximum number of training steps which can be performed.
+        min_steps (int, optional):
+            The minimum number of training steps which can be performed.
+            Defaults to ``4``. It is better to have at least as many steps as
+            the number of parameters of the fitted learning curve model. For
+            example, if ``lc_model="mmf4"`` then ``min_steps`` should be at
+            least ``4``.
+        lc_model (str, optional):
+            The parameteric learning model to use. It should be a string in
+            the following list: ``
+            ["lin2", "loglin2", "loglin3", "loglin4", "pow3","mmf4", "vapor3",
+            "logloglin2", "hill3", "logpow3", "pow4", "exp4", "janoschek4", "weibull4",
+            "ilog2"]``. The number in the name corresponds to the number of
+            parameters of the parametric model. Defaults to ``"mmf4"``.
+        min_obs_to_fit_lc_model (int, optional):
+            The minimum number of observed scores to fit the learning curve
+            model. Defaults to ``4`` because the ``"mmf4"`` model has 4
+            parameters.
+        min_done_for_outlier_detection (int, optional):
+            The minimum number of observed scores at the same step to check
+            for if it is a lower-bound outlier. Defaults to ``10``.
+        iqr_factor_for_outlier_detection (float, optional):
+            The IQR factor for outlier detection. The higher it is the more
+            inclusive the condition will be (i.e. if set very large it is
+            likely not going to detect any outliers). Defaults to ``1.5``.
+        prob_promotion (float, optional):
+            The threshold probabily to stop the iterations. If the current
+            learning curve has a probability greater than ``prob_promotion``
+            to be worse that the best observed score accross all evaluations
+            then the current iterations are stopped. Defaults to ``0.9``
+            (i.e. probability of 0.9 of being worse).
+        early_stopping_patience (float, optional):
+            The patience of the early stopping condition. If it is an ``int``
+            it is directly corresponding to a number of iterations. If it is
+            a ``float`` then it is corresponding to a proportion between
+            [0,1] w.r.t. ``max_steps``. Defaults to ``0.25`` (i.e. 25% of
+            ``max_steps``).
+        reduction_factor (int, optional):
+            The reduction factor of the number of steps to wait before
+            stopping the evaluation. Defaults to ``1`` to extrapolate the
+            learning curve at every step.
+        objective_returned (str, optional):
+            The returned objective. It can be a value in ``
+            ["last", "max", "alc"]`` where ``"last"`` corresponds to the last
+            observed score, ``"max"`` corresponds to the maximum observed
+            score and ``"alc"`` corresponds to the area under the learning
+            curve. Defaults to "last".
+        random_state (int or np.RandomState, optional):
+            The random state of estimation process. Defaults to ``None``.
 
     Raises:
         ValueError: parameters are not valid.
@@ -82,9 +137,7 @@ def __init__(
         super().__init__(max_steps=max_steps)
         self.min_steps = min_steps
 
-        self._f_model = BayesianLearningCurveRegressor.get_parametrics_model_func(
-            lc_model
-        )
+        self._f_model = BayesianLearningCurveRegressor.get_parametrics_model_func(lc_model)
         self._f_model_nparams = int(lc_model[-1])
         self._min_obs_to_fit_lc_model = min_obs_to_fit_lc_model
         self._reduction_factor = reduction_factor
@@ -141,9 +194,7 @@ def _retrieve_best_objective(self) -> float:
 
     def _get_competiting_objectives(self, rung) -> list:
         search_id, _ = self.job.id.split(".")
-        values = self.job.storage.load_metadata_from_all_jobs(
-            search_id, f"_completed_rung_{rung}"
-        )
+        values = self.job.storage.load_metadata_from_all_jobs(search_id, f"_completed_rung_{rung}")
         # Filter out non numerical values (e.g., "F" for failed jobs)
         values = [v for v in values if isinstance(v, Number)]
         return values
@@ -204,10 +255,7 @@ def stop(self) -> bool:
                     )
                     iqr = q3 - q1
                     # lower than the minimum of a box plot
-                    if (
-                        self._objective
-                        < q1 - self.iqr_factor_for_outlier_detection * iqr
-                    ):
+                    if self._objective < q1 - self.iqr_factor_for_outlier_detection * iqr:
                         self.infos_stopped = "outlier"
                         return True
                 self._rung += 1
diff --git a/src/deephyper/stopper/_stopper.py b/src/deephyper/stopper/_stopper.py
index f7c05e46..aed25693 100644
--- a/src/deephyper/stopper/_stopper.py
+++ b/src/deephyper/stopper/_stopper.py
@@ -28,9 +28,14 @@ def to_json(self):
         return json_format
 
     def transform_objective(self, objective: float):
-        """Replaces the currently observed objective by the maximum objective observed from the
-        start. Identity transformation by default.
+        """Replaces currently observed objective by the maximum objective observed from the start.
+
+        By default the identity transformation is used.
+
+        Args:
+            objective (float): the observed objective value.
         """
+        # TODO: check if this code should be removed
         # prev_objective = (
         #     self.observed_objectives[-1] if len(self.observed_objectives) > 0 else None
         # )
@@ -76,7 +81,7 @@ def stop(self) -> bool:
 
     @property
     def observations(self) -> list:
-        """Returns a copy of the list of observations with 0-index the budgets and 1-index the objectives."""
+        """Returns copy of the list of observations with 0-index budgets and 1-index objectives."""
         obs = [self.observed_budgets, self.observed_objectives]
         return copy.deepcopy(obs)
 
diff --git a/src/deephyper/stopper/integration/deepxde.py b/src/deephyper/stopper/integration/deepxde.py
index 74c42962..775aa7f7 100644
--- a/src/deephyper/stopper/integration/deepxde.py
+++ b/src/deephyper/stopper/integration/deepxde.py
@@ -1,25 +1,37 @@
+"""Submodule of DeepXDE integrations for the ``Stopper``."""
+
 from deepxde.callbacks import Callback
 
+from deephyper.evaluator import RunningJob
+
 
 class DeepXDEStopperCallback(Callback):
-    def __init__(self, job, mode="min", monitor="loss_test"):
-        super().__init__()
-        """Callback to use in conjonction with a DeepHyper ``RunningJob`` to stop the training when the ``Stopper`` is triggered.
+    """Callback to use in conjonction with a DeepHyper ``RunningJob``.
 
-        .. code-block:: python
+    This stops the training when the ``Stopper`` is triggered.
 
-            def run(job):
-                callback = DeepXDEStopperCallback(job, ...)
-                ...
-                model.train(..., callbacks=[callback])
-                ...
+    .. code-block:: python
 
-        Args:
-            job (RunningJob): The running job created by DeepHyper.
-            monitor (str, optional): The metric to monitor. It can be any metric collected in the ``History``. Defaults to "loss_test" (equivalent to validation loss).
-            mode (str, optional): If the metric is maximized or minimized. Value in ``["max", "min"]``. Defaults to "min".
-        """
+        def run(job):
+            callback = DeepXDEStopperCallback(job, ...)
+            ...
+            model.train(..., callbacks=[callback])
+            ...
 
+    Args:
+        job (RunningJob):
+            The running job created by DeepHyper.
+        monitor (str, optional):
+            The metric to monitor. It can be any metric collected in the
+            ``History``. Defaults to "loss_test" (equivalent to validation
+            loss).
+        mode (str, optional):
+            If the metric is maximized or minimized. Value in ``
+            ["max", "min"]``. Defaults to "min".
+    """
+
+    def __init__(self, job: RunningJob, mode: str = "min", monitor: str = "loss_test"):
+        super().__init__()
         self.job = job
         self.monitor = monitor
 
@@ -30,11 +42,12 @@ def run(job):
         self.stopped = False
 
     def on_epoch_end(self):
+        """Called at the end of each epoch during training."""
         self.budget += 1
-        self.observe_and_stop(self.budget)
+        self._observe_and_stop(self.budget)
 
-    def observe_and_stop(self, budget):
-        objective = self.get_monitor_value()
+    def _observe_and_stop(self, budget):
+        objective = self._get_monitor_value()
 
         if self.mode == "min":
             objective = -objective
@@ -44,7 +57,7 @@ def observe_and_stop(self, budget):
             self.model.stop_training = True
             self.stopped = True
 
-    def get_monitor_value(self):
+    def _get_monitor_value(self):
         if self.monitor == "loss_train":
             result = sum(self.model.train_state.loss_train)
         elif self.monitor == "loss_test":
diff --git a/src/deephyper/stopper/integration/tf_keras2.py b/src/deephyper/stopper/integration/tf_keras2.py
index 24299726..a5c80b1d 100644
--- a/src/deephyper/stopper/integration/tf_keras2.py
+++ b/src/deephyper/stopper/integration/tf_keras2.py
@@ -1,25 +1,37 @@
+"""Submodule of Tensorflow/Keras integrations for the ``Stopper``."""
+
 import warnings
 
 from tf_keras.callbacks import Callback
 
+from deephyper.evaluator import RunningJob
+
 
 class TFKerasStopperCallback(Callback):
-    def __init__(self, job, monitor="val_loss", mode="min") -> None:
-        """Callback to use in conjonction with a DeepHyper ``RunningJob`` to stop the training when the ``Stopper`` is triggered.
+    """Tensorflow/Keras callback to be used with a DeepHyper ``RunningJob``.
 
-        .. code-block:: python
+    This stops the training when the ``Stopper`` is triggered.
 
-            def run(job):
-                callback = TFKerasStopperCallback(job, ...)
-                ...
-                model.fit(..., callbacks=[callback])
-                ...
+    .. code-block:: python
 
-        Args:
-            job (RunningJob): The running job created by DeepHyper.
-            monitor (str, optional): The metric to monitor. It can be any metric collected in the ``History``. Defaults to "val_loss".
-            mode (str, optional): If the metric is maximized or minimized. Value in ``["max", "min"]``. Defaults to "max".
-        """
+        def run(job):
+            callback = TFKerasStopperCallback(job, ...)
+            ...
+            model.fit(..., callbacks=[callback])
+            ...
+
+    Args:
+        job (RunningJob):
+            The running job created by DeepHyper.
+        monitor (str, optional):
+            The metric to monitor. It can be any metric collected in the
+            ``History``. Defaults to ``"val_loss"``.
+        mode (str, optional):
+            If the metric is maximized or minimized. Value in ``
+            ["max", "min"]``. Defaults to ``"max"``.
+    """
+
+    def __init__(self, job: RunningJob, monitor: str = "val_loss", mode: str = "min"):
         super().__init__()
         self.job = job
         self.monitor = monitor
@@ -30,10 +42,20 @@ def run(job):
         self.budget = 0
 
     def on_epoch_end(self, epoch, logs=None):
+        """Called at the end of an epoch during training.
+
+        Args:
+            epoch: Integer, index of epoch.
+            logs: Dict, metric results for this training epoch, and for the
+              validation epoch if validation is performed. Validation result
+              keys are prefixed with `val_`. For training epoch, the values of
+              the `Model`'s metrics are returned. Example:
+              `{'loss': 0.2, 'accuracy': 0.7}`.
+        """
         self.budget += 1
-        self.observe_and_stop(self.budget, logs)
+        self._observe_and_stop(self.budget, logs)
 
-    def observe_and_stop(self, budget, logs):
+    def _observe_and_stop(self, budget, logs):
         if logs is None:
             return
 
@@ -41,7 +63,8 @@ def observe_and_stop(self, budget, logs):
 
         if objective is None:
             warnings.warn(
-                f"Monitor {self.monitor} is not found in the history logs. Stopper will not be able to stop the training. Available logs are: {list(logs.keys())}"
+                f"Monitor {self.monitor} is not found in the history logs. Stopper will not be "
+                "able to stop the training. Available logs are: {list(logs.keys())}"
             )
             return
 
diff --git a/src/deephyper/stopper/lce/_bayesian_regression.py b/src/deephyper/stopper/lce/_bayesian_regression.py
index 7681d4c1..0b7353d5 100644
--- a/src/deephyper/stopper/lce/_bayesian_regression.py
+++ b/src/deephyper/stopper/lce/_bayesian_regression.py
@@ -43,9 +43,7 @@ def f_pow3(z, rho):
 
 @jax.jit
 def f_mmf4(z, rho):
-    return (rho[0] * rho[1] + rho[2] * jnp.power(z, rho[3])) / (
-        rho[1] + jnp.power(z, rho[3])
-    )
+    return (rho[0] * rho[1] + rho[2] * jnp.power(z, rho[3])) / (rho[1] + jnp.power(z, rho[3]))
 
 
 @jax.jit
@@ -125,9 +123,7 @@ def fit_learning_curve_model_least_square(
     random_state=None,
     verbose=0,
 ):
-    """The learning curve model is assumed to be modeled by 'f' with
-    interface f(z, rho).
-    """
+    """The learning curve model is assumed to be modeled by 'f' with interface f(z, rho)."""
     random_state = check_random_state(random_state)
 
     results = []
@@ -177,9 +173,7 @@ def prob_model(
     num_obs=None,
 ):
     rho = numpyro.sample("rho", dist.Normal(rho_mu_prior, rho_sigma_prior))
-    y_sigma = numpyro.sample(
-        "sigma", dist.Exponential(y_sigma_prior)
-    )  # introducing noise
+    y_sigma = numpyro.sample("sigma", dist.Exponential(y_sigma_prior))  # introducing noise
     y_mu = f(z[:num_obs], rho)
     numpyro.sample("obs", dist.Normal(y_mu, y_sigma), obs=y[:num_obs])
 
@@ -197,16 +191,29 @@ class BayesianLearningCurveRegressor(BaseEstimator, RegressorMixin):
     """Probabilistic model for learning curve regression.
 
     Args:
-        f_model (callable, optional): The model function to use. Defaults to `f_power3` for a Power-Law with 3 parameters.
-        f_model_nparams (int, optional): The number of parameters of the model. Defaults to `3`.
-        max_trials_ls_fit (int, optional): The number of least-square fits that should be tried. Defaults to `10`.
-        mcmc_kernel (str, optional): The MCMC kernel to use. It should be a string in the following list: `["NUTS", "BarkerMH"]`. Defaults to `"NUTS"`.
-        mcmc_num_warmup (int, optional): The number of warmup steps in MCMC. Defaults to `200`.
-        mcmc_num_samples (int, optional): The number of samples in MCMC. Defaults to `1_000`.
-        random_state (int, optional): A random state. Defaults to `None`.
-        verbose (int, optional): Wether or not to use the verbose mode. Defaults to `0` to deactive it.
-        batch_size (int, optional): The expected maximum length of the X, y arrays (used in the `fit(X, y)` method) in order to preallocate memory and compile the code only once. Defaults to `100`.
-        min_max_scaling (bool, optional): Wether or not to use min-max scaling in [0,1] for `y` values. Defaults to False.
+        f_model (callable, optional):
+            The model function to use. Defaults to `f_power3` for a Power-Law with 3 parameters.
+        f_model_nparams (int, optional):
+            The number of parameters of the model. Defaults to `3`.
+        max_trials_ls_fit (int, optional):
+            The number of least-square fits that should be tried. Defaults to `10`.
+        mcmc_kernel (str, optional):
+            The MCMC kernel to use. It should be a string in the following
+            list: `["NUTS", "BarkerMH"]`. Defaults to `"NUTS"`.
+        mcmc_num_warmup (int, optional):
+            The number of warmup steps in MCMC. Defaults to `200`.
+        mcmc_num_samples (int, optional):
+            The number of samples in MCMC. Defaults to `1_000`.
+        random_state (int, optional):
+            A random state. Defaults to `None`.
+        verbose (int, optional):
+            Wether or not to use the verbose mode. Defaults to `0` to deactive it.
+        batch_size (int, optional):
+            The expected maximum length of the X, y arrays (used in the `fit
+            (X, y)` method) in order to preallocate memory and compile the
+            code only once. Defaults to `100`.
+        min_max_scaling (bool, optional):
+            Wether or not to use min-max scaling in [0,1] for `y` values. Defaults to False.
     """
 
     def __init__(
@@ -244,9 +251,14 @@ def fit(self, X, y, update_prior=True):
         """Fit the model.
 
         Args:
-            X (np.ndarray): a 1-D array of inputs.
-            y (_type_): a 1-D array of targets.
-            update_prior (bool, optional): A boolean indicating if the prior distribution should be updated using least-squares before running the Bayesian inference. Defaults to ``True``.
+            X (np.ndarray):
+                A 1-D array of inputs.
+            y (_type_):
+                A 1-D array of targets.
+            update_prior (bool, optional):
+                A boolean indicating if the prior distribution should be
+                updated using least-squares before running the Bayesian
+                inference. Defaults to ``True``.
 
         Raises:
             ValueError: if input arguments are invalid.
@@ -341,11 +353,18 @@ def predict(self, X, return_std=True):
         """Predict the mean and standard deviation of the model.
 
         Args:
-            X (np.ndarray): a 1-D array of inputs.
-            return_std (bool, optional): A boolean indicating if the standard-deviation representing uncertainty in the prediction should be returned. Defaults to ``True``.
+            X (np.ndarray):
+                A 1-D array of inputs.
+            return_std (bool, optional):
+                A boolean indicating if the standard-deviation representing
+                uncertainty in the prediction should be returned. Defaults to
+                ``True``.
 
         Returns:
-            Tuple[np.ndarray, np.ndarray]: the mean prediction with shape ``(len(X),)`` and the standard deviation with shape ``(len(X),)`` if ``return_std`` is ``True``.
+            Tuple[np.ndarray, np.ndarray]:
+                The mean prediction with shape ``(len(X),)`` and the standard
+                deviation with shape ``(len(X),)`` if ``return_std`` is
+                ``True``.
         """
         posterior_samples = self.predict_posterior_samples(X)
 
@@ -364,7 +383,10 @@ def predict_posterior_samples(self, X):
             X (np.ndarray): a 1-D array of inputs.
 
         Returns:
-            np.ndarray: a 2-D array of shape (n_samples, len(X)) where n_samples is the number of samples and len(X) is the length of the input array.
+            np.ndarray:
+                A 2-D array of shape (n_samples, len(X)) where n_samples is
+                the number of samples and len(X) is the length of the input
+                array.
         """
         # Check if fit has been called
         check_is_fitted(self)
@@ -382,8 +404,9 @@ def predict_posterior_samples(self, X):
         return posterior_mu
 
     def prob(self, X, condition):
-        """Compute the approximate probability of P(cond(m(X_i), y_i))
-        where m is the current fitted model and cond a condition.
+        """Compute the approximate probability of P(cond(m(X_i), y_i)).
+
+        Where m is the current fitted model and cond a condition.
 
         Args:
             X (np.array): An array of inputs.
@@ -402,12 +425,20 @@ def prob(self, X, condition):
     def get_parametrics_model_func(name):
         """Return the function of the learning curve model given its name.
 
-        Should be one of ``["lin2", "pow3", "mmf4", "vapor3", "logloglin2", "hill3", "logpow3", "pow4", "exp4", "janoschek4", "weibull4", "ilog2", "arctan3"]`` where the integer suffix indicates the number of parameters of the model.
+        Should be one of ``
+        ["lin2", "pow3", "mmf4", "vapor3", "logloglin2", "hill3", "logpow3", "pow4",
+        "exp4", "janoschek4", "weibull4", "ilog2", "arctan3"]`` where the
+        integer suffix indicates the number of parameters of the model.
 
         Args:
             name (str): The name of the learning curve model.
 
         Returns:
-            callable: a function with signature ``f(x, rho)`` of the learning curve model where ``x`` is a possible input of the model and ``rho`` is a 1-D array for the parameters of the model with length equal to the number of parameters of the model (e.g., it is of length ``3`` for ``"pow3"``).
+            callable:
+                A function with signature ``f(x, rho)`` of the learning curve
+                model where ``x`` is a possible input of the model and
+                ``rho`` is a 1-D array for the parameters of the model with
+                length equal to the number of parameters of the model
+                (e.g., it is of length ``3`` for ``"pow3"``).
         """
         return getattr(sys.modules[__name__], f"f_{name}")


Test patch:
diff --git a/.github/workflows/checks-tests.yml b/.github/workflows/checks-tests.yml
index 39f5019a..dcd04b8e 100644
--- a/.github/workflows/checks-tests.yml
+++ b/.github/workflows/checks-tests.yml
@@ -32,10 +32,10 @@ jobs:
           pip install tox ruff
 
       - name: Run linter checks with ruff
-        run: ruff check examples src
+        run: ruff check 
 
       - name: Run formatter checks with ruff
-        run: ruff format --check examples src
+        run: ruff format --check 
 
       - name: Run tests with tox
         run: tox -e py3
diff --git a/src/deephyper/test/__init__.py b/src/deephyper/test/__init__.py
index ff81741b..901e9eb9 100644
--- a/src/deephyper/test/__init__.py
+++ b/src/deephyper/test/__init__.py
@@ -1,4 +1,4 @@
-"""Subpackage dedicated to reusable testing tools for DeepHyper"""
+"""Subpackage dedicated to reusable testing tools for DeepHyper."""
 
 from ._command import run
 from ._parse_result import parse_result
diff --git a/src/deephyper/test/_command.py b/src/deephyper/test/_command.py
index 7ae98c6d..f1e0825f 100644
--- a/src/deephyper/test/_command.py
+++ b/src/deephyper/test/_command.py
@@ -2,11 +2,16 @@
 import sys
 
 
-def run(command, live_output=False):
+def run(command: str, live_output: bool = False):
     """Test command line interface.
 
     Args:
-        command (str): the command line as a str.
+        command (str):
+            The command line as a string.
+        live_output (bool, Optional):
+            Boolean that indicates if the STDOUT/STDERR streams from the
+            launched subprocess should directly be redirected to the streams
+            of the parent process.
     """
     command = command.split()
     try:
diff --git a/src/deephyper/test/_parse_result.py b/src/deephyper/test/_parse_result.py
index e563690f..c9aa032c 100644
--- a/src/deephyper/test/_parse_result.py
+++ b/src/deephyper/test/_parse_result.py
@@ -2,7 +2,9 @@
 
 
 def parse_result(stream: str) -> float:
-    """Parse the output of a DeepHyper test. The format of the parsed output should be as follows:
+    """Parse the output of a DeepHyper test.
+
+    The format of the parsed output should be as follows:
 
     .. code-block::
 
diff --git a/tests/hpo/test_hp_problem.py b/tests/hpo/test_hp_problem.py
index 74a7e0d9..b5cc1910 100644
--- a/tests/hpo/test_hp_problem.py
+++ b/tests/hpo/test_hp_problem.py
@@ -76,11 +76,10 @@ def test_kwargs(self):
         pb.add_hyperparameter(value=(-10, 10), name="dim0")
 
     def test_dim_with_wrong_name(self):
-        from deephyper.core.exceptions.problem import SpaceDimNameOfWrongType
         from deephyper.hpo import HpProblem
 
         pb = HpProblem()
-        with pytest.raises(SpaceDimNameOfWrongType):
+        with pytest.raises(TypeError):
             pb.add_hyperparameter((-10, 10), 0)
 
     def test_config_space_hp(self):