Beispiel #1
0
 def test_initialize_q_batch_nonneg(self, cuda=False):
     device = torch.device("cuda") if cuda else torch.device("cpu")
     for dtype in (torch.float, torch.double):
         # basic test
         X = torch.rand(5, 3, 4, device=device, dtype=dtype)
         Y = torch.rand(5, device=device, dtype=dtype)
         ics = initialize_q_batch_nonneg(X=X, Y=Y, n=2)
         self.assertEqual(ics.shape, torch.Size([2, 3, 4]))
         self.assertEqual(ics.device, X.device)
         self.assertEqual(ics.dtype, X.dtype)
         # ensure nothing happens if we want all samples
         ics = initialize_q_batch_nonneg(X=X, Y=Y, n=5)
         self.assertTrue(torch.equal(X, ics))
         # make sure things work with constant inputs
         Y = torch.ones(5, device=device, dtype=dtype)
         ics = initialize_q_batch_nonneg(X=X, Y=Y, n=2)
         self.assertEqual(ics.shape, torch.Size([2, 3, 4]))
         self.assertEqual(ics.device, X.device)
         self.assertEqual(ics.dtype, X.dtype)
         # ensure raises correct warning
         Y = torch.zeros(5, device=device, dtype=dtype)
         with warnings.catch_warnings(
                 record=True) as w, settings.debug(True):
             ics = initialize_q_batch_nonneg(X=X, Y=Y, n=2)
             self.assertEqual(len(w), 1)
             self.assertTrue(
                 issubclass(w[-1].category, BadInitialCandidatesWarning))
         self.assertEqual(ics.shape, torch.Size([2, 3, 4]))
         with self.assertRaises(RuntimeError):
             initialize_q_batch_nonneg(X=X, Y=Y, n=10)
         # test less than `n` positive acquisition values
         Y = torch.arange(5, device=device, dtype=dtype) - 3
         ics = initialize_q_batch_nonneg(X=X, Y=Y, n=2)
         self.assertEqual(ics.shape, torch.Size([2, 3, 4]))
         self.assertEqual(ics.device, X.device)
         self.assertEqual(ics.dtype, X.dtype)
         # check that we chose the point with the positive acquisition value
         self.assertTrue(
             torch.equal(ics[0], X[-1]) or torch.equal(ics[1], X[-1]))
         # test less than `n` alpha_pos values
         Y = torch.arange(5, device=device, dtype=dtype)
         ics = initialize_q_batch_nonneg(X=X, Y=Y, n=2, alpha=1.0)
         self.assertEqual(ics.shape, torch.Size([2, 3, 4]))
         self.assertEqual(ics.device, X.device)
         self.assertEqual(ics.dtype, X.dtype)
    def test_gen_batch_initial_conditions_highdim(self):
        d = 2200  # 2200 * 10 (q) > 21201 (sobol max dim)
        bounds = torch.stack([torch.zeros(d), torch.ones(d)])
        ffs_map = {i: random() for i in range(0, d, 2)}
        mock_acqf = MockAcquisitionFunction()
        mock_acqf.objective = lambda y: y.squeeze(-1)
        for dtype in (torch.float, torch.double):
            bounds = bounds.to(device=self.device, dtype=dtype)
            mock_acqf.X_baseline = bounds  # for testing sample_around_best
            mock_acqf.model = MockModel(MockPosterior(mean=bounds[:, :1]))

            for nonnegative, seed, ffs, sample_around_best in product(
                [True, False], [None, 1234], [None, ffs_map], [True, False]):
                with warnings.catch_warnings(
                        record=True) as ws, settings.debug(True):
                    batch_initial_conditions = gen_batch_initial_conditions(
                        acq_function=MockAcquisitionFunction(),
                        bounds=bounds,
                        q=10,
                        num_restarts=1,
                        raw_samples=2,
                        fixed_features=ffs,
                        options={
                            "nonnegative": nonnegative,
                            "eta": 0.01,
                            "alpha": 0.1,
                            "seed": seed,
                            "sample_around_best": sample_around_best,
                        },
                    )
                    self.assertTrue(
                        any(
                            issubclass(w.category, SamplingWarning)
                            for w in ws))
                expected_shape = torch.Size([1, 10, d])
                self.assertEqual(batch_initial_conditions.shape,
                                 expected_shape)
                self.assertEqual(batch_initial_conditions.device,
                                 bounds.device)
                self.assertEqual(batch_initial_conditions.dtype, bounds.dtype)
                if ffs is not None:
                    for idx, val in ffs.items():
                        self.assertTrue(
                            torch.all(batch_initial_conditions[...,
                                                               idx] == val))
Beispiel #3
0
 def test_fit_gpytorch_model_singular(self):
     options = {"disp": False, "maxiter": 5}
     for dtype in (torch.float, torch.double):
         X_train = torch.rand(2, 2, device=self.device, dtype=dtype)
         Y_train = torch.zeros(2, 1, device=self.device, dtype=dtype)
         test_likelihood = GaussianLikelihood(noise_constraint=GreaterThan(
             -1.0, transform=None, initial_value=0.0))
         gp = SingleTaskGP(X_train, Y_train, likelihood=test_likelihood)
         mll = ExactMarginalLogLikelihood(gp.likelihood, gp)
         mll.to(device=self.device, dtype=dtype)
         # this will do multiple retries (and emit warnings, which is desired)
         with warnings.catch_warnings(
                 record=True) as ws, settings.debug(True):
             fit_gpytorch_model(mll, options=options, max_retries=2)
             self.assertTrue(
                 any(
                     issubclass(w.category, OptimizationWarning)
                     for w in ws))
Beispiel #4
0
    def test_InverseCostWeightedUtility(self):
        for batch_shape in ([], [2]):
            for dtype in (torch.float, torch.double):
                # the event shape is `batch_shape x q x t`
                mean = 1 + torch.rand(
                    *batch_shape, 2, 1, device=self.device, dtype=dtype
                )
                mm = MockModel(MockPosterior(mean=mean))

                X = torch.randn(*batch_shape, 3, 2, device=self.device, dtype=dtype)
                deltas = torch.rand(4, *batch_shape, device=self.device, dtype=dtype)

                # test that sampler is required if use_mean=False
                icwu = InverseCostWeightedUtility(mm, use_mean=False)
                with self.assertRaises(RuntimeError):
                    icwu(X, deltas)

                # check warning for negative cost
                mm = MockModel(MockPosterior(mean=mean.clamp_max(-1e-6)))
                icwu = InverseCostWeightedUtility(mm)
                with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                    icwu(X, deltas)
                    self.assertTrue(
                        any(issubclass(w.category, CostAwareWarning) for w in ws)
                    )

                # basic test
                mm = MockModel(MockPosterior(mean=mean))
                icwu = InverseCostWeightedUtility(mm)
                ratios = icwu(X, deltas)
                self.assertTrue(
                    torch.equal(ratios, deltas / mean.squeeze(-1).sum(dim=-1))
                )

                # sampling test
                samples = 1 + torch.rand(  # event shape is q x m
                    *batch_shape, 3, 1, device=self.device, dtype=dtype
                )
                mm = MockModel(MockPosterior(samples=samples))
                icwu = InverseCostWeightedUtility(mm, use_mean=False)
                ratios = icwu(X, deltas, sampler=IIDNormalSampler(4))
                self.assertTrue(
                    torch.equal(ratios, deltas / samples.squeeze(-1).sum(dim=-1))
                )
Beispiel #5
0
 def test_generic_mc_objective_deprecated(self):
     for dtype in (torch.float, torch.double):
         with warnings.catch_warnings(record=True) as ws, settings.debug(True):
             obj = GenericMCObjective(generic_obj_deprecated)
             warning_msg = (
                 "The `objective` callable of `GenericMCObjective` is expected to "
                 "take two arguments. Passing a callable that expects a single "
                 "argument will result in an error in future versions."
             )
             self.assertTrue(
                 any(issubclass(w.category, DeprecationWarning) for w in ws)
             )
             self.assertTrue(any(warning_msg in str(w.message) for w in ws))
         samples = torch.randn(1, device=self.device, dtype=dtype)
         self.assertTrue(torch.equal(obj(samples), generic_obj(samples)))
         samples = torch.randn(2, device=self.device, dtype=dtype)
         self.assertTrue(torch.equal(obj(samples), generic_obj(samples)))
         samples = torch.randn(3, 1, device=self.device, dtype=dtype)
         self.assertTrue(torch.equal(obj(samples), generic_obj(samples)))
         samples = torch.randn(3, 2, device=self.device, dtype=dtype)
         self.assertTrue(torch.equal(obj(samples), generic_obj(samples)))
Beispiel #6
0
 def test_fit_gpytorch_model_sequential(self):
     options = {"disp": False, "maxiter": 1}
     for double, kind, outcome_transform in product(
         (False, True),
         ("SingleTaskGP", "FixedNoiseGP", "HeteroskedasticSingleTaskGP"),
         (False, True),
     ):
         with warnings.catch_warnings(
                 record=True) as ws, settings.debug(True):
             mll = self._getBatchedModel(
                 kind=kind,
                 double=double,
                 outcome_transform=outcome_transform)
             mll = fit_gpytorch_model(mll, options=options, max_retries=1)
             mll = self._getBatchedModel(
                 kind=kind,
                 double=double,
                 outcome_transform=outcome_transform)
             mll = fit_gpytorch_model(mll,
                                      options=options,
                                      sequential=True,
                                      max_retries=1)
             mll = self._getBatchedModel(
                 kind=kind,
                 double=double,
                 outcome_transform=outcome_transform)
             mll = fit_gpytorch_model(mll,
                                      options=options,
                                      sequential=False,
                                      max_retries=1)
             if kind == "HeteroskedasticSingleTaskGP":
                 self.assertTrue(
                     any(
                         issubclass(w.category, BotorchWarning)
                         for w in ws))
                 self.assertTrue(
                     any("Failed to convert ModelList to batched model" in
                         str(w.message) for w in ws))
