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