add glop_proto_solver; split bazel targets for proto_solver

This commit is contained in:
Laurent Perron
2023-11-17 14:17:52 +01:00
parent 803e3d95a0
commit 0ccc07f288
17 changed files with 651 additions and 172 deletions

View File

@@ -13,64 +13,159 @@
package(default_visibility = ["//visibility:public"])
# This works on a fixed set of solvers.
# By default SCIP, GUROBI, PDLP, and CP-SAT interface are included.
cc_library(
name = "proto_solver",
srcs = [
"gurobi_proto_solver.cc",
"highs_proto_solver.cc",
"pdlp_proto_solver.cc",
"sat_proto_solver.cc",
"sat_solver_utils.cc",
"scip_proto_solver.cc",
],
hdrs = [
"gurobi_proto_solver.h",
"highs_proto_solver.h",
"pdlp_proto_solver.h",
"sat_proto_solver.h",
"sat_solver_utils.h",
"scip_proto_solver.h",
],
copts = [
"-DUSE_PDLP",
"-DUSE_SCIP",
"-DUSE_HIGHS",
],
name = "proto_utils",
hdrs = ["proto_utils.h"],
visibility = ["//visibility:public"],
deps = [
"//ortools/base",
"//ortools/base:accurate_sum",
"//ortools/base:dynamic_library",
"//ortools/base:hash",
"//ortools/base:map_util",
"//ortools/base:status_macros",
"//ortools/base:stl_util",
"//ortools/base:timer",
"//ortools/bop:bop_parameters_cc_proto",
"//ortools/bop:integral_solver",
"//ortools/port:proto_utils",
"@com_google_absl//absl/log:check",
"@com_google_protobuf//:protobuf",
],
)
cc_library(
name = "glop_proto_solver",
srcs = ["glop_proto_solver.cc"],
hdrs = ["glop_proto_solver.h"],
deps = [
":proto_utils",
"//ortools/glop:lp_solver",
"//ortools/glop:parameters_cc_proto",
"//ortools/gscip:legacy_scip_params",
"//ortools/gurobi:environment",
"//ortools/glop:parameters_validation",
"//ortools/linear_solver:linear_solver_cc_proto",
"//ortools/linear_solver:model_exporter",
"//ortools/linear_solver:model_validator",
"//ortools/linear_solver:scip_with_glop",
"//ortools/lp_data",
"//ortools/lp_data:base",
"//ortools/lp_data:proto_utils",
"//ortools/port:proto_utils",
"//ortools/util:logging",
"//ortools/util:time_limit",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
],
)
cc_library(
name = "pdlp_proto_solver",
srcs = ["pdlp_proto_solver.cc"],
hdrs = ["pdlp_proto_solver.h"],
deps = [
"//ortools/base:logging",
"//ortools/linear_solver:linear_solver_cc_proto",
"//ortools/linear_solver:model_validator",
"//ortools/pdlp:iteration_stats",
"//ortools/pdlp:primal_dual_hybrid_gradient",
"//ortools/pdlp:quadratic_program",
"//ortools/pdlp:solve_log_cc_proto",
"//ortools/pdlp:solvers_cc_proto",
"//ortools/port:file",
"//ortools/port:proto_utils",
"//ortools/util:lazy_mutable_copy",
"@com_google_absl//absl/status:statusor",
],
)
cc_library(
name = "sat_solver_utils",
srcs = ["sat_solver_utils.cc"],
hdrs = ["sat_solver_utils.h"],
deps = [
"//ortools/glop:parameters_cc_proto",
"//ortools/glop:preprocessor",
"//ortools/linear_solver:linear_solver_cc_proto",
"//ortools/lp_data:proto_utils",
"//ortools/util:logging",
"@com_google_absl//absl/memory",
],
)
cc_library(
name = "sat_proto_solver",
srcs = ["sat_proto_solver.cc"],
hdrs = ["sat_proto_solver.h"],
deps = [
":proto_utils",
":sat_solver_utils",
"//ortools/glop:preprocessor",
"//ortools/linear_solver:linear_solver_cc_proto",
"//ortools/linear_solver:model_validator",
"//ortools/lp_data",
"//ortools/lp_data:base",
"//ortools/port:proto_utils",
"//ortools/sat:cp_model_cc_proto",
"//ortools/sat:cp_model_solver",
"//ortools/sat:lp_utils",
"//ortools/util:fp_utils",
"//ortools/sat:model",
"//ortools/sat:parameters_validation",
"//ortools/sat:sat_parameters_cc_proto",
"//ortools/util:logging",
"//ortools/util:time_limit",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/types:span",
],
)
cc_library(
name = "scip_proto_solver",
srcs = ["scip_proto_solver.cc"],
hdrs = ["scip_proto_solver.h"],
defines = ["USE_SCIP"],
deps = [
"//ortools/base",
"//ortools/base:cleanup",
"//ortools/base:timer",
"//ortools/gscip:legacy_scip_params",
"//ortools/linear_solver:linear_solver_cc_proto",
"//ortools/linear_solver:model_validator",
"//ortools/linear_solver:scip_helper_macros",
"//ortools/util:lazy_mutable_copy",
"@com_google_absl//absl/cleanup",
"@com_google_absl//absl/container:btree",
"@com_google_absl//absl/log",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/synchronization",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"@scip//:libscip",
],
)
cc_library(
name = "gurobi_proto_solver",
srcs = ["gurobi_proto_solver.cc"],
hdrs = ["gurobi_proto_solver.h"],
deps = [
"//ortools/base:cleanup",
"//ortools/base:timer",
"//ortools/gurobi:environment",
"//ortools/linear_solver:linear_solver_cc_proto",
"//ortools/linear_solver:model_validator",
"//ortools/util:lazy_mutable_copy",
"@com_google_absl//absl/base:core_headers",
"@com_google_absl//absl/cleanup",
"@com_google_absl//absl/log",
"@com_google_absl//absl/log:check",
"@com_google_absl//absl/status",
"@com_google_absl//absl/status:statusor",
"@com_google_absl//absl/strings",
"@com_google_absl//absl/strings:str_format",
"@com_google_absl//absl/time",
"@com_google_absl//absl/types:optional",
],
)
cc_library(
name = "highs_proto_solver",
srcs = ["highs_proto_solver.cc"],
hdrs = ["highs_proto_solver.h"],
deps = [
"@com_google_absl//absl/status:statusor",
"//ortools/linear_solver:linear_solver_cc_proto",
"//ortools/linear_solver:model_validator",
"//ortools/port:proto_utils",
],
)

