The CLP Procedure
Example 4.1 Logic-Based Puzzles
Many logic-based puzzles can be formulated as CSPs. Several such instances are shown in this example.
Sudoku
A sudoku is a logic-based, combinatorial number-placement puzzle that uses a partially filled 9×9 grid. The objective is to fill the grid with the digits 1 to 9, so that each column, each row, and each of the nine 3×3 blocks contain only one of each digit. Output 4.1.1 shows an example of a sudoku grid.
Output 4.1.1: An Example of an Unsolved Sudoku Grid

This example illustrates the use of the ALLDIFFERENT constraint to solve the preceding sudoku problem.
The data set indata contains the partially filled values for the grid and is used to create the set of macro variables
, where
is the value of cell
in the grid when specified, and missing otherwise.
data indata;
input C1-C9;
datalines;
. . 5 . . 7 . . 1
. 7 . . 9 . . 3 .
. . . 6 . . . . .
. . 3 . . 1 . . 5
. 9 . . 8 . . 2 .
1 . . 2 . . 4 . .
. . 2 . . 6 . . 9
. . . . 4 . . 8 .
8 . . 1 . . 5 . .
;
%macro store_initial_values;
/* store initial values into macro variable C_i_j */
data _null_;
set indata;
array C{9};
do j = 1 to 9;
i = _N_;
call symput(compress('C_'||put(i,best.)||'_'||put(j,best.)),
put(C[j],best.));
end;
run;
%mend store_initial_values;
%store_initial_values;
Let the variable
represent the value of cell
in the grid. The domain of each of these variables is
. Three sets of alldifferent constraints are used to set the required rules for each row, each column, and each of the 3×3 blocks. The constraint ALLDIFF(
) forces all values in row i to be different, the constraint ALLDIFF(
) forces all values in column j to be different, and the constraint ALLDIFF(
)
forces all values in each block to be different.
The following statements solve the sudoku puzzle:
%macro solve;
proc clp out=mylib.outdata;
/* Declare variables */
/* Nine row constraints */
%do i = 1 %to 9;
var (X_&i._1-X_&i._9) = [1,9];
alldiff(X_&i._1-X_&i._9);
%end;
/* Nine column constraints */
%do j = 1 %to 9;
alldiff(
%do i = 1 %to 9;
X_&i._&j
%end;
);
%end;
/* Nine 3x3 block constraints */
%do s = 0 %to 2;
%do t = 0 %to 2;
alldiff(
%do i = 3*&s + 1 %to 3*&s + 3;
%do j = 3*&t + 1 %to 3*&t + 3;
X_&i._&j
%end;
%end;
);
%end;
%end;
/* Initialize variables to cell values */
/* X_i_j = C_i_j if C_i_j is non-missing */
%do i = 1 %to 9;
%do j = 1 %to 9;
%if &&C_&i._&j ne . %then %do;
lincon X_&i._&j = &&C_&i._&j;
%end;
%end;
%end;
run;
%put &_ORCLP_;
%mend solve;
%solve
Output 4.1.2 shows the solution.
Output 4.1.2: Solution of the Sudoku Grid

Finally, substituting the following PROC CLP statement and invoking the CLP procedure finds all 5,299,405 solutions to the partially filled sudoku grid shown in Output 4.1.3:
proc clp out=mylib.outdata findall timetype=real;
Output 4.1.3: An Example of an Unsolved Sudoku Grid

