Google OR-Tools v9.15
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-2025 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#include <atomic>
15#include <cstdint>
16#include <string>
17#include <utility>
18#include <vector>
19
20#include "absl/base/attributes.h"
21#include "absl/status/status.h"
22#include "absl/status/statusor.h"
23#include "absl/strings/str_cat.h"
33
34namespace operations_research {
35
37 public:
38 explicit PdlpInterface(MPSolver* solver);
39 ~PdlpInterface() override;
40
41 // ----- Solve -----
42 MPSolver::ResultStatus Solve(const MPSolverParameters& param) override;
43
44 bool SupportsDirectlySolveProto(std::atomic<bool>* interrupt) const override {
45 return true;
46 }
48 std::atomic<bool>* interrupt) override {
49 const bool log_error = request->enable_internal_solver_output();
51 log_error, PdlpSolveProto(std::move(request),
52 /*relax_integer_variables=*/true, interrupt));
53 }
54
55 // ----- Model modifications and extraction -----
56 void Reset() override;
57 void SetOptimizationDirection(bool maximize) override;
58 void SetVariableBounds(int index, double lb, double ub) override;
59 void SetVariableInteger(int index, bool integer) override;
60 void SetConstraintBounds(int index, double lb, double ub) override;
61 void AddRowConstraint(MPConstraint* ct) override;
62 void AddVariable(MPVariable* var) override;
63 void SetCoefficient(MPConstraint* constraint, const MPVariable* variable,
64 double new_value, double old_value) override;
65 void ClearConstraint(MPConstraint* constraint) override;
66 void SetObjectiveCoefficient(const MPVariable* variable,
67 double coefficient) override;
68 void SetObjectiveOffset(double value) override;
69 void ClearObjective() override;
70
71 // ------ Query statistics on the solution and the solve ------
72 int64_t iterations() const override;
73 int64_t nodes() const override;
74 MPSolver::BasisStatus row_status(int constraint_index) const override;
75 MPSolver::BasisStatus column_status(int variable_index) const override;
76
77 // ----- Misc -----
78 bool IsContinuous() const override;
79 bool IsLP() const override;
80 bool IsMIP() const override;
81
82 std::string SolverVersion() const override;
83 void* underlying_solver() override;
84 bool InterruptSolve() override;
85
86 void ExtractNewVariables() override;
87 void ExtractNewConstraints() override;
88 void ExtractObjective() override;
89
90 void SetParameters(const MPSolverParameters& param) override;
91 void SetRelativeMipGap(double value) override;
92 void SetPrimalTolerance(double value) override;
93 void SetDualTolerance(double value) override;
94 void SetPresolveMode(int value) override;
95 void SetScalingMode(int value) override;
96 void SetLpAlgorithm(int value) override;
98 const std::string& parameters) override;
99 absl::Status SetNumThreads(int num_threads) override;
100
101 private:
102 void NonIncrementalChange();
103
105 pdlp::SolveLog solve_log_;
106 std::atomic<bool> interrupt_solver_;
107};
108
110 : MPSolverInterface(solver), interrupt_solver_(false) {}
111
113
115 // Reset extraction as this interface is not incremental yet.
116 Reset();
117 interrupt_solver_ = false;
118 ExtractModel();
119
120 SetParameters(param);
121 if (quiet_) {
122 parameters_.set_verbosity_level(0);
123 } else {
124 parameters_.set_verbosity_level(3);
125 }
126
127 solver_->SetSolverSpecificParametersAsString(
128 solver_->solver_specific_parameter_string_);
129
130 // Time limit.
131 if (solver_->time_limit()) {
132 VLOG(1) << "Setting time limit = " << solver_->time_limit() << " ms.";
133 parameters_.mutable_termination_criteria()->set_time_sec_limit(
134 static_cast<double>(solver_->time_limit()) / 1000.0);
135 }
136
137 // Mark variables and constraints as extracted.
138 for (int i = 0; i < solver_->variables_.size(); ++i) {
140 }
141 for (int i = 0; i < solver_->constraints_.size(); ++i) {
143 }
144
145 MPModelProto model_proto;
146 solver_->ExportModelToProto(&model_proto);
147 MPModelRequest request;
148 *request.mutable_model() = std::move(model_proto);
149 if (!google::protobuf::TextFormat::PrintToString(
150 parameters_, request.mutable_solver_specific_parameters())) {
151 LOG(QFATAL) << "Error converting parameters to text format: "
152 << ProtobufDebugString(parameters_);
153 }
154 absl::StatusOr<MPSolutionResponse> response = PdlpSolveProto(
155 std::move(request), /*relax_integer_variables=*/true, &interrupt_solver_);
156
157 if (!response.ok()) {
158 LOG(ERROR) << "Unexpected error solving with PDLP: " << response.status();
159 return MPSolver::ABNORMAL;
160 }
161
162 // The solution must be marked as synchronized even when no solution exists.
164 if (response->status() == MPSOLVER_CANCELLED_BY_USER) {
165 // MPSOLVER_CANCELLED_BY_USER is only for when the solver didn't have time
166 // to return a proper status, and is not convertible to an MPSolver status.
168 } else {
169 result_status_ = static_cast<MPSolver::ResultStatus>(response->status());
170 }
171 if (response->has_solver_specific_info()) {
172 if (!solve_log_.ParseFromString(response->solver_specific_info())) {
173 LOG(DFATAL)
174 << "Unable to parse PDLP's SolveLog from solver_specific_info";
175 }
176 }
177
178 if (response->status() == MPSOLVER_FEASIBLE ||
179 response->status() == MPSOLVER_OPTIMAL) {
180 const absl::Status result = solver_->LoadSolutionFromProto(*response);
181 if (!result.ok()) {
182 LOG(ERROR) << "LoadSolutionFromProto failed: " << result;
183 }
184 }
185
186 return result_status_;
187}
188
190
192 NonIncrementalChange();
193}
194
195void PdlpInterface::SetVariableBounds(int index, double lb, double ub) {
196 NonIncrementalChange();
197}
198
199void PdlpInterface::SetVariableInteger(int index, bool integer) {
200 NonIncrementalChange();
201}
202
203void PdlpInterface::SetConstraintBounds(int index, double lb, double ub) {
204 NonIncrementalChange();
205}
206
208 NonIncrementalChange();
209}
210
212 NonIncrementalChange();
213}
214
216 const MPVariable* const variable,
217 double new_value, double old_value) {
218 NonIncrementalChange();
219}
220
222 NonIncrementalChange();
223}
224
226 double coefficient) {
227 NonIncrementalChange();
228}
229
230void PdlpInterface::SetObjectiveOffset(double value) { NonIncrementalChange(); }
231
232void PdlpInterface::ClearObjective() { NonIncrementalChange(); }
233
235 return solve_log_.iteration_count();
236}
237
238int64_t PdlpInterface::nodes() const {
239 LOG(DFATAL) << "Number of nodes only available for discrete problems";
241}
242
244 // TODO(user): While basis status isn't well defined for PDLP, we could
245 // guess statuses that might be useful.
247}
248
250 // TODO(user): While basis status isn't well defined for PDLP, we could
251 // guess statuses that might be useful.
253}
254
255bool PdlpInterface::IsContinuous() const { return true; }
256
257bool PdlpInterface::IsLP() const { return true; }
258
259bool PdlpInterface::IsMIP() const { return false; }
260
261std::string PdlpInterface::SolverVersion() const { return "PDLP Solver"; }
262
263// TODO(user): Consider returning the SolveLog here, as it could be essential
264// for interpreting the PDLP solution.
265void* PdlpInterface::underlying_solver() { return nullptr; }
266
268 interrupt_solver_ = true;
269 return true;
270}
271
272void PdlpInterface::ExtractNewVariables() { NonIncrementalChange(); }
273
274void PdlpInterface::ExtractNewConstraints() { NonIncrementalChange(); }
275
276void PdlpInterface::ExtractObjective() { NonIncrementalChange(); }
277
281
282absl::Status PdlpInterface::SetNumThreads(int num_threads) {
283 if (num_threads < 1) {
284 return absl::InvalidArgumentError(
285 absl::StrCat("Invalid number of threads: ", num_threads));
286 }
287 parameters_.set_num_threads(num_threads);
288 return absl::OkStatus();
289}
290
291// These have no effect. Use SetSolverSpecificParametersAsString instead.
298
300 const std::string& parameters) {
301 return ProtobufTextFormatMergeFromString(parameters, &parameters_);
302}
303
304void PdlpInterface::NonIncrementalChange() {
305 // The current implementation is not incremental.
307}
308
309namespace {
310
311// See MpSolverInterfaceFactoryRepository for details.
312const void* const kRegisterPdlp ABSL_ATTRIBUTE_UNUSED = [] {
314 [](MPSolver* const solver) { return new PdlpInterface(solver); },
316 return nullptr;
317}();
318
319} // namespace
320
321} // namespace operations_research
::std::string *PROTOBUF_NONNULL mutable_solver_specific_parameters()
::operations_research::MPModelProto *PROTOBUF_NONNULL mutable_model()
static MPSolverInterfaceFactoryRepository * GetInstance()
void Register(MPSolverInterfaceFactory factory, MPSolver::OptimizationProblemType problem_type, std::function< bool()> is_runtime_ready={})
void set_variable_as_extracted(int var_index, bool extracted)
void set_constraint_as_extracted(int ct_index, bool extracted)
static constexpr int64_t kUnknownNumberOfNodes
void SetCommonParameters(const MPSolverParameters &param)
@ NOT_SOLVED
not been solved yet.
@ ABNORMAL
abnormal, i.e., error of some kind.
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 SetSolverSpecificParametersAsString(const std::string &parameters) override
void SetScalingMode(int value) override
std::string SolverVersion() const override
void SetConstraintBounds(int index, double lb, double ub) override
void SetPrimalTolerance(double value) override
void SetVariableInteger(int index, bool integer) override
void SetDualTolerance(double value) override
void SetVariableBounds(int index, double lb, double ub) override
void AddVariable(MPVariable *var) override
MPSolver::ResultStatus Solve(const MPSolverParameters &param) override
void SetPresolveMode(int value) override
void SetOptimizationDirection(bool maximize) override
void AddRowConstraint(MPConstraint *ct) override
void SetRelativeMipGap(double value) override
bool SupportsDirectlySolveProto(std::atomic< bool > *interrupt) const override
void SetObjectiveOffset(double value) override
int64_t iterations() const override
MPSolver::BasisStatus column_status(int variable_index) const override
void SetObjectiveCoefficient(const MPVariable *variable, double coefficient) override
MPSolver::BasisStatus row_status(int constraint_index) const override
void ClearConstraint(MPConstraint *constraint) override
void SetParameters(const MPSolverParameters &param) override
void SetCoefficient(MPConstraint *constraint, const MPVariable *variable, double new_value, double old_value) override
absl::Status SetNumThreads(int num_threads) override
OR-Tools root namespace.
absl::StatusOr< MPSolutionResponse > PdlpSolveProto(LazyMutableCopy< MPModelRequest > request, const bool relax_integer_variables, const std::atomic< bool > *interrupt_solve)
std::string ProtobufDebugString(const P &message)
Definition proto_utils.h:31
MPSolutionResponse ConvertStatusOrMPSolutionResponse(bool log_error, absl::StatusOr< MPSolutionResponse > response)
Definition proto_utils.h:37
bool ProtobufTextFormatMergeFromString(absl::string_view proto_text_string, ProtoType *proto)
Definition proto_utils.h:79