|
Constraint Programming Example
CptS 355 - Programming Language Design Washington State University |
|
Constraint programming exampleHere is the sudoku example of constraint programming, coded in a language called Alice-ML (look it up!). 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
|
|