CBMC
Loading...
Searching...
No Matches
boolbv_quantifier.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module:
4
5Author: Daniel Kroening, kroening@kroening.com
6
7\*******************************************************************/
8
9#include <util/arith_tools.h>
11#include <util/expr_util.h>
12#include <util/invariant.h>
13#include <util/simplify_expr.h>
14
15#include "boolbv.h"
16
18static bool expr_eq(const exprt &expr1, const exprt &expr2)
19{
21}
22
26static std::optional<constant_exprt>
28{
29 if(quantifier_expr.id()==ID_or)
30 {
35 for(auto &x : quantifier_expr.operands())
36 {
37 if(x.id()!=ID_not)
38 continue;
39 exprt y = to_not_expr(x).op();
40 if(y.id()!=ID_ge)
41 continue;
42 const auto &y_binary = to_binary_relation_expr(y);
43 if(expr_eq(var_expr, y_binary.lhs()) && y_binary.rhs().is_constant())
44 {
45 return to_constant_expr(y_binary.rhs());
46 }
47 }
48
49 if(var_expr.type().id() == ID_unsignedbv)
50 return from_integer(0, var_expr.type());
51 }
52 else if(quantifier_expr.id() == ID_and)
53 {
54 // The minimum variable can be of the form `var_expr >= constant`, or
55 // it can be of the form `var_expr == constant` (e.g. in the case where
56 // the interval that bounds the variable is a singleton interval (set
57 // with only one element)).
58 for(auto &x : quantifier_expr.operands())
59 {
60 if(x.id() != ID_ge && x.id() != ID_equal)
61 continue;
62 const auto &x_binary = to_binary_relation_expr(x);
63 if(expr_eq(var_expr, x_binary.lhs()) && x_binary.rhs().is_constant())
64 {
65 return to_constant_expr(x_binary.rhs());
66 }
67 }
68
69 if(var_expr.type().id() == ID_unsignedbv)
70 return from_integer(0, var_expr.type());
71 }
72
73 return {};
74}
75
78static std::optional<constant_exprt>
80{
81 if(quantifier_expr.id()==ID_or)
82 {
87 for(auto &x : quantifier_expr.operands())
88 {
89 if(x.id()!=ID_ge)
90 continue;
91 const auto &x_binary = to_binary_relation_expr(x);
92 if(expr_eq(var_expr, x_binary.lhs()) && x_binary.rhs().is_constant())
93 {
95
97
103 over_i-=1;
104 return from_integer(over_i, x_binary.rhs().type());
105 }
106 }
107 }
108 else
109 {
110 // There are two potential forms we could come across here. The first one
111 // is `!(var_expr >= constant)` - identified by the first if branch - and
112 // the second is `var_expr == constant` - identified by the second else-if
113 // branch. The second form could be met if previous simplification has
114 // identified a singleton interval - see simplify_boolean_expr.cpp.
115 for(auto &x : quantifier_expr.operands())
116 {
117 if(x.id() == ID_not)
118 {
119 exprt y = to_not_expr(x).op();
120 if(y.id() != ID_ge)
121 continue;
122 const auto &y_binary = to_binary_relation_expr(y);
123 if(expr_eq(var_expr, y_binary.lhs()) && y_binary.rhs().is_constant())
124 {
127 over_i -= 1;
128 return from_integer(over_i, y_binary.rhs().type());
129 }
130 }
131 else if(x.id() == ID_equal)
132 {
133 const auto &y_binary = to_binary_relation_expr(x);
134 if(expr_eq(var_expr, y_binary.lhs()) && y_binary.rhs().is_constant())
135 {
136 return to_constant_expr(y_binary.rhs());
137 }
138 }
139 else
140 {
141 // If you need special handling for a particular expression type (say,
142 // after changes to the simplifier) you need to make sure that you add
143 // an `else if` branch above, otherwise the expression will get skipped
144 // and the constraints will not propagate correctly.
145 continue;
146 }
147 }
148 }
149
150 return {};
151}
152
153static std::optional<exprt> eager_quantifier_instantiation(
154 const quantifier_exprt &expr,
155 const namespacet &ns)
156{
157 if(expr.variables().size() > 1)
158 {
159 // Qx,y.P(x,y) is the same as Qx.Qy.P(x,y)
160 auto new_variables = expr.variables();
161 new_variables.pop_back();
163 expr.id(),
164 expr.variables().back(),
165 quantifier_exprt(expr.id(), new_variables, expr.where()));
167 }
168
169 const symbol_exprt &var_expr = expr.symbol();
170
176 const exprt where_simplified = simplify_expr(expr.where(), ns);
177
178 if(
179 (where_simplified.is_true() || where_simplified.is_false()) &&
180 (var_expr.type().id() == ID_integer ||
181 var_expr.type().id() == ID_rational || var_expr.type().id() == ID_real ||
182 var_expr.type().id() == ID_bool ||
184 to_bitvector_type(var_expr.type()).get_width() > 0)))
185 {
186 return where_simplified;
187 }
188
189 if(var_expr.is_boolean())
190 {
191 // Expand in full.
192 // This grows worst-case exponentially in the quantifier nesting depth.
193 if(expr.id() == ID_forall)
194 {
195 // ∀b.f(b) <===> f(0)∧f(1)
196 return and_exprt(
197 expr.instantiate({false_exprt()}), expr.instantiate({true_exprt()}));
198 }
199 else if(expr.id() == ID_exists)
200 {
201 // ∃b.f(b) <===> f(0)∨f(1)
202 return or_exprt(
203 expr.instantiate({false_exprt()}), expr.instantiate({true_exprt()}));
204 }
205 else
207 }
208
209 const std::optional<constant_exprt> min_i =
211 const std::optional<constant_exprt> max_i =
213
214 if(!min_i.has_value() || !max_i.has_value())
215 return {};
216
219
220 if(lb > ub)
221 return {};
222
223 auto expr_simplified =
225
226 std::vector<exprt> expr_insts;
227 for(mp_integer i = lb; i <= ub; ++i)
228 {
230 expr_simplified.instantiate({from_integer(i, var_expr.type())});
231 expr_insts.push_back(constraint_expr);
232 }
233
234 if(expr.id() == ID_forall)
235 {
236 // maintain the domain constraint if it isn't guaranteed
237 // by the instantiations (for a disjunction the domain
238 // constraint is implied by the instantiations)
239 if(where_simplified.id() == ID_and)
240 {
242 var_expr, ID_gt, from_integer(lb, var_expr.type())));
244 var_expr, ID_le, from_integer(ub, var_expr.type())));
245 }
246
248 }
249 else if(expr.id() == ID_exists)
250 {
251 // maintain the domain constraint if it isn't trivially satisfied
252 // by the instantiations (for a conjunction the instantiations are
253 // stronger constraints)
254 if(where_simplified.id() == ID_or)
255 {
257 var_expr, ID_gt, from_integer(lb, var_expr.type())));
259 var_expr, ID_le, from_integer(ub, var_expr.type())));
260 }
261
263 }
264
266}
267
269{
270 PRECONDITION(src.id() == ID_forall || src.id() == ID_exists);
271
272 // We first worry about the scoping of the symbols bound by the quantifier.
273 auto fresh_symbols = fresh_binding(src);
274
275 // replace in where()
277
278 // produce new quantifier expression
279 auto new_src =
281
282 const auto res = eager_quantifier_instantiation(src, ns);
283
284 if(res)
285 return convert_bool(*res);
286
287 // we failed to instantiate here, need to pass to post-processing
288 quantifier_list.emplace_back(quantifiert(src, prop.new_variable()));
289
290 return quantifier_list.back().l;
291}
292
294{
295 if(quantifier_list.empty())
296 return;
297
298 // we do not yet have any elaborate post-processing
299 for(const auto &q : quantifier_list)
300 conversion_failed(q.expr);
301}
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
Pre-defined bitvector types.
const bitvector_typet & to_bitvector_type(const typet &type)
Cast a typet to a bitvector_typet.
static bool expr_eq(const exprt &expr1, const exprt &expr2)
A method to detect equivalence between experts that can contain typecast.
static std::optional< constant_exprt > get_quantifier_var_max(const exprt &var_expr, const exprt &quantifier_expr)
To obtain the max value for the quantifier variable of the specified forall/exists operator.
static std::optional< exprt > eager_quantifier_instantiation(const quantifier_exprt &expr, const namespacet &ns)
static std::optional< constant_exprt > get_quantifier_var_min(const exprt &var_expr, const exprt &quantifier_expr)
To obtain the min value for the quantifier variable of the specified forall/exists operator.
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
Boolean AND.
Definition std_expr.h:2125
const namespacet & ns
Definition arrays.h:56
A base class for expressions that are predicates, i.e., Boolean-typed, and that take exactly two argu...
Definition std_expr.h:731
exprt & where()
Definition std_expr.h:3266
variablest & variables()
Definition std_expr.h:3256
exprt instantiate(const exprt::operandst &) const
substitute free occurrences of the variables in where() by the given values
Definition std_expr.cpp:250
binding_exprt::variablest fresh_binding(const binding_exprt &)
create new, unique variables for the given binding
Definition boolbv.cpp:557
void finish_eager_conversion_quantifiers()
virtual literalt convert_quantifier(const quantifier_exprt &expr)
bvt conversion_failed(const exprt &expr)
Print that the expression of x has failed conversion, then return a vector of x's width.
Definition boolbv.cpp:94
quantifier_listt quantifier_list
Definition boolbv.h:275
A constant literal expression.
Definition std_expr.h:3118
Base class for all expressions.
Definition expr.h:56
const irep_idt & id() const
Definition irep.h:388
A namespacet is essentially one or two symbol tables bound together, to allow for symbol lookups in t...
Definition namespace.h:91
Boolean OR.
Definition std_expr.h:2275
virtual literalt convert_bool(const exprt &expr)
virtual literalt new_variable()=0
A base class for quantifier expressions.
symbol_exprt & symbol()
Expression to hold a symbol (variable)
Definition std_expr.h:131
const exprt & skip_typecast(const exprt &expr)
find the expression nested inside typecasts, if any
Deprecated expression utility functions.
exprt simplify_expr(exprt src, const namespacet &ns)
BigInt mp_integer
Definition smt_terms.h:17
#define UNREACHABLE
This should be used to mark dead code.
Definition invariant.h:525
#define PRECONDITION(CONDITION)
Definition invariant.h:463
exprt disjunction(const exprt::operandst &op)
1) generates a disjunction for two or more operands 2) for one operand, returns the operand 3) return...
Definition std_expr.cpp:71
exprt conjunction(exprt a, exprt b)
Conjunction of two expressions.
Definition std_expr.cpp:83
const binary_relation_exprt & to_binary_relation_expr(const exprt &expr)
Cast an exprt to a binary_relation_exprt.
Definition std_expr.h:895
const constant_exprt & to_constant_expr(const exprt &expr)
Cast an exprt to a constant_exprt.
Definition std_expr.h:3173
const not_exprt & to_not_expr(const exprt &expr)
Cast an exprt to an not_exprt.
Definition std_expr.h:2484