Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

Solving LinkedIn Mini Sudoku with Linear Optimization

Mini Sudoku Banner

Just like I did with the Queens, Tango, and Zip minigames, in this notebook I will show you how to model and solve the LinkedIn Mini Sudoku using LO.


What Sudoku is

Classic Sudoku table

Figure 1:Example of a classic Sudoku puzzle. Source Wikipedia

Mini Sudoku is based on the classic Sudoku game (from the Japanese 数独, meaning unique numbers in a free translation), which originally consists of a 9 ×\times 9 grid and 9 smaller 3 ×\times 3 matrices, with some squares already pre-filled with digits from 1 to 9. The meaning of its name is due to its objective of filling all the squares without repeating digits in the rows, columns, and smaller grids.


How to Play Mini Sudoku

Figure 2:Example of a Mini Sudoku game. Source: LinkedIn

In the case of Mini Sudoku, the game consists of a grid with smaller dimensions than the classic, 6 ×\times 6 grid, composed of six 2 ×\times 3 submatrices.

Objective
Fill all the empty spaces in the game grid with digits from 1 to 6
Rules
Each row, column, and mini-grid of the main game matrix must be filled with a digit from 1 to 6, without repetition in each row, column, or smaller grid.

Problem Modeling

As I did in my previous articles, the LO model for the Mini Sudoku game requires the definition of the following components:

  • Ranges

  • Sets

  • Objective function

  • Decision Variables

  • Constraints

First, let’s define these components considering the most general scenario for building the Abstract Model for the Sudoku game before defining them for the more specific case of Mini Sudoku.

Ranges

In order to consider the most general cases, five ranges will be considered: II and JJ to represent the dimensions of the main grid, UU and VV for the dimensions of the smaller grids, and an interval KK for the range of possible values a square can receive.

I={1,,n}I = \{1, \cdots, n\}
The row range, where nn is the total number of rows (in this case, n=6n = 6)
J={1,,n}J = \{1, \cdots, n\}
The column range, where the total number of columns is equal to the number of rows for dealing with a square matrix
K={1,,n}K = \{1, \cdots, n\}
Range of possible values, where nn is the total number of possible digits, which is expected to be equal to the nn dimensions of the grid
U={1,,p}U = \{1, \cdots, p\}
Number of rows in the game’s submatrices, where pp is the total number of rows in each submatrix
V={1,,q}V = \{1, \cdots, q\}
Number of columns in the game’s submatrices, where qq is the total number of columns in each submatrix, which is expected to be pq=npq = n, that is, the number of squares in each submatrix should be equal to the number of possible digits.

Sets

To facilitate the definition of the constraints, it is important to define at least the set SS of submatrices and FF of pre-filled squares. Furthermore, it is necessary to clearly define which squares of the board comprise each of the submatrices SvuS_{vu}. For example, the first submatrix S11S_{11} is composed of squares in rows 1 and 2 and columns whose index ranges go from 1 to 3. S12S_{12} is also composed of squares in rows 1 and 2, but the column indices range from 4 to 6, which is the second half of the column range; and so on for the remaining SvuS_{vu}.

S={SvuvV,uU}S = \{S_{vu} \mid \forall v \in V, \forall u \in U\}
Set of submatrices SvuS_{vu} existing in the game
Svu={(i,j)i{p(v1)+1,,pv},j{q(u1)+1,,qu}}S_{vu} = \{(i, j) \mid \forall i \in \{p(v-1)+1, \cdots, pv\}, \forall j \in \{q(u-1)+1, \cdots, qu\}\}
Set of squares (i,j)I×J(i, j) \subseteq I \times J that belong to the submatrix SvuS_{vu}
F={(i,j,k)iI,jJ,kK}I×J×KF = \{(i, j, k) \mid i \in I, j \in J, k \in K\} \subseteq I \times J \times K
Subset of pre-filled squares.

Decision Variables

The decision variables will be binary xijkx_{ijk}, representing the decision of whether square (i,j)(i, j) is filled with the value kk. Therefore, as with the models of the other LinkedIn minigames, the Mini Sudoku is a BLOP.

xijk{0,1},(i,j,k)I×J×Kx_{ijk} \in \{0,1\}, \forall (i,j,k) \in I \times J \times K
xijk=1x_{ijk} = 1 if square (i,j)(i,j) is filled with the digit kk
xijk=0x_{ijk} = 0 otherwise.

Objective Function

Once again, as in previous articles, the Sudoku optimization problem does not have a function to be optimized, since we only want to find a solution that satisfies all the rules of the game. Therefore, the BLPP is a feasibility problem, whose objective function consists of maximizing (or minimizing) an arbitrary constant.

Max C\text{Max} \ C

Constraints

Finally, with all the previously defined components, let’s translate the Sudoku rules into mathematical formulations for the BLOP model.

Binary Constraints
First of all, it’s important to clearly state the binary nature of the decision variables in the constraint set.
xijk{0,1},(i,j,k)I×J×Kx_{ijk} \in \{0, 1\}, \forall (i, j, k) \in I \times J \times K
Unique-Digits-Per-Row Constraints
Since there can be no repetition of values for each row, the sum of the xijkx_{ijk} for each row ii must be equal to 1, and there must be a such constraint for column jj and for each digit kk, which in the case of Mini Sudoku will result in 36 constraints.
jJxijk=1,iI,kK\sum_{j \in J}{x_{ijk}}=1, \forall i \in I, \forall k \in K
Unique-Digits-Per-Column Constraints
The same logic applies to each column jj of the game, with one constraint for each row ii and possible digit kk, resulting in 36 more constraints.
iIxijk=1,jJ,kK\sum_{i \in I}{x_{ijk}}=1, \forall j \in J, \forall k \in K
Unique-Digits-Per-Submatrix Constraints
With the set SS of submatrices already well defined, it becomes easier to define the set of constraints that prevent repetition of digits for each submatrix SvuS_{vu}.
(i,j)Svuxijk=1,SvuS,kK\sum_{(i,j) \in S_{vu}}{x_{ijk}}=1, \forall S_{vu} \in S, \forall k \in K
Single-Digit-Per-Square Constraints
In addition, it is necessary to impose a set of constraints to prevent a square from being filled with more than one digit, which is achieved if the sum of xijkx_{ijk} is equal to 1 for each square (i,j)(i, j) existing in the game; therefore, 36 more constraints in the case of Mini Sudoku.
kKxijk=1,iI,jJ\sum_{k \in K}{x_{ijk}}=1, \forall i \in I, \forall j \in J
Already-Filled-Squares Constraints
Finally, for each already filled square, we must remember to impose that xijk=1x_{ijk} = 1 if the square (i,j)(i, j) is already filled with the digit kk.
xijk=1,(i,j,k)Fx_{ijk}=1, \forall (i,j,k) \in F

Abstract Model

