constraint logic programming: applications and ...

9 downloads 0 Views 1MB Size Report
Uytterhoeven, Tom Michiels, for celebrating our birthday, Frank Piessens, Eddy Bevers, ...... kolommatrix met de variabelen. b een kolom matrix met constanten.
s

KATHOLIEKE UNIVERSITEIT LEUVEN FACULTEIT WETENSCHAPPEN FACULTEIT TOEGEPASTE WETENSCHAPPEN DEPARTEMENT COMPUTERWETENSCHAPPEN Celestijnenlaan 200A – B-3001 Heverlee

CONSTRAINT LOGIC PROGRAMMING: APPLICATIONS AND IMPLEMENTATION

Proefschrift voorgedragen tot het behalen van het doctoraat in de informatica

Promotoren: Prof. dr. Danny De Schreye Prof. dr. ir. Maurice Bruynooghe

door Henk VANDECASTEELE

mei 1999

s

KATHOLIEKE UNIVERSITEIT LEUVEN FACULTEIT WETENSCHAPPEN FACULTEIT TOEGEPASTE WETENSCHAPPEN DEPARTEMENT COMPUTERWETENSCHAPPEN Celestijnenlaan 200A – B-3001 Heverlee

CONSTRAINT LOGIC PROGRAMMING: APPLICATIONS AND IMPLEMENTATION

Jury: Prof. dr. ir. Karel De Vlaminck, voorzitter Prof. dr. Danny De Schreye, promotor Prof. dr. ir. Maurice Bruynooghe, promotor Prof. dr. Yves Deville(UCL) Prof. dr. ir. Georges Gielen(ESAT). Prof. dr. Bart Demoen

mei 1999

Proefschrift voorgedragen tot het behalen van het doctoraat in de informatica door Henk VANDECASTEELE

c Katholieke Universiteit Leuven - Faculteit Toegepaste Wetenschappen Arenbergkasteel, B-3001 Heverlee, Belgium

Alle rechten voorbehouden. Niets uit deze uitgave mag worden vermenigvuldigd en/of openbaar gemaakt worden door middel van druk, fotocopie, microfilm, elektronisch of op welke andere wijze ook zonder voorafgaande schriftelijke toestemming van de uitgever.

All rights reserved. No part of the publication may be reproduced in any form by print, photoprint, microfilm or any other means without written permission from the publisher.

Constraint Logic Programming: Applications and Implementation Henk Vandecasteele Department of Computer Science, K.U.Leuven Celestijnenlaan 200A, B-3001 Heverlee, Belgium

Abstract Logic Programming was born with the idea to attach a procedural behaviour to a logical description, such that declarative specifications become executable. This should help the user to concentrate on the correct specification of the problem instead of bothering about implementation details. Constraint Logic Programming is a branch of Logic Programming designed for tackling combinatorial problems. These problems can be found mainly within scheduling and planning. Examples are rostering, knapsack-problems, ... . Constraint Logic Programming allows to specify these problems at a very high level, a solver incorporated in the CLP-engine takes care of an efficient execution. The first part of the thesis explains in detail what Constraint Logic Programming is about and what mechanisms make the system work. The description covers different variants of Constraint Logic Programming: Finite Domain Constraint Logic Programming, CLP based on linear programming and CLP based on intervals. A second part of the thesis concentrates on applications of Constraint Logic programming. A first application concerns planning of maintenances in power plants. Several alternatives were investigated to solve the problem. The second application has a more academic nature: try to proof that a query can never succeed, given a logic theory, without executing the program. This can be useful for certain programs like planners that use a generate and test solving strategy. By detecting that no solution exist, loops can be avoided. The third application is related to the previous, in the sense that termination should be proven for a program, for a given set of queries. Here constraint technology can be used to have a clean general mechanism for constructing proofs where choices on details of the proof can be delayed. Computing an optimal portfolio given the profile of the investor and distribute master students over different research groups were other applications tackled with CLP. The third part of the thesis reports on implementing a Finite Domain Constraint Logic Programming language using logic programming systems like PROLOG and Mercury.

Preface This manuscript is the result of many years of research in the field of Constraint Logic Programming. I guess interest in research on Constraint Logic Programming at our group started already in the early days of Logic Programming as reflected in the work of Maurice Bruynooghe [Bru81] on solving combinatorial problems. There have been a number of quiet periods around this topic, but interest remained. This can be seen from the work of Willem Rosier [RB86] and Maurice Bruynooghe [Bru86], work from D. De Schreye, D. Pollet, J. Ronsyn and M. Bruynooghe [DPRB90] and the work of Kristof Gheysen and Ignace Opsommer [GO90]. At the starting point of my research in the group at the department under the supervision of Danny de Schreye, the group was involved in the ESPRIT-project PRINCE on Constraint Logic Programming. This project contained a work-package on Finite Domain Constraint Logic programming with commitments by Leuven, as a reflection of the interest of the group in CLP. From the beginning I was very pleased to be able to work on that topic. From then on I got involved into several subtopics in CLP. The work started with some prototype implementation of a CLP(Finite Domain) solver on top of Prolog [VD94]. During the years we also got involved into a wide range of real-life applications, mainly on scheduling. Topics were: scheduling working hours for the personnel in a library [Bor95], finding a schedule for maintaining power units [SB96], routing trucks trough a net of roads [Gys94], optimise a set of securities [Van97], settling large amounts of financial transactions [Pal97] and solving an optimal assignment of students to research groups for their master’s thesis [Ver95]. Near to the end I was also happy to use my knowledge in CLP programming to aid in solving LP related problems like: detecting the unsolvability of a query, in the aim of avoiding loops using SLD [BVdD98] and proving the termination of programs [Dec97, DD97a, DDV99]. Of course this work would not have been possible without the support of a lot of people. Many thanks go to my supervisor Danny De Schreye for letting me work on this topic, discussions and his ever lasting courage to read my badly written manuscripts. Of course also the other members of the staff of the group took care of me: Maurice Bruynooghe, Bart Demoen, Gerda Janssens, being patient with me while writing this thesis, Karel De Vlaminck and Bern Martens. I’m also grateful to the other and past members of the research group, too much to name them all. I want to express my special thanks to Veroniek Dumortier, being a faithful officemate for many years. Of course also many other researchers from and outside the department deserve my vii

PREFACE

viii

gratitude: Philip Dutr´e, Philippe Bekaert, Frank Suykens-De Laet, Eric Lafortune, Geert Uytterhoeven, Tom Michiels, for celebrating our birthday, Frank Piessens, Eddy Bevers, Johan De Wolf, Robert Rodoˇek, Andr´e de Waal and of course many others. The people from the system-group, Jean Huens and his co-operators, are honoured for supporting me in all software and hardware problems. And almost last but not least there are my wife, Anne Lavaerts, children, Kaat, Leen and Tine, my parents and my parents in law which I thank all for their support and cheering me up in difficult times. I should neither forget to thank my friends Dirk Lecluse, Damien Fontaine, Geert Touquet and all the people I forgot. Finally, the text of this thesis would be below any quality standards without the very useful comments of Danny De Schreye, Maurice Bruynooghe, Bart Demoen and Yves Deville. May 1999, Henk Vandecasteele

Contents Preface

vii

Contents

ix

1

2

Introduction 1.1 Motivation for Constraint Logic Programming 1.2 ROPE . . . . . . . . . . . . . . . . . . . . . 1.3 Academic Applications . . . . . . . . . . . . 1.4 Industrial Applications . . . . . . . . . . . . 1.5 Closing . . . . . . . . . . . . . . . . . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

1 1 3 4 4 5

Basics on Constraint Logic Programming 2.1 Finite Domain Constraint Logic Programming . . . . . . . . . . . 2.1.1 Intuitive setting . . . . . . . . . . . . . . . . . . . . . . . 2.1.2 History . . . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.3 Technology . . . . . . . . . . . . . . . . . . . . . . . . . 2.1.4 Primitives for Finite Domain CLP languages . . . . . . . 2.1.5 Global versus local constraints, black box versus glass box 2.2 CLP based on Linear Programming . . . . . . . . . . . . . . . . 2.2.1 Intuitive setting . . . . . . . . . . . . . . . . . . . . . . . 2.2.2 Some concepts from Linear Programming . . . . . . . . . 2.2.3 Tailoring Linear Programming Technology to CLP . . . . 2.3 CLP based on Interval Arithmetic . . . . . . . . . . . . . . . . . 2.3.1 Intuitive setting . . . . . . . . . . . . . . . . . . . . . . . 2.3.2 Basic notions . . . . . . . . . . . . . . . . . . . . . . . . 2.3.3 Domain of applications . . . . . . . . . . . . . . . . . . . 2.4 Less frequently used CLP paradigms . . . . . . . . . . . . . . . . 2.4.1 Boolean solvers . . . . . . . . . . . . . . . . . . . . . . . 2.4.2 Algebraic methods . . . . . . . . . . . . . . . . . . . . . 2.5 Contribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . .

7 7 7 8 8 17 22 24 24 25 33 35 35 37 37 38 38 38 39

ix

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

. . . . .

CONTENTS

x

3

4

The ROPE System: Concepts and Implementation 3.1 The Language . . . . . . . . . . . . . . . . . . . . . . . . 3.1.1 Motivation . . . . . . . . . . . . . . . . . . . . . 3.1.2 Specifying the pruning technique. . . . . . . . . . 3.1.3 Parametrised backtracking . . . . . . . . . . . . . 3.2 Implementing a finite domain solver in Prolog . . . . . . . 3.2.1 Transforming to a lower level language: Indexicals 3.2.2 Meta-interpretation versus transformation. . . . . . 3.2.3 The finite domain library . . . . . . . . . . . . . . 3.2.4 Final Remarks . . . . . . . . . . . . . . . . . . . 3.3 Early Mercury as a platform for a solver . . . . . . . . . . 3.3.1 A pure implementation of ROPE in Mercury . . . 3.3.2 Mercury with backtrackable destructive assignment 3.4 Using a More Mature Mercury . . . . . . . . . . . . . . . 3.4.1 Supported Finite Domain Language . . . . . . . . 3.4.2 Data structures and implementation . . . . . . . . 3.4.3 Comparison . . . . . . . . . . . . . . . . . . . . . 3.5 Contribution . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

41 42 42 42 46 49 49 50 54 61 63 63 69 72 72 75 77 79

Proving a query can never succeed 4.1 Introduction . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.2 Models based on a pre-interpretation . . . . . . . . . . . . . . . . 4.2.1 Terminology . . . . . . . . . . . . . . . . . . . . . . . . 4.2.2 The transformation by Codish and Demoen [CD95] . . . . 4.3 Finding a model where the query is false . . . . . . . . . . . . . . 4.3.1 Executing a program under a pre-interpretation . . . . . . 4.3.2 Executing a program under an unknown pre-interpretation 4.4 Using CLP to check solvability and find a solution . . . . . . . . . 4.4.1 Finite domain variables . . . . . . . . . . . . . . . . . . . 4.4.2 Generating constraints from conditions . . . . . . . . . . 4.5 Optimisations . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.5.1 Proving solvability . . . . . . . . . . . . . . . . . . . . . 4.5.2 Subsumption and free variables . . . . . . . . . . . . . . 4.6 Termination of the procedure . . . . . . . . . . . . . . . . . . . . 4.7 Alternative approaches . . . . . . . . . . . . . . . . . . . . . . . 4.8 Benchmarking . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4.9 A step backwards: using Prolog . . . . . . . . . . . . . . . . . . 4.9.1 Ingredients of the new Prolog attempt . . . . . . . . . . . 4.9.2 Some variations on the theme and results . . . . . . . . . 4.10 Contribution . . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . . . . .

81 81 83 83 84 86 86 88 93 93 94 96 96 96 98 99 100 104 104 105 108

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . . . .

xi

5

6

7

8

Constraint-based Termination Analyses 5.1 Basic notions . . . . . . . . . . . . 5.2 State of the art . . . . . . . . . . . . 5.3 Symbolic conditions for termination 5.4 From conditions to constraints . . . 5.4.1 Derivation Rule . . . . . . . 5.4.2 Evaluation Rule . . . . . . . 5.4.3 Substitution Rule . . . . . . 5.5 Finite Domain Solving . . . . . . . 5.6 Experimental evaluation . . . . . . 5.7 Contribution . . . . . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

109 109 113 113 118 119 120 121 124 124 130

Scheduling the Maintenance of Power Plants 6.1 Maintenance of Power Plants . . . . . . . . . . . . . . . 6.2 The Constraint Programming approach . . . . . . . . . . 6.2.1 A High Level Model . . . . . . . . . . . . . . . 6.2.2 A usable model for an average CLP(FD) system 6.2.3 Using CHIP . . . . . . . . . . . . . . . . . . . . 6.2.4 Results . . . . . . . . . . . . . . . . . . . . . . 6.3 Up to a higher level: OLP-FOL . . . . . . . . . . . . . . 6.3.1 OLP-FOL . . . . . . . . . . . . . . . . . . . . . 6.3.2 The specification in OLP-FOL . . . . . . . . . . 6.3.3 Scheduling by Abductive Reasoning . . . . . . . 6.4 Using both Finite Domain CLP and Linear Programming 6.4.1 Some first (failing) experiments . . . . . . . . . 6.4.2 The Second Model . . . . . . . . . . . . . . . . 6.4.3 Upper bound on the optimal solution . . . . . . 6.5 Contribution . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

. . . . . . . . . . . . . . .

131 132 133 133 135 139 142 146 147 147 150 151 151 152 155 157

A few other applications 7.1 Optimising a Set of Securities . . . . . . . . . . . . . . . . . . 7.1.1 A detailed look at the investment problem . . . . . . . . 7.1.2 Defining the input for the problem . . . . . . . . . . . . 7.1.3 Tackle the investment problem with CLP . . . . . . . . 7.1.4 Results . . . . . . . . . . . . . . . . . . . . . . . . . . 7.2 Practising Modelling on a Scheduling Problem . . . . . . . . . 7.2.1 Distribute master students over different research groups 7.2.2 Designing the input format . . . . . . . . . . . . . . . . 7.2.3 Creating a model . . . . . . . . . . . . . . . . . . . . . 7.2.4 Results . . . . . . . . . . . . . . . . . . . . . . . . . . 7.3 Contribution . . . . . . . . . . . . . . . . . . . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

. . . . . . . . . . .

159 159 160 163 164 166 167 167 167 168 171 173

Conclusion

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

. . . . . . . . . .

175

CONTENTS

xii

9

Future Work 179 9.1 Combining a Finite Domain Solver with Techniques from Linear Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 179 9.2 A General Robust CLP solver for LP+ . . . . . . . . . . . . . . . . . . . 181

A Data sets and examples A.1 Data for Scheduling Power Plants . . . . . . . . . . . . . . . . . . . . . A.2 Examples for proving failure . . . . . . . . . . . . . . . . . . . . . . . . A.3 An example of input for FINDER . . . . . . . . . . . . . . . . . . . . .

183 183 185 190

B Scheduling maintenances with CHIP

193

C Benchmarks for MROPE II C.1 Queens . . . . . . . . C.2 Bridge1 . . . . . . . . C.3 Bridge2 . . . . . . . . C.4 Suudoku . . . . . . . . Bibliography

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

. . . .

201 201 202 211 212 217

Chapter 1

Introduction A lot of problems in reality can be described in detail in a logic specification. However, using classic programming languages, such a detailed logical specification does not straightforward lead to a executable program. It is the aim of Logic Programming to attach a procedural behaviour to a logical description, such that declarative specifications become executable [Kow79b, Kow79a]. This should help the user to concentrate on the correct specification of the problem instead of bothering the user about implementation details. The main result of early research in this field is the Prolog language [Gro75]. The Prolog mechanism is based on unification [Rob71] and resolution [Rob65]. A major breakthrough was the WAM design [War83] as an efficient implementation of Prolog. The Logic Programming setting serves very well for specifying problems in scheduling, planning, : : : . Unfortunately the Prolog approach fails for these types of difficult problems. When encoding the problem in Prolog such that the program becomes efficient, one is far from a declarative specification. Instead one has written a solver in Prolog, with little use of the declarative aspect of the language. In practice a lot of algorithms exist for solving problems like scheduling and planning. Many were developed in the field of Operations Research [FGa, FGb]. Systems created within this research are often very powerful but difficult to use. Sometimes a lot of programming is involved, or the problem has to be transformed to a mathematical model which may be long-winded and errorprone. Constraint Logic Programming tries to provide a solution for this by combining constraint solving algorithms with the Logic Programming specification power [JL87].

1.1 Motivation for Constraint Logic Programming The reason why Prolog fails in solving problems like scheduling lies at the heart of the system: unification and backtracking. Although from the very beginning special builtins were added to the language to handle numerical equalities and inequalities, still all vari-

1

CHAPTER 1. INTRODUCTION

2

ables involved in such expressions must be ground before these special builtins can be used. Suppose we have two constraints 1 to satisfy: Example 1.1 Rabbits + Pheasants = 9 (1) Rabbits*4 + Pheasants*2 = 24 (2) then the Prolog solution would be: Program 1.1 problem(Rabbits, Pheasants):number(Rabbits), number(Pheasants), Heads is Rabbits + Pheasants, Legs is Rabbits*4 + Pheasants*2 Heads = 9, Legs = 24. number(0). number(1). number(2). number(3). number(4). number(5). number(6). number(7). number(8). number(9). This program, when run in a Prolog environment, will need an amount of resources which is out of proportion. It will backtrack over all possible combinations of assignments for the variables Rabbits and Pheasants before all solutions are proven to be found. Any intelligent numerical package will immediately reduce the set of equations to the solution! To cure this problem, in Constraint Logic Programming the unification is augmented with a constraint solver. When the CLP engine recognises a constraint then this constraint is handed to the incorporated solver. From then on the program above can be written as: Program 1.2 problem(Rabbits, Pheasants):Rabbits + Pheasants = 9, Rabbits*4 + Pheasants *2 = 24. In practice there exist a wide diversity of different constraint solvers, where every constraint solver is good at its own problems. For each of these solvers another CLP system can be build by integrating it with Logic Programming. Then each of these CLP systems inherits the solving power of the constraints solver but of course inherits its logical specification power from Logic Programming. A number of main streams can be recognised in this wide variety of different Constraint Logic Programming systems: 1 9 animals, rabbits and pheasants, are playing on the grass. We can see 24 legs, how many rabbits and pheasants are there?

1.2. ROPE

    

3

Constraint Logic Programming based on consistency techniques and propagation. The variables involved in the constraints range over a finite set of values, usually integers. CLP solving linear constraints with Gaussian elimination and the simplex algorithm. In this case the values of the variables involved in the constraints range over the rational or floating point numbers. Solving constraints using interval arithmetic. In this case the variables involved range over the floating point numbers. Solvers based on boolean constraint solvers.

Algebraic CLP systems mainly for solving non-linear constraints with real numbers. The second chapter will elaborate on these different types of Constraint Logic Programming systems. The emphasis in that Chapter is on Finite Domain Constraint Logic Programming. This description offers an alternative view using a framework which integrates most Finite Domain CLP technologies.

1.2 ROPE The research reported on in this thesis is twofold. Much effort was spent on solving a number of academic and industrial problems. Apart from that a number of aspects of Finite Domain languages were investigated, prototype Finite Domain CLP systems were implemented. These prototypes were implemented on topic of Logic Programming systems. Chapter 3 starts with some results on new concepts in Finite Domain languages. There are two main issues: (1) the prune operator as a higher level language for expressing indexical constraints and (2) parameterised backtrack behaviour. The chapter continues with different implementations of the system ROPE using Prolog and Mercury. The implementation of the ROPE system using Prolog is based on transformation and uses open-ended data-structures for representing changing information. The first implementation of the ROPE system on top of Mercury, called MROPE, was using an early implementation of Mercury. Since Mercury does not allow open-ended data-structures, data representation was the major issue in the port from Prolog to Mercury. The experiment indicated that backtrackable destructive assignment was needed. The last experiment described in this chapter shows how ROPE was implemented in a more mature Mercury, having backtrackable destructive assignment. Running benchmarks indicate that this version of ROPE, called MROPE II, competes with the CLP(FD) library [COC97] coming with SICStus.

CHAPTER 1. INTRODUCTION

4

1.3 Academic Applications A major part of the thesis handles applications solved using CLP. The aim of the application described in Chapter 4 is to find a proof that a query can never succeed given a logic theory. The contribution of the work described in this Chapter includes more than using CLP for solving the problem. The use of tabling in combination with CLP and uncertainty is non-trivial. The second academic application treated in Chapter 5 uses Finite Domain Constraint Logic Programming for proving termination of programs within Logic Programming. The work is based on replacing ad hoc solutions for the problem at hand by a parameterised solution which covers all ad hoc versions. Instead of checking the ad hoc solutions, conditions are generated for the parameterised solution. The contribution in this application concerns solving these conditions, finding actual values for the parameterised solution.

1.4 Industrial Applications The first industrial application concerns scheduling maintenances for electricity generators in power plants and is described in Chapter 6. This application has been tackled in many ways, using different models and different systems. This includes regular Finite Domain Constraint Logic Programming, the specification language OLP-FOL and Finite Domain Constraint Logic Programming combined with Linear Programming. The application shows that

  

trying different models for the same application is worthwhile, solving scheduling problems starting from a very high-level specification in First Order Logic is possible, combining Finite Domain Constraint Logic programming with Linear Programming is very promising.

The last chapter on industrial application contains two smaller problems. How to optimise a set of securities in Section 7.1 and distribute master students over different research groups in Section 7.2. In contrast with the other applications, the first application in this chapter was solved using Constraint Logic Programming based on Linear Programming. The application was no real success. None of the systems experimented with could cope with the problem at hand. The main problems were instability of numerical computation when using floating point numbers and inefficient computations when using rational numbers. In contradiction the last application was very successful. The program to distribute master’s students over different research groups has been used several times, saving a lot of time for the people using it. The model used in this program and its optimisations is interesting.

1.5. CLOSING

5

1.5 Closing The thesis is concluded with a summary of the topics and contributions of the thesis in Chapter 8. The last chapter describes a few valid candidates for future research on CLP.

6

CHAPTER 1. INTRODUCTION

Chapter 2

Basics on Constraint Logic Programming This chapter goes into detail on state of the art Constraint Logic Programming techniques. The chapter starts with an alternative view on Finite Domain Constraint Logic Programming. A framework is provided, melting together available techniques within Finite Domain CLP. The second part of the chapter introduces the basic concepts of linear programming and states different aspects and the difficulties in integrating these techniques in a CLP environment. The use of Interval Arithmetic for CLP is shortly illustrated as well. The chapter concludes with mentioning other techniques used within CLP such as boolean solvers and algebraic methods.

2.1 Finite Domain Constraint Logic Programming 2.1.1 Intuitive setting In this type of CLP system the algorithms used expect the variables, involved in the constraints, to range over a set with a finite number of elements. In Example 1.1 both variables will then range over the integers from 0 to 9. To each variable involved in constraints such a finite set of elements (the domain) is attached. Such a variable is often called a Finite Domain Variable. The basic algorithm then tries to reduce the domains of the Finite Domain Variables by removing inconsistent values from the domains. A value from a domain is inconsistent with the constraints when the solver can prove no solution can exist with that value for the corresponding variable. With Example 1.1 we can deduce from (2) that 9, 8, 7 are inconsistent values for the variable Rabbits. Because when replacing these values for the variable Rabbits we need a negative value for Pheasants to become 24 at the right hand side of the expression. Then we can deduce from (1) that 0, 1 and 2 are inconsistent for variable

7

CHAPTER 2. BASICS ON CONSTRAINT LOGIC PROGRAMMING

8

Pheasants. Since using these values would need the value 9, 8 and 7 for the variable Rabbits, which were removed. As a results we can remove the values 6 and 5 from the domain of the variable Rabbits as they are inconsistent with constraint (2) and values in the domain of Pheasants being larger than 2. Continuing this propagation the algorithm will deduce that the only consistent values in the domains of Rabbits and Pheasants are 3 and 6 and we have found a solution! If the removal of inconsistent values would lead to an empty domain for one of the variables we have proven no solution exists. On the other hand a finite domain CLP program will almost never lead to a solution only by just expressing a set of constraints. Finite domain solvers remove inconsistent values from the domains, but not all of them. Computing all of them is often too expensive. Actually, when more than one solution exists, reducing all domains to one value by removing inconsistent values is impossible. Therefore, after expressing the constraints of the problem, a search is started by enumerating, making choices on which the system can backtrack on. Of course, after every choice, the constraints are used to remove inconsistent values from other domains given this new information.

2.1.2 History The algorithms used in Finite Domain Constraint Logic Programming originate from the research on discrete combinatorial problems. They are often called consistency techniques. The roots of these techniques go back to Waltz’s filtering algorithm [Wal72] and Fike’s REF-ARF [Fik68]. The algorithms used in these pioneering works were further elaborated and extended by Gaschnig [Gas74], Freuder [Fre78], Mackworth [Mac77] among others. For an overview of the algorithms developed in these first works look at [Nad89] and [Pro93]. CHIP (Constraint Handling In Prolog) is one of the first systems which used this kind of technology in a CLP framework. It was developed at ECRC in Germany [DVS+ 88]. The CHIP system and the ideas behind it are very well described in [Van89]. This experimental system was the basis for the commercial system CHIP V4 exploited by Cosytec. Also CHARME from BULL originated from CHIP at ECRC, but there the interface to the solver was re-engineered towards a C-style language. Later, in the same style as CHARME, the ILOG solver by ILOG was introduced on the market. For a more detailed overview of such industrial tools see [Cra93].

2.1.3 Technology Language The target applications of Finite Domain Constraint Logic Programming languages are Constraint Satisfaction Problems(CSPs). These problems can be characterised by



a tuple V of n variables (V1 ; V2 ; : : : ; Vn ),

2.1. FINITE DOMAIN CONSTRAINT LOGIC PROGRAMMING

  

9

(0)

for each variable Vi , D (0) being the tuple of all these domains: (0) (0) (0) (D1 ; D2 ; : : : ; Dn ), an initial domain Di

let D be the set of all possible domain tuples: D = fD (l ) j8i : Di

(l )

 Di 0 g and ( )

C the set of all constraints c j . A constraint c j is a function from D1(0) x : : : xDn(0)

to f f alse; trueg. Almost always the function is independent of some variables as the variables do not occur in the description of the constraint. We define the set involved (c j ) as the set of indices such that 8i 2 involved (c j ): Vi occurs in a description of the constraint c j .

Definition 2.1 (Solution(s)) Then a tuple S = (v1 ; v2 ; : : : ; vn ) 2 D (0) is a solution for the system < V ; D (0) ; C 8c j 2 C : c j (S) = true. We define S the set of all solutions S of < V ; D (0) ; C >.

>

iff

Depending on the problem the user may want to find

  

one solution S from the set of solutions S , all solutions S (rare, for realistic problems this number of solutions is usually huge if solutions exist), the solution that optimises an optimisation function.

In the last case an additional function f : D (0) ! IN is added to the system. The solution for the optimisation problem < V ; D (0) ; C ; f > where S is the set of solutions for < V ; D (0) ; C > is the solution S 2 S such that 8S0 2 S : f (S0 )  f (S) ( in case of a maximum). Finding the optimum and proving the optimality is in general not possible within a reasonable time. Then the user wants the best solution the system can find within a reasonable time. Achieving (semi-)arc-consistency: AC3’ As mentioned in Section 2.1.1 the algorithms used are based on removing inconsistent values from a tuple of domains D (l ) of the variables V . A value from a domain is inconsistent when the solver can prove the value can never belong to a solution because it violates a constraint c j : Definition 2.2 (Inconsistent value) (l ) (l ) (l ) vi 2 Di is inconsistent with c j given a tuple of domains D (l ) = (D1 ; : : : ; Dn ) iff 8v1 2 D1(l) ; : : : ; vi 1 2 Di(l)1 ; vi+1 2 Di(+l)1 ; : : : ; vn 2 Dn(l) : c j (v1 ; : : : ; vn ) = f alse. When all inconsistent values have been removed from the domains D (l ) with respect to constraint c j then the constraint c j is arc-consistent for those domains D (l ) :

CHAPTER 2. BASICS ON CONSTRAINT LOGIC PROGRAMMING

10

Definition 2.3 (Arc-consistent constraint) A constraint c j 2 C is arc-consistent with D (l ) iff 8i; 8vi 2 Di(l) ; 9v1 2 D1(l) ; : : : ; 9vi 1 2 Di(l)1 ; 9vi+1 2 Di(+l)1 ; : : : ; 9vn 2 Dn(l) : c j (v1 ; : : : ; vn ) = true When all constraints from C are arc-consistent with a tuple of domains D (l ) , then we have achieved arc-consistency for < V ; D (l ) ; C >. Unfortunately for complicated non-binary constraints it can be quite costly to compute all inconsistent values from the domain as also mentioned in 2.1.1. Therefore prune operators are introduced: Definition 2.4 (Prune operator) An operator P : D xC ! D is a prune operator iff (l ) (l ) (1) 8D (l ) ; D (l ) 2 D ; 8c j 2 C ; 8vi 2 Di : P(D (l ) ; c j ) = D (l ) and vi 2 = D ) vi is inconi (l ) sistent with c j for the tuple of domains D , (2) 8D (l ) 2 D ; c j 2 C : P(D (l ) ; c j ) = P(P(D (l ) ; c j ); c j ) (idempotent) and (l ) (l ) (3)8 j ( 8i 2 involved (c j ) : Di is a singleton) ) c j is arc-consistent with P(Di ; c j ) 0

0

0

Such a prune operator P will, given a set of domains and a constraint, remove inconsistent values from these domains but is not guaranteed to remove all of them. A prune operator should be idempotent:  when the operator is applied to a set of domains and a constraint it reaches a fixpoint



when the operator is applied again to the resulting tuple of domains with the same constraint it will not remove extra values. The last condition demands that arc-consistency is reached when all involved variables in c j are instantiated. Let P be the set of valid prune operators. Then we can define a weaker consistency concept based on a prune operator. Definition 2.5 (Semi-arc-consistent constraint) A constraint c j is semi-arc-consistent with respect to a prune operator P 2 P and a tuple of domains D (l ) iff P(D (l ) ; c j ) = D (l ) From this we can deduce a definition for global semi-arc-consistency: when all constraints c j 2 C are semi-arc-consistent given a prune operator P 2 P and a tuple of domains D (l ) then we have achieved semi-arc-consistency for < V ; D (l ) ; C > and the prune operator P. From the last condition in the definition of a prune operator we can deduce that when all domains in D (l ) are singletons a semi-arc-consistent state is also an arcconsistent state. In practice this means that we have found a solution. The reader may already guess that these prune operators are the core of any finite domain CLP system. Inside these operators a whole range of different techniques can be used. Within these techniques some special cases can be recognised [Van89].



Forward checking

2.1. FINITE DOMAIN CONSTRAINT LOGIC PROGRAMMING

11

This pruning strategy corresponds with a prune operator f orward 2 P that will not remove any inconsistent value from the domains of the variables involved in a constraint c j until all but one domain of these involved variables have become a singleton: – if at least two variables in the involved variables from a constraint c j in the tuple of domains D (l ) have non-singleton domains then f orward (D (l ) ; c j ) = D (l ) , – on the other hand if at most one variable in the involved variables from a constraint c j in the domains D (l ) has a non-singleton domain then c j is arcconsistent with f orward (D (l ) ; c j )



Partial lookahead With this technique the domains of the variables are abstracted to the interval between the smallest and largest value in the domain. Then a value is pruned when it is inconsistent with these intervals as domain for the other variables. Such an algorithm has a very low time complexity compared to an arc-consistent pruning algorithm. The corresponding prune operator partial is defined as: –

8i :

D (l ) = partial (D (l ) ; c j ) and (v = min(Di(l ) ) or v = max(Di(l ) )) ) 0

0

0

0

v is consistent with c j for the tuple of domains (D)(l )



8i : if v1 v2 2 Di l are consistent with c j for the tuple of domains D l 8v3 2 Di l : v1 v3 v2 ) v3 2 Di l where D l = partial (D l c j ) ;

( )



( )

::= X in j ask(ground(X),) j ask(X in ,) j [, : : : ,] (The conjunction of , : : : and )


) with min(X) the lowerbound of the domain and max(X) the upperbound. op> ::= + j j  j mod j div nat> ::= a natural number

<
0 , X in 1..N, M1 is M - 1, generate(T,M1,N). safe([]). safe([XjT]):noAttack(X,1,T), safe(T). noAttack( , ,[]). noAttack(X,N,[YjZ]):X Y + N prune(X:p(Y), Y:p(X)), XY prune(X:p(Y),Y:p(X)), YX+N prune(X:p(Y),Y:p(X)), S is N + 1, noAttack(X,S,Z). Program 3.7 Compiled n-queens program queens( Q , N ):getFreeVariables(queens( Q , N ), Free ), queens 1( Q , N ), showSolution( Free ). queens 1( Q , N ):generate 1( Q , N , N ), safe 1( Q ), constraint (enum( Q )). generate 1([],0, ). generate 1([ X j T ], M , N ):M > 0, constraint ( X in 1 .. N ), M1 is M - 1, generate 1( T , M1 , N ). safe 1([]). safe 1([ X j T ]):- noAttack 1( X ,1, T ), safe 1( T ). noAttack 1( , ,[]). noAttack 1( X , N ,[ Y j Z ]):constraint (ask(ground(Y), X in compl(Y + N)) ),

53

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

54

constraint (ask(ground(X), Y in compl(X - N)) ), constraint (ask(ground(Y), X in compl(Y)) ), constraint (ask(ground(X), Y in compl(X)) ), constraint (ask(ground(Y), X in compl(Y - N)) ), constraint (ask(ground(X), Y in compl(X + N)) ). S is N + 1, noAttack 1( X , S , Z ). In the compiled queens program Program 3.7 some of the produced clauses were deleted. It concerns the predicates generate/3, safe/1 and noattack/3 predicates, which are only needed in case these predicates would be queried.

3.2.3 The finite domain library As one can notice, there are three predicates in the finite domain library:

  

getFreeVariables/2. This predicates searches for the free variables in first arguments and stores them in a list in the second argument. showSolution/1 prints out the list of Prolog expressions while substituting the finite domain variable with their semantic value. constraint /1 is by far the most interesting predicate of the finite domain library. This predicate instantiates domain variables to a specific structure, fulfilling the purpose of passing on relevant information for the solver, adds the constraint to the constraint store and activates the solver for checking the constraint.

In the next subsections we first explain how the store, containing constraints and domains, is kept. Representation of the store Within the constraint solver constraints and domains of Finite Domain variables must be stored in some way. These domains and constraints should be easily accessible. Domains are updated and used very frequently. Whenever a domain changes, constraints affected must be activated. As stated before, one alternative is to add to every clause an extra input and output parameter. The data structure in these extra parameters contain information on the constraints connected to the variables and the domains as well. Every finite domain variable then refers to this global data structure with a unique number. When information is changed concerning a finite domain variable, a smaller domain for example, then the out parameter reflects these changes while it still contains the old information on the other variables. Such a working method results in efficiency problems as updates to this global store are dependent on the number of finite domain variables in the system. An example of such a data structure is a flat term where each argument contains information on one finite domain variable (in the sequel, we refer to this representation as ”functor”). The unique number connected to each finite domain variable is the position of this argument

3.2. IMPLEMENTING A FINITE DOMAIN SOLVER IN PROLOG

55

in the functor. If the information on one variable changes then a new functor must be created where all arguments but one must be copied from the old functor. Time and place complexity of this operation is O(N), where N is the number of finite domain variables in the program. A better alternative is a tree structure. In this case the complexity of copying in case of changes is logarithmic in the number of existing finite domain variables. Unfortunately, also access without modification becomes logarithmic in the number of finite domain variables. Therefore we choose to instantiate each finite domain variable to a structure that contains both the domain of the variable and the constraints in which this variable is involved. Then extra arguments for every clause in the program containing information on the current state of constraints and domains are not needed. The access to the data on one variable, having the variable available, becomes independent on the number of variables. There exist several references to one finite domain variable, namely each constraint in which the variable occurs. As a result we cannot replace the variable with a new variable when the domain changes. For this purpose we will use open ended data structures4. Passing on information on constraints and domains through the domain variable itself is particularly interesting for our application, since we need to propagate constraints as soon as some type of changes occurs to the domain of a variable. Thus, by instantiating a domain variable to a structure finiteVar(Domain, Constraint Store), where Domain is a representation of the domain of the variable and Constraint Store of all constraints in the store that contain the variable, easy access for propagation is guaranteed. Actually, as one can notice from the different possible forms of pruning, for each constraint, there are only four classes of constraint to be activated.

   

Constraints to be activated after every change to the domain of a variable. Constraints to be activated after a variable becomes ground. Constraints to be activated after the lower bound of a variable’s domain changes. Constraints to be activated after the upper bound of a variable’s domain changes.

Note that for some pruning directives the involved constraint belongs at the same time to different classes. The structure for the constraint store is then easily chosen: store(Always, Ground, LowerBound, UpperBound). With Always the constraints that need to be checked whenever the domain of the associated variable changes, Ground the constraints that need to be checked as soon as the finite domain variable becomes ground and Lowerbound and UpperBound whenever the lower bound (resp the upper bound) of the domain changes. If we handle a new constraint we check for the operators used in the expression. An expression int(X), min(X) will lead to storing the constraint in the LowerBound constraint list of the variable X. dom(Y) tells the system to put the constraint in the Always constraint list of variable Y. ask(ground(Z), Constraint) will put the constraint in the Ground list of the variable Z. 4 we

come back later to this matter, together with the time complexity issue involved.

56

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

As an example, after treating the 4 low-level constraints of the rabbit program we obtain the following instantiation of the finite domain variables P and R: Example 3.12 P=finiteVar(Domain1,store([],[],[Constr2,Constr4],[Constr2,Constr4])), R=finiteVar(Domain2,store([],[],[Constr1,Constr3],[Constr1,Constr3])), with Constr1=P in 9 - int(R), Constr2=R in 9 - int(P), Constr3=P in (24 - 4*int(R)) div 2, Constr4=R in (24 - 2*int(P)) div 4. (the values for the domains will be discussed in the next subsection) These data structures are generated while the predicate constraint /1 from the finite domain library handles a new constraint. Note that the representation we choose here for the list of constraints is too simplistic. Since constraints can be added at any time of the execution we have to use open ended data structures, discussed below. Representation of the domains As we are going to reason on the bounds of the domains, add and subtract domains we need a representation which is convenient for this kind of computations. Therefore we choose a union of intervals. For example a domain with the values 1, 2, 3, 4, 8, 10, 11, 12 will be represented as 1..4 _ 8..8 _ 10..12 using the same operators as defined in the lowlevel language. A fresh finite domain variable is initialised with the domain 0..infinity. As we have to update the domains of the finite domain variables we also need a structure which we can update by further instantiating. Using an open ended list for explaining the principle, every finite domain variable then starts with the domain representation: [0..infinityj ] For the rabbit problem: after adding the two constraints constraint1 and constraint2 both finite domain variables are instantiated as finiteVar([0..infinity, 0..9 j ], Constraint Store). after adding the two other constraints constraint3 and constraint4 the data structure becomes P = finiteVar([0..infinity, 0..9, 3..7, 5..6, 6..6j ], Store1) R = finiteVar([0..infinity, 0..9, 2..6, 3..4, 3..3j ], Store2) As a result of adding the two constraints to the constraint store and letting them propagate the two variables become ground. How this result is achieved is explained below. Open ended data structures This subject has already been mentioned several times above: the part of the store connected to one variable cannot be replaced but should always be changed by further instantiating it. In case of the constraints a simple open ended list can be used. When adding a

3.2. IMPLEMENTING A FINITE DOMAIN SOLVER IN PROLOG

57

new constraint the end of the list is further instantiated with a new element, the new constraint, and a fresh variable becomes the new end of the list. Also, for some optimisations in the finite domain library and for implementing constraints like cardinality, there is need for removing constraints from the constraint store. This is solved by adding a free variable to every constraint. A constraint can be deactivated by instantiating this free variable to the atom ’old’. When activating constraints, after a domain of a finite domain variable has changed, such constraints must be skipped. Similar to adding new constraints to the store, the domain of the Finite Domain variable should be changed during computation. In principle this could again be done with an open ended list. The annoying thing here is that access to the domain, and updating as well, becomes linear in the number of updates to the finite domain variable. Indeed when fetching the current domain, the whole list of old domains must be traversed until the last element before the open end is reached. In our implementation we used an open ended data structure based on trees, which we will call open ended trees. The access and update complexity of this open ended tree is logarithmic in the number of updates to the domain. Moreover an extra variable is reserved which is instantiated when the domain becomes a singleton, hence fetching the domain of a Finite Domain variable which has been assigned can be done in constant time. For the curious reader interested in this topic the code handling this feature is added: Program 3.8 getDomain(domain(Tree, Var), Range):- % Var is ground for ( atomic(Var) ! % a singleton domain Range = ..(Var, Var) ; findRange(Tree, Range) ). findRange(Tree, Range):( nonvar(Tree) ! lookup(Tree, D) ; upperbound(Limit), Range = ..(0, Limit) % still a free variable ). putDomain(Info, Domain):( Domain = ..(Value, Value) ! arg(2, Info, Value) ; arg(1, Info, Tree), insert(Tree, Domain) ). /* An implementation of an open ended tree. It has an update and retrieval complexity logarithmic in the number of updates. The last value in the tree is always the right-most nonvar element in the tree. When updating the tree grows from left to right, while subtrees grow larger, such that logarithmic access is assured. */

58

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

lookup(tree(Left, El, Right), Value):( var(Right) ! ( var(Left) ! El = Value ; lookup(Left, Value) ) ; lookup(Right, Value) ). insert(Tree, Value):( nonvar(Tree) ! insert1(Tree, Value, 1) ; Tree = tree( , Value, ) ). insert1(tree(Left, , Right), Value, Depth):( var(Right) ! insert2(Left, Value, Depth, Right) ; Depthplus1 is Depth + 1, insert1(Right, Value, Depthplus1) ). insert2(tree(Left, El, Right), Value, Depth, Back):( var(El) ! El = Value ; ( Depth = 0 ! Back = tree( , Value, ) ; Depthmin1 is Depth 1, ( var(Right) ! insert2(Left, Value, Depthmin1, Right) ; insert2(Right, Value, Depthmin1, Back) ) ) ). Queue of constraints A final data structure concerns the queue of constraints which were activated, but not used yet. This is an open ended list where new constraints are added at the end of the

3.2. IMPLEMENTING A FINITE DOMAIN SOLVER IN PROLOG

59

list. A desirable feature of such a queue is that a constraint can only appear once in the queue. For this purpose an unbalanced binary tree is used in the pure Prolog version. In an impure version, we have used a record database. Every constraint contains a unique number. For every constraint in the queue this number is stored in the tree/database. The Finite Domain library The library call constraint /1 is the main entry point of the library. It adds the new constraint to the constraint store. While doing so, new finite domain variables are instantiated to the structure finiteVar(Domain, Store). As we argued above, adding a constraint is done by adding the constraint to the constraint store of every variable which appears in the constraint. Program 3.9 constraint (NewConstraint):/* Store the constraint in the constraint store and add the constraint to the queue if necessary: */ addToConstraintStore(NewConstraint, emptyQueue, Queue), fixpoint(Queue). Another function of the library call constraint /1 is starting the propagation mechanism. This is the process of checking constraints until we are sure that no constraint would further prune the domains of the finite domain variables, as the user specified in the prune declarations. The algorithm used for this is a variant of the AC3 algorithm of Mackworth [Mac77]. There is a queue of constraints to be tested. Whenever a domain of a variable changes, the corresponding constraints to be checked in the constraint store of the variable are added at the end of the queue (if they are not already there). The algorithms stops when the queue is empty. Program 3.10 fixpoint(emptyQueue):- !. fixpoint(Queue):firstFromQueue(Queue, Constraint, Queue1), /* Use the constraint to prune the domains and if domains change add the awaken constraints to the queue: */ testConstraint(Constraint, Queue1, NewQueue), fixpoint(NewQueue). Now we explain some predicates from the pieces of code above more in detail. The predicate addToConstraintStore stores the constraints for future propagation and adds them to the current queue if necessary. Program 3.11 addToConstraintStore(Constraint, QueueIn, QueueOut):-

60

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

( Constraint = X in Range ; ), Constraint = ask(X in Range, ) /* Collect all variables occurring in the range and return them in the right list depending on the required propagation: */ collectVariables(Range, Always, Ground, LowerBound, Upperbound), /* Store the constraint in the appropriate places: */ storeConstraint(Always, 1, Constraint), storeConstraint(Ground, 2, Constraint), storeConstraint(LowerBound, 3, Constraint), storeConstraint(UpperBound, 4, Constraint), addToQueue(Constraint, QueueIn, QueueOut). addToConstraintStore(Constraint, QueueIn, QueueOut):Constraint = ask(ground(X), Delayed Constraint), ( finiteVarBound(X) ! addToConstraintStore(Delayed Constraint, QueueIn, QueueOut) ; storeConstraint([X], 2, Constraint), QueueOut = QueueIn ). The predicate storeConstraints stores the constraint given as third argument in the store connected to the variables given in the first argument. The second argument specifies to which constraint list the constraint should be added. Program 3.12 storeConstraint([], Arg Nr, Constraint). storeConstraint([VarjVars], Arg Nr, Constraint):Var = finite var(Domain, Constraint Store), arg(Arg Nr, Constraint Store, List), /* Add the constraint to the end of the list */ addToEnd(List, Constraint), storeConstraint(Vars, Arg Nr, Constraint). As a last piece of code we give a part of the implementation of the predicate testConstraint/3. Program 3.13 testConstraint(X in Range, QueueIn, QueueOut):/* evaluate Range: */ computeNewDomain(Range, RangeDomain), fetchDomain(X, OldDomain), intersect(RangeDomain, OldDomain, NewDomain), ( NewDomain = OldDomain !

3.2. IMPLEMENTING A FINITE DOMAIN SOLVER IN PROLOG

61

/* No pruning occured, no constraints are activated: */ QueueIn = QueueOut ; storeDomain(X, NewDomain), wakeUpConstraints(OldDomain,NewDomain,X,QueueIn,QueueOut), ). testConstraint(ask(Condition, Constraint), QueueIn, QueueOut):( conditionSatisfied(Condition) ! addToConstraintStore(Constraint, QueueIn, QueueOut) ; QueueIn = QueueOut ). A topic not discussed in this section concerns enumeration. This has been discussed at length in Section 2.1.3 starting on page 12. The implementation used in this Prolog version follows very closely the scheme in Figure 2.3 on page 15.

3.2.4 Final Remarks Power of pruning strategies As an example of how different pruning techniques can change the execution behaviour of a program we decorate the send+more=money program given in Program 3.14 with different pruning strategies. Program 3.14 /* A program that finds assignments for all letters fs,e,n,d,m,r,o,yg such that the addition send + more = money holds. All letters have a different assignment and the letter s and m should be different from zero */ send([S,E,N,D,M,O,R,Y]):alldifferent([S,E,N,D,M,O,R,Y]), S 0, M 0, Y + 10 * O1 = D + E, % (constraint 1) E + 10 * O2 = N + R + O1, % (constraint 2) N + 10 * O3 = E + O + O2, % (constraint 3) O + 9 * M = S + O3, % (constraint 4) enum([S,E,N,D,M,O,R,Y]). alldifferent([]). alldifferent([XjXs]):X in 0..9, different(X, Xs), alldifferent(Xs). different(X, []). different(X, [YjYs]):X Y,

62

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

different(X, Ys). The program above was annotated with different pruning strategies for constraint 1 up to constraint 4. Three variants were used: forward checking, lookahead and an optimised version with prune declaration shown in Program 3.15. Program 3.15 Y+10*O1=D+E prune(Y:p(D,E),D:p(Y,E),E:p(Y,D),O1:p), E+10*O2=N+R+O1 prune(E:p(N,R),N:p(E,R),R:p(E,N),O1:p,O2:p), N+10*O3=E+O+O2 prune(N:p(E,O),E:p(N,O),O:p(N,E),O2:p,O3:p), O+9 *M =S +O3 prune(O:p, M:p,S:p,O3:p), For each of the variants the number of constraint checks were counted and execution time was measured. Send More Money Forward Checking Lookahead Optimised

#Checks 3952 237 150

Timing(sec) 2.24 0.11 0.08

This shows that having a different pruning strategy for each constraint does make sense in some cases. The ROPE solver has been used as a base for development of different applications. Many experiments were performed on different implementations of the solver using standard and non-standard features of Prolog. Usually this is done by writing a metainterpreter which filters out the non-standard features of the language. We succeeded to replace such a meta-interpreter by a transformation a priori. In addition to removing the meta-level of the finite domain program we also perform transformations of the finite domain primitives to more efficient primitives. For a more efficient implementation (not in Prolog) of the low-level language we refer the reader to [DC93]. The aim of our solver was not implementing a fast machinery for these low-level primitives but the development of some useful primitives for the user with an automatic transformation to these low-level primitives. Another alternative is implementing this low-level language with attributed variables as has been done in ECLi PSe [Eur98].

3.3. EARLY MERCURY AS A PLATFORM FOR A SOLVER

63

3.3 Early Mercury as a platform for a solver Mercury [SHC94] is a recent phenomenon in the field of logic programming: it is faster than other logic language implementations (e.g. Prolog) and better suited for the development of large applications because of its compile time error detection capabilities. The implementation described in Section 3.2 is a prototype implemented in pure Prolog [VD94]. This implementation will further be referred to as ROPE. This prototype lacks efficiency because its implementation doesn’t rely on any non-standard support of the Prolog implementation. On the other hand, this helped the development of the ideas of ROPE quite a bit. We believe that the reason for this inefficiency is partly the generality of Prolog (reversible, non-typed predicates) and partly the lack of support for data structures that can be updated at constant cost. When the new logic programming language Mercury emerged, it looked very promising to port our prototype to this new system. One of the advantages of Mercury is faster execution: type, mode and determinacy declarations allow to generate more efficient code. On the other hand, exactly because of these declarations the port was non-trivial. For example the mode declarations do not allow partially instantiated structures. As a result open ended data structures are not possible. The prototype of ROPE in Prolog heavily depends on such partially instantiated data structures. Still, we were encouraged to make the experiment as the designers of Mercury claim there exists alternative methods to cure such problems. The next section reports on an initial, pure Mercury implementation of the finite domain solver ROPE. The implementation of the finite domain solver on Mercury will be called MROPE in the rest of the paper. This first pure version of MROPE did not fulfill our performance expectations and we have experimented with different data structures. Finally we added backtrackable destructive assignment to the Mercury system and report on this. This work was also presented in [VDV96] and [VDV99]. We end with some conclusions.

3.3.1 A pure implementation of ROPE in Mercury In this first experiment we restrict the language to “in”-constraints. This implementation allows to experiment with some small examples. From this we can draw some first conclusions. Data representation Concerning the representations of the domain only some small changes were needed for the typing system: Program 3.16 :- type mrope srange —> ..(int, int). :- type mrope range —> simple(mrope srange) ; :(mrope srange, mrope range).

64

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

A alternative representation could be a list of objects of type mrope srange. However this would increase memory use and slow down code for computing intersections and similar operations. The example set f1,2,3,8,9,12g is then represented as :(..(1,3), :(..(8,9), simple(..(12,12)))) instead of 1..3:8..9:12..12 . Also for the “in”-constraints similar constructs were used as in ROPE. X in int(Y) + 1 is now represented as in(X, int(Y) + val(1)) . The reason for the val/1 functor, is that Mercury does not support subtypes. The type of such constraints looks as follows: Program 3.17 :- type mrope bound —> val(int) ; lo(mrope var) ; up(mrope var) ; mrope bound * mrope bound ; mrope bound / mrope bound ; mrope bound // mrope bound ; mrope bound + mrope bound ; mrope bound - mrope bound. :- type mrope domain —> :(mrope domain,mrope domain) ; ..(mrope bound,mrope bound) ; int(mrope var) ; dom(mrope var) ; val(int) ; compl(mrope domain) ; mrope domain * mrope domain ; mrope domain / mrope domain ; mrope domain + mrope domain ; mrope domain - mrope domain. % Each constraint contains a unique number as first element. % The maximum number used so far is stored in the mrope system a data structure defined later. :- type mrope constraint —> in(int,mrope var,mrope domain) ; askgroundin(int,mrope var,mrope var,mrope domain). In Prolog to connect a domain to a variable we instantiated the finite domain variable to a structure with an open end. By further instantiating this structure the domain could be changed. Such a data structure is not possible in Mercury. The only possibility left is to use an extra in and out argument in the clauses of the program and most of the clauses of the solver. These arguments then contain a global data structure with information on all finite domain variables in the user program at hand. As argued in the previous section, updating such a data structure is usually very time consuming and depends on the number

3.3. EARLY MERCURY AS A PLATFORM FOR A SOLVER

65

of finite domain variables in the user program. In case the update time is logarithmic in the number of finite domain variables, also the access to the domains becomes dependent on the number of finite domain variables. In a first attempt, this global data structure was implemented with the array library in Mercury. The implementation of this array is based on a balanced tree. Here are the type definitions of these extra arguments together with the type definition of the finite domain variables: Program 3.18 :- import module array. :- import module int. :- import module queue. :- type mrope queues —> mrope queues(queue(mrope constraint), queue(mrope constraint), queue(mrope constraint), queue(mrope constraint)). :- type finitevar —> finitevar(mrope range,mrope queues). :- type variables == array(finitevar). % mrope system/3 takes two numbers as input, each being the highest unique number % used so far for mrope constraint objects and mrope var objects. :- type mrope system —> mrope system(int,int,variables). :- type mrope var==int. This subject on representation of constraint store and changing domains is further elaborated in the subsection on results. Also the constraints connected to a finite domain variable are represented in this global data structure. As we want the finite domain system to be incremental, also these data structures are subject to change when new constraints are added to the constraint store. One list of constraints connected with a finite domain variable is represented with the queue data type in the Mercury library. For each of the different propagation schemes, always, ground, lowerbound and upperbound another list is maintained. A similar system was used in the Prolog solution. Also the queue of constraints still to be consumed in the fixpoint algorithm uses this queue data type from the library. Primitives in this first version Because Mercury is a moded system it is not possible to initialise a finite domain variable implicitly at its first occurrence. As a result every finite domain variable has to be initialised explicitly before use. For these purposes the following primitive can be used:

66

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

Program 3.19 :- pred domain(mrope var,mrope domain, mrope system,mrope system). :- mode domain(out,in,in,out) is semidet. Then the “in”-constraints can be formulated with the following primitive: Program 3.20 :- pred in(mrope var,mrope domain, mrope system,mrope system). :- mode in(in,in,in,out) is semidet. These primitives are used in a small example: Program 3.21 % There is a field with both pheasants and rabbits there are 9 animals and 24 legs. % How many pheasants and rabbits are there ? mrope main –> % initialise the variables. domain( F, ..(val(0),val(100)) ), domain( R, ..(val(0),val(100)) ), % constraint : F+R=9 in( F, val(9)-int(R) ), in( R, val(9)-int(F) ), % constraint : 2*F + 4*R = 24 in( F, (val(24)-val(4)*int(R))/val(2) ), in( R, (val(24)-val(2)*int(F))/val(4) ), % display result output([F,R]). In general a problem cannot be solved with constraint propagation only, so a simple enumeration primitive was added to the implementation for experimental purposes. It concerns a predicate that enumerates one variable, with a limited number of heuristics to select a value from the domain of a variable. Also a limited number of backtrack behaviours can be specified. Program 3.22 :- type selectvalue —> up ; down ; middle ; split. :- type backtrackmethod —> standard ; standard ex. :- pred enum(mrope var, selectvalue, backtrackmethod, mrope system, mrope system). :- mode enum(in,in,in,in,out) is nondet.

3.3. EARLY MERCURY AS A PLATFORM FOR A SOLVER

67

Results A first observation concerns the order in which solutions are generated. As it happens, the Mercury version of ROPE generates solutions in the opposite order as the Prolog version. This is because Mercury is intended as a pure logic language and logic makes abstraction of the order in which solutions are found. From that perspective Mercury does not care which solutions are generated first. We do, especially in the enumeration phase. The first results show that this first pure implementation in Mercury is slower than the implementation on top of Prolog using ProLog by BIM. Two versions were tried, one with garbage collection and one without. Mercury uses garbage collection at the level of C [BW88]. Timing is given for finding all solutions. test queens(10), with gc for Mercury queens(10), without gc for Mercury

MROPE 95s 47s

ROPE 39s -

The main difference between the Prolog version and the Mercury version is the different data structure for the domains of the variables and the constraint store. As Mercury is known to be faster than Prolog, at least for Prolog programs that Mercury can execute, the reason for this slow-down must be because of this different data structure. First we measured the speedup we got in the parts of the program where the code did not change. A small program was written which computed intersections of random generated domains. Both programs were run on Mercury and ProLog by BIM on a Sparccenter-1000. About 40.000 intersections were computed.

40.000 intersections

ProLog by BIM 3.7s

Mercury 0.36s

This shows that it is reasonable to expect a 10-fold speedup when going from Prolog to Mercury, on the assumption that Mercury offers efficient pure alternatives for the tricks in the Prolog program. In another experiment we measured the results of changes to the representation of the data in the extra input and output arguments. The original data structure was an array from the Mercury library. Program 3.23 :- import module array. :- type variables == array(finitevar). :- type mrope system —> mrope system(int,int,variables). Every element of this array corresponds with information on a finite domain variable. This array is implemented as a 234-tree. For testing purposes a simplified special purpose balanced tree was written.

68

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

Program 3.24 :- type mybintree(V) —> empty ; tree(int, V, mybintree(V), mybintree(V)). :- type variables == mybintree(finitevar). :- type mrope system —> mrope system(int,int,variables). The last representation concerns the functor. In the last case every argument of the functor is information on one variable. If the information of a variable changes a new functor is created where all arguments but one, the one that changed, are copied. Then the changed information is put in the corresponding argument. Program 3.25 :- type variables —> array(finitevar,finitevar,finitevar,finitevar,finitevar, finitevar,finitevar,finitevar,finitevar,finitevar, finitevar,finitevar,finitevar,finitevar,finitevar, finitevar,finitevar,finitevar,finitevar,finitevar). :- type mrope system —> mrope system(int,int,variables). Again we ran the queens(10) program, using the different representations The Mercury program was running without bookkeeping for garbage collection. For the first time we got timings better than the version in Prolog.

Mercury array mybintree functor /20

queens(10) 47s 25s 21s

From this we can see clearly that a different data structure has a large effect on the efficiency of the program. In another version of the Mercury program checking for duplicates in the constraint queue was added. This was implemented with the help of the same binary tree used in the previous experiment. Also another example was added to the test examples. It concerns the bridge problem described in [Van89].

queens(10) bridge1(200)

MROPE 65s 5s

ROPE 39s 27s

With the checking for duplicates the queens program is slowed down again, while with the new example we get an interesting speedup. So, it is possible that some examples run 5 times faster in this pure implementation of Mercury. As we will see in the next section even better results can be obtained when using backtrackable destructive assignment.

3.3. EARLY MERCURY AS A PLATFORM FOR A SOLVER

69

3.3.2 Mercury with backtrackable destructive assignment The previous section reports on the results of a straightforward port from ROPE to Mercury : the results were not so positive for Mercury. The main reason for this seems the absence of appropriate data representations because there is no good alternative to an open ended data structure in pure Mercury. This section describes the addition of backtrackable destructive assignment to Mercury, and the effect on the efficiency of our constraint solver. In the pure version of the previous section only “in”-constraints were implemented. It was also very difficult to implement for example the cardinality constraint [VD91]. Such a constraint causes the need to remove constraints from the dependency lists connected to a variable. In Prolog this is realised by including a free variable with each constraint. To remove a constraint from a list this variable is instantiated. This is not possible in Mercury. We did not see any good and efficient alternative in Mercury than backtrackable destructive assignment to implement this feature. Preview on the gains of backtrackable destructive assignment Backtrackable destructive assignment is currently not part of the Mercury system. In version v0.5 the modes unique and almost unique were implemented, but this can only be used at the top node of a data structure. Although the modes are there, destructive assignment is not yet there, so we had to do it ourself. Before jumping into the experiment we wanted to have an idea of the efficiency gain we could get. For this purpose we used SICStus Prolog v2.1 which contains a variant of backtrackable destructive assignment in the from of the builtin predicate setarg/3. The Mercury code for MROPE was with some minor changes ported to SICStus v2.1 Prolog. We then again ran the queens(10) problem using different data representations for the constraint store and the domains of the variables. Still all variants use two extra arguments, an in and out argument. But in case setarg is used, the in and out arguments are always unified and the update is done locally. We used the same two data structures as before: a functor and a binary tree. Also in both representations a number of artificial variables were introduced. This should indicate how speed scales when larger examples are tackled. Queens(10) problem in SICStus Prolog functor/20 functor/100 mybintree mybintree with more variables

normal 120s 1000s 152s 247s

with setarg 94s 94s 120s 163s

We concluded that it was worthwhile to try to implement a variant of setarg in Mercury. As the aim is to store the information on a finite domain variable in the variable itself we can assume constant access and update. This was the case in the 94s timing.

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

70

Implementation issues To insert the code in C that implements backtrackable destructive assignment we introduced a dummy module which contains the interface for the code. The C-file generated by Mercury is then replaced with a file which contains the actual code for the backtrackable destructive assignment. Program 3.26 % dummy module to generate template for support routines % to allow backtrackable destructive update in Mercury :- module mrope du. :- interface. :- import module std util. % announce a backtrack point places a marker on the trail :- pred du backtrackpoint announce is det. % untrail all the changes after the last backtrack-point :- pred du backtrack is det. % remove a backtrack point - will remove marker from trail du backtrack % has to be called beforehand to make sure that the marker is the last % element on the trail :- pred du backtrackpoint remove is det. % set argument (1), of predicate (2) to given value (3) % this can be restored by du backtrack when needed :- pred du setarg(int::in, TP::in, TV::in) is det. :- implementation. du du du du

backtrackpoint announce. backtrackpoint remove. backtrack. setarg( , , ).

Results Given backtrackable destructive assignment also the cardinality constraint [VD91] was added to the system. With the additional cardinality constraints a wider range of examples was possible. A new version of the bridge problem was added. In this version all disjunctions are added to the system before any choices are made. The old version of the bridge problem is called “bridge1”, the new version “bridge2”. A variant of the perfect square [AB92] was added with small dimensions. Also a Japanese puzzle called suudoku.

3.3. EARLY MERCURY AS A PLATFORM FOR A SOLVER

71

For comparison the same code was executed in SICStus v2.1 using setarg because the ROPE system on ProLog by BIM and the implementation in Mercury now use totally different data structures.

queens bridge1 bridge2 perfect suudoku

MROPE(Mercury) 21s 1.9s 1.7s 23s 1.2s

MROPE(SICS) 109s 10s 11s 125s 5.5s

ROPE(BIM) 39s 27s 14s 126s 6.8s

pure MROPE 65s 5s

This table shows a bad result for the queens problem. In the version of ROPE in Prolog the different from constraint is handled at a higher level, which allows some optimisation: a constraint X Y can be removed from the constraint store as soon as one variable is instantiated. This is still not done in the Mercury version. So, ignoring the queens result, we observe that MROPE is 5 to 15 times faster than the original ROPE in Prolog. Conclusions and Future work This work reports on the use of Mercury for the implementation of a finite domain solver. This implementation is based on an implementation of such a solver in Prolog. Not all features of the original finite domain solver were implemented. For example transformation of high-level constraints to low-level constraints was not included in the new system. This resulted in the absence of some optimisations which are based on the high level constraints. This explains the bad results for queens. For a usable finite domain solving package these missing parts are essential but they will not change much the obtained results and conclusions. The main result of the experiment concerns data representation: our experiment suggests at least that a straightforward port from Prolog code to Mercury code, will not always lead to a high performance gain. The use of alternative data structures can help, but it seems paramount that because of the lack of open ended data structures, data structures can be locally updated without being completely copied. At the time of the experiment it was surely the intention of the Mercury team to add such features to the language. An initial support for this was added with the introduction of the modes unique and mostly unique in release 0.5 (we started this work at the time of release 0.4). However, in version 0.5 this information is checked, but not yet used and the occurrence of these modes is still restricted to the top level of data structures.

72

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

3.4 Using a More Mature Mercury At the time of writing this thesis the Mercury system has become a lot more mature since the experiment reported in the previous section. In the current version (0.8.1) backtrackable destructive assignment and other features, like impure declarations, were included. This makes it possible to create an efficient but still high level implementation of a Finite Domain solver.

3.4.1 Supported Finite Domain Language Predicates in the interface of the module The interface of the module is very simple: one can initialise a finite domain store; create new finite domain variables; add constraints to the store and start a search for a solution. The solver has an explicit current state, containing all finite domain variables and constraints. It has type fd store. This store is initialised with the predicate init/1. :- pred init(fd store). :- mode init(fd store muo) is det. Every finite domain variable within the system needs to be created before use. This can be done with the predicate new var/4. :- pred new var(finite var, range, fd store, fd store). :- mode new var(out, in, fd store mdi, fd store muo) is det. A new constraint is added to the system by calling add constraint/3. :- pred add constraint(constraint, fd store, fd store). :- mode add constraint(in, fd store mdi, fd store muo) is semidet. Finally enumeration can be started with enum/6. :- pred enum(list(finite var), var selection mode, value selection mode, backtrack mode, fd store, fd store). :- mode enum(in, in, in, in, fd store mdi, fd store muo) is nondet. Other variants of the enumeration exist: one in case of optimisation; one with preferred values for the variables; and one combining optimisation and preferred values. An example where enumeration with preferred values can be used is rescheduling. One can also request for the current domain of a finite domain variable with the predicate value var. :- pred value var(finite var, list(int), fd store). :- mode value var(in, out, fd store mui) is det.

3.4. USING A MORE MATURE MERCURY

73

The reader may have noticed that the predicates making up the interface of the solver use the modes fd store muo, fd store mdi and fd store mui. These modes use mostly unique and mostly dead instantiation patterns. mostly unique and mostly dead allow to specify when there is only one reference to a particular value, and when there will be no more references to that value. Mercury defines some standard modes for manipulating ”mostly unique” values: % mostly unique output :- mode muo :: free -> mostly unique. % mostly unique input :- mode mui :: mostly unique -> mostly unique. % mostly destructive input :- mode mdi :: mostly unique -> mostly dead. fd store mdi is equivalent to mdi but specific for the type fd store. fd store muo relates to muo in the same way and fd store mui relates to mui. In our case it means that when a store has been used as a parameter with mode fd store mdi, the variable referring to this store can not be used in the remainder of the current predicate. A parameter used in a call as parameter with mode fd store muo is known to be the only reference to the current store. The mode fd store mui means that the corresponding parameter is the only reference to that data, and the code of the called predicate will keep it that way. An important property of these mostly unique and mostly dead instantiation patterns is the possibility to use them in non-deterministic code. This is what mostly stands for. A parameter which is mostly dead cannot be used in future code, and is therefore dead, but it could come alive again after backtracking. Types used in the Interface The range-type, which is used to specify a domain of a variable is a bit awkward, but it allows expressing the domains very easily. Also infinite domains are allowed. :- type range —> num(int) /* A singleton */ ; int(int, int) /* An interval the bounds included */ ; unn(int, range) /* A union of an integer and a remaining range */ ; uni(int, int, range) /* A union of an interval made by the first two arguments and another range */ ; inf /* An infinite domain open two both ends */ ; unl(int, range) /* A union of an interval from minus infinity up to the first argument with the range in the second */ ; opl(int) /* An interval from -infinity up to the argument */ ; opr(int). /* An interval from the argument up to plus infinity */ A constraint has the following type:

74

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

:- type expression —> num(int) ; var(finite var) ; +(expression, expression) ; -(expression, expression) ; *(expression, expression). :- type constraint —> (expression, expression) ; =(expression, expression) ; ==(expression, expression) ; >(expression, expression) ; io write string(”number of queens?”), fnon logical io read int(NQ)g, (f init(Store1), generate(Q, NQ, NQ, Store1, Store2), safe(Q, Store2, Store3), enum(Q, up, up, standard ex, Store3, Store4), non logical io write string(”[”), output list(Q , Store4), failg -> ftrueg; io write string(”no solutions”) ). :- pred generate(list(finite var), int, int, fd store, fd store). :- mode generate(out, in, in , fd store mdi, fd store muo) is det.

3.4. USING A MORE MATURE MERCURY

generate(Q, N, M) –> ( fN =0g -> fQ = []g; new var(X, int(1, M)), fN1 is N - 1g, fQ = [XjQ1]g, generate(Q1, N1, M) ). :- pred safe(list(finite var), fd store, fd store). :- mode safe(in, fd store mdi, fd store muo) is semidet. safe(Q) –> ( fQ = []g -> ftrueg ; fQ = [X—T]g, noAttack(X,1,T), safe(T) ). :- pred noAttack(finite var, int, list(finite var), fd store, fd store). :- mode noAttack(in, in, in, fd store mdi, fd store muo) is semidet. noAttack(X, N, Q) –> ( fQ = []g -> ftrueg ; fQ = [YjZ]g, add constraint((var(X), +(var(Y), num(N)))), add constraint((var(X), var(Y))), add constraint((var(Y), +(var(X),num(N)))), fS is N + 1g, noAttack(X, S, Z) ). :- pred output list(list(finite var), fd store). :- mode output list(in, fd store mui). output list(Q, Store):( Q = [X], value var(X, [Vj ], Store), non logical io write int(V), non logical io write string(”]”) ; Q = [X, YjQ1], value var(X, [Vj ], Store), non logical io write int(V), non logical io write string(”, ”), output list([YjQ1], Store) ).

3.4.2 Data structures and implementation A finite domains solver has three main data structures:



domains attached to the finite domain variables,

75

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

76

 

constraints and a queue of constraints to be checked in the fixpoint algorithm.

For all operations on these data structures: creation, retrieval and update, it is important to achieve constant time operations. In this section we will show that Mercury allows to define and use such data structure with a minimum of low-level programming. The lowlevel programming concerns the use of backtrackable destructive assignment. A small module mutable defines such operations: :- module mutable. :- interface. :- type mutable(T). /* a polymorfic mutable object */ :- pred mutable init(mutable(T), T). :- mode mutable init(out, in) is det. /* create a new mutable object with the initial contents */ :- pred mutable overwrite(mutable(T), T). :- mode mutable overwrite(in, in) is det. /* Overwrite the current contents of the mutable object with new data. When the system backtracks over this operation the old data will be restored */ :- pred mutable get(mutable(T), T). :- mode mutable get(in, out) is det. /* Get the current contents of the mutable object */ These operations are implemented using the C-interface of the mercury system: :- pragma(c code, mutable init(Mutable::out, Value::in), will not call mercury, ”f Word *mutable; mutable = make(Word); (*mutable) = Value; Mutable = (Word) mutable; g”). :- pragma(c code,mutable overwrite(Mutable::in,Value::in), will not call mercury, ”f Word *mutable; mutable = (Word *) Mutable; MR trail current value(mutable); (*mutable) = Value;

3.4. USING A MORE MATURE MERCURY

77

g”). :- pragma(c code, mutable get(Mutable::in, Value::out), will not call mercury, ”f Word *mutable; mutable = (Word *) Mutable; Value = *mutable; g”). Using the mutable object a whole new range of new data structures can be used: doubly linked lists, difference lists, : : : . On creation of a new variable, the implementation creates a number of slots containing mutable objects.

 



A slot for the current domain. Five slots for lists of constraints. Each of those five slots contain constraints with a different propagation scheme. The constraints in the first slot are only propagated whenever the domain of the variable becomes a singleton, the second list of constraints becomes active when the lower-bound of the domain changes, the third when the upper bound changes. A fourth list of constraints is activated when the upper bound or the lower bound changes5. The last list of constraints is propagated whatever change to the domain is made. An extra slot can be added containing a string, describing a problem specific meaning of the finite domain variable (can be used for debugging or output purposes).

For storing the constraints in one of these five slots holding constraints a doubly linked list is used. This way new constraints can be added, old constraints deleted and constraints retrieved at constant time. The queue of constraints maintained by the fixpoint algorithm is a difference list. The only way to implement a difference list in the current version of Mercury is using backtrackable destructive assignment. This way constraints can be added and removed in constant time. An important property is the ability to concatenate queues in constant time. Of course special attention has to be paid when using these mutable data structures, bugs in these parts of the program will never be found by the compiler. Fortunately the size of the non-declarative code is rather small, and the advantage of using Mercury is gained for the remainder of the code.

3.4.3 Comparison A small comparison was made between this new implementation (MROPE II), the clp(fd) library [COC97] from SICStus, the old implementation of ROPE in Prolog on top of SICStus, and CHIP, one of the leading commercial products in the area having a lowlevel implementation. 5 In previous version this slot was not available, all constraints to be activated as soon as the lower bound or upper bound changed were added twice to the constraint store for the variable. Once in the list to be activated when the lower bound changed, and once to the list to be activated when he upper bound changed.

78

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

benchmark queens 8 (all solutions) queens 25 (first solution) bridge1 bridge2 suudoku

MROPE II 0.12 5.37 3.35 2.03 0.31

SICStus 0.25 13.45 1.96 2.19 0.16

ROPE(SICStus) 1.06 45.27 8.79 4.44 0.2

CHIP 0.08 2.77 0.82 1.13 0.18

Table 3.1: Comparing the solver in Mercury with other systems on a SparcII

Five small benchmarks were selected. The classic queens problem, finding all solutions for queens(8) and finding the first solution for queens(25). Two implementation of the bridge problem [Van89], the version bridge1 backtracks over the disjunctions for finding the optimal solution, the version bridge2 first expresses the disjunctions connecting a boolean variable to each of the disjunctions. In the end the boolean variables are enumerated for finding the optimal solution. In principle the last version should be superior, since the disjunctive constraints are brought into the store while delaying the choice. These constraints can start pruning before they are decided on. The last benchmark is suudoku, a Japanese puzzle. The code of these benchmarks is added in appendix C. The benchmarks were performed on both a Solaris machine with a SparcII processor running at 167 Mhz and a Linux machine having a Pentium II 233Mhz. On the Solaris machine the SICStus compiler generates “fastcode”, on the Linux machine the SICStus compiler generates “bytecode”, this explains the slower execution on the linux box. From table 3.1 we can see the Mercury implementation of our solver approaches the timing of the solver [COC97] delivered with SICStus. The Mercury implementation outperforms the Prolog implementation ROPE with grace. Our system is still two or three times slower than CHIP, and of course does not have the global constraints of CHIP. The results of running the same benchmarks on Linux, are shown in Table 3.2. Here the Mercury implementation is superior to the SICStus implementation, because the last one is using “bytecode” on Linux. benchmark queens 8 (all solutions) queens 25 (first solution) bridge1 bridge2 suudoku

MROPE II 0.05 2.7 1.57 0.85 0.06

SICStus 0.42 15.7 2.57 1.73 0.18

ROPE(SICStus) 1.79 78.69 14.11 7.79 0.35

Table 3.2: Comparing the solver in Mercury with other systems on a Pentium II

3.5. CONTRIBUTION

79

3.5 Contribution This chapter contributes on the level of new concepts in Finite Domain CLP languages and implementation of CLP languages on top of Logic Programming systems. Two novel concepts were introduced: prune declarations and parameterised backtracking. Prune declarations are a higher level construct, avoiding the need for Indexical Constraints. This is valuable since Indexical constraints are an amalgamation of declarative specification and procedural behaviour. Moreover, Indexical constraints are not guaranteed to have monotonic behaviour. The methods used in parametrised backtracking have been noticed by other people as well. In these cases this resulted in an ad hoc implementation of the recognised problem. A parameterised backtracking generalises the recognised problem and provides the technology to other users without additional effort. The implementation of the ROPE system on top of Prolog shows the feasibility of transforming CLP programs to lower level executable Prolog programs. In also indicates that transforming prune declarations into Indexical Constraints is feasible. Moreover, the implementation of ROPE was valuable for experiments with the cardinality [VD91] and other global constraints. The implementation of the solver ROPE on top Mercury shows that, with the availability of backtrackable destructive assignment, such an approach can compete with more low-level approaches.

80

CHAPTER 3. THE ROPE SYSTEM: CONCEPTS AND IMPLEMENTATION

Chapter 4

Proving a query can never succeed This chapter is on finding a proof that a query, given a logic theory, can never succeed, without executing the program. This is valuable for programs, like planners, which are known to loop if no solution exists. This problem was solved by combining several technologies: pre-interpretations, Constraint Logic Programming and tabling. The system is based on finding a model based on a pre-interpretation where the query is false. The main difficulty is the large search space of pre-interpretations. For this reason choices on the pre-interpretation are delayed as long as possible. During the search, constraints are generated on the pre-interpretation. CLP is used to check these constraints and find solutions. The use of tabling is very specific here since the program developed reasons with unknown pre-interpretations. Also a specific solver was developed to speed up the constraint solving.

4.1 Introduction Theories in first Order Languages are semi-decidable. The aim of automated systems for First Order Languages is, given a sentence A and a program P, decide whether P j= A. Semi-decidable means that the best one can do is build a system that always answers yes, in finite time, when P j= A. But, there will always be a opposite example where the procedure loops for a combination of a program P and a query A, where P j= A is not true. On top of this, systems processing an SLD tree depth first, like Prolog, could go into an infinite loop even when the SLD tree contains a solution. Within different lines of research, attempts have been made to cure this problem: termination analyses [DD94], loop checking [ABK89] and tabling [SSW94]. Within Termination Analysis the main stream of automated systems search for a proof that the SLD tree is finite for a class of queries and a specific computation rule. These systems analyse 81

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

82

the size of syntactic data structures within the program for constructing such proofs. As the termination of programs is undecidable these methods have to be conservative and fail for a substantial class of programs. Loop checking methods try to detect loops while monitoring the execution. As soon as they detect an infinite loop the execution is aborted and failure is reported for the current branch of the computation. Such methods can either remain correct, but as FOL is semi-decidable, infinite branches remain possible. Or the system cuts too many branches and the resulting procedure is incomplete. Tabling on the other hand is a technique that checks whether a call is a syntactic variant of a previous call, within the same or a previous branch, and when this is the case uses the result of that previous call. Tabling can be seen a generalisation and more powerful form of loop checking. There is a substantial class of interesting programs and corresponding queries which have an infinite SLD tree. For these programs a termination proof is impossible and loop checking nor tabling can cut these infinite branches. An example of such a programs is a planning system [dT95]. A basic technique in such systems is to use a generate and test procedure: a plan is generated and then tested for correctness, if not another plan is created. This process is repeated until a correct plan is found. For practical cases the number of plans than can be generated is infinite. When some property in the theory or query rejects all plans the planner is stuck in an infinite loop. Usually the plans generated get larger and more complex such that loop checking nor tabling can avoid the loop. For these systems any checking system that can detect that some of the queries given a theory cannot have solutions could be helpful. The basic idea would be to run in parallel both the program at hand and the program trying to find a proof for failure. When one of the processes returns a positive answer the other process is stopped. A trivial example of a program where the query does not terminate is: Example 4.1 even(zero). even(s(X)):- odd(X). odd(s(X)):- even(X). ?- even(X), even(s(X)). When the corresponding query is executed under Prolog the first call to even will create larger an larger terms, while the second call to even will always fail. The basic concept for proving failure for a query A using a program P relies on finding A). The method is similar to a compilation as in [CD95], a finite model for (P [ false explained in further detail below. Such a compilation abstracts the original program (and the query) to a DATALOG program. In principle, queries for DATALOG programs can be decided on in finite time using bottom-up least model generation as done in [CD95], or top-down execution using tabling [SSW94]. In our case, since the pre-interpretation is unknown, we use a top-down evaluation using a kind of tabling. This procedure generates conditions on the pre-interpretation that, if satisfied, ensure failure of the initial

4.2. MODELS BASED ON A PRE-INTERPRETATION

83

query. Since the search of a pre-interpretation satisfying the conditions is a combinatorial problem we use constraint techniques for this part of the system. For a bounded number of values in the domain of the pre-interpretation the method, including the top-down procedure proving failure and the constraint solving, generates a finite search tree but still shows an exponential behaviour. Next to using Constraint Programming for solving this problem, the main novelty in the mechanism is the use of tabling for predicates only partially defined. These predicates are only partially defined since they rely on open predicates representing the pre-interpretation.

4.2 Models based on a pre-interpretation The way we solve the problem is by trying to find a finite model for the program where the query is false. Since the programs contain functors and we are looking for a finite model this will not be a Herbrand interpretation. We first start with defining the terminology.

4.2.1 Terminology



Pre-interpretation A pre-interpretation J of a program P consist of – a domain D = fd1 ; d2 ; : : : ; dm g, – a set of mappings J , with for every functor f =n in the program P a mapping pre f =n : Dn ! D:

(4.1)

Constants are 0-arity functors. A possible pre-interpretation for the program in Example 4.1 is: Example 4.2 D = fd1 ; d2 g prezero=0 () = d1 pres=1 (d1 ) = d2 pres=1 (d2 ) = d1





An interpretation based on a pre-interpretation An interpretation based on a pre-interpretation < D; J > is a set of mappings I : for each predicate p=n in the program P a mapping I p=n : Dn ! f f alse; trueg. Such an interpretation M based on a pre-interpretation < D; J > is a model for P iff all clauses are true under the interpretation M . Mapping the Herbrand universe Given a pre-interpretation < D; J > a mapping map from each ground term constructed with functors and constants to the domain D is precisely defined. An image

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

84

of such a term can be found by recursively applying the pre-interpretation on the innermost constant or functor of the term. With the pre-interpretation of Example 4.2: Example 4.3 map(s(s(zero))) = d1 map(s(s(s(zero)))) = d2





A corresponding Herbrand interpretation for the original program Given an interpretation I based on a pre-interpretation we can construct an interpretation I  for the original program P: I  is again a set of mappings containing for each predicate p=n a mapping I p=n : (Herbrand )n ! f f alse; trueg where I p=n(t1 ; : : : ; tn ) = true , I p=n (map(t1 ); : : : ; map(tn )) = true. If M is a model based on a pre-interpretation for P, then the corresponding Herbrand interpretation M  is also a model for P. Some properties – A definite program always has a model based on a pre-interpretation: the model where all predicates in I map to true. – The intersection of two models is again a model and as a consequence there is always a unique least model.

– If an existential quantified conjunction 9X L1 ^ : : : ^ Ln is false in a model based on a pre-interpretation it is also false in the least model based on that preinterpretation. Hence, to check whether a existentially quantified conjunction is false in some model based on a pre-interpretation, it suffices to evaluate the conjunction in the least model based on the pre-interpretation. – If M is the least model based on a pre-interpretation for P then the corresponding model M  is a Herbrand model for P, but not necessarily the least model. If a model based on a pre-interpretation, where the query is false, can be found, the query is not true in the least Herbrand model. Therefore no SLD resolution will ever find an answer for that query (but can get trapped in a loop).

4.2.2 The transformation by Codish and Demoen [CD95] In the work by Codish and Demoen the issue is finding the least model for a program based on a pre-interpretation. This least model is then the result for a groundness analysis. Therefore the pre-interpretation defines exactly the groundness propagated during unification. The least model then gives precise information on the groundness properties of all predicates in the program. Their method consists of transforming the original program into an abstract DATALOG program. Giving the following terminology for the original program:

4.2. MODELS BASED ON A PRE-INTERPRETATION

85

The original program is a definite Horn clause program P containing a set of definitions for predicates p/n. In the arguments of those definitions we can find terms built from constants c, functors f/n and variables X. You can find an example of such a program in Example 4.1 with definitions for even/2, odd/2, with arguments s(X) and zero using the constant zero and the functor s/1 and variables as capital letters. The transformation consists of: We transform the original program P to a DATALOG program P’ by replacing each occurrence of a functor f/n by a fresh variable. For every replacement we add a call to f/n+1 in the body, where the first n arguments are the same as the original and the last argument becomes the fresh variable we used to replace the functor. Constants can be treated as functors with zero arguments. For Example 4.1 the transformed program is then: Example 4.4 even(Y):- zero(Y). even(Y):- s(X, Y), odd(X). odd(Y):- s(X, Y), even(X). ?- even(X), s(Y, X), even(Y). The resulting program is a DATALOG program, where the definitions of the new predicates f/n+1 deduced from the functors and constants are missing. In the example s/2 and zero/1. For groundness analysis in [CD95] the pre-interpretation was known. Such a preinterpretation can be added to the transformed program P’ becoming P” by defining the new predicates f/n+1 such that f (v1 ; : : : ; vn ; vn+1 ) 2 P00 , pre f =n(v1 ; : : : ; vn ) = vn+1 For Example 4.1 and the pre-interpretation in Example 4.2 we get: Example 4.5 even(Y):- zero(Y). even(Y):- s(X, Y), odd(X). odd(Y):- s(X, Y), even(X). zero(d1 ). s(d1 , d2 ). s(d2 , d1 ). The core of the problem in proving failure is that, in contrast to the groundness analysis, the pre-interpretation is not given. Then the problem is reformulated to: finding a preinterpretation such that the query is false in the least model.

86

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

4.3 Finding a model where the query is false Suppose we fix the number of elements in the domain D of the pre-interpretation. Then, as the number of functors in a program is finite, the number of different pre-interpretations < D; J > for that domain D is finite. Given a pre-interpretation we can easily compute the least model by computing the least fixpoint with the transformed program P’, as done in [CD95]. Then the only thing left is checking whether the query is false. However, n with an m-element domain an n-arity functor has m(m ) possible pre-interpretations. Even for simple programs this leads to a huge number of possibilities. In our approach we want to avoid enumerating all possible pre-interpretations. We do this by building a set of conditions on the pre-interpretation, such that at each step we know that there exists a pre-interpretation that satisfies the conditions. At the end, the complete set of conditions ensures there exist a model based on any pre-interpretation that satisfies these conditions. Generating these conditions is done by executing the query with tabling over the unknown pre-interpretation. These conditions on the pre-interpretation are translated to a set of constraints over finite domain variables.

4.3.1 Executing a program under a pre-interpretation In Section 4.2.2 we explained in detail how programs can be transformed to DATALOG programs, where all functors f =n were replaced by a fresh variable Xn+1 and a call to f =n + 1 is added to the clause. This new call takes as first n arguments the arguments of the original functor f =n and Xn+1 as last argument. The set of new “open” predicates f =n + 1 represents the unknown pre-interpretation to be found. In our case we will not perform this transformation, but the program execution will behave as if the transformation had been done: Whenever two functors are unified, the values in the pre-interpretation will be unified. When we mention execution under pre-interpretation in the text below we mean that we execute the program as if the former transformation has been applied. This behaviour is defined by the following meta interpreter: Program 4.1 1 solve([]). 2 solve([CalljCalls]):3 functor(Call, Name, Arity), 4 functor(NewCall, Name, Arity), 5 program(NewCall,Body), 6 metaUnify(Call, NewCall), 8 append(Body, Calls, NewCalls), 9 solve(NewCalls). 10 11 metaUnify(Call1, Call2):12 Call1 =.. [Name—Args1], Call2 =.. [Name—Args2], 13 map(Args1, MappedArgs1), map(Args2, MappedArgs2),

4.3. FINDING A MODEL WHERE THE QUERY IS FALSE

14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30

87

MappedArgs1 = MappedArgs2. map([], []). map([ArgjArgs], [MappedArgjMappedArgs]):(var(Arg) MappedArg = Arg; (Arg = d( ) MappedArg = Arg; Arg =.. [NamejFunctorArgs], map(FunctorArgs, MappedFunctorArgs), NewArg =.. [NamejMappedFunctorArgs], pre(NewArg, MappedArg) ) ), map(Args, MappedArgs). append([], X,X). append([XjY], Z, [Xj W]):append(Y, Z, W). The example defined in Example 4.1 is then added in the following format:

Program 4.2 program(even(zero), []). program(even(s(X)), [odd(X)]). program(odd(s(X)), [even(X)]). pre(zero, d(1)). % functor d/1 is reserved for representing domain elements. pre(s(d(1)), d(2)). pre(s(d(2)), d(1)). The main part of the meta interpreter is defined from line 1 to line 9. When calling a predicate we have to avoid normal unification. To this aim in line 3 and line 4 a new call is generated with fresh variables as arguments. Then these new call is used in line 5 to call the definition for the predicate. When clauses are found the arguments in the head of the definition are unified with the arguments of the original call in line 6. This metaUnify will take into account that different functors may unify under the pre-interpretation. In line 9 and 10, the body of the selected calls is added to the list of goals and solve is called recursively. Remark that the call to program in line 6 backtracks over all definitions of the called predicate. The predicate metaUnify will unify two calls taking into account preinterpretations. For both calls all functors will be replaced by the predicate map. After this replacement the arguments can be unified. The predicate map does most of the work. For each functor in the arguments the pre-interpretation is used to find out its value in the domain of the pre-interpretation. But in advance all arguments of such functors have to be handled recursively by map. Remark to be able to proof that the query solve([even(s(X)), even(X)]). fails given a pre-interpretation, the program above needs tabling to avoid loops.

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

88

4.3.2 Executing a program under an unknown pre-interpretation The program above has two problems: its execution requires a known pre-interpretation and it loops. The looping can be solved with tabling. The problem with the unknown preinterpretation could be solved by abduction. The pre/2 predicates becomes an abducible predicate and the definitions for it are abduced during execution. This line of work was followed as reported on in the first part of [BVdD98]. The basic idea of the method explained here, (also reported in the second part of the paper [BVdD98]) is to execute the query, we want to prove false, under an unknown preinterpretation such that constraints are generated on that pre-interpretation. In case there exist a pre-interpretation under these constraints and all constraints have been generated, we have found a proof for the fact that the query can never succeed in the original program. To break the loops from the original program we use also a sort of tabling. As the execution under a pre-interpretation behaves as a DATALOG program we are sure to avoid all loops. The constraints generated on the pre-interpretation has two sources:



Unification Terms that do not unify in the original program could unify under a preinterpretation. In our case, as we do not know the pre-interpretation yet, we store these unifications as “conditions” for the solutions and goals that depend on the success of these unifications. For all solutions resulting from the query we impose that the “conditions” that go with the solutions must be false. Only unification of two terms, where both terms are not a variable, leads to such conditions, otherwise normal unification can be done. Example 4.6 One could find that “even(s(zero))” is a solution for “even(X )” iff “s(zero)” is equal to “zero” under the pre-interpretation. As the pre-interpretation is unknown, this condition is kept with the solution: even(s(zero)) :



fPres zero (

)

= Prezero

g

:

Tabling When finding a new solution for a call to a tabled predicate two things can happen. Either the new solution maps on the old solutions or this solution does not map on the old solutions. Of course as the pre-interpretation is not yet known we cannot check this. The solution is to generate a choice point. First we map the solution on the old ones and generate restrictions on the pre-interpretation such that this is assured. In case this fails (we cannot find a pre-interpretation with a failing goal) the system backtracks and the solution is added as a new solution. At this time we also generate the redundant constraint that the solution should not map on the old ones. This constraint is redundant because adding the negation to the set of constraints failed. Still it can provide additional pruning, cutting away pre-interpretations that otherwise only get rejected after long search. Example 4.7 Suppose we already have the solution “even(zero)” for the predicate even(X). When a new solution “even(s(s(zero)))” is computed, the condition

4.3. FINDING A MODEL WHERE THE QUERY IS FALSE

89

“Pres(s(zero)) = Prezero ” results in subsumption of this new solution. The execution can at any time be represented by a tuple goals: Gs, table: Table, constraints: Constraints >





0

 2)  2)

Of course this could also be expressed by stating: Example 4.12 Ea b = 1 There is a method to generalise this optimisation for expressing subsumption (or no subsumption) of a new solution by old solutions having free variables in the arguments. The idea is to replace, when expressing subsumption constraints, old non-ground solutions by semi-ground solutions. A semi-ground solution is a solution where only variables occur in the argument positions of the solution. Then the solution p(X ; Y ; Y ; Z ; s(Z ; W )) will have four semi-ground versions: Example 4.13 p(X 0 ; Y 0 ; Y 0 ; d1 ; s(d1 ; d1 )), p(X 00 ; Y 00 ; Y 00 ; d1 ; s(d1 ; d2 )), p(X 000 ; Y 000 ; Y 000 ; d2 ; s(d2 ; d1 )) and p(X 0000 ; Y 0000 ; Y 0000 ; d2 ; s(d2 ; d2 )) in a two element domain for the pre-interpretation. When generating constraints for subsumption (no subsumption) there are two cases:

 

The variable in an argument of a solution in the table occurs only once. Then the corresponding argument in the new solution will always be subsumed by the old solution: this argument in the new solution can be discarded. One can generate constraints as if in both solution (old en new) the argument has been removed. If the variable occurs more than once, we have to express equality for subsumption, inequality for no subsumption, of the corresponding arguments of the new solutions.

If we have to express that a solution p(a; b; c; e; f ) is subsumed by the first semiground alternative p(X 0 ; Y 0 ; Y 0 ; d1 ; s(d1 ; d1 )) we have: Example 4.14 Mapping1 , Eb

c + Ee d1 + E f s(d1 ;d1 )

3

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

98

4.6 Termination of the procedure As already mentioned above in 4.3.1, when executing a program under a pre-interpretation the system behaves as executing a DATALOG program. When executing a DATALOG program using tabling a program always terminates. Of course in our case things get more complicated as we execute under an unknown pre-interpretation. The following proof only holds when the number of domain elements m is fixed.

 

      

The number of tables is finite, and is limited by the number of defined predicates in the program P. The number of solutions in a table is limited by the am i where ai is the arity of the predicate i and m the number of elements in the domain of the pre-interpretations. This is because we impose that when adding a new solution, this solution may not be subsumed by the previous one. As subsumption is expressed in the domain of the pre-interpretation, am i is a maximum for the total number of solutions. The number of delayed goals in the table for predicate pi is bounded by the number of calls in the program P and the query to pi . Suppose ti is an upper bound for the number of delayed goals for predicate pi in the program P. In each branch of the search a maximum of ∑ predi am i backtrack points exists where the second alternative has been taken, where ai is the arity of predicate i. Because, whenever the second alternative is taken, a new solution is added. Then, for each predicate, the number of goals added to the list of goals is limited by the product of the maximum number of solutions and the maximum number of delayed goals: ∑ predi am i  ti . Whenever the first alternative of a choice point is used, a goal disappears from the list of goals. Hence the number of choice point, where the first alternative is used, is bounded. As a consequence the number of choice points is bounded for each branch of the search space. And, as the number of branches for each choice points is limited to 2, also the number of branches is limited. Whenever a rule from the procedural engine is applied, except for the first one, one goal disappears from the list of goals. As the number of goals added to the list of goals is limited, also the number of goals removed is limited. As a result, the length of each branch in the computation is bound. As the length of each branch is bounded and the number of branches is bounded, the total computation will terminate.

Although a nice property, such a termination proof does not enforce that the process will terminate within reasonable time. Still the behaviour of the program is exponential.

4.7. ALTERNATIVE APPROACHES

99

4.7 Alternative approaches Plain abduction and tabling The method published in [BVdD98] based on abduction (AB) and tabling executes the transformed program as described in Section 4.2.2 with an undefined pre-interpretation. Whenever the unknown predicates defining the pre-interpretation are called, abduction is used to create an answer. Several control strategies were investigated in this approach. Finally the strategy that delays the creation of choicepoints as far as possible was superior. The system is augmented with intelligent backtracking2 and symmetry checking3. Model generation. The logic program and the clause false query can be considered as a logical theory. A model of this theory is a proof that the query fails. There exist general purpose tools for generating models of logical theories. FINDER [Sla94, Sla97], written in C, is such a tool, it takes as input a set of clauses in a many-sorted first order language, together with specifications of finite cardinalities of the domains for the sorts, and generates interpretations on the given domains which satisfy all the clauses [Sla94]. A basic difference with our approach is that it not only enumerates the pre-interpretation but also the interpretation (the mapping from the atoms to true or false). The system checks whether all ground instances of all clauses are true in the candidate interpretation. If a clause instance is found which is false, then the components of the interpretation which have been used in the evaluation make up what we called the conflict set and are used to direct the search. Another difference is that FINDER stores conflict sets permanently (unless they are too big) and uses elaborate algorithms to exploit them efficiently in pruning the search. FINDER reports a model when it discovers an interpretation which is true for all clause instances. FINDER uses a typed language and can only handle binary predicates and functors. To overcome that restriction we used a special encoding for predicates and functors of higher arity. For example, with n the size of domain D and f a ternary functor, we introduce a domain Dh with cardinality n  n, a binary functor fh : D  D ! Dh , and a binary functor fb : Dh  D ! D. Then we replace every occurrence f (t1 ; t2 ; t3 ) by fb ( fh (t1 ; t2 ); t3 ). In a recent paper, Peltier [Pel98] presents a new system FMCAT INF which claims to do a better failure analysis than FINDER and SEM [ZZ95], and exploits symmetries to further improve the pruning of the search space. As other finite model builders for first order logic, it enumerates the full interpretation, not only the pre-interpretation. Our understanding is that the concept of covering refutation used to prune the search is very similar to the use of intelligent backtracking (which we added to the abductive system described in [BVdD98] after the authors learned about the work of Peltier). 2 Intelligent Backtracking was pioneered by M. Bruynooghe and L. M. Pereira [Bru78, Bru81, PP79, BP84]. An alternative description can be found in [CCF88, CS91]. The aim of Intelligent Backtracking is to skip backtrackpoints irrelevant for the reason of failure backtracking from. This is done by storing some extra information while computing and use this information for analysing the reason for failure. 3 When checking for symmetry one could find that a current situation has occurred before modulo a permutation and if that other situation led to failure, the current leads to failure as well.

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

100

Regular approximations. Within the context of program analysis, the most obvious apquery(X) and to use proach to prove failure is to add a clause shouldfail(X) one or another kind of type inference to show that the success set of shouldfail(X) is empty. A typical representative of such systems is described in [Gd94] which computes a regular approximation of the program. Roughly speaking, for each argument of each predicate, the value it can take in the success set4 are approximated by a type (a canonical unary logic program). Failure of the query is proven if the types of shouldfail(X) are empty. Also set based analysis [Hei92] can be used to approximate the success set. Program specialisation. One could also employ program transformation, and more specifically program specialisation techniques to prove failure of the query. If for the given query, the program can be specialised in the empty program, then the query trivially fails. A technique which has almost the same power as transformations based on the fold/unfold approach is conjunctive partial deduction [LDd96]. By specialising conjunctions of atoms instead of single atoms, it can achieve substantially better results than other specialisers. For example it can specialise the even/odd program into the empty program for our example query.

4.8 Benchmarking Table 4.1 gives details about the benchmarks. Besides the name, the table gives the size of the domain, the number of involved functors, the size of a pre-interpretation, the number of predicates, the number of clauses (without the query), and the number of different pre-interpretations (including symmetric ones). The code of the benchmarks is given in Appendix A.2. What follows is a short description of the different examples.

 

odd even is a trivial example about even and odd numbers. wicked oe is an extension which adds a call to each clause and 4 functors which are irrelevant for success or failure. It allows to see whether failure analysis is accurate enough to achieve the same level of pruning as in odd even. appendlast, reverselast, nreverselast and schedule are small but hard examples from the domain of program specialisation: a powerful specialiser can specialise them into the empty program. The query for appendlast expresses the integrity constraint that appending a list ending in a cannot result in a list ending in a b. The query for reverselast expresses that reversing a list with the accumulating parameter initialised as [a] cannot result in a list ending in a b. The query for nreverselast expresses naive reverse applied on a list beginning with an a cannot result in a list ending in a b. Finally, the schedule program is a program that attempts to transpose successive positions in a list of elements until a configuration is reached with two successive c elements. The query expresses the

4 The

set of ground atoms which are logical consequences of the program.

4.8. BENCHMARKING

name odd even wicked oe appendlast reverselast nreverselast schedule multiseto multisetl blockpair2o blockpair3o blockpair2l blockpair3l blocksol BOO019-1

domain 2 2 3 3 5 3 2 2 2 2 2 2 2 3

101

functor 2 6 4 4 4 4 4 4 9 15 9 15 9 4

size 3 10 12 12 28 12 7 7 19 36 19 36 19 32

predicates 2 3 2 2 3 6 1 2 3 3 5 5 5 1

clauses 3 4 4 4 6 12 7 4 15 15 14 14 14 4

#pre 23 210 312 312 528 312 27 27 219 236 219 236 219 332

Table 4.1: Properties of benchmark programs

integrity constraint that such configuration cannot be reached from a configuration consisting of one c followed by one or more n.

 



multiseto and multisetl are programs to check the equivalence of two multisets, the first uses a binary operator “o” to build sets, the second uses a list representation and auxiliary predicates to manipulate the list. The others are typical examples from a large set of planning problems reasoning on multisets of resources. The first two use the “o” representation for the multiset, the next two the “l” representation. blockpair2o and blockpair2l omit the for success or failure irrelevant argument for collecting the plan (and have 6 functors less). blocksol is there to show what happens when the query does not fail. It uses the list representation and also omits the argument collecting the plan. Finally, tbool is an axiomatisation of a ternary boolean algebra, a typical problem from the theorem proving community (problem BOO019-1 from the TPTP library [SS97]). Its only predicate is equality, whose interpretation can be fixed to the identity.

The abductive procure (AB) was implemented in Prolog and the queries have been executed with MasterProlog on a SUN sparc Ultra-2. The constraint system CS is also written in Prolog, uses the SICStus finite domain solver and was running under SICStus Prolog [Swe97] on the same machine. FINDER is implemented in C and was running again on our SUN sparc Ultra-2. Regular approximations (RA) were computed with a

102

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

system due to John Gallagher, conjunctive partial deduction (CPD) with a system due to Michael Leuschel. Witold Charatonik was so kind to run our examples on a tool (some info can be found in [CP98]) for set based analysis (SBA) he developed together with Harald Ganzinger, Christoph Meyer, Andreas Podelski and Christoph Weidenbach. Finally, Nicolas Peltier was so kind to run our examples on the FMCAT INF system reported in [Pel98] on a sun4 ELC. Table 4.2 gives the times for the various systems. The times are in seconds, unless followed by H, in which case it is in hours. The notation >xH means no solution was found after x hours. For CPD, RA and SBA, we do not give times as these systems do not perform an exhaustive search but a standard analysis of the given program. yes means failure was proven. As blocksol does not fail, it was not run on these systems. Table 4.3 gives the number of backtracks. For the constraint system, two numbers are given, #bckt is the number of backtracks inside the solver, i.e. those wrt. the pre-interpretation, #Tbcktr is the number wrt. the choices made regarding tabling. name odd even wicked oe appendlast reverselast nreverselast schedule multiseto multisetl blockpair2o blockpair3o blockpair2l blockpair3l blocksol BOO019-1

AB 0.01 0.07 0.53 0.38 5.87H 0.10 0.04 0.01 1.83 7.60 2.83 29.24 200.78 1.20

CS 0.00 0.00 0.45 3.70 >19H 0.31 0.04 0.06 0.38 0.42 2.36 2.49 7.7H 4.34

FINDER 0.02 0.02 0.17 0.17 >59550.48 0.03 0.02 0.02 0.08 0.18 0.05 0.12 1896.3 0.03

FMCAT INF 0.00 0.01 45.21 10.79 >900 0.15 0.02 0.08 7.31 >900 204.9 >900 >900 0.06

CPD yes yes yes no yes no no yes no no no no no

RA yes yes no no no no yes no no no no no no

SBA yes yes yes yes no yes yes yes no no no no no

Table 4.2: Times. The results for individual examples have to be interpreted with care. A small change in the control can result in a different enumeration order and a quite different result. For example, a slightly different version of the abductive system AB solves blockpair3l in 2.60s with 18 backtracks. The FINDER system is very sensitive to the order of functors and constants in the input file. The initial input files used for FINDER gave very large execution times and backtracks, many of the planning problems were not solvable within reasonable time. By accident we noticed that putting the constants before the functions yielded much better results. Comparing the twin problems odd even and wicked oe, blockpair2o and blockpair3o, blockpair2l and blockpair3l, we observe that CS with the finite domain solver is almost not distracted by the (for the failure)

4.8. BENCHMARKING

name odd even wicked oe appendlast reverselast nreverselast schedule multiseto multisetl blockpair2o blockpair3o blockpair2l blockpair3l blocksol BOO019-1

103

AB #bcktr 4 64 43 30 190170 24 10 3 17 56 28 130 3615 72

CS #bcktr 0 0 24 68 ? 13 7 6 25 25 3943 4009 1396146 4

#Tbcktr 0 0 1 2 ? 1 0 1 0 0 2 2 385 0

FINDER #bcktr 1 8 618 641 7 > 5:10 37 0 12 262 879 68 366 4007523 14

FMCAT INF #bcktr 3 52 110019 23445 ? 497 104 469 5567 ? 91404 ? ? 33

Table 4.3: Amount of backtracking.

irrelevant extra functors. Apparently, its control strategy is such that those extra functors are enumerated as the last ones, when the more constrained functors already received a correct assignment in the domain of the pre-interpretation. The failure analysis of the abductive system AB includes those functors in conflict sets so that the backtracking becomes less accurate and more backtracks occur before a solution is found. This also holds for the model generators FINDER and FMCAT INF . The abductive system AB is the only system solving all problems and is doing very well in terms of number of backtracks (apart from wicked oe). Its implementation is very straightforward (linear lists for clauses, abduced facts and tabled answers) and as a consequence it is often slower than the constraint system which use more elaborate data structures. The constraint system CS is doing pretty well, in particular when no backtracking is needed over decisions wrt. the subsumption of new answers (#Tbcktr). In fact, the more backtracking occurs over the subsumption decisions, the more the size of the search space becomes close to that of the model generators FINDER and FMCAT INF . So, in case of blocksol it searches the whole space of interpretations of size 2 19  232 whereas AB searches in the space of pre-interpretations of size 2 19 . The poor performance in this problem is not a real drawback. The use of the model generator should be combined with the use of a theorem prover which should be able to find a plan for blocksol. However, one can expect the performance will degrade for problems requiring multiple answers for some predicates. It is interesting to remark that one answer can contain free variables. All its ground instances are true in the interpretation. So, one answer for a predicate can

104

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

express a sizeable number of different interpretations for that predicate. Our results contradict with those of [Pel98] because FINDER most of the time outperforms FMCAT INF . This comparison should be handled with care since these nice results with FINDER were only obtained after serious fine-tuning. However, on the programming examples both systems cannot compete with the systems we developed on the number of backtracks. There is strong evidence that this is because the model generators search in the space of interpretations which can be many times larger than the space of the preinterpretations. The difference in search space disappears in the problem BOO019-1. Both FMCAT INF and FINDER perform quite well on this example. The timings of the FINDER solutions are superior to ours. Of course its fine-tuned implementation in C is an important factor in this speed difference. Conjunctive partial deduction can handle some of the problems which are difficult for us, but cannot handle any of the planning problems. Computing regular approximations is fast, but it can show failure of the most simple problems only. The set based analyser is more precise and fails only on the planning problems and nreverselast.

4.9 A step backwards: using Prolog During the experiments with the CLP program written for this application we noticed that:

  

many finite domain variables were created; very few pruning was done by constraints; the version using abduction was performing rather well on most examples especially when considering the number of backtracks.

As a result it seemed worthwhile to write another program which combined the tabling mechanism from the CLP solution together with the abduction mechanism from the abductive solution.

4.9.1 Ingredients of the new Prolog attempt The higher level from the solution described in Section 4.3.2 was retained in this version. So the deduction rules: Final state (rule 1), Handling a call to a predicate (rule 2), A solution for a tabled predicate (rule 3) and A solution for a query (rule 4) are again used. But the conditions on the pre-interpretation generated by rule 4 and 5 are not translated to Finite Domain CLP constraints. That deduction mechanism generates, as illustrated on Example 4.1, three types of conditions on the pre-interpretation:



Negation of a list of equalities. The pre-interpretation should be such that one of the equalities does not hold under the pre-interpretation. These predicates are generated when a solution for the query is found (rule 4).

4.9. A STEP BACKWARDS: USING PROLOG

105

Example 4.15 In the case of the odd-even program we have: not ([s(zero) = zero℄).



Subsumption of a new solution by tabled solutions for a predicate When a new solution is found for a predicate (rule 3) will, in a first attempt, generate a condition on the pre-interpretation that this new solution should be subsumed by the other solution(s) already found by the system. Example 4.16 In the odd-even example when the second solution for the even predicate is found the condition subsumed (even(s(s(zero))); [even(zero)℄) is generated.



A solution NOT subsumed by the other solutions of a predicate When backtracking on (rule 3), no pre-interpretation is found by assuming that the new solution for a predicate is subsumed by the solutions already found. The only way out is to add the new solution to the table. Then new condition is added to the set of constraints that this new solution should not be subsumed by the old solutions. Example 4.17 This situation does not occur in the odd-even example in Figure 4.1. If we ask the system to find other pre-interpretations for the problem, we would get the condition: not subsumed (even(s(s(zero))); [even(zero)℄).

These conditions are collected by the system but not translated to finite domain CLP constraints. Instead these conditions are tested on containing a solution using abduction whenever a new condition is added. The major difference with the pure abductive approach (AB) is that after such a check the system does not commit to the solution found (It does keep track of the solution for optimising the next search).

4.9.2 Some variations on the theme and results The first version of the system were performing quite well. Then several variations were tried to speed up the program.





Avoid tabling for non-recursive predicates.(O1) It seems a waist of resources to use tabling for predicates that are not recursive. Also when two predicates that are mutually recursive only one of the two predicates could be tabled to break the recursion. The main advantage of this optimisation is to avoid backtrack points in the tabling mechanism. The disadvantage is that solutions for non-recursive predicates that are the same under the pre-interpretation are now treated as being different. This leads to more solutions for the predicates that are tabled and also for the original query. Forget about the subsumption and not-subsumed constraints (O2) Another idea due to Kostis Sagonas avoids the generation of many subsumption and notsubsumption conditions. Whenever new solutions are found, and we can prove that under the current set of conditions that this solution could be a new solution under

106

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

the pre-interpretation, we add it as a new solution to the table. Indeed when two solutions in a table are the same under some pre-interpretation this is harmless. It only leads to some redundant solutions for other tabled predicates and the query. Of course the disadvantage is the possible uncontrolled high number of solutions in the tables.



Using intelligent backtracking (O3) Meanwhile Intelligent Backtracking [Bru81, PP79, CCF88] was added to the pure abduction approach by Maurice Bruynooghe. This gave quite impressive results. Thus it seemed worthwhile adding this optimisation to this Prolog version. The idea of Intelligent Backtracking is to analyse the reason for failure. When proving that a solution exist, by finding a solution, for the set of conditions we keep track of the information used during the search. During the search for a solution for the conditions each choicepoint gets a unique number. When a value in the pre-interpretation for a functor or atom is used this number is remembered. On failure all the numbers remembered are responsible for the current failure. If the most recent choice points are not part of this information we have gained something: we can backtrack to the most recent choice point of the information maintained instead of the last choice point generated.

Of course the optimisations described above can be combined. Some results are gathered in tables 4.4 and 4.5.

name odd even wicked oe appendlast reverselast nreverselast schedule multiseto multisetl blockpair2o blockpair3o blockpair2l blockpair3l blocksol

CLP time 0.01 0.01 0.44 3.44 0.29 0.08 0.09 0.5 0.51 3.6 3.74 7.7h

plain Prolog time 0.00 0.00 0.24 2.60 0.08 0.03 0.02 0.13 0.33 1.85 6.91 3625

O1 time 0.01 0.01 0.27 2.67 0.09 0.03 0.03 0.16 0.56 10.06 189 4373

O2 time 0.02 0.02 93.2 862.9 1.03 0.22 0.97 408 >26.8h 11514 10.5h 3.8h

O3 time 0.01 0.01 0.1 1.15 0.06 0.05 0.04 0.14 0.32 1.28 4.11 2628

Table 4.4: Back to Prolog: timing

In Table 4.4 we can notice that the plain Prolog version is usually faster than the CLP version. Especially for the blocksol benchmark the time used was reduced from 5.7

4.9. A STEP BACKWARDS: USING PROLOG

name odd even wicked oe appendlast reverselast nreverselast schedule multiseto multisetl blockpair2o blockpair3o blockpair2l blockpair3l blocksol

CLP assign 3 3 59 128 41 20 16 72 98 9931 10085 1396146

plain Prolog assign 4 4 397 2347 273 51 18 80 874 7511 18383 4413k

107

O1 assign 4 4 397 2347 273 51 18 80 1450 10438 174112 2856k

O2 assign 7 7 11193 11215 5006 262 214 589429 >53M 3850k 21M 7051k

O3 assign 4 4 126 688 146 43 18 71 537 4013 6036 2492k

Table 4.5: Back to Prolog: backtracking

hours to 1 hour (which is still a lot!). Only the blockpair3l resulted in a 100% slowdown. Compared with the CLP solution we get a serious larger number of assignments during the search for a pre-interpretation. Still the program executes faster due to far less overhead. When looking at the variant that avoids tabling when it is not really needed to break the recursion, we cannot find any improvement on the benchmarks. This is somehow counterintuitive. A possible explanation: avoiding tabling, redundant solutions can propagate to other predicates. Still we expect that for other programs we could get better performance. The second variant, avoiding choice points in the tabling mechanism, results in a serious slow-down for all the benchmarks. This is due to the larger amount of solutions that are kept in the tables. Actually not only the number of solutions is dramatic, but also the size of the solutions is growing very fast. Using these large solutions in conditions results in a very long-winded check. From the three variants implemented only the last variant seems worthwhile. Using Intelligent Backtracking almost always significantly cuts down the number of assignments done during the search for a pre-interpretation given a set of conditions. Due to some overhead the timing results are less significantly better but still better than the plain version. An aspect not mentioned in the tables is the memory used by the several systems. It turns out that the memory used by the CLP application dramatically grows during the search. On the other hand the memory used by the Prolog versions remains almost constant during the search.

108

CHAPTER 4. PROVING A QUERY CAN NEVER SUCCEED

4.10 Contribution The contribution of the work in this chapter concerns the development of a constraintbased approach for finding models based on a pre-interpretation for proving failure of a query. The novelty in the approach concerns the combination of Constraint Logic Programming, tabling and pre-interpretations. The key issue is to delay the choices on the pre-interpretation as long as possible. An important aspect is the top down derivation of information starting from the query to be proven false. Besides of a system using Finite Domain Constraint Logic Programming, a dedicated solver was written augmented with Intelligent Backtracking. The constraint-based solution was compared with an abductive approach developed by Maurice Bruynooghe, the results obtained with the general model generator FINDER and several other approaches. An automated system finding proofs that queries have no solution is important for systems like planners that may loop in case no plan exists.

Chapter 5

Constraint-based Termination Analyses Termination of programs is a pertinent problem in Logic Programming. Indeed, FOL is only semi-decidable, hence every sound and complete proof procedure will be nonterminating for certain programs. The development of methods for automated proof of termination of logic programs is a major research topic [DD94] within Logic Programming. Of course this is not possible for all programs, but within the paradigm of Logic Programming, it is reasonable to expect such automated systems can be designed to find proofs of termination for a substantial class of programs. Such a proof is then built for a logic program given a class of queries and a specific computation rule. It consists of showing the SLD tree has no infinite branches. In [DD97a] Decorte and De Schreye introduce constraints to obtain a more succinct method which can prove termination of an as wide as possible class of programs. This chapter is about the method to solve the generated constraints. This solution method is also integrated into the forthcoming paper [DDV99]. The remainder of the chapter starts with a short introduction to a few concepts used for termination analyses within Logic Programming. We continue with a few pointers to past work within this area. This is followed by an intuitive description of how parameterised conditions are generated given a Logic Program. Then we come to how these conditions can be translated to Finite Domain constraints in order to find a proof for termination. We conclude with some results.

5.1 Basic notions In classic schemes for proving termination of programs, the successive states of execution are mapped to decreasing elements from a well-founded set. An example of such a scheme is connecting an integer function to the flow of execution. A termination proof then consists in showing that the function can never become negative and that the function shrinks 109

110

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

at each step during computation. In an instantiation of this classic scheme for Logic Programming we have to reason about goals and clauses. The mapping of successive states described above is then called the level mapping. This level mapping is connected to the goal, or part of the goal. The idea is then that this level mapping shrinks for successive goals during SLD derivation. This level mapping is an integer function than can never become negative. Definition 5.1 (Level Mapping) A level mapping j:j is a function defined for all possible calls in a program P and a set of queries S. The image of the function j:j is a subset of the natural numbers. Example 5.1 Given the program append([], List, List). append([X|Y], List, [X|Result]):append(Y, List, Result). to prove that the call append([a,b,c,d], List, Result) terminates, we can use the level mapping that measures the length of the first argument. This length can never become negative, and shrinks during the recursive call of append in the second clause of the definition for append. Of course we do not want to proof termination of specific calls but of call patterns, collecting a whole class of specific calls. For instance for the append program, we should be able to proof that any call with the first or last argument ground will terminate. This class of calls will be referred to as S in the remainder of the text. Another notion is the call set, all possible calls that can occur while executing the program. Definition 5.2 (Call Set) Let S be the set of possible calls to the program P. Then Call (P; S) is the set of all possible goals occurring in the SLD tree corresponding with one of the call from S, using the left to right computation rule. Given this formal notion of call set we can define what it means that the level mapping should shrink: Definition 5.3 (Acceptability) A program P is acceptable given a set of calls S when a level mapping j:j exists such that

 8Call 2 Call (P S)  8H : B1 Bn 2 P : such that θ = mgu(Call H ) exists ;

;::: ;

;

;

5.1. BASIC NOTIONS

111

 8Bi 2 fB1

; : : : ; Bn g having the same predicate symbol as Call and for any computed answer substitution θ0 for B1 ; : : : ; Bi 1 :

jCall j jBi θθ0 j >

If a program is acceptable for a set of calls S it will terminate for every call within S. This only holds for directly recursive programs, e.g. mutual recursive program have to be transformed to directly recursive programs before using the definition above. The definition above is quite general, in this work we decompose the definition above using interargument relations, norms, rigidity and rigid acceptability. The notion of interargument relation cannot be illustrated with the append example 5.1. Interargument relations are needed when proving termination of a predicate with intermediate calls before the recursive call. This can be seen in the Example 5.2, having an intermediate call to del before the recursive call to perm. Such an interargument relation relates the sizes of the arguments of intermediate calls. The perm example permutes a list of elements: Example 5.2 perm([], []). perm(List, [Head|Tail]):del(Head, List, List1), perm(List1, Tail). del(Head, [Head|Tail], Tail). del(El, [Head|Tail], [Head|Tail1]):del(El, Tail, Tail1). This example contains definitions for two predicates: perm and del. We want to prove that any call to perm and del, with respectively a ground first and a ground second argument, terminates. Similar to the append program in example 5.1 we want to show that the first argument of perm will shrink during computation and finally the program terminates with the empty list as first argument for perm. In the recursive clause for perm we can see a call to del before the recursive call to perm. Given the outline of the proof above we will need to show that there is a relation (interargument relation) between the sizes of List and List1 after the call to del. More specific the size of List1 should be smaller than List. When reasoning on sizes of terms we use norms. Definition 5.4 (Norm) A norm jj:jj is a function that measures the size of a term. A norm jj:jj for a program P should be defined for every term that can be constructed from functors and atoms occurring in P. The norm should also be defined for terms containing variables. Based on norms we can define relationships between the sizes of arguments of atoms that follow from the program P.

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

112

Definition 5.5 (Interargument Relation) Given a predicate p/n defined in a program P. Then a valid interargument relation wrt a norm jj:jj is a relation R p=n  IN n where 8 p(t1 ; : : : tn ) : if P j= p(t1 ;  ; tn ) then (jjt1 jj;  ; jjtn jj) 2 R p=n . After defining interargument relations, norms and level mapping we can explain rigidity, another component of termination proofs. Rigidity is a property of the level mapping given a set of queries and a program. The property holds when the level mapping of calls that occur in the SLD tree of the computation is invariant for any substitution. As a consequence the size of a call from the call set will never grow during unification with a head of a clause in the program. Definition 5.6 (Rigidity) A level mapping j:j is rigid with respect to a set of queries S and a program P iff

8Call 2 Call (P S) 8θ : jCall j = jCallθj ;

;

If we use a rigid level mapping then we can define acceptability locally as a condition between head and a recursive call in the body. This is because the substitution θ performed on the call during unification and partly execution of the body will not change the level mapping of the call. Definition 5.7 (Rigid Acceptability) Given a set of queries S and a program P then the program P is Rigid Acceptable iff

 there exists a rigid level mapping j j such that  8H : B1 Bn 2 P,  8Bi 2 fB1 Bn g which has the same predicate symbol as H,  8θ such that all atoms B in (B1 Bi 1 )θ satisfy a valid interargument relation :

;::: ;

;::: ;

;::: ;

RB :

jHθj jBi θj >

Notice that rigid acceptability is stronger than acceptability. Proving termination of a Logic Program P and queries S consists of finding:

  

a norm, a level mapping, and interargument relations

where

  

these interargument relations have to be proven correct, the level mapping should be rigid and the level mapping satisfies the conditions for rigid acceptability using the interargument relations.

5.2. STATE OF THE ART

113

5.2 State of the art The first results in this line of research were realised by Ullman and Van Gelder [UV88] and Pl¨umer [Pl¨u90]. They were the first to propose efficient analyses methods. These methods used fairly simple norms, based on list length and term size. Later on followed more conceptual work by Apt, Bezem and Pedreschi [AB91, AP91, Bez92]. This line of work made up a clear inventory of the difficulties involved in the different aspects of termination analyses. Later work focused on inferring norms and level mappings from mode and type information [Ver92, DDF93, MKS96], automatic inference of interargument relations [DV95, BS91, LS97] and automatic verification of the relations and termination [DVB92, Mes96]. Most of the current systems are very precise, unfortunately the execution speed is problematic. It is also quite popular to use different stages in the proving process [DDF93, DVB92, LS97]: infer mode and type information, infer appropriate norms and level mappings, infer interargument relations and finally prove the termination. Although conceptually elegant this method has a few drawbacks:

 

having different stages can slow down the process and there is some precision loss because there is few back propagation between the different stages.

In the constraint based solution summarised below, we again integrate several of the stages, gaining more precision and speed.

5.3 Symbolic conditions for termination As mentioned above a lot of the current systems use different stages when automatically building a proof for termination. In each of these stages some decisions are made on the norms, level mapping and interargument relations. Nothing is wrong with that until at the end the system cannot build a proof because one of the decisions taken in a previous stage turns out to be wrong. In this work we explain how these decisions can be postponed and also the different stages can be avoided. The clue is to introduce undetermined parameters. Using these parameters we can express norms, level mappings and interargument relations in a symbolic form without making decisions. During the process rigidity and rigid acceptability will impose conditions on these parameters. After all these conditions have been found a search can be started for instantiating the parameters. The results is a proof for termination with automatically derived norms, level mappings and interargument relations. In this section using Example 5.2 we illustrate how symbolic conditions for termination are derived from the program. How to deduce Finite Domain constraints from this symbolic conditions is discussed in Section 5.4. The topic is discussed in detail and more formal in [Dec97, DD97a, DDV99]. The example at hand is the perm program given in Example 5.2. Assume that we want to prove that perm terminates when it is called with a terminated list as the first argument,

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

114

the elements from that list are unknown and could be free variables. The second argument of perm is unknown and could be a variable. A mode analysis can infer that del will be called with the first variable unknown, the second argument a terminated list, with unknown elements, and the last argument also unknown. Such analyses should also infer that all recursive calls to perm have a terminated list as first argument. The basic idea of this constraint based approach consist of deducing all necessary parts needed for the proof, but not deciding on the actual contents of these necessary parts. In the program only one functor is used: a list functor. So we define a symbolic norm jj:jj on such a list structure:

jj[t1jt2℄jj = nrm0j

[: :℄

j jjt jj + nrm[ j ℄jjt jj 1 2 2

[: :℄

+ nrm1

: :

(5.1)

This expresses that we use a so called semi-linear norm, which is quite a powerful one for automatically proving termination, but does not decide yet on the values of the [:j:℄ [:j:℄ [:j:℄ different constants (nrm0 , nrm1 and nrm2 ). For the time being they are considered as parameters. In general a norm will be defined for each functor f =n in the program: Definition 5.8 (Symbolic Semi-linear Norm) jj f (t1 ; : : : ; tn )jj = nrm0f =n + ∑ni=1 nrmif =n jjti jj

jj jj being a semi-linear norm with parameters nrm0f n =

:

;::: ;

f =n

nrmn .

Similarly we use parameters to express a symbolic level mapping j:j of the two predicates.

j perm(t1 t2 )j = lev1permjjt1 jj + lev2permjjt2 jj del del jdel (t1 t2 t3 )j = levdel 1 jjt1 jj + lev2 jjt2 jj + lev3 jjt3 jj ;

;

(5.2)

;

(5.3)

del del The parameters are: lev1perm , lev2perm , lev3perm , levdel 1 , lev2 and lev3 . Again we can define a symbolic level mapping for each predicate p=n

Definition 5.9 (Symbolic Level Mapping) j p(t1 ; : : : ; tn )j = ∑ni=1 levip=njjti jj p=n p=n with lev1 ; : : : ; levn parameters for the level mapping. As del is used within a recursive clause for perm, before the recursive call, we need an interargument relation between the arguments of del. Currently only relations of the following form are allowed: given a predicate p(t1 ; t2 ; : : : ; tn ) Definition 5.10 (Symbolic Interargument Relation) p=n p=n p=n IAR( p(t1 ; : : : ; tn ))  ∑i2Ip n reli jjti jj  ∑ j2O p n rel j jjt j jj + rel0 where I p=n can be seen as input arguments of predicate p=n and O p=n as output arguments =

p=n

of p/n. rel0

;::: ;

p=n

reln

are parameters.

=

5.3. SYMBOLIC CONDITIONS FOR TERMINATION

115

We currently use the guideline that all arguments that can never be called with a free variable in the program are input arguments as explained in [DDF93]. Again this relation p=n p=n is symbolic, the parameters are for each argument j: rel j and an extra rel0 . This leads to the following symbolic interargument relation for del: rel2del jjt2 jj  rel1del jjt1 jj + rel3del jjt3 jj + rel0del

(5.4)

Specifically for this symbolic interargument relation we have the parameters: rel2del , rel1del , rel3del and rel0del . Given the symbolic definitions of norm, level mapping and interargument relation, we can now start imposing conditions on the parameters, such that we get a valid termination proof. First of all there is rigidity: level mappings of the calls to predicates must be rigid, so that unification with a clause head cannot increase this level mapping. In other words the level mapping of any specific call should remain constant during the remainder of the computation. Here we will impose rigidity by setting all parameters to zero that correspond to an argument possibly being a variable. This is done for the level mapping and the norm as well. Definition 5.11 (Sufficient Rigidity Condition)

8 p(t1 tn ) 2 Call (P S) : if ti is a variable then levip n = 0 f tn ) occurring in a call from Call (P S) : if ti is a variable nrmi and 8 f (t1 ;:::

=

;

;::: ;

;

n

=

=0

In case of the example, given that only for the first argument we have a terminated list for each call to perm, the contents of the second argument being unknown, we have to set the parameter lev2perm to zero. In case the second argument would be a free variable before the initial call, instantiating it could change the level mapping of this initial call. The level mapping also depends on the norm, so rigidity constrains also the norm. Because we have no information on the instantiation of the elements of the list we cannot use these in the norm, when this norm is used for the level mapping of perm. As a result the parameter [:j:℄ nrm1 has to be set to zero. Similarly, constraining the level mapping of del to be rigid del implies we cannot use the size of the first and last argument of del, setting levdel 1 and lev3 to zero. This leads us to the following rigid level mapping and corresponding norms for the example at hand:

jj[t1jt2℄jj = nrm0j + nrm2j jjt2jj j perm(t1 t2 )j = lev1permjjt1 jj jdel (t1 t2 t3 )j = levdel 2 jjt2 jj [: :℄

;

[: :℄

(5.5)

;

(5.6)

;

(5.7)

Then we should impose that the interargument relation for predicates p=n should be valid. Definition 5.12 (Symbolic Validity Condition) In case we need an interargument relation for a predicate p=n from P then for each clause

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

116

H : B1 ; B2 ; : : : ; Bm defining p=n the following condition is needed: 8θ; IAR(Hθ)

Vm

i=1 IAR(Bi θ)

)

The conditions below are obtained by generating a condition for each clause of del. For a fact we state that the relation between the arguments should hold without a condition. For a clause with a non-empty body, the relation(s) over the arguments of the call(s) in the body have to imply the relation over the arguments of the head.

8head tail : true ) rel2del jj[head jtail ℄jj  rel1del jjhead jj + rel3del jjtail jj + rel0del 8head el tail tail1 : rel2del jjtail jj  rel1del jjEl jj + rel3del jjtail1jj + rel0del ) rel2del jj[head jtail ℄jj  rel1del jjEl jj + rel3del jj[head jtail1℄jj + rel0del Combining this with the formulation from jj jj we get: 8head tail : true ) j rel del )jjtail jj rel del jjhead jj + rel del nrm j j rel del  0 del (rel2 nrm2 3 1 2 0 0 8el head tail tail1 : rel2del jjtail jj rel1del jjEl jj rel3del jjtail1jj rel0del  0 ) j j rel2del nrm2 jjtail jj rel1del jjEl jj rel3del nrm2 jjtail1jj j del rel3del ) rel0del  0 + nrm0 (rel2 ;

;

;

(5.8)

;

(5.9)

:

;

[: :℄

;

;

[:

:

(5.10)

;

[: :℄

[: :℄

[: :℄

(5.11)

Resolving this to explicit conditions on the parameters is a non-trivial tasks and will be discussed in the next section. The last matter involves conditions on the level mapping imposed by rigid acceptability. In case of recursive clauses the level mapping of the recursive call should be smaller than the level mapping of the head: Definition 5.13 (Rigid Acceptability Condition) For each clause H : B1 ; B2 ; : : : ; Bm where B j has the same predicate symbol as the head Vj 1 H we have the condition: 8θ : i=1 IAR(Bi θ) ) jHθj > jBi θj We first deal with setting up the rigid acceptability conditions for the recursive clause for del. Since there are no intermediate body atoms, no interargument relations are needed.

8el head tail tail1 : true ) jdel (el [head jtail ℄ [head jtail1℄)j jdel (el tail tail1)j ;

;

;

;

;

>

;

;

(5.12)

When using the symbolic definitions for the level mapping we deduce

8head tail : true ) levdel 2 jj[head jtail ℄jj ;

>

levdel 2 jjtail jj

(5.13)

5.3. SYMBOLIC CONDITIONS FOR TERMINATION

117

Using the symbolic definition for the norm we have:

j 8tail : true ) levdel 2 (nrm0

[: :℄

j jjtail jj) > levdel jjtail jj 2

[: :℄

+ nrm2

(5.14)

or

j 8tail : true ) levdel 2 (nrm2

1)jjtail jj + levdel 2 nrm0

[: :℄

j

[: :℄

>

0

(5.15)

Finally we need some conditions that ensure the termination of the recursive predicate perm/2. Since a call of del precedes the recursive call to perm we have to take into account the interargument relation on del/3:

8list head list1 tail : rel2del jjlist jj  rel1del jjhead jj + rel3del jjlist1jj + rel0del ) j perm(list [head jtail ℄)j j perm(list1 tail ) ;

;

;

;

>

;

(5.16)

This can then be reduced to:

8list head list1 tail : rel2del jjlist jj  rel1del jjhead jj + rel3del jjlist1jj + rel0del ) perm perm lev1 jjlist jj lev1 jjlist1jj ;

;

;

>

(5.17)

or

8list head list1 tail : rel2del jjlist jj rel1del jjhead jj rel3del jjlist1jj perm perm lev1 jjlist jj lev1 jjlist1jj 0 ;

;

;

>

rel0del

0) (5.18)

Explicit unifications A matter not treated yet, is explicit unification. In principle every unification t1 = t2 can be treated as an ordinary call to a predicate to uni f y=2 with one clause as definition: uni f y(X ; X ). If t1 and t2 are not variables the interargument relation consist of uni f y=2 jjt1 jj + rel2uni f y=2jjt2 jj  rel0uni f y=2 . When handling explicit unification this way rel1 we loose some information when constructing such interargument relations and extra parameters are introduced. Indeed, after unification of two term t1 and t2 , if the unification succeeds jjt1 jj equals jjt2 jj. Rewriting the equation t1 = t2 , computing the most general unifier, we can reduce this to a conjunction of the form

Vl (jjx jj = n ∑j k i 1 =

i

=l +1

ai; j jjxk j jj).

The identifiers xki and xk j are the variables occurring in the equation t1 = t2 and ai; j an expression constructed from parameters used in the symbolic norm. This conjunction can then be used instead of the interargument relation without introduction of extra parameters nor loss of precision. An example illustrates the formula.

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

118

Example 5.3 r(X, Y):- f(X, t(V)) = f(u(P),Y), r(Y,P). For the example above the explicit unification results in the interargument relation jjX jj = nrm0u=1 + nrmu1=1jjPjj^jjY jj = nrmt0=1 + nrmt1=1jjV jj

5.4 From conditions to constraints In the previous section the set of conditions has been reduced to the following set of conditions:

8head tail : true ) ;

[:j:℄ del (rel2 nrm2

rel3del )jjtail jj 8el ; head ; tail ; tail1 : rel2del jjtail jj

rel1del jjEl jj

rel1del jjhead jj + rel2del nrm0

rel3del jjtail1jj

8tail : true ) j

rel3del )

rel0del

1)jjtail jj + l2del nrm0

[: :℄

levdel 2 (nrm2

rel0del  0 )

0 j

[: :℄

>

(5.20) 0

8list head list1 tail : rel2del jjlist jj rel1del jjhead jj rel3del jjlist1jj lev1perm jjlist jj lev1perm jjlist1jj 0 ;

(5.19)

: :

del + nrm0 (rel2

;

0

j jjtail jj rel del jjEl jj rel del nrm[ j ℄ jjtail1jj 1 3 2

[: :℄

rel2del nrm2

rel0del

(5.21)

;

rel0del

0) (5.22)

>

In general constraints of this type are not easy to solve. It concerns a set of constraints of the type:

^k n ^l 8x j 2 IN :( ∑ ai j x j + ai 0  0 ^ ;

;

i=1 j =1 n

i=k+1

∑ al+1 j x j + al+1 0  0 ;

;

n

x ji

=



j =1; j 6= ji

ai; j x j + ai;0) ) (5.23)

j =1

where  is > or . > stems from rigid acceptability conditions for level mappings and  is generated by validity conditions for interargument relations. In Example 5.2 we have no condition in the left-hand side of the equation of the form x ji = ∑nj=1; j6= ji ai; j x j + ai;0 . Such constraints are generated when extracting an interargument relation from an explicit unification as shown with example 5.3.

5.4. FROM CONDITIONS TO CONSTRAINTS

119

If the values for ai; j in formula (5.23) were known this is merely a linear programming problem showing that the set of linear equations from i = 1 to k: ∑nj=1 ai; j x j + ai;0  0, from i = k + 1 to l: ∑nj=1; j6= ji ai; j x j + ai;0 = x ji and the single constraint ∑nj=1 ak+1; j x j + ai;0  0 is inconsistent. (where  is < or ). Unfortunately, the coefficients ai; j are not known, even worse, the actual problem is finding them. In the remainder of the section we will sometimes use Ji as a shorter notation for ∑nj=1 ai; j x j and use Fi instead of ∑nj=1; j6= ji ai; j x j reducing (5.23) to

8x j 2 IN : (

^k

^l

Ji  0 ^

i=1

x ji

= Fi )

) Jl 1  0 +

(5.24)

i=k+1

5.4.1 Derivation Rule Solving the equation (5.23) consists in finding values for the parameters of the problem, instantiating the values ai; j , such that the equation holds for any set of natural numbers x j . If we can find such values for the parameters of the problem we have a proof for the termination of the program and class of query we derived the equations from. A simple approximation for finding such values is finding values for the parameters by expressing that

8 j 2 f1

al +1;0  0

;:::

ng :al +1; j  0

(5.25) (5.26)

Indeed, since all x j are natural numbers, if all al +1; j are larger or equal to zero and al +1;0  0 the whole formula ∑nj=1 al +1; j x j + al +1;0  0 will hold regardless of the truth value of the expressions at the left-hand side of (5.23). This method looses no information in case of Equation (5.19) and (5.21) where the left-hand side of the implication is true. The Equation 5.19 yields: [:j:℄ del (rel2 nrm2

rel3del )  0 (c1) rel1del

rel2del nrm0

rel0del

 0 (c2)  0 (c3)

Equation (5.19) stems from the interargument relation for del, where the first clause of del was used to impose conditions on the general symbolic form of the interargument relation. Equation (c2) already informs that the size of the first argument cannot be used in the interargument relation by constraining the parameter rel1del to zero. This is a correct reduction since the size of the first argument is not relevant. This reduces the interargument relation to: rel2del jjt2 jj  rel3del jjt3 jj + rel0del

(5.27)

On the other hand for Equation (5.22), where the left hand side of the implication is not true, incorrect reductions are made. The resulting set of constraints is unsatisfiable:

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

120

Example 5.4 lev1perm  0 perm lev1  0 0>0 This is because information from the interargument relations and recursive calls encoded in the left-hand side of the formula is not used with the current approximation. The information from the left hand side will be transfered to the right-hand side using rewrite rules described in the following paragraphs, curing this problem. After applying these rules the approximation rule described above becomes quite accurate as argued below.

5.4.2 Evaluation Rule In formula 5.24 the inequalities occurring in the left-hand side stem from interargument relations. When using the derivation rule above the information from these interargument relations is not used. The evaluation rule lends a hand to solve this problem by rewriting (5.24) to (5.28) by subtracting all terms J j occurring at the left of the implication from Jl +1 at the right-hand side.

8x j 2 IN : (

^k i=1

Ji  0 ^

^l

x ji

i=k+1

= Fi )

) Jl

k

+1

∑ Ji  0

(5.28)

i=1

The resulting formula is a safe approximation for (5.24). If we can find a solution for the unknown coefficients of (5.28) then also the equation (5.24) will hold. The proof for this proposition is easy when recognising two cases.



When at least one of the conditions J j  0 or x ji = Fi of the left-hand side of the implication does not hold both (5.28) and (5.24) trivially hold.



When all conditions J j  0 and x ji = Fi hold then ∑ki=1 Ji will always be a positive number. As a result Jl +1 ∑ki=1 Ji  0 implies Jl +1  0.

Although this rewrite rule is an approximation (expression 5.24 might have solutions where 5.28 is false for these solutions) the method works quite well in practice. The method is motivated as follows:

 

p=n

In most examples the parameters reli for the interargument relations can be chosen such that Ji  0 becomes and equality Ji = 0. Subtracting Ji from Jl +1 results in no loss of precision in these cases. The derivation rule will now take the interargument relations on the left-hand side of the implication into account. When one of these interargument relations is not satisfied some term Ji will be a negative number. In case of a condition generated

5.4. FROM CONDITIONS TO CONSTRAINTS

121

from rigid acceptance the right hand side of the implication contains no paramep=n p=n ters reli . As a result the constraint solver can replace the reli parameters with an arbitrary multiple until the term Ji is so negative that Jl +1 ∑ki=1 Ji  0 holds. In case of conditions generated from validity of interargument relations the above reasoning does not always hold. Still the methods works well in practice for interargument relations. If problems occur in this respect this could be cured with an p=n extra parameter multiplied with Ji in case Jl +1 contains the same reli parameters.

5.4.3 Substitution Rule Also the equalities in the left-hand side of the implication are used. For each equality x ji = Fi , all occurrences of x ji on the right-hand side of the implication are replaced (substituted) by Fi . The resulting equation (5.30) is equivalent to the equation (5.28), this can be easily seen by reasoning on validity of the left-hand side of the implication. If the left-hand side of the equation does not hold, then the right-hand side is irrelevant. Otherwise, if the left-hand side holds, then the variables are substituted by something which is actually equal and the resulting right-hand side is equivalent. Resulting in:

8x j 2 IN :(

^k

Ji  0 ^

i=1

^l

x ji

= Fi )

)

i=k+1 k

(Jl +1

∑ Ji )θ  0

(5.29)

i=1

where θ = mgufx j(k+1) =Fk+1; : : : ; x j(l ) =Fl g

(5.30)

The importance of the substitution rule is motivated by an example. Suppose the first delete clause is given by: Example 5.5 del(Head,List,Tail):- List= [Head|Tail]. Then the validity condition generated by this clause is:

8Head List Tail :jjList jj = nrm0j + nrm2j jjTail jj ) rel2del jjList jj rel1del jjHead jj rel3del jjTail jj ;

;

[: :℄

[: :℄

rel0del

0

(5.31)

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

122

Using the derivation rule for formula (5.31) results in a set of constraints: rel2del rel1del rel3del rel0del

0 0 0 0

This reduces the interargument relation to rel2del jjList jj  0

(5.32)

which clearly does not reflect the relation between the size of List and Tail! Using the substitution rule on (5.31), the resulting equation on the right-hand side of the implication becomes identical to the equation without explicit unification in (5.10).

8Head List Tail : jjList jj = nrm0j + nrm2j jjTail jj ) j rel del jjHead jj + (rel del nrm j rel del )jjTail jj rel2del nrm0 1 2 3 2 ;

[: :℄

;

[: :℄

[: :℄

[: :℄

rel0del

(5.33)

Final set of constraints Given the evaluation rule and substitution rule we come back to applying the evaluation rule. After applying the evaluation and substitution rule the resulting Formula (5.30) can again be brought into normal form:

8x j 2 IN : (

^k i=1

Ji  0 ^

^l i=k+1

n

x ji

= Fi )

) ∑ b j x j + b0  0

(5.34)

j =1

Then the set of equations b j  0 and b0  0 result in typical Finite Domain constraints with the parameters as Finite Domain variables. The domains of the Finite Domain variables are restricted to bounded natural numbers. Back to Example (5.2) which we already reduced to the set of equations (5.19), (5.20)(5.21) and (5.22). First we apply the evaluation-rule to Formula (5.20) and (5.22).

5.4. FROM CONDITIONS TO CONSTRAINTS

8head tail : true )

123

;

[:j:℄ del (rel2 nrm2

rel3del )jjtail jj 8el ; head ; tail ; tail1 : rel2del jjtail jj

rel1del jjEl jj

rel1del jjhead jj + rel2del nrm0

j

[: :℄

rel3del jjtail1jj

rel0del

0

(5.35)

rel0del  0 )

j jjtail jj rel del jjEl jj rel del nrm[ j ℄ jjtail1jj 1 3 2 [j℄ del del del + nrm (rel rel ) rel [: :℄

: :

rel2del nrm2

: :

2

0 del (rel2

jjtail jj 8tail : true ) j

0

rel3del jjtail1jj

1)jjtail jj + levdel 2 nrm0

([: :℄)

levdel 2 (nrm2

3

rel1del jjEl jj

j

([: :℄)

>

8list head list1 tail : rel2del jjlist jj rel1del jjhead jj rel3del jjlist1jj perm perm lev1 jjlist jj lev1 jjlist1jj del del del (rel2 jjlist jj rel1 jjhead jj rel3 jjlist1jj ;

;

rel0del )  0

0

;

rel0del

(5.36) (5.37)

0)

rel0del ) > 0

(5.38)

This leads to a set of finite domain constraints:

 0 (c1 )  0 (c2 ) j del del rel2 nrm0 rel0  0 (c3 ) j rel del  0 (c ) rel2del nrm2 4 2 j rel3del ( nrm2 + 1)  0 (c5 ) nrm0 (rel2del rel3del )  0 (c6 ) j 1)  0 (c ) levdel 7 2 (nrm2 rel2del nrmdel 2

rel3del

rel1del

[: :℄ [: :℄

[: :℄

[: :℄

j

[: :℄

levdel 2 nrm0 lev1perm

rel2del rel1del perm lev1 + rel3del rel0del

>

0 (c8 )

 0 (c9 )  0 (c10 )  0 (c11 ) >

0 (c12 )

This finishes the symbolic computation, the constraint c1 up to c12 can now be passed to a finite domain solver.

124

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

5.5 Finite Domain Solving All parameters introduced in the symbolic phase now become finite domain variables. Such a solver checks consistency by finding an assignment for this finite domain variables. For a large set of examples this set of finite domain constraints leads to a solution without real search. In most cases values for the finite domain variables can be found restricting the domains to booleans, in a few cases more values are needed in the domains. With the constraints above we can sketch an arbitrary propagation in such a finite domain solver when solving over the booleans: from (c2 and c10 ): rel1del = 0; from (c8 ): levdel 2 = 1 and [:j:℄ [:j:℄ nrm0 = 1; from (c7 ): nrm2 = 1 from (c12 ): rel0del = 1. Using this result in the other equations we have:

 0 (c01 ) rel2del 1  0 (c03 ) rel2del rel2del  0 (c04 ) rel2del rel3del  0 (c06 ) perm lev1 rel2del  0 (c09 ) lev1perm + rel3del  0 (c011 ) rel2del

rel3del

(c04 ) has now become redundant. Here (c03 ) reduces rel2del to the value 1. (c01 ) and (c06 ) are perm now also redundant. lev1 = 1 is another consequence using (c09 ). Now (c011 ) restricts rel3del to 1. As a result all involved variables are instantiated, proving the termination with

jj[t1 jt2 ℄jj = 1 + jjt2jj j perm(t1 t2 t3 )j = jjt1jj jdel (t1 t2 t3 )j = jjt2 jj ;

;

;

;

after a call to del (t1; t2; t3) : jjt2jj  jjt3jj + 1

The experiments done, reported on below, were performed using a finite domain solver written in Mercury described in Section 3.4.

5.6 Experimental evaluation A prototype of the technique has been implemented. Currently it consists of two different parts. The core of the system, which derives the constraint set, has been coded in Prolog by BIM, version 4.1.0. For the second part, which concentrates on the constraint solving, we have built on the finite domain CLP language ROPE [VD94], which is written in Mercury.

5.6. EXPERIMENTAL EVALUATION

125

Program

Query

Us

LS

SSS

Suc

Term

permute duplicate sum merge reverse dis - con

(g,f) (g,f) (g,g,f) (g,g,f) (g,f,g) (g)

0.08 0.03 0.03 0.06 0.04 0.18

0.69 0.08 0.35 2.06 0.17 0.34

0.04 0.02 0.01 0.03 0.04 0.02

+++ +++ +++ +++ +++ +++

+ + + + + +

Table 7.1: Results on the examples in [DD94]. As in [SSS97], we have tested the performance of the system on three well-known test suites (collected by [LS97]). Tables 7.1, 7.2 and 7.3 show our results on each of the three test suites. The first two columns specify the program within the test suite and how it was queried. In the next column, we display the timings obtained from the mentioned prototype. The next two columns display the timings obtained from respectively the approaches in [LS97] and [SSS97]. The results of [SSS97] were obtained after transforming the programs to Mercury. Both 1 sets of timings emerge from the respective papers. The next column displays success or failure for each respective system: it contains a ‘+’ symbol if the system succeeds in proving termination, a ‘ ’ if it failed to do so. In the final column, we specified whether the program is indeed terminating for the considered queries. Again, ‘+’ means terminating, ‘ ’ means that there exist queries among the ones considered for which the program gets into an infinite computation. Note that, in some cases, the transformation to Mercury changes the termination properties of the program. Our conventions for expressing this in the tables is that, in such cases, we place either ‘+ ’ or ‘  ’ in the final column. Here, ‘+ ’ (resp. ‘  ’) should be read as ‘+’ (resp. ‘ ’) for the columns for our own prototype and the one of [LS97], but read as ‘ ’ (resp. ‘+’) for the column of [SSS97]. Let us first discuss the precision of the approach. Of all terminating test suites, there were only four programs for which the system failed to prove termination with respect to the considered queries: mergesort of Table 7.2, minsort and two versions of mergesort of Table 7.3. In each case we fail because the interargument relation derived is too general for the specific case. Consider e.g. the mergesort program with the call pattern mergesort (g; f ): 1 A third system, proposed in [CT97], provides similar success and timing behavior as [LS97], on several example programs. As the authors did not yet provide success/timing results for the benchmarks, we have left the system out of the comparison.

126

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

mergesort (Nil ; Nil ) mergesort ([e℄; [e℄) mergesort ([e; f ju℄; v)

split ([e; f ju℄; w; y); mergesort (w; x); mergesort (y; z); merge(x; z; v):

merge(x; Nil ; x) merge(Nil ; x; x) merge([ajx℄; [bjy℄; [ajz℄)

a  b; merge(x; [bjy℄; z): a > b; merge([ajx℄; y; z):

merge([ajx℄; [bjy℄; [bjz℄) split (Nil ; Nil ; Nil ) split ([eju℄; [ejv℄; w)

split (u; w; v):

Whereas the split predicate is never called with its arguments being empty lists, this can not be detected by the system, which derives the following relation:

f(x1 x2 x3 ) j x1  x2 + x3g ;

;

To prove termination, the relation should express (for the first recursive call) that x1 > x2 , as the partial data in the atom split ([e; f ju℄; w; y) prevents it from matching with split (Nil ; Nil ; Nil ). The other systems can not deal with these examples either, with the exception of [LS97] on mergesort.t of Table 7.3. Most likely this is achieved through a better propagation of call information into the interargument derivation. On the other hand, [LS97] fails on the perm example of Table 7.3, for which the two other systems are successful. On the whole, for these benchmarks, the precision of the three systems seems very comparable. Note though that some examples of [DD94] were not included in the benchmark of [LS97]. Consider the con f used delete program: con f (x) delete2 (x; y)

delete2 (x; z); delete(u; y; z); con f (y): delete(u; x; z); delete(v; z; y):

Dealing with this program requires linear interargument relations of the type x = y + 2 (for delete2 ) and y = z + 1 (for delete). Only our system is precise enough to generate them.

5.6. EXPERIMENTAL EVALUATION

127

Program

Query

Us

LS

SSS

Suc

Term

append append list fold lte map member mergesort mergesort ap naive rev ordered overlap permutation quicksort select subset subset sum

(g,g,f) (f,f,g) (g) (g,g,f) none (g,f) (f,g) (g,f) (g,f,f) (g,f) (g) (g,g) (g,f) (g,f) (f,g,f) (g,g) (f,g) (f,f,g)

0.03 0.04 0.02 0.04 0.03 0.04 0.02 0.27 0.34 0.04 0.03 0.05 0.13 0.28 0.03 0.05 0.07 0.02

0.17 0.36 0.06 0.29 0.14 0.15 0.09 36.0 27.96 0.32 0.21 0.23 1.39 129.48 0.14 0.33 0.33 0.12

0.03 0.04 0.03 0.03 0.04 0.04 0.01 0.04 0.07 0.02 0.02 0.03 0.07 0.05 0.01 0.06 0.06 0.02

+++ +++ +++ +++ +++ +++ +++ ––– +++ +++ +++ +++ +++ +++ +++ +++ ––– +++

+ + + + + + + + + + + + + + + + – +

Table 7.2: Results on the Apt test suite [AP94]. Other issues in enhanced precision relate to our ability to (automatically) consider any norm or level mapping of the given forms. In [LS97] and [SSS97], the norm is given as input to the systems. Below we discuss cases in which more refined norms are useful. To use them in the systems of [LS97] or [SSS97], one would either need to backtrack over different proof attempts (which would be extremely inefficient) or be very demanding on the creativity of the user. In our system, as opposed to [LS97] and [SSS97], the only input consists of the query pattern. Next, we evaluate the efficiency. All tests have been performed on similar machines. In the case of [LS97], the machine was a SPARCstation 10 model 51 clone. Both the results from [SSS97] and ours were obtained on a SPARCserver 1000. We deliberately did our experiments on this fairly old machine in order to make the comparison with the two other systems feasible. The SPECint92 ratings for these two machines are respectively 65.2 and 60.3, which are quite comparable. Overall, [SSS97] tends to be the fastest system, ours being somewhat slower and the one of [LS97] the slowest. On most of the very small examples in the benchmark (those in Table 7.1 and most of those in Table 7.2) our timings range between being equal to, to being at most double of the ones of [SSS97]. On the somewhat larger examples, including most of the sorting examples, [SSS97] tends to be up to five times faster than we. Note that in these cases, [LS97] can sometimes be 500 or more times slower than [SSS97].

128

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

In evaluation of these differences, it should be noted that the work in [SSS97] was not aimed at developing a novel approach to termination analysis, but at providing a highly optimised implementation of an existing termination analysis technique - optimising some aspects of that technique in the process - using the very efficient programming language Mercury. Our own Prolog implementation is only a prototype. Providing an optimised implementation, in the style of [SSS97], would be a different research issue. Moreover, our system was designed to be able to cope with partially instantiated input, while [SSS97] is for well-moded programs only. As a result, our analysis includes a richer component for combined mode-and-type analysis, which is likely to be (partly) responsible for the increased performance gap between our results and those of [SSS97] for larger programs. Note that our technique has been developed for directly recursive programs only. In order to deal with the mutually recursive programs in the benchmarks (dis - con in Table 7.1, mergesort.t, pl8.4.1 and pl8.4.2 in Table 7.3) we extended the system with a straightforward mutual-to-direct recursion transformation. In the case of predicates with only one argument position (dis - con and the even and odd of pl8.4.1), this transformation just adds a predicate symbol on top of the original predicates. As an example: even(0) even(s(x)) odd (s(x))

odd (x): even(x):

becomes: m(even(0)) m(even(s(x))) m(odd (s(x)))

m(odd (x)): m(even(x)):

For predicates with multiple arguments, this transformation is unreasonably weak, because the m=1 predicate has only one argument and therefore the mode information for the original predicates gets lost (the single argument of m=1 will typically get mode ‘any’ for all calls). Also, interargument relations ranging over the arguments of the original predicates can no longer be expressed in terms of the one argument of m=1. In these cases, we used a transformation similar to the one above, but now we maintain the old arguments of mutually recursive predicates as different arguments of the m predicate and add one extra argument to represent the old predicate symbol (as a functor of arity one). Going back to the even/odd example, the transformation would then yield: m(even( ); 0) m(even( ); s(x)) m(odd ( ); s(x))

m(odd ( ); x): m(even( ); x):

Note that this is a very weak approach to dealing with mutual recursion and that it puts extra precision requirements on the termination analysis. Specifically in clauses of the type:

5.6. EXPERIMENTAL EVALUATION

129

e(x; y)

t (x; y)

taken from pl8.4.2, which become transformed to m(e(

); x; y)

m(t (

); x; y)

it is now crucial to allow norms that measure different functor symbols in different ways. In particular, the term size norm would be insufficient. In the tables, this weak treatment has its effect on the relatively slow treatment of dis - con, mergesort.t and pl8.4.2. In future work, we aim to reformulate the entire technique in terms of mutual recursion, using the minimal cyclic collections of [DVB92]. Program

Query

mergesort.t pl1.1 pl1.1 pl1.2 pl2.3.1 pl3.5.6 pl3.5.6a pl4.01 pl4.4.3 pl4.4.6a pl4.5.2 pl4.5.3a pl5.2.2 pl6.1.1 pl7.2.9 pl7.6.2a pl7.6.2b pl7.6.2c pl8.2.1 pl8.2.1a pl8.3.1 pl8.3.1a pl8.4.1 pl8.4.2

mergesort(g,f) append(g,g,f) append(f,f,g) perm(g,f) p(g,f) p(f) p(f) append3(g,g,g,f) merge(g,g,f) perm(g,f) s(g,f) p(g) turing(g,g,g,f) qsort(g,f) mult(g,g,f) reach(g,g,g) reach(g,g,g,g) reach(g,g,g,g) mergesort(g,f) mergesort(g,f) minsort(g,f) minsort(g,f) even(g)/odd(g) e(g,f)

Us

LS

SSS

Suc

Term

0.43 0.03 0.03 0.16 0.04 0.06 0.05 0.05 0.08 0.09 0.03 0.03 0.09 0.13 0.08 0.10 0.14 0.11 0.29 0.31 0.24 0.20 0.02 0.44

0.43 0.14 0.11 1.05 0.01 0.01 0.06 0.26 1.99 0.31 0.03 0.00 4.26 131.18 0.43 0.05 0.25 28.23 35.82 26.52 2.90 5.72 0.22 4.64

0.06 0.03 0.03 0.08 0.03 0.04 0.05 0.07 0.04 0.06 0.03 0.04 0.07 0.07 0.03 0.03 0.18 0.03 0.06 0.06 0.04 0.06 0.03 0.18

–+– +++ +++ +–+ ––– ––– ++– +++ +++ +++ ––– ––– ––– +++ +++ ––– ––– +++ ––– +++ ––– +++ +++ +++

+ + + + – –

Table 7.3: Results on the Pl¨umer test suite ([Pl¨u90]).

+

+ + + –



– + + – – + + + + + + +

130

CHAPTER 5. CONSTRAINT-BASED TERMINATION ANALYSES

Another observation that can be made from the tables is that we have a relatively slow performance on those examples which do not terminate, or for which we fail to find a proof. The reason for this is that we consider more potential termination proofs than the other approaches. Because we do not commit to one specific norm, when our analysis fails to prove termination, it has actually shown that the termination condition fails for all possible norms of the given type. In a successful proof, this has little effect on the efficiency of the analysis, since the system will terminate as soon as it finds one successful proof. In a failing proof, the constraint solver will take more time in establishing that no solution can be found. This inefficiency might be reduced by taking a satisfiability checking approach to the constraint solving, instead of the finite domain, enumeration and pruning based approach that we currently use. Given the above observations, we believe that the experiments show the feasibility of the approach. For future work, it would be interesting to develop a much more optimised version of our prototype, preferably written in Mercury or C, and focussed on well moded programs only, so that a more accurate comparison with [SSS97] would be possible.

5.7 Contribution The main contribution from the work above is to automate termination analyses, integrating different stages in the proof to one stage, speeding up the proof procedure and enhancing the accuracy of the method. This is joint work with Stefaan Decorte and Danny De Schreye. The contribution from the author concerns automatisation of the last part of the termination proof. Given the validity and rigid acceptance conditions, how to find values for the parameters of the norm, level mappings and interargument relations. Taking into account what constraints are solvable using a finite domain solver and how they could be solved.

Chapter 6

Scheduling the Maintenance of Power Plants This and the next chapter are about real-life problems tackled with CLP. These experiments were done:

  

to gain some expertise in writing CLP programs; to find out the possibilities and limitations of CLP technology; to test and evaluate our home-brewed CLP solver ROPE [VD94].

Several of these experiments were performed in collaboration with master students. Here we can draw a direct conclusion. CLP is a very powerful paradigm in which very quickly correct and non-trivial programs can be written. Unfortunately writing CLP programs is a skill which is for the student quite hard to acquire within one academic year. It is hard to believe for the students that the solver will solve the problem for them and, at least in the initial stage, they only have to make a proper problem description. After they grasped this idea the real works starts. Coming up with a model, that is correct and is suitable to be handled with CLP, is almost impossible. The final step, encoding the model, is within their capabilities but seems to last forever the first time. The problem discussed in this chapter is the scheduling of the maintenance of the power units in the power plants of Flanders. The experiment reported here started as a double project. In the first part of this experiment, a finite domain CLP program was constructed. In the second part of the experiment, a declarative specification of the expert knowledge on the maintenance domain was created [DVS+ 97]. For this purpose (an extension of) the language OLP-FOL, an integration of classical logic and Open Logic Programming was used. Then a solution of the scheduling problem was computed using this specification. This solution was computed by an integration of SLDNFA, an abductive reasoner for OLP-FOL, and the finite domain CLP system ROPE. This double experiment was done in the context of a master’s thesis [SB96]. Later on a new experiment was 131

132

CHAPTER 6. SCHEDULING THE MAINTENANCE OF POWER PLANTS

started to gather experiences with the combination of different CLP technologies [Van98]. In cooperation with IC-Parc ECLi PSe was used to write a new version which integrates finite domain and linear programming methods. To obtain a better insight in the interaction between models and solver methods, the problem was also solved using CHIP. In the following sections we will:

   

explain the problem; look at CLP(FD) solutions using different solvers; describe how the problem can be tackled using OLP-FOL; report on solving the problem combining clp(FD) with Linear Programming.

6.1 Maintenance of Power Plants The Belgian electricity company Tractebel has a number of power plants organised in geographic areas. Each power plant has a number of power generators, in the sequel units. Every year each unit must receive a fixed number (usually 1 or 2) of preventive maintenances. These preventive maintenances enhance the reliability and lifetime of the units. Such maintenances have a known duration. Planning the maintenance of its production units is a general problem for all electricity companies. The key issue is that electric energy cannot be stored. At any time enough capacity must be available to supply for the demand. The computational problem is to schedule these maintenances according to some constraints and optimality criteria. Constraints are of the following form:

     

Some time slots can be prohibited for maintenances of some units. For each power plant, there is an upper limit on the total number of units in maintenance per week for reasons of availability of personnel. The period of some maintenances is fixed in advance. Some maintenances must be non-simultaneous because too few people or machinery is available. Some maintenances must be scheduled simultaneous as specialised people from abroad are needed. For each area, there is an upper limit to the total number of Megawatts not available because of maintenances. Otherwise, energy flow between areas may exceed network capacity.

The objective of the optimisation problem is to maximise the minimal weekly reserve, i.e., the difference between the total operational capacity (not in maintenance) and the expected weekly peak load. This minimises the risk that a break down of a unit or an

6.2. THE CONSTRAINT PROGRAMMING APPROACH

133

unexpected peak in consumption causes a shortage of electrical energy. A secondary aim of the planning is to have a somewhat stable surplus of energy which can be sold abroad. The data given by the electricity company concerned 45 maintenances to be planned over the weeks of one year. This results in a search space of the order 5245 . Even though the manufacturer was satisfied with near optimal solutions, finding such a solution remains a hard problem. The data for this problem are given in Appendix A.1.

6.2 The Constraint Programming approach In this section we describe several programs running under different systems. All of the programs use Finite Domain Constraint Programming. In a later section a program combining Finite Domain Constraint Programming with Linear Programming will be reported on. The initial program was written for the solver ROPE. This program uses the classical approach of: 1. first creating a number of variables; 2. then creating a number of constraints on these variables; 3. and finally calling the solver (the enumeration predicate) with an optimality function for finding solutions. Later on the program was slightly adapted to work with SICStus and ECLi PSe . Also a version was encoded to work with CHIP [CHI97].

6.2.1 A High Level Model In this section we describe a high level model where all implementations using Finite Domain CLP in ROPE, SICStus, ECLi PSe and CHIP are based on. It is high level because the model cannot be implemented “as is” with the different CLP systems. When scheduling maintenances (tasks) over the weeks of the year, a straightforward approach is to associate with each maintenance a variable Starti;m which describes in which week the maintenance m for unit i starts. A number of constants are used for expressing the constraints.

     

units is the total number of units in the scheduling problem. durationi;m is a constant representing the duration of maintenance m of unit i. mainti is the number of maintenances for unit i. max p is the maximum number of units allowed to be on maintenance in plant p. areaa is a set of all units in area a. plant p is a set of all units in plant p.

134



CHAPTER 6. SCHEDULING THE MAINTENANCE OF POWER PLANTS

capi is the capacity of unit i expressed in Megawatt.

Each Starti;m variable takes a value from the domain f1; : : : ; 53 durationi;mg. Now we define constraints for each requirement in the problem specification over these variables Starti;m .



No overlapping maintenances for one unit For each unit i, no maintenances of i may be simultaneous, while unit i has mainti maintenances:

8i m 1 m 2 1  m 1 ;



;


E2 + Post

Expressing the limit on the maximum amount of Megawatts on maintenance simultaneously in one area also leads to a new second order predicate: sum(V; F [V; W ℄; Sum). This expresses that for a given W the summation of all V such that F [V; W ℄ is true is Sum.

8A Max W : ;



;

;

area(A) ^ area max(A; Max) ^ week(W )^ sum( Megawatt ; on maint (U ; W ) ^ unit o f plant (U ; P)^ plant o f area(P; A) ^ capacity(U ; Megawatt )); Sum) ! Sum  Max

The theory should also include a rule expressing that a maintenance should never be performed during a prohibited period:

8U BP EP M : unit (U ) ^ prohibited (U BP EP) ^ maint o f ^start (M B E ) ! E BP _ B EP ;

;

;

;

;

;

;




unit (M ; U )

150



CHAPTER 6. SCHEDULING THE MAINTENANCE OF POWER PLANTS

Simultaneous maintenances are handled by:

8M1 M2 B1 B2 E1 E2 : ;



;

;

;

;

simul (M1 ; M2 ) ^ start (M1 ; B1 ; E1 ) ^ start (M2 ; B2 ; E2 ) ! (B1  B2 ^ E2  E1 )_ (B2  B1 ^ E1  E2 )

Leaves us to express that two maintenances of the same unit should not overlap.

8M1 M2 U ;

;

;

B1 ; E1 ; B2 ; E2 : unit (U )^maint o f unit (M1 ; U ) ^ maint o f unit (M2 ; U )^ start (M1 ; B1 ; B2 ) ^ start (M2 ; B2 ; E2 ) ! E1 < B2 _ E2 < B1

The resulting theory is an axiomatisation of what is a correct schedule; in the sense that its models comprise correct schedules. The theory does not express the optimality criterion. The theory is a pair (P; T ), with T is a set of -7- axioms and P a set of definitions for all except one predicate: start.

6.3.3 Scheduling by Abductive Reasoning Given such a high level description in OLP-FOL it is quite challenging to automatically use this theory for computing (and optimising) a schedule. The solutions for the scheduling problem are given by the models of the theory (P; T ), in particular the set of start atoms in the models. The solutions can also be characterised as abductive solutions. For a given OLP-FOL theory (P; T ) and query Q, an abductive solution is a set of open atoms ∆ such that P + ∆ j= Q and P + ∆ j= T . In the case of the scheduling theory, Q can be taken to be true. To solve the scheduling problem, the implementation departed from an existing implementation of SLDNFA resolution [DD92, DD97b], an abductive procedure for OLP-FOL. To solve the problem at hand, three components had to be added to the procedure:

  

a treatment of the second order operators card and sum; an optimality function for abductive solutions; a finite domain constraint solver

To add finite domain constraint solving, the SLDNFA procedure was coupled with the ROPE system: when SLDNFA selects a constraint (equality or disequality, or domain specification) this constraint is given to the ROPE system. The technique to deal with the second order operators was inspired by the way the two second order constraints in the scheduling problem were solved directly in CLP. When an atom card (U ; F [U ; t ℄; NR) is selected, the procedure rewrites the formula F [U ; t ℄ using the definitions, until a disjunction of conjunctions is obtained with only constraint primitives and open predicates. Each disjunct represents a residual condition under which F [u; t ℄ is true for some object u. For

6.4. USING BOTH FINITE DOMAIN CLP AND LINEAR PROGRAMMING

151

each disjunct, a boolean variable is created such that it is true iff the disjunct is true. The value of NR can then be expressed logically as the sum of these boolean variables. Both the value of the boolean variable and of NR are expressed as ROPE constraints. The current prototype implementation is not complete and can be extended much further. However, the procedure is domain independent and can handle a broad class of scheduling problems with second order constraints. The system computed an acceptable but non-optimal schedule with a reserve level of 2020 Megawatt in 24h. Given the high complexity of the problem, the occurrence of second order predicates in the specification and the fact that the specification was written in an -almost- entirely declarative way, we consider this as a successful experiment. It shows the feasibility of the use of declarative logic even for computationally complex problems.

6.4 Using both Finite Domain CLP and Linear Programming Another topic of interest is the combination of the power of finite domain Constraint Technology and the fast methods from Linear Programming. It is true that in a lot of Finite Domain CLP applications a large amount of linear constraints is involved combined with a small amount of non-linear combinatorial constraints. Not only the non-linear constraints make it difficult to solve the problem with pure Linear Programming, usually one also wants an integer solution. Both Finite Domain technology and Linear Programming methods have ways to handle these problems. As already mentioned the non-linear combinatorial constraints and the desire for an integer solution is a problem for Linear Programming. On the other hand Finite Domain CLP is, in comparison with Linear Programming, quite bad at handling large amounts of linear constraints. Combining these two methods could have several advantages. Information deduced by one method could be used by another, both CLP(FD) and Linear Programming could e.g. narrow the bounds of domains using different methods. Also enumeration could be guided by non-integer solutions found by a Linear Programming tool [Eur98]. People from the ECLi PSe team, during the experiment hosted at IC-parc, were also interested in that topic. They build a library for ECLi PSe that besides adding the constraint to the CLP(FD) engine also forwards the linear constraints to the commercial Linear Programming Package CPLEX. As we do not have a licence for the CPLEX at our departement all experiments using CPLEX were run at IC-parc by Robert Rodosek, also interested in this topic [RWH98].

6.4.1 Some first (failing) experiments The first attempt started with the model in Section 6.2.1 with the constraints illustrated in Section 6.2.2 using the encoding in ECLi PSe . In a first attempt the existing application implemented for ECLi PSe was used with the finite domain library integrated with CPLEX. This did not yield better results. Passing the linear constraints from the program

152

CHAPTER 6. SCHEDULING THE MAINTENANCE OF POWER PLANTS

to the CPLEX solver only caused extra overhead, but no extra pruning or other advantages were achieved. In a next attempt the disjunctive constraints (constraint (6.9) and constraint (6.14)) implemented with Evaluation Constraint Predicates and User-defined Constraints, as can be seen in Figure 6.1 and Figure 6.2, were transformed using the technique described in [RWH98]. This technique consists of introducing boolean variables, such that all constraints become linear. The constraint NotOnMaintw;i;m = (w < Starti;m _ w >= Starti;m + durationi;m)

(6.9)

is then translated with two additional boolean variables B1 and B2 to: NotOnMaintw;i;m  B1 + B2; NotOnMaintw;i;m  B1;

NotOnMaintw;i;m  B2; w + 1 + B1  52  Starti;m + 52;

Starti;m B1  52  w; Starti;m + durationi;m + B2  (52 + durationi;m)  i + 1 + 52 + durationi;m w

B2  (52 + durationi;m)  Starti;m + durationi;m

(6.26)

The boolean variable B1 represents the truth-value of the expression w < Starti;m and b2 the truth-value of the expression w >= Starti;m + di;m . Then these two booleans variables are connected trough linear constraint with the variable NotOnMaintw;i;m . Similar the constraint (6.14): Starti1;m1 + durationi1;m1 + post  Starti2 ;m2 _ Starti2;m2 + durationi2;m2 + post  Starti1 ;m1

(6.14)

can be transformed to linear constraint using the same technique. This, contrary to our expectation, did not yield any performance gain.

6.4.2 The Second Model The model from Section 6.2.2, that we were using up to now in our experiments, could be the reason for our bad results. It might be better to have constraints over boolean variables with most coefficients equal to 1. This could lead to Total Uni-modular constraints [Sch86]. Total Uni-modular problems have a coefficient matrix where all coefficients are 0, -1 or +1 and all subdeterminants are 0, -1, +1. Such problem descriptions have the property that a solution for the problem will be an integer solution! Achieving this Total Uni-modular property is in general not possible, but an almost Total Unimodular model could lead to good results as well.

6.4. USING BOTH FINITE DOMAIN CLP AND LINEAR PROGRAMMING

153

In the second model, for each maintenance m of unit i, 52 boolean variables Mm;i;w are created for w ranging from 1 to 52. Mm;i;w becomes 1 if maintenance m of task i starts in week w. This leads to the following linear constraints:



Basic Constraints The representation of 52 boolean variables for each maintenance allows incorrect states. In a correct solution exactly one of these variables must be 1, the others 0: 52

8i m 1  i  u 1  m  mainti : ∑ Mm i w = 1 ;

;

;

(6.27)

; ;

w=1



End of the year All maintenances should be finished by the end of the year. In the first model this is achieved by initialising the domains of the Start jk variables such that the maintenances do not start to late. Here we have to set the booleans to zero at the end of the year:

8i m w 1  i  u 1  m  mainti 52 ;

;

;

;

;

durationm;i + 1  w  52 :

Mm;i;w = 0



(6.28)

No overlap When there is more than one maintenance for one unit, they should not overlap. In the previous model this is done with constraint (6.10).

8i m1 m2 w1 w2 1  m1 ;

;

;

;

;

w1

< m2  mainti ; 1  w1  52; durationm2;i + 1  w2  w1 + durationm1;i

Mm1 ;i1 ;w1 + Mm2 ;i2 ;w2



(6.30)

Prohibited period Similar to constraint (6.11) in the previous model we have to express that certain periods are prohibited for maintenance. Suppose the period [start ; end ℄ is forbidden for every maintenance on unit i.

8m w 1  m  mainti start ;



1

(6.29) 1:

;

;

durationm;i + 1  w  end : Mm;i;w = 0

(6.31)

Maximum number of units on maintenance in one plant Remember that max p is a bound on the units on maintenance in plant p. mainti

8w 1  w  52 : ∑ ∑ ;

w



i2 plant p m=1 v=w dm;i +1

Mm;i;v  Max p

(6.32)

154

CHAPTER 6. SCHEDULING THE MAINTENANCE OF POWER PLANTS

This constraint in the second model should be compared with constraint (6.12) on page 136 in the previous model.



Fixed maintenances Given maintenance m for unit i at week w then Mm;i;w = 1 (similar to constraint (6.13)on page 137 in the previous model). Again the model can be optimised avoiding generating boolean variables for these maintenances.

8m1 w1 1  m1  mainti m1 6= m w durationm i + 1  w1  w + durationm i ;

;

;

1;

Mm1 ;i;w1



(6.33)

;

;

1:

=0

(6.34)

Non-simultaneous maintenances Given maintenance m1 for unit i1 and maintenance m2 for unit i2 should be performed non-simultaneously with rest period of post between the maintenances.

8w1 w2 1  w1  52 ;

;

;

max(1; w1 duration2 post + 1)  w2 w2  min(52; w1 + post + duration1 1) : Mm1 ;i1 ;w1 + Mm2 ;i2 ;w2

1

(6.35)

This restriction on maintenances was expressed by constraint (6.14) on page 137 in the first model.



Simultaneous maintenances Given maintenance m1 for unit i1 and maintenance m2 for unit i2 , durationi1 < durationi2 , should be performed simultaneously ( expressed by constraint (6.15) in the previous model ).

8w1 1  w1  52 : Mm ;

1 ;i1 ;w1

Mm2 ;i2 ;w1



 

w1



Mm2 ;i2 ;w2 and

w2 =w1 durationi2 +durationi1 w1 +durationi2 durationi1



w2 =w1

Mm1 ;i1 ;w2

(6.36)

Area Constraint Restriction on the number of Megawatts on maintenance in area k mainti

8w 1  w  52 : ∑ ∑ ;

w



i2areak m=1 v=w durationm;i +1

Mm;i;v  capi  maxAreak

This was expressed by constraint (6.16) in the first model.

(6.37)

6.4. USING BOTH FINITE DOMAIN CLP AND LINEAR PROGRAMMING



155

Objective Function Assume for unit i and week w that u mainti

w

Mm;i;v  capi

(6.38)

8w 1  w  52 e(w spare) 2 exceptional : Consumed  MaintWeekw + peakw

(6.39)

8w e(w spare) 2 exceptional : maxCapacity spare  MaintWeekw + peakw

(6.40)

MaintWeekw = ∑





i=1 m=1 v=w durationm;i +1

Then a new variable Consumed should satisfy ;

;

;

;

=

;

where (maxCapacity Consumed ) is the amount of spare energy in non exceptional weeks. Exceptions are given by terms e(w; spare), telling that week w is an exceptional week and should not be used in the optimisation function. spare is the actual spare energy for that week. Those exceptional weeks are introduced to avoid the problem explained in Section 6.2.4 where some weeks had unavoidable amount of fixed maintenance or expected peak consumption. The optimisation constraint for non-exceptional weeks could as well be expressed by:

8w 1  w  52 e(w spare) 2 exceptional : Reserve  maxCapacity MaintWeekw ;

;

;

=

peakw

(6.41)

where Reserve must be maximised. This introduces the large number maxCapacity in the linear constraint store, which should be avoided if possible3 .

6.4.3 Upper bound on the optimal solution The resulting program was executed at IC-Parc using ECLi PSe , using the special finite domain library connected to CPLEX. At an initial stage, it looked as if the program was performing not really better than the original approach. Then it turned out that two types of constraints were responsible for the poor results, because they are not “Total Unimodular”:

 

the restriction on the number of Megawatts on maintenance in one area (constraint (6.37)) and the limit on the maximum number of units on maintenance in one plant (constraint (6.32)).

3 Large

numbers in the linear constraint store triggers unstable computations

156

CHAPTER 6. SCHEDULING THE MAINTENANCE OF POWER PLANTS

12000 11500 11000 10500 10000 9500 9000 8500 8000 7500 7000 0

10

20

30

40

50

Figure 6.6: A solution for the relaxed problem (Level=2100)

The first constraint, because the coefficients of the booleans are the number of Megawatts produced by the unit connected to the boolean. In the ideal case they should all be 1. In the second variant, the constant is not 1 (was also the case for the first constraint). So these constraints, though linear, did not have the properties needed to use fast specialised algorithms in the linear solver. Relaxing the problem, by removing all the constraints of the mentioned type, led quite quickly (10 minutes) to a solution with a reserve level of 2100 as can be seen in Figure 6.6. It is not straightforward how these constraints could be transformed to constraints of the proper type (especially the first one). From the picture we can easily see that the solution violates the restriction on the number of Megawatts on maintenance in one area during the summer. On all correct solutions (Figure 6.3,6.4 and 6.5) there is an unusual amount of reserve during weeks 29, 30 and 31. This is because the energy consumption is very low during these months. During these months the restriction on the number of Megawatts on maintenance in one area becomes active. This is not the case in the relaxed solution. The results obtained with the relaxed problem is very useful:



It gives a precise upper bound on the value of the optimisation function. Since there seems no method for finding the optimal solution this is crucial information. An alternative method would be to add up all peak consumptions and for each maintenance the capacity of the involved week multiplied by the duration. Then dividing this total sum by the number of weeks. This new upper bound would

6.5. CONTRIBUTION

157

necessarily be less precise as it will not take into account holes like on week 30, 34, 39, 51 and 52 in Figure 6.6.

 

The relaxed constraint are not the major constraints from the problem. There are several alternatives for reintroducing the unsatisfied constraints into the schedule: – Use local techniques like switching maintenances from the relaxed solution until the resulting schedule does not violate constraints. – Reformulate the constraints removed from the model such that they are incorporated into the optimisation function. – Make choices before finding a solution, such that the removed constraints can never be violated. For each of the choices another simple optimisation problem is generated.

6.5 Contribution We have illustrated the use of different models for solving a scheduling problem. In the description several systems were used:

    

The clp(FD) library from SICStus. The plain Finite Domain library from ECLi PSe . The Finite Domain library from ECLi PSe combined with the linear programming software CPLEX. The CHIP system. The home-brewed system ROPE.

The best solution was first found using the clp(FD) library from SICStus. Later we found an equivalent solution with CHIP as well. A relaxed problem was solved using the Finite Domain library from ECLi PSe combined with the linear programming software CPLEX. The CHIP system was the best system for returning reasonable solutions. Our experiments also show that the combination of Finite Domain Constraint Logic Programming with Linear Programming is very powerful. The powerful modelling facilities of CLP, combined with the flexible search control, the use of multiple solvers and the skill of an experienced user can quickly lead to efficiently running programs. The consequences can be revolutionary - with programmers actually taking modelling seriously. In addition the problem was solved using a very high-level specification language OLP-FOL. In this specification language the problem was described in First Order Logic and executed by SLDNFA combined with our Finite Domain solver ROPE. The main results from this experiment is showing that the execution of a specification of a scheduling problem in First Order Logic can be made executable.

158

CHAPTER 6. SCHEDULING THE MAINTENANCE OF POWER PLANTS

Chapter 7

A few other applications In this chapter two other problems are tackled. The first problem is optimising a set of securities. The idea is, given a portfolio of securities, to optimise this portfolio given the preferences of the client, the restrictions of the bank and the current situation on the market In the experiments described the aim was to solve the problem with CLP based on Linear Programming. The second application is a smaller but nevertheless interesting problem. It concerns the distribution of master students over the different research groups of the department of computer science of the KUL taking into account the preferences of the students, the sizes of the research groups and level the average quality of students in the different groups as well.

7.1 Optimising a Set of Securities This subject was provided by a Belgian bank. Some initial steps in the development were done in the context of a master’s thesis by Stefaan Van Assche [Van97]. The subject of the application concerns supporting the investment consultant from the bank while advising costumers. It is not a good idea to keep your money inside the back of your sofa. Even if the amount is small, it is always better to invest this money, gaining some return. There exists a large variety of investment possibilities: from bank accounts up to shares in gold mines, all of them with different characteristics. On the other hand the customers wanting to invest their savings have different expectations from these investments. Some want their money available at any time. Some are willing to take large risks in return for a possible high appreciation. Usually the customer has already some investments, where the investment consultant should advice changes in this set of investments. Also the advisor should carefully apply the latest rules issued by the investment division of the bank: on how to spread the capital over the different investment areas ( shares, bonds, money, real

159

160

CHAPTER 7. A FEW OTHER APPLICATIONS

estate); which currencies one should avoid, : : : . Given the wide range of possibilities and the ever changing wishes of the costumer the application should compute the ideal change in the investments of the customer.

7.1.1 A detailed look at the investment problem An advice from the investment consultant involves proposing a new portfolio given the old portfolio, the profile of the customer and the policy of the bank. portfolio As already mentioned one should not keep his property in bare money. One should invest the property to increase the value. The disadvantage of investments are the risk one takes. It is possible that next to the house you bought a railway is constructed. The company from which you bought shares could go bankrupt, : : : . The essential of investment is to enlarge the capital as fast as possible with as few risk as possible. The main method to reduce the risk is spreading the capital among different investments. The set of these different investments in securities is called a portfolio. Some of these investments have a small risk, but also a low expected return. One should have these to rely on in bad times. On the other side of the range there are the investments with a very high risk, but also a very large expected return. These type of investments make the portfolio worthwhile in good times. When investing into high risk securities, one should have different independent papers. customer profile Of course not all of the customers have the same expectations on the return, risk, availability of their capital. The typical family man has no desire in buying high-risk shares. Other people, living in a world of sports cars and beautiful women, get a kick from high-risk investments. Some people have an emotional binding with Russian Ruble. These properties and expectations of the customer we will call the customer profile. bank policy Each bank employees a number of experts studying the financial market in all its dimensions. These people issue guidelines that should be followed within the bank (not only for the customers, but also the investments done by the bank itself). The main aim of the project was checking the suitability of CLP for this type of application. For this purpose it seemed harmless to restrict the range of investments to bonds. Of this type of investments detailed information is available. Still the number of possibilities is large. Our data set counted more than 3000 different bonds. Such bonds have a number of properties: Name of the debtor Name of the company or the state that issues the bond. Nominal value The value of one bond, expressed in the currency of the country where it was issued. The nominal value is in principle the amount of money given to the debtor and is returned in the end. This is the base where the interest is calculated

7.1. OPTIMISING A SET OF SECURITIES

161

on. Remark that often the buyer buys the bonds at lower or higher price than the nominal value. Coupons Pieces of papers connected to the bonds. They are used for the distribution of the interest. The owner is entitled to exchange these papers on the dates mentioned on it for the interest. Periodicity The period between two dates on the coupons. This is usually yearly. End expire date The date on which the last coupon is payed and also the value of the bond is returned to the owner of the bond. Remaining term The time between the current and the end expire date. Repayment price The part of the nominal value of the bond that is payed to the owner on end expire date. This is usually 100 percent. There exist bonds with 0 percent repayment price. Issue currency Currency in which the bond is issued. Nominal interest rate The interest rate connected to the coupons of the bonds. This is the interest rate to compute the payment done at the expire date of the coupons to the owner. This interest rate is applied on the nominal value of the bond, in the issue currency. Exchange rate Apart from the sale of new bonds ( primary market), also older bonds can be sold and bought (secondary market) between investors. The prices of these bonds are subject to supply and demand. Many factor like expire dates, issue currency, general interest expectations, : : : are important for these exchange rates. This exchange rate represents the actual value of the bond. Selling and buying price This is the price the seller get/buyer pays in his own currency, in our case Belgian Frank (BEF), including cost for currency changes. Also the interest that must be payed to the owner from the previous expire date until the current date is intercalculated. Actual interest The actual interest rate the owner gets from now until the end expire date taking into account the Exchange rate, repayment price and the nominal interest rate. Debtor risk Some debtors are more reliable than other. Special offices, e.g. Moody’s Investors Service, estimate the reliability of the debtors. They connect a rating to each of the debtors on a well accepted scale. An important property not immediately connected to a specific bond, but to all bonds in the same currency and the same expire date is the Running Time Risk. This risk

CHAPTER 7. A FEW OTHER APPLICATIONS

162

involves the currency devaluation (inflation) and the interest rate expectations. High inflation is usually connected with high interest rate. When the interest rate becomes larger than the nominal interest rate, the exchange rate starts dropping. The longer the remaining term, the more the exchange rate will drop. Another important aspect, also connected to the expire date of the bonds, and to lesser extend the currency is the Re-investment Risk. When at the expire date, few possibilities are expected for re-investment of the returned money the investor has a serious problem. Such a risk increases when a low interest rate is expected at the expire date. Needless to say that the issue currency is a very important aspect. Usually bonds in an unreliable currency have a much higher nominal interest rate. Therefore the Currency Risk is a very import property when buying bonds in other currencies. Another aspect important for the buyer of bonds is the costs connected to changing his portfolio. These cost must be payed to the bank and other instances to perform the transaction. Indeed it is sometimes possible to change the portfolio of the costumer such that it suits better to his profile and the guidelines of the bank. But the cost connected to selling some of the securities and buying other securities is too high to make it worthwhile. These costs should be included into our model they are called the Arbitration Costs. From the characteristics of bonds a number of characteristics can be computed on a complete portfolio or a change to a portfolio:

        

Total value Mean and bounds on actual interest Mean and bounds on debtor risk Mean and bounds on remaining term Mean and bounds on currency risk Mean and bounds on running time risk Spread over different currencies Spread over different remaining term Arbitration cost (in case of a change to a portfolio)

The profile of the customer may contain wishes on all of these characteristics. Next to that the customer may have wishes on specific bonds. When proposing a new portfolio, usually there is a very wide range of possibilities. This makes it possible to perform an optimisation. Several optimisations are possible:

  

Mean actual interest Mean debtor risk Arbitration cost

7.1. OPTIMISING A SET OF SECURITIES



163

Other risks

The customer profile should indicate how to perform the optimisation. This can be one of the items above, while the other characteristics are bounded, or a combination of the optimisations above.

7.1.2 Defining the input for the problem Data is supplied with Prolog facts in the following format:



Bonds on the secondary market: euromarktsec(Identifier, Debtor, End_expiry_data, Exchange_rate, Currency, Actual_interest_rate, Debtor_risk, Remaining_term, Running_time_risk, Buying_price, Selling_price, Nominal_value).



Rate of currencies(compared to Belgian currency): currency(Identifier, Name_currency, Rating).



Currency risk combined with bank policy on running time risk. This is based on the rating of Moody’s. These ratings are translated into integers as follows: Aaa!1, Aa1!2, Aa2!3, Aa3!4, A1!5, A2!6, A3!7, Baa1!8, Baa2!9, Baa3!10, Ba1!11, Ba2!12, Ba3!13, B1!14, B2!15, B3!16, Caa!17, Ca!18, C!19. The policy of the bank concerning running time risk is translated on a lower bound and upper bound on the remaining term of the bonds expressed in days. Sometimes the bank advises not to buy certain currencies. Then the last two arguments contain the atom “not”. currency_risk(Currency, Currency_rating, Lowerbound_term, Upperbound_term).



The old portfolio is given in the format: old_port_folio(Identifier_portfolio, Identifier_euromarktsec, Quantity).

The preferences of the user are given into facts like:

CHAPTER 7. A FEW OTHER APPLICATIONS

164

      

min_act_interest(Percent). minimum actual interest rate the costumer wants. max_act_interest(Percent). maximum actual interest rate the costumer wants. min_debtor_risk(Rating) Minimum on the risk rating. max_debtor_risk(Rating) Maximum on the risk rating. min_currency_risk(Rating) Minimum on the currency risk rating. max_currency_risk(Rating) Maximum on the currency risk rating. :::

7.1.3 Tackle the investment problem with CLP Solving this kind of problem as a finite domain application would result in solving an integer problem with approximately 9000 finite domain variables with very large domains (+/-10000 elements), this is unsolvable on current computers and technology. Solving the problem over the reals seems a better approach. Rounding the amount of bounds bought or sold to the nearest integer will usually result in only a small deviation of the initial budget. The most applicable CLP technology then becomes systems based on Linear Programming. So we should take care to include only linear constraints in the model. For each possible bond i three CLP variables are created: NewNumberi , Soldi and Boughti. NewNumberi is the number of securities in the new portfolio, Soldi is the number of securities sold and Boughti is the number of new bonds added. Of course there are some restrictions and relations between these variables given initiali , the amount of bonds i in the old portfolio: NewNumberi = Boughti NewNumberi  0; Boughti  0;

Soldi  0; Soldi  initiali :

Soldi + initiali

(7.1) (7.2) (7.3) (7.4) (7.5)

These constraints are actually not enough. There should be a constraint that Boughti and Soldi cannot be non-zero simultaneously. This cannot be expressed with linear constraints without introducing boolean variables. The cure is to leave the constraint out of the model, but count on the minimisation of the arbitration cost. The restrictions on lower bounds and upper bound of the properties of bonds can be used as a preprocessing step. These restrictions could be part of the costumer profile, or part of the policy of the bank. For each bond that does not satisfy all the condition the

7.1. OPTIMISING A SET OF SECURITIES

165

variable NewNumberi can be set to zero. If the bond is not present in the old portfolio one should actually avoid generating the CLP variables for such a bond. There are a number of global variables: Sale the total amount of money received from selling bonds, Bought the price of all papers added to the portfolio and Cost the money to be payed for the transactions. valuei is the mean value of the bonds. We assume that the buying price is about one percent higher than this mean value. Similarly, the selling price is about one percent lower. The cost connected to selling and buying is also approximated as one percent. This cost is in real live far more complicated, but the approximations will do for our experimental purposes. n

Sale = ∑ Salei  valuei  0:99

(7.6)

Bought = ∑ Boughti  valuei  1:01

(7.7)

i=1 n i=1

Cost = 0:01  Sale + 0:01  Bought

(7.8)

Other global constraints compute the total value of the resulting portfolio and restrict the variables of the problem such that the whole budget is used (with new budget the fresh money from the investor). n

Total value = ∑ NewNumberi  valuei

(7.9)

i=1

Sale + new budget = Buy + Cost Then for different properties like mean interest, mean debtor risk, constraint as follows:

(7.10) :::

we can build a

n

MeanProp = ∑ NewNumberi  valuei  Propi

(7.11)

i=1

With Propi the specific property like interesti or debtor riski . Remark that the variable MeanProp should be divided by Total value to become the actual mean value. This trick is necessary to stick with linear constraints. The user can also specify in his profile the limits on the proportion of the budget that can be invested in some currencies. For each currency we then compute the total amount of money spend on bonds in that currency. In this example we want maximum 30 percent of bonds in British pounds: Budgetgbp =



currencyi =gbp

NewNumberi  valuei

Budgetgbp  0:3  Total value

(7.12) (7.13)

CHAPTER 7. A FEW OTHER APPLICATIONS

166

Number of bonds 100 200 300 500 750 1000 2000 3000

SICStus clp(Q) 1.58 sec 20.98 sec 25.36 sec 187.30 sec 622.79 sec 1280.55 sec 10580.48 sec 27679.38 sec

CHIP 0.59 sec 2.07 sec 2.92 sec 5.96 sec 17.98 sec 27.89 sec 168.52 sec(with much memory) Out of memory

Table 7.1: Results for SICStus clp(Q) and CHIP

7.1.4 Results In the program we experimented with some data. The portfolio was optimised towards an optimal gain, given the restrictions on debtor risk, currencies, : : : given by the investor. In a second step the portfolio was minimised towards arbitration costs. This makes sure that at least one of the variables Boughti and Salei is zero for each bond i. The same program was used with SICStus, ECLi PSe and CHIP. Using SICStus the program was tested with both the library clp(q) and clp(r). Unfortunately the resulting program seems to be quite hard on these systems. Only two experiments were successful: with the clp(q) library from SICStus and CHIP. The following experiments failed:





SICStus with clp(r) With this library the system went crazy immediately: It returned solutions which were not solutions and failed for a consistent set of constraints. The coefficients of the linear constraint are probably too large and unbalanced to get some reasonable behaviour. ECLi PSe using a linear solver base on rationals Again the results of this experiments gave unreliable results, even for small instances of the problem. In the latest version of ECLi PSe this library is still available but is not documented. I assume this library is obsolete, the libraries using CPLEX or XPRESS-MP should be used. Still these solvers are based on floating point computations. It is not sure they can handle the large coefficients from our application without serious rounding errors.

With CHIP and SICStus(using clp(q)), both systems are based on rationals, we ran the program with an increasing amount of bonds available. The SICStus system started to slow down rather quickly, while the CHIP version ran out of memory as can be seen in table 7.1 on a sparcUltra II. The main reason for failure are the large coefficients in the equations. Probably standard linear programming would also fail on this. Still I believe there is a way out for

7.2. PRACTISING MODELLING ON A SCHEDULING PROBLEM

167

solving this problem by tuning the constraints. In such an approach the coefficients of the linear constraints should be normalised such that most of them become within the same small range. Then the resulting set of constraints could be used within a linear solver based on floating point numbers, like CPLEX. Still such an approach is far away from CLP, where the user should be able to give in the constraints, whatever they are, and get reasonable efficiency.

7.2 Practising Modelling on a Scheduling Problem A returning hot topic at the department of computer science at the KULeuven is the assignment of the different master students to the different research groups. There are several aspect in this sensitive subject. Of course each research groups want to have as much “good” students as possible. They can do some useful work, but moreover these student are potential candidates for doing research within that group after their studies are finished. Also the students have preferences on the research group to do their master’s thesis with. Of course some research groups are more popular than others. Taking these preferences from the student into account makes the distribution quite difficult.

7.2.1 Distribute master students over different research groups At some point in writing the application the department had 11 research groups, 53 students had to be distributed over these group. Each research group has a different size. An always returning point of discussion during the distribution of the students was: how far should the number of students be proportional to the size of the research group? Depending on the views of the people on a specific point in time, the result could be quite different. A program doing the distribution should take this aspect into account, being able to propose different distributions. Some with a distribution strictly proportional to the sizes of the research groups. Other proposed distributions should have a very weak connections with the sizes, and of course some variants in between. Each year the students make an individual ranking of the different research groups. They can rank each research group as first choice, second choice, ... . Remark that the student can rank several groups as their n-th choice: if a student has no preference between two research groups, he can be given a first choice to both of them. A significant part of the optimisation function should reflect that as much students as possible should get a choice with a very high ranking. The most difficult aspect of the problem is to distribute the students with the conditions above, such that each group gets an proportional amount of the “good” students.

7.2.2 Designing the input format The input file contains two lists with data. The first list contains atoms group(Name; Lower; U pper), where each atom represents a research group. Name is the name of the group. Lower, and U pper the bounds on the number of students allowed for that group.

CHAPTER 7. A FEW OTHER APPLICATIONS

168

The problem of having different proposals, depending on the number of students allowed within one group can then be solved with different input files, giving different bounds. The second list contains a list of all students to be assigned to the different research groups. Each of the terms student (Name; Ranking; Qual ) contains the name of the student (Name), his ranking for the different research groups (Ranking). This arguments is again a list of terms choice(Nr; Group), where Nr is the preference for Group. A student can have several preferences 1 for different groups! Qual is a number representing the “quality” of the student. This could be a grade from the previous year. Example: [groep(cnds, 1, 2), groep(cg, 1, 2), groep(ai, 1, 2), groep(dt, 1, 2) ]. [student(naam1,[keuze(1,cnds),keuze(1,ai),keuze(2,dt)],70), student(naam2,[keuze(1,cnds),keuze(2,dt)], 54), student(naam3,[keuze(1,cg),keuze(3,dt)], 73), student(naam4,[keuze(1,cg),keuze(2,cnds),keuze(3,ai)],78) ].

7.2.3 Creating a model A model consist of a set of variables, connected with constraints and an optimisation function expressed in these variables. The hardest part in developing a model is choosing the variables. After choosing the variables, the constraints follow from that. Criteria for a good choice of these variables:

  

It should be possible to express the constraints and the optimisation function in these variables. The choice of these variables should be as natural as possible. This avoids errors in expressing the constraints. The choice of the variables should also aim at efficiency. It is needless to say that this often conflicts with the previous criterion

In the case of the problem at hand, a natural choice is to have a finite domain variable Groups for each student s. This variables then take a value over a domain where the values in the domain represent the different research groups. However this conflicts with the first condition: one should be able to express constraints and optimisation function. Given only variables representing which group each student get, it is impossible to express, in existing Finite Domain solvers, an optimisation function that distribution should be optimal regarding the preferences of the students. This leads to a model where for each student s an additional variable Choices is created, expressing the ranking of the research

7.2. PRACTISING MODELLING ON A SCHEDULING PROBLEM

169

group assigned, given the personal ranking of the student. A trivial optimisation function then could be a simple sum of all these variables. This set of variables is sufficient for expressing constraints for this problem in the ROPE system [VD94], using the cardinality constraint [VD91] for expressing the bounds on the number of students for each group. An extra set of boolean variables is needed to program the latter constraint when using the clp(FD) [COC97] library coming with SICStus [Swe97]. For each student s and each group g we then get a Boolean variable Bs;g representing whether student s is assigned to the group g. Also in the SICStus variant one needs a variable representing the number of students assigned to a group g: Numberg . This leads to the following set of constraints formalising the complete problem:



Initialising maxGroup is the total number of research groups, maxChoice the lowest ranking a student can get. This constant could for example be set to 3 (or 2), to restrict the worst choice being a third (second) choice.

8student : Groupstudent in 1 maxGroup 8student : Choicestudent in 1 maxChoice 8group : Groupgroup in lowergroup uppergroup ::

::

::

(7.14)

lowergroup and uppergroup are the bounds on the number of students assigned to a research group given in the data.



Propagation The variables Groupstudent and Choicestudent should be connected such that information on one variable is propagated from one to another.

8student choice 1  choice  maxChoice : Choicestudent in choice # , Groupstudent in Set ;

(7.15)

where Set is the set of numbers corresponding to groups having choice as ranking for the involved student.

8student group : Bstudent group # , Groupstudent = group ;

;

(7.16)

Constraint (7.16) expresses that Bstudent ;group is equivalent to the truth value of the constraint Groupstudent = group. Additionally these Boolean Bstudent ;group have to be coupled to the variable Numbergroup:

8group : Numbergroup = ∑

Bstudent ;group

(7.17)

student

When during enumeration variables get assigned, the constraints above will propagate choices to other variables connected by constraints. When the domain of Groupstudent becomes a subset of Set this will be propagated by constraint (7.15)

CHAPTER 7. A FEW OTHER APPLICATIONS

170

to the variable Choicestudent by assigning it to the appropriate value choice. On the other hand when the domain of Groupstudent becomes disjunct from the Set the value choice will be removed from the domain of Choicestudent . Vice versa information from variable Choicestudent can be propagated to Groupstudent . When Choicestudent is instantiated to choice, the domain of Groupstudent is restricted to Set. When choice is removed from the domain of Choicestudent , all values from Set are removed from the domain of Groupstudent . Propagation with constraint (7.16) is similar. Constraint (7.17) will make sure the restriction on the number of students for each research group is satisfied. As soon as enough boolean variable are set to one, all other boolean variables will be set to zero. This information can again be used by constraint (7.16) to reduce the domains of some Groupstudent variables. This will then propagate further to constraint (7.15). Also when enough boolean variable in constraint (7.17) have been restricted to zero, all other variables in the constraint will be set to one. This will again propagate to constraint (7.16) and (7.15).



Enumeration Using heuristics during enumeration is essential for this application. The heuristic performing best is first assigning the student which is bound to get a poor choice. In practice this means that the variable with maximal value for the least number still available in its domain is selected.

Building the optimisation function such that it would achieve a fair assignment regarding the choices of the students and meanwhile distribute the “better” students over the different research groups was impossible. Instead a two phase optimisation process was used. In the first phase an optimal assignment was searched regarding the choices of the students with the function: S=



Choicestudent

(7.18)

student

In the second phase the previous optimisation function (7.18) is used as a constraint, where the Finite Domain variable S was limited to the optimal value, and a few near optimal values. Then all solutions satisfying that constraint are computed, selecting the solution with the best distribution of “quality” students over the different research groups. Optimisation and ROPE version In the ROPE version as already mentioned some of the variables in the model could be avoided, more in particular the variables Bs;g and Numberg . This because constraint (7.17) can be expressed as: card (lowergroup; uppergroup; [Groupstudent in group::group; : : : ℄)

(7.19)

This cardinality constraint imposes that minimum lowergroup and maximum uppergroup of the constraints in the third argument should be true. An optimisation in constraint (7.16)

7.2. PRACTISING MODELLING ON A SCHEDULING PROBLEM

171

and (7.19) is to replace Groupstudent in group::group

(7.20)

Choicestudent in choice::choice

(7.21)

by

where choice is the ranking of student student for group group. This is only possible when the student has only one ranking with the value choice. This is an improvement because the optimisation function acts on the variables Choicestudent . The replacement above then corresponds to a better propagation. Remark that when it is not allowed to have the same ranking for different research groups within the ranking of an individual student, the variables Groupstudent becomes superfluous. Then the model can be simplified to the variables Choicestudent . The resulting program becomes very efficient, but the model becomes unnatural. And anyway, it is not a good idea to forbid the students to have only one first choice.

7.2.4 Results The resulting program was able to find and prove optimal solutions for previous years, which were better than the solutions used. The program has already been used a few times for computing new assignments. The program was quite useful. Still it turns out that some hand work is needed depending on the data. The main functionality was to generate initial correct solutions (also optimal with respect to the optimisation function). Then these solution were manually adapted to find better solutions given special, difficult to formalise, conditions. As an example the results of the last assignment are included. The tables were kindly provided by Frank Piessens. The number of students that filled in a form was 69, as can be read from Table 7.2. CW LI AOI Totaal

aantal studenten 23 42 51 116

aantal keuzeformulieren 21 42 6 69

Table 7.2: The number of students In Table 7.3 we can see which research groups are popular and which not. Since students are allowed to have several first choices there are more first choices than second choices, and more second choices than third choices. Here we already notice that the Distrinet group is very popular, at least compared to DTAI:DT, a research group about the same size at that time, as can be read from Table 7.4 showing the sizes of some of the groups.

CHAPTER 7. A FEW OTHER APPLICATIONS

172

Groep Distrinet DTAI: AI DTAI: DT SOM Hypermedia Graphics Techwet NINES NALAG Mechatronica Totaal

1e keuze 20 6 3 11 11 9 3 0 3 3 69

2e keuze 16 15 6 8 8 9 1 2 2 1 68

3e keuze 15 9 5 9 9 10 0 3 3 0 63

Table 7.3: Which research groups are popular Distrinet 16

DTAI:AI 9

DTAI:DT 18

SOM 7

Hypermedia 10

Graphics 4

Table 7.4: Sizes of research groups from the section informatica The department is structured in two sections, one called Toegepaste Wetenschappen en Numerieke Analyse(TWNA) and the other Informatica. Since all students selecting a group from TWNA have no other choices outside TWNA, and all other students have all choices in the section informatica the problem was decomposed between the two sections. A assignment within TWNA was easily made. Figure 7.5 shows how the students were assigned. Students were grouped by quality, where GO is the highest quality class, O a bit lower, V the main group and V- the lowest quality. Niveau student TechWet NINES NALAG Totaal

GO 2 0 1 3

O 0 0 0 0

V 1 0 1 2

V0 0 0 0

Totaal 3 0 2 5

Table 7.5: A proposal for section TWNA For the section Informatica several proposals were made. In Table 7.6 all students get their first choice. This is probably unacceptable since such a distribution in no way respects the sizes from the groups in table 7.4. A second proposal the number of students assigned to small groups was restricted,

7.3. CONTRIBUTION

Niveau student Distrinet DTAI:AI DTAI:DT SOM Hypermedia Graphics Totaal

173

GO 2 1 0 1 0 0 4

O 5 1 0 4 1 0 11

V 8 4 2 4 7 5 30

V4 0 1 2 3 4 14

Totaal 19 6 3 11 11 9 59

Table 7.6: In this proposal all students get their first choice, but it is unbalanced Niveau student Distrinet DTAI:AI DTAI:DT SOM Hypermedia Graphics Totaal

GO 2 1 0 1 0 0 4

O 5 1 0 4 1 0 11

V 8 5 3 4 7 3 30

V4 2 1 1 3 3 14

Totaal 19 9 4 10 11 6 59

Table 7.7: A proposal where the number of students for small groups is restricted resulting in a few second choices, but a more acceptable schedule, as can be seen in table 7.7. Also a third solution was provided with a more honest distribution concerning the distribution of quality students over the different groups in Table 7.8, but with a lot of students (seven) getting their second choice, especially good students. If possible this should be avoided.

7.3 Contribution In this chapter two problems were tackled using Constraint Logic Programming. The first problem, optimising a set of securities, was handled with CLP based on Linear programming. However, the experiments were not very successful. Still it is of interest why the experiments failed. It turns out that the systems used for this applications do not have a reliable implementation of the simplex algorithm. When using floating point numbers, systems bail out as a result of computing errors. Using rational number is more reliable, but for many equations running time or memory use becomes a problem. The second application was very successful, it was used several times in practice. The model used and its optimisations are certainly a contribution to the methodology on Finite Domain CLP.

CHAPTER 7. A FEW OTHER APPLICATIONS

174

Niveau student Distrinet DTAI:AI DTAI:DT SOM Hypermedia Graphics Totaal

GO 1 1 1 1 0 0 4

O 5 2 0 3 1 0 11

V 8 5 3 3 8 3 30

V4 1 2 2 2 3 14

Totaal 18 9 6 9 11 6 59

Table 7.8: A fair distribution for quality students

Chapter 8

Conclusion This thesis is a reflection of our experiences with Constraint Logic Programming. The first chapter describes an alternative view on Constraint Logic Programming which is the result of several years of practice and experience with solving CLP problems. The main accent is on Finite Domain Constraint Logic Programming, but it also includes a short description of other branches in Constraint Logic Programming, namely CLP based on Linear Programming and Interval reasoning. The second chapter introduces the Finite Domain CLP language ROPE. The first novelty in the design of the language is a more general mechanism for expressing specific propagation for constraints with the prune primitive [VD94]. Such a prune primitive generalises the concept of forward checking, partial lookahead and lookahead. The second novelty is the parameterised enumeration algorithm [Van94], treating different backtrack behaviours in a general way. The remainder of this second chapter reports on experiments on implementing Finite Domain solvers, both in Prolog and Mercury. The second section of the chapter reports on an implementation of a Finite Domain solver in Prolog [VD94]. The software product produced in this experiment contributed significantly to understanding how Finite Domain solvers work in general. It also allowed some experiments with the prune primitive, a general version of forward checking, partial lookahead and lookahead. The second part of this chapter brings the reader to the world of Mercury. At the time of that experiment, performed in cooperation with a master student and published in [VDV96, VDV99], an attempt was made to implement a Finite Domain solver in an early version of Mercury. The main conclusion from this work was that, since Mercury lacks open ended data structures, Mercury desperately needed backtrackable destructive assignment. Since then some support has been added to Mercury for such backtrackable destructive assignment. Given the previous experiences and the added support, a final Finite Domain solver was constructed in Mercury. This solver is competitive with the solver [COC97] included with SICStus. The subsequent chapters in the thesis handle a number of applications developed using Constraint Logic Programming. The first two application have an academic nature, the remainder of the applications are real-life.

175

176

CHAPTER 8. CONCLUSION

The first application involves providing a proof that a query for a definite logic program can never succeed, without executing the program [BVdD98]. For this application, in parallel with an abductive approach by Bruynooghe also described in [BVdD98], the author developed a CLP approach for this problem. The advantage of the CLP system is the possibility to delay choices, encoding restrictions as constraints to be used later to construct a proof. Several versions were constructed. A first version used Finite Domain Constraint Logic programming. This version was more robust than the abductive approach developed by Bruynooghe, in the sense that small variations on the benchmarks did not change execution behaviour much. Still the approach was inferior to the abductive approach as on average much more choicepoints were needed. Also memory consumption in this CLP approach was out of proportion. Other alternatives were developed, replacing the Finite Domain Solver with a specific solver using far less memory and less propagation. Augmenting this solver with Intelligent Backtracking [Bru78], which was also used in the abductive approach, yielded a better system than the Finite Domain approach. Although this last system can stand the comparison in timings with the abductive approach from Bruynooghe, still it uses far more choicepoints. Since the abductive approach uses simple unoptimised data structures, a rewrite of the latter system will outperform the CLP approach. Nonetheless the CLP approach has some features which, if being integrated in the abductive approach, could improve the latter. Chapter five reports on using Constraint Logic Programming as an underlying technique for termination analysis [DDV99]. Such termination analysis is based on deducing constraints that, when solvable, ensure the existence of a proof of termination. The essential component in the method is proposing symbolic formulas for norms, level mapping and interargument relations. Such symbolic formulas contain a number of parameters, which are to be chosen at a later point. In classic methods specific versions of the norms, level mapping and interargument relations are deduced in several stages of computation. Introducing symbolic formulas allows to delay the choices on such specific versions. Instead of checking whether specific formulas for norms, level mappings and interarguments relations make up a proof for terminations, restrictions are generated on the parameters of the symbolic formulas. If these restrictions are solvable we have found a proof. Plugging the solutions for the parameters into the symbolic formulas yields the specific norms, level mappings and interargument relations. These restrictions on the symbolic formulas deduced from the problem have a very special nature and cannot be solved “as is” with any solver. The contribution of the author consist in automating the construction of Finite Domain constraints from these restrictions and actually solving them. The first real-life application concerns scheduling maintenances for electricity units. The project started with a master’s thesis [SB96] developing a CLP program for the application, and a more high-level description in OLP-FOL, executed under SLDNFA [DD92]. In this initial stage the contribution from the author concerns the conception of the first model, which also served as a base for constraints automatically derived by the SLDNFAsystem [DVS+ 97]. Later the project was extended by trying alternative models combining Linear Programming with Finite Domain solving [Van98]. The basic idea of these alternative models was to explore the linear nature of the constraints involved in the application.

177

A first attempt exploiting this aspect failed largely. A fresh start with a completely different model gave better results. Chapter seven tackles two other applications. The first one, optimising a set of securities, was not very successful. In contrast, the second topic, distributing master students over different research groups, was very successful and has been used in practice several times. The contribution of these two application lies in the following: (1) why the first application could not be solved and (2) how the second problem was modelled. Solving the first application showed that the linear programming algorithms integrated in current CLP systems are not of very high quality. The unbalanced coefficients of the linear constraints result in incorrect results when solving over the reals and memory or runtime problems when solving over the rationals.

178

CHAPTER 8. CONCLUSION

Chapter 9

Future Work 9.1 Combining a Finite Domain Solver with Techniques from Linear Programming Both Finite Domain constraint solvers and mathematical software based on Linear Programming are powerful systems for solving search problems. Since both paradigms take a very different approach, depending on the problem one of the methods could fail while the other technology succeeds and vice versa. When dealing with optimisation problems where an optimum is really needed a mathematical viewpoint is often preferred. On the other hand for scheduling problems, where the optimum is hard to find because of a difficult set of combinatorial constraints to be satisfied, a Finite Domain solver will take the lead. In practice, when to use finite domain solving, and when to use linear programming is not so clear. Many problems have an optimisation part, a considerable amount of linear constraints and a set of combinatorial constraints making the problem hard to solve. For these kind of problems an integrated solver using techniques from both linear programming and finite domain solving could be a solution. The CLP system ECLi PSe already has a loose integration between their Finite Domain solver and the linear programming package CPLEX, showing the combination is fruitful. In this coupling, all linear constraints from the problem are also passed to the linear solver. The linear solver checks consistency of these constraints over the reals and gives hints during enumeration. Of course a more integrated system could do more. It is known that Finite Domain solvers perform very bad on a set of linear equations where a lot of variables are involved. Example 9.1 shows a set of linear constraints difficult to solve with Finite Domain solving. When simplifying such a set of linear equation to the set of equations in Example 9.2 with Gaussian elimination, this resulting set of constraints shows a different behaviour when treated by a Finite Domain solver. When solving both problems with a Finite Domain solver, the difference is impressive. The first set of equations needs about 3000 constraint checks before the problem is solved. After

179

CHAPTER 9. FUTURE WORK

180

2  X1 + X2

X3

X4 = 7

X1 + X2 + X3 X4 = 13 X1 + X2 + X3 + X4 = 39 Figure 9.1: CLP(FD) needs about 3000 constraint checks for this problem.

3X1 2X4 = 20 X2 + X4 = 13

(9.1) (9.2)

3X3 + 2X4 = 58

(9.3)

Figure 9.2: The simplified set of constraints is solved with 64 constraint checks. simplification only 64 checks are needed! Another example is where a simplex algorithm computes lower and upper bounds for Finite Domain variables reducing the domains. When feeding the constraints in Figure 9.3 to a Finite Domain solver, as done in an exper-

X 5X

2 Y 3 Y

5  X + 3 Y 3  X + 4 Y

10 50

30  0 12  0

Figure 9.3: Example for computing bounds with simplex iment with SICStus, the domains of X and Y are reduced to the domains 1..6 for X and 0..8 for Y. Using these constraints as input for the simplex algorithm we can find that the domains of X and Y can be reduced to 2..4 for X and 1..4 for Y. A serious reduction of the search space! A cooperation between the solvers should then consist of:

  

Both solvers check for consistency of the set of constraint. Finite Domain system with local propagation, linear programming with Gaussian elimination and simplex. The linear solver could simplify the set of equalities and return them to the Finite Domain solver. The Finite Domain solver could either use them as redundant constraints with better propagation or as a replacement for the current equalities. Although costly the Linear system could derive new lower and upper bounds for the variables in the system.

9.2. A GENERAL ROBUST CLP SOLVER FOR LP+

 

181

When all constraints of the problem are linearised the Linear Programming engine could compute an upper bound for the optimisation function. Also the Finite Domain solver could propagate newly derived bounds of the variables to the simplex algorithm.

As can already be seen in the items above: some operations could be quite costly. For this reason the cooperation between the solvers should be parameterised. This should make it possible to switch on and off certain aspects of the cooperation. The MROPE II system running on top of Mercury is already designed towards an integration with Linear Programming.

9.2 A General Robust CLP solver for LP+ Currently at the department new work is going on designing a declarative specification language which relies on abduction and, as a consequence, requires alternative execution schemes far more complicated than the depth first, left-to-right execution from Prolog. One of the underlying mechanisms would be a (or several) CLP solver(s). Unfortunately CLP is not ready for this. Currently, depending on the problem, one first chooses a CLP paradigm: finite domain solving, linear solving, boolean solving, interval arithmetic, : : : . Then depending on the chosen paradigm one writes a program taking into account the advantages and drawbacks of the chosen paradigm. A few examples of such drawbacks.



Most of the CLP solvers are incomplete. Inconsistent sets of constraints like in Example 9.1 will not be detected by several paradigms: Example 9.1 X 6= Y , X = Y As long as the domains of X and Y contain more than 1 element, A finite domain solver will not find inconsistency because of the local nature of the propagation scheme. Linear programming systems cannot handle inequality and will delay the inequality until X and Y become ground. Of course the incompleteness cannot be solved, but a case as above should be detected.

 

In general CLP systems make no effort to reduce the number of variables involved in the constraints. A general execution scheme could easily introduce unnecessary variables which will slow down the execution scheme. As mentioned in Section 2.1.4 expressing C1 _ C2 , denoting the disjunction of two constraints C1 and C2 , is not possible without “encoding” the disjunction in Finite Domain solvers.

182

CHAPTER 9. FUTURE WORK

From these observation we can conclude that such CLP system are very efficient at what they do, but there is a lot they do not do. In the context of using CLP as an underlying solver one should think of a more general robust solver integrating several algorithms. This general solver should be highly parameterised such that the user at a very high level can choose the behaviour of the system. It should also allow for experimenting with different options, of course without changing the specification. The decision about which algorithms should be included, and what integration is needed will have to be guided by the needs of relevant examples.

Appendix A

Data sets and examples A.1

Data for Scheduling Power Plants

The company involved gave us some test data to experiment with: % plants/1: a list of all plants in the country. % Each plant has an entry plant(Name, Units, Max) % Name: the name of the plant. % Units: the number of units within the plant % Max: the limit on the number of maintenances maintained simultaneous. plants([ plant(aa, 5, 1), plant(bb, 4, 2), plant(cc, 6, 1), plant(dd, 6, 3), plant(ee, 3, 1), plant(ff, 3, 3), plant(gg, 12, 4),plant(hh, 3, 3), plant(ii, 4, 4)]). % unit(Name,Nr,Megawatt,Fixed,Durations,Prohibited,NonSim,Sim,Area) % Name: Name of the plant % Nr: unique number for the unit within the plant % Megawatt: The energy production of the unit % Fixed: Fixed maintenances for the plant % Durations: Durations of the maintenances to be scheduled % Prohibited: Periods prohibited for maintenance. % NonSim: references to maintenances from other units te be maintained % non simultaneous. % Sim: references to maintenances from other units te be maintained % simultaneous. % Area: Description from the area unit(aa, 1,910,[], [3], [p(1,2),p(44,52)],[], [], 1). unit(aa, 2,920,[f(4,5),f(22,25)],[],[], [], [], 1). unit(aa, 3,900,[], [5,1],[p(1,5),p(40,52)],[], [], 1). unit(aa, 4,910,[], [3], [p(1,2),p(51,52)],[n(hh,1,1,1)],[],1). unit(aa, 5,900,[], [5], [p(1,2),p(51,52)],[], [s(cc,4)], 1). unit(bb, 1,440,[], unit(bb, 2,330,[], unit(bb, 3,420,[f(25,26)],

[5], [p(51,51)], [4], [p(51,51)], [4,3],[p(51,51)],

183

[], [], 1). [n(gg,10,0,0)],[],1). [], [], 1).

APPENDIX A. DATA SETS AND EXAMPLES

184

unit(bb, 4,410,[],

[4],

[p(51,51)],

[], [], 1).

unit(cc, 1,200,[], unit(cc, 2,200,[], unit(cc, 3,200,[],

[3], [3], [3],

[p(51,51)], [p(51,51)], [p(51,51)],

unit(cc, 4,410,[], unit(cc, 5,350,[], unit(cc, 6,450,[],

[3], [4], [2],

[p(51,51)], [p(51,51)], [p(51,51)],

[], [], 1). [], [], 1). [n(gg, 6,2,2), n(gg,12,2,2)],[],1). [], [], 1). [], [], 1). [], [], 1).

unit(dd, unit(dd, unit(dd, unit(dd, unit(dd, unit(dd,

[4], [6], [5], [3], [6], [2],

[p(51,51)], [p(51,51)], [p(51,51)], [p(51,51)], [p(51,51)], [p(51,51)],

[], [], [], [], [], [],

unit(ee, 1,290,[], unit(ee, 2,280,[], unit(ee, 3,270,[],

[2], [2], [2],

[], [], [],

[], [], 1). [], [], 1). [], [s(ff,1)], 1).

unit(ff, 1,150,[], unit(ff, 2,150,[], unit(ff, 3,100,[],

[2], [1], [3],

[p(51,51)], [p(51,51)], [p(51,51)],

[], [s(ee,3)], 1). [], [], 1). [], [s(gg,1)], 1).

unit(gg, unit(gg, unit(gg, unit(gg, unit(gg, unit(gg,

[3], [4], [], [2], [3], [2],

[p(51,51)], [p(51,51)], [], [p(51,51)], [p(51,51)], [p(51,51)],

60,[], 50,[], 40,[], 30,[], 20,[], 10,[],

[3], [2], [1], [3], [3], [2],

[p(51,51)], [p(51,51)], [p(51,51)], [p(51,51)], [p(51,51)], [p(51,51)],

[], [s(ff,3)], 1). [], [], 1). [], [], 1). [], [], 1). [], [], 1). [n(cc, 3,2,2), n(gg,12,2,2)],[],1). [], [], 1). [], [], 1). [], [], 1). [n(bb, 2,0,0)],[],1). [], [], 1). [n(cc, 3,2,2), n(gg, 6,2,2)],[],1).

unit(hh, 1, 30,[], unit(hh, 2, 40,[], unit(hh, 3, 40,[],

[2], [3], [2],

[p(51,51)], [p(51,51)], [p(51,51)],

[n(aa, 4,1,1)],[],2). [], [], 2). [], [], 2).

unit(ii, unit(ii, unit(ii, unit(ii,

[], [2], [2], [3],

[], [p(51,51)], [p(51,51)], [p(51,51)],

[], [], [], [],

1,250,[f(9,12)], 2,250,[f(12,15)], 3,150,[f(17,19)], 4,320,[f(23,24)], 5,310,[f(27,31)], 6,350,[f(29,31)],

1,150,[], 2,150,[], 3,150,[f(13,15)], 4,100,[], 5,110,[], 6,120,[],

unit(gg, 7, unit(gg, 8, unit(gg, 9, unit(gg,10, unit(gg,11, unit(gg,12,

1, 2, 3, 4,

20,[f(7,8)], 20,[], 20,[], 20,[],

% areas/1: Areas within the country areas([a(1, 2000), a(2, 110)]). % peak(Week, Megawatt) % For each week the expected peak consumption.

[], [], [], [], [], [],

[], [], [], [],

1). 1). 1). 1). 1). 1).

1). 1). 1). 1).

A.2. EXAMPLES FOR PROVING FAILURE

peak(1, 9480). peak(5, 8980). peak(9, 9280). peak(13, 9050). peak(17, 8440). peak(21, 8650). peak(25, 7980). peak(29, 7100). peak(33, 8330). peak(37, 8750). peak(41, 9290). peak(45, 9590). peak(49, 9550).

A.2

peak(2, 9460). peak(6, 9620). peak(10, 9280). peak(14, 9050). peak(18, 8440). peak(22, 8590). peak(26, 7990). peak(30, 7100). peak(34, 8330). peak(38, 9000). peak(42, 8670). peak(46, 9540). peak(50, 10000).

peak(3, 9460). peak(7, 9250). peak(11, 9450). peak(15, 9050). peak(19, 8500). peak(23, 8270). peak(27, 7910). peak(31, 7550). peak(35, 8630). peak(39, 8860). peak(43, 8990). peak(47, 9540). peak(51, 9000).

185

peak(4, 9480). peak(8, 9530). peak(12, 9200). peak(16, 8590). peak(20, 8650). peak(24, 8270). peak(28, 7910). peak(32, 7900). peak(36, 8630). peak(40, 9290). peak(44, 9440). peak(48, 9550). peak(52, 8000).

Examples for proving failure

The even/odd program. Here and further on, the considered queries are the bodies of 0-arity predicates. The query odd even has no answer. odd_even:- even(X), even(s(X)). even(zero). even(s(X)):- odd(X). odd(s(X)):- even(X). A version of even odd with an extra superfluous argument which creates a term with 4 different functors: wicked_oe:- weird_even(X, _U1), weird_even(s(X), _U2). weird_even(zero,U):- weird_p(U). weird_even(s(X),U):- weird_odd(X,_V), weird_p(U). weird_odd(s(X),U):- weird_even(X,_V), weird_p(U). weird_p(f(g(h(a)))). The appendlast-query first appends the list [a] to a undefined list becoming the list Xs. The subsequent call to last can never succeed as b can never be the last element of the list Xs. appendlast:- app(X, [a], Xs), last(Xs, b). app([],L,L). app([H|X],Y,[H|Z]):- app(X,Y,Z). last([X],X). last([H,H2|T],X):- last([H2|T],X).

186

APPENDIX A. DATA SETS AND EXAMPLES

The query reverselast is similar to the query appendlast but uses reverse with accumulator to put [a] at the end of the list. Predicates defined in examples above are not repeated, e.g last. reverselast:- reva(L, R, [a]), last(R, b). reva([], Acc, Acc). reva([Y|Z], R, Acc):reva(Z, R, [Y|Acc]). The query nreverselast uses naive reverse to put the element a at the end of the list. nreverselast:- rev([a|X], R), last(R, b). rev([], []). rev([X|Y], R):rev(Y, S), app(S, [X], R). The schedule problem is an example with a more complicate reason for failure. schedule:- cFirst(R), mv(R). mv(R):- tr(R,NewR), mv(NewR). mv(R):- atleast2c(R). % success iff R is non-safe state tr([c,n|Rs], [n,c|Rs]). tr([n|Rs], [n|NewRs]):- tr(Rs,NewRs). tr([],[]). cFirst([c|Qs]):- nOnly(Qs). nOnly([n|Qs]):- nOnly(Qs). nOnly([n]). atleast2c([c|L]):- atleast1c(L). atleast2c([n|L]):- atleast2c(L). atleast1c([c|_]). atleast1c([n|L]):- atleast1c(L). sameMultiSet is a predicate to check that two multisets contain the same elements. The multiset is represented with a functor o/2 and a constant emptyMultiSet. Without tabulation, it also has an infinite search tree for ground queries. multiseto:- sameMultiSet(o(a,o(a,emptyMultiSet)), o(_X,o(emptyMultiSet,b))). sameMultiSet(X, X).

A.2. EXAMPLES FOR PROVING FAILURE

187

sameMultiSet(o(X, Y), o(X, Z)):sameMultiSet(Y, Z). sameMultiSet(o(o(X, Y), Z), U):sameMultiSet(o(X, o(Y, Z)), U). sameMultiSet(U, o(o(X, Y), Z)):sameMultiSet(U, o(X, o(Y, Z))). sameMultiSet(o(emptyMultiSet, X), Y):sameMultiSet(X, Y). sameMultiSet(X, o(emptyMultiSet, Y)):sameMultiSet(X, Y). sameMultiSet(o(X, Y), Z):sameMultiSet(o(Y, X), Z). The same problem can be writing using lists. The query does not terminate due to the presence of the variable. multisetl:-

sml([a], X), sml(X, [b]).

sml([], []). sml([X|Y], D):- delete(X, D, E), sml(Y, E). delete(M,[M|T], T). delete(M, [H|T], [H|L]):- delete(M, T, L). A simple planner in the blocks world due to Michael Thielsher. It has to be executed under iterative deepening to find plans for most problems (which have a solution). We have two different action theories from the blocks world. Blocks are identified by integers. The action theory, actionPair, has, besides the usual actions of the blocks world, an action to add or remove a pair of blocks. We have variants with two arguments (which do not collect the plan) and with three argument (the plan is stored in the second argument). And we have versions using multisets based on the functor o/2 and based on lists. For the latter, the names end on l e.g. causesPairl/2 is the 2 argument version using lists and the actionPair theory. For the actionPair theory, the unsolvable query has an initial state with an even number of blocks and a final theory with an odd number of blocks. The query blocksol has a solution (can be found using iterative deepening). blockpair2o:causesPair(o(on(s(nul),nul), o(ta(nul), o(cl(s(nul)),em))), o(on(s(s(nul)),s(nul)), o(on(s(nul),nul), o(ta(nul), o(cl(s(s(nul))),em))))). blockpair3o:causesPair(o(on(s(nul),nul), o(ta(nul), o(cl(s(nul)),em))), _Plan, o(on(s(s(nul)),s(nul)), o(on(s(nul),nul), o(ta(nul), o(cl(s(s(nul))),em))))). blockpair2l:causesPairl([on(s(nul),nul),ta(nul),cl(s(nul)),em], Sequence), m_subset([on(s(s(nul)),s(nul)),on(s(nul),nul), ta(nul),cl(s(s(nul))),em],

188

APPENDIX A. DATA SETS AND EXAMPLES

Sequence, []). blockpair3l:causesPairl([on(s(nul),nul),ta(nul),cl(s(nul)),em],Plan,Sequence), m_subset([on(s(s(nul)),s(nul)),on(s(nul),nul),ta(nul), cl(s(s(nul))),em], Sequence, []). blocksol:- causesZerol([on(s(nul),nul),ta(nul),cl(s(nul)),em], [on(s(s(nul)),s(nul)),on(s(nul),nul),ta(nul),cl(s(s(nul))),em]). causesPair(I1, I2):sameMultiSet(I1, I2). causesPair(I, G):actionPair(C, E), sameMultiSet(o(C, Z), I), causesPair(o(E, Z), G). actionPair(ho(V), o(ta(V),o(cl(V),em))). actionPair(o(cl(V),o(ta(V),em)), ho(V)). actionPair(o(ho(V),cl(W)), o(on(V,W),o(cl(V),em))). actionPair(o(cl(V),o(on(V,W),em)), o(ho(V),cl(W))). actionPair(o(on(V,W),o(cl(V),em)), o(on(s(s(V)),s(V)), o(on(s(V),V), o(on(V,W), o(cl(s(s(V))),em))))). actionPair(o(on(s(s(V)),s(V)), o(on(s(V),V), o(on(V,W), o(cl(s(s(V))),em)))), o(on(V,W), o(cl(V),em))). causesPair(I1, void, I2):sameMultiSet(I1, I2). causesPair(I, plan(A, P), G):actionPair(C, A, E), sameMultiSet(o(C, Z), I), causesPair(o(E, Z), P, G). actionPair(ho(V),put_down(V),o(ta(V),o(cl(V),em))). actionPair(o(cl(V),o(ta(V),em)),pick_up(V),ho(V)). actionPair(o(ho(V),cl(W)),stack(V,W),o(on(V,W),o(cl(V),em))). actionPair(o(cl(V),o(on(V,W),em)),unstack(V),o(ho(V),cl(W))). actionPair(o(on(V,W),o(cl(V),em)),add_two, o(on(s(s(V)),s(V)), o(on(s(V),V), o(on(V,W), o(cl(s(s(V))),em))))). actionPair(o(on(s(s(V)),s(V)), o(on(s(V),V), o(on(V,W), o(cl(s(s(V))),em)))), delete_two, o(on(V,W), o(cl(V),em))).

causesPairl(I,void,I). causesPairl(I,plan(A,P),G):actionPairl(C,A,E), m_subset(C,I,Z), app(E,Z,S), causesPairl(S,P,G).

A.2. EXAMPLES FOR PROVING FAILURE

189

actionPairl([ho(V)],put_down(V),[ta(V),cl(V),em]). actionPairl([cl(V),ta(V),em],pick_up(V),[ho(V)]). actionPairl([ho(V),cl(W)],stack(V,W),[on(V,W),cl(V),em]). actionPairl([cl(V),on(V,W),em],unstack(V),[ho(V),cl(W)]). actionPairl([on(V,W),cl(V),em],add_two, [on(s(s(V)),s(V)),on(s(V),V),on(V,W),cl(s(s(V))),em]). actionPairl([on(s(s(V)),s(V)),on(s(V),V),on(V,W),cl(s(s(V))),em], delete_two, [on(V,W),cl(V),em]).

m_subset([], L, L). m_subset([H|T], L1, L2):delete(H, L1, L3), m_subset(T, L3, L2).

causesPairl(I,I). causesPairl(I,G):actionPairl(C,E), m_subset(C,I,Z), app(E,Z,S), causesPairl(S,G). actionPairl([ho(V)], [ta(V),cl(V),em]). actionPairl([cl(V),ta(V),em], [ho(V)]). actionPairl([ho(V),cl(W)], [on(V,W),cl(V),em]). actionPairl([cl(V),on(V,W),em], [ho(V),cl(W)]). actionPairl([on(V,W),cl(V),em], [on(s(s(V)),s(V)),on(s(V),V), on(V,W),cl(s(s(V))),em]). actionPairl([on(s(s(V)),s(V)),on(s(V),V), on(V,W),cl(s(s(V))),em], [on(V,W),cl(V),em]). causesZerol(I, I). causesZerol(I, G):actionZerol(C, E), m_subset(C, I, Z), app(E, Z, S), causesZerol(S, G). actionZerol([ho(V)], [ta(V), cl(V), em]). actionZerol([cl(V), ta(V), em], [ho(V)]). actionZerol([ho(V), cl(W)], [on(V,W), cl(V), em]). actionZerol([cl(V), on(V, W), em], [ho(V), cl(W)]). actionZerol([on(X, Y), cl(X), em], [on(s(X), X), on(X, Y), cl(s(X)), em]).

Remains the tbool example. It is an axiomatisation of a ternary boolean algebra, a typical problem from the theorem proving community (problem BOO019-1 from the TPTP library [SS97]).

APPENDIX A. DATA SETS AND EXAMPLES

190

equal(X,X). equal(Y,X):- equal(X,Y). equal(X,Z):- equal(X,Y),equal(Y,Z). equal(inverse(A),inverse(B)):- equal(A,B). equal(multiply(C,E,F),multiply(D,E,F)):- equal(C,D). equal(multiply(I,G,J),multiply(I,H,J)):- equal(G,H). equal(multiply(M,N,K),multiply(M,N,L)):- equal(K,L). equal(multiply(multiply(V,W,X),Y,multiply(V,W,Z)), multiply(V,W,multiply(X,Y,Z))). equal(multiply(X,X,Y),X). equal(multiply(inverse(Y),Y,X),X). equal(multiply(X,Y,inverse(Y)),X). tbool:- equal(multiply(y,x,x),x).

A.3

An example of input for FINDER

FINDER input for the multisetl problem. The ternary predicate delete/3 has been converted in a binary delete was3/2 predicate. Besides the sort term for all terms of the original program, a sort pair has been introduced. The extra functor delete argpair is used to bundle two terms of sort term into one of sort pair. sort { term cardinality = 2. pair cardinality = 4. } function { cons: term, term -> term. delete_argpair: term, term -> pair. delete_was3: term, pair -> bool. sml: term, term -> bool. } constant { a: term. nil: term. b: term. ml1: bool. } clause { multisetl -> false. sml(cons(a, nil), z), sml(z, cons(b, nil)) -> multisetl. sml(nil, nil). delete_was3(y, delete_argpair(w, z)), sml(x, z) -> sml(cons(y, x), w). delete_was3(z, delete_argpair(cons(z, y), y)). delete_was3(w, delete_argpair(x, z)) ->

A.3. AN EXAMPLE OF INPUT FOR FINDER

delete_was3(w,delete_argpair(cons(y,x),cons(y,z))). } setting { backtracks: 100000000 solutions:1 verbosity { models: brief stats: full job: brief } } end

191

192

APPENDIX A. DATA SETS AND EXAMPLES

Appendix B

Scheduling maintenances with CHIP /* entry point for the program */ maint(Level, Exceptions):constraints(Data, Max, TotalNeeded, MaxCap,Sums,Level,Exceptions), enumerationt(Data, Max, TotalNeeded, Sums).

/* An example query */ start:-maint(2000, [e(4, 1600), e(50, 2000)]).

constraints(Data, Max, TotalNeeded, MaxCap, Sums, Level, Exceptions):plants(Plants), createData(Plants, Data, DataPlant), nonsim(Data, Data), sim(Data), maxPlant(Plants, DataPlant), maxCapacity(Data, MaxCap), Max::0..MaxCap, Max #>= Level, write(’area maximum, ...’), nl, areaMaximum(Data, Sums), write(’Respect peakLoads, ...’), nl, TotalNeeded :: 0..MaxCap, peakLoads(1, PeakVars, PeakDurs, PeakRes, Exceptions, MaxCap, TotalNeeded), write(’cumulative constraint regarding optimisation’), nl, finalCumulative(PeakVars, PeakDurs, PeakRes, Sums, TotalNeeded), write(MaxCap #= Max + TotalNeeded), nl, MaxCap #= Max + TotalNeeded.

enumerationt(Data, Max, TotalNeeded, Sums):-

193

194

APPENDIX B. SCHEDULING MAINTENANCES WITH CHIP

/* collect Mi-variables (start-dates) */ catchMis(Data, Mis, Du, Ca), write(’start enumeration, ...’), nl, setval(nodes, 0), min_max((myll(Mis), indomain(TotalNeeded), show(Data, MaxCap, Sums, Max), cputime(T), write(time(T)), nl, getval(nodes, Nodes), write(nodes(Nodes)), nl), [TotalNeeded]). myll([]). myll([M|Ms]):indomain(M), incval(nodes, _Current), myll(Ms). /* Check satisfiability of a solution */ maint2(Level, Exceptions, Solution):constraints(Data, Max, TotalNeeded, MaxCap, Sums, Level, Exceptions, Solution), enumerationt(Data, Max, TotalNeeded, Sums).

fill_in_data([], []). fill_in_data([unit(Name, Nr, Maint)|Units], [i(Name, Nr, Sol)|Solution]):!, write(filling(Name, Nr, Maint, Sol)), nl, match(Maint, Sol), fill_in_data(Units, Solution). fill_in_data(_, _):- write(mismatch), nl. match([], []). match([Maint|Maints], [Nr|Nrs]):Maint #= Nr, match(Maints, Nrs). constraints(Data, Max, TotalNeeded, MaxCap, Sums, Level, Exceptions, Solution):plants(Plants), createData(Plants, Data, DataPlant), fill_in_data(Data, Solution), nonsim(Data, Data), sim(Data), maxPlant(Plants, DataPlant), maxCapacity(Data, MaxCap), Max::0..MaxCap, Max #>= Level, write(’area maximum, ...’), nl, areaMaximum(Data, Sums), write(’Respect peakLoads, ...’), nl, TotalNeeded :: 0..MaxCap,

195

peakLoads(1, PeakVars, PeakDurs, PeakRes, Exceptions, MaxCap, TotalNeeded), write(’cumulative constraint regarding optimisation’), nl, finalCumulative(PeakVars, PeakDurs, PeakRes, Sums, TotalNeeded), write(MaxCap #= Max + TotalNeeded), nl, MaxCap #= Max + TotalNeeded.

createData([], [], []). createData([plant(Name, Units, _)| Plants], Data, [DataP| DataPs]):createDataPlant(Units, 0, Name, Data, Data1, DataP), createData(Plants, Data1, DataPs). createDataPlant(Units, Units, _, Data, Data, []):- !. createDataPlant(Units, UnitNr, Name, [unit(Name, UnitNr1, Maint)|Data], Data1, [unit(Name, UnitNr1, Maint)| DataP]):UnitNr1 is UnitNr + 1, unit(Name, UnitNr1, _, Fixed, Durations, Proh, _, _, _), initMijs(Maint, Durations), exclusiveMaint(Maint, Durations), fixProhMaintUnit(Proh, Maint, Durations), fixProhMaintUnit(Fixed, Maint, Durations), createDataPlant(Units, UnitNr1, Name, Data, Data1, DataP). initMijs([], []):- !. initMijs([Mij|Mijs], [Dij|Dijs]):LastStart is 53 - Dij, Mij :: 1 .. LastStart, initMijs(Mijs, Dijs). exclusiveMaint([], _Durations). exclusiveMaint([_M], _Durations). exclusiveMaint([M1, M2], [D1, D2]):write(cumulative([M1, M2], [D1, D2], [1,1], unused, unused, 1, unused, unused)), nl, cumulative([M1, M2], [D1, D2], [1,1], unused, unused, 1, unused, unused). fixProhMaintUnit([], _, _). fixProhMaintUnit([Period|Periods], Maint, Durations):arg(1, Period, Begin), arg(2, Period, End), removeFromMijs(Begin, End, Maint, Durations), fixProhMaintUnit(Periods, Maint, Durations). removeFromMijs(_Begin, _End, [], []). removeFromMijs(Begin, End, [M|Maint], [D|Durations]):F is Begin - D + 1, G is End + 1, removeFromMijsIter(F, G, M), removeFromMijs(Begin, End, Maint, Durations). removeFromMijsIter(F, F, M):- !. removeFromMijsIter(F, G, M):-

196

APPENDIX B. SCHEDULING MAINTENANCES WITH CHIP

M #\= F, F1 is F +1, removeFromMijsIter(F1, G, M).

nonsim([], _). nonsim([unit(Name, Nr, Maint)| Data1], Data2):unit(Name, Nr, _, _, Durations, _, NonSim, _, _), nonsimUnit(NonSim, Maint, Durations, Data2), nonsim(Data1, Data2).

nonsimUnit([], _, _, _). nonsimUnit([n(Name, Nr, Pre, Post)| Other], [M1], [D1], Data):unit(Name, Nr, _, _, [D2], _, _, _, _), memberUnit(Name, Nr, Data, M2), Minus2 is D2 + Pre, Plus2 is D1 + Post, cumulative([M1, M2], [Plus2, Minus2], [1,1], unused, unused, 1, unused, unused), nonsimUnit(Other, [M1], [D1], Data).

memberUnit(Name, Nr, [unit(Name, Nr, [M])| _Data], M):- !. memberUnit(Name, Nr, [_|Data], M):memberUnit(Name, Nr, Data, M).

sim([]). sim([unit(Name, Nr, Maint)| Data]):unit(Name, Nr, _, _, Duration, _, _, Sim, _), simUnit(Sim, Maint, Duration, Data). simUnit([], _, _, Data):sim(Data). simUnit([s(Name, Nr)], [M1], [D1], Data):unit(Name, Nr, _, _, [D2], _, _, _, _), memberUnit(Name, Nr, Data, M2, Data2), simConstraint(M1, D1, M2, D2), sim(Data2). memberUnit(Name, Nr, [unit(Name, Nr, [M])| Data], M, Data):- !. memberUnit(Name, Nr, [_|Data], M, Data1):memberUnit(Name, Nr, Data, M, Data1). simConstraint(M1, D1, M2, D2):D1 > D2, !, M1 # true; non_logical_io__write_string("no_solutions\n") ). :- pred generate(list(finite_var), int, int , fd_store, fd_store). :- mode generate(out, in, in , fd_store_mdi, fd_store_muo) is det. generate(Q, N, M, Store0, Store2):( N =0 -> Q = [], Store2 = Store0; new_var(X, int(1, M), Store0, Store1), N1 is N - 1, Q = [X|Q1], generate(Q1, N1, M, Store1, Store2) ). :- pred safe(list(finite_var) , fd_store, fd_store).

201

APPENDIX C. BENCHMARKS FOR MROPE II

202

:- mode safe(in , fd_store_mdi, fd_store_muo) is semidet. safe(Q, Store0, Store2):( Q = [] -> true, Store2 = Store0; Q = [X|T], noAttack(X,1,T, Store0, Store1), safe(T, Store1, Store2) ). :- pred noAttack(finite_var, int, list(finite_var) , fd_store, fd_store). :- mode noAttack(in, in, in , fd_store_mdi, fd_store_muo) is semidet. noAttack(X, N, Q, Store0, Store4):( Q = [] -> true, Store4 = Store0; Q = [Y|Z], add_constraint((var(X), +(var(Y), num(N))), Store0, Store1), add_constraint((var(X), var(Y)) , Store1, Store2), add_constraint((var(Y), +(var(X),num(N))), Store2, Store3), S is N + 1, noAttack(X,S,Z, Store3, Store4) ). :- pred output_list(list(finite_var) , fd_store). :- mode output_list(in , fd_store_mui). output_list(Q, Store0):( Q = [X], value_var(X, [V|_], Store0), non_logical_io__write_int(V), non_logical_io__write_string("]\n") ; Q = [X, Y|Q1], value_var(X, [V|_], Store0), non_logical_io__write_int(V), non_logical_io__write_string(", "), output_list([Y|Q1], Store0) ).

C.2

Bridge1

:- module bridge1. :- interface. :- import_module io. :- pred main(io__state::di, io__state::uo) is det. :- implementation. :- import_module non_logical_io, fd_solver, list, int, string. main --> ({bridge1}, io__report_stats ). :- pred bridge1 is det. % find a schedule that minimises the time to build a five-segment bridge bridge1:( init(Store1), bridge(200, Store1 , _Store) -> true; non_logical_io__write_string("no_solutions\n") ).

C.2. BRIDGE1

% top level query: bridge(Limit). % with Limit an upperlimit for the end-task. /* Data */ :- type job ---> beginning_of_project ; delivery_preformed_bearers ; erection_temporary_housing ; removal_temporary_housing ; filling_1 ; filling_2 ; costing_point_1 ; costing_point_2 ; end_of_project ; excavation_abutment_1 ; excavation_abutment_2 ; formwork_abutment_1 ; formwork_abutment_2 ; concrete_foundation_abutment_1 ; concrete_foundation_abutment_2 ; concrete_setting_time_abutment_1 ; concrete_setting_time_abutment_2 ; masonry_work_abutment_1 ; masonry_work_abutment_2 ; excavation_pillar_1 ; excavation_pillar_2 ; excavation_pillar_3 ; excavation_pillar_4 ; formwork_pillar_1 ; formwork_pillar_2 ; formwork_pillar_3 ; formwork_pillar_4 ; concrete_foundation_pillar_1; concrete_foundation_pillar_2 ; concrete_foundation_pillar_3; concrete_foundation_pillar_4 ; concrete_setting_time_pillar_1 ; concrete_setting_time_pillar_2 ; concrete_setting_time_pillar_3 ; concrete_setting_time_pillar_4 ; masonry_work_pillar_1 ; masonry_work_pillar_2 ; masonry_work_pillar_3 ; masonry_work_pillar_4 ; foundation_piles_2 ; foundation_piles_3 ; positioning_preformed_bearer_1 ; positioning_preformed_bearer_2 ; positioning_preformed_bearer_3 ; positioning_preformed_bearer_4 ; positioning_preformed_bearer_5. :- pred jobs(list(job)). :- mode jobs(out) is det. jobs([ beginning_of_project, excavation_abutment_1, excavation_pillar_1, excavation_pillar_2, excavation_pillar_3, excavation_pillar_4, excavation_abutment_2, foundation_piles_2, foundation_piles_3, erection_temporary_housing, formwork_abutment_1, formwork_pillar_1, formwork_pillar_2, formwork_pillar_3, formwork_pillar_4, formwork_abutment_2, concrete_foundation_abutment_1,concrete_foundation_pillar_1, concrete_foundation_pillar_2, concrete_foundation_pillar_3, concrete_foundation_pillar_4, concrete_foundation_abutment_2, concrete_setting_time_abutment_1, concrete_setting_time_pillar_1,concrete_setting_time_pillar_2, concrete_setting_time_pillar_3,concrete_setting_time_pillar_4, concrete_setting_time_abutment_2, masonry_work_abutment_1, masonry_work_pillar_1,

203

204

APPENDIX C. BENCHMARKS FOR MROPE II

masonry_work_pillar_2, masonry_work_pillar_3, masonry_work_pillar_4, masonry_work_abutment_2, delivery_preformed_bearers, positioning_preformed_bearer_1, positioning_preformed_bearer_2,positioning_preformed_bearer_3, positioning_preformed_bearer_4,positioning_preformed_bearer_5, removal_temporary_housing, filling_1, filling_2, costing_point_1, costing_point_2, end_of_project ]). :- pred duration(job::in, int::out) is det. duration( beginning_of_project, 0). duration( excavation_abutment_1, 4). duration( excavation_abutment_2, 5). duration( excavation_pillar_1, 2). duration( excavation_pillar_2, 2). duration( excavation_pillar_3, 2). duration( excavation_pillar_4, 2). duration( foundation_piles_2, 20). duration( foundation_piles_3, 13). duration( erection_temporary_housing, 10). duration( formwork_abutment_1, 8). duration( formwork_abutment_2, 10). duration( formwork_pillar_1, 4). duration( formwork_pillar_2, 4). duration( formwork_pillar_3, 4). duration( formwork_pillar_4, 4). duration( concrete_foundation_abutment_1, 1). duration( concrete_foundation_abutment_2, 1). duration( concrete_foundation_pillar_1, 1). duration( concrete_foundation_pillar_2,1). duration( concrete_foundation_pillar_3, 1). duration( concrete_foundation_pillar_4, 1). duration( concrete_setting_time_abutment_1, 1). duration( concrete_setting_time_abutment_2, 1). duration( concrete_setting_time_pillar_1, 1). duration( concrete_setting_time_pillar_2, 1). duration( concrete_setting_time_pillar_3, 1). duration( concrete_setting_time_pillar_4, 1). duration( masonry_work_abutment_1, 16). duration( masonry_work_abutment_2, 20). duration( masonry_work_pillar_1, 8). duration( masonry_work_pillar_2, 8). duration( masonry_work_pillar_3, 8). duration( masonry_work_pillar_4, 8). duration( delivery_preformed_bearers, 2). duration( positioning_preformed_bearer_1, 12). duration( positioning_preformed_bearer_2, 12). duration( positioning_preformed_bearer_3, 12). duration( positioning_preformed_bearer_4, 12). duration( positioning_preformed_bearer_5, 12). duration( removal_temporary_housing, 10). duration( filling_1, 15). duration( filling_2, 10). duration( costing_point_1, 0).

C.2. BRIDGE1

205

duration( costing_point_2, 0). duration( end_of_project, 0). :- type prec ---> pr(job,job). :- pred precedence(list(prec)::out) is det. precedence([pr(beginning_of_project, excavation_abutment_1), pr(beginning_of_project, excavation_pillar_1), pr(beginning_of_project, excavation_pillar_2), pr(beginning_of_project, excavation_pillar_3), pr(beginning_of_project, excavation_pillar_4), pr(beginning_of_project, excavation_abutment_2), pr(beginning_of_project, erection_temporary_housing), pr(excavation_abutment_1, formwork_abutment_1), pr(excavation_pillar_1, formwork_pillar_1), pr(excavation_pillar_2, foundation_piles_2), pr(foundation_piles_2, formwork_pillar_2), pr(excavation_pillar_3, foundation_piles_3), pr(foundation_piles_3, formwork_pillar_3), % pr(excavation_pillar_4, formwork_pillar_4), % pr(excavation_abutment_2, formwork_abutment_2), pr(foundation_piles_2, costing_point_1), pr(foundation_piles_3, costing_point_1), pr(formwork_pillar_2, costing_point_1), pr(formwork_pillar_3, costing_point_2), pr(formwork_abutment_1, concrete_foundation_abutment_1), pr(formwork_pillar_1, concrete_foundation_pillar_1), pr(formwork_pillar_2, concrete_foundation_pillar_2), pr(formwork_pillar_3, concrete_foundation_pillar_3), pr(formwork_pillar_4, concrete_foundation_pillar_4), pr(formwork_abutment_2, concrete_foundation_abutment_2), pr(concrete_foundation_abutment_1, concrete_setting_time_abutment_1), pr(concrete_foundation_pillar_1, concrete_setting_time_pillar_1), pr(concrete_foundation_pillar_2, concrete_setting_time_pillar_2), pr(concrete_foundation_pillar_3, concrete_setting_time_pillar_3), pr(concrete_foundation_pillar_4, concrete_setting_time_pillar_4), pr(concrete_foundation_abutment_2, concrete_setting_time_abutment_2), pr(concrete_setting_time_abutment_1, masonry_work_abutment_1), pr(concrete_setting_time_pillar_1, masonry_work_pillar_1), pr(concrete_setting_time_pillar_2, masonry_work_pillar_2), pr(concrete_setting_time_pillar_3, masonry_work_pillar_3), pr(concrete_setting_time_pillar_4, masonry_work_pillar_4), pr(concrete_setting_time_abutment_2, masonry_work_abutment_2), pr(masonry_work_abutment_1, positioning_preformed_bearer_1), pr(masonry_work_pillar_1, positioning_preformed_bearer_1), pr(masonry_work_pillar_1, positioning_preformed_bearer_2), pr(masonry_work_pillar_2, positioning_preformed_bearer_2), pr(masonry_work_pillar_2, positioning_preformed_bearer_3), pr(masonry_work_pillar_3, positioning_preformed_bearer_3),

206

APPENDIX C. BENCHMARKS FOR MROPE II

pr(masonry_work_pillar_3, positioning_preformed_bearer_4), pr(masonry_work_pillar_4, positioning_preformed_bearer_4), pr(masonry_work_pillar_4, positioning_preformed_bearer_5), pr(masonry_work_abutment_2, positioning_preformed_bearer_5), pr(masonry_work_abutment_1, costing_point_2), pr(masonry_work_pillar_1, costing_point_2), pr(masonry_work_pillar_2, costing_point_2), pr(masonry_work_pillar_3, costing_point_2), pr(masonry_work_pillar_4, costing_point_2), pr(masonry_work_abutment_2, costing_point_2), pr(delivery_preformed_bearers,positioning_preformed_bearer_1), pr(delivery_preformed_bearers,positioning_preformed_bearer_2), pr(delivery_preformed_bearers,positioning_preformed_bearer_3), pr(delivery_preformed_bearers,positioning_preformed_bearer_4), pr(delivery_preformed_bearers,positioning_preformed_bearer_5), pr(positioning_preformed_bearer_1, filling_1), pr(positioning_preformed_bearer_5, filling_2), pr(filling_1, end_of_project), pr(positioning_preformed_bearer_2, end_of_project), pr(positioning_preformed_bearer_3, end_of_project), pr(positioning_preformed_bearer_4, end_of_project), pr(filling_2, end_of_project), pr(costing_point_1, end_of_project), pr(costing_point_2, end_of_project), pr(removal_temporary_housing, end_of_project) ]). :- type maxnf ---> max_nf(job,job,int). :- type minaf ---> min_af(job,job,int). :- type maxef ---> max_ef(job,job,int). :- type minnf ---> min_nf(job,job,int). :- pred max_nf(list(maxnf)::out) is det. :- pred min_af(list(minaf)::out) is det. :- pred max_ef(list(maxef)::out) is det. :- pred min_nf(list(minnf)::out) is det. max_nf([ max_nf(excavation_abutment_1, formwork_abutment_1, 3), max_nf(excavation_pillar_1, formwork_pillar_1, 3), max_nf(foundation_piles_2, formwork_pillar_2, 3), max_nf(foundation_piles_3, formwork_pillar_3, 3), max_nf(excavation_pillar_4, formwork_pillar_4, 3), max_nf(excavation_abutment_2, formwork_abutment_2, 3), max_nf(beginning_of_project, delivery_preformed_bearers, 30) ]). min_af([ min_af(erection_temporary_housing, min_af(erection_temporary_housing, min_af(erection_temporary_housing, min_af(erection_temporary_housing, min_af(erection_temporary_housing, min_af(erection_temporary_housing,

formwork_abutment_1, 6), formwork_pillar_1, 6), formwork_pillar_2, 6), formwork_pillar_3, 6), formwork_pillar_4, 6), formwork_abutment_2, 6) ]).

max_ef([ max_ef(formwork_abutment_1, concrete_foundation_abutment_1, 4), max_ef(formwork_pillar_1, concrete_foundation_pillar_1, 4), max_ef(formwork_pillar_2, concrete_foundation_pillar_2, 4), max_ef(formwork_pillar_3, concrete_foundation_pillar_3, 4), max_ef(formwork_pillar_4, concrete_foundation_pillar_4, 4),

C.2. BRIDGE1

207

max_ef(formwork_abutment_2, concrete_foundation_abutment_2,4)]). min_nf([ min_nf(beginning_of_project, delivery_preformed_bearers, 30), min_nf(masonry_work_abutment_1, removal_temporary_housing, -2), min_nf(masonry_work_pillar_1, removal_temporary_housing, -2), min_nf(masonry_work_pillar_1, removal_temporary_housing, -2), min_nf(masonry_work_pillar_1, removal_temporary_housing, -2), min_nf(masonry_work_pillar_1, removal_temporary_housing, -2), min_nf(masonry_work_abutment_2, removal_temporary_housing,-2)]).

% following ordering of resources is the one which gives the best % efficiency -> make choices about resources used closer to the end % of the project first = backward scheduling :- type machine ---> caterpillar ; crane ; bricklaying ; concrete_mixer ; carpentry ; pile_driver ; excavator. :- type needs ---> res(machine,list(job)). :- pred resource(list(needs)::out) is det. resource([ res(caterpillar, [filling_1, filling_2]), res(crane, [delivery_preformed_bearers, positioning_preformed_bearer_1, positioning_preformed_bearer_2, positioning_preformed_bearer_3, positioning_preformed_bearer_4, positioning_preformed_bearer_5]), res(bricklaying,[masonry_work_abutment_1,masonry_work_pillar_1, masonry_work_pillar_2, masonry_work_pillar_3, masonry_work_pillar_4, masonry_work_abutment_2]), res(concrete_mixer, [concrete_foundation_abutment_1, concrete_foundation_pillar_1, concrete_foundation_pillar_2, concrete_foundation_pillar_3, concrete_foundation_pillar_4, concrete_foundation_abutment_2]), res(carpentry, [formwork_abutment_1, formwork_pillar_1, formwork_pillar_2, formwork_pillar_3, formwork_pillar_4, formwork_abutment_2]), res(pile_driver, [foundation_piles_2, foundation_piles_3]), res(excavator, [excavation_abutment_1, excavation_pillar_1, excavation_pillar_2, excavation_pillar_3, excavation_pillar_4, excavation_abutment_2])]). :- type task ---> task(job,finite_var,int). /* program */ % make_data(Lt, Ld) % Lt = list of tasks

208

APPENDIX C. BENCHMARKS FOR MROPE II

% Ld = list of data structures task(name of the task, begin, duration) % with 0 e(job,finite_var,int,finite_var,int,int). :- pred make_disj(list(needs), list(task), list(exl), int). :- mode make_disj(in, in, out, in) is nondet. make_disj([], _, [], _). make_disj([res(_, Tasks) | OtherResources], Ld, Exl, Nr):gather_tasks(Tasks, Ld, NewTasks), construct_exclusion(NewTasks, First, Nr, NewNr), make_disj(OtherResources, Ld, Last, NewNr), list__append(First,Last,Exl). :- pred gather_tasks(list(job)::in, list(task)::in, list(task)::out) is nondet. gather_tasks([], _, []). gather_tasks([R|Rs], Ld, [task(R,S,D) | Tasks]):list__member(task(R, S, D), Ld), gather_tasks(Rs, Ld, Tasks). :- pred construct_exclusion(list(task), list(exl), int, int). :- mode construct_exclusion(in, out, in, out) is det. :- pred construct_exclusion1(task, list(task), list(exl), int, int). :- mode construct_exclusion1(in, in, out, in, out) is det. construct_exclusion([], [], Nr, Nr). construct_exclusion([Task |Tasks], Exl, Nr, NewNr):construct_exclusion1(Task, Tasks, First, Nr, NewNr1), construct_exclusion(Tasks, Last, NewNr1, NewNr), list__append(First,Last,Exl). construct_exclusion1(_, [], [], Nr, Nr).

210

APPENDIX C. BENCHMARKS FOR MROPE II

construct_exclusion1(task(Name1, Start1, Duration1), [task(_, Start2, Duration2) | Tasks], [e(Name1, Start1, Duration1, Start2, Duration2, Nr) | Exl], Nr, NewNr):NNr is Nr + 1, construct_exclusion1(task(Name1, Start1, Duration1), Tasks, Exl, NNr, NewNr). :- pred put_disj(list(exl), finite_var , fd_store, fd_store). :- mode put_disj(in, in , fd_store_mdi, fd_store_muo) is nondet. put_disj([], _, Store0, Store0). put_disj([e(_ ,Start1,Duration1,Start2,Duration2, _)| Exl], End, Store0, Store3):minimize_handle(End, Store0, Store1), add_constraint(var(Start2) >= var(Start1)+num(Duration1), Store1, Store2), put_disj(Exl, End, Store2, Store3); minimize_handle(End, Store0, Store1), add_constraint(var(Start1) >= var(Start2)+num(Duration2), Store1, Store2), put_disj(Exl, End, Store2, Store3). :- pred data_structure(list(task), list(finite_var), finite_var , fd_store, fd_store) is nondet. :- mode data_structure(out, out, out , fd_store_mdi, fd_store_muo). data_structure(Ld, Starts, End, Store0, Store4):jobs(Lt), make_data(Lt, Ld, Starts, Store0, Store1), list__member(task(beginning_of_project, Begin, 0), Ld), add_constraint( var(Begin) = num(0), Store1, Store2), non_logical_io__write_string("constraints...\n"), constraint(Ld, Store2, Store3), list__member(task(end_of_project, End, 0), Ld), add_constraint( var(End) =< num(200), Store3, Store4).

:- pred bridge(int , fd_store, fd_store) is nondet. :- mode bridge(in , fd_store_mdi, fd_store_muo). bridge(Limit, Store0, Store7):new_var(Dummy, int(0,1), Store0, Store1), non_logical_io__write_string("making the data structure...\n"), data_structure(Ld, Starts, End, Store1, Store2), add_constraint( var(End) =< num(Limit) , Da2, Store3), resource(R), minimize(End,Dummy,Starts, Store3, Store4), non_logical_io__write_string("make the disjunctions\n"), make_disj(R, Ld, Exl, 1), non_logical_io__write_string("impose the disjunctions\n"), put_disj(Exl, End, Store4, Store5), enumM(End, [End], up, up,standard_ex, Store5, Store6), add_constraint( var(Dummy) = num(1), Store6, Store7), non_logical_io__write_string("we should never get here\n").

C.3. BRIDGE2

C.3

211

Bridge2

/* data omitted */ /* From the program, only the clauses different from the clauses above are given */ :- pred make_disj(list(needs), list(task), list(finite_var), int, fd_store, fd_store) is nondet. :- mode make_disj(in, in, out, in , fd_store_mdi, fd_store_muo). make_disj([], _, [], _, Store0, Store0). make_disj([res(_, Tasks) | OtherResources], Ld, Exl, Nr, Store0, Store2):gather_tasks(Tasks, Ld, NewTasks), construct_exclusion(NewTasks, First, Nr, NewNr, Store0, Store1), make_disj(OtherResources, Ld, Last, NewNr, Store1, Store2), list__append(First,Last,Exl). :- pred gather_tasks(list(job)::in, list(task)::in, list(task)::out) is nondet. gather_tasks([], _, []). gather_tasks([R|Rs], Ld, [task(R,S,D) | Tasks]):list__member(task(R, S, D), Ld), gather_tasks(Rs, Ld, Tasks). :- pred construct_exclusion(list(task), list(finite_var), int, int , fd_store, fd_store) is semidet. :- mode construct_exclusion(in,out,in,out,fd_store_mdi,fd_store_muo). :- pred construct_exclusion1(task, list(task), list(finite_var), int, int , fd_store, fd_store) is semidet. :- mode construct_exclusion1(in, in, out, in, out, fd_store_mdi, fd_store_muo).

construct_exclusion([], [], Nr, Nr, Store0, Store0). construct_exclusion([Task |Tasks], Exl, Nr, NewNr, Store0, Store2):construct_exclusion1(Task, Tasks,First,Nr,NewNr1,Store0,Store1), construct_exclusion(Tasks, Last, NewNr1, NewNr, Store1, Store2), list__append(First,Last,Exl). construct_exclusion1(_, [], [], Nr,Nr, Store0, Store0). construct_exclusion1(task(Name1, Start1, Duration1), [task(_, Start2, Duration2) | Tasks], [B1 | Exl], Nr, NewNr, Store0, Store6):new_var(B1, int(0,1), Store0, Store1), new_var(B2, int(0,1), Store1, Store2), add_constraint(var(B1) = num(1) - var(B2), Store2, Store3), add_constraint(equiv(B1, var(Start2)>=var(Start1)+num(Duration1)) , Store3, Store4), add_constraint(equiv(B2, var(Start1)>=var(Start2)+num(Duration2)) , Store4, Store5), NNr is Nr + 1, construct_exclusion1(task(Name1, Start1, Duration1), Tasks, Exl, NNr, NewNr, Store5, Store6).

APPENDIX C. BENCHMARKS FOR MROPE II

212

:- pred bridge(int, fd_store, fd_store) is nondet. :- mode bridge(in, fd_store_mdi, fd_store_muo). bridge(Limit, Store0, Store8):new_var(Dummy, int(0,1), Store0, Store1), non_logical_io__write_string("making the data structure...\n"), data_structure(Ld, Starts, End, Store1, Store2), add_constraint( var(End) =< num(Limit), Store2, Store3), resource(R), minimize(End, Dummy, Starts, Store3, Store4), non_logical_io__write_string("make the disjunctions\n"), make_disj(R, Ld, Exl, 1, Store4, Store5), non_logical_io__write_string("impose the disjunctions\n"), enumM(End, Exl, up, down, standard_ex, Store5, Store6), enumM(End, [End], up, up, standard_ex, Store6, Store7), non_logical_io__write_string("activate minimisation\n"), add_constraint(var(Dummy) = num(1), Store7, Store8), non_logical_io__write_string("we should never get here\n").

C.4

Suudoku

:- module suudoku. :- interface. :- import_module io. :- pred main(io__state::di, io__state::uo) is det. :- implementation. :- import_module non_logical_io, fd_solver, list, int, string, require. main --> ({suudoku}, io__report_stats ). :- pred suudoku is det. suudoku:- ( suudoku1(1), suudoku1(2), suudoku1(3), suudoku1(4), suudoku1(5), suudoku1(6), fail -> true; non_logical_io__write_string("no_solutions\n") ). :- pred suudoku1(int). :- mode suudoku1(in). suudoku1(Nr):init(Store1), (suudoku2(Nr, Store1 , _Store)

-> true; true ).

:- pred suudoku2(int , fd_store, fd_store). :- mode suudoku2(in , fd_store_mdi, fd_store_muo) is nondet. suudoku2(Which, Store0, Store5):domain_problem(Which, Problem, Store0, Store1), row_constraint(Problem, Store1, Store2), column_constraint(Problem, Store2, Store3), block_constraint(Problem, Store3, Store4),

C.4. SUUDOKU

213

enum_problem(Problem, Store4, Store5). :- pred row_constraint(list(list(finite_var)), fd_store, fd_store). :- mode row_constraint(in , fd_store_mdi, fd_store_muo) is semidet. row_constraint([R|Rt], Store0, Store2):alldifferent(R, Store0, Store1), row_constraint(Rt, Store1, Store2). row_constraint([], Store0, Store0). :- pred column_constraint(list(list(finite_var)) , fd_store, fd_store). :- mode column_constraint(in , fd_store_mdi, fd_store_muo) is semidet. :- pred column_constraint(list(finite_var), list(finite_var), list(finite_var), list(finite_var), list(finite_var), list(finite_var), list(finite_var), list(finite_var), list(finite_var), fd_store, fd_store). :- mode column_constraint(in, in, in, in, in, in, in, in, in, fd_store_mdi, fd_store_muo) is semidet. column_constraint([C1,C2,C3,C4,C5,C6,C7,C8,C9], Store0, Store1):column_constraint(C1,C2,C3,C4,C5,C6,C7,C8,C9, Store0, Store1). column_constraint([C1|C1t],[C2|C2t],[C3|C3t],[C4|C4t],[C5|C5t], [C6|C6t],[C7|C7t],[C8|C8t],[C9|C9t], Store0, Store2):alldifferent([C1,C2,C3,C4,C5,C6,C7,C8,C9], Store0, Store1), column_constraint(C1t,C2t,C3t,C4t,C5t,C6t,C7t,C8t,C9t, Store1, Store2). column_constraint([],[],[],[],[],[],[],[],[], Store0, Store0). :- pred block_constraint(list(list(finite_var)) , fd_store, fd_store). :- mode block_constraint(in , fd_store_mdi, fd_store_muo) is semidet. :- pred block_constraint(list(finite_var), list(finite_var), list(finite_var) , fd_store, fd_store) is semidet. :- mode block_constraint(in, in, in, fd_store_mdi, fd_store_muo) is semidet. block_constraint([C1,C2,C3,C4,C5,C6,C7,C8,C9], Store0, Store3):block_constraint(C1,C2,C3, Store0, Store1), block_constraint(C4,C5,C6, Store1, Store2), block_constraint(C7,C8,C9, Store2, Store3). block_constraint([C1,C2,C3|C1t],[C4,C5,C6|C2t],[C7,C8,C9|C3t], Store0, Store2):alldifferent([C1,C2,C3,C4,C5,C6,C7,C8,C9], Store0, Store1), block_constraint(C1t,C2t,C3t, Store1, Store2). block_constraint([],[],[], Store0, Store0). :- pred enum_problem(list(list(finite_var)) , fd_store, fd_store). :- mode enum_problem(in , fd_store_mdi, fd_store_muo) is nondet. enum_problem(P, Store0, Store1):append_all(P, Pf), enum(Pf, firstfail, up, standard_ex, Store0, Store1), output_list(Pf, Store1). :- pred append_all(list(list(T)),list(T)).

214

APPENDIX C. BENCHMARKS FOR MROPE II

:- mode append_all(in,out) is det. :- pred lappend(list(T),list(T),list(T)). :- mode lappend(in,in,out) is det. append_all([], []). append_all([P|R], X):append_all(R, Y), lappend(P, Y, X). lappend([], X, X). lappend([X|Y], Z, [X|W]):lappend(Y, Z, W). :- pred problem2(int,list(list(int))). :- mode problem2(in,out) is det. problem2(Nr, P):- % shokyuu ( Nr=1 -> P=[[1,0,0,8,0,4,0,0,0], [0,2,0,0,0,0,4,5,6], [0,0,3,2,0,5,0,0,0], [0,0,0,4,0,0,8,0,5], [7,8,9,0,5,0,0,0,0], [0,0,0,0,0,6,2,0,3], [8,0,1,0,0,0,7,0,0], [0,0,0,1,2,3,0,8,0], [2,0,5,0,0,0,0,0,9]]; Nr=2 -> P=[[0,0,2,0,3,0,1,0,0], [0,4,0,0,0,0,0,3,0], [1,0,5,0,0,0,0,8,2], [0,0,0,2,0,0,6,5,0], [9,0,0,0,8,7,0,0,3], [0,0,0,0,4,0,0,0,0], [8,0,0,0,7,0,0,0,4], [0,9,3,1,0,0,0,6,0], [0,0,7,0,6,0,5,0,0]]; Nr=3 -> P=[[0,0,0,0,0,0,3,0,0], [0,0,0,8,5,0,0,1,0], [0,0,2,0,0,4,0,0,9], [0,3,0,0,0,2,0,0,4], [8,0,0,0,6,0,0,0,1], [7,0,0,9,0,0,0,5,0], [1,0,0,6,0,0,7,0,0], [0,9,0,0,2,3,0,0,0], [0,0,4,0,0,0,0,0,0]]; Nr=4 -> P=[[0,7,9,0,0,0,0,0,1], [6,0,0,0,0,0,3,8,0], [0,0,0,0,4,2,0,0,0], [0,0,3,9,0,0,0,0,0], [7,8,0,0,0,0,0,2,5], [0,0,0,0,0,4,8,0,0], [0,0,0,3,1,0,0,0,0], [0,5,6,0,0,0,0,0,7], [2,0,0,0,0,0,4,3,0]];

C.4. SUUDOKU

215

Nr=5 -> P=[[0,5,0,7,0,1,0,4,0], [7,0,3,0,0,0,1,0,2], [0,8,0,4,0,6,0,9,0], [9,0,4,0,6,0,8,0,3], [0,0,0,8,0,7,0,0,0], [1,0,8,0,5,0,6,0,9], [0,1,0,6,0,3,0,8,0], [5,0,6,0,0,0,7,0,1], [0,3,0,5,0,9,0,2,0]]; Nr=6 -> P=[[8,0,0,0,0,5,0,0,0], [0,1,2,3,0,0,6,0,0], [0,4,5,6,0,0,0,2,0], [0,7,8,0,0,0,0,0,1], [0,0,0,0,9,0,0,0,0], [9,0,0,0,0,0,8,7,0], [0,2,0,0,0,6,5,4,0], [0,0,4,0,0,3,2,1,0], [0,0,0,1,0,0,0,0,9]]; error("invalid problem") ). :- pred domain_problem(int, list(list(finite_var)) , fd_store, fd_store). :- mode domain_problem(in, out , fd_store_mdi, fd_store_muo) is semidet. domain_problem(Nr,Array, Store0, Store1):problem2(Nr, Ints), problem_rows(Ints,Array, Store0, Store1). :- pred problem_rows(list(list(int)), list(list(finite_var)), fd_store, fd_store). :- mode problem_rows(in, out , fd_store_mdi, fd_store_muo) is semidet. problem_rows([],[], Store0, Store0). problem_rows([P|R],[S|F], Store0, Store2):problem(P,S, Store0, Store1), problem_rows(R,F, Store1, Store2). :- pred problem(list(int), list(finite_var) , fd_store, fd_store). :- mode problem(in, out , fd_store_mdi, fd_store_muo) is semidet. problem([],[], Store0, Store0). problem([P|R],[S|F], Store0, Store2):( P=0 -> new_var(S, int(1,9), Store0, Store1); new_var(S, num(P), Store0, Store1) ), problem(R,F, Store1, Store2). :- pred alldifferent(list(finite_var), fd_store, fd_store) is semidet. :- mode alldifferent(in , fd_store_mdi, fd_store_muo) is semidet. :- pred alldifferent(finite_var, list(finite_var), fd_store, fd_store) is semidet. :- mode alldifferent(in, in , fd_store_mdi, fd_store_muo) is semidet. alldifferent([], Store0, Store0). alldifferent([X|Y], Store0, Store2):alldifferent(X, Y, Store0, Store1),

216

APPENDIX C. BENCHMARKS FOR MROPE II

alldifferent(Y, Store1, Store2). alldifferent(_, [], Store0, Store0). alldifferent(X, [Y|Z], Store0, Store2):add_constraint((var(X), var(Y)), Store0, Store1), alldifferent(X, Z, Store1, Store2). :- pred output_list(list(finite_var) , fd_store). :- mode output_list(in , fd_store_mui). output_list(Q, Store0):( Q = [X], value_var(X, [V|_], Store0), non_logical_io__write_int(V), non_logical_io__write_string("]\n") ; Q = [X, Y|Q1], value_var(X, [V|_], Store0), non_logical_io__write_int(V), non_logical_io__write_string(", "), output_list([Y|Q1], Store0) ).

Bibliography [AB91]

K.R. Apt and M. Bezem. Acyclic programs. New Generation Computing, 9:335–363, 1991.

[AB92]

A. Aggoun and N. Beldiceanu. Extending CHIP in order to solve complex scheduling and placement problems. JFPL, pages 51–66, 1992.

[ABK89]

K.R. Apt, R.N. Bol, and J.W. Klop. On the safe termination of Prolog programs. In Giorgio Levi and Maurizio Martelli, editors, Proceedings of the Sixth International Conference on Logic Programming, pages 353–368, Lisbon, 1989. The MIT Press.

[AP91]

K.R. Apt and D. Pedreschi. Proving termination of general Prolog programs. In Proc. International Conference on Theoretical Aspects of Computer Science, Sendai, Japan, 1991.

[AP94]

K.R. Apt and D. Pedreschi. Modular termination proofs for logic and pure Prolog programs. In Advances in Logic Programming Theory, pages 183– 229. Oxford University Press, 1994.

[BD92]

M. Bruynooghe and D. De Schreye. Meta-interpretation. In S. C. Shapiro, editor, Encyclopedia of Artificial Intelligence, pages 939–940. John Wiley & Sons, Inc, 1992.

[Bez92]

M. Bezem. Characterizing termination of logic programs with level mappings. Journal of Logic Programming, 15(1 & 2):79–98, 1992.

[BO92]

F. Benhamou and W. Older. Applying Interval Arithmetic to Integer and Boolean Constraints. Technical report, Bell Northern Research, 1992.

[BO97]

F. Benhamou and W. Older. Applying Interval Arithmetic to Real, Integer and Boolean Constraints. Journal of Logic Programming, 32(1), July 1997.

[Boo47]

G. Boole. The Mathematical Analysis of Logic. Macmillan, 1847.

217

BIBLIOGRAPHY

218

[Bor95]

S. Borghers. Constraint logic programming applied to a public service schedule problem. Master’s thesis, K.U.Leuven departement computerwetenschappen, 1995.

[BP84]

M. Bruynooghe and L. Pereira. Deduction revision by intelligent backtracking. In J. A. Campbell, editor, Implementation of Prolog, pages 194–215. Ellis Horwoord, 1984.

[Bru78]

M. Bruynooghe. Intelligent backtracking for an interpreter of horn clause logic programs. Technical Report report CW 16, K.U.Leuven, departement of computer science, 1978.

[Bru81]

M. Bruynooghe. Solving combinatorial search problems by intelligent backtracking. Informatin Processing Letters, 12(1):36–39, 1981.

[Bru86]

M. Bruynooghe. Graph coloring and constraint satisfaction. Technical Report report CW 44, K.U.Leuven, departement of computer science, 1986.

[BS87]

W. Buttner and H. Simonis. Embedding boolean expressions into logic programming. Journal of Symbolic Computation, 4(2):191–205, October 1987.

[BS91]

A. Brodsky and Y. Sagiv. Inference of inequality constraints in logic programs. In Proceedings of the 10th ACM SIGACT-SIGMOD-SIGART Symposium on Principles of Database Systems, pages 227–240, Denver, Colorado, 1991.

[BVdD98] M. Bruynooghe, H. Vandecasteele, D.A. de Waal, and M. Denecker. Detecting unsolvable queries for definite logic programs. In Principles of Declarative Programming, Proc. of PLILP, ALP’98. LNCS 1490, pages 118–133, September 1998. [BW88]

H. Boehm and M. Weiser. Garbage collection in an uncooperative environment. Software Practice and Experience, 18:807–820, 1988.

[CCF88]

C. Codognet, P. Codognet, and G. Fil`e. Yet another intelligent backtracking method. Proceedings of the Fifth International Conference on Logic Programming, Seattle 1988.

[CD95]

M. Codish and B. Demoen. Analysing logic programs using ”prop”ositional logic programs and a magic wand. Journal of Logic Programming, 25(3):249–274, December 1995.

[CD96]

P. Codognet and D. Diaz. Compiling constraints in clp(fd). The Journal of Logic Programming, 27(3):185–226, June 1996.

[CHI97]

CHIP System Documentation, November 1997.

BIBLIOGRAPHY

219

[CL94]

Y. Caseau and F. Laburthe. Impoved clp scheduling with task intervals. In P. Van Hentenryck, editor, Proceedings of the Eleventh International Conference on Logic Programming, pages 369–383, 1994.

[CL96]

Y. Caseau and F. Laburthe. Cumulative scheduling with task intervals. In M. Maher, editor, Proceedings of the 1996 Joint International Conference and Symposium on Logic Programming, pages 363–377, 1996.

[COC97]

M. Carlsson, G. Ottosson, and B Carlson. An open-ended finite domain constraint solver. In Proc. of Programming Languages: Implementations, Logics, and Programs, 1997.

[Col90]

A. Colmerauer. An introduction to PrologIII. Communications of the ACM, 30(7):69–96, 1990.

[CP98]

W. Charatonik and A. Podelski. Directional type inference for logic programs. In Fifth International Static Analysis Symposium, LNCS, Pisa, Italy, 1998. to appear.

[Cra93]

J.-Y. Cras. A Review of Industrial Constraint Solving Tools. AI Perspectives. AI Intelligence, 1993.

[CS91]

P. Codognet and T. Sola. Extending wam for intelligent backtracking. Proceedings of the Eighth International Conference on Logic Programming, pages 127–141, 1991.

[CT97]

M. Codish and C. Taboch. A semantic basis for termination analysis of logic programs and its realization using symbolic norm constraints. In Proceedings of the Sixth International Conference on Algebraic and Logic Programming, 1997. To appear.

[DC93]

D. Diaz and P. Codognet. A minimal extention of the WAM for clp(fd). In D.S. Warren, editor, Proceedings of the Tenth International Conference on Logic Programming., pages 774–790, 1993.

[DD92]

M. Denecker and D. De Schreye. SLDNFA; an abductive procedure for normal abductive programs. In K.R. Apt, editor, Proc. of the International Joint Conference and Symposium on Logic Programming, pages 686–700, 1992.

[DD94]

D. De Schreye and S. Decorte. Termination of logic programs: the neverending story. The Journal of Logic Programming, 19 & 20:199–260, May 1994.

[DD97a]

S. Decorte and D. De Schreye. Demand-driven and constraint-based automatic termination analysis for logic programs. In L. Naish, editor, Proc. of the Fourteenth International Conference on Logic Programming, pages 78–92, 1997.

220

BIBLIOGRAPHY

[DD97b]

M. Denecker and D. De Schreye. SLDNFA: an abductive procedure for abductive logic programs. Journal of Logic Programming, 34(2):111–167, 1997.

[DDF93]

S. Decorte, D. De Schreye, and M. Fabris. Automatic inference of norms : a missing link in automatic termination analysis. In D. Miller, editor, Proceedings ILPS’93, pages 420–436, Vancouver, Canada, 1993.

[DDV99]

S. Decorte, D. De Schreye, and H. Vandecasteele. Constraint-based termination analysis of logic programs. ACM Transactions on Programming Languages and Systems, To appear, 1999.

[Dec97]

S. Decorte. Enhancing the Power of Termination Analysis of Logic Programs Through Types and Constraints. PhD thesis, Department of Computer Science, K.U.Leuven, 1997.

[Den93]

M. Denecker. Knowledge Representation and Reasoning in Incomplete Logic Programming. PhD thesis, Department of Computer Science, K.U. Leuven, 1993.

[Dep94]

Departement computerwetenschappen, KULeuven. ROPE User’s Manual 1.0, november 1994.

[DPRB90] D. De Schreye, D. Pollet, J. Ronsyn, and M. Bruynooghe. Implementing finite domain constraint logic programming on top of a Prolog system with a delay mechanism. In N. Jones, editor, proceedings of ESOP90, LNCS 432, pages 106–117. Springer-Verlag, 1990. [dT95]

D.A. de Waal and M. Tielscher. Solving deductive planning problems using program analysis and transformation. In Maurizio Proietti, editor, Logic Program Synthesis and Transformation: 5th International Workshop, LOPSTR’95, pages 189–203, 1995.

[DV95]

D. De Schreye and K. Verschaetse. Deriving linear size relations for logic programs by abstract interpretation. New Generation Computing, 13(2):117– 154, 1995.

[DVB92]

D. De Schreye, K. Verschaetse, and M. Bruynooghe. A framework for analysing the termination of definite logic programs with respect to call patterns. In Proc. FGCS’92, pages 481–488, ICOT Tokyo, 1992. ICOT.

[DVS+ 88] M. Dincbas, P. Van Hentenryck, H. Simonis, A. Aggoun, T. Graf, and F. Berthier. The constraint logic programming language CHIP. In Proceedings of the International Conference on Fifth Generation Computer Systems, pages 693–702. Tokyo, 1988.

BIBLIOGRAPHY

221

[DVS+ 97] M. Denecker, H. Vandecasteele, D. De Schreye, G. Seghers, and T. Baeyens. Scheduling by “abductive execution” of a classical logic specification. Presented at the ERCIM/Compulog workshop in Linz during the Third International Conference on Principles and Practice of Constraint Programming(CP’97), 1997. [Eur98]

European Computer-Industry Research Centre, IC-Parc, Munich, Germany and England, London. ECLiPSe 3.7 User Manual, 1998.

[FGa]

R. Fourer and J.W. Gregory. Linear programming frequently asked questions. http: //www.mcs.anl.gov /home /otc /Guide /faq /linear-programmingfaq.html.

[FGb]

R. Fourer and J.W. Gregory. Non-linear programming frequently asked questions. http://www.mcs.anl.gov/ home/otc/Guide/faq/nonlinear-programming-faq.html.

[Fik68]

R. E. Fikes. A Heuristic Program for Solving Problems Stated as Nondeterministic Procedures. PhD thesis, Comput. Sci. dept., Carnegie-Mellon Univ. Pittsburgh, 1968.

[Fre78]

E.C. Freuder. Synthesizing constraint expressions. Communications of the ACM, 21:958–966, November 1978.

[Gas74]

J. Gaschnig. A constraint satisfaction method for inference making. In Proceedings of the twelfth Annnual Allerton Conference on Circuit System Theory, pages 866–874, 1974.

[GCL92]

K.O Geddes, S.R. Czapor, and G. Labahn. Algorithms for Computer Algebra. Kluwer Academic Publishers, 1992.

[Gd94]

J.P. Gallagher and D.A. de Waal. Fast and precise regular approximations of logic programs,. In P. Van Hentenryck, editor, Proc. ICLP94, pages 599– 613. MIT Press, 1994.

[Gee91]

P.A Geelen. Een duale methodologie voor binaire constraint satisfaction problemen. In Prof. Dr. J. Treur, editor, Proceedings of NAIC’91, pages 169–182, 1991.

[GO90]

K. Gheysen and I. Opsommer. Een implementatie van constraint logisch programmmeren in Prolog en een toepassing ervan in scheduling. Master’s thesis, K.U.Leuven departement computerwetenschappen, 1990.

[Gro75]

Group d’Intelligence Artificielle, Universite d’Aix-Marseille, Luminy in France. PROLOG: Manuel de Reference et d’Utilisation, September 1975.

222

BIBLIOGRAPHY

[Gys94]

R. Gys. Toepassingen van constraint logisch programmeren. Master’s thesis, K. U. Leuven, departement computerwetenschappen, 1994.

[Hei92]

N. Heintze. Set based program analysis. PhD thesis, School of Computer Science, Carnegie Mellon University, 1992.

[HL90]

F.S. Hillier and G.J. Lieberman. Introduction to Operations Reserach. McGraw-Hill Publishing Company, 1990.

[Hol95]

C. Holzbaur. OFAI clp(q,r) Manual, Edition 1.3.3. Austrian Research Institute for Artifical Intelligence, Vienna, 1995. TR-95-09.

[Hon92]

H. Hong. Non-linear real constraints in constraint logic programming. In Proceedings of the International Conference on Algebraic and Logic Programming, pages 201–212. Springer Lecture Notes in Computer Science 632, 1992.

[Hon93]

H. Hong. RISC-CLP(Real): Logic programming with non-linear constraints over the reals. In F. Benhamou and A. Colmerauer, editors, Constraint Logic Programming: Selected Research, pages 133–159. The MIT Press, 1993.

[JL87]

J. Jaffar and J.-L. Lassez. Constraint logic programming. In POPL’87: Proceedings 14th ACM Symposium on Principles of Programming Languages, pages 111–119, Munich, 1987. ACM.

[JMSY92] J. Jaffar, S. Michaylov, P. Stuckey, and R. Yap. The CLP(R) language and system. ACM Transactions on Programming Languages and Systems, 14(3):339–395, 1992. [Kow79a]

R.A. Kowalski. Algorithm = logic + control. Communications of the ACM, 22:424–431, 1979.

[Kow79b]

R.A. Kowalski. Logic for problem solving. Elsevier Science Publisher, 1979.

[LDd96]

M. Leuschel, D. De Schreye, and D.A. de Waal. A Conceptual Embedding of Folding into Partial Deduction: Towards a Maximal Integration. In Michael Maher, editor, Proceedings of the Joint International Conference and Symposium on Logic Programming JICSLP’96, pages 319–332, Bonn, Germany, Sept. 1996. MIT Press. Extended version as Technical Report CW 225, K.U. Leuven. Accessible via http://www.cs.kuleuven.ac.be/˜lpai.

[LS97]

N. Lindenstrauss and Y. Sagiv. Automatic termination analysis of logic programs. In L. Naish, editor, Proc. 14th International Conference on Logic Programming, pages 63–77, Leuven, Belgium, july 1997.

[LT84]

J.W. Lloyd and R.W. Topor. Making Prolog more expressive. Journal of Logic Programming, 1(3):225–240, 1984.

BIBLIOGRAPHY

223

[Mac77]

A.K. Mackworth. Consistency in networks of relations. Artificial Intelligence, 8:99–118, 1977.

[Mes96]

F. Mesnard. Inferring left-terminating classes of queries for constraint logic programs. In Proc. IJCSLP96, pages 7–21, Bonn, Germany, 1996. MITPress.

[MH86]

R. Mohr and T.C. Henderson. Arc and path consistency revisited. Artificial Inteligence, 28:225–233, 1986.

[MKS96]

J.C. Martin, A. King, and P. Soper. Typed norms for typed logic programs. In J. Gallagher, editor, Proceedings of LOPSTR’96: Logic Program Synthesis and Transformation, number 1207 in LNCS, pages 143–153, Stockholm, august 1996. Springer-Verlag.

[MN89]

U. Martin and T. Nipkow. Boolean unification- the story so far. Journal of Symbolic Computation. Unification: Part 1, 7(3,4):275–293, march/april 1989.

[MS98]

K. Marriott and P.J. Stuckey. Programming with Constraints: An introduction. The MIT Press, 1998.

[Mur76]

K. Murthy. Linear and Combinatorial Programming. John Wiley and sons, 1976.

[Nad89]

B.A. Nadel. Constraint satisfaction algorithms. Computational Intelligence, 5(4):188–224, November 1989.

[NKT89]

G.L. Nemhauser, A.H.G Rinnooy Kan, and M.J. Todd, editors. Optimizations, volume 1 of Handbooks in operations research and management science. Elsevier Science Publishers B.V., 1989.

[Pal97]

G. Palmers. Het verrekenen van internationale transacties van waardepapieren in clp. Master’s thesis, K.U.Leuven departement computerwetenschappen, 1997.

[Pel98]

N. Peltier. A new method for automated finite model building exploiting failures and symmetries. Journal of Logic and Computation, 8(4):511–543, 1998.

[Pl¨u90]

L. Pl¨umer. Termination proofs for logic programs. Number 446 in LNAI. Springer-Verlag, 1990.

[PP79]

L.M. Pereira and A. Porto. Intelligent backtracking and sidetracking in horn clause programs. Technical Report reserach report CINUL 2/79, Universitade Nova de Lisboa, 1979.

224

BIBLIOGRAPHY

[Pro93]

P. Prosser. Hybrid algorithms for the constraint satisfaction problem. Computational Intelligence, 9(3):268–299, August 1993.

´ [R94]

J. R´egin. A filtering algorithm for constraints of difference in csps. In Proceedings of the twelfth National Conference on Artifical Intelligence, pages 362–367, 1994.

[RB86]

W. Rosiers and M. Bruynooghe. Emperical study of some constraint satisfaction algorithms. Technical Report report CW 50, K.U.Leuven, departement of computer science, 1986.

[Rob65]

A. Robinson. A machine-oriented logic based on the resolution principle. Journal of the ACM, 12(1):23–41, 1965.

[Rob71]

A. Robinson. Computational logic: The unification computation. Machine Intelligence, 6:63–72, 1971.

[RV96]

P. Refalo and P. Van Hentenryck. CLP(Rlin ) revised. In Michael Maher, editor, Proceedings of the 1996 Joint International Conference and Symposium on Logic Programming, pages 22–36, 1996.

[RWH98]

R. Rodoˇsek, M.G. Wallace, and M.T. Hajian. A new approach to integrating mixed integer programming with constraint logic programming. Annals of Operational Research. Recent Advances in Combinatorial Optimization, 1998. to appear.

[SB96]

G. Seghers and T. Baeyens. Het oplossen van een combinatorisch onderhoudsprobleem voor electriciteitscentrales, uitgewerkt in OLP-FOL en CLP . Master’s thesis, Department of Computer Science, K.U.Leuven, 1996.

[Sch86]

A. Schrijver. Theory of linear and integer programming. John Wiley and sons, 1986.

[SHC94]

Z. Somogyi, F. Henderson, and T. Conway. The implemenatation of mercury: an efficient declarative logic programming language. In Proceedings of the ILPS’94 Postconference Workshop on Implementation Techniques for Logic Programming Languages, 1994.

[Sla94]

J. Slaney. FINDER: Finite domain enumerator system description. Technical Report TR-ARP-2-94, Centre for Information Science Research, Australian National University, Australia, 1994. Also in Proc. CADE-12.

[Sla97]

J. Slaney. FINDER - Finite domain enumerator - version 3.0 - notes and guide. Technical report, Centre for Information Science Research, Australian National University, Australia, 1997.

BIBLIOGRAPHY

225

[SS97]

C.B. Suttner and G. Sutcliffe. The TPTP problem library, version 2.1.0, 15/12/1997. Technical report, James Cook University, Australia, 1997. 97/08.

[SSS97]

C. Speirs, Z. Somogyi, and H. Sondergaard. Termination Analysis for Mercury. In P. Van Henteryck, editor, Proceedings of the Fourth International Symposium on Static Analysis, number 1302 in LNCS, pages 157–171, Paris, France, september 1997. Springer.

[SSW94]

K. Sagonas, T. Swift, and D.S. Warren. XSB as an efficient deductive databse engine. In Proc. of SIGMOD 1994 Conf. ACM. Acm Press, 1994.

[Swe97]

Swedish Institute of Computer Science. SICSTUS Prolog User’s Manual, november 1997.

[UV88]

J.D. Ullman and A. Van Gelder. Efficient tests for top-down termination of logical rules. Journal ACM, 35(2):345–373, april 1988.

[Van89]

P. Van Hentenryck. Constraint Satisfaction in Logic Programming. The MIT press, 1989.

[Van94]

H. Vandecasteele. On backtracking in finite domain problems. In Proceedings of the Fifth Benelux Workshop on Logic Programming, September 1994.

[Van96]

H. Vandecasteele. Compiling Prolog to ANSI C. In Y. Deville, editor, Proceedings of the Eight Benelux Workshop on Logic Programming, 1996.

[Van97]

S. Van Assche. Optimalisatie van een beursbeleggingsportefeuille met behulp van constraint logic programming. Master’s thesis, K.U.Leuven, departement computerwetenschappen, 1997.

[Van98]

H. Vandecasteele. Modelling combinatorial problems for CLP(FD+R). In K.R. Apt and F. van Raamsdonk, editors, Proceedings of the Tenth Benelux Workshop on Logic Programming, 1998.

[VD91]

P. Van Hentenryck and Y. Deville. The cardinality operator: A new logical connective for constraint logic programming. In proceedings of ICLP, pages 745–759, 1991.

[VD93]

H. Vandecasteele and D. De Schreye. Solving finite domain problems with efficient pruning and smart enumeration. In E. Laenens, editor, proceedings of the Fifth Benelux Workshop on Logic Programming, September 1993.

[VD94]

H. Vandecasteele and D. De Schreye. Implementing a finite-domain CLPlanguage on top of Prolog : a transformational approach. In Frank Pfenning, editor, Proceedings of Logic Programming and Automated Reasoning, number 822 in Lecture Notes in Artificial Intelligence, pages 84–98. SpringerVerlag, 1994.

226

BIBLIOGRAPHY

[VDCM92] P. Van Hentenryck, Y. Deville, and T. Choh-Man. A generic arc-consistency algorithm and its specializations. Artificial Intelligence, 57:291–321, 1992. [VDV96]

H. Vandecasteele, B. Demoen, and J. Van Der Auwera. The use of mercury for the implementation of a finite domain solver. In Proceedings of JICSLP’96 Post-conference Workshop on Parallelism and Implementation Technologies for (Constraint) Logic Languages, 1996.

[VDV99]

H. Vandecasteele, B. Demoen, and J. Van Der Auwera. The use of Mercury for the implementation of a finite domain solver. In I. de Castro Dutra, M. Carro, V. Santos Costa, G. Gupta, E. Pontellia, and Silva F, editors, Nova Science Special Volume on Parallelism and Implementation of Logic and Constraint Logic Programming. Nova Science Publishers Inc, 1999.

[Ver92]

K. Verschaetse. Static Termination Analysis for Definite Horn Clause Programs. PhD thesis, Dept. Computer Science, K.U.Leuven, 1992. Accessible via http://www.cs.kuleuven.ac.be/˜lpai.

[Ver95]

K. Verheyden. Opzoek naar modelleringstecnieken voor een planningsproblemen in eindige domein clp-talen aan de hand van een voorbeeld. Master’s thesis, K.U.Leuven departement computerwetenschappen, 1995.

[VLD97]

P. Van Hentenryck, M. Laurent, and Y. Deville. Numerica: a modeling language for global optimization. MIT Press Cambridge(Mass.), 1997.

[VSD91]

P. Van Hentenryck, V. Saraswat, and Y. Deville. Constraint processing in cc(fd). draft, 1991.

[VSD92]

P. Van Hentenryck, V. Saraswat, and Y. Deville. Constraint processing in cc(FD). Brown University, 1992.

[VSD93]

P. Van Hentenryck, V. Saraswat, and Y. Deville. Design, implementation and evaluation of the constraint language cc(fd). Technical report, Brown University, 1993.

[Wal72]

D. Waltz. Generating semantic descriptions from drawing of scenes with shadows. Technical report AI271, MIT, November 1972.

[War83]

D.H.D. Warren. An abstract Prolog instruction set. Technical report 309, SRI International, Menlo Park, 1983.

[Zio74]

S. Zionts. Linear and Integer Programming. Prentice-Hall, inc., 1974.

[ZZ95]

J. Zhang and H. Zhang. Sem: a system for enumerating models. In Proc. IJAI-95, vol 1, pages 298–303, Cambridge MA, 1995. Morgan Kaufmann.

i

Constraint logische programmeertalen: toepassingen en implementatie Nederlandse Samenvatting Henk Vandecasteele

Inhoudsopgave 1

Constraint logisch programmeertalen. 1.1 Eindige domein constraint logisch programmeren. . . . . . . . . . . . 1.2 CLP gebaseerd op lineaire programmatie. . . . . . . . . . . . . . . . 1.3 Andere CLP-varianten. . . . . . . . . . . . . . . . . . . . . . . . . .

ii iv vi ix

2

Het CLP systeem ROPE.

x

3

Bewijzen dat een vraagstelling nooit kan slagen.

xi

4

Terminatie analyse met behulp van CLP.

xiv

5

Planning van onderhoudsbeurten voor generatoren.

xvi

6

Andere toepassingen. 6.1 Optimalisatie van een beleggingsportefeuille. . . . . . . . . . . . . . 6.2 Het verdelen van thesisstudenten onder onderzoeksgroepen. . . . . .

xix xix xx

7

Besluit en toekomstplannen.

xxi

NEDERLANDSE SAMENVATTING

ii

1

Constraint logisch programmeertalen.

Heel wat problemen uit de realiteit kan men tot in het detail omschrijven in een logische specificatie. Gebruik makend van een klassieke imperatieve taal is men dan nog ver verwijderd van een werkend programma. Logisch programmeren is een tak in het onderzoek naar declaratieve talen die een logische specificatie uitvoerbaar wil maken [Kow79b, Kow79a]. Het voordeel hiervan is dat de gebruiker zich kan concentreren op het correct beschrijven van het probleem en zich geen zorgen moet maken over de details van de uitvoering. Een belangrijk resultaat in het onderzoek naar logisch programmeren is de programmeertaal PROLOG [Gro75]. Het PROLOG-uitvoeringsmodel is gebaseerd op unificatie [Rob71] en resolutie [Rob65]. Een belangrijke doorbraak in de implementatie van PROLOG-omgevingen is de WAM (Warren Abstract Machine) [War83], een uitvoeringsmodel met een intermediaire machine dat compilatie voor PROLOG mogelijk maakt. Logisch programmeren leent zich erg goed voor het beschrijven van problemen rond planning en scheduling. Voorbeelden hiervan zijn uurroosters, routeplanning, financi¨ele modellen, ... . Daartegenover faalt het uitvoeringsmodel van PROLOG in grote mate voor het (effici¨ent) uitvoeren van een dergelijke specificatie. De enige manier om dit probleem op te lossen met PROLOG, is de specificatie te herschrijven sterk rekening houdend met het uitvoeringsmodel van PROLOG. Dit komt dan neer op het schrijven van een oplosser in PROLOG. Dit is echter totaal in tegenstelling tot de doelstelling van een declaratieve programmeertaal. In de praktijk bestaan er echter heel wat goede algoritmes voor het oplossen van combinatorisch problemen zoals planning en scheduling. Erg veel van deze technieken werden ontwikkeld in het onderzoeksdomein van operationeel onderzoek [FGa, FGb]. Systemen uit die wereld zijn vaak zeer krachtig maar meestal nogal moeilijk te gebruiken. Soms is men verplicht om te programmeren of om het probleem om te vormen tot een wereldvreemd mathematisch model. Constraint logisch programmeren wil hier een oplossing bieden door dergelijke krachtige oplossers, bijvoorbeeld uit de wereld van operationeel onderzoek, te combineren met de specificatiekracht van logisch programmeren [JL87]. De reden waarom PROLOG faalt bij het uitvoeren van specificaties van scheduling en andere combinatorische problemen ligt in het hart van het systeem: unificatie en resolutie. Alhoewel reeds vanaf het begin primitieven voorzien werden voor het behandelen van numerische vergelijkingen, moeten tijdens uitvoering alle variabelen in een dergelijke expressie volledig gekend zijn. We kunnen dit het best illustreren aan de hand van een voorbeeld 1 : Voorbeeld 1.1 Konijnen + Fazanten = 9 (1) Konijnen*4 + Fazanten*2 = 24 (2) Om een dergelijk probleem te laten oplossen door een PROLOG-systeem met een declaratieve beschrijving werkt men als volgt: 1 Een aantal konijnen en fazanten spelen in het gras, er zijn duidelijk 9 dieren en samen hebben ze 24 pootjes. Hoeveel konijnen en fazanten zijn er?

1. CONSTRAINT LOGISCH PROGRAMMEERTALEN.

iii

probleem(Konijnen, Fazanten):getal(Konijnen), getal(Fazanten), Dieren is Konijnen + Fazanten, Poten is Konijnen*4 + Fazanten *2, Dieren = 9, Poten = 24. getal(0). getal(1). getal(2). getal(3). getal(4). getal(5). getal(6). getal(7). getal(8). getal(9). Wanneer men het bovenstaande programma laat werken onder PROLOG dan krijgt men een erg ineffici¨ent resultaat. Het programma zal een zoekruimte doorzoeken door alle mogelijke combinaties te maken met mogelijke toekenningen voor de twee variabelen Konijnen en Fazanten om alle oplossingen te vinden. Gelijk welk numerisch pakket kan dit probleem zonder te zoeken reduceren tot de oplossing! Dit fenomeen wordt bij constraint logisch programmeren opgelost door unificatie te vervangen door een oplosser voor beperkingen. Wanneer het CLP-uitvoeringsmechanisme een beperking ontdekt, wordt deze beperking behandeld door die oplosser. Het CLP-programma voor het bovenstaande probleem kun men dan schrijven als: Voorbeeld 1.2 probleem(Konijnen, Fazanten):Konijnen + Fazanten = 9, Konijnen*4 + Fazanten *2 = 24. In de praktijk bestaat er een hele waaier aan verschillende oplossers voor beperkingen, waarbij elke oplosser goed is in het oplossen van een bepaald type probleem. Voor elk van die oplossers kan men in principe een constraint logische programmmeertaal maken, waarbij men zo’n specifieke oplosser inbouwt in het uitvoeringsmechanisme. Elk van die varianten op het CLP-thema verwerft dan de kracht van de ingebouwde oplosser maar erft zijn declaratieve expressiemogelijkheden van logisch programmeren. Men kan een aantal stromingen herkennen in de resulterende waaier van CLP-varianten:

 

Constraint logisch programmeren gebaseerd op consistentie-technieken en propagatie. De variabelen in een dergelijk systeem nemen waarden uit een eindig domein van waarden. Een dergelijk systeem noemt men een eindige domein CLP-oplosser. Meer bekend is de Engelstalige naam Finite Domain CLP. CLP-systemen die lineaire beperkingen oplossen met gaussiaanse eliminatie en het simplex algoritme. In dit geval kunnen de variabelen betrokken in de beperkingen waarden aannemen uit de rationale getallen of de vlottende komma getallen.

NEDERLANDSE SAMENVATTING

iv

 

Oplossers werkend met booleaanse variabelen die dan opgelost worden met specifieke technieken voor booleaanse beperkingen. Algebra¨ısche CLP-systemen voor het oplossen van niet-lineaire vergelijkingen over de re¨ele getallen.

We zullen nu enkele van deze CLP-varianten meer in detail beschrijven.

1.1 Eindige domein constraint logisch programmeren. Bij eindige domein CLP, zoals hierboven reeds vermeld, nemen variabelen betrokken in beperkingen waarden aan uit een eindige verzameling van waarden. In het voorbeeld 1.2 nemen beide variabelen waarden aan uit de natuurlijke getallen tussen 0 en 9 (0 en 9 inbegrepen). In een dergelijk systeem wordt er bij elke variabele een domein bijgehouden met nog mogelijke waarden. Het basisalgoritme probeert dan zoveel mogelijk waarden uit deze domeinen te schrappen door te bewijzen dat deze waarden inconsistent zijn met de beperkingen en de waarden uit de domeinen van de andere variabelen. Specifiek voor het voorbeeld 1.2 zal de tweede beperking onmiddellijk aanleiding geven tot het schrappen van waarden. Namelijk voor de waarde 9 in het domein van Konijnen kan men geen waarde vinden in het domein van Fazanten zodat Koni jnen  4 + Fazanten  2 = 24 voldaan is. Bijgevolg kan men de waarde 9 uit het domein van Konijnen schrappen zonder dat er oplossingen voor het probleem verloren gaan. Wanneer dergelijke inconsistente waarden geschrapt worden kan dit aanleiding geven voor het schrappen van andere waarden uit andere domeinen. Zo kan men als gevolg van het schrappen van de waarde 9 uit het domein van de variabele Konijnen ook de waarde 0 schrappen uit het domein van de variabele Fazanten. Die waarde 0 is nu namelijk inconsistent met de eerste beperking Koni jnen + Fazanten = 9. Dit fenomeen waarbij men waarden kan schrappen uit een domein, als gevolg van het schrappen van andere waarden uit het domein van een andere variabele, noemt men propagatie. Men laat deze propagatie doorgaan tot men geen enkele waarde meer kan verwijderen uit een domein. Dit proces noemt men ook vaste-punts-iteratie. Als men propagatie laat doorgaan op ons klein voorbeeldje zal na een aantal stappen een oplossing berekend worden, namelijk 3 konijnen en 6 fazanten. Als er tijdens een dergelijk schrappen van inconsistente waarden alle waarden uit een domein geschrapt worden betekent dit eenvoudig dat er geen oplossing bestaat voor het omschreven probleem. Daartegenover als alle domeinen van variabelen uit een probleem singletons geworden zijn en de propagatie is be¨eindigd dan hebben we een oplossing gevonden. Het schrappen van inconsistente waarden samen met propagatie is echter in het algemeen niet voldoende voor het oplossen van een probleem. Eindige domein oplossers kunnen inconsistente waarden verwijderen maar meestal niet allemaal. Hiervoor zijn drie redenenen.



Eerst en vooral is het vaak te rekenintensief om, gegeven een beperking, alle inconsistente waarden te verwijderen uit de domeinen van de betrokken variabelen. Voor de meeste types beperkingen waarbij n variabelen betrokken zijn en waar elke

1. CONSTRAINT LOGISCH PROGRAMMEERTALEN.

v

variabele in de orde van m waarden in zijn domein heeft heb je hiervoor in de orde van nm stappen voor nodig. Dit wordt meestal vermeden door een benaderend algoritme dat lineair is in het aantal betrokken variabelen (n). Dit heeft natuurlijk als gevolg dat soms inconsistente waarden niet verwijderd worden.



Een tweede reden is het lokale werkingsprincipe van een eindige domein CLPoplosser. Zo zal een waarde alleen geschrapt worden als het inconsistent is met 1 beperking, niet indien je meer dan 1 beperking nodig hebt om de inconsistentie te bewijzen. Een voorbeeld is Voorbeeld 1.3 X1 6= X2, X1 6= X3 en X2 6= X3 En het domein van X1 en X2 is f1,2g en dat van X3 is f1,2,3g. Een eindige domein oplosser zal nooit ontdekken dat 1 en 2 uit het domein van X3 geschrapt kunnen worden2.



De derde reden, waarom je met het schrappen van inconsistente waarden en propagatie alleen meestal geen oplossing kunt berekenen, is dat er meerdere oplossingen zijn. Als twee waarden uit een domein elk tot een oplossing behoren, zul je geen enkele van de twee waarden kunnen schrappen om zo tot e´ e´ n oplossing te komen.

Om dit probleem op te lossen gebruikt men enumeratie. Bij enumeratie zal men proberen een oplossing te vinden door voor de variabelen die nog meer dan 1 waarde in hun domein hebben een waarde te kiezen. Indien er geen oplossing bestaat met die keuze neemt het systeem een andere waarde en gaat zo verder. Op die manier cre e¨ ert men een zoekboom. De knopen in de zoekboom zijn de variabelen die ge¨enumereerd worden, de takken de verschillende waarden in het domein van de variabele die toegekend kunnen worden. Belangrijk is dat na iedere toekenning terug inconsistente waarden uit het domein van andere variabelen geschrapt worden. Een eindig domein systeem bevat steeds een aantal primitieven, die je kunt gebruiken om een probleem uit te drukken. De mogelijkheden en de effici¨entie van een eindig domein CLP-systeem hangt sterk af van welke primitieven het bevat en hoe die precies gebruikt worden om inconsistente waarden te schrappen. Frequent terugkerende primitieven zijn:



Lineaire en niet-lineaire uitdrukkingen, inclusief “verschillend van”. Voorbeelden hiervan zijn: Voorbeeld 1.4 Koni jnen  4 + Fazanten  2 = 24, Koningin11 Koningin14 3,

2 Er zijn echter systemen waar je de beperkingen uit voorbeeld 1.3 als 1 beperking kunt uitdrukken. Dergelijke systemen kunnen dan wel de waarden 1 en 2 uit het domein van X3 schrappen.

NEDERLANDSE SAMENVATTING

vi (Pri js VariabeleKost )  Kwantiteit Winst > 0



VasteKost = Winst,

Vormen van logische formules. Voorbeeld 1.5 card (3; C1 ; C2 ; C3 ; C4 ; C5 ): Minstens 3 van de meegegeven beperkingen C1 , C2 , C3 , C4 en C5 moeten waar zijn.



Het koppelen van een booleaanse variabele aan de waarheidswaarde van een uitdrukking. Voorbeeld 1.6 Bi j #, Starti + Duuri < Start j : De booleaanse variabele Bi j is equivalent met de beperking aan de rechterkant.



Indexicals: een soort deelverzameling van uitdrukkingen. Voorbeeld 1.7 X in min(Y )::max(Z ): De waarde van X moet tussen de kleinste waarde in het domein van Y en de grootste waarde in het domein van Z liggen.

Ook hoe deze beperkingen gebruikt worden om inconsistente waarde te schrappen in de domeinen van de variabelen is erg belangrijk. In de volledige tekst wordt aan de hand van een formalisatie een raamwerk opgebouwd, waarin de huidige bestaande systemen veralgemeend worden.

1.2 CLP gebaseerd op lineaire programmatie. Een populaire techniek binnen operationeel onderzoek is lineaire programmatie [NKT89, HL90, Mur76, GCL92, Sch86, FGa, Zio74]. Meer specifiek de simplex-methode, in al zijn vari¨eteiten, is een veel gebruikte methode voor numerische berekeningen [NKT89]. De basisprincipes van de simplex-methode zijn oud, toch op zijn minst in de computerwereld, ze dateren van 1947 in het werk van G.B. Dantzig. Er zijn heel wat goede numerische pakketten beschikbaar voor het verwerken van lineaire vergelijkingen. Deze pakketten zijn zeer krachtig maar missen de grote expressiviteit van logisch programmeren [RWH98]. Zo moeten de gegevens voor een bepaald probleem vaak eerst bewerkt worden voordat lineaire beperkingen gegenereerd worden. In een aantal van deze pakketten heeft men een externe programmeertaal nodig om dit te bereiken. Ook het formaat van de lineaire vergelijkingen kan soms beperkt zijn. In vergelijking daarmee kun je in de meeste CLP-talen gebaseerd op lineaire programmatie ook niet-lineaire beperkingen en strikte ongelijkheden uitdrukken. Natuurlijk worden deze niet-lineaire uitdrukkingen uitgesteld tot ze lineair worden. Andere voordelen van CLP is de incrementaliteit en de mogelijkheid tot terugkeren op beslissingen.

1. CONSTRAINT LOGISCH PROGRAMMEERTALEN.

vii

Een implementatie van lineaire programmatie in een CLP-taal is gebaseerd op twee technieken: gaussiaanse eliminatie en het simplex-algoritme. Gaussiaanse eliminatie start met een stel vergelijkingen Ax = b. A is een matrix van m rijen en n kolommen. x is een kolommatrix met de variabelen. b een kolom matrix met constanten. De methode bestaat er dan in om de vergelijkingen Ax = b te herschijven totdat men weet of de vergelijkingen consistent zijn en om eventueel een oplossing te berekenen. De herschrijfregel laat toe om van een vergelijking een ander vergelijking, vermenigvuldigd met een factor, af te trekken. Een dergelijk operatie behoudt de oplossingen van de het stelsel vergelijkingen. Tijdens deze herschrijfoperaties probeert men de eerste m kolommen van de matrix een eenheidsmatrix te maken. Het is mogelijk dat men af en toe twee rijen of twee kolommen met elkaar moet verwisselen om dit te bereiken. Zo’n opeenvolging van herschrijvingen heeft twee mogelijke uitkomsten. Ofwel bekomt men zo’n eenheidsmatrix, dan is het systeem oplosbaar. Een mogelijke oplossing is dan:

 

de eerste m variabelen uit x gelijk aan de corresponderende waarde uit b, de andere variabelen krijgen de waarde 0.

Een andere mogelijkheid is dat een volledige rij waarden uit A nul worden. Indien de overeenkomstige waarde in de kolommatrix b niet nul is, is het stelsel onoplosbaar. In het andere geval kan die vergelijking geschrapt worden en gaat de methode verder zonder die vergelijking. Het simplex-algoritme zit iets ingewikkelder in elkaar. Hier zoekt men een oplossing voor de vergelijkingen Ax = b zodat de functie f (x) = c  x minimaal is. A is terug een matrix met n rijen en m kolommen, b een kolommatrix, c een rijmatrix, en x een kolommatrix met variabelen. Bij gaussiaanse eliminatie gaat men ervan uit dat de variabelen uit de kolommatrix x waarden aannemen in de re¨ele of rationale getallen. Bij het simplex-algoritme moeten alle waarden van die variabelen positief of nul zijn. Het simplex-algoritme bestaat uit twee fazen.

 

fase 1 In de eerste fase wordt er nagegaan of het stelsel vergelijkingen Ax = b oplosbaar is door de vergelijkingen in normaalvorm te brengen. fase 2 In de tweede fase zoekt men een de optimale oplossing van het probleem waar f (x) minimaal is, met behoud van de normaalvorm.

De normaalvorm voor het simplex-algoritme is gelijkaardig met het eindresultaat van gaussiaanse eliminatie. Definitie 1.1 Een stelsel Ax=b in het simplex-algoritme staat in normaalvorm indien:



er bestaan m kolommen in de matrix A zodat deze samen een eenheidsmatrix kunnen vormen en

NEDERLANDSE SAMENVATTING

viii



alle waarden in de kolommatrix b zijn positief of nul.

Aangezien in de normaalvorm geen enkele waarde in b negatief mag zijn, kan men in de eerste fase geen gaussiaanse eliminatie gebruiken. In de meeste systemen maakt men gebruik op een speciale manier van fase twee om het algoritme voor fase e´ e´ n te implementeren. We behandelen eerst fase twee. Fase twee start met een stel vergelijkingen in normaalvorm. De variabelen die overeenkomen met de kolommen van de eenheidsmatrix noemt men de basisvariabelen. De andere variabelen noemt men de niet-basisvariabelen. Aangezien alle waarden uit b dan positief zijn heeft de vergelijking Ax=b een oplossing: namelijk de niet-basis variabelen de waarde nul, de basis-variabelen de corresponderende waarde uit de de kolommatrix b. Zo’n oplossing noemt men een basisoplossing. Het simplex-algoritme zal deze normaalvorm behouden. Tijdens iedere iteratie wordt er een variabele geselecteerd uit de verzameling basisvariabelen die toegevoegd wordt aan de niet-basisvariabelen, een niet-basisvariabele wordt dan een basisvariabele. De matrix wordt dan opnieuw in normaalvorm gebracht door rijen van elkaar af te trekken zoals in gaussiaanse eliminatie. Die twee te wisselen variabelen worden zo gekozen zodat:

 

de functie f (x) niet groter wordt voor de basisoplossing na de wissel, alle waarden uit de kolom variabele b blijven niet-negatief.

De basis van de methode is een stelling dat zegt dat als f (x) niet minimaal is er steeds zo’n wissel bestaat. Anders gezegd als er geen wissel bestaat is f (x) minimaal en hebben we de optimale oplossing. Voor fase e´ e´ n bestaan er verschillende varianten. De eenvoudigste variant telt bij iedere vergelijking in Ax = b een artifici¨ele variabele op. Indien een waarde uit de kolomvector b negatief is, wordt die vergelijking eerst met 1 vermenigvuldigd. Deze nieuwe vergelijkingen staan dan onmiddellijk in normaalvorm, waarbij alle artifici¨ele variabelen samen de basis vormen. Om dan een echte basisoplossing te berekenen start men fase twee met als optimalisatiefunctie de som van alle artifici¨ele variabelen. Indien bij het optimum deze functie nul is bestaat er een oplossing, anders niet. En wat essentieel is, de vergelijkingen staan meteen in normaalvorm. De methodes hierboven besproken werken voor re¨ele en rationale getallen. Vaak wil men een probleem met lineaire beperkingen oplossen over de natuurlijke of gehele getallen. De methode die hiervoor gebruikt wordt lijkt een beetje op enumeratie bij eindige domein CLP. Nadat men een oplossing gevonden heeft over de re¨ele of rationale getallen kijkt men na welke variabelen een niet gehele waarde aannemen. Voor die variabele gaat men terug een keuze maken:

 

ofwel is die groter dan de huidige waarde afgerond naar boven, ofwel kleiner dan de huidige waarde afgerond naar beneden.

Voor beide mogelijkheden wordt de optimale oplossing berekend, de beste van de beide is de goede oplossing. Men kan dit optimaliseren door te eisen dat eenmaal men een

1. CONSTRAINT LOGISCH PROGRAMMEERTALEN.

ix

gehele oplossing gevonden heeft de optimalisatiefunctie kleiner moet zijn dan in die reeds gevonden oplossing. Zo verkleint men de zoekruimte. Samen met de optimalisatie noemt men de bovenstaande methode branch and bound. Deze beide methoden, gaussiaanse eliminatie en het simplex-algoritme, op een robuuste manier inbouwen in een CLP-omgeving is niet zo eenvoudig. Er wordt namelijk veel verwacht van een dergelijke combinatie:







1.3

correctheid Een bekend probleem binnen lineaire programmatie is stabiliteit. Indien voor de co¨effici¨enten uit de matrix A vlottende komma-getallen gebruikt worden, kunnen er gemakkelijk na veelvuldige bewerkingen op de rijen grote rekenfouten ontstaan. Dit kan voor een deel voorkomen worden door een goede keuze van welke rijen men van elkaar aftrekt. Ook de vorm van de vergelijkingen op voorhand is belangrijk. Bij lineaire programmatie is het vaak de verantwoordelijkheid van de gebruiker om op voorhand dit na te kijken. Dit wil je vermijden in CLP zodat de gebruiker op een hoger niveau kan werken. Dit wordt vaak opgelost door te werken met rationale getallen, waar geen fouten mee gebeuren. Dit heeft dan terug als nadeel dat na veelvuldig bewerkingen de tellers en noemers van die rationale getallen zeer groot kunnen worden. incrementeel werken en terugkeren op beslissingen Een belangrijke eigenschap van CLP-systemen is incrementaliteit en de mogelijkheid tot terugkeren op beslissingen. Deze eigenschappen zijn niet aanwezig in lineaire programmatie-pakketten. Algoritmes aanpassen om deze nieuwe eigenschappen in te bouwen zonder effici¨entie te verliezen is een moeilijk probleem. Het betekent dat tijdens uitvoering op elk moment nieuwe variabelen en beperkingen kunnen toegevoegd worden. Ook moet men achteraf de laatst toegevoegde beperkingen, en variabelen, terug kunnen verwijderen. Een additioneel probleem is dat, op het moment dat een variabele ingebracht wordt, men meestal de begrenzingen op die variabele niet kent. flexibiliteit Een CLP-systeem moet veel meer soorten vergelijkingen kunnen verwerken: strikte ongelijkheden, verschillend van, variabelen zonder boven of ondergrens, negatieve variabelen, ... .

Andere CLP-varianten.

De twee bovenstaande varianten, eindige domein CLP en CLP gebaseerd op lineaire programmatie, zijn zeker en vast niet de enige CLP-varianten. Zo zijn er bijvoorbeeld CLPsystemen gebaseerd op het rekenen met intervallen [VLD97, BO97]. Dit lijkt een beetje op eindige domein CLP, er wordt namelijk aan elke variabele een interval gekoppeld, dit interval zal men tijdens de computatie proberen zo klein mogelijk te maken. Meestal zijn die intervallen over de vlottende komma-getallen. Bij het kleiner maken van die

NEDERLANDSE SAMENVATTING

x

intervallen, maakt men dan gebruik van interval-computatie. Het interessante aan deze techniek is de betrouwbaarheid. Bij het werken met vlottende komma-getallen treden er vaak fouten op. Bij interval-rekenen kan men dit opvangen door bij het berekenen van de ondergrens van een interval af te ronden naar beneden, en bij een bovengrens naar boven. CLP-systemen gebaseerd op intervallen zijn ook zeer expressief. Zowel lineaire als nietlineaire vergelijkingen zijn toegelaten. Ook gehele getallen en booleaanse waarden kan men integreren in een dergelijk systeem. Net zoals bij eindige domein technieken maakt men gebruik van enumeratie om tot een oplossing te komen. Naast interval-gebaseerde CLP-systemen bestaan er ook systemen die specifieke algoritmes voor booleaanse uitdrukkingen [BS87, MN89, Boo47] bevatten en systemen die niet-lineaire vergelijken over de re¨ele getallen op een algebra¨ısche manier [Hon92, Hon93] oplossen.

2

Het CLP systeem ROPE.

Een resultaat van het onderzoek in dit doctoraat op het vlak van primitieven en het gebruik van primitieven om inconsistent waarden te schrappen zijn de prune-declaraties. Het onderzoek startte met het idee dat “indexicals”, zoals hierboven met een kort voorbeeld aangehaald, zeer krachtige primitieven waren maar nogal moeilijk te gebruiken en daarenboven een mengeling vormen van een declaratieve beschrijving met controle van de uitvoering. Het idee is om declaratieve beperkingen automatisch om te zetten naar indexical-beperkingen, waarbij men die omzetting kan be¨ınvloeden met een annotatie die geen declaratieve betekenis heeft. Zo kan men het declaratieve aspect terug scheiden van het procedurele aspect. Ook het schrijven van foute indexicals is dan uitgesloten. Een ander resultaat in het onderzoek is op het vlak van enumeratie. Het gaat om een verduidelijking van wat de mogelijkheden zijn bij het terugkeren op een beslissing in de zoekboom. In de meeste huidige CLP-talen kun je heuristieken gebruiken om

 

de variabele te kiezen die je wil toekennen en de waarde te kiezen uit het domein die je eerst wil gebruiken

Aan dit schema werd nog een derde heuristiek toegevoegd die duidelijk maakt hoe je het systeem wil laten reageren bij het falen van een toekenning.

 

Wil men een andere waarde toekennen aan dezelfde variabele of wil men nu liever een andere variabele selecteren. Hoe interageert dit met propagatie, wil men eerst de oude waarde verwijderen uit het domein van de variabele en deze verwijdering laten propageren via de beperkingen, of wil men meteen een nieuwe waarde toekennen.

Dit hoofdstuk over de taal ROPE gaat verder met een beschrijving van implementaties van deze taal met behulp van logische programmeertalen. Een eerste experiment omvat

3. BEWIJZEN DAT EEN VRAAGSTELLING NOOIT KAN SLAGEN.

xi

het implementeren van een oplosser in PROLOG met behulp van een transformatie. Deze transformatie zet het CLP-programma om naar een PROLOG-programma. Twee aspecten zijn hier belangrijk. Iedere beperking wordt vervangen door een of meerdere oproepen van een predikaat uit een bibliotheek die de beperkingen toevoegt aan de verzameling beperkingen op dat moment. Het tweede aspect is het vertalen van de beperkingen naar een lager niveau zodat de uitvoering versneld wordt. Andere interessante aspecten in de implementatie zijn de gegevensvoorstelling met datastructuren met een open einde en het werken met een geparameteriseerde enumeratie-procedure. Een tweede experiment in het hoofdstuk over ROPE brengt verslag uit over het gebruik van Mercury als onderliggend mechanisme. Mercury is een nieuwe telg in de familie van de logische programmeertalen. Het is streng getypeerd en bevat een uitgebreid module-systeem. Het bevat ook ondersteuning voor functioneel programmeren. De doelstelling van de ontwerpers van de taal en de compiler is om een snelle logische program´ en van de meertaal te hebben, die geschikt is om grote applicaties te implementeren. E´ paradepaardjes is de strenge compiler die statisch reeds heel veel fouten kan ontdekken in het programma. Dit experiment met Mercury was op basis van e´ e´ n van de eerste versies van het systeem. E´en van de nadelen van Mercury zijn de minder flexibele datastructuren. Bijvoorbeeld, een datastructuur met een open einde is niet mogelijk in Mercury. Een belangrijke vraag was dan ook, hoe we de gegevens van een eindige domein oplosser op een goede manier konden voorstellen in Mercury. Het resultaat van het onderzoek was eenduidig: het toevoegen van destructieve toekenning, die hersteld wordt na backtracken, is essentieel voor het implementeren van een oplosser voor CLP in Mercury. De versie van Mercury op het moment van dit schrijven bevat ondertussen ondersteuning voor destructieve toekenning. Het derde stuk over implementatie in het hoofdstuk beschrijft dan ook een nieuwe versie van een eindige domein oplosser in de huidige Mercury. Testresultaten wijzen uit dat ons prototype concurrentieel is met een sterk geoptimaliseerde eindige domein CLP-bibliotheek [COC97] meegeleverd met SICStus.

3

Bewijzen dat een vraagstelling nooit kan slagen.

Toepassingen van CLP zijn een belangrijk deel in de thesis. Hoofdstuk vier en vijf bevatten twee academische toepassingen waarbij deze sectie de eerste toepassing samenvat. Het doel van geautomatiseerde eerste orde logica systemen is om, gegeven een zin A een een programma P, beslissen of P j= A. Eerste orde logica is echter semi-beslisbaar. Semi-beslisbaar betekent dan dat het beste men ooit kan bereiken is om een systeem te bouwen dat altijd in eindige tijd, ja antwoord indien P j= A. Maar, er zal altijd een tegenvoorbeeld zijn waarbij het systeem in een oneindige lus gaat voor een programma P en een vraagstelling A waar P j= A niet waar is. Bovendien, systemen die SLD-bomen diepte eerste doorzoeken, zoals PROLOG, kunnen in een oneindige lus raken zelfs indien de SLD-boom een oplossing bevat. Terminatie analyse [DD94], lus-detectie [ABK89] en tabulatie [SSW94] zijn technieken die in verband met deze problematiek kunnen gebruikt worden. Binnen terminatie-analyse gaan de meeste systemen na of gegeven een vraag-

NEDERLANDSE SAMENVATTING

xii

stelling, of een klasse van vraagstellingen, de SLD-boom eindig is voor een gegeven computatie-regel. Dergelijke systemen analyseren syntactisch de grootte van datastructuren in het programma, en proberen zo een bewijs op te zetten voor eindigheid. Aangezien het bewijzen van eindigheid van programma’s onbeslisbaar is moeten dergelijke systemen conservatief zijn en falen voor een substanti¨ele klasse van problemen. Lus-detectie systemen houden programma’s tijdens uitvoering in de gaten. Indien dergelijke systemen een lus ontdekken word de computatie afgebroken en de huidige tak van de computatie faalt. Ook deze technieken kunnen niet alle lussen vermijden, tenzij men de correctheid van de uitvoering in het gedrang brengt. Tabulatie is hiermee verwant, tijdens uitvoering gaat het systeem na of een oproep een variant is van een vroeger oproep, in de huidige of een vorige tak in de computatie. Wanneer dit het geval is zal het systeem de oplossingen uit het verleden gebruiken en de huidige oproep niet uitvoeren. Tabulatie kan gezien worden als een veralgemening en sterkere vorm van lus-detectie. Er is echter een niet te verwaarlozen klasse van interessante programma’s en bijhorende vraagstellingen die een oneindige SLD-boom hebben. Voor dergelijke programma’s is een bewijs van terminatie onmogelijk en lus-detectie nog tabulatie kunnen deze oneindige takken in de SLD-boom wegsnijden. Voorbeelden van dergelijke programma’s zijn planningsystemen. Een basistechniek voor planners is om een genereer-en-test procedure te gebruiken. Een plan wordt gegenereerd om daarna getest te worden als zijnde een goed plan, indien het plan verworpen word, genereert het systeem een nieuw plan. Deze procedure wordt herhaald tot een juist plan gevonden wordt. In de praktijk is het aantal plannen die gegeneerd kunnen worden onbeperkt. Indien een eigenschap in de logische theorie of vraagstelling alle plannen op voorhand al incorrect maakt, zal de planner vastlopen in een oneindige lus. Elk systeem, die voor dergelijke problemen soms kan ontdekken dat een vraagstelling nooit kan slagen, is van enig nut. Een mogelijk gebruik is, om parallel met de uitvoering van een vraagstelling, proberen te bewijzen dat er geen antwoord bestaat. Indien e´ e´ n van de parallelle processen een antwoord heeft wordt de andere gestopt. Een triviaal voorbeeld van het soort programma’s waarvan we het falen willen bewijzen is: Voorbeeld 3.1 even(nul). even(opvolger(X)):- oneven(X). oneven(opvolger(X)):- even(X). ?- even(X), even(opvolger(X)). Wanneer je de vraagstelling uit het voorbeeld uitvoert in een PROLOG-omgeving zal het eerste deel uit de vraagstelling steeds grotere en grotere termen opbouwen, terwijl het tweede deel van de vraagstelling steevast zal falen. In het werk dat hierrond gebeurd is, draait alles rond het vinden van een eindig model voor het programma, het model dat de vraagstelling onwaar maakt. Indien we een dergelijk model vinden dan zijn we zeker dat de vraagstelling niet waar is in het kleinste model, dat steeds een deelverzameling is in van elk model van het programma.

3. BEWIJZEN DAT EEN VRAAGSTELLING NOOIT KAN SLAGEN.

xiii

De logische theorie¨en die we willen aanpakken maken gebruik van positieve Hornclause logica. Dus, met functoren maar zonder negatie. Indien we eindige modellen willen bouwen, en de programma’s bevatten functoren, dan kunnen die modellen geen Herbrand-modellen zijn. Daarom maken we gebruik van pre-interpretaties. Het idee is om een pre-interpretatie te zoeken, zodat een model, gebaseerd op die pre-interpretatie, bestaat waarin de vraagstelling onwaar is. Er werden essentieel twee klassen van systemen ontworpen. Een eerste klasse, ontworpen en ge¨ımplementeerd door Bruynooghe maakt gebruik van abductie en tabulatie om een dergelijke pre-interpretatie af te leiden. De tweede klasse, dat deel uitmaakt van de bijdragen in dit doctoraat, bestaat uit het afleiden van een dergelijke pre-interpretatie door gebruik te maken van tabulatie en constraint logisch programmeren. Beide varianten werden beschreven in [BVdD98]. Relevant werk is dat van van Codish en Demoen [CD95], daar werkt men ook met preinterpretaties. Hiervoor worden programma’s getransformeerd naar een abstracte vorm. Gebruik makend van bottom-up computatie leveren deze getransformeerde programma’s het kleinste model, gegeven een pre-interpretatie. Het bovenstaande voorbeeld 3.1 wordt dan omgezet in Voorbeeld 3.2 even(Y):- zero(Y). even(Y):- s(X, Y), odd(X). odd(Y):- s(X, Y), even(X). Dit stukje programma kan dan bottom-up ge¨evalueerd worden met een pre-interpretatie: Voorbeeld 3.3 zero(d1 ). s(d1 , d2 ). s(d2 , d1 ). Onze methode is hiermee verwant, het grootste verschil is dat de pre-interpretatie in ons geval niet gekend is. Ook gebeurt het zoeken niet bottom-up maar top-down om op die manier gericht te kunnen zoeken en de keuzes zolang mogelijk uit te stellen. De methode van Bruynooghe gebruikt een transformatie gelijkaardig met die van Codish en Demoen. Daarna wordt de vraagstelling uitgevoerd op het resulterende programma. Op de plaatsen waar gebruik gemaakt wordt van de pre-interpretatie, en dat specifiek onderdeel van de pre-interpretatie is nog niet gekend, dan wordt die geabduceerd. Daarnaast wordt er ook gebruik gemaakt van tabulatie om lussen te vermijden. Bij de CLP-aanpak wordt de transformatie naar een abstract programma niet doorgevoerd maar wordt tijdens de uitvoering de unificatie vervangen door een zelfgedefinieerde unificatie die een dergelijke transformatie vervangt. Het programma gebaseerd op CLP wordt ook gestart met een onbekende pre-interpretatie. In tegenstelling tot de methode van Bruynooghe wordt de preinterpretatie niet gekozen tijdens het uitvoeren, maar worden er CLP-beperkingen opgezet die er voor zorgen dat een model bestaat waar de vraagstelling onwaar is. Indien na de uitvoering de verzameling CLP-beperkingen een oplossing heeft, hebben we een bewijs

xiv

NEDERLANDSE SAMENVATTING

dat de vraagstelling nooit een oplossing kan hebben. Deze methode gebruikt ook tabulatie om lussen te vermijden. In tegenstelling echter tot de methode van Bruynooghe kan men bij het vinden van een nieuwe oplossing niet gewoon controleren of die oplossing reeds in de tabel zit. Dit hangt namelijk af van de specifieke waarden van de pre-interpretatie op dat moment. In de CLP-methode hebben we die keuze echter uitgesteld. Dit wordt opgelost door een keuzepunt aan te maken. Eerst wordt veronderstelt dat de nieuwe oplossing een variant is van een oude oplossing. Er worden ook CLP-beperkingen gegenereerd die dit opleggen aan de mogelijke pre-interpretaties. Indien op een bepaald moment de CLP-beperkingen inconsistent worden dan keren we terug op die beslissing en voegen de verkregen oplossing toe aan de tabellen als een nieuwe oplossing. Er worden ook redundante CLP-beperkingen gegenereerd om te eisen dat die nieuwe oplossing verschillend is van de vorige. Het voornaamste voordeel van de CLP-variant is de robuustheid. Wijzigingen aan de testvoorbeelden, bijvoorbeeld het toevoegen van irrelevante functoren en/of argumenten heeft een beperkte invloed op de uitvoering. De CLP-methode heeft echter meer keuzepunten nodig dan de aanpak van Bruynooghe. Er bestaan verschillende varianten van het programma, e´ e´ n variant werkt met eindige domein CLP, een andere variant is een speciaal geschreven oplosser die, net zoals de methode van Bruynooghe, gebruik maakt van intelligent backtracken.

4

Terminatie analyse met behulp van CLP.

Terminatie van programma’s is een belangrijk probleem in logische programmeertalen. Zoals in de inleiding reeds vermeld, het doel van logisch programmeren is het schrijven van uitvoerbare specificaties, waarbij het aspect uitvoerbaarheid zo weinig mogelijk aandacht zou moeten krijgen. Echter, eerste orde logica is semi-beslisbaar, elke correcte en complete implementatie van een bewijsprocedure zal niet eindigen voor bepaalde voorbeelden. Terminatie analyse houdt zich bezig met het automatisch bewijzen dat programma’s eindig zijn, gegeven een bepaalde uitvoeringstrategie. Het is theoretisch onmogelijk om dit te bepalen voor alle programma’s. Maar binnen het concept van logisch programmeren is het redelijk te verwachten dat terminatie kan bewezen worden voor een substanti¨ele klasse van programma’s. Een dergelijk bewijs wordt dan opgesteld gegeven een programma, een klasse van vraagstellingen en de computatie-regel. De methode bestaat erin aan te tonen dat de SLD-boom geen oneindige takken bevat. In [DD97a] introduceren D. De Schreye en S. Decorte hoe men op een algemenere manier terminatie kan bewijzen dan daarvoor het geval was. De techniek legt beperkingen op generische formules. Als die beperkingen achteraf opgelost kunnen worden, bekomt men met die oplossing en de generische formules een bewijs van terminatie. Het werk beschreven in het doctoraat concentreert zich op het oplossen van de beperkingen op die generische formules. Dit werk is ook vervat in het te verschijnen artikel [DDV99] en wordt ook behandeld in het doctoraat van S. Decorte [Dec97]. In klassieke bewijzen voor terminatie van programma’s toont men aan dat de opeen-

4. TERMINATIE ANALYSE MET BEHULP VAN CLP.

xv

volgende toestanden tijdens uitvoering kunnen genummerd worden met dalende elementen uit een well-founded verzameling waarden. Een voorbeeld van zo een schema is het koppelen van een geheeltallige functie aan de uitvoeringstoestanden. Een terminatiebewijs bestaat dan uit het aantonen dat de functie nooit negatief kan worden en daarenboven kleiner wordt tijdens iedere stap van de uitvoering. In een instantiatie van een dergelijk schema voor logisch programmeren moeten we redeneren over doelen en regels. De functie gekoppeld aan de opeenvolgende stappen in de uitvoering noemt men een niveauafbeelding, het is een geheeltallige functie die nooit negatief kan worden. Een niveauafbeelding van een Oproep wordt genoteerd als jOproepj. De niveau-afbeelding is een functie gedefinieerd op alle mogelijk oproepen die kunnen voorkomen in een programma. In het werk hier beschreven is een niveau-afbeelding steeds gedefinieerd in functie van een norm van een aantal argumenten van de oproep. Een norm meet op een syntactische manier de grootte van een term. Het is gedefinieerd op alle mogelijke termen die kunnen onstaan tijdens de uitvoering van een programma, inclusief termen die variabelen bevatten. De notatie voor een norm van een Term is jjTermjj. En ander belangrijk concept is de interargumentrelatie. Deze relatie vertelt wat het verband is tussen de grootte van de verschillende argumenten van een oproep nadat de oproep geslaagd is. Een dergelijke relatie maakt opnieuw gebruik van de norm. Interargumentrelaties zijn van belang om bij recursieve definities het verband te leggen tussen de argumenten in het hoofd van een oproep en de recursieve oproep. Ter verduidelijking een voorbeeld: Voorbeeld 4.1 permuteer([], []). permuteer(Lijst, [HoofdjStaart]):verwijder(Hoofd, Lijst, Lijst1), permuteer(Lijst1, Staart). verwijder(Hoofd, [HoofdjStaart], Staart). verwijder(Element, [HoofdjStaart], [HoofdjStaart1]):verwijder(Element, Staart, Staart1). Indien men de eindige uitvoering wil bewijzen van het predikaat permuteer kan dit door te stellen dat het eerste argument van permuteer steeds kleiner wordt bij opeenvolgende oproepen. Hiervoor heeft men een interargumentsrelatie nodig voor het predikaat verwijder. Die relatie geeft dan weer dat, na een oproep, het derde argument kleiner is dan het tweede argument. Een ander concept is stijfheid van de niveau-afbeelding. Een niveauafbeelding is stijf indien voor gelijk welke oproep van een predikaat die kan onstaan tijdens uitvoering, de niveau-afbeelding invariant is voor gelijk welke substitutie. Eindigheid van programma’s wordt dan bewezen door het vinden van een niveau-afbeelding met een bijhorende norm en de nodige interargumentsrelaties zodat:

 

de niveau-afbeelding stijf is, alle interargumentsrelaties volgen uit de definities van de predikaten,

NEDERLANDSE SAMENVATTING

xvi



voor iedere recursieve oproep in de definitie van een predikaat de niveau-afbeelding van de recursieve oproep steeds kleiner is dan die van de originele oproep.

Het moeilijkste onderdeel in het hierboven beschreven bewijsschema is het vinden van de gepaste niveau-afbeelding, norm en interargumentsrelaties. Het nagaan van de condities is gemakkelijk te automatiseren. In het reeds bestaande werk worden in verschillende fazen van computatie dergelijke specifieke niveau-afbeeldingen, normen en interargumentsrelaties geconstrueerd. Dit zonder garantie dat dit tot een volledig bewijs zal leiden. Het hier beschreven werk bekijkt het geheel vanuit een andere hoek door, in plaats van specifieke formules voor te stellen, te werken met generische formules. Deze generische formules bevatten een aantal onbepaalde parameters. Door die parameters in te vullen verkrijgt men alle mogelijke formules die men in de vroegere methodes direct afleidde. Door het vooropstellen van dergelijke generische formules stelt men de keuze van een specifieke formules uit. De techniek bestaat er dan in om voor deze generische formules condities op stellen. Indien men voor de parameters in de generische formules waarden kan vinden zodat de condities voldaan zijn heb je een bewijs van terminatie. Zo heeft men generische formules voor de norm, niveau-afbeelding en de interargumentsrelatie. De condities worden dan afgeleid bij het eisen dat de niveau-afbeelding stijf moet zijn, de interargumentsrelaties correct en de niveau-afbeelding kleiner wordt bij recursieve oproepen. Deze condities zijn echter van een vorm waarvoor, voor zover ons bekend, geen oplosser bestaat die rechtstreeks een oplossing kan vinden uit deze condities. Een belangrijk en moeilijk onderdeel in dit onderzoek is dan ook om toch uit deze condities correcte parameters af te leiden. Dit werd opgelost door in een aantal stappen de condities te herschrijven tot een stel beperkingen die oplosbaar zijn door een eindige domein constraint logische programmeertaal. De bijdrage van dit doctoraat ligt bij deze omzetting en het uiteindelijke vinden van een oplossing [DDV99]. Dit gebeurde in samenwerking met Stefaan Decorte en Danny De Schreye.

5

Planning van onderhoudsbeurten voor generatoren.

Een eerste van een aantal toepassingen uit de realiteit is het plannen van onderhoudsbeurten voor elektrische generatoren in Belgi¨e. Een Belgische electricteitsproducent bezit een aantal fabrieken waar elektrische energie opgewekt wordt georganiseerd in regio’s. Ieder van deze fabrieken bevat een aantal generatoren. Deze generatoren krijgen tijdens het jaar meestal e´ e´ n of twee preventieve onderhoudsbeurten. Dit om de levensduur te verlengen en de betrouwbaarheid te verhogen. Ieder van die onderhoudsbeurten hebben een gekende duur, uitgedrukt in weken. Deze onderhoudsbeurten plannen over het jaar is een algemeen probleem voor elektriciteitsmaatschappijen. Het basisprobleem is dat elektriciteit niet gestockeerd kan worden en tijdens een onderhoudsbeurt genereert een generator geen energie met als gevolg een mogelijk tekort aan energie. Zo moeten die onderhoudsbeurten gepland worden zodat er op ieder moment voldoende energie beschikbaar is. Er moet zelfs rekening gehouden worden met het feit dat werkende generatoren soms uitvallen. Wat wel gekend is, is het verwachte verbruik voor ieder week van het jaar. Indien

5. PLANNING VAN ONDERHOUDSBEURTEN VOOR GENERATOREN.

xvii

met dit verwacht gebruik en de geplande onderhoudsbeurten van de totale beschikbare capaciteit aftrekt bekomt men de reserve. In een ideaal schema wordt die reserve egaal verspreid over de verschillende weken om zo het tekort aan energie minimaal te houden. Bij een dergelijk planning moet er natuurlijk rekening gehouden worden met een aantal voorwaarden.

     

Sommige periodes kunnen verboden zijn voor het plannen van onderhoudsbeurten bij bepaalde generatoren of fabrieken. Voor elke fabriek is er een bovengrens op het aantal generatoren in onderhoud, dit omwille van het aantal beschikbare personeel. Sommige onderhoudsbeurten liggen op voorhand vast. Sommige onderhoudsbeurten gebeuren simultaan, omwille van de beschikbare machines en eventueel personeel. Andere onderhoudsbeurten gebeuren juist wel tegelijk omdat gespecialiseerde firma’s uit het buitenland die onderhoudsbeurten uitvoeren. Voor elke regio is er een bovengrens op de totale capaciteit van de generatoren tegelijk in onderhoud. Anders zorgt overbelasting van het elektriciteitsnetwerk tussen de regio’s voor problemen.

Het beschreven probleem werd in de loop van het doctoraat op verschillende manieren aangepakt. Het begon allemaal met een laatstejaarsthesis [SB96] waarbij het probleem zowel uitgewerkt werd met CLP als beschreven met OLP-FOL. OLP-FOL is een specificatietaal gebaseerd op eerste orde logica uitgebreid met open logisch programmeren. Binnen die eerste poging werd als CLP-systeem ROPE [VD94] gebruikt, onze eigen oplosser. Later werden ook andere systemen gebruikt en vergeleken. Onder meer de eindige domein bibliotheek [COC97] meegeleverd met SICStus, ECLiPSe en het commercieel pakket CHIP. In een later fase werden ook andere modellen uitgewerkt gebruik makend van een integratie van lineaire programmatie in een eindige domein CLP-bibliotheek in ECLiPSe. In een eerste stap werd het probleem met pure CLP aangepakt, namelijk met onze eigen CLP-oplosser ROPE. Het probleem modelleren, het kiezen van variabelen en corresponderende domeinen en beperkingen, was een niet-triviale opgave. Deze te kiezen eindige domein variabelen moesten zowel toelaten om op een gemakkelijke manier de beperkingen van het probleem uit te drukken als om een eenvoudige optimalisatiefunctie aan te maken. Voor elke te plannen onderhoudsbeurt werd een variabele voorzien als startdatum van die onderhoudsbeurt. Het domein van deze variabele bevat de waarden 1 tot en met 52, de weken in het jaar. Behalve wanneer men werkt met CHIP is deze verzameling eindige domein variabelen niet voldoende voor het uitdrukken van alle beperkingen en de optimalisatiefunctie. In het ROPE-systeem, maar ook voor SICStus en ECLiPSe, zijn er extra variabelen nodig. Dit voor het uitdrukken van de disjuncties en de

xviii

NEDERLANDSE SAMENVATTING

optimalisatiefunctie. Zo wordt er voor ieder week van het jaar en voor iedere te plannen onderhoud een booleaanse variabele voorzien. Deze booleaanse variabele is nul als de onderhoudbeurt bezig is, anders heeft ze de waarde e´ e´ n. Zoals reeds opgemerkt kan men deze booleaanse variabelen vermijden door gebruik te maken van de globale cumulativebeperking in CHIP. Met de bovenstaande systemen zijn we er in geslaagd om zeer goede oplossingen te berekenen voor het op te lossen probleem. We zijn er echter niet in geslaagd om de optimale oplossing te berekenen, daarvoor is het probleem te groot. De beste oplossing berekend met SICStus en CHIP zijn van dezelfde kwaliteit, waarbij CHIP deze oplossingen sneller kan berekenen. In een een tweede experiment werd het probleem omschreven in de specificatie-taal OLP-FOL, om daarna uitgevoerd te worden met een implementatie van SLDNFA [DD92]. Bij deze uitvoering werd de specificatie automatisch omgezet naar variabelen en beperkingen geschikt om opgelost te worden in CLP. Alhoewel het uitvoeren zeer lang geduurd heeft (24 uur) en de uiteindelijk oplossing door CLP berekend van mindere kwaliteit was, beschouwen we dit als een zeer geslaagd experiment. Het uitvoerbaar maken van een specificatie van een scheduling probleem op zich is reeds een belangrijke verwezelijking. In een derde aflevering van het verhaal werd een poging gedaan om de uitvoering van het eindige domein CLP-programma te verbeteren door gebruik te maken van lineaire programmatie in combinatie met eindige domein CLP. Het idee van een dergelijke combinatie van eindige domein CLP en lineaire programmatie lijkt interessant. Beide technieken worden namelijk vaak gebruikt om problemen uit dezelfde klasse op te lossen. Beide technieken werken echter op een totaal verschillende manier. Het is dus aannemelijk dat een combinatie van beide oplossers een sterker systeem kunnen opleveren. Mensen uit IC-parc in Londen hebben een dergelijk combinatie gemaakt in een bibliotheek voor ECLiPSe. Een speciale versie van de eindige domein CLP-bibliotheek werd gekoppeld aan CPLEX, een commercieel lineair programmatiepakket, Alle lineaire beperkingen uit het probleem worden ook doorgegeven aan de CPLEX-oplosser. Een eerste experiment waarbij uitgegaan werd van het reeds gebruikte model, hierboven beschreven, voor ECLiPSe was echter teleurstellend. Het doorspelen van de lineaire beperkingen naar CPLEX toe bleek alleen maar moeite te kosten die niets opleverde. Daar kwam verandering in toen het gebruikte model volledig overhoop gegooid werd. In dit nieuwe model werden enkel booleaanse variabelen aangemaakt. Voor iedere week en voor iedere onderhoudsbeurt e´ e´ n variabele. Elk van deze variabele drukt uit of de onderhoudsbeurt start in die week. Dus slecht e´ e´ n booleaanse variabele van alle 52 waarden bevat in een oplossing de waarde e´ e´ n, alle andere variabelen zijn dan nul. Een dergelijk model zou veel interessantere lineaire beperkingen opleveren. In de praktijk bleek dit niet onmiddellijk te kloppen, ook dit model leverde geen optimale oplossing. Merkwaardig genoeg kon er wel een optimale oplossing gevonden worden voor een verzwakt probleem. Een dergelijk aanpak loont dus indien men interesse heeft in een bovengrens voor de optimalisatiefunctie. De verdienste van het werk rond dit probleem toont vooral aan dat modellering, het vastleggen van de variabelen, de bijhorende domeinen en de beperkingen, geen triviaal probleem is. Integendeel een goede keuze kan bepalend zijn voor het succes of het falen

6. ANDERE TOEPASSINGEN.

xix

van de aanpak. Indien mogelijk is het dan ook aan te raden verschillende alternatieven uit te proberen. CLP is hiervoor een zeer geschikte werkomgeving.

6 6.1

Andere toepassingen. Optimalisatie van een beleggingsportefeuille.

Iedereen weet dat het geen goed idee is je geld in de achterkant van je bankstel te bewaren. De kans is groot dat het een paar jaar later minder waard zal zijn! Daarentegen als je het geld investeert is de kans zelfs groot dat je kapitaal toeneemt. Beleggen houdt echter een risico in: er kan naast het huis dat je gekocht hebt een spoorweg aangelegd worden, het bedrijf waar je aandelen van hebt kan bankroet gaan. Om dit fenomeen te bestrijden is het belangrijk om je investeringen te spreiden over verschillende beleggingen met onafhankelijke risicofactoren. Het geheel van deze beleggingen noemt men dan een beleggingsportefeuille. De meeste mensen die hun centen willen investeren stappen hiervoor naar de bank. Men verwacht dat de bank adviezen geeft en meestal ook de genomen beslissingen uitvoert. Een dergelijk advies geven is voor een bankbediende helemaal geen sinecure. De bankbediende moet namelijk met heel veel factoren rekening houden. Zo is de ene klant de andere niet. Sommige klanten zijn brave huisvaders en lopen liever weinig risico en nemen daar maar bij dat de verwachte opbrengst van hun belegging niet al te hoog is. Ander mensen leven in een wereld van sportauto’s en meisjes van lichte zeden en krijgen een kick van risicovolle beleggingen met een hoge verwachte winstwaarde. Deze specifieke eigenschappen van de klant noemen we in het vervolg het profiel van de klant. De bankbediende moet echter nog met meer rekening houden. Zo worden er door de beleggingsafdeling van de bank ook richtlijnen gegeven over wat op dit moment het voordeligste is en nog belangrijker welke beleggingen niet mogen geadviseerd worden. De belangrijkste probleemfactor is dan natuurlijk het zeer grote aanbod van verschillende waardepapieren. Het experiment in de doctoraatstekst beschreven onderzoekt de mogelijkheden om CLP te gebruiken voor het maken van een software-systeem ter ondersteuning van de beleggingsadviseur. Dit systeem moet dan gegeven een profiel van de klant, de beleggingspolitiek van de bank en de oude portefeuille van de klant een voorstel doen dat als basis kan dienen voor het onderhoud met de klant. Het probleem is redelijk eenvoudig te modelleren met behulp van lineaire beperkingen over variabelen die getallen kunnen aannemen over de re¨ele getallen. Het was dan ook aangewezen om een CLP-variant te gebruiken die werkt op basis van gaussiaanse eliminatie en het simplexalgoritme. Er werden experimenten uitgevoerd, terug in het kader van een eindejaarsthesis [Van97], om met wat testdata de sterkte van CLP voor dit probleem te evalueren. De experimenten in het kader van de eindejaarsthesis en ook testen die later in een hernieuwde poging uitgevoerd werden waren negatief. Binnen de CLP-systemen gebaseerd op lineaire programmatie bestaan er twee varianten: werkend met vlottende komma getallen en op basis van rationale getallen. Alle gebruikte systemen die werken op basis van vlottende kommagetallen geven incorrecte resultaten reeds voor kleine voorbeelden.

NEDERLANDSE SAMENVATTING

xx

Systemen gebaseerd op rationale getallen waren betrouwbaar, maar slaagden er niet in het probleem van voldoende grootte binnen een redelijke tijd of redelijke hoeveelheid geheugen op te lossen. We vermoeden dat de grote en ongelijkmatige co¨efici¨enten in de lineaire vergelijkingen de oorzaak zijn van onze problemen. Daarnaast zijn we van oordeel dat de huidige implementatie van de gebruikte lineaire programmatie-bibliotheken voor CLP, niet echt robuust zijn en dus niet voldoen aan de eisen van een CLP-gebruiker.

6.2 Het verdelen van thesisstudenten onder onderzoeksgroepen. Een jaarlijks terugkerend probleem op het departement computerwetenschappen is het verdelen van de thesisstudenten over de verschillende onderzoeksgroepen. Dit is in vele opzichten een gevoelig onderwerp. In het begin van de procedure maken de studenten hun voorkeuren bekend onder de verschillende onderzoeksgroepen. Een belangrijk aspect in de toewijzing is dan ook om, in de mate van het mogelijke, de student toe te kennen aan de onderzoeksgroep die zijn of haar voorkeur wegdraagt. Dit strookt echter meestal niet met de wensen van de verschillende onderzoeksgroepen. Een onderzoeksgroep heeft er namelijk alle belang bij om een aantal goede thesisstudenten aan te trekken. Niet alleen voor het werk die deze studenten kunnen verrichten, maar een goede student is ook een potenti¨ele kandidaat om na het be¨eindigen van zijn studies verder onderzoek te verrichten in de onderzoeksgroep. Daarnaast zijn er ook grenzen aan de totale hoeveelheid thesisstudenten die een onderzoeksgroep op een redelijke manier kan begeleiden. Het interessante aspect aan deze toepassing is de keuze van de eindige domein variabelen en het opstellen van de beperkingen en de optimalisatiefunctie. Hier enkele criteria waar men rekening moet houden bij het kiezen van de eindige domein variabelen.

  

Het moet mogelijk zijn om de beperkingen en de optimalisatiefunctie uit te drukken. De keuze van de variabelen zou niet tegendraads mogen zijn. Dit om fouten te vermijden. De keuze van de variabelen zou ook effici¨entie moeten nastreven. Het is onnodig te zeggen dat dit vaak conflicteert met de bovenstaande criteria.

Een voor de hand liggend keuze is om voor iedere student een eindige domein variabele aan te maken die de onderzoeksgroep voorstelt waaraan hij word toegekend. Deze keuze maakt het echter niet mogelijk om alle beperkingen en de optimalisatiefunctie uit te drukken. Daarom voeren we nog extra eindige domein variabelen in. Voor iedere student voegen we een eindige domein variabele toe die zijn preferentie uitdrukt voor de toegekende onderzoeksgroep. Hoe kleiner de waarde voor deze variabele hoe meer de student de onderzoeksgroep verkiest. Deze verzameling van eindige domein variabelen is voldoende om het probleem op te lossen met onze eigen oplosser ROPE. Oplossers die geen primitief voorzien voor het uitdrukken van een kardinaliteit hebben nog meer eindige domein variabelen nodig. De doctoraatstekst legt uit hoe de variabelen en beperkingen

7. BESLUIT EN TOEKOMSTPLANNEN.

xxi

met elkaar interageren en welke optimalisaties doorgevoerd kunnen worden om de performantie sterk te verbeteren. Goed om weten is dat dit programma met succes verschillende malen in de praktijk gebruikt werd bij het zoeken naar een goede toekenning.

7

Besluit en toekomstplannen.

Deze thesis is de neerslag van een aantal experimenten rond constraint logisch programmeren. Het eerste hoofdstuk beschrijft constraint logisch programmeren vanuit de inzichten bekomen tijdens de jaren van betrokkenheid met constraint logisch programmeren. Het hoofdaccent ligt op eindige domein constraint logische programmeertalen, maar het bevat ook een korte beschrijving van andere takken binnen CLP, zoals CLP gebaseerd op lineaire programmatie en CLP werkend met intervallen. Het tweede hoofdstuk bevat een aantal vernieuwingen rond concepten in eindige domein CLP. Zo bevat het een beschrijving van een manier om specifieke propagatie-mechanismen aan te duiden voor een beperking met het prune-primitief [VD94]. Dit prune-primitief generaliseert concepten zoals forward checking, partial lookahead en lookahead. Ook het opmerken waard is het geparameteriseerde toekennings-algoritme [Van94], die het terugkeren op een beslissingen op een algemene manier behandeld. Het vervolg van hoofdstuk twee rapporteert over enkele experimenten rond het implementeren van eindige domein oplossers in zowel Prolog als Mercury. Een eerste sectie hierover behandeld een implementatie van een eindige domein oplosser in Prolog [VD94]. Het resulterende systeem van dit experiment heeft het zeer veel bijgebracht in het begrijpen van hoe eindige domein oplosser werken. Het maakte onder meer experimenten rond het gebruik het prune-primitief mogelijk. De volgende sectie van het hoofdstuk over ROPE brengt de lezer in de wereld van Mercury. Op het moment van dit experiment, uitgevoerd in samenwerking met een thesisstudent en gepubliceerd in [VDV96, VDV99], werd een poging ondernomen om een eindige domein oplosser te implementeren in e´ e´ n van de eerste versies van Mercury. De hoofdconclusie van dit werk was, aangezien Mercury geen datastructuren met een open einde toelaat, dat het systeem dan maar beter een destructieve toekenning kon hebben. Waarbij bij het terugkeren op een beslissing deze destructieve toekenning ongedaan word gemaakt. Sedert dat experiment werd deze eigenschap toegevoegd aan het Mercury-systeem. Gegeven de voorgaande experimenten en die nieuwe versie van Mercury werd een laatste oplosser gemaakt. Deze laatste oplosser is competitief met de oplosser [COC97] meegeleverd met SICStus. De daaropvolgende hoofdstukken in het doctoraat behandelen een aantal toepassingen. Twee academische toepassingen en daarna een aantal problemen uit de realiteit. De eerste toepassing beschreven in de tekst betreft het vinden van een bewijs dat een logisch programma nooit een oplossing kan hebben, gegeven een vraagstelling[BVdD98]. Dit zonder het progamma uit te voeren. Dit kan nuttig zijn voor systemen zoals planners, die gebaseerd zijn op het genereer-en-test mechanisme. Indien er geen oplossing bestaat kunnen dergelijke systemen gemakkelijk in een oneindige lus terecht komen. Voor deze toepassing, in parallel met een abductieve aanpak ontwikkeld door Maurice

xxii

NEDERLANDSE SAMENVATTING

Bruynooghe [BVdD98], ontwikkelde de auteur een CLP-aanpak. Het voordeel van CLPsystemen is de mogelijkheid om keuzes uit te stellen, waarbij voorwaarden in beperkingen omgezet worden. Als we dan later een oplossing vinden voor deze beperkingen hebben we een oplossing gevonden. Verschillende versies werden gemaakt. Een eerste versie gebruikte eindige domein constraint logisch programmeren. Deze versie was robuuster dan de abductieve aanpak van Bruynooghe, in die mate dat kleine variaties in de testprogramma’s weinig invloed hebben op het uitvoeringsgedrag. Het programma was echter minderwaardig aangezien het gemiddeld veel meer keuzepunten nodig heeft. Daarenboven gebruikt het programma teveel geheugen. Andere alternatieven werden ontworpen waarbij de eindige domein oplosser vervangen werd door een specifieke oplosser dat veel minder geheugen gebruikt, maar ook minder propagatie. Dit programma werd dan verbeterd met intelligente backtracking [Bru78], ook gebruikt in de abductieve aanpak. Het resulterende programma doorstaat de vergelijking in uitvoeringstijden met de abductieve aanpak. Nochthans gebruikt het programma nog steeds meer keuzepunten. Aangezien de abductieve aanpak niet-geoptimaliseerde datastructuren gebruikt zal deze de CLP-aanpak overtreffen na het aanbrengen van verbeteringen. Niettegenstaande bevat de CLP-aanpak een aantal kenmerken die de abductieve aanpak zouden kunnen opwaarderen. Hoofdstuk vijf in de thesis rapporteert over het gebruik van constraint logisch programmeren als een onderliggende techniek voor terminatie-analyse [DDV99]. Dergelijke terminatie-analyse is gebaseerd op het afleiden van condities die, indien oplosbaar, het bestaan van een terminatiebewijs garanderen. De essentie van de methode bestaat uit het afleiden van een aantal symbolische formules voor normen, niveau-afbeeldingen en interargumentsrelaties. Dergelijke symbolische expressies bevatten een aantal parameters die op een later punt gekozen kunnen worden. In klassieke methoden worden specifieke versies van deze normen, niveau-afbeeldingen en interargumentsrelaties afgeleid tijdens verschillende fazen in de computatie. Het introduceren van deze symbolische formules maakt het mogelijke deze keuzes uit te stellen tot een later stadium. Ter vervanging van het controleren of de specifieke formules leiden tot een terminatiebewijs worden er condities gegenereerd op de parameters van de symbolische formules. Indien deze condities oplosbaar zijn hebben we een bewijs voor terminatie gevonden. Het vervangen van de parameters in de symbolische formules door de parameters, gevonden tijdens het oplossen van de condities, leveren specifieke versies op van de normen, niveau-afbeelding en interargumentsrelaties. De restricties op de symbolische formules hebben een eigenaardige vorm zodat, voor zover ons bekend, geen enkele oplosser deze rechtstreeks kan oplossen. De bijdrage van de auteur bestaat in de automatisatie van het afleiden van eindige domein beperkingen uit deze restricties, en in het oplossen van deze beperkingen. Een eerste toepassing uit de realiteit betreft het plannen van onderhoudsbeurten voor elektrische generatoren. Dit project startte met een laatstejaarsthesis [SB96] voor het ontwerpen van een CLP-programma en het schrijven van een specificatie in OLP-FOL, om deze dan uitvoerbaar te maken met SLDNFA [DD92]. In dit initieel stadium bestaat de bijdrage van de auteur uit de conceptie van het eerste CLP-model. Dit model diende ook als basis voor het automatisch genereren van beperkingen met het SLDNFA-systeem [DVS+ 97]. Later werd het project uitgebreid met alternatieve modellen gebruik makend

7. BESLUIT EN TOEKOMSTPLANNEN.

xxiii

van een combinatie van eindige domein CLP en CLP gebaseerd op lineaire programmatie [Van98]. Deze alternatieve modellen moeten de lineaire beperkingen uit de encodering van het probleem uitbuiten om zo tot betere oplossingen te komen. Een eerste poging hiertoe faalde, een nieuwe start met een volledig nieuw model gaf betere resultaten. Het daaropvolgende hoofdstuk pakt twee andere toepassingen aan. Het eerste probleem, het zoeken naar een optimale beleggingsportefeuille, was geen groot succes. In contrast was het tweede onderwerp, het verdelen van thesisstudenten over de verschillende onderzoeksgroepen, wel succesvol en werd in de praktijk verschillende malen gebruikt. De bijdrage in deze twee toepassingen ligt in: (1) waarom het eerste probleem niet opgelost werd door CLP en (2) hoe het tweede probleem gemodelleerd werd. Het eerste probleem bracht aan het licht dat de integratie van algoritmes uit lineaire programmatie in de huidige CLP-systemen niet zo’n hoogvliegers zijn. De ongebalanceerde co¨effici¨enten van de lineaire beperkingen zorgen voor incorrecte resultaten wanneer opgelost over de vlottende komma getallen en geheugenproblemen of grote uitvoeringstijden wanneer deze opgelost worden over de rationale getallen. Achter het onderzoek rond constraint logisch programmeren is op dit moment zeker nog geen punt gezet. Zo bestaat er nog heel wat toekomst voor hybride CLP-systemen die momenteel gescheiden varianten van CLP met elkaar combineren. Een veelbelovende combinatie is die van eindige domein CLP en CLP werkend met lineair programmatie. Beide systemen worden gebruikt voor hetzelfde soort problemen, nochthans zijn de gebruikte technieken totaal verschillend. Een combinatie van beide systemen heeft veel kansen om, door de voordelen van beide systemen uit te buiten, een verbeterd systeem op te leveren. Andere actiepunten zijn het robuuster maken van huidige oplossers. Momenteel is aan het departement werk aan de gang rond het bouwen van een nieuwe declaratieve specificatietaal gebaseerd op abductie. Een van de onderliggende mechanismen voor de uitvoering zijn CLP-bibliotheken. In een aantal opzichten, uitgewerkt in het laatste hoofdstuk in de thesis, voldoen de huidige CLP-systemen niet voor automatisch gegenereerde beperkingen. Het wegwerken van deze moeilijkheden gaat in de richting van een meer robuuste en polyvalente CLP-oplosser.

xxiv

NEDERLANDSE SAMENVATTING

Suggest Documents