13
13
#include < util/invariant.h>
14
14
#include < util/optional.h>
15
15
#include < util/range.h>
16
- #include < util/replace_expr.h>
17
16
#include < util/simplify_expr.h>
18
17
19
18
// / 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)
138
137
return {};
139
138
}
140
139
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)
143
143
{
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
+
144
156
const symbol_exprt &var_expr = expr.symbol ();
145
157
146
158
/* *
147
159
* We need to rewrite the forall/exists quantifier into
148
160
* an OR/AND expr.
149
161
**/
150
162
151
- const exprt re = simplify_expr (expr.where (), ns);
163
+ const exprt where_simplified = simplify_expr (expr.where (), ns);
152
164
153
- if (re .is_true () || re .is_false ())
165
+ if (where_simplified .is_true () || where_simplified .is_false ())
154
166
{
155
- return re ;
167
+ return where_simplified ;
156
168
}
157
169
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);
160
194
161
195
if (!min_i.has_value () || !max_i.has_value ())
162
196
return nullopt;
163
197
164
198
mp_integer lb = numeric_cast_v<mp_integer>(min_i.value ());
165
199
mp_integer ub = numeric_cast_v<mp_integer>(max_i.value ());
166
200
167
- if (lb> ub)
201
+ if (lb > ub)
168
202
return nullopt;
169
203
204
+ auto expr_simplified =
205
+ quantifier_exprt (expr.id (), expr.variables (), where_simplified);
206
+
170
207
std::vector<exprt> expr_insts;
171
- for (mp_integer i= lb; i<= ub; ++i)
208
+ for (mp_integer i = lb; i <= ub; ++i)
172
209
{
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 ())});
177
212
expr_insts.push_back (constraint_expr);
178
213
}
179
214
180
- if (expr.id ()== ID_forall)
215
+ if (expr.id () == ID_forall)
181
216
{
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)
186
221
{
187
222
expr_insts.push_back (binary_predicate_exprt (
188
223
var_expr, ID_gt, from_integer (lb, var_expr.type ())));
189
224
expr_insts.push_back (binary_predicate_exprt (
190
225
var_expr, ID_le, from_integer (ub, var_expr.type ())));
191
226
}
227
+
192
228
return simplify_expr (conjunction (expr_insts), ns);
193
229
}
194
230
else if (expr.id () == ID_exists)
195
231
{
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)
200
236
{
201
237
expr_insts.push_back (binary_predicate_exprt (
202
238
var_expr, ID_gt, from_integer (lb, var_expr.type ())));
203
239
expr_insts.push_back (binary_predicate_exprt (
204
240
var_expr, ID_le, from_integer (ub, var_expr.type ())));
205
241
}
242
+
206
243
return simplify_expr (disjunction (expr_insts), ns);
207
244
}
208
245
@@ -223,7 +260,7 @@ literalt boolbvt::convert_quantifier(const quantifier_exprt &src)
223
260
auto new_src =
224
261
quantifier_exprt (src.id (), std::move (fresh_symbols), where_replaced);
225
262
226
- const auto res = instantiate_quantifier (src, ns);
263
+ const auto res = eager_quantifier_instantiation (src, ns);
227
264
228
265
if (res)
229
266
return convert_bool (*res);
0 commit comments