Ejemplo n.º 1
0
 def __init__(self, n, symbols):
     self.n = n
     self.symbols = symbols
     self.board = None
     self.la = LiteralAllocator()
     self.literal2board = dict()
     self.board2literal = dict()
     self.cnf = CNF()
     self.InitSquare()
     self.Assert()
Ejemplo n.º 2
0
 def getTseytinCNF(self):
     self.cnfForm = CNF()
     if len(self.inputs) == 0:
         return
     visited = []
     componentQueue = deque(self.assertedInputWires)
     # componentQueue = deque(self.inputs)
     visited.append(componentQueue[0])
     visitedGates = set()
     if DEBUG:
         print(
             'Translating circuit graph into CNF using Tseytin Transform.')
         t0 = time.time()
     while len(componentQueue) != 0:
         v = componentQueue.popleft()
         if isinstance(v, Wire):
             if v.variable is None:
                 raise Exception(
                     'All wire components must have a variable bound.')
             if v not in visited:
                 visited.append(v)
             for gate in v.gatesIn:
                 if gate not in visited:
                     visited.append(gate)
                     componentQueue.append(gate)
             if v.gateOut not in visited and v.gateOut is not None:
                 visited.append(v.gateOut)
                 componentQueue.append(v.gateOut)
         elif issubclass(type(v), Gate):
             if v not in visitedGates:
                 visitedGates.add(v)
                 self.cnfForm.mergeWithRaw(self.getTseytinSingleGate(v))
             if v.output not in visited:
                 visited.append(v.output)
                 componentQueue.append(v.output)
             for inputWire in v.inputs:
                 if inputWire not in visited:
                     visited.append(inputWire)
                     componentQueue.append(inputWire)
         else:
             raise Exception(
                 "Logic structure should only contain Wires and Gates")
     if DEBUG:
         total = time.time() - t0
         print('Finished translating ' + str(len(visited)) +
               ' components. (' + str(total) + ' seconds)')
     self.cnfForm.mergeWithRaw(self.getConstantClauses(visited))
Ejemplo n.º 3
0
 def __init__(self, inputs, startLiteral=None, overwriteLiterals=True):
     self.inputs = inputs
     self.assertedInputWires = self.inputs
     self.detectedInputWires = []
     self.constantWires = []
     if overwriteLiterals:
         LogicFormula.assignVariables(inputs, startLiteral)
     self.usedLiterals = self.getAllUsedVariables(self.inputs)
     if startLiteral is None:
         startLiteral = max(self.usedLiterals) + 1
     self.cnfForm = CNF()
     self.getTseytinCNF()
     self.rawCnf = self.cnfForm.rawCNF()
     if DEBUG:
         print(str(len(self.rawCnf)) + ' clauses generated.')
     self.unusedVars = self.cnfForm.usedVariables().symmetric_difference(
         set(range(1,
                   max(self.cnfForm.usedVariables()) + 1)))
     assert len(self.unusedVars) == 0, \
         "There shouldn't be unused variables in the Tseytin transform. Something is clearly wrong"
Ejemplo n.º 4
0
 def __init__(
     self,
     nodeDict,
     d,
     minColors=1,  # (15) Every vertex has at least one color
     adjacentDifColor=0,  # (16) adjacent vertices have different colors
     maxColors=None  #Maximum unique colors per node
 ):
     self.d = d
     self.minColors = minColors
     self.maxColors = maxColors
     self.adjacentDifColor = adjacentDifColor
     self.nodeDict = nodeDict
     self.literalToIndexTuple = None
     self.literalToColor = None
     self.nodeToLiteral = None
     self.clauses = []
     self.solution = []
     self.auxLiteral = -1
     self.origAuxLiteral = -1
     self.nodeToLiteralDict = dict()
     self.literaToNodeDict = dict()
     self.cnf = CNF()
Ejemplo n.º 5
0
class EulerSquare:
    def __init__(self, n, symbols):
        self.n = n
        self.symbols = symbols
        self.board = None
        self.la = LiteralAllocator()
        self.literal2board = dict()
        self.board2literal = dict()
        self.cnf = CNF()
        self.InitSquare()
        self.Assert()

    def InitSquare(self):
        self.board = []
        for row in range(self.n):
            cols = []
            for col in range(self.n):
                symbols = []
                for symbol in range(self.symbols):
                    symbolVals = []
                    for symbolVal in range(self.n):
                        literal = self.la.getLiteral()
                        self.board2literal[(row, col, symbol,
                                            symbolVal)] = literal
                        symbolVals.append(literal)
                    symbols.append(symbolVals)
                cols.append(symbols)
            self.board.append(cols)

        #                                     number of possible symbol values    | how many symbol types |  columns           |            rows
        # self.board = [[[[self.la.getLiteral() for _ in range(self.n)] for _ in range(self.symbols)] for _ in range(self.n)] for _ in range(self.n)]
        # pp.pprint(self.board)

    def Assert(self):
        # Asserts that for each symbol, it doesn't share a row or column with the same symbol
        for symbol in range(self.symbols):
            for row in range(self.n):
                for col in range(self.n):
                    for i in range(self.n):
                        for j in range(self.n):
                            if (row == i) ^ (col == j):
                                for symbolVal in range(self.n):
                                    self.cnf.addClause([
                                        -self.board[row][col][symbol]
                                        [symbolVal],
                                        -self.board[i][j][symbol][symbolVal]
                                    ])

        # Assert that each symbol has a single value assigned
        for symbol in range(self.symbols):
            for row in range(self.n):
                for col in range(self.n):
                    self.cnf.mergeWithRaw(
                        SATUtils.exactlyOne(self.board[row][col][symbol],
                                            forceInefficient=True)[0])

        # For each pair of symbols, they never appear twice
        for symbolA, symbolB in itertools.combinations(range(self.symbols), 2):
            for symbolValA in range(self.n):
                for symbolValB in range(self.n):
                    # get pairs of literals and AND them together. That value must be
                    # true exactly 1 time for each pairing of symbol types.
                    impliedLiterals = []
                    for row in range(self.n):
                        for col in range(self.n):
                            clauses, C = Tseytin.AND(
                                self.board[row][col][symbolA][symbolValA],
                                self.board[row][col][symbolB][symbolValB],
                                self.la.getLiteral())
                            impliedLiterals.append(C)
                            self.cnf.mergeWithRaw(clauses)
                    self.cnf.mergeWithRaw(
                        SATUtils.atLeast(impliedLiterals, 1)[0])

        # pp.pprint(sorted(self.cnf.rawCNF(), key=lambda x: [abs(_) for _ in x]))

    def Solve(self):
        finalVals = pycosat.solve(self.cnf.rawCNF())
        if finalVals == 'UNSAT':
            print(finalVals)
            return
        rows = []
        for row in range(self.n):
            cols = []
            for col in range(self.n):
                symbols = []
                for symbol in range(self.symbols):
                    for symbolVal in range(self.n):
                        val = self.board2literal[(row, col, symbol, symbolVal)]
                        if val in finalVals:
                            symbols.append(symbolVal)
                            break
                cols.append(symbols)
            rows.append(cols)
        pp.pprint(rows)
