class_docstr: Evaluator of Objective that requires train and test data for metric evaluation.
  Its role is to prepare graph on train-data and then evaluate metrics on test data.
  
  :param objective: Objective for evaluating metrics on pipelines.
  :param data_producer: Producer of data folds, each fold is a tuple of (train_data, test_data).
  If it returns a single fold, it's effectively a hold-out validation. For many folds it's k-folds.
  :param time_constraint: Optional time constraint for pipeline.fit.
  :param validation_blocks: Number of validation blocks, optional, used only for time series validation.
  :param pipelines_cache: Cache manager for fitted models, optional.
  :param preprocessing_cache: Cache manager for optional preprocessing encoders and imputers, optional.
  :param eval_n_jobs: number of jobs used to evaluate the objective.
  :params do_unfit: unfit graph after evaluation
class_docstr_tok: ["Evaluator","of","Objective","that","requires","train","and","test","data","for","metric","evaluation",".","Its","role","is","to","prepare","graph","on","train-data","and","then","evaluate","metrics","on","test","data",".",":","param","objective",":","Objective","for","evaluating","metrics","on","pipelines",".",":","param","data_producer",":","Producer","of","data","folds",",","each","fold","is","a","tuple","of","(","train_data",",","test_data",")",".","If","it","returns","a","single","fold",",","it","'s","effectively","a","hold-out","validation",".","For","many","folds","it","'s","k-folds",".",":","param","time_constraint",":","Optional","time","constraint","for","pipeline.fit",".",":","param","validation_blocks",":","Number","of","validation","blocks",",","optional",",","used","only","for","time","series","validation",".",":","param","pipelines_cache",":","Cache","manager","for","fitted","models",",","optional",".",":","param","preprocessing_cache",":","Cache","manager","for","optional","preprocessing","encoders","and","imputers",",","optional",".",":","param","eval_n_jobs",":","number","of","jobs","used","to","evaluate","the","objective",".",":","params","do_unfit",":","unfit","graph","after","evaluation"]
docstr: Fit pipeline before metric evaluation can be performed.
  :param graph: pipeline for train & validation
  :param train_data: InputData for training pipeline
  :param fold_id: id of the fold in cross-validation, used for cache requests.
  :param n_jobs: number of parallel jobs for preparation
docstr_tok: ["Fit","pipeline","before","metric","evaluation","can","be","performed",".",":","param","graph",":","pipeline","for","train","&","validation",":","param","train_data",":","InputData","for","training","pipeline",":","param","fold_id",":","id","of","the","fold","in","cross-validation",",","used","for","cache","requests",".",":","param","n_jobs",":","number","of","parallel","jobs","for","preparation"]
code: def prepare_graph(
      self,
      graph: Pipeline,
      train_data: InputData,
      fold_id: Optional[int] = None,
      n_jobs: int = -1,
  ) -> Pipeline:
      """
      Fit pipeline before metric evaluation can be performed.
      :param graph: pipeline for train & validation
      :param train_data: InputData for training pipeline
      :param fold_id: id of the fold in cross-validation, used for cache requests.
      :param n_jobs: number of parallel jobs for preparation
      """
      if graph.is_fitted:
          # the expected behaviour for the remote evaluation
          return graph
  
      graph.unfit()
  
      # load preprocessing
      graph.try_load_from_cache(
          self._pipelines_cache, self._preprocessing_cache, fold_id
      )
      graph.fit(
          train_data,
          n_jobs=n_jobs,
          time_constraint=self._time_constraint,
      )
  
      if self._pipelines_cache is not None:
          self._pipelines_cache.save_pipeline(graph, fold_id)
      if self._preprocessing_cache is not None:
          self._preprocessing_cache.add_preprocessor(graph, fold_id)
  
      return graph
  
imports: import traceback
  from datetime import timedelta
  from typing import Callable, Iterable, Optional, Tuple
  import numpy
  from golem.core.log import default_log, is_test_session
  from golem.core.optimisers.fitness import Fitness
  from golem.core.optimisers.objective.objective import Objective, to_fitness
  from golem.core.optimisers.objective.objective_eval import ObjectiveEvaluate
  from fedot.core.caching.pipelines_cache import OperationsCache
  from fedot.core.caching.preprocessing_cache import PreprocessingCache
  from fedot.core.data.data import InputData
  from fedot.core.operations.model import Model
  from fedot.core.pipelines.pipeline import Pipeline
  from fedot.utilities.debug import is_recording_mode, save_debug_info_for_pipeline
label_desc: Use image node_id 3 for calling the PipelineObjectiveEvaluate obj's underlying member method code with example usage: obj.prepare_graph(graph, train_data, fold_id, n_jobs) and returns: graph, graph