class_docstr: Class to execute target functions which are python functions. Evaluates function for given
  configuration and resource limit.
  
  The target function can either return a float (the loss), or a tuple with the first element
  being a float and the second being additional run information. In a multi-objective
  setting, the float value is replaced by a list of floats.
  
  Parameters
  ----------
  target_function : Callable
      The target function.
  scenario : Scenario
  required_arguments : list[str], defaults to []
      A list of required arguments, which are passed to the target function.
class_docstr_tok: ["Class","to","execute","target","functions","which","are","python","functions",".","Evaluates","function","for","given","configuration","and","resource","limit",".","The","target","function","can","either","return","a","float","(","the","loss",")",",","or","a","tuple","with","the","first","element","being","a","float","and","the","second","being","additional","run","information",".","In","a","multi-objective","setting",",","the","float","value","is","replaced","by","a","list","of","floats",".","Parameters","--","--","--","--","--","target_function",":","Callable","The","target","function",".","scenario",":","Scenario","required_arguments",":","list","[","str","]",",","defaults","to","[","]","A","list","of","required","arguments",",","which","are","passed","to","the","target","function","."]
docstr: Calls the target function with pynisher if algorithm wall time limit or memory limit is
  set. Otherwise, the function is called directly.
  
  Parameters
  ----------
  config : Configuration
      Configuration to be passed to the target function.
  instance : str | None, defaults to None
      The Problem instance.
  budget : float | None, defaults to None
      A positive, real-valued number representing an arbitrary limit to the target function
      handled by the target function internally.
  seed : int, defaults to None
  dask_data_to_scatter: dict[str, Any]
      This kwargs must be empty when we do not use dask! ()
      When a user scatters data from their local process to the distributed network,
      this data is distributed in a round-robin fashion grouping by number of cores.
      Roughly speaking, we can keep this data in memory and then we do not have to (de-)serialize the data
      every time we would like to execute a target function with a big dataset.
      For example, when your target function has a big dataset shared across all the target function,
      this argument is very useful.
  
  Returns
  -------
  status : StatusType
      Status of the trial.
  cost : float | list[float]
      Resulting cost(s) of the trial.
  runtime : float
      The time the target function took to run.
  additional_info : dict
      All further additional trial information.