With all the components set, we now have assembled the abstract model for a Sudoku game. It’s important to remember that this model assumes the game’s submatrices will be rectangular with dimensions p×qp \times q, such that pq=npq = n.

Max CS.t.:iIxijk=1,jJ,kKjJxijk=1,iI,kKkKxijk=1,iI,jJ(i,j)Svuxijk=1,SvuS,kKxijk=1,(i,j,k)Fxijk{0,1},(i,j,k)I×J×K\begin{array}{lll} & \text{Max } C & \\ \text{S.t.:} & & \\ & \sum_{i \in I}{x_{ijk}}=1, & \forall j \in J, \forall k \in K \\ & \sum_{j \in J}{x_{ijk}}=1, & \forall i \in I, \forall k \in K \\ & \sum_{k \in K}{x_{ijk}}=1, & \forall i \in I, \forall j \in J \\ & \sum_{(i,j) \in S_{vu}}{x_{ijk}}=1, & \forall S_{vu} \in S, \forall k \in K \\ & x_{ijk}=1, & \forall (i, j,k) \in F \\ & x_{ijk} \in \{0,1\}, & \forall (i,j,k) \in I \times J \times K \\ \end{array}

Concrete Model

The example to be solved in this notebook will be Mini Sudoku No. 60, published on LinkedIn on October 10th, 2025

Mini Sudoku No. 60

Figure 3:Mini Sudoku No. 60, October 10th, 2025 (Source: LinkedIn)

Based on the abstract model, it is possible to instantiate a concrete model for this game, as shown below.

Max 0\text{Max} \ 0

S.t.:

