@@ -33,7 +33,141 @@ namespace quantifiers {
33
33
34
34
class ConjectureGenerator ;
35
35
36
- // operator independent index of arguments for an EQC
36
+ /* * operator independent index of arguments for an EQC
37
+ *
38
+ * The (almost) inductive definition of the set of irrelevant terms suggests
39
+ * the following algorithm to compute I, the set of irrelevant equivalence
40
+ * classes. It is a standard algorithm that starts from the empty set and
41
+ * iterates up to a fixed point.
42
+ *
43
+ * declare I and set its value to the empty set
44
+ *
45
+ * for each equivalence class e:
46
+ * if the sort of e is not an inductive datatype sort:
47
+ * add e to I
48
+ *
49
+ * declare I_old
50
+ *
51
+ * do:
52
+ * set I_old to the current value of I
53
+ * for each equivalence class e:
54
+ * if e is not in I:
55
+ * for each term t in e:
56
+ * if t has the form f(t_1, ..., t_n) where f is an atomic
57
+ * trigger that is not a skolem function and the equivalence class
58
+ * of each t_i is in I:
59
+ * add e to I
60
+ * continue to the next equivalence class
61
+ * else
62
+ * continue to the next term in e
63
+ * while I_old is different from I
64
+ *
65
+ * Note the three nested loops in the second phase of the algorithm above.
66
+ * We can avoid inspecting each element of each equivalence class that is not
67
+ * already in I by preparing a family of indices, which can be described
68
+ * abstractly as follows.
69
+ *
70
+ * _Definitions_
71
+ *
72
+ * - 'E' is the set of representatives of equivalence classes.
73
+ * - 'F' is the set of function symbols.
74
+ * - 'T' is the set of terms.
75
+ * - For a set 'X', X* denotes the set of all strings over X.
76
+ * - For a set 'X', X+ denotes the set of non-empty strings over X.
77
+ * - For sets 'X' and 'Y', X x Y denotes their cartesian product.
78
+ * - 't = u' means the terms t and u are syntactically equal.
79
+ * - 't ~ u' means the terms t and u are in the same equivalence class according
80
+ * to the current model
81
+ *
82
+ * _Declarations_
83
+ *
84
+ * - 'OpArgIndex' is a subset of E+, we intend that OpArgIndex(e e_1 ... e_n) is
85
+ * true iff the string e e_1 ... e_n denotes an instance of the C++ class named
86
+ * OpArgIndex
87
+ *
88
+ * - '[[e e_1 ... e_n]]' is the instance of the OpArgIndex class denoted by the
89
+ * string e e_1 ... e_n when OpArgIndex(e e_1 ... e_n) is true, and it is
90
+ * equal to d_op_arg_index[e].d_child[e_1].(...).d_child[e_n]
91
+ *
92
+ * - 'child' is a subset of E+ x E, we intend that child(e e_1 ... e_n, e^) is
93
+ * true iff the map [[e e_1 ... e_n]].d_child contains e^ as a key.
94
+ *
95
+ * - 'ops' : OpArgIndex -> F* is a function where we intend ops(e e_1 ... e_n)
96
+ * to be the same sequence of function symbols as the vector
97
+ * [[e e_1 ... e_n]].d_ops
98
+ *
99
+ * - 'op_terms' : OpArgIndex -> T* is a function where we intend
100
+ * op_terms(e e_1 ... e_n) to be the same sequence of terms as the vector
101
+ * [[e e_1 ... e_n]].d_op_terms
102
+ *
103
+ * - 'added' is a subset of E x T where we intend added(e, t) to be true iff
104
+ * d_op_arg_index[e].addTerm(t) executes successfully.
105
+ *
106
+ * _Invariants_
107
+ *
108
+ * (i) child(e e_1 ... e_n, e^)
109
+ * <==> OpArgIndex(e e_1 ... e_n e^)
110
+ *
111
+ * (ii) OpArgIndex(e e_1 ... e_n)
112
+ * ==> for all 0 <= i < n. OpArgIndex(e e_1 ... e_i)
113
+ *
114
+ * (iii) added(e, f(t_1, ..., t_n))
115
+ * <==> OpArgIndex(e e_1 ... e_n)
116
+ * /\ there exists j. ops(e e_1 ... e_n)(j) = f
117
+ * /\ op_terms(e e_1 ... e_n)(j) = f(t_1, ..., t_n)
118
+ *
119
+ * (iv) d_ops(e e_1 ... e_n) has the same length as |d_op_terms(e e_1 ... e_n)|
120
+ *
121
+ * _Additional guarantees_
122
+ *
123
+ * In the implementation of getEquivalenceClasses, note that we add
124
+ * f(t_1, ..., t_n) to d_op_terms[e] when, among satisfying certain other
125
+ * properties, it is in e's equivalence class. This guarantees that
126
+ * added(e, f(t_1, ..., t_n)) ==> f(t_1, ..., t_n) ~ e.
127
+ *
128
+ * Furthermore the implementation of addTerm ensures that for any equivalence
129
+ * class representative e and for any two terms t = f(t_1, ..., t_n) and
130
+ * u = f(u_1, ..., u_n) such that t != u, t ~ e, u ~ e,
131
+ * and t_i ~ u_i for each i, we that at most one of added(e, t) and
132
+ * added(e, u) is true.
133
+ *
134
+ * _Take-away_
135
+ *
136
+ * The problem of deciding whether the equivalence class represented by e is
137
+ * irrelevant (see comment for computeIrrelevantEqcs) falls to searching for
138
+ * a string e e_1 ... e_n such that
139
+ *
140
+ * - OpArgIndex(e e_1 ... e_n), and
141
+ * - for all 1 <= i < n. e_i is irrelevant, and
142
+ * - ops(e e_1 ... e_n) is non-empty.
143
+ *
144
+ * as implemented in 'getGroundTerms'.
145
+ *
146
+ * We hope is that searching for such a string is more efficient than the
147
+ * naive approach of iterating over all terms in e's equivalence class and
148
+ * checking if any one of these terms is irrelevant.
149
+ *
150
+ * _Example_
151
+ *
152
+ * Let e, e_1, e_2 and e_3 be representatives of equivalence classes.
153
+ * Suppose we're given that
154
+ *
155
+ * - f(t_1,t_2) ~ g(t_3) ~ f(t_4,t_5) ~ f(t_6,t_7) ~ e, and
156
+ * - t_1 ~ t_3 ~ t_4 ~ t_6 ~ e_1, and
157
+ * - t_2 ~ t_5 ~ e_2, and
158
+ * - t_7 ~ e_3
159
+ *
160
+ * Suppose also that we add f(t_1,t_2), g(t_3), f(t_4,t_5), and f(t_6,t_7)
161
+ * to d_op_arg_index[e] in sequence. The resulting data structure looks like
162
+ *
163
+ * [[e]], d_ops = [], d_op_terms = []
164
+ * |
165
+ * e_1 '-- [[e e_1]], d_ops = [ g ], d_op_terms = [ g(t_3) ]
166
+ * |
167
+ * e_2 |-- [[e e_1 e_2]], d_ops = [ f ], d_op_terms = [ f(t_4,t_5) ]
168
+ * |
169
+ * e_3 '-- [[e e_1 e_3]], d_ops = [ f ], d_op_terms = [ f(t_6,t_7) ]
170
+ */
37
171
class OpArgIndex
38
172
{
39
173
public:
0 commit comments