1 |
|
/****************************************************************************** |
2 |
|
* Top contributors (to current version): |
3 |
|
* Andrew Reynolds, Morgan Deters, Andres Noetzli |
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 |
|
* Skolem manager utility. |
14 |
|
*/ |
15 |
|
|
16 |
|
#include "cvc5_private.h" |
17 |
|
|
18 |
|
#ifndef CVC5__EXPR__SKOLEM_MANAGER_H |
19 |
|
#define CVC5__EXPR__SKOLEM_MANAGER_H |
20 |
|
|
21 |
|
#include <string> |
22 |
|
|
23 |
|
#include "expr/node.h" |
24 |
|
|
25 |
|
namespace cvc5 { |
26 |
|
|
27 |
|
class ProofGenerator; |
28 |
|
|
29 |
|
/** Skolem function identifier */ |
30 |
|
enum class SkolemFunId |
31 |
|
{ |
32 |
|
NONE, |
33 |
|
/** an uninterpreted function f s.t. f(x) = x / 0.0 (real division) */ |
34 |
|
DIV_BY_ZERO, |
35 |
|
/** an uninterpreted function f s.t. f(x) = x / 0 (integer division) */ |
36 |
|
INT_DIV_BY_ZERO, |
37 |
|
/** an uninterpreted function f s.t. f(x) = x mod 0 */ |
38 |
|
MOD_BY_ZERO, |
39 |
|
/** an uninterpreted function f s.t. f(x) = sqrt(x) */ |
40 |
|
SQRT, |
41 |
|
/** a wrongly applied selector */ |
42 |
|
SELECTOR_WRONG, |
43 |
|
/** a shared selector */ |
44 |
|
SHARED_SELECTOR, |
45 |
|
/** an application of seq.nth that is out of bounds */ |
46 |
|
SEQ_NTH_OOB, |
47 |
|
/** |
48 |
|
* Regular expression unfold component: if (str.in_re t R), where R is |
49 |
|
* (re.++ r0 ... rn), then the RE_UNFOLD_POS_COMPONENT{t,R,i} is a string |
50 |
|
* skolem ki such that t = (str.++ k0 ... kn) and (str.in_re k0 r0) for |
51 |
|
* i = 0, ..., n. |
52 |
|
*/ |
53 |
|
RE_UNFOLD_POS_COMPONENT, |
54 |
|
/** Higher-order type match predicate, see HoTermDb */ |
55 |
|
HO_TYPE_MATCH_PRED, |
56 |
|
}; |
57 |
|
/** Converts a skolem function name to a string. */ |
58 |
|
const char* toString(SkolemFunId id); |
59 |
|
/** Writes a skolem function name to a stream. */ |
60 |
|
std::ostream& operator<<(std::ostream& out, SkolemFunId id); |
61 |
|
|
62 |
|
/** |
63 |
|
* A manager for skolems that can be used in proofs. This is designed to be |
64 |
|
* a trusted interface to NodeManager::mkSkolem, where one |
65 |
|
* must provide a definition for the skolem they create in terms of a |
66 |
|
* predicate that the introduced variable is intended to witness. |
67 |
|
* |
68 |
|
* It is implemented by mapping terms to an attribute corresponding to their |
69 |
|
* "original form" and "witness form" as described below. Hence, this class does |
70 |
|
* not impact the reference counting of skolem variables which may be deleted if |
71 |
|
* they are not used. |
72 |
|
* |
73 |
|
* We distinguish two kinds of mappings between terms and skolems: |
74 |
|
* |
75 |
|
* (1) "Original form", which associates skolems with the terms they purify. |
76 |
|
* This is used in mkPurifySkolem below. |
77 |
|
* |
78 |
|
* (2) "Witness form", which associates skolems with their formal definition |
79 |
|
* as a witness term. This is used in mkSkolem below. |
80 |
|
* |
81 |
|
* It is possible to unify these so that purification skolems for t are skolems |
82 |
|
* whose witness form is (witness ((x T)) (= x t)). However, there are |
83 |
|
* motivations not to do so. In particular, witness terms in most contexts |
84 |
|
* should be seen as black boxes, converting something to witness form may have |
85 |
|
* unintended consequences e.g. variable shadowing. In contrast, converting to |
86 |
|
* original form does not have these complications. Furthermore, having original |
87 |
|
* form greatly simplifies reasoning in the proof, in particular, it avoids the |
88 |
|
* need to reason about identifiers for introduced variables x. |
89 |
|
* |
90 |
|
* Furthermore, note that original form and witness form may share skolems |
91 |
|
* in the rare case that a witness term is purified. This is currently only the |
92 |
|
* case for algorithms that introduce witness, e.g. BV/set instantiation. |
93 |
|
* |
94 |
|
* Additionally, we consider a third class of skolems (mkSkolemFunction) which |
95 |
|
* are for convenience associated with an identifier, and not a witness term. |
96 |
|
*/ |
97 |
|
class SkolemManager |
98 |
|
{ |
99 |
|
public: |
100 |
10645 |
SkolemManager() {} |
101 |
8004 |
~SkolemManager() {} |
102 |
|
/** |
103 |
|
* This makes a skolem of same type as bound variable v, (say its type is T), |
104 |
|
* whose definition is (witness ((v T)) pred). This definition is maintained |
105 |
|
* by this class. |
106 |
|
* |
107 |
|
* Notice that (exists ((v T)) pred) should be a valid formula. This fact |
108 |
|
* captures the reason for why the returned Skolem was introduced. |
109 |
|
* |
110 |
|
* Take as an example extensionality in arrays: |
111 |
|
* |
112 |
|
* (declare-fun a () (Array Int Int)) |
113 |
|
* (declare-fun b () (Array Int Int)) |
114 |
|
* (assert (not (= a b))) |
115 |
|
* |
116 |
|
* To witness the index where the arrays a and b are disequal, it is intended |
117 |
|
* we call this method on: |
118 |
|
* Node k = mkSkolem( x, F ) |
119 |
|
* where F is: |
120 |
|
* (=> (not (= a b)) (not (= (select a x) (select b x)))) |
121 |
|
* and x is a fresh bound variable of integer type. Internally, this will map |
122 |
|
* k to the term: |
123 |
|
* (witness ((x Int)) (=> (not (= a b)) |
124 |
|
* (not (= (select a x) (select b x))))) |
125 |
|
* A lemma generated by the array solver for extensionality may safely use |
126 |
|
* the skolem k in the standard way: |
127 |
|
* (=> (not (= a b)) (not (= (select a k) (select b k)))) |
128 |
|
* Furthermore, notice that the following lemma does not involve fresh |
129 |
|
* skolem variables and is valid according to the theory of arrays extended |
130 |
|
* with support for witness: |
131 |
|
* (let ((w (witness ((x Int)) (=> (not (= a b)) |
132 |
|
* (not (= (select a x) (select b x))))))) |
133 |
|
* (=> (not (= a b)) (not (= (select a w) (select b w))))) |
134 |
|
* This version of the lemma, which requires no explicit tracking of free |
135 |
|
* Skolem variables, can be obtained by a call to getWitnessForm(...) |
136 |
|
* below. We call this the "witness form" of the lemma above. |
137 |
|
* |
138 |
|
* @param v The bound variable of the same type of the Skolem to create. |
139 |
|
* @param pred The desired property of the Skolem to create, in terms of bound |
140 |
|
* variable v. |
141 |
|
* @param prefix The prefix of the name of the Skolem |
142 |
|
* @param comment Debug information about the Skolem |
143 |
|
* @param flags The flags for the Skolem (see NodeManager::mkSkolem) |
144 |
|
* @param pg The proof generator for this skolem. If non-null, this proof |
145 |
|
* generator must respond to a call to getProofFor(exists v. pred) during |
146 |
|
* the lifetime of the current node manager. |
147 |
|
* @return The skolem whose witness form is registered by this class. |
148 |
|
*/ |
149 |
|
Node mkSkolem(Node v, |
150 |
|
Node pred, |
151 |
|
const std::string& prefix, |
152 |
|
const std::string& comment = "", |
153 |
|
int flags = NodeManager::SKOLEM_DEFAULT, |
154 |
|
ProofGenerator* pg = nullptr); |
155 |
|
/** |
156 |
|
* Make skolemized form of existentially quantified formula q, and store its |
157 |
|
* Skolems into the argument skolems. |
158 |
|
* |
159 |
|
* For example, calling this method on: |
160 |
|
* (exists ((x Int) (y Int)) (P x y)) |
161 |
|
* returns: |
162 |
|
* (P w1 w2) |
163 |
|
* where w1 and w2 are skolems with witness forms: |
164 |
|
* (witness ((x Int)) (exists ((y Int)) (P x y))) |
165 |
|
* (witness ((y Int)) (P w1 y)) |
166 |
|
* respectively. Additionally, this method will add { w1, w2 } to skolems. |
167 |
|
* Notice that y is *not* renamed in the witness form of w1. This is not |
168 |
|
* necessary since w1 is skolem. Although its witness form contains |
169 |
|
* quantification on y, we never construct a term where the witness form |
170 |
|
* of w1 is expanded in the witness form of w2. This avoids variable |
171 |
|
* shadowing. |
172 |
|
* |
173 |
|
* In contrast to mkSkolem, the proof generator is for the *entire* |
174 |
|
* existentially quantified formula q, which may have multiple variables in |
175 |
|
* its prefix. |
176 |
|
* |
177 |
|
* @param q The existentially quantified formula to skolemize, |
178 |
|
* @param skolems Vector to add Skolems of q to, |
179 |
|
* @param prefix The prefix of the name of each of the Skolems |
180 |
|
* @param comment Debug information about each of the Skolems |
181 |
|
* @param flags The flags for the Skolem (see NodeManager::mkSkolem) |
182 |
|
* @param pg The proof generator for this skolem. If non-null, this proof |
183 |
|
* generator must respond to a call to getProofFor(q) during |
184 |
|
* the lifetime of the current node manager. |
185 |
|
* @return The skolemized form of q. |
186 |
|
*/ |
187 |
|
Node mkSkolemize(Node q, |
188 |
|
std::vector<Node>& skolems, |
189 |
|
const std::string& prefix, |
190 |
|
const std::string& comment = "", |
191 |
|
int flags = NodeManager::SKOLEM_DEFAULT, |
192 |
|
ProofGenerator* pg = nullptr); |
193 |
|
/** |
194 |
|
* Same as above, but for special case of (witness ((x T)) (= x t)) |
195 |
|
* where T is the type of t. This skolem is unique for each t, which we |
196 |
|
* implement via an attribute on t. This attribute is used to ensure to |
197 |
|
* associate a unique skolem for each t. |
198 |
|
* |
199 |
|
* Notice that a purification skolem is trivial to justify, and hence it |
200 |
|
* does not require a proof generator. |
201 |
|
* |
202 |
|
* Notice that in very rare cases, two different terms may have the |
203 |
|
* same purification skolem. For example, let k be the skolem introduced to |
204 |
|
* eliminate (ite A B C). Then, the pair of terms: |
205 |
|
* (ite (ite A B C) D E) and (ite k D E) |
206 |
|
* have the same purification skolem. In the implementation, this is a result |
207 |
|
* of the fact that the above terms have the same original form. It is sound |
208 |
|
* to use the same skolem to purify these two terms, since they are |
209 |
|
* definitionally equivalent. |
210 |
|
*/ |
211 |
|
Node mkPurifySkolem(Node t, |
212 |
|
const std::string& prefix, |
213 |
|
const std::string& comment = "", |
214 |
|
int flags = NodeManager::SKOLEM_DEFAULT); |
215 |
|
/** |
216 |
|
* Make skolem function. This method should be used for creating fixed |
217 |
|
* skolem functions of the forms described in SkolemFunId. The user of this |
218 |
|
* method is responsible for providing a proper type for the identifier that |
219 |
|
* matches the description of id. Skolem functions are useful for modelling |
220 |
|
* the behavior of partial functions, or for theory-specific inferences that |
221 |
|
* introduce fresh variables. |
222 |
|
* |
223 |
|
* A skolem function is not given a formal semantics in terms of a witness |
224 |
|
* term, nor is it a purification skolem, thus it does not fall into the two |
225 |
|
* categories of skolems above. This method is motivated by convenience, as |
226 |
|
* the user of this method does not require constructing canonical variables |
227 |
|
* for witness terms. |
228 |
|
* |
229 |
|
* The returned skolem is an ordinary skolem variable that can be used |
230 |
|
* e.g. in APPLY_UF terms when tn is a function type. |
231 |
|
* |
232 |
|
* Notice that we do not insist that tn is a function type. A user of this |
233 |
|
* method may construct a canonical (first-order) skolem using this method |
234 |
|
* as well. |
235 |
|
* |
236 |
|
* @param id The identifier of the skolem function |
237 |
|
* @param tn The type of the returned skolem function |
238 |
|
* @param cacheVal A cache value. The returned skolem function will be |
239 |
|
* unique to the pair (id, cacheVal). This value is required, for instance, |
240 |
|
* for skolem functions that are in fact families of skolem functions, |
241 |
|
* e.g. the wrongly applied case of selectors. |
242 |
|
* @return The skolem function. |
243 |
|
*/ |
244 |
|
Node mkSkolemFunction(SkolemFunId id, |
245 |
|
TypeNode tn, |
246 |
|
Node cacheVal = Node::null(), |
247 |
|
int flags = NodeManager::SKOLEM_DEFAULT); |
248 |
|
/** Same as above, with multiple cache values */ |
249 |
|
Node mkSkolemFunction(SkolemFunId id, |
250 |
|
TypeNode tn, |
251 |
|
const std::vector<Node>& cacheVals, |
252 |
|
int flags = NodeManager::SKOLEM_DEFAULT); |
253 |
|
/** |
254 |
|
* Is k a skolem function? Returns true if k was generated by the above call. |
255 |
|
* Updates the arguments to the values used when constructing it. |
256 |
|
*/ |
257 |
|
bool isSkolemFunction(Node k, SkolemFunId& id, Node& cacheVal) const; |
258 |
|
/** |
259 |
|
* Create a skolem constant with the given name, type, and comment. This |
260 |
|
* should only be used if the definition of the skolem does not matter. |
261 |
|
* The definition of a skolem matters e.g. when the skolem is used in a |
262 |
|
* proof. |
263 |
|
* |
264 |
|
* @param prefix the name of the new skolem variable is the prefix |
265 |
|
* appended with a unique ID. This way a family of skolem variables |
266 |
|
* can be made with unique identifiers, used in dump, tracing, and |
267 |
|
* debugging output. Use SKOLEM_EXACT_NAME flag if you don't want |
268 |
|
* a unique ID appended and use prefix as the name. |
269 |
|
* @param type the type of the skolem variable to create |
270 |
|
* @param comment a comment for dumping output; if declarations are |
271 |
|
* being dumped, this is included in a comment before the declaration |
272 |
|
* and can be quite useful for debugging |
273 |
|
* @param flags an optional mask of bits from SkolemFlags to control |
274 |
|
* mkSkolem() behavior |
275 |
|
*/ |
276 |
|
Node mkDummySkolem(const std::string& prefix, |
277 |
|
const TypeNode& type, |
278 |
|
const std::string& comment = "", |
279 |
|
int flags = NodeManager::SKOLEM_DEFAULT); |
280 |
|
/** |
281 |
|
* Get proof generator for existentially quantified formula q. This returns |
282 |
|
* the proof generator that was provided in a call to mkSkolem above. |
283 |
|
*/ |
284 |
|
ProofGenerator* getProofGenerator(Node q) const; |
285 |
|
/** |
286 |
|
* Convert to witness form, which gets the witness form of a skolem k. |
287 |
|
* Notice this method is *not* recursive, instead, it is a simple attribute |
288 |
|
* lookup. |
289 |
|
* |
290 |
|
* @param k The variable to convert to witness form described above |
291 |
|
* @return k in witness form. |
292 |
|
*/ |
293 |
|
static Node getWitnessForm(Node k); |
294 |
|
/** |
295 |
|
* Convert to original form, which recursively replaces all skolems terms in n |
296 |
|
* by the term they purify. |
297 |
|
* |
298 |
|
* @param n The term or formula to convert to original form described above |
299 |
|
* @return n in original form. |
300 |
|
*/ |
301 |
|
static Node getOriginalForm(Node n); |
302 |
|
|
303 |
|
private: |
304 |
|
/** Cache of skolem functions for mkSkolemFunction above. */ |
305 |
|
std::map<std::tuple<SkolemFunId, TypeNode, Node>, Node> d_skolemFuns; |
306 |
|
/** Backwards mapping of above */ |
307 |
|
std::map<Node, std::tuple<SkolemFunId, TypeNode, Node>> d_skolemFunMap; |
308 |
|
/** |
309 |
|
* Mapping from witness terms to proof generators. |
310 |
|
*/ |
311 |
|
std::map<Node, ProofGenerator*> d_gens; |
312 |
|
/** Get or make skolem attribute for term w, which may be a witness term */ |
313 |
|
static Node mkSkolemInternal(Node w, |
314 |
|
const std::string& prefix, |
315 |
|
const std::string& comment, |
316 |
|
int flags); |
317 |
|
/** |
318 |
|
* Skolemize the first variable of existentially quantified formula q. |
319 |
|
* For example, calling this method on: |
320 |
|
* (exists ((x Int) (y Int)) (P x y)) |
321 |
|
* will return: |
322 |
|
* (witness ((x Int)) (exists ((y Int)) (P x y))) |
323 |
|
* If q is not an existentially quantified formula, then null is |
324 |
|
* returned and an assertion failure is thrown. |
325 |
|
* |
326 |
|
* This method additionally updates qskolem to be the skolemized form of q. |
327 |
|
* In the above example, this is set to: |
328 |
|
* (exists ((y Int)) (P (witness ((x Int)) (exists ((y' Int)) (P x y'))) y)) |
329 |
|
*/ |
330 |
|
Node skolemize(Node q, |
331 |
|
Node& qskolem, |
332 |
|
const std::string& prefix, |
333 |
|
const std::string& comment = "", |
334 |
|
int flags = NodeManager::SKOLEM_DEFAULT); |
335 |
|
}; |
336 |
|
|
337 |
|
} // namespace cvc5 |
338 |
|
|
339 |
|
#endif /* CVC5__EXPR__PROOF_SKOLEM_CACHE_H */ |