forked from google/or-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
implied_bounds.cc
166 lines (146 loc) · 6.1 KB
/
implied_bounds.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
// 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/implied_bounds.h"
namespace operations_research {
namespace sat {
// Just display some global statistics on destruction.
ImpliedBounds::~ImpliedBounds() {
VLOG(1) << num_deductions_ << " enqueued deductions.";
VLOG(1) << bounds_.size() << " implied bounds stored.";
VLOG(1) << num_enqueued_in_var_to_bounds_
<< " implied bounds with view stored.";
}
void ImpliedBounds::Add(Literal literal, IntegerLiteral integer_literal) {
if (!parameters_.use_implied_bounds()) return;
const IntegerVariable var = integer_literal.var;
// Update our local level-zero bound.
if (var >= level_zero_lower_bounds_.size()) {
level_zero_lower_bounds_.resize(var.value() + 1, kMinIntegerValue);
new_level_zero_bounds_.Resize(var + 1);
}
level_zero_lower_bounds_[var] = std::max(
level_zero_lower_bounds_[var], integer_trail_->LevelZeroLowerBound(var));
// Ignore any Add() with a bound worse than the level zero one.
// TODO(user): Check that this never happen? it shouldn't.
if (integer_literal.bound <= level_zero_lower_bounds_[var]) {
return;
}
// We skip any IntegerLiteral refering to a variable with only two consecutive
// possible values. This is because, once shifted this will already be a
// variable in [0, 1] so we shouldn't gain much by substituing it.
if (integer_trail_->LevelZeroUpperBound(var) -
integer_trail_->LevelZeroLowerBound(var) <=
1) {
return;
}
// Add or update the current bound.
const auto key = std::make_pair(literal.Index(), var);
auto insert_result = bounds_.insert({key, integer_literal.bound});
if (!insert_result.second) {
if (insert_result.first->second < integer_literal.bound) {
insert_result.first->second = integer_literal.bound;
} else {
// No new info.
return;
}
}
// Check if we have any deduction. Since at least one of (literal,
// literal.Negated()) must be true, we can take the min bound as valid at
// level zero.
//
// TODO(user): Like in probing, we can also create hole in the domain if there
// is some implied bounds for (literal.NegatedIndex, NegagtionOf(var)) that
// crosses integer_literal.bound.
const auto it = bounds_.find(std::make_pair(literal.NegatedIndex(), var));
if (it != bounds_.end()) {
const IntegerValue deduction = std::min(integer_literal.bound, it->second);
// TODO(user): support Enqueueing level zero fact at a positive level. That
// is, do not loose the info on backtrack. This should be doable. It is also
// why we return a bool in case of conflict when pushing deduction.
++num_deductions_;
level_zero_lower_bounds_[var] = deduction;
new_level_zero_bounds_.Set(var);
// The entries that are equal to the min no longer need to be stored once
// the level zero bound is enqueued.
if (it->second == deduction) {
bounds_.erase(it);
}
if (integer_literal.bound == deduction) {
bounds_.erase(std::make_pair(literal.Index(), var));
// No need to update var_to_bounds_ in this case.
return;
}
}
// If we have a new implied bound and the literal has a view, add it to
// var_to_bounds_. Note that we might add more than one entry with the same
// literal_view, and we will later need to lazily clean the vector up.
if (integer_encoder_->GetLiteralView(literal) != kNoIntegerVariable) {
if (var_to_bounds_.size() <= var) {
var_to_bounds_.resize(var.value() + 1);
}
++num_enqueued_in_var_to_bounds_;
var_to_bounds_[var].push_back({integer_encoder_->GetLiteralView(literal),
integer_literal.bound, true});
} else if (integer_encoder_->GetLiteralView(literal.Negated()) !=
kNoIntegerVariable) {
if (var_to_bounds_.size() <= var) {
var_to_bounds_.resize(var.value() + 1);
}
++num_enqueued_in_var_to_bounds_;
var_to_bounds_[var].push_back(
{integer_encoder_->GetLiteralView(literal.Negated()),
integer_literal.bound, false});
}
}
const std::vector<ImpliedBoundEntry>& ImpliedBounds::GetImpliedBounds(
IntegerVariable var) {
if (var >= var_to_bounds_.size()) return empty_implied_bounds_;
// Lazily remove obsolete entries from the vector.
// TODO(user): Check no duplicate and remove old entry if the enforcement
// is tighter.
int new_size = 0;
std::vector<ImpliedBoundEntry>& ref = var_to_bounds_[var];
const IntegerValue level_zero_lb = std::max(
level_zero_lower_bounds_[var], integer_trail_->LevelZeroLowerBound(var));
level_zero_lower_bounds_[var] = level_zero_lb;
for (const ImpliedBoundEntry& entry : ref) {
if (entry.lower_bound <= level_zero_lb) continue;
ref[new_size++] = entry;
}
ref.resize(new_size);
return ref;
}
void ImpliedBounds::ProcessIntegerTrail(Literal first_decision) {
if (!parameters_.use_implied_bounds()) return;
CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 1);
tmp_integer_literals_.clear();
integer_trail_->AppendNewBounds(&tmp_integer_literals_);
for (const IntegerLiteral lit : tmp_integer_literals_) {
Add(first_decision, lit);
}
}
bool ImpliedBounds::EnqueueNewDeductions() {
CHECK_EQ(sat_solver_->CurrentDecisionLevel(), 0);
for (const IntegerVariable var :
new_level_zero_bounds_.PositionsSetAtLeastOnce()) {
if (!integer_trail_->Enqueue(
IntegerLiteral::GreaterOrEqual(var, level_zero_lower_bounds_[var]),
{}, {})) {
return false;
}
}
new_level_zero_bounds_.SparseClearAll();
return sat_solver_->FinishPropagation();
}
} // namespace sat
} // namespace operations_research