docs: def compare_groups(true_groups, pred_groups):
      r"""
      Finds how predictions need to be modified to match the true grouping.
  
      Notes:
          pred_merges - the merges needed that would need to be done for the
              pred_groups to match true_groups.
          pred_hybrid - the hybrid split/merges needed that would need to be done
              for the pred_groups to match true_groups.
  
      Example:
          >>> # ENABLE_DOCTEST
          >>> from utool.util_alg import *  # NOQA
          >>> import utool as ut
          >>> true_groups = [
          >>>   [20, 21], [22, 23], [1, 2], [12, 13, 14], [4], [5, 6, 3], [7, 8],
          >>>   [9, 10, 11], [31, 32, 33, 34, 35],   [41, 42, 43, 44], [45], [50]
          >>> ]
          >>> pred_groups = [
          >>>     [20, 21, 22, 23], [1, 2], [12], [13, 14], [3, 4], [5, 6,11],
          >>>     [7], [8, 9], [10], [31, 32], [33, 34, 35], [41, 42, 43, 44, 45]
          >>> ]
          >>> comparisons = ut.compare_groups(true_groups, pred_groups)
          >>> print(comparisons)
          >>> result = ut.repr4(comparisons)
          >>> print(result)
          {
              'common': {{1, 2}},
              'pred_hybrid': {{10}, {3, 4}, {5, 6, 11}, {7}, {8, 9}},
              'pred_merges': [{{12}, {13, 14}}, {{31, 32}, {33, 34, 35}}],
              'pred_splits': [{20, 21, 22, 23}, {41, 42, 43, 44, 45}],
              'true_hybrid': {{3, 5, 6}, {4}, {50}, {7, 8}, {9, 10, 11}},
              'true_merges': [{12, 13, 14}, {31, 32, 33, 34, 35}],
              'true_splits': [{{20, 21}, {22, 23}}, {{41, 42, 43, 44}, {45}}],
          }
      """
      import utool as ut
      true = {frozenset(_group) for _group in true_groups}
      pred = {frozenset(_group) for _group in pred_groups}
  
      # Find the groups that are exactly the same
      common = true.intersection(pred)
  
      true_sets = true.difference(common)
      pred_sets = pred.difference(common)
  
      # connected compoment lookups
      pred_conn = {p: frozenset(ps) for ps in pred for p in ps}
      true_conn = {t: frozenset(ts) for ts in true for t in ts}
  
      # How many predictions can be merged into perfect pieces?
      # For each true sets, find if it can be made via merging pred sets
      pred_merges = []
      true_merges = []
      for ts in true_sets:
          ccs = set([pred_conn.get(t, frozenset()) for t in ts])
          if frozenset.union(*ccs) == ts:
              # This is a pure merge
              pred_merges.append(ccs)
              true_merges.append(ts)
  
      # How many predictions can be split into perfect pieces?
      true_splits = []
      pred_splits = []
      for ps in pred_sets:
          ccs = set([true_conn.get(p, frozenset()) for p in ps])
          if frozenset.union(*ccs) == ps:
              # This is a pure merge
              true_splits.append(ccs)
              pred_splits.append(ps)
  
      pred_merges_flat = ut.flatten(pred_merges)
      true_splits_flat = ut.flatten(true_splits)
  
      pred_hybrid = frozenset(map(frozenset, pred_sets)).difference(
          set(pred_splits + pred_merges_flat))
  
      true_hybrid = frozenset(map(frozenset, true_sets)).difference(
          set(true_merges + true_splits_flat))
  
      comparisons = {
          'common': common,
          # 'true_splits_flat': true_splits_flat,
          'true_splits': true_splits,
          'true_merges': true_merges,
          'true_hybrid': true_hybrid,
          'pred_splits': pred_splits,
          'pred_merges': pred_merges,
          # 'pred_merges_flat': pred_merges_flat,
          'pred_hybrid': pred_hybrid,
      }
      return comparisons