58 #include "absl/flags/flag.h"
59 #include "absl/status/status.h"
60 #include "absl/status/statusor.h"
61 #include "absl/strings/match.h"
62 #include "absl/strings/str_format.h"
63 #include "absl/time/time.h"
72 #include "ortools/linear_solver/linear_solver.pb.h"
76 #include "ortools/sat/cp_model.pb.h"
83 "Input file name with solution in .sol format.");
85 "The solver to use: bop, cbc, clp, glop, glpk_lp, glpk_mip, "
86 "gurobi_lp, gurobi_mip, pdlp, scip, knapsack, sat.");
88 "Number of threads to use by the underlying solver.");
90 "Solver specific parameters file. "
91 "If this flag is set, the --params flag is ignored.");
92 ABSL_FLAG(std::string, params,
"",
"Solver specific parameters");
94 "It specifies a limit on the solving time. The duration must be must "
95 "be positive. It default to an infinite duration meaning that no "
96 "time limit will be imposed.");
98 "If non-empty, write the returned solution in csv format with "
99 "each line formed by a variable name and its value.");
102 "Format in which to dump protos (if flags --dump_model, "
103 "--dump_request, or --dump_response are used). Possible values: "
104 "'text', 'binary', 'json' which correspond to text proto format "
105 "binary proto format, and json. If 'binary' or 'json' are used, "
106 "we append '.bin' and '.json' to file names.");
108 "Whether to gzip dumped protos. Appends .gz to their name.");
110 "If non-empty, dumps MPModelProto there.");
112 "If non-empty, dumps MPModelRequest there.");
114 "If non-empty, dumps MPSolutionResponse there.");
116 "If non-empty, output the best solution in Miplib .sol format.");
121 linear_solver_enable_verbose_output);
124 "Run MPSolver on the given input file. Many formats are supported: \n"
125 " - a .mps or .mps.gz file,\n"
126 " - an MPModelProto (binary or text, possibly gzipped),\n"
127 " - an MPModelRequest (binary or text, possibly gzipped).";
132 MPModelRequest ReadMipModel(
const std::string&
input) {
133 MPModelRequest request_proto;
135 if (absl::EndsWith(
input,
".lp")) {
138 absl::StatusOr<MPModelProto> result = ModelProtoFromLpFormat(data);
141 }
else if (absl::EndsWith(
input,
".mps") ||
142 absl::EndsWith(
input,
".mps.gz")) {
144 <<
"Error while parsing the mps file '" <<
input <<
"'.";
152 const bool is_model_proto =
model_proto.variable_size() > 0;
153 const bool is_request_proto =
154 request_proto.model().variable_size() > 0 ||
155 !request_proto.model_delta().baseline_model_file_path().empty();
156 if (!is_model_proto && !is_request_proto) {
157 LOG(FATAL) <<
"Failed to parse '" <<
input
158 <<
"' as an MPModelProto or an MPModelRequest.";
160 CHECK(!(is_model_proto && is_request_proto));
162 if (is_request_proto) {
163 LOG(INFO) <<
"Read input proto as an MPModelRequest.";
165 LOG(INFO) <<
"Read input proto as an MPModelProto.";
168 return request_proto;
171 MPSolutionResponse LocalSolve(
const MPModelRequest& request_proto) {
175 MPSolver solver(request_proto.model().name(),
177 request_proto.solver_type()));
178 const absl::Status set_num_threads_status =
179 solver.SetNumThreads(absl::GetFlag(FLAGS_num_threads));
180 if (set_num_threads_status.ok()) {
181 LOG(INFO) <<
"Set number of threads to " << absl::GetFlag(FLAGS_num_threads)
183 }
else if (absl::GetFlag(FLAGS_num_threads) != 1) {
184 LOG(ERROR) <<
"Failed to set number of threads due to: "
185 << set_num_threads_status.message() <<
". Using 1 as default.";
187 solver.EnableOutput();
189 if (request_proto.has_solver_specific_parameters()) {
190 CHECK(solver.SetSolverSpecificParametersAsString(
191 request_proto.solver_specific_parameters()))
192 <<
"Wrong solver_specific_parameters (bad --params or --params_file ?)";
199 std::string error_message;
200 const MPSolverResponseStatus
status =
201 solver.LoadModelFromProtoWithUniqueNamesOrDie(request_proto.model(),
204 if (
status != MPSOLVER_MODEL_IS_VALID) {
207 if (request_proto.solver_type() ==
208 MPModelRequest::SAT_INTEGER_PROGRAMMING) {
209 sat::CpSolverResponse sat_response;
215 response.set_status_str(error_message);
219 if (request_proto.has_solver_time_limit_seconds()) {
221 absl::Seconds(request_proto.solver_time_limit_seconds()));
228 SigintHandler handler;
229 handler.Register([&solver] { solver.InterruptSolve(); });
237 !absl::GetFlag(FLAGS_verify_solution)) {
238 LOG(INFO) <<
"Verifying the solution";
239 solver.VerifySolution(MPSolverParameters().GetDoubleParam(
248 absl::PrintF(
"%-12s: %d\n",
"Nodes", solver.nodes());
252 solver.FillSolutionResponseProto(&
response);
257 QCHECK(!absl::GetFlag(FLAGS_input).empty()) <<
"--input is required";
258 QCHECK_GE(absl::GetFlag(FLAGS_time_limit), absl::ZeroDuration())
259 <<
"--time_limit must be given a positive duration";
263 <<
"Unsupported --solver: " << absl::GetFlag(FLAGS_solver);
265 MPModelRequest request_proto = ReadMipModel(absl::GetFlag(FLAGS_input));
267 if (!absl::GetFlag(FLAGS_sol_hint).empty()) {
268 const auto read_sol =
269 ParseSolFile(absl::GetFlag(FLAGS_sol_hint), request_proto.model());
270 CHECK(read_sol.ok());
271 const MPSolutionResponse sol = read_sol.value();
272 if (request_proto.model().has_solution_hint()) {
273 LOG(WARNING) <<
"Overwriting solution hint found in the request with "
274 <<
"solution from " << absl::GetFlag(FLAGS_sol_hint);
276 request_proto.mutable_model()->clear_solution_hint();
277 for (
int i = 0; i < sol.variable_value_size(); ++i) {
278 request_proto.mutable_model()->mutable_solution_hint()->add_var_index(i);
279 request_proto.mutable_model()->mutable_solution_hint()->add_var_value(
280 sol.variable_value(i));
284 printf(
"%-12s: '%s'\n",
"File", absl::GetFlag(FLAGS_input).c_str());
288 if (absl::GetFlag(FLAGS_dump_format) ==
"text") {
290 }
else if (absl::GetFlag(FLAGS_dump_format) ==
"binary") {
292 }
else if (absl::GetFlag(FLAGS_dump_format) ==
"json") {
295 LOG(FATAL) <<
"Unsupported --dump_format: "
296 << absl::GetFlag(FLAGS_dump_format);
301 if (absl::GetFlag(FLAGS_time_limit) != absl::InfiniteDuration()) {
302 LOG(INFO) <<
"Setting a time limit of " << absl::GetFlag(FLAGS_time_limit);
303 request_proto.set_solver_time_limit_seconds(
304 absl::ToDoubleSeconds(absl::GetFlag(FLAGS_time_limit)));
306 if (absl::GetFlag(FLAGS_linear_solver_enable_verbose_output)) {
307 request_proto.set_enable_internal_solver_output(
true);
309 if (!absl::GetFlag(FLAGS_params_file).empty()) {
310 CHECK(absl::GetFlag(FLAGS_params).empty())
311 <<
"--params and --params_file are incompatible";
312 std::string file_contents;
315 <<
"Could not read parameters file.";
316 request_proto.set_solver_specific_parameters(file_contents);
318 if (!absl::GetFlag(FLAGS_params).empty()) {
319 request_proto.set_solver_specific_parameters(absl::GetFlag(FLAGS_params));
323 if (!absl::GetFlag(FLAGS_dump_model).empty()) {
325 request_proto.model(), write_format,
326 absl::GetFlag(FLAGS_dump_gzip)));
328 if (!absl::GetFlag(FLAGS_dump_request).empty()) {
330 write_format, absl::GetFlag(FLAGS_dump_gzip)));
334 "%-12s: %s\n",
"Solver",
335 MPModelRequest::SolverType_Name(request_proto.solver_type()).c_str());
336 absl::PrintF(
"%-12s: %s\n",
"Parameters", absl::GetFlag(FLAGS_params));
337 absl::PrintF(
"%-12s: %d x %d\n",
"Dimension",
338 request_proto.model().constraint_size(),
339 request_proto.model().variable_size());
341 const absl::Time solve_start_time = absl::Now();
343 const MPSolutionResponse
response = LocalSolve(request_proto);
345 const absl::Duration solving_time = absl::Now() - solve_start_time;
346 const bool has_solution =
response.status() == MPSOLVER_OPTIMAL ||
347 response.status() == MPSOLVER_FEASIBLE;
348 absl::PrintF(
"%-12s: %s\n",
"Status",
349 MPSolverResponseStatus_Name(
350 static_cast<MPSolverResponseStatus
>(
response.status()))
352 absl::PrintF(
"%-12s: %15.15e\n",
"Objective",
353 has_solution ?
response.objective_value() : 0.0);
354 absl::PrintF(
"%-12s: %15.15e\n",
"BestBound",
355 has_solution ?
response.best_objective_bound() : 0.0);
356 absl::PrintF(
"%-12s: %s\n",
"StatusString",
response.status_str());
357 absl::PrintF(
"%-12s: %-6.4g s\n",
"Time",
358 absl::ToDoubleSeconds(solving_time));
362 if (!absl::GetFlag(FLAGS_sol_file).empty() && has_solution) {
363 std::string sol_string;
364 absl::StrAppend(&sol_string,
"=obj= ",
response.objective_value(),
"\n");
365 for (
int i = 0; i <
response.variable_value().size(); ++i) {
366 absl::StrAppend(&sol_string, request_proto.model().variable(i).name(),
367 " ",
response.variable_value(i),
"\n");
369 LOG(INFO) <<
"Writing .sol solution to '" << absl::GetFlag(FLAGS_sol_file)
374 if (!absl::GetFlag(FLAGS_dump_response).empty() && has_solution) {
376 write_format, absl::GetFlag(FLAGS_dump_gzip)));
378 if (!absl::GetFlag(FLAGS_output_csv).empty() && has_solution) {
379 std::string csv_file;
380 for (
int i = 0; i <
response.variable_value_size(); ++i) {
382 absl::StrFormat(
"%s,%e\n", request_proto.model().variable(i).name(),
393 int main(
int argc,
char** argv) {
395 operations_research::Run();
ResultStatus
The status of solving the problem.
@ FEASIBLE
feasible, or stopped by limit.
OptimizationProblemType
The type of problems (LP or MIP) that will be solved and the underlying solver (GLOP,...
static bool ParseSolverType(absl::string_view solver_id, OptimizationProblemType *type)
Parses the name of the solver.
@ PRIMAL_TOLERANCE
Advanced usage: tolerance for primal feasibility of basic solutions.
CpModelProto const * model_proto
SharedResponseManager * response
ModelSharedTimeLimit * time_limit
void InitGoogle(const char *usage, int *argc, char ***argv, bool deprecated)
int main(int argc, char **argv)
static const char kUsageStr[]
ABSL_DECLARE_FLAG(bool, verify_solution)
ABSL_FLAG(std::string, input, "", "REQUIRED: Input file name.")
A C++ wrapper that provides a simple and unified interface to several linear programming and mixed in...
absl::Status GetContents(const absl::string_view &filename, std::string *output, int flags)
absl::Status SetContents(const absl::string_view &filename, const absl::string_view &contents, int flags)
void ParseFile(const std::string &filename, bool presolve)
std::string CpSolverResponseStats(const CpSolverResponse &response, bool has_objective)
Returns a string with some statistics on the solver response.
Collection of objects used to extend the Constraint Solver library.
bool SolverTypeIsMip(MPModelRequest::SolverType solver_type)
bool WriteProtoToFile(absl::string_view filename, const google::protobuf::Message &proto, ProtoWriteFormat proto_write_format, bool gzipped, bool append_extension_to_file_name)
absl::StatusOr< glop::DenseRow > ParseSolFile(const std::string &file_name, const glop::LinearProgram &model)
bool ReadFileToProto(absl::string_view filename, google::protobuf::Message *proto)
static int input(yyscan_t yyscanner)