Esempio n. 1
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, []
Esempio n. 2
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)
Esempio n. 3
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