Skip to content

Commit 3fee172

Browse files
tautschnigjezhiggins
authored andcommitted
Add CaDiCaL support to CMake
Add CMake configuration to build using CaDiCaL, which was already supported with Makefiles. Also bump the version of CaDiCaL to rel-1.3.0 and fix the use of the cnf_solvert interface. Run the ubuntu-18_04-package GitHub PR action with -Dsat_impl=cadical to avoid future regressions. Fixes: diffblue#5348
1 parent 5c4d617 commit 3fee172

File tree

9 files changed

+206
-45
lines changed

9 files changed

+206
-45
lines changed

.github/workflows/pull-request-checks.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -332,7 +332,7 @@ jobs:
332332
run: |
333333
mkdir build
334334
cd build
335-
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release
335+
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -Dsat_impl=cadical
336336
- name: Build using Ninja
337337
run: |
338338
cd build

CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,8 @@ endif()
6565
set(enable_cbmc_tests on CACHE BOOL "Whether CBMC tests should be enabled")
6666

6767
set(sat_impl "minisat2" CACHE STRING
68-
"This setting controls the SAT library which is used. Valid values are 'minisat2' and 'glucose'"
68+
"This setting controls the SAT library which is used. Valid values are
69+
'minisat2', 'glucose', or 'cadical'"
6970
)
7071

7172
if(${enable_cbmc_tests})

scripts/cadical-patch

Lines changed: 0 additions & 10 deletions
This file was deleted.

src/Makefile

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -152,15 +152,14 @@ glucose-download:
152152
@(cd ../glucose-syrup; patch -p1 < ../scripts/glucose-syrup-patch)
153153
@rm glucose-syrup.tgz
154154

155-
cadical_release = rel-06w
155+
cadical_release = rel-1.3.0
156156
cadical-download:
157157
@echo "Downloading CaDiCaL $(cadical_release)"
158158
@$(DOWNLOADER) https://github.com/arminbiere/cadical/archive/$(cadical_release).tar.gz
159159
@$(TAR) xfz $(cadical_release).tar.gz
160160
@rm -Rf ../cadical
161161
@mv cadical-$(cadical_release) ../cadical
162-
@(cd ../cadical; patch -p1 < ../scripts/cadical-patch)
163-
@cd ../cadical && CXX=$(CXX) CXXFLAGS=-O3 ./configure --debug && make
162+
@cd ../cadical && CXX=$(CXX) ./configure -O3 -s -j && make
164163
@$(RM) $(cadical_release).tar.gz
165164

166165
doc :

src/solvers/CMakeLists.txt

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,36 @@ elseif("${sat_impl}" STREQUAL "glucose")
9898
target_sources(solvers PRIVATE ${glucose_source})
9999

100100
target_link_libraries(solvers glucose-condensed)
101+
elseif("${sat_impl}" STREQUAL "cadical")
102+
message(STATUS "Building solvers with cadical")
103+
104+
download_project(PROJ cadical
105+
URL https://github.com/arminbiere/cadical/archive/rel-1.3.0.tar.gz
106+
PATCH_COMMAND true
107+
COMMAND CXX=${CMAKE_CXX_COMPILER} ./configure -O3 -s -j
108+
URL_MD5 5bd15d1e198d2e904a8af8b7873dd341
109+
)
110+
111+
message(STATUS "Building CaDiCaL")
112+
execute_process(COMMAND make WORKING_DIRECTORY ${cadical_SOURCE_DIR})
113+
114+
target_compile_definitions(solvers PUBLIC
115+
SATCHECK_CADICAL HAVE_CADICAL
116+
)
117+
118+
add_library(cadical STATIC IMPORTED)
119+
120+
set_target_properties(
121+
cadical
122+
PROPERTIES IMPORTED_LOCATION ${cadical_SOURCE_DIR}/build/libcadical.a
123+
)
124+
125+
target_include_directories(solvers
126+
PUBLIC
127+
${cadical_SOURCE_DIR}/src
128+
)
129+
130+
target_link_libraries(solvers cadical)
101131
endif()
102132

