Format examples/cpp

This commit is contained in:
Corentin Le Molgat
2018-11-22 13:15:13 +01:00
parent 6b485a7050
commit 24ad8771ef
22 changed files with 856 additions and 912 deletions

View File

@@ -129,7 +129,9 @@ class AcpData {
}
break;
}
default: { LOG(ERROR) << "Should not be here"; }
default: {
LOG(ERROR) << "Should not be here";
}
}
}

View File

@@ -98,7 +98,9 @@ class AcpData {
}
break;
}
default: { LOG(ERROR) << "Should not be here"; }
default: {
LOG(ERROR) << "Should not be here";
}
}
}

View File

@@ -17,46 +17,43 @@
#include "ortools/constraint_solver/constraint_solver.h"
namespace operations_research {
void RunConstraintProgrammingExample() {
// Instantiate the solver.
Solver solver("ConstraintProgrammingExample");
const int64 numVals = 3;
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 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));
// Define constraints.
std::vector<IntVar*> xyvars = {x, y};
solver.AddConstraint(solver.MakeAllDifferent(xyvars));
LOG(INFO) << "Number of constraints: " << solver.constraints();
LOG(INFO) << "Number of constraints: " << solver.constraints();
// 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);
// 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);
solver.NewSearch(db);
while (solver.NextSolution()) {
LOG(INFO) << "Solution"
<< ": x = " << x->Value()
<< "; y = " << x->Value()
<< "; z = " << z->Value();
}
solver.EndSearch();
LOG(INFO) << "Number of solutions: " << solver.solutions();
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.wall_time() << "ms";
LOG(INFO) << "Memory usage: " << Solver::MemoryUsage() << " bytes";
solver.NewSearch(db);
while (solver.NextSolution()) {
LOG(INFO) << "Solution"
<< ": x = " << x->Value() << "; y = " << x->Value()
<< "; z = " << z->Value();
}
solver.EndSearch();
LOG(INFO) << "Number of solutions: " << solver.solutions();
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << solver.wall_time() << "ms";
LOG(INFO) << "Memory usage: " << Solver::MemoryUsage() << " bytes";
}
} // namespace operations_research
int main(int argc, char **argv) {
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::RunConstraintProgrammingExample();

View File

@@ -182,8 +182,7 @@ void DisplayPlan(
for (int64 order = 0; order < routing.Size(); ++order) {
if (routing.IsStart(order) || routing.IsEnd(order)) continue;
++group_size;
visited.insert(
plan.Value(routing.VehicleVar(order)));
visited.insert(plan.Value(routing.VehicleVar(order)));
if (group_size == max_nodes_per_group) {
if (visited.size() > 1) {
group_same_vehicle_cost += (visited.size() - 1) * same_vehicle_cost;
@@ -214,12 +213,12 @@ void DisplayPlan(
operations_research::IntVar* const slack_var =
routing.IsEnd(order) ? nullptr : time_dimension.SlackVar(order);
if (slack_var != nullptr && plan.Contains(slack_var)) {
StringAppendF(
&plan_output,
"%lld Load(%lld) Time(%lld, %lld) Slack(%lld, %lld)",
routing.IndexToNode(order).value(),
plan.Value(load_var), plan.Min(time_var), plan.Max(time_var),
plan.Min(slack_var), plan.Max(slack_var));
StringAppendF(&plan_output,
"%lld Load(%lld) Time(%lld, %lld) Slack(%lld, %lld)",
routing.IndexToNode(order).value(),
plan.Value(load_var), plan.Min(time_var),
plan.Max(time_var), plan.Min(slack_var),
plan.Max(slack_var));
} else {
StringAppendF(&plan_output, "%lld Load(%lld) Time(%lld, %lld)",
routing.IndexToNode(order).value(),

View File

@@ -64,7 +64,8 @@ class LocationContainer {
MTRandom randomizer_;
const int64 speed_;
gtl::ITIVector<operations_research::RoutingModel::NodeIndex, Location> locations_;
gtl::ITIVector<operations_research::RoutingModel::NodeIndex, Location>
locations_;
};
// Random demand.

View File

@@ -17,71 +17,72 @@
#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();
// 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");
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");
// Maximize x + 10 * y.
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x, 1);
objective->SetCoefficient(y, 10);
objective->SetMaximization();
// Maximize x + 10 * y.
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x, 1);
objective->SetCoefficient(y, 10);
objective->SetMaximization();
// x + 7 * y <= 17.5.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 17.5);
c0->SetCoefficient(x, 1);
c0->SetCoefficient(y, 7);
// x + 7 * y <= 17.5.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 17.5);
c0->SetCoefficient(x, 1);
c0->SetCoefficient(y, 7);
// x <= 3.5
MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 3.5);
c1->SetCoefficient(x, 1);
c1->SetCoefficient(y, 0);
// x <= 3.5
MPConstraint* const c1 = solver.MakeRowConstraint(-infinity, 3.5);
c1->SetCoefficient(x, 1);
c1->SetCoefficient(y, 0);
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!";
}
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";
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";
}
void RunAllExamples() {
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);
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) {

View File

@@ -12,7 +12,7 @@
// limitations under the License.
#include <iomanip>
#include <numeric> // std::iota
#include <numeric> // std::iota
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/constraint_solver.h"
@@ -20,185 +20,170 @@
// Solve a job shop problem:
namespace operations_research {
void SolveJobShopExample() {
// Instantiate the solver.
Solver solver("JobShopExample");
std::array<int, 3> machines;
std::iota(std::begin(machines), std::end(machines), 0);
{
std::ostringstream oss;
for (auto i : machines)
oss << ' ' << i;
LOG(INFO) << "Machines: " << oss.str();
}
void SolveJobShopExample() {
// Instantiate the solver.
Solver solver("JobShopExample");
std::array<int, 3> machines;
std::iota(std::begin(machines), std::end(machines), 0);
{
std::ostringstream oss;
for (auto i : machines) oss << ' ' << i;
LOG(INFO) << "Machines: " << oss.str();
}
// Jobs definition
using MachineIndex = int;
using ProcessingTime = int;
using Task = std::pair<MachineIndex, ProcessingTime>;
using Job = std::vector<Task>;
std::vector<Job> jobs = {
{{0, 3}, {1, 2}, {2, 2}},
{{0, 2}, {2, 1}, {1, 4}},
{{1, 4}, {2, 3}}
};
LOG(INFO) << "Jobs:";
for (int i=0; i < jobs.size(); ++i) {
std::ostringstream problem;
problem << "Job "<< i << ": [";
for (const Task& task: jobs[i]) {
problem << "(" << task.first << ", " << task.second << ")";
}
problem << "]" << std::endl;
LOG(INFO) << problem.str();
}
// Jobs definition
using MachineIndex = int;
using ProcessingTime = int;
using Task = std::pair<MachineIndex, ProcessingTime>;
using Job = std::vector<Task>;
std::vector<Job> jobs = {
{{0, 3}, {1, 2}, {2, 2}}, {{0, 2}, {2, 1}, {1, 4}}, {{1, 4}, {2, 3}}};
LOG(INFO) << "Jobs:";
for (int i = 0; i < jobs.size(); ++i) {
std::ostringstream problem;
problem << "Job " << i << ": [";
for (const Task& task : jobs[i]) {
problem << "(" << task.first << ", " << task.second << ")";
}
problem << "]" << std::endl;
LOG(INFO) << problem.str();
}
// Computes horizon.
ProcessingTime horizon = 0;
for (const Job& job: jobs) {
for (const Task& task: job) {
horizon += task.second;
}
}
LOG(INFO) << "Horizon: " << horizon;
// Computes horizon.
ProcessingTime horizon = 0;
for (const Job& job : jobs) {
for (const Task& task : job) {
horizon += task.second;
}
}
LOG(INFO) << "Horizon: " << horizon;
// Creates tasks.
std::vector<std::vector<IntervalVar*>> tasks_matrix(jobs.size());
for (int i=0; i < jobs.size(); ++i) {
for (int j=0; j < jobs[i].size(); ++j) {
std::ostringstream oss;
oss << "Job_" << i << "_" << j;
tasks_matrix[i].push_back(
solver.MakeFixedDurationIntervalVar(
0,
horizon,
jobs[i][j].second,
false,
oss.str()));
}
}
// Creates tasks.
std::vector<std::vector<IntervalVar*>> tasks_matrix(jobs.size());
for (int i = 0; i < jobs.size(); ++i) {
for (int j = 0; j < jobs[i].size(); ++j) {
std::ostringstream oss;
oss << "Job_" << i << "_" << j;
tasks_matrix[i].push_back(solver.MakeFixedDurationIntervalVar(
0, horizon, jobs[i][j].second, false, oss.str()));
}
}
// Add conjunctive contraints.
for (int i=0; i < jobs.size(); ++i) {
for (int j=0; j < jobs[i].size()-1; ++j) {
solver.AddConstraint(
solver.MakeIntervalVarRelation(
tasks_matrix[i][j+1],
Solver::STARTS_AFTER_END,
tasks_matrix[i][j]));
}
}
// Add conjunctive contraints.
for (int i = 0; i < jobs.size(); ++i) {
for (int j = 0; j < jobs[i].size() - 1; ++j) {
solver.AddConstraint(solver.MakeIntervalVarRelation(
tasks_matrix[i][j + 1], Solver::STARTS_AFTER_END,
tasks_matrix[i][j]));
}
}
// Creates sequence variables and add disjunctive constraints.
std::vector<SequenceVar*> all_sequences;
std::vector<IntVar*> all_machines_jobs;
for (const auto machine: machines) {
std::vector<IntervalVar*> machines_jobs;
for (int i=0; i < jobs.size(); ++i) {
for (int j=0; j < jobs[i].size(); ++j) {
if (jobs[i][j].first == machine)
machines_jobs.push_back(tasks_matrix[i][j]);
}
}
DisjunctiveConstraint* const disj =
solver.MakeDisjunctiveConstraint(
machines_jobs,
"Machine_" + std::to_string(machine));
solver.AddConstraint(disj);
all_sequences.push_back(disj->MakeSequenceVar());
}
// Creates sequence variables and add disjunctive constraints.
std::vector<SequenceVar*> all_sequences;
std::vector<IntVar*> all_machines_jobs;
for (const auto machine : machines) {
std::vector<IntervalVar*> machines_jobs;
for (int i = 0; i < jobs.size(); ++i) {
for (int j = 0; j < jobs[i].size(); ++j) {
if (jobs[i][j].first == machine)
machines_jobs.push_back(tasks_matrix[i][j]);
}
}
DisjunctiveConstraint* const disj = solver.MakeDisjunctiveConstraint(
machines_jobs, "Machine_" + std::to_string(machine));
solver.AddConstraint(disj);
all_sequences.push_back(disj->MakeSequenceVar());
}
// Set the objective.
std::vector<IntVar*> all_ends;
for (const auto& job: tasks_matrix) {
IntervalVar* const task = job.back();
all_ends.push_back(task->EndExpr()->Var());
}
IntVar* const obj_var = solver.MakeMax(all_ends)->Var();
OptimizeVar* const objective_monitor = solver.MakeMinimize(obj_var, 1);
// Set the objective.
std::vector<IntVar*> all_ends;
for (const auto& job : tasks_matrix) {
IntervalVar* const task = job.back();
all_ends.push_back(task->EndExpr()->Var());
}
IntVar* const obj_var = solver.MakeMax(all_ends)->Var();
OptimizeVar* const objective_monitor = solver.MakeMinimize(obj_var, 1);
// ----- Search monitors and decision builder -----
// ----- Search monitors and decision builder -----
// This decision builder will rank all tasks on all machines.
DecisionBuilder* const sequence_phase = solver.MakePhase(
all_sequences,
Solver::SEQUENCE_DEFAULT);
// This decision builder will rank all tasks on all machines.
DecisionBuilder* const sequence_phase =
solver.MakePhase(all_sequences, Solver::SEQUENCE_DEFAULT);
// After the ranking of tasks, the schedule is still loose and any
// task can be postponed at will. But, because the problem is now a PERT
// (http://en.wikipedia.org/wiki/Program_Evaluation_and_Review_Technique),
// we can schedule each task at its earliest start time. This is
// conveniently done by fixing the objective variable to its
// minimum value.
DecisionBuilder* const obj_phase = solver.MakePhase(
obj_var,
Solver::CHOOSE_FIRST_UNBOUND,
Solver::ASSIGN_MIN_VALUE);
// After the ranking of tasks, the schedule is still loose and any
// task can be postponed at will. But, because the problem is now a PERT
// (http://en.wikipedia.org/wiki/Program_Evaluation_and_Review_Technique),
// we can schedule each task at its earliest start time. This is
// conveniently done by fixing the objective variable to its
// minimum value.
DecisionBuilder* const obj_phase = solver.MakePhase(
obj_var, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE);
// The main decision builder (ranks all tasks, then fixes the
// objective_variable).
DecisionBuilder* const main_phase = solver.Compose(sequence_phase, obj_phase);
// The main decision builder (ranks all tasks, then fixes the
// objective_variable).
DecisionBuilder* const main_phase = solver.Compose(sequence_phase, obj_phase);
// Search log.
const int kLogFrequency = 1000000;
SearchMonitor* const search_log =
solver.MakeSearchLog(kLogFrequency, objective_monitor);
// Search log.
const int kLogFrequency = 1000000;
SearchMonitor* const search_log =
solver.MakeSearchLog(kLogFrequency, objective_monitor);
SearchLimit* limit = nullptr;
SearchLimit* limit = nullptr;
// Create the solution collector.
SolutionCollector* const collector = solver.MakeLastSolutionCollector();
collector->Add(all_sequences);
collector->AddObjective(obj_var);
for (const auto machine: machines) {
SequenceVar* const sequence = all_sequences[machine];
for (int i=0; i < sequence->size(); ++i) {
IntervalVar* const t = sequence->Interval(i);
collector->Add(t->StartExpr()->Var());
collector->Add(t->EndExpr()->Var());
}
}
// Create the solution collector.
SolutionCollector* const collector = solver.MakeLastSolutionCollector();
collector->Add(all_sequences);
collector->AddObjective(obj_var);
for (const auto machine : machines) {
SequenceVar* const sequence = all_sequences[machine];
for (int i = 0; i < sequence->size(); ++i) {
IntervalVar* const t = sequence->Interval(i);
collector->Add(t->StartExpr()->Var());
collector->Add(t->EndExpr()->Var());
}
}
// Solve the problem.
if (solver.Solve(main_phase, search_log, objective_monitor, limit, collector)) {
LOG(INFO) << "Optimal Schedule Length: " << collector->objective_value(0);
LOG(INFO) << "";
// Solve the problem.
if (solver.Solve(main_phase, search_log, objective_monitor, limit,
collector)) {
LOG(INFO) << "Optimal Schedule Length: " << collector->objective_value(0);
LOG(INFO) << "";
LOG(INFO) << "Optimal Schedule:";
std::vector<std::string> machine_intervals_list;
for (const auto machine: machines) {
std::ostringstream machine_tasks;
SequenceVar* seq = all_sequences[machine];
machine_tasks << "Machine " << machine << ": ";
for (const auto s: collector->ForwardSequence(0, seq)) {
machine_tasks << seq->Interval(s)->name() << " ";
}
LOG(INFO) << machine_tasks.str();
LOG(INFO) << "Optimal Schedule:";
std::vector<std::string> machine_intervals_list;
for (const auto machine : machines) {
std::ostringstream machine_tasks;
SequenceVar* seq = all_sequences[machine];
machine_tasks << "Machine " << machine << ": ";
for (const auto s : collector->ForwardSequence(0, seq)) {
machine_tasks << seq->Interval(s)->name() << " ";
}
LOG(INFO) << machine_tasks.str();
std::ostringstream machine_intervals;
machine_intervals << "Machine " << machine << ": ";
for (const auto s: collector->ForwardSequence(0, seq)) {
IntervalVar* t = seq->Interval(s);
machine_intervals << "["
<< std::setw(2) << collector->Value(0, t->StartExpr()->Var()) << ", "
<< std::setw(2) << collector->Value(0, t->EndExpr()->Var()) << "] ";
}
machine_intervals_list.push_back(machine_intervals.str());
}
LOG(INFO) << "Time Intervals for Tasks: ";
for (const auto& intervals: machine_intervals_list) {
LOG(INFO) << intervals;
}
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Time: " << solver.wall_time() << "ms";
}
}
std::ostringstream machine_intervals;
machine_intervals << "Machine " << machine << ": ";
for (const auto s : collector->ForwardSequence(0, seq)) {
IntervalVar* t = seq->Interval(s);
machine_intervals << "[" << std::setw(2)
<< collector->Value(0, t->StartExpr()->Var()) << ", "
<< std::setw(2)
<< collector->Value(0, t->EndExpr()->Var()) << "] ";
}
machine_intervals_list.push_back(machine_intervals.str());
}
LOG(INFO) << "Time Intervals for Tasks: ";
for (const auto& intervals : machine_intervals_list) {
LOG(INFO) << intervals;
}
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Time: " << solver.wall_time() << "ms";
}
}
} // namespace operations_research
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::SolveJobShopExample();
return EXIT_SUCCESS;
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::SolveJobShopExample();
return EXIT_SUCCESS;
}

View File

@@ -55,7 +55,8 @@ int64 ComputeHorizon(const JsspInputProblem& problem) {
int64 max_earliest_start = 0;
for (const Job& job : problem.jobs()) {
if (job.has_latest_end()) {
max_latest_end = std::max<int64>(max_latest_end, job.latest_end().value());
max_latest_end =
std::max<int64>(max_latest_end, job.latest_end().value());
} else {
max_latest_end = kint64max;
}
@@ -80,8 +81,8 @@ int64 ComputeHorizon(const JsspInputProblem& problem) {
for (int i = 0; i < num_jobs; ++i) {
int64 max_transition = 0;
for (int j = 0; j < num_jobs; ++j) {
max_transition =
std::max<int64>(max_transition, matrix.transition_time(i * num_jobs + j));
max_transition = std::max<int64>(
max_transition, matrix.transition_time(i * num_jobs + j));
}
sum_of_transitions += max_transition;
}

View File

@@ -13,32 +13,29 @@
// Bi-dimensional knapsack problem.
#include <iterator>
#include <numeric>
#include <sstream>
#include <iterator>
#include "ortools/base/logging.h"
#include "ortools/algorithms/knapsack_solver.h"
#include "ortools/base/logging.h"
namespace operations_research {
void RunKnapsackExample() {
// Instantiate the solver.
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
};
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
}
};
{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};
@@ -47,29 +44,32 @@ namespace operations_research {
// Print solution
std::vector<int> packed_items;
for (std::size_t i=0; i < values.size(); ++i) {
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, ", "));
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) {
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, ", "));
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);
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) {

View File

@@ -18,60 +18,61 @@
#include "ortools/linear_solver/linear_solver.pb.h"
namespace operations_research {
void RunLinearProgrammingExample() {
MPSolver solver("LinearProgrammingExample", MPSolver::GLOP_LINEAR_PROGRAMMING);
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");
void RunLinearProgrammingExample() {
MPSolver solver("LinearProgrammingExample",
MPSolver::GLOP_LINEAR_PROGRAMMING);
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");
// Objectif function: Maximize 3x + 4y.
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x, 3);
objective->SetCoefficient(y, 4);
objective->SetMaximization();
// Objectif function: Maximize 3x + 4y.
MPObjective* const objective = solver.MutableObjective();
objective->SetCoefficient(x, 3);
objective->SetCoefficient(y, 4);
objective->SetMaximization();
// x + 2y <= 14.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 14.0);
c0->SetCoefficient(x, 1);
c0->SetCoefficient(y, 2);
// x + 2y <= 14.
MPConstraint* const c0 = solver.MakeRowConstraint(-infinity, 14.0);
c0->SetCoefficient(x, 1);
c0->SetCoefficient(y, 2);
// 3x - y >= 0.
MPConstraint* const c1 = solver.MakeRowConstraint(0.0, infinity);
c1->SetCoefficient(x, 3);
c1->SetCoefficient(y, -1);
// 3x - y >= 0.
MPConstraint* const c1 = solver.MakeRowConstraint(0.0, infinity);
c1->SetCoefficient(x, 3);
c1->SetCoefficient(y, -1);
// x - y <= 2.
MPConstraint* const c2 = solver.MakeRowConstraint(-infinity, 2.0);
c2->SetCoefficient(x, 1);
c2->SetCoefficient(y, -1);
// x - y <= 2.
MPConstraint* const c2 = solver.MakeRowConstraint(-infinity, 2.0);
c2->SetCoefficient(x, 1);
c2->SetCoefficient(y, -1);
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!";
}
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()];
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()];
}
} // namespace operations_research
int main(int argc, char** argv) {

View File

@@ -106,7 +106,9 @@ void MagicSquare(int grid_size) {
DefaultPhaseParameters::CHOOSE_MAX_VALUE_IMPACT;
break;
}
default: { LOG(FATAL) << "Should not be here"; }
default: {
LOG(FATAL) << "Should not be here";
}
}
parameters.value_selection_schema =
FLAGS_select_max_impact_value ? DefaultPhaseParameters::SELECT_MAX_IMPACT

