20#include "absl/algorithm/container.h"
21#include "absl/cleanup/cleanup.h"
22#include "absl/container/btree_set.h"
23#include "absl/log/check.h"
24#include "absl/log/log.h"
25#include "absl/types/span.h"
34 bool minimize_new_clauses_only) {
38 sat_solver_->AdvanceDeterministicTime(time_limit_);
39 const double threshold =
40 time_limit_->GetElapsedDeterministicTime() + dtime_budget;
44 if (sat_solver_->AssumptionLevel() > 0)
return true;
48 sat_solver_->BlockClauseDeletion(
true);
49 absl::Cleanup unblock_clause_deletion = [&] {
50 sat_solver_->BlockClauseDeletion(
false);
53 const auto old_counter = counters_;
54 const int num_resets = clause_manager_->NumToMinimizeIndexResets();
55 while (!time_limit_->LimitReached()) {
57 sat_solver_->AdvanceDeterministicTime(time_limit_);
58 if (time_limit_->GetElapsedDeterministicTime() >= threshold)
break;
61 if (clause_manager_->NumToMinimizeIndexResets() > num_resets + 1)
break;
65 SatClause* to_minimize = clause_manager_->NextNewClauseToMinimize();
66 if (to_minimize !=
nullptr) {
67 if (!TryToMinimizeClause(to_minimize))
return false;
70 if (minimize_new_clauses_only)
break;
73 SatClause* clause = clause_manager_->NextClauseToMinimize();
74 if (clause !=
nullptr) {
75 if (!TryToMinimizeClause(clause))
return false;
82 const bool result = sat_solver_->ResetToLevelZero();
85 counters_.num_clauses_vivified - old_counter.num_clauses_vivified;
86 last_num_literals_removed_ =
87 counters_.num_removed_literals - old_counter.num_removed_literals;
88 timer.
AddCounter(
"num_vivifed", last_num_vivified_);
89 timer.
AddCounter(
"literals_removed", last_num_literals_removed_);
90 timer.
AddCounter(
"loops", clause_manager_->NumToMinimizeIndexResets());
92 clause_manager_->DeleteRemovedClauses();
96void Vivifier::KeepAllClausesUsedToInfer(BooleanVariable variable) {
98 if (trail_->
Info(variable).
level == 0)
return;
100 std::vector<bool> is_marked(trail_index + 1,
false);
101 is_marked[trail_index] =
true;
103 for (; num > 0 && trail_index >= 0; --trail_index) {
104 if (!is_marked[trail_index])
continue;
105 is_marked[trail_index] =
false;
108 const BooleanVariable var = (*trail_)[trail_index].Variable();
110 if (clause !=
nullptr) {
116 for (
const Literal l : trail_->
Reason(var)) {
117 const AssignmentInfo& info = trail_->
Info(l.Variable());
118 if (info.level == 0)
continue;
119 if (!is_marked[info.trail_index]) {
120 is_marked[info.trail_index] =
true;
127bool Vivifier::SubsumptionIsInteresting(BooleanVariable variable,
132 const int binary_id = binary_clauses_->PropagatorId();
133 const int clause_id = clause_manager_->PropagatorId();
135 CHECK(assignment_.VariableIsAssigned(variable));
136 if (trail_->Info(variable).level == 0)
return true;
137 int trail_index = trail_->Info(variable).trail_index;
138 std::vector<bool> is_marked(trail_index + 1,
false);
139 is_marked[trail_index] =
true;
141 int num_clause_to_mark_as_non_deletable = 0;
142 for (; num > 0 && trail_index >= 0; --trail_index) {
143 if (!is_marked[trail_index])
continue;
144 is_marked[trail_index] =
false;
147 const BooleanVariable var = (*trail_)[trail_index].Variable();
148 const int type = trail_->AssignmentType(var);
150 if (type != binary_id && type != clause_id)
return false;
151 SatClause* clause = clause_manager_->ReasonClauseOrNull(var);
152 if (clause !=
nullptr && clause_manager_->IsRemovable(clause)) {
153 if (clause->size() > max_size) {
156 if (++num_clause_to_mark_as_non_deletable > 1)
return false;
158 for (
const Literal l : trail_->Reason(var)) {
159 const AssignmentInfo& info = trail_->Info(l.Variable());
160 if (info.level == 0)
continue;
161 if (!is_marked[info.trail_index]) {
162 is_marked[info.trail_index] =
true;
167 return num_clause_to_mark_as_non_deletable <= 1;
174bool Vivifier::TryToMinimizeClause(
SatClause* clause) {
175 CHECK(clause !=
nullptr);
176 if (clause->empty())
return true;
177 ++counters_.num_clauses_vivified;
181 std::vector<Literal> candidate;
182 candidate.reserve(clause->size());
187 std::vector<Literal> fixed_false_literals;
198 int longest_valid_prefix = 0;
199 if (sat_solver_->CurrentDecisionLevel() > 0) {
200 candidate.resize(clause->size());
203 const auto& decisions = trail_->Decisions();
204 for (Literal lit : *clause) {
205 if (!assignment_.LiteralIsFalse(lit))
continue;
206 const AssignmentInfo& info = trail_->Info(lit.Variable());
207 if (info.level <= 0 || info.level > clause->size())
continue;
208 if (decisions[info.level - 1].literal == lit.Negated()) {
209 candidate[info.level - 1] = lit;
213 for (
int i = 0;
i < candidate.size(); ++
i) {
214 if (candidate[
i] != Literal()) {
215 ++longest_valid_prefix;
220 counters_.num_reused += longest_valid_prefix;
221 candidate.resize(longest_valid_prefix);
224 for (Literal lit : *clause) {
225 const AssignmentInfo& info = trail_->Info(lit.Variable());
227 if (info.level >= 1 && info.level <= longest_valid_prefix &&
228 candidate[info.level - 1] == lit) {
231 candidate.push_back(lit);
233 CHECK_EQ(candidate.size(), clause->size());
235 if (!sat_solver_->BacktrackAndPropagateReimplications(longest_valid_prefix)) {
239 absl::btree_set<LiteralIndex> moved_last;
240 while (!sat_solver_->ModelIsUnsat()) {
246 moved_last, sat_solver_->CurrentDecisionLevel(), &candidate);
247 if (target_level == -1)
break;
248 if (!sat_solver_->BacktrackAndPropagateReimplications(target_level)) {
251 fixed_false_literals.clear();
254 while (sat_solver_->CurrentDecisionLevel() < candidate.size()) {
255 if (time_limit_->LimitReached())
return true;
256 const int level = sat_solver_->CurrentDecisionLevel();
257 const Literal literal = candidate[level];
259 if (assignment_.LiteralIsFalse(literal)) {
260 if (lrat_proof_handler_ !=
nullptr) {
261 fixed_false_literals.push_back(literal);
263 candidate[level] = candidate.back();
264 candidate.pop_back();
266 }
else if (assignment_.LiteralIsTrue(literal)) {
267 const int variable_level = trail_->Info(literal.Variable()).level;
268 if (variable_level == 0) {
269 DCHECK(lrat_proof_handler_ ==
nullptr ||
270 trail_->GetUnitClauseId(literal.Variable()) !=
kNoClauseId);
271 counters_.num_true++;
272 counters_.num_removed_literals += clause->size();
273 clause_manager_->LazyDelete(clause,
278 if (parameters_.inprocessing_minimization_use_conflict_analysis()) {
282 for (
const Literal l : sat_solver_->GetDecisionsFixing({literal})) {
283 candidate.push_back(l.Negated());
286 candidate.resize(variable_level);
288 fixed_true_literal = literal.Index();
289 candidate.push_back(literal);
297 if (parameters_.subsume_during_vivification() &&
298 clause_manager_->ReasonClauseOrNull(literal.Variable()) != clause &&
299 SubsumptionIsInteresting(literal.Variable(), candidate.size())) {
300 counters_.num_subsumed++;
301 counters_.num_removed_literals += clause->size();
302 KeepAllClausesUsedToInfer(literal.Variable());
303 clause_manager_->LazyDelete(
310 ++counters_.num_decisions;
311 sat_solver_->EnqueueDecisionAndBackjumpOnConflict(literal.Negated());
312 if (sat_solver_->ModelIsUnsat())
return false;
313 if (clause->IsRemoved())
return true;
315 if (sat_solver_->CurrentDecisionLevel() < level) {
316 ++counters_.num_conflicts;
324 std::swap(candidate[level],
325 candidate[sat_solver_->CurrentDecisionLevel()]);
329 if (candidate.empty()) {
330 sat_solver_->NotifyThatModelIsUnsat();
336 if (!parameters_.inprocessing_minimization_use_all_orderings())
break;
337 moved_last.insert(candidate.back().Index());
341 if (candidate.size() == clause->size())
return true;
343 std::vector<ClauseId> clause_ids;
344 if (lrat_proof_handler_ !=
nullptr) {
346 !fixed_false_literals.empty());
353 clause_manager_->AppendClauseIdsFixing({Literal(fixed_true_literal)},
361 clause_manager_->AppendClauseIdsFixing(fixed_false_literals, &clause_ids);
362 clause_ids.push_back(clause_manager_->GetClauseId(clause));
368 absl::c_reverse(candidate);
370 DCHECK(absl::c_all_of(absl::MakeSpan(candidate).subspan(1), [&](Literal l) {
371 return assignment_.LiteralIsFalse(l);
373 if (candidate.size() == 1) {
374 sat_solver_->BacktrackAndPropagateReimplications(0);
375 }
else if (assignment_.LiteralIsFalse(candidate[1]) &&
376 (!assignment_.LiteralIsTrue(candidate[0]) ||
377 trail_->AssignmentLevel(candidate[1]) <
378 trail_->AssignmentLevel(candidate[0]))) {
388 sat_solver_->BacktrackAndPropagateReimplications(
389 std::max(0, trail_->AssignmentLevel(candidate[1])));
391 if (sat_solver_->CurrentDecisionLevel() == 0) {
395 [&](Literal l) {
return assignment_.LiteralIsFalse(l); });
396 if (absl::c_any_of(clause->AsSpan(), [&](Literal l) {
397 return assignment_.LiteralIsTrue(l);
399 counters_.num_removed_literals += clause->size();
405 counters_.num_removed_literals += clause->size() - candidate.size();
406 if (!clause_manager_->InprocessingRewriteClause(clause, candidate,
408 sat_solver_->NotifyThatModelIsUnsat();
415 return sat_solver_->FinishPropagation();
void OverrideLogging(bool value)
void AddCounter(std::string name, int64_t count)
SatClause * ReasonClauseOrNull(BooleanVariable var) const
void KeepClauseForever(SatClause *const clause)
int AssignmentType(BooleanVariable var) const
absl::Span< const Literal > Reason(BooleanVariable var, int64_t conflict_id=-1) const
const AssignmentInfo & Info(BooleanVariable var) const
bool VariableIsAssigned(BooleanVariable var) const
ABSL_MUST_USE_RESULT bool MinimizeByPropagation(bool log_info, double dtime_budget, bool minimize_new_clauses_only=false)
void OpenSourceEraseIf(Container &c, Pred pred)
const LiteralIndex kNoLiteralIndex(-1)
constexpr ClauseId kNoClauseId(0)
int MoveOneUnprocessedLiteralLast(const absl::btree_set< LiteralIndex > &processed, int relevant_prefix_size, std::vector< Literal > *literals)
static constexpr int kSearchDecision