[CP-SAT] improve doc on C++ builder API; rewrite python API to remove the DoubleLinearExpr class

This commit is contained in:
Laurent Perron
2021-12-14 17:15:17 +01:00
parent 3bf1f19e0c
commit 0c22a715b2
13 changed files with 428 additions and 433 deletions

View File

@@ -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++;
}
}

View File

@@ -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 =

View File

@@ -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.

View File

@@ -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);

View File

@@ -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')

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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) {

View File

@@ -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;
}

View File

@@ -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

View File

@@ -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)

View File

@@ -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) {

View File

@@ -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;