Constraint Logic Programming - Status and Prospects 1 ... - CiteSeerX

1 downloads 0 Views 164KB Size Report
agent enforces the constraint that X + Y = Z. The precise syntax used to ... immediate behaviour of the agent is to refine the stored information: now X and Y lie ...
Constraint Logic Programming - Status and Prospects Mark Wallace IC-Parc, William Penney Laboratory, Imperial College, London SW7 2AZ email: [email protected] Constraint logic programming is a new programming paradigm which combines declarative programming with high level control and ecient built-in algorithms. Constraints contribute to program correctness, program clarity and runtime eciency. There are two main kinds of constraints: primitive constraints, which are held in the constraint store, and constraint agents, which react to changes in the store by adding to it further primitive constraints. We brie y describe di erent classes of primitive constraints, and the constraints agents which manipulate them. We describe three ways of constructing constraint agents, and we show how agents constructed in these ways behave cooperatively in producing new information. Finally we brie y introduce some practical applications of constraints.

Keywords: Constraints, Logic programming, Agents, Propagation, Cooperation

1 Introduction Constraints make possible a new programming paradigm. Instead of a computation progressing at each step by changing the stored information (transforming the computing store), progress is made by adding more precise information (re ning the store). Moreover the computation is driven, not by traditional control constructs such as sequencing, iteration and conditionals, but by the arrival of data. Each statement introduces an agent which reacts to re nements of the data. In this programming paradigm the behaviour of an agent is to enforce a constraint. We illustrate these concepts with a simple example. Consider a data store containing information about three integer variables X; Y and Z . Initially all that is known about them is that their values lie between 1 and 100. The statement X+Y #= Z, is now used to introduce an agent. This agent enforces the constraint that X + Y = Z . The precise syntax used to express the equality, #=, is important in specifying the agent's behaviour, as will be detailed in section 3.2 below. The immediate behaviour of the agent is to re ne the stored information: now X and Y lie between 1 and 99 and Z lies between 2 and 100. However the agent's work is not yet completed, because further re nements to X; Y or Z , due to the behaviour of other agents, may enable it to re ne the information about X; Y and Z still further, as illustrated in gure 1. 1

Data Statement Re nement X 2 f1; : : : 100g X+Y#=Z X 2 f1; : : : ; 99g Y 2 f1; : : : 100g Y 2 f1; : : : ; 99g Z 2 f1; : : : 100g Z 2 f2; : : : ; 100g X 2 f1; : : : ; 99g X+Y#=Z X 2 f1; : : : ; 98g Y 2 f2; 4; 6g Y 2 f2; 4; 6g Z 2 f2; : : : ; 100g Z 2 f3; : : : ; 100g X 2 f1; : : : ; 98g X+Y#=Z X 2 f1; 3g Y 2 f2; 4; 6g Y 2 f2; 4g Z 2 f3; 5g Z 2 f3; 5g Figure 1: Multiple Re nements Produced by a Single Agent As this brief introduction has illustrated, there are two kinds of constraints. Primitive constraints, such as X 2 f1; 3g are stored as data. The set of stored primitive constraints is often termed the \constraint store". The other kind of constraints are constraint agents, such as X+Y #= Z, which act on the store. Their behaviour is to add new, tighter, primitive constraints to the store. However this very behaviour is driven by the arrival of primitive constraints, inserted by other constraint agents. This constraint-driven behaviour is a generalisation of data-driven control, which was earlier used in parallel programming languages. In the following section we will motivate constraint programming, in section 3 we will formalise it, and in section 4 we will study some applications.

2 Motivation

2.1 Correctness, Clarity and Eciency

The constraint programming paradigm brings three fundamental bene ts: program correctness, program clarity and program eciency. These bene ts support all three aims of software engineering. A decade of experience has shown that approaches which emphasise correctness, such as formal speci cation languages, have not been accepted in practise because they are too demanding of the program designer. However approaches that emphasise clarity, such as Knowledge Engineering systems, fail to break through into the mass market because they are too inecient. Naturally there is no problem of eciency if the system is encoded in low-level language, like 'C' and its derivatives, but program correctness and maintenance then come back to haunt project managers.

Constraints for Correctness Constraint logic programming (CLP) is a runnable speci cation

language, just as logic programming is. A major step forward in CLP is that there is less temptation to mix speci cation with optimisation which plagues logic programming. This is because CLP allows a \naive" program - the speci cation - to be annotated with extra control features that support optimisation. In other words the features of the language which support speci cation are distinct from the features which support optimisation.

Constraints for Clarity CLP is a high-level programming language which supports symbolic

