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 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 efsm_variable_type(self, variable): a = next(a for a in self.automata if variable in a.variables) if variable.sort() == z3p.BoolSort(): return 'TheLTS->MakeRangeType(0, 1)' elif variable.sort() == z3p.IntSort(): low, high = a.variable_ranges[variable] return "TheLTS->MakeRangeType({}, {})".format(low, high) else: raise NotImplementedError("Channel type cannot be defined for channel {} with sort {}.".format(variable, variable.sort()))
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 efsm_channel_type(self, channel): if isinstance(channel, z3p.ArrayRef): return "{}Type".format(to_camel_case(channel.decl().name())) if channel.sort() == util.UNIT_SORT: return "RangeType" elif channel.sort() == z3p.BoolSort(): return "Range01Type" elif channel.sort() == z3p.IntSort(): variables = self.reading_variables_for_channel(channel) for v in variables: a = next(a for a in self.automata if v in a.variables) low, high = a.variable_ranges[v] return "Range{}{}Type".format(low, high) else: raise NotImplementedError("Channel type cannot be defined for channel {} with sort {}.".format(channel, channel.sort()))
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 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 channel_range(self, channel): if isinstance(channel, z3p.ArrayRef): channel_ranges = [] for i in range(self.array_channel_size(channel)): variables = self.reading_variables_for_channel_field(channel[i]) ranges = set([self.automaton_with_variable(v).variable_ranges[v] for v in variables]) assert len(ranges) == 1 channel_ranges.append(ranges) return channel_ranges # return "{}Type".format(to_camel_case(channel.decl().name())) elif channel.sort() == util.UNIT_SORT: return (0, 0) elif channel.sort() == z3p.BoolSort(): return (0, 1) elif channel.sort() == z3p.IntSort(): variables = self.reading_variables_for_channel_or_channel_field(channel) ranges = set([self.automaton_with_variable(v).variable_ranges[v] for v in variables]) assert len(ranges) == 1 return ranges.pop() else: raise NotImplementedError("Channel type cannot be defined for channel {} with sort {}.".format(channel, channel.sort()))
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 __init__(self, name, locations, initial_location, transitions, variables=None, initial_values=None, input_channels=None, output_channels=None, tables=None, variable_ranges=None, output_channel_ranges=None, compassion=None, justice=None): """ Every node represents a control location and has a unique name. Transitions are of the form (soruce, channel[, channel expression], guard, update, target) where source and target must be automaton control locations, channel an input or output channel, channel expression is there if the channel is an output one, guard is a z3 boolean expression, update is a pair (variable, expression). """ super(SymbolicAutomaton, self).__init__() self.name = name self.variables = variables if variables is not None else [] self.transitions = transitions self.initial_values = initial_values if initial_values is not None else [] self.initials = {} for v, value in zip(self.variables, self.initial_values): self.initials[v] = value assert len(self.variables) == len(self.initial_values), "# of initial values should match # of variables: variables {}, initials: {}".format(self.variables, self.initial_values) self.variable_initial_value = dict(zip([str(v) for v in self.variables], self.initial_values)) # TODO change this to work with uninitialized variables # change variables input to include initial values self.input_channels = input_channels if input_channels is not None else [] self.output_channels = output_channels if output_channels is not None else [] # add channel with unit type unit can be an mtype with just one symbol for l in locations: self.add_node(l) for transition in transitions: channel_expression = [] # if it is an input transition, there is no expression for the message to send if len(transition) == 6: name, source, channel, guard, update, target = transition assert channel not in self.output_channels, "Output channel {} used in input transition {}".format(channel, transition) kind = 'input' # output transitions elif len(transition) == 7: name, source, channel, channel_expression, guard, update, target = transition assert channel not in self.input_channels, "Input channel {} used in output transition {}".format(channel, transition) kind = 'output' # internal transitios elif len(transition) == 5: name, source, guard, update, target = transition channel = None assert source in locations and target in locations, transition assert (channel in self.input_channels or channel in self.output_channels or channel is None), transition if channel is None: kind = 'internal' if name is None: name = 't_{}'.format(self.number_of_edges()) self.add_edge(source, target, channel=channel, channel_expression=channel_expression, guard=guard, update=update, name=name, kind=kind, automaton=self) assert initial_location in self.nodes() self.initial_location = initial_location self.tables = {} if tables is None else tables self.channel_edges = {} self.update_channel_edges() self.changes = [] self.transition_variables = {} self.update_transition_variables() self.variable_ranges = {} if variable_ranges is None else variable_ranges self.output_channel_ranges = {} if output_channel_ranges is None else output_channel_ranges for v in self.variables: if v.sort() == z3p.IntSort(): assert v in self.variable_ranges, "No range declared for {}".format(v) for variable, variable_range in self.variable_ranges.items(): assert variable in self.variables, "Variable {} was not declared".format(variable) assert variable.sort() == z3p.IntSort() assert isinstance(variable_range, tuple) and len(variable_range) == 2 assert isinstance(variable_range[0], int) assert isinstance(variable_range[1], int) assert variable_range[0] <= variable_range[1] for output_channel_or_field, output_channel_range in self.output_channel_ranges.items(): if not z3p.is_const(output_channel_or_field) and output_channel_or_field.decl().kind() == z3p.Z3_OP_SELECT: assert output_channel_or_field.arg(0) in self.output_channels else: assert output_channel_or_field in self.output_channels # assert output_channel.sort() == z3p.IntSort() assert isinstance(output_channel_range, tuple) and len(output_channel_range) == 2 assert isinstance(output_channel_range[0], int) assert isinstance(output_channel_range[1], int) assert output_channel_range[0] < output_channel_range[1] if compassion is not None: self.compassion = compassion else: self.compassion = [] if justice is not None: self.justice = justice else: self.justice = []
def response_channel(address_id, cache_id, directory_id, num_values, num_caches): name = 'response_channel_{}_{}_{}'.format(cache_id, address_id, directory_id) inputs = [] outputs = [] data_ranges = {} number_of_fields = {} suffix = '_{}_{}_{}'.format(cache_id, directory_id, address_id) wbackmsg = 'WBAckMsg' wbackmsg_input = util.new_variable(wbackmsg + suffix, 'unit') wbackmsg_output = util.new_variable(wbackmsg + 'P' + suffix, 'unit') inputs.append(wbackmsg_input) outputs.append(wbackmsg_output) locations = ['empty'] transitions = [] full_state = 'WBAckMsg_full' locations.append(full_state) transitions.append((None, 'empty', wbackmsg_input, TRUE, [], full_state)) transitions.append((None, full_state, wbackmsg_output, [ZERO], TRUE, [], 'empty')) variable_ranges = {} variable_data = z3p.Int('data_{}'.format(name)) variable_num_caches = z3p.Int('num_caches_{}'.format(name)) variable_ranges[variable_data] = (0, num_values - 1) variable_ranges[variable_num_caches] = (0, num_caches) datamsgd2c = 'DataMsgD2C' datamsgd2c_input = z3p.Array(datamsgd2c + suffix, z3p.IntSort(), z3p.IntSort()) datamsgd2c_output = z3p.Array(datamsgd2c + 'P' + suffix, z3p.IntSort(), z3p.IntSort()) inputs.append(datamsgd2c_input) outputs.append(datamsgd2c_output) full_state = 'DataMsgD2C_full' locations.append(full_state) transitions.append((None, 'empty', datamsgd2c_input, TRUE, [(variable_data, datamsgd2c_input[0]), (variable_num_caches, datamsgd2c_input[1])], full_state)) transitions.append((None, full_state, datamsgd2c_output, [variable_data, variable_num_caches], TRUE, [(variable_data, ZERO), (variable_num_caches, ZERO)], 'empty')) datamsgc2c = 'DataMsgC2C' invackmsg = 'InvAckMsg' for other_cache_id in range(num_caches): if other_cache_id != cache_id: suffix2 = '_{}{}'.format(other_cache_id, suffix) datamsgc2c_input = z3p.Int(datamsgc2c + suffix2) datamsgc2c_output = z3p.Int(datamsgc2c + 'P' + suffix2) inputs.append(datamsgc2c_input) outputs.append(datamsgc2c_output) full_state = 'DataMsgC2C_full_{}'.format(other_cache_id) locations.append(full_state) transitions.append((None, 'empty', datamsgc2c_input, TRUE, [(variable_data, datamsgc2c_input)], full_state)) transitions.append((None, full_state, datamsgc2c_output, [variable_data], TRUE, [(variable_data, ZERO)], 'empty')) invackmsg_input = util.new_variable(invackmsg + suffix2, 'unit') invackmsg_output = util.new_variable(invackmsg + 'P' + suffix2, 'unit') inputs.append(invackmsg_input) outputs.append(invackmsg_output) full_state = 'InvAckMsg_full_{}'.format(other_cache_id) locations.append(full_state) transitions.append((None, 'empty', invackmsg_input, TRUE, [], full_state)) transitions.append((None, full_state, invackmsg_output, [ZERO], TRUE, [], 'empty')) return automaton.SymbolicAutomaton(name, locations, 'empty', transitions, variables=[variable_data, variable_num_caches], initial_values=[ZERO, ZERO], input_channels=inputs, output_channels=outputs, variable_ranges=variable_ranges)
UnblockEMsgP[c][d] = {} FwdGetXMsg[c][d] = {} FwdGetSMsg[c][d] = {} DataMsgD2C[c][d] = {} WBAckMsg[c][d] = {} for a in range(NUM_ADDRESSES): suffix = '_{}_{}_{}'.format(c, d, a) LDMsg[c][d][a] = util.new_variable('LDMsg' + suffix, 'unit') STMsg[c][d][a] = z3p.Int('STMsg' + suffix) EVMsg[c][d][a] = util.new_variable('EVMsg' + suffix, 'unit') FwdGetXMsgP[c][d][a] = z3p.Int('FwdGetXMsgP' + suffix) FwdGetSMsgP[c][d][a] = z3p.Int('FwdGetSMsgP' + suffix) DataMsgD2CP[c][d][a] = z3p.Array('DataMsgD2CP' + suffix, z3p.IntSort(), z3p.IntSort()) WBAckMsgP[c][d][a] = util.new_variable('WBAckMsgP' + suffix, 'unit') LDAckMsg[c][d][a] = z3p.Int('LDAckMsg' + suffix) STAckMsg[c][d][a] = z3p.Int('STAckMsg' + suffix) EVAckMsg[c][d][a] = util.new_variable('EVAckMsg' + suffix, 'unit') UnblockSMsg[c][d][a] = util.new_variable('UnblockSMsg' + suffix, 'unit') UnblockEMsg[c][d][a] = util.new_variable('UnblockEMsg' + suffix, 'unit') GetXMsg[c][d][a] = util.new_variable('GetXMsg' + suffix, 'unit') GetSMsg[c][d][a] = util.new_variable('GetSMsg' + suffix, 'unit') WBMsg[c][d][a] = z3p.Int('WBMsg' + suffix) GetXMsgP[c][d][a] = util.new_variable('GetXMsgP' + suffix, 'unit') GetSMsgP[c][d][a] = util.new_variable('GetSMsgP' + suffix, 'unit')