Ejemplo n.º 6
0
class LogicFormula:
    def __init__(self, inputs, startLiteral=None, overwriteLiterals=True):
        self.inputs = inputs
        self.assertedInputWires = self.inputs
        self.detectedInputWires = []
        self.constantWires = []
        if overwriteLiterals:
            LogicFormula.assignVariables(inputs, startLiteral)
        self.usedLiterals = self.getAllUsedVariables(self.inputs)
        if startLiteral is None:
            startLiteral = max(self.usedLiterals) + 1
        self.cnfForm = CNF()
        self.getTseytinCNF()
        self.rawCnf = self.cnfForm.rawCNF()
        if DEBUG:
            print(str(len(self.rawCnf)) + ' clauses generated.')
        self.unusedVars = self.cnfForm.usedVariables().symmetric_difference(
            set(range(1,
                      max(self.cnfForm.usedVariables()) + 1)))
        assert len(self.unusedVars) == 0, \
            "There shouldn't be unused variables in the Tseytin transform. Something is clearly wrong"

    def getConstantClauses(self, visitedPoints):
        clauses = []
        self.constantWires = []
        for wire in visitedPoints:
            if isinstance(
                    wire, Wire
            ) and wire.constant is not None and wire not in self.constantWires:
                self.constantWires.append(wire)
                if wire.constant:
                    clauses.append([wire.variable])
                else:
                    clauses.append([-wire.variable])
        return clauses

    def getTseytinCNF(self):
        self.cnfForm = CNF()
        if len(self.inputs) == 0:
            return
        visited = []
        componentQueue = deque(self.assertedInputWires)
        # componentQueue = deque(self.inputs)
        visited.append(componentQueue[0])
        visitedGates = set()
        if DEBUG:
            print(
                'Translating circuit graph into CNF using Tseytin Transform.')
            t0 = time.time()
        while len(componentQueue) != 0:
            v = componentQueue.popleft()
            if isinstance(v, Wire):
                if v.variable is None:
                    raise Exception(
                        'All wire components must have a variable bound.')
                if v not in visited:
                    visited.append(v)
                for gate in v.gatesIn:
                    if gate not in visited:
                        visited.append(gate)
                        componentQueue.append(gate)
                if v.gateOut not in visited and v.gateOut is not None:
                    visited.append(v.gateOut)
                    componentQueue.append(v.gateOut)
            elif issubclass(type(v), Gate):
                if v not in visitedGates:
                    visitedGates.add(v)
                    self.cnfForm.mergeWithRaw(self.getTseytinSingleGate(v))
                if v.output not in visited:
                    visited.append(v.output)
                    componentQueue.append(v.output)
                for inputWire in v.inputs:
                    if inputWire not in visited:
                        visited.append(inputWire)
                        componentQueue.append(inputWire)
            else:
                raise Exception(
                    "Logic structure should only contain Wires and Gates")
        if DEBUG:
            total = time.time() - t0
            print('Finished translating ' + str(len(visited)) +
                  ' components. (' + str(total) + ' seconds)')
        self.cnfForm.mergeWithRaw(self.getConstantClauses(visited))

    def getTseytinSingleGate(self, gate):
        if not issubclass(type(gate), Gate):
            raise Exception("Must be of type gate")
        # If you manage to get here with inputs/outputs as None I'm impressed!
        if isinstance(gate, Gate2):
            varA = gate.inputA.variable
            varB = gate.inputB.variable
            varOut = gate.output.variable
            if gate.gateType == LogicStructure.AND:
                # print(str(varA) + ' AND ' + str(varB) + ' = ' + str(varOut))
                newClauses, _ = Tseytin.AND(varA, varB, varOut)
                return newClauses
            elif gate.gateType == LogicStructure.NAND:
                newClauses, _ = Tseytin.NAND(varA, varB, varOut)
                # print(str(varA) + ' NAND ' + str(varB) + ' = ' + str(varOut))
                return newClauses
            elif gate.gateType == LogicStructure.OR:
                newClauses, _ = Tseytin.OR(varA, varB, varOut)
                # print(str(varA) + ' OR ' + str(varB) + ' = ' + str(varOut))
                return newClauses
            elif gate.gateType == LogicStructure.NOR:
                newClauses, _ = Tseytin.NOR(varA, varB, varOut)
                # print(str(varA) + ' NOR ' + str(varB) + ' = ' + str(varOut))
                return newClauses
            elif gate.gateType == LogicStructure.XOR:
                newClauses, _ = Tseytin.XOR(varA, varB, varOut)
                # print(str(varA) + ' XOR ' + str(varB) + ' = ' + str(varOut))
                return newClauses
            elif gate.gateType == LogicStructure.XNOR:
                newClauses, _ = Tseytin.XNOR(varA, varB, varOut)
                # print(str(varA) + ' XNOR ' + str(varB) + ' = ' + str(varOut))
                return newClauses
            elif gate.gateType == LogicStructure.IMPLIES:
                newClauses, _ = Tseytin.IMPLIES(varA, varB, varOut)
                # print(str(varA) + ' IMPLIES ' + str(varB) + ' = ' + str(varOut))
                return newClauses
            else:
                raise Exception('Unknown gate')
        elif isinstance(gate, Gate1):
            varA = gate.inputA.variable
            varOut = gate.output.variable
            if gate.gateType == LogicStructure.NOT:
                newClauses, _ = Tseytin.NOT(varA, varOut)
                # print('NOT ' + str(varA) + ' = ' + str(varOut))
                return newClauses
            else:
                raise Exception('Unknown gate')
        elif isinstance(gate, GateCustom):
            raise Exception("Custum logic structures aren't always gates")

    def getAllUsedVariables(self, inputs):
        if DEBUG:
            print('Determining used variables.')
            t0 = time.time()
        if len(inputs) == 0:
            return []
        self.detectedInputWires = []
        self.freeInputs = []
        visited = []
        componentQueue = deque(inputs)
        usedVariables = []
        visited.append(componentQueue[0])
        while len(componentQueue) != 0:
            v = componentQueue.popleft()
            if isinstance(v, Wire):
                if v.variable is None:
                    raise Exception(
                        'All wire components must have a variable bound.')
                elif v.variable not in usedVariables:
                    usedVariables.append(v.variable)
                if v not in visited:
                    visited.append(v)
                if v.gateOut is None:
                    if v not in self.detectedInputWires:
                        self.detectedInputWires.append(v)
                    if v.constant is None and v not in self.freeInputs:
                        self.freeInputs.append(v)
                for gate in v.gatesIn:
                    if gate not in visited:
                        visited.append(gate)
                        componentQueue.append(gate)
                if v.gateOut not in visited and v.gateOut is not None:
                    visited.append(v.gateOut)
                    componentQueue.append(v.gateOut)
            elif issubclass(type(v), Gate):
                if v.output not in visited:
                    visited.append(v.output)
                    componentQueue.append(v.output)
                for inputWire in v.inputs:
                    if inputWire not in visited:
                        visited.append(inputWire)
                        componentQueue.append(inputWire)
            else:
                raise Exception(
                    "Logic structure should only contain Wires and Gates")
        if DEBUG:
            total = time.time() - t0
            print(
                str(len(usedVariables)) + ' used variables found. (' +
                str(total) + ' seconds)')
        return usedVariables

    @staticmethod
    def assignVariables(inputs, startLiteral=None):
        if len(inputs) == 0:
            return
        if startLiteral is None:
            startLiteral = 1
        literalTracker = startLiteral
        visited = []
        componentQueue = deque(inputs)
        visited.append(componentQueue[0])
        if DEBUG:
            print('Assigning variables.')
            t0 = time.time()
        while len(componentQueue) != 0:
            v = componentQueue.popleft()
            if isinstance(v, Wire):
                v.variable = literalTracker
                literalTracker += 1
                # if v.name is not None:
                #     print(v.name + ' assigned: ' + str(v.variable))
                if v not in visited:
                    visited.append(v)
                for gate in v.gatesIn:
                    if gate not in visited:
                        # print('Gate added')
                        visited.append(gate)
                        componentQueue.append(gate)
                if v.gateOut not in visited and v.gateOut is not None:
                    visited.append(v.gateOut)
                    componentQueue.append(v.gateOut)
            elif issubclass(type(v), Gate):
                if v.output not in visited:
                    visited.append(v.output)
                    componentQueue.append(v.output)
                    # print('Wire added')
                for inputWire in v.inputs:
                    if inputWire not in visited:
                        visited.append(inputWire)
                        componentQueue.append(inputWire)
                        # print('Wire added')
            else:
                raise Exception(
                    "Logic structure should only contain Wires and Gates")
        if DEBUG:
            total = time.time() - t0
            print('Variable assignment completed. (' + str(total) +
                  ' seconds)')
        return literalTracker - 1

    # https://en.wikipedia.org/wiki/Tseytin_transformation
    @staticmethod
    def WikipediaExample():
        x1 = Wire()
        x2 = Wire()
        x3 = Wire()
        gate1 = Wire()
        not1 = Gate1(LogicStructure.NOT, x1, gate1)
        gate3_5 = Wire()
        not2 = Gate1(LogicStructure.NOT, x2, gate3_5)
        gate2 = Wire()
        and1 = Gate2(LogicStructure.AND, gate1, x2, gate2)
        gate4 = Wire()
        and2 = Gate2(LogicStructure.AND, x1, gate3_5, gate4)
        gate6 = Wire()
        and3 = Gate2(LogicStructure.AND, gate3_5, x3, gate6)
        gate7 = Wire()
        or1 = Gate2(LogicStructure.OR, gate2, gate4, gate7)
        gate8 = Wire()
        or2 = Gate2(LogicStructure.OR, gate7, gate6, gate8)
        y = gate8
        return [x1, x2, x3], [y]

    @staticmethod
    def Peg1DExample():
        x = [Wire() for _ in range(5)]
        y = [Wire() for _ in range(5)]
        offsets = [i for i in range(-2, 3)]
        for i in range(5):
            inputs = []
            for j in offsets:
                if i + j not in range(5):
                    inputs.append(None)
                else:
                    inputs.append(x[i + j])
            for j in offsets:
                if i + j not in range(5):
                    inputs.append(None)
                else:
                    inputs.append(y[i + j])
            GateCustom().PegSolitaireFlatNextState(inputs, Wire())
        return x + y, []
