class_docstr: Base KTR model object with shared functionality for PyroVI method
  Parameters
  ----------
  level_knot_scale : float
      sigma for level; default to be .1
  level_segments : int
      the number of segments partitioned by the knots of level (trend)
  level_knot_distance : int
      the distance between every two knots of level (trend)
  level_knot_dates : array like
      list of pre-specified dates for the level knots
  seasonality : int, or list of int
      multiple seasonality
  seasonality_fs_order : int, or list of int
      fourier series order for seasonality
  seasonality_segments : int
      the number of segments partitioned by the knots of seasonality
  seasonal_initial_knot_scale : float
      scale parameter for seasonal regressors initial coefficient knots; default to be 1
  seasonal_knot_scale : float
      scale parameter for seasonal regressors drift of coefficient knots; default to be 0.1.
  regressor_col : array-like strings
      regressor columns
  regressor_sign : list
      list of signs with '=' for regular regressor, '+' for positive regressor, and '-' for negative regressor.
  regressor_init_knot_loc : list
      list of regressor knot pooling mean priors, default to be 0's
  regressor_init_knot_scale : list
      list of regressor knot pooling sigma's to control the pooling strength towards the grand mean of regressors;
      default to be 1.
  regressor_knot_scale : list
      list of regressor knot sigma priors; default to be 0.1.
  regression_segments : int
      the number of segments partitioned by the knots of regression
  regression_knot_distance : int
      the distance between every two knots of regression
  regression_knot_dates : array-like
      list of pre-specified dates for regression knots
  regression_rho : float
      sigma in the Gaussian kernel for the regression term
  degree of freedom : int
      degree of freedom for error t-distribution
  date_freq : str
      date frequency; if not supplied, pd.infer_freq will be used to imply the date frequency.
  coef_prior_list : list of dicts
      each dict in the list should have keys as
      'name', prior_start_tp_idx' (inclusive), KTRTimePointPriorKeys.PRIOR_END_TP_IDX.value (not inclusive),
      KTRTimePointPriorKeys.PRIOR_MEAN.value, KTRTimePointPriorKeys.PRIOR_SD.value, and KTRTimePointPriorKeys.PRIOR_REGRESSOR_COL.value
  residuals_scale_upper : float
  flat_multiplier : bool
      Default set as True. If False, we will adjust knot scale with a multiplier based on regressor volume
      around each knot; When True, set all multiplier as 1
  ktrlite_optim_args : dict
      the optimizing config for the ktrlite model (to fit level/seasonality). Default to be dict().
class_docstr_tok: ["Base","KTR","model","object","with","shared","functionality","for","PyroVI","method","Parameters","--","--","--","--","--","level_knot_scale",":","float","sigma","for","level",";","default","to","be",".1","level_segments",":","int","the","number","of","segments","partitioned","by","the","knots","of","level","(","trend",")","level_knot_distance",":","int","the","distance","between","every","two","knots","of","level","(","trend",")","level_knot_dates",":","array","like","list","of","pre-specified","dates","for","the","level","knots","seasonality",":","int",",","or","list","of","int","multiple","seasonality","seasonality_fs_order",":","int",",","or","list","of","int","fourier","series","order","for","seasonality","seasonality_segments",":","int","the","number","of","segments","partitioned","by","the","knots","of","seasonality","seasonal_initial_knot_scale",":","float","scale","parameter","for","seasonal","regressors","initial","coefficient","knots",";","default","to","be","1","seasonal_knot_scale",":","float","scale","parameter","for","seasonal","regressors","drift","of","coefficient","knots",";","default","to","be","0.1.","regressor_col",":","array-like","strings","regressor","columns","regressor_sign",":","list","list","of","signs","with","'=","'","for","regular","regressor",",","'+","'","for","positive","regressor",",","and","'-","'","for","negative","regressor",".","regressor_init_knot_loc",":","list","list","of","regressor","knot","pooling","mean","priors",",","default","to","be","0's","regressor_init_knot_scale",":","list","list","of","regressor","knot","pooling","sigma","'s","to","control","the","pooling","strength","towards","the","grand","mean","of","regressors",";","default","to","be","1.","regressor_knot_scale",":","list","list","of","regressor","knot","sigma","priors",";","default","to","be","0.1.","regression_segments",":","int","the","number","of","segments","partitioned","by","the","knots","of","regression","regression_knot_distance",":","int","the","distance","between","every","two","knots","of","regression","regression_knot_dates",":","array-like","list","of","pre-specified","dates","for","regression","knots","regression_rho",":","float","sigma","in","the","Gaussian","kernel","for","the","regression","term","degree","of","freedom",":","int","degree","of","freedom","for","error","t-distribution","date_freq",":","str","date","frequency",";","if","not","supplied",",","pd.infer_freq","will","be","used","to","imply","the","date","frequency",".","coef_prior_list",":","list","of","dicts","each","dict","in","the","list","should","have","keys","as","'name","'",",","prior_start_tp_idx","'","(","inclusive",")",",","KTRTimePointPriorKeys.PRIOR_END_TP_IDX.value","(","not","inclusive",")",",","KTRTimePointPriorKeys.PRIOR_MEAN.value",",","KTRTimePointPriorKeys.PRIOR_SD.value",",","and","KTRTimePointPriorKeys.PRIOR_REGRESSOR_COL.value","residuals_scale_upper",":","float","flat_multiplier",":","bool","Default","set","as","True",".","If","False",",","we","will","adjust","knot","scale","with","a","multiplier","based","on","regressor","volume","around","each","knot",";","When","True",",","set","all","multiplier","as","1","ktrlite_optim_args",":","dict","the","optimizing","config","for","the","ktrlite","model","(","to","fit","level\/seasonality",")",".","Default","to","be","dict","(",")","."]
docstr: Vectorized version of prediction math
  Parameters
  ----
  coefficient_method : str
      either "smooth" or "empirical". when "empirical" is used, curves are sampled/aggregated directly
      from beta posteriors; when "smooth" is used, first extract sampled/aggregated posteriors of knots
      then beta.
      this mainly impacts the aggregated estimation method; full bayesian should not be impacted
  include_error : bool
      if generating the noise samples
  store_prediction_array : bool
      if storing the prediction array
code: def predict(
      self,
      posterior_estimates,
      df,
      training_meta,
      prediction_meta,
      coefficient_method="smooth",
      include_error=False,
      store_prediction_array=False,
      **kwargs,
  ):
      """Vectorized version of prediction math
      Parameters
      ----
      coefficient_method : str
          either "smooth" or "empirical". when "empirical" is used, curves are sampled/aggregated directly
          from beta posteriors; when "smooth" is used, first extract sampled/aggregated posteriors of knots
          then beta.
          this mainly impacts the aggregated estimation method; full bayesian should not be impacted
      include_error : bool
          if generating the noise samples
      store_prediction_array : bool
          if storing the prediction array
      """
      ################################################################
      # Model Attributes
      ################################################################
      # FIXME: do we still need this?
      model = deepcopy(posterior_estimates)
      arbitrary_posterior_value = list(model.values())[0]
      num_sample = arbitrary_posterior_value.shape[0]
  
      ################################################################
      # Prediction Attributes
      ################################################################
      output_len = prediction_meta[
          PredictionMetaKeys.PREDICTION_DF_LEN.value
      ]
      prediction_start = prediction_meta[PredictionMetaKeys.START.value]
      date_array = training_meta[TrainingMetaKeys.DATE_ARRAY.value]
      num_of_observations = training_meta[
          TrainingMetaKeys.NUM_OF_OBS.value
      ]
      training_end = training_meta[TrainingMetaKeys.END.value]
  
      # Here assume dates are ordered and consecutive
      # if prediction_meta[PredictionMetaKeys.START.value] > self.training_end,
      # assume prediction starts right after train end
      if prediction_start > training_end:
          # time index for prediction start
          start = num_of_observations
      else:
          start = pd.Index(date_array).get_loc(prediction_start)
  
      new_tp = (
          np.arange(start + 1, start + output_len + 1)
          / num_of_observations
      )
      if include_error:
          # in-sample knots
          lev_knot_in = model.get(
              BaseSamplingParameters.LEVEL_KNOT.value
          )
          # TODO: hacky way; let's just assume last two knot distance is knots distance for all knots
          lev_knot_width = (
              self.knots_tp_level[-1] - self.knots_tp_level[-2]
          )
          # check whether we need to put new knots for simulation
          if new_tp[-1] >= self.knots_tp_level[-1] + lev_knot_width:
              # derive knots tp
              knots_tp_level_out = np.arange(
                  self.knots_tp_level[-1] + lev_knot_width,
                  new_tp[-1],
                  lev_knot_width,
              )
              new_knots_tp_level = np.concatenate(
                  [self.knots_tp_level, knots_tp_level_out]
              )
              lev_knot_out = np.random.laplace(
                  0,
                  self.level_knot_scale,
                  size=(lev_knot_in.shape[0], len(knots_tp_level_out)),
              )
              lev_knot_out = np.cumsum(
                  np.concatenate(
                      [lev_knot_in[:, -1].reshape(-1, 1), lev_knot_out],
                      axis=1,
                  ),
                  axis=1,
              )[:, 1:]
              lev_knot = np.concatenate(
                  [lev_knot_in, lev_knot_out], axis=1
              )
          else:
              new_knots_tp_level = self.knots_tp_level
              lev_knot = lev_knot_in
          kernel_level = sandwich_kernel(new_tp, new_knots_tp_level)
      else:
          lev_knot = model.get(BaseSamplingParameters.LEVEL_KNOT.value)
          kernel_level = sandwich_kernel(new_tp, self.knots_tp_level)
      obs_scale = model.get(BaseSamplingParameters.OBS_SCALE.value)
      obs_scale = obs_scale.reshape(-1, 1)
  
      # if self._seasonality is not None:
      # condition of seasonality is checked inside
      total_seas, seas_decomp = self._generate_seas(
          df,
          training_meta,
          self._seasonality_coef_knot_dates,
          self._seasonality_coef_knots,
          self._seasonality,
          self._seasonality_fs_order,
          self._seasonality_labels,
      )
  
      #     # seas is 1-d array, add the batch size back
      #     seas = np.expand_dims(seas, 0)
      # else:
      #     # follow component shapes
      #     seas = np.zeros((1, output_len))
  
      trend = np.matmul(lev_knot, kernel_level.transpose((1, 0)))
      regression = np.zeros(trend.shape)
      if self._num_of_regressors > 0:
          regressor_matrix = df.filter(
              items=self._regressor_col,
          ).values
          regressor_betas = self._get_regression_coefs_matrix(
              training_meta,
              posterior_estimates,
              coefficient_method,
              date_array=prediction_meta[
                  TrainingMetaKeys.DATE_ARRAY.value
              ],
          )
          regression = np.sum(
              regressor_betas * regressor_matrix, axis=-1
          )
  
      if include_error:
          epsilon = nct.rvs(
              self.degree_of_freedom,
              nc=0,
              loc=0,
              scale=obs_scale,
              size=(num_sample, len(new_tp)),
          )
          trend += epsilon
  
      pred_array = trend + total_seas + regression
  
      # if decompose output dictionary of components
      decomp_dict = {
          "prediction": pred_array,
          "trend": trend,
          "regression": regression,
      }
  
      # this is an input from ktrlite
      decomp_dict.update(seas_decomp)
  
      if store_prediction_array:
          self.pred_array = pred_array
      else:
          self.pred_array = None
  
      return decomp_dict
  
imports: from orbit.models.ktrlite import KTRLite
  import pandas
  import numpy
  import math
  from scipy.stats import nct
  from enum import Enum
  import torch
  import matplotlib.pyplot
  from copy import deepcopy
  from ..constants.constants import KTRTimePointPriorKeys, PredictMethod, TrainingMetaKeys, PredictionMetaKeys
  from ..exceptions import IllegalArgument, ModelException, PredictionException
  from ..utils.general import is_ordered_datetime
  from ..utils.kernels import gauss_kernel, sandwich_kernel
  from ..utils.features import make_seasonal_regressors
  from .model_template import ModelTemplate
  from ..estimators.pyro_estimator import PyroEstimatorSVI
  from ..models import KTRLite
  from orbit.constants.palette import OrbitPalette
  from ..utils.knots import get_knot_idx, get_knot_dates
  from ..utils.plot import orbit_style_decorator
label_desc: Use image node_id 19 for calling the KTRModel obj's underlying member method code with example usage: obj.predict(posterior_estimates, df, training_meta, prediction_meta, coefficient_method, include_error, store_prediction_array) and returns: decomp_dict