Files
ortools-clone/ortools/pdlp/python/pdlp.cc
Corentin Le Molgat a66a6daac7 Bump Copyright to 2025
2025-01-10 11:35:44 +01:00

154 lines
6.4 KiB
C++

// Copyright 2010-2025 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// A pybind11 wrapper for pdlp.
#include <optional>
#include <stdexcept>
#include <utility>
#include "Eigen/Core"
#include "absl/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/str_cat.h"
#include "ortools/linear_solver/linear_solver.pb.h"
#include "ortools/pdlp/primal_dual_hybrid_gradient.h"
#include "ortools/pdlp/quadratic_program.h"
#include "ortools/pdlp/quadratic_program_io.h"
#include "ortools/pdlp/solvers.pb.h"
#include "pybind11/eigen.h"
#include "pybind11/pybind11.h"
#include "pybind11/pytypes.h"
#include "pybind11/stl.h"
#include "pybind11_protobuf/native_proto_caster.h"
namespace operations_research::pdlp {
using ::pybind11::arg;
PYBIND11_MODULE(pdlp, m) {
pybind11_protobuf::ImportNativeProtoCasters();
// ---------------------------------------------------------------------------
// quadratic_program.h
// ---------------------------------------------------------------------------
// It's ok to read and assign to the fields of QuadraticProgram. Attempts to
// mutate the fields will likely fail silently because of the copies back and
// forth from Python and C++.
pybind11::class_<QuadraticProgram>(m, "QuadraticProgram")
.def(pybind11::init<>())
.def("resize_and_initialize", &QuadraticProgram::ResizeAndInitialize)
.def("apply_objective_scaling_and_offset",
&QuadraticProgram::ApplyObjectiveScalingAndOffset)
.def_readwrite("objective_vector", &QuadraticProgram::objective_vector)
.def_readwrite("constraint_matrix", &QuadraticProgram::constraint_matrix)
.def_readwrite("constraint_lower_bounds",
&QuadraticProgram::constraint_lower_bounds)
.def_readwrite("constraint_upper_bounds",
&QuadraticProgram::constraint_upper_bounds)
.def_readwrite("variable_lower_bounds",
&QuadraticProgram::variable_lower_bounds)
.def_readwrite("variable_upper_bounds",
&QuadraticProgram::variable_upper_bounds)
.def_readwrite("problem_name", &QuadraticProgram::problem_name)
.def_readwrite("variable_names", &QuadraticProgram::variable_names)
.def_readwrite("constraint_names", &QuadraticProgram::constraint_names)
.def_readwrite("objective_offset", &QuadraticProgram::objective_offset)
.def_readwrite("objective_scaling_factor",
&QuadraticProgram::objective_scaling_factor)
// It appears that pybind11/eigen.h provides only a C++ -> Python
// converter for DiagonalMatrix, so we can't expose objective_matrix as a
// readwrite field. Below are extra methods for setting the objective
// matrix.
.def_readonly("objective_matrix", &QuadraticProgram::objective_matrix)
.def("set_objective_matrix_diagonal",
[](QuadraticProgram& qp,
const Eigen::VectorXd& objective_matrix_diagonal) {
qp.objective_matrix.emplace();
qp.objective_matrix->diagonal() = objective_matrix_diagonal;
})
.def("clear_objective_matrix",
[](QuadraticProgram& qp) { qp.objective_matrix.reset(); });
m.def("validate_quadratic_program_dimensions",
[](const QuadraticProgram& qp) {
const absl::Status status = ValidateQuadraticProgramDimensions(qp);
if (status.ok()) {
return;
} else {
throw std::invalid_argument(absl::StrCat(status.message()));
}
});
m.def("is_linear_program", &IsLinearProgram);
m.def(
"qp_from_mpmodel_proto",
[](const MPModelProto& proto, bool relax_integer_variables,
bool include_names) {
absl::StatusOr<QuadraticProgram> qp =
QpFromMpModelProto(proto, relax_integer_variables, include_names);
if (qp.ok()) {
return *qp;
} else {
throw std::invalid_argument(absl::StrCat(qp.status().message()));
}
},
arg("proto_str"), arg("relax_integer_variables"),
arg("include_names") = false);
m.def("qp_to_mpmodel_proto", [](const QuadraticProgram& qp) {
absl::StatusOr<MPModelProto> proto = QpToMpModelProto(qp);
if (proto.ok()) {
return *proto;
} else {
throw std::invalid_argument(absl::StrCat(proto.status().message()));
}
});
// ---------------------------------------------------------------------------
// quadratic_program_io.h
// ---------------------------------------------------------------------------
m.def("read_quadratic_program_or_die", &ReadQuadraticProgramOrDie,
arg("filename"), arg("include_names") = false);
// ---------------------------------------------------------------------------
// primal_dual_hybrid_gradient.h
// ---------------------------------------------------------------------------
pybind11::class_<PrimalAndDualSolution>(m, "PrimalAndDualSolution")
.def(pybind11::init<>())
.def_readwrite("primal_solution", &PrimalAndDualSolution::primal_solution)
.def_readwrite("dual_solution", &PrimalAndDualSolution::dual_solution);
pybind11::class_<SolverResult>(m, "SolverResult")
.def(pybind11::init<>())
.def_readwrite("primal_solution", &SolverResult::primal_solution)
.def_readwrite("dual_solution", &SolverResult::dual_solution)
.def_readwrite("reduced_costs", &SolverResult::reduced_costs)
.def_readwrite("solve_log", &SolverResult::solve_log);
// TODO(user): Expose interrupt_solve and iteration_stats_callback.
m.def(
"primal_dual_hybrid_gradient",
[](QuadraticProgram qp, PrimalDualHybridGradientParams params,
std::optional<PrimalAndDualSolution> initial_solution) {
return PrimalDualHybridGradient(std::move(qp), params,
std::move(initial_solution));
},
arg("qp"), arg("params"), arg("initial_solution") = std::nullopt);
}
} // namespace operations_research::pdlp