code: def _vnmc_eig_loss(model, guide, observation_labels, target_labels):
      """VNMC loss: to evaluate directly use `vnmc_eig` setting `num_steps=0`."""
  
      def loss_fn(design, num_particles, evaluation=False, **kwargs):
          N, M = num_particles
          expanded_design = lexpand(design, N)
  
          # Sample from p(y, theta | d)
          trace = poutine.trace(model).get_trace(expanded_design)
          y_dict = {
              l: lexpand(trace.nodes[l]["value"], M)
              for l in observation_labels
          }
  
          # Sample M times from q(theta | y, d) for each y
          reexpanded_design = lexpand(expanded_design, M)
          conditional_guide = pyro.condition(guide, data=y_dict)
          guide_trace = poutine.trace(conditional_guide).get_trace(
              y_dict,
              reexpanded_design,
              observation_labels,
              target_labels,
          )
          theta_y_dict = {
              l: guide_trace.nodes[l]["value"] for l in target_labels
          }
          theta_y_dict.update(y_dict)
          guide_trace.compute_log_prob()
  
          # Re-run that through the model to compute the joint
          modelp = pyro.condition(model, data=theta_y_dict)
          model_trace = poutine.trace(modelp).get_trace(
              reexpanded_design
          )
          model_trace.compute_log_prob()
  
          terms = -sum(
              guide_trace.nodes[l]["log_prob"] for l in target_labels
          )
          terms += sum(
              model_trace.nodes[l]["log_prob"] for l in target_labels
          )
          terms += sum(
              model_trace.nodes[l]["log_prob"]
              for l in observation_labels
          )
          terms = -terms.logsumexp(0) + math.log(M)
  
          # At eval time, add p(y | theta, d) terms
          if evaluation:
              trace.compute_log_prob()
              terms += sum(
                  trace.nodes[l]["log_prob"] for l in observation_labels
              )
  
          return _safe_mean_terms(terms)
  
      return loss_fn
  
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 21 for calling a global function with example usage: _vnmc_eig_loss(model, guide, observation_labels, target_labels) and returns: loss_fn, _safe_mean_terms