class_docstr: Wrapper around a `ETS` model, which automatically detects
  the hyperparameters ``seasonal_periods``, ``error``, ``trend``, ``damped_trend`` and ``seasonal``.
docstr: generate [theta]. theta is a list of parameter combination [error, trend, damped_trend, seasonal]
docstr_tok: ["generate","[","theta","]",".","theta","is","a","list","of","parameter","combination","[","error",",","trend",",","damped_trend",",","seasonal","]"]
code: def generate_theta(self, train_data: TimeSeries) -> Iterator:
      """
      generate [theta]. theta is a list of parameter combination [error, trend, damped_trend, seasonal]
      """
      y = train_data.univariates[self.target_name].np_values
  
      # check the size of y
      n_samples = y.shape[0]
      if n_samples <= 3:
          self.information_criterion = InformationCriterion.AIC
  
      # auto-detect seasonality if desired, otherwise just get it from seasonal order
      if self.config.auto_seasonality:
          candidate_m = SeasonalityLayer.generate_theta(
              self, train_data=train_data
          )
          m, _, _ = SeasonalityLayer.evaluate_theta(
              self, thetas=candidate_m, train_data=train_data
          )
      else:
          if self.model.config.seasonal_periods is None:
              m = 1
          else:
              m = max(1, self.model.config.seasonal_periods)
  
      # set the parameters ranges for error, trend, damped_trend and seasonal
      if np.any(y <= 0):
          E_range = ["add"]
          T_range = ["add", None]
      else:
          E_range = ["add", "mul"]
          if self.config.allow_multiplicative_trend:
              T_range = ["add", "mul", None]
          else:
              T_range = ["add", None]
  
      if m <= 1 or y.shape[0] <= m:
          m = 1
          S_range = [None]
      elif np.any(y <= 0):
          S_range = ["add"]
      else:
          S_range = ["add", "mul"]
      D_range = [True, False]
  
      if not self.config.auto_error:
          E_range = [self.model.config.error]
      if not self.config.auto_trend:
          T_range = [self.model.config.trend]
      if not self.config.auto_seasonal:
          S_range = [self.model.config.seasonal]
      if not self.config.auto_damped:
          D_range = [self.model.config.damped_trend]
  
      # Construct a grid search object
      param_values = OrderedDict(
          error=E_range,
          trend=T_range,
          seasonal=S_range,
          damped=D_range,
          m=[m],
      )
      restrictions = [dict(trend=None, damped=True)]
      if self.config.additive_only:
          restrictions.extend(
              [
                  dict(error="mul"),
                  dict(trend="mul"),
                  dict(seasonal="mul"),
              ]
          )
      if self.config.restrict:
          restrictions.append(dict(error="add", trend="mul"))
          restrictions.append(dict(error="add", seasonal="mul"))
          restrictions.append(
              dict(error="mul", trend="mul", seasonal="add")
          )
      return iter(
          GridSearch(
              param_values=param_values, restrictions=restrictions
          )
      )
  
imports: from collections import OrderedDict
  import logging
  from typing import Union, Iterator, Tuple
  import numpy
  import pandas
  from merlion.models.forecast.ets import ETS
  from merlion.models.automl.base import InformationCriterion, ICConfig, ICAutoMLForecaster
  from merlion.models.automl.search import GridSearch
  from merlion.models.automl.seasonality import PeriodicityStrategy, SeasonalityConfig, SeasonalityLayer
  from merlion.utils import TimeSeries
label_desc: Use image node_id 2 for calling the AutoETS obj's underlying member method code with example usage: obj.generate_theta(train_data) and returns: iter