20#include "absl/algorithm/container.h"
21#include "absl/container/flat_hash_set.h"
22#include "absl/log/check.h"
23#include "absl/log/log.h"
24#include "absl/strings/str_cat.h"
25#include "absl/strings/str_join.h"
26#include "absl/types/span.h"
35 if (!VLOG_IS_ON(1))
return;
37 {{
"LratChecker/num_problem_clauses", num_problem_clauses_},
38 {
"LratChecker/num_inferred_clauses", num_inferred_clauses_},
39 {
"LratChecker/num_processed_rup_literals", num_processed_rup_literals_},
40 {
"LratChecker/num_processed_rup_clauses", num_processed_rup_clauses_},
41 {
"LratChecker/num_unneeded_rup_literals", num_unneeded_rup_literals_},
42 {
"LratChecker/num_unneeded_rup_clauses", num_unneeded_rup_clauses_},
43 {
"LratChecker/num_processed_rat_literals", num_processed_rat_literals_},
44 {
"LratChecker/num_processed_rat_clauses", num_processed_rat_clauses_},
45 {
"LratChecker/num_unneeded_rat_literals", num_unneeded_rat_literals_},
46 {
"LratChecker/num_unneeded_rat_clauses", num_unneeded_rat_clauses_},
47 {
"LratChecker/num_deleted_clauses", num_deleted_clauses_},
48 {
"LratChecker/num_deleted_clauses_not_found",
49 num_deleted_clauses_not_found_}});
53 absl::Span<const Literal> clause) {
54 ++num_problem_clauses_;
55 return AddClauseInternal(
id, clause,
true,
60 absl::Span<const Literal> clause,
61 absl::Span<const ClauseId> unit_ids,
62 absl::Span<const RatIds> rat) {
63 ++num_inferred_clauses_;
64 return AddClauseInternal(
id, clause,
false, unit_ids,
69 ++num_deleted_clauses_;
70 for (
const ClauseId clause_id : clause_ids) {
71 const auto it = clauses_.find(clause_id);
72 if (it == clauses_.end()) {
73 ++num_deleted_clauses_not_found_;
76 if (occurrences_needed_) {
77 for (
const Literal literal : it->second) {
78 occurrences_[literal.
Index()]--;
86enum UnitPropagationStatus {
93UnitPropagationStatus Propagate(
94 absl::Span<const Literal> clause,
97 for (
const Literal literal : clause) {
98 if (!false_literals_set[literal]) {
100 unique_unassigned_literal = literal.Index();
104 const Literal unassigned_literal =
Literal(unique_unassigned_literal);
105 DCHECK(!false_literals_set[unassigned_literal]);
106 if (false_literals_set[unassigned_literal.Negated()]) {
111 false_literals_set.
Set(unassigned_literal.Negated());
116bool LratChecker::AddClauseInternal(ClauseId
id,
117 absl::Span<const Literal> clause,
118 bool is_problem_clause,
119 absl::Span<const ClauseId> unit_ids,
120 absl::Span<const RatIds> rat) {
121 if (!valid_)
return false;
122 if (complete_)
return true;
123 if (clauses_.contains(
id))
124 return Error(
id, absl::StrCat(
"clause ID ",
id,
" already used"));
126 FixedCapacityVector<Literal> sorted_clause(clause);
127 std::sort(sorted_clause.begin(), sorted_clause.end());
128 sorted_clause.resize(std::unique(sorted_clause.begin(), sorted_clause.end()) -
129 sorted_clause.begin());
130 for (
int i = 1;
i < sorted_clause.size(); ++
i) {
131 if (sorted_clause[
i] == sorted_clause[
i - 1].Negated()) {
135 if (!sorted_clause.empty()) {
136 const int last_variable = sorted_clause.back().Variable().value();
137 if (last_variable >= num_variables_) {
138 num_variables_ = last_variable + 1;
139 if (occurrences_needed_) {
140 occurrences_.resize(2 * num_variables_, 0);
141 }
else if (clause.size() == 1 && unit_ids.empty() && rat.empty()) {
145 clauses_[id] = std::move(sorted_clause);
151 if (!is_problem_clause) {
152 tmp_false_literals_set_.ClearAndResize(LiteralIndex(2 * num_variables_));
153 for (
const Literal literal : sorted_clause) {
154 tmp_false_literals_set_.Set(literal);
156 UnitPropagationStatus last_propagation_status = kUnit;
157 for (
int i = 0;
i < unit_ids.size(); ++
i) {
158 const ClauseId unit_id = unit_ids[
i];
159 auto it = clauses_.find(unit_id);
160 if (it == clauses_.end()) {
161 return Error(
id, absl::StrCat(
"unit_id ", unit_id,
" not found"));
163 ++num_processed_rup_clauses_;
164 num_processed_rup_literals_ += it->second.size();
165 last_propagation_status = Propagate(it->second, tmp_false_literals_set_);
166 if (last_propagation_status == kError) {
168 id, absl::StrCat(
"unit_id ", unit_id,
" is not unit. literals=[",
169 absl::StrJoin(it->second,
","),
"]"));
170 }
else if (last_propagation_status == kWarning) {
171 last_propagation_status = kUnit;
172 ++num_unneeded_rup_literals_;
173 }
else if (last_propagation_status == kConflict) {
174 num_unneeded_rup_clauses_ += unit_ids.size() -
i - 1;
178 if (last_propagation_status == kUnit) {
180 if (clause.empty())
return Error(
id,
"missing pivot for RAT proof");
181 const Literal pivot = clause.front();
182 if (!occurrences_needed_) {
183 occurrences_needed_ =
true;
184 InitializeOccurrences();
186 if (rat.size() != occurrences_[pivot.Negated()]) {
187 return Error(
id,
"wrong number of resolvant IDs in RAT proof");
189 absl::flat_hash_set<ClauseId>& resolvant_ids_set = tmp_clause_ids_;
190 resolvant_ids_set.clear();
192 for (
const RatIds& rat_ids : rat) {
193 const ClauseId resolvant_id = rat_ids.resolvant_id;
195 if (!resolvant_ids_set.insert(resolvant_id).second) {
197 absl::StrCat(
"duplicate resolvant_id ", resolvant_id));
200 const auto it = clauses_.find(resolvant_id);
201 if (it == clauses_.end()) {
203 id, absl::StrCat(
"resolvant_id ", resolvant_id,
" not found"));
205 const absl::Span<const Literal> resolvant = it->second;
206 if (!absl::c_binary_search(resolvant, pivot.Negated())) {
208 absl::StrCat(
"missing negated pivot in resolvant_id ",
215 tmp_rat_false_literals_set_.CopyFrom(tmp_false_literals_set_);
216 bool has_two_complementary_literals =
false;
217 for (
const Literal literal : resolvant) {
218 if (literal == pivot.Negated())
continue;
219 if (tmp_false_literals_set_[literal.Negated()]) {
220 has_two_complementary_literals =
true;
223 tmp_rat_false_literals_set_.Set(literal);
225 if (has_two_complementary_literals)
continue;
226 last_propagation_status = kUnit;
227 for (
int j = 0; j < rat_ids.unit_ids.size(); ++j) {
228 const ClauseId unit_id = rat_ids.unit_ids[j];
229 const auto it = clauses_.find(unit_id);
230 if (it == clauses_.end()) {
232 absl::StrCat(
"rat.unit_id ", unit_id,
" not found"));
234 ++num_processed_rat_clauses_;
235 num_processed_rat_literals_ += it->second.size();
236 last_propagation_status =
237 Propagate(it->second, tmp_rat_false_literals_set_);
238 if (last_propagation_status == kError) {
240 absl::StrCat(
"rat.unit_id ", unit_id,
" is not unit"));
241 }
else if (last_propagation_status == kWarning) {
242 last_propagation_status = kUnit;
243 ++num_unneeded_rat_literals_;
244 }
else if (last_propagation_status == kConflict) {
245 num_unneeded_rat_clauses_ += rat_ids.unit_ids.size() - j - 1;
249 if (last_propagation_status != kConflict) {
250 return Error(
id, absl::StrCat(
"last unit_id for rat.resolvant_id ",
251 resolvant_id,
" is not a conflict"));
257 if (occurrences_needed_) {
258 for (
const Literal literal : sorted_clause) {
259 occurrences_[literal.Index()]++;
262 clauses_[id] = std::move(sorted_clause);
263 if (clause.empty()) {
269void LratChecker::InitializeOccurrences() {
270 occurrences_.assign(2 * num_variables_, 0);
271 for (
const auto& [
id, clause] : clauses_) {
272 for (
const Literal literal : clause) {
273 occurrences_[literal.Index()]++;
278bool LratChecker::Error(ClauseId
id, std::string_view error) {
280 error_message_ = absl::StrCat(
"In clause ",
id,
": ", error);
void Set(IntegerType index)
LiteralIndex Index() const
bool AddProblemClause(ClauseId id, absl::Span< const Literal > clause)
*Either clause and the r resolvant_id clause have two pairs of *Or all the r unit_ids clauses must become unit and eventually empty bool AddInferredClause(ClauseId id, absl::Span< const Literal > clause, absl::Span< const ClauseId > unit_ids, absl::Span< const RatIds > rat={})
void DeleteClauses(absl::Span< const ClauseId > clause_ids)
const LiteralIndex kNoLiteralIndex(-1)