CBMC
expr_enumerator.h
Go to the documentation of this file.
1 /*******************************************************************\
2 Module: Enumerator Interface
3 Author: 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 
18 typedef std::list<exprt> expr_listt;
19 typedef std::set<exprt> expr_sett;
20 typedef 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 {
116 public:
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;
124  enumerator_baset &operator=(const enumerator_baset &other) = delete;
125 
126  virtual ~enumerator_baset() = default;
127 
128 protected:
129  const namespacet &ns;
130 };
131 
132 typedef std::list<const enumerator_baset *> enumeratorst;
133 
137 {
138 public:
141  {
142  }
143 
145  expr_sett enumerate(const std::size_t size) const override;
146 
147 protected:
149 };
150 
155 {
156 public:
164  const enumeratorst &enumerators,
165  const std::function<bool(const partitiont &)> partition_check,
166  const namespacet &ns)
167  : enumerator_baset(ns),
168  arity(enumerators.size()),
169  sub_enumerators(enumerators),
170  is_good_partition(partition_check)
171  {
172  INVARIANT(
173  arity > 1, "arity should be greater than one for non_leaf_enumeratort");
174  }
175 
177  non_leaf_enumeratort(const enumeratorst &enumerators, const namespacet &ns)
179  enumerators,
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(
190  const enumeratorst &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 
201  virtual bool is_equivalence_class_representation(const expr_listt &es) const
202  {
203  return true;
204  }
205 
206 protected:
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 {
218 public:
220  const irep_idt &op,
221  const enumerator_baset &enumerator_1,
222  const enumerator_baset &enumerator_2,
223  const std::function<bool(const partitiont &)> partition_check,
224  const bool exchangeable,
225  const namespacet &ns)
226  : non_leaf_enumeratort({&enumerator_1, &enumerator_2}, partition_check, ns),
227  is_exchangeable(exchangeable),
228  op_id(op)
229  {
230  }
231 
233  const irep_idt &op,
234  const enumerator_baset &enumerator_1,
235  const enumerator_baset &enumerator_2,
236  const std::function<bool(const partitiont &)> partition_check,
237  const namespacet &ns)
239  op,
240  enumerator_1,
241  enumerator_2,
242  partition_check,
243  &enumerator_1 == &enumerator_2,
244  ns)
245  {
246  }
247 
249  const irep_idt &op,
250  const enumerator_baset &enumerator_1,
251  const enumerator_baset &enumerator_2,
252  const namespacet &ns)
254  op,
255  enumerator_1,
256  enumerator_2,
257  [](const partitiont &) { return true; },
258  ns)
259  {
260  }
261 
262  bool is_commutative(const irep_idt &op) const;
263 
266  bool
267  is_equivalence_class_representation(const expr_listt &exprs) const override;
268 
269 protected:
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 
276  const irep_idt &op_id;
277 };
278 
284 {
285 public:
287  const enumeratorst &enumerators,
288  const namespacet &ns)
289  : enumerator_baset(ns), sub_enumerators(enumerators)
290  {
291  }
292 
293  expr_sett enumerate(const std::size_t size) const override;
294 
295 protected:
297 };
298 
300 
303 {
304 public:
305  explicit enumerator_factoryt(const namespacet &ns) : ns(ns)
306  {
307  }
308 
310  void add_placeholder(const recursive_enumerator_placeholdert &placeholder);
311 
313  void
314  attach_productions(const std::string &id, const enumeratorst &enumerators);
315 
320  std::map<std::string, const enumeratorst> productions_map;
321 
322 protected:
323  const namespacet &ns;
324 
326  std::set<std::string> nonterminal_set;
327 };
328 
336 {
337 public:
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 
355 protected:
357 };
358 
359 #endif // CPROVER_GOTO_SYNTHESIZER_EXPR_ENUMERATOR_H
std::list< exprt > expr_listt
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:94
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