Unique-Digits-Per-Row Constraints
x111+x121+x131+x141+x151+x161=1x_{111} + x_{121} + x_{131} + x_{141} + x_{151} + x_{161} = 1 (Digit 1 on Row 1)
x211+x221+x231+x241+x251+x261=1x_{211} + x_{221} + x_{231} + x_{241} + x_{251} + x_{261} = 1 (Digit 1 on Row 2)
x311+x321+x331+x341+x351+x361=1x_{311} + x_{321} + x_{331} + x_{341} + x_{351} + x_{361} = 1 (Digit 1 on Row 3)
x411+x421+x431+x441+x451+x461=1x_{411} + x_{421} + x_{431} + x_{441} + x_{451} + x_{461} = 1 (Digit 1 on Row 4)
x511+x521+x531+x541+x551+x561=1x_{511} + x_{521} + x_{531} + x_{541} + x_{551} + x_{561} = 1 (Digit 1 on Row 5)
x611+x621+x631+x641+x651+x661=1x_{611} + x_{621} + x_{631} + x_{641} + x_{651} + x_{661} = 1 (Digit 1 on Row 6)
x112+x122+x132+x142+x152+x162=1x_{112} + x_{122} + x_{132} + x_{142} + x_{152} + x_{162} = 1 (Digit 2 on Row 1)
x212+x222+x232+x242+x252+x262=1x_{212} + x_{222} + x_{232} + x_{242} + x_{252} + x_{262} = 1 (Digit 2 on Row 2)
x312+x322+x332+x342+x352+x362=1x_{312} + x_{322} + x_{332} + x_{342} + x_{352} + x_{362} = 1 (Digit 2 on Row 3)
x412+x422+x432+x442+x452+x462=1x_{412} + x_{422} + x_{432} + x_{442} + x_{452} + x_{462} = 1 (Digit 2 on Row 4)
x512+x522+x532+x542+x552+x562=1x_{512} + x_{522} + x_{532} + x_{542} + x_{552} + x_{562} = 1 (Digit 2 on Row 5)
x612+x622+x632+x642+x652+x662=1x_{612} + x_{622} + x_{632} + x_{642} + x_{652} + x_{662} = 1 (Digit 2 on Row 6)
x113+x123+x133+x143+x153+x163=1x_{113} + x_{123} + x_{133} + x_{143} + x_{153} + x_{163} = 1 (Digit 3 on Row 1)
x213+x223+x233+x243+x253+x263=1x_{213} + x_{223} + x_{233} + x_{243} + x_{253} + x_{263} = 1 (Digit 3 on Row 2)
x313+x323+x333+x343+x353+x363=1x_{313} + x_{323} + x_{333} + x_{343} + x_{353} + x_{363} = 1 (Digit 3 on Row 3)
x413+x423+x433+x443+x453+x463=1x_{413} + x_{423} + x_{433} + x_{443} + x_{453} + x_{463} = 1 (Digit 3 on Row 4)
x513+x523+x533+x543+x553+x563=1x_{513} + x_{523} + x_{533} + x_{543} + x_{553} + x_{563} = 1 (Digit 3 on Row 5)
x613+x623+x633+x643+x653+x663=1x_{613} + x_{623} + x_{633} + x_{643} + x_{653} + x_{663} = 1 (Digit 3 on Row 6)
x114+x124+x134+x144+x154+x164=1x_{114} + x_{124} + x_{134} + x_{144} + x_{154} + x_{164} = 1 (Digit 4 on Row 1)
x214+x224+x234+x244+x254+x264=1x_{214} + x_{224} + x_{234} + x_{244} + x_{254} + x_{264} = 1 (Digit 4 on Row 2)
x314+x324+x334+x344+x354+x364=1x_{314} + x_{324} + x_{334} + x_{344} + x_{354} + x_{364} = 1 (Digit 4 on Row 3)
x414+x424+x434+x444+x454+x464=1x_{414} + x_{424} + x_{434} + x_{444} + x_{454} + x_{464} = 1 (Digit 4 on Row 4)
x514+x524+x534+x544+x554+x564=1x_{514} + x_{524} + x_{534} + x_{544} + x_{554} + x_{564} = 1 (Digit 4 on Row 5)
x614+x624+x634+x644+x654+x664=1x_{614} + x_{624} + x_{634} + x_{644} + x_{654} + x_{664} = 1 (Digit 4 on Row 6)
x115+x125+x135+x145+x155+x165=1x_{115} + x_{125} + x_{135} + x_{145} + x_{155} + x_{165} = 1 (Digit 5 on Row 1)
x215+x225+x235+x245+x255+x265=1x_{215} + x_{225} + x_{235} + x_{245} + x_{255} + x_{265} = 1 (Digit 5 on Row 2)
x315+x325+x335+x345+x355+x365=1x_{315} + x_{325} + x_{335} + x_{345} + x_{355} + x_{365} = 1 (Digit 5 on Row 3)
x415+x425+x435+x445+x455+x465=1x_{415} + x_{425} + x_{435} + x_{445} + x_{455} + x_{465} = 1 (Digit 5 on Row 4)
x515+x525+x535+x545+x555+x565=1x_{515} + x_{525} + x_{535} + x_{545} + x_{555} + x_{565} = 1 (Digit 5 on Row 5)
x615+x625+x635+x645+x655+x665=1x_{615} + x_{625} + x_{635} + x_{645} + x_{655} + x_{665} = 1 (Digit 5 on Row 6)
x116+x126+x136+x146+x156+x166=1x_{116} + x_{126} + x_{136} + x_{146} + x_{156} + x_{166} = 1 (Digit 6 on Row 1)
x216+x226+x236+x246+x256+x266=1x_{216} + x_{226} + x_{236} + x_{246} + x_{256} + x_{266} = 1 (Digit 6 on Row 2)
x316+x326+x336+x346+x356+x366=1x_{316} + x_{326} + x_{336} + x_{346} + x_{356} + x_{366} = 1 (Digit 6 on Row 3)
x416+x426+x436+x446+x456+x466=1x_{416} + x_{426} + x_{436} + x_{446} + x_{456} + x_{466} = 1 (Digit 6 on Row 4)
x516+x526+x536+x546+x556+x566=1x_{516} + x_{526} + x_{536} + x_{546} + x_{556} + x_{566} = 1 (Digit 6 on Row 5)
x616+x626+x636+x646+x656+x666=1x_{616} + x_{626} + x_{636} + x_{646} + x_{656} + x_{666} = 1 (Digit 6 on Row 6)
Unique-Digits-Per-Column Constraints
x111+x211+x311+x411+x511+x611=1x_{111} + x_{211} + x_{311} + x_{411} + x_{511} + x_{611} = 1 (Digit 1 on Column 1)
x121+x221+x321+x421+x521+x621=1x_{121} + x_{221} + x_{321} + x_{421} + x_{521} + x_{621} = 1 (Digit 1 on Column 2)
x131+x231+x331+x431+x531+x631=1x_{131} + x_{231} + x_{331} + x_{431} + x_{531} + x_{631} = 1 (Digit 1 on Column 3)
x141+x241+x341+x441+x541+x641=1x_{141} + x_{241} + x_{341} + x_{441} + x_{541} + x_{641} = 1 (Digit 1 on Column 4)
x151+x251+x351+x451+x551+x651=1x_{151} + x_{251} + x_{351} + x_{451} + x_{551} + x_{651} = 1 (Digit 1 on Column 5)
x161+x261+x361+x461+x561+x661=1x_{161} + x_{261} + x_{361} + x_{461} + x_{561} + x_{661} = 1 (Digit 1 on Column 6)
x112+x212+x312+x412+x512+x612=1x_{112} + x_{212} + x_{312} + x_{412} + x_{512} + x_{612} = 1 (Digit 2 on Column 1)
x122+x222+x322+x422+x522+x622=1x_{122} + x_{222} + x_{322} + x_{422} + x_{522} + x_{622} = 1 (Digit 2 on Column 2)
x132+x232+x332+x432+x532+x632=1x_{132} + x_{232} + x_{332} + x_{432} + x_{532} + x_{632} = 1 (Digit 2 on Column 3)
x142+x242+x342+x442+x542+x642=1x_{142} + x_{242} + x_{342} + x_{442} + x_{542} + x_{642} = 1 (Digit 2 on Column 4)
x152+x252+x352+x452+x552+x652=1x_{152} + x_{252} + x_{352} + x_{452} + x_{552} + x_{652} = 1 (Digit 2 on Column 5)
x162+x262+x362+x462+x562+x662=1x_{162} + x_{262} + x_{362} + x_{462} + x_{562} + x_{662} = 1 (Digit 2 on Column 6)
x113+x213+x313+x413+x513+x613=1x_{113} + x_{213} + x_{313} + x_{413} + x_{513} + x_{613} = 1 (Digit 3 on Column 1)
x123+x223+x323+x423+x523+x623=1x_{123} + x_{223} + x_{323} + x_{423} + x_{523} + x_{623} = 1 (Digit 3 on Column 2)
x133+x233+x333+x433+x533+x633=1x_{133} + x_{233} + x_{333} + x_{433} + x_{533} + x_{633} = 1 (Digit 3 on Column 3)
x143+x243+x343+x443+x543+x643=1x_{143} + x_{243} + x_{343} + x_{443} + x_{543} + x_{643} = 1 (Digit 3 on Column 4)
x153+x253+x353+x453+x553+x653=1x_{153} + x_{253} + x_{353} + x_{453} + x_{553} + x_{653} = 1 (Digit 3 on Column 5)
x163+x263+x363+x463+x563+x663=1x_{163} + x_{263} + x_{363} + x_{463} + x_{563} + x_{663} = 1 (Digit 3 on Column 6)
x114+x214+x314+x414+x514+x614=1x_{114} + x_{214} + x_{314} + x_{414} + x_{514} + x_{614} = 1 (Digit 4 on Column 1)
x124+x224+x324+x424+x524+x624=1x_{124} + x_{224} + x_{324} + x_{424} + x_{524} + x_{624} = 1 (Digit 4 on Column 2)
x134+x234+x334+x434+x534+x634=1x_{134} + x_{234} + x_{334} + x_{434} + x_{534} + x_{634} = 1 (Digit 4 on Column 3)
x144+x244+x344+x444+x544+x644=1x_{144} + x_{244} + x_{344} + x_{444} + x_{544} + x_{644} = 1 (Digit 4 on Column 4)
x154+x254+x354+x454+x554+x654=1x_{154} + x_{254} + x_{354} + x_{454} + x_{554} + x_{654} = 1 (Digit 4 on Column 5)
x164+x264+x364+x464+x564+x664=1x_{164} + x_{264} + x_{364} + x_{464} + x_{564} + x_{664} = 1 (Digit 4 on Column 6)
x115+x215+x315+x415+x515+x615=1x_{115} + x_{215} + x_{315} + x_{415} + x_{515} + x_{615} = 1 (Digit 5 on Column 1)
x125+x225+x325+x425+x525+x625=1x_{125} + x_{225} + x_{325} + x_{425} + x_{525} + x_{625} = 1 (Digit 5 on Column 2)
x135+x235+x335+x435+x535+x635=1x_{135} + x_{235} + x_{335} + x_{435} + x_{535} + x_{635} = 1 (Digit 5 on Column 3)
x145+x245+x345+x445+x545+x645=1x_{145} + x_{245} + x_{345} + x_{445} + x_{545} + x_{645} = 1 (Digit 5 on Column 4)
x155+x255+x355+x455+x555+x655=1x_{155} + x_{255} + x_{355} + x_{455} + x_{555} + x_{655} = 1 (Digit 5 on Column 5)
x165+x265+x365+x465+x565+x665=1x_{165} + x_{265} + x_{365} + x_{465} + x_{565} + x_{665} = 1 (Digit 5 on Column 6)
x116+x216+x316+x416+x516+x616=1x_{116} + x_{216} + x_{316} + x_{416} + x_{516} + x_{616} = 1 (Digit 6 on Column 1)
x126+x226+x326+x426+x526+x626=1x_{126} + x_{226} + x_{326} + x_{426} + x_{526} + x_{626} = 1 (Digit 6 on Column 2)
x136+x236+x336+x436+x536+x636=1x_{136} + x_{236} + x_{336} + x_{436} + x_{536} + x_{636} = 1 (Digit 6 on Column 3)
x146+x246+x346+x446+x546+x646=1x_{146} + x_{246} + x_{346} + x_{446} + x_{546} + x_{646} = 1 (Digit 6 on Column 4)
x156+x256+x356+x456+x556+x656=1x_{156} + x_{256} + x_{356} + x_{456} + x_{556} + x_{656} = 1 (Digit 6 on Column 5)
x166+x266+x366+x466+x566+x666=1x_{166} + x_{266} + x_{366} + x_{466} + x_{566} + x_{666} = 1 (Digit 6 on Column 6)
Unique-Digits-Per-Submatrix Constraints
x111+x121+x131+x211+x221+x231=1x_{111} + x_{121} + x_{131} + x_{211} + x_{221} + x_{231} = 1 (Digit 1 on Submatrix S11S_{11})
x112+x122+x132+x212+x222+x232=1x_{112} + x_{122} + x_{132} + x_{212} + x_{222} + x_{232} = 1 (Digit 2 on Submatrix S11S_{11})
x113+x123+x133+x213+x223+x233=1x_{113} + x_{123} + x_{133} + x_{213} + x_{223} + x_{233} = 1 (Digit 3 on Submatrix S11S_{11})
x114+x124+x134+x214+x224+x234=1x_{114} + x_{124} + x_{134} + x_{214} + x_{224} + x_{234} = 1 (Digit 4 on Submatrix S11S_{11})
x115+x125+x135+x215+x225+x235=1x_{115} + x_{125} + x_{135} + x_{215} + x_{225} + x_{235} = 1 (Digit 5 on Submatrix S11S_{11})
x116+x126+x136+x216+x226+x236=1x_{116} + x_{126} + x_{136} + x_{216} + x_{226} + x_{236} = 1 (Digit 6 on Submatrix S11S_{11})
x141+x151+x161+x241+x251+x261=1x_{141} + x_{151} + x_{161} + x_{241} + x_{251} + x_{261} = 1 (Digit 1 on Submatrix S12S_{12})
x142+x152+x162+x242+x252+x262=1x_{142} + x_{152} + x_{162} + x_{242} + x_{252} + x_{262} = 1 (Digit 2 on Submatrix S12S_{12})
x143+x153+x163+x243+x253+x263=1x_{143} + x_{153} + x_{163} + x_{243} + x_{253} + x_{263} = 1 (Digit 3 on Submatrix S12S_{12})
x144+x154+x164+x244+x254+x264=1x_{144} + x_{154} + x_{164} + x_{244} + x_{254} + x_{264} = 1 (Digit 4 on Submatrix S12S_{12})
x145+x155+x165+x245+x255+x265=1x_{145} + x_{155} + x_{165} + x_{245} + x_{255} + x_{265} = 1 (Digit 5 on Submatrix S12S_{12})
x146+x156+x166+x246+x256+x266=1x_{146} + x_{156} + x_{166} + x_{246} + x_{256} + x_{266} = 1 (Digit 6 on Submatrix S12S_{12})
x311+x321+x331+x411+x421+x431=1x_{311} + x_{321} + x_{331} + x_{411} + x_{421} + x_{431} = 1 (Digit 1 on Submatrix S21S_{21})
x312+x322+x332+x412+x422+x432=1x_{312} + x_{322} + x_{332} + x_{412} + x_{422} + x_{432} = 1 (Digit 2 on Submatrix S21S_{21})
x313+x323+x333+x413+x423+x433=1x_{313} + x_{323} + x_{333} + x_{413} + x_{423} + x_{433} = 1 (Digit 3 on Submatrix S21S_{21})
x314+x324+x334+x414+x424+x434=1x_{314} + x_{324} + x_{334} + x_{414} + x_{424} + x_{434} = 1 (Digit 4 on Submatrix S21S_{21})
x315+x325+x335+x415+x425+x435=1x_{315} + x_{325} + x_{335} + x_{415} + x_{425} + x_{435} = 1 (Digit 5 on Submatrix S21S_{21})
x316+x326+x336+x416+x426+x436=1x_{316} + x_{326} + x_{336} + x_{416} + x_{426} + x_{436} = 1 (Digit 6 on Submatrix S21S_{21})
x341+x351+x331+x441+x451+x431=1x_{341} + x_{351} + x_{331} + x_{441} + x_{451} + x_{431} = 1 (Digit 1 on Submatrix S22S_{22})
x342+x352+x362+x442+x452+x462=1x_{342} + x_{352} + x_{362} + x_{442} + x_{452} + x_{462} = 1 (Digit 2 on Submatrix S22S_{22})
x343+x353+x363+x443+x453+x463=1x_{343} + x_{353} + x_{363} + x_{443} + x_{453} + x_{463} = 1 (Digit 3 on Submatrix S22S_{22})
x344+x354+x364+x444+x454+x464=1x_{344} + x_{354} + x_{364} + x_{444} + x_{454} + x_{464} = 1 (Digit 4 on Submatrix S22S_{22})
x345+x355+x365+x445+x455+x465=1x_{345} + x_{355} + x_{365} + x_{445} + x_{455} + x_{465} = 1 (Digit 5 on Submatrix S22S_{22})
x346+x356+x366+x446+x456+x466=1x_{346} + x_{356} + x_{366} + x_{446} + x_{456} + x_{466} = 1 (Digit 6 on Submatrix S22S_{22})
x511+x521+x531+x611+x621+x631=1x_{511} + x_{521} + x_{531} + x_{611} + x_{621} + x_{631} = 1 (Digit 1 on Submatrix S31S_{31})
x512+x522+x532+x612+x622+x632=1x_{512} + x_{522} + x_{532} + x_{612} + x_{622} + x_{632} = 1 (Digit 2 on Submatrix S31S_{31})
x513+x523+x533+x613+x623+x633=1x_{513} + x_{523} + x_{533} + x_{613} + x_{623} + x_{633} = 1 (Digit 3 on Submatrix S31S_{31})
x514+x524+x534+x614+x624+x634=1x_{514} + x_{524} + x_{534} + x_{614} + x_{624} + x_{634} = 1 (Digit 4 on Submatrix S31S_{31})
x515+x525+x535+x615+x625+x635=1x_{515} + x_{525} + x_{535} + x_{615} + x_{625} + x_{635} = 1 (Digit 5 on Submatrix S31S_{31})
x516+x526+x536+x616+x626+x636=1x_{516} + x_{526} + x_{536} + x_{616} + x_{626} + x_{636} = 1 (Digit 6 on Submatrix S31S_{31})
x541+x551+x561+x641+x651+x631=1x_{541} + x_{551} + x_{561} + x_{641} + x_{651} + x_{631} = 1 (Digit 1 on Submatrix S32S_{32})
x542+x552+x562+x642+x652+x662=1x_{542} + x_{552} + x_{562} + x_{642} + x_{652} + x_{662} = 1 (Digit 2 on Submatrix S32S_{32})
x543+x553+x563+x643+x653+x663=1x_{543} + x_{553} + x_{563} + x_{643} + x_{653} + x_{663} = 1 (Digit 3 on Submatrix S32S_{32})
x544+x554+x564+x644+x654+x664=1x_{544} + x_{554} + x_{564} + x_{644} + x_{654} + x_{664} = 1 (Digit 4 on Submatrix S32S_{32})
x545+x555+x565+x645+x655+x665=1x_{545} + x_{555} + x_{565} + x_{645} + x_{655} + x_{665} = 1 (Digit 5 on Submatrix S32S_{32})
x546+x556+x566+x646+x656+x666=1x_{546} + x_{556} + x_{566} + x_{646} + x_{656} + x_{666} = 1 (Digit 6 on Submatrix S32S_{32})
Single-Digit-Per-Square Constraints
x111+x112+x113+x114+x115+x116=1x_{111} + x_{112} + x_{113} + x_{114} + x_{115} + x_{116} = 1 (Square (1, 1))
x121+x122+x123+x124+x125+x126=1x_{121} + x_{122} + x_{123} + x_{124} + x_{125} + x_{126} = 1 (Square (1, 2))
x131+x132+x133+x134+x135+x136=1x_{131} + x_{132} + x_{133} + x_{134} + x_{135} + x_{136} = 1 (Square (1, 3))
x141+x142+x143+x144+x145+x146=1x_{141} + x_{142} + x_{143} + x_{144} + x_{145} + x_{146} = 1 (Square (1, 4))
x151+x152+x153+x154+x155+x156=1x_{151} + x_{152} + x_{153} + x_{154} + x_{155} + x_{156} = 1 (Square (1, 5))
x161+x162+x163+x164+x165+x166=1x_{161} + x_{162} + x_{163} + x_{164} + x_{165} + x_{166} = 1 (Square (1, 6))
x211+x212+x213+x214+x215+x216=1x_{211} + x_{212} + x_{213} + x_{214} + x_{215} + x_{216} = 1 (Square (2, 1))
x221+x222+x223+x224+x225+x226=1x_{221} + x_{222} + x_{223} + x_{224} + x_{225} + x_{226} = 1 (Square (2, 2))
x231+x232+x233+x234+x235+x236=1x_{231} + x_{232} + x_{233} + x_{234} + x_{235} + x_{236} = 1 (Square (2, 3))
x241+x242+x243+x244+x245+x246=1x_{241} + x_{242} + x_{243} + x_{244} + x_{245} + x_{246} = 1 (Square (2, 4))
x251+x252+x253+x254+x255+x256=1x_{251} + x_{252} + x_{253} + x_{254} + x_{255} + x_{256} = 1 (Square (2, 5))
x261+x262+x263+x264+x265+x266=1x_{261} + x_{262} + x_{263} + x_{264} + x_{265} + x_{266} = 1 (Square (2, 6))
x311+x312+x313+x314+x315+x316=1x_{311} + x_{312} + x_{313} + x_{314} + x_{315} + x_{316} = 1 (Square (3, 1))
x321+x322+x323+x324+x325+x326=1x_{321} + x_{322} + x_{323} + x_{324} + x_{325} + x_{326} = 1 (Square (3, 2))
x331+x332+x333+x334+x335+x336=1x_{331} + x_{332} + x_{333} + x_{334} + x_{335} + x_{336} = 1 (Square (3, 3))
x341+x342+x343+x344+x345+x346=1x_{341} + x_{342} + x_{343} + x_{344} + x_{345} + x_{346} = 1 (Square (3, 4))
x351+x352+x353+x354+x355+x356=1x_{351} + x_{352} + x_{353} + x_{354} + x_{355} + x_{356} = 1 (Square (3, 5))
x361+x362+x363+x364+x365+x366=1x_{361} + x_{362} + x_{363} + x_{364} + x_{365} + x_{366} = 1 (Square (3, 6))
x411+x412+x413+x414+x415+x416=1x_{411} + x_{412} + x_{413} + x_{414} + x_{415} + x_{416} = 1 (Square (4, 1))
x421+x422+x423+x424+x425+x426=1x_{421} + x_{422} + x_{423} + x_{424} + x_{425} + x_{426} = 1 (Square (4, 2))
x431+x432+x433+x434+x435+x436=1x_{431} + x_{432} + x_{433} + x_{434} + x_{435} + x_{436} = 1 (Square (4, 3))
x441+x442+x443+x444+x445+x446=1x_{441} + x_{442} + x_{443} + x_{444} + x_{445} + x_{446} = 1 (Square (4, 4))
x451+x452+x453+x454+x455+x456=1x_{451} + x_{452} + x_{453} + x_{454} + x_{455} + x_{456} = 1 (Square (4, 5))
x461+x462+x463+x464+x465+x466=1x_{461} + x_{462} + x_{463} + x_{464} + x_{465} + x_{466} = 1 (Square (4, 6))
x511+x512+x513+x514+x515+x516=1x_{511} + x_{512} + x_{513} + x_{514} + x_{515} + x_{516} = 1 (Square (5, 1))
x521+x522+x523+x524+x525+x526=1x_{521} + x_{522} + x_{523} + x_{524} + x_{525} + x_{526} = 1 (Square (5, 2))
x531+x532+x533+x534+x535+x536=1x_{531} + x_{532} + x_{533} + x_{534} + x_{535} + x_{536} = 1 (Square (5, 3))
x541+x542+x543+x544+x545+x546=1x_{541} + x_{542} + x_{543} + x_{544} + x_{545} + x_{546} = 1 (Square (5, 4))
x551+x552+x553+x554+x555+x556=1x_{551} + x_{552} + x_{553} + x_{554} + x_{555} + x_{556} = 1 (Square (5, 5))
x561+x562+x563+x564+x565+x566=1x_{561} + x_{562} + x_{563} + x_{564} + x_{565} + x_{566} = 1 (Square (5, 6))
x611+x612+x613+x614+x615+x616=1x_{611} + x_{612} + x_{613} + x_{614} + x_{615} + x_{616} = 1 (Square (6, 1))
x621+x622+x623+x624+x625+x626=1x_{621} + x_{622} + x_{623} + x_{624} + x_{625} + x_{626} = 1 (Square (6, 2))
x631+x632+x633+x634+x635+x636=1x_{631} + x_{632} + x_{633} + x_{634} + x_{635} + x_{636} = 1 (Square (6, 3))
x641+x642+x643+x644+x645+x646=1x_{641} + x_{642} + x_{643} + x_{644} + x_{645} + x_{646} = 1 (Square (6, 4))
x651+x652+x653+x654+x655+x656=1x_{651} + x_{652} + x_{653} + x_{654} + x_{655} + x_{656} = 1 (Square (6, 5))
x661+x662+x663+x664+x665+x666=1x_{661} + x_{662} + x_{663} + x_{664} + x_{665} + x_{666} = 1 (Square (6, 6))
Already-Filled-Squares Constraints
x111=1x_{111} = 1 (Square (1, 1) has Digit 1)
x222=1x_{222} = 1 (Square (2, 2) has Digit 2)
x253=1x_{253} = 1 (Square (2, 5) has Digit 3)
x346=1x_{346} = 1 (Square (3, 4) has Digit 6)
x435=1x_{435} = 1 (Square (4, 3) has Digit 5)
x444=1x_{444} = 1 (Square (4, 4) has Digit 4)
x524=1x_{524} = 1 (Square (5, 2) has Digit 4)
x555=1x_{555} = 1 (Square (5, 5) has Digit 5)
x666=1x_{666} = 1 (Square (6, 6) has Digit 6)
Binary Constraints
x111{0,1}x_{111} \in \{0,1\}
x112{0,1}x_{112} \in \{0,1\}
x113{0,1}x_{113} \in \{0,1\}
x114{0,1}x_{114} \in \{0,1\}
x115{0,1}x_{115} \in \{0,1\}
x116{0,1}x_{116} \in \{0,1\}
x121{0,1}x_{121} \in \{0,1\}
x122{0,1}x_{122} \in \{0,1\}
x123{0,1}x_{123} \in \{0,1\}
x124{0,1}x_{124} \in \{0,1\}
x125{0,1}x_{125} \in \{0,1\}
x126{0,1}x_{126} \in \{0,1\}
x131{0,1}x_{131} \in \{0,1\}
x132{0,1}x_{132} \in \{0,1\}
x133{0,1}x_{133} \in \{0,1\}
x134{0,1}x_{134} \in \{0,1\}
x135{0,1}x_{135} \in \{0,1\}
x136{0,1}x_{136} \in \{0,1\}
x141{0,1}x_{141} \in \{0,1\}
x142{0,1}x_{142} \in \{0,1\}
x143{0,1}x_{143} \in \{0,1\}
x144{0,1}x_{144} \in \{0,1\}
x145{0,1}x_{145} \in \{0,1\}
x146{0,1}x_{146} \in \{0,1\}
x151{0,1}x_{151} \in \{0,1\}
x152{0,1}x_{152} \in \{0,1\}
x153{0,1}x_{153} \in \{0,1\}
x154{0,1}x_{154} \in \{0,1\}
x155{0,1}x_{155} \in \{0,1\}
x156{0,1}x_{156} \in \{0,1\}
x161{0,1}x_{161} \in \{0,1\}
x162{0,1}x_{162} \in \{0,1\}
x163{0,1}x_{163} \in \{0,1\}
x164{0,1}x_{164} \in \{0,1\}
x165{0,1}x_{165} \in \{0,1\}
x166{0,1}x_{166} \in \{0,1\}
x211{0,1}x_{211} \in \{0,1\}
x212{0,1}x_{212} \in \{0,1\}
x213{0,1}x_{213} \in \{0,1\}
x214{0,1}x_{214} \in \{0,1\}
x215{0,1}x_{215} \in \{0,1\}
x216{0,1}x_{216} \in \{0,1\}
x221{0,1}x_{221} \in \{0,1\}
x222{0,1}x_{222} \in \{0,1\}
x223{0,1}x_{223} \in \{0,1\}
x224{0,1}x_{224} \in \{0,1\}
x225{0,1}x_{225} \in \{0,1\}
x226{0,1}x_{226} \in \{0,1\}
x231{0,1}x_{231} \in \{0,1\}
x232{0,1}x_{232} \in \{0,1\}
x233{0,1}x_{233} \in \{0,1\}
x234{0,1}x_{234} \in \{0,1\}
x235{0,1}x_{235} \in \{0,1\}
x236{0,1}x_{236} \in \{0,1\}
x241{0,1}x_{241} \in \{0,1\}
x242{0,1}x_{242} \in \{0,1\}
x243{0,1}x_{243} \in \{0,1\}
x244{0,1}x_{244} \in \{0,1\}
x245{0,1}x_{245} \in \{0,1\}
x246{0,1}x_{246} \in \{0,1\}
x251{0,1}x_{251} \in \{0,1\}
x252{0,1}x_{252} \in \{0,1\}
x253{0,1}x_{253} \in \{0,1\}
x254{0,1}x_{254} \in \{0,1\}
x255{0,1}x_{255} \in \{0,1\}
x256{0,1}x_{256} \in \{0,1\}
x261{0,1}x_{261} \in \{0,1\}
x262{0,1}x_{262} \in \{0,1\}
x263{0,1}x_{263} \in \{0,1\}
x264{0,1}x_{264} \in \{0,1\}
x265{0,1}x_{265} \in \{0,1\}
x266{0,1}x_{266} \in \{0,1\}
x311{0,1}x_{311} \in \{0,1\}
x312{0,1}x_{312} \in \{0,1\}
x313{0,1}x_{313} \in \{0,1\}
x314{0,1}x_{314} \in \{0,1\}
x315{0,1}x_{315} \in \{0,1\}
x316{0,1}x_{316} \in \{0,1\}
x321{0,1}x_{321} \in \{0,1\}
x322{0,1}x_{322} \in \{0,1\}
x323{0,1}x_{323} \in \{0,1\}
x324{0,1}x_{324} \in \{0,1\}
x325{0,1}x_{325} \in \{0,1\}
x326{0,1}x_{326} \in \{0,1\}
x331{0,1}x_{331} \in \{0,1\}
x332{0,1}x_{332} \in \{0,1\}
x333{0,1}x_{333} \in \{0,1\}
x334{0,1}x_{334} \in \{0,1\}
x335{0,1}x_{335} \in \{0,1\}
x336{0,1}x_{336} \in \{0,1\}
x341{0,1}x_{341} \in \{0,1\}
x342{0,1}x_{342} \in \{0,1\}
x343{0,1}x_{343} \in \{0,1\}
x344{0,1}x_{344} \in \{0,1\}
x345{0,1}x_{345} \in \{0,1\}
x346{0,1}x_{346} \in \{0,1\}
x351{0,1}x_{351} \in \{0,1\}
x352{0,1}x_{352} \in \{0,1\}
x353{0,1}x_{353} \in \{0,1\}
x354{0,1}x_{354} \in \{0,1\}
x355{0,1}x_{355} \in \{0,1\}
x356{0,1}x_{356} \in \{0,1\}
x361{0,1}x_{361} \in \{0,1\}
x362{0,1}x_{362} \in \{0,1\}
x363{0,1}x_{363} \in \{0,1\}
x364{0,1}x_{364} \in \{0,1\}
x365{0,1}x_{365} \in \{0,1\}
x366{0,1}x_{366} \in \{0,1\}
x411{0,1}x_{411} \in \{0,1\}
x412{0,1}x_{412} \in \{0,1\}
x413{0,1}x_{413} \in \{0,1\}
x414{0,1}x_{414} \in \{0,1\}
x415{0,1}x_{415} \in \{0,1\}
x416{0,1}x_{416} \in \{0,1\}
x421{0,1}x_{421} \in \{0,1\}
x422{0,1}x_{422} \in \{0,1\}
x423{0,1}x_{423} \in \{0,1\}
x424{0,1}x_{424} \in \{0,1\}
x425{0,1}x_{425} \in \{0,1\}
x426{0,1}x_{426} \in \{0,1\}
x431{0,1}x_{431} \in \{0,1\}
x432{0,1}x_{432} \in \{0,1\}
x433{0,1}x_{433} \in \{0,1\}
x434{0,1}x_{434} \in \{0,1\}
x435{0,1}x_{435} \in \{0,1\}
x436{0,1}x_{436} \in \{0,1\}
x441{0,1}x_{441} \in \{0,1\}
x442{0,1}x_{442} \in \{0,1\}
x443{0,1}x_{443} \in \{0,1\}
x444{0,1}x_{444} \in \{0,1\}
x445{0,1}x_{445} \in \{0,1\}
x446{0,1}x_{446} \in \{0,1\}
x451{0,1}x_{451} \in \{0,1\}
x452{0,1}x_{452} \in \{0,1\}
x453{0,1}x_{453} \in \{0,1\}
x454{0,1}x_{454} \in \{0,1\}
x455{0,1}x_{455} \in \{0,1\}
x456{0,1}x_{456} \in \{0,1\}
x461{0,1}x_{461} \in \{0,1\}
x462{0,1}x_{462} \in \{0,1\}
x463{0,1}x_{463} \in \{0,1\}
x464{0,1}x_{464} \in \{0,1\}
x465{0,1}x_{465} \in \{0,1\}
x466{0,1}x_{466} \in \{0,1\}
x511{0,1}x_{511} \in \{0,1\}
x512{0,1}x_{512} \in \{0,1\}
x513{0,1}x_{513} \in \{0,1\}
x514{0,1}x_{514} \in \{0,1\}
x515{0,1}x_{515} \in \{0,1\}
x516{0,1}x_{516} \in \{0,1\}
x521{0,1}x_{521} \in \{0,1\}
x522{0,1}x_{522} \in \{0,1\}
x523{0,1}x_{523} \in \{0,1\}
x524{0,1}x_{524} \in \{0,1\}
x525{0,1}x_{525} \in \{0,1\}
x526{0,1}x_{526} \in \{0,1\}
x531{0,1}x_{531} \in \{0,1\}
x532{0,1}x_{532} \in \{0,1\}
x533{0,1}x_{533} \in \{0,1\}
x534{0,1}x_{534} \in \{0,1\}
x535{0,1}x_{535} \in \{0,1\}
x536{0,1}x_{536} \in \{0,1\}
x541{0,1}x_{541} \in \{0,1\}
x542{0,1}x_{542} \in \{0,1\}
x543{0,1}x_{543} \in \{0,1\}
x544{0,1}x_{544} \in \{0,1\}
x545{0,1}x_{545} \in \{0,1\}
x546{0,1}x_{546} \in \{0,1\}
x551{0,1}x_{551} \in \{0,1\}
x552{0,1}x_{552} \in \{0,1\}
x553{0,1}x_{553} \in \{0,1\}
x554{0,1}x_{554} \in \{0,1\}
x555{0,1}x_{555} \in \{0,1\}
x556{0,1}x_{556} \in \{0,1\}
x561{0,1}x_{561} \in \{0,1\}
x562{0,1}x_{562} \in \{0,1\}
x563{0,1}x_{563} \in \{0,1\}
x564{0,1}x_{564} \in \{0,1\}
x565{0,1}x_{565} \in \{0,1\}
x566{0,1}x_{566} \in \{0,1\}
x611{0,1}x_{611} \in \{0,1\}
x612{0,1}x_{612} \in \{0,1\}
x613{0,1}x_{613} \in \{0,1\}
x614{0,1}x_{614} \in \{0,1\}
x615{0,1}x_{615} \in \{0,1\}
x616{0,1}x_{616} \in \{0,1\}
x621{0,1}x_{621} \in \{0,1\}
x622{0,1}x_{622} \in \{0,1\}
x623{0,1}x_{623} \in \{0,1\}
x624{0,1}x_{624} \in \{0,1\}
x625{0,1}x_{625} \in \{0,1\}
x626{0,1}x_{626} \in \{0,1\}
x631{0,1}x_{631} \in \{0,1\}
x632{0,1}x_{632} \in \{0,1\}
x633{0,1}x_{633} \in \{0,1\}
x634{0,1}x_{634} \in \{0,1\}
x635{0,1}x_{635} \in \{0,1\}
x636{0,1}x_{636} \in \{0,1\}
x641{0,1}x_{641} \in \{0,1\}
x642{0,1}x_{642} \in \{0,1\}
x643{0,1}x_{643} \in \{0,1\}
x644{0,1}x_{644} \in \{0,1\}
x645{0,1}x_{645} \in \{0,1\}
x646{0,1}x_{646} \in \{0,1\}
x651{0,1}x_{651} \in \{0,1\}
x652{0,1}x_{652} \in \{0,1\}
x653{0,1}x_{653} \in \{0,1\}
x654{0,1}x_{654} \in \{0,1\}
x655{0,1}x_{655} \in \{0,1\}
x656{0,1}x_{656} \in \{0,1\}
x661{0,1}x_{661} \in \{0,1\}
x662{0,1}x_{662} \in \{0,1\}
x663{0,1}x_{663} \in \{0,1\}
x664{0,1}x_{664} \in \{0,1\}
x665{0,1}x_{665} \in \{0,1\}
x666{0,1}x_{666} \in \{0,1\}

