Skip to content

Commit 75ef883

Browse files
Address review comments on solver state machine
- Wrap do_prop_solve() in try/catch to set ERROR state on exception - Clarify decision_proceduret docs: D_ERROR represents unsolved or error - Add PRECONDITION(!a.is_constant()) to CaDiCaL is_in_conflict() - Add unit test for new_variable() invalidating SAT state - Call clear_status() in cnft::new_variable() and cnft::new_variables() - Document that ERROR state is intentionally sticky in clear_status() - Invalidate latest_result in set_to_true/set_to_false Co-authored-by: Kiro <kiro-agent@users.noreply.github.com>
1 parent e41b6f3 commit 75ef883

7 files changed

Lines changed: 55 additions & 29 deletions

File tree

src/solvers/decision_procedure.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,10 +34,12 @@ decision_proceduret::operator()(const exprt &assumption)
3434

3535
void decision_proceduret::set_to_true(const exprt &expr)
3636
{
37+
latest_result = resultt::D_ERROR;
3738
set_to(expr, true);
3839
}
3940

4041
void decision_proceduret::set_to_false(const exprt &expr)
4142
{
43+
latest_result = resultt::D_ERROR;
4244
set_to(expr, false);
4345
}

src/solvers/decision_procedure.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,17 +21,18 @@ class exprt;
2121
///
2222
/// A decision procedure follows a state machine:
2323
///
24-
/// UNKNOWN ──operator()──► D_SATISFIABLE ──set_to()──► UNKNOWN
25-
/// ▲ │
26-
/// │ └──get() valid
27-
///
28-
/// UNKNOWN ──operator()──► D_UNSATISFIABLE ──set_to()──► UNKNOWN
24+
/// D_ERROR ──operator()──► D_SATISFIABLE ──set_to()──► D_ERROR
25+
/// ▲ │
26+
/// │ └──get() valid
27+
/// │
28+
/// D_ERROR ──operator()──► D_UNSATISFIABLE ──set_to()──► D_ERROR
2929
///
30-
/// After construction, the procedure is in the UNKNOWN state. Calling
30+
/// After construction, the procedure's status (\ref latest_result) is
31+
/// initialised to D_ERROR, representing "not yet solved". Calling
3132
/// \ref operator() transitions to D_SATISFIABLE, D_UNSATISFIABLE, or D_ERROR.
3233
/// Reading the satisfying assignment via \ref get is only valid in the
3334
/// D_SATISFIABLE state. Adding new constraints (\ref set_to, \ref handle)
34-
/// transitions back to UNKNOWN.
35+
/// resets the status to D_ERROR.
3536
class decision_proceduret
3637
{
3738
public:

src/solvers/prop/prop.cpp

Lines changed: 30 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -36,43 +36,51 @@ bvt propt::new_variables(std::size_t width)
3636
return result;
3737
}
3838

39+
static propt::statust status_from_result(propt::resultt result)
40+
{
41+
switch(result)
42+
{
43+
case propt::resultt::P_SATISFIABLE:
44+
return propt::statust::SAT;
45+
case propt::resultt::P_UNSATISFIABLE:
46+
return propt::statust::UNSAT;
47+
case propt::resultt::P_ERROR:
48+
return propt::statust::ERROR;
49+
}
50+
UNREACHABLE;
51+
}
52+
3953
propt::resultt propt::prop_solve()
4054
{
4155
static const bvt empty_assumptions;
4256
++number_of_solver_calls;
43-
auto result = do_prop_solve(empty_assumptions);
44-
switch(result)
57+
try
58+
{
59+
auto result = do_prop_solve(empty_assumptions);
60+
solver_state = status_from_result(result);
61+
return result;
62+
}
63+
catch(...)
4564
{
46-
case resultt::P_SATISFIABLE:
47-
solver_state = statust::SAT;
48-
break;
49-
case resultt::P_UNSATISFIABLE:
50-
solver_state = statust::UNSAT;
51-
break;
52-
case resultt::P_ERROR:
5365
solver_state = statust::ERROR;
54-
break;
66+
throw;
5567
}
56-
return result;
5768
}
5869

5970
propt::resultt propt::prop_solve(const bvt &assumptions)
6071
{
6172
++number_of_solver_calls;
62-
auto result = do_prop_solve(assumptions);
63-
switch(result)
73+
try
74+
{
75+
auto result = do_prop_solve(assumptions);
76+
solver_state = status_from_result(result);
77+
return result;
78+
}
79+
catch(...)
6480
{
65-
case resultt::P_SATISFIABLE:
66-
solver_state = statust::SAT;
67-
break;
68-
case resultt::P_UNSATISFIABLE:
69-
solver_state = statust::UNSAT;
70-
break;
71-
case resultt::P_ERROR:
7281
solver_state = statust::ERROR;
73-
break;
82+
throw;
7483
}
75-
return result;
7684
}
7785

7886
std::size_t propt::get_number_of_solver_calls() const

src/solvers/prop/prop.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ class propt
163163
virtual resultt do_prop_solve(const bvt &assumptions) = 0;
164164

165165
/// Transition to UNKNOWN state when the solver is mutated.
166+
/// The ERROR state is intentionally sticky: once the solver has encountered
167+
/// an error, adding new constraints does not clear it.
166168
void clear_status()
167169
{
168170
if(solver_state == statust::SAT || solver_state == statust::UNSAT)

src/solvers/sat/cnf.cpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -384,6 +384,7 @@ literalt cnft::lselect(literalt a, literalt b, literalt c)
384384
/// \return New variable as literal
385385
literalt cnft::new_variable()
386386
{
387+
clear_status();
387388
literalt l(_no_variables, false);
388389

389390
set_no_variables(_no_variables+1);
@@ -395,6 +396,7 @@ literalt cnft::new_variable()
395396
/// \return Vector of new variables.
396397
bvt cnft::new_variables(std::size_t width)
397398
{
399+
clear_status();
398400
bvt result;
399401
result.reserve(width);
400402

src/solvers/sat/satcheck_cadical.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ satcheck_cadical_baset::~satcheck_cadical_baset()
197197
bool satcheck_cadical_baset::is_in_conflict(literalt a) const
198198
{
199199
PRECONDITION(solver_state == propt::statust::UNSAT);
200+
PRECONDITION(!a.is_constant());
200201
return solver->failed(a.dimacs());
201202
}
202203

unit/solvers/sat/satcheck_cadical.cpp

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -160,6 +160,16 @@ SCENARIO(
160160
REQUIRE(satcheck.get_status() == propt::statust::UNSAT);
161161
}
162162
}
163+
164+
WHEN("first solve is SAT, then add a new variable")
165+
{
166+
REQUIRE(satcheck.prop_solve() == propt::resultt::P_SATISFIABLE);
167+
REQUIRE(satcheck.get_status() == propt::statust::SAT);
168+
169+
// Adding a new variable transitions state back to UNKNOWN
170+
satcheck.new_variable();
171+
REQUIRE(satcheck.get_status() == propt::statust::UNKNOWN);
172+
}
163173
}
164174
}
165175

0 commit comments

Comments
 (0)