0

Given 2 equations c == a + 4 and t == c + b, if a == -4, then t == b. I am trying to do the opposite, meaning given the above 2 equations, and t == b, I try to find value of a.

I have below code to do this with ForAll and Implies:

from z3 import *

a, b, c, t = BitVecs('a b c t', 32)

g = True
g = And(g, c == (a + 4))
g = And(g, t == (c + b))

s = Solver()
s.add(ForAll([c, t, b], Implies(g, t == b)))

if s.check() == sat:
    print s.model()[a]
else:
    print 'Unsat'

However, running the above code returns Unsat. I expect this returns -4 (or 0xfffffffc), so pretty confused.

Any idea where I am wrong?

Thanks!

user311703
  • 1,113
  • 2
  • 14
  • 25

1 Answers1

3

The formulas you wrote don't correspond to the textual description of your problem. In particular, the implication you used does not ensure t == b must be true for the formulas to be satisfiable.

Based on the textual description of your problem, if t == b is true, then this means the only way t == c + b is true is if c == 0. Since c == 0, then the only way c == a + 4 is true is if a == -4 as desired. The assignments to t and b are arbitrary.

Here are two ways to encode this. In the first case, z3 assigns 0 to t and b both as they are implicitly existentially quantified. In the second case, I've used a universal quantifier over b and t to highlight that the assignment is arbitrary. The formulas cannot be satisfiable for an arbitrary choice of c based on the discussion just stated (since c == 0 must be true), so c should not be universally quantified. The following illustrates this (http://rise4fun.com/Z3Py/uGEV ):

a, b, c, t = BitVecs('a b c t', 32)

g = True
g = And(g, c == (a + 4))
g = And(g, t == (c + b))
g = And(g, t == b)

s = Solver()
s.add(g)
s.check()
print s.model()
ma = s.model()[a]
s = Solver()
s.add(Not(ma == 0xfffffffc))
print s.check() # unsat, proves ma is -4

solve(g) # alternatively, just solve the equations

# illustrate that the assignments to t and b are arbitrary
s = Solver()
g = True
g = And(g, c == (a + 4))
g = And(g, t == (c + b))
g = ForAll([t, b], Implies(t == b, g))
s.add(g)
s.check()
print s.model()
ma = s.model()[a]

Update Based on the comments, below, I hope this example will help to see why you need Implies(t == b, g) instead of Implies(g, t == b) (with z3py link: http://rise4fun.com/Z3Py/pl80I ):

p1, p2 = BitVecs('p1 p2', 4)

solve(Implies(p1 == 1, p2 == 2)) # gives p1 = 14, p2 = 13

solve(And(p1 == 0, Implies(p1 == 1, p2 == 2))) # gives p1 = 0, p2 unassigned

solve(And(p1 == 0, p1 == 1)) # no solution to illustrate that p1 can only be 0 or 1 in previous, but would be unsat is trying to assign both

solve(ForAll([p1], Implies(p1 == 1, p2 == 2))) # gives p2 == 2

solve(ForAll([p1], Implies(p2 == 2, p1 == 1))) # gives p2 = 0 and does not ensure p1 == 1 is true since the formula is satisfiable by picking p2 != 2
Taylor T. Johnson
  • 2,834
  • 16
  • 17
  • Thanks a lot, Taylor! I have few concerns: (1) Your first encode doesnt seem right to me, because if Z3 happens to give another value of `a`, the next part (trying to add opposite condition) would not work. (2) Why in the second encode you had `Implies(t==b, g)`, but not `Implies(g, t==b)`? (3) In more generalized problems, is there any way for us to know which variables (like `c` in this case) should not be universally qualified? I mean how to decide this with computation? Thanks a lot! – user311703 Jun 25 '13 at 15:56
  • 1
    (1) Adding the negation was just done to illustrate the code did what you wanted (assigned the right value to `a`). Of course, if you change the assertions, then a different value might be assigned, and this would fail. You can remove this without changing anything. (2) The way I wrote it is the correct way to encode your textual description that `t == b` holds. (3) As far as an algorithmic way, I'm not sure. I'd first try `solve`, then reason about which variables are assigned arbitrarily. There is probably some relationship to boolean 'don't cares' that someone else might comment on. – Taylor T. Johnson Jun 25 '13 at 16:34
  • Taylor, on (2) above: actually both `g` and `t==b` hold, so IMHO it is possible to have `Implies(g, t==b)` as well. However, when I replaced `Implies(t==b, g)` with `Implies(g, t==b)`, the returned value of `a` is **0**, not **-4** anymore. This is pretty confused to me. Any idea why this happens? Thanks! – user311703 Jun 26 '13 at 02:39
  • You want `Implies(t == b, g)`. This is from the definition of `Implies(p1, p2)`, which is`Or(Not(p1), p2)` (see http://en.wikipedia.org/wiki/Truth_table#Logical_implication ). To ensure `a == -4`, you need `t == b`. If you use `Implies(g, t == b)`, then it is possible for the formula to be satisfiable if `Not(g)` is satisfiable with an arbitrary assignment to whether `t == b` is true (so suppose `t == b` is false, i.e., `t != b` is true). If you use `Implies(t == b, g)`, and `t == b` is true, then for `g` to hold, the assignment must be `a == -4`. See update at answer's end for an example. – Taylor T. Johnson Jun 26 '13 at 12:28