We have to compute the new upper boundary (nub) starting from the
        root nodes.  If a path exists between a root node and another entry in
        `self.upper` we can ignore the root node because it has been
        specialized by one of its successors.

def compute_upper_bound(self):
        """
        We have to compute the new upper boundary (nub) starting from the
        root nodes.  If a path exists between a root node and another entry in
        `self.upper` we can ignore the root node because it has been
        specialized by one of its successors.
        """
        nub = set()
        for root in self.roots - self.upper:
            found = False
            for up in self.upper - self.roots:
                domain = self.get_domain()
                if has_path(domain, root, up):
                    found = True
                    break
            if not found:
                nub.add(root)
        return nub | (self.upper - self.roots)