Skip to content
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

decision_proceduret API with assumptions #7979

Merged
merged 2 commits into from
Dec 19, 2023
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
1 change: 0 additions & 1 deletion regression/cbmc/hex_trace/test.desc
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ main.c
^EXIT=10$
^SIGNAL=0$
a=0 \s*\(0x0\)
b=0ul? \s*\(0x0\)
a=-100 \s*\(0xFFFFFF9C\)
a=2147483647 \s*\(0x7FFFFFFF\)
b=4294967294ul? \s*\(0xFFFFFFFE\)
Expand Down
10 changes: 9 additions & 1 deletion src/solvers/decision_procedure.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,21 @@ Author: Daniel Kroening, [email protected]

#include "decision_procedure.h"

#include <util/std_expr.h>

decision_proceduret::~decision_proceduret()
{
}

decision_proceduret::resultt decision_proceduret::operator()()
{
return dec_solve();
return dec_solve(nil_exprt());
}
Copy link
Collaborator

Choose a reason for hiding this comment

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

My preference would be to use std::optional at the interface rather than nil.

Copy link
Member Author

Choose a reason for hiding this comment

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

I was reluctant to do so since that can't avoid copying the expression.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Wouldn't const std::optional<exprt> & do the trick?

Copy link
Member Author

Choose a reason for hiding this comment

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

No, you'd still need to copy the expression into the optional when creating the optional.

Copy link
Member Author

Choose a reason for hiding this comment

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

std::optional< std::reference_wrapper<exprt> > would work, but then it's getting more verbose.


decision_proceduret::resultt
decision_proceduret::operator()(const exprt &assumption)
{
return dec_solve(assumption);
}

void decision_proceduret::set_to_true(const exprt &expr)
Expand Down
21 changes: 14 additions & 7 deletions src/solvers/decision_procedure.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,18 +17,19 @@ Author: Daniel Kroening, [email protected]

class exprt;

/// An interface for a decision procedure for satisfiability problems.
class decision_proceduret
{
public:
/// For a Boolean expression \p expr, add the constraint 'expr' if \p value
/// is `true`, otherwise add 'not expr'
virtual void set_to(const exprt &expr, bool value) = 0;
virtual void set_to(const exprt &, bool value) = 0;

/// For a Boolean expression \p expr, add the constraint 'expr'
void set_to_true(const exprt &expr);
void set_to_true(const exprt &);

/// For a Boolean expression \p expr, add the constraint 'not expr'
void set_to_false(const exprt &expr);
void set_to_false(const exprt &);

/// Generate a handle, which is an expression that
/// has the same value as the argument in any model
Expand All @@ -37,7 +38,7 @@ class decision_proceduret
/// \ref set_to.
/// The returned expression may be the expression itself or a more compact
/// but solver-specific representation.
virtual exprt handle(const exprt &expr) = 0;
virtual exprt handle(const exprt &) = 0;

/// Result of running the decision procedure
enum class resultt
Expand All @@ -48,12 +49,18 @@ class decision_proceduret
};

/// Run the decision procedure to solve the problem
/// This corresponds to SMT-LIB's check-sat.
resultt operator()();

/// Run the decision procedure to solve the problem under
/// the given assumption.
/// This corresponds to SMT-LIB's check-sat-assuming.
resultt operator()(const exprt &assumption);

/// Return \p expr with variables replaced by values from satisfying
/// assignment if available.
/// Return `nil` if not available
virtual exprt get(const exprt &expr) const = 0;
virtual exprt get(const exprt &) const = 0;

/// Print satisfying assignment to \p out
virtual void print_assignment(std::ostream &out) const = 0;
Expand All @@ -67,8 +74,8 @@ class decision_proceduret
virtual ~decision_proceduret();

protected:
/// Run the decision procedure to solve the problem
virtual resultt dec_solve() = 0;
/// Implementation of the decision procedure.
virtual resultt dec_solve(const exprt &assumption) = 0;
};

/// Add Boolean constraint \p src to decision procedure \p dest
Expand Down
14 changes: 12 additions & 2 deletions src/solvers/prop/prop_conv_solver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -439,7 +439,8 @@ void prop_conv_solvert::finish_eager_conversion()
{
}