View File

@@ -11,53 +11,43 @@
// 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"
#include "ortools/base/logging.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);
}
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) {

View File

@@ -11,76 +11,62 @@
// 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"
#include "ortools/base/logging.h"
namespace operations_research {
struct Arc {
std::pair<NodeIndex, NodeIndex> nodes;
FlowQuantity capacity;
FlowQuantity unit_cost;
};
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}
};
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}
};
// 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);
}
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) {
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::SolveMinCostFlow();

View File

@@ -299,12 +299,12 @@ void SolveKnapsack(MultiDimKnapsackData* const data) {
SearchMonitor* const search_log = solver.MakeSearchLog(1000000, objective);
monitors.push_back(search_log);
}
DecisionBuilder* const db =
solver.MakePhase(assign,
[data](int64 var, int64 value) {
return EvaluateItem(*data, var, value);
},
Solver::CHOOSE_STATIC_GLOBAL_BEST);
DecisionBuilder* const db = solver.MakePhase(
assign,
[data](int64 var, int64 value) {
return EvaluateItem(*data, var, value);
},
Solver::CHOOSE_STATIC_GLOBAL_BEST);
if (FLAGS_time_limit_in_ms != 0) {
LOG(INFO) << "adding time limit of " << FLAGS_time_limit_in_ms << " ms";
SearchLimit* const limit = solver.MakeLimit(

View File

@@ -471,9 +471,10 @@ class NetworkRoutingSolver {
for (int demand_index = 0; demand_index < num_demands; ++demand_index) {
paths.clear();
const Demand& demand = demands_array_[demand_index];
CHECK(DijkstraShortestPath(num_nodes_, demand.source, demand.destination,
[this](int x, int y) { return HasArc(x, y); },
kDisconnectedDistance, &paths));
CHECK(DijkstraShortestPath(
num_nodes_, demand.source, demand.destination,
[this](int x, int y) { return HasArc(x, y); }, kDisconnectedDistance,
&paths));
all_min_path_lengths_.push_back(paths.size() - 1);
}

View File

@@ -43,8 +43,8 @@ void NQueens(int size) {
s.AddConstraint(s.MakeNonEquality(queens[i], queens[j]));
s.AddConstraint(
s.MakeNonEquality(s.MakeSum(queens[i], i), s.MakeSum(queens[j], j)));
s.AddConstraint(s.MakeNonEquality(
s.MakeSum(queens[i], -i), s.MakeSum(queens[j], -j)));
s.AddConstraint(s.MakeNonEquality(s.MakeSum(queens[i], -i),
s.MakeSum(queens[j], -j)));
}
}
std::vector<SearchMonitor*> monitors;

View File

@@ -11,209 +11,194 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <numeric> // std::iota
#include <numeric> // std::iota
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/constraint_solver.h"
namespace operations_research {
void SolveNursesExample() {
// Instantiate the solver.
Solver solver("NursesExample");
std::array<int, 4> nurses;
std::iota(std::begin(nurses), std::end(nurses), 0);
{
std::ostringstream oss;
for (auto i : nurses)
oss << ' ' << i;
LOG(INFO) << "Nurses:" << oss.str();
}
// Nurse assigned to shift 0 means not working that day.
std::array<int, 4> shifts;
std::iota(std::begin(shifts), std::end(shifts), 0);
{
std::ostringstream oss;
for (auto i : shifts)
oss << ' ' << i;
LOG(INFO) << "Shifts:" << oss.str();
}
std::array<int, 7> days;
std::iota(std::begin(days), std::end(days), 0);
{
std::ostringstream oss;
for (auto i : days)
oss << ' ' << i;
LOG(INFO) << "Days:" << oss.str();
}
// Create shift variables.
std::vector<std::vector<IntVar *>> shifts_matrix(nurses.size());
std::vector<IntVar *> shifts_flat;
for (const auto nurse: nurses) {
for (const auto day: days) {
std::ostringstream oss;
oss << "shifts(nurse: " << nurse << ", day: " << day << ")";
IntVar *var = solver.MakeIntVar(shifts.front(), shifts.back(), oss.str());
shifts_matrix[nurse].push_back(var);
shifts_flat.push_back(var);
}
}
// Create nurse variables.
std::vector<std::vector<IntVar *>> nurses_matrix(shifts.size());
for (const auto shift: shifts) {
for (const auto day: days) {
std::ostringstream oss;
oss << "nurses(shift: " << shift << ", day: " << day << ")";
IntVar *var = solver.MakeIntVar(nurses.front(), nurses.back(), oss.str());
nurses_matrix[shift].push_back(var);
}
}
// Set relationships between shifts and nurses.
for (const auto day :days) {
std::vector<IntVar *> nurses_for_day(shifts.size());
for (const auto shift: shifts) {
nurses_for_day[shift] = nurses_matrix[shift][day];
}
for (const auto nurse: nurses) {
IntVar* s = shifts_matrix[nurse][day];
solver.AddConstraint(
solver.MakeEquality(
solver.MakeElement(nurses_for_day, s),
nurse));
}
}
// Make assignments different on each day i.e.
for (const auto day: days) {
// no shift can have two nurses
std::vector<IntVar *> shifts_for_day(nurses.size());
for (const auto nurse: nurses) {
shifts_for_day[nurse] = shifts_matrix[nurse][day];
}
solver.AddConstraint(solver.MakeAllDifferent(shifts_for_day));
// no nurses can have more than one shifts a day
std::vector<IntVar *> nurses_for_day(shifts.size());
for (const auto shift: shifts) {
nurses_for_day[shift] = nurses_matrix[shift][day];
}
solver.AddConstraint(solver.MakeAllDifferent(nurses_for_day));
}
//Each nurse works 5 or 6 days in a week.
for (const auto nurse: nurses) {
std::vector<IntVar *> nurse_is_working;
for (const auto day: days) {
nurse_is_working.push_back(
solver.MakeIsGreaterOrEqualCstVar(
shifts_matrix[nurse][day], 1));
}
solver.AddConstraint(solver.MakeSumGreaterOrEqual(nurse_is_working, 5));
solver.AddConstraint(solver.MakeSumLessOrEqual(nurse_is_working, 6));
}
// Create works_shift variables.
// works_shift_matrix[n][s] is True if
// nurse n works shift s at least once during the week.
std::vector<std::vector<IntVar *>> works_shift_matrix(nurses.size());
for (const auto nurse: nurses) {
for (const auto shift: shifts) {
std::ostringstream oss;
oss << "work_shift(nurse: " << nurse << ", shift: " << shift << ")";
works_shift_matrix[nurse].push_back(solver.MakeBoolVar(oss.str()));
}
}
for (const auto nurse: nurses) {
for (const auto shift: shifts) {
std::vector<IntVar *> shift_s_for_nurse;
for (const auto day: days) {
shift_s_for_nurse.push_back(
solver.MakeIsEqualCstVar(
shifts_matrix[nurse][day],
shift));
}
solver.AddConstraint(
solver.MakeEquality(
works_shift_matrix[nurse][shift],
solver.MakeMax(shift_s_for_nurse)->Var()));
}
}
// For each shift(other than 0), at most 2 nurses are assigned to that shift
// during the week.
for (std::size_t shift=1; shift < shifts.size(); ++shift) {
std::vector<IntVar *> nurses_for_shift;
for (const auto nurse: nurses) {
nurses_for_shift.push_back(works_shift_matrix[nurse][shift]);
}
solver.AddConstraint(solver.MakeSumLessOrEqual(nurses_for_shift, 2));
}
// If a nurse works shifts 2 or 3 on,
// he must also work that shift the previous day or the following day.
for (const auto shift: {2, 3}) {
for (const auto day: days) {
IntVar* v0 = solver.MakeIsEqualVar(
nurses_matrix[shift][day],
nurses_matrix[shift][(day + 1) % 7]);
IntVar* v1 = solver.MakeIsEqualVar(
nurses_matrix[shift][(day + 1) % 7],
nurses_matrix[shift][(day + 2) % 7]);
solver.AddConstraint(
solver.MakeEquality(
solver.MakeMax(v0, v1),
1));
}
}
// ----- Search monitors and decision builder -----
// Create the decision builder.
DecisionBuilder* const main_phase = solver.MakePhase(
shifts_flat,
Solver::CHOOSE_FIRST_UNBOUND,
Solver::ASSIGN_MIN_VALUE);
// Search log.
SearchMonitor* const search_log = nullptr;
SearchLimit* limit = nullptr;
// Create the solution collector.
SolutionCollector* const collector = solver.MakeAllSolutionCollector();
collector->Add(shifts_flat);
// Solve
solver.Solve(main_phase, search_log, nullptr, limit, collector);
LOG(INFO) << "Number of solutions: " << collector->solution_count();
LOG(INFO) << "";
// Display a few solutions picked at random.
std::array<int ,4> a_few_solutions = {859, 2034, 5091, 7003};
for (const auto solution: a_few_solutions) {
LOG(INFO) << "Solution " << solution << ":";
for (const auto day: days) {
LOG(INFO) << "Day " << day << ":";
for (const auto nurse: nurses) {
LOG(INFO)
<< "Nurse " << nurse << " assigned to "
<< "Task " << collector->Value(solution, shifts_flat[nurse * days.size() + day]);
}
}
}
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Time: " << solver.wall_time() << "ms";
void SolveNursesExample() {
// Instantiate the solver.
Solver solver("NursesExample");
std::array<int, 4> nurses;
std::iota(std::begin(nurses), std::end(nurses), 0);
{
std::ostringstream oss;
for (auto i : nurses) oss << ' ' << i;
LOG(INFO) << "Nurses:" << oss.str();
}
// Nurse assigned to shift 0 means not working that day.
std::array<int, 4> shifts;
std::iota(std::begin(shifts), std::end(shifts), 0);
{
std::ostringstream oss;
for (auto i : shifts) oss << ' ' << i;
LOG(INFO) << "Shifts:" << oss.str();
}
std::array<int, 7> days;
std::iota(std::begin(days), std::end(days), 0);
{
std::ostringstream oss;
for (auto i : days) oss << ' ' << i;
LOG(INFO) << "Days:" << oss.str();
}
// Create shift variables.
std::vector<std::vector<IntVar *>> shifts_matrix(nurses.size());
std::vector<IntVar *> shifts_flat;
for (const auto nurse : nurses) {
for (const auto day : days) {
std::ostringstream oss;
oss << "shifts(nurse: " << nurse << ", day: " << day << ")";
IntVar *var = solver.MakeIntVar(shifts.front(), shifts.back(), oss.str());
shifts_matrix[nurse].push_back(var);
shifts_flat.push_back(var);
}
}
// Create nurse variables.
std::vector<std::vector<IntVar *>> nurses_matrix(shifts.size());
for (const auto shift : shifts) {
for (const auto day : days) {
std::ostringstream oss;
oss << "nurses(shift: " << shift << ", day: " << day << ")";
IntVar *var = solver.MakeIntVar(nurses.front(), nurses.back(), oss.str());
nurses_matrix[shift].push_back(var);
}
}
// Set relationships between shifts and nurses.
for (const auto day : days) {
std::vector<IntVar *> nurses_for_day(shifts.size());
for (const auto shift : shifts) {
nurses_for_day[shift] = nurses_matrix[shift][day];
}
for (const auto nurse : nurses) {
IntVar *s = shifts_matrix[nurse][day];
solver.AddConstraint(
solver.MakeEquality(solver.MakeElement(nurses_for_day, s), nurse));
}
}
// Make assignments different on each day i.e.
for (const auto day : days) {
// no shift can have two nurses
std::vector<IntVar *> shifts_for_day(nurses.size());
for (const auto nurse : nurses) {
shifts_for_day[nurse] = shifts_matrix[nurse][day];
}
solver.AddConstraint(solver.MakeAllDifferent(shifts_for_day));
// no nurses can have more than one shifts a day
std::vector<IntVar *> nurses_for_day(shifts.size());
for (const auto shift : shifts) {
nurses_for_day[shift] = nurses_matrix[shift][day];
}
solver.AddConstraint(solver.MakeAllDifferent(nurses_for_day));
}
// Each nurse works 5 or 6 days in a week.
for (const auto nurse : nurses) {
std::vector<IntVar *> nurse_is_working;
for (const auto day : days) {
nurse_is_working.push_back(
solver.MakeIsGreaterOrEqualCstVar(shifts_matrix[nurse][day], 1));
}
solver.AddConstraint(solver.MakeSumGreaterOrEqual(nurse_is_working, 5));
solver.AddConstraint(solver.MakeSumLessOrEqual(nurse_is_working, 6));
}
// Create works_shift variables.
// works_shift_matrix[n][s] is True if
// nurse n works shift s at least once during the week.
std::vector<std::vector<IntVar *>> works_shift_matrix(nurses.size());
for (const auto nurse : nurses) {
for (const auto shift : shifts) {
std::ostringstream oss;
oss << "work_shift(nurse: " << nurse << ", shift: " << shift << ")";
works_shift_matrix[nurse].push_back(solver.MakeBoolVar(oss.str()));
}
}
for (const auto nurse : nurses) {
for (const auto shift : shifts) {
std::vector<IntVar *> shift_s_for_nurse;
for (const auto day : days) {
shift_s_for_nurse.push_back(
solver.MakeIsEqualCstVar(shifts_matrix[nurse][day], shift));
}
solver.AddConstraint(
solver.MakeEquality(works_shift_matrix[nurse][shift],
solver.MakeMax(shift_s_for_nurse)->Var()));
}
}
// For each shift(other than 0), at most 2 nurses are assigned to that shift
// during the week.
for (std::size_t shift = 1; shift < shifts.size(); ++shift) {
std::vector<IntVar *> nurses_for_shift;
for (const auto nurse : nurses) {
nurses_for_shift.push_back(works_shift_matrix[nurse][shift]);
}
solver.AddConstraint(solver.MakeSumLessOrEqual(nurses_for_shift, 2));
}
// If a nurse works shifts 2 or 3 on,
// he must also work that shift the previous day or the following day.
for (const auto shift : {2, 3}) {
for (const auto day : days) {
IntVar *v0 = solver.MakeIsEqualVar(nurses_matrix[shift][day],
nurses_matrix[shift][(day + 1) % 7]);
IntVar *v1 = solver.MakeIsEqualVar(nurses_matrix[shift][(day + 1) % 7],
nurses_matrix[shift][(day + 2) % 7]);
solver.AddConstraint(solver.MakeEquality(solver.MakeMax(v0, v1), 1));
}
}
// ----- Search monitors and decision builder -----
// Create the decision builder.
DecisionBuilder *const main_phase = solver.MakePhase(
shifts_flat, Solver::CHOOSE_FIRST_UNBOUND, Solver::ASSIGN_MIN_VALUE);
// Search log.
SearchMonitor *const search_log = nullptr;
SearchLimit *limit = nullptr;
// Create the solution collector.
SolutionCollector *const collector = solver.MakeAllSolutionCollector();
collector->Add(shifts_flat);
// Solve
solver.Solve(main_phase, search_log, nullptr, limit, collector);
LOG(INFO) << "Number of solutions: " << collector->solution_count();
LOG(INFO) << "";
// Display a few solutions picked at random.
std::array<int, 4> a_few_solutions = {859, 2034, 5091, 7003};
for (const auto solution : a_few_solutions) {
LOG(INFO) << "Solution " << solution << ":";
for (const auto day : days) {
LOG(INFO) << "Day " << day << ":";
for (const auto nurse : nurses) {
LOG(INFO) << "Nurse " << nurse << " assigned to "
<< "Task "
<< collector->Value(solution,
shifts_flat[nurse * days.size() + day]);
}
}
}
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Time: " << solver.wall_time() << "ms";
}
} // namespace operations_research
int main(int argc, char **argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::SolveNursesExample();
return EXIT_SUCCESS;
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::SolveNursesExample();
return EXIT_SUCCESS;
}

View File

@@ -18,48 +18,48 @@
#include "ortools/constraint_solver/constraint_solver.h"
namespace operations_research {
void RunConstraintProgrammingExample() {
// Instantiate the solver.
Solver solver("RabbitsPheasantsExample");
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 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);
// 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);
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);
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";
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) {
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
FLAGS_logtostderr = 1;
operations_research::RunConstraintProgrammingExample();

View File

@@ -11,127 +11,125 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <vector>
#include <cmath>
#include <vector>
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/routing.h"
namespace operations_research {
class DataProblem {
private:
std::vector<std::vector<int>> locations_;
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}
};
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 1;}
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]);
}
}
// 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];
}
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";
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
}
void Solve() {
// Instantiate the data problem.
DataProblem data;
std::size_t GetVehicleNumber() const { return 1; }
const std::vector<std::vector<int>>& GetLocations() const {
return locations_;
}
RoutingModel::NodeIndex GetDepot() const { return RoutingModel::kFirstNode; }
};
// Create Routing Model
RoutingModel routing(
/*! @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(),
data.GetVehicleNumber(),
data.GetDepot());
// Define weight of each edge
ManhattanDistance distance(data);
routing.SetArcCostEvaluatorOfAllVehicles(NewPermanentCallback(&distance, &ManhattanDistance::Run));
// Setting first solution heuristic (cheapest addition).
RoutingSearchParameters searchParameters = RoutingModel::DefaultSearchParameters();
searchParameters.set_first_solution_strategy(FirstSolutionStrategy::PATH_CHEAPEST_ARC);
const Assignment* solution = routing.SolveWithParameters(searchParameters);
PrintSolution(data, routing, *solution);
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 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";
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
}
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));
// Setting first solution heuristic (cheapest addition).
RoutingSearchParameters 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) {

View File

@@ -17,14 +17,12 @@ void Solve() {
const IntVar start_p1 = cp_model.NewIntVar(Domain(500, 800));
const IntVar duration_p1 = cp_model.NewIntVar(Domain(1, 360));
const IntVar end_p1 = cp_model.NewIntVar(Domain(500, 1000));
const IntervalVar p1 =
cp_model.NewIntervalVar(start_p1, duration_p1, end_p1);
const IntervalVar p1 = cp_model.NewIntervalVar(start_p1, duration_p1, end_p1);
const IntVar start_p2 = cp_model.NewIntVar(Domain(500, 800));
const IntVar duration_p2 = cp_model.NewIntVar(Domain(1, 360));
const IntVar end_p2 = cp_model.NewIntVar(Domain(500, 1000));
const IntervalVar p2 =
cp_model.NewIntervalVar(start_p2, duration_p2, end_p2);
const IntervalVar p2 = cp_model.NewIntervalVar(start_p2, duration_p2, end_p2);
cp_model.AddEquality(LinearExpr::Sum({duration_p1, duration_p2}), 360);
cp_model.AddLessOrEqual(end_p1, start_p2);

View File

@@ -11,149 +11,144 @@
// See the License for the specific language governing permissions and
// limitations under the License.
#include <vector>
#include <cmath>
#include <vector>
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/routing.h"
namespace operations_research {
class DataProblem {
private:
std::vector<std::vector<int>> locations_;
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}
};
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]);
}
}
// 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];
}
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";
}
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
std::size_t GetVehicleNumber() const { return 4; }
const std::vector<std::vector<int>>& GetLocations() const {
return locations_;
}
RoutingModel::NodeIndex GetDepot() const { return RoutingModel::kFirstNode; }
};
void Solve() {
// Instantiate the data problem.
DataProblem data;
/*! @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_;
// Create Routing Model
RoutingModel routing(
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(),
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);
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";
}
LOG(INFO) << "";
LOG(INFO) << "Advanced usage:";
LOG(INFO) << "Problem solved in " << routing.solver()->wall_time() << "ms";
}
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) {

View File

@@ -22,8 +22,8 @@
#include "ortools/base/logging.h"
#include "ortools/base/numbers.h"
#include "ortools/base/split.h"
#include "ortools/base/strutil.h"
#include "ortools/base/strtoint.h"
#include "ortools/base/strutil.h"
#include "ortools/base/timer.h"
#include "ortools/sat/cp_model.h"
#include "ortools/sat/model.h"