Ejemplo n.º 7
0
class GraphColoring:
    def __init__(
        self,
        nodeDict,
        d,
        minColors=1,  # (15) Every vertex has at least one color
        adjacentDifColor=0,  # (16) adjacent vertices have different colors
        maxColors=None  #Maximum unique colors per node
    ):
        self.d = d
        self.minColors = minColors
        self.maxColors = maxColors
        self.adjacentDifColor = adjacentDifColor
        self.nodeDict = nodeDict
        self.literalToIndexTuple = None
        self.literalToColor = None
        self.nodeToLiteral = None
        self.clauses = []
        self.solution = []
        self.auxLiteral = -1
        self.origAuxLiteral = -1
        self.nodeToLiteralDict = dict()
        self.literaToNodeDict = dict()
        self.cnf = CNF()

    def generateClauses(self):
        if self.auxLiteral == -1:
            raise TypeError()
        #
        # (15) Every vertex has at least minColors colors
        if self.minColors != None:
            self.enforceMinColors()

        # Every vertex has at most maxColors colors
        if self.maxColors != None:
            self.enforceMaxColors()

        # (16) adjacent vertices have different colors
        if self.adjacentDifColor != None:
            self.enforceAdjacentDifColor()

    def enforceAdjacentDifColor(self):
        for i in self.nodeDict:
            for j in self.nodeDict:
                if self.isAdjacent(i, j):
                    self.assertNodesDifferInColorByR(i, j,
                                                     self.adjacentDifColor)

    def enforceMinColors(self):
        for vertex in self.nodeDict.keys():
            literalsToAssertGEKtrue = [
                self.nodeToLiteral(GraphNode(vertex, k)) for k in range(self.d)
            ]
            [subclauses,
             newHighestLiteral] = SATUtils.atLeast(literalsToAssertGEKtrue,
                                                   self.minColors,
                                                   self.auxLiteral)
            if verboseExpressions:
                for clause in subclauses:
                    self.cnf.addClause(
                        Clause(clause,
                               groupComment=str(vertex) + ' has at least ' +
                               str(self.minColors) + ' colors'))
            self.auxLiteral = newHighestLiteral + 1
            self.clauses += subclauses

    def enforceMaxColors(self):
        for vertex in self.nodeDict.keys():
            literalsToAssertLEKtrue = [
                self.nodeToLiteral(GraphNode(vertex, k)) for k in range(self.d)
            ]
            [subclauses,
             newHighestLiteral] = SATUtils.atMost(literalsToAssertLEKtrue,
                                                  self.minColors,
                                                  self.auxLiteral)
            if verboseExpressions:
                for clause in subclauses:
                    self.cnf.addClause(
                        Clause(clause,
                               groupComment=str(vertex) + ' has at most ' +
                               str(self.minColors) + ' colors'))
            self.auxLiteral = newHighestLiteral + 1
            self.clauses += subclauses

    def assertNodesDifferInColorByR(self, nodeA, nodeB, rVal):
        newClauses = []
        #print([-self.nodeToLiteral(GraphNode(nodeA, 0)), -self.nodeToLiteral(GraphNode(nodeB, 0))], [nodeA, nodeB])
        for r in range(rVal):
            for k in range(self.d):
                if k + r < self.d:
                    newClause = [
                        -self.nodeToLiteral(GraphNode(nodeA, k)),
                        -self.nodeToLiteral(GraphNode(nodeB, k + r))
                    ]
                    newClauses.append(newClause)
                    if verboseExpressions:
                        self.cnf.addClause(
                            Clause(newClause,
                                   comment='not ' + str(k) + ' and ' +
                                   str(k + r) + ' at the same time',
                                   groupComment=str(nodeA) + ' and ' +
                                   str(nodeB) + ' differ in color by ' +
                                   str(rVal)))
                    # make sure to assert the other way too when it's not symmetrical
                    if r != 0:
                        newClause = [
                            -self.nodeToLiteral(GraphNode(nodeA, k + r)),
                            -self.nodeToLiteral(GraphNode(nodeB, k))
                        ]
                        newClauses.append(newClause)
                        if verboseExpressions:
                            self.cnf.addClause(
                                Clause(newClause,
                                       comment='not ' + str(k + r) + ' and ' +
                                       str(k) + ' at the same time',
                                       groupComment=str(nodeA) + ' and ' +
                                       str(nodeB) + ' differ in color by ' +
                                       str(rVal)))
        self.clauses += newClauses

    # https://en.wikipedia.org/wiki/L(h,_k)-coloring
    # L(h, k) Coloring
    # L(2, 1) is equivalent to the radio coloring problem
    def L(self, h, k):
        for i in self.nodeDict:
            for j in self.nodeDict:
                if self.nodeToLiteral(GraphNode(i, 0)) < self.nodeToLiteral(
                        GraphNode(j, 0)):
                    if self.isAdjacent(i, j):
                        self.assertNodesDifferInColorByR(i, j, h)
                    elif self.sharesNeighbor(i, j):
                        self.assertNodesDifferInColorByR(i, j, k)

    def viewClauses(self):
        for clause in self.clauses:
            pp.pprint([(sign(literal), self.literalToIndexTuple(abs(literal)),
                        self.literalToColor(abs(literal)))
                       if literal <= self.origAuxLiteral else
                       (sign(literal), 'aux') for literal in clause])

    def viewSolution(self):
        self.solution = list(pycosat.solve(self.clauses))
        nodeColors = dict()
        if self.solution == list('UNSAT'):
            print(self.solution)
            return False
        for literal in self.solution:
            if literal > 0 and literal < self.origAuxLiteral:
                identifier = self.literalToIndexTuple(literal)
                if identifier not in nodeColors:
                    nodeColors[identifier] = [self.literalToColor(literal)]
                else:
                    nodeColors[identifier].append(self.literalToColor(literal))
        pp.pprint(nodeColors)
        return True

    def viewSolutions(self):
        self.solution = list(pycosat.solve(self.clauses))
        if self.solution == list('UNSAT'):
            print(self.solution)
            return
        for solution in list(pycosat.itersolve(self.clauses)):
            nodeColors = dict()
            for literal in solution:
                if literal > 0 and literal < self.origAuxLiteral:
                    identifier = self.literalToIndexTuple(literal)
                    if identifier not in nodeColors:
                        nodeColors[identifier] = [self.literalToColor(literal)]
                    else:
                        nodeColors[identifier].append(
                            self.literalToColor(literal))
            pp.pprint(nodeColors)

    def defineNodeLiteralConversion(self,
                                    literalToID=None,
                                    literalToColor=None,
                                    GraphNodeToLiteral=None):
        if literalToID == None or literalToColor == None or GraphNodeToLiteral == None:
            self.literaToNodeDict = dict()
            self.nodeToLiteralDict = dict()
            self.auxLiteral = 1
            for node in self.nodeDict.keys():
                self.nodeToLiteralDict[node] = self.auxLiteral
                self.literaToNodeDict[self.auxLiteral] = node
                self.auxLiteral += 1
            literalToID = lambda literal: self.literaToNodeDict[
                (literal - 1) % len(self.nodeDict) + 1]
            literalToColor = lambda literal: (literal - 1) // len(self.nodeDict
                                                                  )
            GraphNodeToLiteral = lambda gnode: self.nodeToLiteralDict[
                gnode.ID] + gnode.color * len(self.nodeDict)

        for color in range(self.d):
            for testNode in self.nodeDict.keys():
                literal = GraphNodeToLiteral(GraphNode(testNode, color))
                nodeIdentifier = literalToID(literal)
                nodeColor = literalToColor(literal)
                if (testNode, color) != (nodeIdentifier, nodeColor):
                    raise TypeError()
                # print((testNode, color))
                # print((nodeIdentifier, nodeColor))
        self.literalToIndexTuple = literalToID
        self.literalToColor = literalToColor
        self.nodeToLiteral = GraphNodeToLiteral
        # initialize the auxLiteral to 1 larger than the largest node found during the verification process
        self.auxLiteral = 1 + max([
            max([
                abs(self.nodeToLiteral(GraphNode(x, color)))
                for x in self.nodeDict.keys()
            ]) for color in range(self.d)
        ])
        self.origAuxLiteral = self.auxLiteral

    # given 2 vertices u, v, asserts those 2 vertices differ in at least r colors
    def assertRdiffColors(self, u, v, r):
        groups = [[[self.nodeToLiteral(GraphNode(u, color))],
                   [-self.nodeToLiteral(GraphNode(v, color))]]
                  for color in range(0, self.d)]

        [subclauses, newHighestLiteral, cardinalityClauses] = SATUtils.atLeastRsub( \
                groups,
                r,
                self.auxLiteral)
        self.auxLiteral = newHighestLiteral + 1
        self.clauses += subclauses

        #repeat for positive literals
        groups = [[[-self.nodeToLiteral(GraphNode(u, color))],
                   [self.nodeToLiteral(GraphNode(v, color))]]
                  for color in range(0, self.d)]

        [subclauses, newHighestLiteral, cardinalityClauses] = SATUtils.atLeastRsub(\
                groups,
                r,
                self.auxLiteral)
        self.auxLiteral = newHighestLiteral + 1
        self.clauses += subclauses

    # returns whether or not u and v share a common neighbor
    def sharesNeighbor(self, u, v):
        if u == v:  # a node doesn't count as its own neighbor's neighbor
            return False
        # u and v share a neighbor if v is in the neighbor list of any of u's neighbors
        for neighbor in self.nodeDict[u]:
            if self.isAdjacent(v, neighbor):
                return True
        return False

    # returns whether or not u and v are adjacent
    def isAdjacent(self, u, v):
        return u in self.nodeDict[v]

    @staticmethod
    def generateKClique(nodesInClique):
        nodeDict = dict()
        for i, a in enumerate(nodesInClique):
            for b in nodesInClique[i + 1:]:
                if a != b:
                    if a not in nodeDict:
                        nodeDict[a] = [b]
                    else:
                        nodeDict[a].append(b)
                    if b not in nodeDict:
                        nodeDict[b] = [a]
                    else:
                        nodeDict[b].append(a)
        return nodeDict

    @staticmethod
    def mergeAintoB(adjListA, adjListB):
        for k, v in adjListA.items():
            if k not in adjListB:
                adjListB[k] = v
            else:
                adjListB[k] += v

    @staticmethod
    def merged(adjListA, adjListB):
        bCopy = adjListB.copy()
        GraphColoring.mergeAintoB(adjListA, bCopy)
        return bCopy

    #Path graph
    # A graph where n-2 vertices have exactly 2 neighbors and 2 vertices have 1 neighbor and all vertices are connected
    # AKA a binary tree with only left or only right children
    @staticmethod
    def P(n, zeroIndexed=True):
        if n < 1:
            raise ValueError()
        if n == 1:
            return {0 if zeroIndexed else 1: []}
        nodeDict = dict()
        if zeroIndexed:
            for i in range(0, n):
                if i == 0:
                    nodeDict[0] = (1, )
                elif i == n - 1:
                    nodeDict[n - 1] = (n - 2, )
                else:
                    nodeDict[i] = (i - 1, i + 1)
        else:
            for i in range(1, n + 1):
                if i == 1:
                    nodeDict[1] = (2, )
                elif i == n:
                    nodeDict[n] = (n - 1, )
                else:
                    nodeDict[i] = (i - 1, i + 1)
        return nodeDict

    # Cycle graph
    # A graph where all vertices are part of a single loop of length n
    @staticmethod
    def C(n, zeroIndexed=True):
        if n < 1:
            raise ValueError()
        if n == 1:
            return {0 if zeroIndexed else 1: []}
        nodeDict = dict()
        if zeroIndexed:
            for i in range(0, n):
                nodeDict[i] = ((i - 1) % n, (i + 1) % n)
        else:
            for i in range(1, n + 1):
                nodeDict[i] = (((i - 2) % n) + 1, ((i) % n) + 1)
        return nodeDict

    # Cycle graph
    # A graph where all vertices connected to all other vertices
    @staticmethod
    def K(n, zeroIndexed=True):
        if n < 1:
            raise ValueError()
        if n == 1:
            return {0 if zeroIndexed else 1: []}
        nodeDict = dict()
        if zeroIndexed:
            for i in range(0, n):
                nodeDict[i] = tuple(list(range(0, i)) + list(range(i + 1, n)))
        else:
            for i in range(1, n + 1):
                nodeDict[i] = tuple(
                    list(range(1, n + 1)[:(i - 1)]) +
                    list(range(1, n + 1)[i:]))
        return nodeDict

    # disconnects each connected edge and connects each disconnected edge
    @staticmethod
    def invert(nodeDict):
        nodeSet = set(nodeDict.keys())
        newNodeDict = dict()
        for vertex, edges in nodeDict.items():
            newNodeDict[vertex] = tuple(
                nodeSet.difference(set(list(edges) + [vertex])))
        return newNodeDict

    # https://en.wikipedia.org/wiki/Cartesian_product_of_graphs
    @staticmethod
    def cartesianProduct(G, H):
        vertexSet = itertools.product(G.keys(), H.keys())
        print(list(vertexSet))
        nodeDict = dict()
        for i, vertexA in enumerate(
                list(itertools.product(G.keys(), H.keys()))[:-1]):
            for vertexB in list(itertools.product(G.keys(), H.keys()))[i:]:
                u = vertexA[0]
                up = vertexA[1]
                v = vertexB[0]
                vp = vertexB[1]
                # distinct vertices (u,u') and (v,v') are adjacent iff:
                # u=v and u' is adjacent to v'
                if u == v and up in H[vp]:
                    if vertexA not in nodeDict:
                        nodeDict[vertexA] = [vertexB]
                    else:
                        nodeDict[vertexA] += [vertexB]
                    if vertexB not in nodeDict:
                        nodeDict[vertexB] = [vertexA]
                    else:
                        nodeDict[vertexB] += [vertexA]
                # u'=v' and u is adjacent to v
                if up == vp and u in G[v]:
                    if vertexA not in nodeDict:
                        nodeDict[vertexA] = [vertexB]
                    else:
                        nodeDict[vertexA] += [vertexB]
                    if vertexB not in nodeDict:
                        nodeDict[vertexB] = [vertexA]
                    else:
                        nodeDict[vertexB] += [vertexA]
        return nodeDict

    # https://en.wikipedia.org/wiki/Lexicographic_product_of_graphs
    @staticmethod
    def lexicographicProduct(G, H, fromScratch=True):
        vertexSet = itertools.product(G.keys(), H.keys())
        print(list(vertexSet))
        nodeDict = dict()
        for i, vertexA in enumerate(
                list(itertools.product(G.keys(), H.keys()))[:-1]):
            for vertexB in list(itertools.product(G.keys(), H.keys()))[i:]:
                u = vertexA[0]
                up = vertexA[1]
                v = vertexB[0]
                vp = vertexB[1]
                # distinct vertices (u,u') and (v,v') are adjacent iff:
                # u is adjacent to v
                if u in G[v]:
                    if vertexA not in nodeDict:
                        nodeDict[vertexA] = [vertexB]
                    else:
                        nodeDict[vertexA] += [vertexB]
                    if vertexB not in nodeDict:
                        nodeDict[vertexB] = [vertexA]
                    else:
                        nodeDict[vertexB] += [vertexA]
                # u=v and u' is adjacent to v'
                if u == v and up in H[vp]:
                    if vertexA not in nodeDict:
                        nodeDict[vertexA] = [vertexB]
                    else:
                        nodeDict[vertexA] += [vertexB]
                    if vertexB not in nodeDict:
                        nodeDict[vertexB] = [vertexA]
                    else:
                        nodeDict[vertexB] += [vertexA]
        return nodeDict

    # https://en.wikipedia.org/wiki/Strong_product_of_graphs
    @staticmethod
    def strongProduct(G, H, fromScratch=True):
        if fromScratch:
            vertexSet = itertools.product(G.keys(), H.keys())
            print(list(vertexSet))
            nodeDict = dict()
            for i, vertexA in enumerate(
                    list(itertools.product(G.keys(), H.keys()))[:-1]):
                for vertexB in list(itertools.product(G.keys(), H.keys()))[i:]:
                    u = vertexA[0]
                    up = vertexA[1]
                    v = vertexB[0]
                    vp = vertexB[1]
                    # distinct vertices (u,u') and (v,v') are adjacent iff:
                    # u=v and u' is adjacent to v'
                    if u == v and up in H[vp]:
                        if vertexA not in nodeDict:
                            nodeDict[vertexA] = [vertexB]
                        else:
                            nodeDict[vertexA] += [vertexB]
                        if vertexB not in nodeDict:
                            nodeDict[vertexB] = [vertexA]
                        else:
                            nodeDict[vertexB] += [vertexA]
                    # u'=v' and u is adjacent to v
                    if up == vp and u in G[v]:
                        if vertexA not in nodeDict:
                            nodeDict[vertexA] = [vertexB]
                        else:
                            nodeDict[vertexA] += [vertexB]
                        if vertexB not in nodeDict:
                            nodeDict[vertexB] = [vertexA]
                        else:
                            nodeDict[vertexB] += [vertexA]
                    # u is adjacent to v and u' is adjacent to v'
                    if u in G[v] and up in H[vp]:
                        if vertexA not in nodeDict:
                            nodeDict[vertexA] = [vertexB]
                        else:
                            nodeDict[vertexA] += [vertexB]
                        if vertexB not in nodeDict:
                            nodeDict[vertexB] = [vertexA]
                        else:
                            nodeDict[vertexB] += [vertexA]
            return nodeDict
        else:
            return GraphColoring.merged(GraphColoring.cartesianProduct(G, H),
                                        GraphColoring.tensorProduct(G, H))

    # https://en.wikipedia.org/wiki/Tensor_product_of_graphs
    @staticmethod
    def tensorProduct(G, H):
        vertexSet = itertools.product(G.keys(), H.keys())
        print(list(vertexSet))
        nodeDict = dict()
        for i, vertexA in enumerate(
                list(itertools.product(G.keys(), H.keys()))[:-1]):
            for vertexB in list(itertools.product(G.keys(), H.keys()))[i:]:
                u = vertexA[0]
                up = vertexA[1]
                v = vertexB[0]
                vp = vertexB[1]
                # distinct vertices (u,u') and (v,v') are adjacent iff:
                # u is adjacent to v and u' is adjacent to v'
                if u in G[v] and up in H[vp]:
                    if vertexA not in nodeDict:
                        nodeDict[vertexA] = [vertexB]
                    else:
                        nodeDict[vertexA] += [vertexB]
                    if vertexB not in nodeDict:
                        nodeDict[vertexB] = [vertexA]
                    else:
                        nodeDict[vertexB] += [vertexA]
        return nodeDict

    # Graph corresponding to the United States state border connections
    # https://writeonly.wordpress.com/2009/03/20/adjacency-list-of-states-of-the-united-states-us/
    # which disagrees with http://mathworld.wolfram.com/ContiguousUSAGraph.html
    @staticmethod
    def US(contiguous=False):
        nodeDict = {
            ('AK',): (),\
            ('AL',): (('MS',),('TN',),('GA',),('FL',),),\
            ('AR',): (('MO',),('TN',),('MS',),('LA',),('TX',),('OK',),),\
            ('AZ',): (('CA',),('NV',),('UT',),('NM',),),\
            ('CA',): (('OR',),('NV',),('AZ',),),\
            ('CO',): (('WY',),('NE',),('KS',),('OK',),('NM',),('UT',),),\
            ('CT',): (('NY',),('MA',),('RI',),),\
            ('DC',): (('MD',),('VA',),),\
            ('DE',): (('MD',),('PA',),('NJ',),),\
            ('FL',): (('AL',),('GA',),),\
            ('GA',): (('FL',),('AL',),('TN',),('NC',),('SC',),),\
            ('HI',): (),\
            ('IA',): (('MN',),('WI',),('IL',),('MO',),('NE',),('SD',),),\
            ('ID',): (('MT',),('WY',),('UT',),('NV',),('OR',),('WA',),),\
            ('IL',): (('IN',),('KY',),('MO',),('IA',),('WI',),),\
            ('IN',): (('MI',),('OH',),('KY',),('IL',),),\
            ('KS',): (('NE',),('MO',),('OK',),('CO',),),\
            ('KY',): (('IN',),('OH',),('WV',),('VA',),('TN',),('MO',),('IL',),),\
            ('LA',): (('TX',),('AR',),('MS',),),\
            ('MA',): (('RI',),('CT',),('NY',),('NH',),('VT',),),\
            ('MD',): (('VA',),('WV',),('PA',),('DC',),('DE',),),\
            ('ME',): (('NH',),),\
            ('MI',): (('WI',),('IN',),('OH',),),\
            ('MN',): (('WI',),('IA',),('SD',),('ND',),),\
            ('MO',): (('IA',),('IL',),('KY',),('TN',),('AR',),('OK',),('KS',),('NE',),),\
            ('MS',): (('LA',),('AR',),('TN',),('AL',),),\
            ('MT',): (('ND',),('SD',),('WY',),('ID',),),\
            ('NC',): (('VA',),('TN',),('GA',),('SC',),),\
            ('ND',): (('MN',),('SD',),('MT',),),\
            ('NE',): (('SD',),('IA',),('MO',),('KS',),('CO',),('WY',),),\
            ('NH',): (('VT',),('ME',),('MA',),),\
            ('NJ',): (('DE',),('PA',),('NY',),),\
            ('NM',): (('AZ',),('CO',),('OK',),('TX',),),\
            ('NV',): (('ID',),('UT',),('AZ',),('CA',),('OR',),),\
            ('NY',): (('NJ',),('PA',),('VT',),('MA',),('CT',),),\
            ('OH',): (('PA',),('WV',),('KY',),('IN',),('MI',),),\
            ('OK',): (('KS',),('MO',),('AR',),('TX',),('NM',),('CO',),),\
            ('OR',): (('CA',),('NV',),('ID',),('WA',),),\
            ('PA',): (('NY',),('NJ',),('DE',),('MD',),('WV',),('OH',),),\
            ('RI',): (('CT',),('MA',),),\
            ('SC',): (('GA',),('NC',),),\
            ('SD',): (('ND',),('MN',),('IA',),('NE',),('WY',),('MT',),),\
            ('TN',): (('KY',),('VA',),('NC',),('GA',),('AL',),('MS',),('AR',),('MO',),),\
            ('TX',): (('NM',),('OK',),('AR',),('LA',),),\
            ('UT',): (('ID',),('WY',),('CO',),('AZ',),('NV',),),\
            ('VA',): (('NC',),('TN',),('KY',),('WV',),('MD',),('DC',),),\
            ('VT',): (('NY',),('NH',),('MA',),),\
            ('WA',): (('ID',),('OR',),),\
            ('WI',): (('MI',),('MN',),('IA',),('IL',),),\
            ('WV',): (('OH',),('PA',),('MD',),('VA',),('KY',),),\
            ('WY',): (('MT',),('SD',),('NE',),('CO',),('UT',),('ID',),)\
        }
        if contiguous:
            del nodeDict[('AK', )]
            del nodeDict[('HI', )]
        return nodeDict
