ruby-minisat 1.14.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.gitignore +24 -0
  2. data/LICENSE +21 -0
  3. data/README.rdoc +56 -0
  4. data/Rakefile +48 -0
  5. data/VERSION +1 -0
  6. data/examples/compat18.rb +65 -0
  7. data/examples/example.rb +26 -0
  8. data/examples/example2.rb +60 -0
  9. data/examples/kakuro.rb +178 -0
  10. data/examples/kakuro.sample +13 -0
  11. data/examples/lonely7.rb +302 -0
  12. data/examples/nonogram.rb +254 -0
  13. data/examples/nonogram.sample +26 -0
  14. data/examples/numberlink.rb +489 -0
  15. data/examples/numberlink.sample +11 -0
  16. data/examples/shikaku.rb +190 -0
  17. data/examples/shikaku.sample +11 -0
  18. data/examples/slitherlink.rb +279 -0
  19. data/examples/slitherlink.sample +11 -0
  20. data/examples/sudoku.rb +216 -0
  21. data/examples/sudoku.sample +11 -0
  22. data/ext/minisat/extconf.rb +9 -0
  23. data/ext/minisat/minisat-wrap.cpp +88 -0
  24. data/ext/minisat/minisat.c +497 -0
  25. data/ext/minisat/minisat.h +53 -0
  26. data/minisat/MiniSat_v1.14.2006-Aug-29.src.zip +0 -0
  27. data/minisat/MiniSat_v1.14/Global.h +274 -0
  28. data/minisat/MiniSat_v1.14/Heap.h +100 -0
  29. data/minisat/MiniSat_v1.14/LICENSE +20 -0
  30. data/minisat/MiniSat_v1.14/Main.C +244 -0
  31. data/minisat/MiniSat_v1.14/Makefile +88 -0
  32. data/minisat/MiniSat_v1.14/README +30 -0
  33. data/minisat/MiniSat_v1.14/Solver.C +781 -0
  34. data/minisat/MiniSat_v1.14/Solver.h +206 -0
  35. data/minisat/MiniSat_v1.14/Solver.o +0 -0
  36. data/minisat/MiniSat_v1.14/SolverTypes.h +130 -0
  37. data/minisat/MiniSat_v1.14/Sort.h +131 -0
  38. data/minisat/MiniSat_v1.14/TODO +73 -0
  39. data/minisat/MiniSat_v1.14/VarOrder.h +96 -0
  40. data/test/test_minisat.rb +143 -0
  41. metadata +114 -0
