def gen_m_graph(q: ConjunctiveQuery) -> nx.DiGraph: """ Computes the M-graph of a given ConjunctiveQuery q. :param q: A ConjunctiveQuery :return: Generated M-Graph """ atoms = q.get_atoms() g = nx.DiGraph() for atom1 in atoms: g.add_node(atom1) closure = transitive_closure(set(atom1.variables()), q.get_consistent_fd()) for atom2 in [atom for atom in atoms if atom != atom1]: if set(q.get_key_vars(atom2)).issubset(closure): g.add_edge(atom1, atom2) return g
def find_bad_internal_fd( q: ConjunctiveQuery) -> FrozenSet[FunctionalDependency]: """ Given a non saturated ConjunctiveQuery, returns the FunctionalDependencies culprit of it's non saturation. :param q: A ConjunctiveQuery. :return: A FrozenSet containing the FunctionalDependencies culprit of the non saturation of q. """ res = frozenset() possible_starts = [] possible_ends = [] for fd in q.get_all_fd().set: if fd.left not in possible_starts: possible_starts.append(fd.left) if fd.right not in possible_ends: possible_ends.append(fd.right) for left in possible_starts: for right in possible_ends: test_fd = FunctionalDependency(left, right) if fd_is_internal(test_fd, q): if right not in transitive_closure(set(left), q.get_consistent_fd()): res = res.union(frozenset([test_fd])) return res