59#include "absl/flags/flag.h"
60#include "absl/log/flags.h"
61#include "absl/status/status.h"
62#include "absl/status/statusor.h"
63#include "absl/strings/match.h"
64#include "absl/strings/str_cat.h"
65#include "absl/strings/str_format.h"
66#include "absl/time/clock.h"
67#include "absl/time/time.h"
86 "Input file name with solution in .sol format.");
87ABSL_FLAG(std::optional<std::string>, solver, std::nullopt,
88 "The solver to use: bop, cbc, clp, glop, glpk_lp, glpk_mip, "
89 "gurobi_lp, gurobi_mip, pdlp, scip, knapsack, sat. If unspecified "
90 "either use MPModelRequest.solver_type if the --input is an "
91 "MPModelRequest and the field is set or use glop.");
93 "Number of threads to use by the underlying solver.");
95 "Solver specific parameters file. "
96 "If this flag is set, the --params flag is ignored.");
97ABSL_FLAG(std::string, params,
"",
"Solver specific parameters");
98ABSL_FLAG(absl::Duration, time_limit, absl::InfiniteDuration(),
99 "It specifies a limit on the solving time. The duration must be must "
100 "be positive. It default to an infinite duration meaning that no "
101 "time limit will be imposed.");
103 "If non-empty, write the returned solution in csv format with "
104 "each line formed by a variable name and its value.");
107 "Format in which to dump protos (if flags --dump_model, "
108 "--dump_request, or --dump_response are used). Possible values: "
109 "'text', 'binary', 'json' which correspond to text proto format "
110 "binary proto format, and json. If 'binary' or 'json' are used, "
111 "we append '.bin' and '.json' to file names.");
113 "Whether to gzip dumped protos. Appends .gz to their name.");
115 "If non-empty, dumps MPModelProto there.");
117 "If non-empty, dumps MPModelRequest there.");
119 "If non-empty, dumps MPSolutionResponse there.");
121 "If non-empty, output the best solution in Miplib .sol format.");
123 "If non-empty, dumps the model in mps format there.");
126 "Run MPSolver on the given input file. Many formats are supported: \n"
127 " - a .mps or .mps.gz file,\n"
128 " - an MPModelProto (binary or text, possibly gzipped),\n"
129 " - an MPModelRequest (binary or text, possibly gzipped).";
134MPModelRequest ReadMipModel(
const std::string&
input) {
135 MPModelRequest request_proto;
136 MPModelProto model_proto;
137 if (absl::EndsWith(
input,
".lp")) {
142 model_proto = std::move(result).value();
143 }
else if (absl::EndsWith(
input,
".mps") ||
144 absl::EndsWith(
input,
".mps.gz")) {
145 QCHECK_OK(glop::MPSReader().ParseFile(
input, &model_proto))
146 <<
"Error while parsing the mps file '" <<
input <<
"'.";
154 const bool is_model_proto = model_proto.variable_size() > 0;
155 const bool is_request_proto =
156 request_proto.model().variable_size() > 0 ||
157 !request_proto.model_delta().baseline_model_file_path().empty();
158 if (!is_model_proto && !is_request_proto) {
159 LOG(FATAL) <<
"Failed to parse '" <<
input
160 <<
"' as an MPModelProto or an MPModelRequest.";
162 CHECK(!(is_model_proto && is_request_proto));
164 if (is_request_proto) {
165 LOG(INFO) <<
"Read input proto as an MPModelRequest.";
167 LOG(INFO) <<
"Read input proto as an MPModelProto.";
168 model_proto.Swap(request_proto.mutable_model());
170 return request_proto;
173MPSolutionResponse LocalSolve(
const MPModelRequest& request_proto) {
177 MPSolver solver(request_proto.model().name(),
179 request_proto.solver_type()));
180 const absl::Status set_num_threads_status =
181 solver.SetNumThreads(absl::GetFlag(FLAGS_num_threads));
182 if (set_num_threads_status.ok()) {
183 LOG(INFO) <<
"Set number of threads to " << absl::GetFlag(FLAGS_num_threads)
185 }
else if (absl::GetFlag(FLAGS_num_threads) != 1) {
186 LOG(ERROR) <<
"Failed to set number of threads due to: "
187 << set_num_threads_status.message() <<
". Using 1 as default.";
189 solver.EnableOutput();
191 if (request_proto.has_solver_specific_parameters()) {
192 CHECK(solver.SetSolverSpecificParametersAsString(
193 request_proto.solver_specific_parameters()))
194 <<
"Wrong solver_specific_parameters (bad --params or --params_file ?)";
197 MPSolutionResponse response;
201 std::string error_message;
203 solver.LoadModelFromProtoWithUniqueNamesOrDie(request_proto.model(),
209 if (request_proto.solver_type() ==
211 sat::CpSolverResponse sat_response;
216 response.set_status(status);
217 response.set_status_str(error_message);
221 if (request_proto.has_solver_time_limit_seconds()) {
223 absl::Seconds(request_proto.solver_time_limit_seconds()));
230 SigintHandler handler;
231 handler.Register([&solver] { solver.InterruptSolve(); });
239 !absl::GetFlag(FLAGS_verify_solution)) {
240 const bool verified =
241 solver.VerifySolution(MPSolverParameters().GetDoubleParam(
243 absl::GetFlag(FLAGS_log_verification_errors));
244 LOG(INFO) <<
"The solution "
245 << (verified ?
"was verified." :
"didn't pass verification.");
252 absl::PrintF(
"%-12s: %d\n",
"Nodes", solver.nodes());
256 solver.FillSolutionResponseProto(&response);
261 QCHECK(!absl::GetFlag(FLAGS_input).empty()) <<
"--input is required";
262 QCHECK_GE(absl::GetFlag(FLAGS_time_limit), absl::ZeroDuration())
263 <<
"--time_limit must be given a positive duration";
266 std::optional<MPSolver::OptimizationProblemType> type;
267 if (
const std::optional<std::string> type_flag = absl::GetFlag(FLAGS_solver);
268 type_flag.has_value()) {
271 <<
"Unsupported --solver: " << type_flag.value();
275 MPModelRequest request_proto = ReadMipModel(absl::GetFlag(FLAGS_input));
277 if (!absl::GetFlag(FLAGS_sol_hint).empty()) {
278 const auto read_sol =
279 ParseSolFile(absl::GetFlag(FLAGS_sol_hint), request_proto.model());
280 CHECK_OK(read_sol.status());
281 const MPSolutionResponse& sol = read_sol.value();
282 if (request_proto.model().has_solution_hint()) {
283 LOG(WARNING) <<
"Overwriting solution hint found in the request with "
284 <<
"solution from " << absl::GetFlag(FLAGS_sol_hint);
286 request_proto.mutable_model()->clear_solution_hint();
287 for (
int i = 0; i < sol.variable_value_size(); ++i) {
288 request_proto.mutable_model()->mutable_solution_hint()->add_var_index(i);
289 request_proto.mutable_model()->mutable_solution_hint()->add_var_value(
290 sol.variable_value(i));
294 printf(
"%-12s: '%s'\n",
"File", absl::GetFlag(FLAGS_input).c_str());
298 if (absl::GetFlag(FLAGS_dump_format) ==
"text") {
300 }
else if (absl::GetFlag(FLAGS_dump_format) ==
"binary") {
302 }
else if (absl::GetFlag(FLAGS_dump_format) ==
"json") {
305 LOG(FATAL) <<
"Unsupported --dump_format: "
306 << absl::GetFlag(FLAGS_dump_format);
309 if (!absl::GetFlag(FLAGS_dump_mps).empty()) {
311 request_proto.model()));
315 if (type.has_value() || !request_proto.has_solver_type()) {
319 if (absl::GetFlag(FLAGS_time_limit) != absl::InfiniteDuration()) {
320 LOG(INFO) <<
"Setting a time limit of " << absl::GetFlag(FLAGS_time_limit);
321 request_proto.set_solver_time_limit_seconds(
322 absl::ToDoubleSeconds(absl::GetFlag(FLAGS_time_limit)));
324 if (absl::GetFlag(FLAGS_linear_solver_enable_verbose_output)) {
325 request_proto.set_enable_internal_solver_output(
true);
327 if (!absl::GetFlag(FLAGS_params_file).empty()) {
328 CHECK(absl::GetFlag(FLAGS_params).empty())
329 <<
"--params and --params_file are incompatible";
330 std::string file_contents;
333 <<
"Could not read parameters file.";
334 request_proto.set_solver_specific_parameters(file_contents);
336 if (!absl::GetFlag(FLAGS_params).empty()) {
337 request_proto.set_solver_specific_parameters(absl::GetFlag(FLAGS_params));
341 if (!absl::GetFlag(FLAGS_dump_model).empty()) {
343 request_proto.model(), write_format,
344 absl::GetFlag(FLAGS_dump_gzip)));
346 if (!absl::GetFlag(FLAGS_dump_request).empty()) {
348 write_format, absl::GetFlag(FLAGS_dump_gzip)));
352 "%-12s: %s\n",
"Solver",
354 absl::PrintF(
"%-12s: %s\n",
"Parameters", absl::GetFlag(FLAGS_params));
355 absl::PrintF(
"%-12s: %d x %d\n",
"Dimension",
356 request_proto.model().constraint_size(),
357 request_proto.model().variable_size());
359 const absl::Time solve_start_time = absl::Now();
361 const MPSolutionResponse response = LocalSolve(request_proto);
363 const absl::Duration solving_time = absl::Now() - solve_start_time;
366 absl::PrintF(
"%-12s: %s\n",
"Status",
370 absl::PrintF(
"%-12s: %15.15e\n",
"Objective",
371 has_solution ? response.objective_value() : 0.0);
372 absl::PrintF(
"%-12s: %15.15e\n",
"BestBound",
373 has_solution ? response.best_objective_bound() : 0.0);
374 absl::PrintF(
"%-12s: %s\n",
"StatusString", response.status_str());
375 absl::PrintF(
"%-12s: %-6.4g s\n",
"Time",
376 absl::ToDoubleSeconds(solving_time));
380 if (!absl::GetFlag(FLAGS_sol_file).empty() && has_solution) {
381 std::string sol_string;
382 absl::StrAppend(&sol_string,
"=obj= ", response.objective_value(),
"\n");
383 for (
int i = 0; i < response.variable_value().size(); ++i) {
384 absl::StrAppend(&sol_string, request_proto.model().variable(i).name(),
385 " ", response.variable_value(i),
"\n");
387 LOG(INFO) <<
"Writing .sol solution to '" << absl::GetFlag(FLAGS_sol_file)
392 if (!absl::GetFlag(FLAGS_dump_response).empty() && has_solution) {
394 write_format, absl::GetFlag(FLAGS_dump_gzip)));
396 if (!absl::GetFlag(FLAGS_output_csv).empty() && has_solution) {
397 std::string csv_file;
398 for (
int i = 0; i < response.variable_value_size(); ++i) {
400 absl::StrFormat(
"%s,%e\n", request_proto.model().variable(i).name(),
401 response.variable_value(i));
411int main(
int argc,
char** argv) {
MPModelRequest_SolverType SolverType
static constexpr SolverType SAT_INTEGER_PROGRAMMING
static const ::std::string & SolverType_Name(T value)
@ FEASIBLE
feasible, or stopped by limit.
@ GLOP_LINEAR_PROGRAMMING
static bool ParseSolverType(absl::string_view solver_id, OptimizationProblemType *type)
void InitGoogle(absl::string_view usage, int *argc, char ***argv, bool deprecated)
ABSL_FLAG(std::string, sol_hint, "", "Input file name with solution in .sol format.")
int main(int argc, char **argv)
static const char kUsageStr[]
absl::StatusOr< std::string > GetContents(absl::string_view path, Options options)
absl::Status SetContents(absl::string_view file_name, absl::string_view contents, Options options)
std::string CpSolverResponseStats(const CpSolverResponse &response, bool has_objective)
absl::Status WriteModelToMpsFile(absl::string_view filename, const MPModelProto &model, const MPModelExportOptions &options)
absl::StatusOr< glop::DenseRow > ParseSolFile(absl::string_view file_name, const glop::LinearProgram &model)
bool SolverTypeIsMip(MPModelRequest::SolverType solver_type)
const ::std::string & MPSolverResponseStatus_Name(T value)
@ MPSOLVER_MODEL_IS_VALID
absl::Status WriteProtoToFile(absl::string_view filename, const google::protobuf::Message &proto, ProtoWriteFormat proto_write_format, bool gzipped, bool append_extension_to_file_name)
absl::Status ReadFileToProto(absl::string_view filename, google::protobuf::Message *proto, bool allow_partial)
absl::StatusOr< MPModelProto > ModelProtoFromLpFormat(absl::string_view model)
static int input(yyscan_t yyscanner)