code: def _vi_ape(
      model,
      design,
      observation_labels,
      target_labels,
      vi_parameters,
      is_parameters,
      y_dist=None,
  ):
      svi_num_steps = vi_parameters.pop("num_steps")
  
      def posterior_entropy(y_dist, design):
          # Important that y_dist is sampled *within* the function
          y = pyro.sample("conditioning_y", y_dist)
          y_dict = {
              label: y[i, ...]
              for i, label in enumerate(observation_labels)
          }
          conditioned_model = pyro.condition(model, data=y_dict)
          svi = SVI(conditioned_model, **vi_parameters)
          with poutine.block():
              for _ in range(svi_num_steps):
                  svi.step(design)
          # Recover the entropy
          with poutine.block():
              guide = vi_parameters["guide"]
              entropy = mean_field_entropy(
                  guide, [design], whitelist=target_labels
              )
          return entropy
  
      if y_dist is None:
          y_dist = EmpiricalMarginal(
              Importance(model, **is_parameters).run(design),
              sites=observation_labels,
          )
  
      # Calculate the expected posterior entropy under this distn of y
      loss_dist = EmpiricalMarginal(
          Search(posterior_entropy).run(y_dist, design)
      )
      loss = loss_dist.mean
  
      return loss
  
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 5 for calling a global function with example usage: _vi_ape(model, design, observation_labels, target_labels, vi_parameters, is_parameters, y_dist) and returns: loss, entropy