36 load_status_(NOT_STARTED),
37 num_declared_tasks_(-1),
40 rcpsp_.set_deadline(-1);
41 rcpsp_.set_horizon(-1);
45 if (load_status_ != NOT_STARTED) {
49 const bool is_rcpsp_max =
50 absl::EndsWith(file_name,
".sch") || absl::EndsWith(file_name,
".SCH");
51 const bool is_patterson = absl::EndsWith(file_name,
".rcp");
52 load_status_ = HEADER_SECTION;
54 for (
const std::string& line :
FileLines(file_name)) {
56 ProcessRcpspMaxLine(line);
57 }
else if (is_patterson) {
58 ProcessPattersonLine(line);
60 ProcessRcpspLine(line);
62 if (load_status_ == ERROR_FOUND) {
67 VLOG(1) <<
"Read file: " << file_name <<
", max = " << is_rcpsp_max
68 <<
", patterson = " << is_patterson <<
", with "
69 << rcpsp_.tasks_size() <<
" tasks, and " << rcpsp_.resources_size()
74 std::string problem_name(
file::Stem(file_name));
75 rcpsp_.set_name(problem_name);
78 return num_declared_tasks_ + 2 == rcpsp_.tasks_size() &&
79 load_status_ == PARSING_FINISHED;
82void RcpspParser::ReportError(
const std::string& line) {
83 LOG(ERROR) <<
"Error: status = " << load_status_ <<
", line = " << line;
84 load_status_ = ERROR_FOUND;
87void RcpspParser::SetNumDeclaredTasks(
int t) {
88 num_declared_tasks_ = t;
89 recipe_sizes_.resize(t + 2, 0);
92void RcpspParser::ProcessRcpspLine(
const std::string& line) {
93 if (absl::StartsWith(line,
"***"))
return;
94 if (absl::StartsWith(line,
"---"))
return;
96 const std::vector<std::string> words =
97 absl::StrSplit(line, absl::ByAnyChar(
" :\t\r"), absl::SkipEmpty());
99 if (words.empty())
return;
101 switch (load_status_) {
106 case HEADER_SECTION: {
107 if (words[0] ==
"file") {
109 }
else if (words[0] ==
"initial") {
110 rcpsp_.set_seed(strtoint64(words[4]));
111 load_status_ = PROJECT_SECTION;
112 }
else if (words[0] ==
"jobs") {
114 SetNumDeclaredTasks(strtoint32(words[4]) - 2);
115 load_status_ = PROJECT_SECTION;
121 case PROJECT_SECTION: {
122 if (words[0] ==
"projects") {
124 }
else if (words[0] ==
"jobs") {
126 SetNumDeclaredTasks(strtoint32(words[4]) - 2);
127 }
else if (words[0] ==
"horizon") {
128 rcpsp_.set_horizon(strtoint32(words[1]));
129 }
else if (words[0] ==
"RESOURCES") {
131 }
else if (words.size() > 1 && words[1] ==
"renewable") {
132 for (
int i = 0;
i < strtoint32(words[2]); ++
i) {
133 Resource*
const res = rcpsp_.add_resources();
134 res->set_max_capacity(-1);
135 res->set_renewable(
true);
136 res->set_unit_cost(0);
138 }
else if (words.size() > 1 && words[1] ==
"nonrenewable") {
139 for (
int i = 0;
i < strtoint32(words[2]); ++
i) {
140 Resource*
const res = rcpsp_.add_resources();
141 res->set_max_capacity(-1);
142 res->set_min_capacity(-1);
143 res->set_renewable(
false);
144 res->set_unit_cost(0);
146 }
else if (words.size() > 1 && words[1] ==
"doubly") {
148 }
else if (words.size() == 2 && words[0] ==
"PROJECT") {
149 load_status_ = INFO_SECTION;
150 }
else if (words.size() == 2 && words[0] ==
"PRECEDENCE") {
152 load_status_ = PRECEDENCE_SECTION;
159 if (words[0] ==
"pronr.") {
161 }
else if (words.size() == 6) {
162 SetNumDeclaredTasks(strtoint32(words[1]));
163 rcpsp_.set_release_date(strtoint32(words[2]));
164 rcpsp_.set_due_date(strtoint32(words[3]));
165 rcpsp_.set_tardiness_cost(strtoint32(words[4]));
166 rcpsp_.set_mpm_time(strtoint32(words[5]));
167 }
else if (words.size() == 2 && words[0] ==
"PRECEDENCE") {
168 load_status_ = PRECEDENCE_SECTION;
174 case PRECEDENCE_SECTION: {
175 if (words[0] ==
"jobnr.") {
177 }
else if (words.size() >= 3) {
178 const int task_index = strtoint32(words[0]) - 1;
179 CHECK_EQ(task_index, rcpsp_.tasks_size());
180 recipe_sizes_[task_index] = strtoint32(words[1]);
181 const int num_successors = strtoint32(words[2]);
182 if (words.size() != 3 + num_successors) {
186 Task*
const task = rcpsp_.add_tasks();
187 for (
int i = 0;
i < num_successors; ++
i) {
189 task->add_successors(strtoint32(words[3 + i]) - 1);
191 }
else if (words[0] ==
"REQUESTS/DURATIONS") {
192 load_status_ = REQUEST_SECTION;
198 case REQUEST_SECTION: {
199 if (words[0] ==
"jobnr.") {
201 }
else if (words.size() == 3 + rcpsp_.resources_size()) {
203 current_task_ = strtoint32(words[0]) - 1;
204 const int current_recipe = strtoint32(words[1]) - 1;
205 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
206 if (current_recipe != 0) {
210 Recipe*
const recipe =
211 rcpsp_.mutable_tasks(current_task_)->add_recipes();
212 recipe->set_duration(strtoint32(words[2]));
213 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
214 const int demand = strtoint32(words[3 + i]);
216 recipe->add_demands(demand);
217 recipe->add_resources(i);
220 }
else if (words.size() == 2 + rcpsp_.resources_size()) {
222 const int current_recipe = strtoint32(words[0]) - 1;
223 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
224 Recipe*
const recipe =
225 rcpsp_.mutable_tasks(current_task_)->add_recipes();
226 recipe->set_duration(strtoint32(words[1]));
227 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
228 const int demand = strtoint32(words[2 + i]);
230 recipe->add_demands(demand);
231 recipe->add_resources(i);
234 }
else if (words[0] ==
"RESOURCEAVAILABILITIES" ||
235 (words[0] ==
"RESOURCE" && words[1] ==
"AVAILABILITIES")) {
236 load_status_ = RESOURCE_SECTION;
242 case RESOURCE_SECTION: {
243 if (words.size() == 2 * rcpsp_.resources_size()) {
245 }
else if (words.size() == rcpsp_.resources_size()) {
246 for (
int i = 0;
i < words.size(); ++
i) {
247 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
249 load_status_ = PARSING_FINISHED;
255 case RESOURCE_MIN_SECTION: {
256 LOG(FATAL) <<
"Should not be here";
259 case PARSING_FINISHED: {
268void RcpspParser::ProcessRcpspMaxLine(
const std::string& line) {
269 const std::vector<std::string> words =
270 absl::StrSplit(line, absl::ByAnyChar(
" :\t[]\r"), absl::SkipEmpty());
272 switch (load_status_) {
277 case HEADER_SECTION: {
278 rcpsp_.set_is_rcpsp_max(
true);
279 if (words.size() == 2) {
280 rcpsp_.set_is_consumer_producer(
true);
281 }
else if (words.size() < 4 || strtoint32(words[3]) != 0) {
286 if (words.size() == 5) {
287 rcpsp_.set_deadline(strtoint32(words[4]));
288 rcpsp_.set_is_resource_investment(
true);
291 SetNumDeclaredTasks(strtoint32(words[0]));
292 temp_delays_.resize(num_declared_tasks_ + 2);
295 if (rcpsp_.is_consumer_producer()) {
296 const int num_nonrenewable_resources = strtoint32(words[1]);
297 for (
int i = 0;
i < num_nonrenewable_resources; ++
i) {
298 Resource*
const res = rcpsp_.add_resources();
299 res->set_max_capacity(-1);
300 res->set_min_capacity(-1);
301 res->set_renewable(
false);
302 res->set_unit_cost(0);
305 const int num_renewable_resources = strtoint32(words[1]);
306 const int num_nonrenewable_resources = strtoint32(words[2]);
307 for (
int i = 0;
i < num_renewable_resources; ++
i) {
308 Resource*
const res = rcpsp_.add_resources();
309 res->set_max_capacity(-1);
310 res->set_renewable(
true);
311 res->set_unit_cost(0);
313 for (
int i = 0;
i < num_nonrenewable_resources; ++
i) {
314 Resource*
const res = rcpsp_.add_resources();
315 res->set_max_capacity(-1);
316 res->set_min_capacity(-1);
317 res->set_renewable(
false);
318 res->set_unit_cost(0);
323 load_status_ = PRECEDENCE_SECTION;
327 case PROJECT_SECTION: {
328 LOG(FATAL) <<
"Should not be here";
332 LOG(FATAL) <<
"Should not be here";
335 case PRECEDENCE_SECTION: {
336 if (words.size() < 3) {
341 const int task_id = strtoint32(words[0]);
342 if (task_id != current_task_) {
349 const int num_recipes = strtoint32(words[1]);
350 recipe_sizes_[task_id] = num_recipes;
351 const int num_successors = strtoint32(words[2]);
353 Task*
const task = rcpsp_.add_tasks();
356 for (
int i = 0;
i < num_successors; ++
i) {
357 task->add_successors(strtoint32(words[3 + i]));
361 for (
int i = 3 + num_successors;
i < words.size(); ++
i) {
362 temp_delays_[task_id].push_back(strtoint32(words[i]));
365 if (task_id == num_declared_tasks_ + 1) {
368 for (
int t = 1; t <= num_declared_tasks_; ++t) {
369 const int num_recipes = recipe_sizes_[t];
370 const int num_successors = rcpsp_.tasks(t).successors_size();
372 for (
int s = 0; s < num_successors; ++s) {
373 PerSuccessorDelays*
const succ_delays =
374 rcpsp_.mutable_tasks(t)->add_successor_delays();
375 for (
int r1 = 0; r1 < num_recipes; ++r1) {
376 PerRecipeDelays*
const recipe_delays =
377 succ_delays->add_recipe_delays();
378 const int other = rcpsp_.tasks(t).successors(s);
379 const int num_other_recipes = recipe_sizes_[other];
380 for (
int r2 = 0; r2 < num_other_recipes; ++r2) {
381 recipe_delays->add_min_delays(temp_delays_[t][count++]);
385 CHECK_EQ(count, temp_delays_[t].size());
390 load_status_ = REQUEST_SECTION;
394 case REQUEST_SECTION: {
395 if (words.size() == 3 + rcpsp_.resources_size()) {
397 current_task_ = strtoint32(words[0]);
400 const int current_recipe = strtoint32(words[1]) - 1;
401 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
402 if (current_recipe != 0) {
406 Recipe*
const recipe =
407 rcpsp_.mutable_tasks(current_task_)->add_recipes();
408 recipe->set_duration(strtoint32(words[2]));
409 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
410 const int demand = strtoint32(words[3 + i]);
412 recipe->add_demands(demand);
413 recipe->add_resources(i);
416 }
else if (words.size() == 2 + rcpsp_.resources_size() &&
417 rcpsp_.is_consumer_producer()) {
419 current_task_ = strtoint32(words[0]);
422 const int current_recipe = strtoint32(words[1]) - 1;
423 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
424 if (current_recipe != 0) {
428 Recipe*
const recipe =
429 rcpsp_.mutable_tasks(current_task_)->add_recipes();
430 recipe->set_duration(0);
431 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
432 const int demand = strtoint32(words[2 + i]);
434 recipe->add_demands(demand);
435 recipe->add_resources(i);
438 }
else if (words.size() == 2 + rcpsp_.resources_size()) {
440 const int current_recipe = strtoint32(words[0]) - 1;
441 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
442 Recipe*
const recipe =
443 rcpsp_.mutable_tasks(current_task_)->add_recipes();
444 recipe->set_duration(strtoint32(words[1]));
445 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
446 const int demand = strtoint32(words[2 + i]);
448 recipe->add_demands(demand);
449 recipe->add_resources(i);
453 if (current_task_ == num_declared_tasks_ + 1) {
454 if (rcpsp_.is_consumer_producer()) {
455 load_status_ = RESOURCE_MIN_SECTION;
457 load_status_ = RESOURCE_SECTION;
462 case RESOURCE_SECTION: {
463 if (words.size() == rcpsp_.resources_size()) {
464 for (
int i = 0;
i < words.size(); ++
i) {
465 if (rcpsp_.is_resource_investment()) {
466 rcpsp_.mutable_resources(i)->set_unit_cost(strtoint32(words[i]));
468 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
471 load_status_ = PARSING_FINISHED;
477 case RESOURCE_MIN_SECTION: {
478 if (words.size() == rcpsp_.resources_size()) {
479 for (
int i = 0;
i < words.size(); ++
i) {
480 rcpsp_.mutable_resources(i)->set_min_capacity(strtoint32(words[i]));
482 load_status_ = RESOURCE_SECTION;
488 case PARSING_FINISHED: {
497void RcpspParser::ProcessPattersonLine(
const std::string& line) {
498 const std::vector<std::string> words =
499 absl::StrSplit(line, absl::ByAnyChar(
" :\t[]\r"), absl::SkipEmpty());
501 if (words.empty())
return;
503 switch (load_status_) {
508 case HEADER_SECTION: {
509 if (words.size() != 2) {
513 SetNumDeclaredTasks(strtoint32(words[0]) - 2);
516 const int num_renewable_resources = strtoint32(words[1]);
517 for (
int i = 0;
i < num_renewable_resources; ++
i) {
518 Resource*
const res = rcpsp_.add_resources();
519 res->set_max_capacity(-1);
520 res->set_min_capacity(-1);
521 res->set_renewable(
true);
522 res->set_unit_cost(0);
526 load_status_ = RESOURCE_SECTION;
529 case PROJECT_SECTION: {
530 LOG(FATAL) <<
"Should not be here";
534 LOG(FATAL) <<
"Should not be here";
537 case PRECEDENCE_SECTION: {
539 for (
int i = 0;
i < words.size(); ++
i) {
540 rcpsp_.mutable_tasks(current_task_)
541 ->add_successors(strtoint32(words[i]) - 1);
543 CHECK_GE(unreads_, 0);
546 if (words.size() < 2 + rcpsp_.resources_size()) {
550 CHECK_EQ(current_task_, rcpsp_.tasks_size());
551 Task*
const task = rcpsp_.add_tasks();
552 Recipe*
const recipe = task->add_recipes();
553 recipe->set_duration(strtoint32(words[0]));
555 const int num_resources = rcpsp_.resources_size();
556 for (
int i = 1;
i <= num_resources; ++
i) {
557 const int demand = strtoint32(words[i]);
559 recipe->add_demands(demand);
560 recipe->add_resources(i - 1);
564 unreads_ = strtoint32(words[1 + num_resources]);
565 for (
int i = 2 + num_resources;
i < words.size(); ++
i) {
567 task->add_successors(strtoint32(words[i]) - 1);
569 CHECK_GE(unreads_, 0);
573 if (unreads_ == 0 && ++current_task_ == num_declared_tasks_ + 2) {
574 load_status_ = PARSING_FINISHED;
578 case REQUEST_SECTION: {
579 LOG(FATAL) <<
"Should not be here";
582 case RESOURCE_SECTION: {
583 if (words.size() == rcpsp_.resources_size()) {
584 for (
int i = 0;
i < words.size(); ++
i) {
585 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
587 load_status_ = PRECEDENCE_SECTION;
594 case RESOURCE_MIN_SECTION: {
595 LOG(FATAL) <<
"Should not be here";
598 case PARSING_FINISHED: {
607int RcpspParser::strtoint32(absl::string_view word) {
609 CHECK(absl::SimpleAtoi(word, &result));
613int64_t RcpspParser::strtoint64(absl::string_view word) {
615 CHECK(absl::SimpleAtoi(word, &result));