docs: def _encode_penman(self, g, top=None):
          """
          Walk graph g and find a spanning dag, then serialize the result.
  
          First, depth-first traversal of preferred orientations (whether
          true or inverted) to create graph p.
  
          If any triples remain, select the first remaining triple whose
          source in the dispreferred orientation exists in p, where
          'first' is determined by the order of inserted nodes (i.e. a
          topological sort). Add this triple, then repeat the depth-first
          traversal of preferred orientations from its target. Repeat
          until no triples remain, or raise an error if there are no
          candidates in the dispreferred orientation (which likely means
          the graph is disconnected).
          """
          if top is None:
              top = g.top
          remaining = set(g.triples())
          variables = g.variables()
          store = defaultdict(lambda: ([], []))  # (preferred, dispreferred)
          for t in g.triples():
              if t.inverted:
                  store[t.target][0].append(t)
                  store[t.source][1].append(Triple(*t, inverted=False))
              else:
                  store[t.source][0].append(t)
                  store[t.target][1].append(Triple(*t, inverted=True))
  
          p = defaultdict(list)
          topolist = [top]
  
          def _update(t):
              src, tgt = (t[2], t[0]) if t.inverted else (t[0], t[2])
              p[src].append(t)
              remaining.remove(t)
              if tgt in variables and t.relation != self.TYPE_REL:
                  topolist.append(tgt)
                  return tgt
              return None
  
          def _explore_preferred(src):
              ts = store.get(src, ([], []))[0]
              for t in ts:
                  if t in remaining:
                      tgt = _update(t)
                      if tgt is not None:
                          _explore_preferred(tgt)
              ts[:] = []  # clear explored list
  
          _explore_preferred(top)
  
          while remaining:
              flip_candidates = [store.get(v, ([],[]))[1] for v in topolist]
              for fc in flip_candidates:
                  fc[:] = [c for c in fc if c in remaining]  # clear superfluous
              if not any(len(fc) > 0 for fc in flip_candidates):
                  raise EncodeError('Invalid graph; possibly disconnected.')
              c = next(c for fc in flip_candidates for c in fc)
              tgt = _update(c)
              if tgt is not None:
                  _explore_preferred(tgt)
  
          return self._layout(p, top, 0, set())