" /> My Constraint Programming Blog: June 2009 Archives

« May 2009 | Main | July 2009 »

June 29, 2009

Constraint programming in ECLiPSe Constraint Programming System

This last few weeks I have programmed in ECLiPSe Constraint Logic Programming system more systematically than before. For example, many of my "learning models" has now been implemented in ECLiPSe. These and other ECLiPSe models is now collected at My ECLiPSe Page.

ECLiPSe

ECLiPSe is very well documented system with a couple of online books for different aspects of the system. For more info see:
  • Documentation
  • Examples
  • The book Constraint Programming using ECLiPSe by Krzysztof R. Apt and Mark Wallace, Cambridge University Press, 2006, ISBN: 9780521866286.
  • Mailing lists


My ECLiPSe page

There is now a My ECLiPSe Page with so far 46 ECLiPSe models (see below). Expect that this number will increase.

Arrays and loops

In these models I have almost exclusively used the following two really great features of ECLiPSe, which are extensions to Prolog proper: For logical loops, also see Logical loops by Joachim Schimpf (2002). A quite recent discussion in comp.lang.prolog, foreach for Prolog discussed this more.

Arrays are very handy to use, especially in combinations with loops, see below for an example. Using loops and arrays means that I have been able to port my models quite easy, often directly from the MiniZinc and/or Comet models. This means that there are very few traditional Prolog recursive predicates in these models. Note that loops don't require arrays, they are as powerful with plain old lists, and ECLiPSe has of course also support for traditional Prolog logic programming.

As I understand it, SICStus Prolog will soon support logical loops (inspired by the ECLiPSe approach). That's great!

Example: Diet problem

For a starter, let's implement a simple diet's problem, where we want to minimize the cost of different food given that the nutrition constraints is met. This problem is in diet.ecl and is shown below.
:- lib(ic).
:- lib(branch_and_bound).

go :-
        % 
        % fetch the data
        % 
        data(Price, Limits, Calories, Chocolate, Sugar, Fat),

        % Xs is the decision variable containing the number
        % of nutrition to buy
        length(Calories, Len), % for setting the length of Xs
        length(Xs, Len),       % set the same length for Xs 
 	Xs :: 0..10,           % domains for Xs
        
        % convert Limits to LimitArray (so we can use LimitArray[i] below)
        length(Limits, LimitLength),
        dim(LimitArray, [LimitLength]),
        collection_to_list(LimitArray, Limits),

        % these constraints calculates: sum(Xs[i]*Y[i]) >= limit 
        Xs * Calories  #>= LimitArray[1], % 500,
        Xs * Chocolate #>= LimitArray[2], %   6,
        Xs * Sugar     #>= LimitArray[3], %  10,
        Xs * Fat       #>= LimitArray[4], %   8,

        %
        % search: minimize the sum of Xs * Price
        % 
        XSum #= Xs * Price, 

        minimize(search(Xs,0,first_fail,indomain,complete,[]),XSum),

        writeln([XSum, Xs]).

%
% data
%
data(
        [ 50, 20, 30, 80], % price in cents for each nutrition
        [500,  6, 10,  8], % limits, requirements for each nutrition
                           % type

        % nutrition for each product
        [400, 200, 150, 500], % calories
        [3,2,0,0],            % chocolate
        [2,2,4,4],            % sugar
        [2,4,1,5]             % fat
).
Note especially the construct Total = List1 * List2 which is a syntactical sugar for following (using MiniZinc/Comet syntax):
Total = sum(i from 1..n) (List1[i]*List2[i])


Using loops and arrays: Quasigroup completion

Quasigroup completion is a problem where starting from a not fully completed Latin square, and the object is to complete this square. quasigroup_completion.ecl is a model for this problem.

The model has two principal parts: * stating the problem instances, which I have done as a matrix using an array of arrays. The [](....) is used for declaring arrays. Here is the first problem:
problem(1, []([](1, _, _, _, 4),  
              [](_, 5, _, _, _),
              [](4, _, _, 2, _),
              [](_, 4, _, _, _),
              [](_, _, 5, _, 1))).
* the model
Below is the code for solving the problem and filling out the squares, as well as keep the Latin square condition (for all rows and all columns there must be unique numbers):
solve(ProblemNum) :-
        problem(ProblemNum, Problem),
        dim(Problem, [N,N]), % get the size of the problem
        Problem[1..N,1..N] :: 1..N,
        (for(I, 1, N), param(Problem, N) do
             ic_global:alldifferent(Problem[I,1..N]),
             ic_global:alldifferent(Problem[1..N,I])
        ),
        search(Problem,0,first_fail,indomain,complete,[backtrack(Backtracks)]),
        nl,
        pretty_print(Problem),
        writeln(backtracks:Backtracks).
