Files
ortools-clone/ortools/constraint_solver/diffn.cc

359 lines
13 KiB
C++
Raw Normal View History

2022-06-17 08:40:20 +02:00
// Copyright 2010-2022 Google LLC
2013-07-05 15:19:52 +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.
2013-07-05 15:19:52 +00:00
#include <algorithm>
2021-04-01 12:13:35 +02:00
#include <cstdint>
#include <limits>
2013-07-05 15:19:52 +00:00
#include <string>
2013-07-07 18:41:11 +00:00
#include <vector>
2013-07-05 15:19:52 +00:00
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
#include "absl/strings/str_format.h"
2018-06-08 16:40:43 +02:00
#include "ortools/base/hash.h"
2022-06-21 11:43:36 +02:00
#include "ortools/base/int_type.h"
#include "ortools/base/logging.h"
2023-08-18 16:14:58 +02:00
#include "ortools/base/types.h"
#include "ortools/constraint_solver/constraint_solver.h"
#include "ortools/constraint_solver/constraint_solveri.h"
#include "ortools/util/string_array.h"
2013-07-05 15:19:52 +00:00
namespace operations_research {
2013-07-29 23:56:21 +00:00
// Diffn constraint, Non overlapping boxs.
2013-07-05 15:19:52 +00:00
namespace {
2022-06-21 11:43:36 +02:00
DEFINE_INT_TYPE(Box, int);
2013-07-05 15:19:52 +00:00
class Diffn : public Constraint {
2020-10-22 23:36:58 +02:00
public:
2020-10-29 14:25:39 +01:00
Diffn(Solver* const solver, const std::vector<IntVar*>& x_vars,
const std::vector<IntVar*>& y_vars, const std::vector<IntVar*>& x_size,
const std::vector<IntVar*>& y_size, bool strict)
2020-10-22 23:36:58 +02:00
: Constraint(solver),
x_(x_vars),
y_(y_vars),
dx_(x_size),
dy_(y_size),
strict_(strict),
size_(x_vars.size()),
fail_stamp_(0) {
2013-07-05 15:19:52 +00:00
CHECK_EQ(x_vars.size(), y_vars.size());
CHECK_EQ(x_vars.size(), x_size.size());
CHECK_EQ(x_vars.size(), y_size.size());
}
~Diffn() override {}
2013-07-05 15:19:52 +00:00
void Post() override {
2020-10-29 14:25:39 +01:00
Solver* const s = solver();
2013-07-05 15:19:52 +00:00
for (int i = 0; i < size_; ++i) {
2020-10-29 14:25:39 +01:00
Demon* const demon = MakeConstraintDemon1(
2013-07-29 23:56:21 +00:00
s, this, &Diffn::OnBoxRangeChange, "OnBoxRangeChange", i);
2013-07-05 15:19:52 +00:00
x_[i]->WhenRange(demon);
y_[i]->WhenRange(demon);
dx_[i]->WhenRange(demon);
dy_[i]->WhenRange(demon);
}
2013-07-29 23:56:21 +00:00
delayed_demon_ = MakeDelayedConstraintDemon0(s, this, &Diffn::PropagateAll,
"PropagateAll");
if (solver()->parameters().diffn_use_cumulative() &&
2021-04-01 12:13:35 +02:00
IsArrayInRange<int64_t>(x_, 0, std::numeric_limits<int64_t>::max()) &&
IsArrayInRange<int64_t>(y_, 0, std::numeric_limits<int64_t>::max())) {
2020-10-29 14:25:39 +01:00
Constraint* ct1 = nullptr;
Constraint* ct2 = nullptr;
2013-07-29 23:56:21 +00:00
{
// We can add redundant cumulative constraints. This is done
// inside a c++ block to avoid leaking memory if adding the
// constraints leads to a failure. A cumulative constraint is
// a scheduling constraint that will perform finer energy
// based reasoning to do more propagation. (see Solver::MakeCumulative).
2021-04-01 12:13:35 +02:00
const int64_t min_x = MinVarArray(x_);
const int64_t max_x = MaxVarArray(x_);
const int64_t max_size_x = MaxVarArray(dx_);
const int64_t min_y = MinVarArray(y_);
const int64_t max_y = MaxVarArray(y_);
const int64_t max_size_y = MaxVarArray(dy_);
if (AreAllBound(dx_)) {
2021-04-01 12:13:35 +02:00
std::vector<int64_t> size_x;
FillValues(dx_, &size_x);
ct1 = MakeCumulativeConstraint(x_, size_x, dy_,
max_size_y + max_y - min_y);
}
if (AreAllBound(dy_)) {
2021-04-01 12:13:35 +02:00
std::vector<int64_t> size_y;
FillValues(dy_, &size_y);
ct2 = MakeCumulativeConstraint(y_, size_y, dx_,
max_size_x + max_x - min_x);
}
}
if (ct1 != nullptr) {
s->AddConstraint(ct1);
}
if (ct2 != nullptr) {
s->AddConstraint(ct2);
2013-07-29 23:56:21 +00:00
}
2013-07-05 15:19:52 +00:00
}
}
void InitialPropagate() override {
// All sizes should be >= 0.
2013-07-05 15:19:52 +00:00
for (int i = 0; i < size_; ++i) {
dx_[i]->SetMin(0);
dy_[i]->SetMin(0);
2013-07-05 15:19:52 +00:00
}
// Force propagation on all boxes.
to_propagate_.clear();
for (int i = 0; i < size_; i++) {
to_propagate_.insert(i);
}
PropagateAll();
}
std::string DebugString() const override {
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
return absl::StrFormat(
"Diffn(x = [%s], y = [%s], dx = [%s], dy = [%s]))",
JoinDebugStringPtr(x_, ", "), JoinDebugStringPtr(y_, ", "),
JoinDebugStringPtr(dx_, ", "), JoinDebugStringPtr(dy_, ", "));
2013-07-05 15:19:52 +00:00
}
2020-10-29 14:25:39 +01:00
void Accept(ModelVisitor* const visitor) const override {
2013-07-05 15:19:52 +00:00
visitor->BeginVisitConstraint(ModelVisitor::kDisjunctive, this);
2013-07-29 23:56:21 +00:00
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kPositionXArgument,
x_);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kPositionYArgument,
y_);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kSizeXArgument,
dx_);
visitor->VisitIntegerVariableArrayArgument(ModelVisitor::kSizeYArgument,
dy_);
2013-07-05 15:19:52 +00:00
visitor->EndVisitConstraint(ModelVisitor::kDisjunctive, this);
}
2020-10-22 23:36:58 +02:00
private:
2013-07-29 23:56:21 +00:00
void PropagateAll() {
for (const int box : to_propagate_) {
2013-07-29 23:56:21 +00:00
FillNeighbors(box);
FailWhenEnergyIsTooLarge(box);
PushOverlappingBoxes(box);
}
to_propagate_.clear();
fail_stamp_ = solver()->fail_stamp();
}
void OnBoxRangeChange(int box) {
if (solver()->fail_stamp() > fail_stamp_ && !to_propagate_.empty()) {
// We have failed in the last propagation and the to_propagate_
// was not cleared.
fail_stamp_ = solver()->fail_stamp();
to_propagate_.clear();
}
to_propagate_.insert(box);
EnqueueDelayedDemon(delayed_demon_);
}
bool CanBoxedOverlap(int i, int j) const {
if (AreBoxedDisjoingHorizontallyForSure(i, j) ||
AreBoxedDisjoingVerticallyForSure(i, j)) {
2013-07-05 15:19:52 +00:00
return false;
}
return true;
}
2013-07-29 23:56:21 +00:00
bool AreBoxedDisjoingHorizontallyForSure(int i, int j) const {
return (x_[i]->Min() >= x_[j]->Max() + dx_[j]->Max()) ||
(x_[j]->Min() >= x_[i]->Max() + dx_[i]->Max()) ||
(!strict_ && (dx_[i]->Min() == 0 || dx_[j]->Min() == 0));
2013-07-05 15:19:52 +00:00
}
2013-07-29 23:56:21 +00:00
bool AreBoxedDisjoingVerticallyForSure(int i, int j) const {
return (y_[i]->Min() >= y_[j]->Max() + dy_[j]->Max()) ||
(y_[j]->Min() >= y_[i]->Max() + dy_[i]->Max()) ||
(!strict_ && (dy_[i]->Min() == 0 || dy_[j]->Min() == 0));
2013-07-05 15:19:52 +00:00
}
2013-07-29 23:56:21 +00:00
// Fill neighbors_ with all boxes that can overlap the given box.
2013-07-05 15:19:52 +00:00
void FillNeighbors(int box) {
2013-07-29 23:56:21 +00:00
// TODO(user): We could maintain a non reversible list of
// neighbors and clean it after each failure.
2013-07-05 15:19:52 +00:00
neighbors_.clear();
for (int other = 0; other < size_; ++other) {
2013-07-29 23:56:21 +00:00
if (other != box && CanBoxedOverlap(other, box)) {
2013-07-05 15:19:52 +00:00
neighbors_.push_back(other);
}
}
}
2013-07-29 23:56:21 +00:00
// Fails if the minimum area of the given box plus the area of its neighbors
// (that must already be computed in neighbors_) is greater than the area of a
// bounding box that necessarily contains all these boxes.
void FailWhenEnergyIsTooLarge(int box) {
2021-04-01 12:13:35 +02:00
int64_t area_min_x = x_[box]->Min();
int64_t area_max_x = x_[box]->Max() + dx_[box]->Max();
int64_t area_min_y = y_[box]->Min();
int64_t area_max_y = y_[box]->Max() + dy_[box]->Max();
int64_t sum_of_areas = dx_[box]->Min() * dy_[box]->Min();
// TODO(user): Is there a better order, maybe sort by distance
2013-07-29 23:56:21 +00:00
// with the current box.
2013-07-05 15:19:52 +00:00
for (int i = 0; i < neighbors_.size(); ++i) {
const int other = neighbors_[i];
// Update Bounding box.
area_min_x = std::min(area_min_x, x_[other]->Min());
area_max_x = std::max(area_max_x, x_[other]->Max() + dx_[other]->Max());
area_min_y = std::min(area_min_y, y_[other]->Min());
area_max_y = std::max(area_max_y, y_[other]->Max() + dy_[other]->Max());
// Update sum of areas.
sum_of_areas += dx_[other]->Min() * dy_[other]->Min();
2021-04-01 12:13:35 +02:00
const int64_t bounding_area =
2013-07-05 15:19:52 +00:00
(area_max_x - area_min_x) * (area_max_y - area_min_y);
if (sum_of_areas > bounding_area) {
solver()->Fail();
}
}
}
2013-07-29 23:56:21 +00:00
// Changes the domain of all the neighbors of a given box (that must
// already be computed in neighbors_) so that they can't overlap the
// mandatory part of the given box.
2013-07-05 15:19:52 +00:00
void PushOverlappingBoxes(int box) {
for (int i = 0; i < neighbors_.size(); ++i) {
PushOneBox(box, neighbors_[i]);
2013-07-05 15:19:52 +00:00
}
}
2013-07-29 23:56:21 +00:00
// Changes the domain of the two given box by excluding the value that
// make them overlap for sure. Note that this function is symmetric in
// the sense that its argument can be swapped for the same result.
void PushOneBox(int box, int other) {
2013-07-29 23:56:21 +00:00
const int state =
(x_[box]->Min() + dx_[box]->Min() <= x_[other]->Max()) +
2013-07-29 23:56:21 +00:00
2 * (x_[other]->Min() + dx_[other]->Min() <= x_[box]->Max()) +
4 * (y_[box]->Min() + dy_[box]->Min() <= y_[other]->Max()) +
8 * (y_[other]->Min() + dy_[other]->Min() <= y_[box]->Max());
// This is an "hack" to be able to easily test for none or for one
// and only one of the conditions below.
switch (state) {
2020-10-22 23:36:58 +02:00
case 0: {
solver()->Fail();
break;
}
case 1: { // We push other left (x increasing).
x_[other]->SetMin(x_[box]->Min() + dx_[box]->Min());
x_[box]->SetMax(x_[other]->Max() - dx_[box]->Min());
dx_[box]->SetMax(x_[other]->Max() - x_[box]->Min());
break;
}
case 2: { // We push other right (x decreasing).
x_[box]->SetMin(x_[other]->Min() + dx_[other]->Min());
x_[other]->SetMax(x_[box]->Max() - dx_[other]->Min());
dx_[other]->SetMax(x_[box]->Max() - x_[other]->Min());
break;
}
case 4: { // We push other up (y increasing).
y_[other]->SetMin(y_[box]->Min() + dy_[box]->Min());
y_[box]->SetMax(y_[other]->Max() - dy_[box]->Min());
dy_[box]->SetMax(y_[other]->Max() - y_[box]->Min());
break;
}
case 8: { // We push other down (y decreasing).
y_[box]->SetMin(y_[other]->Min() + dy_[other]->Min());
y_[other]->SetMax(y_[box]->Max() - dy_[other]->Min());
dy_[other]->SetMax(y_[box]->Max() - y_[other]->Min());
break;
}
default: {
break;
}
2013-07-05 15:19:52 +00:00
}
}
2020-10-29 14:25:39 +01:00
Constraint* MakeCumulativeConstraint(const std::vector<IntVar*>& positions,
2021-04-01 12:13:35 +02:00
const std::vector<int64_t>& sizes,
2020-10-29 14:25:39 +01:00
const std::vector<IntVar*>& demands,
2021-04-01 12:13:35 +02:00
int64_t capacity) {
2020-10-29 14:25:39 +01:00
std::vector<IntervalVar*> intervals;
2013-07-29 23:56:21 +00:00
solver()->MakeFixedDurationIntervalVarArray(positions, sizes, "interval",
&intervals);
return solver()->MakeCumulative(intervals, demands, capacity, "cumul");
2013-07-05 15:19:52 +00:00
}
2020-10-29 14:25:39 +01:00
std::vector<IntVar*> x_;
std::vector<IntVar*> y_;
std::vector<IntVar*> dx_;
std::vector<IntVar*> dy_;
const bool strict_;
2021-04-01 12:13:35 +02:00
const int64_t size_;
2020-10-29 14:25:39 +01:00
Demon* delayed_demon_;
dotnet: Remove reference to dotnet release command - Currently not implemented... Add abseil patch - Add patches/absl-config.cmake Makefile: Add abseil-cpp on unix - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake Makefile: Add abseil-cpp on windows - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake CMake: Add abseil-cpp - Force abseil-cpp SHA1 to 45221cc note: Just before the PR #136 which break all CMake port to absl: C++ Part - Fix warning with the use of ABSL_MUST_USE_RESULT > The macro must appear as the very first part of a function declaration or definition: ... Note: past advice was to place the macro after the argument list. src: dependencies/sources/abseil-cpp-master/absl/base/attributes.h:418 - Rename enum after windows clash - Remove non compact table constraints - Change index type from int64 to int in routing library - Fix file_nonport compilation on windows - Fix another naming conflict with windows (NO_ERROR is a macro) - Cleanup hash containers; work on sat internals - Add optional_boolean sub-proto Sync cpp examples with internal code - reenable issue173 after reducing number of loops port to absl: Python Part - Add back cp_model.INT32_MIN|MAX for examples Update Python examples - Add random_tsp.py - Run words_square example - Run magic_square in python tests port to absl: Java Part - Fix compilation of the new routing parameters in java - Protect some code from SWIG parsing Update Java Examples port to absl: .Net Part Update .Net examples work on sat internals; Add C++ CP-SAT CpModelBuilder API; update sample code and recipes to use the new API; sync with internal code Remove VS 2015 in Appveyor-CI - abseil-cpp does not support VS 2015... improve tables upgrade C++ sat examples to use the new API; work on sat internals update license dates rewrite jobshop_ft06_distance.py to use the CP-SAT solver rename last example revert last commit more work on SAT internals fix
2018-10-31 16:18:18 +01:00
absl::flat_hash_set<int> to_propagate_;
2013-07-05 15:19:52 +00:00
std::vector<int> neighbors_;
2021-04-01 12:13:35 +02:00
uint64_t fail_stamp_;
2013-07-05 15:19:52 +00:00
};
2020-10-22 23:36:58 +02:00
} // namespace
2013-07-05 15:19:52 +00:00
2020-10-29 14:25:39 +01:00
Constraint* Solver::MakeNonOverlappingBoxesConstraint(
const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
const std::vector<IntVar*>& x_size, const std::vector<IntVar*>& y_size) {
return RevAlloc(new Diffn(this, x_vars, y_vars, x_size, y_size, true));
2013-07-05 15:19:52 +00:00
}
2013-07-29 23:56:21 +00:00
2020-10-29 14:25:39 +01:00
Constraint* Solver::MakeNonOverlappingBoxesConstraint(
const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
2021-04-01 12:13:35 +02:00
const std::vector<int64_t>& x_size, const std::vector<int64_t>& y_size) {
2020-10-29 14:25:39 +01:00
std::vector<IntVar*> dx(x_size.size());
std::vector<IntVar*> dy(y_size.size());
2013-07-29 23:56:21 +00:00
for (int i = 0; i < x_size.size(); ++i) {
dx[i] = MakeIntConst(x_size[i]);
dy[i] = MakeIntConst(y_size[i]);
}
return RevAlloc(new Diffn(this, x_vars, y_vars, dx, dy, true));
2013-07-29 23:56:21 +00:00
}
2020-10-29 14:25:39 +01:00
Constraint* Solver::MakeNonOverlappingBoxesConstraint(
const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
const std::vector<int>& x_size, const std::vector<int>& y_size) {
std::vector<IntVar*> dx(x_size.size());
std::vector<IntVar*> dy(y_size.size());
2013-07-29 23:56:21 +00:00
for (int i = 0; i < x_size.size(); ++i) {
dx[i] = MakeIntConst(x_size[i]);
dy[i] = MakeIntConst(y_size[i]);
}
return RevAlloc(new Diffn(this, x_vars, y_vars, dx, dy, true));
}
2020-10-29 14:25:39 +01:00
Constraint* Solver::MakeNonOverlappingNonStrictBoxesConstraint(
const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
const std::vector<IntVar*>& x_size, const std::vector<IntVar*>& y_size) {
return RevAlloc(new Diffn(this, x_vars, y_vars, x_size, y_size, false));
}
2020-10-29 14:25:39 +01:00
Constraint* Solver::MakeNonOverlappingNonStrictBoxesConstraint(
const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
2021-04-01 12:13:35 +02:00
const std::vector<int64_t>& x_size, const std::vector<int64_t>& y_size) {
2020-10-29 14:25:39 +01:00
std::vector<IntVar*> dx(x_size.size());
std::vector<IntVar*> dy(y_size.size());
for (int i = 0; i < x_size.size(); ++i) {
dx[i] = MakeIntConst(x_size[i]);
dy[i] = MakeIntConst(y_size[i]);
}
return RevAlloc(new Diffn(this, x_vars, y_vars, dx, dy, false));
}
2020-10-29 14:25:39 +01:00
Constraint* Solver::MakeNonOverlappingNonStrictBoxesConstraint(
const std::vector<IntVar*>& x_vars, const std::vector<IntVar*>& y_vars,
const std::vector<int>& x_size, const std::vector<int>& y_size) {
std::vector<IntVar*> dx(x_size.size());
std::vector<IntVar*> dy(y_size.size());
for (int i = 0; i < x_size.size(); ++i) {
dx[i] = MakeIntConst(x_size[i]);
dy[i] = MakeIntConst(y_size[i]);
}
return RevAlloc(new Diffn(this, x_vars, y_vars, dx, dy, false));
2013-07-29 23:56:21 +00:00
}
2020-10-22 23:36:58 +02:00
} // namespace operations_research