For a given action, returns at most ``max_num_paths`` paths to the root (production with
        ``START_SYMBOL``) that are not longer than ``max_path_length``.

def get_paths_to_root(self,
                          action: str,
                          max_path_length: int = 20,
                          beam_size: int = 30,
                          max_num_paths: int = 10) -> List[List[str]]:
        """
        For a given action, returns at most ``max_num_paths`` paths to the root (production with
        ``START_SYMBOL``) that are not longer than ``max_path_length``.
        """
        action_left_side, _ = action.split(' -> ')
        right_side_indexed_actions = self._get_right_side_indexed_actions()
        lists_to_expand: List[Tuple[str, List[str]]] = [(action_left_side, [action])]
        completed_paths = []
        while lists_to_expand:
            need_to_expand = False
            for left_side, path in lists_to_expand:
                if left_side == types.START_SYMBOL:
                    completed_paths.append(path)
                else:
                    need_to_expand = True
            if not need_to_expand or len(completed_paths) >= max_num_paths:
                break
            # We keep track of finished and unfinished lists separately because we truncate the beam
            # later, and we want the finished lists to be at the top of the beam.
            finished_new_lists = []
            unfinished_new_lists = []
            for left_side, actions in lists_to_expand:
                for next_left_side, next_action in right_side_indexed_actions[left_side]:
                    if next_action in actions:
                        # Ignoring paths with loops (of size 1)
                        continue
                    new_actions = list(actions)
                    new_actions.append(next_action)
                    # Ignoring lists that are too long, and have too many repetitions.
                    path_length = len(new_actions)
                    if path_length <= max_path_length or next_left_side == types.START_SYMBOL:
                        if next_left_side == types.START_SYMBOL:
                            finished_new_lists.append((next_left_side, new_actions))
                        else:
                            unfinished_new_lists.append((next_left_side, new_actions))
            new_lists = finished_new_lists + unfinished_new_lists
            lists_to_expand = new_lists[:beam_size]
        return completed_paths[:max_num_paths]