Here the loop (for(I,1,N)...) is used for looping through all the rows of the matrix. Note how easy it is to use "slices" of the matrix, e.g. Problem[I,1..N].

Comment: This problem is really a subset of solving a Sudoku. The ECLiPSe distribution has an example for solving these problems: sudoku.ecl.txt.

Another example: XKCD knapsack problem

The xkcd knapsack problem is implemented in xkcd.ecl. The models show some different approaches.

Using "plain" lists and the syntactic sugar for multiplying two lists is is quite short:
go4 :-
        Prices = [215, 275, 335, 355, 420, 580],
        Total = 1505,
        length(Prices, Len),
        length(Xs, Len),
        Xs :: [0..10],
        Total #= Xs * Prices,
        labeling(Xs),
        writeln([Total, Xs]).
The multiplication syntactic sugar don't work with arrays, but we can convert the array (a "collection") to a list with collection_to_list/2. This version also minimize the number of dishes:
go3 :-
        Prices = [](215, 275, 335, 355, 420, 580), % Prices for the dishes
        Total = 1505,       % multiply 15.05 with 100 for integer usage
        dim(Prices, [Len]), % get length of Prices
        dim(Xs, [Len]),     % same length of Xs 
        Xs :: [0..10],      % domain of Xs
        % get all solutions
        knapsack(Prices, Total, Xs),

        collection_to_list(Xs, XsList),
        sum(XsList, NumDishes),
        minimize(search(XsList,0,first_fail,indomain_max,complete,[]),NumDishes),
        writeln([Total, Xs, NumDishes]).
Another approach when using arrays is to use for loops which is shown in the knapsack predicate below. Here for(I,1,Len) is used for incrementing the index counter, and then fromto does the actual summation. Note that fromto is very versatile and can be used for other things. See the documentation for more info.
% 
% Sum the totals: Total = Prices * Xs
%
knapsack(Prices, Total, Xs) :-
        dim(Prices, [Len]), % get the length of the array
        ( for(I, 1, Len), fromto(0, S1, S2, Total), param(Prices, Xs) do
              S2 #= S1+Prices[I]*Xs[I]
        ),
        labeling(Xs).


%
% simple version: Total = 1505
%
% This use the array representation
%
go :-
        Prices = [](215, 275, 335, 355, 420, 580), % Prices for the dishes
        Total = 1505,       % multiply 15.05 with 100 for integer usage
        dim(Prices, [Len]), % get length of Prices
        dim(Xs, [Len]),     % same length of Xs 
        Xs :: [0..10],      % domain of Xs
        findall(Xs, knapsack(Prices, Total, Xs), L),
        length(L, LLen),    % length of solutions
        writeln([total:Total, num_solutions:LLen, solutions:L]).


Some more comments about ECLiPSe

Here are some more comments of ECLiPSe
  • ECLiPSe is based on Prolog, which is a programming language that has fascinated me for a long time.
    struct (records) is another great extension, but I have just used it for a map coloring example map.ecl.
  • There are not very many built in global constraints in ECLiPSe (compared to SICStus Prolog) which is a pity. I have plans to implement decompositions for some global constraints from Global Constraint Catalog.
  • Almost all of the implemented model has used the ic library. I have yet to study (and use) the other libraries, eplex, suspend, propia, and repair which all seem very interesting.
  • there are many useful examples, almost all of them use loops and arrays. A wish: More examples of how to use the eplex library would be nice.
  • MiniZinc/FlatZinc: ECLiPSe has a great support for MiniZinc/FlatZinc, but unfortunately it - as of writing - don't support the recent version of MiniZinc (version 1.0).
  • tkECLiPSe: This is a GUI in TclTk, but I have not used it much (I'm a Emacs guy). There are some visualizations libraries that should be tested more.
  • ECLiPSe is a large system, see the Documentation for the full story.


My ECLiPSe models

I have implemented most of my "learning models" in ECLiPSe. Some are not yet done and will hopefully come later. Also, there are some older experiments in recreational mathematics. See My ECLiPSe Page.

As indicated above, most of these models use arrays and logical loops instead of the traditional Prolog recursion technique. I really like using these extensions since it is much easier to state the constraints, at least of me coming from MiniZinc, Comet and other non-logical programming.

June 24, 2009

My old swedish blog posts about constraint programming translated (via Google)

Before starting this blog (i.e. My Constraint Programming blog) late December 2008, I blogged about constraint programming in my Swedish blog hakank.blogg). Translations of those Swedish blog post has not been collected before, and now it is time.

