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