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%\documentclass[11pt,a4paper]{book} 24%\usepackage{alltt} 25%\usepackage{graphics} 26%%\usepackage{html} 27%\topmargin 0cm 28%\oddsidemargin 0cm 29%\evensidemargin 0cm 30%\textwidth 16cm 31%\textheight 22.5cm 32% 33%% Allow underscores as normal characters (but lose subscripts...) 34%% [moved into include file because of latex2html problem] 35%%\catcode`_=\active 36% 37%\def\eclipse{ECL$^i$PS$^e$} 38% 39%% Don't use a style file for sepiachip because latex2html ignores it 40% 41%\makeindex 42% 43%\begin{document} 44% 45%\title{{\huge Search in ECLiPSe}} 46%\author{Joachim Schimpf \and Kish Shen \and Mark Wallace} 47% 48%\maketitle 49%%\abstract{ 50%%This tutorial is an overwiew of different search methods to Prolog, 51%%and how to implement them in ECLiPSe. 52%%} 53% 54%\tableofcontents 55 56%\chapter{Search Methods} 57%---------------------------------------------------------------------- 58\section{Introduction} 59%---------------------------------------------------------------------- 60In this tutorial we will take a closer look at the principles and 61alternative methods of searching for solutions in the presence of 62constraints. Let us first recall what we are talking about. 63We assume we have the standard pattern of a constraint program: 64\begin{quote}\begin{alltt} 65solve(Data) :- 66 model(Data, Variables), 67 search(Variables), 68 print_solution(Variables). 69\end{alltt}\end{quote} 70The model part contains the logical {\em model} of our problem. It defines 71the variables and the constraints. 72Every variable has a {\em domain} of values that it can take 73(in this context, we only consider domains with a finite number of values). 74 75Once the model is set up, we go into the search phase. 76Search is necessary since generally the implementation of the constraints 77is not complete, i.e.\ not strong enough to logically infer directly 78the solution to the problem. Also, there may be multiple solutions 79which have to be located by search, e.g.\ in order to find the best one. 80In the following, we will use the following terminology: 81\begin{itemize} 82\item If a variable is given a value (from its domain, of course), 83 we call this an {\em assignment}. If every problem variable is given 84 a value, we call this a {\em total assignment}. 85\item A total assignment is a {\em solution} if it satisfies all the 86 constraints. 87\item The {\em search space} is the set of all possible total assignments. 88 The search space is usually very large because it grows exponentially 89 with the problem size: 90 \begin{displaymath} 91 SearchSpaceSize = {DomainSize}^{NumberOfVariables} 92 \end{displaymath} 93\end{itemize} 94 95 96% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 97\subsection{Overview of Search Methods} 98% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 99 100\begin{figure} 101\begin{center} 102\includegraphics{search3.eps} 103\end{center} 104\caption{A search space of size 16} 105\label{figsearchspace} 106\end{figure} 107Figure \ref{figsearchspace} shows a search space with N (here 16) 108possible total assignments, some of which are solutions. 109Search methods now differ in the way in which these assignments 110are visited. 111We can classify search methods according to different criteria: 112\begin{description} 113\item[Complete vs incomplete exploration] complete search means that the search space 114 is investigated in such a way that all solutions are guaranteed to be found. 115 This is necessary when the optimal solution is needed (one has to prove 116 that no better solution exists). Incomplete search may be sufficient when 117 just some solution or a relatively good solution is needed. 118\item[Constructive vs move-based] this indicates whether the method advances 119 by incrementally constructing assignments (thereby reasoning about partial 120 assignments which represent subsets of the search space) or by moving 121 between total assignments (usually by modifying previously explored 122 assignments). 123\item[Randomness] some methods have a random element while others follow 124 fixed rules. 125\end{description} 126Here is table of a selection of search methods together with their properties: 127 128\begin{center} 129\begin{tabular}{|l|lll|} 130\hline 131Method& exploration& assignments& random\\ 132\hline 133Full tree search& complete& constructive& no\\ 134Credit search& incomplete& constructive& no\\ 135Bounded backtrack& incomplete& constructive& no\\ 136Limited discrepancy& complete& constructive& no\\ 137Hill climbing& incomplete& move-based& possibly\\ 138Simulated annealing& incomplete& move-based& yes\\ 139Tabu search& incomplete& move-based& possibly\\ 140Weak commitment& complete& hybrid& no\\ 141\hline 142\end{tabular} 143\end{center} 144 145The constructive search methods usually organise the search space by 146partitioning it systematically. This can be done naturally with a 147search tree (Figure \ref{figsearchtree}). The nodes in this tree 148represent choices which partition the remaining search space into two 149or more (usually mutually exclusive) disjoint sub-spaces. Using such 150a tree structure, the search space can be traversed systematically and 151completely (with as little as O(N) memory requirements). 152 153\begin{figure} 154\begin{center} 155\includegraphics{search4.eps} 156\end{center} 157\caption{Search space structured using a search tree} 158\label{figsearchtree} 159\end{figure} 160Figure \ref{figtreesearch} shows a sample tree search, namely a depth-first 161incomplete traversal. 162As opposed to that, figure \ref{figmovesearch} shows an example of an 163incomplete move-based search which does not follow a fixed search space 164structure. Of course, it will have to take other precautions to avoid 165looping and ensure termination. 166\begin{figure} 167\begin{center} 168\includegraphics{search5.eps} 169\end{center} 170\caption{A move-based search} 171\label{figmovesearch} 172\end{figure} 173 174\begin{figure} 175\begin{center} 176\includegraphics{search6.eps} 177\end{center} 178\caption{A tree search (depth-first)} 179\label{figtreesearch} 180\end{figure} 181 182A few further observations: 183Move-based methods are usually incomplete. This is not surprising given 184typical sizes of search spaces. 185A complete exploration of a huge search space 186is only possible if large sub-spaces can be excluded a priory, and this 187is only possible with constructive methods which allow to reason about 188whole classes of similar assignments. 189Moreover, a complete search method must remember which parts of the 190search space have already been visited. 191This can only be implemented with 192acceptable memory requirements if there is a simple structuring of the 193space that allows compact encoding of sub-spaces. 194 195 196% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 197\subsection{Optimisation and Search} 198% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 199 200Many practical problems are in fact optimisation problems, ie.\ we are 201not just interested in some solution or all solutions, but in 202the best solution. 203 204Fortunately, there is a general method to find the optimal solution 205based on the ability to find all solutions. 206\index{branch-and-bound} 207The {\em branch-and-bound} technique works are follows: 208\begin{enumerate} 209\item Find a first solution 210\item Add a constraint requiring a better solution than the best 211 one we have so far (e.g.\ require lower cost) 212\item Find a solution which satisfies this new constraint. 213 If one exists, we have a new best solution and we repeat step 2. 214 If not, the last solution found is the proven optimum. 215\end{enumerate} 216The library {\bf branch\_and\_bound} provides the generic 217branch-and-bound primitives bb\_min/3 and bb\_min/6. 218 219 220% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 221\subsection{Heuristics} 222% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 223 224Since search space sizes grow exponentially with problem size, 225it is not possible to explore all assignments except for the 226very smallest problems. 227The only way out is {\em not} to look at the whole search space. 228There are only two ways to do this: 229\begin{itemize} 230\item {\bf Prove} that certain areas of the space contain no solutions. 231 This can be done with the help of constraints. This is often referred 232 to as {\em pruning}\index{pruning}. 233\item {\bf Ignore} parts of the search space that are unlikely to contain 234 solutions (i.e.\ do incomplete search), or at least postpone their exploration. 235 This is done by using {\em heuristics}\index{heuristics}. 236 A heuristic is a particular traversal order of the search space 237 which explores promising areas first. 238\end{itemize} 239 240In the following sections we will first investigate the considerable 241degrees of freedom that are available for heuristics within the framework of 242systematic tree search, which is the traditional search method 243in the Constraint Logic Programming world. 244 245Subsequently, we will turn our attention to move-based methods 246which in {\eclipse} can be implemented using the facilities of the repair-library. 247 248 249%---------------------------------------------------------------------- 250\section{Complete Tree Search with Heuristics} 251%---------------------------------------------------------------------- 252 253There is one form of tree search which is especially economic: 254depth-first, left-to-right search by backtracking. It allows to 255traverse a search tree systematically while requiring only a stack 256of maximum depth N for bookkeeping. Most other strategies of tree 257search (e.g. breadth-first) have exponential memory requirements. 258This unique property is the reason why backtracking is a built feature 259of {\eclipse}. Note that the main disadvantage of the depth-first 260strategy (the danger of going down an infinite branch) does not come 261into play here because we deal with finite search trees. 262 263Sometimes depth-first search and heuristic search are treated as antonyms. 264This is only justified when the shape of the search tree is statically fixed. 265Our case is different: we have the freedom of deciding on the shape of every 266sub-tree before we start to traverse it depth-first. While this does not 267allow to arrange for {\em any} order of visiting the leaves of the search tree, 268it does provide considerable flexibility. This flexibility can be exploited 269by variable and value selection strategies. 270 271% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 272\subsection{Search Trees} 273% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 274 275In general, the nodes of a search tree represent {\em choices}. 276\index{choice} 277These choices should be mutually exclusive and therefore partition the 278\index{partition a search space} 279search space into two or more disjoint sub-spaces. 280In other words, the original problem is reduced to a disjunction 281of simpler sub-problems. 282 283In the case of finite-domain problems, the most common form of choice 284is to choose a particular value for a problem variable 285(this technique is often called 286\index{labeling} 287{\em labeling}). 288For a boolean variable, this means setting the variable to 0 in one 289branch of the search tree and to 1 in the other. 290In {\eclipse}, this can be written as a disjunction 291(which is implemented by backtracking): 292\begin{quote}\begin{alltt} 293( X1=0 ; X1=1 ) 294\end{alltt}\end{quote} 295Other forms of choices are possible. If X2 is a variable that can take 296integer values from 0 to 3 (assume it has been declared as \verb'X2::0..3'), 297we can make a n-ary search tree node by writing 298\begin{quote}\begin{alltt} 299( X2=0 ; X2=1 ; X2=2 ; X2=3 ) 300\end{alltt}\end{quote} 301or more compactly 302\begin{quote}\begin{alltt} 303indomain(X2) 304\end{alltt}\end{quote} 305However, choices do not necessarily involve choosing a concrete value 306for a variable. It is also possible to make disjoint choices by 307\index{domain splitting} 308{\em domain splitting}, e.g. 309\begin{quote}\begin{alltt} 310( X2 #=< 1 ; X2 #>= 2 ) 311\end{alltt}\end{quote} 312or by choosing a value in one branch and excluding it in the other: 313\begin{quote}\begin{alltt} 314( X2 = 0 ; X2 #>= 1 ) 315\end{alltt}\end{quote} 316In the following examples, we will mainly use simple labeling, 317which means that the search tree nodes correspond to a variable 318and a node's branches correspond to the different values that the 319variable can take. 320 321 322% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 323\subsection{Variable Selection} 324% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 325 326\begin{figure} 327\begin{center} 328\includegraphics{search1.eps} 329\end{center} 330\caption{The effect of variable selection} 331\label{figvarsel} 332\end{figure} 333 334Figure \ref{figvarsel} shows how variable selection reshapes a search tree. 335If we decide to choose values for X1 first (at the root of the search tree) 336and values for X2 second, then the search tree has one particular shape. 337If we now assume a depth-first, left-to-right traversal by backtracking, 338this corresponds to one particular order of visiting the leaves of the tree: 339(0,0), (0,1), (0,2), (0,3), (1,0), (1,1), (1,2), (1,3). 340 341If we decide to choose values for X2 first and X1 second, then the tree and 342consequently the order of visiting the leaves is different: 343(0,0), (1,0), (0,1), (1,1), (0,2), (1,2), (0,3), (1,3). 344 345While with 2 variables there are only 2 variable selection strategies, 346this number grows exponentially with the number of variables. For 5 347variables there are already $2^{2^{5}-1} = 2147483648$ different variable selection 348strategies to choose from. 349 350Note that the example shows something else: If the domains of the variables 351are different, then the variable selection can change the number of internal 352nodes in the tree (but not the number of leaves). To keep the number of nodes 353down, variables with small domains should be selected first. 354 355 356% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 357\subsection{Value Selection} 358% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 359 360The other way to change the search tree is value selection, i.e. reordering 361the child nodes of a node by choosing the 362values from the domain of a variable in a particular order. 363Figure \ref{figvalsel} shows how this can change the order of visiting the 364leaves: 365(1,2), (1,1), (1,0), (1,3), (0,1), (0,3), (0,0), (0,2). 366 367\begin{figure} 368\begin{center} 369\includegraphics{search2.eps} 370\end{center} 371\caption{The effect of value selection} 372\label{figvalsel} 373\end{figure} 374 375By combining variable and value selection, a large number of different 376heuristics can be implemented. 377To give an idea of the numbers involved, the following table shows the search 378space sizes, the number of possible search space traversal orderings, 379and the number of orderings 380that can be obtained by variable and value selection (assuming domain size 2). 381 382\enableunderscores 383\begin{center} 384\begin{tabular}{|l|l|l|l|} 385\hline 386Variables& Search space& Visiting orders& Selection Strategies\\ 387\hline 3881& 2& 2& 2\\ 3892& 4& 24& 16\\ 3903& 8& 40320& 336\\ 3914& 16& $2.1*10^{13}$& $1.8*10^7$\\ 3925& 32& $2.6*10^{35}$& $3.5*10^{15}$\\ 393n& $2^n$& $2^n!$& 394 $2^{{2^n}-1} \prod_{i=0}^{n-1} (n-1)^{2^i}$\\ 395\hline 396\end{tabular} 397\end{center} 398\disableunderscores 399 400% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 401\subsection{Example} 402% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 403 404We use the famous N-Queens problem to illustrate how heuristics can be applied 405to backtrack search through variable and value selection. 406We model the problem with one variable per queen, assuming that each queen 407occupies one colunm. The variables range from 1 to N and indicate the row 408in which the queen is being placed. The constraints ensure that no two 409queens occupy the same row or diagonal: 410%!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 411% CAUTION: I use the old ## syntax here because 412% #\= is not treated correctly by latex2html 413%!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! 414\begin{quote}\begin{alltt} 415:- lib(fd). 416 417queens(N, Board) :- 418 length(Board, N), 419 Board :: 1..N, 420 ( fromto(Board, [Q1|Cols], Cols, []) do 421 ( foreach(Q2, Cols), count(Dist,1,_), param(Q1) do 422 noattack(Q1, Q2, Dist) 423 ) 424 ). 425 426 noattack(Q1,Q2,Dist) :- 427 Q2 ## Q1, 428 Q2 - Q1 ## Dist, 429 Q1 - Q2 ## Dist. 430\end{alltt}\end{quote} 431We are looking for a first solution to the 16-queens problem by calling 432\begin{quote}\begin{alltt} 433?- queens(16, Vars), % model 434 labeling(Vars). % search 435\end{alltt}\end{quote} 436We start naively, using the pre-defined labeling-predicate that comes with the 437finite-domain library. It is defined as follows: 438\begin{quote}\begin{alltt} 439labeling(AllVars) :- 440 ( foreach(Var, AllVars) do 441 indomain(Var) % select value 442 ). 443\end{alltt}\end{quote} 444The strategy here is simply to select the variables 445from left to right as they occur in the list, and they are 446assigned values starting from the lowest to the numerically highest they can 447take (this is the definition of indomain/1). 448A solution is found after 542 backtracks 449(see section \ref{countbt} below for how to count backtracks). 450 451A first improvement is to employ a 452{\bf general-purpose variable-selection heuristic}, 453\index{first-fail principle} 454the so called first-fail principle. It requires to label the 455variables with the smallest domain first. This reduces the branching 456factor at the root of the search tree and the total number of internal nodes. 457The deleteff/3 predicate implements this strategy for finite domains. 458Using deleteff/3, we can redefine our labeling-routine as follows: 459\begin{quote}\begin{alltt} 460labeling(AllVars) :- 461 ( fromto(AllVars, Vars, VarsRem, []) do 462 deleteff(Var, Vars, VarsRem), % dynamic var-select 463 indomain(Var) % select value 464 ). 465\end{alltt}\end{quote} 466Indeed, for the 16-queens example, this leads to a dramatic improvement, 467the first solution is found with only 3 backtracks now. 468But caution is necessary: The 256-queens instance for example solves 469nicely with the naive strategy, but our improvement leads to a 470disappointment: the time increases dramatically! 471This is not uncommmon with heuristics: one has to keep in mind that the 472search space is not reduced, just re-shaped. Heuristics that yield good 473results with some problems can be useless or counter-productive with others. 474Even different instances of the same problem can exhibit widely different 475characteristics. 476 477Let us try to employ a {\bf problem-specific heuristic}: 478Chess players know that pieces in the middle of the board are more 479useful because they can attack more fields. We could therefore start 480placing queens in the middle of the board to reduce the number of 481unattacked fields earlier. We can achieve that simply by pre-ordering the 482variables such that the middle ones are first in the list: 483\begin{quote}\begin{alltt} 484labeling(AllVars) :- 485 middle_first(AllVars, AllVarsPreOrdered), % static var-select 486 ( foreach(Var, AllVarsPreOrdered) do 487 indomain(Var) % select value 488 ). 489\end{alltt}\end{quote} 490The implementation of middle\_first/2 requries a bit of list manipulation 491and uses primitives from the lists-library: 492\begin{quote}\begin{alltt} 493:- lib(lists). 494 495middle_first(List, Ordered) :- 496 halve(List, Front, Back), 497 reverse(Front, RevFront), 498 splice(Back, RevFront, Ordered). 499\end{alltt}\end{quote} 500This strategy also improves things for the 16-queens instance, the 501first solution requires 17 backtracks. 502 503We can now improve things further by {\bf combining} the two 504variable-selection strategies: 505When we pre-order the variables such that the middle ones are first, 506the deleteff/3 predicate will prefer middle variables when several 507have the same domain size: 508\begin{quote}\begin{alltt} 509labeling(AllVars) :- 510 middle_first(AllVars, AllVarsPreOrdered), % static var-select 511 ( fromto(AllVarsPreOrdered, Vars, VarsRem, []) do 512 deleteff(Var, Vars, VarsRem), % dynamic var-select 513 indomain(Var) % select value 514 ). 515\end{alltt}\end{quote} 516The result is positive: for the 16-queens instance, 517the number of backtracks goes down to zero, 518and more difficult instances become solvable! 519 520Actually, we have not yet implemented our intuitive heuristics properly. 521We start placing queens in the middle columns, but not on the middle rows. 522With our model, that can only be achieved by {\bf changing the value selection}, 523ie.\ setting the variables to values in the middle of their domain. 524We therefore invent a variant of indomain/1 called middle\_first\_indomain/1 525and the resulting labeling routine then looks as follows: 526\begin{quote}\begin{alltt} 527labeling(AllVars) :- 528 middle_first(AllVars, AllVarsPreOrdered), % static var-select 529 ( fromto(AllVarsPreOrdered, Vars, VarsRem, []) do 530 deleteff(Var, Vars, VarsRem), % dynamic var-select 531 middle_first_indomain(Var) % select value 532 ). 533\end{alltt}\end{quote} 534The implementation of middle\_first\_indomain/2 535simply relies on middle\_first/2: 536\begin{quote}\begin{alltt} 537middle_first_indomain(X) :- 538 nonvar(X). 539middle_first_indomain(X) :- 540 var(X), 541 dom(X, List), % the list of values in X's domain 542 middle_first(List, Ordered), 543 member(X, Ordered). 544\end{alltt}\end{quote} 545Surprisingly, this improvement again increases the backtrack count for 54616-queens again to 3. 547However, when looking at a number of different instances of the problem, 548we can observe that the overall behaviour has improved and the 549performance has become more predictable than with the 550initial more naive strategies. 551 552 553% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 554\subsection{Counting Backtracks} 555% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 556 557%The size of the (remaining) search space can be computed easily 558%in finite-domain problems. All we have to do is to multiply the 559%sizes of all the (remaining) variable's domains: 560%\begin{quote}\begin{alltt} 561%search_space(Vars, Size) :- 562% ( foreach(V,Vars), fromto(1,S0,S1,Size) do 563% dvar_domain(V,D), S1 is S0*dom_size(D) 564% ). 565%\end{alltt}\end{quote} 566 567\label{countbt} 568An interesting piece of information during program development is the 569number of backtracks. It is a good measure for the quality of 570both constraint propagation and search heuristics. 571We can instrument our labeling routine as follows: 572\begin{quote}\begin{alltt} 573labeling(AllVars) :- 574 init_backtracks, 575 ( foreach(Var, AllVars) do 576 count_backtracks, % insert this before choice! 577 indomain(Var) 578 ), 579 get_backtracks(B), 580 printf("Solution found after %d backtracks%n", [B]). 581\end{alltt}\end{quote} 582The backtrack counter itself can be implemented by the code below. 583It uses a non-logical counter variable (backtracks) and an additional 584flag (deep\_fail) which ensures that backtracking to exhausted choices 585does not increment the count. 586\begin{quote}\begin{alltt} 587:- local variable(backtracks), variable(deep_fail). 588 589init_backtracks :- 590 setval(backtracks,0). 591 592get_backtracks(B) :- 593 getval(backtracks,B). 594 595count_backtracks :- 596 setval(deep_fail,false). 597count_backtracks :- 598 getval(deep_fail,false), % may fail 599 setval(deep_fail,true), 600 incval(backtracks), 601 fail. 602\end{alltt}\end{quote} 603Note that there are other possible ways of defining the number of backtracks. 604However, the one suggested here has the following useful properties: 605\begin{itemize} 606\item Shallow backtracking (an attempt to instantiate a variable which 607 causes immediate failure due to constraint propagation) is not counted. 608 If constraint propagation works well, the count is therefore zero. 609\item With a perfect heuristic, the first solution is found with zero 610 backtracks. 611\item If there are N solutions, the best achievable value is N (one backtrack 612 per solution). Higher values indicate an opportunity to improve pruning 613 by constraints. 614\end{itemize} 615 616 617%---------------------------------------------------------------------- 618\section{Incomplete Tree Search} 619%---------------------------------------------------------------------- 620%\subsection{Pruning with Extra Constraints} 621%\subsection{First Solution} 622 623% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 624\subsection{Bounded Backtrack Search} 625% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 626 627One way to limit the scope of backtrack search is to keep a 628record of the number of backtracks, and curtail the search when this 629limit is exceeded. 630 631You can find an implementation of Bounded Backtrack Search ({\em BBS}) 632in the file {\tt lds.ecl} in the {\tt doc/examples} directory of your 633{\eclipse} installation. 634The predicate defining bounded backtrack search is 635\verb0bounded_backtrack_search0, and it takes two arguments, a list of 636variables and an integer (the limit on the number of backtracks). 637An example invocation is: 638\begin{quote}\begin{alltt} 639?- [X,Y,Z]::1..3, X+Y+Z#=6, bounded_backtrack_search([X,Y,Z],4). 640\end{alltt}\end{quote} 641The answers are returned on backtracking in the following order: 642\begin{itemize} 643\item 644X = 1 645Y = 2 646Z = 3 647\item 648X = 1 649Y = 3 650Z = 2 651\item 652X = 2 653Y = 1 654Z = 3 655\item 656X = 2 657Y = 2 658Z = 2 659\end{itemize} 660After which the procedure fails outputting 661\verb0Backtrack limit exceeded0. 662 663The implementation uses several 664facilities of {\eclipse}, including {\em non-logical variables} and 665{\em catch and throw}: 666\begin{quote}\begin{alltt} 667:- local variable(backtracks), variable(deep_fail). 668 669bounded_backtrack_search(List,Limit) :- 670 setval(backtracks,Limit), 671 block(bbs_label(List), 672 exceed_limit, 673 (writeln('Backtrack limit exceeded'), fail) 674 ). 675 676bbs_label([]). 677bbs_label([Var|Vars]) :- 678 limit_backtracks, 679 indomain(Var), 680 bbs_label(Vars). 681 682limit_backtracks :- 683 setval(deep_fail,false). 684limit_backtracks :- 685 getval(deep_fail,false), % may fail 686 setval(deep_fail,true), 687 decval(backtracks), 688 (getval(backtracks,0) -> exit_block(exceed_limit) ; fail). 689\end{alltt}\end{quote} 690 691 692% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 693\subsection{Credit Search} 694% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 695 696Credit search is a tree search method where the number of 697nondeterministic choices is limited a priori. This is achieved by 698starting the search at the tree root with a certain integral amount of 699credit. This credit is split between the child nodes, their credit 700between their child nodes, and so on. A single unit of credit cannot 701be split any further: subtrees provided with only a single credit unit 702are not allowed any nondeterministics choices, only one path though these 703subtrees can be explored, i.e. only one leaf in the subtree can be visited. 704Subtrees for which no credit is left are pruned, 705i.e.\ not visited. 706 707The following code (a replacement for labeling/1) 708implements credit search. For ease of understanding, it is 709limited to boolean variables: 710\begin{quote}\begin{alltt} 711% Credit search (for boolean variables only) 712credit_search(Credit, Xs) :- 713 ( 714 foreach(X, Xs), 715 fromto(Credit, ParentCredit, ChildCredit, _) 716 do 717 ( var(X) -> 718 ParentCredit > 0, % possibly cut-off search here 719 ( % Choice 720 X = 0, ChildCredit is (ParentCredit+1)//2 721 ; 722 X = 1, ChildCredit is ParentCredit//2 723 ) 724 ; 725 ChildCredit = ParentCredit 726 ) 727 ). 728\end{alltt}\end{quote} 729Note that the leftmost alternative (here X=0) 730gets slightly more credit than the rightmost one (here X=1) 731by rounding the child node's credit up rather than down. 732This is especially relevant when the leftover credit is down to 1: 733from then on, only the leftmost alternatives will be taken until a 734leaf of the search tree is reached. The leftmost alternative should 735therefore be the one favoured by the search heuristics. 736 737What is a reasonable amount of credit to give to a search? 738In an unconstrained search tree, the credit is equivalent to the 739number of leaf nodes that will be reached. 740The number of leaf nodes grows exponentially with the number of 741labelled variables, while tractable computations should have 742polynomial runtimes. A good rule of thumb could therefore be to 743use as credit the number of variables squared or cubed, thus enforcing 744polynomial runtime. 745 746Note that this method in its pure form allow choices only close to the 747root of the search tree and disallows choices completely below a certain 748tree depth. This is too restrictive when the value selection strategy 749is not good enough. A possible remedy is to combine credit search with 750bounded backtrack search. 751 752 753% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 754\subsection{Timeout} 755% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 756 757Another form of incomplete tree search is simply to use time-outs. 758The branch-and-bound primitives min\_max/6,8 and minimize/6,8 allow 759to specify a maximal runtime. If a timeout occurs, the best solution 760found so far is returned instead of the proven optimum. 761 762A general timeout can be implemented as follows. When Goal has run for 763more than Seconds seconds, it is aborted and TimeOutGoal is called instead. 764\begin{quote}\begin{alltt} 765:- set_event_handler(timeout, exit_block/1). 766 767timeout(Goal, Seconds, TimeOutGoal) :- 768 block( 769 timeout_once(Goal, Seconds), 770 timeout, 771 call(TimeOutGoal) 772 ). 773 774 timeout_once(Goal, Seconds) :- 775 event_after(timeout, Seconds), 776 ( call(Goal) -> 777 cancel_after_event(timeout) 778 ; 779 cancel_after_event(timeout), 780 fail 781 ). 782\end{alltt}\end{quote} 783 784 785%---------------------------------------------------------------------- 786\subsection{Limited Discrepancy Search} 787%---------------------------------------------------------------------- 788 789% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 790\subsubsection{Introduction} 791% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 792 793Limited discrepancy search ({\em LDS}) is a search method that assumes 794the user has a good heuristic for directing the search. A perfect 795heuristic would, of course, not require any search. However most 796heuristics are occasionally misleading. Limited Discrepancy Search 797follows the heuristic on almost every decision. The 798``discrepancy'' is a measure of the degree to which it fails to follow 799the heuristic. LDS starts searching with a discrepancy of $0$ (which 800means it follows the heuristic exactly). Each time LDS fails to find 801a solution with a given discrepancy, the discrepancy is increased and 802search restarts. In theory the search is complete, as eventually the 803discrepancy will become large enough to admit a solution, or cover 804the whole search space. In practice, however, it is only beneficial 805to apply LDS with small discrepancies. Subsequently, if no solution 806is found, other search methods should be tried. 807 808The definitive reference to LDS is: 809\begin{quote} 810Limited Discrepancy Search, Harvey and Ginsberg, 811pp.607-613, Proc. IJCAI'95 812\end{quote} 813 814This reference also suggests that combining LDS with Bounded Backtrack 815Search ({\em BBS}) yields good behaviour. Accordingly the {\eclipse} LDS 816module also supports BBS and its combination with LDS. 817 818% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 819\subsubsection{Limited Discrepancy Search using a Static Heuristic} 820% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 821 822We start by assuming a static heuristic, which is a complete 823assignment to the problem variables specified in advance of the 824search. The predicate supporting static LDS takes a list of variables 825(those which are to be labelled) and a list of values (one heuristic 826value for each variable, respectively). Each variable has a finite 827domain, and its heuristic value should belong to its domain (though 828the LDS search can still succeed even if this is not the case). 829 830The measure of discrepancy, in this case, is simply the number of 831variables labelled differently to the heuristic. Thus the maximum 832discrepancy is just the number of variables to be 833labelled. 834 835LDS search is implemented in the file 836{\tt lds.ecl} in the {\tt doc/examples} directory of your 837{\eclipse} installation. You can copy this file and load it with 838\begin{quote}\begin{alltt} 839:- use_module(lds). 840\end{alltt}\end{quote} 841Static LDS search is then available via the predicate 842{\bf static_lds(Var, Vals, Discrepancy)} whose arguments are 843\begin{description} 844\item[Vars] the list of problem variables. Some of the 845variables may already be instantiated. The others 846must have associated finite domains. 847\item[Vals] the list of values according to the heuristic. It 848must be the same length as Vars, and the heuristic 849must match the value, in case the variable is 850already instantiated. 851\item[Discrepancy] the discrepancy of the solution returned. Typically this 852is an output of the search (an integer between $0$ and the number of 853variables), but it can also be used as an input. 854\end{description} 855The finite domain library must be loaded, and the variables must have 856finite domains. An example invocation is: 857\begin{quote}\begin{alltt} 858?- [X,Y,Z]::1..3, X+Y+Z#=5, static_lds([X,Y,Z],[1,2,3],D). 859\end{alltt}\end{quote} 860The answers are returned on backtracking in the following order: 861\begin{itemize} 862\item 863X = 1 864Y = 2 865Z = 2 866D = 1 867\item 868X = 1 869Y = 1 870Z = 3 871D = 1 872\item 873X = 1 874Y = 3 875Z = 1 876D = 2 877\item 878X = 2 879Y = 2 880Z = 1 881D = 2 882\item 883X = 2 884Y = 1 885Z = 2 886D = 3 887\item 888X = 3 889Y = 1 890Z = 1 891D = 3 892\end{itemize} 893 894 895% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 896\subsubsection{Limited Discrepancy Search using a Dynamic Heuristic} 897% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 898 899Often the heuristic value is calculated on the fly, during search. To 900cope with this we use the {\eclipse} ``tentative value'' facility in 901{\eclipse}'s {\em repair} library. 902The heuristic is stored with the variable as its tentative value. 903 904The tentative value may be changed during search. For example if a 905variable is instantiated as a consequence of constraint propagation 906during search, its tentative value is automatically changed to its 907actual value. 908 909Dynamic LDS search is available in {\eclipse} via the predicate 910{\bf dynamic_lds(Vars, Discrepancy)}. 911Each variable in the list of variables {\em Vars} 912must have a tentative value. 913 914An example invocation is: 915\begin{quote}\begin{alltt} 916?- [X,Y,Z]::1..3, [X,Y,Z] tent_set [1,2,3], X+Y+Z#=5, 917 dynamic_lds([X,Y,Z],D). 918\end{alltt}\end{quote} 919The answers are returned on backtracking in the following order. 920Notice that the first solution has a discrepancy of $0$, because 921constraint propagation instantiates the third variable to $2$, 922thus changing its tentative value from $3$ to $2$. 923\begin{itemize} 924\item 925X = 1 926Y = 2 927Z = 2 928D = 0 929\item 930X = 1 931Y = 1 932Z = 3 933D = 1 934\item 935X = 1 936Y = 3 937Z = 1 938D = 1 939\item 940X = 2 941Y = 2 942Z = 1 943D = 1 944\item 945X = 3 946Y = 1 947Z = 1 948D = 1 949\item 950X = 2 951Y = 1 952Z = 2 953D = 2 954\end{itemize} 955 956% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 957\subsubsection{LDS and BBS Combined} 958% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 959 960The two search techniques, BBS and LDS, can be merged quite simply in 961{\eclipse}, so that for each discrepancy level only a limited number of 962backtracks are allowed. 963 964An example invocation is: 965\begin{quote}\begin{alltt} 966?- Vars=[X,Y,Z], Vars::1..3, Vars tent_set [1,2,3], X+Y+Z#=6, 967 bbs_dynamic_lds(Vars,4,D). 968\end{alltt}\end{quote} 969The answers are returned on backtracking in the following order: 970\begin{itemize} 971\item 972X = 1 973Y = 2 974Z = 3 975D = 0 976\item 977X = 1 978Y = 3 979Z = 2 980D = 1 981\item 982X = 2 983Y = 2 984Z = 2 985D = 1 986\item 987X = 3 988Y = 2 989Z = 1 990D = 1 \\ 991Backtrack limit exceeded 992\item 993X = 2 994Y = 1 995Z = 3 996D = 2 \\ 997Backtrack limit exceeded 998\end{itemize} 999 1000%---------------------------------------------------------------------- 1001\section{Local Search Methods} 1002%---------------------------------------------------------------------- 1003 1004In the following we discuss several examples of move-based (as opposed 1005to constructive search) methods. These methods have originally been developed 1006for unconstrained problems, but they work for certain classes of 1007constrained problems as well. 1008 1009From a technical point of view, the main difference between tree search 1010and move-based search is that tree search is monotonic in the sense that 1011constraints get tightened when going down the tree, and this is undone 1012in reverse order when backing up the tree to a parent node. This fits 1013well with the idea of constraint propagation. 1014In a move-based search, the main characteristics is that a move produces 1015a small change, but it is not clear what effect this will have on the 1016constraints. They may become more or less satisfied. 1017We therefore need implementations of the constraints that monitor changes 1018rather than propagate instantiations. This functionality is provided by 1019the {\eclipse} repair library which is used in the following examples. 1020The repair library is decribed in more detail in the 1021{\eclipse} Constraint Library Manual. 1022 1023The {\eclipse} code for all the examples in this section is available 1024in the file {\tt knapsack_ls.ecl} in the {\tt doc/examples} directory of your 1025{\eclipse} installation. 1026 1027% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1028\subsection{The Knapsack Example} 1029% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1030 1031We will demonstrate the local search methods using the well-known 1032knapsack problem. The problem is the following: given a container of 1033a given capacity and a set of items with given weights and profit 1034values, find out which items have to be packed into the container 1035such that their weights do not exceed the container's capacity and 1036the sum of their profits is maximal. 1037 1038The model for this problem involves N boolean variables, a single 1039inequality constraint to ensure the capacity restriction, and an 1040equality to define the objective function. 1041 1042The tree search program for this problem looks as follows: 1043\begin{quote}\begin{alltt} 1044:- lib(fd). 1045knapsack(N, Profits, Weights, Capacity, Profit) :- 1046 length(Vars, N), % N boolean variables 1047 Vars :: 0..1, 1048 Capacity #>= Weights*Vars, % the single constraint 1049 Profit #= Profits*Vars, % the objective 1050 min_max(labeling(Vars), -Profit). % branch-and-bound search 1051\end{alltt}\end{quote} 1052At the end of the problem modelling code, a standard branch-and-bound tree search 1053(min_max) is invoked in the last line of the code. 1054The parameters mean 1055\begin{itemize} 1056\item {\tt N} - the number of items (integer) 1057\item {\tt Profits} - a list of N integers (profit per item) 1058\item {\tt Weights} - a list of N integers (weight per item) 1059\item {\tt Capacity} - the capacity of the knapsack (integer) 1060\item {\tt Opt} - the optimal result (output) 1061\end{itemize} 1062To be able to use local search, we load the {\bf repair} library and 1063change the problem setup slightly. 1064At the end, we invoke a local search routine instead of tree search: 1065\begin{quote}\begin{alltt} 1066:- lib(fd). 1067:- lib(repair). 1068knapsack(N, Profits, Weights, Capacity, Opt) :- 1069 length(Vars, N), 1070 Vars :: 0..1, 1071 Capacity #>= Weights*Vars r_conflict cap, 1072 Profit tent_is Profits*Vars, 1073 local_search(<extra parameters>, Vars, Profit, Opt). 1074\end{alltt}\end{quote} 1075We are now using 3 features from the repair-library: 1076\begin{description} 1077\item[Constraint annotation r\_conflict:] Constraints annotated in this 1078 way are constantly being monitored for satifying the global 1079 assignment, i.e. it is checked whether they would be satisfied 1080 if all variables were instantiated to their tentative values. 1081 Constraints that are not satisfied in this way appear in the 1082 specified {\em conflict set}. 1083 In the example, the single capacity constraint has been annotated with 1084 {\bf r\_conflict} and it will appear in the conflict set called 1085 {\tt cap} when violated. 1086\item[Result tent\_is ArithExpression:] This is similar to the is/2 built-in 1087 predicate, but it works on the variable's tentative values rather 1088 than requiring the variables to be instantiated. The result is 1089 delivered as the tentative value of Result. Any change of tentative 1090 value inside the ArithExpression leads to an update of the Result. 1091 In the example, the computation of the objective function has 1092 been changed to use {\bf tent\_is} because we want to have the 1093 objective value recomputed efficiently after every move. 1094\item[Tentative values:] Every variable has, apart from its domain, 1095 a tentative value which can be changed using tent\_set/2 and 1096 queried using tent\_get/2. We will use these inside the local search 1097 routine to implement the moves. 1098\end{description} 1099 1100 1101% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1102\newpage 1103\subsection{Search Code Schema} 1104% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1105 1106In the literature, e.g.\ in 1107\begin{quote} 1108Localizer: A Modeling Language for Local Search, 1109L. Michel and P. Van Hentenryck, Proceeding CP97, LNCS 1330, Springer 1997. 1110\end{quote} 1111local search methods are often characterised by 1112the the following nested-loop program schema: 1113\begin{quote}\begin{alltt} 1114local_search: 1115 set starting state 1116 while global_condition 1117 while local_condition 1118 select a move 1119 if acceptable 1120 do the move 1121 if new optimum 1122 remember it 1123 endwhile 1124 set restart state 1125 endwhile 1126\end{alltt}\end{quote} 1127The actual program codes in the following sections all follow this schema, 1128except that some methods (random walk and the tabu search) 1129are even simpler and use only a single loop with a single termination 1130condition. 1131 1132 1133%---------------------------------------------------------------------- 1134\newpage 1135\subsection{Random walk} 1136%---------------------------------------------------------------------- 1137 1138As a simple example of local search, let us look at a random walk 1139strategy. The idea is to start from a random tentative assignment of 1140variables to 0 (item not in knapsack) or 1 (item in knapsack), then to 1141remove random items (changing 1 to 0) if the knapsack's capacity is 1142exceeded and to add random items (changing 0 to 1) if there is 1143capacity left. We do a fixed number (MaxIter) of such steps and keep 1144track of the best solution encountered. 1145 1146Each step consists of 1147\begin{itemize} 1148\item Changing the tentative value of some variable, which in turn causes 1149 the automatic recomputation of the conflict constraint set 1150 and the tentative objective value. 1151\item Checking whether the move lead to a solution and whether this 1152 solution is better than the best one so far. 1153\end{itemize} 1154 1155Here is the {\eclipse} program. We assume that the problem has been set 1156up as explained above. The violation of the capacity constraint 1157is checked by looking at the conflict constraints. If there are no 1158conflict constraints, the constraints are all tentatively satisfied 1159and the current tentative values form a solution to the problem. 1160The associated profit is obtained by looking at the tentative value 1161of the Profit variable (which is being constantly updated by tent\_is). 1162\begin{quote}\begin{alltt} 1163random_walk(MaxIter, VarArr, Profit, Opt) :- 1164 init_tent_values(VarArr, random), % starting point 1165 ( for(_,1,MaxIter), % do MaxIter steps 1166 fromto(0, Best, NewBest, Opt), % track the optimum 1167 param(Profit,VarArr) 1168 do 1169 ( conflict_constraints(cap,[]) -> % it's a solution! 1170 Profit tent_get CurrentProfit, % what is its profit? 1171 ( 1172 CurrentProfit > Best % new optimum? 1173 -> 1174 printf("Found solution with profit %w%n", [CurrentProfit]), 1175 NewBest=CurrentProfit % yes, remember it 1176 ; 1177 NewBest=Best % no, ignore 1178 ), 1179 change_random(VarArr, 0, 1) % add another item 1180 ; 1181 NewBest=Best, 1182 change_random(VarArr, 1, 0) % remove an item 1183 ) 1184 ). 1185\end{alltt}\end{quote} 1186The auxiliary predicate {\tt init_tent_values} sets the tentative values 1187of all variables in the array randomly to 0 or 1: 1188The {\tt change_random} predicate changes a randomly selected variable with 1189a tentative value of 0 to 1, or vice versa. 1190Note that we are using an array, rather than a list of variables, to 1191provide more convenient random access. 1192The complete code and the auxiliary predicate definitions can be found 1193in the file {\tt knapsack_ls.ecl} in the {\tt doc/examples} directory of your 1194{\eclipse} installation. 1195 1196 1197%---------------------------------------------------------------------- 1198\subsection{Hill Climbing} 1199%---------------------------------------------------------------------- 1200 1201The following hill-climbing implementation is an instance of the nested 1202loop program schema introduced above. The idea is to start from 1203a configuration which is certainly a solution (the empty knapsack) 1204and do random uphill moves for at most MaxIter times. Then we restart 1205and try again: 1206\begin{quote}\begin{alltt} 1207hill_climb(MaxTries, MaxIter, VarArr, Profit, Opt) :- 1208 init_tent_values(VarArr, 0), % starting solution 1209 ( 1210 for(I,1,MaxTries), 1211 fromto(0, Opt1, Opt4, Opt), 1212 param(MaxIter,Profit,VarArr) 1213 do 1214 ( 1215 for(J,1,MaxIter), 1216 fromto(Opt1, Opt2, Opt3, Opt4), 1217 param(I,VarArr,Profit) 1218 do 1219 Profit tent_get PrevProfit, 1220 ( 1221 flip_random(VarArr), % try a move 1222 Profit tent_get CurrentProfit, 1223 CurrentProfit > PrevProfit, % is it uphill? 1224 conflict_constraints(cap,[]) % is it a solution? 1225 -> 1226 ( CurrentProfit > Opt2 -> % is it new optimum? 1227 printf("Found solution with profit %w%n", 1228 [CurrentProfit]), 1229 Opt3=CurrentProfit % accept and remember 1230 ; 1231 Opt3=Opt2 % accept 1232 ) 1233 ; 1234 Opt3=Opt2 % reject (move undone) 1235 ) 1236 ), 1237 init_tent_values(VarArr, 0) % restart 1238 ). 1239\end{alltt}\end{quote} 1240The move operator is implemented as follows. It chooses a random variable X 1241from the array of variables and changes its tentative value from 0 to 1 1242or from 1 to 0 respectively: 1243\begin{quote}\begin{alltt} 1244flip_random(VarArr) :- 1245 functor(VarArr, _, N), 1246 X is VarArr[random mod N + 1], 1247 X tent_get Old, 1248 New is 1-Old, 1249 X tent_set New. 1250\end{alltt}\end{quote} 1251Some further points are worth noticing: 1252\begin{itemize} 1253\item The move operation and the acceptance test 1254are within the condition part of the if-then-else construct. 1255As a consequence, if the acceptance test fails (the move either yields 1256no solution or does not improve the objective) the move is automatically 1257undone by backtracking. 1258\item To check whether the move is uphill, we retrieve the tentative 1259value of the Profit-variable before and after the move is done. 1260Remember that, since the move operator changes the tentative values of 1261some variable, the tent\_is/2 primitive will automatically 1262update the Profit variable. 1263\item As in the random walk example, constraint satisfaction is checked 1264by checking whether the conflict constraint set is empty. 1265\end{itemize} 1266 1267%---------------------------------------------------------------------- 1268\newpage 1269\subsection{Simulated Annealing} 1270%---------------------------------------------------------------------- 1271 1272Simulated Annealing is a slightly more complex variant of local search. 1273It follows the schema in figure \ref{figmovesearch} and uses the same 1274move operator as the hill-climbing example. 1275The differences are in the termination conditions and in the 1276acceptance criterion for a move. 1277The outer loop simulates the cooling process by reducing the temperature 1278variable T, the inner loop does random moves until MaxIter steps have been 1279done without improvement of the objective. 1280The acceptance criterion is the classical one for simulated annealing: 1281Uphill moves are always accepted, downhill moves with a probability 1282that decreases with the temperature. The search routine must be invoked 1283with appropriate start and end temperatures, they should roughly correspond 1284to the maximum and minimum profit changes that a move can incur. 1285\begin{quote}\begin{alltt} 1286sim_anneal(Tinit, Tend, MaxIter, VarArr, Profit, Opt) :- 1287 starting_solution(VarArr), % starting solution 1288 ( fromto(Tinit, T, Tnext, Tend), 1289 fromto(0, Opt1, Opt4, Opt), 1290 param(MaxIter,Profit,VarArr,Tend) 1291 do 1292 printf("Temperature is %d%n", [T]), 1293 ( fromto(MaxIter, J0, J1, 0), 1294 fromto(Opt1, Opt2, Opt3, Opt4), 1295 param(VarArr,Profit,T) 1296 do 1297 Profit tent_get PrevProfit, 1298 ( flip_random(VarArr), % try a move 1299 Profit tent_get CurrentProfit, 1300 exp((CurrentProfit-PrevProfit)/T) > frandom, 1301 conflict_constraints(cap,[]) % is it a solution? 1302 -> 1303 ( CurrentProfit > Opt2 -> % is it new optimum? 1304 printf("Found solution with profit %w%n", 1305 [CurrentProfit]), 1306 Opt3=CurrentProfit, % accept and remember 1307 J1=J0 1308 ; CurrentProfit > PrevProfit -> 1309 Opt3=Opt2, J1=J0 % accept 1310 ; 1311 Opt3=Opt2, J1 is J0-1 % accept 1312 ) 1313 ; 1314 Opt3=Opt2, J1 is J0-1 % reject 1315 ) 1316 ), 1317 Tnext is max(fix(0.8*T),Tend) 1318 ). 1319\end{alltt}\end{quote} 1320 1321%---------------------------------------------------------------------- 1322\newpage 1323\subsection{Tabu Search} 1324%---------------------------------------------------------------------- 1325Another variant of local search is tabu search. Here, a number of moves 1326(usually the recent moves) are remembered (the tabu list) to direct the 1327search. Moves are selected by an acceptance criterion, with a 1328different (generally stronger) acceptance crtierion for moves in the tabu 1329list. As in most local search methods there are many possible variants and 1330concrete instances of this basic idea. For example, how a move would be 1331added to or removed from the tabu list has to be specified, along with the 1332different acceptance criteria. 1333 1334In the following simple example, the tabu list has a length determined by 1335the parameter TabuSize. The local moves consist of either adding 1336the item with the best relative profit into the knapsack, or removing 1337the worst one from the knapsack. In both cases, the move gets rememebered 1338in the fixed-size tabu list, and the complementary move is forbidden 1339for the next TabuSize moves. 1340\begin{quote}\begin{alltt} 1341tabu_search(TabuSize, MaxIter, VarArr, Profit, Opt) :- 1342 starting_solution(VarArr), % starting solution 1343 tabu_init(TabuSize, none, Tabu0), 1344 ( fromto(MaxIter, I0, I1, 0), 1345 fromto(Tabu0, Tabu1, Tabu2, _), 1346 fromto(0, Opt1, Opt2, Opt), 1347 param(VarArr,Profit) 1348 do 1349 ( try_set_best(VarArr, MoveId), % try uphill move 1350 conflict_constraints(cap,[]), % is it a solution? 1351 tabu_add(MoveId, Tabu1, Tabu2) % is it allowed? 1352 -> 1353 Profit tent_get CurrentProfit, 1354 ( CurrentProfit > Opt1 -> % is it new optimum? 1355 printf("Found solution with profit %w%n", [CurrentProfit]), 1356 Opt2=CurrentProfit % accept and remember 1357 ; 1358 Opt2=Opt1 % accept 1359 ), 1360 I1 is I0-1 1361 ; 1362 ( try_clear_worst(VarArr, MoveId), % try downhill move 1363 tabu_add(MoveId, Tabu1, Tabu2) % is it allowed? 1364 -> 1365 I1 is I0-1, 1366 Opt2=Opt1 % reject 1367 ; 1368 I1=0, % no moves possible, stop 1369 Opt2=Opt1 % reject 1370 ) 1371 ) 1372 ). 1373\end{alltt}\end{quote} 1374 1375In practice, the tabu search forms only a skeleton around which a complex 1376search algorithm is built. An example of this is applying tabu search to 1377the job-shop problem, as described by Nowicki and Smutnicki ({\it A Fast 1378Taboo Search Algorithm for the Job Shop Problem}, Management Science/Vol.\ 137942, No.\ 6, June 1996). 1380 1381%---------------------------------------------------------------------- 1382\section{Hybrid Search Methods} 1383%---------------------------------------------------------------------- 1384 1385% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1386\subsection{Weak-commitment Search} 1387% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1388\subsubsection{Introduction} 1389% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1390 1391Weak-commitment Search (WCS) can be seen as one of the simplest form of 1392nogood learning. It was proposed by Yokoo, and the main reference is: 1393\begin{quote} 1394Makoto Yokoo, {\it Weak-commitment Search for Solving Constraint Satisfaction 1395Problems}, in AAAI'94, pg. 313-318. 1396\end{quote} 1397WCS starts by giving tentative 1398assignments to the variables of the problem. Labelling of the variables are 1399then performed, guided by a heuristic that considers the tentative 1400values. If a dead-end is reached, 1401where no value can be assigned to a variable without violating some 1402constraint, then the current search is abandoned, and a new search restarted 1403from scratch with an extra `nogood' constraint. This `nogood' constraint 1404remembers the previously assigned 1405values to the already labelled variables in the just abandoned search. This 1406combination of values lead to a dead-end, and will not be tried again in 1407the new search. This is in contrast to a conventional backtracking search, 1408where the search would not be entirely abandoned, but will try assigning 1409new alternative values to one (generally the last assigned) variable. The 1410`weak-commitment' refers to this feature of the search technique, wherein 1411it is not `strongly committed' to the current branch in the search-space as 1412in conventional backtracking search. The aim is that the search would not 1413be `stuck' exhaustively exploring a particular region of a search-space 1414that might not lead to a solution. The search is instead guided by the 1415`nogood' constraints, which are added (`learned') after each step. 1416 1417The min-conflict heuristic of Minton et al. is the heuristic used to 1418label variables. In this heuristics, a `probe' is performed when assigning 1419a value to a variable, in which all the values in the remaining domain of the 1420variable (i.e. values which causes no constraint violations with existing 1421assigned variables) are considered. The value chosen is the value that 1422causes the minimum conflict (constraint violation) with the tentative 1423values of the still unlabelled variables. 1424 1425% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1426\subsubsection{Using the WCS} 1427% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1428The code for the facilities described below is available 1429in the file {\tt wcs.ecl} in the {\tt doc/examples} directory of your 1430{\eclipse} installation. 1431\begin{quote}\begin{alltt} 1432:- compile(wcs). 1433\end{alltt}\end{quote} 1434The WCS is invoked by calling the \verb'wcs/2' predicate: 1435\begin{quote}\begin{alltt} 1436wcs(+Vars, ++Initial) 1437\end{alltt}\end{quote} 1438 1439\noindent 1440where \verb'Vars' is a list of variables that are to be labelled by the 1441search, and \verb'Initial' is a list of the initial tentative values that 1442are assigned to the variables. Before calling the procedure, the user must 1443already have set up the initial constraints so that the search can 1444proceed. During the search process, additional nogood constraints would be 1445added to direct the search. 1446 1447Two example usage of the search are given: 1) a search for potentially all 1448the solutions to the N-Queens problem, and 2) a search for the first 1449solution to the 3SAT problems, when given the constraints on the variables 1450in the form of Prolog facts. 1451 1452The 3SAT example is simpler in that it involves only nogood constraints: 1453the initial constraints on the variables are simply translated into nogood 1454constraints before \verb'wcs/2' is called. In the N-Queens example, 1455additional constraints on the placement of the queens have to be specified 1456before \verb'wcs/2' is called. 1457 1458The constraints that are specified for the WCS have to apply to both the 1459tentative and actual values of the variables. Tentative values are 1460implemented in this predicate using the repair library, and thus the 1461constraints have to be made known to the repair library. This is done using 1462the \verb'r_prop' annotation provided by the library. With this annotation, the 1463repair library would apply the constraint to the tentative values as well 1464as to the normal values. 1465 1466 1467% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1468\subsubsection{Implementation of WCS} 1469% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1470 1471The WCS implementation presented here is a simple and straight-forward 1472implementation in {\eclipse} of the basic algorithm presented by Yokoo. 1473The finite domains and the repair libraries were used. The finite domain 1474library was used to allow for the probing step, where all valid values for 1475a variable are tried. The repair library was used to allow for tentative 1476values to be associated with variables, as specified in the algorithm. 1477 1478The repair library is used in the following way: 1479 1480\begin{itemize} 1481\item Setting up the nogood constraints to apply to both the actual and 1482tentative values of the variables. This is done via the \verb'r_prop' 1483annotation. 1484\item Setting the tentative value of a variable, either initially, or 1485updating it at each restart. This is done using \verb'tent_set/2'. 1486\item Counting the number of constraint violations on the 1487tentative values for remaining unlabelled variables as a variable is 1488labelled to a particular value. This is done via 1489{\tt conflict_constraints/1}. 1490\end{itemize} 1491 1492The predicate also has to implement the probing and restart steps of the 1493WCS which replace the usual tree search strategy. The probing 1494step tries out all the possible valid values for a variable, and picks the 1495value which leads to the least number of conflicts (constraint violations) 1496with the tentative values in the unlabelled variables. This is done with 1497\verb'minimize/2' from the finite domains library: 1498 1499\begin{quote}\begin{alltt} 1500label(Var) :- 1501 minimize(( 1502 indomain(Var), 1503 conflict_constraints(Constraints), 1504 length(Constraints, L) ), L). 1505\end{alltt}\end{quote} 1506 1507The above tries out the available values of \verb'Var', collect the 1508constraints violations on the tentative variables using 1509\verb'conflict_constraints' from the repair library, and counts the number 1510of such constraints using \verb'length/2'. The value with the minimum 1511number of constraint violation is selected as the binding to \verb'Var' by 1512this procedure. 1513 1514The search restart itself is quite easy to implement in {\eclipse}, as the 1515just described labelling procedure, \verb'label/1', does not leave behind a 1516choice-point. Thus, when a dead-end is reached in labelling values, a 1517simple failure will cause the procedure to fail back to the beginning, 1518i.e. before any variable is labelled. The restart is then implemented by 1519specifically creating a choice-point at the start of the search, in the 1520\verb'do_search/2' predicate: 1521 1522\begin{quote}\begin{alltt} 1523do_search(Vars, _) :- 1524 try_one_step(Vars, Vars), 1525 % remember solution as a nogood so it would not be tried again 1526 remember_nogood(Vars). 1527do_search(Vars, N) :- 1528 % hit dead-end and failed, try again from start after recording nogoods 1529 add_nogood(Vars), % put in most recent nogood 1530 getval(nlabels, NL), 1531 printf("Restart %w - labelled %w%n",[N,NL]), 1532 N1 is N + 1, 1533 do_search(Vars, N1). 1534\end{alltt}\end{quote} 1535 1536\noindent 1537\verb'try_one_step/2' tries out one search, with the first argument 1538containing the variables remaining to be labelled (initially all the 1539variables), and the second argument being all the variables. This would 1540fail if the labelling hits a dead-end and fails. In this case, the second 1541clause of \verb'do_search/2' will be tried, in which a new search is 1542started. The only difference is that a new nogood constraint will be 1543remembered. Note that if \verb'try_one_step' succeeds, then a solution will 1544have been generated. To allow for the search of more solutions, this 1545solution is remembered as a nogood in the first clause of 1546\verb'do_search/2'. 1547 1548The main difficulty with implementing restart is to remember the values of 1549labelled variables so that it can be added as a nogood. The addition of the 1550nogood must be done {\it after\/} the failure and backtracking from the 1551dead-end, so that it will not be removed by the backtracking. The problem is 1552that the backtracking process will also remove the bindings on the labelled 1553variables. Thus, some means is required to remember the nogood values from 1554the point just before the failure, which can then be retrieved after the 1555failure to produce a new nogood constraint. Not only do the values 1556themselves have to be remembered, but which variable a particular value is 1557associated with has also to be remembered. This is done using the 1558non-logical variable feature of {\eclipse}, which allows copies of 1559terms to be stored across backtracking. A non-logical variable is 1560declared by a \verb'variable/1' declaration: 1561 1562\begin{quote}\begin{alltt} 1563:- local variable(varbindings). 1564\end{alltt}\end{quote} 1565 1566\noindent 1567which associates the name \verb'varbindings' with a non-logical value. The 1568value of this variable can then be set via \verb'setval/2' and accessed 1569via \verb'getval/2' built-ins. In order to remember 1570which variable is associated with which value, all the variables being 1571labelled, which is organised as a list, 1572are copied using \verb'setval/2'\footnote{The call to copy\_term/3 is used 1573to strip attributes (domains etc) from any remaining variables in Vars.}: 1574\begin{quote}\begin{alltt} 1575remember_nogood(Vars) :- 1576 copy_term(Vars, NVars, _), 1577 setval(varbindings,NVars). 1578\end{alltt}\end{quote} 1579 1580\noindent 1581To remember the current labellings when a dead-end is reached, so that a 1582new nogood constraint can be added for the restarted search, 1583\verb'remember_nogood/1' is called before the actual failure is allow to occur: 1584 1585\begin{quote}\begin{alltt} 1586label_next(Cons, Left0, Vars) :- 1587 pick_var(Cons, Left0, Var, Left1), 1588 incval(nlabels), 1589 ( label(Var) -> 1590 try_one_step(Left1, Vars) 1591 ; 1592 remember_nogood(Vars), 1593 fail 1594 ). 1595\end{alltt}\end{quote} 1596 1597\noindent 1598The routine first picks an unlabelled variable to label next, and 1599if it is successful, the 1600routine recursively tries to label the remaining unlabelled variables. If 1601not, \verb'label(Var)' fails, and the else case of the if-then-else is 1602called to remember the nogoods before failing. 1603 1604As already described, a new nogood constraint is added by the 1605\verb'add_nogood/1' predicate, as shown below: 1606 1607\begin{quote}\begin{alltt} 1608add_nogood(NewConfig) :- 1609 getval(varbindings, Partial), 1610 (foreach(P, Partial), foreach(V,NewConfig), 1611 fromto(NoGoods,NG0, NG1, []), fromto(NGVars,NGV0,NGV1,[]) do 1612 (nonvar(P) -> 1613 V tent_set P, 1614 NG0 = [P|NG1], 1615 NGV0 = [V|NGV1] 1616 ; NG0 = NG1, % keep old tentative value 1617 NGV0 = NGV1 1618 ) 1619 ), 1620 NoGoods ~= NGVars r_prop. % no good 1621\end{alltt}\end{quote} 1622 1623If a variable had been labelled in the previous search, the labelled value 1624becomes the tentative value. Otherwise, the variable retains the original 1625tentative value. 1626 1627The nogood constraints are implemented via the built-in sound difference 1628operator, \verb'~=/2'. For example, 1629 1630\begin{quote}\begin{alltt} 1631[A,B,C] ~= [1,2,3] 1632\end{alltt}\end{quote} 1633 1634\noindent 1635states that the variables \verb'A', \verb'B' and \verb'C' 1636cannot take on the values of \verb'1', \verb'2' and \verb'3' respectively 1637at the same time. The operator will fail when \verb'[A,B,C]' becomes ground 1638and take on the values \verb'[1,2,3]'. If any of the variables take on a 1639value different from what is specified, \verb'~=/2' will (eventually) 1640succeed. The operator thus acts passively, waiting for the variables to be 1641instantiated and then check if they are taking on the `nogood' values, and 1642does not propagate or deduce any further information. 1643 1644The algorithm described by Yokoo does not specify how the next variable is 1645selected for labelling. In this routine, it is done by the 1646\verb'pick_var/4' predicate: 1647 1648\begin{quote}\begin{alltt} 1649pick_var(Cons, Left0, Var, Left) :- 1650 term_variables(Cons, Vars0), 1651 deleteffc(Var0, Vars0, Vars1), 1652 (is_validvar(Var0, Left0, Left) -> 1653 Var = Var0 ; pick_var1(Vars1, Left0, Var, Left) 1654 ). 1655\end{alltt}\end{quote} 1656 1657\noindent 1658The next variable to be labelled is chosen from the set of variables whose 1659tentative values are causing conflict. The repair library maintains the 1660(repair) constraints which are causing conflict, and any variable which are 1661causing conflict will occur in these constraints. The set of conflicting 1662repair constraints is passed to \verb'pick_var/4' in the first argument: 1663\verb'Cons'. \verb'term_variables' is used to obtain all the variables that 1664occur in these constraints. The fd predicate \verb'deleteffc' is then used 1665to select a variable (picking the one with the smallest domain and most 1666constraints), and then this variable is checked to make sure that it is 1667valid variable to be labelled, i.e.\ that it is one of the variables to be 1668labelled. The reason for this check is that it is expected that the WCS 1669routine will be used as part of a larger program, and the program may use 1670the repair library itself, and thus \verb'Cons' may contain constraints 1671unrelated to the WCS labelling. 1672 1673% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1674\subsubsection{Improving Implementation of Nogoods} 1675% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - 1676 1677As already stated, the built-in \verb'~=/2' used for nogood constraints is 1678passive. More powerful propagation can be added to the nogood constraint if 1679the constraint is defined by the user. To try this out, a somewhat more 1680powerful constraint was tried out. This constraint does forward checking, 1681in that when only one variable specified in a nogood remains unlabelled, 1682and the labelled variables are labelled to the values specified by the 1683nogood, the constraint that this last variable cannot take on its nogood 1684value can be propagated. This increase the efficiency of the search in many 1685cases, although at the price of a slightly more complex implementation 1686of the nogood constraint. 1687 1688The nogood constraint is implemented as shown: 1689\begin{quote}\begin{alltt} 1690nogood([X|Xs], [V|Vs]) :- 1691 ( X==V -> nogood(Xs, Vs) 1692 ; var(X) -> nogood(Xs, Vs, X, V) 1693 ; true 1694 ). 1695 1696 nogood([], [], X1, V1) :- X1 ## V1. 1697 nogood([X|Xs], [V|Vs], X1, V1) :- 1698 ( X==V -> nogood(Xs, Vs, X1, V1) 1699 ; var(X) -> suspend(nogood([X1,X|Xs], [V1,V|Vs]), 3, X-X1->inst) 1700 ; true 1701 ). 1702\end{alltt}\end{quote} 1703\noindent 1704The nogood-constraint is set up by 1705\begin{quote}\begin{alltt} 1706nogood(NGVars, NoGoods) r_prop. 1707\end{alltt}\end{quote} 1708The implementation checks whether NGVars matches NoGoods and causes 1709failure if this is the case. If a non-matching variable-value pair is 1710encountered, the constraint disappears. If a variable is encountered, 1711nogood/4 continues checking, and if the variable turns out to be the only 1712one, the corresponding values gets removed from its domain. 1713If a second variable is encountered, the constraint re-suspends until 1714at least one of them gets instantiated. 1715 1716Obviously, this is still a relatively naive implementation of the 1717nogood-technique. As the number of nogoods grows, implementing them 1718via indivudual constraints will become more and more infeasible, and 1719optimisation techniques like merging and indexing of the nogoods 1720will be needed. 1721 1722\end{document} 1723