Source code for botorch.acquisition.thompson_sampling

# Copyright (c) Meta Platforms, Inc. and affiliates.
#
# 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.acquisition.analytic import AcquisitionFunction
from botorch.acquisition.objective import PosteriorTransform
from botorch.models.model import Model
from botorch.sampling.pathwise.posterior_samplers import get_matheron_path_model
from botorch.utils.transforms import t_batch_mode_transform
from torch import Tensor


BATCH_SIZE_CHANGE_ERROR = """The batch size of PathwiseThompsonSampling should \
not change during a forward pass - was {}, now {}. Please re-initialize the \
acquisition if you want to change the batch size."""


[docs] class PathwiseThompsonSampling(AcquisitionFunction): r"""Single-outcome Thompson Sampling packaged as an (analytic) acquisition function. Querying the acquisition function gives the summed values of one or more draws from a pathwise drawn posterior sample, and thus it maximization yields one (or multiple) Thompson sample(s). Example: >>> model = SingleTaskGP(train_X, train_Y) >>> TS = PathwiseThompsonSampling(model) """ def __init__( self, model: Model, posterior_transform: PosteriorTransform | None = None, ) -> None: r"""Single-outcome TS. Args: model: A fitted GP model. posterior_transform: A PosteriorTransform. If using a multi-output model, a PosteriorTransform that transforms the multi-output posterior into a single-output posterior is required. """ if model._is_fully_bayesian: raise NotImplementedError( "PathwiseThompsonSampling is not supported for fully Bayesian models", ) super().__init__(model=model) self.batch_size: int | None = None
[docs] def redraw(self) -> None: self.samples = get_matheron_path_model( model=self.model, sample_shape=torch.Size([self.batch_size]) )
[docs] @t_batch_mode_transform() def forward(self, X: Tensor) -> Tensor: r"""Evaluate the pathwise posterior sample draws on the candidate set X. Args: X: A `(b1 x ... bk) x 1 x d`-dim batched tensor of `d`-dim design points. Returns: A `(b1 x ... bk) x [num_models for fully bayesian]`-dim tensor of evaluations on the posterior sample draws. """ batch_size = X.shape[-2] q_dim = -2 # batch_shape x q x 1 x d X = X.unsqueeze(-2) if self.batch_size is None: self.batch_size = batch_size self.redraw() elif self.batch_size != batch_size: raise ValueError( BATCH_SIZE_CHANGE_ERROR.format(self.batch_size, batch_size) ) # posterior_values.shape post-squeeze: # batch_shape x q x m posterior_values = self.samples(X).squeeze(-2) # sum over batch dim and squeeze num_objectives dim (-1) return posterior_values.sum(q_dim).squeeze(-1)