code: def run_configuration(
      config_file: PathOrStr,
      local: bool = False,
      overwrite: bool = True,
      queue: str = "all.q",  # noqa: C901
      hosts: Optional[List[str]] = None,
      write_summary: bool = True,
      quiet: bool = False,
      ablation: int = 0,
      resume: bool = False,
      log_level: int = logging.INFO,
  ) -> List[str]:
      """
      Run jobs specified in the configuration file locally or on the grid.
  
      Parameters
      ----------
      config_file : :class:`skll.types.PathOrStr`
          Path to the configuration file we would like to use.
      local : bool, default=False
          Should this be run locally instead of on the cluster?
      overwrite : bool, default=True
          If the model files already exist, should we overwrite
          them instead of re-using them?
      queue : str, default="all.q"
          The DRMAA queue to use if we're running on the cluster.
      hosts : Optional[List[str]], default=None
          If running on the cluster, these are the machines we should use.
      write_summary : bool, default=True
          Write a TSV file with a summary of the results.
      quiet : bool, default=False
          Suppress printing of "Loading..." messages.
      ablation : int, default=0
          Number of features to remove when doing an ablation
          experiment. If positive, we will perform repeated ablation
          runs for all combinations of features removing the
          specified number at a time. If ``None``, we will use all
          combinations of all lengths. If 0, the default, no
          ablation is performed. If negative, a ``ValueError`` is
          raised.
      resume : bool, default=False
          If result files already exist for an experiment, do not
          overwrite them. This is very useful when doing a large
          ablation experiment and part of it crashes.
      log_level : int, default=logging.INFO
          The level for logging messages.
  
      Returns
      -------
      result_json_paths : List[str]
          A list of paths to .json results files for each variation in the
          experiment.
  
      Raises
      ------
      ValueError
          If value for ``"ablation"`` is not a positive int or ``None``.
      OSError
          If the lenth of the ``FeatureSet`` name > 210.
      """
      try:
          # Read configuration
          (
              experiment_name,
              task,
              sampler,
              fixed_sampler_parameters,
              feature_hasher,
              hasher_features,
              id_col,
              label_col,
              train_set_name,
              test_set_name,
              suffix,
              featuresets,
              do_shuffle,
              model_path,
              do_grid_search,
              grid_objectives,
              probability,
              pipeline,
              results_path,
              pos_label,
              feature_scaling,
              min_feature_count,
              folds_file,
              grid_search_jobs,
              grid_search_folds,
              cv_folds,
              cv_seed,
              save_cv_folds,
              save_cv_models,
              use_folds_file_for_grid_search,
              do_stratified_folds,
              fixed_parameter_list,
              param_grid_list,
              featureset_names,
              learners,
              prediction_dir,
              log_path,
              train_path,
              test_path,
              ids_to_floats,
              class_map,
              custom_learner_path,
              custom_metric_path,
              learning_curve_cv_folds_list,
              learning_curve_train_sizes,
              output_metrics,
              save_votes,
              wandb_credentials,
          ) = parse_config_file(config_file, log_level=log_level)
  
          # get the main experiment logger that will already have been
          # created by the configuration parser so we don't need anything
          # except the name `experiment`.
          logger = get_skll_logger("experiment")
          wandb_logger = WandbLogger(wandb_credentials)
          wandb_logger.log_configuration(
              {
                  "experiment_name": experiment_name,
                  "task": task,
                  "learners": learners,
              }
          )
  
          # Check if we have gridmap
          if not local and not _HAVE_GRIDMAP:
              local = True
              logger.warning(
                  "gridmap 0.10.1+ not available. Forcing local "
                  "mode.  To run things on a DRMAA-compatible "
                  "cluster, install gridmap>=0.10.1 via pip."
              )
  
          # No grid search or ablation for learning curve generation
          if task == "learning_curve":
              if ablation is None or ablation > 0:
                  ablation = 0
                  logger.warning(
                      "Ablating features is not supported during "
                      "learning curve generation. Ignoring."
                  )
  
          # if we just had a train file and a test file, there are no real featuresets
          # in which case there are no features to ablate
          if len(featuresets) == 1 and len(featuresets[0]) == 1:
              if ablation is None or ablation > 0:
                  ablation = 0
                  logger.warning(
                      "Not enough featuresets for ablation. Ignoring."
                  )
  
          # if performing ablation, expand featuresets to include combinations of
          # features within those sets
          if ablation is None or ablation > 0:
              # Make new feature set lists so that we can iterate without issue
              expanded_fs = []
              expanded_fs_names = []
              for featureset_prefix_list, featureset_name in zip(
                  featuresets, featureset_names
              ):
                  featureset_prefix_sorted_list = sorted(
                      featureset_prefix_list
                  )
                  featureset_prefix_set = set(
                      featureset_prefix_sorted_list
                  )
                  # Expand to all feature combinations if ablation is None
                  if ablation is None:
                      for i in range(
                          1, len(featureset_prefix_sorted_list)
                      ):
                          for excluded_features in combinations(
                              featureset_prefix_sorted_list, i
                          ):
                              expanded_fs.append(
                                  sorted(
                                      featureset_prefix_set
                                      - set(excluded_features)
                                  )
                              )
                              expanded_fs_names.append(
                                  f"{featureset_name}_minus_"
                                  f"{_munge_featureset_name(excluded_features)}"
                              )
                  # Otherwise, just expand removing the specified number at a time
                  else:
                      for excluded_features in combinations(
                          featureset_prefix_sorted_list, ablation
                      ):
                          expanded_fs.append(
                              sorted(
                                  featureset_prefix_set
                                  - set(excluded_features)
                              )
                          )
                          expanded_fs_names.append(
                              f"{featureset_name}_minus_"
                              f"{_munge_featureset_name(excluded_features)}"
                          )
                  # Also add version with nothing removed as baseline
                  expanded_fs.append(featureset_prefix_sorted_list)
                  expanded_fs_names.append(f"{featureset_name}_all")
  
              # Replace original feature set lists
              featuresets = expanded_fs
              featureset_names = expanded_fs_names
          elif ablation < 0:
              raise ValueError(
                  'Value for "ablation" argument must be either '
                  "positive integer or None."
              )
  
          # the list of jobs submitted (if running on grid)
          if not local:
              jobs = []
  
          # the list to hold the paths to all the result json files
          result_json_paths = []
  
          # check if the length of the featureset_name exceeds the maximum length
          # allowed
          for featureset_name in featureset_names:
              if len(featureset_name) > 210:
                  raise OSError(
                      f'System generated file length "{featureset_name}" '
                      "exceeds the maximum length supported.  Please specify "
                      'names of your datasets with "featureset_names".  If you '
                      "are running ablation experiment, please reduce the "
                      'length of the features in "featuresets" because the '
                      "auto-generated name would be longer than the file system"
                      " can handle"
                  )
  
          # if the task is learning curve, and ``metrics`` was specified, then
          # assign the value of ``metrics`` to ``grid_objectives`` - this lets
          # us piggyback on the parallelization of the objectives that is already
          # set up for us to use
          if task == "learning_curve" and len(output_metrics) > 0:
              grid_objectives = output_metrics
  
          # if there were no grid objectives provided, just set it to
          # a list containing a single None so as to allow the parallelization
          # to proceeed and to pass the correct default value of grid_objective
          # down to _classify_featureset().
          grid_objectives_extra: Union[List[str], List[None]]
          grid_objectives_extra = (
              [None] if not grid_objectives else grid_objectives
          )
  
          # Run each featureset-learner-objective combination
          for featureset, featureset_name in zip(
              featuresets, featureset_names
          ):
              for learner_num, learner_name in enumerate(learners):
                  for grid_objective in grid_objectives_extra:
                      # for the individual job name, we need to add the feature set name
                      # and the learner name
                      if (
                          grid_objective is None
                          or len(grid_objectives_extra) == 1
                      ):
                          job_name_components = [
                              experiment_name,
                              featureset_name,
                              learner_name,
                          ]
                      else:
                          job_name_components = [
                              experiment_name,
                              featureset_name,
                              learner_name,
                              grid_objective,
                          ]
  
                      job_name = "_".join(job_name_components)
  
                      # change the prediction prefix to include the feature set
                      prediction_prefix = str(
                          Path(prediction_dir) / job_name
                      )
  
                      # the log file that stores the actual output of this script (e.g.,
                      # the tuned parameters, what kind of experiment was run, etc.)
                      logfile = Path(log_path) / f"{job_name}.log"
  
                      # Figure out result json file path
                      result_json_path = (
                          Path(results_path)
                          / f"{job_name}.results.json"
                      )
  
                      # save the path to the results json file that will be written
                      result_json_paths.append(str(result_json_path))
  
                      # If result file already exists and we're resuming, move on
                      if resume and (
                          result_json_path.exists()
                          and result_json_path.stat().st_size
                      ):
                          logger.info(
                              "Running in resume mode and "
                              f"{result_json_path} exists, so skipping "
                              "job."
                          )
                          continue
  
                      # create job if we're doing things on the grid
                      job_args: Dict[str, Any] = {}
                      job_args["experiment_name"] = experiment_name
                      job_args["task"] = task
                      job_args["sampler"] = sampler
                      job_args["feature_hasher"] = feature_hasher
                      job_args["hasher_features"] = hasher_features
                      job_args["job_name"] = job_name
                      job_args["featureset"] = featureset
                      job_args["featureset_name"] = featureset_name
                      job_args["learner_name"] = learner_name
                      job_args["train_path"] = train_path
                      job_args["test_path"] = test_path
                      job_args["train_set_name"] = train_set_name
                      job_args["test_set_name"] = test_set_name
                      job_args["shuffle"] = do_shuffle
                      job_args["model_path"] = model_path
                      job_args["prediction_prefix"] = prediction_prefix
                      job_args["grid_search"] = do_grid_search
                      job_args["grid_objective"] = grid_objective
                      job_args["output_metrics"] = output_metrics
                      job_args["suffix"] = suffix
                      job_args["log_file"] = logfile
                      job_args["log_level"] = log_level
                      job_args["probability"] = probability
                      job_args["pipeline"] = pipeline
                      job_args["save_votes"] = save_votes
                      job_args["results_path"] = results_path
                      job_args["sampler_parameters"] = (
                          fixed_sampler_parameters
                          if fixed_sampler_parameters
                          else dict()
                      )
                      job_args["fixed_parameters"] = (
                          fixed_parameter_list[learner_num]
                          if fixed_parameter_list
                          else dict()
                      )
                      job_args["param_grid"] = (
                          param_grid_list[learner_num]
                          if param_grid_list
                          else None
                      )
                      job_args["pos_label"] = pos_label
                      job_args["overwrite"] = overwrite
                      job_args["feature_scaling"] = feature_scaling
                      job_args["min_feature_count"] = min_feature_count
                      job_args["grid_search_jobs"] = grid_search_jobs
                      job_args["grid_search_folds"] = grid_search_folds
                      job_args["folds_file"] = folds_file
                      job_args["cv_folds"] = cv_folds
                      job_args["cv_seed"] = cv_seed
                      job_args["save_cv_folds"] = save_cv_folds
                      job_args["save_cv_models"] = save_cv_models
                      job_args[
                          "use_folds_file_for_grid_search"
                      ] = use_folds_file_for_grid_search
                      job_args[
                          "do_stratified_folds"
                      ] = do_stratified_folds
                      job_args["label_col"] = label_col
                      job_args["id_col"] = id_col
                      job_args["ids_to_floats"] = ids_to_floats
                      job_args["quiet"] = quiet
                      job_args["class_map"] = class_map
                      job_args[
                          "custom_learner_path"
                      ] = custom_learner_path
                      job_args[
                          "custom_metric_path"
                      ] = custom_metric_path
                      job_args[
                          "learning_curve_cv_folds"
                      ] = learning_curve_cv_folds_list[learner_num]
                      job_args[
                          "learning_curve_train_sizes"
                      ] = learning_curve_train_sizes
  
                      if not local:
                          jobs.append(
                              Job(
                                  _classify_featureset,
                                  [job_args],
                                  num_slots=(
                                      MAX_CONCURRENT_PROCESSES
                                      if (
                                          do_grid_search
                                          or task == "learning_curve"
                                      )
                                      else 1
                                  ),
                                  name=job_name,
                                  queue=queue,
                              )
                          )
                      else:
                          _classify_featureset(job_args)
  
          # Call get_skll_logger again after _classify_featureset
          # calls are finished so that any warnings that may
          # happen after this point get correctly logged to the
          # main logger
          logger = get_skll_logger("experiment")
  
          # submit the jobs (if running on grid)
          if not local and _HAVE_GRIDMAP:
              if log_path:
                  job_results = process_jobs(
                      jobs, white_list=hosts, temp_dir=log_path
                  )
              else:
                  job_results = process_jobs(jobs, white_list=hosts)
              _check_job_results(job_results)
  
          # write out the summary results file
          if (
              task == "cross_validate" or task == "evaluate"
          ) and write_summary:
              summary_file_name = f"{experiment_name}_summary.tsv"
              with open(
                  Path(results_path) / summary_file_name,
                  "w",
                  newline="",
              ) as output_file:
                  _write_summary_file(
                      result_json_paths, output_file, ablation=ablation
                  )
          elif task == "learning_curve":
              output_file_name = f"{experiment_name}_summary.tsv"
              output_file_path = Path(results_path) / output_file_name
              with open(
                  output_file_path, "w", newline=""
              ) as output_file:
                  _write_learning_curve_file(
                      result_json_paths, output_file
                  )
  
              # generate the actual plot if we have the requirements installed
              generate_learning_curve_plots(
                  experiment_name, results_path, output_file_path
              )
  
      finally:
          # Close/remove any logger handlers
          close_and_remove_logger_handlers(
              get_skll_logger("experiment")
          )
  
      return result_json_paths
  
