Sudoku Examples#
This section includes source code for all of the Gurobi sudoku examples.
The same source code can be found in the examples
directory of the
Gurobi distribution.
/* Copyright 2024, Gurobi Optimization, LLC */
/*
Sudoku example.
The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid
of 3x3 grids. Each cell in the grid must take a value from 0 to 9.
No two grid cells in the same row, column, or 3x3 subgrid may take the
same value.
In the MIP formulation, binary variables x[i,j,v] indicate whether
cell <i,j> takes value 'v'. The constraints are as follows:
1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)
2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)
3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)
4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)
Input datasets for this example can be found in examples/data/sudoku*.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "gurobi_c.h"
#define SUBDIM 3
#define DIM (SUBDIM*SUBDIM)
int
main(int argc,
char *argv[])
{
FILE *fp = NULL;
GRBenv *env = NULL;
GRBmodel *model = NULL;
int board[DIM][DIM];
char inputline[100];
int ind[DIM];
double val[DIM];
double lb[DIM*DIM*DIM];
char vtype[DIM*DIM*DIM];
char *names[DIM*DIM*DIM];
char namestorage[10*DIM*DIM*DIM];
char *cursor;
int optimstatus;
double objval;
int i, j, v, ig, jg, count;
int error = 0;
if (argc < 2) {
fprintf(stderr, "Usage: sudoku_c datafile\n");
exit(1);
}
fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "Error: unable to open input file %s\n", argv[1]);
exit(1);
}
for (i = 0; i < DIM; i++) {
fgets(inputline, 100, fp);
if (strlen(inputline) < 9) {
fprintf(stderr, "Error: not enough board positions specified\n");
exit(1);
}
for (j = 0; j < DIM; j++) {
board[i][j] = (int) inputline[j] - (int) '1';
if (board[i][j] < 0 || board[i][j] >= DIM)
board[i][j] = -1;
}
}
/* Create an empty model */
cursor = namestorage;
for (i = 0; i < DIM; i++) {
for (j = 0; j < DIM; j++) {
for (v = 0; v < DIM; v++) {
if (board[i][j] == v)
lb[i*DIM*DIM+j*DIM+v] = 1;
else
lb[i*DIM*DIM+j*DIM+v] = 0;
vtype[i*DIM*DIM+j*DIM+v] = GRB_BINARY;
names[i*DIM*DIM+j*DIM+v] = cursor;
sprintf(names[i*DIM*DIM+j*DIM+v], "x[%d,%d,%d]", i, j, v+1);
cursor += strlen(names[i*DIM*DIM+j*DIM+v]) + 1;
}
}
}
/* Create environment */
error = GRBloadenv(&env, "sudoku.log");
if (error) goto QUIT;
/* Create new model */
error = GRBnewmodel(env, &model, "sudoku", DIM*DIM*DIM, NULL, lb, NULL,
vtype, names);
if (error) goto QUIT;
/* Each cell gets a value */
for (i = 0; i < DIM; i++) {
for (j = 0; j < DIM; j++) {
for (v = 0; v < DIM; v++) {
ind[v] = i*DIM*DIM + j*DIM + v;
val[v] = 1.0;
}
error = GRBaddconstr(model, DIM, ind, val, GRB_EQUAL, 1.0, NULL);
if (error) goto QUIT;
}
}
/* Each value must appear once in each row */
for (v = 0; v < DIM; v++) {
for (j = 0; j < DIM; j++) {
for (i = 0; i < DIM; i++) {
ind[i] = i*DIM*DIM + j*DIM + v;
val[i] = 1.0;
}
error = GRBaddconstr(model, DIM, ind, val, GRB_EQUAL, 1.0, NULL);
if (error) goto QUIT;
}
}
/* Each value must appear once in each column */
for (v = 0; v < DIM; v++) {
for (i = 0; i < DIM; i++) {
for (j = 0; j < DIM; j++) {
ind[j] = i*DIM*DIM + j*DIM + v;
val[j] = 1.0;
}
error = GRBaddconstr(model, DIM, ind, val, GRB_EQUAL, 1.0, NULL);
if (error) goto QUIT;
}
}
/* Each value must appear once in each subgrid */
for (v = 0; v < DIM; v++) {
for (ig = 0; ig < SUBDIM; ig++) {
for (jg = 0; jg < SUBDIM; jg++) {
count = 0;
for (i = ig*SUBDIM; i < (ig+1)*SUBDIM; i++) {
for (j = jg*SUBDIM; j < (jg+1)*SUBDIM; j++) {
ind[count] = i*DIM*DIM + j*DIM + v;
val[count] = 1.0;
count++;
}
}
error = GRBaddconstr(model, DIM, ind, val, GRB_EQUAL, 1.0, NULL);
if (error) goto QUIT;
}
}
}
/* Optimize model */
error = GRBoptimize(model);
if (error) goto QUIT;
/* Write model to 'sudoku.lp' */
error = GRBwrite(model, "sudoku.lp");
if (error) goto QUIT;
/* Capture solution information */
error = GRBgetintattr(model, GRB_INT_ATTR_STATUS, &optimstatus);
if (error) goto QUIT;
error = GRBgetdblattr(model, GRB_DBL_ATTR_OBJVAL, &objval);
if (error) goto QUIT;
printf("\nOptimization complete\n");
if (optimstatus == GRB_OPTIMAL)
printf("Optimal objective: %.4e\n", objval);
else if (optimstatus == GRB_INF_OR_UNBD)
printf("Model is infeasible or unbounded\n");
else
printf("Optimization was stopped early\n");
printf("\n");
QUIT:
/* Error reporting */
if (error) {
printf("ERROR: %s\n", GRBgeterrormsg(env));
exit(1);
}
fclose(fp);
/* Free model */
GRBfreemodel(model);
/* Free environment */
GRBfreeenv(env);
return 0;
}
/* Copyright 2024, Gurobi Optimization, LLC */
/*
Sudoku example.
The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid
of 3x3 grids. Each cell in the grid must take a value from 0 to 9.
No two grid cells in the same row, column, or 3x3 subgrid may take the
same value.
In the MIP formulation, binary variables x[i,j,v] indicate whether
cell <i,j> takes value 'v'. The constraints are as follows:
1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)
2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)
3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)
4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)
Input datasets for this example can be found in examples/data/sudoku*.
*/
#include "gurobi_c++.h"
#include <sstream>
using namespace std;
#define sd 3
#define n (sd*sd)
string itos(int i) {stringstream s; s << i; return s.str(); }
int
main(int argc,
char *argv[])
{
try {
GRBEnv env = GRBEnv();
GRBModel model = GRBModel(env);
GRBVar vars[n][n][n];
int i, j, v;
// Create 3-D array of model variables
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
for (v = 0; v < n; v++) {
string s = "G_" + itos(i) + "_" + itos(j) + "_" + itos(v);
vars[i][j][v] = model.addVar(0.0, 1.0, 0.0, GRB_BINARY, s);
}
}
}
// Add constraints
// Each cell must take one value
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
GRBLinExpr expr = 0;
for (v = 0; v < n; v++)
expr += vars[i][j][v];
string s = "V_" + itos(i) + "_" + itos(j);
model.addConstr(expr, GRB_EQUAL, 1.0, s);
}
}
// Each value appears once per row
for (i = 0; i < n; i++) {
for (v = 0; v < n; v++) {
GRBLinExpr expr = 0;
for (j = 0; j < n; j++)
expr += vars[i][j][v];
string s = "R_" + itos(i) + "_" + itos(v);
model.addConstr(expr == 1.0, s);
}
}
// Each value appears once per column
for (j = 0; j < n; j++) {
for (v = 0; v < n; v++) {
GRBLinExpr expr = 0;
for (i = 0; i < n; i++)
expr += vars[i][j][v];
string s = "C_" + itos(j) + "_" + itos(v);
model.addConstr(expr == 1.0, s);
}
}
// Each value appears once per sub-grid
for (v = 0; v < n; v++) {
for (int i0 = 0; i0 < sd; i0++) {
for (int j0 = 0; j0 < sd; j0++) {
GRBLinExpr expr = 0;
for (int i1 = 0; i1 < sd; i1++) {
for (int j1 = 0; j1 < sd; j1++) {
expr += vars[i0*sd+i1][j0*sd+j1][v];
}
}
string s = "Sub_" + itos(v) + "_" + itos(i0) + "_" + itos(j0);
model.addConstr(expr == 1.0, s);
}
}
}
// Fix variables associated with pre-specified cells
char input[10];
for (i = 0; i < n; i++) {
cin >> input;
for (j = 0; j < n; j++) {
int val = (int) input[j] - 48 - 1; // 0-based
if (val >= 0)
vars[i][j][val].set(GRB_DoubleAttr_LB, 1.0);
}
}
// Optimize model
model.optimize();
// Write model to file
model.write("sudoku.lp");
cout << endl;
for (i = 0; i < n; i++) {
for (j = 0; j < n; j++) {
for (v = 0; v < n; v++) {
if (vars[i][j][v].get(GRB_DoubleAttr_X) > 0.5)
cout << v+1;
}
}
cout << endl;
}
cout << endl;
} catch(GRBException e) {
cout << "Error code = " << e.getErrorCode() << endl;
cout << e.getMessage() << endl;
} catch (...) {
cout << "Error during optimization" << endl;
}
return 0;
}
/* Copyright 2024, Gurobi Optimization, LLC */
/*
Sudoku example.
The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid
of 3x3 grids. Each cell in the grid must take a value from 0 to 9.
No two grid cells in the same row, column, or 3x3 subgrid may take the
same value.
In the MIP formulation, binary variables x[i,j,v] indicate whether
cell <i,j> takes value 'v'. The constraints are as follows:
1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)
2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)
3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)
4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)
Input datasets for this example can be found in examples/data/sudoku*.
*/
using System;
using System.IO;
using Gurobi;
class sudoku_cs
{
static void Main(string[] args)
{
int n = 9;
int s = 3;
if (args.Length < 1) {
Console.Out.WriteLine("Usage: sudoku_cs filename");
return;
}
try {
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
// Create 3-D array of model variables
GRBVar[,,] vars = new GRBVar[n,n,n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int v = 0; v < n; v++) {
string st = "G_" + i.ToString() + "_" + j.ToString()
+ "_" + v.ToString();
vars[i,j,v] = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, st);
}
}
}
// Add constraints
GRBLinExpr expr;
// Each cell must take one value
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
expr = 0.0;
for (int v = 0; v < n; v++)
expr.AddTerm(1.0, vars[i,j,v]);
string st = "V_" + i.ToString() + "_" + j.ToString();
model.AddConstr(expr == 1.0, st);
}
}
// Each value appears once per row
for (int i = 0; i < n; i++) {
for (int v = 0; v < n; v++) {
expr = 0.0;
for (int j = 0; j < n; j++)
expr.AddTerm(1.0, vars[i,j,v]);
string st = "R_" + i.ToString() + "_" + v.ToString();
model.AddConstr(expr == 1.0, st);
}
}
// Each value appears once per column
for (int j = 0; j < n; j++) {
for (int v = 0; v < n; v++) {
expr = 0.0;
for (int i = 0; i < n; i++)
expr.AddTerm(1.0, vars[i,j,v]);
string st = "C_" + j.ToString() + "_" + v.ToString();
model.AddConstr(expr == 1.0, st);
}
}
// Each value appears once per sub-grid
for (int v = 0; v < n; v++) {
for (int i0 = 0; i0 < s; i0++) {
for (int j0 = 0; j0 < s; j0++) {
expr = 0.0;
for (int i1 = 0; i1 < s; i1++) {
for (int j1 = 0; j1 < s; j1++) {
expr.AddTerm(1.0, vars[i0*s+i1,j0*s+j1,v]);
}
}
string st = "Sub_" + v.ToString() + "_" + i0.ToString()
+ "_" + j0.ToString();
model.AddConstr(expr == 1.0, st);
}
}
}
// Fix variables associated with pre-specified cells
StreamReader sr = File.OpenText(args[0]);
for (int i = 0; i < n; i++) {
string input = sr.ReadLine();
for (int j = 0; j < n; j++) {
int val = (int) input[j] - 48 - 1; // 0-based
if (val >= 0)
vars[i,j,val].LB = 1.0;
}
}
// Optimize model
model.Optimize();
// Write model to file
model.Write("sudoku.lp");
double[,,] x = model.Get(GRB.DoubleAttr.X, vars);
Console.WriteLine();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int v = 0; v < n; v++) {
if (x[i,j,v] > 0.5) {
Console.Write(v+1);
}
}
}
Console.WriteLine();
}
// Dispose of model and env
model.Dispose();
env.Dispose();
} catch (GRBException e) {
Console.WriteLine("Error code: " + e.ErrorCode + ". " + e.Message);
}
}
}
/* Copyright 2024, Gurobi Optimization, LLC */
/*
Sudoku example.
The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid
of 3x3 grids. Each cell in the grid must take a value from 0 to 9.
No two grid cells in the same row, column, or 3x3 subgrid may take the
same value.
In the MIP formulation, binary variables x[i,j,v] indicate whether
cell <i,j> takes value 'v'. The constraints are as follows:
1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)
2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)
3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)
4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)
Input datasets for this example can be found in examples/data/sudoku*.
*/
import com.gurobi.gurobi.*;
import java.io.*;
public class Sudoku {
public static void main(String[] args) {
int n = 9;
int s = 3;
if (args.length < 1) {
System.out.println("Usage: java Sudoku filename");
System.exit(1);
}
try {
GRBEnv env = new GRBEnv();
GRBModel model = new GRBModel(env);
// Create 3-D array of model variables
GRBVar[][][] vars = new GRBVar[n][n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int v = 0; v < n; v++) {
String st = "G_" + String.valueOf(i) + "_" + String.valueOf(j)
+ "_" + String.valueOf(v);
vars[i][j][v] = model.addVar(0.0, 1.0, 0.0, GRB.BINARY, st);
}
}
}
// Add constraints
GRBLinExpr expr;
// Each cell must take one value
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
expr = new GRBLinExpr();
expr.addTerms(null, vars[i][j]);
String st = "V_" + String.valueOf(i) + "_" + String.valueOf(j);
model.addConstr(expr, GRB.EQUAL, 1.0, st);
}
}
// Each value appears once per row
for (int i = 0; i < n; i++) {
for (int v = 0; v < n; v++) {
expr = new GRBLinExpr();
for (int j = 0; j < n; j++)
expr.addTerm(1.0, vars[i][j][v]);
String st = "R_" + String.valueOf(i) + "_" + String.valueOf(v);
model.addConstr(expr, GRB.EQUAL, 1.0, st);
}
}
// Each value appears once per column
for (int j = 0; j < n; j++) {
for (int v = 0; v < n; v++) {
expr = new GRBLinExpr();
for (int i = 0; i < n; i++)
expr.addTerm(1.0, vars[i][j][v]);
String st = "C_" + String.valueOf(j) + "_" + String.valueOf(v);
model.addConstr(expr, GRB.EQUAL, 1.0, st);
}
}
// Each value appears once per sub-grid
for (int v = 0; v < n; v++) {
for (int i0 = 0; i0 < s; i0++) {
for (int j0 = 0; j0 < s; j0++) {
expr = new GRBLinExpr();
for (int i1 = 0; i1 < s; i1++) {
for (int j1 = 0; j1 < s; j1++) {
expr.addTerm(1.0, vars[i0*s+i1][j0*s+j1][v]);
}
}
String st = "Sub_" + String.valueOf(v) + "_" + String.valueOf(i0)
+ "_" + String.valueOf(j0);
model.addConstr(expr, GRB.EQUAL, 1.0, st);
}
}
}
// Fix variables associated with pre-specified cells
File file = new File(args[0]);
FileInputStream fis = new FileInputStream(file);
byte[] input = new byte[n];
for (int i = 0; i < n; i++) {
fis.read(input);
for (int j = 0; j < n; j++) {
int val = (int) input[j] - 48 - 1; // 0-based
if (val >= 0)
vars[i][j][val].set(GRB.DoubleAttr.LB, 1.0);
}
// read the endline byte
fis.read();
}
// Optimize model
model.optimize();
// Write model to file
model.write("sudoku.lp");
double[][][] x = model.get(GRB.DoubleAttr.X, vars);
System.out.println();
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
for (int v = 0; v < n; v++) {
if (x[i][j][v] > 0.5) {
System.out.print(v+1);
}
}
}
System.out.println();
}
// Dispose of model and environment
model.dispose();
env.dispose();
} catch (GRBException e) {
System.out.println("Error code: " + e.getErrorCode() + ". " +
e.getMessage());
} catch (IOException e) {
System.out.println("IO Error");
}
}
}
function sudoku(filename)
% Copyright 2024, Gurobi Optimization, LLC */
%
% Sudoku example.
%
% The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid
% of 3x3 grids. Each cell in the grid must take a value from 0 to 9.
% No two grid cells in the same row, column, or 3x3 subgrid may take the
% same value.
%
% In the MIP formulation, binary variables x[i,j,v] indicate whether
% cell <i,j> takes value 'v'. The constraints are as follows:
% 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)
% 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)
% 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)
% 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)
%
% Input datasets for this example can be found in examples/data/sudoku*.
%
SUBDIM = 3;
DIM = SUBDIM*SUBDIM;
fileID = fopen(filename);
if fileID == -1
fprintf('Could not read file %s, quit\n', filename);
return;
end
board = repmat(-1, DIM, DIM);
for i = 1:DIM
s = fgets(fileID, 100);
if length(s) <= DIM
fprintf('Error: not enough board positions specified, quit\n');
return;
end
for j = 1:DIM
if s(j) ~= '.'
board(i, j) = str2double(s(j));
if board(i,j) < 1 || board(i,j) > DIM
fprintf('Error: Unexpected character in Input line %d, quit\n', i);
return;
end
end
end
end
% Map X(i,j,k) into an index variable in the model
nVars = DIM * DIM * DIM;
% Build model
model.vtype = repmat('B', nVars, 1);
model.lb = zeros(nVars, 1);
model.ub = ones(nVars, 1);
for i = 1:DIM
for j = 1:DIM
for v = 1:DIM
var = (i-1)*DIM*DIM + (j-1)*DIM + v;
model.varnames{var} = sprintf('x[%d,%d,%d]', i, j, v);
end
end
end
% Create constraints:
nRows = 4 * DIM * DIM;
model.A = sparse(nRows, nVars);
model.rhs = ones(nRows, 1);
model.sense = repmat('=', nRows, 1);
Row = 1;
% Each cell gets a value */
for i = 1:DIM
for j = 1:DIM
for v = 1:DIM
if board(i,j) == v
model.lb((i-1)*DIM*DIM + (j-1)*DIM + v) = 1;
end
model.A(Row, (i-1)*DIM*DIM + (j-1)*DIM + v) = 1;
end
Row = Row + 1;
end
end
% Each value must appear once in each row
for v = 1:DIM
for j = 1:DIM
for i = 1:DIM
model.A(Row, (i-1)*DIM*DIM + (j-1)*DIM + v) = 1;
end
Row = Row + 1;
end
end
% Each value must appear once in each column
for v = 1:DIM
for i = 1:DIM
for j = 1:DIM
model.A(Row, (i-1)*DIM*DIM + (j-1)*DIM + v) = 1;
end
Row = Row + 1;
end
end
% Each value must appear once in each subgrid
for v = 1:DIM
for ig = 0: SUBDIM-1
for jg = 0: SUBDIM-1
for i = ig*SUBDIM+1:(ig+1)*SUBDIM
for j = jg*SUBDIM+1:(jg+1)*SUBDIM
model.A(Row, (i-1)*DIM*DIM + (j-1)*DIM + v) = 1;
end
end
Row = Row + 1;
end
end
end
% Save model
gurobi_write(model, 'sudoku_m.lp');
% Optimize model
params.logfile = 'sudoku_m.log';
result = gurobi(model, params);
if strcmp(result.status, 'OPTIMAL')
fprintf('Solution:\n');
for i = 1:DIM
for j = 1:DIM
for v = 1:DIM
var = (i-1)*DIM*DIM + (j-1)*DIM + v;
if result.x(var) > 0.99
fprintf('%d', v);
end
end
end
fprintf('\n');
end
else
fprintf('Problem was infeasible\n')
end
#!/usr/bin/env python3.11
# Copyright 2024, Gurobi Optimization, LLC
# Sudoku example.
# The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid
# of 3x3 grids. Each cell in the grid must take a value from 0 to 9.
# No two grid cells in the same row, column, or 3x3 subgrid may take the
# same value.
#
# In the MIP formulation, binary variables x[i,j,v] indicate whether
# cell <i,j> takes value 'v'. The constraints are as follows:
# 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)
# 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)
# 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)
# 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)
#
# Input datasets for this example can be found in examples/data/sudoku*.
import sys
import math
import gurobipy as gp
from gurobipy import GRB
if len(sys.argv) < 2:
print("Usage: sudoku.py filename")
sys.exit(0)
f = open(sys.argv[1])
grid = f.read().split()
n = len(grid[0])
s = int(math.sqrt(n))
# Create our 3-D array of model variables
model = gp.Model("sudoku")
vars = model.addVars(n, n, n, vtype=GRB.BINARY, name="G")
# Fix variables associated with cells whose values are pre-specified
for i in range(n):
for j in range(n):
if grid[i][j] != ".":
v = int(grid[i][j]) - 1
vars[i, j, v].LB = 1
# Each cell must take one value
model.addConstrs(
(vars.sum(i, j, "*") == 1 for i in range(n) for j in range(n)), name="V"
)
# Each value appears once per row
model.addConstrs(
(vars.sum(i, "*", v) == 1 for i in range(n) for v in range(n)), name="R"
)
# Each value appears once per column
model.addConstrs(
(vars.sum("*", j, v) == 1 for j in range(n) for v in range(n)), name="C"
)
# Each value appears once per subgrid
model.addConstrs(
(
gp.quicksum(
vars[i, j, v]
for i in range(i0 * s, (i0 + 1) * s)
for j in range(j0 * s, (j0 + 1) * s)
)
== 1
for v in range(n)
for i0 in range(s)
for j0 in range(s)
),
name="Sub",
)
model.optimize()
model.write("sudoku.lp")
print("")
print("Solution:")
print("")
# Retrieve optimization result
solution = model.getAttr("X", vars)
for i in range(n):
sol = ""
for j in range(n):
for v in range(n):
if solution[i, j, v] > 0.5:
sol += str(v + 1)
print(sol)
# Copyright 2024, Gurobi Optimization, LLC */
#
# Sudoku example.
#
# The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid
# of 3x3 grids. Each cell in the grid must take a value from 0 to 9.
# No two grid cells in the same row, column, or 3x3 subgrid may take the
# same value.
#
# In the MIP formulation, binary variables x[i,j,v] indicate whether
# cell <i,j> takes value 'v'. The constraints are as follows:
# 1. Each cell must take exactly one value (sum_v x[i,j,v] = 1)
# 2. Each value is used exactly once per row (sum_i x[i,j,v] = 1)
# 3. Each value is used exactly once per column (sum_j x[i,j,v] = 1)
# 4. Each value is used exactly once per 3x3 subgrid (sum_grid x[i,j,v] = 1)
#
# Input datasets for this example can be found in examples/data/sudoku*.
#
library(Matrix)
library(gurobi)
args <- commandArgs(trailingOnly = TRUE)
if (length(args) < 1) {
stop('Usage: Rscript sudoku.R filename\n')
}
# Read input file
conn <- file(args[1], open='r')
if(!isOpen(conn)) {
cat('Could not read file',args[1],'\n')
stop('Stop now\n')
}
linn <- readLines(conn)
close(conn)
# Ensure that all lines have the same length as the number of lines, and
# that the character set is the correct one.
# Load fixed positions in board
Dim <- length(linn)
board <- matrix(0, Dim, Dim, byrow = TRUE)
if (Dim != 9) {
cat('Input file',args[1],'has',Dim,'lines instead of 9\n')
stop('Stop now\n')
}
for (i in 1:Dim) {
line <- strsplit(linn[[i]],split='')[[1]]
if (length(line) != Dim) {
cat('Input line',i,'has',length(line),'characters, expected',Dim,'\n')
stop('Stop now\n')
}
for (j in 1:Dim) {
if (line[[j]] != '.') {
k <- as.numeric(line[[j]])
if (k < 1 || k > Dim) {
cat('Unexpected character in Input line',i,'character',j,'\n')
stop('Stop now\n')
} else {
board[i,j] = k
}
}
}
}
# Map X[i,j,k] into an index variable in the model
nVars <- Dim * Dim * Dim
varIdx <- function(i,j,k) {i + (j-1) * Dim + (k-1) * Dim * Dim}
cat('Dataset grid:',Dim,'x',Dim,'\n')
# Set up parameters
params <- list()
params$logfile <- 'sudoku.log'
# Build model
model <- list()
model$modelname <- 'sudoku'
model$modelsense <- 'min'
# Create variable names, types, and bounds
model$vtype <- 'B'
model$lb <- rep(0, nVars)
model$ub <- rep(1, nVars)
model$varnames <- rep('', nVars)
for (i in 1:Dim) {
for (j in 1:Dim) {
for (k in 1:Dim) {
if (board[i,j] == k) model$lb[varIdx(i,j,k)] = 1
model$varnames[varIdx(i,j,k)] = paste0('X',i,j,k)
}
}
}
# Create (empty) constraints:
model$A <- spMatrix(0,nVars)
model$rhs <- c()
model$sense <- c()
model$constrnames <- c()
# Each cell gets a value:
for (i in 1:Dim) {
for (j in 1:Dim) {
B <- spMatrix(1, nVars,
i = rep(1,Dim),
j = varIdx(i,j,1:Dim),
x = rep(1,Dim))
model$A <- rbind(model$A, B)
model$rhs <- c(model$rhs, 1)
model$sense <- c(model$sense, '=')
model$constrnames <- c(model$constrnames, paste0('OneValInCell',i,j))
}
}
# Each value must appear once in each column
for (i in 1:Dim) {
for (k in 1:Dim) {
B <- spMatrix(1, nVars,
i = rep(1,Dim),
j = varIdx(i,1:Dim,k),
x = rep(1,Dim))
model$A <- rbind(model$A, B)
model$rhs <- c(model$rhs, 1)
model$sense <- c(model$sense, '=')
model$constrnames <- c(model$constrnames, paste0('OnceValueInRow',i,k))
}
}
#Each value must appear once in each row
for (j in 1:Dim) {
for (k in 1:Dim) {
B <- spMatrix(1, nVars,
i = rep(1,Dim),
j = varIdx(1:Dim,j,k),
x = rep(1,Dim))
model$A <- rbind(model$A, B)
model$rhs <- c(model$rhs, 1)
model$sense <- c(model$sense, '=')
model$constrnames <- c(model$constrnames, paste0('OnceValueInColumn',j,k))
}
}
# Each value must appear once in each subgrid
SubDim <- 3
for (k in 1:Dim) {
for (g1 in 1:SubDim) {
for (g2 in 1:SubDim) {
B <- spMatrix(1, nVars,
i = rep(1,Dim),
j = c(varIdx(1+(g1-1)*SubDim,(g2-1)*SubDim + 1:SubDim, k),
varIdx(2+(g1-1)*SubDim,(g2-1)*SubDim + 1:SubDim, k),
varIdx(3+(g1-1)*SubDim,(g2-1)*SubDim + 1:SubDim, k)),
x = rep(1,Dim))
model$A <- rbind(model$A, B)
model$rhs <- c(model$rhs, 1)
model$sense <- c(model$sense, '=')
model$constrnames <- c(model$constrnames,
paste0('OnceValueInSubGrid',g1,g2,k))
}
}
}
# Save model
gurobi_write(model, 'sudoku.lp', params)
# Optimize model
result <- gurobi(model, params = params)
if (result$status == 'OPTIMAL') {
cat('Solution:\n')
cat('----------------------------------\n')
for (i in 1:Dim) {
for (j in 1:Dim) {
if (j %% SubDim == 1) cat('| ')
for (k in 1:Dim) {
if (result$x[varIdx(i,j,k)] > 0.99) {
cat(k,' ')
}
}
}
cat('|\n')
if (i %% SubDim == 0) cat('----------------------------------\n')
}
} else {
cat('Problem was infeasible\n')
}
# Clear space
rm(result, model, board, linn, params)
' Copyright 2024, Gurobi Optimization, LLC
'
' Sudoku example.
'
' The Sudoku board is a 9x9 grid, which is further divided into a 3x3 grid
' of 3x3 grids. Each cell in the grid must take a value from 0 to 9.
' No two grid cells in the same row, column, or 3x3 subgrid may take the
' same value.
' In the MIP formulation, binary variables x(i,j,v) indicate whether
' cell <i,j> takes value 'v'. The constraints are as follows:
' 1. Each cell must take exactly one value (sum_v x(i,j,v) = 1)
' 2. Each value is used exactly once per row (sum_i x(i,j,v) = 1)
' 3. Each value is used exactly once per column (sum_j x(i,j,v) = 1)
' 4. Each value is used exactly once per 3x3 subgrid (sum_grid x(i,j,v) = 1)
'
' Input datasets for this example can be found in examples/data/sudoku*.
Imports System
Imports System.IO
Imports Gurobi
Class sudoku_vb
Shared Sub Main(ByVal args as String())
Dim n As Integer = 9
Dim s As Integer = 3
If args.Length < 1 Then
Console.WriteLine("Usage: sudoku_vb filename")
Return
End If
Try
Dim env As New GRBEnv()
Dim model As New GRBModel(env)
' Create 3-D array of model variables
Dim vars As GRBVar(,,) = New GRBVar(n - 1, n - 1, n - 1) {}
For i As Integer = 0 To n - 1
For j As Integer = 0 To n - 1
For v As Integer = 0 To n - 1
Dim st As String = "G_" & i & "_" & j & "_" & v
vars(i, j, v) = model.AddVar(0.0, 1.0, 0.0, GRB.BINARY, st)
Next
Next
Next
' Add constraints
Dim expr As GRBLinExpr
' Each cell must take one value
For i As Integer = 0 To n - 1
For j As Integer = 0 To n - 1
expr = 0
For v As Integer = 0 To n - 1
expr.AddTerm(1.0, vars(i, j, v))
Next
Dim st As String = "V_" & i & "_" & j
model.AddConstr(expr = 1, st)
Next
Next
' Each value appears once per row
For i As Integer = 0 To n - 1
For v As Integer = 0 To n - 1
expr = 0
For j As Integer = 0 To n - 1
expr.AddTerm(1.0, vars(i, j, v))
Next
Dim st As String = "R_" & i & "_" & v
model.AddConstr(expr = 1, st)
Next
Next
' Each value appears once per column
For j As Integer = 0 To n - 1
For v As Integer = 0 To n - 1
expr = 0
For i As Integer = 0 To n - 1
expr.AddTerm(1.0, vars(i, j, v))
Next
Dim st As String = "C_" & j & "_" & v
model.AddConstr(expr = 1, st)
Next
Next
' Each value appears once per sub-grid
For v As Integer = 0 To n - 1
For i0 As Integer = 0 To s - 1
For j0 As Integer = 0 To s - 1
expr = 0
For i1 As Integer = 0 To s - 1
For j1 As Integer = 0 To s - 1
expr.AddTerm(1.0, vars(i0 * s + i1, j0 * s + j1, v))
Next
Next
Dim st As String = "Sub_" & v & "_" & i0 & "_" & j0
model.AddConstr(expr = 1, st)
Next
Next
Next
' Fix variables associated with pre-specified cells
Dim sr As StreamReader = File.OpenText(args(0))
For i As Integer = 0 To n - 1
Dim input As String = sr.ReadLine()
For j As Integer = 0 To n - 1
Dim val As Integer = Microsoft.VisualBasic.Asc(input(j)) - 48 - 1
' 0-based
If val >= 0 Then
vars(i, j, val).LB = 1.0
End If
Next
Next
' Optimize model
model.Optimize()
' Write model to file
model.Write("sudoku.lp")
Dim x As Double(,,) = model.Get(GRB.DoubleAttr.X, vars)
Console.WriteLine()
For i As Integer = 0 To n - 1
For j As Integer = 0 To n - 1
For v As Integer = 0 To n - 1
If x(i, j, v) > 0.5 Then
Console.Write(v + 1)
End If
Next
Next
Console.WriteLine()
Next
' Dispose of model and env
model.Dispose()
env.Dispose()
Catch e As GRBException
Console.WriteLine("Error code: " & e.ErrorCode & ". " & e.Message)
End Try
End Sub
End Class