2022-06-17 08:40:20 +02:00
|
|
|
// Copyright 2010-2022 Google LLC
|
2014-07-08 09:27:02 +00:00
|
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
|
// you may not use this file except in compliance with the License.
|
|
|
|
|
// You may obtain a copy of the License at
|
|
|
|
|
//
|
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
|
//
|
|
|
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
|
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
|
// See the License for the specific language governing permissions and
|
|
|
|
|
// limitations under the License.
|
2014-07-09 15:18:27 +00:00
|
|
|
|
2014-07-08 09:27:02 +00:00
|
|
|
// Command line interface to the MPSolver class.
|
|
|
|
|
// See linear_solver.h and kUsageStr below.
|
2021-10-12 10:09:18 +02:00
|
|
|
//
|
|
|
|
|
// Examples.
|
|
|
|
|
//
|
2021-10-12 10:17:21 +02:00
|
|
|
// 1. To run SCIP for 90 seconds, dumping available information use:
|
2021-10-12 10:09:18 +02:00
|
|
|
//
|
|
|
|
|
// solve --solver=scip \
|
2021-10-12 10:17:21 +02:00
|
|
|
// --time_limit=90s \
|
2021-10-12 10:09:18 +02:00
|
|
|
// --logtostderr \
|
|
|
|
|
// --linear_solver_enable_verbose_output \
|
|
|
|
|
// --input=/tmp/foo.mps \
|
|
|
|
|
// --dump_model=/tmp/foo.model \
|
|
|
|
|
// --dump_request=/tmp/foo.request \
|
|
|
|
|
// --dump_response=/tmp/foo.response \
|
|
|
|
|
// >/tmp/foo.out 2>/tmp/foo.err
|
|
|
|
|
//
|
|
|
|
|
// 2. To run CP_SAT for 10 minutes with 8 workers, you can use
|
|
|
|
|
// CP-SAT parameters:
|
|
|
|
|
//
|
|
|
|
|
// solve --solver=sat \
|
2021-10-12 10:17:21 +02:00
|
|
|
// --params="max_time_in_seconds:600, num_search_workers:8"
|
2021-10-12 10:09:18 +02:00
|
|
|
// --logtostderr \
|
|
|
|
|
// --input=/tmp/foo.mps \
|
|
|
|
|
// 2>/tmp/foo.err
|
|
|
|
|
//
|
|
|
|
|
// or use the solve binary flags:
|
|
|
|
|
//
|
|
|
|
|
// solve --solver=sat \
|
|
|
|
|
// --time_limit=10m \
|
|
|
|
|
// --num_threads=8 \
|
|
|
|
|
// --logtostderr \
|
|
|
|
|
// --input=/tmp/foo.mps \
|
|
|
|
|
// --dump_model=/tmp/foo.model \
|
|
|
|
|
// --dump_request=/tmp/foo.request \
|
|
|
|
|
// --dump_response=/tmp/foo.response \
|
|
|
|
|
// 2>/tmp/foo.err
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2022-04-06 11:12:27 +02:00
|
|
|
#include <algorithm>
|
2014-07-08 09:27:02 +00:00
|
|
|
#include <cstdio>
|
|
|
|
|
#include <string>
|
2022-04-06 11:12:27 +02:00
|
|
|
#include <vector>
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2021-10-07 17:32:30 +02:00
|
|
|
#include "absl/flags/flag.h"
|
2021-01-14 10:48:19 +01:00
|
|
|
#include "absl/flags/parse.h"
|
|
|
|
|
#include "absl/flags/usage.h"
|
2018-10-31 16:18:18 +01:00
|
|
|
#include "absl/strings/match.h"
|
|
|
|
|
#include "absl/strings/str_format.h"
|
2021-10-12 10:09:18 +02:00
|
|
|
#include "absl/time/time.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/base/commandlineflags.h"
|
2018-06-08 16:40:43 +02:00
|
|
|
#include "ortools/base/file.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/base/integral_types.h"
|
2023-02-01 14:14:30 +01:00
|
|
|
#include "ortools/base/init_google.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/base/logging.h"
|
|
|
|
|
#include "ortools/base/timer.h"
|
|
|
|
|
#include "ortools/linear_solver/linear_solver.h"
|
|
|
|
|
#include "ortools/linear_solver/linear_solver.pb.h"
|
2019-04-18 19:24:37 +02:00
|
|
|
#include "ortools/lp_data/mps_reader.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/lp_data/proto_utils.h"
|
2021-10-07 17:32:30 +02:00
|
|
|
#include "ortools/sat/cp_model.pb.h"
|
|
|
|
|
#include "ortools/sat/cp_model_solver.h"
|
2017-06-08 12:33:16 +02:00
|
|
|
#include "ortools/util/file_util.h"
|
2021-10-07 17:32:30 +02:00
|
|
|
#include "ortools/util/sigint.h"
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2020-10-23 11:50:14 +02:00
|
|
|
ABSL_FLAG(std::string, input, "", "REQUIRED: Input file name.");
|
|
|
|
|
ABSL_FLAG(std::string, solver, "glop",
|
|
|
|
|
"The solver to use: bop, cbc, clp, glop, glpk_lp, glpk_mip, "
|
2022-04-05 16:44:24 -04:00
|
|
|
"gurobi_lp, gurobi_mip, pdlp, scip, knapsack, sat.");
|
2020-10-23 11:50:14 +02:00
|
|
|
ABSL_FLAG(int, num_threads, 1,
|
|
|
|
|
"Number of threads to use by the underlying solver.");
|
|
|
|
|
ABSL_FLAG(std::string, params_file, "",
|
|
|
|
|
"Solver specific parameters file. "
|
|
|
|
|
"If this flag is set, the --params flag is ignored.");
|
|
|
|
|
ABSL_FLAG(std::string, params, "", "Solver specific parameters");
|
2021-10-13 16:39:39 +02:00
|
|
|
ABSL_FLAG(absl::Duration, time_limit, absl::InfiniteDuration(),
|
|
|
|
|
"It specifies a limit on the solving time. The duration must be must "
|
|
|
|
|
"be positive. It default to an infinite duration meaning that no "
|
|
|
|
|
"time limit will be imposed.");
|
2020-10-23 11:50:14 +02:00
|
|
|
ABSL_FLAG(std::string, output_csv, "",
|
|
|
|
|
"If non-empty, write the returned solution in csv format with "
|
|
|
|
|
"each line formed by a variable name and its value.");
|
2015-07-31 16:16:16 +02:00
|
|
|
|
2020-10-23 11:50:14 +02:00
|
|
|
ABSL_FLAG(std::string, dump_format, "text",
|
|
|
|
|
"Format in which to dump protos (if flags --dump_model, "
|
|
|
|
|
"--dump_request, or --dump_response are used). Possible values: "
|
|
|
|
|
"'text', 'binary', 'json' which correspond to text proto format "
|
|
|
|
|
"binary proto format, and json. If 'binary' or 'json' are used, "
|
|
|
|
|
"we append '.bin' and '.json' to file names.");
|
|
|
|
|
ABSL_FLAG(bool, dump_gzip, false,
|
|
|
|
|
"Whether to gzip dumped protos. Appends .gz to their name.");
|
|
|
|
|
ABSL_FLAG(std::string, dump_model, "",
|
|
|
|
|
"If non-empty, dumps MPModelProto there.");
|
|
|
|
|
ABSL_FLAG(std::string, dump_request, "",
|
|
|
|
|
"If non-empty, dumps MPModelRequest there.");
|
|
|
|
|
ABSL_FLAG(std::string, dump_response, "",
|
2021-10-07 17:32:30 +02:00
|
|
|
"If non-empty, dumps MPSolutionResponse there.");
|
|
|
|
|
ABSL_FLAG(std::string, sol_file, "",
|
|
|
|
|
"If non-empty, output the best solution in Miplib .sol format.");
|
2016-10-05 14:08:27 +02:00
|
|
|
|
2020-10-23 11:50:14 +02:00
|
|
|
ABSL_DECLARE_FLAG(bool, verify_solution); // Defined in ./linear_solver.cc
|
2021-10-07 17:32:30 +02:00
|
|
|
ABSL_DECLARE_FLAG(
|
|
|
|
|
bool,
|
|
|
|
|
linear_solver_enable_verbose_output); // Defined in ./linear_solver.cc
|
2017-07-20 11:30:17 -07:00
|
|
|
|
2014-07-08 09:27:02 +00:00
|
|
|
static const char kUsageStr[] =
|
|
|
|
|
"Run MPSolver on the given input file. Many formats are supported: \n"
|
|
|
|
|
" - a .mps or .mps.gz file,\n"
|
|
|
|
|
" - an MPModelProto (binary or text, possibly gzipped),\n"
|
2021-10-07 17:32:30 +02:00
|
|
|
" - an MPModelRequest (binary or text, possibly gzipped).";
|
2014-07-08 09:27:02 +00:00
|
|
|
|
|
|
|
|
namespace operations_research {
|
|
|
|
|
namespace {
|
2015-05-29 00:11:23 +02:00
|
|
|
|
2021-10-07 17:32:30 +02:00
|
|
|
MPModelRequest ReadMipModel(const std::string& input) {
|
|
|
|
|
MPModelRequest request_proto;
|
|
|
|
|
MPModelProto model_proto;
|
|
|
|
|
if (absl::EndsWith(input, ".mps") || absl::EndsWith(input, ".mps.gz")) {
|
|
|
|
|
QCHECK_OK(glop::MPSReader().ParseFile(input, &model_proto))
|
|
|
|
|
<< "Error while parsing the mps file '" << input << "'.";
|
|
|
|
|
} else {
|
|
|
|
|
ReadFileToProto(input, &model_proto);
|
|
|
|
|
ReadFileToProto(input, &request_proto);
|
|
|
|
|
}
|
|
|
|
|
// If the input is a proto in binary format, both ReadFileToProto could
|
|
|
|
|
// return true. Instead use the actual number of variables found to test the
|
|
|
|
|
// correct format of the input.
|
|
|
|
|
const bool is_model_proto = model_proto.variable_size() > 0;
|
|
|
|
|
const bool is_request_proto = request_proto.model().variable_size() > 0;
|
|
|
|
|
if (!is_model_proto && !is_request_proto) {
|
|
|
|
|
LOG(FATAL) << "Failed to parse '" << input
|
|
|
|
|
<< "' as an MPModelProto or an MPModelRequest.";
|
|
|
|
|
} else {
|
|
|
|
|
CHECK(!(is_model_proto && is_request_proto));
|
|
|
|
|
}
|
|
|
|
|
if (is_request_proto) {
|
|
|
|
|
LOG(INFO) << "Read input proto as an MPModelRequest.";
|
|
|
|
|
} else {
|
|
|
|
|
LOG(INFO) << "Read input proto as an MPModelProto.";
|
|
|
|
|
model_proto.Swap(request_proto.mutable_model());
|
|
|
|
|
}
|
|
|
|
|
return request_proto;
|
|
|
|
|
}
|
|
|
|
|
|
2019-04-18 19:24:37 +02:00
|
|
|
// Returns false if an error was encountered.
|
|
|
|
|
// More details should be available in the logs.
|
2021-10-12 10:09:18 +02:00
|
|
|
bool Run(MPSolver::OptimizationProblemType type) {
|
2021-10-07 17:32:30 +02:00
|
|
|
MPModelRequest request_proto = ReadMipModel(absl::GetFlag(FLAGS_input));
|
2018-10-31 16:18:18 +01:00
|
|
|
|
2020-10-21 00:21:54 +02:00
|
|
|
printf("%-12s: '%s'\n", "File", absl::GetFlag(FLAGS_input).c_str());
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2017-03-28 16:13:30 +02:00
|
|
|
// Detect format to dump protos.
|
|
|
|
|
operations_research::ProtoWriteFormat write_format;
|
2020-10-21 00:21:54 +02:00
|
|
|
if (absl::GetFlag(FLAGS_dump_format) == "text") {
|
2017-03-28 16:13:30 +02:00
|
|
|
write_format = ProtoWriteFormat::kProtoText;
|
2020-10-21 00:21:54 +02:00
|
|
|
} else if (absl::GetFlag(FLAGS_dump_format) == "binary") {
|
2017-03-28 16:13:30 +02:00
|
|
|
write_format = ProtoWriteFormat::kProtoBinary;
|
2020-10-21 00:21:54 +02:00
|
|
|
} else if (absl::GetFlag(FLAGS_dump_format) == "json") {
|
2017-03-28 16:13:30 +02:00
|
|
|
write_format = ProtoWriteFormat::kJson;
|
|
|
|
|
} else {
|
2020-10-22 23:36:58 +02:00
|
|
|
LOG(FATAL) << "Unsupported --dump_format: "
|
|
|
|
|
<< absl::GetFlag(FLAGS_dump_format);
|
2017-03-28 16:13:30 +02:00
|
|
|
}
|
|
|
|
|
|
2016-10-05 14:08:27 +02:00
|
|
|
// Create the solver, we use the name of the model as the solver name.
|
2021-10-07 17:32:30 +02:00
|
|
|
MPSolver solver(request_proto.model().name(), type);
|
2020-05-06 18:35:18 +02:00
|
|
|
const absl::Status set_num_threads_status =
|
2020-10-21 00:21:54 +02:00
|
|
|
solver.SetNumThreads(absl::GetFlag(FLAGS_num_threads));
|
2019-04-18 19:24:37 +02:00
|
|
|
if (set_num_threads_status.ok()) {
|
2020-10-21 00:21:54 +02:00
|
|
|
LOG(INFO) << "Set number of threads to " << absl::GetFlag(FLAGS_num_threads)
|
|
|
|
|
<< ".";
|
2021-10-07 17:32:30 +02:00
|
|
|
} else if (absl::GetFlag(FLAGS_num_threads) != 1) {
|
2019-04-18 19:24:37 +02:00
|
|
|
LOG(ERROR) << "Failed to set number of threads due to: "
|
|
|
|
|
<< set_num_threads_status.message() << ". Using 1 as default.";
|
|
|
|
|
}
|
2016-10-05 14:08:27 +02:00
|
|
|
solver.EnableOutput();
|
2020-10-21 00:21:54 +02:00
|
|
|
if (!absl::GetFlag(FLAGS_params_file).empty()) {
|
2016-10-05 14:08:27 +02:00
|
|
|
std::string file_contents;
|
2020-10-21 00:21:54 +02:00
|
|
|
CHECK_OK(file::GetContents(absl::GetFlag(FLAGS_params_file), &file_contents,
|
|
|
|
|
file::Defaults()))
|
2016-10-05 14:08:27 +02:00
|
|
|
<< "Could not read parameters file.";
|
|
|
|
|
CHECK(solver.SetSolverSpecificParametersAsString(file_contents));
|
2020-10-21 00:21:54 +02:00
|
|
|
} else if (!absl::GetFlag(FLAGS_params).empty()) {
|
2020-10-22 23:36:58 +02:00
|
|
|
CHECK(
|
|
|
|
|
solver.SetSolverSpecificParametersAsString(absl::GetFlag(FLAGS_params)))
|
|
|
|
|
<< "Wrong --params format.";
|
2016-10-05 14:08:27 +02:00
|
|
|
}
|
2019-11-22 15:17:10 +01:00
|
|
|
absl::PrintF(
|
|
|
|
|
"%-12s: %s\n", "Solver",
|
2020-10-22 23:36:58 +02:00
|
|
|
MPModelRequest::SolverType_Name(
|
|
|
|
|
static_cast<MPModelRequest::SolverType>(solver.ProblemType()))
|
|
|
|
|
.c_str());
|
2016-10-05 14:08:27 +02:00
|
|
|
|
2014-07-08 09:27:02 +00:00
|
|
|
// Load the proto into the solver.
|
2015-06-17 16:29:25 +02:00
|
|
|
std::string error_message;
|
2016-10-05 14:08:27 +02:00
|
|
|
|
|
|
|
|
// If requested, save the model to file.
|
2020-10-21 00:21:54 +02:00
|
|
|
if (!absl::GetFlag(FLAGS_dump_model).empty()) {
|
2021-10-07 17:32:30 +02:00
|
|
|
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_model),
|
|
|
|
|
request_proto.model(), write_format,
|
|
|
|
|
absl::GetFlag(FLAGS_dump_gzip)));
|
2016-10-05 14:08:27 +02:00
|
|
|
}
|
|
|
|
|
|
2015-06-17 16:29:25 +02:00
|
|
|
const MPSolverResponseStatus status =
|
2021-10-07 17:32:30 +02:00
|
|
|
solver.LoadModelFromProtoWithUniqueNamesOrDie(request_proto.model(),
|
2017-03-28 16:13:30 +02:00
|
|
|
&error_message);
|
2017-07-20 11:30:17 -07:00
|
|
|
// Note, the underlying MPSolver treats time limit equal to 0 as no limit.
|
2019-04-18 19:24:37 +02:00
|
|
|
if (status != MPSOLVER_MODEL_IS_VALID) {
|
|
|
|
|
LOG(ERROR) << MPSolverResponseStatus_Name(status) << ": " << error_message;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
2021-10-07 18:19:24 +02:00
|
|
|
|
|
|
|
|
// Time limits.
|
2021-10-13 16:39:39 +02:00
|
|
|
if (absl::GetFlag(FLAGS_time_limit) != absl::InfiniteDuration()) {
|
2021-10-12 10:09:18 +02:00
|
|
|
LOG(INFO) << "Setting a time limit of " << absl::GetFlag(FLAGS_time_limit);
|
2021-10-07 18:19:24 +02:00
|
|
|
// Overwrite the request time limit.
|
|
|
|
|
request_proto.set_solver_time_limit_seconds(
|
2021-10-12 10:09:18 +02:00
|
|
|
absl::ToDoubleSeconds(absl::GetFlag(FLAGS_time_limit)));
|
2021-10-07 18:19:24 +02:00
|
|
|
}
|
|
|
|
|
if (request_proto.has_solver_time_limit_seconds()) {
|
|
|
|
|
solver.SetTimeLimit(
|
|
|
|
|
absl::Seconds(request_proto.solver_time_limit_seconds()));
|
|
|
|
|
}
|
|
|
|
|
|
2019-11-22 15:17:10 +01:00
|
|
|
absl::PrintF("%-12s: %d x %d\n", "Dimension", solver.NumConstraints(),
|
|
|
|
|
solver.NumVariables());
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2021-10-07 17:32:30 +02:00
|
|
|
// Register a signal handler to interrupt the solve when the user presses ^C.
|
|
|
|
|
// Note that we ignore all previously registered handler here. If SCIP is
|
|
|
|
|
// used, this handler will be overridden by the one of SCIP that does the same
|
|
|
|
|
// thing.
|
|
|
|
|
SigintHandler handler;
|
|
|
|
|
handler.Register([&solver] { solver.InterruptSolve(); });
|
|
|
|
|
|
2014-07-08 09:27:02 +00:00
|
|
|
// Solve.
|
|
|
|
|
MPSolverParameters param;
|
2016-03-18 12:16:04 +01:00
|
|
|
MPSolver::ResultStatus solve_status = MPSolver::NOT_SOLVED;
|
2018-10-31 16:18:18 +01:00
|
|
|
absl::Duration solving_time;
|
|
|
|
|
const absl::Time time_before = absl::Now();
|
|
|
|
|
solve_status = solver.Solve(param);
|
|
|
|
|
solving_time = absl::Now() - time_before;
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2016-10-05 14:08:27 +02:00
|
|
|
// If requested, re-create a corresponding MPModelRequest and save it to file.
|
2020-10-21 00:21:54 +02:00
|
|
|
if (!absl::GetFlag(FLAGS_dump_request).empty()) {
|
2021-10-07 17:32:30 +02:00
|
|
|
request_proto.set_solver_type(
|
2016-10-05 14:08:27 +02:00
|
|
|
static_cast<MPModelRequest::SolverType>(solver.ProblemType()));
|
2021-10-07 17:32:30 +02:00
|
|
|
request_proto.set_solver_time_limit_seconds(solver.time_limit_in_secs());
|
|
|
|
|
request_proto.set_solver_specific_parameters(
|
2016-10-05 14:08:27 +02:00
|
|
|
solver.GetSolverSpecificParametersAsString());
|
2021-10-07 17:32:30 +02:00
|
|
|
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_request), request_proto,
|
2020-10-21 00:21:54 +02:00
|
|
|
write_format, absl::GetFlag(FLAGS_dump_gzip)));
|
2016-10-05 14:08:27 +02:00
|
|
|
}
|
|
|
|
|
|
2016-03-18 12:16:04 +01:00
|
|
|
const bool has_solution =
|
|
|
|
|
solve_status == MPSolver::OPTIMAL || solve_status == MPSolver::FEASIBLE;
|
|
|
|
|
|
2021-10-07 17:32:30 +02:00
|
|
|
if (!absl::GetFlag(FLAGS_sol_file).empty() && has_solution) {
|
|
|
|
|
operations_research::MPSolutionResponse response;
|
|
|
|
|
solver.FillSolutionResponseProto(&response);
|
|
|
|
|
std::string sol_string;
|
|
|
|
|
absl::StrAppend(&sol_string, "=obj= ", response.objective_value(), "\n");
|
|
|
|
|
for (int i = 0; i < response.variable_value().size(); ++i) {
|
|
|
|
|
absl::StrAppend(&sol_string, request_proto.model().variable(i).name(),
|
|
|
|
|
" ", response.variable_value(i), "\n");
|
|
|
|
|
}
|
|
|
|
|
LOG(INFO) << "Writing .sol solution to '" << absl::GetFlag(FLAGS_sol_file)
|
|
|
|
|
<< "'.\n";
|
|
|
|
|
CHECK_OK(file::SetContents(absl::GetFlag(FLAGS_sol_file), sol_string,
|
|
|
|
|
file::Defaults()));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// If requested, get the MPSolutionResponse and save it to file.
|
2020-10-21 00:21:54 +02:00
|
|
|
if (!absl::GetFlag(FLAGS_dump_response).empty() && has_solution) {
|
2016-10-05 14:08:27 +02:00
|
|
|
operations_research::MPSolutionResponse response;
|
|
|
|
|
solver.FillSolutionResponseProto(&response);
|
2020-10-21 00:21:54 +02:00
|
|
|
CHECK(WriteProtoToFile(absl::GetFlag(FLAGS_dump_response), response,
|
|
|
|
|
write_format, absl::GetFlag(FLAGS_dump_gzip)));
|
2015-07-31 16:16:16 +02:00
|
|
|
}
|
2020-10-21 00:21:54 +02:00
|
|
|
if (!absl::GetFlag(FLAGS_output_csv).empty() && has_solution) {
|
2015-07-31 16:16:16 +02:00
|
|
|
operations_research::MPSolutionResponse result;
|
|
|
|
|
solver.FillSolutionResponseProto(&result);
|
|
|
|
|
std::string csv_file;
|
|
|
|
|
for (int i = 0; i < result.variable_value_size(); ++i) {
|
2021-10-07 17:32:30 +02:00
|
|
|
csv_file +=
|
|
|
|
|
absl::StrFormat("%s,%e\n", request_proto.model().variable(i).name(),
|
|
|
|
|
result.variable_value(i));
|
2015-07-31 16:16:16 +02:00
|
|
|
}
|
2020-10-21 00:21:54 +02:00
|
|
|
CHECK_OK(file::SetContents(absl::GetFlag(FLAGS_output_csv), csv_file,
|
|
|
|
|
file::Defaults()));
|
2015-07-31 16:16:16 +02:00
|
|
|
}
|
2017-07-20 11:30:17 -07:00
|
|
|
// If --verify_solution is true, we already verified it. If not, we add
|
|
|
|
|
// a verification step here.
|
2020-10-21 00:21:54 +02:00
|
|
|
if (has_solution && !absl::GetFlag(FLAGS_verify_solution)) {
|
2017-07-20 11:30:17 -07:00
|
|
|
LOG(INFO) << "Verifying the solution";
|
2020-10-22 23:36:58 +02:00
|
|
|
solver.VerifySolution(/*tolerance=*/param.GetDoubleParam(
|
2017-07-20 11:30:17 -07:00
|
|
|
MPSolverParameters::PRIMAL_TOLERANCE),
|
2020-10-22 23:36:58 +02:00
|
|
|
/*log_errors=*/true);
|
2017-07-20 11:30:17 -07:00
|
|
|
}
|
2015-07-31 16:16:16 +02:00
|
|
|
|
2019-11-22 15:17:10 +01:00
|
|
|
absl::PrintF("%-12s: %s\n", "Status",
|
|
|
|
|
MPSolverResponseStatus_Name(
|
2020-10-22 23:36:58 +02:00
|
|
|
static_cast<MPSolverResponseStatus>(solve_status))
|
|
|
|
|
.c_str());
|
2019-11-22 15:17:10 +01:00
|
|
|
absl::PrintF("%-12s: %15.15e\n", "Objective",
|
|
|
|
|
has_solution ? solver.Objective().Value() : 0.0);
|
|
|
|
|
absl::PrintF("%-12s: %15.15e\n", "BestBound",
|
|
|
|
|
has_solution ? solver.Objective().BestBound() : 0.0);
|
2018-10-31 16:18:18 +01:00
|
|
|
absl::PrintF("%-12s: %d\n", "Iterations", solver.iterations());
|
2017-07-20 11:30:17 -07:00
|
|
|
// NOTE(user): nodes() for non-MIP solvers crashes in debug mode by design.
|
|
|
|
|
if (solver.IsMIP()) {
|
2018-10-31 16:18:18 +01:00
|
|
|
absl::PrintF("%-12s: %d\n", "Nodes", solver.nodes());
|
2017-07-20 11:30:17 -07:00
|
|
|
}
|
2019-11-22 15:17:10 +01:00
|
|
|
absl::PrintF("%-12s: %-6.4g\n", "Time", absl::ToDoubleSeconds(solving_time));
|
2019-04-18 19:24:37 +02:00
|
|
|
return true;
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|
2019-04-18 19:24:37 +02:00
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
|
|
|
|
} // namespace operations_research
|
2014-07-08 09:27:02 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
int main(int argc, char** argv) {
|
2021-10-20 12:04:21 +02:00
|
|
|
absl::SetFlag(&FLAGS_logtostderr, true);
|
2021-10-07 17:32:30 +02:00
|
|
|
google::InitGoogleLogging(kUsageStr);
|
2020-10-23 11:50:14 +02:00
|
|
|
absl::ParseCommandLine(argc, argv);
|
2021-10-07 17:32:30 +02:00
|
|
|
QCHECK(!absl::GetFlag(FLAGS_input).empty()) << "--input is required";
|
2021-10-13 16:39:39 +02:00
|
|
|
QCHECK_GE(absl::GetFlag(FLAGS_time_limit), absl::ZeroDuration())
|
|
|
|
|
<< "--time_limit must be given a positive duration";
|
2018-10-31 16:18:18 +01:00
|
|
|
|
2021-10-12 10:09:18 +02:00
|
|
|
operations_research::MPSolver::OptimizationProblemType type;
|
|
|
|
|
CHECK(operations_research::MPSolver::ParseSolverType(
|
|
|
|
|
absl::GetFlag(FLAGS_solver), &type))
|
|
|
|
|
<< "Unsupported --solver: " << absl::GetFlag(FLAGS_solver);
|
|
|
|
|
|
|
|
|
|
if (!operations_research::Run(type)) {
|
2021-10-07 17:32:30 +02:00
|
|
|
// If the solver is SAT and we encountered an error, display it in a format
|
|
|
|
|
// interpretable by our scripts.
|
|
|
|
|
if (type == operations_research::MPSolver::SAT_INTEGER_PROGRAMMING) {
|
|
|
|
|
operations_research::sat::CpSolverResponse response;
|
|
|
|
|
response.set_status(
|
|
|
|
|
operations_research::sat::CpSolverStatus::MODEL_INVALID);
|
|
|
|
|
LOG(INFO) << operations_research::sat::CpSolverResponseStats(response);
|
|
|
|
|
}
|
|
|
|
|
return EXIT_FAILURE;
|
|
|
|
|
}
|
2018-10-31 16:18:18 +01:00
|
|
|
return EXIT_SUCCESS;
|
2014-07-08 09:27:02 +00:00
|
|
|
}
|