Add missing basic examples

C++:
 - [Up] linear_programming
 - [Up] integer_programming
 - constraint_programming_CP / rabbits_pheasants_cp
 - knapsack
 - max_flow / min_cost_flow
 - tsp / vrp
note: previous "fuzzy" tsp has been renamed random_tsp.

.Net:
 - vrp
This commit is contained in:
Corentin Le Molgat
2018-09-26 11:02:04 +02:00
parent a2978f293d
commit 027f5cc3f8
15 changed files with 1133 additions and 271 deletions

View File

@@ -0,0 +1,66 @@
// Copyright 2010-2018 Google LLC
// 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.
// Constraint programming example that shows how to use the API.
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/constraint_solver.h"
namespace operations_research {
void RunConstraintProgrammingExample() {
// Instantiate the solver.
Solver solver("ConstraintProgrammingExample");
const int64 numVals = 3;
// Define decision variables.
IntVar* const x = solver.MakeIntVar(0, numVals - 1, "x");
IntVar* const y = solver.MakeIntVar(0, numVals - 1, "y");
IntVar* const z = solver.MakeIntVar(0, numVals - 1, "z");
// Define constraints.
std::vector<IntVar*> xyvars = {x, y};
solver.AddConstraint(solver.MakeAllDifferent(xyvars));
// Create decision builder to search for solutions.
std::vector<IntVar*> allvars = {x, y, z};
DecisionBuilder* const db = solver.MakePhase(
allvars,
Solver::CHOOSE_FIRST_UNBOUND,
Solver::ASSIGN_MIN_VALUE);
bool has_result = solver.Solve(db);
// Check that the problem has a solution.
if (has_result != true) {
LOG(FATAL) << "The problem does not have a solution!";
}
int count = 0;
while (solver.NextSolution()) {
count++;
LOG(INFO) << "Solution " << count << ":";
LOG(INFO) << "x = " << x->Value()
<< " ; y = " << x->Value()
<< " ; z = " << z->Value();
}
LOG(INFO) << "Number of solutions: " << count;
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.wall_time() << "ms";
}
} // namespace operations_research
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::RunConstraintProgrammingExample();
return 0;
}

View File

