Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
tsptw_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 <cmath>
17#include <limits>
18#include <memory>
19#include <string>
20#include <vector>
21
22#include "absl/strings/match.h"
23#include "absl/strings/str_split.h"
24#include "absl/strings/string_view.h"
27#include "ortools/base/path.h"
31
32namespace operations_research {
33
34namespace {
35
36double DoubleEuc2DDistance(const Coordinates2<double>& from,
37 const Coordinates2<double>& to) {
38 const double xd = from.x - to.x;
39 const double yd = from.y - to.y;
40 return sqrt(xd * xd + yd * yd);
41}
42
43double Euc2DDistance(const Coordinates2<double>& from,
44 const Coordinates2<double>& to) {
45 return std::floor(DoubleEuc2DDistance(from, to));
46}
47
48constexpr double kInfinity = std::numeric_limits<double>::infinity();
49
50std::shared_ptr<zipfile::ZipArchive> OpenZipArchiveIfItExists(
51 absl::string_view file_name) {
52 const absl::string_view archive_name = file::Dirname(file_name);
53 if (file::Extension(archive_name) == "zip") {
54 return zipfile::OpenZipArchive(archive_name);
55 } else {
56 return nullptr;
57 }
58}
59
60} // namespace
61
63 : size_(0),
64 depot_(0),
65 total_service_time_(0),
66 distance_function_(nullptr),
67 time_function_(nullptr) {}
68
69bool TspTWParser::LoadFile(const std::string& file_name) {
70 std::shared_ptr<zipfile::ZipArchive> zip_archive(
71 OpenZipArchiveIfItExists(file_name));
72 coords_.clear();
73 time_windows_.clear();
74 service_times_.clear();
75 distance_matrix_.clear();
76 size_ = 0;
77 depot_ = 0;
78 total_service_time_ = 0;
79 distance_function_ = nullptr;
80 time_function_ = nullptr;
81 return ParseLopezIbanezBlum(file_name) || ParseDaSilvaUrrutia(file_name);
82}
83
84bool TspTWParser::ParseLopezIbanezBlum(const std::string& file_name) {
85 int section = 0;
86 int entry_count = 0;
87 for (const std::string& line :
89 const std::vector<std::string> words =
90 absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty());
91 if (words.empty()) continue;
92 // Parsing comments.
93 if (words[0] == "#") {
94 if (absl::StrContains(line, "service times")) {
95 const double total_service_time =
96 strings::ParseLeadingDoubleValue(words.back(), kInfinity);
97 if (total_service_time != kInfinity) {
98 total_service_time_ = MathUtil::FastInt64Round(total_service_time);
99 }
100 }
101 continue;
102 }
103 switch (section) {
104 case 0: { // Parsing size.
105 if (words.size() != 1) return false;
106 size_ = strings::ParseLeadingInt32Value(words[0], -1);
107 if (size_ < 0) return false;
108 distance_matrix_.reserve(size_ * size_);
109 ++section;
110 entry_count = 0;
111 break;
112 }
113 case 1: { // Parsing distances.
114 if (words.size() != size_) return false;
115 for (const std::string& word : words) {
116 const double distance =
117 strings::ParseLeadingDoubleValue(word, kInfinity);
118 if (distance == kInfinity) return false;
119 distance_matrix_.push_back(distance);
120 }
121 ++entry_count;
122 if (entry_count == size_) {
123 ++section;
124 entry_count = 0;
125 }
126 break;
127 }
128 case 2: { // Parsing time windows.
129 if (words.size() != 2) return false;
130 std::vector<double> values;
131 for (const std::string& word : words) {
132 const double value =
133 strings::ParseLeadingDoubleValue(word, kInfinity);
134 if (value == kInfinity) return false;
135 values.push_back(value);
136 }
137 time_windows_.push_back({values[0], values[1]});
138 service_times_.push_back(0);
139 ++entry_count;
140 if (entry_count == size_) {
141 ++section;
142 }
143 break;
144 }
145 default: {
146 return false;
147 }
148 }
149 }
150 distance_function_ = [this](int from, int to) {
151 return distance_matrix_[from * size_ + to];
152 };
153 time_function_ = distance_function_;
154 return entry_count == size_;
155}
156
157bool TspTWParser::ParseDaSilvaUrrutia(const std::string& file_name) {
158 for (const std::string& line :
160 // Skip header.
161 if (absl::StartsWith(line, "CUST NO.")) continue;
162 const std::vector<std::string> words =
163 absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty());
164 // Skip comments and empty lines.
165 if (words.empty() || words[0] == "!!" || words[0][0] == '#') continue;
166 if (words.size() != 7) return false;
167 // Check that all field values are doubles, except first which must be a
168 // positive integer.
169 const int value = strings::ParseLeadingInt32Value(words[0], -1);
170 if (value < 0) return false;
171 // 999 represents the eof.
172 if (value == 999) continue;
173 std::vector<double> values;
174 for (int i = 1; i < words.size(); ++i) {
176 words[i], std::numeric_limits<double>::infinity());
177 if (value == std::numeric_limits<double>::infinity()) return false;
178 values.push_back(value);
179 }
180 coords_.push_back({values[0], values[1]});
181 time_windows_.push_back({values[3], values[4]});
182 service_times_.push_back(values[5]);
183 }
184 size_ = coords_.size();
185
186 // Enforce the triangular inequality (needed due to rounding).
187 distance_matrix_.reserve(size_ * size_);
188 for (int i = 0; i < size_; ++i) {
189 for (int j = 0; j < size_; ++j) {
190 distance_matrix_.push_back(Euc2DDistance(coords_[i], coords_[j]));
191 }
192 }
193 for (int i = 0; i < size_; i++) {
194 for (int j = 0; j < size_; j++) {
195 for (int k = 0; k < size_; k++) {
196 if (distance_matrix_[i * size_ + j] >
197 distance_matrix_[i * size_ + k] + distance_matrix_[k * size_ + j]) {
198 distance_matrix_[i * size_ + j] =
199 distance_matrix_[i * size_ + k] + distance_matrix_[k * size_ + j];
200 }
201 }
202 }
203 }
204
205 distance_function_ = [this](int from, int to) {
206 return distance_matrix_[from * size_ + to];
207 };
208 time_function_ = [this](int from, int to) {
209 return distance_matrix_[from * size_ + to] + service_times_[from];
210 };
211 return true;
212}
213
214} // namespace operations_research
static int64_t FastInt64Round(double x)
Definition mathutil.h:272
bool LoadFile(const std::string &file_name)
Loads and parses a routing problem from a given file.
double total_service_time() const
Returns the total service time already included in distance_function.
int64_t value
absl::string_view Extension(absl::string_view path)
Definition path.cc:133
absl::string_view Dirname(absl::string_view path)
Definition path.cc:105
In SWIG mode, we don't want anything besides these top-level includes.
double ParseLeadingDoubleValue(const char *str, double deflt)
Definition numbers.cc:210
int32_t ParseLeadingInt32Value(const char *str, int32_t deflt)
Definition numbers.cc:60
std::shared_ptr< ZipArchive > OpenZipArchive(absl::string_view path, const ZipFileOptions &options)
Definition zipfile.cc:32
trees with all degrees equal to
int line
double distance