27 #include "absl/memory/memory.h"
28 #include "absl/status/status.h"
29 #include "absl/status/statusor.h"
30 #include "absl/strings/str_cat.h"
31 #include "absl/strings/str_join.h"
32 #include "absl/time/time.h"
33 #include "google/protobuf/duration.pb.h"
37 #include "ortools/math_opt/callback.pb.h"
42 #include "ortools/math_opt/model.pb.h"
43 #include "ortools/math_opt/model_parameters.pb.h"
44 #include "ortools/math_opt/model_update.pb.h"
45 #include "ortools/math_opt/parameters.pb.h"
46 #include "ortools/math_opt/result.pb.h"
47 #include "ortools/math_opt/solution.pb.h"
49 #include "ortools/math_opt/sparse_containers.pb.h"
54 #include "ortools/pdlp/solve_log.pb.h"
55 #include "ortools/pdlp/solvers.pb.h"
62 using pdlp::PrimalDualHybridGradientParams;
67 auto result = absl::WrapUnique(
new PdlpSolver);
74 PrimalDualHybridGradientParams result;
75 std::vector<std::string> warnings;
77 result.set_verbosity_level(3);
83 result.mutable_termination_criteria()->set_time_sec_limit(
84 absl::ToDoubleSeconds(
88 warnings.push_back(
"parameter node_limit not supported for PDLP");
91 warnings.push_back(
"parameter cutoff_limit not supported for PDLP");
94 warnings.push_back(
"parameter best_objective_limit not supported for PDLP");
97 warnings.push_back(
"parameter best_bound_limit not supported for PDLP");
100 warnings.push_back(
"parameter solution_limit not supported for PDLP");
103 warnings.push_back(
"parameter random_seed not supported for PDLP");
105 if (
parameters.lp_algorithm() != LP_ALGORITHM_UNSPECIFIED) {
106 warnings.push_back(
"parameter lp_algorithm not supported for PDLP");
108 if (
parameters.presolve() != EMPHASIS_UNSPECIFIED) {
109 warnings.push_back(
"parameter presolve not supported for PDLP");
111 if (
parameters.cuts() != EMPHASIS_UNSPECIFIED) {
112 warnings.push_back(
"parameter cuts not supported for PDLP");
114 if (
parameters.heuristics() != EMPHASIS_UNSPECIFIED) {
115 warnings.push_back(
"parameter heuristics not supported for PDLP");
117 if (
parameters.scaling() != EMPHASIS_UNSPECIFIED) {
118 warnings.push_back(
"parameter scaling not supported for PDLP");
123 result.mutable_termination_criteria()->set_iteration_limit(
124 static_cast<int32_t
>(limit));
127 if (!warnings.empty()) {
128 return absl::InvalidArgumentError(absl::StrJoin(warnings,
"; "));
135 absl::StatusOr<TerminationProto> ConvertReason(
137 switch (pdlp_reason) {
138 case pdlp::TERMINATION_REASON_UNSPECIFIED:
140 case pdlp::TERMINATION_REASON_OPTIMAL:
142 case pdlp::TERMINATION_REASON_PRIMAL_INFEASIBLE:
144 case pdlp::TERMINATION_REASON_DUAL_INFEASIBLE:
147 case pdlp::TERMINATION_REASON_TIME_LIMIT:
149 case pdlp::TERMINATION_REASON_ITERATION_LIMIT:
151 case pdlp::TERMINATION_REASON_KKT_MATRIX_PASS_LIMIT:
153 case pdlp::TERMINATION_REASON_NUMERICAL_ERROR:
156 case pdlp::TERMINATION_REASON_INTERRUPTED_BY_USER:
158 case pdlp::TERMINATION_REASON_INVALID_PROBLEM:
161 return absl::InternalError(
162 absl::StrCat(
"Invalid problem sent to PDLP solver "
163 "(TERMINATION_REASON_INVALID_PROBLEM): ",
165 case pdlp::TERMINATION_REASON_INVALID_INITIAL_SOLUTION:
166 return absl::InvalidArgumentError(
167 absl::StrCat(
"PDLP solution hint invalid "
168 "(TERMINATION_REASON_INVALID_INITIAL_SOLUTION): ",
170 case pdlp::TERMINATION_REASON_INVALID_PARAMETER:
172 return absl::InvalidArgumentError(absl::StrCat(
173 "PDLP parameters invalid (TERMINATION_REASON_INVALID_PARAMETER): ",
175 case pdlp::TERMINATION_REASON_OTHER:
179 <<
" not implemented.";
184 const bool has_finite_dual_bound) {
185 ProblemStatusProto problem_status;
187 switch (pdlp_reason) {
188 case pdlp::TERMINATION_REASON_OPTIMAL:
189 problem_status.set_primal_status(FEASIBILITY_STATUS_FEASIBLE);
190 problem_status.set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
192 case pdlp::TERMINATION_REASON_PRIMAL_INFEASIBLE:
193 problem_status.set_primal_status(FEASIBILITY_STATUS_INFEASIBLE);
194 problem_status.set_dual_status(FEASIBILITY_STATUS_UNDETERMINED);
196 case pdlp::TERMINATION_REASON_DUAL_INFEASIBLE:
197 problem_status.set_primal_status(FEASIBILITY_STATUS_UNDETERMINED);
198 problem_status.set_dual_status(FEASIBILITY_STATUS_INFEASIBLE);
200 case pdlp::TERMINATION_REASON_PRIMAL_OR_DUAL_INFEASIBLE:
201 problem_status.set_primal_status(FEASIBILITY_STATUS_UNDETERMINED);
202 problem_status.set_dual_status(FEASIBILITY_STATUS_UNDETERMINED);
203 problem_status.set_primal_or_dual_infeasible(
true);
206 problem_status.set_primal_status(FEASIBILITY_STATUS_UNDETERMINED);
207 problem_status.set_dual_status(FEASIBILITY_STATUS_UNDETERMINED);
210 if (has_finite_dual_bound) {
211 problem_status.set_dual_status(FEASIBILITY_STATUS_FEASIBLE);
213 return problem_status;
218 absl::StatusOr<SolveResultProto> PdlpSolver::MakeSolveResult(
220 const ModelSolveParametersProto& model_params) {
221 SolveResultProto result;
223 ConvertReason(pdlp_result.
solve_log.termination_reason(),
224 pdlp_result.
solve_log.termination_string()));
227 absl::Seconds(pdlp_result.
solve_log.solve_time_sec())));
228 result.mutable_solve_stats()->set_first_order_iterations(
229 pdlp_result.
solve_log.iteration_count());
230 const std::optional<pdlp::ConvergenceInformation> convergence_information =
242 const double objective_scaling_factor =
244 result.mutable_solve_stats()->set_best_primal_bound(
245 objective_scaling_factor * std::numeric_limits<double>::infinity());
246 result.mutable_solve_stats()->set_best_dual_bound(
247 -objective_scaling_factor * std::numeric_limits<double>::infinity());
249 switch (pdlp_result.
solve_log.termination_reason()) {
250 case pdlp::TERMINATION_REASON_OPTIMAL:
251 case pdlp::TERMINATION_REASON_TIME_LIMIT:
252 case pdlp::TERMINATION_REASON_ITERATION_LIMIT:
253 case pdlp::TERMINATION_REASON_KKT_MATRIX_PASS_LIMIT:
254 case pdlp::TERMINATION_REASON_NUMERICAL_ERROR:
255 case pdlp::TERMINATION_REASON_INTERRUPTED_BY_USER: {
256 SolutionProto* solution_proto = result.add_solutions();
261 PrimalSolutionProto* primal_proto =
262 solution_proto->mutable_primal_solution();
263 primal_proto->set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
264 *primal_proto->mutable_variable_values() = *std::move(maybe_primal);
269 if (pdlp_result.
solve_log.termination_reason() ==
270 pdlp::TERMINATION_REASON_OPTIMAL) {
271 primal_proto->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
273 if (convergence_information.has_value()) {
274 primal_proto->set_objective_value(
275 convergence_information->primal_objective());
283 pdlp_result.
dual_solution, model_params.dual_values_filter());
286 pdlp_result.
reduced_costs, model_params.reduced_costs_filter());
288 DualSolutionProto* dual_proto = solution_proto->mutable_dual_solution();
289 dual_proto->set_feasibility_status(SOLUTION_STATUS_UNDETERMINED);
290 *dual_proto->mutable_dual_values() = *std::move(maybe_dual);
291 *dual_proto->mutable_reduced_costs() = *std::move(maybe_reduced);
293 if (pdlp_result.
solve_log.termination_reason() ==
294 pdlp::TERMINATION_REASON_OPTIMAL) {
295 dual_proto->set_feasibility_status(SOLUTION_STATUS_FEASIBLE);
297 if (convergence_information.has_value()) {
298 const double dual_obj = convergence_information->dual_objective();
299 dual_proto->set_objective_value(dual_obj);
302 const double corrected_dual_bound =
303 convergence_information->corrected_dual_objective();
304 result.mutable_solve_stats()->set_best_dual_bound(
305 corrected_dual_bound);
310 case pdlp::TERMINATION_REASON_PRIMAL_INFEASIBLE: {
314 pdlp_result.
dual_solution, model_params.dual_values_filter());
317 pdlp_result.
reduced_costs, model_params.reduced_costs_filter());
319 DualRayProto* dual_ray_proto = result.add_dual_rays();
320 *dual_ray_proto->mutable_dual_values() = *std::move(maybe_dual);
321 *dual_ray_proto->mutable_reduced_costs() = *std::move(maybe_reduced);
324 case pdlp::TERMINATION_REASON_DUAL_INFEASIBLE: {
330 PrimalRayProto* primal_ray_proto = result.add_primal_rays();
331 *primal_ray_proto->mutable_variable_values() = *std::move(maybe_primal);
337 *result.mutable_solve_stats()->mutable_problem_status() =
338 GetProblemStatus(pdlp_result.
solve_log.termination_reason(),
339 std::isfinite(result.solve_stats().best_dual_bound()));
345 const ModelSolveParametersProto& model_parameters,
347 const CallbackRegistrationProto& callback_registration,
const Callback cb,
350 if (message_cb !=
nullptr) {
363 std::atomic<bool> interrupt =
false;
365 interrupter, [&]() { interrupt =
true; });
367 std::optional<PrimalAndDualSolution> initial_solution;
368 if (!model_parameters.solution_hints().empty()) {
370 model_parameters.solution_hints(0));
374 pdlp_bridge_.
pdlp_lp(), pdlp_params, initial_solution, &interrupt);
375 return MakeSolveResult(pdlp_result, model_parameters);
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
absl::StatusOr< SparseDoubleVectorProto > DualVariablesToProto(const Eigen::VectorXd &dual_values, const SparseVectorFilterProto &linear_constraint_filter) const
const pdlp::QuadraticProgram & pdlp_lp() const
static absl::StatusOr< PdlpBridge > FromProto(const ModelProto &model_proto)
InvertedBounds ListInvertedBounds() const
pdlp::PrimalAndDualSolution SolutionHintToWarmStart(const SolutionHintProto &solution_hint) const
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
absl::StatusOr< bool > Update(const ModelUpdateProto &model_update) override
static absl::StatusOr< pdlp::PrimalDualHybridGradientParams > MergeParameters(const SolveParametersProto ¶meters)
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
std::function< void(const std::vector< std::string > &)> MessageCallback
std::function< absl::StatusOr< CallbackResultProto >(const CallbackDataProto &)> Callback
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)
SolverResult PrimalDualHybridGradient(QuadraticProgram qp, const PrimalDualHybridGradientParams ¶ms, 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::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)
absl::Status ToStatus() const
double objective_scaling_factor
Eigen::VectorXd dual_solution
Eigen::VectorXd reduced_costs
Eigen::VectorXd primal_solution