Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
pdlp_interface.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
14#if defined(USE_PDLP)
15
16#include <atomic>
17#include <cstdint>
18#include <optional>
19#include <string>
20#include <utility>
21#include <vector>
22
23#include "absl/base/attributes.h"
24#include "absl/status/status.h"
25#include "absl/status/statusor.h"
26#include "absl/strings/str_cat.h"
27#include "absl/types/optional.h"
30#include "ortools/linear_solver/linear_solver.pb.h"
33#include "ortools/pdlp/solve_log.pb.h"
34#include "ortools/pdlp/solvers.pb.h"
37
38namespace operations_research {
39
41 public:
42 explicit PdlpInterface(MPSolver* solver);
43 ~PdlpInterface() override;
44
45 // ----- Solve -----
46 MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
47
48 bool SupportsDirectlySolveProto(std::atomic<bool>* interrupt) const override {
49 return true;
50 }
52 std::atomic<bool>* interrupt) override {
53 const bool log_error = request->enable_internal_solver_output();
55 log_error, PdlpSolveProto(std::move(request),
56 /*relax_integer_variables=*/true, interrupt));
57 }
58
59 // ----- Model modifications and extraction -----
60 void Reset() override;
61 void SetOptimizationDirection(bool maximize) override;
62 void SetVariableBounds(int index, double lb, double ub) override;
63 void SetVariableInteger(int index, bool integer) override;
64 void SetConstraintBounds(int index, double lb, double ub) override;
65 void AddRowConstraint(MPConstraint* ct) override;
66 void AddVariable(MPVariable* var) override;
67 void SetCoefficient(MPConstraint* constraint, const MPVariable* variable,
68 double new_value, double old_value) override;
69 void ClearConstraint(MPConstraint* constraint) override;
70 void SetObjectiveCoefficient(const MPVariable* variable,
71 double coefficient) override;
72 void SetObjectiveOffset(double value) override;
73 void ClearObjective() override;
74
75 // ------ Query statistics on the solution and the solve ------
76 int64_t iterations() const override;
77 int64_t nodes() const override;
79 MPSolver::BasisStatus column_status(int variable_index) const override;
80
81 // ----- Misc -----
82 bool IsContinuous() const override;
83 bool IsLP() const override;
84 bool IsMIP() const override;
85
86 std::string SolverVersion() const override;
87 void* underlying_solver() override;
88 bool InterruptSolve() override;
89
90 void ExtractNewVariables() override;
91 void ExtractNewConstraints() override;
92 void ExtractObjective() override;
93
94 void SetParameters(const MPSolverParameters& param) override;
95 void SetRelativeMipGap(double value) override;
96 void SetPrimalTolerance(double value) override;
97 void SetDualTolerance(double value) override;
98 void SetPresolveMode(int value) override;
99 void SetScalingMode(int value) override;
100 void SetLpAlgorithm(int value) override;
102 const std::string& parameters) override;
103 absl::Status SetNumThreads(int num_threads) override;
104
105 private:
106 void NonIncrementalChange();
107
108 pdlp::PrimalDualHybridGradientParams parameters_;
109 pdlp::SolveLog solve_log_;
110 std::atomic<bool> interrupt_solver_;
111};
112
114 : MPSolverInterface(solver), interrupt_solver_(false) {}
115
117
119 // Reset extraction as this interface is not incremental yet.
120 Reset();
121 interrupt_solver_ = false;
122 ExtractModel();
123
124 SetParameters(param);
125 if (quiet_) {
126 parameters_.set_verbosity_level(0);
127 } else {
128 parameters_.set_verbosity_level(3);
129 }
130
132 solver_->solver_specific_parameter_string_);
133
134 // Time limit.
135 if (solver_->time_limit()) {
136 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
137 parameters_.mutable_termination_criteria()->set_time_sec_limit(
138 static_cast<double>(solver_->time_limit()) / 1000.0);
139 }
140
141 // Mark variables and constraints as extracted.
142 for (int i = 0; i < solver_->variables_.size(); ++i) {
144 }
145 for (int i = 0; i < solver_->constraints_.size(); ++i) {
147 }
148
149 MPModelProto model_proto;
150 solver_->ExportModelToProto(&model_proto);
151 MPModelRequest request;
152 *request.mutable_model() = std::move(model_proto);
153 if (!google::protobuf::TextFormat::PrintToString(
154 parameters_, request.mutable_solver_specific_parameters())) {
155 LOG(QFATAL) << "Error converting parameters to text format: "
156 << ProtobufDebugString(parameters_);
157 }
158 absl::StatusOr<MPSolutionResponse> response = PdlpSolveProto(
159 std::move(request), /*relax_integer_variables=*/true, &interrupt_solver_);
160
161 if (!response.ok()) {
162 LOG(ERROR) << "Unexpected error solving with PDLP: " << response.status();
163 return MPSolver::ABNORMAL;
164 }
165
166 // The solution must be marked as synchronized even when no solution exists.
168 if (response->status() == MPSOLVER_CANCELLED_BY_USER) {
169 // MPSOLVER_CANCELLED_BY_USER is only for when the solver didn't have time
170 // to return a proper status, and is not convertible to an MPSolver status.
172 } else {
173 result_status_ = static_cast<MPSolver::ResultStatus>(response->status());
174 }
175 if (response->has_solver_specific_info()) {
176 if (!solve_log_.ParseFromString(response->solver_specific_info())) {
177 LOG(DFATAL)
178 << "Unable to parse PDLP's SolveLog from solver_specific_info";
179 }
180 }
181
182 if (response->status() == MPSOLVER_FEASIBLE ||
183 response->status() == MPSOLVER_OPTIMAL) {
184 const absl::Status result = solver_->LoadSolutionFromProto(*response);
185 if (!result.ok()) {
186 LOG(ERROR) << "LoadSolutionFromProto failed: " << result;
187 }
188 }
189
190 return result_status_;
191}
192
194
196 NonIncrementalChange();
197}
198
199void PdlpInterface::SetVariableBounds(int index, double lb, double ub) {
200 NonIncrementalChange();
201}
202
204 NonIncrementalChange();
205}
206
207void PdlpInterface::SetConstraintBounds(int index, double lb, double ub) {
208 NonIncrementalChange();
209}
210
212 NonIncrementalChange();
213}
214
216 NonIncrementalChange();
217}
218
220 const MPVariable* const variable,
221 double new_value, double old_value) {
222 NonIncrementalChange();
223}
224
226 NonIncrementalChange();
227}
228
230 double coefficient) {
231 NonIncrementalChange();
232}
233
234void PdlpInterface::SetObjectiveOffset(double value) { NonIncrementalChange(); }
235
236void PdlpInterface::ClearObjective() { NonIncrementalChange(); }
237
239 return solve_log_.iteration_count();
240}
241
242int64_t PdlpInterface::nodes() const {
243 LOG(DFATAL) << "Number of nodes only available for discrete problems";
245}
246
248 // TODO(user): While basis status isn't well defined for PDLP, we could
249 // guess statuses that might be useful.
251}
252
254 // TODO(user): While basis status isn't well defined for PDLP, we could
255 // guess statuses that might be useful.
257}
258
259bool PdlpInterface::IsContinuous() const { return true; }
260
261bool PdlpInterface::IsLP() const { return true; }
262
263bool PdlpInterface::IsMIP() const { return false; }
264
265std::string PdlpInterface::SolverVersion() const { return "PDLP Solver"; }
266
267// TODO(user): Consider returning the SolveLog here, as it could be essential
268// for interpreting the PDLP solution.
269void* PdlpInterface::underlying_solver() { return nullptr; }
270
272 interrupt_solver_ = true;
273 return true;
274}
275
276void PdlpInterface::ExtractNewVariables() { NonIncrementalChange(); }
277
278void PdlpInterface::ExtractNewConstraints() { NonIncrementalChange(); }
279
280void PdlpInterface::ExtractObjective() { NonIncrementalChange(); }
281
285
286absl::Status PdlpInterface::SetNumThreads(int num_threads) {
287 if (num_threads < 1) {
288 return absl::InvalidArgumentError(
289 absl::StrCat("Invalid number of threads: ", num_threads));
290 }
291 parameters_.set_num_threads(num_threads);
292 return absl::OkStatus();
293}
294
295// These have no effect. Use SetSolverSpecificParametersAsString instead.
302
307
308void PdlpInterface::NonIncrementalChange() {
309 // The current implementation is not incremental.
311}
312
313// Register PDLP in the global linear solver factory.
315 return new PdlpInterface(solver);
316}
317
318} // namespace operations_research
319#endif // #if defined(USE_PDLP)
void set_variable_as_extracted(int var_index, bool extracted)
void set_constraint_as_extracted(int ct_index, bool extracted)
void ResetExtractionInformation()
Resets the extraction information.
static constexpr int64_t kUnknownNumberOfNodes
void ExtractModel()
Extracts model stored in MPSolver.
bool quiet_
Boolean indicator for the verbosity of the solver output.
void SetCommonParameters(const MPSolverParameters &param)
Sets parameters common to LP and MIP in the underlying solver.
SynchronizationStatus sync_status_
Indicates whether the model and the solution are synchronized.
@ NOT_SOLVED
not been solved yet.
@ ABNORMAL
abnormal, i.e., error of some kind.
bool SetSolverSpecificParametersAsString(const std::string &parameters)
void ExportModelToProto(MPModelProto *output_model) const
Exports model to protocol buffer.
absl::Status LoadSolutionFromProto(const MPSolutionResponse &response, double tolerance=std::numeric_limits< double >::infinity())
The class for variables of a Mathematical Programming (MP) model.
MPSolutionResponse DirectlySolveProto(LazyMutableCopy< MPModelRequest > request, std::atomic< bool > *interrupt) override
void SetLpAlgorithm(int value) override
bool IsMIP() const override
Returns true if the problem is discrete and linear.
bool SetSolverSpecificParametersAsString(const std::string &parameters) override
void SetScalingMode(int value) override
Sets the scaling mode.
std::string SolverVersion() const override
Returns a string describing the underlying solver and its version.
void SetConstraintBounds(int index, double lb, double ub) override
Modify bounds of an extracted variable.
void ClearObjective() override
Clears the objective from all its terms.
void SetPrimalTolerance(double value) override
These have no effect. Use SetSolverSpecificParametersAsString instead.
void SetVariableInteger(int index, bool integer) override
Modifies integrality of an extracted variable.
void SetDualTolerance(double value) override
void SetVariableBounds(int index, double lb, double ub) override
Modifies bounds of an extracted variable.
void Reset() override
--— Model modifications and extraction --—
void AddVariable(MPVariable *var) override
Add a variable.
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
--— Solve --—
bool IsContinuous() const override
--— Misc --—
void SetPresolveMode(int value) override
void SetOptimizationDirection(bool maximize) override
Sets the optimization direction (min/max).
bool IsLP() const override
Returns true if the problem is continuous and linear.
void ExtractObjective() override
Extracts the objective.
void AddRowConstraint(MPConstraint *ct) override
Adds a linear constraint.
void SetRelativeMipGap(double value) override
Sets each parameter in the underlying solver.
bool SupportsDirectlySolveProto(std::atomic< bool > *interrupt) const override
void ExtractNewVariables() override
Extracts the variables that have not been extracted yet.
void SetObjectiveOffset(double value) override
Changes the constant term in the linear objective.
int64_t iterations() const override
---— Query statistics on the solution and the solve ---—
void ExtractNewConstraints() override
Extracts the constraints that have not been extracted yet.
MPSolver::BasisStatus column_status(int variable_index) const override
Returns the basis status of a constraint.
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
Changes a coefficient in the linear objective.
MPSolver::BasisStatus row_status(int constraint_index) const override
Returns the basis status of a row.
void ClearConstraint(MPConstraint *constraint) override
Clears a constraint from all its terms.
void SetParameters(const MPSolverParameters &param) override
Sets all parameters in the underlying solver.
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
Changes a coefficient in a constraint.
absl::Status SetNumThreads(int num_threads) override
Sets the number of threads to be used by the solver.
SatParameters parameters
const Constraint * ct
int64_t value
IntVar * var
int constraint_index
int index
In SWIG mode, we don't want anything besides these top-level includes.
absl::StatusOr< MPSolutionResponse > PdlpSolveProto(LazyMutableCopy< MPModelRequest > request, const bool relax_integer_variables, const std::atomic< bool > *interrupt_solve)
MPSolverInterface * BuildPdlpInterface(MPSolver *const solver)
Register PDLP in the global linear solver factory.
std::string ProtobufDebugString(const P &message)
Definition proto_utils.h:32
MPSolutionResponse ConvertStatusOrMPSolutionResponse(bool log_error, absl::StatusOr< MPSolutionResponse > response)
Definition proto_utils.h:41
bool ProtobufTextFormatMergeFromString(absl::string_view proto_text_string, ProtoType *proto)
Definition proto_utils.h:66
int64_t coefficient