Repository: MachineLearningLifeScience/poli (language: python)

Issue:
Include the relevant black boxes from of guacamol



Discussion / hints:

Fix patch:
diff --git a/src/poli/objective_repository/drd3_docking/environment.yml b/src/poli/core/chemistry/environment.yml
similarity index 77%
rename from src/poli/objective_repository/drd3_docking/environment.yml
rename to src/poli/core/chemistry/environment.yml
index 493671ba..ae717d86 100644
--- a/src/poli/objective_repository/drd3_docking/environment.yml
+++ b/src/poli/core/chemistry/environment.yml
@@ -12,13 +12,13 @@ dependencies:
   - openmm
   - pip:
     - "git+https://github.com/MachineLearningLifeScience/poli.git@dev"
-    - biopython==1.81
-    - python-levenshtein==0.12.2
-    - pandas==2.0.3
-    - cachetools==5.3.1
+    - biopython
+    - python-levenshtein
+    - pandas
+    - cachetools
     - rdkit
     - selfies
-    - pytdc
+    - pytdc==0.4.7
     - pyscreener
     - colorama
     - configparse
diff --git a/src/poli/core/chemistry/tdc_black_box.py b/src/poli/core/chemistry/tdc_black_box.py
new file mode 100644
index 00000000..bae62f62
--- /dev/null
+++ b/src/poli/core/chemistry/tdc_black_box.py
@@ -0,0 +1,116 @@
+"""
+Implements an abstract TDC black box for all the PMO [1, 2] related problems.
+
+PMO stands for Practical Molecular Optimization, a benchmark
+suite for molecular optimization problems that extends GuacaMol [3].
+
+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
+[2] Sample Efficiency Matters: A Benchmark for Practical Molecular Optimization
+    Wenhao Gao, Tianfan Fu, Jimeng Sun, Connor W. Coley
+    https://arxiv.org/abs/2206.12411
+[3] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+from poli.core.abstract_black_box import AbstractBlackBox
+
+from poli.core.util.isolation.instancing import instance_function_as_isolated_process
+
+
+class TDCBlackBox(AbstractBlackBox):
+    """
+    An abstract black box for the TDC (Therapeutics Data
+    Commons) problems [1].
+
+    Parameters
+    ----------
+    oracle_name: str
+        The name of the oracle to be used.
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+    **kwargs_for_oracle: dict
+        Other keyword arguments to be passed to the oracle.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, oracle_name, string_representation="SMILES", force_isolation=False, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"), **kwargs_for_oracle
+        Initializes a new instance of the abstract TDC class.
+
+    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
+    """
+
+    def __init__(
+        self,
+        oracle_name: str,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        **kwargs_for_oracle,
+    ):
+        super().__init__(
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+        self.oracle_name = oracle_name
+
+        from_smiles = string_representation.upper() == "SMILES"
+        if not force_isolation:
+            try:
+                from poli.core.chemistry.tdc_isolated_function import (
+                    TDCIsolatedFunction,
+                )
+
+                self.inner_function = TDCIsolatedFunction(
+                    oracle_name=oracle_name,
+                    from_smiles=from_smiles,
+                    **kwargs_for_oracle,
+                )
+            except ImportError:
+                self.inner_function = instance_function_as_isolated_process(
+                    name="tdc__isolated",
+                    oracle_name=oracle_name,
+                    from_smiles=from_smiles,
+                    **kwargs_for_oracle,
+                )
+        else:
+            self.inner_function = instance_function_as_isolated_process(
+                name="tdc__isolated",
+                oracle_name=oracle_name,
+                from_smiles=from_smiles,
+                **kwargs_for_oracle,
+            )
+
+    def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
+        return self.inner_function(x, context)
diff --git a/src/poli/core/chemistry/tdc_isolated_function.py b/src/poli/core/chemistry/tdc_isolated_function.py
index 5b7b2f8b..afc3ca9d 100644
--- a/src/poli/core/chemistry/tdc_isolated_function.py
+++ b/src/poli/core/chemistry/tdc_isolated_function.py
@@ -1,7 +1,7 @@
 """Implement a wrapper around the Therapeutics Data Commons (TDC) oracles [1].
 
-So far, we only support two oracles: `drd3` and `synthetic accessibility`. See
-the documentation on our black-box functions for more details.
+When run, this script registers the TDCIsolatedFunction class as an
+isolated function with the name "tdc__isolated".
 
 References
 ----------
@@ -15,18 +15,15 @@
 
 from tdc import Oracle
 
-from poli.core.abstract_black_box import AbstractBlackBox
 from poli.core.abstract_isolated_function import AbstractIsolatedFunction
-from poli.core.problem_setup_information import ProblemSetupInformation
 
 from poli.core.util.chemistry.string_to_molecule import translate_selfies_to_smiles
 
 
 class TDCIsolatedFunction(AbstractIsolatedFunction):
     """
-    TDCBlackBox is a class that represents a black box for the
-    TDC (Therapeutics Data Commons) problems.
-    It inherits from the AbstractBlackBox class.
+    TDCIsolatedFunction is a class that represents the isolated logic
+    for the TDC (Therapeutics Data Commons) problems.
 
     Parameters
     -----------
@@ -34,13 +31,17 @@ class TDCIsolatedFunction(AbstractIsolatedFunction):
         The name of the oracle used for computing the docking score.
     from_smiles : bool, optional
         Flag indicating whether the input molecules are in SMILES format. Defaults to True.
+    **kwargs_for_oracle : dict, optional
+        Additional keyword arguments passed to the oracle.
 
     Attributes
     ----------
     oracle : Oracle
-        An instance of the Oracle class from TDC.
+        An instance of the Oracle class from TDC. It is instantiated as
+        Oracle(name=oracle_name, **kwargs_for_oracle)
     from_smiles : bool
         Flag indicating whether the input molecules are in SMILES format.
+        False indicates that the input molecules are in SELFIES format.
     """
 
     def __init__(
@@ -56,14 +57,6 @@ def __init__(
         ----------
         oracle_name : str
             The name of the oracle.
-        info : ProblemSetupInformation
-            The problem setup information.
-        batch_size : int, optional
-            The batch size, by default None.
-        parallelize : bool, optional
-            Whether to parallelize the computation, by default False.
-        num_workers : int, optional
-            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
@@ -106,3 +99,13 @@ def __call__(self, x, context=None):
             scores.append(self.oracle(molecule_string))
 
         return np.array(scores).reshape(-1, 1)
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_isolated_function
+
+    register_isolated_function(
+        TDCIsolatedFunction,
+        name="tdc__isolated",
+        conda_environment_name="poli__tdc",
+    )
diff --git a/src/poli/core/util/isolation/instancing.py b/src/poli/core/util/isolation/instancing.py
index 66ff8db6..f598d881 100644
--- a/src/poli/core/util/isolation/instancing.py
+++ b/src/poli/core/util/isolation/instancing.py
@@ -1,20 +1,15 @@
 from pathlib import Path
 import configparser
 import subprocess
-import warnings
 
 import logging
 from poli.core.registry import (
-    _DEFAULT,
     _OBSERVER,
-    _RUN_SCRIPT_LOCATION,
     _ISOLATED_FUNCTION_SCRIPT_LOCATION,
 )
 
-# from poli.objective_repository import AVAILABLE_OBJECTIVES
 from poli.core.util.inter_process_communication.process_wrapper import ProcessWrapper
 
-from .external_black_box import ExternalBlackBox
 from .external_function import ExternalFunction
 
 HOME_DIR = Path.home().resolve()
@@ -42,7 +37,9 @@ def load_config():
     return config
 
 
-def __register_isolated_function_from_repository(name: str, quiet: bool = False):
+def __register_isolated_function_from_repository(
+    name: str, quiet: bool = False
+) -> None:
     """Registers a problem from the repository.
 
     This function takes a problem name, and registers it. The problem name
@@ -61,13 +58,6 @@ def __register_isolated_function_from_repository(name: str, quiet: bool = False)
         If True, we squelch the feedback about environment creation and
         problem registration, by default False.
     """
-    # the name is actually the folder inside
-    # poli/objective_repository, so we need
-    # to
-    # 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)
     assert name.endswith(
         "__isolated"
     ), "By convention, the names of isolated functions always end with '__isolated'"
@@ -77,10 +67,62 @@ def __register_isolated_function_from_repository(name: str, quiet: bool = False)
         Path(__file__).parent.parent.parent.parent / "objective_repository"
     ).resolve()
 
-    file_to_isolate = "isolated_function.py"
     name_without_isolated = name.replace("__isolated", "")
+    environment_file = PATH_TO_REPOSITORY / name_without_isolated / "environment.yml"
+    isolated_file = PATH_TO_REPOSITORY / name_without_isolated / "isolated_function.py"
+
+    __register_isolated_function(
+        environment_file=environment_file,
+        isolated_file=isolated_file,
+        name_for_show=name,
+        quiet=quiet,
+    )
+
+
+def __register_isolated_function_from_core(name: str, quiet: bool = False) -> None:
+    """
+    Registers an isolated function from the core package.
+
+    At the moment, we only have one isolated function
+    available in the core package, which is the TDCIsolatedFunction.
+
+    Parameters
+    ----------
+    name : str
+        The name of the isolated function to register. At the moment,
+        the only available isolated function is "tdc__isolated".
+    quiet : bool, optional
+        If True, we squelch the feedback about environment creation and
+        problem registration, by default False.
+    """
+    ROOT_DIR_OF_POLI_PACKAGE = Path(__file__).parent.parent.parent.parent
+    if name == "tdc__isolated":
+        environment_file = (
+            ROOT_DIR_OF_POLI_PACKAGE / "core" / "chemistry" / "environment.yml"
+        )
+        isolated_file = (
+            ROOT_DIR_OF_POLI_PACKAGE / "core" / "chemistry" / "tdc_isolated_function.py"
+        )
+        __register_isolated_function(
+            environment_file=environment_file,
+            isolated_file=isolated_file,
+            name_for_show="TDC isolated function",
+            quiet=quiet,
+        )
+    else:
+        raise NotImplementedError(
+            "The only core isolated function available is the "
+            "TDCIsolatedFunction (i.e. tdc__isolated)."
+        )
+
 
-    with open(PATH_TO_REPOSITORY / name_without_isolated / "environment.yml", "r") as f:
+def __register_isolated_function(
+    environment_file: Path,
+    isolated_file: Path,
+    name_for_show: str = None,
+    quiet: bool = False,
+):
+    with open(environment_file, "r") as f:
         # This is a really crude way of doing this,
         # but it works. We should probably use a
         # yaml parser instead, but the idea is to keep
@@ -94,30 +136,12 @@ def __register_isolated_function_from_repository(name: str, quiet: bool = False)
         )
         env_name = lines[0].split(":")[1].strip()
 