View File

@@ -0,0 +1,221 @@
// Copyright 2010-2022 Google LLC
// 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.
#include "ortools/linear_solver/proto_solver/glop_proto_solver.h"
#include <atomic>
#include <cstdlib>
#include <functional>
#include <memory>
#include <string>
#include <type_traits>
#include "absl/log/check.h"
#include "absl/strings/str_cat.h"
#include "ortools/base/logging.h"
#include "ortools/glop/lp_solver.h"
#include "ortools/glop/parameters_validation.h"
#include "ortools/linear_solver/linear_solver.pb.h"
#include "ortools/linear_solver/model_validator.h"
#include "ortools/linear_solver/proto_solver/proto_utils.h"
#include "ortools/lp_data/lp_data.h"
#include "ortools/lp_data/lp_types.h"
#include "ortools/lp_data/proto_utils.h"
#include "ortools/port/proto_utils.h"
#include "ortools/util/logging.h"
#include "ortools/util/time_limit.h"
namespace operations_research {
namespace {
MPSolutionResponse ModelInvalidResponse(SolverLogger& logger,
std::string message) {
SOLVER_LOG(&logger, "Invalid model/parameters in glop_solve_proto.\n",
message);
MPSolutionResponse response;
response.set_status(MPSolverResponseStatus::MPSOLVER_MODEL_INVALID);
response.set_status_str(message);
return response;
}
MPSolverResponseStatus ToMPSolverResultStatus(glop::ProblemStatus s) {
switch (s) {
case glop::ProblemStatus::OPTIMAL:
return MPSOLVER_OPTIMAL;
case glop::ProblemStatus::PRIMAL_FEASIBLE:
return MPSOLVER_FEASIBLE;
// Note(user): MPSolver does not have the equivalent of
// INFEASIBLE_OR_UNBOUNDED however UNBOUNDED is almost never relevant in
// applications, so we decided to report this status as INFEASIBLE since
// it should almost always be the case. Historically, we where reporting
// ABNORMAL, but that was more confusing than helpful.
//
// TODO(user): We could argue that it is infeasible to find the optimal of
// an unbounded problem. So it might just be simpler to completely get rid
// of the MpSolver::UNBOUNDED status that seems to never be used
// programmatically.
case glop::ProblemStatus::INFEASIBLE_OR_UNBOUNDED: // PASS_THROUGH_INTENDED
case glop::ProblemStatus::PRIMAL_INFEASIBLE: // PASS_THROUGH_INTENDED
case glop::ProblemStatus::DUAL_UNBOUNDED:
return MPSOLVER_INFEASIBLE;
case glop::ProblemStatus::DUAL_INFEASIBLE: // PASS_THROUGH_INTENDED
case glop::ProblemStatus::PRIMAL_UNBOUNDED:
return MPSOLVER_UNBOUNDED;
case glop::ProblemStatus::DUAL_FEASIBLE: // PASS_THROUGH_INTENDED
case glop::ProblemStatus::INIT:
return MPSOLVER_NOT_SOLVED;
case glop::ProblemStatus::ABNORMAL: // PASS_THROUGH_INTENDED
case glop::ProblemStatus::IMPRECISE: // PASS_THROUGH_INTENDED
case glop::ProblemStatus::INVALID_PROBLEM:
return MPSOLVER_ABNORMAL;
}
LOG(DFATAL) << "Invalid glop::ProblemStatus " << s;
return MPSOLVER_ABNORMAL;
}
} // namespace
MPSolutionResponse GlopSolveProto(
MPModelRequest request, std::atomic<bool>* interrupt_solve,
std::function<void(const std::string&)> logging_callback) {
glop::GlopParameters params;
params.set_log_search_progress(request.enable_internal_solver_output());
// TODO(user): We do not support all the parameters here. In particular the
// logs before the solver is called will not be appended to the response. Fix
// that, and remove code duplication for the logger config. One way should be
// to not touch/configure anything if the logger is already created while
// calling SolveCpModel() and call a common config function from here or from
// inside Solve()?
SolverLogger logger;
if (logging_callback != nullptr) {
logger.AddInfoLoggingCallback(logging_callback);
}
logger.EnableLogging(params.log_search_progress());
logger.SetLogToStdOut(params.log_to_stdout());
// Set it now so that it can be overwritten by the solver specific parameters.
if (request.has_solver_specific_parameters()) {
// See EncodeParametersAsString() documentation.
if (!std::is_base_of<Message, glop::GlopParameters>::value) {
if (!params.MergeFromString(request.solver_specific_parameters())) {
return ModelInvalidResponse(
logger,
"solver_specific_parameters is not a valid binary stream of the "
"GLOPParameters proto");
}
} else {
if (!ProtobufTextFormatMergeFromString(
request.solver_specific_parameters(), &params)) {
return ModelInvalidResponse(
logger,
"solver_specific_parameters is not a valid textual representation "
"of the GlopParameters proto");
}
}
}
if (request.has_solver_time_limit_seconds()) {
params.set_max_time_in_seconds(request.solver_time_limit_seconds());
}
if (!request.model().general_constraint().empty()) {
return ModelInvalidResponse(logger,
"GLOP does not support general constraints");
}
// Model validation and delta handling.
MPSolutionResponse response;
if (!ExtractValidMPModelInPlaceOrPopulateResponseStatus(&request,
&response)) {
// Note that the ExtractValidMPModelInPlaceOrPopulateResponseStatus() can
// also close trivial model (empty or trivially infeasible). So this is not
// always the MODEL_INVALID status.
return response;
}
{
const std::string error = glop::ValidateParameters(params);
if (!error.empty()) {
return ModelInvalidResponse(
logger, absl::StrCat("Invalid Glop parameters: ", error));
}
}
glop::LinearProgram linear_program;
MPModelProtoToLinearProgram(request.model(), &linear_program);
glop::LPSolver lp_solver;
lp_solver.SetParameters(params);
// TimeLimit and interrupt solve.
std::unique_ptr<TimeLimit> time_limit =
TimeLimit::FromParameters(lp_solver.GetParameters());
if (interrupt_solve != nullptr) {
if (interrupt_solve->load()) {
response.set_status(MPSOLVER_CANCELLED_BY_USER);
response.set_status_str(
"Solve not started, because the user set the atomic<bool> in "
"MPSolver::SolveWithProto() to true before solving could "
"start.");
return response;
} else {
time_limit->RegisterExternalBooleanAsLimit(interrupt_solve);
}
}
// Solve and set response status.
const glop::ProblemStatus status =
lp_solver.SolveWithTimeLimit(linear_program, time_limit.get());
const MPSolverResponseStatus result_status = ToMPSolverResultStatus(status);
response.set_status(result_status);
// Fill in solution.
if (result_status == MPSOLVER_OPTIMAL || result_status == MPSOLVER_FEASIBLE) {
response.set_objective_value(lp_solver.GetObjectiveValue());
const int num_vars = request.model().variable_size();
for (int var_id = 0; var_id < num_vars; ++var_id) {
const glop::Fractional solution_value =
lp_solver.variable_values()[glop::ColIndex(var_id)];
response.add_variable_value(solution_value);
const glop::Fractional reduced_cost =
lp_solver.reduced_costs()[glop::ColIndex(var_id)];
response.add_reduced_cost(reduced_cost);
}
}
if (result_status == MPSOLVER_UNKNOWN_STATUS && interrupt_solve != nullptr &&
interrupt_solve->load()) {
response.set_status(MPSOLVER_CANCELLED_BY_USER);
}
const size_t num_constraints = request.model().constraint_size();
for (int ct_id = 0; ct_id < num_constraints; ++ct_id) {
const glop::Fractional dual_value =
lp_solver.dual_values()[glop::RowIndex(ct_id)];
response.add_dual_value(dual_value);
}
return response;
}
std::string GlopSolverVersion() { return glop::LPSolver::GlopVersion(); }
} // namespace operations_research

