code: def marginal_likelihood_eig(
      model,
      design,
      observation_labels,
      target_labels,
      num_samples,
      num_steps,
      marginal_guide,
      cond_guide,
      optim,
      return_history=False,
      final_design=None,
      final_num_samples=None,
  ):
      """Estimates EIG by estimating the marginal entropy, that of :math:`p(y|d)`,
      *and* the conditional entropy, of :math:`p(y|\\theta, d)`, both via Gibbs' Inequality. See [1] for full details.
  
      [1] Foster, Adam, et al. "Variational Bayesian Optimal Experimental Design." arXiv preprint arXiv:1903.05480 (2019).
  
      :param function model: A pyro model accepting `design` as only argument.
      :param torch.Tensor design: Tensor representation of design
      :param list observation_labels: A subset of the sample sites
          present in `model`. These sites are regarded as future observations
          and other sites are regarded as latent variables over which a
          posterior is to be inferred.
      :param list target_labels: A subset of the sample sites over which the posterior
          entropy is to be measured.
      :param int num_samples: Number of samples per iteration.
      :param int num_steps: Number of optimization steps.
      :param function marginal_guide: guide family for use in the marginal estimation.
          The parameters of `guide` are optimised to maximise the log-likelihood objective.
      :param function cond_guide: guide family for use in the likelihood (conditional) estimation.
          The parameters of `guide` are optimised to maximise the log-likelihood objective.
      :param pyro.optim.Optim optim: Optimiser to use.
      :param bool return_history: If `True`, also returns a tensor giving the loss function
          at each step of the optimization.
      :param torch.Tensor final_design: The final design tensor to evaluate at. If `None`, uses
          `design`.
      :param int final_num_samples: The number of samples to use at the final evaluation, If `None,
          uses `num_samples`.
      :return: EIG estimate, optionally includes full optimization history
      :rtype: torch.Tensor or tuple
      """
  
      if isinstance(observation_labels, str):
          observation_labels = [observation_labels]
      if isinstance(target_labels, str):
          target_labels = [target_labels]
      loss = _marginal_likelihood_loss(
          model,
          marginal_guide,
          cond_guide,
          observation_labels,
          target_labels,
      )
      return opt_eig_ape_loss(
          design,
          loss,
          num_samples,
          num_steps,
          optim,
          return_history,
          final_design,
          final_num_samples,
      )
  
imports: import math
  import warnings
  import torch
  import pyro
  from pyro import poutine
  from pyro.contrib.oed.search import Search
  from pyro.contrib.util import lexpand
  from pyro.infer import SVI, EmpiricalMarginal, Importance
  from pyro.infer.autoguide.utils import mean_field_entropy
  from pyro.util import torch_isinf, torch_isnan
label_desc: Use image node_id 11 for calling a global function with example usage: marginal_likelihood_eig(model, design, observation_labels, target_labels, num_samples, num_steps, marginal_guide, cond_guide, optim, return_history, final_design, final_num_samples) and returns: opt_eig_ape_loss