e-constraints.net
the home of Explanation-based Constraint Programming
Search

 Introduction   Implementations   Applications   Pointers 
e-constraints.net : Implementations : PaLM : Manual : Tutorial : First steps

First steps with PaLM

PaLM is an explanation-based constraint programming (e-constraints) language. It is a constraint programming tool that makes heavy use of explanations. Informally, an explanation is a set of constraints (either original constraint or decisions constraints) that justifies a solver action (a value removal, a bound update, a contradiction, etc.). More detailed information about explanations is available on the introduction page.

We suppose here that the user is fluent in choco, the base language of PaLM. Moreover, the user should have successfully installed and configured PaLM.

The examples shown here are available in the doc/tutorial-1.cl file in the PaLM release directory.

Overview


A first program

PaLM has been designed to let any choco user switch to PaLM features by changing only a few things in its code. Let's take a simple example:

Example 1. A simple choco program
[classical-choco-program()
 -> let pb := choco/makeProblem("demo constraint program", 5), // a problem
        a  := choco/makeBoundIntVar(pb, "a", 1, 3),            // a few variables
        b  := choco/makeBoundIntVar(pb, "b", 1, 3),
        c  := choco/makeBoundIntVar(pb, "c", 1, 3)
    in (
        printf("Variables: ~S\n", list(a,b,c)),  printf("posting a > b \n"),
        post(pb, a > b), propagate(pb),

        printf("Variables: ~S\n", list(a,b,c)),  printf("posting a > c \n"),
        post(pb, a > c), propagate(pb),

        printf("Variables: ~S\n", list(a,b,c)),  printf("posting b > c \n"),
        post(pb, b > c), propagate(pb),

        printf("Variables: ~S\n", list(a,b,c))
    )]

Loading and interpreting it into a classical choco interpreter (you can even use the PaLM module for that), the following result is obtained (see example 2). As expected, one can see the various propagation results after posting each constraint.

Example 2. A simple choco program (result)
palm> classical-choco-program()
  Variables: (a:[1.3], b:[1.3], c:[1.3])
  posting a > b
  Variables: (a:[2.3], b:[1.2], c:[1.3])
  posting a > c
  Variables: (a:[2.3], b:[1.2], c:[1.2])
  posting b > c
  Variables: (a:3, b:2, c:1)
  nil
palm>

 Lesson 1  PaLM can be used as a choco interpreter

Switching to PaLM is as simple as defining a PalmProbleminstead of a Problem. Example 3 shows the resulting PaLM code from example 1. As expected, the result (see example 4) is exactly the same except for the pretty printing of variables which is specific to PaLM.

Example 3. A simple PaLM program
[classical-palm-program()
 -> let pb := makePalmProblem("demo constraint program", 5), // a PaLM problem !!!
        a  := makeBoundIntVar(pb, "a", 1, 3),            // a few variables
        b  := makeBoundIntVar(pb, "b", 1, 3),
        c  := makeBoundIntVar(pb, "c", 1, 3)
    in (
        printf("Variables: ~S\n", list(a,b,c)),  printf("posting a > b \n"),
        post(pb, a > b), propagate(pb),

        printf("Variables: ~S\n", list(a,b,c)),  printf("posting a > c \n"),
        post(pb, a > c), propagate(pb),

        printf("Variables: ~S\n", list(a,b,c)),  printf("posting b > c \n"),
        post(pb, b > c), propagate(pb),

        printf("Variables: ~S\n", list(a,b,c))
    )]

Example 4. A simple PaLM program (result)
palm> classical-palm-program()
  Variables: (a:[1..3], b:[1..3], c:[1..3])
  posting a > b
  Variables: (a:[2..3], b:[1..2], c:[1..3])
  posting a > c
  Variables: (a:[2..3], b:[1..2], c:[1..2])
  posting b > c
  Variables: (a:3, b:2, c:1)
  nil
palm>

 Lesson 2  Switching from choco to PaLM just needs switching from a makeProblem to a makePalmProblem

Of course, life is not always that simple. You will find on the reference manual page a complete choco/PaLM compatibility list (some constraints need a new API to work with PaLM). You can also have a look to the complete reference online guide to PaLM.


Getting information about propagation: explanations

The main advantage of explanation-based constraint programming systems such as PaLM is of course the notion of explanation (implemented as the Explanation class). Accessing explanations in PaLM is quite simple: just use the explain method.

Let us first introduce a small example problem that will be used throughout the demonstration of the explain method.

