Example

We'll design a simple program that solves sudoku puzzle using Z3.

In [1]:
# Imports

import z3

We need to encode the rules of the puzzle in propositional logic. To achieve that, we define certain variables that help us get around in the puzzle.

Constants

We start by defining a puzzle board,a 9x9 matrix of integer variables to mark the puzzle board.

In [2]:
# Runs a nested loop to create the two dimensional matrix required.
# c_i_j is the cell at ith row and jth column.

board = []

for i in range(9):
    column = []
    for j in range(9):
        column.append(z3.Int("c_{0}_{1}".format(i+1, j+1)))
    board.append(column)
    
board
Out[2]:
[[c_1_1, c_1_2, c_1_3, c_1_4, c_1_5, c_1_6, c_1_7, c_1_8, c_1_9],
 [c_2_1, c_2_2, c_2_3, c_2_4, c_2_5, c_2_6, c_2_7, c_2_8, c_2_9],
 [c_3_1, c_3_2, c_3_3, c_3_4, c_3_5, c_3_6, c_3_7, c_3_8, c_3_9],
 [c_4_1, c_4_2, c_4_3, c_4_4, c_4_5, c_4_6, c_4_7, c_4_8, c_4_9],
 [c_5_1, c_5_2, c_5_3, c_5_4, c_5_5, c_5_6, c_5_7, c_5_8, c_5_9],
 [c_6_1, c_6_2, c_6_3, c_6_4, c_6_5, c_6_6, c_6_7, c_6_8, c_6_9],
 [c_7_1, c_7_2, c_7_3, c_7_4, c_7_5, c_7_6, c_7_7, c_7_8, c_7_9],
 [c_8_1, c_8_2, c_8_3, c_8_4, c_8_5, c_8_6, c_8_7, c_8_8, c_8_9],
 [c_9_1, c_9_2, c_9_3, c_9_4, c_9_5, c_9_6, c_9_7, c_9_8, c_9_9]]

We define a sudoku instance. 0 denotes empty cells.

In [3]:
sudoku = (  (0,0,0,0,9,4,0,3,0),
            (0,0,0,5,1,0,0,0,7),
            (0,8,9,0,0,0,0,4,0),
            (0,0,0,0,0,0,2,0,8),
            (0,6,0,2,0,1,0,5,0),
            (1,0,2,0,0,0,0,0,0),
            (0,7,0,0,0,0,5,2,0),
            (9,0,0,0,6,5,0,0,0),
            (0,4,0,9,7,0,0,0,0)  )

Constraints

We create a constraint so that the solver only focuses on empty cells, that is, the cells with 0 in them.

In [4]:
# 'If' is the if-then-else expression.
# Keeps the empty cells for the consideration of solver, while fills the non empty cells into the board from the instance.

instance_constraint = []

# Iterate over all cells in the board to check for empty cells.
for i in range(9):
    for j in range(9):
        instance_constraint.append(z3.If(sudoku[i][j] == 0, True, board[i][j] == sudoku[i][j]))

