def test_logprob(self):
        y = self._build_placeholder([1.0, 1.3, 1.9, 2.9, 2.1])

        ssm = LocalLevelStateSpaceModel(
            num_timesteps=5,
            level_scale=0.5,
            initial_state_prior=tfd.MultivariateNormalDiag(
                scale_diag=self._build_placeholder([1.])))

        lp = ssm.log_prob(y[..., np.newaxis])
        expected_lp = -6.5021
        self.assertAllClose(self.evaluate(lp), expected_lp)
  def test_batch_shape(self):
    batch_shape = [4, 2]

    level_scale = self._build_placeholder(
        np.exp(np.random.randn(*batch_shape)))
    initial_state_prior = tfd.MultivariateNormalDiag(
        scale_diag=self._build_placeholder([1.]))

    ssm = LocalLevelStateSpaceModel(
        num_timesteps=10,
        level_scale=level_scale,
        initial_state_prior=initial_state_prior)
    self.assertAllEqual(self.evaluate(ssm.batch_shape_tensor()), batch_shape)

    y = ssm.sample()
    self.assertAllEqual(self.evaluate(tf.shape(y))[:-2], batch_shape)
  def test_joint_sample(self):
    seed = test_util.test_seed(sampler_type='stateless')
    batch_shape = [4, 2]

    level_scale = self._build_placeholder(2 * np.ones(batch_shape))
    observation_noise_scale = self._build_placeholder(1.)
    initial_state_prior = tfd.MultivariateNormalDiag(
        loc=self._build_placeholder([-3]),
        scale_diag=self._build_placeholder([1.]))

    ssm = LocalLevelStateSpaceModel(
        num_timesteps=10,
        level_scale=level_scale,
        observation_noise_scale=observation_noise_scale,
        initial_state_prior=initial_state_prior)

    num_samples = 10000
    sampled_latents, sampled_obs = ssm._joint_sample_n(n=num_samples,
                                                       seed=seed)
    latent_mean, obs_mean = ssm._joint_mean()
    latent_cov, obs_cov = ssm._joint_covariances()
    (sampled_latents_, sampled_obs_,
     latent_mean_, obs_mean_,
     latent_std_, obs_std_) = self.evaluate((sampled_latents, sampled_obs,
                                             latent_mean, obs_mean,
                                             tf.sqrt(latent_cov[..., 0]),
                                             tf.sqrt(obs_cov[..., 0])))

    # Instead of directly comparing means and stddevs, we normalize by stddev
    # to make the stderr constant.
    self.assertAllClose(np.mean(sampled_latents_, axis=0) / latent_std_,
                        latent_mean_ / latent_std_,
                        atol=4. / np.sqrt(num_samples))
    self.assertAllClose(np.mean(sampled_obs_, axis=0) / obs_std_,
                        obs_mean_ / obs_std_,
                        atol=4. / np.sqrt(num_samples))
    self.assertAllClose(np.std(sampled_latents_, axis=0) / latent_std_,
                        np.ones(latent_std_.shape, dtype=latent_std_.dtype),
                        atol=4. / np.sqrt(num_samples))
    self.assertAllClose(np.std(sampled_obs_, axis=0) / obs_std_,
                        np.ones(obs_std_.shape, dtype=obs_std_.dtype),
                        atol=4. / np.sqrt(num_samples))
    def test_stats(self):
        # Build a model with expected initial scale 0.
        level_scale = self._build_placeholder(1.0)
        initial_state_prior = tfd.MultivariateNormalDiag(
            loc=self._build_placeholder([0.]),
            scale_diag=self._build_placeholder([1.]))

        ssm = LocalLevelStateSpaceModel(
            num_timesteps=10,
            level_scale=level_scale,
            initial_state_prior=initial_state_prior)

        # In expectation, the process is constant.
        mean = self.evaluate(ssm.mean())
        self.assertAllClose(mean, np.zeros(10)[:, np.newaxis])

        # variance of level[T] is T * level_scale
        expected_variance = np.arange(1, 11)[:, np.newaxis]
        variance = self.evaluate(ssm.variance())
        self.assertAllClose(variance, expected_variance)
    def testEqualsLocalLevel(self):
        # An AR1 process with coef 1 is just a random walk, equivalent to a local
        # level model. Test that both models define the same distribution
        # (log-prob).
        num_timesteps = 10
        observed_time_series = self._build_placeholder(
            np.random.randn(num_timesteps, 1))

        level_scale = self._build_placeholder(0.1)

        # We'll test an AR1 process, and also (just for kicks) that the trivial
        # embedding as an AR2 process gives the same model.
        coefficients_order1 = np.array([1.]).astype(self.dtype)
        coefficients_order2 = np.array([1., 0.]).astype(self.dtype)

        ar1_ssm = AutoregressiveStateSpaceModel(
            num_timesteps=num_timesteps,
            coefficients=coefficients_order1,
            level_scale=level_scale,
            initial_state_prior=tfd.MultivariateNormalDiag(
                scale_diag=[level_scale]))
        ar2_ssm = AutoregressiveStateSpaceModel(
            num_timesteps=num_timesteps,
            coefficients=coefficients_order2,
            level_scale=level_scale,
            initial_state_prior=tfd.MultivariateNormalDiag(
                scale_diag=[level_scale, 1.]))

        local_level_ssm = LocalLevelStateSpaceModel(
            num_timesteps=num_timesteps,
            level_scale=level_scale,
            initial_state_prior=tfd.MultivariateNormalDiag(
                scale_diag=[level_scale]))

        ar1_lp, ar2_lp, ll_lp = self.evaluate(
            (ar1_ssm.log_prob(observed_time_series),
             ar2_ssm.log_prob(observed_time_series),
             local_level_ssm.log_prob(observed_time_series)))
        self.assertAllClose(ar1_lp, ll_lp)
        self.assertAllClose(ar2_lp, ll_lp)