OR-Tools  9.6
tsplib_parser_test.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 <cstdio>
17 #include <memory>
18 #include <string>
19 #include <vector>
20 
21 #include "absl/container/btree_set.h"
22 #include "absl/flags/flag.h"
23 #include "absl/strings/str_format.h"
24 #include "absl/strings/string_view.h"
25 #include "gtest/gtest.h"
27 #include "ortools/base/helpers.h"
29 #include "ortools/base/map_util.h"
30 #include "ortools/base/memfile.h"
31 #include "ortools/base/path.h"
32 #include "ortools/base/zipfile.h"
33 
34 #if defined(_MSC_VER)
35 #define ROOT_DIR "../../../../../../../"
36 #else
37 #define ROOT_DIR
38 #endif // _MSC_VER
39 
40 ABSL_FLAG(std::string, test_srcdir, "", "REQUIRED: src dir");
41 
42 namespace operations_research {
43 namespace {
44 
45 TEST(TspLibParserTest, GeneratedDataSets) {
46  static const char kName[] = "GoogleTest";
47  static const char* const kTypes[] = {"TSP", "CVRP"};
48  static const char kComment[] = "This is a test";
49  static const int kDimension = 4;
50  static const int kCoordSize = 2;
51  static const int kCapacity = 2;
52  static const char* const kEdgeWeightTypes[] = {
53  "EXPLICIT", "EUC_2D", "EUC_3D", "MAX_2D", "MAX_3D",
54  "MAN_2D", "MAN_3D", "CEIL_2D", "GEO", "ATT"};
55  static const char* const kEdgeWeightFormats[] = {
56  "FULL_MATRIX", "UPPER_ROW", "LOWER_ROW",
57  "UPPER_DIAG_ROW", "LOWER_DIAG_ROW", "UPPER_COL",
58  "LOWER_COL", "UPPER_DIAG_COL", "LOWER_DIAG_COL"};
59  static const char* const kNodeCoordTypes[] = {"TWOD_COORDS", "THREED_COORDS",
60  "NO_COORDS"};
61  static const char* const kDisplayDataTypes[] = {"COORD_DISPLAY",
62  "TWOD_DISPLAY", "NO_DISPLAY"};
63  for (int type = 0; type < ABSL_ARRAYSIZE(kTypes); ++type) {
64  for (int edge_type = 0; edge_type < ABSL_ARRAYSIZE(kEdgeWeightTypes);
65  ++edge_type) {
66  for (int edge_format = 0;
67  edge_format < ABSL_ARRAYSIZE(kEdgeWeightFormats); ++edge_format) {
68  for (int node_type = 0; node_type < ABSL_ARRAYSIZE(kNodeCoordTypes);
69  ++node_type) {
70  if (node_type == 2 && edge_type != 0) break;
71  if (node_type == 1 && edge_type != 2 && edge_type != 4 &&
72  edge_type != 6)
73  break;
74  if (node_type == 0 && edge_type != 1 && edge_type != 3 &&
75  edge_type != 5 && edge_type < 7)
76  break;
77  for (int display_type = 0;
78  display_type < ABSL_ARRAYSIZE(kDisplayDataTypes);
79  ++display_type) {
80  if (display_type == 0 && node_type == 2) break;
81  std::string data = absl::StrFormat("NAME: %s\n", kName);
82  absl::StrAppendFormat(&data, "TYPE: %s\n", kTypes[type]);
83  absl::StrAppendFormat(&data, "COMMENT: %s\n", kComment);
84  absl::StrAppendFormat(&data, "DIMENSION: %d\n", kDimension);
85  if (type == 1) {
86  absl::StrAppendFormat(&data, "CAPACITY: %d\n", kCapacity);
87  }
88  absl::StrAppendFormat(&data, "EDGE_WEIGHT_TYPE: %s\n",
89  kEdgeWeightTypes[edge_type]);
90  if (edge_type == 0) {
91  absl::StrAppendFormat(&data, "EDGE_WEIGHT_FORMAT: %s\n",
92  kEdgeWeightFormats[edge_format]);
93  }
94  absl::StrAppendFormat(&data, "NODE_COORD_TYPE: %s\n",
95  kNodeCoordTypes[node_type]);
96  absl::StrAppendFormat(&data, "DISPLAY_DATA_TYPE: %s\n",
97  kDisplayDataTypes[display_type]);
98  if (node_type != 2) {
99  data += "NODE_COORD_SECTION\n";
100  for (int i = 0; i < kDimension; ++i) {
101  absl::StrAppendFormat(&data, "%d %d %d", i + 1, i % kCoordSize,
102  i / kCoordSize);
103  if (node_type == 1) {
104  data += " 0";
105  }
106  data += "\n";
107  }
108  }
109  if (type == 1) {
110  data += "DEPOT_SECTION\n1\n-1\n";
111  data += "DEMAND_SECTION\n";
112  for (int i = 0; i < kDimension; ++i) {
113  absl::StrAppendFormat(&data, "%d %d\n", i + 1, 1);
114  }
115  }
116  if (display_type == 1) {
117  data += "DISPLAY_DATA_SECTION\n";
118  for (int i = 0; i < kDimension; ++i) {
119  absl::StrAppendFormat(&data, "%d %d %d\n", i + 1,
120  i % kCoordSize, i / kCoordSize);
121  }
122  }
123  if (edge_type == 0) {
124  data += "EDGE_WEIGHT_SECTION\n";
125  // Manhattan distances
126  switch (edge_format) {
127  case 0:
128  for (int i = 0; i < kDimension; ++i) {
129  const int x = i % kCoordSize;
130  const int y = i / kCoordSize;
131  for (int j = 0; j < kDimension; ++j) {
132  const int distance =
133  abs(x - (j % kCoordSize)) + abs(y - (j / kCoordSize));
134  absl::StrAppendFormat(&data, "%d ", distance);
135  }
136  data += "\n";
137  }
138  break;
139  case 1:
140  case 6:
141  for (int i = 0; i < kDimension; ++i) {
142  const int x = i % kCoordSize;
143  const int y = i / kCoordSize;
144  for (int j = i + 1; j < kDimension; ++j) {
145  const int distance =
146  abs(x - (j % kCoordSize)) + abs(y - (j / kCoordSize));
147  absl::StrAppendFormat(&data, "%d ", distance);
148  }
149  data += "\n";
150  }
151  break;
152  case 2:
153  case 5:
154  for (int i = 0; i < kDimension; ++i) {
155  const int x = i % kCoordSize;
156  const int y = i / kCoordSize;
157  for (int j = 0; j < i; ++j) {
158  const int distance =
159  abs(x - (j % kCoordSize)) + abs(y - (j / kCoordSize));
160  absl::StrAppendFormat(&data, "%d ", distance);
161  }
162  data += "\n";
163  }
164  break;
165  case 3:
166  case 8:
167  for (int i = 0; i < kDimension; ++i) {
168  const int x = i % kCoordSize;
169  const int y = i / kCoordSize;
170  for (int j = i; j < kDimension; ++j) {
171  const int distance =
172  abs(x - (j % kCoordSize)) + abs(y - (j / kCoordSize));
173  absl::StrAppendFormat(&data, "%d ", distance);
174  }
175  data += "\n";
176  }
177  break;
178  case 4:
179  case 7:
180  for (int i = 0; i < kDimension; ++i) {
181  const int x = i % kCoordSize;
182  const int y = i / kCoordSize;
183  for (int j = 0; j <= i; ++j) {
184  const int distance =
185  abs(x - (j % kCoordSize)) + abs(y - (j / kCoordSize));
186  absl::StrAppendFormat(&data, "%d ", distance);
187  }
188  data += "\n";
189  }
190  break;
191  }
192  }
193  data += "EOF";
194  const std::string kMMFileName{std::tmpnam(nullptr)};
195  RegisteredMemFile registered(kMMFileName, data);
196  TspLibParser parser;
197  EXPECT_TRUE(parser.LoadFile(kMMFileName));
198  EXPECT_EQ(kDimension, parser.SizeFromFile(kMMFileName));
199  }
200  }
201  }
202  }
203  }
204 }
205 
206 TEST(TspLibParserTest, ParseHCPEdgeList) {
207  static const char* kData =
208  "NAME : test\n"
209  "COMMENT : Test\n"
210  "TYPE : HCP\n"
211  "DIMENSION : 3\n"
212  "EDGE_DATA_FORMAT : EDGE_LIST\n"
213  "EDGE_DATA_SECTION\n"
214  " 3 1\n"
215  " 2 1\n"
216  "-1\nEOF";
217  const std::string kMMFileName{std::tmpnam(nullptr)};
218  RegisteredMemFile registered(kMMFileName, kData);
219  TspLibParser parser;
220  EXPECT_TRUE(parser.LoadFile(kMMFileName));
221  EXPECT_EQ(3, parser.SizeFromFile(kMMFileName));
222  EXPECT_EQ(2, parser.edges()[0].size());
223  EXPECT_EQ(1, parser.edges()[0][0]);
224  EXPECT_EQ(2, parser.edges()[0][1]);
225  EXPECT_EQ(0, parser.edges()[1].size());
226  EXPECT_EQ(0, parser.edges()[2].size());
227 }
228 
229 TEST(TspLibParserTest, ParseHCPAdjList) {
230  static const char* kData =
231  "NAME : test\n"
232  "COMMENT : Test\n"
233  "TYPE : HCP\n"
234  "DIMENSION : 3\n"
235  "EDGE_DATA_FORMAT : ADJ_LIST\n"
236  "EDGE_DATA_SECTION\n"
237  " 3 1 2 -1\n"
238  "-1\nEOF";
239  const std::string kMMFileName{std::tmpnam(nullptr)};
240  RegisteredMemFile registered(kMMFileName, kData);
241  TspLibParser parser;
242  EXPECT_TRUE(parser.LoadFile(kMMFileName));
243  EXPECT_EQ(3, parser.SizeFromFile(kMMFileName));
244  EXPECT_EQ(1, parser.edges()[0].size());
245  EXPECT_EQ(2, parser.edges()[0][0]);
246  EXPECT_EQ(1, parser.edges()[1].size());
247  EXPECT_EQ(2, parser.edges()[1][0]);
248  EXPECT_EQ(0, parser.edges()[2].size());
249 }
250 
251 TEST(TspLibParserTest, ParseKytojoki33Depot) {
252  // This file inverts EDGE_WEIGHT_TYPE and EDGE_WEIGHT_FORMAT.
253  std::string file_name = file::JoinPath(absl::GetFlag(FLAGS_test_srcdir),
254  ROOT_DIR "ortools/routing/testdata/",
255  "tsplib_Kytojoki_33.vrp");
256  TspLibParser parser;
257  EXPECT_TRUE(parser.LoadFile(file_name));
258  // The depot is a new node, given by its coordinates, instead of an existing
259  // node in the graph.
260  EXPECT_EQ(2400, parser.depot());
261  EXPECT_EQ(0, parser.edges().size());
262  EXPECT_EQ(0.0, parser.coordinates()[parser.depot()].x);
263  EXPECT_EQ(0.0, parser.coordinates()[parser.depot()].y);
264 }
265 
266 TEST(TspLibTourParserTest, LoadAllDataSets) {
267  static const char kArchive[] =
268  ROOT_DIR "operations_research_data/TSPLIB95/ALL_tsp.tar.gz";
269  static const char* kExpectedComments[] = {
270  "",
271  ": Optimum solution for att48",
272  ": Optimum solution of bayg29",
273  ": Optimum solution of bays29",
274  "",
275  "",
276  ": Length 6110",
277  ": Length 6528",
278  ": Optimum tour for eil101.tsp (Length 629)",
279  ": Optimal tour for eil51.tsp (426)",
280  ": Optimum tour for eil76.tsp (538)",
281  ": optimal tour for fri26 (937)",
282  ": Optimal tour for gr120 (6942)",
283  ": Optimal solution for gr202 (40160)",
284  ": Optimal solution for gr24 (1272)",
285  ": Optimal solution for gr48 (5046)",
286  ": Optimal solution of gr666 (294358)",
287  ": Optimal tour for gr96 (55209)",
288  ": Optimum tour for kroA100 (21282)",
289  ": Optimal tour for kroC100 (20749)",
290  ": Optimal tour for kroD100 (21294)",
291  ": Optimal tour for lin105 (14379)",
292  ": Optimal tour for pa561 (2763)",
293  ": Optimal solution for pcb442 (50778)",
294  ": optimal tour for pr1002 (259045)",
295  ": Optimal solution for pr2392 (378032)",
296  ": Optimal tour for pr76 (108159)",
297  ": Optimal solution for rd100 (7910)",
298  ": Optimal tour for st70 (675)",
299  ": Optimal solution for tsp225 (3919)",
300  ": Optimal solution for ulysses16 (6859)",
301  ": Optimal solution of ulysses22 (7013)"};
302  int file_index = 0;
303  std::vector<std::string> matches;
304  if (file::Match(file::JoinPath("/tarfs", absl::GetFlag(FLAGS_test_srcdir),
305  kArchive, "*\\.opt\\.tour\\.gz"),
306  &matches, file::Defaults())
307  .ok()) {
308  for (const std::string& match : matches) {
309  TspLibTourParser parser;
310  EXPECT_TRUE(parser.LoadFile(match));
311  EXPECT_EQ(kExpectedComments[file_index], parser.comments());
312  file_index++;
313  }
314  }
315 }
316 
317 TEST(CVRPToursParserTest, LoadAllDataSets) {
318  static const char kArchive[] =
319  ROOT_DIR "operations_research_data/CVRP/Augerat/A-VRP-sol.zip";
320  static const int kExpectedCosts[] = {/*opt-A-n32-k5*/ 784,
321  /*opt-A-n33-k5*/ 661,
322  /*opt-A-n33-k6*/ 742,
323  /*opt-A-n34-k5*/ 778,
324  /*opt-A-n36-k5*/ 799,
325  /*opt-A-n37-k5*/ 669,
326  /*opt-A-n37-k6*/ 949,
327  /*opt-A-n38-k5*/ 730,
328  /*opt-A-n39-k5*/ 822,
329  /*opt-A-n39-k6*/ 831,
330  /*opt-A-n44-k6*/ 937,
331  /*opt-A-n45-k6*/ 944,
332  /*opt-A-n45-k7*/ 1146,
333  /*opt-A-n46-k7*/ 914,
334  /*opt-A-n48-k7*/ 1073,
335  /*opt-A-n53-k7*/ 1010,
336  /*opt-A-n55-k9*/ 1073};
337  int file_index = 0;
338  std::vector<std::string> matches;
339  if (file::Match(file::JoinPath("/zip", absl::GetFlag(FLAGS_test_srcdir),
340  kArchive, "opt-A-\\.*"),
341  &matches, file::Defaults())
342  .ok()) {
343  for (const std::string& match : matches) {
344  CVRPToursParser parser;
345  EXPECT_TRUE(parser.LoadFile(match));
346  EXPECT_EQ(kExpectedCosts[file_index], parser.cost());
347  file_index++;
348  }
349  }
350 }
351 } // namespace
352 } // namespace operations_research
const char * kCapacity
absl::Status Match(std::string_view pattern, std::vector< std::string > *result, const file::Options &options)
Definition: filesystem.cc:20
std::string JoinPath(absl::string_view path1, absl::string_view path2)
Definition: path.cc:25
Options Defaults()
Definition: base/file.h:123
Collection of objects used to extend the Constraint Solver library.
TEST(LinearAssignmentTest, NullMatrix)
double distance
#define ROOT_DIR
ABSL_FLAG(std::string, test_srcdir, "", "REQUIRED: src dir")