Esempio n. 1
0
    def _close(self, ot: utils.ObservationTable) -> utils.ObservationTable:
        """
        Closes the observation table by adding an extra row.

        :param ot: The observation table to close.
        :type ot: ObservationTable
        :return: The closed and updated observation table
        :rtype: ObservationTable
        """
        self._logger.info('Closing the table by adding a row.')
        for s in self._blue.copy():
            if not all([ot.get_row(s) != ot.get_row(u) for u in self._red]):
                continue

            self._red.add(s)
            self._blue.remove(s)

            for a in self._alphabet:
                sa = s + a
                if sa not in self._blue:
                    self._blue.add(sa)
                    ot.add_row(sa)

            for u, e in ot.find_holes():
                ot.put(u, e, self._oracle.membership_query(u + e))

        return ot
Esempio n. 2
0
    def _build_automaton(self, ot: utils.ObservationTable) -> automaton.DFA:
        """
        Builds an automaton from the observation table.

        :param ot: The data to build the dfa from.
        :type ot: ObservationTable
        :return: The dfa built from the observation table.
        :rtype: DFA
        """
        self._logger.info('Building DFA from the table.')
        dfa = automaton.DFA(self._alphabet)

        for u in self._red:
            for v in ot.ot.keys():
                if u == v:
                    continue

                if len(v) < len(u) and ot.get_row(v) != ot.get_row(u):
                    dfa.states.add(automaton.State(u))

        for u in dfa.states:
            if ot.entry_exists(u.name, ''):
                if ot.get(u.name, '') == 1:
                    dfa.accept_states.add(u)
                elif ot.get(u.name, '') == 0:
                    dfa.reject_states.add(u)

            for a in self._alphabet:
                for w in dfa.states:
                    if ot.get_row(u.name + a) == ot.get_row(w.name):
                        dfa.add_transition(u, w, a)

        return dfa.rename_states()
Esempio n. 3
0
    def _consistent(self, ot: utils.ObservationTable) -> utils.ObservationTable:
        """
        Makes the observation table consistent by adding an extra
        column.

        :param ot: The observation table to make consistent.
        :type ot: ObservationTable
        :return: The consistent and updated observation table
        :rtype: ObservationTable
        """
        self._logger.info('Making the table consistent by adding a column.')
        s1, s2, a, e = self._find_inconsistent(ot)

        ae = a + e
        ot.exp.add(ae)
        ot.add_column_to_table(ae)

        for u, e in ot.find_holes():
            ot.put(u, e, self._oracle.membership_query(u + e))

        return ot
Esempio n. 4
0
    def _find_inconsistent(self, ot: utils.ObservationTable) -> Tuple[str, str, str, str]:
        """
        Tries to find two inconsistent rows s1 and s2 in the
        observation table. s1 and s2 are elements of red.
        OT[s1] == OT[s2] and OT[s1.a][e] != OT[s2.a][e] where
        a is an element in the alphabet and e is an
        experiment (element in the set ot.exp)

        :param ot: The observation table to find two inconsistent
                   red states.
        :type ot: ObservationTable
        :return: Inconsistent row
        :rtype: Tuple[str, str, str, str]
        """
        self._logger.info('Trying to find two inconsistent rows in the table.')
        for s1 in self._red:
            for s2 in self._red:
                if s1 == s2:
                    continue
                for a in self._alphabet:
                    for e in ot.exp:
                        if ot.get_row(s1) == ot.get_row(s2) and \
                            ot.entry_exists(s1 + a, e) and ot.entry_exists(s2 + a, e) \
                                and ot.get(s1 + a, e) != ot.get(s2 + a, e):
                            self._logger.info('Found two inconsistent rows {} and {}'
                                              .format(s1, s2))
                            return s1, s2, a, e

        self._logger.info('Did not find a inconsistency in the table.')
        return '', '', '', ''
Esempio n. 5
0
    def _build_automaton(self, ot: utils.ObservationTable) -> automaton.DFA:
        """
        Builds an automaton from the observation table.

        :type ot: ObservationTable
        :return: Automaton built from the observation table
        :rtype: Automaton
        """
        dfa = automaton.DFA(self._alphabet)

        states = {automaton.State(i) for i in self._red}

        we = utils.break_strings_in_two(self._red)
        for w, e in we:
            we = w + e
            if we in self._red and ot.entry_exists(w, e):
                val = ot.get(w, e)
                state = automaton.State(we)
                if val == 1:
                    dfa.accept_states.add(state)
                    states.add(state)
                elif val == 0:
                    dfa.reject_states.add(state)
                    states.add(state)

        for w in states:
            for a in self._alphabet:
                for u in self._red:
                    wa = w.name + a
                    if ot.row_exists(u) and ot.row_exists(wa) and \
                            ot.get_row(u) == ot.get_row(wa):
                        dfa.add_transition(w, automaton.State(u), a)

        return dfa
