def testBroadcastingParams(self):
        def _check(student):
            self.assertEqual(student.mean().shape, (3, ))
            self.assertEqual(student.variance().shape, (3, ))
            self.assertEqual(student.entropy().shape, (3, ))
            self.assertEqual(student.log_prob(2.).shape, (3, ))
            self.assertEqual(student.prob(2.).shape, (3, ))
            self.assertEqual(student.sample(37).shape, (
                37,
                3,
            ))

        _check(student_t.StudentT(df=[
            2.,
            3.,
            4.,
        ], loc=2., scale=1.))
        _check(student_t.StudentT(df=7., loc=[
            2.,
            3.,
            4.,
        ], scale=1.))
        _check(student_t.StudentT(df=7., loc=3., scale=[
            2.,
            3.,
            4.,
        ]))
  def testVarianceAllowNanStatsFalseRaisesForUndefinedBatchMembers(self):
    # df <= 1 ==> variance not defined
    student = student_t.StudentT(df=1., loc=0., scale=1., allow_nan_stats=False)
    with self.assertRaisesOpError("x < y"):
      self.evaluate(student.variance())

    # df <= 1 ==> variance not defined
    student = student_t.StudentT(
        df=0.5, loc=0., scale=1., allow_nan_stats=False)
    with self.assertRaisesOpError("x < y"):
      self.evaluate(student.variance())
  def testStudentSampleMultipleTimes(self):
    df = tf.constant(4.)
    mu = tf.constant(3.)
    sigma = tf.constant(math.sqrt(10.))
    n = tf.constant(100)

    tf.set_random_seed(654321)
    student = student_t.StudentT(df=df, loc=mu, scale=sigma, name="student_t1")
    samples1 = self.evaluate(student.sample(n, seed=123456))

    tf.set_random_seed(654321)
    student2 = student_t.StudentT(df=df, loc=mu, scale=sigma, name="student_t2")
    samples2 = self.evaluate(student2.sample(n, seed=123456))

    self.assertAllClose(samples1, samples2)
 def testStudentSampleMultiDimensional(self):
     batch_size = 7
     df = tf.constant([[5., 7.]] * batch_size)
     mu = tf.constant([[3., -3.]] * batch_size)
     sigma = tf.constant([[math.sqrt(10.), math.sqrt(15.)]] * batch_size)
     df_v = [5., 7.]
     mu_v = [3., -3.]
     sigma_v = [np.sqrt(10.), np.sqrt(15.)]
     n = tf.constant(200000)
     student = student_t.StudentT(df=df, loc=mu, scale=sigma)
     samples = student.sample(n, seed=123456)
     sample_values = self.evaluate(samples)
     self.assertEqual(samples.shape, (200000, batch_size, 2))
     self.assertAllClose(sample_values[:, 0, 0].mean(),
                         mu_v[0],
                         rtol=0.1,
                         atol=0)
     self.assertAllClose(sample_values[:, 0, 0].var(),
                         sigma_v[0]**2 * df_v[0] / (df_v[0] - 2),
                         rtol=0.2,
                         atol=0)
     self._checkKLApprox(df_v[0], mu_v[0], sigma_v[0], sample_values[:, 0,
                                                                     0])
     self.assertAllClose(sample_values[:, 0, 1].mean(),
                         mu_v[1],
                         rtol=0.1,
                         atol=0)
     self.assertAllClose(sample_values[:, 0, 1].var(),
                         sigma_v[1]**2 * df_v[1] / (df_v[1] - 2),
                         rtol=0.2,
                         atol=0)
     self._checkKLApprox(df_v[1], mu_v[1], sigma_v[1], sample_values[:, 0,
                                                                     1])
 def testMeanAllowNanStatsIsFalseWorksWhenAllBatchMembersAreDefined(self):
     mu = [1., 3.3, 4.4]
     student = student_t.StudentT(df=[3., 5., 7.],
                                  loc=mu,
                                  scale=[3., 2., 1.])
     mean = self.evaluate(student.mean())
     self.assertAllClose([1., 3.3, 4.4], mean)
    def testStudentLogPDFMultidimensional(self):
        batch_size = 6
        df = tf.constant([[1.5, 7.2]] * batch_size)
        mu = tf.constant([[3., -3.]] * batch_size)
        sigma = tf.constant([[-math.sqrt(10.), math.sqrt(15.)]] * batch_size)
        df_v = np.array([1.5, 7.2])
        mu_v = np.array([3., -3.])
        sigma_v = np.array([np.sqrt(10.), np.sqrt(15.)])
        t = np.array([[-2.5, 2.5, 4., 0., -1., 2.]], dtype=np.float32).T
        student = student_t.StudentT(df, loc=mu, scale=sigma)
        log_pdf = student.log_prob(t)
        log_pdf_values = self.evaluate(log_pdf)
        self.assertEqual(log_pdf.shape, (6, 2))
        pdf = student.prob(t)
        pdf_values = self.evaluate(pdf)
        self.assertEqual(pdf.shape, (6, 2))

        if not stats:
            return
        expected_log_pdf = stats.t.logpdf(t, df_v, loc=mu_v, scale=sigma_v)
        expected_pdf = stats.t.pdf(t, df_v, loc=mu_v, scale=sigma_v)
        self.assertAllClose(expected_log_pdf, log_pdf_values)
        self.assertAllClose(np.log(expected_pdf), log_pdf_values)
        self.assertAllClose(expected_pdf, pdf_values)
        self.assertAllClose(np.exp(expected_log_pdf), pdf_values)
    def testStudentCDFAndLogCDF(self):
        batch_size = 6
        df = tf.constant([3.] * batch_size)
        mu = tf.constant([7.] * batch_size)
        sigma = tf.constant([-8.] * batch_size)
        df_v = 3.
        mu_v = 7.
        sigma_v = 8.
        t = np.array([-2.5, 2.5, 8., 0., -1., 2.], dtype=np.float32)
        student = student_t.StudentT(df, loc=mu, scale=sigma)

        log_cdf = student.log_cdf(t)
        self.assertEquals(log_cdf.shape, (6, ))
        log_cdf_values = self.evaluate(log_cdf)
        cdf = student.cdf(t)
        self.assertEquals(cdf.shape, (6, ))
        cdf_values = self.evaluate(cdf)

        if not stats:
            return
        expected_log_cdf = stats.t.logcdf(t, df_v, loc=mu_v, scale=sigma_v)
        expected_cdf = stats.t.cdf(t, df_v, loc=mu_v, scale=sigma_v)
        self.assertAllClose(expected_log_cdf,
                            log_cdf_values,
                            atol=0.,
                            rtol=1e-5)
        self.assertAllClose(np.log(expected_cdf),
                            log_cdf_values,
                            atol=0.,
                            rtol=1e-5)
        self.assertAllClose(expected_cdf, cdf_values, atol=0., rtol=1e-5)
        self.assertAllClose(np.exp(expected_log_cdf),
                            cdf_values,
                            atol=0.,
                            rtol=1e-5)
 def testPdfOfSampleMultiDims(self):
     student = student_t.StudentT(df=[7., 11.], loc=[[5.], [6.]], scale=3.)
     self.assertAllEqual([], student.event_shape)
     self.assertAllEqual([], self.evaluate(student.event_shape_tensor()))
     self.assertAllEqual([2, 2], student.batch_shape)
     self.assertAllEqual([2, 2],
                         self.evaluate(student.batch_shape_tensor()))
     num = 50000
     samples = student.sample(num, seed=123456)
     pdfs = student.prob(samples)
     sample_vals, pdf_vals = self.evaluate([samples, pdfs])
     self.assertEqual(samples.shape, (num, 2, 2))
     self.assertEqual(pdfs.shape, (num, 2, 2))
     self.assertNear(5., np.mean(sample_vals[:, 0, :]), err=0.1)
     self.assertNear(6., np.mean(sample_vals[:, 1, :]), err=0.1)
     self._assertIntegral(sample_vals[:, 0, 0], pdf_vals[:, 0, 0], err=0.05)
     self._assertIntegral(sample_vals[:, 0, 1], pdf_vals[:, 0, 1], err=0.05)
     self._assertIntegral(sample_vals[:, 1, 0], pdf_vals[:, 1, 0], err=0.05)
     self._assertIntegral(sample_vals[:, 1, 1], pdf_vals[:, 1, 1], err=0.05)
     if not stats:
         return
     self.assertNear(
         stats.t.var(7., loc=0., scale=3.),  # loc d.n. effect var
         np.var(sample_vals[:, :, 0]),
         err=1.0)
     self.assertNear(
         stats.t.var(11., loc=0., scale=3.),  # loc d.n. effect var
         np.var(sample_vals[:, :, 1]),
         err=1.0)
 def testMeanAllowNanStatsIsTrueReturnsNaNForUndefinedBatchMembers(self):
   mu = [-2, 0., 1., 3.3, 4.4]
   sigma = [5., 4., 3., 2., 1.]
   student = student_t.StudentT(
       df=[0.5, 1., 3., 5., 7.], loc=mu, scale=sigma, allow_nan_stats=True)
   mean = self.evaluate(student.mean())
   self.assertAllClose([np.nan, np.nan, 1., 3.3, 4.4], mean)
    def testStudentPDFAndLogPDF(self):
        batch_size = 6
        df = tf.constant([3.] * batch_size)
        mu = tf.constant([7.] * batch_size)
        sigma = tf.constant([8.] * batch_size)
        df_v = 3.
        mu_v = 7.
        sigma_v = 8.
        t = np.array([-2.5, 2.5, 8., 0., -1., 2.], dtype=np.float32)
        student = student_t.StudentT(df, loc=mu, scale=-sigma)

        log_pdf = student.log_prob(t)
        self.assertEquals(log_pdf.shape, (6, ))
        log_pdf_values = self.evaluate(log_pdf)
        pdf = student.prob(t)
        self.assertEquals(pdf.shape, (6, ))
        pdf_values = self.evaluate(pdf)

        if not stats:
            return

        expected_log_pdf = stats.t.logpdf(t, df_v, loc=mu_v, scale=sigma_v)
        expected_pdf = stats.t.pdf(t, df_v, loc=mu_v, scale=sigma_v)
        self.assertAllClose(expected_log_pdf, log_pdf_values)
        self.assertAllClose(np.log(expected_pdf), log_pdf_values)
        self.assertAllClose(expected_pdf, pdf_values)
        self.assertAllClose(np.exp(expected_log_pdf), pdf_values)
 def testMode(self):
     df = [0.5, 1., 3]
     mu = [-1, 0., 1]
     sigma = [5., 4., 3.]
     student = student_t.StudentT(df=df, loc=mu, scale=sigma)
     # Test broadcast of mu across shape of df/sigma
     mode = self.evaluate(student.mode())
     self.assertAllClose([-1., 0, 1], mode)
 def testMeanAllowNanStatsIsFalseRaisesWhenBatchMemberIsUndefined(self):
     mu = [1., 3.3, 4.4]
     student = student_t.StudentT(df=[0.5, 5., 7.],
                                  loc=mu,
                                  scale=[3., 2., 1.],
                                  allow_nan_stats=False)
     with self.assertRaisesOpError("x < y"):
         self.evaluate(student.mean())
 def testNegativeDofFails(self):
     with self.assertRaisesOpError(r"Condition x > 0 did not hold"):
         student = student_t.StudentT(df=[2, -5.],
                                      loc=0.,
                                      scale=1.,
                                      validate_args=True,
                                      name="S")
         self.evaluate(student.mean())
 def testStudentSampleSmallDfNoNan(self):
     df_v = [1e-1, 1e-5, 1e-10, 1e-20]
     df = tf.constant(df_v)
     n = tf.constant(200000)
     student = student_t.StudentT(df=df, loc=1., scale=1.)
     samples = student.sample(n, seed=123456)
     sample_values = self.evaluate(samples)
     n_val = 200000
     self.assertEqual(sample_values.shape, (n_val, 4))
     self.assertTrue(np.all(np.logical_not(np.isnan(sample_values))))
