def gen_hypothesis(self) -> Onfsm:
        """
        Generate automaton based on the values found in the abstracted observation table.

        Returns:

            Current abstracted hypothesis

        """
        state_distinguish = dict()
        states_dict = dict()
        initial = None

        unified_S = self.S + self.S_dot_A

        stateCounter = 0
        for prefix in self.S:
            state_id = f's{stateCounter}'
            states_dict[prefix] = OnfsmState(state_id)

            states_dict[prefix].prefix = prefix
            state_distinguish[self.row_to_hashable(
                prefix)] = states_dict[prefix]

            if prefix == self.S[0]:
                initial = states_dict[prefix]
            stateCounter += 1

        for prefix in self.S:
            similar_rows = []
            for row in unified_S:
                if self.row_to_hashable(row) == self.row_to_hashable(prefix):
                    similar_rows.append(row)
            for row in similar_rows:
                for a in self.A:
                    for t in self.observation_table.T[row][a]:
                        if (row[0] + a, row[1] + tuple([t])) in unified_S:
                            state_in_S = state_distinguish[
                                self.row_to_hashable(
                                    (row[0] + a, row[1] + tuple([t])))]

                            if (t, state_in_S
                                ) not in states_dict[prefix].transitions[a[0]]:
                                states_dict[prefix].transitions[a[0]].append(
                                    (t, state_in_S))

        assert initial
        automaton = Onfsm(initial, [s for s in states_dict.values()])
        automaton.characterization_set = self.E

        return automaton
Example #2
0
def get_benchmark_ONFSM():
    """
    Returns ONFSM presented in 'Learning Finite State Models of Observable Nondeterministic Systems in a Testing
    Context'.
    """
    from aalpy.automata import Onfsm, OnfsmState

    a = OnfsmState('q0')
    b = OnfsmState('q1')
    c = OnfsmState('g2')
    d = OnfsmState('q3')

    a.transitions['a'].append((0, b))
    a.transitions['b'].append((2, a))
    a.transitions['b'].append((0, c))

    b.transitions['a'].append((2, a))
    b.transitions['b'].append((3, b))

    c.transitions['a'].append((2, d))
    c.transitions['b'].append((0, c))
    c.transitions['b'].append((3, c))

    d.transitions['a'].append((2, b))
    d.transitions['b'].append((3, d))

    return Onfsm(a, [a, b, c, d])
Example #3
0
def get_benchmark_ONFSM():
    """
    Returns ONFSM presented in 'Learning Finite State Models of Observable Nondeterministic Systems in a Testing
    Context'.
    """
    a = OnfsmState('a')
    b = OnfsmState('b')
    c = OnfsmState('c')
    d = OnfsmState('d')

    a.transitions['a'].append((0, b))
    a.transitions['b'].append((2, a))
    a.transitions['b'].append((0, c))

    b.transitions['a'].append((2, a))
    b.transitions['b'].append((3, b))

    c.transitions['a'].append((2, d))
    c.transitions['b'].append((0, c))
    c.transitions['b'].append((3, c))

    d.transitions['a'].append((2, b))
    d.transitions['b'].append((3, d))

    return Onfsm(a, [a, b, c, d])
Example #4
0
    def gen_hypothesis(self) -> Automaton:
        """
        Generate automaton based on the values found in the observation table.

        Returns:

            Current hypothesis

        """
        state_distinguish = dict()
        states_dict = dict()
        initial = None

        stateCounter = 0
        for prefix in self.S:
            state_id = f's{stateCounter}'
            states_dict[prefix] = OnfsmState(state_id)

            states_dict[prefix].prefix = prefix
            state_distinguish[self.row_to_hashable(
                prefix)] = states_dict[prefix]

            if prefix == self.S[0]:
                initial = states_dict[prefix]
            stateCounter += 1

        for prefix in self.S:
            curr_node = self.sul.pta.get_to_node(prefix[0], prefix[1])
            for a in self.A:
                trace = self.sul.pta.get_all_traces(curr_node, a)
                for t in trace:
                    reached_row = (prefix[0] + a, prefix[1] + (t[-1], ))
                    if self.row_to_hashable(
                            reached_row) not in state_distinguish.keys():
                        print('reeee')
                    state_in_S = state_distinguish[self.row_to_hashable(
                        reached_row)]
                    assert state_in_S  # shouldn't be necessary because of the if condition
                    states_dict[prefix].transitions[a[0]].append(
                        (t[-1], state_in_S))

        assert initial
        automaton = Onfsm(initial, [s for s in states_dict.values()])
        automaton.characterization_set = self.E

        return automaton
    def cex_processing(self, cex: tuple, hypothesis: Onfsm):
        """
        Add counterexample to the observation table. If the counterexample leads to a state where an output of the same equivalence class already exists, the prefixes of the counterexample are added to S.A. Otherwise, the postfixes of counterexample are added to E. 


        Args:

            cex: counterexample that should be added to the observation table
            hypothesis: onfsm that implements the counterexample
        """

        cex_len = len(cex[0])
        hypothesis.reset_to_initial()

        for step in range(0, cex_len - 1):
            hypothesis.step_to(cex[0][step], cex[1][step])

        possible_outputs = hypothesis.outputs_on_input(cex[0][cex_len - 1])

        equivalent_output = False

        for out in possible_outputs:
            abstracted_out = self.get_abstraction(out)
            abstracted_out_cex = self.get_abstraction(cex[1][cex_len - 1])
            if abstracted_out_cex == abstracted_out:
                equivalent_output = True
                break

        if equivalent_output:
            # add prefixes of cex to S_dot_A
            cex_prefixes = [(tuple(cex[0][0:i + 1]), tuple(cex[1][0:i + 1])) for i in range(0, len(cex[0]))]
            prefixes_to_extend = self.extend_S_dot_A(cex_prefixes)

            # CHANGED: REMOVED
            # self.observation_table.S_dot_A.extend(prefixes_to_extend)
            self.update_obs_table(s_set=prefixes_to_extend)
        else:
            # add distinguishing suffixes of cex to E
            # CHANGED CEX PROX
            # TODO: this will now not work as cex processing was changed
            # cex_suffixes = non_det_longest_prefix_cex_processing(self.observation_table, cex)
            cex_suffixes = all_suffixes(cex[0])

            added_suffixes = extend_set(self.observation_table.E, cex_suffixes)
            self.update_obs_table(e_set=added_suffixes)