imports: from __future__ import annotations
  import datetime
  import json
  import logging
  from itertools import combinations
  from pathlib import Path
  from typing import Any, Dict, List, Optional, Union
  import matplotlib.pyplot
  import numpy
  from sklearn import __version__
  from sklearn.metrics import get_scorer_names
  from skll.config import parse_config_file
  from skll.config.utils import _munge_featureset_name
  from skll.learner import MAX_CONCURRENT_PROCESSES, Learner, load_custom_learner
  from skll.learner.voting import VotingLearner
  from skll.metrics import _CUSTOM_METRICS, register_custom_metric
  from skll.types import CrossValidateTaskResults, EvaluateTaskResults, PathOrStr, VotingCrossValidateTaskResults
  from skll.utils.logging import close_and_remove_logger_handlers, get_skll_logger
  from skll.version import __version__
  from ..utils.wandb import WandbLogger
  from .input import load_featureset
  from .output import _print_fancy_output, _write_learning_curve_file, _write_skll_folds, _write_summary_file, generate_learning_curve_plots
  from .utils import NumpyTypeEncoder, _check_job_results, _create_learner_result_dicts
label_desc: Use image node_id 2 for calling a global function with example usage: run_configuration(config_file, local, overwrite, queue, hosts, write_summary, quiet, ablation, resume, log_level) and returns: result_json_paths