Application with Pyomo

Just as I did with the Zip minigame, let’s solve Mini Sudoku using Pyomo. The great advantage of this library is that it allows the creation of the optimization model with few lines of code instead of having to define each of the model’s components “manually”, which would be necessary, for example, if we had to build the model in a spreadsheet before solving it with a solution software like Excel Solver or OpenSolver, which could demand a lot of time and attention depending on the size of the problem.

In the case of the Mini Sudoku, as you may have noticed, the model has a huge number of components. Considering only the constraints, there are 216 binary constraints (one for each xijkx_{ijk}), 36 constraints for each set of constraints of unique digits per row, per column, per submatrix, and to guarantee a single digit per square (resulting in a total of 144 constraints), in addition to the constraints for pre-filled squares. In short, a simple Mini Sudoku game will have no fewer than 360 constraints, which is already a too large model to work with manually in a spreadsheet (it’s more worthwhile to simply solve the game than to go through all the work of creating the model to solve it with Solver!).

Considering that the number of constraints in a Sudoku model is given by C(n,k)=n3+4n2+kC(n, k) = n^3 + 4n^2 + k, where nn is the order of the square matrix and kk is the number of pre-filled squares, a common model of a classic Sudoku game, which has a 9x9 matrix, would have no fewer than 1,053 constraints! With Pyomo, not only is model creation greatly facilitated, but it’s even possible to automate this process, simply by providing the problem data.

