Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
file_format_flags.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 <optional>
17#include <ostream>
18#include <string>
19#include <utility>
20#include <vector>
21
22#include "absl/algorithm/container.h"
23#include "absl/base/optimization.h"
24#include "absl/container/flat_hash_map.h"
25#include "absl/log/log.h"
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/match.h"
29#include "absl/strings/str_cat.h"
30#include "absl/strings/str_join.h"
31#include "absl/strings/string_view.h"
32#include "absl/types/span.h"
43
45
46absl::Span<const FileFormat> AllFileFormats() {
47 static constexpr FileFormat kValues[] = {
48 FileFormat::kMathOptBinary,
49 FileFormat::kMathOptText,
50 FileFormat::kLinearSolverBinary,
51 FileFormat::kLinearSolverText,
52 FileFormat::kMPS,
53 FileFormat::kLP,
54 };
55 return absl::MakeConstSpan(kValues);
56}
57
58std::string AbslUnparseFlag(const FileFormat f) {
59 switch (f) {
60 case FileFormat::kMathOptBinary:
61 return "mathopt";
62 case FileFormat::kMathOptText:
63 return "mathopt_txt";
64 case FileFormat::kLinearSolverBinary:
65 return "linear_solver";
66 case FileFormat::kLinearSolverText:
67 return "linear_solver_txt";
68 case FileFormat::kMPS:
69 return "mps";
70 case FileFormat::kLP:
71 return "lp";
72 }
73 ABSL_UNREACHABLE();
74}
75
76std::ostream& operator<<(std::ostream& out, const FileFormat f) {
77 out << AbslUnparseFlag(f);
78 return out;
79}
80
81bool AbslParseFlag(const absl::string_view text, FileFormat* const f,
82 std::string* const error) {
83 for (const FileFormat candidate : AllFileFormats()) {
84 if (text == AbslUnparseFlag(candidate)) {
85 *f = candidate;
86 return true;
87 }
88 }
89
90 absl::StrAppend(error, "not a known value");
91 return false;
92}
93
94absl::flat_hash_map<absl::string_view, FileFormat> ExtensionToFileFormat() {
95 return {
96 {".pb", FileFormat::kMathOptBinary},
97 {".proto", FileFormat::kMathOptBinary},
98 {".pb.txt", FileFormat::kMathOptText},
99 {".pbtxt", FileFormat::kMathOptText},
100 {".textproto", FileFormat::kMathOptText},
101 {".mps", FileFormat::kMPS},
102 {".mps.gz", FileFormat::kMPS},
103 {".lp", FileFormat::kLP},
104 };
105}
106
107std::optional<FileFormat> FormatFromFilePath(absl::string_view file_path) {
108 const absl::flat_hash_map<absl::string_view, FileFormat> extensions =
110
111 // Sort extensions in reverse lexicographic order. The point here would be to
112 // consider ".pb.txt" before we text for ".txt".
113 using ExtensionPair = std::pair<absl::string_view, FileFormat>;
114 std::vector<ExtensionPair> sorted_extensions(extensions.begin(),
115 extensions.end());
116 absl::c_sort(sorted_extensions,
117 [](const ExtensionPair& lhs, const ExtensionPair& rhs) {
118 return lhs > rhs;
119 });
120
121 for (const auto& [extension, format] : sorted_extensions) {
122 if (absl::EndsWith(file_path, extension)) {
123 return format;
124 }
125 }
126 return std::nullopt;
127}
128
129std::optional<FileFormat> FormatFromFlagOrFilePath(
130 const std::optional<FileFormat> format_flag_value,
131 const absl::string_view file_path) {
132 if (format_flag_value.has_value()) {
133 return *format_flag_value;
134 }
135 return FormatFromFilePath(file_path);
136}
137
138namespace {
139
140constexpr absl::string_view kListLinePrefix = "* ";
141constexpr absl::string_view kSubListLinePrefix = " - ";
142
143} // namespace
144
146 // Get the lines for each format and the introduction doc.
147 std::string list = FormatFlagPossibleValuesList();
148
149 // Add the doc of what happen when format is not specified.
150 absl::StrAppend(&list, "\n", kListLinePrefix, "<unset>",
151 ": to guess the format from the file extension:");
152
153 // Builds a map from formats to their extensions.
154 absl::flat_hash_map<FileFormat, std::vector<absl::string_view>>
155 format_extensions;
156 for (const auto& [extension, format] : ExtensionToFileFormat()) {
157 format_extensions[format].push_back(extension);
158 }
159 for (auto& [format, extensions] : format_extensions) {
160 absl::c_sort(extensions);
161 }
162
163 // Here we iterate on all formats so that we list them in the same order as in
164 // the enum.
165 for (const FileFormat format : AllFileFormats()) {
166 // Here we use operator[] as it is not an issue to create new empty entries
167 // in the map.
168 const std::vector<absl::string_view>& extensions =
169 format_extensions[format];
170 if (extensions.empty()) {
171 continue;
172 }
173
174 absl::StrAppend(&list, "\n", kSubListLinePrefix,
175 absl::StrJoin(extensions, ", "), ": ",
176 AbslUnparseFlag(format));
177 }
178 return list;
179}
180
182 const absl::flat_hash_map<FileFormat, absl::string_view> format_help = {
183 {FileFormat::kMathOptBinary, "for a MathOpt ModelProto in binary"},
184 {FileFormat::kMathOptText, "when the proto is in text"},
185 {FileFormat::kLinearSolverBinary,
186 "for a LinearSolver MPModelProto in binary"},
187 {FileFormat::kLinearSolverText, "when the proto is in text"},
188 {FileFormat::kMPS, "for MPS file (which can be GZiped)"},
189 {FileFormat::kLP, " for LP file"},
190 };
191 std::string list;
192 for (const FileFormat format : AllFileFormats()) {
193 absl::StrAppend(&list, "\n", kListLinePrefix, AbslUnparseFlag(format), ": ",
194 format_help.at(format));
195 }
196 return list;
197}
198
199absl::StatusOr<std::pair<ModelProto, std::optional<SolutionHintProto>>>
200ReadModel(const absl::string_view file_path, const FileFormat format) {
201 switch (format) {
202 case FileFormat::kMathOptBinary: {
204 file_path, file::Defaults()));
205 return std::make_pair(std::move(model), std::nullopt);
206 }
207 case FileFormat::kMathOptText: {
209 file_path, file::Defaults()));
210 return std::make_pair(std::move(model), std::nullopt);
211 }
212 case FileFormat::kLinearSolverBinary:
213 case FileFormat::kLinearSolverText: {
215 const MPModelProto linear_solver_model,
216 format == FileFormat::kLinearSolverBinary
220 MPModelProtoToMathOptModel(linear_solver_model));
222 std::optional<SolutionHintProto> hint,
223 MPModelProtoSolutionHintToMathOptHint(linear_solver_model));
224 return std::make_pair(std::move(model), std::move(hint));
225 }
226 case FileFormat::kMPS: {
227 ASSIGN_OR_RETURN(ModelProto model, ReadMpsFile(file_path));
228 return std::make_pair(std::move(model), std::nullopt);
229 }
230 case FileFormat::kLP: {
231 ASSIGN_OR_RETURN(const std::string lp_data,
232 file::GetContents(file_path, file::Defaults()));
234 return std::make_pair(std::move(model), std::nullopt);
235 }
236 }
237 ABSL_UNREACHABLE();
238}
239
240absl::Status WriteModel(const absl::string_view file_path,
241 const ModelProto& model_proto,
242 const std::optional<SolutionHintProto>& hint_proto,
243 const FileFormat format) {
244 switch (format) {
245 case FileFormat::kMathOptBinary:
246 return file::SetBinaryProto(file_path, model_proto, file::Defaults());
247 case FileFormat::kMathOptText:
248 return file::SetTextProto(file_path, model_proto, file::Defaults());
249 case FileFormat::kLinearSolverBinary:
250 case FileFormat::kLinearSolverText: {
251 ASSIGN_OR_RETURN(const MPModelProto linear_solver_model,
252 MathOptModelToMPModelProto(model_proto));
253 if (hint_proto.has_value()) {
254 LOG(WARNING) << "support for converting a MathOpt hint to MPModelProto "
255 "is not yet supported thus the hint has been lost";
256 }
257 return format == FileFormat::kLinearSolverBinary
258 ? file::SetBinaryProto(file_path, linear_solver_model,
260 : file::SetTextProto(file_path, linear_solver_model,
262 }
263 case FileFormat::kMPS: {
264 ASSIGN_OR_RETURN(const std::string mps_data,
265 ModelProtoToMps(model_proto));
266 return file::SetContents(file_path, mps_data, file::Defaults());
267 }
268 case FileFormat::kLP: {
269 ASSIGN_OR_RETURN(const std::string lp_data, ModelProtoToLp(model_proto));
270 return file::SetContents(file_path, lp_data, file::Defaults());
271 }
272 }
273 ABSL_UNREACHABLE();
274}
275
276} // namespace operations_research::math_opt
#define ASSIGN_OR_RETURN(lhs, rexpr)
absl::StatusOr< std::string > GetContents(absl::string_view path, Options options)
Definition file.cc:349
absl::Status SetBinaryProto(absl::string_view file_name, const google::protobuf::Message &proto, Options options)
Definition file.cc:476
absl::Status SetTextProto(absl::string_view file_name, const google::protobuf::Message &proto, Options options)
Definition file.cc:448
absl::Status GetTextProto(absl::string_view file_name, google::protobuf::Message *proto, Options options)
Definition file.cc:409
absl::Status GetBinaryProto(const absl::string_view file_name, google::protobuf::Message *proto, Options options)
Definition file.cc:463
Options Defaults()
Definition file.h:86
absl::Status SetContents(absl::string_view file_name, absl::string_view contents, Options options)
Definition file.cc:389
std::optional< FileFormat > FormatFromFlagOrFilePath(const std::optional< FileFormat > format_flag_value, const absl::string_view file_path)
absl::StatusOr< std::string > ModelProtoToLp(const ModelProto &model)
bool AbslParseFlag(const absl::string_view text, SolverType *const value, std::string *const error)
absl::StatusOr< std::optional< SolutionHintProto > > MPModelProtoSolutionHintToMathOptHint(const MPModelProto &model)
absl::flat_hash_map< absl::string_view, FileFormat > ExtensionToFileFormat()
absl::StatusOr<::operations_research::MPModelProto > MathOptModelToMPModelProto(const ::operations_research::math_opt::ModelProto &model)
std::string OptionalFormatFlagPossibleValuesList()
absl::Status WriteModel(const absl::string_view file_path, const ModelProto &model_proto, const std::optional< SolutionHintProto > &hint_proto, const FileFormat format)
absl::StatusOr< ModelProto > ReadMpsFile(const absl::string_view filename)
absl::StatusOr< std::string > ModelProtoToMps(const ModelProto &model)
std::ostream & operator<<(std::ostream &ostr, const SecondOrderConeConstraint &constraint)
absl::StatusOr< std::pair< ModelProto, std::optional< SolutionHintProto > > > ReadModel(const absl::string_view file_path, const FileFormat format)
absl::StatusOr<::operations_research::math_opt::ModelProto > MPModelProtoToMathOptModel(const ::operations_research::MPModelProto &model)
absl::StatusOr< ModelProto > ModelProtoFromLp(const absl::string_view lp_data)
Definition lp_parser.cc:52
std::optional< FileFormat > FormatFromFilePath(absl::string_view file_path)
std::string AbslUnparseFlag(const SolverType value)
absl::Span< const FileFormat > AllFileFormats()