Files
ortools-clone/ortools/sat/presolve_util.cc
2019-12-09 17:57:13 +01:00

211 lines
6.5 KiB
C++

// Copyright 2010-2018 Google LLC
// 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.
#include "ortools/sat/presolve_util.h"
#include "ortools/base/map_util.h"
#include "ortools/sat/cp_model_utils.h"
namespace operations_research {
namespace sat {
void DomainDeductions::AddDeduction(int literal_ref, int var, Domain domain) {
CHECK_GE(var, 0);
const Index index = IndexFromLiteral(literal_ref);
if (index >= something_changed_.size()) {
something_changed_.Resize(index + 1);
enforcement_to_vars_.resize(index.value() + 1);
}
if (var >= tmp_num_occurrences_.size()) {
tmp_num_occurrences_.resize(var + 1, 0);
}
const auto insert = deductions_.insert({{index, var}, domain});
if (insert.second) {
// New element.
something_changed_.Set(index);
enforcement_to_vars_[index].push_back(var);
} else {
// Existing element.
const Domain& old_domain = insert.first->second;
if (!old_domain.IsIncludedIn(domain)) {
insert.first->second = domain.IntersectionWith(old_domain);
something_changed_.Set(index);
}
}
}
std::vector<std::pair<int, Domain>> DomainDeductions::ProcessClause(
absl::Span<const int> clause) {
std::vector<std::pair<int, Domain>> result;
// We only need to process this clause if something changed since last time.
bool abort = true;
for (const int ref : clause) {
const Index index = IndexFromLiteral(ref);
if (index >= something_changed_.size()) return result;
if (something_changed_[index]) {
abort = false;
}
}
if (abort) return result;
// Count for each variable, how many times it appears in the deductions lists.
std::vector<int> to_process;
std::vector<int> to_clean;
for (const int ref : clause) {
const Index index = IndexFromLiteral(ref);
for (const int var : enforcement_to_vars_[index]) {
if (tmp_num_occurrences_[var] == 0) {
to_clean.push_back(var);
}
tmp_num_occurrences_[var]++;
if (tmp_num_occurrences_[var] == clause.size()) {
to_process.push_back(var);
}
}
}
// Clear the counts.
for (const int var : to_clean) {
tmp_num_occurrences_[var] = 0;
}
// Compute the domain unions.
std::vector<Domain> domains(to_process.size());
for (const int ref : clause) {
const Index index = IndexFromLiteral(ref);
for (int i = 0; i < to_process.size(); ++i) {
domains[i] = domains[i].UnionWith(
gtl::FindOrDieNoPrint(deductions_, {index, to_process[i]}));
}
}
for (int i = 0; i < to_process.size(); ++i) {
result.push_back({to_process[i], std::move(domains[i])});
}
return result;
}
namespace {
// Helper method for variable substitution. Returns the coefficient of 'var' in
// 'proto' and copies other terms in 'terms'.
template <typename ProtoWithVarsAndCoeffs>
int64 GetVarCoeffAndCopyOtherTerms(const int var,
const ProtoWithVarsAndCoeffs& proto,
std::vector<std::pair<int, int64>>* terms) {
bool found = false;
int64 var_coeff = 0;
const int size = proto.vars().size();
for (int i = 0; i < size; ++i) {
int ref = proto.vars(i);
int64 coeff = proto.coeffs(i);
if (!RefIsPositive(ref)) {
ref = NegatedRef(ref);
coeff = -coeff;
}
if (ref == var) {
CHECK(!found);
found = true;
var_coeff = coeff;
continue;
} else {
terms->push_back({ref, coeff});
}
}
CHECK(found);
return var_coeff;
}
// Helper method for variable substituion. Sorts and merges the terms in 'terms'
// and adds them to 'proto'.
template <typename ProtoWithVarsAndCoeffs>
void SortAndMergeTerms(std::vector<std::pair<int, int64>>* terms,
ProtoWithVarsAndCoeffs* proto) {
proto->clear_vars();
proto->clear_coeffs();
std::sort(terms->begin(), terms->end());
int current_var = 0;
int64 current_coeff = 0;
for (const auto entry : *terms) {
CHECK(RefIsPositive(entry.first));
if (entry.first == current_var) {
current_coeff += entry.second;
} else {
if (current_coeff != 0) {
proto->add_vars(current_var);
proto->add_coeffs(current_coeff);
}
current_var = entry.first;
current_coeff = entry.second;
}
}
if (current_coeff != 0) {
proto->add_vars(current_var);
proto->add_coeffs(current_coeff);
}
}
// Adds all the terms from the var definition constraint with given var
// coefficient.
void AddTermsFromVarDefinition(const int var, const int64 var_coeff,
const ConstraintProto& definition,
std::vector<std::pair<int, int64>>* terms) {
const int definition_size = definition.linear().vars().size();
for (int i = 0; i < definition_size; ++i) {
int ref = definition.linear().vars(i);
int64 coeff = definition.linear().coeffs(i);
if (!RefIsPositive(ref)) {
ref = NegatedRef(ref);
coeff = -coeff;
}
if (ref == var) {
continue;
} else {
terms->push_back({ref, -coeff * var_coeff});
}
}
}
} // namespace
void SubstituteVariable(int var, int64 var_coeff_in_definition,
const ConstraintProto& definition,
ConstraintProto* ct) {
CHECK(RefIsPositive(var));
CHECK_EQ(std::abs(var_coeff_in_definition), 1);
// Copy all the terms (except the one refering to var).
std::vector<std::pair<int, int64>> terms;
int64 var_coeff = GetVarCoeffAndCopyOtherTerms(var, ct->linear(), &terms);
if (var_coeff_in_definition < 0) var_coeff *= -1;
AddTermsFromVarDefinition(var, var_coeff, definition, &terms);
// The substitution is correct only if we don't loose information here.
// But for a constant definition rhs that is always the case.
bool exact = false;
Domain offset = ReadDomainFromProto(definition.linear());
offset = offset.MultiplicationBy(-var_coeff, &exact);
CHECK(exact);
const Domain rhs = ReadDomainFromProto(ct->linear());
FillDomainInProto(rhs.AdditionWith(offset), ct->mutable_linear());
SortAndMergeTerms(&terms, ct->mutable_linear());
}
} // namespace sat
} // namespace operations_research