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"
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);
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);
62static int64_t Euc2DDistance(
const Coordinates3<double>& from,
63 const Coordinates3<double>&
to) {
64 return std::round(DoubleEuc2DDistance(from,
to));
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);
76static int64_t Ceil2DDistance(
const Coordinates3<double>& from,
77 const Coordinates3<double>&
to) {
78 return static_cast<int64_t
>(ceil(DoubleEuc2DDistance(from,
to)));
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;
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);
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);
110 sin(from_lat + to_lat) * q3 * q3 - sin(from_lat - to_lat) * q4 * q4;
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) +
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);
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);
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));
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)));
147std::shared_ptr<zipfile::ZipArchive> OpenZipArchiveIfItExists(
148 absl::string_view file_name) {
149 const absl::string_view archive_name =
file::Dirname(file_name);
163 distance_function_(nullptr),
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),
175 std::shared_ptr<zipfile::ZipArchive> zip_archive(
176 OpenZipArchiveIfItExists(file_name));
177 for (
const std::string&
line :
179 ProcessNewLine(
line);
181 FinalizeEdgeWeights();
186 std::shared_ptr<zipfile::ZipArchive> zip_archive(
187 OpenZipArchiveIfItExists(file_name));
189 for (
const std::string&
line :
191 if (RE2::PartialMatch(
line,
"DIMENSION\\s*:\\s*(\\d+)", &
size)) {
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_) {
206 for (
const std::string& word : words) {
207 SetExplicitCost(edge_row_, edge_column_,
atoi64(word));
209 if (edge_column_ >= size_) {
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));
223 if (edge_column_ >= size_) {
225 SetExplicitCost(edge_row_, edge_row_, 0);
226 edge_column_ = edge_row_ + 1;
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));
238 if (edge_column_ >= edge_row_) {
239 SetExplicitCost(edge_column_, edge_column_, 0);
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));
254 if (edge_column_ >= size_) {
256 edge_column_ = edge_row_;
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));
269 if (edge_column_ > edge_row_) {
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);
283 if (4 == words.size()) {
291void TspLibParser::SetUpEdgeWeightSection() {
294 switch (edge_weight_format_) {
296 to_read_ = size_ * size_;
300 SetExplicitCost(0, 0, 0);
302 to_read_ = ((size_ - 1) * size_) / 2;
306 SetExplicitCost(0, 0, 0);
308 to_read_ = ((size_ - 1) * size_) / 2;
312 to_read_ = ((size_ + 1) * size_) / 2;
316 to_read_ = ((size_ + 1) * size_) / 2;
319 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_FORMAT: " << edge_weight_format_;
324void TspLibParser::FinalizeEdgeWeights() {
325 distance_function_ =
nullptr;
327 VLOG(2) <<
"No edge weights";
330 VLOG(2) <<
"Edge weight type: " << edge_weight_type_;
331 switch (edge_weight_type_) {
333 distance_function_ = [
this](
int from,
int to) {
334 return explicit_costs_[from * size_ +
to];
338 distance_function_ = [
this](
int from,
int to) {
339 return Euc2DDistance(coords_[from], coords_[
to]);
343 distance_function_ = [
this](
int from,
int to) {
344 return Euc3DDistance(coords_[from], coords_[
to]);
348 distance_function_ = [
this](
int from,
int to) {
349 return Max2DDistance(coords_[from], coords_[
to]);
353 distance_function_ = [
this](
int from,
int to) {
354 return Max3DDistance(coords_[from], coords_[
to]);
358 distance_function_ = [
this](
int from,
int to) {
359 return Man2DDistance(coords_[from], coords_[
to]);
363 distance_function_ = [
this](
int from,
int to) {
364 return Man3DDistance(coords_[from], coords_[
to]);
368 distance_function_ = [
this](
int from,
int to) {
369 return Ceil2DDistance(coords_[from], coords_[
to]);
373 distance_function_ = [
this](
int from,
int to) {
374 return GeoDistance(coords_[from], coords_[
to]);
378 distance_function_ = [
this](
int from,
int to) {
379 return GeoMDistance(coords_[from], coords_[
to]);
383 distance_function_ = [
this](
int from,
int to) {
384 return ATTDistance(coords_[from], coords_[
to]);
388 LOG(WARNING) <<
"XRAY1 not supported for EDGE_WEIGHT_TYPE";
391 LOG(WARNING) <<
"XRAY2 not supported for EDGE_WEIGHT_TYPE";
394 LOG(WARNING) <<
"SPECIAL not supported for EDGE_WEIGHT_TYPE";
397 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_TYPE: " << edge_weight_type_;
401void TspLibParser::ParseSections(absl::Span<const std::string> words) {
402 const int words_size = words.size();
403 CHECK_GT(words_size, 0);
405 LOG(WARNING) <<
"Unknown section: " << words[0];
408 const std::string& last_word = words[words_size - 1];
411 name_ = absl::StrJoin(words.begin() + 1, words.end(),
" ");
415 CHECK_LE(2, words.size());
416 const std::string&
type = words[1];
418 LOG(WARNING) <<
"Unknown TYPE: " <<
type;
423 if (!comments_.empty()) {
426 comments_ += absl::StrJoin(words.begin() + 1, words.end(),
" ");
430 size_ =
atoi32(last_word);
431 coords_.resize(size_);
437 max_distance_ =
atoi64(last_word);
441 capacity_ =
atoi64(last_word);
444 case EDGE_DATA_FORMAT: {
446 if (!
gtl::FindCopy(*kEdgeDataFormats, last_word, &edge_data_format_)) {
447 LOG(WARNING) <<
"Unknown EDGE_DATA_FORMAT: " << last_word;
451 case EDGE_DATA_SECTION: {
453 edges_.resize(size_);
457 case EDGE_WEIGHT_TYPE: {
458 if (!
gtl::FindCopy(*kEdgeWeightTypes, last_word, &edge_weight_type_)) {
460 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_TYPE: " << last_word;
461 LOG(WARNING) <<
"Trying in EDGE_WEIGHT_FORMAT formats";
463 &edge_weight_format_)) {
464 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_FORMAT: " << last_word;
469 case EDGE_WEIGHT_FORMAT: {
471 &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;
481 case EDGE_WEIGHT_SECTION: {
482 SetUpEdgeWeightSection();
485 case FIXED_EDGES_SECTION: {
489 case NODE_COORD_TYPE: {
492 case DISPLAY_DATA_TYPE: {
495 case DISPLAY_DATA_SECTION: {
499 case NODE_COORD_SECTION: {
503 case DEPOT_SECTION: {
507 case DEMAND_SECTION: {
508 demands_.resize(size_, 0);
516 LOG(WARNING) <<
"Unknown section: " << words[0];
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()) {
526 if (kSections->contains(words[0])) {
531 case EDGE_DATA_SECTION: {
532 CHECK(!words.empty());
533 switch (edge_data_format_) {
535 if (words[0] ==
"-1") {
536 CHECK_EQ(words.size(), 1);
538 for (
auto&
edges : edges_) {
540 const auto it = std::unique(
edges.begin(),
edges.end());
541 edges.resize(std::distance(
edges.begin(), it));
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));
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;
557 edges_[std::min(from,
to)].push_back(std::max(from,
to));
559 CHECK_EQ(i, words.size() - 1);
562 if (
atoi64(words.back()) != -1) {
563 LOG(WARNING) <<
"Missing -1 at the end of ADJ_LIST";
568 LOG(WARNING) <<
"Unknown EDGE_DATA_FORMAT: " << edge_data_format_;
572 case EDGE_WEIGHT_SECTION: {
573 switch (edge_weight_format_) {
575 ParseExplicitFullMatrix(words);
579 ParseExplicitUpperRow(words);
583 ParseExplicitLowerRow(words);
587 ParseExplicitUpperDiagRow(words);
591 ParseExplicitLowerDiagRow(words);
594 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_FORMAT: "
595 << edge_weight_format_;
599 case FIXED_EDGES_SECTION: {
600 if (words.size() == 1) {
601 CHECK_EQ(-1,
atoi64(words[0]));
604 CHECK_EQ(2, words.size());
606 std::make_pair(
atoi64(words[0]) - 1,
atoi64(words[1]) - 1));
610 case NODE_COORD_SECTION: {
611 ParseNodeCoord(words);
614 case DEPOT_SECTION: {
615 if (words.size() == 1) {
618 VLOG(2) <<
"Depot: " <<
depot;
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;
632 if (3 == words.size()) {
636 coords_[
depot].z = 0;
641 case DEMAND_SECTION: {
642 const int64_t node =
atoi64(words[0]) - 1;
643 demands_[node] =
atoi64(words[1]);
647 case DISPLAY_DATA_SECTION: {
648 ParseNodeCoord(words);
652 LOG(ERROR) <<
"Reading data outside section";
656 ParseSections(words);
662 absl::Span<
const std::vector<int>> routes)
const {
663 std::string tours = absl::StrCat(
664 "NAME : ", name_,
"\nCOMMENT :\nTYPE : TOUR\nDIMENSION : ",
size(),
666 for (
const auto& route : routes) {
667 for (
const int node : route) {
668 absl::StrAppend(&tours, node + 1,
"\n");
670 absl::StrAppend(&tours,
"-1\n");
672 return absl::StrCat(tours,
"EOF");
675const absl::flat_hash_map<std::string, TspLibParser::Sections>*
const
676 TspLibParser::kSections =
677 new absl::flat_hash_map<std::string, TspLibParser::Sections>(
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}});
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},
706 {
"CVRP", Types::CVRP},
707 {
"TOUR", Types::TOUR}});
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}});
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},
724 {
"CEIL_2D", CEIL_2D},
730 {
"SPECIAL", SPECIAL}});
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}});
751 section_ = UNDEFINED_SECTION;
754 std::shared_ptr<zipfile::ZipArchive> zip_archive(
755 OpenZipArchiveIfItExists(file_name));
756 for (
const std::string&
line :
758 ProcessNewLine(
line);
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();
768 if (section_ == TOUR_SECTION) {
769 for (
const std::string& word : words) {
770 const int node =
atoi32(word);
772 tour_.push_back(
atoi32(word) - 1);
774 section_ = UNDEFINED_SECTION;
778 const std::string& last_word = words[word_size - 1];
780 LOG(WARNING) <<
"Unknown section: " << words[0];
787 CHECK_EQ(
"TOUR", last_word);
790 comments_ = absl::StrJoin(words.begin() + 1, words.end(),
" ");
794 size_ =
atoi32(last_word);
801 LOG(WARNING) <<
"Unknown key word: " << words[0];
807const absl::flat_hash_map<std::string, TspLibTourParser::Sections>*
const
808 TspLibTourParser::kSections =
809 new absl::flat_hash_map<std::string, TspLibTourParser::Sections>(
812 {
"COMMENT", COMMENT},
813 {
"DIMENSION", DIMENSION},
814 {
"TOUR_SECTION", TOUR_SECTION},
815 {
"EOF", END_OF_FILE}});
824 std::shared_ptr<zipfile::ZipArchive> zip_archive(
825 OpenZipArchiveIfItExists(file_name));
826 for (
const std::string&
line :
828 ProcessNewLine(
line);
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();
838 if (absl::AsciiStrToUpper(words[0]) ==
"COST") {
839 CHECK_EQ(word_size, 2);
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]));
851 LOG(WARNING) <<
"Unknown key word: " << words[0];
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)
absl::string_view Dirname(absl::string_view path)
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
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.
int64_t atoi64(absl::string_view word)
double ParseLeadingDoubleValue(const char *str, double deflt)
std::shared_ptr< ZipArchive > OpenZipArchive(absl::string_view path, const ZipFileOptions &options)
trees with all degrees equal to
static const int64_t kint64max