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\chapter{Repair and Local Search}
24%HEVEA\cutdef[1]{section}
25\label{chaprepair}
26
27\section{Motivation}
28Constraint logic programming uses logical variables.  This means that
29when a variable is instantiated, its value must satisfy all the
30constraints on the variable.  For example if the program includes the
31constraint $X>=2$, then any attempt to instantiate $X$ to a value less
32than $2$ will fail.
33
34However, there are various contexts and methods in which it is useful
35to associate (temporarily) a value with a variable that does not
36satisfy all the constraints on the variable.
37Generally this is true of {\tt repair} techniques.
38These methods start with a
39complete, infeasible, assignment of values to variables and
40change the values of the variables until a feasible assignment is
41found.
42
43Repair methods are useful in the case where a problem has been solved,
44but subsequently external changes to the problem render the solution
45infeasible. This is the normal situation in scheduling applications,
46where machines and vehicles break down, and tasks are delayed.
47
48Repair methods are also useful for solving problems which can be
49broken down into quasi-independent simpler subproblems.  Solutions
50to the subproblems which are useful for solving the complete problem, 
51may not be fully compatible with each other, or even completely
52feasible with respect to the full problem.
53
54Finally there are techniques such as conflict minimisation which seek
55solutions 
56that minimise infeasibility.
57These techniques can be treated as optimisation algorithms, whose
58constraints are wrapped into the optimisation function. 
59However they can also be treated as repair problems, which
60means that the 
61constraints can propagate actively during problem solving.
62
63\quickref{Uses of Repair}{Repair is used for:
64\begin{itemize}
65\item Re-solving problems which have been modified
66\item Combining subproblem solutions and algorithms
67\item Implementing local search
68\item Implementing powerful search heuristics
69\end{itemize}
70}
71
72\section{Syntax}
73\index{repair}
74\subsection{Setting and Getting Tentative Values}
75With the {\tt repair} library each variable can be given a {\em
76tentative} value.  This is different from instantiating the variable.
77Rather the tentative value is a piece of updatable information
78associated with the variable.
79The tentative value can be changed repeatedly during search, not just
80on backtracking.  
81The value is set using the syntax \verb0tent_set0, and retrieved using
82\verb0tent_get0. 
83For example the following query writes first $1$ and then $2$:
84\begin{quote}
85\begin{verbatim}
86?- X tent_set 1, 
87   X tent_get Tent1, 
88   writeln(Tent1), 
89   X tent_set 2,
90   X tent_get Tent2,
91   writeln(Tent2).
92\end{verbatim}
93\end{quote}
94Throughout this query $X$ remains a variable.
95
96A tentative variable may violate constraints.
97The following query writes \verb0succeed0, because
98setting the tentative value to $1$ does not cause a failure:
99\begin{quote}
100\begin{verbatim}
101?- X $> 2,
102   X tent_set 1,
103   writeln(succeed).
104\end{verbatim}
105\end{quote}
106
107\subsection{Building and Accessing Conflict Sets}
108\index{conflict sets}
109The relation between constraints and tentative values can be
110maintained in two ways.
111The first method is by {\em monitoring} a constraint for conflicts. 
112\begin{quote}
113\begin{verbatim}
114?- X $> 2 r_conflict myset,
115   X tent_set 1,
116   writeln(succeed).
117\end{verbatim}
118\end{quote}
119This query also succeeds - but additionally it creates a {\em conflict
120set} named 
121\verb0myset0.  Because $X \$> 2$ is violated by the tentative value of
122$X$, the constraint is recorded in the conflict set.  The conflict set
123written out by the following query is \verb0[X{1} $> 2]0:
124\begin{quote}
125\begin{verbatim}
126?- X $> 2 r_conflict myset,
127   X tent_set 1,
128   conflict_constraints(myset,Conflicts),
129   writeln(Conflicts).
130\end{verbatim}
131\end{quote}
132The conflict can be {\em repaired} by changing the tentative value of
133the variable which causes it:
134\begin{code}
135?- X $> 2 r_conflict myset,
136   X tent_set 1,
137   conflict_constraints(myset,Conflicts),
138   X tent_set 3,
139   conflict_constraints(myset,NoConflicts).
140\end{code}
141This program instantiates \verb0Conflicts0 to \verb0[X{1} $> 2]0,
142but \verb0NoConflicts0 is instantiated to \verb0[]0.
143
144\subsection{Propagating Conflicts}
145Arithmetic equality (\verb0=:=0, \verb0$=0) constraints, instead of
146monitoring for conflicts, 
147can be maintained by propagating tentative values.  
148To do so, they must be rewritten in a functional syntax.
149Consider the constraint \verb0X =:= Y+10.
150For propagation of tentative values, this must
151be rewritten in the form  \verb0X tent_is Y+10.
152If the tentative value of $Y$ is set to $1$, then this will be
153propagated to the tentative value
154of $X$.  The following query writes out the value $2$.
155\begin{quote}
156\begin{verbatim}
157?- X tent_is Y+1,
158   Y tent_set 1,
159   X tent_get(TentX),
160   writeln(TentX).
161\end{verbatim}
162\end{quote}
163
164Each time the tentative value of $Y$ is changed, the value of $X$ is
165kept in step, so the following writes out the value $3$:
166\begin{quote}
167\begin{verbatim}
168?- X tent_is Y+1,
169   Y tent_set 1,
170   Y tent_set 2,
171   X tent_get(TentX),
172   writeln(TentX).
173\end{verbatim}
174\end{quote}
175
176\index{tent\_set/2}
177\index{tent\_get/2}
178\index{r\_conflict/2}
179\index{conflict\_constraints/2}
180\index{tent\_is/2}
181\quickref{Syntax}{Repair supports the 
182following primitives:
183\begin{itemize}
184\item {\tt tent_set/2}
185\item {\tt tent_get/2}
186\item {\tt r_conflict/2}
187\item {\tt conflict_constraints/2}
188\item {\tt tent_is/2}
189\end{itemize}
190(and some others that are not covered in this tutorial).
191}
192
193\section{Repairing Conflicts}
194\index{conflict minimisation}
195If all the constraints of a problem are monitored for conflicts, then
196the problem can be solved by: 
197\begin{itemize}
198\item
199Finding an initial assignment of tentative values for all the problem
200variables
201\item
202Finding a constraint in conflict, and labelling a variable in this
203constraint
204\item
205Instantiating the remaining variables to their tentative values, when
206there are no more constraints in conflict
207\end{itemize}
208
209Consider a satisfiability problem with each clause represented by an
210{\tt ic} constraint, whose form is illustrated by the following example:  
211\verb0(X1 or neg X2 or X3 $= 10.
212This represents the clause $X1 \vee \neg X2 \vee X3$.
213
214To apply conflict minimisation to this problem use the predicate:
215\begin{itemize}
216\item \verb0tent_init0 to find an initial solution 
217\item \verb0conflict_constraints0 and \verb0term_variables0 to find a
218variable to label
219\item \verb0set_to_tent0 to set the remaining variables to their
220tentative values
221\end{itemize}
222The code is as follows:
223\begin{code}
224prop_sat_1(Vars) :-
225    Vars = [X1,X2,X3],
226    tent_init(Vars),
227    (X1 or neg X2 or X3 \$= 1) r_conflict cs,
228    (neg X1 or neg X2 \$= 1) r_conflict cs,
229    (X2 or neg X3 \$= 1) r_conflict cs,
230    min_conflicts(Vars).
231
232tent_init(List) :-
233    ( foreach(Var,List) do Var tent_set 1 ).
234
235min_conflicts(Vars) :-
236    conflict_constraints(cs,List), 
237    ( List = [] -> set_to_tent(Vars) ;
238      List = [Constraint|_] ->
239        term_variables(Constraint,[Var|_]),
240        guess(Var),
241        min_conflicts(Vars)
242    ).
243
244guess(0).
245guess(1).
246
247set_to_tent(Term) :-
248   Term tent_get Tent,
249   Term = Tent.
250\end{code}
251
252The value choice predicate \verb0guess0 is naive.  Since the variable
253occurs in a conflict constraint it would arguably be better to label
254it to another value.  This would be implemented as follows:
255\begin{code}
256guess(Var) :-
257    Var tent_get Value,
258    ( Value = 0 -> (Var=1 ; Var=0) 
259    ; Value = 1 -> (Var=0 ; Var=1)
260    ).
261\end{code}
262
263\subsection{Combining Repair with IC Propagation}
264To illustrate a combination of {\tt repair} with {\tt ic} propagation
265we tackle a scheduling example.
266The problem involves tasks with unknown start times, and known
267durations, which are related by a
268variety of temporal constraints.
269These temporal constraints are handled, for the purposes of this
270example, by {\tt ic}.
271The temporal constraints are encoded thus:
272\begin{code}
273before(TimePoint1,Interval,TimePoint2) :-
274    TimePoint1+Interval #=< TimePoint2.
275\end{code}
276\verb0TimePoint10 and \verb0TimePoint20 are variables (or numbers),
277but we assume, for this example, that the
278\verb0Interval0 is a number. 
279This constraint can enforce a minimum separation between start times,
280or a maximum separation (if the \verb0Interval0 is negative).  It can
281also enforce constraints between end times, by adjusting the
282\verb0Interval0 to account for the task durations.
283
284Additionally we assume that certain tasks require the same resource and
285cannot therefore proceed at the same time.  The resource
286constraint is encoded thus:
287\begin{code}
288noclash(Start1,Duration1,Start2,_) :-
289    Start2 #>= Start1+Duration1.
290noclash(Start1,_,Start2,Duration2) :-
291    Start1 #>= Start2+Duration2.
292\end{code}
293
294Suppose the requirement is to complete the schedule as early as
295possible.
296To express this we introduce a last time point \verb0End0 which is
297constrained to come after all the tasks.
298Ignoring the resource constraints, the temporal constraints are easily
299handled by {\tt ic}.
300The optimal solution is obtained simply by posting the temporal
301constraints and then instantiating each start
302time to the lowest value in its domain.
303
304To deal with the resource constraints conflict minimisation is used.
305The least (i.e.\ optimal) value in the domain of each variable is
306chosen as its tentative value, at each node of the search tree.
307
308To fix a constraint in conflict, we simply invoke its nondetermistic
309definition, and 
310{\eclipse} then unfolds the first clause and sends the new temporal
311constraint \verb0Start2 #>= Start1+Duration10 to {\tt ic}.
312On backtracking, the second clause will be unfolded instead.
313
314After fixing a resource constraint, and posting a new temporal
315constraint, {\tt ic} propagation takes place, and then the tentative
316values are changed to the new {\tt ic} lower bounds.
317
318The code is simply this:
319\begin{code}
320:- lib(ic), lib(repair), lib(branch_and_bound).
321schedule(Starts,End) :-
322    Starts = [S1,S2,...,End],
323    Starts :: 0..1000,
324    before(S2,5,S1),
325    before(S1,8,End),
326    ...
327    noclash(S1,4,S2,8) r_conflict resource_cons,
328    ...
329    minimize(repair_ic(Starts),End).
330
331repair_ic(Starts) :-
332    set_tent_to_min(Starts),
333    conflict_constraints(resource_cons,List),
334    ( List = [] -> 
335        set_to_tent(Starts)
336    ; List = [Constraint|_] ->
337        call(Constraint),
338        repair_ic(Starts)
339    ).
340
341set_tent_to_min(Vars) :-
342    (  foreach(Var,Vars) 
343    do 
344         get_min(Var,Min),
345         Var tent_set Min
346    ).
347\end{code}
348This code is much more robust than the traditional code
349for solving the bridge scheduling example from \cite{VanHentenryck}.
350The code is in the examples directory file \verb0bridge_repair.pl0.
351
352\index{probing}
353This algorithm uses the {\tt ic} solver to:
354\begin{itemize}
355\item Enforce the consistency of the temporal constraints
356\item Set the tentative values to an optimal solution (of this
357relaxation of the original problem)
358\end{itemize} 
359This technique is called {\em probing}.
360The use of the {\tt eplex} solver, instead of {\tt ic} for probing is
361described in  
362chapter \ref{chaphybrid} below.
363
364\quickref{Conflict Minimisation}{Repair naturally supports conflict
365minimisation.
366This algorithm can be combined with other solvers, such as {\tt ic},
367and with optimization.
368}
369
370\section{Introduction to Local Search}
371\subsection{Changing Tentative Values}
372From a technical point of view, the main difference between tree search
373and {\em local} (or move-based) search is that tree search adds
374assignments while local search changes them.   
375During tree search
376constraints get tightened when going down the tree, and this is undone
377in reverse order when backing up the tree to a parent node. This fits
378well with the idea of constraint propagation.
379
380It is characteristic of local search that a move produces
381a small change, but it is not clear what effect this will have on the
382constraints. They may become more or less satisfied.
383We therefore need implementations of the constraints that monitor changes
384rather than propagate instantiations. 
385
386Local search can be implemented quite naturally in {\eclipse} using the
387{\tt repair} library.
388In essence, the difference between implementing tree search techniques
389and local 
390search in {\eclipse} is that, instead of instantiating variables during
391search, local search progresses by changing {\em tentative} values of
392variables.
393For the satisfiability example of the last section, we can change
394\verb0min_conflicts0 to
395\verb0local_search0 by simply replacing the \verb0guess0 predicate by the
396predicate  \verb0move0:
397\begin{code}
398local_search(Vars) :-
399    conflict_constraints(cs,List), 
400    ( List = [] -> 
401        set_to_tent(Vars)
402    ; List = [Constraint|_] ->
403        term_variables(Constraint,[Var|_]),
404        move(Var),
405        local_search(Vars)
406    ).
407
408move(Var) :-
409    Var tent_get Value,
410    NewValue is (1-Value),
411    Var tent_set NewValue.
412\end{code}
413
414There is no guarantee that this move will reach a better assignment,
415since {\em NewValue} may violate more constraints than the
416original {\em Value}.
417
418\subsection{Hill Climbing}
419To find a neighbour which overall increases the number of satisfied
420constraints we could replace \verb0local_search0 with the predicate
421\verb0hill_climb0: 
422\begin{code}
423hill_climb(Vars) :-
424    conflict_constraints(cs,List), 
425    length(List,Count),
426    ( Count = 0 -> 
427          set_to_tent(Vars) 
428    ; try_move(List,NewCount), NewCount < Count ->
429          hill_climb(Vars)
430    ;
431          write('local optimum: '), writeln(Count)
432    ).
433
434try_move(List,NewCount) :-
435      select_var(List,Var),
436      move(Var),
437      conflict_constraints(cs,NewList), 
438      length(NewList,NewCount).
439
440select_var(List,Var) :-
441    member(Constraint,List),
442    term_variables(Constraint,Vars),
443    member(Var,Vars).
444\end{code}
445Some points are worth noticing:
446\begin{itemize}
447\item Constraint satisfaction is recognised
448by finding that the conflict constraint set is empty.
449\item The move operation and the acceptance test
450are within the condition part of the if-then-else construct.
451As a consequence, if the acceptance test fails (the move does not
452improve the objective) the move is automatically 
453undone by backtracking.
454\end{itemize}
455
456The code for \verb0try_move0 is very inefficient, because it
457repeatedly goes through the whole list of conflict constraints to
458count the number of constraints in conflict.
459The facility to propagate tentative values supports more efficient
460maintenance of the number constraints in conflict.  
461This technique is known as maintenance of {\em invariants} (see
462\cite{Localizer}).
463For the propositional satisfiability example we can maintain the
464number of satisfied clauses to make the hill climbing implementation
465more efficient. 
466
467The following program not only monitors each clause for conflict, but
468it also records in a boolean variable whether the clause is satisfied.
469Each tentative assignment to the variables is propagated to the
470tentative value of the boolean.
471The sum of the boolean \verb0BSum0 records for any tentative
472assignment of the propositional variables, the number of satisfied
473clauses.
474This speeds up hill climbing because, after each move, its effect on
475the number of satisfied clauses is automatically computed by the
476propagation of tentative values.
477\begin{code}
478prop_sat_2(Vars) :-
479    Vars = [X1,X2,X3],
480    tent_init(Vars),
481    clause_cons(X1 or neg X2 or X3,B1),
482    clause_cons(neg X1 or neg X2,B2),
483    clause_cons(X2 or neg X3,B3),
484    BSum tent_is B1+B2+B3,
485    hill_climb_2(Vars,BSum).
486
487clause_cons(Clause,B) :- 
488    Clause $= 1 r_conflict cs,
489    B tent_is Clause.
490
491hill_climb_2(Vars,BSum) :-
492    conflict_constraints(cs,List),
493    BSum tent_get Satisfied,
494    ( List=[] -> 
495          set_to_tent(Vars) 
496    ; select_var(List,Var), move(Var), tent_get(BSum) > Satisfied ->
497          hill_climb_2(Vars,BSum)
498    ;
499          write('local optimum: '), writeln(Count)
500    ).
501\end{code}
502
503To check whether the move is uphill, we retrieve the tentative
504value of \verb0BSum0 before and after the move is done. 
505Remember that, since the move operator changes the tentative values of
506some variable, the \verb0tent_is0 primitive will automatically
507update the \verb0BSum0 variable.
508
509This code can be made more efficent by recording more
510invariants, as described in \cite{cp99wkshoptalk}.
511
512\quickref{Local Search and Invariants}{Local 
513search can be implemented
514in {\eclipse} with the {\tt repair} library.
515Invariants can be implemented by tentative value propagation using
516{\tt tent_is/2}.
517}
518
519%----------------------------------------------------------------------
520\section{More Advanced Local Search Methods}
521\index{local search}
522%----------------------------------------------------------------------
523
524In the following we discuss several examples of local search
525methods. These methods have originally been developed 
526for unconstrained problems, but they work for certain classes of
527constrained problems as well.
528
529The {\eclipse} code for all the examples in this section is available
530in the file {\tt knapsack_ls.ecl} in the {\tt doc/examples} directory of your
531{\eclipse} installation.
532
533% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
534\subsection{The Knapsack Example}
535\index{knapsack}
536% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
537
538We will demonstrate the local search methods using the well-known
539knapsack problem. The problem is the following: given a container of
540a given capacity and a set of items with given weights and profit
541values, find out which items have to be packed into the container
542such that their weights do not exceed the container's capacity and
543the sum of their profits is maximal.
544
545The model for this problem involves N boolean variables, a single
546inequality constraint to ensure the capacity restriction, and an
547equality to define the objective function.
548
549\begin{code}
550:- lib(ic).
551:- lib(repair).
552knapsack(N, Profits, Weights, Capacity, Opt) :-
553        length(Vars, N),
554        Vars :: 0..1,
555        Capacity #>= Weights*Vars  r_conflict cap,
556        Profit tent_is Profits*Vars,
557        local_search(<extra parameters>, Vars, Profit, Opt).
558\end{code}
559The parameters mean
560\begin{itemize}
561\item {\tt N} - the number of items (integer)
562\item {\tt Profits} - a list of N integers (profit per item)
563\item {\tt Weights} - a list of N integers (weight per item)
564\item {\tt Capacity} - the capacity of the knapsack (integer)
565\item {\tt Opt} - the optimal result (output)
566\end{itemize}
567
568
569% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
570\subsection{Search Code Schema}
571% - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
572
573In the literature, e.g.\ in \cite{Localizer},
574%\begin{quote}
575%Localizer: A Modeling Language for Local Search,
576%L. Michel and P. Van Hentenryck, Proceeding CP97, LNCS 1330, Springer 1997.
577%\end{quote}
578local search methods are often characterised by
579the the following nested-loop program schema:
580{\samepage
581\begin{code}
582local_search:
583     set starting state
584     while global_condition
585         while local_condition
586             select a move
587             if acceptable
588                 do the move
589                 if new optimum
590                     remember it
591         endwhile
592         set restart state
593     endwhile
594\end{code}
595}
596We give three examples of local search methods coded in {\eclipse} that
597follow this schema: {\em random walk}, {\em simulated annealing} and
598{\em tabu search}.
599Random walk and tabu search do not use the full schema, as there is
600only a single loop with a single termination condition.
601
602
603%----------------------------------------------------------------------
604\subsection{Random walk}
605\index{random walk}
606%----------------------------------------------------------------------
607
608The idea of Random walk is to start from a random tentative assignment of
609variables to 0 (item not in knapsack) or 1 (item in knapsack), then to
610remove random items (changing 1 to 0) if the knapsack's capacity is
611exceeded and to add random items (changing 0 to 1) if there is
612capacity left.  We do a fixed number (MaxIter) of such steps and keep
613track of the best solution encountered.
614
615Each step consists of:
616\begin{itemize}
617\item Changing the tentative value of some variable, which in turn causes
618        the automatic recomputation of the conflict constraint set
619        and the tentative objective value.
620\item Checking whether the move lead to a solution and whether this
621        solution is better than the best one so far.
622\end{itemize}
623
624Here is the {\eclipse} program. We assume that the problem has been set
625up as explained above. The violation of the capacity constraint
626is checked by looking at the conflict constraints. If there are no
627conflict constraints, the constraints are all tentatively satisfied
628and the current tentative values form a solution to the problem.
629The associated profit is obtained by looking at the tentative value
630of the Profit variable (which is being constantly updated by {\tt tent_is}).
631{\small\samepage
632\begin{code}
633random_walk(MaxIter, VarArr, Profit, Opt) :-
634        init_tent_values(VarArr, random),       % starting point
635        (   for(_,1,MaxIter),                   % do MaxIter steps
636            fromto(0, Best, NewBest, Opt),      % track the optimum
637            param(Profit,VarArr)
638        do
639            ( conflict_constraints(cap,[]) ->   % it's a solution!
640                Profit tent_get CurrentProfit,  % what is its profit?
641                (
642                    CurrentProfit > Best        % new optimum?
643                ->
644                    printf("Found solution with profit %w%n", [CurrentProfit]),
645                    NewBest=CurrentProfit       % yes, remember it
646                ;
647                    NewBest=Best                % no, ignore
648                ),
649                change_random(VarArr, 0, 1)     % add another item
650            ;
651                NewBest=Best,
652                change_random(VarArr, 1, 0)     % remove an item
653            )
654        ).
655\end{code}
656}
657The auxiliary predicate {\tt init_tent_values} sets the tentative values
658of all variables in the array randomly to 0 or 1:
659The {\tt change_random} predicate changes a randomly selected variable with
660a tentative value of 0 to 1, or vice versa.
661Note that we are using an array, rather than a list of variables, to
662provide more convenient random access.
663The complete code and the auxiliary predicate definitions can be found
664in the file {\tt knapsack_ls.ecl} in the {\tt doc/examples} directory of your
665{\eclipse} installation.
666
667
668%----------------------------------------------------------------------
669\subsection{Simulated Annealing}
670\index{simulated annealing}
671%----------------------------------------------------------------------
672
673Simulated Annealing is a slightly more complex variant of local search.
674It follows the nested loop schema  and uses a similar
675move operator to the random walk example.
676The main differences are in the termination conditions and in the
677acceptance criterion for a move.
678The outer loop simulates the cooling process by reducing the temperature
679variable \verb0T0, the inner loop does random moves until \verb0MaxIter0
680steps have been 
681done without improvement of the objective.
682
683The acceptance criterion is the classical one for simulated annealing:
684Uphill moves are always accepted, downhill moves with a probability
685that decreases with the temperature. The search routine must be invoked
686with appropriate start and end temperatures, they should roughly correspond
687to the maximum and minimum profit changes that a move can incur.
688{\small
689\begin{code}
690sim_anneal(Tinit, Tend, MaxIter, VarArr, Profit, Opt) :-
691        starting_solution(VarArr),              % starting solution
692        (   fromto(Tinit, T, Tnext, Tend),
693            fromto(0, Opt1, Opt4, Opt),
694            param(MaxIter,Profit,VarArr,Tend)
695        do
696            printf("Temperature is %d%n", [T]),
697            (    fromto(MaxIter, J0, J1, 0),
698                fromto(Opt1, Opt2, Opt3, Opt4),
699                param(VarArr,Profit,T)
700            do
701                Profit tent_get PrevProfit,
702                (   flip_random(VarArr),        % try a move
703                    Profit tent_get CurrentProfit,
704                    exp((CurrentProfit-PrevProfit)/T) > frandom,
705                    conflict_constraints(cap,[])   % is it a solution?
706                ->
707                    ( CurrentProfit > Opt2 ->   % is it new optimum?
708                        printf("Found solution with profit %w%n",
709                                    [CurrentProfit]),
710                        Opt3=CurrentProfit,     % accept and remember
711                        J1=J0
712                    ; CurrentProfit > PrevProfit ->
713                        Opt3=Opt2, J1=J0        % accept
714                    ;
715                        Opt3=Opt2, J1 is J0-1   % accept
716                    )
717                ;
718                    Opt3=Opt2, J1 is J0-1       % reject
719                )
720            ),
721            Tnext is max(fix(0.8*T),Tend)
722        ).
723
724flip_random(VarArr) :-
725        functor(VarArr, _, N),
726        X is VarArr[random mod N + 1],
727        X tent_get Old,
728        New is 1-Old,
729        X tent_set New.
730\end{code}
731}
732
733%----------------------------------------------------------------------
734\subsection{Tabu Search}
735\index{tabu Search}
736%----------------------------------------------------------------------
737Another variant of local search is tabu search.  Here, a number of moves
738(usually the recent moves) are remembered (the tabu list) to direct the
739search. Moves are selected by an acceptance criterion, with a 
740different (generally stronger) acceptance crtierion for moves in the tabu
741list.  Like most local search methods there are many possible variants and
742concrete instances of this basic idea. For example, how a move would be
743added to or removed from the tabu list has to be specified, along with the
744different acceptance criteria.
745
746In the following simple example, the tabu list has a length determined by
747the parameter {\tt TabuSize}. The local moves consist of either adding
748the item with the best relative profit into the knapsack, or removing
749the worst one from the knapsack. In both cases, the move gets rememebered
750in the fixed-size tabu list, and the complementary move is forbidden
751for the next {\tt TabuSize} moves.
752{\small
753\begin{code}
754tabu_search(TabuSize, MaxIter, VarArr, Profit, Opt) :-
755        starting_solution(VarArr),              % starting solution
756        tabu_init(TabuSize, none, Tabu0),
757        (   fromto(MaxIter, I0, I1, 0),
758            fromto(Tabu0, Tabu1, Tabu2, _),
759            fromto(0, Opt1, Opt2, Opt),
760            param(VarArr,Profit)
761        do
762            (   try_set_best(VarArr, MoveId),   % try uphill move
763                conflict_constraints(cap,[]),   % is it a solution?
764                tabu_add(MoveId, Tabu1, Tabu2)  % is it allowed?
765            ->
766                Profit tent_get CurrentProfit,
767                ( CurrentProfit > Opt1 ->       % is it new optimum?
768                    printf("Found solution with profit %w%n", [CurrentProfit]),
769                    Opt2=CurrentProfit          % accept and remember
770                ;
771                    Opt2=Opt1                   % accept
772                ),
773                I1 is I0-1
774            ;
775                (   try_clear_worst(VarArr, MoveId),    % try downhill move
776                    tabu_add(MoveId, Tabu1, Tabu2)      % is it allowed?
777                ->
778                    I1 is I0-1,
779                    Opt2=Opt1                   % reject
780                ;
781                    I1=0,                       % no moves possible, stop
782                    Opt2=Opt1                   % reject
783                )
784            )
785        ).
786\end{code}
787}
788
789In practice, the tabu search forms only a skeleton around which a complex
790search algorithm is built. An example of this is applying tabu search to
791the job-shop problem, see e.g. \cite{jobshoptabu}.
792
793\quickref{Implementing Search}{Repair can be used to implement a wide
794variety of local search and hybrid search techniques.
795}
796
797\section{Repair Exercise}
798Write a predicate 
799\verb0min_conflicts(Vars,Count)0
800that takes two arguments:
801\begin{itemize}
802\item Vars - a list of variables, with tentative 0/1 values
803\item Count - a variable, with a tentative integer value
804\end{itemize}
805
806The specification of \verb0min_conflicts(Vars,Count)0 is as follows:
807
808\begin{enumerate}
809\item If conflict set \verb0cs0 is empty,  instantiate \verb0Vars0 to
810their tentative values 
811\item Otherwise find a variable, \verb0V0, in a conflict constraint
812\item Instantiate \verb0V0 to the value ($0$ or $1$) that maximises
813the tentative value of \verb0Count0 
814\item On backtracking instantiate \verb0V0 the other way.
815\end{enumerate}
816
817%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
818This can be tested with the following propositional satisfiability
819program.
820
821\begin{code}
822cons_clause(Clause,Bool) :-
823    Clause =:= 1 r_conflict cs,
824    Bool tent_is Clause.
825
826prop_sat(Vars,List) :-
827    ( foreach(N,List),
828      foreach(Cl,Clauses),
829      param(Vars)
830    do
831        cl(N,Vars,Cl)
832    ),
833    init_tent_values(Vars),
834    ( foreach(Cl,Clauses), 
835      foreach(B,Bools) 
836    do 
837      cons_clause(Cl,B)
838    ),
839    Count tent_is sum(Bools),
840    min_conflicts(Vars,Count).
841
842init_tent_values(Vars) :- 
843    ( foreach(V,Vars) do V tent_set 1).
844
845cl(1,[X,Y,Z], (X or neg Y or Z)).
846cl(2,[X,Y,Z], (neg X or neg Y)).
847cl(3,[X,Y,Z], (Y or neg Z)).
848cl(4,[X,Y,Z], (X or neg Z)).
849cl(5,[X,Y,Z], (Y or Z)).
850\end{code}
851
852To test your program try the following queries:
853\begin{quote}
854\begin{verbatim}
855?- prop_sat([X,Y,Z],[1,2,3]).
856?- prop_sat([X,Y,Z],[1,2,3,4]).
857?- prop_sat([X,Y,Z],[1,2,3,4,5]).
858
859\end{verbatim}
860\end{quote}
861%HEVEA\cutend
862