Skip to content

Commit 9b72a5c

Browse files
authored
Merge pull request #6550 from diffblue/quantifiers
Quantifiers
2 parents 8bf2480 + e4c5999 commit 9b72a5c

File tree

7 files changed

+105
-24
lines changed

7 files changed

+105
-24
lines changed
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
CORE
2+
boolean-quantifier.smt2
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^unsat$
7+
--
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
(define-fun q1 () Bool (exists ((b Bool)) b))
2+
(define-fun q2 () Bool (exists ((b Bool)) (not b)))
3+
4+
(define-fun q3 () Bool (not (forall ((b Bool)) b)))
5+
(define-fun q4 () Bool (not (forall ((b Bool)) (not b))))
6+
7+
(define-fun q5 () Bool (exists ((a Bool) (b Bool)) (= a b)))
8+
(define-fun q6 () Bool (not (forall ((a Bool) (b Bool)) (= a b))))
9+
10+
(define-fun q7 () Bool (forall ((a Bool)) (exists ((b Bool)) (= a b))))
11+
12+
; the above are all valid, and we assert one of them is not
13+
(assert (not (and q1 q2 q3 q4 q5 q6 q7)))
14+
15+
(check-sat)
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
KNOWNBUG
2+
bv-quantifier-valid.smt2
3+
4+
^EXIT=0$
5+
^SIGNAL=0$
6+
^unsat$
7+
--
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
(define-fun q1 () Bool (exists ((b (_ BitVec 8))) (= b #x00)))
2+
(define-fun q2 () Bool (exists ((b (_ BitVec 8))) (not (= b #x00))))
3+
4+
(define-fun q3 () Bool (not (forall ((b (_ BitVec 8))) (= b #x00))))
5+
(define-fun q4 () Bool (not (forall ((b (_ BitVec 8))) (not (= b #x00)))))
6+
7+
(define-fun q5 () Bool (exists ((a (_ BitVec 8)) (b (_ BitVec 8))) (= a b)))
8+
(define-fun q6 () Bool (not (forall ((a (_ BitVec 8)) (b (_ BitVec 8))) (= a b))))
9+
10+
(define-fun q7 () Bool (forall ((a (_ BitVec 8))) (exists ((b (_ BitVec 8))) (= a b))))
11+
12+
; the above are all valid, and we assert one of them is not
13+
(assert (not (and q1 q2 q3 q4 q5 q6 q7)))
14+
15+
(check-sat)

src/solvers/flattening/boolbv_quantifier.cpp

Lines changed: 61 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ Author: Daniel Kroening, [email protected]
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

Comments
 (0)