Skip to content

Commit bd69b7a

Browse files
committed
unit tests watcher side
1 parent 9accadc commit bd69b7a

File tree

8 files changed

+258
-59
lines changed

8 files changed

+258
-59
lines changed

CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,10 +36,10 @@ set(nlohmann-json_IMPLICIT_CONVERSIONS OFF)
3636

3737
add_subdirectory(fabko)
3838

39-
if (IS_BUILT_FROM_SOURCE AND BUILD_TESTING)
40-
message(STATUS "Tests will be built")
41-
add_subdirectory(tests)
42-
endif ()
39+
#if (BUILD_TESTING)
40+
message(STATUS "Tests will be built")
41+
add_subdirectory(tests)
42+
#endif ()
4343

4444
#
4545
# Installation

docs/cnf/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
# CNF
22

3-
Those are CNF files representing probles that must be runneable from the CLI.
3+
https://www.cs.ubc.ca/~hoos/SATLIB/benchm.html : link to benchmarks to be downloaded.
4+
5+
Those are CNF files representing problems that must be runnable from the CLI.
46
Those are used for testing purposes and validation of the SAT solver which is part of the fabko solution
57

68
## Simple Straightforward formula

docs/cnf/pigeon-hole.cnf

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
p cnf 20 75
2+
3+
c Each pigeon must be in at least one hole
4+
1 2 3 4 0
5+
5 6 7 8 0
6+
9 10 11 12 0
7+
13 14 15 16 0
8+
17 18 19 20 0
9+
10+
c Each pigeon can be in at most one hole
11+
-1 -2 0
12+
-1 -3 0
13+
-1 -4 0
14+
-2 -3 0
15+
-2 -4 0
16+
-3 -4 0
17+
-5 -6 0
18+
-5 -7 0
19+
-5 -8 0
20+
-6 -7 0
21+
-6 -8 0
22+
-7 -8 0
23+
-9 -10 0
24+
-9 -11 0
25+
-9 -12 0
26+
-10 -11 0
27+
-10 -12 0
28+
-11 -12 0
29+
-13 -14 0
30+
-13 -15 0
31+
-13 -16 0
32+
-14 -15 0
33+
-14 -16 0
34+
-15 -16 0
35+
-17 -18 0
36+
-17 -19 0
37+
-17 -20 0
38+
-18 -19 0
39+
-18 -20 0
40+
-19 -20 0
41+
42+
c No two pigeons in the same hole
43+
-1 -5 0
44+
-1 -9 0
45+
-1 -13 0
46+
-1 -17 0
47+
-5 -9 0
48+
-5 -13 0
49+
-5 -17 0
50+
-9 -13 0
51+
-9 -17 0
52+
-13 -17 0
53+
54+
-2 -6 0
55+
-2 -10 0
56+
-2 -14 0
57+
-2 -18 0
58+
-6 -10 0
59+
-6 -14 0
60+
-6 -18 0
61+
-10 -14 0
62+
-10 -18 0
63+
-14 -18 0
64+
65+
-3 -7 0
66+
-3 -11 0
67+
-3 -15 0
68+
-3 -19 0
69+
-7 -11 0
70+
-7 -15 0
71+
-7 -19 0
72+
-11 -15 0
73+
-11 -19 0
74+
-15 -19 0
75+
76+
-4 -8 0
77+
-4 -12 0
78+
-4 -16 0
79+
-4 -20 0
80+
-8 -12 0
81+
-8 -16 0
82+
-8 -20 0
83+
-12 -16 0
84+
-12 -20 0
85+
-16 -20 0

