Google OR-Tools v9.15
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
integer_base.cc
Go to the documentation of this file.
1// Copyright 2010-2025 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
15
16#include <algorithm>
17#include <cstdint>
18#include <numeric>
19#include <tuple>
20#include <utility>
21#include <vector>
22
23#include "absl/log/check.h"
24#include "ortools/util/bitset.h"
25
27
29 if (coeffs[0] == 0) vars[0] = kNoIntegerVariable;
30 if (coeffs[1] == 0) vars[1] = kNoIntegerVariable;
31
32 // Corner case when the underlying variable is the same.
35 // Make sure variable are positive before merging.
36 for (int i = 0; i < 2; ++i) {
37 if (!VariableIsPositive(vars[i])) {
38 coeffs[i] = -coeffs[i];
39 vars[i] = NegationOf(vars[i]);
40 }
41 }
42
43 coeffs[0] += coeffs[1];
44 coeffs[1] = 0;
46 if (coeffs[0] == 0) vars[0] = kNoIntegerVariable;
47 }
48
49 // Make sure coeff are positive.
50 for (int i = 0; i < 2; ++i) {
51 if (coeffs[i] < 0) {
52 coeffs[i] = -coeffs[i];
53 vars[i] = NegationOf(vars[i]);
54 }
55 }
56
57 // Make sure variable are sorted.
58 if (vars[0] > vars[1]) {
59 std::swap(vars[0], vars[1]);
60 std::swap(coeffs[0], coeffs[1]);
61 }
62}
63
65 const uint64_t gcd = std::gcd(coeffs[0].value(), coeffs[1].value());
66 if (gcd > 1) {
67 coeffs[0] /= gcd;
68 coeffs[1] /= gcd;
69 return IntegerValue(gcd);
70 }
71 return IntegerValue(1);
72}
73
75 bool negate = false;
76 if (coeffs[0] == 0) {
77 if (coeffs[1] != 0) {
78 negate = !VariableIsPositive(vars[1]);
79 }
80 } else {
81 negate = !VariableIsPositive(vars[0]);
82 }
83 if (negate) Negate();
84 return negate;
85}
86
88 IntegerValue& ub) {
90 if (coeffs[0] == 0 || coeffs[1] == 0) return false; // abort.
91
92 const bool negated = NegateForCanonicalization();
93 if (negated) {
94 // We need to be able to negate without overflow.
95 CHECK_GE(lb, kMinIntegerValue);
96 CHECK_LE(ub, kMaxIntegerValue);
97 std::swap(lb, ub);
98 lb = -lb;
99 ub = -ub;
100 }
101
102 // Do gcd division.
103 const uint64_t gcd = std::gcd(coeffs[0].value(), coeffs[1].value());
104 if (gcd > 1) {
105 coeffs[0] /= gcd;
106 coeffs[1] /= gcd;
107 ub = FloorRatio(ub, IntegerValue(gcd));
108 lb = CeilRatio(lb, IntegerValue(gcd));
109 }
110
111 CHECK(coeffs[0] != 0 || vars[0] == kNoIntegerVariable);
112 CHECK(coeffs[1] != 0 || vars[1] == kNoIntegerVariable);
113
114 return negated;
115}
116
118 for (int i : {0, 1}) {
119 if ((vars[i] == kNoIntegerVariable) != (coeffs[i] == 0)) {
120 return false;
121 }
122 }
123 if (vars[0] >= vars[1]) {
124 if (vars[0] == kNoIntegerVariable && vars[1] == kNoIntegerVariable) {
125 return true;
126 }
127 return false;
128 }
129
130 if (vars[0] == kNoIntegerVariable) return true;
131
132 return coeffs[0] > 0 && coeffs[1] > 0;
133}
134
136 int var_index, IntegerValue expr_lb, IntegerValue other_var_lb) const {
137 DCHECK_GE(var_index, 0);
138 DCHECK_LT(var_index, 2);
139 const IntegerValue coeff = coeffs[var_index];
140 const IntegerVariable var = vars[var_index];
141 DCHECK_NE(var, kNoIntegerVariable);
142 const IntegerVariable other_var = vars[1 - var_index];
143 const IntegerValue other_coeff = coeffs[1 - var_index];
144 if (other_var == kNoIntegerVariable) {
145 return AffineExpression(expr_lb);
146 }
147 DCHECK_GT(other_coeff, 0);
148
149 if (coeff == 1) {
150 return AffineExpression(other_var, -other_coeff, expr_lb);
151 }
152 DCHECK_GT(coeff, 1);
153 DCHECK_EQ(std::gcd(other_coeff.value(), coeff.value()), 1);
154
155 // We need to handle the case where the coefficient is not unit. We have
156 // a * x + b * y >= expr_lb (with a > 1 and b >= 1).
157 // x >= expr_lb / a - (b / a) * y
158 //
159 // We write b = ceil(b / a) * a - residual, with residual > 0 (since
160 // GCD(a, b)==1). Then we have:
161 // x >= expr_lb / a - ceil(b / a) * y + residual * y / a
162 // x >= expr_lb / a - ceil(b / a) * y + residual * y_lb / a
163 // x >= -ceil(b / a) * y + (expr_lb + residual * y_lb) / a
164 // But now we have
165 // x + ceil(b / a) * y >= (expr_lb + residual * y_lb) / a
166 // And since the lhs is an integer value, this is equivalent to
167 // x + ceil(b / a) * y >= ceil((expr_lb + residual * y_lb) / a)
168 const IntegerValue ceil_coeff_ratio = CeilRatio(other_coeff, coeff);
169 const IntegerValue residual = coeff * ceil_coeff_ratio - other_coeff;
170 return AffineExpression(other_var, -ceil_coeff_ratio,
171 CeilRatio(expr_lb + residual * other_var_lb, coeff));
172}
173
176 for (int i = 0; i < 2; ++i) {
178 coeffs[i] = -coeffs[i];
179 vars[i] = NegationOf(vars[i]);
180 }
181 }
182}
183
187 IntegerValue ub) {
188 const bool negated = expr.CanonicalizeAndUpdateBounds(lb, ub);
189
190 // We only store proper linear2.
191 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) {
193 }
194
195 auto [it, inserted] = best_bounds_.insert({expr, {lb, ub}});
196 if (inserted) {
197 std::pair<AddResult, AddResult> result = {
200 if (negated) std::swap(result.first, result.second);
201 return result;
202 }
203
204 const auto [known_lb, known_ub] = it->second;
205
206 std::pair<AddResult, AddResult> result = {
209 if (lb > known_lb) {
210 result.first = (it->second.first == kMinIntegerValue) ? AddResult::ADDED
212 it->second.first = lb;
213 }
214 if (ub < known_ub) {
215 result.second = (it->second.second == kMaxIntegerValue)
218 it->second.second = ub;
219 }
220 if (negated) std::swap(result.first, result.second);
221 return result;
222}
223
225 IntegerValue lb,
226 IntegerValue ub) const {
227 expr.CanonicalizeAndUpdateBounds(lb, ub);
228 if (expr.coeffs[0] == 0 || expr.coeffs[1] == 0) {
230 }
231
232 const auto it = best_bounds_.find(expr);
233 if (it != best_bounds_.end()) {
234 const auto [known_lb, known_ub] = it->second;
235 if (lb <= known_lb && ub >= known_ub) return RelationStatus::IS_TRUE;
236 if (lb > known_ub || ub < known_lb) return RelationStatus::IS_FALSE;
237 }
239}
240
241std::vector<std::pair<LinearExpression2, IntegerValue>>
243 std::vector<std::pair<LinearExpression2, IntegerValue>> root_relations_sorted;
244 root_relations_sorted.reserve(2 * best_bounds_.size());
245 for (const auto& [expr, bounds] : best_bounds_) {
246 if (bounds.first != kMinIntegerValue) {
247 LinearExpression2 negated_expr = expr;
248 negated_expr.Negate();
249 root_relations_sorted.push_back({negated_expr, -bounds.first});
250 }
251 if (bounds.second != kMaxIntegerValue) {
252 root_relations_sorted.push_back({expr, bounds.second});
253 }
254 }
255 std::sort(root_relations_sorted.begin(), root_relations_sorted.end());
256 return root_relations_sorted;
257}
258
259std::vector<std::tuple<LinearExpression2, IntegerValue, IntegerValue>>
261 std::vector<std::tuple<LinearExpression2, IntegerValue, IntegerValue>>
262 root_relations_sorted;
263 root_relations_sorted.reserve(best_bounds_.size());
264 for (const auto& [expr, bounds] : best_bounds_) {
265 root_relations_sorted.push_back({expr, bounds.first, bounds.second});
266 }
267 std::sort(root_relations_sorted.begin(), root_relations_sorted.end());
268 return root_relations_sorted;
269}
270
271} // namespace operations_research::sat
std::pair< AddResult, AddResult > Add(LinearExpression2 expr, IntegerValue lb, IntegerValue ub)
std::vector< std::tuple< LinearExpression2, IntegerValue, IntegerValue > > GetSortedNonTrivialBounds() const
std::vector< std::pair< LinearExpression2, IntegerValue > > GetSortedNonTrivialUpperBounds() const
RelationStatus GetStatus(LinearExpression2 expr, IntegerValue lb, IntegerValue ub) const
IntegerValue FloorRatio(IntegerValue dividend, IntegerValue positive_divisor)
constexpr IntegerValue kMaxIntegerValue(std::numeric_limits< IntegerValue::ValueType >::max() - 1)
IntegerValue CeilRatio(IntegerValue dividend, IntegerValue positive_divisor)
std::vector< IntegerVariable > NegationOf(absl::Span< const IntegerVariable > vars)
Definition integer.cc:52
constexpr IntegerValue kMinIntegerValue(-kMaxIntegerValue.value())
const IntegerVariable kNoIntegerVariable(-1)
IntegerVariable PositiveVariable(IntegerVariable i)
bool VariableIsPositive(IntegerVariable i)
AffineExpression GetAffineLowerBound(int var_index, IntegerValue expr_lb, IntegerValue other_var_lb) const
bool CanonicalizeAndUpdateBounds(IntegerValue &lb, IntegerValue &ub)