Example 5. A toy example for explain
palm> pb :: makePalmProblem("demo explain", 3)
palm> a :: makeIntVar(pb, "a", 1, 3)
palm> b :: makeIntVar(pb, "b", 1, 3)
palm> c :: makeIntVar(pb, "c", 1, 3)
palm> post(pb, b > c)
palm> post(pb, a > b)
palm> propagate(pb) 
palm> printf("~S ~S ~S\n",a,b,c)
eval> a:3 b:2 c:1

This example shows a classical possible interaction in any constraint solver. As we can see, all variables are instantiated. But, if we want to known exactly why each variable as a value, there is no possible way in classical solvers. However, PaLM provides the explain method.

The explain method can be called in the following ways (see also the reference guide):

  • explain(v: PalmIntVar, s: {INF, SUP, DOM})returns an Explanation (the PaLM class for storing explanations) for the current lower bound (INF), upper bound (SUP) or domain (DOM) of variable v.

    In example 5, we can analyse the current domain of the different variables. For example, calling explain(b, DOM) shows that the current domain of variable b is due to the set {b >= c + 1, a >= b + 1}. More precisely, we can see that the current lower bound of b is due to explain(b, INF) = {b >= c + 1} and the current upper bound is due to explain(b, SUP) = {a >= b + 1}.

    explain can even provide more precise information: why does not value 3 appear in the current domain of variable c ? To answer that you can just check explain(c, VAL, 3) = {b >= c + 1}.

  • explain(c: AbstractConstraint) returns an Explanation for the current activity of constraint c.

    Usually, the Explanation is reduced to c itself. But, PaLM provides the notion of indirect constraints i.e. constraints that are active only if a given context is active at the same time. In that case, explain(c: AbstractConstraint) returns that context (see Manual note dedicated to indirect constraints).

  • explain(pb: PalmProblem) returns an Explanation of the inconsistency of the argument problem. Example 6 shows an inconsistent problem. See in example 7 the information that can be gathered by PaLM.

    Example 6. An inconsistent PaLM problem
    [inconsistent-problem() 
     -> let pb := makePalmProblem("demo explain", 5),
    	a  := makeBoundIntVar(pb, "a", 1, 3),
    	b  := makeBoundIntVar(pb, "b", 1, 3),
    	c  := makeBoundIntVar(pb, "c", 1, 3),
    	d  := makeBoundIntVar(pb, "d", 1, 3),
    	e  := makeBoundIntVar(pb, "e", 1, 3)
        in (
    	printf("\nVariables: ~S\n", list(a,b,c,d,e)),
    
    	post(pb, a > c), propagate(pb),
    	post(pb, b > d), propagate(pb),
    	post(pb, c > e), propagate(pb),
    	post(pb, d > e), propagate(pb),
    
    	printf("Variables: ~S\n", list(a,b,c,d,e)),
    
    	post(pb, e > a), // The offending constraint
    	propagate(pb)
        )]
    

    Example 7. An inconsistent PaLM problem (result)
    palm> inconsistent-problem()
    eval[0]>
    Variables: (a:[1..3], b:[1..3], c:[1..3], d:[1..3], e:[1..3])
    Variables: (a:3, b:3, c:2, d:2, e:1)
    <integer>    // this is symptomatic of a contradiction
     
    palm> explain(choco/getActiveProblem()) // choco/getActiveProblem() returns the current problem being solved
    eval[1]> {a >= c + 1, c >= e + 1, e >= a + 1}
    palm>
    

    Notice that the provided explanation is really focused on the cause of the problem and does not take into account constraints on variables b and d.

 Lesson 3  explain is the key to get information about propagation in PaLM.

Constraints in explanations are expressed using the internal choco/PaLM representation and not the user definition. This can lead to pretty unreadable explanations. PaLM can provide user-friendly explanations [Jussien and Ouis, 2001] (see Manual note dedicated to them for more information).


Using dynamic capabilities of PaLM

PaLM is also a dynamic constraint solver. Constraints can be added (using the post method) either before, during or after search is performed. In the latter case, the search can be re-started from the reached point.

On another interesting feature of PaLM is that constraints can alse be incrementally removed from the constraint system. We use the remove method (you can have a look to the online reference guide).

remove(pb: PalmProblem, ct: AbstractConstraint) removes all the past effects of constraint ct. Some of these past effects can be proved another way and therefore a call to propagate is necessary to get back to a consistent state.

Example 8. A toy example for remove
palm> pb :: makePalmProblem("demo remove", 3)
palm> a :: makeBoundIntVar(pb, "a", 1, 3)
palm> b :: makeBoundIntVar(pb, "b", 1, 3)
palm> c :: makeBoundIntVar(pb, "c", 1, 3)
palm> ct :: (b > c)  // defining ct as a constraint
palm> post(pb, ct)
palm> post(pb, a > b)
palm> propagate(pb) 
palm> printf("~S ~S ~S\n",a,b,c)
eval> a:3 b:2 c:1
palm> remove(pb, ct)
palm> printf("~S ~S ~S\n",a,b,c)
eval> a:[1..3] b:[1..2] c:[1..3]
palm> propagate(pb)
palm> printf("~S ~S ~S\n",a,b,c)
eval> a:[2..3] b:[1..2] c:[1..3]