@@ -1,4 +1,4 @@
// Copyright 2010-2017 Google
// Copyright 2010-2018 Google LLC
// 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
@@ -11,79 +11,82 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Integer programming example that shows how to use the API.
#include "ortools/base/commandlineflags.h"
#include "ortools/base/logging.h"
#include "ortools/linear_solver/linear_solver.h"
namespace operations_research {
void RunIntegerProgrammingExample(
MPSolver::OptimizationProblemType optimization_problem_type) {
MPSolver solver("IntegerProgrammingExample", optimization_problem_type);
const double infinity = solver.infinity();
// x1 and x2 are integer non-negative variables.
MPVariable* const x1 = solver.MakeIntVar(0.0, infinity, "x1");
MPVariable* const x2 = solver.MakeIntVar(0.0, infinity, "x2");
void RunIntegerProgrammingExample(
MPSolver::OptimizationProblemType optimization_problem_type) {
MPSolver solver("IntegerProgrammingExample", optimization_problem_type);
const double infinity = solver.infinity();
// x and y are integer non-negative variables.
MPVariable* const x = solver.MakeIntVar(0.0, infinity, "x");
MPVariable* const y = solver.MakeIntVar(0.0, infinity, "y");
// Minimize x1 + 2 * x2.
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x1, 1);
objective->SetCoefficient(x2, 2);
// Maximize x + 10 * y.
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x, 1);
objective->SetCoefficient(y, 10);
objective->SetMaximization();
// 2 * x2 + 3 * x1 >= 17.
MPConstraint* const c0 = solver.MakeRowConstraint(17, infinity);
c0->SetCoefficient(x1, 3);
c0->SetCoefficient(x2, 2);
// x + 7 * y <= 17.5.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 17.5);
c0->SetCoefficient(x, 1);
c0->SetCoefficient(y, 7);
const MPSolver::ResultStatus result_status = solver.Solve();
// x <= 3.5
MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 3.5);
c1->SetCoefficient(x, 1);
c1->SetCoefficient(y, 0);
// Check that the problem has an optimal solution.
if (result_status != MPSolver::OPTIMAL) {
LOG(FATAL) << "The problem does not have an optimal solution!";
LOG(INFO) << "Number of variables = " << solver.NumVariables();
LOG(INFO) << "Number of constraints = " << solver.NumConstraints();
const MPSolver::ResultStatus result_status = solver.Solve();
// Check that the problem has an optimal solution.
if (result_status != MPSolver::OPTIMAL) {
LOG(FATAL) << "The problem does not have an optimal solution!";
}
LOG(INFO) << "Solution:";
LOG(INFO) << "x = " << x->solution_value();
LOG(INFO) << "y = " << y->solution_value();
LOG(INFO) << "Optimal objective value = " << objective->Value();
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds";
LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations";
LOG(INFO) << "Problem solved in " << solver.nodes() << " branch-and-bound nodes";
}
LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds";
// The objective value of the solution.
LOG(INFO) << "Optimal objective value = " << objective->Value();
// The value of each variable in the solution.
LOG(INFO) << "x1 = " << x1->solution_value();
LOG(INFO) << "x2 = " << x2->solution_value();
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.nodes()
<< " branch-and-bound nodes";
}
void RunAllExamples() {
#if defined(USE_GLPK)
LOG(INFO) << "---- Integer programming example with GLPK ----";
RunIntegerProgrammingExample(MPSolver::GLPK_MIXED_INTEGER_PROGRAMMING);
#endif
void RunAllExamples() {
#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_GLPK)
LOG(INFO) << "---- Integer programming example with GLPK ----";
RunIntegerProgrammingExample(MPSolver::GLPK_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
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::RunAllExamples();
return 0;
}

80
examples/cpp/knapsack.cc Normal file
View File

@@ -0,0 +1,80 @@
// Copyright 2018 Google LLC
// 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.
// Bi-dimensional knapsack problem.
#include <numeric>
#include <sstream>
#include <iterator>
#include "ortools/base/logging.h"
#include "ortools/algorithms/knapsack_solver.h"
namespace operations_research {
void RunKnapsackExample() {
// Instantiate the solver.
KnapsackSolver solver(
KnapsackSolver::KNAPSACK_MULTIDIMENSION_BRANCH_AND_BOUND_SOLVER,
"KnapsackExample");
std::vector<int64> values = {
360, 83, 59, 130, 431, 67, 230, 52, 93, 125, 670, 892, 600, 38, 48, 147,
78, 256, 63, 17, 120, 164, 432, 35, 92, 110, 22, 42, 50, 323, 514, 28, 87,
73, 78, 15, 26, 78, 210, 36, 85, 189, 274, 43, 33, 10, 19, 389, 276, 312
};
std::vector<std::vector<int64>> weights = {
{
7, 0, 30, 22, 80, 94, 11, 81, 70, 64, 59, 18, 0, 36, 3, 8, 15, 42, 9, 0,
42, 47, 52, 32, 26, 48, 55, 6, 29, 84, 2, 4, 18, 56, 7, 29, 93, 44, 71, 3,
86, 66, 31, 65, 0, 79, 20, 65, 52, 13
}
};
std::vector<int64> capacities = {850};
solver.Init(values, weights, capacities);
int64 computed_value = solver.Solve();
// Print solution
std::vector<int> packed_items;
for (std::size_t i=0; i < values.size(); ++i) {
if (solver.BestSolutionContains(i)) packed_items.push_back(i);
}
std::ostringstream packed_items_ss;
std::copy(packed_items.begin(), packed_items.end()-1, std::ostream_iterator<int>(packed_items_ss, ", "));
packed_items_ss << packed_items.back();
std::vector<int64> packed_weights;
packed_weights.reserve(packed_items.size());
for (const auto &it: packed_items) {
packed_weights.push_back(weights[0][it]);
}
std::ostringstream packed_weights_ss;
std::copy(packed_weights.begin(), packed_weights.end()-1, std::ostream_iterator<int>(packed_weights_ss, ", "));
packed_weights_ss << packed_weights.back();
int64 total_weights = std::accumulate(packed_weights.begin(), packed_weights.end(), 0LL);
LOG(INFO) << "Total value: " << computed_value;
LOG(INFO) << "Packed items: {" << packed_items_ss.str() << "}";
LOG(INFO) << "Total weight: " << total_weights;
LOG(INFO) << "Packed weights: {" << packed_weights_ss.str() << "}";
}
} // namespace operations_research
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::RunKnapsackExample();
return 0;
}

View File

@@ -11,116 +11,100 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Linear programming example that shows how to use the API.
#include "ortools/base/commandlineflags.h"
#include "ortools/base/logging.h"
#include "ortools/linear_solver/linear_solver.h"
#include "ortools/linear_solver/linear_solver.pb.h"
namespace operations_research {
void RunLinearProgrammingExample(
MPSolver::OptimizationProblemType optimization_problem_type) {
MPSolver solver("LinearProgrammingExample", optimization_problem_type);
const double infinity = solver.infinity();
// x1, x2 and x3 are continuous non-negative variables.
MPVariable* const x1 = solver.MakeNumVar(0.0, infinity, "x1");
MPVariable* const x2 = solver.MakeNumVar(0.0, infinity, "x2");
MPVariable* const x3 = solver.MakeNumVar(0.0, infinity, "x3");
void RunLinearProgrammingExample(
MPSolver::OptimizationProblemType optimization_problem_type) {
MPSolver solver("LinearProgrammingExample", optimization_problem_type);
const double infinity = solver.infinity();
// x and y are continuous non-negative variables.
MPVariable* const x = solver.MakeNumVar(0.0, infinity, "x");
MPVariable* const y = solver.MakeNumVar(0.0, infinity, "y");
// Maximize 10 * x1 + 6 * x2 + 4 * x3.
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x1, 10);
objective->SetCoefficient(x2, 6);
objective->SetCoefficient(x3, 4);
objective->SetMaximization();
// Objectif function: Maximize 3x + 4y).
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x, 3);
objective->SetCoefficient(y, 4);
objective->SetMaximization();
// x1 + x2 + x3 <= 100.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 100.0);
c0->SetCoefficient(x1, 1);
c0->SetCoefficient(x2, 1);
c0->SetCoefficient(x3, 1);
// x + 2y <= 14.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 14.0);
c0->SetCoefficient(x, 1);
c0->SetCoefficient(y, 2);
// 10 * x1 + 4 * x2 + 5 * x3 <= 600.
MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 600.0);
c1->SetCoefficient(x1, 10);
c1->SetCoefficient(x2, 4);
c1->SetCoefficient(x3, 5);
// 3x - y >= 0.
MPConstraint* const c1 = solver.MakeRowConstraint(0.0, infinity);
c1->SetCoefficient(x, 3);
c1->SetCoefficient(y, -1);
// 2 * x1 + 2 * x2 + 6 * x3 <= 300.
MPConstraint* const c2 = solver.MakeRowConstraint(-infinity, 300.0);
c2->SetCoefficient(x1, 2);
c2->SetCoefficient(x2, 2);
c2->SetCoefficient(x3, 6);
// x - y <= 2.
MPConstraint* const c2 = solver.MakeRowConstraint(-infinity, 2.0);
c2->SetCoefficient(x, 1);
c2->SetCoefficient(y, -1);
// TODO(user): Change example to show = and >= constraints.
LOG(INFO) << "Number of variables = " << solver.NumVariables();
LOG(INFO) << "Number of constraints = " << solver.NumConstraints();
LOG(INFO) << "Number of variables = " << solver.NumVariables();
LOG(INFO) << "Number of constraints = " << solver.NumConstraints();
const MPSolver::ResultStatus result_status = solver.Solve();
// Check that the problem has an optimal solution.
if (result_status != MPSolver::OPTIMAL) {
LOG(FATAL) << "The problem does not have an optimal solution!";
const MPSolver::ResultStatus result_status = solver.Solve();
// Check that the problem has an optimal solution.
if (result_status != MPSolver::OPTIMAL) {
LOG(FATAL) << "The problem does not have an optimal solution!";
}
LOG(INFO) << "Solution:";
LOG(INFO) << "x = " << x->solution_value();
LOG(INFO) << "y = " << y->solution_value();
LOG(INFO) << "Optimal objective value = " << objective->Value();
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds";
LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations";
LOG(INFO) << "x: reduced cost = " << x->reduced_cost();
LOG(INFO) << "y: reduced cost = " << y->reduced_cost();
const std::vector<double> activities = solver.ComputeConstraintActivities();
LOG(INFO) << "c0: dual value = " << c0->dual_value()
<< " activity = " << activities[c0->index()];
LOG(INFO) << "c1: dual value = " << c1->dual_value()
<< " activity = " << activities[c1->index()];
LOG(INFO) << "c2: dual value = " << c2->dual_value()
<< " activity = " << activities[c2->index()];
}
LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds";
// The objective value of the solution.
LOG(INFO) << "Optimal objective value = " << objective->Value();
// The value of each variable in the solution.
LOG(INFO) << "x1 = " << x1->solution_value();
LOG(INFO) << "x2 = " << x2->solution_value();
LOG(INFO) << "x3 = " << x3->solution_value();
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.iterations() << " iterations";
LOG(INFO) << "x1: reduced cost = " << x1->reduced_cost();
LOG(INFO) << "x2: reduced cost = " << x2->reduced_cost();
LOG(INFO) << "x3: reduced cost = " << x3->reduced_cost();
const std::vector<double> activities = solver.ComputeConstraintActivities();
LOG(INFO) << "c0: dual value = " << c0->dual_value()
<< " activity = " << activities[c0->index()];
LOG(INFO) << "c1: dual value = " << c1->dual_value()
<< " activity = " << activities[c1->index()];
LOG(INFO) << "c2: dual value = " << c2->dual_value()
<< " activity = " << activities[c2->index()];
}
void RunAllExamples() {
void RunAllExamples() {
#if defined(USE_GLOP)
LOG(INFO) << "---- Linear programming example with GLOP ----";
RunLinearProgrammingExample(MPSolver::GLOP_LINEAR_PROGRAMMING);
LOG(INFO) << "---- Linear programming example with GLOP ----";
RunLinearProgrammingExample(MPSolver::GLOP_LINEAR_PROGRAMMING);
#endif // USE_GLOP
#if defined(USE_GLPK)
LOG(INFO) << "---- Linear programming example with GLPK ----";
RunLinearProgrammingExample(MPSolver::GLPK_LINEAR_PROGRAMMING);
#endif // USE_GLPK
#if defined(USE_CLP)
LOG(INFO) << "---- Linear programming example with CLP ----";
RunLinearProgrammingExample(MPSolver::CLP_LINEAR_PROGRAMMING);
LOG(INFO) << "---- Linear programming example with CLP ----";
RunLinearProgrammingExample(MPSolver::CLP_LINEAR_PROGRAMMING);
#endif // USE_CLP
#if defined(USE_GLPK)
LOG(INFO) << "---- Linear programming example with GLPK ----";
RunLinearProgrammingExample(MPSolver::GLPK_LINEAR_PROGRAMMING);
#endif // USE_GLPK
#if defined(USE_SLM)
LOG(INFO) << "---- Linear programming example with Sulum ----";
RunLinearProgrammingExample(MPSolver::SULUM_LINEAR_PROGRAMMING);
LOG(INFO) << "---- Linear programming example with Sulum ----";
RunLinearProgrammingExample(MPSolver::SULUM_LINEAR_PROGRAMMING);
#endif // USE_SLM
#if defined(USE_GUROBI)
LOG(INFO) << "---- Linear programming example with Gurobi ----";
RunLinearProgrammingExample(MPSolver::GUROBI_LINEAR_PROGRAMMING);
LOG(INFO) << "---- Linear programming example with Gurobi ----";
RunLinearProgrammingExample(MPSolver::GUROBI_LINEAR_PROGRAMMING);
#endif // USE_GUROBI
#if defined(USE_CPLEX)
LOG(INFO) << "---- Linear programming example with CPLEX ----";
RunLinearProgrammingExample(MPSolver::CPLEX_LINEAR_PROGRAMMING);
LOG(INFO) << "---- Linear programming example with CPLEX ----";
RunLinearProgrammingExample(MPSolver::CPLEX_LINEAR_PROGRAMMING);
#endif // USE_CPLEX
}
}
} // namespace operations_research
int main(int argc, char** argv) {
base::SetFlag(&FLAGS_alsologtostderr, true);
gflags::ParseCommandLineFlags(&argc, &argv, true);
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::RunAllExamples();
return 0;
}

