#!/usr/bin/env python3 import numpy as np class kSAT: def __init__(self): self.__clauses = [] self.__numVariables = 0 def getBindings(self): return self.__clauses def getNumberOfClauses(self): return len(self.__clauses) def getClause(self, clauseIndex): return self.__clauses[clauseIndex] def addClause(self, clause): #counting variables (assuming variables are assigned # consistently from 1 to numTotalVars) for varBinding in clause: if np.absolute(varBinding) > self.__numVariables: self.__numVariables = np.absolute(varBinding) self.__clauses.append(clause) def getDegreesOfVariables(self): degrees = {} for clause in self.__clauses: for binding in clause: varLabel = abs(binding) if varLabel not in degrees: degrees[varLabel] = 1 else: degrees[varLabel] += 1 return degrees def evaluate(satInstance, assignments): evaluations = [] for assignment in assignments: evaluations.append(satInstance.checkAssignment(assignment)) return evaluations def checkAssignment(self, assignment): if not self.__assignmentIsComplete(assignment): print("wrong number of variables in assignment") return False for clause in self.__clauses: clauseResult = False for binding in clause: varIndex = np.absolute(binding) - 1 if ((binding > 0 and assignment[varIndex] == True) or (binding < 0 and assignment[varIndex] == False)): clauseResult = True if clauseResult == False: return False return True def __assignmentIsComplete(self, assignment): if (len(assignment) != self.getNumberOfVariables() or None in assignment): return False return True def getNumberOfVariables(self): return self.__numVariables def toString(self): kSATString = "" for clauseIndex in range(len(self.__clauses) - 1): kSATString += self.__kSATClauseToString(clauseIndex) kSATString += " * " kSATString += self.__kSATClauseToString(len(self.__clauses) - 1) return kSATString def getLableOfBinding(self, clauseIndex, binding): #return label = "%d%d" % (clauseIndex # self.__clauses[clauseIndex][varInClauseIndex]) return (clauseIndex, binding) def getConflicts(self): conflicts = [] for clauseIndex in range(len(self.__clauses)): clause = self.__clauses[clauseIndex] for binding in clause: clauseToCheckIndex = 0 #search for conflict with binding for clauseToCheckIndex in range(clauseIndex, len(self.__clauses)): for bindingToCheck in self.__clauses[clauseToCheckIndex]: if binding == bindingToCheck * -1: conflLable1 = self.getLableOfBinding(clauseIndex, binding) conflLable2 = self.getLableOfBinding(clauseToCheckIndex, bindingToCheck) conflicts.append((conflLable1, conflLable2)) return conflicts def writeDIMACS(self, path): outputFile = open(path, "w") outputFile.write("c A SAT instance\n") outputFile.write("p cnf %d %d \n" % (self.getNumberOfVariables(), self.getNumberOfClauses())) for clause in self.getBindings(): for binding in clause: outputFile.write("%d " % binding) outputFile.write("0\n") outputFile.close() def readDIMACS(self, path): inputFile = open(path, "r") line = inputFile.readline() self.reset() while line != "": if line[0] != "c" and line[0] != "p": bindings = [int(binding) for binding in line.split()] self.addClause(bindings[:len(bindings) -1]) line = inputFile.readline() inputFile.close() def writeJSONLike(self): jsonLike = {} jsonLike["clauses"] = [] for clause in self.__clauses: tmpClause = [] for literal in clause: tmpClause.append(int(literal)) jsonLike["clauses"].append(tmpClause) return jsonLike def readJSONLike(self, jsonLike): self.reset() for clause in jsonLike["clauses"]: self.addClause(clause) def reset(self): self.__clauses = [] self.__numVariables = 0 def __kSATClauseToString(self, clauseIndex): clause = self.__clauses[clauseIndex] varCount = 0 isFirstVar = True; clauseString = "("; for weight in clause: varCount += 1 if not isFirstVar: clauseString += " + " clauseString += self.__kSATVariableToString(weight) isFirstVar = False clauseString += ")" return clauseString def __kSATVariableToString(self, weight): name = "x%d" % np.absolute(weight) if weight < 0: return "!" + name else: return name