103133
if(CMAKE_USE_CUDD)

src/solvers/sat/satcheck_cadical.cpp

Lines changed: 57 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ Author: Michael Tautschnig
1010

1111
#include <util/exception_utils.h>
1212
#include <util/invariant.h>
13+
#include <util/narrow.h>
1314
#include <util/threeval.h>
1415

1516
#ifdef HAVE_CADICAL
@@ -23,7 +24,7 @@ tvt satcheck_cadicalt::l_get(literalt a) const
2324

2425
tvt result;
2526

26-
if(a.var_no() > static_cast<unsigned>(solver->max()))
27+
if(a.var_no() > narrow<unsigned>(solver->vars()))
2728
return tvt(tvt::tv_enumt::TV_UNKNOWN);
2829

2930
const int val = solver->val(a.dimacs());
@@ -62,6 +63,23 @@ void satcheck_cadicalt::lcnf(const bvt &bv)
6263
}
6364
solver->add(0); // terminate clause
6465

66+
with_solver_hardness([this, &bv](solver_hardnesst &hardness) {
67+
// To map clauses to lines of program code, track clause indices in the
68+
// dimacs cnf output. Dimacs output is generated after processing
69+
// clauses to remove duplicates and clauses that are trivially true.
70+
// Here, a clause is checked to see if it can be thus eliminated. If
71+
// not, add the clause index to list of clauses in
72+
// solver_hardnesst::register_clause().
73+
static size_t cnf_clause_index = 0;
74+
bvt cnf;
75+
bool clause_removed = process_clause(bv, cnf);
76+
77+
if(!clause_removed)
78+
cnf_clause_index++;
79+
80+
hardness.register_clause(bv, cnf, cnf_clause_index, !clause_removed);
81+
});
82+
6583
clause_counter++;
6684
}
6785

@@ -72,31 +90,37 @@ propt::resultt satcheck_cadicalt::do_prop_solve()
7290
log.statistics() << (no_variables() - 1) << " variables, " << clause_counter
7391
<< " clauses" << messaget::eom;
7492

75-
if(status == statust::UNSAT)
93+
// if assumptions contains false, we need this to be UNSAT
94+
for(const auto &a : assumptions)
7695
{
77-
log.status() << "SAT checker inconsistent: instance is UNSATISFIABLE"
78-
<< messaget::eom;
79-
}
80-
else
81-
{
82-
switch(solver->solve())
96+
if(a.is_false())
8397
{
84-
case 10:
85-
log.status() << "SAT checker: instance is SATISFIABLE" << messaget::eom;
86-
status = statust::SAT;
87-
return resultt::P_SATISFIABLE;
88-
case 20:
89-
log.status() << "SAT checker: instance is UNSATISFIABLE"
90-
<< messaget::eom;
91-
break;
92-
default:
93-
log.status() << "SAT checker: solving returned without solution"
94-
<< messaget::eom;
95-
throw analysis_exceptiont(
96-
"solving inside CaDiCaL SAT solver has been interrupted");
98+
log.status() << "got FALSE as assumption: instance is UNSATISFIABLE"
99+
<< messaget::eom;
100+
status = statust::UNSAT;
101+
return resultt::P_UNSATISFIABLE;
97102
}
98103
}
99104

105+
for(const auto &a : assumptions)
106+
solver->assume(a.dimacs());
107+
108+
switch(solver->solve())
109+
{
110+
case 10:
111+
log.status() << "SAT checker: instance is SATISFIABLE" << messaget::eom;
112+
status = statust::SAT;
113+
return resultt::P_SATISFIABLE;
114+
case 20:
115+
log.status() << "SAT checker: instance is UNSATISFIABLE" << messaget::eom;
116+
break;
117+
default:
118+
log.status() << "SAT checker: solving returned without solution"
119+
<< messaget::eom;
120+
throw analysis_exceptiont(
121+
"solving inside CaDiCaL SAT solver has been interrupted");
122+
}
123+
100124
status = statust::UNSAT;
101125
return resultt::P_UNSATISFIABLE;
102126
}
@@ -107,8 +131,8 @@ void satcheck_cadicalt::set_assignment(literalt a, bool value)
107131
INVARIANT(false, "method not supported");
108132
}
109133

