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 |
|
namespace theory { |
26 |
|
namespace quantifiers { |
27 |
|
|
28 |
|
struct QAttributes; |
29 |
|
|
30 |
|
/** |
31 |
|
* List of steps used by the quantifiers rewriter, details on these steps |
32 |
|
* can be found in the class below. |
33 |
|
*/ |
34 |
|
enum RewriteStep |
35 |
|
{ |
36 |
|
/** Eliminate symbols (e.g. implies, xor) */ |
37 |
|
COMPUTE_ELIM_SYMBOLS = 0, |
38 |
|
/** Miniscoping */ |
39 |
|
COMPUTE_MINISCOPING, |
40 |
|
/** Aggressive miniscoping */ |
41 |
|
COMPUTE_AGGRESSIVE_MINISCOPING, |
42 |
|
/** Apply the extended rewriter to quantified formula bodies */ |
43 |
|
COMPUTE_EXT_REWRITE, |
44 |
|
/** |
45 |
|
* Term processing (e.g. simplifying terms based on ITE lifting, |
46 |
|
* eliminating extended arithmetic symbols). |
47 |
|
*/ |
48 |
|
COMPUTE_PROCESS_TERMS, |
49 |
|
/** Prenexing */ |
50 |
|
COMPUTE_PRENEX, |
51 |
|
/** Variable elimination */ |
52 |
|
COMPUTE_VAR_ELIMINATION, |
53 |
|
/** Conditional splitting */ |
54 |
|
COMPUTE_COND_SPLIT, |
55 |
|
/** Placeholder for end of steps */ |
56 |
|
COMPUTE_LAST |
57 |
|
}; |
58 |
|
std::ostream& operator<<(std::ostream& out, RewriteStep s); |
59 |
|
|
60 |
19682 |
class QuantifiersRewriter : public TheoryRewriter |
61 |
|
{ |
62 |
|
public: |
63 |
|
static bool isLiteral( Node n ); |
64 |
|
//-------------------------------------variable elimination utilities |
65 |
|
/** is variable elimination |
66 |
|
* |
67 |
|
* Returns true if v is not a subterm of s, and the type of s is a subtype of |
68 |
|
* the type of v. |
69 |
|
*/ |
70 |
|
static bool isVarElim(Node v, Node s); |
71 |
|
/** get variable elimination literal |
72 |
|
* |
73 |
|
* If n asserted with polarity pol is equivalent to an equality of the form |
74 |
|
* v = s for some v in args, where isVariableElim( v, s ) holds, then this |
75 |
|
* method removes v from args, adds v to vars, adds s to subs, and returns |
76 |
|
* true. Otherwise, it returns false. |
77 |
|
*/ |
78 |
|
static bool getVarElimLit(Node n, |
79 |
|
bool pol, |
80 |
|
std::vector<Node>& args, |
81 |
|
std::vector<Node>& vars, |
82 |
|
std::vector<Node>& subs); |
83 |
|
/** variable eliminate for bit-vector equalities |
84 |
|
* |
85 |
|
* If this returns a non-null value ret, then var is updated to a member of |
86 |
|
* args, lit is equivalent to ( var = ret ). |
87 |
|
*/ |
88 |
|
static Node getVarElimLitBv(Node lit, |
89 |
|
const std::vector<Node>& args, |
90 |
|
Node& var); |
91 |
|
/** variable eliminate for string equalities |
92 |
|
* |
93 |
|
* If this returns a non-null value ret, then var is updated to a member of |
94 |
|
* args, lit is equivalent to ( var = ret ). |
95 |
|
*/ |
96 |
|
static Node getVarElimLitString(Node lit, |
97 |
|
const std::vector<Node>& args, |
98 |
|
Node& var); |
99 |
|
/** get variable elimination |
100 |
|
* |
101 |
|
* If n asserted with polarity pol entails a literal lit that corresponds |
102 |
|
* to a variable elimination for some v via the above method, we return true. |
103 |
|
* In this case, we update args/vars/subs based on eliminating v. |
104 |
|
*/ |
105 |
|
static bool getVarElim(Node n, |
106 |
|
bool pol, |
107 |
|
std::vector<Node>& args, |
108 |
|
std::vector<Node>& vars, |
109 |
|
std::vector<Node>& subs); |
110 |
|
/** has variable elimination |
111 |
|
* |
112 |
|
* Returns true if n asserted with polarity pol entails a literal for |
113 |
|
* which variable elimination is possible. |
114 |
|
*/ |
115 |
|
static bool hasVarElim(Node n, bool pol, std::vector<Node>& args); |
116 |
|
/** compute variable elimination inequality |
117 |
|
* |
118 |
|
* This method eliminates variables from the body of quantified formula |
119 |
|
* "body" using (global) reasoning about inequalities. In particular, if there |
120 |
|
* exists a variable x that only occurs in body or annotation qa in literals |
121 |
|
* of the form x>=t with a fixed polarity P, then we may replace all such |
122 |
|
* literals with P. For example, note that: |
123 |
|
* forall xy. x>y OR P(y) is equivalent to forall y. P(y). |
124 |
|
* |
125 |
|
* In the case that a variable x from args can be eliminated in this way, |
126 |
|
* we remove x from args, add x >= t1, ..., x >= tn to bounds, add false, ..., |
127 |
|
* false to subs, and return true. |
128 |
|
*/ |
129 |
|
static bool getVarElimIneq(Node body, |
130 |
|
std::vector<Node>& args, |
131 |
|
std::vector<Node>& bounds, |
132 |
|
std::vector<Node>& subs, |
133 |
|
QAttributes& qa); |
134 |
|
//-------------------------------------end variable elimination utilities |
135 |
|
private: |
136 |
|
static int getPurifyIdLit2(Node n, std::map<Node, int>& visited); |
137 |
|
static bool addCheckElimChild(std::vector<Node>& children, |
138 |
|
Node c, |
139 |
|
Kind k, |
140 |
|
std::map<Node, bool>& lit_pol, |
141 |
|
bool& childrenChanged); |
142 |
|
static void addNodeToOrBuilder(Node n, NodeBuilder& t); |
143 |
|
static void computeArgs(const std::vector<Node>& args, |
144 |
|
std::map<Node, bool>& activeMap, |
145 |
|
Node n, |
146 |
|
std::map<Node, bool>& visited); |
147 |
|
static void computeArgVec(const std::vector<Node>& args, |
148 |
|
std::vector<Node>& activeArgs, |
149 |
|
Node n); |
150 |
|
static void computeArgVec2(const std::vector<Node>& args, |
151 |
|
std::vector<Node>& activeArgs, |
152 |
|
Node n, |
153 |
|
Node ipl); |
154 |
|
static Node computeProcessTerms2(Node body, |
155 |
|
std::map<Node, Node>& cache, |
156 |
|
std::vector<Node>& new_vars, |
157 |
|
std::vector<Node>& new_conds); |
158 |
|
static void computeDtTesterIteSplit( |
159 |
|
Node n, |
160 |
|
std::map<Node, Node>& pcons, |
161 |
|
std::map<Node, std::map<int, Node> >& ncons, |
162 |
|
std::vector<Node>& conj); |
163 |
|
|
164 |
|
//-------------------------------------variable elimination |
165 |
|
/** compute variable elimination |
166 |
|
* |
167 |
|
* This computes variable elimination rewrites for a body of a quantified |
168 |
|
* formula with bound variables args. This method updates args to args' and |
169 |
|
* returns a node body' such that (forall args. body) is equivalent to |
170 |
|
* (forall args'. body'). An example of a variable elimination rewrite is: |
171 |
|
* forall xy. x != a V P( x,y ) ---> forall y. P( a, y ) |
172 |
|
*/ |
173 |
|
static Node computeVarElimination(Node body, |
174 |
|
std::vector<Node>& args, |
175 |
|
QAttributes& qa); |
176 |
|
//-------------------------------------end variable elimination |
177 |
|
//-------------------------------------conditional splitting |
178 |
|
/** compute conditional splitting |
179 |
|
* |
180 |
|
* This computes conditional splitting rewrites for a body of a quantified |
181 |
|
* formula with bound variables args. It returns a body' that is equivalent |
182 |
|
* to body. We split body into a conjunction if variable elimination can |
183 |
|
* occur in one of the conjuncts. Examples of this are: |
184 |
|
* ite( x=a, P(x), Q(x) ) ----> ( x!=a V P(x) ) ^ ( x=a V Q(x) ) |
185 |
|
* (x=a) = P(x) ----> ( x!=a V P(x) ) ^ ( x=a V ~P(x) ) |
186 |
|
* ( x!=a ^ P(x) ) V Q(x) ---> ( x!=a V Q(x) ) ^ ( P(x) V Q(x) ) |
187 |
|
* where in each case, x can be eliminated in the first conjunct. |
188 |
|
*/ |
189 |
|
static Node computeCondSplit(Node body, |
190 |
|
const std::vector<Node>& args, |
191 |
|
QAttributes& qa); |
192 |
|
//-------------------------------------end conditional splitting |
193 |
|
//------------------------------------- process terms |
194 |
|
/** compute process terms |
195 |
|
* |
196 |
|
* This takes as input a quantified formula q with attributes qa whose |
197 |
|
* body is body. |
198 |
|
* |
199 |
|
* This rewrite eliminates problematic terms from the bodies of |
200 |
|
* quantified formulas, which includes performing: |
201 |
|
* - Certain cases of ITE lifting, |
202 |
|
* - Elimination of extended arithmetic functions like to_int/is_int/div/mod, |
203 |
|
* - Elimination of select over store. |
204 |
|
* |
205 |
|
* It may introduce new variables V into new_vars and new conditions C into |
206 |
|
* new_conds. It returns a node retBody such that q of the form |
207 |
|
* forall X. body |
208 |
|
* is equivalent to: |
209 |
|
* forall X, V. ( C => retBody ) |
210 |
|
*/ |
211 |
|
static Node computeProcessTerms(Node body, |
212 |
|
std::vector<Node>& new_vars, |
213 |
|
std::vector<Node>& new_conds, |
214 |
|
Node q, |
215 |
|
QAttributes& qa); |
216 |
|
//------------------------------------- end process terms |
217 |
|
//------------------------------------- extended rewrite |
218 |
|
/** compute extended rewrite |
219 |
|
* |
220 |
|
* This returns the result of applying the extended rewriter on the body |
221 |
|
* of quantified formula q. |
222 |
|
*/ |
223 |
|
static Node computeExtendedRewrite(Node q); |
224 |
|
//------------------------------------- end extended rewrite |
225 |
|
public: |
226 |
|
static Node computeElimSymbols( Node body ); |
227 |
|
/** |
228 |
|
* Compute miniscoping in quantified formula q with attributes in qa. |
229 |
|
*/ |
230 |
|
static Node computeMiniscoping(Node q, QAttributes& qa); |
231 |
|
static Node computeAggressiveMiniscoping( std::vector< Node >& args, Node body ); |
232 |
|
/** |
233 |
|
* This function removes top-level quantifiers from subformulas of body |
234 |
|
* appearing with overall polarity pol. It adds quantified variables that |
235 |
|
* appear in positive polarity positions into args, and those at negative |
236 |
|
* polarity positions in nargs. |
237 |
|
* |
238 |
|
* If prenexAgg is true, we ensure that all top-level quantifiers are |
239 |
|
* eliminated from subformulas. This means that we must expand ITE and |
240 |
|
* Boolean equalities to ensure that quantifiers are at fixed polarities. |
241 |
|
* |
242 |
|
* For example, calling this function on: |
243 |
|
* (or (forall ((x Int)) (P x z)) (not (forall ((y Int)) (Q y z)))) |
244 |
|
* would return: |
245 |
|
* (or (P x z) (not (Q y z))) |
246 |
|
* and add {x} to args, and {y} to nargs. |
247 |
|
*/ |
248 |
|
static Node computePrenex(Node q, |
249 |
|
Node body, |
250 |
|
std::unordered_set<Node>& args, |
251 |
|
std::unordered_set<Node>& nargs, |
252 |
|
bool pol, |
253 |
|
bool prenexAgg); |
254 |
|
/** |
255 |
|
* Apply prenexing aggressively. Returns the prenex normal form of n. |
256 |
|
*/ |
257 |
|
static Node computePrenexAgg(Node n, std::map<Node, Node>& visited); |
258 |
|
static Node computeSplit( std::vector< Node >& args, Node body, QAttributes& qa ); |
259 |
|
private: |
260 |
|
static Node computeOperation(Node f, |
261 |
|
RewriteStep computeOption, |
262 |
|
QAttributes& qa); |
263 |
|
|
264 |
|
public: |
265 |
|
RewriteResponse preRewrite(TNode in) override; |
266 |
|
RewriteResponse postRewrite(TNode in) override; |
267 |
|
|
268 |
|
private: |
269 |
|
/** options */ |
270 |
|
static bool doOperation(Node f, RewriteStep computeOption, QAttributes& qa); |
271 |
|
|
272 |
|
private: |
273 |
|
static Node preSkolemizeQuantifiers(Node n, bool polarity, std::vector< TypeNode >& fvTypes, std::vector<TNode>& fvs); |
274 |
|
public: |
275 |
|
static bool isPrenexNormalForm( Node n ); |
276 |
|
/** preprocess |
277 |
|
* |
278 |
|
* This returns the result of applying simple quantifiers-specific |
279 |
|
* preprocessing to n, including but not limited to: |
280 |
|
* - rewrite rule elimination, |
281 |
|
* - pre-skolemization, |
282 |
|
* - aggressive prenexing. |
283 |
|
* The argument isInst is set to true if n is an instance of a previously |
284 |
|
* registered quantified formula. If this flag is true, we do not apply |
285 |
|
* certain steps like pre-skolemization since we know they will have no |
286 |
|
* effect. |
287 |
|
* |
288 |
|
* The result is wrapped in a trust node of kind TrustNodeKind::REWRITE. |
289 |
|
*/ |
290 |
|
static TrustNode preprocess(Node n, bool isInst = false); |
291 |
|
static Node mkForAll(const std::vector<Node>& args, |
292 |
|
Node body, |
293 |
|
QAttributes& qa); |
294 |
|
static Node mkForall(const std::vector<Node>& args, |
295 |
|
Node body, |
296 |
|
bool marked = false); |
297 |
|
static Node mkForall(const std::vector<Node>& args, |
298 |
|
Node body, |
299 |
|
std::vector<Node>& iplc, |
300 |
|
bool marked = false); |
301 |
|
}; /* class QuantifiersRewriter */ |
302 |
|
|
303 |
|
} // namespace quantifiers |
304 |
|
} // namespace theory |
305 |
|
} // namespace cvc5 |
306 |
|
|
307 |
|
#endif /* CVC5__THEORY__QUANTIFIERS__QUANTIFIERS_REWRITER_H */ |