Scippy

SCIP

Solving Constraint Integer Programs

sudoku_main.cpp
Go to the documentation of this file.
1/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2/* */
3/* This file is part of the program and library */
4/* SCIP --- Solving Constraint Integer Programs */
5/* */
6/* Copyright (c) 2002-2025 Zuse Institute Berlin (ZIB) */
7/* */
8/* Licensed under the Apache License, Version 2.0 (the "License"); */
9/* you may not use this file except in compliance with the License. */
10/* You may obtain a copy of the License at */
11/* */
12/* http://www.apache.org/licenses/LICENSE-2.0 */
13/* */
14/* Unless required by applicable law or agreed to in writing, software */
15/* distributed under the License is distributed on an "AS IS" BASIS, */
16/* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. */
17/* See the License for the specific language governing permissions and */
18/* limitations under the License. */
19/* */
20/* You should have received a copy of the Apache-2.0 license */
21/* along with SCIP; see the file LICENSE. If not visit scipopt.org. */
22/* */
23/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
24
25/**@file sudoku_main.cpp
26 * @brief Sudoku solver built using constrained integer programming
27 * @author Naga V C Gudapati
28*/
29
30#include <iostream>
31#include <sstream>
32
33#include "sudoku_utils.h"
34
35#include <scip/scip.h>
36#include <scip/scipdefplugins.h>
37
38
39int main(int argc, char *argv[])
40{
41 if( argc < 2 )
42 {
43 std::cerr << "call " << argv[0] << " <puzzle file> " << "\n";
44 exit(1);
45 }
46
47 std::string puzzlefilepath = argv[1];
48 auto puzzle = sudoku::getSudokuPuzzle(puzzlefilepath);
49 std::cout << "The unsolved Sudoku Puzzle is: " << "\n";
50 sudoku::printSudoku(puzzle);
51
52 /*
53 * Setting up the SCIP environment
54 */
55 SCIP* scip = nullptr; /* Declaring the scip environment*/
56 SCIP_CALL( SCIPcreate(&scip) ); /*Creating the SCIP environment */
57 SCIP_CALL( SCIPincludeDefaultPlugins(scip) ); /* include default plugins */
58 SCIP_CALL( SCIPcreateProbBasic(scip, "sudoku") ); /* creating the SCIP Problem. */
59
60 /*
61 * The Sudoku puzzle is a feasibility problem and the objsense can be anything
62 */
64
65 /*
66 * We have to define 9x9x9 variables. Let x_{ijk} where i = 1...9, j = 1...9 and k = 1..9 be those binary variables.
67 * x_{ijk} is the the binary variable representing the number k (1 or 2 or ... 9) in the ith row and jth column
68 */
69 std::vector<std::vector<std::vector< SCIP_VAR* >>> xvars(9, std::vector<std::vector< SCIP_VAR* >>(9, std::vector< SCIP_VAR* >(9)));
70 std::ostringstream namebuf;
71
72 for( size_t i = 0; i < 9; ++i )
73 {
74 for( size_t j = 0; j < 9; ++j )
75 {
76 for( size_t k = 0; k < 9; ++k )
77 {
78 SCIP_VAR* var = nullptr;
79 namebuf.str("");
80 namebuf << "x[" << i << "," << j << "," << k << "]";
82 scip, /* SCIP environment */
83 &var, /* reference to the variable */
84 namebuf.str().c_str(), /* name of the variable */
85 0.0, /* lower bound of the variable */
86 1.0, /* upper bound of the variable */
87 1.0, /* obj. coefficient. */
88 SCIP_VARTYPE_BINARY /* variable is binary */
89 ) );
90 SCIP_CALL( SCIPaddVar(scip, var) );
91 xvars[i][j][k] = var;
92 }
93 }
94 }
95
96 /* for each column j and each number k we must have that only one entry of column j is k; since x_{ijk} is 1
97 * if and only if the i-th entry of the j-th column is k, we can model this requirement with the following linear
98 * constraint:
99 * x_1jk + x_2jk + x_3jk + ... + x_9jk = 1 for each j and k
100 */
101 std::vector< SCIP_CONS* > columnconstraints;
102 for( size_t j = 0; j < 9; ++j )
103 {
104 for( size_t k = 0; k < 9; ++k )
105 {
106 SCIP_CONS* cons = nullptr;
107 namebuf.str("");
108 namebuf << "col_" << j << "_" << k << "]";
109
110 /* we first create an empty equality constraint (i.e. the lhs and rhs are equal to 1) and then add the
111 * variables
112 */
114 scip,
115 &cons, /* pointer to hold the created constraint */
116 namebuf.str().c_str(), /* name of constraint */
117 0, /* number of nonzeros in the constraint */
118 nullptr, /* array with variables of constraint entries */
119 nullptr, /* array with coefficients of constraint entries */
120 1.0, /* left hand side of constraint */
121 1.0) ); /* right hand side of constraint */
122 for( size_t i = 0; i < 9; ++i )
123 {
124 SCIP_CALL( SCIPaddCoefLinear(scip, cons, xvars[i][j][k], 1.0) );
125 }
126
127 SCIP_CALL( SCIPaddCons(scip, cons) );
128 columnconstraints.push_back(cons);
129 }
130 }
131
132 /* for each row i and each number k we must have that only one entry of row i is k; we can model
133 * this requirement with the following linear constraint:
134 * x_i1k + x_i2k + x_i3k + ... + x_i9k = 1 for each i and k
135 */
136 std::vector< SCIP_CONS* > rowconstraints;
137 for( size_t i = 0; i < 9; ++i )
138 {
139 for( size_t k = 0; k < 9; ++k )
140 {
141 SCIP_CONS* cons = nullptr;
142
143 namebuf.str("");
144 namebuf << "row_" << i << "_" << k << "]";
145
146 /* we first create an empty equality constraint (i.e. the lhs and rhs are equal to 1) and then add the
147 * variables
148 */
150 scip,
151 &cons, /* pointer to hold the created constraint */
152 namebuf.str().c_str(), /* name of constraint */
153 0, /* number of nonzeros in the constraint */
154 nullptr, /* array with variables of constraint entries */
155 nullptr, /* array with coefficients of constraint entries */
156 1.0, /* left hand side of constraint */
157 1.0) ); /* right hand side of constraint */
158 for( size_t j = 0; j < 9; ++j )
159 {
160 SCIP_CALL( SCIPaddCoefLinear(scip, cons, xvars[i][j][k], 1.0) );
161 }
162
163 SCIP_CALL( SCIPaddCons(scip, cons) );
164 rowconstraints.push_back(cons);
165 }
166 }
167
168 /* for each 3x3 subgrid we must we must have that only one entry is k;
169 * a subgrid is formed by the entries (i,j) such that i is in {p, p + 1, p + 2} and j is in {q, q + 1, q + 2} for
170 * each (p, q) in {(1,1), (1,4), (1,7), (4,1), (4, 4), (4, 7), (7, 1), (7, 4), (7, 7)}
171 * for the (p,q)-th subgrid we can model this requirement with the following linear constraint:
172 * sum_{i in [p, p + 2], j in [q, q + 2]} x_ijk = 1 for each k
173 */
174 std::vector< SCIP_CONS* > subgridconstraints;
175 for( size_t k = 0; k < 9; ++k )
176 {
177 for( size_t p = 0; p < 3; ++p )
178 {
179 for( size_t q = 0; q < 3; ++q )
180 {
181 SCIP_CONS* cons = nullptr;
182
183 namebuf.str("");
184 namebuf << "subgrid_" << k << "_" << p << "_" << q << "]";
185
186 /* we first create an empty an equality constraint (i.e. the lhs and rhs are equal to 1) and then add the
187 * variables
188 */
190 scip,
191 &cons, /* pointer to hold the created constraint */
192 namebuf.str().c_str(), /* name of constraint */
193 0, /* number of nonzeros in the constraint */
194 nullptr, /* array with variables of constraint entries */
195 nullptr, /* array with coefficients of constraint entries */
196 1.0, /* left hand side of constraint */
197 1.0) ); /* right hand side of constraint */
198
199 /* since we are using 0 based indexing we should be careful with the loop indices. */
200 for( size_t j = 3 * (p + 1) - 3; j < 3 * (p + 1); ++j )
201 {
202 for( size_t i = 3 * (q + 1) - 3; i < 3 * (q + 1); ++i )
203 {
204 SCIP_CALL( SCIPaddCoefLinear(scip, cons, xvars[i][j][k], 1.0) );
205 }
206 }
207 SCIP_CALL( SCIPaddCons(scip, cons) );
208 subgridconstraints.push_back(cons);
209 }
210 }
211 }
212
213
214 /* so far we have required that for each column, row, and subgrid only one occurence of {1, ..., 9} appears.
215 * However, we have not yet imposed that they must appear in different positions. So, for example, a solution
216 * that says that the numbers 3 and 4 appear in entry (1,1) could be feasible.
217 * However, that can only happen if some entry (i,j) has no number assigned to it.
218 * Thus, by requiring that each entry (i,j) has a number assigned to it we obtain a correct model.
219 * This requirement with the following linear constraint:
220 * x_ij1 + x_ij2k + x_ij3 + ... + x_ij9 = 1 for each i and j
221 *
222 */
223 std::vector< SCIP_CONS* > fillgridconstraints;
224 for( size_t i = 0; i < 9; ++i )
225 {
226 for( size_t j = 0; j < 9; ++j )
227 {
228 SCIP_CONS* cons = nullptr;
229
230 namebuf.str("");
231 namebuf << "fillgrid_" << i << "_" << j << "]";
232
233 /* we first create an empty an equality constraint (i.e. the lhs and rhs are equal to 1) and then add the
234 * variables
235 */
237 scip,
238 &cons, /* pointer to hold the created constraint */
239 namebuf.str().c_str(), /* name of constraint */
240 0, /* number of nonzeros in the constraint */
241 nullptr, /* array with variables of constraint entries */
242 nullptr, /* array with coefficients of constraint entries */
243 1.0, /* left hand side of constraint */
244 1.0) ); /* right hand side of constraint */
245
246 for( size_t k = 0; k < 9; ++k )
247 {
248 SCIP_CALL( SCIPaddCoefLinear(scip, cons, xvars[i][j][k], 1.0) );
249 }
250
251 SCIP_CALL( SCIPaddCons(scip, cons) );
252 fillgridconstraints.push_back(cons);
253 }
254 }
255
256
257 /* we use SCIPfixVar to fix the binary variables corresponding to the given value in the puzzle to 1.
258 * see https://www.scipopt.org/doc-7.0.1/html/group__PublicVariableMethods.php#ga7965b16efcb2f8cdf7e289198c5cbe16
259 * for more info
260 */
261 for( size_t i = 0; i < 9; ++i )
262 {
263 for( size_t j = 0; j < 9; ++j )
264 {
265 /* The unsolved puzzle where there are blanks are represented by -1 in the puzzle datastructure */
266 if( puzzle[i][j] > 0 )
267 {
268 SCIP_Bool infeasible;
269 SCIP_Bool fixed;
270
271 SCIP_CALL( SCIPfixVar(scip, xvars[i][j][puzzle[i][j] - 1], 1.0, &infeasible, &fixed) );
272
273 assert(fixed == TRUE);
274 /* we are assuming that the puzzle is not instantly infeasible */
275 assert(infeasible == FALSE);
276 }
277 }
278 }
279
280
281 /* in c++, we can set the SCIP parameters by <tt> sSCIPsetIntParam(SCIP* scip, "parameter", param_value) ) </tt> */
282 SCIP_CALL( SCIPsetIntParam(scip, "display/verblevel", 0) ); /* turns off the SCIP output */
284
285 /* Some wrongly generated sudoku puzzles can be infeasible. So we use the solnstatus to display different types of
286 * output.
287 */
288 SCIP_STATUS solutionstatus = SCIPgetStatus( scip );
289
290 if( solutionstatus == SCIP_STATUS_OPTIMAL )
291 {
292 /* SCIP_STATUS_OPTIMAL status indicates that an optimal solution was found, hence we can print the final puzzle */
293 SCIP_SOL* sol;
294 sol = SCIPgetBestSol( scip );
295
296 for( size_t i = 0; i < 9; ++i )
297 {
298 for( size_t j = 0; j < 9; ++j )
299 {
300 for( size_t k = 0; k < 9; ++k )
301 {
302 if( SCIPgetSolVal(scip, sol, xvars[i][j][k]) > 0 )
303 {
304 /* As we are using 0 based indices, to display the final puzzle, we should increment values by 1. */
305 puzzle[i][j] = k + 1;
306 }
307 }
308 }
309 }
310 std::cout << "The solved puzzle is: "
311 << "\n";
312 sudoku::printSudoku( puzzle );
313 }
314 else if( solutionstatus == SCIP_STATUS_INFEASIBLE )
315 {
316 /* solutions status of SCIP_STATUS_INFEASIBLE indicates that the puzzle is infeasible. */
317 std::cout << "Check the Input puzzle"
318 << "\n";
319 }
320 else
321 {
322 std::cerr << "Something went wrong during the optimization." << "\n";
323 exit(1);
324 }
325
326 /*freeing the variables */
327 for( size_t i = 0; i < 9; ++i )
328 {
329 for( size_t j = 0; j < 9; ++j )
330 {
331 for( size_t k = 0; k < 9; ++k )
332 {
333 SCIP_CALL( SCIPreleaseVar(scip, &xvars[i][j][k]) );
334 }
335 }
336 }
337 xvars.clear();
338
339 /* freeing the constraints
340 * we use C++11's auto keyword to iterate over the constraints and free them.
341 */
342 for( auto &constr : columnconstraints )
343 {
344 SCIP_CALL( SCIPreleaseCons(scip, &constr) );
345 }
346 columnconstraints.clear();
347
348 for( auto &constr : rowconstraints )
349 {
350 SCIP_CALL( SCIPreleaseCons(scip, &constr) );
351 }
352 rowconstraints.clear();
353
354 for( auto &constr : subgridconstraints )
355 {
356 SCIP_CALL( SCIPreleaseCons(scip, &constr) );
357 }
358 subgridconstraints.clear();
359
360 for( auto &constr : fillgridconstraints )
361 {
362 SCIP_CALL( SCIPreleaseCons(scip, &constr) );
363 }
364 fillgridconstraints.clear();
365
366 /*freeing scip */
367
369}
#define SCIP_Bool
Definition: def.h:91
#define TRUE
Definition: def.h:93
#define FALSE
Definition: def.h:94
#define SCIP_CALL(x)
Definition: def.h:355
SCIP_RETCODE SCIPaddCoefLinear(SCIP *scip, SCIP_CONS *cons, SCIP_VAR *var, SCIP_Real val)
SCIP_RETCODE SCIPcreateConsBasicLinear(SCIP *scip, SCIP_CONS **cons, const char *name, int nvars, SCIP_VAR **vars, SCIP_Real *vals, SCIP_Real lhs, SCIP_Real rhs)
SCIP_RETCODE SCIPfree(SCIP **scip)
Definition: scip_general.c:402
SCIP_RETCODE SCIPcreate(SCIP **scip)
Definition: scip_general.c:370
SCIP_STATUS SCIPgetStatus(SCIP *scip)
Definition: scip_general.c:562
SCIP_RETCODE SCIPaddVar(SCIP *scip, SCIP_VAR *var)
Definition: scip_prob.c:1907
SCIP_RETCODE SCIPaddCons(SCIP *scip, SCIP_CONS *cons)
Definition: scip_prob.c:3274
SCIP_RETCODE SCIPsetObjsense(SCIP *scip, SCIP_OBJSENSE objsense)
Definition: scip_prob.c:1417
SCIP_RETCODE SCIPcreateProbBasic(SCIP *scip, const char *name)
Definition: scip_prob.c:182
SCIP_RETCODE SCIPsetIntParam(SCIP *scip, const char *name, int value)
Definition: scip_param.c:487
SCIP_RETCODE SCIPreleaseCons(SCIP *scip, SCIP_CONS **cons)
Definition: scip_cons.c:1173
SCIP_SOL * SCIPgetBestSol(SCIP *scip)
Definition: scip_sol.c:2981
SCIP_Real SCIPgetSolVal(SCIP *scip, SCIP_SOL *sol, SCIP_VAR *var)
Definition: scip_sol.c:1765
SCIP_RETCODE SCIPsolve(SCIP *scip)
Definition: scip_solve.c:2635
SCIP_RETCODE SCIPreleaseVar(SCIP *scip, SCIP_VAR **var)
Definition: scip_var.c:1887
SCIP_RETCODE SCIPfixVar(SCIP *scip, SCIP_VAR *var, SCIP_Real fixedval, SCIP_Bool *infeasible, SCIP_Bool *fixed)
Definition: scip_var.c:10318
SCIP_RETCODE SCIPcreateVarBasic(SCIP *scip, SCIP_VAR **var, const char *name, SCIP_Real lb, SCIP_Real ub, SCIP_Real obj, SCIP_VARTYPE vartype)
Definition: scip_var.c:184
std::vector< std::vector< int > > getSudokuPuzzle(const std::string &filepath)
Definition: sudoku_utils.h:43
void printSudoku(const std::vector< std::vector< int > > &sudokupuzzle)
Definition: sudoku_utils.h:88
SCIP callable library.
SCIP_RETCODE SCIPincludeDefaultPlugins(SCIP *scip)
default SCIP plugins
int main(int argc, char *argv[])
Definition: sudoku_main.cpp:39
A set of utilities that are used to read the puzzle and display the puzzle.
@ SCIP_OBJSENSE_MINIMIZE
Definition: type_prob.h:48
@ SCIP_STATUS_OPTIMAL
Definition: type_stat.h:43
@ SCIP_STATUS_INFEASIBLE
Definition: type_stat.h:44
enum SCIP_Status SCIP_STATUS
Definition: type_stat.h:64
@ SCIP_VARTYPE_BINARY
Definition: type_var.h:64