OR-Tools  9.6
pywrap_pdlp.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 
14 // A pybind11 wrapper for pdlp.
15 
16 #include <optional>
17 #include <stdexcept>
18 #include <utility>
19 
20 #include "Eigen/Core"
21 #include "Eigen/SparseCore"
22 #include "absl/status/status.h"
23 #include "absl/status/statusor.h"
24 #include "absl/strings/str_cat.h"
25 #include "absl/strings/string_view.h"
26 #include "ortools/linear_solver/linear_solver.pb.h"
30 #include "ortools/pdlp/solvers.pb.h"
31 #include "pybind11/eigen.h"
32 #include "pybind11/pybind11.h"
33 #include "pybind11/pytypes.h"
34 #include "pybind11/stl.h"
35 
36 namespace pdlp = ::operations_research::pdlp;
37 using ::operations_research::MPModelProto;
38 using ::operations_research::pdlp::QuadraticProgram;
39 using ::pybind11::arg;
40 
41 // TODO(user): The interface uses serialized protos because of issues building
42 // pybind11_protobuf. See
43 // https://github.com/protocolbuffers/protobuf/issues/9464. After
44 // pybind11_protobuf is working, this workaround can be removed.
45 
46 // A mirror of pdlp::SolverResult except with a serialized SolveLog.
48  Eigen::VectorXd primal_solution;
49  Eigen::VectorXd dual_solution;
50  Eigen::VectorXd reduced_costs;
51  pybind11::bytes solve_log_str;
52 };
53 
54 PYBIND11_MODULE(pywrap_pdlp, m) {
55  // ---------------------------------------------------------------------------
56  // quadratic_program.h
57  // ---------------------------------------------------------------------------
58 
59  // It's ok to read and assign to the fields of QuadraticProgram. Attempts to
60  // mutate the fields will likely fail silently because of the copies back and
61  // forth from Python and C++.
62  pybind11::class_<QuadraticProgram>(m, "QuadraticProgram")
63  .def(pybind11::init<>())
64  .def("resize_and_initialize", &QuadraticProgram::ResizeAndInitialize)
65  .def("apply_objective_scaling_and_offset",
66  &QuadraticProgram::ApplyObjectiveScalingAndOffset)
67  .def_readwrite("objective_vector", &QuadraticProgram::objective_vector)
68  .def_readwrite("constraint_matrix", &QuadraticProgram::constraint_matrix)
69  .def_readwrite("constraint_lower_bounds",
70  &QuadraticProgram::constraint_lower_bounds)
71  .def_readwrite("constraint_upper_bounds",
72  &QuadraticProgram::constraint_upper_bounds)
73  .def_readwrite("variable_lower_bounds",
75  .def_readwrite("variable_upper_bounds",
77  .def_readwrite("problem_name", &QuadraticProgram::problem_name)
78  .def_readwrite("variable_names", &QuadraticProgram::variable_names)
79  .def_readwrite("constraint_names", &QuadraticProgram::constraint_names)
80  .def_readwrite("objective_offset", &QuadraticProgram::objective_offset)
81  .def_readwrite("objective_scaling_factor",
82  &QuadraticProgram::objective_scaling_factor)
83  // It appears that pybind11/eigen.h provides only a C++ -> Python
84  // converter for DiagonalMatrix, so we can't expose objective_matrix as a
85  // readwrite field. Below are extra methods for setting the objective
86  // matrix.
87  .def_readonly("objective_matrix", &QuadraticProgram::objective_matrix)
88  .def("set_objective_matrix_diagonal",
89  [](QuadraticProgram& qp,
90  const Eigen::VectorXd& objective_matrix_diagonal) {
91  qp.objective_matrix.emplace();
92  qp.objective_matrix->diagonal() = objective_matrix_diagonal;
93  })
94  .def("clear_objective_matrix",
95  [](QuadraticProgram& qp) { qp.objective_matrix.reset(); });
96 
97  m.def("validate_quadratic_program_dimensions",
98  [](const QuadraticProgram& qp) {
99  const absl::Status status =
101  if (status.ok()) {
102  return;
103  } else {
104  throw std::invalid_argument(absl::StrCat(status.message()));
105  }
106  });
107 
108  m.def("is_linear_program", &pdlp::IsLinearProgram);
109 
110  m.def(
111  "qp_from_mpmodel_proto",
112  [](absl::string_view proto_str, bool relax_integer_variables,
113  bool include_names) {
114  MPModelProto proto;
115  if (!proto.ParseFromString(std::string(proto_str))) {
116  throw std::invalid_argument("Unable to parse input proto");
117  }
118  absl::StatusOr<QuadraticProgram> qp = pdlp::QpFromMpModelProto(
119  proto, relax_integer_variables, include_names);
120  if (qp.ok()) {
121  return *qp;
122  } else {
123  throw std::invalid_argument(absl::StrCat(qp.status().message()));
124  }
125  },
126  arg("proto_str"), arg("relax_integer_variables"),
127  arg("include_names") = false);
128 
129  m.def("qp_to_mpmodel_proto", [](const QuadraticProgram& qp) {
130  absl::StatusOr<MPModelProto> proto = pdlp::QpToMpModelProto(qp);
131  if (proto.ok()) {
132  return pybind11::bytes(proto->SerializeAsString());
133  } else {
134  throw std::invalid_argument(absl::StrCat(proto.status().message()));
135  }
136  });
137 
138  // ---------------------------------------------------------------------------
139  // quadratic_program_io.h
140  // ---------------------------------------------------------------------------
141 
142  m.def("read_quadratic_program_or_die", &pdlp::ReadQuadraticProgramOrDie,
143  arg("filename"), arg("include_names") = false);
144 
145  // ---------------------------------------------------------------------------
146  // primal_dual_hybrid_gradient.h
147  // ---------------------------------------------------------------------------
148 
149  pybind11::class_<pdlp::PrimalAndDualSolution>(m, "PrimalAndDualSolution")
150  .def(pybind11::init<>())
151  .def_readwrite("primal_solution",
153  .def_readwrite("dual_solution",
155 
156  pybind11::class_<PywrapSolverResult>(m, "SolverResult")
157  .def(pybind11::init<>())
158  .def_readwrite("primal_solution", &PywrapSolverResult::primal_solution)
159  .def_readwrite("dual_solution", &PywrapSolverResult::dual_solution)
160  .def_readwrite("reduced_costs", &PywrapSolverResult::reduced_costs)
161  .def_readwrite("solve_log_str", &PywrapSolverResult::solve_log_str);
162 
163  // TODO(user): Expose interrupt_solve and iteration_stats_callback.
164  m.def(
165  "primal_dual_hybrid_gradient",
166  [](QuadraticProgram qp, absl::string_view params_str,
167  std::optional<pdlp::PrimalAndDualSolution> initial_solution) {
168  pdlp::PrimalDualHybridGradientParams params;
169  if (!params.ParseFromString(std::string(params_str))) {
170  throw std::invalid_argument("Unable to parse input params");
171  }
173  std::move(qp), params, std::move(initial_solution));
174  return PywrapSolverResult{
175  .primal_solution = std::move(result.primal_solution),
176  .dual_solution = std::move(result.dual_solution),
177  .reduced_costs = std::move(result.reduced_costs),
178  .solve_log_str = result.solve_log.SerializeAsString()};
179  },
180  arg("qp"), arg("params"), arg("initial_solution") = std::nullopt);
181 }
CpModelProto proto
absl::Status status
Definition: g_gurobi.cc:41
absl::StatusOr< QuadraticProgram > QpFromMpModelProto(const MPModelProto &proto, bool relax_integer_variables, bool include_names)
absl::Status ValidateQuadraticProgramDimensions(const QuadraticProgram &qp)
absl::StatusOr< MPModelProto > QpToMpModelProto(const QuadraticProgram &qp)
bool IsLinearProgram(const QuadraticProgram &qp)
SolverResult PrimalDualHybridGradient(QuadraticProgram qp, const PrimalDualHybridGradientParams &params, const std::atomic< bool > *interrupt_solve, IterationStatsCallback iteration_stats_callback)
QuadraticProgram ReadQuadraticProgramOrDie(const std::string &filename, bool include_names)
PYBIND11_MODULE(pywrap_pdlp, m)
Definition: pywrap_pdlp.cc:54
Eigen::VectorXd dual_solution
Definition: pywrap_pdlp.cc:49
pybind11::bytes solve_log_str
Definition: pywrap_pdlp.cc:51
Eigen::VectorXd reduced_costs
Definition: pywrap_pdlp.cc:50
Eigen::VectorXd primal_solution
Definition: pywrap_pdlp.cc:48
VectorXd variable_lower_bounds
VectorXd variable_upper_bounds
VectorXd objective_vector
VectorXd objective_matrix_diagonal