docstr: Sample from the posterior (with gradients) using base samples.
  
  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])`.
      base_samples: An (optional) Tensor of `N(0, I)` base samples of
          appropriate dimension, typically obtained from a `Sampler`.
          This is used for deterministic optimization.
      train_diff: Difference between train mean and train responses to assume
          during sampling.
  
  Returns:
      Samples from the posterior, a tensor of shape
      `self._extended_shape(sample_shape=sample_shape)`.
docstr_tok: ["Sample","from","the","posterior","(","with","gradients",")","using","base","samples",".","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","]",")","`",".","base_samples",":","An","(","optional",")","Tensor","of","`","N","(","0",",","I",")","`","base","samples","of","appropriate","dimension",",","typically","obtained","from","a","`","Sampler","`",".","This","is","used","for","deterministic","optimization",".","train_diff",":","Difference","between","train","mean","and","train","responses","to","assume","during","sampling",".","Returns",":","Samples","from","the","posterior",",","a","tensor","of","shape","`","self._extended_shape","(","sample_shape=sample_shape",")","`","."]
code: def rsample_from_base_samples(
      self,
      sample_shape: torch.Size,
      base_samples: Optional[Tensor],
      train_diff: Optional[Tensor] = None,
  ) -> Tensor:
      r"""Sample from the posterior (with gradients) using base samples.
  
      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])`.
          base_samples: An (optional) Tensor of `N(0, I)` base samples of
              appropriate dimension, typically obtained from a `Sampler`.
              This is used for deterministic optimization.
          train_diff: Difference between train mean and train responses to assume
              during sampling.
  
      Returns:
          Samples from the posterior, a tensor of shape
          `self._extended_shape(sample_shape=sample_shape)`.
      """
      if train_diff is None:
          train_diff = self.train_diff
  
      (
          base_samples,
          noise_base_samples,
          test_noise_base_samples,
      ) = self._prepare_base_samples(
          sample_shape=sample_shape, base_samples=base_samples
      )
      joint_samples = self._draw_from_base_covar(
          self.joint_covariance_matrix, base_samples
      )
      noise_samples = self._draw_from_base_covar(
          self.train_noise, noise_base_samples
      )
  
      # pluck out the train + test samples and add the likelihood's noise to the
      # train side. This should be fine for higher rank likelihoods.
      n_obs = self.num_tasks * self.num_train
      n_test = joint_samples.shape[-1] - n_obs
      obs_samples, test_samples = torch.split(
          joint_samples, [n_obs, n_test], dim=-1
      )
      updated_obs_samples = obs_samples + noise_samples
      obs_minus_samples = (
          train_diff.reshape(*train_diff.shape[:-2], -1)
          - updated_obs_samples
      )
      train_covar_plus_noise = self.train_train_covar + self.train_noise
      obs_solve = train_covar_plus_noise.solve(
          obs_minus_samples.unsqueeze(-1)
      )
  
      # and multiply the test-observed matrix against the result of the solve
      updated_samples = self.test_train_covar.matmul(obs_solve).squeeze(
          -1
      )
  
      # finally, we add the conditioned samples to the prior samples
      final_samples = test_samples + updated_samples
  
      # add in likelihood noise if necessary
      if self.observation_noise:
          test_noise_samples = self._draw_from_base_covar(
              self.test_noise, test_noise_base_samples
          )
          final_samples = final_samples + test_noise_samples
  
      # and reshape
      final_samples = final_samples.reshape(
          *final_samples.shape[:-1],
          self.test_mean.shape[-2],
          self.num_tasks,
      )
      final_samples = final_samples + self.test_mean
  
      return final_samples
  
file_path: botorch/botorch/posteriors/multitask.py
imports: from typing import Optional, Tuple, Union
  import torch
  from botorch.exceptions.errors import BotorchTensorDimensionError
  from botorch.posteriors.gpytorch import GPyTorchPosterior
  from gpytorch.distributions import MultivariateNormal
  from linear_operator.operators import LinearOperator, to_linear_operator
  from torch import Tensor
label_desc: Use image node_id 5 for calling the MultitaskGPPosterior obj's underlying member method code with example usage: obj.rsample_from_base_samples(sample_shape, base_samples, train_diff) and returns: final_samples