carl  25.02
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:
140  case FormulaType::FORALL:
141  CARL_LOG_ERROR("carl.formula.cnf", "Cannot transform quantified formula to CNF");
142  assert(false);
143  break;
144  }
145  }
146  if (simplify_combinations && swapConstraintBounds(constraint_bounds, subformulas, false)) {
148  } else if (subformulas.empty()) {
149  tseitin.clear();
151  } else if (subformulas.size() == 1) {
152  return subformulas.front();
153  }
154  return Formula<Poly>(FormulaType::OR, std::move(subformulas));
155 }
156 
157 }
158 
159 /**
160  * Converts the given formula to CNF.
161  * @param f Formula to convert.
162  * @param keep_constraints Indicates whether to keep constraints or allow to change them in resolve_negation().
163  * @param simplify_combinations Indicates whether we attempt to simplify combinations of constraints with ConstraintBounds.
164  * @param tseitin_equivalence Indicates whether we use implications or equivalences for tseitin variables.
165  * @return The formula in CNF.
166  */
167 template<typename Poly>
168 Formula<Poly> to_cnf(const Formula<Poly>& f, bool keep_constraints = true, bool simplify_combinations = false, bool tseitin_equivalence = true) {
169  if (!simplify_combinations && f.property_holds(PROP_IS_IN_CNF)) {
170  if (keep_constraints) {
171  return f;
172  } else if (f.type() == FormulaType::NOT) {
173  assert(f.is_literal());
174  return resolve_negation(f,keep_constraints);
175  }
176  } else if (f.is_atom()) {
177  return f;
178  }
179 
180  // Checks for immediate conflicts among constraints
181  formula_to_cnf::ConstraintBounds<Poly> constraint_bounds;
182  // Resulting subformulas
183  Formulas<Poly> subformulas;
184  // Queue of subformulas to process
185  std::vector<Formula<Poly>> subformula_queue = { f };
186  while (!subformula_queue.empty()) {
187  auto current = subformula_queue.back();
188  CARL_LOG_DEBUG("carl.formula.cnf", "Processing " << current << " from " << subformula_queue);
189  subformula_queue.pop_back();
190 
191  switch (current.type()) {
192  case FormulaType::TRUE:
193  break;
194  case FormulaType::FALSE:
197  case FormulaType::BOOL:
198  case FormulaType::UEQ:
201  subformulas.emplace_back(current);
202  break;
204  // Try simplification with ConstraintBounds
205  if (simplify_combinations) {
206  if (addConstraintBound(constraint_bounds, current, true).is_false()) {
207  CARL_LOG_DEBUG("carl.formula.cnf", "Adding " << current << " to constraint bounds yielded a conflict");
209  }
210  } else {
211  subformulas.emplace_back(current);
212  }
213  break;
214  case FormulaType::NOT: {
215  // Resolve negation
216  auto resolved = resolve_negation(current, keep_constraints);
217  if (resolved.is_literal()) {
218  subformulas.emplace_back(resolved);
219  } else {
220  subformula_queue.emplace_back(resolved);
221  }
222  break;
223  }
225  // (=> A B) -> (or (not A) B)
226  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, {
227  !current.premise(), current.conclusion()
228  }));
229  break;
230  case FormulaType::ITE:
231  // (ite C T E) -> (=> C T), (=> (not C) E) -> (or (not C) T), (or C E)
232  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, {
233  !current.condition(), current.first_case()
234  }));
235  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, {
236  current.condition(), current.second_case()
237  }));
238  break;
239  case FormulaType::IFF:
240  if (current.subformulas().size() == 2) {
241  const auto& lhs = current.subformulas().front();
242  const auto& rhs = current.subformulas().back();
243  if (lhs.type() == FormulaType::AND) {
244  auto tmp = formula_to_cnf::construct_iff(rhs, lhs.subformulas());
245  subformula_queue.insert(subformula_queue.end(), tmp.begin(), tmp.end());
246  } else if (rhs.type() == FormulaType::AND) {
247  auto tmp = formula_to_cnf::construct_iff(lhs, rhs.subformulas());
248  subformula_queue.insert(subformula_queue.end(), tmp.begin(), tmp.end());
249  } else {
250  // (iff A B) -> (or !A B), (or A !B)
251  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, { !lhs, rhs }));
252  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, { lhs, !rhs }));
253  }
254  } else {
255  // (iff A ...) -> (or (and A ...) (and (not A) ...))
256  Formulas<Poly> subA;
257  Formulas<Poly> subB;
258  for (const auto& sub: current.subformulas()) {
259  subA.emplace_back(sub);
260  subB.emplace_back(!sub);
261  }
262  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, {
263  Formula<Poly>(FormulaType::AND, std::move(subA)),
264  Formula<Poly>(FormulaType::AND, std::move(subB))
265  }));
266  }
267  break;
268  case FormulaType::XOR: {
269  // (xor A B) -> (or A B), (or (not A) (not B))
270  auto lhs = formula::aux::connectPrecedingSubformulas(current);
271  const auto& rhs = current.subformulas().back();
272  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, { lhs, rhs }));
273  subformula_queue.emplace_back(Formula<Poly>(FormulaType::OR, { !lhs, !rhs }));
274  break;
275  }
276  case FormulaType::AND:
277  // Simply add subformulas to the queue
278  for (const auto& sub: current.subformulas()) {
279  subformula_queue.emplace_back(sub);
280  }
281  break;
282  case FormulaType::OR: {
283  // Call to_cnf_or() to obtain a clause of literals res and the newly created tseitin variables defined in tseitin.
285  auto res = formula_to_cnf::to_cnf_or(current, keep_constraints, simplify_combinations, tseitin_equivalence, tseitin);
286  if (res.is_false()) {
288  }
289  subformula_queue.insert(subformula_queue.end(), tseitin.begin(), tseitin.end());
290  subformulas.emplace_back(res);
291  break;
292  }
293  case FormulaType::EXISTS:
294  case FormulaType::FORALL:
295  CARL_LOG_ERROR("carl.formula.cnf", "Cannot transform quantified formula to CNF");
296  assert(false);
297  break;
298  }
299  }
300  if (simplify_combinations && swapConstraintBounds(constraint_bounds, subformulas, true)) {
302  } else if (subformulas.empty()) {
304  } else if (subformulas.size() == 1) {
305  return subformulas.front();
306  }
307  return Formula<Poly>(FormulaType::AND, std::move(subformulas));
308 }
309 
310 }
#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
@ AUX_EXISTS
@ 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:168
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:563
bool property_holds(const Condition &_property) const
Checks if the given property holds for this formula.
Definition: Formula.h:555
bool is_literal() const
Definition: Formula.h:571
FormulaType type() const
Definition: Formula.h:262
const Formula & back() const
Definition: Formula.h:540