logo
 
Constraint Programming Example
CptS 355 - Programming Language Design
Washington State University
Home
Calendar
Syllabus
Resources
People
Project turn-in

Constraint programming example

In class Friday there were requests to see my sudoku example using constraint programming. The example might help clarify some of what was said in class, but you are responsible only for what was discussed in class.
(*
 * Sudoku solver in alice-ml, using its constraint-programming capability.
 * This is a little tricky to follow if you start reading from the top, but
 * if you start with the in ... end block at the very end of the program
 * it will be clearer. Everything before that is in support of what is 
 * said in that block
 *)

(*
 * the function sudoku is passed to a built-in solver in alice ml;
 * the details are beyond the scope of this presentation. Just note
 * that the result of calling (sudoku sp) is a collection of constrained
 * variables (in the constraint programming sense). Depending on which
 * function is used to call sudoku the effect may be either to find the first
 * solution or all solutions.
 *) 
import structure FD from "x-alice:/lib/gecode/FD"
open FD
fun sudoku sp =
let
   (* 
    * first build the 81 variables of the sudoku problem. We
    * organize them as a vector of vectors. The notation #[...] is
    * a vector expression in alice-ml. rangeVec is a function in the 
    * FD structure. Each of these calls builds a 9-element vector
    * of variables, each variable constrained to values 1 thru 9.
    * Vector.tabulate could be used to avoid writing the same thing
    * 9 times!
    * the parameter sp is a so-called "computation space" roughly
    * corresponding to the abstract machine for constraint programming
    * that I described in class. Alice-ml supports the existence
    * of many such computation spaces at once.
    *)
   val m = #[rangeVec(sp, 9, (1,9)),
             rangeVec(sp, 9, (1,9)),
             rangeVec(sp, 9, (1,9)),
             rangeVec(sp, 9, (1,9)),
             rangeVec(sp, 9, (1,9)),
             rangeVec(sp, 9, (1,9)),
             rangeVec(sp, 9, (1,9)),
             rangeVec(sp, 9, (1,9)),
             rangeVec(sp, 9, (1,9))]
   (* indices - all of the interesting vector indices for this problem *)
   val indices = [0, 1, 2, 3, 4, 5, 6, 7, 8]
   (* 
    * distinct is a function from the FD structure that asserts that 
    * all of the elements of its vector paramenter (v) are distinct.
    * FD.BND means that "bounds propagation" is used -- again the
    * details are beyond the scope of this example.
    *)
   fun distinctRow v = distinct (sp, v, FD.BND)
   (*
    * For columns we have to form a vector of the elements of 
    * the column in order to pass it to distinct.
    * FD.branch states the distribution strategy to use:
    *    in this case choose the variable with the 
    *    fewest values (B_SIZE_MIN) and try the two
    *    cases: 
    *      assume the variable to has the minimum possible value (B_MIN)
    *      assume it does NOT have the minimum possible value 
    *)
   fun distinctCol i = let
      val col = Vector.tabulate (9, fn j => Vector.sub(Vector.sub (m, j), i))
   in	
      distinct (sp, col, FD.BND);
      branch(sp, col, B_SIZE_MIN, B_MIN)
   end
   (* 
    * all of the top left corners of the subsquares;
    * List comprehensions (which alice-ml does not have
    * would be able to express this algorithmically with greater
    * succinctness and clarity
    *)
   val sqTopLeft = [(0,0), (0,3), (0,6),
                  (3,0), (3,3), (3,6),
                  (6,0), (6,3), (6,6)]
   (* 
    * as for distinctCol we need to form a vector containing the elements of the square
    * to pass to distinct 
    *)
   fun distinctSquare (i,j) = let
      (* a list comprehension would be nice here too *)
      val sq = #[Vector.sub(Vector.sub(m,i),j),
                 Vector.sub(Vector.sub(m,i),j+1),
                 Vector.sub(Vector.sub(m,i),j+2),
                 Vector.sub(Vector.sub(m,i+1),j),
                 Vector.sub(Vector.sub(m,i+1),j+1),
                 Vector.sub(Vector.sub(m,i+1),j+2),
                 Vector.sub(Vector.sub(m,i+2),j),
                 Vector.sub(Vector.sub(m,i+2),j+1),
                 Vector.sub(Vector.sub(m,i+2),j+2)]
   in
      distinct(sp, sq, FD.BND)
   end
