예제 #1
0
def test_atmostk():
    for l in range(5, 10):
        for k in range(2, l):
            for e in encs:
                cnf = CardEnc.atmost(lits=list(range(1, l + 1)), bound=k, encoding=getattr(EncType, e))

                # enumerating all models
                with MinisatGH(bootstrap_with=cnf) as solver:
                    for num, model in enumerate(solver.enum_models(), 1):
                        solver.add_clause([-l for l in model[:l]])

                assert num == sum([bincoeff(l, o + 1) for o in range(k)]) + 1, 'wrong number of models for AtMost-{0}-of-{1} ({2})'.format(k, l, e)
예제 #2
0
def solve_sudoku_SAT(sudoku, k):
    #############
    # this solution is adjusted from https://github.com/taufanardi/sudoku-sat-solver/blob/master/Sudoku.py
    # what I have done differently:
    # 1. Adjusted so that it can generate to k-sized problem, not just hardcoded k=3 in the original post
    # 2. Refactored the code to make it more readable and splitted into smaller functions instead of chunk of code
    # 3. Rewrited the `add_distinct_clauses` code to make it more robust and easy to understand
    #############
    # make clauses
    clauses = create_clauses(sudoku, k)
    # append clauses to formula
    formula = CNF()
    for c in clauses:
        formula.append(c)
    # solve the SAT problem
    solver = MinisatGH()
    solver.append_formula(formula)
    answer = solver.solve()
    if not answer:
        return None
    # get the solution
    solution = solver.get_model()
    # reformat the solution into a suduko representation
    for i in range(1, k**2 + 1):
        for j in range(1, k**2 + 1):
            sudoku[i - 1][j - 1] = extract_digit_from_solution(
                i, j, solution, k)
    return sudoku
def solve_sudoku_SAT(sudoku, k):
    num_vertices = k ** 4
    vertices, edges = make_sudoku_graph(k)

    number_colors = k ** 2
    formula = CNF()

    # Assign a positive integer for each propositional variable.
    def var_number(i, c):
        return ((i - 1) * number_colors) + c

    flatten_sudoku = np.array(sudoku).reshape(num_vertices)

    # Clause that ensures that each vertex there is one value.
    for i in range(1, num_vertices + 1):
        clause = []

        sudoku_value = int(flatten_sudoku[i - 1])
        not_assigned = sudoku_value == 0

        if not_assigned:
            for c in range(1, number_colors + 1):
                clause.append(var_number(i, c))
        else:
            clause.append(var_number(i, sudoku_value))

        formula.append(clause)

    # Ensure that only one value is assigned.
    for i in range(1, num_vertices + 1):
        for c1 in range(1, number_colors + 1):
            for c2 in range(c1 + 1, number_colors + 1):
                clause = [-1 * var_number(i, c1), -1 * var_number(i, c2)]
                formula.append(clause)

    # Ensure that the rules of sudoku are kept, no adjacent vertices should have the same color/value.
    for (v1, v2) in edges:
        for c in range(1, number_colors + 1):
            clause = [-1 * var_number(v1, c), -1 * var_number(v2, c)]
            formula.append(clause)

    solver = MinisatGH()
    solver.append_formula(formula)
    answer = solver.solve()

    flatten_vertices = np.array(vertices).reshape(num_vertices)

    if answer:
        print("The sudoku is solved.")
        model = solver.get_model()
        print(model)
        for i in range(1, num_vertices + 1):
            for c in range(1, number_colors + 1):
                if var_number(i, c) in model:
                    flatten_vertices[i - 1] = c

        return flatten_vertices.reshape(k**2, k**2).tolist()
    else:
        print("The sudoku has no solution.")
        return None
예제 #4
0
def test_atmost1():
    encs = list(
        filter(lambda name: not name.startswith('__') and name != 'native',
               dir(EncType)))
    for l in range(10, 20):
        for e in encs:
            cnf = CardEnc.atmost(lits=list(range(1, l + 1)),
                                 bound=1,
                                 encoding=getattr(EncType, e))

            # enumerating all models
            with MinisatGH(bootstrap_with=cnf) as solver:
                for num, model in enumerate(solver.enum_models(), 1):
                    solver.add_clause([-l for l in model[:l]])

            assert num == l + 1, 'wrong number of models for AtMost-1-of-{0} ({1})'.format(
                l, e)
