Skip to content

Commit 4993eb9

Browse files
authored
Merge pull request #8964 from diffblue/smt2-reduction-operators
SMT2 backend: reduction operators
2 parents bd29015 + d287c0b commit 4993eb9

2 files changed

Lines changed: 159 additions & 1 deletion

File tree

src/solvers/smt2/smt2_conv.cpp

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2724,6 +2724,67 @@ void smt2_convt::convert_expr(const exprt &expr)
27242724
// use the lowering
27252725
convert_expr(to_cond_expr(expr).lower());
27262726
}
2727+
else if(expr.id() == ID_reduction_and)
2728+
{
2729+
// This is true iff all bits in the operand are true
2730+
auto &op = to_unary_expr(expr).op();
2731+
auto all_ones = to_bitvector_type(op.type()).all_ones_expr();
2732+
convert_expr(equal_exprt{op, all_ones});
2733+
}
2734+
else if(expr.id() == ID_reduction_nand)
2735+
{
2736+
// This is the negation of "reduction and"
2737+
auto &op = to_unary_expr(expr).op();
2738+
convert_expr(not_exprt{unary_predicate_exprt{ID_reduction_and, op}});
2739+
}
2740+
else if(expr.id() == ID_reduction_or)
2741+
{
2742+
// This is true iff the operand is not zero
2743+
auto &op = to_unary_expr(expr).op();
2744+
auto all_zeros = to_bitvector_type(op.type()).all_zeros_expr();
2745+
convert_expr(notequal_exprt{op, all_zeros});
2746+
}
2747+
else if(expr.id() == ID_reduction_nor)
2748+
{
2749+
// This is the negation of "reduction or"
2750+
auto &op = to_unary_expr(expr).op();
2751+
convert_expr(not_exprt{unary_predicate_exprt{ID_reduction_or, op}});
2752+
}
2753+
else if(expr.id() == ID_reduction_xor)
2754+
{
2755+
// This is the parity of the operand. No SMT-LIB 2 equivalent.
2756+
// Do bit-wise. SMT-LIB 3.0 could do this with "fold bvxor".
2757+
auto &op = to_unary_expr(expr).op();
2758+
auto width = to_bitvector_type(op.type()).get_width();
2759+
PRECONDITION(width >= 1);
2760+
2761+
if(width == 1)
2762+
{
2763+
out << "(= ";
2764+
flatten2bv(op);
2765+
out << " #b1)";
2766+
}
2767+
else
2768+
{
2769+
out << "(let ((?rop ";
2770+
flatten2bv(op);
2771+
out << ")) ";
2772+
2773+
// XOR all bits: extract each bit and use multi-ary bvxor
2774+
out << "(= (bvxor";
2775+
for(std::size_t i = 0; i < width; i++)
2776+
out << " ((_ extract " << i << " " << i << ") ?rop)";
2777+
out << ") #b1)";
2778+
2779+
out << ')'; // let
2780+
}
2781+
}
2782+
else if(expr.id() == ID_reduction_xnor)
2783+
{
2784+
// This is the negation of "reduction xor"
2785+
auto &op = to_unary_expr(expr).op();
2786+
convert_expr(not_exprt{unary_predicate_exprt{ID_reduction_xor, op}});
2787+
}
27272788
else
27282789
INVARIANT_WITH_DIAGNOSTICS(
27292790
false,

unit/solvers/smt2/smt2_conv.cpp

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,15 @@
11
// Author: Diffblue Ltd.
22

3-
#include <testing-utils/use_catch.h>
3+
/// \file
4+
/// Unit tests for smt2_convt
5+
6+
#include <util/bitvector_types.h>
7+
#include <util/namespace.h>
8+
#include <util/std_expr.h>
9+
#include <util/symbol_table.h>
410

511
#include <solvers/smt2/smt2_conv.h>
12+
#include <testing-utils/use_catch.h>
613

714
TEST_CASE(
815
"smt2_convt::convert_identifier character escaping.",
@@ -17,3 +24,93 @@ TEST_CASE(
1724
CHECK(smt2_convt::convert_identifier("|") == "|&124;|");
1825
CHECK(smt2_convt::convert_identifier("&") == "&");
1926
}
27+
28+
/// Helper: extract the "(assert ...)" line from SMT2 output of set_to
29+
static std::string get_assert(const exprt &red_expr)
30+
{
31+
symbol_tablet symbol_table;
32+
namespacet ns(symbol_table);
33+
std::ostringstream out;
34+
smt2_convt conv(ns, "test", "", "QF_BV", smt2_convt::solvert::GENERIC, out);
35+
conv.set_to(red_expr, true);
36+
std::string result = out.str();
37+
auto pos = result.find("(assert ");
38+
REQUIRE(pos != std::string::npos);
39+
// strip trailing newline
40+
auto end = result.find_last_not_of('\n');
41+
return result.substr(pos, end - pos + 1);
42+
}
43+
44+
TEST_CASE("smt2_convt reduction operators", "[core][solvers][smt2]")
45+
{
46+
unsignedbv_typet u2(2);
47+
symbol_exprt sym("x", u2);
48+
49+
SECTION("reduction_and")
50+
{
51+
REQUIRE(
52+
get_assert(unary_predicate_exprt{ID_reduction_and, sym}) ==
53+
"(assert (= x (_ bv3 2)))");
54+
}
55+
56+
SECTION("reduction_nand")
57+
{
58+
REQUIRE(
59+
get_assert(unary_predicate_exprt{ID_reduction_nand, sym}) ==
60+
"(assert (not (= x (_ bv3 2))))");
61+
}
62+
63+
SECTION("reduction_or")
64+
{
65+
REQUIRE(
66+
get_assert(unary_predicate_exprt{ID_reduction_or, sym}) ==
67+
"(assert (not (= x (_ bv0 2))))");
68+
}
69+
70+
SECTION("reduction_nor")
71+
{
72+
REQUIRE(
73+
get_assert(unary_predicate_exprt{ID_reduction_nor, sym}) ==
74+
"(assert (not (not (= x (_ bv0 2)))))");
75+
}
76+
77+
SECTION("reduction_xor")
78+
{
79+
REQUIRE(
80+
get_assert(unary_predicate_exprt{ID_reduction_xor, sym}) ==
81+
"(assert (let ((?rop x)) "
82+
"(= (bvxor ((_ extract 0 0) ?rop) ((_ extract 1 1) ?rop)) #b1)))");
83+
}
84+
85+
SECTION("reduction_xnor")
86+
{
87+
REQUIRE(
88+
get_assert(unary_predicate_exprt{ID_reduction_xnor, sym}) ==
89+
"(assert (not (let ((?rop x)) "
90+
"(= (bvxor ((_ extract 0 0) ?rop) ((_ extract 1 1) ?rop)) #b1))))");
91+
}
92+
93+
SECTION("reduction_xor 1-bit")
94+
{
95+
symbol_exprt sym1("y", unsignedbv_typet(1));
96+
REQUIRE(
97+
get_assert(unary_predicate_exprt{ID_reduction_xor, sym1}) ==
98+
"(assert (= y #b1))");
99+
}
100+
101+
SECTION("reduction_and 1-bit")
102+
{
103+
symbol_exprt sym1("y", unsignedbv_typet(1));
104+
REQUIRE(
105+
get_assert(unary_predicate_exprt{ID_reduction_and, sym1}) ==
106+
"(assert (= y (_ bv1 1)))");
107+
}
108+
109+
SECTION("reduction_or 1-bit")
110+
{
111+
symbol_exprt sym1("y", unsignedbv_typet(1));
112+
REQUIRE(
113+
get_assert(unary_predicate_exprt{ID_reduction_or, sym1}) ==
114+
"(assert (not (= y (_ bv0 1))))");
115+
}
116+
}

0 commit comments

Comments
 (0)