Sudoku
From Wikipedia's English page.
Sudoku is a logic-based, combinatorial number-placement puzzle. In classic sudoku, the objective is to fill a 9×9 grid with digits so that each column, each row, and each of the nine 3×3 subgrids that compose the grid contain all of the digits from 1 to 9. The puzzle setter provides a partially completed grid, which for a well-posed puzzle has a single solution.
Each column, row, and region of the sudoku grid can only have a number from each of 1 to 9.
For instance, given this initial grid:
The final state (i.e. solution) must be:
Constructing a sudoku model
ConstraintModels.sudoku — Functionsudoku(n; start= Dictionary{Int, Int}(), modeler = :JuMP)Create a model for the sudoku problem of domain 1:n² with optional starting values. The modeler argument accepts :raw, :MOI, and :JuMP (default), which refer respectively to the solver internal model, the MathOptInterface model, and the JuMP model.
# Construct a JuMP model `m` and its associated matrix `grid` for sudoku 9×9
m, grid = sudoku(3)
# Same with a starting instance
instance = [
    9  3  0  0  0  0  0  4  0
    0  0  0  0  4  2  0  9  0
    8  0  0  1  9  6  7  0  0
    0  0  0  4  7  0  0  0  0
    0  2  0  0  0  0  0  6  0
    0  0  0  0  2  3  0  0  0
    0  0  8  5  3  1  0  0  2
    0  9  0  2  8  0  0  0  0
    0  7  0  0  0  0  0  5  3
]
m, grid = sudoku(3, start = instance)
# Run the solver
optimize!(m)
# Retrieve and display the values
solution = value.(grid)
display(solution, Val(:sudoku))Detailed implementation
To start modeling with Sudoku with the solver, we will use JuMP.jl syntax.
- First, create a model with 
JuMP.Model(CBLS.Optimizer). Givenn = 3the grid will be of sizen^2 by n^2(i.e. 9×9) 
using CBLS # the JuMP interface of LocalSearchSolvers.jl
using JuMP
N = n^2
model = JuMP.Model(CBLS.Optimizer)- Create a matrix of variables, where each variable represents a cell of the Sudoku, this means that every variable must be an integer between 1 and 9.
 
(If initial values are provided, the variables representing the known values take will be constant variables, and the rest of the unknown variables are initialized as integers between 1 and 9)
# Create and initialize variables.
if isnothing(start) # If no initial configuration is provided
    @variable(m, X[1:N, 1:N], DiscreteSet(1:N)) # Create a matrix of N*N variables with values from 1 to N
else
    @variable(m, X[1:N, 1:N]) # Create a matrix of N*N variables with no value taken yet
    for i in 1:N, j in 1:N # Iterate through the matrix
        v_ij = start[i,j] # Retrieve the value of the current cell
        if 1 ≤ v_ij ≤ N # If the value of the current cell is a number between 1 and N (i.e. already provided by the initial configuration)
        # Create a constraint forcing the variable representing the current cell to be a constant equal to the value provided by the initial configuration
            @constraint(m, X[i,j] in DiscreteSet(v_ij))
        else
            @constraint(m, X[i,j] in DiscreteSet(1:N)) # Else create a constraint stating that the variable must be between 1 and N
        end
    end
end- Define the rows, columns and block constraints. The solver has a Global Constraint 
AllDifferent()stating that a set of variables must have different values. 
for i in 1:N
    @constraint(m, X[i,:] in AllDifferent()) # All variables on the same row must be different
    @constraint(m, X[:,i] in AllDifferent()) # All variables on the same column must be different
end
for i in 0:(n-1), j in 0:(n-1)
    @constraint(m, vec(X[(i*n+1):(n*(i+1)), (j*n+1):(n*(j+1))]) in AllDifferent()) # All variables on the same block must be different
end
 ```
* Finally, solve model using the `optimize!()` function with the model in arguments *
 ```julia
# Run the solver
optimize!(m)After model is solved, use value.(grid) to get the final value of all variables on the grid matrix, and display the solution using the display() function
# Retrieve and display the values
solution = value.(grid)
display(solution, Val(:sudoku))