docs: def get_hypertree_from_predecessors(H, Pv, source_node,
                                      node_weights=None, attr_name="weight"):
      """Gives the hypertree (i.e., the subhypergraph formed from the union of
      the set of paths from an execution of, e.g., the SBT algorithm) defined by
      Pv beginning at a source node. Returns a dictionary mapping each node to
      the ID of the hyperedge that preceeded it in the path (i.e., a Pv vector).
      Assigns the node weights (if provided) as attributes of the nodes (e.g.,
      the rank of that node in a specific instance of the SBT algorithm, or the
      cardinality of that node in a B-Visit traversal, etc.).
  
      :note: The IDs of the hyperedges in the subhypergraph returned may be
          different than those in the original hypergraph (even though the
          tail and head sets are identical).
  
      :param H: the hypergraph which the path algorithm was executed on.
      :param Pv: dictionary mapping each node to the ID of the hyperedge that
              preceeded it in the path.
      :param source_node: the root of the executed path algorithm.
      :param node_weights: [optional] dictionary mapping each node to some weight
                          measure.
      :param attr_name: key into the nodes' attribute dictionaries for their
                      weight values (if node_weights is provided).
      :returns: DirectedHypergraph -- subhypergraph induced by the path
              algorithm specified by the predecessor vector (Pv) from a
              source node.
      :raises: TypeError -- Algorithm only applicable to directed hypergraphs
  
      """
      if not isinstance(H, DirectedHypergraph):
          raise TypeError("Algorithm only applicable to directed hypergraphs")
  
      sub_H = DirectedHypergraph()
  
      # If node weights are not provided, simply collect all the nodes that are
      # will be in the hypertree
      if node_weights is None:
          nodes = [node for node in Pv.keys() if Pv[node] is not None]
          nodes.append(source_node)
      # If node weights are provided, collect all the nodes that will be in the
      # tree and pair them with their corresponding weights
      else:
          nodes = [(node, {attr_name: node_weights[node]})
                   for node in Pv.keys() if Pv[node] is not None]
          nodes.append((source_node, {attr_name: node_weights[source_node]}))
      # Add the collected elements to the hypergraph
      sub_H.add_nodes(nodes)
  
      # Add all hyperedges, specified by Pv, to the hypergraph
      hyperedges = [(H.get_hyperedge_tail(hyperedge_id),
                     H.get_hyperedge_head(hyperedge_id),
                     H.get_hyperedge_attributes(hyperedge_id))
                    for hyperedge_id in Pv.values() if hyperedge_id is not None]
      sub_H.add_hyperedges(hyperedges)
  
      return sub_H