Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
tsplib_parser.cc
Go to the documentation of this file.
1// Copyright 2010-2025 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <algorithm>
17#include <cmath>
18#include <cstdint>
19#include <iterator>
20#include <limits>
21#include <memory>
22#include <string>
23#include <utility>
24#include <vector>
25
26#include "absl/container/flat_hash_map.h"
27#include "absl/log/check.h"
28#include "absl/log/log.h"
29#include "absl/status/status.h"
30#include "absl/status/statusor.h"
31#include "absl/strings/ascii.h"
32#include "absl/strings/str_cat.h"
33#include "absl/strings/str_join.h"
34#include "absl/strings/str_split.h"
35#include "absl/strings/string_view.h"
36#include "absl/types/span.h"
37#include "ortools/base/file.h"
41#include "ortools/base/path.h"
48#include "re2/re2.h"
49
51namespace {
52
53// ----- Distances -----
54// As defined by the TSPLIB95 doc
55
56static int64_t ATTDistance(const Coordinates3<double>& from,
57 const Coordinates3<double>& to) {
58 const double xd = from.x - to.x;
59 const double yd = from.y - to.y;
60 const double euc = sqrt((xd * xd + yd * yd) / 10.0);
61 int64_t distance = std::round(euc);
62 if (distance < euc) ++distance;
63 return distance;
64}
65
66static double DoubleEuc2DDistance(const Coordinates3<double>& from,
67 const Coordinates3<double>& to) {
68 const double xd = from.x - to.x;
69 const double yd = from.y - to.y;
70 return sqrt(xd * xd + yd * yd);
71}
72
73static int64_t Euc2DDistance(const Coordinates3<double>& from,
74 const Coordinates3<double>& to) {
75 return std::round(DoubleEuc2DDistance(from, to));
76}
77
78static int64_t Euc3DDistance(const Coordinates3<double>& from,
79 const Coordinates3<double>& to) {
80 const double xd = from.x - to.x;
81 const double yd = from.y - to.y;
82 const double zd = from.z - to.z;
83 const double euc = sqrt(xd * xd + yd * yd + zd * zd);
84 return std::round(euc);
85}
86
87static int64_t Ceil2DDistance(const Coordinates3<double>& from,
88 const Coordinates3<double>& to) {
89 return static_cast<int64_t>(ceil(DoubleEuc2DDistance(from, to)));
90}
91
92static double ToRad(double x) {
93 static const double kPi = 3.141592;
94 const int64_t deg = static_cast<int64_t>(x);
95 const double min = x - deg;
96 return kPi * (deg + 5.0 * min / 3.0) / 180.0;
97}
98
99static int64_t GeoDistance(const Coordinates3<double>& from,
100 const Coordinates3<double>& to) {
101 static const double kRadius = 6378.388;
102 const double q1 = cos(ToRad(from.y) - ToRad(to.y));
103 const double q2 = cos(ToRad(from.x) - ToRad(to.x));
104 const double q3 = cos(ToRad(from.x) + ToRad(to.x));
105 return static_cast<int64_t>(
106 kRadius * acos(0.5 * ((1.0 + q1) * q2 - (1.0 - q1) * q3)) + 1.0);
107}
108
109static int64_t GeoMDistance(const Coordinates3<double>& from,
110 const Coordinates3<double>& to) {
111 static const double kPi = 3.14159265358979323846264;
112 static const double kRadius = 6378388.0;
113 const double from_lat = kPi * from.x / 180.0;
114 const double to_lat = kPi * to.x / 180.0;
115 const double from_lng = kPi * from.y / 180.0;
116 const double to_lng = kPi * to.y / 180.0;
117 const double q1 = cos(to_lat) * sin(from_lng - to_lng);
118 const double q3 = sin((from_lng - to_lng) / 2.0);
119 const double q4 = cos((from_lng - to_lng) / 2.0);
120 const double q2 =
121 sin(from_lat + to_lat) * q3 * q3 - sin(from_lat - to_lat) * q4 * q4;
122 const double q5 =
123 cos(from_lat - to_lat) * q4 * q4 - cos(from_lat + to_lat) * q3 * q3;
124 return static_cast<int64_t>(kRadius * atan2(sqrt(q1 * q1 + q2 * q2), q5) +
125 1.0);
126}
127
128static int64_t Man2DDistance(const Coordinates3<double>& from,
129 const Coordinates3<double>& to) {
130 const double xd = fabs(from.x - to.x);
131 const double yd = fabs(from.y - to.y);
132 return std::round(xd + yd);
133}
134
135static int64_t Man3DDistance(const Coordinates3<double>& from,
136 const Coordinates3<double>& to) {
137 const double xd = fabs(from.x - to.x);
138 const double yd = fabs(from.y - to.y);
139 const double zd = fabs(from.z - to.z);
140 return std::round(xd + yd + zd);
141}
142
143static int64_t Max2DDistance(const Coordinates3<double>& from,
144 const Coordinates3<double>& to) {
145 const double xd = fabs(from.x - to.x);
146 const double yd = fabs(from.y - to.y);
147 return std::round(std::max(xd, yd));
148}
149
150static int64_t Max3DDistance(const Coordinates3<double>& from,
151 const Coordinates3<double>& to) {
152 const double xd = fabs(from.x - to.x);
153 const double yd = fabs(from.y - to.y);
154 const double zd = fabs(from.z - to.z);
155 return std::round(std::max(xd, std::max(yd, zd)));
156}
157
158std::shared_ptr<zipfile::ZipArchive> OpenZipArchiveIfItExists(
159 absl::string_view file_name) {
160 const absl::string_view archive_name = file::Dirname(file_name);
161 if (file::Extension(archive_name) == "zip") {
162 return zipfile::OpenZipArchive(archive_name);
163 } else {
164 return nullptr;
165 }
166}
167
168} // namespace
169
171 : size_(0),
172 capacity_(std::numeric_limits<int64_t>::max()),
173 max_distance_(std::numeric_limits<int64_t>::max()),
174 distance_function_(nullptr),
175 explicit_costs_(),
176 depot_(0),
177 section_(UNDEFINED_SECTION),
178 type_(Types::UNDEFINED_TYPE),
179 edge_weight_type_(UNDEFINED_EDGE_WEIGHT_TYPE),
180 edge_weight_format_(UNDEFINED_EDGE_WEIGHT_FORMAT),
181 edge_row_(0),
182 edge_column_(0),
183 to_read_(0) {}
184
185namespace {
186absl::StatusOr<File*> OpenFile(absl::string_view file_name) {
187 File* file = nullptr;
188 RETURN_IF_ERROR(file::Open(file_name, "r", &file, file::Defaults()));
189 return file;
190}
191} // namespace
192
193absl::Status TspLibParser::LoadFile(absl::string_view file_name) {
194 std::shared_ptr<zipfile::ZipArchive> zip_archive(
195 OpenZipArchiveIfItExists(file_name));
196 valid_section_found_ = false;
197 ASSIGN_OR_RETURN(File* const file, OpenFile(file_name));
198 for (const std::string& line :
200 ProcessNewLine(line);
201 }
202 FinalizeEdgeWeights();
203 if (!valid_section_found_) {
205 << "Could not find any valid sections in " << file_name;
206 }
207 return absl::OkStatus();
208}
209
210absl::StatusOr<int> TspLibParser::SizeFromFile(
211 absl::string_view file_name) const {
212 std::shared_ptr<zipfile::ZipArchive> zip_archive(
213 OpenZipArchiveIfItExists(file_name));
214 ASSIGN_OR_RETURN(File* const file, OpenFile(file_name));
215 int size = 0;
216 for (const std::string& line :
218 if (RE2::PartialMatch(line, "DIMENSION\\s*:\\s*(\\d+)", &size)) {
219 return size;
220 }
221 }
223 << "Could not determine problem size from " << file_name;
224}
225
226void TspLibParser::ParseExplicitFullMatrix(
227 absl::Span<const std::string> words) {
228 CHECK_LT(edge_row_, size_);
229 if (type_ == Types::SOP && to_read_ == size_ * size_) {
230 // Matrix size is present in SOP which is redundant with dimension and must
231 // not be confused with the first cell of the matrix.
232 return;
233 }
234 for (const std::string& word : words) {
235 SetExplicitCost(edge_row_, edge_column_, atoi64(word));
236 ++edge_column_;
237 if (edge_column_ >= size_) {
238 edge_column_ = 0;
239 ++edge_row_;
240 }
241 --to_read_;
242 }
243}
244
245void TspLibParser::ParseExplicitUpperRow(absl::Span<const std::string> words) {
246 CHECK_LT(edge_row_, size_);
247 for (const std::string& word : words) {
248 SetExplicitCost(edge_row_, edge_column_, atoi64(word));
249 SetExplicitCost(edge_column_, edge_row_, atoi64(word));
250 ++edge_column_;
251 if (edge_column_ >= size_) {
252 ++edge_row_;
253 SetExplicitCost(edge_row_, edge_row_, 0);
254 edge_column_ = edge_row_ + 1;
255 }
256 --to_read_;
257 }
258}
259
260void TspLibParser::ParseExplicitLowerRow(absl::Span<const std::string> words) {
261 CHECK_LT(edge_row_, size_);
262 for (const std::string& word : words) {
263 SetExplicitCost(edge_row_, edge_column_, atoi64(word));
264 SetExplicitCost(edge_column_, edge_row_, atoi64(word));
265 ++edge_column_;
266 if (edge_column_ >= edge_row_) {
267 SetExplicitCost(edge_column_, edge_column_, 0);
268 edge_column_ = 0;
269 ++edge_row_;
270 }
271 --to_read_;
272 }
273}
274
275void TspLibParser::ParseExplicitUpperDiagRow(
276 absl::Span<const std::string> words) {
277 CHECK_LT(edge_row_, size_);
278 for (const std::string& word : words) {
279 SetExplicitCost(edge_row_, edge_column_, atoi64(word));
280 SetExplicitCost(edge_column_, edge_row_, atoi64(word));
281 ++edge_column_;
282 if (edge_column_ >= size_) {
283 ++edge_row_;
284 edge_column_ = edge_row_;
285 }
286 --to_read_;
287 }
288}
289
290void TspLibParser::ParseExplicitLowerDiagRow(
291 absl::Span<const std::string> words) {
292 CHECK_LT(edge_row_, size_);
293 for (const std::string& word : words) {
294 SetExplicitCost(edge_row_, edge_column_, atoi64(word));
295 SetExplicitCost(edge_column_, edge_row_, atoi64(word));
296 ++edge_column_;
297 if (edge_column_ > edge_row_) {
298 edge_column_ = 0;
299 ++edge_row_;
300 }
301 --to_read_;
302 }
303}
304
305void TspLibParser::ParseNodeCoord(absl::Span<const std::string> words) {
306 CHECK_LE(3, words.size()) << words[0];
307 CHECK_GE(4, words.size()) << words[4];
308 const int node(atoi32(words[0]) - 1);
309 coords_[node].x = strings::ParseLeadingDoubleValue(words[1].c_str(), 0);
310 coords_[node].y = strings::ParseLeadingDoubleValue(words[2].c_str(), 0);
311 if (4 == words.size()) {
312 coords_[node].z = strings::ParseLeadingDoubleValue(words[3].c_str(), 0);
313 } else {
314 coords_[node].z = 0;
315 }
316 --to_read_;
317}
318
319void TspLibParser::SetUpEdgeWeightSection() {
320 edge_row_ = 0;
321 edge_column_ = 0;
322 switch (edge_weight_format_) {
323 case FULL_MATRIX:
324 to_read_ = size_ * size_;
325 break;
326 case LOWER_COL:
327 case UPPER_ROW:
328 SetExplicitCost(0, 0, 0);
329 ++edge_column_;
330 to_read_ = ((size_ - 1) * size_) / 2;
331 break;
332 case UPPER_COL:
333 case LOWER_ROW:
334 SetExplicitCost(0, 0, 0);
335 ++edge_row_;
336 to_read_ = ((size_ - 1) * size_) / 2;
337 break;
338 case LOWER_DIAG_COL:
339 case UPPER_DIAG_ROW:
340 to_read_ = ((size_ + 1) * size_) / 2;
341 break;
342 case UPPER_DIAG_COL:
343 case LOWER_DIAG_ROW:
344 to_read_ = ((size_ + 1) * size_) / 2;
345 break;
346 default:
347 LOG(WARNING) << "Unknown EDGE_WEIGHT_FORMAT: " << edge_weight_format_;
348 }
349}
350
351// Fill in the edge weight matrix.
352void TspLibParser::FinalizeEdgeWeights() {
353 distance_function_ = nullptr;
354 if (type_ == Types::HCP) {
355 VLOG(2) << "No edge weights";
356 return;
357 }
358 VLOG(2) << "Edge weight type: " << edge_weight_type_;
359 switch (edge_weight_type_) {
360 case EXPLICIT:
361 distance_function_ = [this](int from, int to) {
362 return explicit_costs_[from * size_ + to];
363 };
364 break;
365 case EUC_2D:
366 distance_function_ = [this](int from, int to) {
367 return Euc2DDistance(coords_[from], coords_[to]);
368 };
369 break;
370 case EUC_3D:
371 distance_function_ = [this](int from, int to) {
372 return Euc3DDistance(coords_[from], coords_[to]);
373 };
374 break;
375 case MAX_2D:
376 distance_function_ = [this](int from, int to) {
377 return Max2DDistance(coords_[from], coords_[to]);
378 };
379 break;
380 case MAX_3D:
381 distance_function_ = [this](int from, int to) {
382 return Max3DDistance(coords_[from], coords_[to]);
383 };
384 break;
385 case MAN_2D:
386 distance_function_ = [this](int from, int to) {
387 return Man2DDistance(coords_[from], coords_[to]);
388 };
389 break;
390 case MAN_3D:
391 distance_function_ = [this](int from, int to) {
392 return Man3DDistance(coords_[from], coords_[to]);
393 };
394 break;
395 case CEIL_2D:
396 distance_function_ = [this](int from, int to) {
397 return Ceil2DDistance(coords_[from], coords_[to]);
398 };
399 break;
400 case GEO:
401 distance_function_ = [this](int from, int to) {
402 return GeoDistance(coords_[from], coords_[to]);
403 };
404 break;
405 case GEOM:
406 distance_function_ = [this](int from, int to) {
407 return GeoMDistance(coords_[from], coords_[to]);
408 };
409 break;
410 case ATT:
411 distance_function_ = [this](int from, int to) {
412 return ATTDistance(coords_[from], coords_[to]);
413 };
414 break;
415 case XRAY1:
416 LOG(WARNING) << "XRAY1 not supported for EDGE_WEIGHT_TYPE";
417 break;
418 case XRAY2:
419 LOG(WARNING) << "XRAY2 not supported for EDGE_WEIGHT_TYPE";
420 break;
421 case SPECIAL:
422 LOG(WARNING) << "SPECIAL not supported for EDGE_WEIGHT_TYPE";
423 break;
424 default:
425 LOG(WARNING) << "Unknown EDGE_WEIGHT_TYPE: " << edge_weight_type_;
426 }
427}
428
429bool TspLibParser::ParseSections(absl::Span<const std::string> words) {
430 const int words_size = words.size();
431 CHECK_GT(words_size, 0);
432 if (!gtl::FindCopy(*kSections, words[0], &section_)) {
433 LOG(WARNING) << "Unknown section: " << words[0];
434 return false;
435 }
436 const std::string& last_word = words[words_size - 1];
437 switch (section_) {
438 case NAME: {
439 name_ = absl::StrJoin(words.begin() + 1, words.end(), " ");
440 break;
441 }
442 case TYPE: {
443 CHECK_LE(2, words.size());
444 const std::string& type = words[1];
445 if (!gtl::FindCopy(*kTypes, type, &type_)) {
446 LOG(WARNING) << "Unknown TYPE: " << type;
447 }
448 break;
449 }
450 case COMMENT: {
451 if (!comments_.empty()) {
452 comments_ += "\n";
453 }
454 comments_ += absl::StrJoin(words.begin() + 1, words.end(), " ");
455 break;
456 }
457 case DIMENSION: {
458 size_ = atoi32(last_word);
459 coords_.resize(size_);
460 break;
461 }
462 case DISTANCE: {
463 // This is non-standard but is supported as an upper bound on the length
464 // of each route.
465 max_distance_ = atoi64(last_word);
466 break;
467 }
468 case CAPACITY: {
469 capacity_ = atoi64(last_word);
470 break;
471 }
472 case EDGE_DATA_FORMAT: {
473 CHECK(Types::HCP == type_);
474 if (!gtl::FindCopy(*kEdgeDataFormats, last_word, &edge_data_format_)) {
475 LOG(WARNING) << "Unknown EDGE_DATA_FORMAT: " << last_word;
476 }
477 break;
478 }
479 case EDGE_DATA_SECTION: {
480 CHECK(Types::HCP == type_);
481 edges_.resize(size_);
482 to_read_ = 1;
483 break;
484 }
485 case EDGE_WEIGHT_TYPE: {
486 if (!gtl::FindCopy(*kEdgeWeightTypes, last_word, &edge_weight_type_)) {
487 // Some data sets invert EDGE_WEIGHT_TYPE and EDGE_WEIGHT_FORMAT.
488 LOG(WARNING) << "Unknown EDGE_WEIGHT_TYPE: " << last_word;
489 LOG(WARNING) << "Trying in EDGE_WEIGHT_FORMAT formats";
490 if (!gtl::FindCopy(*kEdgeWeightFormats, last_word,
491 &edge_weight_format_)) {
492 LOG(WARNING) << "Unknown EDGE_WEIGHT_FORMAT: " << last_word;
493 }
494 }
495 break;
496 }
497 case EDGE_WEIGHT_FORMAT: {
498 if (!gtl::FindCopy(*kEdgeWeightFormats, last_word,
499 &edge_weight_format_)) {
500 // Some data sets invert EDGE_WEIGHT_TYPE and EDGE_WEIGHT_FORMAT.
501 LOG(WARNING) << "Unknown EDGE_WEIGHT_FORMAT: " << last_word;
502 LOG(WARNING) << "Trying in EDGE_WEIGHT_TYPE types";
503 if (!gtl::FindCopy(*kEdgeWeightTypes, last_word, &edge_weight_type_)) {
504 LOG(WARNING) << "Unknown EDGE_WEIGHT_TYPE: " << last_word;
505 }
506 }
507 break;
508 }
509 case EDGE_WEIGHT_SECTION: {
510 SetUpEdgeWeightSection();
511 break;
512 }
513 case FIXED_EDGES_SECTION: {
514 to_read_ = std::numeric_limits<int64_t>::max();
515 break;
516 }
517 case NODE_COORD_TYPE: {
518 break;
519 }
520 case DISPLAY_DATA_TYPE: {
521 break;
522 }
523 case DISPLAY_DATA_SECTION: {
524 to_read_ = size_;
525 break;
526 }
527 case NODE_COORD_SECTION: {
528 to_read_ = size_;
529 break;
530 }
531 case DEPOT_SECTION: {
532 to_read_ = std::numeric_limits<int64_t>::max();
533 break;
534 }
535 case DEMAND_SECTION: {
536 demands_.resize(size_, 0);
537 to_read_ = size_;
538 break;
539 }
540 case END_OF_FILE: {
541 break;
542 }
543 default: {
544 LOG(WARNING) << "Unknown section: " << words[0];
545 }
546 }
547 return true;
548}
549
550void TspLibParser::ProcessNewLine(const std::string& line) {
551 const std::vector<std::string> words =
552 absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty());
553 if (!words.empty()) {
554 // New section detected.
555 if (kSections->contains(words[0])) {
556 to_read_ = 0;
557 }
558 if (to_read_ > 0) {
559 switch (section_) {
560 case EDGE_DATA_SECTION: {
561 CHECK(!words.empty());
562 switch (edge_data_format_) {
563 case EDGE_LIST: {
564 if (words[0] == "-1") {
565 CHECK_EQ(words.size(), 1);
566 // Remove duplicate edges
567 for (auto& edges : edges_) {
568 std::sort(edges.begin(), edges.end());
569 const auto it = std::unique(edges.begin(), edges.end());
570 edges.resize(std::distance(edges.begin(), it));
571 }
572 to_read_ = 0;
573 } else {
574 CHECK_EQ(words.size(), 2);
575 const int from = atoi64(words[0]) - 1;
576 const int to = atoi64(words[1]) - 1;
577 edges_[std::min(from, to)].push_back(std::max(from, to));
578 }
579 break;
580 }
581 case ADJ_LIST: {
582 const int from = atoi64(words[0]) - 1;
583 for (int i = 1; i < words.size(); ++i) {
584 const int to = atoi64(words[i]) - 1;
585 if (to != -2) {
586 edges_[std::min(from, to)].push_back(std::max(from, to));
587 } else {
588 CHECK_EQ(i, words.size() - 1);
589 }
590 }
591 if (atoi64(words.back()) != -1) {
592 LOG(WARNING) << "Missing -1 at the end of ADJ_LIST";
593 }
594 break;
595 }
596 default:
597 LOG(WARNING) << "Unknown EDGE_DATA_FORMAT: " << edge_data_format_;
598 }
599 break;
600 }
601 case EDGE_WEIGHT_SECTION: {
602 switch (edge_weight_format_) {
603 case FULL_MATRIX:
604 ParseExplicitFullMatrix(words);
605 break;
606 case UPPER_ROW:
607 case LOWER_COL:
608 ParseExplicitUpperRow(words);
609 break;
610 case LOWER_ROW:
611 case UPPER_COL:
612 ParseExplicitLowerRow(words);
613 break;
614 case UPPER_DIAG_ROW:
615 case LOWER_DIAG_COL:
616 ParseExplicitUpperDiagRow(words);
617 break;
618 case LOWER_DIAG_ROW:
619 case UPPER_DIAG_COL:
620 ParseExplicitLowerDiagRow(words);
621 break;
622 default:
623 LOG(WARNING) << "Unknown EDGE_WEIGHT_FORMAT: "
624 << edge_weight_format_;
625 }
626 break;
627 }
628 case FIXED_EDGES_SECTION: {
629 if (words.size() == 1) {
630 CHECK_EQ(-1, atoi64(words[0]));
631 to_read_ = 0;
632 } else {
633 CHECK_EQ(2, words.size());
634 fixed_edges_.insert(
635 std::make_pair(atoi64(words[0]) - 1, atoi64(words[1]) - 1));
636 }
637 break;
638 }
639 case NODE_COORD_SECTION: {
640 ParseNodeCoord(words);
641 break;
642 }
643 case DEPOT_SECTION: {
644 if (words.size() == 1) {
645 const int depot(atoi64(words[0]) - 1);
646 if (depot >= 0) {
647 VLOG(2) << "Depot: " << depot;
648 depot_ = depot;
649 } else {
650 to_read_ = 0;
651 }
652 } else if (words.size() >= 2) {
653 CHECK_GE(3, words.size()) << words[3];
654 const int depot(size_ - 1);
655 VLOG(2) << "Depot: " << depot;
656 depot_ = depot;
657 coords_[depot].x =
658 strings::ParseLeadingDoubleValue(words[0].c_str(), 0);
659 coords_[depot].y =
660 strings::ParseLeadingDoubleValue(words[1].c_str(), 0);
661 if (3 == words.size()) {
662 coords_[depot].z =
663 strings::ParseLeadingDoubleValue(words[2].c_str(), 0);
664 } else {
665 coords_[depot].z = 0;
666 }
667 }
668 break;
669 }
670 case DEMAND_SECTION: {
671 const int64_t node = atoi64(words[0]) - 1;
672 demands_[node] = atoi64(words[1]);
673 --to_read_;
674 break;
675 }
676 case DISPLAY_DATA_SECTION: {
677 ParseNodeCoord(words);
678 break;
679 }
680 default: {
681 LOG(ERROR) << "Reading data outside section";
682 }
683 }
684 } else {
685 // TODO(user): Check that proper sections were read (necessary and
686 // non-overlapping ones).
687 valid_section_found_ |= ParseSections(words);
688 }
689 }
690}
691
693 absl::Span<const std::vector<int>> routes) const {
694 std::string tours = absl::StrCat(
695 "NAME : ", name_, "\nCOMMENT :\nTYPE : TOUR\nDIMENSION : ", size(),
696 "\nTOUR_SECTION\n");
697 for (const auto& route : routes) {
698 for (const int node : route) {
699 absl::StrAppend(&tours, node + 1, "\n");
700 }
701 absl::StrAppend(&tours, "-1\n");
702 }
703 return absl::StrCat(tours, "EOF");
704}
705
706const absl::flat_hash_map<std::string, TspLibParser::Sections>* const
707 TspLibParser::kSections =
708 new absl::flat_hash_map<std::string, TspLibParser::Sections>(
709 {{"NAME", NAME},
710 {"TYPE", TYPE},
711 {"COMMENT", COMMENT},
712 {"DIMENSION", DIMENSION},
713 {"DISTANCE", DISTANCE},
714 {"CAPACITY", CAPACITY},
715 {"EDGE_DATA_FORMAT", EDGE_DATA_FORMAT},
716 {"EDGE_DATA_SECTION", EDGE_DATA_SECTION},
717 {"EDGE_WEIGHT_TYPE", EDGE_WEIGHT_TYPE},
718 {"EDGE_WEIGHT_FORMAT", EDGE_WEIGHT_FORMAT},
719 {"EDGE_WEIGHT_SECTION", EDGE_WEIGHT_SECTION},
720 {"FIXED_EDGES_SECTION", FIXED_EDGES_SECTION},
721 {"FIXED_EDGES", FIXED_EDGES_SECTION},
722 {"DISPLAY_DATA_SECTION", DISPLAY_DATA_SECTION},
723 {"NODE_COORD_TYPE", NODE_COORD_TYPE},
724 {"DISPLAY_DATA_TYPE", DISPLAY_DATA_TYPE},
725 {"NODE_COORD_SECTION", NODE_COORD_SECTION},
726 {"DEPOT_SECTION", DEPOT_SECTION},
727 {"DEMAND_SECTION", DEMAND_SECTION},
728 {"EOF", END_OF_FILE}});
729
730const absl::flat_hash_map<std::string, TspLibParser::Types>* const
731 TspLibParser::kTypes =
732 new absl::flat_hash_map<std::string, TspLibParser::Types>(
733 {{"TSP", Types::TSP},
734 {"ATSP", Types::ATSP},
735 {"SOP", Types::SOP},
736 {"HCP", Types::HCP},
737 {"CVRP", Types::CVRP},
738 {"TOUR", Types::TOUR}});
739
740const absl::flat_hash_map<std::string, TspLibParser::EdgeDataFormat>* const
741 TspLibParser::kEdgeDataFormats =
742 new absl::flat_hash_map<std::string, TspLibParser::EdgeDataFormat>(
743 {{"EDGE_LIST", EDGE_LIST}, {"ADJ_LIST", ADJ_LIST}});
744
745const absl::flat_hash_map<std::string, TspLibParser::EdgeWeightTypes>* const
746 TspLibParser::kEdgeWeightTypes =
747 new absl::flat_hash_map<std::string, TspLibParser::EdgeWeightTypes>(
748 {{"EXPLICIT", EXPLICIT},
749 {"EUC_2D", EUC_2D},
750 {"EUC_3D", EUC_3D},
751 {"MAX_2D", MAX_2D},
752 {"MAX_3D", MAX_3D},
753 {"MAN_2D", MAN_2D},
754 {"MAN_3D", MAN_3D},
755 {"CEIL_2D", CEIL_2D},
756 {"GEO", GEO},
757 {"GEOM", GEOM},
758 {"ATT", ATT},
759 {"XRAY1", XRAY1},
760 {"XRAY2", XRAY2},
761 {"SPECIAL", SPECIAL}});
762
763const absl::flat_hash_map<std::string, TspLibParser::EdgeWeightFormats>* const
764 TspLibParser::kEdgeWeightFormats =
765 new absl::flat_hash_map<std::string, TspLibParser::EdgeWeightFormats>(
766 {{"FUNCTION", FUNCTION},
767 {"FULL_MATRIX", FULL_MATRIX},
768 {"UPPER_ROW", UPPER_ROW},
769 {"LOWER_ROW", LOWER_ROW},
770 {"UPPER_DIAG_ROW", UPPER_DIAG_ROW},
771 {"LOWER_DIAG_ROW", LOWER_DIAG_ROW},
772 {"UPPER_COL", UPPER_COL},
773 {"LOWER_COL", LOWER_COL},
774 {"UPPER_DIAG_COL", UPPER_DIAG_COL},
775 {"LOWER_DIAG_COL", LOWER_DIAG_COL}});
776
777TspLibTourParser::TspLibTourParser() : section_(UNDEFINED_SECTION), size_(0) {}
778
779absl::Status TspLibTourParser::LoadFile(absl::string_view file_name) {
780 section_ = UNDEFINED_SECTION;
781 comments_.clear();
782 tour_.clear();
783 std::shared_ptr<zipfile::ZipArchive> zip_archive(
784 OpenZipArchiveIfItExists(file_name));
785 ASSIGN_OR_RETURN(File* const file, OpenFile(file_name));
786 for (const std::string& line :
788 ProcessNewLine(line);
789 }
790 return absl::OkStatus();
791}
792
793void TspLibTourParser::ProcessNewLine(const std::string& line) {
794 const std::vector<std::string> words =
795 absl::StrSplit(line, ' ', absl::SkipEmpty());
796 const int word_size = words.size();
797 if (word_size > 0) {
798 if (section_ == TOUR_SECTION) {
799 for (const std::string& word : words) {
800 const int node = atoi32(word);
801 if (node >= 0) {
802 tour_.push_back(atoi32(word) - 1);
803 } else {
804 section_ = UNDEFINED_SECTION;
805 }
806 }
807 } else {
808 const std::string& last_word = words[word_size - 1];
809 if (!gtl::FindCopy(*kSections, words[0], &section_)) {
810 LOG(WARNING) << "Unknown section: " << words[0];
811 return;
812 }
813 switch (section_) {
814 case NAME:
815 break;
816 case TYPE:
817 CHECK_EQ("TOUR", last_word);
818 break;
819 case COMMENT: {
820 comments_ = absl::StrJoin(words.begin() + 1, words.end(), " ");
821 break;
822 }
823 case DIMENSION:
824 size_ = atoi32(last_word);
825 break;
826 case TOUR_SECTION:
827 break;
828 case END_OF_FILE:
829 break;
830 default:
831 LOG(WARNING) << "Unknown key word: " << words[0];
832 }
833 }
834 }
835}
836
837const absl::flat_hash_map<std::string, TspLibTourParser::Sections>* const
838 TspLibTourParser::kSections =
839 new absl::flat_hash_map<std::string, TspLibTourParser::Sections>(
840 {{"NAME", NAME},
841 {"TYPE", TYPE},
842 {"COMMENT", COMMENT},
843 {"DIMENSION", DIMENSION},
844 {"TOUR_SECTION", TOUR_SECTION},
845 {"EOF", END_OF_FILE}});
846
848
849absl::Status CVRPToursParser::LoadFile(absl::string_view file_name) {
850 tours_.clear();
851 cost_ = 0;
852 std::shared_ptr<zipfile::ZipArchive> zip_archive(
853 OpenZipArchiveIfItExists(file_name));
854 ASSIGN_OR_RETURN(File* const file, OpenFile(file_name));
855 for (const std::string& line :
857 ProcessNewLine(line);
858 }
859 return absl::OkStatus();
860}
861
862void CVRPToursParser::ProcessNewLine(const std::string& line) {
863 const std::vector<std::string> words =
864 absl::StrSplit(line, ' ', absl::SkipEmpty());
865 const int word_size = words.size();
866 if (word_size > 0) {
867 if (absl::AsciiStrToUpper(words[0]) == "COST") {
868 CHECK_EQ(word_size, 2);
869 cost_ = atoi32(words[1]);
870 return;
871 }
872 if (absl::AsciiStrToUpper(words[0]) == "ROUTE") {
873 CHECK_GT(word_size, 2);
874 tours_.resize(tours_.size() + 1);
875 for (int i = 2; i < word_size; ++i) {
876 tours_.back().push_back(atoi32(words[i]));
877 }
878 return;
879 }
880 LOG(WARNING) << "Unknown key word: " << words[0];
881 }
882}
883
884} // namespace operations_research::routing
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
Definition file.h:30
absl::Status LoadFile(absl::string_view file_name)
std::string BuildTourFromRoutes(absl::Span< const std::vector< int > > routes) const
absl::StatusOr< int > SizeFromFile(absl::string_view file_name) const
const std::vector< std::vector< int > > & edges() const
absl::Status LoadFile(absl::string_view file_name)
absl::Status LoadFile(absl::string_view file_name)
Definition file.cc:327
absl::string_view Extension(absl::string_view path)
Definition path.cc:133
Options Defaults()
Definition file.h:86
absl::Status Open(absl::string_view file_name, absl::string_view mode, File **f, Options options)
Definition file.cc:328
absl::string_view Dirname(absl::string_view path)
Definition path.cc:105
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
Definition map_util.h:190
int32_t atoi32(absl::string_view word)
Definition strtoint.h:56
int64_t atoi64(absl::string_view word)
Definition strtoint.h:57
STL namespace.
double ParseLeadingDoubleValue(const char *str, double deflt)
Definition numbers.cc:205
StatusBuilder InvalidArgumentErrorBuilder()
std::shared_ptr< ZipArchive > OpenZipArchive(absl::string_view path, const ZipFileOptions &options)
Definition zipfile.cc:32
trees with all degrees equal to