68
examples/cpp/max_flow.cc Normal file
View File

@@ -0,0 +1,68 @@
// Copyright 2018 Google LLC
// 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 "ortools/base/logging.h"
#include "ortools/graph/max_flow.h"
namespace operations_research {
void SolveMaxFlow() {
const int num_nodes = 5;
// Add each arc
// Can't use std::tuple<NodeIndex, NodeIndex, FlowQuantity>
// Initialization list is not working on std:tuple cf. N4387
// Arc are stored as {{begin_node, end_node}, capacity}
std::vector<std::pair<std::pair<NodeIndex, NodeIndex>, FlowQuantity>> arcs = {
{{0, 1}, 20},
{{0, 2}, 30},
{{0, 3}, 10},
{{1, 2}, 40},
{{1, 4}, 30},
{{2, 3}, 10},
{{2, 4}, 20},
{{3, 2}, 5},
{{3, 4}, 20}
};
StarGraph graph(num_nodes, arcs.size());
MaxFlow max_flow(&graph, 0, num_nodes - 1);
for (const auto &it : arcs) {
ArcIndex arc = graph.AddArc(it.first.first, it.first.second);
max_flow.SetArcCapacity(arc, it.second);
}
LOG(INFO) << "Solving max flow with: "
<< graph.num_nodes() << " nodes, and "
<< graph.num_arcs() << " arcs.";
// Find the maximum flow between node 0 and node 4.
max_flow.Solve();
if (MaxFlow::OPTIMAL != max_flow.status()) {
LOG(FATAL) << "Solving the max flow is not optimal!";
}
FlowQuantity total_flow = max_flow.GetOptimalFlow();
LOG(INFO) << "Maximum flow: " << total_flow;
LOG(INFO) << "";
LOG(INFO) << " Arc : Flow / Capacity";
for (int i = 0; i < arcs.size(); ++i) {
LOG(INFO)
<< graph.Tail(i) << " -> " << graph.Head(i) << ": "
<< max_flow.Flow(i) << " / " << max_flow.Capacity(i);
}
}
} // namespace operations_research
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::SolveMaxFlow();
return 0;
}

View File

