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