So, here are links to most of the blog posts from the category Constraint Programming, translated via Google's Language Tools. Most of the translation is intelligible, but if you have some questions about some particular, please feel to mail me: hakank@bonetmail.com for more information.


November 4, 2005
Swedish: Sesemans matematiska klosterproblem samt lite Constraint Logic Programming
Translated: Sesemans kloster mathematical problems and little Constraint Logic Programming ("kloster" means "convent").


April 18, 2006
Swedish: Choco: Constraint Programming i Java
Translated: Choco: Constraint Programming in Java


The two post above was written when I had just a cursory interest in constraint programming. From about February 2008 and onward, it became my main interest.


April 5, 2008
Swedish: Constraint Programming: Minizinc, Gecode/flatzinc och ECLiPSe/minizinc,
Translated: Constraint Programming: Minizinc, Gecode / flatzinc and ECLIPSE / minizinc.


April 14, 2008
Swedish: MiniZinc-sida samt fler MiniZinc-exempel
Translated: MiniZinc-page and multi-MiniZinc example.


April 21, 2008
Swedish: Applikationer med constraint programming, lite om operations research samt nya MiniZinc-modeller
Translated: Applications of constraint programming, a little of operations research and new MiniZinc models


April 26, 2008
Swedish: Ett litet april-pyssel
Translated: A small-craft in April (a better translation would be "A small April puzzle")


April 27, 2008
Swedish: Mitt OpenOffice Calc-/Excel-skov
Translated: My Open Office Calc-/Excel-skov (better translation: My Open Office Calc-/Excel period).


May 26, 2008
Swedish: Några fler MiniZinc-modeller, t.ex. Smullyans Knights and Knaves-problem
Translated: Some more MiniZinc models, eg Smullyans Knights and Knaves-problem Smullyans Knights and Knaves problem


June 2, 2008
Swedish: MiniZinc/FlatZinc version 0.8 släppt
Translated: MiniZinc / FlatZinc version 0.8 released


June 2, 2008
Swedish: Nya MiniZinc-modeller, flera global constraints, däribland clique
Translated: New MiniZinc models, several global constraints, including Clique


June 5, 2008
Swedish: Mats Anderssons tävling kring fotbolls-EM 2008 - ett MiniZinc-genererat tips
Translated: Mats Andersson racing around championship in 2008 - a MiniZinc-generated tips (it is about a competition how to predict Soccer World Cup 2008 using MiniZinc)


June 24, 2008
Swedish: Tre matematiska / logiska pyssel med constraint programming-lösningar: n-puzzle, SETS, M12 (i MiniZinc)
Translated: Three mathematical / logical craft with constraint programming solutions: n-puzzle, SETS, M12 (in MiniZinc) ("craft" should be translated to "puzzles")


June 24, 2008
Swedish: MiniZinc-nyheter
Translated: MiniZinc news


June 29, 2008
Swedish: Gruppteoretisk lösning av M12 puzzle i GAP
Translated: Group Theoretical solution of the M12 puzzle in GAP (well, this is not really a constraint programming solution, but it is another way of solving the M12 puzzle blogged about in June 24)


June 30, 2008
Swedish: Gruppteoretisk lösning av M12 puzzle i GAP - take 2
Translated: Group Theoretical solution of the M12 puzzle in GAP - take 2


July 4, 2008
Swedish: Martin Chlond's Integer Programming Puzzles i MiniZinc
Translated: Martin's Chlond Integer Programming Puzzles in MiniZinc


July 7, 2008
Swedish: Fler MiniZinc modeller kring recreational mathematics
Translated: More MiniZinc models on recreational mathematics


July 20, 2008
Swedish: Fler constraint programming-modeller i MiniZinc, t.ex. Minesweeper och Game of Life
Translated: More constraint programming models in MiniZinc, eg Minesweeper och Game of Life Minesweeper and the Game of Life


August 17, 2008
Swedish: JaCoP - Java Constraint Programming solver
Translated: JaCoP - Java Constraint Programming solver


September 14, 2008
Swedish: Constraint programming i Java: Choco version 2 släppt - samt jämförelse med JaCoP
Translated: Constraint programming in Java: Choco version 2 released - and comparison with JaCoP


September 28, 2008
Swedish: Constraint programming: Fler MiniZinc-modeller, t.ex. Martin Gardner och nonogram
Translated: Constraint programming: More MiniZinc models, eg Martin Gardner och nonogram Martin Gardner and nonogram


December 27, 2008
Swedish: Constraint programming-nyheter samt nya MiniZinc-modeller
Translated: Constraint programming, news and new MiniZinc models


