This repository has been archived by the owner on Nov 17, 2023. It is now read-only.
forked from rte-france/or-tools
-
Notifications
You must be signed in to change notification settings - Fork 0
/
sat_decision.h
202 lines (169 loc) · 8.34 KB
/
sat_decision.h
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
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
// 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.
#ifndef OR_TOOLS_SAT_SAT_DECISION_H_
#define OR_TOOLS_SAT_SAT_DECISION_H_
#include <vector>
#include "ortools/base/integral_types.h"
#include "ortools/sat/model.h"
#include "ortools/sat/pb_constraint.h"
#include "ortools/sat/sat_base.h"
#include "ortools/sat/sat_parameters.pb.h"
#include "ortools/util/bitset.h"
#include "ortools/util/integer_pq.h"
#include "ortools/util/random_engine.h"
namespace operations_research {
namespace sat {
// Implement the SAT branching policy responsible for deciding the next Boolean
// variable to branch on, and its polarity (true or false).
class SatDecisionPolicy {
public:
explicit SatDecisionPolicy(Model* model);
// Notifies that more variables are now present. Note that currently this may
// change the current variable order because the priority queue need to be
// reconstructed.
void IncreaseNumVariables(int num_variables);
// Reinitializes the decision heuristics (which variables to choose with which
// polarity) according to the current parameters. Note that this also resets
// the activity of the variables to 0. Note that this function is lazy, and
// the work will only happen on the first NextBranch() to cover the cases when
// this policy is not used at all.
void ResetDecisionHeuristic();
// Returns next decision to branch upon. This shouldn't be called if all the
// variables are assigned.
Literal NextBranch();
// Updates statistics about literal occurences in constraints.
// Input is a canonical linear constraint of the form (terms <= rhs).
void UpdateWeightedSign(const std::vector<LiteralWithCoeff>& terms,
Coefficient rhs);
// Bumps the activity of all variables appearing in the conflict. All literals
// must be currently assigned. See VSIDS decision heuristic: Chaff:
// Engineering an Efficient SAT Solver. M.W. Moskewicz et al. ANNUAL ACM IEEE
// DESIGN AUTOMATION CONFERENCE 2001.
void BumpVariableActivities(const std::vector<Literal>& literals);
// Updates the increment used for activity bumps. This is basically the same
// as decaying all the variable activities, but it is a lot more efficient.
void UpdateVariableActivityIncrement();
// Called on Untrail() so that we can update the set of possible decisions.
void Untrail(int target_trail_index);
// Called on a new conflict.
void OnConflict() {
if (parameters_.use_erwa_heuristic()) {
++num_conflicts_;
num_conflicts_stack_.push_back({trail_->Index(), 1});
}
}
// Gives a hint so the solver tries to find a solution with the given literal
// set to true. Currently this take precedence over the phase saving heuristic
// and a variable with a preference will always be branched on according to
// this preference.
//
// The weight is used as a tie-breaker between variable with the same
// activities. Larger weight will be selected first. A weight of zero is the
// default value for the other variables.
//
// Note(user): Having a lot of different weights may slow down the priority
// queue operations if there is millions of variables.
void SetAssignmentPreference(Literal literal, double weight);
// Returns the vector of the current assignment preferences.
std::vector<std::pair<Literal, double>> AllPreferences() const;
private:
// Computes an initial variable ordering.
void InitializeVariableOrdering();
// Rescales activity value of all variables when one of them reached the max.
void RescaleVariableActivities(double scaling_factor);
// Reinitializes the inital polarity of all the variables with an index
// greater than or equal to the given one.
void ResetInitialPolarity(int from);
// Adds the given variable to var_ordering_ or updates its priority if it is
// already present.
void PqInsertOrUpdate(BooleanVariable var);
// Singleton model objects.
const SatParameters& parameters_;
Trail* trail_;
random_engine_t* random_;
// Variable ordering (priority will be adjusted dynamically). queue_elements_
// holds the elements used by var_ordering_ (it uses pointers).
//
// Note that we recover the variable that a WeightedVarQueueElement refers to
// by its position in the queue_elements_ vector, and we can recover the later
// using (pointer - &queue_elements_[0]).
struct WeightedVarQueueElement {
// Interface for the IntegerPriorityQueue.
int Index() const { return var.value(); }
// Priority order. The IntegerPriorityQueue returns the largest element
// first.
//
// Note(user): We used to also break ties using the variable index, however
// this has two drawbacks:
// - On problem with many variables, this slow down quite a lot the priority
// queue operations (which do as little work as possible and hence benefit
// from having the majority of elements with a priority of 0).
// - It seems to be a bad heuristics. One reason could be that the priority
// queue will automatically diversify the choice of the top variables
// amongst the ones with the same priority.
//
// Note(user): For the same reason as explained above, it is probably a good
// idea not to have too many different values for the tie_breaker field. I
// am not even sure we should have such a field...
bool operator<(const WeightedVarQueueElement& other) const {
return weight < other.weight ||
(weight == other.weight && (tie_breaker < other.tie_breaker));
}
BooleanVariable var;
float tie_breaker;
// TODO(user): Experiment with float. In the rest of the code, we use
// double, but maybe we don't need that much precision. Using float here may
// save memory and make the PQ operations faster.
double weight;
};
COMPILE_ASSERT(sizeof(WeightedVarQueueElement) == 16,
ERROR_WeightedVarQueueElement_is_not_well_compacted);
bool var_ordering_is_initialized_ = false;
IntegerPriorityQueue<WeightedVarQueueElement> var_ordering_;
// This is used for the branching heuristic described in "Learning Rate Based
// Branching Heuristic for SAT solvers", J.H.Liang, V. Ganesh, P. Poupart,
// K.Czarnecki, SAT 2016.
//
// The entries are sorted by trail index, and one can get the number of
// conflicts during which a variable at a given trail index i was assigned by
// summing the entry.count for all entries with a trail index greater than i.
struct NumConflictsStackEntry {
int trail_index;
int64 count;
};
int64 num_conflicts_ = 0;
std::vector<NumConflictsStackEntry> num_conflicts_stack_;
// Whether the priority of the given variable needs to be updated in
// var_ordering_. Note that this is only accessed for assigned variables and
// that for efficiency it is indexed by trail indices. If
// pq_need_update_for_var_at_trail_index_[trail_->Info(var).trail_index] is
// true when we untrail var, then either var need to be inserted in the queue,
// or we need to notify that its priority has changed.
BitQueue64 pq_need_update_for_var_at_trail_index_;
// Increment used to bump the variable activities.
double variable_activity_increment_ = 1.0;
// Stores variable activity and the number of time each variable was "bumped".
// The later is only used with the ERWA heuristic.
gtl::ITIVector<BooleanVariable, double> activities_;
gtl::ITIVector<BooleanVariable, double> tie_breakers_;
gtl::ITIVector<BooleanVariable, int64> num_bumps_;
// Used by NextBranch() to choose the polarity of the next decision. For the
// phase saving, the last polarity is stored in trail_->Info(var).
gtl::ITIVector<BooleanVariable, bool> var_use_phase_saving_;
gtl::ITIVector<BooleanVariable, bool> var_polarity_;
// Used in initial polarity computation.
gtl::ITIVector<BooleanVariable, double> weighted_sign_;
};
} // namespace sat
} // namespace operations_research
#endif // OR_TOOLS_SAT_SAT_DECISION_H_