code: def test_noisy_expected_improvement(self):
      for dtype in (torch.float, torch.double):
          model = self._get_model(dtype=dtype)
          X_observed = model.train_inputs[0]
          nfan = 5
          nEI = NoisyExpectedImprovement(
              model, X_observed, num_fantasies=nfan
          )
          LogNEI = LogNoisyExpectedImprovement(
              model, X_observed, num_fantasies=nfan
          )
          # before assigning, check that the attributes exist
          self.assertTrue(hasattr(LogNEI, "model"))
          self.assertTrue(hasattr(LogNEI, "best_f"))
          self.assertIsInstance(LogNEI.model, SingleTaskGP)
          self.assertIsInstance(
              LogNEI.model.likelihood, FixedNoiseGaussianLikelihood
          )
          LogNEI.model = (
              nEI.model
          )  # let the two share their values and fantasies
          LogNEI.best_f = nEI.best_f
  
          X_test = torch.tensor(
              [[[0.25]], [[0.75]]],
              device=X_observed.device,
              dtype=dtype,
          )
          X_test_log = X_test.clone()
          X_test.requires_grad = True
          X_test_log.requires_grad = True
          val = nEI(X_test)
          # testing logNEI yields the same result (also checks dtype)
          log_val = LogNEI(X_test_log)
          exp_log_val = log_val.exp()
          # notably, val[1] is usually zero in this test, which is precisely what
          # gives rise to problems during optimization, and what logNEI avoids
          # since it generally takes a large negative number (<-2000) and has
          # strong gradient signals in this regime.
          rtol = 1e-12 if dtype == torch.double else 1e-6
          atol = rtol
          self.assertAllClose(exp_log_val, val, atol=atol, rtol=rtol)
          # test basics
          self.assertEqual(val.dtype, dtype)
          self.assertEqual(val.device.type, X_observed.device.type)
          self.assertEqual(val.shape, torch.Size([2]))
          # test values
          self.assertGreater(val[0].item(), 8e-5)
          self.assertLess(val[1].item(), 1e-6)
          # test gradient
          val.sum().backward()
          self.assertGreater(X_test.grad[0].abs().item(), 8e-6)
          # testing gradient through exp of log computation
          exp_log_val.sum().backward()
          # testing that first gradient element coincides. The second is in the
          # regime where the naive implementation looses accuracy.
          atol = 2e-5 if dtype == torch.float32 else 1e-12
          rtol = atol
          self.assertAllClose(
              X_test.grad[0], X_test_log.grad[0], atol=atol, rtol=rtol
          )
  
          # test inferred noise model
          other_model = SingleTaskGP(
              X_observed, model.train_targets.unsqueeze(-1)
          )
          for constructor in (
              NoisyExpectedImprovement,
              LogNoisyExpectedImprovement,
          ):
              with self.assertRaises(UnsupportedError):
                  constructor(other_model, X_observed, num_fantasies=5)
              # Test constructor with minimize
              acqf = constructor(
                  model, X_observed, num_fantasies=5, maximize=False
              )
              # test evaluation without gradients enabled
              with torch.no_grad():
                  acqf(X_test)
  
              # testing gradients are only propagated if X_observed requires them
              # i.e. kernel hyper-parameters are not tracked through to best_f
              X_observed.requires_grad = False
              acqf = constructor(model, X_observed, num_fantasies=5)
              self.assertFalse(acqf.best_f.requires_grad)
  
              X_observed.requires_grad = True
              acqf = constructor(model, X_observed, num_fantasies=5)
              self.assertTrue(acqf.best_f.requires_grad)
  
imports: import math
  import torch
  from botorch.acquisition import qAnalyticProbabilityOfImprovement
  from botorch.acquisition.analytic import _compute_log_prob_feas, _ei_helper, _log_ei_helper, AnalyticAcquisitionFunction, ConstrainedExpectedImprovement, ExpectedImprovement, LogConstrainedExpectedImprovement, LogExpectedImprovement, LogNoisyExpectedImprovement, LogProbabilityOfImprovement, NoisyExpectedImprovement, PosteriorMean, PosteriorStandardDeviation, ProbabilityOfImprovement, ScalarizedPosteriorMean, UpperConfidenceBound
  from botorch.acquisition.objective import IdentityMCObjective, ScalarizedPosteriorTransform
  from botorch.exceptions import UnsupportedError
  from botorch.models import SingleTaskGP
  from botorch.posteriors import GPyTorchPosterior
  from botorch.utils.testing import BotorchTestCase, MockModel, MockPosterior
  from gpytorch.distributions import MultitaskMultivariateNormal, MultivariateNormal
  from gpytorch.likelihoods.gaussian_likelihood import FixedNoiseGaussianLikelihood
label_desc: Use image node_id 2 for calling the TestNoisyExpectedImprovement obj's underlying member method code with example usage: obj.test_noisy_expected_improvement() without return types