-    # Moreover, we should only be doing this
-    # if the problem is not already registered.
-    # TODO: do we?
-    # if name in config.sections():
-
-    #     warnings.warn(f"Problem {name} already registered. Skipping")
-    #     return
-
     # 1. create the environment from the yaml file
     if not quiet:
-        print(
-            f"poli 🧪: creating environment {env_name} from {name_without_isolated}/environment.yml"
-        )
+        print(f"poli 🧪: creating environment {env_name} from {environment_file}")
     try:
         subprocess.run(
-            " ".join(
-                [
-                    "conda",
-                    "env",
-                    "create",
-                    "-f",
-                    str(PATH_TO_REPOSITORY / name_without_isolated / "environment.yml"),
-                ]
-            ),
+            " ".join(["conda", "env", "create", "-f", str(environment_file)]),
             shell=True,
             check=True,
             capture_output=True,
@@ -126,7 +150,6 @@ def __register_isolated_function_from_repository(name: str, quiet: bool = False)
         if "already exists" in e.stderr.decode():
             if not quiet:
                 print(f"poli 🧪: {env_name} already exists.")
-            # warnings.warn(f"Environment {env_name} already exists. Will not create it.")
         else:
             raise e
 
@@ -135,19 +158,23 @@ def __register_isolated_function_from_repository(name: str, quiet: bool = False)
     #    dependencies that are not installed)
 
     # Running the file
-    file_to_run = PATH_TO_REPOSITORY / name_without_isolated / file_to_isolate
-    command = " ".join(["conda", "run", "-n", env_name, "python", str(file_to_run)])
+    command = " ".join(["conda", "run", "-n", env_name, "python", str(isolated_file)])
     # warnings.warn("Running the following command: %s. " % command)
 
     if not quiet:
-        print(f"poli 🧪: running registration of {name} from environment {env_name}")
+        if name_for_show:
+            print(
+                f"poli 🧪: running registration of {name_for_show} from environment {env_name}"
+            )
+        else:
+            print(f"poli 🧪: running {isolated_file} from environment {env_name}")
     try:
         subprocess.run(command, check=True, shell=True, capture_output=True)
     except subprocess.CalledProcessError as e:
         raise RuntimeError(
-            f"Found error when running {file_to_run} from environment {env_name}: \n"
+            f"Found error when running {isolated_file} from environment {env_name}: \n"
             f"{e.stderr.decode()}"
-        )
+        ) from e
 
 
 def register_isolated_function_if_available(
@@ -167,7 +194,9 @@ def register_isolated_function_if_available(
         the folder name inside the objective repository. If the
         name contains a `__isolated`, then it is assumed
         that the name refers to an internal file called
-        `isolated_function.py`.
+        `isolated_function.py`. An exception to this
+        is "tdc__isolated", which registers the
+        TDCIsolatedFunction.
     force_register : bool, optional
         If True, then the objective function is registered without asking
         for confirmation, overwriting any previous registration. By default,
@@ -197,16 +226,25 @@ def register_isolated_function_if_available(
                 "want to install it? (y/[n]): "
             )
 
-        if answer == "y":
-            # Register problem
-            logging.debug(f"poli 🧪: Registered the black box from the repository.")
-            __register_isolated_function_from_repository(name, quiet=quiet)
-            # Refresh the config
+        if answer != "y":
+            raise ValueError(
+                f"Objective function '{name}' is not registered. Aborting."
+            )
+
+        # Register problem
+        if name == "tdc__isolated":
+            logging.debug(
+                f"poli 🧪: Registered the isolated function from the repository."
+            )
+            __register_isolated_function_from_core(name, quiet=quiet)
             config = load_config()
         else:
-            raise ValueError(
-                f"Objective function '{name}' won't be registered. Aborting."
+            logging.debug(
+                f"poli 🧪: Registered the isolated function from the repository."
             )
+            __register_isolated_function_from_repository(name, quiet=quiet)
+            # Refresh the config
+            config = load_config()
 
 
 def __create_function_as_isolated_process(
diff --git a/src/poli/objective_repository/__init__.py b/src/poli/objective_repository/__init__.py
index 79673b6d..e6ba21c8 100644
--- a/src/poli/objective_repository/__init__.py
+++ b/src/poli/objective_repository/__init__.py
@@ -3,15 +3,101 @@
 
 from pathlib import Path
 
-# These can be imported from the base environment.
+# Toy examples
 from .white_noise.register import WhiteNoiseProblemFactory, WhiteNoiseBlackBox
 from .aloha.register import AlohaProblemFactory, AlohaBlackBox
 from .toy_continuous_problem.register import (
     ToyContinuousProblemFactory,
     ToyContinuousBlackBox,
 )
+
+# Chemistry black boxes
 from .dockstring.register import DockstringProblemFactory, DockstringBlackBox
 from .drd3_docking.register import DRD3ProblemFactory, DRD3BlackBox
+from .sa_tdc.register import SAProblemFactory, SABlackBox
+from .rdkit_logp.register import LogPBlackBox, LogPProblemFactory
+from .rdkit_qed.register import QEDBlackBox, QEDProblemFactory
+from .gsk3_beta.register import GSK3BetaBlackBox, GSK3BetaProblemFactory
+from .drd2_docking.register import DRD2BlackBox, DRD2ProblemFactory
+from .jnk3.register import JNK3BlackBox, JNK3ProblemFactory
+from .celecoxib_rediscovery.register import (
+    CelecoxibRediscoveryBlackBox,
+    CelecoxibRediscoveryProblemFactory,
+)
+from .thiothixene_rediscovery.register import (
+    ThiothixeneRediscoveryBlackBox,
+    ThiothixeneRediscoveryProblemFactory,
+)
+from .troglitazone_rediscovery.register import (
+    TroglitazoneRediscoveryBlackBox,
+    TroglitazoneRediscoveryProblemFactory,
+)
+from .albuterol_similarity.register import (
+    AlbuterolSimilarityBlackBox,
+    AlbuterolSimilarityProblemFactory,
+)
+from .mestranol_similarity.register import (
+    MestranolSimilarityBlackBox,
+    MestranolSimilarityProblemFactory,
+)
+from .amlodipine_mpo.register import (
+    AmlodipineMPOProblemFactory,
+    AmlodipineMPOBlackBox,
+)
+from .fexofenadine_mpo.register import (
+    FexofenadineMPOProblemFactory,
+    FexofenadineMPOBlackBox,
+)
+from .osimetrinib_mpo.register import (
+    OsimetrinibMPOProblemFactory,
+    OsimetrinibMPOBlackBox,
+)
+from .perindopril_mpo.register import (
+    PerindoprilMPOProblemFactory,
+    PerindoprilMPOBlackBox,
+)
+from .ranolazine_mpo.register import (
+    RanolazineMPOProblemFactory,
+    RanolazineMPOBlackBox,
+)
+from .sitagliptin_mpo.register import (
+    SitagliptinMPOProblemFactory,
+    SitagliptinMPOBlackBox,
+)
+from .zaleplon_mpo.register import (
+    ZaleplonMPOProblemFactory,
+    ZaleplonMPOBlackBox,
+)
+from .deco_hop.register import (
+    DecoHopProblemFactory,
+    DecoHopBlackBox,
+)
+from .scaffold_hop.register import (
+    ScaffoldHopProblemFactory,
+    ScaffoldHopBlackBox,
+)
+from .isomer_c7h8n2o2.register import (
+    IsomerC7H8N2O2ProblemFactory,
+    IsomerC7H8N2O2BlackBox,
+)
+from .isomer_c9h10n2o2pf2cl.register import (
+    IsomerC9H10N2O2PF2ClProblemFactory,
+    IsomerC9H10N2O2PF2ClBlackBox,
+)
+from .median_1.register import (
+    Median1ProblemFactory,
+    Median1BlackBox,
+)
+from .median_2.register import (
+    Median2ProblemFactory,
+    Median2BlackBox,
+)
+from .valsartan_smarts.register import (
+    ValsartanSMARTSProblemFactory,
+    ValsartanSMARTSBlackBox,
+)
+
+# Protein black boxes
 from .foldx_rfp_lambo.register import FoldXRFPLamboBlackBox, FoldXRFPLamboProblemFactory
 from .foldx_sasa.register import FoldXSASABlackBox, FoldXSASAProblemFactory
 from .foldx_stability.register import (
@@ -29,12 +115,11 @@
     PenalizedLogPLamboProblemFactory,
 )
 from .rasp.register import RaspBlackBox, RaspProblemFactory
-from .rdkit_logp.register import LogPBlackBox, LogPProblemFactory
-from .rdkit_qed.register import QEDBlackBox, QEDProblemFactory
 from .rfp_foldx_stability_and_sasa.register import (
     RFPFoldXStabilityAndSASAProblemFactory,
 )
-from .sa_tdc.register import SAProblemFactory, SABlackBox
+
+# Other
 from .super_mario_bros.register import (
     SuperMarioBrosProblemFactory,
     SuperMarioBrosBlackBox,
@@ -74,6 +159,28 @@
     "super_mario_bros": SuperMarioBrosProblemFactory,
     "white_noise": WhiteNoiseProblemFactory,
     "toy_continuous_problem": ToyContinuousProblemFactory,
+    "gsk3_beta": GSK3BetaProblemFactory,
+    "drd2_docking": DRD2ProblemFactory,
+    "jnk3": JNK3ProblemFactory,
+    "celecoxib_rediscovery": CelecoxibRediscoveryProblemFactory,
+    "thiothixene_rediscovery": ThiothixeneRediscoveryProblemFactory,
+    "troglitazone_rediscovery": TroglitazoneRediscoveryProblemFactory,
+    "albuterol_similarity": AlbuterolSimilarityProblemFactory,
+    "mestranol_similarity": MestranolSimilarityProblemFactory,
+    "amlodipine_mpo": AmlodipineMPOProblemFactory,
+    "fexofenadine_mpo": FexofenadineMPOProblemFactory,
+    "osimetrinib_mpo": OsimetrinibMPOProblemFactory,
+    "perindopril_mpo": PerindoprilMPOProblemFactory,
+    "ranolazine_mpo": RanolazineMPOProblemFactory,
+    "sitagliptin_mpo": SitagliptinMPOProblemFactory,
+    "zaleplon_mpo": ZaleplonMPOProblemFactory,
+    "deco_hop": DecoHopProblemFactory,
+    "scaffold_hop": ScaffoldHopProblemFactory,
+    "isomer_c7h8n2o2": IsomerC7H8N2O2ProblemFactory,
+    "isomer_c9h10n2o2pf2cl": IsomerC9H10N2O2PF2ClProblemFactory,
+    "median_1": Median1ProblemFactory,
+    "median_2": Median2ProblemFactory,
+    "valsartan_smarts": ValsartanSMARTSProblemFactory,
 }
 
 AVAILABLE_BLACK_BOXES = {
@@ -95,6 +202,28 @@
     "super_mario_bros": SuperMarioBrosBlackBox,
     "white_noise": WhiteNoiseBlackBox,
     "toy_continuous_problem": ToyContinuousBlackBox,
+    "gsk3_beta": GSK3BetaBlackBox,
+    "drd2_docking": DRD2BlackBox,
+    "jnk3": JNK3BlackBox,
+    "celecoxib_rediscovery": CelecoxibRediscoveryBlackBox,
+    "thiothixene_rediscovery": ThiothixeneRediscoveryBlackBox,
+    "troglitazone_rediscovery": TroglitazoneRediscoveryBlackBox,
+    "albuterol_similarity": AlbuterolSimilarityBlackBox,
+    "mestranol_similarity": MestranolSimilarityBlackBox,
+    "amlodipine_mpo": AmlodipineMPOBlackBox,
+    "fexofenadine_mpo": FexofenadineMPOBlackBox,
+    "osimetrinib_mpo": OsimetrinibMPOBlackBox,
+    "perindopril_mpo": PerindoprilMPOBlackBox,
+    "ranolazine_mpo": RanolazineMPOBlackBox,
+    "sitagliptin_mpo": SitagliptinMPOBlackBox,
+    "zaleplon_mpo": ZaleplonMPOBlackBox,
+    "deco_hop": DecoHopBlackBox,
+    "scaffold_hop": ScaffoldHopBlackBox,
+    "isomer_c7h8n2o2": IsomerC7H8N2O2BlackBox,
+    "isomer_c9h10n2o2pf2cl": IsomerC9H10N2O2PF2ClBlackBox,
+    "median_1": Median1BlackBox,
+    "median_2": Median2BlackBox,
+    "valsartan_smarts": ValsartanSMARTSBlackBox,
 }
 
 
diff --git a/src/poli/objective_repository/albuterol_similarity/__init__.py b/src/poli/objective_repository/albuterol_similarity/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/albuterol_similarity/information.py b/src/poli/objective_repository/albuterol_similarity/information.py
new file mode 100644
index 00000000..cb7e998b
--- /dev/null
+++ b/src/poli/objective_repository/albuterol_similarity/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+albuterol_similarity_info = BlackBoxInformation(
+    name="albuterol_similarity",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/albuterol_similarity/register.py b/src/poli/objective_repository/albuterol_similarity/register.py
new file mode 100644
index 00000000..d6a671f6
--- /dev/null
+++ b/src/poli/objective_repository/albuterol_similarity/register.py
@@ -0,0 +1,232 @@
+"""
+Implements the albuterol similarity task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2], and consists of
+measuring the similarity of molecules (usually provided as SMILES or SELFIES
+strings) to albuterol, a medicine used to treat breathing difficulty among
+other symptoms.
+
+We recommend you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.albuterol_similarity.information import (
+    albuterol_similarity_info,
+)
+
+
+class AlbuterolSimilarityBlackBox(TDCBlackBox):
+    """
+    A black box that measures the similarities of molecules to
+    albuterol, implementation using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2], and consists of
+    rediscovering a certain molecule through optimization. We recommend you
+    cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes an instance of the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Albuterol_Similarity",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return albuterol_similarity_info
+
+
+class AlbuterolSimilarityProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Albuterol Similarity problems.
+
+    We recommend you cite [1, 2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Albuterol Similarity problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return albuterol_similarity_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates an Albuterol similarity problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = AlbuterolSimilarityBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        albuterol_similarity_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return albuterol_similarity_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        AlbuterolSimilarityProblemFactory(),
+        name="albuterol_similarity",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/amlodipine_mpo/__init__.py b/src/poli/objective_repository/amlodipine_mpo/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/amlodipine_mpo/information.py b/src/poli/objective_repository/amlodipine_mpo/information.py
new file mode 100644
index 00000000..127a9d89
--- /dev/null
+++ b/src/poli/objective_repository/amlodipine_mpo/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+amlodipine_mpo_info = BlackBoxInformation(
+    name="amlodipine_mpo",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/amlodipine_mpo/register.py b/src/poli/objective_repository/amlodipine_mpo/register.py
new file mode 100644
index 00000000..94292c6f
--- /dev/null
+++ b/src/poli/objective_repository/amlodipine_mpo/register.py
@@ -0,0 +1,229 @@
+"""
+Implements the Amlodipine MPO task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2]. We recommend
+you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.amlodipine_mpo.information import (
+    amlodipine_mpo_info,
+)
+
+
+class AmlodipineMPOBlackBox(TDCBlackBox):
+    """
+    A black box that computes a molecule's score for Amlodipine MPO,
+    implemented using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2]. Check it
+    for more details on what MPO measures. We recommend citing [1, 2]
+    when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Amlodipine_MPO",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return amlodipine_mpo_info
+
+
+class AmlodipineMPOProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Amlodipine MPO problems.
+
+    This task is inherited from the GuacaMol benchmark [2]. Check it
+    for more details on what MPO measures. We recommend citing [1, 2]
+    when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an AmlodipineMPO problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return amlodipine_mpo_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Amlodipine MPO problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = AmlodipineMPOBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        amlodipine_mpo_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return amlodipine_mpo_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        AmlodipineMPOProblemFactory(),
+        name="amlodipine_mpo",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/celecoxib_rediscovery/__init__.py b/src/poli/objective_repository/celecoxib_rediscovery/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/celecoxib_rediscovery/information.py b/src/poli/objective_repository/celecoxib_rediscovery/information.py
new file mode 100644
index 00000000..0f834c69
--- /dev/null
+++ b/src/poli/objective_repository/celecoxib_rediscovery/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+celecoxib_rediscovery_info = BlackBoxInformation(
+    name="celecoxib_rediscovery",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/celecoxib_rediscovery/register.py b/src/poli/objective_repository/celecoxib_rediscovery/register.py
new file mode 100644
index 00000000..aeed19df
--- /dev/null
+++ b/src/poli/objective_repository/celecoxib_rediscovery/register.py
@@ -0,0 +1,231 @@
+"""
+Implements the Celecoxib rediscovery task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2], and consists of
+rediscovering Celecoxib through optimization. We recommend you cite both
+references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.celecoxib_rediscovery.information import (
+    celecoxib_rediscovery_info,
+)
+
+
+class CelecoxibRediscoveryBlackBox(TDCBlackBox):
+    """
+    Celecoxib rediscovery black box implementation using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2], and consists of
+    rediscovering Celecoxib through optimization. We recommend you cite both
+    references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Celecoxib_Rediscovery",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return celecoxib_rediscovery_info
+
+
+class CelecoxibRediscoveryProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Celecoxib rediscovery problems.
+
+    In this task, the optimization algorithm must find a specific
+    molecule (Celecoxib). See [2] for more details. We recommend you cite
+    both references when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates a Celecoxib rediscovery problem.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return celecoxib_rediscovery_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Celecoxib rediscovery problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = CelecoxibRediscoveryBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        celecoxib_rediscovery_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return celecoxib_rediscovery_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        CelecoxibRediscoveryProblemFactory(),
+        name="celecoxib_rediscovery",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/deco_hop/__init__.py b/src/poli/objective_repository/deco_hop/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/deco_hop/information.py b/src/poli/objective_repository/deco_hop/information.py
new file mode 100644
index 00000000..44155cf9
--- /dev/null
+++ b/src/poli/objective_repository/deco_hop/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+deco_hop_info = BlackBoxInformation(
+    name="deco_hop",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/deco_hop/register.py b/src/poli/objective_repository/deco_hop/register.py
new file mode 100644
index 00000000..82df8285
--- /dev/null
+++ b/src/poli/objective_repository/deco_hop/register.py
@@ -0,0 +1,228 @@
+"""
+Implements the Decorator HOP task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2], and
+consists of maximizing the similarity to a certain SMILES,
+"while excluding specific SMARTS patterns" [1]. We recommend
+you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.deco_hop.information import deco_hop_info
+
+
+class DecoHopBlackBox(TDCBlackBox):
+    """
+    The "Deco HOP" oracle from TDC [1].
+
+    This task is inherited from the GuacaMol benchmark [2], and
+    consists of maximizing the similarity to a certain SMILES,
+    "while excluding specific SMARTS patterns" [1]. We recommend
+    you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Creates a DecoHopBlackBox instance.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Deco Hop",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return deco_hop_info
+
+
+class DecoHopProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Decorator Hop problems.
+
+    We recommend you cite [1, 2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Decorator Hop problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return deco_hop_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Decorator Hop problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = DecoHopBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        deco_hop_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return deco_hop_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        DecoHopProblemFactory(),
+        name="deco_hop",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/drd2_docking/__init__.py b/src/poli/objective_repository/drd2_docking/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/drd2_docking/information.py b/src/poli/objective_repository/drd2_docking/information.py
new file mode 100644
index 00000000..43680cbf
--- /dev/null
+++ b/src/poli/objective_repository/drd2_docking/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+drd2_docking_info = BlackBoxInformation(
+    name="drd2_docking",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/drd2_docking/register.py b/src/poli/objective_repository/drd2_docking/register.py
new file mode 100644
index 00000000..400cc3b1
--- /dev/null
+++ b/src/poli/objective_repository/drd2_docking/register.py
@@ -0,0 +1,227 @@
+"""
+Implements the DRD2 docking task using the TDC oracles [1].
+
+In this task, the classifier of Olivecrona et al. [2] is used
+to predict the "bioactivity" of a molecule to the dopamine
+type 2 receptor. We recommend you cite both references when
+using this task.
+
+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
+[2] Molecular de-novo design through deep reinforcement learning.
+    Olivecrona, M. et al. (2017).
+    Journal of cheminformatics, 9:48.
+    https://jcheminf.biomedcentral.com/articles/10.1186/s13321-017-0235-x
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.drd2_docking.information import drd2_docking_info
+
+
+class DRD2BlackBox(TDCBlackBox):
+    """
+    Docking to the dopamine type 2 receptor, using TDC [1] (which in
+    turn uses Olivecrona et al.'s classifier [2])
+
+    We recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] Molecular de-novo design through deep reinforcement learning.
+        Olivecrona, M. et al. (2017).
+        Journal of cheminformatics, 9:48.
+        https://jcheminf.biomedcentral.com/articles/10.1186/s13321-017-0235-x
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="DRD2",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return drd2_docking_info
+
+
+class DRD2ProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating DRD2 docking problems.
+
+    We recommend you cite [1, 2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates a DRD2 docking problem.
+
+    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
+    [2] Molecular de-novo design through deep reinforcement learning.
+        Olivecrona, M. et al. (2017).
+        Journal of cheminformatics, 9:48.
+        https://jcheminf.biomedcentral.com/articles/10.1186/s13321-017-0235-x
+
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return drd2_docking_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates DRD2 problem instances.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = DRD2BlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        drd2_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return drd2_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        DRD2ProblemFactory(),
+        name="drd2_docking",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/drd3_docking/isolated_function.py b/src/poli/objective_repository/drd3_docking/isolated_function.py
deleted file mode 100644
index 8d0ae286..00000000
--- a/src/poli/objective_repository/drd3_docking/isolated_function.py
+++ /dev/null
@@ -1,41 +0,0 @@
-from poli.core.chemistry.tdc_isolated_function import TDCIsolatedFunction
-from poli.core.registry import register_isolated_function
-
-
-class DRD3IsolatedFunction(TDCIsolatedFunction):
-    """
-    DRD3BlackBox is a class that represents a black box for DRD3 docking.
-
-    Parameters
-    ----------
-    from_smiles : bool, optional
-        Flag indicating whether to use SMILES strings as input, by default True.
-
-    Attributes
-    ----------
-    oracle_name : str
-        The name of the oracle.
-
-    Methods
-    -------
-    __init__(self, info, batch_size=None, parallelize=False, num_workers=None, from_smiles=True)
-        Initializes a new instance of the DRD3BlackBox class.
-    """
-
-    def __init__(
-        self,
-        from_smiles: bool = True,
-    ):
-        oracle_name = "3pbl_docking"
-        super().__init__(
-            oracle_name=oracle_name,
-            from_smiles=from_smiles,
-        )
-
-
-if __name__ == "__main__":
-    register_isolated_function(
-        DRD3IsolatedFunction,
-        name="drd3_docking__isolated",
-        conda_environment_name="poli__tdc",
-    )
diff --git a/src/poli/objective_repository/drd3_docking/readme.md b/src/poli/objective_repository/drd3_docking/readme.md
index 40d67dea..4a492948 100644
--- a/src/poli/objective_repository/drd3_docking/readme.md
+++ b/src/poli/objective_repository/drd3_docking/readme.md
@@ -3,8 +3,7 @@
 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__tdc` environment created.
+- have the `prepare_receptor` binary from the ADFR suite installed and in the path.
 
 The rest of this readme shows you how to do all this:
 
@@ -44,13 +43,11 @@ Thus, we recommend creating a symlink. Write this in your `~/.bashrc` or `~/.zsh
 ln -sf /path/to/ADFR/bin/prepare_receptor /path/to/AutoDock_vina/bin
 ```
 
-## Create the `poli__tdc` environment
+## Where is the `poli__tdc` environment?
 
-### Create the environment from the yml file
-
-This can easily be done by running
+If you want to manually create the `poli__tdc` environment, you can run
 
 ```bash
 # From the base of the poli repo
-conda env create --file src/poli/objective_repository/drd3_docking/environment.yml
+conda env create --file src/poli/core/chemistry/environment.yml
 ```
diff --git a/src/poli/objective_repository/drd3_docking/register.py b/src/poli/objective_repository/drd3_docking/register.py
index 25b5a75a..8cfc2e01 100644
--- a/src/poli/objective_repository/drd3_docking/register.py
+++ b/src/poli/objective_repository/drd3_docking/register.py
@@ -1,5 +1,5 @@
 """
-Implements the DDR3 docking task using the TDC oracles [1].
+Implements the DRD3 docking task using the TDC oracles [1].
 
 References
 ----------
@@ -16,7 +16,7 @@
 
 from poli.core.abstract_problem_factory import AbstractProblemFactory
 from poli.core.black_box_information import BlackBoxInformation
-from poli.core.abstract_black_box import AbstractBlackBox
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
 from poli.core.problem import Problem
 
 from poli.core.util.isolation.instancing import instance_function_as_isolated_process
@@ -28,7 +28,7 @@
 from poli.objective_repository.drd3_docking.information import drd3_docking_info
 
 
-class DRD3BlackBox(AbstractBlackBox):
+class DRD3BlackBox(TDCBlackBox):
     """
     DRD3BlackBox is a class that represents a black box for DRD3 docking.
 
@@ -69,41 +69,19 @@ def __init__(
         evaluation_budget: int = float("inf"),
     ):
         super().__init__(
+            oracle_name="3pbl_docking",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
             batch_size=batch_size,
             parallelize=parallelize,
             num_workers=num_workers,
             evaluation_budget=evaluation_budget,
         )
 
-        from_smiles = string_representation.upper() == "SMILES"
-        if not force_isolation:
-            try:
-                from poli.objective_repository.drd3_docking.isolated_function import (
-                    TDCIsolatedFunction,
-                )
-
-                self.inner_function = TDCIsolatedFunction(
-                    oracle_name="3pbl_docking",
-                    from_smiles=from_smiles,
-                )
-            except ImportError:
-                self.inner_function = instance_function_as_isolated_process(
-                    name="drd3_docking__isolated",
-                    from_smiles=from_smiles,
-                )
-        else:
-            self.inner_function = instance_function_as_isolated_process(
-                name="drd3_docking__isolated",
-                from_smiles=from_smiles,
-            )
-
     @staticmethod
     def get_black_box_info() -> BlackBoxInformation:
         return drd3_docking_info
 
-    def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
-        return self.inner_function(x, context)
-
 
 class DRD3ProblemFactory(AbstractProblemFactory):
     """
diff --git a/src/poli/objective_repository/fexofenadine_mpo/__init__.py b/src/poli/objective_repository/fexofenadine_mpo/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/fexofenadine_mpo/information.py b/src/poli/objective_repository/fexofenadine_mpo/information.py
new file mode 100644
index 00000000..b0938290
--- /dev/null
+++ b/src/poli/objective_repository/fexofenadine_mpo/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+fexofenadine_mpo_info = BlackBoxInformation(
+    name="fexofenadine_mpo",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/fexofenadine_mpo/register.py b/src/poli/objective_repository/fexofenadine_mpo/register.py
new file mode 100644
index 00000000..5b40ca3d
--- /dev/null
+++ b/src/poli/objective_repository/fexofenadine_mpo/register.py
@@ -0,0 +1,225 @@
+"""
+Implements the Fexofenadine MPO task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2]. We recommend
+you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.fexofenadine_mpo.information import fexofenadine_mpo_info
+
+
+class FexofenadineMPOBlackBox(TDCBlackBox):
+    """
+    A black box that gives access to the Fexofenadine oracle,
+    implementation using the TDC [1].
+
+    This task is inherited from the GuacaMol benchmark [2]. We recommend
+    you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Fexofenadine_MPO",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return fexofenadine_mpo_info
+
+
+class FexofenadineMPOProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Fexofenadine MPO problems.
+
+    We recommend you cite [1, 2] when using this problem factory.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Fexofenadine MPO problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return fexofenadine_mpo_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Fexofenadine MPO problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = FexofenadineMPOBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        fexofenadine_mpo_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return fexofenadine_mpo_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        FexofenadineMPOProblemFactory(),
+        name="fexofenadine_mpo",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/gsk3_beta/__init__.py b/src/poli/objective_repository/gsk3_beta/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/gsk3_beta/information.py b/src/poli/objective_repository/gsk3_beta/information.py
new file mode 100644
index 00000000..b4216941
--- /dev/null
+++ b/src/poli/objective_repository/gsk3_beta/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+gsk3_beta_info = BlackBoxInformation(
+    name="gsk3_beta",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/gsk3_beta/register.py b/src/poli/objective_repository/gsk3_beta/register.py
new file mode 100644
index 00000000..15efc282
--- /dev/null
+++ b/src/poli/objective_repository/gsk3_beta/register.py
@@ -0,0 +1,243 @@
+"""
+Implements the Glycogen Synthase Kinase 3 Beta (GSK3Beta) task
+using the TDC oracles [1].
+
+If you use this black box, we recommend citing [1, 2, 3, 4].
+
+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
+[2] Multi-objective de novo drug design with conditional graph generative models.
+    Li, Y., Zhang, L., Liu, Z.
+    Journal of cheminformatics 10.1 (2018).
+[3] Multi-objective molecule generation using interpretable substructures.
+    Jin, Wengong, Regina Barzilay, and Tommi Jaakkola.
+    ICML. 2020.
+    https://people.csail.mit.edu/tommi/papers/JBJ_ICML2020b.pdf
+[4] ExCAPE-DB: an integrated large scale dataset facilitating Big Data analysis in chemogenomics.
+    Jiangming, Sun, et al.
+    Journal of cheminformatics 9.1 (2017).
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.objective_repository.gsk3_beta.information import gsk3_beta_info
+
+
+class GSK3BetaBlackBox(TDCBlackBox):
+    """
+    A black box for the Glycogen Synthase Kinase 3 Beta (GSK3Beta) task,
+    using the Therapeutics Data Commons' oracles [1].
+
+    If you use this black box, we recommend citing [1, 2, 3, 4].
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, info, batch_size=None, parallelize=False, num_workers=None, from_smiles=True)
+        Initializes a new instance of the DRD3BlackBox class.
+
+    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
+    [2] Multi-objective de novo drug design with conditional graph generative models.
+        Li, Y., Zhang, L., Liu, Z.
+        Journal of cheminformatics 10.1 (2018).
+    [3] Multi-objective molecule generation using interpretable substructures.
+        Jin, Wengong, Regina Barzilay, and Tommi Jaakkola.
+        ICML. 2020.
+        https://people.csail.mit.edu/tommi/papers/JBJ_ICML2020b.pdf
+    [4] ExCAPE-DB: an integrated large scale dataset facilitating Big Data analysis in chemogenomics.
+        Jiangming, Sun, et al.
+        Journal of cheminformatics 9.1 (2017).
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="GSK3B",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return gsk3_beta_info
+
+
+class GSK3BetaProblemFactory(AbstractProblemFactory):
+    """
+    A factory for creating Glycogen synthase kinase 3 beta problems.
+
+    If you use this problem factory, we recommend you cite [1, 2, 3, 4].
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates a DRD3 docking problem.
+
+    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
+    [2] Multi-objective de novo drug design with conditional graph generative models.
+        Li, Y., Zhang, L., Liu, Z.
+        Journal of cheminformatics 10.1 (2018).
+    [3] Multi-objective molecule generation using interpretable substructures.
+        Jin, Wengong, Regina Barzilay, and Tommi Jaakkola.
+        ICML. 2020.
+        https://people.csail.mit.edu/tommi/papers/JBJ_ICML2020b.pdf
+    [4] ExCAPE-DB: an integrated large scale dataset facilitating Big Data analysis in chemogenomics.
+        Jiangming, Sun, et al.
+        Journal of cheminformatics 9.1 (2017).
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return gsk3_beta_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates GSK3B problems.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = GSK3BetaBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        gsk3_beta_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return gsk3_beta_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        GSK3BetaProblemFactory(),
+        name="gs3_beta",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/isomer_c7h8n2o2/__init__.py b/src/poli/objective_repository/isomer_c7h8n2o2/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/isomer_c7h8n2o2/information.py b/src/poli/objective_repository/isomer_c7h8n2o2/information.py
new file mode 100644
index 00000000..dfe5c6dc
--- /dev/null
+++ b/src/poli/objective_repository/isomer_c7h8n2o2/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+isomer_c7h8n2o2_info = BlackBoxInformation(
+    name="isomer_c7h8n2o2",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/isomer_c7h8n2o2/register.py b/src/poli/objective_repository/isomer_c7h8n2o2/register.py
new file mode 100644
index 00000000..9c72373b
--- /dev/null
+++ b/src/poli/objective_repository/isomer_c7h8n2o2/register.py
@@ -0,0 +1,225 @@
+"""
+Implements the Isomer C7H8N2O2 task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2]. We recommend
+you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.isomer_c7h8n2o2.information import isomer_c7h8n2o2_info
+
+
+class IsomerC7H8N2O2BlackBox(TDCBlackBox):
+    """
+    A black box that measures the similarities of a certain
+    SMILES string's Isomer presentation to C7H8N2O2, implemented
+    using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2]. We
+    recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation=False, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float('inf'))
+        Initializes a new instance of the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Isomers_C7H8N2O2",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return isomer_c7h8n2o2_info
+
+
+class IsomerC7H8N2O2ProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Isomer C7H8N2O2 problems.
+
+    This task is inherited from the GuacaMol benchmark [2]. We
+    recommend you cite [1, 2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Isomer C7H8N2O2 problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return isomer_c7h8n2o2_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates an Isomer C7H8N2O2 problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = IsomerC7H8N2O2BlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        isomer_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return isomer_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        IsomerC7H8N2O2ProblemFactory(),
+        name="isomer_c7h8n2o2",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/isomer_c9h10n2o2pf2cl/__init__.py b/src/poli/objective_repository/isomer_c9h10n2o2pf2cl/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/isomer_c9h10n2o2pf2cl/information.py b/src/poli/objective_repository/isomer_c9h10n2o2pf2cl/information.py
new file mode 100644
index 00000000..e649b0cf
--- /dev/null
+++ b/src/poli/objective_repository/isomer_c9h10n2o2pf2cl/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+isomer_c9h10n2o2pf2cl_info = BlackBoxInformation(
+    name="isomer_c9h10n2o2pf2cl",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/isomer_c9h10n2o2pf2cl/register.py b/src/poli/objective_repository/isomer_c9h10n2o2pf2cl/register.py
new file mode 100644
index 00000000..466ced67
--- /dev/null
+++ b/src/poli/objective_repository/isomer_c9h10n2o2pf2cl/register.py
@@ -0,0 +1,230 @@
+"""
+Implements the Isomer C9H10N2O2PF2Cl task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2]. We recommend
+you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.isomer_c9h10n2o2pf2cl.information import (
+    isomer_c9h10n2o2pf2cl_info,
+)
+
+
+class IsomerC9H10N2O2PF2ClBlackBox(TDCBlackBox):
+    """
+    A black box that measures the similarities of a certain
+    SMILES string's Isomer presentation to C9H10N2O2PF2Cl, implemented
+    using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2]. We
+    recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation=False, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float('inf'))
+        Initializes a new instance of the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Isomers_C9H10N2O2PF2Cl",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return isomer_c9h10n2o2pf2cl_info
+
+
+class IsomerC9H10N2O2PF2ClProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Isomer C9H10N2O2PF2Cl problems.
+
+    This task is inherited from the GuacaMol benchmark [2]. We
+    recommend you cite [1, 2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Isomer C9H10N2O2PF2Cl problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return isomer_c9h10n2o2pf2cl_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Isomer C9H10N2O2PF2Cl problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = IsomerC9H10N2O2PF2ClBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        isomer_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return isomer_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        IsomerC9H10N2O2PF2ClProblemFactory(),
+        name="isomer_c9h10n2o2pf2cl",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/jnk3/__init__.py b/src/poli/objective_repository/jnk3/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/jnk3/information.py b/src/poli/objective_repository/jnk3/information.py
new file mode 100644
index 00000000..4f3264e6
--- /dev/null
+++ b/src/poli/objective_repository/jnk3/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+jnk3_info = BlackBoxInformation(
+    name="jnk3",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/jnk3/register.py b/src/poli/objective_repository/jnk3/register.py
new file mode 100644
index 00000000..aba63f05
--- /dev/null
+++ b/src/poli/objective_repository/jnk3/register.py
@@ -0,0 +1,239 @@
+"""
+Implements the JNK3 task using the TDC oracles [1].
+
+When using this task, we recommend you cite [1, 2, 3, 4].
+
+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
+[2] Multi-objective de novo drug design with conditional graph generative models.
+    Li, Y., Zhang, L., Liu, Z.
+    Journal of cheminformatics 10.1 (2018).
+[3] Multi-objective molecule generation using interpretable substructures.
+    Jin, Wengong, Regina Barzilay, and Tommi Jaakkola.
+    ICML. 2020.
+    https://people.csail.mit.edu/tommi/papers/JBJ_ICML2020b.pdf
+[4] ExCAPE-DB: an integrated large scale dataset facilitating Big Data analysis in chemogenomics.
+    Jiangming, Sun, et al.
+    Journal of cheminformatics 9.1 (2017).
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.objective_repository.jnk3.information import jnk3_info
+
+
+class JNK3BlackBox(TDCBlackBox):
+    """
+    A black box giving access to the JNK3 task, provided by TDC [1].
+
+    When using this task, we recommend you cite [1, 2, 3, 4].
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, info, batch_size=None, parallelize=False, num_workers=None, from_smiles=True)
+        Initializes a new instance of the DRD3BlackBox class.
+
+    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
+    [2] Multi-objective de novo drug design with conditional graph generative models.
+        Li, Y., Zhang, L., Liu, Z.
+        Journal of cheminformatics 10.1 (2018).
+    [3] Multi-objective molecule generation using interpretable substructures.
+        Jin, Wengong, Regina Barzilay, and Tommi Jaakkola.
+        ICML. 2020.
+        https://people.csail.mit.edu/tommi/papers/JBJ_ICML2020b.pdf
+    [4] ExCAPE-DB: an integrated large scale dataset facilitating Big Data analysis in chemogenomics.
+        Jiangming, Sun, et al.
+        Journal of cheminformatics 9.1 (2017).
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="JNK3",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return jnk3_info
+
+
+class JNK3ProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating JNK3 docking problems.
+
+    When using this task, we recommend you cite [1, 2, 3, 4].
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates a JNK3 problem.
+
+    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
+    [2] Multi-objective de novo drug design with conditional graph generative models.
+        Li, Y., Zhang, L., Liu, Z.
+        Journal of cheminformatics 10.1 (2018).
+    [3] Multi-objective molecule generation using interpretable substructures.
+        Jin, Wengong, Regina Barzilay, and Tommi Jaakkola.
+        ICML. 2020.
+        https://people.csail.mit.edu/tommi/papers/JBJ_ICML2020b.pdf
+    [4] ExCAPE-DB: an integrated large scale dataset facilitating Big Data analysis in chemogenomics.
+        Jiangming, Sun, et al.
+        Journal of cheminformatics 9.1 (2017).
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return jnk3_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a JNK3 problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = JNK3BlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        jnk3_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return jnk3_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        JNK3ProblemFactory(),
+        name="jnk3",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/median_1/__init__.py b/src/poli/objective_repository/median_1/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/median_1/information.py b/src/poli/objective_repository/median_1/information.py
new file mode 100644
index 00000000..71d7c705
--- /dev/null
+++ b/src/poli/objective_repository/median_1/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+median_1_info = BlackBoxInformation(
+    name="median_1",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/median_1/register.py b/src/poli/objective_repository/median_1/register.py
new file mode 100644
index 00000000..f3aa6a07
--- /dev/null
+++ b/src/poli/objective_repository/median_1/register.py
@@ -0,0 +1,224 @@
+"""
+Implements the Median 1 task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2].
+We recommend you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.median_1.information import median_1_info
+
+
+class Median1BlackBox(TDCBlackBox):
+    """
+    A black box giving access to the "Median 1" oracle of TDC [1].
+
+    This task is inherited from the GuacaMol benchmark [2]. We
+    recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float('inf'))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Median 1",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return median_1_info
+
+
+class Median1ProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Median 1 problems.
+
+    We recommend you cite [1, 2] when using this problem factory.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Median 1 problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return median_1_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Median 1 problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = Median1BlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        median_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return median_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        Median1ProblemFactory(),
+        name="median_1",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/median_2/__init__.py b/src/poli/objective_repository/median_2/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/median_2/information.py b/src/poli/objective_repository/median_2/information.py
new file mode 100644
index 00000000..2996efc6
--- /dev/null
+++ b/src/poli/objective_repository/median_2/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+median_2_info = BlackBoxInformation(
+    name="median_2",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/median_2/register.py b/src/poli/objective_repository/median_2/register.py
new file mode 100644
index 00000000..4278d206
--- /dev/null
+++ b/src/poli/objective_repository/median_2/register.py
@@ -0,0 +1,223 @@
+"""
+Implements the Median 2 task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2].
+We recommend citing both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.median_2.information import median_2_info
+
+
+class Median2BlackBox(TDCBlackBox):
+    """
+    A black box that gives access to the "Median 2" oracle of TDC [1].
+
+    This task is inherited from the GuacaMol benchmark [2]. We
+    recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Median 2",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return median_2_info
+
+
+class Median2ProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Median 2 problems.
+
+    We recommend citing [1, 2] when using this problem factory.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Median 2 problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return median_2_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Median 2 problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = Median2BlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        median_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return median_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        Median2ProblemFactory(),
+        name="median_2",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/mestranol_similarity/__init__.py b/src/poli/objective_repository/mestranol_similarity/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/mestranol_similarity/information.py b/src/poli/objective_repository/mestranol_similarity/information.py
new file mode 100644
index 00000000..8d0ede68
--- /dev/null
+++ b/src/poli/objective_repository/mestranol_similarity/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+mestranol_similarity_info = BlackBoxInformation(
+    name="mestranol_similarity",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/mestranol_similarity/register.py b/src/poli/objective_repository/mestranol_similarity/register.py
new file mode 100644
index 00000000..7584d9c5
--- /dev/null
+++ b/src/poli/objective_repository/mestranol_similarity/register.py
@@ -0,0 +1,230 @@
+"""
+Implements the mestranol similarity task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2], and consists of
+measuring the similarity of molecules (usually provided as SMILES or SELFIES
+strings) to mestranol.
+
+We recommend citing both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.mestranol_similarity.information import (
+    mestranol_similarity_info,
+)
+
+
+class MestranolSimilarityBlackBox(TDCBlackBox):
+    """
+    A black box that measures the similarities of molecules to
+    mestranol, implementation using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2].
+    We recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Mestranol_Similarity",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return mestranol_similarity_info
+
+
+class MestranolSimilarityProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Albuterol Similarity problems.
+
+    We recommend you cite [1, 2] when using this problem factory.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates a Mestranol Similarity problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return mestranol_similarity_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Mestranol similarity problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = MestranolSimilarityBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        mestranol_similarity_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return mestranol_similarity_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        MestranolSimilarityProblemFactory(),
+        name="mestranol_similarity",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/osimetrinib_mpo/__init__.py b/src/poli/objective_repository/osimetrinib_mpo/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/osimetrinib_mpo/information.py b/src/poli/objective_repository/osimetrinib_mpo/information.py
new file mode 100644
index 00000000..a0091ed3
--- /dev/null
+++ b/src/poli/objective_repository/osimetrinib_mpo/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+osimetrinib_mpo_info = BlackBoxInformation(
+    name="osimetrinib_mpo",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/osimetrinib_mpo/register.py b/src/poli/objective_repository/osimetrinib_mpo/register.py
new file mode 100644
index 00000000..c8eb49bd
--- /dev/null
+++ b/src/poli/objective_repository/osimetrinib_mpo/register.py
@@ -0,0 +1,224 @@
+"""
+Implements the osimetrinib MPO task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2]. We
+recommend you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.osimetrinib_mpo.information import osimetrinib_mpo_info
+
+
+class OsimetrinibMPOBlackBox(TDCBlackBox):
+    """
+    The TDC oracle for Osimentrinib MPO [1].
+
+    This task is inherited from the GuacaMol benchmark [2].
+    We recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf")
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Osimetrinib_MPO",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return osimetrinib_mpo_info
+
+
+class OsimetrinibMPOProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Osimetrinib MPO problems.
+
+    We recommend you cite [1, 2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Osimetrinib MPO problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return osimetrinib_mpo_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Osimetrinib MPO problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = OsimetrinibMPOBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        osimetrinib_mpo_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return osimetrinib_mpo_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        OsimetrinibMPOProblemFactory(),
+        name="osimetrinib_mpo",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/perindopril_mpo/__init__.py b/src/poli/objective_repository/perindopril_mpo/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/perindopril_mpo/information.py b/src/poli/objective_repository/perindopril_mpo/information.py
new file mode 100644
index 00000000..93803488
--- /dev/null
+++ b/src/poli/objective_repository/perindopril_mpo/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+perindopril_mpo_info = BlackBoxInformation(
+    name="perindopril_mpo",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/perindopril_mpo/register.py b/src/poli/objective_repository/perindopril_mpo/register.py
new file mode 100644
index 00000000..3c3a4027
--- /dev/null
+++ b/src/poli/objective_repository/perindopril_mpo/register.py
@@ -0,0 +1,223 @@
+"""
+Implements the perindopril MPO task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2].
+We recommend you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.perindopril_mpo.information import perindopril_mpo_info
+
+
+class PerindoprilMPOBlackBox(TDCBlackBox):
+    """
+    The TDC oracle for Perindopril MPO [1].
+
+    This task is inherited from the GuacaMol benchmark [2].
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size, parallelize, num_workers, evaluation_budget)
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Perindopril_MPO",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return perindopril_mpo_info
+
+
+class PerindoprilMPOProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Osimetrinib MPO problems.
+
+    We recommend citing [1, 2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Perindopril MPO problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return perindopril_mpo_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Perindopril MPO problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = PerindoprilMPOBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        perindopril_mpo_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return perindopril_mpo_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        PerindoprilMPOProblemFactory(),
+        name="perindopril_mpo",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/ranolazine_mpo/__init__.py b/src/poli/objective_repository/ranolazine_mpo/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/ranolazine_mpo/information.py b/src/poli/objective_repository/ranolazine_mpo/information.py
new file mode 100644
index 00000000..cd76ac53
--- /dev/null
+++ b/src/poli/objective_repository/ranolazine_mpo/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+ranolazine_mpo_info = BlackBoxInformation(
+    name="ranolazine_mpo",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/ranolazine_mpo/register.py b/src/poli/objective_repository/ranolazine_mpo/register.py
new file mode 100644
index 00000000..bae99c00
--- /dev/null
+++ b/src/poli/objective_repository/ranolazine_mpo/register.py
@@ -0,0 +1,224 @@
+"""
+Implements the ranolazine MPO task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2].
+When using this task, we recommend you cite both references.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.ranolazine_mpo.information import ranolazine_mpo_info
+
+
+class RanolazineMPOBlackBox(TDCBlackBox):
+    """
+    The TDC oracle for Ranolazine MPO [1].
+
+    This task is inherited from the GuacaMol benchmark [2].
+    We recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Ranolazine_MPO",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return ranolazine_mpo_info
+
+
+class RanolazineMPOProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Ranolazine MPO problems.
+
+    When using this problem factory, we recommend you cite [1, 2].
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Ranolazine MPO problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return ranolazine_mpo_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Ranolazine MPO problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = RanolazineMPOBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        ranolazine_mpo_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return ranolazine_mpo_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        RanolazineMPOProblemFactory(),
+        name="ranolazine_mpo",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/sa_tdc/register.py b/src/poli/objective_repository/sa_tdc/register.py
index 48808a36..eca14630 100644
--- a/src/poli/objective_repository/sa_tdc/register.py
+++ b/src/poli/objective_repository/sa_tdc/register.py
@@ -13,7 +13,7 @@
 
 import selfies as sf
 
-from poli.core.abstract_black_box import AbstractBlackBox
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
 from poli.core.abstract_problem_factory import AbstractProblemFactory
 from poli.core.black_box_information import BlackBoxInformation
 from poli.core.problem import Problem
@@ -27,7 +27,7 @@
 from poli.objective_repository.sa_tdc.information import sa_tdc_info
 
 
-class SABlackBox(AbstractBlackBox):
+class SABlackBox(TDCBlackBox):
     """Synthetic-accessibility black box implementation using the TDC oracles [1].
 
     Parameters
@@ -72,32 +72,14 @@ def __init__(
             The maximum number of evaluations, by default float("inf").
         """
         super().__init__(
+            oracle_name="SA",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
             batch_size=batch_size,
             parallelize=parallelize,
             num_workers=num_workers,
             evaluation_budget=evaluation_budget,
         )
-        from_smiles = string_representation.upper() == "SMILES"
-        if not force_isolation:
-            try:
-                from poli.objective_repository.sa_tdc.isolated_function import (
-                    SAIsolatedLogic,
-                )
-
-                self.inner_function = SAIsolatedLogic(from_smiles=from_smiles)
-            except ImportError:
-                self.inner_function = instance_function_as_isolated_process(
-                    name="sa_tdc__isolated",
-                    from_smiles=from_smiles,
-                )
-        else:
-            self.inner_function = instance_function_as_isolated_process(
-                name="sa_tdc__isolated",
-                from_smiles=from_smiles,
-            )
-
-    def _black_box(self, x: np.ndarray, context=None) -> np.ndarray:
-        return self.inner_function(x, context)
 
     @staticmethod
     def get_black_box_info() -> BlackBoxInformation:
@@ -162,7 +144,7 @@ def create(
         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).
+            The initial input (taken from TDC: CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1).
         y0: np.ndarray
             The initial output (i.e. the corresponding SA).
         """
@@ -185,7 +167,7 @@ def create(
         )
 
         # Initial example (from the TDC docs)
-        x0_smiles = "CCNC(=O)c1ccc(NC(=O)N2CC[C@H](C)[C@H](O)C2)c(C)c1"
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
         x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
 
         # TODO: change for proper tokenization in the SMILES case.
diff --git a/src/poli/objective_repository/scaffold_hop/__init__.py b/src/poli/objective_repository/scaffold_hop/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/scaffold_hop/information.py b/src/poli/objective_repository/scaffold_hop/information.py
new file mode 100644
index 00000000..ac9ed780
--- /dev/null
+++ b/src/poli/objective_repository/scaffold_hop/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+scaffold_hop_info = BlackBoxInformation(
+    name="scaffold_hop",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/scaffold_hop/register.py b/src/poli/objective_repository/scaffold_hop/register.py
new file mode 100644
index 00000000..cf17359e
--- /dev/null
+++ b/src/poli/objective_repository/scaffold_hop/register.py
@@ -0,0 +1,224 @@
+"""
+Implements the Scaffold HOP task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2].
+We recommend you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.scaffold_hop.information import scaffold_hop_info
+
+
+class ScaffoldHopBlackBox(TDCBlackBox):
+    """
+    A black box that gives access to the "Scaffold Hop" oracle of TDC [1].
+
+    This task is inherited from the GuacaMol benchmark [2].
+    We recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Scaffold Hop",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return scaffold_hop_info
+
+
+class ScaffoldHopProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Scaffold Hop problems.
+
+    We recommend you cite [1, 2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an Scaffold Hop problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return scaffold_hop_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Scaffold Hop problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = ScaffoldHopBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        scaffold_hop_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return scaffold_hop_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        ScaffoldHopProblemFactory(),
+        name="scaffold_hop",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/sitagliptin_mpo/__init__.py b/src/poli/objective_repository/sitagliptin_mpo/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/sitagliptin_mpo/information.py b/src/poli/objective_repository/sitagliptin_mpo/information.py
new file mode 100644
index 00000000..acefbb68
--- /dev/null
+++ b/src/poli/objective_repository/sitagliptin_mpo/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+sitagliptin_mpo_info = BlackBoxInformation(
+    name="sitagliptin_mpo",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/sitagliptin_mpo/register.py b/src/poli/objective_repository/sitagliptin_mpo/register.py
new file mode 100644
index 00000000..54e8ed1a
--- /dev/null
+++ b/src/poli/objective_repository/sitagliptin_mpo/register.py
@@ -0,0 +1,223 @@
+"""
+Implements the sitagliptin MPO task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2].
+We recommend you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.sitagliptin_mpo.information import sitagliptin_mpo_info
+
+
+class SitagliptinMPOBlackBox(TDCBlackBox):
+    """
+    The TDC oracle for Sitagliptin MPO [1].
+
+    This task is inherited from the GuacaMol benchmark [2].
+    We recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Sitagliptin_MPO",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return sitagliptin_mpo_info
+
+
+class SitagliptinMPOProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating sitagliptin MPO problems.
+
+    We recommend you cite the references [1] and [2] when using this task.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an sitagliptin MPO problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return sitagliptin_mpo_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a sitagliptin MPO problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = SitagliptinMPOBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        sitagliptin_mpo_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return sitagliptin_mpo_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        SitagliptinMPOProblemFactory(),
+        name="sitagliptin_mpo",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/thiothixene_rediscovery/__init__.py b/src/poli/objective_repository/thiothixene_rediscovery/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/thiothixene_rediscovery/information.py b/src/poli/objective_repository/thiothixene_rediscovery/information.py
new file mode 100644
index 00000000..c1fcab0d
--- /dev/null
+++ b/src/poli/objective_repository/thiothixene_rediscovery/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+thiothixene_rediscovery_info = BlackBoxInformation(
+    name="thiothixene_rediscovery",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/thiothixene_rediscovery/register.py b/src/poli/objective_repository/thiothixene_rediscovery/register.py
new file mode 100644
index 00000000..202ade66
--- /dev/null
+++ b/src/poli/objective_repository/thiothixene_rediscovery/register.py
@@ -0,0 +1,227 @@
+"""
+Implements the Thiothixene rediscovery task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2], and consists of
+rediscovering a certain molecule through optimization. We recommend you
+cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.thiothixene_rediscovery.information import (
+    thiothixene_rediscovery_info,
+)
+
+
+class ThiothixeneRediscoveryBlackBox(TDCBlackBox):
+    """
+    Thiothixene rediscovery black box implementation using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2], and consists of
+    rediscovering a certain molecule through optimization. We recommend you
+    cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float("inf"))
+        Initializes the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Thiothixene_Rediscovery",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return thiothixene_rediscovery_info
+
+
+class ThiothixeneRediscoveryProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Thiothixene rediscovery problems.
+
+    We recommend you cite [1, 2] when using this problem factory.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates a DRD2 docking problem.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return thiothixene_rediscovery_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Thiothixene rediscovery problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = ThiothixeneRediscoveryBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        thiothixene_rediscovery_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return thiothixene_rediscovery_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        ThiothixeneRediscoveryProblemFactory(),
+        name="thiothixene_rediscovery",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/troglitazone_rediscovery/__init__.py b/src/poli/objective_repository/troglitazone_rediscovery/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/troglitazone_rediscovery/information.py b/src/poli/objective_repository/troglitazone_rediscovery/information.py
new file mode 100644
index 00000000..d0df5baf
--- /dev/null
+++ b/src/poli/objective_repository/troglitazone_rediscovery/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+troglitazone_rediscovery_info = BlackBoxInformation(
+    name="troglitazone_rediscovery",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/troglitazone_rediscovery/register.py b/src/poli/objective_repository/troglitazone_rediscovery/register.py
new file mode 100644
index 00000000..227fdbaf
--- /dev/null
+++ b/src/poli/objective_repository/troglitazone_rediscovery/register.py
@@ -0,0 +1,227 @@
+"""
+Implements the Troglitazone rediscovery task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2], and consists of
+rediscovering a certain molecule through optimization. We recommend you
+cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.troglitazone_rediscovery.information import (
+    troglitazone_rediscovery_info,
+)
+
+
+class TroglitazoneRediscoveryBlackBox(TDCBlackBox):
+    """
+    Troglitazone rediscovery black box implementation using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2], and consists of
+    rediscovering a certain molecule through optimization. We recommend you
+    cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size=None, parallelize=False, num_workers=None, evaluation_budget=float('inf'))
+        Initializes a new instance of the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Troglitazone_Rediscovery",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return troglitazone_rediscovery_info
+
+
+class TroglitazoneRediscoveryProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Thiothixene rediscovery problems.
+
+    We recommend you cite [1, 2] when using this problem factory.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates a Troglitazone rediscovery problem.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return troglitazone_rediscovery_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Troglitazone rediscovery problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = TroglitazoneRediscoveryBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        troglitazone_rediscovery_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return troglitazone_rediscovery_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        TroglitazoneRediscoveryProblemFactory(),
+        name="troglitazone_rediscovery",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/valsartan_smarts/__init__.py b/src/poli/objective_repository/valsartan_smarts/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/valsartan_smarts/information.py b/src/poli/objective_repository/valsartan_smarts/information.py
new file mode 100644
index 00000000..56547041
--- /dev/null
+++ b/src/poli/objective_repository/valsartan_smarts/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+valsartan_smarts_info = BlackBoxInformation(
+    name="valsartan_smarts",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/valsartan_smarts/register.py b/src/poli/objective_repository/valsartan_smarts/register.py
new file mode 100644
index 00000000..d3ebabc1
--- /dev/null
+++ b/src/poli/objective_repository/valsartan_smarts/register.py
@@ -0,0 +1,226 @@
+"""
+Implements the Valsartan SMARTS task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2]. We recommend you
+cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.valsartan_smarts.information import (
+    valsartan_smarts_info,
+)
+
+
+class ValsartanSMARTSBlackBox(TDCBlackBox):
+    """
+    Valsartan SMARTS black box implementation using the TDC oracles [1].
+
+    This task is inherited from the GuacaMol benchmark [2]. We recommend you
+    cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size, parallelize, num_workers, evaluation_budget)
+        Initializes a new instance of the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Valsartan_SMARTS",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return valsartan_smarts_info
+
+
+class ValsartanSMARTSProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating Valsartan SMARTS problems.
+
+    We recommend you cite [1, 2] when using this problem factory.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates a Valsartan SMARTS problem.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return valsartan_smarts_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Valsartan SMARTS problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = ValsartanSMARTSBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        valsartan_smarts_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return valsartan_smarts_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        ValsartanSMARTSProblemFactory(),
+        name="valsartan_smarts",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/src/poli/objective_repository/zaleplon_mpo/__init__.py b/src/poli/objective_repository/zaleplon_mpo/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/src/poli/objective_repository/zaleplon_mpo/information.py b/src/poli/objective_repository/zaleplon_mpo/information.py
new file mode 100644
index 00000000..3ac07f39
--- /dev/null
+++ b/src/poli/objective_repository/zaleplon_mpo/information.py
@@ -0,0 +1,15 @@
+import numpy as np
+
+from poli.core.black_box_information import BlackBoxInformation
+
+zaleplon_mpo_info = BlackBoxInformation(
+    name="zaleplon_mpo",
+    max_sequence_length=np.inf,
+    aligned=False,
+    fixed_length=False,
+    deterministic=True,  # ?
+    alphabet=None,  # TODO: add alphabet once we settle for one for SMLIES/SELFIES.
+    log_transform_recommended=False,
+    discrete=True,
+    padding_token="",
+)
diff --git a/src/poli/objective_repository/zaleplon_mpo/register.py b/src/poli/objective_repository/zaleplon_mpo/register.py
new file mode 100644
index 00000000..6f4910f7
--- /dev/null
+++ b/src/poli/objective_repository/zaleplon_mpo/register.py
@@ -0,0 +1,224 @@
+"""
+Implements the Zaleplon MPO task using the TDC oracles [1].
+
+This task is inherited from the GuacaMol benchmark [2]. We recommend
+you cite both references when using this task.
+
+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
+[2] GuacaMol: benchmarking models for de novo molecular design.
+    Brown, N. et al.  J Chem Inf Model 59 (2019).
+    https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+"""
+
+from typing import Literal
+
+import numpy as np
+
+import selfies as sf
+
+from poli.core.abstract_problem_factory import AbstractProblemFactory
+from poli.core.black_box_information import BlackBoxInformation
+from poli.core.chemistry.tdc_black_box import TDCBlackBox
+from poli.core.problem import Problem
+
+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.chemistry.tdc_black_box import TDCBlackBox
+
+from poli.objective_repository.zaleplon_mpo.information import zaleplon_mpo_info
+
+
+class ZaleplonMPOBlackBox(TDCBlackBox):
+    """
+    The TDC oracle for Zaleplon MPO [1].
+
+    This task is inherited from the GuacaMol benchmark [2].
+    We recommend you cite both references when using this task.
+
+    Parameters
+    ----------
+    string_representation : Literal["SMILES", "SELFIES"], optional
+        A string (either "SMILES" or "SELFIES") specifying which
+        molecule representation you plan to use.
+    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.
+    force_isolation: bool, optional
+        Whether to force the isolation of the black box. Default is False.
+
+    Attributes
+    ----------
+    oracle_name : str
+        The name of the oracle.
+
+    Methods
+    -------
+    __init__(self, string_representation, force_isolation, batch_size, parallelize, num_workers, evaluation_budget)
+        Initializes a new instance of the black box.
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def __init__(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        force_isolation: bool = False,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+    ):
+        super().__init__(
+            oracle_name="Zaleplon_MPO",
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+    @staticmethod
+    def get_black_box_info() -> BlackBoxInformation:
+        return zaleplon_mpo_info
+
+
+class ZaleplonMPOProblemFactory(AbstractProblemFactory):
+    """
+    Factory class for creating zaleplon MPO problems.
+
+    We recommend you cite [1, 2] when using this problem factory.
+
+    Methods
+    ------
+    get_setup_information:
+        Retrieves the setup information for the problem.
+    create:
+        Creates an zaleplon MPO problem, containing a black box
+        and an initial value x0 (taken from the documentation of TDC).
+
+    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
+    [2] GuacaMol: benchmarking models for de novo molecular design.
+        Brown, N. et al.  J Chem Inf Model 59 (2019).
+        https://pubs.acs.org/doi/10.1021/acs.jcim.8b00839
+    """
+
+    def get_setup_information(self) -> BlackBoxInformation:
+        """
+        Retrieves the setup information for the problem.
+
+        Returns
+        --------
+        problem_info: ProblemSetupInformation
+            The setup information for the problem.
+        """
+        return zaleplon_mpo_info
+
+    def create(
+        self,
+        string_representation: Literal["SMILES", "SELFIES"] = "SMILES",
+        seed: int = None,
+        batch_size: int = None,
+        parallelize: bool = False,
+        num_workers: int = None,
+        evaluation_budget: int = float("inf"),
+        force_isolation: bool = False,
+    ) -> Problem:
+        """
+        Creates a Zaleplon MPO problem.
+
+        Parameters
+        ----------
+        string_representation : str, optional
+            The string representation of the molecules. Must be either 'SMILES' or 'SELFIES'. Default is 'SMILES'.
+        seed : int, optional
+            Seed for random number generators. If None, no seed is set.
+        batch_size : int, optional
+            Number of molecules to process in parallel. If None, the default batch size is used.
+        parallelize : bool, optional
+            Whether to parallelize the docking process. Default is False.
+        num_workers : int, optional
+            Number of worker processes to use for parallelization. If None, the number of available CPU cores is used.
+        evaluation_budget:  int, optional
+            The maximum number of function evaluations. Default is infinity.
+        force_isolation: bool, optional
+            Whether to force the isolation of the black box. Default is False.
+
+        Returns
+        -------
+        problem : Problem
+            A problem instance containing the black box, and an initial value x0.
+
+        Raises
+        ------
+        ValueError
+            If the string_representation is not 'SMILES' or 'SELFIES'.
+        """
+        # We start by seeding the RNGs
+        if seed is not None:
+            seed_numpy(seed)
+            seed_python(seed)
+
+        # We check whether the string representation is valid
+        if string_representation.upper() not in ["SMILES", "SELFIES"]:
+            raise ValueError(
+                "Missing required keyword argument: string_representation: str. "
+                "String representation must be either 'SMILES' or 'SELFIES'."
+            )
+
+        f = ZaleplonMPOBlackBox(
+            string_representation=string_representation,
+            force_isolation=force_isolation,
+            batch_size=batch_size,
+            parallelize=parallelize,
+            num_workers=num_workers,
+            evaluation_budget=evaluation_budget,
+        )
+
+        # Initial example (from the TDC docs)
+        x0_smiles = "CC(C)(C)[C@H]1CCc2c(sc(NC(=O)COc3ccc(Cl)cc3)c2C(N)=O)C1"
+        x0_selfies = translate_smiles_to_selfies([x0_smiles])[0]
+
+        if string_representation.upper() == "SMILES":
+            x0 = np.array([list(x0_smiles)])
+        else:
+            x0 = np.array([list(sf.split_selfies(x0_selfies))])
+
+        zaleplon_mpo_problem = Problem(
+            black_box=f,
+            x0=x0,
+        )
+
+        return zaleplon_mpo_problem
+
+
+if __name__ == "__main__":
+    from poli.core.registry import register_problem
+
+    register_problem(
+        ZaleplonMPOProblemFactory(),
+        name="zaleplon_mpo",
+        conda_environment_name="poli__tdc",
+        force=True,
+    )
diff --git a/tox.ini b/tox.ini
index 35bdce22..8b7d2c48 100644
--- a/tox.ini
+++ b/tox.ini
@@ -27,7 +27,7 @@ commands =
     sh -c 'if conda info --envs | grep -q poli__dockstring; then echo "poli__dockstring already exists"; else conda env create -f ./src/poli/objective_repository/dockstring/environment.yml; fi'
     sh -c "conda run -n poli__dockstring python -m pip uninstall -y poli"
     sh -c "conda run -n poli__dockstring python -m pip install -e ."
-    sh -c 'if conda info --envs | grep -q poli__tdc; then echo "poli__tdc already exists"; else conda env create -f ./src/poli/objective_repository/sa_tdc/environment.yml; fi'
+    sh -c 'if conda info --envs | grep -q poli__tdc; then echo "poli__tdc already exists"; else conda env create -f ./src/poli/core/chemistry/environment.yml; fi'
     sh -c "conda run -n poli__tdc python -m pip uninstall -y poli"
     sh -c "conda run -n poli__tdc python -m pip install -e ."
     sh -c 'if conda info --envs | grep -q poli__lambo; then echo "poli__lambo already exists"; else conda env create -f ./src/poli/objective_repository/foldx_rfp_lambo/environment.yml; fi'


Test patch:
diff --git a/src/poli/tests/__init__.py b/src/poli/tests/__init__.py
index 1c91d3a1..e69de29b 100644
--- a/src/poli/tests/__init__.py
+++ b/src/poli/tests/__init__.py
@@ -1,1 +0,0 @@
-__author__ = "Simon Bartels"
diff --git a/src/poli/tests/registry/__init__.py b/src/poli/tests/registry/__init__.py
index 1c91d3a1..e69de29b 100644
--- a/src/poli/tests/registry/__init__.py
+++ b/src/poli/tests/registry/__init__.py
@@ -1,1 +0,0 @@
-__author__ = "Simon Bartels"
diff --git a/src/poli/tests/registry/chemistry/alphabet_qed.json b/src/poli/tests/registry/chemistry/alphabet_qed.json
deleted file mode 100644
index 67b6a7ae..00000000
--- a/src/poli/tests/registry/chemistry/alphabet_qed.json
+++ /dev/null
@@ -1,4 +0,0 @@
-[
-    "",
-    "C"
-]
\ No newline at end of file
diff --git a/src/poli/tests/registry/chemistry/test_chemistry_objectives.py b/src/poli/tests/registry/chemistry/test_chemistry_objectives.py
index abbe3179..b676c148 100644
--- a/src/poli/tests/registry/chemistry/test_chemistry_objectives.py
+++ b/src/poli/tests/registry/chemistry/test_chemistry_objectives.py
@@ -4,9 +4,36 @@
 import numpy as np
 
 from poli import objective_factory
+from poli.objective_repository import (
+    GSK3BetaBlackBox,
+    DRD2BlackBox,
+    JNK3BlackBox,
+    SABlackBox,
+    CelecoxibRediscoveryBlackBox,
+    ThiothixeneRediscoveryBlackBox,
+    TroglitazoneRediscoveryBlackBox,
+    AlbuterolSimilarityBlackBox,
+    MestranolSimilarityBlackBox,
+    AmlodipineMPOBlackBox,
+    FexofenadineMPOBlackBox,
+    OsimetrinibMPOBlackBox,
+    PerindoprilMPOBlackBox,
+    RanolazineMPOBlackBox,
+    SitagliptinMPOBlackBox,
+    ZaleplonMPOBlackBox,
+    DecoHopBlackBox,
+    ScaffoldHopBlackBox,
+    IsomerC7H8N2O2BlackBox,
+    IsomerC9H10N2O2PF2ClBlackBox,
+    Median1BlackBox,
+    Median2BlackBox,
+    ValsartanSMARTSBlackBox,
+)
 
 THIS_DIR = Path(__file__).parent.resolve()
 
+SEED = np.random.randint(0, 1000)
+
 
 def test_force_registering_qed():
     """
@@ -136,3 +163,181 @@ def test_querying_dockstring_using_selfies():
 
     y1 = f(selfies_aspirin)
     f.terminate()
+
+
+test_data_for_pmo = [
+    # Removed until TDC fixes issue #238.
+    ("gsk3_beta", GSK3BetaBlackBox, {"string_representation": "SMILES"}, 0.03),
+    # (
+    #     "gsk3_beta",
+    #     GSK3BetaBlackBox,
+    #     {"string_representation": "SMILES"},
+    #     None,
+    # ),
+    (
+        "drd2_docking",
+        DRD2BlackBox,
+        {"string_representation": "SMILES"},
+        0.0015465365340340924,
+    ),
+    (
+        "sa_tdc",
+        SABlackBox,
+        {"string_representation": "SMILES"},
+        2.706977149048555,
+    ),
+    (
+        "celecoxib_rediscovery",
+        CelecoxibRediscoveryBlackBox,
+        {"string_representation": "SMILES"},
+        0.14728682170542637,
+    ),
+    (
+        "thiothixene_rediscovery",
+        ThiothixeneRediscoveryBlackBox,
+        {"string_representation": "SMILES"},
+        0.17391304347826086,
+    ),
+    (
+        "troglitazone_rediscovery",
+        TroglitazoneRediscoveryBlackBox,
+        {"string_representation": "SMILES"},
+        0.24427480916030533,
+    ),
+    (
+        "albuterol_similarity",
+        AlbuterolSimilarityBlackBox,
+        {"string_representation": "SMILES"},
+        0.2772277227722772,
+    ),
+    (
+        "mestranol_similarity",
+        MestranolSimilarityBlackBox,
+        {"string_representation": "SMILES"},
+        0.19460880999342536,
+    ),
+    (
+        "amlodipine_mpo",
+        AmlodipineMPOBlackBox,
+        {"string_representation": "SMILES"},
+        0.461083967620704,
+    ),
+    (
+        "fexofenadine_mpo",
+        FexofenadineMPOBlackBox,
+        {"string_representation": "SMILES"},
+        0.4336446174984538,
+    ),
+    (
+        "osimetrinib_mpo",
+        OsimetrinibMPOBlackBox,
+        {"string_representation": "SMILES"},
+        0.09011742702110873,
+    ),
+    (
+        "perindopril_mpo",
+        PerindoprilMPOBlackBox,
+        {"string_representation": "SMILES"},
+        0.36023741111440966,
+    ),
+    (
+        "ranolazine_mpo",
+        RanolazineMPOBlackBox,
+        {"string_representation": "SMILES"},
+        0.29285467466584664,
+    ),
+    # The following two have a discrepancy with
+    # the TDC docs. An issue has been raised (#244)
+    (
+        "sitagliptin_mpo",
+        SitagliptinMPOBlackBox,
+        {"string_representation": "SMILES"},
+        3.34970667598234e-12,
+    ),
+    (
+        "zaleplon_mpo",
+        ZaleplonMPOBlackBox,
+        {"string_representation": "SMILES"},
+        0.0019017991803329235,
+    ),
+    # The following should actually output 0.5338365434669443,
+    # but TDC has a discrepancy. An issue has been raised (#244)
+    (
+        "deco_hop",
+        DecoHopBlackBox,
+        {"string_representation": "SMILES"},
+        None,
+    ),
+    (
+        "scaffold_hop",
+        ScaffoldHopBlackBox,
+        {"string_representation": "SMILES"},
+        0.38446411012782694,
+    ),
+    # The following two have a discrepancy with
+    # the TDC docs. An issue has been raised (#244)
+    (
+        "isomer_c7h8n2o2",
+        IsomerC7H8N2O2BlackBox,
+        {"string_representation": "SMILES"},
+        2.1987591132394053e-34,
+    ),
+    (
+        "isomer_c9h10n2o2pf2cl",
+        IsomerC9H10N2O2PF2ClBlackBox,
+        {"string_representation": "SMILES"},
+        1.713908431542013e-15,
+    ),
+    (
+        "median_1",
+        Median1BlackBox,
+        {"string_representation": "SMILES"},
+        0.09722243533981723,
+    ),
+    (
+        "median_2",
+        Median2BlackBox,
+        {"string_representation": "SMILES"},
+        0.12259690287307903,
+    ),
+    (
+        "valsartan_smarts",
+        ValsartanSMARTSBlackBox,
+        {"string_representation": "SMILES"},
+        0.0,
+    ),
+    # Removed until TDC fixes issue #238.
+    ("jnk3", JNK3BlackBox, {"string_representation": "SMILES"}, 0.01),
+    # ("jnk3", JNK3BlackBox, {"string_representation": "SMILES"}, None),
+]
+
+
+@pytest.mark.parametrize(
+    "black_box_name, black_box_class, kwargs_for_black_box, value_to_check",
+    test_data_for_pmo,
+)
+def test_pmo_black_boxes(
+    black_box_name, black_box_class, kwargs_for_black_box, value_to_check
+):
+    from poli import create
+    from poli.core.util.seeding import seed_python_numpy_and_torch
+
+    problem = create(
+        name=black_box_name,
+        seed=SEED,
+        **kwargs_for_black_box,
+    )
+    x0 = problem.x0
+    y0 = problem.black_box(x0)
+
+    seed_python_numpy_and_torch(seed=SEED)
+    f = black_box_class(**kwargs_for_black_box)
+    y0_ = f(x0)
+
+    assert np.allclose(y0_, y0)
+    if value_to_check is not None:
+        assert (y0_ == value_to_check).all()
+
+
+if __name__ == "__main__":
+    test_pmo_black_boxes(*test_data_for_pmo[-1])