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