def transform(c: Circuit, prefix: str='') -> Cnf: '''The function transform takes a Circuit c and returns a Cnf obtained by the Tseitin transformation of c. The optional prefix string will be used for all variable names in the Cnf. ''' inputs.clear() signals.clear() solution = Cnf() # Filling input dictionary for in_str in c.getInputs(): inputs[in_str] = SatVar(prefix + in_str) # Filling signals dictionary (outputs and internal signals) for sig_str in c.getSignals(): signals[sig_str] = SatVar(prefix + sig_str) # Obtaining the CNFs for each signal (either intern or output) for sig_str in c.getSignals(): node = c.getEquation(sig_str) solution = solution & transform_node(node, signals[sig_str], c) return solution pass
def transform(c: Circuit, prefix: str = '') -> Cnf: myCnf = Cnf() inputs = c.getSignals() #On parcourt l'ensemble des inputs pour trouver la transformation Tseitin for signal in inputs: mySatVar = SatVar(prefix + signal) node = c.getEquation(signal) child = node.getChildren() # Il y a une erreur dans test.py : Did not find value for output signal 's_8' in solution # En se rendant dans cra8.crc, on se rend compte qu'il y a une opération d'affection : # s_8 = x19 # On crée donc la fonction EQ dans le fichier adder.py qui est simplement # une opération d'égalité nécessaire pour passer le test de cra8.crc if type(node).__name__ == "Variable": SatVarName = SatVar(prefix + node.getName()) myCnf = myCnf & EQ(SatVarName, mySatVar) if type(node).__name__ == "Literal": myCnf = LiteralNode(node, mySatVar, myCnf) if type(node).__name__ == "BinOp": myCnf = BinOpNode(node, mySatVar, myCnf, prefix) if type(node).__name__ == "UnOp": myCnf = UnOpNode(node, mySatVar, myCnf, prefix) return myCnf
def check(c1: Circuit, c2: Circuit) -> (bool, Solution): numberOutputs_1 = len(c1.getOutputs()) # Les deux circuits sont incompatibles if (c1.getInputs() == c2.getInputs() and c1.getOutputs() == c2.getOutputs()) == 0: return (False, None) # Etape 1 : transformation Tseitin des circuits c1 et c2 cnf1 = transform(c1, 'c1_') cnf2 = transform(c2, 'c2_') cnf = cnf1 & cnf2 output_mitter = SatVar('output_mitter') # Etape 2 : On connecte les entrées c1 et c2 grâce à connector_in for inputs_1 in c1.getInputs(): connector_in = SatVar(inputs_1) #futur lien entre c2 et c1 c1_in = SatVar('c1_' + str(inputs_1)) c2_in = SatVar('c2_' + str(inputs_1)) cnf = cnf & EQ(c1_in, connector_in) & EQ(c2_in, connector_in) # Etape 3 : On connecte les sorties aux différents XOR connector_out_XOR_TAB = [] for outputs_1 in c1.getOutputs(): connector_out = SatVar(outputs_1) connector_out_XOR_TAB.append(connector_out) c1_out = SatVar('c1_' + str(outputs_1)) c2_out = SatVar('c2_' + str(outputs_1)) cnf = cnf & XOR(c1_out, c2_out, connector_out) # Etape 4 : On connecte les sorties des XOR à la porte logique OR finale if (numberOutputs_1 == 1): cnf = cnf & EQ(output_mitter, connector_out_XOR_TAB[0]) else: connector_out_OR_TAB = [SatVar('connector_or' + str(0))] cnf = cnf & OR(connector_out_XOR_TAB[0], connector_out_XOR_TAB[1], connector_out_OR_TAB[0]) for i in range(1, len(connector_out_XOR_TAB) - 2): connector_out_OR_TAB.append(SatVar('connector_or' + str(i))) cnf = cnf & OR(connector_out_XOR_TAB[i + 1], connector_out_OR_TAB[i - 1], connector_out_OR_TAB[i]) cnf = cnf & EQ(output_mitter, connector_out_OR_TAB[ len(connector_out_XOR_TAB) - 3]) # On connecte à la sortie finale cnf = cnf & output_mitter solution = Solver().solve(cnf) if (solution): return (False, solution) else: return (True, None)
def createInputCnf(inputs: set, prefix1: str, prefix2: str) -> Cnf: '''The fucntion createInputCnf takes the common inputs of the circuits being checked, taking into account their different prefixes, and builds the input connections required by the miter circuit logic. Its output is the cnf representing these connections ''' inputCnf = Cnf() for i in inputs: inputCnf &= equivalent(SatVar(i), SatVar(prefix1 + i)) inputCnf &= equivalent(SatVar(i), SatVar(prefix2 + i)) return inputCnf
def transform_node(n: Node, out: SatVar, c: Circuit) -> Cnf: '''The function transformNode recursively analyses the nodes objects it receives and builds the corresponding CNF. Each step's output is its node's CNF so that the final execution has the complete CNF for a given node. ''' cnf = Cnf() # Child nodes analysis for operation nodes children = [] for child in n.getChildren(): if isinstance(child, Literal): lit = SatVar('l_' + str(child.getID())) children.append(lit) if child.getValue() == True: cnf &= lit else: cnf &= ~lit elif isinstance(child, Variable): if child.getName() in inputs: var = inputs[child.getName()] elif child.getName() in signals: var = signals[child.getName()] children.append(var) elif isinstance(child, OpNode): internal = SatVar('y_' + str(child.getID())) children.append(internal) cnf &= transform_node(child, internal, c) # CNF building if isinstance(n, OpNode): if len(children) == 1: if n.getOp() == '~': cnf &= gate_not(children[0], out) elif len(children) == 2: if n.getOp() == '&': cnf &= gate_and(children[0], children[1], out) elif n.getOp() == '|': cnf &= gate_or(children[0], children[1], out) elif n.getOp() == '^': cnf &= gate_xor(children[0], children[1], out) elif isinstance(n, Variable): cnf &= equivalent(signals[n.getName()], out) elif isinstance(n, Literal): lit = SatVar('l_' + str(n.getID())) cnf &= equivalent(lit, out) if n.getValue() == True: cnf &= lit else: cnf &= ~lit return cnf
def transform(c: Circuit, prefix: str='') -> Cnf: '''The function transform takes a Circuit c and returns a Cnf obtained by the Tseitin transformation of c. The optional prefix string will be used for all variable names in the Cnf. ''' cnf = Cnf() for keys in c.getSignals(): s = SatVar(prefix+keys) node = c.getEquation(keys) children = node.getChildren() # This case should not really happen in a real circuit. if type(node).__name__ == "Variable": a = SatVar(prefix+node.getName()) cnf = cnf & mk_eq(s, a) if type(node).__name__ == "Literal": if (node.getValue()): cnf = cnf & s else: cnf = cnf & ~s if type(node).__name__ == "BinOp": a, cnf1 = transform_rec(node.getChild(0), prefix) b, cnf2 = transform_rec(node.getChild(1), prefix) cnf = cnf & cnf1 & cnf2 if (node.getOp() == "&"): cnf = cnf & mk_and(s, a, b) elif (node.getOp() == "^"): cnf = cnf & mk_xor(s, a, b) elif (node.getOp() == "|"): cnf = cnf & mk_or(s, a, b) else: raise ValueError("Unrecognized operator " + node.getOp()) if type(node).__name__ == "UnOp": a, cnf1 = transform_rec(node.getChild(0), prefix) cnf = cnf & cnf1 if (node.getOp() == "~"): cnf = cnf & mk_not(s, a) else: raise ValueError("Unrecognized operator " + node.getOp()) return cnf
def transform_recursive(nd: Node, prefix: str = ''): newCnf = Cnf() if type(nd).__name__ == "Variable": newSatVarName = SatVar(prefix + nd.getName()) return newSatVarName, newCnf newSatVar = SatVar(prefix + str(nd.getID())) if type(nd).__name__ == "Literal": newCnf = LiteralNode(nd, newSatVar, newCnf) if type(nd).__name__ == "BinOp": newCnf = BinOpNode(nd, newSatVar, newCnf, prefix) if type(nd).__name__ == "UnOp": newCnf = UnOpNode(nd, newSatVar, newCnf, prefix) return newSatVar, newCnf
def createComparatorCnf(outputs: set, prefix1: str, prefix2: str) -> (Cnf, SatVar): '''The function createComparatorCnf takes the common outputs of the circuits being checked, taking into account their differente prefixes, and builds the output miter logic with its XOR and OR gates. Its output is both the miter output CNF and the SatVar variable for the miter output symbol ''' # Generation of XOR gates for miter circuit output comparator = Cnf() comp_signals = [] i = 0 for output in outputs: xor_i = SatVar('xor_' + str(i)) output1 = SatVar(prefix1 + output) output2 = SatVar(prefix2 + output) comparator &= gate_xor(output1, output2, xor_i) comp_signals.append(xor_i) i += 1 # Generation of OR gates for miter circuit output or_neutral = SatVar('or_neutral') comparator &= (~or_neutral) i = 0 for xor_i in comp_signals: out = SatVar('or_' + str(i)) curr = xor_i if i == 0: comparator &= gate_or(or_neutral, xor_i, out) else: prev = SatVar('or_' + str(i - 1)) comparator &= gate_or(prev, xor_i, out) i += 1 return comparator, out
def transform_rec(node, prefix = ''): cnf = Cnf() children = node.getChildren() if type(node).__name__ == "Variable": s = SatVar(prefix+node.getName()) return s, cnf s = SatVar(prefix+ "s" + str(node.getID())) if type(node).__name__ == "Literal": if (node.getValue()): cnf = cnf & s else: cnf = cnf & ~s return s, cnf if type(node).__name__ == "BinOp": a, cnf1 = transform_rec(node.getChild(0), prefix) b, cnf2 = transform_rec(node.getChild(1), prefix) cnf = cnf & cnf1 & cnf2 if (node.getOp() == "&"): cnf = cnf & mk_and(s, a, b) elif (node.getOp() == "^"): cnf = cnf & mk_xor(s, a, b) elif (node.getOp() == "|"): cnf = cnf & mk_or(s, a, b) else: raise ValueError("Unrecognized operator " + node.getOp()) return s, cnf if type(node).__name__ == "UnOp": a, cnf1 = transform_rec(node.getChild(0), prefix) cnf = cnf & cnf1 if (node.getOp() == "~"): cnf = cnf & mk_not(s, a) else: raise ValueError("Unrecognized operator " + node.getOp()) return s, cnf
#!/usr/bin/env python3 import circuit.circuit as circ from circuit.cnf import SatVar, Solver, Cnf # circ full_adder { # inputs: a, b, cin # outputs: s, cout # s0 = a ^ b # s = s0 ^ cin # cout = (a & b) | (s0 & cin) # } # Inputs a = SatVar('a') b = SatVar('b') cin = SatVar('cin') # Outputs s = SatVar('s') cout = SatVar('cout') # Internal variables (if needed) s0 = SatVar('s0') s1 = SatVar('s1') s2 = SatVar('s2') def gate_and(in1: SatVar, in2: SatVar, out: SatVar) -> Cnf: return (~in1 | ~in2 | out) & (in1 | ~out) & (in2 | ~out)
for sig in signals: node = c.getEquation(sig) print("{} = {}".format(sig, node)) nd = c.getEquation('x92') print("Node s_13: {} [type {}]".format(nd, type(nd))) print("Node id: {}".format(nd.getID())) print("Node operation: {}".format(nd.getOp())) for ch in nd.getChildren(): print("Child node (id: {}): {} [type: {}]".format(ch.getID(), ch, type(ch))) # ===================================== CNF formulas and SAT solving print("===============================") x = SatVar('x') y = SatVar('y') z = SatVar('z') cnf = (x | ~y) & (z | y) print(cnf) solver = Solver() solution = solver.solve(cnf) print(solution) cnf &= ~x print(cnf) solution = solver.solve(cnf) print(solution) if solution:
def constr(x): return SatVar(x) if test[x] else ~SatVar(x)
def check(c1: Circuit, c2: Circuit) -> (bool, Solution): '''The function check() takes two Circuits as input and performs an equivalence check using a SAT solver. it returns a tuple, where the first entry is a Boolean value (True for equivalent, False for different) and the second value is -- in case of a difference found -- a Solution to the underlying SAT problem, representing a counterexample. If the circuits are indeed equivalent, the second entry will be None. ''' if not (c1.getInputs() == c2.getInputs() and c1.getOutputs() == c2.getOutputs()): return (False, None) cnf1 = transform(c1, "c1_") cnf2 = transform(c2, "c2_") cnf = cnf1 & cnf2 # Inputs should be the same. # Mitter_input isn't necessary. (Only here to preserve the name of the entree. Easier to get along with) for input in c1.getInputs(): mitter_input = SatVar(input) c1_input = SatVar("c1_" + input) c2_input = SatVar("c2_" + input) cnf = cnf & mk_eq(mitter_input, c1_input) & mk_eq(mitter_input, c2_input) outputs = list(c1.getOutputs()) n = len(outputs) s = SatVar("mitter_output") # Computation of the mitter output : # Xor of the outputs of the Circuits # Then an or of all these xor (as we only have made a binary_or function, we do as many or as needed) # # The code would be simplier if a 1 Litteral were added to always make a binary or, even when there only is one xor # But it would be less efficient. if n == 1: c1_output = SatVar("c1_" + outputs[0]) c2_output = SatVar("c2_" + outputs[0]) cnf = cnf & mk_xor(s, c1_output, c2_output) else: for i, output in enumerate(outputs): c1_output = SatVar("c1_" + output) c2_output = SatVar("c2_" + output) xor_output = SatVar("mitter_xor_" + output) cnf = cnf & mk_xor(xor_output, c1_output, c2_output) if i == 1: ith_or = SatVar("mitter_or_1") if i + 1 == n: ith_or = s xor_prev = SatVar("mitter_xor_" + outputs[0]) # = [i-1] cnf = cnf & mk_or(ith_or, xor_prev, xor_output) if i>1: ith_or = SatVar("mitter_or_" + str(i)) if i + 1 == n: ith_or = s or_prev = SatVar("mitter_or_" + str(i-1)) cnf = cnf & mk_or(ith_or, or_prev, xor_output) cnf = cnf & s solution = Solver().solve(cnf) if not solution: return (True, None) else: return (False, solution.assignment)