dlinear  0.0.1
Delta-complete SMT solver for linear programming
Loading...
Searching...
No Matches
TseitinCnfizer.cpp
1
7#include "TseitinCnfizer.h"
8
9#include <algorithm>
10#include <cstddef>
11#include <iterator>
12#include <set>
13#include <string>
14#include <utility>
15
16#include "dlinear/util/Stats.h"
17#include "dlinear/util/Timer.h"
18#include "dlinear/util/exception.h"
19
20namespace dlinear {
21
22namespace {
23// A class to show statistics information at destruction.
24
25// Forward declarations for the helper functions.
26void Cnfize(const Variable &b, const Formula &f, std::vector<Formula> *clauses);
27void CnfizeNegation(const Variable &b, const Formula &f, std::vector<Formula> *clauses);
28void CnfizeConjunction(const Variable &b, const Formula &f, std::vector<Formula> *clauses);
29void CnfizeDisjunction(const Variable &b, const Formula &f, std::vector<Formula> *clauses);
30} // namespace
31
32// The main function of the TseitinCnfizer:
33// - It visits each node and introduce a Boolean variable `b` for
34// each subterm `f`, and keep the relation `b ⇔ f`.
35// - Then it cnfizes each `b ⇔ f` and make a conjunction of them.
36std::vector<Formula> TseitinCnfizer::Process(const Formula &f) const {
37 const TimerGuard timer_guard(&stats_.m_timer(), stats_.enabled());
39
40 std::map<Variable, Formula> map;
41 std::vector<Formula> ret;
42 const Formula head{VisitFormula(f, map)};
43 if (map.empty()) {
44 return {head};
45 }
46 for (auto const &p : map) {
47 if (get_variable(head).equal_to(p.first)) {
48 if (is_conjunction(p.second)) {
49 const std::set<Formula> &conjuncts(get_operands(p.second));
50 copy(conjuncts.begin(), conjuncts.end(), back_inserter(ret));
51 } else {
52 ret.push_back(p.second);
53 }
54 } else {
55 Cnfize(p.first, p.second, &ret);
56 }
57 }
58 return ret;
59}
60std::vector<Formula> TseitinCnfizer::operator()(const Formula &f) const { return Process(f); }
61
62Formula TseitinCnfizer::VisitForall(const Formula &f, std::map<Variable, Formula> &map) const {
63 // Given: f := ∀y. φ(x, y), this process CNFizes φ(x, y) and push the
64 // universal quantifier over conjunctions:
65 //
66 // = ∀y. (clause₁(x, y) ∧ ... ∧ clauseₙ(x, y))
67 // = (∀y. clause₁(x, y)) ∧ ... ∧ (∀y. clauseₙ(x, y))
68 const Variables &quantified_variables{get_quantified_variables(f)}; // y
69 const Formula &quantified_formula{get_quantified_formula(f)}; // φ(x, y)
70 // clause₁(x, y) ∧ ... ∧ clauseₙ(x, y)
71 const std::set<Formula> clauses{get_clauses(naive_cnfizer_.Process(quantified_formula))};
72 const std::set<Formula> new_clauses{::dlinear::map(clauses, [&quantified_variables](const Formula &clause) {
73 DLINEAR_ASSERT(is_clause(clause), "Must be a clause");
74 if (HaveIntersection(clause.GetFreeVariables(), quantified_variables)) {
75 return forall(quantified_variables, clause);
76 } else {
77 return clause;
78 }
79 })};
80
81 DLINEAR_ASSERT(!new_clauses.empty(), "Clause must not be empty");
82 if (new_clauses.size() == 1) {
83 return *(new_clauses.begin());
84 } else {
85 static std::size_t id{0};
86 const Variable bvar{std::string("forall") + std::to_string(id++), Variable::Type::BOOLEAN};
87 map.emplace(bvar, make_conjunction(new_clauses));
88 return Formula{bvar};
89 }
90}
91
92Formula TseitinCnfizer::VisitConjunction(const Formula &f, std::map<Variable, Formula> &map) const {
93 // Introduce a new Boolean variable, `bvar` for `f` and record the
94 // relation `bvar ⇔ f`.
95 static std::size_t id{0};
96 const std::set<Formula> transformed_operands{::dlinear::map(
97 get_operands(f), [this, &map](const Formula &formula) { return this->VisitFormula(formula, map); })};
98 const Variable bvar{std::string("conj") + std::to_string(id++), Variable::Type::BOOLEAN};
99 map.emplace(bvar, make_conjunction(transformed_operands));
100 return Formula{bvar};
101}
102
103Formula TseitinCnfizer::VisitDisjunction(const Formula &f, std::map<Variable, Formula> &map) const {
104 static std::size_t id{0};
105 const std::set<Formula> &transformed_operands{::dlinear::map(
106 get_operands(f), [this, &map](const Formula &formula) { return this->VisitFormula(formula, map); })};
107 const Variable bvar{std::string("disj") + std::to_string(id++), Variable::Type::BOOLEAN};
108 map.emplace(bvar, make_disjunction(transformed_operands));
109 return Formula{bvar};
110}
111
112Formula TseitinCnfizer::VisitNegation(const Formula &f, std::map<Variable, Formula> &map) const {
113 const Formula &operand{get_operand(f)};
114 if (is_atomic(operand)) {
115 return f;
116 } else {
117 const Variable bvar{"neg", Variable::Type::BOOLEAN};
118 const Formula transformed_operand{VisitFormula(operand, map)};
119 map.emplace(bvar, !transformed_operand);
120 return Formula{bvar};
121 }
122}
123
124namespace {
125// Cnfize b ⇔ f and add it to @p clauses. It calls Cnfize<FormulaKind> using
126// pattern-matching.
127void Cnfize(const Variable &b, const Formula &f, std::vector<Formula> *clauses) {
128 switch (f.get_kind()) {
129 case FormulaKind::False:
130 DLINEAR_UNREACHABLE();
131 case FormulaKind::True:
132 DLINEAR_UNREACHABLE();
133 case FormulaKind::Var:
134 DLINEAR_UNREACHABLE();
135 case FormulaKind::Eq:
136 DLINEAR_UNREACHABLE();
137 case FormulaKind::Neq:
138 DLINEAR_UNREACHABLE();
139 case FormulaKind::Gt:
140 DLINEAR_UNREACHABLE();
141 case FormulaKind::Geq:
142 DLINEAR_UNREACHABLE();
143 case FormulaKind::Lt:
144 DLINEAR_UNREACHABLE();
145 case FormulaKind::Leq:
146 DLINEAR_UNREACHABLE();
147 case FormulaKind::Forall:
148 DLINEAR_UNREACHABLE();
149 case FormulaKind::And:
150 return CnfizeConjunction(b, f, clauses);
151 case FormulaKind::Or:
152 return CnfizeDisjunction(b, f, clauses);
153 case FormulaKind::Not:
154 return CnfizeNegation(b, f, clauses);
155 }
156 DLINEAR_UNREACHABLE();
157}
158
159// Add f to clauses if f is not true.
160void Add(const Formula &f, std::vector<Formula> *clauses) {
161 if (!is_true(f)) {
162 clauses->push_back(f);
163 }
164}
165
166// Add f₁ ⇔ f₂ to clauses.
167void AddIff(const Formula &f1, const Formula &f2, std::vector<Formula> *clauses) {
168 Add(imply(f1, f2), clauses);
169 Add(imply(f2, f1), clauses);
170}
171
172// Cnfize b ⇔ ¬b₁ using the following equalities and add to clauses:
173// b ⇔ ¬b₁
174// = (b → ¬b₁) ∧ (¬b₁ → b)
175// = (¬b ∨ ¬b₁) ∧ (b₁ ∨ b) (✓CNF)
176void CnfizeNegation(const Variable &b, const Formula &f, std::vector<Formula> *clauses) {
177 AddIff(Formula{b}, f, clauses);
178} // namespace
179
180// Cnfize b ⇔ (b₁ ∧ ... ∧ bₙ) using the following equalities and add
181// to clauses:
182// b ⇔ (b₁ ∧ ... ∧ bₙ)
183// = (b → (b₁ ∧ ... ∧ bₙ)) ∧ ((b₁ ∧ ... ∧ bₙ) → b)
184// = (¬b ∨ (b₁ ∧ ... ∧ bₙ)) ∧ (¬b₁ ∨ ... ∨ ¬bₙ ∨ b)
185// = (¬b ∨ b₁) ∧ ... (¬b ∨ bₙ) ∧ (¬b₁ ∨ ... ∨ ¬bₙ ∨ b) (✓CNF)
186void CnfizeConjunction(const Variable &b, const Formula &f, std::vector<Formula> *clauses) {
187 // operands = {b₁, ..., bₙ}
188 const std::set<Formula> &operands{get_operands(f)};
189 // negated_operands = {¬b₁, ..., ¬bₙ}
190 const std::set<Formula> &negated_operands{map(operands, [](const Formula &formula) { return !formula; })};
191 Formula ret{Formula::True()};
192 for (const Formula &b_i : operands) {
193 Add(!b || b_i, clauses);
194 }
195 Add(make_disjunction(negated_operands) || b, clauses);
196}
197
198// Cnfize b ⇔ (b₁ ∨ ... ∨ bₙ) using the following equalities and add
199// to clauses:
200// b ⇔ (b₁ ∨ ... ∨ bₙ)
201// = (b → (b₁ ∨ ... ∨ bₙ)) ∧ ((b₁ ∨ ... ∨ bₙ) → b)
202// = (¬b ∨ b₁ ∨ ... ∨ bₙ) ∧ ((¬b₁ ∧ ... ∧ ¬bₙ) ∨ b)
203// = (¬b ∨ b₁ ∨ ... ∨ bₙ) ∧ (¬b₁ ∨ b) ∧ ... ∧ (¬bₙ ∨ b) (✓CNF)
204void CnfizeDisjunction(const Variable &b, const Formula &f, std::vector<Formula> *clauses) {
205 // negated_operands = {¬b₁, ..., ¬bₙ}
206 const std::set<Formula> &negated_operands{map(get_operands(f), [](const Formula &formula) { return !formula; })};
207 Add(!b || f, clauses); // (¬b ∨ b₁ ∨ ... ∨ bₙ)
208 for (const Formula &neg_b_i : negated_operands) {
209 Add(neg_b_i || b, clauses);
210 }
211}
212
213} // namespace
214
215} // namespace dlinear
void Increase()
Increase the iteration counter by one.
Definition Stats.cpp:39
Formula Process(const Formula &f) const
Convert a f into an equivalent formula f' in CNF.
Timer & m_timer()
Get read-write access to the timer of the stats.
Definition Stats.h:50
bool enabled() const
Check whether the stats is enabled.
Definition Stats.h:48
The TimeGuard wraps a timer object and pauses it when the guard object is destructed.
Definition Timer.h:129
const NaiveCnfizer naive_cnfizer_
Naive CNFizer. Transforms nested formulas inside universal quantification.
std::vector< Formula > Process(const Formula &f) const
Convert f into an equi-satisfiable formula in CNF.
Represents a symbolic form of a first-order logic formula.
@ BOOLEAN
A BOOLEAN variable takes a bool value.
bool equal_to(const Variable &v) const
Checks the equality of two variables based on their ID values.
Global namespace for the dlinear library.
Formula make_conjunction(const std::vector< Formula > &formulas)
Make conjunction of formulas.
Definition symbolic.cpp:485
std::set< Formula > map(const std::set< Formula > &formulas, const std::function< Formula(const Formula &)> &func)
Given formulas = {f₁, ..., fₙ} and a func : Formula → Formula, map(formulas, func) returns a set {fun...
Definition symbolic.cpp:47
std::set< Formula > get_clauses(const Formula &f)
Returns the set of clauses in f.
Definition symbolic.cpp:98
Formula imply(const Formula &f1, const Formula &f2)
Return a formula f1 ⇒ f2.
Definition symbolic.cpp:34
Formula make_disjunction(const std::vector< Formula > &formulas)
Make disjunction of formulas.
Definition symbolic.cpp:493
bool is_clause(const Formula &f)
Check if f is a clause.
Definition symbolic.cpp:78
bool HaveIntersection(const Variables &variables1, const Variables &variables2)
Check if the intersection of variables1 and variables2 is non-empty.
Definition symbolic.cpp:126
bool is_atomic(const Formula &f)
Check if f is atomic.
Definition symbolic.cpp:53