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 
14 #ifndef OR_TOOLS_SAT_SAT_DECISION_H_
15 #define OR_TOOLS_SAT_SAT_DECISION_H_
16 
17 #include <cstdint>
18 #include <vector>
19 
20 #include "ortools/base/integral_types.h"
21 #include "ortools/base/strong_vector.h"
22 #include "ortools/sat/model.h"
23 #include "ortools/sat/pb_constraint.h"
24 #include "ortools/sat/sat_base.h"
25 #include "ortools/sat/sat_parameters.pb.h"
26 #include "ortools/sat/util.h"
27 #include "ortools/util/bitset.h"
28 #include "ortools/util/integer_pq.h"
29 
30 namespace operations_research {
31 namespace sat {
32 
33 // Implement the SAT branching policy responsible for deciding the next Boolean
34 // variable to branch on, and its polarity (true or false).
35 class SatDecisionPolicy {
36  public:
37   explicit SatDecisionPolicy(Model* model);
38 
39   // Notifies that more variables are now present. Note that currently this may
40   // change the current variable order because the priority queue need to be
41   // reconstructed.
42   void IncreaseNumVariables(int num_variables);
43 
44   // Reinitializes the decision heuristics (which variables to choose with which
45   // polarity) according to the current parameters. Note that this also resets
46   // the activity of the variables to 0. Note that this function is lazy, and
47   // the work will only happen on the first NextBranch() to cover the cases when
48   // this policy is not used at all.
49   void ResetDecisionHeuristic();
50 
51   // Returns next decision to branch upon. This shouldn't be called if all the
52   // variables are assigned.
53   Literal NextBranch();
54 
55   // Updates statistics about literal occurences in constraints.
56   // Input is a canonical linear constraint of the form (terms <= rhs).
57   void UpdateWeightedSign(const std::vector<LiteralWithCoeff>& terms,
58                           Coefficient rhs);
59 
60   // Bumps the activity of all variables appearing in the conflict. All literals
61   // must be currently assigned. See VSIDS decision heuristic: Chaff:
62   // Engineering an Efficient SAT Solver. M.W. Moskewicz et al. ANNUAL ACM IEEE
63   // DESIGN AUTOMATION CONFERENCE 2001.
64   void BumpVariableActivities(const std::vector<Literal>& literals);
65 
66   // Updates the increment used for activity bumps. This is basically the same
67   // as decaying all the variable activities, but it is a lot more efficient.
68   void UpdateVariableActivityIncrement();
69 
70   // Called on Untrail() so that we can update the set of possible decisions.
71   void Untrail(int target_trail_index);
72 
73   // Called on a new conflict before Untrail(). The trail before the given index
74   // is used in the phase saving heuristic as a partial assignment.
75   void BeforeConflict(int trail_index);
76 
77   // By default, we alternate between a stable phase (better suited for finding
78   // SAT solution) and a more restart heavy phase more suited for proving UNSAT.
79   // This changes a bit the polarity heuristics and is controlled from within
80   // SatRestartPolicy.
SetStablePhase(bool is_stable)81   void SetStablePhase(bool is_stable) { in_stable_phase_ = is_stable; }
InStablePhase()82   bool InStablePhase() const { return in_stable_phase_; }
83 
84   // This is used to temporarily disable phase_saving when we do some probing
85   // during search for instance.
MaybeEnablePhaseSaving(bool save_phase)86   void MaybeEnablePhaseSaving(bool save_phase) {
87     maybe_enable_phase_saving_ = save_phase;
88   }
89 
90   // Gives a hint so the solver tries to find a solution with the given literal
91   // set to true. Currently this take precedence over the phase saving heuristic
92   // and a variable with a preference will always be branched on according to
93   // this preference.
94   //
95   // The weight is used as a tie-breaker between variable with the same
96   // activities. Larger weight will be selected first. A weight of zero is the
97   // default value for the other variables.
98   //
99   // Note(user): Having a lot of different weights may slow down the priority
100   // queue operations if there is millions of variables.
101   void SetAssignmentPreference(Literal literal, double weight);
102 
103   // Returns the vector of the current assignment preferences.
104   std::vector<std::pair<Literal, double>> AllPreferences() const;
105 
106  private:
107   // Computes an initial variable ordering.
108   void InitializeVariableOrdering();
109 
110   // Rescales activity value of all variables when one of them reached the max.
111   void RescaleVariableActivities(double scaling_factor);
112 
113   // Reinitializes the inital polarity of all the variables with an index
114   // greater than or equal to the given one.
115   void ResetInitialPolarity(int from, bool inverted = false);
116 
117   // Code used for resetting the initial polarity at the beginning of each
118   // phase.
119   void RephaseIfNeeded();
120   void UseLongestAssignmentAsInitialPolarity();
121   void FlipCurrentPolarity();
122   void RandomizeCurrentPolarity();
123 
124   // Adds the given variable to var_ordering_ or updates its priority if it is
125   // already present.
126   void PqInsertOrUpdate(BooleanVariable var);
127 
128   // Singleton model objects.
129   const SatParameters& parameters_;
130   const Trail& trail_;
131   ModelRandomGenerator* random_;
132 
133   // Variable ordering (priority will be adjusted dynamically). queue_elements_
134   // holds the elements used by var_ordering_ (it uses pointers).
135   //
136   // Note that we recover the variable that a WeightedVarQueueElement refers to
137   // by its position in the queue_elements_ vector, and we can recover the later
138   // using (pointer - &queue_elements_[0]).
139   struct WeightedVarQueueElement {
140     // Interface for the IntegerPriorityQueue.
IndexWeightedVarQueueElement141     int Index() const { return var.value(); }
142 
143     // Priority order. The IntegerPriorityQueue returns the largest element
144     // first.
145     //
146     // Note(user): We used to also break ties using the variable index, however
147     // this has two drawbacks:
148     // - On problem with many variables, this slow down quite a lot the priority
149     //   queue operations (which do as little work as possible and hence benefit
150     //   from having the majority of elements with a priority of 0).
151     // - It seems to be a bad heuristics. One reason could be that the priority
152     //   queue will automatically diversify the choice of the top variables
153     //   amongst the ones with the same priority.
154     //
155     // Note(user): For the same reason as explained above, it is probably a good
156     // idea not to have too many different values for the tie_breaker field. I
157     // am not even sure we should have such a field...
158     bool operator<(const WeightedVarQueueElement& other) const {
159       return weight < other.weight ||
160              (weight == other.weight && (tie_breaker < other.tie_breaker));
161     }
162 
163     BooleanVariable var;
164     float tie_breaker;
165 
166     // TODO(user): Experiment with float. In the rest of the code, we use
167     // double, but maybe we don't need that much precision. Using float here may
168     // save memory and make the PQ operations faster.
169     double weight;
170   };
171   static_assert(sizeof(WeightedVarQueueElement) == 16,
172                 "ERROR_WeightedVarQueueElement_is_not_well_compacted");
173 
174   bool var_ordering_is_initialized_ = false;
175   IntegerPriorityQueue<WeightedVarQueueElement> var_ordering_;
176 
177   // This is used for the branching heuristic described in "Learning Rate Based
178   // Branching Heuristic for SAT solvers", J.H.Liang, V. Ganesh, P. Poupart,
179   // K.Czarnecki, SAT 2016.
180   //
181   // The entries are sorted by trail index, and one can get the number of
182   // conflicts during which a variable at a given trail index i was assigned by
183   // summing the entry.count for all entries with a trail index greater than i.
184   struct NumConflictsStackEntry {
185     int trail_index;
186     int64_t count;
187   };
188   int64_t num_conflicts_ = 0;
189   std::vector<NumConflictsStackEntry> num_conflicts_stack_;
190 
191   // Whether the priority of the given variable needs to be updated in
192   // var_ordering_. Note that this is only accessed for assigned variables and
193   // that for efficiency it is indexed by trail indices. If
194   // pq_need_update_for_var_at_trail_index_[trail_->Info(var).trail_index] is
195   // true when we untrail var, then either var need to be inserted in the queue,
196   // or we need to notify that its priority has changed.
197   BitQueue64 pq_need_update_for_var_at_trail_index_;
198 
199   // Increment used to bump the variable activities.
200   double variable_activity_increment_ = 1.0;
201 
202   // Stores variable activity and the number of time each variable was "bumped".
203   // The later is only used with the ERWA heuristic.
204   absl::StrongVector<BooleanVariable, double> activities_;
205   absl::StrongVector<BooleanVariable, double> tie_breakers_;
206   absl::StrongVector<BooleanVariable, int64_t> num_bumps_;
207 
208   // If the polarity if forced (externally) we alway use this first.
209   absl::StrongVector<BooleanVariable, bool> has_forced_polarity_;
210   absl::StrongVector<BooleanVariable, bool> forced_polarity_;
211 
212   // If we are in a stable phase, we follow the current target.
213   bool in_stable_phase_ = false;
214   int target_length_ = 0;
215   absl::StrongVector<BooleanVariable, bool> has_target_polarity_;
216   absl::StrongVector<BooleanVariable, bool> target_polarity_;
217 
218   // Otherwise we follow var_polarity_ which is reset at the beginning of
219   // each new polarity phase. This is also overwritten by phase saving.
220   // Each phase last for an arithmetically increasing number of conflicts.
221   absl::StrongVector<BooleanVariable, bool> var_polarity_;
222   bool maybe_enable_phase_saving_ = true;
223   int64_t polarity_phase_ = 0;
224   int64_t num_conflicts_until_rephase_ = 1000;
225 
226   // The longest partial assignment since the last reset.
227   std::vector<Literal> best_partial_assignment_;
228 
229   // Used in initial polarity computation.
230   absl::StrongVector<BooleanVariable, double> weighted_sign_;
231 
232   // Used in InitializeVariableOrdering().
233   std::vector<BooleanVariable> tmp_variables_;
234 };
235 
236 }  // namespace sat
237 }  // namespace operations_research
238 
239 #endif  // OR_TOOLS_SAT_SAT_DECISION_H_
240