CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
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 "boolbv.h"
10
11#include <util/arith_tools.h>
12#include <util/expr_util.h>
13#include <util/invariant.h>
14#include <util/simplify_expr.h>
15
17static bool expr_eq(const exprt &expr1, const exprt &expr2)
18{
20}
21
25static std::optional<constant_exprt>
27{
28 if(quantifier_expr.id()==ID_or)
29 {
34 for(auto &x : quantifier_expr.operands())
35 {
36 if(x.id()!=ID_not)
37 continue;
38 exprt y = to_not_expr(x).op();
39 if(y.id()!=ID_ge)
40 continue;
41 const auto &y_binary = to_binary_relation_expr(y);
42 if(expr_eq(var_expr, y_binary.lhs()) && y_binary.rhs().is_constant())
43 {
44 return to_constant_expr(y_binary.rhs());
45 }
46 }
47
48 if(var_expr.type().id() == ID_unsignedbv)
49 return from_integer(0, var_expr.type());
50 }
51 else if(quantifier_expr.id() == ID_and)
52 {
53 // The minimum variable can be of the form `var_expr >= constant`, or
54 // it can be of the form `var_expr == constant` (e.g. in the case where
55 // the interval that bounds the variable is a singleton interval (set
56 // with only one element)).
57 for(auto &x : quantifier_expr.operands())
58 {
59 if(x.id() != ID_ge && x.id() != ID_equal)
60 continue;
61 const auto &x_binary = to_binary_relation_expr(x);
62 if(expr_eq(var_expr, x_binary.lhs()) && x_binary.rhs().is_constant())
63 {
64 return to_constant_expr(x_binary.rhs());
65 }
66 }
67
68 if(var_expr.type().id() == ID_unsignedbv)
69 return from_integer(0, var_expr.type());
70 }
71
72 return {};
73}
74
77static std::optional<constant_exprt>
79{
80 if(quantifier_expr.id()==ID_or)
81 {
86 for(auto &x : quantifier_expr.operands())
87 {
88 if(x.id()!=ID_ge)
89 continue;
90 const auto &x_binary = to_binary_relation_expr(x);
91 if(expr_eq(var_expr, x_binary.lhs()) && x_binary.rhs().is_constant())
92 {
94
96
102 over_i-=1;
103 return from_integer(over_i, x_binary.rhs().type());
104 }
105 }
106 }
107 else
108 {
109 // There are two potential forms we could come across here. The first one
110 // is `!(var_expr >= constant)` - identified by the first if branch - and
111 // the second is `var_expr == constant` - identified by the second else-if
112 // branch. The second form could be met if previous simplification has
113 // identified a singleton interval - see simplify_boolean_expr.cpp.
114 for(auto &x : quantifier_expr.operands())
115 {
116 if(x.id() == ID_not)
117 {
118 exprt y = to_not_expr(x).op();
119 if(y.id() != ID_ge)
120 continue;
121 const auto &y_binary = to_binary_relation_expr(y);
122 if(expr_eq(var_expr, y_binary.lhs()) && y_binary.rhs().is_constant())
123 {
126 over_i -= 1;
127 return from_integer(over_i, y_binary.rhs().type());
128 }
129 }
130 else if(x.id() == ID_equal)
131 {
132 const auto &y_binary = to_binary_relation_expr(x);
133 if(expr_eq(var_expr, y_binary.lhs()) && y_binary.rhs().is_constant())
134 {
135 return to_constant_expr(y_binary.rhs());
136 }
137 }
138 else
139 {
140 // If you need special handling for a particular expression type (say,
141 // after changes to the simplifier) you need to make sure that you add
142 // an `else if` branch above, otherwise the expression will get skipped
143 // and the constraints will not propagate correctly.
144 continue;
145 }
146 }
147 }
148
149 return {};
150}
151
152static std::optional<exprt> eager_quantifier_instantiation(
153 const quantifier_exprt &expr,
154 const namespacet &ns)
155{
156 if(expr.variables().size() > 1)
157 {
158 // Qx,y.P(x,y) is the same as Qx.Qy.P(x,y)
159 auto new_variables = expr.variables();
160 new_variables.pop_back();
162 expr.id(),
163 expr.variables().back(),
164 quantifier_exprt(expr.id(), new_variables, expr.where()));
166 }
167
168 const symbol_exprt &var_expr = expr.symbol();
169
175 const exprt where_simplified = simplify_expr(expr.where(), ns);
176
177 if(where_simplified.is_true() || where_simplified.is_false())
178 {
179 return where_simplified;
180 }
181
182 if(var_expr.is_boolean())
183 {
184 // Expand in full.
185 // This grows worst-case exponentially in the quantifier nesting depth.
186 if(expr.id() == ID_forall)
187 {
188 // ∀b.f(b) <===> f(0)∧f(1)
189 return and_exprt(
190 expr.instantiate({false_exprt()}), expr.instantiate({true_exprt()}));
191 }
192 else if(expr.id() == ID_exists)
193 {
194 // ∃b.f(b) <===> f(0)∨f(1)
195 return or_exprt(
196 expr.instantiate({false_exprt()}), expr.instantiate({true_exprt()}));
197 }
198 else
200 }
201
202 const std::optional<constant_exprt> min_i =
204 const std::optional<constant_exprt> max_i =
206
207 if(!min_i.has_value() || !max_i.has_value())
208 return {};
209
212
213 if(lb > ub)
214 return {};
215
216 auto expr_simplified =
218
219 std::vector<exprt> expr_insts;
220 for(mp_integer i = lb; i <= ub; ++i)
221 {
223 expr_simplified.instantiate({from_integer(i, var_expr.type())});
224 expr_insts.push_back(constraint_expr);
225 }
226
227 if(expr.id() == ID_forall)
228 {
229 // maintain the domain constraint if it isn't guaranteed
230 // by the instantiations (for a disjunction the domain
231 // constraint is implied by the instantiations)
232 if(where_simplified.id() == ID_and)
233 {
235 var_expr, ID_gt, from_integer(lb, var_expr.type())));
237 var_expr, ID_le, from_integer(ub, var_expr.type())));
238 }
239
241 }
242 else if(expr.id() == ID_exists)
243 {
244 // maintain the domain constraint if it isn't trivially satisfied
245 // by the instantiations (for a conjunction the instantiations are
246 // stronger constraints)
247 if(where_simplified.id() == ID_or)
248 {
250 var_expr, ID_gt, from_integer(lb, var_expr.type())));
252 var_expr, ID_le, from_integer(ub, var_expr.type())));
253 }
254
256 }
257
259}
260
262{
263 PRECONDITION(src.id() == ID_forall || src.id() == ID_exists);
264
265 // We first worry about the scoping of the symbols bound by the quantifier.
266 auto fresh_symbols = fresh_binding(src);
267
268 // replace in where()
270
271 // produce new quantifier expression
272 auto new_src =
274
275 const auto res = eager_quantifier_instantiation(src, ns);
276
277 if(res)
278 return convert_bool(*res);
279
280 // we failed to instantiate here, need to pass to post-processing
281 quantifier_list.emplace_back(quantifiert(src, prop.new_variable()));
282
283 return quantifier_list.back().l;
284}
285
287{
288 if(quantifier_list.empty())
289 return;
290
291 // we do not yet have any elaborate post-processing
292 for(const auto &q : quantifier_list)
293 conversion_failed(q.expr);
294}
constant_exprt from_integer(const mp_integer &int_value, const typet &type)
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:3265
variablest & variables()
Definition std_expr.h:3255
exprt instantiate(const exprt::operandst &) const
substitute free occurrences of the variables in where() by the given values
Definition std_expr.cpp:225
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:3117
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:2270
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 conjunction(const exprt::operandst &op)
1) generates a conjunction for two or more operands 2) for one operand, returns the operand 3) return...
Definition std_expr.cpp:83
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
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:3172
const not_exprt & to_not_expr(const exprt &expr)
Cast an exprt to an not_exprt.
Definition std_expr.h:2479