B
    `MY                 @   s   d Z ddlmZ ddlmZ ddlmZ ddlZddlm  mZ	 ddl
mZ ddl
mZ ddlmZ dd	lmZ dd
lmZ e	jZG dd dejZG dd deZdS )zSums of time-series models.    )absolute_import)division)print_functionN)	bijectors)distributions)util)	Parameter)StructuralTimeSeriesc                   s"   e Zd ZdZd	 fdd	Z  ZS )
AdditiveStateSpaceModela7  A state space model representing a sum of component state space models.

  A state space model (SSM) posits a set of latent (unobserved) variables that
  evolve over time with dynamics specified by a probabilistic transition model
  `p(z[t+1] | z[t])`. At each timestep, we observe a value sampled from an
  observation model conditioned on the current state, `p(x[t] | z[t])`. The
  special case where both the transition and observation models are Gaussians
  with mean specified as a linear function of the inputs, is known as a linear
  Gaussian state space model and supports tractable exact probabilistic
  calculations; see `tfp.distributions.LinearGaussianStateSpaceModel` for
  details.

  The `AdditiveStateSpaceModel` represents a sum of component state space
  models. Each of the `N` components describes a random process
  generating a distribution on observed time series `x1[t], x2[t], ..., xN[t]`.
  The additive model represents the sum of these
  processes, `y[t] = x1[t] + x2[t] + ... + xN[t] + eps[t]`, where
  `eps[t] ~ N(0, observation_noise_scale)` is an observation noise term.

  #### Mathematical Details

  The additive model concatenates the latent states of its component models.
  The generative process runs each component's dynamics in its own subspace of
  latent space, and then observes the sum of the observation models from the
  components.

  Formally, the transition model is linear Gaussian:

  ```
  p(z[t+1] | z[t]) ~ Normal(loc = transition_matrix.matmul(z[t]),
                            cov = transition_cov)
  ```

  where each `z[t]` is a latent state vector concatenating the component
  state vectors, `z[t] = [z1[t], z2[t], ..., zN[t]]`, so it has size
  `latent_size = sum([c.latent_size for c in components])`.

  The transition matrix is the block-diagonal composition of transition
  matrices from the component processes:

  ```
  transition_matrix =
    [[ c0.transition_matrix,  0.,                   ..., 0.                   ],
     [ 0.,                    c1.transition_matrix, ..., 0.                   ],
     [ ...                    ...                   ...                       ],
     [ 0.,                    0.,                   ..., cN.transition_matrix ]]
  ```

  and the noise covariance is similarly the block-diagonal composition of
  component noise covariances:

  ```
  transition_cov =
    [[ c0.transition_cov, 0.,                ..., 0.                ],
     [ 0.,                c1.transition_cov, ..., 0.                ],
     [ ...                ...                     ...               ],
     [ 0.,                0.,                ..., cN.transition_cov ]]
  ```

  The observation model is also linear Gaussian,

  ```
  p(y[t] | z[t]) ~ Normal(loc = observation_matrix.matmul(z[t]),
                          stddev = observation_noise_scale)
  ```

  This implementation assumes scalar observations, so
  `observation_matrix` has shape `[1, latent_size]`. The additive
  observation matrix simply concatenates the observation matrices from each
  component:

  ```
  observation_matrix =
    concat([c0.obs_matrix, c1.obs_matrix, ..., cN.obs_matrix], axis=-1)
  ```

  The effect is that each component observation matrix acts on the dimensions
  of latent state corresponding to that component, and the overall expected
  observation is the sum of the expected observations from each component.

  If `observation_noise_scale` is not explicitly specified, it is also computed
  by summing the noise variances of the component processes:

  ```
  observation_noise_scale = sqrt(sum([
    c.observation_noise_scale**2 for c in components]))
  ```

  #### Examples

  To construct an additive state space model combining a local linear trend
  and day-of-week seasonality component (note, the `StructuralTimeSeries`
  classes, e.g., `Sum`, provide a higher-level interface for this
  construction, which will likely be preferred by most users):

  ```
    num_timesteps = 30
    local_ssm = tfp.sts.LocalLinearTrendStateSpaceModel(
        num_timesteps=num_timesteps,
        level_scale=0.5,
        slope_scale=0.1,
        initial_state_prior=tfd.MultivariateNormalDiag(
            loc=[0., 0.], scale_diag=[1., 1.]))
    day_of_week_ssm = tfp.sts.SeasonalStateSpaceModel(
        num_timesteps=num_timesteps,
        num_seasons=7,
        initial_state_prior=tfd.MultivariateNormalDiag(
            loc=tf.zeros([7]), scale_diag=tf.ones([7])))
    additive_ssm = tfp.sts.AdditiveStateSpaceModel(
        component_ssms=[local_ssm, day_of_week_ssm],
        observation_noise_scale=0.1)

    y = additive_ssm.sample()
    print(y.shape)
    # => []
  ```

          Nr   FTc	                s  t |p
d}t j}	t j|d|	d}g }
|dkrNtdd D }|j}	dd D }|r|d tfd	d|D st	d

|n
d j|rt|tkr|
fddD 7 }
fdd}fdd}t jtfddD t jd}t jt j|ddggdd|	d |
rJt |
 t   W dQ R X  fdd}|dt jf dk	rt jd|	dfdd}nfdd}tt| j||||||||d
 W dQ R X dS )a`
  Build a state space model representing the sum of component models.

    Args:
      component_ssms: Python `list` containing one or more
        `tfd.LinearGaussianStateSpaceModel` instances. The components
        will in general implement different time-series models, with possibly
        different `latent_size`, but they must have the same `dtype`, event
        shape (`num_timesteps` and `observation_size`), and their batch shapes
        must broadcast to a compatible batch shape.
      constant_offset: scalar `float` `Tensor`, or batch of scalars,
        specifying a constant value added to the sum of outputs from the
        component models. This allows the components to model the shifted series
        `observed_time_series - constant_offset`.
        Default value: `0.`
      observation_noise_scale: Optional scalar `float` `Tensor` indicating the
        standard deviation of the observation noise. May contain additional
        batch dimensions, which must broadcast with the batch shape of elements
        in `component_ssms`. If `observation_noise_scale` is specified for the
        `AdditiveStateSpaceModel`, the observation noise scales of component
        models are ignored. If `None`, the observation noise scale is derived
        by summing the noise variances of the component models, i.e.,
        `observation_noise_scale = sqrt(sum(
        [ssm.observation_noise_scale**2 for ssm in component_ssms]))`.
      initial_state_prior: Optional instance of `tfd.MultivariateNormal`
        representing a prior distribution on the latent state at time
        `initial_step`. If `None`, defaults to the independent priors from
        component models, i.e.,
        `[component.initial_state_prior for component in component_ssms]`.
        Default value: `None`.
      initial_step: Optional scalar `int` `Tensor` specifying the starting
        timestep.
        Default value: 0.
      validate_args: Python `bool`. Whether to validate input
        with asserts. If `validate_args` is `False`, and the inputs are
        invalid, correct behavior is not guaranteed.
        Default value: `False`.
      allow_nan_stats: Python `bool`. If `False`, raise an
        exception if a statistic (e.g. mean/mode/etc...) is undefined for any
        batch member. If `True`, batch members with valid parameters leading to
        undefined statistics will return NaN for this statistic.
        Default value: `True`.
      name: Python `str` name prefixed to ops created by this class.
        Default value: "AdditiveStateSpaceModel".

    Raises:
      ValueError: if components have different `num_timesteps`.
    r
   constant_offset)valuenamedtypeNc             S   s   g | ]
}|j qS  )initial_state_prior).0ssmr   r   T/home/dcms/DCMS/lib/python3.7/site-packages/tensorflow_probability/python/sts/sum.py
<listcomp>   s    z4AdditiveStateSpaceModel.__init__.<locals>.<listcomp>c             S   s(   g | ] }t |jd k	rt |jqS )N)tfZget_static_valuenum_timesteps)r   r   r   r   r   r      s   r   c                s   g | ]}| kqS r   r   )r   Zcomponent_timesteps)r   r   r   r      s   zNAdditive model components must all have the same number of timesteps (saw: {})c                s    g | ]}t jj |jd dqS )zEAdditive model components must all have the same number of timesteps.)message)r   	debuggingZassert_equalr   )r   r   )r   r   r   r      s   c                s   t  fddD S )Nc                s   g | ]}|  qS r   )Z"get_transition_matrix_for_timestep)r   r   )tr   r   r   
  s   zRAdditiveStateSpaceModel.__init__.<locals>.transition_matrix_fn.<locals>.<listcomp>)tflZLinearOperatorBlockDiag)r   )component_ssms)r   r   transition_matrix_fn  s    