View File

@@ -0,0 +1,55 @@
// Copyright 2010-2022 Google LLC
// 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.
#ifndef OR_TOOLS_LINEAR_SOLVER_PROTO_SOLVER_GLOP_PROTO_SOLVER_H_
#define OR_TOOLS_LINEAR_SOLVER_PROTO_SOLVER_GLOP_PROTO_SOLVER_H_
#include <atomic>
#include <functional>
#include <string>
#include "ortools/glop/parameters.pb.h"
#include "ortools/linear_solver/linear_solver.pb.h"
namespace operations_research {
// Solve the input LP model with the GLOP solver.
//
// If possible, std::move the request into this function call to avoid a copy.
//
// If you need to change the solver parameters, please use the
// EncodeParametersAsString() function to set the solver_specific_parameters
// field.
//
// The optional interrupt_solve can be used to interrupt the solve early. It
// must only be set to true, never reset to false. It is also used internally by
// the solver that will set it to true for its own internal logic. As a
// consequence the caller should ignore the stored value and should not use the
// same atomic for different concurrent calls.
//
// The optional logging_callback will be called when the GLOP parameter
// log_search_progress is set to true. Passing a callback will disable the
// default logging to INFO. Note though that by default the GLOP parameter
// log_to_stdout is true so even with a callback, the logs will appear on stdout
// too unless log_to_stdout is set to false. The enable_internal_solver_output
// in the request will act as the GLOP parameter log_search_progress.
MPSolutionResponse GlopSolveProto(
MPModelRequest request, std::atomic<bool>* interrupt_solve = nullptr,
std::function<void(const std::string&)> logging_callback = nullptr);
// Returns a string that describes the version of the GLOP solver.
std::string GlopSolverVersion();
} // namespace operations_research
#endif // OR_TOOLS_LINEAR_SOLVER_PROTO_SOLVER_GLOP_PROTO_SOLVER_H_

