Google OR-Tools v9.12
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
carp_parser.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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 <optional>
18#include <string>
19#include <string_view>
20#include <vector>
21
22#include "absl/strings/str_join.h"
23#include "absl/strings/str_split.h"
24#include "absl/strings/string_view.h"
25#include "absl/types/span.h"
28
30
31CarpParser::CarpParser() { Initialize(); }
32
33void CarpParser::Initialize() {
34 name_.clear();
35 comment_.clear();
36 number_of_nodes_ = 0;
37 number_of_edges_with_servicing_ = 0;
38 number_of_edges_without_servicing_ = 0;
39 total_servicing_cost_ = 0;
40 depot_ = 0;
41 traversing_costs_.clear();
42 servicing_demands_.clear();
43 n_vehicles_ = 0;
44 capacity_ = 0;
45 section_ = METADATA;
46}
47
48bool CarpParser::LoadFile(absl::string_view file_name) {
49 Initialize();
50 return ParseFile(file_name);
51}
52
53bool CarpParser::ParseFile(absl::string_view file_name) {
54 static auto section_headers = std::array<const char*, 12>({
55 "NOMBRE",
56 "COMENTARIO",
57 "VERTICES",
58 "ARISTAS_REQ",
59 "ARISTAS_NOREQ",
60 "VEHICULOS",
61 "CAPACIDAD",
62 "TIPO_COSTES_ARISTAS",
63 "COSTE_TOTAL_REQ",
64 "LISTA_ARISTAS_REQ",
65 "LISTA_ARISTAS_NOREQ",
66 "DEPOSITO",
67 });
68
69 for (const std::string& line :
71 const std::vector<std::string> words =
72 absl::StrSplit(line, absl::ByAnyChar(" :\t"), absl::SkipEmpty());
73
74 if (absl::c_linear_search(section_headers, words[0])) {
75 // First, check if a new section has been met.
76 if (words[0] == "LISTA_ARISTAS_REQ") {
77 traversing_costs_.reserve(NumberOfEdges());
78 servicing_demands_.reserve(NumberOfEdgesWithServicing());
79 section_ = ARCS_WITH_SERVICING;
80 } else if (words[0] == "LISTA_ARISTAS_NOREQ") {
81 traversing_costs_.reserve(NumberOfEdges());
82 section_ = ARCS_WITHOUT_SERVICING;
83 } else {
84 if (!ParseMetadataLine(words)) {
85 LOG(ERROR) << "Error when parsing the following metadata line: "
86 << line;
87 return false;
88 }
89 }
90 } else {
91 // If no new section is detected, process according to the current state.
92 switch (section_) {
93 case ARCS_WITH_SERVICING:
94 if (!ParseEdge(line, true)) {
95 LOG(ERROR) << "Could not parse line in LISTA_ARISTAS_REQ: " << line;
96 return false;
97 }
98 break;
99 case ARCS_WITHOUT_SERVICING:
100 if (!ParseEdge(line, false)) {
101 LOG(ERROR) << "Could not parse line in LISTA_ARISTAS_NOREQ: "
102 << line;
103 return false;
104 }
105 break;
106 default:
107 LOG(ERROR) << "Could not parse line outside edge lists: " << line;
108 return false;
109 }
110 }
111 }
112
113 return !servicing_demands_.empty();
114}
115
116namespace {
117std::optional<int64_t> ParseNodeIndex(std::string_view text);
118} // namespace
119
120bool CarpParser::ParseMetadataLine(absl::Span<const std::string> words) {
121 if (words[0] == "NOMBRE") {
122 name_ = absl::StrJoin(words.begin() + 1, words.end(), " ");
123 } else if (words[0] == "COMENTARIO") {
124 comment_ = absl::StrJoin(words.begin() + 1, words.end(), " ");
125 } else if (words[0] == "VERTICES") {
126 number_of_nodes_ = strings::ParseLeadingInt64Value(words[1], -1);
127 if (number_of_nodes_ <= 0) {
128 LOG(ERROR) << "Error when parsing the number of nodes: " << words[1];
129 return false;
130 }
131 } else if (words[0] == "ARISTAS_REQ") {
132 number_of_edges_with_servicing_ =
134 if (number_of_edges_with_servicing_ <= 0) {
135 LOG(ERROR) << "Error when parsing the number of edges with servicing: "
136 << words[1];
137 return false;
138 }
139 } else if (words[0] == "ARISTAS_NOREQ") {
140 number_of_edges_without_servicing_ =
142 if (number_of_edges_without_servicing_ < 0) {
143 // It is possible to have a valid instance with zero edges that have no
144 // servicing (i.e. number_of_edges_without_servicing_ == 0).
145 LOG(ERROR) << "Error when parsing the number of edges without servicing: "
146 << words[1];
147 return false;
148 }
149 } else if (words[0] == "VEHICULOS") {
150 n_vehicles_ = strings::ParseLeadingInt64Value(words[1], -1);
151 if (n_vehicles_ <= 0) {
152 LOG(ERROR) << "Error when parsing the number of vehicles: " << words[1];
153 return false;
154 }
155 } else if (words[0] == "CAPACIDAD") {
156 capacity_ = strings::ParseLeadingInt64Value(words[1], -1);
157 if (capacity_ <= 0) {
158 LOG(ERROR) << "Error when parsing the capacity: " << words[1];
159 return false;
160 }
161 } else if (words[0] == "TIPO_COSTES_ARISTAS") {
162 if (words[1] != "EXPLICITOS") {
163 // Actually, this is the only defined value for this file format.
164 LOG(ERROR) << "Value of TIPO_COSTES_ARISTAS is unexpected, only "
165 "EXPLICITOS is supported, but "
166 << words[1] << " was found";
167 return false;
168 }
169 } else if (words[0] == "COSTE_TOTAL_REQ") {
170 total_servicing_cost_ = strings::ParseLeadingInt64Value(words[1], -1);
171 if (total_servicing_cost_ == -1) {
172 LOG(ERROR) << "Error when parsing the total servicing cost: " << words[1];
173 return false;
174 }
175 } else if (words[0] == "DEPOSITO") {
176 // Supposed to be the last value of the file.
177 const std::optional<int64_t> depot = ParseNodeIndex(words[1]);
178 if (!depot.has_value()) {
179 LOG(ERROR) << "Error when parsing the depot: " << words[1];
180 return false;
181 }
182 depot_ = depot.value();
183 }
184 return true;
185}
186
187bool CarpParser::ParseEdge(std::string_view line, bool with_servicing) {
188 const std::vector<std::string> words =
189 absl::StrSplit(line, absl::ByAnyChar(" :\t(),"), absl::SkipEmpty());
190
191 // Parse the edge.
192 std::optional<int64_t> opt_head = ParseNodeIndex(words[0]);
193 if (!opt_head.has_value()) {
194 LOG(ERROR) << "Error when parsing the head node: " << words[0];
195 return false;
196 }
197 const int64_t head = opt_head.value();
198
199 std::optional<int64_t> opt_tail = ParseNodeIndex(words[1]);
200 if (!opt_tail.has_value()) {
201 LOG(ERROR) << "Error when parsing the tail node: " << words[1];
202 return false;
203 }
204 const int64_t tail = opt_tail.value();
205
206 if (head == tail) {
207 LOG(ERROR) << "The head and tail nodes are identical: " << line;
208 return false;
209 }
210
211 // Parse the cost.
212 if (words[2] != "coste") {
213 LOG(ERROR) << "Unexpected keyword: " << words[2];
214 return false;
215 }
216 const int64_t cost = strings::ParseLeadingInt64Value(words[3], -1);
217 traversing_costs_[{tail, head}] = cost;
218
219 // Parse the servicing if needed.
220 if (with_servicing) {
221 if (words[4] != "demanda") {
222 LOG(ERROR) << "Unexpected keyword: " << words[2];
223 return false;
224 }
225 const int64_t servicing = strings::ParseLeadingInt64Value(words[5], -1);
226 servicing_demands_[{tail, head}] = servicing;
227 }
228
229 // Ensure there are no extraneous elements.
230 const int64_t next_id = (with_servicing) ? 6 : 4;
231 if (words.size() > next_id) {
232 LOG(ERROR) << "Extraneous elements in line, starting with: "
233 << words[next_id];
234 return false;
235 }
236
237 return true;
238}
239
240namespace {
241std::optional<int64_t> ParseNodeIndex(std::string_view text) {
242 const int64_t node = strings::ParseLeadingInt64Value(text, -1);
243 if (node < 0) {
244 LOG(ERROR) << "Could not parse node index: " << text;
245 return std::nullopt;
246 }
247 return {node - 1};
248}
249} // namespace
250} // namespace operations_research::routing
int64_t depot() const
Returns the index of the depot.
Definition carp_parser.h:87
bool LoadFile(absl::string_view file_name)
Loads instance from a file into this parser object.
Common utilities for parsing routing instances.
int64_t ParseLeadingInt64Value(const char *str, int64_t deflt)
Definition numbers.cc:167