Importing Libraries

import matplotlib.pyplot as plt
import networkx as nx
import pyomo.environ as pyo

Solving Mini Sudoku

Before getting down to business, let’s create a NetworkX graph that will represent an instance of the Mini Sudoku game in question. For this purpose, a Sudoku class was created with the necessary methods for creating, solving, and displaying the Sudoku puzzle.

class Sudoku():
    
    def __init__(self, n, grid:tuple):
        self.__n = n

        p, q = grid

        if p*q == n:
            self.__grid = grid
        else:
            raise ValueError("The dimensions of subgrid must match with the sudoku's length")
        
        G = nx.grid_2d_graph(n, n)
        nx.set_node_attributes(G, 0, "value")
        self.matrix = G

    def set_value_to_square(self, square:tuple, value:int):
        square = tuple(i-1 for i in square)
        self.matrix.nodes[square]["value"] = value

    def get_value_from_square(self, square:tuple) -> int:
        square = tuple(i-1 for i in square)
        return self.matrix.nodes[square]["value"]

    def solve(self):

        # Setting the main inputs of the model.
        n = self.__n
        p, q = self.__grid
        filled_values = [
            (n[0]+1, n[1]+1, self.matrix.nodes[n]["value"]) \
            for n in self.matrix.nodes() if self.matrix.nodes[n]["value"] != 0
        ]

        # Instantiating the Concrete Model.
        model = pyo.ConcreteModel()
        
        # Ranges
        I = model.I = pyo.RangeSet(n)
        J = model.J = pyo.RangeSet(n)
        K = model.K = pyo.RangeSet(n)
        U = model.u = pyo.RangeSet(p)
        V = model.v = pyo.RangeSet(q)

        # Sets
        S = model.Submatrices = pyo.Set(
            V, U,
            initialize= lambda model, v, u: \
            [(i, j) for i in range(p*(v-1)+1, p*v+1) for j in range(q*(u-1)+1, q*u+1)]
        )
        F = model.FilledValues = pyo.Set(initialize=filled_values)
        
        # Decision variables
        x = model.x = pyo.Var(I, J, K, within=pyo.Binary, initialize=0)
        
        # Objective function
        model.obj = pyo.Objective(expr=0)
        
        # Constraints
        model.unique_digits_per_row_constraints = pyo.Constraint(
            J, K,
            rule=lambda model, j, k: sum(x[i,j,k] for i in I) == 1
        )

        model.unique_digits_per_column_constraints = pyo.Constraint(
            I, K,
            rule=lambda model, i, k: sum(x[i,j,k] for j in J) == 1
        )

        model.unique_digits_per_submatrix_constraints = pyo.Constraint(
            V, U, K,
            rule=lambda model, v, u, k: sum(x[i,j,k] for (i, j) in S[v,u]) == 1
        )

        model.single_digit_per_square_constraints = pyo.Constraint(
            I, J,
            rule=lambda model, i, j: sum(x[i,j,k] for k in K) == 1
        )

        model.alreadey_filled_squares_constraints = pyo.Constraint(
            F,
            rule=lambda model, i, j, k: x[i,j,k] == 1
        )
        
        # Solving the model by Gurobi.
        solver = pyo.SolverFactory("gurobi")
        solver.solve(model)

        # Saving the solution in the Sudoku grid.
        solution = [(i, j, k) for i in I for j in J for k in K if model.x[i, j, k].value == 1]
        for i, j, k in solution:
            self.set_value_to_square((i, j), k)
            
    
    def show(self):
        plt.figure(figsize=(3, 3))
        nx.draw(
            G= self.matrix,
            pos= {(i, j): (j, -i) for (i, j) in self.matrix.nodes()},
            with_labels= True,
            labels= {n: self.matrix.nodes[n]["value"] for n in self.matrix.nodes() if self.matrix.nodes[n]["value"] != 0},
            font_color="white",
            node_size= 1100,
            node_shape="s",
            node_color= "#1B1F22",
            width= 0,
            edgecolors="#999999",
            linewidths= .5,
        )
        plt.show()

