Skip to content

SMT2: support onehot and onehot0 #8524

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Dec 3, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions src/solvers/flattening/boolbv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -412,8 +412,10 @@
expr.id()==ID_reduction_nor || expr.id()==ID_reduction_nand ||
expr.id()==ID_reduction_xor || expr.id()==ID_reduction_xnor)
return convert_reduction(to_unary_expr(expr));
else if(expr.id()==ID_onehot || expr.id()==ID_onehot0)
return convert_onehot(to_unary_expr(expr));
else if(expr.id() == ID_onehot)
return convert_onehot(to_onehot_expr(expr));

Check warning on line 416 in src/solvers/flattening/boolbv.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv.cpp#L416

Added line #L416 was not covered by tests
else if(expr.id() == ID_onehot0)
return convert_onehot(to_onehot0_expr(expr));

Check warning on line 418 in src/solvers/flattening/boolbv.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/flattening/boolbv.cpp#L418

Added line #L418 was not covered by tests
else if(
const auto binary_overflow =
expr_try_dynamic_cast<binary_overflow_exprt>(expr))
Expand Down
8 changes: 8 additions & 0 deletions src/solvers/smt2/smt2_conv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1831,6 +1831,14 @@
out << ")) #b1)"; // bvlshr, extract, =
}
}
else if(expr.id() == ID_onehot)
{
convert_expr(to_onehot_expr(expr).lower());

Check warning on line 1836 in src/solvers/smt2/smt2_conv.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/smt2/smt2_conv.cpp#L1836

Added line #L1836 was not covered by tests
}
Comment on lines +1834 to +1837
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about ID_onehot0?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added, thank you

else if(expr.id() == ID_onehot0)
{
convert_expr(to_onehot0_expr(expr).lower());

Check warning on line 1840 in src/solvers/smt2/smt2_conv.cpp

View check run for this annotation

Codecov / codecov/patch

src/solvers/smt2/smt2_conv.cpp#L1839-L1840

Added lines #L1839 - L1840 were not covered by tests
}
else if(expr.id()==ID_extractbits)
{
const extractbits_exprt &extractbits_expr = to_extractbits_expr(expr);
Expand Down
34 changes: 34 additions & 0 deletions src/util/bitvector_expr.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -306,3 +306,37 @@
return extractbits_exprt{op(), 0, type()};
}
}

static exprt onehot_lowering(const exprt &expr)
{
exprt one_seen = false_exprt{};
const auto width = to_bitvector_type(expr.type()).get_width();
exprt::operandst more_than_one_seen_disjuncts;
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

May I suggest more_than_one_seen_disjuncts.reserve(width);?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

more_than_one_seen_disjuncts.reserve(width);

Check warning on line 316 in src/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.cpp#L316

Added line #L316 was not covered by tests
for(std::size_t i = 0; i < width; i++)
{

Check warning on line 318 in src/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.cpp#L318

Added line #L318 was not covered by tests
auto bit = extractbit_exprt{expr, i};
more_than_one_seen_disjuncts.push_back(and_exprt{bit, one_seen});
one_seen = or_exprt{one_seen, bit};
}

Check warning on line 323 in src/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.cpp#L323

Added line #L323 was not covered by tests
auto more_than_one_seen = disjunction(more_than_one_seen_disjuncts);

Check warning on line 325 in src/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.cpp#L325

Added line #L325 was not covered by tests
return and_exprt{one_seen, not_exprt{more_than_one_seen}};
}

Check warning on line 328 in src/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.cpp#L328

Added line #L328 was not covered by tests
exprt onehot_exprt::lower() const
{
auto symbol = symbol_exprt{"onehot-op", op().type()};

return let_exprt{symbol, op(), onehot_lowering(symbol)};
}

exprt onehot0_exprt::lower() const
{
auto symbol = symbol_exprt{"onehot-op", op().type()};

Check warning on line 339 in src/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.cpp#L339

Added line #L339 was not covered by tests
// same as onehot, but on flipped operand bits
return let_exprt{symbol, bitnot_exprt{op()}, onehot_lowering(symbol)};
}
70 changes: 70 additions & 0 deletions src/util/bitvector_expr.h
Original file line number Diff line number Diff line change
Expand Up @@ -1742,4 +1742,74 @@
return static_cast<zero_extend_exprt &>(expr);
}

/// \brief A Boolean expression returning true iff the given
/// operand consists of exactly one '1' and '0' otherwise.
class onehot_exprt : public unary_predicate_exprt
{
public:
explicit onehot_exprt(exprt _op)
: unary_predicate_exprt(ID_onehot, std::move(_op))
{
}

/// lowering to extractbit
exprt lower() const;
};

/// \brief Cast an exprt to a \ref onehot_exprt
///
/// \a expr must be known to be \ref onehot_exprt.
///
/// \param expr: Source expression
/// \return Object of type \ref onehot_exprt
inline const onehot_exprt &to_onehot_expr(const exprt &expr)

Check warning on line 1765 in src/util/bitvector_expr.h

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.h#L1765

Added line #L1765 was not covered by tests
{
PRECONDITION(expr.id() == ID_onehot);
onehot_exprt::check(expr);
return static_cast<const onehot_exprt &>(expr);

Check warning on line 1769 in src/util/bitvector_expr.h

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.h#L1767-L1769

Added lines #L1767 - L1769 were not covered by tests
}

