Repository: MachineLearningLifeScience/poli (language: python)

Issue:
Set up automatic documentation
Using sphinx, we could set up automatic documentation of the API of `poli`.


Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index c1227e84..091e36e7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -20,4 +20,8 @@ wandb/
 
 # Ignore temporary files
 tmp/
-src/poli/objective_repository/rasp/101m.pdb
\ No newline at end of file
+src/poli/objective_repository/rasp/101m.pdb
+
+# Documentation
+docs/build/
+docs/source/generated/
\ No newline at end of file
diff --git a/README.MD b/README.MD
index 6fc5a5ff..21ec40b3 100644
--- a/README.MD
+++ b/README.MD
@@ -85,3 +85,24 @@ x = np.array([["[C]", "[C]", "[C]"]])
 print(f"f({x}) = {f(x)}")
 
 ```
+
+## Where can I find the documentation?
+
+
+
+### Building the documentation locally
+
+If you install the `requirements-dev.txt` via
+
+```bash
+pip install -r requirements-dev.txt
+```
+
+then you will have access to `sphinx`. You should be able to build the documentation by going to the docs folder and building it:
+
+```bash
+cd docs/
+make html
+```
+
+Afterwards, you can enter the `build` folder and open `index.html`.
diff --git a/docs/Makefile b/docs/Makefile
new file mode 100644
index 00000000..d0c3cbf1
--- /dev/null
+++ b/docs/Makefile
@@ -0,0 +1,20 @@
+# Minimal makefile for Sphinx documentation
+#
+
+# You can set these variables from the command line, and also
+# from the environment for the first two.
+SPHINXOPTS    ?=
+SPHINXBUILD   ?= sphinx-build
+SOURCEDIR     = source
+BUILDDIR      = build
+
+# Put it first so that "make" without argument is like "make help".
+help:
+	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
+
+.PHONY: help Makefile
+
+# Catch-all target: route all unknown targets to Sphinx using the new
+# "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
+%: Makefile
+	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
diff --git a/docs/make.bat b/docs/make.bat
new file mode 100644
index 00000000..747ffb7b
--- /dev/null
+++ b/docs/make.bat
@@ -0,0 +1,35 @@
+@ECHO OFF
+
+pushd %~dp0
+
+REM Command file for Sphinx documentation
+
+if "%SPHINXBUILD%" == "" (
+	set SPHINXBUILD=sphinx-build
+)
+set SOURCEDIR=source
+set BUILDDIR=build
+
+%SPHINXBUILD% >NUL 2>NUL
+if errorlevel 9009 (
+	echo.
+	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx
+	echo.installed, then set the SPHINXBUILD environment variable to point
+	echo.to the full path of the 'sphinx-build' executable. Alternatively you
+	echo.may add the Sphinx directory to PATH.
+	echo.
+	echo.If you don't have Sphinx installed, grab it from
+	echo.https://www.sphinx-doc.org/
+	exit /b 1
+)
+
+if "%1" == "" goto help
+
+%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+goto end
+
+:help
+%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O%
+
+:end
+popd
diff --git a/docs/source/_templates/custom-class-template.rst b/docs/source/_templates/custom-class-template.rst
new file mode 100644
index 00000000..581e06b5
--- /dev/null
+++ b/docs/source/_templates/custom-class-template.rst
@@ -0,0 +1,32 @@
+{{ fullname | escape | underline}}
+
+.. currentmodule:: {{ module }}
+
+.. autoclass:: {{ objname }}
+    :members:
+    :show-inheritance:
+    :inherited-members:
+
+   {% block methods %}
+   .. automethod:: __init__
+
+   {% if methods %}
+   .. rubric:: {{ _('Methods') }}
+
+   .. autosummary::
+   {% for item in methods %}
+      ~{{ name }}.{{ item }}
+   {%- endfor %}
+   {% endif %}
+   {% endblock %}
+
+   {% block attributes %}
+   {% if attributes %}
+   .. rubric:: {{ _('Attributes') }}
+
+   .. autosummary::
+   {% for item in attributes %}
+      ~{{ name }}.{{ item }}
+   {%- endfor %}
+   {% endif %}
+   {% endblock %}
diff --git a/docs/source/_templates/custom-module-template.rst b/docs/source/_templates/custom-module-template.rst
new file mode 100644
index 00000000..32ae4918
--- /dev/null
+++ b/docs/source/_templates/custom-module-template.rst
@@ -0,0 +1,65 @@
+{{ fullname | escape | underline}}
+
+.. automodule:: {{ fullname }}
+
+   {% block attributes %}
+   {% if attributes %}
+   .. rubric:: {{ _('Module Attributes') }}
+
+   .. autosummary::
+    :toctree:
+   {% for item in attributes %}
+      {{ item }}
+   {%- endfor %}
+   {% endif %}
+   {% endblock %}
+
+   {% block functions %}
+   {% if functions %}
+   .. rubric:: {{ _('Functions') }}
+
+   .. autosummary::
+    :toctree:
+   {% for item in functions %}
+      {{ item }}
+   {%- endfor %}
+   {% endif %}
+   {% endblock %}
+
+   {% block classes %}
+   {% if classes %}
+   .. rubric:: {{ _('Classes') }}
+
+   .. autosummary::
+    :toctree:
+   {% for item in classes %}
+      {{ item }}
+   {%- endfor %}
+   {% endif %}
+   {% endblock %}
+
+   {% block exceptions %}
+   {% if exceptions %}
+   .. rubric:: {{ _('Exceptions') }}
+
+   .. autosummary::
+    :toctree:
+   {% for item in exceptions %}
+      {{ item }}
+   {%- endfor %}
+   {% endif %}
+   {% endblock %}
+
+{% block modules %}
+{% if modules %}
+.. rubric:: Modules
+
+.. autosummary::
+   :toctree:
+   :template: custom-module-template.rst
+   :recursive:
+{% for item in modules %}
+   {{ item }}
+{%- endfor %}
+{% endif %}
+{% endblock %}
diff --git a/docs/source/conf.py b/docs/source/conf.py
new file mode 100644
index 00000000..68ea60d3
--- /dev/null
+++ b/docs/source/conf.py
@@ -0,0 +1,94 @@
+# Configuration file for the Sphinx documentation builder.
+#
+# This file only contains a selection of the most common options. For a full
+# list see the documentation:
+# https://www.sphinx-doc.org/en/master/usage/configuration.html
+
+# -- Path setup --------------------------------------------------------------
+
+# If extensions (or modules to document with autodoc) are in another directory,
+# add these directories to sys.path here. If the directory is relative to the
+# documentation root, use os.path.abspath to make it absolute, like shown here.
+#
+import os
+
+# import sys
+# sys.path.insert(0, os.path.abspath('.'))
+import pathlib
+import sys
+
+path_to_poli = (pathlib.Path(__file__).parents[2].resolve() / "src").as_posix()
+sys.path.insert(0, path_to_poli)
+
+for x in os.walk(path_to_poli):
+    sys.path.insert(0, x[0])
+
+
+# -- Project information -----------------------------------------------------
+
+project = "poli"
+copyright = "2023, MLLS"
+author = "MLLS"
+
+# The full version, including alpha/beta/rc tags
+release = "0.0.1"
+
+
+# -- General configuration ---------------------------------------------------
+
+# Add any Sphinx extension module names here, as strings. They can be
+# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
+# ones.
+extensions = [
+    "sphinx.ext.autodoc",
+    "sphinx.ext.autosummary",
+    "sphinx.ext.napoleon",
+]
+autosummary_generate = True  # Turn on sphinx.ext.autosummary
+
+autodoc_mock_imports = [
+    "biopython",
+    "Bio",
+    "tdc",
+    "pdbtools",
+    "torch",
+    "pandas",
+    "rdkit",
+    "lambo",
+    "selfies",
+    "github",
+    "yaml",
+    "pytz",
+    "matplotlib",
+    "scipy",
+    "sklearn",
+    "tensorflow",
+    "hydra",
+    "ptitprince",
+    "seaborn",
+    "pdbfixer",
+    "openmm",
+    "simtk",
+    "dockstring",
+]
+
+# Add any paths that contain templates here, relative to this directory.
+templates_path = ["_templates"]
+
+# List of patterns, relative to source directory, that match files and
+# directories to ignore when looking for source files.
+# This pattern also affects html_static_path and html_extra_path.
+exclude_patterns = []
+
+
+# -- Options for HTML output -------------------------------------------------
+
+# The theme to use for HTML and HTML Help pages.  See the documentation for
+# a list of builtin themes.
+#
+html_theme = "furo"
+
+# Add any paths that contain custom static files (such as style sheets) here,
+# relative to this directory. They are copied after the builtin static files,
+# so a file named "default.css" will overwrite the builtin "default.css".
+html_static_path = ["_static"]
diff --git a/docs/source/index.rst b/docs/source/index.rst
new file mode 100644
index 00000000..eeacbbb5
--- /dev/null
+++ b/docs/source/index.rst
@@ -0,0 +1,27 @@
+.. poli documentation master file, created by
+   sphinx-quickstart on Thu Dec 21 10:41:44 2023.
+   You can adapt this file completely to your liking, but it should at least
+   contain the root `toctree` directive.
+
+Contents
+========
+
+.. currentmodule:: poli
+
+.. autosummary::
+   :toctree: generated
+   :template: custom-module-template.rst
+   :recursive:
+
+    core
+    objective_factory
+    objective_repository
+
+
+
+Indices and tables
+==================
+
+* :ref:`genindex`
+* :ref:`modindex`
+* :ref:`search`
diff --git a/requirements-dev.txt b/requirements-dev.txt
index fd775422..362a52a1 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -1,3 +1,5 @@
 black
 tox
-setuptools
\ No newline at end of file
+setuptools
+sphinx
+furo
\ No newline at end of file
diff --git a/src/poli/__init__.py b/src/poli/__init__.py
index 1c91d3a1..deca69cc 100644
--- a/src/poli/__init__.py
+++ b/src/poli/__init__.py
@@ -1,1 +1,2 @@
+"""poli, a library for black-box optimization of discrete sequences."""
 __author__ = "Simon Bartels"
diff --git a/src/poli/core/__init__.py b/src/poli/core/__init__.py
index e69de29b..4e8a885f 100644
--- a/src/poli/core/__init__.py
+++ b/src/poli/core/__init__.py
@@ -0,0 +1,2 @@
+"""Core classes and utilities inside poli
+"""
diff --git a/src/poli/core/abstract_black_box.py b/src/poli/core/abstract_black_box.py
index 3a8bbceb..915f7e60 100644
--- a/src/poli/core/abstract_black_box.py
+++ b/src/poli/core/abstract_black_box.py
@@ -46,6 +46,8 @@ class AbstractBlackBox:
     -------
     set_observer(observer)
         Set the observer object for recording observations during evaluation.
+    reset_evaluation_budget()
+        Reset the evaluation budget by setting the number of evaluations made to 0.
     __call__(x, context=None)
         Evaluate the black box function for the given input.
     _black_box(x, context=None)
@@ -111,6 +113,7 @@ def set_observer(self, observer: AbstractObserver):
         self.observer = observer
 
     def reset_evaluation_budget(self):
+        """Resets the evaluation budget by setting the number of evaluations made to 0."""
         self._num_evaluations = 0
 
     def __call__(self, x: np.array, context=None):
@@ -283,6 +286,16 @@ def __neg__(self):
 
 
 class NegativeBlackBox(AbstractBlackBox):
+    """A wrapper for a black box that negates the objective function.
+
+    If you construct a black-box function f for maximizing, then -f is
+    a black-box function for minimizing. This class is a wrapper for
+    implementing the latter.
+
+    The only difference is that the __call__ method returns -f(x) instead
+    of f(x). The _black_box method is the same as the original black box.
+    """
+
     def __init__(self, f: AbstractBlackBox):
         self.f = f
         super().__init__(info=f.info, batch_size=f.batch_size)
diff --git a/src/poli/core/abstract_problem_factory.py b/src/poli/core/abstract_problem_factory.py
index 6a21a7a2..6a7c3b1e 100644
--- a/src/poli/core/abstract_problem_factory.py
+++ b/src/poli/core/abstract_problem_factory.py
@@ -29,14 +29,10 @@ class AbstractProblemFactory(metaclass=MetaProblemFactory):
     """
     Abstract base class for problem factories.
 
-    This class defines the interface for creating problem instances in the POLI framework.
+    This class defines the interface for creating problem instances in poli.
 
-    Attributes:
-    -----------
-        None
-
-    Methods:
-    --------
+    Methods
+    -------
     get_setup_information:
         Returns the setup information for the problem.
     create:
@@ -47,12 +43,12 @@ def get_setup_information(self) -> ProblemSetupInformation:
         """
         Returns the setup information for the problem.
 
-        Returns:
+        Returns
         --------
         problem_info: ProblemSetupInformation
             The setup information for the problem.
 
-        Raises:
+        Raises
         -------
         NotImplementedError:
             This method is abstract and must be implemented by subclasses.
@@ -70,7 +66,7 @@ def create(
         """
         Returns a blackbox function and initial observations.
 
-        Parameters:
+        Parameters
         -----------
         seed:  int
             The seed for random number generation. Default is None.
@@ -83,13 +79,13 @@ def create(
         evaluation_budget:  int, optional
             The maximum number of function evaluations. Default is infinity.
 
-        Returns:
+        Returns
         --------
-            results: Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
-                A tuple containing the blackbox function, initial observations for
-                input variables, and initial observations for output variables.
+        results: Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
+            A tuple containing the blackbox function, initial observations for
+            input variables, and initial observations for output variables.
 
-        Raises:
+        Raises
         -------
             NotImplementedError: This method is abstract and must be implemented by subclasses.
 
diff --git a/src/poli/core/chemistry/__init__.py b/src/poli/core/chemistry/__init__.py
index e69de29b..b8c90d28 100644
--- a/src/poli/core/chemistry/__init__.py
+++ b/src/poli/core/chemistry/__init__.py
@@ -0,0 +1,1 @@
+"""Contains the implementations of core chemistry-related black boxes."""
diff --git a/src/poli/core/chemistry/tdc_black_box.py b/src/poli/core/chemistry/tdc_black_box.py
index a4da08d0..6a369872 100644
--- a/src/poli/core/chemistry/tdc_black_box.py
+++ b/src/poli/core/chemistry/tdc_black_box.py
@@ -1,11 +1,14 @@
-"""In this module, we implement a synthetic-accessibility 
-objective using the TDC oracles [1].
+"""Implement a wrapper around the Therapeutics Data Commons (TDC) oracles [1].
 
-[1] Huang, Kexin, Tianfan Fu, Wenhao Gao, Yue Zhao, Yusuf Roohani, Jure Leskovec, Connor W. Coley, Cao Xiao, Jimeng Sun, and Marinka Zitnik. “Artificial Intelligence Foundation for Therapeutic Science.” Nature Chemical Biology 18, no. 10 (October 2022): 1033-36. https://doi.org/10.1038/s41589-022-01131-2.
+So far, we only support two oracles: `drd3` and `synthetic accessibility`. See
+the documentation on our black-box functions for more details.
 
-See also
--------
-The website for TDC: https://tdcommons.ai/
+References
+----------
+[1] “Artificial Intelligence Foundation for Therapeutic Science.”
+    Huang, Kexin, Tianfan Fu, Wenhao Gao, Yue Zhao, Yusuf Roohani, Jure Leskovec,
+    Connor W. Coley, Cao Xiao, Jimeng Sun, and Marinka Zitnik.  Nature Chemical Biology 18, no. 10
+    (October 2022): 1033-36. https://doi.org/10.1038/s41589-022-01131-2.
 """
 
 import numpy as np
@@ -24,7 +27,7 @@ class TDCBlackBox(AbstractBlackBox):
     TDC (Therapeutics Data Commons) problems.
     It inherits from the AbstractBlackBox class.
 
-    Parameters:
+    Parameters
     -----------
     oracle_name : str
         The name of the oracle used for computing the docking score.
@@ -41,8 +44,8 @@ class TDCBlackBox(AbstractBlackBox):
     from_smiles : bool, optional
         Flag indicating whether the input molecules are in SMILES format. Defaults to True.
 
-    Attributes:
-    -----------
+    Attributes
+    ----------
     oracle : Oracle
         An instance of the Oracle class from TDC.
     from_smiles : bool
@@ -58,6 +61,7 @@ def __init__(
         num_workers: int = None,
         evaluation_budget: int = float("inf"),
         from_smiles: bool = True,
+        **kwargs_for_oracle,
     ):
         """
         Initialize the TDCBlackBox class.
@@ -76,28 +80,30 @@ def __init__(
             The number of workers to use for parallel computation, by default None.
         from_smiles : bool, optional
             Whether to use SMILES representation, by default True.
+        **kwargs_for_oracle : dict, optional
+            Additional keyword arguments for the oracle.
         """
         super().__init__(info, batch_size, parallelize, num_workers, evaluation_budget)
-        self.oracle = Oracle(name=oracle_name)
+        self.oracle = Oracle(name=oracle_name, **kwargs_for_oracle)
         self.from_smiles = from_smiles
 
     def _black_box(self, x, context=None):
         """
         Assuming x is an array of strings,
         we concatenate them and then
-        compute the docking score.
+        compute the oracle score.
 
-        Parameters:
+        Parameters
         -----------
         x : array-like
             An array of strings representing the input molecules.
         context : any, optional
             Additional context information. Defaults to None.
 
-        Returns:
+        Returns
         --------
         scores : array-like
-            An array of docking scores computed for each input molecule.
+            An array of oracle scores computed for each input molecule.
         """
         if not x.dtype.kind in ["U", "S"]:
             raise ValueError(
diff --git a/src/poli/core/multi_objective_black_box.py b/src/poli/core/multi_objective_black_box.py
index 2568a0e7..febd9d2f 100644
--- a/src/poli/core/multi_objective_black_box.py
+++ b/src/poli/core/multi_objective_black_box.py
@@ -17,7 +17,7 @@ class MultiObjectiveBlackBox(AbstractBlackBox):
     by concatenating the results of evaluating individual
     objective functions.
 
-    Parameters:
+    Parameters
     -----------
     info : ProblemSetupInformation
         The problem setup information.
@@ -26,18 +26,18 @@ class MultiObjectiveBlackBox(AbstractBlackBox):
     objective_functions : List[AbstractBlackBox], required
         The list of objective functions to be evaluated. Defaults to None.
 
-    Attributes:
-    -----------
+    Attributes
+    ----------
     objective_functions : List[AbstractBlackBox]
         The list of objective functions to be evaluated.
 
-    Methods:
-    --------
+    Methods
+    -------
     _black_box(x, context=None)
         Evaluate the black box function for the given input.
 
-    Raises:
-    -------
+    Raises
+    ------
     ValueError
         If objective_functions is not provided as a list of AbstractBlackBox
         instances or inherited classes.
@@ -51,22 +51,22 @@ class MultiObjectiveBlackBox(AbstractBlackBox):
     def __init__(
         self,
         info: ProblemSetupInformation,
+        objective_functions: List[AbstractBlackBox],
         batch_size: int = None,
-        objective_functions: List[AbstractBlackBox] = None,
     ) -> None:
         """
         Initialize the MultiObjectiveBlackBox class.
 
-        Parameters:
+        Parameters
         -----------
         info : ProblemSetupInformation
             The problem setup information.
+        objective_functions : List[AbstractBlackBox]
+            The list of objective functions.
         batch_size : int, optional
             The batch size. Defaults to None.
-        objective_functions : List[AbstractBlackBox], required
-            The list of objective functions. Defaults to None.
 
-        Raises:
+        Raises
         -------
         ValueError:
             If objective_functions is not provided as a list of AbstractBlackBox instances or inherited classes.
@@ -84,14 +84,14 @@ def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
         """
         Evaluate the black box function for a given input.
 
-        Parameters:
+        Parameters
         -----------
         x : np.ndarray
             The input values to evaluate the black box function.
         context : any, optional
             Additional context information for the evaluation.
 
-        Returns:
+        Returns
         --------
             array-like: The concatenated results of evaluating the objective functions on the input.
         """
diff --git a/src/poli/core/problem_setup_information.py b/src/poli/core/problem_setup_information.py
index 11fec29c..20031c80 100644
--- a/src/poli/core/problem_setup_information.py
+++ b/src/poli/core/problem_setup_information.py
@@ -1,6 +1,7 @@
-from typing import List, Dict, Union
-
-from pathlib import Path
+"""
+Implements the problem setup information, which contains the problem information (e.g. alphabet, sequence length...).
+"""
+from typing import List
 
 
 class ProblemSetupInformation:
@@ -36,27 +37,72 @@ def __init__(
         self.log_transform_recommended = log_transform_recommended
 
     def get_problem_name(self) -> str:
+        """Returns the problem's name.
+
+        Returns
+        --------
+        name : str
+            The problem's name.
+        """
         return self.name
 
     def get_max_sequence_length(self) -> int:
+        """
+        Returns the maximum sequence length allowed by the black-box.
+
+        Returns
+        --------
+        max_sequence_length : int
+            The length of the longest sequence.
+        """
         return self.max_sequence_length
 
     def sequences_are_aligned(self) -> bool:
+        """
+        Returns whether the sequences need to be aligned.
+
+        Returns
+        --------
+        aligned : bool
+            Whether the sequences need to be aligned.
+        """
         return self.aligned
 
     def get_alphabet(self) -> list:
+        """
+        Returns the alphabet of allowed characters.
+
+        Returns
+        --------
+        alphabet : list[str]
+            List of tokens allowed by the black-box.
+        """
         return self.alphabet
 
     def log_transform_recommended(self) -> bool:
+        """
+        Returns whether the black-box recommends log-transforming the targets.
+
+        Returns
+        --------
+        log_transform_recommended : bool
+            Whether the black-box recommends log-transforming the targets.
+        """
         return self.log_transform_recommended
 
     def as_dict(self) -> dict:
         """Returns all attributes as a dictionary.
 
-        Returns:
+        Returns
         --------
-        dict
+        info : dict
             A dictionary of all attributes.
+
+        Notes
+        -----
+        - It's vital that this method is _not_ called __dict__, since
+          that breaks the serialization of the ProblemSetupInformation
+          when using pickle. (see https://stackoverflow.com/a/75777082/3516175)
         """
         return {
             "name": self.name,
diff --git a/src/poli/core/proteins/__init__.py b/src/poli/core/proteins/__init__.py
index e69de29b..de52f12d 100644
--- a/src/poli/core/proteins/__init__.py
+++ b/src/poli/core/proteins/__init__.py
@@ -0,0 +1,1 @@
+"""Contains the implementations of core protein-related black boxes."""
diff --git a/src/poli/core/proteins/foldx_black_box.py b/src/poli/core/proteins/foldx_black_box.py
index d293ab71..b3d304bf 100644
--- a/src/poli/core/proteins/foldx_black_box.py
+++ b/src/poli/core/proteins/foldx_black_box.py
@@ -3,9 +3,12 @@
 FoldX [1] is a simulator that allows for computing the difference
 in free energy between a wildtype protein and a mutated protein.
 
-[1] Schymkowitz, J., Borg, J., Stricher, F., Nys, R., Rousseau, F.,
-    & Serrano, L. (2005). The FoldX web server: an online force field.
-    Nucleic acids research, 33(suppl_2), W382-W388.
+References
+----------
+[1] The FoldX web server: an online force field.
+    Nucleic acids research Schymkowitz, J., Borg, J., Stricher,
+    F., Nys, R., Rousseau, F., & Serrano, L. (2005). ,
+    33(suppl_2), W382-W388.
 """
 
 from typing import Union, List
@@ -35,7 +38,7 @@ class FoldxBlackBox(AbstractBlackBox):
     """
     A class representing the FoldxBlackBox, which is used for simulating protein mutations using FoldX.
 
-    Parameters:
+    Parameters
     -----------
     info : ProblemSetupInformation, required
         The problem setup information object. (default: None)
@@ -56,8 +59,8 @@ class FoldxBlackBox(AbstractBlackBox):
     eager_repair : bool, optional
         Flag indicating whether to eagerly repair the PDB files. (default: False)
 
-    Attributes:
-    -----------
+    Attributes
+    ----------
     experiment_id : str
         The experiment ID.
     tmp_folder : Path
@@ -71,8 +74,8 @@ class FoldxBlackBox(AbstractBlackBox):
     wildtype_residue_strings : List[str]
         The list of wildtype residue strings for each PDB file.
 
-    Methods:
-    --------
+    Methods
+    -------
     create_working_directory() -> Path:
         Creates and returns the working directory path for the black box.
 
@@ -94,7 +97,7 @@ def __init__(
         """
         Initialize the FoldxBlackBox.
 
-        Parameters:
+        Parameters
         -----------
         info : ProblemSetupInformation, optional
             The problem setup information object. (default: None)
@@ -202,7 +205,7 @@ def create_working_directory(self) -> Path:
         """
         Create and return the working directory path for the black box.
 
-        Returns:
+        Returns
         --------
         Path
             The path to the working directory.
diff --git a/src/poli/core/registry.py b/src/poli/core/registry.py
index 033a8d8b..94caa529 100644
--- a/src/poli/core/registry.py
+++ b/src/poli/core/registry.py
@@ -184,9 +184,9 @@ def register_problem_from_repository(name: str):
     corresponds to a folder inside the objective_repository folder. The
     function will:
     1. create the environment from the yaml file
-    2. run the file from said enviroment (since
-       we can't import the factory: it may have
-       dependencies that are not installed)
+    2. run the file from said enviroment (since we can't
+    import the factory: it may have dependencies that are
+    not installed)
 
     Parameters
     ----------
diff --git a/src/poli/core/util/__init__.py b/src/poli/core/util/__init__.py
index e69de29b..729908b1 100644
--- a/src/poli/core/util/__init__.py
+++ b/src/poli/core/util/__init__.py
@@ -0,0 +1,1 @@
+"""Utilities and core observers for poli"""
diff --git a/src/poli/core/util/abstract_observer.py b/src/poli/core/util/abstract_observer.py
index 0d8d48e1..54443b88 100644
--- a/src/poli/core/util/abstract_observer.py
+++ b/src/poli/core/util/abstract_observer.py
@@ -1,3 +1,4 @@
+"""Abstract class for observers in poli."""
 import numpy as np
 from poli.core.problem_setup_information import ProblemSetupInformation
 
@@ -13,12 +14,8 @@ class AbstractObserver:
     by observing the values of the objective function and the decision variables
     at each iteration.
 
-    Attributes:
-    ----------
-        None
-
-    Methods:
-    --------
+    Methods
+    -------
         observe(x: np.ndarray, y: np.ndarray, context=None) -> None:
             Observes the values of the objective function and the decision variables
             at each iteration of the optimization algorithm. If the observer is
@@ -39,7 +36,7 @@ def observe(self, x: np.ndarray, y: np.ndarray, context=None) -> None:
         """
         Observe the given data points.
 
-        Parameters:
+        Parameters
         ----------
         x: np.ndarray
             The input data points.
@@ -48,7 +45,7 @@ def observe(self, x: np.ndarray, y: np.ndarray, context=None) -> None:
         context: object
             Additional context for the observation.
 
-        Raises:
+        Raises
         -------
         NotImplementedError:
             This method is meant to be overridden by subclasses.
diff --git a/src/poli/core/util/batch/__init__.py b/src/poli/core/util/batch/__init__.py
index 1be9f0c6..cd53ef86 100644
--- a/src/poli/core/util/batch/__init__.py
+++ b/src/poli/core/util/batch/__init__.py
@@ -1,1 +1,2 @@
+"""Utility functions for batching data."""
 from .batch_input import batched
diff --git a/src/poli/core/util/batch/batch_input.py b/src/poli/core/util/batch/batch_input.py
index 4919bc28..cf7c8866 100644
--- a/src/poli/core/util/batch/batch_input.py
+++ b/src/poli/core/util/batch/batch_input.py
@@ -7,6 +7,10 @@ def batched(iterable: Iterable, chunk_size: int):
     """
     Generate batches of elements from an iterable.
 
+    If working on Python 3.12+, this function can be replaced
+    with the built-in function `batched` from `itertools`.
+    https://docs.python.org/3/library/itertools.html#itertools.batched
+
     Parameters
     ----------
     iterable : Iterable
diff --git a/src/poli/core/util/chemistry/__init__.py b/src/poli/core/util/chemistry/__init__.py
index e69de29b..24ccfc1e 100644
--- a/src/poli/core/util/chemistry/__init__.py
+++ b/src/poli/core/util/chemistry/__init__.py
@@ -0,0 +1,1 @@
+"""Utilities for the chemistry-related black-boxes"""
diff --git a/src/poli/core/util/chemistry/string_to_molecule.py b/src/poli/core/util/chemistry/string_to_molecule.py
index fe28ad17..37eb68b9 100644
--- a/src/poli/core/util/chemistry/string_to_molecule.py
+++ b/src/poli/core/util/chemistry/string_to_molecule.py
@@ -11,12 +11,17 @@
 def translate_smiles_to_selfies(
     smiles_strings: List[str], strict: bool = False
 ) -> List[str]:
-    """
+    """Translates a list of SMILES strings to SELFIES strings.
+
     Given a list of SMILES strings, returns the translation
     into SELFIES strings. If strict is True, it raises an error
     if a SMILES string in the list cannot be parsed. Else, it
     returns None for those.
 
+    This function uses the `selfies` package from Aspuru-Guzik's
+    lab. See https://github.com/aspuru-guzik-group/selfies
+
+
     Parameters
     ----------
     smiles_strings : List[str]
@@ -45,11 +50,27 @@ def translate_smiles_to_selfies(
 def translate_selfies_to_smiles(
     selfies_strings: List[str], strict: bool = False
 ) -> List[str]:
-    """
+    """Translates a list of SELFIES strings to SMILES strings.
+
     Given a list of SELFIES strings, returns the translation
     into SMILES strings. If strict is True, it raises an error
     if a SELFIES string in the list cannot be parsed. Else, it
     returns None for those.
+
+    This function uses the `selfies` package from Aspuru-Guzik's
+    lab. See https://github.com/aspuru-guzik-group/selfies
+
+    Parameters
+    ----------
+    selfies_strings : List[str]
+        A list of SELFIES strings.
+    strict : bool, optional
+        If True, raise an error if a SELFIES string in the list cannot be parsed.
+
+    Returns
+    -------
+    smiles_strings : List[str]
+        A list of SMILES strings.
     """
     smiles_strings = []
     for selfies in selfies_strings:
@@ -65,8 +86,9 @@ def translate_selfies_to_smiles(
 
 
 def smiles_to_molecules(smiles_strings: List[str], strict: bool = False) -> Chem.Mol:
-    """
-    Convert a SMILES string to an RDKit molecule. If strict is True,
+    """Converts a list of SMILES strings to RDKit molecules.
+
+    Converts a list of SMILES strings to RDKit molecules. If strict is True,
     it raises an error if a SMILES string in the list cannot be parsed.
 
     Parameters
@@ -84,7 +106,7 @@ def smiles_to_molecules(smiles_strings: List[str], strict: bool = False) -> Chem
         molecule = Chem.MolFromSmiles(smiles_string)
 
         if molecule is None and strict:
-            raise ValueError(f"Failed to parse molecule.")
+            raise ValueError(f"Failed to parse molecule {smiles_string}.")
 
         molecules.append(molecule)
 
@@ -92,8 +114,7 @@ def smiles_to_molecules(smiles_strings: List[str], strict: bool = False) -> Chem
 
 
 def selfies_to_molecules(selfies_strings: List[str]) -> Chem.Mol:
-    """
-    Convert a selfies string to an RDKit molecule.
+    """Converts a list of selfies strings to RDKit molecules.
 
     Parameters
     ----------
diff --git a/src/poli/core/util/external_observer.py b/src/poli/core/util/external_observer.py
index 471ac8e6..e86e87f2 100644
--- a/src/poli/core/util/external_observer.py
+++ b/src/poli/core/util/external_observer.py
@@ -1,3 +1,4 @@
+"""External observer, which can be run in an isolated process."""
 from typing import Any
 import numpy as np
 
@@ -51,7 +52,7 @@ def observe(self, x: np.ndarray, y: np.ndarray, context=None) -> None:
         """
         Observe the given data points.
 
-        Parameters:
+        Parameters
         ----------
         x: np.ndarray
             The input data points.
@@ -60,7 +61,7 @@ def observe(self, x: np.ndarray, y: np.ndarray, context=None) -> None:
         context: object
             Additional context for the observation.
 
-        Raises:
+        Raises
         -------
         Exception:
             If the underlying observer process raises an exception.
@@ -144,7 +145,8 @@ def finish(self) -> None:
             self.process_wrapper = None
 
     def __getattr__(self, __name: str) -> Any:
-        """
+        """Get an attribute of the underlying observer if it exists.
+
         Asks for the attribute of the underlying
         black-box function by sending a message
         to the process w. the msg_type "ATTRIBUTE".
diff --git a/src/poli/core/util/files/__init__.py b/src/poli/core/util/files/__init__.py
index e81463fa..9e2a7638 100644
--- a/src/poli/core/util/files/__init__.py
+++ b/src/poli/core/util/files/__init__.py
@@ -1,1 +1,2 @@
+"""Utilities for downloading files from GitHub repositories."""
 from .download_files_from_github import download_file_from_github_repository
diff --git a/src/poli/core/util/files/download_files_from_github.py b/src/poli/core/util/files/download_files_from_github.py
index 6fe2c073..963584a5 100644
--- a/src/poli/core/util/files/download_files_from_github.py
+++ b/src/poli/core/util/files/download_files_from_github.py
@@ -1,13 +1,14 @@
-"""
-Taken and adapted from:
+"""Utilities for downloading files from GitHub repositories.
+
+This module requires the PyGithub package, install it with:
+    
+        pip install PyGithub
+
+Most of this code was taken and adapted from:
 https://gist.github.com/pdashford/2e4bcd4fc2343e2fd03efe4da17f577d?permalink_comment_id=4274705#gistcomment-4274705
 """
 import base64
-import getopt
 import os
-import shutil
-import sys
-from typing import Optional
 from pathlib import Path
 
 from github import Github, GithubException
@@ -95,8 +96,8 @@ def download_file_from_github_repository(
 
     This function will use an environment variable called
     GITHUB_TOKEN_FOR_POLI if it exists. If it does not exist,
-    it will try to download without it. Note that, for anonymous
-    requests, the rate limit is 60 requests per hour.
+    it will try to download without it. Note that the rate limit
+    is 60 requests per hour for anonymous requests.
     """
     github = Github(login_or_token=os.environ.get("GITHUB_TOKEN_FOR_POLI"))
     repository = github.get_repo(repository_name)
diff --git a/src/poli/core/util/files/integrity.py b/src/poli/core/util/files/integrity.py
index 13308a46..ed335ddb 100644
--- a/src/poli/core/util/files/integrity.py
+++ b/src/poli/core/util/files/integrity.py
@@ -1,4 +1,5 @@
-"""
+"""Utilities for verifying the integrity of files downloaded from the internet.
+
 This module contains utilities for checking the integrity of
 files downloaded from the internet. This is useful for
 reproducibility purposes, and to make sure no malicious
@@ -14,16 +15,16 @@ def compute_md5_from_filepath(filepath: Path, read_mode: str = "rb") -> str:
     Computes the MD5 hex digest from a filepath,
     opening it in binary form by default.
 
-    Parameters:
+    Parameters
     ----------
     filepath : Path
         The path to the file for which the MD5 digest will be computed.
     read_mode : str, optional
         The mode in which the file will be opened. Default is 'rb' (read in binary mode).
 
-    Returns:
+    Returns
     -------
-    str
+    hex_digest : str
         The MD5 hex digest of the file.
 
     Examples:
diff --git a/src/poli/core/util/inter_process_communication/__init__.py b/src/poli/core/util/inter_process_communication/__init__.py
index 1c91d3a1..fe84d06a 100644
--- a/src/poli/core/util/inter_process_communication/__init__.py
+++ b/src/poli/core/util/inter_process_communication/__init__.py
@@ -1,1 +1,2 @@
+"""Module that wraps utility functions for interprocess communication."""
 __author__ = "Simon Bartels"
diff --git a/src/poli/core/util/inter_process_communication/process_wrapper.py b/src/poli/core/util/inter_process_communication/process_wrapper.py
index cc9ae3ff..ce61cf75 100644
--- a/src/poli/core/util/inter_process_communication/process_wrapper.py
+++ b/src/poli/core/util/inter_process_communication/process_wrapper.py
@@ -94,6 +94,8 @@ def __init__(self, run_script, **kwargs_for_factory):
         # TODO: This is a very silly way to handle communication between processes,
         # and it is also very dangerous, because the user can pass arbitrary arguments
         # to the shell script. We should instead use a proper IPC library.
+        # TODO: if we decide to pass this information in the set-up phase (instead
+        # of here), we can remove this.
         string_for_kwargs = ""
         for key, value in kwargs_for_factory.items():
             if isinstance(value, str):
diff --git a/src/poli/core/util/objective_management/__init__.py b/src/poli/core/util/objective_management/__init__.py
index 1c91d3a1..7f0994ca 100644
--- a/src/poli/core/util/objective_management/__init__.py
+++ b/src/poli/core/util/objective_management/__init__.py
@@ -1,1 +1,2 @@
+"""Utilities for creating run scripts for problems and observers."""
 __author__ = "Simon Bartels"
diff --git a/src/poli/core/util/objective_management/make_run_script.py b/src/poli/core/util/objective_management/make_run_script.py
index e6fb16f9..ee02fbda 100644
--- a/src/poli/core/util/objective_management/make_run_script.py
+++ b/src/poli/core/util/objective_management/make_run_script.py
@@ -109,7 +109,7 @@ def _make_run_script(
     cwd : str, optional
         The current working directory for the run script. If not provided, the current working directory is used.
     **kwargs : dict
-        Additional keyword arguments.
+        Additional keyword arguments (currently unused).
 
     Returns
     -------
diff --git a/src/poli/core/util/observer_wrapper.py b/src/poli/core/util/observer_wrapper.py
index 24812641..004ab51e 100644
--- a/src/poli/core/util/observer_wrapper.py
+++ b/src/poli/core/util/observer_wrapper.py
@@ -1,4 +1,4 @@
-"""This script gets called by the mother process to start an observer process.
+"""Script that gets called by the mother process to start an external observer process.
 """
 
 import sys
@@ -12,7 +12,7 @@
 
 def start_observer_process(observer_name, port: int, password: str):
     """
-    Start the observer process.
+    Starts the observer process.
 
     Parameters
     ----------
diff --git a/src/poli/core/util/proteins/__init__.py b/src/poli/core/util/proteins/__init__.py
index e69de29b..0c617e16 100644
--- a/src/poli/core/util/proteins/__init__.py
+++ b/src/poli/core/util/proteins/__init__.py
@@ -0,0 +1,1 @@
+"""Utilities for the protein-related black-boxes"""
diff --git a/src/poli/core/util/proteins/defaults.py b/src/poli/core/util/proteins/defaults.py
index 29000a17..b3d7afd2 100644
--- a/src/poli/core/util/proteins/defaults.py
+++ b/src/poli/core/util/proteins/defaults.py
@@ -1,5 +1,32 @@
 """This module contains the default values for the amino acid encoding.
 
+For amino acid encoding, we use the following:
+
+```python
+AMINO_ACIDS = [
+    "A",
+    "R",
+    "N",
+    "D",
+    "C",
+    "E",
+    "Q",
+    "G",
+    "H",
+    "I",
+    "L",
+    "K",
+    "M",
+    "F",
+    "P",
+    "S",
+    "T",
+    "W",
+    "Y",
+    "V",
+]
+```
+
 """
 
 AMINO_ACIDS = [
diff --git a/src/poli/core/util/proteins/foldx.py b/src/poli/core/util/proteins/foldx.py
index b1469017..ae438130 100644
--- a/src/poli/core/util/proteins/foldx.py
+++ b/src/poli/core/util/proteins/foldx.py
@@ -1,11 +1,18 @@
-"""
-This module contains utilities for querying
-foldx for repairing and simulating the mutations
-of proteins.
+"""FoldX interface for measuring stability and SASA.
+
+This module implements a `FoldxInterface` for querying
+foldx's repairing and simulating models [1].
 
 If this module is imported from a script, it will
 automatically check that the foldx files are in the
 expected location, and raise an error if they are not.
+
+References
+----------
+[1] The FoldX web server: an online force field.
+    Schymkowitz, J., Borg, J., Stricher, F., Nys, R.,
+    Rousseau, F., & Serrano, L. (2005).  Nucleic acids research,
+    33(suppl_2), W382-W388.
 """
 from typing import List, Union
 from pathlib import Path
@@ -119,7 +126,7 @@ def repair(
         Repairs a PDB file with FoldX, overwriting
         the original file if remove_and_rename is True (default: False).
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Union[str, Path]
             The path to the PDB file to be repaired.
@@ -132,7 +139,7 @@ def repair(
             If True, heteroatoms will be removed from the repaired PDB file using pdbtools.
             Default is True.
 
-        Raises:
+        Raises
         ------
         RuntimeError
             If FoldX fails to repair the PDB file.
@@ -204,12 +211,12 @@ def _repair_if_necessary_and_provide_path(self, pdb_file: Path) -> Path:
         then we repair it and return the path of the repaired
         pdb. Otherwise, we return the same path as the input.
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Path
             The path to the PDB file to be repaired.
 
-        Returns:
+        Returns
         -------
         repaired_path: Path
             The path to the repaired PDB file.
@@ -245,7 +252,7 @@ def _simulate_mutations(self, pdb_file: Path, mutations: List[str] = None) -> No
         e.g. ["MA0A"] means that the first residue in the chain
         is mutated from M to A.
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Path
             The path to the PDB file to be repaired.
@@ -253,7 +260,7 @@ def _simulate_mutations(self, pdb_file: Path, mutations: List[str] = None) -> No
             The list of mutations to simulate. If None, we simulate
             the wildtype. Default is None.
 
-        Raises:
+        Raises
         ------
         AssertionError
             If the number of mutations is not 0 or 1.
@@ -332,17 +339,17 @@ def _read_energy(self, pdb_file: Path) -> float:
         assuming that there was a single mutation.
         TODO: add support for multiple mutations.
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Path
             The path to the PDB file to be repaired.
 
-        Returns:
+        Returns
         -------
         energy: float
             The change of energy (ddG) of the mutated structure.
 
-        Raises:
+        Raises
         ------
         AssertionError
             If the results file was not generated.
@@ -361,12 +368,12 @@ def _compute_sasa(self, pdb_file: Path) -> float:
         This method computes the SASA from a FoldX results file,
         assuming that there was a single mutation.
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Path
             The path to the PDB file to be repaired.
 
-        Returns:
+        Returns
         -------
         sasa_score: float
             The SASA score of the mutated structure.
@@ -387,20 +394,20 @@ def compute_stability(self, pdb_file: Path, mutations: List[str] = None) -> floa
         """
         Compute the stability of a protein structure using FoldX.
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Path
             The path to the PDB file of the protein structure.
         mutations : List[str], optional
             A list of mutations to be simulated. Only single mutations are supported. Pass no mutations to compute the energy of the wildtype.
 
-        Returns:
+        Returns
         -------
         float
             The stability of the protein structure (defined as the negative
             change in energy).
 
-        Raises:
+        Raises
         ------
         AssertionError
             If the number of mutations is not 0 or 1.
@@ -422,7 +429,7 @@ def compute_sasa(self, pdb_file: Path, mutations: List[str] = None) -> float:
         """
         Compute the solvent-accessible surface area (SASA) score for a given protein structure.
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Path
             The path to the PDB file of the protein structure.
@@ -430,12 +437,12 @@ def compute_sasa(self, pdb_file: Path, mutations: List[str] = None) -> float:
             A list of mutations to be simulated on the protein structure. Only single mutations are supported.
             Pass no mutations if you want to compute the SASA of the wildtype.
 
-        Returns:
+        Returns
         -------
         float
             The computed SASA score.
 
-        Raises:
+        Raises
         ------
         AssertionError
             If the number of mutations is not 0 or 1.
@@ -455,7 +462,7 @@ def compute_stability_and_sasa(self, pdb_file: Path, mutations: List[str] = None
         """Computes stability and sasa with a single foldx run,
         instead of two separate runs.
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Path
             The path to the PDB file of the protein structure.
@@ -478,7 +485,7 @@ def compute_stability_and_sasa(self, pdb_file: Path, mutations: List[str] = None
     def copy_foldx_files(self, pdb_file: Path):
         """Copies the rotabase and pdb file to the working directory.
 
-        Parameters:
+        Parameters
         ----------
         pdb_file : Path
             The path to the PDB file of the protein structure.
@@ -499,7 +506,7 @@ def write_mutations_to_file(
         """Writes the list of mutations to a file
         in the given directory.
 
-        Parameters:
+        Parameters
         ----------
         wildtype_resiudes : List[Residue]
             The list of wildtype residues.
diff --git a/src/poli/core/util/proteins/mutations.py b/src/poli/core/util/proteins/mutations.py
index 221de1a2..ffb4918e 100644
--- a/src/poli/core/util/proteins/mutations.py
+++ b/src/poli/core/util/proteins/mutations.py
@@ -1,4 +1,5 @@
-"""
+"""Utilities for defining and manipulating mutations on proteins.
+
 This module contains utilities for defining
 mutations on proteins according to foldx.
 
@@ -7,6 +8,9 @@
     - the second letter is the position of the mutation
     - the third letter is the chain ID,
     - the fourth letter is the mutant residue.
+
+See the "Individual List Mode" of https://foldxsuite.crg.eu/parameter/mutant-file
+for more details.
 """
 from pathlib import Path
 from typing import List, Tuple, Union
@@ -19,9 +23,6 @@
 from poli.core.util.proteins.pdb_parsing import parse_pdb_as_residue_strings
 
 
-from typing import List, Tuple
-
-
 def edits_between_strings(
     string_1: str, string_2: str, strict: bool = True
 ) -> List[Tuple[str, int, int]]:
@@ -163,7 +164,7 @@ def find_closest_wildtype_pdb_file_to_mutant(
     """
     Find the closest wildtype PDB file to a given mutant residue string.
 
-    Parameters:
+    Parameters
     ----------
     wildtype_pdb_files : List[Path]
         A list of paths to wildtype PDB files.
@@ -173,13 +174,13 @@ def find_closest_wildtype_pdb_file_to_mutant(
         If True, return the hamming distance along with the best candidate PDB file.
         Default is False.
 
-    Returns:
+    Returns
     -------
     Union[Path, Tuple[Path, int]]
         If return_hamming_distance is True, returns a tuple containing the best candidate PDB file
         and the hamming distance. Otherwise, returns the best candidate PDB file.
 
-    Raises:
+    Raises
     ------
     ValueError
         If no PDB file of the same length as the mutated residue string is found.
diff --git a/src/poli/core/util/proteins/pdb_parsing.py b/src/poli/core/util/proteins/pdb_parsing.py
index 47607f92..4a6de9a0 100644
--- a/src/poli/core/util/proteins/pdb_parsing.py
+++ b/src/poli/core/util/proteins/pdb_parsing.py
@@ -2,7 +2,7 @@
 """
 
 from pathlib import Path
-from typing import Tuple, List
+from typing import List
 
 from Bio import PDB
 from Bio.PDB.Residue import Residue
@@ -18,7 +18,7 @@ def parse_pdb_as_structure(
     The parsing is done quietly by default,
     but you can set verbose=True to get some output.
 
-    Parameters:
+    Parameters
     -----------
     path_to_pdb : Path
         The path to the PDB file.
@@ -28,7 +28,7 @@ def parse_pdb_as_structure(
     verbose : bool, optional
         If True, print the progress of the parsing. Defaults to False.
 
-    Returns:
+    Returns
     --------
     PDB.Structure.Structure
         The parsed structure.
@@ -43,7 +43,7 @@ def parse_pdb_as_residues(
     """
     Parse a PDB file and return a list of Residue objects.
 
-    Parameters:
+    Parameters
     -----------
     path_to_pdb: Path
         The path to the PDB file.
@@ -52,7 +52,7 @@ def parse_pdb_as_residues(
     verbose: bool, optional
         Whether to print verbose output. Defaults to False.
 
-    Returns:
+    Returns
     --------
         residues: List[Residue]
             A list of Residue objects representing the parsed PDB file.
@@ -67,7 +67,7 @@ def parse_pdb_as_residue_strings(
     """
     Parse a PDB file and return a list of residue strings.
 
-    Parameters:
+    Parameters
     ----------
     path_to_pdb : Path
         The path to the PDB file.
@@ -76,7 +76,7 @@ def parse_pdb_as_residue_strings(
     verbose : bool, optional
         Whether to print verbose output, by default False.
 
-    Returns:
+    Returns
     -------
     List[str]
         A list of residue strings.
diff --git a/src/poli/core/util/proteins/rasp/__init__.py b/src/poli/core/util/proteins/rasp/__init__.py
index 4a8cf841..cab267d8 100644
--- a/src/poli/core/util/proteins/rasp/__init__.py
+++ b/src/poli/core/util/proteins/rasp/__init__.py
@@ -1,2 +1,3 @@
+"""Utilities for interacting with the original RaSP codebase."""
 from .rasp_interface import RaspInterface
 from .load_models import load_cavity_and_downstream_models
diff --git a/src/poli/core/util/proteins/rasp/inner_rasp/visualization.py b/src/poli/core/util/proteins/rasp/inner_rasp/visualization.py
index 9c9471dc..a4e8a756 100644
--- a/src/poli/core/util/proteins/rasp/inner_rasp/visualization.py
+++ b/src/poli/core/util/proteins/rasp/inner_rasp/visualization.py
@@ -7,9 +7,9 @@
 from scipy import stats
 from sklearn.metrics import mean_absolute_error
 
-plt.rcParams["figure.dpi"] = 300
-plt.rcParams["figure.figsize"] = [8.0, 8.0]
-plt.rcParams.update({"font.size": 14})
+# plt.rcParams["figure.dpi"] = 300
+# plt.rcParams["figure.figsize"] = [8.0, 8.0]
+# plt.rcParams.update({"font.size": 14})
 import glob
 import math
 import pickle
diff --git a/src/poli/core/util/proteins/rasp/load_models.py b/src/poli/core/util/proteins/rasp/load_models.py
index a65fb03d..f413251d 100644
--- a/src/poli/core/util/proteins/rasp/load_models.py
+++ b/src/poli/core/util/proteins/rasp/load_models.py
@@ -1,4 +1,6 @@
+"""Utilities for loading up the cavity and downstream models for RaSP."""
 from pathlib import Path
+from typing import Tuple
 
 import torch
 
@@ -15,10 +17,27 @@
 RASP_DIR.mkdir(parents=True, exist_ok=True)
 
 
-def load_cavity_and_downstream_models(DEVICE: str = "cpu"):
+def load_cavity_and_downstream_models(
+    DEVICE: str = "cpu",
+) -> Tuple[CavityModel, DownstreamModel]:
+    """Load the cavity and downstream models for RaSP.
+
+    Parameters
+    -----------
+    DEVICE : str, optional
+        The device to load the models on. Defaults to "cpu".
+
+    Returns
+    --------
+    cavity_model_net : CavityModel
+        The cavity model.
+    ds_model_net : DownstreamModel
+        The downstream model.
+    """
     # DEVICE = "cpu"
 
-    # TODO: Ask why this was implemented this way.
+    # TODO: Ask why this was implemented this way (i.e.
+    # loading from a "best_cavity_model_path").
     # A transparent alternative would be to simply
     # load the model from the path itself.
     best_cavity_model_path = RASP_DIR / "cavity_model_15.pt"
@@ -32,5 +51,3 @@ def load_cavity_and_downstream_models(DEVICE: str = "cpu"):
     ds_model_net.eval()
 
     return cavity_model_net, ds_model_net
-
-    ...
diff --git a/src/poli/core/util/proteins/rasp/rasp_interface.py b/src/poli/core/util/proteins/rasp/rasp_interface.py
index 46fd28fc..4de7cd55 100644
--- a/src/poli/core/util/proteins/rasp/rasp_interface.py
+++ b/src/poli/core/util/proteins/rasp/rasp_interface.py
@@ -1,4 +1,5 @@
-"""
+"""An interface to the original RaSP codebase.
+
 This module takes and adapts RaSP's original implementation
 (which can be found at [1]), and writes an interface that 
 handles the preprocessing and inference steps.
@@ -19,12 +20,21 @@
 and tools will be cached in the ~/.poli_objectives/rasp directory,
 so you will only need internet access once.
 
-[1] TODO: add.
-[2] TODO: add.
-
 RaSP's source code was provided with an Apache 2.0 license. Modifications
 from the source have been duly noted. Most of the source code can be found
 at ./inner_rasp.
+
+References
+----------
+[1] “Rapid Protein Stability Prediction Using Deep Learning Representations.”
+    Blaabjerg, Lasse M, Maher M Kassem, Lydia L Good, Nicolas Jonsson, Matteo Cagiada,
+    Kristoffer E Johansson, Wouter Boomsma, Amelie Stein, and Kresten Lindorff-Larsen.
+    Edited by José D Faraldo-Gómez, Detlef Weigel, Nir Ben-Tal, and Julian Echave.
+    eLife 12 (May 2023): e82593. https://doi.org/10.7554/eLife.82593.
+[2] The FoldX web server: an online force field.
+    Schymkowitz, J., Borg, J., Stricher, F., Nys, R.,
+    Rousseau, F., & Serrano, L. (2005).  Nucleic acids research,
+    33(suppl_2), W382-W388.
 """
 
 from typing import List
diff --git a/src/poli/core/util/seeding/__init__.py b/src/poli/core/util/seeding/__init__.py
index 36d226f2..cfde5b66 100644
--- a/src/poli/core/util/seeding/__init__.py
+++ b/src/poli/core/util/seeding/__init__.py
@@ -1,1 +1,2 @@
-from .seeding import seed_numpy, seed_python
+"""Utilities for seeding random number generators."""
+from .seeding import seed_numpy, seed_python, seed_torch, seed_python_numpy_and_torch
diff --git a/src/poli/core/util/seeding/seeding.py b/src/poli/core/util/seeding/seeding.py
index 403daff5..2a9432d7 100644
--- a/src/poli/core/util/seeding/seeding.py
+++ b/src/poli/core/util/seeding/seeding.py
@@ -1,3 +1,4 @@
+"""Utilities for seeding random number generators."""
 import numpy as np
 import random
 
@@ -6,16 +7,13 @@ def seed_numpy(seed: int = None) -> None:
     """
     Seed the NumPy random number generator.
 
-    Parameters:
+    Parameters
     ----------
     seed : int, optional
-        Seed value for the random number generator. If None, the generator is initialized with a random seed.
-
-    Examples:
-    ---------
-    >>> seed_numpy(123)
+        Seed value for the random number generator. If None, then no seeding is performed.
     """
-    np.random.seed(seed)
+    if seed is not None:
+        np.random.seed(seed)
 
 
 def seed_python(seed: int = None) -> None:
@@ -23,9 +21,46 @@ def seed_python(seed: int = None) -> None:
     Seed the random number generator for Python.
 
 
-    Parameters:
+    Parameters
+    ----------
+    seed : int, optional
+        Seed value for the random number generator. If None, then no seeding is performed.
+    """
+    if seed is not None:
+        random.seed(seed)
+
+
+def seed_torch(seed: int = None) -> None:
+    """
+    Seed the random number generator for PyTorch.
+
+
+    Parameters
+    ----------
+    seed : int, optional
+        Seed value for the random number generator. If None, no seeding is performed.
+    """
+    try:
+        import torch
+    except ImportError:
+        return
+
+    if seed is not None:
+        torch.manual_seed(seed)
+        if torch.cuda.is_available():
+            torch.cuda.manual_seed_all(seed)
+
+
+def seed_python_numpy_and_torch(seed: int = None) -> None:
+    """
+    Seed all random number generators.
+
+
+    Parameters
     ----------
     seed : int, optional
-        Seed value for the random number generator. If None, the generator is initialized with a random seed.
+        Seed value for the random number generator. If None, no seeding is performed.
     """
-    random.seed(seed)
+    seed_numpy(seed)
+    seed_python(seed)
+    seed_torch(seed)
diff --git a/src/poli/objective.py b/src/poli/objective.py
index ba4a4d52..06187637 100644
--- a/src/poli/objective.py
+++ b/src/poli/objective.py
@@ -1,3 +1,4 @@
+"""Executable script used for isolation of objective factories and functions."""
 import logging
 import os
 import sys
@@ -12,6 +13,19 @@
 
 
 def parse_factory_kwargs(factory_kwargs: str) -> dict:
+    """Parses the factory kwargs passed to the objective function.
+
+    Parameters
+    ----------
+    factory_kwargs : str
+        The string containing the factory kwargs (see ProcessWrapper
+        for details about how this factory_kwargs strings is built).
+
+    Returns
+    -------
+    kwargs : dict
+        A dictionary containing the factory kwargs, parsed from the string.
+    """
     if factory_kwargs == "":
         # Then the user didn't pass any arguments
         kwargs = {}
@@ -45,6 +59,19 @@ def parse_factory_kwargs(factory_kwargs: str) -> dict:
 
 
 def dynamically_instantiate(obj: str, **kwargs):
+    """Dynamically instantiates an object from a string.
+
+    This function is used internally to instantiate objective
+    factories dynamically, inside isolated processes. It is
+    also used to instantiate external observers.
+
+    Parameters
+    ----------
+    obj : str
+        The string containing the name of the object to be instantiated.
+    **kwargs : dict
+        The keyword arguments to be passed to the object constructor.
+    """
     # FIXME: this method opens up a serious security vulnerability
     # TODO: possible alternative: importlib
     # TODO: another possible alternative: hydra
@@ -76,10 +103,19 @@ def dynamically_instantiate(obj: str, **kwargs):
 
 
 def run(factory_kwargs: str, objective_name: str, port: int, password: str) -> None:
-    """
-    Starts an objective function listener loop to wait for requests.
-    :param objective_name:
-        problem factory name including python packages, e.g. package.subpackage.MyFactoryName
+    """Starts an objective function listener loop to wait for requests.
+
+    Parameters
+    ----------
+    factory_kwargs : str
+        The string containing the factory kwargs (see ProcessWrapper
+        for details about how this factory_kwargs strings is built).
+    objective_name : str
+        The name of the objective function to be instantiated.
+    port : int
+        The port number for the connection with the mother process.
+    password : str
+        The password for the connection with the mother process.
     """
     kwargs = parse_factory_kwargs(factory_kwargs)
 
@@ -129,9 +165,6 @@ def run(factory_kwargs: str, objective_name: str, port: int, password: str) -> N
 
 
 if __name__ == "__main__":
-    # TODO: modify this to allow for passing more
-    # information to run. Said information can be interpreted
-    # as being used for instantiating the problem factory.
     parser = argparse.ArgumentParser()
     parser.add_argument("--objective-name", required=True)
     parser.add_argument("--port", required=True, type=int)
diff --git a/src/poli/objective_factory.py b/src/poli/objective_factory.py
index 198f0946..b073e6eb 100644
--- a/src/poli/objective_factory.py
+++ b/src/poli/objective_factory.py
@@ -1,5 +1,5 @@
 """
-This is the main file relevant for users who want to run objective functions.
+Creates objective functions by providing a common interface to all factories in the repository.
 """
 from typing import Tuple, Any
 import numpy as np
@@ -22,20 +22,62 @@
 
 
 def load_config():
+    """Loads the configuration file containing which objectives are registered.
+
+    Returns
+    -------
+    config : configparser.ConfigParser
+        The configuration file.
+
+    """
     HOME_DIR = Path.home().resolve()
     config_file = str(HOME_DIR / ".poli_objectives" / "config.rc")
     config = configparser.ConfigParser(defaults={_OBSERVER: ""})
-    ls = config.read(config_file)
+    _ = config.read(config_file)
 
     return config
 
 
 class ExternalBlackBox(AbstractBlackBox):
-    def __init__(self, info: ProblemSetupInformation, process_wrapper):
+    """An external version of the black-box function to be instantiated in isolated processes."""
+
+    def __init__(self, info: ProblemSetupInformation, process_wrapper: ProcessWrapper):
+        """
+        Initialize the ExternalBlackBox object.
+
+        Parameters
+        ----------
+        info : ProblemSetupInformation
+            The information about the problem.
+        process_wrapper : ProcessWrapper
+            The process wrapper to communicate with the objective process.
+        """
         super().__init__(info)
         self.process_wrapper = process_wrapper
 
     def _black_box(self, x, context=None):
+        """
+        Evaluates the black-box function.
+
+        In this external black-box, the evaluation is done by sending a message
+        to the objective process and waiting for the response. The interprocess
+        communication is handled by the ProcessWrapper class, which maintains
+        an isolated process in which a black-box function runs with, potentially,
+        a completely different python executable (e.g. inside another conda
+        environment).
+
+        Parameters
+        ----------
+        x : np.ndarray
+            The input data points.
+        context : object
+            Additional context for the observation.
+
+        Returns
+        -------
+        y : np.ndarray
+            The output data points.
+        """
         self.process_wrapper.send(["QUERY", x, context])
         msg_type, *val = self.process_wrapper.recv()
         if msg_type == "EXCEPTION":
@@ -52,6 +94,7 @@ def _black_box(self, x, context=None):
             )
 
     def terminate(self):
+        """Terminates the external black box."""
         # terminate objective process
         if self.process_wrapper is not None:
             try:
@@ -70,10 +113,21 @@ def terminate(self):
                 pass
 
     def __getattr__(self, __name: str) -> Any:
-        """
+        """Gets an attribute from the underlying black-box function.
+
         Asks for the attribute of the underlying
         black-box function by sending a message
         to the process w. the msg_type "ATTRIBUTE".
+
+        Parameters
+        ----------
+        __name : str
+            The name of the attribute.
+
+        Returns
+        -------
+        attribute : Any
+            The attribute of the underlying black-box function.
         """
         self.process_wrapper.send(["ATTRIBUTE", __name])
         msg_type, *msg = self.process_wrapper.recv()
@@ -103,10 +157,32 @@ def __create_from_repository(
     observer: AbstractObserver = None,
     **kwargs_for_factory,
 ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
-    """
+    """Creates the objective function from the repository.
+
     If the problem is available in AVAILABLE_PROBLEM_FACTORIES
     (i.e. if the user could import all the dependencies directly),
     we create the problem directly. Otherwise, we raise an error.
+
+    Parameters
+    ----------
+    name : str
+        The name of the objective function.
+    seed : int, optional
+        The seed value for random number generation.
+    batch_size : int, optional
+        The batch size, passed to the black box to run evaluations on batches.
+        If None, it will evaluate all inputs at once.
+    parallelize : bool, optional
+        If True, then the objective function runs in parallel.
+    num_workers : int, optional
+        When parallelize is True, this specifies the number of processes to use.
+        By default, it uses half the number of available CPUs (rounded down).
+    evaluation_budget : int, optional
+        The maximum number of evaluations allowed. By default, it is infinity.
+    observer : AbstractObserver, optional
+        The observer to use.
+    **kwargs_for_factory : dict, optional
+        Additional keyword arguments for the factory.
     """
     if name not in AVAILABLE_PROBLEM_FACTORIES:
         raise ValueError(
@@ -138,9 +214,30 @@ def __create_as_isolated_process(
     evaluation_budget: int = float("inf"),
     **kwargs_for_factory,
 ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
-    """
+    """Creates the objective function as an isolated process.
+
     If the problem is registered, we create it as an isolated
-    process. Otherwise, we raise an error.
+    process. Otherwise, we raise an error. That is, this function
+    expects the problem to be registered.
+
+    Parameters
+    ----------
+    name : str
+        The name of the objective function.
+    seed : int, optional
+        The seed value for random number generation.
+    batch_size : int, optional
+        The batch size, passed to the black box to run evaluations on batches.
+        If None, it will evaluate all inputs at once.
+    parallelize : bool, optional
+        If True, then the objective function runs in parallel.
+    num_workers : int, optional
+        When parallelize is True, this specifies the number of processes to use.
+        By default, it uses half the number of available CPUs (rounded down).
+    evaluation_budget : int, optional
+        The maximum number of evaluations allowed. By default, it is infinity.
+    **kwargs_for_factory : dict, optional
+        Additional keyword arguments for the factory.
     """
     config = load_config()
     if name not in config:
@@ -181,6 +278,21 @@ def __create_as_isolated_process(
 
 
 def __register_objective_if_available(name: str, force_register: bool = False):
+    """Registers the objective function if it is available in the repository.
+
+    If the objective function is not available in the repository,
+    then we raise an error. If it is available, then we ask the
+    user for confirmation to register it. If the user confirms,
+    then we register it. Otherwise, we raise an error.
+
+    Parameters
+    ----------
+    name : str
+        The name of the objective function.
+    force_register : bool, optional
+        If True, then the objective function is registered without asking
+        for confirmation, overwriting any previous registration.
+    """
     config = load_config()
     if name not in config:
         if name not in AVAILABLE_OBJECTIVES:
@@ -229,34 +341,49 @@ def create(
     **kwargs_for_factory,
 ) -> Tuple[ProblemSetupInformation, AbstractBlackBox, np.ndarray, np.ndarray, object]:
     """
-    Instantiantes a black-box function.
-    :param name:
-        The name of the objective function or a shell-script for execution.
-    :param seed:
-        Information for the objective in case randomization is involved.
-    :param caller_info:
+    Instantiantes a black-box function by calling the `create` method of the associated factory.
+
+    Parameters
+    ----------
+    name : str
+        The name of the objective function.
+    seed : int, optional
+        The seed value for random number generation.
+    caller_info : dict, optional
         Optional information about the caller that is forwarded to the logger to initialize the run.
-    :param observer:
-        Optional observer, external observer by default.
-    :param force_register:
+    observer : AbstractObserver, optional
+        The observer to use.
+    force_register : bool, optional
         If True, then the objective function is registered without asking
         for confirmation, overwriting any previous registration.
-    :param force_isolation:
+    force_isolation : bool, optional
         If True, then the objective function is instantiated as an isolated
         process.
-    :param batch_size:
+    batch_size : int, optional
         The batch size, passed to the black box to run evaluations on batches.
         If None, it will evaluate all inputs at once.
-    :param parallelize:
+    parallelize : bool, optional
         If True, then the objective function runs in parallel.
-    :param num_workers:
+    num_workers : int, optional
         When parallelize is True, this specifies the number of processes to use.
-    :return:
-        problem_information: a ProblemSetupInformation object holding basic properties about the problem
-        f: an objective function that accepts a numpy array and returns a numpy array
-        x0: initial inputs
-        y0: f(x0)
-        observer_info: information from the observer_info about the instantiated run (allows the calling algorithm to connect)
+        By default, it uses half the number of available CPUs (rounded down).
+    evaluation_budget : int, optional
+        The maximum number of evaluations allowed. By default, it is infinity.
+    **kwargs_for_factory : dict, optional
+        Additional keyword arguments for the factory.
+
+    Returns
+    -------
+    problem_information : ProblemSetupInformation
+        The information about the problem.
+    f : AbstractBlackBox
+        The black-box function.
+    x0 : np.ndarray
+        The initial x values.
+    y0 : np.ndarray
+        The initial y values.
+    observer_info : object
+        The information about the observer.
     """
     # If the user can run it with the envionment they currently
     # have, then we do not need to install it.
@@ -319,9 +446,10 @@ def start(
     force_isolation: bool = False,
     **kwargs_for_factory,
 ) -> AbstractBlackBox:
-    """
-    Works just like create, but it does not run the function on anything, and returns
-    only the black-box function.
+    """Starts the black-box function.
+
+    Works just like create, but it returns only the black-box function
+    and resets the evaluation budget.
 
     One example of running this function:
 
@@ -336,18 +464,36 @@ def start(
 
     If the black-box function is created as an isolated process, then
     it will automatically close when the context manager is closed.
+
+    Parameters
+    ----------
+    name : str
+        The name of the objective function.
+    seed : int, optional
+        The seed value for random number generation.
+    caller_info : dict, optional
+        Optional information about the caller that is forwarded to the logger to initialize the run.
+    observer : AbstractObserver, optional
+        The observer to use.
+    force_register : bool, optional
+        If True, then the objective function is registered without asking
+        for confirmation, overwriting any previous registration.
+    force_isolation : bool, optional
+        If True, then the objective function is instantiated as an isolated
+        process.
+    **kwargs_for_factory : dict, optional
+        Additional keyword arguments for the factory.
     """
     # Check if we can import the function immediately
     if name in AVAILABLE_PROBLEM_FACTORIES and not force_isolation:
         f, _, _ = __create_from_repository(name, seed=seed, **kwargs_for_factory)
-        return f
-
-    # Check if the name is indeed registered, or
-    # available in the objective repository
-    __register_objective_if_available(name, force_register=force_register)
+    else:
+        # Check if the name is indeed registered, or
+        # available in the objective repository
+        __register_objective_if_available(name, force_register=force_register)
 
-    # If not, then we create it as an isolated process
-    f, _, _ = __create_as_isolated_process(name, seed=seed, **kwargs_for_factory)
+        # If not, then we create it as an isolated process
+        f, _, _ = __create_as_isolated_process(name, seed=seed, **kwargs_for_factory)
 
     # instantiate observer (if desired)
     if observer is not None:
@@ -355,4 +501,7 @@ def start(
 
     f.set_observer(observer)
 
+    # Reset the counter of evaluations
+    f.reset_evaluation_budget()
+
     return f
diff --git a/src/poli/objective_repository/__init__.py b/src/poli/objective_repository/__init__.py
index 1504de84..c98015dc 100644
--- a/src/poli/objective_repository/__init__.py
+++ b/src/poli/objective_repository/__init__.py
@@ -1,3 +1,5 @@
+"""All objective factories and black boxes inside poli.
+"""
 from pathlib import Path
 
 # These can be imported from the base environment.
diff --git a/src/poli/objective_repository/aloha/__init__.py b/src/poli/objective_repository/aloha/__init__.py
index e69de29b..d3491b0d 100644
--- a/src/poli/objective_repository/aloha/__init__.py
+++ b/src/poli/objective_repository/aloha/__init__.py
@@ -0,0 +1,1 @@
+"""The ALOHA objective factory and function."""
diff --git a/src/poli/objective_repository/aloha/register.py b/src/poli/objective_repository/aloha/register.py
index 6f24654b..aeb96f7e 100644
--- a/src/poli/objective_repository/aloha/register.py
+++ b/src/poli/objective_repository/aloha/register.py
@@ -1,4 +1,5 @@
-"""
+"""Aloha objective factory and function.
+
 This is a registration script for the ALOHA problem,
 a simple example of a discrete black box objective
 function where the goal is to find the sequence
@@ -17,7 +18,7 @@
 from poli.core.abstract_problem_factory import AbstractProblemFactory
 from poli.core.problem_setup_information import ProblemSetupInformation
 
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_python_numpy_and_torch
 
 
 class AlohaBlackBox(AbstractBlackBox):
@@ -92,7 +93,7 @@ def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
         """
         Compute the distance of x to the sequence "ALOHA".
 
-        Parameters:
+        Parameters
         ----------
         x : np.ndarray
             Input array of shape [b, 5] containing strings or integers.
@@ -100,7 +101,7 @@ def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
         context : dict, optional
             Additional context information (default is None).
 
-        Returns:
+        Returns
         -------
         y: np.ndarray
             Array of shape [b, 1] containing the distances to the sequence "ALOHA".
@@ -141,7 +142,7 @@ def get_setup_information(self) -> ProblemSetupInformation:
         """
         Returns the setup information for the problem.
 
-        Returns:
+        Returns
         --------
         problem_info: ProblemSetupInformation
             The setup information for the problem.
@@ -167,7 +168,7 @@ def create(
         """
         Returns an Aloha blackbox function and initial observations.
 
-        Parameters:
+        Parameters
         -----------
         seed:  int, optional
             The seed for random number generation. Default is None.
@@ -180,7 +181,7 @@ def create(
         evaluation_budget:  int, optional
             The maximum number of function evaluations. Default is infinity.
 
-        Returns:
+        Returns
         --------
         results: Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
             A tuple containing the blackbox function, initial observations
@@ -189,8 +190,7 @@ def create(
         """
         # We set the seed for numpy and python
         if seed is not None:
-            seed_numpy(seed)
-            seed_python(seed)
+            seed_python_numpy_and_torch(seed)
 
         problem_info = self.get_setup_information()
         f = AlohaBlackBox(
diff --git a/src/poli/objective_repository/dockstring/__init__.py b/src/poli/objective_repository/dockstring/__init__.py
index e69de29b..53a4085f 100644
--- a/src/poli/objective_repository/dockstring/__init__.py
+++ b/src/poli/objective_repository/dockstring/__init__.py
@@ -0,0 +1,1 @@
+"""Dockstring objective factory and function."""
diff --git a/src/poli/objective_repository/dockstring/register.py b/src/poli/objective_repository/dockstring/register.py
index d3fc3785..ec7205a8 100644
--- a/src/poli/objective_repository/dockstring/register.py
+++ b/src/poli/objective_repository/dockstring/register.py
@@ -1,15 +1,18 @@
-"""
-This module implements and registers a black box
-objective function (and a repository) for dockstring [1].
+"""Dockstring objective factory and function.
+
+This module implements and registers a black box objective function
+(and a repository) for dockstring [1], which is a simple API for
+assessing the docking score of a small molecule to a given protein.
 
-[1] García-Ortegón, Miguel, Gregor N. C. Simm, Austin J. Tripp,
+References
+----------
+[1] “DOCKSTRING: Easy Molecular Docking Yields Better Benchmarks for Ligand Design.”
+    García-Ortegón, Miguel, Gregor N. C. Simm, Austin J. Tripp,
     José Miguel Hernández-Lobato, Andreas Bender, and Sergio Bacallado.
-    “DOCKSTRING: Easy Molecular Docking Yields Better Benchmarks for Ligand Design.”
     Journal of Chemical Information and Modeling 62, no. 15 (August 8, 2022): 3486-3502.
     https://doi.org/10.1021/acs.jcim.1c01334.
-
 """
-from typing import Tuple
+from typing import Tuple, Literal
 
 import numpy as np
 
@@ -26,7 +29,7 @@
     translate_smiles_to_selfies,
 )
 
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_python_numpy_and_torch
 
 
 class DockstringBlackBox(AbstractBlackBox):
@@ -66,9 +69,9 @@ class DockstringBlackBox(AbstractBlackBox):
 
     References
     ----------
-    [1] García-Ortegón, Miguel, Gregor N. C. Simm, Austin J. Tripp,
+    [1] “DOCKSTRING: Easy Molecular Docking Yields Better Benchmarks for Ligand Design.”
+        García-Ortegón, Miguel, Gregor N. C. Simm, Austin J. Tripp,
         José Miguel Hernández-Lobato, Andreas Bender, and Sergio Bacallado.
-        “DOCKSTRING: Easy Molecular Docking Yields Better Benchmarks for Ligand Design.”
         Journal of Chemical Information and Modeling 62, no. 15 (August 8, 2022): 3486-3502.
         https://doi.org/10.1021/acs.jcim.1c01334.
     """
@@ -81,7 +84,7 @@ def __init__(
         num_workers: int = None,
         evaluation_budget: int = float("inf"),
         target_name: str = None,
-        string_representation: str = "SMILES",
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
     ):
         """
         Initialize the dockstring black box object.
@@ -153,7 +156,8 @@ def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
         else:
             molecules_as_smiles = molecules_as_strings
 
-        # TODO: Should we parallelize?
+        # Parallelization is handled by the __call__ method of
+        # the AbstractBlackBox class.
         scores = []
         for smiles in molecules_as_smiles:
             try:
@@ -169,6 +173,27 @@ def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
 
 
 class DockstringProblemFactory(AbstractProblemFactory):
+    """Problem factory for the Dockstring problem.
+
+    Dockstring is a simple API for assessing the docking score
+    of a small molecule to a given protein [1].
+
+    Methods
+    -------
+    get_setup_information()
+        Returns the setup information for the problem.
+    create(...)
+        Creates a problem instance with the specified parameters.
+
+    References
+    ----------
+    [1] “DOCKSTRING: Easy Molecular Docking Yields Better Benchmarks for Ligand Design.”
+        García-Ortegón, Miguel, Gregor N. C. Simm, Austin J. Tripp,
+        José Miguel Hernández-Lobato, Andreas Bender, and Sergio Bacallado.
+        Journal of Chemical Information and Modeling 62, no. 15 (August 8, 2022): 3486-3502.
+        https://doi.org/10.1021/acs.jcim.1c01334.
+    """
+
     def get_setup_information(self) -> ProblemSetupInformation:
         # TODO: We might change this in the future for a
         # default dictionary, depending on whether we
@@ -190,14 +215,41 @@ def create(
         num_workers: int = None,
         evaluation_budget: int = float("inf"),
         target_name: str = None,
-        string_representation: str = "SMILES",
-    ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+    ) -> Tuple[DockstringBlackBox, np.ndarray, np.ndarray]:
+        """Creates a dockstring black box function and initial observations.
+
+        Parameters
+        ----------
+        seed : int, optional
+            The seed value for random number generation, by default None.
+        batch_size : int, optional
+            The batch size for processing data, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize the processing, by default False.
+        num_workers : int, optional
+            The number of workers to use for parallel processing, by default we
+            use half the number of available CPUs (rounded down).
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        target_name : str
+            The name of the target protein (see dockstring for more details).
+        string_representation : str
+            The string representation of the molecules. Either SMILES or SELFIES.
+            Default is SMILES.
+
+        Returns
+        -------
+        results: Tuple[DockstringBlackBox, np.ndarray, np.ndarray]:
+            A tuple containing the blackbox function, initial inputs,
+            and their respective outputs.
+        """
         assert (
             target_name is not None
         ), "Missing required keyword argument 'target_name'. "
 
-        seed_numpy(seed)
-        seed_python(seed)
+        if seed is not None:
+            seed_python_numpy_and_torch(seed)
 
         dockstring_black_box = DockstringBlackBox(
             info=self.get_setup_information(),
diff --git a/src/poli/objective_repository/drd3_docking/__init__.py b/src/poli/objective_repository/drd3_docking/__init__.py
index e69de29b..6534bf2e 100644
--- a/src/poli/objective_repository/drd3_docking/__init__.py
+++ b/src/poli/objective_repository/drd3_docking/__init__.py
@@ -0,0 +1,63 @@
+"""A wrapper around TDC's DRD3 docking objective function.
+
+Prerequisites
+-------------
+
+There are several requirements for running this objective function. We expect you to
+
+- have AutoDock Vina installed in the path,
+- have the `prepare_receptor` binary from the ADFR suite installed and in the path,
+- have the `poli__lambo` environment created.
+
+The rest of this description shows you how to do all this:
+
+Installing AutoDock Vina
+========================
+
+1. Download AutoDock Vina from the Center for Computational Structural Biology's website (https://vina.scripps.edu/downloads/). Uncompress them.
+
+2. Add this to the path by including `export PATH=path/to/AutoDock_vina/bin:$PATH` in your `~/.bashrc` or `~/.zshrc`.
+
+```bash
+# In your ~/.bashrc or ~/.zshrc
+export PATH=path/to/AutoDock_vina/bin:$PATH
+```
+
+Installing the ADFR suite
+=========================
+
+1. Download the installable files (https://ccsb.scripps.edu/adfr/downloads/). It's likely that you will have to run the `./install.sh` script inside the folder, and thus you might have to change its permissions for execution using `chmod +x`
+2. After running `./install.sh`, you should be able to find `.../bin/prepare_receptor`.
+3.  For the docking to run, `pyscreener` needs access to the `prepare_receptor` binary. However, adding all of the ADFR `bin` folder is sometimes problematic, since it has a version of Python inside. Thus, we recommend creating a symlink. Write this in your `~/.bashrc` or `~/.zshrc`:
+
+```bash
+# In your ~/.bashrc or ~/.zshrc
+ln -sf /path/to/ADFR/bin/prepare_receptor /path/to/AutoDock_vina/bin
+```
+
+Create the `poli__lambo` environment
+====================================
+
+This can easily be done by running
+
+.. code-block:: bash
+    
+        # From the base of the poli repo
+        conda env create --file src/poli/objective_repository/ddr3_docking/environment.yml
+
+
+We also need `lambo`'s tasks to be available in Python's path for `poli__lambo`:
+
+.. code-block:: bash
+    
+            git clone https://github.com/samuelstanton/lambo    # For reference, we use 431b052
+            cd lambo
+            pip install -e .  
+
+
+In particular, we need
+- `lambo.tasks.proxy_rfp.proxy_rfp.ProxyRFPTask`
+- the rfp data: see `~/lambo/assets/fpbase`
+
+And now you should be set! Running `register.py` and `query_example.py` should work.
+"""
diff --git a/src/poli/objective_repository/drd3_docking/query_example.py b/src/poli/objective_repository/drd3_docking/query_example.py
deleted file mode 100644
index 2a03a72f..00000000
--- a/src/poli/objective_repository/drd3_docking/query_example.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import numpy as np
-
-from poli import objective_factory
-
-# Using create
-_, f, x0, y0, _ = objective_factory.create(name="drd3_docking", force_register=True)
-print(x0)
-print(y0)
-f.terminate()
-
-# Using start
-x0 = np.array([["C"]])
-with objective_factory.start(name="drd3_docking") as f:
-    print(x0)
-    print(f(x0))
diff --git a/src/poli/objective_repository/drd3_docking/register.py b/src/poli/objective_repository/drd3_docking/register.py
index 86c94892..cb6775cf 100644
--- a/src/poli/objective_repository/drd3_docking/register.py
+++ b/src/poli/objective_repository/drd3_docking/register.py
@@ -1,10 +1,12 @@
 """
-This module implements the DDR3 docking task
-using the TDC oracles [1].
+Implements the DDR3 docking task using the TDC oracles [1].
 
-[1] Huang, K., Fu, T., Gao, W. et al. Artificial intelligence foundation for therapeutic science. Nat Chem Biol 18, 1033-1036 (2022). https://doi.org/10.1038/s41589-022-01131-2
+References
+----------
+[1] Artificial intelligence foundation for therapeutic science.
+    Huang, K., Fu, T., Gao, W. et al.  Nat Chem Biol 18, 1033-1036 (2022). https://doi.org/10.1038/s41589-022-01131-2
 """
-from typing import Tuple
+from typing import Tuple, Literal
 
 import numpy as np
 
@@ -28,7 +30,7 @@ class DRD3BlackBox(TDCBlackBox):
     info : ProblemSetupInformation
         The problem setup information.
     batch_size : int, optional
-        The batch size for parallel execution, by default None.
+        The batch size for simultaneous execution, by default None.
     parallelize : bool, optional
         Flag indicating whether to parallelize execution, by default False.
     num_workers : int, optional
@@ -76,12 +78,8 @@ class DRD3ProblemFactory(AbstractProblemFactory):
 
     This class provides methods for creating DRD3 docking problems and retrieving setup information.
 
-    Attributes:
-    ----------
-        None
-
-    Methods:
-    -------
+    Methods
+    ------
     get_setup_information:
         Retrieves the setup information for the problem.
     create:
@@ -92,7 +90,7 @@ def get_setup_information(self) -> ProblemSetupInformation:
         """
         Retrieves the setup information for the problem.
 
-        Returns:
+        Returns
         --------
         problem_info: ProblemSetupInformation
             The setup information for the problem.
@@ -111,12 +109,12 @@ def create(
         parallelize: bool = False,
         num_workers: int = None,
         evaluation_budget: int = float("inf"),
-        string_representation: str = "SMILES",
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
     ) -> Tuple[TDCBlackBox, np.ndarray, np.ndarray]:
         """
         Create a TDCBlackBox object for DRD3 docking.
 
-        Parameters:
+        Parameters
         ----------
         seed : int, optional
             Seed for random number generators. If None, no seed is set.
@@ -131,12 +129,12 @@ def create(
         string_representation : str, optional
             The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
 
-        Returns:
+        Returns
         -------
         Tuple[TDCBlackBox, np.ndarray, np.ndarray]
             A tuple containing the TDCBlackBox object, the initial input array, and the output array.
 
-        Raises:
+        Raises
         ------
         ValueError
             If the string_representation is not 'SMILES' or 'SELFIES'.
diff --git a/src/poli/objective_repository/foldx_rfp_lambo/__init__.py b/src/poli/objective_repository/foldx_rfp_lambo/__init__.py
index b4583191..fb897c1e 100644
--- a/src/poli/objective_repository/foldx_rfp_lambo/__init__.py
+++ b/src/poli/objective_repository/foldx_rfp_lambo/__init__.py
@@ -1,3 +1,13 @@
+"""
+A wrapper around the RFP task of LamBO [1].
+
+References
+----------
+[1] “Accelerating Bayesian Optimization for Biological Sequence Design with Denoising Autoencoders.”
+Stanton, Samuel, Wesley Maddox, Nate Gruver, Phillip Maffettone,
+Emily Delaney, Peyton Greenside, and Andrew Gordon Wilson.
+arXiv, July 12, 2022. http://arxiv.org/abs/2203.12742.
+"""
 # AUTHOR: Simon Bartels
 # This sequence is problematic due to an X in position 159
 # according to PDB this sequence has the most (98%) similarity: SKGEELIKENMHMKLYMEGTVNNHHFKCTTEGEGKPYEGTQTQRIKVVEGGPLPFAFDILATCFMYGSKTFINHTQGIPDFFKQSFPEGFTWERVTTYEDGGVLTVTQDTSLQDGCLIYNVKLRGVNFPSNGPVMQKKTLGWEATTETLYPADGGLEGRCDMALKLVGGGHLHCNLKTTYRSKKPAKNLKMPGVYFVDRRLERIKEADNETYVEQHEVAVARYCDLPSKL
diff --git a/src/poli/objective_repository/foldx_rfp_lambo/register.py b/src/poli/objective_repository/foldx_rfp_lambo/register.py
index cd10e1b8..522ccf88 100644
--- a/src/poli/objective_repository/foldx_rfp_lambo/register.py
+++ b/src/poli/objective_repository/foldx_rfp_lambo/register.py
@@ -1,3 +1,4 @@
+"""RFP objective factory and black box function."""
 __author__ = "Simon Bartels"
 
 import logging
diff --git a/src/poli/objective_repository/foldx_sasa/__init__.py b/src/poli/objective_repository/foldx_sasa/__init__.py
index e69de29b..e7f82c4c 100644
--- a/src/poli/objective_repository/foldx_sasa/__init__.py
+++ b/src/poli/objective_repository/foldx_sasa/__init__.py
@@ -0,0 +1,1 @@
+"""A black box function for solvent accessible surface area (SASA) prediction."""
diff --git a/src/poli/objective_repository/foldx_sasa/foldx_utils.py b/src/poli/objective_repository/foldx_sasa/foldx_utils.py
deleted file mode 100644
index bc49680e..00000000
--- a/src/poli/objective_repository/foldx_sasa/foldx_utils.py
+++ /dev/null
@@ -1,26 +0,0 @@
-AMINO_ACIDS = [
-    "A",
-    "R",
-    "N",
-    "D",
-    "C",
-    "E",
-    "Q",
-    "G",
-    "H",
-    "I",
-    "L",
-    "K",
-    "M",
-    "F",
-    "P",
-    "S",
-    "T",
-    "W",
-    "Y",
-    "V",
-]
-MAX_SEQUENCE_LENGTH = 100
-
-ENCODING = {amino_acid: i for i, amino_acid in enumerate(AMINO_ACIDS)}
-INVERSE_ENCODING = {i: amino_acid for amino_acid, i in ENCODING.items()}
diff --git a/src/poli/objective_repository/foldx_sasa/register.py b/src/poli/objective_repository/foldx_sasa/register.py
index d1073473..8ae1183b 100644
--- a/src/poli/objective_repository/foldx_sasa/register.py
+++ b/src/poli/objective_repository/foldx_sasa/register.py
@@ -1,4 +1,4 @@
-"""This script registers the SASA FoldX black box and objective factory.
+"""Registers the SASA FoldX black box and objective factory.
 
 FoldX [1] is a simulator that allows for computing the difference
 in free energy between a wildtype protein and a mutated protein.
@@ -42,7 +42,7 @@ class FoldXSASABlackBox(FoldxBlackBox):
     """
     A black box implementation for computing the solvent accessible surface area (SASA) score using FoldX.
 
-    Parameters:
+    Parameters
     -----------
     info : ProblemSetupInformation, optional
         The problem setup information. Default is None.
@@ -93,19 +93,20 @@ def __init__(
         )
 
     def _black_box(self, x: np.ndarray, context: None) -> np.ndarray:
-        """
+        """Computes the SASA score for a given mutation x.
+
         Runs the given input x and pdb files provided
         in the context through FoldX and returns the
         total SASA score.
 
-        Parameters:
+        Parameters
         -----------
         x : np.ndarray
             The input array representing the mutations.
         context : None
             The context for the black box computation.
 
-        Returns:
+        Returns
         --------
         np.ndarray
             The computed SASA score(s) as a numpy array.
@@ -148,8 +149,8 @@ class FoldXSASAProblemFactory(AbstractProblemFactory):
     """
     Factory class for creating FoldX SASA (Solvent Accessible Surface Area) problems.
 
-    Methods:
-    --------
+    Methods
+    -------
     get_setup_information:
         Returns the setup information for the problem.
     create:
@@ -198,7 +199,7 @@ def create(
         """
         Create a FoldXSASABlackBox object and compute the initial values of wildtypes.
 
-        Parameters:
+        Parameters
         ----------
         seed : int, optional
             Seed for random number generators. If None is passed,
@@ -222,12 +223,12 @@ def create(
         eager_repair : bool, optional
             Flag indicating whether to perform eager repair.
 
-        Returns:
+        Returns
         -------
         Tuple[AbstractBlackBox, np.ndarray, np.ndarray]
             A tuple containing the FoldXSASABlackBox object, the initial wildtype sequences, and the initial fitness values.
 
-        Raises:
+        Raises
         ------
         ValueError
             If wildtype_pdb_path is missing or has an invalid type.
diff --git a/src/poli/objective_repository/foldx_stability/__init__.py b/src/poli/objective_repository/foldx_stability/__init__.py
index e69de29b..fce2a60e 100644
--- a/src/poli/objective_repository/foldx_stability/__init__.py
+++ b/src/poli/objective_repository/foldx_stability/__init__.py
@@ -0,0 +1,1 @@
+"""Black box objective factory and function for FoldX stability."""
diff --git a/src/poli/objective_repository/foldx_stability/foldx_utils.py b/src/poli/objective_repository/foldx_stability/foldx_utils.py
deleted file mode 100644
index bc49680e..00000000
--- a/src/poli/objective_repository/foldx_stability/foldx_utils.py
+++ /dev/null
@@ -1,26 +0,0 @@
-AMINO_ACIDS = [
-    "A",
-    "R",
-    "N",
-    "D",
-    "C",
-    "E",
-    "Q",
-    "G",
-    "H",
-    "I",
-    "L",
-    "K",
-    "M",
-    "F",
-    "P",
-    "S",
-    "T",
-    "W",
-    "Y",
-    "V",
-]
-MAX_SEQUENCE_LENGTH = 100
-
-ENCODING = {amino_acid: i for i, amino_acid in enumerate(AMINO_ACIDS)}
-INVERSE_ENCODING = {i: amino_acid for amino_acid, i in ENCODING.items()}
diff --git a/src/poli/objective_repository/foldx_stability/register.py b/src/poli/objective_repository/foldx_stability/register.py
index bba21749..26a8d18d 100644
--- a/src/poli/objective_repository/foldx_stability/register.py
+++ b/src/poli/objective_repository/foldx_stability/register.py
@@ -20,7 +20,6 @@
 
 from Bio.SeqUtils import seq1
 
-from poli.core.abstract_black_box import AbstractBlackBox
 from poli.core.abstract_problem_factory import AbstractProblemFactory
 from poli.core.problem_setup_information import ProblemSetupInformation
 
@@ -233,7 +232,7 @@ def create(
         Tuple[AbstractBlackBox, np.ndarray, np.ndarray]
             A tuple containing the created black box function, the initial sequence(s), and the initial fitness value(s).
 
-        Raises:
+        Raises
         ------
         ValueError
             If wildtype_pdb_path is missing or has an invalid type.
diff --git a/src/poli/objective_repository/foldx_stability_and_sasa/__init__.py b/src/poli/objective_repository/foldx_stability_and_sasa/__init__.py
index e69de29b..7f7172ef 100644
--- a/src/poli/objective_repository/foldx_stability_and_sasa/__init__.py
+++ b/src/poli/objective_repository/foldx_stability_and_sasa/__init__.py
@@ -0,0 +1,1 @@
+"""A black box for computing stability and solvent accessible surface area (SASA) using FoldX."""
diff --git a/src/poli/objective_repository/foldx_stability_and_sasa/readme.md b/src/poli/objective_repository/foldx_stability_and_sasa/readme.md
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/poli/objective_repository/foldx_stability_and_sasa/register.py b/src/poli/objective_repository/foldx_stability_and_sasa/register.py
index 3e132fa7..10bf975f 100644
--- a/src/poli/objective_repository/foldx_stability_and_sasa/register.py
+++ b/src/poli/objective_repository/foldx_stability_and_sasa/register.py
@@ -1,5 +1,5 @@
 """
-This script registers the stability and SASA FoldX black box
+Registers the stability and SASA FoldX black box
 and objective factory.
 
 FoldX [1] is a simulator that allows for computing the difference
@@ -42,7 +42,7 @@ class FoldXStabilityAndSASABlackBox(FoldxBlackBox):
     """
     A black box implementation for computing the solvent accessible surface area (SASA) score using FoldX.
 
-    Parameters:
+    Parameters
     -----------
     info : ProblemSetupInformation, optional
         The problem setup information. Default is None.
@@ -101,14 +101,14 @@ def _black_box(self, x: np.ndarray, context: None) -> np.ndarray:
         Since the goal is MINIMIZING the energy,
         we return the negative of the total energy.
 
-        Parameters:
+        Parameters
         -----------
         x : np.ndarray
             The input array representing the mutations.
         context : None
             The context for the black box computation.
 
-        Returns:
+        Returns
         --------
         y: np.ndarray
             The computed stability and SASA score(s) as a numpy array.
@@ -155,8 +155,8 @@ class FoldXStabilityAndSASAProblemFactory(AbstractProblemFactory):
     """
     Factory class for creating FoldX SASA (Solvent Accessible Surface Area) problems.
 
-    Methods:
-    --------
+    Methods
+    -------
     get_setup_information:
         Returns the setup information for the problem.
     create:
@@ -202,7 +202,7 @@ def create(
         """
         Create a FoldXSASABlackBox object and compute the initial values of wildtypes.
 
-        Parameters:
+        Parameters
         ----------
         seed : int, optional
             Seed for random number generators. If None is passed,
@@ -226,12 +226,12 @@ def create(
         eager_repair : bool, optional
             Flag indicating whether to perform eager repair.
 
-        Returns:
+        Returns
         -------
         Tuple[AbstractBlackBox, np.ndarray, np.ndarray]
             A tuple containing the FoldXSASABlackBox object, the initial wildtype sequences, and the initial fitness values.
 
-        Raises:
+        Raises
         ------
         ValueError
             If wildtype_pdb_path is missing or has an invalid type.
diff --git a/src/poli/objective_repository/gfp_cbas/make_vae.py b/src/poli/objective_repository/gfp_cbas/make_vae.py
index dd934beb..580b8fa1 100644
--- a/src/poli/objective_repository/gfp_cbas/make_vae.py
+++ b/src/poli/objective_repository/gfp_cbas/make_vae.py
@@ -127,7 +127,7 @@ def _build_latent_vars(self, mu_z, log_var_z, epsilon_std=1.0, kl_scale=1.0):
             epsilon_std: standard deviation of p(epsilon)
             kl_scale: weight of KL divergence loss
 
-        Returns:
+        Returns
             Variables representing z and epsilon
 
         """
diff --git a/src/poli/objective_repository/penalized_logp_lambo/__init__.py b/src/poli/objective_repository/penalized_logp_lambo/__init__.py
index e69de29b..ac8d489a 100644
--- a/src/poli/objective_repository/penalized_logp_lambo/__init__.py
+++ b/src/poli/objective_repository/penalized_logp_lambo/__init__.py
@@ -0,0 +1,13 @@
+"""The exact same computation of LogP used by LamBO.
+
+In [1], the authors optimize jointly for the Quantitative Estimate of
+Druglikeness (QED) and a penalized version of log-solubility (logP).
+For fair comparisons, we implement the exact same computation of logP.
+
+References
+----------
+[1] “Accelerating Bayesian Optimization for Biological Sequence Design with Denoising Autoencoders.”
+Stanton, Samuel, Wesley Maddox, Nate Gruver, Phillip Maffettone,
+Emily Delaney, Peyton Greenside, and Andrew Gordon Wilson.
+arXiv, July 12, 2022. http://arxiv.org/abs/2203.12742.
+"""
diff --git a/src/poli/objective_repository/penalized_logp_lambo/query_example.py b/src/poli/objective_repository/penalized_logp_lambo/query_example.py
deleted file mode 100644
index 1fe262d8..00000000
--- a/src/poli/objective_repository/penalized_logp_lambo/query_example.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import numpy as np
-
-from poli import objective_factory
-
-# Using create
-_, f, x0, y0, _ = objective_factory.create(name="penalized_logp_lambo")
-print(x0)
-print(y0)
-f.terminate()
-
-# Using start
-x0 = np.array([["C"]])
-with objective_factory.start(name="penalized_logp_lambo") as f:
-    print(x0)
-    print(f(x0))
diff --git a/src/poli/objective_repository/penalized_logp_lambo/register.py b/src/poli/objective_repository/penalized_logp_lambo/register.py
index 97791585..71ebc0ac 100644
--- a/src/poli/objective_repository/penalized_logp_lambo/register.py
+++ b/src/poli/objective_repository/penalized_logp_lambo/register.py
@@ -3,7 +3,12 @@
 as LaMBO does it [1]. We do this by importing the function
 they use to compute `logp`.
 
-[1]: TODO: add reference.
+References
+----------
+[1] “Accelerating Bayesian Optimization for Biological Sequence Design with Denoising Autoencoders.”
+Stanton, Samuel, Wesley Maddox, Nate Gruver, Phillip Maffettone,
+Emily Delaney, Peyton Greenside, and Andrew Gordon Wilson.
+arXiv, July 12, 2022. http://arxiv.org/abs/2203.12742.
 """
 from typing import Tuple
 import numpy as np
diff --git a/src/poli/objective_repository/rasp/readme.md b/src/poli/objective_repository/rasp/readme.md
deleted file mode 100644
index d8d5119b..00000000
--- a/src/poli/objective_repository/rasp/readme.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# `RaSP`, Rapid Stability Predictions
-
-This objective function was first discussed in [TODO: ADD](), where the authors propose a deep learning-based way of assessing single mutations from proteins.
-
-We implement it by bringing their (old) [open source implementation](https://github.com/KULL-Centre/papers/tree/main/2022/ML-ddG-Blaabjerg-et-al), and adapting it to `poli`'s set-up. The code is thus mostly theirs.
-
-## We'll need `clang`
-
-## We clone a repo, so we need `git`
-
-## How to use
-
-TODO: write.
diff --git a/src/poli/objective_repository/rasp/register.py b/src/poli/objective_repository/rasp/register.py
index 4bee5c27..56fae866 100644
--- a/src/poli/objective_repository/rasp/register.py
+++ b/src/poli/objective_repository/rasp/register.py
@@ -8,7 +8,13 @@
 this black box can be considered a drop-in replacement of FoldX,
 or Rosetta.
 
-[1] TODO: add reference and implementation.
+[1] “Rapid Protein Stability Prediction Using Deep Learning Representations.”
+Blaabjerg, Lasse M, Maher M Kassem, Lydia L Good, Nicolas Jonsson,
+Matteo Cagiada, Kristoffer E Johansson, Wouter Boomsma, Amelie Stein,
+and Kresten Lindorff-Larsen.  Edited by José D Faraldo-Gómez,
+Detlef Weigel, Nir Ben-Tal, and Julian Echave. eLife 12
+(May 2023): e82593. https://doi.org/10.7554/eLife.82593.
+
 """
 from typing import Union, List, Tuple
 from pathlib import Path
@@ -31,7 +37,7 @@
 )
 from poli.core.util.proteins.mutations import find_closest_wildtype_pdb_file_to_mutant
 from poli.core.util.proteins.defaults import AMINO_ACIDS
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_python_numpy_and_torch
 
 import numpy as np
 
@@ -69,6 +75,58 @@
 
 
 class RaspBlackBox(AbstractBlackBox):
+    """
+    RaSP Black Box implementation.
+
+    Parameters
+    ----------
+    info : ProblemSetupInformation
+        The problem setup information object.
+    batch_size : int, optional
+        The batch size for parallel evaluation, by default None.
+    parallelize : bool, optional
+        Flag indicating whether to parallelize evaluation, by default False.
+    num_workers : int, optional
+        The number of workers for parallel evaluation, by default None.
+    evaluation_budget : int, optional
+        The evaluation budget, by default float("inf").
+    wildtype_pdb_path : Union[Path, List[Path]]
+        The path(s) to the wildtype PDB file(s), by default None.
+    chains_to_keep : List[str], optional
+        The chains to keep in the PDB file(s), by default we
+        keep the chain "A" for all pdbs passed.
+    alphabet : List[str], optional
+        The alphabet for the problem, by default we use
+        the amino acid list provided in poli.core.util.proteins.defaults.
+    experiment_id : str, optional
+        The experiment ID, by default None.
+    tmp_folder : Path, optional
+        The temporary folder path, by default None.
+
+    Methods
+    -------
+    _black_box(x, context=None)
+        The main black box method that performs the computation, i.e.
+        it computes the stability of the mutant(s) in x.
+    _clean_wildtype_pdb_files()
+        This function cleans the wildtype pdb files
+        stored in self.wildtype_pdb_paths, using
+        cached results if they exist.
+
+
+    Raises
+    ------
+    AssertionError
+        If wildtype_pdb_path is not provided.
+
+    Notes
+    -----
+    - The wildtype_pdb_path can be a single Path object or a list of Path objects.
+    - If chains_to_keep is not provided, it defaults to keeping chain A for all wildtypes.
+    - If experiment_id is not provided, it is generated using the current timestamp and a random UUID.
+    - If tmp_folder is not provided, it defaults to the default temporary path.
+    """
+
     def __init__(
         self,
         info: ProblemSetupInformation,
@@ -83,7 +141,44 @@ def __init__(
         tmp_folder: Path = None,
     ):
         """
-        TODO: document
+        Initialize the RaSP Register object.
+
+        Parameters:
+        -----------
+        info : ProblemSetupInformation
+            The problem setup information object.
+        batch_size : int, optional
+            The batch size for parallel evaluation, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize evaluation, by default False.
+        num_workers : int, optional
+            The number of workers for parallel evaluation, by default None.
+        evaluation_budget : int, optional
+            The evaluation budget, by default float("inf").
+        wildtype_pdb_path : Union[Path, List[Path]]
+            The path(s) to the wildtype PDB file(s), by default None.
+        chains_to_keep : List[str], optional
+            The chains to keep in the PDB file(s), by default we
+            keep the chain "A" for all pdbs passed.
+        alphabet : List[str], optional
+            The alphabet for the problem, by default we use
+            the amino acid list provided in poli.core.util.proteins.defaults.
+        experiment_id : str, optional
+            The experiment ID, by default None.
+        tmp_folder : Path, optional
+            The temporary folder path, by default None.
+
+        Raises:
+        -------
+        AssertionError
+            If wildtype_pdb_path is not provided.
+
+        Notes:
+        ------
+        - The wildtype_pdb_path can be a single Path object or a list of Path objects.
+        - If chains_to_keep is not provided, it defaults to keeping chain A for all wildtypes.
+        - If experiment_id is not provided, it is generated using the current timestamp and a random UUID.
+        - If tmp_folder is not provided, it defaults to the default temporary path.
         """
         assert wildtype_pdb_path is not None, (
             "Missing required argument wildtype_pdb_file. "
@@ -214,8 +309,22 @@ def _clean_wildtype_pdb_files(self):
 
     def _black_box(self, x, context=None):
         """
-        TODO: document
-
+        Computes the stability of the mutant(s) in x.
+
+        Parameters
+        ----------
+        x : np.ndarray
+            Input array of shape [b, L] containing strings.
+        context : dict, optional
+            Additional context information (default is None).
+
+        Returns
+        -------
+        y : np.ndarray
+            The stability of the mutant(s) in x.
+
+        Notes
+        -----
         - x is a np.array[str] of shape [b, L], where L is the length
           of the longest sequence in the batch, and b is the batch size.
           We process it by concantenating the array into a single string,
@@ -304,7 +413,7 @@ def _black_box(self, x, context=None):
 class RaspProblemFactory(AbstractProblemFactory):
     def get_setup_information(self) -> ProblemSetupInformation:
         """
-        TODO: document
+        Returns the problem setup information for RaSP.
         """
         alphabet = AMINO_ACIDS
 
@@ -326,9 +435,45 @@ def create(
         alphabet: List[str] = None,
         experiment_id: str = None,
         tmp_folder: Path = None,
-    ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
-        seed_numpy(seed)
-        seed_python(seed)
+    ) -> Tuple[RaspBlackBox, np.ndarray, np.ndarray]:
+        """
+        Creates a RaSP black box instance, alongside initial
+        observations.
+
+        Parameters
+        ----------
+        seed : int, optional
+            The seed value for random number generation, by default None.
+        batch_size : int, optional
+            The batch size for parallel evaluation, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize evaluation, by default False.
+        num_workers : int, optional
+            The number of workers for parallel evaluation, by default None.
+        evaluation_budget : int, optional
+            The evaluation budget, by default float("inf").
+        wildtype_pdb_path : Union[Path, List[Path]]
+            (Required) The path(s) to the wildtype PDB file(s).
+        alphabet : List[str], optional
+            The alphabet for the problem, by default we use
+            the amino acid list provided in poli.core.util.proteins.defaults.
+        experiment_id : str, optional
+            The experiment ID, by default None.
+        tmp_folder : Path, optional
+            The temporary folder path, by default None.
+
+        Returns
+        -------
+        f : RaspBlackBox
+            The RaSP black box instance.
+        x0 : np.ndarray
+            The initial observations (i.e. the wildtypes as sequences
+            of amino acids).
+        y0 : np.ndarray
+            The initial observations (i.e. the stability of the wildtypes).
+        """
+        if seed is not None:
+            seed_python_numpy_and_torch(seed)
 
         if wildtype_pdb_path is None:
             raise ValueError(
diff --git a/src/poli/objective_repository/rdkit_logp/__init__.py b/src/poli/objective_repository/rdkit_logp/__init__.py
index e69de29b..f166cc52 100644
--- a/src/poli/objective_repository/rdkit_logp/__init__.py
+++ b/src/poli/objective_repository/rdkit_logp/__init__.py
@@ -0,0 +1,1 @@
+"""Log-solubility (LogP) computed using RDKit."""
diff --git a/src/poli/objective_repository/rdkit_logp/register.py b/src/poli/objective_repository/rdkit_logp/register.py
index 36755435..ca9541d9 100644
--- a/src/poli/objective_repository/rdkit_logp/register.py
+++ b/src/poli/objective_repository/rdkit_logp/register.py
@@ -1,7 +1,8 @@
-"""
+"""Registers the logP objective factory and black-box
+
 This is a registration script for the rdkit_logp problem,
 whose black box objective function returns the log quotient
-of solubility (a.k.a. logP) [1].
+of solubility (a.k.a. logP).
 
 This black box is a simple wrapper around RDKit's
 descriptors. We allow for both SMILES and SELFIES
@@ -19,14 +20,12 @@
 If you are interested in running this directly,
 instead of inside an isolated process, run:
 
-```
+`
 pip install rdkit selfies
-```
-
-[1] TODO: add reference
+`
 """
 from pathlib import Path
-from typing import Tuple, List
+from typing import Tuple, List, Literal
 import json
 
 import numpy as np
@@ -39,22 +38,59 @@
 
 from poli.core.util.chemistry.string_to_molecule import strings_to_molecules
 
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_python_numpy_and_torch
 
 
 class LogPBlackBox(AbstractBlackBox):
-    """
-    A simple black box that returns the QED
+    """Log-solubility of a small molecule.
+
+    A simple black box that returns the LogP
     of a molecule. By default, we assume that the
     result of concatenating the tokens will be
     a SMILES string, but you can set the context
     variable "from_selfies" to True to indicate
     that the input is a SELFIES string.
 
-    RDKit's Chem.MolFromSmiles function and qed are known
+    RDKit's Chem.MolFromSmiles function and logP are known
     for failing silently, so we return NaN if the
     molecule cannot be parsed or if qed returns
     something other than a float.
+
+    Parameters
+    ----------
+    info : ProblemSetupInformation
+        The problem setup information.
+    batch_size : int, optional
+        The batch size for processing multiple inputs simultaneously, by default None.
+    parallelize : bool, optional
+        Flag indicating whether to parallelize the computation, by default False.
+    num_workers : int, optional
+        The number of workers to use for parallel computation, by default None.
+    evaluation_budget:  int, optional
+        The maximum number of function evaluations. Default is infinity.
+    alphabet : List[str], optional
+        The alphabet to use for the black box. If not provided,
+        we use the alphabet from the problem setup information.
+    from_selfies : bool, optional
+        Flag indicating whether the input is a SELFIES string,
+        by default False (i.e. we expect a SMILES string).
+
+    Attributes
+    ----------
+    string_to_idx : dict
+        The mapping of symbols to their corresponding indices in the alphabet.
+    idx_to_string : dict
+        The mapping of indices to their corresponding symbols in the alphabet.
+    from_selfies : bool
+        Flag indicating whether the input is a SELFIES string.
+    from_smiles : bool
+        Flag indicating whether the input is a SMILES string.
+
+    Methods
+    -------
+    _black_box(x, context=None)
+        The main black box method that performs the computation, i.e.
+        it computes the logP of the molecule in x.
     """
 
     def __init__(
@@ -67,6 +103,28 @@ def __init__(
         alphabet: List[str] = None,
         from_selfies: bool = False,
     ):
+        """
+        Initializes the LogP black box.
+
+        Parameters
+        ----------
+        info : ProblemSetupInformation
+            The problem setup information.
+        batch_size : int, optional
+            The batch size for processing multiple inputs simultaneously, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize the computation, by default False.
+        num_workers : int, optional
+            The number of workers to use for parallel computation, by default None.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        alphabet : List[str], optional
+            The alphabet to use for the black box. If not provided,
+            we use the alphabet from the problem setup information.
+        from_selfies : bool, optional
+            Flag indicating whether the input is a SELFIES string,
+            by default False (i.e. we expect a SMILES string).
+        """
         if alphabet is None:
             # TODO: remove this as soon as we have a default alphabet
             assert info.alphabet is not None, (
@@ -92,10 +150,11 @@ def __init__(
 
     # The only method you have to define
     def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
-        """
+        """Computes the logP of a molecule x (array of strings).
+
         Assuming that x is an array of integers of length L,
         we use the alphabet to construct a SMILES string,
-        and query QED from RDKit.
+        and query logP from RDKit.
         """
         if x.dtype.kind in ["U", "S"]:
             molecule_strings = ["".join([x_ij for x_ij in x_i.flatten()]) for x_i in x]
@@ -139,6 +198,14 @@ def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
 
 class LogPProblemFactory(AbstractProblemFactory):
     def get_setup_information(self) -> ProblemSetupInformation:
+        """
+        Returns the setup information for the logP problem.
+
+        Returns
+        -------
+        info : ProblemSetupInformation
+            The setup information for the logP problem.
+        """
         return ProblemSetupInformation(
             name="rdkit_logp",
             max_sequence_length=np.inf,
@@ -155,10 +222,42 @@ def create(
         evaluation_budget: int = float("inf"),
         path_to_alphabet: Path = None,
         alphabet: List[str] = None,
-        string_representation: str = "SMILES",
-    ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
-        seed_numpy(seed)
-        seed_python(seed)
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+    ) -> Tuple[LogPBlackBox, np.ndarray, np.ndarray]:
+        """Creates a logP problem instance.
+
+        Parameters
+        ----------
+        seed : int, optional
+            The seed value for random number generation, by default None.
+        batch_size : int, optional
+            The batch size for processing multiple inputs simultaneously, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize the computation, by default False.
+        num_workers : int, optional
+            The number of workers to use for parallel computation, by default None.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        path_to_alphabet : Path, optional
+            The path to the alphabet file, by default None.
+        alphabet : List[str], optional
+            The alphabet to use for the black box. If not provided,
+            we use the alphabet from the problem setup information.
+        string_representation : str, optional
+            The string representation of the input, by default "SMILES".
+            Supported values are "SMILES" and "SELFIES".
+
+        Returns
+        -------
+        f : LogPBlackBox
+            The logP black box function.
+        x0 : np.ndarray
+            The initial input values (a single carbon).
+        y0 : np.ndarray
+            The initial output values (the logP of a single carbon).
+        """
+        if seed is not None:
+            seed_python_numpy_and_torch(seed)
 
         if path_to_alphabet is None and alphabet is None:
             # TODO: add support for more file types
diff --git a/src/poli/objective_repository/rdkit_qed/__init__.py b/src/poli/objective_repository/rdkit_qed/__init__.py
index e69de29b..9a3ce526 100644
--- a/src/poli/objective_repository/rdkit_qed/__init__.py
+++ b/src/poli/objective_repository/rdkit_qed/__init__.py
@@ -0,0 +1,1 @@
+"""Quantitative Estimate of Druglikeness (QED) using RDKit."""
diff --git a/src/poli/objective_repository/rdkit_qed/register.py b/src/poli/objective_repository/rdkit_qed/register.py
index d001a559..bbe8765d 100644
--- a/src/poli/objective_repository/rdkit_qed/register.py
+++ b/src/poli/objective_repository/rdkit_qed/register.py
@@ -1,8 +1,9 @@
-"""
+"""Registers the black box factory and function for QED using RDKit.
+
 This is a registration script for the rdkit_qed problem,
 whose black box objective function returns the quantitative
 estimate of druglikeness, which is a continuous version
-of Lipinsky's rule of 5 [1].
+of Lipinsky's rule of 5.
 
 This black box is a simple wrapper around RDKit's
 Chem.QED.qed function, which returns a float between
@@ -19,14 +20,12 @@
 
 Run:
 
-```
+`
 pip install rdkit selfies
-```
-
-[1] TODO: add reference
+`
 """
 from pathlib import Path
-from typing import Tuple, List
+from typing import Tuple, List, Literal
 import json
 
 import numpy as np
@@ -39,11 +38,12 @@
 
 from poli.core.util.chemistry.string_to_molecule import strings_to_molecules
 
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_python_numpy_and_torch
 
 
 class QEDBlackBox(AbstractBlackBox):
-    """
+    """Quantitative estimate of druglikeness (QED) black box.
+
     A simple black box that returns the QED
     of a molecule. By default, we assume that the
     result of concatenating the tokens will be
@@ -55,6 +55,40 @@ class QEDBlackBox(AbstractBlackBox):
     for failing silently, so we return NaN if the
     molecule cannot be parsed or if qed returns
     something other than a float.
+
+    Parameters
+    ----------
+    info : ProblemSetupInformation
+        The problem setup information.
+    batch_size : int, optional
+        The batch size for processing multiple inputs simultaneously, by default None.
+    parallelize : bool, optional
+        Flag indicating whether to parallelize the computation, by default False.
+    num_workers : int, optional
+        The number of workers to use for parallel computation, by default None.
+    evaluation_budget:  int, optional
+        The maximum number of function evaluations. Default is infinity.
+    alphabet : List[str], optional
+        The alphabet to use for the black box. If not provided,
+        the alphabet from the problem setup information will be used.
+        We strongly advice providing an alphabet.
+    from_selfies : bool, optional
+        Flag indicating whether the input is a SELFIES string, by default False.
+
+    Attributes
+    ----------
+    alphabet : List[str]
+        The alphabet to use for the black box.
+    string_to_idx : dict
+        The mapping of symbols to their corresponding indices in the alphabet.
+    idx_to_string : dict
+        The mapping of indices to their corresponding symbols in the alphabet.
+
+    Methods
+    -------
+    _black_box(x, context=None)
+        The main black box method that performs the computation, i.e.
+        it computes the qed of the molecule in x.
     """
 
     def __init__(
@@ -67,6 +101,28 @@ def __init__(
         alphabet: List[str] = None,
         from_selfies: bool = False,
     ):
+        """
+        Initialize the QEDBlackBox.
+
+        Parameters
+        ----------
+        info : ProblemSetupInformation
+            The problem setup information object.
+        batch_size : int, optional
+            The batch size for parallel evaluation, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize the evaluation, by default False.
+        num_workers : int, optional
+            The number of workers for parallel evaluation, by default None.
+        evaluation_budget : int, optional
+            The maximum number of evaluations, by default float("inf").
+        alphabet : List[str], optional
+            The alphabet for encoding molecules, by default it's
+            the one inside the problem setup information. We strongly
+            advice providing an alphabet.
+        from_selfies : bool, optional
+            Flag indicating whether the molecules are encoded using SELFIES, by default False.
+        """
         super().__init__(info, batch_size, parallelize, num_workers)
         if alphabet is None:
             # TODO: remove this as soon as we have a default alphabet
@@ -94,10 +150,20 @@ def __init__(
 
     # The only method you have to define
     def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
-        """
-        Assuming that x is an array of integers of length L,
-        we use the alphabet to construct a SMILES string,
-        and query QED from RDKit.
+        """Computes the qed of the molecule in x.
+
+        Parameters
+        ----------
+        x : np.ndarray
+            Input array of shape [b, L] containing strings or integers.
+            If the elements are integers, they are converted to strings using the alphabet.
+        context : dict, optional
+            Additional context information (default is None).
+
+        Returns
+        -------
+        y : np.ndarray
+            Array of shape [b, 1] containing the qed of the molecule in x.
         """
         # We check if the user provided an array of strings
         if x.dtype.kind in ["U", "S"]:
@@ -140,6 +206,21 @@ def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
 
 
 class QEDProblemFactory(AbstractProblemFactory):
+    """Problem factory for the QED problem.
+
+    The Quantitative Estimate of Druglikeness (QED) objective
+    function returns a "continuous" version of Lipinsky's rule
+    of 5, which is a heuristic to evaluate the druglikeness
+    of a molecule (discarding molecules that are e.g. too heavy).
+
+    Methods
+    -------
+    get_setup_information()
+        Returns the setup information for the problem.
+    create(...)
+        Creates a problem instance with the specified parameters.
+    """
+
     def get_setup_information(self) -> ProblemSetupInformation:
         # TODO: Add a default alphabet here.
         return ProblemSetupInformation(
@@ -158,10 +239,45 @@ def create(
         evaluation_budget: int = float("inf"),
         alphabet: List[str] = None,
         path_to_alphabet: Path = None,
-        string_representation: str = "SMILES",
-    ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
-        seed_numpy(seed)
-        seed_python(seed)
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+    ) -> Tuple[QEDBlackBox, np.ndarray, np.ndarray]:
+        """Creates a QED black box function and initial observations.
+
+        Parameters
+        ----------
+        seed : int, optional
+            The seed value for random number generation, by default None.
+        batch_size : int, optional
+            The batch size for parallel evaluation, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize the evaluation, by default False.
+        num_workers : int, optional
+            The number of workers for parallel evaluation, by default None.
+        evaluation_budget : int, optional
+            The maximum number of evaluations, by default float("inf").
+        alphabet : List[str], optional
+            The alphabet to use for encoding molecules, by default None.
+            If not provided, the alphabet from the problem setup information
+            will be used. We strongly advice providing an alphabet.
+        path_to_alphabet : Path, optional
+            The path to a json file containing the alphabet, by default None.
+            If not provided, the alphabet from the problem setup information
+            will be used. We strongly advice providing an alphabet.
+        string_representation : str, optional
+            The string representation to use, by default "SMILES".
+            It must be either "SMILES" or "SELFIES".
+
+        Returns
+        -------
+        f : QEDBlackBox
+            The QED black box function.
+        x0 : np.ndarray
+            The initial input (a single carbon).
+        y0 : np.ndarray
+            The initial output (the qed of a single carbon).
+        """
+        if seed is not None:
+            seed_python_numpy_and_torch(seed)
 
         if path_to_alphabet is None and alphabet is None:
             # TODO: add support for more file types
diff --git a/src/poli/objective_repository/sa_tdc/__init__.py b/src/poli/objective_repository/sa_tdc/__init__.py
index e69de29b..9c6cc674 100644
--- a/src/poli/objective_repository/sa_tdc/__init__.py
+++ b/src/poli/objective_repository/sa_tdc/__init__.py
@@ -0,0 +1,1 @@
+"""A wrapper around the synthetic accessiblity (SA) oracle of TDC."""
diff --git a/src/poli/objective_repository/sa_tdc/query_example.py b/src/poli/objective_repository/sa_tdc/query_example.py
deleted file mode 100644
index d42878cc..00000000
--- a/src/poli/objective_repository/sa_tdc/query_example.py
+++ /dev/null
@@ -1,15 +0,0 @@
-import numpy as np
-
-from poli import objective_factory
-
-# Using create
-_, f, x0, y0, _ = objective_factory.create(name="sa_tdc", force_register=True)
-print(x0)
-print(y0)  # Should be ~ 2.8548...
-f.terminate()
-
-# Using start
-x0 = np.array([["C"]])
-with objective_factory.start(name="sa_tdc") as f:
-    print(x0)
-    print(f(x0))
diff --git a/src/poli/objective_repository/sa_tdc/register.py b/src/poli/objective_repository/sa_tdc/register.py
index b098d039..01bd3dd3 100644
--- a/src/poli/objective_repository/sa_tdc/register.py
+++ b/src/poli/objective_repository/sa_tdc/register.py
@@ -1,8 +1,10 @@
 """
-In this module, we implement a synthetic-accessibility 
-objective using the TDC oracles [1].
+Implements a synthetic-accessibility objective using the TDC oracles [1].
 
-[1] TODO: add reference.
+References
+----------
+[1] Artificial intelligence foundation for therapeutic science.
+    Huang, K., Fu, T., Gao, W. et al.  Nat Chem Biol 18, 1033-1036 (2022). https://doi.org/10.1038/s41589-022-01131-2
 """
 from typing import Tuple
 
@@ -16,10 +18,28 @@
 
 from poli.core.util.chemistry.string_to_molecule import translate_smiles_to_selfies
 
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_numpy, seed_python, seed_python_numpy_and_torch
 
 
 class SABlackBox(TDCBlackBox):
+    """Synthetic-accessibility black box implementation using the TDC oracles [1].
+
+    Parameters
+    ----------
+    info : ProblemSetupInformation
+        The problem setup information.
+    batch_size : int, optional
+        The batch size for simultaneous execution, by default None.
+    parallelize : bool, optional
+        Flag indicating whether to parallelize execution, by default False.
+    num_workers : int, optional
+        The number of workers for parallel execution, by default None.
+    evaluation_budget:  int, optional
+        The maximum number of function evaluations. Default is infinity.
+    from_smiles : bool, optional
+        Flag indicating whether to use SMILES strings as input, by default True.
+    """
+
     def __init__(
         self,
         info: ProblemSetupInformation,
@@ -29,6 +49,24 @@ def __init__(
         evaluation_budget: int = float("inf"),
         from_smiles: bool = True,
     ):
+        """
+        Initialize the SABlackBox object.
+
+        Parameters
+        ----------
+        info : ProblemSetupInformation
+            The problem setup information object.
+        batch_size : int, optional
+            The batch size for parallel evaluation, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize the evaluation, by default False.
+        num_workers : int, optional
+            The number of workers for parallel evaluation, by default None.
+        evaluation_budget : int, optional
+            The maximum number of evaluations, by default float("inf").
+        from_smiles : bool, optional
+            Flag indicating whether to use SMILES strings as input, by default True.
+        """
         oracle_name = "SA"
         super().__init__(
             oracle_name=oracle_name,
@@ -42,7 +80,25 @@ def __init__(
 
 
 class SAProblemFactory(AbstractProblemFactory):
+    """Problem factory for the synthetic-accessibility problem.
+
+    Methods
+    -------
+    get_setup_information()
+        Returns the setup information for the problem.
+    create(...)
+        Creates a synthetic-accessibility problem instance with the specified parameters.
+    """
+
     def get_setup_information(self) -> ProblemSetupInformation:
+        """
+        Returns the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
         return ProblemSetupInformation(
             name="sa_tdc",
             max_sequence_length=np.inf,
@@ -60,7 +116,31 @@ def create(
         string_representation: str = "SMILES",
     ) -> Tuple[SABlackBox, np.ndarray, np.ndarray]:
         """
-        TODO: document.
+        Creates a synthetic-accessibility problem instance with the specified parameters.
+
+        Parameters
+        -----------
+        seed:  int, optional
+            The seed for random number generation. Default is None.
+        batch_size:  int, optional
+            The batch size for simultaneous evaluation. Default is None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize the evaluation. Default is False.
+        num_workers:  int, optional
+            The number of workers for parallel evaluation. Default is None.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        string_representation : str, optional
+            The string representation of the input molecules. Default is "SMILES".
+
+        Returns
+        --------
+        f: SABlackBox
+            The synthetic-accessibility black box function.
+        x0: np.ndarray
+            The initial input (taken from TDC: CCNC(=O)c1ccc(NC(=O)N2CC[C@H](C)[C@H](O)C2)c(C)c1).
+        y0: np.ndarray
+            The initial output (i.e. the corresponding SA).
         """
         seed_numpy(seed)
         seed_python(seed)
diff --git a/src/poli/objective_repository/super_mario_bros/__init__.py b/src/poli/objective_repository/super_mario_bros/__init__.py
index e69de29b..c7a29832 100644
--- a/src/poli/objective_repository/super_mario_bros/__init__.py
+++ b/src/poli/objective_repository/super_mario_bros/__init__.py
@@ -0,0 +1,1 @@
+"""A toy problem avout creating Super Mario Bros levels."""
diff --git a/src/poli/objective_repository/super_mario_bros/level_utils.py b/src/poli/objective_repository/super_mario_bros/level_utils.py
index dbe5e3f5..50865731 100644
--- a/src/poli/objective_repository/super_mario_bros/level_utils.py
+++ b/src/poli/objective_repository/super_mario_bros/level_utils.py
@@ -1,3 +1,4 @@
+"""Utilities for transforming levels to arrays and back."""
 from typing import List
 from itertools import product
 
@@ -7,20 +8,26 @@
 Tensor = torch.Tensor
 
 
-def level_to_list(level_txt):
+def level_to_list(level_txt: str) -> List[List[str]]:
+    """
+    Takes a level as a string and returns
+    a list of lists of individual tokens.
+    """
     # Returns a list by splitting the level text
     # by \n.
     as_list = level_txt.split("\n")
     return [list(row) for row in as_list if row != ""]
 
 
-def level_to_array(level_txt):
+def level_to_array(level_txt: str) -> np.ndarray:
+    """Parses a level from string to numpy array."""
     # Returns a np array by splitting the level text
     # by \n.
     return np.array(level_to_list(level_txt))
 
 
 def levels_to_onehot(levels: np.ndarray, n_sprites: int = 11) -> np.ndarray:
+    """Transforms an array [b, w, h] of integers into a one-hot array [b, n_sprites, w, h]."""
     batch_size, w, h = levels.shape
     y_onehot = np.zeros((batch_size, n_sprites, h, w))
     for b, level in enumerate(levels):
@@ -41,6 +48,9 @@ def vectorized(prob_matrix, items):
 
 
 def onehot_to_levels(levels_onehot: np.ndarray, sampling=False, seed=0) -> np.ndarray:
+    """
+    Transforms a level from probits to integers.
+    """
     if sampling:
         # From log-softmax to softmax.
         levels_onehot = np.exp(levels_onehot)
@@ -109,6 +119,10 @@ def add_padding_to_level(level: np.ndarray, n_padding: int = 1) -> np.ndarray:
 
 
 def clean_level(level: np.ndarray) -> List[List[int]]:
+    """
+    Cleans a level by removing Mario (token id: 11),
+    and replacing it with empty space.
+    """
     # Cleaning up Mario (11), replacing
     # it with empty space (2).
     level[level == 11] = 2
diff --git a/src/poli/objective_repository/super_mario_bros/model.py b/src/poli/objective_repository/super_mario_bros/model.py
index 698801da..666443d5 100644
--- a/src/poli/objective_repository/super_mario_bros/model.py
+++ b/src/poli/objective_repository/super_mario_bros/model.py
@@ -1,7 +1,5 @@
 """
 A categorical VAE that can train on Mario.
-
-The notation very much follows the original VAE paper.
 """
 from typing import Tuple
 from pathlib import Path
diff --git a/src/poli/objective_repository/super_mario_bros/readme.md b/src/poli/objective_repository/super_mario_bros/readme.md
deleted file mode 100644
index e69de29b..00000000
diff --git a/src/poli/objective_repository/super_mario_bros/register.py b/src/poli/objective_repository/super_mario_bros/register.py
index 6fb9d7b3..8ed5755b 100644
--- a/src/poli/objective_repository/super_mario_bros/register.py
+++ b/src/poli/objective_repository/super_mario_bros/register.py
@@ -1,10 +1,10 @@
-"""
-This script registers Super Mario Bros as an
-objective function for POLi.
+"""Registers the Super Mario Bros objective function and factory.
 
 The metric being recorded is the number of
 jumps Mario makes in a given level. The goal
-is, of course, maximizing it.
+is, of course, maximizing it while keeping the
+level playable.
+
 """
 from typing import Tuple
 from pathlib import Path
@@ -16,7 +16,7 @@
 from poli.core.abstract_problem_factory import AbstractProblemFactory
 from poli.core.problem_setup_information import ProblemSetupInformation
 
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_python_numpy_and_torch
 
 from model import load_example_model
 
@@ -32,6 +32,37 @@
 
 
 class SMBBlackBox(AbstractBlackBox):
+    """
+    Black box which returns how many jumps were performed
+    by Mario in the given level.
+
+    Parameters
+    ----------
+    info : ProblemSetupInformation
+        The problem setup information.
+    batch_size : int, optional
+        The batch size for simultaneous execution, by default None.
+    parallelize : bool, optional
+        Flag indicating whether to parallelize execution, by default False.
+    num_workers : int, optional
+        The number of workers for parallel execution, by default None.
+    evaluation_budget:  int, optional
+        The maximum number of function evaluations. Default is infinity.
+
+    Attributes
+    ----------
+    model : torch.nn.Module
+        The model used for the simulation.
+
+    Methods
+    -------
+    _black_box(self, x, context=None)
+        Runs the given input x as a latent code
+        through the model and returns the number
+        of jumps Mario makes in the level. If the
+        level is not solvable, returns np.NaN.
+    """
+
     def __init__(
         self,
         info: ProblemSetupInformation,
@@ -40,6 +71,22 @@ def __init__(
         num_workers: int = None,
         evaluation_budget: int = float("inf"),
     ):
+        """
+        Initializes a new instance of the SMBBlackBox class.
+
+        Parameters
+        ----------
+        info : ProblemSetupInformation
+            The problem setup information.
+        batch_size : int, optional
+            The batch size for simultaneous execution, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize execution, by default False.
+        num_workers : int, optional
+            The number of workers for parallel execution, by default None.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        """
         super().__init__(
             info=info,
             batch_size=batch_size,
@@ -50,11 +97,7 @@ def __init__(
         self.model = load_example_model(THIS_DIR / "example.pt")
 
     def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
-        """
-        Runs the given input x as a latent code
-        through the model and returns the number
-        of jumps Mario makes in the level.
-        """
+        """Computes number of jumps in a given latent code x."""
         z = torch.from_numpy(x).float()
         z = z.unsqueeze(0)
 
@@ -67,12 +110,25 @@ def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
         if res["marioStatus"] == 1:
             jumps = res["jumpActionsPerformed"]
         else:
-            jumps = -10.0
+            jumps = np.nan
+
         return np.array([jumps], dtype=float).reshape(1, 1)
 
 
 class SMBProblemFactory(AbstractProblemFactory):
+    """
+    Problem factory for the Super Mario Bros objective function.
+
+    Methods
+    -------
+    get_setup_information(self)
+        Returns the setup information for the problem.
+    create(...) -> Tuple[SMBBlackBox, np.ndarray, np.ndarray]
+        Creates a new instance of the SMBBlackBox class.
+    """
+
     def get_setup_information(self) -> ProblemSetupInformation:
+        """Returns the setup information for the problem."""
         alphabet_symbols = ["z1", "z2"]
         alphabet = {symbol: i for i, symbol in enumerate(alphabet_symbols)}
 
@@ -90,9 +146,33 @@ def create(
         parallelize: bool = False,
         num_workers: int = None,
         evaluation_budget: int = float("inf"),
-    ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
-        seed_numpy(seed)
-        seed_python(seed)
+    ) -> Tuple[SMBBlackBox, np.ndarray, np.ndarray]:
+        """Creates a new instance of the Super Mario Bros problem.
+
+        Parameters
+        ----------
+        seed : int, optional
+            The seed for the random number generator, by default None.
+        batch_size : int, optional
+            The batch size for simultaneous execution, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize execution, by default False.
+        num_workers : int, optional
+            The number of workers for parallel execution, by default None.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+
+        Returns
+        -------
+        f: SMBBlackBox
+            The SMBBlackBox instance.
+        x0: np.ndarray
+            The initial latent code.
+        y0: np.ndarray
+            The initial objective function value (i.e. number of jumps).
+        """
+        if seed is not None:
+            seed_python_numpy_and_torch(seed)
 
         info = self.get_setup_information()
         f = SMBBlackBox(
diff --git a/src/poli/objective_repository/toy_continuous_problem/__init__.py b/src/poli/objective_repository/toy_continuous_problem/__init__.py
index e69de29b..5345efe6 100644
--- a/src/poli/objective_repository/toy_continuous_problem/__init__.py
+++ b/src/poli/objective_repository/toy_continuous_problem/__init__.py
@@ -0,0 +1,1 @@
+"""A collection of toy continuous functions for optimization."""
diff --git a/src/poli/objective_repository/toy_continuous_problem/definitions.py b/src/poli/objective_repository/toy_continuous_problem/definitions.py
index cf8553a2..00f7ceb9 100644
--- a/src/poli/objective_repository/toy_continuous_problem/definitions.py
+++ b/src/poli/objective_repository/toy_continuous_problem/definitions.py
@@ -1,14 +1,11 @@
-"""
+"""Defines several continuous toy problems.
+
 This script defines all the artificial landscapes with
 signature [np.ndarray] -> np.ndarray. You might
 see that the signs have been flipped from [1] or [2]. This is
 because we're dealing with maximizations instead of
 minimizations.
 
-In what follows, x is a tensor of arbitrary dimension
-(either (b, d), or (d,), where d is the dimension of
-the design space).
-
 [1] Ali R. Al-Roomi (2015). Unconstrained Single-Objective Benchmark
     Functions Repository [https://www.al-roomi.org/benchmarks/unconstrained].
     Halifax, Nova Scotia, Canada: Dalhousie University, Electrical and Computer
diff --git a/src/poli/objective_repository/toy_continuous_problem/register.py b/src/poli/objective_repository/toy_continuous_problem/register.py
index 032d344d..b73687c2 100644
--- a/src/poli/objective_repository/toy_continuous_problem/register.py
+++ b/src/poli/objective_repository/toy_continuous_problem/register.py
@@ -1,4 +1,5 @@
-"""
+"""Registers the toy continuous problem objective function and factory.
+
 This is a registration script for the toy continuous
 objectives which are usually used to benchmark continuous
 optimization algorithms in several dimensions.
@@ -9,12 +10,8 @@
 The problem is registered as 'toy_continuous_problem',
 and it uses a conda environment called 'poli__base'
 (see the environment.yml file in this folder).
-
-The following problems are registered:
-TODO: write.
 """
 from typing import Tuple
-from string import ascii_uppercase
 
 import numpy as np
 
@@ -22,7 +19,7 @@
 from poli.core.abstract_problem_factory import AbstractProblemFactory
 from poli.core.problem_setup_information import ProblemSetupInformation
 
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_python_numpy_and_torch
 
 from .toy_continuous_problem import (
     POSSIBLE_FUNCTIONS,
@@ -31,6 +28,51 @@
 
 
 class ToyContinuousBlackBox(AbstractBlackBox):
+    """
+    A black box implementation for evaluating the Toy Continuous Problem.
+
+    Parameters
+    ----------
+    info : ProblemSetupInformation
+        The problem setup information.
+    batch_size : int, optional
+        The batch size for parallel evaluation, by default None.
+    parallelize : bool, optional
+        Whether to parallelize the evaluation, by default False.
+    num_workers : int, optional
+        The number of workers for parallel evaluation, by default None.
+    evaluation_budget : int, optional
+        The maximum number of evaluations, by default float("inf").
+    function_name : str
+        The name of the toy continuous function to evaluate, by default None.
+    n_dimensions : int
+        The number of dimensions for the toy continuous function, by default 2.
+    embed_in : int, optional
+        If not None, the continuous problem is randomly embedded in this dimension.
+        By default, None.
+
+    Attributes
+    ----------
+    alphabet : None
+        The alphabet for the toy continuous problem.
+    function_name : str
+        The name of the toy continuous function.
+    n_dimensions : int
+        The number of dimensions for the toy continuous function.
+    embed_in : int
+        The dimension in which to embed the problem.
+    function : ToyContinuousProblem
+        The toy continuous problem instance.
+    bounds : Tuple[np.ndarray, np.ndarray]
+        The lower and upper bounds for the toy continuous problem.
+
+    Methods
+    -------
+    _black_box(x, context=None)
+        Evaluates the toy continuous problem on a continuous input x.
+
+    """
+
     def __init__(
         self,
         info: ProblemSetupInformation,
@@ -56,6 +98,7 @@ def __init__(
             n_dims=n_dimensions,
             embed_in=embed_in,
         )
+        self.bounds = self.function.limits
 
         super().__init__(
             info=info,
@@ -65,13 +108,28 @@ def __init__(
             evaluation_budget=evaluation_budget,
         )
 
-    # The only method you have to define
     def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
-        # x is a [b, L] array of strings or ints, if they are
-        # ints, then we should convert them to strings
-        # using the alphabet.
-        # TODO: this assumes that the input is a batch of size 1.
-        # Address this when we change __call__.
+        """
+        Evaluates the toy continuous problem on a continuous input x.
+
+        Parameters
+        ----------
+        x : np.ndarray
+            The input to evaluate (expected to be an array [b, L] of floats).
+        context : dict, optional
+            The context of the evaluation, by default None.
+
+        Returns
+        -------
+        y : np.ndarray
+            The evaluation of the toy continuous problem.
+
+        Raises
+        ------
+        ValueError
+            If the input x is not of type float.
+
+        """
         if not x.dtype.kind == "f":
             raise ValueError("Expected a batch of floats. ")
 
@@ -80,6 +138,14 @@ def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
 
 class ToyContinuousProblemFactory(AbstractProblemFactory):
     def get_setup_information(self) -> ProblemSetupInformation:
+        """
+        Returns the setup information for the problem.
+
+        Returns
+        -------
+        problem_info : ProblemSetupInformation
+            The setup information for the problem.
+        """
         return ProblemSetupInformation(
             name="toy_continuous_problem",
             max_sequence_length=np.inf,
@@ -97,14 +163,51 @@ def create(
         function_name: str = None,
         n_dimensions: int = 2,
         embed_in: int = None,
-    ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
+    ) -> Tuple[ToyContinuousBlackBox, np.ndarray, np.ndarray]:
+        """
+        Creates a new instance of the toy continuous problem.
+
+        Parameters
+        ----------
+        seed : int, optional
+            The seed for the random number generator, by default None.
+        batch_size : int, optional
+            The batch size for simultaneous execution, by default None.
+        parallelize : bool, optional
+            Flag indicating whether to parallelize execution, by default False.
+        num_workers : int, optional
+            The number of workers for parallel execution, by default None.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        function_name : str, optional
+            The name of the toy continuous function to evaluate, by default None.
+        n_dimensions : int, optional
+            The number of dimensions for the toy continuous function, by default 2.
+        embed_in : int, optional
+            If not None, the continuous problem is randomly embedded in this dimension.
+            By default, None.
+
+        Returns
+        -------
+        f : ToyContinuousBlackBox
+            The toy continuous black box instance.
+        x0 : np.ndarray
+            The initial input.
+        y0 : np.ndarray
+            The initial objective function value.
+
+        Raises
+        ------
+        ValueError
+            If the function_name is not one of the valid functions.
+        """
         assert (
             function_name in POSSIBLE_FUNCTIONS
         ), f"'{function_name}' is not a valid function name. Expected it to be one of {POSSIBLE_FUNCTIONS}."
 
         # We set the seed for numpy and python
-        seed_numpy(seed)
-        seed_python(seed)
+        if seed is not None:
+            seed_python_numpy_and_torch(seed)
 
         problem_info = self.get_setup_information()
         f = ToyContinuousBlackBox(
diff --git a/src/poli/objective_repository/toy_continuous_problem/toy_continuous_problem.py b/src/poli/objective_repository/toy_continuous_problem/toy_continuous_problem.py
index c809e95d..171c66a6 100644
--- a/src/poli/objective_repository/toy_continuous_problem/toy_continuous_problem.py
+++ b/src/poli/objective_repository/toy_continuous_problem/toy_continuous_problem.py
@@ -1,8 +1,5 @@
 """
-A series of testbed functions for optimization. FYI, we changed
-the functions from testing maximization instead of minimization.
-
-When run, this script plots the three example functions provided.
+A series of testbed functions for optimization.
 
 See for more examples:
 https://en.wikipedia.org/wiki/Test_functions_for_optimization
@@ -61,8 +58,7 @@
 
 class ToyContinuousProblem:
     """
-    This class will contain the toy objective functions, their limits,
-    and the optima location.
+    Contains the toy objective functions, their limits, and the optima location.
 
     For more information, check definitions.py and [1].
 
diff --git a/src/poli/objective_repository/white_noise/__init__.py b/src/poli/objective_repository/white_noise/__init__.py
index e69de29b..0f580d75 100644
--- a/src/poli/objective_repository/white_noise/__init__.py
+++ b/src/poli/objective_repository/white_noise/__init__.py
@@ -0,0 +1,1 @@
+"""A toy problem that returns white noise."""
diff --git a/src/poli/objective_repository/white_noise/register.py b/src/poli/objective_repository/white_noise/register.py
index 1cf7af56..bbb20b63 100644
--- a/src/poli/objective_repository/white_noise/register.py
+++ b/src/poli/objective_repository/white_noise/register.py
@@ -1,4 +1,5 @@
-"""
+"""Registers the white noise problem factory and black box.
+
 This is a registration script for the white_noise problem,
 whose black box objective function returns standard Gaussian
 noise.
@@ -15,10 +16,33 @@
 from poli.core.abstract_problem_factory import AbstractProblemFactory
 from poli.core.problem_setup_information import ProblemSetupInformation
 
-from poli.core.util.seeding import seed_numpy, seed_python
+from poli.core.util.seeding import seed_python_numpy_and_torch
 
 
 class WhiteNoiseBlackBox(AbstractBlackBox):
+    """
+    A toy black box function that generates standard Gaussian noise.
+
+    Parameters
+    ----------
+    info : ProblemSetupInformation
+        The problem setup information.
+    batch_size : int, optional
+        The batch size for vectorized evaluation.
+    parallelize : bool, optional
+        Whether to parallelize the evaluation.
+    num_workers : int, optional
+        The number of workers for parallel evaluation.
+    evaluation_budget : int, optional
+        The maximum number of evaluations.
+
+    Methods
+    -------
+    _black_box(x, context=None)
+        Returns standard Gaussian noise.
+
+    """
+
     def __init__(
         self,
         info: ProblemSetupInformation,
@@ -27,6 +51,24 @@ def __init__(
         num_workers: int = None,
         evaluation_budget: int = float("inf"),
     ):
+        """
+        Initializes a WhiteNoiseBlackBox.
+
+        Parameters
+        ----------
+        info : ProblemSetupInformation
+            The problem setup information.
+        batch_size : int, optional
+            The batch size for vectorized evaluation, by default None (i.e.
+            all of the input).
+        parallelize : bool, optional
+            Whether to parallelize the evaluation, by default False.
+        num_workers : int, optional
+            The number of workers for parallel evaluation, by default None (which
+            corresponds to half the CPUs available, rounded downwards).
+        evaluation_budget : int, optional
+            The maximum number of evaluations, by default float("inf").
+        """
         super().__init__(
             info=info,
             batch_size=batch_size,
@@ -35,13 +77,34 @@ def __init__(
             evaluation_budget=evaluation_budget,
         )
 
-    # The only method you have to define
     def _black_box(self, x: np.ndarray, context: dict = None) -> np.ndarray:
+        """Returns standard Gaussian noise.
+
+        Parameters
+        ----------
+        x : np.ndarray
+            The input.
+        context : dict, optional
+            The context, by default None.
+
+        Returns
+        -------
+        y : np.ndarray
+            Standard Gaussian noise.
+        """
         return np.random.randn(x.shape[0], 1)
 
 
 class WhiteNoiseProblemFactory(AbstractProblemFactory):
     def get_setup_information(self) -> ProblemSetupInformation:
+        """
+        Returns the setup information for the problem.
+
+        Returns
+        -------
+        problem_info : ProblemSetupInformation
+            The setup information for the problem.
+        """
         # A mock alphabet made of the 10 digits.
         alphabet = [str(i) for i in range(10)]
 
@@ -60,8 +123,34 @@ def create(
         num_workers: int = None,
         evaluation_budget: int = float("inf"),
     ) -> Tuple[AbstractBlackBox, np.ndarray, np.ndarray]:
-        seed_numpy(seed)
-        seed_python(seed)
+        """
+        Create a white noise black box with the specified parameters.
+
+        Parameters:
+        ----------
+        seed : int, optional
+            The seed value for random number generation. If not provided, no seeding will be performed.
+        batch_size : int, optional
+            The number of samples to evaluate each time. If not provided, the default batch size will be used.
+        parallelize : bool, optional
+            Whether to parallelize the evaluation of samples. Defaults to False.
+        num_workers : int, optional
+            The number of worker processes to use for parallel evaluation. If not provided,
+            half the available CPUs will be used (rounded down).
+        evaluation_budget : int, optional
+            The maximum number of evaluations allowed. Defaults to infinity.
+
+        Returns:
+        -------
+        f : WhiteNoiseBlackBox
+            The white noise black box.
+        x0 : np.ndarray
+            The initial input (an array [["1", "2", "3"]]).
+        y0 : np.ndarray
+            The initial objective function value (an array of shape (1, 1)).
+        """
+        if seed is not None:
+            seed_python_numpy_and_torch(seed)
 
         problem_info = self.get_setup_information()
         f = WhiteNoiseBlackBox(
diff --git a/tox.dev.ini b/tox.ini
similarity index 100%
rename from tox.dev.ini
rename to tox.ini


Test patch:
diff --git a/.github/workflows/python-tox-testing-including-conda.yml b/.github/workflows/python-tox-testing-including-conda.yml
index e5bb748f..0ce7631d 100644
--- a/.github/workflows/python-tox-testing-including-conda.yml
+++ b/.github/workflows/python-tox-testing-including-conda.yml
@@ -23,7 +23,7 @@ jobs:
         python -m pip install tox
     - name: Test linting with tox
       run: |
-        tox -c tox.dev.ini -e lint
+        tox -c tox.ini -e lint
     - name: Test poli-base with tox (ignoring RaSP)
       run: |
-        tox -c tox.dev.ini -e poli-base-py39 -- --ignore=src/poli/tests/registry/proteins/test_rasp.py
+        tox -c tox.ini -e poli-base-py39 -- --ignore=src/poli/tests/registry/proteins/test_rasp.py