instance_constraint
Out[4]:
[If(True, True, c_1_1 == 0),
 If(True, True, c_1_2 == 0),
 If(True, True, c_1_3 == 0),
 If(True, True, c_1_4 == 0),
 If(False, True, c_1_5 == 9),
 If(False, True, c_1_6 == 4),
 If(True, True, c_1_7 == 0),
 If(False, True, c_1_8 == 3),
 If(True, True, c_1_9 == 0),
 If(True, True, c_2_1 == 0),
 If(True, True, c_2_2 == 0),
 If(True, True, c_2_3 == 0),
 If(False, True, c_2_4 == 5),
 If(False, True, c_2_5 == 1),
 If(True, True, c_2_6 == 0),
 If(True, True, c_2_7 == 0),
 If(True, True, c_2_8 == 0),
 If(False, True, c_2_9 == 7),
 If(True, True, c_3_1 == 0),
 If(False, True, c_3_2 == 8),
 If(False, True, c_3_3 == 9),
 If(True, True, c_3_4 == 0),
 If(True, True, c_3_5 == 0),
 If(True, True, c_3_6 == 0),
 If(True, True, c_3_7 == 0),
 If(False, True, c_3_8 == 4),
 If(True, True, c_3_9 == 0),
 If(True, True, c_4_1 == 0),
 If(True, True, c_4_2 == 0),
 If(True, True, c_4_3 == 0),
 If(True, True, c_4_4 == 0),
 If(True, True, c_4_5 == 0),
 If(True, True, c_4_6 == 0),
 If(False, True, c_4_7 == 2),
 If(True, True, c_4_8 == 0),
 If(False, True, c_4_9 == 8),
 If(True, True, c_5_1 == 0),
 If(False, True, c_5_2 == 6),
 If(True, True, c_5_3 == 0),
 If(False, True, c_5_4 == 2),
 If(True, True, c_5_5 == 0),
 If(False, True, c_5_6 == 1),
 If(True, True, c_5_7 == 0),
 If(False, True, c_5_8 == 5),
 If(True, True, c_5_9 == 0),
 If(False, True, c_6_1 == 1),
 If(True, True, c_6_2 == 0),
 If(False, True, c_6_3 == 2),
 If(True, True, c_6_4 == 0),
 If(True, True, c_6_5 == 0),
 If(True, True, c_6_6 == 0),
 If(True, True, c_6_7 == 0),
 If(True, True, c_6_8 == 0),
 If(True, True, c_6_9 == 0),
 If(True, True, c_7_1 == 0),
 If(False, True, c_7_2 == 7),
 If(True, True, c_7_3 == 0),
 If(True, True, c_7_4 == 0),
 If(True, True, c_7_5 == 0),
 If(True, True, c_7_6 == 0),
 If(False, True, c_7_7 == 5),
 If(False, True, c_7_8 == 2),
 If(True, True, c_7_9 == 0),
 If(False, True, c_8_1 == 9),
 If(True, True, c_8_2 == 0),
 If(True, True, c_8_3 == 0),
 If(True, True, c_8_4 == 0),
 If(False, True, c_8_5 == 6),
 If(False, True, c_8_6 == 5),
 If(True, True, c_8_7 == 0),
 If(True, True, c_8_8 == 0),
 If(True, True, c_8_9 == 0),
 If(True, True, c_9_1 == 0),
 If(False, True, c_9_2 == 4),
 If(True, True, c_9_3 == 0),
 If(False, True, c_9_4 == 9),
 If(False, True, c_9_5 == 7),
 If(True, True, c_9_6 == 0),
 If(True, True, c_9_7 == 0),
 If(True, True, c_9_8 == 0),
 If(True, True, c_9_9 == 0)]

We then encode the cell constraint. A cell must contain a number between 1 and 9.

In [5]:
# 'And' represents the logical and function, denoted as '&'.

cell_constraint  = []

# Iterate over all cells in the board to check that all numerals are between 1 and 9.
for i in range(9):
    for j in range(9):
        cell_constraint.append(z3.And(1 <= board[i][j], board[i][j] <= 9))