docstr_tok: ["Calls","the","target","function","with","pynisher","if","algorithm","wall","time","limit","or","memory","limit","is","set",".","Otherwise",",","the","function","is","called","directly",".","Parameters","--","--","--","--","--","config",":","Configuration","Configuration","to","be","passed","to","the","target","function",".","instance",":","str","|","None",",","defaults","to","None","The","Problem","instance",".","budget",":","float","|","None",",","defaults","to","None","A","positive",",","real-valued","number","representing","an","arbitrary","limit","to","the","target","function","handled","by","the","target","function","internally",".","seed",":","int",",","defaults","to","None","dask_data_to_scatter",":","dict","[","str",",","Any","]","This","kwargs","must","be","empty","when","we","do","not","use","dask","!","(",")","When","a","user","scatters","data","from","their","local","process","to","the","distributed","network",",","this","data","is","distributed","in","a","round-robin","fashion","grouping","by","number","of","cores",".","Roughly","speaking",",","we","can","keep","this","data","in","memory","and","then","we","do","not","have","to","(","de-",")","serialize","the","data","every","time","we","would","like","to","execute","a","target","function","with","a","big","dataset",".","For","example",",","when","your","target","function","has","a","big","dataset","shared","across","all","the","target","function",",","this","argument","is","very","useful",".","Returns","--","--","--","-","status",":","StatusType","Status","of","the","trial",".","cost",":","float","|","list","[","float","]","Resulting","cost","(","s",")","of","the","trial",".","runtime",":","float","The","time","the","target","function","took","to","run",".","additional_info",":","dict","All","further","additional","trial","information","."]
code: def run(
      self,
      config: Configuration,
      instance: str | None = None,
      budget: float | None = None,
      seed: int | None = None,
      **dask_data_to_scatter: dict[str, Any],
  ) -> tuple[StatusType, float | list[float], float, dict]:
      """Calls the target function with pynisher if algorithm wall time limit or memory limit is
      set. Otherwise, the function is called directly.
  
      Parameters
      ----------
      config : Configuration
          Configuration to be passed to the target function.
      instance : str | None, defaults to None
          The Problem instance.
      budget : float | None, defaults to None
          A positive, real-valued number representing an arbitrary limit to the target function
          handled by the target function internally.
      seed : int, defaults to None
      dask_data_to_scatter: dict[str, Any]
          This kwargs must be empty when we do not use dask! ()
          When a user scatters data from their local process to the distributed network,
          this data is distributed in a round-robin fashion grouping by number of cores.
          Roughly speaking, we can keep this data in memory and then we do not have to (de-)serialize the data
          every time we would like to execute a target function with a big dataset.
          For example, when your target function has a big dataset shared across all the target function,
          this argument is very useful.
  
      Returns
      -------
      status : StatusType
          Status of the trial.
      cost : float | list[float]
          Resulting cost(s) of the trial.
      runtime : float
          The time the target function took to run.
      additional_info : dict
          All further additional trial information.
      """
      # The kwargs are passed to the target function.
      kwargs: dict[str, Any] = {}
      kwargs.update(dask_data_to_scatter)
  
      if "seed" in self._required_arguments:
          kwargs["seed"] = seed
  
      if "instance" in self._required_arguments:
          kwargs["instance"] = instance
  
      if "budget" in self._required_arguments:
          kwargs["budget"] = budget
  
      # Presetting
      cost: float | list[float] = self._crash_cost
      runtime = 0.0
      additional_info = {}
      status = StatusType.CRASHED
  
      # If memory limit or walltime limit is set, we wanna use pynisher
      target_function: Callable
      if (
          self._memory_limit is not None
          or self._algorithm_walltime_limit is not None
      ):
          target_function = limit(
              self._target_function,
              memory=self._memory_limit,
              wall_time=self._algorithm_walltime_limit,
              wrap_errors=True,  # Hard to describe; see https://github.com/automl/pynisher
          )
      else:
          target_function = self._target_function
  
      # We don't want the user to change the configuration
      config_copy = copy.deepcopy(config)
  
      # Call target function
      try:
          start_time = time.time()
          rval = self(config_copy, target_function, kwargs)
          runtime = time.time() - start_time
          status = StatusType.SUCCESS
      except WallTimeoutException:
          status = StatusType.TIMEOUT
      except MemoryLimitException:
          status = StatusType.MEMORYOUT
      except Exception as e:
          cost = np.asarray(cost).squeeze().tolist()
          additional_info = {
              "traceback": traceback.format_exc(),
              "error": repr(e),
          }
          status = StatusType.CRASHED
  
      if status != StatusType.SUCCESS:
          return status, cost, runtime, additional_info
  
      if isinstance(rval, tuple):
          result, additional_info = rval
      else:
          result, additional_info = rval, {}
  
      # Do some sanity checking (for multi objective)
      error = f"Returned costs {result} does not match the number of objectives {self._objectives}."
  
      # If dict convert to array and make sure the order is correct
      if isinstance(result, dict):
          if len(result) != len(self._objectives):
              raise RuntimeError(error)
  
          ordered_cost: list[float] = []
          for name in self._objectives:
              if name not in result:
                  raise RuntimeError(
                      f"Objective {name} was not found in the returned costs."
                  )
  
              ordered_cost.append(result[name])
  
          result = ordered_cost
  
      if isinstance(result, list):
          if len(result) != len(self._objectives):
              raise RuntimeError(error)
  
      if isinstance(result, float):
          if (
              isinstance(self._objectives, list)
              and len(self._objectives) != 1
          ):
              raise RuntimeError(error)
  
      cost = result
  
      if cost is None:
          status = StatusType.CRASHED
          cost = self.crash_cost
  
      # We want to get either a float or a list of floats.
      cost = np.asarray(cost).squeeze().tolist()
  
      return status, cost, runtime, additional_info
  
imports: from __future__ import annotations
  from typing import Any, Callable
  import copy
  import inspect
  import math
  import time
  import traceback
  from functools import partial
  import numpy
  from ConfigSpace import Configuration
  from pynisher import MemoryLimitException, WallTimeoutException, limit
  from smac.runner.abstract_runner import StatusType
  from smac.runner.abstract_serial_runner import AbstractSerialRunner
  from smac.scenario import Scenario
  from smac.utils.logging import get_logger
label_desc: Use image node_id 3 for calling the TargetFunctionRunner obj's underlying member method code with example usage: obj.run(config, instance, budget, seed) and returns: status,  cost,  runtime,  additional_info, status,  cost,  runtime,  additional_info