GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/expr/skolem_manager.h Lines: 2 2 100.0 %
Date: 2021-09-18 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
  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
9858
  SkolemManager() {}
101
9858
  ~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 */