cell_constraint
Out[5]:
[And(c_1_1 >= 1, c_1_1 <= 9),
 And(c_1_2 >= 1, c_1_2 <= 9),
 And(c_1_3 >= 1, c_1_3 <= 9),
 And(c_1_4 >= 1, c_1_4 <= 9),
 And(c_1_5 >= 1, c_1_5 <= 9),
 And(c_1_6 >= 1, c_1_6 <= 9),
 And(c_1_7 >= 1, c_1_7 <= 9),
 And(c_1_8 >= 1, c_1_8 <= 9),
 And(c_1_9 >= 1, c_1_9 <= 9),
 And(c_2_1 >= 1, c_2_1 <= 9),
 And(c_2_2 >= 1, c_2_2 <= 9),
 And(c_2_3 >= 1, c_2_3 <= 9),
 And(c_2_4 >= 1, c_2_4 <= 9),
 And(c_2_5 >= 1, c_2_5 <= 9),
 And(c_2_6 >= 1, c_2_6 <= 9),
 And(c_2_7 >= 1, c_2_7 <= 9),
 And(c_2_8 >= 1, c_2_8 <= 9),
 And(c_2_9 >= 1, c_2_9 <= 9),
 And(c_3_1 >= 1, c_3_1 <= 9),
 And(c_3_2 >= 1, c_3_2 <= 9),
 And(c_3_3 >= 1, c_3_3 <= 9),
 And(c_3_4 >= 1, c_3_4 <= 9),
 And(c_3_5 >= 1, c_3_5 <= 9),
 And(c_3_6 >= 1, c_3_6 <= 9),
 And(c_3_7 >= 1, c_3_7 <= 9),
 And(c_3_8 >= 1, c_3_8 <= 9),
 And(c_3_9 >= 1, c_3_9 <= 9),
 And(c_4_1 >= 1, c_4_1 <= 9),
 And(c_4_2 >= 1, c_4_2 <= 9),
 And(c_4_3 >= 1, c_4_3 <= 9),
 And(c_4_4 >= 1, c_4_4 <= 9),
 And(c_4_5 >= 1, c_4_5 <= 9),
 And(c_4_6 >= 1, c_4_6 <= 9),
 And(c_4_7 >= 1, c_4_7 <= 9),
 And(c_4_8 >= 1, c_4_8 <= 9),
 And(c_4_9 >= 1, c_4_9 <= 9),
 And(c_5_1 >= 1, c_5_1 <= 9),
 And(c_5_2 >= 1, c_5_2 <= 9),
 And(c_5_3 >= 1, c_5_3 <= 9),
 And(c_5_4 >= 1, c_5_4 <= 9),
 And(c_5_5 >= 1, c_5_5 <= 9),
 And(c_5_6 >= 1, c_5_6 <= 9),
 And(c_5_7 >= 1, c_5_7 <= 9),
 And(c_5_8 >= 1, c_5_8 <= 9),
 And(c_5_9 >= 1, c_5_9 <= 9),
 And(c_6_1 >= 1, c_6_1 <= 9),
 And(c_6_2 >= 1, c_6_2 <= 9),
 And(c_6_3 >= 1, c_6_3 <= 9),
 And(c_6_4 >= 1, c_6_4 <= 9),
 And(c_6_5 >= 1, c_6_5 <= 9),
 And(c_6_6 >= 1, c_6_6 <= 9),
 And(c_6_7 >= 1, c_6_7 <= 9),
 And(c_6_8 >= 1, c_6_8 <= 9),
 And(c_6_9 >= 1, c_6_9 <= 9),
 And(c_7_1 >= 1, c_7_1 <= 9),
 And(c_7_2 >= 1, c_7_2 <= 9),
 And(c_7_3 >= 1, c_7_3 <= 9),
 And(c_7_4 >= 1, c_7_4 <= 9),
 And(c_7_5 >= 1, c_7_5 <= 9),
 And(c_7_6 >= 1, c_7_6 <= 9),
 And(c_7_7 >= 1, c_7_7 <= 9),
 And(c_7_8 >= 1, c_7_8 <= 9),
 And(c_7_9 >= 1, c_7_9 <= 9),
 And(c_8_1 >= 1, c_8_1 <= 9),
 And(c_8_2 >= 1, c_8_2 <= 9),
 And(c_8_3 >= 1, c_8_3 <= 9),
 And(c_8_4 >= 1, c_8_4 <= 9),
 And(c_8_5 >= 1, c_8_5 <= 9),
 And(c_8_6 >= 1, c_8_6 <= 9),
 And(c_8_7 >= 1, c_8_7 <= 9),
 And(c_8_8 >= 1, c_8_8 <= 9),
 And(c_8_9 >= 1, c_8_9 <= 9),
 And(c_9_1 >= 1, c_9_1 <= 9),
 And(c_9_2 >= 1, c_9_2 <= 9),
 And(c_9_3 >= 1, c_9_3 <= 9),
 And(c_9_4 >= 1, c_9_4 <= 9),
 And(c_9_5 >= 1, c_9_5 <= 9),
 And(c_9_6 >= 1, c_9_6 <= 9),
 And(c_9_7 >= 1, c_9_7 <= 9),
 And(c_9_8 >= 1, c_9_8 <= 9),
 And(c_9_9 >= 1, c_9_9 <= 9)]

We then encode the row constraint. Every row must have a digit appear only once.

In [6]:
# 'Distinct' makes sure that the ith row shall be full of distinct numbers, that is, no number occuring twice.
# Where, i loops through all the rows of the board.

row_constraint = []

for i in range(9):
    # Get a row of the board.
    row = board[i]
    
    # Add the distinct constraint for this particular row.
    row_constraint.append(z3.Distinct(row))    

