Exemplo n.º 1
0
def sameCount(E, partialCount, boardColor):

  # Final partial counts should be equal to the full count
  for c in range(rowNum * columnNum + 1):
      E.add_constraint(iff(totalCount[c], partialCount[rowNum- 1][columnNum - 1][c]))

  # You can't have more pieces than you've already seen
  for i in range(rowNum):
      for j in range(columnNum):
        for c in range((i * 7) + j + 2,rowNum * columnNum + 1):
          E.add_constraint(~partialCount[i][j][c])

  # First index: only black piece or red piece could possibly be true
  E.add_constraint(iff(partialCount[0][0][0], ~boardColor[0][0]))
  E.add_constraint(iff(partialCount[0][0][1], boardColor[0][0]))

  #General pattern: Looks at the other color pieces to decide the current color piece.
  for x in range(1, rowNum * columnNum):
      i = x // columnNum
      j = x % columnNum
      E.add_constraint(iff(partialCount[i][j][0], partialCount[(i-1) if (j==0) else i][(columnNum-1) if (j==0) else (j-1)][0] & ~boardColor[i][j]))
      for c in range(1,x+2):
          increased = partialCount[(i-1) if (j==0) else i][(columnNum-1) if (j==0) else (j-1)][c-1] & boardColor[i][j]
          stay_same = partialCount[(i-1) if (j==0) else i][(columnNum-1) if (j==0) else (j-1)][c] & ~boardColor[i][j]
          E.add_constraint(iff(partialCount[i][j][c], increased | stay_same))
  return E
Exemplo n.º 2
0
def set_truth_encodings():
    """
    Sets the constraints required to determine the state of the middle square
    """

    # If there are any adjacent squares with a True y condition, the middle square
    # is a mine. See the boolean condition guide (top) for an explanation
    E.add_constraint(
        iff((y[1][1] | y[1][2] | y[1][3] | y[2][1] | y[2][3]
             | y[3][1] | y[3][2] | y[3][3]), m[2][2]))
    # If there are any adjacent squares with a True x condition, the middle square
    # is safe. See the boolean condition guide (top) for an explanation
    E.add_constraint(
        iff((x[1][1] | x[1][2] | x[1][3] | x[2][1] | x[2][3]
             | x[3][1] | x[3][2] | x[3][3]), s[2][2]))
    # If the middle square isn't a mine or safe, it is unknown
    E.add_constraint(iff(~m[2][2] & ~s[2][2], u[2][2]))
Exemplo n.º 3
0
def validBoard(E):
  for i in range(rowNum):
    for j in range(columnNum): 

      # If position(i, j) is empty, then neither black or red can occupy position(i, j).
      E.add_constraint(emptyBoard[i][j] >> (~redBoard[i][j] & ~blackBoard[i][j]))

      # If position(i, j) is red, then neither black and empty can occupy position(i, j)
      # Calls gravity constraint
      E.add_constraint(redBoard[i][j] >> (~blackBoard[i][j] & ~emptyBoard[i][j] & gravityRule(i, j)))

      # If position(i, j) is black, then neither red and empty can occupy position(i, j)
      # Calls gravity constraint
      E.add_constraint(blackBoard[i][j] >> (~redBoard[i][j] & ~emptyBoard[i][j] & gravityRule(i, j)))

      # Here to make sure implication works properly above, exactly one has to be true.
      E.add_constraint(emptyBoard[i][j] | redBoard[i][j] | blackBoard[i][j])

  # General: ColorWin if and only if there is ColorRowWin, or  
  # ColorColumnWin, or ColorDiagonalWin.
  E.add_constraint(iff(BlackWin, (BlackColumnWin() | BlackRowWin() | leftBlackDiagonalWin() | rightBlackDiagonalWin())))
  E.add_constraint(iff(RedWin, (RedColumnWin() | RedRowWin() | leftRedDiagonalWin() | rightRedDiagonalWin())))

  # General: NoWin if and only if there is notColorRowWin, and 
  # notColorColumnWin, and notColorDiagonalWin.
  E.add_constraint(iff(NoWin, ((~BlackColumnWin() & ~BlackRowWin() & ~leftBlackDiagonalWin() & ~rightBlackDiagonalWin()) & (~RedColumnWin() & ~RedRowWin() & ~leftRedDiagonalWin() & ~rightRedDiagonalWin()))))
  
  #All posibilities of Connect Four Game outcome
  E.add_constraint(iff(BlackWin, (~RedWin & ~NoWin)))
  E.add_constraint(iff(RedWin, (~BlackWin & ~NoWin)))
  E.add_constraint(iff(NoWin, (~RedWin & ~BlackWin)))

  return E
