docs: def _expectation(p, mean, none, kern, feat, nghp=None):
      """
      Compute the expectation:
      expectation[n] = <x_n K_{x_n, Z}>_p(x_n)
          - K_{.,.} :: RBF kernel
  
      :return: NxDxM
      """
      Xmu, Xcov = p.mu, p.cov
  
      with tf.control_dependencies([tf.assert_equal(
              tf.shape(Xmu)[1], tf.constant(kern.input_dim, settings.int_type),
              message="Currently cannot handle slicing in exKxz.")]):
          Xmu = tf.identity(Xmu)
  
      with params_as_tensors_for(kern, feat):
          D = tf.shape(Xmu)[1]
          lengthscales = kern.lengthscales if kern.ARD \
              else tf.zeros((D,), dtype=settings.float_type) + kern.lengthscales
  
          chol_L_plus_Xcov = tf.cholesky(tf.matrix_diag(lengthscales ** 2) + Xcov)  # NxDxD
          all_diffs = tf.transpose(feat.Z) - tf.expand_dims(Xmu, 2)  # NxDxM
  
          sqrt_det_L = tf.reduce_prod(lengthscales)
          sqrt_det_L_plus_Xcov = tf.exp(tf.reduce_sum(tf.log(tf.matrix_diag_part(chol_L_plus_Xcov)), axis=1))
          determinants = sqrt_det_L / sqrt_det_L_plus_Xcov  # N
  
          exponent_mahalanobis = tf.cholesky_solve(chol_L_plus_Xcov, all_diffs)  # NxDxM
          non_exponent_term = tf.matmul(Xcov, exponent_mahalanobis, transpose_a=True)
          non_exponent_term = tf.expand_dims(Xmu, 2) + non_exponent_term  # NxDxM
  
          exponent_mahalanobis = tf.reduce_sum(all_diffs * exponent_mahalanobis, 1)  # NxM
          exponent_mahalanobis = tf.exp(-0.5 * exponent_mahalanobis)  # NxM
  
          return kern.variance * (determinants[:, None] * exponent_mahalanobis)[:, None, :] * non_exponent_term