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)
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)
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
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