OR-Tools  9.6
tsplib_parser.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 "ortools/base/map_util.h"
30 #include "ortools/base/numbers.h"
31 #include "ortools/base/path.h"
32 #include "ortools/base/strtoint.h"
33 #include "ortools/base/zipfile.h"
35 #include "re2/re2.h"
36 
37 namespace operations_research {
38 namespace {
39 
40 // ----- Distances -----
41 // As defined by the TSPLIB95 doc
42 
43 static int64_t ATTDistance(const Coordinates3<double>& from,
44  const Coordinates3<double>& to) {
45  const double xd = from.x - to.x;
46  const double yd = from.y - to.y;
47  const double euc = sqrt((xd * xd + yd * yd) / 10.0);
48  int64_t distance = std::round(euc);
49  if (distance < euc) ++distance;
50  return distance;
51 }
52 
53 static double DoubleEuc2DDistance(const Coordinates3<double>& from,
54  const Coordinates3<double>& to) {
55  const double xd = from.x - to.x;
56  const double yd = from.y - to.y;
57  return sqrt(xd * xd + yd * yd);
58 }
59 
60 static int64_t Euc2DDistance(const Coordinates3<double>& from,
61  const Coordinates3<double>& to) {
62  return std::round(DoubleEuc2DDistance(from, to));
63 }
64 
65 static int64_t Euc3DDistance(const Coordinates3<double>& from,
66  const Coordinates3<double>& to) {
67  const double xd = from.x - to.x;
68  const double yd = from.y - to.y;
69  const double zd = from.z - to.z;
70  const double euc = sqrt(xd * xd + yd * yd + zd * zd);
71  return std::round(euc);
72 }
73 
74 static int64_t Ceil2DDistance(const Coordinates3<double>& from,
75  const Coordinates3<double>& to) {
76  return static_cast<int64_t>(ceil(DoubleEuc2DDistance(from, to)));
77 }
78 
79 static double ToRad(double x) {
80  static const double kPi = 3.141592;
81  const int64_t deg = static_cast<int64_t>(x);
82  const double min = x - deg;
83  return kPi * (deg + 5.0 * min / 3.0) / 180.0;
84 }
85 
86 static int64_t GeoDistance(const Coordinates3<double>& from,
87  const Coordinates3<double>& to) {
88  static const double kRadius = 6378.388;
89  const double q1 = cos(ToRad(from.y) - ToRad(to.y));
90  const double q2 = cos(ToRad(from.x) - ToRad(to.x));
91  const double q3 = cos(ToRad(from.x) + ToRad(to.x));
92  return static_cast<int64_t>(
93  kRadius * acos(0.5 * ((1.0 + q1) * q2 - (1.0 - q1) * q3)) + 1.0);
94 }
95 
96 static int64_t GeoMDistance(const Coordinates3<double>& from,
97  const Coordinates3<double>& to) {
98  static const double kPi = 3.14159265358979323846264;
99  static const double kRadius = 6378388.0;
100  const double from_lat = kPi * from.x / 180.0;
101  const double to_lat = kPi * to.x / 180.0;
102  const double from_lng = kPi * from.y / 180.0;
103  const double to_lng = kPi * to.y / 180.0;
104  const double q1 = cos(to_lat) * sin(from_lng - to_lng);
105  const double q3 = sin((from_lng - to_lng) / 2.0);
106  const double q4 = cos((from_lng - to_lng) / 2.0);
107  const double q2 =
108  sin(from_lat + to_lat) * q3 * q3 - sin(from_lat - to_lat) * q4 * q4;
109  const double q5 =
110  cos(from_lat - to_lat) * q4 * q4 - cos(from_lat + to_lat) * q3 * q3;
111  return static_cast<int64_t>(kRadius * atan2(sqrt(q1 * q1 + q2 * q2), q5) +
112  1.0);
113 }
114 
115 static int64_t Man2DDistance(const Coordinates3<double>& from,
116  const Coordinates3<double>& to) {
117  const double xd = fabs(from.x - to.x);
118  const double yd = fabs(from.y - to.y);
119  return std::round(xd + yd);
120 }
121 
122 static int64_t Man3DDistance(const Coordinates3<double>& from,
123  const Coordinates3<double>& to) {
124  const double xd = fabs(from.x - to.x);
125  const double yd = fabs(from.y - to.y);
126  const double zd = fabs(from.z - to.z);
127  return std::round(xd + yd + zd);
128 }
129 
130 static int64_t Max2DDistance(const Coordinates3<double>& from,
131  const Coordinates3<double>& to) {
132  const double xd = fabs(from.x - to.x);
133  const double yd = fabs(from.y - to.y);
134  return std::round(std::max(xd, yd));
135 }
136 
137 static int64_t Max3DDistance(const Coordinates3<double>& from,
138  const Coordinates3<double>& to) {
139  const double xd = fabs(from.x - to.x);
140  const double yd = fabs(from.y - to.y);
141  const double zd = fabs(from.z - to.z);
142  return std::round(std::max(xd, std::max(yd, zd)));
143 }
144 
145 std::shared_ptr<zipfile::ZipArchive> OpenZipArchiveIfItExists(
146  const std::string& file_name) {
147  const absl::string_view archive_name = file::Dirname(file_name);
148  if (file::Extension(archive_name) == "zip") {
149  return zipfile::OpenZipArchive(archive_name);
150  } else {
151  return nullptr;
152  }
153 }
154 
155 } // namespace
156 
158  : size_(0),
159  capacity_(kint64max),
160  max_distance_(kint64max),
161  distance_function_(nullptr),
162  explicit_costs_(),
163  depot_(0),
164  section_(UNDEFINED_SECTION),
165  type_(Types::UNDEFINED_TYPE),
166  edge_weight_type_(UNDEFINED_EDGE_WEIGHT_TYPE),
167  edge_weight_format_(UNDEFINED_EDGE_WEIGHT_FORMAT),
168  edge_row_(0),
169  edge_column_(0),
170  to_read_(0) {}
171 
172 bool TspLibParser::LoadFile(const std::string& file_name) {
173  std::shared_ptr<zipfile::ZipArchive> zip_archive(
174  OpenZipArchiveIfItExists(file_name));
175  for (const std::string& line :
177  ProcessNewLine(line);
178  }
179  FinalizeEdgeWeights();
180  return true;
181 }
182 
183 int TspLibParser::SizeFromFile(const std::string& file_name) const {
184  std::shared_ptr<zipfile::ZipArchive> zip_archive(
185  OpenZipArchiveIfItExists(file_name));
186  int size = 0;
187  for (const std::string& line :
189  if (RE2::PartialMatch(line, "DIMENSION\\s*:\\s*(\\d+)", &size)) {
190  break;
191  }
192  }
193  return size;
194 }
195 
196 void TspLibParser::ParseExplicitFullMatrix(
197  const std::vector<std::string>& words) {
198  CHECK_LT(edge_row_, size_);
199  if (type_ == Types::SOP && to_read_ == size_ * size_) {
200  // Matrix size is present in SOP which is redundant with dimension and must
201  // not be confused with the first cell of the matrix.
202  return;
203  }
204  for (const std::string& word : words) {
205  SetExplicitCost(edge_row_, edge_column_, atoi64(word));
206  ++edge_column_;
207  if (edge_column_ >= size_) {
208  edge_column_ = 0;
209  ++edge_row_;
210  }
211  --to_read_;
212  }
213 }
214 
215 void TspLibParser::ParseExplicitUpperRow(
216  const std::vector<std::string>& words) {
217  CHECK_LT(edge_row_, size_);
218  for (const std::string& word : words) {
219  SetExplicitCost(edge_row_, edge_column_, atoi64(word));
220  SetExplicitCost(edge_column_, edge_row_, atoi64(word));
221  ++edge_column_;
222  if (edge_column_ >= size_) {
223  ++edge_row_;
224  SetExplicitCost(edge_row_, edge_row_, 0);
225  edge_column_ = edge_row_ + 1;
226  }
227  --to_read_;
228  }
229 }
230 
231 void TspLibParser::ParseExplicitLowerRow(
232  const std::vector<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 
247 void TspLibParser::ParseExplicitUpperDiagRow(
248  const std::vector<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 
262 void TspLibParser::ParseExplicitLowerDiagRow(
263  const std::vector<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 
277 void TspLibParser::ParseNodeCoord(const std::vector<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 
291 void 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.
324 void 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 
401 void TspLibParser::ParseSections(const std::vector<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 
521 void 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  const std::vector<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 
675 const 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 
699 const 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 
709 const 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 
714 const 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 
732 const 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 
746 TspLibTourParser::TspLibTourParser() : section_(UNDEFINED_SECTION), size_(0) {}
747 
748 // TODO(user): Return false when issues were encountered while parsing the
749 // file.
750 bool 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 
763 void 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 
807 const 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.
821 bool 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 
833 void 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 max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
bool LoadFile(const std::string &file_name)
int SizeFromFile(const std::string &file_name) const
bool LoadFile(const std::string &file_name)
const std::vector< std::vector< int > > & edges() const
Definition: tsplib_parser.h:75
std::string BuildTourFromRoutes(const std::vector< std::vector< int >> &routes) const
bool LoadFile(const std::string &file_name)
static const int64_t kint64max
absl::string_view Dirname(absl::string_view path)
Definition: path.cc:105
absl::string_view Extension(absl::string_view path)
Definition: path.cc:133
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
Definition: map_util.h:185
Collection of objects used to extend the Constraint Solver library.
int64_t atoi64(const std::string &word)
Definition: strtoint.h:58
int32_t atoi32(const std::string &word)
Definition: strtoint.h:57
double ParseLeadingDoubleValue(const char *str, double deflt)
Definition: numbers.cc:200
std::shared_ptr< ZipArchive > OpenZipArchive(absl::string_view path, const ZipFileOptions &options)
Definition: zipfile.cc:32
int line
Definition: parse_proto.cc:31
double distance
#define VLOG(verboselevel)
Definition: vlog.h:39