December 29, 2008
Swedish: My Constraint Programming Blog
Translated: My Constraint Programming Blog


After that, entries about constraint programming at my swedish blog posts where just summaries of the stuff written here at My Constraint Programming blog.

June 13, 2009

Miscellaneous news

Here are some miscellaneous news.

Choco

Version 2.1

I have forgot to mention that Choco have released version 2.1 . Download this version via the download page. The Sourceforge download page is here.

Changed models for version 2.1

I have changed some of my Choco models for version 2.1. The biggest change was the use of cumulative in FurnitureMoving.java where the call to the cumulative constraint have changed and now use TaskVariable.

I added the following
// Create TaskVariables
TaskVariable[] t = new TaskVariable[starts.length];
   for(int i = 0; i < starts.length; i++){
      t[i] = makeTaskVar("", starts[i], ends[i], durations[i]);
   }
and changed the call to cumulative to:
 
m.addConstraint(cumulative(null, t, constantArray(_heights), constant(NumPersons)));
Also, the line
durations[i] = makeConstantVar("durations" + i, durationsInts[i]);
was changed to
durations[i] = makeIntVar("durations" + i, durationsInts[i], durationsInts[i]);
For some of the other models (compiled for version 2.0.*), I just recompiled the source and it worked without any change in the code.

MiniZinc

List of global constraints

At the MiniZinc site, there is now a list of the supported global constraints in MiniZinc: MiniZinc: Global Constraints.

MiniZinc Library

MiniZinc Library is a great collections of problems, examples, and tests. (For some reason is not linked from the main MiniZinc site.)

Output in Minizinc

I wrote here about the change of output in MiniZinc version 1.0 . Only the distributed solver minizinc supports the output statement (for some kind of "pretty printing" of the output), but the solvers using the FlatZinc format has no such support. Since I want to see matrices presented in a normal way for all solvers, I wrote a simple Perl program to show matrices more pretty than just a single line. Also, single arrays are shown without array1d(...).

The Perl program is mzn_show.pl and it simply takes the result from a solver and reformats the result.

An example: The output from the debruijn_binary.mzn from a solver using FlatZinc file, such as Gecode/FlatZinc and MiniZinc's fdmip is:
bin_code = array1d(1..8, [0, 0, 0, 0, 1, 0, 1, 1]);
binary = array2d(1..8, 1..4, [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0]);
x = array1d(1..8, [0, 1, 2, 5, 11, 6, 12, 8]);
When filtering this with mzn_show.pl it will be shown as:
bin_code: 0 0 0 0 1 0 1 1
% binary = array2d(1..8, 1..4, [0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0]);
binary:
  0 0 0 0
  0 0 0 1
  0 0 1 0
  0 1 0 1
  1 0 1 1
  0 1 1 0
  1 1 0 0
  1 0 0 0
x: 0 1 2 5 11 6 12 8
Very simple, but quite useful.

Somewhat related: The NEWS file of the latest ROTD (Release of The Day) states the following:
FlatZinc output processing tool

We have added a new tool, solns2dzn, that can be used to process the output of FlatZinc implementations in various ways. For example, it can extract each individual solution and write it to a separate file.
This program is distributed as source (Mercury), not as an executable, so I have not been able to test it.

Hidato and exists

In the hidato.mzn model I have changed the exists construct
forall(k in 1..r*c-1) (
  exists(i in 1..r, j in 1..c) (
    k = x[i, j] % fix this k
    /\
    exists(a, b in  {-1, 0, 1} where
      i+a >= 1 /\ j+b >=  1 /\
      i+a <= r /\ j+b <= c
      /\ not(a = 0 /\ b = 0)
    )
     (
       % find the next k
       k + 1 = x[i+a, j+b]
     )
  )
)
to a a version using just var ints:
forall(k in 1..r*c-1) (
    let {
       var 1..r: i,
       var 1..c: j,
       var {-1,0,1}: a,
       var {-1,0,1}: b
    }
    in
    k = x[i, j] % fix this k
    /\
    i+a >= 1 /\ j+b >=  1 /\
    i+a <= r /\ j+b <= c
    /\ not(a = 0 /\ b = 0)
    /\
    % find the next k
    k + 1 = x[i+a, j+b]
)
This replacement of exists with int vars, if possible, seems to always be more effective.

However, there is one use of exists where it is harder to replace in this way. As an example, take Pandigital numbers in any base (the model includes a presentation of the problem) where - among other things - we want to find some array indices of the integer array x in order to find the positions of three numbers num1, num2 and res (the result of num1 * num2).
% num1. 
exists(j in 1..x_len) (
   j = len1 /\
   toNum([x[i] | i in 1..j], num1, base)
)

