def testSamplingDeterministic(self):
   # pyformat: disable
   scale = tf.linalg.LinearOperatorFullMatrix(self._input(
       [[2., -1.],
        [-1., 2.]]))
   # pyformat: enable
   tf.set_random_seed(2)
   dist1 = mvt.MultivariateStudentTLinearOperator(
       loc=[1., 2.], df=5., scale=scale)
   samples1 = self.evaluate(dist1.sample(100, seed=1))
   tf.set_random_seed(2)
   dist2 = mvt.MultivariateStudentTLinearOperator(
       loc=[1., 2.], df=5., scale=scale)
   samples2 = self.evaluate(dist2.sample(100, seed=1))
   self.assertAllClose(samples1, samples2)
 def testMeanAllDefined(self):
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([0., 0.]),
       df=self._input(2.),
       scale=tf.linalg.LinearOperatorDiag(self._input([1., 1.])))
   mean = self.evaluate(dist.mean())
   self.assertAllClose([0., 0.], mean)
 def testMeanSomeUndefinedNaNAllowed(self):
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([[0., 0.], [1., 1.]]),
       df=self._input([1., 2.]),
       scale=tf.linalg.LinearOperatorDiag(self._input([[1., 1.], [1., 1.]])),
       allow_nan_stats=True)
   mean = self.evaluate(dist.mean())
   self.assertAllClose([[np.nan, np.nan], [1., 1.]], mean)
 def testNotPositiveDefinite(self):
   with self.assertRaisesRegexp(ValueError,
                                "`scale` must be positive definite."):
     mvt.MultivariateStudentTLinearOperator(
         loc=self._input([0.]),
         df=self._input(1.),
         scale=tf.linalg.LinearOperatorDiag(self._input([1.])),
         validate_args=True)
 def testBadScaleDType(self):
   with self.assertRaisesRegexp(TypeError,
                                "`scale` must have floating-point dtype."):
     mvt.MultivariateStudentTLinearOperator(
         loc=[0.],
         df=1.,
         scale=tf.linalg.LinearOperatorIdentity(
             num_rows=1, dtype=tf.int32, is_positive_definite=True))
 def testEntropy(self):
   scale = tf.linalg.LinearOperatorDiag(self._input([2., 2.]))
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([0., 0.]), df=self._input([2., 3.]), scale=scale)
   # From Kotz S. and Nadarajah S. (2004). Multivariate t Distributions and
   # Their Applications. Cambridge University Press. p22.
   self.assertAllClose(
       [0.5 * np.log(16.) + 3.83788, 0.5 * np.log(16.) + 3.50454],
       dist.entropy())
 def testMeanSomeUndefinedNaNNotAllowed(self):
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([[0., 0.], [1., 1.]]),
       df=self._input([1., 2.]),
       scale=tf.linalg.LinearOperatorDiag(self._input([[1., 1.], [1., 1.]])),
       allow_nan_stats=False)
   with self.assertRaisesRegexp(tf.errors.InvalidArgumentError,
                                "mean not defined for components of df <= 1"):
     self.evaluate(dist.mean())
 def testNonPositiveDf(self):
   with self.assertRaisesRegexp(tf.errors.InvalidArgumentError,
                                "`df` must be positive"):
     self.evaluate(
         mvt.MultivariateStudentTLinearOperator(
             loc=self._input([0.]),
             df=self._input(0.),
             scale=tf.linalg.LinearOperatorDiag(
                 self._input([1.]), is_positive_definite=True),
             validate_args=True).df)
 def testSamplingConsistency(self):
   # pyformat: disable
   scale = tf.linalg.LinearOperatorFullMatrix(self._input(
       [[2., -1.],
        [-1., 2.]]))
   # pyformat: enable
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([1., 2.]), df=self._input(5.), scale=scale)
   self.run_test_sample_consistent_mean_covariance(
       sess_run_fn=self.evaluate, dist=dist)
 def testCovarianceSomeUndefinedNaNAllowed(self):
   scale = tf.linalg.LinearOperatorDiag(self._input([2., 2.]))
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([0., 0.]),
       df=self._input([2., 1.]),
       scale=scale,
       allow_nan_stats=True)
   cov = self.evaluate(dist.covariance())
   self.assertAllClose(np.full([2, 2], np.inf), cov[0])
   self.assertAllClose(np.full([2, 2], np.nan), cov[1])
