20#include "absl/strings/numbers.h"
21#include "absl/strings/str_split.h"
22#include "absl/time/clock.h"
30DratChecker::Clause::Clause(
int first_literal_index,
int num_literals)
31 : first_literal_index(first_literal_index), num_literals(num_literals) {}
33std::size_t DratChecker::ClauseHash::operator()(
34 const ClauseIndex clause_index)
const {
36 for (Literal
literal : checker->Literals(checker->clauses_[clause_index])) {
42bool DratChecker::ClauseEquiv::operator()(
43 const ClauseIndex clause_index1,
const ClauseIndex clause_index2)
const {
44 return checker->Literals(checker->clauses_[clause_index1]) ==
45 checker->Literals(checker->clauses_[clause_index2]);
48DratChecker::DratChecker()
50 clause_set_(0, ClauseHash(this), ClauseEquiv(this)),
53bool DratChecker::Clause::IsDeleted(ClauseIndex clause_index)
const {
54 return deleted_index <= clause_index;
59 const ClauseIndex clause_index = AddClause(clause);
61 const auto it = clause_set_.find(clause_index);
62 if (it != clause_set_.end()) {
63 clauses_[*it].num_copies += 1;
66 clause_set_.insert(clause_index);
71 const ClauseIndex infered_clause_index = AddClause(clause);
73 first_infered_clause_index_ = infered_clause_index;
76 const auto it = clause_set_.find(infered_clause_index);
77 if (it != clause_set_.end()) {
78 clauses_[*it].num_copies += 1;
79 if (*it >= first_infered_clause_index_ && !clause.empty()) {
84 clauses_[infered_clause_index].rat_literal_index =
86 clause_set_.insert(infered_clause_index);
90ClauseIndex DratChecker::AddClause(absl::Span<const Literal> clause) {
91 const int first_literal_index = literals_.size();
92 literals_.insert(literals_.end(), clause.begin(), clause.end());
95 std::sort(literals_.begin() + first_literal_index, literals_.end());
97 std::unique(literals_.begin() + first_literal_index, literals_.end()),
100 for (
int i = first_literal_index + 1; i < literals_.size(); ++i) {
101 CHECK(literals_[i] != literals_[i - 1].Negated());
104 Clause(first_literal_index, literals_.size() - first_literal_index));
105 if (!clause.empty()) {
107 std::max(num_variables_, literals_.back().Variable().value() + 1);
109 return ClauseIndex(clauses_.
size() - 1);
114 const auto it = clause_set_.find(AddClause(clause));
115 if (it != clause_set_.end()) {
116 Clause& existing_clause = clauses_[*it];
117 existing_clause.num_copies -= 1;
118 if (existing_clause.num_copies == 0) {
120 existing_clause.deleted_index = clauses_.
size() - 1;
121 if (clauses_.
back().num_literals >= 2) {
122 clauses_[ClauseIndex(clauses_.
size() - 2)].deleted_clauses.
push_back(
125 clause_set_.erase(it);
128 LOG(
WARNING) <<
"Couldn't find deleted clause";
134void DratChecker::RemoveLastClause() {
135 literals_.resize(clauses_.
back().first_literal_index);
145 clauses_.
back().num_literals != 0) {
146 return Status::INVALID;
148 clauses_.
back().is_needed_for_proof =
true;
155 const int64_t start_time_nanos = absl::GetCurrentTimeNanos();
158 for (ClauseIndex i(clauses_.
size() - 1); i >= first_infered_clause_index_;
163 const Clause& clause = clauses_[i];
166 for (
const ClauseIndex j : clause.deleted_clauses) {
169 if (!clause.is_needed_for_proof) {
173 if (HasRupProperty(i, Literals(clause))) {
188 if (clause.rat_literal_index ==
kNoLiteralIndex)
return Status::INVALID;
190 std::vector<Literal> resolvent;
191 for (ClauseIndex j(0); j < i; ++j) {
192 if (!clauses_[j].IsDeleted(i) &&
196 if (!
Resolve(Literals(clause), Literals(clauses_[j]),
197 Literal(clause.rat_literal_index), &tmp_assignment_,
199 !HasRupProperty(i, resolvent)) {
200 return Status::INVALID;
205 LogStatistics(absl::GetCurrentTimeNanos() - start_time_nanos);
206 return Status::VALID;
210 return GetClausesNeededForProof(ClauseIndex(0), first_infered_clause_index_);
214 return GetClausesNeededForProof(first_infered_clause_index_,
215 ClauseIndex(clauses_.
size()));
218std::vector<std::vector<Literal>> DratChecker::GetClausesNeededForProof(
219 ClauseIndex begin, ClauseIndex
end)
const {
220 std::vector<std::vector<Literal>> result;
221 for (ClauseIndex i = begin; i <
end; ++i) {
222 const Clause& clause = clauses_[i];
223 if (clause.is_needed_for_proof) {
224 const absl::Span<const Literal>& literals = Literals(clause);
225 result.emplace_back(literals.begin(), literals.end());
227 const int rat_literal_clause_index =
228 std::find(literals.begin(), literals.end(),
229 Literal(clause.rat_literal_index)) -
231 std::swap(result.back()[0], result.back()[rat_literal_clause_index]);
238absl::Span<const Literal> DratChecker::Literals(
const Clause& clause)
const {
239 return absl::Span<const Literal>(
240 literals_.data() + clause.first_literal_index, clause.num_literals);
243void DratChecker::Init() {
245 assignment_.
Resize(num_variables_);
247 high_priority_literals_to_assign_.clear();
248 low_priority_literals_to_assign_.clear();
249 watched_literals_.
clear();
250 watched_literals_.
resize(2 * num_variables_);
251 single_literal_clauses_.clear();
253 tmp_assignment_.
Resize(num_variables_);
256 for (ClauseIndex clause_index(0); clause_index < clauses_.
size();
258 Clause& clause = clauses_[clause_index];
259 if (clause.num_literals >= 2) {
263 WatchClause(clause_index);
265 }
else if (clause.num_literals == 1) {
266 single_literal_clauses_.push_back(clause_index);
271void DratChecker::WatchClause(ClauseIndex clause_index) {
272 const Literal* clause_literals =
273 literals_.data() + clauses_[clause_index].first_literal_index;
274 watched_literals_[clause_literals[0].Index()].
push_back(clause_index);
275 watched_literals_[clause_literals[1].Index()].
push_back(clause_index);
278bool DratChecker::HasRupProperty(ClauseIndex num_clauses,
279 absl::Span<const Literal> clause) {
281 for (
const Literal
literal : clause) {
289 for (
const ClauseIndex clause_index : single_literal_clauses_) {
290 const Clause& clause = clauses_[clause_index];
293 if (clause_index < num_clauses && !clause.IsDeleted(num_clauses)) {
294 if (clause.is_needed_for_proof) {
295 high_priority_literals_to_assign_.push_back(
296 {literals_[clause.first_literal_index], clause_index});
298 low_priority_literals_to_assign_.push_back(
299 {literals_[clause.first_literal_index], clause_index});
304 while (!(high_priority_literals_to_assign_.empty() &&
305 low_priority_literals_to_assign_.empty()) &&
307 std::vector<LiteralToAssign>& stack =
308 high_priority_literals_to_assign_.empty()
309 ? low_priority_literals_to_assign_
310 : high_priority_literals_to_assign_;
311 const LiteralToAssign literal_to_assign = stack.back();
317 conflict = literal_to_assign.source_clause_index;
324 unit_stack_.push_back(literal_to_assign.source_clause_index);
325 conflict = AssignAndPropagate(num_clauses, literal_to_assign.literal,
326 literal_to_assign.source_clause_index);
329 MarkAsNeededForProof(&clauses_[conflict]);
332 for (
const Literal
literal : assigned_) {
336 high_priority_literals_to_assign_.clear();
337 low_priority_literals_to_assign_.clear();
343ClauseIndex DratChecker::AssignAndPropagate(ClauseIndex num_clauses,
345 ClauseIndex source_clause_index) {
348 assignment_source_[
literal.Variable()] = source_clause_index;
350 const Literal false_literal =
literal.Negated();
351 std::vector<ClauseIndex>& watched = watched_literals_[false_literal.Index()];
352 int new_watched_size = 0;
354 for (
const ClauseIndex clause_index : watched) {
355 if (clause_index >= num_clauses) {
360 Clause& clause = clauses_[clause_index];
361 DCHECK(!clause.IsDeleted(num_clauses));
363 watched[new_watched_size++] = clause_index;
367 Literal* clause_literals = literals_.data() + clause.first_literal_index;
368 const Literal other_watched_literal(LiteralIndex(
370 clause_literals[1].
Index().
value() ^ false_literal.Index().value()));
372 watched[new_watched_size++] = clause_index;
376 bool new_watched_literal_found =
false;
377 for (
int i = 2; i < clause.num_literals; ++i) {
379 clause_literals[0] = other_watched_literal;
380 clause_literals[1] = clause_literals[i];
381 clause_literals[i] = false_literal;
382 watched_literals_[clause_literals[1].Index()].
push_back(clause_index);
383 new_watched_literal_found =
true;
388 if (!new_watched_literal_found) {
393 conflict_index = clause_index;
400 if (clause.is_needed_for_proof) {
401 high_priority_literals_to_assign_.push_back(
402 {other_watched_literal, clause_index});
404 low_priority_literals_to_assign_.push_back(
405 {other_watched_literal, clause_index});
408 watched[new_watched_size++] = clause_index;
411 watched.resize(new_watched_size);
412 return conflict_index;
415void DratChecker::MarkAsNeededForProof(Clause* clause) {
416 const auto mark_clause_and_sources = [&](Clause* clause) {
417 clause->is_needed_for_proof =
true;
418 for (
const Literal
literal : Literals(*clause)) {
419 const ClauseIndex source_clause_index =
420 assignment_source_[
literal.Variable()];
422 clauses_[source_clause_index].tmp_is_needed_for_proof_step =
true;
426 mark_clause_and_sources(clause);
427 for (
int i = unit_stack_.size() - 1; i >= 0; --i) {
428 Clause& unit_clause = clauses_[unit_stack_[i]];
429 if (unit_clause.tmp_is_needed_for_proof_step) {
430 mark_clause_and_sources(&unit_clause);
434 unit_clause.tmp_is_needed_for_proof_step =
false;
439void DratChecker::LogStatistics(int64_t duration_nanos)
const {
440 int problem_clauses_needed_for_proof = 0;
441 int infered_clauses_needed_for_proof = 0;
442 for (ClauseIndex i(0); i < clauses_.
size(); ++i) {
443 if (clauses_[i].is_needed_for_proof) {
444 if (i < first_infered_clause_index_) {
445 ++problem_clauses_needed_for_proof;
447 ++infered_clauses_needed_for_proof;
451 LOG(
INFO) << problem_clauses_needed_for_proof
452 <<
" problem clauses needed for proof, out of "
453 << first_infered_clause_index_;
454 LOG(
INFO) << infered_clauses_needed_for_proof
455 <<
" infered clauses needed for proof, out of "
456 << clauses_.
size() - first_infered_clause_index_;
457 LOG(
INFO) << num_rat_checks_ <<
" RAT infered clauses";
458 LOG(
INFO) <<
"verification time: " << 1e-9 * duration_nanos <<
" s";
462 return std::find(clause.begin(), clause.end(),
literal) != clause.end();
466 absl::Span<const Literal> other_clause,
468 std::vector<Literal>* resolvent) {
474 if (
literal != complementary_literal) {
482 for (
const Literal other_literal : other_clause) {
483 if (other_literal != complementary_literal.
Negated()) {
488 resolvent->push_back(other_literal);
495 if (
literal != complementary_literal) {
505 int num_variables = 0;
507 std::vector<Literal> literals;
508 std::ifstream
file(file_path);
511 while (std::getline(
file, line)) {
513 std::vector<absl::string_view> words =
514 absl::StrSplit(line, absl::ByAnyChar(
" \t"), absl::SkipWhitespace());
515 if (words.empty() || words[0] ==
"c") {
519 if (words[0] ==
"p") {
520 if (num_clauses > 0 || words.size() != 4 || words[1] !=
"cnf" ||
521 !absl::SimpleAtoi(words[2], &num_variables) || num_variables <= 0 ||
522 !absl::SimpleAtoi(words[3], &num_clauses) || num_clauses <= 0) {
523 LOG(
ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
524 <<
" of " << file_path;
531 for (
int i = 0; i < words.size(); ++i) {
533 if (!absl::SimpleAtoi(words[i], &signed_value) ||
534 std::abs(signed_value) > num_variables ||
535 (signed_value == 0 && i != words.size() - 1)) {
536 LOG(
ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
537 <<
" of " << file_path;
541 if (signed_value != 0) {
542 literals.push_back(
Literal(signed_value));
554 bool ends_with_empty_clause =
false;
555 std::vector<Literal> literals;
556 std::ifstream
file(file_path);
559 while (std::getline(
file, line)) {
561 std::vector<absl::string_view> words =
562 absl::StrSplit(line, absl::ByAnyChar(
" \t"), absl::SkipWhitespace());
563 bool delete_clause = !words.empty() && words[0] ==
"d";
565 for (
int i = (delete_clause ? 1 : 0); i < words.size(); ++i) {
567 if (!absl::SimpleAtoi(words[i], &signed_value) ||
568 (signed_value == 0 && i != words.size() - 1)) {
569 LOG(
ERROR) <<
"Invalid content '" << line <<
"' at line " << line_number
570 <<
" of " << file_path;
574 if (signed_value != 0) {
575 literals.push_back(
Literal(signed_value));
580 ends_with_empty_clause =
false;
583 ends_with_empty_clause = literals.empty();
586 if (!ends_with_empty_clause) {
594 const std::vector<std::vector<Literal>>& clauses,
596 std::ofstream output_stream(file_path, std::ofstream::out);
598 output_stream <<
"p cnf " << num_variables <<
" " << clauses.size() <<
"\n";
600 for (
const auto& clause : clauses) {
604 output_stream <<
"0\n";
606 output_stream.close();
607 return output_stream.good();
#define CHECK_EQ(val1, val2)
#define DCHECK(condition)
#define DCHECK_EQ(val1, val2)
void resize(size_type new_size)
void push_back(const value_type &x)
A simple class to enforce both an elapsed time limit and a deterministic time limit in the same threa...
void AddInferedClause(absl::Span< const Literal > clause)
void DeleteClause(absl::Span< const Literal > clause)
Status Check(double max_time_in_seconds)
std::vector< std::vector< Literal > > GetUnsatSubProblem() const
void AddProblemClause(absl::Span< const Literal > clause)
std::vector< std::vector< Literal > > GetOptimizedProof() const
bool LiteralIsAssigned(Literal literal) const
bool LiteralIsTrue(Literal literal) const
void AssignFromTrueLiteral(Literal literal)
void UnassignLiteral(Literal literal)
bool LiteralIsFalse(Literal literal) const
void Resize(int num_variables)
ModelSharedTimeLimit * time_limit
void swap(IdMap< K, V > &a, IdMap< K, V > &b)
const LiteralIndex kNoLiteralIndex(-1)
bool PrintClauses(const std::string &file_path, SatFormat format, const std::vector< std::vector< Literal > > &clauses, int num_variables)
bool Resolve(absl::Span< const Literal > clause, absl::Span< const Literal > other_clause, Literal complementary_literal, VariablesAssignment *assignment, std::vector< Literal > *resolvent)
bool AddInferedAndDeletedClauses(const std::string &file_path, DratChecker *drat_checker)
bool ContainsLiteral(absl::Span< const Literal > clause, Literal literal)
bool AddProblemClauses(const std::string &file_path, DratChecker *drat_checker)
const ClauseIndex kNoClauseIndex(-1)
Collection of objects used to extend the Constraint Solver library.
uint64_t Hash(uint64_t num, uint64_t c)
std::optional< int64_t > end