row_constraint
Out[6]:
[Distinct(c_1_1,
          c_1_2,
          c_1_3,
          c_1_4,
          c_1_5,
          c_1_6,
          c_1_7,
          c_1_8,
          c_1_9),
 Distinct(c_2_1,
          c_2_2,
          c_2_3,
          c_2_4,
          c_2_5,
          c_2_6,
          c_2_7,
          c_2_8,
          c_2_9),
 Distinct(c_3_1,
          c_3_2,
          c_3_3,
          c_3_4,
          c_3_5,
          c_3_6,
          c_3_7,
          c_3_8,
          c_3_9),
 Distinct(c_4_1,
          c_4_2,
          c_4_3,
          c_4_4,
          c_4_5,
          c_4_6,
          c_4_7,
          c_4_8,
          c_4_9),
 Distinct(c_5_1,
          c_5_2,
          c_5_3,
          c_5_4,
          c_5_5,
          c_5_6,
          c_5_7,
          c_5_8,
          c_5_9),
 Distinct(c_6_1,
          c_6_2,
          c_6_3,
          c_6_4,
          c_6_5,
          c_6_6,
          c_6_7,
          c_6_8,
          c_6_9),
 Distinct(c_7_1,
          c_7_2,
          c_7_3,
          c_7_4,
          c_7_5,
          c_7_6,
          c_7_7,
          c_7_8,
          c_7_9),
 Distinct(c_8_1,
          c_8_2,
          c_8_3,
          c_8_4,
          c_8_5,
          c_8_6,
          c_8_7,
          c_8_8,
          c_8_9),
 Distinct(c_9_1,
          c_9_2,
          c_9_3,
          c_9_4,
          c_9_5,
          c_9_6,
          c_9_7,
          c_9_8,
          c_9_9)]

We then encode the column constraint. Each column must have a digit appear only once.

In [7]:
# 'Distinct' makes sure that the jth column of the ith row shall be full of distinct numbers, that is, no number occuring twice.
# Where, i loops through all the rows of the board and j loops through all the columns of the board.

col_constraint = []

for j in range(9):
    
    # Get the column.
    column = []
    for i in range(9):
        # Append all rows for this particular column.
        column.append(board[i][j])
        
    # Add the distinct constraint for this particular column.
    col_constraint.append(z3.Distinct(column))

col_constraint
Out[7]:
[Distinct(c_1_1,
          c_2_1,
          c_3_1,
          c_4_1,
          c_5_1,
          c_6_1,
          c_7_1,
          c_8_1,
          c_9_1),
 Distinct(c_1_2,
          c_2_2,
          c_3_2,
          c_4_2,
          c_5_2,
          c_6_2,
          c_7_2,
          c_8_2,
          c_9_2),
 Distinct(c_1_3,
          c_2_3,
          c_3_3,
          c_4_3,
          c_5_3,
          c_6_3,
          c_7_3,
          c_8_3,
          c_9_3),
 Distinct(c_1_4,
          c_2_4,
          c_3_4,
          c_4_4,
          c_5_4,
          c_6_4,
          c_7_4,
          c_8_4,
          c_9_4),
 Distinct(c_1_5,
          c_2_5,
          c_3_5,
          c_4_5,
          c_5_5,
          c_6_5,
          c_7_5,
          c_8_5,
          c_9_5),
 Distinct(c_1_6,
          c_2_6,
          c_3_6,
          c_4_6,
          c_5_6,
          c_6_6,
          c_7_6,
          c_8_6,
          c_9_6),
 Distinct(c_1_7,
          c_2_7,
          c_3_7,
          c_4_7,
          c_5_7,
          c_6_7,
          c_7_7,
          c_8_7,
          c_9_7),
 Distinct(c_1_8,
          c_2_8,
          c_3_8,
          c_4_8,
          c_5_8,
          c_6_8,
          c_7_8,
          c_8_8,
          c_9_8),
 Distinct(c_1_9,
          c_2_9,
          c_3_9,
          c_4_9,
          c_5_9,
          c_6_9,
          c_7_9,
          c_8_9,
          c_9_9)]

We then encode the sub-square constraint. Every sub-square must have a digit appear only once.

In [8]:
# The first loop goes through all rows and columns in a subsquare, and then all subsquares are looped through.

ssq_constraint = []

# Iterate over all subsquares.
for i0 in range(3):
    for j0 in range(3):
        
        # Inside a subsquare, get all the cells.
        sub_square = []
        
        for i in range(3):
            for j in range(3):
                sub_square.append(board[3*i0 + i][3*j0 + j])
        
        # Add the distinct constraint for this particular subsquare.
        ssq_constraint.append(z3.Distinct(sub_square))