110-
satcheck_cadicalt::satcheck_cadicalt() :
111-
solver(new CaDiCaL::Solver())
134+
satcheck_cadicalt::satcheck_cadicalt(message_handlert &message_handler)
135+
: cnf_solvert(message_handler), solver(new CaDiCaL::Solver())
112136
{
113137
solver->set("quiet", 1);
114138
}
@@ -120,13 +144,20 @@ satcheck_cadicalt::~satcheck_cadicalt()
120144

121145
void satcheck_cadicalt::set_assumptions(const bvt &bv)
122146
{
123-
INVARIANT(false, "method not supported");
147+
// We filter out 'true' assumptions which cause spurious results with CaDiCaL.
148+
assumptions.clear();
149+
for(const auto &assumption : bv)
150+
{
151+
if(!assumption.is_true())
152+
{
153+
assumptions.push_back(assumption);
154+
}
155+
}
124156
}
125157

126158
bool satcheck_cadicalt::is_in_conflict(literalt a) const
127159
{
128-
INVARIANT(false, "method not supported");
129-
return false;
160+
return solver->failed(a.dimacs());
130161
}
131162

132163
#endif

src/solvers/sat/satcheck_cadical.h

Lines changed: 24 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,17 @@ Author: Michael Tautschnig
1212

1313
#include "cnf.h"
1414

15+
#include <solvers/hardness_collector.h>
16+
1517
namespace CaDiCaL // NOLINT(readability/namespace)
1618
{
1719
class Solver; // NOLINT(readability/identifiers)
1820
}
1921

20-
class satcheck_cadicalt:public cnf_solvert
22+
class satcheck_cadicalt : public cnf_solvert, public hardness_collectort
2123
{
2224
public:
23-
satcheck_cadicalt();
25+
explicit satcheck_cadicalt(message_handlert &message_handler);
2426
virtual ~satcheck_cadicalt();
2527

2628
const std::string solver_text() override;
@@ -32,19 +34,37 @@ class satcheck_cadicalt:public cnf_solvert
3234
void set_assumptions(const bvt &_assumptions) override;
3335
bool has_set_assumptions() const override
3436
{
35-
return false;
37+
return true;
3638
}
3739
bool has_is_in_conflict() const override
3840
{
39-
return false;
41+
return true;
4042
}
4143
bool is_in_conflict(literalt a) const override;
4244

45+
void
46+
with_solver_hardness(std::function<void(solver_hardnesst &)> handler) override
47+
{
48+
if(solver_hardness.has_value())
49+
{
50+
handler(solver_hardness.value());
51+
}
52+
}
53+
54+
void enable_hardness_collection() override
55+
{
56+
solver_hardness = solver_hardnesst{};
57+
}
58+
4359
protected:
4460
resultt do_prop_solve() override;
4561

4662
// NOLINTNEXTLINE(readability/identifiers)
4763
CaDiCaL::Solver * solver;
64+
65+
bvt assumptions;
66+
67+
optionalt<solver_hardnesst> solver_hardness;
4868
};
4969

5070
#endif // CPROVER_SOLVERS_SAT_SATCHECK_CADICAL_H

