OR-Tools  9.3
pywrap_model_builder_helper.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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 model_builder_helper.
15// Use the pure python wrapper model_builder_helper.py instead of this
16// interface.
17
18#include <stdexcept>
19#include <string>
20
21#include "absl/strings/str_cat.h"
22#include "Eigen/Core"
23#include "Eigen/SparseCore"
24#include "pybind11/eigen.h"
25#include "pybind11/pybind11.h"
26#include "pybind11/pytypes.h"
27#include "ortools/linear_solver/linear_solver.pb.h"
30
31using ::Eigen::SparseMatrix;
32using ::Eigen::VectorXd;
33using ::operations_research::ModelBuilderHelper;
34using ::operations_research::ModelSolverHelper;
35using ::operations_research::MPConstraintProto;
36using ::operations_research::MPModelExportOptions;
37using ::operations_research::MPModelProto;
38using ::operations_research::MPVariableProto;
39using ::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
46pybind11::bytes BuildModel(
47 const Eigen::Ref<const VectorXd>& variable_lower_bounds,
48 const Eigen::Ref<const VectorXd>& variable_upper_bounds,
49 const Eigen::Ref<const VectorXd>& objective_coefficients,
50 const Eigen::Ref<const VectorXd>& constraint_lower_bounds,
51 const Eigen::Ref<const VectorXd>& constraint_upper_bounds,
52 const SparseMatrix<double, Eigen::RowMajor>& constraint_matrix) {
53 const int num_variables = variable_lower_bounds.size();
54 const int num_constraints = constraint_lower_bounds.size();
55
56 if (variable_upper_bounds.size() != num_variables) {
57 throw std::invalid_argument(
58 absl::StrCat("Invalid size ", variable_upper_bounds.size(),
59 " for variable_upper_bounds. Expected: ", num_variables));
60 }
61 if (objective_coefficients.size() != num_variables) {
62 throw std::invalid_argument(absl::StrCat(
63 "Invalid size ", objective_coefficients.size(),
64 " for linear_objective_coefficients. Expected: ", num_variables));
65 }
66 if (constraint_upper_bounds.size() != num_constraints) {
67 throw std::invalid_argument(absl::StrCat(
68 "Invalid size ", constraint_upper_bounds.size(),
69 " for constraint_upper_bounds. Expected: ", num_constraints));
70 }
71 if (constraint_matrix.cols() != num_variables) {
72 throw std::invalid_argument(
73 absl::StrCat("Invalid number of columns ", constraint_matrix.cols(),
74 " in constraint_matrix. Expected: ", num_variables));
75 }
76 if (constraint_matrix.rows() != num_constraints) {
77 throw std::invalid_argument(
78 absl::StrCat("Invalid number of rows ", constraint_matrix.rows(),
79 " in constraint_matrix. Expected: ", num_constraints));
80 }
81
82 MPModelProto model;
83 for (int i = 0; i < num_variables; ++i) {
84 MPVariableProto* variable = model.add_variable();
85 variable->set_lower_bound(variable_lower_bounds[i]);
86 variable->set_upper_bound(variable_upper_bounds[i]);
87 variable->set_objective_coefficient(objective_coefficients[i]);
88 }
89
90 for (int row = 0; row < num_constraints; ++row) {
91 MPConstraintProto* constraint = model.add_constraint();
92 constraint->set_lower_bound(constraint_lower_bounds[row]);
93 constraint->set_upper_bound(constraint_upper_bounds[row]);
94 for (SparseMatrix<double, Eigen::RowMajor>::InnerIterator it(
95 constraint_matrix, row);
96 it; ++it) {
97 constraint->add_coefficient(it.value());
98 constraint->add_var_index(it.col());
99 }
100 }
101
102 return model.SerializeAsString();
103}
104
105PYBIND11_MODULE(pywrap_model_builder_helper, m) {
106 m.def("BuildModel", BuildModel, arg("variable_lower_bounds"),
107 arg("variable_upper_bounds"), arg("objective_coefficients"),
108 arg("constraint_lower_bounds"), arg("constraint_upper_bounds"),
109 arg("constraint_matrix"));
110
111 pybind11::class_<MPModelExportOptions>(m, "MPModelExportOptions")
112 .def(pybind11::init<>())
113 .def_readwrite("obfuscate", &MPModelExportOptions::obfuscate)
114 .def_readwrite("log_invalid_names",
115 &MPModelExportOptions::log_invalid_names)
116 .def_readwrite("show_unused_variables",
117 &MPModelExportOptions::show_unused_variables)
118 .def_readwrite("max_line_length", &MPModelExportOptions::max_line_length);
119
120 m.def(
121 "ExportModelProtoToMpsString",
122 [](const std::string& input_model, const MPModelExportOptions& options) {
123 MPModelProto model;
124 if (!model.ParseFromString(input_model)) {
125 throw std::invalid_argument(
126 "Unable to parse input_model as MPModelProto.");
127 }
128 return ModelBuilderHelper::ExportModelProtoToMpsString(model, options);
129 },
130 arg("input_model"), arg("options") = MPModelExportOptions());
131 m.def(
132 "ExportModelProtoToLpString",
133 [](const std::string& input_model, const MPModelExportOptions& options) {
134 MPModelProto model;
135 if (!model.ParseFromString(input_model)) {
136 throw std::invalid_argument(
137 "Unable to parse input_model as MPModelProto.");
138 }
139 return ModelBuilderHelper::ExportModelProtoToLpString(model, options);
140 },
141 arg("input_model"), arg("options") = MPModelExportOptions());
142
143 m.def("ImportFromMpsString", [](const std::string& str) {
144 MPModelProto model = ModelBuilderHelper::ImportFromMpsString(str);
145 return pybind11::bytes(model.SerializeAsString());
146 });
147 m.def("ImportFromMpsFile", [](const std::string& str) {
148 MPModelProto model = ModelBuilderHelper::ImportFromMpsFile(str);
149 return pybind11::bytes(model.SerializeAsString());
150 });
151 m.def("ImportFromLpString", [](const std::string& str) {
152 MPModelProto model = ModelBuilderHelper::ImportFromLpString(str);
153 return pybind11::bytes(model.SerializeAsString());
154 });
155 m.def("ImportFromLpFile", [](const std::string& str) {
156 MPModelProto model = ModelBuilderHelper::ImportFromLpFile(str);
157 return pybind11::bytes(model.SerializeAsString());
158 });
159
160 pybind11::class_<ModelSolverHelper>(m, "ModelSolverHelper")
161 .def(pybind11::init<>())
162 .def(
163 "Solve",
164 [](ModelSolverHelper* solver_helper, const std::string& request_str) {
165 operations_research::MPModelRequest request;
166 if (!request.ParseFromString(request_str)) {
167 throw std::invalid_argument(
168 "Unable to parse request as MPModelRequest.");
169 }
170 return pybind11::bytes(
171 solver_helper->Solve(request).SerializeAsString());
172 },
173 // The GIL is released during the solve to allow Python threads to do
174 // other things in parallel, e.g., log and interrupt.
175 pybind11::call_guard<pybind11::gil_scoped_release>())
176 .def("InterruptSolve", &ModelSolverHelper::InterruptSolve,
177 "Returns true if the interrupt signal was correctly sent, that is, "
178 "if the underlying solver supports it.")
179 .def("SetLogCallback", &ModelSolverHelper::SetLogCallback);
180}
GRBmodel * model
RowIndex row
Definition: markowitz.cc:182
PYBIND11_MODULE(pywrap_model_builder_helper, m)
pybind11::bytes BuildModel(const Eigen::Ref< const VectorXd > &variable_lower_bounds, const Eigen::Ref< const VectorXd > &variable_upper_bounds, const Eigen::Ref< const VectorXd > &objective_coefficients, const Eigen::Ref< const VectorXd > &constraint_lower_bounds, const Eigen::Ref< const VectorXd > &constraint_upper_bounds, const SparseMatrix< double, Eigen::RowMajor > &constraint_matrix)
VectorXd variable_lower_bounds
VectorXd variable_upper_bounds