diff --git a/botorch/posteriors/__init__.py b/botorch/posteriors/__init__.py index a2b2211f55..a7990c1219 100644 --- a/botorch/posteriors/__init__.py +++ b/botorch/posteriors/__init__.py @@ -4,7 +4,6 @@ # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. -from botorch.posteriors.deterministic import DeterministicPosterior from botorch.posteriors.fully_bayesian import ( FullyBayesianPosterior, GaussianMixturePosterior, @@ -18,7 +17,6 @@ from botorch.posteriors.transformed import TransformedPosterior __all__ = [ - "DeterministicPosterior", "GaussianMixturePosterior", "FullyBayesianPosterior", "GPyTorchPosterior", diff --git a/botorch/posteriors/deterministic.py b/botorch/posteriors/deterministic.py index 1e39be3291..4b87eb9e4d 100644 --- a/botorch/posteriors/deterministic.py +++ b/botorch/posteriors/deterministic.py @@ -3,89 +3,3 @@ # # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. - -r""" -Deterministic (degenerate) posteriors. Used in conjunction with deterministic -models. -""" - -from __future__ import annotations - -from typing import Optional -from warnings import warn - -import torch -from botorch.posteriors.posterior import Posterior -from torch import Tensor - - -class DeterministicPosterior(Posterior): - r"""Deterministic posterior. - - [DEPRECATED] Use `EnsemblePosterior` instead. - """ - - def __init__(self, values: Tensor) -> None: - r""" - Args: - values: Values of the samples produced by this posterior. - """ - warn( - "`DeterministicPosterior` is marked for deprecation, consider using " - "`EnsemblePosterior`.", - DeprecationWarning, - ) - self.values = values - - @property - def device(self) -> torch.device: - r"""The torch device of the posterior.""" - return self.values.device - - @property - def dtype(self) -> torch.dtype: - r"""The torch dtype of the posterior.""" - return self.values.dtype - - def _extended_shape( - self, sample_shape: torch.Size = torch.Size() # noqa: B008 - ) -> torch.Size: - r"""Returns the shape of the samples produced by the posterior with - the given `sample_shape`. - """ - return sample_shape + self.values.shape - - @property - def mean(self) -> Tensor: - r"""The mean of the posterior as a `(b) x n x m`-dim Tensor.""" - return self.values - - @property - def variance(self) -> Tensor: - r"""The variance of the posterior as a `(b) x n x m`-dim Tensor. - - As this is a deterministic posterior, this is a tensor of zeros. - """ - return torch.zeros_like(self.values) - - def rsample( - self, - sample_shape: Optional[torch.Size] = None, - ) -> Tensor: - r"""Sample from the posterior (with gradients). - - For the deterministic posterior, this just returns the values expanded - to the requested shape. - - Args: - sample_shape: A `torch.Size` object specifying the sample shape. To - draw `n` samples, set to `torch.Size([n])`. To draw `b` batches - of `n` samples each, set to `torch.Size([b, n])`. - - Returns: - Samples from the posterior, a tensor of shape - `self._extended_shape(sample_shape=sample_shape)`. - """ - if sample_shape is None: - sample_shape = torch.Size([1]) - return self.values.expand(self._extended_shape(sample_shape)) diff --git a/botorch/sampling/__init__.py b/botorch/sampling/__init__.py index 0c57f783ad..9b6c758283 100644 --- a/botorch/sampling/__init__.py +++ b/botorch/sampling/__init__.py @@ -5,7 +5,6 @@ # LICENSE file in the root directory of this source tree. from botorch.sampling.base import MCSampler -from botorch.sampling.deterministic import DeterministicSampler from botorch.sampling.get_sampler import get_sampler from botorch.sampling.list_sampler import ListSampler from botorch.sampling.normal import IIDNormalSampler, SobolQMCNormalSampler @@ -20,7 +19,6 @@ __all__ = [ - "DeterministicSampler", "ForkedRNGSampler", "get_sampler", "IIDNormalSampler", diff --git a/botorch/sampling/deterministic.py b/botorch/sampling/deterministic.py index c5d1c0bb4c..4b87eb9e4d 100644 --- a/botorch/sampling/deterministic.py +++ b/botorch/sampling/deterministic.py @@ -3,35 +3,3 @@ # # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. - -r""" -A dummy sampler for use with deterministic models. -""" - -from __future__ import annotations - -from botorch.posteriors.deterministic import DeterministicPosterior -from botorch.sampling.stochastic_samplers import StochasticSampler - - -class DeterministicSampler(StochasticSampler): - r"""A sampler that simply calls `posterior.rsample`, intended to be used with - `DeterministicModel` & `DeterministicPosterior`. - - [DEPRECATED] - Use `IndexSampler` in conjunction with `EnsemblePosterior` - instead of `DeterministicSampler` with `DeterministicPosterior`. - - This is effectively signals that `StochasticSampler` is safe to use with - deterministic models since their output is deterministic by definition. - """ - - def _update_base_samples( - self, posterior: DeterministicPosterior, base_sampler: DeterministicSampler - ) -> None: - r"""This is a no-op since there are no base samples to update. - - Args: - posterior: The posterior for which the base samples are constructed. - base_sampler: The base sampler to retrieve the base samples from. - """ - return diff --git a/botorch/sampling/get_sampler.py b/botorch/sampling/get_sampler.py index 2f92f4e54b..0bf4b9bf20 100644 --- a/botorch/sampling/get_sampler.py +++ b/botorch/sampling/get_sampler.py @@ -9,7 +9,6 @@ import torch from botorch.logging import logger -from botorch.posteriors.deterministic import DeterministicPosterior from botorch.posteriors.ensemble import EnsemblePosterior from botorch.posteriors.gpytorch import GPyTorchPosterior from botorch.posteriors.posterior import Posterior @@ -17,7 +16,6 @@ from botorch.posteriors.torch import TorchPosterior from botorch.posteriors.transformed import TransformedPosterior from botorch.sampling.base import MCSampler -from botorch.sampling.deterministic import DeterministicSampler from botorch.sampling.index_sampler import IndexSampler from botorch.sampling.list_sampler import ListSampler from botorch.sampling.normal import ( @@ -119,16 +117,6 @@ def _get_sampler_list( return ListSampler(*samplers) -@GetSampler.register(DeterministicPosterior) -def _get_sampler_deterministic( - posterior: DeterministicPosterior, - sample_shape: torch.Size, - seed: Optional[int] = None, -) -> MCSampler: - r"""Get the dummy `DeterministicSampler` for the `DeterministicPosterior`.""" - return DeterministicSampler(sample_shape=sample_shape, seed=seed) - - @GetSampler.register(EnsemblePosterior) def _get_sampler_ensemble( posterior: EnsemblePosterior, diff --git a/test/models/test_model.py b/test/models/test_model.py index dd30c106ea..273e60887e 100644 --- a/test/models/test_model.py +++ b/test/models/test_model.py @@ -10,7 +10,7 @@ from botorch.exceptions.errors import InputDataError from botorch.models.deterministic import GenericDeterministicModel from botorch.models.model import Model, ModelDict, ModelList -from botorch.posteriors.deterministic import DeterministicPosterior +from botorch.posteriors.ensemble import EnsemblePosterior from botorch.posteriors.posterior_list import PosteriorList from botorch.utils.datasets import SupervisedDataset from botorch.utils.testing import BotorchTestCase, MockModel, MockPosterior @@ -35,7 +35,10 @@ def evaluate(self, Y): def forward(self, posterior): return PosteriorList( - *[DeterministicPosterior(2 * p.mean + 1) for p in posterior.posteriors] + *[ + EnsemblePosterior(2 * p.mean.unsqueeze(0) + 1) + for p in posterior.posteriors + ] ) diff --git a/test/posteriors/test_deterministic.py b/test/posteriors/test_deterministic.py index 0dbfa37bb9..4b87eb9e4d 100644 --- a/test/posteriors/test_deterministic.py +++ b/test/posteriors/test_deterministic.py @@ -3,37 +3,3 @@ # # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. - -import itertools - -from warnings import catch_warnings - -import torch -from botorch.posteriors.deterministic import DeterministicPosterior -from botorch.utils.testing import BotorchTestCase - - -class TestDeterministicPosterior(BotorchTestCase): - def test_DeterministicPosterior(self): - for shape, dtype in itertools.product( - ((3, 2), (2, 3, 1)), (torch.float, torch.double) - ): - values = torch.randn(*shape, device=self.device, dtype=dtype) - p = DeterministicPosterior(values) - with catch_warnings(record=True) as ws: - p = DeterministicPosterior(values) - self.assertTrue( - any("marked for deprecation" in str(w.message) for w in ws) - ) - self.assertEqual(p.device.type, self.device.type) - self.assertEqual(p.dtype, dtype) - self.assertEqual(p._extended_shape(), values.shape) - with self.assertRaises(NotImplementedError): - p.base_sample_shape - self.assertTrue(torch.equal(p.mean, values)) - self.assertTrue(torch.equal(p.variance, torch.zeros_like(values))) - # test sampling - samples = p.rsample() - self.assertTrue(torch.equal(samples, values.unsqueeze(0))) - samples = p.rsample(torch.Size([2])) - self.assertTrue(torch.equal(samples, values.expand(2, *values.shape))) diff --git a/test/posteriors/test_posterior.py b/test/posteriors/test_posterior.py index b2331253f5..1396e42405 100644 --- a/test/posteriors/test_posterior.py +++ b/test/posteriors/test_posterior.py @@ -9,7 +9,7 @@ import torch from botorch.posteriors import GPyTorchPosterior, Posterior, PosteriorList -from botorch.posteriors.deterministic import DeterministicPosterior +from botorch.posteriors.ensemble import EnsemblePosterior from botorch.utils.testing import BotorchTestCase from gpytorch.distributions import MultivariateNormal from linear_operator.operators import to_linear_operator @@ -57,7 +57,7 @@ def _make_gpytorch_posterior(self, shape, dtype): def _make_deterministic_posterior(self, shape, dtype): mean = torch.rand(*shape, 1, dtype=dtype, device=self.device) - return DeterministicPosterior(values=mean) + return EnsemblePosterior(values=mean.unsqueeze(0)) def test_posterior_list(self): for dtype, use_deterministic in product( diff --git a/test/sampling/test_deterministic.py b/test/sampling/test_deterministic.py index 4dedef1605..4b87eb9e4d 100644 --- a/test/sampling/test_deterministic.py +++ b/test/sampling/test_deterministic.py @@ -3,22 +3,3 @@ # # This source code is licensed under the MIT license found in the # LICENSE file in the root directory of this source tree. - -import torch -from botorch.sampling.deterministic import DeterministicSampler -from botorch.utils.testing import BotorchTestCase, MockPosterior - - -class TestDeterministicSampler(BotorchTestCase): - def test_deterministic_sampler(self): - # Basic usage. - samples = torch.rand(1, 2) - posterior = MockPosterior(samples=samples) - sampler = DeterministicSampler(sample_shape=torch.Size([2])) - self.assertTrue(torch.equal(samples.repeat(2, 1, 1), sampler(posterior))) - - # Test _update_base_samples. - sampler._update_base_samples( - posterior=posterior, - base_sampler=sampler, - ) diff --git a/test/sampling/test_get_sampler.py b/test/sampling/test_get_sampler.py index b8d65683fe..77aa645d49 100644 --- a/test/sampling/test_get_sampler.py +++ b/test/sampling/test_get_sampler.py @@ -5,7 +5,6 @@ # LICENSE file in the root directory of this source tree. import torch -from botorch.posteriors.deterministic import DeterministicPosterior from botorch.posteriors.gpytorch import GPyTorchPosterior from botorch.posteriors.posterior_list import PosteriorList from botorch.posteriors.torch import TorchPosterior @@ -13,7 +12,6 @@ from botorch.sampling.get_sampler import get_sampler from botorch.sampling.list_sampler import ListSampler from botorch.sampling.normal import IIDNormalSampler, SobolQMCNormalSampler -from botorch.sampling.stochastic_samplers import StochasticSampler from botorch.utils.testing import BotorchTestCase from gpytorch.distributions import MultivariateNormal from torch.distributions.gamma import Gamma @@ -22,40 +20,42 @@ class TestGetSampler(BotorchTestCase): def test_get_sampler(self): # Basic usage w/ gpytorch posterior. - posterior = GPyTorchPosterior( + mvn_posterior = GPyTorchPosterior( distribution=MultivariateNormal(torch.rand(2), torch.eye(2)) ) + seed = 2 + n_samples = 10 sampler = get_sampler( - posterior=posterior, sample_shape=torch.Size([10]), seed=2 + posterior=mvn_posterior, sample_shape=torch.Size([n_samples]), seed=seed ) self.assertIsInstance(sampler, SobolQMCNormalSampler) - self.assertEqual(sampler.seed, 2) - self.assertEqual(sampler.sample_shape, torch.Size([10])) + self.assertEqual(sampler.seed, seed) + self.assertEqual(sampler.sample_shape, torch.Size([n_samples])) # Fallback to IID sampler. - posterior = GPyTorchPosterior( + big_mvn_posterior = GPyTorchPosterior( distribution=MultivariateNormal(torch.rand(22000), torch.eye(22000)) ) - sampler = get_sampler(posterior=posterior, sample_shape=torch.Size([10])) + sampler = get_sampler( + posterior=big_mvn_posterior, sample_shape=torch.Size([n_samples]) + ) self.assertIsInstance(sampler, IIDNormalSampler) - self.assertEqual(sampler.sample_shape, torch.Size([10])) + self.assertEqual(sampler.sample_shape, torch.Size([n_samples])) # Transformed posterior. tf_post = TransformedPosterior( - posterior=posterior, sample_transform=lambda X: X + posterior=big_mvn_posterior, sample_transform=lambda X: X ) - sampler = get_sampler(posterior=tf_post, sample_shape=torch.Size([10])) + sampler = get_sampler(posterior=tf_post, sample_shape=torch.Size([n_samples])) self.assertIsInstance(sampler, IIDNormalSampler) - self.assertEqual(sampler.sample_shape, torch.Size([10])) + self.assertEqual(sampler.sample_shape, torch.Size([n_samples])) - # PosteriorList with transformed & deterministic. - post_list = PosteriorList( - tf_post, DeterministicPosterior(values=torch.rand(1, 2)) - ) + # PosteriorList with transformed & original + post_list = PosteriorList(tf_post, mvn_posterior) sampler = get_sampler(posterior=post_list, sample_shape=torch.Size([5])) self.assertIsInstance(sampler, ListSampler) self.assertIsInstance(sampler.samplers[0], IIDNormalSampler) - self.assertIsInstance(sampler.samplers[1], StochasticSampler) + self.assertIsInstance(sampler.samplers[1], SobolQMCNormalSampler) for s in sampler.samplers: self.assertEqual(s.sample_shape, torch.Size([5]))