Skip to content

Commit 08da931

Browse files
committed
Add examples directory, integrate parsing into SAT instantiation
1 parent 60d9be3 commit 08da931

File tree

9 files changed

+149
-184
lines changed

9 files changed

+149
-184
lines changed

dimacs.txt

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

examples/complex.dimacs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
c Problem taken from UMiami CS as mentioned in the README
2+
c The mappings from lettered to numeric literals were done
3+
c alphabetically: l=1, m=2, n=3, p=4, q=5, r=6, t=7
4+
c
5+
c The correct answer should be 0000111 respectively to the
6+
c mapping above.
7+
c
8+
c Note: If attempting to debug or look through the decision
9+
c tree on this problem, that these are 0 indexed and l is
10+
c internally represented as 0.
11+
p cnf 7 8
12+
-3 -7 0
13+
2 5 3 0
14+
1 -2 0
15+
-1 -5 0
16+
1 -4 0
17+
6 4 3 0
18+
-6 -1 0
19+
7 0

examples/format-example.dimacs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
c The DIMACS format is pretty limited. You use `c` for comments,
2+
c then start describing the problem using `p`
3+
4+
p cnf 2 2
5+
6+
c Where the `p` signifies that this line is going to set up the
7+
c parameters. Then you specify what format the SAT problem will
8+
c be in (it should _always_ be CNF for this project). And finally,
9+
c you give it two numbers: the number of literals, and the number
10+
c of clauses. After that, each uncommented line should be:
11+
12+
1 -2 0
13+
14+
c The first two numbers are the literals. Negative literals are
15+
c negated. The 0 delineates that the line is over, meaning that
16+
c you could, for instance do something like:
17+
18+
-1
19+
c Comment in the middle
20+
2
21+
0
22+
23+
c This will be properly parsed and output 10 as a possible answer.

examples/simple-unsat.dimacs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
c Example of the simplest unsatisfiable SAT problem.
2+
p cnf 2 4
3+
1 2 0
4+
-1 2 0
5+
1 -2 0
6+
-1 -2 0

examples/simple.dimacs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
c This is a very simple SAT problem where the viable answer
2+
c is either (1:ON and 2:OFF) or (1:OFF and 2:ON)
3+
p cnf 2 2
4+
1 -2 0
5+
-1 2 0

examples/wiki.dimacs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
c Problem taken from the DPLL Wikipedia page as mentioned
2+
c in the README. Mapping from alphabetical to numeric literals
3+
c was done in alphabetical order.
4+
p cnf 4 8
5+
-1 2 3 0
6+
1 3 4 0
7+
1 -3 4 0
8+
1 3 -4 0
9+
1 -3 -4 0
10+
-2 -3 4 0
11+
-1 2 -3 0
12+
-1 -2 3 0

src/main.cpp

Lines changed: 17 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,191 +1,29 @@
1-
#include <stdio.h>
21
#include <vector>
32
#include <iostream>
4-
#include <fstream>
5-
#include <sstream>
63
#include "sat.h"
74

