code: def test_bilog(self, seed=0):
      torch.random.manual_seed(seed)
  
      ms = (1, 2)
      batch_shapes = (torch.Size(), torch.Size([2]))
      dtypes = (torch.float, torch.double)
  
      # test transform and untransform
      for m, batch_shape, dtype in itertools.product(
          ms, batch_shapes, dtypes
      ):
          # test init
          tf = Bilog()
          self.assertTrue(tf.training)
          self.assertIsNone(tf._outputs)
  
          # no observation noise
          Y = torch.rand(
              *batch_shape, 3, m, device=self.device, dtype=dtype
          )
          Y_tf, Yvar_tf = tf(Y, None)
          self.assertTrue(tf.training)
          self.assertAllClose(Y_tf, Y.sign() * (Y.abs() + 1).log())
          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, atol=1e-7)
          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)
  
          # test error if observation noise present
          tf = Bilog()
          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
          )
          with self.assertRaises(NotImplementedError):
              tf(Y, Yvar)
          tf.eval()
          with self.assertRaises(NotImplementedError):
              tf.untransform(Y, Yvar)
  
          # untransform_posterior
          tf = Bilog()
          Y_tf, Yvar_tf = tf(Y, None)
          tf.eval()
          shape = batch_shape + torch.Size([3, m])
          posterior = _get_test_posterior(
              shape, device=self.device, dtype=dtype
          )
          p_utf = tf.untransform_posterior(posterior)
          self.assertIsInstance(p_utf, TransformedPosterior)
          self.assertEqual(p_utf.device.type, self.device.type)
          self.assertTrue(p_utf.dtype == dtype)
  
          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)
  
      # test transforming a subset of outcomes
      for batch_shape, dtype in itertools.product(batch_shapes, dtypes):
          m = 2
          outputs = [-1]
  
          # test init
          tf = Bilog(outputs=outputs)
          self.assertTrue(tf.training)
          # cannot normalize indices b/c we don't know dimension yet
          self.assertEqual(tf._outputs, [-1])
  
          # no observation noise
          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.allclose(
                  Y_tf[..., 1],
                  Y[..., 1].sign() * (Y[..., 1].abs() + 1).log(),
              )
          )
          self.assertAllClose(Y_tf[..., 0], Y[..., 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
          with self.assertRaises(NotImplementedError):
              tf_subset = tf.subset_output(idcs=[0])
  
          # with observation noise
          tf = Bilog(outputs=outputs)
          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
          )
          with self.assertRaises(NotImplementedError):
              tf(Y, Yvar)
  
          # error on untransform_posterior
          with self.assertRaises(NotImplementedError):
              tf.untransform_posterior(None)
  
          # test subset_output with positive on subset of outcomes (pos. index)
          tf = Bilog(outputs=[0])
          Y_tf, Yvar_tf = tf(Y, None)
          tf_subset = tf.subset_output(idcs=[0])
          Y_tf_subset, Yvar_tf_subset = tf_subset(Y[..., [0]], None)
          self.assertTrue(torch.equal(Y_tf_subset, Y_tf[..., [0]]))
          self.assertIsNone(Yvar_tf_subset)
  
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 9 for calling the TestOutcomeTransforms obj's underlying member method code with example usage: obj.test_bilog(seed) without return types