OR-Tools  9.6
pdlp_proto_solver.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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"
22 #include "ortools/base/logging.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 
34 namespace operations_research {
35 
36 absl::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 ASSIGN_OR_RETURN(lhs, rexpr)
SharedResponseManager * response
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, IterationStatsCallback iteration_stats_callback)
std::optional< ConvergenceInformation > GetConvergenceInformation(const IterationStats &stats, PointType candidate_type)
Collection of objects used to extend the Constraint Solver library.
std::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)
bool ProtobufTextFormatMergeFromString(absl::string_view proto_text_string, ProtoType *proto)