8-
std::optional<std::vector<std::vector<std::tuple<int, bool>>>> parse(std::string path) {
9-
std::ifstream in(path);
10-
std::vector<std::vector<std::tuple<int,bool>>> pc;
11-
12-
std::string dimacsType;
13-
int literals;
14-
int clauses;
15-
for(std::string line; std::getline(in, line);) {
16-
if(line.empty() || line[0] == 'c') continue;
17-
// TODO: Throw exception
18-
if (line[0] != 'p') {
19-
std::cout << "NOT PARAMS" << std::endl;
20-
return std::nullopt;
21-
}
22-
else {
23-
char pch;
24-
std::stringstream ss(line);
25-
ss >> pch >> dimacsType >> literals >> clauses;
26-
if(!ss.eof()) {
27-
// TODO: Throw exception
28-
return std::nullopt;
29-
}
30-
break;
31-
}
32-
}
33-
std::vector<std::tuple<int, bool>> clause;
34-
for(std::string line; std::getline(in, line);) {
35-
if(line.empty() || line[0] == 'c') continue;
36-
if(line[0] == 'p') return std::nullopt;
37-
int literal;
38-
std::stringstream ss(line);
39-
while(ss >> literal && literal != 0)
40-
clause.emplace_back(std::make_tuple(std::abs(literal) - 1, literal < 0));
41-
if(literal == 0) {
42-
pc.push_back(std::move(clause));
43-
clause = {};
44-
}
45-
}
46-
return std::make_optional(pc);
47-
}
48-
49-
int main() {
50-
auto result = parse("/Users/galileo/CLionProjects/sat-solver/dimacs.txt");
51-
if(result.has_value()) {
52-
for (const auto& a : result.value()) {
53-
for(const auto& b : a)
54-
std::cout << "(" << std::get<0>(b) << "," << std::get<1>(b) << ") ";
55-
std::cout << std::endl;
56-
}
57-
}
58-
{
59-
std::cout << "Problem 1: Answer is either 01 or 10\nResult: ";
60-
std::vector<std::tuple<int, bool>> p1{
61-
std::make_tuple(0, false), std::make_tuple(1, true)
62-
};
63-
std::vector<std::tuple<int, bool>> p2{
64-
std::make_tuple(0, true), std::make_tuple(1, false)
65-
};
66-
std::vector<std::vector<std::tuple<int, bool>>> pClauses{
67-
p1, p2
68-
};
69-
auto sat = NaiveSAT(2, pClauses);
70-
auto result = sat.solve();
71-
if (!result.has_value()) {
72-
std::cout << "unsatisfiable";
73-
}
5+
void printResults(const std::optional<std::vector<bool>>& result) {
6+
if (!result.has_value()) {
7+
std::cout << "unsatisfiable" << std::endl;
8+
} else {
749
for (const auto b : result.value()) {
7510
std::cout << b;
7611
}
12+
std::cout << std::endl;
7713
}
78-
{
79-
std::cout << "\n\nProblem 2: Unsatisfiable\nResult: ";
80-
std::vector<std::tuple<int, bool>> q1{
81-
std::make_tuple(0, false), std::make_tuple(1, false)
82-
};
83-
std::vector<std::tuple<int, bool>> q2{
84-
std::make_tuple(0, false), std::make_tuple(1, true)
85-
};
86-
std::vector<std::tuple<int, bool>> q3{
87-
std::make_tuple(0, true), std::make_tuple(1, false)
88-
};
89-
std::vector<std::tuple<int, bool>> q4{
90-
std::make_tuple(0, true), std::make_tuple(1, true)
91-
};
92-
std::vector<std::vector<std::tuple<int, bool>>> qClauses{
93-
q1, q2, q3, q4
94-
};
95-
96-
auto sat = NaiveSAT(4, qClauses);
97-
auto result = sat.solve();
98-
if (!result.has_value()) {
99-
std::cout << "unsatisfiable";
100-
} else {
101-
for (const auto b : result.value()) {
102-
std::cout << b;
103-
}
104-
}
105-
}
106-
107-
{
108-
std::cout << "\n\nProblem 3: Answer should be 0000111\nResult: ";
109-
std::vector<std::tuple<int, bool>> r1{
110-
std::make_tuple(2, true), std::make_tuple(6, true)
111-
};
112-
std::vector<std::tuple<int, bool>> r2{
113-
std::make_tuple(1, false), std::make_tuple(2, false),
114-
std::make_tuple(4, false)//, std::make_tuple(0, true)
115-
};
116-
std::vector<std::tuple<int, bool>> r3{
117-
std::make_tuple(0, false), std::make_tuple(1, true)
118-
};
119-
std::vector<std::tuple<int, bool>> r4{
120-
std::make_tuple(0, true), std::make_tuple(4, true)
121-
};
122-
std::vector<std::tuple<int, bool>> r5{
123-
std::make_tuple(0, false), std::make_tuple(3, true)
124-
};
125-
std::vector<std::tuple<int, bool>> r6{
126-
std::make_tuple(5, false), std::make_tuple(2, false), std::make_tuple(3, false)
127-
};
128-
std::vector<std::tuple<int, bool>> r7{
129-
std::make_tuple(5, true), std::make_tuple(0, true)
130-
};
131-
std::vector<std::tuple<int, bool>> r8{
132-
std::make_tuple(6, false)
133-
};
134-
std::vector<std::vector<std::tuple<int, bool>>> rClauses{
135-
r1, r2, r3, r4, r5, r6, r7, r8
136-
};
137-
138-
auto sat = NaiveSAT(7, rClauses);
139-
auto result = sat.solve();
140-
if (!result.has_value()) {
141-
std::cout << "unsatisfiable";
142-
} else {
143-
for (const auto b : result.value()) {
144-
std::cout << b;
145-
}
146-
}
147-
}
14+
}
14815

149-
{
150-
// Taken from the DPLL Algorithm Wikipedia page
151-
std::cout << "\n\nProblem 4: Answer should be 1111 or 1101\nResult: ";
152-
std::vector<std::tuple<int, bool>> w1{
153-
std::make_tuple(0, true), std::make_tuple(1, false), std::make_tuple(2, false)
154-
};
155-
std::vector<std::tuple<int, bool>> w2{
156-
std::make_tuple(0, false), std::make_tuple(2, false), std::make_tuple(3, false)
157-
};
158-
std::vector<std::tuple<int, bool>> w3{
159-
std::make_tuple(0, false), std::make_tuple(2, false), std::make_tuple(3, true)
160-
};
161-
std::vector<std::tuple<int, bool>> w4{
162-
std::make_tuple(0, false), std::make_tuple(2, true), std::make_tuple(3, false)
163-
};
164-
std::vector<std::tuple<int, bool>> w5{
165-
std::make_tuple(0, false), std::make_tuple(2, true), std::make_tuple(3, true)
166-
};
167-
std::vector<std::tuple<int, bool>> w6{
168-
std::make_tuple(1, true), std::make_tuple(2, true), std::make_tuple(3, false)
169-
};
170-
std::vector<std::tuple<int, bool>> w7{
171-
std::make_tuple(0, true), std::make_tuple(1, false), std::make_tuple(2, true)
172-
};
173-
std::vector<std::tuple<int, bool>> w8{
174-
std::make_tuple(0, true), std::make_tuple(1, true), std::make_tuple(2, false)
175-
};
176-
std::vector<std::vector<std::tuple<int, bool>>> wClauses{
177-
w1, w2, w3, w4, w5, w6, w7, w8
178-
};
16+
int main() {
17+
auto simple = NaiveSAT::parse("examples/simple.dimacs");
18+
printResults(simple.solve());
19+
auto simple_unsat = NaiveSAT::parse("examples/simple-unsat.dimacs");
20+
printResults(simple_unsat.solve());
21+
auto complex = NaiveSAT::parse("examples/complex.dimacs");
22+
printResults(complex.solve());
23+
auto wiki = NaiveSAT::parse("examples/wiki.dimacs");
24+
printResults(wiki.solve());
25+
auto format_example = NaiveSAT::parse("examples/format-example.dimacs");
26+
printResults(format_example.solve());
17927

180-
auto sat = NaiveSAT(4, wClauses);
181-
auto result = sat.solve();
182-
if (!result.has_value()) {
183-
std::cout << "unsatisfiable";
184-
} else {
185-
for (const auto b : result.value()) {
186-
std::cout << b;
187-
}
188-
}
189-
}
19028
return 0;
19129
}

src/sat.cpp

Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include <vector>
33
#include <unordered_map>
44
#include <iostream>
5+
#include <fstream>
6+
#include <sstream>
57

68
std::optional<std::vector<bool>> NaiveSAT::solve() {
79
return(this->solve(this->clauses, this->literals));
@@ -142,3 +144,67 @@ std::vector<std::vector<std::tuple<int, bool>>> NaiveSAT::simplify(std::vector<s
142144
return clauses;
143145
}
144146

147+
/**
148+
* Parse file at path as a DIMACS-compliant file
149+
*
150+
* @param path to DIMACS file
151+
* @return NaiveSAT represenatation of the problem
152+
* @throws std::runtime_exception
153+
*/
154+
NaiveSAT NaiveSAT::parse(const std::string& path) {
155+
std::ifstream in(path);
156+
if(!in.is_open())
157+
throw std::runtime_error("could not open file at " + path);
158+
std::vector<std::vector<std::tuple<int,bool>>> pc;
159+
160+
std::string dimacsType;
161+
int literals;
162+
int clauses;
163+
// First parse until we get to the parameters
164+
for(std::string line; std::getline(in, line);) {
165+
if(line.empty() || line[0] == 'c') continue;
166+
if (line[0] != 'p') {
167+
throw std::runtime_error("found problem contents when expecting parameters");
168+
}
169+
else {
170+
// Throwaway char to read the 'p' into.
171+
char pch;
172+
std::stringstream ss(line);
173+
// Read in the parameters
174+
ss >> pch >> dimacsType >> literals >> clauses;
175+
// Check that that is both the end of that line,
176+
// and that this is a CNF DIMACS file.
177+
if(!ss.eof() || dimacsType != "cnf") {
178+
throw std::runtime_error("received unexpected parameters");
179+
}
180+
break;
181+
}
182+
}
183+
// Now we read each of the clauses
184+
std::vector<std::tuple<int, bool>> clause;
185+
for(std::string line; std::getline(in, line);) {
186+
if(line.empty() || line[0] == 'c') continue;
187+
if(line[0] == 'p') throw std::runtime_error("found parameters when expecting problem contents");
188+
189+
int literal;
190+
std::stringstream ss(line);
191+
// Read into the the clause until we hit the end of the line or a 0
192+
// Anything after the 0 is ignored and treated as a comment.
193+
while(ss >> literal && literal != 0)
194+
clause.emplace_back(std::make_tuple(std::abs(literal) - 1, literal < 0));
195+
// If the above loop ended on the 'end of the line' case
196+
// and not the 0 case, then we need to continue to add to
197+
// the same clause until we hit the 0 case.
198+
199+
// Once we find the 0 delimiter, we move the clause to the end
200+
// of the clauses container, and then reinitialize so it's empty.
201+
if(literal == 0) {
202+
pc.push_back(std::move(clause));
203+
clause = {};
204+
}
205+
}
206+
if(pc.size() != clauses)
207+
throw std::runtime_error("invalid number of clauses");
208+
return NaiveSAT(literals, pc);
209+
}
210+

src/sat.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ class NaiveSAT : SAT {
2222
NaiveSAT(int numLiterals, std::vector<std::vector<std::tuple<int, bool>>> clauses)
2323
: SAT(numLiterals, std::move(clauses)) {};
2424
public:
25+
static NaiveSAT parse(const std::string& path);
2526
std::optional<std::vector<bool>> solve() override;
2627
private:
2728
std::optional<std::vector<bool>> solve(std::vector<std::vector<std::tuple<int,bool>>> clauses, std::vector<bool> literals);

0 commit comments

Comments
 (0)