fix merge
This commit is contained in:
36
.gitignore
vendored
Normal file
36
.gitignore
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
Makefile.local
|
||||
|
||||
dependencies/archives/autoconf-2.69.tar.gz
|
||||
dependencies/archives/autoconf.patch
|
||||
dependencies/archives/automake-1.15.tar.gz
|
||||
dependencies/archives/bison-3.0.4.tar.gz
|
||||
dependencies/archives/flex-2.6.0.tar.bz2
|
||||
dependencies/archives/gflags-2.1.2.tar.gz
|
||||
dependencies/archives/help2man-1.43.3.tar.gz
|
||||
dependencies/archives/libtool-2.4.6.tar.gz
|
||||
dependencies/archives/mono-4.0.1.tar.bz2
|
||||
dependencies/archives/patchelf-0.8.tar.gz
|
||||
dependencies/archives/swig-3.0.7.tar.gz
|
||||
dependencies/install/
|
||||
dependencies/sources/autoconf-2.69/
|
||||
dependencies/sources/automake-1.15/
|
||||
dependencies/sources/bison-3.0.4/
|
||||
dependencies/sources/cbc-2.9.8/
|
||||
dependencies/sources/flex-2.6.0/
|
||||
dependencies/sources/gflags-2.1.2/
|
||||
dependencies/sources/google-apputils-0.4.2/
|
||||
dependencies/sources/help2man-1.43.3/
|
||||
dependencies/sources/libtool-2.4.6/
|
||||
dependencies/sources/mono-4.0.1
|
||||
dependencies/sources/pcre-8.37/
|
||||
dependencies/sources/protobuf-3.0.0-beta-2/
|
||||
dependencies/sources/sparsehash-2.0.3/
|
||||
dependencies/sources/swig-3.0.8/
|
||||
|
||||
*.a
|
||||
*.o
|
||||
*.so
|
||||
*.py[co]
|
||||
|
||||
bin/
|
||||
src/gen/
|
||||
@@ -4,7 +4,7 @@ If you do contribute, we need to make sure that we have the right to
|
||||
distribute your contribution. You'll need to fill out one of the
|
||||
following online forms.
|
||||
|
||||
Contributions from individuals:
|
||||
Contributions from individuals:
|
||||
https://cla.developers.google.com/about/google-individual
|
||||
|
||||
Contributions from corporations:
|
||||
|
||||
7
Makefile
7
Makefile
@@ -41,11 +41,8 @@ clean: clean_cc clean_java clean_python clean_csharp clean_compat
|
||||
include $(OR_ROOT)makefiles/Makefile.port
|
||||
OR_ROOT_FULL=$(OR_TOOLS_TOP)
|
||||
|
||||
# We include predefined variables
|
||||
include $(OR_ROOT)makefiles/Makefile.def
|
||||
|
||||
# Then we overwrite the local ones if the Makefile.local file exists.
|
||||
-include $(OR_ROOT)Makefile.local
|
||||
# Load local variables
|
||||
include $(OR_ROOT)Makefile.local
|
||||
|
||||
# Then include specific system commands and definitions
|
||||
include $(OR_ROOT)makefiles/Makefile.$(SYSTEM)
|
||||
|
||||
17
README
17
README
@@ -16,7 +16,8 @@ or-tools/
|
||||
examples/com/ <- Java examples
|
||||
examples/cpp/ <- C++ examples
|
||||
examples/csharp/ <- C# examples
|
||||
examples/flatzinc/ <- Flatzinc examples
|
||||
examples/data/ <- Data files for examples
|
||||
examples/flatzinc/ <- Flatzinc examples
|
||||
examples/python/ <- Python examples
|
||||
examples/tests/ <- C# unit tests and bug reports
|
||||
lib/ <- Libraries and jar files
|
||||
@@ -25,17 +26,17 @@ or-tools/
|
||||
src/ <- Source code
|
||||
src/algorithms/ <- Basic algorithms
|
||||
src/base/ <- Basic utilities
|
||||
src/bop/ <- Boolean solver based on SAT
|
||||
src/com/ <- C# and Java source files
|
||||
src/bop/ <- Boolean solver based on SAT
|
||||
src/com/ <- C# and Java source files
|
||||
src/constraint_solver/ <- Constraint solver
|
||||
src/flatzinc/ <- Flatzinc interpreter
|
||||
src/flatzinc/ <- Flatzinc interpreter
|
||||
src/gen/ <- Generated files
|
||||
src/glop/ <- Linear solver
|
||||
src/glop/ <- Linear solver
|
||||
src/graph/ <- Graph algorithms
|
||||
src/linear_solver/ <- Linear solver wrapper
|
||||
src/lp_data/ <- Data structures for linear model
|
||||
src/ortools <- Python source code
|
||||
src/sat/ <- Sat solver
|
||||
src/lp_data/ <- Data structures for linear model
|
||||
src/ortools <- Python source code
|
||||
src/sat/ <- Sat solver
|
||||
src/util/ <- Utilities needed by the constraint solver
|
||||
tools/ <- Windows binaries
|
||||
|
||||
|
||||
@@ -1,158 +0,0 @@
|
||||
/*
|
||||
Colors and images taken from the Sphinx (http://sphinx.pocoo.org/)
|
||||
"pyramid" theme.
|
||||
Sphinx is written by Georg Brandl and licensed under the BSD license.
|
||||
The "pyramid" theme comes from the Pyramid web framework project
|
||||
(http://readthedocs.org/docs/pyramid/en/latest/) and is designed by
|
||||
Blaise Laflamme.
|
||||
*/
|
||||
|
||||
body {
|
||||
font-family: Tahoma, Geneva, sans-serif;
|
||||
line-height:1.4em;
|
||||
margin:15px;
|
||||
background-color:#FFFFFF;
|
||||
}
|
||||
|
||||
.examples_list {
|
||||
padding:0 ! important;
|
||||
margin:0 ! important;
|
||||
}
|
||||
|
||||
img {border:none;}
|
||||
a { text-decoration: none;}
|
||||
a:visited { color: #2929ff;}
|
||||
a:hover { color: #0000ff;}
|
||||
|
||||
#wrap {
|
||||
margin:0 auto;
|
||||
max-width:1024px;
|
||||
min-width:480px;
|
||||
position:relative;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-size:1.8em;
|
||||
font-weight:bold;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-size:1.1em;
|
||||
font-weight:bold;
|
||||
margin:10px 0px 10px 0px;
|
||||
}
|
||||
|
||||
.empty {
|
||||
color: #D8D8D8;
|
||||
}
|
||||
|
||||
.rightmenu {
|
||||
float:right;
|
||||
margin:10px 30px 10px 30px;
|
||||
}
|
||||
.description {
|
||||
margin-top:10px;
|
||||
font-size:1.2em;
|
||||
}
|
||||
.toolbar {
|
||||
margin-top:20px;
|
||||
}
|
||||
.footer {
|
||||
font-size:.8em;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
li {
|
||||
margin:1px 0px 1px 0px;
|
||||
}
|
||||
|
||||
dl{
|
||||
width:100%;
|
||||
overflow:hidden;
|
||||
}
|
||||
dt {
|
||||
float:left;
|
||||
width:10%; /* adjust the width; make sure the total of both is 100% */
|
||||
text-decoration: underline;
|
||||
}
|
||||
dd.examples_list {
|
||||
float:left;
|
||||
width:90%; /* adjust the width; make sure the total of both is 100% */
|
||||
}
|
||||
|
||||
|
||||
|
||||
.language {
|
||||
font-variant: small-caps;
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
/* Progress bars */
|
||||
|
||||
.progress_background {
|
||||
padding:0px;
|
||||
background:#F2F1F0; /* #F5F5F5; #F1F1F1; */
|
||||
}
|
||||
|
||||
|
||||
.progress_foreground_green {
|
||||
/* background:#00FF00; */
|
||||
background: #98FB98; /* #C0EF4A; */
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.progress_foreground_red {
|
||||
/* background:#FF0000; */
|
||||
background:#FE8181; /* #FF4500; #FEEFB3;*/
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
|
||||
/* Admonitions */
|
||||
|
||||
.seealso, .warning, .note, .topic, .safe_trees {
|
||||
border: 1px solid;
|
||||
margin: 10px 0px;
|
||||
padding:15px 10px 15px 50px;
|
||||
background-repeat: no-repeat;
|
||||
background-position: 10px center;
|
||||
}
|
||||
.seealso {
|
||||
color: #4F8A10;
|
||||
background-color: #DFF2BF;
|
||||
background-image: url('dialog-seealso.png');
|
||||
}
|
||||
.warning {
|
||||
color: #D8000C;
|
||||
background-color: #FFBABA;
|
||||
background-image:url('dialog-warning.png');
|
||||
}
|
||||
.note {
|
||||
color: #9F6000;
|
||||
background-color: #FEEFB3;
|
||||
background-image: url('dialog-note.png');
|
||||
}
|
||||
.topic {
|
||||
color: #00529B;
|
||||
background-color: #BDE5F8;
|
||||
background-image: url('dialog-topic.png');
|
||||
}
|
||||
|
||||
.safe_trees {
|
||||
color: #338C1E;
|
||||
background-color: #C0EF4A; /*#A8D141;*/
|
||||
background-image: url('earth.png');
|
||||
}
|
||||
|
||||
.code_line {
|
||||
background: #E6E6E6; /* #C0EF4A; */
|
||||
background-repeat: no-repeat;
|
||||
font-family: "Courier New", Courier, monospace;
|
||||
font-stretch: condensed;
|
||||
font-size: -80%;
|
||||
text-align:center;
|
||||
}
|
||||
|
||||
.hidden { display: none; }
|
||||
.unhidden { display: block; }
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 464 B After Width: | Height: | Size: 19 KiB |
@@ -1,595 +0,0 @@
|
||||
/*
|
||||
* basic.css
|
||||
* ~~~~~~~~~
|
||||
*
|
||||
* Sphinx stylesheet -- basic theme.
|
||||
*
|
||||
* :copyright: Copyright 2007-2011 by the Sphinx team, see AUTHORS.
|
||||
* :license: BSD, see LICENSE for details.
|
||||
*
|
||||
*/
|
||||
|
||||
/* -- main layout ----------------------------------------------------------- */
|
||||
|
||||
div.clearer {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
/* -- relbar ---------------------------------------------------------------- */
|
||||
|
||||
div.related {
|
||||
width: 100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.related h3 {
|
||||
display: none;
|
||||
}
|
||||
|
||||
div.related ul {
|
||||
margin: 0;
|
||||
padding: 0 0 0 10px;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.related li {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
div.related li.right {
|
||||
float: right;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
/* -- sidebar --------------------------------------------------------------- */
|
||||
|
||||
div.sphinxsidebarwrapper {
|
||||
padding: 10px 5px 0 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar {
|
||||
float: left;
|
||||
width: 270px;
|
||||
margin-left: -100%;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul {
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul,
|
||||
div.sphinxsidebar ul.want-points {
|
||||
margin-left: 20px;
|
||||
list-style: square;
|
||||
}
|
||||
|
||||
div.sphinxsidebar ul ul {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
div.sphinxsidebar form {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input {
|
||||
border: 1px solid #98dbcc;
|
||||
font-family: sans-serif;
|
||||
font-size: 1em;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input[type="text"] {
|
||||
width: 170px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar input[type="submit"] {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
div.sphinxsidebar div.sphinxsidebarwrapper h2 {
|
||||
# font-size: 1.875em;
|
||||
# margin:25px -30px 25px;
|
||||
}
|
||||
|
||||
img {
|
||||
border: 0;
|
||||
}
|
||||
|
||||
/* -- search page ----------------------------------------------------------- */
|
||||
|
||||
ul.search {
|
||||
margin: 10px 0 0 20px;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
ul.search li {
|
||||
padding: 5px 0 5px 20px;
|
||||
background-image: url(file.png);
|
||||
background-repeat: no-repeat;
|
||||
background-position: 0 7px;
|
||||
}
|
||||
|
||||
ul.search li a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
ul.search li div.context {
|
||||
color: #888;
|
||||
margin: 2px 0 0 30px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
ul.keywordmatches li.goodmatch a {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* -- index page ------------------------------------------------------------ */
|
||||
|
||||
table.contentstable {
|
||||
width: 90%;
|
||||
}
|
||||
|
||||
table.contentstable p.biglink {
|
||||
line-height: 150%;
|
||||
}
|
||||
|
||||
a.biglink {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
span.linkdescr {
|
||||
font-style: italic;
|
||||
padding-top: 5px;
|
||||
font-size: 90%;
|
||||
}
|
||||
|
||||
/* -- general index --------------------------------------------------------- */
|
||||
|
||||
table.indextable {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
table.indextable td {
|
||||
text-align: left;
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
table.indextable dl, table.indextable dd {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
table.indextable tr.pcap {
|
||||
height: 10px;
|
||||
}
|
||||
|
||||
table.indextable tr.cap {
|
||||
margin-top: 10px;
|
||||
background-color: #f2f2f2;
|
||||
}
|
||||
|
||||
|
||||
.table {
|
||||
align: center;
|
||||
}
|
||||
|
||||
img.toggler {
|
||||
margin-right: 3px;
|
||||
margin-top: 3px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
div.modindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
div.genindex-jumpbox {
|
||||
border-top: 1px solid #ddd;
|
||||
border-bottom: 1px solid #ddd;
|
||||
margin: 1em 0 1em 0;
|
||||
padding: 0.4em;
|
||||
}
|
||||
|
||||
/* -- general body styles --------------------------------------------------- */
|
||||
|
||||
a.headerlink {
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
h1:hover > a.headerlink,
|
||||
h2:hover > a.headerlink,
|
||||
h3:hover > a.headerlink,
|
||||
h4:hover > a.headerlink,
|
||||
h5:hover > a.headerlink,
|
||||
h6:hover > a.headerlink,
|
||||
dt:hover > a.headerlink {
|
||||
visibility: visible;
|
||||
}
|
||||
|
||||
div.body p.caption {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
div.body td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.first {
|
||||
margin-top: 0 !important;
|
||||
}
|
||||
|
||||
p.rubric {
|
||||
margin-top: 30px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
img.align-left, .figure.align-left, object.align-left {
|
||||
clear: left;
|
||||
float: left;
|
||||
margin-right: 1em;
|
||||
}
|
||||
|
||||
img.align-right, .figure.align-right, object.align-right {
|
||||
clear: right;
|
||||
float: right;
|
||||
margin-left: 1em;
|
||||
}
|
||||
|
||||
img.align-center, .figure.align-center, object.align-center {
|
||||
display: block;
|
||||
margin-left: auto;
|
||||
margin-right: auto;
|
||||
}
|
||||
|
||||
.align-left {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.align-center {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.align-right {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
/* -- sidebars -------------------------------------------------------------- */
|
||||
|
||||
div.sidebar {
|
||||
margin: 0 0 0.5em 1em;
|
||||
border: 1px solid #ddb;
|
||||
padding: 7px 7px 0 7px;
|
||||
background-color: #ffe;
|
||||
width: 40%;
|
||||
float: right;
|
||||
}
|
||||
|
||||
p.sidebar-title {
|
||||
font-weight: bold;
|
||||
|
||||
}
|
||||
|
||||
/* -- files-sidebar----------------------------------------------------- */
|
||||
|
||||
/* -- files-sidebar----------------------------------------------------- */
|
||||
|
||||
div.files-sidebar {
|
||||
margin: 0 0 0.5em 1em;
|
||||
border: 1px solid #ddb;
|
||||
padding: 0px 0px 0px 7px;
|
||||
background-color: #FFFFFF;
|
||||
width: 200px;
|
||||
float: right;
|
||||
|
||||
}
|
||||
|
||||
div.files-sidebar p {
|
||||
font-family: 'Trebuchet MS', sans-serif;
|
||||
font-size: 18px;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
div.files-sidebar { color:#eee; }
|
||||
div.files-sidebar ol {
|
||||
font-size:16px;
|
||||
color: #20435C;
|
||||
padding:0;
|
||||
margin:0;
|
||||
}
|
||||
div.files-sidebar ol li {
|
||||
list-style-type: none;
|
||||
|
||||
padding:0;
|
||||
margin:0;
|
||||
}
|
||||
div.files-sidebar ol li ol { list-style-image: url("nested.png"); padding:5px 0 5px 18px; font-size:15px; text-decoration: none; }
|
||||
div.files-sidebar ol li ol li { color:#D80000; height:15px; margin-left:10px; color: #9A0303; text-decoration: none; }
|
||||
div.files-sidebar ol li ol li a { color:#D80000; height:15px; color: #9A0303; text-decoration: none;}
|
||||
div.files-sidebar ol li ol li a:hover { color: #D80000; height:15px; color: #9A0303; text-decoration: underline;}
|
||||
|
||||
|
||||
/* -- topics ---------------------------------------------------------------- */
|
||||
|
||||
div.topic {
|
||||
border: 1px solid #ccc;
|
||||
padding: 7px 7px 0 7px;
|
||||
margin: 10px 0 10px 0;
|
||||
}
|
||||
|
||||
p.topic-title {
|
||||
font-size: 1.1em;
|
||||
font-weight: bold;
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
/* -- admonitions ----------------------------------------------------------- */
|
||||
|
||||
div.admonition {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
padding: 7px;
|
||||
}
|
||||
|
||||
div.admonition dt {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.admonition dl {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
p.admonition-title {
|
||||
margin: 0px 10px 5px 0px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
div.body p.centered {
|
||||
text-align: center;
|
||||
margin-top: 25px;
|
||||
}
|
||||
|
||||
/* -- tables ---------------------------------------------------------------- */
|
||||
|
||||
table.docutils {
|
||||
border: 0;
|
||||
border-collapse: collapse;
|
||||
align: center;
|
||||
margin: auto;
|
||||
}
|
||||
|
||||
table.docutils td, table.docutils th {
|
||||
padding: 1px 8px 1px 5px;
|
||||
border-top: 0;
|
||||
border-left: 0;
|
||||
border-right: 0;
|
||||
border-bottom: 1px solid #aaa;
|
||||
}
|
||||
|
||||
table.field-list td, table.field-list th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
table.footnote td, table.footnote th {
|
||||
border: 0 !important;
|
||||
}
|
||||
|
||||
th {
|
||||
text-align: left;
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
table.citation {
|
||||
border-left: solid 1px gray;
|
||||
margin-left: 1px;
|
||||
}
|
||||
|
||||
table.citation td {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
/* -- other body styles ----------------------------------------------------- */
|
||||
|
||||
ol.arabic {
|
||||
list-style: decimal;
|
||||
}
|
||||
|
||||
ol.loweralpha {
|
||||
list-style: lower-alpha;
|
||||
}
|
||||
|
||||
ol.upperalpha {
|
||||
list-style: upper-alpha;
|
||||
}
|
||||
|
||||
ol.lowerroman {
|
||||
list-style: lower-roman;
|
||||
}
|
||||
|
||||
ol.upperroman {
|
||||
list-style: upper-roman;
|
||||
}
|
||||
|
||||
dl {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
dd p {
|
||||
margin-top: 0px;
|
||||
}
|
||||
|
||||
dd ul, dd table {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
dd {
|
||||
margin-top: 3px;
|
||||
margin-bottom: 10px;
|
||||
margin-left: 30px;
|
||||
}
|
||||
|
||||
dt:target, .highlighted {
|
||||
background-color: #fbe54e;
|
||||
}
|
||||
|
||||
dl.glossary dt {
|
||||
font-weight: bold;
|
||||
font-size: 1.1em;
|
||||
}
|
||||
|
||||
.field-list ul {
|
||||
margin: 0;
|
||||
padding-left: 1em;
|
||||
}
|
||||
|
||||
.field-list p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.refcount {
|
||||
color: #060;
|
||||
}
|
||||
|
||||
.optional {
|
||||
font-size: 1.3em;
|
||||
}
|
||||
|
||||
.versionmodified {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.system-message {
|
||||
background-color: #fda;
|
||||
padding: 5px;
|
||||
border: 3px solid red;
|
||||
}
|
||||
|
||||
.footnote:target {
|
||||
background-color: #ffa;
|
||||
}
|
||||
|
||||
.line-block {
|
||||
display: block;
|
||||
margin-top: 1em;
|
||||
margin-bottom: 1em;
|
||||
}
|
||||
|
||||
.line-block .line-block {
|
||||
margin-top: 0;
|
||||
margin-bottom: 0;
|
||||
margin-left: 1.5em;
|
||||
}
|
||||
|
||||
.guilabel, .menuselection {
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
.accelerator {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.classifier {
|
||||
font-style: oblique;
|
||||
}
|
||||
|
||||
abbr, acronym {
|
||||
border-bottom: dotted 1px;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
/* -- code displays --------------------------------------------------------- */
|
||||
|
||||
pre {
|
||||
overflow: auto;
|
||||
overflow-y: hidden; /* fixes display issues on Chrome browsers */
|
||||
}
|
||||
|
||||
td.linenos pre {
|
||||
padding: 5px 0px;
|
||||
border: 0;
|
||||
background-color: transparent;
|
||||
color: #aaa;
|
||||
}
|
||||
|
||||
table.highlighttable {
|
||||
margin-left: 0.5em;
|
||||
}
|
||||
|
||||
table.highlighttable td {
|
||||
padding: 0 0.5em 0 0.5em;
|
||||
}
|
||||
|
||||
tt.descname {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
font-size: 1.2em;
|
||||
}
|
||||
|
||||
tt.descclassname {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
tt.xref, a tt {
|
||||
background-color: transparent;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
h1 tt, h2 tt, h3 tt, h4 tt, h5 tt, h6 tt {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
.viewcode-link {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.viewcode-back {
|
||||
float: right;
|
||||
font-family: sans-serif;
|
||||
}
|
||||
|
||||
div.viewcode-block:target {
|
||||
margin: -1px -10px;
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
/* -- math display ---------------------------------------------------------- */
|
||||
|
||||
img.math {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
div.body div.math p {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
span.eqno {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* -- printout stylesheet --------------------------------------------------- */
|
||||
|
||||
@media print {
|
||||
div.document,
|
||||
div.documentwrapper,
|
||||
div.bodywrapper {
|
||||
margin: 0 !important;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
div.sphinxsidebar,
|
||||
div.related,
|
||||
div.footer,
|
||||
#top-link {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -173,7 +173,7 @@ in the previous section.</p>
|
||||
<p>The assignment possibilities define the <em>search space</em><a class="footnote-reference" href="#search-space-details" id="id7">[8]</a>. In our 4-queens example, the search space is defined
|
||||
by all possible assignments for the 16 variables <img class="math" src="../../_images/math/9ff77ee087b4287c6e82e414efbe4d79aba8c012.png" alt="x_{ij}" style="vertical-align: -6px"/>. For each of them, we have 2 possibilities:
|
||||
<img class="math" src="../../_images/math/bc1f9d9bf8a1b606a4188b5ce9a2af1809e27a89.png" alt="0" style="vertical-align: 0px"/> or <img class="math" src="../../_images/math/dce34f4dfb2406144304ad0d6106c5382ddd1446.png" alt="1" style="vertical-align: -1px"/>. Thus in total,
|
||||
we have <img class="math" src="../../_images/math/6cd5d6515772b39bbb40cae461898f6d7762d6b8.png" alt="16^2 = 256" style="vertical-align: -1px"/> possibilities. This is the <em>size</em> of the search space. It’s important to understand that the search space
|
||||
we have <img class="math" src="../../_images/math/6cd5d6515772b39bbb40cae461898f6d7762d6b8.png" alt="2^16 = 65536" style="vertical-align: -1px"/> possibilities. This is the <em>size</em> of the search space. It’s important to understand that the search space
|
||||
is defined by the variables and their domain (i.e. the model) and not by the problem itself<a class="footnote-reference" href="#four-queens-reduced-search-space" id="id8">[5]</a>.
|
||||
Actually, it is also defined by the constraints you added to the model because those constraints reduce the possibilities and thus
|
||||
the search space<a class="footnote-reference" href="#determining-search-space-size" id="id9">[6]</a>.</p>
|
||||
|
||||
@@ -183,7 +183,7 @@ encapsulated in an <tt class="docutils literal"><span class="pre">IntExpr</span>
|
||||
<div class="highlight-c++"><div class="highlight"><pre><span class="n">IntExpr</span><span class="o">*</span> <span class="nf">PerformedExpr</span><span class="p">();</span>
|
||||
</pre></div>
|
||||
</div>
|
||||
<p>The corresponding <tt class="docutils literal"><span class="pre">IntExpr</span></tt> acts like a <img class="math" src="../../_images/math/016f10daec11b69c18d2bf68896ecd98339fe426.png" alt="0-1" style="vertical-align: -1px"/> <tt class="docutils literal"><span class="pre">IntervalVar</span></tt><a class="footnote-reference" href="#performed-intexpr-is-intervalvar" id="id3">[2]</a>.
|
||||
<p>The corresponding <tt class="docutils literal"><span class="pre">IntExpr</span></tt> acts like a <img class="math" src="../../_images/math/016f10daec11b69c18d2bf68896ecd98339fe426.png" alt="0-1" style="vertical-align: -1px"/> <tt class="docutils literal"><span class="pre">IntVar</span></tt><a class="footnote-reference" href="#performed-intexpr-is-intvar" id="id3">[2]</a>.
|
||||
If its minimum value is <img class="math" src="../../_images/math/dce34f4dfb2406144304ad0d6106c5382ddd1446.png" alt="1" style="vertical-align: -1px"/>, the corresponding <tt class="docutils literal"><span class="pre">IntervalVar</span></tt> variables must be performed. If its
|
||||
maximal value is <img class="math" src="../../_images/math/bc1f9d9bf8a1b606a4188b5ce9a2af1809e27a89.png" alt="0" style="vertical-align: 0px"/>, the corresponding <tt class="docutils literal"><span class="pre">IntervalVar</span></tt> is unperformed and if <img class="math" src="../../_images/math/ce4b0697810792acb7da44ab5ef5c62c45917f4f.png" alt="\text{min} = 0" style="vertical-align: -1px"/>
|
||||
and <img class="math" src="../../_images/math/c3c8f330093ec775717da39692935b7d17af3d62.png" alt="\text{max} = 1" style="vertical-align: -1px"/>, the corresponding <tt class="docutils literal"><span class="pre">IntervalVar</span></tt> might be performed.</p>
|
||||
@@ -698,10 +698,10 @@ Machine_2: Job 1 (2,3) Job 2 (7,10) Job 0 (10,12)
|
||||
thus doesn’t necessarily correspond to the figure. Read on.</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="docutils footnote" frame="void" id="performed-intexpr-is-intervalvar" rules="none">
|
||||
<table class="docutils footnote" frame="void" id="performed-intexpr-is-intvar" rules="none">
|
||||
<colgroup><col class="label" /><col /></colgroup>
|
||||
<tbody valign="top">
|
||||
<tr><td class="label"><a class="fn-backref" href="#id3">[2]</a></td><td>Actually, it is an <tt class="docutils literal"><span class="pre">IntervalVar</span></tt>!</td></tr>
|
||||
<tr><td class="label"><a class="fn-backref" href="#id3">[2]</a></td><td>Actually, it is an <tt class="docutils literal"><span class="pre">IntVar</span></tt>!</td></tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table class="docutils footnote" frame="void" id="sequencevar-virtually-conceptualized" rules="none">
|
||||
@@ -873,4 +873,4 @@ Search:
|
||||
Created using <a href="http://sphinx.pocoo.org/">Sphinx</a> 1.1.3.
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
</html>
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.google.ortools.constraintsolver.Assignment;
|
||||
import com.google.ortools.constraintsolver.IntVar;
|
||||
import com.google.ortools.constraintsolver.NodeEvaluator2;
|
||||
import com.google.ortools.constraintsolver.RoutingModel;
|
||||
import com.google.ortools.constraintsolver.RoutingEnums.FirstSolutionStrategy;
|
||||
import com.google.ortools.constraintsolver.RoutingSearchParameters;
|
||||
|
||||
import java.util.ArrayList;
|
||||
@@ -229,13 +230,14 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows {
|
||||
}
|
||||
|
||||
// Solving
|
||||
RoutingSearchParameters parameters = new RoutingSearchParameters();
|
||||
parameters.setNo_lns(true);
|
||||
parameters.setFirst_solution("AllUnperformed");
|
||||
parameters.setTrace(true);
|
||||
RoutingSearchParameters parameters =
|
||||
RoutingSearchParameters.newBuilder()
|
||||
.mergeFrom(RoutingModel.defaultSearchParameters())
|
||||
.setFirstSolutionStrategy(FirstSolutionStrategy.Value.ALL_UNPERFORMED)
|
||||
.build();
|
||||
|
||||
logger.info("Search");
|
||||
Assignment solution = model.solveWithParameters(parameters, null);
|
||||
Assignment solution = model.solveWithParameters(parameters);
|
||||
|
||||
if (solution != null) {
|
||||
String output = "Total cost: " + solution.objectiveValue() + "\n";
|
||||
|
||||
@@ -27,17 +27,18 @@ import com.google.ortools.linearsolver.MPVariable;
|
||||
public class IntegerProgramming {
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
private static MPSolver createSolver (String solverType)
|
||||
throws java.lang.IllegalArgumentException {
|
||||
return new MPSolver("IntegerProgrammingExample",
|
||||
MPSolver.OptimizationProblemType.valueOf(solverType));
|
||||
private static MPSolver createSolver (String solverType) {
|
||||
try {
|
||||
return new MPSolver("IntegerProgrammingExample",
|
||||
MPSolver.OptimizationProblemType.valueOf(solverType));
|
||||
} catch (java.lang.IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void runIntegerProgrammingExample(String solverType) {
|
||||
MPSolver solver = null;
|
||||
try {
|
||||
solver = createSolver(solverType);
|
||||
} catch (java.lang.IllegalArgumentException e) {
|
||||
MPSolver solver = createSolver(solverType);
|
||||
if (solver == null) {
|
||||
System.out.println("Could not create solver " + solverType);
|
||||
return;
|
||||
}
|
||||
|
||||
41
examples/com/google/ortools/samples/Issue173.java
Normal file
41
examples/com/google/ortools/samples/Issue173.java
Normal file
@@ -0,0 +1,41 @@
|
||||
package com.google.ortools.samples;
|
||||
|
||||
import com.google.ortools.linearsolver.MPConstraint;
|
||||
import com.google.ortools.linearsolver.MPObjective;
|
||||
import com.google.ortools.linearsolver.MPSolver;
|
||||
import com.google.ortools.linearsolver.MPVariable;
|
||||
|
||||
public class Issue173 {
|
||||
|
||||
static {
|
||||
System.loadLibrary("jniortools");
|
||||
}
|
||||
|
||||
public static void breakit() {
|
||||
|
||||
for (int i = 0; i < 50000; i++) {
|
||||
solveLP();
|
||||
}
|
||||
}
|
||||
|
||||
private static void solveLP() {
|
||||
MPSolver solver = new MPSolver(
|
||||
"test",
|
||||
MPSolver.OptimizationProblemType.CBC_MIXED_INTEGER_PROGRAMMING);
|
||||
MPVariable x = solver.makeNumVar(Double.NEGATIVE_INFINITY,
|
||||
Double.POSITIVE_INFINITY, "x");
|
||||
|
||||
final MPObjective objective = solver.objective();
|
||||
objective.setMaximization();
|
||||
objective.setCoefficient(x, 1);
|
||||
|
||||
MPConstraint constraint = solver.makeConstraint(0, 5);
|
||||
constraint.setCoefficient(x, 1);
|
||||
|
||||
solver.solve();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
breakit();
|
||||
}
|
||||
}
|
||||
@@ -27,18 +27,19 @@ import com.google.ortools.linearsolver.MPVariable;
|
||||
public class LinearProgramming {
|
||||
static { System.loadLibrary("jniortools"); }
|
||||
|
||||
private static MPSolver createSolver (String solverType)
|
||||
throws java.lang.IllegalArgumentException {
|
||||
return new MPSolver("LinearProgrammingExample",
|
||||
MPSolver.OptimizationProblemType.valueOf(solverType));
|
||||
private static MPSolver createSolver (String solverType) {
|
||||
try {
|
||||
return new MPSolver("LinearProgrammingExample",
|
||||
MPSolver.OptimizationProblemType.valueOf(solverType));
|
||||
} catch (java.lang.IllegalArgumentException e) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static void runLinearProgrammingExample(String solverType,
|
||||
boolean printModel) {
|
||||
MPSolver solver = null;
|
||||
try {
|
||||
solver = createSolver(solverType);
|
||||
} catch (java.lang.IllegalArgumentException e) {
|
||||
MPSolver solver = createSolver(solverType);
|
||||
if (solver == null) {
|
||||
System.out.println("Could not create solver " + solverType);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@
|
||||
|
||||
package com.google.ortools.samples;
|
||||
|
||||
import com.google.ortools.constraintsolver.ConstraintSolverParameters;
|
||||
import com.google.ortools.constraintsolver.DecisionBuilder;
|
||||
import com.google.ortools.constraintsolver.IntVar;
|
||||
import com.google.ortools.constraintsolver.Solver;
|
||||
@@ -24,10 +25,8 @@ import java.util.logging.Logger;
|
||||
* Sample showing how to model using the constraint programming solver.
|
||||
*
|
||||
*/
|
||||
|
||||
public class RabbitsPheasants {
|
||||
private static Logger logger =
|
||||
Logger.getLogger(RabbitsPheasants.class.getName());
|
||||
private static Logger logger = Logger.getLogger(RabbitsPheasants.class.getName());
|
||||
|
||||
static {
|
||||
System.loadLibrary("jniortools");
|
||||
@@ -39,18 +38,21 @@ public class RabbitsPheasants {
|
||||
* and 56 legs. How many rabbits and how many pheasants are we thus
|
||||
* seeing?
|
||||
*/
|
||||
private static void solve() {
|
||||
Solver solver = new Solver("RabbitsPheasants");
|
||||
private static void solve(boolean traceSearch) {
|
||||
ConstraintSolverParameters parameters =
|
||||
ConstraintSolverParameters.newBuilder()
|
||||
.mergeFrom(Solver.defaultSolverParameters())
|
||||
.setTraceSearch(traceSearch)
|
||||
.build();
|
||||
Solver solver = new Solver("RabbitsPheasants", parameters);
|
||||
IntVar rabbits = solver.makeIntVar(0, 100, "rabbits");
|
||||
IntVar pheasants = solver.makeIntVar(0, 100, "pheasants");
|
||||
solver.addConstraint(solver.makeEquality(solver.makeSum(rabbits, pheasants),
|
||||
20));
|
||||
solver.addConstraint(solver.makeEquality(solver.makeSum(
|
||||
solver.makeProd(rabbits, 4),
|
||||
solver.makeProd(pheasants, 2)), 56));
|
||||
DecisionBuilder db = solver.makePhase(rabbits, pheasants,
|
||||
Solver.CHOOSE_FIRST_UNBOUND,
|
||||
Solver.ASSIGN_MIN_VALUE);
|
||||
solver.addConstraint(solver.makeEquality(solver.makeSum(rabbits, pheasants), 20));
|
||||
solver.addConstraint(
|
||||
solver.makeEquality(
|
||||
solver.makeSum(solver.makeProd(rabbits, 4), solver.makeProd(pheasants, 2)), 56));
|
||||
DecisionBuilder db =
|
||||
solver.makePhase(rabbits, pheasants, Solver.CHOOSE_FIRST_UNBOUND, Solver.ASSIGN_MIN_VALUE);
|
||||
solver.newSearch(db);
|
||||
solver.nextSolution();
|
||||
logger.info(rabbits.toString());
|
||||
@@ -59,6 +61,7 @@ public class RabbitsPheasants {
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
RabbitsPheasants.solve();
|
||||
boolean traceSearch = args.length > 0 && args[1].equals("--trace");
|
||||
RabbitsPheasants.solve(traceSearch);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,8 @@ import java.text.*;
|
||||
import com.google.ortools.constraintsolver.Assignment;
|
||||
import com.google.ortools.constraintsolver.NodeEvaluator2;
|
||||
import com.google.ortools.constraintsolver.RoutingModel;
|
||||
import com.google.ortools.constraintsolver.FirstSolutionStrategy;
|
||||
import com.google.ortools.constraintsolver.RoutingSearchParameters;
|
||||
|
||||
class Tsp {
|
||||
|
||||
@@ -61,8 +63,6 @@ class Tsp {
|
||||
static void solve(int size, int forbidden, int seed)
|
||||
{
|
||||
RoutingModel routing = new RoutingModel(size, 1);
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
routing.setFirstSolutionStrategy(RoutingModel.ROUTING_PATH_CHEAPEST_ARC);
|
||||
|
||||
// Setting the cost function.
|
||||
// Put a permanent callback to the distance accessor here. The callback
|
||||
@@ -93,7 +93,13 @@ class Tsp {
|
||||
"dummy");
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
Assignment solution = routing.solve();
|
||||
RoutingSearchParameters search_parameters =
|
||||
RoutingSearchParameters.newBuilder()
|
||||
.mergeFrom(RoutingModel.defaultSearchParameters())
|
||||
.setFirstSolutionStrategy(FirstSolutionStrategy.Value.PATH_CHEAPEST_ARC)
|
||||
.build();
|
||||
|
||||
Assignment solution = routing.solveWithParameters(search_parameters);
|
||||
if (solution != null) {
|
||||
// Solution cost.
|
||||
System.out.println("Cost = " + solution.objectiveValue());
|
||||
|
||||
@@ -371,11 +371,7 @@ void CostasSoft(const int dim) {
|
||||
|
||||
// Computes a Costas Array.
|
||||
void CostasHard(const int dim) {
|
||||
SolverParameters parameters;
|
||||
if (!FLAGS_export_profile.empty()) {
|
||||
parameters.profile_level = SolverParameters::NORMAL_PROFILING;
|
||||
}
|
||||
Solver solver("costas", parameters);
|
||||
Solver solver("costas");
|
||||
|
||||
// create the variables
|
||||
std::vector<IntVar*> vars;
|
||||
|
||||
@@ -21,8 +21,6 @@
|
||||
// distances are computed using the Manhattan distance. Distances are assumed
|
||||
// to be in meters and times in seconds.
|
||||
|
||||
#include <memory>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback.h"
|
||||
@@ -30,22 +28,19 @@
|
||||
#include "base/commandlineflags.h"
|
||||
#include "base/integral_types.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/stringprintf.h"
|
||||
#include "constraint_solver/routing.h"
|
||||
#include "base/random.h"
|
||||
#include "constraint_solver/routing_flags.h"
|
||||
#include "cpp/cvrptw_lib.h"
|
||||
|
||||
using operations_research::Assignment;
|
||||
using operations_research::IntVar;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::Solver;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::StringAppendF;
|
||||
using operations_research::StringPrintf;
|
||||
|
||||
|
||||
DECLARE_string(routing_first_solution);
|
||||
DECLARE_bool(routing_no_lns);
|
||||
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
@@ -55,183 +50,9 @@ DEFINE_bool(vrp_use_same_vehicle_costs, false,
|
||||
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
const int kMaxNodesPerGroup = 10;
|
||||
const int64 kMaxNodesPerGroup = 10;
|
||||
const int64 kSameVehicleCost = 1000;
|
||||
|
||||
// Random seed generator.
|
||||
int32 GetSeed() {
|
||||
if (FLAGS_vrp_use_deterministic_random_seed) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
}
|
||||
}
|
||||
|
||||
// Location container, contains positions of orders and can be used to obtain
|
||||
// Manhattan distances/times between locations.
|
||||
class LocationContainer {
|
||||
public:
|
||||
explicit LocationContainer(int64 speed)
|
||||
: randomizer_(GetSeed()), speed_(speed) {
|
||||
CHECK_LT(0, speed_);
|
||||
}
|
||||
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
|
||||
void AddRandomLocation(int64 x_max, int64 y_max) {
|
||||
AddLocation(randomizer_.Uniform(x_max + 1), randomizer_.Uniform(y_max + 1));
|
||||
}
|
||||
int64 ManhattanDistance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return locations_[from].DistanceTo(locations_[to]);
|
||||
}
|
||||
int64 ManhattanTime(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return ManhattanDistance(from, to) / speed_;
|
||||
}
|
||||
|
||||
private:
|
||||
class Location {
|
||||
public:
|
||||
Location() : x_(0), y_(0) {}
|
||||
Location(int64 x, int64 y) : x_(x), y_(y) {}
|
||||
int64 DistanceTo(const Location& location) const {
|
||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
||||
}
|
||||
|
||||
private:
|
||||
static int64 Abs(int64 value) { return std::max(value, -value); }
|
||||
|
||||
int64 x_;
|
||||
int64 y_;
|
||||
};
|
||||
|
||||
ACMRandom randomizer_;
|
||||
const int64 speed_;
|
||||
ITIVector<RoutingModel::NodeIndex, Location> locations_;
|
||||
};
|
||||
|
||||
// Random demand.
|
||||
class RandomDemand {
|
||||
public:
|
||||
RandomDemand(int size, RoutingModel::NodeIndex depot)
|
||||
: size_(size), depot_(depot) {
|
||||
CHECK_LT(0, size_);
|
||||
}
|
||||
void Initialize() {
|
||||
const int64 kDemandMax = 5;
|
||||
const int64 kDemandMin = 1;
|
||||
demand_.reset(new int64[size_]);
|
||||
ACMRandom randomizer(GetSeed());
|
||||
for (int order = 0; order < size_; ++order) {
|
||||
if (order == depot_) {
|
||||
demand_[order] = 0;
|
||||
} else {
|
||||
demand_[order] =
|
||||
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
|
||||
return demand_[from.value()];
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<int64[]> demand_;
|
||||
const int size_;
|
||||
const RoutingModel::NodeIndex depot_;
|
||||
};
|
||||
|
||||
// Service time (proportional to demand) + transition time callback.
|
||||
class ServiceTimePlusTransition {
|
||||
public:
|
||||
ServiceTimePlusTransition(int64 time_per_demand_unit,
|
||||
RoutingModel::NodeEvaluator2* demand,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: time_per_demand_unit_(time_per_demand_unit),
|
||||
demand_(demand),
|
||||
transition_time_(transition_time) {}
|
||||
int64 Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return time_per_demand_unit_ * demand_->Run(from, to) +
|
||||
transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
private:
|
||||
const int64 time_per_demand_unit_;
|
||||
std::unique_ptr<RoutingModel::NodeEvaluator2> demand_;
|
||||
std::unique_ptr<RoutingModel::NodeEvaluator2> transition_time_;
|
||||
};
|
||||
|
||||
// Route plan displayer.
|
||||
// TODO(user): Move the display code to the routing library.
|
||||
void DisplayPlan(const RoutingModel& routing, const Assignment& plan) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
StringAppendF(&dropped, " %d", order);
|
||||
} else {
|
||||
StringAppendF(&dropped, ", %d", order);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dropped.empty()) {
|
||||
plan_output += "Dropped orders:" + dropped + "\n";
|
||||
}
|
||||
|
||||
if (FLAGS_vrp_use_same_vehicle_costs) {
|
||||
int group_size = 0;
|
||||
int64 same_vehicle_cost = 0;
|
||||
std::set<int> visited;
|
||||
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
++group_size;
|
||||
visited.insert(
|
||||
plan.Value(routing.VehicleVar(routing.NodeToIndex(order))));
|
||||
if (group_size == kMaxNodesPerGroup) {
|
||||
if (visited.size() > 1) {
|
||||
same_vehicle_cost += (visited.size() - 1) * kSameVehicleCost;
|
||||
}
|
||||
group_size = 0;
|
||||
visited.clear();
|
||||
}
|
||||
}
|
||||
if (visited.size() > 1) {
|
||||
same_vehicle_cost += (visited.size() - 1) * kSameVehicleCost;
|
||||
}
|
||||
LOG(INFO) << "Same vehicle costs: " << same_vehicle_cost;
|
||||
}
|
||||
|
||||
// Display actual output for each vehicle.
|
||||
const RoutingDimension& capacity_dimension =
|
||||
routing.GetDimensionOrDie(kCapacity);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
for (int route_number = 0; route_number < routing.vehicles();
|
||||
++route_number) {
|
||||
int64 order = routing.Start(route_number);
|
||||
StringAppendF(&plan_output, "Route %d: ", route_number);
|
||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
||||
plan_output += "Empty\n";
|
||||
} else {
|
||||
while (true) {
|
||||
IntVar* const load_var = capacity_dimension.CumulVar(order);
|
||||
IntVar* const time_var = time_dimension.CumulVar(order);
|
||||
StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld) -> ",
|
||||
order, plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var));
|
||||
if (routing.IsEnd(order)) break;
|
||||
order = plan.Value(routing.NextVar(order));
|
||||
}
|
||||
plan_output += "\n";
|
||||
}
|
||||
}
|
||||
LOG(INFO) << plan_output;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags( &argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
|
||||
@@ -242,16 +63,17 @@ int main(int argc, char** argv) {
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles);
|
||||
routing.SetDepot(kDepot);
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
FLAGS_routing_first_solution = "PathCheapestArc";
|
||||
// Disabling Large Neighborhood Search, comment out to activate it.
|
||||
FLAGS_routing_no_lns = true;
|
||||
RoutingSearchParameters parameters =
|
||||
operations_research::BuildSearchParametersFromFlags();
|
||||
parameters.set_first_solution_strategy(
|
||||
operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
parameters.mutable_local_search_operators()->set_use_path_lns(false);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed);
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
|
||||
locations.AddRandomLocation(kXMax, kYMax);
|
||||
}
|
||||
@@ -263,7 +85,8 @@ int main(int argc, char** argv) {
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot);
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
@@ -278,9 +101,11 @@ int main(int argc, char** argv) {
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
const operations_research::RoutingDimension& time_dimension =
|
||||
routing.GetDimensionOrDie(kTime);
|
||||
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed());
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
@@ -313,9 +138,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const Assignment* solution = routing.Solve();
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
if (solution != NULL) {
|
||||
DisplayPlan(routing, *solution);
|
||||
DisplayPlan(routing, *solution, FLAGS_vrp_use_same_vehicle_costs,
|
||||
kMaxNodesPerGroup, kSameVehicleCost,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
} else {
|
||||
LOG(INFO) << "No solution found.";
|
||||
}
|
||||
|
||||
226
examples/cpp/cvrptw_lib.cc
Normal file
226
examples/cpp/cvrptw_lib.cc
Normal file
@@ -0,0 +1,226 @@
|
||||
// Copyright 2010-2014 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
#include "cpp/cvrptw_lib.h"
|
||||
|
||||
#include <set>
|
||||
|
||||
#include "base/logging.h"
|
||||
#include "base/stringprintf.h"
|
||||
#include "constraint_solver/routing.h"
|
||||
#include "base/random.h"
|
||||
#include "base/random.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
int32 GetSeed(bool deterministic) {
|
||||
if (deterministic) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
}
|
||||
}
|
||||
|
||||
LocationContainer::LocationContainer(int64 speed, bool use_deterministic_seed)
|
||||
: randomizer_(GetSeed(use_deterministic_seed)), speed_(speed) {
|
||||
CHECK_LT(0, speed_);
|
||||
}
|
||||
|
||||
void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max) {
|
||||
AddRandomLocation(x_max, y_max, 1);
|
||||
}
|
||||
|
||||
void LocationContainer::AddRandomLocation(int64 x_max, int64 y_max,
|
||||
int duplicates) {
|
||||
const int64 x = randomizer_.Uniform(x_max + 1);
|
||||
const int64 y = randomizer_.Uniform(y_max + 1);
|
||||
for (int i = 0; i < duplicates; ++i) {
|
||||
AddLocation(x, y);
|
||||
}
|
||||
}
|
||||
|
||||
int64 LocationContainer::ManhattanDistance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return locations_[from].DistanceTo(locations_[to]);
|
||||
}
|
||||
|
||||
int64 LocationContainer::NegManhattanDistance(
|
||||
RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
|
||||
return -ManhattanDistance(from, to);
|
||||
}
|
||||
|
||||
int64 LocationContainer::ManhattanTime(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return ManhattanDistance(from, to) / speed_;
|
||||
}
|
||||
|
||||
bool LocationContainer::SameLocation(RoutingModel::NodeIndex node1,
|
||||
RoutingModel::NodeIndex node2) const {
|
||||
if (node1 < locations_.size() && node2 < locations_.size()) {
|
||||
return locations_[node1].IsAtSameLocation(locations_[node2]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int64 LocationContainer::SameLocationFromIndex(int64 node1, int64 node2) const {
|
||||
// The direct conversion from constraint model indices to routing model
|
||||
// nodes is correct because the depot is node 0.
|
||||
// TODO(user): Fetch proper indices from routing model.
|
||||
return SameLocation(RoutingModel::NodeIndex(node1),
|
||||
RoutingModel::NodeIndex(node2));
|
||||
}
|
||||
|
||||
LocationContainer::Location::Location() : x_(0), y_(0) {}
|
||||
|
||||
LocationContainer::Location::Location(int64 x, int64 y) : x_(x), y_(y) {}
|
||||
|
||||
int64 LocationContainer::Location::DistanceTo(const Location& location) const {
|
||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
||||
}
|
||||
|
||||
bool LocationContainer::Location::IsAtSameLocation(
|
||||
const Location& location) const {
|
||||
return x_ == location.x_ && y_ == location.y_;
|
||||
}
|
||||
|
||||
int64 LocationContainer::Location::Abs(int64 value) {
|
||||
return std::max(value, -value);
|
||||
}
|
||||
|
||||
RandomDemand::RandomDemand(int size, RoutingModel::NodeIndex depot,
|
||||
bool use_deterministic_seed)
|
||||
: size_(size),
|
||||
depot_(depot),
|
||||
use_deterministic_seed_(use_deterministic_seed) {
|
||||
CHECK_LT(0, size_);
|
||||
}
|
||||
|
||||
void RandomDemand::Initialize() {
|
||||
const int64 kDemandMax = 5;
|
||||
const int64 kDemandMin = 1;
|
||||
demand_.reset(new int64[size_]);
|
||||
MTRandom randomizer(GetSeed(use_deterministic_seed_));
|
||||
for (int order = 0; order < size_; ++order) {
|
||||
if (order == depot_) {
|
||||
demand_[order] = 0;
|
||||
} else {
|
||||
demand_[order] =
|
||||
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int64 RandomDemand::Demand(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex /*to*/) const {
|
||||
return demand_[from.value()];
|
||||
}
|
||||
|
||||
ServiceTimePlusTransition::ServiceTimePlusTransition(
|
||||
int64 time_per_demand_unit, RoutingModel::NodeEvaluator2* demand,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: time_per_demand_unit_(time_per_demand_unit),
|
||||
demand_(demand),
|
||||
transition_time_(transition_time) {}
|
||||
|
||||
int64 ServiceTimePlusTransition::Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return time_per_demand_unit_ * demand_->Run(from, to) +
|
||||
transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
StopServiceTimePlusTransition::StopServiceTimePlusTransition(
|
||||
int64 stop_time, const LocationContainer& location_container,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: stop_time_(stop_time),
|
||||
location_container_(location_container),
|
||||
transition_time_(transition_time) {}
|
||||
|
||||
int64 StopServiceTimePlusTransition::Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return location_container_.SameLocation(from, to)
|
||||
? 0
|
||||
: stop_time_ + transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
void DisplayPlan(
|
||||
const RoutingModel& routing, const operations_research::Assignment& plan,
|
||||
bool use_same_vehicle_costs, int64 max_nodes_per_group,
|
||||
int64 same_vehicle_cost,
|
||||
const operations_research::RoutingDimension& capacity_dimension,
|
||||
const operations_research::RoutingDimension& time_dimension) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
StringAppendF(&dropped, " %d", order);
|
||||
} else {
|
||||
StringAppendF(&dropped, ", %d", order);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dropped.empty()) {
|
||||
plan_output += "Dropped orders:" + dropped + "\n";
|
||||
}
|
||||
|
||||
if (use_same_vehicle_costs) {
|
||||
int group_size = 0;
|
||||
int64 group_same_vehicle_cost = 0;
|
||||
std::set<int> visited;
|
||||
const RoutingModel::NodeIndex kFirstNodeAfterDepot(1);
|
||||
for (RoutingModel::NodeIndex order = kFirstNodeAfterDepot;
|
||||
order < routing.nodes(); ++order) {
|
||||
++group_size;
|
||||
visited.insert(
|
||||
plan.Value(routing.VehicleVar(routing.NodeToIndex(order))));
|
||||
if (group_size == max_nodes_per_group) {
|
||||
if (visited.size() > 1) {
|
||||
group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost;
|
||||
}
|
||||
group_size = 0;
|
||||
visited.clear();
|
||||
}
|
||||
}
|
||||
if (visited.size() > 1) {
|
||||
group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost;
|
||||
}
|
||||
LOG(INFO) << "Same vehicle costs: " << group_same_vehicle_cost;
|
||||
}
|
||||
|
||||
// Display actual output for each vehicle.
|
||||
for (int route_number = 0; route_number < routing.vehicles();
|
||||
++route_number) {
|
||||
int64 order = routing.Start(route_number);
|
||||
StringAppendF(&plan_output, "Route %d: ", route_number);
|
||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
||||
plan_output += "Empty\n";
|
||||
} else {
|
||||
while (true) {
|
||||
operations_research::IntVar* const load_var =
|
||||
capacity_dimension.CumulVar(order);
|
||||
operations_research::IntVar* const time_var =
|
||||
time_dimension.CumulVar(order);
|
||||
StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld) -> ",
|
||||
order, plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var));
|
||||
if (routing.IsEnd(order)) break;
|
||||
order = plan.Value(routing.NextVar(order));
|
||||
}
|
||||
plan_output += "\n";
|
||||
}
|
||||
}
|
||||
LOG(INFO) << plan_output;
|
||||
}
|
||||
} // namespace operations_research
|
||||
131
examples/cpp/cvrptw_lib.h
Normal file
131
examples/cpp/cvrptw_lib.h
Normal file
@@ -0,0 +1,131 @@
|
||||
// Copyright 2010-2014 Google
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// This header provides functions to help creating random instaces of the
|
||||
// vehicle routing problem; random capacities and random time windows.
|
||||
#ifndef OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
||||
#define OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "constraint_solver/routing.h"
|
||||
#include "base/random.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
// Random seed generator.
|
||||
int32 GetSeed(bool deterministic);
|
||||
|
||||
// Location container, contains positions of orders and can be used to obtain
|
||||
// Manhattan distances/times between locations.
|
||||
class LocationContainer {
|
||||
public:
|
||||
LocationContainer(int64 speed, bool use_deterministic_seed);
|
||||
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
|
||||
void AddRandomLocation(int64 x_max, int64 y_max);
|
||||
void AddRandomLocation(int64 x_max, int64 y_max, int duplicates);
|
||||
int64 ManhattanDistance(
|
||||
operations_research::RoutingModel::NodeIndex from,
|
||||
operations_research::RoutingModel::NodeIndex to) const;
|
||||
int64 NegManhattanDistance(
|
||||
operations_research::RoutingModel::NodeIndex from,
|
||||
operations_research::RoutingModel::NodeIndex to) const;
|
||||
int64 ManhattanTime(operations_research::RoutingModel::NodeIndex from,
|
||||
operations_research::RoutingModel::NodeIndex to) const;
|
||||
|
||||
bool SameLocation(operations_research::RoutingModel::NodeIndex node1,
|
||||
operations_research::RoutingModel::NodeIndex node2) const;
|
||||
int64 SameLocationFromIndex(int64 node1, int64 node2) const;
|
||||
|
||||
private:
|
||||
class Location {
|
||||
public:
|
||||
Location();
|
||||
Location(int64 x, int64 y);
|
||||
int64 DistanceTo(const Location& location) const;
|
||||
bool IsAtSameLocation(const Location& location) const;
|
||||
|
||||
private:
|
||||
static int64 Abs(int64 value);
|
||||
|
||||
int64 x_;
|
||||
int64 y_;
|
||||
};
|
||||
|
||||
MTRandom randomizer_;
|
||||
const int64 speed_;
|
||||
ITIVector<operations_research::RoutingModel::NodeIndex, Location> locations_;
|
||||
};
|
||||
|
||||
// Random demand.
|
||||
class RandomDemand {
|
||||
public:
|
||||
RandomDemand(int size, operations_research::RoutingModel::NodeIndex depot,
|
||||
bool use_deterministic_seed);
|
||||
void Initialize();
|
||||
int64 Demand(operations_research::RoutingModel::NodeIndex from,
|
||||
operations_research::RoutingModel::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
std::unique_ptr<int64[]> demand_;
|
||||
const int size_;
|
||||
const operations_research::RoutingModel::NodeIndex depot_;
|
||||
const bool use_deterministic_seed_;
|
||||
};
|
||||
|
||||
// Service time (proportional to demand) + transition time callback.
|
||||
class ServiceTimePlusTransition {
|
||||
public:
|
||||
ServiceTimePlusTransition(
|
||||
int64 time_per_demand_unit,
|
||||
operations_research::RoutingModel::NodeEvaluator2* demand,
|
||||
operations_research::RoutingModel::NodeEvaluator2* transition_time);
|
||||
int64 Compute(operations_research::RoutingModel::NodeIndex from,
|
||||
operations_research::RoutingModel::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
const int64 time_per_demand_unit_;
|
||||
std::unique_ptr<operations_research::RoutingModel::NodeEvaluator2> demand_;
|
||||
std::unique_ptr<operations_research::RoutingModel::NodeEvaluator2>
|
||||
transition_time_;
|
||||
};
|
||||
|
||||
// Stop service time + transition time callback.
|
||||
class StopServiceTimePlusTransition {
|
||||
public:
|
||||
StopServiceTimePlusTransition(
|
||||
int64 stop_time, const LocationContainer& location_container,
|
||||
operations_research::RoutingModel::NodeEvaluator2* transition_time);
|
||||
int64 Compute(operations_research::RoutingModel::NodeIndex from,
|
||||
operations_research::RoutingModel::NodeIndex to) const;
|
||||
|
||||
private:
|
||||
const int64 stop_time_;
|
||||
const LocationContainer& location_container_;
|
||||
std::unique_ptr<operations_research::RoutingModel::NodeEvaluator2> demand_;
|
||||
std::unique_ptr<operations_research::RoutingModel::NodeEvaluator2>
|
||||
transition_time_;
|
||||
};
|
||||
|
||||
// Route plan displayer.
|
||||
// TODO(user): Move the display code to the routing library.
|
||||
void DisplayPlan(
|
||||
const operations_research::RoutingModel& routing,
|
||||
const operations_research::Assignment& plan, bool use_same_vehicle_costs,
|
||||
int64 max_nodes_per_group, int64 same_vehicle_cost,
|
||||
const operations_research::RoutingDimension& capacity_dimension,
|
||||
const operations_research::RoutingDimension& time_dimension);
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_EXAMPLES_CVRPTW_LIB_H_
|
||||
@@ -19,7 +19,6 @@
|
||||
// must visit certain nodes (refueling nodes) before the quantity of fuel
|
||||
// reaches zero. Fuel consumption is proportional to the distance traveled.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback.h"
|
||||
@@ -27,22 +26,22 @@
|
||||
#include "base/commandlineflags.h"
|
||||
#include "base/integral_types.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/stringprintf.h"
|
||||
#include "constraint_solver/routing.h"
|
||||
#include "constraint_solver/routing_flags.h"
|
||||
#include "cpp/cvrptw_lib.h"
|
||||
#include "base/random.h"
|
||||
|
||||
using operations_research::Assignment;
|
||||
using operations_research::IntVar;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::Solver;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::StringAppendF;
|
||||
using operations_research::StringPrintf;
|
||||
|
||||
|
||||
DECLARE_string(routing_first_solution);
|
||||
DECLARE_bool(routing_no_lns);
|
||||
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
@@ -52,174 +51,12 @@ const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
const char* kFuel = "Fuel";
|
||||
|
||||
// Random seed generator.
|
||||
int32 GetSeed() {
|
||||
if (FLAGS_vrp_use_deterministic_random_seed) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
}
|
||||
}
|
||||
|
||||
// Returns true if node is a refueling node (based on node / refuel node ratio).
|
||||
bool IsRefuelNode(int64 node) {
|
||||
const int64 kRefuelNodeRatio = 10;
|
||||
return (node % kRefuelNodeRatio == 0);
|
||||
}
|
||||
|
||||
// Location container, contains positions of orders and can be used to obtain
|
||||
// Manhattan distances/times between locations.
|
||||
class LocationContainer {
|
||||
public:
|
||||
explicit LocationContainer(int64 speed)
|
||||
: randomizer_(GetSeed()), speed_(speed) {
|
||||
CHECK_LT(0, speed_);
|
||||
}
|
||||
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
|
||||
void AddRandomLocation(int64 x_max, int64 y_max) {
|
||||
AddLocation(randomizer_.Uniform(x_max + 1), randomizer_.Uniform(y_max + 1));
|
||||
}
|
||||
int64 ManhattanDistance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return locations_[from].DistanceTo(locations_[to]);
|
||||
}
|
||||
int64 NegManhattanDistance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return -ManhattanDistance(from, to);
|
||||
}
|
||||
int64 ManhattanTime(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return ManhattanDistance(from, to) / speed_;
|
||||
}
|
||||
|
||||
private:
|
||||
class Location {
|
||||
public:
|
||||
Location() : x_(0), y_(0) {}
|
||||
Location(int64 x, int64 y) : x_(x), y_(y) {}
|
||||
int64 DistanceTo(const Location& location) const {
|
||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
||||
}
|
||||
|
||||
private:
|
||||
static int64 Abs(int64 value) { return std::max(value, -value); }
|
||||
|
||||
int64 x_;
|
||||
int64 y_;
|
||||
};
|
||||
|
||||
ACMRandom randomizer_;
|
||||
const int64 speed_;
|
||||
ITIVector<RoutingModel::NodeIndex, Location> locations_;
|
||||
};
|
||||
|
||||
// Random demand.
|
||||
class RandomDemand {
|
||||
public:
|
||||
RandomDemand(int size, RoutingModel::NodeIndex depot)
|
||||
: size_(size), depot_(depot) {
|
||||
CHECK_LT(0, size_);
|
||||
}
|
||||
void Initialize() {
|
||||
const int64 kDemandMax = 5;
|
||||
const int64 kDemandMin = 1;
|
||||
demand_.reset(new int64[size_]);
|
||||
ACMRandom randomizer(GetSeed());
|
||||
for (int order = 0; order < size_; ++order) {
|
||||
if (order == depot_) {
|
||||
demand_[order] = 0;
|
||||
} else {
|
||||
demand_[order] =
|
||||
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
|
||||
// Refuel nodes don't have a demand.
|
||||
if (!IsRefuelNode(from.value())) {
|
||||
return demand_[from.value()];
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<int64[]> demand_;
|
||||
const int size_;
|
||||
const RoutingModel::NodeIndex depot_;
|
||||
};
|
||||
|
||||
// Service time (proportional to demand) + transition time callback.
|
||||
class ServiceTimePlusTransition {
|
||||
public:
|
||||
ServiceTimePlusTransition(int64 time_per_demand_unit,
|
||||
RoutingModel::NodeEvaluator2* demand,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: time_per_demand_unit_(time_per_demand_unit),
|
||||
demand_(demand),
|
||||
transition_time_(transition_time) {}
|
||||
int64 Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return time_per_demand_unit_ * demand_->Run(from, to) +
|
||||
transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
private:
|
||||
const int64 time_per_demand_unit_;
|
||||
std::unique_ptr<RoutingModel::NodeEvaluator2> demand_;
|
||||
std::unique_ptr<RoutingModel::NodeEvaluator2> transition_time_;
|
||||
};
|
||||
|
||||
// Route plan displayer.
|
||||
// TODO(user): Move the display code to the routing library.
|
||||
void DisplayPlan(const RoutingModel& routing, const Assignment& plan) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
StringAppendF(&dropped, " %d", order);
|
||||
} else {
|
||||
StringAppendF(&dropped, ", %d", order);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dropped.empty()) {
|
||||
plan_output += "Dropped orders:" + dropped + "\n";
|
||||
}
|
||||
|
||||
// Display actual output for each vehicle.
|
||||
const RoutingDimension& capacity_dimension =
|
||||
routing.GetDimensionOrDie(kCapacity);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel);
|
||||
for (int route_number = 0; route_number < routing.vehicles();
|
||||
++route_number) {
|
||||
int64 order = routing.Start(route_number);
|
||||
StringAppendF(&plan_output, "Route %d: ", route_number);
|
||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
||||
plan_output += "Empty\n";
|
||||
} else {
|
||||
while (true) {
|
||||
IntVar* const load_var = capacity_dimension.CumulVar(order);
|
||||
IntVar* const time_var = time_dimension.CumulVar(order);
|
||||
IntVar* const fuel_var = fuel_dimension.CumulVar(order);
|
||||
StringAppendF(&plan_output,
|
||||
"%lld Load(%lld) Time(%lld, %lld) Fuel(%lld, %lld) -> ",
|
||||
order, plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var), plan.Min(fuel_var),
|
||||
plan.Max(fuel_var));
|
||||
if (routing.IsEnd(order)) break;
|
||||
order = plan.Value(routing.NextVar(order));
|
||||
}
|
||||
}
|
||||
plan_output += "\n";
|
||||
}
|
||||
LOG(INFO) << plan_output;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags( &argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
|
||||
@@ -230,16 +67,17 @@ int main(int argc, char** argv) {
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles);
|
||||
routing.SetDepot(kDepot);
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
FLAGS_routing_first_solution = "PathCheapestArc";
|
||||
// Disabling Large Neighborhood Search, comment out to activate it.
|
||||
FLAGS_routing_no_lns = true;
|
||||
RoutingSearchParameters parameters =
|
||||
operations_research::BuildSearchParametersFromFlags();
|
||||
parameters.set_first_solution_strategy(
|
||||
operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
parameters.mutable_local_search_operators()->set_use_path_lns(false);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed);
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
|
||||
locations.AddRandomLocation(kXMax, kYMax);
|
||||
}
|
||||
@@ -251,7 +89,8 @@ int main(int argc, char** argv) {
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot);
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
@@ -266,9 +105,10 @@ int main(int argc, char** argv) {
|
||||
routing.AddDimension(
|
||||
NewPermanentCallback(&time, &ServiceTimePlusTransition::Compute),
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/true, kTime);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
const operations_research::RoutingDimension& time_dimension =
|
||||
routing.GetDimensionOrDie(kTime);
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed());
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
if (!IsRefuelNode(order)) {
|
||||
@@ -285,7 +125,8 @@ int main(int argc, char** argv) {
|
||||
NewPermanentCallback(&locations,
|
||||
&LocationContainer::NegManhattanDistance),
|
||||
kFuelCapacity, kFuelCapacity, /*fix_start_cumul_to_zero=*/false, kFuel);
|
||||
const RoutingDimension& fuel_dimension = routing.GetDimensionOrDie(kFuel);
|
||||
const operations_research::RoutingDimension& fuel_dimension =
|
||||
routing.GetDimensionOrDie(kFuel);
|
||||
for (int order = 0; order < routing.Size(); ++order) {
|
||||
// Only let slack free for refueling nodes.
|
||||
if (!IsRefuelNode(order) || routing.IsStart(order)) {
|
||||
@@ -305,9 +146,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const Assignment* solution = routing.Solve();
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
if (solution != NULL) {
|
||||
DisplayPlan(routing, *solution);
|
||||
DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
} else {
|
||||
LOG(INFO) << "No solution found.";
|
||||
}
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
// empty routes; fix this when we have an API on the cumulative constraints
|
||||
// with variable demands.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback.h"
|
||||
@@ -29,22 +28,21 @@
|
||||
#include "base/commandlineflags.h"
|
||||
#include "base/integral_types.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/stringprintf.h"
|
||||
#include "constraint_solver/routing.h"
|
||||
#include "constraint_solver/routing_flags.h"
|
||||
#include "cpp/cvrptw_lib.h"
|
||||
#include "base/random.h"
|
||||
|
||||
using operations_research::Assignment;
|
||||
using operations_research::IntervalVar;
|
||||
using operations_research::IntVar;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::Solver;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::StringAppendF;
|
||||
using operations_research::StringPrintf;
|
||||
|
||||
DECLARE_string(routing_first_solution);
|
||||
DECLARE_bool(routing_no_lns);
|
||||
DEFINE_int32(vrp_orders, 100, "Nodes in the problem.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
@@ -53,155 +51,6 @@ DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
|
||||
// Random seed generator.
|
||||
int32 GetSeed() {
|
||||
if (FLAGS_vrp_use_deterministic_random_seed) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
}
|
||||
}
|
||||
|
||||
// Location container, contains positions of orders and can be used to obtain
|
||||
// Manhattan distances/times between locations.
|
||||
class LocationContainer {
|
||||
public:
|
||||
explicit LocationContainer(int64 speed)
|
||||
: randomizer_(GetSeed()), speed_(speed) {
|
||||
CHECK_LT(0, speed_);
|
||||
}
|
||||
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
|
||||
void AddRandomLocation(int64 x_max, int64 y_max) {
|
||||
AddLocation(randomizer_.Uniform(x_max + 1), randomizer_.Uniform(y_max + 1));
|
||||
}
|
||||
int64 ManhattanDistance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return locations_[from].DistanceTo(locations_[to]);
|
||||
}
|
||||
int64 ManhattanTime(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return ManhattanDistance(from, to) / speed_;
|
||||
}
|
||||
|
||||
private:
|
||||
class Location {
|
||||
public:
|
||||
Location() : x_(0), y_(0) {}
|
||||
Location(int64 x, int64 y) : x_(x), y_(y) {}
|
||||
int64 DistanceTo(const Location& location) const {
|
||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
||||
}
|
||||
|
||||
private:
|
||||
static int64 Abs(int64 value) { return std::max(value, -value); }
|
||||
|
||||
int64 x_;
|
||||
int64 y_;
|
||||
};
|
||||
|
||||
ACMRandom randomizer_;
|
||||
const int64 speed_;
|
||||
ITIVector<RoutingModel::NodeIndex, Location> locations_;
|
||||
};
|
||||
|
||||
// Random demand.
|
||||
class RandomDemand {
|
||||
public:
|
||||
RandomDemand(int size, RoutingModel::NodeIndex depot)
|
||||
: size_(size), depot_(depot) {
|
||||
CHECK_LT(0, size_);
|
||||
}
|
||||
void Initialize() {
|
||||
const int64 kDemandMax = 5;
|
||||
const int64 kDemandMin = 1;
|
||||
demand_.reset(new int64[size_]);
|
||||
ACMRandom randomizer(GetSeed());
|
||||
for (int order = 0; order < size_; ++order) {
|
||||
if (order == depot_) {
|
||||
demand_[order] = 0;
|
||||
} else {
|
||||
demand_[order] =
|
||||
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
|
||||
return demand_[from.value()];
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<int64[]> demand_;
|
||||
const int size_;
|
||||
const RoutingModel::NodeIndex depot_;
|
||||
};
|
||||
|
||||
// Service time (proportional to demand) + transition time callback.
|
||||
class ServiceTimePlusTransition {
|
||||
public:
|
||||
ServiceTimePlusTransition(int64 time_per_demand_unit,
|
||||
RoutingModel::NodeEvaluator2* demand,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: time_per_demand_unit_(time_per_demand_unit),
|
||||
demand_(demand),
|
||||
transition_time_(transition_time) {}
|
||||
int64 Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return time_per_demand_unit_ * demand_->Run(from, to) +
|
||||
transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
private:
|
||||
const int64 time_per_demand_unit_;
|
||||
std::unique_ptr<RoutingModel::NodeEvaluator2> demand_;
|
||||
std::unique_ptr<RoutingModel::NodeEvaluator2> transition_time_;
|
||||
};
|
||||
|
||||
// Route plan displayer.
|
||||
// TODO(user): Move the display code to the routing library.
|
||||
void DisplayPlan(const RoutingModel& routing, const Assignment& plan) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
StringAppendF(&dropped, " %d", order);
|
||||
} else {
|
||||
StringAppendF(&dropped, ", %d", order);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dropped.empty()) {
|
||||
plan_output += "Dropped orders:" + dropped + "\n";
|
||||
}
|
||||
|
||||
// Display actual output for each vehicle.
|
||||
const RoutingDimension& capacity_dimension =
|
||||
routing.GetDimensionOrDie(kCapacity);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
for (int route_number = 0; route_number < routing.vehicles();
|
||||
++route_number) {
|
||||
int64 order = routing.Start(route_number);
|
||||
StringAppendF(&plan_output, "Route %d: ", route_number);
|
||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
||||
plan_output += "Empty\n";
|
||||
} else {
|
||||
while (true) {
|
||||
IntVar* const load_var = capacity_dimension.CumulVar(order);
|
||||
IntVar* const time_var = time_dimension.CumulVar(order);
|
||||
StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld) -> ",
|
||||
order, plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var));
|
||||
if (routing.IsEnd(order)) break;
|
||||
order = plan.Value(routing.NextVar(order));
|
||||
}
|
||||
}
|
||||
}
|
||||
LOG(INFO) << plan_output;
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags( &argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_orders) << "Specify an instance size greater than 0.";
|
||||
@@ -212,16 +61,17 @@ int main(int argc, char** argv) {
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(FLAGS_vrp_orders + 1, FLAGS_vrp_vehicles);
|
||||
routing.SetDepot(kDepot);
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
FLAGS_routing_first_solution = "PathCheapestArc";
|
||||
// Disabling Large Neighborhood Search, comment out to activate it.
|
||||
FLAGS_routing_no_lns = true;
|
||||
RoutingSearchParameters parameters =
|
||||
operations_research::BuildSearchParametersFromFlags();
|
||||
parameters.set_first_solution_strategy(
|
||||
operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
parameters.mutable_local_search_operators()->set_use_path_lns(false);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed);
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int location = 0; location <= FLAGS_vrp_orders; ++location) {
|
||||
locations.AddRandomLocation(kXMax, kYMax);
|
||||
}
|
||||
@@ -233,7 +83,8 @@ int main(int argc, char** argv) {
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot);
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
@@ -250,7 +101,7 @@ int main(int argc, char** argv) {
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
|
||||
// Adding time windows.
|
||||
ACMRandom randomizer(GetSeed());
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
@@ -259,15 +110,15 @@ int main(int argc, char** argv) {
|
||||
|
||||
// Adding resource constraints at the depot (start and end location of
|
||||
// routes).
|
||||
std::vector<IntVar*> start_end_times;
|
||||
std::vector<operations_research::IntVar*> start_end_times;
|
||||
for (int i = 0; i < FLAGS_vrp_vehicles; ++i) {
|
||||
start_end_times.push_back(routing.CumulVar(routing.End(i), kTime));
|
||||
start_end_times.push_back(routing.CumulVar(routing.Start(i), kTime));
|
||||
}
|
||||
// Build corresponding time intervals.
|
||||
const int64 kVehicleSetup = 180;
|
||||
Solver* const solver = routing.solver();
|
||||
std::vector<IntervalVar*> intervals;
|
||||
operations_research::Solver* const solver = routing.solver();
|
||||
std::vector<operations_research::IntervalVar*> intervals;
|
||||
solver->MakeFixedDurationIntervalVarArray(start_end_times, kVehicleSetup,
|
||||
"depot_interval", &intervals);
|
||||
// Constrain the number of maximum simultaneous intervals at depot.
|
||||
@@ -290,9 +141,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const Assignment* solution = routing.Solve();
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
if (solution != NULL) {
|
||||
DisplayPlan(routing, *solution);
|
||||
DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
} else {
|
||||
LOG(INFO) << "No solution found.";
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
// limits the number of vehicles which can simultaneously leave or enter a node
|
||||
// to one.
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "base/callback.h"
|
||||
@@ -27,25 +26,25 @@
|
||||
#include "base/commandlineflags.h"
|
||||
#include "base/integral_types.h"
|
||||
#include "base/logging.h"
|
||||
#include "base/stringprintf.h"
|
||||
#include "base/join.h"
|
||||
#include "constraint_solver/routing.h"
|
||||
#include "constraint_solver/routing_flags.h"
|
||||
#include "cpp/cvrptw_lib.h"
|
||||
#include "base/random.h"
|
||||
|
||||
using operations_research::Assignment;
|
||||
using operations_research::IntervalVar;
|
||||
using operations_research::IntExpr;
|
||||
using operations_research::IntVar;
|
||||
using operations_research::RoutingDimension;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::Solver;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::LocationContainer;
|
||||
using operations_research::RandomDemand;
|
||||
using operations_research::ServiceTimePlusTransition;
|
||||
using operations_research::GetSeed;
|
||||
using operations_research::StopServiceTimePlusTransition;
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::StrCat;
|
||||
using operations_research::StringAppendF;
|
||||
using operations_research::StringPrintf;
|
||||
|
||||
DECLARE_string(routing_first_solution);
|
||||
DECLARE_bool(routing_no_lns);
|
||||
DEFINE_int32(vrp_stops, 25, "Stop locations in the problem.");
|
||||
DEFINE_int32(vrp_orders_per_stop, 5, "Nodes for each stop.");
|
||||
DEFINE_int32(vrp_vehicles, 20, "Size of Traveling Salesman Problem instance.");
|
||||
@@ -55,180 +54,6 @@ DEFINE_bool(vrp_use_deterministic_random_seed, false,
|
||||
const char* kTime = "Time";
|
||||
const char* kCapacity = "Capacity";
|
||||
|
||||
// Random seed generator.
|
||||
int32 GetSeed() {
|
||||
if (FLAGS_vrp_use_deterministic_random_seed) {
|
||||
return ACMRandom::DeterministicSeed();
|
||||
} else {
|
||||
return ACMRandom::HostnamePidTimeSeed();
|
||||
}
|
||||
}
|
||||
|
||||
// Location container, contains positions of orders and can be used to obtain
|
||||
// Manhattan distances/times between locations.
|
||||
class LocationContainer {
|
||||
public:
|
||||
explicit LocationContainer(int64 speed)
|
||||
: randomizer_(GetSeed()), speed_(speed) {
|
||||
CHECK_LT(0, speed_);
|
||||
}
|
||||
void AddLocation(int64 x, int64 y) { locations_.push_back(Location(x, y)); }
|
||||
void AddRandomLocation(int64 x_max, int64 y_max, int duplicates) {
|
||||
const int64 x = randomizer_.Uniform(x_max + 1);
|
||||
const int64 y = randomizer_.Uniform(y_max + 1);
|
||||
for (int i = 0; i < duplicates; ++i) {
|
||||
AddLocation(x, y);
|
||||
}
|
||||
}
|
||||
int64 ManhattanDistance(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return locations_[from].DistanceTo(locations_[to]);
|
||||
}
|
||||
int64 ManhattanTime(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return ManhattanDistance(from, to) / speed_;
|
||||
}
|
||||
bool SameLocation(RoutingModel::NodeIndex node1,
|
||||
RoutingModel::NodeIndex node2) const {
|
||||
if (node1 < locations_.size() && node2 < locations_.size()) {
|
||||
return locations_[node1].IsAtSameLocation(locations_[node2]);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int64 SameLocationFromIndex(int64 node1, int64 node2) const {
|
||||
// The direct conversion from constraint model indices to routing model
|
||||
// nodes is correct because the depot is node 0.
|
||||
// TODO(user): Fetch proper indices from routing model.
|
||||
return SameLocation(RoutingModel::NodeIndex(node1),
|
||||
RoutingModel::NodeIndex(node2));
|
||||
}
|
||||
|
||||
private:
|
||||
class Location {
|
||||
public:
|
||||
Location() : x_(0), y_(0) {}
|
||||
Location(int64 x, int64 y) : x_(x), y_(y) {}
|
||||
int64 DistanceTo(const Location& location) const {
|
||||
return Abs(x_ - location.x_) + Abs(y_ - location.y_);
|
||||
}
|
||||
bool IsAtSameLocation(const Location& location) const {
|
||||
return x_ == location.x_ && y_ == location.y_;
|
||||
}
|
||||
|
||||
private:
|
||||
static int64 Abs(int64 value) { return std::max(value, -value); }
|
||||
|
||||
int64 x_;
|
||||
int64 y_;
|
||||
};
|
||||
|
||||
ACMRandom randomizer_;
|
||||
const int64 speed_;
|
||||
ITIVector<RoutingModel::NodeIndex, Location> locations_;
|
||||
};
|
||||
|
||||
// Random demand.
|
||||
class RandomDemand {
|
||||
public:
|
||||
RandomDemand(int size, RoutingModel::NodeIndex depot)
|
||||
: size_(size), depot_(depot) {
|
||||
CHECK_LT(0, size_);
|
||||
}
|
||||
void Initialize() {
|
||||
const int64 kDemandMax = 5;
|
||||
const int64 kDemandMin = 1;
|
||||
demand_.reset(new int64[size_]);
|
||||
ACMRandom randomizer(GetSeed());
|
||||
for (int order = 0; order < size_; ++order) {
|
||||
if (order == depot_) {
|
||||
demand_[order] = 0;
|
||||
} else {
|
||||
demand_[order] =
|
||||
kDemandMin + randomizer.Uniform(kDemandMax - kDemandMin + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
int64 Demand(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) const {
|
||||
return demand_[from.value()];
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<int64[]> demand_;
|
||||
const int size_;
|
||||
const RoutingModel::NodeIndex depot_;
|
||||
};
|
||||
|
||||
// Stop service time + transition time callback.
|
||||
class StopServiceTimePlusTransition {
|
||||
public:
|
||||
StopServiceTimePlusTransition(int64 stop_time,
|
||||
const LocationContainer& location_container,
|
||||
RoutingModel::NodeEvaluator2* transition_time)
|
||||
: stop_time_(stop_time),
|
||||
location_container_(location_container),
|
||||
transition_time_(transition_time) {}
|
||||
int64 Compute(RoutingModel::NodeIndex from,
|
||||
RoutingModel::NodeIndex to) const {
|
||||
return location_container_.SameLocation(from, to)
|
||||
? 0
|
||||
: stop_time_ + transition_time_->Run(from, to);
|
||||
}
|
||||
|
||||
private:
|
||||
const int64 stop_time_;
|
||||
const LocationContainer& location_container_;
|
||||
std::unique_ptr<RoutingModel::NodeEvaluator2> demand_;
|
||||
std::unique_ptr<RoutingModel::NodeEvaluator2> transition_time_;
|
||||
};
|
||||
|
||||
// Route plan displayer.
|
||||
// TODO(user): Move the display code to the routing library.
|
||||
void DisplayPlan(const RoutingModel& routing, const Assignment& plan) {
|
||||
// Display plan cost.
|
||||
std::string plan_output = StringPrintf("Cost %lld\n", plan.ObjectiveValue());
|
||||
|
||||
// Display dropped orders.
|
||||
std::string dropped;
|
||||
for (int order = 1; order < routing.nodes(); ++order) {
|
||||
if (plan.Value(routing.NextVar(order)) == order) {
|
||||
if (dropped.empty()) {
|
||||
StringAppendF(&dropped, " %d", order);
|
||||
} else {
|
||||
StringAppendF(&dropped, ", %d", order);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!dropped.empty()) {
|
||||
plan_output += "Dropped orders:" + dropped + "\n";
|
||||
}
|
||||
LOG(INFO) << plan_output;
|
||||
|
||||
// Display actual output for each vehicle.
|
||||
const RoutingDimension& capacity_dimension =
|
||||
routing.GetDimensionOrDie(kCapacity);
|
||||
const RoutingDimension& time_dimension = routing.GetDimensionOrDie(kTime);
|
||||
for (int route_number = 0; route_number < routing.vehicles();
|
||||
++route_number) {
|
||||
std::string route_output;
|
||||
int64 order = routing.Start(route_number);
|
||||
StringAppendF(&route_output, "Route %d: ", route_number);
|
||||
if (routing.IsEnd(plan.Value(routing.NextVar(order)))) {
|
||||
route_output += "Empty\n";
|
||||
} else {
|
||||
while (true) {
|
||||
IntVar* const load_var = capacity_dimension.CumulVar(order);
|
||||
IntVar* const time_var = time_dimension.CumulVar(order);
|
||||
StringAppendF(&route_output, " -> %lld Load(%lld) Time(%lld, %lld)",
|
||||
order, plan.Value(load_var), plan.Min(time_var),
|
||||
plan.Max(time_var));
|
||||
if (routing.IsEnd(order)) break;
|
||||
order = plan.Value(routing.NextVar(order));
|
||||
}
|
||||
}
|
||||
LOG(INFO) << route_output;
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags( &argc, &argv, true);
|
||||
CHECK_LT(0, FLAGS_vrp_stops) << "Specify an instance size greater than 0.";
|
||||
@@ -241,16 +66,17 @@ int main(int argc, char** argv) {
|
||||
const RoutingModel::NodeIndex kDepot(0);
|
||||
RoutingModel routing(vrp_orders + 1, FLAGS_vrp_vehicles);
|
||||
routing.SetDepot(kDepot);
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
FLAGS_routing_first_solution = "PathCheapestArc";
|
||||
// Disabling Large Neighborhood Search, comment out to activate it.
|
||||
FLAGS_routing_no_lns = true;
|
||||
RoutingSearchParameters parameters =
|
||||
operations_research::BuildSearchParametersFromFlags();
|
||||
parameters.set_first_solution_strategy(
|
||||
operations_research::FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
parameters.mutable_local_search_operators()->set_use_path_lns(false);
|
||||
|
||||
// Setting up locations.
|
||||
const int64 kXMax = 100000;
|
||||
const int64 kYMax = 100000;
|
||||
const int64 kSpeed = 10;
|
||||
LocationContainer locations(kSpeed);
|
||||
LocationContainer locations(kSpeed, FLAGS_vrp_use_deterministic_random_seed);
|
||||
for (int stop = 0; stop <= FLAGS_vrp_stops; ++stop) {
|
||||
const int num_orders = stop == 0 ? 1 : FLAGS_vrp_orders_per_stop;
|
||||
locations.AddRandomLocation(kXMax, kYMax, num_orders);
|
||||
@@ -263,7 +89,8 @@ int main(int argc, char** argv) {
|
||||
// Adding capacity dimension constraints.
|
||||
const int64 kVehicleCapacity = 40;
|
||||
const int64 kNullCapacitySlack = 0;
|
||||
RandomDemand demand(routing.nodes(), kDepot);
|
||||
RandomDemand demand(routing.nodes(), kDepot,
|
||||
FLAGS_vrp_use_deterministic_random_seed);
|
||||
demand.Initialize();
|
||||
routing.AddDimension(NewPermanentCallback(&demand, &RandomDemand::Demand),
|
||||
kNullCapacitySlack, kVehicleCapacity,
|
||||
@@ -280,7 +107,7 @@ int main(int argc, char** argv) {
|
||||
kHorizon, kHorizon, /*fix_start_cumul_to_zero=*/false, kTime);
|
||||
|
||||
// Adding time windows, for the sake of simplicty same for each stop.
|
||||
ACMRandom randomizer(GetSeed());
|
||||
ACMRandom randomizer(GetSeed(FLAGS_vrp_use_deterministic_random_seed));
|
||||
const int64 kTWDuration = 5 * 3600;
|
||||
for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) {
|
||||
const int64 start = randomizer.Uniform(kHorizon - kTWDuration);
|
||||
@@ -292,7 +119,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Adding resource constraints at order locations.
|
||||
Solver* const solver = routing.solver();
|
||||
operations_research::Solver* const solver = routing.solver();
|
||||
std::vector<IntervalVar*> intervals;
|
||||
for (int stop = 0; stop < FLAGS_vrp_stops; ++stop) {
|
||||
std::vector<IntervalVar*> stop_intervals;
|
||||
@@ -304,14 +131,15 @@ int main(int argc, char** argv) {
|
||||
intervals.push_back(interval);
|
||||
stop_intervals.push_back(interval);
|
||||
// Link order and interval.
|
||||
IntVar* const order_start = routing.CumulVar(order, kTime);
|
||||
operations_research::IntVar* const order_start =
|
||||
routing.CumulVar(order, kTime);
|
||||
solver->AddConstraint(
|
||||
solver->MakeIsEqualCt(interval->SafeStartExpr(0), order_start,
|
||||
interval->PerformedExpr()->Var()));
|
||||
// Make interval performed iff corresponding order has service time.
|
||||
// An order has no service time iff it is at the same location as the
|
||||
// next order on the route.
|
||||
IntVar* const is_null_duration =
|
||||
operations_research::IntVar* const is_null_duration =
|
||||
solver->MakeElement([&locations, order](int64 index) {
|
||||
return locations.SameLocationFromIndex(order, index);
|
||||
}, routing.NextVar(order))->Var();
|
||||
@@ -343,9 +171,13 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const Assignment* solution = routing.Solve();
|
||||
const operations_research::Assignment* solution =
|
||||
routing.SolveWithParameters(parameters);
|
||||
if (solution != NULL) {
|
||||
DisplayPlan(routing, *solution);
|
||||
DisplayPlan(routing, *solution, /*use_same_vehicle_costs=*/false,
|
||||
/*max_nodes_per_group=*/0, /*same_vehicle_cost=*/0,
|
||||
routing.GetDimensionOrDie(kCapacity),
|
||||
routing.GetDimensionOrDie(kTime));
|
||||
LOG(INFO) << "Stop intervals:";
|
||||
for (IntervalVar* const interval : intervals) {
|
||||
if (solution->PerformedValue(interval)) {
|
||||
|
||||
@@ -60,24 +60,24 @@ void RunIntegerProgrammingExample(
|
||||
|
||||
void RunAllExamples() {
|
||||
#if defined(USE_GLPK)
|
||||
LOG(INFO) << "---- Integer programming example with GLPK ----";
|
||||
RunIntegerProgrammingExample(MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING);
|
||||
LOG(INFO) << "---- Integer programming example with GLPK ----";
|
||||
RunIntegerProgrammingExample(MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING);
|
||||
#endif
|
||||
#if defined(USE_CBC)
|
||||
LOG(INFO) << "---- Integer programming example with CBC ----";
|
||||
RunIntegerProgrammingExample(MPSolver::CBC_MIXED_INTEGER_PROGRAMMING);
|
||||
LOG(INFO) << "---- Integer programming example with CBC ----";
|
||||
RunIntegerProgrammingExample(MPSolver::CBC_MIXED_INTEGER_PROGRAMMING);
|
||||
#endif
|
||||
#if defined(USE_SCIP)
|
||||
LOG(INFO) << "---- Integer programming example with SCIP ----";
|
||||
RunIntegerProgrammingExample(MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING);
|
||||
LOG(INFO) << "---- Integer programming example with SCIP ----";
|
||||
RunIntegerProgrammingExample(MPSolver::SCIP_MIXED_INTEGER_PROGRAMMING);
|
||||
#endif
|
||||
#if defined(USE_GUROBI)
|
||||
LOG(INFO) << "---- Integer programming example with Gurobi ----";
|
||||
RunIntegerProgrammingExample(MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING);
|
||||
LOG(INFO) << "---- Integer programming example with Gurobi ----";
|
||||
RunIntegerProgrammingExample(MPSolver::GUROBI_MIXED_INTEGER_PROGRAMMING);
|
||||
#endif // USE_GUROBI
|
||||
#if defined(USE_CPLEX)
|
||||
LOG(INFO) << "---- Integer programming example with CPLEX ----";
|
||||
RunIntegerProgrammingExample(MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING);
|
||||
LOG(INFO) << "---- Integer programming example with CPLEX ----";
|
||||
RunIntegerProgrammingExample(MPSolver::CPLEX_MIXED_INTEGER_PROGRAMMING);
|
||||
#endif // USE_CPLEX
|
||||
}
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -12,10 +12,10 @@
|
||||
// limitations under the License.
|
||||
|
||||
|
||||
#include "graph/linear_assignment.h"
|
||||
#include "base/commandlineflags.h"
|
||||
#include "base/logging.h"
|
||||
#include "graph/ebert_graph.h"
|
||||
#include "graph/linear_assignment.h"
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -51,24 +51,23 @@ void AnotherAssignment() {
|
||||
{{8, 7, 9, 9}, {5, 2, 7, 8}, {6, 1, 4, 9}, {2, 3, 2, 6}});
|
||||
const int kSize = matrice.size();
|
||||
ForwardStarGraph graph(2 * kSize, kSize * kSize);
|
||||
LinearSumAssignment<ForwardStarGraph>* assignement =
|
||||
new LinearSumAssignment<ForwardStarGraph>(graph, kSize);
|
||||
LinearSumAssignment<ForwardStarGraph> assignement(graph, kSize);
|
||||
for (int i = 0; i < kSize; ++i) {
|
||||
CHECK_EQ(kSize, matrice[i].size());
|
||||
for (int j = 0; j < kSize; ++j) {
|
||||
int arcIndex = graph.AddArc(i, j + kSize);
|
||||
assignement->SetArcCost(arcIndex, matrice[i][j]);
|
||||
assignement.SetArcCost(arcIndex, matrice[i][j]);
|
||||
}
|
||||
}
|
||||
|
||||
bool succes = assignement->ComputeAssignment();
|
||||
LOG(INFO) << "Cost : " << assignement->GetCost();
|
||||
assignement.ComputeAssignment();
|
||||
LOG(INFO) << "Cost : " << assignement.GetCost();
|
||||
}
|
||||
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
gflags::ParseCommandLineFlags( &argc, &argv, true);
|
||||
operations_research::AssignmentOn4x4Matrix();
|
||||
operations_research::AnotherAssignment();
|
||||
return 0;
|
||||
|
||||
@@ -73,10 +73,10 @@ std::string ConstraintLabel(int index) { return StringPrintf("ct_%i", index); }
|
||||
|
||||
// Scans argument to add links in the graph.
|
||||
template <class T>
|
||||
void ExportLinks(const CPModelProto& model, const std::string& source,
|
||||
const T& proto, GraphExporter* const exporter) {
|
||||
void ExportLinks(const CpModel& model, const std::string& source, const T& proto,
|
||||
GraphExporter* const exporter) {
|
||||
const std::string& arg_name = model.tags(proto.argument_index());
|
||||
if (proto.has_integer_expression_index()) {
|
||||
if (proto.type() == CpArgument::EXPRESSION) {
|
||||
exporter->WriteLink(source, ExprLabel(proto.integer_expression_index()),
|
||||
arg_name);
|
||||
}
|
||||
@@ -84,7 +84,7 @@ void ExportLinks(const CPModelProto& model, const std::string& source,
|
||||
exporter->WriteLink(source, ExprLabel(proto.integer_expression_array(i)),
|
||||
arg_name);
|
||||
}
|
||||
if (proto.has_interval_index()) {
|
||||
if (proto.type() == CpArgument::INTERVAL) {
|
||||
exporter->WriteLink(source, IntervalLabel(proto.interval_index()),
|
||||
arg_name);
|
||||
}
|
||||
@@ -92,7 +92,7 @@ void ExportLinks(const CPModelProto& model, const std::string& source,
|
||||
exporter->WriteLink(source, IntervalLabel(proto.interval_array(i)),
|
||||
arg_name);
|
||||
}
|
||||
if (proto.has_sequence_index()) {
|
||||
if (proto.type() == CpArgument::SEQUENCE) {
|
||||
exporter->WriteLink(source, SequenceLabel(proto.sequence_index()),
|
||||
arg_name);
|
||||
}
|
||||
@@ -104,8 +104,7 @@ void ExportLinks(const CPModelProto& model, const std::string& source,
|
||||
|
||||
// Scans the expression protobuf to see if it corresponds to an
|
||||
// integer variable with min_value == max_value.
|
||||
bool GetValueIfConstant(const CPModelProto& model,
|
||||
const CPIntegerExpressionProto& proto,
|
||||
bool GetValueIfConstant(const CpModel& model, const CpIntegerExpression& proto,
|
||||
int64* const value) {
|
||||
CHECK_NOTNULL(value);
|
||||
const int expr_type = proto.type_index();
|
||||
@@ -115,12 +114,12 @@ bool GetValueIfConstant(const CPModelProto& model,
|
||||
if (proto.arguments_size() != 2) {
|
||||
return false;
|
||||
}
|
||||
const CPArgumentProto& arg1_proto = proto.arguments(0);
|
||||
const CpArgument& arg1_proto = proto.arguments(0);
|
||||
if (model.tags(arg1_proto.argument_index()) != ModelVisitor::kMinArgument) {
|
||||
return false;
|
||||
}
|
||||
const int64 value1 = arg1_proto.integer_value();
|
||||
const CPArgumentProto& arg2_proto = proto.arguments(1);
|
||||
const CpArgument& arg2_proto = proto.arguments(1);
|
||||
if (model.tags(arg2_proto.argument_index()) != ModelVisitor::kMaxArgument) {
|
||||
return false;
|
||||
}
|
||||
@@ -134,12 +133,12 @@ bool GetValueIfConstant(const CPModelProto& model,
|
||||
}
|
||||
|
||||
// Declares a labelled expression in the graph file.
|
||||
void DeclareExpression(int index, const CPModelProto& proto,
|
||||
void DeclareExpression(int index, const CpModel& proto,
|
||||
GraphExporter* const exporter) {
|
||||
const CPIntegerExpressionProto& expr = proto.expressions(index);
|
||||
const CpIntegerExpression& expr = proto.expressions(index);
|
||||
const std::string label = ExprLabel(index);
|
||||
int64 value = 0;
|
||||
if (expr.has_name()) {
|
||||
if (!expr.name().empty()) {
|
||||
exporter->WriteNode(label, expr.name(), "oval", kGreen1);
|
||||
} else if (GetValueIfConstant(proto, expr, &value)) {
|
||||
exporter->WriteNode(label, StringPrintf("%lld", value), "oval", kYellow);
|
||||
@@ -149,11 +148,11 @@ void DeclareExpression(int index, const CPModelProto& proto,
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareInterval(int index, const CPModelProto& proto,
|
||||
void DeclareInterval(int index, const CpModel& proto,
|
||||
GraphExporter* const exporter) {
|
||||
const CPIntervalVariableProto& interval = proto.intervals(index);
|
||||
const CpIntervalVariable& interval = proto.intervals(index);
|
||||
const std::string label = IntervalLabel(index);
|
||||
if (interval.has_name()) {
|
||||
if (!interval.name().empty()) {
|
||||
exporter->WriteNode(label, interval.name(), "circle", kGreen2);
|
||||
} else {
|
||||
const std::string& type = proto.tags(interval.type_index());
|
||||
@@ -161,11 +160,11 @@ void DeclareInterval(int index, const CPModelProto& proto,
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareSequence(int index, const CPModelProto& proto,
|
||||
void DeclareSequence(int index, const CpModel& proto,
|
||||
GraphExporter* const exporter) {
|
||||
const CPSequenceVariableProto& sequence = proto.sequences(index);
|
||||
const CpSequenceVariable& sequence = proto.sequences(index);
|
||||
const std::string label = SequenceLabel(index);
|
||||
if (sequence.has_name()) {
|
||||
if (!sequence.name().empty()) {
|
||||
exporter->WriteNode(label, sequence.name(), "circle", kGreen3);
|
||||
} else {
|
||||
const std::string& type = proto.tags(sequence.type_index());
|
||||
@@ -173,16 +172,16 @@ void DeclareSequence(int index, const CPModelProto& proto,
|
||||
}
|
||||
}
|
||||
|
||||
void DeclareConstraint(int index, const CPModelProto& proto,
|
||||
void DeclareConstraint(int index, const CpModel& proto,
|
||||
GraphExporter* const exporter) {
|
||||
const CPConstraintProto& ct = proto.constraints(index);
|
||||
const CpConstraint& ct = proto.constraints(index);
|
||||
const std::string& type = proto.tags(ct.type_index());
|
||||
const std::string label = ConstraintLabel(index);
|
||||
exporter->WriteNode(label, type, "rectangle", kBlue);
|
||||
}
|
||||
|
||||
// Parses the proto and exports it to a graph file.
|
||||
void ExportToGraphFile(const CPModelProto& proto, File* const file,
|
||||
void ExportToGraphFile(const CpModel& proto, File* const file,
|
||||
GraphExporter::GraphFormat format) {
|
||||
std::unique_ptr<GraphExporter> exporter(
|
||||
GraphExporter::MakeFileExporter(file, format));
|
||||
@@ -210,7 +209,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file,
|
||||
}
|
||||
|
||||
for (int i = 0; i < proto.expressions_size(); ++i) {
|
||||
const CPIntegerExpressionProto& expr = proto.expressions(i);
|
||||
const CpIntegerExpression& expr = proto.expressions(i);
|
||||
const std::string label = ExprLabel(i);
|
||||
for (int j = 0; j < expr.arguments_size(); ++j) {
|
||||
ExportLinks(proto, label, expr.arguments(j), exporter.get());
|
||||
@@ -218,7 +217,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file,
|
||||
}
|
||||
|
||||
for (int i = 0; i < proto.intervals_size(); ++i) {
|
||||
const CPIntervalVariableProto& interval = proto.intervals(i);
|
||||
const CpIntervalVariable& interval = proto.intervals(i);
|
||||
const std::string label = IntervalLabel(i);
|
||||
for (int j = 0; j < interval.arguments_size(); ++j) {
|
||||
ExportLinks(proto, label, interval.arguments(j), exporter.get());
|
||||
@@ -226,7 +225,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file,
|
||||
}
|
||||
|
||||
for (int i = 0; i < proto.sequences_size(); ++i) {
|
||||
const CPSequenceVariableProto& sequence = proto.sequences(i);
|
||||
const CpSequenceVariable& sequence = proto.sequences(i);
|
||||
const std::string label = SequenceLabel(i);
|
||||
for (int j = 0; j < sequence.arguments_size(); ++j) {
|
||||
ExportLinks(proto, label, sequence.arguments(j), exporter.get());
|
||||
@@ -234,7 +233,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file,
|
||||
}
|
||||
|
||||
for (int i = 0; i < proto.constraints_size(); ++i) {
|
||||
const CPConstraintProto& ct = proto.constraints(i);
|
||||
const CpConstraint& ct = proto.constraints(i);
|
||||
const std::string label = ConstraintLabel(i);
|
||||
for (int j = 0; j < ct.arguments_size(); ++j) {
|
||||
ExportLinks(proto, label, ct.arguments(j), exporter.get());
|
||||
@@ -242,7 +241,7 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file,
|
||||
}
|
||||
|
||||
if (proto.has_objective()) {
|
||||
const CPObjectiveProto& obj = proto.objective();
|
||||
const CpObjective& obj = proto.objective();
|
||||
exporter->WriteLink(kObjLabel, ExprLabel(obj.objective_index()),
|
||||
ModelVisitor::kExpressionArgument);
|
||||
}
|
||||
@@ -254,13 +253,13 @@ void ExportToGraphFile(const CPModelProto& proto, File* const file,
|
||||
int Run() {
|
||||
// ----- Load input file into protobuf -----
|
||||
|
||||
File* const file = File::Open(FLAGS_input, "r");
|
||||
if (file == NULL) {
|
||||
File* file;
|
||||
if (!file::Open(FLAGS_input, "r", &file, file::Defaults()).ok()) {
|
||||
LOG(WARNING) << "Cannot open " << FLAGS_input;
|
||||
return kProblem;
|
||||
}
|
||||
|
||||
CPModelProto model_proto;
|
||||
CpModel model_proto;
|
||||
RecordReader reader(file);
|
||||
if (!(reader.ReadProtocolMessage(&model_proto) && reader.Close())) {
|
||||
LOG(INFO) << "No model found in " << file->filename();
|
||||
@@ -270,7 +269,7 @@ int Run() {
|
||||
// ----- Display loaded protobuf -----
|
||||
|
||||
LOG(INFO) << "Read model " << model_proto.model();
|
||||
if (model_proto.has_license_text()) {
|
||||
if (!model_proto.license_text().empty()) {
|
||||
LOG(INFO) << "License = " << model_proto.license_text();
|
||||
}
|
||||
|
||||
@@ -296,8 +295,9 @@ int Run() {
|
||||
}
|
||||
|
||||
if (!FLAGS_insert_license.empty()) {
|
||||
File* const license = File::Open(FLAGS_insert_license, "rb");
|
||||
if (license == NULL) {
|
||||
File* license;
|
||||
if (!file::Open(FLAGS_insert_license, "rb", &license, file::Defaults())
|
||||
.ok()) {
|
||||
LOG(WARNING) << "Cannot open " << FLAGS_insert_license;
|
||||
return kProblem;
|
||||
}
|
||||
@@ -306,7 +306,7 @@ int Run() {
|
||||
license->Read(text, size);
|
||||
text[size] = '\0';
|
||||
model_proto.set_license_text(text);
|
||||
license->Close();
|
||||
license->Close(file::Defaults()).IgnoreError();
|
||||
}
|
||||
|
||||
// ----- Reporting -----
|
||||
@@ -355,8 +355,8 @@ int Run() {
|
||||
// ----- Output -----
|
||||
|
||||
if (!FLAGS_output.empty()) {
|
||||
File* const output = File::Open(FLAGS_output, "wb");
|
||||
if (output == NULL) {
|
||||
File* output;
|
||||
if (!file::Open(FLAGS_output, "wb", &output, file::Defaults()).ok()) {
|
||||
LOG(INFO) << "Cannot open " << FLAGS_output;
|
||||
return kProblem;
|
||||
}
|
||||
@@ -369,23 +369,23 @@ int Run() {
|
||||
}
|
||||
|
||||
if (!FLAGS_dot_file.empty()) {
|
||||
File* const dot_file = File::Open(FLAGS_dot_file, "w");
|
||||
if (dot_file == NULL) {
|
||||
File* dot_file;
|
||||
if (!file::Open(FLAGS_dot_file, "w", &dot_file, file::Defaults()).ok()) {
|
||||
LOG(INFO) << "Cannot open " << FLAGS_dot_file;
|
||||
return kProblem;
|
||||
}
|
||||
ExportToGraphFile(model_proto, dot_file, GraphExporter::DOT_FORMAT);
|
||||
dot_file->Close();
|
||||
dot_file->Close(file::Defaults()).IgnoreError();
|
||||
}
|
||||
|
||||
if (!FLAGS_gml_file.empty()) {
|
||||
File* const gml_file = File::Open(FLAGS_gml_file, "w");
|
||||
if (gml_file == NULL) {
|
||||
File* gml_file;
|
||||
if (!file::Open(FLAGS_gml_file, "w", &gml_file, file::Defaults()).ok()) {
|
||||
LOG(INFO) << "Cannot open " << FLAGS_gml_file;
|
||||
return kProblem;
|
||||
}
|
||||
ExportToGraphFile(model_proto, gml_file, GraphExporter::GML_FORMAT);
|
||||
gml_file->Close();
|
||||
gml_file->Close(file::Defaults()).IgnoreError();
|
||||
}
|
||||
return kOk;
|
||||
}
|
||||
|
||||
@@ -118,7 +118,8 @@ class NetworkRoutingData {
|
||||
// Returns the capacity of an arc, and 0 if the arc is not defined.
|
||||
int Capacity(int node1, int node2) const {
|
||||
return FindWithDefault(
|
||||
all_arcs_, std::make_pair(std::min(node1, node2), std::max(node1, node2)), 0);
|
||||
all_arcs_,
|
||||
std::make_pair(std::min(node1, node2), std::max(node1, node2)), 0);
|
||||
}
|
||||
|
||||
// Returns the demand between the source and the destination, and 0 if
|
||||
@@ -131,7 +132,8 @@ class NetworkRoutingData {
|
||||
// External building API.
|
||||
void set_num_nodes(int num_nodes) { num_nodes_ = num_nodes; }
|
||||
void AddArc(int node1, int node2, int capacity) {
|
||||
all_arcs_[std::make_pair(std::min(node1, node2), std::max(node1, node2))] = capacity;
|
||||
all_arcs_[std::make_pair(std::min(node1, node2), std::max(node1, node2))] =
|
||||
capacity;
|
||||
}
|
||||
void AddDemand(int source, int destination, int traffic) {
|
||||
all_demands_[std::make_pair(source, destination)] = traffic;
|
||||
@@ -476,11 +478,9 @@ class NetworkRoutingSolver {
|
||||
for (int demand_index = 0; demand_index < num_demands; ++demand_index) {
|
||||
paths.clear();
|
||||
const Demand& demand = demands_array_[demand_index];
|
||||
ResultCallback2<int64, int, int>* const graph_callback =
|
||||
NewPermanentCallback(this, &NetworkRoutingSolver::HasArc);
|
||||
CHECK(DijkstraShortestPath(num_nodes_, demand.source, demand.destination,
|
||||
graph_callback, kDisconnectedDistance,
|
||||
&paths));
|
||||
[this](int x, int y) { return HasArc(x, y); },
|
||||
kDisconnectedDistance, &paths));
|
||||
all_min_path_lengths_.push_back(paths.size() - 1);
|
||||
}
|
||||
|
||||
@@ -801,12 +801,13 @@ class NetworkRoutingSolver {
|
||||
}
|
||||
|
||||
// DecisionBuilder.
|
||||
Solver::IndexEvaluator2 value_evaluator =
|
||||
[this, &usage_costs](int64 var, int64 value) {
|
||||
Solver::IndexEvaluator2 eval_marginal_cost = [this, &usage_costs](
|
||||
int64 var, int64 value) {
|
||||
return EvaluateMarginalCost(usage_costs, var, value);
|
||||
};
|
||||
DecisionBuilder* const db =
|
||||
solver.MakePhase(decision_vars, Solver::CHOOSE_RANDOM, value_evaluator);
|
||||
|
||||
DecisionBuilder* const db = solver.MakePhase(
|
||||
decision_vars, Solver::CHOOSE_RANDOM, eval_marginal_cost);
|
||||
|
||||
// Limits.
|
||||
if (time_limit != 0 || fail_limit != 0) {
|
||||
@@ -833,12 +834,8 @@ class NetworkRoutingSolver {
|
||||
actual_usage_costs));
|
||||
SearchLimit* const lns_limit =
|
||||
solver.MakeLimit(kint64max, kint64max, FLAGS_lns_limit, kint64max);
|
||||
Solver::IndexEvaluator2 marginal_evaluator =
|
||||
[this, &usage_costs](int64 var, int64 value) {
|
||||
return EvaluateMarginalCost(usage_costs, var, value);
|
||||
};
|
||||
DecisionBuilder* const inner_db = solver.MakePhase(
|
||||
decision_vars, Solver::CHOOSE_RANDOM, marginal_evaluator);
|
||||
decision_vars, Solver::CHOOSE_RANDOM, eval_marginal_cost);
|
||||
|
||||
DecisionBuilder* const apply = solver.RevAlloc(new ApplyMaxDiscrepancy);
|
||||
DecisionBuilder* const max_discrepency_db = solver.Compose(apply, inner_db);
|
||||
|
||||
@@ -36,7 +36,7 @@ DEFINE_int32(
|
||||
size, 0,
|
||||
"Size of the problem. If equal to 0, will test several increasing sizes.");
|
||||
DEFINE_bool(use_symmetry, false, "Use Symmetry Breaking methods");
|
||||
DECLARE_bool(cp_no_solve);
|
||||
DECLARE_bool(cp_disable_solve);
|
||||
|
||||
static const int kNumSolutions[] = {
|
||||
1, 0, 0, 2, 10, 4, 40, 92, 352, 724, 2680, 14200, 73712, 365596, 2279184};
|
||||
@@ -174,13 +174,13 @@ void CheckNumberOfSolutions(int size, int num_solutions) {
|
||||
if (FLAGS_use_symmetry) {
|
||||
if (size - 1 < kKnownUniqueSolutions) {
|
||||
CHECK_EQ(num_solutions, kNumUniqueSolutions[size - 1]);
|
||||
} else if (!FLAGS_cp_no_solve) {
|
||||
} else if (!FLAGS_cp_disable_solve) {
|
||||
CHECK_GT(num_solutions, 0);
|
||||
}
|
||||
} else {
|
||||
if (size - 1 < kKnownSolutions) {
|
||||
CHECK_EQ(num_solutions, kNumSolutions[size - 1]);
|
||||
} else if (!FLAGS_cp_no_solve) {
|
||||
} else if (!FLAGS_cp_disable_solve) {
|
||||
CHECK_GT(num_solutions, 0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
// http://en.wikipedia.org/wiki/Vehicle_routing_problem
|
||||
// http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.123.9965&rep=rep1&type=pdf.
|
||||
// Reads data in the format defined by Li & Lim
|
||||
// (http://www.sintef.no/Projectweb/TOP/PDPTW/Li--Lim-benchmark/Documentation/).
|
||||
// (https://www.sintef.no/projectweb/top/pdptw/li-lim-benchmark/documentation/).
|
||||
|
||||
#include <vector>
|
||||
|
||||
@@ -46,14 +46,13 @@
|
||||
#include "base/split.h"
|
||||
#include "base/mathutil.h"
|
||||
#include "constraint_solver/routing.h"
|
||||
#include "constraint_solver/routing_flags.h"
|
||||
|
||||
DECLARE_bool(routing_no_lns);
|
||||
DEFINE_string(pdp_file, "",
|
||||
"File containing the Pickup and Delivery Problem to solve.");
|
||||
DEFINE_int32(pdp_force_vehicles, 0,
|
||||
"Force the number of vehicles used (maximum number of routes.");
|
||||
DEFINE_bool(pdp_display_solution, false,
|
||||
"Displays the solution of the Pickup and Delivery Problem.");
|
||||
DECLARE_string(routing_first_solution);
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
@@ -165,7 +164,7 @@ bool SafeParseInt64Array(const std::string& str, std::vector<int64>* parsed_int)
|
||||
} // namespace
|
||||
|
||||
// Builds and solves a model from a file in the format defined by Li & Lim
|
||||
// (http://www.sintef.no/static/am/opti/projects/top/vrp/format_pdp.htm).
|
||||
// (https://www.sintef.no/projectweb/top/pdptw/li-lim-benchmark/documentation/).
|
||||
bool LoadAndSolve(const std::string& pdp_file) {
|
||||
// Load all the lines of the file in RAM (it shouldn't be too large anyway).
|
||||
std::vector<std::string> lines;
|
||||
@@ -279,11 +278,15 @@ bool LoadAndSolve(const std::string& pdp_file) {
|
||||
}
|
||||
|
||||
// Set up search parameters.
|
||||
routing.set_first_solution_strategy(RoutingModel::ROUTING_ALL_UNPERFORMED);
|
||||
FLAGS_routing_no_lns = true;
|
||||
RoutingSearchParameters parameters = BuildSearchParametersFromFlags();
|
||||
if (FLAGS_routing_first_solution.empty()) {
|
||||
parameters.set_first_solution_strategy(
|
||||
operations_research::FirstSolutionStrategy::ALL_UNPERFORMED);
|
||||
}
|
||||
parameters.mutable_local_search_operators()->set_use_path_lns(false);
|
||||
|
||||
// Solve pickup and delivery problem.
|
||||
const Assignment* assignment = routing.Solve(NULL);
|
||||
const Assignment* assignment = routing.SolveWithParameters(parameters);
|
||||
if (NULL != assignment) {
|
||||
LOG(INFO) << "Cost: " << assignment->ObjectiveValue();
|
||||
LOG(INFO) << VerboseOutput(routing, *assignment, coords, service_times);
|
||||
|
||||
@@ -27,11 +27,13 @@
|
||||
#include "base/strutil.h"
|
||||
#include "algorithms/sparse_permutation.h"
|
||||
#include "sat/boolean_problem.h"
|
||||
#include "sat/drat.h"
|
||||
#include "cpp/opb_reader.h"
|
||||
#include "sat/optimization.h"
|
||||
#include "cpp/sat_cnf_reader.h"
|
||||
#include "sat/sat_solver.h"
|
||||
#include "sat/simplification.h"
|
||||
#include "sat/symmetry.h"
|
||||
#include "util/time_limit.h"
|
||||
#include "base/random.h"
|
||||
#include "base/status.h"
|
||||
@@ -100,11 +102,6 @@ DEFINE_bool(presolve, false,
|
||||
DEFINE_bool(probing, false, "If true, presolve the problem using probing.");
|
||||
|
||||
|
||||
DEFINE_bool(refine_core, false,
|
||||
"If true, turn on the unsat_proof parameters and if the problem is "
|
||||
"UNSAT, refine as much as possible its UNSAT core in order to get "
|
||||
"a small one.");
|
||||
|
||||
DEFINE_bool(reduce_memory_usage, false,
|
||||
"If true, do not keep a copy of the original problem in memory."
|
||||
"This reduce the memory usage, but disable the solution cheking at "
|
||||
@@ -152,8 +149,8 @@ void LoadBooleanProblem(std::string filename, LinearBooleanProblem* problem) {
|
||||
std::string SolutionString(const LinearBooleanProblem& problem,
|
||||
const std::vector<bool>& assignment) {
|
||||
std::string output;
|
||||
VariableIndex limit(problem.original_num_variables());
|
||||
for (VariableIndex index(0); index < limit; ++index) {
|
||||
BooleanVariable limit(problem.original_num_variables());
|
||||
for (BooleanVariable index(0); index < limit; ++index) {
|
||||
if (index > 0) output += " ";
|
||||
output += StringPrintf(
|
||||
"%d", Literal(index, assignment[index.value()]).SignedValue());
|
||||
@@ -178,16 +175,17 @@ int Run() {
|
||||
<< FLAGS_params;
|
||||
}
|
||||
|
||||
// Enforce some parameters if we are looking for UNSAT core.
|
||||
if (FLAGS_refine_core) {
|
||||
parameters.set_unsat_proof(true);
|
||||
parameters.set_treat_binary_clauses_separately(false);
|
||||
}
|
||||
Model model;
|
||||
DratWriter* drat_writer = model.GetOrCreate<DratWriter>();
|
||||
|
||||
// Initialize the solver.
|
||||
std::unique_ptr<SatSolver> solver(new SatSolver());
|
||||
solver->SetDratWriter(drat_writer);
|
||||
solver->SetParameters(parameters);
|
||||
|
||||
// The global time limit.
|
||||
std::unique_ptr<TimeLimit> time_limit(TimeLimit::FromParameters(parameters));
|
||||
|
||||
// Read the problem.
|
||||
LinearBooleanProblem problem;
|
||||
LoadBooleanProblem(FLAGS_input, &problem);
|
||||
@@ -232,13 +230,21 @@ int Run() {
|
||||
LOG(INFO) << "UNSAT when setting the objective constraint.";
|
||||
}
|
||||
|
||||
if (drat_writer != nullptr) {
|
||||
drat_writer->SetNumVariables(solver->NumVariables());
|
||||
}
|
||||
|
||||
// Symmetries!
|
||||
if (FLAGS_use_symmetry) {
|
||||
CHECK(!FLAGS_reduce_memory_usage) << "incompatible";
|
||||
LOG(INFO) << "Finding symmetries of the problem.";
|
||||
std::vector<std::unique_ptr<SparsePermutation>> generators;
|
||||
FindLinearBooleanProblemSymmetries(problem, &generators);
|
||||
solver->AddSymmetries(&generators);
|
||||
std::unique_ptr<SymmetryPropagator> propagator(new SymmetryPropagator);
|
||||
for (int i = 0; i < generators.size(); ++i) {
|
||||
propagator->AddSymmetry(std::move(generators[i]));
|
||||
}
|
||||
solver->AddPropagator(std::move(propagator));
|
||||
}
|
||||
|
||||
// Optimize?
|
||||
@@ -279,17 +285,54 @@ int Run() {
|
||||
if (FLAGS_presolve) {
|
||||
SatPostsolver postsolver(problem.num_variables());
|
||||
|
||||
// Some problems are formulated in such a way that our SAT heuristics
|
||||
// simply works without conflict. Get them out of the way first because it
|
||||
// is possible that the presolve lose this "lucky" ordering. This is in
|
||||
// particular the case on the SAT14.crafted.complete-xxx-... problems.
|
||||
//
|
||||
// TODO(user): Use the SolveWithRandomParameters() instead, it sounds
|
||||
// like a better idea to try a bunch of different heuristic rather than
|
||||
// just the default one.
|
||||
result = SatSolver::LIMIT_REACHED;
|
||||
{
|
||||
MTRandom random("A random seed.");
|
||||
SatParameters new_params = parameters;
|
||||
new_params.set_log_search_progress(false);
|
||||
new_params.set_max_number_of_conflicts(1);
|
||||
const int num_times = 1000;
|
||||
for (int i = 0; i < num_times && !time_limit->LimitReached(); ++i) {
|
||||
solver->SetParameters(new_params);
|
||||
result = solver->SolveWithTimeLimit(time_limit.get());
|
||||
if (result != SatSolver::LIMIT_REACHED) {
|
||||
LOG(INFO) << "Problem solved by trivial heuristic!";
|
||||
break;
|
||||
}
|
||||
|
||||
// We randomize at the end so that the default params is executed
|
||||
// at least once.
|
||||
solver->RestoreSolverToAssumptionLevel();
|
||||
RandomizeDecisionHeuristic(&random, &new_params);
|
||||
new_params.set_random_seed(i);
|
||||
solver->SetParameters(new_params);
|
||||
solver->ResetDecisionHeuristic();
|
||||
}
|
||||
|
||||
// Restore the initial parameters.
|
||||
solver->SetParameters(parameters);
|
||||
solver->ResetDecisionHeuristic();
|
||||
}
|
||||
|
||||
// We use a new block so the memory used by the presolver can be
|
||||
// reclaimed as soon as it is no longer needed.
|
||||
//
|
||||
// TODO(user): Automatically adapt the number of iterations.
|
||||
result = SatSolver::MODEL_SAT;
|
||||
for (int i = 0; i < 4; ++i) {
|
||||
const int max_num_passes = result == SatSolver::LIMIT_REACHED ? 4 : 0;
|
||||
for (int i = 0; i < max_num_passes && !time_limit->LimitReached(); ++i) {
|
||||
const int saved_num_variables = solver->NumVariables();
|
||||
|
||||
// Probe + find equivalent literals.
|
||||
// TODO(user): Use a derived time limit in the probing phase.
|
||||
ITIVector<LiteralIndex, LiteralIndex> equiv_map;
|
||||
ProbeAndFindEquivalentLiteral(solver.get(), &postsolver, &equiv_map);
|
||||
ProbeAndFindEquivalentLiteral(solver.get(), &postsolver, drat_writer,
|
||||
&equiv_map);
|
||||
if (solver->IsModelUnsat()) {
|
||||
printf("c unsat during probing!\n");
|
||||
result = SatSolver::MODEL_UNSAT;
|
||||
@@ -303,8 +346,10 @@ int Run() {
|
||||
postsolver.FixVariable(solver->LiteralTrail()[i]);
|
||||
}
|
||||
|
||||
// TODO(user): Pass the time_limit to the presolver.
|
||||
SatPresolver presolver(&postsolver);
|
||||
presolver.SetParameters(parameters);
|
||||
presolver.SetDratWriter(drat_writer);
|
||||
presolver.SetEquivalentLiteralMapping(equiv_map);
|
||||
solver->ExtractClauses(&presolver);
|
||||
solver.release();
|
||||
@@ -317,19 +362,24 @@ int Run() {
|
||||
break;
|
||||
}
|
||||
|
||||
postsolver.ApplyMapping(presolver.VariableMapping());
|
||||
if (drat_writer != nullptr) {
|
||||
drat_writer->ApplyMapping(presolver.VariableMapping());
|
||||
}
|
||||
|
||||
// Load the presolved problem in a new solver.
|
||||
solver.reset(new SatSolver());
|
||||
solver->SetDratWriter(drat_writer);
|
||||
solver->SetParameters(parameters);
|
||||
presolver.LoadProblemIntoSatSolver(solver.get());
|
||||
postsolver.ApplyMapping(presolver.VariableMapping());
|
||||
|
||||
// Stop if a fixed point has been reached.
|
||||
if (solver->NumVariables() == saved_num_variables) break;
|
||||
}
|
||||
|
||||
// Solve.
|
||||
if (result != SatSolver::MODEL_UNSAT) {
|
||||
result = solver->Solve();
|
||||
if (result == SatSolver::LIMIT_REACHED) {
|
||||
result = solver->SolveWithTimeLimit(time_limit.get());
|
||||
}
|
||||
|
||||
// Recover the solution.
|
||||
@@ -343,40 +393,6 @@ int Run() {
|
||||
ExtractAssignment(problem, *solver, &solution);
|
||||
CHECK(IsAssignmentValid(problem, solution));
|
||||
}
|
||||
|
||||
// Unsat with verification.
|
||||
// Note(user): For now we just compute an UNSAT core and check it.
|
||||
if (result == SatSolver::MODEL_UNSAT && parameters.unsat_proof()) {
|
||||
CHECK(!FLAGS_reduce_memory_usage) << "incompatible";
|
||||
std::vector<int> core;
|
||||
solver->ComputeUnsatCore(&core);
|
||||
LOG(INFO) << "UNSAT. Identified a core of " << core.size()
|
||||
<< " constraints.";
|
||||
|
||||
// The following block is mainly for testing the UNSAT core feature.
|
||||
if (FLAGS_refine_core) {
|
||||
int old_core_size = core.size();
|
||||
LinearBooleanProblem old_problem;
|
||||
LinearBooleanProblem core_unsat_problem;
|
||||
old_problem = problem;
|
||||
int i = 1;
|
||||
do {
|
||||
ExtractSubproblem(old_problem, core, &core_unsat_problem);
|
||||
core_unsat_problem.set_name(StringPrintf("Subproblem #%d", i));
|
||||
old_core_size = core.size();
|
||||
old_problem = core_unsat_problem;
|
||||
SatSolver new_solver;
|
||||
new_solver.SetParameters(parameters);
|
||||
CHECK(LoadBooleanProblem(core_unsat_problem, &new_solver));
|
||||
CHECK_EQ(new_solver.Solve(), SatSolver::MODEL_UNSAT)
|
||||
<< "Wrong core!";
|
||||
new_solver.ComputeUnsatCore(&core);
|
||||
LOG(INFO) << "Core #" << i << " checked, next size is "
|
||||
<< core.size();
|
||||
++i;
|
||||
} while (core.size() != old_core_size);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
418
examples/cpp/slitherlink.cc
Normal file
418
examples/cpp/slitherlink.cc
Normal file
@@ -0,0 +1,418 @@
|
||||
#include <deque>
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
#include "constraint_solver/constraint_solver.h"
|
||||
#include "constraint_solver/constraint_solveri.h"
|
||||
#include "util/string_array.h"
|
||||
|
||||
const std::vector<std::vector<int>> small = {
|
||||
{ 3, 2, -1, 3 },
|
||||
{ -1, -1, -1, 2 },
|
||||
{ 3, -1, -1, -1 },
|
||||
{ 3, -1, 3, 1 }
|
||||
};
|
||||
|
||||
const std::vector<std::vector<int>> medium = {
|
||||
{ -1, 0, -1, 1, -1, -1, 1, -1 },
|
||||
{ -1, 3, -1, -1, 2, 3, -1, 2 },
|
||||
{ -1, -1, 0, -1, -1, -1, -1, 0 },
|
||||
{ -1, 3, -1, -1, 0, -1, -1, -1 },
|
||||
{ -1, -1, -1, 3, -1, -1, 0, -1 },
|
||||
{ 1, -1, -1, -1, -1, 3, -1, -1 },
|
||||
{ 3, -1, 1, 3, -1, -1, 3, -1 },
|
||||
{ -1, 0, -1, -1, 3, -1, 3, -1 }
|
||||
};
|
||||
|
||||
const std::vector<std::vector<int>> big = {
|
||||
{ 3, -1, -1, -1, 2, -1, 1, -1, 1, 2 },
|
||||
{ 1, -1, 0, -1, 3, -1, 2, 0, -1, -1 },
|
||||
{ -1, 3, -1, -1, -1, -1, -1, -1, 3, -1 },
|
||||
{ 2, 0, -1, 3, -1, 2, 3, -1, -1, -1 },
|
||||
{ -1, -1, -1, 1, 1, 1, -1, -1, 3, 3 },
|
||||
{ 2, 3, -1, -1, 2, 2, 3, -1, -1, -1 },
|
||||
{ -1, -1, -1, 1, 2, -1, 2, -1, 3, 3 },
|
||||
{ -1, 2, -1, -1, -1, -1, -1, -1, 2, -1 },
|
||||
{ -1, -1, 1, 1, -1, 2, -1, 1, -1, 3 },
|
||||
{ 3, 3, -1, 1, -1, 2, -1, -1, -1, 2 }
|
||||
};
|
||||
|
||||
namespace operations_research {
|
||||
namespace {
|
||||
std::vector<IntVar*> NeighboringArcs(
|
||||
int i, int j,
|
||||
const std::vector<std::vector<IntVar*>>& h_arcs,
|
||||
const std::vector<std::vector<IntVar*>>& v_arcs) {
|
||||
std::vector<IntVar*> tmp;
|
||||
if (j > 0) {
|
||||
tmp.push_back(h_arcs[i][j - 1]);
|
||||
}
|
||||
if (j < v_arcs.size() - 1) {
|
||||
tmp.push_back(h_arcs[i][j]);
|
||||
}
|
||||
if (i > 0) {
|
||||
tmp.push_back(v_arcs[j][i - 1]);
|
||||
}
|
||||
if (i < h_arcs.size() - 1) {
|
||||
tmp.push_back(v_arcs[j][i]);
|
||||
}
|
||||
return tmp;
|
||||
}
|
||||
|
||||
// Dedicated constraint: Sum(boolvars) is even.
|
||||
class BooleanSumEven : public Constraint {
|
||||
public:
|
||||
BooleanSumEven(Solver* const s, const std::vector<IntVar*>& vars)
|
||||
: Constraint(s),
|
||||
vars_(vars),
|
||||
num_possible_true_vars_(0),
|
||||
num_always_true_vars_(0) {}
|
||||
|
||||
virtual ~BooleanSumEven() {}
|
||||
|
||||
virtual void Post() {
|
||||
for (int i = 0; i < vars_.size(); ++i) {
|
||||
if (!vars_[i]->Bound()) {
|
||||
Demon* const u = MakeConstraintDemon1(
|
||||
solver(), this, &BooleanSumEven::Update, "Update", i);
|
||||
vars_[i]->WhenBound(u);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual void InitialPropagate() {
|
||||
int num_always_true = 0;
|
||||
int num_possible_true = 0;
|
||||
int possible_true_index = -1;
|
||||
for (int i = 0; i < vars_.size(); ++i) {
|
||||
const IntVar* const var = vars_[i];
|
||||
if (var->Min() == 1) {
|
||||
num_always_true++;
|
||||
num_possible_true++;
|
||||
} else if (var->Max() == 1) {
|
||||
num_possible_true++;
|
||||
possible_true_index = i;
|
||||
}
|
||||
}
|
||||
if (num_always_true == num_possible_true && num_possible_true % 2 == 1) {
|
||||
solver()->Fail();
|
||||
} else if (num_possible_true == num_always_true + 1) {
|
||||
DCHECK_NE(-1, possible_true_index);
|
||||
vars_[possible_true_index]->SetValue(num_always_true % 2);
|
||||
}
|
||||
num_possible_true_vars_.SetValue(solver(), num_possible_true);
|
||||
num_always_true_vars_.SetValue(solver(), num_always_true);
|
||||
}
|
||||
|
||||
void Update(int index) {
|
||||
DCHECK(vars_[index]->Bound());
|
||||
const int64 value = vars_[index]->Min(); // Faster than Value().
|
||||
if (value == 0) {
|
||||
num_possible_true_vars_.Decr(solver());
|
||||
} else {
|
||||
DCHECK_EQ(1, value);
|
||||
num_always_true_vars_.Incr(solver());
|
||||
}
|
||||
if (num_always_true_vars_.Value() == num_possible_true_vars_.Value() &&
|
||||
num_possible_true_vars_.Value() % 2 == 1) {
|
||||
solver()->Fail();
|
||||
} else if (num_possible_true_vars_.Value() ==
|
||||
num_always_true_vars_.Value() + 1) {
|
||||
int possible_true_index = -1;
|
||||
for (int i = 0; i < vars_.size(); ++i) {
|
||||
if (!vars_[i]->Bound()) {
|
||||
possible_true_index = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (possible_true_index != -1) {
|
||||
if (num_possible_true_vars_.Value() % 2 == 0) {
|
||||
vars_[possible_true_index]->SetMin(1);
|
||||
} else {
|
||||
vars_[possible_true_index]->SetMax(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::string DebugString() const {
|
||||
return StringPrintf("BooleanSumEven([%s])",
|
||||
JoinDebugStringPtr(vars_, ", ").c_str());
|
||||
}
|
||||
|
||||
virtual void Accept(ModelVisitor* const visitor) const {
|
||||
visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
|
||||
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
||||
vars_);
|
||||
visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<IntVar*> vars_;
|
||||
NumericalRev<int> num_possible_true_vars_;
|
||||
NumericalRev<int> num_always_true_vars_;
|
||||
};
|
||||
|
||||
Constraint* MakeBooleanSumEven(Solver* const s, const std::vector<IntVar*>& v) {
|
||||
return s->RevAlloc(new BooleanSumEven(s, v));
|
||||
}
|
||||
|
||||
// Dedicated constraint: There is a single path on the grid.
|
||||
// This constraint does not enforce the non-crossing, this is done
|
||||
// by the constraint on the degree of each node.
|
||||
class GridSinglePath : public Constraint {
|
||||
public:
|
||||
GridSinglePath(Solver* const solver,
|
||||
const std::vector<std::vector<IntVar*>>& h_arcs,
|
||||
const std::vector<std::vector<IntVar*>>& v_arcs)
|
||||
: Constraint(solver),
|
||||
h_arcs_(h_arcs),
|
||||
v_arcs_(v_arcs) {}
|
||||
|
||||
~GridSinglePath() {}
|
||||
|
||||
void Post() override {
|
||||
Demon* const demon =
|
||||
solver()->MakeDelayedConstraintInitialPropagateCallback(this);
|
||||
for (auto& row : h_arcs_) {
|
||||
for (IntVar* const var : row) {
|
||||
var->WhenBound(demon);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto& column : v_arcs_) {
|
||||
for (IntVar* const var : column) {
|
||||
var->WhenBound(demon);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// This constraint implements a single propagation.
|
||||
// If one point is on the path, it checks the reachability of all possible
|
||||
// nodes, and zero out the unreachable parts.
|
||||
void InitialPropagate() override {
|
||||
const int num_rows = h_arcs_.size(); // number of points
|
||||
const int num_columns = v_arcs_.size(); // number of points
|
||||
|
||||
const int num_points = num_rows * num_columns;
|
||||
int root_node = -1;
|
||||
std::unordered_set<int> possible_points;
|
||||
std::vector<std::vector<int>> neighbors(num_points);
|
||||
for (int i = 0; i < num_rows; ++i) {
|
||||
for (int j = 0; j < num_columns - 1; ++j) {
|
||||
IntVar* const h_arc = h_arcs_[i][j];
|
||||
if (h_arc->Max() == 1) {
|
||||
const int head = i * num_columns + j;
|
||||
const int tail = i * num_columns + j + 1;
|
||||
neighbors[head].push_back(tail);
|
||||
neighbors[tail].push_back(head);
|
||||
possible_points.insert(head);
|
||||
possible_points.insert(tail);
|
||||
if (root_node == -1 && h_arc->Min() == 1) {
|
||||
root_node = head;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i = 0; i < num_rows - 1; ++i) {
|
||||
for (int j = 0; j < num_columns; ++j) {
|
||||
IntVar* const v_arc = v_arcs_[j][i];
|
||||
if (v_arc->Max() == 1) {
|
||||
const int head = i * num_columns + j;
|
||||
const int tail = (i + 1) * num_columns + j;
|
||||
neighbors[head].push_back(tail);
|
||||
neighbors[tail].push_back(head);
|
||||
possible_points.insert(head);
|
||||
possible_points.insert(tail);
|
||||
if (root_node == -1 && v_arc->Min() == 1) {
|
||||
root_node = head;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (root_node == -1) {
|
||||
return;
|
||||
}
|
||||
std::unordered_set<int> visited_points;
|
||||
std::deque<int> to_process;
|
||||
to_process.push_back(root_node);
|
||||
while (!to_process.empty()) {
|
||||
const int candidate = to_process.front();
|
||||
to_process.pop_front();
|
||||
visited_points.insert(candidate);
|
||||
for (int neighbor : neighbors[candidate]) {
|
||||
if (!ContainsKey(visited_points, neighbor)) {
|
||||
to_process.push_back(neighbor);
|
||||
visited_points.insert(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (visited_points.size() < possible_points.size()) {
|
||||
for (const int point: visited_points) {
|
||||
possible_points.erase(point);
|
||||
}
|
||||
// Loop on unreachable points and zero all neighboring arcs.
|
||||
for (const int point : possible_points) {
|
||||
const int i = point / num_columns;
|
||||
const int j = point % num_columns;
|
||||
const std::vector<IntVar*> neighbors =
|
||||
NeighboringArcs(i, j, h_arcs_, v_arcs_);
|
||||
for (IntVar* const var : neighbors) {
|
||||
var->SetMax(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<std::vector<IntVar*>> h_arcs_;
|
||||
const std::vector<std::vector<IntVar*>> v_arcs_;
|
||||
};
|
||||
|
||||
Constraint* MakeSingleLoop(Solver* const solver,
|
||||
const std::vector<std::vector<IntVar*>>& h_arcs,
|
||||
const std::vector<std::vector<IntVar*>>& v_arcs) {
|
||||
return solver->RevAlloc(new GridSinglePath(solver, h_arcs, v_arcs));
|
||||
}
|
||||
|
||||
void PrintSolution(const std::vector<std::vector<int>>& data,
|
||||
const std::vector<std::vector<IntVar*>>& h_arcs,
|
||||
const std::vector<std::vector<IntVar*>>& v_arcs) {
|
||||
const int num_rows = data.size();
|
||||
const int num_columns = data[0].size();
|
||||
|
||||
for (int i = 0; i < num_rows; ++i) {
|
||||
std::string first_line;
|
||||
std::string second_line;
|
||||
std::string third_line;
|
||||
for (int j = 0; j < num_columns; ++j) {
|
||||
const int h_arc = h_arcs[i][j]->Value();
|
||||
const int v_arc = v_arcs[j][i]->Value();
|
||||
const int sum = data[i][j];
|
||||
first_line += h_arc == 1 ? " ---" : " ";
|
||||
second_line += v_arc == 1 ? "|" : " ";
|
||||
second_line += sum == -1 ? " " : StringPrintf(" %d ", sum).c_str();
|
||||
third_line += v_arc == 1 ? "| " : " ";
|
||||
}
|
||||
const int termination = v_arcs[num_columns][i]->Value();
|
||||
second_line += termination == 1 ? "|" : " ";
|
||||
third_line += termination == 1 ? "|" : " ";
|
||||
std::cout << first_line << std::endl;
|
||||
std::cout << third_line << std::endl;
|
||||
std::cout << second_line << std::endl;
|
||||
std::cout << third_line << std::endl;
|
||||
}
|
||||
std::string last_line;
|
||||
for (int j = 0; j < num_columns; ++j) {
|
||||
const int h_arc = h_arcs[num_rows][j]->Value();
|
||||
last_line += h_arc == 1 ? " ---" : " ";
|
||||
}
|
||||
std::cout << last_line << std::endl;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void SlitherLink(const std::vector<std::vector<int>>& data) {
|
||||
const int num_rows = data.size();
|
||||
const int num_columns = data[0].size();
|
||||
|
||||
Solver solver("slitherlink");
|
||||
std::vector<IntVar*> all_vars;
|
||||
std::vector<std::vector<IntVar*>> h_arcs(num_rows + 1);
|
||||
for (int i = 0; i < num_rows + 1; ++i) {
|
||||
solver.MakeBoolVarArray(num_columns, StringPrintf("h_arc_%i_", i),
|
||||
&h_arcs[i]);
|
||||
all_vars.insert(all_vars.end(), h_arcs[i].begin(), h_arcs[i].end());
|
||||
}
|
||||
|
||||
std::vector<std::vector<IntVar*>> v_arcs(num_columns + 1);
|
||||
for (int i = 0; i < num_columns + 1; ++i) {
|
||||
solver.MakeBoolVarArray(num_rows, StringPrintf("v_arc_%i_", i), &v_arcs[i]);
|
||||
all_vars.insert(all_vars.end(), v_arcs[i].begin(), v_arcs[i].end());
|
||||
}
|
||||
|
||||
// Constraint on the sum of arcs.
|
||||
for (int i = 0; i < num_rows; ++i) {
|
||||
for (int j = 0; j < num_columns; ++j) {
|
||||
const int value = data[i][j];
|
||||
if (value != -1) {
|
||||
std::vector<IntVar*> square = { h_arcs[i][j], h_arcs[i + 1][j],
|
||||
v_arcs[j][i], v_arcs[j + 1][i] };
|
||||
solver.AddConstraint(solver.MakeSumEquality(square, value));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Single loop: each node has a degree 0 or 2.
|
||||
const std::vector<int> zero_or_two = { 0, 2 };
|
||||
for (int i = 0; i < num_rows + 1; ++i) {
|
||||
for (int j = 0; j < num_columns + 1; ++j) {
|
||||
const std::vector<IntVar*> neighbors =
|
||||
NeighboringArcs(i, j, h_arcs, v_arcs);
|
||||
solver.AddConstraint(
|
||||
solver.MakeMemberCt(solver.MakeSum(neighbors), zero_or_two));
|
||||
}
|
||||
}
|
||||
|
||||
// Single loop: sum of arc on row or column is even
|
||||
for (int i = 0; i < num_columns; ++i) {
|
||||
std::vector<IntVar*> column;
|
||||
for (int j = 0; j < num_rows + 1; ++j) {
|
||||
column.push_back(h_arcs[j][i]);
|
||||
}
|
||||
solver.AddConstraint(MakeBooleanSumEven(&solver, column));
|
||||
}
|
||||
for (int i = 0; i < num_rows; ++i) {
|
||||
std::vector<IntVar*> row;
|
||||
for (int j = 0; j < num_columns + 1; ++j) {
|
||||
row.push_back(v_arcs[j][i]);
|
||||
}
|
||||
solver.AddConstraint(MakeBooleanSumEven(&solver, row));
|
||||
}
|
||||
|
||||
// Hamiltonian path: add single path constraint.
|
||||
solver.AddConstraint(MakeSingleLoop(&solver, h_arcs, v_arcs));
|
||||
|
||||
// Special rule on corners: value == 3 implies 2 border arcs used.
|
||||
if (data[0][0] == 3) {
|
||||
h_arcs[0][0]->SetMin(1);
|
||||
v_arcs[0][0]->SetMin(1);
|
||||
}
|
||||
if (data[0][num_columns - 1] == 3) {
|
||||
h_arcs[0][num_columns - 1]->SetMin(1);
|
||||
v_arcs[num_columns][0]->SetMin(1);
|
||||
}
|
||||
if (data[num_rows - 1][0] == 3) {
|
||||
h_arcs[num_rows][0]->SetMin(1);
|
||||
v_arcs[0][num_rows - 1]->SetMin(1);
|
||||
}
|
||||
if (data[num_rows - 1][num_columns - 1] == 3) {
|
||||
h_arcs[num_rows][num_columns - 1]->SetMin(1);
|
||||
v_arcs[num_columns][num_rows - 1]->SetMin(1);
|
||||
}
|
||||
|
||||
// Search.
|
||||
DecisionBuilder* const db = solver.MakePhase(
|
||||
all_vars, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MAX_VALUE);
|
||||
|
||||
SearchMonitor* const log = solver.MakeSearchLog(1000000);
|
||||
|
||||
solver.NewSearch(db, log);
|
||||
while (solver.NextSolution()) {
|
||||
PrintSolution(data, h_arcs, v_arcs);
|
||||
}
|
||||
solver.EndSearch();
|
||||
}
|
||||
} // namespace operations_research
|
||||
|
||||
int main() {
|
||||
std::cout << "Small problem" << std::endl;
|
||||
operations_research::SlitherLink(small);
|
||||
std::cout << "Medium problem" << std::endl;
|
||||
operations_research::SlitherLink(medium);
|
||||
std::cout << "Big problem" << std::endl;
|
||||
operations_research::SlitherLink(big);
|
||||
return 0;
|
||||
}
|
||||
@@ -30,10 +30,11 @@
|
||||
|
||||
DEFINE_string(input, "", "REQUIRED: Input file name.");
|
||||
DEFINE_string(solver, "glop",
|
||||
"The solver to use: "
|
||||
"cbc, clp, cplex, cplex_mip, glop, glpk_lp, glpk_mip,"
|
||||
" gurobi_lp, gurobi_mip, scip.");
|
||||
DEFINE_string(params, "", "Solver specific parameters");
|
||||
"The solver to use: bop, cbc, clp, cplex, cplex_mip, glop, glpk_lp, "
|
||||
"glpk_mip, gurobi_lp, gurobi_mip, scip, knapsack.");
|
||||
DEFINE_string(params_file, "",
|
||||
"Solver specific parameters file. "
|
||||
"If this flag is set, the --params flag is ignored.");DEFINE_string(params, "", "Solver specific parameters");
|
||||
DEFINE_int64(time_limit_ms, 0,
|
||||
"If stricitly positive, specifies a limit in ms on the solving"
|
||||
" time.");
|
||||
@@ -58,7 +59,6 @@ static const char kUsageStr[] =
|
||||
namespace operations_research {
|
||||
namespace {
|
||||
|
||||
|
||||
void Run() {
|
||||
// Create the solver and set its parameters.
|
||||
MPSolver::OptimizationProblemType type;
|
||||
@@ -102,9 +102,6 @@ void Run() {
|
||||
#endif
|
||||
#if defined(USE_BOP)
|
||||
} else if (FLAGS_solver == "bop") {
|
||||
LOG(WARNING) << "This version of BOP needs a time limit to be set via "
|
||||
<< "the --params flag. Ex: "
|
||||
<< "--params=max_time_in_seconds:10,log_search_progress:true";
|
||||
type = MPSolver::BOP_INTEGER_PROGRAMMING;
|
||||
#endif
|
||||
} else {
|
||||
@@ -112,13 +109,20 @@ void Run() {
|
||||
}
|
||||
MPSolver solver("command line solver", type);
|
||||
solver.EnableOutput();
|
||||
if (!FLAGS_params.empty()) {
|
||||
if (!FLAGS_params_file.empty()) {
|
||||
std::string file_contents;
|
||||
CHECK_OK(
|
||||
file::GetContents(FLAGS_params_file, &file_contents, file::Defaults()))
|
||||
<< "Could not read parameters file.";
|
||||
CHECK(solver.SetSolverSpecificParametersAsString(file_contents));
|
||||
} else if (!FLAGS_params.empty()) {
|
||||
CHECK(solver.SetSolverSpecificParametersAsString(FLAGS_params))
|
||||
<< "Wrong --params format.";
|
||||
}
|
||||
printf("%-12s: %s\n", "Solver", MPModelRequest::SolverType_Name(
|
||||
static_cast<MPModelRequest::SolverType>(
|
||||
solver.ProblemType())).c_str());
|
||||
printf("%-12s: %s\n", "Solver",
|
||||
MPModelRequest::SolverType_Name(
|
||||
static_cast<MPModelRequest::SolverType>(solver.ProblemType()))
|
||||
.c_str());
|
||||
|
||||
// Load the problem into an MPModelProto.
|
||||
MPModelProto model_proto;
|
||||
@@ -130,9 +134,11 @@ void Run() {
|
||||
glop::MPSReader mps_reader;
|
||||
mps_reader.set_log_errors(FLAGS_forced_mps_format == "free" ||
|
||||
FLAGS_forced_mps_format == "fixed");
|
||||
const bool fixed_read = FLAGS_forced_mps_format != "free" &&
|
||||
const bool fixed_read =
|
||||
FLAGS_forced_mps_format != "free" &&
|
||||
mps_reader.LoadFileWithMode(FLAGS_input, false, &linear_program_fixed);
|
||||
const bool free_read = FLAGS_forced_mps_format != "fixed" &&
|
||||
const bool free_read =
|
||||
FLAGS_forced_mps_format != "fixed" &&
|
||||
mps_reader.LoadFileWithMode(FLAGS_input, true, &linear_program_free);
|
||||
CHECK(fixed_read || free_read)
|
||||
<< "Error while parsing the mps file '" << FLAGS_input << "' "
|
||||
@@ -183,17 +189,21 @@ void Run() {
|
||||
printf("%-12s: %d x %d\n", "Dimension", solver.NumConstraints(),
|
||||
solver.NumVariables());
|
||||
|
||||
|
||||
// Solve.
|
||||
MPSolverParameters param;
|
||||
MPSolver::ResultStatus solve_status;
|
||||
MPSolver::ResultStatus solve_status = MPSolver::NOT_SOLVED;
|
||||
double solving_time_in_sec = 0;
|
||||
{
|
||||
ScopedWallTime timer(&solving_time_in_sec);
|
||||
solve_status = solver.Solve(param);
|
||||
}
|
||||
|
||||
const bool has_solution =
|
||||
solve_status == MPSolver::OPTIMAL || solve_status == MPSolver::FEASIBLE;
|
||||
|
||||
// If requested, get the solver result and output it.
|
||||
if (!FLAGS_output.empty()) {
|
||||
if (!FLAGS_output.empty() && has_solution) {
|
||||
operations_research::MPSolutionResponse result;
|
||||
solver.FillSolutionResponseProto(&result);
|
||||
if (HasSuffixString(FLAGS_output, ".txt")) {
|
||||
@@ -202,7 +212,7 @@ void Run() {
|
||||
CHECK_OK(file::SetBinaryProto(FLAGS_output, result, file::Defaults()));
|
||||
}
|
||||
}
|
||||
if (!FLAGS_output_csv.empty()) {
|
||||
if (!FLAGS_output_csv.empty() && has_solution) {
|
||||
operations_research::MPSolutionResponse result;
|
||||
solver.FillSolutionResponseProto(&result);
|
||||
std::string csv_file;
|
||||
@@ -216,8 +226,10 @@ void Run() {
|
||||
|
||||
printf("%-12s: %s\n", "Status",
|
||||
MPSolverResponseStatus_Name(
|
||||
static_cast<MPSolverResponseStatus>(solve_status)).c_str());
|
||||
printf("%-12s: %15.15e\n", "Objective", solver.Objective().Value());
|
||||
static_cast<MPSolverResponseStatus>(solve_status))
|
||||
.c_str());
|
||||
printf("%-12s: %15.15e\n", "Objective",
|
||||
has_solution ? solver.Objective().Value() : 0.0);
|
||||
printf("%-12s: %-6.4g\n", "Time", solving_time_in_sec);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
@@ -33,10 +33,12 @@
|
||||
#include "base/integral_types.h"
|
||||
#include "base/join.h"
|
||||
#include "constraint_solver/routing.h"
|
||||
#include "constraint_solver/routing_flags.h"
|
||||
#include "base/random.h"
|
||||
|
||||
using operations_research::Assignment;
|
||||
using operations_research::RoutingModel;
|
||||
using operations_research::RoutingSearchParameters;
|
||||
using operations_research::ACMRandom;
|
||||
using operations_research::StrAppend;
|
||||
|
||||
@@ -46,8 +48,8 @@ DEFINE_int32(tsp_random_forbidden_connections, 0,
|
||||
"Number of random forbidden connections.");
|
||||
DEFINE_bool(tsp_use_deterministic_random_seed, false,
|
||||
"Use deterministic random seeds.");
|
||||
DECLARE_string(routing_first_solution);
|
||||
DECLARE_bool(routing_no_lns);
|
||||
|
||||
namespace operations_research {
|
||||
|
||||
// Random seed generator.
|
||||
int32 GetSeed() {
|
||||
@@ -100,18 +102,17 @@ class RandomMatrix {
|
||||
const int size_;
|
||||
};
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags( &argc, &argv, true);
|
||||
void Tsp() {
|
||||
if (FLAGS_tsp_size > 0) {
|
||||
// TSP of size FLAGS_tsp_size.
|
||||
// Second argument = 1 to build a single tour (it's a TSP).
|
||||
// Nodes are indexed from 0 to FLAGS_tsp_size - 1, by default the start of
|
||||
// the route is node 0.
|
||||
RoutingModel routing(FLAGS_tsp_size, 1);
|
||||
RoutingSearchParameters parameters = BuildSearchParametersFromFlags();
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
FLAGS_routing_first_solution = "PathCheapestArc";
|
||||
// Disabling Large Neighborhood Search, comment out to activate it.
|
||||
FLAGS_routing_no_lns = true;
|
||||
parameters.set_first_solution_strategy(
|
||||
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
|
||||
|
||||
// Setting the cost function.
|
||||
// Put a permanent callback to the distance accessor here. The callback
|
||||
@@ -139,7 +140,7 @@ int main(int argc, char** argv) {
|
||||
}
|
||||
}
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
const Assignment* solution = routing.Solve();
|
||||
const Assignment* solution = routing.SolveWithParameters(parameters);
|
||||
if (solution != NULL) {
|
||||
// Solution cost.
|
||||
LOG(INFO) << "Cost " << solution->ObjectiveValue();
|
||||
@@ -161,5 +162,11 @@ int main(int argc, char** argv) {
|
||||
} else {
|
||||
LOG(INFO) << "Specify an instance size greater than 0.";
|
||||
}
|
||||
}
|
||||
} // namespace operations_research
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
gflags::ParseCommandLineFlags( &argc, &argv, true);
|
||||
operations_research::Tsp();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -259,13 +259,13 @@ public class CapacitatedVehicleRoutingProblemWithTimeWindows {
|
||||
}
|
||||
|
||||
// Solving
|
||||
RoutingSearchParameters parameters = new RoutingSearchParameters();
|
||||
parameters.no_lns = true;
|
||||
parameters.first_solution = "AllUnperformed";
|
||||
parameters.trace = true;
|
||||
RoutingSearchParameters search_parameters =
|
||||
RoutingModel.DefaultSearchParameters();
|
||||
search_parameters.FirstSolutionStrategy =
|
||||
FirstSolutionStrategy.Types.Value.ALL_UNPERFORMED;
|
||||
|
||||
Console.WriteLine("Search");
|
||||
Assignment solution = model.SolveWithParameters(parameters, null);
|
||||
Assignment solution = model.SolveWithParameters(search_parameters);
|
||||
|
||||
if (solution != null) {
|
||||
String output = "Total cost: " + solution.ObjectiveValue() + "\n";
|
||||
|
||||
@@ -48,8 +48,6 @@ class Tsp
|
||||
static void Solve(int size, int forbidden, int seed)
|
||||
{
|
||||
RoutingModel routing = new RoutingModel(size, 1);
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
routing.SetFirstSolutionStrategy(RoutingModel.ROUTING_PATH_CHEAPEST_ARC);
|
||||
|
||||
// Setting the cost function.
|
||||
// Put a permanent callback to the distance accessor here. The callback
|
||||
@@ -79,7 +77,13 @@ class Tsp
|
||||
"dummy");
|
||||
|
||||
// Solve, returns a solution if any (owned by RoutingModel).
|
||||
Assignment solution = routing.Solve();
|
||||
RoutingSearchParameters search_parameters =
|
||||
RoutingModel.DefaultSearchParameters();
|
||||
// Setting first solution heuristic (cheapest addition).
|
||||
search_parameters.FirstSolutionStrategy =
|
||||
FirstSolutionStrategy.Types.Value.PATH_CHEAPEST_ARC;
|
||||
|
||||
Assignment solution = routing.SolveWithParameters(search_parameters);
|
||||
if (solution != null) {
|
||||
// Solution cost.
|
||||
Console.WriteLine("Cost = {0}", solution.ObjectiveValue());
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user