1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Haniel Barbosa, Morgan Deters |
4 |
|
* |
5 |
|
* This file is part of the cvc5 project. |
6 |
|
* |
7 |
|
* Copyright (c) 2009-2021 by the authors listed in the file AUTHORS |
8 |
|
* in the top-level source directory and their institutional affiliations. |
9 |
|
* All rights reserved. See the file COPYING in the top-level source |
10 |
|
* directory for licensing information. |
11 |
|
* **************************************************************************** |
12 |
|
* |
13 |
|
* Rewriter for the theory of inductive quantifiers. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H |
19 |
|
#define CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H |
20 |
|
|
21 |
|
#include "proof/trust_node.h" |
22 |
|
#include "theory/theory_rewriter.h" |
23 |
|
|
24 |
|
namespace cvc5 { |
25 |
|
|
26 |
|
class Options; |
27 |
|
|
28 |
|
namespace theory { |
29 |
|
namespace quantifiers { |
30 |
|
|
31 |
|
struct QAttributes; |
32 |
|
|
33 |
|
/** |
34 |
|
* List of steps used by the quantifiers rewriter, details on these steps |
35 |
|
* can be found in the class below. |
36 |
|
*/ |
37 |
|
enum RewriteStep |
38 |
|
{ |
39 |
|
/** Eliminate symbols (e.g. implies, xor) */ |
40 |
|
COMPUTE_ELIM_SYMBOLS = 0, |
41 |
|
/** Miniscoping */ |
42 |
|
COMPUTE_MINISCOPING, |
43 |
|
/** Aggressive miniscoping */ |
44 |
|
COMPUTE_AGGRESSIVE_MINISCOPING, |
45 |
|
/** Apply the extended rewriter to quantified formula bodies */ |
46 |
|
COMPUTE_EXT_REWRITE, |
47 |
|
/** |
48 |
|
* Term processing (e.g. simplifying terms based on ITE lifting, |
49 |
|
* eliminating extended arithmetic symbols). |
50 |
|
*/ |
51 |
|
COMPUTE_PROCESS_TERMS, |
52 |
|
/** Prenexing */ |
53 |
|
COMPUTE_PRENEX, |
54 |
|
/** Variable elimination */ |
55 |
|
COMPUTE_VAR_ELIMINATION, |
56 |
|
/** Conditional splitting */ |
57 |
|
COMPUTE_COND_SPLIT, |
58 |
|
/** Placeholder for end of steps */ |
59 |
|
COMPUTE_LAST |
60 |
|
}; |
61 |
|
std::ostream& operator<<(std::ostream& out, RewriteStep s); |
62 |
|
|
63 |
6382 |
class QuantifiersRewriter : public TheoryRewriter |
64 |
|
{ |
65 |
|
public: |
66 |
|
QuantifiersRewriter(const Options& opts); |
67 |
|
/** Pre-rewrite n */ |
68 |
|
RewriteResponse preRewrite(TNode in) override; |
69 |
|
/** Post-rewrite n */ |
70 |
|
RewriteResponse postRewrite(TNode in) override; |
71 |
|
|
72 |
|
static bool isLiteral( Node n ); |
73 |
|
//-------------------------------------variable elimination utilities |
74 |
|
/** is variable elimination |
75 |
|
* |
76 |
|
* Returns true if v is not a subterm of s, and the type of s is a subtype of |
77 |
|
* the type of v. |
78 |
|
*/ |
79 |
|
static bool isVarElim(Node v, Node s); |
80 |
|
/** get variable elimination literal |
81 |
|
* |
82 |
|
* If n asserted with polarity pol in body, and is equivalent to an equality |
83 |
|
* of the form v = s for some v in args, where isVariableElim( v, s ) holds, |
84 |
|
* then this method removes v from args, adds v to vars, adds s to subs, and |
85 |
|
* returns true. Otherwise, it returns false. |
86 |
|
*/ |
87 |
|
bool getVarElimLit(Node body, |
88 |
|
Node n, |
89 |
|
bool pol, |
90 |
|
std::vector<Node>& args, |
91 |
|
std::vector<Node>& vars, |
92 |
|
std::vector<Node>& subs) const; |
93 |
|
/** |
94 |
|
* Get variable eliminate for an equality based on theory-specific reasoning. |
95 |
|
*/ |
96 |
|
static Node getVarElimEq(Node lit, const std::vector<Node>& args, Node& var); |
97 |
|
/** variable eliminate for real equalities |
98 |
|
* |
99 |
|
* If this returns a non-null value ret, then var is updated to a member of |
100 |
|
* args, lit is equivalent to ( var = ret ). |
101 |
|
*/ |
102 |
|
static Node getVarElimEqReal(Node lit, |
103 |
|
const std::vector<Node>& args, |
104 |
|
Node& var); |
105 |
|
/** variable eliminate for bit-vector equalities |
106 |
|
* |
107 |
|
* If this returns a non-null value ret, then var is updated to a member of |
108 |
|
* args, lit is equivalent to ( var = ret ). |
109 |
|
*/ |
110 |
|
static Node getVarElimEqBv(Node lit, |
111 |
|
const std::vector<Node>& args, |
112 |
|
Node& var); |
113 |
|
/** variable eliminate for string equalities |
114 |
|
* |
115 |
|
* If this returns a non-null value ret, then var is updated to a member of |
116 |
|
* args, lit is equivalent to ( var = ret ). |
117 |
|
*/ |
118 |
|
static Node getVarElimEqString(Node lit, |
119 |
|
const std::vector<Node>& args, |
120 |
|
Node& var); |
121 |
|
/** get variable elimination |
122 |
|
* |
123 |
|
* If there exists an n with some polarity in body, and entails a literal that |
124 |
|
* corresponds to a variable elimination for some v via the above method |
125 |
|
* getVarElimLit, we return true. In this case, we update args/vars/subs |
126 |
|
* based on eliminating v. |
127 |
|
*/ |
128 |
|
bool getVarElim(Node body, |
129 |
|
std::vector<Node>& args, |
130 |
|
std::vector<Node>& vars, |
131 |
|
std::vector<Node>& subs) const; |
132 |
|
/** has variable elimination |
133 |
|
* |
134 |
|
* Returns true if n asserted with polarity pol entails a literal for |
135 |
|
* which variable elimination is possible. |
136 |
|
*/ |
137 |
|
bool hasVarElim(Node n, bool pol, std::vector<Node>& args) const; |
138 |
|
/** compute variable elimination inequality |
139 |
|
* |
140 |
|
* This method eliminates variables from the body of quantified formula |
141 |
|
* "body" using (global) reasoning about inequalities. In particular, if there |
142 |
|
* exists a variable x that only occurs in body or annotation qa in literals |
143 |
|
* of the form x>=t with a fixed polarity P, then we may replace all such |
144 |
|
* literals with P. For example, note that: |
145 |
|
* forall xy. x>y OR P(y) is equivalent to forall y. P(y). |
146 |
|
* |
147 |
|
* In the case that a variable x from args can be eliminated in this way, |
148 |
|
* we remove x from args, add x >= t1, ..., x >= tn to bounds, add false, ..., |
149 |
|
* false to subs, and return true. |
150 |
|
*/ |
151 |
|
static bool getVarElimIneq(Node body, |
152 |
|
std::vector<Node>& args, |
153 |
|
std::vector<Node>& bounds, |
154 |
|
std::vector<Node>& subs, |
155 |
|
QAttributes& qa); |
156 |
|
//-------------------------------------end variable elimination utilities |
157 |
|
/** |
158 |
|
* Eliminates IMPLIES/XOR, removes duplicates/infers tautologies of AND/OR, |
159 |
|
* and computes NNF. |
160 |
|
*/ |
161 |
|
Node computeElimSymbols(Node body) const; |
162 |
|
/** |
163 |
|
* Compute miniscoping in quantified formula q with attributes in qa. |
164 |
|
*/ |
165 |
|
Node computeMiniscoping(Node q, QAttributes& qa) const; |
166 |
|
Node computeAggressiveMiniscoping(std::vector<Node>& args, Node body) const; |
167 |
|
/** |
168 |
|
* This function removes top-level quantifiers from subformulas of body |
169 |
|
* appearing with overall polarity pol. It adds quantified variables that |
170 |
|
* appear in positive polarity positions into args, and those at negative |
171 |
|
* polarity positions in nargs. |
172 |
|
* |
173 |
|
* If prenexAgg is true, we ensure that all top-level quantifiers are |
174 |
|
* eliminated from subformulas. This means that we must expand ITE and |
175 |
|
* Boolean equalities to ensure that quantifiers are at fixed polarities. |
176 |
|
* |
177 |
|
* For example, calling this function on: |
178 |
|
* (or (forall ((x Int)) (P x z)) (not (forall ((y Int)) (Q y z)))) |
179 |
|
* would return: |
180 |
|
* (or (P x z) (not (Q y z))) |
181 |
|
* and add {x} to args, and {y} to nargs. |
182 |
|
*/ |
183 |
|
Node computePrenex(Node q, |
184 |
|
Node body, |
185 |
|
std::unordered_set<Node>& args, |
186 |
|
std::unordered_set<Node>& nargs, |
187 |
|
bool pol, |
188 |
|
bool prenexAgg) const; |
189 |
|
Node computeSplit(std::vector<Node>& args, Node body, QAttributes& qa) const; |
190 |
|
|
191 |
|
static bool isPrenexNormalForm(Node n); |
192 |
|
static Node mkForAll(const std::vector<Node>& args, |
193 |
|
Node body, |
194 |
|
QAttributes& qa); |
195 |
|
static Node mkForall(const std::vector<Node>& args, |
196 |
|
Node body, |
197 |
|
bool marked = false); |
198 |
|
static Node mkForall(const std::vector<Node>& args, |
199 |
|
Node body, |
200 |
|
std::vector<Node>& iplc, |
201 |
|
bool marked = false); |
202 |
|
|
203 |
|
private: |
204 |
|
/** |
205 |
|
* Helper method for getVarElim, called when n has polarity pol in body. |
206 |
|
*/ |
207 |
|
bool getVarElimInternal(Node body, |
208 |
|
Node n, |
209 |
|
bool pol, |
210 |
|
std::vector<Node>& args, |
211 |
|
std::vector<Node>& vars, |
212 |
|
std::vector<Node>& subs) const; |
213 |
|
bool addCheckElimChild(std::vector<Node>& children, |
214 |
|
Node c, |
215 |
|
Kind k, |
216 |
|
std::map<Node, bool>& lit_pol, |
217 |
|
bool& childrenChanged) const; |
218 |
|
static void computeArgs(const std::vector<Node>& args, |
219 |
|
std::map<Node, bool>& activeMap, |
220 |
|
Node n, |
221 |
|
std::map<Node, bool>& visited); |
222 |
|
static void computeArgVec(const std::vector<Node>& args, |
223 |
|
std::vector<Node>& activeArgs, |
224 |
|
Node n); |
225 |
|
static void computeArgVec2(const std::vector<Node>& args, |
226 |
|
std::vector<Node>& activeArgs, |
227 |
|
Node n, |
228 |
|
Node ipl); |
229 |
|
Node computeProcessTerms2(Node body, |
230 |
|
std::map<Node, Node>& cache, |
231 |
|
std::vector<Node>& new_vars, |
232 |
|
std::vector<Node>& new_conds) const; |
233 |
|
static void computeDtTesterIteSplit( |
234 |
|
Node n, |
235 |
|
std::map<Node, Node>& pcons, |
236 |
|
std::map<Node, std::map<int, Node> >& ncons, |
237 |
|
std::vector<Node>& conj); |
238 |
|
|
239 |
|
//-------------------------------------variable elimination |
240 |
|
/** compute variable elimination |
241 |
|
* |
242 |
|
* This computes variable elimination rewrites for a body of a quantified |
243 |
|
* formula with bound variables args. This method updates args to args' and |
244 |
|
* returns a node body' such that (forall args. body) is equivalent to |
245 |
|
* (forall args'. body'). An example of a variable elimination rewrite is: |
246 |
|
* forall xy. x != a V P( x,y ) ---> forall y. P( a, y ) |
247 |
|
*/ |
248 |
|
Node computeVarElimination(Node body, |
249 |
|
std::vector<Node>& args, |
250 |
|
QAttributes& qa) const; |
251 |
|
//-------------------------------------end variable elimination |
252 |
|
//-------------------------------------conditional splitting |
253 |
|
/** compute conditional splitting |
254 |
|
* |
255 |
|
* This computes conditional splitting rewrites for a body of a quantified |
256 |
|
* formula with bound variables args. It returns a body' that is equivalent |
257 |
|
* to body. We split body into a conjunction if variable elimination can |
258 |
|
* occur in one of the conjuncts. Examples of this are: |
259 |
|
* ite( x=a, P(x), Q(x) ) ----> ( x!=a V P(x) ) ^ ( x=a V Q(x) ) |
260 |
|
* (x=a) = P(x) ----> ( x!=a V P(x) ) ^ ( x=a V ~P(x) ) |
261 |
|
* ( x!=a ^ P(x) ) V Q(x) ---> ( x!=a V Q(x) ) ^ ( P(x) V Q(x) ) |
262 |
|
* where in each case, x can be eliminated in the first conjunct. |
263 |
|
*/ |
264 |
|
Node computeCondSplit(Node body, |
265 |
|
const std::vector<Node>& args, |
266 |
|
QAttributes& qa) const; |
267 |
|
//-------------------------------------end conditional splitting |
268 |
|
//------------------------------------- process terms |
269 |
|
/** compute process terms |
270 |
|
* |
271 |
|
* This takes as input a quantified formula q with attributes qa whose |
272 |
|
* body is body. |
273 |
|
* |
274 |
|
* This rewrite eliminates problematic terms from the bodies of |
275 |
|
* quantified formulas, which includes performing: |
276 |
|
* - Certain cases of ITE lifting, |
277 |
|
* - Elimination of extended arithmetic functions like to_int/is_int/div/mod, |
278 |
|
* - Elimination of select over store. |
279 |
|
* |
280 |
|
* It may introduce new variables V into new_vars and new conditions C into |
281 |
|
* new_conds. It returns a node retBody such that q of the form |
282 |
|
* forall X. body |
283 |
|
* is equivalent to: |
284 |
|
* forall X, V. ( C => retBody ) |
285 |
|
*/ |
286 |
|
Node computeProcessTerms(Node body, |
287 |
|
std::vector<Node>& new_vars, |
288 |
|
std::vector<Node>& new_conds, |
289 |
|
Node q, |
290 |
|
QAttributes& qa) const; |
291 |
|
//------------------------------------- end process terms |
292 |
|
//------------------------------------- extended rewrite |
293 |
|
/** compute extended rewrite |
294 |
|
* |
295 |
|
* This returns the result of applying the extended rewriter on the body |
296 |
|
* of quantified formula q. |
297 |
|
*/ |
298 |
|
static Node computeExtendedRewrite(Node q); |
299 |
|
//------------------------------------- end extended rewrite |
300 |
|
/** |
301 |
|
* Return true if we should do operation computeOption on quantified formula |
302 |
|
* q with attributes qa. |
303 |
|
*/ |
304 |
|
bool doOperation(Node q, RewriteStep computeOption, QAttributes& qa) const; |
305 |
|
/** |
306 |
|
* Return the rewritten form of q after applying operator computeOption to it. |
307 |
|
*/ |
308 |
|
Node computeOperation(Node q, |
309 |
|
RewriteStep computeOption, |
310 |
|
QAttributes& qa) const; |
311 |
|
/** Reference to the options */ |
312 |
|
const Options& d_opts; |
313 |
|
}; /* class QuantifiersRewriter */ |
314 |
|
|
315 |
|
} // namespace quantifiers |
316 |
|
} // namespace theory |
317 |
|
} // namespace cvc5 |
318 |
|
|
319 |
|
#endif /* CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H */ |