code: def test_standardize(self):
      # test error on incompatible dim
      tf = Standardize(m=1)
      with self.assertRaisesRegex(
          RuntimeError,
          r"Wrong output dimension. Y.size\(-1\) is 2; expected 1.",
      ):
          tf(torch.zeros(3, 2, device=self.device), None)
      # test error on incompatible batch shape
      with self.assertRaisesRegex(
          RuntimeError,
          r"Expected Y.shape\[:-2\] to be torch.Size\(\[\]\), matching the "
          "`batch_shape` argument to `Standardize`, but got "
          r"Y.shape\[:-2\]=torch.Size\(\[2\]\).",
      ):
          tf(torch.zeros(2, 3, 1, device=self.device), None)
  
      ms = (1, 2)
      batch_shapes = (torch.Size(), torch.Size([2]))
      dtypes = (torch.float, torch.double)
  
      # test transform, untransform, untransform_posterior
      for m, batch_shape, dtype in itertools.product(
          ms, batch_shapes, dtypes
      ):
          # test init
          tf = Standardize(m=m, batch_shape=batch_shape)
          self.assertTrue(tf.training)
          self.assertEqual(tf._m, m)
          self.assertIsNone(tf._outputs)
          self.assertEqual(tf._batch_shape, batch_shape)
          self.assertEqual(tf._min_stdv, 1e-8)
  
          # no observation noise
          with torch.random.fork_rng():
              torch.manual_seed(0)
              Y = torch.rand(
                  *batch_shape, 3, m, device=self.device, dtype=dtype
              )
          Y_tf, Yvar_tf = tf(Y, None)
          self.assertTrue(tf.training)
          self.assertTrue(torch.all(Y_tf.mean(dim=-2).abs() < 1e-4))
          self.assertIsNone(Yvar_tf)
          tf.eval()
          self.assertFalse(tf.training)
          Y_utf, Yvar_utf = tf.untransform(Y_tf, Yvar_tf)
          self.assertAllClose(Y_utf, Y)
          self.assertIsNone(Yvar_utf)
  
          # subset_output
          tf_subset = tf.subset_output(idcs=[0])
          Y_tf_subset, Yvar_tf_subset = tf_subset(Y[..., [0]])
          self.assertTrue(torch.equal(Y_tf[..., [0]], Y_tf_subset))
          self.assertIsNone(Yvar_tf_subset)
          with self.assertRaises(RuntimeError):
              tf.subset_output(idcs=[0, 1, 2])
  
          # with observation noise
          tf = Standardize(m=m, batch_shape=batch_shape)
          with torch.random.fork_rng():
              torch.manual_seed(0)
              Y = torch.rand(
                  *batch_shape, 3, m, device=self.device, dtype=dtype
              )
              Yvar = 1e-8 + torch.rand(
                  *batch_shape, 3, m, device=self.device, dtype=dtype
              )
          Y_tf, Yvar_tf = tf(Y, Yvar)
          self.assertTrue(tf.training)
          self.assertTrue(torch.all(Y_tf.mean(dim=-2).abs() < 1e-4))
          Yvar_tf_expected = Yvar / Y.std(dim=-2, keepdim=True) ** 2
          self.assertAllClose(Yvar_tf, Yvar_tf_expected)
          tf.eval()
          self.assertFalse(tf.training)
          Y_utf, Yvar_utf = tf.untransform(Y_tf, Yvar_tf)
          self.assertAllClose(Y_utf, Y)
          self.assertAllClose(Yvar_utf, Yvar)
  
          # untransform_posterior
          for interleaved, lazy in itertools.product(
              (True, False), (True, False)
          ):
              if (
                  m == 1 and interleaved
              ):  # interleave has no meaning for m=1
                  continue
              shape = batch_shape + torch.Size([3, m])
              posterior = _get_test_posterior(
                  shape,
                  device=self.device,
                  dtype=dtype,
                  interleaved=interleaved,
                  lazy=lazy,
              )
              p_utf = tf.untransform_posterior(posterior)
              self.assertEqual(p_utf.device.type, self.device.type)
              self.assertTrue(p_utf.dtype == dtype)
              mean_expected = tf.means + tf.stdvs * posterior.mean
              variance_expected = tf.stdvs**2 * posterior.variance
              self.assertAllClose(p_utf.mean, mean_expected)
              self.assertAllClose(p_utf.variance, variance_expected)
              samples = p_utf.rsample()
              self.assertEqual(samples.shape, torch.Size([1]) + shape)
              samples = p_utf.rsample(sample_shape=torch.Size([4]))
              self.assertEqual(samples.shape, torch.Size([4]) + shape)
              samples2 = p_utf.rsample(sample_shape=torch.Size([4, 2]))
              self.assertEqual(
                  samples2.shape, torch.Size([4, 2]) + shape
              )
              # TODO: Test expected covar (both interleaved and non-interleaved)
  
          # Untransform BlockDiagLinearOperator.
          if m > 1:
              base_lcv = DiagLinearOperator(
                  torch.rand(
                      *batch_shape,
                      m,
                      3,
                      device=self.device,
                      dtype=dtype,
                  )
              )
              lcv = BlockDiagLinearOperator(base_lcv)
              mvn = MultitaskMultivariateNormal(
                  mean=torch.rand(
                      *batch_shape,
                      3,
                      m,
                      device=self.device,
                      dtype=dtype,
                  ),
                  covariance_matrix=lcv,
                  interleaved=False,
              )
              posterior = GPyTorchPosterior(distribution=mvn)
              p_utf = tf.untransform_posterior(posterior)
              self.assertEqual(p_utf.device.type, self.device.type)
              self.assertTrue(p_utf.dtype == dtype)
              mean_expected = tf.means + tf.stdvs * posterior.mean
              variance_expected = tf.stdvs**2 * posterior.variance
              self.assertAllClose(p_utf.mean, mean_expected)
              self.assertAllClose(p_utf.variance, variance_expected)
              self.assertIsInstance(
                  p_utf.distribution.lazy_covariance_matrix,
                  DiagLinearOperator,
              )
              samples2 = p_utf.rsample(sample_shape=torch.Size([4, 2]))
              self.assertEqual(
                  samples2.shape,
                  torch.Size([4, 2]) + batch_shape + torch.Size([3, m]),
              )
  
          # untransform_posterior for non-GPyTorch posterior
          posterior2 = TransformedPosterior(
              posterior=posterior,
              sample_transform=lambda s: s,
              mean_transform=lambda m, v: m,
              variance_transform=lambda m, v: v,
          )
          p_utf2 = tf.untransform_posterior(posterior2)
          self.assertEqual(p_utf2.device.type, self.device.type)
          self.assertTrue(p_utf2.dtype == dtype)
          mean_expected = tf.means + tf.stdvs * posterior.mean
          variance_expected = tf.stdvs**2 * posterior.variance
          self.assertAllClose(p_utf2.mean, mean_expected)
          self.assertAllClose(p_utf2.variance, variance_expected)
          # TODO: Test expected covar (both interleaved and non-interleaved)
          samples = p_utf2.rsample()
          self.assertEqual(samples.shape, torch.Size([1]) + shape)
          samples = p_utf2.rsample(sample_shape=torch.Size([4]))
          self.assertEqual(samples.shape, torch.Size([4]) + shape)
          samples2 = p_utf2.rsample(sample_shape=torch.Size([4, 2]))
          self.assertEqual(samples2.shape, torch.Size([4, 2]) + shape)
  
          # test error on incompatible output dimension
          # TODO: add a unit test for MTGP posterior once #840 goes in
          tf_big = Standardize(m=4)
          Y = torch.arange(4, device=self.device, dtype=dtype).reshape(
              (1, 4)
          )
          tf_big(Y)
          with self.assertRaisesRegex(
              RuntimeError,
              "Incompatible output dimensions encountered. Transform has output "
              f"dimension {tf_big._m} and posterior has "
              f"{posterior._extended_shape()[-1]}.",
          ):
              tf_big.untransform_posterior(posterior2)
  
      # test transforming a subset of outcomes
      for batch_shape, dtype in itertools.product(batch_shapes, dtypes):
          m = 2
          outputs = [-1]
  
          # test init
          tf = Standardize(
              m=m, outputs=outputs, batch_shape=batch_shape
          )
          self.assertTrue(tf.training)
          self.assertEqual(tf._m, m)
          self.assertEqual(tf._outputs, [1])
          self.assertEqual(tf._batch_shape, batch_shape)
          self.assertEqual(tf._min_stdv, 1e-8)
  
          # no observation noise
          with torch.random.fork_rng():
              torch.manual_seed(0)
              Y = torch.rand(
                  *batch_shape, 3, m, device=self.device, dtype=dtype
              )
          Y_tf, Yvar_tf = tf(Y, None)
          self.assertTrue(tf.training)
          Y_tf_mean = Y_tf.mean(dim=-2)
          self.assertTrue(torch.all(Y_tf_mean[..., 1].abs() < 1e-4))
          self.assertAllClose(Y_tf_mean[..., 0], Y.mean(dim=-2)[..., 0])
          self.assertIsNone(Yvar_tf)
          tf.eval()
          self.assertFalse(tf.training)
          Y_utf, Yvar_utf = tf.untransform(Y_tf, Yvar_tf)
          self.assertAllClose(Y_utf, Y)
          self.assertIsNone(Yvar_utf)
  
          # subset_output
          tf_subset = tf.subset_output(idcs=[0])
          Y_tf_subset, Yvar_tf_subset = tf_subset(Y[..., [0]])
          self.assertTrue(torch.equal(Y_tf[..., [0]], Y_tf_subset))
          self.assertIsNone(Yvar_tf_subset)
          with self.assertRaises(RuntimeError):
              tf.subset_output(idcs=[0, 1, 2])
  
          # with observation noise
          tf = Standardize(
              m=m, outputs=outputs, batch_shape=batch_shape
          )
          with torch.random.fork_rng():
              torch.manual_seed(0)
              Y = torch.rand(
                  *batch_shape, 3, m, device=self.device, dtype=dtype
              )
              Yvar = 1e-8 + torch.rand(
                  *batch_shape, 3, m, device=self.device, dtype=dtype
              )
          Y_tf, Yvar_tf = tf(Y, Yvar)
          self.assertTrue(tf.training)
          Y_tf_mean = Y_tf.mean(dim=-2)
          self.assertTrue(torch.all(Y_tf_mean[..., 1].abs() < 1e-4))
          self.assertAllClose(Y_tf_mean[..., 0], Y.mean(dim=-2)[..., 0])
          Yvar_tf_expected = Yvar / Y.std(dim=-2, keepdim=True) ** 2
          self.assertAllClose(Yvar_tf[..., 1], Yvar_tf_expected[..., 1])
          self.assertAllClose(Yvar_tf[..., 0], Yvar[..., 0])
          tf.eval()
          self.assertFalse(tf.training)
          Y_utf, Yvar_utf = tf.untransform(Y_tf, Yvar_tf)
          self.assertAllClose(Y_utf, Y)
          self.assertAllClose(Yvar_utf, Yvar)
  
          # error on untransform_posterior
          with self.assertRaises(NotImplementedError):
              tf.untransform_posterior(None)
  
imports: import itertools
  from copy import deepcopy
  import torch
  from botorch.models.transforms.outcome import Bilog, ChainedOutcomeTransform, Log, OutcomeTransform, Power, Standardize
  from botorch.models.transforms.utils import norm_to_lognorm_mean, norm_to_lognorm_variance
  from botorch.posteriors import GPyTorchPosterior, TransformedPosterior
  from botorch.utils.testing import BotorchTestCase
  from gpytorch.distributions import MultitaskMultivariateNormal, MultivariateNormal
  from linear_operator.operators import BlockDiagLinearOperator, DenseLinearOperator, DiagLinearOperator
label_desc: Use image node_id 4 for calling the TestOutcomeTransforms obj's underlying member method code with example usage: obj.test_standardize() without return types