SAT solver: deceptively simple, can be applied (through tools like Alloy) to solve remarkably complex problems.
There are some things that are difficult for SAT solvers to solve. This results in things that are difficult (or impossible) to do in Alloy.
Problems with Alloy:
What if you try to put these things explicitly in the SAT solver? So instead of having just boolean variables, the solver would know what it means to add, or to be a bit vector, etc.
Z3: Sat Modulo Theory Solver (SMT Solver)
def example1():
s = Solver()
p = Bool("p")
q = Bool("q")
s.add(Or(p,q))
s.add(Or(Not(p),Not(q)))
print(s.check())
example1()
> sat
Why is it sat
, unsat
instead of true
or false
?
We don’t give any bounds to Z3, so it might come back with the answer I don't know
m = s.model
m.evaluate(p)
> False
m.evaluate(q)
> True
def example2():
s = Solver()
a = Int("a")
b = Int("b")
f = Function("f", Intsort(), Intsort()) //function of one argument from integers to integers
s.add(b > a)
s.add(f(b) < f(a))
s.check()
> sat
m = s.model()
m.evaluate(a)
> -1
m.evaluate(b)
> 0
m.evaluate(f(a)
> 0
m.evaluate(f(b))
> -1
m.evaluate(f(10))
> -1
So Z3 creates a total function from integers to integers, but it’s not necessarily continuous or anything.
Q: How do you create a two-argument function?
A: I don’t remember! But there’s a way! It’s probably in the documentation.
Note: We didn’t say for all a and b such that a > b that f(b) > f(a), so it just finds an example of an a and b for which it is true
def example3():
s = Solver()
val1 = Int("val1")
val2 = Int("val2")
idx = Int("idx")
arr = Array("arr", Intsort(), Intsort())
s.add(val1 > (2 * val2))
s.add(Select(arr, (1 + idx)) == val2)
s.check()
> sat
m = s.model()
m.evaluate(val1)
> 1
m.evaluate(val2)
> 0
m.evaluate(Select(arr, (1+idx)))
> 0
s.add(Select(Store(arr, idx, 1), idx) != 1)
s.check()
> unsat
s.model
> ERROR
def example4():
s = Solver()
x = Real("x") //computable reals, not the real reals
s.add(x ** 2 > 4)
s.add(x ** 2 < 9)
s.check()
> sat
m = s.model()
m.evaluate(x)
> 5/2
Note that this would not be satisfiable if we only had Integers
s.add(x != float(5)/2)
m.evaluate(x)
> 11/4
And we can repeat this indefinitely
s.add(x ** 2 == 6)
m.evaluate(x)
> 2.4494897427?
Note the ?
at the end. This means that Z3 did its best, but it couldn’t find the exact answer because sqrt(6)
is irrational.
Sometimes it just reduces to a SAT problem.
Other times, it can do clever things (Gaussian elimination, matrix row reduction, etc)
(y <= 5 or x > y) and (y > 5 or y < 15 or x > 10)
Lazy method: label each inequality with a boolean variable:
(A1 or A2) and (not A1 or A3 or A4)
Solve this formula with a boolean SAT solver.
We get an instance, say A1 = T, A2 = T, A3 = T, A4 = T
Then we solve the system of equations induced by this instance:
y <= 5, x > y, y < 15, x > 10
Q: What if the system we get is unsatisfiable?
A: Find another boolean instance and try again.
Q: Does the solver know that A1 implies A3?
A: A smart solver would, but a dumb/simple solver probably would not.