The solution summaries shown in Output 4.1.4 and Output 4.1.5 correspond to solutions of the CSP in two different modes. The first solution summary corresponds to solving the CSP in single-machine mode, and the second solution summary corresponds to solving the CSP in distributed mode by using a grid that contains 142 nodes.
Output 4.1.4: Single-Machine Mode
| Solution Summary | |
|---|---|
| Solution Status | All Solutions |
| Variable Selection | MINR |
| Value Selection | MIN |
| Solutions Found | 5299405 |
| Solution Time | 510.463005 |
Output 4.1.5: Distributed Mode
| Solution Summary | |
|---|---|
| Solution Status | All Solutions |
| Variable Selection | MINR |
| Value Selection | MIN |
| Solutions Found | 5299405 |
| Solution Time | 32.777298 |
Pi Day Sudoku
The basic structure of the classical sudoku problem can easily be extended to formulate more complex puzzles. One such example is the Pi Day sudoku puzzle. Pi Day is a celebration of the number that occurs every March 14. In honor of Pi Day, Brainfreeze Puzzles (Riley and Taalman 2008) celebrated this day with a special 12×12 grid sudoku puzzle. The 2008 Pi Day sudoku puzzle is shown in Output 4.1.6.
Output 4.1.6: Pi Day Sudoku 2008

