OR-Tools  9.1
presolve_util.cc
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
15
16#include <cstdint>
17
20
21namespace operations_research {
22namespace sat {
23
24void DomainDeductions::AddDeduction(int literal_ref, int var, Domain domain) {
25 CHECK_GE(var, 0);
26 const Index index = IndexFromLiteral(literal_ref);
27 if (index >= something_changed_.size()) {
28 something_changed_.Resize(index + 1);
29 enforcement_to_vars_.resize(index.value() + 1);
30 }
31 if (var >= tmp_num_occurrences_.size()) {
32 tmp_num_occurrences_.resize(var + 1, 0);
33 }
34 const auto insert = deductions_.insert({{index, var}, domain});
35 if (insert.second) {
36 // New element.
37 something_changed_.Set(index);
38 enforcement_to_vars_[index].push_back(var);
39 } else {
40 // Existing element.
41 const Domain& old_domain = insert.first->second;
42 if (!old_domain.IsIncludedIn(domain)) {
43 insert.first->second = domain.IntersectionWith(old_domain);
44 something_changed_.Set(index);
45 }
46 }
47}
48
49std::vector<std::pair<int, Domain>> DomainDeductions::ProcessClause(
50 absl::Span<const int> clause) {
51 std::vector<std::pair<int, Domain>> result;
52
53 // We only need to process this clause if something changed since last time.
54 bool abort = true;
55 for (const int ref : clause) {
56 const Index index = IndexFromLiteral(ref);
57 if (index >= something_changed_.size()) return result;
58 if (something_changed_[index]) {
59 abort = false;
60 }
61 }
62 if (abort) return result;
63
64 // Count for each variable, how many times it appears in the deductions lists.
65 std::vector<int> to_process;
66 std::vector<int> to_clean;
67 for (const int ref : clause) {
68 const Index index = IndexFromLiteral(ref);
69 for (const int var : enforcement_to_vars_[index]) {
70 if (tmp_num_occurrences_[var] == 0) {
71 to_clean.push_back(var);
72 }
73 tmp_num_occurrences_[var]++;
74 if (tmp_num_occurrences_[var] == clause.size()) {
75 to_process.push_back(var);
76 }
77 }
78 }
79
80 // Clear the counts.
81 for (const int var : to_clean) {
82 tmp_num_occurrences_[var] = 0;
83 }
84
85 // Compute the domain unions.
86 std::vector<Domain> domains(to_process.size());
87 for (const int ref : clause) {
88 const Index index = IndexFromLiteral(ref);
89 for (int i = 0; i < to_process.size(); ++i) {
90 domains[i] = domains[i].UnionWith(
91 gtl::FindOrDieNoPrint(deductions_, {index, to_process[i]}));
92 }
93 }
94
95 for (int i = 0; i < to_process.size(); ++i) {
96 result.push_back({to_process[i], std::move(domains[i])});
97 }
98 return result;
99}
100
101namespace {
102// Helper method for variable substitution. Returns the coefficient of 'var' in
103// 'proto' and copies other terms in 'terms'.
104template <typename ProtoWithVarsAndCoeffs>
105int64_t GetVarCoeffAndCopyOtherTerms(
106 const int var, const ProtoWithVarsAndCoeffs& proto,
107 std::vector<std::pair<int, int64_t>>* terms) {
108 bool found = false;
109 int64_t var_coeff = 0;
110 const int size = proto.vars().size();
111 for (int i = 0; i < size; ++i) {
112 int ref = proto.vars(i);
113 int64_t coeff = proto.coeffs(i);
114 if (!RefIsPositive(ref)) {
115 ref = NegatedRef(ref);
116 coeff = -coeff;
117 }
118
119 if (ref == var) {
120 CHECK(!found);
121 found = true;
122 var_coeff = coeff;
123 continue;
124 } else {
125 terms->push_back({ref, coeff});
126 }
127 }
128 CHECK(found);
129 return var_coeff;
130}
131
132// Helper method for variable substituion. Sorts and merges the terms in 'terms'
133// and adds them to 'proto'.
134template <typename ProtoWithVarsAndCoeffs>
135void SortAndMergeTerms(std::vector<std::pair<int, int64_t>>* terms,
136 ProtoWithVarsAndCoeffs* proto) {
137 proto->clear_vars();
138 proto->clear_coeffs();
139 std::sort(terms->begin(), terms->end());
140 int current_var = 0;
141 int64_t current_coeff = 0;
142 for (const auto entry : *terms) {
143 CHECK(RefIsPositive(entry.first));
144 if (entry.first == current_var) {
145 current_coeff += entry.second;
146 } else {
147 if (current_coeff != 0) {
148 proto->add_vars(current_var);
149 proto->add_coeffs(current_coeff);
150 }
151 current_var = entry.first;
152 current_coeff = entry.second;
153 }
154 }
155 if (current_coeff != 0) {
156 proto->add_vars(current_var);
157 proto->add_coeffs(current_coeff);
158 }
159}
160
161// Adds all the terms from the var definition constraint with given var
162// coefficient.
163void AddTermsFromVarDefinition(const int var, const int64_t var_coeff,
164 const ConstraintProto& definition,
165 std::vector<std::pair<int, int64_t>>* terms) {
166 const int definition_size = definition.linear().vars().size();
167 for (int i = 0; i < definition_size; ++i) {
168 int ref = definition.linear().vars(i);
169 int64_t coeff = definition.linear().coeffs(i);
170 if (!RefIsPositive(ref)) {
171 ref = NegatedRef(ref);
172 coeff = -coeff;
173 }
174
175 if (ref == var) {
176 continue;
177 } else {
178 terms->push_back({ref, -coeff * var_coeff});
179 }
180 }
181}
182} // namespace
183
184void SubstituteVariable(int var, int64_t var_coeff_in_definition,
185 const ConstraintProto& definition,
188 CHECK_EQ(std::abs(var_coeff_in_definition), 1);
189
190 // Copy all the terms (except the one referring to var).
191 std::vector<std::pair<int, int64_t>> terms;
192 int64_t var_coeff = GetVarCoeffAndCopyOtherTerms(var, ct->linear(), &terms);
193
194 if (var_coeff_in_definition < 0) var_coeff *= -1;
195
196 AddTermsFromVarDefinition(var, var_coeff, definition, &terms);
197
198 // The substitution is correct only if we don't loose information here.
199 // But for a constant definition rhs that is always the case.
200 bool exact = false;
201 Domain offset = ReadDomainFromProto(definition.linear());
202 offset = offset.MultiplicationBy(-var_coeff, &exact);
203 CHECK(exact);
204
205 const Domain rhs = ReadDomainFromProto(ct->linear());
206 FillDomainInProto(rhs.AdditionWith(offset), ct->mutable_linear());
207
208 SortAndMergeTerms(&terms, ct->mutable_linear());
209}
210
211} // namespace sat
212} // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:491
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:698
#define CHECK_GE(val1, val2)
Definition: base/logging.h:702
We call domain any subset of Int64 = [kint64min, kint64max].
bool IsIncludedIn(const Domain &domain) const
Returns true iff D is included in the given domain.
Domain AdditionWith(const Domain &domain) const
Returns {x ∈ Int64, ∃ a ∈ D, ∃ b ∈ domain, x = a + b}.
Domain MultiplicationBy(int64_t coeff, bool *exact=nullptr) const
Returns {x ∈ Int64, ∃ e ∈ D, x = e * coeff}.
Domain IntersectionWith(const Domain &domain) const
Returns the intersection of D and domain.
const ::operations_research::sat::LinearConstraintProto & linear() const
std::vector< std::pair< int, Domain > > ProcessClause(absl::Span< const int > clause)
void AddDeduction(int literal_ref, int var, Domain domain)
CpModelProto proto
const Constraint * ct
IntVar * var
Definition: expr_array.cc:1874
const Collection::value_type::second_type & FindOrDieNoPrint(const Collection &collection, const typename Collection::value_type::first_type &key)
Definition: map_util.h:216
bool RefIsPositive(int ref)
void SubstituteVariable(int var, int64_t var_coeff_in_definition, const ConstraintProto &definition, ConstraintProto *ct)
void FillDomainInProto(const Domain &domain, ProtoWithDomain *proto)
Domain ReadDomainFromProto(const ProtoWithDomain &proto)
Collection of objects used to extend the Constraint Solver library.
int index
Definition: pack.cc:509