@@ -0,0 +1,88 @@
// Copyright 2018 Google LLC
// 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 "ortools/base/logging.h"
#include "ortools/graph/min_cost_flow.h"
namespace operations_research {
struct Arc {
std::pair<NodeIndex, NodeIndex> nodes;
FlowQuantity capacity;
FlowQuantity unit_cost;
};
void SolveMinCostFlow() {
// Define supply of each node.
const std::vector<std::pair<NodeIndex, FlowQuantity>> supplies = {
{0, 20},
{1, 0},
{2, 0},
{3,-5},
{4,-15}
};
// Define each arc
// Can't use std::tuple<NodeIndex, NodeIndex, FlowQuantity>
// Initialization list is not working on std:tuple cf. N4387
// Arc are stored as {{begin_node, end_node}, capacity}
const std::vector<Arc> arcs = {
{{0, 1}, 15, 4},
{{0, 2}, 8, 4},
{{1, 2}, 20, 2},
{{1, 3}, 4, 2},
{{1, 4}, 10, 6},
{{2, 3}, 15, 1},
{{2, 4}, 4, 3},
{{3, 4}, 20, 2},
{{4, 2}, 5, 3}
};
StarGraph graph(supplies.size(), arcs.size());
MinCostFlow min_cost_flow(&graph);
for (const auto &it : arcs) {
ArcIndex arc = graph.AddArc(it.nodes.first, it.nodes.second);
min_cost_flow.SetArcCapacity(arc, it.capacity);
min_cost_flow.SetArcUnitCost(arc, it.unit_cost);
}
for (const auto &it : supplies) {
min_cost_flow.SetNodeSupply(it.first, it.second);
}
LOG(INFO) << "Solving min cost flow with: "
<< graph.num_nodes() << " nodes, and "
<< graph.num_arcs() << " arcs.";
// Find the maximum flow between node 0 and node 4.
min_cost_flow.Solve();
if (MinCostFlow::OPTIMAL != min_cost_flow.status()) {
LOG(FATAL) << "Solving the max flow is not optimal!";
}
FlowQuantity total_flow_cost = min_cost_flow.GetOptimalCost();
LOG(INFO) << "Minimum cost flow: " << total_flow_cost;
LOG(INFO) << "";
LOG(INFO) << "Arc : Flow / Capacity / Cost";
for (int i = 0; i < arcs.size(); ++i) {
LOG(INFO)
<< graph.Tail(i) << " -> " << graph.Head(i) << ": "
<< min_cost_flow.Flow(i) << " / " << min_cost_flow.Capacity(i)
<< " / " << min_cost_flow.UnitCost(i);
}
}
} // namespace operations_research
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::SolveMinCostFlow();
return 0;
}

View File

@@ -0,0 +1,67 @@
// Copyright 2018 Google LLC
// 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.
// Knowing that we see 20 heads and 56 legs,
// how many pheasants and rabbits are we looking at ?
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/constraint_solver.h"
namespace operations_research {
void RunConstraintProgrammingExample() {
// Instantiate the solver.
Solver solver("RabbitsPheasantsExample");
// Define decision variables.
IntVar* const rabbits = solver.MakeIntVar(0, 20, "rabbits");
IntVar* const pheasants = solver.MakeIntVar(0, 20, "pheasants");
// Define constraints.
IntExpr* const heads = solver.MakeSum(rabbits, pheasants);
Constraint* const c0 = solver.MakeEquality(heads, 20);
solver.AddConstraint(c0);
IntExpr* const legs = solver.MakeSum(solver.MakeProd(rabbits, 4), solver.MakeProd(pheasants, 2));
Constraint* const c1 = solver.MakeEquality(legs, 56);
solver.AddConstraint(c1);
DecisionBuilder* const db = solver.MakePhase(
rabbits, pheasants,
Solver::CHOOSE_FIRST_UNBOUND,
Solver::ASSIGN_MIN_VALUE);
bool has_result = solver.Solve(db);
// Check that the problem has a solution.
if (has_result != true) {
LOG(FATAL) << "The problem does not have a solution!";
}
int count = 0;
while (solver.NextSolution()) {
count++;
LOG(INFO) << "Solution " << count << ":";
LOG(INFO) << "rabbits = " << rabbits->Value();
LOG(INFO) << "pheasants = " << rabbits->Value();
}
LOG(INFO) << "Number of solutions: " << count;
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.wall_time() << " milliseconds";
}
} // namespace operations_research
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::RunConstraintProgrammingExample();
return 0;
}

167
examples/cpp/random_tsp.cc Normal file
View File

@@ -0,0 +1,167 @@
// Copyright 2010-2017 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.
//
// Traveling Salesman Sample.
//
// This is a sample using the routing library to solve a Traveling Salesman
// Problem.
// The description of the problem can be found here:
// http://en.wikipedia.org/wiki/Travelling_salesman_problem.
// For small problems one can use the hamiltonian path library directly (cf
// graph/hamiltonian_path.h).
// The optimization engine uses local search to improve solutions, first
// solutions being generated using a cheapest addition heuristic.
// Optionally one can randomly forbid a set of random connections between nodes
// (forbidden arcs).
#include <memory>
#include "google/protobuf/text_format.h"
#include "ortools/base/callback.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/join.h"
#include "ortools/base/random.h"
#include "ortools/constraint_solver/routing.h"
#include "ortools/constraint_solver/routing_enums.pb.h"
#include "ortools/constraint_solver/routing_flags.h"
DEFINE_int32(tsp_size, 10, "Size of Traveling Salesman Problem instance.");
DEFINE_bool(tsp_use_random_matrix, true, "Use random cost matrix.");
DEFINE_int32(tsp_random_forbidden_connections, 0,
"Number of random forbidden connections.");
DEFINE_bool(tsp_use_deterministic_random_seed, false,
"Use deterministic random seeds.");
namespace operations_research {
// Random seed generator.
int32 GetSeed() {
if (FLAGS_tsp_use_deterministic_random_seed) {
return ACMRandom::DeterministicSeed();
} else {
return ACMRandom::HostnamePidTimeSeed();
}
}
// Cost/distance functions.
// Sample function.
int64 MyDistance(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) {
// Put your distance code here.
return (from + to).value(); // for instance
}
// Random matrix.
class RandomMatrix {
public:
explicit RandomMatrix(int size) : size_(size) {}
void Initialize() {
matrix_.reset(new int64[size_ * size_]);
const int64 kDistanceMax = 100;
ACMRandom randomizer(GetSeed());
for (RoutingModel::NodeIndex from = RoutingModel::kFirstNode; from < size_;
++from) {
for (RoutingModel::NodeIndex to = RoutingModel::kFirstNode; to < size_;
++to) {
if (to != from) {
matrix_[MatrixIndex(from, to)] = randomizer.Uniform(kDistanceMax);
} else {
matrix_[MatrixIndex(from, to)] = 0LL;
}
}
}
}
int64 Distance(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return matrix_[MatrixIndex(from, to)];
}
private:
int64 MatrixIndex(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return (from * size_ + to).value();
}
std::unique_ptr<int64[]> matrix_;
const int size_;
};
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, RoutingModel::NodeIndex(0));
RoutingSearchParameters parameters = BuildSearchParametersFromFlags();
// Setting first solution heuristic (cheapest addition).
parameters.set_first_solution_strategy(
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
// Setting the cost function.
// Put a permanent callback to the distance accessor here. The callback
// has the following signature: ResultCallback2<int64, int64, int64>.
// The two arguments are the from and to node inidices.
RandomMatrix matrix(FLAGS_tsp_size);
if (FLAGS_tsp_use_random_matrix) {
matrix.Initialize();
routing.SetArcCostEvaluatorOfAllVehicles(
NewPermanentCallback(&matrix, &RandomMatrix::Distance));
} else {
routing.SetArcCostEvaluatorOfAllVehicles(
NewPermanentCallback(MyDistance));
}
// Forbid node connections (randomly).
ACMRandom randomizer(GetSeed());
int64 forbidden_connections = 0;
while (forbidden_connections < FLAGS_tsp_random_forbidden_connections) {
const int64 from = randomizer.Uniform(FLAGS_tsp_size - 1);
const int64 to = randomizer.Uniform(FLAGS_tsp_size - 1) + 1;
if (routing.NextVar(from)->Contains(to)) {
LOG(INFO) << "Forbidding connection " << from << " -> " << to;
routing.NextVar(from)->RemoveValue(to);
++forbidden_connections;
}
}
// Solve, returns a solution if any (owned by RoutingModel).
const Assignment* solution = routing.SolveWithParameters(parameters);
if (solution != nullptr) {
// Solution cost.
LOG(INFO) << "Cost " << solution->ObjectiveValue();
// Inspect solution.
// Only one route here; otherwise iterate from 0 to routing.vehicles() - 1
const int route_number = 0;
std::string route;
for (int64 node = routing.Start(route_number); !routing.IsEnd(node);
node = solution->Value(routing.NextVar(node))) {
absl::StrAppend(&route, routing.IndexToNode(node).value(), " (", node,
") -> ");
}
const int64 end = routing.End(route_number);
absl::StrAppend(&route, routing.IndexToNode(end).value(), " (", end, ")");
LOG(INFO) << route;
} else {
LOG(INFO) << "No solution found.";
}
} 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

