class_docstr: Declarative representation of a finitary sum-product operation.
  
  After normalization via the :func:`~funsor.terms.normalize` interpretation
  contractions will canonically order their terms by type::
  
      Delta, Number, Tensor, Gaussian
class_docstr_tok: ["Declarative","representation","of","a","finitary","sum-product","operation",".","After","normalization","via","the",":","func",":","`","~funsor.terms.normalize","`","interpretation","contractions","will","canonically","order","their","terms","by","type",":",":","Delta",",","Number",",","Tensor",",","Gaussian"]
code: def _sample(self, sampled_vars, sample_inputs, rng_key):
      sampled_vars = sampled_vars.intersection(self.inputs)
      if not sampled_vars:
          return self
      for term in self.terms:
          if isinstance(term, Delta):
              sampled_vars -= term.fresh
      if not sampled_vars:
          return self
  
      if self.red_op in (ops.null, ops.logaddexp):
          if rng_key is not None and get_backend() == "jax":
              import jax
  
              rng_keys = jax.random.split(rng_key, len(self.terms))
          else:
              rng_keys = [None] * len(self.terms)
  
          if self.bin_op in (ops.null, ops.logaddexp):
              # Design choice: we sample over logaddexp reductions, but leave
              # logaddexp binary choices symbolic.
              terms = [
                  term._sample(
                      sampled_vars.intersection(term.inputs),
                      sample_inputs,
                      rng_key,
                  )
                  for term, rng_key in zip(self.terms, rng_keys)
              ]
              return Contraction(
                  self.red_op, self.bin_op, self.reduced_vars, *terms
              )
  
          if self.bin_op is ops.add:
              # Sample variables greedily in order of the terms in which they appear.
              for term in self.terms:
                  greedy_vars = sampled_vars.intersection(term.inputs)
                  if greedy_vars:
                      break
              assert greedy_vars
              greedy_terms, terms = [], []
              for term in self.terms:
                  if greedy_vars.isdisjoint(term.inputs):
                      terms.append(term)
                  elif isinstance(
                      term, Delta
                  ) and greedy_vars.isdisjoint(term.fresh):
                      terms.append(term)
                  else:
                      greedy_terms.append(term)
              if len(greedy_terms) == 1:
                  term = greedy_terms[0]
                  terms.append(
                      term._sample(
                          greedy_vars, sample_inputs, rng_keys[0]
                      )
                  )
                  result = Contraction(
                      self.red_op,
                      self.bin_op,
                      self.reduced_vars,
                      *terms,
                  )
              elif (
                  len(greedy_terms) == 2
                  and isinstance(greedy_terms[0], Tensor)
                  and isinstance(greedy_terms[1], Gaussian)
              ):
                  discrete, gaussian = greedy_terms
                  term = discrete + gaussian.log_normalizer
                  terms.append(gaussian)
                  terms.append(-gaussian.log_normalizer)
                  terms.append(
                      term._sample(
                          greedy_vars, sample_inputs, rng_keys[0]
                      )
                  )
                  result = Contraction(
                      self.red_op,
                      self.bin_op,
                      self.reduced_vars,
                      *terms,
                  )
              elif any(
                  isinstance(term, funsor.distribution.Distribution)
                  and not greedy_vars.isdisjoint(term.value.inputs)
                  for term in greedy_terms
              ):
                  sampled_terms = [
                      term._sample(
                          greedy_vars.intersection(term.value.inputs),
                          sample_inputs,
                          rng_key,
                      )
                      for term, rng_key in zip(greedy_terms, rng_keys)
                      if isinstance(
                          term, funsor.distribution.Distribution
                      )
                      and not greedy_vars.isdisjoint(term.value.inputs)
                  ]
                  result = Contraction(
                      self.red_op,
                      self.bin_op,
                      self.reduced_vars,
                      *(terms + sampled_terms),
                  )
              else:
                  raise NotImplementedError(
                      "Unhandled case: {}".format(
                          ", ".join(str(type(t)) for t in greedy_terms)
                      )
                  )
              return result._sample(
                  sampled_vars - greedy_vars, sample_inputs, rng_keys[1]
              )
  
      raise TypeError(
          "Cannot sample through ops ({}, {})".format(
              self.red_op, self.bin_op
          )
      )
  
imports: import functools
  import itertools
  from collections import Counter, OrderedDict, defaultdict
  from functools import reduce
  from typing import Tuple, Union
  import opt_einsum
  import funsor
  import funsor.ops
  from funsor.affine import affine_inputs
  from funsor.delta import Delta
  from funsor.domains import find_domain
  from funsor.gaussian import Gaussian
  from funsor.interpretations import eager, normalize, reflect
  from funsor.interpreter import children
  from funsor.ops import DISTRIBUTIVE_OPS, AssociativeOp, NullOp
  from funsor.tensor import Tensor
  from funsor.terms import _INFIX, Align, Binary, Funsor, Number, Reduce, Subs, Unary, Variable, to_funsor
  from funsor.typing import Variadic
  from funsor.util import broadcast_shape, get_backend, quote
label_desc: Use image node_id 4 for calling the Contraction obj's underlying member method code with example usage: obj._sample(sampled_vars, sample_inputs, rng_key) and returns: self, self, Contraction, result