z3 0.0.20181229 → 0.0.20211213

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (102) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +0 -2
  3. data/Rakefile +8 -1
  4. data/examples/abc_path +187 -0
  5. data/examples/abc_path-1.txt +7 -0
  6. data/examples/algebra_problems +12 -12
  7. data/examples/aquarium +133 -0
  8. data/examples/aquarium-1.txt +11 -0
  9. data/examples/bridges +2 -2
  10. data/examples/cats_organized_neatly +133 -0
  11. data/examples/cats_organized_neatly-10.txt +15 -0
  12. data/examples/cats_organized_neatly-3.txt +8 -0
  13. data/examples/cats_organized_neatly-48.txt +32 -0
  14. data/examples/circuit_problems +4 -4
  15. data/examples/clogic_puzzle +2 -2
  16. data/examples/color_nonogram +150 -0
  17. data/examples/color_nonogram-1.txt +23 -0
  18. data/examples/crossflip +2 -4
  19. data/examples/dominion +153 -0
  20. data/examples/dominion-1.txt +8 -0
  21. data/examples/dominosa +133 -0
  22. data/examples/dominosa-1.txt +8 -0
  23. data/examples/eulero +99 -0
  24. data/examples/eulero-1.txt +5 -0
  25. data/examples/four_hackers_puzzle +2 -2
  26. data/examples/futoshiki +128 -0
  27. data/examples/futoshiki-1.txt +17 -0
  28. data/examples/kakurasu +73 -0
  29. data/examples/kakurasu-1.txt +2 -0
  30. data/examples/kakuro +2 -2
  31. data/examples/killer_sudoku +88 -0
  32. data/examples/killer_sudoku-1.txt +17 -0
  33. data/examples/killer_sudoku-2.txt +53 -0
  34. data/examples/kinematics_problems +20 -20
  35. data/examples/knights_puzzle +2 -2
  36. data/examples/kropki +100 -0
  37. data/examples/kropki-1.txt +13 -0
  38. data/examples/letter_connections +2 -2
  39. data/examples/light_up +2 -2
  40. data/examples/minisudoku +2 -2
  41. data/examples/miracle_sudoku +135 -0
  42. data/examples/miracle_sudoku-1.txt +9 -0
  43. data/examples/mortal_coil_puzzle +2 -2
  44. data/examples/nanro +245 -0
  45. data/examples/nanro-1.txt +8 -0
  46. data/examples/nine_clocks +106 -0
  47. data/examples/nonogram +2 -2
  48. data/examples/pyramid_nonogram +2 -2
  49. data/examples/regexp_crossword_solver +2 -2
  50. data/examples/regexp_solver +2 -2
  51. data/examples/renzoku +124 -0
  52. data/examples/renzoku-1.txt +17 -0
  53. data/examples/sandwich_sudoku +101 -0
  54. data/examples/sandwich_sudoku-1.txt +10 -0
  55. data/examples/selfref +2 -2
  56. data/examples/skyscrapers +118 -0
  57. data/examples/skyscrapers-1.txt +6 -0
  58. data/examples/skyscrapers-2.txt +11 -0
  59. data/examples/star_battle +134 -0
  60. data/examples/star_battle-1.txt +10 -0
  61. data/examples/stitches +180 -0
  62. data/examples/stitches-1.txt +11 -0
  63. data/examples/sudoku +2 -2
  64. data/examples/suguru +199 -0
  65. data/examples/suguru-1.txt +17 -0
  66. data/examples/verbal_arithmetic +2 -2
  67. data/examples/yajilin +268 -0
  68. data/examples/yajilin-1.txt +10 -0
  69. data/lib/z3/expr/expr.rb +3 -3
  70. data/lib/z3/low_level_auto.rb +138 -10
  71. data/lib/z3/optimize.rb +1 -0
  72. data/lib/z3/very_low_level.rb +5 -1
  73. data/lib/z3/very_low_level_auto.rb +35 -3
  74. data/spec/integration/abc_path_spec.rb +21 -0
  75. data/spec/integration/aquarium_spec.rb +27 -0
  76. data/spec/integration/cats_organized_neatly_spec.rb +14 -0
  77. data/spec/integration/color_nonogram_spec.rb +28 -0
  78. data/spec/integration/dominion_spec.rb +14 -0
  79. data/spec/integration/dominosa_spec.rb +21 -0
  80. data/spec/integration/eulero_spec.rb +11 -0
  81. data/spec/integration/futoshiki_spec.rb +23 -0
  82. data/spec/integration/kakurasu_spec.rb +18 -0
  83. data/spec/integration/killer_sudoku_spec.rb +10 -0
  84. data/spec/integration/knights_puzzle_spec.rb +11 -11
  85. data/spec/integration/kropki_spec.rb +19 -0
  86. data/spec/integration/miracle_sudoku_spec.rb +15 -0
  87. data/spec/integration/mortal_coil_puzzle_spec.rb +8 -6
  88. data/spec/integration/nanro_spec.rb +39 -0
  89. data/spec/integration/nine_clocks_spec.rb +30 -0
  90. data/spec/integration/regexp_crossword_solver_spec.rb +1 -1
  91. data/spec/integration/renzoku_spec.rb +23 -0
  92. data/spec/integration/sandwich_sudoku_spec.rb +15 -0
  93. data/spec/integration/skyscraper_spec.rb +10 -0
  94. data/spec/integration/star_battle_spec.rb +27 -0
  95. data/spec/integration/stitches_spec.rb +25 -0
  96. data/spec/integration/suguru_spec.rb +23 -0
  97. data/spec/integration/yajilin_spec.rb +25 -0
  98. data/spec/optimize_spec.rb +3 -1
  99. data/spec/set_expr_spec.rb +15 -9
  100. data/spec/solver_spec.rb +1 -2
  101. data/spec/spec_helper.rb +15 -0
  102. metadata +86 -7