View File

@@ -0,0 +1,67 @@
// Copyright 2010-2022 Google LLC
// 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.
#ifndef OR_TOOLS_LINEAR_SOLVER_PROTO_SOLVER_PROTO_UTILS_H_
#define OR_TOOLS_LINEAR_SOLVER_PROTO_SOLVER_PROTO_UTILS_H_
#include <string>
#include <type_traits>
#include "absl/log/check.h"
#include "google/protobuf/message.h"
#include "ortools/port/proto_utils.h"
namespace operations_research {
#if defined(PROTOBUF_INTERNAL_IMPL)
using google::protobuf::Message;
#else
using google::protobuf::Message;
#endif
// Returns a string that should be used in MPModelRequest's
// solver_specific_parameters field to encode the glop parameters.
//
// The returned string's content depends on the version of the proto library
// that is linked in the binary.
//
// By default it will contain the textual representation of the input proto.
// But when the proto-lite is used, it will contain the binary stream of the
// proto instead since it is not possible to build the textual representation in
// that case.
//
// This function will test if the proto-lite is used and expect a binary stream
// when it is the case. So in order for your code to be portable, you should
// always use this function to set the specific parameters.
//
// Proto-lite disables some features of protobufs and messages inherit from
// MessageLite directly instead of inheriting from Message (which is itself a
// specialization of MessageLite).
// See https://protobuf.dev/reference/cpp/cpp-generated/#message for details.
template <typename P>
std::string EncodeParametersAsString(const P& parameters) {
if constexpr (!std::is_base_of<Message, P>::value) {
// Here we use SerializeToString() instead of SerializeAsString() since the
// later ignores errors and returns an empty string instead (which can be a
// valid value when no fields are set).
std::string bytes;
CHECK(parameters.SerializeToString(&bytes));
return bytes;
}
return ProtobufShortDebugString(parameters);
}
} // namespace operations_research
#endif // OR_TOOLS_LINEAR_SOLVER_PROTO_SOLVER_PROTO_UTILS_H_

