[CP-SAT] improve doc on C++ builder API; rewrite python API to remove the DoubleLinearExpr class
This commit is contained in:
@@ -106,7 +106,7 @@ int64_t ComputeHorizon(const JsspInputProblem& problem) {
|
||||
struct JobTaskData {
|
||||
IntervalVar interval;
|
||||
IntVar start;
|
||||
IntVar duration;
|
||||
LinearExpr duration;
|
||||
IntVar end;
|
||||
};
|
||||
|
||||
@@ -488,7 +488,8 @@ void CreateObjective(
|
||||
for (int a = 0; a < num_alternatives; ++a) {
|
||||
// Add cost if present.
|
||||
if (task.cost_size() > 0) {
|
||||
objective_vars.push_back(job_task_to_alternatives[j][t][a].presence);
|
||||
objective_vars.push_back(
|
||||
IntVar(job_task_to_alternatives[j][t][a].presence));
|
||||
objective_coeffs.push_back(task.cost(a));
|
||||
}
|
||||
}
|
||||
@@ -626,14 +627,13 @@ void AddMakespanRedundantConstraints(
|
||||
const int num_machines = problem.machines_size();
|
||||
|
||||
// Global energetic reasoning.
|
||||
std::vector<IntVar> all_task_durations;
|
||||
LinearExpr sum_of_duration;
|
||||
for (const std::vector<JobTaskData>& tasks : job_to_tasks) {
|
||||
for (const JobTaskData& task : tasks) {
|
||||
all_task_durations.push_back(task.duration);
|
||||
sum_of_duration += task.duration;
|
||||
}
|
||||
}
|
||||
cp_model.AddLessOrEqual(LinearExpr::Sum(all_task_durations),
|
||||
makespan * num_machines);
|
||||
cp_model.AddLessOrEqual(sum_of_duration, makespan * num_machines);
|
||||
}
|
||||
|
||||
void DisplayJobStatistics(
|
||||
@@ -648,8 +648,7 @@ void DisplayJobStatistics(
|
||||
for (const std::vector<JobTaskData>& job : job_to_tasks) {
|
||||
num_tasks += job.size();
|
||||
for (const JobTaskData& task : job) {
|
||||
if (task.duration.Proto().domain_size() != 2 ||
|
||||
task.duration.Proto().domain(0) != task.duration.Proto().domain(1)) {
|
||||
if (!task.duration.IsConstant()) {
|
||||
num_tasks_with_variable_duration++;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -578,7 +578,7 @@ class NetworkRoutingSolver {
|
||||
// Node - Graph Constraint.
|
||||
for (int demand_index = 0; demand_index < num_demands; ++demand_index) {
|
||||
for (int arc = 0; arc < num_arcs; ++arc) {
|
||||
path_vars[demand_index].push_back(cp_model.NewBoolVar());
|
||||
path_vars[demand_index].push_back(IntVar(cp_model.NewBoolVar()));
|
||||
}
|
||||
// Fill Tuple Set for AllowedAssignment constraint.
|
||||
TableConstraint path_ct =
|
||||
|
||||
@@ -262,7 +262,7 @@ void LoadAndSolve(const std::string& file_name) {
|
||||
}
|
||||
|
||||
// Check that we have not already visited this exact set of candidate jobs.
|
||||
if (gtl::ContainsKey(visited_job_lists, intersecting_jobs)) continue;
|
||||
if (visited_job_lists.contains(intersecting_jobs)) continue;
|
||||
visited_job_lists.insert(intersecting_jobs);
|
||||
|
||||
// Collect the relevant worker job vars.
|
||||
|
||||
@@ -107,14 +107,14 @@ void OpponentModel(int num_teams) {
|
||||
std::vector<IntVar> day_home_aways;
|
||||
for (int t = 0; t < num_teams; ++t) {
|
||||
day_opponents.push_back(opponents[t][d]);
|
||||
day_home_aways.push_back(home_aways[t][d]);
|
||||
day_home_aways.push_back(IntVar(home_aways[t][d]));
|
||||
}
|
||||
|
||||
builder.AddInverseConstraint(day_opponents, day_opponents);
|
||||
|
||||
for (int first_team = 0; first_team < num_teams; ++first_team) {
|
||||
IntVar first_home = day_home_aways[first_team];
|
||||
IntVar second_home = builder.NewBoolVar();
|
||||
const IntVar first_home = IntVar(day_home_aways[first_team]);
|
||||
const IntVar second_home = IntVar(builder.NewBoolVar());
|
||||
builder.AddVariableElement(day_opponents[first_team], day_home_aways,
|
||||
second_home);
|
||||
builder.AddEquality(first_home + second_home, 1);
|
||||
|
||||
@@ -466,37 +466,37 @@ class CpModelTest(unittest.TestCase):
|
||||
|
||||
def testAssertIsInt64(self):
|
||||
print('testAssertIsInt64')
|
||||
cp_model_helper.AssertIsInt64(123)
|
||||
cp_model_helper.AssertIsInt64(2**63 - 1)
|
||||
cp_model_helper.AssertIsInt64(-2**63)
|
||||
cp_model_helper.assert_is_int64(123)
|
||||
cp_model_helper.assert_is_int64(2**63 - 1)
|
||||
cp_model_helper.assert_is_int64(-2**63)
|
||||
|
||||
def testCapInt64(self):
|
||||
print('testCapInt64')
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapInt64(cp_model.INT_MAX), cp_model.INT_MAX)
|
||||
cp_model_helper.to_capped_int64(cp_model.INT_MAX), cp_model.INT_MAX)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapInt64(cp_model.INT_MAX + 1), cp_model.INT_MAX)
|
||||
cp_model_helper.to_capped_int64(cp_model.INT_MAX + 1), cp_model.INT_MAX)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapInt64(cp_model.INT_MIN), cp_model.INT_MIN)
|
||||
cp_model_helper.to_capped_int64(cp_model.INT_MIN), cp_model.INT_MIN)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapInt64(cp_model.INT_MIN - 1), cp_model.INT_MIN)
|
||||
self.assertEqual(cp_model_helper.CapInt64(15), 15)
|
||||
cp_model_helper.to_capped_int64(cp_model.INT_MIN - 1), cp_model.INT_MIN)
|
||||
self.assertEqual(cp_model_helper.to_capped_int64(15), 15)
|
||||
|
||||
def testCapSub(self):
|
||||
print('testCapSub')
|
||||
self.assertEqual(cp_model_helper.CapSub(10, 5), 5)
|
||||
self.assertEqual(cp_model_helper.capped_subtraction(10, 5), 5)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapSub(cp_model.INT_MIN, 5), cp_model.INT_MIN)
|
||||
cp_model_helper.capped_subtraction(cp_model.INT_MIN, 5), cp_model.INT_MIN)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapSub(cp_model.INT_MIN, -5), cp_model.INT_MIN)
|
||||
cp_model_helper.capped_subtraction(cp_model.INT_MIN, -5), cp_model.INT_MIN)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapSub(cp_model.INT_MAX, 5), cp_model.INT_MAX)
|
||||
cp_model_helper.capped_subtraction(cp_model.INT_MAX, 5), cp_model.INT_MAX)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapSub(cp_model.INT_MAX, -5), cp_model.INT_MAX)
|
||||
cp_model_helper.capped_subtraction(cp_model.INT_MAX, -5), cp_model.INT_MAX)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapSub(2, cp_model.INT_MIN), cp_model.INT_MAX)
|
||||
cp_model_helper.capped_subtraction(2, cp_model.INT_MIN), cp_model.INT_MAX)
|
||||
self.assertEqual(
|
||||
cp_model_helper.CapSub(2, cp_model.INT_MAX), cp_model.INT_MIN)
|
||||
cp_model_helper.capped_subtraction(2, cp_model.INT_MAX), cp_model.INT_MIN)
|
||||
|
||||
def testGetOrMakeIndexFromConstant(self):
|
||||
print('testGetOrMakeIndexFromConstant')
|
||||
@@ -539,8 +539,8 @@ class CpModelTest(unittest.TestCase):
|
||||
y = model.NewIntVar(0, 3, 'y')
|
||||
self.assertEqual(repr(x), 'x(0..4)')
|
||||
self.assertEqual(repr(x * 2), 'ProductCst(x(0..4), 2)')
|
||||
self.assertEqual(repr(x + y), 'SumArray(x(0..4), y(0..3), 0)')
|
||||
self.assertEqual(repr(x + 5), 'SumArray(x(0..4), 5)')
|
||||
self.assertEqual(repr(x + y), 'Sum(x(0..4), y(0..3))')
|
||||
self.assertEqual(repr(x + 5), 'Sum(x(0..4), 5)')
|
||||
|
||||
def testDisplayBounds(self):
|
||||
print('testDisplayBounds')
|
||||
|
||||
@@ -1033,6 +1033,8 @@ public final class CpModel {
|
||||
* rectangle is aligned with the X and Y axis, and is defined by two intervals which represent its
|
||||
* projection onto the X and Y axis.
|
||||
*
|
||||
* <p>Furthermore, one box is optional if at least one of the x or y interval is optional.
|
||||
*
|
||||
* @param xIntervals the X coordinates of the rectangles
|
||||
* @param yIntervals the Y coordinates of the rectangles
|
||||
* @return an instance of the Constraint class
|
||||
|
||||
@@ -33,6 +33,8 @@ BoolVar::BoolVar(int index, CpModelBuilder* builder)
|
||||
: builder_(builder), index_(index) {}
|
||||
|
||||
BoolVar BoolVar::WithName(const std::string& name) {
|
||||
DCHECK(builder_ != nullptr);
|
||||
if (builder_ == nullptr) return *this;
|
||||
builder_->MutableProto()
|
||||
->mutable_variables(PositiveRef(index_))
|
||||
->set_name(name);
|
||||
@@ -40,6 +42,7 @@ BoolVar BoolVar::WithName(const std::string& name) {
|
||||
}
|
||||
|
||||
std::string BoolVar::Name() const {
|
||||
if (builder_ == nullptr) return "null";
|
||||
const std::string& name =
|
||||
builder_->Proto().variables(PositiveRef(index_)).name();
|
||||
if (RefIsPositive(index_)) {
|
||||
@@ -50,6 +53,7 @@ std::string BoolVar::Name() const {
|
||||
}
|
||||
|
||||
std::string BoolVar::DebugString() const {
|
||||
if (builder_ == nullptr) return "null";
|
||||
if (index_ < 0) {
|
||||
return absl::StrFormat("Not(%s)", Not().DebugString());
|
||||
} else {
|
||||
@@ -91,28 +95,48 @@ IntVar::IntVar(int index, CpModelBuilder* builder)
|
||||
}
|
||||
|
||||
IntVar::IntVar(const BoolVar& var) {
|
||||
if (var.builder_ == nullptr) {
|
||||
*this = IntVar();
|
||||
return;
|
||||
}
|
||||
builder_ = var.builder_;
|
||||
index_ = builder_->GetOrCreateIntegerIndex(var.index_);
|
||||
DCHECK(RefIsPositive(index_));
|
||||
}
|
||||
|
||||
BoolVar IntVar::ToBoolVar() const {
|
||||
CHECK_EQ(2, Proto().domain_size());
|
||||
CHECK_GE(Proto().domain(0), 0);
|
||||
CHECK_LE(Proto().domain(1), 1);
|
||||
if (builder_ != nullptr) {
|
||||
const IntegerVariableProto& proto = builder_->Proto().variables(index_);
|
||||
DCHECK_EQ(2, proto.domain_size());
|
||||
DCHECK_GE(proto.domain(0), 0);
|
||||
DCHECK_LE(proto.domain(1), 1);
|
||||
}
|
||||
return BoolVar(index_, builder_);
|
||||
}
|
||||
|
||||
IntVar IntVar::WithName(const std::string& name) {
|
||||
DCHECK(builder_ != nullptr);
|
||||
if (builder_ == nullptr) return *this;
|
||||
builder_->MutableProto()->mutable_variables(index_)->set_name(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
std::string IntVar::Name() const {
|
||||
if (builder_ == nullptr) return "null";
|
||||
return builder_->Proto().variables(index_).name();
|
||||
}
|
||||
|
||||
LinearExpr IntVar::AddConstant(int64_t value) const {
|
||||
return LinearExpr(*this).AddConstant(value);
|
||||
}
|
||||
|
||||
Domain IntVar::Domain() const {
|
||||
if (builder_ == nullptr) return Domain();
|
||||
return ReadDomainFromProto(builder_->Proto().variables(index_));
|
||||
}
|
||||
|
||||
std::string IntVar::DebugString() const {
|
||||
if (builder_ == nullptr) return "null";
|
||||
return VarDebugString(builder_->Proto(), index_);
|
||||
}
|
||||
|
||||
@@ -146,14 +170,6 @@ std::string VarDebugString(const CpModelProto& proto, int index) {
|
||||
return output;
|
||||
}
|
||||
|
||||
const IntegerVariableProto& IntVar::Proto() const {
|
||||
return builder_->Proto().variables(index_);
|
||||
}
|
||||
|
||||
IntegerVariableProto* IntVar::MutableProto() const {
|
||||
return builder_->MutableProto()->mutable_variables(index_);
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const IntVar& var) {
|
||||
os << var.DebugString();
|
||||
return os;
|
||||
@@ -269,12 +285,14 @@ LinearExpr& LinearExpr::AddVar(IntVar var) { return AddTerm(var, 1); }
|
||||
LinearExpr& LinearExpr::AddVar(BoolVar var) { return AddTerm(var, 1); }
|
||||
|
||||
LinearExpr& LinearExpr::AddTerm(IntVar var, int64_t coeff) {
|
||||
DCHECK(var.builder_ != nullptr);
|
||||
variables_.push_back(var.index_);
|
||||
coefficients_.push_back(coeff);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LinearExpr& LinearExpr::AddTerm(BoolVar var, int64_t coeff) {
|
||||
DCHECK(var.builder_ != nullptr);
|
||||
const int index = var.index_;
|
||||
if (RefIsPositive(index)) {
|
||||
variables_.push_back(index);
|
||||
@@ -628,32 +646,48 @@ IntervalVar::IntervalVar(int index, CpModelBuilder* builder)
|
||||
: builder_(builder), index_(index) {}
|
||||
|
||||
IntervalVar IntervalVar::WithName(const std::string& name) {
|
||||
DCHECK(builder_ != nullptr);
|
||||
if (builder_ == nullptr) return *this;
|
||||
builder_->MutableProto()->mutable_constraints(index_)->set_name(name);
|
||||
return *this;
|
||||
}
|
||||
|
||||
LinearExpr IntervalVar::StartExpr() const {
|
||||
return LinearExpr::FromProto(Proto().start());
|
||||
DCHECK(builder_ != nullptr);
|
||||
if (builder_ == nullptr) return LinearExpr();
|
||||
return LinearExpr::FromProto(
|
||||
builder_->Proto().constraints(index_).interval().start());
|
||||
}
|
||||
|
||||
LinearExpr IntervalVar::SizeExpr() const {
|
||||
return LinearExpr::FromProto(Proto().size());
|
||||
DCHECK(builder_ != nullptr);
|
||||
if (builder_ == nullptr) return LinearExpr();
|
||||
return LinearExpr::FromProto(
|
||||
builder_->Proto().constraints(index_).interval().size());
|
||||
}
|
||||
|
||||
LinearExpr IntervalVar::EndExpr() const {
|
||||
return LinearExpr::FromProto(Proto().end());
|
||||
DCHECK(builder_ != nullptr);
|
||||
if (builder_ == nullptr) return LinearExpr();
|
||||
return LinearExpr::FromProto(
|
||||
builder_->Proto().constraints(index_).interval().end());
|
||||
}
|
||||
|
||||
BoolVar IntervalVar::PresenceBoolVar() const {
|
||||
DCHECK(builder_ != nullptr);
|
||||
if (builder_ == nullptr) return BoolVar();
|
||||
return BoolVar(builder_->Proto().constraints(index_).enforcement_literal(0),
|
||||
builder_);
|
||||
}
|
||||
|
||||
const std::string& IntervalVar::Name() const {
|
||||
std::string IntervalVar::Name() const {
|
||||
if (builder_ == nullptr) return "null";
|
||||
return builder_->Proto().constraints(index_).name();
|
||||
}
|
||||
|
||||
std::string IntervalVar::DebugString() const {
|
||||
if (builder_ == nullptr) return "null";
|
||||
|
||||
CHECK_GE(index_, 0);
|
||||
const CpModelProto& proto = builder_->Proto();
|
||||
const ConstraintProto& ct_proto = proto.constraints(index_);
|
||||
@@ -670,16 +704,6 @@ std::string IntervalVar::DebugString() const {
|
||||
return output;
|
||||
}
|
||||
|
||||
const IntervalConstraintProto& IntervalVar::Proto() const {
|
||||
return builder_->Proto().constraints(index_).interval();
|
||||
}
|
||||
|
||||
IntervalConstraintProto* IntervalVar::MutableProto() const {
|
||||
return builder_->MutableProto()
|
||||
->mutable_constraints(index_)
|
||||
->mutable_interval();
|
||||
}
|
||||
|
||||
std::ostream& operator<<(std::ostream& os, const IntervalVar& var) {
|
||||
os << var.DebugString();
|
||||
return os;
|
||||
@@ -1261,6 +1285,16 @@ void CpModelBuilder::AddHint(IntVar var, int64_t value) {
|
||||
cp_model_.mutable_solution_hint()->add_values(value);
|
||||
}
|
||||
|
||||
void CpModelBuilder::AddHint(BoolVar var, bool value) {
|
||||
if (var.index_ >= 0) {
|
||||
cp_model_.mutable_solution_hint()->add_vars(var.index_);
|
||||
cp_model_.mutable_solution_hint()->add_values(value);
|
||||
} else {
|
||||
cp_model_.mutable_solution_hint()->add_vars(PositiveRef(var.index_));
|
||||
cp_model_.mutable_solution_hint()->add_values(!value);
|
||||
}
|
||||
}
|
||||
|
||||
void CpModelBuilder::ClearHints() {
|
||||
cp_model_.mutable_solution_hint()->Clear();
|
||||
}
|
||||
|
||||
@@ -62,13 +62,17 @@ class IntVar;
|
||||
/**
|
||||
* A Boolean variable.
|
||||
*
|
||||
* This class wraps an IntegerVariableProto with domain [0, 1].
|
||||
* It supports the logical negation (Not).
|
||||
* This class refer to an IntegerVariableProto with domain [0, 1] or to its
|
||||
* logical negation (Not). This is called a Boolean Literal in other context.
|
||||
*
|
||||
* This can only be constructed via \c CpModelBuilder.NewBoolVar().
|
||||
*/
|
||||
class BoolVar {
|
||||
public:
|
||||
/// A default constructed BoolVar can be used to mean not defined yet.
|
||||
/// However, it shouldn't be passed to any of the functions in this file.
|
||||
/// Doing so will crash in debug mode and will result in an invalid model in
|
||||
/// opt mode.
|
||||
BoolVar();
|
||||
|
||||
/// Sets the name of the variable.
|
||||
@@ -81,17 +85,14 @@ class BoolVar {
|
||||
/// Returns the logical negation of the current Boolean variable.
|
||||
BoolVar Not() const { return BoolVar(NegatedRef(index_), builder_); }
|
||||
|
||||
/// Equality test with another boolvar.
|
||||
bool operator==(const BoolVar& other) const {
|
||||
return other.builder_ == builder_ && other.index_ == index_;
|
||||
}
|
||||
|
||||
/// Dis-Equality test.
|
||||
bool operator!=(const BoolVar& other) const {
|
||||
return other.builder_ != builder_ || other.index_ != index_;
|
||||
}
|
||||
|
||||
/// Debug string.
|
||||
std::string DebugString() const;
|
||||
|
||||
/**
|
||||
@@ -133,59 +134,55 @@ BoolVar Not(BoolVar x);
|
||||
*
|
||||
* This class wraps an IntegerVariableProto.
|
||||
* This can only be constructed via \c CpModelBuilder.NewIntVar().
|
||||
*
|
||||
* Note that a BoolVar can be used in any place that accept an
|
||||
* IntVar via an implicit cast. It will simply take the value
|
||||
* 0 (when false) or 1 (when true).
|
||||
*/
|
||||
class IntVar {
|
||||
public:
|
||||
/// A default constructed IntVar can be used to mean not defined yet.
|
||||
/// However, it shouldn't be passed to any of the functions in this file.
|
||||
/// Doing so will crash in debug mode and will result in an invalid model in
|
||||
/// opt mode.
|
||||
IntVar();
|
||||
|
||||
/// Implicit cast BoolVar -> IntVar.
|
||||
/// Cast BoolVar -> IntVar.
|
||||
/// The IntVar will take the value 1 (when the bool is true) and 0 otherwise.
|
||||
///
|
||||
/// Warning: If you construct an IntVar from a negated BoolVar, this might
|
||||
/// create a new variable in the model.
|
||||
/// create a new variable in the model. Otherwise this just point to the same
|
||||
/// underlying variable.
|
||||
IntVar(const BoolVar& var); // NOLINT(runtime/explicit)
|
||||
|
||||
/// Cast IntVar -> BoolVar.
|
||||
///
|
||||
/// Warning: This checks that the domain of the var is within {0,1} and
|
||||
/// crash if it is not the case. Use at your own risk.
|
||||
/// Warning: The domain of the var must be within {0,1}. If not, we crash
|
||||
/// in debug mode, and in opt mode you will get an invalid model if you use
|
||||
/// this BoolVar anywhere since it will not have a valid domain.
|
||||
BoolVar ToBoolVar() const;
|
||||
|
||||
/// Sets the name of the variable.
|
||||
IntVar WithName(const std::string& name);
|
||||
|
||||
/// Returns the name of the variable (or the empty string if not set).
|
||||
const std::string& Name() const { return Proto().name(); }
|
||||
std::string Name() const;
|
||||
|
||||
/// Adds a constant value to an integer variable and returns a linear
|
||||
/// expression.
|
||||
LinearExpr AddConstant(int64_t value) const;
|
||||
|
||||
/// Equality test with another IntVar.
|
||||
bool operator==(const IntVar& other) const {
|
||||
return other.builder_ == builder_ && other.index_ == index_;
|
||||
}
|
||||
|
||||
/// Difference test with another IntVar.
|
||||
bool operator!=(const IntVar& other) const {
|
||||
return other.builder_ != builder_ || other.index_ != index_;
|
||||
}
|
||||
|
||||
/// Returns a debug string.
|
||||
// Returns the domain of the variable.
|
||||
Domain Domain() const;
|
||||
|
||||
std::string DebugString() const;
|
||||
|
||||
/// Returns the underlying protobuf object (useful for testing).
|
||||
const IntegerVariableProto& Proto() const;
|
||||
|
||||
/// Returns the mutable underlying protobuf object (useful for model edition).
|
||||
IntegerVariableProto* MutableProto() const;
|
||||
|
||||
/// Returns the index of the variable in the model. This will be non-negative.
|
||||
int index() const { return index_; }
|
||||
|
||||
/// Deprecated. Just do var + cte where needed.
|
||||
LinearExpr AddConstant(int64_t value) const;
|
||||
|
||||
private:
|
||||
friend class BoolVar;
|
||||
friend class CpModelBuilder;
|
||||
@@ -213,8 +210,8 @@ std::ostream& operator<<(std::ostream& os, const IntVar& var);
|
||||
* x when added to the linear expression. It also support operator overloads to
|
||||
* construct the linear expression naturally.
|
||||
*
|
||||
* Furthermore, static methods allows sums and scalar products, with or without
|
||||
* an additional constant.
|
||||
* Furthermore, static methods allow to construct a linear expression from sums
|
||||
* or scalar products.
|
||||
*
|
||||
* Usage:
|
||||
* \code
|
||||
@@ -240,13 +237,11 @@ std::ostream& operator<<(std::ostream& os, const IntVar& var);
|
||||
*/
|
||||
class LinearExpr {
|
||||
public:
|
||||
/// Creates an empty linear expression with value zero.
|
||||
LinearExpr() = default;
|
||||
|
||||
/**
|
||||
* Constructs a linear expression from a Boolean variable.
|
||||
*
|
||||
* It deals with logical negation correctly.
|
||||
*/
|
||||
/// Constructs a linear expression from a Boolean variable.
|
||||
/// It deals with logical negation correctly.
|
||||
LinearExpr(BoolVar var); // NOLINT(runtime/explicit)
|
||||
|
||||
/// Constructs a linear expression from an integer variable.
|
||||
@@ -261,7 +256,7 @@ class LinearExpr {
|
||||
/// Constructs the sum of a list of Boolean variables.
|
||||
static LinearExpr Sum(absl::Span<const BoolVar> vars);
|
||||
|
||||
/// Constructs the sum of a list of Boolean variables.
|
||||
/// Constructs the sum of a list of variables.
|
||||
// TODO(user): Remove when the operators + and * are implemented.
|
||||
static LinearExpr Sum(std::initializer_list<IntVar> vars);
|
||||
|
||||
@@ -313,6 +308,9 @@ class LinearExpr {
|
||||
/// Returns the vector of coefficients.
|
||||
const std::vector<int64_t>& coefficients() const { return coefficients_; }
|
||||
|
||||
/// Returns true if the expression has no variables.
|
||||
const bool IsConstant() const { return variables_.empty(); }
|
||||
|
||||
/// Returns the constant term.
|
||||
int64_t constant() const { return constant_; }
|
||||
|
||||
@@ -371,11 +369,8 @@ class DoubleLinearExpr {
|
||||
public:
|
||||
DoubleLinearExpr();
|
||||
|
||||
/**
|
||||
* Constructs a linear expression from a Boolean variable.
|
||||
*
|
||||
* It deals with logical negation correctly.
|
||||
*/
|
||||
/// Constructs a linear expression from a Boolean variable.
|
||||
/// It deals with logical negation correctly.
|
||||
explicit DoubleLinearExpr(BoolVar var);
|
||||
|
||||
/// Constructs a linear expression from an integer variable.
|
||||
@@ -440,6 +435,9 @@ class DoubleLinearExpr {
|
||||
/// Returns the vector of coefficients.
|
||||
const std::vector<double>& coefficients() const { return coefficients_; }
|
||||
|
||||
// Returns true if the expression has no variable.
|
||||
const bool IsConstant() const { return variables_.empty(); }
|
||||
|
||||
/// Returns the constant term.
|
||||
double constant() const { return constant_; }
|
||||
|
||||
@@ -476,14 +474,17 @@ std::ostream& operator<<(std::ostream& os, const DoubleLinearExpr& e);
|
||||
*/
|
||||
class IntervalVar {
|
||||
public:
|
||||
/// Default ctor.
|
||||
/// A default constructed IntervalVar can be used to mean not defined yet.
|
||||
/// However, it shouldn't be passed to any of the functions in this file.
|
||||
/// Doing so will crash in debug mode and will result in an invalid model in
|
||||
/// opt mode.
|
||||
IntervalVar();
|
||||
|
||||
/// Sets the name of the variable.
|
||||
IntervalVar WithName(const std::string& name);
|
||||
|
||||
/// Returns the name of the interval (or the empty string if not set).
|
||||
const std::string& Name() const;
|
||||
std::string Name() const;
|
||||
|
||||
/// Returns the start linear expression. Note that this rebuilds the
|
||||
/// expression each time this method is called.
|
||||
@@ -517,12 +518,6 @@ class IntervalVar {
|
||||
/// Returns a debug string.
|
||||
std::string DebugString() const;
|
||||
|
||||
/// Returns the underlying protobuf object (useful for testing).
|
||||
const IntervalConstraintProto& Proto() const;
|
||||
|
||||
/// Returns the mutable underlying protobuf object (useful for model edition).
|
||||
IntervalConstraintProto* MutableProto() const;
|
||||
|
||||
/// Returns the index of the interval constraint in the model.
|
||||
int index() const { return index_; }
|
||||
|
||||
@@ -1072,6 +1067,9 @@ class CpModelBuilder {
|
||||
/// Adds hinting to a variable.
|
||||
void AddHint(IntVar var, int64_t value);
|
||||
|
||||
/// Adds hinting to a Boolean variable.
|
||||
void AddHint(BoolVar var, bool value);
|
||||
|
||||
/// Removes all hints.
|
||||
void ClearHints();
|
||||
|
||||
@@ -1209,7 +1207,10 @@ inline LinearExpr operator*(int64_t factor, LinearExpr expr) {
|
||||
|
||||
// For DoubleLinearExpr.
|
||||
|
||||
inline DoubleLinearExpr operator-(DoubleLinearExpr expr) { return expr *= -1; }
|
||||
inline DoubleLinearExpr operator-(DoubleLinearExpr expr) {
|
||||
expr *= -1;
|
||||
return expr;
|
||||
}
|
||||
|
||||
inline DoubleLinearExpr operator+(const DoubleLinearExpr& lhs,
|
||||
const DoubleLinearExpr& rhs) {
|
||||
@@ -1238,23 +1239,13 @@ inline DoubleLinearExpr operator+(DoubleLinearExpr&& lhs,
|
||||
}
|
||||
}
|
||||
|
||||
inline DoubleLinearExpr operator+(const DoubleLinearExpr& lhs, double rhs) {
|
||||
DoubleLinearExpr temp(lhs);
|
||||
temp += rhs;
|
||||
return temp;
|
||||
inline DoubleLinearExpr operator+(DoubleLinearExpr expr, double rhs) {
|
||||
expr += rhs;
|
||||
return expr;
|
||||
}
|
||||
inline DoubleLinearExpr operator+(DoubleLinearExpr&& lhs, double rhs) {
|
||||
lhs += rhs;
|
||||
return std::move(lhs);
|
||||
}
|
||||
inline DoubleLinearExpr operator+(double lhs, DoubleLinearExpr&& rhs) {
|
||||
rhs += lhs;
|
||||
return std::move(rhs);
|
||||
}
|
||||
inline DoubleLinearExpr operator+(double lhs, const DoubleLinearExpr& rhs) {
|
||||
DoubleLinearExpr temp(rhs);
|
||||
temp += lhs;
|
||||
return temp;
|
||||
inline DoubleLinearExpr operator+(double lhs, DoubleLinearExpr expr) {
|
||||
expr += lhs;
|
||||
return expr;
|
||||
}
|
||||
|
||||
inline DoubleLinearExpr operator-(const DoubleLinearExpr& lhs,
|
||||
@@ -1284,24 +1275,14 @@ inline DoubleLinearExpr operator-(DoubleLinearExpr&& lhs,
|
||||
}
|
||||
}
|
||||
|
||||
inline DoubleLinearExpr operator-(const DoubleLinearExpr& lhs, double rhs) {
|
||||
DoubleLinearExpr temp(lhs);
|
||||
temp -= rhs;
|
||||
return temp;
|
||||
inline DoubleLinearExpr operator-(DoubleLinearExpr epxr, double rhs) {
|
||||
epxr -= rhs;
|
||||
return epxr;
|
||||
}
|
||||
inline DoubleLinearExpr operator-(DoubleLinearExpr&& lhs, double rhs) {
|
||||
lhs -= rhs;
|
||||
return std::move(lhs);
|
||||
}
|
||||
inline DoubleLinearExpr operator-(double lhs, DoubleLinearExpr&& rhs) {
|
||||
rhs *= -1;
|
||||
rhs += lhs;
|
||||
return std::move(rhs);
|
||||
}
|
||||
inline DoubleLinearExpr operator-(double lhs, const DoubleLinearExpr& rhs) {
|
||||
DoubleLinearExpr temp = -rhs;
|
||||
temp += lhs;
|
||||
return temp;
|
||||
inline DoubleLinearExpr operator-(double lhs, DoubleLinearExpr expr) {
|
||||
expr *= -1;
|
||||
expr += lhs;
|
||||
return expr;
|
||||
}
|
||||
|
||||
inline DoubleLinearExpr operator*(DoubleLinearExpr expr, double factor) {
|
||||
|
||||
@@ -798,6 +798,7 @@ void AppendNoOverlap2dRelaxation(const ConstraintProto& ct, Model* model,
|
||||
integer_trail->LevelZeroLowerBound(x_sizes[i]) *
|
||||
integer_trail->LevelZeroLowerBound(y_sizes[i]);
|
||||
if (area_min != 0) {
|
||||
// Not including the term if we don't have a view is ok.
|
||||
(void)lc.AddLiteralTerm(presence_literal, area_min);
|
||||
}
|
||||
}
|
||||
@@ -1288,17 +1289,15 @@ void AddNoOverlap2dCutGenerator(const ConstraintProto& ct, Model* m,
|
||||
m->GetOrCreate<IntervalsRepository>();
|
||||
bool has_variable_part = false;
|
||||
for (int i = 0; i < x_intervals.size(); ++i) {
|
||||
// Ignore absent intervals.
|
||||
// Ignore absent rectangles.
|
||||
if (intervals_repository->IsAbsent(x_intervals[i]) ||
|
||||
intervals_repository->IsAbsent(y_intervals[i])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Checks non-present intervals.
|
||||
if ((intervals_repository->IsOptional(x_intervals[i]) &&
|
||||
!intervals_repository->IsPresent(x_intervals[i])) ||
|
||||
(intervals_repository->IsOptional(y_intervals[i]) &&
|
||||
!intervals_repository->IsPresent(y_intervals[i]))) {
|
||||
if (!intervals_repository->IsPresent(x_intervals[i]) ||
|
||||
!intervals_repository->IsPresent(y_intervals[i])) {
|
||||
has_variable_part = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -196,7 +196,7 @@ class LinearExpr(object):
|
||||
@classmethod
|
||||
def Term(cls, expression, coefficient):
|
||||
"""Creates `expression * coefficient`."""
|
||||
if coefficient == 0:
|
||||
if cmh.is_zero(coefficient):
|
||||
return 0
|
||||
else:
|
||||
return expression * coefficient
|
||||
@@ -204,7 +204,7 @@ class LinearExpr(object):
|
||||
@classmethod
|
||||
def IsEmptyOrAllNull(cls, coefficients):
|
||||
for c in coefficients:
|
||||
if c != 0:
|
||||
if not cmh.is_zero(c):
|
||||
return False
|
||||
return True
|
||||
|
||||
@@ -224,41 +224,92 @@ class LinearExpr(object):
|
||||
for index, coeff in zip(proto.vars(), proto.coeffs()):
|
||||
variables.append(IntVar(model, index, None))
|
||||
coeffs.append(coeff)
|
||||
if coeff != 1:
|
||||
if not cmh.is_one(coeff):
|
||||
all_ones = False
|
||||
if all_ones:
|
||||
return _SumArray(variables, offset)
|
||||
else:
|
||||
return _ScalProd(variables, coeffs, offset)
|
||||
|
||||
def GetVarValueMap(self):
|
||||
"""Scans the expression, and return a list of (var_coef_map, constant)."""
|
||||
def GetIntegerVarValueMap(self):
|
||||
"""Scans the expression, and returns (var_coef_map, constant)."""
|
||||
coeffs = collections.defaultdict(int)
|
||||
constant = 0
|
||||
to_process = [(self, 1)]
|
||||
while to_process: # Flatten to avoid recursion.
|
||||
expr, coef = to_process.pop()
|
||||
if isinstance(expr, _ProductCst):
|
||||
expr, coeff = to_process.pop()
|
||||
if cmh.is_integral(expr):
|
||||
constant += coeff * int(expr)
|
||||
elif isinstance(expr, _ProductCst):
|
||||
to_process.append(
|
||||
(expr.Expression(), coef * expr.Coefficient()))
|
||||
(expr.Expression(), coeff * expr.Coefficient()))
|
||||
elif isinstance(expr, _Sum):
|
||||
to_process.append((expr.Left(), coeff))
|
||||
to_process.append((expr.Right(), coeff))
|
||||
elif isinstance(expr, _SumArray):
|
||||
for e in expr.Expressions():
|
||||
to_process.append((e, coef))
|
||||
constant += expr.Constant() * coef
|
||||
to_process.append((e, coeff))
|
||||
constant += expr.Constant() * coeff
|
||||
elif isinstance(expr, _ScalProd):
|
||||
for e, c in zip(expr.Expressions(), expr.Coefficients()):
|
||||
to_process.append((e, coef * c))
|
||||
constant += expr.Constant() * coef
|
||||
to_process.append((e, coeff * c))
|
||||
constant += expr.Constant() * coeff
|
||||
elif isinstance(expr, IntVar):
|
||||
coeffs[expr] += coef
|
||||
coeffs[expr] += coeff
|
||||
elif isinstance(expr, _NotBooleanVariable):
|
||||
constant += coef
|
||||
coeffs[expr.Not()] -= coef
|
||||
constant += coeff
|
||||
coeffs[expr.Not()] -= coeff
|
||||
else:
|
||||
raise TypeError('Unrecognized linear expression: ' + str(expr))
|
||||
|
||||
return coeffs, constant
|
||||
|
||||
def GetFloatVarValueMap(self):
|
||||
"""Scans the expression. Returns (var_coef_map, constant, is_integer)."""
|
||||
coeffs = {}
|
||||
constant = 0
|
||||
to_process = [(self, 1)]
|
||||
while to_process: # Flatten to avoid recursion.
|
||||
expr, coeff = to_process.pop()
|
||||
if cmh.is_integral(expr): # Keep integrality.
|
||||
constant += coeff * int(expr)
|
||||
elif cmh.is_a_number(expr):
|
||||
constant += coeff * float(expr)
|
||||
elif isinstance(expr, _ProductCst):
|
||||
to_process.append(
|
||||
(expr.Expression(), coeff * expr.Coefficient()))
|
||||
elif isinstance(expr, _Sum):
|
||||
to_process.append((expr.Left(), coeff))
|
||||
to_process.append((expr.Right(), coeff))
|
||||
elif isinstance(expr, _SumArray):
|
||||
for e in expr.Expressions():
|
||||
to_process.append((e, coeff))
|
||||
constant += expr.Constant() * coeff
|
||||
elif isinstance(expr, _ScalProd):
|
||||
for e, c in zip(expr.Expressions(), expr.Coefficients()):
|
||||
to_process.append((e, coeff * c))
|
||||
constant += expr.Constant() * coeff
|
||||
elif isinstance(expr, IntVar):
|
||||
if expr in coeffs:
|
||||
coeffs[expr] += coeff
|
||||
else:
|
||||
coeffs[expr] = coeff
|
||||
elif isinstance(expr, _NotBooleanVariable):
|
||||
constant += coeff
|
||||
if expr.Not() in coeffs:
|
||||
coeffs[expr.Not()] -= coeff
|
||||
else:
|
||||
coeffs[expr.Not()] = -coeff
|
||||
else:
|
||||
raise TypeError('Unrecognized linear expression: ' + str(expr))
|
||||
is_integer = cmh.is_integral(constant)
|
||||
if is_integer:
|
||||
for coeff in coeffs.values():
|
||||
if not cmh.is_integral(coeff):
|
||||
is_integer = False
|
||||
break
|
||||
return coeffs, constant, is_integer
|
||||
|
||||
def __hash__(self):
|
||||
return object.__hash__(self)
|
||||
|
||||
@@ -268,36 +319,36 @@ class LinearExpr(object):
|
||||
'please use CpModel.AddAbsEquality')
|
||||
|
||||
def __add__(self, arg):
|
||||
if cmh.IsIntegral(arg) and int(arg) == 0:
|
||||
if cmh.is_zero(arg):
|
||||
return self
|
||||
return _SumArray([self, arg])
|
||||
return _Sum(self, arg)
|
||||
|
||||
def __radd__(self, arg):
|
||||
if cmh.IsIntegral(arg) and int(arg) == 0:
|
||||
if cmh.is_zero(arg):
|
||||
return self
|
||||
return _SumArray([self, arg])
|
||||
return _Sum(self, arg)
|
||||
|
||||
def __sub__(self, arg):
|
||||
if cmh.IsIntegral(arg) and int(arg) == 0:
|
||||
if cmh.is_zero(arg):
|
||||
return self
|
||||
return _SumArray([self, -arg])
|
||||
return _Sum(self, -arg)
|
||||
|
||||
def __rsub__(self, arg):
|
||||
return _SumArray([-self, arg])
|
||||
return _Sum(-self, arg)
|
||||
|
||||
def __mul__(self, arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
if arg == 1:
|
||||
arg = cmh.assert_is_a_number(arg)
|
||||
if cmh.is_one(arg):
|
||||
return self
|
||||
elif arg == 0:
|
||||
elif cmh.is_zero(arg):
|
||||
return 0
|
||||
return _ProductCst(self, arg)
|
||||
|
||||
def __rmul__(self, arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
if arg == 1:
|
||||
arg = cmh.assert_is_a_number(arg)
|
||||
if cmh.is_one(arg):
|
||||
return self
|
||||
elif arg == 0:
|
||||
elif cmh.is_zero(arg):
|
||||
return 0
|
||||
return _ProductCst(self, arg)
|
||||
|
||||
@@ -354,29 +405,29 @@ class LinearExpr(object):
|
||||
def __eq__(self, arg):
|
||||
if arg is None:
|
||||
return False
|
||||
if cmh.IsIntegral(arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
if cmh.is_integral(arg):
|
||||
arg = cmh.assert_is_int64(arg)
|
||||
return BoundedLinearExpression(self, [arg, arg])
|
||||
else:
|
||||
return BoundedLinearExpression(self - arg, [0, 0])
|
||||
|
||||
def __ge__(self, arg):
|
||||
if cmh.IsIntegral(arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
if cmh.is_integral(arg):
|
||||
arg = cmh.assert_is_int64(arg)
|
||||
return BoundedLinearExpression(self, [arg, INT_MAX])
|
||||
else:
|
||||
return BoundedLinearExpression(self - arg, [0, INT_MAX])
|
||||
|
||||
def __le__(self, arg):
|
||||
if cmh.IsIntegral(arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
if cmh.is_integral(arg):
|
||||
arg = cmh.assert_is_int64(arg)
|
||||
return BoundedLinearExpression(self, [INT_MIN, arg])
|
||||
else:
|
||||
return BoundedLinearExpression(self - arg, [INT_MIN, 0])
|
||||
|
||||
def __lt__(self, arg):
|
||||
if cmh.IsIntegral(arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
if cmh.is_integral(arg):
|
||||
arg = cmh.assert_is_int64(arg)
|
||||
if arg == INT_MIN:
|
||||
raise ArithmeticError('< INT_MIN is not supported')
|
||||
return BoundedLinearExpression(self, [INT_MIN, arg - 1])
|
||||
@@ -384,8 +435,8 @@ class LinearExpr(object):
|
||||
return BoundedLinearExpression(self - arg, [INT_MIN, -1])
|
||||
|
||||
def __gt__(self, arg):
|
||||
if cmh.IsIntegral(arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
if cmh.is_integral(arg):
|
||||
arg = cmh.assert_is_int64(arg)
|
||||
if arg == INT_MAX:
|
||||
raise ArithmeticError('> INT_MAX is not supported')
|
||||
return BoundedLinearExpression(self, [arg + 1, INT_MAX])
|
||||
@@ -395,8 +446,8 @@ class LinearExpr(object):
|
||||
def __ne__(self, arg):
|
||||
if arg is None:
|
||||
return True
|
||||
if cmh.IsIntegral(arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
if cmh.is_integral(arg):
|
||||
arg = cmh.assert_is_int64(arg)
|
||||
if arg == INT_MAX:
|
||||
return BoundedLinearExpression(self, [INT_MIN, INT_MAX - 1])
|
||||
elif arg == INT_MIN:
|
||||
@@ -409,17 +460,40 @@ class LinearExpr(object):
|
||||
[INT_MIN, -1, 1, INT_MAX])
|
||||
|
||||
|
||||
class _Sum(LinearExpr):
|
||||
"""Represents the sum of two LinearExprs."""
|
||||
|
||||
def __init__(self, left, right):
|
||||
for x in [left, right]:
|
||||
if not cmh.is_a_number(x) and not isinstance(x, LinearExpr):
|
||||
raise TypeError('Not an linear expression: ' + str(x))
|
||||
self.__left = left
|
||||
self.__right = right
|
||||
|
||||
def Left(self):
|
||||
return self.__left
|
||||
|
||||
def Right(self):
|
||||
return self.__right
|
||||
|
||||
def __str__(self):
|
||||
return f'({self.__left} + {self.__right})'
|
||||
|
||||
def __repr__(self):
|
||||
return f'Sum({repr(self.__left)}, {repr(self.__right)})'
|
||||
|
||||
|
||||
class _ProductCst(LinearExpr):
|
||||
"""Represents the product of a LinearExpr by a constant."""
|
||||
|
||||
def __init__(self, expr, coef):
|
||||
coef = cmh.AssertIsInt64(coef)
|
||||
def __init__(self, expr, coeff):
|
||||
coeff = cmh.assert_is_a_number(coeff)
|
||||
if isinstance(expr, _ProductCst):
|
||||
self.__expr = expr.Expression()
|
||||
self.__coef = expr.Coefficient() * coef
|
||||
self.__coef = expr.Coefficient() * coeff
|
||||
else:
|
||||
self.__expr = expr
|
||||
self.__coef = coef
|
||||
self.__coef = coeff
|
||||
|
||||
def __str__(self):
|
||||
if self.__coef == -1:
|
||||
@@ -445,8 +519,10 @@ class _SumArray(LinearExpr):
|
||||
self.__expressions = []
|
||||
self.__constant = constant
|
||||
for x in expressions:
|
||||
if cmh.IsIntegral(x):
|
||||
x = cmh.AssertIsInt64(x)
|
||||
if cmh.is_a_number(x):
|
||||
if cmh.is_zero(x):
|
||||
continue
|
||||
x = cmh.assert_is_a_number(x)
|
||||
self.__constant += x
|
||||
elif isinstance(x, LinearExpr):
|
||||
self.__expressions.append(x)
|
||||
@@ -483,11 +559,11 @@ class _ScalProd(LinearExpr):
|
||||
'In the LinearExpr.ScalProd method, the expression array and the '
|
||||
' coefficient array must have the same length.')
|
||||
for e, c in zip(expressions, coefficients):
|
||||
c = cmh.AssertIsInt64(c)
|
||||
if c == 0:
|
||||
c = cmh.assert_is_a_number(c)
|
||||
if cmh.is_zero(c):
|
||||
continue
|
||||
if cmh.IsIntegral(e):
|
||||
e = cmh.AssertIsInt64(e)
|
||||
if cmh.is_a_number(e):
|
||||
e = cmh.assert_is_a_number(e)
|
||||
self.__constant += e * c
|
||||
elif isinstance(e, LinearExpr):
|
||||
self.__expressions.append(e)
|
||||
@@ -498,15 +574,15 @@ class _ScalProd(LinearExpr):
|
||||
def __str__(self):
|
||||
output = None
|
||||
for expr, coeff in zip(self.__expressions, self.__coefficients):
|
||||
if not output and coeff == 1:
|
||||
if not output and cmh.is_one(coeff):
|
||||
output = str(expr)
|
||||
elif not output and coeff == -1:
|
||||
elif not output and cmh.is_minus_one(coeff):
|
||||
output = '-' + str(expr)
|
||||
elif not output:
|
||||
output = '{} * {}'.format(coeff, str(expr))
|
||||
elif coeff == 1:
|
||||
elif cmh.is_one(coeff):
|
||||
output += ' + {}'.format(str(expr))
|
||||
elif coeff == -1:
|
||||
elif cmh.is_minus_one(coeff):
|
||||
output += ' - {}'.format(str(expr))
|
||||
elif coeff > 1:
|
||||
output += ' + {} * {}'.format(coeff, str(expr))
|
||||
@@ -560,7 +636,7 @@ class IntVar(LinearExpr):
|
||||
# model is a CpModelProto, domain is a Domain, and name is a string.
|
||||
# case 2:
|
||||
# model is a CpModelProto, domain is an index (int), and name is None.
|
||||
if cmh.IsIntegral(domain) and name is None:
|
||||
if cmh.is_integral(domain) and name is None:
|
||||
self.__index = int(domain)
|
||||
self.__var = model.variables[domain]
|
||||
else:
|
||||
@@ -681,7 +757,7 @@ class BoundedLinearExpression(object):
|
||||
return self.__bounds
|
||||
|
||||
def __bool__(self):
|
||||
coeffs_map, constant = self.__expr.GetVarValueMap()
|
||||
coeffs_map, constant = self.__expr.GetIntegerVarValueMap()
|
||||
all_coeffs = set(coeffs_map.values())
|
||||
same_var = set([0])
|
||||
eq_bounds = [0, 0]
|
||||
@@ -701,136 +777,6 @@ class BoundedLinearExpression(object):
|
||||
+ ' is not supported.')
|
||||
|
||||
|
||||
class DoubleLinearExpr(object):
|
||||
"""Holds an double linear expression.
|
||||
|
||||
* In CP-SAT, the objective can be a linear expression with double
|
||||
coefficients:
|
||||
|
||||
```
|
||||
model.Minimize(cp_model.DoubleLinearExpr.Sum(expressions))
|
||||
model.Maximize(cp_model.DoubleLinearExpr.ScalProd(expressions, coefficients))
|
||||
```
|
||||
"""
|
||||
|
||||
@classmethod
|
||||
def Sum(cls, expressions):
|
||||
"""Creates the expression sum(expressions)."""
|
||||
if len(expressions) == 1:
|
||||
return expressions[0]
|
||||
coefficients = [1.0] * len(expressions)
|
||||
return DoubleLinearExpr(expressions, coefficients)
|
||||
|
||||
@classmethod
|
||||
def ScalProd(cls, expressions, coefficients):
|
||||
"""Creates the expression sum(expressions[i] * coefficients[i])."""
|
||||
if LinearExpr.IsEmptyOrAllNull(coefficients):
|
||||
return 0.0
|
||||
else:
|
||||
return DoubleLinearExpr(expressions, coefficients)
|
||||
|
||||
@classmethod
|
||||
def Term(cls, expression, coefficient):
|
||||
"""Creates `expression * coefficient`."""
|
||||
if coefficient == 0:
|
||||
return 0
|
||||
else:
|
||||
return DoubleLinearExpr([expression], [coefficient])
|
||||
|
||||
@classmethod
|
||||
def IsEmptyOrAllNull(cls, coefficients):
|
||||
for c in coefficients:
|
||||
if c != 0.0:
|
||||
return False
|
||||
return True
|
||||
|
||||
def Expressions(self):
|
||||
return self.__expressions
|
||||
|
||||
def Coefficients(self):
|
||||
return self.__coefficients
|
||||
|
||||
def Constant(self):
|
||||
return self.__constant
|
||||
|
||||
def GetVarValueMap(self):
|
||||
"""Scans the expression, and return a list of (var_coef_map, constant)."""
|
||||
coeffs = collections.defaultdict(int)
|
||||
constant = 0.0
|
||||
to_process = [(self, 1.0)]
|
||||
while to_process: # Flatten to avoid recursion.
|
||||
expr, coef = to_process.pop()
|
||||
if isinstance(expr, IntVar):
|
||||
coeffs[expr] += coef
|
||||
elif isinstance(expr, _NotBooleanVariable):
|
||||
constant += coef
|
||||
coeffs[expr.Not()] -= coef
|
||||
elif isinstance(expr, DoubleLinearExpr):
|
||||
for e, c in zip(expr.Expressions(), expr.Coefficients()):
|
||||
to_process.append((e, coef * c))
|
||||
constant += expr.Constant() * coef
|
||||
else:
|
||||
raise TypeError('Unrecognized linear expression: ' + str(expr))
|
||||
|
||||
return coeffs, constant
|
||||
|
||||
def __init__(self, expressions, coefficients, constant=0):
|
||||
self.__expressions = []
|
||||
self.__coefficients = []
|
||||
self.__constant = constant
|
||||
if len(expressions) != len(coefficients):
|
||||
raise TypeError(
|
||||
'In the DoubleLinearExpr builders, the expression array and the '
|
||||
' coefficient array must have the same length.')
|
||||
for e, c in zip(expressions, coefficients):
|
||||
c = cmh.AssertIsNumber(c)
|
||||
if c == 0.0:
|
||||
continue
|
||||
if cmh.IsNumber(e):
|
||||
e = cmh.AssertIsNumber(e)
|
||||
self.__constant += e * c
|
||||
elif isinstance(e, DoubleLinearExpr):
|
||||
self.__constant += c * e.Constant()
|
||||
for ee, cc in zip(e.Expressions(), e.Coefficients()):
|
||||
self.__expressions.append(ee)
|
||||
self.__coefficients.append(cc)
|
||||
elif isinstance(e, IntVar) or isinstance(e, _NotBooleanVariable):
|
||||
self.__expressions.append(e)
|
||||
self.__coefficients.append(c)
|
||||
else:
|
||||
raise TypeError('Not a double linear expression: ' + str(e))
|
||||
|
||||
def __str__(self):
|
||||
output = None
|
||||
for expr, coeff in zip(self.__expressions, self.__coefficients):
|
||||
if not output and coeff == 1.0:
|
||||
output = str(expr)
|
||||
elif not output and coeff == -1.0:
|
||||
output = '-' + str(expr)
|
||||
elif not output:
|
||||
output = '{} * {}'.format(coeff, str(expr))
|
||||
elif coeff == 1.0:
|
||||
output += ' + {}'.format(str(expr))
|
||||
elif coeff == -1.0:
|
||||
output += ' - {}'.format(str(expr))
|
||||
elif coeff > 1.0:
|
||||
output += ' + {} * {}'.format(coeff, str(expr))
|
||||
elif coeff < -1.0:
|
||||
output += ' - {} * {}'.format(-coeff, str(expr))
|
||||
if self.__constant > 0.0:
|
||||
output += ' + {}'.format(self.__constant)
|
||||
elif self.__constant < 0.0:
|
||||
output += ' - {}'.format(-self.__constant)
|
||||
if output is None:
|
||||
output = '0.0'
|
||||
return output
|
||||
|
||||
def __repr__(self):
|
||||
return 'DoubleLinearExpr([{}], [{}], {})'.format(
|
||||
', '.join(map(repr, self.__expressions)),
|
||||
', '.join(map(repr, self.__coefficients)), self.__constant)
|
||||
|
||||
|
||||
class Constraint(object):
|
||||
"""Base class for constraints.
|
||||
|
||||
@@ -868,12 +814,12 @@ class Constraint(object):
|
||||
self.
|
||||
"""
|
||||
|
||||
if cmh.IsIntegral(boolvar) and int(boolvar) == 1:
|
||||
if cmh.is_integral(boolvar) and int(boolvar) == 1:
|
||||
# Always true. Do nothing.
|
||||
pass
|
||||
elif isinstance(boolvar, list):
|
||||
for b in boolvar:
|
||||
if cmh.IsIntegral(b) and int(b) == 1:
|
||||
if cmh.is_integral(b) and int(b) == 1:
|
||||
pass
|
||||
else:
|
||||
self.__constraint.enforcement_literal.append(b.Index())
|
||||
@@ -983,7 +929,7 @@ def ObjectIsATrueLiteral(literal):
|
||||
proto = literal.Not().Proto()
|
||||
return (len(proto.domain) == 2 and proto.domain[0] == 0 and
|
||||
proto.domain[1] == 0)
|
||||
if cmh.IsIntegral(literal):
|
||||
if cmh.is_integral(literal):
|
||||
return int(literal) == 1
|
||||
return False
|
||||
|
||||
@@ -998,7 +944,7 @@ def ObjectIsAFalseLiteral(literal):
|
||||
proto = literal.Not().Proto()
|
||||
return (len(proto.domain) == 2 and proto.domain[0] == 1 and
|
||||
proto.domain[1] == 1)
|
||||
if cmh.IsIntegral(literal):
|
||||
if cmh.is_integral(literal):
|
||||
return int(literal) == 0
|
||||
return False
|
||||
|
||||
@@ -1072,17 +1018,19 @@ class CpModel(object):
|
||||
if isinstance(linear_expr, LinearExpr):
|
||||
ct = Constraint(self.__model.constraints)
|
||||
model_ct = self.__model.constraints[ct.Index()]
|
||||
coeffs_map, constant = linear_expr.GetVarValueMap()
|
||||
coeffs_map, constant = linear_expr.GetIntegerVarValueMap()
|
||||
for t in coeffs_map.items():
|
||||
if not isinstance(t[0], IntVar):
|
||||
raise TypeError('Wrong argument' + str(t))
|
||||
c = cmh.AssertIsInt64(t[1])
|
||||
c = cmh.assert_is_int64(t[1])
|
||||
model_ct.linear.vars.append(t[0].Index())
|
||||
model_ct.linear.coeffs.append(c)
|
||||
model_ct.linear.domain.extend(
|
||||
[cmh.CapSub(x, constant) for x in domain.FlattenedIntervals()])
|
||||
model_ct.linear.domain.extend([
|
||||
cmh.capped_subtraction(x, constant)
|
||||
for x in domain.FlattenedIntervals()
|
||||
])
|
||||
return ct
|
||||
elif cmh.IsIntegral(linear_expr):
|
||||
elif cmh.is_integral(linear_expr):
|
||||
if not domain.Contains(int(linear_expr)):
|
||||
return self.AddBoolOr([]) # Evaluate to false.
|
||||
# Nothing to do otherwise.
|
||||
@@ -1135,7 +1083,7 @@ class CpModel(object):
|
||||
if not variables:
|
||||
raise ValueError('AddElement expects a non-empty variables array')
|
||||
|
||||
if cmh.IsIntegral(index):
|
||||
if cmh.is_integral(index):
|
||||
return self.Add(list(variables)[int(index)] == target)
|
||||
|
||||
ct = Constraint(self.__model.constraints)
|
||||
@@ -1173,8 +1121,8 @@ class CpModel(object):
|
||||
ct = Constraint(self.__model.constraints)
|
||||
model_ct = self.__model.constraints[ct.Index()]
|
||||
for arc in arcs:
|
||||
tail = cmh.AssertIsInt32(arc[0])
|
||||
head = cmh.AssertIsInt32(arc[1])
|
||||
tail = cmh.assert_is_int32(arc[0])
|
||||
head = cmh.assert_is_int32(arc[1])
|
||||
lit = self.GetOrMakeBooleanIndex(arc[2])
|
||||
model_ct.circuit.tails.append(tail)
|
||||
model_ct.circuit.heads.append(head)
|
||||
@@ -1217,7 +1165,7 @@ class CpModel(object):
|
||||
raise TypeError('Tuple ' + str(t) + ' has the wrong arity')
|
||||
ar = []
|
||||
for v in t:
|
||||
ar.append(cmh.AssertIsInt64(v))
|
||||
ar.append(cmh.assert_is_int64(v))
|
||||
model_ct.table.values.extend(ar)
|
||||
return ct
|
||||
|
||||
@@ -1308,18 +1256,18 @@ class CpModel(object):
|
||||
model_ct = self.__model.constraints[ct.Index()]
|
||||
model_ct.automaton.vars.extend(
|
||||
[self.GetOrMakeIndex(x) for x in transition_variables])
|
||||
starting_state = cmh.AssertIsInt64(starting_state)
|
||||
starting_state = cmh.assert_is_int64(starting_state)
|
||||
model_ct.automaton.starting_state = starting_state
|
||||
for v in final_states:
|
||||
v = cmh.AssertIsInt64(v)
|
||||
v = cmh.assert_is_int64(v)
|
||||
model_ct.automaton.final_states.append(v)
|
||||
for t in transition_triples:
|
||||
if len(t) != 3:
|
||||
raise TypeError('Tuple ' + str(t) +
|
||||
' has the wrong arity (!= 3)')
|
||||
tail = cmh.AssertIsInt64(t[0])
|
||||
label = cmh.AssertIsInt64(t[1])
|
||||
head = cmh.AssertIsInt64(t[2])
|
||||
tail = cmh.assert_is_int64(t[0])
|
||||
label = cmh.assert_is_int64(t[1])
|
||||
head = cmh.assert_is_int64(t[2])
|
||||
model_ct.automaton.transition_tail.append(tail)
|
||||
model_ct.automaton.transition_label.append(label)
|
||||
model_ct.automaton.transition_head.append(head)
|
||||
@@ -1653,7 +1601,7 @@ class CpModel(object):
|
||||
Returns:
|
||||
An `IntervalVar` object.
|
||||
"""
|
||||
size = cmh.AssertIsInt64(size)
|
||||
size = cmh.assert_is_int64(size)
|
||||
start_expr = self.ParseLinearExpression(start)
|
||||
size_expr = self.ParseLinearExpression(size)
|
||||
end_expr = self.ParseLinearExpression(start + size)
|
||||
@@ -1724,7 +1672,7 @@ class CpModel(object):
|
||||
Returns:
|
||||
An `IntervalVar` object.
|
||||
"""
|
||||
size = cmh.AssertIsInt64(size)
|
||||
size = cmh.assert_is_int64(size)
|
||||
start_expr = self.ParseLinearExpression(start)
|
||||
size_expr = self.ParseLinearExpression(size)
|
||||
end_expr = self.ParseLinearExpression(start + size)
|
||||
@@ -1871,8 +1819,8 @@ class CpModel(object):
|
||||
elif (isinstance(arg, _ProductCst) and
|
||||
isinstance(arg.Expression(), IntVar) and arg.Coefficient() == -1):
|
||||
return -arg.Expression().Index() - 1
|
||||
elif cmh.IsIntegral(arg):
|
||||
arg = cmh.AssertIsInt64(arg)
|
||||
elif cmh.is_integral(arg):
|
||||
arg = cmh.assert_is_int64(arg)
|
||||
return self.GetOrMakeIndexFromConstant(arg)
|
||||
else:
|
||||
raise TypeError('NotSupported: model.GetOrMakeIndex(' + str(arg) +
|
||||
@@ -1886,8 +1834,8 @@ class CpModel(object):
|
||||
elif isinstance(arg, _NotBooleanVariable):
|
||||
self.AssertIsBooleanVariable(arg.Not())
|
||||
return arg.Index()
|
||||
elif cmh.IsIntegral(arg):
|
||||
cmh.AssertIsBoolean(arg)
|
||||
elif cmh.is_integral(arg):
|
||||
cmh.assert_is_boolean(arg)
|
||||
return self.GetOrMakeIndexFromConstant(int(arg))
|
||||
else:
|
||||
raise TypeError('NotSupported: model.GetOrMakeBooleanIndex(' +
|
||||
@@ -1917,7 +1865,7 @@ class CpModel(object):
|
||||
"""Returns a LinearExpressionProto built from a LinearExpr instance."""
|
||||
result = cp_model_pb2.LinearExpressionProto()
|
||||
mult = -1 if negate else 1
|
||||
if cmh.IsIntegral(linear_expr):
|
||||
if cmh.is_integral(linear_expr):
|
||||
result.offset = int(linear_expr) * mult
|
||||
return result
|
||||
|
||||
@@ -1926,12 +1874,12 @@ class CpModel(object):
|
||||
result.coeffs.append(mult)
|
||||
return result
|
||||
|
||||
coeffs_map, constant = linear_expr.GetVarValueMap()
|
||||
coeffs_map, constant = linear_expr.GetIntegerVarValueMap()
|
||||
result.offset = constant * mult
|
||||
for t in coeffs_map.items():
|
||||
if not isinstance(t[0], IntVar):
|
||||
raise TypeError('Wrong argument' + str(t))
|
||||
c = cmh.AssertIsInt64(t[1])
|
||||
c = cmh.assert_is_int64(t[1])
|
||||
result.vars.append(t[0].Index())
|
||||
result.coeffs.append(c * mult)
|
||||
return result
|
||||
@@ -1949,28 +1897,29 @@ class CpModel(object):
|
||||
else:
|
||||
self.__model.objective.vars.append(self.Negated(obj.Index()))
|
||||
self.__model.objective.scaling_factor = -1
|
||||
elif isinstance(obj, DoubleLinearExpr):
|
||||
coeffs_map, constant = obj.GetVarValueMap()
|
||||
self.__model.floating_point_objective.maximize = not minimize
|
||||
self.__model.floating_point_objective.offset = constant
|
||||
for v, c, in coeffs_map.items():
|
||||
self.__model.floating_point_objective.coeffs.append(c)
|
||||
self.__model.floating_point_objective.vars.append(v.Index())
|
||||
elif isinstance(obj, LinearExpr):
|
||||
coeffs_map, constant = obj.GetVarValueMap()
|
||||
if minimize:
|
||||
self.__model.objective.scaling_factor = 1
|
||||
self.__model.objective.offset = constant
|
||||
else:
|
||||
self.__model.objective.scaling_factor = -1
|
||||
self.__model.objective.offset = -constant
|
||||
for v, c, in coeffs_map.items():
|
||||
self.__model.objective.coeffs.append(c)
|
||||
coeffs_map, constant, is_integer = obj.GetFloatVarValueMap()
|
||||
if is_integer:
|
||||
if minimize:
|
||||
self.__model.objective.vars.append(v.Index())
|
||||
self.__model.objective.scaling_factor = 1
|
||||
self.__model.objective.offset = constant
|
||||
else:
|
||||
self.__model.objective.vars.append(self.Negated(v.Index()))
|
||||
elif cmh.IsIntegral(obj):
|
||||
self.__model.objective.scaling_factor = -1
|
||||
self.__model.objective.offset = -constant
|
||||
for v, c, in coeffs_map.items():
|
||||
self.__model.objective.coeffs.append(c)
|
||||
if minimize:
|
||||
self.__model.objective.vars.append(v.Index())
|
||||
else:
|
||||
self.__model.objective.vars.append(
|
||||
self.Negated(v.Index()))
|
||||
else:
|
||||
self.__model.floating_point_objective.maximize = not minimize
|
||||
self.__model.floating_point_objective.offset = constant
|
||||
for v, c, in coeffs_map.items():
|
||||
self.__model.floating_point_objective.coeffs.append(c)
|
||||
self.__model.floating_point_objective.vars.append(v.Index())
|
||||
elif cmh.is_integral(obj):
|
||||
self.__model.objective.offset = int(obj)
|
||||
self.__model.objective.scaling_factor = 1
|
||||
else:
|
||||
@@ -2062,7 +2011,7 @@ class CpModel(object):
|
||||
|
||||
def EvaluateLinearExpr(expression, solution):
|
||||
"""Evaluate a linear expression against a solution."""
|
||||
if cmh.IsIntegral(expression):
|
||||
if cmh.is_integral(expression):
|
||||
return int(expression)
|
||||
if not isinstance(expression, LinearExpr):
|
||||
raise TypeError('Cannot interpret %s as a linear expression.' %
|
||||
@@ -2071,27 +2020,35 @@ def EvaluateLinearExpr(expression, solution):
|
||||
value = 0
|
||||
to_process = [(expression, 1)]
|
||||
while to_process:
|
||||
expr, coef = to_process.pop()
|
||||
if isinstance(expr, _ProductCst):
|
||||
to_process.append((expr.Expression(), coef * expr.Coefficient()))
|
||||
expr, coeff = to_process.pop()
|
||||
if cmh.is_integral(expr):
|
||||
value += int(expr) * coeff
|
||||
elif isinstance(expr, _ProductCst):
|
||||
to_process.append((expr.Expression(), coeff * expr.Coefficient()))
|
||||
elif isinstance(expr, _Sum):
|
||||
to_process.append((expr.Left(), coeff))
|
||||
to_process.append((expr.Right(), coeff))
|
||||
elif isinstance(expr, _SumArray):
|
||||
for e in expr.Expressions():
|
||||
to_process.append((e, coef))
|
||||
value += expr.Constant() * coef
|
||||
to_process.append((e, coeff))
|
||||
value += expr.Constant() * coeff
|
||||
elif isinstance(expr, _ScalProd):
|
||||
for e, c in zip(expr.Expressions(), expr.Coefficients()):
|
||||
to_process.append((e, coef * c))
|
||||
value += expr.Constant() * coef
|
||||
to_process.append((e, coeff * c))
|
||||
value += expr.Constant() * coeff
|
||||
elif isinstance(expr, IntVar):
|
||||
value += coef * solution.solution[expr.Index()]
|
||||
value += coeff * solution.solution[expr.Index()]
|
||||
elif isinstance(expr, _NotBooleanVariable):
|
||||
value += coef * (1 - solution.solution[expr.Not().Index()])
|
||||
value += coeff * (1 - solution.solution[expr.Not().Index()])
|
||||
else:
|
||||
raise TypeError(f'Cannot interpret {expr} as a linear expression.')
|
||||
|
||||
return value
|
||||
|
||||
|
||||
def EvaluateBooleanExpression(literal, solution):
|
||||
"""Evaluate a boolean expression against a solution."""
|
||||
if cmh.IsIntegral(literal):
|
||||
if cmh.is_integral(literal):
|
||||
return bool(literal)
|
||||
elif isinstance(literal, IntVar) or isinstance(literal,
|
||||
_NotBooleanVariable):
|
||||
@@ -2101,8 +2058,7 @@ def EvaluateBooleanExpression(literal, solution):
|
||||
else:
|
||||
return not solution.solution[-index - 1]
|
||||
else:
|
||||
raise TypeError('Cannot interpret %s as a boolean expression.' %
|
||||
literal)
|
||||
raise TypeError(f'Cannot interpret {literal} as a boolean expression.')
|
||||
|
||||
|
||||
class CpSolver(object):
|
||||
@@ -2308,14 +2264,13 @@ class CpSolverSolutionCallback(pywrapsat.SolutionCallback):
|
||||
"""
|
||||
if not self.HasResponse():
|
||||
raise RuntimeError('Solve() has not be called.')
|
||||
if cmh.IsIntegral(lit):
|
||||
if cmh.is_integral(lit):
|
||||
return bool(lit)
|
||||
elif isinstance(lit, IntVar) or isinstance(lit, _NotBooleanVariable):
|
||||
index = lit.Index()
|
||||
return self.SolutionBooleanValue(index)
|
||||
else:
|
||||
raise TypeError('Cannot interpret %s as a boolean expression.' %
|
||||
lit)
|
||||
raise TypeError(f'Cannot interpret {lit} as a boolean expression.')
|
||||
|
||||
def Value(self, expression):
|
||||
"""Evaluates an linear expression in the current solution.
|
||||
@@ -2332,32 +2287,36 @@ class CpSolverSolutionCallback(pywrapsat.SolutionCallback):
|
||||
"""
|
||||
if not self.HasResponse():
|
||||
raise RuntimeError('Solve() has not be called.')
|
||||
if cmh.IsIntegral(expression):
|
||||
return int(expression)
|
||||
if not isinstance(expression, LinearExpr):
|
||||
raise TypeError('Cannot interpret %s as a linear expression.' %
|
||||
expression)
|
||||
|
||||
value = 0
|
||||
to_process = [(expression, 1)]
|
||||
while to_process:
|
||||
expr, coef = to_process.pop()
|
||||
if isinstance(expr, _ProductCst):
|
||||
expr, coeff = to_process.pop()
|
||||
if cmh.is_integral(expr):
|
||||
value += int(expr) * coeff
|
||||
elif isinstance(expr, _ProductCst):
|
||||
to_process.append(
|
||||
(expr.Expression(), coef * expr.Coefficient()))
|
||||
(expr.Expression(), coeff * expr.Coefficient()))
|
||||
elif isinstance(expr, _Sum):
|
||||
to_process.append((expr.Left(), coeff))
|
||||
to_process.append((expr.Right(), coeff))
|
||||
elif isinstance(expr, _SumArray):
|
||||
for e in expr.Expressions():
|
||||
to_process.append((e, coef))
|
||||
value += expr.Constant() * coef
|
||||
to_process.append((e, coeff))
|
||||
value += expr.Constant() * coeff
|
||||
elif isinstance(expr, _ScalProd):
|
||||
for e, c in zip(expr.Expressions(), expr.Coefficients()):
|
||||
to_process.append((e, coef * c))
|
||||
value += expr.Constant() * coef
|
||||
to_process.append((e, coeff * c))
|
||||
value += expr.Constant() * coeff
|
||||
elif isinstance(expr, IntVar):
|
||||
value += coef * self.SolutionIntegerValue(expr.Index())
|
||||
value += coeff * self.SolutionIntegerValue(expr.Index())
|
||||
elif isinstance(expr, _NotBooleanVariable):
|
||||
value += coef * (1 -
|
||||
self.SolutionIntegerValue(expr.Not().Index()))
|
||||
value += coeff * (1 -
|
||||
self.SolutionIntegerValue(expr.Not().Index()))
|
||||
else:
|
||||
raise TypeError(
|
||||
f'Cannot interpret {expression} as a linear expression.')
|
||||
|
||||
return value
|
||||
|
||||
|
||||
|
||||
@@ -21,48 +21,70 @@ INT32_MIN = -2147483648
|
||||
INT32_MAX = 2147483647
|
||||
|
||||
|
||||
def IsIntegral(x):
|
||||
def is_integral(x):
|
||||
"""Checks if x has either a number.Integral or a np.integer type."""
|
||||
return isinstance(x, numbers.Integral) or isinstance(x, np.integer)
|
||||
|
||||
|
||||
def IsNumber(x):
|
||||
def is_a_number(x):
|
||||
"""Checks if x has either a number.Number or a np.double type."""
|
||||
return isinstance(x, numbers.Number) or isinstance(x, np.double)
|
||||
return isinstance(x, numbers.Number) or isinstance(
|
||||
x, np.double) or isinstance(x, np.integer)
|
||||
|
||||
|
||||
def AssertIsInt64(x):
|
||||
def is_zero(x):
|
||||
"""Checks if the x is 0 or 0.0."""
|
||||
return (is_integral(x) and int(x) == 0) or (is_a_number(x) and
|
||||
float(x) == 0.0)
|
||||
|
||||
|
||||
def is_one(x):
|
||||
"""Checks if x is 1 or 1.0."""
|
||||
return (is_integral(x) and int(x) == 1) or (is_a_number(x) and
|
||||
float(x) == 1.0)
|
||||
|
||||
|
||||
def is_minus_one(x):
|
||||
"""Checks if x is -1 or -1.0."""
|
||||
return (is_integral(x) and int(x) == -1) or (is_a_number(x) and
|
||||
float(x) == -1.0)
|
||||
|
||||
|
||||
def assert_is_int64(x):
|
||||
"""Asserts that x is integer and x is in [min_int_64, max_int_64]."""
|
||||
if not IsIntegral(x):
|
||||
if not is_integral(x):
|
||||
raise TypeError('Not an integer: %s' % x)
|
||||
if x < INT_MIN or x > INT_MAX:
|
||||
raise OverflowError('Does not fit in an int64_t: %s' % x)
|
||||
return int(x)
|
||||
|
||||
|
||||
def AssertIsInt32(x):
|
||||
def assert_is_int32(x):
|
||||
"""Asserts that x is integer and x is in [min_int_32, max_int_32]."""
|
||||
if not IsIntegral(x):
|
||||
if not is_integral(x):
|
||||
raise TypeError('Not an integer: %s' % x)
|
||||
if x < INT32_MIN or x > INT32_MAX:
|
||||
raise OverflowError('Does not fit in an int32_t: %s' % x)
|
||||
return int(x)
|
||||
|
||||
|
||||
def AssertIsBoolean(x):
|
||||
def assert_is_boolean(x):
|
||||
"""Asserts that x is 0 or 1."""
|
||||
if not IsIntegral(x) or x < 0 or x > 1:
|
||||
if not is_integral(x) or x < 0 or x > 1:
|
||||
raise TypeError('Not an boolean: %s' % x)
|
||||
|
||||
|
||||
def AssertIsNumber(x):
|
||||
def assert_is_a_number(x):
|
||||
"""Asserts that x is a number and returns it."""
|
||||
if not IsNumber(x):
|
||||
if not is_a_number(x):
|
||||
raise TypeError('Not an number: %s' % x)
|
||||
return float(x)
|
||||
elif is_integral(x):
|
||||
return int(x)
|
||||
else:
|
||||
return float(x)
|
||||
|
||||
|
||||
def CapInt64(v):
|
||||
def to_capped_int64(v):
|
||||
"""Restrict v within [INT_MIN..INT_MAX] range."""
|
||||
if v > INT_MAX:
|
||||
return INT_MAX
|
||||
@@ -71,10 +93,10 @@ def CapInt64(v):
|
||||
return v
|
||||
|
||||
|
||||
def CapSub(x, y):
|
||||
def capped_subtraction(x, y):
|
||||
"""Saturated arithmetics. Returns x - y truncated to the int64_t range."""
|
||||
AssertIsInt64(x)
|
||||
AssertIsInt64(y)
|
||||
assert_is_int64(x)
|
||||
assert_is_int64(y)
|
||||
if y == 0:
|
||||
return x
|
||||
if x == y:
|
||||
@@ -88,4 +110,4 @@ def CapSub(x, y):
|
||||
return INT_MIN
|
||||
if y == INT_MIN:
|
||||
return INT_MAX
|
||||
return CapInt64(x - y)
|
||||
return to_capped_int64(x - y)
|
||||
|
||||
@@ -54,7 +54,7 @@ void NurseSat() {
|
||||
// Creates shift variables.
|
||||
// shifts[(n, d, s)]: nurse 'n' works shift 's' on day 'd'.
|
||||
// [START variables]
|
||||
std::map<std::tuple<int, int, int>, IntVar> shifts;
|
||||
std::map<std::tuple<int, int, int>, BoolVar> shifts;
|
||||
for (int n : all_nurses) {
|
||||
for (int d : all_days) {
|
||||
for (int s : all_shifts) {
|
||||
@@ -70,12 +70,12 @@ void NurseSat() {
|
||||
// [START exactly_one_nurse]
|
||||
for (int d : all_days) {
|
||||
for (int s : all_shifts) {
|
||||
std::vector<IntVar> x;
|
||||
LinearExpr sum;
|
||||
for (int n : all_nurses) {
|
||||
auto key = std::make_tuple(n, d, s);
|
||||
x.push_back(shifts[key]);
|
||||
sum += shifts[key];
|
||||
}
|
||||
cp_model.AddEquality(LinearExpr::Sum(x), 1);
|
||||
cp_model.AddEquality(sum, 1);
|
||||
}
|
||||
}
|
||||
// [END exactly_one_nurse]
|
||||
@@ -84,12 +84,12 @@ void NurseSat() {
|
||||
// [START at_most_one_shift]
|
||||
for (int n : all_nurses) {
|
||||
for (int d : all_days) {
|
||||
std::vector<IntVar> x;
|
||||
LinearExpr sum;
|
||||
for (int s : all_shifts) {
|
||||
auto key = std::make_tuple(n, d, s);
|
||||
x.push_back(shifts[key]);
|
||||
sum += shifts[key];
|
||||
}
|
||||
cp_model.AddLessOrEqual(LinearExpr::Sum(x), 1);
|
||||
cp_model.AddLessOrEqual(sum, 1);
|
||||
}
|
||||
}
|
||||
// [END at_most_one_shift]
|
||||
@@ -107,7 +107,7 @@ void NurseSat() {
|
||||
max_shifts_per_nurse = min_shifts_per_nurse + 1;
|
||||
}
|
||||
for (int n : all_nurses) {
|
||||
std::vector<IntVar> num_shifts_worked;
|
||||
std::vector<BoolVar> num_shifts_worked;
|
||||
// int num_shifts_worked = 0;
|
||||
for (int d : all_days) {
|
||||
for (int s : all_shifts) {
|
||||
|
||||
@@ -1183,7 +1183,7 @@ void GenerateNoOverlap2dEnergyCut(
|
||||
// TODO(user): use the offset of the energy expression if better
|
||||
// than size_min * demand_min.
|
||||
DCHECK(!x_helper->IsPresent(t) || !y_helper->IsPresent(t));
|
||||
const Literal lit = x_helper->IsOptional(t) && !x_helper->IsPresent(t)
|
||||
const Literal lit = !x_helper->IsPresent(t)
|
||||
? x_helper->PresenceLiteral(t)
|
||||
: y_helper->PresenceLiteral(t);
|
||||
if (cut.AddLiteralTerm(lit,
|
||||
@@ -1252,8 +1252,7 @@ CutGenerator CreateNoOverlap2dEnergyCutGenerator(
|
||||
if (y_helper->IsAbsent(box) || y_helper->IsAbsent(box)) continue;
|
||||
// We cannot consider boxes controlled by 2 active enforcement
|
||||
// literals.
|
||||
if (x_helper->IsOptional(box) && y_helper->IsOptional(box) &&
|
||||
!x_helper->IsPresent(box) && !y_helper->IsPresent(box) &&
|
||||
if (!x_helper->IsPresent(box) && !y_helper->IsPresent(box) &&
|
||||
x_helper->PresenceLiteral(box) !=
|
||||
y_helper->PresenceLiteral(box)) {
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user