The rules for this puzzle are a little different from the rules for standard sudoku:
Rather than using regular 3 × 3 blocks, this puzzle uses jigsaw regions such that highlighted regions in the middle resemble the Greek letter
. Each jigsaw region consists of 12 contiguous cells.
The first 12 digits of
are used instead of the digits 1–9. Each row, column, and jigsaw region contains the first 12 digits of
(
) in some order. In other words, there are no 7s; one each of 2, 4, 6, 8, and 9; two each of 1 and 3; and three 5s.
The data set raw contains the partially filled values for the grid and, similar to the sudoku problem, is used to create the set of macro variables
where
is the value of cell
in the grid when specified, and missing otherwise.
data raw;
input C1-C12;
datalines;
3 . . 1 5 4 . . 1 . 9 5
. 1 . . 3 . . . . 1 3 6
. . 4 . . 3 . 8 . . 2 .
5 . . 1 . . 9 2 5 . . 1
. 9 . . 5 . . 5 . . . .
5 8 1 . . 9 . . 3 . 6 .
. 5 . 8 . . 2 . . 5 5 3
. . . . 5 . . 6 . . 1 .
2 . . 5 1 5 . . 5 . . 9
. 6 . . 4 . 1 . . 3 . .
1 5 1 . . . . 5 . . 5 .
5 5 . 4 . . 3 1 6 . . 8
;
%macro cdata;
/* store each pre-filled value into macro variable C_i_j */
data _null_;
set raw;
array C{12};
do j = 1 to 12;
i = _N_;
call symput(compress('C_'||put(i,best.)||'_'||put(j,best.)),
put(C[j],best.));
end;
run;
%mend cdata;
%cdata;
As in the sudoku problem, let the variable represent the value of the cell that corresponds to row i and column j. The domain of each of these variables is
.
For each row, column, and jigsaw region, a GCC statement is specified to enforce the condition that it contain exactly the first twelve digits of .
In particular, the variables in row r, are
. The SAS macro %CONS_ROW(R) enforces the GCC constraint that row r contain exactly two 1s, two 3s, three 5s, no 7s, and one of each of the other values:
%macro cons_row(r);
/* Row r must contain two 1's, two 3's, three 5's, no 7's, */
/* and one for each of other values from 1 to 9. */
gcc(X_&r._1-X_&r._12) =
( (1, 2, 2) (3, 2, 2) (5, 3, 3) (7, 0, 0) DL=1 DU=1 );
%mend cons_row;
The variables in column c are . The SAS macro %CONS_COL(C) enforces a similar GCC constraint for each column c.
%macro cons_col(c);
/* Column c must contain two 1's, two 3's, three 5's, */
/* no 7's, and one for each of other values from 1 to 9. */
gcc( %do r = 1 %to 12;
X_&r._&c.
%end;
) = ((1, 2, 2) (3, 2, 2) (5, 3, 3) (7, 0, 0) DL=1 DU=1);
%mend cons_col;
Generalizing this concept further, the SAS macro %CONS_REGION(VARS) enforces the GCC constraint for the jigsaw region that is defined by the macro variable VARS.
%macro cons_region(vars);
/* Jigsaw region that contains &vars must contain two 1's, */
/* two 3's, three 5's, no 7's, and one for each of other */
/* values from 1 to 9. */
gcc(&vars.) = ((1, 2, 2) (3, 2, 2) (5, 3, 3) (7, 0, 0) DL=1 DU=1);
%mend cons_region;
The following SAS statements incorporate the preceding macros to define the GCC constraints in order to find all solutions of the Pi Day sudoku 2008 puzzle:
%macro pds(fn=mylib.pdsout,solns=allsolns,varsel=MINR,maxtime=900);
proc clp out=&fn &solns
varselect=&varsel /* Variable selection strategy */
maxtime=&maxtime; /* Time limit */
/* Variable X_i_j represents the grid of ith row and jth column. */
var (
%do i = 1 %to 12;
X_&i._1 - X_&i._12
%end;
) = [1,9];
/* X_i_j = C_i_j if C_i_j is non-missing */
%do i = 1 %to 12;
%do j = 1 %to 12;
%if &&C_&i._&j ne . %then %do;
lincon X_&i._&j = &&C_&i._&j;
%end;
%end;
%end;
/* 12 Row constraints: */
%do r = 1 %to 12;
%cons_row(&r);
%end;
/* 12 Column constraints: */
%do c = 1 %to 12;
%cons_col(&c);
%end;
/* 12 Jigsaw region constraints: */
/* Each jigsaw region is defined by the macro variable &vars. */
/* Region 1: */
%let vars = X_1_1 - X_1_3 X_2_1 - X_2_3
X_3_1 X_3_2 X_4_1 X_4_2 X_5_1 X_5_2;
%cons_region(&vars.);
/* Region 2: */
%let vars = X_1_4 - X_1_9 X_2_4 - X_2_9;
%cons_region(&vars.);
/* Region 3: */
%let vars = X_1_10 - X_1_12 X_2_10 - X_2_12
X_3_11 X_3_12 X_4_11 X_4_12 X_5_11 X_5_12;
%cons_region(&vars.);
/* Region 4: */
%let vars = X_3_3 - X_3_6 X_4_3 - X_4_6 X_5_3 - X_5_6;
%cons_region(&vars.);
/* Region 5: */
%let vars = X_3_7 - X_3_10 X_4_7 - X_4_10 X_5_7 - X_5_10;
%cons_region(&vars.);
/* Region 6: */
%let vars = X_6_1 - X_6_3 X_7_1 - X_7_3
X_8_1 - X_8_3 X_9_1 - X_9_3;
%cons_region(&vars.);
/* Region 7: */
%let vars = X_6_4 X_6_5 X_7_4 X_7_5 X_8_4 X_8_5
X_9_4 X_9_5 X_10_4 X_10_5 X_11_4 X_11_5;
%cons_region(&vars.);
/* Region 8: */
%let vars = X_6_6 X_6_7 X_7_6 X_7_7 X_8_6 X_8_7
X_9_6 X_9_7 X_10_6 X_10_7 X_11_6 X_11_7;
%cons_region(&vars.);
/* Region 9: */
%let vars = X_6_8 X_6_9 X_7_8 X_7_9 X_8_8 X_8_9
X_9_8 X_9_9 X_10_8 X_10_9 X_11_8 X_11_9;
%cons_region(&vars.);
/* Region 10: */
%let vars = X_6_10 - X_6_12 X_7_10 - X_7_12
X_8_10 - X_8_12 X_9_10 - X_9_12;
%cons_region(&vars.);
/* Region 11: */
%let vars = X_10_1 - X_10_3 X_11_1 - X_11_3 X_12_1 - X_12_6;
%cons_region(&vars.);
/* Region 12: */
%let vars = X_10_10 - X_10_12 X_11_10 - X_11_12 X_12_7 - X_12_12;
%cons_region(&vars.);
run;
%put &_ORCLP_;
%mend pds;
%pds;
The only solution of the 2008 Pi Day sudoku puzzle is shown in Output 4.1.7.
Output 4.1.7: Solution to Pi Day Sudoku 2008
| Pi Day Sudoku 2008 |
| Obs | C1 | C2 | C3 | C4 | C5 | C6 | C7 | C8 | C9 | C10 | C11 | C12 |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 1 | 3 | 2 | 5 | 1 | 5 | 4 | 6 | 3 | 1 | 8 | 9 | 5 |
| 2 | 4 | 1 | 5 | 2 | 3 | 8 | 5 | 9 | 5 | 1 | 3 | 6 |
| 3 | 6 | 1 | 4 | 5 | 9 | 3 | 5 | 8 | 3 | 1 | 2 | 5 |
| 4 | 5 | 3 | 3 | 1 | 8 | 5 | 9 | 2 | 5 | 6 | 4 | 1 |
| 5 | 8 | 9 | 2 | 6 | 5 | 1 | 1 | 5 | 4 | 3 | 3 | 5 |
| 6 | 5 | 8 | 1 | 5 | 2 | 9 | 4 | 3 | 3 | 5 | 6 | 1 |
| 7 | 1 | 5 | 3 | 8 | 1 | 6 | 2 | 4 | 9 | 5 | 5 | 3 |
| 8 | 9 | 4 | 5 | 3 | 5 | 1 | 5 | 6 | 8 | 2 | 1 | 3 |
| 9 | 2 | 3 | 6 | 5 | 1 | 5 | 3 | 1 | 5 | 4 | 8 | 9 |
| 10 | 3 | 6 | 8 | 9 | 4 | 5 | 1 | 5 | 1 | 3 | 5 | 2 |
| 11 | 1 | 5 | 1 | 3 | 6 | 3 | 8 | 5 | 2 | 9 | 5 | 4 |
| 12 | 5 | 5 | 9 | 4 | 3 | 2 | 3 | 1 | 6 | 5 | 1 | 8 |
The corresponding completed grid is shown in Output 4.1.8.
Output 4.1.8: Solution to Pi Day Sudoku 2008

