31
loading...
This website collects cookies to deliver better user experience
$ brew install z3
$ gem install z3
2 + X = 4
.#!/usr/bin/env ruby
require "z3"
solver = Z3::Solver.new
solver.assert 2 + Z3.Int("X") == 4
if solver.satisfiable?
model = solver.model
puts "X = #{model[Z3.Int("X")]}"
else
puts "There are no solutions"
end
$ ./x_plus_2_equals_4
X = 2
2 + Z3.Var("X") == 4
and telling it Z3.Var("X") is an integer
separately, each variable carries both its name and its type (called "sort" in Z3, mostly to avoid confusion with programming language types).==
operator. This is much more readable than typing out something like Z3.Equals(Z3.Add(Z3.Number(2), Z3.Int("X")), Z3.Number(4))
).and/&&
, or/||
, and ?:
are not overloadable in Ruby, so we'd need to use &
and |
instead.#!/usr/bin/env ruby
require "z3"
solver = Z3::Solver.new
letters = %w[s e n d m o r e m o n e y].uniq.to_h{|letter| [letter, Z3.Int(letter)]}
letters.each_value do |var|
solver.assert var >= 0
solver.assert var <= 9
end
solver.assert letters["m"] != 0
solver.assert letters["s"] != 0
solver.assert Z3.Distinct(*letters.values)
send = 1000 * letters["s"] + 100 * letters["e"] + 10 * letters["n"] + letters["d"]
more = 1000 * letters["m"] + 100 * letters["o"] + 10 * letters["r"] + letters["e"]
money = 10000 * letters["m"] + 1000 * letters["o"] + 100 * letters["n"] + 10 * letters["e"] + letters["y"]
solver.assert send + more == money
if solver.satisfiable?
model = solver.model
model.each do |var, value|
puts "#{var}=#{value}"
end
puts "#{model[send]} + #{model[more]} = #{model[money]}"
else
puts "There are no solutions"
end
$ ./send_more_money
d=7
e=5
m=1
n=6
o=0
r=8
s=9
y=2
9567 + 1085 = 10652
Z3.Int(letter)
can get rather tedious, so I created a dictionary {"s" => Z3.Int("s"), ...}
to make the code a bit more concise.var >= 0
and var <= 9
0
, as per rules of the puzzleZ3.Distinct(var1, var2, ...)
instead of double nested loop of var1 != var2
*
and +
#!/usr/bin/env ruby
require "z3"
solver = Z3::Solver.new
fib = (0..100).map{|i| Z3.Int("fib(#{i})")}
solver.assert fib[0] == 0
solver.assert fib[1] == 1
(2..100).each do |i|
solver.assert fib[i] == fib[i-1] + fib[i-2]
end
if solver.satisfiable?
model = solver.model
fib.each do |var|
puts "#{var}=#{model[var]}"
end
else
puts "There are no solutions"
end
$ ./fib
fib(0)=0
fib(1)=1
fib(2)=1
fib(3)=2
fib(4)=3
fib(5)=5
fib(6)=8
fib(7)=13
fib(8)=21
fib(9)=34
fib(10)=55
fib(11)=89
fib(12)=144
fib(13)=233
fib(14)=377
fib(15)=610
fib(16)=987
fib(17)=1597
fib(18)=2584
fib(19)=4181
fib(20)=6765
...
fib(90)=2880067194370816120
fib(91)=4660046610375530309
fib(92)=7540113804746346429
fib(93)=12200160415121876738
fib(94)=19740274219868223167
fib(95)=31940434634990099905
fib(96)=51680708854858323072
fib(97)=83621143489848422977
fib(98)=135301852344706746049
fib(99)=218922995834555169026
fib(100)=354224848179261915075
fib(42)
and fib(69)
and asked it for the rest?#!/usr/bin/env ruby
require "z3"
solver = Z3::Solver.new
fib = (0..100).map{|i| Z3.Int("fib(#{i})")}
solver.assert fib[42] == 267914296
solver.assert fib[69] == 117669030460994
(2..100).each do |i|
solver.assert fib[i] == fib[i-1] + fib[i-2]
end
if solver.satisfiable?
model = solver.model
fib.each do |var|
puts "#{var}=#{model[var]}"
end
else
puts "There are no solutions"
end
#!/usr/bin/env ruby
require "z3"
sudoku = %w[
_6_5_9_4_
92_____76
__1___9__
7__6_3__9
_________
3__4_1__7
__6___7__
24_____65
_9_1_8_3_
]
solver = Z3::Solver.new
vars = (0..8).map{|y|
(0..8).map{|x|
Z3.Int("cell[#{x},#{y}]")
}
}
# All cells are 1-9
vars.flatten.each do |var|
solver.assert var >= 1
solver.assert var <= 9
end
# Cells match sudoku grid if it already has numbers
9.times do |y|
9.times do |x|
if sudoku[y][x] =~ /\d/
solver.assert vars[y][x] == sudoku[y][x].to_i
end
end
end
# Each row has distinct numbers
vars.each do |row|
solver.assert Z3.Distinct(*row)
end
# Each column has distinct numbers
vars.transpose.each do |col|
solver.assert Z3.Distinct(*col)
end
# Each box has distinct numbers
[0, 3, 6].each do |i|
[0, 3, 6].each do |j|
cells = vars[i, 3].flat_map{|row| row[j, 3]}
solver.assert Z3.Distinct(*cells)
end
end
if solver.satisfiable?
model = solver.model
9.times do |y|
9.times do |x|
print model[vars[y][x]]
end
print "\n"
end
end
$ ./sudoku
863579241
925314876
471826953
714683529
689752314
352491687
136245798
248937165
597168432
X
is true. So we send Z3 a list of assertions, then we send it not X
, and ask for a model. If it says imposible, then this proves X
. Otherwise the model is the counterexample, so we not only know X
is not always true, but we have an example for it.#!/usr/bin/env ruby
require "z3"
solver = Z3::Solver.new
v = Z3.Bitvec("v", 64)
naive_is_power_of_two = Z3.Or(*(0..63).map{|i| v == 2**i})
tricky_is_power_of_two = (v & (v - 1)) == 0
theorem = (tricky_is_power_of_two == naive_is_power_of_two)
solver.assert !theorem
if solver.satisfiable?
counterexample = solver.model
puts "Counterexample exists:"
counterexample.each do |n,v|
puts "* #{n} = #{v}"
end
else
puts "There are no counterexamples, it always holds."
end
$ ./power_of_two
Counterexample exists:
* v = 0
v = 0
!Z3.Bitvec("v", 64)
declared v
as 64-bit bit vector (int64
or such in most low level languages). Z3 doesn't distinguish between "signed" and "unsigned" bitvectors - for operations where it matters like >
, it provides .signed_gt
and .unsigned_gt
versions, but most things (+
, *
, ==
, &
, |
) etc. work the same.naive_is_power_of_two
as (v == 1) | (v == 2) | (v == 4) | ... | (v == 2**63)
. Z3.Or(s1, s2, s3, ...)
is a shortcut, as it's very common code. As I mentioned before, Ruby's ||
cannot be overloaded so we need to use either |
or Z3.Or
.tricky_is_power_of_two
to be our trick operation. For bitvectors, Z3 provides bit operations like &
, |
, ^
etc., in addition to regular arithmetic. Z3 generally cannot do bit operations on unlimited size integers.tricky_is_power_of_two == naive_is_power_of_two
could be false#!/usr/bin/env ruby
require "z3"
solver = Z3::Solver.new
v = Z3.Bitvec("v", 64)
naive_is_power_of_two = Z3.Or(*(0..63).map{|i| v == 2**i})
tricky_is_power_of_two = (v != 0) & ((v & (v - 1)) == 0)
theorem = (tricky_is_power_of_two == naive_is_power_of_two)
solver.assert !theorem
if solver.satisfiable?
counterexample = solver.model
puts "Counterexample exists:"
counterexample.each do |n,v|
puts "* #{n} = #{v}"
end
else
puts "There are no counterexamples, it always holds."
end
$ ./power_of_two_2
There are no counterexamples, it always holds.
2**64
possible values, but for Z3 it's no big deal.#!/usr/bin/env ruby
# A race car accelerates uniformly from 18.5 m/s to 46.1 m/s in 2.47 seconds.
# Determine the acceleration of the car and the distance traveled.
require "z3"
solver = Z3::Solver.new
a = Z3.Real("a")
v0 = Z3.Real("v0")
v1 = Z3.Real("v1")
t = Z3.Real("t")
d = Z3.Real("d")
solver.assert a == (v1 - v0) / t
solver.assert v0 == 18.5
solver.assert v1 == 46.1
solver.assert t == 2.47
solver.assert d == (v0 + v1) / 2 * t
if solver.satisfiable?
model = solver.model
model.each do |var, value|
puts "#{var}=#{value}"
end
else
puts "There are no solutions"
end
$ ./physics
a=2760/247
d=79781/1000
t=247/100
v0=37/2
v1=461/10
.to_f
.