Skip to content

Commit 348845e

Browse files
Documentation for OpArgIndex in conjecture generator (cvc5#11665)
1 parent cecff59 commit 348845e

File tree

1 file changed

+135
-1
lines changed

1 file changed

+135
-1
lines changed

src/theory/quantifiers/conjecture_generator.h

Lines changed: 135 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,141 @@ namespace quantifiers {
3333

3434
class ConjectureGenerator;
3535

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+
*/
37171
class OpArgIndex
38172
{
39173
public:

0 commit comments

Comments
 (0)