code: def test_chained_input_transform(self):
      ds = (1, 2)
      batch_shapes = (torch.Size(), torch.Size([2]))
      dtypes = (torch.float, torch.double)
      # set seed to range where this is known to not be flaky
      torch.manual_seed(randint(0, 1000))
  
      for d, batch_shape, dtype in itertools.product(
          ds, batch_shapes, dtypes
      ):
          bounds = torch.tensor(
              [[-2.0] * d, [2.0] * d], device=self.device, dtype=dtype
          )
          tf1 = Normalize(d=d, bounds=bounds, batch_shape=batch_shape)
          tf2 = Normalize(d=d, batch_shape=batch_shape)
          tf = ChainedInputTransform(stz_fixed=tf1, stz_learned=tf2)
          # make copies for validation below
          tf1_, tf2_ = deepcopy(tf1), deepcopy(tf2)
          self.assertTrue(tf.training)
          self.assertEqual(
              sorted(tf.keys()), ["stz_fixed", "stz_learned"]
          )
          self.assertEqual(tf["stz_fixed"], tf1)
          self.assertEqual(tf["stz_learned"], tf2)
          self.assertFalse(tf.is_one_to_many)
  
          X = torch.rand(
              *batch_shape, 4, d, device=self.device, dtype=dtype
          )
          X_tf = tf(X)
          X_tf_ = tf2_(tf1_(X))
          self.assertTrue(tf1.training)
          self.assertTrue(tf2.training)
          self.assertTrue(torch.equal(X_tf, X_tf_))
          X_utf = tf.untransform(X_tf)
          self.assertAllClose(X_utf, X, atol=1e-4, rtol=1e-4)
  
          # test not transformed on eval
          tf1.transform_on_eval = False
          tf2.transform_on_eval = False
          tf = ChainedInputTransform(stz_fixed=tf1, stz_learned=tf2)
          tf.eval()
          self.assertTrue(torch.equal(tf(X), X))
          tf1.transform_on_eval = True
          tf2.transform_on_eval = True
          tf = ChainedInputTransform(stz_fixed=tf1, stz_learned=tf2)
          tf.eval()
          self.assertTrue(torch.equal(tf(X), X_tf))
  
          # test not transformed on train
          tf1.transform_on_train = False
          tf2.transform_on_train = False
          tf = ChainedInputTransform(stz_fixed=tf1, stz_learned=tf2)
          tf.train()
          self.assertTrue(torch.equal(tf(X), X))
  
          # test __eq__
          other_tf = ChainedInputTransform(
              stz_fixed=tf1, stz_learned=tf2
          )
          self.assertTrue(tf.equals(other_tf))
          # change order
          other_tf = ChainedInputTransform(
              stz_learned=tf2, stz_fixed=tf1
          )
          self.assertFalse(tf.equals(other_tf))
          # Identical transforms but different objects.
          other_tf = ChainedInputTransform(
              stz_fixed=tf1, stz_learned=deepcopy(tf2)
          )
          self.assertTrue(tf.equals(other_tf))
  
          # test preprocess_transform
          tf2.transform_on_train = False
          tf1.transform_on_train = True
          tf = ChainedInputTransform(stz_fixed=tf1, stz_learned=tf2)
          self.assertTrue(
              torch.equal(tf.preprocess_transform(X), tf1.transform(X))
          )
  
      # test one-to-many
      tf2 = InputPerturbation(perturbation_set=bounds)
      tf = ChainedInputTransform(stz=tf1, pert=tf2)
      self.assertTrue(tf.is_one_to_many)
  
imports: import itertools
  import warnings
  from copy import deepcopy
  from random import randint
  import torch
  from botorch import settings
  from botorch.exceptions.errors import BotorchTensorDimensionError
  from botorch.exceptions.warnings import UserInputWarning
  from botorch.models.transforms.input import AffineInputTransform, AppendFeatures, ChainedInputTransform, FilterFeatures, InputPerturbation, InputStandardize, InputTransform, Log10, Normalize, OneHotToNumeric, Round, Warp
  from botorch.models.transforms.utils import expand_and_copy_tensor
  from botorch.models.utils import fantasize
  from botorch.utils.testing import BotorchTestCase
  from gpytorch.priors import LogNormalPrior
  from torch import Tensor
  from torch.distributions import Kumaraswamy
  from torch.nn import Module
  from torch.nn.functional import one_hot
label_desc: Use image node_id 4 for calling the TestInputTransforms obj's underlying member method code with example usage: obj.test_chained_input_transform() without return types