1 // See www.openfst.org for extensive documentation on this weighted
2 // finite-state transducer library.
4 // Functions and classes for various FST state queues with a unified interface.
11 #include <type_traits>
17 #include <fst/arcfilter.h>
18 #include <fst/connect.h>
20 #include <fst/topsort.h>
25 // The Queue interface is:
32 // // Constructor: may need args (e.g., FST, comparator) for some queues.
33 // Queue(...) override;
35 // // Returns the head of the queue.
36 // StateId Head() const override;
38 // // Inserts a state.
39 // void Enqueue(StateId s) override;
41 // // Removes the head of the queue.
42 // void Dequeue() override;
44 // // Updates ordering of state s when weight changes, if necessary.
45 // void Update(StateId s) override;
47 // // Is the queue empty?
48 // bool Empty() const override;
50 // // Removes all states from the queue.
51 // void Clear() override;
56 TRIVIAL_QUEUE = 0, // Single state queue.
57 FIFO_QUEUE = 1, // First-in, first-out queue.
58 LIFO_QUEUE = 2, // Last-in, first-out queue.
59 SHORTEST_FIRST_QUEUE = 3, // Shortest-first queue.
60 TOP_ORDER_QUEUE = 4, // Topologically-ordered queue.
61 STATE_ORDER_QUEUE = 5, // State ID-ordered queue.
62 SCC_QUEUE = 6, // Component graph top-ordered meta-queue.
63 AUTO_QUEUE = 7, // Auto-selected queue.
67 // QueueBase, templated on the StateId, is a virtual base class shared by all
68 // queues considered by AutoQueue.
74 virtual ~QueueBase() {}
76 // Concrete implementation.
78 explicit QueueBase(QueueType type) : queue_type_(type), error_(false) {}
80 void SetError(bool error) { error_ = error; }
82 bool Error() const { return error_; }
84 QueueType Type() const { return queue_type_; }
88 virtual StateId Head() const = 0;
89 virtual void Enqueue(StateId) = 0;
90 virtual void Dequeue() = 0;
91 virtual void Update(StateId) = 0;
92 virtual bool Empty() const = 0;
93 virtual void Clear() = 0;
96 QueueType queue_type_;
100 // Trivial queue discipline; one may enqueue at most one state at a time. It
101 // can be used for strongly connected components with only one state and no
104 class TrivialQueue : public QueueBase<S> {
108 TrivialQueue() : QueueBase<StateId>(TRIVIAL_QUEUE), front_(kNoStateId) {}
110 virtual ~TrivialQueue() = default;
112 StateId Head() const final { return front_; }
114 void Enqueue(StateId s) final { front_ = s; }
116 void Dequeue() final { front_ = kNoStateId; }
118 void Update(StateId) final {}
120 bool Empty() const final { return front_ == kNoStateId; }
122 void Clear() final { front_ = kNoStateId; }
128 // First-in, first-out queue discipline.
130 // This is not a final class.
132 class FifoQueue : public QueueBase<S> {
136 FifoQueue() : QueueBase<StateId>(FIFO_QUEUE) {}
138 virtual ~FifoQueue() = default;
140 StateId Head() const override { return queue_.back(); }
142 void Enqueue(StateId s) override { queue_.push_front(s); }
144 void Dequeue() override { queue_.pop_back(); }
146 void Update(StateId) override {}
148 bool Empty() const override { return queue_.empty(); }
150 void Clear() override { queue_.clear(); }
153 std::deque<StateId> queue_;
156 // Last-in, first-out queue discipline.
158 class LifoQueue : public QueueBase<S> {
162 LifoQueue() : QueueBase<StateId>(LIFO_QUEUE) {}
164 virtual ~LifoQueue() = default;
166 StateId Head() const final { return queue_.front(); }
168 void Enqueue(StateId s) final { queue_.push_front(s); }
170 void Dequeue() final { queue_.pop_front(); }
172 void Update(StateId) final {}
174 bool Empty() const final { return queue_.empty(); }
176 void Clear() final { queue_.clear(); }
179 std::deque<StateId> queue_;
182 // Shortest-first queue discipline, templated on the StateId and as well as a
183 // comparison functor used to compare two StateIds. If a (single) state's order
184 // changes, it can be reordered in the queue with a call to Update(). If update
185 // is false, call to Update() does not reorder the queue.
187 // This is not a final class.
188 template <typename S, typename Compare, bool update = true>
189 class ShortestFirstQueue : public QueueBase<S> {
193 explicit ShortestFirstQueue(Compare comp)
194 : QueueBase<StateId>(SHORTEST_FIRST_QUEUE), heap_(comp) {}
196 virtual ~ShortestFirstQueue() = default;
198 StateId Head() const override { return heap_.Top(); }
200 void Enqueue(StateId s) override {
202 for (StateId i = key_.size(); i <= s; ++i) key_.push_back(kNoStateId);
203 key_[s] = heap_.Insert(s);
209 void Dequeue() override {
211 key_[heap_.Pop()] = kNoStateId;
217 void Update(StateId s) override {
219 if (s >= key_.size() || key_[s] == kNoStateId) {
222 heap_.Update(key_[s], s);
226 bool Empty() const override { return heap_.Empty(); }
228 void Clear() override {
230 if (update) key_.clear();
234 Heap<StateId, Compare> heap_;
235 std::vector<ssize_t> key_;
240 // Given a vector that maps from states to weights, and a comparison functor
241 // for weights, this class defines a comparison function object between states.
242 template <typename StateId, typename Less>
243 class StateWeightCompare {
245 using Weight = typename Less::Weight;
247 StateWeightCompare(const std::vector<Weight> &weights, const Less &less)
248 : weights_(weights), less_(less) {}
250 bool operator()(const StateId s1, const StateId s2) const {
251 return less_(weights_[s1], weights_[s2]);
255 // Borrowed references.
256 const std::vector<Weight> &weights_;
260 } // namespace internal
262 // Shortest-first queue discipline, templated on the StateId and Weight, is
263 // specialized to use the weight's natural order for the comparison function.
264 template <typename S, typename Weight>
265 class NaturalShortestFirstQueue final
266 : public ShortestFirstQueue<
267 S, internal::StateWeightCompare<S, NaturalLess<Weight>>> {
270 using Compare = internal::StateWeightCompare<StateId, NaturalLess<Weight>>;
272 explicit NaturalShortestFirstQueue(const std::vector<Weight> &distance)
273 : ShortestFirstQueue<StateId, Compare>(Compare(distance, less_)) {}
275 virtual ~NaturalShortestFirstQueue() = default;
278 // This is non-static because the constructor for non-idempotent weights will
279 // result in a an error.
280 const NaturalLess<Weight> less_{};
283 // Topological-order queue discipline, templated on the StateId. States are
284 // ordered in the queue topologically. The FST must be acyclic.
286 class TopOrderQueue : public QueueBase<S> {
290 // This constructor computes the topological order. It accepts an arc filter
291 // to limit the transitions considered in that computation (e.g., only the
293 template <class Arc, class ArcFilter>
294 TopOrderQueue(const Fst<Arc> &fst, ArcFilter filter)
295 : QueueBase<StateId>(TOP_ORDER_QUEUE),
301 TopOrderVisitor<Arc> top_order_visitor(&order_, &acyclic);
302 DfsVisit(fst, &top_order_visitor, filter);
304 FSTERROR() << "TopOrderQueue: FST is not acyclic";
305 QueueBase<S>::SetError(true);
307 state_.resize(order_.size(), kNoStateId);
310 // This constructor is passed the pre-computed topological order.
311 explicit TopOrderQueue(const std::vector<StateId> &order)
312 : QueueBase<StateId>(TOP_ORDER_QUEUE),
316 state_(order.size(), kNoStateId) {}
318 virtual ~TopOrderQueue() = default;
320 StateId Head() const final { return state_[front_]; }
322 void Enqueue(StateId s) final {
323 if (front_ > back_) {
324 front_ = back_ = order_[s];
325 } else if (order_[s] > back_) {
327 } else if (order_[s] < front_) {
330 state_[order_[s]] = s;
333 void Dequeue() final {
334 state_[front_] = kNoStateId;
335 while ((front_ <= back_) && (state_[front_] == kNoStateId)) ++front_;
338 void Update(StateId) final {}
340 bool Empty() const final { return front_ > back_; }
343 for (StateId s = front_; s <= back_; ++s) state_[s] = kNoStateId;
351 std::vector<StateId> order_;
352 std::vector<StateId> state_;
355 // State order queue discipline, templated on the StateId. States are ordered in
356 // the queue by state ID.
358 class StateOrderQueue : public QueueBase<S> {
363 : QueueBase<StateId>(STATE_ORDER_QUEUE), front_(0), back_(kNoStateId) {}
365 virtual ~StateOrderQueue() = default;
367 StateId Head() const final { return front_; }
369 void Enqueue(StateId s) final {
370 if (front_ > back_) {
372 } else if (s > back_) {
374 } else if (s < front_) {
377 while (enqueued_.size() <= s) enqueued_.push_back(false);
381 void Dequeue() final {
382 enqueued_[front_] = false;
383 while ((front_ <= back_) && (enqueued_[front_] == false)) ++front_;
386 void Update(StateId) final {}
388 bool Empty() const final { return front_ > back_; }
391 for (StateId i = front_; i <= back_; ++i) enqueued_[i] = false;
399 std::vector<bool> enqueued_;
402 // SCC topological-order meta-queue discipline, templated on the StateId and a
403 // queue used inside each SCC. It visits the SCCs of an FST in topological
404 // order. Its constructor is passed the queues to to use within an SCC.
405 template <class S, class Queue>
406 class SccQueue : public QueueBase<S> {
410 // Constructor takes a vector specifying the SCC number per state and a
411 // vector giving the queue to use per SCC number.
412 SccQueue(const std::vector<StateId> &scc,
413 std::vector<std::unique_ptr<Queue>> *queue)
414 : QueueBase<StateId>(SCC_QUEUE),
420 virtual ~SccQueue() = default;
422 StateId Head() const final {
423 while ((front_ <= back_) &&
424 (((*queue_)[front_] && (*queue_)[front_]->Empty()) ||
425 (((*queue_)[front_] == nullptr) &&
426 ((front_ >= trivial_queue_.size()) ||
427 (trivial_queue_[front_] == kNoStateId))))) {
430 if ((*queue_)[front_]) {
431 return (*queue_)[front_]->Head();
433 return trivial_queue_[front_];
437 void Enqueue(StateId s) final {
438 if (front_ > back_) {
439 front_ = back_ = scc_[s];
440 } else if (scc_[s] > back_) {
442 } else if (scc_[s] < front_) {
445 if ((*queue_)[scc_[s]]) {
446 (*queue_)[scc_[s]]->Enqueue(s);
448 while (trivial_queue_.size() <= scc_[s]) {
449 trivial_queue_.push_back(kNoStateId);
451 trivial_queue_[scc_[s]] = s;
455 void Dequeue() final {
456 if ((*queue_)[front_]) {
457 (*queue_)[front_]->Dequeue();
458 } else if (front_ < trivial_queue_.size()) {
459 trivial_queue_[front_] = kNoStateId;
463 void Update(StateId s) final {
464 if ((*queue_)[scc_[s]]) (*queue_)[scc_[s]]->Update(s);
467 bool Empty() const final {
468 // Queues SCC number back_ is not empty unless back_ == front_.
469 if (front_ < back_) {
471 } else if (front_ > back_) {
473 } else if ((*queue_)[front_]) {
474 return (*queue_)[front_]->Empty();
476 return (front_ >= trivial_queue_.size()) ||
477 (trivial_queue_[front_] == kNoStateId);
482 for (StateId i = front_; i <= back_; ++i) {
484 (*queue_)[i]->Clear();
485 } else if (i < trivial_queue_.size()) {
486 trivial_queue_[i] = kNoStateId;
494 std::vector<std::unique_ptr<Queue>> *queue_;
495 const std::vector<StateId> &scc_;
496 mutable StateId front_;
498 std::vector<StateId> trivial_queue_;
501 // Automatic queue discipline. It selects a queue discipline for a given FST
502 // based on its properties.
504 class AutoQueue : public QueueBase<S> {
508 // This constructor takes a state distance vector that, if non-null and if
509 // the Weight type has the path property, will entertain the shortest-first
510 // queue using the natural order w.r.t to the distance.
511 template <class Arc, class ArcFilter>
512 AutoQueue(const Fst<Arc> &fst,
513 const std::vector<typename Arc::Weight> *distance, ArcFilter filter)
514 : QueueBase<StateId>(AUTO_QUEUE) {
515 using Weight = typename Arc::Weight;
516 // TrivialLess is never instantiated since the construction of Less is
517 // guarded by Properties() & kPath. It is only here to avoid instantiating
518 // NaturalLess for non-path weights.
520 using Weight = typename Arc::Weight;
521 bool operator()(const Weight &, const Weight &) const { return false; }
524 typename std::conditional<(Weight::Properties() & kPath) == kPath,
525 NaturalLess<Weight>, TrivialLess>::type;
526 using Compare = internal::StateWeightCompare<StateId, Less>;
527 // First checks if the FST is known to have these properties.
529 fst.Properties(kAcyclic | kCyclic | kTopSorted | kUnweighted, false);
530 if ((props & kTopSorted) || fst.Start() == kNoStateId) {
531 queue_.reset(new StateOrderQueue<StateId>());
532 VLOG(2) << "AutoQueue: using state-order discipline";
533 } else if (props & kAcyclic) {
534 queue_.reset(new TopOrderQueue<StateId>(fst, filter));
535 VLOG(2) << "AutoQueue: using top-order discipline";
536 } else if ((props & kUnweighted) && (Weight::Properties() & kIdempotent)) {
537 queue_.reset(new LifoQueue<StateId>());
538 VLOG(2) << "AutoQueue: using LIFO discipline";
541 // Decomposes into strongly-connected components.
542 SccVisitor<Arc> scc_visitor(&scc_, nullptr, nullptr, &properties);
543 DfsVisit(fst, &scc_visitor, filter);
544 auto nscc = *std::max_element(scc_.begin(), scc_.end()) + 1;
545 std::vector<QueueType> queue_types(nscc);
546 std::unique_ptr<Less> less;
547 std::unique_ptr<Compare> comp;
548 if (distance && (Weight::Properties() & kPath)) {
549 less.reset(new Less);
550 comp.reset(new Compare(*distance, *less));
552 // Finds the queue type to use per SCC.
555 SccQueueType(fst, scc_, &queue_types, filter, less.get(), &all_trivial,
557 // If unweighted and semiring is idempotent, uses LIFO queue.
559 queue_.reset(new LifoQueue<StateId>());
560 VLOG(2) << "AutoQueue: using LIFO discipline";
563 // If all the SCC are trivial, the FST is acyclic and the scc number gives
564 // the topological order.
566 queue_.reset(new TopOrderQueue<StateId>(scc_));
567 VLOG(2) << "AutoQueue: using top-order discipline";
570 VLOG(2) << "AutoQueue: using SCC meta-discipline";
571 queues_.resize(nscc);
572 for (StateId i = 0; i < nscc; ++i) {
573 switch (queue_types[i]) {
576 VLOG(3) << "AutoQueue: SCC #" << i << ": using trivial discipline";
578 case SHORTEST_FIRST_QUEUE:
580 new ShortestFirstQueue<StateId, Compare, false>(*comp));
581 VLOG(3) << "AutoQueue: SCC #" << i
582 << ": using shortest-first discipline";
585 queues_[i].reset(new LifoQueue<StateId>());
586 VLOG(3) << "AutoQueue: SCC #" << i << ": using LIFO discipline";
590 queues_[i].reset(new FifoQueue<StateId>());
591 VLOG(3) << "AutoQueue: SCC #" << i << ": using FIFO discipine";
595 queue_.reset(new SccQueue<StateId, QueueBase<StateId>>(scc_, &queues_));
599 virtual ~AutoQueue() = default;
601 StateId Head() const final { return queue_->Head(); }
603 void Enqueue(StateId s) final { queue_->Enqueue(s); }
605 void Dequeue() final { queue_->Dequeue(); }
607 void Update(StateId s) final { queue_->Update(s); }
609 bool Empty() const final { return queue_->Empty(); }
611 void Clear() final { queue_->Clear(); }
614 template <class Arc, class ArcFilter, class Less>
615 static void SccQueueType(const Fst<Arc> &fst, const std::vector<StateId> &scc,
616 std::vector<QueueType> *queue_types,
617 ArcFilter filter, Less *less, bool *all_trivial,
620 std::unique_ptr<QueueBase<StateId>> queue_;
621 std::vector<std::unique_ptr<QueueBase<StateId>>> queues_;
622 std::vector<StateId> scc_;
625 // Examines the states in an FST's strongly connected components and determines
626 // which type of queue to use per SCC. Stores result as a vector of QueueTypes
627 // which is assumed to have length equal to the number of SCCs. An arc filter
628 // is used to limit the transitions considered (e.g., only the epsilon graph).
629 // The argument all_trivial is set to true if every queue is the trivial queue.
630 // The argument unweighted is set to true if the semiring is idempotent and all
631 // the arc weights are equal to Zero() or One().
632 template <class StateId>
633 template <class Arc, class ArcFilter, class Less>
634 void AutoQueue<StateId>::SccQueueType(const Fst<Arc> &fst,
635 const std::vector<StateId> &scc,
636 std::vector<QueueType> *queue_type,
637 ArcFilter filter, Less *less,
638 bool *all_trivial, bool *unweighted) {
639 using StateId = typename Arc::StateId;
640 using Weight = typename Arc::Weight;
643 for (StateId i = 0; i < queue_type->size(); ++i) {
644 (*queue_type)[i] = TRIVIAL_QUEUE;
646 for (StateIterator<Fst<Arc>> sit(fst); !sit.Done(); sit.Next()) {
647 const auto state = sit.Value();
648 for (ArcIterator<Fst<Arc>> ait(fst, state); !ait.Done(); ait.Next()) {
649 const auto &arc = ait.Value();
650 if (!filter(arc)) continue;
651 if (scc[state] == scc[arc.nextstate]) {
652 auto &type = (*queue_type)[scc[state]];
653 if (!less || ((*less)(arc.weight, Weight::One()))) {
655 } else if ((type == TRIVIAL_QUEUE) || (type == LIFO_QUEUE)) {
656 if (!(Weight::Properties() & kIdempotent) ||
657 (arc.weight != Weight::Zero() && arc.weight != Weight::One())) {
658 type = SHORTEST_FIRST_QUEUE;
663 if (type != TRIVIAL_QUEUE) *all_trivial = false;
665 if (!(Weight::Properties() & kIdempotent) ||
666 (arc.weight != Weight::Zero() && arc.weight != Weight::One())) {
673 // An A* estimate is a function object that maps from a state ID to a an
674 // estimate of the shortest distance to the final states.
676 // A trivial A* estimate, yielding a queue which behaves the same in Dijkstra's
678 template <typename StateId, typename Weight>
679 struct TrivialAStarEstimate {
680 const Weight &operator()(StateId) const { return Weight::One(); }
683 // A non-trivial A* estimate using a vector of the estimated future costs.
684 template <typename StateId, typename Weight>
685 class NaturalAStarEstimate {
687 NaturalAStarEstimate(const std::vector<Weight> &beta) :
690 const Weight &operator()(StateId s) const { return beta_[s]; }
693 const std::vector<Weight> &beta_;
696 // Given a vector that maps from states to weights representing the shortest
697 // distance from the initial state, a comparison function object between
698 // weights, and an estimate of the shortest distance to the final states, this
699 // class defines a comparison function object between states.
700 template <typename S, typename Less, typename Estimate>
701 class AStarWeightCompare {
704 using Weight = typename Less::Weight;
706 AStarWeightCompare(const std::vector<Weight> &weights, const Less &less,
707 const Estimate &estimate)
708 : weights_(weights), less_(less), estimate_(estimate) {}
710 bool operator()(StateId s1, StateId s2) const {
711 const auto w1 = Times(weights_[s1], estimate_(s1));
712 const auto w2 = Times(weights_[s2], estimate_(s2));
713 return less_(w1, w2);
717 const std::vector<Weight> &weights_;
719 const Estimate &estimate_;
722 // A* queue discipline templated on StateId, Weight, and Estimate.
723 template <typename S, typename Weight, typename Estimate>
724 class NaturalAStarQueue : public ShortestFirstQueue<
725 S, AStarWeightCompare<S, NaturalLess<Weight>, Estimate>> {
728 using Compare = AStarWeightCompare<StateId, NaturalLess<Weight>, Estimate>;
730 NaturalAStarQueue(const std::vector<Weight> &distance,
731 const Estimate &estimate)
732 : ShortestFirstQueue<StateId, Compare>(
733 Compare(distance, less_, estimate)) {}
735 ~NaturalAStarQueue() = default;
738 // This is non-static because the constructor for non-idempotent weights will
739 // result in a an error.
740 const NaturalLess<Weight> less_{};
743 // A state equivalence class is a function object that maps from a state ID to
744 // an equivalence class (state) ID. The trivial equivalence class maps a state
746 template <typename StateId>
747 struct TrivialStateEquivClass {
748 StateId operator()(StateId s) const { return s; }
751 // Distance-based pruning queue discipline: Enqueues a state only when its
752 // shortest distance (so far), as specified by distance, is less than (as
753 // specified by comp) the shortest distance Times() the threshold to any state
754 // in the same equivalence class, as specified by the functor class_func. The
755 // underlying queue discipline is specified by queue. The ownership of queue is
756 // given to this class.
758 // This is not a final class.
759 template <typename Queue, typename Less, typename ClassFnc>
760 class PruneQueue : public QueueBase<typename Queue::StateId> {
762 using StateId = typename Queue::StateId;
763 using Weight = typename Less::Weight;
765 PruneQueue(const std::vector<Weight> &distance, Queue *queue,
766 const Less &less, const ClassFnc &class_fnc, Weight threshold)
767 : QueueBase<StateId>(OTHER_QUEUE),
771 class_fnc_(class_fnc),
772 threshold_(std::move(threshold)) {}
774 virtual ~PruneQueue() = default;
776 StateId Head() const override { return queue_->Head(); }
778 void Enqueue(StateId s) override {
779 const auto c = class_fnc_(s);
780 if (c >= class_distance_.size()) {
781 class_distance_.resize(c + 1, Weight::Zero());
783 if (less_(distance_[s], class_distance_[c])) {
784 class_distance_[c] = distance_[s];
786 // Enqueues only if below threshold limit.
787 const auto limit = Times(class_distance_[c], threshold_);
788 if (less_(distance_[s], limit)) queue_->Enqueue(s);
791 void Dequeue() override { queue_->Dequeue(); }
793 void Update(StateId s) override {
794 const auto c = class_fnc_(s);
795 if (less_(distance_[s], class_distance_[c])) {
796 class_distance_[c] = distance_[s];
801 bool Empty() const override { return queue_->Empty(); }
803 void Clear() override { queue_->Clear(); }
806 const std::vector<Weight> &distance_; // Shortest distance to state.
807 std::unique_ptr<Queue> queue_;
808 const Less &less_; // Borrowed reference.
809 const ClassFnc &class_fnc_; // Equivalence class functor.
810 Weight threshold_; // Pruning weight threshold.
811 std::vector<Weight> class_distance_; // Shortest distance to class.
814 // Pruning queue discipline (see above) using the weight's natural order for the
815 // comparison function. The ownership of the queue argument is given to this
817 template <typename Queue, typename Weight, typename ClassFnc>
818 class NaturalPruneQueue final
819 : public PruneQueue<Queue, NaturalLess<Weight>, ClassFnc> {
821 using StateId = typename Queue::StateId;
823 NaturalPruneQueue(const std::vector<Weight> &distance, Queue *queue,
824 const ClassFnc &class_fnc, Weight threshold)
825 : PruneQueue<Queue, NaturalLess<Weight>, ClassFnc>(
826 distance, queue, NaturalLess<Weight>(), class_fnc, threshold) {}
828 virtual ~NaturalPruneQueue() = default;
831 // Filter-based pruning queue discipline: enqueues a state only if allowed by
832 // the filter, specified by the state filter functor argument. The underlying
833 // queue discipline is specified by the queue argument. The ownership of the
834 // queue is given to this class.
835 template <typename Queue, typename Filter>
836 class FilterQueue : public QueueBase<typename Queue::StateId> {
838 using StateId = typename Queue::StateId;
840 FilterQueue(Queue *queue, const Filter &filter)
841 : QueueBase<StateId>(OTHER_QUEUE), queue_(queue), filter_(filter) {}
843 virtual ~FilterQueue() = default;
845 StateId Head() const final { return queue_->Head(); }
847 // Enqueues only if allowed by state filter.
848 void Enqueue(StateId s) final {
849 if (filter_(s)) queue_->Enqueue(s);
852 void Dequeue() final { queue_->Dequeue(); }
854 void Update(StateId s) final {}
856 bool Empty() const final { return queue_->Empty(); }
858 void Clear() final { queue_->Clear(); }
861 std::unique_ptr<Queue> queue_;
862 const Filter &filter_;
867 #endif // FST_QUEUE_H_