class_docstr: Similar to :class:`~gpytorch.variational.VariationalStrategy`,
  except the whitening operation is performed using Contour Integral Quadrature
  rather than Cholesky (see `Pleiss et al. (2020)`_ for more info).
  See the `CIQ-SVGP tutorial`_ for an example.
  
  Contour Integral Quadrature uses iterative matrix-vector multiplication to approximate
  the :math:`\mathbf K_{\mathbf Z \mathbf Z}^{-1/2}` matrix used for the whitening operation.
  This can be more efficient than the standard variational strategy for large numbers
  of inducing points (e.g. :math:`M > 1000`) or when the inducing points have structure
  (e.g. they lie on an evenly-spaced grid).
  
  .. note::
  
      It is recommended that this object is used in conjunction with
      :obj:`~gpytorch.variational.NaturalVariationalDistribution` and
      `natural gradient descent`_.
  
  :param model: Model this strategy is applied to.
      Typically passed in when the VariationalStrategy is created in the
      __init__ method of the user defined model.
  :param inducing_points: Tensor containing a set of inducing
      points to use for variational inference.
  :param variational_distribution: A
      VariationalDistribution object that represents the form of the variational distribution :math:`q(\mathbf u)`
  :param learn_inducing_locations: (Default True): Whether or not
      the inducing point locations :math:`\mathbf Z` should be learned (i.e. are they
      parameters of the model).
  :param jitter_val: Amount of diagonal jitter to add for Cholesky factorization numerical stability
  
  .. _Pleiss et al. (2020):
      https://arxiv.org/pdf/2006.11267.pdf
  .. _CIQ-SVGP tutorial:
      examples/04_Variational_and_Approximate_GPs/SVGP_CIQ.html
  .. _natural gradient descent:
      examples/04_Variational_and_Approximate_GPs/Natural_Gradient_Descent.html
code: def forward(
      self,
      x: torch.Tensor,
      inducing_points: torch.Tensor,
      inducing_values: torch.Tensor,
      variational_inducing_covar: Optional[LinearOperator] = None,
      *params,
      **kwargs,
  ) -> MultivariateNormal:
      # Compute full prior distribution
      full_inputs = torch.cat([inducing_points, x], dim=-2)
      full_output = self.model.forward(full_inputs, *params, **kwargs)
      full_covar = full_output.lazy_covariance_matrix
  
      # Covariance terms
      num_induc = inducing_points.size(-2)
      test_mean = full_output.mean[..., num_induc:]
      induc_induc_covar = (
          full_covar[..., :num_induc, :num_induc]
          .evaluate_kernel()
          .add_jitter(self.jitter_val)
      )
      induc_data_covar = full_covar[
          ..., :num_induc, num_induc:
      ].to_dense()
      data_data_covar = full_covar[
          ..., num_induc:, num_induc:
      ].add_jitter(self.jitter_val)
  
      # Compute interpolation terms
      # K_XZ K_ZZ^{-1} \mu_z
      # K_XZ K_ZZ^{-1/2} \mu_Z
      with settings.max_preconditioner_size(
          0
      ):  # Turn off preconditioning for CIQ
          interp_term = to_linear_operator(
              induc_induc_covar
          ).sqrt_inv_matmul(induc_data_covar)
  
      # Compute interpolated mean and variance terms
      # We have separate computation rules for NGD versus standard GD
      if self._ngd():
          interp_mean, interp_var, kl_div = _NgdInterpTerms.apply(
              interp_term,
              self._variational_distribution.natural_vec,
              self._variational_distribution.natural_mat,
          )
  
          # Compute the covariance of q(f)
          predictive_var = (
              data_data_covar.diagonal(dim1=-1, dim2=-2)
              - interp_term.pow(2).sum(dim=-2)
              + interp_var
          )
          predictive_var = torch.clamp_min(
              predictive_var,
              settings.min_variance.value(predictive_var.dtype),
          )
          predictive_covar = DiagLinearOperator(predictive_var)
  
          # Also compute and cache the KL divergence
          if not hasattr(self, "_memoize_cache"):
              self._memoize_cache = dict()
          self._memoize_cache["kl"] = kl_div
  
      else:
          # Compute interpolated mean term
          interp_mean = torch.matmul(
              interp_term.transpose(-1, -2),
              (
                  inducing_values - self.prior_distribution.mean
              ).unsqueeze(-1),
          ).squeeze(-1)
  
          # Compute the covariance of q(f)
          middle_term = (
              self.prior_distribution.lazy_covariance_matrix.mul(-1)
          )
          if variational_inducing_covar is not None:
              middle_term = SumLinearOperator(
                  variational_inducing_covar, middle_term
              )
          predictive_covar = SumLinearOperator(
              data_data_covar.add_jitter(self.jitter_val),
              MatmulLinearOperator(
                  interp_term.transpose(-1, -2),
                  middle_term @ interp_term,
              ),
          )
  
      # Compute the mean of q(f)
      # k_XZ K_ZZ^{-1/2} (m - K_ZZ^{-1/2} \mu_Z) + \mu_X
      predictive_mean = interp_mean + test_mean
  
      # Return the distribution
      return MultivariateNormal(predictive_mean, predictive_covar)
  
imports: from typing import Optional, Tuple
  import torch
  from linear_operator import to_linear_operator
  from linear_operator.operators import DiagLinearOperator, LinearOperator, MatmulLinearOperator, SumLinearOperator
  from linear_operator.utils import linear_cg
  from torch import Tensor
  from torch.autograd.function import FunctionCtx
  from ..None import settings
  from ..distributions import Delta, Distribution, MultivariateNormal
  from ..module import Module
  from ..utils.memoize import cached
  from ._variational_strategy import _VariationalStrategy
  from .natural_variational_distribution import NaturalVariationalDistribution
label_desc: Use image node_id 4 for calling the CiqVariationalStrategy obj's underlying member method code with example usage: obj.forward(x, inducing_points, inducing_values, variational_inducing_covar) and returns: MultivariateNormal