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