From c48148ffaf50df602a2b3f30f4edaedebcab08dc Mon Sep 17 00:00:00 2001 From: "Jip J. Dekker" Date: Tue, 9 Feb 2021 14:10:12 +1100 Subject: [PATCH] Initial import from LNS paper --- assets/bibliography/references.bib | 91 +++ assets/glossary.tex | 13 +- assets/mzn/2_knapsack.mzntex | 4 +- assets/mzn/6_adaptive.mzn | 8 + assets/mzn/6_adaptive.mzntex | 10 + assets/mzn/6_basic_complete.mzn | 8 + assets/mzn/6_basic_complete.mzntex | 10 + assets/mzn/6_basic_complete_transformed.mzn | 16 + .../mzn/6_basic_complete_transformed.mzntex | 18 + assets/mzn/6_hill_climbing.mzn | 3 + assets/mzn/6_hill_climbing.mzntex | 5 + assets/mzn/6_lns_minisearch.mzn | 8 + assets/mzn/6_lns_minisearch.mzntex | 10 + assets/mzn/6_lns_minisearch_pred.mzn | 3 + assets/mzn/6_lns_minisearch_pred.mzntex | 5 + assets/mzn/6_restart_ann.mzn | 18 + assets/mzn/6_restart_ann.mzntex | 20 + assets/mzn/6_round_robin.mzn | 7 + assets/mzn/6_round_robin.mzntex | 9 + assets/mzn/6_simulated_annealing.mzn | 7 + assets/mzn/6_simulated_annealing.mzntex | 9 + assets/mzn/6_state_access.mzn | 5 + assets/mzn/6_state_access.mzntex | 7 + assets/mzn/6_status.mzn | 5 + assets/mzn/6_status.mzntex | 7 + assets/mzn/minilns/assign_random.mzn | 3 + assets/mzn/minilns/basic.mzn | 3 + assets/mzn/minilns/restart.mzn | 19 + assets/packages.tex | 37 +- assets/py/2_dyn_knapsack.pytex | 4 +- assets/shorthands.tex | 4 + chapters/2_background.tex | 20 +- chapters/6_incremental.tex | 528 +++++++++++++++++- dekker_thesis.tex | 1 + 34 files changed, 892 insertions(+), 33 deletions(-) create mode 100644 assets/mzn/6_adaptive.mzn create mode 100644 assets/mzn/6_adaptive.mzntex create mode 100644 assets/mzn/6_basic_complete.mzn create mode 100644 assets/mzn/6_basic_complete.mzntex create mode 100644 assets/mzn/6_basic_complete_transformed.mzn create mode 100644 assets/mzn/6_basic_complete_transformed.mzntex create mode 100644 assets/mzn/6_hill_climbing.mzn create mode 100644 assets/mzn/6_hill_climbing.mzntex create mode 100644 assets/mzn/6_lns_minisearch.mzn create mode 100644 assets/mzn/6_lns_minisearch.mzntex create mode 100644 assets/mzn/6_lns_minisearch_pred.mzn create mode 100644 assets/mzn/6_lns_minisearch_pred.mzntex create mode 100644 assets/mzn/6_restart_ann.mzn create mode 100644 assets/mzn/6_restart_ann.mzntex create mode 100644 assets/mzn/6_round_robin.mzn create mode 100644 assets/mzn/6_round_robin.mzntex create mode 100644 assets/mzn/6_simulated_annealing.mzn create mode 100644 assets/mzn/6_simulated_annealing.mzntex create mode 100644 assets/mzn/6_state_access.mzn create mode 100644 assets/mzn/6_state_access.mzntex create mode 100644 assets/mzn/6_status.mzn create mode 100644 assets/mzn/6_status.mzntex create mode 100644 assets/mzn/minilns/assign_random.mzn create mode 100644 assets/mzn/minilns/basic.mzn create mode 100644 assets/mzn/minilns/restart.mzn create mode 100644 assets/shorthands.tex diff --git a/assets/bibliography/references.bib b/assets/bibliography/references.bib index 3bb1a8c..2312d46 100644 --- a/assets/bibliography/references.bib +++ b/assets/bibliography/references.bib @@ -26,6 +26,77 @@ programming}, } +@inproceedings{michel-2005-comet, + author = {Laurent Michel and Pascal Van Hentenryck}, + editor = {Peter van Beek}, + title = {The Comet Programming Language and System}, + booktitle = {Principles and Practice of Constraint Programming - {CP} + 2005, 11th International Conference, {CP} 2005, Sitges, Spain, + October 1-5, 2005, Proceedings}, + series = {Lecture Notes in Computer Science}, + volume = 3709, + pages = {881--881}, + publisher = {Springer}, + year = 2005, + url = {https://doi.org/10.1007/11564751_119}, + doi = {10.1007/11564751_119}, + timestamp = {Tue, 14 May 2019 10:00:45 +0200}, + biburl = {https://dblp.org/rec/conf/cp/MichelH05a.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} + +@article{pisinger-2007-heuristic, + author = {David Pisinger and Stefan Ropke}, + title = {A general heuristic for vehicle routing problems}, + journal = {Comput. Oper. Res.}, + volume = 34, + number = 8, + pages = {2403--2435}, + year = 2007, + url = {https://doi.org/10.1016/j.cor.2005.09.012}, + doi = {10.1016/j.cor.2005.09.012}, + timestamp = {Tue, 18 Feb 2020 13:56:22 +0100}, + biburl = {https://dblp.org/rec/journals/cor/PisingerR07.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} + +@inproceedings{rendl-2015-minisearch, + author = {Andrea Rendl and Tias Guns and Peter J. Stuckey and Guido + Tack}, + editor = {Gilles Pesant}, + title = {MiniSearch: {A} Solver-Independent Meta-Search Language for + MiniZinc}, + booktitle = {Principles and Practice of Constraint Programming - 21st + International Conference, {CP} 2015, Cork, Ireland, August 31 + - September 4, 2015, Proceedings}, + series = {Lecture Notes in Computer Science}, + volume = 9255, + pages = {376--392}, + publisher = {Springer}, + year = 2015, + url = {https://doi.org/10.1007/978-3-319-23219-5_27}, + doi = {10.1007/978-3-319-23219-5_27}, + timestamp = {Sun, 25 Oct 2020 23:13:15 +0100}, + biburl = {https://dblp.org/rec/conf/cp/RendlGST15.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} + +@article{ropke-2006-adaptive, + author = {Stefan Ropke and David Pisinger}, + title = {An Adaptive Large Neighborhood Search Heuristic for the + Pickup and Delivery Problem with Time Windows}, + journal = {Transp. Sci.}, + volume = 40, + number = 4, + pages = {455--472}, + year = 2006, + url = {https://doi.org/10.1287/trsc.1050.0135}, + doi = {10.1287/trsc.1050.0135}, + timestamp = {Tue, 08 Sep 2020 13:28:27 +0200}, + biburl = {https://dblp.org/rec/journals/transci/RopkeP06.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} + @book{silvano-1990-knapsack, author = {Martello, Silvano and Toth, Paolo}, title = {Knapsack Problems: Algorithms and Computer Implementations}, @@ -34,3 +105,23 @@ publisher = {John Wiley \& Sons, Inc.}, address = {USA} } + +@inproceedings{stuckey-2013-functions, + author = {Peter J. Stuckey and Guido Tack}, + editor = {Carla P. Gomes and Meinolf Sellmann}, + title = {MiniZinc with Functions}, + booktitle = {Integration of {AI} and {OR} Techniques in Constraint + Programming for Combinatorial Optimization Problems, 10th + International Conference, {CPAIOR} 2013, Yorktown Heights, NY, + USA, May 18-22, 2013. Proceedings}, + series = {Lecture Notes in Computer Science}, + volume = 7874, + pages = {268--283}, + publisher = {Springer}, + year = 2013, + url = {https://doi.org/10.1007/978-3-642-38171-3_18}, + doi = {10.1007/978-3-642-38171-3_18}, + timestamp = {Tue, 14 May 2019 10:00:47 +0200}, + biburl = {https://dblp.org/rec/conf/cpaior/StuckeyT13.bib}, + bibsource = {dblp computer science bibliography, https://dblp.org} +} diff --git a/assets/glossary.tex b/assets/glossary.tex index 7ad824e..9fdf8e2 100644 --- a/assets/glossary.tex +++ b/assets/glossary.tex @@ -17,11 +17,18 @@ name={decision variable}, description={TODO}, } -\newglossaryentry{minizinc}{ - name={MiniZinc}, +\newglossaryentry{flatzinc}{ + name={Flat\-Zinc}, + description={TODO}, +} +\newglossaryentry{minisearch}{ + name={Mini\-Search}, + description={TODO}, +} +\newglossaryentry{minizinc}{ + name={Mini\-Zinc}, description={TODO}, } -\newcommand{\minizinc}{\gls{minizinc}} \newglossaryentry{solver}{ name={solver}, description={TODO}, diff --git a/assets/mzn/2_knapsack.mzntex b/assets/mzn/2_knapsack.mzntex index 3be9448..9ea1b4c 100644 --- a/assets/mzn/2_knapsack.mzntex +++ b/assets/mzn/2_knapsack.mzntex @@ -1,4 +1,4 @@ -\begin{Verbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] \PY{c}{\PYZpc{} Problem parameters} \PY{k+kt}{enum}\PY{l+s}{ }\PY{n+nv}{TOYS}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{p}{\PYZob{}}\PY{n+nv}{football}\PY{p}{,}\PY{l+s}{ }\PY{n+nv}{tennisball}\PY{p}{,}\PY{l+s}{ }\PY{n+nv}{stuffed\PYZus{}lama}\PY{p}{,}\PY{l+s}{ }\PY{n+nv}{stuffed\PYZus{}elephant}\PY{g+gr}{\PYZcb{}}\PY{p}{;} \PY{k+kt}{array}\PY{p}{[}\PY{n+nv}{TOYS}\PY{g+gr}{]}\PY{l+s}{ }\PY{k+kt}{of}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{toy\PYZus{}joy}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{p}{[}\PY{l+m}{63}\PY{p}{,}\PY{l+s}{ }\PY{l+m}{12}\PY{p}{,}\PY{l+s}{ }\PY{l+m}{50}\PY{p}{,}\PY{l+s}{ }\PY{l+m}{100}\PY{g+gr}{]}\PY{p}{;} @@ -14,4 +14,4 @@ \PY{c}{\PYZpc{} Goal} \PY{k}{solve}\PY{l+s}{ }\PY{k}{maximize}\PY{l+s}{ }\PY{n+nv}{total\PYZus{}joy}\PY{p}{;} -\end{Verbatim} +\end{BVerbatim} diff --git a/assets/mzn/6_adaptive.mzn b/assets/mzn/6_adaptive.mzn new file mode 100644 index 0000000..dcc0f37 --- /dev/null +++ b/assets/mzn/6_adaptive.mzn @@ -0,0 +1,8 @@ +predicate adaptiveUniform(array[int] of var int: x, float: initialDestrRate) = + let { var float: rate; } in + if status() = START then rate = initialDestrRate + elseif status() = UNSAT then rate = min(lastval(rate)-0.02,0.6) + else rate = max(lastval(rate)+0.02,0.95) + endif /\ + forall(i in index_set(x)) + (if uniform(0.0,1.0) > rate then x[i] = sol(x[i]) else true endif); diff --git a/assets/mzn/6_adaptive.mzntex b/assets/mzn/6_adaptive.mzntex new file mode 100644 index 0000000..a9542a3 --- /dev/null +++ b/assets/mzn/6_adaptive.mzntex @@ -0,0 +1,10 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k}{predicate}\PY{l+s}{ }\PY{n+nf}{adaptiveUniform}\PY{p}{(}\PY{k+kt}{array}\PY{p}{[}\PY{k+kt}{int}\PY{g+gr}{]}\PY{l+s}{ }\PY{k+kt}{of}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{x}\PY{p}{,}\PY{l+s}{ }\PY{k+kt}{float}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{initialDestrRate}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{let}\PY{l+s}{ }\PY{p}{\PYZob{}}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{float}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{rate}\PY{p}{;}\PY{l+s}{ }\PY{g+gr}{\PYZcb{}}\PY{l+s}{ }\PY{o}{in} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{if}\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nv}{START}\PY{l+s}{ }\PY{k}{then}\PY{l+s}{ }\PY{n+nv}{rate}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nv}{initialDestrRate} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{elseif}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nv}{UNSAT}\PY{l+s}{ }\PY{k}{then}\PY{l+s}{ }\PY{n+nv}{rate}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nb}{min}\PY{p}{(}\PY{n+nf}{lastval}\PY{p}{(}\PY{n+nv}{rate}\PY{g+gr}{)}\PY{o}{\PYZhy{}}\PY{l+m}{0.02}\PY{p}{,}\PY{l+m}{0.6}\PY{g+gr}{)} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{else}\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{n+nv}{rate}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nb}{max}\PY{p}{(}\PY{n+nf}{lastval}\PY{p}{(}\PY{n+nv}{rate}\PY{g+gr}{)}\PY{o}{+}\PY{l+m}{0.02}\PY{p}{,}\PY{l+m}{0.95}\PY{g+gr}{)} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{endif}\PY{l+s}{ }\PY{o}{/\PYZbs{}} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{forall}\PY{p}{(}\PY{n+nv}{i}\PY{l+s}{ }\PY{o}{in}\PY{l+s}{ }\PY{n+nb}{index\PYZus{}set}\PY{p}{(}\PY{n+nv}{x}\PY{g+gr}{)}\PY{g+gr}{)} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{p}{(}\PY{k}{if}\PY{l+s}{ }\PY{n+nf}{uniform}\PY{p}{(}\PY{l+m}{0.0}\PY{p}{,}\PY{l+m}{1.0}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{\PYZgt{}}\PY{l+s}{ }\PY{n+nv}{rate}\PY{l+s}{ }\PY{k}{then}\PY{l+s}{ }\PY{n+nv}{x}\PY{p}{[}\PY{n+nv}{i}\PY{g+gr}{]}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nf}{sol}\PY{p}{(}\PY{n+nv}{x}\PY{p}{[}\PY{n+nv}{i}\PY{g+gr}{]}\PY{g+gr}{)}\PY{l+s}{ }\PY{k}{else}\PY{l+s}{ }\PY{l}{true}\PY{l+s}{ }\PY{k}{endif}\PY{g+gr}{)}\PY{p}{;} +\end{BVerbatim} diff --git a/assets/mzn/6_basic_complete.mzn b/assets/mzn/6_basic_complete.mzn new file mode 100644 index 0000000..64f4348 --- /dev/null +++ b/assets/mzn/6_basic_complete.mzn @@ -0,0 +1,8 @@ +array[1..n] of var 1..n: x; % decision variables +var int: cost; % objective function +% ... some constraints defining the problem +% The user-defined LNS strategy +predicate my_lns() = basic_lns(uniformNeighbourhood(x,0.2)); +% Solve using my\_lns, restart every 500 nodes, overall timeout 120 seconds +solve ::on_restart("my_lns") ::restart_constant(500) ::timeout(120) + minimize cost; diff --git a/assets/mzn/6_basic_complete.mzntex b/assets/mzn/6_basic_complete.mzntex new file mode 100644 index 0000000..e7c71b8 --- /dev/null +++ b/assets/mzn/6_basic_complete.mzntex @@ -0,0 +1,10 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k+kt}{array}\PY{p}{[}\PY{l+m}{1}\PY{o}{..}\PY{n+nv}{n}\PY{g+gr}{]}\PY{l+s}{ }\PY{k+kt}{of}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{l+m}{1}\PY{o}{..}\PY{n+nv}{n}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{x}\PY{p}{;}\PY{l+s}{ }\PY{l+s}{ }\PY{c}{\PYZpc{} decision variables} +\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{cost}\PY{p}{;}\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{c}{\PYZpc{} objective function} +\PY{c}{\PYZpc{} ... some constraints defining the problem} +\PY{c}{\PYZpc{} The user-defined LNS strategy} +\PY{k}{predicate}\PY{l+s}{ }\PY{n+nf}{my\PYZus{}lns}\PY{p}{(}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nf}{basic\PYZus{}lns}\PY{p}{(}\PY{n+nf}{uniformNeighbourhood}\PY{p}{(}\PY{n+nv}{x}\PY{p}{,}\PY{l+m}{0.2}\PY{g+gr}{)}\PY{g+gr}{)}\PY{p}{;} +\PY{c}{\PYZpc{} Solve using my\_lns, restart every 500 nodes, overall timeout 120 seconds} +\PY{k}{solve}\PY{l+s}{ }\PY{p}{:}\PY{p}{:}\PY{n+nf}{on\PYZus{}restart}\PY{p}{(}\PY{l+s}{\PYZdq{}}\PY{l+s}{my\PYZus{}lns}\PY{l+s}{\PYZdq{}}\PY{l+s}{) ::restart\PYZus{}constant(500) ::timeout(120)} +\PY{l+s}{ minimize cost;} +\end{BVerbatim} diff --git a/assets/mzn/6_basic_complete_transformed.mzn b/assets/mzn/6_basic_complete_transformed.mzn new file mode 100644 index 0000000..e4a4986 --- /dev/null +++ b/assets/mzn/6_basic_complete_transformed.mzn @@ -0,0 +1,16 @@ +var 1..5: s;@ \Vlabel{line:6:status:start}@ +constraint status(s); +var bool b1; +constraint int_ne_reif(s,1,b1); % b1 <-> status()!=START @\Vlabel{line:6:status:end}@ + +var 0.0..1.0: rnd1;@\Vlabel{line:6:x1:start}@ +constraint float_uniform(0.0,1.0,rnd1); +var bool: b2; +constraint float_gt_reif(rnd1,0.2,b2); +var bool: b3; +constraint bool_and(b1,b2,b3); +var 1..3: x1; +constraint int_sol(x[1],x1);@\Vlabel{line:6:x1}@ +% (status()!=START /\ uniform(0.0,1.0)>0.2) -> x[1]=sol(x[1]) +constraint int_eq_imp(x[1],x1,b3); @\Vlabel{line:6:x1:end}@ +@...@ diff --git a/assets/mzn/6_basic_complete_transformed.mzntex b/assets/mzn/6_basic_complete_transformed.mzntex new file mode 100644 index 0000000..121e9d7 --- /dev/null +++ b/assets/mzn/6_basic_complete_transformed.mzntex @@ -0,0 +1,18 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k+kt}{var}\PY{l+s}{ }\PY{l+m}{1}\PY{o}{..}\PY{l+m}{5}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{s}\PY{p}{;}\PY{esc}{ \Vlabel{line:6:status:start}} +\PY{k}{constraint}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{n+nv}{s}\PY{g+gr}{)}\PY{p}{;} +\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{bool}\PY{l+s}{ }\PY{n+nv}{b1}\PY{p}{;} +\PY{k}{constraint}\PY{l+s}{ }\PY{n+nf}{int\PYZus{}ne\PYZus{}reif}\PY{p}{(}\PY{n+nv}{s}\PY{p}{,}\PY{l+m}{1}\PY{p}{,}\PY{n+nv}{b1}\PY{g+gr}{)}\PY{p}{;}\PY{l+s}{ }\PY{c}{\PYZpc{} b1 <-> status()!=START }\PY{esc}{\Vlabel{line:6:status:end}} + +\PY{k+kt}{var}\PY{l+s}{ }\PY{l+m}{0.0}\PY{o}{..}\PY{l+m}{1.0}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{rnd1}\PY{p}{;}\PY{esc}{\Vlabel{line:6:x1:start}} +\PY{k}{constraint}\PY{l+s}{ }\PY{n+nf}{float\PYZus{}uniform}\PY{p}{(}\PY{l+m}{0.0}\PY{p}{,}\PY{l+m}{1.0}\PY{p}{,}\PY{n+nv}{rnd1}\PY{g+gr}{)}\PY{p}{;} +\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{bool}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{b2}\PY{p}{;} +\PY{k}{constraint}\PY{l+s}{ }\PY{n+nf}{float\PYZus{}gt\PYZus{}reif}\PY{p}{(}\PY{n+nv}{rnd1}\PY{p}{,}\PY{l+m}{0.2}\PY{p}{,}\PY{n+nv}{b2}\PY{g+gr}{)}\PY{p}{;} +\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{bool}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{b3}\PY{p}{;} +\PY{k}{constraint}\PY{l+s}{ }\PY{n+nf}{bool\PYZus{}and}\PY{p}{(}\PY{n+nv}{b1}\PY{p}{,}\PY{n+nv}{b2}\PY{p}{,}\PY{n+nv}{b3}\PY{g+gr}{)}\PY{p}{;} +\PY{k+kt}{var}\PY{l+s}{ }\PY{l+m}{1}\PY{o}{..}\PY{l+m}{3}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{x1}\PY{p}{;} +\PY{k}{constraint}\PY{l+s}{ }\PY{n+nf}{int\PYZus{}sol}\PY{p}{(}\PY{n+nv}{x}\PY{p}{[}\PY{l+m}{1}\PY{g+gr}{]}\PY{p}{,}\PY{n+nv}{x1}\PY{g+gr}{)}\PY{p}{;}\PY{esc}{\Vlabel{line:6:x1}} +\PY{c}{\PYZpc{} (status()!=START /\ uniform(0.0,1.0)>0.2) -> x[1]=sol(x[1])} +\PY{k}{constraint}\PY{l+s}{ }\PY{n+nf}{int\PYZus{}eq\PYZus{}imp}\PY{p}{(}\PY{n+nv}{x}\PY{p}{[}\PY{l+m}{1}\PY{g+gr}{]}\PY{p}{,}\PY{n+nv}{x1}\PY{p}{,}\PY{n+nv}{b3}\PY{g+gr}{)}\PY{p}{;}\PY{l+s}{ }\PY{esc}{\Vlabel{line:6:x1:end}} +\PY{esc}{...} +\end{BVerbatim} diff --git a/assets/mzn/6_hill_climbing.mzn b/assets/mzn/6_hill_climbing.mzn new file mode 100644 index 0000000..9f2cb53 --- /dev/null +++ b/assets/mzn/6_hill_climbing.mzn @@ -0,0 +1,3 @@ +predicate hill_climbing() = + if status()=START then true + else _objective < sol(_objective) endif; diff --git a/assets/mzn/6_hill_climbing.mzntex b/assets/mzn/6_hill_climbing.mzntex new file mode 100644 index 0000000..f2eeaa7 --- /dev/null +++ b/assets/mzn/6_hill_climbing.mzntex @@ -0,0 +1,5 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k}{predicate}\PY{l+s}{ }\PY{n+nf}{hill\PYZus{}climbing}\PY{p}{(}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{if}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{g+gr}{)}\PY{o}{=}\PY{n+nv}{START}\PY{l+s}{ }\PY{k}{then}\PY{l+s}{ }\PY{l}{true} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{else}\PY{l+s}{ }\PY{l+s}{\PYZus{}}\PY{l+s}{o}\PY{l+s}{b}\PY{l+s}{j}\PY{l+s}{e}\PY{l+s}{c}\PY{l+s}{t}\PY{l+s}{i}\PY{l+s}{v}\PY{l+s}{e}\PY{l+s}{ }\PY{o}{\PYZlt{}}\PY{l+s}{ }\PY{n+nf}{sol}\PY{p}{(}\PY{l+s}{\PYZus{}}\PY{l+s}{o}\PY{l+s}{b}\PY{l+s}{j}\PY{l+s}{e}\PY{l+s}{c}\PY{l+s}{t}\PY{l+s}{i}\PY{l+s}{v}\PY{l+s}{e}\PY{g+gr}{)}\PY{l+s}{ }\PY{k}{endif}\PY{p}{;} +\end{BVerbatim} diff --git a/assets/mzn/6_lns_minisearch.mzn b/assets/mzn/6_lns_minisearch.mzn new file mode 100644 index 0000000..2227df0 --- /dev/null +++ b/assets/mzn/6_lns_minisearch.mzn @@ -0,0 +1,8 @@ +function ann: lns(var int: obj, array[int] of var int: vars, + int: iterations, float: destrRate, int: exploreTime) = + repeat (i in 1..iterations) ( scope( + if has_sol() then post(uniformNeighbourhood(vars,destrRate)) + else true endif /\ + time_limit(exploreTime, minimize_bab(obj)) /\ + commit() /\ print() + ) /\ post(obj < sol(obj)) ); diff --git a/assets/mzn/6_lns_minisearch.mzntex b/assets/mzn/6_lns_minisearch.mzntex new file mode 100644 index 0000000..6ec515a --- /dev/null +++ b/assets/mzn/6_lns_minisearch.mzntex @@ -0,0 +1,10 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k}{function}\PY{l+s}{ }\PY{k+kt}{ann}\PY{p}{:}\PY{l+s}{ }\PY{n+nf}{lns}\PY{p}{(}\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{obj}\PY{p}{,}\PY{l+s}{ }\PY{k+kt}{array}\PY{p}{[}\PY{k+kt}{int}\PY{g+gr}{]}\PY{l+s}{ }\PY{k+kt}{of}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{vars}\PY{p}{,} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{iterations}\PY{p}{,}\PY{l+s}{ }\PY{k+kt}{float}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{destrRate}\PY{p}{,}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{exploreTime}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=} +\PY{l+s}{ }\PY{l+s}{ }\PY{n+nv}{repeat}\PY{l+s}{ }\PY{p}{(}\PY{n+nv}{i}\PY{l+s}{ }\PY{o}{in}\PY{l+s}{ }\PY{l+m}{1}\PY{o}{..}\PY{n+nv}{iterations}\PY{g+gr}{)}\PY{l+s}{ }\PY{p}{(}\PY{l+s}{ }\PY{n+nf}{scope}\PY{p}{(} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k}{if}\PY{l+s}{ }\PY{n+nf}{has\PYZus{}sol}\PY{p}{(}\PY{g+gr}{)}\PY{l+s}{ }\PY{k}{then}\PY{l+s}{ }\PY{n+nf}{post}\PY{p}{(}\PY{n+nf}{uniformNeighbourhood}\PY{p}{(}\PY{n+nv}{vars}\PY{p}{,}\PY{n+nv}{destrRate}\PY{g+gr}{)}\PY{g+gr}{)} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k}{else}\PY{l+s}{ }\PY{l}{true}\PY{l+s}{ }\PY{k}{endif}\PY{l+s}{ }\PY{o}{/\PYZbs{}} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{n+nf}{time\PYZus{}limit}\PY{p}{(}\PY{n+nv}{exploreTime}\PY{p}{,}\PY{l+s}{ }\PY{n+nf}{minimize\PYZus{}bab}\PY{p}{(}\PY{n+nv}{obj}\PY{g+gr}{)}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{/\PYZbs{}} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{n+nf}{commit}\PY{p}{(}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{/\PYZbs{}}\PY{l+s}{ }\PY{n+nf}{print}\PY{p}{(}\PY{g+gr}{)} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{/\PYZbs{}}\PY{l+s}{ }\PY{n+nf}{post}\PY{p}{(}\PY{n+nv}{obj}\PY{l+s}{ }\PY{o}{\PYZlt{}}\PY{l+s}{ }\PY{n+nf}{sol}\PY{p}{(}\PY{n+nv}{obj}\PY{g+gr}{)}\PY{g+gr}{)}\PY{l+s}{ }\PY{g+gr}{)}\PY{p}{;} +\end{BVerbatim} diff --git a/assets/mzn/6_lns_minisearch_pred.mzn b/assets/mzn/6_lns_minisearch_pred.mzn new file mode 100644 index 0000000..6bbd4f6 --- /dev/null +++ b/assets/mzn/6_lns_minisearch_pred.mzn @@ -0,0 +1,3 @@ +predicate uniformNeighbourhood(array[int] of var int: x, float: destrRate) = + forall(i in index_set(x)) + (if uniform(0.0,1.0) > destrRate then x[i] = sol(x[i]) else true endif); diff --git a/assets/mzn/6_lns_minisearch_pred.mzntex b/assets/mzn/6_lns_minisearch_pred.mzntex new file mode 100644 index 0000000..a6b0e5b --- /dev/null +++ b/assets/mzn/6_lns_minisearch_pred.mzntex @@ -0,0 +1,5 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k}{predicate}\PY{l+s}{ }\PY{n+nf}{uniformNeighbourhood}\PY{p}{(}\PY{k+kt}{array}\PY{p}{[}\PY{k+kt}{int}\PY{g+gr}{]}\PY{l+s}{ }\PY{k+kt}{of}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{x}\PY{p}{,}\PY{l+s}{ }\PY{k+kt}{float}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{destrRate}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{forall}\PY{p}{(}\PY{n+nv}{i}\PY{l+s}{ }\PY{o}{in}\PY{l+s}{ }\PY{n+nb}{index\PYZus{}set}\PY{p}{(}\PY{n+nv}{x}\PY{g+gr}{)}\PY{g+gr}{)} +\PY{l+s}{ }\PY{l+s}{ }\PY{p}{(}\PY{k}{if}\PY{l+s}{ }\PY{n+nf}{uniform}\PY{p}{(}\PY{l+m}{0.0}\PY{p}{,}\PY{l+m}{1.0}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{\PYZgt{}}\PY{l+s}{ }\PY{n+nv}{destrRate}\PY{l+s}{ }\PY{k}{then}\PY{l+s}{ }\PY{n+nv}{x}\PY{p}{[}\PY{n+nv}{i}\PY{g+gr}{]}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nf}{sol}\PY{p}{(}\PY{n+nv}{x}\PY{p}{[}\PY{n+nv}{i}\PY{g+gr}{]}\PY{g+gr}{)}\PY{l+s}{ }\PY{k}{else}\PY{l+s}{ }\PY{l}{true}\PY{l+s}{ }\PY{k}{endif}\PY{g+gr}{)}\PY{p}{;} +\end{BVerbatim} diff --git a/assets/mzn/6_restart_ann.mzn b/assets/mzn/6_restart_ann.mzn new file mode 100644 index 0000000..398a141 --- /dev/null +++ b/assets/mzn/6_restart_ann.mzn @@ -0,0 +1,18 @@ +% post predicate "pred" whenever the solver restarts +annotation on_restart(string: pred); +% restart after fixed number of nodes +annotation restart_constant(int: nodes); +% restart with scaled Luby sequence +annotation restart_luby(int: scale); +% restart with scaled geometric sequence ($scale*base^n$ in the $n$-th iteration) +annotation restart_geometric(float: base, int: scale); +% restart with linear sequence ($scale*n$ in the $n$-th iteration) +annotation restart_linear(int: scale); +% restart on each solution +annotation restart_on_solution; +% restart without branch-and-bound constraints on the objective +annotation restart_without_objective; +% overall time limit for search +annotation timeout(int: seconds); +% overall limit on number of restarts +annotation restart_limit(int: n_restarts); diff --git a/assets/mzn/6_restart_ann.mzntex b/assets/mzn/6_restart_ann.mzntex new file mode 100644 index 0000000..d804fbf --- /dev/null +++ b/assets/mzn/6_restart_ann.mzntex @@ -0,0 +1,20 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{c}{\PYZpc{} post predicate "pred" whenever the solver restarts} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nf}{on\PYZus{}restart}\PY{p}{(}\PY{k+kt}{string}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{pred}\PY{g+gr}{)}\PY{p}{;} +\PY{c}{\PYZpc{} restart after fixed number of nodes} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nf}{restart\PYZus{}constant}\PY{p}{(}\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{nodes}\PY{g+gr}{)}\PY{p}{;} +\PY{c}{\PYZpc{} restart with scaled Luby sequence} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nf}{restart\PYZus{}luby}\PY{p}{(}\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{scale}\PY{g+gr}{)}\PY{p}{;} +\PY{c}{\PYZpc{} restart with scaled geometric sequence ($scale*base^n$ in the $n$-th iteration)} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nf}{restart\PYZus{}geometric}\PY{p}{(}\PY{k+kt}{float}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{base}\PY{p}{,}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{scale}\PY{g+gr}{)}\PY{p}{;} +\PY{c}{\PYZpc{} restart with linear sequence ($scale*n$ in the $n$-th iteration)} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nf}{restart\PYZus{}linear}\PY{p}{(}\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{scale}\PY{g+gr}{)}\PY{p}{;} +\PY{c}{\PYZpc{} restart on each solution} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nv}{restart\PYZus{}on\PYZus{}solution}\PY{p}{;} +\PY{c}{\PYZpc{} restart without branch-and-bound constraints on the objective} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nv}{restart\PYZus{}without\PYZus{}objective}\PY{p}{;} +\PY{c}{\PYZpc{} overall time limit for search} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nf}{timeout}\PY{p}{(}\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{seconds}\PY{g+gr}{)}\PY{p}{;} +\PY{c}{\PYZpc{} overall limit on number of restarts} +\PY{k}{annotation}\PY{l+s}{ }\PY{n+nf}{restart\PYZus{}limit}\PY{p}{(}\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{n\PYZus{}restarts}\PY{g+gr}{)}\PY{p}{;} +\end{BVerbatim} diff --git a/assets/mzn/6_round_robin.mzn b/assets/mzn/6_round_robin.mzn new file mode 100644 index 0000000..d00fe13 --- /dev/null +++ b/assets/mzn/6_round_robin.mzn @@ -0,0 +1,7 @@ +predicate round_robin(array[int] of var bool: nbhs) = + let { int: N = length(nbhs); + var -1..N-1: select; % Neighbourhood selection + } in if status()=START then select= -1 + else select= (lastval(select) + 1) mod N + endif /\ + forall(i in 1..N) (select=i-1 -> nbhs[i]);@\Vlabel{line:6:roundrobin:post}@ diff --git a/assets/mzn/6_round_robin.mzntex b/assets/mzn/6_round_robin.mzntex new file mode 100644 index 0000000..352d358 --- /dev/null +++ b/assets/mzn/6_round_robin.mzntex @@ -0,0 +1,9 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k}{predicate}\PY{l+s}{ }\PY{n+nf}{round\PYZus{}robin}\PY{p}{(}\PY{k+kt}{array}\PY{p}{[}\PY{k+kt}{int}\PY{g+gr}{]}\PY{l+s}{ }\PY{k+kt}{of}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{bool}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{nbhs}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k}{let}\PY{l+s}{ }\PY{p}{\PYZob{}}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{N}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nb}{length}\PY{p}{(}\PY{n+nv}{nbhs}\PY{g+gr}{)}\PY{p}{;} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{o}{\PYZhy{}}\PY{l+m}{1}\PY{o}{..}\PY{n+nv}{N}\PY{o}{\PYZhy{}}\PY{l+m}{1}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{select}\PY{p}{;}\PY{l+s}{ }\PY{c}{\PYZpc{} Neighbourhood selection} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{g+gr}{\PYZcb{}}\PY{l+s}{ }\PY{o}{in}\PY{l+s}{ }\PY{l+s}{ }\PY{k}{if}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{g+gr}{)}\PY{o}{=}\PY{n+nv}{START}\PY{l+s}{ }\PY{k}{then}\PY{l+s}{ }\PY{n+nv}{select}\PY{o}{=}\PY{l+s}{ }\PY{o}{\PYZhy{}}\PY{l+m}{1} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k}{else}\PY{l+s}{ }\PY{n+nv}{select}\PY{o}{=}\PY{l+s}{ }\PY{p}{(}\PY{n+nf}{lastval}\PY{p}{(}\PY{n+nv}{select}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{+}\PY{l+s}{ }\PY{l+m}{1}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{mod}\PY{l+s}{ }\PY{n+nv}{N} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k}{endif}\PY{l+s}{ }\PY{o}{/\PYZbs{}} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k}{forall}\PY{p}{(}\PY{n+nv}{i}\PY{l+s}{ }\PY{o}{in}\PY{l+s}{ }\PY{l+m}{1}\PY{o}{..}\PY{n+nv}{N}\PY{g+gr}{)}\PY{l+s}{ }\PY{p}{(}\PY{n+nv}{select}\PY{o}{=}\PY{n+nv}{i}\PY{o}{\PYZhy{}}\PY{l+m}{1}\PY{l+s}{ }\PY{o}{\PYZhy{}\PYZgt{}}\PY{l+s}{ }\PY{n+nv}{nbhs}\PY{p}{[}\PY{n+nv}{i}\PY{g+gr}{]}\PY{g+gr}{)}\PY{p}{;}\PY{esc}{\Vlabel{line:6:roundrobin:post}} +\end{BVerbatim} diff --git a/assets/mzn/6_simulated_annealing.mzn b/assets/mzn/6_simulated_annealing.mzn new file mode 100644 index 0000000..3bc9ecc --- /dev/null +++ b/assets/mzn/6_simulated_annealing.mzn @@ -0,0 +1,7 @@ +predicate simulated_annealing(float: initTemp, float: coolingRate) = + let { var float: temp; } in + if status()=START then temp = initTemp + else + temp = lastval(temp)*(1-coolingRate) /\ % cool down + _objective < sol(_objective) - ceil(log(uniform(0.0,1.0)) * temp) + endif; diff --git a/assets/mzn/6_simulated_annealing.mzntex b/assets/mzn/6_simulated_annealing.mzntex new file mode 100644 index 0000000..cf7aa70 --- /dev/null +++ b/assets/mzn/6_simulated_annealing.mzntex @@ -0,0 +1,9 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k}{predicate}\PY{l+s}{ }\PY{n+nf}{simulated\PYZus{}annealing}\PY{p}{(}\PY{k+kt}{float}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{initTemp}\PY{p}{,}\PY{l+s}{ }\PY{k+kt}{float}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{coolingRate}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{let}\PY{l+s}{ }\PY{p}{\PYZob{}}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{float}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{temp}\PY{p}{;}\PY{l+s}{ }\PY{g+gr}{\PYZcb{}}\PY{l+s}{ }\PY{o}{in} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{if}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{g+gr}{)}\PY{o}{=}\PY{n+nv}{START}\PY{l+s}{ }\PY{k}{then}\PY{l+s}{ }\PY{n+nv}{temp}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nv}{initTemp} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{else} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{n+nv}{temp}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{n+nf}{lastval}\PY{p}{(}\PY{n+nv}{temp}\PY{g+gr}{)}\PY{o}{*}\PY{p}{(}\PY{l+m}{1}\PY{o}{\PYZhy{}}\PY{n+nv}{coolingRate}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{/\PYZbs{}}\PY{l+s}{ }\PY{c}{\PYZpc{} cool down} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{\PYZus{}}\PY{l+s}{o}\PY{l+s}{b}\PY{l+s}{j}\PY{l+s}{e}\PY{l+s}{c}\PY{l+s}{t}\PY{l+s}{i}\PY{l+s}{v}\PY{l+s}{e}\PY{l+s}{ }\PY{o}{\PYZlt{}}\PY{l+s}{ }\PY{n+nf}{sol}\PY{p}{(}\PY{l+s}{\PYZus{}}\PY{l+s}{o}\PY{l+s}{b}\PY{l+s}{j}\PY{l+s}{e}\PY{l+s}{c}\PY{l+s}{t}\PY{l+s}{i}\PY{l+s}{v}\PY{l+s}{e}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{\PYZhy{}}\PY{l+s}{ }\PY{n+nb}{ceil}\PY{p}{(}\PY{n+nb}{log}\PY{p}{(}\PY{n+nf}{uniform}\PY{p}{(}\PY{l+m}{0.0}\PY{p}{,}\PY{l+m}{1.0}\PY{g+gr}{)}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{*}\PY{l+s}{ }\PY{n+nv}{temp}\PY{g+gr}{)} +\PY{l+s}{ }\PY{l+s}{ }\PY{k}{endif}\PY{p}{;} +\end{BVerbatim} diff --git a/assets/mzn/6_state_access.mzn b/assets/mzn/6_state_access.mzn new file mode 100644 index 0000000..2a8732a --- /dev/null +++ b/assets/mzn/6_state_access.mzn @@ -0,0 +1,5 @@ +% Report the status of the solver (before restarting). +enum STATUS = {START, UNKNOWN, UNSAT, SAT, OPT} @\label{ann:enum_status}@ +function STATUS: status(); @\label{ann:status}@ +% Provide access to the last assigned value of variable x. +function int: lastval(var int: x); diff --git a/assets/mzn/6_state_access.mzntex b/assets/mzn/6_state_access.mzntex new file mode 100644 index 0000000..7547547 --- /dev/null +++ b/assets/mzn/6_state_access.mzntex @@ -0,0 +1,7 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{c}{\PYZpc{} Report the status of the solver (before restarting).} +\PY{k+kt}{enum}\PY{l+s}{ }\PY{n+nv}{STATUS}\PY{l+s}{ }\PY{o}{=}\PY{l+s}{ }\PY{p}{\PYZob{}}\PY{n+nv}{START}\PY{p}{,}\PY{l+s}{ }\PY{n+nv}{UNKNOWN}\PY{p}{,}\PY{l+s}{ }\PY{n+nv}{UNSAT}\PY{p}{,}\PY{l+s}{ }\PY{n+nv}{SAT}\PY{p}{,}\PY{l+s}{ }\PY{n+nv}{OPT}\PY{g+gr}{\PYZcb{}}\PY{l+s}{ }\PY{esc}{\label{ann:enum_status}} +\PY{k}{function}\PY{l+s}{ }\PY{n+nv}{STATUS}\PY{p}{:}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{g+gr}{)}\PY{p}{;}\PY{l+s}{ }\PY{esc}{\label{ann:status}} +\PY{c}{\PYZpc{} Provide access to the last assigned value of variable x.} +\PY{k}{function}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nf}{lastval}\PY{p}{(}\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{x}\PY{g+gr}{)}\PY{p}{;} +\end{BVerbatim} diff --git a/assets/mzn/6_status.mzn b/assets/mzn/6_status.mzn new file mode 100644 index 0000000..d90c637 --- /dev/null +++ b/assets/mzn/6_status.mzn @@ -0,0 +1,5 @@ +predicate status(var int: stat); @\Vlabel{line:6:status}@ +function var STATUS: status() = + let { var STATUS: stat; + constraint status(stat); + } in stat; diff --git a/assets/mzn/6_status.mzntex b/assets/mzn/6_status.mzntex new file mode 100644 index 0000000..d6b80e9 --- /dev/null +++ b/assets/mzn/6_status.mzntex @@ -0,0 +1,7 @@ +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\PY{k}{predicate}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{k+kt}{var}\PY{l+s}{ }\PY{k+kt}{int}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{stat}\PY{g+gr}{)}\PY{p}{;}\PY{l+s}{ }\PY{esc}{\Vlabel{line:6:status}} +\PY{k}{function}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{n+nv}{STATUS}\PY{p}{:}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{g+gr}{)}\PY{l+s}{ }\PY{o}{=} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k}{let}\PY{l+s}{ }\PY{p}{\PYZob{}}\PY{l+s}{ }\PY{k+kt}{var}\PY{l+s}{ }\PY{n+nv}{STATUS}\PY{p}{:}\PY{l+s}{ }\PY{n+nv}{stat}\PY{p}{;} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{k}{constraint}\PY{l+s}{ }\PY{n+nf}{status}\PY{p}{(}\PY{n+nv}{stat}\PY{g+gr}{)}\PY{p}{;} +\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{l+s}{ }\PY{g+gr}{\PYZcb{}}\PY{l+s}{ }\PY{o}{in}\PY{l+s}{ }\PY{n+nv}{stat}\PY{p}{;} +\end{BVerbatim} diff --git a/assets/mzn/minilns/assign_random.mzn b/assets/mzn/minilns/assign_random.mzn new file mode 100644 index 0000000..f501ea6 --- /dev/null +++ b/assets/mzn/minilns/assign_random.mzn @@ -0,0 +1,3 @@ +predicate assign_random(array[int] of var int: X, int: R) = + forall (i in index_set(X)) + (if uniform(1,100) < R then X[i] = sol(X[i]) else true endif); diff --git a/assets/mzn/minilns/basic.mzn b/assets/mzn/minilns/basic.mzn new file mode 100644 index 0000000..dee8efb --- /dev/null +++ b/assets/mzn/minilns/basic.mzn @@ -0,0 +1,3 @@ +predicate basic_LNS() = + (status() != START) -> nbh(X); + diff --git a/assets/mzn/minilns/restart.mzn b/assets/mzn/minilns/restart.mzn new file mode 100644 index 0000000..343e787 --- /dev/null +++ b/assets/mzn/minilns/restart.mzn @@ -0,0 +1,19 @@ +% A search annotation that supplies a function to be executed on restart +ann: on_restart(string: pred); @\Vlabel{ann:on_restart}@ +% An annotation for FlatZinc that accesses solver state +ann: solver_state; +% The 'status' function reports the status of the solver (before restarting). +enum STATUS = {START, UNKNOWN, UNSAT, SAT, OPT} @\Vlabel{ann:enum_status}@ +function var STATUS: status(); @\Vlabel{ann:status}@ +% The 'sol' functions provides access to solution values of model variables. +% The sol functions are meaningful when the solver status is not START. +function var bool: sol(var bool: x); @\Vlabel{ann:sol1}@ +function var float: sol(var float: x); @\Vlabel{ann:sol2}@ +function var int: sol(var int: x); @\Vlabel{ann:sol3}@ +function var set of int: sol(var set of int: x); @\Vlabel{ann:sol4}@ +% The 'lastval' functions provides access to the last assigned value of variables. +% The lastval functions are meaningful when the solver status is not START. +function var bool: lastval(var bool: x); +function var float: lastval(var float: x); +function var int: lastval(var int: x); +function var set of int: lastval(var set of int: x); diff --git a/assets/packages.tex b/assets/packages.tex index 30c6ae4..3c0fadb 100644 --- a/assets/packages.tex +++ b/assets/packages.tex @@ -1,3 +1,4 @@ +\hfuzz=1.5pt \usepackage{csquotes} \usepackage[australian]{babel} \usepackage{hyperref} @@ -36,9 +37,7 @@ BoldItalicFont=*-BoldItalic, ] %% Mathmatical font \usepackage{unicode-math} -\setmathfont[ -Scale=1.4 -]{GFSNeohellenicMath.otf} +\setmathfont{GFSNeohellenicMath.otf} % References \usepackage[ @@ -48,6 +47,7 @@ style=apa, % Glossary / Acronyms \usepackage[acronym,toc]{glossaries} +\glsdisablehyper \defglsentryfmt[main]{\ifglsused{\glslabel}{\glsgenentryfmt}{\textit{\glsgenentryfmt}}} \makeglossaries{} @@ -55,23 +55,30 @@ style=apa, \usepackage{fancyvrb} \usepackage{color} \input{assets/pygments_header.tex} +\newcommand{\highlightfile}[1]{\input{#1tex}} \DeclareNewTOC[ -type=program, +type=listing, float, -name=Program, +name=Listing, counterwithin=chapter, atbegin={% +\centering \scriptsize } ]{program} -\crefname{program}{program}{programs} -\DeclareNewTOC[ -type=model, -float, -name=Model, -counterwithin=chapter, -atbegin={% -\scriptsize + +\crefname{listing}{listing}{listings} +\newcommand{\Vlabel}[1]{\label[line]{#1}\hypertarget{#1}{}} +\newcommand{\lref}[1]{\hyperlink{#1}{\FancyVerbLineautorefname~\ref*{#1}}} +\newcommand{\lrefrange}[2]{\FancyVerbLineautorefname{}s~\hyperlink{#1}{\ref*{#1}}--\hyperlink{#2}{\ref*{#2}}} +\newcommand{\Lrefrange}[2]{Lines~\hyperlink{#1}{\ref*{#1}}--\hyperlink{#2}{\ref*{#2}}} + +% TODO: What am I doing with this? +\newcommand*\justify{% + \fontdimen2\font=0.4em% interword space + \fontdimen3\font=0.2em% interword stretch + \fontdimen4\font=0.1em% interword shrink + \fontdimen7\font=0.1em% extra space + \hyphenchar\font=`\-% allowing hyphenation } -]{model} -\crefname{model}{model}{models} +\newcommand{\mzninline}[1]{\texttt{\small\justify\detokenize{#1}}} diff --git a/assets/py/2_dyn_knapsack.pytex b/assets/py/2_dyn_knapsack.pytex index d106337..5594e27 100644 --- a/assets/py/2_dyn_knapsack.pytex +++ b/assets/py/2_dyn_knapsack.pytex @@ -1,4 +1,4 @@ -\begin{Verbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] +\begin{BVerbatim}[commandchars=\\\{\},numbers=left,firstnumber=1,stepnumber=1,codes={\catcode`\$=3\catcode`\^=7\catcode`\_=8}] \PY{n}{toys\PYZus{}joy} \PY{o}{=} \PY{p}{[}\PY{l+m+mi}{63}\PY{p}{,} \PY{l+m+mi}{12}\PY{p}{,} \PY{l+m+mi}{50}\PY{p}{,} \PY{l+m+mi}{100}\PY{p}{]} \PY{n}{toys\PYZus{}space} \PY{o}{=} \PY{p}{[}\PY{l+m+mi}{32}\PY{p}{,} \PY{l+m+mi}{8}\PY{p}{,} \PY{l+m+mi}{16}\PY{p}{,} \PY{l+m+mi}{40}\PY{p}{]} \PY{n}{space\PYZus{}left} \PY{o}{=} \PY{l+m+mi}{64} @@ -27,4 +27,4 @@ \PY{n}{table}\PY{p}{[}\PY{n}{i}\PY{p}{]}\PY{p}{[}\PY{n}{j}\PY{p}{]} \PY{o}{=} \PY{n}{table}\PY{p}{[}\PY{n}{i} \PY{o}{\PYZhy{}} \PY{l+m+mi}{1}\PY{p}{]}\PY{p}{[}\PY{n}{j}\PY{p}{]} \PY{n}{optimal\PYZus{}joy} \PY{o}{=} \PY{n}{table}\PY{p}{[}\PY{n}{num\PYZus{}toys}\PY{p}{]}\PY{p}{[}\PY{n}{space\PYZus{}left}\PY{p}{]} -\end{Verbatim} +\end{BVerbatim} diff --git a/assets/shorthands.tex b/assets/shorthands.tex new file mode 100644 index 0000000..27aecaa --- /dev/null +++ b/assets/shorthands.tex @@ -0,0 +1,4 @@ +\newcommand{\eg}{e.g.} +\newcommand{\flatzinc}{\gls{flatzinc}} +\newcommand{\minisearch}{\gls{minisearch}} +\newcommand{\minizinc}{\gls{minizinc}} diff --git a/chapters/2_background.tex b/chapters/2_background.tex index 0292507..e158332 100644 --- a/chapters/2_background.tex +++ b/chapters/2_background.tex @@ -22,33 +22,33 @@ amount of space left in the car, so we cannot bring all the toys. Since Audrey gets enjoys playing with some toys more than others, we can now try and pick the toys that bring Audrey the most amount of joy, but still fit in the car. -\begin{program}[ht] - \input{assets/py/2_dyn_knapsack.pytex} - \caption{\label{prog:dyn-knapsack} A Python program that solves a 0-1 knapsack +\begin{listing}[ht] + \highlightfile{assets/py/2_dyn_knapsack.py} + \caption{\label{lst:2-dyn-knapsack} A Python program that solves a 0-1 knapsack problem using dynamic programming} -\end{program} +\end{listing} A well educated reader in optimisation problems might immediately recognise that this is a variation on the widely known \textit{knapsack problem}, more specifically a \textit{0-1 knapsack problem} \autocite[13--67]{silvano-1990-knapsack}. A commonly used solution to this problem is based on dynamic programming. An implementation of this approach is -shown in \cref{prog:dyn-knapsack}. In a naive recursive approach we would try +shown in \cref{lst:2-dyn-knapsack}. In a naive recursive approach we would try all different combinations of toys to find the combination that will give the most joy, but using a dynamic programming approach this exponential behaviour (on the number of toys) can be avoided. -\begin{model}[ht] - \input{assets/mzn/2_knapsack.mzntex} - \caption{\label{model:knapsack} A \minizinc\ model describing a 0-1 knapsack +\begin{listing}[ht] + \highlightfile{assets/mzn/2_knapsack.mzn} + \caption{\label{lst:2-mzn-knapsack} A \minizinc\ model describing a 0-1 knapsack problem} -\end{model} +\end{listing} A constraint model offers a different view of the problem. Instead of specifying the manner in which we can find the solution, we give a concise description of the problem in terms of what we already know, the \glspl{problem-parameter}, what we wish to know, the \glspl{decision-variable}, and the relationships that -should exists between them, the \glspl{constraint}. \Cref{model:knapsack} shows +should exists between them, the \glspl{constraint}. \Cref{lst:2-mzn-knapsack} shows a \minizinc\ model of the knapsack problem, where the different elements of the constraint model are separated. Although a constraint model does not contain any instructions to find a suitable solutions, these models can generally be given diff --git a/chapters/6_incremental.tex b/chapters/6_incremental.tex index d2fa9c7..ffe433e 100644 --- a/chapters/6_incremental.tex +++ b/chapters/6_incremental.tex @@ -1,3 +1,529 @@ -%************************************************ \chapter{Incremental Solving}\label{ch:incremental} %************************************************ + +\section{Modelling of Neighbourhoods and Meta-heuristics} +\label{section:2-modelling-nbhs} +% +% Start with a brief review of most common neighbourhoods and then explain: +% \begin{itemize} +% \item Random built in with integers +% \begin{itemize} +% \item Explain the built in +% \item Give an example of use (use model) +% \item Limitations if any +% \end{itemize} + +% \item Solution based one +% \begin{itemize} +% \item Explain the built in +% \item Give an example of use (use model) +% \item Limitations if any +% \end{itemize} +% \end{itemize} + +% End with future work for other built ins (hint which ones would be useful). + +Most LNS literature discusses neighbourhoods in terms of ``destroying'' part of +a solution that is later repaired. However, from a declarative modelling point +of view, it is more natural to see neighbourhoods as adding new constraints and +variables that need to be applied to the base model, \eg forcing variables to +take the same value as in the previous solution. + +This section introduces a \minizinc\ extension that enables modellers to define +neighbourhoods using the $\mathit{nbh(a)}$ approach described above. This +extension is based on the constructs introduced in +\minisearch\~\autocite{rendl-2015-minisearch}, as summarised below. + +\subsection{LNS in \glsentrytext{minisearch}} + +\minisearch\ introduced a \minizinc\ extension that enables modellers to express +meta-searches inside a \minizinc\ model. A meta-search in \minisearch\ typically +solves a given \minizinc\ model, performs some calculations on the solution, adds +new constraints and then solves again. + +An LNS definition in \minisearch\ consists of two parts. The first part is a +declarative definition of a neighbourhood as a \minizinc\ predicate that posts +the constraints that should be added with respect to a previous solution. This +makes use of the \minisearch\ function: \mzninline{function int: sol(var int: + x)}, which returns the value that variable \mzninline{x} was assigned to in +the previous solution (similar functions are defined for Boolean, float and set +variables). In addition, a neighbourhood predicate will typically make use of +the random number generators available in the \minizinc\ standard library. +\Cref{lst:6-lns-minisearch-pred} shows a simple random neighbourhood. For each +decision variable \mzninline{x[i]}, it draws a random number from a uniform +distribution and, if it exceeds threshold \mzninline{destrRate}, posts +constraints forcing \mzninline{x[i]} to take the same value as in the previous +solution. For example, \mzninline{uniformNeighbourhood(x, 0.2)} would result in +each variable in the array \mzninline{x} having a 20\% chance of being +unconstrained, and an 80\% chance of being assigned to the value it had in the +previous solution. + +\begin{listing} + \highlightfile{assets/mzn/6_lns_minisearch_pred.mzn} + \caption{\label{lst:6-lns-minisearch-pred} A simple random LNS predicate + implemented in \minisearch{}} +\end{listing} + +\begin{listing} + \highlightfile{assets/mzn/6_lns_minisearch.mzn} + \caption{\label{lst:6-lns-minisearch} A simple LNS metaheuristic implemented + in \minisearch{}} +\end{listing} + +The second part of a \minisearch\ LNS is the meta-search itself. The most basic +example is that of function \mzninline{lns} in \cref{lst:6-lns-minisearch}. It +performs a fixed number of iterations, each invoking the neighbourhood predicate +\mzninline{uniformNeighbourhood} in a fresh scope (so that the constraints only +affect the current loop iteration). It then searches for a solution +(\mzninline{minimize_bab}) with a given timeout, and if the search does return a +new solution, it commits to that solution (so that it becomes available to the +\mzninline{sol} function in subsequent iterations). The \texttt{lns} function +also posts the constraint \mzninline{obj < sol(obj)}, ensuring the objective +value in the next iteration is strictly better than that of the current +solution. + +\paragraph{Limitations of the \minisearch\ approach.} + +Although \minisearch\ enables the modeller to express \emph{neighbourhoods} in a +declarative way, the definition of the \emph{meta-search} is rather unintuitive +and difficult to debug, leading to unwieldy code for defining simple restarting +strategies. Furthermore, the \minisearch\ implementation requires either a close +integration of the backend solver into the \minisearch\ system, or it drives the +solver through the regular text-file based \flatzinc\ interface, leading to a +significant communication overhead. + +To address these two issues for LNS, we propose to keep modelling neighbourhoods +as predicates, but define a small number of additional \minizinc\ built-in +annotations and functions that (a) allow us to express important aspects of the +meta-search in a more convenient way, and (b) enable a simple compilation scheme +that requires no additional communication with and only small, simple extensions +of the backend solver. + +The approach we follow here is therefore to \textbf{extend \flatzinc}, such that +the definition of neighbourhoods can be communicated to the solver together with +the problem instance. This maintains the loose coupling of \minizinc\ and +solver, while avoiding the costly communication and cold-starting of the +black-box approach. + +\subsection{Restart annotations} + +Instead of the complex \minisearch\ definitions, we propose to add support for +simple meta-searches that are purely based on the notion of \emph{restarts}. A +restart happens when a solver abandons its current search efforts, returns to +the root node of the search tree, and begins a new exploration. Many CP solvers +already provide support for controlling their restarting behaviour, e.g.\ they +can periodically restart after a certain number of nodes, or restart for every +solution. Typically, solvers also support posting additional constraints upon +restarting (e.g Comet~\autocite{michel-2005-comet}) that are only valid for the +particular restart (i.e., they are ``retracted'' for the next restart). + +In its simplest form, we can therefore implement LNS by specifying a +neighbourhood predicate, and annotating the \mzninline{solve} item to indicate +the predicate should be invoked upon each restart: + +\mzninline{solve ::on_restart(myNeighbourhood) minimize cost;} + +Note that \minizinc\ currently does not support passing functions or predicates +as arguments. Calling the predicate, as in +\mzninline{::on_restart(myNeighbourhood())}, would not have the correct +semantics, since the predicate needs to be called for \emph{each} restart. As a +workaround, we currently pass the name of the predicate to be called for each +restart as a string (see the definition of the new \mzninline{on_restart} +annotation in \cref{lst:6-restart-ann}). + +The second component of our LNS definition is the \emph{restarting strategy}, +defining how much effort the solver should put into each neighbourhood (i.e., +restart), and when to stop the overall search. + +We propose adding new search annotations to \minizinc\ to control this behaviour +(see \cref{lst:6-restart-ann}). The \mzninline{restart_on_solution} annotation +tells the solver to restart immediately for each solution, rather than looking +for the best one in each restart, while \mzninline{restart_without_objective} +tells it not to add branch-and-bound constraints on the objective. The other +\mzninline{restart_X} annotations define different strategies for restarting the +search when no solution is found. The \mzninline{timeout} annotation gives an +overall time limit for the search, whereas \mzninline{restart_limit} stops the +search after a fixed number of restarts. + +\begin{listing}[t] + \highlightfile{assets/mzn/6_restart_ann.mzn} + \caption{\label{lst:6-restart-ann} New annotations to control the restarting + behaviour} +\end{listing} + +\subsection{Neighbourhood selection} + +It is often beneficial to use several neighbourhood definitions for a problem. +Different neighbourhoods may be able to improve different aspects of a solution, +at different phases of the search. Adaptive LNS \autocite{ropke-2006-adaptive, + pisinger-2007-heuristic}, which keeps track of the neighbourhoods that led to +improvements and favours them for future iterations, is the prime example for +this approach. A simpler scheme may apply several neighbourhoods in a +round-robin fashion. + +In \minisearch\, adaptive or round-robin approaches can be implemented using +\emph{state variables}, which support destructive update (overwriting the value +they store). In this way, the \minisearch\ strategy can store values to be used +in later iterations. We use the \emph{solver state} instead, i.e., normal +decision variables, and define two simple built-in functions to access the +solver state \emph{of the previous restart}. This approach is sufficient for +expressing neighbourhood selection strategies, and its implementation is much +simpler. + +\paragraph{State access and initialisation} + +The state access functions are defined in \cref{lst:6-state-access}. Function +\mzninline{status} returns the status of the previous restart, namely: +\mzninline{START} (there has been no restart yet); \mzninline{UNSAT} (the +restart failed); \mzninline{SAT} (the restart found a solution); \mzninline{OPT} +(the restart found and proved an optimal solution); and \mzninline{UNKNOWN} (the +restart did not fail or find a solution). Function \mzninline{lastval} (which, +like \mzninline{sol}, has versions for all basic variable types) allows +modellers to access the last value assigned to a variable (the value is +undefined if \mzninline{status()=START}). + +\begin{listing}[t] + \highlightfile{assets/mzn/6_state_access.mzn} + \caption{\label{lst:6-state-access} Functions for accessing previous solver + states} +\end{listing} + +In order to be able to initialise the variables used for state access, we +reinterpret \mzninline{on_restart} so that the predicate is also called for the +initial search (i.e., before the first ``real'' restart) with the same +semantics, that is, any constraint posted by the predicate will be retracted for +the next restart. + +\paragraph{Parametric neighbourhood selection predicates} + +We define standard neighbourhood selection strategies as predicates that are +parametric over the neighbourhoods they should apply. For example, since +\mzninline{on_restart} now also includes the initial search, we can define a +strategy \mzninline{basic_lns} that applies a neighbourhood only if the current +status is not \mzninline{START}: + +\mzninline{predicate basic_lns(var bool: nbh) = (status()!=START -> nbh);} + +In order to use this predicate with the \mzninline{on_restart} annotation, we +cannot simply pass \mzninline{basic_lns(uniformNeighbourhood(x,0.2))}. First of +all, calling \mzninline{uniformNeighbourhood} like that would result in a +\emph{single} evaluation of the predicate, since \minizinc\ employs a +call-by-value evaluation strategy. Furthermore, the \mzninline{on_restart} +annotation only accepts the name of a nullary predicate. Therefore, users have +to define their overall strategy in a new predicate. \Cref{lst:6-basic-complete} +shows a complete example of a basic LNS model. + +\begin{listing}[t] + \highlightfile{assets/mzn/6_basic_complete.mzn} + \caption{\label{lst:6-basic-complete} Complete LNS example} +\end{listing} + +We can also define round-robin and adaptive strategies using these primitives. +%\paragraph{Round-robin LNS} +\Cref{lst:6-round-robin} defines a round-robin LNS meta-heuristic, which cycles +through a list of \mzninline{N} neighbourhoods \mzninline{nbhs}. To do this, it +uses the decision variable \mzninline{select}. In the initialisation phase +(\mzninline{status()=START}), \mzninline{select} is set to \mzninline{-1}, which +means none of the neighbourhoods is activated. In any following restart, +\mzninline{select} is incremented modulo \mzninline{N}, by accessing the last +value assigned in a previous restart (\mzninline{lastval(select)}). This will +activate a different neighbourhood for each restart +(\lref{line:6:roundrobin:post}). + +\begin{listing}[t] + \highlightfile{assets/mzn/6_round_robin.mzn} + \caption{\label{lst:6-round-robin} A predicate providing the round robin + meta-heuristic} +\end{listing} + +%\paragraph{Adaptive LNS} +For adaptive LNS, a simple strategy is to change the size of the neighbourhood +depending on whether the previous size was successful or not. +\Cref{lst:6-adaptive} shows an adaptive version of the +\mzninline{uniformNeighbourhood} that increases the number of free variables +when the previous restart failed, and decreases it when it succeeded, within the +bounds $[0.6,0.95]$. + +\begin{listing}[t] + \highlightfile{assets/mzn/6_adaptive.mzn} + \caption{\label{lst:6-adaptive} A simple adaptive neighbourhood} +\end{listing} + +\subsection{Meta-heuristics} + +The LNS strategies we have seen so far rely on the default behaviour of +\minizinc\ solvers to use branch-and-bound for optimisation: when a new solution +is found, the solver adds a constraint to the remainder of the search to only +accept better solutions, as defined by the objective function in the +\mzninline{minimize} or \mzninline{maximize} clause of the \mzninline{solve} +item. When combined with restarts and LNS, this is equivalent to a simple +hill-climbing meta-heuristic. + +We can use the constructs introduced above to implement alternative +meta-heuristics such as simulated annealing. In particular, we use +\mzninline{restart_without_objective} to tell the solver not to add the +branch-and-bound constraint on restart. It will still use the declared objective +to decide whether a new solution is the globally best one seen so far, and only +output those (to maintain the convention of \minizinc\ solvers that the last +solution printed at any point in time is the currently best known one). +% +With \mzninline{restart_without_objective}, the restart predicate is now +responsible for constraining the objective function. Note that a simple +hill-climbing (for minimisation) can still be defined easily in this context as: + +{ + \centering + \scriptsize + \highlightfile{assets/mzn/6_hill_climbing.mzn} +} + +It takes advantage of the fact that the declared objective function is available +through the built-in variable \mzninline{_objective}. +% +A simulated annealing strategy is also easy to +express: + +{ + \centering + \scriptsize + \highlightfile{assets/mzn/6_simulated_annealing.mzn} +} + +\section{Compilation of Neighbourhoods} \label{section:compilation} + +The neighbourhoods defined in the previous section can be executed with +\minisearch\ by adding support for the \mzninline{status} and +\mzninline{lastval} built-in functions, and by defining the main restart loop. +The \minisearch{} evaluator will then call a solver to produce a solution, and +evaluate the neighbourhood predicate, incrementally producing new \flatzinc\ to +be added to the next round of solving. +% +While this is a viable approach, our goal is to keep the compiler and solver +separate, by embedding the entire LNS specification into the \flatzinc\ that is +passed to the solver. +% +This section introduces such a compilation approach. It only requires simple +modifications of the \minizinc\ compiler, and the compiled \flatzinc\ can be +executed by standard CP solvers with a small set of simple extensions. + +\subsection{Compilation overview} + +The neighbourhood definitions from the previous section have an important +property that makes them easy to compile to standard \flatzinc: they are defined +in terms of standard \minizinc\ expressions, with the exception of a few new +built-in functions. When the neighbourhood predicates are evaluated in the +\minisearch\ way, the \minisearch\ runtime implements those built-in functions, +computing the correct value whenever a predicate is evaluated. +% +Instead, the compilation scheme presented below uses a limited form of +\emph{partial evaluation}: parameters known at compile time will be fully +evaluated; those only known during the solving, such as the result of a call to +any of the new functions (\mzninline{sol}, \mzninline{status}, etc.), are +replaced by decision variables. This essentially \textbf{turns the new built-in + functions into constraints} that have to be supported by the target solver. +The neighbourhood predicate can then be added as a constraint to the model. The +evaluation is performed by hijacking the solver's own capabilities: It will +automatically perform the evaluation of the new functions by propagating the new +constraints. + +To compile an LNS specification to standard \flatzinc, the \minizinc\ compiler +performs four simple steps: + +\begin{enumerate} + \item Replace the annotation \mzninline{::on_restart("X")} with a call to + predicate \mzninline{X}. + \item Inside predicate \mzninline{X} and any other predicate called + recursively from \mzninline{X}: treat any call to built-in functions + \mzninline{sol}, \mzninline{status}, and \mzninline{lastval} as + returning a \mzninline{var} instead of a \mzninline{par} value; and + rename calls to random functions, e.g., \mzninline{uniform} to + \mzninline{uniform_nbh}, in order to distinguish them from their + standard library versions. + \item Convert any expression containing a call from step 2 to \mzninline{var} + to ensure the functions are compiled as constraints, rather than + statically evaluated by the \minizinc\ compiler. + \item Compile the resulting model using an extension of the \minizinc\ + standard library that provides declarations for these built-in + functions, as defined below. +\end{enumerate} + +These transformations will not change the code of many neighbourhood +definitions, since the built-in functions are often used in positions that +accept both parameters and variables. For example, the +\mzninline{uniformNeighbourhood} predicate from \cref{lst:6-lns-minisearch-pred} +uses \mzninline{uniform(0.0,1.0)} in an \mzninline{if} expression, and +\mzninline{sol(x[i])} in an equality constraint. Both expressions can be +translated to \flatzinc\ when the functions return a \mzninline{var}. + +\subsection{Compiling the new built-ins} + +We can compile models that contain the new built-ins by extending the \minizinc\ +standard library as follows. + +\paragraph{\mzninline{status}} + +\Cref{lst:6-status} shows the definition of the \mzninline{status} function. It +simply replaces the functional form by a predicate \mzninline{status} (declared +in \lref{line:6:status}), which constrains its local variable argument +\mzninline{stat} to take the status value. + +\begin{listing}[t] + \highlightfile{assets/mzn/6_status.mzn} + \caption{\label{lst:6-status} MiniZinc definition of the \mzninline{status} function} +\end{listing} + + +\paragraph{\mzninline{sol} and \mzninline{lastval}} + +Since \mzninline{sol} is overloaded for different variable types and \flatzinc\ +does not support overloading, we produce type-specific built-ins for every type +of solver variable (\mzninline{int_sol(x, xi)}, \mzninline{bool_sol(x, xi)}, +etc.). The resolving of the \mzninline{sol} function into these specific +built-ins is done using an overloaded definition like the one shown +in~\Cref{lst:6-int-sol} for integer variables. If the value of the variable in +question becomes known at compile time, we use that value instead. Otherwise, we +replace the function call with a type specific \mzninline{int_sol} predicate, +which is the constraint that will be executed by the solver. +% +\begin{listing}[t] + \centering + % \begin{mzn} + % predicate int_sol(var int: x, var int: xi); + % function int: sol(var int: x) = if is_fixed(x) then fix(x) + % else let { var lb(x)..ub(x): xi; + % constraint int_sol(x,xi); + % } in xi; + % endif; + % \end{mzn} + \caption{\label{lst:6-int-sol} MiniZinc definition of the \mzninline{sol} + function for integer variables} +\end{listing} +% +To improve the compilation of the model further, we use the declared bounds of +the argument (\mzninline{lb(x)..ub(x)}) to constrain the variable returned by +\mzninline{sol}. This bounds information is important for the compiler to be +able to generate the most efficient \flatzinc\ code for expressions involving +\mzninline{sol}. The compilation of \mzninline{lastval} is similar to that for +\mzninline{sol}. + +\paragraph{Random number functions} + +Calls to the random number functions have been renamed by appending +\texttt{\_nbh}, so that the compiler does not simply evaluate them statically. +The definition of these new functions follows the same pattern as for +\mzninline{sol}, \mzninline{status}, and \mzninline{lastval}. The MiniZinc +definition of the \mzninline{uniform_nbh} function is shown in +\Cref{lst:6-int-rnd}.% +\footnote{Random number functions need to be marked as \mzninline{::impure} for + the compiler not to apply Common Subexpression Elimination + (CSE)~\autocite{stuckey-2013-functions} if they are called multiple times with + the same arguments.}% +Note that the function accepts variable arguments \mzninline{l} and +\mzninline{u}, so that it can be used in combination with other functions, such +as \mzninline{sol}. + +\begin{listing}[t] + \centering + % \begin{mzn} + % predicate float_uniform(var float:l, var float: u, var float: r); + % function var float: uniform_nbh(var float: l, var float: u) :: impure = + % let { var lb(l)..ub(u): rnd; + % constraint float_uniform(l,u,rnd): + % } in rnd; + % \end{mzn} + \caption{\label{lst:6-int-rnd} MiniZinc definition of the + \mzninline{uniform_nbh} function for floats} +\end{listing} + + +\subsection{Solver support for LNS \glsentrytext{flatzinc}} + +We will now show the minimal extensions required from a solver to interpret the +new \flatzinc\ constraints and, consequently, to execute LNS definitions +expressed in \minizinc. + +First, the solver needs to parse and support the restart annotations +of~\cref{lst:6-restart-ann}. Many solvers already support all this +functionality. Second, the solver needs to be able to parse the new constraints +\mzninline{status}, and all versions of \mzninline{sol}, \mzninline{lastval}, +and random number functions like \mzninline{float_uniform}. In addition, for the +new constraints the solver needs to: +\begin{itemize} + \item \mzninline{status(s)}: record the status of the previous restart, and + fix \mzninline{s} to the recorded status. + \item \mzninline{sol(x,sx)} (variants): constrain \mzninline{sx} to be equal + to the value of \mzninline{x} in the incumbent solution. If there is no + incumbent solution, it has no effect. + \item \mzninline{lastval(x,lx)} (variants): constrain \mzninline{lx} to take + the last value assigned to \mzninline{x} during search. If no value was + ever assigned, it has no effect. Note that many solvers (in particular + SAT and LCG solvers) already track \mzninline{lastval} for their + variables for use in search. To support LNS a solver must at least track + the \emph{lastval} of each of the variables involved in such a + constraint. This is straightforward by using the \mzninline{lastval} + propagator itself. It wakes up whenever the first argument is fixed, and + updates the last value (a non-backtrackable value). + \item random number functions: fix their variable argument to a random number + in the appropriate probability distribution. +\end{itemize} + +Importantly, these constraints need to be propagated in a way that their effects +can be undone for the next restart. Typically, this means the solver must not +propagate these constraints in the root node of the search. + +Modifying a solver to support this functionality is straightforward if it +already has a mechanism for posting constraints during restarts. We have +implemented these extensions for both Gecode (110 new lines of code) and Chuffed +(126 new lines of code). + +For example, consider the model from \cref{lst:6-basic-complete} again. +\Cref{lst:6-flat-pred} shows a part of the \flatzinc\ that arises from compiling +\mzninline{basic_lns(uniformNeighbourhood(x, 0.2))}, assuming that +\mzninline{index_set(x) = 1..n}. + +\Lrefrange{line:6:status:start}{line:6:status:end} define a Boolean variable +\mzninline{b1} that is true iff the status is not \mzninline{START}. The second +block of code (\lrefrange{line:6:x1:start}{line:6:x1:end}) represents the +decomposition of the expression + +\mzninline{(status() != START /\ uniform(0.0,1.0) > 0.2) -> x[1] = sol(x[1])} +% +which is the result of merging the implication from the \mzninline{basic_lns} +predicate with the \mzninline{if} expression from +\mzninline{uniformNeighbourhood}. The code first introduces and constrains a +variable for the random number, then adds two Boolean variables: \mzninline{b2} +is constrained to be true iff the random number is greater than 0.2; while +\mzninline{b3} is constrained to be the conjunction \mzninline{status()!=START + /\ uniform(0.0,1.0)>0.2}. \lref{line:6:x1} constrains \mzninline{x1} to be the +value of \mzninline{x[1]} in the previous solution. Finally, the half-reified +constraint in \lref{line:6:x1:end} implements \mzninline{b3 -> x[1]=sol(x[1])}. +We have omitted the similar code generated for \mzninline{x[2]} to +\mzninline{x[n]}. Note that the \flatzinc\ shown here has been simplified for +presentation. + +\begin{listing}[t] + \highlightfile{assets/mzn/6_basic_complete_transformed.mzn} + \caption{\label{lst:6-flat-pred} \flatzinc\ that results from compiling \\ + \mzninline{basic_lns(uniformNeighbourhood(x,0.2))}.} +\end{listing} + +The first time the solver is invoked, it sets \mzninline{s} to 1 +(\mzninline{START}). Propagation will fix \mzninline{b1} to \mzninline{false} +and \mzninline{b3} to \mzninline{false}. Therefore, the implication in +\lref{line:6:x1:end} is not activated, leaving \mzninline{x[1]} unconstrained. +The neighbourhood constraints are effectively switched off. + +When the solver restarts, all of the special propagators are re-executed. +Now \mzninline{s} is not 1, and \mzninline{b1} will be set to +\mzninline{true}. The \mzninline{float_random} propagator assigns +\mzninline{rnd1} a new random value and, depending on whether it is greater +than \mzninline{0.2}, the Boolean variables \mzninline{b2}, and consequently +\mzninline{b3} will be assigned. If it is \mzninline{true}, the constraint +in line \lref{line:6:x1:end} will become active and assign \mzninline{x[1]} +to its value in the previous solution. + +Furthermore, it is not strictly necessary to guard \mzninline{int_uniform} +against being invoked before \mzninline{status()!=START}, since the +\mzninline{sol} constraints will simply not propagate anything in case no +solution has been recorded yet, but we use this simple example to illustrate +how these Boolean conditions are compiled and evaluated. diff --git a/dekker_thesis.tex b/dekker_thesis.tex index f642a0b..477aa2a 100644 --- a/dekker_thesis.tex +++ b/dekker_thesis.tex @@ -9,6 +9,7 @@ DIV=calc, \input{assets/packages} \input{assets/layout} +\input{assets/shorthands} % Bibliography preamble \addbibresource{assets/bibliography/references.bib}