Ejemplo n.º 8
0
 def __init__(self, height=0, width=0):
     self.height = height
     self.width = width
     self.game = None
     self.literalAllocator = LiteralAllocator()
     self.cnf = CNF()
Ejemplo n.º 9
0
class PegSolitaire:
    def __init__(self, height=0, width=0):
        self.height = height
        self.width = width
        self.game = None
        self.literalAllocator = LiteralAllocator()
        self.cnf = CNF()

    def Test1D(self):
        self.height = 1
        self.width = 7
        numGen = 3
        self.game = PegGameInstance(height=self.height, width=self.width)
        self.game.boards = [
            PegBoardState(height=self.height, width=self.width, time=t)
            for t in range(numGen)
        ]
        self.literalAllocator = LiteralAllocator()
        # Start state
        for i in range(self.width):
            if i != 3:
                self.game[0][0][i].state = PegState.ALIVE
            else:
                self.game[0][0][i].state = PegState.DEAD
        # Intermediate states
        for t in range(1, numGen):
            for i in range(self.width):
                self.game[t][0][i].state = PegState.DONTCARE

    def DefaultGame(self):
        self.height = 7
        self.width = 7
        self.game = PegGameInstance(height=7, width=7)
        self.game.boards = [
            PegBoardState(height=7, width=7, time=t) for t in range(31)
        ]
        self.literalAllocator = LiteralAllocator()
        # Start state
        for i in range(7):
            for j in range(7):
                if i in range(2, 5) or j in range(2, 5):
                    if i == j and i == 3:
                        self.game[0][i][j].state = PegState.DEAD
                    else:
                        self.game[0][i][j].state = PegState.ALIVE
        # Intermediate states
        for t in range(1, 30):
            for i in range(7):
                for j in range(7):
                    if i in range(2, 5) or j in range(2, 5):
                        self.game[t][i][j].state = PegState.DONTCARE
        # Final state
        for i in range(7):
            for j in range(7):
                if i in range(2, 5) or j in range(2, 5):
                    if i == j and i == 3:
                        self.game[30][i][j].state = PegState.ALIVE
                    else:
                        self.game[30][i][j].state = PegState.DEAD

    def BasicTest(self):
        self.height = 5
        self.width = 5
        numGen = 3
        self.game = PegGameInstance(height=self.height, width=self.width)
        self.game.boards = [
            PegBoardState(height=self.height, width=self.width, time=t)
            for t in range(numGen)
        ]
        self.literalAllocator = LiteralAllocator()
        # Start state
        for i in range(self.height):
            for j in range(self.width):
                if i == j and i == 2:
                    self.game[0][i][j].state = PegState.DEAD
                else:
                    self.game[0][i][j].state = PegState.ALIVE
        # Intermediate states
        for t in range(1, numGen):
            for i in range(self.height):
                for j in range(self.width):
                    self.game[t][i][j].state = PegState.DONTCARE

    def assignLiterals(self):
        for t in range(len(self.game.boards)):
            for i in range(self.height):
                for j in range(self.width):
                    if self.game[t][i][j].state != PegState.DNE:
                        self.game[t][i][
                            j].variable = self.literalAllocator.getLiteral()
        print(self.game)

    def FillInSequence(self):
        sequence = self.game.boards
        if len(sequence) <= 1:
            raise Exception('A sequence needs at least 2 elements!')
        for board in sequence:
            assert isinstance(board, PegBoardState)
            assert board.height == sequence[0].height and board.width == sequence[0].width, \
                'Cannot compare boards with different dimensions.'
        self.assignLiterals()
        height = sequence[0].height
        width = sequence[0].width

        self.assertTransitions()
        self.assertOneMovePerTurn()
        self.assertFixedStates()

        for solution in pycosat.itersolve(self.cnf.rawCNF()):
            updatedSequence = []
            for tiling in sequence:
                newTilingA = PegBoardState(tiling.height, tiling.width,
                                           tiling.time)

                for row in range(tiling.height):
                    for col in range(tiling.width):
                        if tiling[row][col].state != PegState.DNE:
                            if tiling[row][col].variable in solution:
                                if tiling[row][
                                        col].state == PegState.ALIVE or tiling[
                                            row][
                                                col].state == PegState.DONTCARE:
                                    # This means that we either forced the cell to be alive or we derived a possible value
                                    newTilingA[row][col].state = PegState.ALIVE
                                else:
                                    # raise Exception("Computed state is incompatible with original state")
                                    pass
                            elif -tiling[row][col].variable in solution:
                                if tiling[row][
                                        col].state == PegState.DEAD or tiling[
                                            row][
                                                col].state == PegState.DONTCARE:
                                    # This means that we either forced the cell to be dead or we derived a possible value
                                    newTilingA[row][col].state = PegState.DEAD
                                    pass
                                else:
                                    # raise Exception("Computed state is incompatible with original state")
                                    pass
                            else:
                                raise Exception(
                                    "Input wasn't even in the solution! Something is clearly wrong here."
                                )
                updatedSequence.append(newTilingA)
            gameSolution = PegGameInstance()
            gameSolution.SetFrames(updatedSequence)
            print(gameSolution)
            break

    def assertTransitions(self):
        self.assertAllTriplets()

    def assertFixedStates(self):
        fixedVals = []
        for t in range(1):  #len(self.game.boards)):
            for row in range(self.height):
                for col in range(self.width):
                    if self.game.boards[t][row][col].state == PegState.ALIVE:
                        fixedVals.append(
                            self.game.boards[t][row][col].variable)
                    if self.game.boards[t][row][col].state == PegState.DEAD:
                        fixedVals.append(
                            -self.game.boards[t][row][col].variable)
        self.cnf.mergeWithRaw([[x] for x in fixedVals])

    def assertTriplet(self, jumper, skipped, endpoint, time):
        a = self.game.boards[time][jumper[0]][jumper[1]].variable
        b = self.game.boards[time][skipped[0]][skipped[1]].variable
        c = self.game.boards[time][endpoint[0]][endpoint[1]].variable
        x = self.game.boards[time + 1][jumper[0]][jumper[1]].variable
        y = self.game.boards[time + 1][skipped[0]][skipped[1]].variable
        z = self.game.boards[time + 1][endpoint[0]][endpoint[1]].variable
        clauses = [[-a, -b, c, -x, y], [-a, -b, c, -x, -z], [-a, -b, c, x, -y],
                   [-a, -b, c, -y, -z], [-a, -b, c, x, z], [-a, -b, c, y, z]]
        self.cnf.mergeWithRaw(clauses)

    def assertAllTriplets(self):
        for t in range(len(self.game.boards) - 1):
            for row in range(self.height):
                for col in range(self.width):
                    for direction in [[-1, 0], [1, 0], [0, -1], [0, 1]]:
                        xDir = direction[0]
                        yDir = direction[1]
                        jumper = [row, col]
                        skipped = [row + xDir, col + yDir]
                        endpoint = [row + 2 * xDir, col + 2 * yDir]
                        if self.isValidTriple(jumper, skipped, endpoint):
                            self.assertTriplet(jumper, skipped, endpoint, t)

    def assertOneMovePerTurn(self):
        initPop = 0
        for row in range(self.height):
            for col in range(self.width):
                if self.game.boards[0][row][col].state == PegState.ALIVE:
                    initPop += 1

        for t in range(len(self.game.boards)):
            boardLiterals = []
            for row in range(self.height):
                for col in range(self.width):
                    if self.game.boards[t][row][col].state != PegState.DNE:
                        boardLiterals.append(
                            self.game.boards[t][row][col].variable)
            # Try swapping for SATUtils.atLeast to check for faster speeds
            newClauses, highestLiteral = SATUtils.exactlyR(
                boardLiterals,
                initPop - t,
                startLiteral=self.literalAllocator.getCurrLiteral())
            if highestLiteral >= self.literalAllocator.getCurrLiteral():
                self.literalAllocator.getLiterals(
                    highestLiteral - self.literalAllocator.getCurrLiteral() +
                    1)
            self.cnf.mergeWithRaw(newClauses)

    def isValidTriple(self, jumper, skipped, endpoint):
        if endpoint[0] not in range(self.width) or endpoint[1] not in range(
                self.height):
            return False
        if skipped[0] not in range(self.width) or skipped[1] not in range(
                self.height):
            return False
        if self.game.boards[0][jumper[0]][jumper[1]].state == PegState.DNE:
            return False
        if self.game.boards[0][skipped[0]][skipped[1]].state == PegState.DNE:
            return False
        if self.game.boards[0][endpoint[0]][endpoint[1]].state == PegState.DNE:
            return False
        return True