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