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