0

Consider a set of constraints F = [a + b > 10, a*a + b + 10 < 50].

When I run it using:

s = Solver()
s.add(F)
s.check()

I get sat solution.

If I run it with:

s = Solver()
s.check(F)

I get an unknown solution. Can someone explain why this is happening?

2 Answers2

1

Let's see:

from z3 import *

a = Int('a')
b = Int('b')
F = [a + b > 10, a*a + b + 10 < 50]

s = Solver()
s.add(F)
print (s.check())
print (s.model())

This prints:

sat
[b = 15, a = -4]

That looks good to me.

Let's try your second variant:

from z3 import *

a = Int('a')
b = Int('b')
F = [a + b > 10, a*a + b + 10 < 50]

s = Solver()
print (s.check(F))
print (s.model())

This prints:

sat
[b = 7, a = 4]

That looks good to me too.

So, I don't know how you're getting the unknown answer. Maybe you have an old version of z3; or you've some other things in your program you're not telling us about.

The important thing to note, however, is that s.add(F); s.check() AND s.check(F) are different operations:

  • s.add(F); s.check() means: Assert the constraints in F; check that they are satisfiable.

  • s.check(F) means: Check that all the other constraints are satisfiable, assuming F is. In particular, it does not assert F. (This is important if you do further asserts/checks later on.)

So, in general these two different ways of using check are used for different purposes; and can yield different answers. But in the presence of no other assertions around, you'll get a solution for both, though of course the models might be different.

Aside One reason you can get unknown is in the presence of non-linear constraints. And your a*a+b+10 < 50 is non-linear, since it does have a multiplication of a variable by itself. You can deal with that either by using a bit-vector instead of an Int (if applicable), or using the nonlinear-solver; which can still give you unknown, but might perform better. But just looking at your question as you asked it, z3 is just fine handling it.

alias
  • 28,120
  • 2
  • 23
  • 40
0

To find out what is going on within s.check(F), you can do the following:

from z3 import *
import inspect

a = Int('a')
b = Int('b')
F = [a + b > 10, a*a + b + 10 < 50]

s = Solver()
print (s.check(F))
print (s.model())

source_check = inspect.getsource(s.check)  
print(source_check)

The resulting output:

sat
[b = 10, a = 1]
    def check(self, *assumptions):
        """Check whether the assertions in the given solver plus the optional assumptions are consistent or not.

        >>> x = Int('x')
        >>> s = Solver()
        >>> s.check()
        sat
        >>> s.add(x > 0, x < 2)
        >>> s.check()
        sat
        >>> s.model().eval(x)
        1
        >>> s.add(x < 1)
        >>> s.check()
        unsat
        >>> s.reset()
        >>> s.add(2**x == 4)
        >>> s.check()
        unknown
        """
        s = BoolSort(self.ctx)
        assumptions = _get_args(assumptions)
        num = len(assumptions)
        _assumptions = (Ast * num)()
        for i in range(num):
            _assumptions[i] = s.cast(assumptions[i]).as_ast()
        r = Z3_solver_check_assumptions(self.ctx.ref(), self.solver, num, _assumptions)
        return CheckSatResult(r)

The semantics of assumptions vs. assertions are discussed here and here. But if have to admit that they are not really clear to me yet.

Axel Kemper
  • 10,544
  • 2
  • 31
  • 54