def _convert(self, obj): if isinstance(obj, FSort): return z3.FPSort(obj.exp, obj.mantissa, ctx=self._context) elif isinstance(obj, RM): if obj == RM_RNE: return z3.RNE(ctx=self._context) elif obj == RM_RNA: return z3.RNA(ctx=self._context) elif obj == RM_RTP: return z3.RTP(ctx=self._context) elif obj == RM_RTN: return z3.RTN(ctx=self._context) elif obj == RM_RTZ: return z3.RTZ(ctx=self._context) else: raise BackendError("unrecognized rounding mode") elif obj is True: return z3.BoolVal(True, ctx=self._context) elif obj is False: return z3.BoolVal(False, ctx=self._context) elif isinstance(obj, (numbers.Number, str)): return obj elif hasattr(obj, '__module__') and obj.__module__ in ('z3', 'z3.z3'): return obj else: l.debug("BackendZ3 encountered unexpected type %s", type(obj)) raise BackendError("unexpected type %s encountered in BackendZ3" % type(obj))
def from_real(self,val): assert isinstance(val,str) if val.lower() == '+inf': self._mk_pinf() elif val.lower() == '-inf': self._mk_ninf() elif val.lower() == 'nan': self._mk_nan() else: self.z3_ds = z3.FPVal(val,z3.FPSort(self.ne,self.ns))
def _ty_sort(ty): 'Translate a Type expression to a Z3 Sort' if isinstance(ty, IntType): return z3.BitVecSort(ty.width) return { PtrType: z3.BitVecSort(64), HalfType: z3.FloatHalf(), SingleType: z3.Float32(), DoubleType: z3.Float64(), FP128Type: z3.Float128(), X86FP80Type: z3.FPSort(15, 64), }[type(ty)]
def _convert(self, obj, result=None): if type(obj) is NativeBVV: return z3.BitVecVal(obj.value, obj.bits) elif isinstance(obj, FSort): return z3.FPSort(obj.exp, obj.mantissa) elif isinstance(obj, RM): if obj == RM_RNE: return z3.RNE() elif obj == RM_RNA: return z3.RNA() elif obj == RM_RTP: return z3.RTP() elif obj == RM_RTN: return z3.RTN() elif obj == RM_RTZ: return z3.RTZ() else: raise BackendError("unrecognized rounding mode") elif isinstance(obj, NativeFPV): val = str(obj.value) sort = self._convert(obj.sort) if val == 'inf': return z3.fpPlusInfinity(sort) elif val == '-inf': return z3.fpMinusInfinity(sort) elif val == '0.0': return z3.fpPlusZero(sort) elif val == '-0.0': return z3.fpMinusZero(sort) elif val == 'nan': return z3.fpNaN(sort) else: better_val = str(Decimal(obj.value)) return z3.FPVal(better_val, sort) elif obj is True: return z3.BoolVal(True) elif obj is False: return z3.BoolVal(False) elif type(obj) in (int, long, float, str): return obj elif hasattr(obj, '__module__') and obj.__module__ == 'z3': return obj else: l.debug("BackendZ3 encountered unexpected type %s", type(obj)) raise BackendError("unexpected type %s encountered in BackendZ3" % type(obj))
def _gen(expr_z3, symbolTable, cache, result): ###Leaf: var if _is_variable(expr_z3): if DEBUG: print "-- Branch _is_variable with ", expr_z3 symVar = expr_z3.decl().name() symVar = rename_var(symVar) if z3.is_int(expr_z3): symType = Sort.Int elif z3.is_fp(expr_z3): if expr_z3.sort() == z3.Float64(): symType = Sort.Float64 elif expr_z3.sort() == z3.Float32(): symType = Sort.Float32 else: raise NotImplementedError("Unexpected sort.", expr_z3.sort()) elif z3.is_real(expr_z3): symType = Sort.Float warnings.warn( "****WARNING****: Real variable '%s' treated as floating point" % symVar) else: raise NotImplementedError("Unexpected type for %s" % symbolName) if (symVar in symbolTable.keys()): assert symType == symbolTable[symVar] else: symbolTable[symVar] = symType if expr_z3.sort() == z3.Float32(): symVar = "TR32(%s)" % symVar # do the same ting for verify !!!!!!!! return symVar ###Leaf: val if _is_value(expr_z3): if DEBUG: print "-- Branch _is_value" if z3.is_fp(expr_z3) or z3.is_real(expr_z3): if DEBUG: print "---- Sub-Branch FP or Real" if isinstance(expr_z3, z3.FPNumRef): if DEBUG: print "------- Sub-Sub-Branch _is_FPNumRef" try: str_ret = str(sympy.Float(str(expr_z3), 17)) except ValueError: if expr_z3.isInf() and expr_z3.decl().kind( ) == z3.Z3_OP_FPA_PLUS_INF: str_ret = "INFINITY" elif expr_z3.isInf() and expr_z3.decl().kind( ) == z3.Z3_OP_FPA_MINUS_INF: str_ret = "- INFINITY" elif expr_z3.isNaN(): str_ret = "NAN" else: offset = 127 if expr_z3.sort() == z3.Float32( ) else 1023 #Z3 new version needs the offset to be taken into consideration expr_z3_exponent = expr_z3.exponent_as_long() - offset str_ret = str( sympy.Float((-1)**float(expr_z3.sign()) * float(str(expr_z3.significand())) * 2**(expr_z3_exponent), 17)) else: if DEBUG: print "------- Sub-Sub-Branch other than FPNumRef, probably FPRef" str_ret = str(sympy.Float(str((expr_z3)), 17)) elif z3.is_int(expr_z3): if DEBUG: print "---- Sub-Branch Integer" str_ret = str(sympy.Integer(str(expr_z3))) elif _is_true(expr_z3): str_ret = "0" elif _is_false(expr_z3): str_ret = "1" else: raise NotImplementedError( "[XSat: Coral Benchmarking] type not considered ") if expr_z3.sort() == z3.Float32(): str_ret = str_ret + "f" return str_ret #if (expr_z3 in cache): return cache[expr_z3] #cache will be a set of defined IDs #if (var_name(expr_z3) in cache): return cache[expr_z3] if (expr_z3.get_id() in cache): return var_name(expr_z3) cache.add(expr_z3.get_id()) #cache[expr_z3]=var_name(expr_z3) sort_z3 = expr_z3.decl().kind() expr_type = 'double' if expr_z3.sort() == z3.FPSort(8, 24): expr_type = 'float' ### if sort_z3 == z3.Z3_OP_FPA_LE: if DEBUG: print "-- Branch _is_le" lhs = _gen(expr_z3.arg(0), symbolTable, cache, result) rhs = _gen(expr_z3.arg(1), symbolTable, cache, result) toAppend= "double %s = DLE(%s,%s); " \ %( var_name(expr_z3), \ lhs,\ rhs,\ ) result.append(toAppend) return var_name(expr_z3) #########!!!!!!!!!!!! need to do something if sort_z3 == z3.Z3_OP_FPA_TO_FP: if DEBUG: print "-- Branch _is_fpFP" assert expr_z3.num_args() == 2 if not (_is_RNE(expr_z3.arg(0))): warnings.warn( "WARNING!!! I expect the first argument of fpFP is RNE, but it is ", expr_z3.arg(0)) x = _gen(expr_z3.arg(1), symbolTable, cache, result) if expr_z3.sort() == z3.FPSort(8, 24): x = "TR32(%s)" % x #else if expr_z3.sort()==z3.FPSort(8,24): # x = "TR(%s)" %x toAppend= "%s %s = %s; " \ %( expr_type, var_name(expr_z3), \ x,\ ) result.append(toAppend) return var_name(expr_z3) if sort_z3 == z3.Z3_OP_FPA_LT: if DEBUG: print "-- Branch _is_lt" lhs = _gen(expr_z3.arg(0), symbolTable, cache, result) rhs = _gen(expr_z3.arg(1), symbolTable, cache, result) toAppend= "double %s = DLT(%s,%s);" \ %( var_name(expr_z3), \ lhs,\ rhs,\ ) result.append(toAppend) return var_name(expr_z3) if _is_eq(expr_z3): if DEBUG: print "-- Branch _is_eq" lhs = _gen(expr_z3.arg(0), symbolTable, cache, result) rhs = _gen(expr_z3.arg(1), symbolTable, cache, result) toAppend= "double %s = DEQ(%s,%s);" \ %( var_name(expr_z3), \ lhs,\ rhs,\ ) result.append(toAppend) return var_name(expr_z3) if _is_fpMul(expr_z3): if DEBUG: print "-- Branch _is_fpMul" if not _is_RNE(expr_z3.arg(0)): warnings.warn( "WARNING!!! arg(0) is not RNE but is treated as RNE. arg(0) = ", expr_z3.arg(0)) assert expr_z3.num_args() == 3 lhs = _gen(expr_z3.arg(1), symbolTable, cache, result) rhs = _gen(expr_z3.arg(2), symbolTable, cache, result) toAppend= "%s %s = %s*%s; " \ %( expr_type, var_name(expr_z3), \ lhs,\ rhs,\ ) result.append(toAppend) return var_name(expr_z3) if _is_fpDiv(expr_z3): if DEBUG: print "-- Branch _is_fpDiv" if not _is_RNE(expr_z3.arg(0)): warnings.warn( "WARNING!!! arg(0) is not RNE but is treated as RNE. arg(0) = ", expr_z3.arg(0)) assert expr_z3.num_args() == 3 lhs = _gen(expr_z3.arg(1), symbolTable, cache, result) rhs = _gen(expr_z3.arg(2), symbolTable, cache, result) toAppend= "%s %s = %s/%s; " \ %(expr_type, var_name(expr_z3), \ lhs,\ rhs,\ ) result.append(toAppend) return var_name(expr_z3) if _is_fpAdd(expr_z3): if DEBUG: print "-- Branch _is_fpAdd" if not _is_RNE(expr_z3.arg(0)): warnings.warn( "WARNING!!! arg(0) is not RNE but is treated as RNE. arg(0) = ", expr_z3.arg(0)) assert expr_z3.num_args() == 3 lhs = _gen(expr_z3.arg(1), symbolTable, cache, result) rhs = _gen(expr_z3.arg(2), symbolTable, cache, result) toAppend= "%s %s = %s+%s;" \ %( expr_type, var_name(expr_z3), \ lhs,\ rhs,\ ) result.append(toAppend) return var_name(expr_z3) if z3.is_and(expr_z3): if DEBUG: print "-- Branch _is_and" ##TODO Not sure if symbolTable will be treated in a multi-threaded way toAppendExpr = _gen(expr_z3.arg(0), symbolTable, cache, result) for i in range(1, expr_z3.num_args()): toAppendExpr = 'BAND( %s,%s )' % ( toAppendExpr, _gen(expr_z3.arg(i), symbolTable, cache, result)) toAppend= "double %s = %s; " \ %( var_name(expr_z3), \ toAppendExpr,\ ) result.append(toAppend) return var_name(expr_z3) if z3.is_not(expr_z3): if DEBUG: print "-- Branch _is_not" assert expr_z3.num_args() == 1 if not (expr_z3.arg(0).num_args() == 2): warnings.warn( "WARNING!!! arg(0) is not RNE but is treated as RNE. arg(0) = ", expr_z3.arg(0)) op1 = _gen(expr_z3.arg(0).arg(0), symbolTable, cache, result) op2 = _gen(expr_z3.arg(0).arg(1), symbolTable, cache, result) if _is_ge(expr_z3.arg(0)): a = "DLT(%s,%s)" % (op1, op2) elif _is_gt(expr_z3.arg(0)): a = "DLE(%s,%s)" % (op1, op2) elif _is_le(expr_z3.arg(0)): a = "DGT(%s,%s)" % (op1, op2) elif _is_lt(expr_z3.arg(0)): a = "DGE(%s,%s)" % (op1, op2) elif _is_eq(expr_z3.arg(0)): a = "DNE(%s,%s)" % (op1, op2) elif _is_distinct(expr_z3.arg(0)): a = "DEQ(%s,%s)" % (op1, op2) else: raise NotImplementedError( "Not implemented case 004 for expr_z3 = %s. 'Not(or... )' is not handled yet" % expr_z3) toAppend= "%s %s = %s; " \ %( expr_type, var_name(expr_z3), \ a,\ ) result.append(toAppend) return var_name(expr_z3) if _is_fpNeg(expr_z3): if DEBUG: print "-- Branch _is_fpNeg" assert expr_z3.num_args() == 1 op1 = _gen(expr_z3.arg(0), symbolTable, cache, result) toAppend = "%s %s = - %s ;" \ %(expr_type, var_name(expr_z3), \ op1,\ ) result.append(toAppend) return var_name(expr_z3) raise NotImplementedError( "Not implemented case 002 for expr_z3 = %s, kind(%s)" % (expr_z3, expr_z3.decl().kind()))
def main(): """Lantern demo""" # Initialize a PyTorch network # Lantern currently supports: Linear, ReLU, Hardtanh, Dropout, Identity net = nn.Sequential(nn.Linear(2, 5), nn.ReLU(), nn.Linear(5, 1), nn.ReLU()) print("A PyTorch network:") print(net) print() # Normally, we would train this network to compute some function. However, # for this demo, we'll just use the initialized weights. print("Network parameters:") print(list(net.parameters())) print() # lantern.as_z3(model) returns a triple of z3 constraints, input variables, # and output variables that directly correspond to the behavior of the # given PyTorch network. By default, latnern assumes Real-sorted variables. constraints, in_vars, out_vars = lantern.as_z3(net) print("Z3 constraints, input variables, output variables (Real-sorted):") print(constraints) print(in_vars) print(out_vars) print() # The 'payoff' is that we can prove theorems about our network with z3. # Trivially, we can ask for a satisfying assignment of variables print("A satisfying assignment to the variables in this network:") z3.solve(constraints) print() # However, we can run the network "backwards"; e.g. what is an *input* that # causes the network to output the value 0 (if such an input exists)? constraints.append(out_vars[0] == 0) print("An assignment such that the output variable is 0:") z3.solve(constraints) print() # To more precisely represent the underlying computations, consider using # an appropriate floating-point sort; PyTorch defaults to single precision. # To speed up satisfiability computations, models can be 'rounded', which # truncates the mantissa of every PyTorch model parameter. Note that the # exponent part remains the same (11 bits) so that the result can be # returned as a Python float. Here, we truncate to 10 bits (half precision). rounded_net = lantern.round_model(net, 10) constraints, in_vars, out_vars = lantern.as_z3(rounded_net, sort=z3.FPSort(11, 10)) print("Z3 constraints, input, output (FPSort(11, 10)):") print(constraints) print(in_vars) print(out_vars) print() # We add the constraint that the output must be 0.0, and solve using a # solver for FloatingPoint theory. print("An assignment such that the output variable is 0.0:") constraints.append(out_vars[0] == 0.0) z3.solve_using(z3.SolverFor("QF_FP"), *constraints) print() # Note that the constraints, and variables are all 'ordinary' Z3Py objects. print("Happy hacking!")
def generateFiniteLenFloats(name, count, tp, ctx): tp = np.dtype(tp) fpSort = floatTypes[tp.itemsize] if isinstance(fpSort, tuple): fpSort = z3.FPSort(*fpSort) return [z3.FP(name + "__" + str(i), fpSort) for i in range(count)]
import z3 from z3 import If, Or, Extract, Concat, BitVecVal, FPVal, BitVecSort, RNE, RTZ, fpSignedToFP, fpToSBV, fpFPToFP, fpIsNaN, K, Select, Store, BV2Int import subprocess import sys import traceback import itertools import json Float = z3.FPSort(8, 24) Double = z3.FPSort(11, 53) JAVA_HOME = "/usr/lib/jvm/java-8-openjdk-amd64" JAVA = "{}/bin/java".format(JAVA_HOME) PATH_DATA_OUTPUT = "pathConstraints.txt" class StackEntry: def __init__(self, branchId, done, isTrue): self.branchId = branchId self.done = done self.isTrue = isTrue def __repr__(self): return '({}, {}, {})'.format(self.branchId, self.done, self.isTrue) class Variable: def __init__(self, varType, varName): self.varType = varType self.varName = varName class Assignment:
def get_pred(self): if self.is_ninf(): return copy.deepcopy(self) if self.is_pzero(): return Float('-0',ne=self.ne,ns=self.ns).get_pred() bv_str = self.get_bv_str() #print(bv_str) sign = bv_str[0] if sign == '0': succ = int(bv_str[1:],2) - 1 return Float(val=z3.simplify(z3.fpBVToFP(z3.Int2BV(z3.IntVal(succ),num_bits = self.ns + self.ne),z3.FPSort(self.ne,self.ns))),ne=self.ne, ns=self.ns) else: succ = int(bv_str[1:],2) + 1 return -Float(val=z3.simplify(z3.fpBVToFP(z3.Int2BV(z3.IntVal(succ),num_bits = self.ns + self.ne),z3.FPSort(self.ne,self.ns))),ne=self.ne, ns=self.ns)
def _mk_nan(self): bv = '0' + ('1' * self.ne) + ('1' * (self.ns -1)) val = int(bv,2) self.__dict__ = copy.deepcopy(Float(val=z3.simplify(z3.fpBVToFP(z3.Int2BV(z3.IntVal(val),num_bits = self.ns + self.ne),z3.FPSort(self.ne,self.ns))),ne=self.ne, ns=self.ns).__dict__)
def _mk_pzero(self): self.__dict__ = copy.deepcopy(Float(val=z3.simplify(z3.fpBVToFP(z3.Int2BV(z3.IntVal(0),num_bits = self.ns + self.ne),z3.FPSort(self.ne,self.ns))),ne=self.ne, ns=self.ns).__dict__)