Beispiel #7
0
 def test_initialize_q_batch(self):
     for dtype in (torch.float, torch.double):
         # basic test
         X = torch.rand(5, 3, 4, device=self.device, dtype=dtype)
         Y = torch.rand(5, device=self.device, dtype=dtype)
         ics = initialize_q_batch(X=X, Y=Y, n=2)
         self.assertEqual(ics.shape, torch.Size([2, 3, 4]))
         self.assertEqual(ics.device, X.device)
         self.assertEqual(ics.dtype, X.dtype)
         # ensure nothing happens if we want all samples
         ics = initialize_q_batch(X=X, Y=Y, n=5)
         self.assertTrue(torch.equal(X, ics))
         # ensure raises correct warning
         Y = torch.zeros(5, device=self.device, dtype=dtype)
         with warnings.catch_warnings(
                 record=True) as w, settings.debug(True):
             ics = initialize_q_batch(X=X, Y=Y, n=2)
             self.assertEqual(len(w), 1)
             self.assertTrue(
                 issubclass(w[-1].category, BadInitialCandidatesWarning))
         self.assertEqual(ics.shape, torch.Size([2, 3, 4]))
         with self.assertRaises(RuntimeError):
             initialize_q_batch(X=X, Y=Y, n=10)
Beispiel #8
0
 def test_construct_base_samples(self):
     test_shapes = [
         {"batch": [2], "output": [4, 3], "sample": [5]},
         {"batch": [1], "output": [5, 3], "sample": [5, 6]},
         {"batch": [2, 3], "output": [2, 3], "sample": [5]},
     ]
     for tshape, qmc, seed, dtype in itertools.product(
         test_shapes, (False, True), (None, 1234), (torch.float, torch.double)
     ):
         batch_shape = torch.Size(tshape["batch"])
         output_shape = torch.Size(tshape["output"])
         sample_shape = torch.Size(tshape["sample"])
         expected_shape = sample_shape + batch_shape + output_shape
         samples = construct_base_samples(
             batch_shape=batch_shape,
             output_shape=output_shape,
             sample_shape=sample_shape,
             qmc=qmc,
             seed=seed,
             device=self.device,
             dtype=dtype,
         )
         self.assertEqual(samples.shape, expected_shape)
         self.assertEqual(samples.device.type, self.device.type)
         self.assertEqual(samples.dtype, dtype)
     # check that warning is issued if dimensionality is too large
     with warnings.catch_warnings(record=True) as w, settings.debug(True):
         construct_base_samples(
             batch_shape=torch.Size(),
             output_shape=torch.Size([200, 6]),
             sample_shape=torch.Size([1]),
             qmc=True,
         )
         self.assertEqual(len(w), 1)
         self.assertTrue(issubclass(w[-1].category, SamplingWarning))
         exp_str = f"maximum supported by qmc ({SobolEngine.MAXDIM})"
         self.assertTrue(exp_str in str(w[-1].message))
Beispiel #9
0
 def test_validate_input_scaling(self):
     train_X = 2 + torch.rand(3, 4, 3)
     train_Y = torch.randn(3, 4, 2)
     # check that nothing is being checked
     with settings.validate_input_scaling(False), settings.debug(True):
         with warnings.catch_warnings(record=True) as ws:
             validate_input_scaling(train_X=train_X, train_Y=train_Y)
             self.assertFalse(
                 any(issubclass(w.category, InputDataWarning) for w in ws))
     # check that warnings are being issued
     with settings.debug(True), warnings.catch_warnings(record=True) as ws:
         validate_input_scaling(train_X=train_X, train_Y=train_Y)
         self.assertTrue(
             any(issubclass(w.category, InputDataWarning) for w in ws))
     # check that errors are raised when requested
     with settings.debug(True):
         with self.assertRaises(InputDataError):
             validate_input_scaling(train_X=train_X,
                                    train_Y=train_Y,
                                    raise_on_fail=True)
     # check that no errors are being raised if everything is standardized
     train_X_min = train_X.min(dim=-1, keepdim=True)[0]
     train_X_max = train_X.max(dim=-1, keepdim=True)[0]
     train_X_std = (train_X - train_X_min) / (train_X_max - train_X_min)
     train_Y_std = (train_Y - train_Y.mean(
         dim=-2, keepdim=True)) / train_Y.std(dim=-2, keepdim=True)
     with settings.debug(True), warnings.catch_warnings(record=True) as ws:
         validate_input_scaling(train_X=train_X_std, train_Y=train_Y_std)
         self.assertFalse(
             any(issubclass(w.category, InputDataWarning) for w in ws))
     # test that negative variances raise an error
     train_Yvar = torch.rand_like(train_Y_std)
     train_Yvar[0, 0, 1] = -0.5
     with settings.debug(True):
         with self.assertRaises(InputDataError):
             validate_input_scaling(train_X=train_X_std,
                                    train_Y=train_Y_std,
                                    train_Yvar=train_Yvar)
     # check that NaNs raise errors
     train_X_std[0, 0, 0] = float("nan")
     with settings.debug(True):
         with self.assertRaises(InputDataError):
             validate_input_scaling(train_X=train_X_std,
                                    train_Y=train_Y_std)