View File

@@ -27,14 +27,13 @@
#include <vector>
#include "absl/log/check.h"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "absl/types/span.h"
#include "ortools/base/logging.h"
#include "ortools/glop/preprocessor.h"
#include "ortools/linear_solver/linear_solver.pb.h"
#include "ortools/linear_solver/model_validator.h"
#include "ortools/linear_solver/proto_solver/proto_utils.h"
#include "ortools/linear_solver/proto_solver/sat_solver_utils.h"
#include "ortools/lp_data/lp_data.h"
#include "ortools/lp_data/lp_types.h"
@@ -52,19 +51,6 @@ namespace operations_research {
namespace {
#if defined(PROTOBUF_INTERNAL_IMPL)
using google::protobuf::Message;
#else
using google::protobuf::Message;
#endif
// Proto-lite disables some features of protos and messages inherit from
// MessageLite directly instead of inheriting from Message (which is itself a
// specialization of MessageLite).
// See https://protobuf.dev/reference/cpp/cpp-generated/#message for details.
constexpr bool kProtoLiteSatParameters =
!std::is_base_of<Message, sat::SatParameters>::value;
MPSolverResponseStatus ToMPSolverResponseStatus(sat::CpSolverStatus status,
bool has_objective) {
switch (status) {
@@ -116,10 +102,9 @@ MPSolutionResponse InfeasibleResponse(SolverLogger& logger,
return response;
}
MPSolutionResponse ModelInvalidResponse(SolverLogger& logger,
MPSolutionResponse InvalidModelResponse(SolverLogger& logger,
std::string message) {
SOLVER_LOG(&logger, "Invalid model/parameters in sat_solve_proto.\n",
message);
SOLVER_LOG(&logger, "Invalid model in sat_solve_proto.\n", message);
// This is needed for our benchmark scripts.
if (logger.LoggingIsEnabled()) {
@@ -134,35 +119,32 @@ MPSolutionResponse ModelInvalidResponse(SolverLogger& logger,
return response;
}
MPSolutionResponse InvalidParametersResponse(SolverLogger& logger,
std::string message) {
SOLVER_LOG(&logger, "Invalid parameters in sat_solve_proto.\n", message);
// This is needed for our benchmark scripts.
if (logger.LoggingIsEnabled()) {
sat::CpSolverResponse cp_response;
cp_response.set_status(sat::CpSolverStatus::MODEL_INVALID);
SOLVER_LOG(&logger, CpSolverResponseStats(cp_response));
}
MPSolutionResponse response;
response.set_status(
MPSolverResponseStatus::MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
response.set_status_str(message);
return response;
}
} // namespace
absl::StatusOr<MPSolutionResponse> SatSolveProto(
MPSolutionResponse SatSolveProto(
MPModelRequest request, std::atomic<bool>* interrupt_solve,
std::function<void(const std::string&)> logging_callback,
std::function<void(const MPSolution&)> solution_callback) {
sat::SatParameters params;
params.set_log_search_progress(request.enable_internal_solver_output());
// Set it now so that it can be overwritten by the solver specific parameters.
if (request.has_solver_specific_parameters()) {
// See EncodeSatParametersAsString() documentation.
if (kProtoLiteSatParameters) {
if (!params.MergeFromString(request.solver_specific_parameters())) {
return absl::InvalidArgumentError(
"solver_specific_parameters is not a valid binary stream of the "
"SatParameters proto");
}
} else {
if (!ProtobufTextFormatMergeFromString(
request.solver_specific_parameters(), &params)) {
return absl::InvalidArgumentError(
"solver_specific_parameters is not a valid textual representation "
"of the SatParameters proto");
}
}
}
if (request.has_solver_time_limit_seconds()) {
params.set_max_time_in_seconds(request.solver_time_limit_seconds());
}
// TODO(user): We do not support all the parameters here. In particular the
// logs before the solver is called will not be appended to the response. Fix
@@ -177,6 +159,46 @@ absl::StatusOr<MPSolutionResponse> SatSolveProto(
logger.EnableLogging(params.log_search_progress());
logger.SetLogToStdOut(params.log_to_stdout());
// Set it now so that it can be overwritten by the solver specific parameters.
if (request.has_solver_specific_parameters()) {
// See EncodeSatParametersAsString() documentation.
if constexpr (!std::is_base_of<Message, sat::SatParameters>::value) {
if (!params.MergeFromString(request.solver_specific_parameters())) {
return InvalidParametersResponse(
logger,
"solver_specific_parameters is not a valid binary stream of the "
"SatParameters proto");
}
} else {
if (!ProtobufTextFormatMergeFromString(
request.solver_specific_parameters(), &params)) {
return InvalidParametersResponse(
logger,
"solver_specific_parameters is not a valid textual representation "
"of the SatParameters proto");
}
}
}
// Validate parameters.
{
const std::string error = sat::ValidateParameters(params);
if (!error.empty()) {
return InvalidParametersResponse(
logger, absl::StrCat("Invalid CP-SAT parameters: ", error));
}
}
// Reconfigure the logger in case the solver_specific_parameters overwrite its
// configuration. Note that the invalid parameter message will be logged
// before that though according to request.enable_internal_solver_output().
logger.EnableLogging(params.log_search_progress());
logger.SetLogToStdOut(params.log_to_stdout());
if (request.has_solver_time_limit_seconds()) {
params.set_max_time_in_seconds(request.solver_time_limit_seconds());
}
// Model validation and delta handling.
MPSolutionResponse response;
if (!ExtractValidMPModelInPlaceOrPopulateResponseStatus(&request,
@@ -200,15 +222,7 @@ absl::StatusOr<MPSolutionResponse> SatSolveProto(
MPModelProto* const mp_model = request.mutable_model();
if (!sat::MPModelProtoValidationBeforeConversion(params, *mp_model,
&logger)) {
return ModelInvalidResponse(logger, "Extra CP-SAT validation failed.");
}
{
const std::string error = sat::ValidateParameters(params);
if (!error.empty()) {
return ModelInvalidResponse(
logger, absl::StrCat("Invalid CP-SAT parameters: ", error));
}
return InvalidModelResponse(logger, "Extra CP-SAT validation failed.");
}
// This is good to do before any presolve.
@@ -236,7 +250,7 @@ absl::StatusOr<MPSolutionResponse> SatSolveProto(
return InfeasibleResponse(
logger, "Problem proven infeasible during MIP presolve");
case glop::ProblemStatus::INVALID_PROBLEM:
return ModelInvalidResponse(
return InvalidModelResponse(
logger, "Problem detected invalid during MIP presolve");
default:
// TODO(user): We put the INFEASIBLE_OR_UNBOUNBED case here since there
@@ -293,7 +307,7 @@ absl::StatusOr<MPSolutionResponse> SatSolveProto(
}
}
if (!all_integer) {
return ModelInvalidResponse(
return InvalidModelResponse(
logger,
"The model contains non-integer variables but the parameter "
"'only_solve_ip' was set. Change this parameter if you "
@@ -305,7 +319,7 @@ absl::StatusOr<MPSolutionResponse> SatSolveProto(
sat::CpModelProto cp_model;
if (!ConvertMPModelProtoToCpModelProto(params, *mp_model, &cp_model,
&logger)) {
return ModelInvalidResponse(logger,
return InvalidModelResponse(logger,
"Failed to convert model into CP-SAT model");
}
DCHECK_EQ(cp_model.variables().size(), var_scaling.size());
@@ -433,19 +447,6 @@ absl::StatusOr<MPSolutionResponse> SatSolveProto(
return response;
}
std::string EncodeSatParametersAsString(const sat::SatParameters& parameters) {
if (kProtoLiteSatParameters) {
// Here we use SerializeToString() instead of SerializeAsString() since the
// later ignores errors and returns an empty string instead (which can be a
// valid value when no fields are set).
std::string bytes;
CHECK(parameters.SerializeToString(&bytes));
return bytes;
}
return ProtobufShortDebugString(parameters);
}
std::string SatSolverVersion() { return sat::CpSatSolverVersion(); }
} // namespace operations_research

View File

@@ -18,7 +18,6 @@
#include <functional>
#include <string>
#include "absl/status/statusor.h"
#include "ortools/linear_solver/linear_solver.pb.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/util/logging.h"
@@ -50,27 +49,11 @@ namespace operations_research {
// found by the solver. The solver may call solution_callback from multiple
// threads, but it will ensure that at most one thread executes
// solution_callback at a time.
absl::StatusOr<MPSolutionResponse> SatSolveProto(
MPSolutionResponse SatSolveProto(
MPModelRequest request, std::atomic<bool>* interrupt_solve = nullptr,
std::function<void(const std::string&)> logging_callback = nullptr,
std::function<void(const MPSolution&)> solution_callback = nullptr);
// Returns a string that should be used in MPModelRequest's
// solver_specific_parameters field to encode the SAT parameters.
//
// The returned string's content depends on the version of the proto library
// that is linked in the binary.
//
// By default it will contain the textual representation of the input proto.
// But when the proto-lite is used, it will contain the binary stream of the
// proto instead since it is not possible to build the textual representation in
// that case.
//
// The SatSolveProto() function will test if the proto-lite is used and expect a
// binary stream when it is the case. So in order for your code to be portable,
// you should always use this function to set the specific parameters.
std::string EncodeSatParametersAsString(const sat::SatParameters& parameters);
// Returns a string that describes the version of the CP-SAT solver.
std::string SatSolverVersion();