def lossy_duplicating_tag_data_channel(name): input_channel = z3p.Array('{}_input_channel'.format(name), z3p.IntSort(), z3p.IntSort()) output_channel = z3p.Array('{}_output_channel'.format(name), z3p.IntSort(), z3p.IntSort()) tag = z3p.Int('{}_channel_tag'.format(name)) data = z3p.Int('{}_channel_data'.format(name)) transitions = [] transitions.append(('t0', 'empty', input_channel, z3p.BoolVal(True), [(tag, input_channel[0]), (data, input_channel[1])], 'full')) transitions.append( ('t1', 'full', output_channel, [tag, data], z3p.BoolVal(True), [], 'empty')) transitions.append( ('t2', 'full', input_channel, z3p.BoolVal(True), [], 'full')) transitions.append(('t_duplication', 'full', output_channel, [tag, data], z3p.BoolVal(True), [], 'full')) transitions.append( ('t_loss', 'empty', input_channel, z3p.BoolVal(True), [], 'empty')) return automaton.SymbolicAutomaton( '{}_tag_data_channel'.format(name), ['empty', 'full'], 'empty', transitions, input_channels=[input_channel], output_channels=[output_channel], variables=[tag, data], initial_values=[z3p.IntVal(0), z3p.IntVal(0)], variable_ranges={ tag: (0, 1), data: (min_value_message, max_value_message) }, compassion=['t1', 't0'])
def lossy_duplicating_tag_channel(name): input_channel = z3p.Int('{}_input_channel'.format(name)) output_channel = z3p.Int('{}_output_channel'.format(name)) tag = z3p.Int('{}_channel_tag'.format(name)) transitions = [] transitions.append(('t0', 'empty', input_channel, z3p.BoolVal(True), [(tag, input_channel)], 'full')) transitions.append( ('t_loss', 'empty', input_channel, z3p.BoolVal(True), [], 'empty')) transitions.append( ('t1', 'full', output_channel, [tag], z3p.BoolVal(True), [], 'empty')) transitions.append(('t_duplicating', 'full', output_channel, [tag], z3p.BoolVal(True), [], 'full')) transitions.append( ('t2', 'full', input_channel, z3p.BoolVal(True), [], 'full')) return automaton.SymbolicAutomaton( '{}_tag_channel'.format(name), ['empty', 'full'], 'empty', transitions, input_channels=[input_channel], output_channels=[output_channel], variables=[tag], initial_values=[z3p.IntVal(0)], variable_ranges={tag: (0, 1)}, output_channel_ranges={output_channel: (0, 1)}, compassion=['t0', 't1'])
def python_value_to_z3(value): if isinstance(value, bool): return z3p.BoolVal(value) elif isinstance(value, int) or isinstance(value, long): return z3p.IntVal(value) else: raise NotImplementedError("unknown value {}".format(value))
def receiver(): forward_output_channel = z3p.Array('forward_output_channel', z3p.IntSort(), z3p.IntSort()) backward_input_channel = z3p.Int('backward_input_channel') receive = z3p.Int('receive') input_tag = z3p.Int('receiver_tag') input_data = z3p.Int('receiver_data') expected_tag = z3p.Int('expected_tag') transitions = [] transitions.append( ('t0', 'initial', forward_output_channel, z3p.BoolVal(True), [(input_tag, forward_output_channel[0]), (input_data, forward_output_channel[1])], 'q0')) transitions.append( ('t1', 'q0', receive, [input_data], z3p.Eq(input_tag, expected_tag), [], 'q1')) transitions.append( ('t2', 'q1', backward_input_channel, [expected_tag], z3p.BoolVal(True), [(expected_tag, z3p.If(z3p.Eq(expected_tag, 0), z3p.IntVal(1), z3p.IntVal(0)))], 'initial')) transitions.append(('t3', 'q0', z3p.Neq(input_tag, expected_tag), [], 'q2')) transitions.append( ('t4', 'q2', backward_input_channel, [z3p.If(z3p.Eq(expected_tag, 0), z3p.IntVal(1), z3p.IntVal(0))], z3p.BoolVal(True), [], 'initial')) return automaton.SymbolicAutomaton( 'receiver', ['initial', 'q0', 'q1', 'q2'], 'initial', transitions, input_channels=[forward_output_channel], output_channels=[receive, backward_input_channel], variables=[input_tag, input_data, expected_tag], initial_values=[z3p.IntVal(0), z3p.IntVal(0), z3p.IntVal(0)], variable_ranges={ input_tag: (0, 1), input_data: (min_value_message, max_value_message), expected_tag: (0, 1) }, output_channel_ranges={ receive: (min_value_message, max_value_message), backward_input_channel: (0, 1) }, justice=['t3', 't1'])
def z3_expression_to_nusmv(expression, tables=None, other_tables=None): z3_to_nusmv = { z3p.Z3_OP_AND: ' & ', z3p.Z3_OP_ADD: ' + ', z3p.Z3_OP_MOD: ' mod ', z3p.Z3_OP_EQ: ' = ', z3p.Z3_OP_LT: ' < ', z3p.Z3_OP_GT: ' > ' } kind = expression.decl().kind() if (z3p.is_const(expression) and expression.decl().name().startswith('table_')): assert tables is not None table_name = expression.decl().name() assert table_name in tables or table_name in other_tables table_expression = tables[ table_name] if table_name in tables else other_tables[table_name] case_expression = 'case ' for condition, result in table_expression: c = z3_expression_to_nusmv(condition, tables) r = z3_expression_to_nusmv(result, tables) case_expression += '{} : {}; '.format(c, r) case_expression += 'esac' return case_expression elif kind in z3_to_nusmv: children = [ z3_expression_to_nusmv(c, tables) for c in expression.children() ] return '({})'.format(z3_to_nusmv[kind].join(children)) elif expression == z3p.BoolVal(True): return 'TRUE' elif expression == z3p.BoolVal(False): return 'FALSE' elif kind == z3p.Z3_OP_NOT: e = z3_expression_to_nusmv(expression.arg(0)) return '!({})'.format(e) elif z3p.is_const(expression): return str(expression) elif kind == z3p.Z3_OP_ITE: guard = z3_expression_to_nusmv(expression.arg(0)) then_branch = z3_expression_to_nusmv(expression.arg(1)) else_branch = z3_expression_to_nusmv(expression.arg(2)) return '(case {} : {}; TRUE : {}; esac)'.format( guard, then_branch, else_branch) else: raise NotImplementedError( 'Cannot translate expression {} to nusmv.'.format(expression))
def timer(): timeout = util.new_variable('timeout', 'unit') transitions = [('t0', 'initial', timeout, [z3p.IntVal(0)], z3p.BoolVal(True), [], 'initial')] return automaton.SymbolicAutomaton('timer', ['initial'], 'initial', transitions, output_channels=[timeout])
def create_guard_hole(self, hole_name): """ variable is the name of the variable to update """ signature = [] domains = [] for variable in self.variables: variable_sort = variable.sort() signature.append(variable_sort) if variable.sort() == z3p.IntSort(): domains.append(range(self.variable_ranges[variable][0], self.variable_ranges[variable][1] + 1)) elif variable.sort() == z3p.BoolSort(): domains.append([z3p.BoolVal(False), z3p.BoolVal(True)]) else: raise NotImplementedError("Unimplemented type of variable {}".format(variable_sort)) signature.append(z3p.BoolSort()) f = z3p.Function(hole_name, *signature) return (f, domains, None, None)
def receiver_client(): transitions = [] receive = z3p.Int('receive') transitions.append( ('t0', 'initial', receive, z3p.BoolVal(True), [], 'initial')) return automaton.SymbolicAutomaton('receiver_client', ['initial'], 'initial', transitions, input_channels=[receive])
def sender(): transitions = [] input_message = z3p.Int('sender_input_message') sender_tag = z3p.Int('sender_tag') ack_tag = z3p.Int('ack_tag') forward_input_channel = z3p.Array('forward_input_channel', z3p.IntSort(), z3p.IntSort()) backward_output_channel = z3p.Int('backward_output_channel') send = z3p.Int('send') timeout = util.new_variable('timeout', 'unit') transitions.append(('t0', 'initial', send, z3p.BoolVal(True), [(input_message, send)], 'q0')) transitions.append( ('t1', 'q0', forward_input_channel, [sender_tag, input_message], z3p.BoolVal(True), [], 'q1')) transitions.append(('t2', 'q1', timeout, z3p.BoolVal(True), [], 'q0')) transitions.append(('t3', 'q1', backward_output_channel, z3p.BoolVal(True), [(ack_tag, backward_output_channel)], 'q2')) transitions.append(('t4', 'q2', z3p.Eq(ack_tag, sender_tag), [ (sender_tag, z3p.If(z3p.Eq(sender_tag, 0), z3p.IntVal(1), z3p.IntVal(0))) ], 'initial')) transitions.append(('t5', 'q2', z3p.Neq(ack_tag, sender_tag), [], 'q0')) return automaton.SymbolicAutomaton( 'sender', ['initial', 'q0', 'q1', 'q2'], 'initial', transitions, variables=[input_message, sender_tag, ack_tag], initial_values=[z3p.IntVal(0), z3p.IntVal(0), z3p.IntVal(0)], input_channels=[send, timeout, backward_output_channel], output_channels=[forward_input_channel], variable_ranges={ input_message: (min_value_message, max_value_message), sender_tag: (0, 1), ack_tag: (0, 1) }, output_channel_ranges={ forward_input_channel[1]: (min_value_message, max_value_message), forward_input_channel[0]: (0, 1) })
def translate_expression(self, e): if e == z3p.BoolVal(True): return 'TheLTS->MakeTrue()' elif e == z3p.BoolVal(False): return 'TheLTS->MakeFalse()' elif z3p.is_int_value(e): return 'TheLTS->MakeVal("{i}", TheLTS->MakeRangeType({i}, {i}))'.format(i=e) elif z3p.is_const(e) and e in self.channels: return self.translate_channel_reference(e) elif z3p.is_const(e): return '{}Exp'.format(to_camel_case(e.decl().name())) elif e.num_args() == 1: return self.translate_unary_expression(e) elif e.num_args() == 2 and e.decl().kind() == z3p.Z3_OP_SELECT: return self.translate_channel_reference(e) elif e.num_args() == 2: return self.translate_binary_expression(e) elif e.num_args() == 3: return self.translate_ternary_expreession(e) else: raise NotImplementedError("can't translate expression {} with {}".format(e, e.num_args()))
def sender_client(): transitions = [] send = z3p.Int('send') for i in range(min_value_message, max_value_message + 1): transitions.append(('t{}'.format(i), 'initial', send, [z3p.IntVal(i)], z3p.BoolVal(True), [], 'initial')) return automaton.SymbolicAutomaton( 'sender_client', ['initial'], 'initial', transitions, output_channels=[send], output_channel_ranges={send: (min_value_message, max_value_message)})
def create_update_hole(self, variable_or_variable_name, hole_name): """ variable is the name of the variable to update """ if isinstance(variable_or_variable_name, str): variable_to_update = next(v for v in self.variables if v.decl().name() == variable_or_variable_name) else: variable_to_update = variable_or_variable_name signature = [] domains = [] for variable in self.variables: variable_sort = variable.sort() signature.append(variable_sort) if variable.sort() == z3p.IntSort(): domains.append([z3p.IntVal(i) for i in range(self.variable_ranges[variable][0], self.variable_ranges[variable][1] + 1)]) elif variable.sort() == z3p.BoolSort(): domains.append([z3p.BoolVal(False), z3p.BoolVal(True)]) else: raise NotImplementedError("Unimplemented type of variable {}".format(variable_sort)) signature.append(variable_to_update.sort()) f = z3p.Function(hole_name, *signature) constraint = z3p.And([z3p.Or([z3p.Eq(f(*arg), z3p.IntVal(result)) for result in range(self.variable_ranges[variable_to_update][0], self.variable_ranges[variable_to_update][1] + 1)]) for arg in itertools.product(*domains)]) return (f, domains, constraint, (variable_to_update, self.variables.index(variable_to_update)))
def condition_to_resolve_deadlock(self, locations, memory, verbose=1): """Locations is a map from automaton to current location Memory can contain symbolic values. """ if verbose > 0: print "Deadlock locations {}".format(locations) candidates = self.candidate_transitions(locations) if verbose > 0: print "Candidates are {}".format(candidates) if len(candidates) == 0: return z3p.BoolVal(False) else: return z3p.Or([z3p.And([a.transition_condition(edge[2]['name'], memory[a]) for a, edge in candidate]) for candidate in candidates])
def lookup_table_to_conditional_expression(lookup_table, input_variables): conditional_expression = [] for input_values, output_value in lookup_table.items(): if len(input_values) == 1: conditional = z3p.Eq(input_variables[0], input_values[0]) elif len(input_values) == 0: assert len(lookup_table.values()) == 1 return [(z3p.BoolVal(True), output_value)] else: conditional = z3p.And([ z3p.Eq(var, val) for var, val in zip(input_variables, input_values) ]) conditional_expression += [(conditional, output_value)] return conditional_expression
def guard_enabled(self, state, guard, verbose=0): if guard.decl().name().startswith('table'): conditional = self.tables[guard.decl().name()] expression = z3p.BoolVal(False) for condition, result in conditional: expression = z3p.Or(expression, condition if z3p.is_true(result) else z3p.BoolVal(False)) guard = expression guard_evaluated = util.evaluate_expression(guard, {}, state, self.tables) if verbose > 0: print 'Evaluated guard is {}'.format(guard_evaluated) print 'Simplified guard is {}'.format(z3p.simplify(guard_evaluated)) s = z3p.Solver() s.add(z3p.Neq(guard_evaluated, True)) if s.check() == z3p.unsat: return True else: return False
def evaluate_expression(expression, symbolic_memory, concrete_memory, tables=None): if isinstance(expression, int): return expression if isinstance(expression, tuple): return expression if z3p.is_const(expression): if z3p.is_bool_or_int_value(expression): return expression if expression in symbolic_memory: return symbolic_memory[expression] elif expression.decl().name().startswith('table_'): assert expression.decl().name() in tables table_expression = tables[expression.decl().name()] first_value = table_expression[0][1] if first_value.sort() == z3p.IntSort(): last_else = z3p.IntVal(0) else: last_else = z3p.BoolVal(False) if_expression = None for conditional, value in table_expression: guard = evaluate_expression(conditional, symbolic_memory, concrete_memory) last_else = z3p.If(guard, value, last_else) return z3p.simplify(last_else) else: return concrete_memory[expression] elif expression.decl().kind() == z3p.Z3_OP_SELECT: if expression in symbolic_memory: return symbolic_memory[expression] else: return concrete_memory[expression] else: new_args = [ evaluate_expression(expression.arg(i), symbolic_memory, concrete_memory, tables) for i in range(expression.num_args()) ] if expression.decl().kind() == z3p.Z3_OP_AND: return z3p.And(*new_args) else: return expression.decl()(*new_args)
def lookup_table_for_function(self, function_name): """Returns a model for the function as a dictionary. The dictionary has tuples of argument values as keys. In particular, if the function has a single argument, the key will still be a single element tuple. """ assert (function_name in self.function_names and function_name in self.z3_functions_by_name) z3_function = self.z3_functions_by_name[function_name] model = self.solver.model() table = {} for i in self.function_name_to_domain_values[function_name]: table[i] = model.evaluate(z3_function(i)) # If a value is unconstrained the z3 model might just return # a variable back, therefore for the boolean case if not z3p.is_bool_or_int_value( table[i]) and table[i].sort() == z3p.BoolSort(): # a constant value is returned here. table[i] = z3p.BoolVal(False) return table
def liveness_monitor2(): send = z3p.Int('send') receive = z3p.Int('receive') transitions = [] transitions.append( ('t1', 'initial', receive, z3p.BoolVal(True), [], 'accept')) transitions.append( ('t2', 'initial', receive, z3p.BoolVal(True), [], 'initial')) transitions.append( ('t3', 'initial', send, z3p.BoolVal(True), [], 'initial')) transitions.append( ('t4', 'accept', receive, z3p.BoolVal(True), [], 'accept')) transitions.append(('t5', 'accept', send, z3p.BoolVal(True), [], 'final')) transitions.append( ('t6', 'final', receive, z3p.BoolVal(True), [], 'final')) transitions.append(('t7', 'final', send, z3p.BoolVal(True), [], 'final')) return automaton.SymbolicAutomaton('liveness_monitor2', ['initial', 'accept', 'final'], 'initial', transitions, input_channels=[receive, send])
def safety_monitor(): safety_data_sent = z3p.Int('safety_data_sent') safety_data_received = z3p.Int('safety_data_received') variable_ranges = { safety_data_sent: (min_value_message, max_value_message), safety_data_received: (min_value_message, max_value_message) } send = z3p.Int('send') receive = z3p.Int('receive') transitions = [] transitions.append(('t0', 'waiting_for_send', send, z3p.BoolVal(True), [(safety_data_sent, send)], 'waiting_for_receive')) transitions.append( ('t1', 'waiting_for_receive', receive, z3p.BoolVal(True), [(safety_data_received, receive)], 'check_received_data')) transitions.append(('t2', 'check_received_data', z3p.Neq(safety_data_sent, safety_data_received), [], 'error')) transitions.append(('t3', 'check_received_data', z3p.Eq(safety_data_sent, safety_data_received), [], 'waiting_for_send')) transitions.append( ('t4', 'waiting_for_send', receive, z3p.BoolVal(True), [], 'error')) transitions.append( ('t5', 'waiting_for_receive', send, z3p.BoolVal(True), [], 'error')) transitions.append(('t6', 'error', send, z3p.BoolVal(True), [], 'error')) transitions.append( ('t6', 'error', receive, z3p.BoolVal(True), [], 'error')) return automaton.SymbolicAutomaton( 'safety_monitor', [ 'waiting_for_send', 'waiting_for_receive', 'error', 'check_received_data' ], 'waiting_for_send', transitions, [safety_data_sent, safety_data_received], [z3p.IntVal(0), z3p.IntVal(0)], input_channels=[send, receive], variable_ranges=variable_ranges)
import automaton import util import z3p TRUE = z3p.BoolVal(True) def INT(x): return z3p.IntVal(x) ZERO = z3p.IntVal(0) ONE = z3p.IntVal(1) NUM_ADDRESSES = 1 NUM_CACHES = 2 NUM_DIRECTORIES = 1 NUM_VALUES = 1 LDMsg = {} STMsg = {} EVMsg = {} FwdGetXMsgP = {} FwdGetSMsgP = {} InvAckMsg = {} InvAckMsgP = {} DataMsgD2CP = {} WBAckMsgP = {} DataMsgC2C = {} DataMsgC2CP = {}