computation, meta-programming and a relational form. These features enable CLP to integrate well into information systems built on non- rst-normal-form extensions of the relational model (often termed \object" models). Whilst logic programming is traditionally executed in a top-down fashion, CLP allows forward and backward reasoning as well as demons and guarded rules, all of which enable the system to naturally capture requirements from querying to simulation, integrity constraint enforcement to demon-driven graphical front ends. The advantage of CLP is that these possibilities are not o ered in a monolithic package: it is still possible to write simple speci cations with simple control annotations that can be run eciently.

Constraints for Eciency CLP is an ecient implementation language, supporting specialised

algorithms tailored to solve speci c classes of constraints highly eciently. The availability of these built-in algorithms means that the user can de ne the program behaviour at a very high level: by annotating a constraint as belonging to a certain class, the e ect is that the constraint is handled by the appropriate algorithm. Except for developers of new specialised constraint solvers, there is no need for end users to drop into a low-level language to gain eciency. At the risk of trivialising the contribution of this paradigm, one could say that CLP provides the glue that enables programmers to stick together specialised algorithms into a single coherent program. As will hopefully become clear from this paper, the CLP framework suuports a close cooperation between the algorithms in a way that is not supported, for example, when a set of packages are glued together by the underlying operating system. Constraints programming has been successfully applied for solving complex industrial problems in areas such as planning, transportation, scheduling and resource allocation: but applications are the subject of the third section.

2.2 Constraints Programming Example

As a toy example let us write a program to colour a map so that no two neighbouring countries have the same colour. A generic logic program, in Prolog syntax, that tries nd possible ways of

a c d

b

Figure 2: A Simple Map to Colour colouring this map with only three colours (red, green and blue) is in gure 3. In this program the unde ned predicate ne constrains its arguments to take di erent values. We will show how di erent de nitions of ne cause the program to behave in di erent ways. The predicate chosen is de ned by three facts: chosen(red)., chosen(green)., chosen(blue). At runtime the system tries each de nition in turn. If a failure occurs later in the computation, then the alternative de nitions are tried in a last-in rst-out basis.

coloured(a(A),b(B),c(C),d(D)) :ne(A,B), ne(A,C), ne(A,D), ne(B,C), ne(B,D), ne(C,D), chosen(A), chosen(B), chosen(C), chosen(D). chosen(red). chosen(green). chosen(blue).

Figure 3: A Generic Logic Program for Map Colouring The rst de nition of ne uses the original disequality of Prolog: ne(X,Y) :- X\=Y. If invoked when either of its arguments are uninstantiated variables X\=Y simply fails. To avoid this incorrect behaviour it is possible to place the constraints after all the choices. In this case the program correctly detects that there is no possible colouring after checking all 81 alternatives. A more ecient Prolog program can be written by interleaving choices and constraints, but the same e ect can be achieved by using the above program with a new de nition: ne(X,Y) :- X~=Y. This disequality predicate delays until both arguments have a xed value. It then immediately wakes up and fails if both values are the same. If the values are di erent it succeeds. This program detects that our map cannot be coloured with three colours after trying only 33 alternatives. Another disequality constraint is available in CLP, which assumes its arguments have a nite set of possible values. We can use it by de ning ne(X,Y) :- X##Y. This disequality delays until one of its arguments has a xed value. This value is then removed from the set of possible values for the other. To obtain the advantage of X##Y it is necessary to declare the set of possible values for each variable, yielding the following program. This program detects that the map cannot be coloured(a(A),b(B),c(C),d(D)) :[A,B,C,D]::[red,green,blue], ne(A,B), ne(A,C), ne(A,D), ne(B,C), ne(B,D), ne(C,D), chosen(A), chosen(B), chosen(C), chosen(D). ne(X,Y) :- X##Y. chosen(red).

chosen(green).

chosen(blue).

Figure 4: A Finite Domain CLP Program for Map Colouring coloured after trying only 6 alternatives. Although this example is so trivial that it is quite simple to solve it in Prolog, the CLP program scales up to complex maps and graphs in a way that is impossible using a programming language without constraints.

3 Formalisation Constraints programming is the embedding of constraint handling within an existing \host" programming language. Firstly the computer store normally associated with the host programming language is replaced by a constraint store. Secondly constraint agents are introduced, invoked by new statements in the extended language. The data types of the host language can naturally be incorporated into the constraint store and respected by constraint agents. A harder problem is accommodate the control structures of the host language to the constraint handling. In the case of a procedural host language, such as 'C', this means introducing a specialised control regime in those parts of the programme which handle constraints. When the host programming language is logic programming, extensions to the constraint handling have been implemented to accommodate backtracking. In the remainder of this section we con ne ourselves to the constraint handling part.

3.1 Primitive Constraints

Primitive constraints are the constraints that can be held in the constraint store. The simplest constraint store is the ordinary single-assignment store used in functional programming. In our terms it is a constraint store in which all constraint have the form V ariable = V alue. The rst generalisation is the introduction of the logical variable. This allows information of the form V ariable = Term to be stored, where Term is any term in rst-order logic. For example it is possible to store X = f (Y ). However there is a problem about consistency. What happens if the following are stored: X + 1 = Y and Y + 1 = X ? In logic programming this problem is nessed by treating functions simply as \constructors"and detecting inconsistencies using substitutions and uni cation. If + is a constructor, then 1 + 1 = 2 is false. Moreover X + 1 = 2 + Y if, and only if, X = 2 and Y = 1. The extension of logic programming to store equations involving mathematical functions was an important breakthrough. The problem of inconsistency is now handled by a specialised solver. In fact not only (linear) equations but also inequations can be checked for consistency by standard mathematical techniques. Naively it is necessary, each time a new constraint was added to the store, to apply the solver to the whole constraint store to detect any inconsistencies. In practice it is often possible to avoid this overhead [JMSY92]. One seemingly trivial class of primitive constraints were identi ed: domain constraints. Domain constraints are constraints that only apply to a single variable. They are obviously very ecient to process since the consistency of the whole constraint store follows from the separate consistency of the domain constraints on each variable. Thus when a new domain constraint is added it is only necessary to test its consistency with the other domain constraints on that variable. Finally an obvious optimisation is to maintain a normal form representing the conjunction of the domain constraints on each variable. Except for keeping information in case of backtracking, there is now no need to further store the original domain constraints. For combinatorial problems it is necessary to handle discrete variables: variables that can only take a nite number of possible values. The rst CLP system to utilise nite domain constraints as primitive constraints was the CHIP system [DvS+88]. Examples in this paper will use the syntax of the ECLiPSe CLP language [Me95], which is a descendant of CHIP. In ECLiPSe the statement X::[1,4,8] introduces a nite domain constraint on the variable X . This nite domain is held in the constraint store and becomes re ned as values from this domain are ruled out by further

constraints. In any particular constraint store, only primitive constraints of one class are stored. For example only nite domain constraints might be stored, or only linear equations. However in a single CLP system there might be several constraint stores holding di erent classes of primitive constraints. In many CLP systems, it is not possible for variables appearing in one constraint store also to appear in another store. In principle, however, it is perfectly possible for a variable to appear in two di erent constraint stores. In this case a new primitive constraint on the variable in one store causes a new constraint agent to be spawned acting on the variable in the other store.

3.2 Constraint Agents

Constraint agents are processes that involve a xed set of variables. During their lifetime they alternate between sleeping and waking states. They are woken from their sleep when an extra primitive constraint on one or more of their variables is recorded. Sometimes, after checking certain conditions, the woken agent simply goes back to sleep again. Otherwise the agent really wakes up and exhibits a certain behaviour which may result in new agents being spawned, new primitive constraints being added to the store, or an inconsistency being detected (which is equivalent to an inconsistent constraint being added to the store). The simplest constraint agent is one which adds a primitive constraint to the constraint store and then exits. The most fundamental example is assigning a value to a variable, eg. X=3. This agent adds X = 3 to the constraint store and exits. The next two examples are the disequality constraints illustrated in the previous section. The rst disequality constraint is invoked by the syntax X~=Y. This agent does not do anything until both X and Y have a xed value. Only when the primitive constraints in the store entail X = valx and Y = valy for some unique values valx and valy , does the agent wake up. Then its behaviour is to check that valx is di erent from valy . In case they are the same, an inconsistency has been detected. If the constraint store holds nite domain constraints, then the more powerful constraint agent invoked by the syntax X ## Y can be used. This agent wakes up as soon as either X or Y has a xed value. It then removes this value from the nite domain of the other variable and exits. The introduction gave a more complex example of a constraint agent X+Y #= Z acting on a constraint store containing nite domain constraints. The behaviour of this agent, in the ECLiPSe system, is to nd the highest and lowest values for each variable, X , Y and Z for which there are values in the domains of the other variables which satisfy the equation. All values larger than the highest, and smaller than the lowest, are then pruned from the domain of the variable. This agent does not exit until all but one of its variables have a xed value. Until then it wakes up whenever the nite domain of any of its variables are tightened. Thus it performs further pruning repeatedly during the program execution. These constraint agents are all built into the ECLiPSE system. However it is also possible for CLP programmers to build new agents. We describe three ways in which this can be done.

3.3 Constructing Constraint Agents

Guarded Rules Consider the constraint and(X; Y; Z ), involving three boolean variables X; Y

and Z , which states that Z is the conjunction of X and Y . We shall present in gure 5 a set

of guarded rules which de ne a constraint agent acting on a store whose primitive constraints are equations of the form V ar = value or V ar1 = V ar2. and(X,Y,Z) and(X,Y,Z) and(X,Y,Z) and(X,Y,Z) and(X,Y,Z) and(X,Y,Z)

Suggest Documents