class_docstr: Probit GP for preference learning with Laplace approximation
  
  A probit-likelihood GP that learns via pairwise comparison data, using a
  Laplace approximation of the posterior of the estimated utility values. By
  default it uses a scaled RBF kernel.
  
  Implementation is based on [Chu2005preference]_.
  Also see [Brochu2010tutorial]_ for additional reference.
  
  Note that in [Chu2005preference]_ the likelihood of a pairwise comparison
  is :math:`\left(\frac{f(x_1) - f(x_2)}{\sqrt{2}\sigma}\right)`, i.e. a scale is
  used in the denominator. To maintain consistency with usage of kernels
  elsewhere in BoTorch, we instead do not include :math:`\sigma` in the code
  (implicitly setting it to 1) and use ScaleKernel to scale the function.
  
  In the example below, the user/decision maker has stated that they prefer
  the first item over the second item and the third item over the second item,
  generating comparisons [0, 1] and [2, 1].
  Example:
      >>> from botorch.models import PairwiseGP
      >>> import torch
      >>> datapoints = torch.Tensor([[1, 2, 3], [4, 5, 6], [7, 8, 9]])
      >>> comparisons = torch.Tensor([[0, 1], [2, 1]])
      >>> model = PairwiseGP(datapoints, comparisons)
class_docstr_tok: ["Probit","GP","for","preference","learning","with","Laplace","approximation","A","probit-likelihood","GP","that","learns","via","pairwise","comparison","data",",","using","a","Laplace","approximation","of","the","posterior","of","the","estimated","utility","values",".","By","default","it","uses","a","scaled","RBF","kernel",".","Implementation","is","based","on","[","Chu2005preference","]","_",".","Also","see","[","Brochu2010tutorial","]","_","for","additional","reference",".","Note","that","in","[","Chu2005preference","]","_","the","likelihood","of","a","pairwise","comparison","is",":","math",":","`","\\left","(","\\frac","{","f","(","x_1",")","-","f","(","x_2",")","}","{","\\sqrt","{","2","}","\\sigma","}","\\right",")","`",",","i.e",".","a","scale","is","used","in","the","denominator",".","To","maintain","consistency","with","usage","of","kernels","elsewhere","in","BoTorch",",","we","instead","do","not","include",":","math",":","`","\\sigma","`","in","the","code","(","implicitly","setting","it","to","1",")","and","use","ScaleKernel","to","scale","the","function",".","In","the","example","below",",","the","user\/decision","maker","has","stated","that","they","prefer","the","first","item","over","the","second","item","and","the","third","item","over","the","second","item",",","generating","comparisons","[","0",",","1","]","and","[","2",",","1","]",".","Example",":",">",">",">","from","botorch.models","import","PairwiseGP",">",">",">","import","torch",">",">",">","datapoints","=","torch.Tensor","(","[","[","1",",","2",",","3","]",",","[","4",",","5",",","6","]",",","[","7",",","8",",","9","]","]",")",">",">",">","comparisons","=","torch.Tensor","(","[","[","0",",","1","]",",","[","2",",","1","]","]",")",">",">",">","model","=","PairwiseGP","(","datapoints",",","comparisons",")"]
docstr: Condition the model on new observations.
  
  Note that unlike other BoTorch models, PairwiseGP requires Y to be
  pairwise comparisons
  
  Args:
      X: A `batch_shape x n x d` dimension tensor X
      Y: A tensor of size `batch_shape x m x 2`. (i, j) means
          f_i is preferred over f_j
      kwargs: Not used.
  
  Returns:
      A (deepcopied) `Model` object of the same type, representing the
      original model conditioned on the new observations `(X, Y)`.
