Chapter 1 Getting Started
This first chapter introduces the overall framework of FaCiLe and gives
a preliminary insight about its programming environment and functionalities.
OCaml code using FaCiLe facilities (file csp.ml
in the
following examples) must be compiled with the library of object byte
code facile.cma
when batch compiling with ocamlc
:
ocamlc facile.cma csp.ml
and with the library of object code facile.cmxa
for native
compilation with ocamlopt
:
ocamlopt facile.cmxa csp.ml
provided that the standard installation of FaCiLe (and previously of
the OCaml system of course) has been performed (see p. ??) and
that the facile.cm[x]a
files has been thereafter successfully
created in the OCaml standard library directory. For larger programs,
a generic Makefile can be found in directory examples
(see p. ??).
It may however be convenient to use an OCaml custom toplevel to
experiment toy examples or check small piece of serious (thus large)
code. A FaCiLe toplevel (i.e. in which facile.cma
is pre-loaded)
is easily build with the following command:
ocamlmktop -o facile facile.cma
and invoked with ./facile
.
The two following sections give a quick overview of the main basic
concepts of FaCiLe with the help of two very simple examples which
are precisely explained step by step.
1.1 Basics
We first give a slight taste of FaCiLe with the recurrent trivial
problem of the Canadian flag:
one have to repaint the Canadian flag (shown in figure 1.1)
with its two original colors, red and white, such that two
neighbouring areas don't have the same color and the maple leaf is of
course red. The CSP model is desperately straightforward:
-
one variable for each area l, c, r and m;
- all variables have the same domain [0..1], 0 being red and 1, white;
- one difference constraint for each adjacence l ¹ c,
c ¹ r, m ¹ c and the maple leaf is forced to be red m = 0.
Figure 1.1:
The problem of the Canadian flag
The following piece of code solves this problem:
maple.ml
open Facile
open Easy
let _ =
(* Variables *)
let red = 0 and white = 1 in
let dom = Domain.create [red; white] in
let l = Fd.create dom and c = Fd.create dom
and r = Fd.create dom and m = Fd.create dom in
(* Constraints *)
Cstr.post (fd2e l <>~ fd2e c);
Cstr.post (fd2e c <>~ fd2e r);
Cstr.post (fd2e m <>~ fd2e c);
Cstr.post (fd2e m =~ i2e red);
(* Goal *)
let var_list = [l;c;r;m] in
let goal = Goals.GlList.labeling var_list in
(* Search *)
if Goals.solve goal then begin
Printf.printf "l="; Fd.fprint stdout l;
Printf.printf " c="; Fd.fprint stdout c;
Printf.printf " r="; Fd.fprint stdout r;
Printf.printf " m="; Fd.fprint stdout m;
print_newline () end
else
prerr_endline "No solution"
ocamlc facile.cma maple.ml
./a.out
l=0 c=1 r=0 m=0
The new flag is therefore a faithful copy of the genuine one.
This small example introduces the following features of FaCiLe:
-
The user interface to the library is provided by module
Facile
which gathers several specialized ``sub-modules''. We thus advise to
open module Facile
systematically to lighten FaCiLe functions
calls. Most frequently used functions and sub-modules can then be
directly accessed by opening module Easy
(open Easy
).
Functions and modules names have been carefully chosen to avoid
name clashes as much as possible with OCaml standard library when
opening these two modules, but the ``dot prefix'' notation
can still be used in case of fortuitous overlapping.
- The problem variables are created by a call to function
create
of module Fd
(for Finite domain,
see 4.13) which takes a domain of type Domain.t
as
only argument. Domains are build and handled by functions of
module Domain
(see 4.4) like
Domain.create l
which creates a domain containing all integers
of list l
.
-
fd2e
and i2e
constructs an expression respectively
from a variable and an integer. More complex arithmetic expressions
and constraints are build with infix operators (obtained by adding the
suffix ~
to their integer counterparts) taking two expressions
as arguments. Most usual arithmetic operators (not necessarily infix)
are provided in module Arith
(see 4.2).
- Function
post
from module Cstr
adds a constraint to the
constraint ``store'', which means that the constraint is taken into account
and domain reduction is performed (as well as propagation on other variables).
- The search goal is here a simple labeling of the list of all the
problem variables
[l;c;r;m]
obtained by a call to function
labeling
of sub-module GlList
embedded in module
Goals
(see 4.7). The goal is therafter solved by
a call to solve
which returns false
if a failure occured
and true
otherwise.
- The solution is then printed using function
fprint
from
module Fd
, which prints a variable on an output channel, i.e. its
domain if the variable is not instantiated and its value otherwise.
This piece of code illustrates a typical FaCiLe CSP solving with the
following pervasive ordered structure:
-
data and variables declaration;
- constraints statements;
- search goal specification;
- goal solving, i.e. searching solution(s).
In the next section, a more sophisticated example will help to
describe more precisely how these very features can be easily
implemented with FaCiLe.
1.2 A Classic Example
We solve now the even more recurrent cryptarithmetic problem
SEND+MORE=MONEY (see figure 1.2) where each letter stands for
a distinct digit with M¹ 0 and S¹ 0.
|
|
S |
E |
N |
D |
+ |
M |
O |
R |
E |
|
M |
O |
N |
E |
Y |
|
Figure 1.2:
The
SEND+
MORE=
MONEY problem
We model this problem with one variable for each digit plus three auxilliary
variables to carry over, and the subsequent four arithmetic constraints
specifying the result of the addition as we would do by hand. The following
program implement this model:
smm.ml
open Facile
open Easy
let _ =
(* Variables *)
let s = Fd.interval 0 9 and e = Fd.interval 0 9 and n = Fd.interval 0 9
and d = Fd.interval 0 9 and m = Fd.interval 0 9 and o = Fd.interval 0 9
and r = Fd.interval 0 9 and y = Fd.interval 0 9 in
(* Constraints *)
Cstr.post (fd2e m >~ i2e 0);
Cstr.post (fd2e s >~ i2e 0);
let digits = [|s;e;n;d;m;o;r;y|] in
Cstr.post (Alldiff.cstr digits);
let c = Fd.array 3 0 1 in (* Carry array *)
let one x = fd2e x and ten x = i2e 10 *~ fd2e x in
Cstr.post ( one d +~ one e =~ one y +~ ten c.(0));
Cstr.post (one c.(0) +~ one n +~ one r =~ one e +~ ten c.(1));
Cstr.post (one c.(1) +~ one e +~ one o =~ one n +~ ten c.(2));
Cstr.post (one c.(2) +~ one s +~ one m =~ one o +~ ten m);
(* Search goal solving *)
if Goals.solve (Goals.GlArray.labeling digits) then begin
let value = Fd.min in
Printf.printf " %d%d%d%d\\n" (value s) (value e) (value n) (value d);
Printf.printf "+ %d%d%d%d\\n" (value m) (value o) (value r) (value e);
Printf.printf "=%d%d%d%d%d\\n" (value m) (value o) (value n) (value e) (value
y)
end else
prerr_endline "No solution"
ocamlc facile.cma smm.ml
./a.out
9567
+ 1085
=10652
We detail each step of the above example:
-
Variables whose domain ranges an integer interval are created
with function Fd.interval inf sup which creates a variable whose
domain contains all integers between
inf
and sup
(inclusive).
- Disequations M¹ 0 and S¹ 0 are then expressed by arithmetic
inequality constraints and we assert that all digits must be distinct
with the global
Alldiff.cstr
constraint which takes an array of
variables as argument (see 4.1). FaCiLe provides
some other global constraints as well, such as the global cardinality
constraint (a.k.a. the ``distribute'' constraint) or the sorting
constraint (see 4.6 and 4.11), usually
embedded in separate module and called with function cstr
.
- The three auxilliary carry variables are then created with
Fd.array n inf sup
which builds an array of n
variables
whose domains range the interval [inf
..sup
], and two
auxilliary functions one x
and ten x
are defined which
return an arithmetic expression being respectively equal to x
and ten times x
to lighten the main constraints statements.
- The equations reproducing the way we would compute the addition of
figure 1.2 by hand are then straightforwardly stated an posted
to the constraint store. The problem is finally solved as in the
previous example by a simple labeling of the decision variables,
i.e. the ``digits'', using function
labeling
of module
Goals.GlArray
(which is the counterpart of GlList
over arrays of variables). The solution is then printed with
function Fd.min
which obviously returns the minimal value
contained in the domain of a variable or its instantiation value
if the variable is ground (which is here the case as function
labeling
instantiates all the variables or raise a failure
which would yield a No solution
answer).
We could of course have used a different but equivalent model
constraining the addition to be exact without auxilliary carry
variables:
...
let op1 =
i2e 1000 *~ fd2e s +~ i2e 100 *~ fd2e e +~ i2e 10 *~ fd2e n +~ fd2e d
and op2 =
i2e 1000 *~ fd2e m +~ i2e 100 *~ fd2e o +~ i2e 10 *~ fd2e r +~ fd2e e in
let result =
i2e 10000 *~ fd2e m +~
i2e 1000 *~ fd2e o +~ i2e 100 *~ fd2e n +~ i2e 10 *~ fd2e e +~ fd2e y in
Cstr.post (op1 +~ op2 =~ op3);
...
This alternative model would undoubtedly produce the same result.
The next chapter will explore in a more formal way how to handle
the main concepts of FaCiLe introduced in the two previous examples.