Next, a variation of this problem illustrates the use of the EVALVARSEL= option to help determine an appropriate selection strategy. Consider the same Pi Day sudoku puzzle with a few more missing cells, as shown in Output 4.1.9. The objective is to find all the possible solutions, subject to the same constraints as before.
Output 4.1.9: Pi Day Sudoku 2008

When you specify the EVALVARSEL= option without including a keyword list, the CLP procedure evaluates each available variable selection strategy for the amount of time that you specify in the MAXTIME= option. When you are running in distributed mode, in which there are at least as many machines as strategies, the CSPs are solved concurrently, and each selection strategy is evaluated on a different machine. The EvalInfo ODS table, which is shown in Output 4.1.10, identifies the variable selection strategies that find a solution within one second in distributed mode on a 10-machine network. This information can also be found in the _ORCLPEVS_ macro variable.
Output 4.1.10: EvalInfo ODS Table
| Variable Selection Strategy Evaluation | |||
|---|---|---|---|
| Selection Strategy | Evaluation Time | Solution Index | Variables Selected |
| MINR | 1 | 1 | 144 |
| FIFO | 0.73 | 2 | 144 |
| MAXC | 0.41 | 3 | 144 |
| MAXCS | 0.4 | 4 | 144 |
| MINRMAXC | 1.01 | NOT FOUND | 125 |
| WDEG | 0.02 | 5 | 144 |
| DOMDDEG | 1.01 | NOT FOUND | 125 |
| DOMWDEG | 0.79 | 6 | 144 |
| DOMDEG | 1 | 7 | 144 |
Finally, the CLP procedure is invoked in distributed mode by using the FINDALL option and the WDEG selection strategy. The resulting solution summary, which is shown in Output 4.1.11, indicates that all 5,361 solutions have been found, along with the solution time.
Output 4.1.11: WDEG Selection Strategy
| Solution Summary | |
|---|---|
| Solution Status | All Solutions |
| Variable Selection | WDEG |
| Value Selection | MIN |
| Solutions Found | 5361 |
| Solution Time | 20.5 |