27#include "google/protobuf/duration.pb.h"
28#include "absl/memory/memory.h"
29#include "absl/status/status.h"
30#include "absl/status/statusor.h"
31#include "absl/strings/str_cat.h"
32#include "absl/strings/str_join.h"
33#include "absl/time/time.h"
34#include "ortools/pdlp/iteration_stats.h"
35#include "ortools/pdlp/primal_dual_hybrid_gradient.h"
36#include "ortools/pdlp/quadratic_program.h"
37#include "ortools/pdlp/solve_log.pb.h"
38#include "ortools/pdlp/solvers.pb.h"
39#include "ortools/math_opt/callback.pb.h"
43#include "ortools/math_opt/model.pb.h"
44#include "ortools/math_opt/model_parameters.pb.h"
45#include "ortools/math_opt/model_update.pb.h"
46#include "ortools/math_opt/parameters.pb.h"
47#include "ortools/math_opt/result.pb.h"
48#include "ortools/math_opt/solution.pb.h"
50#include "ortools/math_opt/sparse_containers.pb.h"
59using pdlp::PrimalDualHybridGradientParams;
60using pdlp::SolverResult;
64 auto result = absl::WrapUnique(
new PdlpSolver);
69std::pair<PrimalDualHybridGradientParams, std::vector<std::string>>
71 PrimalDualHybridGradientParams result;
72 std::vector<std::string> warnings;
82 result.mutable_termination_criteria()->set_time_sec_limit(
83 absl::ToDoubleSeconds(
87 warnings.push_back(
"parameter cutoff_limit not supported for PDLP");
90 warnings.push_back(
"parameter best_objective_limit not supported for PDLP");
93 warnings.push_back(
"parameter best_bound_limit not supported for PDLP");
96 warnings.push_back(
"parameter solution_limit not supported for PDLP");
99 warnings.push_back(
"parameter random_seed not supported for PDLP");
101 if (
parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
102 warnings.push_back(
"parameter lp_algorithm not supported for PDLP");
104 if (
parameters.presolve() != EMPHASIS_UNSPECIFIED) {
105 warnings.push_back(
"parameter presolve not supported for PDLP");
107 if (
parameters.cuts() != EMPHASIS_UNSPECIFIED) {
108 warnings.push_back(
"parameter cuts not supported for PDLP");
110 if (
parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
111 warnings.push_back(
"parameter heuristics not supported for PDLP");
113 if (
parameters.scaling() != EMPHASIS_UNSPECIFIED) {
114 warnings.push_back(
"parameter scaling not supported for PDLP");
119 result.mutable_termination_criteria()->set_iteration_limit(
120 static_cast<int32_t
>(limit));
123 return {std::move(result), std::move(warnings)};
128absl::StatusOr<TerminationProto> ConvertReason(
130 switch (pdlp_reason) {
131 case pdlp::TERMINATION_REASON_UNSPECIFIED:
133 case pdlp::TERMINATION_REASON_OPTIMAL:
135 case pdlp::TERMINATION_REASON_PRIMAL_INFEASIBLE:
137 case pdlp::TERMINATION_REASON_DUAL_INFEASIBLE:
140 case pdlp::TERMINATION_REASON_TIME_LIMIT:
142 case pdlp::TERMINATION_REASON_ITERATION_LIMIT:
144 case pdlp::TERMINATION_REASON_KKT_MATRIX_PASS_LIMIT:
146 case pdlp::TERMINATION_REASON_NUMERICAL_ERROR:
149 case pdlp::TERMINATION_REASON_INVALID_PROBLEM:
152 return absl::InternalError(
153 absl::StrCat(
"Invalid problem sent to PDLP solver "
154 "(TERMINATION_REASON_INVALID_PROBLEM): ",
157 case pdlp::TERMINATION_REASON_INVALID_PARAMETER:
158 return absl::InvalidArgumentError(absl::StrCat(
159 "PDLP parameters invalid (TERMINATION_REASON_INVALID_PARAMETER): ",
161 case pdlp::TERMINATION_REASON_OTHER:
165 <<
" not implemented.";
170 const bool has_finite_dual_bound) {
171 ProblemStatusProto problem_status;
173 switch (pdlp_reason) {
174 case pdlp::TERMINATION_REASON_OPTIMAL:
175 problem_status.set_primal_status(FEASIBILITY_STATUS_FEASIBLE);
176 problem_status.set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
178 case pdlp::TERMINATION_REASON_PRIMAL_INFEASIBLE:
179 problem_status.set_primal_status(FEASIBILITY_STATUS_INFEASIBLE);
180 problem_status.set_dual_status(FEASIBILITY_STATUS_UNDETERMINED);
182 case pdlp::TERMINATION_REASON_DUAL_INFEASIBLE:
183 problem_status.set_primal_status(FEASIBILITY_STATUS_UNDETERMINED);
184 problem_status.set_dual_status(FEASIBILITY_STATUS_INFEASIBLE);
186 case pdlp::TERMINATION_REASON_PRIMAL_OR_DUAL_INFEASIBLE:
187 problem_status.set_primal_status(FEASIBILITY_STATUS_UNDETERMINED);
188 problem_status.set_dual_status(FEASIBILITY_STATUS_UNDETERMINED);
189 problem_status.set_primal_or_dual_infeasible(
true);
192 problem_status.set_primal_status(FEASIBILITY_STATUS_UNDETERMINED);
193 problem_status.set_dual_status(FEASIBILITY_STATUS_UNDETERMINED);
196 if (has_finite_dual_bound) {
197 problem_status.set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
199 return problem_status;
204absl::Status PdlpSolver::FillSolveResult(
205 const pdlp::SolverResult& pdlp_result,
206 const ModelSolveParametersProto& model_params, SolveResultProto& result) {
208 ConvertReason(pdlp_result.solve_log.termination_reason(),
209 pdlp_result.solve_log.termination_string()));
212 absl::Seconds(pdlp_result.solve_log.solve_time_sec())));
213 const std::optional<pdlp::ConvergenceInformation> convergence_information =
214 pdlp::GetConvergenceInformation(pdlp_result.solve_log.solution_stats(),
215 pdlp_result.solve_log.solution_type());
225 const double objective_scaling_factor =
226 pdlp_bridge_.
pdlp_lp().objective_scaling_factor;
227 result.mutable_solve_stats()->set_best_primal_bound(
228 objective_scaling_factor * std::numeric_limits<double>::infinity());
229 result.mutable_solve_stats()->set_best_dual_bound(
230 -objective_scaling_factor * std::numeric_limits<double>::infinity());
232 switch (pdlp_result.solve_log.termination_reason()) {
233 case pdlp::TERMINATION_REASON_OPTIMAL:
234 case pdlp::TERMINATION_REASON_TIME_LIMIT:
235 case pdlp::TERMINATION_REASON_ITERATION_LIMIT:
236 case pdlp::TERMINATION_REASON_KKT_MATRIX_PASS_LIMIT:
237 case pdlp::TERMINATION_REASON_NUMERICAL_ERROR: {
238 SolutionProto* solution_proto = result.add_solutions();
241 pdlp_result.primal_solution, model_params.variable_values_filter());
243 PrimalSolutionProto* primal_proto =
244 solution_proto->mutable_primal_solution();
245 primal_proto->set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
246 *primal_proto->mutable_variable_values() = *std::move(maybe_primal);
251 if (pdlp_result.solve_log.termination_reason() ==
252 pdlp::TERMINATION_REASON_OPTIMAL) {
253 primal_proto->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
255 if (convergence_information.has_value()) {
256 primal_proto->set_objective_value(
257 convergence_information->primal_objective());
265 pdlp_result.dual_solution, model_params.dual_values_filter());
268 pdlp_result.reduced_costs, model_params.reduced_costs_filter());
270 DualSolutionProto* dual_proto = solution_proto->mutable_dual_solution();
271 dual_proto->set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
272 *dual_proto->mutable_dual_values() = *std::move(maybe_dual);
273 *dual_proto->mutable_reduced_costs() = *std::move(maybe_reduced);
275 if (pdlp_result.solve_log.termination_reason() ==
276 pdlp::TERMINATION_REASON_OPTIMAL) {
277 dual_proto->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
279 if (convergence_information.has_value()) {
280 const double dual_obj = convergence_information->dual_objective();
281 dual_proto->set_objective_value(dual_obj);
284 const double corrected_dual_bound =
285 convergence_information->corrected_dual_objective();
286 result.mutable_solve_stats()->set_best_dual_bound(
287 corrected_dual_bound);
292 case pdlp::TERMINATION_REASON_PRIMAL_INFEASIBLE: {
296 pdlp_result.dual_solution, model_params.dual_values_filter());
299 pdlp_result.reduced_costs, model_params.reduced_costs_filter());
301 DualRayProto* dual_ray_proto = result.add_dual_rays();
302 *dual_ray_proto->mutable_dual_values() = *std::move(maybe_dual);
303 *dual_ray_proto->mutable_reduced_costs() = *std::move(maybe_reduced);
306 case pdlp::TERMINATION_REASON_DUAL_INFEASIBLE: {
310 pdlp_result.primal_solution, model_params.variable_values_filter());
312 PrimalRayProto* primal_ray_proto = result.add_primal_rays();
313 *primal_ray_proto->mutable_variable_values() = *std::move(maybe_primal);
319 *result.mutable_solve_stats()->mutable_problem_status() =
320 GetProblemStatus(pdlp_result.solve_log.termination_reason(),
321 std::isfinite(result.solve_stats().best_dual_bound()));
322 return absl::OkStatus();
327 const ModelSolveParametersProto& model_parameters,
329 const CallbackRegistrationProto& callback_registration,
const Callback cb,
334 if (message_cb !=
nullptr) {
341 SolveResultProto result;
343 if (!param_warnings.empty()) {
344 if (
parameters.strictness().bad_parameter()) {
345 return absl::InvalidArgumentError(absl::StrJoin(param_warnings,
"; "));
347 for (std::string& warning : param_warnings) {
348 result.add_warnings(std::move(warning));
352 const SolverResult pdlp_result =
353 PrimalDualHybridGradient(pdlp_bridge_.
pdlp_lp(), pdlp_params);
354 RETURN_IF_ERROR(FillSolveResult(pdlp_result, model_parameters, result));
360 return absl::InternalError(
"PDLP solver does not support incrementalism");
const pdlp::QuadraticProgram & pdlp_lp() const
absl::StatusOr< SparseDoubleVectorProto > DualVariablesToProto(const Eigen::VectorXd &dual_values, const SparseVectorFilterProto &linear_constraint_filter) const
static absl::StatusOr< PdlpBridge > FromProto(const ModelProto &model_proto)
absl::StatusOr< SparseDoubleVectorProto > PrimalVariablesToProto(const Eigen::VectorXd &primal_values, const SparseVectorFilterProto &variable_filter) const
absl::StatusOr< SparseDoubleVectorProto > ReducedCostsToProto(const Eigen::VectorXd &reduced_costs, const SparseVectorFilterProto &variable_filter) const
bool CanUpdate(const ModelUpdateProto &model_update) override
absl::Status Update(const ModelUpdateProto &model_update) override
static absl::StatusOr< std::unique_ptr< SolverInterface > > New(const ModelProto &model, const InitArgs &init_args)
absl::StatusOr< SolveResultProto > Solve(const SolveParametersProto ¶meters, const ModelSolveParametersProto &model_parameters, MessageCallback message_cb, const CallbackRegistrationProto &callback_registration, Callback cb, SolveInterrupter *interrupter) override
static std::pair< pdlp::PrimalDualHybridGradientParams, std::vector< std::string > > MergeParameters(const SolveParametersProto ¶meters)
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
int SetVLOGLevel(const char *module_pattern, int log_level)
constexpr absl::string_view kMessageCallbackNotSupported
absl::Status CheckRegisteredCallbackEvents(const CallbackRegistrationProto ®istration, const absl::flat_hash_set< CallbackEventProto > &supported_events)
MATH_OPT_REGISTER_SOLVER(SOLVER_TYPE_CP_SAT, CpSatSolver::New)
TerminationProto NoSolutionFoundTermination(const LimitProto limit, const absl::string_view detail)
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)