def add_relation(
        self, new_rel: cl.Relation, new_dom: Domain, optimize_check_implies=True
    ):
        """
        new_rel, new_dom: the relation and domain to be added
        optimize_check_implies: bool
            If enabled, dont add relations (aka predicates) that are already
            provably true based on existing predicates. This should not be necessary for correctness:
            object addition should check if relation constraints are satisfied/implied before adding more.
            But it may (?) help speed, and the unit tests assume it is enabled for the most part
        """

        assert new_dom is not self

        lazydebug(
            logger,
            lambda: f"add_relation {new_rel} {new_dom} to existing {len(self.relations)}",
        )

        if not optimize_check_implies:
            self.relations.append((new_rel, new_dom))
            return

        covered = False

        for i, (er, ed) in enumerate(self.relations):
            if isinstance(new_rel, cl.NegatedRelation):
                continue
            elif isinstance(er, cl.NegatedRelation):
                continue
            elif reldom_implies((er, ed), (new_rel, new_dom)):
                covered = True
            elif reldom_satisfies((er, ed), (new_rel, new_dom)) or reldom_satisfies(
                (new_rel, new_dom), (er, ed)
            ):
                lazydebug(
                    logger,
                    lambda: f"Tightening existing relation {(er, ed)} with {(new_rel, new_dom)}",
                )
                self.relations[i] = reldom_intersection((new_rel, new_dom), (er, ed))
                covered = True
            elif new_dom.intersects(ed, require_satisfies_right=True):
                lazydebug(logger, lambda: f"Tightening domain {ed} with {new_dom}")
                self.relations[i] = (er, ed.intersection(new_dom))
            else:
                lazydebug(
                    logger,
                    lambda: f"{(er, ed)} is not relevant for {(new_rel, new_dom)}",
                )

        if not covered:
            lazydebug(
                logger,
                lambda: f"optimize_check_implies found nothing, adding relation {new_rel} {new_dom}",
            )
            self.relations.append((new_rel, new_dom))

        if self.is_recursive():
            raise ValueError(
                f"Encountered recursive domain after add_relation {new_rel=} {new_dom=} onto {self.tags=} {len(self.relations)=}"
            )

        new_rel, new_dom: the relation and domain to be added
        optimize_check_implies: bool
            If enabled, dont add relations (aka predicates) that are already
            provably true based on existing predicates. This should not be necessary for correctness:
            object addition should check if relation constraints are satisfied/implied before adding more.
            But it may (?) help speed, and the unit tests assume it is enabled for the most part
        
infinigen/core/constraints/reasoning/domain.py