docstr: Performs a grid search over all hyper parameters to select the best model,
  using the fitted values on the training series `ts`.
  
  
  Uses 'LocalForecastingModel.gridsearch' with 'use_fitted_values=True' and 'metric=metrics.mae`.
  
  Parameters
  ----------
  ts
      The TimeSeries on which the model will be tested.
  thetas
      A list of thetas to loop over. Defaults to [1, 2, 3].
  m
      Optionally, the season used to decompose the time series.
  normalization
      If `True`, the data is normalized so that the mean is 1. Defaults to `True`.
  n_jobs
      The number of jobs to run in parallel. Parallel jobs are created only when there are two or more theta
      values to be evaluated. Each job will instantiate, train, and evaluate a different instance of the model.
      Defaults to `1` (sequential). Setting the parameter to `-1` means using all the available cores.
  
  Returns
  -------
  FourTheta
      The best performing model on the time series.
docstr_tok: ["Performs","a","grid","search","over","all","hyper","parameters","to","select","the","best","model",",","using","the","fitted","values","on","the","training","series","`","ts","`",".","Uses","'LocalForecastingModel.gridsearch","'","with","'use_fitted_values=True","'","and","'metric=metrics.mae","`",".","Parameters","--","--","--","--","--","ts","The","TimeSeries","on","which","the","model","will","be","tested",".","thetas","A","list","of","thetas","to","loop","over",".","Defaults","to","[","1",",","2",",","3","]",".","m","Optionally",",","the","season","used","to","decompose","the","time","series",".","normalization","If","`","True","`",",","the","data","is","normalized","so","that","the","mean","is","1",".","Defaults","to","`","True","`",".","n_jobs","The","number","of","jobs","to","run","in","parallel",".","Parallel","jobs","are","created","only","when","there","are","two","or","more","theta","values","to","be","evaluated",".","Each","job","will","instantiate",",","train",",","and","evaluate","a","different","instance","of","the","model",".","Defaults","to","`","1","`","(","sequential",")",".","Setting","the","parameter","to","`","-1","`","means","using","all","the","available","cores",".","Returns","--","--","--","-","FourTheta","The","best","performing","model","on","the","time","series","."]
code: def select_best_model(
      ts: TimeSeries,
      thetas: Optional[List[int]] = None,
      m: Optional[int] = None,
      normalization: bool = True,
      n_jobs: int = 1,
  ) -> "FourTheta":
      """
      Performs a grid search over all hyper parameters to select the best model,
      using the fitted values on the training series `ts`.
  
  
      Uses 'LocalForecastingModel.gridsearch' with 'use_fitted_values=True' and 'metric=metrics.mae`.
  
      Parameters
      ----------
      ts
          The TimeSeries on which the model will be tested.
      thetas
          A list of thetas to loop over. Defaults to [1, 2, 3].
      m
          Optionally, the season used to decompose the time series.
      normalization
          If `True`, the data is normalized so that the mean is 1. Defaults to `True`.
      n_jobs
          The number of jobs to run in parallel. Parallel jobs are created only when there are two or more theta
          values to be evaluated. Each job will instantiate, train, and evaluate a different instance of the model.
          Defaults to `1` (sequential). Setting the parameter to `-1` means using all the available cores.
  
      Returns
      -------
      FourTheta
          The best performing model on the time series.
      """
      # Only import if needed
      from darts.metrics import mae
  
      if thetas is None:
          thetas = [1, 2, 3]
      if (ts.values(copy=False) <= 0).any():
          drift_mode = [TrendMode.LINEAR]
          model_mode = [ModelMode.ADDITIVE]
          season_mode = [SeasonalityMode.ADDITIVE]
          logger.warning(
              "The given TimeSeries has negative values. The method will only test "
              "linear trend and additive modes."
          )
      else:
          season_mode = [season for season in SeasonalityMode]
          model_mode = [model for model in ModelMode]
          drift_mode = [trend for trend in TrendMode]
  
      theta = FourTheta.gridsearch(
          {
              "theta": thetas,
              "model_mode": model_mode,
              "season_mode": season_mode,
              "trend_mode": drift_mode,
              "seasonality_period": [m],
              "normalization": [normalization],
          },
          ts,
          use_fitted_values=True,
          metric=mae,
          n_jobs=n_jobs,
      )
      return theta
  
imports: import math
  from typing import List, Optional
  import numpy
  import statsmodels.tsa.holtwinters
  from darts.logging import get_logger, raise_if_not, raise_log
  from darts.models.forecasting.forecasting_model import LocalForecastingModel
  from darts.timeseries import TimeSeries
  from darts.utils.statistics import check_seasonality, extract_trend_and_seasonality, remove_from_series
  from darts.utils.utils import ModelMode, SeasonalityMode, TrendMode
label_desc: Use image node_id 4 for calling the FourTheta obj's underlying member method code with example usage: obj.select_best_model(ts, thetas, m, normalization, n_jobs) and returns: theta