@@ -1,4 +1,4 @@
// Copyright 2010-2017 Google
// Copyright 2018 Google LLC
// 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
@@ -11,157 +11,129 @@
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Traveling Salesman Sample.
//
// This is a sample using the routing library to solve a Traveling Salesman
// Problem.
// The description of the problem can be found here:
// http://en.wikipedia.org/wiki/Travelling_salesman_problem.
// For small problems one can use the hamiltonian path library directly (cf
// graph/hamiltonian_path.h).
// The optimization engine uses local search to improve solutions, first
// solutions being generated using a cheapest addition heuristic.
// Optionally one can randomly forbid a set of random connections between nodes
// (forbidden arcs).
#include <memory>
#include "google/protobuf/text_format.h"
#include "ortools/base/callback.h"
#include "ortools/base/commandlineflags.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/join.h"
#include "ortools/base/random.h"
#include <vector>
#include <cmath>
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/routing.h"
#include "ortools/constraint_solver/routing_enums.pb.h"
#include "ortools/constraint_solver/routing_flags.h"
DEFINE_int32(tsp_size, 10, "Size of Traveling Salesman Problem instance.");
DEFINE_bool(tsp_use_random_matrix, true, "Use random cost matrix.");
DEFINE_int32(tsp_random_forbidden_connections, 0,
"Number of random forbidden connections.");
DEFINE_bool(tsp_use_deterministic_random_seed, false,
"Use deterministic random seeds.");
namespace operations_research {
class DataProblem {
private:
std::vector<std::vector<int>> locations_;
// Random seed generator.
int32 GetSeed() {
if (FLAGS_tsp_use_deterministic_random_seed) {
return ACMRandom::DeterministicSeed();
} else {
return ACMRandom::HostnamePidTimeSeed();
}
}
public:
DataProblem() {
locations_ = {
{4, 4},
{2, 0}, {8, 0},
{0, 1}, {1, 1},
{5, 2}, {7, 2},
{3, 3}, {6, 3},
{5, 5}, {8, 5},
{1, 6}, {2, 6},
{3, 7}, {6, 7},
{0, 8}, {7, 8}
};
// Cost/distance functions.
// Compute locations in meters using the block dimension defined as follow
// Manhattan average block: 750ft x 264ft -> 228m x 80m
// here we use: 114m x 80m city block
// src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
std::array<int, 2> cityBlock = {228/2, 80};
for (auto &i: locations_) {
i[0] = i[0] * cityBlock[0];
i[1] = i[1] * cityBlock[1];
}
}
// Sample function.
int64 MyDistance(RoutingModel::NodeIndex from, RoutingModel::NodeIndex to) {
// Put your distance code here.
return (from + to).value(); // for instance
}
std::size_t GetVehicleNumber() const { return 1;}
const std::vector<std::vector<int>>& GetLocations() const { return locations_;}
RoutingModel::NodeIndex GetDepot() const { return RoutingModel::kFirstNode;}
};
// Random matrix.
class RandomMatrix {
public:
explicit RandomMatrix(int size) : size_(size) {}
void Initialize() {
matrix_.reset(new int64[size_ * size_]);
const int64 kDistanceMax = 100;
ACMRandom randomizer(GetSeed());
for (RoutingModel::NodeIndex from = RoutingModel::kFirstNode; from < size_;
++from) {
for (RoutingModel::NodeIndex to = RoutingModel::kFirstNode; to < size_;
++to) {
if (to != from) {
matrix_[MatrixIndex(from, to)] = randomizer.Uniform(kDistanceMax);
} else {
matrix_[MatrixIndex(from, to)] = 0LL;
/*! @brief Manhattan distance implemented as a callback.
* @details It uses an array of positions and
* computes the Manhattan distance between the two positions of two different indices.*/
class ManhattanDistance: public RoutingModel::NodeEvaluator2 {
private:
std::vector<std::vector<int64>> distances_;
public:
ManhattanDistance(const DataProblem& data) {
// Precompute distance between location to have distance callback in O(1)
distances_ = std::vector<std::vector<int64>>(
data.GetLocations().size(),
std::vector<int64>(
data.GetLocations().size(),
0LL));
for (std::size_t fromNode = 0; fromNode < data.GetLocations().size(); fromNode++) {
for (std::size_t toNode = 0; toNode < data.GetLocations().size(); toNode++) {
if (fromNode != toNode)
distances_[fromNode][toNode] =
std::abs(data.GetLocations()[toNode][0] - data.GetLocations()[fromNode][0]) +
std::abs(data.GetLocations()[toNode][1] - data.GetLocations()[fromNode][1]);
}
}
}
}
int64 Distance(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return matrix_[MatrixIndex(from, to)];
bool IsRepeatable() const override {return true;}
//! @brief Returns the manhattan distance between the two nodes.
int64 Run(RoutingModel::NodeIndex FromNode, RoutingModel::NodeIndex ToNode) override {
return distances_[FromNode.value()][ToNode.value()];
}
};
//! @brief Print the solution
//! @param[in] data Data of the problem.
//! @param[in] routing Routing solver used.
//! @param[in] solution Solution found by the solver.
void PrintSolution(
const DataProblem& data,
const RoutingModel& routing,
const Assignment& solution) {
LOG(INFO) << "Objective: " << solution.ObjectiveValue();
// Inspect solution.
int64 index = routing.Start(0);
LOG(INFO) << "Route for Vehicle 0:";
int64 distance = 0LL;
std::stringstream route;
while (routing.IsEnd(index) == false) {
route << routing.IndexToNode(index).value() << " -> ";
int64 previous_index = index;
index = solution.Value(routing.NextVar(index));
distance += const_cast<RoutingModel&>(routing).GetArcCostForVehicle(previous_index, index, 0LL);
}
LOG(INFO) << route.str() << routing.IndexToNode(index).value();
LOG(INFO) << "Distance of the route: " << distance << "m";
}
private:
int64 MatrixIndex(RoutingModel::NodeIndex from,
RoutingModel::NodeIndex to) const {
return (from * size_ + to).value();
}
std::unique_ptr<int64[]> matrix_;
const int size_;
};
void Solve() {
// Instantiate the data problem.
DataProblem data;
// Create Routing Model
RoutingModel routing(
data.GetLocations().size(),
data.GetVehicleNumber(),
data.GetDepot());
// Define weight of each edge
ManhattanDistance distance(data);
routing.SetArcCostEvaluatorOfAllVehicles(NewPermanentCallback(&distance, &ManhattanDistance::Run));
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, RoutingModel::NodeIndex(0));
RoutingSearchParameters parameters = BuildSearchParametersFromFlags();
// Setting first solution heuristic (cheapest addition).
parameters.set_first_solution_strategy(
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
RoutingSearchParameters searchParameters = RoutingModel::DefaultSearchParameters();
searchParameters.set_first_solution_strategy(FirstSolutionStrategy::PATH_CHEAPEST_ARC);
// Setting the cost function.
// Put a permanent callback to the distance accessor here. The callback
// has the following signature: ResultCallback2<int64, int64, int64>.
// The two arguments are the from and to node inidices.
RandomMatrix matrix(FLAGS_tsp_size);
if (FLAGS_tsp_use_random_matrix) {
matrix.Initialize();
routing.SetArcCostEvaluatorOfAllVehicles(
NewPermanentCallback(&matrix, &RandomMatrix::Distance));
} else {
routing.SetArcCostEvaluatorOfAllVehicles(
NewPermanentCallback(MyDistance));
}
// Forbid node connections (randomly).
ACMRandom randomizer(GetSeed());
int64 forbidden_connections = 0;
while (forbidden_connections < FLAGS_tsp_random_forbidden_connections) {
const int64 from = randomizer.Uniform(FLAGS_tsp_size - 1);
const int64 to = randomizer.Uniform(FLAGS_tsp_size - 1) + 1;
if (routing.NextVar(from)->Contains(to)) {
LOG(INFO) << "Forbidding connection " << from << " -> " << to;
routing.NextVar(from)->RemoveValue(to);
++forbidden_connections;
}
}
// Solve, returns a solution if any (owned by RoutingModel).
const Assignment* solution = routing.SolveWithParameters(parameters);
if (solution != nullptr) {
// Solution cost.
LOG(INFO) << "Cost " << solution->ObjectiveValue();
// Inspect solution.
// Only one route here; otherwise iterate from 0 to routing.vehicles() - 1
const int route_number = 0;
std::string route;
for (int64 node = routing.Start(route_number); !routing.IsEnd(node);
node = solution->Value(routing.NextVar(node))) {
absl::StrAppend(&route, routing.IndexToNode(node).value(), " (", node,
") -> ");
}
const int64 end = routing.End(route_number);
absl::StrAppend(&route, routing.IndexToNode(end).value(), " (", end, ")");
LOG(INFO) << route;
} else {
LOG(INFO) << "No solution found.";
}
} else {
LOG(INFO) << "Specify an instance size greater than 0.";
const Assignment* solution = routing.SolveWithParameters(searchParameters);
PrintSolution(data, routing, *solution);
}
}
} // namespace operations_research
int main(int argc, char** argv) {
gflags::ParseCommandLineFlags(&argc, &argv, true);
operations_research::Tsp();
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::Solve();
return 0;
}

161
examples/cpp/vrp.cc Normal file
View File

@@ -0,0 +1,161 @@
// Copyright 2018 Google LLC
// 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 <vector>
#include <cmath>
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/routing.h"
namespace operations_research {
class DataProblem {
private:
std::vector<std::vector<int>> locations_;
public:
DataProblem() {
locations_ = {
{4, 4},
{2, 0}, {8, 0},
{0, 1}, {1, 1},
{5, 2}, {7, 2},
{3, 3}, {6, 3},
{5, 5}, {8, 5},
{1, 6}, {2, 6},
{3, 7}, {6, 7},
{0, 8}, {7, 8}
};
// Compute locations in meters using the block dimension defined as follow
// Manhattan average block: 750ft x 264ft -> 228m x 80m
// here we use: 114m x 80m city block
// src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
std::array<int, 2> cityBlock = {228/2, 80};
for (auto &i: locations_) {
i[0] = i[0] * cityBlock[0];
i[1] = i[1] * cityBlock[1];
}
}
std::size_t GetVehicleNumber() const { return 4;}
const std::vector<std::vector<int>>& GetLocations() const { return locations_;}
RoutingModel::NodeIndex GetDepot() const { return RoutingModel::kFirstNode;}
};
/*! @brief Manhattan distance implemented as a callback.
* @details It uses an array of positions and
* computes the Manhattan distance between the two positions of two different indices.*/
class ManhattanDistance: public RoutingModel::NodeEvaluator2 {
private:
std::vector<std::vector<int64>> distances_;
public:
ManhattanDistance(const DataProblem& data) {
// Precompute distance between location to have distance callback in O(1)
distances_ = std::vector<std::vector<int64>>(
data.GetLocations().size(),
std::vector<int64>(
data.GetLocations().size(),
0LL));
for (std::size_t fromNode = 0; fromNode < data.GetLocations().size(); fromNode++) {
for (std::size_t toNode = 0; toNode < data.GetLocations().size(); toNode++) {
if (fromNode != toNode)
distances_[fromNode][toNode] =
std::abs(data.GetLocations()[toNode][0] - data.GetLocations()[fromNode][0]) +
std::abs(data.GetLocations()[toNode][1] - data.GetLocations()[fromNode][1]);
}
}
}
bool IsRepeatable() const override {return true;}
//! @brief Returns the manhattan distance between the two nodes.
int64 Run(RoutingModel::NodeIndex FromNode, RoutingModel::NodeIndex ToNode) override {
return distances_[FromNode.value()][ToNode.value()];
}
};
//! @brief Add distance Dimension.
//! @param[in] data Data of the problem.
//! @param[in, out] routing Routing solver used.
static void AddDistanceDimension(const DataProblem& data, RoutingModel* routing) {
std::string distance("Distance");
routing->AddDimension(
new ManhattanDistance(data),
0, // null slack
3000, // maximum distance per vehicle
true, // start cumul to zero
distance);
RoutingDimension* distanceDimension = routing->GetMutableDimension(distance);
// Try to minimize the max distance among vehicles.
// /!\ It doesn't mean the standard deviation is minimized
distanceDimension->SetGlobalSpanCostCoefficient(100);
}
//! @brief Print the solution
//! @param[in] data Data of the problem.
//! @param[in] routing Routing solver used.
//! @param[in] solution Solution found by the solver.
void PrintSolution(
const DataProblem& data,
const RoutingModel& routing,
const Assignment& solution) {
LOG(INFO) << "Objective: " << solution.ObjectiveValue();
// Inspect solution.
for (int i=0; i < data.GetVehicleNumber(); ++i) {
int64 index = routing.Start(i);
LOG(INFO) << "Route for Vehicle " << i << ":";
int64 distance = 0LL;
std::stringstream route;
while (routing.IsEnd(index) == false) {
route << routing.IndexToNode(index).value() << " -> ";
int64 previous_index = index;
index = solution.Value(routing.NextVar(index));
distance += const_cast<RoutingModel&>(routing).GetArcCostForVehicle(previous_index, index, i);
}
LOG(INFO) << route.str() << routing.IndexToNode(index).value();
LOG(INFO) << "Distance of the route: " << distance << "m";
}
}
void Solve() {
// Instantiate the data problem.
DataProblem data;
// Create Routing Model
RoutingModel routing(
data.GetLocations().size(),
data.GetVehicleNumber(),
data.GetDepot());
// Define weight of each edge
ManhattanDistance distance(data);
routing.SetArcCostEvaluatorOfAllVehicles(
NewPermanentCallback(&distance, &ManhattanDistance::Run));
AddDistanceDimension(data, &routing);
// Setting first solution heuristic (cheapest addition).
auto searchParameters = RoutingModel::DefaultSearchParameters();
searchParameters.set_first_solution_strategy(
FirstSolutionStrategy::PATH_CHEAPEST_ARC);
const Assignment* solution = routing.SolveWithParameters(searchParameters);
PrintSolution(data, routing, *solution);
}
} // namespace operations_research
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::Solve();
return 0;
}

164
examples/dotnet/vrp.cs Normal file
View File

@@ -0,0 +1,164 @@
// Copyright 2018 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.
using System;
using System.Collections.Generic;
using Google.OrTools.ConstraintSolver;
/// <summary>
/// This is a sample using the routing library .Net wrapper to solve a VRP problem.
/// A description of the problem can be found here:
/// http://en.wikipedia.org/wiki/Vehicle_routing_problem.
/// </summary>
public class VRP {
class DataProblem {
private int[,] locations_;
// Constructor:
public DataProblem() {
locations_ = new int[,] {
{4, 4},
{2, 0}, {8, 0},
{0, 1}, {1, 1},
{5, 2}, {7, 2},
{3, 3}, {6, 3},
{5, 5}, {8, 5},
{1, 6}, {2, 6},
{3, 7}, {6, 7},
{0, 8}, {7, 8}
};
// Compute locations in meters using the block dimension defined as follow
// Manhattan average block: 750ft x 264ft -> 228m x 80m
// here we use: 114m x 80m city block
// src: https://nyti.ms/2GDoRIe "NY Times: Know Your distance"
int[] cityBlock = {228/2, 80};
for (int i=0; i < locations_.GetLength(0); i++) {
locations_[i, 0] = locations_[i, 0] * cityBlock[0];
locations_[i, 1] = locations_[i, 1] * cityBlock[1];
}
}
public int GetVehicleNumber() { return 4;}
public ref readonly int[,] GetLocations() { return ref locations_;}
public int GetLocationNumber() { return locations_.GetLength(0);}
public int GetDepot() { return 0;}
};
/// <summary>
/// Manhattan distance implemented as a callback. It uses an array of
/// positions and computes the Manhattan distance between the two
/// positions of two different indices.
/// </summary>
class ManhattanDistance : NodeEvaluator2 {
private int[,] distances_;
public ManhattanDistance(in DataProblem data) {
// precompute distance between location to have distance callback in O(1)
distances_ = new int[data.GetLocationNumber(), data.GetLocationNumber()];
for (int fromNode = 0; fromNode < data.GetLocationNumber(); fromNode++) {
for (int toNode = 0; toNode < data.GetLocationNumber(); toNode++) {
if (fromNode == toNode)
distances_[fromNode, toNode] = 0;
else
distances_[fromNode, toNode] =
Math.Abs(data.GetLocations()[toNode, 0] - data.GetLocations()[fromNode, 0]) +
Math.Abs(data.GetLocations()[toNode, 1] - data.GetLocations()[fromNode, 1]);
}
}
}
/// <summary>
/// Returns the manhattan distance between the two nodes
/// </summary>
public override long Run(int FromNode, int ToNode) {
return distances_[FromNode, ToNode];
}
};
/// <summary>
/// Add distance Dimension
/// </summary>
static void AddDistanceDimension(
in DataProblem data,
in RoutingModel routing) {
String distance = "Distance";
routing.AddDimension(
new ManhattanDistance(data),
0, // null slack
3000, // maximum distance per vehicle
true, // start cumul to zero
distance);
RoutingDimension distanceDimension = routing.GetDimensionOrDie(distance);
// Try to minimize the max distance among vehicles.
// /!\ It doesn't mean the standard deviation is minimized
distanceDimension.SetGlobalSpanCostCoefficient(100);
}
/// <summary>
/// Print the solution
/// </summary>
static void PrintSolution(
in DataProblem data,
in RoutingModel routing,
in Assignment solution) {
Console.WriteLine("Objective: {0}", solution.ObjectiveValue());
// Inspect solution.
for (int i=0; i < data.GetVehicleNumber(); ++i) {
Console.WriteLine("Route for Vehicle " + i + ":");
long distance = 0;
var index = routing.Start(i);
while (routing.IsEnd(index) == false) {
Console.Write("{0} -> ", routing.IndexToNode(index));
var previousIndex = index;
index = solution.Value(routing.NextVar(index));
distance += routing.GetArcCostForVehicle(previousIndex, index, i);
}
Console.WriteLine("{0}", routing.IndexToNode(index));
Console.WriteLine("Distance of the route: {0}m", distance);
}
}
/// <summary>
/// Solves the current routing problem.
/// </summary>
static void Solve() {
// Instantiate the data problem.
DataProblem data = new DataProblem();
// Create Routing Model
RoutingModel routing = new RoutingModel(
data.GetLocationNumber(),
data.GetVehicleNumber(),
data.GetDepot());
// Define weight cost of each edge
NodeEvaluator2 distanceEvaluator = new ManhattanDistance(data);
//protect callbacks from the GC
GC.KeepAlive(distanceEvaluator);
routing.SetArcCostEvaluatorOfAllVehicles(distanceEvaluator);
AddDistanceDimension(data, routing);
// Setting first solution heuristic (cheapest addition).
RoutingSearchParameters searchParameters = RoutingModel.DefaultSearchParameters();
searchParameters.FirstSolutionStrategy = FirstSolutionStrategy.Types.Value.PathCheapestArc;
Assignment solution = routing.SolveWithParameters(searchParameters);
PrintSolution(data, routing, solution);
}
public static void Main(String[] args) {
Solve();
}
}

View File

@@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<LangVersion>7.2</LangVersion>
<TargetFramework>netcoreapp2.1</TargetFramework>
<EnableDefaultItems>false</EnableDefaultItems>
<RestoreSources>../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json</RestoreSources>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugType>full</DebugType>
<Optimize>true</Optimize>
<GenerateTailCalls>true</GenerateTailCalls>
</PropertyGroup>
<ItemGroup>
<Compile Include="vrp.cs" />
<PackageReference Include="Google.OrTools" Version="6.9.*" />
</ItemGroup>
</Project>

View File

@@ -12,7 +12,6 @@
// limitations under the License.
import java.io.*;
import static java.lang.Math.abs;
//import java.util.*;
import com.google.ortools.constraintsolver.RoutingModel;
import com.google.ortools.constraintsolver.NodeEvaluator2;

View File

@@ -37,6 +37,7 @@ cc:
$(warning Cannot find '$@' command which is needed for build. Please make sure it is installed and in system path.)
test_cc: cc
check_cc: cc
test_fz: cc
else
cc: $(OR_TOOLS_LIBS)
@@ -46,6 +47,8 @@ test_cc: \
test_cc_tests \
test_cc_samples \
test_cc_examples
.PHONY: check_cc
check_cc: check_cc_examples
test_fz: \
test_fz_examples
BUILT_LANGUAGES += C++
@@ -468,6 +471,15 @@ $(OBJ_DIR)/%.$O: $(TEST_DIR)/%.cc $(CP_DEPS) $(SAT_DEPS) $(LP_DEPS) | $(OBJ_DIR)
.PHONY: test_cc_examples # Build and Run all C++ Examples (located in examples/cpp)
test_cc_examples: cc
$(MAKE) rcc_linear_programming
$(MAKE) rcc_integer_programming
$(MAKE) rcc_constraint_programming_cp
$(MAKE) rcc_rabbits_pheasants_cp
$(MAKE) rcc_tsp
$(MAKE) rcc_vrp
$(MAKE) rcc_knapsack
$(MAKE) rcc_max_flow
$(MAKE) rcc_min_cost_flow
$(MAKE) rcc_costas_array
$(MAKE) rcc_cryptarithm
$(MAKE) rcc_cvrp_disjoint_tw
@@ -482,13 +494,11 @@ test_cc_examples: cc
$(MAKE) rcc_flow_api
# $(MAKE) rcc_frequency_assignment_problem # Need data file
$(MAKE) rcc_golomb ARGS="--size=5"
$(MAKE) rcc_integer_programming
$(MAKE) rcc_jobshop ARGS="--data_file=examples/data/jobshop/ft06"
$(MAKE) rcc_jobshop_earlytardy ARGS="--machine_count=6 --job_count=6"
$(MAKE) rcc_jobshop_ls ARGS="--data_file=examples/data/jobshop/ft06"
$(MAKE) rcc_jobshop_sat ARGS="--input=examples/data/jobshop/ft06"
$(MAKE) rcc_linear_assignment_api
$(MAKE) rcc_linear_programming
$(MAKE) rcc_linear_solver_protocol_buffers
$(MAKE) rcc_ls_api
$(MAKE) rcc_magic_square
@@ -497,17 +507,29 @@ test_cc_examples: cc
$(MAKE) rcc_multidim_knapsack ARGS="--data_file examples/data/multidim_knapsack/PB1.DAT"
$(MAKE) rcc_network_routing ARGS="--clients=10 --backbones=5 --demands=10 --traffic_min=5 --traffic_max=10 --min_client_degree=2 --max_client_degree=5 --min_backbone_degree=3 --max_backbone_degree=5 --max_capacity=20 --fixed_charge_cost=10"
$(MAKE) rcc_nqueens
$(MAKE) rcc_pdptw ARGS="--pdp_file examples/data/pdptw/LC1_2_1.txt"
$(MAKE) rcc_random_tsp
$(MAKE) rcc_pdptw ARGS="--pdp_file=examples/data/pdptw/LC1_2_1.txt"
# $(MAKE) rcc_shift_minimization_sat # Port to new API.
# $(MAKE) rcc_solve # Need data file
$(MAKE) rcc_sports_scheduling ARGS="--num_teams=8 --time_limit=10000"
$(MAKE) rcc_strawberry_fields_with_column_generation
$(MAKE) rcc_tsp
$(MAKE) rcc_weighted_tardiness_sat
$(OBJ_DIR)/%.$O: $(CC_EX_DIR)/%.cc $(CP_DEPS) $(SAT_DEPS) $(LP_DEPS)| $(OBJ_DIR)
$(CCC) $(CFLAGS) -c $(CC_EX_PATH)$S$*.cc $(OBJ_OUT)$(OBJ_DIR)$S$*.$O
.PHONY: check_cc_examples # Build and Run few C++ Examples
check_cc_examples:
$(MAKE) rcc_linear_programming
$(MAKE) rcc_integer_programming
$(MAKE) rcc_constraint_programming_cp
$(MAKE) rcc_rabbits_pheasants_cp
$(MAKE) rcc_tsp
$(MAKE) rcc_vrp
$(MAKE) rcc_knapsack
$(MAKE) rcc_max_flow
$(MAKE) rcc_min_cost_flow
.PHONY: test_cc_samples # Build and Run all C++ Samples (located in ortools/*/samples)
test_cc_samples: cc
$(MAKE) rcc_binpacking_problem

View File

@@ -535,6 +535,7 @@ test_dotnet_examples_csharp: $(DOTNET_ORTOOLS_NUPKG)
$(MAKE) rdotnet_to_num
$(MAKE) rdotnet_traffic_lights
$(MAKE) rdotnet_tsp
$(MAKE) rdotnet_vrp
$(MAKE) rdotnet_volsay
$(MAKE) rdotnet_volsay2
$(MAKE) rdotnet_volsay3