class_docstr: This method approximates the Gaussian process posterior using a multivariate Gaussian.
  
  The key reference is :cite:t:`Opper:2009`.
  
  The idea is that the posterior over the function-value vector F is
  approximated by a Gaussian, and the KL divergence is minimised between
  the approximation and the posterior. It turns out that the optimal
  posterior precision shares off-diagonal elements with the prior, so
  only the diagonal elements of the precision need be adjusted.
  The posterior approximation is
  
  .. math::
  
     q(\mathbf f) = N(\mathbf f \,|\, \mathbf K \boldsymbol \alpha,
                       [\mathbf K^{-1} + \textrm{diag}(\boldsymbol \lambda))^2]^{-1})
  
  This approach has only 2ND parameters, rather than the N + N^2 of vgp,
  but the optimization is non-convex and in practice may cause difficulty.
docstr: q_alpha, q_lambda are variational parameters, size [N, R]
  This method computes the variational lower bound on the likelihood,
  which is:
  
  .. math::
  
     E_{q(F)} [ \log p(Y|F) ] - KL[ q(F) || p(F)]
  
  with
  
  .. math::
  
     q(f) = N(f |
         K \alpha + \textrm{mean},
         [K^-1 + \textrm{diag}(\textrm{square}(\lambda))]^-1) .
docstr_tok: ["q_alpha",",","q_lambda","are","variational","parameters",",","size","[","N",",","R","]","This","method","computes","the","variational","lower","bound","on","the","likelihood",",","which","is",":","..","math",":",":","E_","{","q","(","F",")","}","[","\\log","p","(","Y|F",")","]","-","KL","[","q","(","F",")","||","p","(","F",")","]","with","..","math",":",":","q","(","f",")","=","N","(","f","|","K","\\alpha","+","\\textrm","{","mean","}",",","[","K^-1","+","\\textrm","{","diag","}","(","\\textrm","{","square","}","(","\\lambda",")",")","]","^-1",")","."]
code: def elbo(self) -> tf.Tensor:
      r"""
      q_alpha, q_lambda are variational parameters, size [N, R]
      This method computes the variational lower bound on the likelihood,
      which is:
  
      .. math::
  
         E_{q(F)} [ \log p(Y|F) ] - KL[ q(F) || p(F)]
  
      with
  
      .. math::
  
         q(f) = N(f |
             K \alpha + \textrm{mean},
             [K^-1 + \textrm{diag}(\textrm{square}(\lambda))]^-1) .
      """
      X_data, Y_data = self.data
  
      K = self.kernel(X_data)
      K_alpha = tf.linalg.matmul(K, self.q_alpha)
      f_mean = K_alpha + self.mean_function(X_data)
  
      # compute the variance for each of the outputs
      I = tf.tile(
          tf.eye(self.num_data, dtype=default_float())[None, ...],
          [self.num_latent_gps, 1, 1],
      )
      A = (
          I
          + tf.transpose(self.q_lambda)[:, None, ...]
          * tf.transpose(self.q_lambda)[:, :, None, ...]
          * K
      )
      L = tf.linalg.cholesky(A)
      Li = tf.linalg.triangular_solve(L, I)
      tmp = Li / tf.transpose(self.q_lambda)[:, None, ...]
      f_var = 1.0 / tf.square(self.q_lambda) - tf.transpose(
          tf.reduce_sum(tf.square(tmp), 1)
      )
  
      # some statistics about A are used in the KL
      A_logdet = 2.0 * tf.reduce_sum(
          tf.math.log(tf.linalg.diag_part(L))
      )
      trAi = tf.reduce_sum(tf.square(Li))
  
      KL = 0.5 * (
          A_logdet
          + trAi
          - self.num_data * self.num_latent_gps
          + tf.reduce_sum(K_alpha * self.q_alpha)
      )
  
      v_exp = self.likelihood.variational_expectations(
          X_data, f_mean, f_var, Y_data
      )
      return tf.reduce_sum(v_exp) - KL
  
imports: from typing import Optional
  import numpy
  import tensorflow
  from check_shapes import check_shapes, inherit_check_shapes
  import gpflow
  from ..None import posteriors
  from ..base import InputData, MeanAndVariance, Parameter, RegressionData
  from ..conditionals import conditional
  from ..config import default_float, default_jitter
  from ..kernels import Kernel
  from ..kullback_leiblers import gauss_kl
  from ..likelihoods import Likelihood
  from ..mean_functions import MeanFunction
  from ..utilities import assert_params_false, is_variable, triangular, triangular_size
  from .model import GPModel
  from .training_mixins import InternalDataTrainingLossMixin
  from .util import data_input_to_tensor
label_desc: Use image node_id 3 for calling the VGPOpperArchambeau obj's underlying member method code with example usage: obj.elbo() and returns: unknown