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