fix merge

This commit is contained in:
Laurent Perron
2016-06-03 04:44:30 -07:00
2914 changed files with 18349 additions and 57169 deletions

36
.gitignore vendored Normal file
View 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/

View File

@@ -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:

View File

@@ -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
View File

@@ -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

View File

@@ -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

View File

@@ -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;
}
}

View File

@@ -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&#8217;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&#8217;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>

View File

@@ -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&#8217;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>

View File

@@ -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";

View File

@@ -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;
}

View 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();
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}

View File

@@ -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());

View File

@@ -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;

View File

@@ -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
View 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
View 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_

View File

@@ -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.";
}

View File

@@ -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.";
}

View File

@@ -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)) {

View File

@@ -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

View File

@@ -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;

View File

@@ -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;
}

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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);

View File

@@ -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
View 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;
}

View File

@@ -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

View File

@@ -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;
}

View File

@@ -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";

View File

@@ -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