Skip to content

Commit 6a8e038

Browse files
committed
Simplify quantified expressions over constants
Other simplification rules may turn the where-clause of a quantified expression into a (Boolean) constant. We can completely remove such quantified expressions so as not to distract back-ends that may special-case quantified expressions. We saw such expressions when working on mlkem-native.
1 parent 3c915eb commit 6a8e038

File tree

5 files changed

+72
-3
lines changed

5 files changed

+72
-3
lines changed

src/solvers/flattening/boolbv_quantifier.cpp

+10-3
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,14 @@ Author: Daniel Kroening, [email protected]
66
77
\*******************************************************************/
88

9-
#include "boolbv.h"
10-
119
#include <util/arith_tools.h>
10+
#include <util/bitvector_types.h>
1211
#include <util/expr_util.h>
1312
#include <util/invariant.h>
1413
#include <util/simplify_expr.h>
1514

15+
#include "boolbv.h"
16+
1617
/// A method to detect equivalence between experts that can contain typecast
1718
static bool expr_eq(const exprt &expr1, const exprt &expr2)
1819
{
@@ -174,7 +175,13 @@ static std::optional<exprt> eager_quantifier_instantiation(
174175

175176
const exprt where_simplified = simplify_expr(expr.where(), ns);
176177

177-
if(where_simplified.is_true() || where_simplified.is_false())
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 ||
183+
(can_cast_type<bitvector_typet>(var_expr.type()) &&
184+
to_bitvector_type(var_expr.type()).get_width() > 0)))
178185
{
179186
return where_simplified;
180187
}

src/util/simplify_expr.cpp

+4
Original file line numberDiff line numberDiff line change
@@ -3128,6 +3128,10 @@ simplify_exprt::resultt<> simplify_exprt::simplify_node(const exprt &node)
31283128
{
31293129
r = simplify_prophecy_pointer_in_range(*prophecy_pointer_in_range);
31303130
}
3131+
else if(expr.id() == ID_exists || expr.id() == ID_forall)
3132+
{
3133+
r = simplify_quantifier_expr(to_quantifier_expr(expr));
3134+
}
31313135

31323136
if(!no_change_join_operands)
31333137
r = changed(r);

src/util/simplify_expr_boolean.cpp

+30
Original file line numberDiff line numberDiff line change
@@ -377,3 +377,33 @@ simplify_exprt::resultt<> simplify_exprt::simplify_not(const not_exprt &expr)
377377

378378
return unchanged(expr);
379379
}
380+
381+
simplify_exprt::resultt<>
382+
simplify_exprt::simplify_quantifier_expr(const quantifier_exprt &expr)
383+
{
384+
const exprt &where = expr.where();
385+
386+
if(!expr.is_boolean() || !where.is_boolean())
387+
{
388+
return unchanged(expr);
389+
}
390+
391+
// the following simplification only holds when the domain is non-empty
392+
if(
393+
(where.is_false() || where.is_true()) &&
394+
std::all_of(
395+
expr.variables().begin(),
396+
expr.variables().end(),
397+
[](const symbol_exprt &v)
398+
{
399+
return v.type().id() == ID_integer || v.type().id() == ID_rational ||
400+
v.type().id() == ID_real || v.type().id() == ID_bool ||
401+
(can_cast_type<bitvector_typet>(v.type()) &&
402+
to_bitvector_type(v.type()).get_width() > 0);
403+
}))
404+
{
405+
return where;
406+
}
407+
408+
return unchanged(expr);
409+
}

src/util/simplify_expr_class.h

+4
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ class popcount_exprt;
6868
class power_exprt;
6969
class prophecy_pointer_in_range_exprt;
7070
class prophecy_r_or_w_ok_exprt;
71+
class quantifier_exprt;
7172
class refined_string_exprt;
7273
class shift_exprt;
7374
class sign_exprt;
@@ -254,6 +255,9 @@ class simplify_exprt
254255
[[nodiscard]] resultt<>
255256
simplify_prophecy_pointer_in_range(const prophecy_pointer_in_range_exprt &);
256257

258+
/// Try to simplify exists/forall to a constant expression.
259+
[[nodiscard]] resultt<> simplify_quantifier_expr(const quantifier_exprt &);
260+
257261
// auxiliary
258262
bool simplify_if_implies(
259263
exprt &expr, const exprt &cond, bool truth, bool &new_truth);

unit/util/simplify_expr.cpp

+24
Original file line numberDiff line numberDiff line change
@@ -586,3 +586,27 @@ TEST_CASE("Simplify power", "[core][util]")
586586
simplify_expr(power_exprt{a, from_integer(1, integer_typet{})}, ns) == a);
587587
}
588588
}
589+
590+
TEST_CASE("Simplify quantifier", "[core][util]")
591+
{
592+
const symbol_tablet symbol_table;
593+
const namespacet ns{symbol_table};
594+
595+
SECTION("Simplification for exists")
596+
{
597+
symbol_exprt a{"a", integer_typet{}};
598+
599+
REQUIRE(simplify_expr(exists_exprt{a, false_exprt{}}, ns) == false_exprt{});
600+
601+
REQUIRE(simplify_expr(exists_exprt{a, true_exprt{}}, ns) == true_exprt{});
602+
}
603+
604+
SECTION("Simplification for forall")
605+
{
606+
symbol_exprt a{"a", integer_typet{}};
607+
608+
REQUIRE(simplify_expr(forall_exprt{a, false_exprt{}}, ns) == false_exprt{});
609+
610+
REQUIRE(simplify_expr(forall_exprt{a, true_exprt{}}, ns) == true_exprt{});
611+
}
612+
}

0 commit comments

Comments
 (0)