Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
glop_proto_solver.cc
Go to the documentation of this file.
1// Copyright 2010-2024 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <atomic>
17#include <cstdlib>
18#include <functional>
19#include <memory>
20#include <optional>
21#include <string>
22#include <type_traits>
23#include <utility>
24
25#include "absl/log/check.h"
26#include "absl/strings/str_cat.h"
30#include "ortools/linear_solver/linear_solver.pb.h"
40
41namespace operations_research {
42
43namespace {
44
45MPSolutionResponse ModelInvalidResponse(SolverLogger& logger,
46 std::string message) {
47 SOLVER_LOG(&logger, "Invalid model in glop_solve_proto.\n", message);
48
49 MPSolutionResponse response;
50 response.set_status(MPSolverResponseStatus::MPSOLVER_MODEL_INVALID);
51 response.set_status_str(message);
52 return response;
53}
54
55MPSolutionResponse ModelInvalidParametersResponse(SolverLogger& logger,
56 std::string message) {
57 SOLVER_LOG(&logger, "Invalid parameters in glop_solve_proto.\n", message);
58
59 MPSolutionResponse response;
60 response.set_status(
61 MPSolverResponseStatus::MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
62 response.set_status_str(message);
63 return response;
64}
65
66MPSolverResponseStatus ToMPSolverResultStatus(glop::ProblemStatus s) {
67 switch (s) {
69 return MPSOLVER_OPTIMAL;
71 return MPSOLVER_FEASIBLE;
72
73 // Note(user): MPSolver does not have the equivalent of
74 // INFEASIBLE_OR_UNBOUNDED however UNBOUNDED is almost never relevant in
75 // applications, so we decided to report this status as INFEASIBLE since
76 // it should almost always be the case. Historically, we where reporting
77 // ABNORMAL, but that was more confusing than helpful.
78 //
79 // TODO(user): We could argue that it is infeasible to find the optimal of
80 // an unbounded problem. So it might just be simpler to completely get rid
81 // of the MpSolver::UNBOUNDED status that seems to never be used
82 // programmatically.
83 case glop::ProblemStatus::INFEASIBLE_OR_UNBOUNDED: // PASS_THROUGH_INTENDED
84 case glop::ProblemStatus::PRIMAL_INFEASIBLE: // PASS_THROUGH_INTENDED
86 return MPSOLVER_INFEASIBLE;
87
88 case glop::ProblemStatus::DUAL_INFEASIBLE: // PASS_THROUGH_INTENDED
90 return MPSOLVER_UNBOUNDED;
91
92 case glop::ProblemStatus::DUAL_FEASIBLE: // PASS_THROUGH_INTENDED
94 return MPSOLVER_NOT_SOLVED;
95
96 case glop::ProblemStatus::ABNORMAL: // PASS_THROUGH_INTENDED
97 case glop::ProblemStatus::IMPRECISE: // PASS_THROUGH_INTENDED
99 return MPSOLVER_ABNORMAL;
100 }
101 LOG(DFATAL) << "Invalid glop::ProblemStatus " << s;
102 return MPSOLVER_ABNORMAL;
103}
104
105} // namespace
106
107MPSolutionResponse GlopSolveProto(
108 LazyMutableCopy<MPModelRequest> request, std::atomic<bool>* interrupt_solve,
109 std::function<void(const std::string&)> logging_callback) {
110 glop::GlopParameters params;
111 params.set_log_search_progress(request->enable_internal_solver_output());
112
113 // TODO(user): We do not support all the parameters here. In particular the
114 // logs before the solver is called will not be appended to the response. Fix
115 // that, and remove code duplication for the logger config. One way should be
116 // to not touch/configure anything if the logger is already created while
117 // calling SolveCpModel() and call a common config function from here or from
118 // inside Solve()?
119 SolverLogger logger;
120 if (logging_callback != nullptr) {
121 logger.AddInfoLoggingCallback(logging_callback);
122 }
123 logger.EnableLogging(params.log_search_progress());
124 logger.SetLogToStdOut(params.log_to_stdout());
125
126 // Set it now so that it can be overwritten by the solver specific parameters.
127 if (request->has_solver_specific_parameters()) {
128 // See EncodeParametersAsString() documentation.
129 if (!std::is_base_of<Message, glop::GlopParameters>::value) {
130 if (!params.MergeFromString(request->solver_specific_parameters())) {
131 return ModelInvalidParametersResponse(
132 logger,
133 "solver_specific_parameters is not a valid binary stream of the "
134 "GLOPParameters proto");
135 }
136 } else {
138 request->solver_specific_parameters(), &params)) {
139 return ModelInvalidParametersResponse(
140 logger,
141 "solver_specific_parameters is not a valid textual representation "
142 "of the GlopParameters proto");
143 }
144 }
145 }
146 if (request->has_solver_time_limit_seconds()) {
147 params.set_max_time_in_seconds(request->solver_time_limit_seconds());
148 }
149
150 {
151 const std::string error = glop::ValidateParameters(params);
152 if (!error.empty()) {
153 return ModelInvalidParametersResponse(
154 logger, absl::StrCat("Invalid Glop parameters: ", error));
155 }
156 }
157
158 MPSolutionResponse response;
159 glop::LinearProgram linear_program;
160
161 // Model validation and delta handling.
162 {
163 std::optional<LazyMutableCopy<MPModelProto>> optional_model =
164 GetMPModelOrPopulateResponse(request, &response);
165 if (!optional_model) return response;
166
167 const MPModelProto& mp_model = **optional_model;
168 if (!mp_model.general_constraint().empty()) {
169 return ModelInvalidResponse(logger,
170 "GLOP does not support general constraints");
171 }
172
173 // Convert and clear the request and mp_model as it is no longer needed.
174 MPModelProtoToLinearProgram(mp_model, &linear_program);
175 std::move(request).dispose();
176 }
177
178 glop::LPSolver lp_solver;
179 lp_solver.SetParameters(params);
180
181 // TimeLimit and interrupt solve.
182 std::unique_ptr<TimeLimit> time_limit =
184 if (interrupt_solve != nullptr) {
185 if (interrupt_solve->load()) {
186 response.set_status(MPSOLVER_CANCELLED_BY_USER);
187 response.set_status_str(
188 "Solve not started, because the user set the atomic<bool> in "
189 "MPSolver::SolveWithProto() to true before solving could "
190 "start.");
191 return response;
192 } else {
193 time_limit->RegisterExternalBooleanAsLimit(interrupt_solve);
194 }
195 }
196
197 // Solve and set response status.
199 lp_solver.SolveWithTimeLimit(linear_program, time_limit.get());
200 const MPSolverResponseStatus result_status = ToMPSolverResultStatus(status);
201 response.set_status(result_status);
202
203 // Fill in solution.
204 if (result_status == MPSOLVER_OPTIMAL || result_status == MPSOLVER_FEASIBLE) {
205 response.set_objective_value(lp_solver.GetObjectiveValue());
206
207 const int num_vars = linear_program.num_variables().value();
208 for (int var_id = 0; var_id < num_vars; ++var_id) {
209 const glop::Fractional solution_value =
210 lp_solver.variable_values()[glop::ColIndex(var_id)];
211 response.add_variable_value(solution_value);
212
213 const glop::Fractional reduced_cost =
214 lp_solver.reduced_costs()[glop::ColIndex(var_id)];
215 response.add_reduced_cost(reduced_cost);
216 }
217 }
218
219 if (result_status == MPSOLVER_UNKNOWN_STATUS && interrupt_solve != nullptr &&
220 interrupt_solve->load()) {
221 response.set_status(MPSOLVER_CANCELLED_BY_USER);
222 }
223
224 const int num_constraints = linear_program.num_constraints().value();
225 for (int ct_id = 0; ct_id < num_constraints; ++ct_id) {
226 const glop::Fractional dual_value =
227 lp_solver.dual_values()[glop::RowIndex(ct_id)];
228 response.add_dual_value(dual_value);
229 }
230
231 return response;
232}
233
235
236} // namespace operations_research
void EnableLogging(bool enable)
Definition logging.h:46
void SetLogToStdOut(bool enable)
Should all messages be displayed on stdout ?
Definition logging.h:52
void AddInfoLoggingCallback(std::function< void(const std::string &message)> callback)
Definition logging.cc:29
static std::unique_ptr< TimeLimit > FromParameters(const Parameters &parameters)
Definition time_limit.h:140
A full-fledged linear programming solver.
Definition lp_solver.h:31
const GlopParameters & GetParameters() const
Definition lp_solver.cc:138
static std::string GlopVersion()
Returns a string that describes the version of the solver.
Definition lp_solver.cc:122
const DenseRow & reduced_costs() const
Definition lp_solver.h:110
Fractional GetObjectiveValue() const
Returns the objective value of the solution with its offset and scaling.
Definition lp_solver.cc:527
const DenseRow & variable_values() const
Accessors to information related to variables.
Definition lp_solver.h:109
ABSL_MUST_USE_RESULT ProblemStatus SolveWithTimeLimit(const LinearProgram &lp, TimeLimit *time_limit)
Definition lp_solver.cc:150
const DenseColumn & dual_values() const
Definition lp_solver.h:121
void SetParameters(const GlopParameters &parameters)
Definition lp_solver.cc:126
ColIndex num_variables() const
Returns the number of variables.
Definition lp_data.h:213
RowIndex num_constraints() const
Returns the number of constraints.
Definition lp_data.h:216
absl::Status status
Definition g_gurobi.cc:44
time_limit
Definition solve.cc:22
std::string ValidateParameters(const GlopParameters &params)
ProblemStatus
Different statuses for a given problem.
Definition lp_types.h:107
@ ABNORMAL
An error occurred during the solving process.
@ INVALID_PROBLEM
The input problem was invalid (see LinearProgram.IsValid()).
@ INIT
The solver didn't had a chance to prove anything.
In SWIG mode, we don't want anything besides these top-level includes.
std::string GlopSolverVersion()
Returns a string that describes the version of the GLOP solver.
MPSolutionResponse GlopSolveProto(LazyMutableCopy< MPModelRequest > request, std::atomic< bool > *interrupt_solve, std::function< void(const std::string &)> logging_callback)
std::optional< LazyMutableCopy< MPModelProto > > GetMPModelOrPopulateResponse(LazyMutableCopy< MPModelRequest > &request, MPSolutionResponse *response)
bool ProtobufTextFormatMergeFromString(absl::string_view proto_text_string, ProtoType *proto)
Definition proto_utils.h:66
std::string message
Definition trace.cc:397
#define SOLVER_LOG(logger,...)
Definition logging.h:109