Skip to content

Commit d43ee91

Browse files
committed
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 83618df commit d43ee91

File tree

9 files changed

+208
-45
lines changed

9 files changed

+208
-45
lines changed

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

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -324,7 +324,7 @@ jobs:
324324
run: |
325325
mkdir build
326326
cd build
327-
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release
327+
cmake .. -G Ninja -DCMAKE_BUILD_TYPE=Release -Dsat_impl=cadical
328328
- name: Build using Ninja
329329
run: |
330330
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: 56 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ tvt satcheck_cadicalt::l_get(literalt a) const
2323

2424
tvt result;
2525

26-
if(a.var_no() > static_cast<unsigned>(solver->max()))
26+
if(a.var_no() > static_cast<unsigned>(solver->vars()))
2727
return tvt(tvt::tv_enumt::TV_UNKNOWN);
2828

2929
const int val = solver->val(a.dimacs());
@@ -62,6 +62,23 @@ void satcheck_cadicalt::lcnf(const bvt &bv)
6262
}
6363
solver->add(0); // terminate clause
6464

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

@@ -72,31 +89,37 @@ propt::resultt satcheck_cadicalt::do_prop_solve()
7289
log.statistics() << (no_variables() - 1) << " variables, " << clause_counter
7390
<< " clauses" << messaget::eom;
7491

75-
if(status == statust::UNSAT)
92+
// if assumptions contains false, we need this to be UNSAT
93+
for(const auto &a : assumptions)
7694
{
77-
log.status() << "SAT checker inconsistent: instance is UNSATISFIABLE"
78-
<< messaget::eom;
79-
}
80-
else
81-
{
82-
switch(solver->solve())
95+
if(a.is_false())
8396
{
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");
97+
log.status() << "got FALSE as assumption: instance is UNSATISFIABLE"
98+
<< messaget::eom;
99+
status = statust::UNSAT;
100+
return resultt::P_UNSATISFIABLE;
97101
}
98102
}
99103

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

110-
satcheck_cadicalt::satcheck_cadicalt() :
111-
solver(new CaDiCaL::Solver())
133+
satcheck_cadicalt::satcheck_cadicalt(message_handlert &message_handler)
134+
: cnf_solvert(message_handler), solver(new CaDiCaL::Solver())
112135
{
113136
solver->set("quiet", 1);
114137
}
@@ -120,13 +143,20 @@ satcheck_cadicalt::~satcheck_cadicalt()
120143

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

126157
bool satcheck_cadicalt::is_in_conflict(literalt a) const
127158
{
128-
INVARIANT(false, "method not supported");
129-
return false;
159+
return solver->failed(a.dimacs());
130160
}
131161

132162
#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: 7 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 \
@@ -225,6 +226,12 @@ EXCLUDED_TESTS=expr_undefined_casts.cpp
225226
ifneq ($(WITH_MEMORY_ANALYZER),1)
226227
EXCLUDED_TESTS += gdb_api.cpp
227228
endif
229+
ifeq ($(MINISAT2),)
230+
EXCLUDED_TESTS += satcheck_minisat2.cpp
231+
endif
232+
ifeq ($(CADICAL),)
233+
EXCLUDED_TESTS += satcheck_cadical.cpp
234+
endif
228235

229236
N_CATCH_TESTS = $(shell \
230237
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)