class_docstr: Random perfect matching from ``N`` sources to ``N`` destinations where each
  source matches exactly **one** destination and each destination matches
  exactly **one** source.
  
  Samples are represented as long tensors of shape ``(N,)`` taking values in
  ``{0,...,N-1}`` and satisfying the above one-one constraint. The log
  probability of a sample ``v`` is the sum of edge logits, up to the log
  partition function ``log Z``:
  
  .. math::
  
      \log p(v) = \sum_s \text{logits}[s, v[s]] - \log Z
  
  Exact computations are expensive. To enable tractable approximations, set a
  number of belief propagation iterations via the ``bp_iters`` argument.  The
  :meth:`log_partition_function` and :meth:`log_prob` methods use a Bethe
  approximation [1,2,3,4].
  
  **References:**
  
  [1] Michael Chertkov, Lukas Kroc, Massimo Vergassola (2008)
      "Belief propagation and beyond for particle tracking"
      https://arxiv.org/pdf/0806.1199.pdf
  [2] Bert Huang, Tony Jebara (2009)
      "Approximating the Permanent with Belief Propagation"
      https://arxiv.org/pdf/0908.1769.pdf
  [3] Pascal O. Vontobel (2012)
      "The Bethe Permanent of a Non-Negative Matrix"
      https://arxiv.org/pdf/1107.4196.pdf
  [4] M Chertkov, AB Yedidia (2013)
      "Approximating the permanent with fractional belief propagation"
      http://www.jmlr.org/papers/volume14/chertkov13a/chertkov13a.pdf
  
  :param Tensor logits: An ``(N, N)``-shaped tensor of edge logits.
  :param int bp_iters: Optional number of belief propagation iterations. If
      unspecified or ``None`` expensive exact algorithms will be used.
code: def log_partition_function(self):
      if self.bp_iters is None:
          # Brute force.
          d = self.enumerate_support()
          s = torch.arange(d.size(-1), dtype=d.dtype, device=d.device)
          return self.logits[s, d].sum(-1).logsumexp(-1)
  
      # Approximate mean field beliefs b via Sinkhorn iteration.
      # We find that Sinkhorn iteration is more robust and faster than the
      # optimal belief propagation updates suggested in [1-4].
      finfo = torch.finfo(self.logits.dtype)
      # Note gradients are more accurate when shift is not detached.
      shift = self.logits.max(1, True).values
      shift.data.clamp_(min=finfo.min, max=finfo.max)
      logits = self.logits - shift
      d = logits.logsumexp(0)
      for _ in range(self.bp_iters):
          s = (logits - d).logsumexp(-1, True)
          d = (logits - s).logsumexp(0)
      b = (logits - (d + s)).exp()
  
      def log(x):
          return x.clamp(min=finfo.tiny).log()
  
      # Evaluate the Bethe free energy.
      b_ = (1 - b).clamp(min=0)
      logits = logits.clamp(min=-1 / finfo.eps)
      free_energy = (b * (log(b) - logits)).sum() - (b_ * log(b_)).sum()
      log_Z = shift.sum() - free_energy
      assert torch.isfinite(log_Z)
      return log_Z
  
imports: import itertools
  import logging
  import warnings
  import torch
  from torch.distributions import constraints
  from torch.distributions.utils import lazy_property
  from .torch import Categorical
  from .torch_distribution import TorchDistribution
label_desc: Use image node_id 3 for calling the OneOneMatching obj's underlying member method code with example usage: obj.log_partition_function() and returns: log_Z, self, x