code: def run(self) -> None:
      self.logger.info("Beginning Experiment.")
      exp_folder: Path = Path()
      dataset_folder: Path = Path()
      if self.artifacts:
          if self.save_folder is None:
              raise ValueError("Provide a folder to save artifacts.")
  
          self.logger.debug(
              "Generating Hash for provided configuration."
          )
          self.generate_hash()
          exp_folder: Path = self.save_folder / self.hash
          exp_folder.mkdir(parents=True, exist_ok=True)
          self.logger.info(
              f"Saving objects to '{exp_folder.resolve()}'."
          )
      self._read_datasets()
      for dataset_id, (dataset_name, dataset) in enumerate(
          self.datasets.items()
      ):
          self.logger.info(f"Using '{dataset_name}'.")
          if self.artifacts:
              dataset_folder: Path = exp_folder / dataset_name
              dataset_folder.mkdir(parents=True, exist_ok=True)
              self.logger.debug(
                  f"Saving dataset-related objects to '{dataset_folder.resolve()}'."
              )
          for method in self.config.methods:
              for method_name, method_items in method.items():
                  self.logger.info(
                      f"Testing '{method_name}', saved in '{dataset_folder.resolve()}'."
                  )
                  if self.artifacts:
                      method_folder: Path = dataset_folder / method_name
                      method_folder.mkdir(parents=True, exist_ok=True)
                      self.logger.debug(
                          "Saving dataset&method-related objects to "
                          f"'{method_folder.resolve()}'."
                      )
  
                  # Instantiate one random search optimizer
                  # The objective function is doing the heavy lifting in HPTs code.
  
                  # Validate if we received pre-processing techniques.
                  if hasattr(method_items, "preprocessing"):
                      _, pre_configs = list(
                          method_items.preprocessing.items()
                      )[0]
                      pre = import_object(pre_configs.classpath)
                  else:
                      pre_configs = DictConfig({"args": None})
                      pre = PreIdentity
                  # Validate in the same way the post-processing technique.
                  if hasattr(method_items, "postprocessing"):
                      _, post_configs = list(
                          method_items.postprocessing.items()
                      )[0]
                      post = import_object(post_configs.classpath)
                  else:
                      post_configs = DictConfig({"args": None})
                      post = PostIdentity
  
                  _, inp_configs = list(
                      method_items.inprocessing.items()
                  )[0]
                  inp = import_object(inp_configs.classpath)
  
                  # Kinda big line to get the threshold parameters...
                  threshold_configs = self.config.datasets[dataset_id][
                      dataset_name
                  ].threshold
                  threshold = Threshold(**threshold_configs)
                  # This is a big instantiation, maybe receive datasets all together?
                  self.logger.debug(
                      f"Creating objective function for "
                      f"'{method_name}' in '{dataset_name}'."
                  )
                  objective = ObjectiveFunction(
                      X_train=dataset["train"]["X"],
                      y_train=dataset["train"]["y"],
                      s_train=dataset["train"]["s"],
                      X_val=dataset["validation"]["X"],
                      y_val=dataset["validation"]["y"],
                      s_val=dataset["validation"]["s"],
                      X_test=dataset["test"]["X"],
                      y_test=dataset["test"]["y"],
                      s_test=dataset["test"]["s"],
                      threshold=threshold,
                      preprocessing_method=pre,  # type: ignore
                      preprocessing_configs=pre_configs.args,
                      inprocessing_method=inp,  # type: ignore
                      inprocessing_configs=inp_configs.args,
                      postprocessing_method=post,  # type: ignore
                      postprocessing_configs=post_configs.args,
                      eval_metric="tpr",  # TODO Change this
                      alpha=None,
                      artifacts=self.artifacts,  # type: ignore
                      artifacts_folder=method_folder,  # type: ignore
                  )
  
                  tuner = OptunaTuner(objective, sampler=self.sampler)
  
                  tuner.optimize(
                      n_trials=self.config.optimization.n_trials,
                      n_jobs=self.config.optimization.n_jobs,
                  )
  
                  if "results" in self.artifacts:
                      with open(
                          method_folder / "results.pickle", "wb"
                      ) as file:
                          pickle.dump(objective._models_results, file)
  
imports: import datetime
  import hashlib
  import json
  import pickle
  from pathlib import Path
  from typing import Iterable, Optional, Tuple, Union
  from hpt import OptunaTuner
  from omegaconf import DictConfig, ListConfig
  from optuna.samplers import BaseSampler
  from ..datasets.dataset import Dataset
  from ..methods.inprocessing.inprocessing import InProcessing
  from ..methods.postprocessing.identity import Identity
  from ..methods.postprocessing.postprocessing import PostProcessing
  from ..methods.postprocessing.threshold import Threshold
  from ..methods.preprocessing.identity import Identity
  from ..methods.preprocessing.preprocessing import PreProcessing
  from ..optimization import ObjectiveFunction
  from ..utils import ConfigReader, create_logger, import_object
label_desc: Use image node_id 6 for calling the Experiment obj's underlying member method code with example usage: obj.run() without return types