Exemplo n.º 4
0
def columnWin(E, winColumnColor, boardColor):
  for i in range(rowNum - 3):
    for j in range(columnNum):
      # Winning column and its position of either 4 red or 4 black slots within the column. 
      E.add_constraint(iff(winColumnColor[i][j], (boardColor[i][j] & boardColor[i+1][j] & boardColor[i+2][j] & boardColor[i+3][j])))

      # Checks that there is a possible route to play in order to win by a column unless top row
      if (i > 0):
        E.add_constraint(winColumnColor[i][j] >> (emptyBoard[i-1][j]))
      
      #Checks that only one column can win
      special = winColumnColor[i][j]
      false = ~true
      for i2 in range(rowNum - 3):
        for j2 in range(columnNum):
          if (i != i2) | (j != j2):
            false |= winColumnColor[i2][j2]
      E.add_constraint(special >> ~false)
  return E
Exemplo n.º 5
0
def rowWin(E, winRowColor, boardColor):
  for i in range(rowNum):
    for j in range(columnNum - 3):
      #Winning row and its position of either 4 red or 4 black slots within the row. 
      E.add_constraint(iff(winRowColor[i][j], (boardColor[i][j] & boardColor[i][j + 1] & boardColor[i][j + 2] & boardColor[i][j + 3]))) 

      #Checks that there is at least one possible route to play in order to win by a row unless top row
      if (i > 0):
        E.add_constraint(winRowColor[i][j] >> (emptyBoard[i-1][j] | emptyBoard[i-1][j + 1] | emptyBoard[i-1][j + 2] | emptyBoard[i-1][j + 3]))


      #Checks that only a single row channel can be a winning row channel
      special = winRowColor[i][j]
      false = ~true
      for i2 in range(rowNum):
        for j2 in range(columnNum - 3):
          if (i != i2):
            false |= winRowColor[i2][j2]
      E.add_constraint(special >> ~false)
  return E
Exemplo n.º 6
0
def rightDiagonalWin(E, rightWinDiagonalColor, boardColor):
  for i in range(rowNum - 3):
    for j in range(columnNum - 4, columnNum):
      #Winning diagonal going left and down.
      E.add_constraint(iff(rightWinDiagonalColor[i][j-3], (boardColor[i][j] & boardColor[i+1][j-1] & boardColor[i+2][j-2] & boardColor[i+3][j-3])))

      # Checks that there is a possible route to play in order to win by a right diagonal unless top row
      if (i > 0):
        E.add_constraint(rightWinDiagonalColor[i][j-3] >> (emptyBoard[i-1][j] | emptyBoard[i][j-1] | emptyBoard[i+1][j - 2] | emptyBoard[i+2][j - 3]))

      # Only one right facing diagonal channel can be a winning diagonal channel
      special = rightWinDiagonalColor[i][j-3]
      false = ~true
      for i2 in range(rowNum - 3):
        for j2 in range(columnNum - 4, columnNum):
          if (i != i2) | (j != j2):
            if (i - 1 != i2) | (j + 1 != j2):
              if (i - 2 != i2) | (j + 2 != j2):
                 if (i + 1 != i2) | (j - 1 != j2):
                   if (i + 2 != i2) | (j - 2 != j2):
                    false |= rightWinDiagonalColor[i2][j2-3]
      E.add_constraint(special >> ~false)
  return E
Exemplo n.º 7
0
def test_iff(a: nnf.NNF, b: nnf.NNF):
    c = operators.iff(a, b)
    for model in nnf.all_models(c.vars()):
        assert ((a.satisfied_by(model) == b.satisfied_by(model)
                 ) == c.satisfied_by(model))
