@@ -13,7 +13,6 @@ Author: Daniel Kroening, kroening@kroening.com
1313#include < util/invariant.h>
1414#include < util/optional.h>
1515#include < util/range.h>
16- #include < util/replace_expr.h>
1716#include < util/simplify_expr.h>
1817
1918// / A method to detect equivalence between experts that can contain typecast
@@ -138,71 +137,109 @@ get_quantifier_var_max(const exprt &var_expr, const exprt &quantifier_expr)
138137 return {};
139138}
140139
141- static optionalt<exprt>
142- instantiate_quantifier (const quantifier_exprt &expr, const namespacet &ns)
140+ static optionalt<exprt> eager_quantifier_instantiation (
141+ const quantifier_exprt &expr,
142+ const namespacet &ns)
143143{
144+ if (expr.variables ().size () > 1 )
145+ {
146+ // Qx,y.P(x,y) is the same as Qx.Qy.P(x,y)
147+ auto new_variables = expr.variables ();
148+ new_variables.pop_back ();
149+ auto new_expression = quantifier_exprt (
150+ expr.id (),
151+ expr.variables ().back (),
152+ quantifier_exprt (expr.id (), new_variables, expr.where ()));
153+ return eager_quantifier_instantiation (new_expression, ns);
154+ }
155+
144156 const symbol_exprt &var_expr = expr.symbol ();
145157
146158 /* *
147159 * We need to rewrite the forall/exists quantifier into
148160 * an OR/AND expr.
149161 **/
150162
151- const exprt re = simplify_expr (expr.where (), ns);
163+ const exprt where_simplified = simplify_expr (expr.where (), ns);
152164
153- if (re .is_true () || re .is_false ())
165+ if (where_simplified .is_true () || where_simplified .is_false ())
154166 {
155- return re ;
167+ return where_simplified ;
156168 }
157169
158- const optionalt<constant_exprt> min_i = get_quantifier_var_min (var_expr, re);
159- const optionalt<constant_exprt> max_i = get_quantifier_var_max (var_expr, re);
170+ if (var_expr.is_boolean ())
171+ {
172+ // Expand in full.
173+ // This grows worst-case exponentially in the quantifier nesting depth.
174+ if (expr.id () == ID_forall)
175+ {
176+ // ∀b.f(b) <===> f(0)∧f(1)
177+ return and_exprt (
178+ expr.instantiate ({false_exprt ()}), expr.instantiate ({true_exprt ()}));
179+ }
180+ else if (expr.id () == ID_exists)
181+ {
182+ // ∃b.f(b) <===> f(0)∨f(1)
183+ return or_exprt (
184+ expr.instantiate ({false_exprt ()}), expr.instantiate ({true_exprt ()}));
185+ }
186+ else
187+ UNREACHABLE;
188+ }
189+
190+ const optionalt<constant_exprt> min_i =
191+ get_quantifier_var_min (var_expr, where_simplified);
192+ const optionalt<constant_exprt> max_i =
193+ get_quantifier_var_max (var_expr, where_simplified);
160194
161195 if (!min_i.has_value () || !max_i.has_value ())
162196 return nullopt ;
163197
164198 mp_integer lb = numeric_cast_v<mp_integer>(min_i.value ());
165199 mp_integer ub = numeric_cast_v<mp_integer>(max_i.value ());
166200
167- if (lb> ub)
201+ if (lb > ub)
168202 return nullopt ;
169203
204+ auto expr_simplified =
205+ quantifier_exprt (expr.id (), expr.variables (), where_simplified);
206+
170207 std::vector<exprt> expr_insts;
171- for (mp_integer i= lb; i<= ub; ++i)
208+ for (mp_integer i = lb; i <= ub; ++i)
172209 {
173- exprt constraint_expr = re;
174- replace_expr (var_expr,
175- from_integer (i, var_expr.type ()),
176- constraint_expr);
210+ exprt constraint_expr =
211+ expr_simplified.instantiate ({from_integer (i, var_expr.type ())});
177212 expr_insts.push_back (constraint_expr);
178213 }
179214
180- if (expr.id ()== ID_forall)
215+ if (expr.id () == ID_forall)
181216 {
182- // maintain the domain constraint if it isn't guaranteed by the
183- // instantiations (for a disjunction the domain constraint is implied by the
184- // instantiations)
185- if (re .id () == ID_and)
217+ // maintain the domain constraint if it isn't guaranteed
218+ // by the instantiations (for a disjunction the domain
219+ // constraint is implied by the instantiations)
220+ if (where_simplified .id () == ID_and)
186221 {
187222 expr_insts.push_back (binary_predicate_exprt (
188223 var_expr, ID_gt, from_integer (lb, var_expr.type ())));
189224 expr_insts.push_back (binary_predicate_exprt (
190225 var_expr, ID_le, from_integer (ub, var_expr.type ())));
191226 }
227+
192228 return simplify_expr (conjunction (expr_insts), ns);
193229 }
194230 else if (expr.id () == ID_exists)
195231 {
196- // maintain the domain constraint if it isn't trivially satisfied by the
197- // instantiations (for a conjunction the instantiations are stronger
198- // constraints)
199- if (re .id () == ID_or)
232+ // maintain the domain constraint if it isn't trivially satisfied
233+ // by the instantiations (for a conjunction the instantiations are
234+ // stronger constraints)
235+ if (where_simplified .id () == ID_or)
200236 {
201237 expr_insts.push_back (binary_predicate_exprt (
202238 var_expr, ID_gt, from_integer (lb, var_expr.type ())));
203239 expr_insts.push_back (binary_predicate_exprt (
204240 var_expr, ID_le, from_integer (ub, var_expr.type ())));
205241 }
242+
206243 return simplify_expr (disjunction (expr_insts), ns);
207244 }
208245
@@ -223,7 +260,7 @@ literalt boolbvt::convert_quantifier(const quantifier_exprt &src)
223260 auto new_src =
224261 quantifier_exprt (src.id (), std::move (fresh_symbols), where_replaced);
225262
226- const auto res = instantiate_quantifier (src, ns);
263+ const auto res = eager_quantifier_instantiation (src, ns);
227264
228265 if (res)
229266 return convert_bool (*res);
0 commit comments