data/examples/suguru ADDED
@@ -0,0 +1,199 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require_relative "../lib/z3"
5
+
6
+ class Suguru
7
+ def initialize(path)
8
+ @data = Pathname(path).readlines.map(&:chomp)
9
+ @ysize = (@data.size - 1) / 2
10
+ @xsize = (@data[0].size - 1) / 2
11
+ raise unless @data.size == 2 * @ysize + 1
12
+ raise unless @data.all?{ |row| row.size == 2 * @xsize + 1 }
13
+ @solver = Z3::Solver.new
14
+ end
15
+
16
+ def call
17
+ @connections = calculate_connections
18
+ @containers = calculate_containers
19
+
20
+ @ysize.times do |y|
21
+ @ysize.times do |x|
22
+ v = cell_data(x, y)
23
+ if v
24
+ @solver.assert cell(x, y) == v
25
+ end
26
+ end
27
+ end
28
+
29
+ @containers.each do |name, cells|
30
+ @solver.assert Z3.Distinct(*cells)
31
+ cells.each do |c|
32
+ @solver.assert c >= 1
33
+ @solver.assert c <= cells.size
34
+ end
35
+ end
36
+
37
+ @ysize.times do |y|
38
+ @xsize.times do |x|
39
+ @solver.assert cell(x, y) != cell(x+1, y)
40
+ @solver.assert cell(x, y) != cell(x-1, y+1)
41
+ @solver.assert cell(x, y) != cell(x, y+1)
42
+ @solver.assert cell(x, y) != cell(x+1, y+1)
43
+ end
44
+ end
45
+
46
+ if @solver.satisfiable?
47
+ @model = @solver.model
48
+ print_answer
49
+ else
50
+ puts "failed to solve"
51
+ end
52
+ end
53
+
54
+ private
55
+
56
+ def calculate_containers
57
+ cells = @xsize.times.flat_map{|x| @ysize.times.map{|y| [x,y]}}
58
+
59
+ containers = {}
60
+ cell_assignments = {}
61
+ cells.each do |c|
62
+ ct = c.join(",")
63
+ containers[ct] = [c]
64
+ cell_assignments[c] = ct
65
+ end
66
+
67
+ @connections.each do |c1, c2|
68
+ # Already in same container
69
+ ct1 = cell_assignments[c1]
70
+ ct2 = cell_assignments[c2]
71
+ next if ct1 == ct2
72
+ containers.delete(ct2).each do |c3|
73
+ cell_assignments[c3] = ct1
74
+ containers[ct1] << c3
75
+ end
76
+ end
77
+
78
+ containers.transform_values{|cells| cells.map{|x,y| cell(x,y)}}
79
+ end
80
+
81
+ def calculate_connections
82
+ result = []
83
+
84
+ @ysize.times do |y|
85
+ (0..@xsize-2).each do |x|
86
+ if cell_open_right?(x, y)
87
+ result << [[x,y], [x+1,y]]
88
+ end
89
+ end
90
+ end
91
+
92
+ @xsize.times do |x|
93
+ (0..@ysize-2).each do |y|
94
+ if cell_open_down?(x, y)
95
+ result << [[x,y], [x,y+1]]
96
+ end
97
+ end
98
+ end
99
+
100
+ result
101
+ end
102
+
103
+ def cell_open_right?(x, y)
104
+ @data[2*y+1][2*x+2] == " "
105
+ end
106
+
107
+ def cell_open_down?(x, y)
108
+ @data[2*y+2][2*x+1] == " "
109
+ end
110
+
111
+ def cell_data(x, y)
112
+ v = @data[2*y+1][2*x+1]
113
+ return nil if v == " "
114
+ v.to_i
115
+ end
116
+
117
+ def cell(x, y)
118
+ Z3.Int("c#{x},#{y}")
119
+ end
120
+
121
+ def print_answer
122
+ output = @data.map(&:dup)
123
+ @ysize.times do |y|
124
+ @xsize.times do |x|
125
+ # This only works for 1-9, could use hex or something for more
126
+ value = @model[cell(x,y)].to_s
127
+ output[2*y+1][2*x+1, 1] = value
128
+ end
129
+ end
130
+
131
+ puts output
132
+ end
133
+ end
134
+
135
+ path = ARGV[0] || Pathname(__dir__) + "suguru-1.txt"
136
+ Suguru.new(path).call
137
+
138
+
139
+ __END__
140
+
141
+ # Based on https://play.google.com/store/apps/details?id=com.alexuvarov.android.kropki.puzzle&hl=en_GB
142
+ #
143
+ # NxN grid
144
+ # Each cell contains numbers 1 to N
145
+ # Each row and each column contains distinct numbers
146
+ # If there's black dot (o) between cells, one is twice the other
147
+ # If there's white dot (*) between cells, one is the other plus one
148
+ # If there's no dot, neither of these is true
149
+ # (for 1/2, dot can be of either color)
150
+
151
+ class Kropki
152
+ def call
153
+ # Cells contain numbers 1 - N
154
+ @size.times do |y|
155
+ @size.times do |x|
156
+ @solver.assert cell(x, y) >= 1
157
+ @solver.assert cell(x, y) <= @size
158
+ end
159
+ end
160
+
161
+ # Each row and column distinct
162
+ @size.times do |y|
163
+ @solver.assert Z3.Distinct(*@size.times.map{ |x| cell(x,y) })
164
+ end
165
+ @size.times do |x|
166
+ @solver.assert Z3.Distinct(*@size.times.map{ |y| cell(x,y) })
167
+ end
168
+
169
+ # Horizontal dots
170
+ @size.times do |y|
171
+ (0...@size).each do |x|
172
+ dot_constraints cell(x, y), cell(x+1, y), @data[2*y+1][2*x+2]
173
+ end
174
+ end
175
+
176
+ # Vertical dots
177
+ @size.times do |x|
178
+ (0...@size).each do |y|
179
+ dot_constraints cell(x, y), cell(x, y+1), @data[2*y+2][2*x+1]
180
+ end
181
+ end
182
+ end
183
+
184
+ private
185
+
186
+ def dot_constraints(c1, c2, dot)
187
+ if dot == "o"
188
+ @solver.assert Z3.Or(c1 == c2 + 1, c2 == c1 + 1)
189
+ elsif dot == "*"
190
+ @solver.assert Z3.Or(c1 == c2 * 2, c2 == c1 * 2)
191
+ else
192
+ @solver.assert c1 != c2 + 1
193
+ @solver.assert c2 != c1 + 1
194
+ @solver.assert c1 != c2 * 2
195
+ @solver.assert c2 != c1 * 2
196
+ end
197
+ end
198
+
199
+ end
@@ -0,0 +1,17 @@
1
+ +---+-----+
2
+ | |1 4 |
3
+ | | +---+
4
+ |2 6| | 2|
5
+ | | | |
6
+ | | |4 3|
7
+ +---+-+ |
8
+ | 3 | |
9
+ | +-+---+
10
+ |2 | |
11
+ +-+ +-+ |
12
+ | | |6 4|
13
+ +-+---+ |
14
+ | |3 | 3|
15
+ | | +---+
16
+ | | 1 2 |
17
+ +-+-------+
@@ -16,7 +16,7 @@ class VerbalArithmetic
16
16
  @c = c.chars.map{|v| @vars[v]}
