diff --git a/examples/cpp/constraint_programming_cp.cc b/examples/cpp/constraint_programming_cp.cc new file mode 100644 index 0000000000..b6eb509318 --- /dev/null +++ b/examples/cpp/constraint_programming_cp.cc @@ -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 xyvars = {x, y}; + solver.AddConstraint(solver.MakeAllDifferent(xyvars)); + + // Create decision builder to search for solutions. + std::vector 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; +} diff --git a/examples/cpp/integer_programming.cc b/examples/cpp/integer_programming.cc index 2ca53459b9..67d325bc0f 100644 --- a/examples/cpp/integer_programming.cc +++ b/examples/cpp/integer_programming.cc @@ -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; } diff --git a/examples/cpp/knapsack.cc b/examples/cpp/knapsack.cc new file mode 100644 index 0000000000..10c65e94f0 --- /dev/null +++ b/examples/cpp/knapsack.cc @@ -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 +#include +#include +#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 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> 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 capacities = {850}; + + solver.Init(values, weights, capacities); + int64 computed_value = solver.Solve(); + + // Print solution + std::vector 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(packed_items_ss, ", ")); + packed_items_ss << packed_items.back(); + + std::vector 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(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; +} diff --git a/examples/cpp/linear_programming.cc b/examples/cpp/linear_programming.cc index 5067ceaa11..e7f79cd1f7 100644 --- a/examples/cpp/linear_programming.cc +++ b/examples/cpp/linear_programming.cc @@ -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 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 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; } diff --git a/examples/cpp/max_flow.cc b/examples/cpp/max_flow.cc new file mode 100644 index 0000000000..22e16e6ee1 --- /dev/null +++ b/examples/cpp/max_flow.cc @@ -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 + // Initialization list is not working on std:tuple cf. N4387 + // Arc are stored as {{begin_node, end_node}, capacity} + std::vector, 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; +} diff --git a/examples/cpp/min_cost_flow.cc b/examples/cpp/min_cost_flow.cc new file mode 100644 index 0000000000..61d7e9dc6a --- /dev/null +++ b/examples/cpp/min_cost_flow.cc @@ -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 nodes; + FlowQuantity capacity; + FlowQuantity unit_cost; + }; + + void SolveMinCostFlow() { + // Define supply of each node. + const std::vector> supplies = { + {0, 20}, + {1, 0}, + {2, 0}, + {3,-5}, + {4,-15} + }; + + // Define each arc + // Can't use std::tuple + // Initialization list is not working on std:tuple cf. N4387 + // Arc are stored as {{begin_node, end_node}, capacity} + const std::vector 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; +} diff --git a/examples/cpp/rabbits_pheasants_cp.cc b/examples/cpp/rabbits_pheasants_cp.cc new file mode 100644 index 0000000000..477414b4a1 --- /dev/null +++ b/examples/cpp/rabbits_pheasants_cp.cc @@ -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; +} diff --git a/examples/cpp/random_tsp.cc b/examples/cpp/random_tsp.cc new file mode 100644 index 0000000000..ffb865e0cb --- /dev/null +++ b/examples/cpp/random_tsp.cc @@ -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 + +#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 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. + // 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; +} diff --git a/examples/cpp/tsp.cc b/examples/cpp/tsp.cc index ffb865e0cb..00ea058b59 100644 --- a/examples/cpp/tsp.cc +++ b/examples/cpp/tsp.cc @@ -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 - -#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 +#include +#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> 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 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>& 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> distances_; + public: + ManhattanDistance(const DataProblem& data) { + // Precompute distance between location to have distance callback in O(1) + distances_ = std::vector>( + data.GetLocations().size(), + std::vector( + 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(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 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. - // 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; } diff --git a/examples/cpp/vrp.cc b/examples/cpp/vrp.cc new file mode 100644 index 0000000000..a266231ab0 --- /dev/null +++ b/examples/cpp/vrp.cc @@ -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 +#include +#include "ortools/base/logging.h" +#include "ortools/constraint_solver/routing.h" + +namespace operations_research { + class DataProblem { + private: + std::vector> 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 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>& 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> distances_; + public: + ManhattanDistance(const DataProblem& data) { + // Precompute distance between location to have distance callback in O(1) + distances_ = std::vector>( + data.GetLocations().size(), + std::vector( + 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(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; +} diff --git a/examples/dotnet/vrp.cs b/examples/dotnet/vrp.cs new file mode 100644 index 0000000000..5cbb0b3566 --- /dev/null +++ b/examples/dotnet/vrp.cs @@ -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; + +/// +/// 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. +/// +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;} + }; + + + /// + /// 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. + /// + 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]); + } + } + } + + /// + /// Returns the manhattan distance between the two nodes + /// + public override long Run(int FromNode, int ToNode) { + return distances_[FromNode, ToNode]; + } + }; + + /// + /// Add distance Dimension + /// + 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); + } + + /// + /// Print the solution + /// + 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); + } + } + + /// + /// Solves the current routing problem. + /// + 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(); + } +} diff --git a/examples/dotnet/vrp.csproj b/examples/dotnet/vrp.csproj new file mode 100644 index 0000000000..991979bef8 --- /dev/null +++ b/examples/dotnet/vrp.csproj @@ -0,0 +1,20 @@ + + + Exe + 7.2 + netcoreapp2.1 + false + ../../packages;$(RestoreSources);https://api.nuget.org/v3/index.json + + + + full + true + true + + + + + + + diff --git a/examples/java/Vrp.java b/examples/java/Vrp.java index cbad5d6883..ed77ee912d 100644 --- a/examples/java/Vrp.java +++ b/examples/java/Vrp.java @@ -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; diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk index 44cd1b31eb..b18b92dadc 100755 --- a/makefiles/Makefile.cpp.mk +++ b/makefiles/Makefile.cpp.mk @@ -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 diff --git a/makefiles/Makefile.dotnet.mk b/makefiles/Makefile.dotnet.mk index fb4a361164..edab3973a9 100644 --- a/makefiles/Makefile.dotnet.mk +++ b/makefiles/Makefile.dotnet.mk @@ -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