ssq_constraint
Out[8]:
[Distinct(c_1_1,
          c_1_2,
          c_1_3,
          c_2_1,
          c_2_2,
          c_2_3,
          c_3_1,
          c_3_2,
          c_3_3),
 Distinct(c_1_4,
          c_1_5,
          c_1_6,
          c_2_4,
          c_2_5,
          c_2_6,
          c_3_4,
          c_3_5,
          c_3_6),
 Distinct(c_1_7,
          c_1_8,
          c_1_9,
          c_2_7,
          c_2_8,
          c_2_9,
          c_3_7,
          c_3_8,
          c_3_9),
 Distinct(c_4_1,
          c_4_2,
          c_4_3,
          c_5_1,
          c_5_2,
          c_5_3,
          c_6_1,
          c_6_2,
          c_6_3),
 Distinct(c_4_4,
          c_4_5,
          c_4_6,
          c_5_4,
          c_5_5,
          c_5_6,
          c_6_4,
          c_6_5,
          c_6_6),
 Distinct(c_4_7,
          c_4_8,
          c_4_9,
          c_5_7,
          c_5_8,
          c_5_9,
          c_6_7,
          c_6_8,
          c_6_9),
 Distinct(c_7_1,
          c_7_2,
          c_7_3,
          c_8_1,
          c_8_2,
          c_8_3,
          c_9_1,
          c_9_2,
          c_9_3),
 Distinct(c_7_4,
          c_7_5,
          c_7_6,
          c_8_4,
          c_8_5,
          c_8_6,
          c_9_4,
          c_9_5,
          c_9_6),
 Distinct(c_7_7,
          c_7_8,
          c_7_9,
          c_8_7,
          c_8_8,
          c_8_9,
          c_9_7,
          c_9_8,
          c_9_9)]

Solving constraints

Using Z3's solving abilities.

In [9]:
# Initialize a solver instance
s = z3.Solver()
s
Out[9]:
[]

Add all the constraints to the solver.

In [10]:
# 'add' method adds the constraints to the solver.
s.add(instance_constraint)
s.add(cell_constraint)
s.add(row_constraint)
s.add(col_constraint)
s.add(ssq_constraint)

# or just pass a cumulative of constraints to the solver.
#s.add(instance_constraint + cell_constraint + row_constraint + col_constraint + ssq_constraint)

