# Source code for botorch.models.model_list_gp_regression

#!/usr/bin/env python3
#
# LICENSE file in the root directory of this source tree.

r"""
Model List GP Regression models.
"""

from __future__ import annotations

from copy import deepcopy
from typing import Any, List

from botorch.exceptions.errors import BotorchTensorDimensionError
from botorch.models.gpytorch import GPyTorchModel, ModelListGPyTorchModel
from gpytorch.models import IndependentModelList
from torch import Tensor

[docs]class ModelListGP(IndependentModelList, ModelListGPyTorchModel): r"""A multi-output GP model with independent GPs for the outputs. This model supports different-shaped training inputs for each of its sub-models. It can be used with any BoTorch models. Internally, this model is just a list of individual models, but it implements the same input/output interface as all other BoTorch models. This makes it very flexible and convenient to work with. The sequential evaluation comes at a performance cost though - if you are using a block design (i.e. the same number of training example for each output, and a similar model structure, you should consider using a batched GP model instead). """ def __init__(self, *gp_models: GPyTorchModel) -> None: r"""A multi-output GP model with independent GPs for the outputs. Args: *gp_models: An variable number of single-output BoTorch models. If models have input/output transforms, these are honored individually for each model. Example: >>> model1 = SingleTaskGP(train_X1, train_Y1) >>> model2 = SingleTaskGP(train_X2, train_Y2) >>> model = ModelListGP(model1, model2) """ super().__init__(*gp_models)
[docs] def condition_on_observations( self, X: Tensor, Y: Tensor, **kwargs: Any ) -> ModelListGP: r"""Condition the model on new observations. Args: X: A batch_shape x n' x d-dim Tensor, where d is the dimension of the feature space, n' is the number of points per batch, and batch_shape is the batch shape (must be compatible with the batch shape of the model). Y: A batch_shape' x n' x m-dim Tensor, where m is the number of model outputs, n' is the number of points per batch, and batch_shape' is the batch shape of the observations. batch_shape' must be broadcastable to batch_shape using standard broadcasting semantics. If Y has fewer batch dimensions than X, its is assumed that the missing batch dimensions are the same for all Y. Returns: A ModelListGPyTorchModel representing the original model conditioned on the new observations (X, Y) (and possibly noise observations passed in via kwargs). Here the i-th model has n_i + n' training examples, where the n' training examples have been added and all test-time caches have been updated. """ self._validate_tensor_args( X=X, Y=Y, Yvar=kwargs.get("noise", None), strict=False ) inputs = [X] * self.num_outputs if Y.shape[-1] != self.num_outputs: raise BotorchTensorDimensionError( "Incorrect number of outputs for observations. Received " f"{Y.shape[-1]} observation outputs, but model has " f"{self.num_outputs} outputs." ) targets = [Y[..., i] for i in range(Y.shape[-1])] if "noise" in kwargs: noise = kwargs.pop("noise") # Note: dimension checks were performed in _validate_tensor_args kwargs_ = {**kwargs, "noise": [noise[..., i] for i in range(Y.shape[-1])]} else: kwargs_ = kwargs return super().get_fantasy_model(inputs, targets, **kwargs_)
[docs] def subset_output(self, idcs: List[int]) -> ModelListGP: r"""Subset the model along the output dimension. Args: idcs: The output indices to subset the model to. Returns: The current model, subset to the specified output indices. """ return self.__class__(*[deepcopy(self.models[i]) for i in idcs])