class_docstr: Computes a covariance matrix based on the Spectral Mixture Kernel
  between inputs :math:`\mathbf{x_1}` and :math:`\mathbf{x_2}`.
  
  It was proposed in `Gaussian Process Kernels for Pattern Discovery and Extrapolation`_.
  
  .. note::
      Unlike other kernels,
  
          * ard_num_dims **must equal** the number of dimensions of the data.
          * This kernel should not be combined with a :class:`gpytorch.kernels.ScaleKernel`.
  
  :param int num_mixtures: The number of components in the mixture.
  :param int ard_num_dims: Set this to match the dimensionality of the input.
      It should be `d` if x1 is a `... x n x d` matrix. (Default: `1`.)
  :param batch_shape: Set this if the data is batch of input data. It should
      be `b_1 x ... x b_j` if x1 is a `b_1 x ... x b_j x n x d` tensor. (Default: `torch.Size([])`.)
  :type batch_shape: torch.Size, optional
  :param active_dims: Set this if you want to compute the covariance of only
      a few input dimensions. The ints corresponds to the indices of the dimensions. (Default: `None`.)
  :type active_dims: float, optional
  :param eps: The minimum value that the lengthscale can take (prevents divide by zero errors). (Default: `1e-6`.)
  :type eps: float, optional
  
  :param mixture_scales_prior: A prior to set on the mixture_scales parameter
  :type mixture_scales_prior: ~gpytorch.priors.Prior, optional
  :param mixture_scales_constraint: A constraint to set on the mixture_scales parameter
  :type mixture_scales_constraint: ~gpytorch.constraints.Interval, optional
  :param mixture_means_prior: A prior to set on the mixture_means parameter
  :type mixture_means_prior: ~gpytorch.priors.Prior, optional
  :param mixture_means_constraint: A constraint to set on the mixture_means parameter
  :type mixture_means_constraint: ~gpytorch.constraints.Interval, optional
  :param mixture_weights_prior: A prior to set on the mixture_weights parameter
  :type mixture_weights_prior: ~gpytorch.priors.Prior, optional
  :param mixture_weights_constraint: A constraint to set on the mixture_weights parameter
  :type mixture_weights_constraint: ~gpytorch.constraints.Interval, optional
  
  :ivar torch.Tensor mixture_scales: The lengthscale parameter. Given
      `k` mixture components, and `... x n x d` data, this will be of size `... x k x 1 x d`.
  :ivar torch.Tensor mixture_means: The mixture mean parameters (`... x k x 1 x d`).
  :ivar torch.Tensor mixture_weights: The mixture weight parameters (`... x k`).
  
  Example:
  
      >>> # Non-batch
      >>> x = torch.randn(10, 5)
      >>> covar_module = gpytorch.kernels.SpectralMixtureKernel(num_mixtures=4, ard_num_dims=5)
      >>> covar = covar_module(x)  # Output: LazyVariable of size (10 x 10)
      >>>
      >>> # Batch
      >>> batch_x = torch.randn(2, 10, 5)
      >>> covar_module = gpytorch.kernels.SpectralMixtureKernel(num_mixtures=4, batch_size=2, ard_num_dims=5)
      >>> covar = covar_module(x)  # Output: LazyVariable of size (10 x 10)
  
  .. _Gaussian Process Kernels for Pattern Discovery and Extrapolation:
      https://arxiv.org/pdf/1302.4245.pdf
code: def forward(
      self,
      x1: torch.Tensor,
      x2: torch.Tensor,
      diag: bool = False,
      last_dim_is_batch: bool = False,
      **params,
  ) -> Tuple[torch.Tensor, torch.Tensor]:
      n, num_dims = x1.shape[-2:]
  
      if not num_dims == self.ard_num_dims:
          raise RuntimeError(
              "The SpectralMixtureKernel expected the input to have {} dimensionality "
              "(based on the ard_num_dims argument). Got {}.".format(
                  self.ard_num_dims, num_dims
              )
          )
  
      # Expand x1 and x2 to account for the number of mixtures
      # Should make x1/x2 (... x k x n x d) for k mixtures
      x1_ = x1.unsqueeze(-3)
      x2_ = x2.unsqueeze(-3)
  
      # Compute distances - scaled by appropriate parameters
      x1_exp = x1_ * self.mixture_scales
      x2_exp = x2_ * self.mixture_scales
      x1_cos = x1_ * self.mixture_means
      x2_cos = x2_ * self.mixture_means
  
      # Create grids
      x1_exp_, x2_exp_ = self._create_input_grid(
          x1_exp, x2_exp, diag=diag, **params
      )
      x1_cos_, x2_cos_ = self._create_input_grid(
          x1_cos, x2_cos, diag=diag, **params
      )
  
      # Compute the exponential and cosine terms
      exp_term = (x1_exp_ - x2_exp_).pow_(2).mul_(-2 * math.pi**2)
      cos_term = (x1_cos_ - x2_cos_).mul_(2 * math.pi)
      res = exp_term.exp_() * cos_term.cos_()
  
      # Sum over mixtures
      mixture_weights = self.mixture_weights.view(
          *self.mixture_weights.shape, 1, 1
      )
      if not diag:
          mixture_weights = mixture_weights.unsqueeze(-2)
  
      res = (res * mixture_weights).sum(-3 if diag else -4)
  
      # Product over dimensions
      if last_dim_is_batch:
          # Put feature-dimension in front of data1/data2 dimensions
          res = res.permute(*list(range(0, res.dim() - 3)), -1, -3, -2)
      else:
          res = res.prod(-1)
  
      return res
  
imports: import logging
  import math
  from typing import Optional, Tuple, Union
  import torch
  from ..constraints import Interval, Positive
  from ..priors import Prior
  from .kernel import Kernel
label_desc: Use image node_id 14 for calling the SpectralMixtureKernel obj's underlying member method code with example usage: obj.forward(x1, x2, diag, last_dim_is_batch) and returns: res