Files
ortools-clone/src/constraint_solver/expr_array.cc

3147 lines
101 KiB
C++
Raw Normal View History

2013-06-11 14:49:19 +00:00
// Copyright 2010-2013 Google
2010-09-15 12:42:33 +00:00
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// Array Expression constraints
2011-09-21 15:16:48 +00:00
#include <string.h>
#include <algorithm>
#include <string>
#include <vector>
2010-09-15 12:42:33 +00:00
#include "base/integral_types.h"
#include "base/logging.h"
#include "base/scoped_ptr.h"
#include "base/stringprintf.h"
2011-09-21 15:16:48 +00:00
#include "constraint_solver/constraint_solver.h"
2010-09-15 12:42:33 +00:00
#include "constraint_solver/constraint_solveri.h"
2011-09-05 13:45:29 +00:00
#include "util/string_array.h"
2010-09-15 12:42:33 +00:00
namespace operations_research {
namespace {
// ----- Tree Array Constraint -----
2010-09-15 12:42:33 +00:00
class TreeArrayConstraint : public CastConstraint {
public:
TreeArrayConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
IntVar* const sum_var)
: CastConstraint(solver, sum_var),
2012-05-30 08:53:53 +00:00
vars_(vars),
size_(vars.size()),
block_size_(solver->parameters().array_split_size) {
std::vector<int> lengths;
lengths.push_back(size_);
while (lengths.back() > 1) {
const int current = lengths.back();
lengths.push_back((current + block_size_ - 1) / block_size_);
}
tree_.resize(lengths.size());
for (int i = 0; i < lengths.size(); ++i) {
tree_[i].resize(lengths[lengths.size() - i - 1]);
}
DCHECK_GE(tree_.size(), 1);
DCHECK_EQ(1, tree_[0].size());
root_node_ = &tree_[0][0];
}
string DebugStringInternal(const string& name) const {
return StringPrintf("%s(%s) == %s", name.c_str(),
2012-05-30 11:53:36 +00:00
DebugStringVector(vars_, ", ").c_str(),
target_var_->DebugString().c_str());
}
void AcceptInternal(const string& name, ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(name, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
2012-05-30 11:53:36 +00:00
vars_.data(), vars_.size());
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
target_var_);
visitor->EndVisitConstraint(name, this);
}
// Increases min by delta_min, reduces max by delta_max.
void ReduceRange(int depth, int position, int64 delta_min, int64 delta_max) {
NodeInfo* const info = &tree_[depth][position];
if (delta_min > 0) {
info->node_min.SetValue(solver(), info->node_min.Value() + delta_min);
}
if (delta_max > 0) {
info->node_max.SetValue(solver(), info->node_max.Value() - delta_max);
2010-09-15 12:42:33 +00:00
}
}
// Sets the range on the given node.
void SetRange(int depth, int position, int64 new_min, int64 new_max) {
NodeInfo* const info = &tree_[depth][position];
if (new_min > info->node_min.Value()) {
info->node_min.SetValue(solver(), new_min);
}
if (new_max < info->node_max.Value()) {
info->node_max.SetValue(solver(), new_max);
}
}
void InitLeaf(int position, int64 var_min, int64 var_max) {
2012-06-14 16:43:04 +00:00
InitNode(MaxDepth(), position, var_min, var_max);
}
void InitNode(int depth, int position, int64 node_min, int64 node_max) {
2012-06-14 16:43:04 +00:00
tree_[depth][position].node_min.SetValue(solver(), node_min);
tree_[depth][position].node_max.SetValue(solver(), node_max);
}
int64 Min(int depth, int position) const {
return tree_[depth][position].node_min.Value();
}
int64 Max(int depth, int position) const {
return tree_[depth][position].node_max.Value();
}
int64 RootMin() const { return root_node_->node_min.Value(); }
int64 RootMax() const { return root_node_->node_max.Value(); }
int Parent(int position) const { return position / block_size_; }
int ChildStart(int position) const { return position * block_size_; }
int ChildEnd(int depth, int position) const {
DCHECK_LT(depth + 1, tree_.size());
return std::min((position + 1) * block_size_ - 1, Width(depth + 1) - 1);
}
bool IsLeaf(int depth) const { return depth == MaxDepth(); }
int MaxDepth() const { return tree_.size() - 1; }
int Width(int depth) const { return tree_[depth].size(); }
2011-07-11 20:13:14 +00:00
protected:
2012-05-30 11:53:36 +00:00
std::vector<IntVar*> vars_;
const int size_;
private:
struct NodeInfo {
NodeInfo() : node_min(0), node_max(0) {}
Rev<int64> node_min;
Rev<int64> node_max;
};
std::vector<std::vector<NodeInfo> > tree_;
const int block_size_;
NodeInfo* root_node_;
};
2010-09-15 12:42:33 +00:00
// ---------- Sum Array ----------
// Some of these optimizations here are described in:
2010-11-26 09:54:32 +00:00
// "Bounds consistency techniques for long linear constraints". In
// Workshop on Techniques for Implementing Constraint Programming
// Systems (TRICS), a workshop of CP 2002, N. Beldiceanu, W. Harvey,
// Martin Henz, Francois Laburthe, Eric Monfroy, Tobias Müller,
// Laurent Perron and Christian Schulte editors, pages 3946, 2002.
// ----- SumConstraint -----
2010-09-15 12:42:33 +00:00
// This constraint implements sum(vars) == sum_var.
class SumConstraint : public TreeArrayConstraint {
2010-09-15 12:42:33 +00:00
public:
SumConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
IntVar* const sum_var)
2012-05-30 08:53:53 +00:00
: TreeArrayConstraint(solver, vars, sum_var), sum_demon_(NULL) {}
2010-09-15 12:42:33 +00:00
virtual ~SumConstraint() {}
2010-09-15 12:42:33 +00:00
virtual void Post() {
for (int i = 0; i < size_; ++i) {
Demon* const demon = MakeConstraintDemon1(
solver(), this, &SumConstraint::LeafChanged, "LeafChanged", i);
vars_[i]->WhenRange(demon);
}
sum_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
solver(), this, &SumConstraint::SumChanged, "SumChanged"));
target_var_->WhenRange(sum_demon_);
2010-09-15 12:42:33 +00:00
}
virtual void InitialPropagate() {
// Copy vars to leaf nodes.
for (int i = 0; i < size_; ++i) {
2012-06-14 16:43:04 +00:00
InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
}
// Compute up.
for (int i = MaxDepth() - 1; i >= 0; --i) {
for (int j = 0; j < Width(i); ++j) {
int64 sum_min = 0;
int64 sum_max = 0;
const int block_start = ChildStart(j);
const int block_end = ChildEnd(i, j);
for (int k = block_start; k <= block_end; ++k) {
sum_min += Min(i + 1, k);
sum_max += Max(i + 1, k);
}
2012-06-14 16:43:04 +00:00
InitNode(i, j, sum_min, sum_max);
}
}
// Propagate to sum_var.
target_var_->SetRange(RootMin(), RootMax());
2010-09-15 12:42:33 +00:00
// Push down.
SumChanged();
2010-09-15 12:42:33 +00:00
}
void SumChanged() {
2012-06-14 16:43:04 +00:00
if (target_var_->Max() == RootMin() && target_var_->Max() != kint64max) {
// We can fix all terms to min.
for (int i = 0; i < size_; ++i) {
vars_[i]->SetValue(vars_[i]->Min());
}
2012-06-14 16:43:04 +00:00
} else if (target_var_->Min() == RootMax() &&
target_var_->Min() != kint64min) {
// We can fix all terms to max.
for (int i = 0; i < size_; ++i) {
vars_[i]->SetValue(vars_[i]->Max());
}
} else {
PushDown(0, 0, target_var_->Min(), target_var_->Max());
}
2010-09-15 12:42:33 +00:00
}
void PushDown(int depth, int position, int64 new_min, int64 new_max) {
// Nothing to do?
if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
return;
}
2010-09-15 12:42:33 +00:00
// Leaf node -> push to leaf var.
if (IsLeaf(depth)) {
vars_[position]->SetRange(new_min, new_max);
return;
}
2010-09-15 12:42:33 +00:00
// Standard propagation from the bounds of the sum to the
// individuals terms.
2010-09-15 12:42:33 +00:00
// These are maintained automatically in the tree structure.
const int64 sum_min = Min(depth, position);
const int64 sum_max = Max(depth, position);
2010-09-15 12:42:33 +00:00
// Intersect the new bounds with the computed bounds.
new_max = std::min(sum_max, new_max);
new_min = std::max(sum_min, new_min);
2010-09-15 12:42:33 +00:00
// Detect failure early.
if (new_max < sum_min || new_min > sum_max) {
solver()->Fail();
}
2010-09-15 12:42:33 +00:00
// Push to children nodes.
const int block_start = ChildStart(position);
const int block_end = ChildEnd(depth, position);
for (int i = block_start; i <= block_end; ++i) {
const int64 target_var_min = Min(depth + 1, i);
const int64 target_var_max = Max(depth + 1, i);
const int64 residual_min = sum_min - target_var_min;
const int64 residual_max = sum_max - target_var_max;
PushDown(depth + 1, i, new_min - residual_max, new_max - residual_min);
}
// TODO(user) : Is the diameter optimization (see reference
// above, rule 5) useful?
2010-09-15 12:42:33 +00:00
}
void LeafChanged(int term_index) {
IntVar* const var = vars_[term_index];
PushUp(term_index, var->Min() - var->OldMin(), var->OldMax() - var->Max());
EnqueueDelayedDemon(sum_demon_); // TODO(user): Is this needed?
2010-09-15 12:42:33 +00:00
}
void PushUp(int position, int64 delta_min, int64 delta_max) {
DCHECK_GE(delta_max, 0);
DCHECK_GE(delta_min, 0);
DCHECK_GT(delta_min + delta_max, 0);
for (int depth = MaxDepth(); depth >= 0; --depth) {
ReduceRange(depth, position, delta_min, delta_max);
position = Parent(position);
}
DCHECK_EQ(0, position);
target_var_->SetRange(RootMin(), RootMax());
2010-09-15 12:42:33 +00:00
}
string DebugString() const { return DebugStringInternal("Sum"); }
2011-07-11 20:13:14 +00:00
virtual void Accept(ModelVisitor* const visitor) const {
AcceptInternal(ModelVisitor::kSumEqual, visitor);
}
private:
Demon* sum_demon_;
};
2010-09-15 12:42:33 +00:00
2012-06-14 16:43:04 +00:00
// ----- SafeSumConstraint -----
bool DetectSumOverflow(const std::vector<IntVar*>& vars) {
int64 sum_min = 0;
int64 sum_max = 0;
for (int i = 0; i < vars.size(); ++i) {
sum_min = CapAdd(sum_min, vars[i]->Min());
sum_max = CapAdd(sum_max, vars[i]->Max());
if (sum_min == kint64min || sum_max == kint64max) {
return true;
}
}
return false;
}
// This constraint implements sum(vars) == sum_var.
class SafeSumConstraint : public TreeArrayConstraint {
public:
SafeSumConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
2012-06-14 16:43:04 +00:00
IntVar* const sum_var)
: TreeArrayConstraint(solver, vars, sum_var), sum_demon_(NULL) {}
virtual ~SafeSumConstraint() {}
virtual void Post() {
for (int i = 0; i < size_; ++i) {
Demon* const demon = MakeConstraintDemon1(
solver(), this, &SafeSumConstraint::LeafChanged, "LeafChanged", i);
2012-06-14 16:43:04 +00:00
vars_[i]->WhenRange(demon);
}
sum_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
solver(), this, &SafeSumConstraint::SumChanged, "SumChanged"));
2012-06-14 16:43:04 +00:00
target_var_->WhenRange(sum_demon_);
}
void SafeComputeNode(int depth, int position, int64* const sum_min,
2012-06-14 16:43:04 +00:00
int64* const sum_max) {
DCHECK_LT(depth, MaxDepth());
const int block_start = ChildStart(position);
const int block_end = ChildEnd(depth, position);
for (int k = block_start; k <= block_end; ++k) {
if (*sum_min != kint64min) {
*sum_min = CapAdd(*sum_min, Min(depth + 1, k));
}
if (*sum_max != kint64max) {
*sum_max = CapAdd(*sum_max, Max(depth + 1, k));
}
if (*sum_min == kint64min && *sum_max == kint64max) {
break;
}
}
}
virtual void InitialPropagate() {
// Copy vars to leaf nodes.
for (int i = 0; i < size_; ++i) {
InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
}
// Compute up.
for (int i = MaxDepth() - 1; i >= 0; --i) {
for (int j = 0; j < Width(i); ++j) {
int64 sum_min = 0;
int64 sum_max = 0;
SafeComputeNode(i, j, &sum_min, &sum_max);
InitNode(i, j, sum_min, sum_max);
}
}
// Propagate to sum_var.
target_var_->SetRange(RootMin(), RootMax());
// Push down.
SumChanged();
}
void SumChanged() {
2013-06-11 14:49:19 +00:00
DCHECK(CheckInternalState());
2012-06-14 16:43:04 +00:00
if (target_var_->Max() == RootMin()) {
// We can fix all terms to min.
for (int i = 0; i < size_; ++i) {
vars_[i]->SetValue(vars_[i]->Min());
}
} else if (target_var_->Min() == RootMax()) {
// We can fix all terms to max.
for (int i = 0; i < size_; ++i) {
vars_[i]->SetValue(vars_[i]->Max());
}
} else {
PushDown(0, 0, target_var_->Min(), target_var_->Max());
}
}
void PushDown(int depth, int position, int64 new_min, int64 new_max) {
// Nothing to do?
if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
return;
}
// Leaf node -> push to leaf var.
if (IsLeaf(depth)) {
vars_[position]->SetRange(new_min, new_max);
return;
}
// Standard propagation from the bounds of the sum to the
// individuals terms.
// These are maintained automatically in the tree structure.
const int64 sum_min = Min(depth, position);
const int64 sum_max = Max(depth, position);
// Intersect the new bounds with the computed bounds.
new_max = std::min(sum_max, new_max);
new_min = std::max(sum_min, new_min);
// Detect failure early.
if (new_max < sum_min || new_min > sum_max) {
solver()->Fail();
}
// Push to children nodes.
const int block_start = ChildStart(position);
const int block_end = ChildEnd(depth, position);
for (int pos = block_start; pos <= block_end; ++pos) {
const int64 target_var_min = Min(depth + 1, pos);
const int64 residual_min =
sum_min != kint64min ? CapSub(sum_min, target_var_min) : kint64min;
2012-06-14 16:43:04 +00:00
const int64 target_var_max = Max(depth + 1, pos);
const int64 residual_max =
sum_max != kint64max ? CapSub(sum_max, target_var_max) : kint64max;
PushDown(depth + 1, pos,
(residual_max == kint64min ? kint64min
: CapSub(new_min, residual_max)),
(residual_min == kint64max ? kint64min
: CapSub(new_max, residual_min)));
2012-06-14 16:43:04 +00:00
}
// TODO(user) : Is the diameter optimization (see reference
// above, rule 5) useful?
}
void LeafChanged(int term_index) {
IntVar* const var = vars_[term_index];
PushUp(term_index, CapSub(var->Min(), var->OldMin()),
2012-06-14 16:43:04 +00:00
CapSub(var->OldMax(), var->Max()));
EnqueueDelayedDemon(sum_demon_); // TODO(user): Is this needed?
2012-06-14 16:43:04 +00:00
}
void PushUp(int position, int64 delta_min, int64 delta_max) {
DCHECK_GE(delta_max, 0);
DCHECK_GE(delta_min, 0);
2013-06-11 14:49:19 +00:00
if (CapAdd(delta_min, delta_max) == 0) {
// This may happen if the computation of old min/max has under/overflowed
// resulting in no actual change in min and max.
return;
}
2012-06-14 16:43:04 +00:00
bool delta_corrupted = false;
for (int depth = MaxDepth(); depth >= 0; --depth) {
if (Min(depth, position) != kint64min &&
Max(depth, position) != kint64max && delta_min != kint64max &&
delta_max != kint64max && !delta_corrupted) { // No overflow.
2012-06-14 16:43:04 +00:00
ReduceRange(depth, position, delta_min, delta_max);
} else if (depth == MaxDepth()) { // Leaf.
SetRange(depth, position, vars_[position]->Min(),
2012-06-14 16:43:04 +00:00
vars_[position]->Max());
delta_corrupted = true;
} else { // Recompute.
int64 sum_min = 0;
int64 sum_max = 0;
SafeComputeNode(depth, position, &sum_min, &sum_max);
if (sum_min == kint64min && sum_max == kint64max) {
return; // Nothing to do upward.
}
SetRange(depth, position, sum_min, sum_max);
delta_corrupted = true;
}
position = Parent(position);
}
DCHECK_EQ(0, position);
target_var_->SetRange(RootMin(), RootMax());
}
string DebugString() const { return DebugStringInternal("Sum"); }
2012-06-14 16:43:04 +00:00
virtual void Accept(ModelVisitor* const visitor) const {
AcceptInternal(ModelVisitor::kSumEqual, visitor);
}
private:
2013-06-11 14:49:19 +00:00
bool CheckInternalState() {
for (int i = 0; i < size_; ++i) {
CheckLeaf(i, vars_[i]->Min(), vars_[i]->Max());
}
// Check up.
for (int i = MaxDepth() - 1; i >= 0; --i) {
for (int j = 0; j < Width(i); ++j) {
int64 sum_min = 0;
int64 sum_max = 0;
SafeComputeNode(i, j, &sum_min, &sum_max);
CheckNode(i, j, sum_min, sum_max);
}
}
return true;
}
void CheckLeaf(int position, int64 var_min, int64 var_max) {
CheckNode(MaxDepth(), position, var_min, var_max);
}
void CheckNode(int depth, int position, int64 node_min, int64 node_max) {
DCHECK_EQ(Min(depth, position), node_min);
DCHECK_EQ(Max(depth, position), node_max);
}
2012-06-14 16:43:04 +00:00
Demon* sum_demon_;
};
2010-09-15 12:42:33 +00:00
// ---------- Min Array ----------
// This constraint implements min(vars) == min_var.
class MinConstraint : public TreeArrayConstraint {
2010-09-15 12:42:33 +00:00
public:
MinConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
IntVar* const min_var)
2012-05-30 08:53:53 +00:00
: TreeArrayConstraint(solver, vars, min_var), min_demon_(NULL) {}
2010-09-15 12:42:33 +00:00
virtual ~MinConstraint() {}
2010-09-15 12:42:33 +00:00
virtual void Post() {
2010-09-15 12:42:33 +00:00
for (int i = 0; i < size_; ++i) {
Demon* const demon = MakeConstraintDemon1(
solver(), this, &MinConstraint::LeafChanged, "LeafChanged", i);
vars_[i]->WhenRange(demon);
2010-09-15 12:42:33 +00:00
}
min_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
solver(), this, &MinConstraint::MinVarChanged, "MinVarChanged"));
target_var_->WhenRange(min_demon_);
2010-09-15 12:42:33 +00:00
}
virtual void InitialPropagate() {
// Copy vars to leaf nodes.
for (int i = 0; i < size_; ++i) {
2012-06-14 16:43:04 +00:00
InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
2010-09-15 12:42:33 +00:00
}
// Compute up.
for (int i = MaxDepth() - 1; i >= 0; --i) {
for (int j = 0; j < Width(i); ++j) {
int64 min_min = kint64max;
int64 min_max = kint64max;
const int block_start = ChildStart(j);
const int block_end = ChildEnd(i, j);
for (int k = block_start; k <= block_end; ++k) {
min_min = std::min(min_min, Min(i + 1, k));
min_max = std::min(min_max, Max(i + 1, k));
}
2012-06-14 16:43:04 +00:00
InitNode(i, j, min_min, min_max);
2010-09-15 12:42:33 +00:00
}
}
// Propagate to min_var.
target_var_->SetRange(RootMin(), RootMax());
2010-09-15 12:42:33 +00:00
// Push down.
MinVarChanged();
2010-09-15 12:42:33 +00:00
}
void MinVarChanged() {
PushDown(0, 0, target_var_->Min(), target_var_->Max());
2010-09-15 12:42:33 +00:00
}
void PushDown(int depth, int position, int64 new_min, int64 new_max) {
// Nothing to do?
if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
return;
2010-09-15 12:42:33 +00:00
}
// Leaf node -> push to leaf var.
if (IsLeaf(depth)) {
vars_[position]->SetRange(new_min, new_max);
return;
2010-09-15 12:42:33 +00:00
}
const int64 node_min = Min(depth, position);
const int64 node_max = Max(depth, position);
2010-09-15 12:42:33 +00:00
int candidate = -1;
int active = 0;
const int block_start = ChildStart(position);
const int block_end = ChildEnd(depth, position);
2010-09-15 12:42:33 +00:00
if (new_max < node_max) {
// Look if only one candidat to push the max down.
for (int i = block_start; i <= block_end; ++i) {
if (Min(depth + 1, i) <= new_max) {
if (active++ >= 1) {
break;
}
candidate = i;
}
2010-09-15 12:42:33 +00:00
}
if (active == 0) {
solver()->Fail();
2010-09-15 12:42:33 +00:00
}
}
if (node_min < new_min) {
for (int i = block_start; i <= block_end; ++i) {
if (i == candidate && active == 1) {
PushDown(depth + 1, i, new_min, new_max);
} else {
PushDown(depth + 1, i, new_min, Max(depth + 1, i));
2010-09-15 12:42:33 +00:00
}
}
} else if (active == 1) {
PushDown(depth + 1, candidate, Min(depth + 1, candidate), new_max);
2010-09-15 12:42:33 +00:00
}
}
void LeafChanged(int term_index) {
IntVar* const var = vars_[term_index];
SetRange(MaxDepth(), term_index, var->Min(), var->Max());
PushUp(term_index);
}
2012-05-31 10:53:29 +00:00
void PushUp(int position) {
int depth = MaxDepth();
while (depth > 0) {
const int parent = Parent(position);
const int parent_depth = depth - 1;
int64 min_min = kint64max;
int64 min_max = kint64max;
const int block_start = ChildStart(parent);
const int block_end = ChildEnd(parent_depth, parent);
for (int k = block_start; k <= block_end; ++k) {
min_min = std::min(min_min, Min(depth, k));
min_max = std::min(min_max, Max(depth, k));
}
if (min_min > Min(parent_depth, parent) ||
min_max < Max(parent_depth, parent)) {
SetRange(parent_depth, parent, min_min, min_max);
} else {
break;
2010-09-15 12:42:33 +00:00
}
depth = parent_depth;
2012-06-05 08:50:33 +00:00
position = parent;
}
if (depth == 0) { // We have pushed all the way up.
target_var_->SetRange(RootMin(), RootMax());
2010-09-15 12:42:33 +00:00
}
2012-06-04 14:11:32 +00:00
MinVarChanged();
2010-09-15 12:42:33 +00:00
}
string DebugString() const { return DebugStringInternal("Min"); }
2010-09-15 12:42:33 +00:00
2011-07-11 20:13:14 +00:00
virtual void Accept(ModelVisitor* const visitor) const {
AcceptInternal(ModelVisitor::kMinEqual, visitor);
2011-07-11 20:13:14 +00:00
}
2010-09-15 12:42:33 +00:00
private:
Demon* min_demon_;
2010-09-15 12:42:33 +00:00
};
// ---------- Max Array ----------
2010-09-15 12:42:33 +00:00
// This constraint implements max(vars) == max_var.
class MaxConstraint : public TreeArrayConstraint {
public:
MaxConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
IntVar* const max_var)
2012-05-30 08:53:53 +00:00
: TreeArrayConstraint(solver, vars, max_var), max_demon_(NULL) {}
2010-09-15 12:42:33 +00:00
virtual ~MaxConstraint() {}
2010-09-15 12:42:33 +00:00
virtual void Post() {
2010-09-15 12:42:33 +00:00
for (int i = 0; i < size_; ++i) {
Demon* const demon = MakeConstraintDemon1(
solver(), this, &MaxConstraint::LeafChanged, "LeafChanged", i);
vars_[i]->WhenRange(demon);
2010-09-15 12:42:33 +00:00
}
max_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
solver(), this, &MaxConstraint::MaxVarChanged, "MaxVarChanged"));
target_var_->WhenRange(max_demon_);
2010-09-15 12:42:33 +00:00
}
virtual void InitialPropagate() {
// Copy vars to leaf nodes.
2010-09-15 12:42:33 +00:00
for (int i = 0; i < size_; ++i) {
2012-06-14 16:43:04 +00:00
InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
2010-09-15 12:42:33 +00:00
}
// Compute up.
for (int i = MaxDepth() - 1; i >= 0; --i) {
for (int j = 0; j < Width(i); ++j) {
int64 max_min = kint64min;
int64 max_max = kint64min;
const int block_start = ChildStart(j);
const int block_end = ChildEnd(i, j);
for (int k = block_start; k <= block_end; ++k) {
max_min = std::max(max_min, Min(i + 1, k));
max_max = std::max(max_max, Max(i + 1, k));
2010-09-15 12:42:33 +00:00
}
2012-06-14 16:43:04 +00:00
InitNode(i, j, max_min, max_max);
2010-09-15 12:42:33 +00:00
}
}
// Propagate to min_var.
target_var_->SetRange(RootMin(), RootMax());
2011-07-11 20:13:14 +00:00
// Push down.
MaxVarChanged();
2011-07-11 20:13:14 +00:00
}
2010-09-15 12:42:33 +00:00
void MaxVarChanged() {
PushDown(0, 0, target_var_->Min(), target_var_->Max());
2010-09-15 12:42:33 +00:00
}
void PushDown(int depth, int position, int64 new_min, int64 new_max) {
// Nothing to do?
if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
return;
2010-09-15 12:42:33 +00:00
}
// Leaf node -> push to leaf var.
if (IsLeaf(depth)) {
vars_[position]->SetRange(new_min, new_max);
return;
2010-09-15 12:42:33 +00:00
}
const int64 node_min = Min(depth, position);
const int64 node_max = Max(depth, position);
2010-09-15 12:42:33 +00:00
int candidate = -1;
int active = 0;
const int block_start = ChildStart(position);
const int block_end = ChildEnd(depth, position);
2010-09-15 12:42:33 +00:00
if (node_min < new_min) {
// Look if only one candidat to push the max down.
for (int i = block_start; i <= block_end; ++i) {
if (Max(depth + 1, i) >= new_min) {
if (active++ >= 1) {
break;
}
candidate = i;
}
2010-09-15 12:42:33 +00:00
}
if (active == 0) {
solver()->Fail();
2010-09-15 12:42:33 +00:00
}
}
if (node_max > new_max) {
for (int i = block_start; i <= block_end; ++i) {
if (i == candidate && active == 1) {
PushDown(depth + 1, i, new_min, new_max);
} else {
PushDown(depth + 1, i, Min(depth + 1, i), new_max);
}
2010-09-15 12:42:33 +00:00
}
} else if (active == 1) {
PushDown(depth + 1, candidate, new_min, Max(depth + 1, candidate));
2010-09-15 12:42:33 +00:00
}
}
void LeafChanged(int term_index) {
IntVar* const var = vars_[term_index];
SetRange(MaxDepth(), term_index, var->Min(), var->Max());
PushUp(term_index);
}
2012-05-31 10:53:29 +00:00
void PushUp(int position) {
int depth = MaxDepth();
while (depth > 0) {
const int parent = Parent(position);
const int parent_depth = depth - 1;
int64 max_min = kint64min;
int64 max_max = kint64min;
const int block_start = ChildStart(parent);
const int block_end = ChildEnd(parent_depth, parent);
for (int k = block_start; k <= block_end; ++k) {
2012-06-01 08:46:18 +00:00
max_min = std::max(max_min, Min(depth, k));
max_max = std::max(max_max, Max(depth, k));
2010-09-15 12:42:33 +00:00
}
if (max_min > Min(parent_depth, parent) ||
max_max < Max(parent_depth, parent)) {
SetRange(parent_depth, parent, max_min, max_max);
} else {
break;
2010-09-15 12:42:33 +00:00
}
depth = parent_depth;
2012-06-05 08:50:33 +00:00
position = parent;
2010-09-15 12:42:33 +00:00
}
if (depth == 0) { // We have pushed all the way up.
target_var_->SetRange(RootMin(), RootMax());
2010-09-15 12:42:33 +00:00
}
2012-06-04 14:11:32 +00:00
MaxVarChanged();
2010-09-15 12:42:33 +00:00
}
string DebugString() const { return DebugStringInternal("Max"); }
2010-09-15 12:42:33 +00:00
virtual void Accept(ModelVisitor* const visitor) const {
AcceptInternal(ModelVisitor::kMaxEqual, visitor);
2010-09-15 12:42:33 +00:00
}
private:
Demon* max_demon_;
};
2012-06-30 15:12:14 +00:00
// Boolean And and Ors
class ArrayBoolAndEq : public CastConstraint {
public:
ArrayBoolAndEq(Solver* const s, const std::vector<IntVar*>& vars,
2012-06-30 15:12:14 +00:00
IntVar* const target)
: CastConstraint(s, target),
vars_(vars),
demons_(vars.size()),
unbounded_(0) {}
virtual ~ArrayBoolAndEq() {}
virtual void Post() {
for (int i = 0; i < vars_.size(); ++i) {
if (!vars_[i]->Bound()) {
demons_[i] = MakeConstraintDemon1(
solver(), this, &ArrayBoolAndEq::PropagateVar, "PropagateVar", i);
2012-06-30 15:12:14 +00:00
vars_[i]->WhenBound(demons_[i]);
}
}
if (!target_var_->Bound()) {
Demon* const target_demon = MakeConstraintDemon0(
solver(), this, &ArrayBoolAndEq::PropagateTarget, "PropagateTarget");
target_var_->WhenBound(target_demon);
}
2012-06-30 15:12:14 +00:00
}
virtual void InitialPropagate() {
target_var_->SetRange(0, 1);
if (target_var_->Min() == 1) {
for (int i = 0; i < vars_.size(); ++i) {
vars_[i]->SetMin(1);
}
} else {
int possible_zero = -1;
2012-06-30 15:12:14 +00:00
int ones = 0;
int unbounded = 0;
for (int i = 0; i < vars_.size(); ++i) {
if (!vars_[i]->Bound()) {
unbounded++;
possible_zero = i;
} else if (vars_[i]->Max() == 0) {
InhibitAll();
target_var_->SetMax(0);
return;
} else {
DCHECK_EQ(1, vars_[i]->Min());
ones++;
}
2012-06-30 15:12:14 +00:00
}
if (unbounded == 0) {
2012-06-30 15:12:14 +00:00
target_var_->SetMin(1);
} else if (target_var_->Max() == 0 && unbounded == 1) {
CHECK_NE(-1, possible_zero);
vars_[possible_zero]->SetMax(0);
2012-06-30 15:12:14 +00:00
} else {
unbounded_.SetValue(solver(), unbounded);
}
}
}
void PropagateVar(int index) {
if (vars_[index]->Min() == 1) {
unbounded_.Decr(solver());
2012-06-30 21:49:51 +00:00
if (unbounded_.Value() == 0 && !decided_.Switched()) {
target_var_->SetMin(1);
decided_.Switch(solver());
} else if (target_var_->Max() == 0 && unbounded_.Value() == 1 &&
!decided_.Switched()) {
ForceToZero();
2012-06-30 15:12:14 +00:00
}
} else {
InhibitAll();
target_var_->SetMax(0);
}
}
void PropagateTarget() {
if (target_var_->Min() == 1) {
for (int i = 0; i < vars_.size(); ++i) {
vars_[i]->SetMin(1);
}
} else {
if (unbounded_.Value() == 1 && !decided_.Switched()) {
ForceToZero();
2012-06-30 15:12:14 +00:00
}
}
}
string DebugString() const {
return StringPrintf("And(%s) == %s", DebugStringVector(vars_, ", ").c_str(),
2012-06-30 15:12:14 +00:00
target_var_->DebugString().c_str());
}
void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kMinEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.data(), vars_.size());
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
target_var_);
visitor->EndVisitConstraint(ModelVisitor::kMinEqual, this);
}
private:
void InhibitAll() {
for (int i = 0; i < demons_.size(); ++i) {
if (demons_[i] != NULL) {
demons_[i]->inhibit(solver());
}
}
}
void ForceToZero() {
for (int i = 0; i < vars_.size(); ++i) {
if (vars_[i]->Min() == 0) {
vars_[i]->SetValue(0);
decided_.Switch(solver());
return;
}
}
solver()->Fail();
}
2012-06-30 15:12:14 +00:00
std::vector<IntVar*> vars_;
std::vector<Demon*> demons_;
NumericalRev<int> unbounded_;
RevSwitch decided_;
};
class ArrayBoolOrEq : public CastConstraint {
public:
ArrayBoolOrEq(Solver* const s, const std::vector<IntVar*>& vars,
IntVar* const target)
: CastConstraint(s, target),
vars_(vars),
demons_(vars.size()),
unbounded_(0) {}
virtual ~ArrayBoolOrEq() {}
virtual void Post() {
for (int i = 0; i < vars_.size(); ++i) {
if (!vars_[i]->Bound()) {
demons_[i] = MakeConstraintDemon1(
solver(), this, &ArrayBoolOrEq::PropagateVar, "PropagateVar", i);
vars_[i]->WhenBound(demons_[i]);
}
}
if (!target_var_->Bound()) {
Demon* const target_demon = MakeConstraintDemon0(
solver(), this, &ArrayBoolOrEq::PropagateTarget, "PropagateTarget");
target_var_->WhenBound(target_demon);
}
}
virtual void InitialPropagate() {
target_var_->SetRange(0, 1);
2012-06-30 19:35:22 +00:00
if (target_var_->Max() == 0) {
for (int i = 0; i < vars_.size(); ++i) {
vars_[i]->SetMax(0);
}
} else {
int zeros = 0;
int possible_one = -1;
int unbounded = 0;
for (int i = 0; i < vars_.size(); ++i) {
if (!vars_[i]->Bound()) {
unbounded++;
possible_one = i;
} else if (vars_[i]->Min() == 1) {
InhibitAll();
target_var_->SetMin(1);
return;
} else {
DCHECK_EQ(0, vars_[i]->Max());
zeros++;
}
}
if (unbounded == 0) {
target_var_->SetMax(0);
} else if (target_var_->Min() == 1 && unbounded == 1) {
CHECK_NE(-1, possible_one);
vars_[possible_one]->SetMin(1);
} else {
unbounded_.SetValue(solver(), unbounded);
}
}
}
void PropagateVar(int index) {
if (vars_[index]->Min() == 0) {
unbounded_.Decr(solver());
2012-06-30 21:49:51 +00:00
if (unbounded_.Value() == 0 && !decided_.Switched()) {
target_var_->SetMax(0);
decided_.Switch(solver());
}
if (target_var_->Min() == 1 && unbounded_.Value() == 1 &&
!decided_.Switched()) {
ForceToOne();
}
} else {
InhibitAll();
target_var_->SetMin(1);
}
}
void PropagateTarget() {
2012-06-30 20:24:22 +00:00
if (target_var_->Max() == 0) {
for (int i = 0; i < vars_.size(); ++i) {
vars_[i]->SetMax(0);
}
} else {
if (unbounded_.Value() == 1 && !decided_.Switched()) {
ForceToOne();
}
}
}
string DebugString() const {
return StringPrintf("Or(%s) == %s", DebugStringVector(vars_, ", ").c_str(),
target_var_->DebugString().c_str());
}
void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kMaxEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.data(), vars_.size());
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
target_var_);
visitor->EndVisitConstraint(ModelVisitor::kMaxEqual, this);
}
private:
void InhibitAll() {
for (int i = 0; i < demons_.size(); ++i) {
if (demons_[i] != NULL) {
demons_[i]->inhibit(solver());
}
}
}
void ForceToOne() {
for (int i = 0; i < vars_.size(); ++i) {
if (vars_[i]->Max() == 1) {
vars_[i]->SetValue(1);
decided_.Switch(solver());
return;
}
}
solver()->Fail();
}
std::vector<IntVar*> vars_;
std::vector<Demon*> demons_;
NumericalRev<int> unbounded_;
RevSwitch decided_;
};
2012-06-30 15:12:14 +00:00
2010-09-15 12:42:33 +00:00
// ---------- Specialized cases ----------
class BaseSumBooleanConstraint : public Constraint {
public:
BaseSumBooleanConstraint(Solver* const s, const IntVar* const* vars, int size)
: Constraint(s), vars_(new IntVar* [size]), size_(size) {
2010-09-15 12:42:33 +00:00
CHECK_GT(size_, 0);
CHECK(vars != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
}
virtual ~BaseSumBooleanConstraint() {}
2010-09-15 12:42:33 +00:00
protected:
string DebugStringInternal(const string& name) const;
const scoped_array<IntVar*> vars_;
const int size_;
RevSwitch inactive_;
2010-09-15 12:42:33 +00:00
};
string BaseSumBooleanConstraint::DebugStringInternal(const string& name) const {
string out = name + "(";
for (int i = 0; i < size_; ++i) {
if (i > 0) {
out += ", ";
}
out += vars_[i]->DebugString();
}
out += ")";
return out;
}
// ----- Sum of Boolean <= 1 -----
class SumBooleanLessOrEqualToOne : public BaseSumBooleanConstraint {
2010-09-15 12:42:33 +00:00
public:
SumBooleanLessOrEqualToOne(Solver* const s, const IntVar* const* vars,
2010-09-15 12:42:33 +00:00
int size)
: BaseSumBooleanConstraint(s, vars, size) {}
2010-09-15 12:42:33 +00:00
virtual ~SumBooleanLessOrEqualToOne() {}
virtual void Post() {
for (int i = 0; i < size_; ++i) {
if (!vars_[i]->Bound()) {
Demon* u = MakeConstraintDemon1(
solver(), this, &SumBooleanLessOrEqualToOne::Update, "Update", i);
2010-09-15 12:42:33 +00:00
vars_[i]->WhenBound(u);
}
}
}
virtual void InitialPropagate() {
for (int i = 0; i < size_; ++i) {
if (vars_[i]->Min() == 1) {
PushAllToZeroExcept(i);
return;
}
}
}
void Update(int index) {
if (!inactive_.Switched()) {
2010-09-15 12:42:33 +00:00
DCHECK(vars_[index]->Bound());
if (vars_[index]->Min() == 1) {
PushAllToZeroExcept(index);
}
}
}
void PushAllToZeroExcept(int index) {
inactive_.Switch(solver());
2010-09-15 12:42:33 +00:00
for (int i = 0; i < size_; ++i) {
if (i != index && vars_[i]->Max() != 0) {
vars_[i]->SetMax(0);
}
}
}
virtual string DebugString() const {
return DebugStringInternal("SumBooleanLessOrEqualToOne");
}
2011-07-11 20:13:14 +00:00
virtual void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kSumLessOrEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.get(), size_);
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, 1);
2011-07-11 20:13:14 +00:00
visitor->EndVisitConstraint(ModelVisitor::kSumLessOrEqual, this);
}
2010-09-15 12:42:33 +00:00
};
// ----- Sum of Boolean >= 1 -----
// We implement this one as a Max(array) == 1.
class SumBooleanGreaterOrEqualToOne : public BaseSumBooleanConstraint {
public:
SumBooleanGreaterOrEqualToOne(Solver* const s, const IntVar* const* vars,
2010-09-15 12:42:33 +00:00
int size);
virtual ~SumBooleanGreaterOrEqualToOne() {}
virtual void Post();
virtual void InitialPropagate();
void Update(int index);
void UpdateVar();
virtual string DebugString() const;
2011-07-11 20:13:14 +00:00
virtual void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kSumGreaterOrEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.get(), size_);
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, 1);
2011-07-11 20:13:14 +00:00
visitor->EndVisitConstraint(ModelVisitor::kSumGreaterOrEqual, this);
}
2010-09-15 12:42:33 +00:00
private:
RevBitSet bits_;
};
SumBooleanGreaterOrEqualToOne::SumBooleanGreaterOrEqualToOne(
Solver* const s, const IntVar* const* vars, int size)
2010-09-15 12:42:33 +00:00
: BaseSumBooleanConstraint(s, vars, size), bits_(size) {}
void SumBooleanGreaterOrEqualToOne::Post() {
for (int i = 0; i < size_; ++i) {
Demon* d = MakeConstraintDemon1(
solver(), this, &SumBooleanGreaterOrEqualToOne::Update, "Update", i);
2010-09-15 12:42:33 +00:00
vars_[i]->WhenRange(d);
}
}
void SumBooleanGreaterOrEqualToOne::InitialPropagate() {
for (int i = 0; i < size_; ++i) {
IntVar* const var = vars_[i];
if (var->Min() == 1LL) {
inactive_.Switch(solver());
2010-09-15 12:42:33 +00:00
return;
}
if (var->Max() == 1LL) {
bits_.SetToOne(solver(), i);
}
}
if (bits_.IsCardinalityZero()) {
solver()->Fail();
} else if (bits_.IsCardinalityOne()) {
vars_[bits_.GetFirstBit(0)]->SetValue(1LL);
inactive_.Switch(solver());
2010-09-15 12:42:33 +00:00
}
}
void SumBooleanGreaterOrEqualToOne::Update(int index) {
if (!inactive_.Switched()) {
2010-09-15 12:42:33 +00:00
if (vars_[index]->Min() == 1LL) { // Bound to 1.
inactive_.Switch(solver());
2010-09-15 12:42:33 +00:00
} else {
bits_.SetToZero(solver(), index);
if (bits_.IsCardinalityZero()) {
solver()->Fail();
} else if (bits_.IsCardinalityOne()) {
vars_[bits_.GetFirstBit(0)]->SetValue(1LL);
inactive_.Switch(solver());
2010-09-15 12:42:33 +00:00
}
}
}
}
string SumBooleanGreaterOrEqualToOne::DebugString() const {
return DebugStringInternal("SumBooleanGreaterOrEqualToOne");
}
// ----- Sum of Boolean == 1 -----
class SumBooleanEqualToOne : public BaseSumBooleanConstraint {
public:
SumBooleanEqualToOne(Solver* const s, IntVar* const* vars, int size)
2010-09-15 12:42:33 +00:00
: BaseSumBooleanConstraint(s, vars, size), active_vars_(0) {}
virtual ~SumBooleanEqualToOne() {}
virtual void Post() {
for (int i = 0; i < size_; ++i) {
Demon* u = MakeConstraintDemon1(
solver(), this, &SumBooleanEqualToOne::Update, "Update", i);
2010-09-15 12:42:33 +00:00
vars_[i]->WhenBound(u);
}
}
virtual void InitialPropagate() {
int min1 = 0;
int max1 = 0;
int index_min = -1;
int index_max = -1;
for (int i = 0; i < size_; ++i) {
const IntVar* const var = vars_[i];
if (var->Min() == 1) {
min1++;
index_min = i;
}
if (var->Max() == 1) {
max1++;
index_max = i;
}
}
if (min1 > 1 || max1 == 0) {
solver()->Fail();
} else if (min1 == 1) {
DCHECK_NE(-1, index_min);
PushAllToZeroExcept(index_min);
} else if (max1 == 1) {
DCHECK_NE(-1, index_max);
vars_[index_max]->SetValue(1);
inactive_.Switch(solver());
2010-09-15 12:42:33 +00:00
} else {
active_vars_.SetValue(solver(), max1);
2010-09-15 12:42:33 +00:00
}
}
void Update(int index) {
if (!inactive_.Switched()) {
2010-09-15 12:42:33 +00:00
DCHECK(vars_[index]->Bound());
const int64 value = vars_[index]->Min(); // Faster than Value().
if (value == 0) {
active_vars_.Decr(solver());
DCHECK_GE(active_vars_.Value(), 0);
if (active_vars_.Value() == 0) {
2010-09-15 12:42:33 +00:00
solver()->Fail();
} else if (active_vars_.Value() == 1) {
2010-09-15 12:42:33 +00:00
bool found = false;
for (int i = 0; i < size_; ++i) {
IntVar* const var = vars_[i];
if (var->Max() == 1) {
var->SetValue(1);
PushAllToZeroExcept(i);
found = true;
break;
}
}
if (!found) {
solver()->Fail();
}
}
} else {
PushAllToZeroExcept(index);
}
}
}
void PushAllToZeroExcept(int index) {
inactive_.Switch(solver());
2010-09-15 12:42:33 +00:00
for (int i = 0; i < size_; ++i) {
if (i != index && vars_[i]->Max() != 0) {
vars_[i]->SetMax(0);
}
}
}
virtual string DebugString() const {
return DebugStringInternal("SumBooleanEqualToOne");
}
2011-07-11 20:13:14 +00:00
virtual void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.get(), size_);
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, 1);
2011-07-11 20:13:14 +00:00
visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
}
2010-09-15 12:42:33 +00:00
private:
NumericalRev<int> active_vars_;
2010-09-15 12:42:33 +00:00
};
// ----- Sum of Boolean Equal To Var -----
class SumBooleanEqualToVar : public BaseSumBooleanConstraint {
public:
SumBooleanEqualToVar(Solver* const s, IntVar* const* bool_vars, int size,
IntVar* const sum_var)
: BaseSumBooleanConstraint(s, bool_vars, size),
num_possible_true_vars_(0),
num_always_true_vars_(0),
sum_var_(sum_var) {}
virtual ~SumBooleanEqualToVar() {}
virtual void Post() {
for (int i = 0; i < size_; ++i) {
Demon* const u = MakeConstraintDemon1(
solver(), this, &SumBooleanEqualToVar::Update, "Update", i);
vars_[i]->WhenBound(u);
}
if (!sum_var_->Bound()) {
Demon* const u = MakeConstraintDemon0(
solver(), this, &SumBooleanEqualToVar::UpdateVar, "UpdateVar");
sum_var_->WhenRange(u);
}
}
virtual void InitialPropagate() {
int num_always_true_vars = 0;
int possible_true = 0;
for (int i = 0; i < size_; ++i) {
const IntVar* const var = vars_[i];
if (var->Min() == 1) {
num_always_true_vars++;
}
if (var->Max() == 1) {
possible_true++;
}
}
sum_var_->SetRange(num_always_true_vars, possible_true);
const int64 var_min = sum_var_->Min();
const int64 var_max = sum_var_->Max();
if (num_always_true_vars == var_max && possible_true > var_max) {
PushAllUnboundToZero();
} else if (possible_true == var_min && num_always_true_vars < var_min) {
PushAllUnboundToOne();
} else {
num_possible_true_vars_.SetValue(solver(), possible_true);
num_always_true_vars_.SetValue(solver(), num_always_true_vars);
}
}
void UpdateVar() {
2012-07-06 15:59:16 +00:00
if (!inactive_.Switched()) {
if (num_possible_true_vars_.Value() == sum_var_->Min()) {
PushAllUnboundToOne();
sum_var_->SetValue(num_possible_true_vars_.Value());
} else if (num_always_true_vars_.Value() == sum_var_->Max()) {
PushAllUnboundToZero();
sum_var_->SetValue(num_always_true_vars_.Value());
}
}
}
void Update(int index) {
if (!inactive_.Switched()) {
DCHECK(vars_[index]->Bound());
const int64 value = vars_[index]->Min(); // Faster than Value().
if (value == 0) {
num_possible_true_vars_.Decr(solver());
2012-05-24 22:19:41 +00:00
sum_var_->SetRange(num_always_true_vars_.Value(),
num_possible_true_vars_.Value());
if (num_possible_true_vars_.Value() == sum_var_->Min()) {
PushAllUnboundToOne();
}
} else {
DCHECK_EQ(1, value);
num_always_true_vars_.Incr(solver());
2012-05-24 22:19:41 +00:00
sum_var_->SetRange(num_always_true_vars_.Value(),
num_possible_true_vars_.Value());
if (num_always_true_vars_.Value() == sum_var_->Max()) {
PushAllUnboundToZero();
}
}
}
}
void PushAllUnboundToZero() {
int64 counter = 0;
inactive_.Switch(solver());
for (int i = 0; i < size_; ++i) {
if (vars_[i]->Min() == 0) {
vars_[i]->SetValue(0);
} else {
counter++;
}
}
if (counter < sum_var_->Min() || counter > sum_var_->Max()) {
solver()->Fail();
}
}
void PushAllUnboundToOne() {
int64 counter = 0;
inactive_.Switch(solver());
for (int i = 0; i < size_; ++i) {
if (vars_[i]->Max() == 1) {
vars_[i]->SetValue(1);
counter++;
}
}
if (counter < sum_var_->Min() || counter > sum_var_->Max()) {
solver()->Fail();
}
}
virtual string DebugString() const {
return StringPrintf("%s == %s", DebugStringInternal("SumBoolean").c_str(),
2012-05-24 22:19:41 +00:00
sum_var_->DebugString().c_str());
}
2011-07-11 20:13:14 +00:00
virtual void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.get(), size_);
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
2011-07-11 20:13:14 +00:00
sum_var_);
visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
}
private:
NumericalRev<int> num_possible_true_vars_;
NumericalRev<int> num_always_true_vars_;
IntVar* const sum_var_;
};
2010-09-15 12:42:33 +00:00
// ---------- ScalProd ----------
// ----- Boolean Scal Prod -----
struct Container {
IntVar* var;
int64 coef;
Container(IntVar* v, int64 c) : var(v), coef(c) {}
bool operator<(const Container& c) const { return (coef < c.coef); }
};
// This method will sort both vars and coefficients in increasing
// coefficient order. Vars with null coefficients will be
// removed. Bound vars will be collected and the sum of the
// corresponding products (when the var is bound to 1) is returned by
// this method.
// If keep_inside is true, the constant will be added back into the
// scalprod as IntConst(1) * constant.
int64 SortBothChangeConstant(IntVar** const vars, int64* const coefs,
int* const size, bool keep_inside) {
2010-09-15 12:42:33 +00:00
CHECK_NOTNULL(vars);
CHECK_NOTNULL(coefs);
CHECK_NOTNULL(size);
if (*size == 0) {
return 0;
}
2010-09-15 12:42:33 +00:00
int64 cst = 0;
std::vector<Container> to_sort;
2010-09-15 12:42:33 +00:00
for (int index = 0; index < *size; ++index) {
if (vars[index]->Bound()) {
cst += coefs[index] * vars[index]->Min();
} else if (coefs[index] != 0) {
to_sort.push_back(Container(vars[index], coefs[index]));
}
}
if (keep_inside && cst != 0) {
CHECK_LT(to_sort.size(), *size);
Solver* const solver = vars[0]->solver();
to_sort.push_back(Container(solver->MakeIntConst(1), cst));
cst = 0;
}
2010-09-15 12:42:33 +00:00
std::sort(to_sort.begin(), to_sort.end());
*size = to_sort.size();
for (int index = 0; index < *size; ++index) {
vars[index] = to_sort[index].var;
coefs[index] = to_sort[index].coef;
}
return cst;
}
// This constraint implements sum(vars) == var. It is delayed such
// that propagation only occurs when all variables have been touched.
class BooleanScalProdLessConstant : public Constraint {
public:
BooleanScalProdLessConstant(Solver* const s, const IntVar* const* vars,
int size, const int64* const coefs,
2010-09-15 12:42:33 +00:00
int64 upper_bound)
: Constraint(s),
vars_(new IntVar* [size]),
2010-09-15 12:42:33 +00:00
size_(size),
coefs_(new int64[size]),
upper_bound_(upper_bound),
first_unbound_backward_(size_ - 1),
sum_of_bound_variables_(0LL),
max_coefficient_(0) {
CHECK_GT(size, 0);
CHECK(vars != NULL);
CHECK(coefs != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
memcpy(coefs_.get(), coefs, size_ * sizeof(*coefs));
for (int i = 0; i < size_; ++i) {
DCHECK_GE(coefs_[i], 0);
}
upper_bound_ -=
SortBothChangeConstant(vars_.get(), coefs_.get(), &size_, false);
2010-09-15 12:42:33 +00:00
max_coefficient_.SetValue(s, coefs_[size_ - 1]);
}
BooleanScalProdLessConstant(Solver* const s, const IntVar* const* vars,
int size, const int* const coefs,
2010-09-15 12:42:33 +00:00
int64 upper_bound)
: Constraint(s),
vars_(new IntVar* [size]),
2010-09-15 12:42:33 +00:00
size_(size),
coefs_(new int64[size]),
upper_bound_(upper_bound),
first_unbound_backward_(size_ - 1),
sum_of_bound_variables_(0LL),
max_coefficient_(0) {
CHECK_GT(size, 0);
CHECK(vars != NULL);
CHECK(coefs != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
for (int i = 0; i < size_; ++i) {
DCHECK_GE(coefs[i], 0);
coefs_[i] = coefs[i];
}
upper_bound_ -=
SortBothChangeConstant(vars_.get(), coefs_.get(), &size_, false);
2010-09-15 12:42:33 +00:00
max_coefficient_.SetValue(s, coefs_[size_ - 1]);
}
virtual ~BooleanScalProdLessConstant() {}
virtual void Post() {
for (int var_index = 0; var_index < size_; ++var_index) {
if (vars_[var_index]->Bound()) {
continue;
}
Demon* d = MakeConstraintDemon1(solver(), this,
&BooleanScalProdLessConstant::Update,
"InitialPropagate", var_index);
2010-09-15 12:42:33 +00:00
vars_[var_index]->WhenRange(d);
}
}
void PushFromTop() {
const int64 slack = upper_bound_ - sum_of_bound_variables_.Value();
if (slack < 0) {
solver()->Fail();
}
if (slack < max_coefficient_.Value()) {
int64 last_unbound = first_unbound_backward_.Value();
for (; last_unbound >= 0; --last_unbound) {
2010-09-15 12:42:33 +00:00
if (!vars_[last_unbound]->Bound()) {
if (coefs_[last_unbound] <= slack) {
max_coefficient_.SetValue(solver(), coefs_[last_unbound]);
break;
} else {
vars_[last_unbound]->SetValue(0);
}
}
}
first_unbound_backward_.SetValue(solver(), last_unbound);
}
}
virtual void InitialPropagate() {
Solver* const s = solver();
int last_unbound = -1;
int64 sum = 0LL;
for (int index = 0; index < size_; ++index) {
if (vars_[index]->Bound()) {
const int64 value = vars_[index]->Min();
sum += value * coefs_[index];
} else {
last_unbound = index;
}
}
sum_of_bound_variables_.SetValue(s, sum);
first_unbound_backward_.SetValue(s, last_unbound);
PushFromTop();
}
void Update(int var_index) {
if (vars_[var_index]->Min() == 1) {
sum_of_bound_variables_.SetValue(
solver(), sum_of_bound_variables_.Value() + coefs_[var_index]);
PushFromTop();
}
}
virtual string DebugString() const {
return StringPrintf("BooleanScalProd([%s], [%s]) <= %" GG_LL_FORMAT "d)",
DebugStringArray(vars_.get(), size_, ", ").c_str(),
Int64ArrayToString(coefs_.get(), size_, ", ").c_str(),
upper_bound_);
2010-09-15 12:42:33 +00:00
}
2011-07-11 20:13:14 +00:00
void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kScalProdLessOrEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.get(), size_);
visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
coefs_.get(), size_);
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, upper_bound_);
2011-07-11 20:13:14 +00:00
visitor->EndVisitConstraint(ModelVisitor::kScalProdLessOrEqual, this);
}
2010-09-15 12:42:33 +00:00
private:
scoped_array<IntVar*> vars_;
int size_;
scoped_array<int64> coefs_;
int64 upper_bound_;
Rev<int> first_unbound_backward_;
Rev<int64> sum_of_bound_variables_;
Rev<int64> max_coefficient_;
};
// ----- PositiveBooleanScalProdEqVar -----
class PositiveBooleanScalProdEqVar : public CastConstraint {
2010-09-15 12:42:33 +00:00
public:
PositiveBooleanScalProdEqVar(Solver* const s, const IntVar* const* vars,
int size, const int64* const coefs,
IntVar* const var)
: CastConstraint(s, var),
2010-09-15 12:42:33 +00:00
size_(size),
vars_(new IntVar* [size_]),
2010-09-15 12:42:33 +00:00
coefs_(new int64[size_]),
first_unbound_backward_(size_ - 1),
sum_of_bound_variables_(0LL),
sum_of_all_variables_(0LL),
max_coefficient_(0) {
CHECK_GT(size, 0);
CHECK(vars != NULL);
CHECK(coefs != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
memcpy(coefs_.get(), coefs, size_ * sizeof(*coefs));
SortBothChangeConstant(vars_.get(), coefs_.get(), &size_, true);
2010-09-15 12:42:33 +00:00
max_coefficient_.SetValue(s, coefs_[size_ - 1]);
}
PositiveBooleanScalProdEqVar(Solver* const s, const IntVar* const* vars,
int size, const int* const coefs,
IntVar* const var)
: CastConstraint(s, var),
size_(size),
vars_(new IntVar* [size_]),
coefs_(new int64[size_]),
first_unbound_backward_(size_ - 1),
sum_of_bound_variables_(0LL),
sum_of_all_variables_(0LL),
max_coefficient_(0) {
CHECK_GT(size, 0);
CHECK(vars != NULL);
CHECK(coefs != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
for (int i = 0; i < size_; ++i) {
coefs_[i] = coefs[i];
}
SortBothChangeConstant(vars_.get(), coefs_.get(), &size_, true);
max_coefficient_.SetValue(s, coefs_[size_ - 1]);
}
2010-09-15 12:42:33 +00:00
virtual ~PositiveBooleanScalProdEqVar() {}
virtual void Post() {
for (int var_index = 0; var_index < size_; ++var_index) {
if (vars_[var_index]->Bound()) {
continue;
}
Demon* const d = MakeConstraintDemon1(
solver(), this, &PositiveBooleanScalProdEqVar::Update, "Update",
var_index);
2010-09-15 12:42:33 +00:00
vars_[var_index]->WhenRange(d);
}
if (!target_var_->Bound()) {
Demon* const uv = MakeConstraintDemon0(
solver(), this, &PositiveBooleanScalProdEqVar::Propagate,
"Propagate");
target_var_->WhenRange(uv);
2010-09-15 12:42:33 +00:00
}
}
void Propagate() {
target_var_->SetRange(sum_of_bound_variables_.Value(),
sum_of_all_variables_.Value());
const int64 slack_up = target_var_->Max() - sum_of_bound_variables_.Value();
const int64 slack_down = sum_of_all_variables_.Value() - target_var_->Min();
2010-09-15 12:42:33 +00:00
const int64 max_coeff = max_coefficient_.Value();
if (slack_down < max_coeff || slack_up < max_coeff) {
int64 last_unbound = first_unbound_backward_.Value();
for (; last_unbound >= 0; --last_unbound) {
if (!vars_[last_unbound]->Bound()) {
if (coefs_[last_unbound] > slack_up) {
vars_[last_unbound]->SetValue(0);
} else if (coefs_[last_unbound] > slack_down) {
vars_[last_unbound]->SetValue(1);
} else {
max_coefficient_.SetValue(solver(), coefs_[last_unbound]);
break;
}
}
}
first_unbound_backward_.SetValue(solver(), last_unbound);
}
}
virtual void InitialPropagate() {
Solver* const s = solver();
int last_unbound = -1;
int64 sum_bound = 0;
int64 sum_all = 0;
2010-09-15 12:42:33 +00:00
for (int index = 0; index < size_; ++index) {
const int64 value = vars_[index]->Max() * coefs_[index];
sum_all += value;
if (vars_[index]->Bound()) {
sum_bound += value;
} else {
last_unbound = index;
}
}
sum_of_bound_variables_.SetValue(s, sum_bound);
sum_of_all_variables_.SetValue(s, sum_all);
first_unbound_backward_.SetValue(s, last_unbound);
Propagate();
}
void Update(int var_index) {
if (vars_[var_index]->Min() == 1) {
sum_of_bound_variables_.SetValue(
solver(), sum_of_bound_variables_.Value() + coefs_[var_index]);
} else {
sum_of_all_variables_.SetValue(
solver(), sum_of_all_variables_.Value() - coefs_[var_index]);
}
Propagate();
}
virtual string DebugString() const {
return StringPrintf("PositiveBooleanScal([%s], [%s]) == %s",
DebugStringArray(vars_.get(), size_, ", ").c_str(),
Int64ArrayToString(coefs_.get(), size_, ", ").c_str(),
target_var_->DebugString().c_str());
2010-09-15 12:42:33 +00:00
}
2011-07-11 20:13:14 +00:00
void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kScalProdEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.get(), size_);
visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
coefs_.get(), size_);
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
target_var_);
2011-07-11 20:13:14 +00:00
visitor->EndVisitConstraint(ModelVisitor::kScalProdEqual, this);
}
2010-09-15 12:42:33 +00:00
private:
int size_;
scoped_array<IntVar*> vars_;
scoped_array<int64> coefs_;
Rev<int> first_unbound_backward_;
Rev<int64> sum_of_bound_variables_;
Rev<int64> sum_of_all_variables_;
Rev<int64> max_coefficient_;
};
// ----- PositiveBooleanScalProd -----
class PositiveBooleanScalProd : public BaseIntExpr {
public:
// this constructor will copy the array. The caller can safely delete the
// exprs array himself
PositiveBooleanScalProd(Solver* const s, const IntVar* const* vars, int size,
2010-09-15 12:42:33 +00:00
const int64* const coefs)
: BaseIntExpr(s),
size_(size),
vars_(new IntVar* [size_]),
coefs_(new int64[size_]) {
2010-09-15 12:42:33 +00:00
CHECK_GT(size_, 0);
CHECK(vars != NULL);
CHECK(coefs != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
memcpy(coefs_.get(), coefs, size_ * sizeof(*coefs));
SortBothChangeConstant(vars_.get(), coefs_.get(), &size_, true);
2010-09-15 12:42:33 +00:00
for (int i = 0; i < size_; ++i) {
DCHECK_GE(coefs_[i], 0);
}
}
PositiveBooleanScalProd(Solver* const s, const IntVar* const* vars, int size,
2010-09-15 12:42:33 +00:00
const int* const coefs)
: BaseIntExpr(s),
size_(size),
vars_(new IntVar* [size_]),
coefs_(new int64[size_]) {
2010-09-15 12:42:33 +00:00
CHECK_GT(size_, 0);
CHECK(vars != NULL);
CHECK(coefs != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
for (int i = 0; i < size_; ++i) {
coefs_[i] = coefs[i];
DCHECK_GE(coefs_[i], 0);
}
SortBothChangeConstant(vars_.get(), coefs_.get(), &size_, true);
2010-09-15 12:42:33 +00:00
}
virtual ~PositiveBooleanScalProd() {}
virtual int64 Min() const {
int64 min = 0;
for (int i = 0; i < size_; ++i) {
if (vars_[i]->Min()) {
min += coefs_[i];
}
}
return min;
2010-09-15 12:42:33 +00:00
}
virtual void SetMin(int64 m) { SetRange(m, kint64max); }
2010-09-15 12:42:33 +00:00
virtual int64 Max() const {
int64 max = 0;
for (int i = 0; i < size_; ++i) {
if (vars_[i]->Max()) {
max += coefs_[i];
}
}
return max;
2010-09-15 12:42:33 +00:00
}
virtual void SetMax(int64 m) { SetRange(kint64min, m); }
2010-09-15 12:42:33 +00:00
virtual void SetRange(int64 l, int64 u) {
int64 current_min = 0;
int64 current_max = 0;
2010-09-15 12:42:33 +00:00
int64 diameter = -1;
for (int i = 0; i < size_; ++i) {
const int64 coefficient = coefs_[i];
const int64 var_min = vars_[i]->Min() * coefficient;
const int64 var_max = vars_[i]->Max() * coefficient;
current_min += var_min;
current_max += var_max;
if (var_min != var_max) { // Coefficients are increasing.
diameter = var_max - var_min;
}
}
if (u >= current_max && l <= current_min) {
return;
}
if (u < current_min || l > current_max) {
solver()->Fail();
}
u = std::min(current_max, u);
l = std::max(l, current_min);
if (u - l > diameter) {
return;
}
for (int i = 0; i < size_; ++i) {
const int64 coefficient = coefs_[i];
IntVar* const var = vars_[i];
const int64 new_min = l - current_max + var->Max() * coefficient;
const int64 new_max = u - current_min + var->Min() * coefficient;
if (new_max < 0 || new_min > coefficient || new_min > new_max) {
solver()->Fail();
}
if (new_min > 0LL) {
var->SetMin(1LL);
} else if (new_max < coefficient) {
var->SetMax(0LL);
}
}
}
virtual string DebugString() const {
return StringPrintf("PositiveBooleanScalProd([%s], [%s])",
DebugStringArray(vars_.get(), size_, ", ").c_str(),
Int64ArrayToString(coefs_.get(), size_, ", ").c_str());
2010-09-15 12:42:33 +00:00
}
virtual void WhenRange(Demon* d) {
for (int i = 0; i < size_; ++i) {
vars_[i]->WhenRange(d);
}
}
virtual IntVar* CastToVar() {
Solver* const s = solver();
int64 vmin = 0LL;
int64 vmax = 0LL;
Range(&vmin, &vmax);
IntVar* const var = solver()->MakeIntVar(vmin, vmax);
if (size_ > 0) {
CastConstraint* const ct = s->RevAlloc(new PositiveBooleanScalProdEqVar(
s, vars_.get(), size_, coefs_.get(), var));
s->AddCastConstraint(ct, var, this);
2010-09-15 12:42:33 +00:00
}
return var;
}
2011-07-11 20:13:14 +00:00
void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitIntegerExpression(ModelVisitor::kScalProd, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.get(), size_);
visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
coefs_.get(), size_);
2011-07-11 20:13:14 +00:00
visitor->EndVisitIntegerExpression(ModelVisitor::kScalProd, this);
}
2010-09-15 12:42:33 +00:00
private:
int size_;
scoped_array<IntVar*> vars_;
scoped_array<int64> coefs_;
};
// ----- PositiveBooleanScalProdEqCst ----- (all constants >= 0)
class PositiveBooleanScalProdEqCst : public Constraint {
public:
PositiveBooleanScalProdEqCst(Solver* const s, const IntVar* const* vars,
int size, const int64* const coefs,
2010-09-15 12:42:33 +00:00
int64 constant)
: Constraint(s),
size_(size),
vars_(new IntVar* [size_]),
2010-09-15 12:42:33 +00:00
coefs_(new int64[size_]),
first_unbound_backward_(size_ - 1),
sum_of_bound_variables_(0LL),
sum_of_all_variables_(0LL),
constant_(constant),
max_coefficient_(0) {
CHECK_GT(size, 0);
CHECK(vars != NULL);
CHECK(coefs != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
memcpy(coefs_.get(), coefs, size_ * sizeof(*coefs));
constant_ -=
SortBothChangeConstant(vars_.get(), coefs_.get(), &size_, false);
2010-09-15 12:42:33 +00:00
max_coefficient_.SetValue(s, coefs_[size_ - 1]);
}
PositiveBooleanScalProdEqCst(Solver* const s, const IntVar* const* vars,
int size, const int* const coefs, int64 constant)
2010-09-15 12:42:33 +00:00
: Constraint(s),
size_(size),
vars_(new IntVar* [size_]),
2010-09-15 12:42:33 +00:00
coefs_(new int64[size_]),
first_unbound_backward_(size_ - 1),
sum_of_bound_variables_(0LL),
sum_of_all_variables_(0LL),
constant_(constant),
max_coefficient_(0) {
CHECK_GT(size, 0);
CHECK(vars != NULL);
CHECK(coefs != NULL);
memcpy(vars_.get(), vars, size_ * sizeof(*vars));
for (int i = 0; i < size; ++i) {
coefs_[i] = coefs[i];
}
constant_ -=
SortBothChangeConstant(vars_.get(), coefs_.get(), &size_, false);
2010-09-15 12:42:33 +00:00
max_coefficient_.SetValue(s, coefs_[size_ - 1]);
}
virtual ~PositiveBooleanScalProdEqCst() {}
virtual void Post() {
for (int var_index = 0; var_index < size_; ++var_index) {
if (!vars_[var_index]->Bound()) {
Demon* const d = MakeConstraintDemon1(
solver(), this, &PositiveBooleanScalProdEqCst::Update, "Update",
var_index);
2010-09-15 12:42:33 +00:00
vars_[var_index]->WhenRange(d);
}
}
}
void Propagate() {
if (sum_of_bound_variables_.Value() > constant_ ||
sum_of_all_variables_.Value() < constant_) {
solver()->Fail();
}
const int64 slack_up = constant_ - sum_of_bound_variables_.Value();
const int64 slack_down = sum_of_all_variables_.Value() - constant_;
const int64 max_coeff = max_coefficient_.Value();
if (slack_down < max_coeff || slack_up < max_coeff) {
int64 last_unbound = first_unbound_backward_.Value();
for (; last_unbound >= 0; --last_unbound) {
if (!vars_[last_unbound]->Bound()) {
if (coefs_[last_unbound] > slack_up) {
vars_[last_unbound]->SetValue(0);
} else if (coefs_[last_unbound] > slack_down) {
vars_[last_unbound]->SetValue(1);
} else {
max_coefficient_.SetValue(solver(), coefs_[last_unbound]);
break;
}
}
}
first_unbound_backward_.SetValue(solver(), last_unbound);
}
}
virtual void InitialPropagate() {
Solver* const s = solver();
int last_unbound = -1;
int64 sum_bound = 0LL;
int64 sum_all = 0LL;
for (int index = 0; index < size_; ++index) {
const int64 value = vars_[index]->Max() * coefs_[index];
sum_all += value;
if (vars_[index]->Bound()) {
sum_bound += value;
} else {
last_unbound = index;
}
}
sum_of_bound_variables_.SetValue(s, sum_bound);
sum_of_all_variables_.SetValue(s, sum_all);
first_unbound_backward_.SetValue(s, last_unbound);
Propagate();
}
void Update(int var_index) {
if (vars_[var_index]->Min() == 1) {
sum_of_bound_variables_.SetValue(
solver(), sum_of_bound_variables_.Value() + coefs_[var_index]);
} else {
sum_of_all_variables_.SetValue(
solver(), sum_of_all_variables_.Value() - coefs_[var_index]);
}
Propagate();
}
virtual string DebugString() const {
return StringPrintf(
"PositiveBooleanScalProd([%s], [%s]) == %" GG_LL_FORMAT "d",
DebugStringArray(vars_.get(), size_, ", ").c_str(),
Int64ArrayToString(coefs_.get(), size_, ", ").c_str(), constant_);
2010-09-15 12:42:33 +00:00
}
2011-07-11 20:13:14 +00:00
void Accept(ModelVisitor* const visitor) const {
visitor->BeginVisitConstraint(ModelVisitor::kScalProdEqual, this);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
vars_.get(), size_);
visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
coefs_.get(), size_);
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, constant_);
2011-07-11 20:13:14 +00:00
visitor->EndVisitConstraint(ModelVisitor::kScalProdEqual, this);
}
2010-09-15 12:42:33 +00:00
private:
int size_;
scoped_array<IntVar*> vars_;
scoped_array<int64> coefs_;
Rev<int> first_unbound_backward_;
Rev<int64> sum_of_bound_variables_;
Rev<int64> sum_of_all_variables_;
int64 constant_;
Rev<int64> max_coefficient_;
};
// ----- Linearizer -----
#define IS_TYPE(type, tag) type.compare(ModelVisitor::tag) == 0
class ExprLinearizer : public ModelParser {
public:
ExprLinearizer(hash_map<IntVar*, int64>* const map)
: map_(map), constant_(0) {}
virtual ~ExprLinearizer() {}
// Begin/End visit element.
virtual void BeginVisitModel(const string& solver_name) {
LOG(FATAL) << "Should not be here";
2012-09-04 21:40:12 +00:00
}
virtual void EndVisitModel(const string& solver_name) {
LOG(FATAL) << "Should not be here";
2012-09-04 21:40:12 +00:00
}
virtual void BeginVisitConstraint(const string& type_name,
const Constraint* const constraint) {
LOG(FATAL) << "Should not be here";
2012-09-04 21:40:12 +00:00
}
virtual void EndVisitConstraint(const string& type_name,
const Constraint* const constraint) {
LOG(FATAL) << "Should not be here";
2012-09-04 21:40:12 +00:00
}
virtual void BeginVisitExtension(const string& type) {
LOG(FATAL) << "Should not be here";
}
virtual void EndVisitExtension(const string& type) {
LOG(FATAL) << "Should not be here";
}
virtual void BeginVisitIntegerExpression(const string& type_name,
const IntExpr* const expr) {
BeginVisit(true);
}
virtual void EndVisitIntegerExpression(const string& type_name,
const IntExpr* const expr) {
if (IS_TYPE(type_name, kSum)) {
VisitSum(expr);
} else if (IS_TYPE(type_name, kScalProd)) {
VisitScalProd(expr);
} else if (IS_TYPE(type_name, kDifference)) {
VisitDifference(expr);
} else if (IS_TYPE(type_name, kOpposite)) {
VisitOpposite(expr);
} else if (IS_TYPE(type_name, kProduct)) {
VisitProduct(expr);
} else if (IS_TYPE(type_name, kTrace)) {
VisitTrace(expr);
} else {
VisitIntegerExpression(expr);
2012-09-04 21:40:12 +00:00
}
EndVisit();
2012-09-04 21:40:12 +00:00
}
virtual void VisitIntegerVariable(const IntVar* const variable,
const string& operation, int64 value,
const IntVar* const delegate) {
if (operation == ModelVisitor::kSumOperation) {
AddConstant(value);
VisitSubExpression(delegate);
} else if (operation == ModelVisitor::kDifferenceOperation) {
AddConstant(value);
PushMultiplier(-1);
VisitSubExpression(delegate);
PopMultiplier();
} else if (operation == ModelVisitor::kProductOperation) {
PushMultiplier(value);
VisitSubExpression(delegate);
PopMultiplier();
} else if (operation == ModelVisitor::kTraceOperation) {
VisitSubExpression(delegate);
}
}
virtual void VisitIntegerVariable(const IntVar* const variable,
const IntExpr* const delegate) {
if (delegate != NULL) {
VisitSubExpression(delegate);
} else {
if (variable->Bound()) {
AddConstant(variable->Min());
2012-06-30 15:12:14 +00:00
} else {
RegisterExpression(variable, 1);
}
}
}
// Visit integer arguments.
virtual void VisitIntegerArgument(const string& arg_name, int64 value) {
Top()->SetIntegerArgument(arg_name, value);
}
virtual void VisitIntegerArrayArgument(const string& arg_name,
const int64* const values, int size) {
Top()->SetIntegerArrayArgument(arg_name, values, size);
}
virtual void VisitIntegerMatrixArgument(const string& arg_name,
const IntTupleSet& values) {
Top()->SetIntegerMatrixArgument(arg_name, values);
}
// Visit integer expression argument.
virtual void VisitIntegerExpressionArgument(const string& arg_name,
const IntExpr* const argument) {
Top()->SetIntegerExpressionArgument(arg_name, argument);
}
virtual void VisitIntegerVariableArrayArgument(const string& arg_name,
const IntVar* const* arguments,
int size) {
Top()->SetIntegerVariableArrayArgument(arg_name, arguments, size);
}
// Visit interval argument.
virtual void VisitIntervalArgument(const string& arg_name,
const IntervalVar* const argument) {}
virtual void VisitIntervalArrayArgument(const string& arg_name,
const IntervalVar* const* argument,
int size) {}
void Visit(const IntExpr* const expr, int64 multiplier) {
2013-07-22 18:31:22 +00:00
if (expr->Min() == expr->Max()) {
constant_ += expr->Min() * multiplier;
} else {
PushMultiplier(multiplier);
expr->Accept(this);
PopMultiplier();
}
}
int64 Constant() const { return constant_; }
virtual string DebugString() const { return "ExprLinearizer"; }
private:
void BeginVisit(bool active) { PushArgumentHolder(); }
void EndVisit() { PopArgumentHolder(); }
void VisitSubExpression(const IntExpr* const cp_expr) {
cp_expr->Accept(this);
}
void VisitSum(const IntExpr* const cp_expr) {
if (Top()->HasIntegerVariableArrayArgument(ModelVisitor::kVarsArgument)) {
const std::vector<const IntVar*>& cp_vars =
Top()->FindIntegerVariableArrayArgumentOrDie(
ModelVisitor::kVarsArgument);
for (int i = 0; i < cp_vars.size(); ++i) {
VisitSubExpression(cp_vars[i]);
}
} else if (Top()->HasIntegerExpressionArgument(
ModelVisitor::kLeftArgument)) {
const IntExpr* const left = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kLeftArgument);
const IntExpr* const right = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kRightArgument);
VisitSubExpression(left);
VisitSubExpression(right);
} else {
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kExpressionArgument);
const int64 value =
Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
VisitSubExpression(expr);
AddConstant(value);
}
}
void VisitScalProd(const IntExpr* const cp_expr) {
const std::vector<const IntVar*>& cp_vars =
Top()->FindIntegerVariableArrayArgumentOrDie(
ModelVisitor::kVarsArgument);
const std::vector<int64>& cp_coefficients = Top()->FindIntegerArrayArgumentOrDie(
ModelVisitor::kCoefficientsArgument);
CHECK_EQ(cp_vars.size(), cp_coefficients.size());
for (int i = 0; i < cp_vars.size(); ++i) {
const int64 coefficient = cp_coefficients[i];
PushMultiplier(coefficient);
VisitSubExpression(cp_vars[i]);
PopMultiplier();
}
}
void VisitDifference(const IntExpr* const cp_expr) {
if (Top()->HasIntegerExpressionArgument(ModelVisitor::kLeftArgument)) {
const IntExpr* const left = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kLeftArgument);
const IntExpr* const right = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kRightArgument);
VisitSubExpression(left);
PushMultiplier(-1);
VisitSubExpression(right);
PopMultiplier();
} else {
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kExpressionArgument);
const int64 value =
Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
AddConstant(value);
PushMultiplier(-1);
VisitSubExpression(expr);
PopMultiplier();
}
}
void VisitOpposite(const IntExpr* const cp_expr) {
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kExpressionArgument);
PushMultiplier(-1);
VisitSubExpression(expr);
PopMultiplier();
}
void VisitProduct(const IntExpr* const cp_expr) {
if (Top()->HasIntegerExpressionArgument(
ModelVisitor::kExpressionArgument)) {
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kExpressionArgument);
const int64 value =
Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
PushMultiplier(value);
VisitSubExpression(expr);
PopMultiplier();
} else {
RegisterExpression(cp_expr, 1);
}
}
void VisitTrace(const IntExpr* const cp_expr) {
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
ModelVisitor::kExpressionArgument);
VisitSubExpression(expr);
}
void VisitIntegerExpression(const IntExpr* const cp_expr) {
RegisterExpression(cp_expr, 1);
}
void RegisterExpression(const IntExpr* const expr, int64 coef) {
(*map_)[const_cast<IntExpr*>(expr)->Var()] += coef * multipliers_.back();
}
void AddConstant(int64 constant) {
constant_ += constant * multipliers_.back();
}
void PushMultiplier(int64 multiplier) {
2012-06-29 20:03:47 +00:00
if (multipliers_.empty()) {
multipliers_.push_back(multiplier);
} else {
multipliers_.push_back(multiplier * multipliers_.back());
}
}
void PopMultiplier() { multipliers_.pop_back(); }
hash_map<IntVar*, int64>* const map_;
std::vector<int64> multipliers_;
int64 constant_;
};
#undef IS_TYPE
// ----- Factory functions -----
template <class T>
Constraint* MakeScalProdEqualityFct(Solver* const solver,
const std::vector<IntVar*>& vars,
const std::vector<T>& coefficients, int64 cst) {
const int size = vars.size();
if (size == 0 || AreAllNull<T>(coefficients)) {
return cst == 0 ? solver->MakeTrueConstraint()
: solver->MakeFalseConstraint();
}
if (AreAllBoundOrNull(vars, coefficients)) {
int64 sum = 0;
for (int i = 0; i < size; ++i) {
sum += coefficients[i] * vars[i]->Min();
}
return sum == cst ? solver->MakeTrueConstraint()
: solver->MakeFalseConstraint();
}
if (AreAllOnes(coefficients)) {
return solver->MakeSumEquality(vars, cst);
}
if (AreAllBooleans(vars) && AreAllPositive<T>(coefficients) && size > 2) {
// TODO(user) : bench BooleanScalProdEqVar with IntConst.
return solver->RevAlloc(new PositiveBooleanScalProdEqCst(
solver, vars.data(), size, coefficients.data(), cst));
}
// Simplications.
int constants = 0;
int positives = 0;
int negatives = 0;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
constants++;
} else if (coefficients[i] > 0) {
positives++;
} else {
negatives++;
}
}
if (positives > 0 && negatives > 0) {
std::vector<IntVar*> pos_terms;
std::vector<IntVar*> neg_terms;
int64 rhs = cst;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
pos_terms.push_back(solver->MakeProd(vars[i], coefficients[i])->Var());
} else {
neg_terms.push_back(solver->MakeProd(vars[i], -coefficients[i])->Var());
}
}
if (negatives == 1) {
if (rhs != 0) {
pos_terms.push_back(solver->MakeIntConst(-rhs));
}
return solver->MakeSumEquality(pos_terms, neg_terms[0]);
} else if (positives == 1) {
if (rhs != 0) {
neg_terms.push_back(solver->MakeIntConst(rhs));
}
return solver->MakeSumEquality(neg_terms, pos_terms[0]);
} else {
if (rhs != 0) {
neg_terms.push_back(solver->MakeIntConst(rhs));
}
return solver->MakeEquality(solver->MakeSum(pos_terms),
solver->MakeSum(neg_terms));
}
} else if (positives == 1) {
2013-07-19 03:47:59 +00:00
IntExpr* pos_term = NULL;
int64 rhs = cst;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
2013-07-19 03:47:59 +00:00
pos_term = solver->MakeProd(vars[i], coefficients[i]);
} else {
LOG(FATAL) << "Should not be here";
}
}
return solver->MakeEquality(pos_term, rhs);
} else if (negatives == 1) {
2013-07-19 03:47:59 +00:00
IntExpr* neg_term = NULL;
int64 rhs = cst;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
LOG(FATAL) << "Should not be here";
} else {
2013-07-19 03:47:59 +00:00
neg_term = solver->MakeProd(vars[i], -coefficients[i]);
}
}
return solver->MakeEquality(neg_term, -rhs);
} else if (positives > 1) {
std::vector<IntVar*> pos_terms;
int64 rhs = cst;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
pos_terms.push_back(solver->MakeProd(vars[i], coefficients[i])->Var());
} else {
LOG(FATAL) << "Should not be here";
}
}
return solver->MakeSumEquality(pos_terms, rhs);
} else if (negatives > 1) {
std::vector<IntVar*> neg_terms;
int64 rhs = cst;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
LOG(FATAL) << "Should not be here";
} else {
neg_terms.push_back(solver->MakeProd(vars[i], -coefficients[i])->Var());
}
}
return solver->MakeSumEquality(neg_terms, -rhs);
}
std::vector<IntVar*> terms;
for (int i = 0; i < size; ++i) {
terms.push_back(solver->MakeProd(vars[i], coefficients[i])->Var());
}
return solver->MakeSumEquality(terms, solver->MakeIntConst(cst));
}
template <class T>
Constraint* MakeScalProdEqualityVarFct(Solver* const solver,
const std::vector<IntVar*>& vars,
const std::vector<T>& coefficients,
IntVar* const target) {
const int size = vars.size();
if (size == 0 || AreAllNull<T>(coefficients)) {
return solver->MakeEquality(target, Zero());
}
if (AreAllOnes(coefficients)) {
return solver->MakeSumEquality(vars, target);
}
if (AreAllBooleans(vars) && AreAllPositive<T>(coefficients)) {
// TODO(user) : bench BooleanScalProdEqVar with IntConst.
return solver->RevAlloc(new PositiveBooleanScalProdEqVar(
solver, vars.data(), size, coefficients.data(), target));
}
std::vector<IntVar*> terms;
for (int i = 0; i < size; ++i) {
terms.push_back(solver->MakeProd(vars[i], coefficients[i])->Var());
}
return solver->MakeSumEquality(terms, target);
}
template <class T>
Constraint* MakeScalProdGreaterOrEqualFct(Solver* solver,
const std::vector<IntVar*>& vars,
const std::vector<T>& coefficients,
int64 cst) {
const int size = vars.size();
if (size == 0 || AreAllNull<T>(coefficients)) {
return cst <= 0 ? solver->MakeTrueConstraint()
: solver->MakeFalseConstraint();
}
if (AreAllOnes(coefficients)) {
return solver->MakeSumGreaterOrEqual(vars, cst);
}
if (cst == 1 && AreAllBooleans(vars) && AreAllPositive(coefficients)) {
// can move all coefficients to 1.
std::vector<IntVar*> terms;
for (int i = 0; i < size; ++i) {
if (coefficients[i] > 0) {
terms.push_back(vars[i]);
}
}
return solver->MakeSumGreaterOrEqual(terms, 1);
}
std::vector<IntVar*> terms;
for (int i = 0; i < size; ++i) {
terms.push_back(solver->MakeProd(vars[i], coefficients[i])->Var());
}
return solver->MakeSumGreaterOrEqual(terms, cst);
}
2010-09-15 12:42:33 +00:00
template <class T>
Constraint* MakeScalProdLessOrEqualFct(Solver* solver,
const std::vector<IntVar*>& vars,
const std::vector<T>& coefficients,
int64 upper_bound) {
2012-09-04 21:40:12 +00:00
const int size = vars.size();
if (size == 0 || AreAllNull<T>(coefficients)) {
2010-09-15 12:42:33 +00:00
return upper_bound >= 0 ? solver->MakeTrueConstraint()
: solver->MakeFalseConstraint();
2010-09-15 12:42:33 +00:00
}
// TODO(user) : compute constant on the fly.
2012-09-04 21:40:12 +00:00
if (AreAllBoundOrNull(vars, coefficients)) {
2010-09-15 12:42:33 +00:00
int64 cst = 0;
for (int i = 0; i < size; ++i) {
cst += vars[i]->Min() * coefficients[i];
}
return cst <= upper_bound ? solver->MakeTrueConstraint()
: solver->MakeFalseConstraint();
2010-09-15 12:42:33 +00:00
}
2012-09-04 21:40:12 +00:00
if (AreAllOnes(coefficients)) {
return solver->MakeSumLessOrEqual(vars, upper_bound);
}
if (AreAllBooleans(vars) && AreAllPositive<T>(coefficients)) {
return solver->RevAlloc(new BooleanScalProdLessConstant(
solver, vars.data(), size, coefficients.data(), upper_bound));
2010-09-15 12:42:33 +00:00
}
2012-06-23 18:58:32 +00:00
// Some simplications
int constants = 0;
int positives = 0;
int negatives = 0;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
constants++;
} else if (coefficients[i] > 0) {
positives++;
} else {
negatives++;
}
}
if (positives > 0 && negatives > 0) {
std::vector<IntVar*> pos_terms;
std::vector<IntVar*> neg_terms;
int64 rhs = upper_bound;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
pos_terms.push_back(solver->MakeProd(vars[i], coefficients[i])->Var());
} else {
neg_terms.push_back(solver->MakeProd(vars[i], -coefficients[i])->Var());
}
}
if (negatives == 1) {
2013-07-19 03:47:59 +00:00
IntExpr* const neg_term = solver->MakeSum(neg_terms[0], rhs);
return solver->MakeLessOrEqual(solver->MakeSum(pos_terms), neg_term);
2012-06-23 18:58:32 +00:00
} else if (positives == 1) {
2013-07-19 03:47:59 +00:00
IntExpr* const pos_term = solver->MakeSum(pos_terms[0], -rhs);
return solver->MakeGreaterOrEqual(solver->MakeSum(neg_terms), pos_term);
2012-06-23 18:58:32 +00:00
} else {
if (rhs != 0) {
neg_terms.push_back(solver->MakeIntConst(rhs));
}
return solver->MakeLessOrEqual(solver->MakeSum(pos_terms),
solver->MakeSum(neg_terms));
2012-06-23 18:58:32 +00:00
}
} else if (positives == 1) {
2013-07-19 03:47:59 +00:00
IntExpr* pos_term = NULL;
2012-06-23 18:58:32 +00:00
int64 rhs = upper_bound;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
2013-07-19 03:47:59 +00:00
pos_term = solver->MakeProd(vars[i], coefficients[i]);
2012-06-23 18:58:32 +00:00
} else {
LOG(FATAL) << "Should not be here";
}
}
return solver->MakeLessOrEqual(pos_term, rhs);
} else if (negatives == 1) {
2013-07-19 03:47:59 +00:00
IntExpr* neg_term = NULL;
2012-06-23 18:58:32 +00:00
int64 rhs = upper_bound;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
LOG(FATAL) << "Should not be here";
} else {
2013-07-19 03:47:59 +00:00
neg_term = solver->MakeProd(vars[i], -coefficients[i]);
2012-06-23 18:58:32 +00:00
}
}
return solver->MakeGreaterOrEqual(neg_term, -rhs);
} else if (positives > 1) {
std::vector<IntVar*> pos_terms;
int64 rhs = upper_bound;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
pos_terms.push_back(solver->MakeProd(vars[i], coefficients[i])->Var());
} else {
LOG(FATAL) << "Should not be here";
}
}
return solver->MakeSumLessOrEqual(pos_terms, rhs);
} else if (negatives > 1) {
std::vector<IntVar*> neg_terms;
int64 rhs = upper_bound;
for (int i = 0; i < size; ++i) {
if (coefficients[i] == 0 || vars[i]->Bound()) {
rhs -= coefficients[i] * vars[i]->Min();
} else if (coefficients[i] > 0) {
LOG(FATAL) << "Should not be here";
} else {
neg_terms.push_back(solver->MakeProd(vars[i], -coefficients[i])->Var());
}
}
return solver->MakeSumGreaterOrEqual(neg_terms, -rhs);
}
std::vector<IntVar*> terms;
2010-09-15 12:42:33 +00:00
for (int i = 0; i < size; ++i) {
terms.push_back(solver->MakeProd(vars[i], coefficients[i])->Var());
}
return solver->MakeLessOrEqual(solver->MakeSum(terms), upper_bound);
}
2013-08-01 22:17:49 +00:00
IntExpr* MakeSumArrayAux(Solver* const solver,
const std::vector<IntVar*>& vars, int64 constant) {
const int size = vars.size();
DCHECK_GT(size, 2);
int64 new_min = 0;
int64 new_max = 0;
for (int i = 0; i < size; ++i) {
if (new_min != kint64min) {
new_min = CapAdd(vars[i]->Min(), new_min);
}
if (new_max != kint64max) {
new_max = CapAdd(vars[i]->Max(), new_max);
}
}
IntExpr* const cache = solver->Cache()->FindVarArrayExpression(
vars, ModelCache::VAR_ARRAY_SUM);
if (cache != NULL) {
return cache;
} else {
const string name =
StringPrintf("Sum([%s])", NameVector(vars, ", ").c_str());
IntVar* const sum_var = solver->MakeIntVar(new_min, new_max, name);
if (AreAllBooleans(vars)) {
solver->AddConstraint(
solver->RevAlloc(new SumBooleanEqualToVar(
solver, vars.data(), vars.size(), sum_var)));
} else {
solver->AddConstraint(
solver->RevAlloc(new SumConstraint(solver, vars, sum_var)));
}
solver->Cache()->InsertVarArrayExpression(sum_var, vars,
ModelCache::VAR_ARRAY_SUM);
return solver->MakeSum(sum_var, constant);
}
}
IntExpr* MakeSumAux(Solver* const solver, const std::vector<IntVar*> vars,
int64 constant) {
2012-06-14 19:32:41 +00:00
const int size = vars.size();
if (size == 0) {
2013-06-11 14:49:19 +00:00
return solver->MakeIntConst(constant);
} else if (size == 1) {
return solver->MakeSum(vars[0], constant);
} else if (size == 2) {
return solver->MakeSum(solver->MakeSum(vars[0], vars[1]), constant);
} else {
2013-08-01 22:17:49 +00:00
return MakeSumArrayAux(solver, vars, constant);
2012-09-05 15:52:00 +00:00
}
}
2013-06-11 14:49:19 +00:00
IntExpr* MakeScalProdAux(Solver* solver, const std::vector<IntVar*>& vars,
const std::vector<int64>& coefs, int64 constant) {
if (AreAllOnes(coefs)) {
return MakeSumAux(solver, vars, constant);
2013-06-11 14:49:19 +00:00
}
const int size = vars.size();
if (size == 0) {
return solver->MakeIntConst(constant);
} else if (size == 1) {
return solver->MakeSum(solver->MakeProd(vars[0], coefs[0]), constant);
} else if (size == 2) {
return solver->MakeSum(solver->MakeSum(solver->MakeProd(vars[0], coefs[0]),
solver->MakeProd(vars[1], coefs[1])),
constant);
} else {
if (AreAllBooleans(vars)) {
if (AreAllPositive<int64>(coefs)) {
return solver->MakeSum(
solver->RegisterIntExpr(
solver->RevAlloc(new PositiveBooleanScalProd(
solver, vars.data(), size, coefs.data()))),
constant);
2012-09-05 15:52:00 +00:00
} else {
// If some coefficients are non-positive, partition coefficients in two
// sets, one for the positive coefficients P and one for the negative
// ones N.
// Create two PositiveBooleanScalProd expressions, one on P (s1), the
// other on Opposite(N) (s2).
// The final expression is then s1 - s2.
// If P is empty, the expression is Opposite(s2).
std::vector<int64> positive_coefs;
std::vector<int64> negative_coefs;
std::vector<IntVar*> positive_coef_vars;
std::vector<IntVar*> negative_coef_vars;
for (int i = 0; i < size; ++i) {
const int coef = coefs[i];
if (coef > 0) {
positive_coefs.push_back(coef);
positive_coef_vars.push_back(vars[i]);
} else if (coef < 0) {
negative_coefs.push_back(-coef);
negative_coef_vars.push_back(vars[i]);
}
}
CHECK_GT(negative_coef_vars.size(), 0);
IntExpr* const negatives =
MakeScalProdAux(solver, negative_coef_vars, negative_coefs, 0);
if (!positive_coef_vars.empty()) {
IntExpr* const positives = MakeScalProdAux(solver, positive_coef_vars,
positive_coefs, constant);
2013-07-19 03:47:59 +00:00
return solver->MakeDifference(positives, negatives);
} else {
2013-06-18 13:21:15 +00:00
return solver->MakeDifference(constant, negatives);
}
2012-09-05 15:52:00 +00:00
}
}
}
std::vector<IntVar*> terms;
for (int i = 0; i < size; ++i) {
terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
}
int64 new_min = 0;
int64 new_max = 0;
for (int i = 0; i < size; ++i) {
if (new_min != kint64min) {
new_min = CapAdd(terms[i]->Min(), new_min);
}
if (new_max != kint64max) {
new_max = CapAdd(terms[i]->Max(), new_max);
}
}
const string name =
StringPrintf("ScalProd([%s], [%s])", NameVector(vars, ", ").c_str(),
Int64VectorToString(coefs, ", ").c_str());
IntVar* const scal_prod_var = solver->MakeIntVar(new_min, new_max, name);
solver->AddConstraint(
solver->RevAlloc(new SumConstraint(solver, terms, scal_prod_var)));
return solver->MakeSum(scal_prod_var, constant);
}
template <class T>
IntExpr* MakeScalProdFct(Solver* solver, const std::vector<IntVar*>& pre_vars,
const std::vector<T>& pre_coefs) {
hash_map<IntVar*, int64> map;
ExprLinearizer lin(&map);
for (int i = 0; i < pre_vars.size(); ++i) {
lin.Visit(pre_vars[i], pre_coefs[i]);
}
const int64 constant = lin.Constant();
std::vector<IntVar*> vars;
std::vector<int64> coefs;
for (ConstIter<hash_map<IntVar*, int64> > iter(map); !iter.at_end(); ++iter) {
if (iter->second != 0) {
vars.push_back(iter->first);
coefs.push_back(iter->second);
}
}
return MakeScalProdAux(solver, vars, coefs, constant);
}
IntExpr* MakeSumFct(Solver* solver, const std::vector<IntVar*>& pre_vars) {
hash_map<IntVar*, int64> map;
ExprLinearizer lin(&map);
for (int i = 0; i < pre_vars.size(); ++i) {
lin.Visit(pre_vars[i], 1);
}
const int64 constant = lin.Constant();
std::vector<IntVar*> vars;
std::vector<int64> coefs;
for (ConstIter<hash_map<IntVar*, int64> > iter(map); !iter.at_end(); ++iter) {
if (iter->second != 0) {
vars.push_back(iter->first);
coefs.push_back(iter->second);
}
}
return MakeScalProdAux(solver, vars, coefs, constant);
2012-09-05 15:52:00 +00:00
}
} // namespace
2010-09-15 12:42:33 +00:00
2012-09-04 21:40:12 +00:00
// ----- API -----
IntExpr* Solver::MakeSum(const std::vector<IntVar*>& vars) {
const int size = vars.size();
if (size == 0) {
return MakeIntConst(0LL);
} else if (size == 1) {
return vars[0];
} else if (size == 2) {
return MakeSum(vars[0], vars[1]);
} else {
IntExpr* const cache =
model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_SUM);
if (cache != NULL) {
2013-07-19 03:47:59 +00:00
return cache;
2012-09-04 21:40:12 +00:00
} else {
int64 new_min = 0;
int64 new_max = 0;
for (int i = 0; i < size; ++i) {
if (new_min != kint64min) {
new_min = CapAdd(vars[i]->Min(), new_min);
}
if (new_max != kint64max) {
new_max = CapAdd(vars[i]->Max(), new_max);
}
}
2012-09-05 15:52:00 +00:00
IntVar* sum_var = NULL;
2012-09-04 21:40:12 +00:00
const bool all_booleans = AreAllBooleans(vars);
if (all_booleans) {
2012-09-05 15:52:00 +00:00
const string name =
StringPrintf("BooleanSum([%s])", NameVector(vars, ", ").c_str());
sum_var = MakeIntVar(new_min, new_max, name);
2012-09-04 21:40:12 +00:00
AddConstraint(RevAlloc(
new SumBooleanEqualToVar(this, vars.data(), vars.size(), sum_var)));
} else if (new_min != kint64min && new_max != kint64max) {
2012-09-05 15:52:00 +00:00
sum_var = MakeSumFct(this, vars)->Var();
2012-09-04 21:40:12 +00:00
} else {
2012-09-05 15:52:00 +00:00
const string name =
StringPrintf("Sum([%s])", NameVector(vars, ", ").c_str());
sum_var = MakeIntVar(new_min, new_max, name);
2012-09-04 21:40:12 +00:00
AddConstraint(RevAlloc(new SafeSumConstraint(this, vars, sum_var)));
}
model_cache_->InsertVarArrayExpression(sum_var, vars,
ModelCache::VAR_ARRAY_SUM);
2012-09-04 21:40:12 +00:00
return sum_var;
}
}
}
IntExpr* Solver::MakeMin(const std::vector<IntVar*>& vars) {
const int size = vars.size();
if (size == 0) {
return MakeIntConst(0LL);
} else if (size == 1) {
return vars[0];
} else if (size == 2) {
return MakeMin(vars[0], vars[1]);
} else {
IntExpr* const cache =
model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_MIN);
if (cache != NULL) {
2013-07-19 03:47:59 +00:00
return cache;
2012-09-04 21:40:12 +00:00
} else {
if (AreAllBooleans(vars)) {
IntVar* const new_var = MakeBoolVar();
AddConstraint(RevAlloc(new ArrayBoolAndEq(this, vars, new_var)));
model_cache_->InsertVarArrayExpression(new_var, vars,
ModelCache::VAR_ARRAY_MIN);
2012-09-04 21:40:12 +00:00
return new_var;
} else {
int64 new_min = kint64max;
int64 new_max = kint64max;
for (int i = 0; i < size; ++i) {
new_min = std::min(new_min, vars[i]->Min());
new_max = std::min(new_max, vars[i]->Max());
}
IntVar* const new_var = MakeIntVar(new_min, new_max);
AddConstraint(RevAlloc(new MinConstraint(this, vars, new_var)));
model_cache_->InsertVarArrayExpression(new_var, vars,
ModelCache::VAR_ARRAY_MIN);
2012-09-04 21:40:12 +00:00
return new_var;
}
}
}
}
IntExpr* Solver::MakeMax(const std::vector<IntVar*>& vars) {
const int size = vars.size();
if (size == 0) {
return MakeIntConst(0LL);
} else if (size == 1) {
return vars[0];
} else if (size == 2) {
return MakeMax(vars[0], vars[1]);
} else {
IntExpr* const cache =
model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_MAX);
if (cache != NULL) {
2013-07-19 03:47:59 +00:00
return cache;
2012-09-04 21:40:12 +00:00
} else {
if (AreAllBooleans(vars)) {
IntVar* const new_var = MakeBoolVar();
AddConstraint(RevAlloc(new ArrayBoolOrEq(this, vars, new_var)));
model_cache_->InsertVarArrayExpression(new_var, vars,
ModelCache::VAR_ARRAY_MIN);
2012-09-04 21:40:12 +00:00
return new_var;
} else {
int64 new_min = kint64min;
int64 new_max = kint64min;
for (int i = 0; i < size; ++i) {
new_min = std::max(new_min, vars[i]->Min());
new_max = std::max(new_max, vars[i]->Max());
}
IntVar* const new_var = MakeIntVar(new_min, new_max);
AddConstraint(RevAlloc(new MaxConstraint(this, vars, new_var)));
model_cache_->InsertVarArrayExpression(new_var, vars,
ModelCache::VAR_ARRAY_MAX);
2012-09-04 21:40:12 +00:00
return new_var;
}
}
}
}
Constraint* Solver::MakeMinEquality(const std::vector<IntVar*>& vars,
IntVar* const min_var) {
const int size = vars.size();
if (size > 2) {
if (AreAllBooleans(vars)) {
return RevAlloc(new ArrayBoolAndEq(this, vars, min_var));
} else {
return RevAlloc(new MinConstraint(this, vars, min_var));
}
} else if (size == 2) {
2013-07-19 03:47:59 +00:00
return MakeEquality(MakeMin(vars[0], vars[1]), min_var);
2012-09-04 21:40:12 +00:00
} else if (size == 1) {
return MakeEquality(vars[0], min_var);
} else {
return MakeEquality(min_var, Zero());
}
}
Constraint* Solver::MakeMaxEquality(const std::vector<IntVar*>& vars,
IntVar* const max_var) {
const int size = vars.size();
if (size > 2) {
if (AreAllBooleans(vars)) {
return RevAlloc(new ArrayBoolOrEq(this, vars, max_var));
} else {
return RevAlloc(new MaxConstraint(this, vars, max_var));
}
} else if (size == 2) {
2013-07-19 03:47:59 +00:00
return MakeEquality(MakeMax(vars[0], vars[1]), max_var);
2012-09-04 21:40:12 +00:00
} else if (size == 1) {
return MakeEquality(vars[0], max_var);
} else {
return MakeEquality(max_var, Zero());
}
}
Constraint* Solver::MakeSumLessOrEqual(const std::vector<IntVar*>& vars, int64 cst) {
const int size = vars.size();
if (cst == 1LL && AreAllBooleans(vars) && size > 2) {
return RevAlloc(new SumBooleanLessOrEqualToOne(this, vars.data(), size));
} else {
return MakeLessOrEqual(MakeSum(vars), cst);
}
}
Constraint* Solver::MakeSumGreaterOrEqual(const std::vector<IntVar*>& vars,
int64 cst) {
const int size = vars.size();
if (cst == 1LL && AreAllBooleans(vars) && size > 2) {
return RevAlloc(new SumBooleanGreaterOrEqualToOne(this, vars.data(), size));
} else {
return MakeGreaterOrEqual(MakeSum(vars), cst);
}
}
Constraint* Solver::MakeSumEquality(const std::vector<IntVar*>& vars, int64 cst) {
const int size = vars.size();
if (size == 0) {
return cst == 0 ? MakeTrueConstraint() : MakeFalseConstraint();
}
if (AreAllBooleans(vars) && size > 2) {
if (cst == 1) {
return RevAlloc(new SumBooleanEqualToOne(this, vars.data(), size));
} else if (cst < 0 || cst > size) {
return MakeFalseConstraint();
} else {
return RevAlloc(
new SumBooleanEqualToVar(this, vars.data(), size, MakeIntConst(cst)));
2012-09-04 21:40:12 +00:00
}
} else {
if (vars.size() == 1) {
return MakeEquality(vars[0], cst);
} else if (vars.size() == 2) {
2013-07-19 03:47:59 +00:00
return MakeEquality(vars[0], MakeDifference(cst, vars[1]));
2012-09-04 21:40:12 +00:00
}
if (DetectSumOverflow(vars)) {
return RevAlloc(new SafeSumConstraint(this, vars, MakeIntConst(cst)));
} else {
return RevAlloc(new SumConstraint(this, vars, MakeIntConst(cst)));
}
}
}
Constraint* Solver::MakeSumEquality(const std::vector<IntVar*>& vars,
IntVar* const var) {
const int size = vars.size();
if (size == 0) {
return MakeEquality(var, Zero());
}
if (AreAllBooleans(vars) && size > 2) {
return RevAlloc(new SumBooleanEqualToVar(this, vars.data(), size, var));
} else if (size == 0) {
return MakeEquality(var, Zero());
} else if (size == 1) {
return MakeEquality(vars[0], var);
} else if (size == 2) {
2013-07-19 03:47:59 +00:00
return MakeEquality(MakeSum(vars[0], vars[1]), var);
2012-09-04 21:40:12 +00:00
} else {
if (DetectSumOverflow(vars)) {
return RevAlloc(new SafeSumConstraint(this, vars, var));
} else {
return RevAlloc(new SumConstraint(this, vars, var));
}
}
}
Constraint* Solver::MakeScalProdEquality(const std::vector<IntVar*>& vars,
const std::vector<int64>& coefficients,
int64 cst) {
DCHECK_EQ(vars.size(), coefficients.size());
return MakeScalProdEqualityFct<int64>(this, vars, coefficients, cst);
}
Constraint* Solver::MakeScalProdEquality(const std::vector<IntVar*>& vars,
const std::vector<int>& coefficients,
int64 cst) {
DCHECK_EQ(vars.size(), coefficients.size());
return MakeScalProdEqualityFct<int>(this, vars, coefficients, cst);
}
Constraint* Solver::MakeScalProdEquality(const std::vector<IntVar*>& vars,
const std::vector<int64>& coefficients,
IntVar* const target) {
DCHECK_EQ(vars.size(), coefficients.size());
return MakeScalProdEqualityVarFct<int64>(this, vars, coefficients, target);
}
Constraint* Solver::MakeScalProdEquality(const std::vector<IntVar*>& vars,
const std::vector<int>& coefficients,
IntVar* const target) {
DCHECK_EQ(vars.size(), coefficients.size());
return MakeScalProdEqualityVarFct<int>(this, vars, coefficients, target);
}
Constraint* Solver::MakeScalProdGreaterOrEqual(const std::vector<IntVar*>& vars,
const std::vector<int64>& coeffs,
int64 cst) {
DCHECK_EQ(vars.size(), coeffs.size());
return MakeScalProdGreaterOrEqualFct<int64>(this, vars, coeffs, cst);
}
Constraint* Solver::MakeScalProdGreaterOrEqual(const std::vector<IntVar*>& vars,
const std::vector<int>& coeffs,
int64 cst) {
DCHECK_EQ(vars.size(), coeffs.size());
return MakeScalProdGreaterOrEqualFct<int>(this, vars, coeffs, cst);
}
Constraint* Solver::MakeScalProdLessOrEqual(const std::vector<IntVar*>& vars,
const std::vector<int64>& coefficients,
int64 cst) {
DCHECK_EQ(vars.size(), coefficients.size());
return MakeScalProdLessOrEqualFct<int64>(this, vars, coefficients, cst);
}
Constraint* Solver::MakeScalProdLessOrEqual(const std::vector<IntVar*>& vars,
const std::vector<int>& coefficients,
int64 cst) {
DCHECK_EQ(vars.size(), coefficients.size());
return MakeScalProdLessOrEqualFct<int>(this, vars, coefficients, cst);
}
IntExpr* Solver::MakeScalProd(const std::vector<IntVar*>& vars,
const std::vector<int64>& coefs) {
DCHECK_EQ(vars.size(), coefs.size());
2012-06-14 19:32:41 +00:00
return MakeScalProdFct<int64>(this, vars, coefs);
2010-09-15 12:42:33 +00:00
}
IntExpr* Solver::MakeScalProd(const std::vector<IntVar*>& vars,
const std::vector<int>& coefs) {
DCHECK_EQ(vars.size(), coefs.size());
2012-06-14 19:32:41 +00:00
return MakeScalProdFct<int>(this, vars, coefs);
2010-09-15 12:42:33 +00:00
}
} // namespace operations_research