Esempio n. 6
0
    def _find_inconsistent(
            self, ot: utils.ObservationTable) -> Tuple[str, str, str, str]:
        """
        Tries to find two inconsistent rows s1 and s2 in the
        observation table. s1 and s2 are elements of red.
        OT[s1] == OT[s2] and OT[s1.a][e] != OT[s2.a][e] where
        a is an element in the alphabet and e is an
        experiment (element in the set ot.exp)

        :param ot: The observation table to find two inconsistent
                   red states.
        :type ot: ObservationTable
        :return: Inconsistent row
        :rtype: Tuple[str, str, str, str]
        """
        for s1 in sorted(self._red):
            for s2 in sorted(self._red):
                if s1 == s2:
                    continue
                for a in sorted(self._alphabet):
                    for e in sorted(ot.exp):
                        if ot.get_row(s1) == ot.get_row(s2) and \
                            ot.entry_exists(s1 + a, e) and ot.entry_exists(s2 + a, e) \
                                and ot.get(s1 + a, e) != ot.get(s2 + a, e):
                            return s1, s2, a, e
        return '', '', '', ''
Esempio n. 7
0
    def _useq(self, ot: utils.ObservationTable, answer: str) -> utils.ObservationTable:
        """
        This method is called when the table is closed and complete.
        The algorithm then makes an equivalence query to the oracle,
        if the oracle is not satisfied and provides us with a
        counterexample, then this method is called with that counterexample.
        The method adds new rows to the observation table, to account
        for the new counterexample.

        :param ot: The observation table to update
        :type ot: ObservationTable
        :param answer: The counter-example given by the oracle
        :type answer: str
        :return: Updated ObservationTable
        :rtype: ObservationTable
        """
        prefix_set = set(utils.prefix_set({answer}, self._alphabet))
        self._logger.info('Updating table by adding the following prefixes: {}'
                          .format(', '.join(prefix_set)))

        for p in prefix_set:

            if p not in self._red:
                if p not in self._blue:
                    ot.add_row(p)
                self._red.add(p)
                self._blue.discard(p)

            for a in self._alphabet:
                pa = p + a
                if pa not in prefix_set:
                    if pa not in self._blue:
                        if pa not in self._red:
                            ot.add_row(pa)
                        self._blue.add(pa)
                        self._red.discard(pa)

        for u, e in ot.find_holes():
            ot.put(u, e, self._oracle.membership_query(u + e))

        return ot
 def test_ot_01(self):
     ot = ObservationTable(set(), {''}, set())
     ot.put('', '', 1)
     self.assertEqual(1, ot.get('', ''))
     self.assertEqual({'': 1}, ot.get_row(''))
 def test_ot_03(self):
     ot = ObservationTable(set(), {''}, set())
     ot.add_row('a')
     self.assertEqual({}, ot.get_row('a'))
     ot.put('a', 'a', 1)
     ot.put('a', 'b', 0)
     self.assertEqual(0, ot.get('a', 'b'))
     self.assertEqual(True, ot.row_exists('a'))
     self.assertEqual(False, ot.row_exists('b'))
     try:
         ot.get('a', 'c')
         self.fail('Indexing row and column that'
                   ' is not defined should throw an exception!')
     except KeyError:
         self.assertTrue(True)
Esempio n. 10
0
 def test_ot_02(self):
     ot = ObservationTable(set(), {''}, set())
     ot.add_row('a')
     self.assertEqual({}, ot.get_row('a'))
     ot.put('a', 'b', 0)
     self.assertEqual(0, ot.get('a', 'b'))
Esempio n. 11
0
    def _fill_holes(
            self,
            ot: utils.ObservationTable) -> Tuple[utils.ObservationTable, bool]:
        """
        Tries to make the table complete by filling in all the entries that
        are None.

        :param ot: the updated observation table
        :return: updated ObservationTable and whether the method was successful.
        :rtype: tuple(ObservationTable, bool)
        """
        self._logger.info(
            'Try to make table complete by filling in * entries.')
        for p in self._blue:
            r = ot.find_compatible_row(p)
            if r is not None:
                for e in ot.exp:
                    if ot.entry_exists(p, e) and ot.get(p, e) is not None:
                        ot.put(r, e, ot.get(p, e))
            else:
                return ot, True

        for r in self._red:
            for e in ot.exp:
                if ot.entry_exists(r, e) and ot.get(r, e) is None:
                    ot.put(r, e, 1)

        for p in self._blue:
            r = ot.find_compatible_row(p)
            if r is not None:
                for e in ot.exp:
                    if ot.entry_exists(p, e) and ot.get(p, e) is None:
                        if ot.entry_exists(r, e):
                            ot.put(p, e, ot.get(r, e))
            else:
                return ot, True

        return ot, False