def f_reversible(self, example):
    """Transforms a single program to its reversible IR."""

    def get_subj_rel_to_objects(conjuncts):
      """Merges conjuncts that share the same subject and relation."""
      subj_rel_to_objects = collections.OrderedDict()
      for conjunct in conjuncts:
        if conjunct.startswith("FILTER"):  # Keep FILTER conjunts as is.
          subj_rel_to_objects[conjunct] = None
        else:
          subj, rel, obj = conjunct.split()  # A (subj, rel, obj) triple.
          if rel == "a":  # Keep unary conjuncts as is.
            subj_rel_to_objects[conjunct] = None
          else:  # Handle a binary conjunct.
            subj_rel = (subj, rel)
            if subj_rel not in subj_rel_to_objects:
              subj_rel_to_objects[subj_rel] = []
            subj_rel_to_objects[subj_rel].append(obj)
      return subj_rel_to_objects

    def get_conjuncts_reversible(
        subj_rel_to_objects,
        subj_objs_to_rels):
      """Generates conjuncts in their reversible intermediate representation."""
      conjuncts_reversible = []
      added_subj_objs = []
      for subj_rel, objects in subj_rel_to_objects.items():
        if objects is None:  # Only wrap the conjunct with parentheses.
          conjuncts_reversible.append("( {} )".format(subj_rel))
        else:
          # Prepare conjunct in the form of (s , (r_1 r_2 ...) (o_1 , o_2 ...)).
          subj, _ = subj_rel
          objects_tup = tuple(objects)
          if (objects_tup, subj) in added_subj_objs:
            # Already handled the conjuncts with this subject and objects list.
            continue
          else:
            added_subj_objs.append((objects_tup, subj))
          conjunct_reversible = "( {} ( {} ) ( {} ) )".format(
              subj, " , ".join(subj_objs_to_rels[(objects_tup, subj)]),
              " , ".join(objects))
          conjuncts_reversible.append(conjunct_reversible)
      return conjuncts_reversible

    # Trim long relations.
    program_trimmed_rel = str(example.program)
    # Sort relations in the reverse order to avoid bad replacement.
    sorted_relations = sorted(
        self.trimmed_relations.keys(),
        key=lambda x: x.count("ns:"),
        reverse=True)
    for relation in sorted_relations:
      relation_trimmed = self.trimmed_relations[relation]
      program_trimmed_rel = program_trimmed_rel.replace(relation,
                                                        relation_trimmed)

    prefix, conjuncts = self._get_program_parts(program_trimmed_rel)

    # Prepare subject-relation to objects map.
    subj_rel_to_objects = get_subj_rel_to_objects(conjuncts)

    # Prepare subject-objects to relations map.
    subj_objs_to_rels = collections.defaultdict(list)
    for subj_rel, objects in subj_rel_to_objects.items():
      if objects is not None:
        objects_tuple = tuple(objects)
        subj, rel = subj_rel
        key = (objects_tuple, subj)
        subj_objs_to_rels[key].append(rel)

    conjuncts_reversible = get_conjuncts_reversible(subj_rel_to_objects,
                                                    subj_objs_to_rels)
    reversible_ir = "{} lb {} rb".format(prefix,
                                         " . ".join(conjuncts_reversible))
    return reversible_ir
Transforms a single program to its reversible IR.
language/compir/dataset_parsers/cfq_parser.py