6

just wondering in z3py , how do I check if a given constant expression is a variable or value ? For example

x = Int('x')
x_ = IntVal(7)
ColorVal, (White,Black)  = EnumSort("ColorVal",["While","Black"])
mycolor = Const("mycolor",ColorVal)

So x,mycolor would all be variables and x_,True,False,White,Black would be values and not variables .

z3py has is_var predicate but for different purpose. This is useful if I want to rename all variables in a formula to something else.

Greg Hewgill
  • 951,095
  • 183
  • 1,149
  • 1,285
Vu Nguyen
  • 987
  • 1
  • 9
  • 20

2 Answers2

8

What you call variables are (technically) called uninterpreted constants in Z3. Values (such as 1, true, #x01) are called interpreted constants. So, in principle, a fast way to check whether a is a "variable" can be done using the following piece of code:

is_const(a) and a.decl().kind() == Z3_OP_UNINTERPRETED

This piece of code works for everything, but datatypes. After trying your example, I realized that Z3 is incorrectly returning Z3_OP_UNINTERPRETED for datatype constructors. I fixed that for Z3 4.2. In the meantime, you can use the following workaround where the function is_datatype_const_value(a) returns True is a is a constant constructor.

def is_datatype_sort(s):
  return s.kind() == Z3_DATATYPE_SORT

def is_datatype_constructor(x):
  s = x.sort()
  if is_datatype_sort(s):
    n = s.num_constructors()
    f = x.decl()
    for i in range(n):
      c = s.constructor(i)
      if eq(c, f):
        return True
  return False 

# Return True if x is a constant constructor, that is, a constructor without arguments.
def is_datatype_const_value(x):
  return is_const(x) and is_datatype_constructor(x)

Then, the following code catches all uninterpreted constants:

 is_const(a) and a.decl().kind() == Z3_OP_UNINTERPRETED and not is_datatype_const_value(a)

The following link contains a complete example. http://rise4fun.com/Z3Py/vjb

zell
  • 9,830
  • 10
  • 62
  • 115
Leonardo de Moura
  • 21,065
  • 2
  • 47
  • 53
0

One way to do this for the integers is with is_int and is_int_value:

x = Int('x')
print "x types"
print is_int(x) # true, is of sort int
print is_int_value(x) # false, not a "value"

x_ = IntVal(7)
print "x_ types"
print is_int(x_) # true, is also of sort int
print is_int_value(x_) # true, is a value

For reals, you can do this using is_real to check the variable sort, and use (the disjunction of) is_algebraic_value and is_rational_value for the values (I didn't see a function like is_real_value in the API, but I think this disjunct will do it). For bitvectors, you can use is_bv_value for values, and is_bv to check the variable sort.

The .NET API has Expr.IsNumeral, and you can see how these are implemented in the API here (e.g., Expr.IsIntNum [the equivalent of the Python is_int_value] checks if both Expr.IsNumeral and Expr.IsInt are true): http://research.microsoft.com/en-us/um/redmond/projects/z3/_expr_8cs_source.html

I did not immediately see a way to do this for custom-defined enumeration sorts. As one alternative, you could encode your enum using bitvectors and compare variables / values using is_bv_value. As a better workaround though, you appear to need to use the more general algebraic datatypes and their automatically created "recognizers". The Python API does not seem to properly create the recognizers if you declare them as enum sorts. Here's one way to do it for what's effectively an enum sort (but declared as the more general datatype).

Z3Py encoding of the following: http://rise4fun.com/Z3Py/ELtn

ColorVal = Datatype('ColorVal')
ColorVal.declare('white')
ColorVal.declare('black')
ColorVal = ColorVal.create()

mycolor = Const("mycolor", ColorVal)

print ColorVal.recognizer(0) # is_white
print ColorVal.recognizer(1) # is_black

print simplify(ColorVal.is_white(mycolor)) # returns is_white(mycolor)
print simplify(ColorVal.is_black(mycolor)) # returns is_black(mycolor)

mycolorVal = ColorVal.white # set to value white
print simplify(ColorVal.is_white(mycolorVal)) # true
print simplify(ColorVal.is_black(mycolorVal)) # false

# compare "variable" versus "value" with return of is_white, is_black, etc.: if it gives a boolean value, it's a value, if not, it's a variable
print "var vs. value"
x = simplify(ColorVal.is_white(mycolor))
print is_true(x) or is_false(x) # returns false, since x is is_white(mycolor)
y = simplify(ColorVal.is_white(mycolorVal))
print is_true(y) or is_false(y) # true

ColorValEnum, (whiteEnum,blackEnum)  = EnumSort("ColorValEnum",["whiteEnum","blackEnum"])
mycolorEnum = Const("mycolorEnum",ColorValEnum)

print ColorValEnum.recognizer(0) # is_whiteEnum
print ColorValEnum.recognizer(1) # is_blackEnum

# it appears that declaring an enum does not properly create the recognizers in the Python API:
#print simplify(ColorValEnum.is_whiteEnum(mycolorEnum)) # error: gives DatatypeSortRef instance has no attribute 'is_whiteEnum'
#print simplify(ColorValEnum.is_blackEnum(mycolorEnum)) # error: gives DatatypeSortRef instance has no attribute 'is_blackEnum'
Taylor T. Johnson
  • 2,834
  • 16
  • 17
  • right, for real, ints etc I can use those but not for custom type. – Vu Nguyen Sep 04 '12 at 22:47
  • I added way to do this for enums, so long as you declare them as a custom datatype, instead of using EnumSort. It looks like the Python API does not properly create recognizers for the datatype values if you declare them using EnumSort; there's an example of this at the bottom of the new script – Taylor T. Johnson Sep 05 '12 at 14:05
  • hi, thanks, I understand what you did and it would work. But this seems too complicated and probably takes lots of time to do all those checking, i.e. checking if mycolorVal is Colorval.v1 or v2 or v3 .... . I thought z3 would provide a simple way to check if something is a value or variable. – Vu Nguyen Sep 05 '12 at 22:21
  • actually, I think this method would work ok (originally I thought you have to check against all the recognizers). Thanks .. thought I still hope for some simpler way. – Vu Nguyen Sep 05 '12 at 22:42
  • It turns out that you can still use EnumSort, the trick is instead of using `simplify(ColorValEnum.is_whiteEnum(mycolorEnum))` , you do `simplify(ColorValEnum.recognizer(0)(mycolorEnum))` – Vu Nguyen Sep 06 '12 at 16:29