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

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 upper C Subscript i j left-parenthesis i equals 1 ellipsis 9 comma j equals 1 ellipsis 9 right-parenthesis, where upper C Subscript i j is the value of cell left-parenthesis i comma j right-parenthesis 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 upper X Subscript i j left-parenthesis i equals 1 ellipsis 9 comma j equals 1 ellipsis 9 right-parenthesis represent the value of cell left-parenthesis i comma j right-parenthesis in the grid. The domain of each of these variables is left-bracket 1 comma 9 right-bracket. 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(upper X Subscript i Baseline 1 Baseline minus upper X Subscript i Baseline 9) forces all values in row i to be different, the constraint ALLDIFF(upper X Subscript 1 j Baseline minus upper X Subscript 9 j) forces all values in column j to be different, and the constraint ALLDIFF(upper X Subscript i j) left-parenthesis left-parenthesis i equals 1 comma 2 comma 3 semicolon j equals 1 comma 2 comma 3 right-parenthesis comma left-parenthesis i equals 1 comma 2 comma 3 semicolon j equals 4 comma 5 comma 6 right-parenthesis comma ellipsis comma left-parenthesis i equals 7 comma 8 comma 9 semicolon j equals 7 comma 8 comma 9 right-parenthesis right-parenthesis 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

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

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

The CLP Procedure

Solution Summary
Solution StatusAll Solutions
Variable SelectionMINR
Value SelectionMIN
  
Solutions Found5299405
Solution Time510.463005


Output 4.1.5: Distributed Mode

The CLP Procedure

Solution Summary
Solution StatusAll Solutions
Variable SelectionMINR
Value SelectionMIN
  
Solutions Found5299405
Solution Time32.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 pi 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

Pi Day Sudoku 2008


The rules for this puzzle are a little different from the rules for standard sudoku:

  1. Rather than using regular 3 × 3 blocks, this puzzle uses jigsaw regions such that highlighted regions in the middle resemble the Greek letter pi. Each jigsaw region consists of 12 contiguous cells.

  2. The first 12 digits of pi are used instead of the digits 1–9. Each row, column, and jigsaw region contains the first 12 digits of pi (3 1 4 1 5 9 2 6 5 3 5 8) 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 upper C Subscript i j left-parenthesis i equals 1 comma ellipsis comma 12 comma j equals 1 comma ellipsis comma 12 right-parenthesis where upper C Subscript i j is the value of cell left-parenthesis i comma j right-parenthesis 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 upper X Subscript i j represent the value of the cell that corresponds to row i and column j. The domain of each of these variables is left-bracket 1 comma 9 right-bracket.

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 pi.

In particular, the variables in row r, r equals 1 comma ellipsis comma 12 are upper X Subscript r Baseline 1 Baseline comma ellipsis comma upper X Subscript r Baseline 12 Baseline. 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 upper X Subscript 1 c Baseline comma ellipsis comma upper X Subscript 12 c Baseline. 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

ObsC1C2C3C4C5C6C7C8C9C10C11C12
1325154631895
2415238595136
3614593583125
4533185925641
5892651154335
6581529433561
7153816249553
8945351568213
9236515315489
10368945151352
11151363852954
12559432316518


The corresponding completed grid is shown in Output 4.1.8.

Output 4.1.8: Solution to Pi Day Sudoku 2008

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

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

The CLP Procedure

Variable Selection Strategy Evaluation
Selection StrategyEvaluation TimeSolution IndexVariables Selected
MINR11144
FIFO0.732144
MAXC0.413144
MAXCS0.44144
MINRMAXC1.01NOT FOUND125
WDEG0.025144
DOMDDEG1.01NOT FOUND125
DOMWDEG0.796144
DOMDEG17144


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

The CLP Procedure

Solution Summary
Solution StatusAll Solutions
Variable SelectionWDEG
Value SelectionMIN
  
Solutions Found5361
Solution Time20.5


Last updated: September 09, 2026