s
Out[10]:
[If(True, True, c_1_1 = 0), If(True, True, c_1_2 = 0), If(True, True, c_1_3 = 0), If(True, True, c_1_4 = 0), If(False, True, c_1_5 = 9), If(False, True, c_1_6 = 4), If(True, True, c_1_7 = 0), If(False, True, c_1_8 = 3), If(True, True, c_1_9 = 0), If(True, True, c_2_1 = 0), If(True, True, c_2_2 = 0), If(True, True, c_2_3 = 0), If(False, True, c_2_4 = 5), If(False, True, c_2_5 = 1), If(True, True, c_2_6 = 0), If(True, True, c_2_7 = 0), If(True, True, c_2_8 = 0), If(False, True, c_2_9 = 7), If(True, True, c_3_1 = 0), If(False, True, c_3_2 = 8), If(False, True, c_3_3 = 9), If(True, True, c_3_4 = 0), If(True, True, c_3_5 = 0), If(True, True, c_3_6 = 0), If(True, True, c_3_7 = 0), If(False, True, c_3_8 = 4), If(True, True, c_3_9 = 0), If(True, True, c_4_1 = 0), If(True, True, c_4_2 = 0), If(True, True, c_4_3 = 0), If(True, True, c_4_4 = 0), If(True, True, c_4_5 = 0), If(True, True, c_4_6 = 0), If(False, True, c_4_7 = 2), If(True, True, c_4_8 = 0), If(False, True, c_4_9 = 8), If(True, True, c_5_1 = 0), If(False, True, c_5_2 = 6), If(True, True, c_5_3 = 0), If(False, True, c_5_4 = 2), If(True, True, c_5_5 = 0), If(False, True, c_5_6 = 1), If(True, True, c_5_7 = 0), If(False, True, c_5_8 = 5), If(True, True, c_5_9 = 0), If(False, True, c_6_1 = 1), If(True, True, c_6_2 = 0), If(False, True, c_6_3 = 2), If(True, True, c_6_4 = 0), If(True, True, c_6_5 = 0), If(True, True, c_6_6 = 0), If(True, True, c_6_7 = 0), If(True, True, c_6_8 = 0), If(True, True, c_6_9 = 0), If(True, True, c_7_1 = 0), If(False, True, c_7_2 = 7), If(True, True, c_7_3 = 0), If(True, True, c_7_4 = 0), If(True, True, c_7_5 = 0), If(True, True, c_7_6 = 0), If(False, True, c_7_7 = 5), If(False, True, c_7_8 = 2), If(True, True, c_7_9 = 0), If(False, True, c_8_1 = 9), If(True, True, c_8_2 = 0), If(True, True, c_8_3 = 0), If(True, True, c_8_4 = 0), If(False, True, c_8_5 = 6), If(False, True, c_8_6 = 5), If(True, True, c_8_7 = 0), If(True, True, c_8_8 = 0), If(True, True, c_8_9 = 0), If(True, True, c_9_1 = 0), If(False, True, c_9_2 = 4), If(True, True, c_9_3 = 0), If(False, True, c_9_4 = 9), If(False, True, c_9_5 = 7), If(True, True, c_9_6 = 0), If(True, True, c_9_7 = 0), If(True, True, c_9_8 = 0), If(True, True, c_9_9 = 0), c_1_1 ≥ 1 ∧ c_1_1 ≤ 9, c_1_2 ≥ 1 ∧ c_1_2 ≤ 9, c_1_3 ≥ 1 ∧ c_1_3 ≤ 9, c_1_4 ≥ 1 ∧ c_1_4 ≤ 9, c_1_5 ≥ 1 ∧ c_1_5 ≤ 9, c_1_6 ≥ 1 ∧ c_1_6 ≤ 9, c_1_7 ≥ 1 ∧ c_1_7 ≤ 9, c_1_8 ≥ 1 ∧ c_1_8 ≤ 9, c_1_9 ≥ 1 ∧ c_1_9 ≤ 9, c_2_1 ≥ 1 ∧ c_2_1 ≤ 9, c_2_2 ≥ 1 ∧ c_2_2 ≤ 9, c_2_3 ≥ 1 ∧ c_2_3 ≤ 9, c_2_4 ≥ 1 ∧ c_2_4 ≤ 9, c_2_5 ≥ 1 ∧ c_2_5 ≤ 9, c_2_6 ≥ 1 ∧ c_2_6 ≤ 9, c_2_7 ≥ 1 ∧ c_2_7 ≤ 9, c_2_8 ≥ 1 ∧ c_2_8 ≤ 9, c_2_9 ≥ 1 ∧ c_2_9 ≤ 9, c_3_1 ≥ 1 ∧ c_3_1 ≤ 9, c_3_2 ≥ 1 ∧ c_3_2 ≤ 9, c_3_3 ≥ 1 ∧ c_3_3 ≤ 9, c_3_4 ≥ 1 ∧ c_3_4 ≤ 9, c_3_5 ≥ 1 ∧ c_3_5 ≤ 9, c_3_6 ≥ 1 ∧ c_3_6 ≤ 9, c_3_7 ≥ 1 ∧ c_3_7 ≤ 9, c_3_8 ≥ 1 ∧ c_3_8 ≤ 9, c_3_9 ≥ 1 ∧ c_3_9 ≤ 9, c_4_1 ≥ 1 ∧ c_4_1 ≤ 9, c_4_2 ≥ 1 ∧ c_4_2 ≤ 9, c_4_3 ≥ 1 ∧ c_4_3 ≤ 9, c_4_4 ≥ 1 ∧ c_4_4 ≤ 9, c_4_5 ≥ 1 ∧ c_4_5 ≤ 9, c_4_6 ≥ 1 ∧ c_4_6 ≤ 9, c_4_7 ≥ 1 ∧ c_4_7 ≤ 9, c_4_8 ≥ 1 ∧ c_4_8 ≤ 9, c_4_9 ≥ 1 ∧ c_4_9 ≤ 9, c_5_1 ≥ 1 ∧ c_5_1 ≤ 9, c_5_2 ≥ 1 ∧ c_5_2 ≤ 9, c_5_3 ≥ 1 ∧ c_5_3 ≤ 9, c_5_4 ≥ 1 ∧ c_5_4 ≤ 9, c_5_5 ≥ 1 ∧ c_5_5 ≤ 9, c_5_6 ≥ 1 ∧ c_5_6 ≤ 9, c_5_7 ≥ 1 ∧ c_5_7 ≤ 9, c_5_8 ≥ 1 ∧ c_5_8 ≤ 9, c_5_9 ≥ 1 ∧ c_5_9 ≤ 9, c_6_1 ≥ 1 ∧ c_6_1 ≤ 9, c_6_2 ≥ 1 ∧ c_6_2 ≤ 9, c_6_3 ≥ 1 ∧ c_6_3 ≤ 9, …]