z>AdditiveStateSpaceModel.__init__.<locals>.transition_matrix_fnc                s   t  fddD S )Nc                s   g | ]}|  qS r   )Z!get_transition_noise_for_timestep)r   r   )r   r   r   r     s   zQAdditiveStateSpaceModel.__init__.<locals>.transition_noise_fn.<locals>.<listcomp>)sts_utilfactored_joint_mvn)r   )r   )r   r   transition_noise_fn  s    
z=AdditiveStateSpaceModel.__init__.<locals>.transition_noise_fnc                s   g | ]}|  qS r   )#get_observation_matrix_for_timestep)r   r   )initial_stepr   r   r     s   )r   r      )axis)r   c                s$   t tj fddD ddS )Nc                s   g | ]}|    qS r   )r!   Zto_dense)r   r   )broadcast_obs_matrixr   r   r   r   !  s   zSAdditiveStateSpaceModel.__init__.<locals>.observation_matrix_fn.<locals>.<listcomp>)r$   )r   ZLinearOperatorFullMatrixr   concat)r   )r%   r   )r   r   observation_matrix_fn  s    z?AdditiveStateSpaceModel.__init__.<locals>.observation_matrix_fn.observation_noise_scalec                s.   t jt fddD  dtjf dS )Nc                s   g | ]}|   qS r   )"get_observation_noise_for_timestepZmean)r   r   )r   r   r   r   -  s   zRAdditiveStateSpaceModel.__init__.<locals>.observation_noise_fn.<locals>.<listcomp>.)loc
scale_diag)tfdMultivariateNormalDiagsumr   newaxis)r   )r   r)   offset_vector)r   r   observation_noise_fn+  s    z>AdditiveStateSpaceModel.__init__.<locals>.observation_noise_fnc                s.   t tjtdg fddD  S )N)r+   r,   c                s   g | ]}|  qS r   )r*   )r   r   )r   r   r   r   6  s   zRAdditiveStateSpaceModel.__init__.<locals>.observation_noise_fn.<locals>.<listcomp>)r   Zsum_mvnsr-   r.   r   Z
zeros_like)r   )r   r1   )r   r   r2   1  s    
)
r   Ztransition_matrixZtransition_noiseZobservation_matrixZobservation_noiser   r"   validate_argsallow_nan_statsr   )r   
name_scoper   Zassert_same_float_dtypeZconvert_to_tensorr   r   r   all
ValueErrorformatr   lenbroadcast_batch_shapeint32Zonesr'   Zcontrol_dependenciesidentityr0   superr
   __init__)selfr   r   r)   r   r"   r3   r4   r   r   Z
assertionsZstatic_num_timestepsr   r    r:   r(   r2   )	__class__)r%   r   r"   r   r)   r1   r   r>      sl    9







z AdditiveStateSpaceModel.__init__)r   NNr   FTN)__name__
__module____qualname____doc__r>   __classcell__r   r   )r@   r   r
   $   s   v      r
   c                   sZ   e Zd ZdZd fdd	Zedd Zedd Zed	d
 ZdddZ	dddZ
  ZS )Suma  Sum of structural time series components.

  This class enables compositional specification of a structural time series
  model from basic components. Given a list of component models, it represents
  an additive model, i.e., a model of time series that may be decomposed into a
  sum of terms corresponding to the component models.

  Formally, the additive model represents a random process
  `g[t] = f1[t] + f2[t] + ... + fN[t] + eps[t]`, where the `f`'s are the
  random processes represented by the components, and
  `eps[t] ~ Normal(loc=0, scale=observation_noise_scale)` is an observation
  noise term. See the `AdditiveStateSpaceModel` documentation for mathematical
  details.

  This model inherits the parameters (with priors) of its components, and
  adds an `observation_noise_scale` parameter governing the level of noise in
  the observed time series.

  #### Examples

  To construct a model combining a local linear trend with a day-of-week effect:

  ```
    local_trend = tfp.sts.LocalLinearTrend(
        observed_time_series=observed_time_series,
        name='local_trend')
    day_of_week_effect = tfp.sts.Seasonal(
        num_seasons=7,
        observed_time_series=observed_time_series,
        name='day_of_week_effect')
    additive_model = tfp.sts.Sum(
        components=[local_trend, day_of_week_effect],
        observed_time_series=observed_time_series)

    print([p.name for p in additive_model.parameters])
    # => `[observation_noise_scale,
    #      local_trend_level_scale,
    #      local_trend_slope_scale,
    #      day_of_week_effect_drift_scale`]

    print(local_trend.latent_size,
          seasonal.latent_size,
          additive_model.latent_size)
    # => `2`, `7`, `9`
  ```

  Nc          
      sP  t |p
d6}|dk	r,t|\}}}nd\}}|dkrVtjt jd| dd}|dkrb|}dd |D }	t|	tt	|	krt
d	|	td
d |D }
td|ttj|dt gg}x@|D ]8}x2|jD ](}|td|j|j|j|jd qW qW || _|
| _|| _tt| j|tdd |D |d W dQ R X dS )aq  Specify a structural time series model representing a sum of components.

    Args:
      components: Python `list` of one or more StructuralTimeSeries instances.
        These must have unique names.
      constant_offset: optional scalar `float` `Tensor`, or batch of scalars,
        specifying a constant value added to the sum of outputs from the
        component models. This allows the components to model the shifted series
        `observed_time_series - constant_offset`. If `None`, this is set to the
        mean of the provided `observed_time_series`.
        Default value: `None`.
      observation_noise_scale_prior: optional `tfd.Distribution` instance
        specifying a prior on `observation_noise_scale`. If `None`, a heuristic
        default prior is constructed based on the provided
        `observed_time_series`.
        Default value: `None`.
      observed_time_series: optional `float` `Tensor` of shape
        `batch_shape + [T, 1]` (omitting the trailing unit dimension is also
        supported when `T > 1`), specifying an observed time series. This is
        used to set the constant offset, if not provided, and to construct a
        default heuristic `observation_noise_scale_prior` if not provided. May
        optionally be an instance of `tfp.sts.MaskedTimeSeries`, which includes
        a mask `Tensor` to specify timesteps with missing observations.
        Default value: `None`.
      name: Python `str` name of this model component; used as `name_scope`
        for ops created by this class.
        Default value: 'Sum'.

    Raises:
      ValueError: if components do not have unique names.
    rF   N)g        g      ?g{Gz?g       @)r+   scalec             S   s   g | ]
}|j qS r   )r   )r   cr   r   r   r     s    z Sum.__init__.<locals>.<listcomp>z%Components must have unique names: {}c             S   s   g | ]}|j |fqS r   )r   )r   rH   r   r   r   r     s    r)   )rG   z{}_{})r   priorbijectorc             S   s   g | ]
}|j qS r   )latent_size)r   	componentr   r   r   r     s    )
parametersrK   r   )r   r5   r   Zempirical_statisticsr-   Z	LogNormalmathlogr9   setr7   r8   collectionsOrderedDictr   tfbZChainZAffineScalarZSoftplusrM   appendr   rI   rJ   _components_components_by_name_constant_offsetr=   rF   r>   r/   )r?   
componentsr   Zobservation_noise_scale_priorZobserved_time_seriesr   Zobserved_meanZobserved_stddev_Zcomponent_namescomponents_by_namerM   rL   Z	parameter)r@   r   r   r>   w  sB    &


zSum.__init__c             C   s   | j S )z0List of component `StructuralTimeSeries` models.)rU   )r?   r   r   r   rX     s    zSum.componentsc             C   s   | j S )z2OrderedDict mapping component names to components.)rV   )r?   r   r   r   rZ     s    zSum.components_by_namec             C   s   | j S )z-Constant value subtracted from observed data.)rW   )r?   r   r   r   r     s    zSum.constant_offsetr   c       
   	      s   t d| | |  fdd| jD }|dd }g }xF| jD ]<}t|j}|d| }	||d }||j||	|d qBW W dQ R X |S )a  Build an ordered list of Distribution instances for component models.

    Args:
      num_timesteps: Python `int` number of timesteps to model.
      param_vals: a list of `Tensor` parameter values in order corresponding to
        `self.parameters`, or a dict mapping from parameter names to values.
      initial_step: optional `int` specifying the initial timestep to model.
        This is relevant when the model contains time-varying components,
        e.g., holidays or seasonality.

    Returns:
      component_ssms: a Python list of `LinearGaussianStateSpaceModel`
        Distribution objects, in order corresponding to `self.components`.
    !make_component_state_space_modelsc                s   g | ]} |j  qS r   )r   )r   p)	param_mapr   r   r     s    z9Sum.make_component_state_space_models.<locals>.<listcomp>r#   N)
param_valsr"   )r   r5   Z_canonicalize_param_vals_as_maprM   rX   r9   rT   Zmake_state_space_model)
r?   r   r^   r"   param_vals_listZremaining_param_valsr   rL   Znum_parametersZcomponent_param_valsr   )r]   r   r[     s    

z%Sum.make_component_state_space_modelsc       	         sL    fdd| j D }|d }| j| |d}|d kr:| j}t|||||dS )Nc                s   g | ]} |j  qS r   )r   )r   r\   )r]   r   r   r     s    z/Sum._make_state_space_model.<locals>.<listcomp>r   )r   r^   r"   )r   r   r)   r   r"   )rM   r[   r   r
   )	r?   r   r]   r"   r   r   r_   r)   r   r   )r]   r   _make_state_space_model  s    zSum._make_state_space_model)NNNN)r   )r   NN)rA   rB   rC   rD   r>   propertyrX   rZ   r   r[   r`   rE   r   r   )r@   r   rF   F  s   /   O
+  rF   )rD   
__future__r   r   r   rQ   Ztensorflow.compat.v2compatZv2r   Ztensorflow_probability.pythonr   rS   r   r-   Z*tensorflow_probability.python.sts.internalr   r   Z8tensorflow_probability.python.sts.structural_time_seriesr   r	   Zlinalgr   ZLinearGaussianStateSpaceModelr
   rF   r   r   r   r   <module>   s     $