decision_proceduret::resultt prop_conv_solvert::dec_solve()
decision_proceduret::resultt
prop_conv_solvert::dec_solve(const exprt &assumption)
{
// post-processing isn't incremental yet
if(!post_processing_done)
Expand All @@ -459,7 +460,16 @@ decision_proceduret::resultt prop_conv_solvert::dec_solve()

log.statistics() << "Solving with " << prop.solver_text() << messaget::eom;

switch(prop.prop_solve(assumption_stack))
if(assumption.is_nil())
push();
else
push({literal_exprt(convert(assumption))});

auto prop_result = prop.prop_solve(assumption_stack);

pop();

switch(prop_result)
{
case propt::resultt::P_SATISFIABLE:
return resultt::D_SATISFIABLE;
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/prop/prop_conv_solver.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class prop_conv_solvert : public conflict_providert,
virtual void finish_eager_conversion();

// overloading from decision_proceduret
decision_proceduret::resultt dec_solve() override;
decision_proceduret::resultt dec_solve(const exprt &) override;
void print_assignment(std::ostream &out) const override;
std::string decision_procedure_text() const override;
exprt get(const exprt &expr) const override;
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/refinement/bv_refinement.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ class bv_refinementt:public bv_pointerst

explicit bv_refinementt(const infot &info);

decision_proceduret::resultt dec_solve() override;
decision_proceduret::resultt dec_solve(const exprt &) override;

std::string decision_procedure_text() const override
{
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/refinement/bv_refinement_loop.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ bv_refinementt::bv_refinementt(const infot &info)
PRECONDITION(prop.has_is_in_conflict());
}

decision_proceduret::resultt bv_refinementt::dec_solve()
decision_proceduret::resultt bv_refinementt::dec_solve(const exprt &assumption)
{
// do the usual post-processing
log.status() << "BV-Refinement: post-processing" << messaget::eom;
Expand Down
12 changes: 10 additions & 2 deletions src/solvers/smt2/smt2_conv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -317,9 +317,17 @@ void smt2_convt::define_object_size(
}
}

decision_proceduret::resultt smt2_convt::dec_solve()
decision_proceduret::resultt smt2_convt::dec_solve(const exprt &assumption)
{
write_footer();
if(assumption.is_nil())
write_footer();
else
{
assumptions.push_back(literal_exprt(convert(assumption)));
write_footer();
assumptions.pop_back();
}

out.flush();
return decision_proceduret::resultt::D_ERROR;
}
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/smt2/smt2_conv.h
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@ class smt2_convt : public stack_decision_proceduret

std::size_t number_of_solver_calls = 0;

resultt dec_solve() override;
resultt dec_solve(const exprt &) override;

void write_header();
/// Writes the end of the SMT file to the `smt_convt::out` stream. These parts
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/smt2/smt2_dec.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ std::string smt2_dect::decision_procedure_text() const
// clang-format on
}

decision_proceduret::resultt smt2_dect::dec_solve()
decision_proceduret::resultt smt2_dect::dec_solve(const exprt &assumption)
{
++number_of_solver_calls;

Expand Down
2 changes: 1 addition & 1 deletion src/solvers/smt2/smt2_dec.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,11 +39,11 @@ class smt2_dect : protected smt2_stringstreamt, public smt2_convt
{
}

resultt dec_solve() override;
std::string decision_procedure_text() const override;

protected:
message_handlert &message_handler;
resultt dec_solve(const exprt &) override;

/// Everything except the footer is cached, so that output files can be
/// rewritten with varying footers.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -687,7 +687,8 @@ exprt smt2_incremental_decision_proceduret::lower(exprt expression) const
return lowered;
}

decision_proceduret::resultt smt2_incremental_decision_proceduret::dec_solve()
decision_proceduret::resultt
smt2_incremental_decision_proceduret::dec_solve(const exprt &assumption)
{
++number_of_solver_calls;
define_object_properties();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ class smt2_incremental_decision_proceduret final

protected:
// Implementation of protected decision_proceduret member function.
resultt dec_solve() override;
resultt dec_solve(const exprt &) override;
/// \brief Defines a function of array sort and asserts the element values
/// from `array_exprt` or `array_of_exprt`.
/// \details
Expand Down
9 changes: 6 additions & 3 deletions src/solvers/strings/string_refinement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -603,7 +603,8 @@ output_equations(std::ostream &output, const std::vector<exprt> &equations)
/// \return `resultt::D_SATISFIABLE` if the constraints are satisfiable,
/// `resultt::D_UNSATISFIABLE` if they are unsatisfiable,
/// `resultt::D_ERROR` if the limit of iteration was reached.
decision_proceduret::resultt string_refinementt::dec_solve()
decision_proceduret::resultt
string_refinementt::dec_solve(const exprt &assumption)
{
#ifdef DEBUG
log.debug() << "dec_solve: Initial set of equations" << messaget::eom;
Expand Down Expand Up @@ -781,7 +782,8 @@ decision_proceduret::resultt string_refinementt::dec_solve()
// Initial try without index set
const auto get = [this](const exprt &expr) { return this->get(expr); };
dependencies.clean_cache();
const decision_proceduret::resultt initial_result = supert::dec_solve();
const decision_proceduret::resultt initial_result =
supert::dec_solve(nil_exprt());
if(initial_result == resultt::D_SATISFIABLE)
{
bool satisfied;
Expand Down Expand Up @@ -822,7 +824,8 @@ decision_proceduret::resultt string_refinementt::dec_solve()
while((loop_bound_--) > 0)
{
dependencies.clean_cache();
const decision_proceduret::resultt refined_result = supert::dec_solve();
const decision_proceduret::resultt refined_result =
supert::dec_solve(nil_exprt());

if(refined_result == resultt::D_SATISFIABLE)
{
Expand Down
4 changes: 3 additions & 1 deletion src/solvers/strings/string_refinement.h
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,9 @@ class string_refinementt final : public bv_refinementt

exprt get(const exprt &expr) const override;
void set_to(const exprt &expr, bool value) override;
decision_proceduret::resultt dec_solve() override;

protected:
decision_proceduret::resultt dec_solve(const exprt &) override;

private:
// Base class
Expand Down
1 change: 1 addition & 0 deletions unit/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ SRC += analyses/ai/ai.cpp \
path_strategies.cpp \
pointer-analysis/value_set.cpp \
solvers/bdd/miniBDD/miniBDD.cpp \
solvers/flattening/boolbv.cpp \
solvers/floatbv/float_utils.cpp \
solvers/prop/bdd_expr.cpp \
solvers/sat/external_sat.cpp \
Expand Down
50 changes: 50 additions & 0 deletions unit/solvers/flattening/boolbv.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/*******************************************************************\

Module: Unit tests for boolbvt

Author: Daniel Kroening

\*******************************************************************/

/// \file
/// Unit tests for boolbvt

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

#include <solvers/flattening/boolbv.h>
#include <solvers/sat/satcheck.h>
#include <testing-utils/use_catch.h>

SCENARIO("boolbvt", "[core][solvers][flattening][boolbvt]")
{
console_message_handlert message_handler;
message_handler.set_verbosity(0);

GIVEN("A satisfiable bit-vector formula f")
{
satcheckt satcheck(message_handler);
symbol_tablet symbol_table;
namespacet ns(symbol_table);
boolbvt boolbv(ns, satcheck, message_handler);

unsignedbv_typet u32(32);
boolbv << equal_exprt(symbol_exprt("x", u32), from_integer(10, u32));

THEN("is indeed satisfiable")
{
REQUIRE(boolbv() == decision_proceduret::resultt::D_SATISFIABLE);
}
THEN("is unsatisfiable under an inconsistent assumption")
{
auto assumption =
equal_exprt(symbol_exprt("x", u32), from_integer(11, u32));
REQUIRE(
boolbv(assumption) == decision_proceduret::resultt::D_UNSATISFIABLE);
}
}
}
4 changes: 4 additions & 0 deletions unit/solvers/flattening/module_dependencies.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
solvers/flattening
solvers/sat
testing-utils
util
8 changes: 4 additions & 4 deletions unit/solvers/strings/string_refinement/string_refinement.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ SCENARIO("string refinement", "[core][solvers][strings][string_refinement]")
solver.set_to(
equal_exprt{return_length, from_integer(15, int_type)}, true);

auto result = solver.dec_solve();
auto result = solver();
THEN("The formula is satisfiable")
{
REQUIRE(result == decision_proceduret::resultt::D_SATISFIABLE);
Expand Down Expand Up @@ -136,7 +136,7 @@ SCENARIO("string refinement", "[core][solvers][strings][string_refinement]")
"The formula is satisfiable and the model of the array should have"
"length 10 and end with 'b'")
{
auto result = solver.dec_solve();
auto result = solver();
REQUIRE(result == decision_proceduret::resultt::D_SATISFIABLE);
const exprt array_model = solver.get(array1);
REQUIRE(can_cast_expr<array_exprt>(array_model));
Expand Down Expand Up @@ -183,7 +183,7 @@ SCENARIO("string refinement", "[core][solvers][strings][string_refinement]")
"The model for array1 has length 10 and contains 'c' at "
" position 3 and b at position 9")
{
auto result = solver.dec_solve();
auto result = solver();
REQUIRE(result == decision_proceduret::resultt::D_SATISFIABLE);
const exprt array_model = solver.get(array1);
REQUIRE(can_cast_expr<array_exprt>(array_model));
Expand Down Expand Up @@ -230,7 +230,7 @@ SCENARIO("string refinement", "[core][solvers][strings][string_refinement]")
true);
THEN("The model for array1 has length 10 and contains 'c' at position 9")
{
auto result = solver.dec_solve();
auto result = solver();
REQUIRE(result == decision_proceduret::resultt::D_SATISFIABLE);
const exprt array_model = solver.get(array1);
REQUIRE(can_cast_expr<array_exprt>(array_model));
Expand Down