def atom_plus(atom: Atom, q: ConjunctiveQuery) -> Set[AtomValue]: """ Computes the plus q set of an atom (Read report for more information) :param atom: Atom whose plus will be computed :param q: A ConjunctiveQuery :return: The plus q set of variables of atom """ if q.is_atom_consistent(atom): return transitive_closure(set(q.get_key_vars(atom)), q.get_all_fd()) else: return transitive_closure(set(q.get_key_vars(atom)), q.get_all_fd(atom))
def all_cycles_weak(attack_graph: nx.DiGraph, q: ConjunctiveQuery) -> bool: """ Returns True if the given Attack Graph contains no strong cycle :param attack_graph: An Attack Graph :param q: A ConjunctiveQuery :return: True if attack_graph contains no strong cycle, False if not """ cycles = nx.algorithms.simple_cycles(attack_graph) for cycle in cycles: full_cycle = cycle + [cycle[0]] for i in range(0, len(cycle)): atom1 = full_cycle[i] atom2 = full_cycle[i + 1] for var in q.get_key_vars(atom2): if var not in transitive_closure(set(q.get_key_vars(atom1)), q.get_all_fd()): return False return True
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