class_docstr: The main class for modelTuner
  
  Attributes:
      method: the optimization method. Currently only 'gradient_descent'
              is supported.
      loss:   the optimization loss function. Currently only 'mse' (one-day
              ahead prediction) is supported.
docstr: Main function for tuning the DLM model.
  
  Args:
      untunedDLM: The DLM object that needs tuning
      maxit: The maximum number of iteractions for gradient descent.
      step: the moving length at each iteraction.
  
  Returns:
      A tuned DLM object in unintialized status.
code: def tune(self, untunedDLM, maxit=100, step=1.0):
      """Main function for tuning the DLM model.
  
      Args:
          untunedDLM: The DLM object that needs tuning
          maxit: The maximum number of iteractions for gradient descent.
          step: the moving length at each iteraction.
  
      Returns:
          A tuned DLM object in unintialized status.
      """
      # make a deep copy of the original dlm
      tunedDLM = deepcopy(untunedDLM)
  
      if not tunedDLM.initialized:
          tunedDLM.fitForwardFilter()
      discounts = array(tunedDLM._getDiscounts())
      self.current_mse = tunedDLM._getMSE()
  
      # using gradient descent
      if self.method == "gradient_descent":
          # Disable all info and warning for faster processing.
          log_level = tunedDLM.getLoggingLevel()
          tunedDLM.setLoggingLevel("CRITICAL")
  
          for i in range(maxit):
              gradient = self.find_gradient(discounts, tunedDLM)
              discounts -= gradient * step
              discounts = list(map(lambda x: self.cutoff(x), discounts))
              tunedDLM._setDiscounts(discounts)
              tunedDLM.fitForwardFilter()
              self.current_mse = tunedDLM._getMSE()
  
          # Recover logger level
          tunedDLM.setLoggingLevel(log_level)
  
          if i < maxit - 1:
              tunedDLM._logger.info("Converge successfully!")
          else:
              tunedDLM._logger.warning(
                  "The algorithm stops without converging."
              )
              if (
                  min(discounts) <= 0.7 + self.err
                  or max(discounts) >= 1 - 2 * self.err
              ):
                  tunedDLM._logger.info(
                      "Possible reason: some discount is too close to 1 or 0.7"
                      " (0.7 is smallest discount that is permissible."
                  )
              else:
                  tunedDLM._logger.info(
                      "It might require more step to converge."
                      " Use tune(..., maixt = <a larger number>) instead."
                  )
  
      self.discounts = discounts
      tunedDLM._setDiscounts(discounts, change_component=True)
      return tunedDLM
  
file_path: pydlm/pydlm/tuner/dlmTuner.py
imports: from copy import deepcopy
  from numpy import array
  import logging
label_desc: Use image node_id 2 for calling the modelTuner obj's underlying member method code with example usage: obj.tune(untunedDLM, maxit, step) and returns: tunedDLM