1% BEGIN LICENSE BLOCK 2% Version: CMPL 1.1 3% 4% The contents of this file are subject to the Cisco-style Mozilla Public 5% License Version 1.1 (the "License"); you may not use this file except 6% in compliance with the License. You may obtain a copy of the License 7% at www.eclipse-clp.org/license. 8% 9% Software distributed under the License is distributed on an "AS IS" 10% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See 11% the License for the specific language governing rights and limitations 12% under the License. 13% 14% The Original Code is The ECLiPSe Constraint Logic Programming System. 15% The Initial Developer of the Original Code is Cisco Systems, Inc. 16% Portions created by the Initial Developer are 17% Copyright (C) 2006 Cisco Systems, Inc. All Rights Reserved. 18% 19% Contributor(s): 20% 21% END LICENSE BLOCK 22 23%---------------------------------------------------------------------- 24\chapter{Implementing Constraints} 25\label{chapimpl} 26%HEVEA\cutdef[1]{section} 27%---------------------------------------------------------------------- 28 29This chapter describes how to use \eclipse{}'s advanced control 30\index{implementing constraints} 31facilities for implementing constraints. 32Note that the Generalised Propagation library lib(propia) and 33the Constraint Handling Rules library lib(ech) provide other, 34higher-level ways to implement constraints. Those are more 35suited for prototyping, while this chapter introduces those low-level 36primitives that are actually used in the implementation of the various 37\eclipse{} constraint solvers. 38 39 40%---------------------------------------------------------------------- 41\section{What is a Constraint in Logic Programming?} 42%---------------------------------------------------------------------- 43 44Constraints fit very naturally into the Logic Programming paradigm. 45Declaratively, a constraint is just the same as any other predicate. 46Indeed, in \eclipse{}, ``constraints'' are not a particular 47programming language construct, constraints are just a conceptual notion. 48 49Consider the following standard Prolog query: 50\begin{quote}\begin{verbatim} 51?- member(X, [5,7,3,4]), X =< 4. 52\end{verbatim}\end{quote} 53This will succeed with X = 3 after some search. 54In this example, both the member/2 goal and the inequality goal could 55be considered `constraints on X' because they both restrict the 56possible values for X. Usually, however, member/2 would not be considered 57a ``constraint'' because of its backtracking (search) behaviour: 58\begin{quote}\begin{verbatim} 59?- member(X, [5, 7, 3, 4]). 60X = 5 61More (0.00s cpu) 62X = 7 63More (0.04s cpu) 64\end{verbatim}\end{quote} 65Also, the standard Prolog inequality would not be considered a ``constraint'', 66because if invoked on its own it will raise an error: 67\begin{quote}\begin{verbatim} 68?- X =< 4. 69instantiation fault in X =< 4 70\end{verbatim}\end{quote} 71\index{constraint} 72In the following, we will call a predicate a {\bf constraint} only if it 73\begin{itemize} 74\item behaves deterministically 75\item somehow actively enforces its declarative meaning 76\end{itemize} 77 78 79%---------------------------------------------------------------------- 80\section{Background: Constraint Satisfaction Problems} 81\index{constraint satisfaction problem} 82\index{CSP} 83\label{csp} 84%---------------------------------------------------------------------- 85 86There is a large body of scientific work and literature about 87Constraint Satisfaction Problems, or CSPs. 88CSPs are a restricted class of constraint problems with the 89following properties 90\enableunderscores 91\begin{itemize} 92\item there is a fixed set of variables $X_1, ..., X_n$ 93\item every variable $X_i$ has a finite domain $D_i$ of values that the variable 94 is allowed to take. In general, this can be an arbitrary, unordered 95 domain. 96\item usually one considers only binary (2-variable) constraints $c_{ij}(X_i,X_j)$. 97 Every constraint is simply defined as a set of pairs of consistent values. 98\item the problem is to find a valuation (labeling) of the variables such 99 that all the constraints are satisfied. 100\end{itemize} 101\disableunderscores 102The restriction to binary constraints is not really limiting since every CSP 103can be transformed into a binary CSP. However, this is often not necessary 104since many algorithms can be generalised to n-ary constraints. 105 106A CSP network is the graph formed by considering the variables as nodes 107and the constraints as arcs between them. 108In such a network, several levels of consistency can be defined: 109\index{node consistency} 110\index{arc consistency} 111\index{path consistency} 112\enableunderscores 113\begin{description} 114\item[Node consistency] $\forall{v \in D_i} : c_i(v)$ 115 (not very interesting). It means that 116 all unary constraints are reflected in the domains 117\item[Arc consistency] $\forall{v \in D_i}\ \exists{w \in D_j} : c_{ij}(v,w)$ 118 (most practically relevant). 119 It means that for every value in the domain of one 120 variable, there is a compatible value in the domain of the 121 other variable in the constraint. In practice, constraints 122 are symmetric, so the reverse property also holds. 123\item[Path consistency] $\forall{v \in D_i}\ \forall{w \in D_j}\ \exists{u \in D_k} : c_{ik}(v,u), c_{kj}(u,w)$ 124 (usually too expensive). One can show that this property extends to 125 whole paths, i.e.\ on any path of constraints between variables i and j 126 the variables have domain values which are compatible with any 127 domain values for i and j. 128\end{description} 129\disableunderscores 130Note that neither of these conditions is sufficient for the 131problem to be satisfiable. It is still necessary to search for solutions. 132Computing networks with these consistency levels can however be a useful 133intermediate step to finding a solution to the CSP. 134 135Consequently, a complete CSP solver needs the following design decisions: 136\begin{itemize} 137\item what level of consistency do we want to employ? 138\item at what time during search do we want to (re)establish this consistency? 139\item what algorithm do we use to establish this consistency? 140\end{itemize} 141In practice, the most relevant consistency level is arc-consistency. 142Consequently, a number of algorithms have been proposed for the 143purpose of establishing arc-consistency. The algorithms used in \eclipse{} 144\index{AC-3} 145are mostly variants of AC-3 \cite{mackworth77} 146\index{AC-5} 147and AC-5 \cite{vanhentenryck92generic}. 148 149\ignore{ 150procedure REVISE(Vi,Vj) 151 DELETE <- false; 152 for each X in Di do 153 if there is no such Y in Dj such that (X,Y) is consistent, 154 then 155 delete X from Di; 156 DELETE <- true; 157 endif; 158 endfor; 159 return DELETE; 160 end REVISE 161 162procedure AC-3 163 Q <- {(Vi,Vj) in arcs(G),i#j}; 164 while not Q empty 165 select and delete any arc (Vk,Vm) from Q; 166 if REVISE(Vk,Vm) then 167 Q <- Q union {(Vi,Vk) such that (Vi,Vk) in arcs(G),i#k,i#m} 168 endif 169 endwhile 170 end AC-3 171} 172 173 174%---------------------------------------------------------------------- 175\section{Constraint Behaviours} 176\index{behaviour of a constraint} 177%---------------------------------------------------------------------- 178 179As opposed to the theoretical CSP framework sketched in the previous section, 180in \eclipse{} we usually deal with more heterogeneous situation. 181We want to allow the integration of very different constraints, 182and we want to achieve a separation of constraint propagation and search. 183Therefore, we are not interested in an overall problem solving algorithm 184which controls search and constraint propagation globally for the whole 185problem and all constraints. 186We prefer to view the constraint solving process as in figure \ref{figclpexec}: 187the search process is controlled by an algorithmic program, 188while constraint propagation is performed by data-driven agents which 189do local (again algorithmic) computations on one or several constraints. 190\begin{figure} 191\begin{center} 192\resizebox{0.4\textwidth}{!}{\includegraphics{clpexec.eps}} 193\end{center} 194\caption{Control during Constraint Solving} 195\label{figclpexec} 196\end{figure} 197Individual constraints can then be implemented with different behaviours, 198and freely mixed within a single computation. 199Constraint behaviours can essentially be characterised by 200\index{trigger condition} 201\begin{itemize} 202\item their triggering condition ({\bf when} are they executed) 203\item the action they perform when triggered ({\bf what} do they do) 204\end{itemize} 205Let us now look at examples of different constraint behaviours. 206 207\subsection{Consistency Check} 208\index{consistency check} 209The \verb.=<./2 predicate, whose standard Prolog version raises an error 210when invoked with uninstantiated variable, is also implemented by the 211{\bf suspend} library. Both implementations 212have the same declarative meaning, but the {\bf suspend} version can 213be considered to be a proper constraint. 214It implements a {\bf passive test}, i.e.\ 215it simply delays until both arguments are numbers, and then succeeds or fails: 216\begin{quote}\begin{verbatim} 217?- suspend : (X =< 4). 218X = X 219There is 1 delayed goal. 220Yes (0.00s cpu) 221 222?- suspend : (X =< 4), X = 2. 223X = 2 224Yes (0.00s cpu) 225 226?- suspend : (X =< 4), X = 5. 227No (0.00s cpu) 228\end{verbatim}\end{quote} 229 230\subsection{Forward Checking} 231\index{forward checking} 232Often a constraint can already do useful work before all its arguments 233are instantiated. In particular, this is the case when we are working 234with domain variables. Consider {\bf ic}'s disequality constraint \verb.#\=. : 235Even when only one side is instantiated, it can already remove this value 236from the domain of the other (still uninstantiated) side: 237\begin{quote}\begin{verbatim} 238?- X :: 1 .. 5, 239 X #\= 3. 240X = X{[1, 2, 4, 5]} 241Yes (0.00s cpu) 242\end{verbatim}\end{quote} 243If both sides are uninstantiated, the constraint cannot do anything useful. 244It therefore waits (delays) until one side becomes instantiated, 245but then wakes up and acts as before. 246This behaviour is sometimes called forward checking \cite{VanHentenryck}: 247\begin{quote}\begin{verbatim} 248?- [X,Y] :: 1 .. 5, 249 X #\= Y. % delays 250X = X{1 .. 5} 251Y = Y{1 .. 5} 252There is 1 delayed goal. 253Yes (0.00s cpu) 254 255?- X :: 1 .. 5, 256 X #\= Y, % delays 257 Y = 3. % wakes 258X = X{[1, 2, 4, 5]} 259Y = 3 260Yes (0.01s cpu) 261\end{verbatim}\end{quote} 262 263\subsection{Domain (Arc) Consistency} 264\index{arc consistency} 265\index{domain consistency} 266For many constraints, even more eager behaviour is possible. 267For example, {\bf ic}'s inequality constraints performs {\bf domain updates} as 268soon as possible, even when one or both arguments are still variables: 269\begin{quote}\begin{verbatim} 270?- [X, Y] :: 1 .. 5, X #< Y. 271X = X{1 .. 4} 272Y = Y{2 .. 5} 273There is 1 delayed goal. 274Yes (0.00s cpu) 275 276?- [X, Y] :: 1 .. 5, X #< Y, X #> 2. 277Y = Y{[4, 5]} 278X = X{[3, 4]} 279There is 1 delayed goal. 280Yes (0.00s cpu) 281\end{verbatim}\end{quote} 282Inconsistent values are removed form the domains as soon as possible. 283This behaviour corresponds to {\bf arc consistency} as discussed in 284section \ref{csp}. 285 286\subsection{Bounds Consistency} 287\index{bounds consistency} 288Note however that not all {\bf ic} constraints maintain full domain 289arc consistency. For performance reasons, 290the \verb.#=. constraint only maintains bounds consistency, which is 291weaker, as illustrated by the following example: 292\begin{quote}\begin{verbatim} 293?- [X, Y] :: 1 .. 5, X #= Y + 1, X #\= 3. 294Y = Y{1 .. 4} 295X = X{[2, 4, 5]} 296There is 1 delayed goal. 297Yes (0.00s cpu) 298\end{verbatim}\end{quote} 299Here, the value 2 for Y was not removed even though it is not arc consistent 300(there is no value for X which is compatible with it). 301 302\index{incompleteness of propagation} 303It is important to understand that this kind of propagation incompleteness 304does not affect correctness: the constraint will simply detect the 305inconsistency later, when its arguments have become more instantiated. 306In terms of the search tree, this means that a branch will not be pruned 307as early as possible, and extra time might be spent searching. 308 309 310\quickref{Typical Constraint Behaviours}{ 311\begin{description} 312\item[Consistency Checking] 313 wait until all variables instantiated, then check 314\item[Forward Checking] 315 wait until one variable left, then compute consequences 316\item[Domain (Arc) Consistency] 317 wait until a domain changes, then compute consequences for other domains 318\item[Bounds Consistency] 319 wait until a domain bound changes, then compute consequences for other bounds 320\end{description} 321} 322 323 324\ignore{ 325\subsection{The Resolvent} 326\index{resolvent} 327The term {\bf resolvent} originates from Logic Programming. 328It is the set of all goals that need to be satisfied. 329The computation typically starts with a top-level goal, 330then gets successively transformed (by substituting goals that 331match a clause head with an instance of the clause body, ie.\ a 332sequence of sub-goals), 333and eventually terminates with one of the trivial goals 334{\bf true} or {\bf fail}. 335For example, given the program 336\begin{quote}\begin{verbatim} 337p :- q,r. 338q. 339r :- q. 340\end{verbatim}\end{quote} 341and the goal p, the resolvent goes through the following states 342before the goal is proven and the computation terminates: 343\begin{quote}\begin{verbatim} 344p ----> q,r ----> r ----> q ----> {} 345\end{verbatim}\end{quote} 346 347\index{Prolog} 348While in Prolog the resolvent is always processed from left to right, 349the resolvent in {\eclipse} is more structured, and can be manipulated 350in a much more flexible way. 351This is achieved by two basic mechanisms, {\bf suspension} 352and {\bf priorities}. 353 354\index{suspended goal} 355{\bf Suspended} goals form the part of the resolvent which is 356currently not being considered. This is typically done when we 357know that we cannot currently infer any interesting information from them. 358 359\index{priority} 360The remaining goals are ordered according to their {\bf priority}. 361At any time, the system attempts to solve the most urgent subgoal first. 362{\eclipse} currently supports a fixed range of 12 different priorities, 363priority 1 being the most urgent and 12 the least urgent. 364 365Figure \ref{figresolv} shows the structure of the resolvent. 366When a toplevel goal is launched, it has priority 12 and is the only 367member of the resolvent. As execution proceeds, active goals may be 368suspended, and suspended goals may be woken and scheduled with a 369particular priority. 370\begin{figure} 371% picture has been made with xfig and exported as encapsulated postscript 372\includegraphics{resolv.eps} 373\caption{Structure of the resolvent} 374\label{figresolv} 375\end{figure} 376} 377 378 379 380%---------------------------------------------------------------------- 381\section{Programming Basic Behaviours} 382%---------------------------------------------------------------------- 383 384As an example, we will look at creating constraint versions of the 385following predicate. 386It defines a relationship between containers of type 1, 2 or 3, 387and their capacity: 388\begin{code} 389capacity(1, N) :- N>=0.0, N=<350.0. 390capacity(2, N) :- N>=0.0, N=<180.0. 391capacity(3, N) :- N>=0.0, N=<50.0. 392\end{code} 393This definition gives the intended declarative meaning, 394but does not behave as a constraint: 395{\tt capacity(3, C)} will raise an error, and 396{\tt capacity(Type, 30.5)} will generate several solutions nondeterministically. 397Only calls like {\tt capacity(3, 27.1)} will act correctly as a test. 398 399 400\subsection{Consistency Check} 401\index{consistency check} 402 403To program the passive consistency check behaviour, we need to wait until 404both arguments of the predicate are instantiated. 405This can be achieved by adding an \eclipse{} {\bf delay clause}: 406\begin{code} 407delay capacity(T,N) if var(T);var(N). 408capacity(1, N) :- N>=0.0, N=<350.0. 409capacity(2, N) :- N>=0.0, N=<180.0. 410capacity(3, N) :- N>=0.0, N=<50.0. 411\end{code} 412The delay clause specifies that any call to capacity/2 will delay as long 413as one of the arguments is a variable. When the variables become instantiated 414later, execution will be resumed automatically, and 415the instantiations will be checked for satisfying the constraint. 416 417 418\subsection{Forward Checking} 419\index{forward checking} 420 421For Forward Checking, we will assume that we have interval domain variables, 422as provided by the {\bf ic} library (without domain variables, there would 423not be much interesting propagation to be done). 424 425Here is one implementation of a forward checking version: 426\begin{code} 427:- lib(ic). 428delay capacity(T, N) if var(T), var(N). 429capacity(T, N) :- nonvar(N), !, 430 N >= 0, 431 ( N =< 50.0 -> T :: [1,2,3] 432 ; N =< 180.0 -> T :: [1,2] 433 ; N =< 350.0 -> T = 1 434 ; fail 435 ). 436capacity(1, N) :- N\$>=0.0, N\$=<350.0. 437capacity(2, N) :- N\$>=0.0, N\$=<180.0. 438capacity(3, N) :- N\$>=0.0, N\$=<50.0. 439\end{code} 440Note that the delay clause now only lets goals delay when both 441arguments are variables. As soon as one is instantiated, the 442goal wakes up and, depending on which is the instantiated argument, 443either the first, or one of the last three clauses is executed. 444Some examples of the behaviour: 445\begin{quote}\begin{verbatim} 446?- capacity(T, C). 447There is 1 delayed goal. 448Yes (0.00s cpu) 449 450?- capacity(3, C). 451C = C{0.0 .. 50.0} 452Yes (0.00s cpu) 453 454?- capacity(T, C), C = 100. 455T = T{[1, 2]} 456C = 100 457Yes (0.00s cpu) 458\end{verbatim}\end{quote} 459 460A disadvantage of the above implementation is that when the predicate wakes 461up, it can be either because T was instantiated, or because C was 462instantiated. An extra check ({\tt nonvar(N)}) is needed to distinguish the two cases. 463Alternatively, we could have created two agents (delayed goals), each one 464specialised for one of these cases: 465\begin{code} 466capacity(T, N) :- 467 capacity_forward(T, N), 468 capacity_backward(T, N). 469 470delay capacity_forward(T, _N) if var(T). 471capacity_forward(1, N) :- N\$>=0.0, N\$=<350.0. 472capacity_forward(2, N) :- N\$>=0.0, N\$=<180.0. 473capacity_forward(3, N) :- N\$>=0.0, N\$=<50.0. 474 475delay capacity_backward(_T, N) if var(N). 476capacity_backward(T, N) :- 477 N >= 0, 478 ( N =< 50.0 -> T :: [1,2,3] 479 ; N =< 180.0 -> T :: [1,2] 480 ; N =< 350.0 -> T = 1 481 ; fail 482 ). 483\end{code} 484Unfortunately, there is a drawback to this implementation as well: 485once one of the two delayed goals has done its work, all the constraint's 486information has been incorporated into the remaining variable's domain. 487However, the other delayed goal is still waiting, and will eventually 488wake up when the remaining variable gets instantiated as well, at which time 489it will then do a redundant check. 490 491The choice between having one or several agents for a constraint is a 492choice we will face every time we implement a constraint. 493 494 495 496%---------------------------------------------------------------------- 497\section{Basic Suspension Facility} 498%---------------------------------------------------------------------- 499 500For the more complex constraint behaviours (beyond those waiting for 501instantiations), we need to employ lower-level primitives of the \eclipse{} 502kernel (suspensions and priorities). 503\index{suspension} 504\index{priority} 505If we want to add a new constraint to an existing solver, we also 506need to use the lower-level interface that the particular solver 507provides. 508 509Apart from the delay clauses used above, 510\eclipse{} also provides a more powerful (though less declarative) way of 511causing a goal to delay. 512The following is another implementation of the constraint checking behaviour, 513this time using the suspend/3 built-in predicate to create a delayed goal for 514\index{suspend/3} 515capacity/2: 516\begin{code} 517capacity(T,N) :- (var(T);var(N)), !, 518 suspend(capacity(T,N), 0, [T,N]->inst). 519capacity(1, N) :- N>=0.0, N=<350.0. 520capacity(2, N) :- N>=0.0, N=<180.0. 521capacity(3, N) :- N>=0.0, N=<50.0. 522\end{code} 523\quickref{The Basic Suspension Facilities}{ 524\begin{description} 525\item[{\bf suspend(Goal, Priority, Triggers)}] 526 \index{suspend/3} 527 Creates Goal as a delayed goal with a given waking priority and 528 triggering conditions. 529 Triggers is a list of Variables->Conditions terms, specifying 530 under which conditions the goal will be woken up. 531 The priority specifies with which priority the goal will be scheduled 532 after it has been triggered. 533 A priority of 0 selects the default for the predicate. 534 Otherwise, valid priorities range are from 535 1 (most urgent, reserved for debugging purposes) to 12 (least urgent). 536\end{description} 537Some valid triggers: 538\index{trigger condition} 539\begin{description} 540\item[X->inst] wake when the variable becomes instantiated (most specific) 541\item[X->constrained] wake when the variable becomes constrained somehow 542 (most general) 543\item[X->ic:min] wake when the lower bound of an ic-variable changes 544\item[X->ic:max] wake when the upper bound of an ic-variable changes 545\item[X->ic:hole] wake an internal domain value gets removed 546\end{description} 547} 548 549 550\section{A Bounds-Consistent IC constraint} 551 552To show the basic ideas, we will simply reimplement a constraint that 553already exists in the {\bf ic} solver, the inequality constraint. 554We want a constraint ge/2 that takes two {\bf ic} variables (or numbers) 555and constrains the first to be greater or equal to the second. 556 557\index{bounds consistency} 558The behaviour should be to maintain bounds-consistency: 559If we have a goal {\tt ge(X,Y)}, where the domain of \verb/X is X{1..5}/ and 560the domain of \verb/Y is Y{3..7}/, we would like the domains to be updated such 561that the upper bound of Y gets reduced to 5, and the lower bound of X 562gets increased to 3. The following code achieves this: 563\begin{code} 564ge(X, Y) :- 565 get_bounds(X, _, XH), 566 get_bounds(Y, YL, _), 567 ( var(X),var(Y) -> 568 suspend(ge(X,Y), 0, [X->ic:max, Y->ic:min]) 569 ; 570 true 571 ), 572 X #>= YL, % impose new bounds 573 Y #=< XH. 574\end{code} 575We have used a single primitive from the low-level interface of the 576{\bf ic} library: {\bf get_bounds/3}, which extracts the current 577domain bounds from a variable. Further, we have used the information 578that the library implements trigger conditions called {\bf min} 579and {\bf max}, which cause a goal to wake up when the lower/upper 580bound on an {\bf ic} variable changes. 581 582Note that we suspend a new instance of the {\tt ge(X,Y)} goal {\em before} 583we impose the new bounds on the variables. This is important when the 584constraint is to be used together with other constraints of higher 585priority: imposing a bound may immediately wake and execute such 586a higher-priority constraint. The higher-priority constraint may then 587in turn change one of the bounds that ought to wake ge/2 again. 588This only works if ge/2 has already been (re-)suspended at that time. 589 590 591\section{Using a Demon} 592\index{demon} 593Every time the relevant variable bounds change, the delayed ge/2 goal 594wakes up and (as long as there are still two variables) a new, 595identical goal gets delayed. 596To better support this situation, {\eclipse} provides a special type 597of predicate, called a {\bf demon}. 598A predicate is turned into a 599\index{demon} 600demon by annotating it with a 601\bipref{demon/1}{../bips/kernel/compiler/demon-1.html} 602declaration. 603A demon goal differs from a normal goal only in its behaviour on 604waking. While a normal goal disappears from the resolvent when it is 605woken, the demon remains in the resolvent. 606Declaratively, this corresponds to an implicit recursive call in 607the body of each demon clause. 608Or, in other words, the demon goal forks into one goal that remains in the 609suspended part of the resolvent, and an identical one 610that gets scheduled for execution. 611 612With a demon, our above example can be done more 613efficiently. One complication arises, however. Since the goal 614implicitly re-suspends, it now has to be explicitly killed when 615it is no longer needed. The easiest way to achieve this is to 616let it have a handle to itself (its `suspension') in one of its arguments. 617This can then be used to kill the suspension when required: 618\begin{code} 619ge(X, Y) :- 620 suspend(ge(X,Y,MySusp), 0, [X->ic:max, Y->ic:min], MySusp), 621 ge(X, Y, MySusp). 622 623:- demon ge/3. 624ge(X, Y, MySusp) :- 625 get_bounds(X, _, XH), 626 get_bounds(Y, YL, _), 627 ( var(X),var(Y) -> 628 true % implicitly re-suspend 629 ; 630 kill_suspension(MySusp) 631 ), 632 X #>= YL, % impose new bounds 633 Y #=< XH. 634\end{code} 635We have used the new primitives suspend/4 and kill_suspension/1. 636 637 638%---------------------------------------------------------------------- 639%\section{Constraints with many variables} 640%---------------------------------------------------------------------- 641 642%---------------------------------------------------------------------- 643\section{Exercises} 644%---------------------------------------------------------------------- 645 646\begin{enumerate} 647 648\item Implement a constraint atmost/3 649\begin{quote}\begin{verbatim} 650atmost(+N, +List, +V) 651\end{verbatim}\end{quote} 652 which takes an integer N, an integer V and a list List 653 containing integers or integer domain variables. 654 655 Meaning: at most N elements of List have value V. 656 657 Behaviour: Fail as soon as too many list elements are 658 instantiated to value V. 659 This requires only basic suspension facilities, no domain 660 information needs to be taken into account. 661 662 Tests are provided in the file {\tt atmost.tst}. 663 You can test your constraint by loading the library {\tt lib(test_util)} 664 and then calling {\tt test(atmost)}. 665 666 667\item Implement a constraint offset/3 668 669\begin{quote}\begin{verbatim} 670offset(?X,+Const,?Y) 671\end{verbatim}\end{quote} 672which is declaratively like 673\begin{quote}\begin{verbatim} 674offset(X,Const,Y) :- Y #= X+Const. 675\end{verbatim}\end{quote} 676 but maintains domain-arc-consistency (i.e. propagates 677 "holes", while the above definition only maintains 678 bounds-consistency). 679 680 Use suspension built-ins and domain-access primitives 681 from the ic_kernel module. 682 Use not_unify/2 to test whether a value is outside 683 a variable's domain. 684 685 Tests are provided in the file {\tt offset.tst}. 686 You can test your constraint by loading the library {\tt lib(test_util)}. 687 and then calling {\tt test(offset)}. 688 689\end{enumerate} 690 691%HEVEA\cutend 692