Files
ortools-clone/examples/cpp/job_shop_cp.cc
2018-10-10 08:09:08 +02:00

205 lines
6.5 KiB
C++

// Copyright 2018 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <iomanip>
#include <numeric> // std::iota
#include "ortools/base/logging.h"
#include "ortools/constraint_solver/constraint_solver.h"
// 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();
}
// 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;
// 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]));
}
}
// 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);
// ----- 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);
// 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);
// Search log.
const int kLogFrequency = 1000000;
SearchMonitor* const search_log =
solver.MakeSearchLog(kLogFrequency, objective_monitor);
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());
}
}
// 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();
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 0;
}