Exemplo n.º 8
0
def encode_circuit_search(original_theory, num_gates, models=[]):

    ########
    # Vars #
    ########

    # Inputs to the circuit are the variables of the input theory
    inputs = [Var(v) for v in original_theory.vars()]
    if models:
        input_clones = clone_varset(models, inputs, inputs)
    else:
        input_clones = {}

    # Gates are either & | or ~
    gates = []
    gate_modalities = {}
    for i in range(num_gates):
        gates.append(Var(Gate(i)))
        gate_modalities[gates[-1]] = {}
        for m in ['and', 'or', 'not']:
            gate_modalities[gates[-1]][m] = Var(GateType(gates[-1], m))

    if models:
        gate_clones = clone_varset(models, inputs, gates)
    else:
        gate_clones = {}

    # Single (arbitrary) gate is the circuit output
    output = gates[0]

    # Connections between inputs/gates to gates, and transitivity
    connections = {}
    unconnections = {}
    for src in inputs + gates[1:]:
        connections[src] = {}
        for dst in gates:
            if src != dst:
                connections[src][dst] = Var(Connection(src, dst))
                if dst not in unconnections:
                    unconnections[dst] = {}
                unconnections[dst][src] = connections[src][dst]
    C = connections # convenience

    orders = {}
    for src in gates:
        orders[src] = {}
        for dst in gates:
            orders[src][dst] = Var(Order(src,dst))



    ###############
    # Constraints #
    ###############

    ''' add decorators for simplifying adding constraints (i.e. a conjunct)'''
    conjuncts = []

    # Orderings (to forbid cycles in the circuit)
    for g1 in gates:
        # Connection implies orders
        for src in connections:
            for dst in connections[src]:
                if isinstance(src.name, Gate) and isinstance(dst.name, Gate):
                    conjuncts.append(~connections[src][dst] | orders[src][dst])
        # Can't order before yourself
        conjuncts.append(~orders[g1][g1])
        # Transitive closure
        for g2 in gates:
            for g3 in gates:
                conjuncts.append(~orders[g1][g2] | ~orders[g2][g3] | orders[g1][g3])

    if FORCE_TREE:
        # At max one outgoing connection on a gate
        for src in gates[1:]:
            for dst1 in connections[src]:
                for dst2 in connections[src]:
                    if dst1 != dst2:
                        conjuncts.append(~connections[src][dst1] | ~connections[src][dst2])

    # Every gate has at least one input
    for dst in unconnections:
        conjuncts.append(Or(unconnections[dst].values()))

    # Every gate has at most two inputs and negation gates have at most one
    for j in gates:
        for i1 in inputs+gates[1:]:
            for i2 in inputs+gates[1:]:
                if not unique([j,i1,i2]):
                    continue
                conjuncts.append(~gate_modalities[j]['not'] | ~C[i1][j] | ~C[i2][j])
                for i3 in inputs+gates[1:]:
                    if not unique([j,i1,i2,i3]):
                        continue
                    conjuncts.append(~C[i1][j] | ~C[i2][j] | ~C[i3][j])

    # Every gate has exactly one modality
    for g in gates:
        # At least one
        conjuncts.append(Or(gate_modalities[g].values()))

        # At most one
        for m1 in ['and', 'or', 'not']:
            remaining = set(['and', 'or', 'not']) - set([m1])
            conjuncts.append(Or([~gate_modalities[g][m2] for m2 in remaining]))

    # Re-usable theories
    notneg_cache = {}
    def notneg(src, dst, cloned_src=None):
        if not cloned_src:
            cloned_src = src
        if (cloned_src,dst) not in notneg_cache:
            notneg_cache[(cloned_src,dst)] = ~C[src][dst] | cloned_src
        return notneg_cache[(cloned_src,dst)]

    notpos_cache = {}
    def notpos(src, dst, cloned_src=None):
        if not cloned_src:
            cloned_src = src
        if (cloned_src,dst) not in notpos_cache:
            notpos_cache[(cloned_src,dst)] = ~C[src][dst] | ~cloned_src
        return notpos_cache[(cloned_src,dst)]

    # Implement the gates
    for g in gates:

        ins = [i for i in inputs+gates[1:] if i != g]

        conjuncts.append(~gate_modalities[g]['and'] | iff(g, And([notneg(src,g) for src in ins])))

        t = Or([notpos(src,g).negate() for src in ins])
        conjuncts.append(~gate_modalities[g]['or'] | iff(g, t))

        conjuncts.append(~gate_modalities[g]['not'] | iff(g, t.negate()))

        for m in models:
            bitvec = model_to_bitvec(m, inputs)
            orig_mapping = {**{input_clones[bitvec][i]: i for i in inputs if i != g},
                            **{gate_clones[bitvec][i]: i for i in gates[1:] if i != g}}
            ins = orig_mapping.keys()

            conjuncts.append(~gate_modalities[g]['and'] | iff(gate_clones[bitvec][g],
                     And([notneg(orig_mapping[src],g,src) for src in ins])))

            t = Or([notpos(orig_mapping[src],g,src).negate() for src in ins])
            conjuncts.append(~gate_modalities[g]['or'] | iff(gate_clones[bitvec][g], t))

            conjuncts.append(~gate_modalities[g]['not'] | iff(gate_clones[bitvec][g], t.negate()))


    # Finally, lock in the models
    if models:
        for m in models:
            bitvec = model_to_bitvec(m, inputs)
            for var in m:
                if m[var]:
                    conjuncts.append(input_clones[bitvec][Var(var)])
                else:
                    conjuncts.append(~input_clones[bitvec][Var(var)])
            if original_theory.satisfied_by(m):
                conjuncts.append(gate_clones[bitvec][output])
            else:
                conjuncts.append(~gate_clones[bitvec][output])
    else:
        for model in all_models(original_theory.vars()):

            t = false # negating the conjunction because of the implication: flips the signs
            for var,val in model.items():
                if val:
                    t |= ~Var(var)
                else:
                    t |= Var(var)
            if original_theory.satisfied_by(model):
                t |= output
            else:
                t |= ~output
            conjuncts.append(t)


    versions = {}
    example = {}
    for c in conjuncts:
        cn = c.simplify().to_CNF()
        stats = "(%d / %d / %d) > (%d / %d / %d)" % (c.simplify().size(), c.simplify().height(), len(c.simplify().vars()),
                                                     cn.size(), cn.height(), len(cn.vars()))
        example[stats] = str(c.simplify())
        versions[stats] = versions.get(stats, 0) + 1
    print("Conjunct stats:")
    for k in versions:
        print("\n - (%d) %s: %s" % (versions[k], k, example[k]))

    T = And(conjuncts)
    return T.simplify(), inputs, gates, output, connections, unconnections, gate_modalities
    def build_general_constraints(self):
        E = self.theory
        t = self.props.t
        x = self.props.x
        r = self.props.r
        c = self.props.c
        A = self.props.A

        # Checking that each row and column have the correct number of tents
        # Will need to be changed from exhaustive listing to some adder later
        # This also verifies that the row and column hints are set properly
        for i in range(self.num_rows):
            # Ensure only one of r[i][...] is set
            E.add_constraint(( r[i][0] & ~r[i][1] & ~r[i][2] & ~r[i][3]) | \
                             (~r[i][0] &  r[i][1] & ~r[i][2] & ~r[i][3]) | \
                             (~r[i][0] & ~r[i][1] &  r[i][2] & ~r[i][3]) | \
                             (~r[i][0] & ~r[i][1] & ~r[i][2] &  r[i][3]))
            # no tents
            E.add_constraint(iff(r[i][0],  ~x[i][0] & ~x[i][1] & ~x[i][2] & ~x[i][3] & ~x[i][4]))
            # 1 tent
            E.add_constraint(iff(r[i][1], ( x[i][0] & ~x[i][1] & ~x[i][2] & ~x[i][3] & ~x[i][4]) | \
                                          (~x[i][0] &  x[i][1] & ~x[i][2] & ~x[i][3] & ~x[i][4]) | \
                                          (~x[i][0] & ~x[i][1] &  x[i][2] & ~x[i][3] & ~x[i][4]) | \
                                          (~x[i][0] & ~x[i][1] & ~x[i][2] &  x[i][3] & ~x[i][4]) | \
                                          (~x[i][0] & ~x[i][1] & ~x[i][2] & ~x[i][3] &  x[i][4])))
            # 2 tents
            E.add_constraint(iff(r[i][2], ( x[i][0] & ~x[i][1] &  x[i][2] & ~x[i][3] & ~x[i][4]) | \
                                          ( x[i][0] & ~x[i][1] & ~x[i][2] &  x[i][3] & ~x[i][4]) | \
                                          ( x[i][0] & ~x[i][1] & ~x[i][2] & ~x[i][3] &  x[i][4]) | \
                                          (~x[i][0] &  x[i][1] & ~x[i][2] &  x[i][3] & ~x[i][4]) | \
                                          (~x[i][0] &  x[i][1] & ~x[i][2] & ~x[i][3] &  x[i][4]) | \
                                          (~x[i][0] & ~x[i][1] &  x[i][2] & ~x[i][3] &  x[i][4])))
            # 3 tents
            E.add_constraint(iff(r[i][3],   x[i][0] & ~x[i][1] &  x[i][2] & ~x[i][3] &  x[i][4]))

        for j in range(self.num_cols):
            # Ensure only one of c[j][...] is set
            E.add_constraint(( c[j][0] & ~c[j][1] & ~c[j][2] & ~c[j][3]) | \
                             (~c[j][0] &  c[j][1] & ~c[j][2] & ~c[j][3]) | \
                             (~c[j][0] & ~c[j][1] &  c[j][2] & ~c[j][3]) | \
                             (~c[j][0] & ~c[j][1] & ~c[j][2] &  c[j][3]))
            # no tents
            E.add_constraint(iff(c[j][0],  ~x[0][j] & ~x[1][j] & ~x[2][j] & ~x[3][j] & ~x[4][j]))
            # 1 tent
            E.add_constraint(iff(c[j][1], ( x[0][j] & ~x[1][j] & ~x[2][j] & ~x[3][j] & ~x[4][j]) | \
                                          (~x[0][j] &  x[1][j] & ~x[2][j] & ~x[3][j] & ~x[4][j]) | \
                                          (~x[0][j] & ~x[1][j] &  x[2][j] & ~x[3][j] & ~x[4][j]) | \
                                          (~x[0][j] & ~x[1][j] & ~x[2][j] &  x[3][j] & ~x[4][j]) | \
                                          (~x[0][j] & ~x[1][j] & ~x[2][j] & ~x[3][j] &  x[4][j])))
            # 2 tents
            E.add_constraint(iff(c[j][2], ( x[0][j] & ~x[1][j] &  x[2][j] & ~x[3][j] & ~x[4][j]) | \
                                          ( x[0][j] & ~x[1][j] & ~x[2][j] &  x[3][j] & ~x[4][j]) | \
                                          ( x[0][j] & ~x[1][j] & ~x[2][j] & ~x[3][j] &  x[4][j]) | \
                                          (~x[0][j] &  x[1][j] & ~x[2][j] &  x[3][j] & ~x[4][j]) | \
                                          (~x[0][j] &  x[1][j] & ~x[2][j] & ~x[3][j] &  x[4][j]) | \
                                          (~x[0][j] & ~x[1][j] &  x[2][j] & ~x[3][j] &  x[4][j])))
            # 3 tents
            E.add_constraint(iff(c[j][3],   x[0][j] & ~x[1][j] &  x[2][j] & ~x[3][j] &  x[4][j]))

        for i in range(self.num_rows):
            for j in range(self.num_cols):
                ### Constrain that tents and trees can't be in the same square
                E.add_constraint((~x[i][j] & ~t[i][j]) | (~x[i][j] &  t[i][j]) | ( x[i][j] & ~t[i][j]))

                ### Constrain that tents can't be adjacent
                # For each cell in the grid, if there is a tent there, then there can't be one:
                # to the right, below, below and to the right, or below and to the left.
                # I.e.
                #  |x|1
                # -+-+-
                # 4|2|3
                # If x is a tent, then 1,2,3,4 must not be tents.
                # We need to be careful to not exceed the bounds of our arrays however.
                adjacent_cells = []
                # Check if 1 is in bounds
                if j < (self.num_cols - 1):
                    adjacent_cells.append(x[i][j+1])
                # Check if 2 is in bounds
                if i < (self.num_rows - 1):
                    adjacent_cells.append(x[i+1][j])
                # 3 is in bounds iff both 1 and 2 are in bounds.
                if len(adjacent_cells) == 2:
                    adjacent_cells.append(x[i+1][j+1])
                # Check if 4 is in bounds
                if i < (self.num_rows - 1) and j > 0:
                    adjacent_cells.append(x[i+1][j-1])
                # Reduce adjacent cell list into formula saying that those cells are false
                # (if we have any adjacent cells)
                if len(adjacent_cells) > 0:
                    formula = reduce(lambda acc,x: acc & x, (~x for x in adjacent_cells))
                    E.add_constraint(x[i][j] >> formula)

                ### Constrain that tent must be adjacent to tree
                # For each cell in the grid, if there is a tent there, then there must be
                # a tree adjacent.
                # I.e.
                #  |1|
                # -+-+-
                # 3|x|4
                # -+-+-
                #  |2|
                # If x is a tent, then one (or more) of 1,2,3,4 must be a tree.
                # We need to be careful to not exceed the bounds of our arrays however.
                adjacent_cells = []
                # Check if 1 is in bounds
                if i > 0:
                    adjacent_cells.append(t[i-1][j])
                # Check if 2 is in bounds
                if i < (self.num_rows - 1):
                    adjacent_cells.append(t[i+1][j])
                # Check if 3 is in bounds
                if j > 0:
                    adjacent_cells.append(t[i][j-1])
                # Check if 4 is in bounds
                if j < (self.num_cols - 1):
                    adjacent_cells.append(t[i][j+1])
                # Reduce adjacent cell list into formula saying that one of those cells is true
                # (if we have any adjacent cells)
                if len(adjacent_cells) > 0:
                    formula = reduce(lambda acc,x: acc | x, adjacent_cells)
                    E.add_constraint(x[i][j] >> formula)

                ### Constrain that if t(i,j) is true, then exactly one of A(d,i,j) must be true for all d
                # Get all A(d,i,j) that are valid (not out of bounds)
                adjacency_entries = [A[d][i][j] for d in range(len(TentsAndTreesTheory.DIRECTIONS)) if A[d][i][j] is not None]
                # Build up (A & ~B & ~C) | (~A & B & ~C) | (~A & ~B & C) expression
                parts = []
                for positive_adj in adjacency_entries:
                    # Build individual (A & ~B & ~C) and put it into parts
                    part_formula = positive_adj
                    for other_adj in adjacency_entries:
                        if other_adj == positive_adj:
                            continue
                        part_formula &= ~other_adj
                    parts.append(part_formula)
                # OR all individual parts together
                formula = reduce(lambda acc,x: acc | x, parts)
                E.add_constraint(t[i][j] >> formula)

                ### Constrain that if x(i,j) is true, then exactly one of the A entries pointing at it must be true
                # Get all A(d,i,j) that are valid (not out of bounds)
                adjacency_entries = [self.safe_A(d,i-oi,j-oj) for d,(oi,oj) in enumerate(TentsAndTreesTheory.DIRECTIONS) if self.safe_A(d,i-oi,j-oj) is not None]
                # Build up (A & ~B & ~C) | (~A & B & ~C) | (~A & ~B & C) expression
                parts = []
                for positive_adj in adjacency_entries:
                    # Build individual (A & ~B & ~C) and put it into parts
                    part_formula = positive_adj
                    for other_adj in adjacency_entries:
                        if other_adj == positive_adj:
                            continue
                        part_formula &= ~other_adj
                    parts.append(part_formula)
                # OR all individual parts together
                formula = reduce(lambda acc,x: acc | x, parts)
                E.add_constraint(x[i][j] >> formula)

                ### More adjacency constraints, direction-specific
                for d,(offset_i,offset_j) in enumerate(TentsAndTreesTheory.DIRECTIONS):
                    # Check if grid in this direction exists by checking the A matrix
                    if A[d][i][j] is None:
                        continue
                    ### Constrain that if A(d,i,j) is true, then t(i,j) is true
                    E.add_constraint(A[d][i][j] >> t[i][j])
                    ### Constrain that if A(d,i,j) is true, then x((i,j) + dirn) is true
                    E.add_constraint(A[d][i][j] >> x[i+offset_i][j+offset_j])