34 load_status_(NOT_STARTED),
35 num_declared_tasks_(-1),
38 rcpsp_.set_deadline(-1);
39 rcpsp_.set_horizon(-1);
43 if (load_status_ != NOT_STARTED) {
47 const bool is_rcpsp_max =
48 absl::EndsWith(file_name,
".sch") || absl::EndsWith(file_name,
".SCH");
49 const bool is_patterson = absl::EndsWith(file_name,
".rcp");
50 load_status_ = HEADER_SECTION;
54 ProcessRcpspMaxLine(
line);
55 }
else if (is_patterson) {
56 ProcessPattersonLine(
line);
58 ProcessRcpspLine(
line);
60 if (load_status_ == ERROR_FOUND) {
65 VLOG(1) <<
"Read file: " << file_name <<
", max = " << is_rcpsp_max
66 <<
", patterson = " << is_patterson <<
", with "
67 << rcpsp_.tasks_size() <<
" tasks, and " << rcpsp_.resources_size()
72 std::string problem_name(
file::Stem(file_name));
73 rcpsp_.set_name(problem_name);
76 return num_declared_tasks_ + 2 == rcpsp_.tasks_size() &&
77 load_status_ == PARSING_FINISHED;
80void RcpspParser::ReportError(
const std::string&
line) {
81 LOG(ERROR) <<
"Error: status = " << load_status_ <<
", line = " <<
line;
82 load_status_ = ERROR_FOUND;
85void RcpspParser::SetNumDeclaredTasks(
int t) {
86 num_declared_tasks_ = t;
87 recipe_sizes_.resize(t + 2, 0);
90void RcpspParser::ProcessRcpspLine(
const std::string&
line) {
91 if (absl::StartsWith(
line,
"***"))
return;
92 if (absl::StartsWith(
line,
"---"))
return;
94 const std::vector<std::string> words =
95 absl::StrSplit(
line, absl::ByAnyChar(
" :\t\r"), absl::SkipEmpty());
97 if (words.empty())
return;
99 switch (load_status_) {
104 case HEADER_SECTION: {
105 if (words[0] ==
"file") {
106 rcpsp_.set_basedata(words[3]);
107 }
else if (words[0] ==
"initial") {
108 rcpsp_.set_seed(strtoint64(words[4]));
109 load_status_ = PROJECT_SECTION;
110 }
else if (words[0] ==
"jobs") {
112 SetNumDeclaredTasks(strtoint32(words[4]) - 2);
113 load_status_ = PROJECT_SECTION;
119 case PROJECT_SECTION: {
120 if (words[0] ==
"projects") {
122 }
else if (words[0] ==
"jobs") {
124 SetNumDeclaredTasks(strtoint32(words[4]) - 2);
125 }
else if (words[0] ==
"horizon") {
126 rcpsp_.set_horizon(strtoint32(words[1]));
127 }
else if (words[0] ==
"RESOURCES") {
129 }
else if (words.size() > 1 && words[1] ==
"renewable") {
130 for (
int i = 0;
i < strtoint32(words[2]); ++
i) {
131 Resource*
const res = rcpsp_.add_resources();
132 res->set_max_capacity(-1);
133 res->set_renewable(
true);
134 res->set_unit_cost(0);
136 }
else if (words.size() > 1 && words[1] ==
"nonrenewable") {
137 for (
int i = 0;
i < strtoint32(words[2]); ++
i) {
138 Resource*
const res = rcpsp_.add_resources();
139 res->set_max_capacity(-1);
140 res->set_min_capacity(-1);
141 res->set_renewable(
false);
142 res->set_unit_cost(0);
144 }
else if (words.size() > 1 && words[1] ==
"doubly") {
146 }
else if (words.size() == 2 && words[0] ==
"PROJECT") {
147 load_status_ = INFO_SECTION;
148 }
else if (words.size() == 2 && words[0] ==
"PRECEDENCE") {
150 load_status_ = PRECEDENCE_SECTION;
157 if (words[0] ==
"pronr.") {
159 }
else if (words.size() == 6) {
160 SetNumDeclaredTasks(strtoint32(words[1]));
161 rcpsp_.set_release_date(strtoint32(words[2]));
162 rcpsp_.set_due_date(strtoint32(words[3]));
163 rcpsp_.set_tardiness_cost(strtoint32(words[4]));
164 rcpsp_.set_mpm_time(strtoint32(words[5]));
165 }
else if (words.size() == 2 && words[0] ==
"PRECEDENCE") {
166 load_status_ = PRECEDENCE_SECTION;
172 case PRECEDENCE_SECTION: {
173 if (words[0] ==
"jobnr.") {
175 }
else if (words.size() >= 3) {
176 const int task_index = strtoint32(words[0]) - 1;
177 CHECK_EQ(task_index, rcpsp_.tasks_size());
178 recipe_sizes_[task_index] = strtoint32(words[1]);
179 const int num_successors = strtoint32(words[2]);
180 if (words.size() != 3 + num_successors) {
184 Task*
const task = rcpsp_.add_tasks();
185 for (
int i = 0;
i < num_successors; ++
i) {
187 task->add_successors(strtoint32(words[3 + i]) - 1);
189 }
else if (words[0] ==
"REQUESTS/DURATIONS") {
190 load_status_ = REQUEST_SECTION;
196 case REQUEST_SECTION: {
197 if (words[0] ==
"jobnr.") {
199 }
else if (words.size() == 3 + rcpsp_.resources_size()) {
201 current_task_ = strtoint32(words[0]) - 1;
202 const int current_recipe = strtoint32(words[1]) - 1;
203 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
204 if (current_recipe != 0) {
208 Recipe*
const recipe =
209 rcpsp_.mutable_tasks(current_task_)->add_recipes();
210 recipe->set_duration(strtoint32(words[2]));
211 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
212 const int demand = strtoint32(words[3 + i]);
214 recipe->add_demands(
demand);
215 recipe->add_resources(i);
218 }
else if (words.size() == 2 + rcpsp_.resources_size()) {
220 const int current_recipe = strtoint32(words[0]) - 1;
221 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
222 Recipe*
const recipe =
223 rcpsp_.mutable_tasks(current_task_)->add_recipes();
224 recipe->set_duration(strtoint32(words[1]));
225 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
226 const int demand = strtoint32(words[2 + i]);
228 recipe->add_demands(
demand);
229 recipe->add_resources(i);
232 }
else if (words[0] ==
"RESOURCEAVAILABILITIES" ||
233 (words[0] ==
"RESOURCE" && words[1] ==
"AVAILABILITIES")) {
234 load_status_ = RESOURCE_SECTION;
240 case RESOURCE_SECTION: {
241 if (words.size() == 2 * rcpsp_.resources_size()) {
243 }
else if (words.size() == rcpsp_.resources_size()) {
244 for (
int i = 0;
i < words.size(); ++
i) {
245 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
247 load_status_ = PARSING_FINISHED;
253 case RESOURCE_MIN_SECTION: {
254 LOG(FATAL) <<
"Should not be here";
257 case PARSING_FINISHED: {
266void RcpspParser::ProcessRcpspMaxLine(
const std::string&
line) {
267 const std::vector<std::string> words =
268 absl::StrSplit(
line, absl::ByAnyChar(
" :\t[]\r"), absl::SkipEmpty());
270 switch (load_status_) {
275 case HEADER_SECTION: {
276 rcpsp_.set_is_rcpsp_max(
true);
277 if (words.size() == 2) {
278 rcpsp_.set_is_consumer_producer(
true);
279 }
else if (words.size() < 4 || strtoint32(words[3]) != 0) {
284 if (words.size() == 5) {
285 rcpsp_.set_deadline(strtoint32(words[4]));
286 rcpsp_.set_is_resource_investment(
true);
289 SetNumDeclaredTasks(strtoint32(words[0]));
290 temp_delays_.resize(num_declared_tasks_ + 2);
293 if (rcpsp_.is_consumer_producer()) {
294 const int num_nonrenewable_resources = strtoint32(words[1]);
295 for (
int i = 0;
i < num_nonrenewable_resources; ++
i) {
296 Resource*
const res = rcpsp_.add_resources();
297 res->set_max_capacity(-1);
298 res->set_min_capacity(-1);
299 res->set_renewable(
false);
300 res->set_unit_cost(0);
303 const int num_renewable_resources = strtoint32(words[1]);
304 const int num_nonrenewable_resources = strtoint32(words[2]);
305 for (
int i = 0;
i < num_renewable_resources; ++
i) {
306 Resource*
const res = rcpsp_.add_resources();
307 res->set_max_capacity(-1);
308 res->set_renewable(
true);
309 res->set_unit_cost(0);
311 for (
int i = 0;
i < num_nonrenewable_resources; ++
i) {
312 Resource*
const res = rcpsp_.add_resources();
313 res->set_max_capacity(-1);
314 res->set_min_capacity(-1);
315 res->set_renewable(
false);
316 res->set_unit_cost(0);
321 load_status_ = PRECEDENCE_SECTION;
325 case PROJECT_SECTION: {
326 LOG(FATAL) <<
"Should not be here";
330 LOG(FATAL) <<
"Should not be here";
333 case PRECEDENCE_SECTION: {
334 if (words.size() < 3) {
339 const int task_id = strtoint32(words[0]);
340 if (task_id != current_task_) {
347 const int num_recipes = strtoint32(words[1]);
348 recipe_sizes_[task_id] = num_recipes;
349 const int num_successors = strtoint32(words[2]);
351 Task*
const task = rcpsp_.add_tasks();
354 for (
int i = 0;
i < num_successors; ++
i) {
355 task->add_successors(strtoint32(words[3 + i]));
359 for (
int i = 3 + num_successors;
i < words.size(); ++
i) {
360 temp_delays_[task_id].push_back(strtoint32(words[i]));
363 if (task_id == num_declared_tasks_ + 1) {
366 for (
int t = 1; t <= num_declared_tasks_; ++t) {
367 const int num_recipes = recipe_sizes_[t];
368 const int num_successors = rcpsp_.tasks(t).successors_size();
370 for (
int s = 0; s < num_successors; ++s) {
371 PerSuccessorDelays*
const succ_delays =
372 rcpsp_.mutable_tasks(t)->add_successor_delays();
373 for (
int r1 = 0; r1 < num_recipes; ++r1) {
374 PerRecipeDelays*
const recipe_delays =
375 succ_delays->add_recipe_delays();
376 const int other = rcpsp_.tasks(t).successors(s);
377 const int num_other_recipes = recipe_sizes_[other];
378 for (
int r2 = 0; r2 < num_other_recipes; ++r2) {
379 recipe_delays->add_min_delays(temp_delays_[t][count++]);
383 CHECK_EQ(count, temp_delays_[t].
size());
388 load_status_ = REQUEST_SECTION;
392 case REQUEST_SECTION: {
393 if (words.size() == 3 + rcpsp_.resources_size()) {
395 current_task_ = strtoint32(words[0]);
398 const int current_recipe = strtoint32(words[1]) - 1;
399 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
400 if (current_recipe != 0) {
404 Recipe*
const recipe =
405 rcpsp_.mutable_tasks(current_task_)->add_recipes();
406 recipe->set_duration(strtoint32(words[2]));
407 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
408 const int demand = strtoint32(words[3 + i]);
410 recipe->add_demands(
demand);
411 recipe->add_resources(i);
414 }
else if (words.size() == 2 + rcpsp_.resources_size() &&
415 rcpsp_.is_consumer_producer()) {
417 current_task_ = strtoint32(words[0]);
420 const int current_recipe = strtoint32(words[1]) - 1;
421 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
422 if (current_recipe != 0) {
426 Recipe*
const recipe =
427 rcpsp_.mutable_tasks(current_task_)->add_recipes();
428 recipe->set_duration(0);
429 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
430 const int demand = strtoint32(words[2 + i]);
432 recipe->add_demands(
demand);
433 recipe->add_resources(i);
436 }
else if (words.size() == 2 + rcpsp_.resources_size()) {
438 const int current_recipe = strtoint32(words[0]) - 1;
439 CHECK_EQ(current_recipe, rcpsp_.tasks(current_task_).recipes_size());
440 Recipe*
const recipe =
441 rcpsp_.mutable_tasks(current_task_)->add_recipes();
442 recipe->set_duration(strtoint32(words[1]));
443 for (
int i = 0;
i < rcpsp_.resources_size(); ++
i) {
444 const int demand = strtoint32(words[2 + i]);
446 recipe->add_demands(
demand);
447 recipe->add_resources(i);
451 if (current_task_ == num_declared_tasks_ + 1) {
452 if (rcpsp_.is_consumer_producer()) {
453 load_status_ = RESOURCE_MIN_SECTION;
455 load_status_ = RESOURCE_SECTION;
460 case RESOURCE_SECTION: {
461 if (words.size() == rcpsp_.resources_size()) {
462 for (
int i = 0;
i < words.size(); ++
i) {
463 if (rcpsp_.is_resource_investment()) {
464 rcpsp_.mutable_resources(i)->set_unit_cost(strtoint32(words[i]));
466 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
469 load_status_ = PARSING_FINISHED;
475 case RESOURCE_MIN_SECTION: {
476 if (words.size() == rcpsp_.resources_size()) {
477 for (
int i = 0;
i < words.size(); ++
i) {
478 rcpsp_.mutable_resources(i)->set_min_capacity(strtoint32(words[i]));
480 load_status_ = RESOURCE_SECTION;
486 case PARSING_FINISHED: {
495void RcpspParser::ProcessPattersonLine(
const std::string&
line) {
496 const std::vector<std::string> words =
497 absl::StrSplit(
line, absl::ByAnyChar(
" :\t[]\r"), absl::SkipEmpty());
499 if (words.empty())
return;
501 switch (load_status_) {
506 case HEADER_SECTION: {
507 if (words.size() != 2) {
511 SetNumDeclaredTasks(strtoint32(words[0]) - 2);
514 const int num_renewable_resources = strtoint32(words[1]);
515 for (
int i = 0;
i < num_renewable_resources; ++
i) {
516 Resource*
const res = rcpsp_.add_resources();
517 res->set_max_capacity(-1);
518 res->set_min_capacity(-1);
519 res->set_renewable(
true);
520 res->set_unit_cost(0);
524 load_status_ = RESOURCE_SECTION;
527 case PROJECT_SECTION: {
528 LOG(FATAL) <<
"Should not be here";
532 LOG(FATAL) <<
"Should not be here";
535 case PRECEDENCE_SECTION: {
537 for (
int i = 0;
i < words.size(); ++
i) {
538 rcpsp_.mutable_tasks(current_task_)
539 ->add_successors(strtoint32(words[i]) - 1);
541 CHECK_GE(unreads_, 0);
544 if (words.size() < 2 + rcpsp_.resources_size()) {
548 CHECK_EQ(current_task_, rcpsp_.tasks_size());
549 Task*
const task = rcpsp_.add_tasks();
550 Recipe*
const recipe = task->add_recipes();
551 recipe->set_duration(strtoint32(words[0]));
553 const int num_resources = rcpsp_.resources_size();
554 for (
int i = 1;
i <= num_resources; ++
i) {
555 const int demand = strtoint32(words[i]);
557 recipe->add_demands(
demand);
558 recipe->add_resources(i - 1);
562 unreads_ = strtoint32(words[1 + num_resources]);
563 for (
int i = 2 + num_resources;
i < words.size(); ++
i) {
565 task->add_successors(strtoint32(words[i]) - 1);
567 CHECK_GE(unreads_, 0);
571 if (unreads_ == 0 && ++current_task_ == num_declared_tasks_ + 2) {
572 load_status_ = PARSING_FINISHED;
576 case REQUEST_SECTION: {
577 LOG(FATAL) <<
"Should not be here";
580 case RESOURCE_SECTION: {
581 if (words.size() == rcpsp_.resources_size()) {
582 for (
int i = 0;
i < words.size(); ++
i) {
583 rcpsp_.mutable_resources(i)->set_max_capacity(strtoint32(words[i]));
585 load_status_ = PRECEDENCE_SECTION;
592 case RESOURCE_MIN_SECTION: {
593 LOG(FATAL) <<
"Should not be here";
596 case PARSING_FINISHED: {
605int RcpspParser::strtoint32(absl::string_view word) {
607 CHECK(absl::SimpleAtoi(word, &result));
611int64_t RcpspParser::strtoint64(absl::string_view word) {
613 CHECK(absl::SimpleAtoi(word, &result));