OR-Tools  9.3
affine_relation.h
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
14#ifndef OR_TOOLS_UTIL_AFFINE_RELATION_H_
15#define OR_TOOLS_UTIL_AFFINE_RELATION_H_
16
17#include <vector>
18
21#include "ortools/base/macros.h"
22
23namespace operations_research {
24
25// Union-Find algorithm to maintain "representative" for relations of the form:
26// x = coeff * y + offset, where "coeff" and "offset" are integers. The variable
27// x and y are represented by non-negative integer indices. The idea is to
28// express variables in affine relation using as little different variables as
29// possible (the representatives).
30//
31// IMPORTANT: If there are relations with std::abs(coeff) != 1, then some
32// relations might be ignored. See TryAdd() for more details.
33//
34// TODO(user): it might be possible to do something fancier and drop less
35// relations if all the affine relations are given before hand.
37 public:
38 AffineRelation() : num_relations_(0) {}
39
40 // Returns the number of relations added to the class and not ignored.
41 int NumRelations() const { return num_relations_; }
42
43 // Adds the relation x = coeff * y + offset to the class.
44 // Returns true if it wasn't ignored.
45 //
46 // This relation will only be taken into account if the representative of x
47 // and the one of y are different and if the relation can be transformed into
48 // a similar relation with integer coefficient between the two
49 // representatives.
50 //
51 // That is, given that:
52 // - x = coeff_x * representative_x + offset_x
53 // - y = coeff_y * representative_y + offset_y
54 // we have:
55 // coeff_x * representative_x + offset_x =
56 // coeff * coeff_y * representative_y + coeff * offset_y + offset.
57 // Which can be simplified with the introduction of new variables to:
58 // coeff_x * representative_x = new_coeff * representative_y + new_offset.
59 // And we can merge the two if:
60 // - new_coeff and new_offset are divisible by coeff_x.
61 // - OR coeff_x and new_offset are divisible by new_coeff.
62 //
63 // Checked preconditions: x >=0, y >= 0, coeff != 0 and x != y.
64 //
65 // IMPORTANT: we do not check for integer overflow, but that could be added
66 // if it is needed.
67 bool TryAdd(int x, int y, int64_t coeff, int64_t offset);
68
69 // Same as TryAdd() with the option to disallow the use of a given
70 // representative.
71 bool TryAdd(int x, int y, int64_t coeff, int64_t offset, bool allow_rep_x,
72 bool allow_rep_y);
73
74 // Returns a valid relation of the form x = coeff * representative + offset.
75 // Note that this can return x = x. Non-const because of path-compression.
76 struct Relation {
78 int64_t coeff;
79 int64_t offset;
80
81 Relation(int r, int64_t c, int64_t o)
82 : representative(r), coeff(c), offset(o) {}
83 explicit Relation(int r) : representative(r) {}
84
85 const bool operator==(const Relation& other) const {
86 return representative == other.representative && coeff == other.coeff &&
87 offset == other.offset;
88 }
89 };
90 Relation Get(int x) const;
91
92 // Advanced usage. This is a bit hacky and will just decrease the class size
93 // of a variable without any extra checks. Use with care. In particular when
94 // this is called, then x should never be used anymore in any of the non const
95 // calls of this class.
96 void IgnoreFromClassSize(int x) {
97 if (x >= size_.size()) return; // never seen here.
98 CHECK_NE(size_[x], kSizeForRemovedEntry) << x;
99 const int r = Get(x).representative;
100 if (r != x) {
101 CHECK_GT(size_[r], 1);
102 size_[r]--;
103 } else {
104 CHECK_EQ(size_[r], 1);
105 }
106 size_[x] = kSizeForRemovedEntry;
107 }
108
109 // Returns the size of the class of x.
110 int ClassSize(int x) const {
111 if (x >= representative_.size()) return 1;
112 return size_[Get(x).representative];
113 }
114
115 private:
116 const int kSizeForRemovedEntry = 0;
117
118 void IncreaseSizeOfMemberVectors(int new_size) {
119 if (new_size <= representative_.size()) return;
120 for (int i = representative_.size(); i < new_size; ++i) {
121 representative_.push_back(i);
122 }
123 offset_.resize(new_size, 0);
124 coeff_.resize(new_size, 1);
125 size_.resize(new_size, 1);
126 }
127
128 void CompressPath(int x) const {
129 DCHECK_GE(x, 0);
130 DCHECK_LT(x, representative_.size());
131 tmp_path_.clear();
132 int parent = x;
133 while (parent != representative_[parent]) {
134 tmp_path_.push_back(parent);
135 parent = representative_[parent];
136 }
137 for (const int var : ::gtl::reversed_view(tmp_path_)) {
138 const int old_parent = representative_[var];
139 offset_[var] += coeff_[var] * offset_[old_parent];
140 coeff_[var] *= coeff_[old_parent];
141 representative_[var] = parent;
142 }
143 }
144
145 int num_relations_;
146
147 // The equivalence class representative for each variable index.
148 mutable std::vector<int> representative_;
149
150 // The offset and coefficient such that
151 // variable[index] = coeff * variable[representative_[index]] + offset;
152 mutable std::vector<int64_t> coeff_;
153 mutable std::vector<int64_t> offset_;
154
155 // The size of each representative "tree", used to get a good complexity when
156 // we have the choice of which tree to merge into the other.
157 //
158 // TODO(user): Using a "rank" might be faster, but because we sometimes
159 // need to merge the bad subtree into the better one, it is trickier to
160 // maintain than in the classic union-find algorithm.
161 std::vector<int> size_;
162
163 // Used by CompressPath() to maintain the coeff/offset during compression.
164 mutable std::vector<int> tmp_path_;
165};
166
167inline bool AffineRelation::TryAdd(int x, int y, int64_t coeff,
168 int64_t offset) {
169 return TryAdd(x, y, coeff, offset, true, true);
170}
171
172inline bool AffineRelation::TryAdd(int x, int y, int64_t coeff, int64_t offset,
173 bool allow_rep_x, bool allow_rep_y) {
174 CHECK_NE(coeff, 0);
175 CHECK_NE(x, y);
176 CHECK_GE(x, 0);
177 CHECK_GE(y, 0);
178 IncreaseSizeOfMemberVectors(std::max(x, y) + 1);
179 CHECK_NE(size_[x], kSizeForRemovedEntry) << x;
180 CHECK_NE(size_[y], kSizeForRemovedEntry) << y;
181 CompressPath(x);
182 CompressPath(y);
183 const int rep_x = representative_[x];
184 const int rep_y = representative_[y];
185 if (rep_x == rep_y) return false;
186
187 // TODO(user): It should be possible to optimize this code block a bit, for
188 // instance depending on the magnitude of new_coeff vs coeff_x, we may already
189 // know that one of the two merge is not possible.
190 const int64_t coeff_x = coeff_[x];
191 const int64_t new_coeff = coeff * coeff_[y];
192 const int64_t new_offset = coeff * offset_[y] + offset - offset_[x];
193 const bool condition1 =
194 allow_rep_y && (new_coeff % coeff_x == 0) && (new_offset % coeff_x == 0);
195 const bool condition2 = allow_rep_x && (coeff_x % new_coeff == 0) &&
196 (new_offset % new_coeff == 0);
197 if (condition1 && (!condition2 || size_[x] <= size_[y])) {
198 representative_[rep_x] = rep_y;
199 size_[rep_y] += size_[rep_x];
200 coeff_[rep_x] = new_coeff / coeff_x;
201 offset_[rep_x] = new_offset / coeff_x;
202 } else if (condition2) {
203 representative_[rep_y] = rep_x;
204 size_[rep_x] += size_[rep_y];
205 coeff_[rep_y] = coeff_x / new_coeff;
206 offset_[rep_y] = -new_offset / new_coeff;
207 } else {
208 return false;
209 }
210 ++num_relations_;
211 return true;
212}
213
215 if (x >= representative_.size() || representative_[x] == x) return {x, 1, 0};
216 CompressPath(x);
217 return {representative_[x], coeff_[x], offset_[x]};
218}
219
220} // namespace operations_research
221
222#endif // OR_TOOLS_UTIL_AFFINE_RELATION_H_
int64_t max
Definition: alldiff_cst.cc:140
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#define CHECK_GE(val1, val2)
Definition: base/logging.h:707
#define CHECK_GT(val1, val2)
Definition: base/logging.h:708
#define DCHECK_GE(val1, val2)
Definition: base/logging.h:895
#define CHECK_NE(val1, val2)
Definition: base/logging.h:704
#define DCHECK_LT(val1, val2)
Definition: base/logging.h:894
bool TryAdd(int x, int y, int64_t coeff, int64_t offset)
IntVar * var
Definition: expr_array.cc:1874
ReverseView< Container > reversed_view(const Container &c)
Collection of objects used to extend the Constraint Solver library.
const bool operator==(const Relation &other) const
const double coeff