def test_constant_offset(self): offset_ = 1.23456 offset = self._build_placeholder(offset_) ssm = self._dummy_model() additive_ssm = AdditiveStateSpaceModel([ssm]) additive_ssm_with_offset = AdditiveStateSpaceModel( [ssm], constant_offset=offset) additive_ssm_with_offset_and_explicit_scale = AdditiveStateSpaceModel( [ssm], constant_offset=offset, observation_noise_scale=( ssm.get_observation_noise_for_timestep(0).stddev()[..., 0])) mean_, offset_mean_, offset_with_scale_mean_ = self.evaluate( (additive_ssm.mean(), additive_ssm_with_offset.mean(), additive_ssm_with_offset_and_explicit_scale.mean())) print(mean_.shape, offset_mean_.shape, offset_with_scale_mean_.shape) self.assertAllClose(mean_, offset_mean_ - offset_) self.assertAllClose(mean_, offset_with_scale_mean_ - offset_) # Offset should not affect the stddev. stddev_, offset_stddev_, offset_with_scale_stddev_ = self.evaluate( (additive_ssm.stddev(), additive_ssm_with_offset.stddev(), additive_ssm_with_offset_and_explicit_scale.stddev())) self.assertAllClose(stddev_, offset_stddev_) self.assertAllClose(stddev_, offset_with_scale_stddev_)
def test_nesting_additive_ssms(self): ssm1 = self._dummy_model(batch_shape=[1, 2]) ssm2 = self._dummy_model(batch_shape=[3, 2]) observation_noise_scale = 0.1 additive_ssm = AdditiveStateSpaceModel( [ssm1, ssm2], observation_noise_scale=observation_noise_scale) nested_additive_ssm = AdditiveStateSpaceModel( [AdditiveStateSpaceModel([ssm1]), AdditiveStateSpaceModel([ssm2])], observation_noise_scale=observation_noise_scale) # Test that both models behave equivalently. y = self.evaluate(nested_additive_ssm.sample()) additive_lp = additive_ssm.log_prob(y) nested_additive_lp = nested_additive_ssm.log_prob(y) self.assertAllClose(self.evaluate(additive_lp), self.evaluate(nested_additive_lp)) additive_mean = additive_ssm.mean() nested_additive_mean = nested_additive_ssm.mean() self.assertAllClose(self.evaluate(additive_mean), self.evaluate(nested_additive_mean)) additive_variance = additive_ssm.variance() nested_additive_variance = nested_additive_ssm.variance() self.assertAllClose(self.evaluate(additive_variance), self.evaluate(nested_additive_variance))
def test_constant_offset(self, is_scalar=True): offset_ = np.array(3.1415) if is_scalar else np.array( [3., 1., 4., 1., 5.]) offset = self._build_placeholder(offset_) ssm = self._dummy_model() additive_ssm = AdditiveStateSpaceModel([ssm]) additive_ssm_with_offset = AdditiveStateSpaceModel( [ssm], constant_offset=offset) additive_ssm_with_offset_and_explicit_scale = AdditiveStateSpaceModel( [ssm], constant_offset=offset, observation_noise_scale=( ssm.get_observation_noise_for_timestep(0).stddev()[..., 0])) mean_, offset_mean_, offset_with_scale_mean_ = self.evaluate( (additive_ssm.mean(), additive_ssm_with_offset.mean(), additive_ssm_with_offset_and_explicit_scale.mean())) self.assertAllClose(mean_, offset_mean_ - offset_[..., tf.newaxis]) self.assertAllClose(mean_, offset_with_scale_mean_ - offset_[..., tf.newaxis]) # Offset should not affect the stddev. stddev_, offset_stddev_, offset_with_scale_stddev_ = self.evaluate( (additive_ssm.stddev(), additive_ssm_with_offset.stddev(), additive_ssm_with_offset_and_explicit_scale.stddev())) self.assertAllClose(stddev_, offset_stddev_) self.assertAllClose(stddev_, offset_with_scale_stddev_)
def test_sum_of_local_linear_trends(self): # We know analytically that the sum of two local linear trends is # another local linear trend, with means and variances scaled # accordingly, so the additive model should match this behavior. level_scale = 0.5 slope_scale = 1.1 initial_level = 3. initial_slope = -2. observation_noise_scale = 0. num_timesteps = 5 y = self._build_placeholder([1.0, 2.5, 4.3, 6.1, 7.8]) # Combine two local linear trend models, one a full model, the other # with just a moving mean (zero slope). local_ssm = LocalLinearTrendStateSpaceModel( num_timesteps=num_timesteps, level_scale=level_scale, slope_scale=slope_scale, initial_state_prior=tfd.MultivariateNormalDiag( loc=self._build_placeholder([initial_level, initial_slope]), scale_diag=self._build_placeholder([1., 1.]))) second_level_scale = 0.3 second_initial_level = 1.1 moving_level_ssm = LocalLinearTrendStateSpaceModel( num_timesteps=num_timesteps, level_scale=second_level_scale, slope_scale=0., initial_state_prior=tfd.MultivariateNormalDiag( loc=self._build_placeholder([second_initial_level, 0.]), scale_diag=self._build_placeholder([1., 0.]))) additive_ssm = AdditiveStateSpaceModel( [local_ssm, moving_level_ssm], observation_noise_scale=observation_noise_scale) # Build the analytical sum of the two processes. target_ssm = LocalLinearTrendStateSpaceModel( num_timesteps=num_timesteps, level_scale=np.float32( np.sqrt(level_scale**2 + second_level_scale**2)), slope_scale=np.float32(slope_scale), observation_noise_scale=observation_noise_scale, initial_state_prior=tfd.MultivariateNormalDiag( loc=self._build_placeholder( [initial_level + second_initial_level, initial_slope + 0.]), scale_diag=self._build_placeholder(np.sqrt([2., 1.])))) # Test that both models behave equivalently. additive_mean = additive_ssm.mean() target_mean = target_ssm.mean() self.assertAllClose(self.evaluate(additive_mean), self.evaluate(target_mean)) additive_variance = additive_ssm.variance() target_variance = target_ssm.variance() self.assertAllClose(self.evaluate(additive_variance), self.evaluate(target_variance)) additive_lp = additive_ssm.log_prob(y[:, np.newaxis]) target_lp = target_ssm.log_prob(y[:, np.newaxis]) self.assertAllClose(self.evaluate(additive_lp), self.evaluate(target_lp))