Esempio n. 1
0
    def test_prefix_set_03(self):
        s = {'abcc', 'ccbad', 'ccab'}
        prefix_set = {
            '', 'a', 'c', 'ab', 'cc', 'cca', 'ccb', 'abc', 'ccab', 'ccba',
            'abcc', 'ccbad'
        }

        self.assertSetEqual(prefix_set, set(utils.prefix_set(s)))
Esempio n. 2
0
    def learn(self) -> automaton.DFA:
        """
        Learns the grammar from the sets of positive and negative
        example strings. This method returns a DFA that is
        consistent with the sample.

        :return: DFA
        :rtype: Automaton
        """
        self._logger.info('Start learning with alphabet = {}\n'
                          'positive samples = {}\n'
                          'negative samples = {}'.format(
                              self._alphabet, self._pos_examples,
                              self._neg_examples))

        self._logger.info('Building PTA')
        dfa = automaton.build_pta(self._pos_examples)

        pref_set = utils.prefix_set(self._pos_examples)
        self._blue = {
            automaton.State(i)
            for i in self._alphabet.intersection(pref_set)
        }

        while len(self._blue) != 0:
            qb = _choose(self._blue)
            self._blue.remove(qb)

            found = False
            for qr in sorted(self._red, key=functools.cmp_to_key(_cmp)):
                if self._compatible(self._merge(dfa.copy(), qr, qb)):
                    dfa = self._merge(dfa, qr, qb)
                    new_blue_states = set()
                    for q in self._red:
                        for a in self._alphabet:
                            if dfa.transition_exists(q, a) and \
                                    dfa.transition(q, a) not in self._red:
                                new_blue_states.add(dfa.transition(q, a))

                    self._blue.update(new_blue_states)
                    found = True

            if not found:
                dfa = self._promote(qb, dfa)

        for s in self._neg_examples:
            q, accepted = dfa.parse_string(s)
            if not accepted:
                dfa.reject_states.add(q)

        return dfa.remove_dead_states()
Esempio n. 3
0
def build_pta(s_plus: Set[str], s_minus: Set[str]=set()) -> DFA:
    """
    Function that builds a prefix tree acceptor from the example strings
    S = S+ union S-

    :param s_plus: Set containing positive examples of the target language
    :type s_plus: set
    :param s_minus: Set containing negative examples of the target language
    :type s_minus: set
    :return: An dfa representing a prefix tree acceptor
    :rtype: DFA
    """
    samples = s_plus.union(s_minus)

    alphabet = utils.determine_alphabet(samples)
    pta = DFA(alphabet)

    for letter in alphabet:
        pta.add_transition(State(''), State(letter), letter)

    states = {
        State(u) for u in utils.prefix_set(samples)
    }

    new_states = set()
    for u in states:
        for a in alphabet:
            ua = State(u.name + a)
            if ua not in states:
                new_states.add(ua)

            pta.add_transition(u, ua, a)

    states.update(new_states)

    for u in states:
        if u.name in s_plus:
            pta.accept_states.add(u)
        if u.name in s_minus:
            pta.reject_states.add(u)

    pta.states = states

    return pta
Esempio n. 4
0
    def _get_eq_membership_queries(self):
        ot = self._ot.copy()
        red = self._red.copy()
        blue = self._blue.copy()

        prefix_set = sorted(
            set(
                utils.prefix_set({self._user_info['counterExample']},
                                 self._alphabet)))

        for p in prefix_set:

            if p not in red:
                if p not in blue:
                    ot.add_row(p)
                red.add(p)
                blue.discard(p)

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

        queries = []
        for u, e in sorted(ot.find_holes()):
            ue = u + e
            if ue not in self._pos_examples:
                queries.append(ue)

        self._answer = {
            'stage': 'equivalenceMembershipQueries',
            'mq': queries,
            'blue': list(self._blue),
            'red': list(self._red),
            'alphabet': list(self._alphabet),
            'observationTable': self._ot.ot,
            'exp': list(self._ot.exp),
            'sta': list(self._ot.sta)
        }
Esempio n. 5
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
Esempio n. 6
0
    def _use_eq(self):
        """
        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.
        """
        query_answers = self._user_info['queryAnswers']
        prefix_set = sorted(
            set(
                utils.prefix_set({self._user_info['counterExample']},
                                 self._alphabet)))

        for p in prefix_set:

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

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

        for u, e in sorted(self._ot.find_holes()):
            ue = u + e
            if ue in self._pos_examples:
                self._ot.put(u, e, True)
            else:
                self._ot.put(u, e, query_answers[u + e])
Esempio n. 7
0
    def test_prefix_set_02(self):
        s = {'aaa', 'bbb', 'aba'}
        prefix_set = {'', 'a', 'b', 'aa', 'ab', 'bb', 'aaa', 'bbb', 'aba'}

        self.assertSetEqual(prefix_set, set(utils.prefix_set(s)))