z3 0.0.20251017 → 0.0.20260727

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 (65) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +45 -5
  3. data/Rakefile +11 -0
  4. data/examples/aquarium +2 -4
  5. data/examples/color_nonogram +1 -2
  6. data/examples/dominosa +2 -2
  7. data/examples/kakurasu +7 -6
  8. data/examples/kakurasu-2.txt +2 -0
  9. data/examples/light_up +35 -31
  10. data/examples/light_up-2.txt +2 -0
  11. data/examples/nanro +1 -4
  12. data/examples/regexp_string_matcher.rb +1 -1
  13. data/examples/simple_regexp_parser.rb +1 -1
  14. data/examples/star_battle +3 -6
  15. data/examples/stitches +5 -5
  16. data/examples/yajilin +3 -5
  17. data/lib/z3/expr/bitvec_expr.rb +9 -8
  18. data/lib/z3/expr/bool_expr.rb +26 -0
  19. data/lib/z3/expr/expr.rb +6 -1
  20. data/lib/z3/expr/set_expr.rb +1 -1
  21. data/lib/z3/func_decl.rb +29 -4
  22. data/lib/z3/goal.rb +7 -0
  23. data/lib/z3/interface.rb +20 -1
  24. data/lib/z3/low_level.rb +32 -3
  25. data/lib/z3/low_level_auto.rb +20 -8
  26. data/lib/z3/model.rb +7 -3
  27. data/lib/z3/optimize.rb +45 -2
  28. data/lib/z3/param_descrs.rb +55 -0
  29. data/lib/z3/params.rb +99 -0
  30. data/lib/z3/printer.rb +17 -3
  31. data/lib/z3/probe.rb +20 -3
  32. data/lib/z3/reference_counted.rb +23 -0
  33. data/lib/z3/solver.rb +56 -2
  34. data/lib/z3/sort/float_sort.rb +15 -7
  35. data/lib/z3/sort/set_sort.rb +2 -2
  36. data/lib/z3/sort/sort.rb +5 -2
  37. data/lib/z3/tactic.rb +9 -1
  38. data/lib/z3/very_low_level.rb +3 -0
  39. data/lib/z3/very_low_level_auto.rb +5 -2
  40. data/lib/z3.rb +3 -0
  41. data/spec/array_expr_spec.rb +7 -16
  42. data/spec/bitvec_expr_spec.rb +32 -3
  43. data/spec/bool_expr_spec.rb +27 -0
  44. data/spec/expr_spec.rb +7 -0
  45. data/spec/float_expr_spec.rb +15 -9
  46. data/spec/float_sort_spec.rb +75 -0
  47. data/spec/goal_spec.rb +16 -0
  48. data/spec/integration/examples/knights_puzzle-10.txt +90 -0
  49. data/spec/integration/examples/knights_puzzle-9.txt +90 -0
  50. data/spec/integration/kakurasu_spec.rb +15 -0
  51. data/spec/integration/light_up_spec.rb +8 -0
  52. data/spec/integration/regexp_solver_spec.rb +6 -0
  53. data/spec/model_spec.rb +56 -0
  54. data/spec/optimize_spec.rb +72 -0
  55. data/spec/param_descrs_spec.rb +50 -0
  56. data/spec/params_spec.rb +85 -0
  57. data/spec/printer_spec.rb +35 -0
  58. data/spec/probe_spec.rb +26 -0
  59. data/spec/reference_counted_spec.rb +91 -0
  60. data/spec/set_expr_spec.rb +4 -3
  61. data/spec/set_sort_spec.rb +15 -0
  62. data/spec/solver_spec.rb +130 -7
  63. data/spec/sort_spec.rb +50 -0
  64. data/spec/tactic_spec.rb +34 -0
  65. metadata +28 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: eba319d56d6c1d848c4d50a904b5ce6f324589fd10606f8e8233d2118b22ab5b
4
- data.tar.gz: 5a38dc72afd6e48f57e712b4db42c78587e532c5605f6c0a3290c255c8f682bf
3
+ metadata.gz: 2722943f324098ac25b10410d1b4d8b24afab4681ee8b28772824f25daf83ad9
4
+ data.tar.gz: b6e1b3225862edce94fe37751739467db74e6ebbd432e122f53aa00afff1e0b1
5
5
  SHA512:
6
- metadata.gz: 8f40bbe4cb4c034313d76071d97c857648da9317bb53eecdae07114981d90576e45a4832fc3df7f508641b70e3b8b8fc03f787c7424b680574f0d6ed57785c13
7
- data.tar.gz: 902d5ef0940cda61a31e99899756eaab0d1eb6fc051a3a162aebf1752ca1c00eff685f18931b68849af42920fe08d8eeda4994251d9b7bd8defce256f3d2b59d
6
+ metadata.gz: 35a9657f906d701f0b433cf209e90280a05f65f7df10c748febcf8f6deeb3cae180fb002572461bce9db63b1c6aedc0ee31e2474c7460f1a3611731121a508fd
7
+ data.tar.gz: 72343dc4e5bca1b0dd33a0f483a92552390b07ea004759dbbaac413fa2195b5287f047ed701e5ab4bbdf3c368bba66901d064d03231399e1b9f154a3ac3679e3
data/README.md CHANGED
@@ -1,12 +1,52 @@
1
- This is Ruby interface for Z3 [ https://github.com/Z3Prover/z3 ].
1
+ # Ruby bindings for Z3
2
2
 
3
- Minimum required version is Z3 4.8.
3
+ This is a Ruby interface for [Z3](https://github.com/Z3Prover/z3).
4
4
 
5
- It's in very early stages of development. Pull requests always welcome.
5
+ Recommended [Z3](https://github.com/Z3Prover/z3) version is 4.16 or newer. Make sure you have it first (e.g. `brew install z3` on MacOS).
6
6
 
7
- ### Interface
7
+ ```sh
8
+ gem install z3
9
+ ```
10
+
11
+ [API documentation is here](https://taw.github.io/z3/).
12
+
13
+ ## Basic usage
14
+
15
+ Variables are initialized with `Z3.Bool`, `Z3.Int`, `Z3.Real`, `Z3.Bitvec`.
16
+
17
+ Constrain and solve with `Z3::Solver` and `Z3::Optimize`.
18
+
19
+ ```ruby
20
+ require 'z3'
21
+
22
+ # make z3 variables
23
+ a, b = Z3.Int('a'), Z3.Int('b')
24
+
25
+ # add constraints with expressions
26
+ solver = Z3::Solver.new
27
+ solver.assert(a > 1)
28
+ solver.assert(b > 0)
29
+ solver.assert(a + b == 3)
30
+
31
+ # check sat, find model
32
+ if solver.satisfiable?
33
+ model = solver.model
34
+
35
+ # convert z3 model to ruby types
36
+ hash = model.to_h do |zvar, zvalue|
37
+ [zvar.to_s, zvalue.to_i]
38
+ end
39
+
40
+ p hash
41
+ # {"a" => 2, "b" => 1}
42
+ end
43
+ ```
44
+
45
+ ## Interface
46
+
47
+ The public interface is various methods in `Z3` module, and on objects created by it.
8
48
 
9
- The public interface is various methods in `Z3` module, and on objects created by it. `examples/` directory is probably the best place to start.
49
+ The [`examples/`](https://github.com/taw/z3/blob/master/examples) directory is probably the best place to start.
10
50
 
11
51
  You can use most Ruby operators to construct Z3 expressions, but use `| &` instead of `|| &&` for boolean operators. They unfortunately have wrong operator precedence so you'll need to use some extra parentheses.
12
52
 
data/Rakefile CHANGED
@@ -1,4 +1,5 @@
1
1
  require "json"
2
+ require "rdoc/task"
2
3
 
3
4
  task "default" => "spec"
4
5
  task "test" => "spec"
@@ -61,3 +62,13 @@ task "coverage:missing" do
61
62
  file.puts missing
62
63
  end
63
64
  end
65
+
66
+ RDoc::Task.new do |rdoc|
67
+ rdoc.main = "README.md"
68
+ rdoc.rdoc_dir = "docs"
69
+ rdoc.rdoc_files.include("README.md", "lib/**/*.rb")
70
+ rdoc.rdoc_files.exclude("lib/z3/hacks.rb")
71
+ rdoc.rdoc_files.exclude("lib/z3/low_level.rb")
72
+ rdoc.rdoc_files.exclude("lib/z3/very_low_level.rb")
73
+ rdoc.rdoc_files.exclude("lib/z3/very_low_level_auto.rb")
74
+ end
data/examples/aquarium CHANGED
@@ -25,14 +25,12 @@ class Aquarium
25
25
 
26
26
  # Row sums
27
27
  @ysize.times do |y|
28
- sum = Z3.Add(*@xsize.times.map{|x| cell_var(x, y).ite(1, 0) })
29
- @solver.assert sum == @rows[y]
28
+ @solver.assert Z3.Exactly(@xsize.times.map{|x| cell_var(x, y) }, @rows[y])
30
29
  end
31
30
 
32
31
  # Col sums
33
32
  @xsize.times do |x|
34
- sum = Z3.Add(*@ysize.times.map{|y| cell_var(x, y).ite(1, 0) })
35
- @solver.assert sum == @cols[x]
33
+ @solver.assert Z3.Exactly(@ysize.times.map{|y| cell_var(x, y) }, @cols[x])
36
34
  end
37
35
 
38
36
  # Container water flow
@@ -58,8 +58,7 @@ class ColorNonogram
58
58
  def constraint_line(color_constraints, line_vars)
59
59
  color_constraints.each_with_index do |(expected_total, expected_continuity), color_index|
60
60
  correct_color = line_vars.map{|vi| (vi == color_index) }
61
- total = Z3.Add(*correct_color.map{|cc| cc.ite(1, 0)})
62
- @solver.assert total == expected_total
61
+ @solver.assert Z3.Exactly(correct_color, expected_total)
63
62
  # For 0 / 1 this is all
64
63
  if expected_total >= 2
65
64
  continuity = continuity_somewhere(correct_color, expected_total)
data/examples/dominosa CHANGED
@@ -73,7 +73,7 @@ class Dominosa
73
73
  end
74
74
 
75
75
  all.each do |_, vars|
76
- @solver.assert Z3.Add(*vars.map{|v| v.ite(1,0) }) == 1
76
+ @solver.assert Z3.Exactly(vars, 1)
77
77
  end
78
78
  end
79
79
 
@@ -86,7 +86,7 @@ class Dominosa
86
86
  left_var(x,y),
87
87
  up_var(x,y),
88
88
  ].compact
89
- @solver.assert Z3.Add(*vars.map{|v| v.ite(1,0) }) == 1
89
+ @solver.assert Z3.Exactly(vars, 1)
90
90
  end
91
91
  end
92
92
  end
data/examples/kakurasu CHANGED
@@ -21,7 +21,7 @@ class Kakurasu
21
21
  @cvars = {}
22
22
  @rvars = {}
23
23
  @ysize.times do |y|
24
- @ysize.times do |x|
24
+ @xsize.times do |x|
25
25
  b = Z3.Bool("b#{x},#{y}")
26
26
  r = Z3.Int("r#{x},#{y}")
27
27
  c = Z3.Int("c#{x},#{y}")
@@ -37,12 +37,13 @@ class Kakurasu
37
37
  end
38
38
  end
39
39
 
40
- @xsize.times do |x|
41
- @solver.assert Z3.Add(*@rvars[x]) == @row_sums[x]
40
+ # Row sums weight cells by their column index, column sums by their row index
41
+ @ysize.times do |y|
42
+ @solver.assert Z3.Add(*@rvars[y]) == @row_sums[y]
42
43
  end
43
44
 
44
- @ysize.times do |y|
45
- @solver.assert Z3.Add(*@cvars[y]) == @col_sums[y]
45
+ @xsize.times do |x|
46
+ @solver.assert Z3.Add(*@cvars[x]) == @col_sums[x]
46
47
  end
47
48
 
48
49
  if @solver.satisfiable?
@@ -57,7 +58,7 @@ class Kakurasu
57
58
 
58
59
  def print_board!
59
60
  @ysize.times do |y|
60
- @ysize.times do |x|
61
+ @xsize.times do |x|
61
62
  if @model[@bvars[[x,y]]].to_b
62
63
  print "[X]"
63
64
  else
@@ -0,0 +1,2 @@
1
+ 4 1 7 4 5 7
2
+ 20 9 4 15
data/examples/light_up CHANGED
@@ -3,6 +3,7 @@
3
3
  require "pathname"
4
4
  require_relative "../lib/z3"
5
5
 
6
+ # Also known as Akari
6
7
  class LightUp
7
8
  def initialize(path)
8
9
  data = Pathname(path).read
@@ -14,29 +15,23 @@ class LightUp
14
15
  end
15
16
 
16
17
  def call
17
- @lamps = map_coordinates{|x,y| int01(x,y)}
18
+ @lamps = map_coordinates{|x,y| Z3.Bool("l#{x},#{y}") }
18
19
 
19
- # No lamps on walls
20
- # Numbers count lamps next to node (diagonals don't count)
21
- (0...@ysize).each do |y|
22
- (0...@xsize).each do |x|
23
- v = @data[[x,y]]
24
- next if v == "."
25
- @solver.assert @lamps[[x,y]] == 0
26
- if ("0".."4").include?(v)
27
- @solver.assert Z3.Add(*lamps_next_to_cell(x,y)) == v.to_i
28
- end
20
+ each_cell do |x, y|
21
+ v = @data[[x,y]]
22
+ if v == "."
23
+ # Every white cell must be lit: at least one lamp in its line of sight
24
+ @solver.assert Z3.AtLeast(line_of_sight(x, y), 1)
25
+ else
26
+ # No lamps on walls; numbers count lamps next to node (diagonals don't count)
27
+ @solver.assert ~@lamps[[x,y]]
28
+ @solver.assert Z3.Exactly(lamps_next_to_cell(x, y), v.to_i) if ("0".."4").include?(v)
29
29
  end
30
30
  end
31
31
 
32
- # Every cell is in raycast of a lamp
33
- # No lamp is in raycast of another lamp
34
- (0...@ysize).each do |y|
35
- (0...@xsize).each do |x|
36
- raycast = Z3.Add(*raycast_cells(x,y))
37
- @solver.assert Z3.Implies(@lamps[[x,y]] == 0, raycast >= 1)
38
- @solver.assert Z3.Implies(@lamps[[x,y]] == 1, raycast == 0)
39
- end
32
+ # No lamp is in raycast of another: at most one lamp per maximal white run
33
+ (white_runs(:horizontal) + white_runs(:vertical)).each do |run|
34
+ @solver.assert Z3.AtMost(run.map{|xy| @lamps[xy]}, 1)
40
35
  end
41
36
 
42
37
  if @solver.satisfiable?
@@ -53,11 +48,11 @@ class LightUp
53
48
  [[x0-1,y0],[x0+1,y0],[x0,y0-1],[x0,y0+1]].map{|x,y| @lamps[[x,y]]}.compact
54
49
  end
55
50
 
56
- def raycast_cells(x0,y0)
57
- result = []
51
+ # The white cell itself plus every white cell visible from it in a straight line
52
+ def line_of_sight(x0,y0)
53
+ result = [@lamps[[x0,y0]]]
58
54
  [[1,0],[-1,0],[0,1],[0,-1]].each do |dx,dy|
59
- x = x0+dx
60
- y = y0+dy
55
+ x, y = x0+dx, y0+dy
61
56
  while @data[[x,y]] == "."
62
57
  result << @lamps[[x,y]]
63
58
  x += dx
@@ -67,6 +62,22 @@ class LightUp
67
62
  result
68
63
  end
69
64
 
65
+ # Maximal runs of white cells, broken up by walls, in either direction
66
+ def white_runs(direction)
67
+ lines = if direction == :horizontal
68
+ (0...@ysize).map{|y| (0...@xsize).map{|x| [x,y]}}
69
+ else
70
+ (0...@xsize).map{|x| (0...@ysize).map{|y| [x,y]}}
71
+ end
72
+ lines.flat_map do |line|
73
+ line.chunk{|xy| @data[xy] == "."}.select{|white,_| white}.map{|_,run| run}
74
+ end
75
+ end
76
+
77
+ def each_cell
78
+ (0...@ysize).each{|y| (0...@xsize).each{|x| yield x, y}}
79
+ end
80
+
70
81
  def map_coordinates
71
82
  Hash[(0...@xsize).to_a.product((0...@ysize).to_a).map{|x,y| [[x,y],yield(x,y)]}]
72
83
  end
@@ -76,7 +87,7 @@ class LightUp
76
87
  (0...@xsize).each do |x|
77
88
  if @data[[x,y]] != "."
78
89
  print(@data[[x,y]])
79
- elsif @model[@lamps[[x,y]]].to_i == 1
90
+ elsif @model[@lamps[[x,y]]].to_b
80
91
  print("*")
81
92
  else
82
93
  print(" ")
@@ -85,13 +96,6 @@ class LightUp
85
96
  puts "\n"
86
97
  end
87
98
  end
88
-
89
- def int01(x,y)
90
- v = Z3.Int("l#{x},#{y}")
91
- @solver.assert v >= 0
92
- @solver.assert v <= 1
93
- v
94
- end
95
99
  end
96
100
 
97
101
  path = ARGV[0] || Pathname(__dir__) + "light_up-1.txt"
@@ -0,0 +1,2 @@
1
+ 0.
2
+ ..
data/examples/nanro CHANGED
@@ -91,10 +91,7 @@ class Nanro
91
91
  @solver.assert (cvar(x,y) != 0).implies(Z3.Or(nvar(x,y) == 0, nvar(x,y) == nval))
92
92
  end
93
93
 
94
- @solver.assert Z3.Add(
95
- *enum_for(:each_xy).map{|x,y|
96
- (nvar(x,y) == 0).ite(1,0)
97
- }) == 1
94
+ @solver.assert Z3.Exactly(enum_for(:each_xy).map{|x,y| nvar(x,y) == 0 }, 1)
98
95
  end
99
96
 
100
97
  def call
@@ -47,7 +47,7 @@ class RegexpStringMatcher
47
47
  ~regexp_match([:seq, anchor[1], [:any_star]], start_idx, @str.size)
48
48
  when :lookbehind
49
49
  regexp_match([:seq, [:any_star], anchor[1]], 0, start_idx)
50
- when :lookbehind
50
+ when :negative_lookbehind
51
51
  ~regexp_match([:seq, [:any_star], anchor[1]], 0, start_idx)
52
52
  else
53
53
  raise "Unknown anchor type #{anchor[0]}"
@@ -108,7 +108,7 @@ class SimpleRegexpParser
108
108
  sequence(repeat(base, min - 1, max), part)
109
109
  end
110
110
  elsif max == 0 # a{0} -> empty, not really a thing
111
- :empty
111
+ empty
112
112
  else
113
113
  if min == 0
114
114
  # (a){2,3} -> ()|a{1,2}(a)
data/examples/star_battle CHANGED
@@ -15,20 +15,17 @@ class StarBattle
15
15
  def call
16
16
  # Each row contains 2 stars
17
17
  @size.times do |y|
18
- sum = Z3.Add(*@size.times.map{|x| cell_var(x, y).ite(1, 0) })
19
- @solver.assert sum == 2
18
+ @solver.assert Z3.Exactly(@size.times.map{|x| cell_var(x, y) }, 2)
20
19
  end
21
20
 
22
21
  # Each column contains 2 stars
23
22
  @size.times do |x|
24
- sum = Z3.Add(*@size.times.map{|y| cell_var(x, y).ite(1, 0) })
25
- @solver.assert sum == 2
23
+ @solver.assert Z3.Exactly(@size.times.map{|y| cell_var(x, y) }, 2)
26
24
  end
27
25
 
28
26
  # Each container contains 2 stars
29
27
  coords.group_by{|x,y| container_at(x,y) }.each do |name, cells|
30
- sum = Z3.Add(*cells.map{|x,y| cell_var(x, y).ite(1, 0) })
31
- @solver.assert sum == 2
28
+ @solver.assert Z3.Exactly(cells.map{|x,y| cell_var(x, y) }, 2)
32
29
  end
33
30
 
34
31
  # Can't be adjacent
data/examples/stitches CHANGED
@@ -55,20 +55,20 @@ class StitchesSolver
55
55
  end
56
56
 
57
57
  def row_of_cell_vars(y)
58
- @xsize.times.map{|x| @cell_vars[[x,y]].ite(1,0) }
58
+ @xsize.times.map{|x| @cell_vars[[x,y]] }
59
59
  end
60
60
 
61
61
  def col_of_cell_vars(x)
62
- @ysize.times.map{|y| @cell_vars[[x,y]].ite(1,0) }
62
+ @ysize.times.map{|y| @cell_vars[[x,y]] }
63
63
  end
64
64
 
65
65
  def setup_cell_assertions!
66
66
  @xsize.times do |x|
67
- @solver.assert Z3.Add(*col_of_cell_vars(x)) == @col_counts[x]
67
+ @solver.assert Z3.Exactly(col_of_cell_vars(x), @col_counts[x])
68
68
  end
69
69
 
70
70
  @ysize.times do |y|
71
- @solver.assert Z3.Add(*row_of_cell_vars(y)) == @row_counts[y]
71
+ @solver.assert Z3.Exactly(row_of_cell_vars(y), @row_counts[y])
72
72
  end
73
73
  end
74
74
 
@@ -110,7 +110,7 @@ class StitchesSolver
110
110
 
111
111
  def setup_stitch_assertions!
112
112
  @stitch_groups.each do |g, vars|
113
- @solver.assert Z3.Add(*vars.map{|v| v.ite(1,0)}) == @stitch_count
113
+ @solver.assert Z3.Exactly(vars, @stitch_count)
114
114
  end
115
115
  end
116
116
 
data/examples/yajilin CHANGED
@@ -134,7 +134,8 @@ class Yajilin
134
134
  raise
135
135
  end
136
136
 
137
- @solver.assert Z3.Add(*stripe.map{|c| c.ite(0,1)}) == count
137
+ # cvar is true for white, so ~cvar counts the black cells the arrow sees
138
+ @solver.assert Z3.Exactly(stripe.map{|c| ~c }, count)
138
139
  end
139
140
  end
140
141
  end
@@ -182,10 +183,7 @@ class Yajilin
182
183
  end
183
184
 
184
185
  # 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
186
+ @solver.assert Z3.Exactly(enum_for(:each_xy).map{|x,y| nvar(x,y) == 0 }, 1)
189
187
  end
190
188
 
191
189
  def call
@@ -77,10 +77,12 @@ module Z3
77
77
  end
78
78
 
79
79
  def rotate_left(num)
80
+ raise Z3::Exception, "Rotation amount must be a nonnegative Integer" unless num.is_a?(Integer) and num >= 0
80
81
  sort.new(LowLevel.mk_rotate_left(num, self))
81
82
  end
82
83
 
83
84
  def rotate_right(num)
85
+ raise Z3::Exception, "Rotation amount must be a nonnegative Integer" unless num.is_a?(Integer) and num >= 0
84
86
  sort.new(LowLevel.mk_rotate_right(num, self))
85
87
  end
86
88
 
@@ -95,10 +97,12 @@ module Z3
95
97
  end
96
98
 
97
99
  def zero_ext(size)
100
+ raise Z3::Exception, "Extension size must be a nonnegative Integer" unless size.is_a?(Integer) and size >= 0
98
101
  BitvecSort.new(sort.size + size).new(LowLevel.mk_zero_ext(size, self))
99
102
  end
100
103
 
101
104
  def sign_ext(size)
105
+ raise Z3::Exception, "Extension size must be a nonnegative Integer" unless size.is_a?(Integer) and size >= 0
102
106
  BitvecSort.new(sort.size + size).new(LowLevel.mk_sign_ext(size, self))
103
107
  end
104
108
 
@@ -106,9 +110,6 @@ module Z3
106
110
  raise Z3::Exception, "Use #signed_add_no_overflow? or #unsigned_add_no_overflow? for Bitvec, not #add_no_overflow?"
107
111
  end
108
112
 
109
- def add_no_overflow?(other)
110
- raise "Use signed_add_no_overflow? or unsigned_add_no_overflow?"
111
- end
112
113
  def signed_add_no_overflow?(other)
113
114
  BitvecExpr.SignedAddNoOverflow(self, other)
114
115
  end
@@ -123,11 +124,11 @@ module Z3
123
124
  BitvecExpr.SignedAddNoUnderflow(self, other)
124
125
  end
125
126
  def unsigned_add_no_underflow?(other)
126
- raise "Unsigned + cannot underflow"
127
+ raise Z3::Exception, "Unsigned + cannot underflow"
127
128
  end
128
129
 
129
130
  def unsigned_neg_no_overflow?
130
- raise "There is no unsigned negation"
131
+ raise Z3::Exception, "There is no unsigned negation"
131
132
  end
132
133
  def signed_neg_no_overflow?
133
134
  BitvecExpr.SignedNegNoOverflow(self)
@@ -137,7 +138,7 @@ module Z3
137
138
  end
138
139
 
139
140
  def mul_no_overflow?(other)
140
- raise "Use signed_mul_no_overflow? or unsigned_mul_no_overflow?"
141
+ raise Z3::Exception, "Use signed_mul_no_overflow? or unsigned_mul_no_overflow?"
141
142
  end
142
143
  def signed_mul_no_overflow?(other)
143
144
  BitvecExpr.SignedMulNoOverflow(self, other)
@@ -153,7 +154,7 @@ module Z3
153
154
  BitvecExpr.SignedMulNoUnderflow(self, other)
154
155
  end
155
156
  def unsigned_mul_no_underflow?(other)
156
- raise "Unsigned + cannot underflow"
157
+ raise Z3::Exception, "Unsigned * cannot underflow"
157
158
  end
158
159
 
159
160
  def div_no_overflow?(other)
@@ -163,7 +164,7 @@ module Z3
163
164
  BitvecExpr.SignedDivNoOverflow(self, other)
164
165
  end
165
166
  def unsigned_div_no_overflow?(other)
166
- raise "Unsigned / cannot underflow"
167
+ raise Z3::Exception, "Unsigned / cannot overflow"
167
168
  end
168
169
 
169
170
  def >>(other)
@@ -71,6 +71,32 @@ module Z3
71
71
  b, c = coerce_to_same_sort(b, c)
72
72
  b.sort.new(LowLevel.mk_ite(a, b, c))
73
73
  end
74
+
75
+ # Native cardinality constraint: at most k of the given Bool exprs are true
76
+ def AtMost(args, k)
77
+ args = cardinality_args(args, k)
78
+ BoolSort.new.new(LowLevel.mk_atmost(args, k))
79
+ end
80
+
81
+ # Native cardinality constraint: at least k of the given Bool exprs are true
82
+ def AtLeast(args, k)
83
+ args = cardinality_args(args, k)
84
+ BoolSort.new.new(LowLevel.mk_atleast(args, k))
85
+ end
86
+
87
+ # Native cardinality constraint: exactly k of the given Bool exprs are true
88
+ def Exactly(args, k)
89
+ args = cardinality_args(args, k)
90
+ BoolSort.new.new(LowLevel.mk_pbeq(args, [1] * args.size, k))
91
+ end
92
+
93
+ private
94
+
95
+ def cardinality_args(args, k)
96
+ raise Z3::Exception, "Cardinality bound must be a non-negative Integer" unless k.is_a?(Integer) and k >= 0
97
+ raise Z3::Exception, "Cardinality constraint requires at least one argument" if args.empty?
98
+ coerce_to_same_bool_sort(*args)
99
+ end
74
100
  end
75
101
  end
76
102
  end
data/lib/z3/expr/expr.rb CHANGED
@@ -106,6 +106,7 @@ module Z3
106
106
  end
107
107
 
108
108
  def And(*args)
109
+ raise Z3::Exception, "And requires at least one argument" if args.empty?
109
110
  args = coerce_to_same_sort(*args)
110
111
  case args[0]
111
112
  when BoolExpr
@@ -120,6 +121,7 @@ module Z3
120
121
  end
121
122
 
122
123
  def Or(*args)
124
+ raise Z3::Exception, "Or requires at least one argument" if args.empty?
123
125
  args = coerce_to_same_sort(*args)
124
126
  case args[0]
125
127
  when BoolExpr
@@ -134,6 +136,7 @@ module Z3
134
136
  end
135
137
 
136
138
  def Xor(*args)
139
+ raise Z3::Exception, "Xor requires at least one argument" if args.empty?
137
140
  args = coerce_to_same_sort(*args)
138
141
  case args[0]
139
142
  when BoolExpr
@@ -150,7 +153,7 @@ module Z3
150
153
  end
151
154
 
152
155
  def Add(*args)
153
- raise Z3::Exception if args.empty?
156
+ raise Z3::Exception, "Add requires at least one argument" if args.empty?
154
157
  args = coerce_to_same_sort(*args)
155
158
  case args[0]
156
159
  when ArithExpr
@@ -165,6 +168,7 @@ module Z3
165
168
  end
166
169
 
167
170
  def Sub(*args)
171
+ raise Z3::Exception, "Sub requires at least one argument" if args.empty?
168
172
  args = coerce_to_same_sort(*args)
169
173
  case args[0]
170
174
  when ArithExpr
@@ -179,6 +183,7 @@ module Z3
179
183
  end
180
184
 
181
185
  def Mul(*args)
186
+ raise Z3::Exception, "Mul requires at least one argument" if args.empty?
182
187
  args = coerce_to_same_sort(*args)
183
188
  case args[0]
184
189
  when ArithExpr
@@ -23,7 +23,7 @@ module Z3
23
23
  end
24
24
 
25
25
  def intersection(other)
26
- SetExpr.Union(self, other)
26
+ SetExpr.Intersection(self, other)
27
27
  end
28
28
 
29
29
  def difference(other)
data/lib/z3/func_decl.rb CHANGED
@@ -23,10 +23,35 @@ module Z3
23
23
  Sort.from_pointer(LowLevel::get_range(self))
24
24
  end
25
25
 
26
- # def ast_parameter(i)
27
- # # vs arity ?
28
- # Z3::Ast.new(LowLevel.get_decl_ast_parameter(self, i))
29
- # end
26
+ # Decl parameters are the `_`-part of an indexed decl like
27
+ # `(_ map or)`, `(_ extract 7 0)`, `(_ zero_extend 8)`.
28
+ # They have nothing to do with the domain, so they're not counted by #arity.
29
+ PARAMETER_KINDS = {
30
+ 0 => :int,
31
+ 1 => :double,
32
+ 2 => :rational,
33
+ 3 => :symbol,
34
+ 4 => :sort,
35
+ 5 => :ast,
36
+ 6 => :func_decl,
37
+ 7 => :internal,
38
+ 8 => :zstring,
39
+ }
40
+
41
+ def num_parameters
42
+ LowLevel.get_decl_num_parameters(self)
43
+ end
44
+
45
+ def parameter_kind(i)
46
+ raise Z3::Exception, "Trying to access parameter #{i} but decl has #{num_parameters} parameters" if i < 0 or i >= num_parameters
47
+ k = LowLevel.get_decl_parameter_kind(self, i)
48
+ PARAMETER_KINDS.fetch(k) { raise Z3::Exception, "Unknown decl parameter kind #{k}" }
49
+ end
50
+
51
+ def func_decl_parameter(i)
52
+ raise Z3::Exception, "Parameter #{i} is a #{parameter_kind(i)}, not a func decl" unless parameter_kind(i) == :func_decl
53
+ FuncDecl.new(LowLevel.get_decl_func_decl_parameter(self, i))
54
+ end
30
55
 
31
56
  def to_s
32
57
  name
data/lib/z3/goal.rb CHANGED
@@ -1,8 +1,11 @@
1
1
  module Z3
2
2
  class Goal
3
+ include ReferenceCounted
4
+
3
5
  attr_reader :_goal
4
6
  def initialize(_goal)
5
7
  @_goal = _goal
8
+ inc_ref! :goal, _goal
6
9
  end
7
10
 
8
11
  def assert(ast)
@@ -55,6 +58,10 @@ module Z3
55
58
  LowLevel.goal_to_string(self)
56
59
  end
57
60
 
61
+ def to_dimacs(include_names=true)
62
+ LowLevel.goal_to_dimacs_string(self, include_names)
63
+ end
64
+
58
65
  class << self
59
66
  def new(models=false, unsat_cores=false, proofs=false)
60
67
  super LowLevel.mk_goal(!!models, !!unsat_cores, !!proofs)