#!/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
|