OR-Tools  9.3
mathopt_solve_main.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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
14// Tool to run MathOpt on the given problems.
15//
16// Examples:
17//
18// mathopt_solve --input_file model.pb
19//
20// mathopt_solve --input_file model.mps.gz --solver_type=glop
21//
22// mathopt_solve --input_file model --solver_logs --format=mathopt
23//
24#include <iostream>
25#include <memory>
26#include <optional>
27#include <string>
28#include <utility>
29#include <vector>
30
31#include "absl/flags/flag.h"
32#include "absl/status/status.h"
33#include "absl/status/statusor.h"
34#include "absl/strings/match.h"
35#include "absl/strings/str_cat.h"
36#include "absl/strings/str_join.h"
37#include "absl/strings/string_view.h"
38#include "absl/time/time.h"
39#include "ortools/base/file.h"
46#include "ortools/math_opt/parameters.pb.h"
48
49inline constexpr absl::string_view kMathOptBinaryFormat = "mathopt";
50inline constexpr absl::string_view kMathOptTextFormat = "mathopt_txt";
51inline constexpr absl::string_view kMPSFormat = "mps";
52inline constexpr absl::string_view kAutoFormat = "auto";
53
54inline constexpr absl::string_view kPbExt = ".pb";
55inline constexpr absl::string_view kProtoExt = ".proto";
56inline constexpr absl::string_view kPbTxtExt = ".pb.txt";
57inline constexpr absl::string_view kTextProtoExt = ".textproto";
58inline constexpr absl::string_view kMPSExt = ".mps";
59inline constexpr absl::string_view kMPSGzipExt = ".mps.gz";
60
61namespace {
62
63struct SolverTypeProtoFormatter {
64 void operator()(
65 std::string* const out,
66 const operations_research::math_opt::SolverTypeProto solver_type) {
67 out->append(EnumToString(EnumFromProto(solver_type).value()));
68 }
69};
70
71} // namespace
72
73ABSL_FLAG(std::string, input_file, "",
74 "the file containing the model to solve; use --format to specify the "
75 "file format");
76ABSL_FLAG(std::string, format, "auto",
77 absl::StrCat(
78 "the format of the --input_file; possible values:\n", "* ",
79 kMathOptBinaryFormat, ": for a MathOpt ModelProto in binary\n",
80 "* ", kMathOptTextFormat, ": when the proto is in text\n", "* ",
81 kMPSFormat, ": for MPS file (which can be GZiped)\n", "* ",
82 kAutoFormat, ": to guess the format from the file extension:\n",
83 " - '", kPbExt, "', '", kProtoExt, "': ", kMathOptBinaryFormat,
84 "\n", " - '", kPbTxtExt, "', '", kTextProtoExt,
85 "': ", kMathOptTextFormat, "\n", " - '", kMPSExt, "', '",
86 kMPSGzipExt, "': ", kMPSFormat));
88 std::vector<std::string>, update_files, {},
89 absl::StrCat(
90 "the file containing ModelUpdateProto to apply to the --input_file; "
91 "when this flag is used, the --format must be either ",
95 absl::StrCat(
96 "the solver to use, possible values: ",
97 absl::StrJoin(
99 ->RegisteredSolvers(),
100 ", ", SolverTypeProtoFormatter())));
101ABSL_FLAG(bool, solver_logs, false,
102 "use a message callback to print the solver convergence logs");
103ABSL_FLAG(absl::Duration, time_limit, absl::InfiniteDuration(),
104 "the time limit to use for the solve");
105
106namespace operations_research {
107namespace math_opt {
108namespace {
109
110// Returned the guessed format (one of the kXxxFormat constant) from the file
111// extension; or nullopt.
112std::optional<absl::string_view> FormatFromFilePath(
113 const absl::string_view file_path) {
114 const std::vector<std::pair<absl::string_view, absl::string_view>>
115 extension_to_format = {
119 };
120
121 for (const auto& [ext, format] : extension_to_format) {
122 if (absl::EndsWith(file_path, ext)) {
123 return format;
124 }
125 }
126
127 return std::nullopt;
128}
129
130// Returns the ModelProto read from the given file. The format must not be
131// kAutoFormat; other invalid values will be reported as QFATAL log mentioning
132// the --format flag.
133absl::StatusOr<ModelProto> ReadModel(const absl::string_view file_path,
134 const absl::string_view format) {
135 if (format == kMathOptBinaryFormat) {
136 return file::GetBinaryProto<ModelProto>(file_path, file::Defaults());
137 }
138 if (format == kMathOptTextFormat) {
139 return file::GetTextProto<ModelProto>(file_path, file::Defaults());
140 }
141 if (format == kMPSFormat) {
142 return ReadMpsFile(file_path);
143 }
144 LOG(QFATAL) << "Unsupported value of --format: " << format;
145}
146
147// Returns the ModelUpdateProto read from the given file. The format must be
148// kMathOptBinaryFormat or kMathOptTextFormat; other values will generate an
149// error.
150absl::StatusOr<ModelUpdateProto> ReadModelUpdate(
151 const absl::string_view file_path, const absl::string_view format) {
152 if (format == kMathOptBinaryFormat) {
153 return file::GetBinaryProto<ModelUpdateProto>(file_path, file::Defaults());
154 }
155 if (format == kMathOptTextFormat) {
156 return file::GetTextProto<ModelUpdateProto>(file_path, file::Defaults());
157 }
158 return absl::InternalError(
159 absl::StrCat("invalid format in ReadModelUpdate(): ", format));
160}
161
162// Prints the summary of the solve result.
163absl::Status PrintSummary(const SolveResult& result) {
164 std::cout << "Solve finished:\n"
165 << " termination: " << result.termination << "\n"
166 << " solve time: " << result.solve_stats.solve_time
167 << "\n best primal bound: " << result.solve_stats.best_primal_bound
168 << "\n best dual bound: " << result.solve_stats.best_dual_bound
169 << std::endl;
170 if (result.solutions.empty()) {
171 std::cout << " no solution" << std::endl;
172 }
173 for (int i = 0; i < result.solutions.size(); ++i) {
174 const Solution& solution = result.solutions[i];
175 std::cout << " solution #" << (i + 1) << " objective: ";
176 if (solution.primal_solution.has_value()) {
177 std::cout << solution.primal_solution->objective_value;
178 } else {
179 std::cout << "n/a";
180 }
181 std::cout << std::endl;
182 }
183
184 return absl::OkStatus();
185}
186
187absl::Status RunSolver() {
188 const std::string input_file_path = absl::GetFlag(FLAGS_input_file);
189 if (input_file_path.empty()) {
190 LOG(QFATAL) << "The flag --input_file is mandatory.";
191 }
192
193 // Parses --format.
194 std::string format = absl::GetFlag(FLAGS_format);
195 if (format == kAutoFormat) {
196 const std::optional<absl::string_view> guessed_format =
197 FormatFromFilePath(input_file_path);
198 if (!guessed_format) {
199 LOG(QFATAL) << "Can't guess the format from the file extension, please "
200 "use --format to specify the file format explicitly.";
201 }
202 format = *guessed_format;
203 }
204 // We deal with input validation in the ReadModel() function.
205
206 // Read the model and the optional updates.
207 const std::vector<std::string> update_file_paths =
208 absl::GetFlag(FLAGS_update_files);
209 if (!update_file_paths.empty() && format != kMathOptBinaryFormat &&
210 format != kMathOptTextFormat) {
211 LOG(QFATAL) << "Can't use --update_files with a input of format " << format
212 << ".";
213 }
214
215 OR_ASSIGN_OR_RETURN3(const ModelProto model_proto,
216 ReadModel(input_file_path, format),
217 _ << "failed to read " << input_file_path);
218
219 std::vector<ModelUpdateProto> model_updates;
220 for (const std::string& update_file_path : update_file_paths) {
221 ASSIGN_OR_RETURN(ModelUpdateProto update,
222 ReadModelUpdate(update_file_path, format));
223 model_updates.emplace_back(std::move(update));
224 }
225
226 // Solve the problem.
227 ASSIGN_OR_RETURN(const std::unique_ptr<Model> model,
229 for (int u = 0; u < model_updates.size(); ++u) {
230 const ModelUpdateProto& update = model_updates[u];
231 RETURN_IF_ERROR(model->ApplyUpdateProto(update))
232 << "failed to apply the update file: " << update_file_paths[u];
233 }
234
235 SolveArguments solve_args = {
236 .parameters = {.time_limit = absl::GetFlag(FLAGS_time_limit)},
237 };
238 if (absl::GetFlag(FLAGS_solver_logs)) {
239 solve_args.message_callback = PrinterMessageCallback(std::cout, "logs| ");
240 }
242 const SolveResult result,
243 Solve(*model, absl::GetFlag(FLAGS_solver_type), solve_args),
244 _ << "the solver failed");
245
246 RETURN_IF_ERROR(PrintSummary(result));
247
248 return absl::OkStatus();
249}
250
251} // namespace
252} // namespace math_opt
253} // namespace operations_research
254
255int main(int argc, char* argv[]) {
256 InitGoogle(argv[0], &argc, &argv, /*remove_flags=*/true);
257
258 const absl::Status status = operations_research::math_opt::RunSolver();
259 // We don't use QCHECK_OK() here since the logged message contains more than
260 // the failing status.
261 if (!status.ok()) {
262 LOG(QFATAL) << status;
263 }
264
265 return 0;
266}
#define LOG(severity)
Definition: base/logging.h:420
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
static absl::StatusOr< std::unique_ptr< Model > > FromModelProto(const ModelProto &model_proto)
CpModelProto const * model_proto
ModelSharedTimeLimit * time_limit
int64_t value
absl::Status status
Definition: g_gurobi.cc:35
GRBmodel * model
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
Definition: init_google.h:32
constexpr absl::string_view kMathOptTextFormat
int main(int argc, char *argv[])
constexpr absl::string_view kPbExt
constexpr absl::string_view kMathOptBinaryFormat
constexpr absl::string_view kMPSGzipExt
constexpr absl::string_view kMPSExt
constexpr absl::string_view kAutoFormat
constexpr absl::string_view kMPSFormat
constexpr absl::string_view kTextProtoExt
ABSL_FLAG(std::string, input_file, "", "the file containing the model to solve; use --format to specify the " "file format")
constexpr absl::string_view kProtoExt
constexpr absl::string_view kPbTxtExt
int Defaults()
Definition: base/file.h:120
absl::string_view EnumToString(const E value)
Definition: enums.h:283
absl::StatusOr< SolveResult > Solve(const Model &model, const SolverType solver_type, const SolveArguments &solve_args, const SolverInitArguments &init_args)
Definition: solve.cc:94
MessageCallback PrinterMessageCallback(std::ostream &output_stream, const absl::string_view prefix)
absl::StatusOr< ModelProto > ReadMpsFile(const absl::string_view filename)
std::optional< typename EnumProto< P >::Cpp > EnumFromProto(const P proto_value)
Definition: enums.h:275
Collection of objects used to extend the Constraint Solver library.
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)