21 #include "absl/strings/numbers.h"
22 #include "absl/strings/str_cat.h"
23 #include "absl/strings/str_split.h"
24 #include "google/protobuf/wrappers.pb.h"
29 #include "ortools/scheduling/jobshop_scheduling.pb.h"
33 "Scaling factor for floating point penalties.");
36 namespace scheduling {
39 void JsspParser::SetJobs(
int job_count) {
40 CHECK_GT(job_count, 0);
41 declared_job_count_ = job_count;
42 problem_.clear_jobs();
43 for (
int i = 0; i < job_count; ++i) {
44 problem_.add_jobs()->set_name(absl::StrCat(
"J", i));
48 void JsspParser::SetMachines(
int machine_count) {
49 CHECK_GT(machine_count, 0);
50 declared_machine_count_ = machine_count;
51 problem_.clear_machines();
52 for (
int i = 0; i < machine_count; ++i) {
53 problem_.add_machines()->set_name(absl::StrCat(
"M", i));
63 if (absl::EndsWith(filename,
"fjs")) {
65 }
else if (absl::EndsWith(filename,
".txt")) {
73 const std::string problem_name(
file::Stem(filename));
74 problem_.set_name(problem_name);
80 switch (problem_type_) {
82 ProcessJsspLine(
line);
86 ProcessTaillardLine(
line);
90 ProcessFlexibleLine(
line);
94 ProcessSdstLine(
line);
98 ProcessTardinessLine(
line);
102 ProcessPssLine(
line);
106 ProcessEarlyTardyLine(
line);
110 LOG(FATAL) <<
"Should not be here.";
118 void JsspParser::ProcessJsspLine(
const std::string&
line) {
119 const std::vector<std::string> words =
120 absl::StrSplit(
line,
' ', absl::SkipEmpty());
121 switch (parser_state_) {
123 if (words.size() == 2 && words[0] ==
"instance") {
124 problem_.set_name(words[1]);
126 current_job_index_ = 0;
127 }
else if (words.size() == 1 && words[0] ==
"1") {
129 }
else if (words.size() == 2) {
130 SetJobs(strtoint32(words[0]));
131 SetMachines(strtoint32(words[1]));
138 if (words.size() == 2) {
139 SetJobs(strtoint32(words[0]));
140 SetMachines(strtoint32(words[1]));
141 problem_.set_makespan_cost_per_time_unit(1L);
147 CHECK_GE(words.size(), declared_machine_count_ * 2);
148 Job*
const job = problem_.mutable_jobs(current_job_index_);
149 for (
int i = 0; i < declared_machine_count_; ++i) {
150 const int machine_id = strtoint32(words[2 * i]);
151 const int64_t duration = strtoint64(words[2 * i + 1]);
152 Task*
const task = job->add_tasks();
153 task->add_machine(machine_id);
154 task->add_duration(duration);
156 if (words.size() == declared_machine_count_ * 2 + 3) {
158 const int due_date = strtoint32(words[declared_machine_count_ * 2]);
159 const int early_cost =
160 strtoint32(words[declared_machine_count_ * 2 + 1]);
161 const int late_cost =
162 strtoint32(words[declared_machine_count_ * 2 + 2]);
163 job->set_early_due_date(due_date);
164 job->set_late_due_date(due_date);
165 job->set_earliness_cost_per_time_unit(early_cost);
166 job->set_lateness_cost_per_time_unit(late_cost);
168 current_job_index_++;
169 if (current_job_index_ == declared_job_count_) {
170 parser_state_ =
DONE;
175 LOG(FATAL) <<
"Should not be here with state " << parser_state_;
180 void JsspParser::ProcessTaillardLine(
const std::string&
line) {
181 const std::vector<std::string> words =
182 absl::StrSplit(
line,
' ', absl::SkipEmpty());
184 switch (parser_state_) {
186 if (words.size() == 2) {
187 problem_type_ =
SDST;
188 ProcessSdstLine(
line);
190 }
else if (words.size() == 3) {
192 ProcessTardinessLine(
line);
195 if (words.size() == 1 && strtoint32(words[0]) > 0) {
197 SetJobs(strtoint32(words[0]));
202 CHECK_EQ(1, words.size());
203 SetMachines(strtoint32(words[0]));
204 problem_.set_makespan_cost_per_time_unit(1L);
209 CHECK_EQ(1, words.size());
210 const int seed = strtoint32(words[0]);
211 problem_.set_seed(seed);
216 ABSL_FALLTHROUGH_INTENDED;
218 CHECK_EQ(1, words.size());
219 current_job_index_ = strtoint32(words[0]);
224 CHECK_EQ(1, words.size());
229 CHECK_EQ(declared_machine_count_, words.size());
230 Job*
const job = problem_.mutable_jobs(current_job_index_);
231 for (
int i = 0; i < declared_machine_count_; ++i) {
232 const int64_t duration = strtoint64(words[i]);
233 Task*
const task = job->add_tasks();
234 task->add_machine(i);
235 task->add_duration(duration);
238 current_job_index_ == declared_job_count_ - 1 ?
DONE :
JOB_READ;
242 LOG(FATAL) <<
"Should not be here with state " << parser_state_;
246 void JsspParser::ProcessFlexibleLine(
const std::string&
line) {
247 const std::vector<std::string> words =
248 absl::StrSplit(
line,
' ', absl::SkipEmpty());
249 switch (parser_state_) {
251 CHECK_GE(words.size(), 2);
252 SetJobs(strtoint32(words[0]));
253 SetMachines(strtoint32(words[1]));
254 problem_.set_makespan_cost_per_time_unit(1L);
259 const int operations_count = strtoint32(words[0]);
261 Job*
const job = problem_.mutable_jobs(current_job_index_);
262 for (
int operation = 0; operation < operations_count; ++operation) {
263 const int alternatives_count = strtoint32(words[
index++]);
264 Task*
const task = job->add_tasks();
265 for (
int alt = 0; alt < alternatives_count; alt++) {
267 const int machine_id = strtoint32(words[
index++]) - 1;
268 const int64_t duration = strtoint64(words[
index++]);
269 task->add_machine(machine_id);
270 task->add_duration(duration);
273 CHECK_LE(
index, words.size());
274 current_job_index_++;
275 if (current_job_index_ == declared_job_count_) {
276 parser_state_ =
DONE;
281 LOG(FATAL) <<
"Should not be here with state " << parser_state_;
285 void JsspParser::ProcessSdstLine(
const std::string&
line) {
286 const std::vector<std::string> words =
287 absl::StrSplit(
line,
' ', absl::SkipEmpty());
288 switch (parser_state_) {
290 if (words.size() == 2) {
291 SetJobs(strtoint32(words[0]));
292 SetMachines(strtoint32(words[1]));
293 problem_.set_makespan_cost_per_time_unit(1L);
295 current_machine_index_ = 0;
300 CHECK_EQ(words.size(), declared_machine_count_ * 2);
301 Job*
const job = problem_.mutable_jobs(current_job_index_);
302 for (
int i = 0; i < declared_machine_count_; ++i) {
303 const int machine_id = strtoint32(words[2 * i]);
304 const int64_t duration = strtoint64(words[2 * i + 1]);
305 Task*
const task = job->add_tasks();
306 task->add_machine(machine_id);
307 task->add_duration(duration);
309 current_job_index_++;
310 if (current_job_index_ == declared_job_count_) {
316 CHECK_EQ(1, words.size());
317 CHECK_EQ(
"SSD", words[0]);
322 CHECK_EQ(1, words.size());
323 CHECK_EQ(words[0], absl::StrCat(
"M", current_machine_index_)) <<
line;
324 current_job_index_ = 0;
329 CHECK_EQ(declared_job_count_, words.size());
330 Machine*
const machine =
331 problem_.mutable_machines(current_machine_index_);
332 for (
const std::string& w : words) {
333 const int64_t t = strtoint64(w);
334 machine->mutable_transition_time_matrix()->add_transition_time(t);
336 if (++current_job_index_ == declared_job_count_) {
337 parser_state_ = ++current_machine_index_ == declared_machine_count_
344 LOG(FATAL) <<
"Should not be here with state " << parser_state_
345 <<
"with line " <<
line;
350 void JsspParser::ProcessTardinessLine(
const std::string&
line) {
351 const std::vector<std::string> words =
352 absl::StrSplit(
line,
' ', absl::SkipEmpty());
353 switch (parser_state_) {
355 CHECK_EQ(3, words.size());
356 SetJobs(strtoint32(words[0]));
357 SetMachines(strtoint32(words[1]));
359 current_job_index_ = 0;
363 CHECK_GE(words.size(), 6);
364 Job*
const job = problem_.mutable_jobs(current_job_index_);
365 const int64_t est = strtoint64(words[0]);
367 job->mutable_earliest_start()->set_value(est);
369 job->set_late_due_date(strtoint64(words[1]));
370 const double weight = std::stod(words[2]);
371 const int64_t tardiness =
static_cast<int64_t
>(
372 round(
weight * absl::GetFlag(FLAGS_jssp_scaling_up_factor)));
373 job->set_lateness_cost_per_time_unit(tardiness);
374 const int num_operations = strtoint32(words[3]);
375 for (
int i = 0; i < num_operations; ++i) {
376 const int machine_id = strtoint32(words[4 + 2 * i]) - 1;
377 const int64_t duration = strtoint64(words[5 + 2 * i]);
378 Task*
const task = job->add_tasks();
379 task->add_machine(machine_id);
380 task->add_duration(duration);
382 current_job_index_++;
383 if (current_job_index_ == declared_job_count_) {
385 bool all_integral =
true;
386 for (
const Job& job : problem_.jobs()) {
387 if (job.lateness_cost_per_time_unit() %
388 absl::GetFlag(FLAGS_jssp_scaling_up_factor) !=
390 all_integral =
false;
395 for (Job& job : *problem_.mutable_jobs()) {
396 job.set_lateness_cost_per_time_unit(
397 job.lateness_cost_per_time_unit() /
398 absl::GetFlag(FLAGS_jssp_scaling_up_factor));
401 problem_.mutable_scaling_factor()->set_value(
402 1.0L / absl::GetFlag(FLAGS_jssp_scaling_up_factor));
404 parser_state_ =
DONE;
409 LOG(FATAL) <<
"Should not be here with state " << parser_state_
410 <<
"with line " <<
line;
415 void JsspParser::ProcessPssLine(
const std::string&
line) {
416 const std::vector<std::string> words =
417 absl::StrSplit(
line,
' ', absl::SkipEmpty());
418 switch (parser_state_) {
420 problem_.set_makespan_cost_per_time_unit(1L);
421 CHECK_EQ(1, words.size());
422 SetJobs(strtoint32(words[0]));
427 CHECK_EQ(1, words.size());
428 SetMachines(strtoint32(words[0]));
430 current_job_index_ = 0;
434 CHECK_EQ(1, words.size());
435 CHECK_EQ(declared_machine_count_, strtoint32(words[0]));
436 if (++current_job_index_ == declared_job_count_) {
438 current_job_index_ = 0;
439 current_machine_index_ = 0;
444 CHECK_EQ(4, words.size());
445 CHECK_EQ(0, strtoint32(words[2]));
446 CHECK_EQ(0, strtoint32(words[3]));
447 const int machine_id = strtoint32(words[0]) - 1;
448 const int duration = strtoint32(words[1]);
449 Job*
const job = problem_.mutable_jobs(current_job_index_);
450 Task*
const task = job->add_tasks();
451 task->add_machine(machine_id);
452 task->add_duration(duration);
453 if (++current_machine_index_ == declared_machine_count_) {
454 current_machine_index_ = 0;
455 if (++current_job_index_ == declared_job_count_) {
456 current_job_index_ = -1;
457 current_machine_index_ = 0;
459 transition_index_ = 0;
460 for (
int m = 0; m < declared_machine_count_; ++m) {
461 Machine*
const machine = problem_.mutable_machines(m);
462 for (
int i = 0; i < declared_job_count_ * declared_job_count_;
464 machine->mutable_transition_time_matrix()->add_transition_time(0);
472 CHECK_EQ(1, words.size());
473 const int index = transition_index_++;
474 const int size = declared_job_count_ * declared_machine_count_ + 1;
475 const int t1 =
index / size;
476 const int t2 =
index % size;
477 if (t1 == 0 || t2 == 0) {
480 const int item1 = t1 - 1;
481 const int item2 = t2 - 1;
482 const int job1 = item1 / declared_machine_count_;
483 const int task1 = item1 % declared_machine_count_;
484 const int m1 = problem_.jobs(job1).tasks(task1).machine(0);
485 const int job2 = item2 / declared_machine_count_;
486 const int task2 = item2 % declared_machine_count_;
487 const int m2 = problem_.jobs(job2).tasks(task2).machine(0);
491 const int transition = strtoint32(words[0]);
492 Machine*
const machine = problem_.mutable_machines(m1);
493 machine->mutable_transition_time_matrix()->set_transition_time(
494 job1 * declared_job_count_ + job2, transition);
495 if (transition_index_ == size * size) {
496 parser_state_ =
DONE;
501 LOG(FATAL) <<
"Should not be here with state " << parser_state_
502 <<
"with line " <<
line;
507 void JsspParser::ProcessEarlyTardyLine(
const std::string&
line) {
508 const std::vector<std::string> words =
509 absl::StrSplit(
line,
' ', absl::SkipEmpty());
510 switch (parser_state_) {
512 CHECK_EQ(words.size(), declared_machine_count_ * 2 + 3);
513 Job*
const job = problem_.mutable_jobs(current_job_index_);
514 for (
int i = 0; i < declared_machine_count_; ++i) {
515 const int machine_id = strtoint32(words[2 * i]);
516 const int64_t duration = strtoint64(words[2 * i + 1]);
517 Task*
const task = job->add_tasks();
518 task->add_machine(machine_id);
519 task->add_duration(duration);
522 const int due_date = strtoint32(words[declared_machine_count_ * 2]);
523 const int early_cost = strtoint32(words[declared_machine_count_ * 2 + 1]);
524 const int late_cost = strtoint32(words[declared_machine_count_ * 2 + 2]);
525 job->set_early_due_date(due_date);
526 job->set_late_due_date(due_date);
527 job->set_earliness_cost_per_time_unit(early_cost);
528 job->set_lateness_cost_per_time_unit(late_cost);
529 current_job_index_++;
530 if (current_job_index_ == declared_job_count_) {
531 parser_state_ =
DONE;
536 LOG(FATAL) <<
"Should not be here with state " << parser_state_;
541 int JsspParser::strtoint32(
const std::string& word) {
543 CHECK(absl::SimpleAtoi(word, &result));
547 int64_t JsspParser::strtoint64(
const std::string& word) {
549 CHECK(absl::SimpleAtoi(word, &result));
bool ParseFile(const std::string &filename)
ABSL_FLAG(int64_t, jssp_scaling_up_factor, 100000L, "Scaling factor for floating point penalties.")
absl::string_view Stem(absl::string_view path)
Collection of objects used to extend the Constraint Solver library.