code: def unpack_edges(self, edges, starts, ends):
      """Unpack the edges for a sparse hidden Markov model.
  
      This function takes in a SparseHMM object and sets of edges and adds the
      edges to the model. It is designed to allow the model to be initialized
      either through passing in the edges initially or created through the
      `add_edge` API. Doing this is slightly more complicated than in the
      DenseHMM case because we use an underlying sparse representation to store
      the edges and so cannot as easily simply modify a random element in each
      call to `add_edge`.
  
  
      Parameters
      ----------
      self: SparseHMM
              A torchegranate sparse HMM.
  
      edges: list
              A list of 3-ples that consist of the parent distribution, the child
              distribution, and the probability on that edge.
  
      starts: list, tuple, numpy.ndarray, torch.tensor
              A vector of probabilities indicating the probability of starting in
              each state. Must sum to 1.0.
  
      ends: list, tuple, numpy.ndarray, torch.tensor
              A vector of probabilities indicating the probability of ending at
              each state. Does not have to sum to 1.0.
      """
  
      self.n_edges = len(edges)
      n = len(self.distributions)
  
      self.starts = None
      if starts is not None:
          self.starts = torch.log(
              _check_parameter(
                  _cast_as_parameter(starts),
                  "starts",
                  ndim=1,
                  shape=(n,),
                  min_value=0.0,
                  max_value=1.0,
                  value_sum=1.0,
              )
          )
  
      if ends is None:
          self.ends = (
              torch.empty(n, dtype=self.dtype, device=self.device) - inf
          )
      else:
          self.ends = torch.log(
              _check_parameter(
                  _cast_as_parameter(ends),
                  "ends",
                  ndim=1,
                  shape=(n,),
                  min_value=0.0,
                  max_value=1.0,
              )
          )
  
      _edge_idx_starts = torch.empty(
          self.n_edges, dtype=torch.int64, device=self.device
      )
      _edge_idx_ends = torch.empty(
          self.n_edges, dtype=torch.int64, device=self.device
      )
      _edge_log_probs = torch.empty(
          self.n_edges, dtype=self.dtype, device=self.device
      )
  
      idx = 0
      for edge in edges:
          if not hasattr(edge, "__len__") or len(edge) != 3:
              raise ValueError("Each edge must have three elements.")
  
          ni, nj, probability = edge
          if not isinstance(ni, Distribution):
              raise ValueError("First element must be a distribution.")
  
          if not isinstance(nj, Distribution):
              raise ValueError("Second element must be a distribution.")
  
          if not isinstance(probability, float):
              raise ValueError("Third element must be a float.")
  
          if probability < 0 or probability > 1:
              raise ValueError("Third element must be between 0 and 1.")
  
          if ni is self.start:
              if self.starts is None:
                  self.starts = (
                      torch.zeros(
                          n, dtype=self.dtype, device=self.device
                      )
                      - inf
                  )
  
              j = self.distributions.index(nj)
              self.starts[j] = math.log(probability)
  
          elif nj is self.end:
              i = self.distributions.index(ni)
              self.ends[i] = math.log(probability)
  
          else:
              i = self.distributions.index(ni)
              j = self.distributions.index(nj)
  
              _edge_idx_starts[idx] = i
              _edge_idx_ends[idx] = j
              _edge_log_probs[idx] = math.log(probability)
              idx += 1
  
      self._edge_idx_starts = _cast_as_parameter(_edge_idx_starts[:idx])
      self._edge_idx_ends = _cast_as_parameter(_edge_idx_ends[:idx])
      self._edge_log_probs = _cast_as_parameter(_edge_log_probs[:idx])
      self.n_edges = idx
      self.edges = self._edge_log_probs
  
      if idx == 0:
          raise ValueError(
              "Must pass in edges to a sparse model, cannot "
              + "be uniformly initialized or it would be a dense model."
          )
  
      self._edge_keymap = {}
      for i in range(idx):
          start = self._edge_idx_starts[i].item()
          end = self._edge_idx_ends[i].item()
          self._edge_keymap[(start, end)] = i
  
      if self.starts is None:
          self.starts = torch.log(
              torch.ones(n, dtype=self.dtype, device=self.device) / n
          )
  
      self.starts = _cast_as_parameter(self.starts)
      self.ends = _cast_as_parameter(self.ends)
  
imports: import math
  import numpy
  import torch
  from .._utils import _cast_as_tensor
  from .._utils import _cast_as_parameter
  from .._utils import _update_parameter
  from .._utils import _check_parameter
  from ..distributions._distribution import Distribution
  from ._base import _BaseHMM
  from ._base import _check_inputs
label_desc: Use image node_id 1 for calling a global function with example usage: unpack_edges(edges, starts, ends) without return types