CBMC
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages
prop_conv_solver.cpp
Go to the documentation of this file.
1/*******************************************************************\
2
3Module:
4
5Author: Daniel Kroening, kroening@kroening.com
6
7\*******************************************************************/
8
9#include "prop_conv_solver.h"
10
11#include "literal_expr.h"
12
13#include <algorithm>
14#include <chrono> // IWYU pragma: keep
15
17{
19}
20
22{
23 for(const auto &bit : bv)
24 if(!bit.is_constant())
26}
27
32
37
39{
40 // We can only improve Booleans.
41 if(!expr.is_boolean())
42 return expr;
43
44 // We convert to a literal to obtain a 'small' handle
45 literalt l = convert(expr);
46
47 // The literal may be a constant as a result of non-trivial
48 // propagation. We return constants as such.
49 if(l.is_true())
50 return true_exprt();
51 else if(l.is_false())
52 return false_exprt();
53
54 // freeze to enable incremental use
55 set_frozen(l);
56
57 return literal_exprt(l);
58}
59
61{
62 auto result =
63 symbols.insert(std::pair<irep_idt, literalt>(identifier, literalt()));
64
65 if(!result.second)
66 return result.first->second;
67
69 prop.set_variable_name(literal, identifier);
70
71 // insert
72 result.first->second = literal;
73
74 return literal;
75}
76
77std::optional<bool> prop_conv_solvert::get_bool(const exprt &expr) const
78{
79 // trivial cases
80
81 if(expr.is_true())
82 {
83 return true;
84 }
85 else if(expr.is_false())
86 {
87 return false;
88 }
89 else if(expr.id() == ID_symbol)
90 {
91 symbolst::const_iterator result =
92 symbols.find(to_symbol_expr(expr).get_identifier());
93
94 // This may fail if the symbol isn't Boolean or
95 // not in the formula.
96 if(result == symbols.end())
97 return {};
98
99 return prop.l_get(result->second).is_true();
100 }
101 else if(expr.id() == ID_literal)
102 {
103 return prop.l_get(to_literal_expr(expr).get_literal()).is_true();
104 }
105
106 // sub-expressions
107
108 if(expr.id() == ID_not)
109 {
110 if(expr.is_boolean())
111 {
112 auto tmp = get_bool(to_not_expr(expr).op());
113 if(tmp.has_value())
114 return !*tmp;
115 else
116 return {};
117 }
118 }
119 else if(expr.id() == ID_and || expr.id() == ID_or)
120 {
121 if(expr.is_boolean() && expr.operands().size() >= 1)
122 {
123 for(const auto &op : expr.operands())
124 {
125 auto tmp = get_bool(op);
126 if(!tmp.has_value())
127 return {};
128
129 if(expr.id() == ID_and)
130 {
131 if(*tmp == false)
132 return false;
133 }
134 else // or
135 {
136 if(*tmp == true)
137 return true;
138 }
139 }
140
141 return expr.id() == ID_and;
142 }
143 }
144
145 // check cache
146
147 cachet::const_iterator cache_result = cache.find(expr);
148 if(cache_result == cache.end())
149 return {}; // not in formula
150 else
151 return prop.l_get(cache_result->second).is_true();
152}
153
155{
156 if(!use_cache || expr.id() == ID_symbol || expr.is_constant())
157 {
159 if(freeze_all && !literal.is_constant())
161 return literal;
162 }
163
164 // check cache first
165 auto result = cache.insert({expr, literalt()});
166
167 // get a reference to the cache entry
168 auto &cache_entry = result.first->second;
169
170 if(!result.second) // found in cache
171 return cache_entry;
172
173 // The following may invalidate the iterator result.first,
174 // but note that the _reference_ is guaranteed to remain valid.
176
177 // store the literal in the cache using the reference
179
180 if(freeze_all && !literal.is_constant())
182
183#if 0
184 std::cout << literal << "=" << expr << '\n';
185#endif
186
187 return literal;
188}
189
191{
192 PRECONDITION(expr.is_boolean());
193
194 const exprt::operandst &op = expr.operands();
195
196 if(expr.is_constant())
197 {
198 if(expr.is_true())
199 return const_literal(true);
200 else
201 {
202 INVARIANT(
203 expr.is_false(),
204 "constant expression of type bool should be either true or false");
205 return const_literal(false);
206 }
207 }
208 else if(expr.id() == ID_symbol)
209 {
210 return get_literal(to_symbol_expr(expr).get_identifier());
211 }
212 else if(expr.id() == ID_literal)
213 {
214 return to_literal_expr(expr).get_literal();
215 }
216 else if(expr.id() == ID_nondet_symbol)
217 {
218 return prop.new_variable();
219 }
220 else if(expr.id() == ID_implies)
221 {
223 return prop.limplies(
225 }
226 else if(expr.id() == ID_if)
227 {
228 const if_exprt &if_expr = to_if_expr(expr);
229 return prop.lselect(
230 convert(if_expr.cond()),
231 convert(if_expr.true_case()),
232 convert(if_expr.false_case()));
233 }
234 else if(expr.id() == ID_constraint_select_one)
235 {
237 op.size() >= 2,
238 "constraint_select_one should have at least two operands");
239
240 std::vector<literalt> op_bv;
241 op_bv.reserve(op.size());
242
243 for(const auto &op : expr.operands())
244 op_bv.push_back(convert(op));
245
246 // add constraints
247
248 bvt b;
249 b.reserve(op_bv.size() - 1);
250
251 for(unsigned i = 1; i < op_bv.size(); i++)
252 b.push_back(prop.lequal(op_bv[0], op_bv[i]));
253
255
256 return op_bv[0];
257 }
258 else if(
259 expr.id() == ID_or || expr.id() == ID_and || expr.id() == ID_xor ||
260 expr.id() == ID_nor || expr.id() == ID_nand || expr.id() == ID_xnor)
261 {
262 INVARIANT(
263 !op.empty(),
264 "operator '" + expr.id_string() + "' takes at least one operand");
265
266 bvt bv;
267
268 for(const auto &operand : op)
269 bv.push_back(convert(operand));
270
271 CHECK_RETURN(!bv.empty());
272
273 if(expr.id() == ID_or)
274 return prop.lor(bv);
275 else if(expr.id() == ID_nor)
276 return !prop.lor(bv);
277 else if(expr.id() == ID_and)
278 return prop.land(bv);
279 else if(expr.id() == ID_nand)
280 return !prop.land(bv);
281 else if(expr.id() == ID_xor)
282 return prop.lxor(bv);
283 else if(expr.id() == ID_xnor)
284 return !prop.lxor(bv);
285 }
286 else if(expr.id() == ID_not)
287 {
288 INVARIANT(op.size() == 1, "not takes one operand");
289 return !convert(op.front());
290 }
291 else if(expr.id() == ID_equal || expr.id() == ID_notequal)
292 {
293 INVARIANT(op.size() == 2, "equality takes two operands");
294 bool equal = (expr.id() == ID_equal);
295
296 if(op[0].is_boolean() && op[1].is_boolean())
297 {
298 literalt tmp1 = convert(op[0]), tmp2 = convert(op[1]);
299 return equal ? prop.lequal(tmp1, tmp2) : prop.lxor(tmp1, tmp2);
300 }
301 }
302 else if(expr.id() == ID_named_term)
303 {
304 const auto &named_term_expr = to_named_term_expr(expr);
308 return value_converted;
309 }
310
311 return convert_rest(expr);
312}
313
315{
316 // fall through
317 ignoring(expr);
318 return prop.new_variable();
319}
320
322{
324 return true;
325
326 // optimization for constraint of the form
327 // new_variable = value
328
329 if(expr.lhs().id() == ID_symbol)
330 {
331 const irep_idt &identifier = to_symbol_expr(expr.lhs()).get_identifier();
332
333 literalt tmp = convert(expr.rhs());
334
335 std::pair<symbolst::iterator, bool> result =
336 symbols.insert(std::pair<irep_idt, literalt>(identifier, tmp));
337
338 if(result.second)
339 return false; // ok, inserted!
340
341 // nah, already there
342 }
343
344 return true;
345}
346
348{
349 PRECONDITION(expr.is_boolean());
350
351 const bool has_only_boolean_operands = std::all_of(
352 expr.operands().begin(), expr.operands().end(), [](const exprt &expr) {
353 return expr.is_boolean();
354 });
355
357 {
358 if(expr.id() == ID_not)
359 {
360 add_constraints_to_prop(to_not_expr(expr).op(), !value);
361 return;
362 }
363 else
364 {
365 if(value)
366 {
367 // set_to_true
368
369 if(expr.id() == ID_and)
370 {
371 for(const auto &op : expr.operands())
372 add_constraints_to_prop(op, true);
373
374 return;
375 }
376 else if(expr.id() == ID_or)
377 {
378 // Special case for a CNF-clause,
379 // i.e., a constraint that's a disjunction.
380
381 if(!expr.operands().empty())
382 {
383 bvt bv;
384 bv.reserve(expr.operands().size());
385
386 for(const auto &op : expr.operands())
387 bv.push_back(convert(op));
388
389 prop.lcnf(bv);
390 return;
391 }
392 }
393 else if(expr.id() == ID_implies)
394 {
395 const auto &implies_expr = to_implies_expr(expr);
398 prop.lcnf(!l_lhs, l_rhs);
399 return;
400 }
401 else if(expr.id() == ID_equal)
402 {
404 return;
405 }
406 }
407 else
408 {
409 // set_to_false
410 if(expr.id() == ID_implies) // !(a=>b) == (a && !b)
411 {
413
416 return;
417 }
418 else if(expr.id() == ID_or) // !(a || b) == (!a && !b)
419 {
420 for(const auto &op : expr.operands())
421 add_constraints_to_prop(op, false);
422 return;
423 }
424 }
425 }
426 }
427
428 // fall back to convert
429 prop.l_set_to(convert(expr), value);
430}
431
433{
434 // fall through
435
436 log.warning() << "warning: ignoring " << expr.pretty() << messaget::eom;
437}
438
442
445{
446 // post-processing isn't incremental yet
448 {
449 const auto post_process_start = std::chrono::steady_clock::now();
450
451 log.progress() << "Post-processing" << messaget::eom;
454
455 const auto post_process_stop = std::chrono::steady_clock::now();
456 std::chrono::duration<double> post_process_runtime =
457 std::chrono::duration<double>(post_process_stop - post_process_start);
458 log.statistics() << "Runtime Post-process: " << post_process_runtime.count()
459 << "s" << messaget::eom;
460 }
461
462 log.progress() << "Solving with " << prop.solver_text() << messaget::eom;
463
464 if(assumption.is_nil())
465 push();
466 else
467 push({assumption});
468
470
471 pop();
472
473 switch(prop_result)
474 {
480 return resultt::D_ERROR;
481 }
482
484}
485
487{
488 if(expr.is_boolean())
489 {
490 auto value = get_bool(expr);
491
492 if(value.has_value())
493 {
494 if(*value)
495 return true_exprt();
496 else
497 return false_exprt();
498 }
499 }
500
501 exprt tmp = expr;
502 for(auto &op : tmp.operands())
503 {
504 exprt tmp_op = get(op);
505 op.swap(tmp_op);
506 }
507 return tmp;
508}
509
510void prop_conv_solvert::print_assignment(std::ostream &out) const
511{
512 for(const auto &symbol : symbols)
513 out << symbol.first << " = " << prop.l_get(symbol.second) << '\n';
514}
515
520
521const char *prop_conv_solvert::context_prefix = "prop_conv::context$";
522
523void prop_conv_solvert::set_to(const exprt &expr, bool value)
524{
525 if(assumption_stack.empty())
526 {
527 // We are in the root context.
528 add_constraints_to_prop(expr, value);
529 }
530 else
531 {
532 // We have a child context. We add context_literal ==> expr to the formula.
534 or_exprt(literal_exprt(!assumption_stack.back()), expr), value);
535 }
536}
537
538void prop_conv_solvert::push(const std::vector<exprt> &assumptions)
539{
540 // We push the given assumptions as a single context onto the stack.
541 assumption_stack.reserve(assumption_stack.size() + assumptions.size());
542 for(const auto &assumption : assumptions)
543 {
544 auto literal = convert(assumption);
545 if(!literal.is_constant())
547 assumption_stack.push_back(literal);
548 }
549 context_size_stack.push_back(assumptions.size());
550}
551
553{
554 // We create a new context literal.
556 context_prefix + std::to_string(context_literal_counter++), bool_typet()));
557
559 context_size_stack.push_back(1);
560}
561
563{
564 // We remove the context from the stack.
566 context_size_stack.pop_back();
567}
568
569// This method out-of-line because gcc-5.5.0-12ubuntu1 20171010 miscompiles it
570// when inline (in certain build configurations, notably -O2 -g0) by producing
571// a non-virtual thunk with c++03 ABI but a function body with c++11 ABI, the
572// mismatch leading to a missing vtable entry and consequent null-pointer deref
573// whenever this function is called.
575{
576 return "propositional reduction";
577}
ait supplies three of the four components needed: an abstract interpreter (in this case handling func...
Definition ai.h:562
exprt & lhs()
Definition std_expr.h:668
exprt & rhs()
Definition std_expr.h:678
The Boolean type.
Definition std_types.h:36
resultt
Result of running the decision procedure.
void set_to_true(const exprt &)
For a Boolean expression expr, add the constraint 'expr'.
dstringt has one field, an unsigned integer no which is an index into a static table of strings.
Definition dstring.h:38
Equality.
Definition std_expr.h:1366
Base class for all expressions.
Definition expr.h:56
std::vector< exprt > operandst
Definition expr.h:58
bool is_true() const
Return whether the expression is a constant representing true.
Definition expr.cpp:27
bool is_boolean() const
Return whether the expression represents a Boolean.
Definition expr.h:224
bool is_false() const
Return whether the expression is a constant representing false.
Definition expr.cpp:34
bool is_constant() const
Return whether the expression is a constant.
Definition expr.h:212
operandst & operands()
Definition expr.h:94
The Boolean constant false.
Definition std_expr.h:3199
The trinary if-then-else operator.
Definition std_expr.h:2497
Boolean implication.
Definition std_expr.h:2225
std::string pretty(unsigned indent=0, unsigned max_indent=0) const
Definition irep.cpp:482
const std::string & id_string() const
Definition irep.h:391
const irep_idt & id() const
Definition irep.h:388
bool is_nil() const
Definition irep.h:368
bool is_true() const
Definition literal.h:156
bool is_false() const
Definition literal.h:161
mstreamt & statistics() const
Definition message.h:411
mstreamt & warning() const
Definition message.h:396
mstreamt & progress() const
Definition message.h:416
static eomt eom
Definition message.h:289
Boolean OR.
Definition std_expr.h:2270
void pop() override
Pop whatever is on top of the stack.
virtual bool set_equality_to_true(const equal_exprt &expr)
virtual literalt convert_bool(const exprt &expr)
decision_proceduret::resultt dec_solve(const exprt &) override
Implementation of the decision procedure.
void set_frozen(literalt)
std::vector< size_t > context_size_stack
assumption_stack is segmented in contexts; Number of assumptions in each context on the stack
std::size_t context_literal_counter
To generate unique literal names for contexts.
std::size_t get_number_of_solver_calls() const override
Return the number of incremental solver calls.
virtual void finish_eager_conversion()
virtual literalt get_literal(const irep_idt &symbol)
std::string decision_procedure_text() const override
Return a textual description of the decision procedure.
virtual void ignoring(const exprt &expr)
void add_constraints_to_prop(const exprt &expr, bool value)
Helper method used by set_to for adding the constraints to prop.
void push() override
Push a new context on the stack This context becomes a child context nested in the current context.
virtual std::optional< bool > get_bool(const exprt &expr) const
Get a boolean value from the model if the formula is satisfiable.
literalt convert(const exprt &expr) override
Convert a Boolean expression and return the corresponding literal.
exprt get(const exprt &expr) const override
Return expr with variables replaced by values from satisfying assignment if available.
bvt assumption_stack
Assumptions on the stack.
virtual literalt convert_rest(const exprt &expr)
void set_to(const exprt &expr, bool value) override
For a Boolean expression expr, add the constraint 'current_context => expr' if value is true,...
exprt handle(const exprt &expr) override
Generate a handle, which is an expression that has the same value as the argument in any model that i...
static const char * context_prefix
bool is_in_conflict(const exprt &expr) const override
Returns true if an expression is in the final conflict leading to UNSAT.
void print_assignment(std::ostream &out) const override
Print satisfying assignment to out.
void l_set_to_true(literalt a)
Definition prop.h:52
virtual literalt land(literalt a, literalt b)=0
std::size_t get_number_of_solver_calls() const
Definition prop.cpp:52
virtual void set_variable_name(literalt, const irep_idt &)
Definition prop.h:95
virtual literalt limplies(literalt a, literalt b)=0
virtual literalt lselect(literalt a, literalt b, literalt c)=0
virtual void set_frozen(literalt)
Definition prop.h:117
virtual void l_set_to(literalt a, bool value)
Definition prop.h:47
virtual literalt lxor(literalt a, literalt b)=0
virtual std::string solver_text() const =0
void lcnf(literalt l0, literalt l1)
Definition prop.h:58
resultt prop_solve()
Definition prop.cpp:39
virtual literalt new_variable()=0
virtual literalt lequal(literalt a, literalt b)=0
virtual literalt lor(literalt a, literalt b)=0
virtual bool is_in_conflict(literalt l) const =0
Returns true if an assumption is in the final conflict.
virtual tvt l_get(literalt a) const =0
Expression to hold a symbol (variable)
Definition std_expr.h:131
The Boolean constant true.
Definition std_expr.h:3190
std::vector< literalt > bvt
Definition literal.h:201
literalt const_literal(bool value)
Definition literal.h:188
const literal_exprt & to_literal_expr(const exprt &expr)
Cast a generic exprt to a literal_exprt.
#define CHECK_RETURN(CONDITION)
Definition invariant.h:495
#define UNREACHABLE
This should be used to mark dead code.
Definition invariant.h:525
#define DATA_INVARIANT(CONDITION, REASON)
This condition should be used to document that assumptions that are made on goto_functions,...
Definition invariant.h:534
#define PRECONDITION(CONDITION)
Definition invariant.h:463
#define INVARIANT(CONDITION, REASON)
This macro uses the wrapper function 'invariant_violated_string'.
Definition invariant.h:423
const named_term_exprt & to_named_term_expr(const exprt &expr)
Cast an exprt to a named_term_exprt.
Definition std_expr.h:3792
const if_exprt & to_if_expr(const exprt &expr)
Cast an exprt to an if_exprt.
Definition std_expr.h:2577
const not_exprt & to_not_expr(const exprt &expr)
Cast an exprt to an not_exprt.
Definition std_expr.h:2479
const symbol_exprt & to_symbol_expr(const exprt &expr)
Cast an exprt to a symbol_exprt.
Definition std_expr.h:272
const implies_exprt & to_implies_expr(const exprt &expr)
Cast an exprt to a implies_exprt.
Definition std_expr.h:2250
const equal_exprt & to_equal_expr(const exprt &expr)
Cast an exprt to an equal_exprt.
Definition std_expr.h:1407