Skip to content

Commit f1d9d07

Browse files
authored
Merge pull request #5469 from fbbotero-aws/external_sat_solver
Allow CBMC to call an external sat solver
2 parents 5e47c48 + b57bb30 commit f1d9d07

11 files changed

+372
-2
lines changed

src/cbmc/cbmc_parse_options.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -440,6 +440,13 @@ void cbmc_parse_optionst::get_command_line_options(optionst &options)
440440
options.set_option("smt2", true);
441441
}
442442

443+
if(cmdline.isset("external-sat-solver"))
444+
{
445+
options.set_option(
446+
"external-sat-solver", cmdline.get_value("external-sat-solver")),
447+
solver_set = true;
448+
}
449+
443450
if(cmdline.isset("yices"))
444451
{
445452
options.set_option("yices", true), solver_set=true;
@@ -1113,6 +1120,7 @@ void cbmc_parse_optionst::help()
11131120
" --yices use Yices\n"
11141121
" --z3 use Z3\n"
11151122
" --refine use refinement procedure (experimental)\n"
1123+
" --external-sat-solver cmd command to invoke SAT solver process\n"
11161124
HELP_STRING_REFINEMENT_CBMC
11171125
" --outfile filename output formula to given file\n"
11181126
" --arrays-uf-never never turn arrays into uninterpreted functions\n" // NOLINT(*)

src/cbmc/cbmc_parse_options.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ class optionst;
5757
OPT_JSON_INTERFACE \
5858
"(smt1)(smt2)(fpa)(cvc3)(cvc4)(boolector)(yices)(z3)(mathsat)" \
5959
"(cprover-smt2)" \
60+
"(external-sat-solver):" \
6061
"(no-sat-preprocessor)" \
6162
"(beautify)" \
6263
"(dimacs)(refine)(max-node-refinement):(refine-arrays)(refine-arithmetic)"\

src/goto-checker/solver_factory.cpp

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ Author: Daniel Kroening, Peter Schrammel
3232
#include <solvers/prop/solver_resource_limits.h>
3333
#include <solvers/refinement/bv_refinement.h>
3434
#include <solvers/sat/dimacs_cnf.h>
35+
#include <solvers/sat/external_sat.h>
3536
#include <solvers/sat/satcheck.h>
3637
#include <solvers/strings/string_refinement.h>
3738

@@ -131,6 +132,8 @@ std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_solver()
131132
{
132133
if(options.get_bool_option("dimacs"))
133134
return get_dimacs();
135+
if(options.is_set("external-sat-solver"))
136+
return get_external_sat();
134137
if(
135138
options.get_bool_option("refine") &&
136139
!options.get_bool_option("refine-strings"))
@@ -229,9 +232,24 @@ std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_dimacs()
229232

230233
auto bv_dimacs =
231234
util_make_unique<bv_dimacst>(ns, *prop, message_handler, filename);
235+
232236
return util_make_unique<solvert>(std::move(bv_dimacs), std::move(prop));
233237
}
234238

239+
std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_external_sat()
240+
{
241+
no_beautification();
242+
no_incremental_check();
243+
244+
std::string external_sat_solver = options.get_option("external-sat-solver");
245+
auto prop =
246+
util_make_unique<external_satt>(message_handler, external_sat_solver);
247+
248+
auto bv_pointers = util_make_unique<bv_pointerst>(ns, *prop, message_handler);
249+
250+
return util_make_unique<solvert>(std::move(bv_pointers), std::move(prop));
251+
}
252+
235253
std::unique_ptr<solver_factoryt::solvert> solver_factoryt::get_bv_refinement()
236254
{
237255
std::unique_ptr<propt> prop = [this]() -> std::unique_ptr<propt> {

src/goto-checker/solver_factory.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,7 @@ class solver_factoryt
7272

7373
std::unique_ptr<solvert> get_default();
7474
std::unique_ptr<solvert> get_dimacs();
75+
std::unique_ptr<solvert> get_external_sat();
7576
std::unique_ptr<solvert> get_bv_refinement();
7677
std::unique_ptr<solvert> get_string_refinement();
7778
std::unique_ptr<solvert> get_smt2(smt2_dect::solvert solver);

src/solvers/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -184,6 +184,7 @@ SRC = $(BOOLEFORCE_SRC) \
184184
sat/cnf.cpp \
185185
sat/cnf_clause_list.cpp \
186186
sat/dimacs_cnf.cpp \
187+
sat/external_sat.cpp \
187188
sat/pbs_dimacs_cnf.cpp \
188189
sat/resolution_proof.cpp \
189190
smt2/letify.cpp \

src/solvers/sat/dimacs_cnf.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ void dimacs_cnft::write_problem_line(std::ostream &out)
5151
<< clauses.size() << "\n";
5252
}
5353

54-
static void write_dimacs_clause(
54+
void dimacs_cnft::write_dimacs_clause(
5555
const bvt &clause,
5656
std::ostream &out,
5757
bool break_lines)
@@ -100,5 +100,5 @@ void dimacs_cnft::write_clauses(std::ostream &out)
100100

101101
void dimacs_cnf_dumpt::lcnf(const bvt &bv)
102102
{
103-
write_dimacs_clause(bv, out, true);
103+
dimacs_cnft::write_dimacs_clause(bv, out, true);
104104
}

src/solvers/sat/dimacs_cnf.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,9 @@ class dimacs_cnft:public cnf_clause_listt
3232
void set_assignment(literalt a, bool value) override;
3333
bool is_in_conflict(literalt l) const override;
3434

35+
static void
36+
write_dimacs_clause(const bvt &, std::ostream &, bool break_lines);
37+
3538
protected:
3639
void write_problem_line(std::ostream &out);
3740
void write_clauses(std::ostream &out);

src/solvers/sat/external_sat.cpp

Lines changed: 169 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,169 @@
1+
/// \file
2+
/// Allows call an external SAT solver to allow faster integration of
3+
/// newer SAT solvers
4+
/// \author Francis Botero <[email protected]>
5+
6+
#include "external_sat.h"
7+
8+
#include "dimacs_cnf.h"
9+
10+
#include <util/exception_utils.h>
11+
#include <util/run.h>
12+
#include <util/string_utils.h>
13+
#include <util/tempfile.h>
14+
15+
#include <chrono>
16+
#include <cstdlib>
17+
#include <fstream>
18+
#include <random>
19+
#include <sstream>
20+
#include <string>
21+
#include <thread>
22+
23+
external_satt::external_satt(message_handlert &message_handler, std::string cmd)
24+
: cnf_clause_list_assignmentt(message_handler), solver_cmd(std::move(cmd))
25+
{
26+
}
27+
28+
const std::string external_satt::solver_text()
29+
{
30+
return "External SAT solver";
31+
}
32+
33+
bool external_satt::is_in_conflict(literalt) const
34+
{
35+
UNIMPLEMENTED;
36+
}
37+
38+
void external_satt::set_assignment(literalt, bool)
39+
{
40+
UNIMPLEMENTED;
41+
}
42+
43+
void external_satt::write_cnf_file(std::string cnf_file)
44+
{
45+
log.status() << "Writing temporary CNF" << messaget::eom;
46+
std::ofstream out(cnf_file);
47+
48+
// We start counting at 1, thus there is one variable fewer.
49+
out << "p cnf " << (no_variables() - 1) << ' ' << no_clauses() << '\n';
50+
51+
for(auto &c : clauses)
52+
dimacs_cnft::write_dimacs_clause(c, out, false);
53+
54+
out.close();
55+
}
56+
57+
std::string external_satt::execute_solver(std::string cnf_file)
58+
{
59+
log.status() << "Invoking SAT solver" << messaget::eom;
60+
std::ostringstream response_ostream;
61+
auto cmd_result = run(solver_cmd, {"", cnf_file}, "", response_ostream, "");
62+
63+
log.status() << "Solver returned code: " << cmd_result << messaget::eom;
64+
return response_ostream.str();
65+
}
66+
67+
external_satt::resultt external_satt::parse_result(std::string solver_output)
68+
{
69+
std::istringstream response_istream(solver_output);
70+
std::string line;
71+
external_satt::resultt result = resultt::P_ERROR;
72+
std::vector<bool> assigned_variables(no_variables(), false);
73+
assignment.insert(assignment.begin(), no_variables(), tvt(false));
74+
75+
while(getline(response_istream, line))
76+
{
77+
if(line[0] == 's')
78+
{
79+
auto parts = split_string(line, ' ');
80+
if(parts.size() < 2)
81+
{
82+
log.error() << "external SAT solver has provided an unexpected "
83+
"response in results.\nUnexpected reponse: "
84+
<< line << messaget::eom;
85+
return resultt::P_ERROR;
86+
}
87+
88+
auto status = parts[1];
89+
log.status() << "result:" << status << messaget::eom;
90+
if(status == "UNSATISFIABLE")
91+
{
92+
return resultt::P_UNSATISFIABLE;
93+
}
94+
if(status == "SATISFIABLE")
95+
{
96+
result = resultt::P_SATISFIABLE;
97+
}
98+
if(status == "TIMEOUT")
99+
{
100+
log.error() << "external SAT solver reports a timeout" << messaget::eom;
101+
return resultt::P_ERROR;
102+
}
103+
}
104+
105+
if(line[0] == 'v')
106+
{
107+
auto assignments = split_string(line, ' ');
108+
109+
// remove the first element which should be 'v' identifying
110+
// the line as the satisfying assignments
111+
assignments.erase(assignments.begin());
112+
auto number_of_variables = no_variables();
113+
for(auto &assignment_string : assignments)
114+
{
115+
try
116+
{
117+
signed long long as_long = std::stol(assignment_string);
118+
size_t index = std::labs(as_long);
119+
120+
if(index >= number_of_variables)
121+
{
122+
log.error() << "SAT assignment " << as_long
123+
<< " out of range of CBMC largest variable of "
124+
<< (number_of_variables - 1) << messaget::eom;
125+
return resultt::P_ERROR;
126+
}
127+
assignment[index] = tvt(as_long >= 0);
128+
assigned_variables[index] = true;
129+
}
130+
catch(...)
131+
{
132+
log.error() << "SAT assignment " << assignment_string
133+
<< " caused an exception while processing"
134+
<< messaget::eom;
135+
return resultt::P_ERROR;
136+
}
137+
}
138+
// Assignments can span multiple lines so returning early isn't an option
139+
}
140+
}
141+
142+
if(result == resultt::P_SATISFIABLE)
143+
{
144+
// We don't need to check zero
145+
for(size_t index = 1; index < no_variables(); index++)
146+
{
147+
if(!assigned_variables[index])
148+
{
149+
log.error() << "No assignment was found for literal: " << index
150+
<< messaget::eom;
151+
return resultt::P_ERROR;
152+
}
153+
}
154+
return resultt::P_SATISFIABLE;
155+
}
156+
157+
log.error() << "external SAT solver has provided an unexpected response"
158+
<< messaget::eom;
159+
return resultt::P_ERROR;
160+
}
161+
162+
external_satt::resultt external_satt::do_prop_solve()
163+
{
164+
// create a temporary file
165+
temporary_filet cnf_file("external-sat", ".cnf");
166+
write_cnf_file(cnf_file());
167+
auto output = execute_solver(cnf_file());
168+
return parse_result(output);
169+
}

src/solvers/sat/external_sat.h

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/// \file
2+
/// Allows calling an external SAT solver to allow faster integration of
3+
/// newer SAT solvers
4+
/// \author Francis Botero <[email protected]>
5+
6+
#ifndef CPROVER_SOLVERS_SAT_EXTERNAL_SAT_H
7+
#define CPROVER_SOLVERS_SAT_EXTERNAL_SAT_H
8+
9+
#include "cnf_clause_list.h"
10+
class external_satt : public cnf_clause_list_assignmentt
11+
{
12+
public:
13+
explicit external_satt(message_handlert &message_handler, std::string cmd);
14+
15+
bool has_set_assumptions() const override final
16+
{
17+
return false;
18+
}
19+
20+
bool has_is_in_conflict() const override final
21+
{
22+
return false;
23+
}
24+
25+
const std::string solver_text() override;
26+
27+
bool is_in_conflict(literalt) const override;
28+
void set_assignment(literalt, bool) override;
29+
30+
protected:
31+
resultt do_prop_solve() override;
32+
std::string solver_cmd;
33+
void write_cnf_file(std::string);
34+
std::string execute_solver(std::string);
35+
resultt parse_result(std::string);
36+
};
37+
38+
#endif // CPROVER_SOLVERS_SAT_EXTERNAL_SAT_H

unit/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ SRC += analyses/ai/ai.cpp \
5858
solvers/floatbv/float_utils.cpp \
5959
solvers/lowering/byte_operators.cpp \
6060
solvers/prop/bdd_expr.cpp \
61+
solvers/sat/external_sat.cpp \
6162
solvers/sat/satcheck_minisat2.cpp \
6263
solvers/strings/array_pool/array_pool.cpp \
6364
solvers/strings/string_constraint_generator_valueof/calculate_max_string_length.cpp \

0 commit comments

Comments
 (0)