Verified Commit 2b457dd4 authored by Vladimír Štill's avatar Vladimír Štill
Browse files

CFL: Use iterative deepening for word generation in CFG, boost full comparison

parent 475edefb
Loading
Loading
Loading
Loading
Loading
+39 −28
Original line number Diff line number Diff line
@@ -529,7 +529,7 @@ class CFG:
            return word

        if full_cmp_cnt is None:
            full_cmp_cnt = pow(2, 12)
            full_cmp_cnt = pow(2, 16)

        if max_cmp_len is None:
            max_cmp_len = min(max(pow(2, len(left.nonterminals) + 1),
@@ -764,8 +764,10 @@ class WordGenerator:

    def __init__(self, cfg: CFG):
        self.cfg = cfg.cnf()
        self.seen: Set[CFG.Symbols] = set()
        self.queue: Deque[CFG.Symbols] = deque([(self.cfg.init,)])
        self._seen: Set[CFG.Symbols] = set()
        self._stack: List[CFG.Symbols] = [(self.cfg.init,)]
        self._cur_lenght = 1
        self._next_length: Optional[int] = 2
        self.last: Optional[CFG.Word] = None \
            if Eps() not in self.cfg.rules.get(self.cfg.init, []) \
            else ()
@@ -781,20 +783,21 @@ class WordGenerator:
        return self.last

    def _next(self) -> Optional[CFG.Word]:
        # Walk in BFS order so the the sentences are explored from shorter
        # to longer for CNF.
        # As we yield a word immediatelly on finding it between the sentences
        # (i.e., when we find a sentence with no nonterminals), we also yield
        # words from shorter to longer for CNF grammars (because a word of
        # length N needs exactly N + (N - 1) derivations in CNF and therefore
        # shorter words preceed longer once in BFS order.
        while self.queue:
            sentence = self.queue.popleft()
            if sentence in self.seen:
        # Iterative-Deepening DFS (as a generator)
        while self._stack or self._next_length is not None:
            while self._stack:
                sentence = self._stack.pop()
                if len(sentence) > self._cur_lenght:  # cutoff
                    self._next_length = len(sentence) \
                        if self._next_length is None \
                        else min(self._next_length, len(sentence))
                    continue
            self.seen.add(sentence)
                if sentence in self._seen:
                    continue
                self._seen.add(sentence)

                if CFG.all_terminal(sentence):
                    if len(sentence) == self._cur_lenght:
                        self.last = typing.cast(CFG.Word, sentence)
                        return self.last
                else:
@@ -805,6 +808,14 @@ class WordGenerator:
                                if isinstance(p, Eps):
                                    continue
                                new_sentence = sentence[:i] + p + sentence[i + 1:]
                            self.queue.append(new_sentence)
                                self._stack.append(new_sentence)
                            break  # it suffices to perform left derivations

            if self._next_length is not None \
                    and self._next_length > self._cur_lenght:
                self._cur_lenght = self._next_length
                self._next_length = None
                self._stack = [(self.cfg.init,)]
                self._seen = set()

        return None