GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/expr/skolem_manager.h Lines: 2 2 100.0 %
Date: 2021-05-21 Branches: 0 0 0.0 %

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