2022-06-17 08:40:20 +02:00
|
|
|
// Copyright 2010-2022 Google LLC
|
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.
|
2014-07-09 15:18:27 +00:00
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
//
|
|
|
|
|
// Array Expression constraints
|
|
|
|
|
|
2011-09-21 15:16:48 +00:00
|
|
|
#include <algorithm>
|
2013-12-16 10:24:42 +00:00
|
|
|
#include <cmath>
|
2021-04-01 12:13:35 +02:00
|
|
|
#include <cstdint>
|
|
|
|
|
#include <limits>
|
2011-09-21 15:16:48 +00:00
|
|
|
#include <string>
|
|
|
|
|
#include <vector>
|
|
|
|
|
|
2018-10-31 16:18:18 +01:00
|
|
|
#include "absl/strings/str_format.h"
|
|
|
|
|
#include "absl/strings/str_join.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/base/integral_types.h"
|
2018-06-08 16:40:43 +02:00
|
|
|
#include "ortools/base/logging.h"
|
2017-04-26 17:30:25 +02:00
|
|
|
#include "ortools/base/mathutil.h"
|
|
|
|
|
#include "ortools/constraint_solver/constraint_solver.h"
|
|
|
|
|
#include "ortools/constraint_solver/constraint_solveri.h"
|
|
|
|
|
#include "ortools/util/saturated_arithmetic.h"
|
|
|
|
|
#include "ortools/util/string_array.h"
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
namespace operations_research {
|
2011-08-11 05:15:18 +00:00
|
|
|
namespace {
|
2011-05-26 09:34:39 +00:00
|
|
|
// ----- Tree Array Constraint -----
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
class TreeArrayConstraint : public CastConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
TreeArrayConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const sum_var)
|
2020-10-22 23:36:58 +02:00
|
|
|
: CastConstraint(solver, sum_var),
|
|
|
|
|
vars_(vars),
|
2016-02-03 15:15:58 +01:00
|
|
|
block_size_(solver->parameters().array_split_size()) {
|
2011-05-26 09:34:39 +00:00
|
|
|
std::vector<int> lengths;
|
2013-10-10 15:23:20 +00:00
|
|
|
lengths.push_back(vars_.size());
|
2011-05-26 09:34:39 +00:00
|
|
|
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];
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
std::string DebugStringInternal(const std::string& name) const {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("%s(%s) == %s", name,
|
|
|
|
|
JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
target_var_->DebugString());
|
2012-05-28 21:37:13 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void AcceptInternal(const std::string& name,
|
|
|
|
|
ModelVisitor* const visitor) const {
|
2012-05-28 21:37:13 +00:00
|
|
|
visitor->BeginVisitConstraint(name, this);
|
|
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2012-05-28 21:37:13 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
|
|
|
|
target_var_);
|
|
|
|
|
visitor->EndVisitConstraint(name, this);
|
|
|
|
|
}
|
|
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
// Increases min by delta_min, reduces max by delta_max.
|
2021-04-01 12:13:35 +02:00
|
|
|
void ReduceRange(int depth, int position, int64_t delta_min,
|
|
|
|
|
int64_t delta_max) {
|
2020-10-29 14:25:39 +01:00
|
|
|
NodeInfo* const info = &tree_[depth][position];
|
2011-05-26 09:34:39 +00:00
|
|
|
if (delta_min > 0) {
|
2020-10-22 23:36:58 +02:00
|
|
|
info->node_min.SetValue(solver(),
|
|
|
|
|
CapAdd(info->node_min.Value(), delta_min));
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
if (delta_max > 0) {
|
2020-10-22 23:36:58 +02:00
|
|
|
info->node_max.SetValue(solver(),
|
|
|
|
|
CapSub(info->node_max.Value(), delta_max));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2011-05-26 09:34:39 +00:00
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
// Sets the range on the given node.
|
2021-04-01 12:13:35 +02:00
|
|
|
void SetRange(int depth, int position, int64_t new_min, int64_t new_max) {
|
2020-10-29 14:25:39 +01:00
|
|
|
NodeInfo* const info = &tree_[depth][position];
|
2012-05-28 21:37:13 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void InitLeaf(int position, int64_t var_min, int64_t var_max) {
|
2012-06-14 16:43:04 +00:00
|
|
|
InitNode(MaxDepth(), position, var_min, var_max);
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void InitNode(int depth, int position, int64_t node_min, int64_t 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);
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t Min(int depth, int position) const {
|
2011-12-16 21:02:59 +00:00
|
|
|
return tree_[depth][position].node_min.Value();
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t Max(int depth, int position) const {
|
2011-12-16 21:02:59 +00:00
|
|
|
return tree_[depth][position].node_max.Value();
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t RootMin() const { return root_node_->node_min.Value(); }
|
2011-05-26 09:34:39 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t RootMax() const { return root_node_->node_max.Value(); }
|
2011-05-26 09:34:39 +00:00
|
|
|
|
2013-07-24 00:28:11 +00:00
|
|
|
int Parent(int position) const { return position / block_size_; }
|
2011-05-26 09:34:39 +00:00
|
|
|
|
2013-07-24 00:28:11 +00:00
|
|
|
int ChildStart(int position) const { return position * block_size_; }
|
2011-05-26 09:34:39 +00:00
|
|
|
|
|
|
|
|
int ChildEnd(int depth, int position) const {
|
|
|
|
|
DCHECK_LT(depth + 1, tree_.size());
|
2011-05-30 14:27:57 +00:00
|
|
|
return std::min((position + 1) * block_size_ - 1, Width(depth + 1) - 1);
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
|
2013-07-24 00:28:11 +00:00
|
|
|
bool IsLeaf(int depth) const { return depth == MaxDepth(); }
|
2011-05-26 09:34:39 +00:00
|
|
|
|
2013-07-24 00:28:11 +00:00
|
|
|
int MaxDepth() const { return tree_.size() - 1; }
|
2011-05-26 09:34:39 +00:00
|
|
|
|
2013-07-24 00:28:11 +00:00
|
|
|
int Width(int depth) const { return tree_[depth].size(); }
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
protected:
|
2020-10-29 14:25:39 +01:00
|
|
|
const std::vector<IntVar*> vars_;
|
2012-05-28 21:37:13 +00:00
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2011-05-26 09:34:39 +00:00
|
|
|
struct NodeInfo {
|
2011-12-16 21:02:59 +00:00
|
|
|
NodeInfo() : node_min(0), node_max(0) {}
|
2021-04-01 12:13:35 +02:00
|
|
|
Rev<int64_t> node_min;
|
|
|
|
|
Rev<int64_t> node_max;
|
2011-05-26 09:34:39 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
std::vector<std::vector<NodeInfo> > tree_;
|
|
|
|
|
const int block_size_;
|
2020-10-29 14:25:39 +01:00
|
|
|
NodeInfo* root_node_;
|
2011-05-26 09:34:39 +00:00
|
|
|
};
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
// ---------- Sum Array ----------
|
|
|
|
|
|
2010-11-25 16:17:25 +00:00
|
|
|
// 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,
|
2018-09-10 13:32:30 +02:00
|
|
|
// Laurent Perron and Christian Schulte editors, pages 39-46, 2002.
|
2010-11-25 16:17:25 +00:00
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
// ----- SumConstraint -----
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
// This constraint implements sum(vars) == sum_var.
|
|
|
|
|
class SumConstraint : public TreeArrayConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SumConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const sum_var)
|
2013-10-10 15:23:20 +00:00
|
|
|
: TreeArrayConstraint(solver, vars, sum_var), sum_demon_(nullptr) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~SumConstraint() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const demon = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &SumConstraint::LeafChanged, "LeafChanged", i);
|
2011-05-26 09:34:39 +00:00
|
|
|
vars_[i]->WhenRange(demon);
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
sum_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
|
|
|
|
solver(), this, &SumConstraint::SumChanged, "SumChanged"));
|
2011-11-07 15:31:18 +00:00
|
|
|
target_var_->WhenRange(sum_demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2011-05-26 09:34:39 +00:00
|
|
|
// Copy vars to leaf nodes.
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2012-06-14 16:43:04 +00:00
|
|
|
InitLeaf(i, vars_[i]->Min(), vars_[i]->Max());
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
// Compute up.
|
2011-05-30 14:27:57 +00:00
|
|
|
for (int i = MaxDepth() - 1; i >= 0; --i) {
|
2011-05-26 09:34:39 +00:00
|
|
|
for (int j = 0; j < Width(i); ++j) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum_min = 0;
|
|
|
|
|
int64_t sum_max = 0;
|
2011-05-26 09:34:39 +00:00
|
|
|
const int block_start = ChildStart(j);
|
|
|
|
|
const int block_end = ChildEnd(i, j);
|
|
|
|
|
for (int k = block_start; k <= block_end; ++k) {
|
2015-10-23 13:45:43 +02:00
|
|
|
sum_min = CapAdd(sum_min, Min(i + 1, k));
|
|
|
|
|
sum_max = CapAdd(sum_max, Max(i + 1, k));
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
2012-06-14 16:43:04 +00:00
|
|
|
InitNode(i, j, sum_min, sum_max);
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Propagate to sum_var.
|
2011-11-07 15:31:18 +00:00
|
|
|
target_var_->SetRange(RootMin(), RootMax());
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
// Push down.
|
|
|
|
|
SumChanged();
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
void SumChanged() {
|
2021-04-01 12:13:35 +02:00
|
|
|
if (target_var_->Max() == RootMin() &&
|
|
|
|
|
target_var_->Max() != std::numeric_limits<int64_t>::max()) {
|
2011-05-26 09:34:39 +00:00
|
|
|
// We can fix all terms to min.
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2011-05-26 09:34:39 +00:00
|
|
|
vars_[i]->SetValue(vars_[i]->Min());
|
|
|
|
|
}
|
2012-06-14 16:43:04 +00:00
|
|
|
} else if (target_var_->Min() == RootMax() &&
|
2021-04-01 12:13:35 +02:00
|
|
|
target_var_->Min() != std::numeric_limits<int64_t>::min()) {
|
2011-05-26 09:34:39 +00:00
|
|
|
// We can fix all terms to max.
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2011-05-26 09:34:39 +00:00
|
|
|
vars_[i]->SetValue(vars_[i]->Max());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2011-11-07 15:31:18 +00:00
|
|
|
PushDown(0, 0, target_var_->Min(), target_var_->Max());
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void PushDown(int depth, int position, int64_t new_min, int64_t new_max) {
|
2011-05-26 09:34:39 +00:00
|
|
|
// Nothing to do?
|
|
|
|
|
if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2011-05-26 09:34:39 +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
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
// Standard propagation from the bounds of the sum to the
|
|
|
|
|
// individuals terms.
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
// These are maintained automatically in the tree structure.
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t sum_min = Min(depth, position);
|
|
|
|
|
const int64_t sum_max = Max(depth, position);
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2011-05-26 09:34:39 +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
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
// Detect failure early.
|
|
|
|
|
if (new_max < sum_min || new_min > sum_max) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2011-05-26 09:34:39 +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) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t target_var_min = Min(depth + 1, i);
|
|
|
|
|
const int64_t target_var_max = Max(depth + 1, i);
|
|
|
|
|
const int64_t residual_min = CapSub(sum_min, target_var_min);
|
|
|
|
|
const int64_t residual_max = CapSub(sum_max, target_var_max);
|
2015-10-23 13:45:43 +02:00
|
|
|
PushDown(depth + 1, i, CapSub(new_min, residual_max),
|
|
|
|
|
CapSub(new_max, residual_min));
|
2011-05-26 09:34:39 +00:00
|
|
|
}
|
|
|
|
|
// TODO(user) : Is the diameter optimization (see reference
|
|
|
|
|
// above, rule 5) useful?
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2011-05-26 09:34:39 +00:00
|
|
|
void LeafChanged(int term_index) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = vars_[term_index];
|
2015-10-23 13:45:43 +02:00
|
|
|
PushUp(term_index, CapSub(var->Min(), var->OldMin()),
|
|
|
|
|
CapSub(var->OldMax(), var->Max()));
|
2020-10-22 23:36:58 +02:00
|
|
|
EnqueueDelayedDemon(sum_demon_); // TODO(user): Is this needed?
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void PushUp(int position, int64_t delta_min, int64_t delta_max) {
|
2011-05-26 09:34:39 +00:00
|
|
|
DCHECK_GE(delta_max, 0);
|
|
|
|
|
DCHECK_GE(delta_min, 0);
|
2015-10-23 13:45:43 +02:00
|
|
|
DCHECK_GT(CapAdd(delta_min, delta_max), 0);
|
2011-05-30 14:27:57 +00:00
|
|
|
for (int depth = MaxDepth(); depth >= 0; --depth) {
|
2011-05-26 09:34:39 +00:00
|
|
|
ReduceRange(depth, position, delta_min, delta_max);
|
|
|
|
|
position = Parent(position);
|
|
|
|
|
}
|
|
|
|
|
DCHECK_EQ(0, position);
|
2011-11-07 15:31:18 +00:00
|
|
|
target_var_->SetRange(RootMin(), RootMax());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2018-06-08 16:40:43 +02:00
|
|
|
std::string DebugString() const override {
|
|
|
|
|
return DebugStringInternal("Sum");
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
AcceptInternal(ModelVisitor::kSumEqual, visitor);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* sum_demon_;
|
2011-05-26 09:34:39 +00:00
|
|
|
};
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2014-08-15 16:56:56 +00:00
|
|
|
// This constraint implements sum(vars) == target_var.
|
2014-07-18 21:32:02 +00:00
|
|
|
class SmallSumConstraint : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SmallSumConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const target_var)
|
2020-10-22 23:36:58 +02:00
|
|
|
: Constraint(solver),
|
|
|
|
|
vars_(vars),
|
|
|
|
|
target_var_(target_var),
|
|
|
|
|
computed_min_(0),
|
|
|
|
|
computed_max_(0),
|
|
|
|
|
sum_demon_(nullptr) {}
|
2014-07-18 21:32:02 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~SmallSumConstraint() override {}
|
2014-07-18 21:32:02 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2014-07-18 21:32:02 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2014-08-15 16:56:56 +00:00
|
|
|
if (!vars_[i]->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const demon = MakeConstraintDemon1(
|
2015-08-13 16:00:54 +02:00
|
|
|
solver(), this, &SmallSumConstraint::VarChanged, "VarChanged",
|
|
|
|
|
vars_[i]);
|
2014-08-15 16:56:56 +00:00
|
|
|
vars_[i]->WhenRange(demon);
|
|
|
|
|
}
|
2014-07-18 21:32:02 +00:00
|
|
|
}
|
|
|
|
|
sum_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
|
|
|
|
solver(), this, &SmallSumConstraint::SumChanged, "SumChanged"));
|
|
|
|
|
target_var_->WhenRange(sum_demon_);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2014-07-18 21:32:02 +00:00
|
|
|
// Compute up.
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum_min = 0;
|
|
|
|
|
int64_t sum_max = 0;
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2015-10-23 13:45:43 +02:00
|
|
|
sum_min = CapAdd(sum_min, var->Min());
|
|
|
|
|
sum_max = CapAdd(sum_max, var->Max());
|
2014-07-18 21:32:02 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Propagate to sum_var.
|
|
|
|
|
computed_min_.SetValue(solver(), sum_min);
|
|
|
|
|
computed_max_.SetValue(solver(), sum_max);
|
|
|
|
|
target_var_->SetRange(sum_min, sum_max);
|
|
|
|
|
|
|
|
|
|
// Push down.
|
|
|
|
|
SumChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SumChanged() {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t new_min = target_var_->Min();
|
|
|
|
|
int64_t new_max = target_var_->Max();
|
|
|
|
|
const int64_t sum_min = computed_min_.Value();
|
|
|
|
|
const int64_t sum_max = computed_max_.Value();
|
|
|
|
|
if (new_max == sum_min && new_max != std::numeric_limits<int64_t>::max()) {
|
2014-07-18 21:32:02 +00:00
|
|
|
// We can fix all terms to min.
|
|
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
|
|
|
|
vars_[i]->SetValue(vars_[i]->Min());
|
|
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
} else if (new_min == sum_max &&
|
|
|
|
|
new_min != std::numeric_limits<int64_t>::min()) {
|
2014-07-18 21:32:02 +00:00
|
|
|
// We can fix all terms to max.
|
|
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
|
|
|
|
vars_[i]->SetValue(vars_[i]->Max());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (new_min > sum_min || new_max < sum_max) { // something to do.
|
2014-07-18 21:32:02 +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);
|
|
|
|
|
|
|
|
|
|
// Detect failure early.
|
|
|
|
|
if (new_max < sum_min || new_min > sum_max) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Push to variables.
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t var_min = var->Min();
|
|
|
|
|
const int64_t var_max = var->Max();
|
|
|
|
|
const int64_t residual_min = CapSub(sum_min, var_min);
|
|
|
|
|
const int64_t residual_max = CapSub(sum_max, var_max);
|
2015-10-23 13:45:43 +02:00
|
|
|
var->SetRange(CapSub(new_min, residual_max),
|
|
|
|
|
CapSub(new_max, residual_min));
|
2014-07-18 21:32:02 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VarChanged(IntVar* var) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t delta_min = CapSub(var->Min(), var->OldMin());
|
|
|
|
|
const int64_t delta_max = CapSub(var->OldMax(), var->Max());
|
2014-07-18 21:32:02 +00:00
|
|
|
computed_min_.Add(solver(), delta_min);
|
|
|
|
|
computed_max_.Add(solver(), -delta_max);
|
|
|
|
|
if (computed_max_.Value() < target_var_->Max() ||
|
|
|
|
|
computed_min_.Value() > target_var_->Min()) {
|
|
|
|
|
target_var_->SetRange(computed_min_.Value(), computed_max_.Value());
|
|
|
|
|
} else {
|
|
|
|
|
EnqueueDelayedDemon(sum_demon_);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("SmallSum(%s) == %s",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
target_var_->DebugString());
|
2014-07-18 21:32:02 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2014-07-18 21:32:02 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
|
|
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
|
|
|
|
vars_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
|
|
|
|
target_var_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
const std::vector<IntVar*> vars_;
|
|
|
|
|
IntVar* target_var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
NumericalRev<int64_t> computed_min_;
|
|
|
|
|
NumericalRev<int64_t> computed_max_;
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* sum_demon_;
|
2014-07-18 21:32:02 +00:00
|
|
|
};
|
2012-06-14 16:43:04 +00:00
|
|
|
// ----- SafeSumConstraint -----
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
bool DetectSumOverflow(const std::vector<IntVar*>& vars) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum_min = 0;
|
|
|
|
|
int64_t sum_max = 0;
|
2012-06-14 16:43:04 +00:00
|
|
|
for (int i = 0; i < vars.size(); ++i) {
|
|
|
|
|
sum_min = CapAdd(sum_min, vars[i]->Min());
|
|
|
|
|
sum_max = CapAdd(sum_max, vars[i]->Max());
|
2021-04-01 12:13:35 +02:00
|
|
|
if (sum_min == std::numeric_limits<int64_t>::min() ||
|
|
|
|
|
sum_max == std::numeric_limits<int64_t>::max()) {
|
2012-06-14 16:43:04 +00:00
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// This constraint implements sum(vars) == sum_var.
|
|
|
|
|
class SafeSumConstraint : public TreeArrayConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SafeSumConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const sum_var)
|
2013-10-10 15:23:20 +00:00
|
|
|
: TreeArrayConstraint(solver, vars, sum_var), sum_demon_(nullptr) {}
|
2012-06-14 16:43:04 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~SafeSumConstraint() override {}
|
2012-06-14 16:43:04 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const demon = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &SafeSumConstraint::LeafChanged, "LeafChanged", i);
|
2012-06-14 16:43:04 +00:00
|
|
|
vars_[i]->WhenRange(demon);
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
sum_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
|
|
|
|
solver(), this, &SafeSumConstraint::SumChanged, "SumChanged"));
|
2012-06-14 16:43:04 +00:00
|
|
|
target_var_->WhenRange(sum_demon_);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void SafeComputeNode(int depth, int position, int64_t* const sum_min,
|
|
|
|
|
int64_t* const sum_max) {
|
2012-06-14 16:43:04 +00:00
|
|
|
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) {
|
2021-04-01 12:13:35 +02:00
|
|
|
if (*sum_min != std::numeric_limits<int64_t>::min()) {
|
2012-06-14 16:43:04 +00:00
|
|
|
*sum_min = CapAdd(*sum_min, Min(depth + 1, k));
|
|
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
if (*sum_max != std::numeric_limits<int64_t>::max()) {
|
2012-06-14 16:43:04 +00:00
|
|
|
*sum_max = CapAdd(*sum_max, Max(depth + 1, k));
|
|
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
if (*sum_min == std::numeric_limits<int64_t>::min() &&
|
|
|
|
|
*sum_max == std::numeric_limits<int64_t>::max()) {
|
2012-06-14 16:43:04 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2012-06-14 16:43:04 +00:00
|
|
|
// Copy vars to leaf nodes.
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.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) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum_min = 0;
|
|
|
|
|
int64_t sum_max = 0;
|
2012-06-14 16:43:04 +00:00
|
|
|
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.
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2012-06-14 16:43:04 +00:00
|
|
|
vars_[i]->SetValue(vars_[i]->Min());
|
|
|
|
|
}
|
|
|
|
|
} else if (target_var_->Min() == RootMax()) {
|
|
|
|
|
// We can fix all terms to max.
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2012-06-14 16:43:04 +00:00
|
|
|
vars_[i]->SetValue(vars_[i]->Max());
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
PushDown(0, 0, target_var_->Min(), target_var_->Max());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void PushDown(int depth, int position, int64_t new_min, int64_t new_max) {
|
2012-06-14 16:43:04 +00:00
|
|
|
// 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.
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t sum_min = Min(depth, position);
|
|
|
|
|
const int64_t sum_max = Max(depth, position);
|
2012-06-14 16:43:04 +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);
|
|
|
|
|
|
|
|
|
|
// 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) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t target_var_min = Min(depth + 1, pos);
|
|
|
|
|
const int64_t residual_min =
|
|
|
|
|
sum_min != std::numeric_limits<int64_t>::min()
|
|
|
|
|
? CapSub(sum_min, target_var_min)
|
|
|
|
|
: std::numeric_limits<int64_t>::min();
|
|
|
|
|
const int64_t target_var_max = Max(depth + 1, pos);
|
|
|
|
|
const int64_t residual_max =
|
|
|
|
|
sum_max != std::numeric_limits<int64_t>::max()
|
|
|
|
|
? CapSub(sum_max, target_var_max)
|
|
|
|
|
: std::numeric_limits<int64_t>::max();
|
2013-07-24 00:28:11 +00:00
|
|
|
PushDown(depth + 1, pos,
|
2021-04-01 12:13:35 +02:00
|
|
|
(residual_max == std::numeric_limits<int64_t>::min()
|
|
|
|
|
? std::numeric_limits<int64_t>::min()
|
|
|
|
|
: CapSub(new_min, residual_max)),
|
|
|
|
|
(residual_min == std::numeric_limits<int64_t>::max()
|
|
|
|
|
? std::numeric_limits<int64_t>::min()
|
|
|
|
|
: 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) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = vars_[term_index];
|
2013-07-24 00:28:11 +00:00
|
|
|
PushUp(term_index, CapSub(var->Min(), var->OldMin()),
|
2012-06-14 16:43:04 +00:00
|
|
|
CapSub(var->OldMax(), var->Max()));
|
2020-10-22 23:36:58 +02:00
|
|
|
EnqueueDelayedDemon(sum_demon_); // TODO(user): Is this needed?
|
2012-06-14 16:43:04 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void PushUp(int position, int64_t delta_min, int64_t delta_max) {
|
2012-06-14 16:43:04 +00:00
|
|
|
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) {
|
2021-04-01 12:13:35 +02:00
|
|
|
if (Min(depth, position) != std::numeric_limits<int64_t>::min() &&
|
|
|
|
|
Max(depth, position) != std::numeric_limits<int64_t>::max() &&
|
|
|
|
|
delta_min != std::numeric_limits<int64_t>::max() &&
|
|
|
|
|
delta_max != std::numeric_limits<int64_t>::max() &&
|
|
|
|
|
!delta_corrupted) { // No overflow.
|
2012-06-14 16:43:04 +00:00
|
|
|
ReduceRange(depth, position, delta_min, delta_max);
|
2020-10-22 23:36:58 +02:00
|
|
|
} else if (depth == MaxDepth()) { // Leaf.
|
2013-07-24 00:28:11 +00:00
|
|
|
SetRange(depth, position, vars_[position]->Min(),
|
2012-06-14 16:43:04 +00:00
|
|
|
vars_[position]->Max());
|
|
|
|
|
delta_corrupted = true;
|
2020-10-22 23:36:58 +02:00
|
|
|
} else { // Recompute.
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum_min = 0;
|
|
|
|
|
int64_t sum_max = 0;
|
2012-06-14 16:43:04 +00:00
|
|
|
SafeComputeNode(depth, position, &sum_min, &sum_max);
|
2021-04-01 12:13:35 +02:00
|
|
|
if (sum_min == std::numeric_limits<int64_t>::min() &&
|
|
|
|
|
sum_max == std::numeric_limits<int64_t>::max()) {
|
2020-10-22 23:36:58 +02:00
|
|
|
return; // Nothing to do upward.
|
2012-06-14 16:43:04 +00:00
|
|
|
}
|
|
|
|
|
SetRange(depth, position, sum_min, sum_max);
|
|
|
|
|
delta_corrupted = true;
|
|
|
|
|
}
|
|
|
|
|
position = Parent(position);
|
|
|
|
|
}
|
|
|
|
|
DCHECK_EQ(0, position);
|
|
|
|
|
target_var_->SetRange(RootMin(), RootMax());
|
|
|
|
|
}
|
|
|
|
|
|
2018-06-08 16:40:43 +02:00
|
|
|
std::string DebugString() const override {
|
|
|
|
|
return DebugStringInternal("Sum");
|
|
|
|
|
}
|
2012-06-14 16:43:04 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2012-06-14 16:43:04 +00:00
|
|
|
AcceptInternal(ModelVisitor::kSumEqual, visitor);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2013-06-11 14:49:19 +00:00
|
|
|
bool CheckInternalState() {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2013-06-11 14:49:19 +00:00
|
|
|
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) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum_min = 0;
|
|
|
|
|
int64_t sum_max = 0;
|
2013-06-11 14:49:19 +00:00
|
|
|
SafeComputeNode(i, j, &sum_min, &sum_max);
|
|
|
|
|
CheckNode(i, j, sum_min, sum_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void CheckLeaf(int position, int64_t var_min, int64_t var_max) {
|
2013-06-11 14:49:19 +00:00
|
|
|
CheckNode(MaxDepth(), position, var_min, var_max);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void CheckNode(int depth, int position, int64_t node_min, int64_t node_max) {
|
2013-06-11 14:49:19 +00:00
|
|
|
DCHECK_EQ(Min(depth, position), node_min);
|
|
|
|
|
DCHECK_EQ(Max(depth, position), node_max);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* sum_demon_;
|
2012-06-14 16:43:04 +00:00
|
|
|
};
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
// ---------- Min Array ----------
|
|
|
|
|
|
2018-10-31 16:18:18 +01:00
|
|
|
// This constraint implements min(vars) == min_var.
|
2012-05-28 21:37:13 +00:00
|
|
|
class MinConstraint : public TreeArrayConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
MinConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const min_var)
|
2013-10-10 15:23:20 +00:00
|
|
|
: TreeArrayConstraint(solver, vars, min_var), min_demon_(nullptr) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~MinConstraint() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const demon = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &MinConstraint::LeafChanged, "LeafChanged", i);
|
2012-05-28 21:37:13 +00:00
|
|
|
vars_[i]->WhenRange(demon);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
min_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
|
|
|
|
solver(), this, &MinConstraint::MinVarChanged, "MinVarChanged"));
|
2012-05-28 21:37:13 +00:00
|
|
|
target_var_->WhenRange(min_demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2012-05-28 21:37:13 +00:00
|
|
|
// Copy vars to leaf nodes.
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.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
|
|
|
}
|
|
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
// Compute up.
|
|
|
|
|
for (int i = MaxDepth() - 1; i >= 0; --i) {
|
|
|
|
|
for (int j = 0; j < Width(i); ++j) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t min_min = std::numeric_limits<int64_t>::max();
|
|
|
|
|
int64_t min_max = std::numeric_limits<int64_t>::max();
|
2012-05-28 21:37:13 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
// Propagate to min_var.
|
|
|
|
|
target_var_->SetRange(RootMin(), RootMax());
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
// Push down.
|
|
|
|
|
MinVarChanged();
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
void MinVarChanged() {
|
|
|
|
|
PushDown(0, 0, target_var_->Min(), target_var_->Max());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void PushDown(int depth, int position, int64_t new_min, int64_t new_max) {
|
2012-05-28 21:37:13 +00:00
|
|
|
// Nothing to do?
|
|
|
|
|
if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
|
|
|
|
|
return;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-28 21:37:13 +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
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t node_min = Min(depth, position);
|
|
|
|
|
const int64_t node_max = Max(depth, position);
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2012-05-28 21:37:13 +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
|
|
|
|
2012-05-28 21:37:13 +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
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
if (active == 0) {
|
|
|
|
|
solver()->Fail();
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-28 21:37:13 +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
|
|
|
}
|
|
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
} else if (active == 1) {
|
|
|
|
|
PushDown(depth + 1, candidate, Min(depth + 1, candidate), new_max);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-10 15:23:20 +00:00
|
|
|
// TODO(user): Regroup code between Min and Max constraints.
|
2012-05-28 21:37:13 +00:00
|
|
|
void LeafChanged(int term_index) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = vars_[term_index];
|
2012-05-28 21:37:13 +00:00
|
|
|
SetRange(MaxDepth(), term_index, var->Min(), var->Max());
|
2013-08-01 23:38:44 +00:00
|
|
|
const int parent_depth = MaxDepth() - 1;
|
|
|
|
|
const int parent = Parent(term_index);
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t old_min = var->OldMin();
|
|
|
|
|
const int64_t var_min = var->Min();
|
|
|
|
|
const int64_t var_max = var->Max();
|
2013-08-01 23:38:44 +00:00
|
|
|
if ((old_min == Min(parent_depth, parent) && old_min != var_min) ||
|
|
|
|
|
var_max < Max(parent_depth, parent)) {
|
|
|
|
|
// Can influence the parent bounds.
|
|
|
|
|
PushUp(term_index);
|
|
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-31 10:53:29 +00:00
|
|
|
void PushUp(int position) {
|
2012-05-28 21:37:13 +00:00
|
|
|
int depth = MaxDepth();
|
|
|
|
|
while (depth > 0) {
|
|
|
|
|
const int parent = Parent(position);
|
|
|
|
|
const int parent_depth = depth - 1;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t min_min = std::numeric_limits<int64_t>::max();
|
|
|
|
|
int64_t min_max = std::numeric_limits<int64_t>::max();
|
2012-05-28 21:37:13 +00:00
|
|
|
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
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
depth = parent_depth;
|
2012-06-05 08:50:33 +00:00
|
|
|
position = parent;
|
2012-05-28 21:37:13 +00:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
if (depth == 0) { // We have pushed all the way up.
|
2012-05-28 21:37:13 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2018-06-08 16:40:43 +02:00
|
|
|
std::string DebugString() const override {
|
|
|
|
|
return DebugStringInternal("Min");
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2012-05-28 21:37:13 +00:00
|
|
|
AcceptInternal(ModelVisitor::kMinEqual, visitor);
|
2011-07-11 20:13:14 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* min_demon_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2014-07-18 00:14:31 +00:00
|
|
|
class SmallMinConstraint : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SmallMinConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const target_var)
|
2020-10-22 23:36:58 +02:00
|
|
|
: Constraint(solver),
|
|
|
|
|
vars_(vars),
|
|
|
|
|
target_var_(target_var),
|
|
|
|
|
computed_min_(0),
|
|
|
|
|
computed_max_(0) {}
|
2014-07-18 00:14:31 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~SmallMinConstraint() override {}
|
2014-07-18 00:14:31 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2014-07-18 00:14:31 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2014-08-15 16:56:56 +00:00
|
|
|
if (!vars_[i]->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const demon = MakeConstraintDemon1(
|
2015-08-13 16:00:54 +02:00
|
|
|
solver(), this, &SmallMinConstraint::VarChanged, "VarChanged",
|
|
|
|
|
vars_[i]);
|
2014-08-15 16:56:56 +00:00
|
|
|
vars_[i]->WhenRange(demon);
|
|
|
|
|
}
|
2014-07-18 00:14:31 +00:00
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const mdemon = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
2014-07-18 00:14:31 +00:00
|
|
|
solver(), this, &SmallMinConstraint::MinVarChanged, "MinVarChanged"));
|
|
|
|
|
target_var_->WhenRange(mdemon);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t min_min = std::numeric_limits<int64_t>::max();
|
|
|
|
|
int64_t min_max = std::numeric_limits<int64_t>::max();
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2014-07-18 00:14:31 +00:00
|
|
|
min_min = std::min(min_min, var->Min());
|
|
|
|
|
min_max = std::min(min_max, var->Max());
|
|
|
|
|
}
|
2014-07-18 00:29:58 +00:00
|
|
|
computed_min_.SetValue(solver(), min_min);
|
|
|
|
|
computed_max_.SetValue(solver(), min_max);
|
2014-07-18 00:14:31 +00:00
|
|
|
// Propagate to min_var.
|
|
|
|
|
target_var_->SetRange(min_min, min_max);
|
|
|
|
|
|
|
|
|
|
// Push down.
|
|
|
|
|
MinVarChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("SmallMin(%s) == %s",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
target_var_->DebugString());
|
2014-07-18 00:14:31 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2014-07-18 00:14:31 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kMinEqual, this);
|
|
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
|
|
|
|
vars_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
|
|
|
|
target_var_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kMinEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
void VarChanged(IntVar* var) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t old_min = var->OldMin();
|
|
|
|
|
const int64_t var_min = var->Min();
|
|
|
|
|
const int64_t var_max = var->Max();
|
2014-07-18 00:29:58 +00:00
|
|
|
if ((old_min == computed_min_.Value() && old_min != var_min) ||
|
|
|
|
|
var_max < computed_max_.Value()) {
|
2014-07-18 00:14:31 +00:00
|
|
|
// Can influence the min var bounds.
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t min_min = std::numeric_limits<int64_t>::max();
|
|
|
|
|
int64_t min_max = std::numeric_limits<int64_t>::max();
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2014-07-18 00:14:31 +00:00
|
|
|
min_min = std::min(min_min, var->Min());
|
|
|
|
|
min_max = std::min(min_max, var->Max());
|
|
|
|
|
}
|
2014-08-15 16:56:56 +00:00
|
|
|
if (min_min > computed_min_.Value() || min_max < computed_max_.Value()) {
|
2014-07-18 00:29:58 +00:00
|
|
|
computed_min_.SetValue(solver(), min_min);
|
|
|
|
|
computed_max_.SetValue(solver(), min_max);
|
|
|
|
|
target_var_->SetRange(computed_min_.Value(), computed_max_.Value());
|
2014-07-18 00:14:31 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
MinVarChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MinVarChanged() {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t new_min = target_var_->Min();
|
|
|
|
|
const int64_t new_max = target_var_->Max();
|
2014-07-18 00:14:31 +00:00
|
|
|
// Nothing to do?
|
2014-08-15 16:56:56 +00:00
|
|
|
if (new_min <= computed_min_.Value() && new_max >= computed_max_.Value()) {
|
2014-07-18 00:14:31 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* candidate = nullptr;
|
2014-07-18 00:14:31 +00:00
|
|
|
int active = 0;
|
|
|
|
|
|
2014-07-18 00:29:58 +00:00
|
|
|
if (new_max < computed_max_.Value()) {
|
2014-07-18 00:14:31 +00:00
|
|
|
// Look if only one candidate to push the max down.
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2014-07-18 00:14:31 +00:00
|
|
|
if (var->Min() <= new_max) {
|
|
|
|
|
if (active++ >= 1) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
candidate = var;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (active == 0) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-07-18 00:29:58 +00:00
|
|
|
if (computed_min_.Value() < new_min) {
|
2014-07-18 00:14:31 +00:00
|
|
|
if (active == 1) {
|
|
|
|
|
candidate->SetRange(new_min, new_max);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2014-07-18 00:14:31 +00:00
|
|
|
var->SetMin(new_min);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (active == 1) {
|
|
|
|
|
candidate->SetMax(new_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars_;
|
|
|
|
|
IntVar* const target_var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
Rev<int64_t> computed_min_;
|
|
|
|
|
Rev<int64_t> computed_max_;
|
2014-07-18 00:14:31 +00:00
|
|
|
};
|
|
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
// ---------- Max Array ----------
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2018-10-31 16:18:18 +01:00
|
|
|
// This constraint implements max(vars) == max_var.
|
2012-05-28 21:37:13 +00:00
|
|
|
class MaxConstraint : public TreeArrayConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
MaxConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const max_var)
|
2013-10-10 15:23:20 +00:00
|
|
|
: TreeArrayConstraint(solver, vars, max_var), max_demon_(nullptr) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~MaxConstraint() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const demon = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &MaxConstraint::LeafChanged, "LeafChanged", i);
|
2012-05-28 21:37:13 +00:00
|
|
|
vars_[i]->WhenRange(demon);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
max_demon_ = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
|
|
|
|
solver(), this, &MaxConstraint::MaxVarChanged, "MaxVarChanged"));
|
2012-05-28 21:37:13 +00:00
|
|
|
target_var_->WhenRange(max_demon_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2012-05-28 21:37:13 +00:00
|
|
|
// Copy vars to leaf nodes.
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.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
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
|
|
|
|
|
// Compute up.
|
|
|
|
|
for (int i = MaxDepth() - 1; i >= 0; --i) {
|
|
|
|
|
for (int j = 0; j < Width(i); ++j) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t max_min = std::numeric_limits<int64_t>::min();
|
|
|
|
|
int64_t max_max = std::numeric_limits<int64_t>::min();
|
2012-05-28 21:37:13 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
// Propagate to min_var.
|
|
|
|
|
target_var_->SetRange(RootMin(), RootMax());
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
// Push down.
|
|
|
|
|
MaxVarChanged();
|
2011-07-11 20:13:14 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
void MaxVarChanged() {
|
|
|
|
|
PushDown(0, 0, target_var_->Min(), target_var_->Max());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void PushDown(int depth, int position, int64_t new_min, int64_t new_max) {
|
2012-05-28 21:37:13 +00:00
|
|
|
// Nothing to do?
|
|
|
|
|
if (new_min <= Min(depth, position) && new_max >= Max(depth, position)) {
|
|
|
|
|
return;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-28 21:37:13 +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
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t node_min = Min(depth, position);
|
|
|
|
|
const int64_t node_max = Max(depth, position);
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2012-05-28 21:37:13 +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
|
|
|
|
2012-05-28 21:37:13 +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
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
if (active == 0) {
|
|
|
|
|
solver()->Fail();
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-28 21:37:13 +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
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
} else if (active == 1) {
|
|
|
|
|
PushDown(depth + 1, candidate, new_min, Max(depth + 1, candidate));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2012-05-28 21:37:13 +00:00
|
|
|
void LeafChanged(int term_index) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = vars_[term_index];
|
2012-05-28 21:37:13 +00:00
|
|
|
SetRange(MaxDepth(), term_index, var->Min(), var->Max());
|
2013-08-01 23:38:44 +00:00
|
|
|
const int parent_depth = MaxDepth() - 1;
|
|
|
|
|
const int parent = Parent(term_index);
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t old_max = var->OldMax();
|
|
|
|
|
const int64_t var_min = var->Min();
|
|
|
|
|
const int64_t var_max = var->Max();
|
2013-08-01 23:38:44 +00:00
|
|
|
if ((old_max == Max(parent_depth, parent) && old_max != var_max) ||
|
|
|
|
|
var_min > Min(parent_depth, parent)) {
|
|
|
|
|
// Can influence the parent bounds.
|
|
|
|
|
PushUp(term_index);
|
|
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
}
|
|
|
|
|
|
2012-05-31 10:53:29 +00:00
|
|
|
void PushUp(int position) {
|
2012-05-28 21:37:13 +00:00
|
|
|
int depth = MaxDepth();
|
|
|
|
|
while (depth > 0) {
|
|
|
|
|
const int parent = Parent(position);
|
|
|
|
|
const int parent_depth = depth - 1;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t max_min = std::numeric_limits<int64_t>::min();
|
|
|
|
|
int64_t max_max = std::numeric_limits<int64_t>::min();
|
2012-05-28 21:37:13 +00:00
|
|
|
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
|
|
|
}
|
2012-05-28 21:37:13 +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
|
|
|
}
|
2012-05-28 21:37:13 +00:00
|
|
|
depth = parent_depth;
|
2012-06-05 08:50:33 +00:00
|
|
|
position = parent;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
if (depth == 0) { // We have pushed all the way up.
|
2012-05-28 21:37:13 +00:00
|
|
|
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
|
|
|
}
|
|
|
|
|
|
2018-06-08 16:40:43 +02:00
|
|
|
std::string DebugString() const override {
|
|
|
|
|
return DebugStringInternal("Max");
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2012-05-28 21:37:13 +00:00
|
|
|
AcceptInternal(ModelVisitor::kMaxEqual, visitor);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* max_demon_;
|
2012-05-28 21:37:13 +00:00
|
|
|
};
|
2012-05-30 07:57:52 +00:00
|
|
|
|
2014-07-18 00:29:58 +00:00
|
|
|
class SmallMaxConstraint : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SmallMaxConstraint(Solver* const solver, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const target_var)
|
2020-10-22 23:36:58 +02:00
|
|
|
: Constraint(solver),
|
|
|
|
|
vars_(vars),
|
|
|
|
|
target_var_(target_var),
|
|
|
|
|
computed_min_(0),
|
|
|
|
|
computed_max_(0) {}
|
2014-07-18 00:29:58 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~SmallMaxConstraint() override {}
|
2014-07-18 00:29:58 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2014-07-18 00:29:58 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2014-08-15 16:56:56 +00:00
|
|
|
if (!vars_[i]->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const demon = MakeConstraintDemon1(
|
2015-08-13 16:00:54 +02:00
|
|
|
solver(), this, &SmallMaxConstraint::VarChanged, "VarChanged",
|
|
|
|
|
vars_[i]);
|
2014-08-15 16:56:56 +00:00
|
|
|
vars_[i]->WhenRange(demon);
|
|
|
|
|
}
|
2014-07-18 00:29:58 +00:00
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const mdemon = solver()->RegisterDemon(MakeDelayedConstraintDemon0(
|
2014-07-18 00:29:58 +00:00
|
|
|
solver(), this, &SmallMaxConstraint::MaxVarChanged, "MinVarChanged"));
|
|
|
|
|
target_var_->WhenRange(mdemon);
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t max_min = std::numeric_limits<int64_t>::min();
|
|
|
|
|
int64_t max_max = std::numeric_limits<int64_t>::min();
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2014-07-18 00:29:58 +00:00
|
|
|
max_min = std::max(max_min, var->Min());
|
|
|
|
|
max_max = std::max(max_max, var->Max());
|
|
|
|
|
}
|
|
|
|
|
computed_min_.SetValue(solver(), max_min);
|
|
|
|
|
computed_max_.SetValue(solver(), max_max);
|
|
|
|
|
// Propagate to min_var.
|
|
|
|
|
target_var_->SetRange(max_min, max_max);
|
|
|
|
|
|
|
|
|
|
// Push down.
|
|
|
|
|
MaxVarChanged();
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("SmallMax(%s) == %s",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
target_var_->DebugString());
|
2014-07-18 00:29:58 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2014-07-18 00:29:58 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kMaxEqual, this);
|
|
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
|
|
|
|
vars_);
|
|
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
|
|
|
|
target_var_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kMaxEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
void VarChanged(IntVar* var) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t old_max = var->OldMax();
|
|
|
|
|
const int64_t var_min = var->Min();
|
|
|
|
|
const int64_t var_max = var->Max();
|
2014-07-18 00:29:58 +00:00
|
|
|
if ((old_max == computed_max_.Value() && old_max != var_max) ||
|
2020-10-22 23:36:58 +02:00
|
|
|
var_min > computed_min_.Value()) { // REWRITE
|
2020-10-29 15:56:44 +01:00
|
|
|
// Can influence the min var bounds.
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t max_min = std::numeric_limits<int64_t>::min();
|
|
|
|
|
int64_t max_max = std::numeric_limits<int64_t>::min();
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2014-07-18 00:29:58 +00:00
|
|
|
max_min = std::max(max_min, var->Min());
|
|
|
|
|
max_max = std::max(max_max, var->Max());
|
|
|
|
|
}
|
2014-08-15 16:56:56 +00:00
|
|
|
if (max_min > computed_min_.Value() || max_max < computed_max_.Value()) {
|
2014-07-18 00:29:58 +00:00
|
|
|
computed_min_.SetValue(solver(), max_min);
|
|
|
|
|
computed_max_.SetValue(solver(), max_max);
|
|
|
|
|
target_var_->SetRange(computed_min_.Value(), computed_max_.Value());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
MaxVarChanged();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void MaxVarChanged() {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t new_min = target_var_->Min();
|
|
|
|
|
const int64_t new_max = target_var_->Max();
|
2014-07-18 00:29:58 +00:00
|
|
|
// Nothing to do?
|
2014-08-15 16:56:56 +00:00
|
|
|
if (new_min <= computed_min_.Value() && new_max >= computed_max_.Value()) {
|
2014-07-18 00:29:58 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* candidate = nullptr;
|
2014-07-18 00:29:58 +00:00
|
|
|
int active = 0;
|
|
|
|
|
|
|
|
|
|
if (new_min > computed_min_.Value()) {
|
|
|
|
|
// Look if only one candidate to push the max down.
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2014-07-18 00:29:58 +00:00
|
|
|
if (var->Max() >= new_min) {
|
|
|
|
|
if (active++ >= 1) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
candidate = var;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (active == 0) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (computed_max_.Value() > new_max) {
|
|
|
|
|
if (active == 1) {
|
|
|
|
|
candidate->SetRange(new_min, new_max);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
for (IntVar* const var : vars_) {
|
2014-07-18 00:29:58 +00:00
|
|
|
var->SetMax(new_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else if (active == 1) {
|
|
|
|
|
candidate->SetMin(new_min);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars_;
|
|
|
|
|
IntVar* const target_var_;
|
2021-04-01 12:13:35 +02:00
|
|
|
Rev<int64_t> computed_min_;
|
|
|
|
|
Rev<int64_t> computed_max_;
|
2014-07-18 00:29:58 +00:00
|
|
|
};
|
|
|
|
|
|
2012-06-30 15:12:14 +00:00
|
|
|
// Boolean And and Ors
|
|
|
|
|
|
|
|
|
|
class ArrayBoolAndEq : public CastConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
ArrayBoolAndEq(Solver* const s, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const target)
|
2020-10-22 23:36:58 +02:00
|
|
|
: CastConstraint(s, target),
|
|
|
|
|
vars_(vars),
|
|
|
|
|
demons_(vars.size()),
|
2012-06-30 15:12:14 +00:00
|
|
|
unbounded_(0) {}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~ArrayBoolAndEq() override {}
|
2012-06-30 15:12:14 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2012-06-30 15:12:14 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
|
|
|
|
if (!vars_[i]->Bound()) {
|
2015-08-13 16:00:54 +02:00
|
|
|
demons_[i] =
|
|
|
|
|
MakeConstraintDemon1(solver(), this, &ArrayBoolAndEq::PropagateVar,
|
|
|
|
|
"PropagateVar", vars_[i]);
|
2012-06-30 15:12:14 +00:00
|
|
|
vars_[i]->WhenBound(demons_[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
if (!target_var_->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const target_demon = MakeConstraintDemon0(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &ArrayBoolAndEq::PropagateTarget, "PropagateTarget");
|
2013-06-18 09:43:51 +00:00
|
|
|
target_var_->WhenBound(target_demon);
|
|
|
|
|
}
|
2012-06-30 15:12:14 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2012-06-30 15:12:14 +00:00
|
|
|
target_var_->SetRange(0, 1);
|
|
|
|
|
if (target_var_->Min() == 1) {
|
|
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
|
|
|
|
vars_[i]->SetMin(1);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
2013-06-18 09:43:51 +00:00
|
|
|
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) {
|
2013-06-18 09:43:51 +00:00
|
|
|
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
|
|
|
}
|
2013-06-18 09:43:51 +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) {
|
2013-06-18 09:43:51 +00:00
|
|
|
CHECK_NE(-1, possible_zero);
|
|
|
|
|
vars_[possible_zero]->SetMax(0);
|
2012-06-30 15:12:14 +00:00
|
|
|
} else {
|
|
|
|
|
unbounded_.SetValue(solver(), unbounded);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void PropagateVar(IntVar* var) {
|
2015-08-13 16:00:54 +02:00
|
|
|
if (var->Min() == 1) {
|
2012-06-30 15:12:14 +00:00
|
|
|
unbounded_.Decr(solver());
|
2012-06-30 21:49:51 +00:00
|
|
|
if (unbounded_.Value() == 0 && !decided_.Switched()) {
|
|
|
|
|
target_var_->SetMin(1);
|
|
|
|
|
decided_.Switch(solver());
|
2013-07-24 00:28:11 +00:00
|
|
|
} else if (target_var_->Max() == 0 && unbounded_.Value() == 1 &&
|
2013-06-18 09:43:51 +00:00
|
|
|
!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()) {
|
2013-06-18 09:43:51 +00:00
|
|
|
ForceToZero();
|
2012-06-30 15:12:14 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("And(%s) == %s", JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
target_var_->DebugString());
|
2012-06-30 15:12:14 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2012-06-30 15:12:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kMinEqual, this);
|
|
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2012-06-30 15:12:14 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
|
|
|
|
target_var_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kMinEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2013-06-18 09:43:51 +00:00
|
|
|
void InhibitAll() {
|
|
|
|
|
for (int i = 0; i < demons_.size(); ++i) {
|
2013-10-10 15:23:20 +00:00
|
|
|
if (demons_[i] != nullptr) {
|
2013-06-18 09:43:51 +00:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
const std::vector<IntVar*> vars_;
|
|
|
|
|
std::vector<Demon*> demons_;
|
2012-06-30 15:12:14 +00:00
|
|
|
NumericalRev<int> unbounded_;
|
|
|
|
|
RevSwitch decided_;
|
|
|
|
|
};
|
|
|
|
|
|
2012-06-30 15:28:09 +00:00
|
|
|
class ArrayBoolOrEq : public CastConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
ArrayBoolOrEq(Solver* const s, const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const target)
|
2020-10-22 23:36:58 +02:00
|
|
|
: CastConstraint(s, target),
|
|
|
|
|
vars_(vars),
|
|
|
|
|
demons_(vars.size()),
|
2012-06-30 15:28:09 +00:00
|
|
|
unbounded_(0) {}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~ArrayBoolOrEq() override {}
|
2012-06-30 15:28:09 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2012-06-30 15:28:09 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
|
|
|
|
if (!vars_[i]->Bound()) {
|
2015-08-13 16:00:54 +02:00
|
|
|
demons_[i] =
|
|
|
|
|
MakeConstraintDemon1(solver(), this, &ArrayBoolOrEq::PropagateVar,
|
|
|
|
|
"PropagateVar", vars_[i]);
|
2012-06-30 15:28:09 +00:00
|
|
|
vars_[i]->WhenBound(demons_[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
if (!target_var_->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const target_demon = MakeConstraintDemon0(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &ArrayBoolOrEq::PropagateTarget, "PropagateTarget");
|
2013-06-18 09:43:51 +00:00
|
|
|
target_var_->WhenBound(target_demon);
|
|
|
|
|
}
|
2012-06-30 15:28:09 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2012-06-30 15:28:09 +00:00
|
|
|
target_var_->SetRange(0, 1);
|
2012-06-30 19:35:22 +00:00
|
|
|
if (target_var_->Max() == 0) {
|
2012-06-30 15:28:09 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
|
|
|
|
vars_[i]->SetMax(0);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
int zeros = 0;
|
2013-06-18 09:43:51 +00:00
|
|
|
int possible_one = -1;
|
2012-06-30 15:28:09 +00:00
|
|
|
int unbounded = 0;
|
|
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2013-06-18 09:43:51 +00:00
|
|
|
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++;
|
|
|
|
|
}
|
2012-06-30 15:28:09 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
if (unbounded == 0) {
|
2012-06-30 15:28:09 +00:00
|
|
|
target_var_->SetMax(0);
|
|
|
|
|
} else if (target_var_->Min() == 1 && unbounded == 1) {
|
2013-06-18 09:43:51 +00:00
|
|
|
CHECK_NE(-1, possible_one);
|
|
|
|
|
vars_[possible_one]->SetMin(1);
|
2012-06-30 15:28:09 +00:00
|
|
|
} else {
|
|
|
|
|
unbounded_.SetValue(solver(), unbounded);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void PropagateVar(IntVar* var) {
|
2015-08-13 16:00:54 +02:00
|
|
|
if (var->Min() == 0) {
|
2012-06-30 15:28:09 +00:00
|
|
|
unbounded_.Decr(solver());
|
2012-06-30 21:49:51 +00:00
|
|
|
if (unbounded_.Value() == 0 && !decided_.Switched()) {
|
|
|
|
|
target_var_->SetMax(0);
|
|
|
|
|
decided_.Switch(solver());
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
if (target_var_->Min() == 1 && unbounded_.Value() == 1 &&
|
2012-06-30 15:28:09 +00:00
|
|
|
!decided_.Switched()) {
|
2013-06-18 09:43:51 +00:00
|
|
|
ForceToOne();
|
2012-06-30 15:28:09 +00:00
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
InhibitAll();
|
|
|
|
|
target_var_->SetMin(1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PropagateTarget() {
|
2012-06-30 20:24:22 +00:00
|
|
|
if (target_var_->Max() == 0) {
|
2012-06-30 15:28:09 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
|
|
|
|
vars_[i]->SetMax(0);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (unbounded_.Value() == 1 && !decided_.Switched()) {
|
2013-06-18 09:43:51 +00:00
|
|
|
ForceToOne();
|
2012-06-30 15:28:09 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("Or(%s) == %s", JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
target_var_->DebugString());
|
2012-06-30 15:28:09 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2012-06-30 15:28:09 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kMaxEqual, this);
|
|
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2012-06-30 15:28:09 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
|
|
|
|
target_var_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kMaxEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2013-06-18 09:43:51 +00:00
|
|
|
void InhibitAll() {
|
|
|
|
|
for (int i = 0; i < demons_.size(); ++i) {
|
2013-10-10 15:23:20 +00:00
|
|
|
if (demons_[i] != nullptr) {
|
2013-06-18 09:43:51 +00:00
|
|
|
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();
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
const std::vector<IntVar*> vars_;
|
|
|
|
|
std::vector<Demon*> demons_;
|
2012-06-30 15:28:09 +00:00
|
|
|
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 {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
BaseSumBooleanConstraint(Solver* const s, const std::vector<IntVar*>& vars)
|
2013-10-10 15:23:20 +00:00
|
|
|
: Constraint(s), vars_(vars) {}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~BaseSumBooleanConstraint() override {}
|
2011-12-16 21:02:59 +00:00
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
protected:
|
2020-10-29 14:25:39 +01:00
|
|
|
std::string DebugStringInternal(const std::string& name) const {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("%s(%s)", name, JoinDebugStringPtr(vars_, ", "));
|
2013-10-10 15:23:20 +00:00
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
const std::vector<IntVar*> vars_;
|
2011-12-16 21:02:59 +00:00
|
|
|
RevSwitch inactive_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ----- Sum of Boolean <= 1 -----
|
|
|
|
|
|
2013-07-24 00:28:11 +00:00
|
|
|
class SumBooleanLessOrEqualToOne : public BaseSumBooleanConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SumBooleanLessOrEqualToOne(Solver* const s, const std::vector<IntVar*>& vars)
|
2013-10-10 15:23:20 +00:00
|
|
|
: BaseSumBooleanConstraint(s, vars) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~SumBooleanLessOrEqualToOne() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (!vars_[i]->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* u = MakeConstraintDemon1(solver(), this,
|
2020-10-22 23:36:58 +02:00
|
|
|
&SumBooleanLessOrEqualToOne::Update,
|
|
|
|
|
"Update", vars_[i]);
|
2010-09-15 12:42:33 +00:00
|
|
|
vars_[i]->WhenBound(u);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (vars_[i]->Min() == 1) {
|
2015-08-13 16:00:54 +02:00
|
|
|
PushAllToZeroExcept(vars_[i]);
|
2010-09-15 12:42:33 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Update(IntVar* var) {
|
2011-12-16 21:02:59 +00:00
|
|
|
if (!inactive_.Switched()) {
|
2015-08-13 16:00:54 +02:00
|
|
|
DCHECK(var->Bound());
|
|
|
|
|
if (var->Min() == 1) {
|
|
|
|
|
PushAllToZeroExcept(var);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void PushAllToZeroExcept(IntVar* var) {
|
2011-12-16 21:02:59 +00:00
|
|
|
inactive_.Switch(solver());
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const other = vars_[i];
|
2015-08-13 16:00:54 +02:00
|
|
|
if (other != var && other->Max() != 0) {
|
|
|
|
|
other->SetMax(0);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2010-09-15 12:42:33 +00:00
|
|
|
return DebugStringInternal("SumBooleanLessOrEqualToOne");
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kSumLessOrEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2011-07-14 23:37:47 +00:00
|
|
|
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 {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SumBooleanGreaterOrEqualToOne(Solver* const s,
|
|
|
|
|
const std::vector<IntVar*>& vars);
|
2015-04-16 15:49:51 +02:00
|
|
|
~SumBooleanGreaterOrEqualToOne() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override;
|
|
|
|
|
void InitialPropagate() override;
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
void Update(int index);
|
|
|
|
|
void UpdateVar();
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override;
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kSumGreaterOrEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, 1);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kSumGreaterOrEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2010-09-15 12:42:33 +00:00
|
|
|
RevBitSet bits_;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
SumBooleanGreaterOrEqualToOne::SumBooleanGreaterOrEqualToOne(
|
2020-10-29 14:25:39 +01:00
|
|
|
Solver* const s, const std::vector<IntVar*>& vars)
|
2013-10-10 15:23:20 +00:00
|
|
|
: BaseSumBooleanConstraint(s, vars), bits_(vars.size()) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
|
|
|
|
void SumBooleanGreaterOrEqualToOne::Post() {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* d = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &SumBooleanGreaterOrEqualToOne::Update, "Update", i);
|
2010-09-15 12:42:33 +00:00
|
|
|
vars_[i]->WhenRange(d);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SumBooleanGreaterOrEqualToOne::InitialPropagate() {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = vars_[i];
|
2010-09-15 12:42:33 +00:00
|
|
|
if (var->Min() == 1LL) {
|
2011-12-16 21:02:59 +00:00
|
|
|
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()) {
|
2021-04-01 12:13:35 +02:00
|
|
|
vars_[bits_.GetFirstBit(0)]->SetValue(int64_t{1});
|
2011-12-16 21:02:59 +00:00
|
|
|
inactive_.Switch(solver());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void SumBooleanGreaterOrEqualToOne::Update(int index) {
|
2011-12-16 21:02:59 +00:00
|
|
|
if (!inactive_.Switched()) {
|
2020-10-22 23:36:58 +02:00
|
|
|
if (vars_[index]->Min() == 1LL) { // Bound to 1.
|
2011-12-16 21:02:59 +00:00
|
|
|
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()) {
|
2021-04-01 12:13:35 +02:00
|
|
|
vars_[bits_.GetFirstBit(0)]->SetValue(int64_t{1});
|
2011-12-16 21:02:59 +00:00
|
|
|
inactive_.Switch(solver());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2013-12-16 10:24:42 +00:00
|
|
|
std::string SumBooleanGreaterOrEqualToOne::DebugString() const {
|
2010-09-15 12:42:33 +00:00
|
|
|
return DebugStringInternal("SumBooleanGreaterOrEqualToOne");
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ----- Sum of Boolean == 1 -----
|
|
|
|
|
|
|
|
|
|
class SumBooleanEqualToOne : public BaseSumBooleanConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SumBooleanEqualToOne(Solver* const s, const std::vector<IntVar*>& vars)
|
2013-10-10 15:23:20 +00:00
|
|
|
: BaseSumBooleanConstraint(s, vars), active_vars_(0) {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~SumBooleanEqualToOne() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* u = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &SumBooleanEqualToOne::Update, "Update", i);
|
2010-09-15 12:42:33 +00:00
|
|
|
vars_[i]->WhenBound(u);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2010-09-15 12:42:33 +00:00
|
|
|
int min1 = 0;
|
|
|
|
|
int max1 = 0;
|
|
|
|
|
int index_min = -1;
|
|
|
|
|
int index_max = -1;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntVar* const var = vars_[i];
|
2010-09-15 12:42:33 +00:00
|
|
|
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);
|
2011-12-16 21:02:59 +00:00
|
|
|
inactive_.Switch(solver());
|
2010-09-15 12:42:33 +00:00
|
|
|
} else {
|
2011-12-16 21:02:59 +00:00
|
|
|
active_vars_.SetValue(solver(), max1);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Update(int index) {
|
2011-12-16 21:02:59 +00:00
|
|
|
if (!inactive_.Switched()) {
|
2010-09-15 12:42:33 +00:00
|
|
|
DCHECK(vars_[index]->Bound());
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t value = vars_[index]->Min(); // Faster than Value().
|
2010-09-15 12:42:33 +00:00
|
|
|
if (value == 0) {
|
2011-12-16 21:02:59 +00:00
|
|
|
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();
|
2011-12-16 21:02:59 +00:00
|
|
|
} else if (active_vars_.Value() == 1) {
|
2010-09-15 12:42:33 +00:00
|
|
|
bool found = false;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = vars_[i];
|
2010-09-15 12:42:33 +00:00
|
|
|
if (var->Max() == 1) {
|
|
|
|
|
var->SetValue(1);
|
|
|
|
|
PushAllToZeroExcept(i);
|
|
|
|
|
found = true;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!found) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
PushAllToZeroExcept(index);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PushAllToZeroExcept(int index) {
|
2011-12-16 21:02:59 +00:00
|
|
|
inactive_.Switch(solver());
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (i != index && vars_[i]->Max() != 0) {
|
|
|
|
|
vars_[i]->SetMax(0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2010-09-15 12:42:33 +00:00
|
|
|
return DebugStringInternal("SumBooleanEqualToOne");
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, 1);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2011-12-16 21:02:59 +00:00
|
|
|
NumericalRev<int> active_vars_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2011-02-18 10:42:57 +00:00
|
|
|
// ----- Sum of Boolean Equal To Var -----
|
|
|
|
|
|
|
|
|
|
class SumBooleanEqualToVar : public BaseSumBooleanConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
SumBooleanEqualToVar(Solver* const s, const std::vector<IntVar*>& bool_vars,
|
|
|
|
|
IntVar* const sum_var)
|
2020-10-22 23:36:58 +02:00
|
|
|
: BaseSumBooleanConstraint(s, bool_vars),
|
|
|
|
|
num_possible_true_vars_(0),
|
|
|
|
|
num_always_true_vars_(0),
|
|
|
|
|
sum_var_(sum_var) {}
|
2011-02-18 10:42:57 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~SumBooleanEqualToVar() override {}
|
2011-02-18 10:42:57 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const u = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &SumBooleanEqualToVar::Update, "Update", i);
|
2011-02-18 10:42:57 +00:00
|
|
|
vars_[i]->WhenBound(u);
|
|
|
|
|
}
|
|
|
|
|
if (!sum_var_->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const u = MakeConstraintDemon0(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &SumBooleanEqualToVar::UpdateVar, "UpdateVar");
|
2011-02-18 10:42:57 +00:00
|
|
|
sum_var_->WhenRange(u);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2011-02-18 10:42:57 +00:00
|
|
|
int num_always_true_vars = 0;
|
|
|
|
|
int possible_true = 0;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntVar* const var = vars_[i];
|
2011-02-18 10:42:57 +00:00
|
|
|
if (var->Min() == 1) {
|
|
|
|
|
num_always_true_vars++;
|
|
|
|
|
}
|
|
|
|
|
if (var->Max() == 1) {
|
|
|
|
|
possible_true++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
sum_var_->SetRange(num_always_true_vars, possible_true);
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t var_min = sum_var_->Min();
|
|
|
|
|
const int64_t var_max = sum_var_->Max();
|
2011-02-18 10:42:57 +00:00
|
|
|
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 {
|
2011-12-16 21:02:59 +00:00
|
|
|
num_possible_true_vars_.SetValue(solver(), possible_true);
|
|
|
|
|
num_always_true_vars_.SetValue(solver(), num_always_true_vars);
|
2011-02-18 10:42:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
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());
|
|
|
|
|
}
|
2011-02-18 10:42:57 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Update(int index) {
|
2011-12-16 21:02:59 +00:00
|
|
|
if (!inactive_.Switched()) {
|
2011-02-18 10:42:57 +00:00
|
|
|
DCHECK(vars_[index]->Bound());
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t value = vars_[index]->Min(); // Faster than Value().
|
2011-02-18 10:42:57 +00:00
|
|
|
if (value == 0) {
|
2011-12-16 21:02:59 +00:00
|
|
|
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());
|
2011-12-16 21:02:59 +00:00
|
|
|
if (num_possible_true_vars_.Value() == sum_var_->Min()) {
|
2011-02-18 10:42:57 +00:00
|
|
|
PushAllUnboundToOne();
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
DCHECK_EQ(1, value);
|
2011-12-16 21:02:59 +00:00
|
|
|
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());
|
2011-12-16 21:02:59 +00:00
|
|
|
if (num_always_true_vars_.Value() == sum_var_->Max()) {
|
2011-02-18 10:42:57 +00:00
|
|
|
PushAllUnboundToZero();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PushAllUnboundToZero() {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t counter = 0;
|
2011-12-16 21:02:59 +00:00
|
|
|
inactive_.Switch(solver());
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2011-02-18 10:42:57 +00:00
|
|
|
if (vars_[i]->Min() == 0) {
|
|
|
|
|
vars_[i]->SetValue(0);
|
|
|
|
|
} else {
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (counter < sum_var_->Min() || counter > sum_var_->Max()) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PushAllUnboundToOne() {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t counter = 0;
|
2011-12-16 21:02:59 +00:00
|
|
|
inactive_.Switch(solver());
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2011-02-18 10:42:57 +00:00
|
|
|
if (vars_[i]->Max() == 1) {
|
|
|
|
|
vars_[i]->SetValue(1);
|
|
|
|
|
counter++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (counter < sum_var_->Min() || counter > sum_var_->Max()) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("%s == %s", DebugStringInternal("SumBoolean"),
|
|
|
|
|
sum_var_->DebugString());
|
2011-02-18 10:42:57 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kSumEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
2011-07-11 20:13:14 +00:00
|
|
|
sum_var_);
|
|
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kSumEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2011-12-16 21:02:59 +00:00
|
|
|
NumericalRev<int> num_possible_true_vars_;
|
|
|
|
|
NumericalRev<int> num_always_true_vars_;
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const sum_var_;
|
2011-02-18 10:42:57 +00:00
|
|
|
};
|
|
|
|
|
|
2010-09-15 12:42:33 +00:00
|
|
|
// ---------- ScalProd ----------
|
|
|
|
|
|
|
|
|
|
// ----- Boolean Scal Prod -----
|
|
|
|
|
|
|
|
|
|
struct Container {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* var;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t coef;
|
|
|
|
|
Container(IntVar* v, int64_t c) : var(v), coef(c) {}
|
2020-10-29 14:25:39 +01:00
|
|
|
bool operator<(const Container& c) const { return (coef < c.coef); }
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2013-12-11 16:02:19 +00:00
|
|
|
// This method will sort both vars and coefficients in increasing
|
2010-09-15 12:42:33 +00:00
|
|
|
// 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.
|
2011-08-11 05:15:18 +00:00
|
|
|
// If keep_inside is true, the constant will be added back into the
|
|
|
|
|
// scalprod as IntConst(1) * constant.
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t SortBothChangeConstant(std::vector<IntVar*>* const vars,
|
|
|
|
|
std::vector<int64_t>* const coefs,
|
|
|
|
|
bool keep_inside) {
|
2013-10-10 15:23:20 +00:00
|
|
|
CHECK(vars != nullptr);
|
|
|
|
|
CHECK(coefs != nullptr);
|
|
|
|
|
if (vars->empty()) {
|
2011-08-11 05:15:18 +00:00
|
|
|
return 0;
|
|
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst = 0;
|
2011-05-17 20:38:55 +00:00
|
|
|
std::vector<Container> to_sort;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int index = 0; index < vars->size(); ++index) {
|
|
|
|
|
if ((*vars)[index]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
cst = CapAdd(cst, CapProd((*coefs)[index], (*vars)[index]->Min()));
|
2013-10-10 15:23:20 +00:00
|
|
|
} else if ((*coefs)[index] != 0) {
|
|
|
|
|
to_sort.push_back(Container((*vars)[index], (*coefs)[index]));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2011-08-11 05:15:18 +00:00
|
|
|
if (keep_inside && cst != 0) {
|
2013-10-10 15:23:20 +00:00
|
|
|
CHECK_LT(to_sort.size(), vars->size());
|
2020-10-29 14:25:39 +01:00
|
|
|
Solver* const solver = (*vars)[0]->solver();
|
2011-08-11 05:15:18 +00:00
|
|
|
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());
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int index = 0; index < to_sort.size(); ++index) {
|
|
|
|
|
(*vars)[index] = to_sort[index].var;
|
|
|
|
|
(*coefs)[index] = to_sort[index].coef;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2013-10-10 15:23:20 +00:00
|
|
|
vars->resize(to_sort.size());
|
|
|
|
|
coefs->resize(to_sort.size());
|
2010-09-15 12:42:33 +00:00
|
|
|
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 {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
BooleanScalProdLessConstant(Solver* const s, const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& coefs,
|
|
|
|
|
int64_t upper_bound)
|
2020-10-22 23:36:58 +02:00
|
|
|
: Constraint(s),
|
|
|
|
|
vars_(vars),
|
|
|
|
|
coefs_(coefs),
|
|
|
|
|
upper_bound_(upper_bound),
|
|
|
|
|
first_unbound_backward_(vars.size() - 1),
|
|
|
|
|
sum_of_bound_variables_(0LL),
|
2010-09-15 12:42:33 +00:00
|
|
|
max_coefficient_(0) {
|
2013-10-10 15:23:20 +00:00
|
|
|
CHECK(!vars.empty());
|
|
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2010-09-15 12:42:33 +00:00
|
|
|
DCHECK_GE(coefs_[i], 0);
|
|
|
|
|
}
|
2015-10-23 13:45:43 +02:00
|
|
|
upper_bound_ =
|
|
|
|
|
CapSub(upper_bound, SortBothChangeConstant(&vars_, &coefs_, false));
|
2013-10-10 15:23:20 +00:00
|
|
|
max_coefficient_.SetValue(s, coefs_[vars_.size() - 1]);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~BooleanScalProdLessConstant() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int var_index = 0; var_index < vars_.size(); ++var_index) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (vars_[var_index]->Bound()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* d = MakeConstraintDemon1(solver(), this,
|
2013-07-24 00:28:11 +00:00
|
|
|
&BooleanScalProdLessConstant::Update,
|
|
|
|
|
"InitialPropagate", var_index);
|
2010-09-15 12:42:33 +00:00
|
|
|
vars_[var_index]->WhenRange(d);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void PushFromTop() {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t slack = CapSub(upper_bound_, sum_of_bound_variables_.Value());
|
2010-09-15 12:42:33 +00:00
|
|
|
if (slack < 0) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
if (slack < max_coefficient_.Value()) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t last_unbound = first_unbound_backward_.Value();
|
2011-05-30 14:27:57 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2020-10-29 14:25:39 +01:00
|
|
|
Solver* const s = solver();
|
2010-09-15 12:42:33 +00:00
|
|
|
int last_unbound = -1;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum = 0LL;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int index = 0; index < vars_.size(); ++index) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (vars_[index]->Bound()) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t value = vars_[index]->Min();
|
2015-10-23 13:45:43 +02:00
|
|
|
sum = CapAdd(sum, CapProd(value, coefs_[index]));
|
2010-09-15 12:42:33 +00:00
|
|
|
} 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(
|
2015-10-23 13:45:43 +02:00
|
|
|
solver(), CapAdd(sum_of_bound_variables_.Value(), coefs_[var_index]));
|
2010-09-15 12:42:33 +00:00
|
|
|
PushFromTop();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("BooleanScalProd([%s], [%s]) <= %d)",
|
2018-10-31 16:18:18 +01:00
|
|
|
JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
absl::StrJoin(coefs_, ", "), upper_bound_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kScalProdLessOrEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
coefs_);
|
2013-07-24 00:28:11 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, upper_bound_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kScalProdLessOrEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars_;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs_;
|
|
|
|
|
int64_t upper_bound_;
|
2010-09-15 12:42:33 +00:00
|
|
|
Rev<int> first_unbound_backward_;
|
2021-04-01 12:13:35 +02:00
|
|
|
Rev<int64_t> sum_of_bound_variables_;
|
|
|
|
|
Rev<int64_t> max_coefficient_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ----- PositiveBooleanScalProdEqVar -----
|
|
|
|
|
|
2011-11-07 15:31:18 +00:00
|
|
|
class PositiveBooleanScalProdEqVar : public CastConstraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
PositiveBooleanScalProdEqVar(Solver* const s,
|
|
|
|
|
const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& coefs,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var)
|
2020-10-22 23:36:58 +02:00
|
|
|
: CastConstraint(s, var),
|
|
|
|
|
vars_(vars),
|
|
|
|
|
coefs_(coefs),
|
|
|
|
|
first_unbound_backward_(vars.size() - 1),
|
|
|
|
|
sum_of_bound_variables_(0LL),
|
|
|
|
|
sum_of_all_variables_(0LL),
|
|
|
|
|
max_coefficient_(0) {
|
2013-10-10 15:23:20 +00:00
|
|
|
SortBothChangeConstant(&vars_, &coefs_, true);
|
|
|
|
|
max_coefficient_.SetValue(s, coefs_[vars_.size() - 1]);
|
2012-06-18 15:05:28 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~PositiveBooleanScalProdEqVar() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int var_index = 0; var_index < vars_.size(); ++var_index) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (vars_[var_index]->Bound()) {
|
|
|
|
|
continue;
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const d = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &PositiveBooleanScalProdEqVar::Update, "Update",
|
|
|
|
|
var_index);
|
2010-09-15 12:42:33 +00:00
|
|
|
vars_[var_index]->WhenRange(d);
|
|
|
|
|
}
|
2011-11-07 15:31:18 +00:00
|
|
|
if (!target_var_->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const uv = MakeConstraintDemon0(
|
2013-07-24 00:28:11 +00:00
|
|
|
solver(), this, &PositiveBooleanScalProdEqVar::Propagate,
|
|
|
|
|
"Propagate");
|
2011-11-07 15:31:18 +00:00
|
|
|
target_var_->WhenRange(uv);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void Propagate() {
|
2011-11-07 15:31:18 +00:00
|
|
|
target_var_->SetRange(sum_of_bound_variables_.Value(),
|
|
|
|
|
sum_of_all_variables_.Value());
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t slack_up =
|
2015-10-23 13:45:43 +02:00
|
|
|
CapSub(target_var_->Max(), sum_of_bound_variables_.Value());
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t slack_down =
|
2015-10-23 13:45:43 +02:00
|
|
|
CapSub(sum_of_all_variables_.Value(), target_var_->Min());
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t max_coeff = max_coefficient_.Value();
|
2010-09-15 12:42:33 +00:00
|
|
|
if (slack_down < max_coeff || slack_up < max_coeff) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t last_unbound = first_unbound_backward_.Value();
|
2010-09-15 12:42:33 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2020-10-29 14:25:39 +01:00
|
|
|
Solver* const s = solver();
|
2010-09-15 12:42:33 +00:00
|
|
|
int last_unbound = -1;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum_bound = 0;
|
|
|
|
|
int64_t sum_all = 0;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int index = 0; index < vars_.size(); ++index) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t value = CapProd(vars_[index]->Max(), coefs_[index]);
|
2015-10-23 13:45:43 +02:00
|
|
|
sum_all = CapAdd(sum_all, value);
|
2010-09-15 12:42:33 +00:00
|
|
|
if (vars_[index]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
sum_bound = CapAdd(sum_bound, value);
|
2010-09-15 12:42:33 +00:00
|
|
|
} 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(
|
2015-10-23 13:45:43 +02:00
|
|
|
solver(), CapAdd(sum_of_bound_variables_.Value(), coefs_[var_index]));
|
2010-09-15 12:42:33 +00:00
|
|
|
} else {
|
|
|
|
|
sum_of_all_variables_.SetValue(
|
2015-10-23 13:45:43 +02:00
|
|
|
solver(), CapSub(sum_of_all_variables_.Value(), coefs_[var_index]));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
Propagate();
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("PositiveBooleanScal([%s], [%s]) == %s",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
absl::StrJoin(coefs_, ", "),
|
|
|
|
|
target_var_->DebugString());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kScalProdEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
coefs_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerExpressionArgument(ModelVisitor::kTargetArgument,
|
2011-11-07 15:31:18 +00:00
|
|
|
target_var_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kScalProdEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars_;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs_;
|
2010-09-15 12:42:33 +00:00
|
|
|
Rev<int> first_unbound_backward_;
|
2021-04-01 12:13:35 +02:00
|
|
|
Rev<int64_t> sum_of_bound_variables_;
|
|
|
|
|
Rev<int64_t> sum_of_all_variables_;
|
|
|
|
|
Rev<int64_t> max_coefficient_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ----- PositiveBooleanScalProd -----
|
|
|
|
|
|
|
|
|
|
class PositiveBooleanScalProd : public BaseIntExpr {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2010-09-15 12:42:33 +00:00
|
|
|
// this constructor will copy the array. The caller can safely delete the
|
|
|
|
|
// exprs array himself
|
2020-10-29 14:25:39 +01:00
|
|
|
PositiveBooleanScalProd(Solver* const s, const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& coefs)
|
2013-10-10 15:23:20 +00:00
|
|
|
: BaseIntExpr(s), vars_(vars), coefs_(coefs) {
|
|
|
|
|
CHECK(!vars.empty());
|
|
|
|
|
SortBothChangeConstant(&vars_, &coefs_, true);
|
|
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2010-09-15 12:42:33 +00:00
|
|
|
DCHECK_GE(coefs_[i], 0);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~PositiveBooleanScalProd() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t Min() const override {
|
|
|
|
|
int64_t min = 0;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (vars_[i]->Min()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
min = CapAdd(min, coefs_[i]);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2011-08-11 05:15:18 +00:00
|
|
|
return min;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void SetMin(int64_t m) override {
|
|
|
|
|
SetRange(m, std::numeric_limits<int64_t>::max());
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t Max() const override {
|
|
|
|
|
int64_t max = 0;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (vars_[i]->Max()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
max = CapAdd(max, coefs_[i]);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
2011-08-11 05:15:18 +00:00
|
|
|
return max;
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void SetMax(int64_t m) override {
|
|
|
|
|
SetRange(std::numeric_limits<int64_t>::min(), m);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void SetRange(int64_t l, int64_t u) override {
|
|
|
|
|
int64_t current_min = 0;
|
|
|
|
|
int64_t current_max = 0;
|
|
|
|
|
int64_t diameter = -1;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t coefficient = coefs_[i];
|
|
|
|
|
const int64_t var_min = CapProd(vars_[i]->Min(), coefficient);
|
|
|
|
|
const int64_t var_max = CapProd(vars_[i]->Max(), coefficient);
|
2015-10-23 13:45:43 +02:00
|
|
|
current_min = CapAdd(current_min, var_min);
|
|
|
|
|
current_max = CapAdd(current_max, var_max);
|
2020-10-22 23:36:58 +02:00
|
|
|
if (var_min != var_max) { // Coefficients are increasing.
|
2015-10-23 13:45:43 +02:00
|
|
|
diameter = CapSub(var_max, var_min);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
|
2015-10-23 13:45:43 +02:00
|
|
|
if (CapSub(u, l) > diameter) {
|
2010-09-15 12:42:33 +00:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t coefficient = coefs_[i];
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = vars_[i];
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t new_min =
|
2015-10-23 13:45:43 +02:00
|
|
|
CapAdd(CapSub(l, current_max), CapProd(var->Max(), coefficient));
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t new_max =
|
2015-10-23 13:45:43 +02:00
|
|
|
CapAdd(CapSub(u, current_min), CapProd(var->Min(), coefficient));
|
2010-09-15 12:42:33 +00:00
|
|
|
if (new_max < 0 || new_min > coefficient || new_min > new_max) {
|
|
|
|
|
solver()->Fail();
|
|
|
|
|
}
|
|
|
|
|
if (new_min > 0LL) {
|
2021-04-01 12:13:35 +02:00
|
|
|
var->SetMin(int64_t{1});
|
2010-09-15 12:42:33 +00:00
|
|
|
} else if (new_max < coefficient) {
|
2021-04-01 12:13:35 +02:00
|
|
|
var->SetMax(int64_t{0});
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-10-31 16:18:18 +01:00
|
|
|
return absl::StrFormat("PositiveBooleanScalProd([%s], [%s])",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
absl::StrJoin(coefs_, ", "));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void WhenRange(Demon* d) override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int i = 0; i < vars_.size(); ++i) {
|
2010-09-15 12:42:33 +00:00
|
|
|
vars_[i]->WhenRange(d);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* CastToVar() override {
|
|
|
|
|
Solver* const s = solver();
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t vmin = 0LL;
|
|
|
|
|
int64_t vmax = 0LL;
|
2010-09-15 12:42:33 +00:00
|
|
|
Range(&vmin, &vmax);
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const var = solver()->MakeIntVar(vmin, vmax);
|
2017-04-19 16:20:56 +02:00
|
|
|
if (!vars_.empty()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
CastConstraint* const ct =
|
2013-10-10 15:23:20 +00:00
|
|
|
s->RevAlloc(new PositiveBooleanScalProdEqVar(s, vars_, coefs_, var));
|
2011-11-07 15:31:18 +00:00
|
|
|
s->AddCastConstraint(ct, var, this);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
return var;
|
|
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitIntegerExpression(ModelVisitor::kScalProd, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
coefs_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitIntegerExpression(ModelVisitor::kScalProd, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars_;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ----- PositiveBooleanScalProdEqCst ----- (all constants >= 0)
|
|
|
|
|
|
|
|
|
|
class PositiveBooleanScalProdEqCst : public Constraint {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2020-10-29 14:25:39 +01:00
|
|
|
PositiveBooleanScalProdEqCst(Solver* const s,
|
|
|
|
|
const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& coefs,
|
|
|
|
|
int64_t constant)
|
2020-10-22 23:36:58 +02:00
|
|
|
: Constraint(s),
|
|
|
|
|
vars_(vars),
|
|
|
|
|
coefs_(coefs),
|
|
|
|
|
first_unbound_backward_(vars.size() - 1),
|
|
|
|
|
sum_of_bound_variables_(0LL),
|
|
|
|
|
sum_of_all_variables_(0LL),
|
|
|
|
|
constant_(constant),
|
|
|
|
|
max_coefficient_(0) {
|
2013-10-10 15:23:20 +00:00
|
|
|
CHECK(!vars.empty());
|
2015-10-23 13:45:43 +02:00
|
|
|
constant_ =
|
|
|
|
|
CapSub(constant_, SortBothChangeConstant(&vars_, &coefs_, false));
|
2013-10-10 15:23:20 +00:00
|
|
|
max_coefficient_.SetValue(s, coefs_[vars_.size() - 1]);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~PositiveBooleanScalProdEqCst() override {}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void Post() override {
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int var_index = 0; var_index < vars_.size(); ++var_index) {
|
2010-09-15 12:42:33 +00:00
|
|
|
if (!vars_[var_index]->Bound()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
Demon* const d = MakeConstraintDemon1(
|
2013-07-24 00:28:11 +00:00
|
|
|
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();
|
|
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t slack_up = CapSub(constant_, sum_of_bound_variables_.Value());
|
|
|
|
|
const int64_t slack_down = CapSub(sum_of_all_variables_.Value(), constant_);
|
|
|
|
|
const int64_t max_coeff = max_coefficient_.Value();
|
2010-09-15 12:42:33 +00:00
|
|
|
if (slack_down < max_coeff || slack_up < max_coeff) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t last_unbound = first_unbound_backward_.Value();
|
2010-09-15 12:42:33 +00:00
|
|
|
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);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
void InitialPropagate() override {
|
2020-10-29 14:25:39 +01:00
|
|
|
Solver* const s = solver();
|
2010-09-15 12:42:33 +00:00
|
|
|
int last_unbound = -1;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum_bound = 0LL;
|
|
|
|
|
int64_t sum_all = 0LL;
|
2013-10-10 15:23:20 +00:00
|
|
|
for (int index = 0; index < vars_.size(); ++index) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t value = CapProd(vars_[index]->Max(), coefs_[index]);
|
2015-10-23 13:45:43 +02:00
|
|
|
sum_all = CapAdd(sum_all, value);
|
2010-09-15 12:42:33 +00:00
|
|
|
if (vars_[index]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
sum_bound = CapAdd(sum_bound, value);
|
2010-09-15 12:42:33 +00:00
|
|
|
} 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(
|
2015-10-23 13:45:43 +02:00
|
|
|
solver(), CapAdd(sum_of_bound_variables_.Value(), coefs_[var_index]));
|
2010-09-15 12:42:33 +00:00
|
|
|
} else {
|
|
|
|
|
sum_of_all_variables_.SetValue(
|
2015-10-23 13:45:43 +02:00
|
|
|
solver(), CapSub(sum_of_all_variables_.Value(), coefs_[var_index]));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
Propagate();
|
|
|
|
|
}
|
|
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override {
|
2018-12-03 14:26:31 +01:00
|
|
|
return absl::StrFormat("PositiveBooleanScalProd([%s], [%s]) == %d",
|
|
|
|
|
JoinDebugStringPtr(vars_, ", "),
|
|
|
|
|
absl::StrJoin(coefs_, ", "), constant_);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2011-07-11 20:13:14 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void Accept(ModelVisitor* const visitor) const override {
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->BeginVisitConstraint(ModelVisitor::kScalProdEqual, this);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kVarsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
vars_);
|
2011-07-14 23:37:47 +00:00
|
|
|
visitor->VisitIntegerArrayArgument(ModelVisitor::kCoefficientsArgument,
|
2013-10-10 15:23:20 +00:00
|
|
|
coefs_);
|
2013-07-24 00:28:11 +00:00
|
|
|
visitor->VisitIntegerArgument(ModelVisitor::kValueArgument, constant_);
|
2011-07-11 20:13:14 +00:00
|
|
|
visitor->EndVisitConstraint(ModelVisitor::kScalProdEqual, this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars_;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs_;
|
2010-09-15 12:42:33 +00:00
|
|
|
Rev<int> first_unbound_backward_;
|
2021-04-01 12:13:35 +02:00
|
|
|
Rev<int64_t> sum_of_bound_variables_;
|
|
|
|
|
Rev<int64_t> sum_of_all_variables_;
|
|
|
|
|
int64_t constant_;
|
|
|
|
|
Rev<int64_t> max_coefficient_;
|
2010-09-15 12:42:33 +00:00
|
|
|
};
|
|
|
|
|
|
2013-06-18 09:43:51 +00:00
|
|
|
// ----- Linearizer -----
|
|
|
|
|
|
|
|
|
|
#define IS_TYPE(type, tag) type.compare(ModelVisitor::tag) == 0
|
|
|
|
|
|
|
|
|
|
class ExprLinearizer : public ModelParser {
|
2020-10-22 23:36:58 +02:00
|
|
|
public:
|
2015-05-06 13:05:28 +02:00
|
|
|
explicit ExprLinearizer(
|
2021-04-01 12:13:35 +02:00
|
|
|
absl::flat_hash_map<IntVar*, int64_t>* const variables_to_coefficients)
|
2014-06-11 22:16:31 +00:00
|
|
|
: variables_to_coefficients_(variables_to_coefficients), constant_(0) {}
|
2013-06-18 09:43:51 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
~ExprLinearizer() override {}
|
2013-06-18 09:43:51 +00:00
|
|
|
|
|
|
|
|
// Begin/End visit element.
|
2020-10-29 14:25:39 +01:00
|
|
|
void BeginVisitModel(const std::string& solver_name) override {
|
2013-06-18 09:43:51 +00:00
|
|
|
LOG(FATAL) << "Should not be here";
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void EndVisitModel(const std::string& solver_name) override {
|
2013-06-18 09:43:51 +00:00
|
|
|
LOG(FATAL) << "Should not be here";
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void BeginVisitConstraint(const std::string& type_name,
|
|
|
|
|
const Constraint* const constraint) override {
|
2013-06-18 09:43:51 +00:00
|
|
|
LOG(FATAL) << "Should not be here";
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void EndVisitConstraint(const std::string& type_name,
|
|
|
|
|
const Constraint* const constraint) override {
|
2013-06-18 09:43:51 +00:00
|
|
|
LOG(FATAL) << "Should not be here";
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void BeginVisitExtension(const std::string& type) override {}
|
2013-06-18 09:43:51 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void EndVisitExtension(const std::string& type) override {}
|
|
|
|
|
void BeginVisitIntegerExpression(const std::string& type_name,
|
|
|
|
|
const IntExpr* const expr) override {
|
2013-06-18 09:43:51 +00:00
|
|
|
BeginVisit(true);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void EndVisitIntegerExpression(const std::string& type_name,
|
|
|
|
|
const IntExpr* const expr) override {
|
2013-06-18 09:43:51 +00:00
|
|
|
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);
|
2012-06-15 09:03:39 +00:00
|
|
|
} else {
|
2013-06-18 09:43:51 +00:00
|
|
|
VisitIntegerExpression(expr);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
EndVisit();
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitIntegerVariable(const IntVar* const variable,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::string& operation, int64_t value,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const delegate) override {
|
2013-06-18 09:43:51 +00:00
|
|
|
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);
|
2012-06-15 09:03:39 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitIntegerVariable(const IntVar* const variable,
|
|
|
|
|
IntExpr* const delegate) override {
|
2013-10-10 15:23:20 +00:00
|
|
|
if (delegate != nullptr) {
|
2013-06-18 09:43:51 +00:00
|
|
|
VisitSubExpression(delegate);
|
2012-06-15 09:03:39 +00:00
|
|
|
} else {
|
2013-06-18 09:43:51 +00:00
|
|
|
if (variable->Bound()) {
|
|
|
|
|
AddConstant(variable->Min());
|
2012-06-30 15:12:14 +00:00
|
|
|
} else {
|
2013-06-18 09:43:51 +00:00
|
|
|
RegisterExpression(variable, 1);
|
2012-06-15 09:03:39 +00:00
|
|
|
}
|
|
|
|
|
}
|
2012-06-29 17:09:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Visit integer arguments.
|
2021-04-01 12:13:35 +02:00
|
|
|
void VisitIntegerArgument(const std::string& arg_name,
|
|
|
|
|
int64_t value) override {
|
2012-06-29 17:09:19 +00:00
|
|
|
Top()->SetIntegerArgument(arg_name, value);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitIntegerArrayArgument(const std::string& arg_name,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& values) override {
|
2013-10-10 15:23:20 +00:00
|
|
|
Top()->SetIntegerArrayArgument(arg_name, values);
|
2012-06-29 17:09:19 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitIntegerMatrixArgument(const std::string& arg_name,
|
|
|
|
|
const IntTupleSet& values) override {
|
2012-06-29 17:09:19 +00:00
|
|
|
Top()->SetIntegerMatrixArgument(arg_name, values);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Visit integer expression argument.
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitIntegerExpressionArgument(const std::string& arg_name,
|
|
|
|
|
IntExpr* const argument) override {
|
2012-06-29 17:09:19 +00:00
|
|
|
Top()->SetIntegerExpressionArgument(arg_name, argument);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
void VisitIntegerVariableArrayArgument(
|
2020-10-29 14:25:39 +01:00
|
|
|
const std::string& arg_name,
|
|
|
|
|
const std::vector<IntVar*>& arguments) override {
|
2013-10-10 15:23:20 +00:00
|
|
|
Top()->SetIntegerVariableArrayArgument(arg_name, arguments);
|
2012-06-29 17:09:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Visit interval argument.
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitIntervalArgument(const std::string& arg_name,
|
|
|
|
|
IntervalVar* const argument) override {}
|
2012-06-29 17:09:19 +00:00
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
void VisitIntervalArrayArgument(
|
2020-10-29 14:25:39 +01:00
|
|
|
const std::string& arg_name,
|
|
|
|
|
const std::vector<IntervalVar*>& argument) override {}
|
2012-06-29 17:09:19 +00:00
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void Visit(const IntExpr* const expr, int64_t multiplier) {
|
2013-07-22 18:31:22 +00:00
|
|
|
if (expr->Min() == expr->Max()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
constant_ = CapAdd(constant_, CapProd(expr->Min(), multiplier));
|
2013-07-22 18:31:22 +00:00
|
|
|
} else {
|
|
|
|
|
PushMultiplier(multiplier);
|
|
|
|
|
expr->Accept(this);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
}
|
2012-06-29 17:09:19 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t Constant() const { return constant_; }
|
2012-06-29 17:09:19 +00:00
|
|
|
|
2015-04-16 15:49:51 +02:00
|
|
|
std::string DebugString() const override { return "ExprLinearizer"; }
|
2012-08-14 18:00:08 +00:00
|
|
|
|
2020-10-22 23:36:58 +02:00
|
|
|
private:
|
2013-07-24 00:28:11 +00:00
|
|
|
void BeginVisit(bool active) { PushArgumentHolder(); }
|
2012-06-29 17:09:19 +00:00
|
|
|
|
2013-07-24 00:28:11 +00:00
|
|
|
void EndVisit() { PopArgumentHolder(); }
|
2012-06-29 17:09:19 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitSubExpression(const IntExpr* const cp_expr) {
|
2012-06-29 17:09:19 +00:00
|
|
|
cp_expr->Accept(this);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitSum(const IntExpr* const cp_expr) {
|
2012-06-29 17:09:19 +00:00
|
|
|
if (Top()->HasIntegerVariableArrayArgument(ModelVisitor::kVarsArgument)) {
|
2020-10-29 14:25:39 +01:00
|
|
|
const std::vector<IntVar*>& cp_vars =
|
2012-06-29 17:09:19 +00:00
|
|
|
Top()->FindIntegerVariableArrayArgumentOrDie(
|
|
|
|
|
ModelVisitor::kVarsArgument);
|
|
|
|
|
for (int i = 0; i < cp_vars.size(); ++i) {
|
|
|
|
|
VisitSubExpression(cp_vars[i]);
|
|
|
|
|
}
|
|
|
|
|
} else if (Top()->HasIntegerExpressionArgument(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kLeftArgument)) {
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntExpr* const left = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kLeftArgument);
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntExpr* const right = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kRightArgument);
|
2012-06-29 17:09:19 +00:00
|
|
|
VisitSubExpression(left);
|
|
|
|
|
VisitSubExpression(right);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kExpressionArgument);
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t value =
|
2012-06-29 17:09:19 +00:00
|
|
|
Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
|
|
|
|
|
VisitSubExpression(expr);
|
|
|
|
|
AddConstant(value);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitScalProd(const IntExpr* const cp_expr) {
|
|
|
|
|
const std::vector<IntVar*>& cp_vars =
|
2012-06-29 17:09:19 +00:00
|
|
|
Top()->FindIntegerVariableArrayArgumentOrDie(
|
|
|
|
|
ModelVisitor::kVarsArgument);
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& cp_coefficients =
|
2016-11-29 13:40:04 +01:00
|
|
|
Top()->FindIntegerArrayArgumentOrDie(
|
|
|
|
|
ModelVisitor::kCoefficientsArgument);
|
2012-06-29 17:09:19 +00:00
|
|
|
CHECK_EQ(cp_vars.size(), cp_coefficients.size());
|
|
|
|
|
for (int i = 0; i < cp_vars.size(); ++i) {
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t coefficient = cp_coefficients[i];
|
2012-06-29 17:09:19 +00:00
|
|
|
PushMultiplier(coefficient);
|
|
|
|
|
VisitSubExpression(cp_vars[i]);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitDifference(const IntExpr* const cp_expr) {
|
2012-06-29 17:09:19 +00:00
|
|
|
if (Top()->HasIntegerExpressionArgument(ModelVisitor::kLeftArgument)) {
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntExpr* const left = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kLeftArgument);
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntExpr* const right = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kRightArgument);
|
2012-06-29 17:09:19 +00:00
|
|
|
VisitSubExpression(left);
|
|
|
|
|
PushMultiplier(-1);
|
|
|
|
|
VisitSubExpression(right);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kExpressionArgument);
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t value =
|
2012-06-29 17:09:19 +00:00
|
|
|
Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
|
|
|
|
|
AddConstant(value);
|
|
|
|
|
PushMultiplier(-1);
|
|
|
|
|
VisitSubExpression(expr);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitOpposite(const IntExpr* const cp_expr) {
|
|
|
|
|
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kExpressionArgument);
|
2012-06-29 17:09:19 +00:00
|
|
|
PushMultiplier(-1);
|
|
|
|
|
VisitSubExpression(expr);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitProduct(const IntExpr* const cp_expr) {
|
2012-06-29 17:09:19 +00:00
|
|
|
if (Top()->HasIntegerExpressionArgument(
|
|
|
|
|
ModelVisitor::kExpressionArgument)) {
|
2020-10-29 14:25:39 +01:00
|
|
|
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kExpressionArgument);
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t value =
|
2012-06-29 17:09:19 +00:00
|
|
|
Top()->FindIntegerArgumentOrDie(ModelVisitor::kValueArgument);
|
|
|
|
|
PushMultiplier(value);
|
|
|
|
|
VisitSubExpression(expr);
|
|
|
|
|
PopMultiplier();
|
|
|
|
|
} else {
|
|
|
|
|
RegisterExpression(cp_expr, 1);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitTrace(const IntExpr* const cp_expr) {
|
|
|
|
|
const IntExpr* const expr = Top()->FindIntegerExpressionArgumentOrDie(
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelVisitor::kExpressionArgument);
|
2013-06-18 09:43:51 +00:00
|
|
|
VisitSubExpression(expr);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void VisitIntegerExpression(const IntExpr* const cp_expr) {
|
2012-06-29 17:09:19 +00:00
|
|
|
RegisterExpression(cp_expr, 1);
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void RegisterExpression(const IntExpr* const expr, int64_t coef) {
|
|
|
|
|
int64_t& value =
|
2020-10-29 14:25:39 +01:00
|
|
|
(*variables_to_coefficients_)[const_cast<IntExpr*>(expr)->Var()];
|
2015-10-23 13:45:43 +02:00
|
|
|
value = CapAdd(value, CapProd(coef, multipliers_.back()));
|
2012-06-29 17:09:19 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void AddConstant(int64_t constant) {
|
2015-10-23 13:45:43 +02:00
|
|
|
constant_ = CapAdd(constant_, CapProd(constant, multipliers_.back()));
|
2012-06-29 17:09:19 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
void PushMultiplier(int64_t multiplier) {
|
2012-06-29 20:03:47 +00:00
|
|
|
if (multipliers_.empty()) {
|
|
|
|
|
multipliers_.push_back(multiplier);
|
|
|
|
|
} else {
|
2015-10-23 13:45:43 +02:00
|
|
|
multipliers_.push_back(CapProd(multiplier, multipliers_.back()));
|
2012-06-29 20:03:47 +00:00
|
|
|
}
|
2012-06-29 17:09:19 +00:00
|
|
|
}
|
|
|
|
|
|
2013-07-24 00:28:11 +00:00
|
|
|
void PopMultiplier() { multipliers_.pop_back(); }
|
2012-06-29 17:09:19 +00:00
|
|
|
|
2013-12-05 09:29:08 +00:00
|
|
|
// We do need a IntVar* as key, and not const IntVar*, because clients of this
|
|
|
|
|
// class typically iterate over the map keys and use them as mutable IntVar*.
|
2021-04-01 12:13:35 +02:00
|
|
|
absl::flat_hash_map<IntVar*, int64_t>* const variables_to_coefficients_;
|
|
|
|
|
std::vector<int64_t> multipliers_;
|
|
|
|
|
int64_t constant_;
|
2012-06-29 17:09:19 +00:00
|
|
|
};
|
2013-06-18 09:43:51 +00:00
|
|
|
#undef IS_TYPE
|
|
|
|
|
|
|
|
|
|
// ----- Factory functions -----
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
void DeepLinearize(Solver* const solver, const std::vector<IntVar*>& pre_vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& pre_coefs,
|
|
|
|
|
std::vector<IntVar*>* vars, std::vector<int64_t>* coefs,
|
|
|
|
|
int64_t* constant) {
|
2014-06-11 22:16:31 +00:00
|
|
|
CHECK(solver != nullptr);
|
|
|
|
|
CHECK(vars != nullptr);
|
|
|
|
|
CHECK(coefs != nullptr);
|
|
|
|
|
CHECK(constant != nullptr);
|
2014-05-24 21:03:56 +00:00
|
|
|
*constant = 0;
|
|
|
|
|
vars->reserve(pre_vars.size());
|
|
|
|
|
coefs->reserve(pre_coefs.size());
|
|
|
|
|
// Try linear scan of the variables to check if there is nothing to do.
|
2014-06-11 22:16:31 +00:00
|
|
|
bool need_linearization = false;
|
2014-05-24 21:03:56 +00:00
|
|
|
for (int i = 0; i < pre_vars.size(); ++i) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const variable = pre_vars[i];
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t coefficient = pre_coefs[i];
|
2014-06-11 22:16:31 +00:00
|
|
|
if (variable->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
*constant = CapAdd(*constant, CapProd(coefficient, variable->Min()));
|
2014-06-11 22:16:31 +00:00
|
|
|
} else if (solver->CastExpression(variable) == nullptr) {
|
|
|
|
|
vars->push_back(variable);
|
|
|
|
|
coefs->push_back(coefficient);
|
2014-05-24 21:03:56 +00:00
|
|
|
} else {
|
2014-06-11 22:16:31 +00:00
|
|
|
need_linearization = true;
|
2014-05-24 21:03:56 +00:00
|
|
|
vars->clear();
|
|
|
|
|
coefs->clear();
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2014-06-11 22:16:31 +00:00
|
|
|
if (need_linearization) {
|
2014-05-24 21:03:56 +00:00
|
|
|
// Instrospect the variables to simplify the sum.
|
2021-04-01 12:13:35 +02:00
|
|
|
absl::flat_hash_map<IntVar*, int64_t> variables_to_coefficients;
|
2014-06-11 22:16:31 +00:00
|
|
|
ExprLinearizer linearizer(&variables_to_coefficients);
|
2014-05-24 21:03:56 +00:00
|
|
|
for (int i = 0; i < pre_vars.size(); ++i) {
|
2014-06-11 22:16:31 +00:00
|
|
|
linearizer.Visit(pre_vars[i], pre_coefs[i]);
|
2014-05-24 21:03:56 +00:00
|
|
|
}
|
2014-06-11 22:16:31 +00:00
|
|
|
*constant = linearizer.Constant();
|
2020-10-29 14:25:39 +01:00
|
|
|
for (const auto& variable_to_coefficient : variables_to_coefficients) {
|
2014-06-11 22:16:31 +00:00
|
|
|
if (variable_to_coefficient.second != 0) {
|
|
|
|
|
vars->push_back(variable_to_coefficient.first);
|
|
|
|
|
coefs->push_back(variable_to_coefficient.second);
|
2014-05-24 21:03:56 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* MakeScalProdEqualityFct(Solver* const solver,
|
|
|
|
|
const std::vector<IntVar*>& pre_vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& pre_coefs,
|
|
|
|
|
int64_t cst) {
|
|
|
|
|
int64_t constant = 0;
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs;
|
2014-05-24 21:03:56 +00:00
|
|
|
DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
|
2015-10-23 13:45:43 +02:00
|
|
|
cst = CapSub(cst, constant);
|
2014-05-24 21:03:56 +00:00
|
|
|
|
2013-06-18 09:43:51 +00:00
|
|
|
const int size = vars.size();
|
2014-05-24 21:03:56 +00:00
|
|
|
if (size == 0 || AreAllNull(coefs)) {
|
2013-06-18 09:43:51 +00:00
|
|
|
return cst == 0 ? solver->MakeTrueConstraint()
|
2013-07-24 00:28:11 +00:00
|
|
|
: solver->MakeFalseConstraint();
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2014-05-24 21:03:56 +00:00
|
|
|
if (AreAllBoundOrNull(vars, coefs)) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t sum = 0;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2015-10-23 13:45:43 +02:00
|
|
|
sum = CapAdd(sum, CapProd(coefs[i], vars[i]->Min()));
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
return sum == cst ? solver->MakeTrueConstraint()
|
|
|
|
|
: solver->MakeFalseConstraint();
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2014-05-24 21:03:56 +00:00
|
|
|
if (AreAllOnes(coefs)) {
|
2013-06-18 09:43:51 +00:00
|
|
|
return solver->MakeSumEquality(vars, cst);
|
|
|
|
|
}
|
2014-07-09 11:10:20 +00:00
|
|
|
if (AreAllBooleans(vars) && size > 2) {
|
|
|
|
|
if (AreAllPositive(coefs)) {
|
|
|
|
|
return solver->RevAlloc(
|
|
|
|
|
new PositiveBooleanScalProdEqCst(solver, vars, coefs, cst));
|
|
|
|
|
}
|
|
|
|
|
if (AreAllNegative(coefs)) {
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> opp_coefs(coefs.size());
|
2014-07-09 11:10:20 +00:00
|
|
|
for (int i = 0; i < coefs.size(); ++i) {
|
|
|
|
|
opp_coefs[i] = -coefs[i];
|
|
|
|
|
}
|
|
|
|
|
return solver->RevAlloc(
|
|
|
|
|
new PositiveBooleanScalProdEqCst(solver, vars, opp_coefs, -cst));
|
2014-06-26 12:32:22 +00:00
|
|
|
}
|
|
|
|
|
}
|
2014-05-24 01:58:15 +00:00
|
|
|
|
2013-06-18 09:43:51 +00:00
|
|
|
// Simplications.
|
|
|
|
|
int constants = 0;
|
|
|
|
|
int positives = 0;
|
|
|
|
|
int negatives = 0;
|
|
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2013-06-18 09:43:51 +00:00
|
|
|
constants++;
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
2013-06-18 09:43:51 +00:00
|
|
|
positives++;
|
|
|
|
|
} else {
|
|
|
|
|
negatives++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (positives > 0 && negatives > 0) {
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> pos_terms;
|
|
|
|
|
std::vector<IntVar*> neg_terms;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = cst;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
|
|
|
|
pos_terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
} else {
|
2014-05-24 21:03:56 +00:00
|
|
|
neg_terms.push_back(solver->MakeProd(vars[i], -coefs[i])->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
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));
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
return solver->MakeEquality(solver->MakeSum(pos_terms),
|
|
|
|
|
solver->MakeSum(neg_terms));
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
} else if (positives == 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* pos_term = nullptr;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = cst;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
|
|
|
|
pos_term = solver->MakeProd(vars[i], coefs[i]);
|
2013-06-18 09:43:51 +00:00
|
|
|
} else {
|
|
|
|
|
LOG(FATAL) << "Should not be here";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return solver->MakeEquality(pos_term, rhs);
|
|
|
|
|
} else if (negatives == 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* neg_term = nullptr;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = cst;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
2013-06-18 09:43:51 +00:00
|
|
|
LOG(FATAL) << "Should not be here";
|
|
|
|
|
} else {
|
2014-05-24 21:03:56 +00:00
|
|
|
neg_term = solver->MakeProd(vars[i], -coefs[i]);
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return solver->MakeEquality(neg_term, -rhs);
|
|
|
|
|
} else if (positives > 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> pos_terms;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = cst;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
|
|
|
|
pos_terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
} else {
|
|
|
|
|
LOG(FATAL) << "Should not be here";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return solver->MakeSumEquality(pos_terms, rhs);
|
|
|
|
|
} else if (negatives > 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> neg_terms;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = cst;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
2013-06-18 09:43:51 +00:00
|
|
|
LOG(FATAL) << "Should not be here";
|
|
|
|
|
} else {
|
2014-05-24 21:03:56 +00:00
|
|
|
neg_terms.push_back(solver->MakeProd(vars[i], -coefs[i])->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return solver->MakeSumEquality(neg_terms, -rhs);
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> terms;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
return solver->MakeSumEquality(terms, solver->MakeIntConst(cst));
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* MakeScalProdEqualityVarFct(Solver* const solver,
|
|
|
|
|
const std::vector<IntVar*>& pre_vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& pre_coefs,
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const target) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t constant = 0;
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs;
|
2014-05-24 21:03:56 +00:00
|
|
|
DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
|
2014-05-24 01:58:15 +00:00
|
|
|
|
2013-06-18 09:43:51 +00:00
|
|
|
const int size = vars.size();
|
2021-04-01 12:13:35 +02:00
|
|
|
if (size == 0 || AreAllNull<int64_t>(coefs)) {
|
2014-05-24 21:03:56 +00:00
|
|
|
return solver->MakeEquality(target, constant);
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2014-05-24 21:03:56 +00:00
|
|
|
if (AreAllOnes(coefs)) {
|
|
|
|
|
return solver->MakeSumEquality(vars,
|
|
|
|
|
solver->MakeSum(target, -constant)->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
if (AreAllBooleans(vars) && AreAllPositive<int64_t>(coefs)) {
|
2013-06-18 09:43:51 +00:00
|
|
|
// TODO(user) : bench BooleanScalProdEqVar with IntConst.
|
2014-05-24 21:03:56 +00:00
|
|
|
return solver->RevAlloc(new PositiveBooleanScalProdEqVar(
|
|
|
|
|
solver, vars, coefs, solver->MakeSum(target, -constant)->Var()));
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> terms;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2014-05-24 21:03:56 +00:00
|
|
|
return solver->MakeSumEquality(terms,
|
|
|
|
|
solver->MakeSum(target, -constant)->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* MakeScalProdGreaterOrEqualFct(Solver* solver,
|
|
|
|
|
const std::vector<IntVar*>& pre_vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& pre_coefs,
|
|
|
|
|
int64_t cst) {
|
|
|
|
|
int64_t constant = 0;
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs;
|
2014-05-24 21:03:56 +00:00
|
|
|
DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
|
2015-10-23 13:45:43 +02:00
|
|
|
cst = CapSub(cst, constant);
|
2014-05-24 21:03:56 +00:00
|
|
|
|
2013-06-18 09:43:51 +00:00
|
|
|
const int size = vars.size();
|
2021-04-01 12:13:35 +02:00
|
|
|
if (size == 0 || AreAllNull<int64_t>(coefs)) {
|
2013-06-18 09:43:51 +00:00
|
|
|
return cst <= 0 ? solver->MakeTrueConstraint()
|
2013-07-24 00:28:11 +00:00
|
|
|
: solver->MakeFalseConstraint();
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2014-05-24 21:03:56 +00:00
|
|
|
if (AreAllOnes(coefs)) {
|
2013-06-18 09:43:51 +00:00
|
|
|
return solver->MakeSumGreaterOrEqual(vars, cst);
|
|
|
|
|
}
|
2014-05-24 21:03:56 +00:00
|
|
|
if (cst == 1 && AreAllBooleans(vars) && AreAllPositive(coefs)) {
|
|
|
|
|
// can move all coefs to 1.
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> terms;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] > 0) {
|
2013-06-18 09:43:51 +00:00
|
|
|
terms.push_back(vars[i]);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return solver->MakeSumGreaterOrEqual(terms, 1);
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> terms;
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
return solver->MakeSumGreaterOrEqual(terms, cst);
|
|
|
|
|
}
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* MakeScalProdLessOrEqualFct(Solver* solver,
|
|
|
|
|
const std::vector<IntVar*>& pre_vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& pre_coefs,
|
|
|
|
|
int64_t upper_bound) {
|
|
|
|
|
int64_t constant = 0;
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs;
|
2014-05-24 21:03:56 +00:00
|
|
|
DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
|
2015-10-23 13:45:43 +02:00
|
|
|
upper_bound = CapSub(upper_bound, constant);
|
2014-05-24 21:03:56 +00:00
|
|
|
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
2021-04-01 12:13:35 +02:00
|
|
|
if (size == 0 || AreAllNull<int64_t>(coefs)) {
|
2010-09-15 12:42:33 +00:00
|
|
|
return upper_bound >= 0 ? solver->MakeTrueConstraint()
|
2013-07-24 00:28:11 +00:00
|
|
|
: solver->MakeFalseConstraint();
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
// TODO(user) : compute constant on the fly.
|
2014-05-24 21:03:56 +00:00
|
|
|
if (AreAllBoundOrNull(vars, coefs)) {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst = 0;
|
2010-09-15 12:42:33 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2015-10-23 13:45:43 +02:00
|
|
|
cst = CapAdd(cst, CapProd(vars[i]->Min(), coefs[i]));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
return cst <= upper_bound ? solver->MakeTrueConstraint()
|
|
|
|
|
: solver->MakeFalseConstraint();
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2014-05-24 21:03:56 +00:00
|
|
|
if (AreAllOnes(coefs)) {
|
2012-09-04 21:40:12 +00:00
|
|
|
return solver->MakeSumLessOrEqual(vars, upper_bound);
|
|
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
if (AreAllBooleans(vars) && AreAllPositive<int64_t>(coefs)) {
|
2014-05-24 21:03:56 +00:00
|
|
|
return solver->RevAlloc(
|
|
|
|
|
new BooleanScalProdLessConstant(solver, vars, coefs, 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) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2012-06-23 18:58:32 +00:00
|
|
|
constants++;
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
2012-06-23 18:58:32 +00:00
|
|
|
positives++;
|
|
|
|
|
} else {
|
|
|
|
|
negatives++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (positives > 0 && negatives > 0) {
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> pos_terms;
|
|
|
|
|
std::vector<IntVar*> neg_terms;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = upper_bound;
|
2012-06-23 18:58:32 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
|
|
|
|
pos_terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
2012-06-23 18:58:32 +00:00
|
|
|
} else {
|
2014-05-24 21:03:56 +00:00
|
|
|
neg_terms.push_back(solver->MakeProd(vars[i], -coefs[i])->Var());
|
2012-06-23 18:58:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (negatives == 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const neg_term = solver->MakeSum(neg_terms[0], rhs);
|
2013-07-19 03:47:59 +00:00
|
|
|
return solver->MakeLessOrEqual(solver->MakeSum(pos_terms), neg_term);
|
2012-06-23 18:58:32 +00:00
|
|
|
} else if (positives == 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const pos_term = solver->MakeSum(pos_terms[0], -rhs);
|
2013-07-19 03:47:59 +00:00
|
|
|
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));
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
return solver->MakeLessOrEqual(solver->MakeSum(pos_terms),
|
|
|
|
|
solver->MakeSum(neg_terms));
|
2012-06-23 18:58:32 +00:00
|
|
|
}
|
|
|
|
|
} else if (positives == 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* pos_term = nullptr;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = upper_bound;
|
2012-06-23 18:58:32 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
|
|
|
|
pos_term = solver->MakeProd(vars[i], coefs[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) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* neg_term = nullptr;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = upper_bound;
|
2012-06-23 18:58:32 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
2012-06-23 18:58:32 +00:00
|
|
|
LOG(FATAL) << "Should not be here";
|
|
|
|
|
} else {
|
2014-05-24 21:03:56 +00:00
|
|
|
neg_term = solver->MakeProd(vars[i], -coefs[i]);
|
2012-06-23 18:58:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return solver->MakeGreaterOrEqual(neg_term, -rhs);
|
|
|
|
|
} else if (positives > 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> pos_terms;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = upper_bound;
|
2012-06-23 18:58:32 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
|
|
|
|
pos_terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
2012-06-23 18:58:32 +00:00
|
|
|
} else {
|
|
|
|
|
LOG(FATAL) << "Should not be here";
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return solver->MakeSumLessOrEqual(pos_terms, rhs);
|
|
|
|
|
} else if (negatives > 1) {
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> neg_terms;
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t rhs = upper_bound;
|
2012-06-23 18:58:32 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (coefs[i] == 0 || vars[i]->Bound()) {
|
2015-10-23 13:45:43 +02:00
|
|
|
rhs = CapSub(rhs, CapProd(coefs[i], vars[i]->Min()));
|
2014-05-24 21:03:56 +00:00
|
|
|
} else if (coefs[i] > 0) {
|
2012-06-23 18:58:32 +00:00
|
|
|
LOG(FATAL) << "Should not be here";
|
|
|
|
|
} else {
|
2014-05-24 21:03:56 +00:00
|
|
|
neg_terms.push_back(solver->MakeProd(vars[i], -coefs[i])->Var());
|
2012-06-23 18:58:32 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return solver->MakeSumGreaterOrEqual(neg_terms, -rhs);
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> terms;
|
2010-09-15 12:42:33 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2014-05-24 21:03:56 +00:00
|
|
|
terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
return solver->MakeLessOrEqual(solver->MakeSum(terms), upper_bound);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* MakeSumArrayAux(Solver* const solver, const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t constant) {
|
2013-08-01 22:17:49 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
DCHECK_GT(size, 2);
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t new_min = 0;
|
|
|
|
|
int64_t new_max = 0;
|
2013-08-01 22:17:49 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2021-04-01 12:13:35 +02:00
|
|
|
if (new_min != std::numeric_limits<int64_t>::min()) {
|
2013-08-01 22:17:49 +00:00
|
|
|
new_min = CapAdd(vars[i]->Min(), new_min);
|
|
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
if (new_max != std::numeric_limits<int64_t>::max()) {
|
2013-08-01 22:17:49 +00:00
|
|
|
new_max = CapAdd(vars[i]->Max(), new_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const cache =
|
2013-10-10 15:23:20 +00:00
|
|
|
solver->Cache()->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_SUM);
|
|
|
|
|
if (cache != nullptr) {
|
2013-08-01 23:49:28 +00:00
|
|
|
return solver->MakeSum(cache, constant);
|
2013-08-01 22:17:49 +00:00
|
|
|
} else {
|
2013-12-16 10:24:42 +00:00
|
|
|
const std::string name =
|
2018-10-31 16:18:18 +01:00
|
|
|
absl::StrFormat("Sum([%s])", JoinNamePtr(vars, ", "));
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const sum_var = solver->MakeIntVar(new_min, new_max, name);
|
2013-08-01 22:17:49 +00:00
|
|
|
if (AreAllBooleans(vars)) {
|
|
|
|
|
solver->AddConstraint(
|
2013-10-10 15:23:20 +00:00
|
|
|
solver->RevAlloc(new SumBooleanEqualToVar(solver, vars, sum_var)));
|
2016-02-03 15:15:58 +01:00
|
|
|
} else if (size <= solver->parameters().array_split_size()) {
|
2014-07-18 21:32:02 +00:00
|
|
|
solver->AddConstraint(
|
|
|
|
|
solver->RevAlloc(new SmallSumConstraint(solver, vars, sum_var)));
|
2013-08-01 22:17:49 +00:00
|
|
|
} else {
|
|
|
|
|
solver->AddConstraint(
|
|
|
|
|
solver->RevAlloc(new SumConstraint(solver, vars, sum_var)));
|
|
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
solver->Cache()->InsertVarArrayExpression(sum_var, vars,
|
|
|
|
|
ModelCache::VAR_ARRAY_SUM);
|
2013-08-01 22:17:49 +00:00
|
|
|
return solver->MakeSum(sum_var, constant);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* MakeSumAux(Solver* const solver, const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t constant) {
|
2012-06-14 19:32:41 +00:00
|
|
|
const int size = vars.size();
|
2013-06-18 09:43:51 +00:00
|
|
|
if (size == 0) {
|
2013-06-11 14:49:19 +00:00
|
|
|
return solver->MakeIntConst(constant);
|
2013-06-18 09:43:51 +00:00
|
|
|
} 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-18 09:43:51 +00:00
|
|
|
}
|
2013-06-11 14:49:19 +00:00
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* MakeScalProdAux(Solver* solver, const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& coefs, int64_t constant) {
|
2013-06-18 09:43:51 +00:00
|
|
|
if (AreAllOnes(coefs)) {
|
|
|
|
|
return MakeSumAux(solver, vars, constant);
|
2013-06-11 14:49:19 +00:00
|
|
|
}
|
|
|
|
|
|
2013-06-18 09:43:51 +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) {
|
2013-09-19 06:59:49 +00:00
|
|
|
if (coefs[0] > 0 && coefs[1] < 0) {
|
|
|
|
|
return solver->MakeSum(
|
|
|
|
|
solver->MakeDifference(solver->MakeProd(vars[0], coefs[0]),
|
|
|
|
|
solver->MakeProd(vars[1], -coefs[1])),
|
|
|
|
|
constant);
|
|
|
|
|
} else if (coefs[0] < 0 && coefs[1] > 0) {
|
|
|
|
|
return solver->MakeSum(
|
|
|
|
|
solver->MakeDifference(solver->MakeProd(vars[1], coefs[1]),
|
|
|
|
|
solver->MakeProd(vars[0], -coefs[0])),
|
|
|
|
|
constant);
|
|
|
|
|
} else {
|
|
|
|
|
return solver->MakeSum(
|
|
|
|
|
solver->MakeSum(solver->MakeProd(vars[0], coefs[0]),
|
|
|
|
|
solver->MakeProd(vars[1], coefs[1])),
|
|
|
|
|
constant);
|
|
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
} else {
|
|
|
|
|
if (AreAllBooleans(vars)) {
|
2014-05-24 21:03:56 +00:00
|
|
|
if (AreAllPositive(coefs)) {
|
2014-05-25 16:34:08 +00:00
|
|
|
if (vars.size() > 8) {
|
2014-06-11 22:16:31 +00:00
|
|
|
return solver->MakeSum(
|
2020-10-22 23:36:58 +02:00
|
|
|
solver
|
|
|
|
|
->RegisterIntExpr(solver->RevAlloc(
|
|
|
|
|
new PositiveBooleanScalProd(solver, vars, coefs)))
|
2015-08-13 16:00:54 +02:00
|
|
|
->Var(),
|
2014-05-25 16:34:08 +00:00
|
|
|
constant);
|
2014-06-11 22:16:31 +00:00
|
|
|
} else {
|
2014-05-25 16:34:08 +00:00
|
|
|
return solver->MakeSum(
|
|
|
|
|
solver->RegisterIntExpr(solver->RevAlloc(
|
|
|
|
|
new PositiveBooleanScalProd(solver, vars, coefs))),
|
|
|
|
|
constant);
|
|
|
|
|
}
|
2012-09-05 15:52:00 +00:00
|
|
|
} else {
|
2013-06-18 09:43:51 +00:00
|
|
|
// 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).
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> positive_coefs;
|
|
|
|
|
std::vector<int64_t> negative_coefs;
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> positive_coef_vars;
|
|
|
|
|
std::vector<IntVar*> negative_coef_vars;
|
2013-06-18 09:43:51 +00:00
|
|
|
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);
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const negatives =
|
2013-06-18 09:43:51 +00:00
|
|
|
MakeScalProdAux(solver, negative_coef_vars, negative_coefs, 0);
|
|
|
|
|
if (!positive_coef_vars.empty()) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const positives = MakeScalProdAux(solver, positive_coef_vars,
|
2013-06-18 09:43:51 +00:00
|
|
|
positive_coefs, constant);
|
2013-07-19 03:47:59 +00:00
|
|
|
return solver->MakeDifference(positives, negatives);
|
2013-06-18 09:43:51 +00:00
|
|
|
} else {
|
2013-06-18 13:21:15 +00:00
|
|
|
return solver->MakeDifference(constant, negatives);
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2012-09-05 15:52:00 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> terms;
|
2012-09-05 15:52:00 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
|
|
|
|
terms.push_back(solver->MakeProd(vars[i], coefs[i])->Var());
|
|
|
|
|
}
|
2013-08-01 23:38:44 +00:00
|
|
|
return MakeSumArrayAux(solver, terms, constant);
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* MakeScalProdFct(Solver* solver, const std::vector<IntVar*>& pre_vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& pre_coefs) {
|
|
|
|
|
int64_t constant = 0;
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs;
|
2014-05-24 21:03:56 +00:00
|
|
|
DeepLinearize(solver, pre_vars, pre_coefs, &vars, &coefs, &constant);
|
2014-05-13 12:56:44 +00:00
|
|
|
|
2013-10-18 13:50:35 +00:00
|
|
|
if (vars.empty()) {
|
|
|
|
|
return solver->MakeIntConst(constant);
|
|
|
|
|
}
|
2013-09-19 06:59:49 +00:00
|
|
|
// Can we simplify using some gcd computation.
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t gcd = std::abs(coefs[0]);
|
2013-09-18 17:07:01 +00:00
|
|
|
for (int i = 1; i < coefs.size(); ++i) {
|
2014-04-04 09:17:50 +00:00
|
|
|
gcd = MathUtil::GCD64(gcd, std::abs(coefs[i]));
|
2013-09-19 06:59:49 +00:00
|
|
|
if (gcd == 1) {
|
2013-09-18 17:07:01 +00:00
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
2013-09-19 06:59:49 +00:00
|
|
|
if (constant != 0 && gcd != 1) {
|
2014-04-04 09:17:50 +00:00
|
|
|
gcd = MathUtil::GCD64(gcd, std::abs(constant));
|
2013-09-19 06:59:49 +00:00
|
|
|
}
|
|
|
|
|
if (gcd > 1) {
|
2013-09-18 17:07:01 +00:00
|
|
|
for (int i = 0; i < coefs.size(); ++i) {
|
2013-09-19 06:59:49 +00:00
|
|
|
coefs[i] /= gcd;
|
2013-09-18 17:07:01 +00:00
|
|
|
}
|
|
|
|
|
return solver->MakeProd(
|
2013-10-18 13:50:35 +00:00
|
|
|
MakeScalProdAux(solver, vars, coefs, constant / gcd), gcd);
|
2013-09-18 17:07:01 +00:00
|
|
|
}
|
2013-06-18 09:43:51 +00:00
|
|
|
return MakeScalProdAux(solver, vars, coefs, constant);
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* MakeSumFct(Solver* solver, const std::vector<IntVar*>& pre_vars) {
|
2021-04-01 12:13:35 +02:00
|
|
|
absl::flat_hash_map<IntVar*, int64_t> variables_to_coefficients;
|
2014-06-11 22:16:31 +00:00
|
|
|
ExprLinearizer linearizer(&variables_to_coefficients);
|
2013-06-18 09:43:51 +00:00
|
|
|
for (int i = 0; i < pre_vars.size(); ++i) {
|
2014-06-11 22:16:31 +00:00
|
|
|
linearizer.Visit(pre_vars[i], 1);
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
const int64_t constant = linearizer.Constant();
|
2020-10-29 14:25:39 +01:00
|
|
|
std::vector<IntVar*> vars;
|
2021-04-01 12:13:35 +02:00
|
|
|
std::vector<int64_t> coefs;
|
2020-10-29 14:25:39 +01:00
|
|
|
for (const auto& variable_to_coefficient : variables_to_coefficients) {
|
2014-06-11 22:16:31 +00:00
|
|
|
if (variable_to_coefficient.second != 0) {
|
|
|
|
|
vars.push_back(variable_to_coefficient.first);
|
|
|
|
|
coefs.push_back(variable_to_coefficient.second);
|
2013-06-18 09:43:51 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return MakeScalProdAux(solver, vars, coefs, constant);
|
2012-09-05 15:52:00 +00:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace
|
2010-09-15 12:42:33 +00:00
|
|
|
|
2012-09-04 21:40:12 +00:00
|
|
|
// ----- API -----
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* Solver::MakeSum(const std::vector<IntVar*>& vars) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (size == 0) {
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeIntConst(int64_t{0});
|
2012-09-04 21:40:12 +00:00
|
|
|
} else if (size == 1) {
|
|
|
|
|
return vars[0];
|
|
|
|
|
} else if (size == 2) {
|
|
|
|
|
return MakeSum(vars[0], vars[1]);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const cache =
|
2012-09-04 21:40:12 +00:00
|
|
|
model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_SUM);
|
2013-10-10 15:23:20 +00:00
|
|
|
if (cache != nullptr) {
|
2013-07-19 03:47:59 +00:00
|
|
|
return cache;
|
2012-09-04 21:40:12 +00:00
|
|
|
} else {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t new_min = 0;
|
|
|
|
|
int64_t new_max = 0;
|
2012-09-04 21:40:12 +00:00
|
|
|
for (int i = 0; i < size; ++i) {
|
2021-04-01 12:13:35 +02:00
|
|
|
if (new_min != std::numeric_limits<int64_t>::min()) {
|
2012-09-04 21:40:12 +00:00
|
|
|
new_min = CapAdd(vars[i]->Min(), new_min);
|
|
|
|
|
}
|
2021-04-01 12:13:35 +02:00
|
|
|
if (new_max != std::numeric_limits<int64_t>::max()) {
|
2012-09-04 21:40:12 +00:00
|
|
|
new_max = CapAdd(vars[i]->Max(), new_max);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* sum_expr = nullptr;
|
2012-09-04 21:40:12 +00:00
|
|
|
const bool all_booleans = AreAllBooleans(vars);
|
|
|
|
|
if (all_booleans) {
|
2013-12-16 10:24:42 +00:00
|
|
|
const std::string name =
|
2018-10-31 16:18:18 +01:00
|
|
|
absl::StrFormat("BooleanSum([%s])", JoinNamePtr(vars, ", "));
|
2014-05-25 02:29:50 +00:00
|
|
|
sum_expr = MakeIntVar(new_min, new_max, name);
|
|
|
|
|
AddConstraint(
|
|
|
|
|
RevAlloc(new SumBooleanEqualToVar(this, vars, sum_expr->Var())));
|
2021-04-01 12:13:35 +02:00
|
|
|
} else if (new_min != std::numeric_limits<int64_t>::min() &&
|
|
|
|
|
new_max != std::numeric_limits<int64_t>::max()) {
|
2014-05-25 02:29:50 +00:00
|
|
|
sum_expr = MakeSumFct(this, vars);
|
2012-09-04 21:40:12 +00:00
|
|
|
} else {
|
2013-12-16 10:24:42 +00:00
|
|
|
const std::string name =
|
2018-10-31 16:18:18 +01:00
|
|
|
absl::StrFormat("Sum([%s])", JoinNamePtr(vars, ", "));
|
2014-05-25 02:29:50 +00:00
|
|
|
sum_expr = MakeIntVar(new_min, new_max, name);
|
|
|
|
|
AddConstraint(
|
|
|
|
|
RevAlloc(new SafeSumConstraint(this, vars, sum_expr->Var())));
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
2014-05-25 02:29:50 +00:00
|
|
|
model_cache_->InsertVarArrayExpression(sum_expr, vars,
|
2013-07-24 00:28:11 +00:00
|
|
|
ModelCache::VAR_ARRAY_SUM);
|
2014-05-25 02:29:50 +00:00
|
|
|
return sum_expr;
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* Solver::MakeMin(const std::vector<IntVar*>& vars) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (size == 0) {
|
2016-09-12 13:40:24 +02:00
|
|
|
LOG(WARNING) << "operations_research::Solver::MakeMin() was called with an "
|
|
|
|
|
"empty list of variables. Was this intentional?";
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeIntConst(std::numeric_limits<int64_t>::max());
|
2012-09-04 21:40:12 +00:00
|
|
|
} else if (size == 1) {
|
|
|
|
|
return vars[0];
|
|
|
|
|
} else if (size == 2) {
|
|
|
|
|
return MakeMin(vars[0], vars[1]);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const cache =
|
2012-09-04 21:40:12 +00:00
|
|
|
model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_MIN);
|
2013-10-10 15:23:20 +00:00
|
|
|
if (cache != nullptr) {
|
2013-07-19 03:47:59 +00:00
|
|
|
return cache;
|
2012-09-04 21:40:12 +00:00
|
|
|
} else {
|
|
|
|
|
if (AreAllBooleans(vars)) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const new_var = MakeBoolVar();
|
2012-09-04 21:40:12 +00:00
|
|
|
AddConstraint(RevAlloc(new ArrayBoolAndEq(this, vars, new_var)));
|
2013-07-24 00:28:11 +00:00
|
|
|
model_cache_->InsertVarArrayExpression(new_var, vars,
|
|
|
|
|
ModelCache::VAR_ARRAY_MIN);
|
2012-09-04 21:40:12 +00:00
|
|
|
return new_var;
|
|
|
|
|
} else {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t new_min = std::numeric_limits<int64_t>::max();
|
|
|
|
|
int64_t new_max = std::numeric_limits<int64_t>::max();
|
2012-09-04 21:40:12 +00:00
|
|
|
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());
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const new_var = MakeIntVar(new_min, new_max);
|
2016-02-03 15:15:58 +01:00
|
|
|
if (size <= parameters_.array_split_size()) {
|
2014-07-18 00:14:31 +00:00
|
|
|
AddConstraint(RevAlloc(new SmallMinConstraint(this, vars, new_var)));
|
|
|
|
|
} else {
|
|
|
|
|
AddConstraint(RevAlloc(new MinConstraint(this, vars, new_var)));
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
model_cache_->InsertVarArrayExpression(new_var, vars,
|
|
|
|
|
ModelCache::VAR_ARRAY_MIN);
|
2012-09-04 21:40:12 +00:00
|
|
|
return new_var;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* Solver::MakeMax(const std::vector<IntVar*>& vars) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (size == 0) {
|
2016-09-12 13:40:24 +02:00
|
|
|
LOG(WARNING) << "operations_research::Solver::MakeMax() was called with an "
|
|
|
|
|
"empty list of variables. Was this intentional?";
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeIntConst(std::numeric_limits<int64_t>::min());
|
2012-09-04 21:40:12 +00:00
|
|
|
} else if (size == 1) {
|
|
|
|
|
return vars[0];
|
|
|
|
|
} else if (size == 2) {
|
|
|
|
|
return MakeMax(vars[0], vars[1]);
|
|
|
|
|
} else {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* const cache =
|
2012-09-04 21:40:12 +00:00
|
|
|
model_cache_->FindVarArrayExpression(vars, ModelCache::VAR_ARRAY_MAX);
|
2013-10-10 15:23:20 +00:00
|
|
|
if (cache != nullptr) {
|
2013-07-19 03:47:59 +00:00
|
|
|
return cache;
|
2012-09-04 21:40:12 +00:00
|
|
|
} else {
|
|
|
|
|
if (AreAllBooleans(vars)) {
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const new_var = MakeBoolVar();
|
2012-09-04 21:40:12 +00:00
|
|
|
AddConstraint(RevAlloc(new ArrayBoolOrEq(this, vars, new_var)));
|
2013-07-24 00:28:11 +00:00
|
|
|
model_cache_->InsertVarArrayExpression(new_var, vars,
|
|
|
|
|
ModelCache::VAR_ARRAY_MIN);
|
2012-09-04 21:40:12 +00:00
|
|
|
return new_var;
|
|
|
|
|
} else {
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t new_min = std::numeric_limits<int64_t>::min();
|
|
|
|
|
int64_t new_max = std::numeric_limits<int64_t>::min();
|
2012-09-04 21:40:12 +00:00
|
|
|
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());
|
|
|
|
|
}
|
2020-10-29 14:25:39 +01:00
|
|
|
IntVar* const new_var = MakeIntVar(new_min, new_max);
|
2016-02-03 15:15:58 +01:00
|
|
|
if (size <= parameters_.array_split_size()) {
|
2014-07-18 00:29:58 +00:00
|
|
|
AddConstraint(RevAlloc(new SmallMaxConstraint(this, vars, new_var)));
|
|
|
|
|
} else {
|
|
|
|
|
AddConstraint(RevAlloc(new MaxConstraint(this, vars, new_var)));
|
|
|
|
|
}
|
2013-07-24 00:28:11 +00:00
|
|
|
model_cache_->InsertVarArrayExpression(new_var, vars,
|
|
|
|
|
ModelCache::VAR_ARRAY_MAX);
|
2012-09-04 21:40:12 +00:00
|
|
|
return new_var;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeMinEquality(const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const min_var) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (size > 2) {
|
|
|
|
|
if (AreAllBooleans(vars)) {
|
|
|
|
|
return RevAlloc(new ArrayBoolAndEq(this, vars, min_var));
|
2016-02-03 15:15:58 +01:00
|
|
|
} else if (size <= parameters_.array_split_size()) {
|
2014-07-18 00:14:31 +00:00
|
|
|
return RevAlloc(new SmallMinConstraint(this, vars, min_var));
|
2012-09-04 21:40:12 +00:00
|
|
|
} 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 {
|
2016-09-12 13:40:24 +02:00
|
|
|
LOG(WARNING) << "operations_research::Solver::MakeMinEquality() was called "
|
|
|
|
|
"with an empty list of variables. Was this intentional?";
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeEquality(min_var, std::numeric_limits<int64_t>::max());
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeMaxEquality(const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const max_var) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (size > 2) {
|
|
|
|
|
if (AreAllBooleans(vars)) {
|
|
|
|
|
return RevAlloc(new ArrayBoolOrEq(this, vars, max_var));
|
2016-02-03 15:15:58 +01:00
|
|
|
} else if (size <= parameters_.array_split_size()) {
|
2014-07-18 00:29:58 +00:00
|
|
|
return RevAlloc(new SmallMaxConstraint(this, vars, max_var));
|
2012-09-04 21:40:12 +00:00
|
|
|
} 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 {
|
2016-09-12 13:40:24 +02:00
|
|
|
LOG(WARNING) << "operations_research::Solver::MakeMaxEquality() was called "
|
|
|
|
|
"with an empty list of variables. Was this intentional?";
|
2021-04-01 12:13:35 +02:00
|
|
|
return MakeEquality(max_var, std::numeric_limits<int64_t>::min());
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeSumLessOrEqual(const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (cst == 1LL && AreAllBooleans(vars) && size > 2) {
|
2013-10-10 15:23:20 +00:00
|
|
|
return RevAlloc(new SumBooleanLessOrEqualToOne(this, vars));
|
2012-09-04 21:40:12 +00:00
|
|
|
} else {
|
|
|
|
|
return MakeLessOrEqual(MakeSum(vars), cst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeSumGreaterOrEqual(const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (cst == 1LL && AreAllBooleans(vars) && size > 2) {
|
2013-10-10 15:23:20 +00:00
|
|
|
return RevAlloc(new SumBooleanGreaterOrEqualToOne(this, vars));
|
2012-09-04 21:40:12 +00:00
|
|
|
} else {
|
|
|
|
|
return MakeGreaterOrEqual(MakeSum(vars), cst);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeSumEquality(const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (size == 0) {
|
|
|
|
|
return cst == 0 ? MakeTrueConstraint() : MakeFalseConstraint();
|
|
|
|
|
}
|
|
|
|
|
if (AreAllBooleans(vars) && size > 2) {
|
|
|
|
|
if (cst == 1) {
|
2013-10-10 15:23:20 +00:00
|
|
|
return RevAlloc(new SumBooleanEqualToOne(this, vars));
|
2012-09-04 21:40:12 +00:00
|
|
|
} else if (cst < 0 || cst > size) {
|
|
|
|
|
return MakeFalseConstraint();
|
|
|
|
|
} else {
|
2013-10-10 15:23:20 +00:00
|
|
|
return RevAlloc(new SumBooleanEqualToVar(this, vars, 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)));
|
2016-02-03 15:15:58 +01:00
|
|
|
} else if (size <= parameters_.array_split_size()) {
|
2014-07-18 21:32:02 +00:00
|
|
|
return RevAlloc(new SmallSumConstraint(this, vars, MakeIntConst(cst)));
|
2012-09-04 21:40:12 +00:00
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new SumConstraint(this, vars, MakeIntConst(cst)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeSumEquality(const std::vector<IntVar*>& vars,
|
|
|
|
|
IntVar* const var) {
|
2012-09-04 21:40:12 +00:00
|
|
|
const int size = vars.size();
|
|
|
|
|
if (size == 0) {
|
|
|
|
|
return MakeEquality(var, Zero());
|
|
|
|
|
}
|
|
|
|
|
if (AreAllBooleans(vars) && size > 2) {
|
2013-10-10 15:23:20 +00:00
|
|
|
return RevAlloc(new SumBooleanEqualToVar(this, vars, var));
|
2012-09-04 21:40:12 +00:00
|
|
|
} 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);
|
2014-08-15 16:56:56 +00:00
|
|
|
} else {
|
2012-09-04 21:40:12 +00:00
|
|
|
if (DetectSumOverflow(vars)) {
|
|
|
|
|
return RevAlloc(new SafeSumConstraint(this, vars, var));
|
2016-02-03 15:15:58 +01:00
|
|
|
} else if (size <= parameters_.array_split_size()) {
|
2014-07-18 21:32:02 +00:00
|
|
|
return RevAlloc(new SmallSumConstraint(this, vars, var));
|
2012-09-04 21:40:12 +00:00
|
|
|
} else {
|
|
|
|
|
return RevAlloc(new SumConstraint(this, vars, var));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeScalProdEquality(
|
|
|
|
|
const std::vector<IntVar*>& vars, const std::vector<int64_t>& coefficients,
|
|
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
DCHECK_EQ(vars.size(), coefficients.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdEqualityFct(this, vars, coefficients, cst);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeScalProdEquality(const std::vector<IntVar*>& vars,
|
|
|
|
|
const std::vector<int>& coefficients,
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
DCHECK_EQ(vars.size(), coefficients.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdEqualityFct(this, vars, ToInt64Vector(coefficients), cst);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeScalProdEquality(
|
|
|
|
|
const std::vector<IntVar*>& vars, const std::vector<int64_t>& coefficients,
|
|
|
|
|
IntVar* const target) {
|
2012-09-04 21:40:12 +00:00
|
|
|
DCHECK_EQ(vars.size(), coefficients.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdEqualityVarFct(this, vars, coefficients, target);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeScalProdEquality(const std::vector<IntVar*>& vars,
|
|
|
|
|
const std::vector<int>& coefficients,
|
|
|
|
|
IntVar* const target) {
|
2012-09-04 21:40:12 +00:00
|
|
|
DCHECK_EQ(vars.size(), coefficients.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdEqualityVarFct(this, vars, ToInt64Vector(coefficients),
|
|
|
|
|
target);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
2021-04-01 12:13:35 +02:00
|
|
|
Constraint* Solver::MakeScalProdGreaterOrEqual(
|
|
|
|
|
const std::vector<IntVar*>& vars, const std::vector<int64_t>& coeffs,
|
|
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
DCHECK_EQ(vars.size(), coeffs.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdGreaterOrEqualFct(this, vars, coeffs, cst);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeScalProdGreaterOrEqual(const std::vector<IntVar*>& vars,
|
|
|
|
|
const std::vector<int>& coeffs,
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
DCHECK_EQ(vars.size(), coeffs.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdGreaterOrEqualFct(this, vars, ToInt64Vector(coeffs), cst);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeScalProdLessOrEqual(
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<IntVar*>& vars, const std::vector<int64_t>& coefficients,
|
|
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
DCHECK_EQ(vars.size(), coefficients.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdLessOrEqualFct(this, vars, coefficients, cst);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
Constraint* Solver::MakeScalProdLessOrEqual(
|
|
|
|
|
const std::vector<IntVar*>& vars, const std::vector<int>& coefficients,
|
2021-04-01 12:13:35 +02:00
|
|
|
int64_t cst) {
|
2012-09-04 21:40:12 +00:00
|
|
|
DCHECK_EQ(vars.size(), coefficients.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdLessOrEqualFct(this, vars, ToInt64Vector(coefficients),
|
|
|
|
|
cst);
|
2012-09-04 21:40:12 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* Solver::MakeScalProd(const std::vector<IntVar*>& vars,
|
2021-04-01 12:13:35 +02:00
|
|
|
const std::vector<int64_t>& coefs) {
|
2012-05-30 12:53:19 +00:00
|
|
|
DCHECK_EQ(vars.size(), coefs.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdFct(this, vars, coefs);
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
|
|
|
|
|
2020-10-29 14:25:39 +01:00
|
|
|
IntExpr* Solver::MakeScalProd(const std::vector<IntVar*>& vars,
|
|
|
|
|
const std::vector<int>& coefs) {
|
2012-05-30 12:53:19 +00:00
|
|
|
DCHECK_EQ(vars.size(), coefs.size());
|
2013-10-10 15:23:20 +00:00
|
|
|
return MakeScalProdFct(this, vars, ToInt64Vector(coefs));
|
2010-09-15 12:42:33 +00:00
|
|
|
}
|
2020-10-22 23:36:58 +02:00
|
|
|
} // namespace operations_research
|