add glop_proto_solver; split bazel targets for proto_solver
This commit is contained in:
@@ -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",
|
||||
],
|
||||
)
|
||||
221
ortools/linear_solver/proto_solver/glop_proto_solver.cc
Normal file
221
ortools/linear_solver/proto_solver/glop_proto_solver.cc
Normal 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(), ¶ms)) {
|
||||
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
|
||||
55
ortools/linear_solver/proto_solver/glop_proto_solver.h
Normal file
55
ortools/linear_solver/proto_solver/glop_proto_solver.h
Normal 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_
|
||||
67
ortools/linear_solver/proto_solver/proto_utils.h
Normal file
67
ortools/linear_solver/proto_solver/proto_utils.h
Normal 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_
|
||||
@@ -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(), ¶ms)) {
|
||||
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(), ¶ms)) {
|
||||
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
|
||||
|
||||
@@ -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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user