def _encode_input(image, z3_mask, window_size): """Encodes the image pixels by multiplying them with masking variables. Converts the pixels into z3.ExprRef by multiplying them with their corresponding masking variable. For an image with pixels with the same spatial dimensions are multiplied with the same masking variable. Args: image: float numpy array with shape (image_edge_length, image_edge_length, image_channels), image. z3_mask: list of z3.ExprRef with length (edge_length // window_size) ** 2, unique masking variables. window_size: int, side length of the square mask. Returns: list of list of list of z3.ExprRef with dimensions (image_channels, image_edge_length, image_edge_length), encoded input. """ image_edge_length, _, image_channels = image.shape encoded_input = [] for channel in range(image_channels): # Slicing the image across each channel encoded_input_per_channel = [] for image_row in range(image_edge_length): encoded_input_row = [] for image_column in range(image_edge_length): index = utils.convert_pixel_to_mask_index( edge_length=image_edge_length, window_size=window_size, flattened_pixel_index=image_row * image_edge_length + image_column) encoded_input_row.append( z3.ToReal(z3_mask[index]) * image[image_row][image_column][channel]) encoded_input_per_channel.append(encoded_input_row) encoded_input.append(encoded_input_per_channel) return encoded_input
def test_formulate_smt_constraints_fully_connected_layer(self): # For a neural network with 4 hidden nodes in the first layer, with the # original first layer activations = 1, and the SMT encoding of # the first hidden nodes- [mask_0, mask_1, mask_2, mask_3]. For # masked_activation > delta * original (k such constraints), only k mask # bits should be set to 1 and the others to 0. image_edge_length = 2 top_k = np.random.randint(low=1, high=image_edge_length**2) z3_mask = [_get_z3_var(index=i) for i in range(image_edge_length**2)] smt_first_layer = [1 * z3.ToReal(i) for i in z3_mask] nn_first_layer = np.ones(len(smt_first_layer)) z3_optimizer = utils.ImageOptimizer(z3_mask=z3_mask, window_size=1, edge_length=image_edge_length) z3_optimizer = masking._formulate_smt_constraints_fully_connected_layer( z3_optimizer=z3_optimizer, smt_first_layer=smt_first_layer, nn_first_layer=nn_first_layer, top_k=top_k, gamma=np.random.rand()) mask, result = z3_optimizer._optimize() self.assertEqual(result, 'sat') self.assertEqual(np.sum(mask), top_k)
def test_smt_forward_equals_nn_forward(self): weights = [ np.random.random_sample((3, 3)), np.random.random_sample((1, 3)) ] biases = [np.random.random_sample(3), np.random.random_sample(1)] activations = ['relu', 'linear'] z3_var = z3.Int('var') nn_output, _ = utils.nn_forward(np.ones(3), weights, biases, activations) smt_output, _ = utils.smt_forward( [z3.ToReal(z3_var), z3.ToReal(z3_var), z3.ToReal(z3_var)], weights, biases, activations) self._assert_z3_constraint_sat(constraint=z3.And( smt_output[0] - nn_output[0] < 1e-4, smt_output[0] - nn_output[0] > -1e-4), z3_var=z3_var)
def test_formulate_smt_constraints_convolution_layer_text(self): with self.test_session(): # Temporary graphs should be created inside a session. Notice multiple # graphs are being created in this particular code. So, if each graph # isn't created inside a separate session, the tensor names will have # unwanted integer suffices, which then would cause problems while # accessing tensors by name. _create_temporary_tf_graph_text_cnn(self.test_model_path) # The 1st convolution layer has 12 neurons. image = np.ones(5) tensor_names = { 'input': 'input_1:0', 'embedding': 'embedding/embedding_lookup/Identity_1:0', 'first_layer': 'conv1d/BiasAdd:0', 'first_layer_relu': 'conv1d/Relu:0', 'logits': 'dense/BiasAdd:0', 'softmax': 'dense/Sigmoid:0', 'weights_layer_1': 'conv1d/conv1d/ExpandDims_1:0', 'biases_layer_1': 'conv1d/BiasAdd/ReadVariableOp:0' } session = utils.restore_model(self.test_model_path) cnn_predictions = session.run( tensor_names, feed_dict={tensor_names['input']: image.reshape(1, 5)}) text_embedding = masking._remove_batch_axis( cnn_predictions['embedding']) z3_mask = [ z3.Int('mask_%d' % i) for i in range(text_embedding.shape[0]) ] masked_input = [] for mask_bit, embedding_row in zip(z3_mask, text_embedding): masked_input.append( [z3.ToReal(mask_bit) * i for i in embedding_row]) first_layer_activations = masking._reorder( masking._remove_batch_axis( cnn_predictions['first_layer'])).reshape(-1) z3_optimizer = masking._formulate_smt_constraints_convolution_layer( z3_optimizer=utils.TextOptimizer(z3_mask=z3_mask), kernels=masking._reshape_kernels( kernels=cnn_predictions['weights_layer_1'], model_type='text_cnn'), biases=cnn_predictions['biases_layer_1'], chosen_indices=first_layer_activations.argsort()[-5:], conv_activations=first_layer_activations, input_activation_maps=[masked_input], output_activation_map_shape=masking._get_activation_map_shape( activation_maps_shape=cnn_predictions['first_layer'].shape, model_type='text_cnn'), strides=1, padding=(0, 0), gamma=0.5) mask, result = z3_optimizer.generate_mask() self.assertEqual(result, 'sat') self.assertEqual(mask.shape, (5, )) session.close()
def _process_text(image, run_params): """Generates the masked embedding and does a forward pass of the image. Args: image: float numpy array with shape (num_words,), text to be masked. run_params: RunParams with model_type, model_path, image_placeholder_shape, activations, tensor_names, input, first_layer, logits. Returns: masked_input: nested list of z3.ExprRef with dimensions (1, num_words, num_latent_dimensions). unmasked_predictions: dict, * input: float numpy array, the input tensor to the neural network. * first_layer: float numpy array, the first layer tensor in the neural network. * first_layer_relu: str, the first layer relu activation tensor in the neural network. * logits: str, the logits tensor in the neural network. * softmax: float numpy array, the softmax tensor in the neural network. * weights_layer_1: float numpy array, the first layer fc / conv weights. * biases_layer_1: float numpy array, the first layer fc / conv biases. * (text only) embedding: float numpy array with shape (num_words, num_latent_dimensions), the embedding layer. session: tf.Session, tensorflow session with the loaded neural network. optimizer: utils.TextOptimizer, z3 optimizer for image. Raises: ValueError: Raises an error if the text isn't a 1D array. """ if image.ndim != 1: raise ValueError('The text input should be a 1D numpy array. ' 'Shape of the received input: %s' % str(image.shape)) session = utils.restore_model(run_params.model_path) unmasked_predictions = session.run( run_params.tensor_names, feed_dict={ run_params.tensor_names['input']: image.reshape( run_params.image_placeholder_shape)}) text_embedding = _remove_batch_axis(unmasked_predictions['embedding']) # text_embedding has a shape (num_words, num_latent_dimensions) z3_mask = [z3.Int('mask_%d' % i) for i in range(text_embedding.shape[0])] # masked_input has a shape (num_words, num_latent_dimensions) masked_input = [] for mask_bit, embedding_row in zip(z3_mask, text_embedding): masked_input.append([z3.ToReal(mask_bit) * i for i in embedding_row]) return ([masked_input], unmasked_predictions, session, utils.TextOptimizer(z3_mask=z3_mask))
def prepare_solver(instance: Instance) -> Tuple[z3.Solver, Dict[int, str]]: solver = z3.Solver() variables = [] ingredient_indices: Dict[str, int] = {} ingredient_names: Dict[int, str] = {} num_of_meals = len(MEAL_NAMES) # create a list of variables for each ingredient for ingredient_id in range(len(instance.ingredients)): meal_vars = [] name = instance.ingredients[ingredient_id].name ingredient_indices[name] = ingredient_id ingredient_names[ingredient_id] = name for meal_id in range(num_of_meals): var_name = make_variable_name(meal_id, ingredient_id) var = z3.Int(var_name) solver.add(var >= 0) meal_vars.append(z3.ToReal(var)) variables.append(meal_vars) # add constraints for food conflicts for food1, food2 in instance.conflicts: food1_id = ingredient_indices[food1] food2_id = ingredient_indices[food2] food1_vars = variables[food1_id] food2_vars = variables[food2_id] for var1, var2 in zip(food1_vars, food2_vars): solver.add(z3.Or(var1 == 0, var2 == 0)) # add constraints for total amount of substances macro_sums = {} for macro in instance.macros: macro_sums[macro] = 0.0 for ingredient_vars, ingredient in zip(variables, instance.ingredients): ingredient_sum = z3.Sum(ingredient_vars) for macro in instance.macros: macro_sums[macro] += ingredient_sum * ingredient.macros[macro] for target in instance.targets: macro_sum = macro_sums[target.macro] solver.add(macro_sum >= target.min) solver.add(macro_sum <= target.max) # ensure that meals are not empty num_of_ingredients = len(variables) for meal_id in range(num_of_meals): meal_conditions = [] for ingredient_id in range(num_of_ingredients): meal_conditions.append(variables[ingredient_id][meal_id] > 0) solver.add(z3.Or(meal_conditions)) return solver, ingredient_names
def test_smt_constraints_final_layer(self): # The SMT encoding of the final layer - [mask_0, mask_1, mask_2, mask_3]. # For logit_label_index > rest, the mask_bit at label_index should be set to # 1. image_edge_length = 2 label_index = np.random.randint(low=0, high=image_edge_length ** 2) z3_mask = [_get_z3_var(index=i) for i in range(image_edge_length ** 2)] smt_output = [1 * z3.ToReal(i) for i in z3_mask] z3_optimizer = utils.ImageOptimizer( z3_mask=z3_mask, window_size=1, edge_length=image_edge_length) z3_optimizer = masking._formulate_smt_constraints_final_layer( z3_optimizer=z3_optimizer, smt_output=smt_output, delta=np.random.rand(), label_index=label_index) mask, result = z3_optimizer._optimize() self.assertEqual(result, 'sat') self.assertEqual(mask.reshape(-1)[label_index], 1) self.assertEqual(np.sum(mask), 1)
def z3_matchLeftAndRight(left,right,op): """Appropriately change the two variables so that they can be used in an expression Parameters ---------- left,right : pyObjectManager.Int.Int or pyObjectManager.Real.Real or pyObjectManager.BitVec.BitVec or pyObjectManager.Char.Char Objects to be matched op : ast.* Operation that will be performed Returns ------- tuple (z3ObjectLeft,z3ObjectRight) tuple of z3 objects that can be used in an expression The purpose of this function is to match two pyObjectManager.* variables to a given ast operation element. Z3 needs to have matched types, and this call will not only match the objects, but also attempt to concretize input wherever possible. Example ------- If you want to auto-match BitVector sizes:: In [1]: import z3, pyState.z3Helpers, ast In [2]: from pySym.pyObjectManager.BitVec import BitVec In [3]: from pySym.pyState import State In [4]: state = State() In [5]: x = BitVec("x",0,16,state=state) In [6]: y = BitVec("y",0,32,state=state) In [7]: l,r = pyState.z3Helpers.z3_matchLeftAndRight(x,y,ast.Add()) In [8]: s = z3.Solver() In [9]: s.add(l + r == 12) In [10]: s Out[10]: [SignExt(16, 0x@0) + 0y@0 == 12] In [11]: s.check() Out[11]: sat """ lType = type(left) rType = type(right) # If it's char, just grab the BitVec object if lType is Char: left = left.variable lType = type(left) if rType is Char: right = right.variable rType = type(right) logger.debug("z3_matchLeftAndRight: Called to match {0} and {1}".format(type(left),type(right))) needBitVec = True if type(op) in [ast.BitXor, ast.BitAnd, ast.BitOr, ast.LShift, ast.RShift] else False # TODO: If the two sizes are different, we'll have problems down the road. bitVecSize = max([c.size for c in [b for b in [left,right] if type(b) is BitVec]],default=Z3_DEFAULT_BITVEC_SIZE) ##################################### # Case: Both are already BitVectors # ##################################### # Check length. Extend if needed. if type(left) is BitVec and type(right) is BitVec: logger.debug("z3_matchLeftAndRight: Matching BitVecLength @ {0} (left={1},right={2})".format(bitVecSize,left.size,right.size)) if left.size < right.size: # Sign extend left's value to match left = z3.SignExt(right.size-left.size,left.getZ3Object()) right = right.getZ3Object() elif right.size > left.size: right = z3.SignExt(left.size-right.size,right.getZ3Object()) left = left.getZ3Object() # Sync-up the output variables left = left.getZ3Object() if type(left) in [Int, Real, BitVec] else left right = right.getZ3Object() if type(right) in [Int, Real, BitVec] else right logger.debug("z3_matchLeftAndRight: Returning {0} and {1}".format(type(left),type(right))) return left,right ##################################### # Case: One is BitVec and one isn't # ##################################### # For now only handling casting of int to BV. Not other way around. if (lType is BitVec and rType is Int) or (rType is Int and needBitVec): # If we need to convert to BitVec and it is a constant, not variable, do so more directly if right.isStatic(): right = z3.BitVecVal(right.getValue(),bitVecSize) # Otherwise cast it. Not optimal, but oh well. else: right = z3_int_to_bv(right.getZ3Object(),size=bitVecSize) if (rType is BitVec and lType is Int) or (lType is Int and needBitVec): if left.isStatic(): left = z3.BitVecVal(left.getValue(),bitVecSize) else: left = z3_int_to_bv(left.getZ3Object(),size=bitVecSize) ################################ # Case: One is Int one is Real # ################################ # So long as this isn't modular arithmetic, let's change to Real things if lType is Real and rType is Int and type(op) is not ast.Mod: if right.isStatic(): right = z3.RealVal(right.getValue()) else: # TODO: Z3 is really bad at handling these... right = z3.ToReal(right.getZ3Object()) if rType is Real and lType is Int and type(op) is not ast.Mod: if left.isStatic(): left = z3.RealVal(left.getValue()) else: # TODO: Z3 is really bad at handling these... left = z3.ToReal(left.getZ3Object()) ############################################ # Case: One is Int one is Real for ast.Mod # ############################################ # So long as this isn't modular arithmetic, let's change to Real things if lType is Real and rType is Int and type(op) is ast.Mod: if left.isStatic(): leftVal = left.getValue() left = z3.IntVal(leftVal) if int(leftVal) != leftVal: logger.warn("Truncating value for Modular Arithmetic. That may or may not be what was intended!") # See if we can swing this the other direction elif right.isStatic(): rightVal = right.getValue() right = z3.RealVal(rightVal) if rType is Real and lType is Int and type(op) is ast.Mod: if right.isStatic(): rightVal = right.getValue() right = z3.IntVal(rightVal) if int(rightVal) != rightVal: logger.warn("Truncating value for Modular Arithmetic. That may or may not be what was intended!") # See if we can swing this the other direction else: left = z3.RealVal(left.getValue()) # Sync-up the output variables left = left.getZ3Object() if type(left) in [Int, Real, BitVec] else left right = right.getZ3Object() if type(right) in [Int, Real, BitVec] else right logger.debug("z3_matchLeftAndRight: Returning {0} and {1}".format(type(left),type(right))) return left,right
class SimpleParser: precedence = ( ('left', 'ASSIGN'), ('right', 'QM', 'COLON'), ('left', 'OR'), ('left', 'AND'), ('left', 'BITOR'), ('left', 'BITXOR'), ('left', 'BITAND'), ('left', 'EQ', 'NEQ'), ('left', 'LT', 'GT', 'GTE', 'LTE'), ('left', 'LSHIFT', 'RSHIFT'), ('left', 'PLUS', 'MINUS'), ('left', 'TIME', 'DIVIDE', 'MODULO'), ('right', 'MINUS', 'PLUS', 'SIZEOF', 'NOT', 'BITNOT', 'PLUSPLUS', 'MINUSMINUS', 'INTCASTOR', 'INTARRCASTOR', 'REALCASTOR', 'BOOLCASTOR'), # Unary minus operator ('left', 'LBRACK', 'LPAREN', 'DOT', 'ARROW', 'PLUSPLUS', 'MINUSMINUS'), ) type_switch = { 'bool': z3.Bool, 'uchar': z3.Int, 'uint': z3.Int, 'ulong': z3.Int, 'byte': z3.Int, 'sbyte': z3.Int, 'ushort': z3.Int, 'char': z3.Int, 'int': z3.Int, 'long': z3.Int, 'short': z3.Int, 'float': z3.Real, 'double': z3.Real, 'intArray': lambda x: z3.Array(x, z3.IntSort(), z3.IntSort()), 'realArray': lambda x: z3.Array(x, z3.RealSort(), z3.IntSort()), 'string': lambda x: z3.Array(x, z3.IntSort(), z3.IntSort()), # <-- No clue if correct (04/17/2020) 'stringArray': lambda x: z3.Array(x, z3.IntSort(), z3.IntSort()) } unary_ops = { '!': z3.Not, '-': lambda x: -x, '+': lambda x: +x, # TODO (?): INTARRCASTOR. Look into ArraySort(). # 'INTARRCASTOR': lambda x: x if x.sort() != z3.ArraySort(z3.IntSort(), z3.IntSort()) else z3.Array(x, z3.IntSort(), z3.IntSort()), # 'INTARRCASTOR': None, # 'INTCASTOR': lambda x: x if x.sort() != z3.RealSort() else z3.ToInt(x), 'INTCASTOR': lambda x: x if isinstance(x, int) else (int(x) if isinstance(x, float) else (x if x.sort() != z3.RealSort() else z3.ToInt(x))), 'REALCASTOR': lambda x: x if isinstance(x, float) else (float(x) if isinstance(x, int) else (x if x.sort() != z3.IntSort() else z3.ToReal(x))) # 'REALCASTOR': lambda x: x if x.sort() != z3.IntSort() else z3.ToReal(x) } binary_ops = { '>': lambda x, y: x > y, '>=': lambda x, y: x >= y, '<': lambda x, y: x < y, '<=': lambda x, y: x <= y, '==': lambda x, y: x == y, '!=': lambda x, y: x != y, '+': lambda x, y: x + y, '-': lambda x, y: x - y, '*': lambda x, y: x * y, '/': lambda x, y: x / y, '%': lambda x, y: x % y, '||': z3.Or, '&&': z3.And, } tokens = ( # 'NULL', 'INT', # instant values 'REAL', 'CHAR', 'STRINGS', 'LBRACK', # Brackets 'RBRACK', 'LPAREN', 'RPAREN', 'VARIABLE', 'DOT', # Operators 'ARROW', 'SIZEOF', 'GT', 'GTE', 'LT', 'LTE', 'EQ', 'NEQ', 'PLUS', 'PLUSPLUS', 'MINUS', 'MINUSMINUS', 'TIME', 'DIVIDE', 'LSHIFT', 'RSHIFT', 'BITAND', 'BITOR', 'BITXOR', 'BITNOT', 'OR', 'AND', 'NOT', 'MODULO', 'ASSIGN', 'QM', 'COLON', 'BOOLT', 'INTT', 'REALT', 'COMMA', 'INTCASTOR', 'INTARRCASTOR', 'REALCASTOR', 'BOOLCASTOR', 'TOOBIG' ) def __init__(self, lexer, types, hooks=None, **kwargs): # self.parser = yacc.yacc(module=self, **kwargs) self.types = defaultdict(lambda: 'int') self.types.update(types) self.function_hooks = hooks if hooks else dict() self.lexer = lexer self.errored = False self.context = dict() self.parser = yacc.yacc(module=self, **kwargs) def p_end(self, p): """end : expr | empty """ if len(p) == 2: self.result = p[1] if not self.errored else False # print('p[1], self.errored',p[1], self.errored) # print('result:', self.result, 'errored: ', self.errored) self.errored = False def p_types(self, p): """inttype : INTT realtype : REALT booltype : BOOLT """ if len(p) == 2: p[0] = p[1] def p_leftv_expr(self, p): """expr : leftv """ p[0] = p[1] def p_define_new(self, p): """leftv : inttype VARIABLE | realtype VARIABLE | booltype VARIABLE | leftv DOT VARIABLE | VARIABLE """ if p.slice[1].type == 'inttype': p[0] = z3.Int(p[2]) p.slice[0].name = p[2] name = p[2] self.types[name] = 'int' elif p.slice[1].type == 'realtype': p[0] = z3.Real(p[2]) p.slice[0].name = p[2] name = p[2] self.types[name] = 'double' elif p.slice[1].type == 'booltype': p[0] = z3.Bool(p[2]) p.slice[0].name = p[2] name = p[2] self.types[name] = 'bool' elif len(p) == 2: if p[1] in self.context: p[0] = self.context[p[1]] p.slice[0].name = p[1] name = p[1] # print('{} in context, get {}'.format(name, p[0])) else: if p[1] not in self.types: print( 'Warning: Type of {} is undefined, using default type: int'.format(p[1])) p[0] = self.type_switch[self.types[p[1]]](p[1]) p.slice[0].name = p[1] name = p[1] elif len(p) == 4: var_name = '{}.{}'.format(p[1], p[3]) if var_name not in self.types: print( 'Warning: Type of {} is undefined, using default type: int'.format(var_name)) p[0] = self.type_switch[self.types[var_name]](var_name) p.slice[0].name = var_name name = var_name if len(p) > 2: self.context[name] = p[0] def p_variable_value(self, p): """expr : STRINGS | REAL | CHAR | INT """ p[0] = p[1] def p_assign(self, p): """expr : leftv ASSIGN expr """ key = getattr(p.slice[1], 'name') self.context[key] = p[3] p[1] = p[3] p[0] = True def p_expression(self, p): """expr : expr LBRACK expr RBRACK | LPAREN expr RPAREN """ if len(p) == 4: # binary ops, paren, element access if p.slice[2].type == 'binary_op': p[0] = self.binary_ops[p[2]](p[1], p[3]) elif p.slice[1].type == 'LPAREN': p[0] = p[2] elif len(p) == 5: # array access p[0] = p[1][p[3]] def p_qsmark_choice(self, p): """expr : expr QM expr COLON expr """ p[0] = z3.If(p[1], p[3], p[5]) print(p[1], p[3], p[5]) def p_binary_ops(self, p): """expr : expr GT expr | expr GTE expr | expr LT expr | expr LTE expr | expr EQ expr | expr NEQ expr | expr PLUS expr | expr TIME expr | expr MINUS expr | expr DIVIDE expr | expr MODULO expr | expr OR expr | expr AND expr """ if len(p) == 4: # print(p[1], p[3], p[2]) p[0] = self.binary_ops[p[2]](p[1], p[3]) # print(p[0]) elif len(p) == 2: p[0] = p[1] def p_unary_ops(self, p): """expr : MINUS expr | NOT expr """ p[0] = self.unary_ops[p[1]](p[2]) def p_casting(self, p): """expr : INTCASTOR expr | INTARRCASTOR expr | REALCASTOR expr | BOOLCASTOR expr """ p[0] = self.unary_ops[p.slice[1].type](p[2]) def p_function(self, p): """expr : leftv LPAREN paramList RPAREN paramList : paramList COMMA expr | expr """ if len(p) == 5: # TODO: Add `Math.Abs()` checker here!!!! if str(p[1]) not in self.function_hooks: print( 'Function {} not found, treating as plain string'.format(p[1])) p[0] = self.function_hooks[str(p[1])](*p[3]) elif len(p) == 4: p[0] = p[1] + [p[3], ] elif len(p) == 2: p[0] = [p[1], ] # def p_casting(self, p): # """castor : LPAREN inttype RPAREN # | LPAREN realtype RPAREN # """ # if p.slice[2].type == 'inttype': # p[0] = '2int' # elif p.slice[2].type == 'realtype': # p[0] = '2real' def p_empty(self, p): """empty : """ pass def p_error(self, p): print("Syntax error, treating as string: {}".format(p)) self.errored = True raise ValueError('Syntax error') def test(self, data): # data = 'a != (int[])null&&a[2] + a[3] < 4&&-51 < a[0]&&a[0] < 51&&-51 < a[1]&&a[1] < 51&&-51 < a[2]&&a[2] < 51&&a.Length == 3&&a[1] >= a[0]&&a[0] >= a[1]&&a[2] >= a[0]&&a[0] >= a[2]&&a[0] + a[2] * a[1]&&int s = a[0] - a[2]&&s > a[2] - a[1]&&(a[2]!=1 || a[2]!=4)&&4u < (uint)(1 + a[4])&&a[5] == \'c\'&&Math.floor(a[0])&&a[2] < a[1] ? a[0] % a[1] : a[1] % a[0]&&double s0 = 0.98&&s1 = 0.6&&s1 + -(double)((int)s0) != 0.49'.split('&&') # data = 'a != (int[])null&&-51 < a[0]&&a[0] < 51&&1 < a.Length&&-51 < a[1]&&a[1] < 51&&a[1] < a[0]&&a.Length == 2&&a[0] >= a[1]'.split('&&') # data = data.replace('\\r\\n', '') # data = data.split('&&') models = [] errors = [] toAdd = False # Don't add the preamble for d in data: if not d: continue # print(d) try: # print(d) ret = self.parse(d) # print(ret) if type(ret) == z3.z3.ArithRef: ret = z3.If(ret == 0, True, False) except (ValueError, KeyError) as e: ret = None except z3.z3types.Z3Exception as e: ret = None print('Z3 error', e) # Changed 'ret == False' -> 'ret is False' to avoid triggering int 0 if ret is False or ret == None: errors.append(d) else: models.append(ret) # self.parser.clear_context() # Checks if all eles in `models` eval to True retVal = '&&'.join(sorted(errors)), z3.simplify( reduce(z3.And, models, True)) if len(models) > 0 else z3.BoolVal(True) return retVal def test2(self, data: list): results = [] for d in data: print(d) try: self.parser.parse(d, lexer=self.lexer) except (ValueError, KeyError) as e: self.result = None except z3.z3types.Z3Exception as e: self.result = None print('Z3 error', e) # except ImportError as e: # pass print(self.result) self.errored = False print('-----') results.append(self.result) return results def parse(self, text): self.parser.parse(text, lexer=self.lexer.clone()) return self.result def predParse(self, pred): models = [] errors = [] try: ret = self.parse(pred) if type(ret) == z3.z3.ArithRef: ret = z3.If(ret == 0, True, False) except (ValueError, KeyError) as e: ret = None except z3.z3types.Z3Exception as e: ret = None print('Z3 error', e) # Changed 'ret == False' -> 'ret is False' to avoid triggering int 0 if ret is False or ret == None: errors.append(pred) else: models.append(ret) # Checks if all eles in `models` eval to True retVal = '&&'.join(sorted(errors)), z3.simplify( reduce(z3.And, models, True)) if len(models) > 0 else z3.BoolVal(True) return retVal[1] def clear_context(self): self.context = dict()
def cast(self, value, is_type, to_type): if is_type is Types.STRING and isinstance(to_type, z3.z3.DatatypeSortRef): # the value is a string and it should be cast to a datatyperef # (i.e. an enum-type), just return the value because we can deal with it return value value_is_int = isinstance(value, int) value_is_float = isinstance(value, float) if is_type is to_type: # already correct type, just return the value return value """ INT <---> INTEGER """ elif is_type is Types.INT and to_type is Types.INTEGER: if value_is_int or value_is_float: return value # this happens if it is an int numeral (e.g. 2) else: return z3.BV2Int(value) elif is_type is Types.INTEGER and to_type is Types.INT: if value_is_int or value_is_float: return value else: return z3.Int2BV(value, 32) """ INT <---> FLOAT """ elif is_type is Types.FLOAT and to_type is Types.INT: if value_is_float: return value # this happens if it is a float numeral (e.g. 3.14) else: return z3.fpToSBV(z3.RNE(), value, z3.BitVecSort(32)) elif is_type is Types.INT and to_type is Types.FLOAT: if value_is_int: return value else: return z3.fpSignedToFP(z3.RNE(), value, z3.Float32()) """ INTEGER <---> FLOAT """ elif is_type is Types.FLOAT and to_type is Types.INTEGER: if value_is_float: return value # this happens if it is a float numeral (e.g. 3.14) else: return self.cast(self.cast(value, Types.FLOAT, Types.INT), Types.INT, Types.INTEGER) elif is_type is Types.INTEGER and to_type is Types.FLOAT: if value_is_int: return value else: return self.cast(self.cast(value, Types.INTEGER, Types.INT), Types.INT, Types.FLOAT) """ from REAL """ elif is_type is Types.REAL and to_type is Types.INTEGER: if value_is_float: return value else: return z3.ToInt(value) elif is_type is Types.REAL and to_type is Types.INT: return self.cast(self.cast(value, Types.REAL, Types.INTEGER), Types.INTEGER, Types.INT) elif is_type is Types.REAL and to_type is Types.FLOAT: """ Rounding modes: probably should make these parameterizable! roundNearestTiesToEven ... RNE() = default roundNearestTiesToAway ... RNA() roundTowardPositive ...... RTP() roundTowardNegative ...... RTN() roundTowardZero .......... RTZ() """ if value_is_int or value_is_float: # int numeral return value else: return z3.fpRealToFP(z3.RNE(), value, z3.Float32()) """ to REAL """ elif is_type is Types.INT and to_type is Types.REAL: if value_is_int or value_is_float: # int numeral return value else: return z3.ToReal(self.cast(value, Types.INT, Types.INTEGER)) elif is_type is Types.INTEGER and to_type is Types.REAL: if value_is_int or value_is_float: # int numeral return value else: return z3.ToReal(value) elif is_type is Types.FLOAT and to_type is Types.REAL: if value_is_int or value_is_float: return value # this happens if it is a float numeral (e.g. 3.14) else: return z3.fpToReal(value) """ FROM BOOL conversions """ elif is_type is Types.BOOL and to_type is Types.INT: return z3.If(value, z3.BitVecVal(1, 32), z3.BitVecVal(0, 32)) elif is_type is Types.BOOL and to_type is Types.INTEGER: return z3.If(value, 1, 0) elif is_type is Types.BOOL and to_type is Types.REAL: return z3.If(value, 1.0, 0.0) elif is_type is Types.BOOL and to_type is Types.FLOAT: return z3.If(value, z3.FPVal(1.0, z3.Float32()), z3.FPVal(0.0, z3.Float32())) """ TO BOOL conversions """ elif is_type is Types.INT and to_type is Types.BOOL: return value == 1 elif is_type is Types.INTEGER and to_type is Types.BOOL: return value == 1 elif is_type is Types.REAL and to_type is Types.BOOL: return value == 1 elif is_type is Types.FLOAT and to_type is Types.BOOL: return value == 1 raise TypeError(f"Don't know how to cast from {is_type} to {to_type}!")
def walk_toreal(self, formula, args, **kwargs): return z3.ToReal(args[0])
def cast(expr, type_): if type_ == float: return z3.ToReal(expr) else: raise NotImplementedError
def as_z3(self): return z3.ToReal(self.expr.as_z3())
def rec(t): if t.is_var(): z3_t = convert_const(t.name, t.T, ctx) if t.T == NatType and t.name not in assms: assms[t.name] = z3_t >= 0 return z3_t elif t.is_forall(): nm = name.get_variant_name(t.arg.var_name, var_names) var_names.append(nm) v = Var(nm, t.arg.var_T) z3_v = convert_const(nm, t.arg.var_T, ctx) return z3.ForAll(z3_v, rec(t.arg.subst_bound(v))) elif t.is_exists(): nm = name.get_variant_name(t.arg.var_name, var_names) var_names.append(nm) v = Var(nm, t.arg.var_T) z3_v = convert_const(nm, t.arg.var_T, ctx) return z3.Exists(z3_v, rec(t.arg.subst_bound(v))) elif t.is_number(): return t.dest_number() elif t.is_implies(): return z3.Implies(rec(t.arg1), rec(t.arg)) elif t.is_equals(): return rec(t.arg1) == rec(t.arg) elif t.is_conj(): return z3.And(rec(t.arg1), rec(t.arg)) if ctx is None else z3.And( rec(t.arg1), rec(t.arg), ctx) elif t.is_disj(): return z3.Or(rec(t.arg1), rec(t.arg)) if ctx is None else z3.Or( rec(t.arg1), rec(t.arg), ctx) elif logic.is_if(t): b, t1, t2 = t.args return z3.If(rec(b), rec(t1), rec(t2), ctx) elif t.is_not(): return z3.Not(rec(t.arg), ctx) elif t.is_plus(): return rec(t.arg1) + rec(t.arg) elif t.is_minus(): m, n = rec(t.arg1), rec(t.arg) if t.arg1.get_type() == NatType: return z3.If(m >= n, m - n, 0, ctx) return m - n elif t.is_uminus(): return -rec(t.arg) elif t.is_times(): return rec(t.arg1) * rec(t.arg) elif t.is_less_eq(): return rec(t.arg1) <= rec(t.arg) elif t.is_less(): return rec(t.arg1) < rec(t.arg) elif t.is_greater_eq(): return rec(t.arg1) >= rec(t.arg) elif t.is_greater(): return rec(t.arg1) > rec(t.arg) elif t.is_divides(): return rec(t.arg1) / rec(t.arg) elif t.is_comb('of_nat', 1): if t.get_type() == RealType: if t.arg.is_var(): if t.arg.name not in to_real: nm = name.get_variant_name("r" + t.arg.name, var_names) var_names.append(nm) to_real[t.arg.name] = nm z3_t = convert_const(nm, RealType, ctx) assms[nm] = z3_t >= 0 return z3_t else: return convert_const(to_real[t.arg.name], RealType, ctx) return z3.ToReal(rec(t.arg)) else: raise Z3Exception("convert: unsupported of_nat " + repr(t)) elif t.is_comb('max', 2): a, b = rec(t.arg1), rec(t.arg) return z3.If(a >= b, a, b, ctx) elif t.is_comb('min', 2): a, b = rec(t.arg1), rec(t.arg) return z3.If(a <= b, a, b, ctx) elif t.is_comb('abs', 1): a = rec(t.arg) return z3.If(a >= 0, a, -a, ctx) elif t.is_comb('member', 2): a, S = rec(t.arg1), rec(t.arg) return S(a) elif t.is_comb(): return rec(t.fun)(rec(t.arg)) elif t.is_const(): if t == true: return z3.BoolVal(True, ctx) elif t == false: return z3.BoolVal(False, ctx) else: raise Z3Exception("convert: unsupported constant " + repr(t)) else: raise Z3Exception("convert: unsupported operation " + repr(t))
def __k_float__(self): try: return inference.InferenceResult.load_result( Z3Proxy.init_expr(z3.ToReal(self.value), {**self.defaults})) except z3.Z3Exception: return inference.InferenceResult.load_result(nodes.Uninferable())
# Make sure they are 0 or 1 for x in X: S.add(x >= 0) S.add(x <= 1) Y = [] Z = [] val = 0 # Use the lists from before to build up what the value of a solution would be for x in X: y = z3.Real('y' + str(val)) z = z3.Real('z' + str(val)) Y.append(y) Z.append(z) S.add(y == z3.ToReal(x) * maximized_parameter_vector[val]) S.add(z == z3.ToReal(x) * minimized_parameter_vector[val]) val += 1 # Set up the min and max S.maximize(z3.Sum(Y)) S.minimize(z3.Sum(Z)) # Get a solution S.check() solution = S.model() print("Here are the zipcodes you may be interested in:") val = 0 solution_zips = []