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