-
Notifications
You must be signed in to change notification settings - Fork 10
/
Copy pathm-search.tex.in
executable file
·2297 lines (2040 loc) · 86.6 KB
/
m-search.tex.in
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
% -*- mode: LaTeX; -*-
\chapter{Search}
\label{chap:m:search}
This chapter discusses how \emph{exploration} for search is used
for solving Gecode models. Exploration defines a strategy how to
explore parts of the search tree and how to possibly modify the
tree's shape during exploration (for example, during
branch-and-bound best solution search by adding new constraints).
This chapter restricts itself to simple search engines to find
solutions, Gist as an interactive and graphical search engine is
discussed in \autoref{chap:m:gist}.
\paragraph{Overview.}
\autoref{sec:m:search:re} explains how search in Gecode makes use
of hybrid recomputation and why it is efficient. Even though this
section does not belong to the basic reading material, you are
highly encouraged to read it.
\autoref{sec:m:search:parallel} explains how parallel search can
be used in Gecode and what can be expected of parallel search in
principle. How search engines can be used is explained in
\autoref{sec:m:search:simple}. Restart-based search is discussed
in \autoref{sec:m:search:restart} and portfolio-based search is
discussed in \autoref{sec:m:search:portfolio}. This is followed
by a discussion in \autoref{sec:m:search:nogoods} how no-goods
from restarts can be used. \autoref{sec:m:search:trace} describes
how the execution of search engines can be traced and
\autoref{sec:m:search:cpprofiler} how the CPProfiler can be used
for tracing during search.
\begin{convention}
Note that the same conventions hold as in \autoref{chap:m:int}.
\end{convention}
\section{Hybrid recomputation}
\label{sec:m:search:re}
A central requirement for search is that it can return to
previous states: as spaces constitute nodes of the search tree, a
previous state is nothing but a space. Returning to a previous
space might be necessary because an alternative suggested by a
branching did not lead to a solution, or even if a solution has
been found more solutions might be requested. As propagation and
branching change spaces, provisions must be taken that search can
actually return to a previous space, or an equivalent version of
that space.
Two space are \emph{equivalent} if propagation and branching and
hence search behave exactly the same on both spaces. Equivalent
spaces can be different, for example, they contain different yet
equivalent propagators, or are allocated at a different memory
area.
Gecode employs a hybrid of two techniques for restoring spaces:
\emph{recomputation} and \emph{cloning}.
If you want to know how search engines can be programmed in
Gecode, please consult \autoref{part:s}.
\subsection{Cloning}
Cloning creates a clone of a space (this is supported by the
virtual \?copy? member function as discussed in
\autoref{chap:m:started}). A clone and the original space are of
course equivalent. Restoration with cloning is straightforward:
before following a particular alternative during search, a clone
of the space is made and used later if necessary.
\subsection{Recomputation}
\label{sec:m:search:recomp}
Recomputation remembers what has happened during branching:
rather than storing an entire clone of a space just enough
information to redo the effect of a brancher is stored. The
information stored is called a \emph{choice} in
Gecode. Redoing the effect is called to \emph{commit} a space:
given a space and a choice committing re-executes
the brancher as described by the choice and the
alternative to be explored (for example, left or right).
Consider the following part of a model, which constrains both
the sum and the product of \?x[0]? and \?x[1]? to be equal to
\?x[2]?:
\begin{code}
IntVarArray x(home, 3, 1, 6);
rel(home, x[0] + x[1] == x[2]);
mult(home, x[0], x[1], x[2]);
branch(home, x, INT_VAR_NONE(), INT_VAL_MIN());
\end{code}
\begin{figure}
\newcommand{\domINC}[2]{\{\mathtt{#1},\ldots,\mathtt{#2}\}}
\begin{center}
\begin{tabular}{c@{\qquad\qquad}c}
\begin{tabular}[c]{c}
\psset{xunit=0.04,yunit=0.04,runit=0.04}
\begin{pspicture}(96,152)(0,0)
\DefineNode{32}{143}{n1u}
\DefineNode{32}{133}{n1c}
\DefineNode{48}{105}{n12u}
\DefineNode{48}{95}{n12c}
\DefineNode{64}{67}{n122u}
\DefineNode{64}{57}{n122c}
\DefineNode{80}{27}{n1222u}
\DefineNode{80}{19}{n1222c}
\DefineLink{n122c}{n1222u}
\HiddenLinkL{n122c}{n1222c}{\footnotesize\quad$\mathtt{x[0]} \neq \mathtt 3$}
\DefineNode{48}{27}{n1221u}
\DefineNode{48}{19}{n1221c}
\DefineLink{n122c}{n1221u}
\HiddenLinkR{n122c}{n1221c}{\footnotesize$\mathtt{x[0]} = \mathtt 3$\quad}
\DefineLink{n12c}{n122u}
\HiddenLinkL{n12c}{n122c}{\footnotesize\quad$\mathtt{x[0]} \neq \mathtt 2$}
\DefineNode{32}{67}{n121u}
\DefineNode{32}{57}{n121c}
\DefineLink{n12c}{n121u}
\HiddenLinkR{n12c}{n121c}{\footnotesize$\mathtt{x[0]} = \mathtt 2$\quad}
\DefineLink{n1c}{n12u}
\HiddenLinkL{n1c}{n12c}{\footnotesize\quad$\mathtt{x[0]} \neq \mathtt 1$}
\DefineNode{16}{103}{n11u}
\DefineNode{16}{95}{n11c}
\DefineLink{n1c}{n11u}
\HiddenLinkR{n1c}{n11c}{\footnotesize$\mathtt{x[0]} = \mathtt 1$\quad}
\ChoiceNodeI{32}{133}{1}
\ChoiceNodeI{48}{95}{3}
\ChoiceNodeI{64}{57}{5}
\FailedNodeI{80}{19}{7}
\FailedNodeI{48}{19}{6}
\SolvedNodeI{32}{57}{4}
\FailedNodeI{16}{95}{2}
\end{pspicture}
\end{tabular}
&
\begin{tabular}[c]{|c||c|c|c|}
\hline
\textbf{node} & $\mathtt{x[0]}$ & $\mathtt{x[1]}$ & $\mathtt{x[2]}$
\\\hline\hline
1 & $\domINC{1}{5}$ & $\domINC{1}{5}$ & $\domINC{2}{6}$
\\\hline
3 & $\domINC{2}{5}$ & $\domINC{1}{3}$ & $\domINC{3}{6}$
\\\hline
4 & $\mathtt 2$ & $\mathtt 2$ & $\mathtt 4$
\\\hline
5 & $\domINC{3}{5}$ & $\domINC{1}{2}$ & $\domINC{4}{6}$
\\\hline
\end{tabular}
\end{tabular}
\end{center}
\caption{Example search tree}
\label{fig:m:search:tree}
\end{figure}
The corresponding search tree is shown in
\autoref{fig:m:search:tree}. A red box corresponds to a failed
node, a green diamond to a solution, and a blue circle to a
choice node (a node that has a not-yet finished brancher left).
An example choice for node~3 is
$\left(\mathtt{x[0]} = \mathtt 2\right) \vee \left(\mathtt{x[0]}
\neq \mathtt 2\right)$ where the left alternative (or the
$0$-th alternative) is $\mathtt{x[0]} = \mathtt 2$ and the right
alternative ($1$-st alternative) is $\mathtt{x[0]} \neq \mathtt
2$. Committing a space for node~3 to the $1$-st alternative
posts the constraint $\mathtt{x[0]} \neq \mathtt 2$.
More precisely, a choice does not store the actual
variables but the position among the variables of the brancher
(storing \?0? rather than \?x[0]?). By that, a choice can be used with an equivalent yet different space.
This is essential as the space used during recomputation will be
different from the space for which the choice has
been created.
\tip{Search is indeterministic}{
\label{tip:m:search:wmp}%
Gecode has been carefully designed to support non-monotonic
propagators: they are essential for example for randomized or
approximation propagation algorithms. A propagator in Gecode must
be \emph{weakly} monotonic: essentially, a propagator must be
correct but it does not need to always prune exactly the same
way. A consequence of this is that search is indeterministic: it
might be that two different searches find solutions in a
different order (possibly returning a different first solution)
or that the number of explored nodes is different. However,
search is always sound and complete: it never misses any
solution, it does not duplicate solutions, nor does it report
non-solutions as solutions.
If you want to know more about weakly monotonic propagators and
their interaction with search, we recommend to
consult~\cite{SchulteTack:CP:2009}.
}
\subsection{Hybrid recomputation}
The hybrid of recomputation and cloning works as follows. For
each new choice node, a choice is stored. Then,
every now and then search also stores a clone of a space (say,
every eight steps). Now, restoring a space at a certain position
in the search tree traverses the path in the tree upwards until a
clone $c$ is found on the path. Then recomputation creates a
clone $c'$ of $c$ (in certain cases, recomputation might use $c$
directly as an optimization). Then all choices on
the path are committed on $c'$ yielding an equivalent space.
\begin{figure}
\begin{center}
\psset{xunit=0.04,yunit=0.04,runit=0.04}
\begin{pspicture}(240,152)
\DefineNode{64}{143}{n1u}
\DefineNode{64}{133}{n1c}
\DefineNode{48}{105}{n2u}
\DefineNode{48}{95}{n2c}
\DefineNode{32}{67}{n3u}
\DefineNode{32}{57}{n3c}
\DefineNode{16}{27}{n4u}
\DefineNode{16}{19}{n4c}
\DefineNode{48}{27}{n5u}
\DefineNode{48}{19}{n5c}
\DefineLink{n1c}{n2u}
\DefineLink{n2c}{n3u}
\DefineLink{n3c}{n4u}
\DefineFatLink{n3c}{n5u}
\ChoiceNodeI{64}{133}{1}
\UChoiceNodeI{48}{95}{2}
\UChoiceNodeI{32}{57}{3}
\FailedNodeI{16}{19}{4}
\GuessNode{48}{19}
\DefineNode{120}{133}{t1}
\DefineNode{120}{95}{t2}
\DefineNode{120}{57}{t3}
\DefineNode{120}{19}{t4}
\rput(120,133){\makebox(0,0)[l]{space $\mathtt c$ and choice
$\mathtt{ch1}$}}
\rput(120,95){\makebox(0,0)[l]{choice $\mathtt{ch2}$}}
\rput(120,57){\makebox(0,0)[l]{choice $\mathtt{ch3}$}}
\ncline[nodesep=15]{->}{t1}{n1c}
\ncline[nodesep=15]{->}{t2}{n2c}
\ncline[nodesep=15]{->}{t3}{n3c}
\end{pspicture}
\end{center}
\caption{Hybrid recomputation}
\label{fig:m:search:hybrid}
\end{figure}
\begin{samepage}
To recompute the node \texttt{?} for the example shown in
\autoref{fig:m:search:hybrid}, the following operations are
executed:
\begin{code}
Space* s = c->clone();
s->commit(ch1, 0);
s->commit(ch2, 0);
s->commit(ch3, 1);
\end{code}
\end{samepage}
\subsection{Why recomputation is almost for free}
An absolutely fundamental property of the above hybrid is that an
equivalent space is computed without performing any constraint
propagation! Remember: committing just reposts constraints but
does not perform constraint propagation.
Reconsider the example from \autoref{fig:m:search:hybrid}.
Search has just failed at node~4 and must compute a space for
node \texttt{?}.
Suppose that only cloning but no recomputation is used. Then, a
clone of the space for node~3 is created (from the clone that is
stored in node~3) and that clone is committed to the first
alternative of $\mathtt{d3}$ (this corresponds to the slightly
thicker edge in \autoref{fig:m:search:hybrid}). After that,
constraint propagation is performed (by executing the \?status()?
function of a space, see also \autoref{tip:m:started:status}) to
find out if and how search must continue. That is: there is one
\?clone? operation, one \?commit? operation, and one \?status?
operation to perform constraint propagation.
With hybrid recomputation, one \?clone? operation, three
\?commit? operations, and one \?status? operation to perform
constraint propagation are needed (as shown above). The good news
is that \?commit? operations are very cheap (most often, just
modifying a single variable or posting a constraint). What is
essential is that in both cases only a \emph{single} \?status?
operation is executed. Hence, the cost for constraint propagation
during hybrid recomputation turns out to be not much higher than
the cost without recomputation.
For hybrid recomputation, some additional propagation might have
to be done compared to cloning. As it turns out, the additional
cost is rather small. This is due to the fact that constraint
propagation executes all propagators that might be able to remove
values for variables until no more propagation is possible (a
fixpoint for the propagators is computed). Due to the
approximative nature of ``might be able to remove values'' the
additional propagation tends to show only a very small increase
in runtime.
\subsection{Adaptive recomputation}
Consider the case that a search engine finds a failed node. That
means that some brancher has made an erroneous decision and now
search has to recover from that decision. It is quite likely that
not only the last decision is wrong but that the decision that
lead to failure is somewhere higher up in the search tree. With
other words, it is quite likely that search following a
depth-first left-most strategy must explore an entire failed
subtree to recover from the erroneous decision. In that case it
would be better for hybrid recomputation if there was a clone
close to the failed node rather than far away.
To optimize recomputation in this example scenario, Gecode uses
\emph{adaptive recomputation}: if a node must be recomputed,
adaptive recomputation creates an additional clone in the middle
of the recomputation path. A clone created during adaptive
recomputation is likely to be a good investment. Most likely, an
entire failed subtree will be explored. Hence, the clone will be
reused several times for reducing the amount of constraint
propagation during recomputation.
More information about search based on recomputation (although
not using choices) can be found in~\cite{Schulte:LNAI:2002}.
Search using choices has been inspired by batch
recomputation~\cite{components} and decomposition-based
search~\cite{DecoSearch}. For an empirical evaluation of
different techniques for search,
see~\cite{ReischukSchulteEa:CP:2009}.
\subsection{Controlling recomputation}
Hybrid and adaptive recomputation can be easily controlled by two
integers $c_d$ (\emph{commit distance})
and $a_d$ (\emph{adaptive distance}). The value for $c_d$ controls how
many clones are created during exploration: a search engine
creates clones during exploration to ensure that recomputation
executes at most $c_d$ commit operations. The value for $a_d$
controls adaptive recomputation: only if the clone for
recomputation is more than $a_d$ commit operations away from the
node to be recomputed, adaptive recomputation is used.
Values for $c_d$ and $a_d$ are used to configure the behavior of
search engines using hybrid and adaptive recomputation, see more
in the next Section.
The number of commit operations as distance measure is
approximately the same as the length of a path in the search
tree. It is only an approximation as search engines use
additional techniques to avoid some unused clone and commit
operations.
\tip{Values for $c_d$ and $a_d$}{
If $c_d=1$, recomputation is never used (you might not want
to try that for any other reason but curiosity; it takes too
much memory to be useful). Likewise, to switch off cloning, you
can use a value for $c_d$ that is larger than the expected
depth of the search tree. If $a_d\geq c_d$, adaptive
recomputation is never used.
}
\section{Parallel search}
\label{sec:m:search:parallel}
Parallel search has but one motivation: try to make search more
efficient by employing several threads (or workers) to explore
different parts of the search tree in parallel.
Gecode uses a standard work-stealing architecture for parallel
search: initially, all work (the entire search tree to be
explored) is given to a single worker for exploration, making the
worker busy. All other workers are initially idle, and try to
steal work from a busy worker. Stealing work means that part of
the search tree is given from a busy worker to an idle worker
such that the idle worker can become busy itself. If a busy
worker becomes idle, it tries to steal new work from a busy
worker.
As work-stealing is indeterministic (depending on how threads are
scheduled, machine load, and other factors), the work that is
stolen varies over different runs for the very same problem: an
idle worker could potentially steal different subtrees from
different busy workers. As different subtrees contain different
solutions, it is indeterministic which solution is found first.
When using parallel search one needs to take the following facts
into account (note that some facts are not particular to
parallel search, check \autoref{tip:m:search:wmp}: they are just
more likely to occur):
\begin{itemize}
\item The order in which solutions are found might be different
compared to the order in which sequential search finds
solutions. Likewise, the order in which solutions are found
might differ from one parallel search to the next. This is just
a direct consequence of the indeterministic nature of parallel
search.
\item Naturally, the amount of search needed to find a first
solution might differ both from sequential search and among
different parallel searches. Note that this might actually lead
to super-linear speedup (for $n$ workers, the time to find a
first solution is less than $1/n$ the time of sequential
search) or also to real slowdown.
\item For best solution search, the number of solutions until a
best solution is found as well as the solutions found are
indeterministic. First, any better solution is legal (it does
not matter which one) and different runs will sometimes be
lucky (or not so lucky) to find a good solution rather quickly.
Second, as a better solution prunes the remaining search space
the size of the search space depends crucially on how quickly
good solutions are found.
\item As a corollary to the above items, the deviation in runtime
and number of nodes explored for parallel search can be quite
high for different runs of the same problem.
\item Parallel search needs more memory. As a rule of thumb, the
amount of memory needed scales linearly with the number of
workers used.
\item For parallel search to deliver some speedup, the search
tree must be sufficiently large. Otherwise, not all threads
might be able to find work and idle threads might slow down
busy threads by the overhead of unsuccessful work-stealing.
\item From all the facts listed, it should be clear that for
depth-first left-most search for just a single solution it is
notoriously difficult to obtain consistent speedup. If the
heuristic is very good (there are almost no failures), sequential
left-most depth-first search is optimal in exploring the single
path to the first solution. Hence, all additional work will be
wasted and the work-stealing overhead might slow down the
otherwise optimal search.
\end{itemize}
\tip{Be optimistic about parallel search}{%
After reading the above list of facts you might have come to
the conclusion that parallel search is not worth it as it does
not exploit the parallelism of your computer very well. Well,
why not turn the argument upside down: your machine will almost
for sure have more than a single processing unit and maybe
quite some. With sequential search, all units but one will be
idle anyway.
The point of parallel search is to make search go faster. It
is not to perfectly utilize your parallel hardware. Parallel
search makes good use (and very often excellent use for large
problems with large search trees) of the additional processing
power your computer has anyway.
\begin{figure}
\begin{cmd}
GolombRuler
m[12] = {0, 1, 3, 7, 12, 20, 30, 44, 65, 80, 96, 122}
m[12] = {0, 1, 3, 7, 12, 20, 30, 44, 65, 90, 105, 121}
m[12] = {0, 1, 3, 7, 12, 20, 30, 45, 61, 82, 96, 118}
... (additional solutions omitted)
m[12] = {0, 2, 6, 24, 29, 40, 43, 55, 68, 75, 76, 85}
Initial
propagators: 58
branchers: 1
Summary
runtime: 14.866 (14866.000 ms)
solutions: 17
propagations: 519555681
nodes: 3836351
failures: 1918148
restarts: 0
no-goods: 0
peak depth: 26
\end{cmd}
\caption{Output for Golomb rulers with eight workers}
\label{fig:m:search:out:8}
\end{figure}
\begin{samepage}
For example, on my machine with eight cores and using Gecode 4.2.0, running
\gecoderef[example]{golomb-ruler} for size $12$ as follows
\begin{cmd}
golomb-ruler.exe -threads 8 12
\end{cmd}
\end{samepage}
prints something like shown in \autoref{fig:m:search:out:8}.
\begin{figure}
\begin{cmd}
GolombRuler
m[12] = {0, 1, 3, 7, 12, 20, 30, 44, 65, 80, 96, 122}
m[12] = {0, 1, 3, 7, 12, 20, 30, 44, 65, 90, 105, 121}
m[12] = {0, 1, 3, 7, 12, 20, 30, 45, 61, 82, 96, 118}
... (additional solutions omitted)
m[12] = {0, 2, 6, 24, 29, 40, 43, 55, 68, 75, 76, 85}
Initial
propagators: 58
branchers: 1
Summary
runtime: 1:47.316 (107316.000 ms)
solutions: 16
propagations: 692676452
nodes: 5313357
failures: 2656663
restarts: 0
no-goods: 0
peak depth: 24
\end{cmd}
\caption{Output for Golomb rulers with one worker}
\label{fig:m:search:out:1}
\end{figure}
Compared to sequential search where one gets something like shown
in \autoref{fig:m:search:out:1} one gets a speedup of $7.2$. }
Parallel search is controlled by the number of threads (or
workers) used for search. If a single worker is requested,
sequential search is used. The number of threads to be used for
search is controlled by the search options passed to a search
engine, see the following section for details.
Gecode also provides parallel portfolio search which is discussed
in \autoref{sec:m:search:portfolio}.
\tip{Do not optimize by branching alone}{%
\label{tip:m:search:parbab}%
\begin{samepage}
A common modeling technique for optimization problems that does
not work for parallel search is the following. Suppose, one has
a variable \?c? for the cost of a problem and one wants to
minimize the cost. Then, one could use the following code
fragment
\begin{code}
branch(home, c, INT_VAL_MIN());
\end{code}
which will try the values for \?c? in increasing order.
\end{samepage}
With sequential search, searching for the first solution with a
standard depth-first left-most search engine will deliver a
best solution, that is, a solution with least cost for \?c?.
With parallel search, the first solution found might of course
not be a best one. Hence, instead of using plain left-most
depth-first search, one should use best solution search with a
proper constrain function that guarantees that \?c? will be
minimized. This will as always guarantee that the last solution
found is the best.
For an example, see the naive model for the bin packing case
study in \autoref{sec:c:bpp:naive} where a branching first
branches on the number of required bins.
}
\section{Search engines}
\label{sec:m:search:simple}
\begin{important}
Do not forget to add
\begin{code}
#include <gecode/search.hh>
\end{code}
to your program when you want to use search engines.
\end{important}
\begin{figure}
\begin{center}
\begin{tabular}{|l|l|l|}
\hline
\multicolumn{1}{|c|}{member} &
\multicolumn{1}{c|}{type} &
\multicolumn{1}{c|}{meaning} \\
\hline\hline
\?propagate? & \?unsigned long int? & propagators executed\\
\hline
\?fail? & \?unsigned long int? & failed nodes explored\\
\?node? & \?unsigned long int? & nodes explored\\
\?restart? & \?unsigned long int? & restarts performed\\
\?nogood? & \?unsigned long int? & no-goods generated\\
\hline
\?depth? & \?unsigned long int? & maximal depth of explored tree\\
\hline
\end{tabular}
\end{center}
\caption[Search statistics]{Search statistics (partial)}
\label{fig:m:search:statistics}
\end{figure}
All search engines in Gecode are parametric (are templates) with
respect to a subclass \?T? of \gecoderef[class]{Space} (for
example, \?SendMoreMoney? in \autoref{sec:m:started:first}).
Moreover, all search engines share the same interface:
\begin{itemize}
\item The search engine is initialized by a constructor taking a
pointer to an instance of the space subclass \?T? as argument.
By default, the search engine takes a clone of the space
passed.
This behavior can be changed, as can be other aspects of a
search engine, see \autoref{sec:m:search:options}.
\item A next solution can be requested by a \?next()? member
function. If no more solutions exist, \?next()? returns
\?NULL?. Otherwise, the engine returns a solution which again
is an instance of \?T?. The client of the search engine is
responsible for deleting solutions.
\item A search engine can be asked for statistics information by
the \?statistics()? member function. The function returns an
object of type \gecoderef[class]{Search::Statistics}. The
statistics information provided is partially summarized in
\autoref{fig:m:search:statistics} (see
\autoref{sec:m:search:restart} for the meaning of \?restart?
and \autoref{sec:m:search:nogoods} for the meaning of \?nogood?).
\item A search engine can be queried by \?stopped()? whether the
search engine has been stopped by a \emph{stop object}. Stop
objects are discussed in \autoref{sec:m:search:stop}.
\item The destructor deletes all resources used by the search
engine.
\end{itemize}
Note that search engines use pointers to objects rather than
references to objects. The reason is that some pointers might be
\?NULL?-pointers (for example, if \?next()? fails to find a
solution) and that users of search engines have to think about
deleting solutions computed by search engines.
\begin{figure}
\begin{center}
\begin{tabular}{|l|l|l|c|c|}
\hline
\multicolumn{1}{|c|}{engine} &
\multicolumn{1}{c|}{shortcut} &
\multicolumn{1}{c|}{exploration} &
best solution &
parallel \\
\hline\hline
\gecoderef[class]{DFS} & \?dfs? & depth-first left-most & & \YES\\
\gecoderef[class]{LDS} & \?lds? & limited discrepancy~\cite{HarveyGinsberg:95} &&\\
\hline
\gecoderef[class]{BAB} & \?bab? & branch-and-bound & \YES & \YES\\
\hline
\end{tabular}
\end{center}
\caption{Available search engines}
\label{fig:m:search:engine}
\end{figure}
For each search engine there also exists a convenient shortcut
function (of the same name but entirely in lowercase letters)
that returns either the first solution or, in the case of best
solution search, the last (and hence best) solution. The
available search engines are summarized in
\autoref{fig:m:search:engine}.
\?BAB? continues search when a solution is found by adding a
constraint (through the \?constrain()? function as discussed in
\autoref{sec:m:started:search-best}) to search for a better
solution to all remaining nodes of the search tree.
Note that the version of Gecode (\GecodeVersion) this document
corresponds to does not support parallel search for \?LDS?.
\subsection{Search options}
\label{sec:m:search:options}
All search engines can take a default option value of type
\gecoderef[class]{Search::Options} when being created. The
options are summarized in \autoref{fig:m:search:options}.
The default values for the options are defined in the namespace
\gecoderef[namespace]{Search::Config}.
\begin{figure}
\begin{center}
\begin{tabular}{|l|l|l|}
\hline
\multicolumn{1}{|c|}{member} &
\multicolumn{1}{c|}{type} &
\multicolumn{1}{c|}{meaning} \\
\hline\hline
\?threads? & \?double? & number of parallel threads to use\\
\hline
\?c_d? & \?unsigned int? & commit recomputation distance\\
\?a_d? & \?unsigned int? & adaptive recomputation distance\\
\hline
\?clone? & \?bool? & whether engine uses a clone when created\\
\hline
\?d_l? & \?unsigned int? & discrepancy limit (for \?LDS?)\\
\hline
\?nogoods_limit? & \?unsigned int? & depth limit for no-good generation\\
\hline
\?assets? & \?unsigned int? & number of assets in a portfolio\\
\hline
\?stop? & \?Search::Stop*? & stop object (\?NULL? if none)\\
\hline
\?cutoff? & \?Search::Cutoff*? & cutoff object (\?NULL? if none)\\
\hline
\?tracer? & \?SearchTracer*? & search tracer (\?NULL? if none)\\
\hline
\end{tabular}
\end{center}
\caption{Search options}
\label{fig:m:search:options}
\end{figure}
The meaning of the values for the search options are
straightforward but for \?threads? (\?cutoff? is explained in
\autoref{sec:m:search:restart}, \?assets? is explained in
\autoref{sec:m:search:portfolio}, \?nogoods_limit? is
explained in \autoref{sec:m:search:nogoods}, and \?tracer? is
explained in \autoref{sec:m:search:trace}).
Assume that your computer has
$m$ processing units\footnote{This is a very rough
characterization: a processing unit could be a CPU, a processor
core, or a multi-threading unit. If you want to find out how
many processing units Gecode believes your machine has, invoke
the configuration summary as described in
\autoref{tip:m:comfy:conf}.} and that the value for \?threads?
is $n$.
\begin{itemize}
\item If $n=0$, then $m$ threads are used (as many as available
processing units).
\item If $n\geq 1$, then $n$ threads are used (absolute number
of threads to be used).
\item If $n\leq -1$, then $m+n$ threads are used (absolute
number of processing units not to be used). For example, when
$n=-6$ and $m=8$, then $2$ threads are used.
\item If $0<n<1$, then $n\cdot m$ threads are used (relative
number of processing units to be used). For example, when
$n=0.5$ and $m=8$, then $4$ threads are used.
\item If $-1<n<0$, then $(1+n)\cdot m$ threads are used
(relative number of processing units not to be used). For
example, when $n=-0.25$ and $m=8$, then $6$ threads are used.
\end{itemize}
Note that all values are of course rounded and that at least one
thread will be used.
\subsection{Stop objects}
\label{sec:m:search:stop}
A stop object (a subclass of \gecoderef[class]{Search::Stop})
implements a single virtual member function \?stop()? that takes
two arguments, the first of type
\gecoderef[class]{Search::Statistics} and the second of type
\gecoderef[class]{Search::Options},
and returns either \?true? or \?false?. If a stop object is passed
to a search engine (by passing it as \?stop? member of a search
option), the search engine calls the \?stop()? function of the
stop object before every exploration step and passes the current
statistics as argument. If the \?stop()? function returns true,
the search engine stops its execution.
When a search engine is stopped its \?next()? function returns
\?NULL? as solution. To find out whether a search engine has been
stopped or whether there are no more solutions, the \?stopped()?
member function of a search engine can be used. Search can be
resumed by calling \?next()? again after the stop object has been
modified (for example, by increasing the node or time limit).
Note that when using several threads for parallel search, each
thread checks whether it is stopped independently using the very
same stop object. If one thread is stopped, then the entire
search engine is stopped.
\begin{figure}
\begin{center}
\begin{tabular}{|l|l|}
\hline
\multicolumn{1}{|c|}{class} &
\multicolumn{1}{c|}{description} \\
\hline\hline
\gecoderef[class]{Search::NodeStop} & node limit exceeded\\
\hline
\gecoderef[class]{Search::FailStop} & failure limit exceeded\\
\hline
\gecoderef[class]{Search::TimeStop} & time limit exceeded\\
\hline
\end{tabular}
\end{center}
\caption{Predefined stop objects}
\label{fig:m:search:stop}
\end{figure}
Gecode provides several predefined stop objects, see
\gecoderef[group]{TaskModelSearchStop}. For an overview see
\autoref{fig:m:search:stop}. Objects of these classes can be
created conveniently by, for example:
\begin{code}
Stop* s = Search::Stop::node(l);
\end{code}
The class \gecoderef[class]{Search::Stop} also provides similar
static functions \?fail()? and \?time()?.
\tip{Number of threads for stop objects}{%
As mentioned above, each thread in parallel search uses the very same
stop object. For example, when using the predefined
\gecoderef[class]{Search::NodeStop} stop object with a node limit
of $n$, then each thread can explore up to $n$ nodes.
If you want to have finer control (say, only allow each thread to
explore up to $n/m$ nodes where $m$ is the number of threads)
you can use the search option argument that is passed as the
second argument to the \?stop? member function to scale the node
limit according to the number of available threads.
}
\section{Restart-based search}
\label{sec:m:search:restart}
The idea of restart-based search is to run search with a given
\emph{cutoff} (Gecode uses the number of failures during search
as cutoff-measure). When search reaches the cutoff, it is stopped
and then restarted with a new and typically increased cutoff.
The whole point of restarting search is that it is not restarted
on exactly the same problem but on a modified or
re-configured problem. Possible modifications include but are not
limited to:
\begin{itemize}
\item Improved information from the search for branching
heuristics such as action (see
\autoref{sec:m:branch:action}) or AFC (see
\autoref{sec:m:branch:afc}): the now stopped search has
gathered some information of which branching can take advantage
in the next restart of search.
\item The next search can use different random numbers that
controls branching. A typical strategy would be to use
tie-breaking for combining a branching heuristic with a random
branching heuristic (see \autoref{sec:m:branch:tie}) and
control the degree of randomness by a tie-breaking limit
function.
\item The next search uses an entirely different branching
heuristic.
\item The next search adds so-called no-goods derived from the
now stopped search. No-goods are additional constraints that
prevent the restarted search to make decisions during search
that lead to failure in the stopped search. No-goods are
discussed in detail in \autoref{sec:m:search:nogoods}.
\item The next search ``keeps'' only a randomly selected part of
a previous solution and tries to find a different solution.
This is often used for optimization problems and is known as
LNS (Large Neighborhood Search)~\cite{LNS}. How restart-based
can be used for LNS is discussed in
\autoref{sec:m:search:restart:lns}.
\end{itemize}
For an example illustrating the effect of restart-based search,
see \autoref{sec:c:crossword:info}. A general overview of
restart-based search can be found in~\cite{vanBeek:CPH:2006}.
\subsection{Restart-based search as a meta search engine}
\label{sec:m:search:restart:meta}
Restart-based search in Gecode is implemented as a \emph{meta
search engine}: the meta search engine uses one of the Gecode
search engines discussed in~\autoref{sec:m:search:simple} to
perform search for each individual restart. The meta engine then
controls the engine, the cutoff values, and how the problem is
configured before each restart. The interface of the meta search
engine in Gecode is exactly the same as the interface of a
non-meta search engine. In addition to the restart meta search
engine, Gecode offers a portfolio meta search engine which is
described in \autoref{sec:m:search:portfolio}.
The restart meta search engine \gecoderef[class]{RBS} is
parametric with respect to both
the script to be solved (a subclass of \gecoderef[class]{Space})
and the search engine to be used.
For example, when we want to use the \gecoderef[class]{DFS}
engine for the script \?s? of class type \?Script?, the meta
engine \?e? can be created by (\?o? are mandatory search
options, see below):
\begin{code}
RBS<Script,DFS> e(s,o);
\end{code}
Now \?e? implements exactly the same interface as the normal
engines (that is, \?next()? to request the next solution,
\?statistics()? to return the meta engine's statistic, and \?stopped()?
to check whether the meta engine has been stopped).
The meta engine honors all search options as discussed in
\autoref{sec:m:search:options}. If parallel search is requested,
the engine used by the meta engine will use the specified number
of processing units to perform parallel search. The meta engine
requires that the search options specify a
\gecoderef[class]{Search::Cutoff} object defining the cutoff
sequence.
\paragraph{Best solution search.}
Restart-based search can be used for both finding any solution or
finding a best solution. For searching for a best solution, the
meta engine should be used with the \gecoderef[class]{BAB}
engine, for example as:
\begin{code}
RBS<Script,BAB> e(s,o);
\end{code}
The behavior whether the engine performs a restart when a better
solution is performed or whether the \?BAB? engine continues to
find a better solution with a restart can be controlled as
described in \autoref{sec:m:search:restart:configure}.
When using restart-based search for finding a best solution it is
essential to use the \gecoderef[class]{BAB} engine when used as
part of a portfolio-based search engine, see
\autoref{sec:m:search:portfolio:arbitrary}.
\paragraph{Parallel search.}
The restart-based search engine supports parallel search in that
the engine used for performing the restarts can be run in
parallel. The number of threads used can be described by the
search options passed to the restart-based search engine as
described in \autoref{sec:m:search:parallel}.
\tip{Controlling restart-based search with the commandline
driver}{
\label{tip:m:search:restartcmd}%
The commandline driver transparently supports restart-based
search. Depending on which options are passed on the commandline,
either a search engine or the restart-based meta search engine is
used for search. See \autoref{sec:m:driver:options} for details.
}
\subsection{Cutoff generators}
\label{sec:m:search:restart:cutoff}
The meta engine uses a cutoff generator that generates a sequence
of cutoff values. A cutoff generator must be implemented by
inheriting from the class \gecoderef[class]{Search::Cutoff}. This
abstract class requires that two virtual member functions
\?operator()()? and \?operator++()? are implemented, where the
first returns the current cutoff value and the second increments
to the next cutoff value and returns it. Cutoff values are of
type \?unsigned long int?.
When using the restart meta engine, an instance of a subclass of
\gecoderef[class]{Search::Cutoff} must be passed to the engine by
using the search options (see
\autoref{sec:m:search:options}). For example, when \?s? is a
space to be solved and \?c? a cutoff generator, then the restart
engine can be created by:
\begin{code}
Search::Options o;
o.cutoff = c;
RBS<Script,DFS> e(s,o);
\end{code}
Gecode provides some commonly used cutoff generators:
\begin{description}
\item[Geometric.] A geometric cutoff sequence is defined by the
scale-factor \?s? and the base \?b?. Then, the sequence
consists of the cutoff values:
$$
\mathtt{s}\cdot\mathtt{b}^i\qquad\text{for }i=0,1,2,\ldots
$$
\begin{samepage}
A cutoff generator \?c? for a geometric cutoff sequence with
scale-factor \?s? (of type \?unsigned long int?) and base \?b?
(of type \?double?) is created by:
\begin{smallcode}
Search::Cutoff* c = Search::Cutoff::geometric(s,b);
\end{smallcode}
\end{samepage}
The generator is implemented by the class
\gecoderef[class]{Search::CutoffGeometric}.
\item[Luby.] A Luby cutoff sequence is based on the Luby-sequence
from~\cite{Luby}. The sequence starts with a \?1?. The next
part of the sequence is the entire previous sequence (only
\?1?) with the last value of the previous sequence (\?1? again)
doubled. This construction is then repeated, leading to the
sequence:
$$
1,1,2,1,1,2,4,1,1,2,1,1,2,4,8,\ldots
$$
To be practically useful, the values in the Luby sequence are
scaled by multiplying them with a scale-factor \?s?.
A cutoff generator \?c? for a Luby cutoff sequence with scale-factor
\?s? (of type \?unsigned long int?) is created by:
\begin{smallcode}
Search::Cutoff* c = Search::Cutoff::luby(s);
\end{smallcode}
The generator is implemented by the class
\gecoderef[class]{Search::CutoffLuby}.
\item[Random.] A random cutoff sequence consists of uniformly
randomly chosen values between a lower bound \?min? and an
upper bound \?max?. To focus on rather different values, only
values from the set of $\mathtt n+1$ values:
$$
\setc{\mathtt{min}+\lfloor i\cdot(\mathtt{max}-\mathtt{min})/\mathtt{n}\rfloor}{i=0,\ldots,\mathtt{n}}
$$