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,13 @@
1
+ # quoted from PencilBox (http://pencilbox.sourceforge.jp/)
2
+ **\** **\** **\** 03\** 05\** 15\** **\** **\** 20\** 16\** **\** **\**
3
+ **\** **\** 07\07 . . . 08\** **\17 . . 35\** **\**
4
+ **\** 04\15 . . . . . 19\24 . . . 24\**
5
+ **\05 . . 10\** **\10 . . . . **\17 . .
6
+ **\07 . . . 06\10 . . . 12\** 12\15 . .
7
+ **\** **\** 15\06 . . . **\35 . . . . .
8
+ **\** 10\06 . . . 08\** **\** 35\10 . . . **\**
9
+ **\15 . . . . . 20\09 . . . 12\** 06\**
10
+ **\07 . . **\** 06\18 . . . **\06 . . .
11
+ **\08 . . 03\20 . . . . 15\** 16\07 . .
12
+ **\** **\08 . . . **\33 . . . . . **\**
13
+ **\** **\** **\04 . . **\** **\23 . . . **\** **\**
@@ -0,0 +1,302 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ruby-minisat example -- lonely7.rb
4
+
5
+
6
+ require "minisat"
7
+ require File.dirname($0) + "/compat18" if RUBY_VERSION < "1.9.0"
8
+
9
+
10
+ ###############################################################################
11
+
12
+
13
+ class Digit
14
+ def initialize
15
+ @vars = (0..9).map { $solver.new_var }
16
+ $solver << vars
17
+ vars.combination(2) {|v1, v2| $solver << [-v1, -v2] }
18
+ end
19
+
20
+ attr_reader :vars
21
+
22
+ def is(n)
23
+ case n
24
+ when Digit
25
+ vars.zip(n.vars) {|v1, v2| $solver << [v1, -v2] << [-v1, v2] }
26
+ when Numeric
27
+ $solver << vars[n]
28
+ else raise TypeError
29
+ end
30
+ end
31
+
32
+ def is_not(n)
33
+ case n
34
+ when Digit
35
+ vars.zip(n.vars) {|v1, v2| $solver << [-v1, -v2] }
36
+ when Numeric
37
+ $solver << -vars[n]
38
+ else raise TypeError
39
+ end
40
+ end
41
+
42
+ def sum(a, b, f = nil)
43
+ sumdiff(a, b, f) {|na, nb| na + nb }
44
+ end
45
+
46
+ def diff(a, b, f = nil)
47
+ sumdiff(a, b, f) {|na, nb| na - nb }
48
+ end
49
+
50
+ def sumdiff(a, b, f = nil)
51
+ c = f ? Digit.new : self
52
+ f2 = pair(a, b, c) {|na, nb| n = yield(na, nb); [n % 10, n % 10 != n] }
53
+ f ? flag(c, f, f2) {|n| n = yield(n, 1); [n % 10, n % 10 != n] } : f2
54
+ end
55
+
56
+ def prod(a, b, d)
57
+ if b.is_a?(Numeric)
58
+ b2 = Digit.new
59
+ b2.is b
60
+ b = b2
61
+ end
62
+ a.vars.each_with_index do |va, na|
63
+ b.vars.each_with_index do |vb, nb|
64
+ nd, nc = (na * nb).divmod(10)
65
+ $solver << [-va, -vb, vars[nc]] << [-va, -vb, d.vars[nd]]
66
+ end
67
+ end
68
+ end
69
+
70
+ def less(b, f)
71
+ fs = (0..9).map { $solver.new_var }
72
+ fr = $solver.new_var
73
+ b.vars.each_with_index do |vb, nb|
74
+ va = vars[nb]
75
+ vf = fs[nb]
76
+ $solver << [-f, -vb] + vars[0..nb]
77
+ $solver << [-vb, -va, vf] << [-vf, va] << [-vf, vb] << [-vf, fr]
78
+ end
79
+ $solver << [-fr, f] << [-fr] + fs
80
+ fr
81
+ end
82
+
83
+ def pair(a, b, c)
84
+ f = $solver.new_var
85
+ a.vars.each_with_index do |va, na|
86
+ b.vars.each_with_index do |vb, nb|
87
+ nc, f2 = yield(na, nb)
88
+ $solver << [-va, -vb, c.vars[nc]] << [-va, -vb, f2 ? f : -f]
89
+ end
90
+ end
91
+ f
92
+ end
93
+
94
+ def flag(a, f0, f1)
95
+ f2 = $solver.new_var
96
+ $solver << [f0, -f2]
97
+ a.vars.each_with_index do |v, n|
98
+ $solver << [-v, f0, vars[n]]
99
+ n2, f = yield n
100
+ $solver << [-v, -f0, vars[n2]] << [-v, -f0, f ? f2 : -f2]
101
+ end
102
+
103
+ f3 = $solver.new_var
104
+ $solver << [-f1, f3] << [-f2, f3] << [-f3, f1, f2]
105
+ f3
106
+ end
107
+
108
+ def to_i
109
+ (0..9).find {|n| $solver[vars[n]] }.to_i
110
+ end
111
+ end
112
+
113
+ class Digits
114
+ def initialize(n)
115
+ @digits = n.is_a?(Numeric) ? (0...n).map { Digit.new } : n
116
+ end
117
+
118
+ attr_reader :digits
119
+
120
+ def is(num)
121
+ case num
122
+ when Adder, Subtracter
123
+ f = nil
124
+ z = zero
125
+ ms = [num.left, num.right, self].
126
+ max {|x, y| x.digits.size <=> y.digits.size }
127
+ as = ms.extend(num.left.digits, z)
128
+ bs = ms.extend(num.right.digits, z)
129
+ cs = ms.extend(digits, z)
130
+ as.zip(bs, cs) do |a, b, c|
131
+ f = num.is_a?(Adder) ? c.sum(a, b, f) : c.diff(a, b, f)
132
+ end
133
+ $solver << -f
134
+ when Multiplier
135
+ f = d = nil
136
+ b = num.right
137
+ as = extend(num.left.digits, zero)
138
+ as.zip(digits) do |a, c|
139
+ c2 = d ? Digit.new : c
140
+ d2 = Digit.new
141
+ c2.prod(a, b, d2)
142
+ f = c.sum(c2, d, f) if d
143
+ d = d2
144
+ end
145
+ $solver << -f
146
+ d.is 0
147
+ end
148
+ end
149
+
150
+ def less(b)
151
+ z = zero
152
+ bs = extend(b.digits, z)
153
+ as = b.extend(digits, z)
154
+ f = $solver.new_var
155
+ $solver << f
156
+ as.reverse.zip(bs.reverse).each do |a, b|
157
+ f = a.less(b, f)
158
+ end
159
+ $solver << -f
160
+ end
161
+
162
+ def extend(as, z)
163
+ digits.size > as.size ? as + [z] * (digits.size - as.size) : as
164
+ end
165
+
166
+ def top
167
+ digits.last
168
+ end
169
+
170
+ def *(d); Multiplier.new(self, d); end
171
+ def -(d); Subtracter.new(self, d); end
172
+ def +(d); Adder.new(self, d); end
173
+
174
+ def [](*arg)
175
+ case arg.size
176
+ when 1 then digits.reverse[arg.first]
177
+ when 2 then Digits.new(digits.reverse[arg.first, arg.last].reverse)
178
+ end
179
+ end
180
+
181
+ def to_i
182
+ digits.map {|v| v.to_i }.reverse.inject(0) {|z, x| z * 10 + x }
183
+ end
184
+
185
+ Adder = Struct.new(:left, :right)
186
+ Subtracter = Struct.new(:left, :right)
187
+ Multiplier = Struct.new(:left, :right)
188
+ end
189
+
190
+ def zero
191
+ d = Digit.new
192
+ d.is 0
193
+ d
194
+ end
195
+
196
+
197
+ ###############################################################################
198
+
199
+
200
+ $solver = MiniSat::Solver.new
201
+
202
+ str = <<END
203
+ x7yzw
204
+ ---------
205
+ aaa )bbbbbbbb
206
+ cccc
207
+ -----
208
+ ddd
209
+ eee
210
+ ----
211
+ ffff
212
+ ggg
213
+ -----
214
+ hhhh
215
+ hhhh
216
+ ----
217
+ 0
218
+ END
219
+
220
+ puts "problem:"
221
+ puts str.gsub(/[a-z]/m, "#")
222
+
223
+ a = Digits.new(3); a.top.is_not 0
224
+ b = Digits.new(8); b.top.is_not 0
225
+ c = Digits.new(4); c.top.is_not 0
226
+ d = Digits.new(3); d.top.is_not 0
227
+ e = Digits.new(3); e.top.is_not 0
228
+ f = Digits.new(4); f.top.is_not 0
229
+ g = Digits.new(3); g.top.is_not 0
230
+ h = Digits.new(4); h.top.is_not 0
231
+ i = Digits.new(3)
232
+ j = Digits.new(4)
233
+ x, y, z, w = (0..3).map { Digit.new }
234
+ x.is_not 0
235
+
236
+ c.is a * x; d[0, 2].is b[0, 4] - c; d[0, 2].less a
237
+ e.is a * 7; d[2].is b[4]; f[0, 3].is d - e; f[0, 3].less a
238
+ g.is a * y; f[3].is b[5]; h[0, 2].is f - g; h[0, 2].less a
239
+ z.is 0; h[0, 3].less a
240
+ h.is a * w; h[2].is b[6]; h[3].is b[7]
241
+
242
+ $solver.solve
243
+
244
+ puts "answer:"
245
+ puts str.
246
+ gsub(/a+/, a.to_i.to_s).
247
+ gsub(/b+/, b.to_i.to_s).
248
+ gsub(/c+/, c.to_i.to_s).
249
+ gsub(/d+/, d.to_i.to_s).
250
+ gsub(/e+/, e.to_i.to_s).
251
+ gsub(/f+/, f.to_i.to_s).
252
+ gsub(/g+/, g.to_i.to_s).
253
+ gsub(/h+/, h.to_i.to_s).
254
+ gsub(/x+/, x.to_i.to_s).
255
+ gsub(/y+/, y.to_i.to_s).
256
+ gsub(/z+/, z.to_i.to_s).
257
+ gsub(/w+/, w.to_i.to_s)
258
+ puts
259
+ puts
260
+
261
+
262
+ ###############################################################################
263
+
264
+
265
+ $solver = MiniSat::Solver.new
266
+
267
+ str = <<END
268
+ send
269
+ + more
270
+ ------
271
+ money
272
+ END
273
+ puts "problem:"
274
+ puts str
275
+ puts
276
+
277
+ as = Digits.new(4); as.top.is_not 0
278
+ bs = Digits.new(4); bs.top.is_not 0
279
+ cs = Digits.new(5); cs.top.is_not 0
280
+
281
+ h = {}
282
+
283
+ as.digits.reverse.zip(%w(s e n d)) {|d, c| (h[c] ||= []) << d }
284
+ bs.digits.reverse.zip(%w(m o r e)) {|d, c| (h[c] ||= []) << d }
285
+ cs.digits.reverse.zip(%w(m o n e y)) {|d, c| (h[c] ||= []) << d }
286
+ cs.is as + bs
287
+
288
+ h.each do |c, ds|
289
+ ds[1..-1].each {|d| ds.first.is d }
290
+ h.each do |c2, ds2|
291
+ next if c == c2
292
+ ds2.each {|d2| ds.first.is_not d2 }
293
+ end
294
+ end
295
+
296
+ $solver.solve
297
+
298
+ puts "answer:"
299
+ h.each {|c, d| str = str.gsub(c) { d.first.to_i } }
300
+ puts str
301
+ puts
302
+ puts
@@ -0,0 +1,254 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # ruby-minisat example -- nonogram.rb
4
+ # ref: http://en.wikipedia.org/wiki/Nonogram
5
+
6
+ ##
7
+ ## SAT configuration:
8
+ ## - Variables: assign variables to each cell and each number in rule (below)
9
+ ## - Clauses: sudoku rules (below)
10
+ ##
11
+ ##
12
+ ## Variables:
13
+ ## - one variable to each cell
14
+ ## - n * m variables to each rule (length n)
15
+ ## - or, m variables to each number
16
+ ## - where m = n - rule.sum - rule.size + 2
17
+ ##
18
+ ## example: rule is [1, 2], and six cells
19
+ ## - cells : a, b, c, d, e, f
20
+ ## - number 1: p, q, r
21
+ ## - number 2: s, t, u
22
+ ##
23
+ ## abcdef pqr stu
24
+ ## 1.22.. <=> 1.. 2..
25
+ ## 1..22. <=> 1.. .2.
26
+ ## 1...22 <=> 1.. ..2
27
+ ## .1.22. <=> .1. .2.
28
+ ## .1..22 <=> .1. ..2
29
+ ## ..1.22 <=> ..1 ..2
30
+ ##
31
+ ## # p means `1 block starts from a', q means b, and r means c
32
+ ## # s means `2 blocks starts from c', t means d, and u means e
33
+ ##
34
+ ##
35
+ ## Constraints:
36
+ ##
37
+ ## - constraint type 1. exact one variable in each number is true
38
+ ## - constraint type 2. order of rule variables
39
+ ## - constraint type 3. correspondence between cell variables and rule ones
40
+ ##
41
+ ## for above example:
42
+ ##
43
+ ## - constraint type 1.
44
+ ## - p, q, r <- exact one
45
+ ## - s, t, u <- exact one
46
+ ##
47
+ ## - constraint type 2.
48
+ ## - q => t or u
49
+ ## - r => u
50
+ ## - s => p (option)
51
+ ## - t => p or q (option)
52
+ ##
53
+ ## - constraint type 3.
54
+ ## a b c d e f
55
+ ## - a <=> p
56
+ ## - b <=> q
57
+ ## - c <=> r|s
58
+ ## - d <=> s|t
59
+ ## - e <=> t|u
60
+ ## - f <=> u
61
+ ##
62
+ ## Encoding:
63
+ ## - encoding `if': a => x1 or x2 or ... or xn:
64
+ ## (-a | x1 | x2 | ... | xn)
65
+ ##
66
+ ## - encoding `iff': a <=> x1 or x2 or ... or xn:
67
+ ## (a | -x1) & (a | -x2) & ... & (a | -xn) & (-a | x1 | x2 | ... | xn)
68
+ ##
69
+
70
+ require "minisat"
71
+ require File.dirname($0) + "/compat18" if RUBY_VERSION < "1.9.0"
72
+
73
+
74
+ def error(msg)
75
+ $stderr.puts msg
76
+ exit 1
77
+ end
78
+
79
+
80
+ def parse_file(file)
81
+ lines = []
82
+ base = nil
83
+ rows = []
84
+ File.read(file).split(/\n/).each do |line|
85
+ next if line[/^\s*#/]
86
+ lines << line
87
+ next unless line.index(".")
88
+
89
+ rows << line.split.map {|x| x.to_i }.select {|x| x > 0 }
90
+
91
+ ary = []
92
+ line.scan(/\s*\./) {|s| ary << [$`.size, s.size] }
93
+ base ||= ary
94
+ error "corrupt field" unless base == ary
95
+ end
96
+
97
+ cols = base.map { [] }
98
+ lines.each do |line|
99
+ cols.zip(base) do |c, (i, j)|
100
+ n = line[i, j].to_i
101
+ c << n if n > 0
102
+ end
103
+ end
104
+
105
+ [rows, cols]
106
+ end
107
+
108
+
109
+ def define_sat(solver, rows, cols)
110
+ # define variables
111
+ v_cells = rows.map { cols.map { solver.new_var } }
112
+ v_rows = rows.map {|r| define_rule_vars(solver, cols.size, r) }
113
+ v_cols = cols.map {|r| define_rule_vars(solver, rows.size, r) }
114
+
115
+ # define constraints type 1 and 2
116
+ (v_rows + v_cols).each do |r|
117
+ define_constraint_type_1(solver, r)
118
+ define_constraint_type_2(solver, r)
119
+ end
120
+
121
+ # define constraints type 3
122
+ v_cells.zip(rows, v_rows) do |l, n, r|
123
+ define_constraint_type_3(solver, l, n, r)
124
+ end
125
+ v_cells.transpose.zip(cols, v_cols) do |l, n, r|
126
+ define_constraint_type_3(solver, l, n, r)
127
+ end
128
+
129
+ [v_cells, v_rows, v_cols]
130
+ end
131
+
132
+
133
+ # define rule variables : 6 -> [1, 2] -> [p, q, r]
134
+ def define_rule_vars(solver, size, rule)
135
+ num = size - rule.inject(0) {|z, x| z + x + 1 } + 2
136
+ rule.map { (0...num).map { solver.new_var } }
137
+ end
138
+
139
+
140
+ # constraint type 1. exact one variable in each number is true
141
+ def define_constraint_type_1(solver, r)
142
+ # r : [[p,q,r], [s,t,u]]
143
+ r.each do |r2|
144
+ solver << r2
145
+ r2.combination(2) {|v1, v2| solver << [-v1, -v2] }
146
+ end
147
+ end
148
+
149
+
150
+ # define constraint type 2: order of rule variables
151
+ def define_constraint_type_2(solver, r)
152
+ # r : [[p,q,r], [s,t,u]]
153
+ r.each_cons(2) do |r2, r3|
154
+ (1...r2.size).each do |i|
155
+ solver << ([-r2[i]] + r3[i..-1])
156
+ end
157
+ end
158
+ end
159
+
160
+
161
+ # define constraint type 3: correspondence between cell variables and rule ones
162
+ def define_constraint_type_3(solver, l, n, r)
163
+ # l : a,b,c,d,e,f
164
+ # n : [1, 2]
165
+ # r : [[p,q,r], [s,t,u]]
166
+ t = l.map { [] }
167
+ r.each_with_index do |r2, i|
168
+ off = n[0, i].inject(0) {|z, x| z + x + 1 }
169
+ n[i].times do |j|
170
+ r2.each_with_index do |v, k|
171
+ t[off + j + k] |= [v]
172
+ end
173
+ end
174
+ end; t
175
+ # t : [[p], [q], [r,s], [s,t], [t,u], [u]]
176
+ l.zip(t) do |v, c|
177
+ c.each {|v2| solver << [v, -v2] }
178
+ solver << ([-v] + c)
179
+ end
180
+ end
181
+
182
+
183
+ def solve_sat(solver)
184
+ start = Time.now
185
+ result = solver.solve
186
+ eplise = Time.now - start
187
+ puts "time: %.6f sec." % eplise
188
+ result
189
+ end
190
+
191
+
192
+ def make_solution(solver, v_cells, v_rows, v_cols)
193
+ v_cells.map {|l| l.map {|v| solver[v] } }
194
+ end
195
+
196
+ def output_field(field, rows, cols)
197
+ w = rows.map {|cs| cs.size }.max
198
+ r = rows.map do |rs|
199
+ ([" "] * w + rs.map {|r| r.to_s.rjust(2) }).last(w).join
200
+ end
201
+ h = cols.map {|cs| cs.size }.max
202
+ c = cols.map do |cs|
203
+ ([" "] * h + cs.map {|c| c.to_s.rjust(2) }).last(h)
204
+ end.transpose.map {|l| " " * (r.first.size + 1) + l.join }
205
+ puts c
206
+ field.zip(r) do |line, prefix|
207
+ puts prefix + " " + line.map {|x| x ? "##" : " ." }.join
208
+ end
209
+ end
210
+
211
+
212
+ def add_constraint(solver, vars)
213
+ solver << vars.flatten.map {|v| solver[v] ? -v : v }
214
+ end
215
+
216
+
217
+
218
+ error "usage: nonogram.rb nonogram.sample" if ARGV.empty?
219
+
220
+ ARGV.each do |file|
221
+ nonogram = parse_file(file)
222
+
223
+ solver = MiniSat::Solver.new
224
+
225
+ puts "defining SAT..."
226
+ vars = define_sat(solver, *nonogram)
227
+
228
+ puts "solving SAT..."
229
+ result = solve_sat(solver)
230
+ puts "result: " + (result ? "solvable" : "unsolvable")
231
+ puts
232
+ next unless result
233
+
234
+ puts "translating model into solution..."
235
+ solution = make_solution(solver, *vars)
236
+ puts "solution found:"
237
+ output_field(solution, *nonogram)
238
+ puts
239
+
240
+ puts "checking different solution..."
241
+ add_constraint(solver, vars)
242
+ result = solve_sat(solver)
243
+ puts "result: " +
244
+ (result ? "different solution found" : "different solution not found")
245
+ puts
246
+ next unless result
247
+
248
+ puts "translating model into solution..."
249
+ solution = make_solution(solver, *vars)
250
+ puts "different solution:"
251
+ output_field(solution, *nonogram)
252
+ puts
253
+ puts
254
+ end