Exemple #15
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 testBroadcastingPdfArgs(self):

    def _assert_shape(student, arg, shape):
      self.assertEqual(student.log_prob(arg).shape, shape)
      self.assertEqual(student.prob(arg).shape, shape)

    def _check(student):
      _assert_shape(student, 2., (3,))
      xs = np.array([2., 3., 4.], dtype=np.float32)
      _assert_shape(student, xs, (3,))
      xs = np.array([xs])
      _assert_shape(student, xs, (1, 3))
      xs = xs.T
      _assert_shape(student, xs, (3, 3))

    _check(student_t.StudentT(df=[2., 3., 4.,], loc=2., scale=1.))
    _check(student_t.StudentT(df=7., loc=[2., 3., 4.,], scale=1.))
    _check(student_t.StudentT(df=7., loc=3., scale=[2., 3., 4.,]))

    def _check2d(student):
      _assert_shape(student, 2., (1, 3))
      xs = np.array([2., 3., 4.], dtype=np.float32)
      _assert_shape(student, xs, (1, 3))
      xs = np.array([xs])
      _assert_shape(student, xs, (1, 3))
      xs = xs.T
      _assert_shape(student, xs, (3, 3))

    _check2d(student_t.StudentT(df=[[2., 3., 4.,]], loc=2., scale=1.))
    _check2d(student_t.StudentT(df=7., loc=[[2., 3., 4.,]], scale=1.))
    _check2d(student_t.StudentT(df=7., loc=3., scale=[[2., 3., 4.,]]))

    def _check2d_rows(student):
      _assert_shape(student, 2., (3, 1))
      xs = np.array([2., 3., 4.], dtype=np.float32)  # (3,)
      _assert_shape(student, xs, (3, 3))
      xs = np.array([xs])  # (1,3)
      _assert_shape(student, xs, (3, 3))
      xs = xs.T  # (3,1)
      _assert_shape(student, xs, (3, 1))

    _check2d_rows(student_t.StudentT(df=[[2.], [3.], [4.]], loc=2., scale=1.))
    _check2d_rows(student_t.StudentT(df=7., loc=[[2.], [3.], [4.]], scale=1.))
    _check2d_rows(student_t.StudentT(df=7., loc=3., scale=[[2.], [3.], [4.]]))
 def testFullyReparameterized(self):
     df = tf.constant(2.0)
     mu = tf.constant(1.0)
     sigma = tf.constant(3.0)
     with tf.GradientTape() as tape:
         tape.watch(df)
         tape.watch(mu)
         tape.watch(sigma)
         student = student_t.StudentT(df=df, loc=mu, scale=sigma)
         samples = student.sample(100)
     grad_df, grad_mu, grad_sigma = tape.gradient(samples, [df, mu, sigma])
     self.assertIsNotNone(grad_df)
     self.assertIsNotNone(grad_mu)
     self.assertIsNotNone(grad_sigma)
    def testVarianceAllowNanStatsFalseGivesCorrectValueForDefinedBatchMembers(
            self):
        # df = 1.5 ==> infinite variance.
        df = [1.5, 3., 5., 7.]
        mu = [0., 1., 3.3, 4.4]
        sigma = [4., 3., 2., 1.]
        student = student_t.StudentT(df=df, loc=mu, scale=sigma)
        var = self.evaluate(student.variance())

        if not stats:
            return
        expected_var = [
            stats.t.var(d, loc=m, scale=s) for (d, m, s) in zip(df, mu, sigma)
        ]
        self.assertAllClose(expected_var, var)
  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 testStd(self):
        # Defined for all batch members.
        df = [3.5, 5., 3., 5., 7.]
        mu = [-2.2]
        sigma = [5., 4., 3., 2., 1.]
        student = student_t.StudentT(df=df, loc=mu, scale=sigma)
        # Test broadcast of mu across shape of df/sigma
        stddev = self.evaluate(student.stddev())
        mu *= len(df)

        if not stats:
            return
        expected_stddev = [
            stats.t.std(d, loc=m, scale=s) for (d, m, s) in zip(df, mu, sigma)
        ]
        self.assertAllClose(expected_stddev, stddev)
 def testStudentSample(self):
   df = tf.constant(4.)
   mu = tf.constant(3.)
   sigma = tf.constant(-math.sqrt(10.))
   df_v = 4.
   mu_v = 3.
   sigma_v = np.sqrt(10.)
   n = tf.constant(200000)
   student = student_t.StudentT(df=df, loc=mu, scale=sigma)
   samples = student.sample(n, seed=123456)
   sample_values = self.evaluate(samples)
   n_val = 200000
   self.assertEqual(sample_values.shape, (n_val,))
   self.assertAllClose(sample_values.mean(), mu_v, rtol=0.1, atol=0)
   self.assertAllClose(
       sample_values.var(), sigma_v**2 * df_v / (df_v - 2), rtol=0.1, atol=0)
   self._checkKLApprox(df_v, mu_v, sigma_v, sample_values)
 def testVarianceAllowNanStatsTrueReturnsNaNforUndefinedBatchMembers(self):
   # df = 0.5 ==> undefined mean ==> undefined variance.
   # df = 1.5 ==> infinite variance.
   df = [0.5, 1.5, 3., 5., 7.]
   mu = [-2, 0., 1., 3.3, 4.4]
   sigma = [5., 4., 3., 2., 1.]
   student = student_t.StudentT(
       df=df, loc=mu, scale=sigma, allow_nan_stats=True)
   var = self.evaluate(student.variance())
   # Past versions of scipy differed from our preferred behavior for undefined
   # variance; newer versions changed this, but in order not to deal with
   # straddling both versions, we just override our expectation for the
   # undefined case.
   expected_var = [
       stats.t.var(d, loc=m, scale=s) for (d, m, s) in zip(df, mu, sigma)
   ]
   expected_var[0] = np.nan
   self.assertAllClose(expected_var, var)
 def testPdfOfSample(self):
   student = student_t.StudentT(df=3., loc=np.pi, scale=1.)
   num = 20000
   samples = student.sample(num, seed=123456)
   pdfs = student.prob(samples)
   mean = student.mean()
   mean_pdf = student.prob(student.mean())
   sample_vals, pdf_vals, mean_val, mean_pdf_val = self.evaluate(
       [samples, pdfs, student.mean(), mean_pdf])
   self.assertEqual(samples.shape, (num,))
   self.assertEqual(pdfs.shape, (num,))
   self.assertEqual(mean.shape, ())
   self.assertNear(np.pi, np.mean(sample_vals), err=0.1)
   self.assertNear(np.pi, mean_val, err=1e-6)
   # Verify integral over sample*pdf ~= 1.
   # Tolerance increased since eager was getting a value of 1.002041.
   self._assertIntegral(sample_vals, pdf_vals, err=5e-2)
   if not stats:
     return
   self.assertNear(stats.t.pdf(np.pi, 3., loc=np.pi), mean_pdf_val, err=1e-6)
    def testStudentEntropy(self):
        df_v = np.array([[2., 3., 7.]])  # 1x3
        mu_v = np.array([[1., -1, 0]])  # 1x3
        sigma_v = np.array([[1., -2., 3.]]).T  # transposed => 3x1
        student = student_t.StudentT(df=df_v, loc=mu_v, scale=sigma_v)
        ent = student.entropy()
        ent_values = self.evaluate(ent)

        # Help scipy broadcast to 3x3
        ones = np.array([[1, 1, 1]])
        sigma_bc = np.abs(sigma_v) * ones
        mu_bc = ones.T * mu_v
        df_bc = ones.T * df_v
        if not stats:
            return
        expected_entropy = stats.t.entropy(np.reshape(df_bc, [-1]),
                                           loc=np.reshape(mu_bc, [-1]),
                                           scale=np.reshape(sigma_bc, [-1]))
        expected_entropy = np.reshape(expected_entropy, df_bc.shape)
        self.assertAllClose(expected_entropy, ent_values)
