docstr: Computes the posterior predictive covariance of a GP
  
  :param Tensor test_mean: The test prior mean
  :param ~linear_operator.operators.LinearOperator test_train_covar:
      Covariance matrix between test and train inputs
  :return: The predictive posterior mean of the test points
docstr_tok: ["Computes","the","posterior","predictive","covariance","of","a","GP",":","param","Tensor","test_mean",":","The","test","prior","mean",":","param","~linear_operator.operators.LinearOperator","test_train_covar",":","Covariance","matrix","between","test","and","train","inputs",":","return",":","The","predictive","posterior","mean","of","the","test","points"]
code: def exact_predictive_mean(
      self, test_mean: Tensor, test_train_covar: LinearOperator
  ) -> Tensor:
      """
      Computes the posterior predictive covariance of a GP
  
      :param Tensor test_mean: The test prior mean
      :param ~linear_operator.operators.LinearOperator test_train_covar:
          Covariance matrix between test and train inputs
      :return: The predictive posterior mean of the test points
      """
      # NOTE TO FUTURE SELF:
      # You **cannot* use addmv here, because test_train_covar may not actually be a non lazy tensor even for an exact
      # GP, and using addmv requires you to to_dense test_train_covar, which is obviously a huge no-no!
  
      # see https://github.com/cornellius-gp/gpytorch/pull/2317#discussion_r1157994719
      mean_cache = self.mean_cache
      if len(mean_cache.shape) == 4:
          mean_cache = mean_cache.squeeze(1)
  
      # Handle NaNs
      nan_policy = settings.observation_nan_policy.value()
      if nan_policy == "ignore":
          res = (test_train_covar @ mean_cache.unsqueeze(-1)).squeeze(
              -1
          )
      elif nan_policy == "mask":
          # Restrict train dimension to observed values
          observed = settings.observation_nan_policy._get_observed(
              mean_cache, torch.Size((mean_cache.shape[-1],))
          )
          full_mask = torch.ones(
              test_mean.shape[-1],
              dtype=torch.bool,
              device=test_mean.device,
          )
          test_train_covar = MaskedLinearOperator(
              to_linear_operator(test_train_covar),
              full_mask,
              observed.reshape(-1),
          )
          res = (
              test_train_covar @ mean_cache[..., observed].unsqueeze(-1)
          ).squeeze(-1)
      else:  # 'fill'
          # Set the columns corresponding to missing observations to 0 to ignore them during matmul.
          mask = (~torch.isnan(mean_cache)).to(torch.float)[
              ..., None, :
          ]
          test_train_covar = test_train_covar * mask
          mean = settings.observation_nan_policy._fill_tensor(
              mean_cache
          )
          res = (test_train_covar @ mean.unsqueeze(-1)).squeeze(-1)
      res = res + test_mean
  
      return res
  
imports: import functools
  import string
  import warnings
  import torch
  from linear_operator import to_dense, to_linear_operator
  from linear_operator.operators import AddedDiagLinearOperator, BatchRepeatLinearOperator, ConstantMulLinearOperator, DenseLinearOperator, InterpolatedLinearOperator, LinearOperator, LowRankRootAddedDiagLinearOperator, MaskedLinearOperator, MatmulLinearOperator, RootLinearOperator, ZeroLinearOperator
  from linear_operator.utils.cholesky import psd_safe_cholesky
  from linear_operator.utils.interpolation import left_interp, left_t_interp
  from torch import Tensor
  from ..None import settings
  from ..distributions import MultitaskMultivariateNormal
  from ..lazy import LazyEvaluatedKernelTensor
  from ..utils.memoize import add_to_cache, cached, clear_cache_hook, pop_from_cache
label_desc: Use image node_id 12 for calling the DefaultPredictionStrategy obj's underlying member method code with example usage: obj.exact_predictive_mean(test_mean, test_train_covar) and returns: res