def get_small_pomdp(): from aalpy.automata import Mdp, MdpState q0 = MdpState("q0", "init") q1 = MdpState("q1", "beep") q2 = MdpState("q2", "beep") q3 = MdpState("q3", "coffee") q4 = MdpState("q4", "tea") q0.transitions['but'].append((q0, 1)) q0.transitions['coin'].append((q1, 0.8)) q0.transitions['coin'].append((q2, 0.2)) q1.transitions['coin'].append((q1, 1)) q1.transitions['but'].append((q3, 1)) q2.transitions['coin'].append((q2, 0.3)) q2.transitions['coin'].append((q1, 0.7)) q2.transitions['but'].append((q4, 1)) q3.transitions['coin'].append((q3, 1)) q3.transitions['but'].append((q3, 1)) q4.transitions['coin'].append((q4, 1)) q4.transitions['but'].append((q4, 1)) return Mdp(q0, [q0, q1, q2, q3, q4])
def smm_to_mdp_conversion(smm: StochasticMealyMachine): """ Convert SMM to MDP. Args: smm: StochasticMealyMachine: SMM to convert Returns: equivalent MDP """ inputs = smm.get_input_alphabet() mdp_states = [] smm_state_to_mdp_state = dict() init_state = MdpState("0", "___start___") mdp_states.append(init_state) for s in smm.states: incoming_edges = defaultdict(list) incoming_outputs = set() for pre_s in smm.states: for i in inputs: incoming_edges[i] += filter(lambda t: t[0] == s, pre_s.transitions[i]) incoming_outputs.update(map(lambda t: t[1], incoming_edges[i])) state_id = 0 for o in incoming_outputs: new_state_id = s.state_id + str(state_id) state_id += 1 new_state = MdpState(new_state_id, o) mdp_states.append(new_state) smm_state_to_mdp_state[(s.state_id, o)] = new_state for s in smm.states: mdp_states_for_s = { mdp_state for (s_id, o), mdp_state in smm_state_to_mdp_state.items() if s_id == s.state_id } for i in inputs: for outgoing_t in s.transitions[i]: target_smm_state = outgoing_t[0] output = outgoing_t[1] prob = outgoing_t[2] target_mdp_state = smm_state_to_mdp_state[( target_smm_state.state_id, output)] for mdp_state in mdp_states_for_s: mdp_state.transitions[i].append((target_mdp_state, prob)) if s == smm.initial_state: init_state.transitions[i].append((target_mdp_state, prob)) return Mdp(init_state, mdp_states)
def generate_random_mdp(num_states, len_input, custom_outputs=None, num_unique_outputs=None): """ Generates random MDP. Args: num_states: number of states len_input: number of inputs custom_outputs: user predefined outputs num_unique_outputs: number of outputs Returns: random MDP """ num_unique_outputs = num_states if not num_unique_outputs else num_unique_outputs outputs = [ random_string_generator(random.randint(3, 7)) for _ in range(num_unique_outputs) ] outputs = custom_outputs if custom_outputs else outputs while len(outputs) < num_states: outputs.append(random.choice(outputs)) possible_probabilities = [1.0, 1.0, 1.0, 1.0, 0.8, 0.5, 0.9] states = [] for i in range(num_states): states.append(MdpState(f'q{i}', outputs.pop())) for state in states: for i in range(len_input): prob = random.choice(possible_probabilities) if prob == 1.: state.transitions[i].append((random.choice(states), prob)) else: new_states = list(states) s1 = random.choice(new_states) new_states.remove(s1) state.transitions[i].append((s1, prob)) state.transitions[i].append( (random.choice(new_states), round(1 - prob, 2))) return Mdp(states[0], states), list(range(len_input))
def get_faulty_coffee_machine_MDP(): q0 = MdpState("q0", "init") q1 = MdpState("q1", "beep") q2 = MdpState("q2", "coffee") q0.transitions['but'].append((q0, 1)) q0.transitions['coin'].append((q1, 1)) q1.transitions['but'].append((q0, 0.1)) q1.transitions['but'].append((q2, 0.9)) q1.transitions['coin'].append((q1, 1)) q2.transitions['but'].append((q0, 1)) q2.transitions['coin'].append((q1, 1)) mdp = Mdp(q0, [q0, q1, q2]) return mdp
def get_weird_coffee_machine_MDP(): from aalpy.automata import Mdp, MdpState q0 = MdpState("q0", "init") q1 = MdpState("q1", "beep") q2 = MdpState("q2", "coffee") q3 = MdpState("q3", "beep") q4 = MdpState("q4", "coffee") q5 = MdpState("q5", "init") q6 = MdpState("q6", "crash") q0.transitions['but'].append((q0, 1)) q0.transitions['coin'].append((q1, 1)) q0.transitions['koin'].append((q3, 1)) q1.transitions['but'].append((q0, 0.1)) q1.transitions['but'].append((q2, 0.9)) q3.transitions['but'].append((q0, 0.1)) q3.transitions['but'].append((q4, 0.9)) q1.transitions['coin'].append((q1, 1)) q3.transitions['koin'].append((q3, 1)) q1.transitions['koin'].append((q3, 1)) q3.transitions['coin'].append((q1, 1)) q2.transitions['but'].append((q0, 1)) q2.transitions['coin'].append((q1, 1)) q2.transitions['koin'].append((q3, 1)) q4.transitions['coin'].append((q1, 1)) q4.transitions['koin'].append((q3, 1)) q4.transitions['but'].append((q5, 1)) q5.transitions['but'].append((q6, 1)) q5.transitions['coin'].append((q6, 1)) q5.transitions['koin'].append((q6, 1)) q6.transitions['but'].append((q6, 1)) q6.transitions['coin'].append((q6, 1)) q6.transitions['koin'].append((q6, 1)) mdp = Mdp(q0, [q0, q1, q2, q3, q4, q5, q6]) return mdp
def to_mdp(): eq_oracle = RandomWMethodEqOracle(alphabet, model_sul) learned_model = run_Lstar(alphabet, model_sul, eq_oracle, 'moore') # CC2640R2-no-feature-req.dot # {'mtu_req', 'pairing_req',} have 0.3 percent chance of looping to initial state moore_mdp_state_map = dict() initial_mdp_state = None for state in learned_model.states: mdp_state = MdpState(state.state_id, state.output) moore_mdp_state_map[state.prefix] = mdp_state if not state.prefix: initial_mdp_state = mdp_state # moore_mdp_state_map['sink'] = MdpState('sink', 'NO_RESPONSE') assert initial_mdp_state for state in learned_model.states: mdp_state = moore_mdp_state_map[state.prefix] state_num = int(mdp_state.state_id[1:]) for i in alphabet: reached_moore = state.transitions[i].prefix # if i in {'pairing_req', 'mtu_req'} and mdp_state.output != moore_mdp_state_map[reached_moore].output: if state_num % 2 == 0 and mdp_state.output != moore_mdp_state_map[ reached_moore].output: mdp_state.transitions[i].append((mdp_state, 0.2)) mdp_state.transitions[i].append( (moore_mdp_state_map[reached_moore], 0.8)) else: mdp_state.transitions[i].append( (moore_mdp_state_map[reached_moore], 1.)) mdp = Mdp(initial_mdp_state, list(moore_mdp_state_map.values())) return mdp
def generate_hypothesis(self): """Generates the hypothesis from the observation table. :return: current hypothesis Args: Returns: """ r_state_map = dict() state_counter = 0 for r in self.compatibility_classes_representatives: if self.automaton_type == 'mdp': r_state_map[r] = MdpState(state_id=f's{state_counter}', output=r[-1]) else: r_state_map[r] = StochasticMealyState(state_id=f's{state_counter}') r_state_map[r].prefix = r state_counter += 1 if self.automaton_type == 'mdp': r_state_map['chaos'] = MdpState(state_id=f's{state_counter}', output='chaos') for i in self.input_alphabet: r_state_map['chaos'].transitions[i[0]].append((r_state_map['chaos'], 1.)) else: r_state_map['chaos'] = StochasticMealyState(state_id=f'chaos') for i in self.input_alphabet: r_state_map['chaos'].transitions[i[0]].append((r_state_map['chaos'], 'chaos', 1.)) for s in self.compatibility_classes_representatives: for i in self.input_alphabet: freq_dict = self.T[s][i] total_sum = sum(freq_dict.values()) origin_state = s if self.strategy == 'classic' and not self.teacher.complete_query(s, i) \ or self.strategy != 'classic' and i not in self.T[s]: if self.automaton_type == 'mdp': r_state_map[origin_state].transitions[i[0]].append((r_state_map['chaos'], 1.)) else: r_state_map[origin_state].transitions[i[0]].append((r_state_map['chaos'], 'chaos', 1.)) else: if len(freq_dict.items()) == 0: if self.automaton_type == 'mdp': r_state_map[origin_state].transitions[i[0]].append((r_state_map['chaos'], 1.)) else: r_state_map[origin_state].transitions[i[0]].append((r_state_map['chaos'], 'chaos', 1.)) else: for output, frequency in freq_dict.items(): new_state = self.get_representative(s + i + tuple([output])) if self.automaton_type == 'mdp': r_state_map[origin_state].transitions[i[0]].append( (r_state_map[new_state], frequency / total_sum)) else: r_state_map[origin_state].transitions[i[0]].append( (r_state_map[new_state], output, frequency / total_sum)) if self.automaton_type == 'mdp': return Mdp(r_state_map[self.get_representative(self.initial_output)], list(r_state_map.values())) else: return StochasticMealyMachine(r_state_map[tuple()], list(r_state_map.values()))
def generate_random_mdp(num_states, input_size, output_size, possible_probabilities=None): """ Generates random MDP. Args: num_states: number of states input_size: number of inputs output_size: user predefined outputs possible_probabilities: list of possible probability pairs to choose from Returns: random MDP """ inputs = [f'i{i+1}' for i in range(input_size)] outputs = [f'o{i+1}' for i in range(output_size)] if not possible_probabilities: possible_probabilities = [(1., ), (1., ), (1., ), (0.9, 0.1), (0.8, 0.2), (0.7, 0.3), (0.8, 0.1, 0.1), (0.7, 0.2, 0.1), (0.6, 0.2, 0.1, 0.1)] # ensure that there are no infinite loops possible_probabilities = [ p for p in possible_probabilities if len(p) <= num_states ] state_outputs = outputs.copy() states = [] for i in range(num_states): curr_output = state_outputs.pop(0) if state_outputs else random.choice( outputs) states.append(MdpState(f'q{i}', curr_output)) state_buffer = list(states) for state in states: for i in inputs: prob = random.choice(possible_probabilities) reached_states = [] for _ in prob: while True: new_state = random.choice( state_buffer) if state_buffer else random.choice( states) # ensure determinism if new_state.output not in { s.output for s in reached_states }: break if state_buffer: state_buffer.remove(new_state) reached_states.append(new_state) for prob, reached_state in zip(prob, reached_states): state.transitions[i].append((reached_state, prob)) return Mdp(states[0], states)
def load_automaton_from_file(path, automaton_type, compute_prefixes=False): """ Loads the automaton from the file. Standard of the automatas strictly follows syntax found at: https://automata.cs.ru.nl/Syntax/Overview. Args: path: path to the file automaton_type: type of the automaton, if not specified it will be automatically determined according, one of ['dfa', 'mealy', 'moore', 'mdp', 'smm', 'onfsm'] compute_prefixes: it True, shortest path to reach every state will be computed and saved in the prefix of the state. Useful when loading the model to use them as a equivalence oracle. (Default value = False) Returns: automaton """ graph = graph_from_dot_file(path)[0] assert automaton_type in automaton_types node = MealyState if automaton_type == 'mealy' else DfaState if automaton_type == 'dfa' else MooreState node = MdpState if automaton_type == 'mdp' else StochasticMealyState if automaton_type == 'smm' else node node = OnfsmState if automaton_type == 'onfsm' else node assert node is not None node_label_dict = dict() for n in graph.get_node_list(): if n.get_name() == '__start0' or n.get_name() == '': continue label = None if 'label' in n.get_attributes().keys(): label = n.get_attributes()['label'] label = _process_label(label) node_name = n.get_name() output = None if automaton_type == 'moore' and label != "": label_output = _process_label(label) label = label_output.split('|')[0] output = label_output.split('|')[1] if automaton_type == 'mdp': node_label_dict[node_name] = node(node_name, label) else: node_label_dict[node_name] = node( label, output) if automaton_type == 'moore' else node(label) if 'shape' in n.get_attributes().keys( ) and 'doublecircle' in n.get_attributes()['shape']: node_label_dict[node_name].is_accepting = True initial_node = None for edge in graph.get_edge_list(): if edge.get_source() == '__start0': initial_node = node_label_dict[edge.get_destination()] continue source = node_label_dict[edge.get_source()] destination = node_label_dict[edge.get_destination()] label = edge.get_attributes()['label'] label = _process_label(label) if automaton_type == 'mealy': inp = label.split('/')[0] out = label.split('/')[1] inp = int(inp) if inp.isdigit() else inp out = int(out) if out.isdigit() else out source.transitions[inp] = destination source.output_fun[inp] = out elif automaton_type == 'onfsm': inp = label.split('/')[0] out = label.split('/')[1] inp = int(inp) if inp.isdigit() else inp out = int(out) if out.isdigit() else out source.transitions[inp].append((out, destination)) elif automaton_type == 'smm': inp = label.split('/')[0] out_prob = label.split('/')[1] out = out_prob.split(':')[0] prob = out_prob.split(':')[1] inp = int(inp) if inp.isdigit() else inp out = int(out) if out.isdigit() else out source.transitions[inp].append((destination, out, float(prob))) elif automaton_type == 'mdp': inp = label.split(':')[0] prob = label.split(':')[1] inp = int(inp) if inp.isdigit() else inp prob = float(prob) source.transitions[inp].append((destination, prob)) else: source.transitions[int(label) if label.isdigit( ) else label] = destination if automaton_type == 'dfa': automaton = Dfa(initial_node, list(node_label_dict.values())) elif automaton_type == 'moore': automaton = MooreMachine(initial_node, list(node_label_dict.values())) elif automaton_type == 'mdp': automaton = Mdp(initial_node, list(node_label_dict.values())) elif automaton_type == 'smm': automaton = StochasticMealyMachine(initial_node, list(node_label_dict.values())) elif automaton_type == 'onfsm': automaton = Onfsm(initial_node, list(node_label_dict.values())) else: automaton = MealyMachine(initial_node, list(node_label_dict.values())) assert automaton.is_input_complete() if compute_prefixes: for state in automaton.states: state.prefix = automaton.get_shortest_path(automaton.initial_state, state) return automaton