Beispiel #10
0
    def test_q_expected_improvement(self):
        for dtype in (torch.float, torch.double):
            tkwargs = {"device": self.device, "dtype": dtype}
            # the event shape is `b x q x t` = 1 x 1 x 1
            samples = torch.zeros(1, 1, 1, **tkwargs)
            mm = MockModel(MockPosterior(samples=samples))
            # X is `q x d` = 1 x 1. X is a dummy and unused b/c of mocking
            X = torch.zeros(1, 1, **tkwargs)

            # basic test
            sampler = IIDNormalSampler(num_samples=2)
            acqf = qExpectedImprovement(model=mm, best_f=0, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)

            # test shifting best_f value
            acqf = qExpectedImprovement(model=mm, best_f=-1, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 1.0)

            # TODO: Test batched best_f, batched model, batched evaluation

            # basic test, no resample
            sampler = IIDNormalSampler(num_samples=2, seed=12345)
            acqf = qExpectedImprovement(model=mm, best_f=0, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            res = acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, no resample
            sampler = SobolQMCNormalSampler(num_samples=2)
            acqf = qExpectedImprovement(model=mm, best_f=0, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, resample
            sampler = SobolQMCNormalSampler(num_samples=2, resample=True)
            acqf = qExpectedImprovement(model=mm, best_f=0, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertFalse(torch.equal(acqf.sampler.base_samples, bs))

            # basic test for X_pending and warning
            acqf.set_X_pending()
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(None)
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(X)
            self.assertEqual(acqf.X_pending, X)
            mm._posterior._samples = torch.zeros(1, 2, 1, **tkwargs)
            res = acqf(X)
            X2 = torch.zeros(1, 1, 1, **tkwargs, requires_grad=True)
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                acqf.set_X_pending(X2)
                self.assertEqual(acqf.X_pending, X2)
                self.assertEqual(len(ws), 1)
                self.assertTrue(issubclass(ws[-1].category, BotorchWarning))
Beispiel #11
0
    def test_sample_points_around_best(self):
        tkwargs = {"device": self.device}
        _bounds = torch.ones(2, 2)
        _bounds[1] = 2
        for dtype in (torch.float, torch.double):
            tkwargs["dtype"] = dtype
            bounds = _bounds.to(**tkwargs)
            X_train = 1 + torch.rand(20, 2, **tkwargs)
            model = MockModel(
                MockPosterior(mean=(2 * X_train + 1).sum(dim=-1, keepdim=True))
            )
            # test NEI with X_baseline
            acqf = qNoisyExpectedImprovement(model, X_baseline=X_train)
            with mock.patch(
                "botorch.optim.initializers.sample_perturbed_subset_dims"
            ) as mock_subset_dims:
                X_rnd = sample_points_around_best(
                    acq_function=acqf,
                    n_discrete_points=4,
                    sigma=1e-3,
                    bounds=bounds,
                )
                mock_subset_dims.assert_not_called()
            self.assertTrue(X_rnd.shape, torch.Size([4, 2]))
            self.assertTrue((X_rnd >= 1).all())
            self.assertTrue((X_rnd <= 2).all())
            # test model that returns a batched mean
            model = MockModel(
                MockPosterior(
                    mean=(2 * X_train + 1).sum(dim=-1, keepdim=True).unsqueeze(0)
                )
            )
            acqf = qNoisyExpectedImprovement(model, X_baseline=X_train)
            X_rnd = sample_points_around_best(
                acq_function=acqf,
                n_discrete_points=4,
                sigma=1e-3,
                bounds=bounds,
            )
            self.assertTrue(X_rnd.shape, torch.Size([4, 2]))
            self.assertTrue((X_rnd >= 1).all())
            self.assertTrue((X_rnd <= 2).all())

            # test EI without X_baseline
            acqf = qExpectedImprovement(model, best_f=0.0)

            with warnings.catch_warnings(record=True) as w, settings.debug(True):

                X_rnd = sample_points_around_best(
                    acq_function=acqf,
                    n_discrete_points=4,
                    sigma=1e-3,
                    bounds=bounds,
                )
                self.assertEqual(len(w), 1)
                self.assertTrue(issubclass(w[-1].category, BotorchWarning))
                self.assertIsNone(X_rnd)

            # set train inputs
            model.train_inputs = (X_train,)
            X_rnd = sample_points_around_best(
                acq_function=acqf,
                n_discrete_points=4,
                sigma=1e-3,
                bounds=bounds,
            )
            self.assertTrue(X_rnd.shape, torch.Size([4, 2]))
            self.assertTrue((X_rnd >= 1).all())
            self.assertTrue((X_rnd <= 2).all())

            # test an acquisition function that has objective=None
            # and maximize=False
            pm = PosteriorMean(model, maximize=False)
            self.assertIsNone(pm.objective)
            self.assertFalse(pm.maximize)
            X_rnd = sample_points_around_best(
                acq_function=pm,
                n_discrete_points=4,
                sigma=0,
                bounds=bounds,
                best_pct=1e-8,  # ensures that we only use best value
            )
            idx = (-model.posterior(X_train).mean).argmax()
            self.assertTrue((X_rnd == X_train[idx : idx + 1]).all(dim=-1).all())

            # test acquisition function that has no model
            ff = FixedFeatureAcquisitionFunction(pm, d=2, columns=[0], values=[0])
            # set X_baseline for testing purposes
            ff.X_baseline = X_train
            with warnings.catch_warnings(record=True) as w, settings.debug(True):
                X_rnd = sample_points_around_best(
                    acq_function=ff,
                    n_discrete_points=4,
                    sigma=1e-3,
                    bounds=bounds,
                )
                self.assertEqual(len(w), 1)
                self.assertTrue(issubclass(w[-1].category, BotorchWarning))
                self.assertIsNone(X_rnd)

            # test constraints with NEHVI
            constraints = [lambda Y: Y[..., 0]]
            ref_point = torch.zeros(2, **tkwargs)
            # test cases when there are and are not any feasible points
            for any_feas in (True, False):
                Y_train = torch.stack(
                    [
                        torch.linspace(-0.5, 0.5, X_train.shape[0], **tkwargs)
                        if any_feas
                        else torch.ones(X_train.shape[0], **tkwargs),
                        X_train.sum(dim=-1),
                    ],
                    dim=-1,
                )
                moo_model = MockModel(MockPosterior(mean=Y_train, samples=Y_train))
                acqf = qNoisyExpectedHypervolumeImprovement(
                    moo_model,
                    ref_point=ref_point,
                    X_baseline=X_train,
                    constraints=constraints,
                    cache_root=False,
                )
                X_rnd = sample_points_around_best(
                    acq_function=acqf,
                    n_discrete_points=4,
                    sigma=0.0,
                    bounds=bounds,
                )
                self.assertTrue(X_rnd.shape, torch.Size([4, 2]))
                # this should be true since sigma=0
                # and we should only be returning feasible points
                violation = constraints[0](Y_train)
                neg_violation = -violation.clamp_min(0.0)
                feas = neg_violation == 0
                eq_mask = (X_train.unsqueeze(1) == X_rnd.unsqueeze(0)).all(dim=-1)
                if feas.any():
                    # determine
                    # create n_train x n_rnd tensor of booleans
                    eq_mask = (X_train.unsqueeze(1) == X_rnd.unsqueeze(0)).all(dim=-1)
                    # check that all X_rnd correspond to feasible points
                    self.assertEqual(eq_mask[feas].sum(), 4)
                else:
                    idcs = torch.topk(neg_violation, k=2).indices
                    self.assertEqual(eq_mask[idcs].sum(), 4)
                self.assertTrue((X_rnd >= 1).all())
                self.assertTrue((X_rnd <= 2).all())
            # test that subset_dims is called if d>=21
            X_train = 1 + torch.rand(20, 21, **tkwargs)
            model = MockModel(
                MockPosterior(mean=(2 * X_train + 1).sum(dim=-1, keepdim=True))
            )
            bounds = torch.ones(2, 21, **tkwargs)
            bounds[1] = 2
            # test NEI with X_baseline
            acqf = qNoisyExpectedImprovement(model, X_baseline=X_train)
            with mock.patch(
                "botorch.optim.initializers.sample_perturbed_subset_dims",
                wraps=sample_perturbed_subset_dims,
            ) as mock_subset_dims:
                X_rnd = sample_points_around_best(
                    acq_function=acqf, n_discrete_points=5, sigma=1e-3, bounds=bounds
                )
            self.assertTrue(X_rnd.shape, torch.Size([5, 2]))
            self.assertTrue((X_rnd >= 1).all())
            self.assertTrue((X_rnd <= 2).all())
            mock_subset_dims.assert_called_once()
            # test tiny prob_perturb to make sure we perturb at least one dimension
            X_rnd = sample_points_around_best(
                acq_function=acqf,
                n_discrete_points=5,
                sigma=1e-3,
                bounds=bounds,
                prob_perturb=1e-8,
            )
            self.assertTrue(
                ((X_rnd.unsqueeze(0) == X_train.unsqueeze(1)).all(dim=-1)).sum() == 0
            )
Beispiel #12
0
    def test_get_X_baseline(self):
        tkwargs = {"device": self.device}
        for dtype in (torch.float, torch.double):
            tkwargs["dtype"] = dtype
            X_train = torch.rand(20, 2, **tkwargs)
            model = MockModel(
                MockPosterior(mean=(2 * X_train +
                                    1).sum(dim=-1, keepdim=True)))
            # test NEI with X_baseline
            acqf = qNoisyExpectedImprovement(model, X_baseline=X_train[:2])
            X = get_X_baseline(acq_function=acqf)
            self.assertTrue(torch.equal(X, acqf.X_baseline))
            # test EI without X_baseline
            acqf = qExpectedImprovement(model, best_f=0.0)

            with warnings.catch_warnings(
                    record=True) as w, settings.debug(True):

                X_rnd = get_X_baseline(acq_function=acqf, )
                self.assertEqual(len(w), 1)
                self.assertTrue(issubclass(w[-1].category, BotorchWarning))
                self.assertIsNone(X_rnd)

            # set train inputs
            model.train_inputs = (X_train, )
            X = get_X_baseline(acq_function=acqf, )
            self.assertTrue(torch.equal(X, X_train))
            # test that we fail back to train_inputs if X_baseline is an empty tensor
            acqf.register_buffer("X_baseline", X_train[:0])
            X = get_X_baseline(acq_function=acqf, )
            self.assertTrue(torch.equal(X, X_train))

            # test acquisitipon function without X_baseline or model
            acqf = FixedFeatureAcquisitionFunction(acqf,
                                                   d=2,
                                                   columns=[0],
                                                   values=[0])
            with warnings.catch_warnings(
                    record=True) as w, settings.debug(True):
                X_rnd = get_X_baseline(acq_function=acqf, )
                self.assertEqual(len(w), 1)
                self.assertTrue(issubclass(w[-1].category, BotorchWarning))
                self.assertIsNone(X_rnd)

            Y_train = 2 * X_train[:2] + 1
            moo_model = MockModel(MockPosterior(mean=Y_train, samples=Y_train))
            ref_point = torch.zeros(2, **tkwargs)
            # test NEHVI with X_baseline
            acqf = qNoisyExpectedHypervolumeImprovement(
                moo_model,
                ref_point=ref_point,
                X_baseline=X_train[:2],
                cache_root=False,
            )
            X = get_X_baseline(acq_function=acqf, )
            self.assertTrue(torch.equal(X, acqf.X_baseline))
            # test qEHVI without train_inputs
            acqf = qExpectedHypervolumeImprovement(
                moo_model,
                ref_point=ref_point,
                partitioning=FastNondominatedPartitioning(
                    ref_point=ref_point,
                    Y=Y_train,
                ),
            )
            # test extracting train_inputs from model list GP
            model_list = ModelListGP(
                SingleTaskGP(X_train, Y_train[:, :1]),
                SingleTaskGP(X_train, Y_train[:, 1:]),
            )
            acqf = qExpectedHypervolumeImprovement(
                model_list,
                ref_point=ref_point,
                partitioning=FastNondominatedPartitioning(
                    ref_point=ref_point,
                    Y=Y_train,
                ),
            )
            X = get_X_baseline(acq_function=acqf, )
            self.assertTrue(torch.equal(X, X_train))

            # test MESMO for which we need to use
            # `acqf.mo_model`
            batched_mo_model = SingleTaskGP(X_train, Y_train)
            acqf = qMultiObjectiveMaxValueEntropy(
                batched_mo_model,
                sample_pareto_frontiers=lambda model: torch.rand(
                    10, 2, **tkwargs),
            )
            X = get_X_baseline(acq_function=acqf, )
            self.assertTrue(torch.equal(X, X_train))
            # test that if there is an input transform that is applied
            # to the train_inputs when the model is in eval mode, we
            # extract the untransformed train_inputs
            model = SingleTaskGP(X_train,
                                 Y_train[:, :1],
                                 input_transform=Warp(indices=[0, 1]))
            model.eval()
            self.assertFalse(torch.equal(model.train_inputs[0], X_train))
            acqf = qExpectedImprovement(model, best_f=0.0)
            X = get_X_baseline(acq_function=acqf, )
            self.assertTrue(torch.equal(X, X_train))
Beispiel #13
0
    def test_fit_gpytorch_model(self,
                                cuda=False,
                                optimizer=fit_gpytorch_scipy):
        options = {"disp": False, "maxiter": 5}
        for double in (False, True):
            mll = self._getModel(double=double, cuda=cuda)
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(mll,
                                         optimizer=optimizer,
                                         options=options,
                                         max_retries=1)
                if optimizer == fit_gpytorch_scipy:
                    self.assertEqual(len(ws), 1)
                    self.assertTrue(MAX_RETRY_MSG in str(ws[0].message))
            model = mll.model
            # Make sure all of the parameters changed
            self.assertGreater(model.likelihood.raw_noise.abs().item(), 1e-3)
            self.assertLess(model.mean_module.constant.abs().item(), 0.1)
            self.assertGreater(
                model.covar_module.base_kernel.raw_lengthscale.abs().item(),
                0.1)
            self.assertGreater(model.covar_module.raw_outputscale.abs().item(),
                               1e-3)

            # test overriding the default bounds with user supplied bounds
            mll = self._getModel(double=double, cuda=cuda)
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(
                    mll,
                    optimizer=optimizer,
                    options=options,
                    max_retries=1,
                    bounds={"likelihood.noise_covar.raw_noise": (1e-1, None)},
                )
                if optimizer == fit_gpytorch_scipy:
                    self.assertEqual(len(ws), 1)
                    self.assertTrue(MAX_RETRY_MSG in str(ws[0].message))

            model = mll.model
            self.assertGreaterEqual(model.likelihood.raw_noise.abs().item(),
                                    1e-1)
            self.assertLess(model.mean_module.constant.abs().item(), 0.1)
            self.assertGreater(
                model.covar_module.base_kernel.raw_lengthscale.abs().item(),
                0.1)
            self.assertGreater(model.covar_module.raw_outputscale.abs().item(),
                               1e-3)

            # test tracking iterations
            mll = self._getModel(double=double, cuda=cuda)
            if optimizer is fit_gpytorch_torch:
                options["disp"] = True
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                mll, iterations = optimizer(mll,
                                            options=options,
                                            track_iterations=True)
                if optimizer == fit_gpytorch_scipy:
                    self.assertEqual(len(ws), 1)
                    self.assertTrue(MAX_ITER_MSG in str(ws[0].message))
            self.assertEqual(len(iterations), options["maxiter"])
            self.assertIsInstance(iterations[0], OptimizationIteration)

            # test extra param that does not affect loss
            options["disp"] = False
            mll = self._getModel(double=double, cuda=cuda)
            mll.register_parameter(
                "dummy_param",
                torch.nn.Parameter(
                    torch.tensor(
                        [5.0],
                        dtype=torch.double if double else torch.float,
                        device=torch.device("cuda" if cuda else "cpu"),
                    )),
            )
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(mll,
                                         optimizer=optimizer,
                                         options=options,
                                         max_retries=1)
                if optimizer == fit_gpytorch_scipy:
                    self.assertEqual(len(ws), 1)
                    self.assertTrue(MAX_RETRY_MSG in str(ws[0].message))
            self.assertTrue(mll.dummy_param.grad is None)

            # test excluding a parameter
            mll = self._getModel(double=double, cuda=cuda)
            original_raw_noise = mll.model.likelihood.noise_covar.raw_noise.item(
            )
            original_mean_module_constant = mll.model.mean_module.constant.item(
            )
            options["exclude"] = [
                "model.mean_module.constant",
                "likelihood.noise_covar.raw_noise",
            ]
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(mll,
                                         optimizer=optimizer,
                                         options=options,
                                         max_retries=1)
                if optimizer == fit_gpytorch_scipy:
                    self.assertEqual(len(ws), 1)
                    self.assertTrue(MAX_RETRY_MSG in str(ws[0].message))
            model = mll.model
            # Make excluded params did not change
            self.assertEqual(model.likelihood.noise_covar.raw_noise.item(),
                             original_raw_noise)
            self.assertEqual(model.mean_module.constant.item(),
                             original_mean_module_constant)
            # Make sure other params did change
            self.assertGreater(
                model.covar_module.base_kernel.raw_lengthscale.abs().item(),
                0.1)
            self.assertGreater(model.covar_module.raw_outputscale.abs().item(),
                               1e-3)
Beispiel #14
0
 def test_prune_inferior_points(self):
     for dtype in (torch.float, torch.double):
         X = torch.rand(3, 2, device=self.device, dtype=dtype)
         # the event shape is `q x t` = 3 x 1
         samples = torch.tensor([[-1.0], [0.0], [1.0]],
                                device=self.device,
                                dtype=dtype)
         mm = MockModel(MockPosterior(samples=samples))
         # test that a batched X raises errors
         with self.assertRaises(UnsupportedError):
             prune_inferior_points(model=mm, X=X.expand(2, 3, 2))
         # test that a batched model raises errors (event shape is `q x t` = 3 x 1)
         mm2 = MockModel(MockPosterior(samples=samples.expand(2, 3, 1)))
         with self.assertRaises(UnsupportedError):
             prune_inferior_points(model=mm2, X=X)
         # test that invalid max_frac is checked properly
         with self.assertRaises(ValueError):
             prune_inferior_points(model=mm, X=X, max_frac=1.1)
         # test basic behaviour
         X_pruned = prune_inferior_points(model=mm, X=X)
         self.assertTrue(torch.equal(X_pruned, X[[-1]]))
         # test custom objective
         neg_id_obj = GenericMCObjective(lambda Y, X: -(Y.squeeze(-1)))
         X_pruned = prune_inferior_points(model=mm,
                                          X=X,
                                          objective=neg_id_obj)
         self.assertTrue(torch.equal(X_pruned, X[[0]]))
         # test non-repeated samples (requires mocking out MockPosterior's rsample)
         samples = torch.tensor(
             [[[3.0], [0.0], [0.0]], [[0.0], [2.0], [0.0]],
              [[0.0], [0.0], [1.0]]],
             device=self.device,
             dtype=dtype,
         )
         with mock.patch.object(MockPosterior,
                                "rsample",
                                return_value=samples):
             mm = MockModel(MockPosterior(samples=samples))
             X_pruned = prune_inferior_points(model=mm, X=X)
         self.assertTrue(torch.equal(X_pruned, X))
         # test max_frac limiting
         with mock.patch.object(MockPosterior,
                                "rsample",
                                return_value=samples):
             mm = MockModel(MockPosterior(samples=samples))
             X_pruned = prune_inferior_points(model=mm, X=X, max_frac=2 / 3)
         if self.device == torch.device("cuda"):
             # sorting has different order on cuda
             self.assertTrue(
                 torch.equal(X_pruned, torch.stack([X[2], X[1]], dim=0)))
         else:
             self.assertTrue(torch.equal(X_pruned, X[:2]))
         # test that zero-probability is in fact pruned
         samples[2, 0, 0] = 10
         with mock.patch.object(MockPosterior,
                                "rsample",
                                return_value=samples):
             mm = MockModel(MockPosterior(samples=samples))
             X_pruned = prune_inferior_points(model=mm, X=X)
         self.assertTrue(torch.equal(X_pruned, X[:2]))
         # test high-dim sampling
         with ExitStack() as es:
             mock_event_shape = es.enter_context(
                 mock.patch(
                     "botorch.utils.testing.MockPosterior.base_sample_shape",
                     new_callable=mock.PropertyMock,
                 ))
             mock_event_shape.return_value = torch.Size(
                 [1, 1, torch.quasirandom.SobolEngine.MAXDIM + 1])
             es.enter_context(
                 mock.patch.object(MockPosterior,
                                   "rsample",
                                   return_value=samples))
             mm = MockModel(MockPosterior(samples=samples))
             with warnings.catch_warnings(
                     record=True) as ws, settings.debug(True):
                 prune_inferior_points(model=mm, X=X)
                 self.assertTrue(
                     issubclass(ws[-1].category, SamplingWarning))
Beispiel #15
0
    def test_q_upper_confidence_bound_batch(self):
        # TODO: T41739913 Implement tests for all MCAcquisitionFunctions
        for dtype in (torch.float, torch.double):
            samples = torch.zeros(2, 2, 1, device=self.device, dtype=dtype)
            samples[0, 0, 0] = 1.0
            mm = MockModel(MockPosterior(samples=samples))
            # X is a dummy and unused b/c of mocking
            X = torch.zeros(1, 1, 1, device=self.device, dtype=dtype)

            # test batch mode
            sampler = IIDNormalSampler(num_samples=2)
            acqf = qUpperConfidenceBound(model=mm, beta=0.5, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res[0].item(), 1.0)
            self.assertEqual(res[1].item(), 0.0)

            # test batch mode, no resample
            sampler = IIDNormalSampler(num_samples=2, seed=12345)
            acqf = qUpperConfidenceBound(model=mm, beta=0.5, sampler=sampler)
            res = acqf(X)  # 1-dim batch
            self.assertEqual(res[0].item(), 1.0)
            self.assertEqual(res[1].item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 2, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))
            res = acqf(X.expand(2, 1, 1))  # 2-dim batch
            self.assertEqual(res[0].item(), 1.0)
            self.assertEqual(res[1].item(), 0.0)
            # the base samples should have the batch dim collapsed
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 2, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X.expand(2, 1, 1))
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # test batch mode, qmc, no resample
            sampler = SobolQMCNormalSampler(num_samples=2)
            acqf = qUpperConfidenceBound(model=mm, beta=0.5, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res[0].item(), 1.0)
            self.assertEqual(res[1].item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 2, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # test batch mode, qmc, resample
            sampler = SobolQMCNormalSampler(num_samples=2, resample=True)
            acqf = qUpperConfidenceBound(model=mm, beta=0.5, sampler=sampler)
            res = acqf(X)  # 1-dim batch
            self.assertEqual(res[0].item(), 1.0)
            self.assertEqual(res[1].item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 2, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertFalse(torch.equal(acqf.sampler.base_samples, bs))
            res = acqf(X.expand(2, 1, 1))  # 2-dim batch
            self.assertEqual(res[0].item(), 1.0)
            self.assertEqual(res[1].item(), 0.0)
            # the base samples should have the batch dim collapsed
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 2, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X.expand(2, 1, 1))
            self.assertFalse(torch.equal(acqf.sampler.base_samples, bs))

            # basic test for X_pending and warning
            acqf.set_X_pending()
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(None)
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(X)
            self.assertEqual(acqf.X_pending, X)
            res = acqf(X)
            X2 = torch.zeros(
                1, 1, 1, device=self.device, dtype=dtype, requires_grad=True
            )
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                acqf.set_X_pending(X2)
                self.assertEqual(acqf.X_pending, X2)
                self.assertEqual(len(ws), 1)
                self.assertTrue(issubclass(ws[-1].category, BotorchWarning))
Beispiel #16
0
    def test_q_expected_improvement(self):
        for dtype in (torch.float, torch.double):
            # the event shape is `b x q x t` = 1 x 1 x 1
            samples = torch.zeros(1, 1, 1, device=self.device, dtype=dtype)
            mm = MockModel(MockPosterior(samples=samples))
            # X is `q x d` = 1 x 1. X is a dummy and unused b/c of mocking
            X = torch.zeros(1, 1, device=self.device, dtype=dtype)

            # basic test
            sampler = IIDNormalSampler(num_samples=2)
            acqf = qExpectedImprovement(model=mm, best_f=0, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)

            # test shifting best_f value
            acqf = qExpectedImprovement(model=mm, best_f=-1, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 1.0)

            # test size verification of best_f
            with self.assertRaises(ValueError):
                qExpectedImprovement(
                    model=mm, best_f=torch.zeros(2, device=self.device, dtype=dtype)
                )

            # basic test, no resample
            sampler = IIDNormalSampler(num_samples=2, seed=12345)
            acqf = qExpectedImprovement(model=mm, best_f=0, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            res = acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, no resample
            sampler = SobolQMCNormalSampler(num_samples=2)
            acqf = qExpectedImprovement(model=mm, best_f=0, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, resample
            sampler = SobolQMCNormalSampler(num_samples=2, resample=True)
            acqf = qExpectedImprovement(model=mm, best_f=0, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertFalse(torch.equal(acqf.sampler.base_samples, bs))

            # basic test for X_pending and warning
            acqf.set_X_pending()
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(None)
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(X)
            self.assertEqual(acqf.X_pending, X)
            res = acqf(X)
            X2 = torch.zeros(
                1, 1, 1, device=self.device, dtype=dtype, requires_grad=True
            )
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                acqf.set_X_pending(X2)
                self.assertEqual(acqf.X_pending, X2)
                self.assertEqual(len(ws), 1)
                self.assertTrue(issubclass(ws[-1].category, BotorchWarning))

        # test bad objective type
        obj = ScalarizedObjective(
            weights=torch.rand(2, device=self.device, dtype=dtype)
        )
        with self.assertRaises(UnsupportedError):
            qExpectedImprovement(model=mm, best_f=0, sampler=sampler, objective=obj)
    def test_get_f_X_samples(self):
        tkwargs = {"device": self.device}
        for dtype in (torch.float, torch.double):
            tkwargs["dtype"] = dtype
            mean = torch.zeros(5, 1, **tkwargs)
            variance = torch.ones(5, 1, **tkwargs)
            mm = MockModel(
                MockPosterior(mean=mean,
                              variance=variance,
                              samples=torch.rand(5, 1, **tkwargs)))
            # basic test
            sampler = IIDNormalSampler(1)
            acqf = DummyCachedCholeskyAcqf(model=mm, sampler=sampler)
            acqf._setup(model=mm, sampler=sampler, cache_root=True)
            q = 3
            baseline_L = torch.eye(5 - q, **tkwargs)
            acqf._baseline_L = baseline_L
            posterior = mm.posterior(torch.rand(5, 1, **tkwargs))
            # basic test
            rv = torch.rand(1, 5, 1, **tkwargs)
            with mock.patch(
                    "botorch.acquisition.cached_cholesky.sample_cached_cholesky",
                    return_value=rv,
            ) as mock_sample_cached_cholesky:
                samples = acqf._get_f_X_samples(posterior=posterior, q_in=q)
                mock_sample_cached_cholesky.assert_called_once_with(
                    posterior=posterior,
                    baseline_L=acqf._baseline_L,
                    q=q,
                    base_samples=acqf.sampler.base_samples,
                    sample_shape=acqf.sampler.sample_shape,
                )
            self.assertTrue(torch.equal(rv, samples))

            # test fall back when sampling from cached cholesky fails
            for error_cls in (NanError, NotPSDError):
                base_samples = torch.rand(1, 5, 1, **tkwargs)
                acqf.sampler.base_samples = base_samples
                acqf._baseline_L = baseline_L
                with mock.patch(
                        "botorch.acquisition.cached_cholesky.sample_cached_cholesky",
                        side_effect=error_cls,
                ) as mock_sample_cached_cholesky:
                    with warnings.catch_warnings(
                            record=True) as ws, settings.debug(True):
                        samples = acqf._get_f_X_samples(posterior=posterior,
                                                        q_in=q)
                        mock_sample_cached_cholesky.assert_called_once_with(
                            posterior=posterior,
                            baseline_L=acqf._baseline_L,
                            q=q,
                            base_samples=base_samples,
                            sample_shape=acqf.sampler.sample_shape,
                        )
                        self.assertTrue(
                            issubclass(ws[0].category, BotorchWarning))
                        self.assertTrue(samples.shape, torch.Size([1, q, 1]))
            # test HOGP
            hogp = HigherOrderGP(torch.zeros(2, 1), torch.zeros(2, 1,
                                                                1)).eval()
            acqf = DummyCachedCholeskyAcqf(model=hogp, sampler=sampler)
            acqf._setup(model=hogp, sampler=sampler, cache_root=True)
            mock_samples = torch.rand(5, 1, 1, **tkwargs)
            posterior = MockPosterior(mean=mean,
                                      variance=variance,
                                      samples=mock_samples)
            samples = acqf._get_f_X_samples(posterior=posterior, q_in=q)
            self.assertTrue(torch.equal(samples,
                                        mock_samples[2:].unsqueeze(0)))
Beispiel #18
0
    def test_cache_root(self):
        sample_cached_path = (
            "botorch.acquisition.cached_cholesky.sample_cached_cholesky")
        raw_state_dict = {
            "likelihood.noise_covar.raw_noise":
            torch.tensor([[0.0895], [0.2594]], dtype=torch.float64),
            "mean_module.constant":
            torch.tensor([[-0.4545], [-0.1285]], dtype=torch.float64),
            "covar_module.raw_outputscale":
            torch.tensor([1.4876, 1.4897], dtype=torch.float64),
            "covar_module.base_kernel.raw_lengthscale":
            torch.tensor([[[-0.7202, -0.2868]], [[-0.8794, -1.2877]]],
                         dtype=torch.float64),
        }
        # test batched models (e.g. for MCMC)
        for train_batch_shape, m, dtype in product(
            (torch.Size([]), torch.Size([3])), (1, 2),
            (torch.float, torch.double)):
            state_dict = deepcopy(raw_state_dict)
            for k, v in state_dict.items():
                if m == 1:
                    v = v[0]
                if len(train_batch_shape) > 0:
                    v = v.unsqueeze(0).expand(*train_batch_shape, *v.shape)
                state_dict[k] = v
            tkwargs = {"device": self.device, "dtype": dtype}
            if m == 2:
                objective = GenericMCObjective(lambda Y, X: Y.sum(dim=-1))
            else:
                objective = None
            for k, v in state_dict.items():
                state_dict[k] = v.to(**tkwargs)
            all_close_kwargs = ({
                "atol": 1e-1,
                "rtol": 0.0,
            } if dtype == torch.float else {
                "atol": 1e-4,
                "rtol": 0.0
            })
            torch.manual_seed(1234)
            train_X = torch.rand(*train_batch_shape, 3, 2, **tkwargs)
            train_Y = (
                torch.sin(train_X * 2 * pi) +
                torch.randn(*train_batch_shape, 3, 2, **tkwargs))[..., :m]
            train_Y = standardize(train_Y)
            model = SingleTaskGP(
                train_X,
                train_Y,
            )
            if len(train_batch_shape) > 0:
                X_baseline = train_X[0]
            else:
                X_baseline = train_X
            model.load_state_dict(state_dict, strict=False)
            # test sampler with collapse_batch_dims=False
            sampler = IIDNormalSampler(5, seed=0, collapse_batch_dims=False)
            with self.assertRaises(UnsupportedError):
                qNoisyExpectedImprovement(
                    model=model,
                    X_baseline=X_baseline,
                    sampler=sampler,
                    objective=objective,
                    prune_baseline=False,
                    cache_root=True,
                )
            sampler = IIDNormalSampler(5, seed=0)
            torch.manual_seed(0)
            acqf = qNoisyExpectedImprovement(
                model=model,
                X_baseline=X_baseline,
                sampler=sampler,
                objective=objective,
                prune_baseline=False,
                cache_root=True,
            )

            orig_base_samples = acqf.base_sampler.base_samples.detach().clone()
            sampler2 = IIDNormalSampler(5, seed=0)
            sampler2.base_samples = orig_base_samples
            torch.manual_seed(0)
            acqf_no_cache = qNoisyExpectedImprovement(
                model=model,
                X_baseline=X_baseline,
                sampler=sampler2,
                objective=objective,
                prune_baseline=False,
                cache_root=False,
            )
            for q, batch_shape in product(
                (1, 3), (torch.Size([]), torch.Size([3]), torch.Size([4, 3]))):
                test_X = (0.3 +
                          0.05 * torch.randn(*batch_shape, q, 2, **tkwargs)
                          ).requires_grad_(True)
                with mock.patch(
                        sample_cached_path,
                        wraps=sample_cached_cholesky) as mock_sample_cached:
                    torch.manual_seed(0)
                    val = acqf(test_X)
                    mock_sample_cached.assert_called_once()
                val.sum().backward()
                base_samples = acqf.sampler.base_samples.detach().clone()
                X_grad = test_X.grad.clone()
                test_X2 = test_X.detach().clone().requires_grad_(True)
                acqf_no_cache.sampler.base_samples = base_samples
                with mock.patch(
                        sample_cached_path,
                        wraps=sample_cached_cholesky) as mock_sample_cached:
                    torch.manual_seed(0)
                    val2 = acqf_no_cache(test_X2)
                mock_sample_cached.assert_not_called()
                self.assertTrue(torch.allclose(val, val2, **all_close_kwargs))
                val2.sum().backward()
                self.assertTrue(
                    torch.allclose(X_grad, test_X2.grad, **all_close_kwargs))
            # test we fall back to standard sampling for
            # ill-conditioned covariances
            acqf._baseline_L = torch.zeros_like(acqf._baseline_L)
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                with torch.no_grad():
                    acqf(test_X)
            self.assertEqual(len(ws), 1)
            self.assertTrue(issubclass(ws[-1].category, BotorchWarning))
    def test_q_expected_hypervolume_improvement(self):
        tkwargs = {"device": self.device}
        for dtype in (torch.float, torch.double):
            ref_point = [0.0, 0.0]
            tkwargs["dtype"] = dtype
            pareto_Y = torch.tensor(
                [[4.0, 5.0], [5.0, 5.0], [8.5, 3.5], [8.5, 3.0], [9.0, 1.0]],
                **tkwargs)
            partitioning = NondominatedPartitioning(num_outcomes=2)
            # the event shape is `b x q x m` = 1 x 1 x 2
            samples = torch.zeros(1, 1, 2, **tkwargs)
            mm = MockModel(MockPosterior(samples=samples))
            # test error if there is not pareto_Y initialized in partitioning
            with self.assertRaises(BotorchError):
                qExpectedHypervolumeImprovement(model=mm,
                                                ref_point=ref_point,
                                                partitioning=partitioning)
            partitioning.update(Y=pareto_Y)
            # test error if ref point has wrong shape
            with self.assertRaises(ValueError):
                qExpectedHypervolumeImprovement(model=mm,
                                                ref_point=ref_point[:1],
                                                partitioning=partitioning)

            X = torch.zeros(1, 1, **tkwargs)
            # basic test
            sampler = IIDNormalSampler(num_samples=1)
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            # check ref point
            self.assertTrue(
                torch.equal(acqf.ref_point, torch.tensor(ref_point,
                                                         **tkwargs)))
            # check cached indices
            self.assertTrue(hasattr(acqf, "q_subset_indices"))
            self.assertIn("q_choose_1", acqf.q_subset_indices)
            self.assertTrue(
                torch.equal(
                    acqf.q_subset_indices["q_choose_1"],
                    torch.tensor([[0]], device=self.device),
                ))

            # test q=2
            X2 = torch.zeros(2, 1, **tkwargs)
            samples2 = torch.zeros(1, 2, 2, **tkwargs)
            mm2 = MockModel(MockPosterior(samples=samples2))
            acqf.model = mm2
            res = acqf(X2)
            self.assertEqual(res.item(), 0.0)
            # check cached indices
            self.assertTrue(hasattr(acqf, "q_subset_indices"))
            self.assertIn("q_choose_1", acqf.q_subset_indices)
            self.assertTrue(
                torch.equal(
                    acqf.q_subset_indices["q_choose_1"],
                    torch.tensor([[0], [1]], device=self.device),
                ))
            self.assertIn("q_choose_2", acqf.q_subset_indices)
            self.assertTrue(
                torch.equal(
                    acqf.q_subset_indices["q_choose_2"],
                    torch.tensor([[0, 1]], device=self.device),
                ))
            self.assertNotIn("q_choose_3", acqf.q_subset_indices)
            # now back to 1 and sure all caches were cleared
            acqf.model = mm
            res = acqf(X)
            self.assertNotIn("q_choose_2", acqf.q_subset_indices)
            self.assertIn("q_choose_1", acqf.q_subset_indices)
            self.assertTrue(
                torch.equal(
                    acqf.q_subset_indices["q_choose_1"],
                    torch.tensor([[0]], device=self.device),
                ))

            X = torch.zeros(1, 1, **tkwargs)
            samples = torch.zeros(1, 1, 2, **tkwargs)
            mm = MockModel(MockPosterior(samples=samples))
            # basic test, no resample
            sampler = IIDNormalSampler(num_samples=2, seed=12345)
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 2]))
            bs = acqf.sampler.base_samples.clone()
            res = acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, no resample
            sampler = SobolQMCNormalSampler(num_samples=2)
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 2]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, resample
            sampler = SobolQMCNormalSampler(num_samples=2, resample=True)
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 2]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertFalse(torch.equal(acqf.sampler.base_samples, bs))

            # basic test for X_pending and warning
            acqf.set_X_pending()
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(None)
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(X)
            self.assertEqual(acqf.X_pending, X)
            res = acqf(X)
            X2 = torch.zeros(1, 1, 1, requires_grad=True, **tkwargs)
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                acqf.set_X_pending(X2)
                self.assertEqual(acqf.X_pending, X2)
                self.assertEqual(len(ws), 1)
                self.assertTrue(issubclass(ws[-1].category, BotorchWarning))

            # test objective
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
                objective=IdentityMCMultiOutputObjective(),
            )
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)

            # Test that the hypervolume improvement is correct for given sample
            # test q = 1
            X = torch.zeros(1, 1, **tkwargs)
            # basic test
            samples = torch.tensor([[[6.5, 4.5]]], **tkwargs)
            mm = MockModel(MockPosterior(samples=samples))
            sampler = IIDNormalSampler(num_samples=1)
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 1.5)
            # test q = 1, does not contribute
            samples = torch.tensor([0.0, 1.0], **tkwargs).view(1, 1, 2)
            sampler = IIDNormalSampler(1)
            mm = MockModel(MockPosterior(samples=samples))
            acqf.model = mm
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)

            # test q = 2, both points contribute
            X = torch.zeros(2, 1, **tkwargs)
            samples = torch.tensor([[6.5, 4.5], [7.0, 4.0]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            acqf.model = mm
            res = acqf(X)
            self.assertEqual(res.item(), 1.75)

            # test q = 2, only 1 point contributes
            samples = torch.tensor([[6.5, 4.5], [6.0, 4.0]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            acqf.model = mm
            res = acqf(X)
            self.assertEqual(res.item(), 1.5)

            # test q = 2, neither contributes
            samples = torch.tensor([[2.0, 2.0], [0.0, 0.1]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            acqf.model = mm
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)

            # test q = 2, test point better than current best second objective
            samples = torch.tensor([[6.5, 4.5], [6.0, 6.0]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            acqf.model = mm
            res = acqf(X)
            self.assertEqual(res.item(), 8.0)

            # test q = 2, test point better than current-best first objective
            samples = torch.tensor([[6.5, 4.5], [9.0, 2.0]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 2.0)
            # test q = 3, all contribute
            X = torch.zeros(3, 1, **tkwargs)
            samples = torch.tensor([[6.5, 4.5], [9.0, 2.0], [7.0, 4.0]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 2.25)
            # test q = 3, not all contribute
            samples = torch.tensor([[6.5, 4.5], [9.0, 2.0], [7.0, 5.0]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 3.5)
            # test q = 3, none contribute
            samples = torch.tensor([[0.0, 4.5], [1.0, 2.0], [3.0, 0.0]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)

            # test m = 3, q=1
            pareto_Y = torch.tensor(
                [[4.0, 2.0, 3.0], [3.0, 5.0, 1.0], [2.0, 4.0, 2.0],
                 [1.0, 3.0, 4.0]],
                **tkwargs,
            )
            partitioning = NondominatedPartitioning(num_outcomes=3, Y=pareto_Y)
            samples = torch.tensor([[1.0, 2.0, 6.0]], **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            ref_point = [-1.0] * 3
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            X = torch.zeros(1, 2, **tkwargs)
            res = acqf(X)
            self.assertEqual(res.item(), 12.0)

            # change reference point
            ref_point = [0.0] * 3
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 4.0)

            # test m = 3, no contribution
            ref_point = [1.0] * 3
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)

            # test m = 3, q = 2
            pareto_Y = torch.tensor(
                [[4.0, 2.0, 3.0], [3.0, 5.0, 1.0], [2.0, 4.0, 2.0]], **tkwargs)
            samples = torch.tensor([[1.0, 2.0, 6.0], [1.0, 3.0, 4.0]],
                                   **tkwargs).unsqueeze(0)
            mm = MockModel(MockPosterior(samples=samples))
            ref_point = [-1.0] * 3
            partitioning = NondominatedPartitioning(num_outcomes=3, Y=pareto_Y)
            acqf = qExpectedHypervolumeImprovement(
                model=mm,
                ref_point=ref_point,
                partitioning=partitioning,
                sampler=sampler,
            )
            X = torch.zeros(2, 2, **tkwargs)
            res = acqf(X)
            self.assertEqual(res.item(), 22.0)
Beispiel #20
0
    def test_q_upper_confidence_bound(self):
        for dtype in (torch.float, torch.double):
            # the event shape is `b x q x t` = 1 x 1 x 1
            samples = torch.zeros(1, 1, 1, device=self.device, dtype=dtype)
            mm = MockModel(MockPosterior(samples=samples))
            # X is `q x d` = 1 x 1. X is a dummy and unused b/c of mocking
            X = torch.zeros(1, 1, device=self.device, dtype=dtype)

            # basic test
            sampler = IIDNormalSampler(num_samples=2)
            acqf = qUpperConfidenceBound(model=mm, beta=0.5, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)

            # basic test, no resample
            sampler = IIDNormalSampler(num_samples=2, seed=12345)
            acqf = qUpperConfidenceBound(model=mm, beta=0.5, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            res = acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, no resample
            sampler = SobolQMCNormalSampler(num_samples=2)
            acqf = qUpperConfidenceBound(model=mm, beta=0.5, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, resample
            sampler = SobolQMCNormalSampler(num_samples=2, resample=True)
            acqf = qUpperConfidenceBound(model=mm, beta=0.5, sampler=sampler)
            res = acqf(X)
            self.assertEqual(res.item(), 0.0)
            self.assertEqual(acqf.sampler.base_samples.shape,
                             torch.Size([2, 1, 1, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertFalse(torch.equal(acqf.sampler.base_samples, bs))

            # basic test for X_pending and warning
            acqf.set_X_pending()
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(None)
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(X)
            self.assertEqual(acqf.X_pending, X)
            mm._posterior._samples = mm._posterior._samples.expand(1, 2, 1)
            res = acqf(X)
            X2 = torch.zeros(1,
                             1,
                             1,
                             device=self.device,
                             dtype=dtype,
                             requires_grad=True)
            with warnings.catch_warnings(
                    record=True) as ws, settings.debug(True):
                acqf.set_X_pending(X2)
                self.assertEqual(acqf.X_pending, X2)
                self.assertEqual(len(ws), 1)
                self.assertTrue(issubclass(ws[-1].category, BotorchWarning))
Beispiel #21
0
    def test_q_noisy_expected_improvement(self):
        for dtype in (torch.float, torch.double):
            # the event shape is `b x q x t` = 1 x 2 x 1
            samples_noisy = torch.tensor([1.0, 0.0], device=self.device, dtype=dtype)
            samples_noisy = samples_noisy.view(1, 2, 1)
            # X_baseline is `q' x d` = 1 x 1
            X_baseline = torch.zeros(1, 1, device=self.device, dtype=dtype)
            mm_noisy = MockModel(MockPosterior(samples=samples_noisy))
            # X is `q x d` = 1 x 1
            X = torch.zeros(1, 1, device=self.device, dtype=dtype)

            # basic test
            sampler = IIDNormalSampler(num_samples=2)
            acqf = qNoisyExpectedImprovement(
                model=mm_noisy, X_baseline=X_baseline, sampler=sampler
            )
            res = acqf(X)
            self.assertEqual(res.item(), 1.0)

            # basic test, no resample
            sampler = IIDNormalSampler(num_samples=2, seed=12345)
            acqf = qNoisyExpectedImprovement(
                model=mm_noisy, X_baseline=X_baseline, sampler=sampler
            )
            res = acqf(X)
            self.assertEqual(res.item(), 1.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 2, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, no resample
            sampler = SobolQMCNormalSampler(num_samples=2)
            acqf = qNoisyExpectedImprovement(
                model=mm_noisy, X_baseline=X_baseline, sampler=sampler
            )
            res = acqf(X)
            self.assertEqual(res.item(), 1.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 2, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertTrue(torch.equal(acqf.sampler.base_samples, bs))

            # basic test, qmc, resample
            sampler = SobolQMCNormalSampler(num_samples=2, resample=True, seed=12345)
            acqf = qNoisyExpectedImprovement(
                model=mm_noisy, X_baseline=X_baseline, sampler=sampler
            )
            res = acqf(X)
            self.assertEqual(res.item(), 1.0)
            self.assertEqual(acqf.sampler.base_samples.shape, torch.Size([2, 1, 2, 1]))
            bs = acqf.sampler.base_samples.clone()
            acqf(X)
            self.assertFalse(torch.equal(acqf.sampler.base_samples, bs))

            # basic test for X_pending and warning
            sampler = SobolQMCNormalSampler(num_samples=2)
            samples_noisy_pending = torch.tensor(
                [1.0, 0.0, 0.0], device=self.device, dtype=dtype
            )
            samples_noisy_pending = samples_noisy_pending.view(1, 3, 1)
            mm_noisy_pending = MockModel(MockPosterior(samples=samples_noisy_pending))
            acqf = qNoisyExpectedImprovement(
                model=mm_noisy_pending, X_baseline=X_baseline, sampler=sampler
            )
            acqf.set_X_pending()
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(None)
            self.assertIsNone(acqf.X_pending)
            acqf.set_X_pending(X)
            self.assertEqual(acqf.X_pending, X)
            res = acqf(X)
            X2 = torch.zeros(
                1, 1, 1, device=self.device, dtype=dtype, requires_grad=True
            )
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                acqf.set_X_pending(X2)
                self.assertEqual(acqf.X_pending, X2)
                self.assertEqual(len(ws), 1)
                self.assertTrue(issubclass(ws[-1].category, BotorchWarning))
Beispiel #22
0
    def test_fit_gpytorch_model(self, optimizer=fit_gpytorch_scipy):
        options = {"disp": False, "maxiter": 5}
        for double in (False, True):
            mll = self._getModel(double=double)
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(
                    mll, optimizer=optimizer, options=options, max_retries=1
                )
                if optimizer == fit_gpytorch_scipy:
                    self.assertTrue(
                        any(issubclass(w.category, OptimizationWarning)) for w in ws
                    )
                    self.assertEqual(
                        sum(1 for w in ws if MAX_RETRY_MSG in str(w.message)), 1
                    )
            model = mll.model
            # Make sure all of the parameters changed
            self.assertGreater(model.likelihood.raw_noise.abs().item(), 1e-3)
            self.assertLess(model.mean_module.constant.abs().item(), 0.1)
            self.assertGreater(
                model.covar_module.base_kernel.raw_lengthscale.abs().item(), 0.1
            )
            self.assertGreater(model.covar_module.raw_outputscale.abs().item(), 1e-3)

            # test overriding the default bounds with user supplied bounds
            mll = self._getModel(double=double)
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(
                    mll,
                    optimizer=optimizer,
                    options=options,
                    max_retries=1,
                    bounds={"likelihood.noise_covar.raw_noise": (1e-1, None)},
                )
                if optimizer == fit_gpytorch_scipy:
                    self.assertTrue(
                        any(issubclass(w.category, OptimizationWarning)) for w in ws
                    )
                    self.assertEqual(
                        sum(1 for w in ws if MAX_RETRY_MSG in str(w.message)), 1
                    )

            model = mll.model
            self.assertGreaterEqual(model.likelihood.raw_noise.abs().item(), 1e-1)
            self.assertLess(model.mean_module.constant.abs().item(), 0.1)
            self.assertGreater(
                model.covar_module.base_kernel.raw_lengthscale.abs().item(), 0.1
            )
            self.assertGreater(model.covar_module.raw_outputscale.abs().item(), 1e-3)

            # test tracking iterations
            mll = self._getModel(double=double)
            if optimizer is fit_gpytorch_torch:
                options["disp"] = True
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                mll, info_dict = optimizer(mll, options=options, track_iterations=True)
                if optimizer == fit_gpytorch_scipy:
                    self.assertEqual(
                        sum(1 for w in ws if MAX_ITER_MSG in str(w.message)), 1
                    )
            self.assertEqual(len(info_dict["iterations"]), options["maxiter"])
            self.assertIsInstance(info_dict["iterations"][0], OptimizationIteration)
            self.assertTrue("fopt" in info_dict)
            self.assertTrue("wall_time" in info_dict)

            # Test different optimizer, for scipy optimizer,
            # because of different scipy OptimizeResult.message type
            if optimizer == fit_gpytorch_scipy:
                with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                    mll, info_dict = optimizer(
                        mll, options=options, track_iterations=False, method="slsqp"
                    )
                self.assertGreaterEqual(len(ws), 1)
                self.assertEqual(len(info_dict["iterations"]), 0)
                self.assertTrue("fopt" in info_dict)
                self.assertTrue("wall_time" in info_dict)

            # test extra param that does not affect loss
            options["disp"] = False
            mll = self._getModel(double=double)
            mll.register_parameter(
                "dummy_param",
                torch.nn.Parameter(
                    torch.tensor(
                        [5.0],
                        dtype=torch.double if double else torch.float,
                        device=self.device,
                    )
                ),
            )
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(
                    mll, optimizer=optimizer, options=options, max_retries=1
                )
                if optimizer == fit_gpytorch_scipy:
                    self.assertEqual(
                        sum(1 for w in ws if MAX_RETRY_MSG in str(w.message)), 1
                    )
            self.assertTrue(mll.dummy_param.grad is None)

            # test excluding a parameter
            mll = self._getModel(double=double)
            original_raw_noise = mll.model.likelihood.noise_covar.raw_noise.item()
            original_mean_module_constant = mll.model.mean_module.constant.item()
            options["exclude"] = [
                "model.mean_module.constant",
                "likelihood.noise_covar.raw_noise",
            ]
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(
                    mll, optimizer=optimizer, options=options, max_retries=1
                )
                if optimizer == fit_gpytorch_scipy:
                    self.assertTrue(
                        any(issubclass(w.category, OptimizationWarning)) for w in ws
                    )
                    self.assertEqual(
                        sum(1 for w in ws if MAX_RETRY_MSG in str(w.message)), 1
                    )
            model = mll.model
            # Make excluded params did not change
            self.assertEqual(
                model.likelihood.noise_covar.raw_noise.item(), original_raw_noise
            )
            self.assertEqual(
                model.mean_module.constant.item(), original_mean_module_constant
            )
            # Make sure other params did change
            self.assertGreater(
                model.covar_module.base_kernel.raw_lengthscale.abs().item(), 0.1
            )
            self.assertGreater(model.covar_module.raw_outputscale.abs().item(), 1e-3)

            # test non-default setting for approximate MLL computation
            is_scipy = optimizer == fit_gpytorch_scipy
            mll = self._getModel(double=double)
            with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                mll = fit_gpytorch_model(
                    mll,
                    optimizer=optimizer,
                    options=options,
                    max_retries=1,
                    approx_mll=is_scipy,
                )
                if is_scipy:
                    self.assertTrue(
                        any(issubclass(w.category, OptimizationWarning)) for w in ws
                    )
                    self.assertEqual(
                        sum(1 for w in ws if MAX_RETRY_MSG in str(w.message)), 1
                    )
            model = mll.model
            # Make sure all of the parameters changed
            self.assertGreater(model.likelihood.raw_noise.abs().item(), 1e-3)
            self.assertLess(model.mean_module.constant.abs().item(), 0.1)
            self.assertGreater(
                model.covar_module.base_kernel.raw_lengthscale.abs().item(), 0.1
            )
            self.assertGreater(model.covar_module.raw_outputscale.abs().item(), 1e-3)
Beispiel #23
0
    def test_prune_inferior_points_multi_objective(self):
        tkwargs = {"device": self.device}
        for dtype in (torch.float, torch.double):
            tkwargs["dtype"] = dtype
            X = torch.rand(3, 2, **tkwargs)
            ref_point = torch.tensor([0.25, 0.25], **tkwargs)
            # the event shape is `q x m` = 3 x 2
            samples = torch.tensor([[1.0, 2.0], [2.0, 1.0], [3.0, 4.0]], **tkwargs)
            mm = MockModel(MockPosterior(samples=samples))
            # test that a batched X raises errors
            with self.assertRaises(UnsupportedError):
                prune_inferior_points_multi_objective(
                    model=mm, X=X.expand(2, 3, 2), ref_point=ref_point
                )
            # test that a batched model raises errors (event shape is `q x m` = 3 x m)
            mm2 = MockModel(MockPosterior(samples=samples.expand(2, 3, 2)))
            with self.assertRaises(UnsupportedError):
                prune_inferior_points_multi_objective(
                    model=mm2, X=X, ref_point=ref_point
                )
            # test that invalid max_frac is checked properly
            with self.assertRaises(ValueError):
                prune_inferior_points_multi_objective(
                    model=mm, X=X, max_frac=1.1, ref_point=ref_point
                )
            # test basic behaviour
            X_pruned = prune_inferior_points_multi_objective(
                model=mm, X=X, ref_point=ref_point
            )
            self.assertTrue(torch.equal(X_pruned, X[[-1]]))
            # test unstd objective
            unstd_obj = UnstandardizeMCMultiOutputObjective(
                Y_mean=samples.mean(dim=0), Y_std=samples.std(dim=0), outcomes=[0, 1]
            )
            X_pruned = prune_inferior_points_multi_objective(
                model=mm, X=X, ref_point=ref_point, objective=unstd_obj
            )
            self.assertTrue(torch.equal(X_pruned, X[[-1]]))
            # test constraints
            samples_constrained = torch.tensor(
                [[1.0, 2.0, -1.0], [2.0, 1.0, -1.0], [3.0, 4.0, 1.0]], **tkwargs
            )
            mm_constrained = MockModel(MockPosterior(samples=samples_constrained))
            X_pruned = prune_inferior_points_multi_objective(
                model=mm_constrained,
                X=X,
                ref_point=ref_point,
                objective=unstd_obj,
                constraints=[lambda Y: Y[..., -1]],
            )
            self.assertTrue(torch.equal(X_pruned, X[:2]))

            # test non-repeated samples (requires mocking out MockPosterior's rsample)
            samples = torch.tensor(
                [[[3.0], [0.0], [0.0]], [[0.0], [2.0], [0.0]], [[0.0], [0.0], [1.0]]],
                device=self.device,
                dtype=dtype,
            )
            with mock.patch.object(MockPosterior, "rsample", return_value=samples):
                mm = MockModel(MockPosterior(samples=samples))
                X_pruned = prune_inferior_points_multi_objective(
                    model=mm, X=X, ref_point=ref_point
                )
            self.assertTrue(torch.equal(X_pruned, X))
            # test max_frac limiting
            with mock.patch.object(MockPosterior, "rsample", return_value=samples):
                mm = MockModel(MockPosterior(samples=samples))
                X_pruned = prune_inferior_points_multi_objective(
                    model=mm, X=X, ref_point=ref_point, max_frac=2 / 3
                )
            if self.device.type == "cuda":
                # sorting has different order on cuda
                self.assertTrue(torch.equal(X_pruned, torch.stack([X[2], X[1]], dim=0)))
            else:
                self.assertTrue(torch.equal(X_pruned, X[:2]))
            # test that zero-probability is in fact pruned
            samples[2, 0, 0] = 10
            with mock.patch.object(MockPosterior, "rsample", return_value=samples):
                mm = MockModel(MockPosterior(samples=samples))
                X_pruned = prune_inferior_points_multi_objective(
                    model=mm, X=X, ref_point=ref_point
                )
            self.assertTrue(torch.equal(X_pruned, X[:2]))
            # test high-dim sampling
            with ExitStack() as es:
                mock_event_shape = es.enter_context(
                    mock.patch(
                        "botorch.utils.testing.MockPosterior.event_shape",
                        new_callable=mock.PropertyMock,
                    )
                )
                mock_event_shape.return_value = torch.Size(
                    [1, 1, torch.quasirandom.SobolEngine.MAXDIM + 1]
                )
                es.enter_context(
                    mock.patch.object(MockPosterior, "rsample", return_value=samples)
                )
                mm = MockModel(MockPosterior(samples=samples))
                with warnings.catch_warnings(record=True) as ws, settings.debug(True):
                    prune_inferior_points_multi_objective(
                        model=mm, X=X, ref_point=ref_point
                    )
                    self.assertTrue(issubclass(ws[-1].category, SamplingWarning))

            # test marginalize_dim and constraints
            samples = torch.tensor([[1.0, 2.0], [2.0, 1.0], [3.0, 4.0]], **tkwargs)
            samples = samples.unsqueeze(-3).expand(
                *samples.shape[:-2],
                2,
                *samples.shape[-2:],
            )
            mm = MockModel(MockPosterior(samples=samples))
            X_pruned = prune_inferior_points_multi_objective(
                model=mm,
                X=X,
                ref_point=ref_point,
                objective=unstd_obj,
                constraints=[lambda Y: Y[..., -1] - 3.0],
                marginalize_dim=-3,
            )
            self.assertTrue(torch.equal(X_pruned, X[:2]))