1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Mathias Preiner |
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 |
|
* Utility for single invocation partitioning. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__THEORY__QUANTIFIERS__SINGLE_INV_PARTITION_H |
19 |
|
#define CVC5__THEORY__QUANTIFIERS__SINGLE_INV_PARTITION_H |
20 |
|
|
21 |
|
#include <map> |
22 |
|
#include <vector> |
23 |
|
|
24 |
|
#include "expr/node.h" |
25 |
|
#include "expr/type_node.h" |
26 |
|
|
27 |
|
namespace cvc5 { |
28 |
|
namespace theory { |
29 |
|
namespace quantifiers { |
30 |
|
|
31 |
|
/** single invocation partition |
32 |
|
* |
33 |
|
* This is a utility to group formulas into single invocation/non-single |
34 |
|
* invocation parts, relative to a set of "input functions". |
35 |
|
* It can be used when either the set of input functions is fixed, |
36 |
|
* or is unknown. |
37 |
|
* |
38 |
|
* (EX1) For example, if input functions are { f }, |
39 |
|
* then the formula is ( f( x, y ) = g( y ) V f( x, y ) = b ) |
40 |
|
* is single invocation since there is only one |
41 |
|
* unique application of f, that is, f( x, y ). |
42 |
|
* Notice that |
43 |
|
* exists f. forall xy. f( x, y ) = g( y ) V f( x, y ) = b |
44 |
|
* is equivalent to: |
45 |
|
* forall xy. exists z. z = g( y ) V z = b |
46 |
|
* |
47 |
|
* When handling multiple input functions, we only infer a formula |
48 |
|
* is single invocation if all (relevant) input functions have the |
49 |
|
* same argument types. An input function is relevant if it is |
50 |
|
* specified by the input in a call to init() or occurs in the |
51 |
|
* formula we are processing. |
52 |
|
* |
53 |
|
* Notice that this class may introduce auxiliary variables to |
54 |
|
* coerce a formula into being single invocation. For example, |
55 |
|
* see Example 5 of Reynolds et al. SYNT 2017. |
56 |
|
* |
57 |
|
*/ |
58 |
|
class SingleInvocationPartition |
59 |
|
{ |
60 |
|
public: |
61 |
1158 |
SingleInvocationPartition() : d_has_input_funcs(false) {} |
62 |
1158 |
~SingleInvocationPartition() {} |
63 |
|
/** initialize this partition for formula n, with input functions funcs |
64 |
|
* |
65 |
|
* This initializes this class to check whether formula n is single |
66 |
|
* invocation with respect to the input functions in funcs only. |
67 |
|
* Below, the "processed formula" is a formula generated by this |
68 |
|
* call that is equivalent to n (if this call is successful). |
69 |
|
* |
70 |
|
* This method returns true if all input functions have identical |
71 |
|
* argument types, and false otherwise. Notice that all |
72 |
|
* access functions below are only valid if this class is |
73 |
|
* successfully initialized. |
74 |
|
*/ |
75 |
|
bool init(std::vector<Node>& funcs, Node n); |
76 |
|
|
77 |
|
/** initialize this partition for formula n |
78 |
|
* |
79 |
|
* In contrast to the above method, this version assumes that |
80 |
|
* all uninterpreted functions are input functions. That is, this |
81 |
|
* method is equivalent to the above function with funcs containing |
82 |
|
* all uninterpreted functions occurring in n. |
83 |
|
*/ |
84 |
|
bool init(Node n); |
85 |
|
|
86 |
|
/** is the processed formula purely single invocation? |
87 |
|
* |
88 |
|
* A formula is purely single invocation if it is equivalent to: |
89 |
|
* t[ f1( x ), ..., fn( x ), x ], |
90 |
|
* for some F, where f1...fn are the input functions. |
91 |
|
* Notice that the free variables of t are exactly x. |
92 |
|
*/ |
93 |
319 |
bool isPurelySingleInvocation() { return d_conjuncts[1].empty(); } |
94 |
|
/** is the processed formula non-ground single invocation? |
95 |
|
* |
96 |
|
* A formula is non-ground single invocation if it is equivalent to: |
97 |
|
* F[ f1( x ), ..., fn( x ), x, y ], |
98 |
|
* for some F, where f1...fn are the input functions. |
99 |
|
*/ |
100 |
3 |
bool isNonGroundSingleInvocation() |
101 |
|
{ |
102 |
3 |
return d_conjuncts[3].size() == d_conjuncts[1].size(); |
103 |
|
} |
104 |
|
|
105 |
|
/** Get the (portion of) the processed formula that is single invocation |
106 |
|
* |
107 |
|
* Notice this method returns the anti-skolemized version of the input |
108 |
|
* formula. In (EX1), this method returns: |
109 |
|
* z = g( y ) V z = b |
110 |
|
* where z is the first-order variable for f (see |
111 |
|
* getFirstOrderVariableForFunction). |
112 |
|
*/ |
113 |
96 |
Node getSingleInvocation() { return getConjunct(0); } |
114 |
|
/** Get the (portion of) the processed formula that is not single invocation |
115 |
|
* |
116 |
|
* This formula and the above form a partition of the conjuncts of the |
117 |
|
* processed formula, that is: |
118 |
|
* getSingleInvocation() * sigma ^ getNonSingleInvocation() |
119 |
|
* is equivalent to the processed formula, where sigma is a substitution of |
120 |
|
* the form: |
121 |
|
* z_1 -> f_1( x ) .... z_n -> f_n( x ) |
122 |
|
* where z_i are the first-order variables for input functions f_i |
123 |
|
* for all i=1,...,n, and x are the single invocation arguments of the input |
124 |
|
* formulas (see d_si_vars). |
125 |
|
*/ |
126 |
|
Node getNonSingleInvocation() { return getConjunct(1); } |
127 |
|
/** get full specification |
128 |
|
* |
129 |
|
* This returns getSingleInvocation() * sigma ^ getNonSingleInvocation(), |
130 |
|
* which is equivalent to the processed formula, where sigma is the |
131 |
|
* substitution described above. |
132 |
|
*/ |
133 |
3 |
Node getFullSpecification() { return getConjunct(2); } |
134 |
|
/** get first order variable for input function f |
135 |
|
* |
136 |
|
* This corresponds to the variable that we used when anti-skolemizing |
137 |
|
* function f. For example, in (EX1), if getSingleInvocation() returns: |
138 |
|
* z = g( y ) V z = b |
139 |
|
* Then, getFirstOrderVariableForFunction(f) = z. |
140 |
|
*/ |
141 |
|
Node getFirstOrderVariableForFunction(Node f) const; |
142 |
|
|
143 |
|
/** get function for first order variable |
144 |
|
* |
145 |
|
* Opposite direction of above, where: |
146 |
|
* getFunctionForFirstOrderVariable(z) = f. |
147 |
|
*/ |
148 |
|
Node getFunctionForFirstOrderVariable(Node v) const; |
149 |
|
|
150 |
|
/** get function invocation for |
151 |
|
* |
152 |
|
* Returns f( x ) where x are the single invocation arguments of the input |
153 |
|
* formulas (see d_si_vars). If f is not an input function, it returns null. |
154 |
|
*/ |
155 |
|
Node getFunctionInvocationFor(Node f) const; |
156 |
|
|
157 |
|
/** get single invocation variables, appends them to sivars */ |
158 |
|
void getSingleInvocationVariables(std::vector<Node>& sivars) const; |
159 |
|
|
160 |
|
/** get all variables |
161 |
|
* |
162 |
|
* Appends all free variables of the processed formula to vars. |
163 |
|
*/ |
164 |
|
void getAllVariables(std::vector<Node>& vars) const; |
165 |
|
|
166 |
|
/** get function variables |
167 |
|
* |
168 |
|
* Appends all first-order variables corresponding to input functions to |
169 |
|
* fvars. |
170 |
|
*/ |
171 |
|
void getFunctionVariables(std::vector<Node>& fvars) const; |
172 |
|
|
173 |
|
/** get functions |
174 |
|
* |
175 |
|
* Gets all input functions. This has the same order as the list of |
176 |
|
* function variables above. |
177 |
|
*/ |
178 |
|
void getFunctions(std::vector<Node>& fs) const; |
179 |
|
|
180 |
|
/** print debugging information on Trace c */ |
181 |
|
void debugPrint(const char* c); |
182 |
|
|
183 |
|
private: |
184 |
|
/** map from input functions to whether they have an anti-skolemizable type |
185 |
|
* An anti-skolemizable type is one of the form: |
186 |
|
* ( T1, ..., Tn ) -> T |
187 |
|
* where Ti = d_arg_types[i] for i = 1,...,n. |
188 |
|
*/ |
189 |
|
std::map<Node, bool> d_funcs; |
190 |
|
|
191 |
|
/** map from functions to the invocation we inferred for them */ |
192 |
|
std::map<Node, Node> d_func_inv; |
193 |
|
|
194 |
|
/** the list of first-order variables for functions |
195 |
|
* In (EX1), this is the list { z }. |
196 |
|
*/ |
197 |
|
std::vector<Node> d_func_vars; |
198 |
|
|
199 |
|
/** the arguments that we based the anti-skolemization on. |
200 |
|
* In (EX1), this is the list { x, y }. |
201 |
|
*/ |
202 |
|
std::vector<Node> d_si_vars; |
203 |
|
|
204 |
|
/** every free variable of conjuncts[2] */ |
205 |
|
std::unordered_set<Node> d_all_vars; |
206 |
|
/** map from functions to first-order variables that anti-skolemized them */ |
207 |
|
std::map<Node, Node> d_func_fo_var; |
208 |
|
/** map from first-order variables to the function it anti-skolemized */ |
209 |
|
std::map<Node, Node> d_fo_var_to_func; |
210 |
|
|
211 |
|
/** The argument types for this single invocation partition. |
212 |
|
* These are the argument types of the input functions we are |
213 |
|
* processing, where notice that: |
214 |
|
* d_si_vars[i].getType()==d_arg_types[i] |
215 |
|
*/ |
216 |
|
std::vector<TypeNode> d_arg_types; |
217 |
|
|
218 |
|
/** the list of conjuncts with various properties : |
219 |
|
* 0 : the single invocation conjuncts (stored in anti-skolemized form), |
220 |
|
* 1 : the non-single invocation conjuncts, |
221 |
|
* 2 : all conjuncts, |
222 |
|
* 3 : non-ground single invocation conjuncts. |
223 |
|
*/ |
224 |
|
std::vector<Node> d_conjuncts[4]; |
225 |
|
|
226 |
|
/** did we initialize this class with input functions? */ |
227 |
|
bool d_has_input_funcs; |
228 |
|
/** the input functions we initialized this class with */ |
229 |
|
std::vector<Node> d_input_funcs; |
230 |
|
/** all input functions */ |
231 |
|
std::vector<Node> d_all_funcs; |
232 |
|
/** skolem of the same type as input functions */ |
233 |
|
std::vector<Node> d_input_func_sks; |
234 |
|
|
235 |
|
/** infer the argument types of uninterpreted function applications |
236 |
|
* |
237 |
|
* If this method returns true, then typs contains the list of types of |
238 |
|
* the arguments (in order) of all uninterpreted functions in n. |
239 |
|
* If this method returns false, then there exists (at least) two |
240 |
|
* uninterpreted functions in n whose argument types are not identical. |
241 |
|
*/ |
242 |
|
bool inferArgTypes(Node n, |
243 |
|
std::vector<TypeNode>& typs, |
244 |
|
std::map<Node, bool>& visited); |
245 |
|
|
246 |
|
/** is anti-skolemizable type |
247 |
|
* |
248 |
|
* This method returns true if f's argument types are equal to the |
249 |
|
* argument types we have fixed in this class (see d_arg_types). |
250 |
|
*/ |
251 |
|
bool isAntiSkolemizableType(Node f); |
252 |
|
|
253 |
|
/** |
254 |
|
* This is the entry point for initializing this class, |
255 |
|
* which is called by the public init(...) methods. |
256 |
|
* funcs are the input functions (if any were explicitly provide), |
257 |
|
* typs is the types of the arguments of funcs, |
258 |
|
* n is the formula to process, |
259 |
|
* has_funcs is whether input functions were explicitly provided. |
260 |
|
*/ |
261 |
|
bool init(std::vector<Node>& funcs, |
262 |
|
std::vector<TypeNode>& typs, |
263 |
|
Node n, |
264 |
|
bool has_funcs); |
265 |
|
|
266 |
|
/** |
267 |
|
* Collect the top-level conjuncts of the formula (equivalent to) |
268 |
|
* n or the negation of n if pol=false, and store them in conj. |
269 |
|
*/ |
270 |
|
bool collectConjuncts(Node n, bool pol, std::vector<Node>& conj); |
271 |
|
|
272 |
|
/** process conjunct n |
273 |
|
* |
274 |
|
* This function is called when n is a top-level conjunction in a |
275 |
|
* formula that is equivalent to the input formula given to this |
276 |
|
* class via init. |
277 |
|
* |
278 |
|
* args : stores the arguments (if any) that we have seen in an application |
279 |
|
* of an application of an input function in this conjunct. |
280 |
|
* terms/subs : this stores a term substitution with entries of the form: |
281 |
|
* f(args) -> z |
282 |
|
* where z is the first-order variable for input function f. |
283 |
|
*/ |
284 |
|
bool processConjunct(Node n, |
285 |
|
std::map<Node, bool>& visited, |
286 |
|
std::vector<Node>& args, |
287 |
|
std::vector<Node>& terms, |
288 |
|
std::vector<Node>& subs); |
289 |
|
|
290 |
|
/** get the and node corresponding to d_conjuncts[index] */ |
291 |
|
Node getConjunct(int index); |
292 |
|
}; |
293 |
|
|
294 |
|
} // namespace quantifiers |
295 |
|
} // namespace theory |
296 |
|
} // namespace cvc5 |
297 |
|
|
298 |
|
#endif /* CVC5__THEORY__QUANTIFIERS__SINGLE_INV_PARTITION_H */ |