def get_non_crashing_cover_set(fsm: MealyMachine): scs = get_state_cover_set(fsm) non_crashing = set() for seq in scs: fsm.reset() output = fsm.process_input(seq) if (output is not None) and ("error" not in output): non_crashing.add(seq) return non_crashing
def get_dset_outputs(fsm, dset): states = fsm.get_states() outputs = {} for state in states: mm = MealyMachine(state) out = [] for dseq in dset: out.append(mm.process_input(dseq)) mm.reset() outputs[state] = tuple(out.copy()) return outputs
def __init__(self, fsm: MealyMachine): self.fsm = fsm self.A = fsm.get_alphabet() self.states = self.fsm.get_states() self.root = PartitionNode(self.states, self.A, self) self.nodes = [self.root] self.wanted = set([state.name for state in fsm.get_states()]) self.closed = set() self.solution = set()
def get_reached_error_states(fsm: MealyMachine): reached = set() states = fsm.get_states() for state in states: outputs = list(zip(*state.edges.values()))[1] for output in outputs: if (match := re.search('error_([0-9]*)', output)) is not None: reached.add(int(match.group(1)))
def check_distinguishing_set(fsm, dset): states = fsm.get_states() outputs = {} for state in states: mm = MealyMachine(state) out = [] for dseq in dset: out.append(mm.process_input(dseq)) mm.reset() outputs[state] = tuple(out.copy()) if len(set(outputs.values())) < len(outputs): print("Not unique", outputs) return False else: print('succes!', len(outputs), 'states,', len(set(outputs)), 'unique outputs') return True
def mealy2mcrl2(fsm: MealyMachine, path): states = fsm.get_states() # Collect the actions performed to put into the act section of the mcrl2 file acts = set() # Collect the lines to go in the proc section of the mcrl2 file proc_lines = [] # We also need the initial state, for the init line in the mcrl2 file init_line = f'init {fsm.initial_state.name};' for state in states: # Build the line for this state state_line = f'{state.name} = ' first = True intermediate_lines = [] for action, (next_state, output) in state.edges.items(): # Keep track of actions acts.add(action) acts.add(output) # Transitions to intermediate state intermediate_state_name = f'o_{state.name}_{output}' state_line += f'{" + " if not first else ""}{action}.{intermediate_state_name}' # Transitions from intermediate state to actual next state intermediate_lines.append(f'\t{intermediate_state_name} = {output}.{next_state.name};') first = False if len(state.edges) == 0: state_line += 'delta' state_line += ';' proc_lines.append(state_line) proc_lines += intermediate_lines with open(path, 'w') as file: # Act file.write(f'act {", ".join([str(x) for x in acts])};\n\n') # Proc file.write('proc ') for line in proc_lines: file.write(f'{line}\n') file.write('\n') # Init file.write(init_line)
def value_iterate(dfa: MealyMachine, gamma, epsilon=0.0001): states = dfa.get_states() actions = dfa.get_alphabet() Q = {} Q_1 = {} # Initialize Q0 for sa in product(states, actions): Q[sa] = 0 converged = False while not converged: # Iterate for s, a in product(states, actions): next_state, action_result = s.next(a) action_reward = 0 if action_result == "end" else int(action_result) future_reward = gamma * max([Q[(next_state, next_action)] for next_action in actions]) Q_1[(s, a)] = action_reward + future_reward # Check convergence converged = max([abs(Q[sa] - Q_1[sa]) for sa in product(states, actions)]) < epsilon Q = Q_1 Q_1 = {} # Annotate states with their values for state in states: value = max([Q[sa] for sa in product([state], actions)]) state.name = f'{state.name}/{value:.2f}' # Also update the action rewards for action in actions: next_state, output = state.edges[action] state.edges[action] = (next_state, f'{Q[(state, action)]:.2f}') dfa.render_graph(tempfile.mktemp('.gv'))
def load_mealy_dot(path, parse_rules=industrial_mealy_parser): # Parse the dot file context = {'nodes': [], 'edges': []} with open(path, 'r') as file: for line in file.readlines(): _parse(parse_rules, line, context) # Build the mealy graph nodes = {name: MealyState(name) for (name, _) in context['nodes']} for (frm, to), edge_properties in context['edges']: input, output = edge_properties['label'].strip('"').split('/') nodes[frm].add_edge(input, output, nodes[to]) startnode = nodes[context['start']] return MealyMachine(startnode)
def _render(fsm: MealyMachine, filename): g = Digraph('G', filename=filename) g.attr(rankdir='LR') states = fsm.get_states() # Add states for state in states: g.node(state.name) # Add transitions: for state in states: for action, (other_state, output) in state.edges.items(): g.edge(state.name, other_state.name, label=f'{action}/{output}') g.save()
def build_dfa(self): # Gather states from S S = self.S # The rows can function as index to the 'state' objects state_rows = set([tuple(self._get_row(s)) for s in S]) initial_state_row = tuple(self._get_row(tuple())) # Generate state names for convenience state_names = { state_row: f's{n + 1}' for (n, state_row) in enumerate(state_rows) } # Build the state objects and get the initial and accepting states states: Dict[Tuple, MealyState] = { state_row: MealyState(state_names[state_row]) for state_row in state_rows } initial_state = states[initial_state_row] # Add the connections between states A = [a for (a, ) in self.A] # Keep track of states already visited visited_rows = [] for s in S: s_row = tuple(self._get_row(s)) if s_row not in visited_rows: for a in A: if self._is_valid(s + (a, )): sa_row = tuple(self._get_row(s + (a, ))) if sa_row in states.keys(): try: cur_output = self.query(s + (a, )) states[s_row].add_edge(a, cur_output, states[sa_row]) except: # Can't add the same edge twice pass else: visited_rows.append(s_row) return MealyMachine(initial_state)
def construct_hypothesis(self): # Keep track of the initial state initial_state = self.S[()] # Keep track of the amount of states, so we can sift again if # the sifting process created a new state n = len(list(self.S.items())) items_added = True # Todo: figure out a neater way to handle missing states during sifting than to just redo the whole thing while items_added: # Add transitions for access_seq, cur_state in list(self.S.items()): for a in self.A: next_state = self.sift(access_seq + a) output = self.query(access_seq + a) cur_state.add_edge(a[0], output, next_state, override=True) # Check if no new state was added n2 = len((self.S.items())) items_added = n != n2 # print("items added", items_added) n = n2 # Add spanning tree transitions for access_seq, state in self.S.items(): if len(access_seq) > 0: ancestor_acc_seq = access_seq[0:-1] ancestor_state = self.S[ancestor_acc_seq] a = access_seq[-1] output = self.query(ancestor_acc_seq + (a, )) ancestor_state.add_edge(a, output, state, override=True) # Find accepting states # accepting_states = [state for access_seq, state in self.S.items() if self.query(access_seq)] return MealyMachine(initial_state)
def MakeRandomMealyMachine(n_states, A_in, A_out): states = [MealyState(f's{x + 1}') for x in range(n_states)] def get_reachable(start_state, states): to_visit = [start_state] visited = [] while len(to_visit) > 0: cur_state = to_visit.pop() if cur_state not in visited: visited.append(cur_state) for action, (other_state, output) in cur_state.edges.items(): if other_state not in visited and other_state not in to_visit: to_visit.append(other_state) return visited, list(set(states).difference(set(visited))) def fix_missing(states): for state in states: for a in A_in: if a not in state.edges.keys(): state.add_edge(a, "error", state) reached, unreached = get_reachable(states[0], states) while len(unreached) > 0: x = random.choice(reached) y = random.choice(unreached) a = random.choice(A_in) o = random.choice(A_out) x.add_edge(a, o, y, override=True) reached, unreached = get_reachable(states[0], states) fix_missing(states) return MealyMachine(states[0])
def minimize(mm: MealyMachine): dset = get_distinguishing_set(mm) dset_outputs = get_dset_outputs(mm, dset) # Find non-unique states: state_map = {} for state, outputs in dset_outputs.items(): if outputs not in state_map: state_map[outputs] = [state] else: state_map[outputs].append(state) for outputs, states in state_map.items(): if len(states) > 1: og_state = states[0] rest_states = states[1:] states = mm.get_states() for state in states: for action, (other_state, output) in state.edges.items(): if other_state in rest_states: state.edges[action] = og_state, output return mm
def mealy2nusmv_withintermediate(fsm: MealyMachine): # Collect fsm info states = fsm.get_states() alphabet = fsm.get_alphabet() inputs = set() outputs = set() statenames = set() initial_state_name = fsm.initial_state.name for state in states: statenames.add(state.name) for input, (next_state, output) in state.edges.items(): inputs.add(input) outputs.add(output) # Get a comma separated list of the state names + extra intermediate output states # The intermediate states are named *original state name*_o_*input* statenames_with_intermediate = [] for statename in statenames: statenames_with_intermediate.append(statename) for a in alphabet: statenames_with_intermediate.append(f'{statename}_o_{a}') statenames_with_intermediate = ",".join(statenames_with_intermediate) first_valid_inputs = [] for input, (next_state, output) in fsm.initial_state.edges.items(): if output != "invalid_input": first_valid_inputs.append(input) # Assemble smv file smvlines = [] smvlines.append("MODULE main\n") smvlines.append("VAR\n") smvlines.append("\tstate : {" + statenames_with_intermediate + "};\n") smvlines.append("\toutput: {" + ",".join(outputs) + "," + ",".join(inputs) + "};\n") smvlines.append("ASSIGN\n") smvlines.append(f"\tinit(state) := {initial_state_name};\n") smvlines.append("\tnext(state) := case\n") for state in states: for input, (next_state, output) in state.edges.items(): if output != "invalid_input": intermediate_name = f'{state.name}_o_{input}' smvlines.append(f"\t\t\tstate = {state.name} & output = {input}: {intermediate_name};\n") smvlines.append(f"\t\t\tstate = {intermediate_name} : {next_state.name};\n") smvlines.append("\t\t\tTRUE : state;\n") smvlines.append("\tesac;\n") # The initial inputs also can't be invalid input, # So we cannot just choose any alphabet character #smvlines.append("\tinit(output) := {" + ",".join(alphabet) + "};\n") smvlines.append("\tinit(output) := {" + ",".join(first_valid_inputs) + "};\n") smvlines.append("\tnext(output) := case\n") for state in states: for input, (next_state, output) in state.edges.items(): if output != "invalid_input": intermediate_name = f'{state.name}_o_{input}' smvlines.append(f"\t\t\tstate = {state.name} & next(state) = {intermediate_name} : {output};\n") valid_next_inputs = [n_input for (n_input, (_, n_output)) in next_state.edges.items() if n_output != "invalid_input"] if len(valid_next_inputs) > 0: smvlines.append(f"\t\t\tstate = {intermediate_name} : " + "{" + ",".join(valid_next_inputs) + "};\n") smvlines.append("\t\t\tTRUE : output;\n") smvlines.append("\tesac;\n") return smvlines
name_to_c[a] = b c_to_name[b] = a return name_to_c, c_to_name if __name__ == "__main__": s1 = MealyState('s1') s2 = MealyState('s2') s3 = MealyState('s3') s1.add_edge('a', 'nice', s2) s1.add_edge('b', 'a', s1) s2.add_edge('a', 'nice', s3) s2.add_edge('b', 'back_lol', s1) s3.add_edge('a', 'b', s3) s3.add_edge('b', 'c', s1) mm = MealyMachine(s1) constrpath = '/home/tom/projects/lstar/rers/TrainingSeqLtlRers2020/Problem1/constraints-Problem1.txt' mappingpath= '/home/tom/projects/lstar/rers/TrainingSeqLtlRers2020/Problem1/Problem1_alphabet_mapping_C_version.txt' mealy_lines = mealy2nusmv_withintermediate(mm) ltl_lines = rersltl2smv_withintermediate(constrpath, mappingpath) with open('test.smv', 'w') as file: file.writelines(mealy_lines) file.writelines(ltl_lines) print('done')
from equivalencecheckers.wmethod import WmethodEquivalenceChecker from learners.TTTmealylearner import TTTMealyLearner from suls.mealymachine import MealyState, MealyMachine from teachers.teacher import Teacher # Set up an example mealy machine s1 = MealyState('1') s2 = MealyState('2') s3 = MealyState('3') s1.add_edge('a', 'nice', s2) s1.add_edge('b', 'B', s1) s2.add_edge('a', 'nice', s3) s2.add_edge('b', 'back', s1) s3.add_edge('a', 'A', s3) s3.add_edge('b', 'back', s1) mm = MealyMachine(s1) # Use the W method equivalence checker eqc = WmethodEquivalenceChecker(mm, m=len(mm.get_states())) teacher = Teacher(mm, eqc) # We are learning a mealy machine learner = TTTMealyLearner(teacher) hyp = learner.run() hyp.render_graph(tempfile.mktemp('.gv')) learner.DTree.render_graph(tempfile.mktemp('.gv'))
from suls.mealymachine import MealyState, MealyMachine s1 = MealyState('q0') s2 = MealyState('q1') s3 = MealyState('q2') s1.add_edge('a', '1', s2) s1.add_edge('b', '0', s1) s2.add_edge('b', '2', s3) s2.add_edge('a', '0', s2) s3.add_edge('a', '3', s3) s3.add_edge('b', '3', s3) dfa = MealyMachine(s1) dfa.render_graph('example_mealy', format='png')
from suls.mealymachine import MealyMachine from suls.mealymachine import MealyState as State from util.mealy2nusmv import mealy2nusmv_withintermediate q0 = State('q0') q1 = State('q1') q0.add_edge('a', 'A', q1) mm = MealyMachine(q0) for line in mealy2nusmv_withintermediate(mm): print(line, end='')
from equivalencecheckers.wmethod import WmethodEquivalenceChecker from learners.mealylearner import MealyLearner from suls.mealymachine import MealyState, MealyMachine from teachers.teacher import Teacher # Set up an example mealy machine s1 = MealyState('1') s2 = MealyState('2') s3 = MealyState('3') s1.add_edge('a', 'nice', s2) s1.add_edge('b', 'B', s1) s2.add_edge('a', 'nice', s3) s2.add_edge('b', 'back', s1) s3.add_edge('a', 'A', s3) s3.add_edge('b', 'back', s1) mm = MealyMachine(s1) mm.render_graph(tempfile.mktemp('.gv')) # Use the W method equivalence checker eqc = WmethodEquivalenceChecker(mm) teacher = Teacher(mm, eqc) # We are learning a mealy machine learner = MealyLearner(teacher) hyp = learner.run() hyp.render_graph(tempfile.mktemp('.gv'))