It’s worth noting that the Sudoku class will only be instantiated if the product of the dimensions of the Submatrix_ passed in the grid argument matches the size of the Sudoku puzzle.

This allows you to create and display the puzzle in question.

mini_sudoku = Sudoku(6, (2,3))
mini_sudoku.set_value_to_square((1,1), 1)
mini_sudoku.set_value_to_square((2,2), 2)
mini_sudoku.set_value_to_square((2,5), 3)
mini_sudoku.set_value_to_square((3,3), 3)
mini_sudoku.set_value_to_square((3,4), 6)
mini_sudoku.set_value_to_square((4,3), 5)
mini_sudoku.set_value_to_square((4,4), 4)
mini_sudoku.set_value_to_square((5,2), 4)
mini_sudoku.set_value_to_square((5,5), 5)
mini_sudoku.set_value_to_square((6,6), 6)

mini_sudoku.show()
<Figure size 300x300 with 1 Axes>

With the game instantiated, simply solve it by calling the solve() method.

mini_sudoku.solve()
mini_sudoku.show()
<Figure size 300x300 with 1 Axes>

Which corresponds exactly to the solution of the game, as expected.

Mini Sudoku solution

Figure 4:Mini Sudoku No. 60’s Solution Source: LinkedIn


References

  • Mini Sudoku. LinkedIn. Available at https://www.linkedin.com/games/mini-sudoku. Accessed on October 10th, 2025.

  • TAKANO, Kevin; DE FREITAS, Rosiane and DE SÁ, Vinícius Gusmão. O jogo de lógica Sudoku: modelagem teórica, NP-Completude e estratégias algorítmicas exatas e heurísticas. In: CONCURSO DE TRABALHOS DE INICIAÇÃO CIENTÍFICA DA SBC (CTIC-SBC), 34., 2015, Recife. Anais […]. Porto Alegre: Sociedade Brasileira de Computação, 2015. p. 71–80.

  • Sudoku. Wikipedia. May 7, 2025. Available at Sudoku. Accessed on October 10th, 2025.