Exemple #25
0
  def testVarianceAllowNanStatsTrueReturnsNaNforUndefinedBatchMembers(self):
    # df = 0.5 ==> undefined mean ==> undefined variance.
    # df = 1.5 ==> infinite variance.
    df = [0.5, 1.5, 3., 5., 7.]
    mu = [-2, 0., 1., 3.3, 4.4]
    sigma = [5., 4., 3., 2., 1.]
    student = student_t.StudentT(
        df=df, loc=mu, scale=sigma, allow_nan_stats=True)
    var = self.evaluate(student.variance())
    ## scipy uses inf for variance when the mean is undefined.  When mean is
    # undefined we say variance is undefined as well.  So test the first
    # member of var, making sure it is NaN, then replace with inf and compare
    # to scipy.
    self.assertTrue(np.isnan(var[0]))
    var[0] = np.inf

    if not stats:
      return
    expected_var = [
        stats.t.var(d, loc=m, scale=s) for (d, m, s) in zip(df, mu, sigma)
    ]
    self.assertAllClose(expected_var, var)
    def __init__(self,
                 df,
                 loc=None,
                 scale_identity_multiplier=None,
                 scale_diag=None,
                 scale_tril=None,
                 scale_perturb_factor=None,
                 scale_perturb_diag=None,
                 validate_args=False,
                 allow_nan_stats=True,
                 name="VectorStudentT"):
        """Instantiates the vector Student's t-distributions on `R^k`.

    The `batch_shape` is the broadcast between `df.batch_shape` and
    `Affine.batch_shape` where `Affine` is constructed from `loc` and
    `scale_*` arguments.

    The `event_shape` is the event shape of `Affine.event_shape`.

    Args:
      df: Floating-point `Tensor`. The degrees of freedom of the
        distribution(s). `df` must contain only positive values. Must be
        scalar if `loc`, `scale_*` imply non-scalar batch_shape or must have the
        same `batch_shape` implied by `loc`, `scale_*`.
      loc: Floating-point `Tensor`. If this is set to `None`, no `loc` is
        applied.
      scale_identity_multiplier: floating point rank 0 `Tensor` representing a
        scaling done to the identity matrix. When `scale_identity_multiplier =
        scale_diag=scale_tril = None` then `scale += IdentityMatrix`. Otherwise
        no scaled-identity-matrix is added to `scale`.
      scale_diag: Floating-point `Tensor` representing the diagonal matrix.
        `scale_diag` has shape [N1, N2, ..., k], which represents a k x k
        diagonal matrix. When `None` no diagonal term is added to `scale`.
      scale_tril: Floating-point `Tensor` representing the diagonal matrix.
        `scale_diag` has shape [N1, N2, ..., k, k], which represents a k x k
        lower triangular matrix. When `None` no `scale_tril` term is added to
        `scale`. The upper triangular elements above the diagonal are ignored.
      scale_perturb_factor: Floating-point `Tensor` representing factor matrix
        with last two dimensions of shape `(k, r)`. When `None`, no rank-r
        update is added to `scale`.
      scale_perturb_diag: Floating-point `Tensor` representing the diagonal
        matrix. `scale_perturb_diag` has shape [N1, N2, ..., r], which
        represents an r x r Diagonal matrix. When `None` low rank updates will
        take the form `scale_perturb_factor * scale_perturb_factor.T`.
      validate_args: Python `bool`, default `False`. When `True` distribution
        parameters are checked for validity despite possibly degrading runtime
        performance. When `False` invalid inputs may silently render incorrect
        outputs.
      allow_nan_stats: Python `bool`, default `True`. When `True`,
        statistics (e.g., mean, mode, variance) use the value "`NaN`" to
        indicate the result is undefined. When `False`, an exception is raised
        if one or more of the statistic's batch members are undefined.
      name: Python `str` name prefixed to Ops created by this class.
    """
        parameters = dict(locals())
        graph_parents = [
            df, loc, scale_identity_multiplier, scale_diag, scale_tril,
            scale_perturb_factor, scale_perturb_diag
        ]
        with tf.name_scope(name) as name:
            with tf.name_scope("init"):
                dtype = dtype_util.common_dtype(graph_parents, tf.float32)
                df = tf.convert_to_tensor(value=df, name="df", dtype=dtype)
                # The shape of the _VectorStudentT distribution is governed by the
                # relationship between df.batch_shape and affine.batch_shape. In
                # pseudocode the basic procedure is:
                #   if df.batch_shape is scalar:
                #     if affine.batch_shape is not scalar:
                #       # broadcast distribution.sample so
                #       # it has affine.batch_shape.
                #     self.batch_shape = affine.batch_shape
                #   else:
                #     if affine.batch_shape is scalar:
                #       # let affine broadcasting do its thing.
                #     self.batch_shape = df.batch_shape
                # All of the above magic is actually handled by TransformedDistribution.
                # Here we really only need to collect the affine.batch_shape and decide
                # what we're going to pass in to TransformedDistribution's
                # (override) batch_shape arg.
                affine = affine_bijector.Affine(
                    shift=loc,
                    scale_identity_multiplier=scale_identity_multiplier,
                    scale_diag=scale_diag,
                    scale_tril=scale_tril,
                    scale_perturb_factor=scale_perturb_factor,
                    scale_perturb_diag=scale_perturb_diag,
                    validate_args=validate_args,
                    dtype=dtype)
                distribution = student_t.StudentT(
                    df=df,
                    loc=tf.zeros([], dtype=affine.dtype),
                    scale=tf.ones([], dtype=affine.dtype))
                batch_shape, override_event_shape = (
                    distribution_util.shapes_from_loc_and_scale(
                        affine.shift, affine.scale))
                override_batch_shape = distribution_util.pick_vector(
                    distribution.is_scalar_batch(), batch_shape,
                    tf.constant([], dtype=tf.int32))
                super(_VectorStudentT,
                      self).__init__(distribution=distribution,
                                     bijector=affine,
                                     batch_shape=override_batch_shape,
                                     event_shape=override_event_shape,
                                     validate_args=validate_args,
                                     name=name)
                self._parameters = parameters