code: def _parallelFitTasks(
      estimator: Estimator,
      train: DataFrame,
      evaluator: Evaluator,
      validation: DataFrame,
      epm: Sequence["ParamMap"],
  ) -> List[Callable[[], Tuple[int, float]]]:
      """
      Creates a list of callables which can be called from different threads to fit and evaluate
      an estimator in parallel. Each callable returns an `(index, metric)` pair.
  
      Parameters
      ----------
      est : :py:class:`pyspark.ml.baseEstimator`
          he estimator to be fit.
      train : :py:class:`pyspark.sql.DataFrame`
          DataFrame, training data set, used for fitting.
      eva : :py:class:`pyspark.ml.evaluation.Evaluator`
          used to compute `metric`
      validation : :py:class:`pyspark.sql.DataFrame`
          DataFrame, validation data set, used for evaluation.
      epm : :py:class:`collections.abc.Sequence`
          Sequence of ParamMap, params maps to be used during fitting & evaluation.
      collectSubModel : bool
          Whether to collect sub model.
  
      Returns
      -------
      tuple
          (int, float), an index into `epm` and the associated metric value.
      """
  
      active_session = SparkSession.getActiveSession()
  
      if active_session is None:
          raise RuntimeError(
              "An active SparkSession is required for running cross valiator fit tasks."
          )
  
      def get_single_task(
          index: int, param_map: Any
      ) -> Callable[[], Tuple[int, float]]:
          def single_task() -> Tuple[int, float]:
              if not is_remote():
                  # Active session is thread-local variable, in background thread the active session
                  # is not set, the following line sets it as the main thread active session.
                  active_session._jvm.SparkSession.setActiveSession(  # type: ignore[union-attr]
                      active_session._jsparkSession  # type: ignore[union-attr]
                  )
  
              model = estimator.fit(train, param_map)
              metric = evaluator.evaluate(
                  model.transform(validation, param_map)  # type: ignore[union-attr]
              )
              return index, metric
  
          return single_task
  
      return [
          get_single_task(index, param_map)
          for index, param_map in enumerate(epm)
      ]
  
imports: from multiprocessing.pool import ThreadPool
  from typing import Any, Callable, Dict, List, Optional, Sequence, Tuple, Union, cast, TYPE_CHECKING
  import numpy
  import pandas
  from pyspark import keyword_only, since, inheritable_thread_target
  from pyspark.ml.connect import Estimator, Model
  from pyspark.ml.connect.base import Evaluator
  from pyspark.ml.connect.io_utils import MetaAlgorithmReadWrite, ParamsReadWrite
  from pyspark.ml.param import Params, Param, TypeConverters
  from pyspark.ml.param.shared import HasParallelism, HasSeed
  from pyspark.sql.functions import col, lit, rand
  from pyspark.sql.dataframe import DataFrame
  from pyspark.sql import SparkSession
  from pyspark.sql.utils import is_remote
label_desc: Use image node_id 1 for calling a global function with example usage: _parallelFitTasks(estimator, train, evaluator, validation, epm) and returns: unknown, single_task, index,  metric