[CP-SAT] revamp python implementation: introduce proper FloatLinearExpr class, move most of the expressions classes to C++
This commit is contained in:
@@ -698,9 +698,12 @@ void LoadSubcircuitConstraint(int num_nodes, const std::vector<int>& tails,
|
||||
|
||||
std::function<void(Model*)> CircuitCovering(
|
||||
absl::Span<const std::vector<Literal>> graph,
|
||||
const std::vector<int>& distinguished_nodes) {
|
||||
return [=, graph = std::vector<std::vector<Literal>>(
|
||||
graph.begin(), graph.end())](Model* model) {
|
||||
absl::Span<const int> distinguished_nodes) {
|
||||
return [=,
|
||||
distinguished_nodes = std::vector<int>(distinguished_nodes.begin(),
|
||||
distinguished_nodes.end()),
|
||||
graph = std::vector<std::vector<Literal>>(
|
||||
graph.begin(), graph.end())](Model* model) {
|
||||
CircuitCoveringPropagator* constraint =
|
||||
new CircuitCoveringPropagator(graph, distinguished_nodes, model);
|
||||
constraint->RegisterWith(model->GetOrCreate<GenericLiteralWatcher>());
|
||||
|
||||
@@ -255,7 +255,7 @@ std::function<void(Model*)> ExactlyOnePerRowAndPerColumn(
|
||||
absl::Span<const std::vector<Literal>> graph);
|
||||
std::function<void(Model*)> CircuitCovering(
|
||||
absl::Span<const std::vector<Literal>> graph,
|
||||
const std::vector<int>& distinguished_nodes);
|
||||
absl::Span<const int> distinguished_nodes);
|
||||
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -17,11 +17,27 @@ load("@pip_deps//:requirements.bzl", "requirement")
|
||||
load("@pybind11_bazel//:build_defs.bzl", "pybind_extension")
|
||||
load("@rules_python//python:defs.bzl", "py_library", "py_test")
|
||||
|
||||
cc_library(
|
||||
name = "linear_expr",
|
||||
srcs = ["linear_expr.cc"],
|
||||
hdrs = ["linear_expr.h"],
|
||||
deps = [
|
||||
"//ortools/sat:cp_model_cc_proto",
|
||||
"//ortools/util:sorted_interval_list",
|
||||
"@com_google_absl//absl/container:btree",
|
||||
"@com_google_absl//absl/container:fixed_array",
|
||||
"@com_google_absl//absl/container:flat_hash_map",
|
||||
"@com_google_absl//absl/log:check",
|
||||
"@com_google_absl//absl/strings",
|
||||
],
|
||||
)
|
||||
|
||||
pybind_extension(
|
||||
name = "swig_helper",
|
||||
srcs = ["swig_helper.cc"],
|
||||
visibility = ["//visibility:public"],
|
||||
deps = [
|
||||
":linear_expr",
|
||||
"//ortools/sat:cp_model_cc_proto",
|
||||
"//ortools/sat:sat_parameters_cc_proto",
|
||||
"//ortools/sat:swig_helper",
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -366,30 +366,6 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertEqual(x, prod.expression())
|
||||
self.assertEqual(4, prod.coefficient())
|
||||
|
||||
def testSimplification2(self) -> None:
|
||||
print("testSimplification2")
|
||||
model = cp_model.CpModel()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = 2 * (x * 2)
|
||||
self.assertEqual(x, prod.expression())
|
||||
self.assertEqual(4, prod.coefficient())
|
||||
|
||||
def testSimplification3(self) -> None:
|
||||
print("testSimplification3")
|
||||
model = cp_model.CpModel()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = (2 * x) * 2
|
||||
self.assertEqual(x, prod.expression())
|
||||
self.assertEqual(4, prod.coefficient())
|
||||
|
||||
def testSimplification4(self) -> None:
|
||||
print("testSimplification4")
|
||||
model = cp_model.CpModel()
|
||||
x = model.new_int_var(-10, 10, "x")
|
||||
prod = 2 * (2 * x)
|
||||
self.assertEqual(x, prod.expression())
|
||||
self.assertEqual(4, prod.coefficient())
|
||||
|
||||
def testLinearNonEqualWithConstant(self) -> None:
|
||||
print("testLinearNonEqualWithConstant")
|
||||
model = cp_model.CpModel()
|
||||
@@ -459,7 +435,7 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertEqual(16.1, solver.objective_value)
|
||||
|
||||
def testNaturalApiMaximizeComplex(self) -> None:
|
||||
print("testNaturalApiMaximizeFloat")
|
||||
print("testNaturalApiMaximizeComplex")
|
||||
model = cp_model.CpModel()
|
||||
x1 = model.new_bool_var("x1")
|
||||
x2 = model.new_bool_var("x1")
|
||||
@@ -1169,7 +1145,7 @@ class CpModelTest(absltest.TestCase):
|
||||
self.assertEqual(str(x < 2), "x <= 1")
|
||||
self.assertEqual(str(x != 2), "x != 2")
|
||||
self.assertEqual(str(x * 3), "(3 * x)")
|
||||
self.assertEqual(str(-x), "-x")
|
||||
self.assertEqual(str(-x), "(-x)")
|
||||
self.assertEqual(str(x + 3), "(x + 3)")
|
||||
self.assertEqual(str(x <= cp_model.INT_MAX), "True (unbounded expr x)")
|
||||
self.assertEqual(str(x != 9223372036854775807), "x <= 9223372036854775806")
|
||||
@@ -1177,14 +1153,14 @@ class CpModelTest(absltest.TestCase):
|
||||
y = model.new_int_var(0, 4, "y")
|
||||
self.assertEqual(
|
||||
str(cp_model.LinearExpr.weighted_sum([x, y + 1, 2], [1, -2, 3])),
|
||||
"x - 2 * (y + 1) + 6",
|
||||
"(x - 2 * (y + 1) + 6)",
|
||||
)
|
||||
self.assertEqual(str(cp_model.LinearExpr.term(x, 3)), "(3 * x)")
|
||||
self.assertEqual(str(x != y), "(x + -y) != 0")
|
||||
self.assertEqual(str(x != y), "(x - y) != 0")
|
||||
self.assertEqual(
|
||||
"0 <= x <= 10", str(cp_model.BoundedLinearExpression(x, [0, 10]))
|
||||
"0 <= x <= 10",
|
||||
str(cp_model.BoundedLinearExpression(x, cp_model.Domain(0, 10))),
|
||||
)
|
||||
print(str(model))
|
||||
b = model.new_bool_var("b")
|
||||
self.assertEqual(str(cp_model.LinearExpr.term(b.negated(), 3)), "(3 * not(b))")
|
||||
|
||||
@@ -1198,15 +1174,15 @@ class CpModelTest(absltest.TestCase):
|
||||
y = model.new_int_var(0, 3, "y")
|
||||
z = model.new_int_var(0, 3, "z")
|
||||
self.assertEqual(repr(x), "x(0..4)")
|
||||
self.assertEqual(repr(x * 2), "ProductCst(x(0..4), 2)")
|
||||
self.assertEqual(repr(x + y), "sum(x(0..4), y(0..3))")
|
||||
self.assertEqual(repr(x * 2), "IntAffine(expr=x(0..4), coeff=2, offset=0)")
|
||||
self.assertEqual(repr(x + y), "IntSum(x(0..4), y(0..3), 0)")
|
||||
self.assertEqual(
|
||||
repr(cp_model.LinearExpr.sum([x, y, z])),
|
||||
"SumArray(x(0..4), y(0..3), z(0..3), 0)",
|
||||
"IntSum(x(0..4), y(0..3), z(0..3), 0)",
|
||||
)
|
||||
self.assertEqual(
|
||||
repr(cp_model.LinearExpr.weighted_sum([x, y, 2], [1, 2, 3])),
|
||||
"weighted_sum([x(0..4), y(0..3)], [1, 2], 6)",
|
||||
"IntWeightedSum([x(0..4), y(0..3)], [1, 2], 6)",
|
||||
)
|
||||
i = model.new_interval_var(x, 2, y, "i")
|
||||
self.assertEqual(repr(i), "i(start = x, size = 2, end = y)")
|
||||
|
||||
609
ortools/sat/python/linear_expr.cc
Normal file
609
ortools/sat/python/linear_expr.cc
Normal file
@@ -0,0 +1,609 @@
|
||||
// Copyright 2010-2024 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.
|
||||
|
||||
#include "ortools/sat/python/linear_expr.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstdlib>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "ortools/util/sorted_interval_list.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
namespace python {
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::Sum(
|
||||
const std::vector<FloatExprOrValue>& exprs) {
|
||||
return Sum(exprs, 0.0);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::Sum(
|
||||
const std::vector<FloatExprOrValue>& exprs, double cst) {
|
||||
std::vector<FloatLinearExpr*> lin_exprs;
|
||||
for (const FloatExprOrValue& choice : exprs) {
|
||||
if (choice.expr != nullptr) {
|
||||
lin_exprs.push_back(choice.expr);
|
||||
} else {
|
||||
cst += choice.value;
|
||||
}
|
||||
}
|
||||
if (lin_exprs.empty()) return new FloatConstant(cst);
|
||||
if (lin_exprs.size() == 1) return Affine(lin_exprs[0], 1.0, cst);
|
||||
return new FloatWeightedSum(lin_exprs, cst);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::WeightedSum(
|
||||
const std::vector<FloatExprOrValue>& exprs,
|
||||
const std::vector<double>& coeffs) {
|
||||
return WeightedSum(exprs, coeffs, 0.0);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::WeightedSum(
|
||||
const std::vector<FloatExprOrValue>& exprs,
|
||||
const std::vector<double>& coeffs, double cst) {
|
||||
std::vector<FloatLinearExpr*> lin_exprs;
|
||||
std::vector<double> lin_coeffs;
|
||||
for (int i = 0; i < exprs.size(); ++i) {
|
||||
if (exprs[i].expr != nullptr) {
|
||||
lin_exprs.push_back(exprs[i].expr);
|
||||
lin_coeffs.push_back(coeffs[i]);
|
||||
} else {
|
||||
cst += exprs[i].value * coeffs[i];
|
||||
}
|
||||
}
|
||||
|
||||
if (lin_exprs.empty()) return new FloatConstant(cst);
|
||||
if (lin_exprs.size() == 1) {
|
||||
return Affine(lin_exprs[0], lin_coeffs[0], cst);
|
||||
}
|
||||
return new FloatWeightedSum(lin_exprs, lin_coeffs, cst);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::Term(FloatLinearExpr* expr, double coeff) {
|
||||
return new FloatAffine(expr, coeff, 0.0);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::Affine(FloatLinearExpr* expr, double coeff,
|
||||
double offset) {
|
||||
return new FloatAffine(expr, coeff, offset);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::Constant(double value) {
|
||||
return new FloatConstant(value);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::FloatAddCst(double cst) {
|
||||
if (cst == 0.0) return this;
|
||||
return new FloatAffine(this, 1.0, cst);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::FloatAdd(FloatLinearExpr* other) {
|
||||
std::vector<FloatLinearExpr*> exprs;
|
||||
exprs.push_back(this);
|
||||
exprs.push_back(other);
|
||||
return new FloatWeightedSum(exprs, 0);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::FloatSubCst(double cst) {
|
||||
if (cst == 0.0) return this;
|
||||
return new FloatAffine(this, 1.0, -cst);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::FloatSub(FloatLinearExpr* other) {
|
||||
std::vector<FloatLinearExpr*> exprs;
|
||||
exprs.push_back(this);
|
||||
exprs.push_back(other);
|
||||
return new FloatWeightedSum(exprs, {1, -1}, 0);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::FloatRSub(FloatLinearExpr* other) {
|
||||
std::vector<FloatLinearExpr*> exprs;
|
||||
exprs.push_back(this);
|
||||
exprs.push_back(other);
|
||||
return new FloatWeightedSum(exprs, {-1, 1}, 0);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::FloatRSubCst(double cst) {
|
||||
return new FloatAffine(this, -1.0, cst);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::FloatMulCst(double cst) {
|
||||
if (cst == 0.0) return Sum({});
|
||||
if (cst == 1.0) return this;
|
||||
return new FloatAffine(this, cst, 0.0);
|
||||
}
|
||||
|
||||
FloatLinearExpr* FloatLinearExpr::FloatNeg() {
|
||||
return new FloatAffine(this, -1.0, 0.0);
|
||||
}
|
||||
|
||||
void FloatExprVisitor::AddToProcess(FloatLinearExpr* expr, double coeff) {
|
||||
to_process_.push_back(std::make_pair(expr, coeff));
|
||||
}
|
||||
void FloatExprVisitor::AddConstant(double constant) { offset_ += constant; }
|
||||
void FloatExprVisitor::AddVarCoeff(BaseIntVar* var, double coeff) {
|
||||
canonical_terms_[var] += coeff;
|
||||
}
|
||||
double FloatExprVisitor::Process(FloatLinearExpr* expr,
|
||||
std::vector<BaseIntVar*>* vars,
|
||||
std::vector<double>* coeffs) {
|
||||
AddToProcess(expr, 1.0);
|
||||
while (!to_process_.empty()) {
|
||||
const auto [expr, coeff] = to_process_.back();
|
||||
to_process_.pop_back();
|
||||
expr->VisitAsFloat(this, coeff);
|
||||
}
|
||||
|
||||
vars->clear();
|
||||
coeffs->clear();
|
||||
for (const auto& [var, coeff] : canonical_terms_) {
|
||||
if (coeff == 0) continue;
|
||||
vars->push_back(var);
|
||||
coeffs->push_back(coeff);
|
||||
}
|
||||
|
||||
return offset_;
|
||||
}
|
||||
|
||||
CanonicalFloatExpression::CanonicalFloatExpression(FloatLinearExpr* expr) {
|
||||
FloatExprVisitor lin;
|
||||
offset_ = lin.Process(expr, &vars_, &coeffs_);
|
||||
}
|
||||
|
||||
void FloatConstant::VisitAsFloat(FloatExprVisitor* lin, double c) {
|
||||
lin->AddConstant(value_ * c);
|
||||
}
|
||||
|
||||
std::string FloatConstant::ToString() const { return absl::StrCat(value_); }
|
||||
|
||||
std::string FloatConstant::DebugString() const {
|
||||
return absl::StrCat("FloatConstant(", value_, ")");
|
||||
}
|
||||
|
||||
FloatWeightedSum::FloatWeightedSum(const std::vector<FloatLinearExpr*>& exprs,
|
||||
double offset)
|
||||
: exprs_(exprs.begin(), exprs.end()),
|
||||
coeffs_(exprs.size(), 1),
|
||||
offset_(offset) {}
|
||||
|
||||
FloatWeightedSum::FloatWeightedSum(const std::vector<FloatLinearExpr*>& exprs,
|
||||
const std::vector<double>& coeffs,
|
||||
double offset)
|
||||
: exprs_(exprs.begin(), exprs.end()),
|
||||
coeffs_(coeffs.begin(), coeffs.end()),
|
||||
offset_(offset) {}
|
||||
|
||||
void FloatWeightedSum::VisitAsFloat(FloatExprVisitor* lin, double c) {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
}
|
||||
lin->AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::string FloatWeightedSum::ToString() const {
|
||||
if (exprs_.empty()) {
|
||||
return absl::StrCat(offset_);
|
||||
}
|
||||
std::string s = "(";
|
||||
bool first_printed = true;
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
if (coeffs_[i] == 0.0) continue;
|
||||
if (first_printed) {
|
||||
first_printed = false;
|
||||
if (coeffs_[i] == 1.0) {
|
||||
absl::StrAppend(&s, exprs_[i]->ToString());
|
||||
} else if (coeffs_[i] == -1.0) {
|
||||
absl::StrAppend(&s, "-", exprs_[i]->ToString());
|
||||
} else {
|
||||
absl::StrAppend(&s, coeffs_[i], " * ", exprs_[i]->ToString());
|
||||
}
|
||||
} else {
|
||||
if (coeffs_[i] == 1.0) {
|
||||
absl::StrAppend(&s, " + ", exprs_[i]->ToString());
|
||||
} else if (coeffs_[i] == -1.0) {
|
||||
absl::StrAppend(&s, " - ", exprs_[i]->ToString());
|
||||
} else if (coeffs_[i] > 0.0) {
|
||||
absl::StrAppend(&s, " + ", coeffs_[i], " * ", exprs_[i]->ToString());
|
||||
} else {
|
||||
absl::StrAppend(&s, " - ", -coeffs_[i], " * ", exprs_[i]->ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there are no terms, just print the offset.
|
||||
if (first_printed) {
|
||||
return absl::StrCat(offset_);
|
||||
}
|
||||
|
||||
// If there is an offset, print it.
|
||||
if (offset_ != 0.0) {
|
||||
if (offset_ > 0.0) {
|
||||
absl::StrAppend(&s, " + ", offset_);
|
||||
} else {
|
||||
absl::StrAppend(&s, " - ", -offset_);
|
||||
}
|
||||
}
|
||||
absl::StrAppend(&s, ")");
|
||||
return s;
|
||||
}
|
||||
|
||||
FloatAffine::FloatAffine(FloatLinearExpr* expr, double coeff, double offset)
|
||||
: expr_(expr), coeff_(coeff), offset_(offset) {}
|
||||
|
||||
void FloatAffine::VisitAsFloat(FloatExprVisitor* lin, double c) {
|
||||
lin->AddToProcess(expr_, c * coeff_);
|
||||
lin->AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::string FloatAffine::ToString() const {
|
||||
std::string s = "(";
|
||||
if (coeff_ == 1.0) {
|
||||
absl::StrAppend(&s, expr_->ToString());
|
||||
} else if (coeff_ == -1.0) {
|
||||
absl::StrAppend(&s, "-", expr_->ToString());
|
||||
} else {
|
||||
absl::StrAppend(&s, coeff_, " * ", expr_->ToString());
|
||||
}
|
||||
if (offset_ > 0.0) {
|
||||
absl::StrAppend(&s, " + ", offset_);
|
||||
} else if (offset_ < 0.0) {
|
||||
absl::StrAppend(&s, " - ", -offset_);
|
||||
}
|
||||
absl::StrAppend(&s, ")");
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string FloatAffine::DebugString() const {
|
||||
return absl::StrCat("FloatAffine(expr=", expr_->DebugString(),
|
||||
", coeff=", coeff_, ", offset=", offset_, ")");
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::Sum(const std::vector<IntExprOrValue>& exprs) {
|
||||
return Sum(exprs, 0);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::Sum(const std::vector<IntExprOrValue>& exprs,
|
||||
int64_t cst) {
|
||||
std::vector<IntLinExpr*> lin_exprs;
|
||||
for (const IntExprOrValue& choice : exprs) {
|
||||
if (choice.expr != nullptr) {
|
||||
lin_exprs.push_back(choice.expr);
|
||||
} else {
|
||||
cst += choice.value;
|
||||
}
|
||||
}
|
||||
if (lin_exprs.empty()) return new IntConstant(cst);
|
||||
if (lin_exprs.size() == 1) return Affine(lin_exprs[0], 1, cst);
|
||||
return new IntSum(lin_exprs, cst);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::WeightedSum(const std::vector<IntExprOrValue>& exprs,
|
||||
const std::vector<int64_t>& coeffs) {
|
||||
return WeightedSum(exprs, coeffs, 0);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::WeightedSum(const std::vector<IntExprOrValue>& exprs,
|
||||
const std::vector<int64_t>& coeffs,
|
||||
int64_t cst) {
|
||||
std::vector<IntLinExpr*> lin_exprs;
|
||||
std::vector<int64_t> lin_coeffs;
|
||||
for (int i = 0; i < exprs.size(); ++i) {
|
||||
if (exprs[i].expr != nullptr) {
|
||||
lin_exprs.push_back(exprs[i].expr);
|
||||
lin_coeffs.push_back(coeffs[i]);
|
||||
} else {
|
||||
cst += exprs[i].value * coeffs[i];
|
||||
}
|
||||
}
|
||||
if (lin_exprs.empty()) return new IntConstant(cst);
|
||||
if (lin_exprs.size() == 1) {
|
||||
return IntLinExpr::Affine(lin_exprs[0], lin_coeffs[0], cst);
|
||||
}
|
||||
return new IntWeightedSum(lin_exprs, lin_coeffs, cst);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::Term(IntLinExpr* expr, int64_t coeff) {
|
||||
return Affine(expr, coeff, 0);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::Affine(IntLinExpr* expr, int64_t coeff,
|
||||
int64_t offset) {
|
||||
if (coeff == 1 && offset == 0) return expr;
|
||||
if (coeff == 0) return new IntConstant(offset);
|
||||
return new IntAffine(expr, coeff, offset);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::Constant(int64_t value) {
|
||||
return new IntConstant(value);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::IntAddCst(int64_t cst) {
|
||||
if (cst == 0) return this;
|
||||
return new IntAffine(this, 1, cst);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::IntAdd(IntLinExpr* other) {
|
||||
std::vector<IntLinExpr*> exprs;
|
||||
exprs.push_back(this);
|
||||
exprs.push_back(other);
|
||||
return new IntSum(exprs, 0);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::IntSubCst(int64_t cst) {
|
||||
if (cst == 0) return this;
|
||||
return new IntAffine(this, 1, -cst);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::IntSub(IntLinExpr* other) {
|
||||
std::vector<IntLinExpr*> exprs;
|
||||
exprs.push_back(this);
|
||||
exprs.push_back(other);
|
||||
return new IntWeightedSum(exprs, {1, -1}, 0);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::IntRSubCst(int64_t cst) {
|
||||
return new IntAffine(this, -1, cst);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::IntMulCst(int64_t cst) {
|
||||
if (cst == 0) return new IntConstant(0);
|
||||
if (cst == 1) return this;
|
||||
return new IntAffine(this, cst, 0);
|
||||
}
|
||||
|
||||
IntLinExpr* IntLinExpr::IntNeg() { return new IntAffine(this, -1, 0); }
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::Eq(IntLinExpr* other) {
|
||||
return new BoundedLinearExpression(this, other, Domain(0));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::EqCst(int64_t cst) {
|
||||
return new BoundedLinearExpression(this, Domain(cst));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::Ne(IntLinExpr* other) {
|
||||
return new BoundedLinearExpression(this, other, Domain(0).Complement());
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::NeCst(int64_t cst) {
|
||||
return new BoundedLinearExpression(this, Domain(cst).Complement());
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::Le(IntLinExpr* other) {
|
||||
return new BoundedLinearExpression(
|
||||
this, other, Domain(std::numeric_limits<int64_t>::min(), 0));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::LeCst(int64_t cst) {
|
||||
return new BoundedLinearExpression(
|
||||
this, Domain(std::numeric_limits<int64_t>::min(), cst));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::Lt(IntLinExpr* other) {
|
||||
return new BoundedLinearExpression(
|
||||
this, other, Domain(std::numeric_limits<int64_t>::min(), -1));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::LtCst(int64_t cst) {
|
||||
return new BoundedLinearExpression(
|
||||
this, Domain(std::numeric_limits<int64_t>::min(), cst - 1));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::Ge(IntLinExpr* other) {
|
||||
return new BoundedLinearExpression(
|
||||
this, other, Domain(0, std::numeric_limits<int64_t>::max()));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::GeCst(int64_t cst) {
|
||||
return new BoundedLinearExpression(
|
||||
this, Domain(cst, std::numeric_limits<int64_t>::max()));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::Gt(IntLinExpr* other) {
|
||||
return new BoundedLinearExpression(
|
||||
this, other, Domain(1, std::numeric_limits<int64_t>::max()));
|
||||
}
|
||||
|
||||
BoundedLinearExpression* IntLinExpr::GtCst(int64_t cst) {
|
||||
return new BoundedLinearExpression(
|
||||
this, Domain(cst + 1, std::numeric_limits<int64_t>::max()));
|
||||
}
|
||||
|
||||
void IntExprVisitor::AddToProcess(IntLinExpr* expr, int64_t coeff) {
|
||||
to_process_.push_back(std::make_pair(expr, coeff));
|
||||
}
|
||||
|
||||
void IntExprVisitor::AddConstant(int64_t constant) { offset_ += constant; }
|
||||
|
||||
void IntExprVisitor::AddVarCoeff(BaseIntVar* var, int64_t coeff) {
|
||||
canonical_terms_[var] += coeff;
|
||||
}
|
||||
|
||||
void IntExprVisitor::ProcessAll() {
|
||||
while (!to_process_.empty()) {
|
||||
const auto [expr, coeff] = to_process_.back();
|
||||
to_process_.pop_back();
|
||||
expr->VisitAsInt(this, coeff);
|
||||
}
|
||||
}
|
||||
|
||||
int64_t IntExprVisitor::Process(std::vector<BaseIntVar*>* vars,
|
||||
std::vector<int64_t>* coeffs) {
|
||||
ProcessAll();
|
||||
vars->clear();
|
||||
coeffs->clear();
|
||||
for (const auto& [var, coeff] : canonical_terms_) {
|
||||
if (coeff == 0) continue;
|
||||
vars->push_back(var);
|
||||
coeffs->push_back(coeff);
|
||||
}
|
||||
|
||||
return offset_;
|
||||
}
|
||||
|
||||
int64_t IntExprVisitor::Evaluate(IntLinExpr* expr,
|
||||
const CpSolverResponse& solution) {
|
||||
AddToProcess(expr, 1);
|
||||
ProcessAll();
|
||||
int64_t value = offset_;
|
||||
for (const auto& [var, coeff] : canonical_terms_) {
|
||||
if (coeff == 0) continue;
|
||||
value += coeff * solution.solution(var->index());
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
bool BaseIntVarComparator::operator()(const BaseIntVar* lhs,
|
||||
const BaseIntVar* rhs) const {
|
||||
return lhs->index() < rhs->index();
|
||||
}
|
||||
|
||||
BoundedLinearExpression::BoundedLinearExpression(IntLinExpr* expr,
|
||||
const Domain& bounds)
|
||||
: bounds_(bounds) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(expr, 1);
|
||||
offset_ = lin.Process(&vars_, &coeffs_);
|
||||
}
|
||||
|
||||
BoundedLinearExpression::BoundedLinearExpression(IntLinExpr* pos,
|
||||
IntLinExpr* neg,
|
||||
const Domain& bounds)
|
||||
: bounds_(bounds) {
|
||||
IntExprVisitor lin;
|
||||
lin.AddToProcess(pos, 1);
|
||||
lin.AddToProcess(neg, -1);
|
||||
offset_ = lin.Process(&vars_, &coeffs_);
|
||||
}
|
||||
|
||||
BoundedLinearExpression::BoundedLinearExpression(int64_t offset,
|
||||
const Domain& bounds)
|
||||
: bounds_(bounds), offset_(offset) {}
|
||||
|
||||
const Domain& BoundedLinearExpression::bounds() const { return bounds_; }
|
||||
const std::vector<BaseIntVar*>& BoundedLinearExpression::vars() const {
|
||||
return vars_;
|
||||
}
|
||||
const std::vector<int64_t>& BoundedLinearExpression::coeffs() const {
|
||||
return coeffs_;
|
||||
}
|
||||
int64_t BoundedLinearExpression::offset() const { return offset_; }
|
||||
|
||||
std::string BoundedLinearExpression::ToString() const {
|
||||
std::string s;
|
||||
if (vars_.empty()) {
|
||||
s = absl::StrCat(offset_);
|
||||
} else if (vars_.size() == 1) {
|
||||
const std::string var_name = vars_[0]->ToString();
|
||||
if (coeffs_[0] == 1) {
|
||||
s = var_name;
|
||||
} else if (coeffs_[0] == -1) {
|
||||
s = absl::StrCat("-", var_name);
|
||||
} else {
|
||||
s = absl::StrCat(coeffs_[0], " * ", var_name);
|
||||
}
|
||||
if (offset_ > 0) {
|
||||
absl::StrAppend(&s, " + ", offset_);
|
||||
} else if (offset_ < 0) {
|
||||
absl::StrAppend(&s, " - ", -offset_);
|
||||
}
|
||||
} else {
|
||||
s = "(";
|
||||
for (int i = 0; i < vars_.size(); ++i) {
|
||||
const std::string var_name = vars_[i]->ToString();
|
||||
if (i == 0) {
|
||||
if (coeffs_[i] == 1) {
|
||||
absl::StrAppend(&s, var_name);
|
||||
} else if (coeffs_[i] == -1) {
|
||||
absl::StrAppend(&s, "-", var_name);
|
||||
} else {
|
||||
absl::StrAppend(&s, coeffs_[i], " * ", var_name);
|
||||
}
|
||||
} else {
|
||||
if (coeffs_[i] == 1) {
|
||||
absl::StrAppend(&s, " + ", var_name);
|
||||
} else if (coeffs_[i] == -1) {
|
||||
absl::StrAppend(&s, " - ", var_name);
|
||||
} else if (coeffs_[i] > 1) {
|
||||
absl::StrAppend(&s, " + ", coeffs_[i], " * ", var_name);
|
||||
} else {
|
||||
absl::StrAppend(&s, " - ", -coeffs_[i], " * ", var_name);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (offset_ > 0) {
|
||||
absl::StrAppend(&s, " + ", offset_);
|
||||
} else if (offset_ < 0) {
|
||||
absl::StrAppend(&s, " - ", -offset_);
|
||||
}
|
||||
absl::StrAppend(&s, ")");
|
||||
}
|
||||
if (bounds_.IsFixed()) {
|
||||
absl::StrAppend(&s, " == ", bounds_.Min());
|
||||
} else if (bounds_.NumIntervals() == 1) {
|
||||
if (bounds_.Min() == std::numeric_limits<int64_t>::min()) {
|
||||
if (bounds_.Max() == std::numeric_limits<int64_t>::max()) {
|
||||
return absl::StrCat("True (unbounded expr ", s, ")");
|
||||
} else {
|
||||
absl::StrAppend(&s, " <= ", bounds_.Max());
|
||||
}
|
||||
} else if (bounds_.Max() == std::numeric_limits<int64_t>::max()) {
|
||||
absl::StrAppend(&s, " >= ", bounds_.Min());
|
||||
} else {
|
||||
return absl::StrCat(bounds_.Min(), " <= ", s, " <= ", bounds_.Max());
|
||||
}
|
||||
} else if (bounds_.Complement().IsFixed()) {
|
||||
absl::StrAppend(&s, " != ", bounds_.Complement().Min());
|
||||
} else {
|
||||
absl::StrAppend(&s, " in ", bounds_.ToString());
|
||||
}
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string BoundedLinearExpression::DebugString() const {
|
||||
return absl::StrCat("BoundedLinearExpression(vars=[",
|
||||
absl::StrJoin(vars_, ", ",
|
||||
[](std::string* out, BaseIntVar* var) {
|
||||
absl::StrAppend(out, var->DebugString());
|
||||
}),
|
||||
"], coeffs=[", absl::StrJoin(coeffs_, ", "),
|
||||
"], offset=", offset_, ", bounds=", bounds_.ToString(),
|
||||
")");
|
||||
}
|
||||
|
||||
bool BoundedLinearExpression::CastToBool(bool* result) const {
|
||||
const bool is_zero = bounds_.IsFixed() && bounds_.FixedValue() == 0;
|
||||
const Domain complement = bounds_.Complement();
|
||||
const bool is_all_but_zero =
|
||||
complement.IsFixed() && complement.FixedValue() == 0;
|
||||
if (is_zero || is_all_but_zero) {
|
||||
if (vars_.empty()) {
|
||||
*result = is_zero;
|
||||
return true;
|
||||
} else if (vars_.size() == 2 && coeffs_[0] + coeffs_[1] == 0 &&
|
||||
std::abs(coeffs_[0]) == 1) {
|
||||
*result = is_all_but_zero;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} // namespace python
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
581
ortools/sat/python/linear_expr.h
Normal file
581
ortools/sat/python/linear_expr.h
Normal file
@@ -0,0 +1,581 @@
|
||||
// Copyright 2010-2024 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.
|
||||
|
||||
#ifndef OR_TOOLS_SAT_PYTHON_LINEAR_EXPR_H_
|
||||
#define OR_TOOLS_SAT_PYTHON_LINEAR_EXPR_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/container/btree_map.h"
|
||||
#include "absl/container/fixed_array.h"
|
||||
#include "absl/log/check.h"
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "absl/strings/str_join.h"
|
||||
#include "ortools/sat/cp_model.pb.h"
|
||||
#include "ortools/util/sorted_interval_list.h"
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
namespace python {
|
||||
|
||||
class BoundedLinearExpression;
|
||||
class CanonicalFloatExpression;
|
||||
class FloatExprVisitor;
|
||||
class FloatLinearExpr;
|
||||
class IntExprVisitor;
|
||||
class IntLinExpr;
|
||||
class BaseIntVar;
|
||||
class NotBooleanVariable;
|
||||
|
||||
// A class to hold an floating point linear expression or a double constant.
|
||||
struct FloatExprOrValue {
|
||||
explicit FloatExprOrValue(FloatLinearExpr* e) : expr(e) {}
|
||||
explicit FloatExprOrValue(double v) : value(v) {}
|
||||
|
||||
FloatLinearExpr* expr = nullptr;
|
||||
double value = 0;
|
||||
};
|
||||
|
||||
// A linear expression that can be either integer or floating point.
|
||||
class FloatLinearExpr {
|
||||
public:
|
||||
virtual ~FloatLinearExpr() = default;
|
||||
virtual void VisitAsFloat(FloatExprVisitor* /*lin*/, double /*c*/) {}
|
||||
virtual bool is_integer() const { return false; }
|
||||
virtual std::string ToString() const { return "FloatLinearExpr"; }
|
||||
virtual std::string DebugString() const { return ToString(); }
|
||||
|
||||
static FloatLinearExpr* Sum(const std::vector<FloatExprOrValue>& exprs);
|
||||
static FloatLinearExpr* Sum(const std::vector<FloatExprOrValue>& exprs,
|
||||
double cst);
|
||||
static FloatLinearExpr* WeightedSum(
|
||||
const std::vector<FloatExprOrValue>& exprs,
|
||||
const std::vector<double>& coeffs);
|
||||
static FloatLinearExpr* WeightedSum(
|
||||
const std::vector<FloatExprOrValue>& exprs,
|
||||
const std::vector<double>& coeffs, double cst);
|
||||
static FloatLinearExpr* Term(FloatLinearExpr* expr, double coeff);
|
||||
static FloatLinearExpr* Affine(FloatLinearExpr* expr, double coeff,
|
||||
double offset);
|
||||
static FloatLinearExpr* Constant(double value);
|
||||
|
||||
FloatLinearExpr* FloatAddCst(double cst);
|
||||
FloatLinearExpr* FloatAdd(FloatLinearExpr* other);
|
||||
FloatLinearExpr* FloatSubCst(double cst);
|
||||
FloatLinearExpr* FloatSub(FloatLinearExpr* other);
|
||||
FloatLinearExpr* FloatRSub(FloatLinearExpr* other);
|
||||
FloatLinearExpr* FloatRSubCst(double cst);
|
||||
FloatLinearExpr* FloatMulCst(double cst);
|
||||
FloatLinearExpr* FloatNeg();
|
||||
};
|
||||
|
||||
// Compare the indices of variables.
|
||||
struct BaseIntVarComparator {
|
||||
bool operator()(const BaseIntVar* lhs, const BaseIntVar* rhs) const;
|
||||
};
|
||||
|
||||
// A visitor class to process a floating point linear expression.
|
||||
class FloatExprVisitor {
|
||||
public:
|
||||
void AddToProcess(FloatLinearExpr* expr, double coeff);
|
||||
void AddConstant(double constant);
|
||||
void AddVarCoeff(BaseIntVar* var, double coeff);
|
||||
double Process(FloatLinearExpr* expr, std::vector<BaseIntVar*>* vars,
|
||||
std::vector<double>* coeffs);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<FloatLinearExpr*, double>> to_process_;
|
||||
absl::btree_map<BaseIntVar*, double, BaseIntVarComparator> canonical_terms_;
|
||||
double offset_ = 0;
|
||||
};
|
||||
|
||||
// A class to build a canonical floating point linear expression.
|
||||
class CanonicalFloatExpression {
|
||||
public:
|
||||
explicit CanonicalFloatExpression(FloatLinearExpr* expr);
|
||||
const std::vector<BaseIntVar*>& vars() const { return vars_; }
|
||||
const std::vector<double>& coeffs() const { return coeffs_; }
|
||||
double offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
double offset_;
|
||||
std::vector<BaseIntVar*> vars_;
|
||||
std::vector<double> coeffs_;
|
||||
};
|
||||
|
||||
// A class to hold a constant.
|
||||
class FloatConstant : public FloatLinearExpr {
|
||||
public:
|
||||
explicit FloatConstant(double value) : value_(value) {}
|
||||
~FloatConstant() override = default;
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override;
|
||||
std::string ToString() const override;
|
||||
std::string DebugString() const override;
|
||||
|
||||
private:
|
||||
double value_;
|
||||
};
|
||||
|
||||
// A class to hold a weighted sum of floating point linear expressions.
|
||||
class FloatWeightedSum : public FloatLinearExpr {
|
||||
public:
|
||||
FloatWeightedSum(const std::vector<FloatLinearExpr*>& exprs, double offset);
|
||||
FloatWeightedSum(const std::vector<FloatLinearExpr*>& exprs,
|
||||
const std::vector<double>& coeffs, double offset);
|
||||
~FloatWeightedSum() override = default;
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override;
|
||||
std::string ToString() const override;
|
||||
|
||||
private:
|
||||
const absl::FixedArray<FloatLinearExpr*, 2> exprs_;
|
||||
const absl::FixedArray<double, 2> coeffs_;
|
||||
double offset_;
|
||||
};
|
||||
|
||||
// A class to hold float_exr * a = b.
|
||||
class FloatAffine : public FloatLinearExpr {
|
||||
public:
|
||||
FloatAffine(FloatLinearExpr* expr, double coeff, double offset);
|
||||
~FloatAffine() override = default;
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override;
|
||||
std::string ToString() const override;
|
||||
std::string DebugString() const override;
|
||||
|
||||
FloatLinearExpr* expression() const { return expr_; }
|
||||
double coefficient() const { return coeff_; }
|
||||
double offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
FloatLinearExpr* expr_;
|
||||
double coeff_;
|
||||
double offset_;
|
||||
};
|
||||
|
||||
// A struct to hold an integer linear expression or an integer constant.
|
||||
struct IntExprOrValue {
|
||||
explicit IntExprOrValue(IntLinExpr* e) : expr(e) {}
|
||||
explicit IntExprOrValue(int64_t v) : value(v) {}
|
||||
|
||||
IntLinExpr* expr = nullptr;
|
||||
int64_t value = 0;
|
||||
};
|
||||
|
||||
class IntLinExpr : public FloatLinearExpr {
|
||||
public:
|
||||
~IntLinExpr() override = default;
|
||||
virtual void VisitAsInt(IntExprVisitor* /*lin*/, int64_t /*c*/) {}
|
||||
bool is_integer() const override { return true; }
|
||||
std::string ToString() const override { return "IntLinExpr"; }
|
||||
|
||||
static IntLinExpr* Sum(const std::vector<IntLinExpr*>& exprs);
|
||||
static IntLinExpr* Sum(const std::vector<IntLinExpr*>& exprs, int64_t cst);
|
||||
static IntLinExpr* Sum(const std::vector<IntExprOrValue>& exprs, int64_t cst);
|
||||
static IntLinExpr* Sum(const std::vector<IntExprOrValue>& exprs);
|
||||
static IntLinExpr* WeightedSum(const std::vector<IntExprOrValue>& exprs,
|
||||
const std::vector<int64_t>& coeffs);
|
||||
static IntLinExpr* WeightedSum(const std::vector<IntExprOrValue>& exprs,
|
||||
const std::vector<int64_t>& coeffs,
|
||||
int64_t cst);
|
||||
static IntLinExpr* Term(IntLinExpr* expr, int64_t coeff);
|
||||
static IntLinExpr* Affine(IntLinExpr* expr, int64_t coeff, int64_t offset);
|
||||
static IntLinExpr* Constant(int64_t value);
|
||||
|
||||
IntLinExpr* IntAddCst(int64_t cst);
|
||||
IntLinExpr* IntAdd(IntLinExpr* other);
|
||||
IntLinExpr* IntSubCst(int64_t cst);
|
||||
IntLinExpr* IntSub(IntLinExpr* other);
|
||||
IntLinExpr* IntRSubCst(int64_t cst);
|
||||
IntLinExpr* IntMulCst(int64_t cst);
|
||||
IntLinExpr* IntNeg();
|
||||
|
||||
BoundedLinearExpression* EqCst(int64_t cst);
|
||||
BoundedLinearExpression* NeCst(int64_t cst);
|
||||
BoundedLinearExpression* GeCst(int64_t cst);
|
||||
BoundedLinearExpression* LeCst(int64_t cst);
|
||||
BoundedLinearExpression* LtCst(int64_t cst);
|
||||
BoundedLinearExpression* GtCst(int64_t cst);
|
||||
BoundedLinearExpression* Eq(IntLinExpr* other);
|
||||
BoundedLinearExpression* Ne(IntLinExpr* other);
|
||||
BoundedLinearExpression* Ge(IntLinExpr* other);
|
||||
BoundedLinearExpression* Le(IntLinExpr* other);
|
||||
BoundedLinearExpression* Lt(IntLinExpr* other);
|
||||
BoundedLinearExpression* Gt(IntLinExpr* other);
|
||||
};
|
||||
|
||||
// A visitor class to process an integer linear expression.
|
||||
class IntExprVisitor {
|
||||
public:
|
||||
void AddToProcess(IntLinExpr* expr, int64_t coeff);
|
||||
void AddConstant(int64_t constant);
|
||||
void AddVarCoeff(BaseIntVar* var, int64_t coeff);
|
||||
void ProcessAll();
|
||||
int64_t Process(std::vector<BaseIntVar*>* vars, std::vector<int64_t>* coeffs);
|
||||
int64_t Evaluate(IntLinExpr* expr, const CpSolverResponse& solution);
|
||||
|
||||
private:
|
||||
std::vector<std::pair<IntLinExpr*, int64_t>> to_process_;
|
||||
absl::btree_map<BaseIntVar*, int64_t, BaseIntVarComparator> canonical_terms_;
|
||||
int64_t offset_ = 0;
|
||||
};
|
||||
|
||||
// A class to hold a linear expression with bounds.
|
||||
class BoundedLinearExpression {
|
||||
public:
|
||||
BoundedLinearExpression(IntLinExpr* expr, const Domain& bounds);
|
||||
|
||||
BoundedLinearExpression(IntLinExpr* pos, IntLinExpr* neg,
|
||||
const Domain& bounds);
|
||||
BoundedLinearExpression(int64_t offset, const Domain& bounds);
|
||||
~BoundedLinearExpression() = default;
|
||||
|
||||
const Domain& bounds() const;
|
||||
const std::vector<BaseIntVar*>& vars() const;
|
||||
const std::vector<int64_t>& coeffs() const;
|
||||
int64_t offset() const;
|
||||
std::string ToString() const;
|
||||
std::string DebugString() const;
|
||||
bool CastToBool(bool* result) const;
|
||||
|
||||
private:
|
||||
Domain bounds_;
|
||||
int64_t offset_;
|
||||
std::vector<BaseIntVar*> vars_;
|
||||
std::vector<int64_t> coeffs_;
|
||||
};
|
||||
|
||||
// A class to hold a constant.
|
||||
class IntConstant : public IntLinExpr {
|
||||
public:
|
||||
explicit IntConstant(int64_t value) : value_(value) {}
|
||||
~IntConstant() override = default;
|
||||
void VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
lin->AddConstant(value_ * c);
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
lin->AddConstant(value_ * c);
|
||||
}
|
||||
|
||||
std::string ToString() const override { return absl::StrCat(value_); }
|
||||
|
||||
std::string DebugString() const override {
|
||||
return absl::StrCat("IntConstant(", value_, ")");
|
||||
}
|
||||
|
||||
private:
|
||||
int64_t value_;
|
||||
};
|
||||
|
||||
// A class to hold a sum of integer linear expressions.
|
||||
class IntSum : public IntLinExpr {
|
||||
public:
|
||||
IntSum(const std::vector<IntLinExpr*>& exprs, int64_t offset)
|
||||
: exprs_(exprs.begin(), exprs.end()), offset_(offset) {}
|
||||
~IntSum() override = default;
|
||||
|
||||
void VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], c);
|
||||
}
|
||||
lin->AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], c);
|
||||
}
|
||||
lin->AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
if (exprs_.empty()) {
|
||||
return absl::StrCat(offset_);
|
||||
}
|
||||
std::string s = "(";
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
if (i > 0) {
|
||||
absl::StrAppend(&s, " + ");
|
||||
}
|
||||
absl::StrAppend(&s, exprs_[i]->ToString());
|
||||
}
|
||||
if (offset_ != 0) {
|
||||
if (offset_ > 0) {
|
||||
absl::StrAppend(&s, " + ", offset_);
|
||||
} else {
|
||||
absl::StrAppend(&s, " - ", -offset_);
|
||||
}
|
||||
}
|
||||
absl::StrAppend(&s, ")");
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string DebugString() const override {
|
||||
return absl::StrCat("IntSum(",
|
||||
absl::StrJoin(exprs_, ", ",
|
||||
[](std::string* out, IntLinExpr* expr) {
|
||||
absl::StrAppend(out,
|
||||
expr->DebugString());
|
||||
}),
|
||||
", ", offset_, ")");
|
||||
}
|
||||
|
||||
private:
|
||||
const absl::FixedArray<IntLinExpr*, 2> exprs_;
|
||||
int64_t offset_;
|
||||
};
|
||||
|
||||
// A class to hold a weighted sum of integer linear expressions.
|
||||
class IntWeightedSum : public IntLinExpr {
|
||||
public:
|
||||
IntWeightedSum(const std::vector<IntLinExpr*>& exprs,
|
||||
const std::vector<int64_t>& coeffs, int64_t offset)
|
||||
: exprs_(exprs.begin(), exprs.end()),
|
||||
coeffs_(coeffs.begin(), coeffs.end()),
|
||||
offset_(offset) {}
|
||||
~IntWeightedSum() override = default;
|
||||
|
||||
void VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
}
|
||||
lin->AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
lin->AddToProcess(exprs_[i], coeffs_[i] * c);
|
||||
}
|
||||
lin->AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
if (exprs_.empty()) {
|
||||
return absl::StrCat(offset_);
|
||||
}
|
||||
std::string s = "(";
|
||||
bool first_printed = true;
|
||||
for (int i = 0; i < exprs_.size(); ++i) {
|
||||
if (coeffs_[i] == 0) continue;
|
||||
if (first_printed) {
|
||||
first_printed = false;
|
||||
if (coeffs_[i] == 1) {
|
||||
absl::StrAppend(&s, exprs_[i]->ToString());
|
||||
} else if (coeffs_[i] == -1) {
|
||||
absl::StrAppend(&s, "-", exprs_[i]->ToString());
|
||||
} else {
|
||||
absl::StrAppend(&s, coeffs_[i], " * ", exprs_[i]->ToString());
|
||||
}
|
||||
} else {
|
||||
if (coeffs_[i] == 1) {
|
||||
absl::StrAppend(&s, " + ", exprs_[i]->ToString());
|
||||
} else if (coeffs_[i] == -1) {
|
||||
absl::StrAppend(&s, " - ", exprs_[i]->ToString());
|
||||
} else if (coeffs_[i] > 1) {
|
||||
absl::StrAppend(&s, " + ", coeffs_[i], " * ", exprs_[i]->ToString());
|
||||
} else {
|
||||
absl::StrAppend(&s, " - ", -coeffs_[i], " * ", exprs_[i]->ToString());
|
||||
}
|
||||
}
|
||||
}
|
||||
// If there are no terms, just print the offset.
|
||||
if (first_printed) {
|
||||
return absl::StrCat(offset_);
|
||||
}
|
||||
|
||||
// If there is an offset, print it.
|
||||
if (offset_ != 0) {
|
||||
if (offset_ > 0) {
|
||||
absl::StrAppend(&s, " + ", offset_);
|
||||
} else {
|
||||
absl::StrAppend(&s, " - ", -offset_);
|
||||
}
|
||||
}
|
||||
absl::StrAppend(&s, ")");
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string DebugString() const override {
|
||||
return absl::StrCat(
|
||||
"IntWeightedSum([",
|
||||
absl::StrJoin(exprs_, ", ",
|
||||
[](std::string* out, IntLinExpr* expr) {
|
||||
absl::StrAppend(out, expr->DebugString());
|
||||
}),
|
||||
"], [", absl::StrJoin(coeffs_, ", "), "], ", offset_, ")");
|
||||
}
|
||||
|
||||
private:
|
||||
const absl::FixedArray<IntLinExpr*, 2> exprs_;
|
||||
const absl::FixedArray<int64_t, 2> coeffs_;
|
||||
int64_t offset_;
|
||||
};
|
||||
|
||||
// A class to hold int_exr * a = b.
|
||||
class IntAffine : public IntLinExpr {
|
||||
public:
|
||||
IntAffine(IntLinExpr* expr, int64_t coeff, int64_t offset)
|
||||
: expr_(expr), coeff_(coeff), offset_(offset) {}
|
||||
~IntAffine() override = default;
|
||||
|
||||
void VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
lin->AddToProcess(expr_, c * coeff_);
|
||||
lin->AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
lin->AddToProcess(expr_, c * coeff_);
|
||||
lin->AddConstant(offset_ * c);
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
std::string s = "(";
|
||||
if (coeff_ == 1) {
|
||||
absl::StrAppend(&s, expr_->ToString());
|
||||
} else if (coeff_ == -1) {
|
||||
absl::StrAppend(&s, "-", expr_->ToString());
|
||||
} else {
|
||||
absl::StrAppend(&s, coeff_, " * ", expr_->ToString());
|
||||
}
|
||||
if (offset_ > 0) {
|
||||
absl::StrAppend(&s, " + ", offset_);
|
||||
} else if (offset_ < 0) {
|
||||
absl::StrAppend(&s, " - ", -offset_);
|
||||
}
|
||||
absl::StrAppend(&s, ")");
|
||||
return s;
|
||||
}
|
||||
|
||||
std::string DebugString() const override {
|
||||
return absl::StrCat("IntAffine(expr=", expr_->DebugString(),
|
||||
", coeff=", coeff_, ", offset=", offset_, ")");
|
||||
}
|
||||
|
||||
IntLinExpr* expression() const { return expr_; }
|
||||
int64_t coefficient() const { return coeff_; }
|
||||
int64_t offset() const { return offset_; }
|
||||
|
||||
private:
|
||||
IntLinExpr* expr_;
|
||||
int64_t coeff_;
|
||||
int64_t offset_;
|
||||
};
|
||||
|
||||
// A Boolean literal (a Boolean variable or its negation).
|
||||
class Literal {
|
||||
public:
|
||||
virtual ~Literal() = default;
|
||||
virtual int index() const = 0;
|
||||
virtual Literal* negated() = 0;
|
||||
};
|
||||
|
||||
// A class to hold a variable index.
|
||||
class BaseIntVar : public IntLinExpr, public Literal {
|
||||
public:
|
||||
explicit BaseIntVar(int index)
|
||||
: index_(index), is_boolean_(false), negated_(nullptr) {
|
||||
DCHECK_GE(index, 0);
|
||||
}
|
||||
BaseIntVar(int index, bool is_boolean)
|
||||
: index_(index), is_boolean_(is_boolean), negated_(nullptr) {
|
||||
DCHECK_GE(index, 0);
|
||||
}
|
||||
|
||||
~BaseIntVar() override = default;
|
||||
|
||||
int index() const override { return index_; }
|
||||
|
||||
void VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
lin->AddVarCoeff(this, c);
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
lin->AddVarCoeff(this, c);
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
if (is_boolean_) {
|
||||
return absl::StrCat("BooleanBaseIntVar(", index_, ")");
|
||||
} else {
|
||||
return absl::StrCat("BaseIntVar(", index_, ")");
|
||||
}
|
||||
}
|
||||
|
||||
std::string DebugString() const override {
|
||||
return absl::StrCat("BaseIntVar(index=", index_,
|
||||
", is_boolean=", is_boolean_, ")");
|
||||
}
|
||||
|
||||
Literal* negated() override;
|
||||
|
||||
bool is_boolean() const { return is_boolean_; }
|
||||
|
||||
bool operator<(const BaseIntVar& other) const {
|
||||
return index_ < other.index_;
|
||||
}
|
||||
|
||||
protected:
|
||||
const int index_;
|
||||
bool is_boolean_;
|
||||
Literal* negated_;
|
||||
};
|
||||
|
||||
// A class to hold a negated variable index.
|
||||
class NotBooleanVariable : public IntLinExpr, public Literal {
|
||||
public:
|
||||
explicit NotBooleanVariable(BaseIntVar* var) : var_(var) {}
|
||||
~NotBooleanVariable() override = default;
|
||||
|
||||
int index() const override { return -var_->index() - 1; }
|
||||
|
||||
void VisitAsInt(IntExprVisitor* lin, int64_t c) override {
|
||||
lin->AddVarCoeff(var_, -c);
|
||||
lin->AddConstant(c);
|
||||
}
|
||||
|
||||
void VisitAsFloat(FloatExprVisitor* lin, double c) override {
|
||||
lin->AddVarCoeff(var_, -c);
|
||||
lin->AddConstant(c);
|
||||
}
|
||||
|
||||
std::string ToString() const override {
|
||||
return absl::StrCat("not(", var_->ToString(), ")");
|
||||
}
|
||||
|
||||
Literal* negated() override { return var_; }
|
||||
|
||||
std::string DebugString() const override {
|
||||
return absl::StrCat("NotBooleanVariable(index=", var_->index(), ")");
|
||||
}
|
||||
|
||||
private:
|
||||
BaseIntVar* var_;
|
||||
};
|
||||
|
||||
inline Literal* BaseIntVar::negated() {
|
||||
if (negated_ == nullptr) {
|
||||
negated_ = new NotBooleanVariable(this);
|
||||
}
|
||||
return negated_;
|
||||
}
|
||||
|
||||
} // namespace python
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
#endif // OR_TOOLS_SAT_PYTHON_LINEAR_EXPR_H_
|
||||
@@ -12,21 +12,18 @@
|
||||
// limitations under the License.
|
||||
|
||||
// This file wraps the swig_helper.h classes in python using pybind11.
|
||||
// Because pybind11_protobuf does not support building with CMake for OR-Tools,
|
||||
// the API has been transformed to use serialized protos from Python to C++ and
|
||||
// from C++ to python:
|
||||
// from Python to C++: use proto.SerializeToString(). This creates a python
|
||||
// string that is passed to C++ and parsed back to proto.
|
||||
// from C++ to Python, we cast the result of proto.SerializeAsString() to
|
||||
// pybind11::bytes. This is passed back to python, which will reconstruct
|
||||
// the proto using PythonProto.FromString(byte[]).
|
||||
|
||||
#include "ortools/sat/swig_helper.h"
|
||||
|
||||
#include <string>
|
||||
#include <Python.h>
|
||||
|
||||
#include "absl/strings/string_view.h"
|
||||
#include <cstdint>
|
||||
#include <limits>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
#include "absl/strings/str_cat.h"
|
||||
#include "ortools/sat/cp_model.pb.h"
|
||||
#include "ortools/sat/python/linear_expr.h"
|
||||
#include "ortools/util/sorted_interval_list.h"
|
||||
#include "pybind11/cast.h"
|
||||
#include "pybind11/functional.h"
|
||||
@@ -35,22 +32,20 @@
|
||||
#include "pybind11/stl.h"
|
||||
#include "pybind11_protobuf/native_proto_caster.h"
|
||||
|
||||
using ::operations_research::Domain;
|
||||
using ::operations_research::sat::CpModelProto;
|
||||
using ::operations_research::sat::CpSatHelper;
|
||||
using ::operations_research::sat::CpSolverResponse;
|
||||
using ::operations_research::sat::IntegerVariableProto;
|
||||
using ::operations_research::sat::SatParameters;
|
||||
using ::operations_research::sat::SolutionCallback;
|
||||
using ::operations_research::sat::SolveWrapper;
|
||||
using ::pybind11::arg;
|
||||
namespace py = pybind11;
|
||||
|
||||
namespace operations_research {
|
||||
namespace sat {
|
||||
namespace python {
|
||||
|
||||
using ::py::arg;
|
||||
|
||||
class PySolutionCallback : public SolutionCallback {
|
||||
public:
|
||||
using SolutionCallback::SolutionCallback; /* Inherit constructors */
|
||||
|
||||
void OnSolutionCallback() const override {
|
||||
::pybind11::gil_scoped_acquire acquire;
|
||||
::py::gil_scoped_acquire acquire;
|
||||
PYBIND11_OVERRIDE_PURE(
|
||||
void, /* Return type */
|
||||
SolutionCallback, /* Parent class */
|
||||
@@ -61,12 +56,141 @@ class PySolutionCallback : public SolutionCallback {
|
||||
}
|
||||
};
|
||||
|
||||
// A trampoline class to override the __str__ and __repr__ methods.
|
||||
class PyBaseIntVar : public BaseIntVar {
|
||||
public:
|
||||
using BaseIntVar::BaseIntVar; /* Inherit constructors */
|
||||
|
||||
std::string ToString() const override {
|
||||
PYBIND11_OVERRIDE_NAME(std::string, // Return type (ret_type)
|
||||
BaseIntVar, // Parent class (cname)
|
||||
"__str__", // Name of method in Python (name)
|
||||
ToString, // Name of function in C++ (fn)
|
||||
);
|
||||
}
|
||||
|
||||
std::string DebugString() const override {
|
||||
PYBIND11_OVERRIDE_NAME(std::string, // Return type (ret_type)
|
||||
BaseIntVar, // Parent class (cname)
|
||||
"__repr__", // Name of method in Python (name)
|
||||
DebugString, // Name of function in C++ (fn)
|
||||
);
|
||||
}
|
||||
};
|
||||
|
||||
// A class to wrap a C++ CpSolverResponse in a Python object, avoid the proto
|
||||
// conversion back to python.
|
||||
class ResponseWrapper {
|
||||
public:
|
||||
explicit ResponseWrapper(const CpSolverResponse& response)
|
||||
: response_(response) {}
|
||||
|
||||
double BestObjectiveBound() const { return response_.best_objective_bound(); }
|
||||
|
||||
bool BooleanValue(Literal* lit) const {
|
||||
const int index = lit->index();
|
||||
if (index >= 0) {
|
||||
return response_.solution(index) != 0;
|
||||
} else {
|
||||
return response_.solution(-index - 1) == 0;
|
||||
}
|
||||
}
|
||||
|
||||
bool FixedBooleanValue(bool lit) const { return lit; }
|
||||
|
||||
double DeterministicTime() const { return response_.deterministic_time(); }
|
||||
|
||||
int64_t NumBinaryPropagations() const {
|
||||
return response_.num_binary_propagations();
|
||||
}
|
||||
|
||||
int64_t NumBooleans() const { return response_.num_booleans(); }
|
||||
|
||||
int64_t NumBranches() const { return response_.num_branches(); }
|
||||
|
||||
int64_t NumConflicts() const { return response_.num_conflicts(); }
|
||||
|
||||
int64_t NumIntegerPropagations() const {
|
||||
return response_.num_integer_propagations();
|
||||
}
|
||||
|
||||
int64_t NumRestarts() const { return response_.num_restarts(); }
|
||||
|
||||
double ObjectiveValue() const { return response_.objective_value(); }
|
||||
|
||||
const CpSolverResponse& Response() const { return response_; }
|
||||
|
||||
std::string ResponseStats() const {
|
||||
return CpSatHelper::SolverResponseStats(response_);
|
||||
}
|
||||
|
||||
std::string SolutionInfo() const { return response_.solution_info(); }
|
||||
|
||||
std::vector<int> SufficientAssumptionsForInfeasibility() const {
|
||||
return std::vector<int>(
|
||||
response_.sufficient_assumptions_for_infeasibility().begin(),
|
||||
response_.sufficient_assumptions_for_infeasibility().end());
|
||||
}
|
||||
|
||||
CpSolverStatus Status() const { return response_.status(); }
|
||||
|
||||
double UserTime() const { return response_.user_time(); }
|
||||
|
||||
int64_t Value(IntLinExpr* expr) const {
|
||||
IntExprVisitor visitor;
|
||||
return visitor.Evaluate(expr, response_);
|
||||
}
|
||||
|
||||
int64_t FixedValue(int64_t value) const { return value; }
|
||||
|
||||
double WallTime() const { return response_.wall_time(); }
|
||||
|
||||
private:
|
||||
const CpSolverResponse response_;
|
||||
};
|
||||
|
||||
void throw_error(PyObject* py_exception, const std::string& message) {
|
||||
PyErr_SetString(py_exception, message.c_str());
|
||||
throw py::error_already_set();
|
||||
}
|
||||
|
||||
const char* kIntLinExprClassDoc = R"doc(
|
||||
Holds an integer linear expression.
|
||||
|
||||
A linear expression is built from integer constants and variables.
|
||||
For example, `x + 2 * (y - z + 1)`.
|
||||
|
||||
Linear expressions are used in CP-SAT models in constraints and in the
|
||||
objective:
|
||||
|
||||
* You can define linear constraints as in:
|
||||
|
||||
```
|
||||
model.add(x + 2 * y <= 5)
|
||||
model.add(sum(array_of_vars) == 5)
|
||||
```
|
||||
|
||||
* In CP-SAT, the objective is a linear expression:
|
||||
|
||||
```
|
||||
model.minimize(x + 2 * y + z)
|
||||
```
|
||||
|
||||
* For large arrays, using the LinearExpr class is faster that using the python
|
||||
`sum()` function. You can create constraints and the objective from lists of
|
||||
linear expressions or coefficients as follows:
|
||||
|
||||
```
|
||||
model.minimize(cp_model.LinearExpr.sum(expressions))
|
||||
model.add(cp_model.LinearExpr.weighted_sum(expressions, coefficients) >= 0)
|
||||
```)doc";
|
||||
|
||||
PYBIND11_MODULE(swig_helper, m) {
|
||||
pybind11_protobuf::ImportNativeProtoCasters();
|
||||
pybind11::module::import("ortools.util.python.sorted_interval_list");
|
||||
py::module::import("ortools.util.python.sorted_interval_list");
|
||||
|
||||
pybind11::class_<SolutionCallback, PySolutionCallback>(m, "SolutionCallback")
|
||||
.def(pybind11::init<>())
|
||||
py::class_<SolutionCallback, PySolutionCallback>(m, "SolutionCallback")
|
||||
.def(py::init<>())
|
||||
.def("OnSolutionCallback", &SolutionCallback::OnSolutionCallback)
|
||||
.def("BestObjectiveBound", &SolutionCallback::BestObjectiveBound)
|
||||
.def("DeterministicTime", &SolutionCallback::DeterministicTime)
|
||||
@@ -84,10 +208,58 @@ PYBIND11_MODULE(swig_helper, m) {
|
||||
arg("index"))
|
||||
.def("StopSearch", &SolutionCallback::StopSearch)
|
||||
.def("UserTime", &SolutionCallback::UserTime)
|
||||
.def("WallTime", &SolutionCallback::WallTime);
|
||||
.def("WallTime", &SolutionCallback::WallTime)
|
||||
.def(
|
||||
"Value",
|
||||
[](const SolutionCallback& callback, IntLinExpr* expr) {
|
||||
IntExprVisitor visitor;
|
||||
return visitor.Evaluate(expr, callback.Response());
|
||||
},
|
||||
"Returns the value of a linear expression after solve.")
|
||||
.def(
|
||||
"Value", [](const SolutionCallback&, int64_t value) { return value; },
|
||||
"Returns the value of a linear expression after solve.")
|
||||
.def(
|
||||
"BooleanValue",
|
||||
[](const SolutionCallback& callback, Literal* lit) {
|
||||
const int index = lit->index();
|
||||
if (index >= 0) {
|
||||
return callback.Response().solution(index) != 0;
|
||||
} else {
|
||||
return callback.Response().solution(-index - 1) == 0;
|
||||
}
|
||||
},
|
||||
"Returns the boolean value of a literal after solve.")
|
||||
.def(
|
||||
"BooleanValue", [](const SolutionCallback&, bool lit) { return lit; },
|
||||
"Returns the boolean value of a literal after solve.");
|
||||
|
||||
pybind11::class_<SolveWrapper>(m, "SolveWrapper")
|
||||
.def(pybind11::init<>())
|
||||
py::class_<ResponseWrapper>(m, "ResponseWrapper")
|
||||
.def(py::init<const CpSolverResponse&>())
|
||||
.def("best_objective_bound", &ResponseWrapper::BestObjectiveBound)
|
||||
.def("boolean_value", &ResponseWrapper::BooleanValue, arg("lit"))
|
||||
.def("boolean_value", &ResponseWrapper::FixedBooleanValue, arg("lit"))
|
||||
.def("deterministic_time", &ResponseWrapper::DeterministicTime)
|
||||
.def("num_binary_propagations", &ResponseWrapper::NumBinaryPropagations)
|
||||
.def("num_booleans", &ResponseWrapper::NumBooleans)
|
||||
.def("num_branches", &ResponseWrapper::NumBranches)
|
||||
.def("num_conflicts", &ResponseWrapper::NumConflicts)
|
||||
.def("num_integer_propagations", &ResponseWrapper::NumIntegerPropagations)
|
||||
.def("num_restarts", &ResponseWrapper::NumRestarts)
|
||||
.def("objective_value", &ResponseWrapper::ObjectiveValue)
|
||||
.def("response", &ResponseWrapper::Response)
|
||||
.def("response_stats", &ResponseWrapper::ResponseStats)
|
||||
.def("solution_info", &ResponseWrapper::SolutionInfo)
|
||||
.def("status", &ResponseWrapper::Status)
|
||||
.def("sufficient_assumptions_for_infeasibility",
|
||||
&ResponseWrapper::SufficientAssumptionsForInfeasibility)
|
||||
.def("user_time", &ResponseWrapper::UserTime)
|
||||
.def("value", &ResponseWrapper::Value, arg("expr"))
|
||||
.def("value", &ResponseWrapper::FixedValue, arg("value"))
|
||||
.def("wall_time", &ResponseWrapper::WallTime);
|
||||
|
||||
py::class_<SolveWrapper>(m, "SolveWrapper")
|
||||
.def(py::init<>())
|
||||
.def("add_log_callback", &SolveWrapper::AddLogCallback,
|
||||
arg("log_callback"))
|
||||
.def("add_solution_callback", &SolveWrapper::AddSolutionCallback,
|
||||
@@ -98,13 +270,13 @@ PYBIND11_MODULE(swig_helper, m) {
|
||||
.def("set_parameters", &SolveWrapper::SetParameters, arg("parameters"))
|
||||
.def("solve",
|
||||
[](SolveWrapper* solve_wrapper,
|
||||
const CpModelProto& model_proto) -> CpSolverResponse {
|
||||
::pybind11::gil_scoped_release release;
|
||||
return solve_wrapper->Solve(model_proto);
|
||||
const CpModelProto& model_proto) -> ResponseWrapper {
|
||||
::py::gil_scoped_release release;
|
||||
return ResponseWrapper(solve_wrapper->Solve(model_proto));
|
||||
})
|
||||
.def("stop_search", &SolveWrapper::StopSearch);
|
||||
|
||||
pybind11::class_<CpSatHelper>(m, "CpSatHelper")
|
||||
py::class_<CpSatHelper>(m, "CpSatHelper")
|
||||
.def_static("model_stats", &CpSatHelper::ModelStats, arg("model_proto"))
|
||||
.def_static("solver_response_stats", &CpSatHelper::SolverResponseStats,
|
||||
arg("response"))
|
||||
@@ -114,4 +286,494 @@ PYBIND11_MODULE(swig_helper, m) {
|
||||
arg("variable_proto"))
|
||||
.def_static("write_model_to_file", &CpSatHelper::WriteModelToFile,
|
||||
arg("model_proto"), arg("filename"));
|
||||
}
|
||||
|
||||
py::class_<FloatExprOrValue>(m, "FloatExprOrValue")
|
||||
.def(py::init<FloatLinearExpr*>())
|
||||
.def(py::init<double>())
|
||||
.def_readonly("value", &FloatExprOrValue::value)
|
||||
.def_readonly("expr", &FloatExprOrValue::expr);
|
||||
|
||||
py::implicitly_convertible<FloatLinearExpr*, FloatExprOrValue>();
|
||||
py::implicitly_convertible<double, FloatExprOrValue>();
|
||||
|
||||
py::class_<FloatLinearExpr>(m, "FloatLinearExpr")
|
||||
.def(py::init<>())
|
||||
.def_static("sum",
|
||||
py::overload_cast<const std::vector<FloatExprOrValue>&>(
|
||||
&FloatLinearExpr::Sum),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static(
|
||||
"sum",
|
||||
py::overload_cast<const std::vector<FloatExprOrValue>&, double>(
|
||||
&FloatLinearExpr::Sum),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("weighted_sum",
|
||||
py::overload_cast<const std::vector<FloatExprOrValue>&,
|
||||
const std::vector<double>&>(
|
||||
&FloatLinearExpr::WeightedSum),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("weighted_sum",
|
||||
py::overload_cast<const std::vector<FloatExprOrValue>&,
|
||||
const std::vector<double>&, double>(
|
||||
&FloatLinearExpr::WeightedSum),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("WeightedSum",
|
||||
py::overload_cast<const std::vector<FloatExprOrValue>&,
|
||||
const std::vector<double>&>(
|
||||
&FloatLinearExpr::WeightedSum),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("term", &FloatLinearExpr::Term, arg("expr"), arg("coeff"),
|
||||
"Returns expr * coeff.", py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def_static("affine", &FloatLinearExpr::Affine, arg("expr"), arg("coeff"),
|
||||
arg("offset"), "Returns expr * coeff + offset.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("constant", FloatLinearExpr::Constant, arg("value"),
|
||||
"Returns a constant linear expression.",
|
||||
py::return_value_policy::automatic)
|
||||
.def("__str__", &FloatLinearExpr::ToString)
|
||||
.def("__repr__", &FloatLinearExpr::DebugString)
|
||||
.def("is_integer", &FloatLinearExpr::is_integer)
|
||||
.def("__add__", &FloatLinearExpr::FloatAddCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__add__", &FloatLinearExpr::FloatAdd,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__radd__", &FloatLinearExpr::FloatAddCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__radd__", &FloatLinearExpr::FloatAdd,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__sub__", &FloatLinearExpr::FloatSub,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__sub__", &FloatLinearExpr::FloatSubCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__rsub__", &FloatLinearExpr::FloatRSub,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__rsub__", &FloatLinearExpr::FloatRSubCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__mul__", &FloatLinearExpr::FloatMulCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__rmul__", &FloatLinearExpr::FloatMulCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__neg__", &FloatLinearExpr::FloatNeg,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>());
|
||||
|
||||
py::class_<FloatAffine, FloatLinearExpr>(m, "FloatAffine")
|
||||
.def(py::init<FloatLinearExpr*, double, double>())
|
||||
.def_property_readonly("expression", &FloatAffine::expression)
|
||||
.def_property_readonly("coefficient", &FloatAffine::coefficient)
|
||||
.def_property_readonly("offset", &FloatAffine::offset);
|
||||
|
||||
py::class_<CanonicalFloatExpression>(m, "CanonicalFloatExpression")
|
||||
.def(py::init<FloatLinearExpr*>())
|
||||
.def_property_readonly("vars", &CanonicalFloatExpression::vars)
|
||||
.def_property_readonly("coeffs", &CanonicalFloatExpression::coeffs)
|
||||
.def_property_readonly("offset", &CanonicalFloatExpression::offset);
|
||||
|
||||
py::class_<IntExprOrValue>(m, "IntExprOrValue")
|
||||
.def(py::init<IntLinExpr*>())
|
||||
.def(py::init<int64_t>())
|
||||
.def_readonly("value", &IntExprOrValue::value)
|
||||
.def_readonly("expr", &IntExprOrValue::expr);
|
||||
|
||||
py::implicitly_convertible<IntLinExpr*, IntExprOrValue>();
|
||||
py::implicitly_convertible<int64_t, IntExprOrValue>();
|
||||
|
||||
py::class_<IntLinExpr, FloatLinearExpr>(m, "LinearExpr", kIntLinExprClassDoc)
|
||||
.def(py::init<>())
|
||||
.def_static("sum",
|
||||
py::overload_cast<const std::vector<IntExprOrValue>&>(
|
||||
&IntLinExpr::Sum),
|
||||
"Returns sum(exprs)", py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def_static(
|
||||
"sum",
|
||||
py::overload_cast<const std::vector<IntExprOrValue>&, int64_t>(
|
||||
&IntLinExpr::Sum),
|
||||
"Returns sum(exprs) + cst", py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def_static("weighted_sum",
|
||||
py::overload_cast<const std::vector<IntExprOrValue>&,
|
||||
const std::vector<int64_t>&>(
|
||||
&IntLinExpr::WeightedSum),
|
||||
"Returns sum(exprs[i] * coeffs[i]",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("weighted_sum",
|
||||
py::overload_cast<const std::vector<IntExprOrValue>&,
|
||||
const std::vector<int64_t>&, int64_t>(
|
||||
&IntLinExpr::WeightedSum),
|
||||
"Returns sum(exprs[i] * coeffs[i]) + cst",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("weighted_sum",
|
||||
py::overload_cast<const std::vector<FloatExprOrValue>&,
|
||||
const std::vector<double>&>(
|
||||
&FloatLinearExpr::WeightedSum),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("term", &IntLinExpr::Term, arg("expr"), arg("coeff"),
|
||||
"Returns expr * coeff.", py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def_static("affine", &IntLinExpr::Affine, arg("expr"), arg("coeff"),
|
||||
arg("offset"), "Returns expr * coeff.",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("constant", IntLinExpr::Constant, arg("value"),
|
||||
"Returns a constant linear expression.",
|
||||
py::return_value_policy::automatic)
|
||||
.def("is_integer", &IntLinExpr::is_integer)
|
||||
.def("__add__", &IntLinExpr::IntAddCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__add__", &FloatLinearExpr::FloatAddCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__add__", &IntLinExpr::IntAdd, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>(), py::keep_alive<0, 2>())
|
||||
.def("__add__", &FloatLinearExpr::FloatAdd,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__radd__", &IntLinExpr::IntAddCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__radd__", &FloatLinearExpr::FloatAddCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__radd__", &FloatLinearExpr::FloatAdd,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__sub__", &IntLinExpr::IntSubCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__sub__", &FloatLinearExpr::FloatSubCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__sub__", &FloatLinearExpr::FloatSubCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__sub__", &IntLinExpr::IntSub, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>(), py::keep_alive<0, 2>())
|
||||
.def("__sub__", &FloatLinearExpr::FloatSub,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__rsub__", &IntLinExpr::IntRSubCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__rsub__", &FloatLinearExpr::FloatRSubCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__rsub__", &FloatLinearExpr::FloatRSub,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>(),
|
||||
py::keep_alive<0, 2>())
|
||||
.def("__mul__", &IntLinExpr::IntMulCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__rmul__", &IntLinExpr::IntMulCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__neg__", &IntLinExpr::IntNeg, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def("__mul__", &FloatLinearExpr::FloatMulCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__rmul__", &FloatLinearExpr::FloatMulCst,
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__eq__", &IntLinExpr::Eq, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>(), py::keep_alive<0, 2>())
|
||||
.def("__eq__", &IntLinExpr::EqCst, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def("__ne__", &IntLinExpr::Ne, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>(), py::keep_alive<0, 2>())
|
||||
.def("__ne__", &IntLinExpr::NeCst, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def("__lt__", &IntLinExpr::Lt, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>(), py::keep_alive<0, 2>())
|
||||
.def(
|
||||
"__lt__",
|
||||
[](IntLinExpr* expr, int64_t bound) {
|
||||
if (bound == std::numeric_limits<int64_t>::min()) {
|
||||
throw_error(PyExc_ArithmeticError, "< INT_MIN is not supported");
|
||||
}
|
||||
return expr->LtCst(bound);
|
||||
},
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__le__", &IntLinExpr::Le, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>(), py::keep_alive<0, 2>())
|
||||
.def(
|
||||
"__le__",
|
||||
[](IntLinExpr* expr, int64_t bound) {
|
||||
if (bound == std::numeric_limits<int64_t>::min()) {
|
||||
throw_error(PyExc_ArithmeticError, "<= INT_MIN is not supported");
|
||||
}
|
||||
return expr->LeCst(bound);
|
||||
},
|
||||
py::return_value_policy::automatic,
|
||||
|
||||
py::keep_alive<0, 1>())
|
||||
.def("__gt__", &IntLinExpr::Gt, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>(), py::keep_alive<0, 2>())
|
||||
.def(
|
||||
"__gt__",
|
||||
[](IntLinExpr* expr, int64_t bound) {
|
||||
if (bound == std::numeric_limits<int64_t>::max()) {
|
||||
throw_error(PyExc_ArithmeticError, "> INT_MAX is not supported");
|
||||
}
|
||||
return expr->GtCst(bound);
|
||||
},
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def("__ge__", &IntLinExpr::Ge, py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>(), py::keep_alive<0, 2>())
|
||||
.def(
|
||||
"__ge__",
|
||||
[](IntLinExpr* expr, int64_t bound) {
|
||||
if (bound == std::numeric_limits<int64_t>::max()) {
|
||||
throw_error(PyExc_ArithmeticError, ">= INT_MAX is not supported");
|
||||
}
|
||||
return expr->GeCst(bound);
|
||||
},
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
|
||||
.def("__div__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling / on a linear expression is not supported, "
|
||||
"please use CpModel.add_division_equality");
|
||||
})
|
||||
.def("__div__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling / on a linear expression is not supported, "
|
||||
"please use CpModel.add_division_equality");
|
||||
})
|
||||
.def("__truediv__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling // on a linear expression is not supported, "
|
||||
"please use CpModel.add_division_equality");
|
||||
})
|
||||
.def("__truediv__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling // on a linear expression is not supported, "
|
||||
"please use CpModel.add_division_equality");
|
||||
})
|
||||
.def("__mod__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling %% on a linear expression is not supported, "
|
||||
"please use CpModel.add_modulo_equality");
|
||||
})
|
||||
.def("__mod__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling %% on a linear expression is not supported, "
|
||||
"please use CpModel.add_modulo_equality");
|
||||
})
|
||||
.def("__pow__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling ** on a linear expression is not supported, "
|
||||
"please use CpModel.add_multiplication_equality");
|
||||
})
|
||||
.def("__pow__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling ** on a linear expression is not supported, "
|
||||
"please use CpModel.add_multiplication_equality");
|
||||
})
|
||||
.def("__lshift__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(
|
||||
PyExc_NotImplementedError,
|
||||
"calling left shift on a linear expression is not supported");
|
||||
})
|
||||
.def("__lshift__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(
|
||||
PyExc_NotImplementedError,
|
||||
"calling left shift on a linear expression is not supported");
|
||||
})
|
||||
.def("__rshift__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(
|
||||
PyExc_NotImplementedError,
|
||||
"calling right shift on a linear expression is not supported");
|
||||
})
|
||||
.def("__rshift__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(
|
||||
PyExc_NotImplementedError,
|
||||
"calling right shift on a linear expression is not supported");
|
||||
})
|
||||
.def("__and__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling and on a linear expression is not supported");
|
||||
})
|
||||
.def("__and__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling and on a linear expression is not supported");
|
||||
})
|
||||
.def("__or__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling or on a linear expression is not supported");
|
||||
})
|
||||
.def("__or__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling or on a linear expression is not supported");
|
||||
})
|
||||
.def("__xor__",
|
||||
[](IntLinExpr* /*self*/, IntLinExpr* /*other*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling xor on a linear expression is not supported");
|
||||
})
|
||||
.def("__xor__",
|
||||
[](IntLinExpr* /*self*/, int64_t /*cst*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"calling xor on a linear expression is not supported");
|
||||
})
|
||||
.def("__abs__",
|
||||
[](IntLinExpr* /*self*/) {
|
||||
throw_error(
|
||||
PyExc_NotImplementedError,
|
||||
"calling abs() on a linear expression is not supported, "
|
||||
"please use CpModel.add_abs_equality");
|
||||
})
|
||||
.def("__bool__",
|
||||
[](IntLinExpr* /*self*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"Evaluating a LinearExpr instance as a Boolean is "
|
||||
"not implemented.");
|
||||
})
|
||||
.def_static("Sum",
|
||||
py::overload_cast<const std::vector<IntExprOrValue>&>(
|
||||
&IntLinExpr::Sum),
|
||||
"Returns sum(exprs)", py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def_static(
|
||||
"Sum",
|
||||
py::overload_cast<const std::vector<IntExprOrValue>&, int64_t>(
|
||||
&IntLinExpr::Sum),
|
||||
"Returns sum(exprs) + cst", py::return_value_policy::automatic,
|
||||
py::keep_alive<0, 1>())
|
||||
.def_static("WeightedSum",
|
||||
py::overload_cast<const std::vector<IntExprOrValue>&,
|
||||
const std::vector<int64_t>&>(
|
||||
&IntLinExpr::WeightedSum),
|
||||
"Returns sum(exprs[i] * coeffs[i]",
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("WeightedSum",
|
||||
py::overload_cast<const std::vector<FloatExprOrValue>&,
|
||||
const std::vector<double>&>(
|
||||
&FloatLinearExpr::WeightedSum),
|
||||
py::return_value_policy::automatic, py::keep_alive<0, 1>())
|
||||
.def_static("Term", &IntLinExpr::Term, arg("expr"), arg("coeff"),
|
||||
"Returns expr * coeff.", py::return_value_policy::automatic);
|
||||
|
||||
py::class_<IntAffine, IntLinExpr>(m, "IntAffine")
|
||||
.def(py::init<IntLinExpr*, int64_t, int64_t>())
|
||||
.def_property_readonly("expression", &IntAffine::expression)
|
||||
.def_property_readonly("coefficient", &IntAffine::coefficient)
|
||||
.def_property_readonly("offset", &IntAffine::offset);
|
||||
|
||||
py::class_<Literal>(m, "Literal")
|
||||
.def_property_readonly("index", &Literal::index,
|
||||
"The index of the variable in the model.")
|
||||
.def("negated", &Literal::negated,
|
||||
R"doc(
|
||||
Returns the negation of a literal (a Boolean variable or its negation).
|
||||
|
||||
This method implements the logical negation of a Boolean variable.
|
||||
It is only valid if the variable has a Boolean domain (0 or 1).
|
||||
|
||||
Note that this method is nilpotent: `x.negated().negated() == x`.
|
||||
)doc",
|
||||
py::return_value_policy::automatic, py::keep_alive<1, 0>())
|
||||
.def("__invert__", &Literal::negated,
|
||||
"Returns the negation of the current literal.",
|
||||
py::return_value_policy::automatic)
|
||||
.def("__bool__",
|
||||
[](Literal* /*self*/) {
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
"Evaluating a Literal instance as a Boolean is "
|
||||
"not implemented.");
|
||||
})
|
||||
// PEP8 Compatibility.
|
||||
.def("Not", &Literal::negated, py::return_value_policy::automatic)
|
||||
.def("Index", &Literal::index);
|
||||
|
||||
py::class_<BaseIntVar, PyBaseIntVar, IntLinExpr, Literal>(m, "BaseIntVar")
|
||||
.def(py::init<int>())
|
||||
.def(py::init<int, bool>())
|
||||
.def_property_readonly("index", &BaseIntVar::index,
|
||||
"The index of the variable in the model.")
|
||||
.def_property_readonly("is_boolean", &BaseIntVar::is_boolean,
|
||||
"Whether the variable is boolean.")
|
||||
.def("__str__", &BaseIntVar::ToString)
|
||||
.def("__repr__", &BaseIntVar::DebugString)
|
||||
.def(
|
||||
"negated",
|
||||
[](BaseIntVar* self) {
|
||||
if (!self->is_boolean()) {
|
||||
throw_error(PyExc_TypeError,
|
||||
"negated() is only supported for boolean variables.");
|
||||
}
|
||||
return self->negated();
|
||||
},
|
||||
"Returns the negation of the current Boolean variable.",
|
||||
py::return_value_policy::automatic, py::keep_alive<1, 0>())
|
||||
.def(
|
||||
"__invert__",
|
||||
[](BaseIntVar* self) {
|
||||
if (!self->is_boolean()) {
|
||||
throw_error(PyExc_ValueError,
|
||||
"negated() is only supported for boolean variables.");
|
||||
}
|
||||
return self->negated();
|
||||
},
|
||||
"Returns the negation of the current Boolean variable.",
|
||||
py::return_value_policy::automatic, py::keep_alive<1, 0>())
|
||||
// PEP8 Compatibility.
|
||||
.def(
|
||||
"Not",
|
||||
[](BaseIntVar* self) {
|
||||
if (!self->is_boolean()) {
|
||||
throw_error(PyExc_ValueError,
|
||||
"negated() is only supported for boolean variables.");
|
||||
}
|
||||
return self->negated();
|
||||
},
|
||||
py::return_value_policy::automatic, py::keep_alive<1, 0>());
|
||||
|
||||
py::class_<NotBooleanVariable, IntLinExpr, Literal>(m, "NotBooleanVariable")
|
||||
.def(py::init<BaseIntVar*>())
|
||||
.def_property_readonly("index", &NotBooleanVariable::index,
|
||||
"The index of the variable in the model.")
|
||||
.def("__str__", &NotBooleanVariable::ToString)
|
||||
.def("__repr__", &NotBooleanVariable::DebugString)
|
||||
.def("negated", &NotBooleanVariable::negated,
|
||||
"Returns the negation of the current Boolean variable.",
|
||||
py::return_value_policy::automatic)
|
||||
.def("__invert__", &NotBooleanVariable::negated,
|
||||
"Returns the negation of the current Boolean variable.",
|
||||
py::return_value_policy::automatic)
|
||||
.def("Not", &NotBooleanVariable::negated,
|
||||
"Returns the negation of the current Boolean variable.",
|
||||
py::return_value_policy::automatic);
|
||||
|
||||
py::class_<BoundedLinearExpression>(m, "BoundedLinearExpression")
|
||||
.def(py::init<IntLinExpr*, const Domain&>())
|
||||
.def(py::init<int64_t, const Domain&>())
|
||||
.def_property_readonly("bounds", &BoundedLinearExpression::bounds)
|
||||
.def_property_readonly("vars", &BoundedLinearExpression::vars)
|
||||
.def_property_readonly("coeffs", &BoundedLinearExpression::coeffs)
|
||||
.def_property_readonly("offset", &BoundedLinearExpression::offset)
|
||||
.def("__str__", &BoundedLinearExpression::ToString)
|
||||
.def("__repr__", &BoundedLinearExpression::DebugString)
|
||||
.def("__bool__", [](const BoundedLinearExpression& self) {
|
||||
bool result;
|
||||
if (self.CastToBool(&result)) return result;
|
||||
throw_error(PyExc_NotImplementedError,
|
||||
absl::StrCat("Evaluating a BoundedLinearExpression '",
|
||||
self.ToString(),
|
||||
"'instance as a Boolean is "
|
||||
"not implemented.")
|
||||
.c_str());
|
||||
return false;
|
||||
});
|
||||
} // NOLINT(readability/fn_size)
|
||||
|
||||
} // namespace python
|
||||
} // namespace sat
|
||||
} // namespace operations_research
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
"""Tests for ortools.sat.python.swig_helper."""
|
||||
"""Unit tests for ortools.sat.python.swig_helper."""
|
||||
|
||||
from absl.testing import absltest
|
||||
from google.protobuf import text_format
|
||||
@@ -44,6 +44,19 @@ class BestBoundCallback:
|
||||
self.best_bound = bb
|
||||
|
||||
|
||||
class TestIntVar(swig_helper.BaseIntVar):
|
||||
|
||||
def __init__(self, index: int, name: str, is_boolean: bool = False) -> None:
|
||||
swig_helper.BaseIntVar.__init__(self, index, is_boolean)
|
||||
self._name = name
|
||||
|
||||
def __str__(self) -> str:
|
||||
return self._name
|
||||
|
||||
def __repr__(self) -> str:
|
||||
return self._name
|
||||
|
||||
|
||||
class SwigHelperTest(absltest.TestCase):
|
||||
|
||||
def testVariableDomain(self):
|
||||
@@ -96,10 +109,10 @@ class SwigHelperTest(absltest.TestCase):
|
||||
self.assertTrue(text_format.Parse(model_string, model))
|
||||
|
||||
solve_wrapper = swig_helper.SolveWrapper()
|
||||
solution = solve_wrapper.solve(model)
|
||||
response_wrapper = solve_wrapper.solve(model)
|
||||
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, solution.status)
|
||||
self.assertEqual(30.0, solution.objective_value)
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, response_wrapper.status())
|
||||
self.assertEqual(30.0, response_wrapper.objective_value())
|
||||
|
||||
def testSimpleSolveWithCore(self):
|
||||
model_string = """
|
||||
@@ -140,10 +153,10 @@ class SwigHelperTest(absltest.TestCase):
|
||||
|
||||
solve_wrapper = swig_helper.SolveWrapper()
|
||||
solve_wrapper.set_parameters(parameters)
|
||||
solution = solve_wrapper.solve(model)
|
||||
response_wrapper = solve_wrapper.solve(model)
|
||||
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, solution.status)
|
||||
self.assertEqual(30.0, solution.objective_value)
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, response_wrapper.status())
|
||||
self.assertEqual(30.0, response_wrapper.objective_value())
|
||||
|
||||
def testSimpleSolveWithProtoApi(self):
|
||||
model = cp_model_pb2.CpModelProto()
|
||||
@@ -162,11 +175,11 @@ class SwigHelperTest(absltest.TestCase):
|
||||
model.objective.scaling_factor = -1
|
||||
|
||||
solve_wrapper = swig_helper.SolveWrapper()
|
||||
solution = solve_wrapper.solve(model)
|
||||
response_wrapper = solve_wrapper.solve(model)
|
||||
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, solution.status)
|
||||
self.assertEqual(30.0, solution.objective_value)
|
||||
self.assertEqual(30.0, solution.best_objective_bound)
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, response_wrapper.status())
|
||||
self.assertEqual(30.0, response_wrapper.objective_value())
|
||||
self.assertEqual(30.0, response_wrapper.best_objective_bound())
|
||||
|
||||
def testSolutionCallback(self):
|
||||
model_string = """
|
||||
@@ -184,10 +197,10 @@ class SwigHelperTest(absltest.TestCase):
|
||||
params = sat_parameters_pb2.SatParameters()
|
||||
params.enumerate_all_solutions = True
|
||||
solve_wrapper.set_parameters(params)
|
||||
solution = solve_wrapper.solve(model)
|
||||
response_wrapper = solve_wrapper.solve(model)
|
||||
|
||||
self.assertEqual(5, callback.solution_count())
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, solution.status)
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, response_wrapper.status())
|
||||
|
||||
def testBestBoundCallback(self):
|
||||
model_string = """
|
||||
@@ -213,10 +226,10 @@ class SwigHelperTest(absltest.TestCase):
|
||||
params.linearization_level = 2
|
||||
params.log_search_progress = True
|
||||
solve_wrapper.set_parameters(params)
|
||||
solution = solve_wrapper.solve(model)
|
||||
response_wrapper = solve_wrapper.solve(model)
|
||||
|
||||
self.assertEqual(2.6, best_bound_callback.best_bound)
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, solution.status)
|
||||
self.assertEqual(cp_model_pb2.OPTIMAL, response_wrapper.status())
|
||||
|
||||
def testModelStats(self):
|
||||
model_string = """
|
||||
@@ -257,6 +270,94 @@ class SwigHelperTest(absltest.TestCase):
|
||||
stats = swig_helper.CpSatHelper.model_stats(model)
|
||||
self.assertTrue(stats)
|
||||
|
||||
def testIntLinExpr(self):
|
||||
x = TestIntVar(0, "x")
|
||||
self.assertTrue(x.is_integer())
|
||||
self.assertIsInstance(x, swig_helper.BaseIntVar)
|
||||
self.assertIsInstance(x, swig_helper.LinearExpr)
|
||||
e1 = x + 2
|
||||
self.assertTrue(e1.is_integer())
|
||||
self.assertEqual(str(e1), "(x + 2)")
|
||||
e2 = 3 + x
|
||||
self.assertTrue(e2.is_integer())
|
||||
self.assertEqual(str(e2), "(x + 3)")
|
||||
y = TestIntVar(1, "y")
|
||||
e3 = y * 5
|
||||
self.assertTrue(e3.is_integer())
|
||||
self.assertEqual(str(e3), "(5 * y)")
|
||||
e4 = -2 * y
|
||||
self.assertTrue(e4.is_integer())
|
||||
self.assertEqual(str(e4), "(-2 * y)")
|
||||
e5 = x - 1
|
||||
self.assertTrue(e5.is_integer())
|
||||
self.assertEqual(str(e5), "(x - 1)")
|
||||
e6 = x - 2 * y
|
||||
self.assertTrue(e6.is_integer())
|
||||
self.assertEqual(str(e6), "(x - (2 * y))")
|
||||
z = TestIntVar(2, "z", True)
|
||||
e7 = -z
|
||||
self.assertTrue(e7.is_integer())
|
||||
self.assertEqual(str(e7), "(-z)")
|
||||
not_z = ~z
|
||||
self.assertTrue(not_z.is_integer())
|
||||
self.assertEqual(str(not_z), "not(z)")
|
||||
self.assertEqual(not_z.index, -3)
|
||||
|
||||
e8 = swig_helper.LinearExpr.sum([x, y, z])
|
||||
self.assertEqual(str(e8), "(x + y + z)")
|
||||
e9 = swig_helper.LinearExpr.sum([x, y, z], 11)
|
||||
self.assertEqual(str(e9), "(x + y + z + 11)")
|
||||
e10 = swig_helper.LinearExpr.weighted_sum([x, y, z], [1, 2, 3])
|
||||
self.assertEqual(str(e10), "(x + 2 * y + 3 * z)")
|
||||
e11 = swig_helper.LinearExpr.weighted_sum([x, y, z], [1, 2, 3], -5)
|
||||
self.assertEqual(str(e11), "(x + 2 * y + 3 * z - 5)")
|
||||
|
||||
def testFloatLinExpr(self):
|
||||
x = TestIntVar(0, "x")
|
||||
self.assertTrue(x.is_integer())
|
||||
self.assertIsInstance(x, TestIntVar)
|
||||
self.assertIsInstance(x, swig_helper.LinearExpr)
|
||||
self.assertIsInstance(x, swig_helper.FloatLinearExpr)
|
||||
e1 = x + 2.5
|
||||
self.assertFalse(e1.is_integer())
|
||||
self.assertEqual(str(e1), "(x + 2.5)")
|
||||
e2 = 3.1 + x
|
||||
self.assertFalse(e2.is_integer())
|
||||
self.assertEqual(str(e2), "(x + 3.1)")
|
||||
y = TestIntVar(1, "y")
|
||||
e3 = y * 5.2
|
||||
self.assertFalse(e3.is_integer())
|
||||
self.assertEqual(str(e3), "(5.2 * y)")
|
||||
e4 = -2.2 * y
|
||||
self.assertFalse(e4.is_integer())
|
||||
self.assertEqual(str(e4), "(-2.2 * y)")
|
||||
e5 = x - 1.1
|
||||
self.assertFalse(e5.is_integer())
|
||||
self.assertEqual(str(e5), "(x - 1.1)")
|
||||
e6 = x + 2.4 * y
|
||||
self.assertFalse(e6.is_integer())
|
||||
self.assertEqual(str(e6), "(x + (2.4 * y))")
|
||||
e7 = x - 2.4 * y
|
||||
self.assertFalse(e7.is_integer())
|
||||
self.assertEqual(str(e7), "(x - (2.4 * y))")
|
||||
|
||||
z = TestIntVar(2, "z")
|
||||
e8 = swig_helper.FloatLinearExpr.sum([x, y, z])
|
||||
self.assertFalse(e8.is_integer())
|
||||
self.assertEqual(str(e8), "(x + y + z)")
|
||||
e9 = swig_helper.FloatLinearExpr.sum([x, y, z], 1.5)
|
||||
self.assertFalse(e9.is_integer())
|
||||
self.assertEqual(str(e9), "(x + y + z + 1.5)")
|
||||
e10 = swig_helper.FloatLinearExpr.weighted_sum([x, y, z], [1.0, 2.2, 3.3])
|
||||
self.assertFalse(e10.is_integer())
|
||||
self.assertEqual(str(e10), "(x + 2.2 * y + 3.3 * z)")
|
||||
e11 = swig_helper.FloatLinearExpr.weighted_sum([x, y, z], [1.0, 2.2, 3.3], 1.5)
|
||||
self.assertFalse(e11.is_integer())
|
||||
self.assertEqual(str(e11), "(x + 2.2 * y + 3.3 * z + 1.5)")
|
||||
e12 = (x + 2) * 3.1
|
||||
self.assertFalse(e12.is_integer())
|
||||
self.assertEqual(str(e12), "(3.1 * (x + 2))")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
absltest.main()
|
||||
|
||||
@@ -92,7 +92,8 @@ void SolutionCallback::StopSearch() {
|
||||
if (wrapper_ != nullptr) wrapper_->StopSearch();
|
||||
}
|
||||
|
||||
operations_research::sat::CpSolverResponse SolutionCallback::Response() const {
|
||||
const operations_research::sat::CpSolverResponse& SolutionCallback::Response()
|
||||
const {
|
||||
return response_;
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ class SolutionCallback {
|
||||
// Stops the search.
|
||||
void StopSearch();
|
||||
|
||||
operations_research::sat::CpSolverResponse Response() const;
|
||||
const operations_research::sat::CpSolverResponse& Response() const;
|
||||
|
||||
// We use mutable and non const methods to overcome SWIG difficulties.
|
||||
void SetWrapperClass(SolveWrapper* wrapper) const;
|
||||
|
||||
Reference in New Issue
Block a user