Exemplo n.º 1
0
def construct_implication_graph(cnf: str) -> DirectedGraph:
    cnf = Path(cnf)
    assert cnf.is_file()

    formula: List[Clause] = parse_cnf_to_list(cnf)

    implication_graph = DirectedGraph()
    for clause in formula:
        vertex_a, vertex_b = clause
        implication_graph.add_edge(-vertex_a, vertex_b)
        implication_graph.add_edge(-vertex_b, vertex_a)

    return implication_graph
Exemplo n.º 2
0
    def assign(graph: DirectedGraph, scc_assignment: dict, vertex, root):
        if vertex not in scc_assignment:
            # assign vertex to the root of a strongly_connected_component
            scc_assignment[vertex] = root

            for in_neighbor in graph.in_neighbors(vertex):
                assign(graph, scc_assignment, in_neighbor, root)
Exemplo n.º 3
0
def get_strongly_connected_components(
        graph: DirectedGraph) -> Dict[Vertex, Vertex]:
    """
    Kosaraju's algorithm, time complexity: O(n)

    Args:
        graph: A DirectedGraph object

    Returns:
        a dictionary that maps each vertex to the root vertex of its strongly connected component
    """
    # key: vertex, value: the root vertex of a strongly connected component
    scc_assignment: Dict[Vertex, Vertex] = {}
    visited = []
    L: List = []

    if not isinstance(graph, DirectedGraph):
        return

    def visit(graph: DirectedGraph, vertex, visited, L):
        if vertex not in visited:
            visited.append(vertex)
            for i in graph.out_neighbors(vertex):
                visit(graph, i, visited, L)
            L.insert(0, vertex)

    def assign(graph: DirectedGraph, scc_assignment: dict, vertex, root):
        if vertex not in scc_assignment:
            # assign vertex to the root of a strongly_connected_component
            scc_assignment[vertex] = root

            for in_neighbor in graph.in_neighbors(vertex):
                assign(graph, scc_assignment, in_neighbor, root)

    # O(n), n = number of vertices
    for vertex in graph.all_vertices():
        visit(graph, vertex, visited, L)

    # O(n), n = number of vertices
    for vertex in L:
        assign(graph, scc_assignment, vertex, vertex)

    return scc_assignment
Exemplo n.º 4
0
def explore(g, v, visited, pre, post):
    global clock
    pre[v] = clock
    clock += 1
    visited[v] = True
    for w in g.adj[v]:
        if not visited[w]:
            explore(g, w, visited, pre, post)
    post[v] = clock
    clock += 1


if __name__ == "__main__":
    from dgraph import DirectedGraph
    g = DirectedGraph(7)

    g.add_edge(0, 5)
    g.add_edge(0, 2)
    g.add_edge(0, 1)
    g.add_edge(3, 6)
    g.add_edge(3, 5)
    g.add_edge(3, 4)
    g.add_edge(5, 2)
    g.add_edge(6, 4)
    g.add_edge(6, 0)
    g.add_edge(3, 2)
    g.add_edge(1, 4)

    print(connected_comp_dfs(g))
Exemplo n.º 5
0
def solve(cnf: str) -> List[bool]:
    """
    Linear-time 2-SAT Solver

    Args:
        cnf: a filepath that leads to a 2-SAT CNF file

    Returns:
        None if CNF is not satisfiable, or
        A satisfiable solution represented by boolean truth assignment in sequence [x1, x2, x3, ..., xn].
    """
    # parsing CNF file costs O(n)
    # constructing implication graph costs O(n)
    graph: DirectedGraph = construct_implication_graph(cnf)
    scc_assignment: Dict[Vertex,
                         Vertex] = get_strongly_connected_components(graph)
    scc: Dict[Vertex, StronglyConnectedComponent] = {}

    # Checking if satisfiable costs O(n)
    for vertex in scc_assignment.keys():
        root = scc_assignment[vertex]
        if root in scc:
            scc[root].append(vertex)
        else:
            scc[root] = [vertex]

        if -vertex in scc[root]:
            print("UNSATISFIABLE")
            return

    # At this point, we know that the 2-SAT is satisfiable
    print("SATISFIABLE")

    # To find a satisfiable truth assignment
    # Building condensation graph based on strongly connected components
    # costs O(n)
    condensed_graph = DirectedGraph()
    for vertex in scc_assignment.keys():
        self_root = scc_assignment[vertex]
        for out_neighbor in graph.out_neighbors(vertex):
            out_root = scc_assignment[out_neighbor]
            if self_root == out_root:
                continue
            condensed_graph.add_edge(self_root, out_root)

    # Topologically sorting costs O(n)
    reversed_topological_order: List = condensed_graph.topological_sort()[::-1]

    # Create truth assignment costs O(n)
    truth_assignment = {}
    for root in reversed_topological_order:
        if root not in truth_assignment:
            for literal in scc[root]:
                truth_assignment[literal] = 1
                truth_assignment[-literal] = 0

    # Construct solution in order costs O(n)
    solution = [
        truth_assignment[i] for i in sorted(list(truth_assignment.keys()))
        if i > 0
    ]

    # print(solution)
    return solution
Exemplo n.º 6
0
 def visit(graph: DirectedGraph, vertex, visited, L):
     if vertex not in visited:
         visited.append(vertex)
         for i in graph.out_neighbors(vertex):
             visit(graph, i, visited, L)
         L.insert(0, vertex)
Exemplo n.º 7
0
if __name__ == "__main__":
    from dgraph import DirectedGraph
    from directed_dfs import connected_comp_dfs
    from dfs import connected_comp_dfs_order
    g = DirectedGraph(13)

    g.add_edge(0, 1)
    g.add_edge(0, 5)
    g.add_edge(2, 0)
    g.add_edge(2, 3)
    g.add_edge(3, 2)
    g.add_edge(3, 5)
    g.add_edge(4, 2)
    g.add_edge(4, 3)
    g.add_edge(5, 4)
    g.add_edge(6, 0)
    g.add_edge(6, 4)
    g.add_edge(6, 8)
    g.add_edge(6, 9)
    g.add_edge(7, 6)
    g.add_edge(7, 9)
    g.add_edge(8, 6)
    g.add_edge(9, 10)
    g.add_edge(9, 11)
    g.add_edge(10, 12)
    g.add_edge(11, 4)
    g.add_edge(11, 12)
    g.add_edge(12, 9)

    g_r = g.reverse()
    postorder = [