def convert_to_cnf(nnf):
    """
    Convert an NNF to an NNF which is a member of CNF

    Parameters
    ----------
    nnf : Circuit
        An arbitrary NNF statement with an instantiated graph representation

    Returns
    -------
    Circuit
        An equivalent CNF reformatted Circuit statement
    """
    assert nnf.is_nnf(), "Can only convert NNF's to CNF. Please make NNF first"
    root = nnf.graph.find_dag_root()
    assert (root > -1), "No root found"

    cnf_graph = convert_to_cnf_helper(nnf, root, max(nnf.graph.nodes()))

    return circuits.Circuit(clauses=compute_clauses(cnf_graph),
                            variables=cnf_graph.get_variables(
                                cnf_graph.find_dag_root()),
                            num_edges=len(cnf_graph.edges()),
                            num_nodes=len(cnf_graph.nodes()),
                            graph_representation=cnf_graph)
Example #2
0
def parse_DNF_natural_language(dnf_text_description):
    """
    Given a DNF expression in natural language form, convert it to a DIMACS file.

    Example strings:
        - ((1 and 2) or (-3) or (-4 and 5))
        - ((1 and 2 and 3))
    CAUTION: formatting of input strings is strict.
    """
    graph_representation = circuits.DiGraph()
    idx = 1
    or_node = idx
    graph_representation.add_node(or_node, value="or", type="LOGIC_GATE")
    idx += 1

    dnf_text_description = dnf_text_description[1:-1]

    while len(dnf_text_description) > 0:
        if dnf_text_description.startswith('('):
            and_node = idx
            idx += 1
            graph_representation.add_node(and_node,
                                          value="and",
                                          type="LOGIC_GATE")
            graph_representation.add_edge(or_node, and_node)
            substring_end = dnf_text_description.find(")")
            leaves = re.findall('-?\d+', dnf_text_description[:substring_end])

            for leaf in leaves:
                graph_representation.add_node(idx,
                                              value=int(leaf),
                                              type="LEAF")
                graph_representation.add_edge(and_node, idx)
                idx += 1
            dnf_text_description = dnf_text_description[substring_end + 1:]
        if dnf_text_description.startswith(' or '):
            dnf_text_description = dnf_text_description[len(' or '):]

    clauses = [
        node for node in graph_representation.nodes()
        if graph_representation.nodes[node]['type'] == 'LOGIC_GATE'
    ]
    circuit = circuits.Circuit(
        clauses=clauses,
        variables=graph_representation.get_variables(
            graph_representation.find_dag_root()),
        num_edges=graph_representation.number_of_nodes(),
        num_nodes=graph_representation.number_of_edges(),
        graph_representation=graph_representation)

    return circuit
def convert_to_epsinv(circuit):
    """
    Convert an arbitrary logical sentence to an epsilon-inverted sentence

    Parameters
    ----------
    circuit : Circuit
        An arbitrary circuit with an instantiated graph representation

    Returns
    -------
    Circuit
        An equivalent epsilon-inverted reformatted Circuit statement
    """
    e_graph = convert_to_epsinv_helper(circuit)
    return circuits.Circuit(clauses=compute_clauses(e_graph),
                            variables=e_graph.get_variables(
                                e_graph.find_dag_root()),
                            num_edges=len(e_graph.edges()),
                            num_nodes=len(e_graph.nodes()),
                            graph_representation=e_graph)
def convert_ddnnf_to_odnf(circuit):
    """
    Given a ddnnf form, convert it to ODNF.

    Parameters
    ----------
    circuit : Circuit
        With instantiated DAG

    Returns
    -------
    circuit in ODNF form
    """
    assert circuit.is_decomposable() and circuit.is_deterministic(
    ), "Can only convert dDNNF to ODNF"
    root = circuit.graph.find_dag_root()
    odnf_graph_schema = convert_ddnnf_to_odnf_helper(circuit, root)

    odnf_graph = circuits.DiGraph()
    idx = max(circuit.graph.nodes()) + 1
    or_node = idx
    idx += 1
    odnf_graph.add_node(or_node, value="or", type="LOGIC_GATE")
    for sublist in odnf_graph_schema:
        and_idx = idx
        odnf_graph.add_node(and_idx, value="and", type="LOGIC_GATE")
        odnf_graph.add_edge(or_node, and_idx)
        idx += 1
        for element in sublist:
            odnf_graph.add_node(idx,
                                value=circuit.graph.node[element]['value'],
                                type=circuit.graph.node[element]['type'])
            odnf_graph.add_edge(and_idx, idx)
            idx += 1

    return circuits.Circuit(clauses=compute_clauses(odnf_graph),
                            variables=odnf_graph.get_variables(or_node),
                            num_edges=len(odnf_graph.edges()),
                            num_nodes=len(odnf_graph.nodes()),
                            graph_representation=odnf_graph)
