GCC Code Coverage Report
Directory: . Exec Total Coverage
File: src/theory/quantifiers/sygus/transition_inference.h Lines: 12 12 100.0 %
Date: 2021-08-14 Branches: 4 8 50.0 %

Line Exec Source
1
/******************************************************************************
2
 * Top contributors (to current version):
3
 *   Andrew Reynolds
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
 * Utility for inferring whether a synthesis conjecture encodes a
14
 * transition system.
15
 */
16
17
#include "cvc5_private.h"
18
19
#ifndef CVC5__THEORY__QUANTIFIERS__TRANSITION_INFERENCE_H
20
#define CVC5__THEORY__QUANTIFIERS__TRANSITION_INFERENCE_H
21
22
#include <map>
23
#include <vector>
24
25
#include "expr/node.h"
26
27
#include "theory/quantifiers/cegqi/inst_strategy_cegqi.h"
28
#include "theory/quantifiers/inst_match_trie.h"
29
#include "theory/quantifiers/single_inv_partition.h"
30
31
namespace cvc5 {
32
namespace theory {
33
namespace quantifiers {
34
35
/**
36
 * Utility for storing a deterministic trace of a transition system. A trace
37
 * is stored as a collection of vectors of values that have been taken by
38
 * the variables of transition system. For example, say we have a transition
39
 * system with variables x,y,z. Say the precondition constrains these variables
40
 * to x=1,y=2,z=3, and say that the transition relation admits the single
41
 * trace: [1,2,3], [2,3,4], [3,4,5]. We store these three vectors of variables
42
 * in the trie within this class.
43
 *
44
 * This utility is used for determining whether a transition system has a
45
 * deterministic terminating trace and hence a trivial invariant.
46
 */
47
62
class DetTrace
48
{
49
 public:
50
  /** The current value of the trace */
51
  std::vector<Node> d_curr;
52
  /**
53
   * Increment the trace: index the current values, if successful (they are
54
   * not a duplicate of a previous value), update the current values to vals.
55
   * Returns true if the increment was successful.
56
   */
57
  bool increment(Node loc, std::vector<Node>& vals);
58
  /**
59
   * Construct the formula that this trace represents with respect to variables
60
   * in vars. For details, see DetTraceTrie::constructFormula below.
61
   */
62
  Node constructFormula(const std::vector<Node>& vars);
63
  /** Debug print this trace on trace message c */
64
  void print(const char* c) const;
65
66
 private:
67
  /**
68
   * A trie of value vectors for the variables of a transition system. Nodes
69
   * are stored as data in tries with no children at the leaves of this trie.
70
   */
71
2602
  class DetTraceTrie
72
  {
73
   public:
74
    /** the children of this trie */
75
    std::map<Node, DetTraceTrie> d_children;
76
    /** Add data loc to this trie, indexed by val. */
77
    bool add(Node loc, const std::vector<Node>& val);
78
    /** clear the trie */
79
428
    void clear() { d_children.clear(); }
80
    /**
81
     * Construct the formula corresponding to this trie with respect to
82
     * variables in vars. For example, if we have indexed [1,2,3] and [2,3,4]
83
     * and vars is [x,y,z], then this method returns:
84
     *   ( x=1 ^ y=2 ^ z=3 ) V ( x=2 ^ y=3 ^ z=4 ).
85
     */
86
    Node constructFormula(const std::vector<Node>& vars, unsigned index = 0);
87
  };
88
  /** The above trie data structure for this class */
89
  DetTraceTrie d_trie;
90
};
91
92
/**
93
 * Trace increment status, used for incrementTrace below.
94
 */
95
enum TraceIncStatus
96
{
97
  // the trace was successfully incremented to a new value
98
  TRACE_INC_SUCCESS,
99
  // the trace terminated
100
  TRACE_INC_TERMINATE,
101
  // the trace encountered a bad state (violating the post-condition)
102
  TRACE_INC_CEX,
103
  // the trace was invalid
104
  TRACE_INC_INVALID
105
};
106
107
/**
108
 * This class is used for inferring that an arbitrary synthesis conjecture
109
 * corresponds to an invariant synthesis problem for some predicate (d_func).
110
 *
111
 * The invariant-to-synthesize can either be explicitly given, via a call
112
 * to initialize( f, vars ), or otherwise inferred if this method is not called.
113
 */
114
1160
class TransitionInference
115
{
116
 public:
117
1160
  TransitionInference() : d_complete(false) {}
118
  /** Process the conjecture n
119
   *
120
   * This initializes this class with information related to viewing it as a
121
   * transition system. This means we infer a function, the state variables,
122
   * the pre/post condition and transition relation.
123
   *
124
   * The node n should be the inner body of the negated synthesis conjecture,
125
   * prior to generating the deep embedding. That is, given:
126
   *   forall f. ~forall x. P( f, x ),
127
   * this method expects n to be P( f, x ), and the argument f to be the
128
   * function f above.
129
   */
130
  void process(Node n, Node f);
131
  /**
132
   * Same as above, without specifying f. This will try to infer a function f
133
   * based on the body of n.
134
   */
135
  void process(Node n);
136
  /**
137
   * Get the function that is the subject of the synthesis problem we are
138
   * analyzing.
139
   */
140
  Node getFunction() const;
141
  /**
142
   * Get the variables that the function is applied to. These are the free
143
   * variables of the pre/post condition, and transition relation. These are
144
   * fresh (Skolem) variables allocated by this class.
145
   */
146
  void getVariables(std::vector<Node>& vars) const;
147
  /**
148
   * Get the pre/post condition, or transition relation that was inferred by
149
   * this class.
150
   */
151
  Node getPreCondition() const;
152
  Node getPostCondition() const;
153
  Node getTransitionRelation() const;
154
  /**
155
   * Was the analysis of the conjecture complete?
156
   *
157
   * If this is false, then the system we have inferred does not take into
158
   * account all of the synthesis conjecture. This is the case when process(...)
159
   * was called on formula that does not have the shape of a transition system.
160
   */
161
75
  bool isComplete() const { return d_complete; }
162
  /**
163
   * Was the analysis of the conjecture trivial? This is true if the function
164
   * did not occur in the conjecture.
165
   */
166
5
  bool isTrivial() const { return d_trivial; }
167
168
  /**
169
   * The following two functions are used for computing whether this transition
170
   * relation is deterministic and terminating.
171
   *
172
   * The argument fwd is whether we are going in the forward direction of the
173
   * transition system (starting from the precondition).
174
   *
175
   * If fwd is true, the initializeTrace method returns TRACE_INC_SUCCESS if the
176
   * precondition consists of a single conjunct of the form
177
   *   ( x1 = t1 ^ ... ^ xn = tn )
178
   * where x1...xn are the state variables of the transition system. Otherwise
179
   * it returns TRACE_INC_INVALID.
180
   */
181
  TraceIncStatus initializeTrace(DetTrace& dt, bool fwd = true);
182
  /**
183
   * Increment the trace dt in direction fwd.
184
   *
185
   * If fwd is true, the incrementTrace method returns TRACE_INC_INVALID if the
186
   * transition relation is not of the form
187
   *   ( x1' = t1[X] ^ ... ^ xn' = tn[X] ^ Q[X] ^ P(x1...xn) ) => P( x1'...xn' )
188
   * Otherwise, it returns TRACE_INC_TERMINATE if the values of
189
   * x1' = t1[dt.d_curr] ^ ... ^ xn' = tn[dt.d_curr] have already been executed
190
   * on trace dt (the trace has looped), or if
191
   * x1' = t1[dt.d_curr] ^ ... ^ xn' = tn[dt.d_curr] ^ Q[dt.d_curr] is unsat
192
   * (the trace has terminated). It returns TRACE_INC_CEX if the postcondition
193
   * is false for the values t1[dt.d_curr] ^ ... ^ tn[dt.d_curr]. Otherwise,
194
   * it returns TRACE_INC_SUCCESS.
195
   */
196
  TraceIncStatus incrementTrace(DetTrace& dt, bool fwd = true);
197
  /**
198
   * Constructs the formula corresponding to trace dt with respect to the
199
   * variables of this class.
200
   */
201
  Node constructFormulaTrace(DetTrace& dt) const;
202
203
 private:
204
  /**
205
   * The function (predicate) that is the subject of the invariant synthesis
206
   * problem we are inferring.
207
   */
208
  Node d_func;
209
  /** The variables that the function is applied to */
210
  std::vector<Node> d_vars;
211
  /**
212
   * The variables that the function is applied to in the next state of the
213
   * inferred transition relation.
214
   */
215
  std::vector<Node> d_prime_vars;
216
  /**
217
   * Was the analysis of the synthesis conjecture passed to the process method
218
   * of this class complete?
219
   */
220
  bool d_complete;
221
  /** Was the analyis trivial? */
222
  bool d_trivial;
223
224
  /** process disjunct
225
   *
226
   * The purpose of this function is to infer pre/post/transition conditions
227
   * for a (possibly unknown) invariant-to-synthesis, given a conjunct from
228
   * an arbitrary synthesis conjecture.
229
   *
230
   * Assume our negated synthesis conjecture is of the form:
231
   *    forall f. exists x. (and (or F11 ... F1{m_1}) ... (or Fn1 ... Fn{m_n}))
232
   * This method is called on each (or Fi1 ... Fi{m_i}), where topLevel is true
233
   * for each of Fi1...F1{m_i} and false otherwise. It adds each of Fi1..Fi{m_i}
234
   * to disjuncts.
235
   *
236
   * If this method returns true, then (1) all applications of free function
237
   * symbols have operator d_func. Note this function may set d_func to a
238
   * function symbol in n if d_func was null prior to this call. In other words,
239
   * this method may infer the subject of the invariant synthesis problem;
240
   * (2) all occurrences of d_func are "top-level", that is, each Fij may be
241
   * of the form (not) <d_func>( tj ), but otherwise d_func does not occur in
242
   * (or Fi1 ... Fi{m_i}); (3) there exists at most one occurrence of
243
   * <d_func>( tj ), and (not <d_func>( tk )).
244
   *
245
   * If the above conditions are met, then terms[true] is set to <d_func>( tj )
246
   * if Fij is <d_func>( tj ) for some j, and likewise terms[false]
247
   * is set to <d_func>( tk ) if Fik is (not <d_func>( tk )) for some k.
248
   *
249
   * The argument visited caches the results of this function for (topLevel, n).
250
   */
251
  bool processDisjunct(Node n,
252
                       std::map<bool, Node>& terms,
253
                       std::vector<Node>& disjuncts,
254
                       std::map<bool, std::map<Node, bool> >& visited,
255
                       bool topLevel);
256
  /**
257
   * This method infers if the conjunction of disjuncts is equivalent to a
258
   * conjunction of the form
259
   *   (~) const_var[1] = const_subs[1] ... (~) const_var[n] = const_subs[n]
260
   * where the above equalities are negated iff reqPol is false, and
261
   *   const_var[1] ... const_var[n]
262
   * are distinct members of vars
263
   */
264
  void getConstantSubstitution(const std::vector<Node>& vars,
265
                               const std::vector<Node>& disjuncts,
266
                               std::vector<Node>& const_var,
267
                               std::vector<Node>& const_subs,
268
                               bool reqPol);
269
  /** get normalized substitution
270
   *
271
   * This method takes as input a node curr of the form I( t1, ..., tn ) and
272
   * a vector of variables pvars, where pvars.size()=n. For each ti that is
273
   * a variable, it adds ti to vars, and pvars[i] to subs. For each ti that is
274
   * not a variable, it adds the disequality ti != pvars[i] to disjuncts.
275
   *
276
   * This function is used for instance to normalize an arbitrary application of
277
   * I so that is over arguments pvars. For instance if curr is I(3,5,y) and
278
   * pvars = { x1,x2,x3 }, then the formula:
279
   *   I(3,5,y) ^ P(y)
280
   * is equivalent to:
281
   *   x1 != 3 V x2 != 5 V I(x1,x2,x3) V P( y ) { y -> x3 }
282
   * Here, we add y and x3 to vars and subs respectively, and x1!=3 and x2!=5
283
   * to disjuncts.
284
   */
285
  void getNormalizedSubstitution(Node curr,
286
                                 const std::vector<Node>& pvars,
287
                                 std::vector<Node>& vars,
288
                                 std::vector<Node>& subs,
289
                                 std::vector<Node>& disjuncts);
290
  /**
291
   * Stores one of the components of the inferred form of the synthesis
292
   * conjecture (precondition, postcondition, or transition relation).
293
   */
294
3480
  class Component
295
  {
296
   public:
297
3480
    Component() {}
298
    /** The formula that was inferred for this component */
299
    Node d_this;
300
    /** The list of conjuncts of the above formula */
301
    std::vector<Node> d_conjuncts;
302
    /**
303
     * Maps formulas to the constant equality substitution that it entails.
304
     * For example, the formula (x=4 ^ y=x+5) may map to { x -> 4, y -> 9 }.
305
     */
306
    std::map<Node, std::map<Node, Node> > d_const_eq;
307
    /** Does this component have conjunct c? */
308
454
    bool has(Node c) const
309
    {
310
908
      return std::find(d_conjuncts.begin(), d_conjuncts.end(), c)
311
1362
             != d_conjuncts.end();
312
    }
313
  };
314
  /** Components for the pre/post condition and transition relation. */
315
  Component d_pre;
316
  Component d_post;
317
  Component d_trans;
318
  /**
319
   * Initialize trace dt, loc is a node to identify the trace, fwd is whether
320
   * we are going in the forward direction of the transition system (starting
321
   * from the precondition).
322
   *
323
   * The argument loc is a conjunct of transition relation that entails that the
324
   * trace dt has executed in its last step to its current value. For example,
325
   * if the transition relation is ( x'=x+1 ^ P( x ) ) => P(x'), and our trace's
326
   * current value was last updated [x:=1] -> [x:=2] based on x'=x+1, then loc
327
   * is the node x'=x+1.
328
   */
329
  TraceIncStatus initializeTrace(DetTrace& dt, Node loc, bool fwd = true);
330
  /** Same as above, for incrementing the trace dt */
331
  TraceIncStatus incrementTrace(DetTrace& dt, Node loc, bool fwd = true);
332
};
333
334
}  // namespace quantifiers
335
}  // namespace theory
336
}  // namespace cvc5
337
338
#endif