/\  % num2
exists(j, k in 1..x_len) (
   j = len1+1 /\ 
   k = len1+len2 /\ k > j  /\
   toNum([x[i] | i in j..k], num2, base)
)

/\ % the product
exists(k in 1..x_len) (
   k = len1+len2+1 /\
   toNum([x[i] | i in k..x_len], res, base)
)
Using this approach, we have to use exists since indices in a range (e.g. j in 1..j) must not be an int var.

Also

Both Choco and MiniZinc sites now have links to my site, which is quite fun:
* Choco, Users (last)
* MiniZinc and FlatZinc (also last)

Work related page in english

The page Håkan Kjellerstrand, CV and work related interests is a english version of my work related page. (The original, swedish, version is here.)

June 03, 2009

Scheduling with the cumulatives constraint in Gecode

One of the few problems of my learning problems that has not been modeled in Gecode before was the Furniture moving problem (from Marriott & Stuckey "Programming with Constraints", page 112f). The reason has been that Gecode don't have the common cumulative constraint, but instead the generalized and extended cumulatives constraint (note the last "s"). This cumulatives constraint was defined and explained in the paper A new multi-resource cumulatives constraint with negative heights (ps.Z) by Nicolas Beldiceanu and Mats Carlsson.

Furniture moving

The last couple of days I have read that paper and experimented with the cumulatives constraint in modeling the Furniture moving problem. The Gecode program is here: furniture_moving.cpp.

Some notes
The cumulatives paper shows 8 examples/variants of usuage (A..H, which are explained below) and the Furniture moving problem is modeled after the G variant, a covering model of producers/consumers. The main approach is that we have the four tasks (consumers, the furnitures to move) and three movers (producers). The height of the consumers is coded as negative, and the movers as height 1 (positive), i.e.
int _durations[] = {   30,    10,  15,   15                };
int _height[] =    {   -2,    -1,  -2,   -2,      1,  1,  1};
int _limit[] =     {0}; // just one machine
Just for fun, I also let the durations of the movers "free" to see how long they have to work according to this model. The result was that the movers 2 and 3 have to work full time (60 minutes), but the first mover has to work just 10 minutes.

8 Cumulatives examples

In the paper cited above, Beldiceanu and Carlsson showed 8 different variants (A to H) of how to model with the cumulatives constraint. I have tried to implement all these examples in Gecode as faithful as possible.

The Gecode code is cumulatives_examples.cpp and is fairly general with a short explanations (cited from the paper) for each variants. For selecting a specific problem instance it takes a command line parameter where 0 is example A to parameter 7 for example H.

Below are some comments for each example, and as said before there are more comments in the Gecode code. Please note that Gecode is 0-based so we start numbering tasks and machines from 0 and onward.

Example A

This is the "plain old" cumulative with one machine.

Example B

cumulative with two machines.

Example C

Note that the atmost parameter of cumulatives is set to false since we have an at least constraint. Also a dummy task (0) are used here.

Example D

Same as the above, except for the "dummy" task. Also, I have added an another cumulatives constraint with atmost=true so all tasks are not "stacked" above each other.

Example E

Producer/consumer model. This one was quite tricky to understand first, but after reading the description some times I realized it's beauty: all producers start at time 0 and and produce whatever they produce, and all consumers ends at the "last date" (here time 6) and consumes whatever they consumes.

Example F

Generalization of example E with two machines.

Example G

A "covering problem", also elegant stated with cumulatives. Here there are two tasks (consumers, task 0 and 1) which have negative height, and 4 persons (producers) with the positive height 1. The atmost parameter must be false.

It is this variant that the above mentioned Furniture moving problem was modeled after.

Example H

A generalization of Example G with two machines. Here I have hard coded which machine a task/person should belong since otherwise only task 6 would be at machine 1. Also, I added an atmost limit for each machine for esthetic reasons.

Another scheduling example: Building a house

This example is a standard example from OPL: how to build a house with some dependencies of different tasks, e.g. that masonry must be finished before carpentry, painting must be finished before moving in etc.

This problem was done in Gecode with cumulatives (as variant A, see above) and was very straightforward to implements, both the cumulatives and the dependencies graph. There code is here: building_a_house.cpp.

See also: Some scheduling models in Comet

Comet has a great support for scheduling with a special Schedule framework, much like OPL's. Here are my Comet models that use this Schedule framework. The two first models are standard OPL examples. Also, the Comet model furniture_moving_cumulative.co is a variant of the Furniture moving problem, using a cumulative constraint implemented as a decomposition.