docstr_tok: ["Condition","the","model","on","new","observations",".","Note","that","unlike","other","BoTorch","models",",","PairwiseGP","requires","Y","to","be","pairwise","comparisons","Args",":","X",":","A","`","batch_shape","x","n","x","d","`","dimension","tensor","X","Y",":","A","tensor","of","size","`","batch_shape","x","m","x","2","`",".","(","i",",","j",")","means","f_i","is","preferred","over","f_j","kwargs",":","Not","used",".","Returns",":","A","(","deepcopied",")","`","Model","`","object","of","the","same","type",",","representing","the","original","model","conditioned","on","the","new","observations","`","(","X",",","Y",")","`","."]
code: def condition_on_observations(
      self, X: Tensor, Y: Tensor, **kwargs: Any
  ) -> Model:
      r"""Condition the model on new observations.
  
      Note that unlike other BoTorch models, PairwiseGP requires Y to be
      pairwise comparisons
  
      Args:
          X: A `batch_shape x n x d` dimension tensor X
          Y: A tensor of size `batch_shape x m x 2`. (i, j) means
              f_i is preferred over f_j
          kwargs: Not used.
  
      Returns:
          A (deepcopied) `Model` object of the same type, representing the
          original model conditioned on the new observations `(X, Y)`.
      """
      new_model = deepcopy(self)
  
      if self._has_no_data():
          # If the model previously has no data, set X and Y as the data directly
          new_model.set_train_data(X, Y, update_model=True)
      else:
          # Can only condition on pairwise comparisons instead of the directly
          # observed values. Raise a RuntimeError if Y is not a tensor presenting
          # pairwise comparisons
          if Y.dtype in (float32, float64) or Y.shape[-1] != 2:
              raise RuntimeError(
                  "Conditioning on non-pairwise comparison observations."
              )
  
          # Reshaping datapoints and comparisons by batches
          Y_new_batch_shape = Y.shape[:-2]
          new_datapoints = self.datapoints.expand(
              Y_new_batch_shape + self.datapoints.shape[-2:]
          )
          new_comparisons = self.comparisons.expand(
              Y_new_batch_shape + self.comparisons.shape[-2:]
          )
          # Reshape X since Y may have additional batch dim. from fantasy models
          X = X.expand(Y_new_batch_shape + X.shape[-2:])
  
          new_datapoints = torch.cat(
              (new_datapoints, X.to(new_datapoints)), dim=-2
          )
  
          shifted_comp = Y.to(new_comparisons) + self.n
          new_comparisons = torch.cat(
              (new_comparisons, shifted_comp), dim=-2
          )
  
          # TODO: be smart about how we can update covar matrix here
          new_model.set_train_data(
              new_datapoints, new_comparisons, update_model=True
          )
  
      return new_model
  
imports: from __future__ import annotations
  import warnings
  from copy import deepcopy
  from typing import Any, Dict, Iterable, List, Optional, Tuple, Union
  import numpy
  import torch
  from botorch.acquisition.objective import PosteriorTransform
  from botorch.exceptions import UnsupportedError
  from botorch.exceptions.warnings import _get_single_precision_warning, InputDataWarning
  from botorch.models.likelihoods.pairwise import PairwiseLikelihood, PairwiseProbitLikelihood
  from botorch.models.model import FantasizeMixin, Model
  from botorch.models.transforms.input import InputTransform
  from botorch.models.utils.assorted import consolidate_duplicates
  from botorch.posteriors.gpytorch import GPyTorchPosterior
  from botorch.posteriors.posterior import Posterior
  from gpytorch import settings
  from gpytorch.constraints import GreaterThan, Interval
  from gpytorch.distributions.multivariate_normal import MultivariateNormal
  from gpytorch.kernels.rbf_kernel import RBFKernel
  from gpytorch.kernels.scale_kernel import ScaleKernel
  from gpytorch.means.constant_mean import ConstantMean
  from gpytorch.mlls import MarginalLogLikelihood
  from gpytorch.models.gp import GP
  from gpytorch.priors.smoothed_box_prior import SmoothedBoxPrior
  from gpytorch.priors.torch_priors import GammaPrior
  from linear_operator.operators import LinearOperator, RootLinearOperator
  from linear_operator.utils.cholesky import psd_safe_cholesky
  from linear_operator.utils.errors import NotPSDError
  from scipy import optimize
  from torch import float32, float64, Tensor
  from torch.nn.modules.module import _IncompatibleKeys
label_desc: Use image node_id 25 for calling the PairwiseGP obj's underlying member method code with example usage: obj.condition_on_observations(X, Y) and returns: new_model