(*   val givens = [
      (0,1,6), (0,3,1), (0,5,4), (0,7,5),
      (1,2,8), (1,3,3), (1,5,5), (1,6,6),
      (2,0,2), (2,8,1),
      (3,0,8), (3,3,4), (3,5,7), (3,8,6),
      (4,2,6), (4,6,3),
      (5,0,7), (5,3,9), (5,5,1), (5,8,4),
      (6,0,5), (6,8,2),
      (7,2,7), (7,3,2), (7,5,6), (7,6,9),
      (8,1,4), (8,3,5), (8,5,8), (8,7,7)]
*)
(*   val givens = [
      (0,2,1), (0,6,8),
      (1,1,7), (1,3,3), (1,4,1), (1,7,9),
      (2,0,3), (2,4,4), (2,5,5), (2,8,7),
      (3,1,9), (3,3,7), (3,6,5),
      (4,1,4), (4,2,2), (4,4,5), (4,6,1), (4,7,3),
      (5,2,3), (5,5,9), (5,7,4),
      (6,0,2), (6,3,5), (6,4,7), (6,8,4),
      (7,1,3), (7,4,9), (7,5,1), (7,7,6),
      (8,2,4), (8,6,3)
   ]   
*)
   (* 
    * Being lazy I didn't write any input code and just
    * coded the sudoku puzzle input as part of the program
    *)
   val givens = [
      (0,3,1), (0,6,7), (0,7,4),
      (1,1,5), (1,4,9), (1,7,3), (1,8,2),
      (2,2,6), (2,3,7), (2,6,9),
      (3,0,4), (3,3,8),
      (4,1,2), (4,7,1),
      (5,5,9), (5,8,5),
      (6,2,4), (6,5,7), (6,6,3),
      (7,0,7), (7,1,3), (7,4,2), (7,7,6),
      (8,1,6), (8,2,5), (8,5,4)
   ]   

   (* 
    * To assert that a particular square (i,j) has value v we use
    * FD.reLI which inserts an integer relation (EQ in this case) between
    * a particular variable and value
    *)
   fun insertGiven (i,j,v) = relI(sp,Vector.sub(Vector.sub(m, i),j), EQ, v)
in
   (* 
    * distinctRow imposes all the constraints needed to say that the elements of a row
    * are distinct. The representation of the problem is a vector of vectors, each
    * vector containing the "variables" (in the constraint programming sense) of a 
    * single row. distinctRow takes a vector of such variables. 
    *)
   Vector.app distinctRow m;
   (*
    * distinctCol imposes all the constraints needed to say that the elements of a column
    * are distinct. Since the columns are not themselves vectors (the jth col is the jth element
    * of each of the nine row vectors) distinctCol takes an int column number as its parameter
    *)
   List.app distinctCol indices;
   (*
    * distinctSquare imposes all the constraints for a single square given the indices of 
    * its top left corner
    *)
   List.app distinctSquare sqTopLeft;
   (*
    * given a list of (i,j,v) tuples, insertGivens asserts the equality of 
    * the variable representing the (i,j) square to v.
    *)
   List.app insertGiven givens;
   (*
    * The result is the vector of vectors of variables.
    *)
   m
end


                                                                                                                                                                                                                                                                                                                                             
  (c) 2003 Curtis Dyreson, (c) 2004, 2005 Carl H. Hauser           E-mail questions or comments to Prof. Carl Hauser