OR-Tools  9.6
nearp_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 <array>
17 #include <iterator>
18 #include <optional>
19 #include <string>
20 #include <string_view>
21 #include <vector>
22 
23 #include "absl/strings/str_join.h"
24 #include "absl/strings/str_split.h"
25 #include "ortools/base/numbers.h"
27 
28 namespace operations_research {
29 
30 NearpParser::NearpParser() { Initialize(); }
31 
32 void NearpParser::Initialize() {
33  name_.clear();
34  comment_.clear();
35  num_arcs_ = 0;
36  num_edges_ = 0;
37  num_nodes_ = 0;
38  num_arcs_with_servicing_ = 0;
39  num_edges_with_servicing_ = 0;
40  num_nodes_with_servicing_ = 0;
41  depot_ = 0;
42  arc_traversing_costs_.clear();
43  edge_traversing_costs_.clear();
44  arc_servicing_demands_.clear();
45  edge_servicing_demands_.clear();
46  node_servicing_demands_.clear();
47  num_vehicles_ = 0;
48  capacity_ = 0;
49  section_ = METADATA;
50 }
51 
52 bool NearpParser::LoadFile(const std::string& file_name) {
53  Initialize();
54  return ParseFile(file_name);
55 }
56 
57 bool NearpParser::ParseFile(const std::string& file_name) {
58  // Only put the first word as header, as the main check is just done on this
59  // first word (no ambiguity is possible for well-formed files; a more precise
60  // check is done for metadata).
61  static auto section_headers = std::array<const char*, 14>({
62  "Name",
63  "Optimal", // "value"
64  "#Vehicles",
65  "Capacity",
66  "Depot", // "Node"
67  "#Nodes",
68  "#Edges",
69  "#Arcs",
70  "#Required", // "N", "E", or "A"
71  "ReN.",
72  "ReE.",
73  "EDGE",
74  "ReA.",
75  "ARC",
76  });
77 
78  for (const std::string& line :
80  const std::vector<std::string> words =
81  absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty());
82 
83  if (words.empty()) continue;
84 
85  if (absl::c_linear_search(section_headers, words[0])) {
86  // First, check if a new section has been met.
87  if (words[0] == "ReN.") {
88  node_servicing_demands_.reserve(NumberOfNodesWithServicing());
89  section_ = NODES_WITH_SERVICING;
90  } else if (words[0] == "ReE.") {
91  edge_traversing_costs_.reserve(NumberOfEdges());
92  edge_servicing_demands_.reserve(NumberOfEdgesWithServicing());
93  section_ = EDGES_WITH_SERVICING;
94  } else if (words[0] == "EDGE") {
95  edge_traversing_costs_.reserve(NumberOfEdges());
96  section_ = EDGES_WITHOUT_SERVICING;
97  } else if (words[0] == "ReA.") {
98  arc_traversing_costs_.reserve(NumberOfArcs());
99  arc_servicing_demands_.reserve(NumberOfArcsWithServicing());
100  section_ = ARCS_WITH_SERVICING;
101  } else if (words[0] == "ARC") {
102  arc_traversing_costs_.reserve(NumberOfArcs());
103  section_ = ARCS_WITHOUT_SERVICING;
104  } else {
105  if (!ParseMetadataLine(words)) {
106  LOG(ERROR) << "Error when parsing a metadata line: " << line;
107  return false;
108  }
109  }
110  } else {
111  // If no new section is detected, process according to the current state.
112 
113  // Is there still data expected? Don't process the line if every element
114  // it should contain have been read: there might be some garbage at the
115  // end (like comments without delimiter).
116  switch (section_) {
117  case NODES_WITH_SERVICING:
118  if (node_servicing_demands_.size() == NumberOfNodesWithServicing())
119  continue;
120  break;
121  case EDGES_WITH_SERVICING:
122  if (edge_servicing_demands_.size() == NumberOfEdgesWithServicing())
123  continue;
124  break;
125  case EDGES_WITHOUT_SERVICING:
126  // edge_traversing_costs_ is filled with all edges, whether they need
127  // servicing or not. The section EDGES_WITHOUT_SERVICING must be after
128  // EDGES_WITH_SERVICING: once edge_traversing_costs_ has one value
129  // per edge (coming first from EDGES_WITH_SERVICING, then from
130  // EDGES_WITHOUT_SERVICING), no more value should come.
131  if (edge_traversing_costs_.size() == NumberOfEdges()) continue;
132  break;
133  case ARCS_WITH_SERVICING:
134  if (arc_servicing_demands_.size() == NumberOfArcsWithServicing())
135  continue;
136  break;
137  case ARCS_WITHOUT_SERVICING:
138  // arc_traversing_costs_ is filled with all arcs, do they need
139  // servicing or not. The section ARCS_WITHOUT_SERVICING must be after
140  // ARCS_WITH_SERVICING: once edge_traversing_costs_ has one value
141  // per arc (coming first from ARCS_WITH_SERVICING, then from
142  // ARCS_WITHOUT_SERVICING), no more value should come.
143  if (arc_traversing_costs_.size() == NumberOfArcs()) continue;
144  break;
145  default:
146  break;
147  }
148 
149  // Data is still expected: parse the current line according to the state.
150  switch (section_) {
151  case NODES_WITH_SERVICING:
152  if (!ParseNode(line)) {
153  LOG(ERROR) << "Could not parse line in required nodes: " << line;
154  return false;
155  }
156  break;
157  case EDGES_WITH_SERVICING:
158  if (!ParseEdge(line, true)) {
159  LOG(ERROR) << "Could not parse line in required edges: " << line;
160  return false;
161  }
162  break;
163  case EDGES_WITHOUT_SERVICING:
164  if (!ParseEdge(line, false)) {
165  LOG(ERROR) << "Could not parse line in edges: " << line;
166  return false;
167  }
168  break;
169  case ARCS_WITH_SERVICING:
170  if (!ParseArc(line, true)) {
171  LOG(ERROR) << "Could not parse line in required arcs: " << line;
172  return false;
173  }
174  break;
175  case ARCS_WITHOUT_SERVICING:
176  if (!ParseArc(line, false)) {
177  LOG(ERROR) << "Could not parse line in arcs: " << line;
178  return false;
179  }
180  break;
181  default:
182  LOG(ERROR) << "Could not parse line outside node-edge-arc lists: "
183  << line;
184  return false;
185  }
186  }
187  }
188 
189  return section_ != METADATA;
190 }
191 
192 namespace {
193 std::optional<int64_t> ParseNodeIndex(std::string_view text);
194 
195 struct ArcOrEdge {
196  const int64_t tail;
197  const int64_t head;
198  const int64_t traversing_cost;
199  const int64_t servicing_demand;
200  const int64_t servicing_cost;
201 };
202 
203 std::optional<ArcOrEdge> ParseArcOrEdge(std::string_view line,
204  bool with_servicing);
205 } // namespace
206 
207 bool NearpParser::ParseMetadataLine(const std::vector<std::string>& words) {
208  if (words[0] == "Name") {
209  name_ = absl::StrJoin(words.begin() + 1, words.end(), " ");
210  } else if (words[0] == "Optimal" && words[1] == "value") {
211  comment_ = absl::StrJoin(words.begin() + 2, words.end(), " ");
212  } else if (words[0] == "#Vehicles") {
213  num_vehicles_ = strings::ParseLeadingInt32Value(words[1], -1);
214  // -1 indicates that the number of vehicles is unknown.
215  if (num_vehicles_ < -1) {
216  LOG(ERROR) << "Error when parsing the number of vehicles: " << words[1];
217  return false;
218  }
219  } else if (words[0] == "Capacity") {
220  capacity_ = strings::ParseLeadingInt64Value(words[1], -1);
221  if (capacity_ <= 0) {
222  LOG(ERROR) << "Error when parsing the capacity: " << words[1];
223  return false;
224  }
225  } else if (words[0] == "Depot" && words[1] == "Node") {
226  const std::optional<int64_t> depot = ParseNodeIndex(words[2]);
227  if (!depot.has_value()) {
228  LOG(ERROR) << "Error when parsing the depot: " << words[1];
229  return false;
230  }
231  depot_ = depot.value();
232  } else if (words[0] == "#Nodes") {
233  num_nodes_ = strings::ParseLeadingInt32Value(words[1], -1);
234  if (num_nodes_ <= 0) {
235  LOG(ERROR) << "Error when parsing the number of nodes: " << words[1];
236  return false;
237  }
238  } else if (words[0] == "#Edges") {
239  num_edges_ = strings::ParseLeadingInt32Value(words[1], -1);
240  if (num_edges_ <= 0) {
241  LOG(ERROR) << "Error when parsing the number of edges: " << words[1];
242  return false;
243  }
244  } else if (words[0] == "#Arcs") {
245  num_arcs_ = strings::ParseLeadingInt32Value(words[1], -1);
246  if (num_arcs_ <= 0) {
247  LOG(ERROR) << "Error when parsing the number of arcs: " << words[1];
248  return false;
249  }
250  } else if (words[0] == "#Required" && words[1] == "N") {
251  num_nodes_with_servicing_ = strings::ParseLeadingInt32Value(words[2], -1);
252  if (num_nodes_with_servicing_ <= 0) {
253  LOG(ERROR) << "Error when parsing the number of nodes with servicing: "
254  << words[1];
255  return false;
256  }
257  } else if (words[0] == "#Required" && words[1] == "E") {
258  num_edges_with_servicing_ = strings::ParseLeadingInt32Value(words[2], -1);
259  if (num_edges_with_servicing_ <= 0) {
260  LOG(ERROR) << "Error when parsing the number of edges with servicing: "
261  << words[1];
262  return false;
263  }
264  } else if (words[0] == "#Required" && words[1] == "A") {
265  num_arcs_with_servicing_ = strings::ParseLeadingInt32Value(words[2], -1);
266  if (num_arcs_with_servicing_ <= 0) {
267  LOG(ERROR) << "Error when parsing the number of arcs with servicing: "
268  << words[1];
269  return false;
270  }
271  } else {
272  LOG(ERROR) << "Unrecognized metadata line: " << absl::StrJoin(words, " ");
273  return false;
274  }
275  return true;
276 }
277 
278 bool NearpParser::ParseArc(std::string_view line, bool with_servicing) {
279  std::optional<ArcOrEdge> parsed_arc = ParseArcOrEdge(line, with_servicing);
280  if (!parsed_arc.has_value()) {
281  return false;
282  }
283 
284  Arc arc{parsed_arc->tail, parsed_arc->head};
285  arc_traversing_costs_[arc] = parsed_arc->traversing_cost;
286 
287  if (with_servicing) {
288  arc_servicing_demands_[arc] = parsed_arc->servicing_demand;
289  arc_servicing_costs_[arc] = parsed_arc->servicing_cost;
290  }
291 
292  return true;
293 }
294 
295 bool NearpParser::ParseEdge(std::string_view line, bool with_servicing) {
296  std::optional<ArcOrEdge> parsed_edge = ParseArcOrEdge(line, with_servicing);
297  if (!parsed_edge.has_value()) {
298  return false;
299  }
300 
301  Edge edge{parsed_edge->tail, parsed_edge->head};
302  edge_traversing_costs_[edge] = parsed_edge->traversing_cost;
303 
304  if (with_servicing) {
305  edge_servicing_demands_[edge] = parsed_edge->servicing_demand;
306  edge_servicing_costs_[edge] = parsed_edge->servicing_cost;
307  }
308 
309  return true;
310 }
311 
312 bool NearpParser::ParseNode(std::string_view line) {
313  const std::vector<std::string> words =
314  absl::StrSplit(line, absl::ByAnyChar(" :\t(),"), absl::SkipEmpty());
315 
316  // Parse the name and ID.
317  const int64_t id = strings::ParseLeadingInt64Value(words[0].substr(1), 0) - 1;
318  if (id < 0) {
319  LOG(ERROR) << "Error when parsing the node name: " << words[0];
320  return false;
321  }
322 
323  // Parse the servicing details if needed.
324  const int64_t servicing_demand =
325  strings::ParseLeadingInt64Value(words[1], -1);
326  if (servicing_demand < 0) {
327  LOG(ERROR) << "Error when parsing the node servicing demand: " << words[1];
328  return false;
329  }
330 
331  const int64_t servicing_cost = strings::ParseLeadingInt64Value(words[2], -1);
332  if (servicing_cost < 0) {
333  LOG(ERROR) << "Error when parsing the node servicing cost: " << words[1];
334  return false;
335  }
336 
337  // Once the values have been parsed successfully, save them.
338  node_servicing_demands_.insert({id, servicing_demand});
339  node_servicing_costs_.insert({id, servicing_cost});
340 
341  return true;
342 }
343 
344 namespace {
345 std::optional<int64_t> ParseNodeIndex(std::string_view text) {
346  const int64_t node = strings::ParseLeadingInt64Value(text, -1);
347  if (node < 0) {
348  LOG(ERROR) << "Could not parse node index: " << text;
349  return std::nullopt;
350  }
351  return {node - 1};
352 }
353 
354 std::optional<ArcOrEdge> ParseArcOrEdge(std::string_view line,
355  bool with_servicing) {
356  const std::vector<std::string> words =
357  absl::StrSplit(line, absl::ByAnyChar(" :\t(),"), absl::SkipEmpty());
358 
359  // Parse the name.
360  const std::string name = words[0];
361 
362  // Parse the tail and the head of the arc/edge.
363  std::optional<int64_t> opt_tail = ParseNodeIndex(words[1]);
364  if (!opt_tail.has_value()) {
365  LOG(ERROR) << "Error when parsing the tail node: " << words[1];
366  return {};
367  }
368  const int64_t tail = opt_tail.value();
369 
370  std::optional<int64_t> opt_head = ParseNodeIndex(words[2]);
371  if (!opt_head.has_value()) {
372  LOG(ERROR) << "Error when parsing the head node: " << words[2];
373  return {};
374  }
375  const int64_t head = opt_head.value();
376 
377  if (tail == head) {
378  LOG(ERROR) << "The head and tail nodes are identical: " << line;
379  return {};
380  }
381 
382  // Parse the traversing cost.
383  const int64_t traversing_cost = strings::ParseLeadingInt64Value(words[3], -1);
384  if (traversing_cost < 0) {
385  LOG(ERROR) << "Error when parsing the traversing cost: " << words[3];
386  return {};
387  }
388 
389  // Ensure there are no extraneous elements.
390  const int64_t next_id = (with_servicing) ? 6 : 4;
391  if (words.size() > next_id) {
392  LOG(ERROR) << "Extraneous elements in line, starting with: "
393  << words[next_id];
394  return {};
395  }
396 
397  // Parse the servicing details if needed and return the elements.
398  if (!with_servicing) {
399  return ArcOrEdge{tail, head, traversing_cost,
400  /*servicing_demand=*/-1,
401  /*servicing_cost=*/-1};
402  } else {
403  const int64_t servicing_demand =
404  strings::ParseLeadingInt64Value(words[4], -1);
405  if (servicing_demand < 0) {
406  LOG(ERROR) << "Error when parsing the servicing demand: " << words[4];
407  return {};
408  }
409 
410  const int64_t servicing_cost =
411  strings::ParseLeadingInt64Value(words[5], -1);
412  if (servicing_cost < 0) {
413  LOG(ERROR) << "Error when parsing the servicing cost: " << words[5];
414  return {};
415  }
416 
417  return ArcOrEdge{tail, head, traversing_cost, servicing_demand,
419  }
420 }
421 } // namespace
422 
423 std::string NearpParser::GetArcName(Arc arc) const {
424  if (arc_servicing_costs_.contains(arc)) {
425  int64_t arc_position = std::distance(arc_servicing_demands_.begin(),
426  arc_servicing_demands_.find(arc));
427  CHECK_LT(arc_position, arc_servicing_demands_.size());
428  return absl::StrCat("A", arc_position + 1);
429  } else {
430  int64_t arc_position = std::distance(arc_traversing_costs_.begin(),
431  arc_traversing_costs_.find(arc));
432  CHECK_LT(arc_position, arc_traversing_costs_.size());
433  return absl::StrCat("NrA", arc_position - num_arcs_with_servicing_ + 1);
434  }
435 }
436 
437 std::string NearpParser::GetEdgeName(Edge edge) const {
438  if (edge_servicing_costs_.contains(edge)) {
439  int64_t edge_position = std::distance(edge_servicing_demands_.begin(),
440  edge_servicing_demands_.find(edge));
441  CHECK_LT(edge_position, edge_servicing_demands_.size());
442  return absl::StrCat("E", edge_position + 1);
443  } else {
444  int64_t edge_position = std::distance(edge_traversing_costs_.begin(),
445  edge_traversing_costs_.find(edge));
446  CHECK_LT(edge_position, edge_traversing_costs_.size());
447  return absl::StrCat("NrE", edge_position - num_edges_with_servicing_ + 1);
448  }
449 }
450 } // namespace operations_research
size_type size() const
ABSL_ATTRIBUTE_REINITIALIZES void clear()
std::pair< iterator, bool > insert(const value_type &v)
void reserve(size_t n)
std::string GetArcName(Arc arc) const
std::string GetEdgeName(Edge edge) const
bool LoadFile(const std::string &file_name)
Definition: nearp_parser.cc:52
const std::string name
int arc
Collection of objects used to extend the Constraint Solver library.
int64_t ParseLeadingInt64Value(const char *str, int64_t deflt)
Definition: numbers.cc:161
int32_t ParseLeadingInt32Value(const char *str, int32_t deflt)
Definition: numbers.cc:58
const int64_t traversing_cost
const int64_t head
const int64_t tail
const int64_t servicing_demand
const int64_t servicing_cost
int line
Definition: parse_proto.cc:31
double distance