fabko/compiler/backend/sat/solver.cpp

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -98,8 +98,8 @@ Model make_model_from_cnf_file(const std::filesystem::path& cnf_file) {
9898

9999
std::istringstream iss(line);
100100
std::string p, cnf;
101-
std::size_t num_variables;
102-
std::size_t num_clauses;
101+
int num_variables = 0;
102+
int num_clauses = 0;
103103

104104
iss >> p >> cnf >> num_variables >> num_clauses;
105105
if (p != "p" || cnf != "cnf") {
@@ -143,10 +143,12 @@ std::vector<solver::result> solver::solve(std::int32_t expected) {
143143

144144
for (auto i = 0; i < expected; ++i) {
145145
auto r = impl_details::solve_sat(context_, model_);
146+
146147
if (!r.has_value()) {
147148
auto error = r.error();
148149
if (error == sat_error::unsatisfiable) {
149-
log_error("SAT solver cannot find solution for mode : UNSATISFIABLE");
150+
if (i == 0)
151+
log_info("SAT solver cannot find solution for mode : UNSATISFIABLE");
150152
} else {
151153
log_error("SAT solver : an error occurred");
152154
}

fabko/compiler/backend/sat/solver.hh

Lines changed: 19 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -104,26 +104,36 @@ class Clause_Watcher {
104104
*/
105105
explicit Clause_Watcher(const Vars_Soa& vs, const Clause& clause)
106106
: watchers_([&]() -> std::array<std::optional<Vars_Soa::struct_id>, 2> { //
107-
using std::get;
108107
fabko_assert(!clause.get_literals().empty(), "Cannot make a clause watchers over an empty clause");
109-
if (clause.get_literals().size() == 1) {
110-
return {vs[clause.get_literals().front().second].struct_id(), std::nullopt};
111-
}
108+
112109
auto filtered = std::ranges::views::filter(
113110
clause.get_literals(), [&vs](const auto& lit_id_pair) { return get<soa_assignment>(vs[lit_id_pair.second]) == assignment::not_assigned; }) //
114111
| std::views::transform([&vs](const auto& lit_id_pair) { return vs[lit_id_pair.second].struct_id(); }); //
115112

116113
auto it = filtered.begin();
117114
if (it == filtered.end())
118-
return {std::nullopt, std::nullopt}; // no watched literals, clause is satisfied
119-
++it;
120-
if (it == filtered.end())
121-
return {*it, std::nullopt}; // no watched literals, clause is satisfied
115+
return {std::nullopt, std::nullopt}; // no watched literals, clause is satisfied
116+
if (++it == filtered.end())
117+
return {*filtered.begin(), std::nullopt}; // no watched literals, clause is satisfied
122118

123119
return {std::make_optional(*filtered.begin()), std::make_optional(*it)};
124120
}()) {}
125121

126-
[[nodiscard]] std::size_t size() const { return (watchers_[0].has_value() ? 1 : 0) + watchers_[1].has_value() ? 1 : 0; }
122+
[[nodiscard]] std::size_t size() const { return (watchers_[0].has_value() ? 1 : 0) + (watchers_[1].has_value() ? 1 : 0); }
123+
124+
/**
125+
* @return variable ids that are currently under watch
126+
*/
127+
[[nodiscard]] std::vector<Vars_Soa::struct_id> get_watched() const {
128+
std::vector<Vars_Soa::struct_id> res;
129+
if (watchers_[0].has_value()) {
130+
res.push_back(watchers_[0].value());
131+
}
132+
if (watchers_[1].has_value()) {
133+
res.push_back(watchers_[1].value());
134+
}
135+
return res;
136+
}
127137

128138
private:
129139
std::array<std::optional<Vars_Soa::struct_id>, 2> watchers_; //!< The watched literals (1 for unit clauses, 2 for other clauses)

fabko/compiler/backend/sat/solver_impl.cpp

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,16 @@
1212

1313
#include <algorithm>
1414
#include <expected>
15+
#include <numeric>
1516
#include <optional>
1617
#include <ranges>
1718

18-
#include "common/exception.hh"
1919
#include "common/logging.hh"
2020
#include "solver.hh"
2121

2222
#include "solver_context.hh"
2323

24-
#include <numeric>
25-
26-
namespace fabko::compiler::sat {
27-
namespace impl_details {
24+
namespace fabko::compiler::sat::impl_details {
2825

2926
namespace {
3027
constexpr std::string SECTION = "sat_solver"; //!< logging a section for the SAT solver
@@ -127,7 +124,7 @@ conflict_resolution_result resolve_conflict(Solver_Context& ctx, Clauses_Soa::so
127124
const auto& trail_assign_ctx = get<soa_assignment_ctx>(trail_node);
128125

129126
if (!trail_assign_ctx.is_decision() && trail_assign_ctx.decision_level_ == ctx.current_decision_level_) {
130-
if (std::ranges::any_of(current_level_vars, [trail_var](const auto& ss) { return get<soa_literal>(ss).value() == trail_var; })) {
127+
if (std::ranges::any_of(current_level_vars, [trail_var](const auto& current_var) { return get<soa_literal>(current_var).value() == trail_var; })) {
131128
break;
132129
}
133130
}
@@ -155,15 +152,18 @@ conflict_resolution_result resolve_conflict(Solver_Context& ctx, Clauses_Soa::so
155152

156153
// add literals from the propagation clause (except current trail literal)
157154
for (const auto [prop_lit, prop_varid] : propagation_vars) {
155+
const auto prop_node = ctx.vars_soa_[prop_varid];
156+
const auto& prop_assign_ctx = get<soa_assignment_ctx>(prop_node);
157+
158158
if (prop_lit.value() == trail_lit.value()
159159
|| std::ranges::any_of(learned_clause, [&prop_lit](const auto& lit_map) { return prop_lit.value() == lit_map.first.value(); }))
160160
continue;
161161

162162
learned_clause.emplace_back(prop_lit, prop_varid);
163-
if (trail_assign_ctx.decision_level_ == ctx.current_decision_level_) {
164-
current_level_vars.push_back(ctx.vars_soa_[prop_varid]);
165-
} else if (trail_assign_ctx.decision_level_ > backtrack_level) {
166-
backtrack_level = trail_assign_ctx.decision_level_;
163+
if (prop_assign_ctx.decision_level_ == ctx.current_decision_level_) {
164+
current_level_vars.push_back(prop_node);
165+
} else if (prop_assign_ctx.decision_level_ > backtrack_level) {
166+
backtrack_level = prop_assign_ctx.decision_level_;
167167
}
168168
}
169169
}
@@ -305,7 +305,7 @@ bool make_decision(Solver_Context& ctx) {
305305
auto& [lit, assignment, assignment_context, meta] = s;
306306

307307
assignment_context.decision_level_ = ctx.current_decision_level_;
308-
assignment = assignment::on; // always set at on by default. Off will be set by propagation (@incomplete : really ?)
308+
assignment = assignment::on;
309309

310310
log_debug("make decision: level({}) :: {} -> {}", ctx.current_decision_level_, lit.value(), to_string(assignment));
311311
ctx.trail_.push_back((*var_highest_vsids).struct_id());
@@ -314,9 +314,6 @@ bool make_decision(Solver_Context& ctx) {
314314
}
315315

316316
std::expected<solver::result, sat_error> solve_sat(Solver_Context& ctx, const Model& model) {
317-
if (unit_propagation(ctx).has_value())
318-
return std::unexpected(sat_error::unsatisfiable);
319-
320317
solver::result solution;
321318
while (solution.literals.empty()) {
322319
if (ctx.conflict_count_since_last_restart_ >= ctx.config_.restart_threshold) {
@@ -330,12 +327,16 @@ std::expected<solver::result, sat_error> solve_sat(Solver_Context& ctx, const Mo
330327
ctx.config_.restart_threshold *= static_cast<int>(ctx.config_.restart_multiplier);
331328
}
332329

333-
const auto conflict = unit_propagation(ctx);
334-
if (conflict.has_value()) {
330+
if (const auto conflict = unit_propagation(ctx); conflict.has_value()) {
335331
++ctx.conflict_count_since_last_restart_;
336332
++ctx.statistics_.conflicts;
337333
const auto& [learned_clause, backtrack_level] = resolve_conflict(ctx, ctx.clauses_soa_[conflict.value()]);
338334

335+
if (ctx.current_decision_level_ == 0 && backtrack_level == 0) {
336+
log_info("Conflict found on level 0, unsatisfiable");
337+
return std::unexpected(sat_error::unsatisfiable);
338+
}
339+
339340
if (learned_clause.is_empty()) {
340341
log_info("Conflict resolved into an empty clause, unsatisfiable");
341342
return std::unexpected(sat_error::unsatisfiable);
@@ -367,5 +368,4 @@ std::expected<solver::result, sat_error> solve_sat(Solver_Context& ctx, const Mo
367368
return solution;
368369
}
369370

370-
} // namespace impl_details
371-
} // namespace fabko::compiler::sat
371+
} // namespace fabko::compiler::sat::impl_details

tests/CMakeLists.txt

Lines changed: 21 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,31 @@ find_package(Catch2 3 CONFIG REQUIRED)
77
include(CTest)
88
include(Catch)
99

10+
#
1011
# Test of the Agent
1112
#
13+
#add_executable(test_agent)
14+
#target_sources(test_agent
15+
# PRIVATE
16+
# ${CMAKE_CURRENT_SOURCE_DIR}/agent/test_acl.cpp
17+
#)
18+
#target_compile_features(test_agent PUBLIC cxx_std_26)
19+
#target_link_libraries(test_agent
20+
# PRIVATE Catch2::Catch2WithMain fabko::agent)
1221
#
13-
add_executable(test_agent)
14-
target_sources(test_agent
15-
PRIVATE
16-
agent/test_acl.cpp
17-
)
18-
target_compile_features(test_agent PUBLIC cxx_std_26)
19-
target_link_libraries(test_agent
20-
PRIVATE Catch2::Catch2WithMain fabko::agent)
21-
22-
catch_discover_tests(test_agent)
22+
#catch_discover_tests(test_agent)
2323

24-
25-
# Test of the compiler
2624
#
27-
#add_executable(test_core)
28-
#target_sources(test_core
29-
# PRIVATE
30-
#
31-
#)
32-
#target_compile_features(test_core PUBLIC cxx_std_26)
33-
#target_compile_features(test_core
34-
# PRIVATE -DTEST_ASSETS_PATH="${CMAKE_CURRENT_SOURCE_DIR}/_assets")
35-
#target_link_libraries(test_core
36-
# PRIVATE fabko::core Catch2::Catch2WithMain)
25+
# Test of the compiler
3726
#
38-
#catch_discover_tests(test_core)
27+
add_executable(test_compiler)
28+
target_sources(test_compiler
29+
PRIVATE
30+
${CMAKE_CURRENT_SOURCE_DIR}/compiler/soa/watcher_testcase.cpp
31+
)
32+
target_compile_features(test_compiler PUBLIC cxx_std_26)
33+
target_link_libraries(test_compiler
34+
PRIVATE fabko::compiler Catch2::Catch2WithMain)
35+
36+
catch_discover_tests(test_compiler)
3937

0 commit comments

Comments
 (0)