rewrite model builder pybind11 code to use shared_ptr; add in place optimizations
This commit is contained in:
@@ -18,6 +18,7 @@
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
@@ -63,7 +64,6 @@ using ::operations_research::mb::Variable;
|
||||
using ::operations_research::mb::WeightedSumArray;
|
||||
|
||||
namespace py = pybind11;
|
||||
using ::py::arg;
|
||||
|
||||
void ThrowError(PyObject* py_exception, const std::string& message) {
|
||||
PyErr_SetString(py_exception, message.c_str());
|
||||
@@ -211,38 +211,30 @@ const char* kVarClassDoc = R"doc(A variable (continuous or integral).
|
||||
model is feasible, or optimal if you provided an objective function.
|
||||
)doc";
|
||||
|
||||
void ProcessExprArg(const py::handle& arg, LinearExpr*& expr,
|
||||
double& float_value) {
|
||||
if (py::isinstance<LinearExpr>(arg)) {
|
||||
expr = arg.cast<LinearExpr*>();
|
||||
} else {
|
||||
float_value = arg.cast<double>();
|
||||
}
|
||||
}
|
||||
|
||||
LinearExpr* SumArguments(py::args args, const py::kwargs& kwargs) {
|
||||
std::vector<LinearExpr*> linear_exprs;
|
||||
std::shared_ptr<LinearExpr> SumArguments(py::args args,
|
||||
const py::kwargs& kwargs) {
|
||||
std::vector<std::shared_ptr<LinearExpr>> linear_exprs;
|
||||
double float_offset = 0.0;
|
||||
|
||||
const auto process_arg = [&](const py::handle& arg) -> void {
|
||||
if (py::isinstance<LinearExpr>(arg)) {
|
||||
linear_exprs.push_back(arg.cast<LinearExpr*>());
|
||||
} else {
|
||||
float_offset += arg.cast<double>();
|
||||
}
|
||||
};
|
||||
|
||||
if (args.size() == 1 && py::isinstance<py::sequence>(args[0])) {
|
||||
// Normal list or tuple argument.
|
||||
py::sequence elements = args[0].cast<py::sequence>();
|
||||
linear_exprs.reserve(elements.size());
|
||||
for (const py::handle& arg : elements) {
|
||||
process_arg(arg);
|
||||
if (py::isinstance<LinearExpr>(arg)) {
|
||||
linear_exprs.push_back(arg.cast<std::shared_ptr<LinearExpr>>());
|
||||
} else {
|
||||
float_offset += arg.cast<double>();
|
||||
}
|
||||
}
|
||||
} else { // Direct sum(x, y, 3, ..) without [].
|
||||
linear_exprs.reserve(args.size());
|
||||
for (const py::handle arg : args) {
|
||||
process_arg(arg);
|
||||
if (py::isinstance<LinearExpr>(arg)) {
|
||||
linear_exprs.push_back(arg.cast<std::shared_ptr<LinearExpr>>());
|
||||
} else {
|
||||
float_offset += arg.cast<double>();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -259,21 +251,21 @@ LinearExpr* SumArguments(py::args args, const py::kwargs& kwargs) {
|
||||
}
|
||||
|
||||
if (linear_exprs.empty()) {
|
||||
return new FixedValue(float_offset);
|
||||
return std::make_shared<FixedValue>(float_offset);
|
||||
} else if (linear_exprs.size() == 1) {
|
||||
if (float_offset == 0.0) {
|
||||
return linear_exprs[0];
|
||||
} else {
|
||||
return new AffineExpr(linear_exprs[0], 1.0, float_offset);
|
||||
return std::make_shared<AffineExpr>(linear_exprs[0], 1.0, float_offset);
|
||||
}
|
||||
} else {
|
||||
return new SumArray(linear_exprs, float_offset);
|
||||
return std::make_shared<SumArray>(linear_exprs, float_offset);
|
||||
}
|
||||
}
|
||||
|
||||
LinearExpr* WeightedSumArguments(py::sequence expressions,
|
||||
const std::vector<double>& coefficients,
|
||||
double offset = 0.0) {
|
||||
std::shared_ptr<LinearExpr> WeightedSumArguments(
|
||||
py::sequence expressions, const std::vector<double>& coefficients,
|
||||
double offset = 0.0) {
|
||||
if (expressions.size() != coefficients.size()) {
|
||||
ThrowError(PyExc_ValueError,
|
||||
absl::StrCat("LinearExpr::weighted_sum() requires the same "
|
||||
@@ -281,205 +273,269 @@ LinearExpr* WeightedSumArguments(py::sequence expressions,
|
||||
expressions.size(), " != ", coefficients.size()));
|
||||
}
|
||||
|
||||
std::vector<LinearExpr*> linear_exprs;
|
||||
std::vector<std::shared_ptr<LinearExpr>> linear_exprs;
|
||||
std::vector<double> coeffs;
|
||||
linear_exprs.reserve(expressions.size());
|
||||
coeffs.reserve(expressions.size());
|
||||
|
||||
for (int i = 0; i < expressions.size(); ++i) {
|
||||
py::handle arg = expressions[i];
|
||||
LinearExpr* expr = nullptr;
|
||||
double value = 0.0;
|
||||
ProcessExprArg(arg, expr, value);
|
||||
if (expr != nullptr && coefficients[i] != 0.0) {
|
||||
linear_exprs.push_back(expr);
|
||||
coeffs.push_back(coefficients[i]);
|
||||
continue;
|
||||
} else if (value != 0.0) {
|
||||
offset += coefficients[i] * value;
|
||||
std::shared_ptr<LinearExpr> expr = nullptr;
|
||||
if (py::isinstance<LinearExpr>(arg)) {
|
||||
if (coefficients[i] != 0.0) {
|
||||
linear_exprs.push_back(arg.cast<std::shared_ptr<LinearExpr>>());
|
||||
coeffs.push_back(coefficients[i]);
|
||||
}
|
||||
} else {
|
||||
offset += arg.cast<double>() * coefficients[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (linear_exprs.empty()) {
|
||||
return new FixedValue(offset);
|
||||
return std::make_shared<FixedValue>(offset);
|
||||
} else if (linear_exprs.size() == 1) {
|
||||
if (offset == 0.0 && coeffs[0] == 1.0) {
|
||||
return linear_exprs[0];
|
||||
} else {
|
||||
return new AffineExpr(linear_exprs[0], coeffs[0], offset);
|
||||
}
|
||||
return LinearExpr::Affine(linear_exprs[0], coeffs[0], offset);
|
||||
} else {
|
||||
return new WeightedSumArray(linear_exprs, coeffs, offset);
|
||||
return std::make_shared<WeightedSumArray>(linear_exprs, coeffs, offset);
|
||||
}
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(model_builder_helper, m) {
|
||||
pybind11_protobuf::ImportNativeProtoCasters();
|
||||
|
||||
py::class_<LinearExpr>(m, "LinearExpr", kLinearExprClassDoc)
|
||||
py::class_<LinearExpr, std::shared_ptr<LinearExpr>>(m, "LinearExpr",
|
||||
kLinearExprClassDoc)
|
||||
.def_static("sum", &SumArguments,
|
||||
"Creates `sum(expressions) [+ constant]`.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
"Creates `sum(expressions) [+ constant]`.")
|
||||
.def_static(
|
||||
"weighted_sum", &WeightedSumArguments,
|
||||
"Creates `sum(expressions[i] * coefficients[i]) [+ constant]`.",
|
||||
arg("expressions"), arg("coefficients"), py::kw_only(),
|
||||
arg("constant") = 0.0, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def_static("term", &LinearExpr::Term, arg("expr").none(false),
|
||||
arg("coeff"), "Returns expr * coeff.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("term", &LinearExpr::Affine, arg("expr").none(false),
|
||||
arg("coeff"), py::kw_only(), py::arg("constant"),
|
||||
"Returns expr * coeff [+ constant].",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("term", &LinearExpr::AffineCst, arg("value"), arg("coeff"),
|
||||
py::kw_only(), py::arg("constant"),
|
||||
"Returns value * coeff [+ constant].",
|
||||
py::return_value_policy::automatic)
|
||||
.def_static("affine", &LinearExpr::Affine, arg("expr").none(false),
|
||||
arg("coeff"), arg("constant") = 0.0,
|
||||
"Returns expr * coeff + constant.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("affine", &LinearExpr::AffineCst, arg("value"), arg("coeff"),
|
||||
arg("constant") = 0.0, "Returns value * coeff + constant.",
|
||||
py::return_value_policy::automatic)
|
||||
.def_static("constant", &LinearExpr::Constant, arg("value"),
|
||||
"Returns a constant linear expression.",
|
||||
py::return_value_policy::automatic)
|
||||
py::arg("expressions"), py::arg("coefficients"), py::kw_only(),
|
||||
py::arg("constant") = 0.0)
|
||||
.def_static("term", &LinearExpr::Term, py::arg("expr").none(false),
|
||||
py::arg("coeff"), "Returns expr * coeff.")
|
||||
.def_static("term", &LinearExpr::Affine, py::arg("expr").none(false),
|
||||
py::arg("coeff"), py::kw_only(), py::arg("constant"),
|
||||
"Returns expr * coeff [+ constant].")
|
||||
.def_static("term", &LinearExpr::AffineCst, py::arg("value"),
|
||||
py::arg("coeff"), py::kw_only(), py::arg("constant"),
|
||||
"Returns value * coeff [+ constant].")
|
||||
.def_static("affine", &LinearExpr::Affine, py::arg("expr").none(false),
|
||||
py::arg("coeff"), py::arg("constant") = 0.0,
|
||||
"Returns expr * coeff + constant.")
|
||||
.def_static("affine", &LinearExpr::AffineCst, py::arg("value"),
|
||||
py::arg("coeff"), py::arg("constant") = 0.0,
|
||||
"Returns value * coeff + constant.")
|
||||
.def_static("constant", &LinearExpr::Constant, py::arg("value"),
|
||||
"Returns a constant linear expression.")
|
||||
// Methods.
|
||||
.def("__str__", &LinearExpr::ToString)
|
||||
.def("__repr__", &LinearExpr::DebugString)
|
||||
// Operators.
|
||||
// Note that we keep the 3 APIS (expr, int, double) instead of using an
|
||||
// py::handle argument as this is more efficient.
|
||||
.def("__add__", &LinearExpr::Add, arg("other").none(false),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__add__", &LinearExpr::AddFloat, arg("cst"),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__radd__", &LinearExpr::AddFloat, arg("cst"),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__sub__", &LinearExpr::Sub, arg("other").none(false),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__sub__", &LinearExpr::SubFloat, arg("cst"),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__rsub__", &LinearExpr::RSubFloat, arg("cst"),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__mul__", &LinearExpr::MulFloat, arg("cst"),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__rmul__", &LinearExpr::MulFloat, arg("cst"),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def(
|
||||
"__truediv__",
|
||||
[](LinearExpr* self, double cst) {
|
||||
if (cst == 0.0) {
|
||||
ThrowError(PyExc_ZeroDivisionError,
|
||||
"Division by zero is not supported.");
|
||||
}
|
||||
return self->MulFloat(1.0 / cst);
|
||||
},
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__neg__", &LinearExpr::Neg, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def("__add__", &LinearExpr::Add, py::arg("other").none(false))
|
||||
.def("__add__", &LinearExpr::AddFloat, py::arg("cst"))
|
||||
.def("__radd__", &LinearExpr::AddFloat, py::arg("cst"))
|
||||
.def("__sub__", &LinearExpr::Sub, py::arg("other").none(false))
|
||||
.def("__sub__", &LinearExpr::SubFloat, py::arg("cst"))
|
||||
.def("__rsub__", &LinearExpr::RSubFloat, py::arg("cst"))
|
||||
.def("__mul__", &LinearExpr::MulFloat, py::arg("cst"))
|
||||
.def("__rmul__", &LinearExpr::MulFloat, py::arg("cst"))
|
||||
.def("__truediv__",
|
||||
[](std::shared_ptr<LinearExpr> self, double cst) {
|
||||
if (cst == 0.0) {
|
||||
ThrowError(PyExc_ZeroDivisionError,
|
||||
"Division by zero is not supported.");
|
||||
}
|
||||
return self->MulFloat(1.0 / cst);
|
||||
})
|
||||
.def("__neg__", &LinearExpr::Neg)
|
||||
// Comparison operators.
|
||||
.def("__eq__", &LinearExpr::Eq, arg("other").none(false),
|
||||
"Creates the constraint `self == other`.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__eq__", &LinearExpr::EqCst, arg("cst"),
|
||||
"Creates the constraint `self == cst`.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__le__", &LinearExpr::Le, arg("other").none(false),
|
||||
"Creates the constraint `self <= other`.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__le__", &LinearExpr::LeCst, arg("cst"),
|
||||
"Creates the constraint `self <= cst`.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__ge__", &LinearExpr::Ge, arg("other").none(false),
|
||||
"Creates the constraint `self >= other`.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__ge__", &LinearExpr::GeCst, arg("cst"),
|
||||
"Creates the constraint `self >= cst`.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__eq__", &LinearExpr::Eq, py::arg("other").none(false),
|
||||
"Creates the constraint `self == other`.")
|
||||
.def("__eq__", &LinearExpr::EqCst, py::arg("cst"),
|
||||
"Creates the constraint `self == cst`.")
|
||||
.def("__le__", &LinearExpr::Le, py::arg("other").none(false),
|
||||
"Creates the constraint `self <= other`.")
|
||||
.def("__le__", &LinearExpr::LeCst, py::arg("cst"),
|
||||
"Creates the constraint `self <= cst`.")
|
||||
.def("__ge__", &LinearExpr::Ge, py::arg("other").none(false),
|
||||
"Creates the constraint `self >= other`.")
|
||||
.def("__ge__", &LinearExpr::GeCst, py::arg("cst"),
|
||||
"Creates the constraint `self >= cst`.")
|
||||
// Disable other operators as they are not supported.
|
||||
.def("__floordiv__",
|
||||
[](LinearExpr* /*self*/, py::handle /*other*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/, py::handle /*other*/) {
|
||||
ThrowError(PyExc_NotImplementedError,
|
||||
"calling // on a linear expression is not supported.");
|
||||
})
|
||||
.def("__mod__",
|
||||
[](LinearExpr* /*self*/, py::handle /*other*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/, py::handle /*other*/) {
|
||||
ThrowError(PyExc_NotImplementedError,
|
||||
"calling %% on a linear expression is not supported.");
|
||||
})
|
||||
.def("__pow__",
|
||||
[](LinearExpr* /*self*/, py::handle /*other*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/, py::handle /*other*/) {
|
||||
ThrowError(PyExc_NotImplementedError,
|
||||
"calling ** on a linear expression is not supported.");
|
||||
})
|
||||
.def("__lshift__",
|
||||
[](LinearExpr* /*self*/, py::handle /*other*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/, py::handle /*other*/) {
|
||||
ThrowError(
|
||||
PyExc_NotImplementedError,
|
||||
"calling left shift on a linear expression is not supported");
|
||||
})
|
||||
.def("__rshift__",
|
||||
[](LinearExpr* /*self*/, py::handle /*other*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/, py::handle /*other*/) {
|
||||
ThrowError(
|
||||
PyExc_NotImplementedError,
|
||||
"calling right shift on a linear expression is not supported");
|
||||
})
|
||||
.def("__and__",
|
||||
[](LinearExpr* /*self*/, py::handle /*other*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/, py::handle /*other*/) {
|
||||
ThrowError(PyExc_NotImplementedError,
|
||||
"calling and on a linear expression is not supported");
|
||||
})
|
||||
.def("__or__",
|
||||
[](LinearExpr* /*self*/, py::handle /*other*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/, py::handle /*other*/) {
|
||||
ThrowError(PyExc_NotImplementedError,
|
||||
"calling or on a linear expression is not supported");
|
||||
})
|
||||
.def("__xor__",
|
||||
[](LinearExpr* /*self*/, py::handle /*other*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/, py::handle /*other*/) {
|
||||
ThrowError(PyExc_NotImplementedError,
|
||||
"calling xor on a linear expression is not supported");
|
||||
})
|
||||
.def("__abs__",
|
||||
[](LinearExpr* /*self*/) {
|
||||
[](std::shared_ptr<LinearExpr> /*self*/) {
|
||||
ThrowError(
|
||||
PyExc_NotImplementedError,
|
||||
"calling abs() on a linear expression is not supported.");
|
||||
})
|
||||
.def("__bool__", [](LinearExpr* /*self*/) {
|
||||
.def("__bool__", [](std::shared_ptr<LinearExpr> /*self*/) {
|
||||
ThrowError(PyExc_NotImplementedError,
|
||||
"Evaluating a LinearExpr instance as a Boolean is "
|
||||
"not supported.");
|
||||
});
|
||||
|
||||
// Expose Internal classes, mostly for testing.
|
||||
py::class_<FlatExpr, LinearExpr>(m, "FlatExpr")
|
||||
.def(py::init<const LinearExpr*>())
|
||||
.def(py::init<const LinearExpr*, const LinearExpr*>())
|
||||
.def(py::init<const std::vector<const Variable*>&,
|
||||
const std::vector<double>&, double>(),
|
||||
py::keep_alive<1, 2>())
|
||||
py::class_<FlatExpr, std::shared_ptr<FlatExpr>, LinearExpr>(m, "FlatExpr")
|
||||
.def(py::init<std::shared_ptr<LinearExpr>>())
|
||||
.def(py::init<std::shared_ptr<LinearExpr>, std::shared_ptr<LinearExpr>>())
|
||||
.def(py::init<const std::vector<std::shared_ptr<Variable>>&,
|
||||
const std::vector<double>&, double>())
|
||||
.def(py::init<double>())
|
||||
.def_property_readonly("vars", &FlatExpr::vars)
|
||||
.def("variable_indices", &FlatExpr::VarIndices)
|
||||
.def_property_readonly("coeffs", &FlatExpr::coeffs)
|
||||
.def_property_readonly("offset", &FlatExpr::offset);
|
||||
|
||||
py::class_<AffineExpr, LinearExpr>(m, "AffineExpr")
|
||||
.def(py::init<LinearExpr*, double, double>())
|
||||
py::class_<SumArray, std::shared_ptr<SumArray>, LinearExpr>(
|
||||
m, "SumArray", "Holds a sum of linear expressions, and constants.")
|
||||
.def(py::init<std::vector<std::shared_ptr<LinearExpr>>, double>())
|
||||
.def(
|
||||
"__add__",
|
||||
[](py::object self,
|
||||
std::shared_ptr<LinearExpr> other) -> std::shared_ptr<LinearExpr> {
|
||||
const int num_uses = Py_REFCNT(self.ptr());
|
||||
std::shared_ptr<SumArray> expr =
|
||||
self.cast<std::shared_ptr<SumArray>>();
|
||||
if (num_uses == 4) {
|
||||
expr->AddInPlace(other);
|
||||
return expr;
|
||||
}
|
||||
return expr->Add(other);
|
||||
},
|
||||
py::arg("other").none(false),
|
||||
"Returns the sum of `self` and `other`.")
|
||||
.def(
|
||||
"__add__",
|
||||
[](py::object self, double cst) -> std::shared_ptr<LinearExpr> {
|
||||
const int num_uses = Py_REFCNT(self.ptr());
|
||||
std::shared_ptr<SumArray> expr =
|
||||
self.cast<std::shared_ptr<SumArray>>();
|
||||
if (num_uses == 4) {
|
||||
expr->AddFloatInPlace(cst);
|
||||
return expr;
|
||||
}
|
||||
return expr->AddFloat(cst);
|
||||
},
|
||||
py::arg("cst"), "Returns `self` + `cst`.")
|
||||
.def("__radd__", &LinearExpr::Add, py::arg("other").none(false),
|
||||
"Returns `self` + `other`.")
|
||||
.def(
|
||||
"__radd__",
|
||||
[](py::object self, double cst) -> std::shared_ptr<LinearExpr> {
|
||||
const int num_uses = Py_REFCNT(self.ptr());
|
||||
std::shared_ptr<SumArray> expr =
|
||||
self.cast<std::shared_ptr<SumArray>>();
|
||||
if (num_uses == 4) {
|
||||
expr->AddFloatInPlace(cst);
|
||||
return expr;
|
||||
}
|
||||
return expr->AddFloat(cst);
|
||||
},
|
||||
py::arg("cst"), "Returns `self` + `cst`.")
|
||||
.def(
|
||||
"__sub__",
|
||||
[](py::object self,
|
||||
std::shared_ptr<LinearExpr> other) -> std::shared_ptr<LinearExpr> {
|
||||
const int num_uses = Py_REFCNT(self.ptr());
|
||||
std::shared_ptr<SumArray> expr =
|
||||
self.cast<std::shared_ptr<SumArray>>();
|
||||
if (num_uses == 4) {
|
||||
expr->AddInPlace(other->Neg());
|
||||
return expr;
|
||||
}
|
||||
return expr->Sub(other);
|
||||
},
|
||||
py::arg("other").none(false), "Returns `self` - `other`.")
|
||||
.def(
|
||||
"__sub__",
|
||||
[](py::object self, double cst) -> std::shared_ptr<LinearExpr> {
|
||||
const int num_uses = Py_REFCNT(self.ptr());
|
||||
std::shared_ptr<SumArray> expr =
|
||||
self.cast<std::shared_ptr<SumArray>>();
|
||||
if (num_uses == 4) {
|
||||
expr->AddFloatInPlace(-cst);
|
||||
return expr;
|
||||
}
|
||||
return expr->SubFloat(cst);
|
||||
},
|
||||
py::arg("cst"), "Returns `self` - `cst`.")
|
||||
.def_property_readonly(
|
||||
"num_exprs", &SumArray::num_exprs,
|
||||
"Returns the number of linear expressions in the sum.")
|
||||
.def_property_readonly("offset", &SumArray::offset,
|
||||
"Returns the offset of the sum.");
|
||||
|
||||
py::class_<AffineExpr, std::shared_ptr<AffineExpr>, LinearExpr>(m,
|
||||
"AffineExpr")
|
||||
.def(py::init<std::shared_ptr<LinearExpr>, double, double>())
|
||||
.def("__add__", &AffineExpr::Add, py::arg("other").none(false),
|
||||
"Returns `self` + `other`.")
|
||||
.def("__add__", &AffineExpr::AddFloat, py::arg("cst"),
|
||||
"Returns `self` + `cst`.")
|
||||
.def("__radd__", &AffineExpr::Add, py::arg("other").none(false),
|
||||
"Returns `self` + `other`.")
|
||||
.def("__radd__", &AffineExpr::AddFloat, py::arg("cst"),
|
||||
"Returns `self` + `cst`.")
|
||||
.def("__sub__", &AffineExpr::Sub, py::arg("other").none(false),
|
||||
"Returns `self` - `other`.")
|
||||
.def("__sub__", &AffineExpr::SubFloat, py::arg("cst"),
|
||||
"Returns `self` - `cst`.")
|
||||
.def("__rsub__", &AffineExpr::RSubFloat, py::arg("cst"),
|
||||
"Returns `cst` - `self`.")
|
||||
.def("__mul__", &AffineExpr::MulFloat, py::arg("cst"),
|
||||
"Returns `self` * `cst`.")
|
||||
.def("__rmul__", &AffineExpr::MulFloat, py::arg("cst"),
|
||||
"Returns `self` * `cst`.")
|
||||
.def("__neg__", &AffineExpr::Neg, "Returns -`self`.")
|
||||
.def_property_readonly("expression", &AffineExpr ::expression)
|
||||
.def_property_readonly("coefficient", &AffineExpr::coefficient)
|
||||
.def_property_readonly("offset", &AffineExpr::offset);
|
||||
|
||||
py::class_<Variable, LinearExpr>(m, "Variable", kVarClassDoc)
|
||||
py::class_<Variable, std::shared_ptr<Variable>, LinearExpr>(m, "Variable",
|
||||
kVarClassDoc)
|
||||
.def(py::init<ModelBuilderHelper*, int>())
|
||||
.def(py::init<ModelBuilderHelper*, double, double, bool>())
|
||||
.def(py::init<ModelBuilderHelper*, double, double, bool, std::string>())
|
||||
@@ -505,11 +561,14 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
return absl::HashOf(std::make_tuple(self.helper(), self.index()));
|
||||
});
|
||||
|
||||
py::class_<BoundedLinearExpression>(m, "BoundedLinearExpression")
|
||||
.def(py::init<const LinearExpr*, double, double>())
|
||||
.def(py::init<const LinearExpr*, const LinearExpr*, double, double>())
|
||||
.def(py::init<const LinearExpr*, int64_t, int64_t>())
|
||||
.def(py::init<const LinearExpr*, const LinearExpr*, int64_t, int64_t>())
|
||||
py::class_<BoundedLinearExpression, std::shared_ptr<BoundedLinearExpression>>(
|
||||
m, "BoundedLinearExpression")
|
||||
.def(py::init<std::shared_ptr<LinearExpr>, double, double>())
|
||||
.def(py::init<std::shared_ptr<LinearExpr>, std::shared_ptr<LinearExpr>,
|
||||
double, double>())
|
||||
.def(py::init<std::shared_ptr<LinearExpr>, int64_t, int64_t>())
|
||||
.def(py::init<std::shared_ptr<LinearExpr>, std::shared_ptr<LinearExpr>,
|
||||
int64_t, int64_t>())
|
||||
.def_property_readonly("vars", &BoundedLinearExpression::vars)
|
||||
.def_property_readonly("coeffs", &BoundedLinearExpression::coeffs)
|
||||
.def_property_readonly("lower_bound",
|
||||
@@ -531,7 +590,7 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
.def("__str__", &BoundedLinearExpression::ToString)
|
||||
.def("__repr__", &BoundedLinearExpression::DebugString);
|
||||
|
||||
m.def("to_mpmodel_proto", &ToMPModelProto, arg("helper"));
|
||||
m.def("to_mpmodel_proto", &ToMPModelProto, py::arg("helper"));
|
||||
|
||||
py::class_<MPModelExportOptions>(m, "MPModelExportOptions")
|
||||
.def(py::init<>())
|
||||
@@ -545,26 +604,26 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
py::class_<ModelBuilderHelper>(m, "ModelBuilderHelper")
|
||||
.def(py::init<>())
|
||||
.def("overwrite_model", &ModelBuilderHelper::OverwriteModel,
|
||||
arg("other_helper"))
|
||||
py::arg("other_helper"))
|
||||
.def("export_to_mps_string", &ModelBuilderHelper::ExportToMpsString,
|
||||
arg("options") = MPModelExportOptions())
|
||||
py::arg("options") = MPModelExportOptions())
|
||||
.def("export_to_lp_string", &ModelBuilderHelper::ExportToLpString,
|
||||
arg("options") = MPModelExportOptions())
|
||||
py::arg("options") = MPModelExportOptions())
|
||||
.def("write_to_mps_file", &ModelBuilderHelper::WriteToMpsFile,
|
||||
arg("filename"), arg("options") = MPModelExportOptions())
|
||||
py::arg("filename"), py::arg("options") = MPModelExportOptions())
|
||||
.def("read_model_from_proto_file",
|
||||
&ModelBuilderHelper::ReadModelFromProtoFile, arg("filename"))
|
||||
&ModelBuilderHelper::ReadModelFromProtoFile, py::arg("filename"))
|
||||
.def("write_model_to_proto_file",
|
||||
&ModelBuilderHelper::WriteModelToProtoFile, arg("filename"))
|
||||
&ModelBuilderHelper::WriteModelToProtoFile, py::arg("filename"))
|
||||
.def("import_from_mps_string", &ModelBuilderHelper::ImportFromMpsString,
|
||||
arg("mps_string"))
|
||||
py::arg("mps_string"))
|
||||
.def("import_from_mps_file", &ModelBuilderHelper::ImportFromMpsFile,
|
||||
arg("mps_file"))
|
||||
py::arg("mps_file"))
|
||||
#if defined(USE_LP_PARSER)
|
||||
.def("import_from_lp_string", &ModelBuilderHelper::ImportFromLpString,
|
||||
arg("lp_string"))
|
||||
py::arg("lp_string"))
|
||||
.def("import_from_lp_file", &ModelBuilderHelper::ImportFromLpFile,
|
||||
arg("lp_file"))
|
||||
py::arg("lp_file"))
|
||||
#else
|
||||
.def("import_from_lp_string", [](const std::string& lp_string) {
|
||||
LOG(INFO) << "Parsing LP string is not compiled in";
|
||||
@@ -588,9 +647,9 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
constraint_upper_bounds, constraint_matrix,
|
||||
helper->mutable_model());
|
||||
},
|
||||
arg("variable_lower_bound"), arg("variable_upper_bound"),
|
||||
arg("objective_coefficients"), arg("constraint_lower_bounds"),
|
||||
arg("constraint_upper_bounds"), arg("constraint_matrix"))
|
||||
py::arg("variable_lower_bound"), py::arg("variable_upper_bound"),
|
||||
py::arg("objective_coefficients"), py::arg("constraint_lower_bounds"),
|
||||
py::arg("constraint_upper_bounds"), py::arg("constraint_matrix"))
|
||||
.def("add_var", &ModelBuilderHelper::AddVar)
|
||||
.def("add_var_array",
|
||||
[](ModelBuilderHelper* helper, std::vector<size_t> shape, double lb,
|
||||
@@ -651,14 +710,14 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
return result;
|
||||
})
|
||||
.def("set_var_lower_bound", &ModelBuilderHelper::SetVarLowerBound,
|
||||
arg("var_index"), arg("lb"))
|
||||
py::arg("var_index"), py::arg("lb"))
|
||||
.def("set_var_upper_bound", &ModelBuilderHelper::SetVarUpperBound,
|
||||
arg("var_index"), arg("ub"))
|
||||
py::arg("var_index"), py::arg("ub"))
|
||||
.def("set_var_integrality", &ModelBuilderHelper::SetVarIntegrality,
|
||||
arg("var_index"), arg("is_integer"))
|
||||
py::arg("var_index"), py::arg("is_integer"))
|
||||
.def("set_var_objective_coefficient",
|
||||
&ModelBuilderHelper::SetVarObjectiveCoefficient, arg("var_index"),
|
||||
arg("coeff"))
|
||||
&ModelBuilderHelper::SetVarObjectiveCoefficient,
|
||||
py::arg("var_index"), py::arg("coeff"))
|
||||
.def("set_objective_coefficients",
|
||||
[](ModelBuilderHelper* helper, const std::vector<int>& indices,
|
||||
const std::vector<double>& coefficients) {
|
||||
@@ -667,29 +726,29 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
helper->SetVarObjectiveCoefficient(i, c);
|
||||
}
|
||||
})
|
||||
.def("set_var_name", &ModelBuilderHelper::SetVarName, arg("var_index"),
|
||||
arg("name"))
|
||||
.def("set_var_name", &ModelBuilderHelper::SetVarName,
|
||||
py::arg("var_index"), py::arg("name"))
|
||||
.def("var_lower_bound", &ModelBuilderHelper::VarLowerBound,
|
||||
arg("var_index"))
|
||||
py::arg("var_index"))
|
||||
.def("var_upper_bound", &ModelBuilderHelper::VarUpperBound,
|
||||
arg("var_index"))
|
||||
py::arg("var_index"))
|
||||
.def("var_is_integral", &ModelBuilderHelper::VarIsIntegral,
|
||||
arg("var_index"))
|
||||
py::arg("var_index"))
|
||||
.def("var_objective_coefficient",
|
||||
&ModelBuilderHelper::VarObjectiveCoefficient, arg("var_index"))
|
||||
.def("var_name", &ModelBuilderHelper::VarName, arg("var_index"))
|
||||
&ModelBuilderHelper::VarObjectiveCoefficient, py::arg("var_index"))
|
||||
.def("var_name", &ModelBuilderHelper::VarName, py::arg("var_index"))
|
||||
.def("add_linear_constraint", &ModelBuilderHelper::AddLinearConstraint)
|
||||
.def("set_constraint_lower_bound",
|
||||
&ModelBuilderHelper::SetConstraintLowerBound, arg("ct_index"),
|
||||
arg("lb"))
|
||||
&ModelBuilderHelper::SetConstraintLowerBound, py::arg("ct_index"),
|
||||
py::arg("lb"))
|
||||
.def("set_constraint_upper_bound",
|
||||
&ModelBuilderHelper::SetConstraintUpperBound, arg("ct_index"),
|
||||
arg("ub"))
|
||||
&ModelBuilderHelper::SetConstraintUpperBound, py::arg("ct_index"),
|
||||
py::arg("ub"))
|
||||
.def("add_term_to_constraint", &ModelBuilderHelper::AddConstraintTerm,
|
||||
arg("ct_index"), arg("var_index"), arg("coeff"))
|
||||
py::arg("ct_index"), py::arg("var_index"), py::arg("coeff"))
|
||||
.def("add_terms_to_constraint",
|
||||
[](ModelBuilderHelper* helper, int ct_index,
|
||||
const std::vector<const Variable*>& vars,
|
||||
const std::vector<std::shared_ptr<Variable>>& vars,
|
||||
const std::vector<double>& coefficients) {
|
||||
for (int i = 0; i < vars.size(); ++i) {
|
||||
helper->AddConstraintTerm(ct_index, vars[i]->index(),
|
||||
@@ -697,39 +756,39 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
}
|
||||
})
|
||||
.def("safe_add_term_to_constraint",
|
||||
&ModelBuilderHelper::SafeAddConstraintTerm, arg("ct_index"),
|
||||
arg("var_index"), arg("coeff"))
|
||||
&ModelBuilderHelper::SafeAddConstraintTerm, py::arg("ct_index"),
|
||||
py::arg("var_index"), py::arg("coeff"))
|
||||
.def("set_constraint_name", &ModelBuilderHelper::SetConstraintName,
|
||||
arg("ct_index"), arg("name"))
|
||||
py::arg("ct_index"), py::arg("name"))
|
||||
.def("set_constraint_coefficient",
|
||||
&ModelBuilderHelper::SetConstraintCoefficient, arg("ct_index"),
|
||||
arg("var_index"), arg("coeff"))
|
||||
&ModelBuilderHelper::SetConstraintCoefficient, py::arg("ct_index"),
|
||||
py::arg("var_index"), py::arg("coeff"))
|
||||
.def("constraint_lower_bound", &ModelBuilderHelper::ConstraintLowerBound,
|
||||
arg("ct_index"))
|
||||
py::arg("ct_index"))
|
||||
.def("constraint_upper_bound", &ModelBuilderHelper::ConstraintUpperBound,
|
||||
arg("ct_index"))
|
||||
py::arg("ct_index"))
|
||||
.def("constraint_name", &ModelBuilderHelper::ConstraintName,
|
||||
arg("ct_index"))
|
||||
py::arg("ct_index"))
|
||||
.def("constraint_var_indices", &ModelBuilderHelper::ConstraintVarIndices,
|
||||
arg("ct_index"))
|
||||
py::arg("ct_index"))
|
||||
.def("constraint_coefficients",
|
||||
&ModelBuilderHelper::ConstraintCoefficients, arg("ct_index"))
|
||||
&ModelBuilderHelper::ConstraintCoefficients, py::arg("ct_index"))
|
||||
.def("add_enforced_linear_constraint",
|
||||
&ModelBuilderHelper::AddEnforcedLinearConstraint)
|
||||
.def("is_enforced_linear_constraint",
|
||||
&ModelBuilderHelper::IsEnforcedConstraint)
|
||||
.def("set_enforced_constraint_lower_bound",
|
||||
&ModelBuilderHelper::SetEnforcedConstraintLowerBound,
|
||||
arg("ct_index"), arg("lb"))
|
||||
py::arg("ct_index"), py::arg("lb"))
|
||||
.def("set_enforced_constraint_upper_bound",
|
||||
&ModelBuilderHelper::SetEnforcedConstraintUpperBound,
|
||||
arg("ct_index"), arg("ub"))
|
||||
py::arg("ct_index"), py::arg("ub"))
|
||||
.def("add_term_to_enforced_constraint",
|
||||
&ModelBuilderHelper::AddEnforcedConstraintTerm, arg("ct_index"),
|
||||
arg("var_index"), arg("coeff"))
|
||||
&ModelBuilderHelper::AddEnforcedConstraintTerm, py::arg("ct_index"),
|
||||
py::arg("var_index"), py::arg("coeff"))
|
||||
.def("add_terms_to_enforced_constraint",
|
||||
[](ModelBuilderHelper* helper, int ct_index,
|
||||
const std::vector<const Variable*>& vars,
|
||||
const std::vector<std::shared_ptr<Variable>>& vars,
|
||||
const std::vector<double>& coefficients) {
|
||||
for (int i = 0; i < vars.size(); ++i) {
|
||||
helper->AddEnforcedConstraintTerm(ct_index, vars[i]->index(),
|
||||
@@ -737,47 +796,53 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
}
|
||||
})
|
||||
.def("safe_add_term_to_enforced_constraint",
|
||||
&ModelBuilderHelper::SafeAddEnforcedConstraintTerm, arg("ct_index"),
|
||||
arg("var_index"), arg("coeff"))
|
||||
&ModelBuilderHelper::SafeAddEnforcedConstraintTerm,
|
||||
py::arg("ct_index"), py::arg("var_index"), py::arg("coeff"))
|
||||
.def("set_enforced_constraint_name",
|
||||
&ModelBuilderHelper::SetEnforcedConstraintName, arg("ct_index"),
|
||||
arg("name"))
|
||||
&ModelBuilderHelper::SetEnforcedConstraintName, py::arg("ct_index"),
|
||||
py::arg("name"))
|
||||
.def("set_enforced_constraint_coefficient",
|
||||
&ModelBuilderHelper::SetEnforcedConstraintCoefficient,
|
||||
arg("ct_index"), arg("var_index"), arg("coeff"))
|
||||
py::arg("ct_index"), py::arg("var_index"), py::arg("coeff"))
|
||||
.def("enforced_constraint_lower_bound",
|
||||
&ModelBuilderHelper::EnforcedConstraintLowerBound, arg("ct_index"))
|
||||
&ModelBuilderHelper::EnforcedConstraintLowerBound,
|
||||
py::arg("ct_index"))
|
||||
.def("enforced_constraint_upper_bound",
|
||||
&ModelBuilderHelper::EnforcedConstraintUpperBound, arg("ct_index"))
|
||||
&ModelBuilderHelper::EnforcedConstraintUpperBound,
|
||||
py::arg("ct_index"))
|
||||
.def("enforced_constraint_name",
|
||||
&ModelBuilderHelper::EnforcedConstraintName, arg("ct_index"))
|
||||
&ModelBuilderHelper::EnforcedConstraintName, py::arg("ct_index"))
|
||||
.def("enforced_constraint_var_indices",
|
||||
&ModelBuilderHelper::EnforcedConstraintVarIndices, arg("ct_index"))
|
||||
&ModelBuilderHelper::EnforcedConstraintVarIndices,
|
||||
py::arg("ct_index"))
|
||||
.def("enforced_constraint_coefficients",
|
||||
&ModelBuilderHelper::EnforcedConstraintCoefficients, arg("ct_index"))
|
||||
&ModelBuilderHelper::EnforcedConstraintCoefficients,
|
||||
py::arg("ct_index"))
|
||||
.def("set_enforced_constraint_indicator_variable_index",
|
||||
&ModelBuilderHelper::SetEnforcedIndicatorVariableIndex,
|
||||
arg("ct_index"), arg("var_index"))
|
||||
py::arg("ct_index"), py::arg("var_index"))
|
||||
.def("set_enforced_constraint_indicator_value",
|
||||
&ModelBuilderHelper::SetEnforcedIndicatorValue, arg("ct_index"),
|
||||
arg("positive"))
|
||||
&ModelBuilderHelper::SetEnforcedIndicatorValue, py::arg("ct_index"),
|
||||
py::arg("positive"))
|
||||
.def("enforced_constraint_indicator_variable_index",
|
||||
&ModelBuilderHelper::EnforcedIndicatorVariableIndex, arg("ct_index"))
|
||||
&ModelBuilderHelper::EnforcedIndicatorVariableIndex,
|
||||
py::arg("ct_index"))
|
||||
.def("enforced_constraint_indicator_value",
|
||||
&ModelBuilderHelper::EnforcedIndicatorValue, arg("ct_index"))
|
||||
&ModelBuilderHelper::EnforcedIndicatorValue, py::arg("ct_index"))
|
||||
.def("num_variables", &ModelBuilderHelper::num_variables)
|
||||
.def("num_constraints", &ModelBuilderHelper::num_constraints)
|
||||
.def("name", &ModelBuilderHelper::name)
|
||||
.def("set_name", &ModelBuilderHelper::SetName, arg("name"))
|
||||
.def("set_name", &ModelBuilderHelper::SetName, py::arg("name"))
|
||||
.def("clear_objective", &ModelBuilderHelper::ClearObjective)
|
||||
.def("maximize", &ModelBuilderHelper::maximize)
|
||||
.def("set_maximize", &ModelBuilderHelper::SetMaximize, arg("maximize"))
|
||||
.def("set_maximize", &ModelBuilderHelper::SetMaximize,
|
||||
py::arg("maximize"))
|
||||
.def("set_objective_offset", &ModelBuilderHelper::SetObjectiveOffset,
|
||||
arg("offset"))
|
||||
py::arg("offset"))
|
||||
.def("objective_offset", &ModelBuilderHelper::ObjectiveOffset)
|
||||
.def("clear_hints", &ModelBuilderHelper::ClearHints)
|
||||
.def("add_hint", &ModelBuilderHelper::AddHint, arg("var_index"),
|
||||
arg("var_value"));
|
||||
.def("add_hint", &ModelBuilderHelper::AddHint, py::arg("var_index"),
|
||||
py::arg("var_value"));
|
||||
|
||||
py::enum_<SolveStatus>(m, "SolveStatus")
|
||||
.value("OPTIMAL", SolveStatus::OPTIMAL)
|
||||
@@ -799,7 +864,7 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
py::class_<ModelSolverHelper>(m, "ModelSolverHelper")
|
||||
.def(py::init<const std::string&>())
|
||||
.def("solver_is_supported", &ModelSolverHelper::SolverIsSupported)
|
||||
.def("solve", &ModelSolverHelper::Solve, arg("model"),
|
||||
.def("solve", &ModelSolverHelper::Solve, py::arg("model"),
|
||||
// The GIL is released during the solve to allow Python threads to do
|
||||
// other things in parallel, e.g., log and interrupt.
|
||||
py::call_guard<py::gil_scoped_release>())
|
||||
@@ -829,11 +894,11 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
.def("set_log_callback", &ModelSolverHelper::SetLogCallback)
|
||||
.def("clear_log_callback", &ModelSolverHelper::ClearLogCallback)
|
||||
.def("set_time_limit_in_seconds",
|
||||
&ModelSolverHelper::SetTimeLimitInSeconds, arg("limit"))
|
||||
&ModelSolverHelper::SetTimeLimitInSeconds, py::arg("limit"))
|
||||
.def("set_solver_specific_parameters",
|
||||
&ModelSolverHelper::SetSolverSpecificParameters,
|
||||
arg("solver_specific_parameters"))
|
||||
.def("enable_output", &ModelSolverHelper::EnableOutput, arg("output"))
|
||||
py::arg("solver_specific_parameters"))
|
||||
.def("enable_output", &ModelSolverHelper::EnableOutput, py::arg("output"))
|
||||
.def("has_solution", &ModelSolverHelper::has_solution)
|
||||
.def("has_response", &ModelSolverHelper::has_response)
|
||||
.def("response", &ModelSolverHelper::response)
|
||||
@@ -844,18 +909,20 @@ PYBIND11_MODULE(model_builder_helper, m) {
|
||||
.def("objective_value", &ModelSolverHelper::objective_value)
|
||||
.def("best_objective_bound", &ModelSolverHelper::best_objective_bound)
|
||||
.def("variable_value", &ModelSolverHelper::variable_value,
|
||||
arg("var_index"))
|
||||
py::arg("var_index"))
|
||||
.def("expression_value",
|
||||
[](const ModelSolverHelper& helper, LinearExpr* expr) {
|
||||
[](const ModelSolverHelper& helper,
|
||||
std::shared_ptr<LinearExpr> expr) {
|
||||
if (!helper.has_response()) {
|
||||
throw std::logic_error(
|
||||
"Accessing a solution value when none has been found.");
|
||||
}
|
||||
return helper.expression_value(expr);
|
||||
})
|
||||
.def("reduced_cost", &ModelSolverHelper::reduced_cost, arg("var_index"))
|
||||
.def("dual_value", &ModelSolverHelper::dual_value, arg("ct_index"))
|
||||
.def("activity", &ModelSolverHelper::activity, arg("ct_index"))
|
||||
.def("reduced_cost", &ModelSolverHelper::reduced_cost,
|
||||
py::arg("var_index"))
|
||||
.def("dual_value", &ModelSolverHelper::dual_value, py::arg("ct_index"))
|
||||
.def("activity", &ModelSolverHelper::activity, py::arg("ct_index"))
|
||||
.def("variable_values",
|
||||
[](const ModelSolverHelper& helper) {
|
||||
if (!helper.has_response()) {
|
||||
|
||||
@@ -2243,6 +2243,127 @@ class ModelBuilderExamplesTest(absltest.TestCase):
|
||||
self.assertEqual(z.index, ct.indicator_variable.index)
|
||||
self.assertFalse(ct.indicator_value)
|
||||
|
||||
def testInPlaceSumModifications(self) -> None:
|
||||
model = mb.Model()
|
||||
x = [model.new_int_var(0, 10, f"x{i}") for i in range(5)]
|
||||
y = [model.new_int_var(0, 10, f"y{i}") for i in range(5)]
|
||||
e1 = sum(x)
|
||||
self.assertIsInstance(e1, mbh.SumArray)
|
||||
self.assertEqual(e1.offset, 0)
|
||||
self.assertEqual(e1.num_exprs, 5)
|
||||
e1_str = str(e1)
|
||||
_ = e1 + y[0]
|
||||
_ = sum(y) + e1
|
||||
self.assertEqual(e1_str, str(e1))
|
||||
|
||||
e2 = sum(x) - 2 - y[0] - 0.1
|
||||
e2_str = str(e2)
|
||||
self.assertIsInstance(e2, mbh.SumArray)
|
||||
self.assertEqual(e2.offset, -2.1)
|
||||
self.assertEqual(e2.num_exprs, 6)
|
||||
_ = e2 + 2.5
|
||||
self.assertEqual(str(e2), e2_str)
|
||||
|
||||
e3 = 1.2 + sum(x) + 0.3
|
||||
self.assertIsInstance(e3, mbh.SumArray)
|
||||
self.assertEqual(e3.offset, 1.5)
|
||||
self.assertEqual(e3.num_exprs, 5)
|
||||
|
||||
def testLargeSum(self) -> None:
|
||||
model = mb.Model()
|
||||
x = [model.new_int_var(0, 10, f"x{i}") for i in range(100000)]
|
||||
model.add(sum(x) == 10)
|
||||
|
||||
def testSimplification1(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = (x * 2) * 2
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(4, prod.coefficient)
|
||||
self.assertEqual(0, prod.offset)
|
||||
|
||||
def testSimplification2(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = 2 * (x * 2)
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(4, prod.coefficient)
|
||||
self.assertEqual(0, prod.offset)
|
||||
|
||||
def testSimplification3(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = (2 * x) * 2
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(4, prod.coefficient)
|
||||
self.assertEqual(0, prod.offset)
|
||||
|
||||
def testSimplification4(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = 2 * (2 * x)
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(4, prod.coefficient)
|
||||
self.assertEqual(0, prod.offset)
|
||||
|
||||
def testSimplification5(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = 2 * (x + 1)
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(2, prod.coefficient)
|
||||
self.assertEqual(2, prod.offset)
|
||||
|
||||
def testSimplification6(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = (x + 1) * 2
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(2, prod.coefficient)
|
||||
self.assertEqual(2, prod.offset)
|
||||
|
||||
def testSimplification7(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = 2 * (x - 1)
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(2, prod.coefficient)
|
||||
self.assertEqual(-2, prod.offset)
|
||||
|
||||
def testSimplification8(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = (x - 1) * 2
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(2, prod.coefficient)
|
||||
self.assertEqual(-2, prod.offset)
|
||||
|
||||
def testSimplification9(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = 2 * (1 - x)
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(-2, prod.coefficient)
|
||||
self.assertEqual(2, prod.offset)
|
||||
|
||||
def testSimplification10(self):
|
||||
model = mb.Model()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = (1 - x) * 2
|
||||
self.assertIsInstance(prod, mbh.AffineExpr)
|
||||
self.assertEqual(x, prod.expression)
|
||||
self.assertEqual(-2, prod.coefficient)
|
||||
self.assertEqual(2, prod.offset)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
absltest.main()
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -250,7 +251,7 @@ void ModelBuilderHelper::SetConstraintCoefficient(int ct_index, int var_index,
|
||||
}
|
||||
}
|
||||
// If we reach this point, the variable does not exist in the constraint yet,
|
||||
// so we add it to the constraint as a new term.
|
||||
// so we add it to the constraint as a newterm.
|
||||
ct_proto->add_var_index(var_index);
|
||||
ct_proto->add_coefficient(coeff);
|
||||
}
|
||||
@@ -280,7 +281,7 @@ std::vector<double> ModelBuilderHelper::ConstraintCoefficients(
|
||||
|
||||
int ModelBuilderHelper::AddEnforcedLinearConstraint() {
|
||||
const int index = model_.general_constraint_size();
|
||||
// Create the new general constraint, and force the type to indicator ct.
|
||||
// Create the mew constraint, and force the type to indicator ct.
|
||||
model_.add_general_constraint()->mutable_indicator_constraint();
|
||||
return index;
|
||||
}
|
||||
@@ -716,7 +717,8 @@ double ModelSolverHelper::variable_value(int var_index) const {
|
||||
return response_.value().variable_value(var_index);
|
||||
}
|
||||
|
||||
double ModelSolverHelper::expression_value(LinearExpr* expr) const {
|
||||
double ModelSolverHelper::expression_value(
|
||||
std::shared_ptr<LinearExpr> expr) const {
|
||||
if (!has_response()) return 0.0;
|
||||
evaluator_.Clear();
|
||||
evaluator_.AddToProcess(expr, 1.0);
|
||||
@@ -783,55 +785,68 @@ void ModelSolverHelper::SetSolverSpecificParameters(
|
||||
void ModelSolverHelper::EnableOutput(bool enabled) { solver_output_ = enabled; }
|
||||
|
||||
// Expressions.
|
||||
LinearExpr* LinearExpr::Term(LinearExpr* expr, double coeff) {
|
||||
return new AffineExpr(expr, coeff, 0.0);
|
||||
std::shared_ptr<LinearExpr> LinearExpr::Term(std::shared_ptr<LinearExpr> expr,
|
||||
double coeff) {
|
||||
return std::make_shared<AffineExpr>(expr, coeff, 0.0);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::Affine(LinearExpr* expr, double coeff,
|
||||
double constant) {
|
||||
std::shared_ptr<LinearExpr> LinearExpr::Affine(std::shared_ptr<LinearExpr> expr,
|
||||
double coeff, double constant) {
|
||||
if (coeff == 1.0 && constant == 0.0) return expr;
|
||||
return new AffineExpr(expr, coeff, constant);
|
||||
return std::make_shared<AffineExpr>(expr, coeff, constant);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::AffineCst(double value, double coeff, double constant) {
|
||||
return new FixedValue(value * coeff + constant);
|
||||
std::shared_ptr<LinearExpr> LinearExpr::AffineCst(double value, double coeff,
|
||||
double constant) {
|
||||
return std::make_shared<FixedValue>(value * coeff + constant);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::Constant(double value) { return new FixedValue(value); }
|
||||
|
||||
LinearExpr* LinearExpr::Add(LinearExpr* expr) {
|
||||
return new SumArray({this, expr}, 0.0);
|
||||
std::shared_ptr<LinearExpr> LinearExpr::Constant(double value) {
|
||||
return std::make_shared<FixedValue>(value);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::AddFloat(double cst) {
|
||||
if (cst == 0.0) return this;
|
||||
return new AffineExpr(this, 1.0, cst);
|
||||
std::shared_ptr<LinearExpr> LinearExpr::Add(std::shared_ptr<LinearExpr> expr) {
|
||||
std::vector<std::shared_ptr<LinearExpr>> exprs;
|
||||
exprs.push_back(shared_from_this());
|
||||
exprs.push_back(expr);
|
||||
return std::make_shared<SumArray>(exprs, 0.0);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::Sub(LinearExpr* expr) {
|
||||
return new WeightedSumArray({this, expr}, {1, -1}, 0.0);
|
||||
std::shared_ptr<LinearExpr> LinearExpr::AddFloat(double cst) {
|
||||
if (cst == 0.0) return shared_from_this();
|
||||
return std::make_shared<AffineExpr>(shared_from_this(), 1.0, cst);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::SubFloat(double cst) {
|
||||
if (cst == 0.0) return this;
|
||||
return new AffineExpr(this, 1.0, -cst);
|
||||
std::shared_ptr<LinearExpr> LinearExpr::Sub(std::shared_ptr<LinearExpr> expr) {
|
||||
std::vector<std::shared_ptr<LinearExpr>> exprs;
|
||||
exprs.push_back(shared_from_this());
|
||||
exprs.push_back(expr);
|
||||
std::vector<double> coeffs = {1.0, -1.0};
|
||||
return std::make_shared<WeightedSumArray>(exprs, coeffs, 0.0);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::RSubFloat(double cst) {
|
||||
return new AffineExpr(this, -1.0, cst);
|
||||
std::shared_ptr<LinearExpr> LinearExpr::SubFloat(double cst) {
|
||||
if (cst == 0.0) return shared_from_this();
|
||||
return std::make_shared<AffineExpr>(shared_from_this(), 1.0, -cst);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::MulFloat(double cst) {
|
||||
if (cst == 0.0) return new FixedValue(0.0);
|
||||
if (cst == 1.0) return this;
|
||||
return new AffineExpr(this, cst, 0.0);
|
||||
std::shared_ptr<LinearExpr> LinearExpr::RSubFloat(double cst) {
|
||||
return std::make_shared<AffineExpr>(shared_from_this(), -1.0, cst);
|
||||
}
|
||||
|
||||
LinearExpr* LinearExpr::Neg() { return new AffineExpr(this, -1, 0); }
|
||||
std::shared_ptr<LinearExpr> LinearExpr::MulFloat(double cst) {
|
||||
if (cst == 0.0) return std::make_shared<FixedValue>(0.0);
|
||||
if (cst == 1.0) return shared_from_this();
|
||||
return std::make_shared<AffineExpr>(shared_from_this(), cst, 0.0);
|
||||
}
|
||||
|
||||
std::shared_ptr<LinearExpr> LinearExpr::Neg() {
|
||||
return std::make_shared<AffineExpr>(shared_from_this(), -1, 0);
|
||||
}
|
||||
|
||||
// Expression visitors.
|
||||
|
||||
void ExprVisitor::AddToProcess(const LinearExpr* expr, double coeff) {
|
||||
void ExprVisitor::AddToProcess(std::shared_ptr<LinearExpr> expr, double coeff) {
|
||||
to_process_.push_back(std::make_pair(expr, coeff));
|
||||
}
|
||||
|
||||
@@ -842,11 +857,11 @@ void ExprVisitor::Clear() {
|
||||
offset_ = 0.0;
|
||||
}
|
||||
|
||||
void ExprFlattener::AddVarCoeff(const Variable* var, double coeff) {
|
||||
void ExprFlattener::AddVarCoeff(std::shared_ptr<Variable> var, double coeff) {
|
||||
canonical_terms_[var] += coeff;
|
||||
}
|
||||
|
||||
double ExprFlattener::Flatten(std::vector<const Variable*>* vars,
|
||||
double ExprFlattener::Flatten(std::vector<std::shared_ptr<Variable>>* vars,
|
||||
std::vector<double>* coeffs) {
|
||||
while (!to_process_.empty()) {
|
||||
const auto [expr, coeff] = to_process_.back();
|
||||
@@ -865,7 +880,7 @@ double ExprFlattener::Flatten(std::vector<const Variable*>* vars,
|
||||
return offset_;
|
||||
}
|
||||
|
||||
void ExprEvaluator::AddVarCoeff(const Variable* var, double coeff) {
|
||||
void ExprEvaluator::AddVarCoeff(std::shared_ptr<Variable> var, double coeff) {
|
||||
offset_ += coeff * helper_->variable_value(var->index());
|
||||
}
|
||||
|
||||
@@ -879,20 +894,21 @@ double ExprEvaluator::Evaluate() {
|
||||
return offset_;
|
||||
}
|
||||
|
||||
FlatExpr::FlatExpr(const LinearExpr* expr) {
|
||||
FlatExpr::FlatExpr(std::shared_ptr<LinearExpr> expr) {
|
||||
ExprFlattener lin;
|
||||
lin.AddToProcess(expr, 1.0);
|
||||
offset_ = lin.Flatten(&vars_, &coeffs_);
|
||||
}
|
||||
|
||||
FlatExpr::FlatExpr(const LinearExpr* pos, const LinearExpr* neg) {
|
||||
FlatExpr::FlatExpr(std::shared_ptr<LinearExpr> pos,
|
||||
std::shared_ptr<LinearExpr> neg) {
|
||||
ExprFlattener lin;
|
||||
lin.AddToProcess(pos, 1.0);
|
||||
lin.AddToProcess(neg, -1.0);
|
||||
offset_ = lin.Flatten(&vars_, &coeffs_);
|
||||
}
|
||||
|
||||
FlatExpr::FlatExpr(const std::vector<const Variable*>& vars,
|
||||
FlatExpr::FlatExpr(const std::vector<std::shared_ptr<Variable>>& vars,
|
||||
const std::vector<double>& coeffs, double offset)
|
||||
: vars_(vars), coeffs_(coeffs), offset_(offset) {}
|
||||
|
||||
@@ -901,13 +917,13 @@ FlatExpr::FlatExpr(double offset) : offset_(offset) {}
|
||||
std::vector<int> FlatExpr::VarIndices() const {
|
||||
std::vector<int> var_indices;
|
||||
var_indices.reserve(vars_.size());
|
||||
for (const Variable* var : vars_) {
|
||||
for (const std::shared_ptr<Variable>& var : vars_) {
|
||||
var_indices.push_back(var->index());
|
||||
}
|
||||
return var_indices;
|
||||
}
|
||||
|
||||
void FlatExpr::Visit(ExprVisitor& lin, double c) const {
|
||||
void FlatExpr::Visit(ExprVisitor& lin, double c) {
|
||||
for (int i = 0; i < vars_.size(); ++i) {
|
||||
lin.AddVarCoeff(vars_[i], coeffs_[i] * c);
|
||||
}
|
||||
@@ -966,9 +982,10 @@ std::string FlatExpr::ToString() const {
|
||||
std::string FlatExpr::DebugString() const {
|
||||
std::string s = absl::StrCat(
|
||||
"FlatExpr(",
|
||||
absl::StrJoin(vars_, ", ", [](std::string* out, const Variable* expr) {
|
||||
absl::StrAppend(out, expr->DebugString());
|
||||
}));
|
||||
absl::StrJoin(vars_, ", ",
|
||||
[](std::string* out, std::shared_ptr<Variable> expr) {
|
||||
absl::StrAppend(out, expr->DebugString());
|
||||
}));
|
||||
if (offset_ != 0.0) {
|
||||
absl::StrAppend(&s, ", offset=", offset_);
|
||||
}
|
||||
@@ -976,7 +993,7 @@ std::string FlatExpr::DebugString() const {
|
||||
return s;
|
||||
}
|
||||
|
||||
void FixedValue::Visit(ExprVisitor& lin, double c) const {
|
||||
void FixedValue::Visit(ExprVisitor& lin, double c) {
|
||||
lin.AddConstant(value_ * c);
|
||||
}
|
||||
|
||||
@@ -986,14 +1003,14 @@ std::string FixedValue::DebugString() const {
|
||||
return absl::StrCat("FixedValue(", value_, ")");
|
||||
}
|
||||
|
||||
WeightedSumArray::WeightedSumArray(const std::vector<LinearExpr*>& exprs,
|
||||
const std::vector<double>& coeffs,
|
||||
double offset)
|
||||
WeightedSumArray::WeightedSumArray(
|
||||
const std::vector<std::shared_ptr<LinearExpr>>& exprs,
|
||||
const std::vector<double>& coeffs, double offset)
|
||||
: exprs_(exprs.begin(), exprs.end()),
|
||||
coeffs_(coeffs.begin(), coeffs.end()),
|
||||
offset_(offset) {}
|
||||
|
||||
void WeightedSumArray::Visit(ExprVisitor& lin, double c) const {
|
||||
void WeightedSumArray::Visit(ExprVisitor& lin, double c) {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin.AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
}
|
||||
@@ -1047,22 +1064,44 @@ std::string WeightedSumArray::ToString() const {
|
||||
}
|
||||
|
||||
std::string WeightedSumArray::DebugString() const {
|
||||
return absl::StrCat("WeightedSumArray([",
|
||||
absl::StrJoin(exprs_, ", ",
|
||||
[](std::string* out, const LinearExpr* e) {
|
||||
absl::StrAppend(out, e->DebugString());
|
||||
}),
|
||||
"], [", absl::StrJoin(coeffs_, "], "), offset_, ")");
|
||||
return absl::StrCat(
|
||||
"WeightedSumArray([",
|
||||
absl::StrJoin(exprs_, ", ",
|
||||
[](std::string* out, std::shared_ptr<LinearExpr> e) {
|
||||
absl::StrAppend(out, e->DebugString());
|
||||
}),
|
||||
"], [", absl::StrJoin(coeffs_, "], "), offset_, ")");
|
||||
}
|
||||
|
||||
AffineExpr::AffineExpr(LinearExpr* expr, double coeff, double offset)
|
||||
AffineExpr::AffineExpr(std::shared_ptr<LinearExpr> expr, double coeff,
|
||||
double offset)
|
||||
: expr_(expr), coeff_(coeff), offset_(offset) {}
|
||||
|
||||
void AffineExpr::Visit(ExprVisitor& lin, double c) const {
|
||||
void AffineExpr::Visit(ExprVisitor& lin, double c) {
|
||||
lin.AddToProcess(expr_, c * coeff_);
|
||||
lin.AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::shared_ptr<LinearExpr> AffineExpr::AddFloat(double cst) {
|
||||
return LinearExpr::Affine(expr_, coeff_, offset_ + cst);
|
||||
}
|
||||
|
||||
std::shared_ptr<LinearExpr> AffineExpr::SubFloat(double cst) {
|
||||
return LinearExpr::Affine(expr_, coeff_, offset_ - cst);
|
||||
}
|
||||
|
||||
std::shared_ptr<LinearExpr> AffineExpr::RSubFloat(double cst) {
|
||||
return LinearExpr::Affine(expr_, -coeff_, cst - offset_);
|
||||
}
|
||||
|
||||
std::shared_ptr<LinearExpr> AffineExpr::MulFloat(double cst) {
|
||||
return LinearExpr::Affine(expr_, coeff_ * cst, offset_ * cst);
|
||||
}
|
||||
|
||||
std::shared_ptr<LinearExpr> AffineExpr::Neg() {
|
||||
return LinearExpr::Affine(expr_, -coeff_, -offset_);
|
||||
}
|
||||
|
||||
std::string AffineExpr::ToString() const {
|
||||
std::string s = "(";
|
||||
if (coeff_ == 1.0) {
|
||||
@@ -1085,36 +1124,41 @@ std::string AffineExpr::DebugString() const {
|
||||
return absl::StrCat("AffineExpr(expr=", expr_->DebugString(),
|
||||
", coeff=", coeff_, ", offset=", offset_, ")");
|
||||
}
|
||||
BoundedLinearExpression* LinearExpr::Eq(LinearExpr* rhs) {
|
||||
return new BoundedLinearExpression(this, rhs, 0.0, 0.0);
|
||||
std::shared_ptr<BoundedLinearExpression> LinearExpr::Eq(
|
||||
std::shared_ptr<LinearExpr> rhs) {
|
||||
return std::make_shared<BoundedLinearExpression>(shared_from_this(), rhs, 0.0,
|
||||
0.0);
|
||||
}
|
||||
|
||||
BoundedLinearExpression* LinearExpr::EqCst(double rhs) {
|
||||
return new BoundedLinearExpression(this, rhs, rhs);
|
||||
std::shared_ptr<BoundedLinearExpression> LinearExpr::EqCst(double rhs) {
|
||||
return std::make_shared<BoundedLinearExpression>(shared_from_this(), rhs,
|
||||
rhs);
|
||||
}
|
||||
|
||||
BoundedLinearExpression* LinearExpr::Le(LinearExpr* rhs) {
|
||||
return new BoundedLinearExpression(
|
||||
this, rhs, -std::numeric_limits<double>::infinity(), 0.0);
|
||||
std::shared_ptr<BoundedLinearExpression> LinearExpr::Le(
|
||||
std::shared_ptr<LinearExpr> rhs) {
|
||||
return std::make_shared<BoundedLinearExpression>(
|
||||
shared_from_this(), rhs, -std::numeric_limits<double>::infinity(), 0.0);
|
||||
}
|
||||
|
||||
BoundedLinearExpression* LinearExpr::LeCst(double rhs) {
|
||||
return new BoundedLinearExpression(
|
||||
this, -std::numeric_limits<double>::infinity(), rhs);
|
||||
std::shared_ptr<BoundedLinearExpression> LinearExpr::LeCst(double rhs) {
|
||||
return std::make_shared<BoundedLinearExpression>(
|
||||
shared_from_this(), -std::numeric_limits<double>::infinity(), rhs);
|
||||
}
|
||||
|
||||
BoundedLinearExpression* LinearExpr::Ge(LinearExpr* rhs) {
|
||||
return new BoundedLinearExpression(this, rhs, 0.0,
|
||||
std::numeric_limits<double>::infinity());
|
||||
std::shared_ptr<BoundedLinearExpression> LinearExpr::Ge(
|
||||
std::shared_ptr<LinearExpr> rhs) {
|
||||
return std::make_shared<BoundedLinearExpression>(
|
||||
shared_from_this(), rhs, 0.0, std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
BoundedLinearExpression* LinearExpr::GeCst(double rhs) {
|
||||
return new BoundedLinearExpression(this, rhs,
|
||||
std::numeric_limits<double>::infinity());
|
||||
std::shared_ptr<BoundedLinearExpression> LinearExpr::GeCst(double rhs) {
|
||||
return std::make_shared<BoundedLinearExpression>(
|
||||
shared_from_this(), rhs, std::numeric_limits<double>::infinity());
|
||||
}
|
||||
|
||||
bool VariableComparator::operator()(const Variable* lhs,
|
||||
const Variable* rhs) const {
|
||||
bool VariableComparator::operator()(std::shared_ptr<Variable> lhs,
|
||||
std::shared_ptr<Variable> rhs) const {
|
||||
return lhs->index() < rhs->index();
|
||||
}
|
||||
|
||||
@@ -1211,9 +1255,8 @@ void Variable::SetObjectiveCoefficient(double coeff) {
|
||||
helper_->SetVarObjectiveCoefficient(index_, coeff);
|
||||
}
|
||||
|
||||
BoundedLinearExpression::BoundedLinearExpression(const LinearExpr* expr,
|
||||
double lower_bound,
|
||||
double upper_bound) {
|
||||
BoundedLinearExpression::BoundedLinearExpression(
|
||||
std::shared_ptr<LinearExpr> expr, double lower_bound, double upper_bound) {
|
||||
FlatExpr flat_expr(expr);
|
||||
vars_ = flat_expr.vars();
|
||||
coeffs_ = flat_expr.coeffs();
|
||||
@@ -1221,10 +1264,9 @@ BoundedLinearExpression::BoundedLinearExpression(const LinearExpr* expr,
|
||||
upper_bound_ = upper_bound - flat_expr.offset();
|
||||
}
|
||||
|
||||
BoundedLinearExpression::BoundedLinearExpression(const LinearExpr* pos,
|
||||
const LinearExpr* neg,
|
||||
double lower_bound,
|
||||
double upper_bound) {
|
||||
BoundedLinearExpression::BoundedLinearExpression(
|
||||
std::shared_ptr<LinearExpr> pos, std::shared_ptr<LinearExpr> neg,
|
||||
double lower_bound, double upper_bound) {
|
||||
FlatExpr flat_expr(pos, neg);
|
||||
vars_ = flat_expr.vars();
|
||||
coeffs_ = flat_expr.coeffs();
|
||||
@@ -1232,9 +1274,9 @@ BoundedLinearExpression::BoundedLinearExpression(const LinearExpr* pos,
|
||||
upper_bound_ = upper_bound - flat_expr.offset();
|
||||
}
|
||||
|
||||
BoundedLinearExpression::BoundedLinearExpression(const LinearExpr* expr,
|
||||
int64_t lower_bound,
|
||||
int64_t upper_bound) {
|
||||
BoundedLinearExpression::BoundedLinearExpression(
|
||||
std::shared_ptr<LinearExpr> expr, int64_t lower_bound,
|
||||
int64_t upper_bound) {
|
||||
FlatExpr flat_expr(expr);
|
||||
vars_ = flat_expr.vars();
|
||||
coeffs_ = flat_expr.coeffs();
|
||||
@@ -1242,10 +1284,9 @@ BoundedLinearExpression::BoundedLinearExpression(const LinearExpr* expr,
|
||||
upper_bound_ = upper_bound - flat_expr.offset();
|
||||
}
|
||||
|
||||
BoundedLinearExpression::BoundedLinearExpression(const LinearExpr* pos,
|
||||
const LinearExpr* neg,
|
||||
int64_t lower_bound,
|
||||
int64_t upper_bound) {
|
||||
BoundedLinearExpression::BoundedLinearExpression(
|
||||
std::shared_ptr<LinearExpr> pos, std::shared_ptr<LinearExpr> neg,
|
||||
int64_t lower_bound, int64_t upper_bound) {
|
||||
FlatExpr flat_expr(pos, neg);
|
||||
vars_ = flat_expr.vars();
|
||||
coeffs_ = flat_expr.coeffs();
|
||||
@@ -1255,7 +1296,8 @@ BoundedLinearExpression::BoundedLinearExpression(const LinearExpr* pos,
|
||||
|
||||
double BoundedLinearExpression::lower_bound() const { return lower_bound_; }
|
||||
double BoundedLinearExpression::upper_bound() const { return upper_bound_; }
|
||||
const std::vector<const Variable*>& BoundedLinearExpression::vars() const {
|
||||
const std::vector<std::shared_ptr<Variable>>& BoundedLinearExpression::vars()
|
||||
const {
|
||||
return vars_;
|
||||
}
|
||||
const std::vector<double>& BoundedLinearExpression::coeffs() const {
|
||||
@@ -1302,13 +1344,13 @@ std::string BoundedLinearExpression::ToString() const {
|
||||
}
|
||||
if (lower_bound_ == upper_bound_) {
|
||||
return absl::StrCat(s, " == ", lower_bound_);
|
||||
} else if (lower_bound_ == std::numeric_limits<double>::min()) {
|
||||
if (upper_bound_ == std::numeric_limits<double>::max()) {
|
||||
return absl::StrCat("True (unbounded expr ", s, ")");
|
||||
} else if (lower_bound_ == -std::numeric_limits<double>::infinity()) {
|
||||
if (upper_bound_ == std::numeric_limits<double>::infinity()) {
|
||||
return absl::StrCat("-inf <= ", s, " <= inf");
|
||||
} else {
|
||||
return absl::StrCat(s, " <= ", upper_bound_);
|
||||
}
|
||||
} else if (upper_bound_ == std::numeric_limits<double>::max()) {
|
||||
} else if (upper_bound_ == std::numeric_limits<double>::infinity()) {
|
||||
return absl::StrCat(s, " >= ", lower_bound_);
|
||||
} else {
|
||||
return absl::StrCat(lower_bound_, " <= ", s, " <= ", upper_bound_);
|
||||
@@ -1316,14 +1358,14 @@ std::string BoundedLinearExpression::ToString() const {
|
||||
}
|
||||
|
||||
std::string BoundedLinearExpression::DebugString() const {
|
||||
return absl::StrCat("BoundedLinearExpression(vars=[",
|
||||
absl::StrJoin(vars_, ", ",
|
||||
[](std::string* out, const Variable* var) {
|
||||
absl::StrAppend(out, var->DebugString());
|
||||
}),
|
||||
"], coeffs=[", absl::StrJoin(coeffs_, ", "),
|
||||
"], lower_bound=", lower_bound_,
|
||||
", upper_bound=", upper_bound_, ")");
|
||||
return absl::StrCat(
|
||||
"BoundedLinearExpression(vars=[",
|
||||
absl::StrJoin(vars_, ", ",
|
||||
[](std::string* out, std::shared_ptr<Variable> var) {
|
||||
absl::StrAppend(out, var->DebugString());
|
||||
}),
|
||||
"], coeffs=[", absl::StrJoin(coeffs_, ", "),
|
||||
"], lower_bound=", lower_bound_, ", upper_bound=", upper_bound_, ")");
|
||||
}
|
||||
|
||||
bool BoundedLinearExpression::CastToBool(bool* result) const {
|
||||
|
||||
@@ -14,9 +14,11 @@
|
||||
#ifndef OR_TOOLS_LINEAR_SOLVER_WRAPPERS_MODEL_BUILDER_HELPER_H_
|
||||
#define OR_TOOLS_LINEAR_SOLVER_WRAPPERS_MODEL_BUILDER_HELPER_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstdint>
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -43,70 +45,75 @@ class ModelBuilderHelper;
|
||||
class ModelSolverHelper;
|
||||
class Variable;
|
||||
|
||||
// A linear expression that can be either integer or floating point.
|
||||
class LinearExpr {
|
||||
// A linear expression that containing variables and constants.
|
||||
class LinearExpr : public std::enable_shared_from_this<LinearExpr> {
|
||||
public:
|
||||
virtual ~LinearExpr() = default;
|
||||
virtual void Visit(ExprVisitor& /*lin*/, double /*c*/) const = 0;
|
||||
virtual void Visit(ExprVisitor& /*lin*/, double /*c*/) = 0;
|
||||
virtual std::string ToString() const = 0;
|
||||
virtual std::string DebugString() const = 0;
|
||||
|
||||
static LinearExpr* Term(LinearExpr* expr, double coeff);
|
||||
static LinearExpr* Affine(LinearExpr* expr, double coeff, double constant);
|
||||
static LinearExpr* AffineCst(double value, double coeff, double constant);
|
||||
static LinearExpr* Constant(double value);
|
||||
static std::shared_ptr<LinearExpr> Term(std::shared_ptr<LinearExpr> expr,
|
||||
double coeff);
|
||||
static std::shared_ptr<LinearExpr> Affine(std::shared_ptr<LinearExpr> expr,
|
||||
double coeff, double constant);
|
||||
static std::shared_ptr<LinearExpr> AffineCst(double value, double coeff,
|
||||
double constant);
|
||||
static std::shared_ptr<LinearExpr> Constant(double value);
|
||||
|
||||
LinearExpr* Add(LinearExpr* expr);
|
||||
LinearExpr* AddFloat(double cst);
|
||||
LinearExpr* Sub(LinearExpr* expr);
|
||||
LinearExpr* SubFloat(double cst);
|
||||
LinearExpr* RSubFloat(double cst);
|
||||
LinearExpr* MulFloat(double cst);
|
||||
LinearExpr* Neg();
|
||||
std::shared_ptr<LinearExpr> Add(std::shared_ptr<LinearExpr> expr);
|
||||
std::shared_ptr<LinearExpr> AddFloat(double cst);
|
||||
std::shared_ptr<LinearExpr> Sub(std::shared_ptr<LinearExpr> expr);
|
||||
std::shared_ptr<LinearExpr> SubFloat(double cst);
|
||||
std::shared_ptr<LinearExpr> RSubFloat(double cst);
|
||||
std::shared_ptr<LinearExpr> MulFloat(double cst);
|
||||
std::shared_ptr<LinearExpr> Neg();
|
||||
|
||||
BoundedLinearExpression* Eq(LinearExpr* rhs);
|
||||
BoundedLinearExpression* EqCst(double rhs);
|
||||
BoundedLinearExpression* Ge(LinearExpr* rhs);
|
||||
BoundedLinearExpression* GeCst(double rhs);
|
||||
BoundedLinearExpression* Le(LinearExpr* rhs);
|
||||
BoundedLinearExpression* LeCst(double rhs);
|
||||
std::shared_ptr<BoundedLinearExpression> Eq(std::shared_ptr<LinearExpr> rhs);
|
||||
std::shared_ptr<BoundedLinearExpression> EqCst(double rhs);
|
||||
std::shared_ptr<BoundedLinearExpression> Ge(std::shared_ptr<LinearExpr> rhs);
|
||||
std::shared_ptr<BoundedLinearExpression> GeCst(double rhs);
|
||||
std::shared_ptr<BoundedLinearExpression> Le(std::shared_ptr<LinearExpr> rhs);
|
||||
std::shared_ptr<BoundedLinearExpression> LeCst(double rhs);
|
||||
};
|
||||
|
||||
// Compare the indices of variables.
|
||||
struct VariableComparator {
|
||||
bool operator()(const Variable* lhs, const Variable* rhs) const;
|
||||
bool operator()(std::shared_ptr<Variable> lhs,
|
||||
std::shared_ptr<Variable> rhs) const;
|
||||
};
|
||||
|
||||
// A visitor class to parse a floating point linear expression.
|
||||
class ExprVisitor {
|
||||
public:
|
||||
virtual ~ExprVisitor() = default;
|
||||
void AddToProcess(const LinearExpr* expr, double coeff);
|
||||
void AddToProcess(std::shared_ptr<LinearExpr> expr, double coeff);
|
||||
void AddConstant(double constant);
|
||||
virtual void AddVarCoeff(const Variable* var, double coeff) = 0;
|
||||
virtual void AddVarCoeff(std::shared_ptr<Variable> var, double coeff) = 0;
|
||||
void Clear();
|
||||
|
||||
protected:
|
||||
std::vector<std::pair<const LinearExpr*, double>> to_process_;
|
||||
std::vector<std::pair<std::shared_ptr<LinearExpr>, double>> to_process_;
|
||||
double offset_ = 0;
|
||||
};
|
||||
|
||||
class ExprFlattener : public ExprVisitor {
|
||||
public:
|
||||
~ExprFlattener() override = default;
|
||||
void AddVarCoeff(const Variable* var, double coeff) override;
|
||||
double Flatten(std::vector<const Variable*>* vars,
|
||||
void AddVarCoeff(std::shared_ptr<Variable> var, double coeff) override;
|
||||
double Flatten(std::vector<std::shared_ptr<Variable>>* vars,
|
||||
std::vector<double>* coeffs);
|
||||
|
||||
private:
|
||||
absl::btree_map<const Variable*, double, VariableComparator> canonical_terms_;
|
||||
absl::btree_map<std::shared_ptr<Variable>, double, VariableComparator>
|
||||
canonical_terms_;
|
||||
};
|
||||
|
||||
class ExprEvaluator : public ExprVisitor {
|
||||
public:
|
||||
explicit ExprEvaluator(ModelSolverHelper* helper) : helper_(helper) {}
|
||||
~ExprEvaluator() override = default;
|
||||
void AddVarCoeff(const Variable* var, double coeff) override;
|
||||
void AddVarCoeff(std::shared_ptr<Variable> var, double coeff) override;
|
||||
double Evaluate();
|
||||
|
||||
private:
|
||||
@@ -116,23 +123,23 @@ class ExprEvaluator : public ExprVisitor {
|
||||
// A flat linear expression sum(vars[i] * coeffs[i]) + offset
|
||||
class FlatExpr : public LinearExpr {
|
||||
public:
|
||||
explicit FlatExpr(const LinearExpr* expr);
|
||||
explicit FlatExpr(std::shared_ptr<LinearExpr> expr);
|
||||
// Flatten pos - neg.
|
||||
FlatExpr(const LinearExpr* pos, const LinearExpr* neg);
|
||||
FlatExpr(const std::vector<const Variable*>&, const std::vector<double>&,
|
||||
double);
|
||||
FlatExpr(std::shared_ptr<LinearExpr> pos, std::shared_ptr<LinearExpr> neg);
|
||||
FlatExpr(const std::vector<std::shared_ptr<Variable>>&,
|
||||
const std::vector<double>&, double);
|
||||
explicit FlatExpr(double offset);
|
||||
const std::vector<const Variable*>& vars() const { return vars_; }
|
||||
const std::vector<std::shared_ptr<Variable>>& vars() const { return vars_; }
|
||||
std::vector<int> VarIndices() const;
|
||||
const std::vector<double>& coeffs() const { return coeffs_; }
|
||||
double offset() const { return offset_; }
|
||||
|
||||
void Visit(ExprVisitor& lin, double c) const override;
|
||||
void Visit(ExprVisitor& lin, double c) override;
|
||||
std::string ToString() const override;
|
||||
std::string DebugString() const override;
|
||||
|
||||
private:
|
||||
std::vector<const Variable*> vars_;
|
||||
std::vector<std::shared_ptr<Variable>> vars_;
|
||||
std::vector<double> coeffs_;
|
||||
double offset_;
|
||||
};
|
||||
@@ -141,11 +148,12 @@ class FlatExpr : public LinearExpr {
|
||||
// double offsets.
|
||||
class SumArray : public LinearExpr {
|
||||
public:
|
||||
explicit SumArray(const std::vector<LinearExpr*>& exprs, double offset)
|
||||
: exprs_(exprs.begin(), exprs.end()), offset_(offset) {}
|
||||
explicit SumArray(std::vector<std::shared_ptr<LinearExpr>> exprs,
|
||||
double offset)
|
||||
: exprs_(std::move(exprs)), offset_(offset) {}
|
||||
~SumArray() override = default;
|
||||
|
||||
void Visit(ExprVisitor& lin, double c) const override {
|
||||
void Visit(ExprVisitor& lin, double c) override {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin.AddToProcess(exprs_[i], c);
|
||||
}
|
||||
@@ -181,9 +189,10 @@ class SumArray : public LinearExpr {
|
||||
std::string DebugString() const override {
|
||||
std::string s = absl::StrCat(
|
||||
"SumArray(",
|
||||
absl::StrJoin(exprs_, ", ", [](std::string* out, LinearExpr* expr) {
|
||||
absl::StrAppend(out, expr->DebugString());
|
||||
}));
|
||||
absl::StrJoin(exprs_, ", ",
|
||||
[](std::string* out, std::shared_ptr<LinearExpr> expr) {
|
||||
absl::StrAppend(out, expr->DebugString());
|
||||
}));
|
||||
if (offset_ != 0.0) {
|
||||
absl::StrAppend(&s, ", offset=", offset_);
|
||||
}
|
||||
@@ -191,24 +200,29 @@ class SumArray : public LinearExpr {
|
||||
return s;
|
||||
}
|
||||
|
||||
void AddInPlace(std::shared_ptr<LinearExpr> expr) { exprs_.push_back(expr); }
|
||||
void AddFloatInPlace(double cst) { offset_ += cst; }
|
||||
int num_exprs() const { return exprs_.size(); }
|
||||
double offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
const absl::FixedArray<LinearExpr*, 2> exprs_;
|
||||
const double offset_;
|
||||
std::vector<std::shared_ptr<LinearExpr>> exprs_;
|
||||
double offset_;
|
||||
};
|
||||
|
||||
// A class to hold a weighted sum of floating point linear expressions.
|
||||
class WeightedSumArray : public LinearExpr {
|
||||
public:
|
||||
WeightedSumArray(const std::vector<LinearExpr*>& exprs,
|
||||
WeightedSumArray(const std::vector<std::shared_ptr<LinearExpr>>& exprs,
|
||||
const std::vector<double>& coeffs, double offset);
|
||||
~WeightedSumArray() override = default;
|
||||
|
||||
void Visit(ExprVisitor& lin, double c) const override;
|
||||
void Visit(ExprVisitor& lin, double c) override;
|
||||
std::string ToString() const override;
|
||||
std::string DebugString() const override;
|
||||
|
||||
private:
|
||||
const absl::FixedArray<LinearExpr*, 2> exprs_;
|
||||
const absl::FixedArray<std::shared_ptr<LinearExpr>, 2> exprs_;
|
||||
const absl::FixedArray<double, 2> coeffs_;
|
||||
double offset_;
|
||||
};
|
||||
@@ -216,20 +230,26 @@ class WeightedSumArray : public LinearExpr {
|
||||
// A class to hold linear_expr * a = b.
|
||||
class AffineExpr : public LinearExpr {
|
||||
public:
|
||||
AffineExpr(LinearExpr* expr, double coeff, double offset);
|
||||
AffineExpr(std::shared_ptr<LinearExpr> expr, double coeff, double offset);
|
||||
~AffineExpr() override = default;
|
||||
|
||||
void Visit(ExprVisitor& lin, double c) const override;
|
||||
void Visit(ExprVisitor& lin, double c) override;
|
||||
|
||||
std::string ToString() const override;
|
||||
std::string DebugString() const override;
|
||||
|
||||
LinearExpr* expression() const { return expr_; }
|
||||
std::shared_ptr<LinearExpr> expression() const { return expr_; }
|
||||
double coefficient() const { return coeff_; }
|
||||
double offset() const { return offset_; }
|
||||
|
||||
std::shared_ptr<LinearExpr> AddFloat(double cst);
|
||||
std::shared_ptr<LinearExpr> SubFloat(double cst);
|
||||
std::shared_ptr<LinearExpr> RSubFloat(double cst);
|
||||
std::shared_ptr<LinearExpr> MulFloat(double cst);
|
||||
std::shared_ptr<LinearExpr> Neg();
|
||||
|
||||
private:
|
||||
LinearExpr* expr_;
|
||||
std::shared_ptr<LinearExpr> expr_;
|
||||
double coeff_;
|
||||
double offset_;
|
||||
};
|
||||
@@ -240,7 +260,7 @@ class FixedValue : public LinearExpr {
|
||||
explicit FixedValue(double value) : value_(value) {}
|
||||
~FixedValue() override = default;
|
||||
|
||||
void Visit(ExprVisitor& lin, double c) const override;
|
||||
void Visit(ExprVisitor& lin, double c) override;
|
||||
|
||||
std::string ToString() const override;
|
||||
std::string DebugString() const override;
|
||||
@@ -275,8 +295,10 @@ class Variable : public LinearExpr {
|
||||
double objective_coefficient() const;
|
||||
void SetObjectiveCoefficient(double coeff);
|
||||
|
||||
void Visit(ExprVisitor& lin, double c) const override {
|
||||
lin.AddVarCoeff(this, c);
|
||||
void Visit(ExprVisitor& lin, double c) override {
|
||||
std::shared_ptr<Variable> var =
|
||||
std::static_pointer_cast<Variable>(shared_from_this());
|
||||
lin.AddVarCoeff(var, c);
|
||||
}
|
||||
|
||||
std::string ToString() const override;
|
||||
@@ -291,34 +313,36 @@ class Variable : public LinearExpr {
|
||||
};
|
||||
|
||||
template <typename H>
|
||||
H AbslHashValue(H h, const Variable* i) {
|
||||
H AbslHashValue(H h, std::shared_ptr<Variable> i) {
|
||||
return H::combine(std::move(h), i->index());
|
||||
}
|
||||
|
||||
// A class to hold a linear expression with bounds.
|
||||
class BoundedLinearExpression {
|
||||
public:
|
||||
BoundedLinearExpression(const LinearExpr* expr, double lower_bound,
|
||||
BoundedLinearExpression(std::shared_ptr<LinearExpr> expr, double lower_bound,
|
||||
double upper_bound);
|
||||
BoundedLinearExpression(const LinearExpr* pos, const LinearExpr* neg,
|
||||
double lower_bound, double upper_bound);
|
||||
BoundedLinearExpression(const LinearExpr* expr, int64_t lower_bound,
|
||||
BoundedLinearExpression(std::shared_ptr<LinearExpr> pos,
|
||||
std::shared_ptr<LinearExpr> neg, double lower_bound,
|
||||
double upper_bound);
|
||||
BoundedLinearExpression(std::shared_ptr<LinearExpr> expr, int64_t lower_bound,
|
||||
int64_t upper_bound);
|
||||
BoundedLinearExpression(std::shared_ptr<LinearExpr> pos,
|
||||
std::shared_ptr<LinearExpr> neg, int64_t lower_bound,
|
||||
int64_t upper_bound);
|
||||
BoundedLinearExpression(const LinearExpr* pos, const LinearExpr* neg,
|
||||
int64_t lower_bound, int64_t upper_bound);
|
||||
|
||||
~BoundedLinearExpression() = default;
|
||||
|
||||
double lower_bound() const;
|
||||
double upper_bound() const;
|
||||
const std::vector<const Variable*>& vars() const;
|
||||
const std::vector<std::shared_ptr<Variable>>& vars() const;
|
||||
const std::vector<double>& coeffs() const;
|
||||
std::string ToString() const;
|
||||
std::string DebugString() const;
|
||||
bool CastToBool(bool* result) const;
|
||||
|
||||
private:
|
||||
std::vector<const Variable*> vars_;
|
||||
std::vector<std::shared_ptr<Variable>> vars_;
|
||||
std::vector<double> coeffs_;
|
||||
double lower_bound_;
|
||||
double upper_bound_;
|
||||
@@ -485,7 +509,7 @@ class ModelSolverHelper {
|
||||
double objective_value() const;
|
||||
double best_objective_bound() const;
|
||||
double variable_value(int var_index) const;
|
||||
double expression_value(LinearExpr* expr) const;
|
||||
double expression_value(std::shared_ptr<LinearExpr> expr) const;
|
||||
double reduced_cost(int var_index) const;
|
||||
double dual_value(int ct_index) const;
|
||||
double activity(int ct_index);
|
||||
|
||||
Reference in New Issue
Block a user