@@ -0,0 +1,11 @@
1
+ # quoted from PencilBox (http://pencilbox.sourceforge.jp/)
2
+ 3 2 . . 3 2 . . 2 0
3
+ . . 2 1 . . 3 2 . .
4
+ 3 . . . 2 . . . 2 .
5
+ . 1 3 . . 3 1 . . 3
6
+ . . . 0 1 . . 3 1 1
7
+ 1 1 2 . . 3 3 . . .
8
+ 3 . . 3 2 . . 2 1 .
9
+ . 2 . . . 1 . . . 3
10
+ . . 0 1 . . 1 3 . .
11
+ 2 2 . . 2 3 . . 1 2
@@ -0,0 +1,216 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ruby-minisat example -- sudoku.rb
4
+ # ref: http://en.wikipedia.org/wiki/Sudoku
5
+
6
+ ##
7
+ ## SAT configuration:
8
+ ## - Variables: assign to each cell the same number variables as puzzle size
9
+ ## - Clauses: sudoku rules (below)
10
+ ##
11
+ ##
12
+ ## sudoku basic rules:
13
+ ## - rule 1. There is at least one number in each entry
14
+ ## - rule 2. Each number appears at most once in each row
15
+ ## - rule 3. Each number appears at most once in each column
16
+ ## - rule 4. Each number appears at most once in each sub-grid
17
+ ##
18
+ ## sudoku auxiliary rules (for speed up):
19
+ ## - auxiliary rule 1. There is at most one number in each entry
20
+ ## - auxiliary rule 2. Each number appears at least once in each row
21
+ ## - auxiliary rule 3. Each number appears at least once in each column
22
+ ## - auxiliary rule 4. Each number appears at least once in each sub-grid
23
+ ##
24
+ ##
25
+ ## see [1] in detail.
26
+ ##
27
+ ## [1] I. Lynce, and J. Ouaknine. Sudoku as a SAT Problem. Proceedings of the
28
+ ## Ninth International Symposium on Artificial Intelligence and Mathematics
29
+ ## (AIMATH 2006), Jan. 2006
30
+ ##
31
+
32
+ require "minisat"
33
+ require File.dirname($0) + "/compat18" if RUBY_VERSION < "1.9.0"
34
+
35
+
36
+ def error(msg)
37
+ $stderr.puts msg
38
+ exit 1
39
+ end
40
+
41
+
42
+ def parse_file(file)
43
+ s_puzz = w_grid = h_grid = nil
44
+ field = []
45
+ File.read(file).split(/\n/).each do |line|
46
+ case line
47
+ when /^\s*#.*$/, /^\s*$/
48
+ when /^\s*grid-size\s*:\s*(\d+)x(\d+)\s*$/
49
+ w_grid = $1.to_i
50
+ h_grid = $2.to_i
51
+ else
52
+ line = line.split.map {|n| n.to_i }
53
+ s_puzz ||= line.size
54
+ unless s_puzz == line.size
55
+ error "illegal width: row #{ field.size + 1 }"
56
+ end
57
+ field << line
58
+ end
59
+ end
60
+ error "illegal height" unless field.size == s_puzz
61
+ unless w_grid * h_grid == s_puzz
62
+ error "illegal size: #{ s_puzz } != #{ w_grid } * #{ h_grid }"
63
+ end
64
+
65
+ a_puzz = (0...s_puzz).to_a
66
+ a_grid = (0...s_puzz).step(w_grid).to_a.
67
+ product((0...s_puzz).step(h_grid).to_a)
68
+ a_off = (0...w_grid).to_a.product((0...h_grid).to_a)
69
+
70
+ [a_puzz, a_grid, a_off, field]
71
+ end
72
+
73
+
74
+ def define_sat(solver, a_puzz, a_grid, a_off, field)
75
+ # define variables
76
+ vars = a_puzz.map {|x| a_puzz.map {|y| a_puzz.map {|z| solver.new_var }}}
77
+
78
+ # define clauses
79
+ a_puzz.each do |x|
80
+ a_puzz.each do |y|
81
+ # rule 1
82
+ solver << a_puzz.map {|z| vars[x][y][z] }
83
+
84
+ # auxiliary rule 1
85
+ a_puzz.combination(2) do |z1, z2|
86
+ solver << [-vars[x][y][z1], -vars[x][y][z2]]
87
+ end
88
+ end
89
+ end
90
+ a_puzz.each do |z|
91
+ a_puzz.each do |i|
92
+ a_puzz.combination(2) do |j, k|
93
+ # rule 2
94
+ solver << [-vars[j][i][z], -vars[k][i][z]]
95
+
96
+ # rule 3
97
+ solver << [-vars[i][j][z], -vars[i][k][z]]
98
+ end
99
+
100
+ # auxiliary rule 2
101
+ solver << a_puzz.map {|j| vars[j][i][z] }
102
+
103
+ # auxiliary rule 3
104
+ solver << a_puzz.map {|j| vars[i][j][z] }
105
+ end
106
+
107
+ a_grid.each do |xg, yg|
108
+ a_off.combination(2) do |(xo1, yo1), (xo2, yo2)|
109
+ # rule 4
110
+ solver << [-vars[xg + xo1][yg + yo1][z], -vars[xg + xo2][yg + yo2][z]]
111
+ end
112
+
113
+ # auxiliary rule 4
114
+ solver << a_off.map {|xo, yo| vars[xg + xo][yg + yo][z] }
115
+ end
116
+ end
117
+
118
+ vars
119
+ end
120
+
121
+
122
+ def make_assumptions(vars, a_puzz, a_grid, a_off, field)
123
+ # translate fixed cells into assumption
124
+ assumps = []
125
+
126
+ field.zip(a_puzz) do |line, y|
127
+ line.zip(a_puzz) do |n, x|
128
+ next if n == 0
129
+ a_puzz.each do |z|
130
+ v = vars[x][y][z]
131
+ assumps << (z == n - 1 ? v : -v)
132
+ end
133
+ end
134
+ end
135
+
136
+ assumps
137
+ end
138
+
139
+
140
+ def solve_sat(solver, assumps)
141
+ start = Time.now
142
+ result = solver.solve(*assumps)
143
+ eplise = Time.now - start
144
+ puts "time: %.6f sec." % eplise
145
+ result
146
+ end
147
+
148
+
149
+ def make_solution(solver, vars, a_puzz, a_grid, a_off, field)
150
+ a_puzz.map do |y|
151
+ a_puzz.map do |x|
152
+ 1 + a_puzz.find {|z| solver[vars[x][y][z]] }
153
+ end
154
+ end
155
+ end
156
+
157
+
158
+ def add_constraint(solver, vars)
159
+ solver << vars.flatten.map {|v| solver[v] ? -v : v }
160
+ end
161
+
162
+
163
+ def output_field(field)
164
+ puts field.map {|l| " " + l.map {|c| c == 0 ? "." : c }.join(" ") }
165
+ end
166
+
167
+
168
+
169
+ error "usage: sudoku.rb sudoku.sample" if ARGV.empty?
170
+
171
+ ARGV.each do |file|
172
+ sudoku = parse_file(file)
173
+ puts "problem:"
174
+ output_field(sudoku.last)
175
+ puts
176
+
177
+ solver = MiniSat::Solver.new
178
+
179
+ puts "defining SAT..."
180
+ vars = define_sat(solver, *sudoku)
181
+ puts "variables : #{ solver.var_size }"
182
+ puts "clauses : #{ solver.clause_size }"
183
+ puts
184
+
185
+ puts "translating fixed cells into assumptions..."
186
+ assumps = make_assumptions(vars, *sudoku)
187
+ puts "assumptions : #{ assumps.size }"
188
+ puts
189
+
190
+ puts "solving SAT..."
191
+ result = solve_sat(solver, assumps)
192
+ puts "result: " + (result ? "solvable" : "unsolvable")
193
+ puts
194
+ next unless result
195
+
196
+ puts "translating model into solution..."
197
+ solution = make_solution(solver, vars, *sudoku)
198
+ puts "solution found:"
199
+ output_field(solution)
200
+ puts
201
+
202
+ puts "checking different solution..."
203
+ add_constraint(solver, vars)
204
+ result = solve_sat(solver, assumps)
205
+ puts "result: " +
206
+ (result ? "different solution found" : "different solution not found")
207
+ puts
208
+ next unless result
209
+
210
+ puts "translating model into solution..."
211
+ solution = make_solution(solver, vars, *sudoku)
212
+ puts "different solution:"
213
+ output_field(solution)
214
+ puts
215
+ puts
216
+ end
@@ -0,0 +1,11 @@
1
+ # quoted from PencilBox (http://pencilbox.sourceforge.jp/)
2
+ grid-size: 3x3
3
+ 1 . . . . . . . 3
4
+ . 9 . . 5 . . 4 .
5
+ . . 8 . . 7 9 . .
6
+ . . . 3 . 1 2 . .
7
+ . 6 . . . . . 1 .
8
+ . . 9 4 . 8 . . .
9
+ . . 1 6 . . 8 . .
10
+ . 4 . . 9 . . 6 .
11
+ 2 . . . . . . . 1
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ CONFIG["CXX"] ||= "g++"
4
+ MINISAT_DIR = File.join(File.dirname(__FILE__), "../../minisat/MiniSat_v1.14")
5
+
6
+ minisat_include, _ = dir_config("minisat", MINISAT_DIR, "")
7
+ $objs = ["minisat.o", "minisat-wrap.o", minisat_include + "/Solver.o"]
8
+
9
+ create_makefile("minisat") if have_library("stdc++")
@@ -0,0 +1,88 @@
1
+ /******************************************************************************
2
+
3
+ ruby-minisat -- ruby binding for MiniSat
4
+
5
+ *******************************************************************************
6
+
7
+ The MIT License
8
+
9
+ Copyright (c) 2007 Yusuke Endoh
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in
19
+ all copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
+ THE SOFTWARE.
28
+
29
+ ******************************************************************************/
30
+
31
+
32
+ #include "Solver.h"
33
+ #include "minisat.h"
34
+
35
+ /***** variable **************************************************************/
36
+
37
+ extern "C" int wrap_lit_pos_var(int v) {
38
+ return index(Lit(v, false));
39
+ }
40
+
41
+ extern "C" int wrap_lit_neg_var(int v) {
42
+ return index(Lit(v, true));
43
+ }
44
+
45
+ /***** solver ****************************************************************/
46
+
47
+ extern "C" wrap_solver wrap_solver_new() {
48
+ return (wrap_solver) new Solver();
49
+ }
50
+
51
+ extern "C" void wrap_solver_free(wrap_solver slv) {
52
+ delete (Solver*) slv;
53
+ }
54
+
55
+ extern "C" int wrap_solver_new_var(wrap_solver slv) {
56
+ return ((Solver*) slv)->newVar();
57
+ }
58
+
59
+ extern "C" int wrap_solver_add_clause(wrap_solver slv, int *lits, int len) {
60
+ vec<Lit> lit_vec;
61
+ for(int i = 0; i < len; i++) lit_vec.push(toLit(lits[i]));
62
+ ((Solver*) slv)->addClause(lit_vec);
63
+ return ((Solver*) slv)->okay() ? 1 : 0;
64
+ }
65
+
66
+ extern "C" int wrap_solver_ref_var(wrap_solver slv, int var) {
67
+ lbool b = ((Solver*) slv)->model[var];
68
+ return (b == l_False) ? 0 : (b == l_True) ? 1 : 2;
69
+ }
70
+
71
+ extern "C" int wrap_solver_solve(wrap_solver slv, int *lits, int len) {
72
+ vec<Lit> lit_vec;
73
+ for(int i = 0; i < len; i++) lit_vec.push(toLit(lits[i]));
74
+ return ((Solver*) slv)->solve(lit_vec) ? 1 : 0;
75
+ }
76
+
77
+ extern "C" int wrap_solver_simplify_db(wrap_solver slv) {
78
+ ((Solver*) slv)->simplifyDB();
79
+ return ((Solver*) slv)->okay() ? 1 : 0;
80
+ }
81
+
82
+ extern "C" int wrap_solver_var_size(wrap_solver slv) {
83
+ return ((Solver*) slv)->nVars();
84
+ }
85
+
86
+ extern "C" int wrap_solver_clause_size(wrap_solver slv) {
87
+ return ((Solver*) slv)->nClauses();
88
+ }
@@ -0,0 +1,497 @@
1
+ /******************************************************************************
2
+
3
+ ruby-minisat -- ruby binding for MiniSat
4
+
5
+ *******************************************************************************
6
+
7
+ The MIT License
8
+
9
+ Copyright (c) 2007, 2010 Yusuke Endoh
10
+
11
+ Permission is hereby granted, free of charge, to any person obtaining a copy
12
+ of this software and associated documentation files (the "Software"), to deal
13
+ in the Software without restriction, including without limitation the rights
14
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
15
+ copies of the Software, and to permit persons to whom the Software is
16
+ furnished to do so, subject to the following conditions:
17
+
18
+ The above copyright notice and this permission notice shall be included in
19
+ all copies or substantial portions of the Software.
20
+
21
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
22
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
24
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
26
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
27
+ THE SOFTWARE.
28
+
29
+ ******************************************************************************/
30
+
31
+ /*
32
+ * Author:: Yusuke Endoh
33
+ * Date:: 2010/07/07
34
+ * Copyright:: Copyright (c) 2007, 2010 Yusuke Endoh
35
+ */
36
+
37
+ #include "ruby.h"
38
+ #include "minisat.h"
39
+
40
+ VALUE rb_mMiniSat, rb_cSolver, rb_cVariable, rb_cLiteral;
41
+
42
+ #define SATISFIED 0 /* satisfied */
43
+ #define NOT_SOLVED_YET 1 /* not solved yet */
44
+ #define UNSATISFIABLE 2 /* always unsatisfiable */
45
+ #define UNSATISFIABLE_UNDER_ASSUMPTIONS 3 /* unsatisfiable under assumptions */
46
+
47
+ /***** type def **************************************************************/
48
+
49
+ typedef struct csolver_tag {
50
+ wrap_solver *solver;
51
+ int result;
52
+ int clause_count;
53
+ } csolver;
54
+
55
+ typedef struct cvariable_tag {
56
+ int value;
57
+ VALUE solver;
58
+ } cvariable;
59
+
60
+ static void check_model_available(int result, int check_only_unsatisfied)
61
+ {
62
+ switch(result) {
63
+ case NOT_SOLVED_YET:
64
+ if(!check_only_unsatisfied) {
65
+ rb_raise(rb_eRuntimeError, "not solved yet");
66
+ }
67
+ break;
68
+ case UNSATISFIABLE:
69
+ rb_raise(rb_eRuntimeError, "unsatisfied");
70
+ break;
71
+ case UNSATISFIABLE_UNDER_ASSUMPTIONS:
72
+ if(!check_only_unsatisfied) {
73
+ rb_raise(rb_eRuntimeError, "unsatisfied under assumption");
74
+ }
75
+ break;
76
+ }
77
+ }
78
+
79
+
80
+ /***** variable **************************************************************/
81
+
82
+ static void value_free(cvariable *cvar)
83
+ {
84
+ free(cvar);
85
+ }
86
+
87
+ static void value_mark(cvariable *cval)
88
+ {
89
+ rb_gc_mark(cval->solver);
90
+ }
91
+
92
+ /*
93
+ * call-seq:
94
+ * +variable -> Literal
95
+ *
96
+ * Returns positive literal of variable.
97
+ *
98
+ */
99
+ static VALUE variable_pos(VALUE rvar)
100
+ {
101
+ cvariable *cvar, *clit;
102
+ VALUE rlit;
103
+
104
+ Data_Get_Struct(rvar, cvariable, cvar);
105
+ rlit = Data_Make_Struct(rb_cLiteral, cvariable, value_mark, value_free, clit);
106
+ clit->value = wrap_lit_pos_var(cvar->value);
107
+ clit->solver = cvar->solver;
108
+ if(OBJ_TAINTED(rvar)) OBJ_TAINT(rlit);
109
+
110
+ return rlit;
111
+ }
112
+
113
+ /*
114
+ * call-seq:
115
+ * -variable -> Literal
116
+ *
117
+ * Returns negative literal of variable.
118
+ *
119
+ */
120
+ static VALUE variable_neg(VALUE rvar)
121
+ {
122
+ cvariable *cvar, *clit;
123
+ VALUE rlit;
124
+
125
+ Data_Get_Struct(rvar, cvariable, cvar);
126
+ rlit = Data_Make_Struct(rb_cLiteral, cvariable, value_mark, value_free, clit);
127
+ clit->value = wrap_lit_neg_var(cvar->value);
128
+ clit->solver = cvar->solver;
129
+ if(OBJ_TAINTED(rvar)) OBJ_TAINT(rlit);
130
+
131
+ return rlit;
132
+ }
133
+
134
+ /*
135
+ * call-seq:
136
+ * variable.value -> true or false
137
+ *
138
+ * Returns an assignment if the SAT is satisfiable. Raises an exception when
139
+ * the SAT is not satisfied.
140
+ *
141
+ */
142
+ static VALUE variable_value(VALUE rvar)
143
+ {
144
+ cvariable *cvar;
145
+ csolver *cslv;
146
+
147
+ Data_Get_Struct(rvar, cvariable, cvar);
148
+ Data_Get_Struct(cvar->solver, csolver, cslv);
149
+ check_model_available(cslv->result, 0);
150
+ switch(wrap_solver_ref_var(cslv->solver, cvar->value)) {
151
+ case 0: return Qfalse;
152
+ case 1: return Qtrue;
153
+ }
154
+
155
+ return Qnil;
156
+ }
157
+
158
+ static void convert_lits(int *lits, int argc, VALUE *argv, VALUE rslv)
159
+ {
160
+ int i;
161
+ VALUE rval;
162
+ cvariable *cval;
163
+
164
+ for(i = 0; i < argc; i++) {
165
+ rval = argv[i];
166
+ if(TYPE(rval) != T_DATA
167
+ || RDATA(rval)->dfree != (RUBY_DATA_FUNC)value_free) {
168
+ rb_raise(rb_eTypeError,
169
+ "wrong argument type %s (expected Variable or Literal)",
170
+ rb_obj_classname(rval));
171
+ }
172
+ Data_Get_Struct(rval, cvariable, cval);
173
+ if(cval->solver != rslv) {
174
+ rb_raise(rb_eArgError,
175
+ "Variable or Literal of different solver");
176
+ }
177
+ lits[i] = (CLASS_OF(rval) == rb_cVariable) ?
178
+ wrap_lit_pos_var(cval->value) : cval->value;
179
+ }
180
+ }
181
+
182
+
183
+ /***** solver ****************************************************************/
184
+
185
+ static void solver_free(csolver *cslv)
186
+ {
187
+ wrap_solver_free(cslv->solver);
188
+ free(cslv);
189
+ }
190
+
191
+ static VALUE solver_alloc(VALUE klass)
192
+ {
193
+ csolver *cslv;
194
+ VALUE rslv;
195
+
196
+ rslv = Data_Make_Struct(klass, csolver, NULL, solver_free, cslv);
197
+ cslv->solver = wrap_solver_new();
198
+ cslv->result = NOT_SOLVED_YET;
199
+
200
+ return rslv;
201
+ }
202
+
203
+ /*
204
+ * call-seq:
205
+ * solver.new_var -> Variable
206
+ *
207
+ * Returns new variable for constructing SAT formula. Raises an exception when
208
+ * the SAT is already prove to be always unsatisfiable.
209
+ *
210
+ */
211
+ static VALUE solver_new_var(VALUE rslv)
212
+ {
213
+ csolver *cslv;
214
+ cvariable *cvar;
215
+ VALUE rvar;
216
+
217
+ Data_Get_Struct(rslv, csolver, cslv);
218
+ check_model_available(cslv->result, 1);
219
+ rvar =
220
+ Data_Make_Struct(rb_cVariable, cvariable, value_mark, value_free, cvar);
221
+ cvar->value = wrap_solver_new_var(cslv->solver);
222
+ cvar->solver = rslv;
223
+ cslv->result = NOT_SOLVED_YET;
224
+ if(OBJ_TAINTED(rslv)) OBJ_TAINT(rvar);
225
+
226
+ return rvar;
227
+ }
228
+
229
+ /*
230
+ * call-seq:
231
+ * solver.add_clause(var, lit,...) -> solver
232
+ *
233
+ * Adds clause consisting of the literals to the SAT and returns solver
234
+ * itself. If Variables are passed, they are handled as its positive Literal.
235
+ * The SAT may be automatically proved to be always unsatisfiable just after
236
+ * the clause added. In the case, <code>solver.solved?</code> becomes to
237
+ * return true.
238
+ *
239
+ * solver.add_clause(a, b, -c) # add clause: (a or b or not c)
240
+ *
241
+ */
242
+ static VALUE solver_add_clause(int argc, VALUE *argv, VALUE rslv)
243
+ {
244
+ csolver *cslv;
245
+ int *lits;
246
+
247
+ Data_Get_Struct(rslv, csolver, cslv);
248
+ lits = ALLOCA_N(int, argc);
249
+ convert_lits(lits, argc, argv, rslv);
250
+ if(!wrap_solver_add_clause(cslv->solver, lits, argc)) {
251
+ cslv->result = UNSATISFIABLE;
252
+ }
253
+ else {
254
+ cslv->result = NOT_SOLVED_YET;
255
+ cslv->clause_count++;
256
+ }
257
+
258
+ return rslv;
259
+ }
260
+
261
+ /*
262
+ * call-seq:
263
+ * solver << var_or_lit_or_ary -> solver
264
+ *
265
+ * Almost same as Solver#add_caluse. This method receives Array of Variable
266
+ * or Literal.
267
+ *
268
+ * solver << a # equivalent to solver.add_clause(a)
269
+ * solver << [a, b, -c] # equivalent to solver.add_clause(a, b, -c)
270
+ *
271
+ */
272
+ static VALUE solver_add_clause_2(VALUE rslv, VALUE rcls)
273
+ {
274
+ if(TYPE(rcls) == T_DATA
275
+ && RDATA(rcls)->dfree == (RUBY_DATA_FUNC)value_free) {
276
+ return solver_add_clause(1, &rcls, rslv);
277
+ }
278
+ else {
279
+ rcls = rb_convert_type(rcls, T_ARRAY, "Array", "to_ary");
280
+ return solver_add_clause(RARRAY_LEN(rcls), RARRAY_PTR(rcls), rslv);
281
+ }
282
+ }
283
+
284
+ /*
285
+ * call-seq:
286
+ * solver[var] -> true or false
287
+ *
288
+ * Returns a value of specified variable if the SAT is satisfied. Raises an
289
+ * exception if not.
290
+ *
291
+ */
292
+ static VALUE solver_ref_var(VALUE rslv, VALUE rvar)
293
+ {
294
+ csolver *cslv;
295
+ cvariable *cvar;
296
+
297
+ Data_Get_Struct(rslv, csolver, cslv);
298
+ if(CLASS_OF(rvar) != rb_cVariable) {
299
+ rb_raise(rb_eTypeError,
300
+ "wrong argument type %s (expected Variable)",
301
+ rb_obj_classname(rvar));
302
+ }
303
+ check_model_available(cslv->result, 0);
304
+ Data_Get_Struct(rvar, cvariable, cvar);
305
+ switch(wrap_solver_ref_var(cslv->solver, cvar->value)) {
306
+ case 0: return Qfalse;
307
+ case 1: return Qtrue;
308
+ }
309
+
310
+ return Qnil;
311
+ }
312
+
313
+ /*
314
+ * call-seq:
315
+ * solver.solve -> true or false
316
+ *
317
+ * Determines whether the SAT is satisfiable or not. Returns true if the SAT
318
+ * is satisfied, false otherwise.
319
+ *
320
+ */
321
+ static VALUE solver_solve(int argc, VALUE *argv, VALUE rslv)
322
+ {
323
+ csolver *cslv;
324
+ int *lits;
325
+
326
+ Data_Get_Struct(rslv, csolver, cslv);
327
+ lits = ALLOCA_N(int, argc);
328
+ convert_lits(lits, argc, argv, rslv);
329
+ if(wrap_solver_solve(cslv->solver, lits, argc)) {
330
+ cslv->result = SATISFIED;
331
+ return Qtrue;
332
+ }
333
+ else {
334
+ cslv->result =
335
+ argc == 0 ? UNSATISFIABLE : UNSATISFIABLE_UNDER_ASSUMPTIONS;
336
+ return Qfalse;
337
+ }
338
+ }
339
+
340
+ /*
341
+ * call-seq:
342
+ * solver.simplify_db -> true or false
343
+ *
344
+ * Detects conflicts independent of the assumptions. This is useful when the
345
+ * same SAT is solved many times under some different assumptions.
346
+ *
347
+ */
348
+ static VALUE solver_simplify_db(VALUE rslv)
349
+ {
350
+ csolver *cslv;
351
+
352
+ Data_Get_Struct(rslv, csolver, cslv);
353
+
354
+ check_model_available(cslv->result, 0);
355
+ if(!wrap_solver_simplify_db(cslv->solver)) {
356
+ cslv->result = UNSATISFIABLE;
357
+ return Qfalse;
358
+ }
359
+
360
+ return Qtrue;
361
+ }
362
+
363
+ /*
364
+ * call-seq:
365
+ * solver.var_size -> integer
366
+ *
367
+ * Returns the count of defined variables.
368
+ *
369
+ */
370
+ static VALUE solver_var_size(VALUE rslv)
371
+ {
372
+ csolver *cslv;
373
+
374
+ Data_Get_Struct(rslv, csolver, cslv);
375
+
376
+ return LONG2NUM(wrap_solver_var_size(cslv->solver));
377
+ }
378
+
379
+ /*
380
+ * call-seq:
381
+ * solver.clause_size -> integer
382
+ *
383
+ * Returns the count of added clauses.
384
+ *
385
+ */
386
+ static VALUE solver_clause_size(VALUE rslv)
387
+ {
388
+ csolver *cslv;
389
+
390
+ Data_Get_Struct(rslv, csolver, cslv);
391
+
392
+ return LONG2NUM(cslv->clause_count);
393
+ }
394
+
395
+ /*
396
+ * call-seq:
397
+ * solver.to_s -> string
398
+ *
399
+ * Creates a printable version of solver.
400
+ *
401
+ * p solver #=> #<MiniSat::Solver:0xb7d3c1d0 not solved yet>
402
+ * p solver #=> #<MiniSat::Solver:0xb7d3c1d0 satisfied>
403
+ * p solver #=> #<MiniSat::Solver:0xb7d3c1d0 unsatisfiable>
404
+ */
405
+ static VALUE solver_to_s(VALUE rslv)
406
+ {
407
+ const char *cname = rb_obj_classname(rslv);
408
+ const char *msg = NULL;
409
+ char *buf;
410
+ csolver *cslv;
411
+ VALUE str;
412
+ size_t len;
413
+
414
+ Data_Get_Struct(rslv, csolver, cslv);
415
+ switch(cslv->result) {
416
+ case NOT_SOLVED_YET:
417
+ msg = "not solved yet";
418
+ break;
419
+ case SATISFIED:
420
+ msg = "satisfied";
421
+ break;
422
+ case UNSATISFIABLE:
423
+ msg = "unsatisfiable";
424
+ break;
425
+ case UNSATISFIABLE_UNDER_ASSUMPTIONS:
426
+ msg = "unsatisfiable under assumptions";
427
+ break;
428
+ }
429
+ len = strlen(cname) + strlen(msg) + 6 + 16;
430
+ buf = ALLOCA_N(char, len);
431
+ snprintf(buf, len + 1, "#<%s:%p %s>", cname, (void*)rslv, msg);
432
+ str = rb_str_new2(buf);
433
+ if(OBJ_TAINTED(rslv)) OBJ_TAINT(str);
434
+
435
+ return str;
436
+ }
437
+
438
+ /*
439
+ * call-seq:
440
+ * solver.solved? -> true or false
441
+ *
442
+ * Returns true if the SAT is solved, or false if not.
443
+ *
444
+ */
445
+ static VALUE solver_solved_p(VALUE rslv)
446
+ {
447
+ csolver *cslv;
448
+
449
+ Data_Get_Struct(rslv, csolver, cslv);
450
+
451
+ return (cslv->result != NOT_SOLVED_YET ? Qtrue : Qfalse);
452
+ }
453
+
454
+ /*
455
+ * call-seq:
456
+ * solver.satisfied? -> true or false
457
+ *
458
+ * Returns true if the SAT is satisfied, or false if not.
459
+ *
460
+ */
461
+ static VALUE solver_satisfied_p(VALUE rslv)
462
+ {
463
+ csolver *cslv;
464
+
465
+ Data_Get_Struct(rslv, csolver, cslv);
466
+
467
+ return (cslv->result == SATISFIED ? Qtrue : Qfalse);
468
+ }
469
+
470
+
471
+ void Init_minisat()
472
+ {
473
+ rb_mMiniSat = rb_define_module("MiniSat");
474
+
475
+ rb_cSolver = rb_define_class_under(rb_mMiniSat, "Solver", rb_cObject);
476
+ rb_define_alloc_func(rb_cSolver, solver_alloc);
477
+ rb_define_method(rb_cSolver, "new_var", solver_new_var, 0);
478
+ rb_define_method(rb_cSolver, "add_clause", solver_add_clause, -1);
479
+ rb_define_method(rb_cSolver, "<<", solver_add_clause_2, 1);
480
+ rb_define_method(rb_cSolver, "[]", solver_ref_var, 1);
481
+ rb_define_method(rb_cSolver, "solve", solver_solve, -1);
482
+ rb_define_method(rb_cSolver, "simplify_db", solver_simplify_db, 0);
483
+ rb_define_method(rb_cSolver, "var_size", solver_var_size, 0);
484
+ rb_define_method(rb_cSolver, "clause_size", solver_clause_size, 0);
485
+ rb_define_method(rb_cSolver, "to_s", solver_to_s, 0);
486
+ rb_define_method(rb_cSolver, "solved?", solver_solved_p, 0);
487
+ rb_define_method(rb_cSolver, "satisfied?", solver_satisfied_p, 0);
488
+
489
+ rb_cVariable = rb_define_class_under(rb_mMiniSat, "Variable", rb_cObject);
490
+ rb_undef_method(CLASS_OF(rb_cVariable), "new");
491
+ rb_define_method(rb_cVariable, "+@", variable_pos, 0);
492
+ rb_define_method(rb_cVariable, "-@", variable_neg, 0);
493
+ rb_define_method(rb_cVariable, "value", variable_value, 0);
494
+
495
+ rb_cLiteral = rb_define_class_under(rb_mMiniSat, "Literal", rb_cObject);
496
+ rb_undef_method(CLASS_OF(rb_cLiteral), "new");
497
+ }