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"
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;
68 const double xd = from.x -
to.x;
69 const double yd = from.y -
to.y;
70 return sqrt(xd * xd + yd * yd);
75 return std::round(DoubleEuc2DDistance(from,
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);
89 return static_cast<int64_t
>(ceil(DoubleEuc2DDistance(from,
to)));
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;
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);
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);
121 sin(from_lat + to_lat) * q3 * q3 - sin(from_lat - to_lat) * q4 * q4;
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) +
130 const double xd = fabs(from.x -
to.x);
131 const double yd = fabs(from.y -
to.y);
132 return std::round(xd + yd);
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);
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));
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)));
158std::shared_ptr<zipfile::ZipArchive> OpenZipArchiveIfItExists(
159 absl::string_view file_name) {
160 const absl::string_view archive_name =
file::Dirname(file_name);
172 capacity_(
std::numeric_limits<int64_t>::max()),
173 max_distance_(
std::numeric_limits<int64_t>::max()),
174 distance_function_(nullptr),
177 section_(UNDEFINED_SECTION),
179 edge_weight_type_(UNDEFINED_EDGE_WEIGHT_TYPE),
180 edge_weight_format_(UNDEFINED_EDGE_WEIGHT_FORMAT),
186absl::StatusOr<File*> OpenFile(absl::string_view file_name) {
194 std::shared_ptr<zipfile::ZipArchive> zip_archive(
195 OpenZipArchiveIfItExists(file_name));
196 valid_section_found_ =
false;
198 for (
const std::string& line :
200 ProcessNewLine(line);
202 FinalizeEdgeWeights();
203 if (!valid_section_found_) {
205 <<
"Could not find any valid sections in " << file_name;
207 return absl::OkStatus();
211 absl::string_view file_name)
const {
212 std::shared_ptr<zipfile::ZipArchive> zip_archive(
213 OpenZipArchiveIfItExists(file_name));
216 for (
const std::string& line :
218 if (RE2::PartialMatch(line,
"DIMENSION\\s*:\\s*(\\d+)", &
size)) {
223 <<
"Could not determine problem size from " << file_name;
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_) {
234 for (
const std::string& word : words) {
235 SetExplicitCost(edge_row_, edge_column_,
atoi64(word));
237 if (edge_column_ >= size_) {
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));
251 if (edge_column_ >= size_) {
253 SetExplicitCost(edge_row_, edge_row_, 0);
254 edge_column_ = edge_row_ + 1;
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));
266 if (edge_column_ >= edge_row_) {
267 SetExplicitCost(edge_column_, edge_column_, 0);
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));
282 if (edge_column_ >= size_) {
284 edge_column_ = edge_row_;
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));
297 if (edge_column_ > edge_row_) {
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);
311 if (4 == words.size()) {
319void TspLibParser::SetUpEdgeWeightSection() {
322 switch (edge_weight_format_) {
324 to_read_ = size_ * size_;
328 SetExplicitCost(0, 0, 0);
330 to_read_ = ((size_ - 1) * size_) / 2;
334 SetExplicitCost(0, 0, 0);
336 to_read_ = ((size_ - 1) * size_) / 2;
340 to_read_ = ((size_ + 1) * size_) / 2;
344 to_read_ = ((size_ + 1) * size_) / 2;
347 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_FORMAT: " << edge_weight_format_;
352void TspLibParser::FinalizeEdgeWeights() {
353 distance_function_ =
nullptr;
355 VLOG(2) <<
"No edge weights";
358 VLOG(2) <<
"Edge weight type: " << edge_weight_type_;
359 switch (edge_weight_type_) {
361 distance_function_ = [
this](
int from,
int to) {
362 return explicit_costs_[from * size_ +
to];
366 distance_function_ = [
this](
int from,
int to) {
367 return Euc2DDistance(coords_[from], coords_[
to]);
371 distance_function_ = [
this](
int from,
int to) {
372 return Euc3DDistance(coords_[from], coords_[
to]);
376 distance_function_ = [
this](
int from,
int to) {
377 return Max2DDistance(coords_[from], coords_[
to]);
381 distance_function_ = [
this](
int from,
int to) {
382 return Max3DDistance(coords_[from], coords_[
to]);
386 distance_function_ = [
this](
int from,
int to) {
387 return Man2DDistance(coords_[from], coords_[
to]);
391 distance_function_ = [
this](
int from,
int to) {
392 return Man3DDistance(coords_[from], coords_[
to]);
396 distance_function_ = [
this](
int from,
int to) {
397 return Ceil2DDistance(coords_[from], coords_[
to]);
401 distance_function_ = [
this](
int from,
int to) {
402 return GeoDistance(coords_[from], coords_[
to]);
406 distance_function_ = [
this](
int from,
int to) {
407 return GeoMDistance(coords_[from], coords_[
to]);
411 distance_function_ = [
this](
int from,
int to) {
412 return ATTDistance(coords_[from], coords_[
to]);
416 LOG(WARNING) <<
"XRAY1 not supported for EDGE_WEIGHT_TYPE";
419 LOG(WARNING) <<
"XRAY2 not supported for EDGE_WEIGHT_TYPE";
422 LOG(WARNING) <<
"SPECIAL not supported for EDGE_WEIGHT_TYPE";
425 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_TYPE: " << edge_weight_type_;
429bool TspLibParser::ParseSections(absl::Span<const std::string> words) {
430 const int words_size = words.size();
431 CHECK_GT(words_size, 0);
433 LOG(WARNING) <<
"Unknown section: " << words[0];
436 const std::string& last_word = words[words_size - 1];
439 name_ = absl::StrJoin(words.begin() + 1, words.end(),
" ");
443 CHECK_LE(2, words.size());
444 const std::string&
type = words[1];
446 LOG(WARNING) <<
"Unknown TYPE: " <<
type;
451 if (!comments_.empty()) {
454 comments_ += absl::StrJoin(words.begin() + 1, words.end(),
" ");
458 size_ =
atoi32(last_word);
459 coords_.resize(size_);
465 max_distance_ =
atoi64(last_word);
469 capacity_ =
atoi64(last_word);
472 case EDGE_DATA_FORMAT: {
474 if (!
gtl::FindCopy(*kEdgeDataFormats, last_word, &edge_data_format_)) {
475 LOG(WARNING) <<
"Unknown EDGE_DATA_FORMAT: " << last_word;
479 case EDGE_DATA_SECTION: {
481 edges_.resize(size_);
485 case EDGE_WEIGHT_TYPE: {
486 if (!
gtl::FindCopy(*kEdgeWeightTypes, last_word, &edge_weight_type_)) {
488 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_TYPE: " << last_word;
489 LOG(WARNING) <<
"Trying in EDGE_WEIGHT_FORMAT formats";
491 &edge_weight_format_)) {
492 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_FORMAT: " << last_word;
497 case EDGE_WEIGHT_FORMAT: {
499 &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;
509 case EDGE_WEIGHT_SECTION: {
510 SetUpEdgeWeightSection();
513 case FIXED_EDGES_SECTION: {
514 to_read_ = std::numeric_limits<int64_t>::max();
517 case NODE_COORD_TYPE: {
520 case DISPLAY_DATA_TYPE: {
523 case DISPLAY_DATA_SECTION: {
527 case NODE_COORD_SECTION: {
531 case DEPOT_SECTION: {
532 to_read_ = std::numeric_limits<int64_t>::max();
535 case DEMAND_SECTION: {
536 demands_.resize(size_, 0);
544 LOG(WARNING) <<
"Unknown section: " << words[0];
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()) {
555 if (kSections->contains(words[0])) {
560 case EDGE_DATA_SECTION: {
561 CHECK(!words.empty());
562 switch (edge_data_format_) {
564 if (words[0] ==
"-1") {
565 CHECK_EQ(words.size(), 1);
567 for (
auto&
edges : edges_) {
569 const auto it = std::unique(
edges.begin(),
edges.end());
570 edges.resize(std::distance(
edges.begin(), it));
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));
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;
586 edges_[std::min(from,
to)].push_back(std::max(from,
to));
588 CHECK_EQ(i, words.size() - 1);
591 if (
atoi64(words.back()) != -1) {
592 LOG(WARNING) <<
"Missing -1 at the end of ADJ_LIST";
597 LOG(WARNING) <<
"Unknown EDGE_DATA_FORMAT: " << edge_data_format_;
601 case EDGE_WEIGHT_SECTION: {
602 switch (edge_weight_format_) {
604 ParseExplicitFullMatrix(words);
608 ParseExplicitUpperRow(words);
612 ParseExplicitLowerRow(words);
616 ParseExplicitUpperDiagRow(words);
620 ParseExplicitLowerDiagRow(words);
623 LOG(WARNING) <<
"Unknown EDGE_WEIGHT_FORMAT: "
624 << edge_weight_format_;
628 case FIXED_EDGES_SECTION: {
629 if (words.size() == 1) {
630 CHECK_EQ(-1,
atoi64(words[0]));
633 CHECK_EQ(2, words.size());
635 std::make_pair(
atoi64(words[0]) - 1,
atoi64(words[1]) - 1));
639 case NODE_COORD_SECTION: {
640 ParseNodeCoord(words);
643 case DEPOT_SECTION: {
644 if (words.size() == 1) {
647 VLOG(2) <<
"Depot: " <<
depot;
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;
661 if (3 == words.size()) {
665 coords_[
depot].z = 0;
670 case DEMAND_SECTION: {
671 const int64_t node =
atoi64(words[0]) - 1;
672 demands_[node] =
atoi64(words[1]);
676 case DISPLAY_DATA_SECTION: {
677 ParseNodeCoord(words);
681 LOG(ERROR) <<
"Reading data outside section";
687 valid_section_found_ |= ParseSections(words);
693 absl::Span<
const std::vector<int>> routes)
const {
694 std::string tours = absl::StrCat(
695 "NAME : ", name_,
"\nCOMMENT :\nTYPE : TOUR\nDIMENSION : ",
size(),
697 for (
const auto& route : routes) {
698 for (
const int node : route) {
699 absl::StrAppend(&tours, node + 1,
"\n");
701 absl::StrAppend(&tours,
"-1\n");
703 return absl::StrCat(tours,
"EOF");
706const absl::flat_hash_map<std::string, TspLibParser::Sections>*
const
707 TspLibParser::kSections =
708 new absl::flat_hash_map<std::string, TspLibParser::Sections>(
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}});
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},
737 {
"CVRP", Types::CVRP},
738 {
"TOUR", Types::TOUR}});
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}});
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},
755 {
"CEIL_2D", CEIL_2D},
761 {
"SPECIAL", SPECIAL}});
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}});
780 section_ = UNDEFINED_SECTION;
783 std::shared_ptr<zipfile::ZipArchive> zip_archive(
784 OpenZipArchiveIfItExists(file_name));
786 for (
const std::string& line :
788 ProcessNewLine(line);
790 return absl::OkStatus();
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();
798 if (section_ == TOUR_SECTION) {
799 for (
const std::string& word : words) {
800 const int node =
atoi32(word);
802 tour_.push_back(
atoi32(word) - 1);
804 section_ = UNDEFINED_SECTION;
808 const std::string& last_word = words[word_size - 1];
810 LOG(WARNING) <<
"Unknown section: " << words[0];
817 CHECK_EQ(
"TOUR", last_word);
820 comments_ = absl::StrJoin(words.begin() + 1, words.end(),
" ");
824 size_ =
atoi32(last_word);
831 LOG(WARNING) <<
"Unknown key word: " << words[0];
837const absl::flat_hash_map<std::string, TspLibTourParser::Sections>*
const
838 TspLibTourParser::kSections =
839 new absl::flat_hash_map<std::string, TspLibTourParser::Sections>(
842 {
"COMMENT", COMMENT},
843 {
"DIMENSION", DIMENSION},
844 {
"TOUR_SECTION", TOUR_SECTION},
845 {
"EOF", END_OF_FILE}});
852 std::shared_ptr<zipfile::ZipArchive> zip_archive(
853 OpenZipArchiveIfItExists(file_name));
855 for (
const std::string& line :
857 ProcessNewLine(line);
859 return absl::OkStatus();
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();
867 if (absl::AsciiStrToUpper(words[0]) ==
"COST") {
868 CHECK_EQ(word_size, 2);
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]));
880 LOG(WARNING) <<
"Unknown key word: " << words[0];
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
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)
absl::string_view Extension(absl::string_view path)
absl::Status Open(absl::string_view file_name, absl::string_view mode, File **f, Options options)
absl::string_view Dirname(absl::string_view path)
bool FindCopy(const Collection &collection, const Key &key, Value *const value)
int32_t atoi32(absl::string_view word)
int64_t atoi64(absl::string_view word)
double ParseLeadingDoubleValue(const char *str, double deflt)
StatusBuilder InvalidArgumentErrorBuilder()
std::shared_ptr< ZipArchive > OpenZipArchive(absl::string_view path, const ZipFileOptions &options)
trees with all degrees equal to