From a265ee08385bad303f2d7787f2f63b386227387c Mon Sep 17 00:00:00 2001 From: "lperron@google.com" Date: Mon, 16 Jul 2012 18:40:45 +0000 Subject: [PATCH] skeleton of early tardy jobshop --- examples/cpp/jobshop_earlytardy.cc | 190 +++++++++++++++++++++++++++++ examples/cpp/jobshop_earlytardy.h | 111 +++++++++++++++++ makefiles/Makefile.cpp.mk | 6 + 3 files changed, 307 insertions(+) create mode 100644 examples/cpp/jobshop_earlytardy.cc create mode 100644 examples/cpp/jobshop_earlytardy.h diff --git a/examples/cpp/jobshop_earlytardy.cc b/examples/cpp/jobshop_earlytardy.cc new file mode 100644 index 0000000000..18111c6060 --- /dev/null +++ b/examples/cpp/jobshop_earlytardy.cc @@ -0,0 +1,190 @@ +// Copyright 2010-2012 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This model implements a simple jobshop problem. +// +// A jobshop is a standard scheduling problem where you must schedule a +// set of jobs on a set of machines. Each job is a sequence of tasks +// (a task can only start when the preceding task finished), each of +// which occupies a single specific machine during a specific +// duration. Therefore, a job is simply given by a sequence of pairs +// (machine id, duration). + +// The objective is to minimize the 'makespan', which is the duration +// between the start of the first task (across all machines) and the +// completion of the last task (across all machines). +// +// This will be modelled by sets of intervals variables (see class +// IntervalVar in constraint_solver/constraint_solver.h), one per +// task, representing the [start_time, end_time] of the task. Tasks +// in the same job will be linked by precedence constraints. Tasks on +// the same machine will be covered by Sequence constraints. +// +// Search will then be applied on the sequence constraints. + +#include +#include + +#include "base/commandlineflags.h" +#include "base/commandlineflags.h" +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "constraint_solver/constraint_solver.h" +#include "cpp/jobshop_earlytardy.h" + +DEFINE_string( + data_file, + "", + "Required: input file description the scheduling problem to solve, " + "in our jssp format:\n" + " - the first line is \"instance \"\n" + " - the second line is \" \"\n" + " - then one line per job, with a single space-separated " + "list of \" \"\n" + "note: jobs with one task are not supported"); +DEFINE_int32(time_limit_in_ms, 0, "Time limit in ms, 0 means no limit."); + +namespace operations_research { +void EtJobShop(const EtJobShopData& data) { + Solver solver("et_jobshop"); + const int machine_count = data.machine_count(); + const int job_count = data.job_count(); + const int horizon = data.horizon(); + + // ----- Creates all Intervals and vars ----- + + // Stores all tasks attached interval variables per job. + std::vector > jobs_to_tasks(job_count); + // machines_to_tasks stores the same interval variables as above, but + // grouped my machines instead of grouped by jobs. + std::vector > machines_to_tasks(machine_count); + + // Creates all individual interval variables. + for (int job_id = 0; job_id < job_count; ++job_id) { + const Job& job = data.GetJob(job_id); + const std::vector& tasks = job.all_tasks; + for (int task_index = 0; task_index < tasks.size(); ++task_index) { + const Task& task = tasks[task_index]; + CHECK_EQ(job_id, task.job_id); + const string name = StringPrintf("J%dM%dI%dD%d", + task.job_id, + task.machine_id, + task_index, + task.duration); + IntervalVar* const one_task = + solver.MakeFixedDurationIntervalVar(0, + horizon, + task.duration, + false, + name); + jobs_to_tasks[task.job_id].push_back(one_task); + machines_to_tasks[task.machine_id].push_back(one_task); + } + } + + // ----- Creates model ----- + + // Creates precedences inside jobs. + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + for (int task_index = 0; task_index < task_count - 1; ++task_index) { + IntervalVar* const t1 = jobs_to_tasks[job_id][task_index]; + IntervalVar* const t2 = jobs_to_tasks[job_id][task_index + 1]; + Constraint* const prec = + solver.MakeIntervalVarRelation(t2, Solver::STARTS_AFTER_END, t1); + solver.AddConstraint(prec); + } + } + + // Adds disjunctive constraints on unary resources. + for (int machine_id = 0; machine_id < machine_count; ++machine_id) { + solver.AddConstraint( + solver.MakeDisjunctiveConstraint(machines_to_tasks[machine_id])); + } + + // Creates sequences variables on machines. A sequence variable is a + // dedicated variable whose job is to sequence interval variables. + std::vector all_sequences; + for (int machine_id = 0; machine_id < machine_count; ++machine_id) { + const string name = StringPrintf("Machine_%d", machine_id); + SequenceVar* const sequence = + solver.MakeSequenceVar(machines_to_tasks[machine_id], name); + all_sequences.push_back(sequence); + } + + // Creates array of end_times of jobs. + std::vector all_ends; + for (int job_id = 0; job_id < job_count; ++job_id) { + const int task_count = jobs_to_tasks[job_id].size(); + IntervalVar* const task = jobs_to_tasks[job_id][task_count - 1]; + all_ends.push_back(task->EndExpr()->Var()); + } + + // Objective: minimize the makespan (maximum end times of all tasks) + // of the problem. + IntVar* const objective_var = solver.MakeMax(all_ends)->Var(); + OptimizeVar* const objective_monitor = solver.MakeMinimize(objective_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(objective_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 = NULL; + if (FLAGS_time_limit_in_ms > 0) { + limit = solver.MakeTimeLimit(FLAGS_time_limit_in_ms); + } + + // Search. + solver.Solve(main_phase, search_log, objective_monitor, limit); +} +} // namespace operations_research + +static const char kUsage[] = + "Usage: see flags.\nThis program runs a simple job shop optimization " + "output besides the debug LOGs of the solver."; + +int main(int argc, char **argv) { + google::SetUsageMessage(kUsage); + google::ParseCommandLineFlags(&argc, &argv, true); + if (FLAGS_data_file.empty()) { + LOG(FATAL) << "Please supply a data file with --data_file="; + } + operations_research::EtJobShopData data; + data.Load(FLAGS_data_file); + operations_research::EtJobShop(data); + return 0; +} diff --git a/examples/cpp/jobshop_earlytardy.h b/examples/cpp/jobshop_earlytardy.h new file mode 100644 index 0000000000..a488f0161e --- /dev/null +++ b/examples/cpp/jobshop_earlytardy.h @@ -0,0 +1,111 @@ +// Copyright 2010-2012 Google +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// This model implements a simple jobshop problem. +// +// A jobshop is a standard scheduling problem where you must schedule +// a set of jobs on a set of machines. Each job is a sequence of +// tasks (a task can only start when the preceding task finished), +// each of which occupies a single specific machine during a specific +// duration. Therefore, a job is a sequence of pairs (machine id, +// duration), along with a release data (minimum start date of the +// first task of the job, and due data (end time of the last job) with +// a tardiness linear penalty. + +// The objective is to minimize the 'makespan', which is the duration +// between the start of the first task (across all machines) and the +// completion of the last task (across all machines). +// +// This will be modelled by sets of intervals variables (see class +// IntervalVar in constraint_solver/constraint_solver.h), one per +// task, representing the [start_time, end_time] of the task. Tasks +// in the same job will be linked by precedence constraints. Tasks on +// the same machine will be covered by Sequence constraints. + +#ifndef OR_TOOLS_EXAMPLES_JOBSHOP_EARLYTARDY_H_ +#define OR_TOOLS_EXAMPLES_JOBSHOP_EARLYTARDY_H_ + +#include +#include + +#include + +#include "base/integral_types.h" +#include "base/logging.h" +#include "base/stringprintf.h" +#include "base/strtoint.h" +#include "base/file.h" +#include "base/filelinereader.h" +#include "base/split.h" + +namespace operations_research { +struct Task { + Task(int j, int m, int d) : job_id(j), machine_id(m), duration(d) {} + int job_id; + int machine_id; + int duration; +}; + +struct Job { + Job(int r, int d, int ew, int tw) + : release_date(r), + due_date(d), + earlyness_weight(ew), + tardiness_weight(tw) {} + int release_date; + int due_date; + int earlyness_weight; + int tardiness_weight; + std::vector all_tasks; +}; + +class EtJobShopData { + public: + EtJobShopData() + : machine_count_(0), + job_count_(0), + horizon_(0) {} + + ~EtJobShopData() {} + + void Load(const string& filename) { + + } + // The number of machines in the jobshop. + int machine_count() const { return machine_count_; } + + // The number of jobs in the jobshop. + int job_count() const { return job_count_; } + + // The name of the jobshop instance. + const string& name() const { return name_; } + + // The horizon of the workshop (the sum of all durations), which is + // a trivial upper bound of the optimal make_span. + int horizon() const { return horizon_; } + + // Returns the tasks of a job, ordered by precedence. + const Job& GetJob(int job_id) const { + return all_jobs_[job_id]; + } + + private: + string name_; + int machine_count_; + int job_count_; + int horizon_; + std::vector all_jobs_; +}; +} // namespace operations_research +#endif OR_TOOLS_EXAMPLES_JOBSHOP_EARLYTARDY_H_ + diff --git a/makefiles/Makefile.cpp.mk b/makefiles/Makefile.cpp.mk index 272a4e8df7..18c3fd6d89 100644 --- a/makefiles/Makefile.cpp.mk +++ b/makefiles/Makefile.cpp.mk @@ -672,6 +672,12 @@ $(OBJ_DIR)/jobshop_ls.$O:$(EX_DIR)/cpp/jobshop_ls.cc $(SRC_DIR)/constraint_solve $(BIN_DIR)/jobshop_ls$E: $(CP_DEPS) $(OBJ_DIR)/jobshop_ls.$O $(CCC) $(CFLAGS) $(OBJ_DIR)/jobshop_ls.$O $(CP_LNK) $(LDFLAGS) $(EXEOUT)jobshop_ls$E +$(OBJ_DIR)/jobshop_earlytardy.$O:$(EX_DIR)/cpp/jobshop_earlytardy.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(EX_DIR)/cpp/jobshop_earlytardy.h + $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/jobshop_earlytardy.cc $(OBJ_OUT)jobshop_earlytardy.$O + +$(BIN_DIR)/jobshop_earlytardy$E: $(CP_DEPS) $(OBJ_DIR)/jobshop_earlytardy.$O + $(CCC) $(CFLAGS) $(OBJ_DIR)/jobshop_earlytardy.$O $(CP_LNK) $(LDFLAGS) $(EXEOUT)jobshop_earlytardy$E + $(OBJ_DIR)/magic_square.$O:$(EX_DIR)/cpp/magic_square.cc $(SRC_DIR)/constraint_solver/constraint_solver.h $(CCC) $(CFLAGS) -c $(EX_DIR)$Scpp/magic_square.cc $(OBJ_OUT)magic_square.$O