def convert_nnf_to_bdd(circuit):
    """
    Using the pyeda library, converts an arbitrary circuit to BDD representation
    """
    root = circuit.graph.find_dag_root()
    variables = list(circuit.graph.get_variables(root))

    conv_string = "abcdefghij"

    # create map of variables (e.g. 1, 2) to strings (e.g. "a", "b")
    bdd_variables = {}
    # create reverse map (e.g. "a", "b" to 1, 2)
    reverse_mapping_variables_bdd = {}
    for i in range(0, len(variables)):
        number_to_string = str(variables[i])
        tmp_string = ""
        for char in number_to_string:
            tmp_string += conv_string[int(char)]

        bdd_variables[variables[i]] = bddvar(tmp_string)
        reverse_mapping_variables_bdd[tmp_string] = variables[i]

    expression = circuit.graph.collapse_graph_to_formula_promela_syntax(root)

    for variable in variables:
        expression = expression.replace(str(variable),
                                        str(bdd_variables[variable]))

    bdd = expr2bdd(expr(expression))
    bdd = bdd2expr(bdd)
    bdd = str(bdd).replace(" ", "").replace("~", "-")

    graph, _ = bdd_expr_to_dag(bdd, 0, reverse_mapping_variables_bdd)
    return circuits.Circuit(clauses=compute_clauses(graph),
                            variables=graph.get_variables(
                                graph.find_dag_root()),
                            num_edges=len(graph.edges()),
                            num_nodes=len(graph.nodes()),
                            graph_representation=graph)
def convert_odnf_to_mods(circuit):
    """
    Given an ODNF form, convert it to MODS.

    Parameters
    ----------
    circuit : Circuit
        With instantiated DAG

    Returns
    -------
    circuit in MODS form
    """
    assert circuit.is_simple_conjunction() and circuit.is_deterministic(
    ), "Can only convert ODNF to MODS"
    mods_schema = convert_odnf_to_mods_helper(circuit)

    mods_graph = circuits.DiGraph()
    idx = 0
    or_index = idx
    idx += 1
    mods_graph.add_node(or_index, value="or", type="LOGIC_GATE")
    for clause in mods_schema:
        and_index = idx
        idx += 1
        mods_graph.add_node(and_index, value="and", type="LOGIC_GATE")
        mods_graph.add_edge(or_index, and_index)
        for variable in clause:
            mods_graph.add_node(idx, value=variable, type="LEAF")
            mods_graph.add_edge(and_index, idx)
            idx += 1

    return circuits.Circuit(clauses=compute_clauses(mods_graph),
                            variables=mods_graph.get_variables(or_index),
                            num_edges=len(mods_graph.edges()),
                            num_nodes=len(mods_graph.nodes()),
                            graph_representation=mods_graph)
def tseitin(nnf):
    """
    Convert an NNF to an NNF which is a member of CNF
    using Tseitin Translation

    https://profs.info.uaic.ro/~stefan.ciobaca/logic-2018-2019/notes7.pdf


    Parameters
    ----------
    nnf : NNF
        An arbitrary NNF statement with an instantiated graph representation

    Returns
    -------
    NNF
        An equivalent CNF reformatted NNF statement
    """
    assert nnf.is_nnf(), "Can only convert NNF's to CNF. Please make NNF first"
    root = nnf.graph.find_dag_root()
    assert (root > -1), "No root found"

    idx = max(nnf.graph.nodes()) + 1

    # associate a new variable with each LOGIC_GATE node in the DAG
    for i in range(0, len(nnf.clauses)):
        nnf.graph.node[nnf.clauses[i]]['tseitin_aux_var'] = "t_" + str(idx)
        idx += 1

    cnf_graph = tseitin_helper(nnf, root, root, idx)

    return circuits.Circuit(clauses=compute_clauses(cnf_graph),
                            variables=cnf_graph.get_variables(
                                cnf_graph.find_dag_root()),
                            num_edges=len(cnf_graph.edges()),
                            num_nodes=len(cnf_graph.nodes()),
                            graph_representation=cnf_graph)
