Files
ortools-clone/ortools/constraint_solver/utilities.cc
Mizux Seiha 8bb54b04ef Bump Copyright to 2021
FYI:
find ortools \( -type d -name .git -prune \) -o -type f -print0 | xargs -0 sed -i 's/\(Copyright 2010\)-2018/\1-2021/g'
2021-04-01 21:00:53 +02:00

836 lines
24 KiB
C++

// Copyright 2010-2021 Google LLC
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
#include <cstdint>
#include <string>
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/strings/str_format.h"
#include "absl/strings/str_join.h"
#include "ortools/base/hash.h"
#include "ortools/base/integral_types.h"
#include "ortools/base/logging.h"
#include "ortools/base/map_util.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/util/bitset.h"
namespace operations_research {
// ---------- SmallRevBitSet ----------
SmallRevBitSet::SmallRevBitSet(int64_t size) : bits_(0LL) {
DCHECK_GT(size, 0);
DCHECK_LE(size, 64);
}
void SmallRevBitSet::SetToOne(Solver* const solver, int64_t pos) {
DCHECK_GE(pos, 0);
bits_.SetValue(solver, bits_.Value() | OneBit64(pos));
}
void SmallRevBitSet::SetToZero(Solver* const solver, int64_t pos) {
DCHECK_GE(pos, 0);
bits_.SetValue(solver, bits_.Value() & ~OneBit64(pos));
}
int64_t SmallRevBitSet::Cardinality() const {
return BitCount64(bits_.Value());
}
int64_t SmallRevBitSet::GetFirstOne() const {
if (bits_.Value() == 0) {
return -1;
}
return LeastSignificantBitPosition64(bits_.Value());
}
// ---------- RevBitSet ----------
RevBitSet::RevBitSet(int64_t size)
: size_(size),
length_(BitLength64(size)),
bits_(new uint64_t[length_]),
stamps_(new uint64_t[length_]) {
DCHECK_GE(size, 1);
memset(bits_, 0, sizeof(*bits_) * length_);
memset(stamps_, 0, sizeof(*stamps_) * length_);
}
RevBitSet::~RevBitSet() {
delete[] bits_;
delete[] stamps_;
}
void RevBitSet::Save(Solver* const solver, int offset) {
const uint64_t current_stamp = solver->stamp();
if (current_stamp > stamps_[offset]) {
stamps_[offset] = current_stamp;
solver->SaveValue(&bits_[offset]);
}
}
void RevBitSet::SetToOne(Solver* const solver, int64_t index) {
DCHECK_GE(index, 0);
DCHECK_LT(index, size_);
const int64_t offset = BitOffset64(index);
const int64_t pos = BitPos64(index);
if (!(bits_[offset] & OneBit64(pos))) {
Save(solver, offset);
bits_[offset] |= OneBit64(pos);
}
}
void RevBitSet::SetToZero(Solver* const solver, int64_t index) {
DCHECK_GE(index, 0);
DCHECK_LT(index, size_);
const int64_t offset = BitOffset64(index);
const int64_t pos = BitPos64(index);
if (bits_[offset] & OneBit64(pos)) {
Save(solver, offset);
bits_[offset] &= ~OneBit64(pos);
}
}
bool RevBitSet::IsSet(int64_t index) const {
DCHECK_GE(index, 0);
DCHECK_LT(index, size_);
return IsBitSet64(bits_, index);
}
int64_t RevBitSet::Cardinality() const {
int64_t card = 0;
for (int i = 0; i < length_; ++i) {
card += BitCount64(bits_[i]);
}
return card;
}
bool RevBitSet::IsCardinalityZero() const {
for (int i = 0; i < length_; ++i) {
if (bits_[i]) {
return false;
}
}
return true;
}
bool RevBitSet::IsCardinalityOne() const {
bool found_one = false;
for (int i = 0; i < length_; ++i) {
const uint64_t partial = bits_[i];
if (partial) {
if (!(partial & (partial - 1))) {
if (found_one) {
return false;
}
found_one = true;
} else {
return false;
}
}
}
return found_one;
}
int64_t RevBitSet::GetFirstBit(int start) const {
return LeastSignificantBitPosition64(bits_, start, size_ - 1);
}
void RevBitSet::ClearAll(Solver* const solver) {
for (int offset = 0; offset < length_; ++offset) {
if (bits_[offset]) {
Save(solver, offset);
bits_[offset] = uint64_t{0};
}
}
}
// ----- RevBitMatrix -----
RevBitMatrix::RevBitMatrix(int64_t rows, int64_t columns)
: RevBitSet(rows * columns), rows_(rows), columns_(columns) {
DCHECK_GE(rows, 1);
DCHECK_GE(columns, 1);
}
RevBitMatrix::~RevBitMatrix() {}
void RevBitMatrix::SetToOne(Solver* const solver, int64_t row, int64_t column) {
DCHECK_GE(row, 0);
DCHECK_LT(row, rows_);
DCHECK_GE(column, 0);
DCHECK_LT(column, columns_);
RevBitSet::SetToOne(solver, row * columns_ + column);
}
void RevBitMatrix::SetToZero(Solver* const solver, int64_t row,
int64_t column) {
DCHECK_GE(row, 0);
DCHECK_LT(row, rows_);
DCHECK_GE(column, 0);
DCHECK_LT(column, columns_);
RevBitSet::SetToZero(solver, row * columns_ + column);
}
int64_t RevBitMatrix::Cardinality(int row) const {
DCHECK_GE(row, 0);
DCHECK_LT(row, rows_);
const int start = row * columns_;
return BitCountRange64(bits_, start, start + columns_ - 1);
}
bool RevBitMatrix::IsCardinalityOne(int row) const {
// TODO(user) : Optimize this one.
return Cardinality(row) == 1;
}
bool RevBitMatrix::IsCardinalityZero(int row) const {
const int start = row * columns_;
return IsEmptyRange64(bits_, start, start + columns_ - 1);
}
int64_t RevBitMatrix::GetFirstBit(int row, int start) const {
DCHECK_GE(start, 0);
DCHECK_GE(row, 0);
DCHECK_LT(row, rows_);
DCHECK_LT(start, columns_);
const int beginning = row * columns_;
const int end = beginning + columns_ - 1;
int64_t position =
LeastSignificantBitPosition64(bits_, beginning + start, end);
if (position == -1) {
return -1;
} else {
return position - beginning;
}
}
void RevBitMatrix::ClearAll(Solver* const solver) {
RevBitSet::ClearAll(solver);
}
// ----- UnsortedNullableRevBitset -----
UnsortedNullableRevBitset::UnsortedNullableRevBitset(int bit_size)
: bit_size_(bit_size),
word_size_(BitLength64(bit_size)),
bits_(word_size_, 0),
active_words_(word_size_) {}
void UnsortedNullableRevBitset::Init(Solver* const solver,
const std::vector<uint64_t>& mask) {
CHECK_LE(mask.size(), word_size_);
for (int i = 0; i < mask.size(); ++i) {
if (mask[i]) {
bits_.SetValue(solver, i, mask[i]);
active_words_.Insert(solver, i);
}
}
}
bool UnsortedNullableRevBitset::RevSubtract(Solver* const solver,
const std::vector<uint64_t>& mask) {
bool changed = false;
to_remove_.clear();
for (int index : active_words_) {
if (index < mask.size() && (bits_[index] & mask[index]) != 0) {
changed = true;
const uint64_t result = bits_[index] & ~mask[index];
bits_.SetValue(solver, index, result);
if (result == 0) {
to_remove_.push_back(index);
}
}
}
CleanUpActives(solver);
return changed;
}
void UnsortedNullableRevBitset::CleanUpActives(Solver* const solver) {
// We remove indices of null words in reverse order, as this may be a simpler
// operations on the RevIntSet (no actual swap).
for (int i = to_remove_.size() - 1; i >= 0; --i) {
active_words_.Remove(solver, to_remove_[i]);
}
}
bool UnsortedNullableRevBitset::RevAnd(Solver* const solver,
const std::vector<uint64_t>& mask) {
bool changed = false;
to_remove_.clear();
for (int index : active_words_) {
if (index < mask.size()) {
if ((bits_[index] & ~mask[index]) != 0) {
changed = true;
const uint64_t result = bits_[index] & mask[index];
bits_.SetValue(solver, index, result);
if (result == 0) {
to_remove_.push_back(index);
}
}
} else {
// Zero the word as the mask is implicitely null.
changed = true;
bits_.SetValue(solver, index, 0);
to_remove_.push_back(index);
}
}
CleanUpActives(solver);
return changed;
}
bool UnsortedNullableRevBitset::Intersects(const std::vector<uint64_t>& mask,
int* support_index) {
DCHECK_GE(*support_index, 0);
DCHECK_LT(*support_index, word_size_);
if (mask[*support_index] & bits_[*support_index]) {
return true;
}
for (int index : active_words_) {
if (bits_[index] & mask[index]) {
*support_index = index;
return true;
}
}
return false;
}
// ----- PrintModelVisitor -----
namespace {
class PrintModelVisitor : public ModelVisitor {
public:
PrintModelVisitor() : indent_(0) {}
~PrintModelVisitor() override {}
// Header/footers.
void BeginVisitModel(const std::string& solver_name) override {
LOG(INFO) << "Model " << solver_name << " {";
Increase();
}
void EndVisitModel(const std::string& solver_name) override {
LOG(INFO) << "}";
Decrease();
CHECK_EQ(0, indent_);
}
void BeginVisitConstraint(const std::string& type_name,
const Constraint* const constraint) override {
LOG(INFO) << Spaces() << type_name;
Increase();
}
void EndVisitConstraint(const std::string& type_name,
const Constraint* const constraint) override {
Decrease();
}
void BeginVisitIntegerExpression(const std::string& type_name,
const IntExpr* const expr) override {
LOG(INFO) << Spaces() << type_name;
Increase();
}
void EndVisitIntegerExpression(const std::string& type_name,
const IntExpr* const expr) override {
Decrease();
}
void BeginVisitExtension(const std::string& type_name) override {
LOG(INFO) << Spaces() << type_name;
Increase();
}
void EndVisitExtension(const std::string& type_name) override { Decrease(); }
void VisitIntegerVariable(const IntVar* const variable,
IntExpr* const delegate) override {
if (delegate != nullptr) {
delegate->Accept(this);
} else {
if (variable->Bound() && variable->name().empty()) {
LOG(INFO) << Spaces() << variable->Min();
} else {
LOG(INFO) << Spaces() << variable->DebugString();
}
}
}
void VisitIntegerVariable(const IntVar* const variable,
const std::string& operation, int64_t value,
IntVar* const delegate) override {
LOG(INFO) << Spaces() << "IntVar";
Increase();
LOG(INFO) << Spaces() << value;
LOG(INFO) << Spaces() << operation;
delegate->Accept(this);
Decrease();
}
void VisitIntervalVariable(const IntervalVar* const variable,
const std::string& operation, int64_t value,
IntervalVar* const delegate) override {
if (delegate != nullptr) {
LOG(INFO) << Spaces() << operation << " <" << value << ", ";
Increase();
delegate->Accept(this);
Decrease();
LOG(INFO) << Spaces() << ">";
} else {
LOG(INFO) << Spaces() << variable->DebugString();
}
}
void VisitSequenceVariable(const SequenceVar* const sequence) override {
LOG(INFO) << Spaces() << sequence->DebugString();
}
// Variables.
void VisitIntegerArgument(const std::string& arg_name,
int64_t value) override {
LOG(INFO) << Spaces() << arg_name << ": " << value;
}
void VisitIntegerArrayArgument(const std::string& arg_name,
const std::vector<int64_t>& values) override {
LOG(INFO) << Spaces() << arg_name << ": [" << absl::StrJoin(values, ", ")
<< "]";
}
void VisitIntegerMatrixArgument(const std::string& arg_name,
const IntTupleSet& values) override {
const int rows = values.NumTuples();
const int columns = values.Arity();
std::string array = "[";
for (int i = 0; i < rows; ++i) {
if (i != 0) {
array.append(", ");
}
array.append("[");
for (int j = 0; j < columns; ++j) {
if (j != 0) {
array.append(", ");
}
absl::StrAppendFormat(&array, "%d", values.Value(i, j));
}
array.append("]");
}
array.append("]");
LOG(INFO) << Spaces() << arg_name << ": " << array;
}
void VisitIntegerExpressionArgument(const std::string& arg_name,
IntExpr* const argument) override {
set_prefix(absl::StrFormat("%s: ", arg_name));
Increase();
argument->Accept(this);
Decrease();
}
void VisitIntegerVariableArrayArgument(
const std::string& arg_name,
const std::vector<IntVar*>& arguments) override {
LOG(INFO) << Spaces() << arg_name << ": [";
Increase();
for (int i = 0; i < arguments.size(); ++i) {
arguments[i]->Accept(this);
}
Decrease();
LOG(INFO) << Spaces() << "]";
}
// Visit interval argument.
void VisitIntervalArgument(const std::string& arg_name,
IntervalVar* const argument) override {
set_prefix(absl::StrFormat("%s: ", arg_name));
Increase();
argument->Accept(this);
Decrease();
}
virtual void VisitIntervalArgumentArray(
const std::string& arg_name, const std::vector<IntervalVar*>& arguments) {
LOG(INFO) << Spaces() << arg_name << ": [";
Increase();
for (int i = 0; i < arguments.size(); ++i) {
arguments[i]->Accept(this);
}
Decrease();
LOG(INFO) << Spaces() << "]";
}
// Visit sequence argument.
void VisitSequenceArgument(const std::string& arg_name,
SequenceVar* const argument) override {
set_prefix(absl::StrFormat("%s: ", arg_name));
Increase();
argument->Accept(this);
Decrease();
}
virtual void VisitSequenceArgumentArray(
const std::string& arg_name, const std::vector<SequenceVar*>& arguments) {
LOG(INFO) << Spaces() << arg_name << ": [";
Increase();
for (int i = 0; i < arguments.size(); ++i) {
arguments[i]->Accept(this);
}
Decrease();
LOG(INFO) << Spaces() << "]";
}
std::string DebugString() const override { return "PrintModelVisitor"; }
private:
void Increase() { indent_ += 2; }
void Decrease() { indent_ -= 2; }
std::string Spaces() {
std::string result;
for (int i = 0; i < indent_ - 2 * (!prefix_.empty()); ++i) {
result.append(" ");
}
if (!prefix_.empty()) {
result.append(prefix_);
prefix_ = "";
}
return result;
}
void set_prefix(const std::string& prefix) { prefix_ = prefix; }
int indent_;
std::string prefix_;
};
// ---------- ModelStatisticsVisitor -----------
class ModelStatisticsVisitor : public ModelVisitor {
public:
ModelStatisticsVisitor()
: num_constraints_(0),
num_variables_(0),
num_expressions_(0),
num_casts_(0),
num_intervals_(0),
num_sequences_(0),
num_extensions_(0) {}
~ModelStatisticsVisitor() override {}
// Begin/End visit element.
void BeginVisitModel(const std::string& solver_name) override {
// Reset statistics.
num_constraints_ = 0;
num_variables_ = 0;
num_expressions_ = 0;
num_casts_ = 0;
num_intervals_ = 0;
num_sequences_ = 0;
num_extensions_ = 0;
already_visited_.clear();
constraint_types_.clear();
expression_types_.clear();
extension_types_.clear();
}
void EndVisitModel(const std::string& solver_name) override {
// Display statistics.
LOG(INFO) << "Model has:";
LOG(INFO) << " - " << num_constraints_ << " constraints.";
for (const auto& it : constraint_types_) {
LOG(INFO) << " * " << it.second << " " << it.first;
}
LOG(INFO) << " - " << num_variables_ << " integer variables.";
LOG(INFO) << " - " << num_expressions_ << " integer expressions.";
for (const auto& it : expression_types_) {
LOG(INFO) << " * " << it.second << " " << it.first;
}
LOG(INFO) << " - " << num_casts_ << " expressions casted into variables.";
LOG(INFO) << " - " << num_intervals_ << " interval variables.";
LOG(INFO) << " - " << num_sequences_ << " sequence variables.";
LOG(INFO) << " - " << num_extensions_ << " model extensions.";
for (const auto& it : extension_types_) {
LOG(INFO) << " * " << it.second << " " << it.first;
}
}
void BeginVisitConstraint(const std::string& type_name,
const Constraint* const constraint) override {
num_constraints_++;
AddConstraintType(type_name);
}
void BeginVisitIntegerExpression(const std::string& type_name,
const IntExpr* const expr) override {
AddExpressionType(type_name);
num_expressions_++;
}
void BeginVisitExtension(const std::string& type_name) override {
AddExtensionType(type_name);
num_extensions_++;
}
void VisitIntegerVariable(const IntVar* const variable,
IntExpr* const delegate) override {
num_variables_++;
Register(variable);
if (delegate) {
num_casts_++;
VisitSubArgument(delegate);
}
}
void VisitIntegerVariable(const IntVar* const variable,
const std::string& operation, int64_t value,
IntVar* const delegate) override {
num_variables_++;
Register(variable);
num_casts_++;
VisitSubArgument(delegate);
}
void VisitIntervalVariable(const IntervalVar* const variable,
const std::string& operation, int64_t value,
IntervalVar* const delegate) override {
num_intervals_++;
if (delegate) {
VisitSubArgument(delegate);
}
}
void VisitSequenceVariable(const SequenceVar* const sequence) override {
num_sequences_++;
for (int i = 0; i < sequence->size(); ++i) {
VisitSubArgument(sequence->Interval(i));
}
}
// Visit integer expression argument.
void VisitIntegerExpressionArgument(const std::string& arg_name,
IntExpr* const argument) override {
VisitSubArgument(argument);
}
void VisitIntegerVariableArrayArgument(
const std::string& arg_name,
const std::vector<IntVar*>& arguments) override {
for (int i = 0; i < arguments.size(); ++i) {
VisitSubArgument(arguments[i]);
}
}
// Visit interval argument.
void VisitIntervalArgument(const std::string& arg_name,
IntervalVar* const argument) override {
VisitSubArgument(argument);
}
void VisitIntervalArrayArgument(
const std::string& arg_name,
const std::vector<IntervalVar*>& arguments) override {
for (int i = 0; i < arguments.size(); ++i) {
VisitSubArgument(arguments[i]);
}
}
// Visit sequence argument.
void VisitSequenceArgument(const std::string& arg_name,
SequenceVar* const argument) override {
VisitSubArgument(argument);
}
void VisitSequenceArrayArgument(
const std::string& arg_name,
const std::vector<SequenceVar*>& arguments) override {
for (int i = 0; i < arguments.size(); ++i) {
VisitSubArgument(arguments[i]);
}
}
std::string DebugString() const override { return "ModelStatisticsVisitor"; }
private:
void Register(const BaseObject* const object) {
already_visited_.insert(object);
}
bool AlreadyVisited(const BaseObject* const object) {
return gtl::ContainsKey(already_visited_, object);
}
// T should derive from BaseObject
template <typename T>
void VisitSubArgument(T* object) {
if (!AlreadyVisited(object)) {
Register(object);
object->Accept(this);
}
}
void AddConstraintType(const std::string& constraint_type) {
constraint_types_[constraint_type]++;
}
void AddExpressionType(const std::string& expression_type) {
expression_types_[expression_type]++;
}
void AddExtensionType(const std::string& extension_type) {
extension_types_[extension_type]++;
}
absl::flat_hash_map<std::string, int> constraint_types_;
absl::flat_hash_map<std::string, int> expression_types_;
absl::flat_hash_map<std::string, int> extension_types_;
int num_constraints_;
int num_variables_;
int num_expressions_;
int num_casts_;
int num_intervals_;
int num_sequences_;
int num_extensions_;
absl::flat_hash_set<const BaseObject*> already_visited_;
};
// ---------- Variable Degree Visitor ---------
class VariableDegreeVisitor : public ModelVisitor {
public:
explicit VariableDegreeVisitor(
absl::flat_hash_map<const IntVar*, int>* const map)
: map_(map) {}
~VariableDegreeVisitor() override {}
// Begin/End visit element.
void VisitIntegerVariable(const IntVar* const variable,
IntExpr* const delegate) override {
IntVar* const var = const_cast<IntVar*>(variable);
if (gtl::ContainsKey(*map_, var)) {
(*map_)[var]++;
}
if (delegate) {
VisitSubArgument(delegate);
}
}
void VisitIntegerVariable(const IntVar* const variable,
const std::string& operation, int64_t value,
IntVar* const delegate) override {
IntVar* const var = const_cast<IntVar*>(variable);
if (gtl::ContainsKey(*map_, var)) {
(*map_)[var]++;
}
VisitSubArgument(delegate);
}
void VisitIntervalVariable(const IntervalVar* const variable,
const std::string& operation, int64_t value,
IntervalVar* const delegate) override {
if (delegate) {
VisitSubArgument(delegate);
}
}
void VisitSequenceVariable(const SequenceVar* const sequence) override {
for (int i = 0; i < sequence->size(); ++i) {
VisitSubArgument(sequence->Interval(i));
}
}
// Visit integer expression argument.
void VisitIntegerExpressionArgument(const std::string& arg_name,
IntExpr* const argument) override {
VisitSubArgument(argument);
}
void VisitIntegerVariableArrayArgument(
const std::string& arg_name,
const std::vector<IntVar*>& arguments) override {
for (int i = 0; i < arguments.size(); ++i) {
VisitSubArgument(arguments[i]);
}
}
// Visit interval argument.
void VisitIntervalArgument(const std::string& arg_name,
IntervalVar* const argument) override {
VisitSubArgument(argument);
}
void VisitIntervalArrayArgument(
const std::string& arg_name,
const std::vector<IntervalVar*>& arguments) override {
for (int i = 0; i < arguments.size(); ++i) {
VisitSubArgument(arguments[i]);
}
}
// Visit sequence argument.
void VisitSequenceArgument(const std::string& arg_name,
SequenceVar* const argument) override {
VisitSubArgument(argument);
}
void VisitSequenceArrayArgument(
const std::string& arg_name,
const std::vector<SequenceVar*>& arguments) override {
for (int i = 0; i < arguments.size(); ++i) {
VisitSubArgument(arguments[i]);
}
}
std::string DebugString() const override { return "VariableDegreeVisitor"; }
private:
// T should derive from BaseObject
template <typename T>
void VisitSubArgument(T* object) {
object->Accept(this);
}
absl::flat_hash_map<const IntVar*, int>* const map_;
};
} // namespace
ModelVisitor* Solver::MakePrintModelVisitor() {
return RevAlloc(new PrintModelVisitor);
}
ModelVisitor* Solver::MakeStatisticsModelVisitor() {
return RevAlloc(new ModelStatisticsVisitor);
}
ModelVisitor* Solver::MakeVariableDegreeVisitor(
absl::flat_hash_map<const IntVar*, int>* const map) {
return RevAlloc(new VariableDegreeVisitor(map));
}
// ----- Vector manipulations -----
std::vector<int64_t> ToInt64Vector(const std::vector<int>& input) {
std::vector<int64_t> result(input.size());
for (int i = 0; i < input.size(); ++i) {
result[i] = input[i];
}
return result;
}
} // namespace operations_research