/// \copydoc to_onehot_expr(const exprt &)
inline onehot_exprt &to_onehot_expr(exprt &expr)
{
PRECONDITION(expr.id() == ID_onehot);
onehot_exprt::check(expr);
return static_cast<onehot_exprt &>(expr);
}

/// \brief A Boolean expression returning true iff the given
/// operand consists of exactly one '0' and '1' otherwise.
class onehot0_exprt : public unary_predicate_exprt
{
public:
explicit onehot0_exprt(exprt _op)
: unary_predicate_exprt(ID_onehot0, std::move(_op))
{
}

/// lowering to extractbit
exprt lower() const;
};

/// \brief Cast an exprt to a \ref onehot0_exprt
///
/// \a expr must be known to be \ref onehot0_exprt.
///
/// \param expr: Source expression
/// \return Object of type \ref onehot0_exprt
inline const onehot0_exprt &to_onehot0_expr(const exprt &expr)

Check warning on line 1800 in src/util/bitvector_expr.h

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.h#L1800

Added line #L1800 was not covered by tests
{
PRECONDITION(expr.id() == ID_onehot0);
onehot0_exprt::check(expr);
return static_cast<const onehot0_exprt &>(expr);

Check warning on line 1804 in src/util/bitvector_expr.h

View check run for this annotation

Codecov / codecov/patch

src/util/bitvector_expr.h#L1802-L1804

Added lines #L1802 - L1804 were not covered by tests
}

/// \copydoc to_onehot0_expr(const exprt &)
inline onehot0_exprt &to_onehot0_expr(exprt &expr)
{
PRECONDITION(expr.id() == ID_onehot0);
onehot0_exprt::check(expr);
return static_cast<onehot0_exprt &>(expr);
}

#endif // CPROVER_UTIL_BITVECTOR_EXPR_H
78 changes: 78 additions & 0 deletions unit/util/bitvector_expr.cpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,15 @@
// Author: Diffblue Ltd.

#include <util/arith_tools.h>
#include <util/bitvector_expr.h>
#include <util/bitvector_types.h>
#include <util/cout_message.h>
#include <util/namespace.h>
#include <util/std_expr.h>

Check warning on line 8 in unit/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

unit/util/bitvector_expr.cpp#L8

Added line #L8 was not covered by tests
#include <util/symbol_table.h>

#include <solvers/flattening/boolbv.h>
#include <solvers/sat/satcheck.h>

Check warning on line 12 in unit/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

unit/util/bitvector_expr.cpp#L12

Added line #L12 was not covered by tests
#include <testing-utils/use_catch.h>

TEST_CASE(
Expand Down Expand Up @@ -64,3 +71,74 @@
}
}
}

TEST_CASE("onehot expression lowering", "[core][util][expr]")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for adding this test; but could onehot0 please also be included?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

{
console_message_handlert message_handler;
message_handler.set_verbosity(0);
satcheckt satcheck{message_handler};
symbol_tablet symbol_table;
namespacet ns{symbol_table};
boolbvt boolbv{ns, satcheck, message_handler};
unsignedbv_typet u8{8};

GIVEN("A bit-vector that is one-hot")
{
boolbv << onehot_exprt{from_integer(64, u8)}.lower();

THEN("the lowering of onehot is true")
{
REQUIRE(boolbv() == decision_proceduret::resultt::D_SATISFIABLE);
}
}

GIVEN("A bit-vector that is not one-hot")
{
boolbv << onehot_exprt{from_integer(5, u8)}.lower();

THEN("the lowering of onehot is false")
{
REQUIRE(boolbv() == decision_proceduret::resultt::D_UNSATISFIABLE);
}
}

Check warning on line 104 in unit/util/bitvector_expr.cpp

View check run for this annotation

Codecov / codecov/patch

unit/util/bitvector_expr.cpp#L104

Added line #L104 was not covered by tests
GIVEN("A bit-vector that is not one-hot")
{
boolbv << onehot_exprt{from_integer(0, u8)}.lower();

THEN("the lowering of onehot is false")
{
REQUIRE(boolbv() == decision_proceduret::resultt::D_UNSATISFIABLE);
}
}

GIVEN("A bit-vector that is one-hot 0")
{
boolbv << onehot0_exprt{from_integer(0xfe, u8)}.lower();

THEN("the lowering of onehot0 is true")
{
REQUIRE(boolbv() == decision_proceduret::resultt::D_SATISFIABLE);
}
}

GIVEN("A bit-vector that is not one-hot 0")
{
boolbv << onehot0_exprt{from_integer(0x7e, u8)}.lower();

THEN("the lowering of onehot0 is false")
{
REQUIRE(boolbv() == decision_proceduret::resultt::D_UNSATISFIABLE);
}
}

GIVEN("A bit-vector that is not one-hot 0")
{
boolbv << onehot0_exprt{from_integer(0xff, u8)}.lower();

THEN("the lowering of onehot0 is false")
{
REQUIRE(boolbv() == decision_proceduret::resultt::D_UNSATISFIABLE);
}
}
}
2 changes: 2 additions & 0 deletions unit/util/module_dependencies.txt
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
testing-utils
util
solvers/flattening
solvers/sat
Loading