code: def test_normalize(self) -> None:
      # set seed to range where this is known to not be flaky
      torch.manual_seed(randint(0, 1000))
      for dtype in (torch.float, torch.double):
          # basic init, learned bounds
          nlz = Normalize(d=2)
          self.assertFalse(nlz.is_one_to_many)
          self.assertTrue(nlz.learn_bounds)
          self.assertTrue(nlz.training)
          self.assertEqual(nlz._d, 2)
          self.assertEqual(nlz.mins.shape, torch.Size([1, 2]))
          self.assertEqual(nlz.ranges.shape, torch.Size([1, 2]))
          nlz = Normalize(d=2, batch_shape=torch.Size([3]))
          self.assertTrue(nlz.learn_bounds)
          self.assertTrue(nlz.training)
          self.assertEqual(nlz._d, 2)
          self.assertEqual(nlz.mins.shape, torch.Size([3, 1, 2]))
          self.assertEqual(nlz.ranges.shape, torch.Size([3, 1, 2]))
          self.assertTrue(nlz.equals(Normalize(**nlz.get_init_args())))
  
          # learn_bounds=False with no bounds.
          with self.assertWarnsRegex(UserInputWarning, "learn_bounds"):
              Normalize(d=2, learn_bounds=False)
  
          # learn_bounds=True with bounds provided.
          bounds = torch.zeros(2, 2, device=self.device, dtype=dtype)
          nlz = Normalize(d=2, bounds=bounds, learn_bounds=True)
          self.assertTrue(nlz.learn_bounds)
          self.assertTrue(torch.equal(nlz.mins, bounds[..., 0:1, :]))
          self.assertTrue(
              torch.equal(
                  nlz.ranges, bounds[..., 1:2, :] - bounds[..., 0:1, :]
              )
          )
  
          # basic init, fixed bounds
          nlz = Normalize(d=2, bounds=bounds)
          self.assertFalse(nlz.learn_bounds)
          self.assertTrue(nlz.training)
          self.assertEqual(nlz._d, 2)
          self.assertTrue(torch.equal(nlz.mins, bounds[..., 0:1, :]))
          self.assertTrue(
              torch.equal(
                  nlz.ranges, bounds[..., 1:2, :] - bounds[..., 0:1, :]
              )
          )
          # with grad
          bounds.requires_grad = True
          bounds = bounds * 2
          self.assertIsNotNone(bounds.grad_fn)
          nlz = Normalize(d=2, bounds=bounds)
          # Set learn_coefficients=True for testing.
          nlz.learn_coefficients = True
          # We have grad in train mode.
          self.assertIsNotNone(nlz.coefficient.grad_fn)
          self.assertIsNotNone(nlz.offset.grad_fn)
          # Grad is detached in eval mode.
          nlz.eval()
          self.assertIsNone(nlz.coefficient.grad_fn)
          self.assertIsNone(nlz.offset.grad_fn)
          self.assertTrue(nlz.equals(Normalize(**nlz.get_init_args())))
  
          # basic init, provided indices
          with self.assertRaises(ValueError):
              nlz = Normalize(d=2, indices=[0, 1, 2])
          with self.assertRaises(ValueError):
              nlz = Normalize(d=2, indices=[0, 2])
          with self.assertRaises(ValueError):
              nlz = Normalize(d=2, indices=[0, 0])
          with self.assertRaises(ValueError):
              nlz = Normalize(d=2, indices=[])
          nlz = Normalize(d=2, indices=[0])
          self.assertTrue(nlz.learn_bounds)
          self.assertTrue(nlz.training)
          self.assertEqual(nlz._d, 2)
          self.assertEqual(nlz.mins.shape, torch.Size([1, 1]))
          self.assertEqual(nlz.ranges.shape, torch.Size([1, 1]))
          self.assertEqual(len(nlz.indices), 1)
          nlz.to(device=self.device)
          self.assertTrue(
              (
                  nlz.indices
                  == torch.tensor(
                      [0], dtype=torch.long, device=self.device
                  )
              ).all()
          )
          self.assertTrue(nlz.equals(Normalize(**nlz.get_init_args())))
  
          # test .to
          other_dtype = (
              torch.float if dtype == torch.double else torch.double
          )
          nlz.to(other_dtype)
          self.assertTrue(nlz.mins.dtype == other_dtype)
          # test incompatible dimensions of specified bounds
          with self.assertRaises(BotorchTensorDimensionError):
              bounds = torch.zeros(
                  2, 3, device=self.device, dtype=dtype
              )
              Normalize(d=2, bounds=bounds)
  
          # test jitter
          nlz = Normalize(d=2, min_range=1e-4)
          self.assertEqual(nlz.min_range, 1e-4)
          X = torch.cat((torch.randn(4, 1), torch.zeros(4, 1)), dim=-1)
          X = X.to(self.device)
          self.assertEqual(torch.isfinite(nlz(X)).sum(), X.numel())
          with self.assertRaisesRegex(
              ValueError, r"must have at least \d+ dim"
          ):
              nlz(torch.randn(X.shape[-1], dtype=dtype))
  
          # basic usage
          for batch_shape in (torch.Size(), torch.Size([3])):
              # learned bounds
              nlz = Normalize(d=2, batch_shape=batch_shape)
              X = torch.randn(
                  *batch_shape, 4, 2, device=self.device, dtype=dtype
              )
              for _X in (
                  torch.stack((X, X)),
                  X,
              ):  # check batch_shape is obeyed
                  X_nlzd = nlz(_X)
                  self.assertEqual(
                      nlz.mins.shape, batch_shape + (1, X.shape[-1])
                  )
                  self.assertEqual(
                      nlz.ranges.shape, batch_shape + (1, X.shape[-1])
                  )
  
              self.assertEqual(X_nlzd.min().item(), 0.0)
              self.assertEqual(X_nlzd.max().item(), 1.0)
  
              nlz.eval()
              X_unnlzd = nlz.untransform(X_nlzd)
              self.assertAllClose(X, X_unnlzd, atol=1e-3, rtol=1e-3)
              expected_bounds = torch.cat(
                  [
                      X.min(dim=-2, keepdim=True)[0],
                      X.max(dim=-2, keepdim=True)[0],
                  ],
                  dim=-2,
              )
              atol = 1e-6 if dtype is torch.float32 else 1e-12
              rtol = 1e-4 if dtype is torch.float32 else 1e-8
              self.assertAllClose(
                  nlz.bounds, expected_bounds, atol=atol, rtol=rtol
              )
              # test errors on wrong shape
              nlz = Normalize(d=2, batch_shape=batch_shape)
              X = torch.randn(
                  *batch_shape, 2, 1, device=self.device, dtype=dtype
              )
              with self.assertRaises(BotorchTensorDimensionError):
                  nlz(X)
  
              # fixed bounds
              bounds = torch.tensor(
                  [[-2.0, -1], [1, 2.0]],
                  device=self.device,
                  dtype=dtype,
              ).expand(*batch_shape, 2, 2)
              nlz = Normalize(d=2, bounds=bounds)
              X = torch.rand(
                  *batch_shape, 4, 2, device=self.device, dtype=dtype
              )
              X_nlzd = nlz(X)
              self.assertTrue(torch.equal(nlz.bounds, bounds))
              X_unnlzd = nlz.untransform(X_nlzd)
              self.assertAllClose(X, X_unnlzd, atol=1e-4, rtol=1e-4)
  
              # test no normalization on eval
              nlz = Normalize(
                  d=2,
                  bounds=bounds,
                  batch_shape=batch_shape,
                  transform_on_eval=False,
              )
              X_nlzd = nlz(X)
              X_unnlzd = nlz.untransform(X_nlzd)
              self.assertAllClose(X, X_unnlzd, atol=1e-4, rtol=1e-4)
              nlz.eval()
              self.assertTrue(torch.equal(nlz(X), X))
  
              # test no normalization on train
              nlz = Normalize(
                  d=2,
                  bounds=bounds,
                  batch_shape=batch_shape,
                  transform_on_train=False,
              )
              X_nlzd = nlz(X)
              self.assertTrue(torch.equal(nlz(X), X))
              nlz.eval()
              X_nlzd = nlz(X)
              X_unnlzd = nlz.untransform(X_nlzd)
              self.assertAllClose(X, X_unnlzd, atol=1e-4, rtol=1e-4)
  
              # test reverse
              nlz = Normalize(
                  d=2,
                  bounds=bounds,
                  batch_shape=batch_shape,
                  reverse=True,
              )
              X2 = nlz(X_nlzd)
              self.assertAllClose(X2, X, atol=1e-4, rtol=1e-4)
              X_nlzd2 = nlz.untransform(X2)
              self.assertAllClose(X_nlzd, X_nlzd2, atol=1e-4, rtol=1e-4)
  
              # test non complete indices
              indices = [0, 2]
              nlz = Normalize(
                  d=3, batch_shape=batch_shape, indices=indices
              )
              X = torch.randn(
                  *batch_shape, 4, 3, device=self.device, dtype=dtype
              )
              X_nlzd = nlz(X)
              self.assertEqual(X_nlzd[..., indices].min().item(), 0.0)
              self.assertEqual(X_nlzd[..., indices].max().item(), 1.0)
              self.assertAllClose(X_nlzd[..., 1], X[..., 1])
              nlz.eval()
              X_unnlzd = nlz.untransform(X_nlzd)
              self.assertAllClose(X, X_unnlzd, atol=1e-4, rtol=1e-4)
              expected_bounds = torch.cat(
                  [
                      X.min(dim=-2, keepdim=True)[0],
                      X.max(dim=-2, keepdim=True)[0],
                  ],
                  dim=-2,
              )[..., indices]
              self.assertTrue(
                  torch.allclose(
                      nlz.bounds, expected_bounds, atol=1e-4, rtol=1e-4
                  )
              )
              # test errors on wrong shape
              nlz = Normalize(d=2, batch_shape=batch_shape)
              X = torch.randn(
                  *batch_shape, 2, 1, device=self.device, dtype=dtype
              )
              with self.assertRaises(BotorchTensorDimensionError):
                  nlz(X)
  
              # test equals
              nlz = Normalize(
                  d=2,
                  bounds=bounds,
                  batch_shape=batch_shape,
                  reverse=True,
              )
              nlz2 = Normalize(
                  d=2,
                  bounds=bounds,
                  batch_shape=batch_shape,
                  reverse=False,
              )
              self.assertFalse(nlz.equals(nlz2))
              nlz3 = Normalize(
                  d=2,
                  bounds=bounds,
                  batch_shape=batch_shape,
                  reverse=True,
              )
              self.assertTrue(nlz.equals(nlz3))
              new_bounds = bounds + 1
              nlz4 = Normalize(
                  d=2,
                  bounds=new_bounds,
                  batch_shape=batch_shape,
                  reverse=True,
              )
              self.assertFalse(nlz.equals(nlz4))
              nlz5 = Normalize(d=2, batch_shape=batch_shape)
              self.assertFalse(nlz.equals(nlz5))
              nlz6 = Normalize(
                  d=2, batch_shape=batch_shape, indices=[0, 1]
              )
              self.assertFalse(nlz5.equals(nlz6))
              nlz7 = Normalize(
                  d=2, batch_shape=batch_shape, indices=[0]
              )
              self.assertFalse(nlz5.equals(nlz7))
              nlz8 = Normalize(
                  d=2, batch_shape=batch_shape, indices=[0, 1]
              )
              self.assertTrue(nlz6.equals(nlz8))
              nlz9 = Normalize(
                  d=3, batch_shape=batch_shape, indices=[0, 1]
              )
              nlz10 = Normalize(
                  d=3, batch_shape=batch_shape, indices=[0, 2]
              )
              self.assertFalse(nlz9.equals(nlz10))
  
              # test with grad
              nlz = Normalize(d=1)
              X.requires_grad = True
              X = X * 2
              self.assertIsNotNone(X.grad_fn)
              nlz(X)
              self.assertIsNotNone(nlz.coefficient.grad_fn)
              self.assertIsNotNone(nlz.offset.grad_fn)
              nlz.eval()
              self.assertIsNone(nlz.coefficient.grad_fn)
              self.assertIsNone(nlz.offset.grad_fn)
  
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 2 for calling the TestInputTransforms obj's underlying member method code with example usage: obj.test_normalize() without return types