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