unit/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,7 @@ SRC += analyses/ai/ai.cpp \
6969
solvers/lowering/byte_operators.cpp \
7070
solvers/prop/bdd_expr.cpp \
7171
solvers/sat/external_sat.cpp \
72+
solvers/sat/satcheck_cadical.cpp \
7273
solvers/sat/satcheck_minisat2.cpp \
7374
solvers/strings/array_pool/array_pool.cpp \
7475
solvers/strings/string_constraint_generator_valueof/calculate_max_string_length.cpp \
@@ -228,6 +229,9 @@ endif
228229
ifeq ($(MINISAT2),)
229230
EXCLUDED_TESTS += satcheck_minisat2.cpp
230231
endif
232+
ifeq ($(CADICAL),)
233+
EXCLUDED_TESTS += satcheck_cadical.cpp
234+
endif
231235

232236
N_CATCH_TESTS = $(shell \
233237
cat $$(find . -name "*.cpp" \

unit/solvers/sat/satcheck_cadical.cpp

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
/*******************************************************************\
2+
3+
Module: Unit tests for satcheck_cadical
4+
5+
Author: Peter Schrammel, Michael Tautschnig
6+
7+
\*******************************************************************/
8+
9+
/// \file
10+
/// Unit tests for satcheck_cadical
11+
12+
#ifdef HAVE_CADICAL
13+
14+
# include <testing-utils/use_catch.h>
15+
16+
# include <solvers/prop/literal.h>
17+
# include <solvers/sat/satcheck_cadical.h>
18+
# include <util/cout_message.h>
19+
20+
SCENARIO("satcheck_cadical", "[core][solvers][sat][satcheck_cadical]")
21+
{
22+
console_message_handlert message_handler;
23+
24+
GIVEN("A satisfiable formula f")
25+
{
26+
satcheck_cadicalt satcheck(message_handler);
27+
literalt f = satcheck.new_variable();
28+
satcheck.l_set_to_true(f);
29+
30+
THEN("is indeed satisfiable")
31+
{
32+
REQUIRE(satcheck.prop_solve() == propt::resultt::P_SATISFIABLE);
33+
}
34+
THEN("is unsatisfiable under a false assumption")
35+
{
36+
bvt assumptions;
37+
assumptions.push_back(const_literal(false));
38+
satcheck.set_assumptions(assumptions);
39+
REQUIRE(satcheck.prop_solve() == propt::resultt::P_UNSATISFIABLE);
40+
}
41+
}
42+
43+
GIVEN("An unsatisfiable formula f && !f")
44+
{
45+
satcheck_cadicalt satcheck(message_handler);
46+
literalt f = satcheck.new_variable();
47+
satcheck.l_set_to_true(satcheck.land(f, !f));
48+
49+
THEN("is indeed unsatisfiable")
50+
{
51+
REQUIRE(satcheck.prop_solve() == propt::resultt::P_UNSATISFIABLE);
52+
}
53+
}
54+
55+
GIVEN("An unsatisfiable formula false implied by a")
56+
{
57+
satcheck_cadicalt satcheck(message_handler);
58+
literalt a = satcheck.new_variable();
59+
literalt a_implies_false = satcheck.lor(!a, const_literal(false));
60+
satcheck.l_set_to_true(a_implies_false);
61+
62+
THEN("is indeed unsatisfiable under assumption a")
63+
{
64+
bvt assumptions;
65+
assumptions.push_back(a);
66+
satcheck.set_assumptions(assumptions);
67+
REQUIRE(satcheck.prop_solve() == propt::resultt::P_UNSATISFIABLE);
68+
}
69+
THEN("is still unsatisfiable under assumption a and true")
70+
{
71+
bvt assumptions;
72+
assumptions.push_back(const_literal(true));
73+
assumptions.push_back(a);
74+
satcheck.set_assumptions(assumptions);
75+
REQUIRE(satcheck.prop_solve() == propt::resultt::P_UNSATISFIABLE);
76+
}
77+
THEN("becomes satisfiable when assumption a is lifted")
78+
{
79+
bvt assumptions;
80+
satcheck.set_assumptions(assumptions);
81+
REQUIRE(satcheck.prop_solve() == propt::resultt::P_SATISFIABLE);
82+
}
83+
}
84+
}
85+
86+
#endif

0 commit comments

Comments
 (0)