class_docstr: An implementation of the DiCE operator compatible with Pyro features.
  
  This implementation correctly handles:
  - scaled log-probability due to subsampling
  - independence in different ordinals due to plate
  - weights due to parallel and sequential enumeration
  - weights due to local multiple sampling
  
  This assumes restricted dependency structure on the model and guide:
  variables outside of an :class:`~pyro.plate` can never depend on
  variables inside that :class:`~pyro.plate`.
  
  References:
  [1] Jakob Foerster, Greg Farquhar, Maruan Al-Shedivat, Tim Rocktaeschel,
      Eric P. Xing, Shimon Whiteson (2018)
      "DiCE: The Infinitely Differentiable Monte-Carlo Estimator"
      https://arxiv.org/abs/1802.05098
  [2] Laurence Aitchison (2018)
      "Tensor Monte Carlo: particle methods for the GPU era"
      https://arxiv.org/abs/1806.08593
  
  :param pyro.poutine.trace.Trace guide_trace: A guide trace.
  :param ordering: A dictionary mapping model site names to ordinal values.
      Ordinal values may be any type that is (1) ``<=`` comparable and (2)
      hashable; the canonical ordinal is a ``frozenset`` of site names.
docstr: Returns a differentiable expected cost, summing over costs at given ordinals.
  
  :param dict costs: A dict mapping ordinals to lists of cost tensors
  :returns: a scalar expected cost
  :rtype: torch.Tensor or float
docstr_tok: ["Returns","a","differentiable","expected","cost",",","summing","over","costs","at","given","ordinals",".",":","param","dict","costs",":","A","dict","mapping","ordinals","to","lists","of","cost","tensors",":","returns",":","a","scalar","expected","cost",":","rtype",":","torch.Tensor","or","float"]
code: def compute_expectation(self, costs):
      """
      Returns a differentiable expected cost, summing over costs at given ordinals.
  
      :param dict costs: A dict mapping ordinals to lists of cost tensors
      :returns: a scalar expected cost
      :rtype: torch.Tensor or float
      """
      # Share computation across all cost terms.
      with shared_intermediates() as cache:
          ring = MarginalRing(cache=cache)
          expected_cost = 0.0
          for ordinal, cost_terms in costs.items():
              log_factors = self._get_log_factors(ordinal)
              scale = math.exp(
                  sum(
                      x
                      for x in log_factors
                      if not isinstance(x, torch.Tensor)
                  )
              )
              log_factors = [
                  x for x in log_factors if isinstance(x, torch.Tensor)
              ]
  
              # Collect log_prob terms to query for marginal probability.
              queries = {
                  frozenset(cost._pyro_dims): None
                  for cost in cost_terms
              }
              for log_factor in log_factors:
                  key = frozenset(log_factor._pyro_dims)
                  if queries.get(key, False) is None:
                      queries[key] = log_factor
              # Ensure a query exists for each cost term.
              for cost in cost_terms:
                  key = frozenset(cost._pyro_dims)
                  if queries[key] is None:
                      query = torch.zeros_like(cost)
                      query._pyro_dims = cost._pyro_dims
                      log_factors.append(query)
                      queries[key] = query
  
              # Perform sum-product contraction. Note that plates never need to be
              # product-contracted due to our plate-based dependency ordering.
              sum_dims = (
                  set().union(*(x._pyro_dims for x in log_factors))
                  - ordinal
              )
              for query in queries.values():
                  require_backward(query)
              root = ring.sumproduct(log_factors, sum_dims)
              root._pyro_backward()
              probs = {
                  key: query._pyro_backward_result.exp()
                  for key, query in queries.items()
              }
  
              # Aggregate prob * cost terms.
              for cost in cost_terms:
                  key = frozenset(cost._pyro_dims)
                  prob = probs[key]
                  prob._pyro_dims = queries[key]._pyro_dims
                  mask = prob > 0
                  if torch._C._get_tracing_state() or not mask.all():
                      mask._pyro_dims = prob._pyro_dims
                      cost, prob, mask = packed.broadcast_all(
                          cost, prob, mask
                      )
                      prob = prob.masked_select(mask)
                      cost = cost.masked_select(mask)
                  else:
                      cost, prob = packed.broadcast_all(cost, prob)
                  expected_cost = expected_cost + scale * _fulldot(
                      prob, cost
                  )
  
      LAST_CACHE_SIZE[0] = count_cached_ops(cache)
      return expected_cost
  
imports: import math
  import numbers
  from collections import Counter, defaultdict
  from contextlib import contextmanager
  import torch
  from opt_einsum import shared_intermediates
  from opt_einsum.sharing import count_cached_ops
  from pyro.distributions.util import is_identically_zero
  from pyro.ops import packed
  from pyro.ops.einsum.adjoint import require_backward
  from pyro.ops.rings import MarginalRing
  from pyro.poutine.util import site_is_subsample
  from ..None import settings
label_desc: Use image node_id 3 for calling the Dice obj's underlying member method code with example usage: obj.compute_expectation(costs) and returns: expected_cost