code: def _quadrature_expectation_markov(
      p: MarkovGaussian,
      obj1: ExpectationObject,
      inducing_variable1: Optional[InducingVariables],
      obj2: ExpectationObject,
      inducing_variable2: Optional[InducingVariables],
      nghp: Optional[int] = None,
  ) -> tf.Tensor:
      """
      Handling of quadrature expectations for Markov Gaussians (useful for time series)
      Fallback method for missing analytic expectations wrt Markov Gaussians
      Nota Bene: obj1 is always associated with x_n, whereas obj2 always with x_{n+1}
                 if one requires e.g. <x_{n+1} K_{x_n, Z}>_p(x_{n:n+1}), compute the
                 transpose and then transpose the result of the expectation
      """
      nghp = 40 if nghp is None else nghp
  
      # logger.warning(
      #     "Quadrature is used to calculate the expectation. This means that "
      #     "an analytical implementations is not available for the given combination."
      # )
  
      if obj2 is None:
  
          def eval_func(x: TensorType) -> tf.Tensor:
              return get_eval_func(obj1, inducing_variable1)(x)
  
          mu, cov = (
              p.mu[:-1],
              p.cov[0, :-1],
          )  # cross covariances are not needed
      elif obj1 is None:
  
          def eval_func(x: TensorType) -> tf.Tensor:
              return get_eval_func(obj2, inducing_variable2)(x)
  
          mu, cov = (
              p.mu[1:],
              p.cov[0, 1:],
          )  # cross covariances are not needed
      else:
  
          def eval_func(x: TensorType) -> tf.Tensor:
              x1 = tf.split(x, 2, 1)[0]
              x2 = tf.split(x, 2, 1)[1]
              res1 = get_eval_func(
                  obj1, inducing_variable1, np.s_[:, :, None]
              )(x1)
              res2 = get_eval_func(
                  obj2, inducing_variable2, np.s_[:, None, :]
              )(x2)
              return res1 * res2
  
          mu = tf.concat((p.mu[:-1, :], p.mu[1:, :]), 1)  # Nx2D
          cov_top = tf.concat(
              (p.cov[0, :-1, :, :], p.cov[1, :-1, :, :]), 2
          )  # NxDx2D
          cov_bottom = tf.concat(
              (
                  tf.linalg.adjoint(p.cov[1, :-1, :, :]),
                  p.cov[0, 1:, :, :],
              ),
              2,
          )
          cov = tf.concat((cov_top, cov_bottom), 1)  # Nx2Dx2D
  
      return mvnquad(eval_func, mu, cov, nghp)
  
file_path: GPflow/gpflow/expectations/quadratures.py
imports: from typing import Any, Callable, Optional, Type, Union, cast
  import numpy
  import tensorflow
  from check_shapes import check_shapes
  from ..None import kernels
  from ..None import mean_functions
  from ..base import TensorType
  from ..covariances import Kuf
  from ..inducing_variables import InducingVariables
  from ..probability_distributions import DiagonalGaussian, Gaussian, MarkovGaussian
  from ..quadrature import mvnquad
  from .None import dispatch
  from .expectations import ExpectationObject, PackedExpectationObject, quadrature_expectation
label_desc: Use image node_id 3 for calling a global function with example usage: _quadrature_expectation_markov(p, obj1, inducing_variable1, obj2, inducing_variable2, nghp) and returns: mvnquad, get_eval_func, get_eval_func, unknown