carl  24.04
Computer ARithmetic Library
CNF.h
Go to the documentation of this file.
1 #pragma once
2 
3 #include "../Formula.h"
4 #include "ConstraintBounds.h"
5 #include "Negations.h"
6 #include "aux.h"
7 
8 namespace carl {
9 namespace formula_to_cnf {
10 
11 /**
12  * Constructs the equivalent of
13  * (iff lhs (and *rhs_and)))
14  * The result is the list
15  * (=> lhs (and *rhs_and))
16  * (=> rhs !lhs) (for each rhs in rhs_and)
17  */
18 template<typename Poly>
19 std::vector<Formula<Poly>> construct_iff(const Formula<Poly>& lhs, const std::vector<Formula<Poly>>& rhs_and) {
20  std::vector<Formula<Poly>> res;
21  Formulas<Poly> subs = { lhs };
22  for (const auto& sub: rhs_and) {
23  subs.emplace_back(!sub);
24  res.emplace_back(FormulaType::OR, sub, !lhs);
25  }
26  res.emplace_back(FormulaType::OR, std::move(subs));
27  return res;
28 }
29 
30 template<typename Poly>
31 using TseitinConstraints = std::vector<Formula<Poly>>;
32 template<typename Poly>
34 
35 /**
36  * Converts an OR to cnf.
37  *
38  */
39 template<typename Poly>
40 Formula<Poly> to_cnf_or(const Formula<Poly>& f, bool keep_constraints, bool simplify_combinations, bool tseitin_equivalence, TseitinConstraints<Poly>& tseitin) {
41  // Checks for immediate tautologies among constraints
42  ConstraintBounds<Poly> constraint_bounds;
43  // Resulting subformulas
44  Formulas<Poly> subformulas;
45  // Queue of subformulas to process
46  std::vector<Formula<Poly>> subformula_queue = { f };
47  while (!subformula_queue.empty()) {
48  auto current = subformula_queue.back();
49  CARL_LOG_DEBUG("carl.formula.cnf", "Processing " << current << " from " << subformula_queue);
50  subformula_queue.pop_back();
51 
52  switch (current.type()) {
53  case FormulaType::TRUE:
55  case FormulaType::FALSE:
56  break;
58  case FormulaType::BOOL:
59  case FormulaType::UEQ:
62  subformulas.emplace_back(current);
63  break;
65  // Try simplification with ConstraintBounds
66  if (simplify_combinations) {
67  if (addConstraintBound(constraint_bounds, current, false).is_false()) {
68  CARL_LOG_DEBUG("carl.formula.cnf", "Adding " << current << " to constraint bounds yielded a tautology");
70  }
71  } else {
72  subformulas.emplace_back(current);
73  }
74  break;
75  case FormulaType::NOT: {
76  // Resolve negation
77  auto resolved = resolve_negation(current, keep_constraints);
78  if (resolved.is_literal()) {
79  subformulas.emplace_back(resolved);
80  } else {
81  subformula_queue.emplace_back(resolved);
82  }
83  break;
84  }
86  // (=> A B) -> (not A), B
87  subformula_queue.emplace_back(!current.premise());
88  subformula_queue.emplace_back(current.conclusion());
89  break;
90  case FormulaType::ITE:
91  // (ite C T E) -> (and (=> C T) (=> (not C) E)) -> (and (or (not C) T) (or C E))
92  subformula_queue.emplace_back(Formula<Poly>(FormulaType::AND, {
94  !current.condition(), current.first_case()
95  }),
97  current.condition(), current.second_case()
98  })
99  }));
100  break;
101  case FormulaType::IFF: {
102  // (iff A ...) -> (and A ...), (and (not A) ...)
103  Formulas<Poly> subA;
104  Formulas<Poly> subB;
105  for (const auto& sub: current.subformulas()) {
106  subA.emplace_back(sub);
107  subB.emplace_back(!sub);
108  }
109  subformula_queue.emplace_back(Formula<Poly>(FormulaType::AND, std::move(subA)));
110  subformula_queue.emplace_back(Formula<Poly>(FormulaType::AND, std::move(subB)));
111  break;
112  }
113  case FormulaType::XOR: {
114  // (xor A B) -> (and A (not B)), (and (not A) B)
115  auto lhs = formula::aux::connectPrecedingSubformulas(current);
116  const auto& rhs = current.subformulas().back();
117  subformula_queue.emplace_back(Formula<Poly>(FormulaType::AND, { lhs, !rhs }));
118  subformula_queue.emplace_back(Formula<Poly>(FormulaType::AND, { !lhs, rhs }));
119  break;
120  }
121  case FormulaType::OR:
122  // Simply add subformulas to the queue
123  for (const auto& sub: current.subformulas()) {
124  subformula_queue.emplace_back(sub);
125  }
126  break;
127  case FormulaType::AND: {
128  // Replace by a fresh tseitin variable.
129  auto tseitinVar = FormulaPool<Poly>::getInstance().createTseitinVar(current);
130  if (tseitin_equivalence) {
131  tseitin.emplace_back(Formula<Poly>(FormulaType::IFF, { tseitinVar, current }));
132  } else {
133  tseitin.emplace_back(Formula<Poly>(FormulaType::IMPLIES, { tseitinVar, current }));
134  }
135  subformulas.emplace_back(tseitinVar);
136  break;
137  }
138  case FormulaType::EXISTS:
139  case FormulaType::FORALL:
140  CARL_LOG_ERROR("carl.formula.cnf", "Cannot transform quantified formula to CNF");
141  assert(false);
142  break;
143  }
144  }
145  if (simplify_combinations && swapConstraintBounds(constraint_bounds, subformulas, false)) {
147  } else if (subformulas.empty()) {
148  tseitin.clear();
150  } else if (subformulas.size() == 1) {
151  return subformulas.front();
152  }
153  return Formula<Poly>(FormulaType::OR, std::move(subformulas));
154 }
155 
156 }
157 
158 /**
159  * Converts the given formula to CNF.
160  * @param f Formula to convert.
161  * @param keep_constraints Indicates whether to keep constraints or allow to change them in resolve_negation().
162  * @param simplify_combinations Indicates whether we attempt to simplify combinations of constraints with ConstraintBounds.
163  * @param tseitin_equivalence Indicates whether we use implications or equivalences for tseitin variables.
164  * @return The formula in CNF.
165  */
166 template<typename Poly>
167 Formula<Poly> to_cnf(const Formula<Poly>& f, bool keep_constraints = true, bool simplify_combinations = false, bool tseitin_equivalence = true) {
168  if (!simplify_combinations && f.property_holds(PROP_IS_IN_CNF)) {
169  if (keep_constraints) {
170  return f;
171  } else if (f.type() == FormulaType::NOT) {
172  assert(f.is_literal());
173  return resolve_negation(f,keep_constraints);
174  }
175  } else if (f.is_atom()) {
176  return f;
177  }
178 
179  // Checks for immediate conflicts among constraints
180  formula_to_cnf::ConstraintBounds<Poly> constraint_bounds;
181  // Resulting subformulas
182  Formulas<Poly> subformulas;
183  // Queue of subformulas to process
184  std::vector<Formula<Poly>> subformula_queue = { f };
185  while (!subformula_queue.empty()) {
186  auto current = subformula_queue.back();
187  CARL_LOG_DEBUG("carl.formula.cnf", "Processing " << current << " from " << subformula_queue);
188  subformula_queue.pop_back();
189 
190  switch (current.type()) {
191  case FormulaType::TRUE:
192  break;
193  case FormulaType::FALSE:
196  case FormulaType::BOOL:
197  case FormulaType::UEQ:
200  subformulas.emplace_back(current);
201  break;
203  // Try simplification with ConstraintBounds
204  if (simplify_combinations) {
205  if (addConstraintBound(constraint_bounds, current, true).is_false()) {
206  CARL_LOG_DEBUG("carl.formula.cnf", "Adding " << current << " to constraint bounds yielded a conflict");
208  }
209  } else {
210  subformulas.emplace_back(current);
211  }
212  break;
213  case FormulaType::NOT: {
214  // Resolve negation
215  auto resolved = resolve_negation(current, keep_constraints);
216  if (resolved.is_literal()) {
217  subformulas.emplace_back(resolved);
218  } else {
219  subformula_queue.emplace_back(resolved);
220  }
221  break;
222  }
224  // (=> A B) -> (or (not A) B)
225  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, {
226  !current.premise(), current.conclusion()
227  }));
228  break;
229  case FormulaType::ITE:
230  // (ite C T E) -> (=> C T), (=> (not C) E) -> (or (not C) T), (or C E)
231  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, {
232  !current.condition(), current.first_case()
233  }));
234  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, {
235  current.condition(), current.second_case()
236  }));
237  break;
238  case FormulaType::IFF:
239  if (current.subformulas().size() == 2) {
240  const auto& lhs = current.subformulas().front();
241  const auto& rhs = current.subformulas().back();
242  if (lhs.type() == FormulaType::AND) {
243  auto tmp = formula_to_cnf::construct_iff(rhs, lhs.subformulas());
244  subformula_queue.insert(subformula_queue.end(), tmp.begin(), tmp.end());
245  } else if (rhs.type() == FormulaType::AND) {
246  auto tmp = formula_to_cnf::construct_iff(lhs, rhs.subformulas());
247  subformula_queue.insert(subformula_queue.end(), tmp.begin(), tmp.end());
248  } else {
249  // (iff A B) -> (or !A B), (or A !B)
250  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, { !lhs, rhs }));
251  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, { lhs, !rhs }));
252  }
253  } else {
254  // (iff A ...) -> (or (and A ...) (and (not A) ...))
255  Formulas<Poly> subA;
256  Formulas<Poly> subB;
257  for (const auto& sub: current.subformulas()) {
258  subA.emplace_back(sub);
259  subB.emplace_back(!sub);
260  }
261  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, {
262  Formula<Poly>(FormulaType::AND, std::move(subA)),
263  Formula<Poly>(FormulaType::AND, std::move(subB))
264  }));
265  }
266  break;
267  case FormulaType::XOR: {
268  // (xor A B) -> (or A B), (or (not A) (not B))
269  auto lhs = formula::aux::connectPrecedingSubformulas(current);
270  const auto& rhs = current.subformulas().back();
271  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, { lhs, rhs }));
272  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, { !lhs, !rhs }));
273  break;
274  }
275  case FormulaType::AND:
276  // Simply add subformulas to the queue
277  for (const auto& sub: current.subformulas()) {
278  subformula_queue.emplace_back(sub);
279  }
280  break;
281  case FormulaType::OR: {
282  // Call to_cnf_or() to obtain a clause of literals res and the newly created tseitin variables defined in tseitin.
284  auto res = formula_to_cnf::to_cnf_or(current, keep_constraints, simplify_combinations, tseitin_equivalence, tseitin);
285  if (res.is_false()) {
287  }
288  subformula_queue.insert(subformula_queue.end(), tseitin.begin(), tseitin.end());
289  subformulas.emplace_back(res);
290  break;
291  }
292  case FormulaType::EXISTS:
293  case FormulaType::FORALL:
294  CARL_LOG_ERROR("carl.formula.cnf", "Cannot transform quantified formula to CNF");
295  assert(false);
296  break;
297  }
298  }
299  if (simplify_combinations && swapConstraintBounds(constraint_bounds, subformulas, true)) {
301  } else if (subformulas.empty()) {
303  } else if (subformulas.size() == 1) {
304  return subformulas.front();
305  }
306  return Formula<Poly>(FormulaType::AND, std::move(subformulas));
307 }
308 
309 }
#define CARL_LOG_ERROR(channel, msg)
Definition: carl-logging.h:40
#define CARL_LOG_DEBUG(channel, msg)
Definition: carl-logging.h:43
carl is the main namespace for the library.
bool swapConstraintBounds(ConstraintBounds< Pol > &_constraintBounds, Formulas< Pol > &_intoFormulas, bool _inConjunction)
Stores for every polynomial for which we determined bounds for given constraints a minimal set of con...
@ CONSTRAINT
@ BITVECTOR
@ VARCOMPARE
@ VARASSIGN
Formula< Pol > addConstraintBound(ConstraintBounds< Pol > &_constraintBounds, const Formula< Pol > &_constraint, bool _inConjunction)
Adds the bound to the bounds of the polynomial specified by this constraint.
static constexpr Condition PROP_IS_IN_CNF
Definition: Condition.h:53
std::vector< Formula< Poly > > Formulas
Formula< Pol > resolve_negation(const Formula< Pol > &f, bool _keepConstraint=true, bool resolve_varcomp=false)
Resolves the outermost negation of this formula.
Definition: Negations.h:12
Formula< Poly > to_cnf(const Formula< Poly > &f, bool keep_constraints=true, bool simplify_combinations=false, bool tseitin_equivalence=true)
Converts the given formula to CNF.
Definition: CNF.h:167
std::unordered_map< T1, T2, std::hash< T1 > > FastMap
Formula< Pol > connectPrecedingSubformulas(const Formula< Pol > &f)
[Auxiliary method]
Definition: aux.h:13
std::vector< Formula< Poly > > construct_iff(const Formula< Poly > &lhs, const std::vector< Formula< Poly >> &rhs_and)
Constructs the equivalent of (iff lhs (and *rhs_and))) The result is the list (=> lhs (and *rhs_and))...
Definition: CNF.h:19
FastMap< Poly, std::map< typename Poly::NumberType, std::pair< Relation, Formula< Poly > >> > ConstraintBounds
Definition: CNF.h:33
std::vector< Formula< Poly > > TseitinConstraints
Definition: CNF.h:31
Formula< Poly > to_cnf_or(const Formula< Poly > &f, bool keep_constraints, bool simplify_combinations, bool tseitin_equivalence, TseitinConstraints< Poly > &tseitin)
Converts an OR to cnf.
Definition: CNF.h:40
static FormulaPool< Pol > & getInstance()
Returns the single instance of this class by reference.
Definition: Singleton.h:45
bool is_atom() const
Definition: Formula.h:541
bool property_holds(const Condition &_property) const
Checks if the given property holds for this formula.
Definition: Formula.h:533
bool is_literal() const
Definition: Formula.h:549
FormulaType type() const
Definition: Formula.h:254
const Formula & back() const
Definition: Formula.h:518