Example #6
0
    def gen_hypothesis(self) -> Automaton:
        """
        Generate automaton based on the values found in the observation table.

        Returns:

            Current hypothesis

        """
        state_distinguish = dict()
        states_dict = dict()
        initial = None

        stateCounter = 0
        for prefix in self.S:
            state_id = f's{stateCounter}'
            states_dict[prefix] = OnfsmState(state_id)

            states_dict[prefix].prefix = prefix
            state_distinguish[self.row_to_hashable(
                prefix)] = states_dict[prefix]

            if prefix == self.S[0]:
                initial = states_dict[prefix]
            stateCounter += 1

        for prefix in self.S:
            for a in self.A:
                for t in self.T[prefix][a]:
                    state_in_S = state_distinguish[self.row_to_hashable(
                        (prefix[0] + a, prefix[1] + tuple([t])))]
                    assert state_in_S
                    states_dict[prefix].transitions[a[0]].append(
                        (t, state_in_S))

        assert initial
        automaton = Onfsm(initial, [s for s in states_dict.values()])
        automaton.characterization_set = self.E

        return automaton
Example #7
0
def generate_random_ONFSM(num_states,
                          num_inputs,
                          num_outputs,
                          multiple_out_prob=0.1):
    """
    Randomly generate an observable non-deterministic finite-state machine.

    Args:

      num_states: number of states
      num_inputs: number of inputs
      num_outputs: number of outputs
      multiple_out_prob: probability that state will have multiple outputs (Default value = 0.1)

    Returns:

        randomly generated ONFSM

    """
    inputs = [
        random_string_generator(random.randint(1, 3))
        for _ in range(num_inputs)
    ]
    outputs = [
        random_string_generator(random.randint(3, 7))
        for _ in range(num_outputs)
    ]

    states = []
    for i in range(num_states):
        state = OnfsmState(f's{i}')
        states.append(state)

    for state in states:
        for i in inputs:
            state_outputs = 1
            if random.random() <= multiple_out_prob and num_outputs > 1:
                state_outputs = random.randint(2, num_outputs)

            random_out = random.sample(outputs, state_outputs)
            for index in range(state_outputs):
                state.transitions[i].append(
                    (random_out[index], random.choice(states)))

    return Onfsm(states[0], states)
Example #8
0
def generate_random_ONFSM(num_states,
                          num_inputs,
                          num_outputs,
                          multiple_out_prob=0.1):
    """
    Randomly generate an observable non-deterministic finite-state machine.

    Args:

      num_states: number of states
      num_inputs: number of inputs
      num_outputs: number of outputs
      multiple_out_prob: probability that state will have multiple outputs (Default value = 0.5)

    Returns:

        randomly generated ONFSM

    """
    inputs = [f'i{i+1}' for i in range(num_inputs)]
    outputs = [f'o{i+1}' for i in range(num_outputs)]

    states = []
    for i in range(num_states):
        state = OnfsmState(f's{i}')
        states.append(state)

    state_buffer = states.copy()

    for state in states:
        for i in inputs:
            state_outputs = 1
            if random.random() <= multiple_out_prob and num_outputs >= 2:
                state_outputs = random.randint(2, num_outputs)

            random_out = random.sample(outputs, state_outputs)
            for index in range(state_outputs):
                if state_buffer:
                    new_state = random.choice(state_buffer)
                    state_buffer.remove(new_state)
                else:
                    new_state = random.choice(states)
                state.transitions[i].append((random_out[index], new_state))

    return Onfsm(states[0], states)
Example #9
0
def get_ONFSM():
    """
    Returns example of an ONFSM.
    """
    from aalpy.automata import Onfsm, OnfsmState

    q0 = OnfsmState('q0')
    q1 = OnfsmState('q1')
    q2 = OnfsmState('q2')
    q3 = OnfsmState('q3')
    q4 = OnfsmState('q4')
    q5 = OnfsmState('q5')
    q6 = OnfsmState('q6')
    q7 = OnfsmState('q7')
    q8 = OnfsmState('q8')

    q0.transitions['a'].append((2, q1))
    q0.transitions['b'].append((0, q0))

    q1.transitions['a'].append((2, q0))
    q1.transitions['b'].append((0, q2))

    q2.transitions['a'].append((1, q2))
    q2.transitions['b'].append((0, q3))

    q3.transitions['a'].append((2, q8))
    q3.transitions['b'].append((0, q4))

    q4.transitions['a'].append((1, q4))
    q4.transitions['b'].append((0, q5))

    q5.transitions['a'].append((2, q6))
    q5.transitions['b'].append((0, q7))

    q6.transitions['a'].append((2, q5))
    q6.transitions['b'].append((0, q6))

    q7.transitions['a'].append((1, q7))
    q7.transitions['b'].append(('O', q0))

    q8.transitions['a'].append((2, q3))
    q8.transitions['b'].append((0, q8))

    return Onfsm(q0, [q0, q1, q2, q3, q4, q5, q6, q7, q8])
Example #10
0
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