OR-Tools  9.3
pdlp_proto_solver.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 const 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 error_response;
47 if (!ProtobufTextFormatMergeFromString(request.solver_specific_parameters(),
48 &params)) {
49 error_response.set_status(
50 MPSolverResponseStatus::MPSOLVER_MODEL_INVALID_SOLVER_PARAMETERS);
51 return error_response;
52 }
53 if (interrupt_solve != nullptr && interrupt_solve->load() == true) {
54 error_response.set_status(MPSolverResponseStatus::MPSOLVER_NOT_SOLVED);
55 return error_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 const absl::optional<LazyMutableCopy<MPModelProto>> optional_model =
63 ExtractValidMPModelOrPopulateResponseStatus(request, &error_response);
64 if (!optional_model) {
65 LOG_IF(WARNING, request.enable_internal_solver_output())
66 << "Failed to extract a valid model from protocol buffer. Status: "
67 << ProtoEnumToString<MPSolverResponseStatus>(error_response.status())
68 << " (" << error_response.status()
69 << "): " << error_response.status_str();
70 return error_response;
71 }
72
75 pdlp::QpFromMpModelProto(optional_model->get(), relax_integer_variables));
76 const double objective_scaling_factor = qp.objective_scaling_factor;
77
78 pdlp::SolverResult pdhg_result =
79 pdlp::PrimalDualHybridGradient(std::move(qp), params, interrupt_solve);
80
81 // PDLP's statuses don't map very cleanly to MPSolver statuses. Do the best
82 // we can for now.
83 MPSolutionResponse response;
84 switch (pdhg_result.solve_log.termination_reason()) {
85 case pdlp::TERMINATION_REASON_OPTIMAL:
86 response.set_status(MPSOLVER_OPTIMAL);
87 break;
88 case pdlp::TERMINATION_REASON_NUMERICAL_ERROR:
89 response.set_status(MPSOLVER_ABNORMAL);
90 break;
91 case pdlp::TERMINATION_REASON_PRIMAL_INFEASIBLE:
92 response.set_status(MPSOLVER_INFEASIBLE);
93 break;
94 case pdlp::TERMINATION_REASON_INTERRUPTED_BY_USER:
95 response.set_status(MPSOLVER_CANCELLED_BY_USER);
96 break;
97 default:
98 response.set_status(MPSOLVER_NOT_SOLVED);
99 }
100 if (pdhg_result.solve_log.has_termination_string()) {
101 response.set_status_str(pdhg_result.solve_log.termination_string());
102 }
103
104 const std::optional<pdlp::ConvergenceInformation> convergence_information =
105 pdlp::GetConvergenceInformation(pdhg_result.solve_log.solution_stats(),
106 pdhg_result.solve_log.solution_type());
107
108 if (convergence_information.has_value()) {
109 response.set_objective_value(convergence_information->primal_objective());
110 }
111 // variable_value and dual_value are supposed to be set iff 'status' is
112 // OPTIMAL or FEASIBLE. However, we set them in all cases.
113
114 for (const double v : pdhg_result.primal_solution) {
115 response.add_variable_value(v);
116 }
117
118 // QpFromMpModelProto converts maximization problems to minimization problems
119 // for PDLP by negating the objective and setting objective_scaling_factor to
120 // -1. This maintains the same set of primal solutions. Dual solutions need to
121 // be negated if objective_scaling_factor is -1.
122 for (const double v : pdhg_result.dual_solution) {
123 response.add_dual_value(objective_scaling_factor * v);
124 }
125
126 for (const double v : pdhg_result.reduced_costs) {
127 response.add_reduced_cost(objective_scaling_factor * v);
128 }
129
130 response.set_solver_specific_info(pdhg_result.solve_log.SerializeAsString());
131
132 return response;
133}
134
135} // namespace operations_research
#define LOG_IF(severity, condition)
Definition: base/logging.h:479
#define ASSIGN_OR_RETURN(lhs, rexpr)
SharedResponseManager * response
const int WARNING
Definition: log_severity.h:31
absl::StatusOr< QuadraticProgram > QpFromMpModelProto(const MPModelProto &proto, bool relax_integer_variables, bool include_names)
absl::optional< ConvergenceInformation > GetConvergenceInformation(const IterationStats &stats, PointType candidate_type)
SolverResult PrimalDualHybridGradient(QuadraticProgram qp, const PrimalDualHybridGradientParams &params, const std::atomic< bool > *interrupt_solve, IterationStatsCallback iteration_stats_callback)
Collection of objects used to extend the Constraint Solver library.
bool ProtobufTextFormatMergeFromString(const std::string &proto_text_string, ProtoType *proto)
absl::optional< LazyMutableCopy< MPModelProto > > ExtractValidMPModelOrPopulateResponseStatus(const MPModelRequest &request, MPSolutionResponse *response)
If the model is valid and non-empty, returns it (possibly after extracting the model_delta).
absl::StatusOr< MPSolutionResponse > PdlpSolveProto(const MPModelRequest &request, const bool relax_integer_variables, const std::atomic< bool > *interrupt_solve)