Example #8
0
def parse_NNF_DIMACS(fname):
    assert (fname.endswith(".nnf")), "File not .nnf format"
    """
    Parse a DIMACS .nnf file.

    The .nnf format is specified in the included file /FormulaFormats.pdf.
    The original source is http://reasoning.cs.ucla.edu/c2d/, from Adnan Darwiche's lab.

    Parameters
    ----------
    fname : str
        Name of file storing an Circuit sentence

    Returns
    -------
    nnf
        An Circuit representing the DIMACs file contents

    """
    nnf_dimacs_form = read_file(fname)
    idx = 0

    all_variables = set()
    graph_representation = circuits.DiGraph()
    clauses = []

    # todo - add clauses to Circuit

    for line in nnf_dimacs_form:
        line_payload = line.split()[:]
        # ignore comments
        if line_payload[0] == "c":
            pass
        # get topline data
        elif line_payload[0] == "nnf":
            (nNodes, nEdges, nVars) = map(int, line_payload[1:])
        # add a leaf
        elif line_payload[0] == "L":
            variable = int(line_payload[1])
            all_variables.add(abs(variable))
            graph_representation.add_node(idx, value=variable, type="LEAF")
            idx += 1
        # add an and clause
        elif line_payload[0] == "A":
            num_child_nodes = int(line_payload[1])
            # A 0 indicates a "True" leaf
            if num_child_nodes == 0:
                graph_representation.add_node(idx, value="True", type="LEAF")
                idx += 1
            else:
                child_nodes = map(int, line_payload[2:])
                graph_representation.add_node(idx,
                                              value="and",
                                              type="LOGIC_GATE")
                for child in child_nodes:
                    graph_representation.add_edge(idx, child)
                # append the index of the AND node in the graph to the list of clauses
                clauses.append(idx)
                idx += 1
        # add an or clause
        elif line_payload[0] == "O":
            conflict_variable = int(line_payload[1])
            #TODO: handle conflict variable
            num_child_nodes = int(line_payload[2])
            child_nodes = map(int, line_payload[3:])
            # O j 0 indicates a "False" leaf
            if num_child_nodes == 0:
                graph_representation.add_node(idx, value="False", type="LEAF")
                idx += 1
            else:
                graph_representation.add_node(idx,
                                              value="or",
                                              type="LOGIC_GATE")
                for child in child_nodes:
                    graph_representation.add_edge(idx, child)
                # append the index of the OR node in the graph to the list of clauses
                clauses.append(idx)
                idx += 1

    # Specifically for dDNNF conversions: some add an extraneous "True" or "False"
    # node. Remove this.
    roots = []
    for node in graph_representation.nodes():
        if graph_representation.in_degree(node) == 0:
            roots.append(node)
    if len(roots) > 1:
        for root in roots:
            if graph_representation.out_degree(root) == 0:
                graph_representation.remove_node(root)

    # If the graph is not a DAG, return None
    roots = []
    for node in graph_representation.nodes():
        if graph_representation.in_degree(node) == 0:
            roots.append(node)
    if len(roots) > 1 or len(roots) == 0:
        print("RETURNING NONE")
        return None

    nnf = circuits.Circuit(clauses=clauses,
                           variables=all_variables,
                           num_edges=nEdges,
                           num_nodes=nNodes,
                           graph_representation=graph_representation)

    nnf_dimacs_form.close()
    return nnf
Example #9
0
def parse_CNF_DIMACS(fname):
    assert (fname.endswith(".cnf")), "File not .cnf format"
    """
    Parse a DIMACS .cnf file.

    The .cnf format is specified in the included file /FormulaFormats.pdf.
    One source is http://reasoning.cs.ucla.edu/c2d/, from Adnan Darwiche's lab.
    Note that CNF is a subset of NNF.
    As such, read in NNF-like format (with assigned indices for each node).

    Parameters
    ----------
    fname : str
        Name of file storing a CNF sentence

    Returns
    -------
    nnf
        An NNF representing the DIMACs file contents

    """
    cnf_dimacs_form = read_file(fname)

    idx = 0

    all_variables = set()
    graph_representation = circuits.DiGraph()
    clauses = []

    for line in cnf_dimacs_form:
        line_payload = line.split()[:]
        # ignore comments
        if line_payload[0] == "c":
            pass
        # get topline data
        elif line_payload[0] == "p" and line_payload[1] == "cnf":
            (nVars, nClauses) = map(int, line_payload[2:])
        # add each clause
        elif line_payload[0].replace('-', '').isdigit():
            # track the added leaves for each clause for joining OR node
            added_leaves = []
            # create a LEAF node for each mentioned variable
            for variable in map(int, line_payload[0:-1]):
                all_variables.add(abs(variable))
                graph_representation.add_node(idx, value=variable, type="LEAF")
                added_leaves.append(idx)
                idx += 1
            # create an OR node joining all variables listed in a clause
            graph_representation.add_node(idx, value="or", type="LOGIC_GATE")
            for leaf in added_leaves:
                graph_representation.add_edge(idx, leaf)
            clauses.append(idx)

            idx += 1

    # if graph is empty, just return it like that
    if len(graph_representation.nodes()) == 0:
        print("GRAPH WAS EMPTY")
        return None

    # otherwise, join all OR nodes (tracked as clauses) with an AND node
    graph_representation.add_node(idx, value="and", type="LOGIC_GATE")
    for clause in clauses:
        graph_representation.add_edge(idx, clause)
    clauses.append(idx)

    nnf = circuits.Circuit(clauses=clauses,
                           variables=all_variables,
                           num_edges=graph_representation.number_of_nodes(),
                           num_nodes=graph_representation.number_of_edges(),
                           graph_representation=graph_representation)

    cnf_dimacs_form.close()
    return nnf