clang  19.0.0git
CNFFormula.cpp
Go to the documentation of this file.
1 //===- CNFFormula.cpp -------------------------------------------*- C++ -*-===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 //
9 // A representation of a boolean formula in 3-CNF.
10 //
11 //===----------------------------------------------------------------------===//
12 
14 #include "llvm/ADT/DenseSet.h"
15 
16 #include <queue>
17 
18 namespace clang {
19 namespace dataflow {
20 
21 namespace {
22 
23 /// Applies simplifications while building up a BooleanFormula.
24 /// We keep track of unit clauses, which tell us variables that must be
25 /// true/false in any model that satisfies the overall formula.
26 /// Such variables can be dropped from subsequently-added clauses, which
27 /// may in turn yield more unit clauses or even a contradiction.
28 /// The total added complexity of this preprocessing is O(N) where we
29 /// for every clause, we do a lookup for each unit clauses.
30 /// The lookup is O(1) on average. This method won't catch all
31 /// contradictory formulas, more passes can in principle catch
32 /// more cases but we leave all these and the general case to the
33 /// proper SAT solver.
34 struct CNFFormulaBuilder {
35  // Formula should outlive CNFFormulaBuilder.
36  explicit CNFFormulaBuilder(CNFFormula &CNF) : Formula(CNF) {}
37 
38  /// Adds the `L1 v ... v Ln` clause to the formula. Applies
39  /// simplifications, based on single-literal clauses.
40  ///
41  /// Requirements:
42  ///
43  /// `Li` must not be `NullLit`.
44  ///
45  /// All literals must be distinct.
46  void addClause(ArrayRef<Literal> Literals) {
47  // We generate clauses with up to 3 literals in this file.
48  assert(!Literals.empty() && Literals.size() <= 3);
49  // Contains literals of the simplified clause.
50  llvm::SmallVector<Literal> Simplified;
51  for (auto L : Literals) {
52  assert(L != NullLit &&
53  llvm::all_of(Simplified, [L](Literal S) { return S != L; }));
54  auto X = var(L);
55  if (trueVars.contains(X)) { // X must be true
56  if (isPosLit(L))
57  return; // Omit clause `(... v X v ...)`, it is `true`.
58  else
59  continue; // Omit `!X` from `(... v !X v ...)`.
60  }
61  if (falseVars.contains(X)) { // X must be false
62  if (isNegLit(L))
63  return; // Omit clause `(... v !X v ...)`, it is `true`.
64  else
65  continue; // Omit `X` from `(... v X v ...)`.
66  }
67  Simplified.push_back(L);
68  }
69  if (Simplified.empty()) {
70  // Simplification made the clause empty, which is equivalent to `false`.
71  // We already know that this formula is unsatisfiable.
72  Formula.addClause(Simplified);
73  return;
74  }
75  if (Simplified.size() == 1) {
76  // We have new unit clause.
77  const Literal lit = Simplified.front();
78  const Variable v = var(lit);
79  if (isPosLit(lit))
80  trueVars.insert(v);
81  else
82  falseVars.insert(v);
83  }
84  Formula.addClause(Simplified);
85  }
86 
87  /// Returns true if we observed a contradiction while adding clauses.
88  /// In this case then the formula is already known to be unsatisfiable.
89  bool isKnownContradictory() { return Formula.knownContradictory(); }
90 
91 private:
92  CNFFormula &Formula;
93  llvm::DenseSet<Variable> trueVars;
94  llvm::DenseSet<Variable> falseVars;
95 };
96 
97 } // namespace
98 
100  : LargestVar(LargestVar), KnownContradictory(false) {
101  Clauses.push_back(0);
102  ClauseStarts.push_back(0);
103 }
104 
106  assert(llvm::all_of(lits, [](Literal L) { return L != NullLit; }));
107 
108  if (lits.empty())
109  KnownContradictory = true;
110 
111  const size_t S = Clauses.size();
112  ClauseStarts.push_back(S);
113  Clauses.insert(Clauses.end(), lits.begin(), lits.end());
114 }
115 
117  llvm::DenseMap<Variable, Atom> &Atomics) {
118  // The general strategy of the algorithm implemented below is to map each
119  // of the sub-values in `Vals` to a unique variable and use these variables in
120  // the resulting CNF expression to avoid exponential blow up. The number of
121  // literals in the resulting formula is guaranteed to be linear in the number
122  // of sub-formulas in `Vals`.
123 
124  // Map each sub-formula in `Vals` to a unique variable.
125  llvm::DenseMap<const Formula *, Variable> FormulaToVar;
126  // Store variable identifiers and Atom of atomic booleans.
127  Variable NextVar = 1;
128  {
129  std::queue<const Formula *> UnprocessedFormulas;
130  for (const Formula *F : Formulas)
131  UnprocessedFormulas.push(F);
132  while (!UnprocessedFormulas.empty()) {
133  Variable Var = NextVar;
134  const Formula *F = UnprocessedFormulas.front();
135  UnprocessedFormulas.pop();
136 
137  if (!FormulaToVar.try_emplace(F, Var).second)
138  continue;
139  ++NextVar;
140 
141  for (const Formula *Op : F->operands())
142  UnprocessedFormulas.push(Op);
143  if (F->kind() == Formula::AtomRef)
144  Atomics[Var] = F->getAtom();
145  }
146  }
147 
148  auto GetVar = [&FormulaToVar](const Formula *F) {
149  auto ValIt = FormulaToVar.find(F);
150  assert(ValIt != FormulaToVar.end());
151  return ValIt->second;
152  };
153 
154  CNFFormula CNF(NextVar - 1);
155  std::vector<bool> ProcessedSubVals(NextVar, false);
156  CNFFormulaBuilder builder(CNF);
157 
158  // Add a conjunct for each variable that represents a top-level conjunction
159  // value in `Vals`.
160  for (const Formula *F : Formulas)
161  builder.addClause(posLit(GetVar(F)));
162 
163  // Add conjuncts that represent the mapping between newly-created variables
164  // and their corresponding sub-formulas.
165  std::queue<const Formula *> UnprocessedFormulas;
166  for (const Formula *F : Formulas)
167  UnprocessedFormulas.push(F);
168  while (!UnprocessedFormulas.empty()) {
169  const Formula *F = UnprocessedFormulas.front();
170  UnprocessedFormulas.pop();
171  const Variable Var = GetVar(F);
172 
173  if (ProcessedSubVals[Var])
174  continue;
175  ProcessedSubVals[Var] = true;
176 
177  switch (F->kind()) {
178  case Formula::AtomRef:
179  break;
180  case Formula::Literal:
181  CNF.addClause(F->literal() ? posLit(Var) : negLit(Var));
182  break;
183  case Formula::And: {
184  const Variable LHS = GetVar(F->operands()[0]);
185  const Variable RHS = GetVar(F->operands()[1]);
186 
187  if (LHS == RHS) {
188  // `X <=> (A ^ A)` is equivalent to `(!X v A) ^ (X v !A)` which is
189  // already in conjunctive normal form. Below we add each of the
190  // conjuncts of the latter expression to the result.
191  builder.addClause({negLit(Var), posLit(LHS)});
192  builder.addClause({posLit(Var), negLit(LHS)});
193  } else {
194  // `X <=> (A ^ B)` is equivalent to `(!X v A) ^ (!X v B) ^ (X v !A v
195  // !B)` which is already in conjunctive normal form. Below we add each
196  // of the conjuncts of the latter expression to the result.
197  builder.addClause({negLit(Var), posLit(LHS)});
198  builder.addClause({negLit(Var), posLit(RHS)});
199  builder.addClause({posLit(Var), negLit(LHS), negLit(RHS)});
200  }
201  break;
202  }
203  case Formula::Or: {
204  const Variable LHS = GetVar(F->operands()[0]);
205  const Variable RHS = GetVar(F->operands()[1]);
206 
207  if (LHS == RHS) {
208  // `X <=> (A v A)` is equivalent to `(!X v A) ^ (X v !A)` which is
209  // already in conjunctive normal form. Below we add each of the
210  // conjuncts of the latter expression to the result.
211  builder.addClause({negLit(Var), posLit(LHS)});
212  builder.addClause({posLit(Var), negLit(LHS)});
213  } else {
214  // `X <=> (A v B)` is equivalent to `(!X v A v B) ^ (X v !A) ^ (X v
215  // !B)` which is already in conjunctive normal form. Below we add each
216  // of the conjuncts of the latter expression to the result.
217  builder.addClause({negLit(Var), posLit(LHS), posLit(RHS)});
218  builder.addClause({posLit(Var), negLit(LHS)});
219  builder.addClause({posLit(Var), negLit(RHS)});
220  }
221  break;
222  }
223  case Formula::Not: {
224  const Variable Operand = GetVar(F->operands()[0]);
225 
226  // `X <=> !Y` is equivalent to `(!X v !Y) ^ (X v Y)` which is
227  // already in conjunctive normal form. Below we add each of the
228  // conjuncts of the latter expression to the result.
229  builder.addClause({negLit(Var), negLit(Operand)});
230  builder.addClause({posLit(Var), posLit(Operand)});
231  break;
232  }
233  case Formula::Implies: {
234  const Variable LHS = GetVar(F->operands()[0]);
235  const Variable RHS = GetVar(F->operands()[1]);
236 
237  // `X <=> (A => B)` is equivalent to
238  // `(X v A) ^ (X v !B) ^ (!X v !A v B)` which is already in
239  // conjunctive normal form. Below we add each of the conjuncts of
240  // the latter expression to the result.
241  builder.addClause({posLit(Var), posLit(LHS)});
242  builder.addClause({posLit(Var), negLit(RHS)});
243  builder.addClause({negLit(Var), negLit(LHS), posLit(RHS)});
244  break;
245  }
246  case Formula::Equal: {
247  const Variable LHS = GetVar(F->operands()[0]);
248  const Variable RHS = GetVar(F->operands()[1]);
249 
250  if (LHS == RHS) {
251  // `X <=> (A <=> A)` is equivalent to `X` which is already in
252  // conjunctive normal form. Below we add each of the conjuncts of the
253  // latter expression to the result.
254  builder.addClause(posLit(Var));
255 
256  // No need to visit the sub-values of `Val`.
257  continue;
258  }
259  // `X <=> (A <=> B)` is equivalent to
260  // `(X v A v B) ^ (X v !A v !B) ^ (!X v A v !B) ^ (!X v !A v B)` which
261  // is already in conjunctive normal form. Below we add each of the
262  // conjuncts of the latter expression to the result.
263  builder.addClause({posLit(Var), posLit(LHS), posLit(RHS)});
264  builder.addClause({posLit(Var), negLit(LHS), negLit(RHS)});
265  builder.addClause({negLit(Var), posLit(LHS), negLit(RHS)});
266  builder.addClause({negLit(Var), negLit(LHS), posLit(RHS)});
267  break;
268  }
269  }
270  if (builder.isKnownContradictory()) {
271  return CNF;
272  }
273  for (const Formula *Child : F->operands())
274  UnprocessedFormulas.push(Child);
275  }
276 
277  // Unit clauses that were added later were not
278  // considered for the simplification of earlier clauses. Do a final
279  // pass to find more opportunities for simplification.
280  CNFFormula FinalCNF(NextVar - 1);
281  CNFFormulaBuilder FinalBuilder(FinalCNF);
282 
283  // Collect unit clauses.
284  for (ClauseID C = 1; C <= CNF.numClauses(); ++C) {
285  if (CNF.clauseSize(C) == 1) {
286  FinalBuilder.addClause(CNF.clauseLiterals(C)[0]);
287  }
288  }
289 
290  // Add all clauses that were added previously, preserving the order.
291  for (ClauseID C = 1; C <= CNF.numClauses(); ++C) {
292  FinalBuilder.addClause(CNF.clauseLiterals(C));
293  if (FinalBuilder.isKnownContradictory()) {
294  break;
295  }
296  }
297  // It is possible there were new unit clauses again, but
298  // we stop here and leave the rest to the solver algorithm.
299  return FinalCNF;
300 }
301 
302 } // namespace dataflow
303 } // namespace clang
#define X(type, name)
Definition: Value.h:143
do v
Definition: arm_acle.h:83
A boolean formula in 3-CNF (conjunctive normal form with at most 3 literals per clause).
Definition: CNFFormula.h:68
size_t clauseSize(ClauseID C) const
Returns the number of literals in clause C.
Definition: CNFFormula.h:122
CNFFormula(Variable LargestVar)
Definition: CNFFormula.cpp:99
llvm::ArrayRef< Literal > clauseLiterals(ClauseID C) const
Returns the literals of clause C.
Definition: CNFFormula.h:129
ClauseID numClauses() const
Returns the number of clauses in the formula.
Definition: CNFFormula.h:119
void addClause(ArrayRef< Literal > lits)
Adds the L1 v ...
Definition: CNFFormula.cpp:105
ArrayRef< const Formula * > operands() const
Definition: Formula.h:82
Atom getAtom() const
Definition: Formula.h:68
@ Equal
True if LHS is false or RHS is true.
Definition: Formula.h:64
@ Implies
True if either LHS or RHS is true.
Definition: Formula.h:63
@ AtomRef
A reference to an atomic boolean variable.
Definition: Formula.h:54
@ Literal
Constant true or false.
Definition: Formula.h:56
@ Or
True if LHS and RHS are both true.
Definition: Formula.h:62
@ And
True if its only operand is false.
Definition: Formula.h:61
bool literal() const
Definition: Formula.h:73
Kind kind() const
Definition: Formula.h:66
uint32_t ClauseID
Clause identifiers are represented as positive integers.
Definition: CNFFormula.h:42
constexpr Literal posLit(Variable V)
Returns the positive literal V.
Definition: CNFFormula.h:49
constexpr bool isNegLit(Literal L)
Returns whether L is a negative literal.
Definition: CNFFormula.h:58
CNFFormula buildCNF(const llvm::ArrayRef< const Formula * > &Formulas, llvm::DenseMap< Variable, Atom > &Atomics)
Converts the conjunction of Vals into a formula in conjunctive normal form where each clause has at l...
Definition: CNFFormula.cpp:116
constexpr Literal NullLit
A null literal is used as a placeholder in various data structures and algorithms.
Definition: CNFFormula.h:39
constexpr Variable var(Literal L)
Returns the variable of L.
Definition: CNFFormula.h:64
constexpr Literal negLit(Variable V)
Returns the negative literal !V.
Definition: CNFFormula.h:52
uint32_t Literal
Literals are represented as positive integers.
Definition: CNFFormula.h:35
uint32_t Variable
Boolean variables are represented as positive integers.
Definition: CNFFormula.h:25
constexpr bool isPosLit(Literal L)
Returns whether L is a positive literal.
Definition: CNFFormula.h:55
The JSON file list parser is used to communicate input to InstallAPI.
#define false
Definition: stdbool.h:26