The remove methods leads to the same resulting domains that if the constraints was never added (this is not necessary the case for the resulting explanations).

Example 9. What if the constraint was not added
palm> pb :: makePalmProblem("demo remove", 3)
palm> a :: makeBoundIntVar(pb, "a", 1, 3)
palm> b :: makeBoundIntVar(pb, "b", 1, 3)
palm> c :: makeBoundIntVar(pb, "c", 1, 3)
palm> post(pb, a > b)
palm> propagate(pb) 
palm> printf("~S ~S ~S\n",a,b,c)
eval> a:[2..3] b:[1..2] c:[1..3]

 Lesson 4  remove is the PaLM efficient way of undoing constraints. It has an incremental behavior.


Solving over-constrained problems

Being able to identify explanations for contradiction and having access to incremental tools for removing constraints leads to the automatic resolution of over-constrained problems. PaLM provides a few tools for that:

  • the post method can be used with a third parameter giving a user preference for the posted constraint (the lower the value, the weaker the importance of the constraint - see the online reference guide) along the with the ability to specify a maximum relaxation level when declaring a problem with makePalmProblem.

  • the exception handling mechanisms of claire can be used to catch and handle contradiction during propagation or search. PaLM provides the PalmContradiction exception class denoting contradictions occuring during propagation.

  • the repair(pb: PalmProblem) method takes an over-constrained problem, computes an explanation for the contradiction, selects constraints to relax (taking into account the user preferences - as in possibilistic CSP [Bistarelli et al., 1996]), and removes them. The repair code is recursively called until a feasible situation is achieved. (repair is described in the online reference guide)

Example 10 shows some short PaLM code that shows how to handle over-constrained problems. Example 11 shows the resulting execution.

Example 10. Handling over-constrained problems

[over-constrained-problem()
 -> let pb := makePalmProblem("demo over-constrained system", 3, 5), 
                                    // maximum relaxation level: 5
        a  := makeBoundIntVar(pb, "a", 1, 3),
        b  := makeBoundIntVar(pb, "b", 1, 3),
        c  := makeBoundIntVar(pb, "c", 1, 3),
	ct1 := (a > b),
	ct2 := (b > c),
	ct3 := (c > a)
    in (
        try ( // using claire exception handling mechanisms
            printf("Variables: ~S\n", list(a,b,c)),

            printf("posting ~S \n", ct1),
            post(pb, ct1, 2), propagate(pb), // ct1 is not that important (weight 2)
            printf("Variables: ~S\n", list(a,b,c)),

            printf("posting ~S \n", ct2),
            post(pb, ct2, 5), propagate(pb), // ct2 is really important (weight 5)
            printf("Variables: ~S\n", list(a,b,c)),

            printf("posting ~S \n", ct3),
            post(pb, ct3, 3), propagate(pb), // ct1 is quite important (weight 3)
            printf("Variables: ~S\n", list(a,b,c))
        )
        catch PalmContradiction ( // if a contradiction occurs
            repair(pb),
            printf("Variables: ~S\n", list(a,b,c))
        )
    )]

Example 11. Handling over-constrained problems (result)
palm> over-constrained-problem()
eval[0]> 
  Variables: (a:[1..3], b:[1..3], c:[1..3])
  posting a >= b + 1
  Variables: (a:[2..3], b:[1..2], c:[1..3])
  posting b >= c + 1
  Variables: (a:3, b:2, c:1)
  posting c >= a + 1
  // here a contradiction occurred : repair is called
  PALM: Removing constraint a >= b + 1 (w:2) 
  // the least important relevant constraints has been chosen for relaxation
  Variables: (a:1, b:3, c:2)
  nil
palm>

The tutorial note dedicated to search can help you in designing your own over-constrained problem solver.


Bibliography
  • [Bistarelli et al., 1996] Stefano Bistarelli, Hélène Fargier, Ugo Montanari, Francesca Rossi, Thomas Schiex, and Gérard Verfaillie. Semiring-based csps and valued csps: basic properties and comparison. In Michael Jampel, Eugene Freuder, and Michael Maher, editors, Over-Constrained Systems, number 1106 in Lecture Notes in Computer Science, pages 111-150. Springer Verlag, 1996.

- Last modified: Wed Nov 27 13:36:01 Paris, Madrid 2002 by Webmaster