CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
expr_enumerator.h
Go to the documentation of this file.
1/*******************************************************************\
2Module: Enumerator Interface
3Author: Qinheping Hu
4\*******************************************************************/
5
8
9#ifndef CPROVER_GOTO_SYNTHESIZER_EXPR_ENUMERATOR_H
10#define CPROVER_GOTO_SYNTHESIZER_EXPR_ENUMERATOR_H
11
12#include <util/expr.h>
13
14#include <list>
15#include <map>
16#include <set>
17
18typedef std::list<exprt> expr_listt;
19typedef std::set<exprt> expr_sett;
20typedef std::list<std::size_t> partitiont;
21
22/*
23 This is the interface of grammar-based enumerators. Users can specify tree
24 grammars, and enumerate expressions derived by the grammars using these
25 enumerators. A tree grammar consists of a finite set of nonterminals and
26 a finite set of productions of the form A -> t where A is some nonterminal,
27 and t is some tree composed from nonterminal symbols and constants.
28
29 In the level of productions, we introduce three classes of enumerators:
30 1. `leaf_enumeratort` enumerates `exprt`-typed expressions. It is used
31 to enumerate leaves of trees derived from the tree grammar.
32 Example:
33 a `leaf_enumeratort` with `leaf_exprs` {0} enumerates expressions derived
34 from the production
35 * -> 0.
36 2. `non_leaf_enumeratort` enumerates expressions recursively built from
37 sub-trees---trees enumerated by sub-enumerators.
38 Example:
39 a `non_leaf_enumeratort` with `sub_enumerators` {A, B} and `op` +
40 enumerates expressions derived from the production
41 * -> A + B.
42 3. `alternatives_enumeratort` enumerates expressions that can be enumerated
43 by any of its `sub_enumerators`
44 Example:
45 a `alternatives_enumeratort` with `sub_enumerators` the enumerators in
46 the Example 1 and 2 enumerates expressions derived from the production
47 * -> 0 | A + B.
48
49 One missing part in the above enumerators is the left-hand-side of
50 productions, it should be a nonterminal symbol associated with a finite set
51 of productions. So we introduce `recursive_enumerator_placeholdert` for
52 nonterminal symbols, and `recursive_enumerator_factoryt` for grammars.
53 Each placeholder has an identifier, and corresponds to a nonterminal in the
54 grammar. Each factory maintains a map from identifier to placeholder, and a
55 map from identifiers to placeholder's productions.
56
57 As an example, to specify a grammar G with two nonterminals A and B, and two
58 productions
59 A -> A + B | 0
60 B -> 1.
61 We need to construct a factory with two placeholders ph_A and ph_b whose
62 identifiers are "A" and "B", respectively.
63 The factory should contain a set {ph_A, ph_B}, and a map
64 {("A", E_A), ("B", E_B)}
65 where the alternatives enumerator E_A is for the production
66 -> A + B | 0
67 and the alternatives enumerator E_B is for the production
68 -> 1 | 2.
69 Note that E_A contains a binary non-leaf enumerator E_plus with
70 sub-enumerators (ph_A, ph_B) and operator `ID_plus`, and a leaf enumerator
71 E_0 with `leaf_exprs` {0}. E_B contains a leaf enumerator E_1 with
72 `leaf_exprs` {1} and a leaf enumerator E_2 with `leaf_exprs` {2}.
73
74 Let [E, n] to be the result of enumeration E.enumerate(n)---expressions with
75 size n enumerated by the enumerator E. To enumerate expressions with size 5
76 in L(A) in the above example, the algorithm works as follow.
77 [ph_A, 5] = [E_A, 5] looking up "A" from `productions_map` of ph_A.factory
78 = [E_plus, 5] \/ [E_0, 5]
79 = [E_plus, 5] \/ {} = [E_plus, 5]
80 [E_0, 5] = {} as leaf enumerator enumerates only expressions with size 1.
81
82 To enumerate [E_plus, 5], we first enumerate expressions from two
83 sub-enumerators E_A and E_B of E_plus, and then combine them using the
84 operator ID_plus. In E_plus, the size of the operator ID_plus is 1, so the
85 sum of sizes of two sub-expressions should be 4. There are three way to
86 partition 4: (3, 1), (2, 2), and (1, 3). So
87 [E_plus, 5] = [E_A, 3]*[E_B, 1] \/ [E_A, 2]*[E_B, 2] \/ [E_A, 1]*[E_B, 3]
88 Note that E_B contain only leaf expressions. Therefore [E_B, 1] = {1, 2}, and
89 [E_B, n] = {} for all n > 1. The * operator is the Cartesian products
90 instantiated with plus operator.
91 [E_plus, 5] = [E_A, 3]*[E_B, 1] \/ [E_A, 2]*[E_B, 2] \/ [E_A, 1]*[E_B, 3]
92 = [E_A, 3]*{1, 2} \/ [E_A, 2]*{} \/ [E_A, 2]*{}
93 = [E_A, 3]*{1, 2} \/ {} \/ {}
94 = [E_A, 3]*{1, 2}
95 = ([E_A, 1]*[E_B, 1])*{1, 2}
96 = ({0}*{1,2})*{1, 2}
97 = {0+1, 0+2}*{1, 2}
98 = {(0+1)+1, (0+2)+1, (0+1)+2, (0+2)+2}
99 So, expressions in L(A) are {(0+1)+1, (0+2)+1, (0+1)+2, (0+2)+2}.
100
101 Why Cartesian products.
102 For a grammar,
103 A -> B + B
104 B -> 1 | 2,
105 the expressions enumerated by B are {1, 2}. To enumerate expressions in L(A).
106 We need to combine sub-expressions {1, 2} into a plus-expression.
107 {1, 2} + {1, 2}
108 Because each nonterminal B can yield either 1 and 2 independently, we have to
109 combine sub-expressions as their cartesian products.
110 {1 + 1, 1 + 2, 2 + 1, 2 + 2}.
111*/
112
115{
116public:
117 explicit enumerator_baset(const namespacet &ns) : ns(ns)
118 {
119 }
120
121 virtual expr_sett enumerate(const std::size_t size) const = 0;
122
123 enumerator_baset(const enumerator_baset &other) = delete;
125
126 virtual ~enumerator_baset() = default;
127
128protected:
130};
131
132typedef std::list<const enumerator_baset *> enumeratorst;
133
137{
138public:
143
145 expr_sett enumerate(const std::size_t size) const override;
146
147protected:
149};
150
155{
156public:
165 const std::function<bool(const partitiont &)> partition_check,
166 const namespacet &ns)
168 arity(enumerators.size()),
171 {
172 INVARIANT(
173 arity > 1, "arity should be greater than one for non_leaf_enumeratort");
174 }
175
180 [](const partitiont &) { return true; },
181 ns)
182 {
183 }
184
185 expr_sett enumerate(const std::size_t size) const override;
186
189 std::set<expr_listt> cartesian_product_of_enumerators(
191 const enumeratorst::const_iterator &it,
192 const partitiont &partition,
193 const partitiont::const_iterator &it_partition) const;
194
197 std::list<partitiont>
198 get_partitions(const std::size_t n, const std::size_t k) const;
199
202 {
203 return true;
204 }
205
206protected:
208 virtual exprt instantiate(const expr_listt &exprs) const = 0;
209
210 const std::size_t arity;
212 const std::function<bool(const partitiont &)> is_good_partition;
213};
214
217{
218public:
220 const irep_idt &op,
223 const std::function<bool(const partitiont &)> partition_check,
224 const bool exchangeable,
225 const namespacet &ns)
228 op_id(op)
229 {
230 }
231
233 const irep_idt &op,
236 const std::function<bool(const partitiont &)> partition_check,
237 const namespacet &ns)
239 op,
244 ns)
245 {
246 }
247
249 const irep_idt &op,
252 const namespacet &ns)
254 op,
257 [](const partitiont &) { return true; },
258 ns)
259 {
260 }
261
262 bool is_commutative(const irep_idt &op) const;
263
266 bool
268
269protected:
270 exprt instantiate(const expr_listt &exprs) const override;
271
272 // Whether the two sub-enumerators are exchangeable---they enumerate the same
273 // set of expressions.
274 const bool is_exchangeable = false;
275
277};
278
284{
285public:
292
293 expr_sett enumerate(const std::size_t size) const override;
294
295protected:
297};
298
300
303{
304public:
306 {
307 }
308
311
313 void
314 attach_productions(const std::string &id, const enumeratorst &enumerators);
315
320 std::map<std::string, const enumeratorst> productions_map;
321
322protected:
324
326 std::set<std::string> nonterminal_set;
327};
328
336{
337public:
344 const std::string &id,
345 const namespacet &ns)
347 {
348 factory.add_placeholder(*this);
349 }
350
351 expr_sett enumerate(const std::size_t size) const override;
352
353 const std::string identifier;
354
355protected:
357};
358
359#endif // CPROVER_GOTO_SYNTHESIZER_EXPR_ENUMERATOR_H
std::list< exprt > expr_listt
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Enumerators that enumerates expressions in the union of enumerated expressions of sub-enumerators.
const enumeratorst sub_enumerators
expr_sett enumerate(const std::size_t size) const override
alternatives_enumeratort(const enumeratorst &enumerators, const namespacet &ns)
Enumerator that enumerates binary functional expressions.
binary_functional_enumeratort(const irep_idt &op, const enumerator_baset &enumerator_1, const enumerator_baset &enumerator_2, const std::function< bool(const partitiont &)> partition_check, const bool exchangeable, const namespacet &ns)
bool is_equivalence_class_representation(const expr_listt &exprs) const override
Determine whether a tuple of expressions is the representation of some equivalence class.
binary_functional_enumeratort(const irep_idt &op, const enumerator_baset &enumerator_1, const enumerator_baset &enumerator_2, const namespacet &ns)
binary_functional_enumeratort(const irep_idt &op, const enumerator_baset &enumerator_1, const enumerator_baset &enumerator_2, const std::function< bool(const partitiont &)> partition_check, const namespacet &ns)
exprt instantiate(const expr_listt &exprs) const override
Combine a list of sub-expressions to construct the top-level expression.
bool is_commutative(const irep_idt &op) const
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
A base class for expression enumerators.
const namespacet & ns
virtual expr_sett enumerate(const std::size_t size) const =0
enumerator_baset(const namespacet &ns)
enumerator_baset(const enumerator_baset &other)=delete
virtual ~enumerator_baset()=default
enumerator_baset & operator=(const enumerator_baset &other)=delete
Factory for enumerator that can be used to represent a tree grammar.
enumerator_factoryt(const namespacet &ns)
void add_placeholder(const recursive_enumerator_placeholdert &placeholder)
Add a new placeholder/nonterminal to the grammar.
std::set< std::string > nonterminal_set
Set of nonterminals in the grammar.
std::map< std::string, const enumeratorst > productions_map
Map from names of nonterminals to rhs of productions with lhs being them.
void attach_productions(const std::string &id, const enumeratorst &enumerators)
Attach enumerators to the placeholder with id.
const namespacet & ns
Base class for all expressions.
Definition expr.h:56
Enumerator that enumerates leaf expressions from a given list.
expr_sett enumerate(const std::size_t size) const override
Enumerate expressions in the set of leaf_exprs.
leaf_enumeratort(const expr_sett &leaf_exprs, const namespacet &ns)
const expr_sett leaf_exprs
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
Non-leaf enumerator enumerates expressions of form f(a_1, a_2, ..., a_n) where a_i's are sub-expressi...
expr_sett enumerate(const std::size_t size) const override
non_leaf_enumeratort(const enumeratorst &enumerators, const namespacet &ns)
As default: no optimization. partition_check Accept all partitions.
const std::size_t arity
std::list< partitiont > get_partitions(const std::size_t n, const std::size_t k) const
Enumerate all partitions of n into k components.
virtual exprt instantiate(const expr_listt &exprs) const =0
Combine a list of sub-expressions to construct the top-level expression.
non_leaf_enumeratort(const enumeratorst &enumerators, const std::function< bool(const partitiont &)> partition_check, const namespacet &ns)
const std::function< bool(const partitiont &)> is_good_partition
const enumeratorst sub_enumerators
std::set< expr_listt > cartesian_product_of_enumerators(const enumeratorst &enumerators, const enumeratorst::const_iterator &it, const partitiont &partition, const partitiont::const_iterator &it_partition) const
Given a list enumerators of enumerators, return the Cartesian product of expressions enumerated by ea...
virtual bool is_equivalence_class_representation(const expr_listt &es) const
As default, keep all expression tuples.
Placeholders for actual enumerators, which represent nonterminals.
const enumerator_factoryt & factory
expr_sett enumerate(const std::size_t size) const override
recursive_enumerator_placeholdert(enumerator_factoryt &factory, const std::string &id, const namespacet &ns)
std::unordered_set< exprt, irep_hash > expr_sett
std::list< exprt > expr_listt
std::set< exprt > expr_sett
std::list< const enumerator_baset * > enumeratorst
std::list< std::size_t > partitiont
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423