예제 #5
0
def solve_sudoku_SAT(sudoku, k):
    """
        I used the rules described in here 
        http://anytime.cs.umass.edu/aimath06/proceedings/P34.pdf
    """
    k2 = k**2
    N_propositionals = k2**3
    propositions = list(range(1, N_propositionals + 1))

    propositions = np.array(propositions).reshape((k2, k2, k2))
    rules = CNF()

    ## These rules add the values we know to be true
    for i, row in enumerate(sudoku):
        for j, item in enumerate(row):
            props = list(propositions[i, j])
            if item > 0:
                rules.append([int(props[item - 1])])

    ## first rule each entry has a value, if it is set, that value must be True
    for i, row in enumerate(sudoku):
        for j, item in enumerate(row):
            rules.append([int(item) for item in propositions[i, j, :]])

    ## second rule each row value appears once
    for y in range(k2):
        for z in range(k2):
            for x in range(k2 - 1):
                for i in range(x + 1, k2):
                    props = [propositions[x, y, z], propositions[i, y, z]]
                    rules.append([-int(item) for item in props])

    ## third rule each column value appears once
    for x in range(k2):
        for z in range(k2):
            for y in range(k2 - 1):
                for i in range(y + 1, k2):
                    props = [propositions[x, y, z], propositions[x, i, z]]
                    rules.append([-int(item) for item in props])

    ## fourth rule each 3x3 value appears once
    for z in range(k2):  # for all values
        for i in range(k):  # x block
            for j in range(k):  # y block
                for x in range(k):  # x place in block
                    for y in range(k):  # y place in block
                        for l in range(y + 1, k):  # for each
                            props = [
                                propositions[k * i + x, k * j + y, z],
                                propositions[k * i + x, k * j + l, z]
                            ]
                            rules.append([-int(item) for item in props])

                        for l in range(x + 1, k):
                            for m in range(0, k):
                                props = [
                                    propositions[k * i + x, k * j + y, z],
                                    propositions[k * i + l, k * j + m, z]
                                ]
                                rules.append([-int(item) for item in props])

    ## There is at most one number in each entry
    for y in range(k2):
        for x in range(k2):
            for z in range(k2 - 1):
                for i in range(z + 1, k2):
                    props = [propositions[x, y, z], propositions[x, y, i]]
                    rules.append([-int(item) for item in props])

    ## each number appears at least once in row and column and 3x3
    for y in range(k2):
        for z in range(k2):
            rules.append([int(item) for item in propositions[:, y, z]])

    for x in range(k2):
        for z in range(k2):
            rules.append([int(item) for item in propositions[x, :, z]])

    for i in range(k):  # x block
        for j in range(k):  # y block
            for x in range(k):  # x place in block
                for y in range(k):  # y place in block
                    rules.append([
                        int(item)
                        for item in propositions[k * i + x, k * j + y, :]
                    ])

    solver = MinisatGH()
    solver.append_formula(rules)
    answer = solver.solve()
    if answer == True:
        for i, lit in enumerate(solver.get_model()):
            if lit > 0:
                idx = lit - 1
                sudoku[(idx // k2) // k2][(idx // k2) % k2] = (idx % k2) + 1
    else:
        print("Did not find a model!")

    return sudoku
예제 #6
0
def solve_sudoku_SAT(sudoku, k):

    ### note: this solution assumes that we don't go over 2 digits in the size/numbers (so works up to 9*9 units)

    from pysat.formula import CNF
    from pysat.solvers import MinisatGH

    ## constraint id is calculated as follows: concatenate row num, col num and value, padded two 2 digits

    solver = MinisatGH()
    formula = CNF()

    ## Approach:
    ## We have variables for each possible value in each cell (so rowNum * colNum * potential_values = k*k * k*k * k*k)
    ## for each unit (row, co, box) we add a rule that at least one should be true (a or b or c or ...)
    ## than for each pair in a unit, we add that the two cannot be true at the same time (not a or not b)

    ## adding: one position can't take two values

    for rowInd in range(k * k):
        for colInd in range(k * k):
            for valOne in range(k * k - 1):
                for valTwo in range(valOne + 1, k * k):
                    formula.append([
                        -int(
                            pad_str(rowInd + 1) + pad_str(colInd + 1) +
                            pad_str(valOne + 1)), -int(
                                pad_str(rowInd + 1) + pad_str(colInd + 1) +
                                pad_str(valTwo + 1))
                    ])

    ## adding: row rules
    for rowInd in range(k * k):
        for possible_value in range(k * k):
            ## adding that one should be true
            formula.append([
                int(
                    pad_str(rowInd + 1) + pad_str(colInd + 1) +
                    pad_str(possible_value + 1)) for colInd in range(k * k)
            ])
            ## adding that two cannot be true
            for colIndOne in range(k * k - 1):
                for colIndTwo in range(colIndOne + 1, k * k):
                    formula.append([
                        -int(
                            pad_str(rowInd + 1) + pad_str(colIndOne + 1) +
                            pad_str(possible_value + 1)), -int(
                                pad_str(rowInd + 1) + pad_str(colIndTwo + 1) +
                                pad_str(possible_value + 1))
                    ])

    ## adding: col rules
    for colInd in range(k * k):
        for possible_value in range(k * k):
            ## adding that one should be true
            formula.append([
                int(
                    pad_str(rowInd + 1) + pad_str(colInd + 1) +
                    pad_str(possible_value + 1)) for rowInd in range(k * k)
            ])
            ## adding that two cannot be true
            for rowIndOne in range(k * k - 1):
                for rowIndTwo in range(rowIndOne + 1, k * k):
                    formula.append([
                        -int(
                            pad_str(rowIndOne + 1) + pad_str(colInd + 1) +
                            pad_str(possible_value + 1)), -int(
                                pad_str(rowIndTwo + 1) + pad_str(colInd + 1) +
                                pad_str(possible_value + 1))
                    ])

    ## adding: box rules
    for rowStart in range(0, k * k, k):
        for colStart in range(0, k * k, k):
            ## looping inside box
            for possible_value in range(k * k):

                ## adding that one should be true
                box_ids = []
                for rowInd in range(rowStart, rowStart + k):
                    for colInd in range(colStart, colStart + k):
                        box_ids.append(
                            int(
                                pad_str(rowInd + 1) + pad_str(colInd + 1) +
                                pad_str(possible_value + 1)))
                formula.append(box_ids)

                ## adding that two cannot be true
                for rowIndOne in range(rowStart, rowStart + k):
                    for colIndOne in range(colStart, colStart + k):
                        for rowIndTwo in range(rowIndOne, rowStart + k):
                            for colIndTwo in range(colIndOne, colStart + k):
                                if not (rowIndOne == rowIndTwo
                                        and colIndOne == colIndTwo):
                                    formula.append([
                                        -int(
                                            pad_str(rowIndOne + 1) +
                                            pad_str(colIndOne + 1) +
                                            pad_str(possible_value + 1)), -int(
                                                pad_str(rowIndTwo + 1) +
                                                pad_str(colIndTwo + 1) +
                                                pad_str(possible_value + 1))
                                    ])

    ## Adding the input values as literals
    for rowInd in range(k * k):
        for colInd in range(k * k):
            if sudoku[rowInd][colInd] != 0:
                formula.append([
                    int(
                        pad_str(rowInd + 1) + pad_str(colInd + 1) +
                        pad_str(sudoku[rowInd][colInd]))
                ])

    ## calling the solver
    solver.append_formula(formula)
    answer = solver.solve()
    if not answer:
        return None
    else:
        ### reconstruct sudoku from solution
        for lit in solver.get_model():
            if lit > 0:
                lit_split = [int(x) for x in str(lit)]
                ## appending leading zero if needed, since int conversion removes it
                if len(lit_split) < 6:
                    lit_split = [0] + lit_split
                print(lit_split)

                sudoku[10 * lit_split[0] + lit_split[1] -
                       1][10 * lit_split[2] + lit_split[3] -
                          1] = 10 * lit_split[4] + lit_split[5]
        return sudoku
예제 #7
0
            nv = card.nv

# Encode constraints for each cell
for i in range(N**2):
    for j in range(N**2):
        # Atleast-1 clause
        F.append(V[i][j])
        # Atmost-1 encoding
        card = pysat.card.CardEnc.atmost(lits=V[i][j],
                                         top_id=nv,
                                         bound=1,
                                         encoding=enc)
        F.extend(card.clauses)
        nv = card.nv

solver = Solver(bootstrap_with=F.clauses, use_timer=True)

# First compute a solution
if solver.solve():
    solution = [
        v for v in solver.get_model() if v > 0 and v <= max_problem_var
    ]
    S = set(solution)
    for i in range(N**2):
        for j in range(N**2):
            vs = V[i][j]
            trues = [ix + 1 for (ix, v) in enumerate(vs) if v in S]
            print("%2d" % trues[0], end="")
        print()
    print(solver.time())
예제 #8
0
def solve_sudoku_SAT(sudoku, k):

    # give each possible value for cell (i, j) a (boolean) variable, resulting in in total k^6 variables
    variables = [[[v + k * k * (k * k * i + j) for v in range(1, k * k + 1)]
                  for j in range(k * k)] for i in range(k * k)]

    formula = CNF()

    # iterate over rows and columns
    for i in range(k * k):

        # keep track of which variables belong to the row and column and to which values v
        row_vars = [[] for v in range(k * k)]
        col_vars = [[] for v in range(k * k)]

        for j in range(k * k):
            if sudoku[i][j] > 0:
                # values we are certain of are added as unit clause
                formula.append([variables[i][j][sudoku[i][j] - 1]])

            # each cell must contain a value
            formula.append(
                variables[i][j]
            )  # this says: one of the variables belonging to (i,j) must be true
            for v1 in range(k * k):
                row_vars[v1].append(
                    variables[i][j][v1]
                )  # save the variable belonging to the row for each value
                col_vars[v1].append(
                    variables[j][i]
                    [v1])  # same for the column: note the swapped i and j

                for v2 in range(v1 + 1, k * k):
                    # prevent cells from having multiple values
                    # in cell (i,j) any two variables v1, v2 (v1 != v2) cannot both be true
                    formula.append(
                        [-variables[i][j][v1], -variables[i][j][v2]])

        for v in range(k * k):
            # each row and column must contain each possible value
            formula.append(
                row_vars[v]
            )  # for each possible value v, one corresponding variable must be true in row i
            formula.append(col_vars[v])  # same for the column

            for v1 in range(k * k):
                for v2 in range(v1 + 1, k * k):
                    # and values in rows and columns cannot appear more than once
                    # even though this is already implied, adding these lines speeds up the search process
                    formula.append([-row_vars[v][v1], -row_vars[v][v2]])
                    formula.append([-col_vars[v][v1], -col_vars[v][v2]])

    # iterate over blocks
    for b1 in range(k):  # block vertical index
        for b2 in range(k):  # block horizontal index
            # keep track of which variables belong to the block and to which values v
            block_vars = [[] for v in range(k * k)]
            for i in range(k * b1, k + k * b1):  # row
                for j in range(k * b2, k + k * b2):  # column
                    for v in range(k * k):  # value
                        block_vars[v].append(variables[i][j][v])
            for v in range(k * k):
                # the block must contain every possible value once
                formula.append(block_vars[v])
                for v1 in range(k * k):
                    for v2 in range(v1 + 1, k * k):
                        # and two values in the block cannot be the same
                        formula.append(
                            [-block_vars[v][v1], -block_vars[v][v2]])

    solver = MinisatGH()
    solver.append_formula(formula)

    answer = solver.solve()

    if answer:
        # if a solution is found
        # we convert the variables back to indices (i,j) and a value v
        solution = [[0 for j in range(k * k)]
                    for i in range(k * k)]  # empty k*k x k*k solution grid
        for l in solver.get_model():  # for each variable l in the model
            if l > 0:  # variable l is true
                idx = l - 1  # this makes indexing easier
                i = int(idx / k**4)  # row index
                j = int(idx / (k * k) % (k * k))  # column index
                v = idx % (k * k) + 1  # value
                solution[i][j] = v
        return solution
    return None  # if no solution is found
예제 #9
0
def solve_sudoku_SAT(sudoku, k):
    """
    Function that solves given sudoku list with SAT encoding. Edges correspond
    to undirected edge between cells in same row, column or k*k block.
    """

    formula = CNF()

    num_vertices = (k * k) * (k * k)  # 81 for k = 3
    # Nd array representation of sudoku
    graph = np.array(sudoku).reshape(num_vertices, 1)
    # Create 2d np array of vertice numbers of 0 until num_vertices-1 for edges
    vertices = np.arange(num_vertices).reshape(k * k, k * k)
    edges = get_edges(vertices, k)
    vertices = vertices.reshape(num_vertices, 1)
    # Number of possible values in sudoku which we refer to as numbers (nums/n)
    num_nums = k * k

    # Return propositional variable for each vertex and num combination
    def var_v_n(v, n):
        return (v * num_nums) + n

    # Each cell contains a value between 1 and k*k
    for v in range(num_vertices):

        # Clause to ensure at least one num can be assigned to one vertex
        clause = [var_v_n(v, n) for n in range(1, num_nums + 1)]
        formula.append(clause)

        for n1 in range(1, num_nums + 1):
            for n2 in range(n1 + 1, num_nums + 1):
                # Clause ensures at most one num can be assigned to one vertex
                clause = [-1 * var_v_n(v, n1), -1 * var_v_n(v, n2)]
                formula.append(clause)

        # If a cell contains u in input, cell in solution must contain u
        if graph[v] != 0:
            cell = var_v_n(vertices[v].item(), graph[v].item())
            formula.append([cell])

    # Each two different cells in same row,col,block must contain diff values
    for (v1, v2) in edges:
        for n in range(1, num_nums + 1):
            # Num assigned to vertex 1 of edge should be true, OR num assigned
            # to other vertex of edge should be true
            clause = [-var_v_n(v1.item(), n), -var_v_n(v2.item(), n)]
            formula.append(clause)

    solver = MinisatGH()
    solver.append_formula(formula)

    answer = solver.solve()
    if answer:
        model = solver.get_model()
        # Fill in sudoku graph with answer
        for v in range(num_vertices):
            for n in range(1, num_nums + 1):
                if var_v_n(v, n) in model:
                    graph[v] = n
        graph = graph.reshape(k * k, k * k).tolist()
    else:
        graph = None

    return graph
예제 #10
0
def solve_sudoku_SAT(sudoku: List[List[int]], k: int) -> Union[List[List[int]], None]:
    """Solve a k-by-k block Sudoku puzzle by encoding the problem as a propositional CNF formula
       and using a satisfiability (SAT) solver

    :param sudoku: A Sudoku puzzle represented as a list of lists. Values to be filled in are set to zero (0)
    :type sudoku: List[List[int]]
    :param k: Size of the Sudoku puzzle (a grid of k x k blocks)
    :type k: int
    :return: The solved sudoku puzzles as a list of lists or None if no solution is found.
    :rtype: Union[List[List[int]], None]
    """
    # Record a (row, column, value) triple as a Variable ID in a PySAT ID Pool
    def triplet_to_id(row_index: int, column_index: int, val: int) -> int:
        # indexes need to start at 1 in the id pool
        return id_pool.id(tuple([row_index + 1, column_index + 1, val]))

    # Extract the (row, column, value) triple from the PySAT Variable ID
    def id_to_triplet(vid: int) -> Tuple[int, int, int]:
        row, column, val = id_pool.obj(vid)
        # convert back to indexes
        return row - 1, column - 1, val

    solved_sudoku = copy(sudoku)

    grid_size = k**2
    cell_indices = list(get_cell_indices(k))

    formula = CNF()
    id_pool = IDPool()

    # Build the CNF Formula using the definition of Kwon and Jain (2006)
    # (https://www.researchgate.net/publication/228566840_Optimized_CNF_encoding_for_sudoku_puzzles)
    # Extended Encoding = cell definedness AND cell uniqueness AND row definedness AND row uniqueness
    #   AND column definedness AND column uniqueness AND block definedness
    #   AND block uniqueness AND assigned cells
    # definedness: each (cell, row, column, block) has at least one number from 1 to grid_size+1
    # uniqueness each (cell, row, column, block) has at most one number from 1 to grid_size+1

    # Row/column/cell level
    # You can swap indices to declare uniqueness and definedness in a single loop
    for row_index, column_index in cell_indices:
        # Cell definedness
        formula.append([triplet_to_id(row_index, column_index, v) for v in range(grid_size)])

        # Row definedness
        formula.append([triplet_to_id(row_index, v, column_index) for v in range(grid_size)])

        # Column definedness
        formula.append([triplet_to_id(v, column_index, row_index) for v in range(grid_size)])

        # Assigned cells
        val = sudoku[row_index][column_index]
        if val != 0:
            formula.append([triplet_to_id(row_index, column_index, val - 1)])

        for val_index_i, val_index_j in itertools.combinations(range(grid_size), 2):
            # Cell uniqueness
            formula.append([
                -triplet_to_id(row_index, column_index, val_index_i),
                -triplet_to_id(row_index, column_index, val_index_j)])

            # Row uniqueness
            formula.append([
                -triplet_to_id(row_index, val_index_i, column_index),
                -triplet_to_id(row_index, val_index_j, column_index)])

            # Column uniqueness
            formula.append([
                -triplet_to_id(val_index_i, row_index, column_index),
                -triplet_to_id(val_index_j, row_index, column_index)])

    # Block level
    for val in range(grid_size):
        for block_indices in get_cell_indices_by_block(k):
            # Block definedness
            formula.append([triplet_to_id(r, c, val) for r, c in block_indices])

            # Block uniqueness
            for block_index_tuple in itertools.combinations(block_indices, 2):
                (row_1, column_1), (row_2, column_2) = block_index_tuple
                formula.append([
                    -triplet_to_id(row_1, column_1, val),
                    -triplet_to_id(row_2, column_2, val)])

    # Solve
    solver = MinisatGH()
    solver.append_formula(formula)
    answer = solver.solve()

    if not answer:
        return None

    # Fill in solution
    model = solver.get_model()
    for vid in model:
        if vid > 0:
            row_index, column_index, val = id_to_triplet(vid)
            solved_sudoku[row_index][column_index] = val + 1

    return solved_sudoku
예제 #11
0
def solve_sudoku_SAT(sudoku, k):
    '''
        Function that solves a sudoku using a SAT solver.
        Creates the formula and feeds it to the MinisatGH solver.

        Returns: the filled in sudoku if it was solved, None otherwise.
    '''
    kk = k * k
    formula = CNF()

    # functions to be used within this solver
    def var_number(i, j, n):
        ''' Determines a unique index for a sudoku cell based in its row, column and possible value. '''
        return i * kk * kk + j * kk + n

    # create literals for values that are already filled in, these must always be satisfied
    for i in range(kk):
        for j in range(kk):
            if sudoku[i][j] != 0:
                clause = [var_number(i, j, sudoku[i][j])]
                formula.append(clause)

    # ensure all cells have at least one of the specified values
    for i in range(kk):
        for j in range(kk):
            clause = []
            # iterate over all possible values, determine the unique index
            for n in range(1, kk + 1):
                clause.append(var_number(i, j, n))
            # ensure at least one is satisfied
            formula.append(clause)

    # iterate over rows (needed for multiple checks)
    for i in range(kk):
        for j in range(kk):
            # ensure at most one value is picked
            for n1 in range(1, kk + 1):
                for n2 in range(n1 + 1, kk + 1):
                    # only one of the two values can be true
                    clause = [
                        -1 * var_number(i, j, n1), -1 * var_number(i, j, n2)
                    ]
                    formula.append(clause)

        # ensure proper row and column (no double values)
        for n in range(1, kk + 1):
            for j1 in range(kk):
                for j2 in range(j1 + 1, kk):
                    # ensure no column contains same value twice
                    clause = [
                        -1 * var_number(i, j1, n), -1 * var_number(i, j2, n)
                    ]
                    formula.append(clause)
                    # ensure no row contains same value twice
                    clause = [
                        -1 * var_number(j1, i, n), -1 * var_number(j2, i, n)
                    ]
                    formula.append(clause)

    # create list with indices of blocks
    indices = create_indices(k)

    # iterate over block indices
    for ind in indices:
        # iterate over possible values
        for n in range(1, kk + 1):
            # create every combination of block indices possible
            for i1 in range(len(ind)):
                for i2 in range(i1 + 1, len(ind)):
                    # ensure no value appears more than once
                    clause = [
                        -1 * var_number(ind[i1][0], ind[i1][1], n),
                        -1 * var_number(ind[i2][0], ind[i2][1], n)
                    ]
                    formula.append(clause)

    # initialise the solver and give it the formula
    solver = MinisatGH()
    solver.append_formula(formula)

    # attempt to solve formula
    answer = solver.solve()

    # fill the sudoku when an answer has been found
    if answer:
        # get model from solver
        model = solver.get_model()
        for i in range(kk):
            for j in range(kk):
                for n in range(1, kk + 1):
                    if var_number(i, j, n) in model:
                        sudoku[i][j] = n
        return sudoku

    # return None when no solution was found
    return None
예제 #12
0
def solve_sudoku_SAT(sudoku, k):
    """
    This function encodes the sudoku problem into SAT, and uses a SAT solver to solve the problem
    """

    # First we create a list containing all the edges for all vertices in the sudoku
    length = len(sudoku)
    num_vertices = length**2
    matrix = np.arange(num_vertices).reshape(length, length)
    edges = []

    sudoku = np.array(sudoku).reshape(length * length)

    # The loop below fills the edges list with all edges in the sudoku
    for i in range(length):
        for j in range(length):

            # specify the current value i,j as the left-hand value for the edge tuple
            left = int(matrix[i][j] + 1)

            # iterate over values in the square
            col = j // k
            row = i // k
            rows = matrix[(row * k):(row * k + k)]
            box = [x[(col * k):(col * k + k)] for x in rows]
            for v in range(k):
                for w in range(k):
                    right = int(box[v][w] + 1)

                    # make sure that we're not assigning the current value as the right-hand vertix
                    if (row * k + v, col * k + w) != (i, j):
                        if (left, right) not in edges:
                            edges.append((left, right))

            # iterative over cells in row i,
            for g in range(length):
                right = int(matrix[i][g] + 1)
                if (i, g) != (i, j):
                    if (left, right) not in edges:
                        edges.append((left, right))

            # iterate over cells in column j,
            for c in range(length):
                right = int(matrix[c][j] + 1)
                if (c, j) != (i, j):
                    if (left, right) not in edges:
                        edges.append((left, right))

    # specify the range of values which can be assigned to empty cells
    num_values = k**2
    formula = CNF()

    # this function will asign a positive integer for each propositional variable
    def var_number(i, c):
        return ((i - 1) * num_values) + c

    # we then add a clause which states that for each vertex in the graph, there must be at least one value v,
    # for which p__i,v is true
    for i in range(1, num_vertices + 1):
        clause = []

        # if the given sudoku has a 0 at that index, we assign all possible values from num_values
        if int(sudoku[i - 1]) == 0:
            for c in range(1, num_values + 1):
                clause.append(var_number(i, c))
                formula.append(clause)
        # if the given sudoku already has an assigned value for that index, we only add a single clause
        else:
            clause.append(var_number(i, int(sudoku[i - 1])))
            formula.append(clause)

    # at most one value for which p__i,v is true
    for i in range(1, num_vertices + 1):
        for c1 in range(1, num_values + 1):
            for c2 in range(c1 + 1, num_values + 1):
                clause = [-1 * var_number(i, c1), -1 * var_number(i, c2)]
                formula.append(clause)

    # ensure that values are assigned "properly"
    for (i1, i2) in edges:
        for c in range(1, num_values + 1):
            clause = [-1 * var_number(i1, c), -1 * var_number(i2, c)]
            formula.append(clause)

    solver = MinisatGH()
    solver.append_formula(formula)
    answer = solver.solve()

    # reshape the resulting matrix so that we can index it with a single value
    matrix = matrix.reshape(length**2)

    if answer == True:
        print("The sudoku is solved.")
        model = solver.get_model()
        for i in range(1, num_vertices + 1):
            for c in range(1, num_values + 1):
                if var_number(i, c) in model:
                    matrix[i - 1] = c
        return matrix.reshape(length, length).tolist()
    else:
        print("The sudoku has no solution.")
        return None
예제 #13
0
def solve_sudoku_SAT(sudoku, k):
    # creating the formula for the SAT solver
    formula = CNF()

    # creating a sudoku array with individual vertice numbers
    num_vertices = k**4
    list_vertices = list(range(1, (k**2) * (k**2) + 1))
    vertice_list = []
    for i in range(k**2):
        vertice_list.append(list_vertices[:k**2])
        del list_vertices[:k**2]
    array_vertices = np.asarray(vertice_list)

    # generating the edges
    edges = make_edges(k)

    # filling in the formula with clauses
    # adding the clauses for every cell containing a unique index for every value
    num_digits = k**2
    for i in range(1, num_vertices + 1):
        clause = []
        for d in range(1, num_digits + 1):
            clause.append(var_number(i, d, num_digits))
        formula.append(clause)

    # adding the clause that every cell can only contain one value
    for i in range(1, num_vertices + 1):
        for c1 in range(1, num_digits + 1):
            for c2 in range(c1 + 1, num_digits + 1):
                clause = [
                    -1 * var_number(i, c1, num_digits),
                    -1 * var_number(i, c2, num_digits)
                ]
                formula.append(clause)

    #adding the clauses that edges cannot contain the same value
    for (i, j) in edges:
        for d in range(1, num_digits + 1):
            clause = [
                int(-var_number(i, d, num_digits)),
                int(-var_number(j, d, num_digits))
            ]
            formula.append(clause)

    # adding the clauses that contain the true values for the cells which we are certain about (that are already filled in)
    for i in range(k**2):
        for j in range(k**2):
            if sudoku[i][j] != 0:
                clause = [
                    int(
                        var_number(array_vertices[i, j], sudoku[i][j],
                                   num_digits))
                ]
                formula.append(clause)

    # creating sat solver and checking for an answer
    solver = MinisatGH()
    solver.append_formula(formula)
    answer = solver.solve()

    # check if sudoku is solvable and fill in sudoku and return sudoku, otherwise return None
    if answer == True:
        model = solver.get_model()
        sudoku = array_vertices
        for i in range(k**2):
            for j in range(k**2):
                for d in range(1, k**2 + 1):
                    if var_number(array_vertices[i, j], d,
                                  num_digits) in model:
                        sudoku[i, j] = d
                        break
        sudoku = sudoku.tolist()
        return sudoku
    else:
        return None
예제 #14
0
def solve_sudoku_SAT(sudoku, k):

    formula = CNF()

    num_rows = num_cols = num_values = k**2

    # creates propositional variables indicated as unique positive integers
    def s(row, col, value):
        return (k**4) * row + (k**2) * col + value

    def s_extract(row, col, pos_lit):
        return pos_lit - ((k**4) * row + (k**2) * col)

    # at least one number in each entry
    for row in range(num_rows):
        for col in range(num_cols):
            clause = []

            # if entry already given append unit clause
            if sudoku[row][col] != 0:
                clause.append(s(row, col, sudoku[row][col]))

            # else append disjunction
            else:
                for value in range(1, num_values + 1):
                    clause.append(s(row, col, value))
            formula.append(clause)

    # each value can appear at most once in every row
    for col in range(num_cols):
        for value in range(1, num_values + 1):
            for row1 in range(num_rows - 1):
                for row2 in range(row1 + 1, num_rows):
                    clause = [-s(row1, col, value), -s(row2, col, value)]
                    formula.append(clause)

    # each value can appear at most once in every column
    for row in range(num_cols):
        for value in range(1, num_values + 1):
            for col1 in range(num_cols - 1):
                for col2 in range(col1 + 1, num_cols):
                    clause = [-s(row, col1, value), -s(row, col2, value)]
                    formula.append(clause)

    # each value can appear at most once in every block
    for value in range(1, num_values + 1):
        for i in range(k):
            for j in range(k):
                for x in range(k):
                    for y in range(k):
                        for y1 in range(y + 1, k):
                            clause = [
                                -s(k * i + x, k * j + y, value),
                                -s(k * i + x, k * j + y1, value)
                            ]
                            formula.append(clause)

    for value in range(1, num_values + 1):
        for i in range(k):
            for j in range(k):
                for x in range(k):
                    for y in range(k):
                        for x1 in range(x + 1, k):
                            for l in range(k):
                                clause = [
                                    -s(k * i + x, k * j + y, value),
                                    -s(k * i + x1, k * j + l, value)
                                ]
                                formula.append(clause)

    solver = MinisatGH()
    solver.append_formula(formula)

    answer = solver.solve()

    pos_lits = [value for value in solver.get_model() if value > 0]

    count = 0
    for row in range(num_rows):
        for col in range(num_cols):
            if sudoku[row][col] == 0:
                sudoku[row][col] = s_extract(row, col, pos_lits[count])
            count += 1

    return sudoku
예제 #15
0
def hads_to_graphs(all_columns=True, transpose=True):

    if DEBUGGING:
        start_time = datetime.datetime.now()

    translate = {
        '0': [-1, -1, -1, -1],
        '1': [-1, -1, -1, 1],
        '2': [-1, -1, 1, -1],
        '3': [-1, -1, 1, 1],
        '4': [-1, 1, -1, -1],
        '5': [-1, 1, -1, 1],
        '6': [-1, 1, 1, -1],
        '7': [-1, 1, 1, 1],
        '8': [1, -1, -1, -1],
        '9': [1, -1, -1, 1],
        'A': [1, -1, 1, -1],
        'B': [1, -1, 1, 1],
        'C': [1, 1, -1, -1],
        'D': [1, 1, -1, 1],
        'E': [1, 1, 1, -1],
        'F': [1, 1, 1, 1]
    }

    #tracks the number of graphs and hadamard matrices
    hctr = 0
    gctr = 0

    mults_to_clauses = dict()

    emap = dict()
    ectr = 0
    for a in range(K):
        for b in range(a + 1, K):
            ectr += 1
            emap[ectr] = (a, b)
            emap[(a, b)] = ectr

    re_emap = dict()
    ectr = 0
    for b in range(K):
        for a in range(b):
            ectr += 1
            #re_emap[(a,b)] = ectr
            re_emap[emap[(a, b)]] = ectr
            print('relabeled index of', (a, b),
                  'from',
                  emap[(a, b)],
                  'to',
                  ectr,
                  file=sys.stderr)

    #return

    if all_columns:
        max_col = K - 1

    else:
        max_col = 0

    for curr_line in fileinput.input():
        myh = []
        #print('curr line is:')
        #print(curr_line)
        #print('just did it')
        #print('currline[:-1] is:')
        #print(curr_line[:-1])
        #print('just did it')
        #print()
        if DEBUGGING:
            print('the entries of curr line are:', file=sys.stderr)
            for s in curr_line:
                print(s)

        for s in curr_line[:-1]:
            if DEBUGGING:
                print(s, file=sys.stderr)
                print(translate[s], file=sys.stderr)
            myh.extend(translate[s])
        myh = matrix([myh[i * K:(i + 1) * K] for i in range(K)])

        #for line in chunk.splitlines():
        #    myh = []
        #    for s in line:
        #        myh.extend(translate[s])
        #myh = matrix([myh[i*K:(i+1)*K] for i in range(K)]])

        if VERBOSE:
            print('trying matrix', hctr, '...', file=sys.stderr)

        for col in range(max_col + 1):
            hh = swap_cols(myh, 0, col)
            dd = diagonal_matrix([hh[j, 0] for j in range(K)])

            hh = dd * hh

            dd = diagonal_matrix([hh[0, j] for j in range(K)])
            hh = hh * dd

            if transpose:
                newhh = []
                for i in range(K):
                    newhh.append([hh[j, i] for j in range(K)])
                hh = matrix(newhh)

            nclauses = 0
            nvars = K * (K - 1) / 2

            if SOLVER_NUM == 0:
                g = Glucose3()
            if SOLVER_NUM == 1:
                g = Glucose4()
            if SOLVER_NUM == 2:
                g = Lingeling()
            if SOLVER_NUM == 3:
                g = MapleChrono()
            if SOLVER_NUM == 4:
                g = MapleCM()
            if SOLVER_NUM == 5:
                g = Maplesat()
            if SOLVER_NUM == 6:
                g = Minicard()
            if SOLVER_NUM == 7:
                g = Minisat22()
            if SOLVER_NUM == 8:
                g = MinisatGH()

            matchings = dict()

            ectr = 0

            for b in range(K - 1):
                ectr += 1
                my_row = [0 for i in range(K - 1)]
                my_row[b] = K
                my_row = tuple(my_row)
                matchings[my_row] = [(ectr, 0, b)]

            for a in range(1, K):
                for b in range(a + 1, K):
                    if DEBUGGING:
                        print('trying', (a, b), file=sys.stderr)
                    ectr += 1
                    hab = tuple([hh[a, j] * hh[b, j] for j in range(K)])
                    my_row = []
                    for j in range(1, K):
                        dot = 0
                        for k in range(K):
                            dot += hab[k] * hh[j, k]
                        my_row.append(dot)

                    my_row = tuple(my_row)

                    if my_row in matchings:
                        matchings[my_row].append((ectr, a, b))
                        old_ectr = matchings[my_row][0][0]
                        if DEBUGGING:
                            print('the old ectr,ectr=',
                                  old_ectr,
                                  ectr,
                                  file=sys.stderr)
                        g.add_clause((re_emap[old_ectr], -re_emap[ectr]))
                        g.add_clause((-re_emap[old_ectr], re_emap[ectr]))
                        nclauses += 2

                    else:
                        matchings[my_row] = [(ectr, a, b)]

                        coes_to_inds = dict()
                        mults = []

                        for i in range(K - 1):
                            coe = my_row[i]
                            if coe in coes_to_inds:
                                coes_to_inds[coe].append(i)
                            else:
                                coes_to_inds[coe] = [i]

                        mults = [(coe, len(coes_to_inds[coe]))
                                 for coe in coes_to_inds if coe]
                        mults.sort()
                        mults = tuple(mults)

                        if mults in mults_to_clauses:
                            old_ectr, num_new_vars, old_coes_to_inds, old_clauses = mults_to_clauses[
                                mults]

                            #must relabel the old indices to match the new ones
                            #for each coefficient c
                            relab = dict()

                            for coe in old_coes_to_inds:
                                coe_ctr = 0
                                for old_index in old_coes_to_inds[coe]:
                                    relab[1 + old_index] = re_emap[
                                        1 + coes_to_inds[coe][coe_ctr]]
                                    relab[-1 - old_index] = -re_emap[
                                        1 + coes_to_inds[coe][coe_ctr]]
                                    coe_ctr += 1

                            relab[old_ectr] = re_emap[ectr]
                            relab[-old_ectr] = -re_emap[ectr]

                            for i in range(1, 1 + num_new_vars):
                                relab[old_ectr + i] = i + nvars
                                relab[-old_ectr - i] = -i - nvars

                            for curr_clause in old_clauses:
                                g.add_clause([relab[k] for k in curr_clause])

                            nclauses += len(old_clauses)
                            nvars += num_new_vars

                        else:
                            if DEBUGGING:
                                print('dealing with',
                                      mults,
                                      'for the first time...',
                                      file=sys.stderr)
                            cnf = PBEnc.equals(lits=range(1, K) + [ectr],
                                               weights=list(my_row) + [-K],
                                               bound=0,
                                               encoding=4)
                            curr_clauses, maxvar = simplify_clause_list(
                                cnf.clauses, ectr + 1)

                            num_new_vars = maxvar - ectr

                            if DEBUGGING:
                                print("", file=sys.stderr)
                                print("", file=sys.stderr)
                                print('adding new clauses; there are',
                                      nvars,
                                      'vars',
                                      file=sys.stderr)

                            for curr_clause in curr_clauses:
                                if DEBUGGING:
                                    print('relabeling clause',
                                          c,
                                          'with ectr=',
                                          ectr,
                                          'and',
                                          nvars,
                                          'vars',
                                          file=sys.stderr)
                                new_clause = []
                                for k in curr_clause:
                                    if abs(k) <= ectr:
                                        if k > 0:
                                            new_clause.append(re_emap[k])
                                        else:
                                            new_clause.append(-re_emap[-k])
                                    else:
                                        if k > 0:
                                            if DEBUGGING:
                                                print(k,
                                                      'is the',
                                                      k - ectr,
                                                      'th dummy...',
                                                      'shift it up by',
                                                      nvars,
                                                      file=sys.stderr)
                                            new_clause.append(k - ectr + nvars)
                                        else:
                                            new_clause.append(k + ectr - nvars)
                                            if DEBUGGING:
                                                print(k,
                                                      'is the',
                                                      k + ectr,
                                                      'th dummy...',
                                                      'shift it down by',
                                                      nvars,
                                                      file=sys.stderr)
                                if DEBUGGING:
                                    print('    adding',
                                          new_clause,
                                          file=sys.stderr)
                                g.add_clause(new_clause)

                            nvars += num_new_vars
                            nclauses += len(curr_clauses)

                            mults_to_clauses[mults] = (ectr, num_new_vars,
                                                       coes_to_inds,
                                                       curr_clauses)

                            if DEBUGGING:
                                print('now there are', (nvars, nclauses),
                                      'vars,clauses',
                                      file=sys.stderr)

            sol_ctr = 0

            if VERBOSE:
                print('there are',
                      nvars,
                      'variables and',
                      nclauses,
                      'clauses',
                      file=sys.stderr)
                print('trying to find all solutions!', file=sys.stderr)

            if True:  #nvars >= 2000:
                while g.solve():
                    new_sol = g.get_model()
                    sol_ctr += 1

                    if sol_ctr % 1000 == 0:
                        print('   ',
                              sol_ctr,
                              'found so far...',
                              file=sys.stderr)

                    gctr += 1
                    #nx.write_graph6(nx.Graph([emap[k] for k in new_sol[:K*(K-1)/2] if k > 0]), sys.stdout, nodes = range(K))
                    #print( str([emap[k] for k in new_sol[:K*(K-1)/2] if k > 0]), file = sys.stdout )
                    g.add_clause(
                        [-new_sol[1 + j * (j + 1) / 2] for j in range(K - 1)])
                    #print([k for k in new_sol[:K*(K-1)/2] if k > 0], file = sys.stderr)
                    #print('which is really:', file = sys.stderr)
                    #print([re_emap[emap[k]] for k in new_sol[:K*(K-1)/2] if k > 0], file = sys.stderr)
                    #print('got written as\n', file = sys.stderr)
                    print(sol_to_g6(new_sol[:K * (K - 1) / 2]),
                          file=sys.stdout)
                    #g.add_clause( [-new_sol[j] for j in range(K-1)] )

                if VERBOSE:
                    print(sol_ctr, 'solutions found!', file=sys.stderr)
                    if DEBUGGING:
                        end_time = datetime.datetime.now()
                        elapsed_time = end_time - start_time
                        print('total time elapsed:',
                              elapsed_time.seconds,
                              ":",
                              elapsed_time.microseconds,
                              'matrices solved:',
                              hctr,
                              file=sys.stderr)

            else:
                if VERBOSE:
                    print('too hard for now!', file=sys.stderr)

            hctr += 1
    return