示例#11
0
    def get_marginal_distribution(self, index_points=None):
        """Compute the marginal over function values at `index_points`.

    Args:
      index_points: `float` `Tensor` representing finite (batch of) vector(s) of
        points in the index set over which the TP is defined. Shape has the form
        `[b1, ..., bB, e, f1, ..., fF]` where `F` is the number of feature
        dimensions and must equal `kernel.feature_ndims` and `e` is the number
        (size) of index points in each batch. Ultimately this distribution
        corresponds to a `e`-dimensional multivariate student t. The batch shape
        must be broadcastable with `kernel.batch_shape` and any batch dims
        yielded by `mean_fn`.

    Returns:
      marginal: a `StudentT` or `MultivariateStudentT` distribution,
        according to whether `index_points` consists of one or many index
        points, respectively.
    """
        with self._name_and_control_scope('get_marginal_distribution'):
            df = tf.convert_to_tensor(self.df)
            index_points = self._get_index_points(index_points)
            squared_scale = self._compute_covariance(index_points)
            if self._is_univariate_marginal(index_points):
                squared_scale = (df - 2.) / df * squared_scale
            else:
                squared_scale = ((df - 2.) / df)[..., tf.newaxis,
                                                 tf.newaxis] * squared_scale
            loc = self._mean_fn(index_points)
            # If we're sure the number of index points is 1, we can just construct a
            # scalar Normal. This has computational benefits and supports things like
            # CDF that aren't otherwise straightforward to provide.
            if self._is_univariate_marginal(index_points):
                scale = tf.sqrt(squared_scale)
                # `loc` has a trailing 1 in the shape; squeeze it.
                loc = tf.squeeze(loc, axis=-1)
                return student_t.StudentT(
                    df=df,
                    loc=loc,
                    scale=scale,
                    validate_args=self._validate_args,
                    allow_nan_stats=self._allow_nan_stats,
                    name='marginal_distribution')
            else:
                scale = tf.linalg.LinearOperatorLowerTriangular(
                    tf.linalg.cholesky(
                        _add_diagonal_shift(squared_scale, self.jitter)),
                    is_non_singular=True,
                    name='StudentTProcessScaleLinearOperator')
                return multivariate_student_t.MultivariateStudentTLinearOperator(
                    df=df,
                    loc=loc,
                    scale=scale,
                    validate_args=self._validate_args,
                    allow_nan_stats=self._allow_nan_stats,
                    name='marginal_distribution')
 def testCovarianceSomeUndefinedNaNNotAllowed(self):
   scale = tf.linalg.LinearOperatorDiag(self._input([2., 2.]))
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([0., 0.]),
       df=self._input(1.),
       scale=scale,
       allow_nan_stats=False)
   with self.assertRaisesRegexp(
       tf.errors.InvalidArgumentError,
       "covariance not defined for components of df <= 1"):
     self.evaluate(dist.covariance())
 def _as_multivariate_t(self, loc=None):
   # Rebuild the Multivariate T Distribution on every call because the
   # underlying tensor shapes might have changed.
   df = tf.convert_to_tensor(self.df)
   loc = tf.convert_to_tensor(self.loc if loc is None else loc)
   return multivariate_student_t.MultivariateStudentTLinearOperator(
       df=df,
       loc=_vec(loc),
       scale=tf.linalg.LinearOperatorKronecker(
           [self.scale_row, self.scale_column]),
       validate_args=self.validate_args)
 def testSamplingSmallDfNoNaN(self):
   scale = tf.linalg.LinearOperatorDiag(self._input([1., 1.]))
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([0., 0.]),
       df=self._input([1e-1, 1e-5, 1e-10, 1e-20]),
       scale=scale)
   samples = dist.sample(int(2e5), seed=1)
   log_probs = dist.log_prob(samples)
   samples, log_probs = self.evaluate([samples, log_probs])
   self.assertTrue(np.all(np.isfinite(samples)))
   self.assertTrue(np.all(np.isfinite(log_probs)))
 def testCovarianceAllDefined(self,
                              diag=None,
                              full=None,
                              expected_mvn_cov=None):
   if diag is not None:
     scale = tf.linalg.LinearOperatorDiag(self._input(diag))
   else:
     scale = tf.linalg.LinearOperatorFullMatrix(self._input(full))
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([0., 0.]), df=self._input(3.), scale=scale)
   cov = self.evaluate(dist.covariance())
   self.assertAllClose(np.array(expected_mvn_cov) * 3. / (3. - 2.), cov)
 def testVarianceStdSomeUndefinedNaNAllowed(self):
   scale = tf.linalg.LinearOperatorDiag(self._input([2., 2.]))
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([0., 0.]),
       df=self._input([2., 1.]),
       scale=scale,
       allow_nan_stats=True)
   var = self.evaluate(dist.variance())
   std = self.evaluate(dist.stddev())
   self.assertAllClose([np.inf, np.inf], var[0])
   self.assertAllClose([np.nan, np.nan], var[1])
   self.assertAllClose([np.inf, np.inf], std[0])
   self.assertAllClose([np.nan, np.nan], std[1])
 def testHypersphereVolume(self, radius):
   # pyformat: disable
   scale = tf.linalg.LinearOperatorFullMatrix(
       self._input([[1.,  -0.5],
                    [-0.5, 1.]]))
   # pyformat: enable
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=self._input([1., 1.]), df=self._input(4.), scale=scale)
   self.run_test_sample_consistent_log_prob(
       sess_run_fn=self.evaluate,
       dist=dist,
       radius=radius,
       num_samples=int(5e6),
       rtol=0.05)
 def testSamplingFullyReparameterized(self):
   df = self._input(2.)
   loc = self._input([1., 2.])
   diag = self._input([3., 4.])
   with tf.GradientTape() as tape:
     tape.watch(df)
     tape.watch(loc)
     tape.watch(diag)
     scale = tf.linalg.LinearOperatorDiag(diag)
     dist = mvt.MultivariateStudentTLinearOperator(loc=loc, df=df, scale=scale)
     samples = dist.sample(100)
   grad_df, grad_loc, grad_diag = tape.gradient(samples, [df, loc, diag])
   self.assertIsNotNone(grad_df)
   self.assertIsNotNone(grad_loc)
   self.assertIsNotNone(grad_diag)
  def testLogProbSameFor1D(self):
    # 1D MVT is exactly a regular Student's T distribution.
    t_dist = student_t.StudentT(
        df=self._input(5.), loc=self._input(2.), scale=self._input(3.))
    scale = tf.linalg.LinearOperatorDiag([self._input(3.)])
    mvt_dist = mvt.MultivariateStudentTLinearOperator(
        loc=[self._input(2.)], df=self._input(5.), scale=scale)

    test_points = tf.cast(tf.linspace(-10.0, 10.0, 100), self.dtype)

    t_log_probs = self.evaluate(t_dist.log_prob(test_points))
    mvt_log_probs = self.evaluate(
        mvt_dist.log_prob(test_points[..., tf.newaxis]))

    self.assertAllClose(t_log_probs, mvt_log_probs)
  def testBroadcasting(self, loc, df, diag, batch_shape):
    # Test that broadcasting works across all 3 parameters.
    loc = self._input(loc)
    df = self._input(df)
    diag = self._input(diag)

    scale = tf.linalg.LinearOperatorDiag(diag, is_positive_definite=True)
    dist = mvt.MultivariateStudentTLinearOperator(
        loc=loc, df=df, scale=scale, validate_args=True)

    sample = dist.sample(3)
    log_prob = dist.log_prob(sample)
    mean = dist.mean()
    mode = dist.mode()
    cov = dist.covariance()
    std = dist.stddev()
    var = dist.variance()
    entropy = dist.entropy()
    if self.use_static_shape:
      self.assertAllEqual([3] + batch_shape + [2], sample.shape)
      self.assertAllEqual([3] + batch_shape, log_prob.shape)
      self.assertAllEqual(batch_shape + [2], mean.shape)
      self.assertAllEqual(batch_shape + [2], mode.shape)
      self.assertAllEqual(batch_shape + [2, 2], cov.shape)
      self.assertAllEqual(batch_shape + [2], std.shape)
      self.assertAllEqual(batch_shape + [2], var.shape)
      self.assertAllEqual(batch_shape, entropy.shape)
      self.assertAllEqual([2], dist.event_shape)
      self.assertAllEqual(batch_shape, dist.batch_shape)

    sample = self.evaluate(sample)
    log_prob = self.evaluate(log_prob)
    mean = self.evaluate(mean)
    mode = self.evaluate(mode)
    cov = self.evaluate(cov)
    std = self.evaluate(std)
    var = self.evaluate(var)
    entropy = self.evaluate(entropy)
    self.assertAllEqual([3] + batch_shape + [2], sample.shape)
    self.assertAllEqual([3] + batch_shape, log_prob.shape)
    self.assertAllEqual(batch_shape + [2], mean.shape)
    self.assertAllEqual(batch_shape + [2], mode.shape)
    self.assertAllEqual(batch_shape + [2, 2], cov.shape)
    self.assertAllEqual(batch_shape + [2], std.shape)
    self.assertAllEqual(batch_shape + [2], var.shape)
    self.assertAllEqual(batch_shape, entropy.shape)
    self.assertAllEqual([2], self.evaluate(dist.event_shape_tensor()))
    self.assertAllEqual(batch_shape, self.evaluate(dist.batch_shape_tensor()))
 def marginal_fn(df,
                 loc,
                 covariance,
                 validate_args=False,
                 allow_nan_stats=False,
                 name='marginal_distribution'):
     squared_scale = (
         (df - 2.) / df)[..., tf.newaxis, tf.newaxis] * covariance
     scale = tf.linalg.LinearOperatorLowerTriangular(
         tf.linalg.cholesky(_add_diagonal_shift(squared_scale, jitter)),
         is_non_singular=True,
         name='StudentTProcessScaleLinearOperator')
     return multivariate_student_t.MultivariateStudentTLinearOperator(
         df=df,
         loc=loc,
         scale=scale,
         validate_args=validate_args,
         allow_nan_stats=allow_nan_stats,
         name=name)
  def testVarianceStdAllDefined(self,
                                diag=None,
                                full=None,
                                update=None,
                                expected_mvn_var=None):
    if diag is not None:
      scale = tf.linalg.LinearOperatorDiag(self._input(diag))
    elif full is not None:
      scale = tf.linalg.LinearOperatorFullMatrix(self._input(full))
    if update is not None:
      scale = tf.linalg.LinearOperatorLowRankUpdate(scale, self._input(update))

    dist = mvt.MultivariateStudentTLinearOperator(
        loc=self._input([0., 0.]), df=self._input(3.), scale=scale)
    var = self.evaluate(dist.variance())
    std = self.evaluate(dist.stddev())
    # df = 3, so we expect the variance of the MVT to exceed MVN by a factor of
    # 3 / (3 - 2) = 3.
    self.assertAllClose(np.array(expected_mvn_var) * 3., var)
    self.assertAllClose(np.sqrt(np.array(expected_mvn_var) * 3.), std)
  def testLogProb(self):
    # Test that numerically integrating over some portion of the domain yields a
    # normalization constant of close to 1.
    # pyformat: disable
    scale = tf.linalg.LinearOperatorFullMatrix(
        self._input([[1.,  -0.5],
                     [-0.5, 1.]]))
    # pyformat: enable
    dist = mvt.MultivariateStudentTLinearOperator(
        loc=self._input([1., 1.]), df=self._input(5.), scale=scale)

    spacings = tf.cast(tf.linspace(-20., 20., 100), self.dtype)
    x, y = tf.meshgrid(spacings, spacings)
    points = tf.concat([x[..., tf.newaxis], y[..., tf.newaxis]], -1)
    log_probs = dist.log_prob(points)
    normalization = tf.exp(
        tf.reduce_logsumexp(log_probs)) * (spacings[1] - spacings[0])**2
    self.assertAllClose(1., self.evaluate(normalization), atol=1e-3)

    mode_log_prob = dist.log_prob(dist.mode())
    self.assertTrue(np.all(self.evaluate(mode_log_prob >= log_probs)))
 def testMode(self):
   dist = mvt.MultivariateStudentTLinearOperator(
       loc=[0., 0.], df=2., scale=tf.linalg.LinearOperatorDiag([[1., 1.]]))
   mode = self.evaluate(dist.mode())
   self.assertAllClose([[0., 0.]], mode)