17
17
  end
18
18
 
19
- def solve!
19
+ def call
20
20
  @solver.assert @a[0] != 0
21
21
  @solver.assert @b[0] != 0
22
22
  @solver.assert @c[0] != 0
@@ -44,4 +44,4 @@ class VerbalArithmetic
44
44
  end
45
45
  end
46
46
 
47
- VerbalArithmetic.new("SEND", "MORE", "MONEY").solve!
47
+ VerbalArithmetic.new("SEND", "MORE", "MONEY").call
data/examples/yajilin ADDED
@@ -0,0 +1,268 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "pathname"
4
+ require_relative "../lib/z3"
5
+
6
+ class Yajilin
7
+ def initialize(path)
8
+ @data = Pathname(path)
9
+ .readlines
10
+ .map{|line|
11
+ line
12
+ .chomp
13
+ .split
14
+ .map{|x| x == "." ? nil : x}
15
+ }
16
+ @xsize = @data.size
17
+ @ysize = @data[0].size
18
+ raise unless @data.all?{|row| row.size == @xsize}
19
+ @solver = Z3::Solver.new
20
+ end
21
+
22
+ def on_board?(x,y)
23
+ x >= 0 and y >= 0 and x < @xsize and y < @ysize
24
+ end
25
+
26
+ # true = white
27
+ # false = black
28
+ def cvar(x,y)
29
+ return unless on_board?(x,y)
30
+ Z3.Bool("c[#{x},#{y}]")
31
+ end
32
+
33
+ # true - connected
34
+ # right from (x,y)
35
+ def hvar(x,y)
36
+ return unless on_board?(x,y) and x != @xsize - 1
37
+ Z3.Bool("h[#{x},#{y}]")
38
+ end
39
+
40
+ # true - connected
41
+ # down from (x,y)
42
+ def vvar(x,y)
43
+ return unless on_board?(x,y) and y != @ysize - 1
44
+ Z3.Bool("v[#{x},#{y}]")
45
+ end
46
+
47
+ # Every cell has a number that shows its position within the loop
48
+ def nvar(x,y)
49
+ Z3.Int("n[#{x},#{y}]")
50
+ end
51
+
52
+ def hint_cell?(x,y)
53
+ !!@data[y][x]
54
+ end
55
+
56
+ def neighbours(x,y)
57
+ [
58
+ [x+1, y ],
59
+ [x-1, y ],
60
+ [x, y+1],
61
+ [x, y-1],
62
+ ].select{|nx,ny|
63
+ on_board?(nx, ny) and !hint_cell?(nx,ny)
64
+ }
65
+ end
66
+
67
+ def neighbour_cvars(x,y)
68
+ neighbours(x,y)
69
+ .map{|nx,ny| cvar(nx, ny)}
70
+ end
71
+
72
+ def connections_at(x,y)
73
+ [
74
+ hvar(x,y),
75
+ hvar(x-1,y),
76
+ vvar(x,y),
77
+ vvar(x,y-1),
78
+ ].compact
79
+ end
80
+
81
+ def count_loops_at(x,y)
82
+ Z3.Add(*connections_at(x,y).map{|n| n.ite(1,0)})
83
+ end
84
+
85
+ def assert_connections
86
+ each_xy do |x,y|
87
+ if hint_cell?(x, y)
88
+ @solver.assert count_loops_at(x,y) == 0
89
+ else
90
+ @solver.assert cvar(x,y).implies(count_loops_at(x,y) == 2)
91
+ @solver.assert (~cvar(x,y)).implies(count_loops_at(x,y) == 0)
92
+ end
93
+ end
94
+ end
95
+
96
+ def assert_no_black_multiples
97
+ each_xy do |x,y|
98
+ next if hint_cell?(x, y)
99
+ @solver.assert (~cvar(x,y)).implies(Z3.And(*neighbour_cvars(x,y)))
100
+ end
101
+ end
102
+
103
+ def stripe_left(x0,y)
104
+ (0...x0).map{|x| cvar(x,y) unless hint_cell?(x,y) }.compact
105
+ end
106
+
107
+ def stripe_right(x0,y)
108
+ (x0+1...@xsize).map{|x| cvar(x,y) unless hint_cell?(x,y) }.compact
109
+ end
110
+
111
+ def stripe_up(x,y0)
112
+ (0...y0).map{|y| cvar(x,y) unless hint_cell?(x,y) }.compact
113
+ end
114
+
115
+ def stripe_down(x,y0)
116
+ (y0+1...@ysize).map{|y| cvar(x,y) unless hint_cell?(x,y) }.compact
117
+ end
118
+
119
+ def assert_arrows
120
+ each_xy do |x,y|
121
+ if hint_cell?(x, y)
122
+ count = @data[y][x][0].to_i
123
+ dir = @data[y][x][1]
124
+ case dir
125
+ when "<", "←"
126
+ stripe = stripe_left(x,y)
127
+ when ">", "→"
128
+ stripe = stripe_right(x,y)
129
+ when "^", "↑"
130
+ stripe = stripe_up(x,y)
131
+ when "_", "↓"
132
+ stripe = stripe_down(x,y)
133
+ else
134
+ raise
135
+ end
136
+
137
+ @solver.assert Z3.Add(*stripe.map{|c| c.ite(0,1)}) == count
138
+ end
139
+ end
140
+ end
141
+
142
+ def each_xy
143
+ @ysize.times do |y|
144
+ @xsize.times do |x|
145
+ yield(x,y)
146
+ end
147
+ end
148
+ end
149
+
150
+ def connections_and_nvars(x,y)
151
+ [
152
+ [x+1, y, hvar(x, y)],
153
+ [x-1, y, hvar(x-1, y)],
154
+ [x, y+1, vvar(x, y)],
155
+ [x, y-1, vvar(x, y-1)],
156
+ ].select{|nx,ny,convar|
157
+ on_board?(nx, ny) and !hint_cell?(nx,ny)
158
+ }.map{|nx,ny,convar| [nvar(nx,ny), convar] }
159
+ end
160
+
161
+ # Loop will have Ns of 0+, all distinct and consecutive
162
+ # Non-loop will have Ns of MAX + cell_number
163
+ def assert_single_loop
164
+ # max_value = @xsize * @ysize + 10
165
+ max_value = 1000
166
+
167
+ each_xy do |x,y|
168
+ cell_idx = x + y * @xsize
169
+ @solver.assert nvar(x,y) >= 0
170
+ if hint_cell?(x,y)
171
+ @solver.assert nvar(x,y) == max_value + cell_idx
172
+ else
173
+ @solver.assert (~cvar(x,y)).implies( nvar(x,y) == max_value + cell_idx )
174
+
175
+ nval = connections_and_nvars(x,y)
176
+ .map{|n,c| c.ite(n+1, max_value) }
177
+ .reduce{|a,b| (a <= b).ite(a, b) }
178
+
179
+ @solver.assert cvar(x,y).implies((nvar(x,y) == 0) | (nvar(x,y) == nval) )
180
+ @solver.assert cvar(x,y).implies(nvar(x,y) < max_value)
181
+ end
182
+ end
183
+
184
+ # only one loop start
185
+ @solver.assert Z3.Add(
186
+ *enum_for(:each_xy).map{|x,y|
187
+ (nvar(x,y) == 0).ite(1,0)
188
+ }) == 1
189
+ end
190
+
191
+ def call
192
+ assert_connections
193
+ assert_no_black_multiples
194
+ assert_arrows
195
+ assert_single_loop
196
+
197
+ if @solver.satisfiable?
198
+ @model = @solver.model
199
+ print_answer
200
+ else
201
+ puts "failed to solve"
202
+ end
203
+ end
204
+
205
+ def hint_data(x,y)
206
+ u = @data[y][x]
207
+ n = u[0]
208
+ a = {
209
+ "<" => "←",
210
+ ">" => "→",
211
+ "_" => "↓",
212
+ "^" => "↑",
213
+ }[u[1]] || u[1]
214
+ n + a
215
+ end
216
+
217
+ # TODO: make it nicer
218
+ def print_answer
219
+ @ysize.times do |y|
220
+ @xsize.times do |x|
221
+ if hint_cell?(x,y)
222
+ print hint_data(x,y)
223
+ # print "O "
224
+ else
225
+ c = @model[cvar(x,y)].to_b
226
+ if c
227
+ print "* "
228
+ else
229
+ print "# "
230
+ end
231
+ end
232
+
233
+ next if x == @xsize - 1
234
+ if @model[hvar(x,y)].to_b
235
+ print "-"
236
+ else
237
+ print " "
238
+ end
239
+ end
240
+ print "\n"
241
+
242
+ next if y == @ysize-1
243
+ @xsize.times do |x|
244
+ if @model[vvar(x,y)].to_b
245
+ print "| "
246
+ else
247
+ print " "
248
+ end
249
+ end
250
+
251
+ print "\n"
252
+ end
253
+ end
254
+
255
+ def print_debug_loop_info
256
+ print "\n"
257
+ @ysize.times do |y|
258
+ @xsize.times do |x|
259
+ n = @model[nvar(x,y)].to_i
260
+ print "%8d" % n
261
+ end
262
+ print "\n"
263
+ end
264
+ end
265
+ end
266
+
267
+ path = ARGV[0] || Pathname(__dir__) + "yajilin-1.txt"
268
+ Yajilin.new(path).call
@@ -0,0 +1,10 @@
1
+ . . . . . . . . . .
2
+ . . . . 2↓ . . . . .
3
+ . . . . . . . . . .
4
+ . . 2↑ . . . . 2↑ . .
5
+ . . . 1↓ . . . . . .
6
+ . . . . . . 1→ . . .
7
+ . . . . . . . . 0→ .
8
+ 0↑ . . 1← . 1↓ . . . .
9
+ . . . . . . . . 2← .
10
+ . . . . . . . . . .
data/lib/z3/expr/expr.rb CHANGED
@@ -115,7 +115,7 @@ module Z3
115
115
  a.sort.new(Z3::LowLevel.mk_bvand(a, b))
116
116
  end
117
117
  else
118
- raise Z3::Exception, "Can't perform logic operations on #{a.sort} exprs, only Bool and Bitvec"
118
+ raise Z3::Exception, "Can't perform logic operations on #{args[0].sort} exprs, only Bool and Bitvec"
119
119
  end
120
120
  end
121
121
 
@@ -129,7 +129,7 @@ module Z3
129
129
  a.sort.new(Z3::LowLevel.mk_bvor(a, b))
130
130
  end
131
131
  else
132
- raise Z3::Exception, "Can't perform logic operations on #{a.sort} exprs, only Bool and Bitvec"
132
+ raise Z3::Exception, "Can't perform logic operations on #{args[0].sort} exprs, only Bool and Bitvec"
133
133
  end
134
134
  end
135
135
 
@@ -145,7 +145,7 @@ module Z3
145
145
  a.sort.new(Z3::LowLevel.mk_bvxor(a, b))
146
146
  end
147
147
  else
148
- raise Z3::Exception, "Can't perform logic operations on #{a.sort} exprs, only Bool and Bitvec"
148
+ raise Z3::Exception, "Can't perform logic operations on #{args[0].sort} exprs, only Bool and Bitvec"
149
149
  end
150
150
  end
151
151