Check if the scenario is satisfiable (problem with the given constraints and instance).

In [11]:
# 'check' method verifies the SAT nature of the scenario.

s.check()
Out[11]:
sat

If it is satisfiable, then we move on to creating a model for it. Model is the solution of a satisfiability problem (aka system of constraints).

In [12]:
# 'model' method instantiates a model for the given scenario.

if s.check() == z3.sat:
    m = s.model()
    print(m)
else:
    m = None
    print("Unsatisfiable")
[c_9_9 = 6,
 c_1_4 = 8,
 c_6_7 = 7,
 c_7_1 = 3,
 c_4_3 = 3,
 c_6_6 = 9,
 c_7_6 = 8,
 c_9_8 = 8,
 c_7_5 = 4,
 c_9_3 = 1,
 c_7_4 = 1,
 c_8_3 = 8,
 c_3_9 = 5,
 c_2_8 = 9,
 c_6_9 = 3,
 c_9_6 = 2,
 c_3_5 = 2,
 c_1_2 = 1,
 c_1_7 = 6,
 c_4_2 = 9,
 c_2_6 = 6,
 c_9_1 = 5,
 c_1_9 = 2,
 c_7_3 = 6,
 c_3_6 = 3,
 c_8_2 = 2,
 c_4_6 = 7,
 c_9_7 = 3,
 c_5_5 = 3,
 c_7_9 = 9,
 c_5_3 = 7,
 c_5_9 = 4,
 c_8_9 = 1,
 c_4_5 = 5,
 c_8_7 = 4,
 c_8_8 = 7,
 c_6_5 = 8,
 c_2_3 = 4,
 c_5_1 = 8,
 c_2_7 = 8,
 c_4_1 = 4,
 c_6_2 = 5,
 c_6_8 = 6,
 c_3_1 = 6,
 c_1_1 = 7,
 c_1_3 = 5,
 c_4_4 = 6,
 c_2_1 = 2,
 c_8_4 = 3,
 c_4_8 = 1,
 c_3_4 = 7,
 c_2_2 = 3,
 c_5_7 = 9,
 c_6_4 = 4,
 c_3_7 = 1,
 c_9_5 = 7,
 c_9_4 = 9,
 c_9_2 = 4,
 c_8_6 = 5,
 c_8_5 = 6,
 c_8_1 = 9,
 c_7_8 = 2,
 c_7_7 = 5,
 c_7_2 = 7,
 c_6_3 = 2,
 c_6_1 = 1,
 c_5_8 = 5,
 c_5_6 = 1,
 c_5_4 = 2,
 c_5_2 = 6,
 c_4_9 = 8,
 c_4_7 = 2,
 c_3_8 = 4,
 c_3_3 = 9,
 c_3_2 = 8,
 c_2_9 = 7,
 c_2_5 = 1,
 c_2_4 = 5,
 c_1_8 = 3,
 c_1_6 = 4,
 c_1_5 = 9]
In [13]:
# 'evaluate' method computes and gives the output of the model.

if m != None:
    solution = []
    for i in range(9):
        column = []
        for j in range(9):
            column.append(m.evaluate(board[i][j]))
        solution.append(column)
    z3.print_matrix(solution)
[[7, 1, 5, 8, 9, 4, 6, 3, 2],
 [2, 3, 4, 5, 1, 6, 8, 9, 7],
 [6, 8, 9, 7, 2, 3, 1, 4, 5],
 [4, 9, 3, 6, 5, 7, 2, 1, 8],
 [8, 6, 7, 2, 3, 1, 9, 5, 4],
 [1, 5, 2, 4, 8, 9, 7, 6, 3],
 [3, 7, 6, 1, 4, 8, 5, 2, 9],
 [9, 2, 8, 3, 6, 5, 4, 7, 1],
 [5, 4, 1, 9, 7, 2, 3, 8, 6]]
In [ ]: