26#include "absl/container/flat_hash_set.h"
27#include "absl/log/check.h"
28#include "absl/strings/numbers.h"
29#include "absl/strings/str_split.h"
30#include "absl/strings/string_view.h"
31#include "absl/time/clock.h"
32#include "absl/types/span.h"
43DratChecker::Clause::Clause(
size_t first_literal_index,
int num_literals)
44 : first_literal_index(first_literal_index), num_literals(num_literals) {}
46std::size_t DratChecker::ClauseHash::operator()(
47 const ClauseIndex clause_index)
const {
49 for (Literal literal : checker->Literals(checker->clauses_[clause_index])) {
55bool DratChecker::ClauseEquiv::operator()(
56 const ClauseIndex clause_index1,
const ClauseIndex clause_index2)
const {
57 return checker->Literals(checker->clauses_[clause_index1]) ==
58 checker->Literals(checker->clauses_[clause_index2]);
63 clause_set_(0, ClauseHash(this), ClauseEquiv(this)),
66bool DratChecker::Clause::IsDeleted(ClauseIndex clause_index)
const {
67 return deleted_index <= clause_index;
72 const ClauseIndex clause_index = MaybeAddClause(clause);
75 const auto it = clause_set_.find(clause_index);
76 if (it != clause_set_.end()) {
77 clauses_[*it].num_copies += 1;
80 clause_set_.insert(clause_index);
85 const ClauseIndex inferred_clause_index = MaybeAddClause(clause);
88 first_inferred_clause_index_ = inferred_clause_index;
91 const auto it = clause_set_.find(inferred_clause_index);
92 if (it != clause_set_.end()) {
93 clauses_[*it].num_copies += 1;
94 if (*it >= first_inferred_clause_index_ && !clause.empty()) {
95 CHECK_EQ(clauses_[*it].rat_literal_index, clause[0].Index());
99 clauses_[inferred_clause_index].rat_literal_index =
101 clause_set_.insert(inferred_clause_index);
105ClauseIndex DratChecker::MaybeAddClause(absl::Span<const Literal> clause) {
106 const size_t first_literal_index = literals_.size();
107 literals_.insert(literals_.end(), clause.begin(), clause.end());
110 std::sort(literals_.begin() + first_literal_index, literals_.end());
112 std::unique(literals_.begin() + first_literal_index, literals_.end()),
115 for (
size_t i = first_literal_index + 1;
i < literals_.size(); ++
i) {
116 if (literals_[
i] == literals_[
i - 1].Negated()) {
117 literals_.resize(first_literal_index);
122 Clause(first_literal_index, literals_.size() - first_literal_index));
123 if (!clause.empty()) {
125 std::max(num_variables_, literals_.back().Variable().value() + 1);
127 return ClauseIndex(clauses_.size() - 1);
132 const ClauseIndex clause_index = MaybeAddClause(clause);
134 const auto it = clause_set_.find(clause_index);
135 if (it != clause_set_.end()) {
136 Clause& existing_clause = clauses_[*it];
137 existing_clause.num_copies -= 1;
138 if (existing_clause.num_copies == 0) {
139 DCHECK(existing_clause.deleted_index == std::numeric_limits<int>::max());
140 existing_clause.deleted_index = clauses_.size() - 1;
141 if (clauses_.back().num_literals >= 2) {
142 clauses_[ClauseIndex(clauses_.size() - 2)].deleted_clauses.push_back(
145 clause_set_.erase(it);
148 LOG(WARNING) <<
"Couldn't find deleted clause";
154void DratChecker::RemoveLastClause() {
155 literals_.resize(clauses_.back().first_literal_index);
164 if (clauses_.empty() || first_inferred_clause_index_ ==
kNoClauseIndex ||
165 clauses_.back().num_literals != 0) {
168 clauses_.back().is_needed_for_proof =
true;
175 const int64_t start_time_nanos = absl::GetCurrentTimeNanos();
176 TimeLimit time_limit(max_time_in_seconds);
178 for (ClauseIndex
i(clauses_.size() - 1);
i >= first_inferred_clause_index_;
183 const Clause& clause = clauses_[
i];
186 for (
const ClauseIndex j : clause.deleted_clauses) {
189 if (!clause.is_needed_for_proof) {
193 if (HasRupProperty(
i, Literals(clause))) {
210 std::vector<Literal> resolvent;
211 for (ClauseIndex j(0); j <
i; ++j) {
212 if (!clauses_[j].IsDeleted(
i) &&
216 if (!
Resolve(Literals(clause), Literals(clauses_[j]),
217 Literal(clause.rat_literal_index), &tmp_assignment_,
219 !HasRupProperty(
i, resolvent)) {
225 LogStatistics(absl::GetCurrentTimeNanos() - start_time_nanos);
230 return GetClausesNeededForProof(ClauseIndex(0), first_inferred_clause_index_);
234 return GetClausesNeededForProof(first_inferred_clause_index_,
235 ClauseIndex(clauses_.size()));
238std::vector<std::vector<Literal>> DratChecker::GetClausesNeededForProof(
239 ClauseIndex
begin, ClauseIndex
end)
const {
240 std::vector<std::vector<Literal>> result;
242 const Clause& clause = clauses_[
i];
243 if (clause.is_needed_for_proof) {
244 const absl::Span<const Literal>& literals =
Literals(clause);
245 result.emplace_back(literals.begin(), literals.end());
247 const size_t rat_literal_clause_index =
248 std::find(literals.begin(), literals.end(),
249 Literal(clause.rat_literal_index)) -
251 std::swap(result.back()[0], result.back()[rat_literal_clause_index]);
258absl::Span<const Literal> DratChecker::Literals(
const Clause& clause)
const {
259 return absl::Span<const Literal>(
260 literals_.data() + clause.first_literal_index, clause.num_literals);
263void DratChecker::Init() {
265 assignment_.Resize(num_variables_);
267 high_priority_literals_to_assign_.clear();
268 low_priority_literals_to_assign_.clear();
269 watched_literals_.clear();
270 watched_literals_.resize(2 * num_variables_);
271 single_literal_clauses_.clear();
273 tmp_assignment_.Resize(num_variables_);
276 for (ClauseIndex clause_index(0); clause_index < clauses_.size();
278 Clause& clause = clauses_[clause_index];
279 if (clause.num_literals >= 2) {
282 if (clause.deleted_index == std::numeric_limits<int>::max()) {
283 WatchClause(clause_index);
285 }
else if (clause.num_literals == 1) {
286 single_literal_clauses_.push_back(clause_index);
291void DratChecker::WatchClause(ClauseIndex clause_index) {
292 const Literal* clause_literals =
293 literals_.data() + clauses_[clause_index].first_literal_index;
294 watched_literals_[clause_literals[0].Index()].push_back(clause_index);
295 watched_literals_[clause_literals[1].Index()].push_back(clause_index);
298bool DratChecker::HasRupProperty(ClauseIndex num_clauses,
299 absl::Span<const Literal> clause) {
301 for (
const Literal literal : clause) {
303 AssignAndPropagate(num_clauses, literal.Negated(),
kNoClauseIndex);
309 for (
const ClauseIndex clause_index : single_literal_clauses_) {
310 const Clause& clause = clauses_[clause_index];
313 if (clause_index < num_clauses && !clause.IsDeleted(num_clauses)) {
314 if (clause.is_needed_for_proof) {
315 high_priority_literals_to_assign_.push_back(
316 {literals_[clause.first_literal_index], clause_index});
318 low_priority_literals_to_assign_.push_back(
319 {literals_[clause.first_literal_index], clause_index});
324 while (!(high_priority_literals_to_assign_.empty() &&
325 low_priority_literals_to_assign_.empty()) &&
327 std::vector<LiteralToAssign>& stack =
328 high_priority_literals_to_assign_.empty()
329 ? low_priority_literals_to_assign_
330 : high_priority_literals_to_assign_;
331 const LiteralToAssign literal_to_assign = stack.back();
333 if (assignment_.LiteralIsAssigned(literal_to_assign.literal)) {
336 if (assignment_.LiteralIsFalse(literal_to_assign.literal)) {
337 conflict = literal_to_assign.source_clause_index;
344 unit_stack_.push_back(literal_to_assign.source_clause_index);
345 conflict = AssignAndPropagate(num_clauses, literal_to_assign.literal,
346 literal_to_assign.source_clause_index);
349 MarkAsNeededForProof(&clauses_[conflict]);
352 for (
const Literal literal : assigned_) {
353 assignment_.UnassignLiteral(literal);
356 high_priority_literals_to_assign_.clear();
357 low_priority_literals_to_assign_.clear();
363ClauseIndex DratChecker::AssignAndPropagate(ClauseIndex num_clauses,
365 ClauseIndex source_clause_index) {
366 assigned_.push_back(literal);
367 assignment_.AssignFromTrueLiteral(literal);
368 assignment_source_[literal.Variable()] = source_clause_index;
370 const Literal false_literal = literal.Negated();
371 std::vector<ClauseIndex>& watched = watched_literals_[false_literal.Index()];
372 int new_watched_size = 0;
374 for (
const ClauseIndex clause_index : watched) {
375 if (clause_index >= num_clauses) {
380 Clause& clause = clauses_[clause_index];
381 DCHECK(!clause.IsDeleted(num_clauses));
383 watched[new_watched_size++] = clause_index;
387 Literal* clause_literals = literals_.data() + clause.first_literal_index;
388 const Literal other_watched_literal(LiteralIndex(
389 clause_literals[0].
Index().value() ^
390 clause_literals[1].
Index().value() ^ false_literal.Index().value()));
391 if (assignment_.LiteralIsTrue(other_watched_literal)) {
392 watched[new_watched_size++] = clause_index;
396 bool new_watched_literal_found =
false;
397 for (
int i = 2;
i < clause.num_literals; ++
i) {
398 if (!assignment_.LiteralIsFalse(clause_literals[
i])) {
399 clause_literals[0] = other_watched_literal;
400 clause_literals[1] = clause_literals[
i];
401 clause_literals[
i] = false_literal;
402 watched_literals_[clause_literals[1].Index()].push_back(clause_index);
403 new_watched_literal_found =
true;
408 if (!new_watched_literal_found) {
409 if (assignment_.LiteralIsFalse(other_watched_literal)) {
413 conflict_index = clause_index;
415 DCHECK(!assignment_.LiteralIsAssigned(other_watched_literal));
420 if (clause.is_needed_for_proof) {
421 high_priority_literals_to_assign_.push_back(
422 {other_watched_literal, clause_index});
424 low_priority_literals_to_assign_.push_back(
425 {other_watched_literal, clause_index});
428 watched[new_watched_size++] = clause_index;
431 watched.resize(new_watched_size);
432 return conflict_index;
435void DratChecker::MarkAsNeededForProof(Clause* clause) {
436 const auto mark_clause_and_sources = [&](Clause* clause) {
437 clause->is_needed_for_proof =
true;
438 for (
const Literal literal : Literals(*clause)) {
439 const ClauseIndex source_clause_index =
440 assignment_source_[literal.Variable()];
442 clauses_[source_clause_index].tmp_is_needed_for_proof_step =
true;
446 mark_clause_and_sources(clause);
447 for (
int i = unit_stack_.size() - 1;
i >= 0; --
i) {
448 Clause& unit_clause = clauses_[unit_stack_[
i]];
449 if (unit_clause.tmp_is_needed_for_proof_step) {
450 mark_clause_and_sources(&unit_clause);
454 unit_clause.tmp_is_needed_for_proof_step =
false;
459void DratChecker::LogStatistics(int64_t duration_nanos)
const {
460 int problem_clauses_needed_for_proof = 0;
461 int inferred_clauses_needed_for_proof = 0;
462 for (ClauseIndex
i(0);
i < clauses_.size(); ++
i) {
463 if (clauses_[
i].is_needed_for_proof) {
464 if (
i < first_inferred_clause_index_) {
465 ++problem_clauses_needed_for_proof;
467 ++inferred_clauses_needed_for_proof;
471 VLOG(1) << problem_clauses_needed_for_proof
472 <<
" problem clauses needed for proof, out of "
473 << first_inferred_clause_index_;
474 VLOG(1) << inferred_clauses_needed_for_proof
475 <<
" inferred clauses needed for proof, out of "
476 << clauses_.size() - first_inferred_clause_index_;
477 VLOG(1) << num_rat_checks_ <<
" RAT inferred clauses";
478 VLOG(1) <<
"verification time: " << 1e-9 * duration_nanos <<
" s";
482 return std::find(clause.begin(), clause.end(), literal) != clause.end();
486 absl::Span<const Literal> other_clause,
488 std::vector<Literal>* resolvent) {
493 for (
const Literal literal : clause) {
494 if (literal != complementary_literal) {
497 resolvent->push_back(literal);
502 for (
const Literal other_literal : other_clause) {
503 if (other_literal != complementary_literal.
Negated()) {
508 resolvent->push_back(other_literal);
514 for (
const Literal literal : clause) {
515 if (literal != complementary_literal) {
527 std::vector<Literal> literals;
528 std::ifstream
file(file_path);
531 while (std::getline(
file, line)) {
533 std::vector<absl::string_view> words =
534 absl::StrSplit(line, absl::ByAnyChar(
" \t"), absl::SkipWhitespace());
535 if (words.empty() || words[0] ==
"c") {
539 if (words[0] ==
"p") {
540 if (num_clauses > 0 || words.size() != 4 || words[1] !=
"cnf" ||
542 !absl::SimpleAtoi(words[3], &num_clauses) || num_clauses <= 0) {
543 LOG(ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
544 <<
" of " << file_path;
551 for (
int i = 0;
i < words.size(); ++
i) {
553 if (!absl::SimpleAtoi(words[
i], &signed_value) ||
555 (signed_value == 0 &&
i != words.size() - 1)) {
556 LOG(ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
557 <<
" of " << file_path;
561 if (signed_value != 0) {
562 literals.push_back(
Literal(signed_value));
574 bool ends_with_empty_clause =
false;
575 std::vector<Literal> literals;
576 std::ifstream
file(file_path);
579 while (std::getline(
file, line)) {
581 std::vector<absl::string_view> words =
582 absl::StrSplit(line, absl::ByAnyChar(
" \t"), absl::SkipWhitespace());
583 bool delete_clause = !words.empty() && words[0] ==
"d";
585 for (
int i = (delete_clause ? 1 : 0);
i < words.size(); ++
i) {
587 if (!absl::SimpleAtoi(words[
i], &signed_value) ||
588 (signed_value == 0 &&
i != words.size() - 1)) {
589 LOG(ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
590 <<
" of " << file_path;
594 if (signed_value != 0) {
595 literals.push_back(
Literal(signed_value));
600 ends_with_empty_clause =
false;
603 ends_with_empty_clause = literals.empty();
606 if (!ends_with_empty_clause) {
614 absl::Span<
const std::vector<Literal>> clauses,
616 std::ofstream output_stream(file_path, std::ofstream::out);
618 output_stream <<
"p cnf " <<
num_variables <<
" " << clauses.size() <<
"\n";
620 for (
const auto& clause : clauses) {
621 for (
Literal literal : clause) {
622 output_stream << literal.SignedValue() <<
" ";
624 output_stream <<
"0\n";
626 output_stream.close();
627 return output_stream.good();
void AddProblemClause(absl::Span< const Literal > clause)
std::vector< std::vector< Literal > > GetUnsatSubProblem() const
Status Check(double max_time_in_seconds)
std::vector< std::vector< Literal > > GetOptimizedProof() const
void AddInferredClause(absl::Span< const Literal > clause)
void DeleteClause(absl::Span< const Literal > clause)
int num_variables() const
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsFalse(Literal literal) const
void AssignFromTrueLiteral(Literal literal)
void UnassignLiteral(Literal literal)
void push_back(const value_type &val)
bool AddProblemClauses(const std::string &file_path, DratChecker *drat_checker)
const LiteralIndex kNoLiteralIndex(-1)
bool Resolve(absl::Span< const Literal > clause, absl::Span< const Literal > other_clause, Literal complementary_literal, VariablesAssignment *assignment, std::vector< Literal > *resolvent)
bool PrintClauses(const std::string &file_path, SatFormat format, absl::Span< const std::vector< Literal > > clauses, int num_variables)
std::vector< Literal > Literals(absl::Span< const int > input)
bool ContainsLiteral(absl::Span< const Literal > clause, Literal literal)
bool AddInferredAndDeletedClauses(const std::string &file_path, DratChecker *drat_checker)
const ClauseIndex kNoClauseIndex(-1)
ClosedInterval::Iterator end(ClosedInterval interval)
ClosedInterval::Iterator begin(ClosedInterval interval)
uint64_t Hash(uint64_t num, uint64_t c)