class_docstr: A class to enable easy implementation of optimization over
  arbitrary parameter spaces and using arbitrary rewards.
  The optimization problem can be stated in pseudo code:
  
  Maximize_{param} reward(some_function(param))
  
  Note that 'reward(some_function(param))' can be considered as the objective
  which is to be maximized. The objective is a two part calculation in this framework
  
      - calculate a function for param
      - calculate the reward for the above
  
  Here is a more detailed mathematical explanation in more detailed pseudo code:
  
  Assume:
      - ``param`` is a (potentially multivariate) parameter in a parameter space ``param_iterable``
      - ``calc_with_param`` is a function which depends on ``param``
      - psudeo code: 'calc_result = calc_with_param(param, ...)'
      - psudeo code: 'obj_value = reward.apply(calc_result.data)'
  
  Goal:
      - optimize (maximize) 'obj_value' across all possible param
  
  Note that `reward` does not take ``param`` as an input and only applied to
  the updated data calculated using ``param``.
  
  The class initializes by passing an arbitrary ``reward``
  for optimization and a potentially multivariate parameter
  (given in ``param_iterable``) to optimize.
  
  The ``reward`` object is required to implement the ``apply`` method
  which is the case for this class:
  `~greykite.detection.detector.reward.Reward`
  
  The optimization method (``optimize_param``) is the main method in this class and
  works simply by iterating over ``param_iterable`` and calculating the reward
  to choose the optimal parameter.
  The class assumes that larger is better for the reward function, during optimization.
  
  The classes inherting this class, need to implement ``calc_with_param`` method to be able to use the optimizer and given that implementation.
  
  Parameters
  ----------
  reward : `~greykite.detection.detector.reward.Reward` or None, default None
      The reward to be used in the optimization.
  param_iterable : iterable or None, default None
      An iterable with every element being a parameter passed to the method
      ``calc_with_param`` which takes ``param`` as one of its arguments and
      ``data`` as the other.
      Each ``param`` can be a dictionary including values for a set of variables.
      The optimizer method (``optimize_param``) will iterate over all the
      parameters to find the best parameter in terms of the specified reward.
  
  Attributes
  ----------
  data : `dataclasses.dataclass` or None, default None
      A data class object which includes the data for fitting or
      prediction. Depending on the model, this data class might
      include various fields. A simple example is given in
      `~greykite.detection.detector.data.Data`
  fit_info : `dict`
      A dictionary which includes information about the fitted model.
      It is expected that this includes ``"full_param"`` after the fitting
      so that the `predict` function can use that param during the prediction
      and simply call `calc_with_param`.
      In that case the `predict` function does not need further implementation
      in child classes as it's already implemented in this class.
code: def __init__(self, reward=None, param_iterable=None):
      self.reward = reward
      self.param_iterable = param_iterable
      # Initialize attributes
      self.data = None
      self.fit_info = {"param_full": None}
  
file_path: greykite/greykite/detection/detector/optimizer.py
imports: import dataclasses
  from abc import abstractmethod
  from dataclasses import dataclass
  from typing import Optional
  from greykite.detection.detector.data import DetectorData
label_desc: Use image node_id 1 to create a new Optimizer object  with example: obj = Optimizer(reward, param_iterable)