Format examples/cpp
This commit is contained in:
@@ -129,7 +129,9 @@ class AcpData {
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { LOG(ERROR) << "Should not be here"; }
|
||||
default: {
|
||||
LOG(ERROR) << "Should not be here";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -98,7 +98,9 @@ class AcpData {
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: { LOG(ERROR) << "Should not be here"; }
|
||||
default: {
|
||||
LOG(ERROR) << "Should not be here";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(),
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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(
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user