class_docstr: Gaussian Process Regression model.
  
  The core of a Gaussian Process is a covariance function :math:`k` which governs
  the similarity between input points. Given :math:`k`, we can establish a
  distribution over functions :math:`f` by a multivarite normal distribution
  
  .. math:: p(f(X)) = \mathcal{N}(0, k(X, X)),
  
  where :math:`X` is any set of input points and :math:`k(X, X)` is a covariance
  matrix whose entries are outputs :math:`k(x, z)` of :math:`k` over input pairs
  :math:`(x, z)`. This distribution is usually denoted by
  
  .. math:: f \sim \mathcal{GP}(0, k).
  
  .. note:: Generally, beside a covariance matrix :math:`k`, a Gaussian Process can
      also be specified by a mean function :math:`m` (which is a zero-value function
      by default). In that case, its distribution will be
  
      .. math:: p(f(X)) = \mathcal{N}(m(X), k(X, X)).
  
  Given inputs :math:`X` and their noisy observations :math:`y`, the Gaussian Process
  Regression model takes the form
  
  .. math::
      f &\sim \mathcal{GP}(0, k(X, X)),\\
      y & \sim f + \epsilon,
  
  where :math:`\epsilon` is Gaussian noise.
  
  .. note:: This model has :math:`\mathcal{O}(N^3)` complexity for training,
      :math:`\mathcal{O}(N^3)` complexity for testing. Here, :math:`N` is the number
      of train inputs.
  
  Reference:
  
  [1] `Gaussian Processes for Machine Learning`,
  Carl E. Rasmussen, Christopher K. I. Williams
  
  :param torch.Tensor X: A input data for training. Its first dimension is the number
      of data points.
  :param torch.Tensor y: An output data for training. Its last dimension is the
      number of data points.
  :param ~pyro.contrib.gp.kernels.kernel.Kernel kernel: A Pyro kernel object, which
      is the covariance function :math:`k`.
  :param torch.Tensor noise: Variance of Gaussian noise of this model.
  :param callable mean_function: An optional mean function :math:`m` of this Gaussian
      process. By default, we use zero mean.
  :param float jitter: A small positive term which is added into the diagonal part of
      a covariance matrix to help stablize its Cholesky decomposition.
code: def model(self):
      self.set_mode("model")
  
      N = self.X.size(0)
      Kff = self.kernel(self.X)
      Kff.view(-1)[:: N + 1] += (
          self.jitter + self.noise
      )  # add noise to diagonal
      Lff = torch.linalg.cholesky(Kff)
  
      zero_loc = self.X.new_zeros(self.X.size(0))
      f_loc = zero_loc + self.mean_function(self.X)
      if self.y is None:
          f_var = Lff.pow(2).sum(dim=-1)
          return f_loc, f_var
      else:
          return pyro.sample(
              self._pyro_get_fullname("y"),
              dist.MultivariateNormal(f_loc, scale_tril=Lff)
              .expand_by(self.y.shape[:-1])
              .to_event(self.y.dim() - 1),
              obs=self.y,
          )
  
file_path: pyro/pyro/contrib/gp/models/gpr.py
imports: import torch
  import torch.distributions
  from torch.distributions import constraints
  import pyro
  import pyro.distributions
  from pyro.contrib.gp.models.model import GPModel
  from pyro.contrib.gp.util import conditional
  from pyro.nn.module import PyroParam, pyro_method
  from pyro.util import warn_if_nan
label_desc: Use image node_id 2 for calling the GPRegression obj's underlying member method code with example usage: obj.model() and returns: f_loc,  f_var, pyro