19 #include "absl/strings/numbers.h"
20 #include "absl/strings/str_split.h"
21 #include "absl/time/clock.h"
29 DratChecker::Clause::Clause(
int first_literal_index,
int num_literals)
30 : first_literal_index(first_literal_index), num_literals(num_literals) {}
32 std::size_t DratChecker::ClauseHash::operator()(
33 const ClauseIndex clause_index)
const {
35 for (Literal
literal : checker->Literals(checker->clauses_[clause_index])) {
41 bool DratChecker::ClauseEquiv::operator()(
42 const ClauseIndex clause_index1,
const ClauseIndex clause_index2)
const {
43 return checker->Literals(checker->clauses_[clause_index1]) ==
44 checker->Literals(checker->clauses_[clause_index2]);
49 clause_set_(0, ClauseHash(this), ClauseEquiv(this)),
52 bool DratChecker::Clause::IsDeleted(ClauseIndex clause_index)
const {
53 return deleted_index <= clause_index;
58 const ClauseIndex clause_index = AddClause(clause);
60 const auto it = clause_set_.find(clause_index);
61 if (it != clause_set_.end()) {
62 clauses_[*it].num_copies += 1;
65 clause_set_.insert(clause_index);
70 const ClauseIndex infered_clause_index = AddClause(clause);
72 first_infered_clause_index_ = infered_clause_index;
75 const auto it = clause_set_.find(infered_clause_index);
76 if (it != clause_set_.end()) {
77 clauses_[*it].num_copies += 1;
78 if (*it >= first_infered_clause_index_ && !clause.empty()) {
83 clauses_[infered_clause_index].rat_literal_index =
85 clause_set_.insert(infered_clause_index);
89 ClauseIndex DratChecker::AddClause(absl::Span<const Literal> clause) {
90 const int first_literal_index = literals_.size();
91 literals_.insert(literals_.end(), clause.begin(), clause.end());
94 std::sort(literals_.begin() + first_literal_index, literals_.end());
96 std::unique(literals_.begin() + first_literal_index, literals_.end()),
99 for (
int i = first_literal_index + 1; i < literals_.size(); ++i) {
100 CHECK(literals_[i] != literals_[i - 1].Negated());
103 Clause(first_literal_index, literals_.size() - first_literal_index));
104 if (!clause.empty()) {
106 std::max(num_variables_, literals_.back().Variable().value() + 1);
108 return ClauseIndex(clauses_.
size() - 1);
113 const auto it = clause_set_.find(AddClause(clause));
114 if (it != clause_set_.end()) {
115 Clause& existing_clause = clauses_[*it];
116 existing_clause.num_copies -= 1;
117 if (existing_clause.num_copies == 0) {
119 existing_clause.deleted_index = clauses_.
size() - 1;
120 if (clauses_.
back().num_literals >= 2) {
121 clauses_[ClauseIndex(clauses_.
size() - 2)].deleted_clauses.
push_back(
124 clause_set_.erase(it);
127 LOG(
WARNING) <<
"Couldn't find deleted clause";
133 void DratChecker::RemoveLastClause() {
134 literals_.resize(clauses_.
back().first_literal_index);
144 clauses_.
back().num_literals != 0) {
145 return Status::INVALID;
147 clauses_.
back().is_needed_for_proof =
true;
154 const int64 start_time_nanos = absl::GetCurrentTimeNanos();
157 for (ClauseIndex i(clauses_.
size() - 1); i >= first_infered_clause_index_;
162 const Clause& clause = clauses_[i];
165 for (
const ClauseIndex j : clause.deleted_clauses) {
168 if (!clause.is_needed_for_proof) {
172 if (HasRupProperty(i, Literals(clause))) {
187 if (clause.rat_literal_index ==
kNoLiteralIndex)
return Status::INVALID;
189 std::vector<Literal> resolvent;
190 for (ClauseIndex j(0); j < i; ++j) {
191 if (!clauses_[j].IsDeleted(i) &&
195 if (!
Resolve(Literals(clause), Literals(clauses_[j]),
196 Literal(clause.rat_literal_index), &tmp_assignment_,
198 !HasRupProperty(i, resolvent)) {
199 return Status::INVALID;
204 LogStatistics(absl::GetCurrentTimeNanos() - start_time_nanos);
205 return Status::VALID;
209 return GetClausesNeededForProof(ClauseIndex(0), first_infered_clause_index_);
213 return GetClausesNeededForProof(first_infered_clause_index_,
214 ClauseIndex(clauses_.
size()));
217 std::vector<std::vector<Literal>> DratChecker::GetClausesNeededForProof(
218 ClauseIndex begin, ClauseIndex end)
const {
219 std::vector<std::vector<Literal>> result;
220 for (ClauseIndex i = begin; i < end; ++i) {
221 const Clause& clause = clauses_[i];
222 if (clause.is_needed_for_proof) {
223 const absl::Span<const Literal>& literals = Literals(clause);
224 result.emplace_back(literals.begin(), literals.end());
226 const int rat_literal_clause_index =
227 std::find(literals.begin(), literals.end(),
228 Literal(clause.rat_literal_index)) -
230 std::swap(result.back()[0], result.back()[rat_literal_clause_index]);
237 absl::Span<const Literal> DratChecker::Literals(
const Clause& clause)
const {
238 return absl::Span<const Literal>(
239 literals_.data() + clause.first_literal_index, clause.num_literals);
242 void DratChecker::Init() {
244 assignment_.
Resize(num_variables_);
246 high_priority_literals_to_assign_.clear();
247 low_priority_literals_to_assign_.clear();
248 watched_literals_.
clear();
249 watched_literals_.
resize(2 * num_variables_);
250 single_literal_clauses_.clear();
252 tmp_assignment_.
Resize(num_variables_);
255 for (ClauseIndex clause_index(0); clause_index < clauses_.
size();
257 Clause& clause = clauses_[clause_index];
258 if (clause.num_literals >= 2) {
262 WatchClause(clause_index);
264 }
else if (clause.num_literals == 1) {
265 single_literal_clauses_.push_back(clause_index);
270 void DratChecker::WatchClause(ClauseIndex clause_index) {
271 const Literal* clause_literals =
272 literals_.data() + clauses_[clause_index].first_literal_index;
273 watched_literals_[clause_literals[0].Index()].
push_back(clause_index);
274 watched_literals_[clause_literals[1].Index()].
push_back(clause_index);
277 bool DratChecker::HasRupProperty(ClauseIndex num_clauses,
278 absl::Span<const Literal> clause) {
280 for (
const Literal
literal : clause) {
288 for (
const ClauseIndex clause_index : single_literal_clauses_) {
289 const Clause& clause = clauses_[clause_index];
292 if (clause_index < num_clauses && !clause.IsDeleted(num_clauses)) {
293 if (clause.is_needed_for_proof) {
294 high_priority_literals_to_assign_.push_back(
295 {literals_[clause.first_literal_index], clause_index});
297 low_priority_literals_to_assign_.push_back(
298 {literals_[clause.first_literal_index], clause_index});
303 while (!(high_priority_literals_to_assign_.empty() &&
304 low_priority_literals_to_assign_.empty()) &&
306 std::vector<LiteralToAssign>& stack =
307 high_priority_literals_to_assign_.empty()
308 ? low_priority_literals_to_assign_
309 : high_priority_literals_to_assign_;
310 const LiteralToAssign literal_to_assign = stack.back();
316 conflict = literal_to_assign.source_clause_index;
323 unit_stack_.push_back(literal_to_assign.source_clause_index);
324 conflict = AssignAndPropagate(num_clauses, literal_to_assign.literal,
325 literal_to_assign.source_clause_index);
328 MarkAsNeededForProof(&clauses_[conflict]);
331 for (
const Literal
literal : assigned_) {
335 high_priority_literals_to_assign_.clear();
336 low_priority_literals_to_assign_.clear();
342 ClauseIndex DratChecker::AssignAndPropagate(ClauseIndex num_clauses,
344 ClauseIndex source_clause_index) {
347 assignment_source_[
literal.Variable()] = source_clause_index;
349 const Literal false_literal =
literal.Negated();
350 std::vector<ClauseIndex>& watched = watched_literals_[false_literal.Index()];
351 int new_watched_size = 0;
353 for (
const ClauseIndex clause_index : watched) {
354 if (clause_index >= num_clauses) {
359 Clause& clause = clauses_[clause_index];
360 DCHECK(!clause.IsDeleted(num_clauses));
362 watched[new_watched_size++] = clause_index;
366 Literal* clause_literals = literals_.data() + clause.first_literal_index;
367 const Literal other_watched_literal(LiteralIndex(
369 clause_literals[1].
Index().
value() ^ false_literal.Index().value()));
371 watched[new_watched_size++] = clause_index;
375 bool new_watched_literal_found =
false;
376 for (
int i = 2; i < clause.num_literals; ++i) {
378 clause_literals[0] = other_watched_literal;
379 clause_literals[1] = clause_literals[i];
380 clause_literals[i] = false_literal;
381 watched_literals_[clause_literals[1].Index()].
push_back(clause_index);
382 new_watched_literal_found =
true;
387 if (!new_watched_literal_found) {
392 conflict_index = clause_index;
399 if (clause.is_needed_for_proof) {
400 high_priority_literals_to_assign_.push_back(
401 {other_watched_literal, clause_index});
403 low_priority_literals_to_assign_.push_back(
404 {other_watched_literal, clause_index});
407 watched[new_watched_size++] = clause_index;
410 watched.resize(new_watched_size);
411 return conflict_index;
414 void DratChecker::MarkAsNeededForProof(Clause* clause) {
415 const auto mark_clause_and_sources = [&](Clause* clause) {
416 clause->is_needed_for_proof =
true;
417 for (
const Literal
literal : Literals(*clause)) {
418 const ClauseIndex source_clause_index =
419 assignment_source_[
literal.Variable()];
421 clauses_[source_clause_index].tmp_is_needed_for_proof_step =
true;
425 mark_clause_and_sources(clause);
426 for (
int i = unit_stack_.size() - 1; i >= 0; --i) {
427 Clause& unit_clause = clauses_[unit_stack_[i]];
428 if (unit_clause.tmp_is_needed_for_proof_step) {
429 mark_clause_and_sources(&unit_clause);
433 unit_clause.tmp_is_needed_for_proof_step =
false;
438 void DratChecker::LogStatistics(
int64 duration_nanos)
const {
439 int problem_clauses_needed_for_proof = 0;
440 int infered_clauses_needed_for_proof = 0;
441 for (ClauseIndex i(0); i < clauses_.
size(); ++i) {
442 if (clauses_[i].is_needed_for_proof) {
443 if (i < first_infered_clause_index_) {
444 ++problem_clauses_needed_for_proof;
446 ++infered_clauses_needed_for_proof;
450 LOG(
INFO) << problem_clauses_needed_for_proof
451 <<
" problem clauses needed for proof, out of "
452 << first_infered_clause_index_;
453 LOG(
INFO) << infered_clauses_needed_for_proof
454 <<
" infered clauses needed for proof, out of "
455 << clauses_.
size() - first_infered_clause_index_;
456 LOG(
INFO) << num_rat_checks_ <<
" RAT infered clauses";
457 LOG(
INFO) <<
"verification time: " << 1e-9 * duration_nanos <<
" s";
461 return std::find(clause.begin(), clause.end(),
literal) != clause.end();
464 bool Resolve(absl::Span<const Literal> clause,
465 absl::Span<const Literal> other_clause,
467 std::vector<Literal>* resolvent) {
473 if (
literal != complementary_literal) {
481 for (
const Literal other_literal : other_clause) {
482 if (other_literal != complementary_literal.
Negated()) {
487 resolvent->push_back(other_literal);
494 if (
literal != complementary_literal) {
504 int num_variables = 0;
506 std::vector<Literal> literals;
507 std::ifstream
file(file_path);
510 while (std::getline(
file, line)) {
512 std::vector<absl::string_view> words =
513 absl::StrSplit(line, absl::ByAnyChar(
" \t"), absl::SkipWhitespace());
514 if (words.empty() || words[0] ==
"c") {
518 if (words[0] ==
"p") {
519 if (num_clauses > 0 || words.size() != 4 || words[1] !=
"cnf" ||
520 !absl::SimpleAtoi(words[2], &num_variables) || num_variables <= 0 ||
521 !absl::SimpleAtoi(words[3], &num_clauses) || num_clauses <= 0) {
522 LOG(
ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
523 <<
" of " << file_path;
530 for (
int i = 0; i < words.size(); ++i) {
532 if (!absl::SimpleAtoi(words[i], &signed_value) ||
533 std::abs(signed_value) > num_variables ||
534 (signed_value == 0 && i != words.size() - 1)) {
535 LOG(
ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
536 <<
" of " << file_path;
540 if (signed_value != 0) {
541 literals.push_back(
Literal(signed_value));
553 bool ends_with_empty_clause =
false;
554 std::vector<Literal> literals;
555 std::ifstream
file(file_path);
558 while (std::getline(
file, line)) {
560 std::vector<absl::string_view> words =
561 absl::StrSplit(line, absl::ByAnyChar(
" \t"), absl::SkipWhitespace());
562 bool delete_clause = !words.empty() && words[0] ==
"d";
564 for (
int i = (delete_clause ? 1 : 0); i < words.size(); ++i) {
566 if (!absl::SimpleAtoi(words[i], &signed_value) ||
567 (signed_value == 0 && i != words.size() - 1)) {
568 LOG(
ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
569 <<
" of " << file_path;
573 if (signed_value != 0) {
574 literals.push_back(
Literal(signed_value));
579 ends_with_empty_clause =
false;
582 ends_with_empty_clause = literals.empty();
585 if (!ends_with_empty_clause) {
593 const std::vector<std::vector<Literal>>& clauses,
595 std::ofstream output_stream(file_path, std::ofstream::out);
597 output_stream <<
"p cnf " << num_variables <<
" " << clauses.size() <<
"\n";
599 for (
const auto& clause : clauses) {
603 output_stream <<
"0\n";
605 output_stream.close();
606 return output_stream.good();