Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
pdlp_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 <optional>
18#include <string>
19#include <utility>
20
21#include "absl/status/statusor.h"
24#include "ortools/linear_solver/linear_solver.pb.h"
29#include "ortools/pdlp/solve_log.pb.h"
30#include "ortools/pdlp/solvers.pb.h"
33
34namespace operations_research {
35
36absl::StatusOr<MPSolutionResponse> PdlpSolveProto(
37 LazyMutableCopy<MPModelRequest> request, const bool relax_integer_variables,
38 const std::atomic<bool>* interrupt_solve) {
39 pdlp::PrimalDualHybridGradientParams params;
40 if (request->enable_internal_solver_output()) {
41 params.set_verbosity_level(3);
42 } else {
43 params.set_verbosity_level(0);
44 }
45
46 MPSolutionResponse response;
47 if (!ProtobufTextFormatMergeFromString(request->solver_specific_parameters(),
48 &params)) {
49 response.set_status(
50 MPSolverResponseStatus::MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
51 return response;
52 }
53 if (interrupt_solve != nullptr && interrupt_solve->load() == true) {
54 response.set_status(MPSolverResponseStatus::MPSOLVER_NOT_SOLVED);
55 return response;
56 }
57 if (request->has_solver_time_limit_seconds()) {
58 params.mutable_termination_criteria()->set_time_sec_limit(
59 request->solver_time_limit_seconds());
60 }
61
62 std::optional<LazyMutableCopy<MPModelProto>> optional_model =
63 GetMPModelOrPopulateResponse(request, &response);
64 if (!optional_model) return response;
65
68 pdlp::QpFromMpModelProto(**optional_model, relax_integer_variables));
69
70 // We can now clear the request and optional_model.
71 std::move(request).dispose();
72 optional_model.reset();
73
74 const double objective_scaling_factor = qp.objective_scaling_factor;
75 pdlp::SolverResult pdhg_result =
76 pdlp::PrimalDualHybridGradient(std::move(qp), params, interrupt_solve);
77
78 // PDLP's statuses don't map very cleanly to MPSolver statuses. Do the best
79 // we can for now.
80 switch (pdhg_result.solve_log.termination_reason()) {
81 case pdlp::TERMINATION_REASON_OPTIMAL:
82 response.set_status(MPSOLVER_OPTIMAL);
83 break;
84 case pdlp::TERMINATION_REASON_NUMERICAL_ERROR:
85 response.set_status(MPSOLVER_ABNORMAL);
86 break;
87 case pdlp::TERMINATION_REASON_PRIMAL_INFEASIBLE:
88 response.set_status(MPSOLVER_INFEASIBLE);
89 break;
90 case pdlp::TERMINATION_REASON_INTERRUPTED_BY_USER:
91 response.set_status(MPSOLVER_CANCELLED_BY_USER);
92 break;
93 default:
94 response.set_status(MPSOLVER_NOT_SOLVED);
95 break;
96 }
97 if (pdhg_result.solve_log.has_termination_string()) {
98 response.set_status_str(pdhg_result.solve_log.termination_string());
99 }
100
101 const std::optional<pdlp::ConvergenceInformation> convergence_information =
102 pdlp::GetConvergenceInformation(pdhg_result.solve_log.solution_stats(),
103 pdhg_result.solve_log.solution_type());
104
105 if (convergence_information.has_value()) {
106 response.set_objective_value(convergence_information->primal_objective());
107 }
108 // variable_value and dual_value are supposed to be set iff 'status' is
109 // OPTIMAL or FEASIBLE. However, we set them in all cases.
110
111 for (const double v : pdhg_result.primal_solution) {
112 response.add_variable_value(v);
113 }
114
115 // QpFromMpModelProto converts maximization problems to minimization problems
116 // for PDLP by negating the objective and setting objective_scaling_factor to
117 // -1. This maintains the same set of primal solutions. Dual solutions need to
118 // be negated if objective_scaling_factor is -1.
119 for (const double v : pdhg_result.dual_solution) {
120 response.add_dual_value(objective_scaling_factor * v);
121 }
122
123 for (const double v : pdhg_result.reduced_costs) {
124 response.add_reduced_cost(objective_scaling_factor * v);
125 }
126
127 response.set_solver_specific_info(pdhg_result.solve_log.SerializeAsString());
128
129 return response;
130}
131
132} // namespace operations_research
#define ASSIGN_OR_RETURN(lhs, rexpr)
std::optional< ConvergenceInformation > GetConvergenceInformation(const IterationStats &stats, PointType candidate_type)
absl::StatusOr< QuadraticProgram > QpFromMpModelProto(const MPModelProto &proto, bool relax_integer_variables, bool include_names)
SolverResult PrimalDualHybridGradient(QuadraticProgram qp, const PrimalDualHybridGradientParams &params, const std::atomic< bool > *interrupt_solve, std::function< void(const std::string &)> message_callback, IterationStatsCallback iteration_stats_callback)
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)
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