|
// Copyright 2005-2024 Google LLC
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the 'License');
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an 'AS IS' BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
//
|
|
// See www.openfst.org for extensive documentation on this weighted
|
|
// finite-state transducer library.
|
|
//
|
|
// Composition filters to support lookahead matchers, useful for improving
|
|
// composition efficiency with certain inputs.
|
|
|
|
#ifndef FST_LOOKAHEAD_FILTER_H_
|
|
#define FST_LOOKAHEAD_FILTER_H_
|
|
|
|
#include <sys/types.h>
|
|
|
|
#include <cstdint>
|
|
#include <memory>
|
|
#include <vector>
|
|
|
|
#include <fst/log.h>
|
|
#include <fst/arc.h>
|
|
#include <fst/filter-state.h>
|
|
#include <fst/fst-decl.h>
|
|
#include <fst/fst.h>
|
|
#include <fst/lookahead-matcher.h>
|
|
#include <fst/matcher.h>
|
|
#include <fst/properties.h>
|
|
#include <fst/util.h>
|
|
|
|
namespace fst {
|
|
|
|
// Identifies and verifies the capabilities of the matcher to be used for
|
|
// lookahead with the composition filters below. This version is passed two
|
|
// matchers.
|
|
template <class Matcher1, class Matcher2>
|
|
MatchType LookAheadMatchType(const Matcher1 &m1, const Matcher2 &m2) {
|
|
const auto type1 = m1.Type(false);
|
|
const auto type2 = m2.Type(false);
|
|
if (type1 == MATCH_OUTPUT && m1.Flags() & kOutputLookAheadMatcher) {
|
|
return MATCH_OUTPUT;
|
|
} else if (type2 == MATCH_INPUT && m2.Flags() & kInputLookAheadMatcher) {
|
|
return MATCH_INPUT;
|
|
} else if (m1.Flags() & kOutputLookAheadMatcher &&
|
|
m1.Type(true) == MATCH_OUTPUT) {
|
|
return MATCH_OUTPUT;
|
|
} else if (m2.Flags() & kInputLookAheadMatcher &&
|
|
m2.Type(true) == MATCH_INPUT) {
|
|
return MATCH_INPUT;
|
|
} else {
|
|
return MATCH_NONE;
|
|
}
|
|
}
|
|
|
|
// Identifies and verifies the capabilities of the matcher to be used for
|
|
// lookahead with the composition filters below. This version uses the FST's
|
|
// default matchers.
|
|
template <class Arc>
|
|
MatchType LookAheadMatchType(const Fst<Arc> &fst1, const Fst<Arc> &fst2) {
|
|
LookAheadMatcher<Fst<Arc>> matcher1(fst1, MATCH_OUTPUT);
|
|
LookAheadMatcher<Fst<Arc>> matcher2(fst2, MATCH_INPUT);
|
|
return LookAheadMatchType(matcher1, matcher2);
|
|
}
|
|
|
|
// LookAheadSelector is a helper class for selecting among possibly distinct
|
|
// FST and matcher types without using a common base class. This lets us avoid
|
|
// virtual function calls. It stores and returns the appropriate FSTs and
|
|
// matcher for lookahead. It is templated on the matcher types. General case
|
|
// has no methods.
|
|
template <class Matcher1, class Matcher2, MatchType MT>
|
|
class LookAheadSelector {};
|
|
|
|
// Stores and returns the appropriate FST and matcher for lookahead. Specialized
|
|
// for two matchers of same type with the (match) type argument determining
|
|
// which is used for lookahead.
|
|
template <class Matcher, MatchType MT>
|
|
class LookAheadSelector<Matcher, Matcher, MT> {
|
|
public:
|
|
using FST = typename Matcher::FST;
|
|
|
|
LookAheadSelector(Matcher *lmatcher1, Matcher *lmatcher2, MatchType type)
|
|
: lmatcher1_(lmatcher1->Copy()),
|
|
lmatcher2_(lmatcher2->Copy()),
|
|
type_(type) {}
|
|
|
|
LookAheadSelector(const LookAheadSelector<Matcher, Matcher, MT> &selector)
|
|
: lmatcher1_(selector.lmatcher1_->Copy()),
|
|
lmatcher2_(selector.lmatcher2_->Copy()),
|
|
type_(selector.type_) {}
|
|
|
|
const FST &GetFst() const {
|
|
return type_ == MATCH_OUTPUT ? lmatcher2_->GetFst() : lmatcher1_->GetFst();
|
|
}
|
|
|
|
Matcher *GetMatcher() const {
|
|
return type_ == MATCH_OUTPUT ? lmatcher1_.get() : lmatcher2_.get();
|
|
}
|
|
|
|
private:
|
|
std::unique_ptr<Matcher> lmatcher1_;
|
|
std::unique_ptr<Matcher> lmatcher2_;
|
|
MatchType type_;
|
|
};
|
|
|
|
// Stores and returns the appropriate FST and matcher for lookahead.
|
|
// Specialized for lookahead on input labels.
|
|
template <class Matcher1, class Matcher2>
|
|
class LookAheadSelector<Matcher1, Matcher2, MATCH_INPUT> {
|
|
public:
|
|
using FST1 = typename Matcher1::FST;
|
|
|
|
LookAheadSelector(Matcher1 *lmatcher1, Matcher2 *lmatcher2, MatchType)
|
|
: fst_(lmatcher1->GetFst().Copy()), lmatcher_(lmatcher2->Copy()) {}
|
|
|
|
LookAheadSelector(
|
|
const LookAheadSelector<Matcher1, Matcher2, MATCH_INPUT> &selector)
|
|
: fst_(selector.fst_->Copy()), lmatcher_(selector.lmatcher_->Copy()) {}
|
|
|
|
const FST1 &GetFst() const { return *fst_; }
|
|
|
|
Matcher2 *GetMatcher() const { return lmatcher_.get(); }
|
|
|
|
private:
|
|
std::unique_ptr<const FST1> fst_;
|
|
std::unique_ptr<Matcher2> lmatcher_;
|
|
};
|
|
|
|
// Stores and returns the appropriate FST and matcher for lookahead.
|
|
// Specialized for lookahead on output labels.
|
|
template <class Matcher1, class Matcher2>
|
|
class LookAheadSelector<Matcher1, Matcher2, MATCH_OUTPUT> {
|
|
public:
|
|
using FST2 = typename Matcher2::FST;
|
|
|
|
LookAheadSelector(Matcher1 *lmatcher1, Matcher2 *lmatcher2, MatchType)
|
|
: fst_(lmatcher2->GetFst().Copy()), lmatcher_(lmatcher1->Copy()) {}
|
|
|
|
LookAheadSelector(
|
|
const LookAheadSelector<Matcher1, Matcher2, MATCH_OUTPUT> &selector)
|
|
: fst_(selector.fst_->Copy()), lmatcher_(selector.lmatcher_->Copy()) {}
|
|
|
|
const FST2 &GetFst() const { return *fst_; }
|
|
|
|
Matcher1 *GetMatcher() const { return lmatcher_.get(); }
|
|
|
|
private:
|
|
std::unique_ptr<const FST2> fst_;
|
|
std::unique_ptr<Matcher1> lmatcher_;
|
|
};
|
|
|
|
// This filter uses a lookahead matcher in FilterArc(arc1, arc2) to examine the
|
|
// future of the composition state (arc1.nextstate, arc2.nextstate), blocking
|
|
// moving forward when its determined to be
|
|
// non-coaccessible. It is templated on an underlying filter, typically the
|
|
// epsilon filter. Which matcher is the lookahead matcher is determined by the
|
|
// template argument MT unless it is MATCH_BOTH. In that case, both matcher
|
|
// arguments must be lookahead matchers of the same type and one will be
|
|
// selected by LookAheadMatchType() based on their capability.
|
|
template <class Filter, class M1 = LookAheadMatcher<typename Filter::FST1>,
|
|
class M2 = M1, MatchType MT = MATCH_BOTH>
|
|
class LookAheadComposeFilter {
|
|
public:
|
|
using Arc = typename Filter::Arc;
|
|
using StateId = typename Arc::StateId;
|
|
using Weight = typename Arc::Weight;
|
|
|
|
using FST1 = typename Filter::FST1;
|
|
using FST2 = typename Filter::FST2;
|
|
using Matcher1 = typename Filter::Matcher1;
|
|
using Matcher2 = typename Filter::Matcher2;
|
|
using FilterState = typename Filter::FilterState;
|
|
|
|
LookAheadComposeFilter(const FST1 &fst1, const FST2 &fst2, M1 *matcher1,
|
|
M2 *matcher2)
|
|
: filter_(fst1, fst2, matcher1, matcher2),
|
|
lookahead_type_(MT == MATCH_BOTH
|
|
? LookAheadMatchType(*filter_.GetMatcher1(),
|
|
*filter_.GetMatcher2())
|
|
: MT),
|
|
selector_(filter_.GetMatcher1(), filter_.GetMatcher2(),
|
|
lookahead_type_),
|
|
flags_(lookahead_type_ == MATCH_OUTPUT
|
|
? filter_.GetMatcher1()->Flags()
|
|
: filter_.GetMatcher2()->Flags()) {
|
|
if (lookahead_type_ == MATCH_NONE) {
|
|
FSTERROR() << "LookAheadComposeFilter: 1st argument cannot "
|
|
<< "match/look-ahead on output labels and 2nd argument "
|
|
<< "cannot match/look-ahead on input labels";
|
|
}
|
|
selector_.GetMatcher()->InitLookAheadFst(selector_.GetFst());
|
|
}
|
|
|
|
LookAheadComposeFilter(
|
|
const LookAheadComposeFilter<Filter, M1, M2, MT> &filter,
|
|
bool safe = false)
|
|
: filter_(filter.filter_, safe),
|
|
lookahead_type_(filter.lookahead_type_),
|
|
selector_(filter_.GetMatcher1(), filter_.GetMatcher2(),
|
|
lookahead_type_),
|
|
flags_(filter.flags_) {
|
|
selector_.GetMatcher()->InitLookAheadFst(selector_.GetFst(), true);
|
|
}
|
|
|
|
FilterState Start() const { return filter_.Start(); }
|
|
|
|
void SetState(StateId s1, StateId s2, const FilterState &fs) {
|
|
filter_.SetState(s1, s2, fs);
|
|
}
|
|
|
|
FilterState FilterArc(Arc *arc1, Arc *arc2) const {
|
|
lookahead_arc_ = false;
|
|
const FilterState &fs = filter_.FilterArc(arc1, arc2);
|
|
if (fs == FilterState::NoState()) return FilterState::NoState();
|
|
return LookAheadOutput() ? LookAheadFilterArc(arc1, arc2, fs)
|
|
: LookAheadFilterArc(arc2, arc1, fs);
|
|
}
|
|
|
|
void FilterFinal(Weight *weight1, Weight *weight2) const {
|
|
filter_.FilterFinal(weight1, weight2);
|
|
}
|
|
|
|
// Returns matchers; ownership stays with filter.
|
|
|
|
Matcher1 *GetMatcher1() { return filter_.GetMatcher1(); }
|
|
|
|
Matcher2 *GetMatcher2() { return filter_.GetMatcher2(); }
|
|
|
|
const LookAheadSelector<Matcher1, Matcher2, MT> &Selector() const {
|
|
return selector_;
|
|
}
|
|
|
|
uint64_t Properties(uint64_t inprops) const {
|
|
auto outprops = filter_.Properties(inprops);
|
|
if (lookahead_type_ == MATCH_NONE) outprops |= kError;
|
|
return outprops;
|
|
}
|
|
|
|
uint32_t LookAheadFlags() const { return flags_; }
|
|
|
|
bool LookAheadArc() const { return lookahead_arc_; }
|
|
|
|
bool LookAheadOutput() const {
|
|
if (MT == MATCH_OUTPUT) {
|
|
return true;
|
|
} else if (MT == MATCH_INPUT) {
|
|
return false;
|
|
} else if (lookahead_type_ == MATCH_OUTPUT) {
|
|
return true;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private:
|
|
FilterState LookAheadFilterArc(Arc *arca, Arc *arcb,
|
|
const FilterState &fs) const {
|
|
auto &labela = LookAheadOutput() ? arca->olabel : arca->ilabel;
|
|
if (labela != 0 && !(flags_ & kLookAheadNonEpsilons)) return fs;
|
|
if (labela == 0 && !(flags_ & kLookAheadEpsilons)) return fs;
|
|
lookahead_arc_ = true;
|
|
selector_.GetMatcher()->SetState(arca->nextstate);
|
|
return selector_.GetMatcher()->LookAheadFst(selector_.GetFst(),
|
|
arcb->nextstate)
|
|
? fs
|
|
: FilterState::NoState();
|
|
}
|
|
|
|
Filter filter_; // Underlying filter.
|
|
MatchType lookahead_type_; // Lookahead match type.
|
|
LookAheadSelector<Matcher1, Matcher2, MT> selector_;
|
|
uint32_t flags_; // Lookahead flags.
|
|
mutable bool lookahead_arc_; // Look-ahead performed at last FilterArc()?
|
|
|
|
LookAheadComposeFilter &operator=(const LookAheadComposeFilter &) = delete;
|
|
};
|
|
|
|
// This filter adds weight-pushing to a lookahead composition filter using the
|
|
// LookAheadWeight() method of matcher argument. It is templated on an
|
|
// underlying lookahead filter, typically the basic lookahead filter.
|
|
// Weight-pushing in composition brings weights forward as much as possible
|
|
// based on the lookahead information.
|
|
template <class Filter, class M1 = LookAheadMatcher<typename Filter::FST1>,
|
|
class M2 = M1, MatchType MT = MATCH_BOTH>
|
|
class PushWeightsComposeFilter {
|
|
public:
|
|
using Arc = typename Filter::Arc;
|
|
using StateId = typename Filter::StateId;
|
|
using Weight = typename Filter::Weight;
|
|
|
|
using FST1 = typename Filter::FST1;
|
|
using FST2 = typename Filter::FST2;
|
|
using Matcher1 = typename Filter::Matcher1;
|
|
using Matcher2 = typename Filter::Matcher2;
|
|
|
|
using FilterState1 = typename Filter::FilterState;
|
|
using FilterState2 = WeightFilterState<Weight>;
|
|
using FilterState = PairFilterState<FilterState1, FilterState2>;
|
|
|
|
PushWeightsComposeFilter(const FST1 &fst1, const FST2 &fst2, M1 *matcher1,
|
|
M2 *matcher2)
|
|
: filter_(fst1, fst2, matcher1, matcher2), fs_(FilterState::NoState()) {}
|
|
|
|
PushWeightsComposeFilter(
|
|
const PushWeightsComposeFilter<Filter, M1, M2, MT> &filter,
|
|
bool safe = false)
|
|
: filter_(filter.filter_, safe), fs_(FilterState::NoState()) {}
|
|
|
|
FilterState Start() const {
|
|
return FilterState(filter_.Start(), FilterState2(Weight::One()));
|
|
}
|
|
|
|
void SetState(StateId s1, StateId s2, const FilterState &fs) {
|
|
fs_ = fs;
|
|
filter_.SetState(s1, s2, fs.GetState1());
|
|
}
|
|
|
|
FilterState FilterArc(Arc *arc1, Arc *arc2) const {
|
|
const auto &fs1 = filter_.FilterArc(arc1, arc2);
|
|
if (fs1 == FilterState1::NoState()) return FilterState::NoState();
|
|
if (!(LookAheadFlags() & kLookAheadWeight)) {
|
|
return FilterState(fs1, FilterState2(Weight::One()));
|
|
}
|
|
const auto &lweight = LookAheadArc()
|
|
? Selector().GetMatcher()->LookAheadWeight()
|
|
: Weight::One();
|
|
const auto &fs2 = fs_.GetState2();
|
|
const auto &fweight = fs2.GetWeight();
|
|
// Disallows Zero() weight futures.
|
|
if (lweight == Weight::Zero()) return FilterState::NoState();
|
|
arc2->weight = Divide(Times(arc2->weight, lweight), fweight);
|
|
return FilterState(fs1, FilterState2(lweight.Quantize()));
|
|
}
|
|
|
|
void FilterFinal(Weight *weight1, Weight *weight2) const {
|
|
filter_.FilterFinal(weight1, weight2);
|
|
if (!(LookAheadFlags() & kLookAheadWeight) || *weight1 == Weight::Zero()) {
|
|
return;
|
|
}
|
|
const auto &fs2 = fs_.GetState2();
|
|
const auto &fweight = fs2.GetWeight();
|
|
*weight1 = Divide(*weight1, fweight);
|
|
}
|
|
|
|
// Returns matchers; ownership states with filter.
|
|
|
|
Matcher1 *GetMatcher1() { return filter_.GetMatcher1(); }
|
|
|
|
Matcher2 *GetMatcher2() { return filter_.GetMatcher2(); }
|
|
|
|
const LookAheadSelector<Matcher1, Matcher2, MT> &Selector() const {
|
|
return filter_.Selector();
|
|
}
|
|
|
|
uint32_t LookAheadFlags() const { return filter_.LookAheadFlags(); }
|
|
|
|
bool LookAheadArc() const { return filter_.LookAheadArc(); }
|
|
|
|
bool LookAheadOutput() const { return filter_.LookAheadOutput(); }
|
|
|
|
uint64_t Properties(uint64_t props) const {
|
|
return filter_.Properties(props) & kWeightInvariantProperties;
|
|
}
|
|
|
|
private:
|
|
Filter filter_; // Underlying filter.
|
|
FilterState fs_; // Current filter state.
|
|
|
|
PushWeightsComposeFilter &operator=(const PushWeightsComposeFilter &) =
|
|
delete;
|
|
};
|
|
|
|
// This filter adds label-pushing to a lookahead composition filter using the
|
|
// LookAheadPrefix() method of the matcher argument. It is templated on an
|
|
// underlying filter, typically the basic lookahead or weight-pushing lookahead
|
|
// filter. Label-pushing in composition matches labels as early as possible
|
|
// based on the lookahead information.
|
|
template <class Filter, class M1 = LookAheadMatcher<typename Filter::FST1>,
|
|
class M2 = M1, MatchType MT = MATCH_BOTH>
|
|
class PushLabelsComposeFilter {
|
|
public:
|
|
using Arc = typename Filter::Arc;
|
|
using Label = typename Arc::Label;
|
|
using StateId = typename Arc::StateId;
|
|
using Weight = typename Arc::Weight;
|
|
|
|
using FST1 = typename Filter::FST1;
|
|
using FST2 = typename Filter::FST2;
|
|
using Matcher1 = MultiEpsMatcher<typename Filter::Matcher1>;
|
|
using Matcher2 = MultiEpsMatcher<typename Filter::Matcher2>;
|
|
using FilterState1 = typename Filter::FilterState;
|
|
using FilterState2 = IntegerFilterState<Label>;
|
|
using FilterState = PairFilterState<FilterState1, FilterState2>;
|
|
|
|
PushLabelsComposeFilter(const FST1 &fst1, const FST2 &fst2, M1 *matcher1,
|
|
M2 *matcher2)
|
|
: filter_(fst1, fst2, matcher1, matcher2),
|
|
fs_(FilterState::NoState()),
|
|
fst1_(filter_.GetMatcher1()->GetFst()),
|
|
fst2_(filter_.GetMatcher2()->GetFst()),
|
|
matcher1_(fst1_, MATCH_OUTPUT,
|
|
filter_.LookAheadOutput() ? kMultiEpsList : kMultiEpsLoop,
|
|
filter_.GetMatcher1(), /*own_matcher=*/false),
|
|
matcher2_(fst2_, MATCH_INPUT,
|
|
filter_.LookAheadOutput() ? kMultiEpsLoop : kMultiEpsList,
|
|
filter_.GetMatcher2(), /*own_matcher=*/false) {}
|
|
|
|
PushLabelsComposeFilter(
|
|
const PushLabelsComposeFilter<Filter, M1, M2, MT> &filter,
|
|
bool safe = false)
|
|
: filter_(filter.filter_, safe),
|
|
fs_(FilterState::NoState()),
|
|
fst1_(filter_.GetMatcher1()->GetFst()),
|
|
fst2_(filter_.GetMatcher2()->GetFst()),
|
|
matcher1_(fst1_, MATCH_OUTPUT,
|
|
filter_.LookAheadOutput() ? kMultiEpsList : kMultiEpsLoop,
|
|
filter_.GetMatcher1(), /*own_matcher=*/false),
|
|
matcher2_(fst2_, MATCH_INPUT,
|
|
filter_.LookAheadOutput() ? kMultiEpsLoop : kMultiEpsList,
|
|
filter_.GetMatcher2(), /*own_matcher=*/false) {}
|
|
|
|
FilterState Start() const {
|
|
return FilterState(filter_.Start(), FilterState2(kNoLabel));
|
|
}
|
|
|
|
void SetState(StateId s1, StateId s2, const FilterState &fs) {
|
|
fs_ = fs;
|
|
filter_.SetState(s1, s2, fs.GetState1());
|
|
if (!(LookAheadFlags() & kLookAheadPrefix)) return;
|
|
narcsa_ = LookAheadOutput() ? internal::NumArcs(fst1_, s1)
|
|
: internal::NumArcs(fst2_, s2);
|
|
const auto &fs2 = fs_.GetState2();
|
|
const auto &flabel = fs2.GetState();
|
|
GetMatcher1()->ClearMultiEpsLabels();
|
|
GetMatcher2()->ClearMultiEpsLabels();
|
|
if (flabel != kNoLabel) { // Have a lookahead label?
|
|
GetMatcher1()->AddMultiEpsLabel(flabel); // Yes, make it a multi-epsilon
|
|
GetMatcher2()->AddMultiEpsLabel(flabel); // label so that it matches the
|
|
} // implicit epsilon arc to be
|
|
} // modified below when pushing.
|
|
|
|
FilterState FilterArc(Arc *arc1, Arc *arc2) const {
|
|
if (!(LookAheadFlags() & kLookAheadPrefix)) {
|
|
return FilterState(filter_.FilterArc(arc1, arc2), FilterState2(kNoLabel));
|
|
}
|
|
const auto &fs2 = fs_.GetState2();
|
|
const auto &flabel = fs2.GetState();
|
|
if (flabel != kNoLabel) { // Have a lookahead label?
|
|
return LookAheadOutput() ? PushedLabelFilterArc(arc1, arc2, flabel)
|
|
: PushedLabelFilterArc(arc2, arc1, flabel);
|
|
}
|
|
const auto &fs1 = filter_.FilterArc(arc1, arc2);
|
|
if (fs1 == FilterState1::NoState()) return FilterState::NoState();
|
|
if (!LookAheadArc()) return FilterState(fs1, FilterState2(kNoLabel));
|
|
return LookAheadOutput() ? PushLabelFilterArc(arc1, arc2, fs1)
|
|
: PushLabelFilterArc(arc2, arc1, fs1);
|
|
}
|
|
|
|
void FilterFinal(Weight *weight1, Weight *weight2) const {
|
|
filter_.FilterFinal(weight1, weight2);
|
|
if (!(LookAheadFlags() & kLookAheadPrefix) || *weight1 == Weight::Zero()) {
|
|
return;
|
|
}
|
|
const auto &fs2 = fs_.GetState2();
|
|
const auto &flabel = fs2.GetState();
|
|
if (flabel != kNoLabel) *weight1 = Weight::Zero();
|
|
}
|
|
|
|
// Returns matchers; ownership states with filter.
|
|
|
|
Matcher1 *GetMatcher1() { return &matcher1_; }
|
|
|
|
Matcher2 *GetMatcher2() { return &matcher2_; }
|
|
|
|
uint64_t Properties(uint64_t iprops) const {
|
|
const auto oprops = filter_.Properties(iprops);
|
|
if (LookAheadOutput()) {
|
|
return oprops & kOLabelInvariantProperties;
|
|
} else {
|
|
return oprops & kILabelInvariantProperties;
|
|
}
|
|
}
|
|
|
|
private:
|
|
const LookAheadSelector<typename Filter::Matcher1, typename Filter::Matcher2,
|
|
MT>
|
|
&Selector() const {
|
|
return filter_.Selector();
|
|
}
|
|
|
|
// Consumes an already pushed label.
|
|
FilterState PushedLabelFilterArc(Arc *arca, Arc *arcb, Label flabel) const {
|
|
auto &labela = LookAheadOutput() ? arca->olabel : arca->ilabel;
|
|
const auto &labelb = LookAheadOutput() ? arcb->ilabel : arcb->olabel;
|
|
if (labelb != kNoLabel) {
|
|
return FilterState::NoState(); // Blocks non-(multi-)epsilon label
|
|
} else if (labela == flabel) {
|
|
labela = 0; // Converts match to multi-epsilon to epsilon.
|
|
return Start();
|
|
} else if (labela == 0) {
|
|
if (narcsa_ == 1) return fs_; // Takes epsilon, keeping state with label.
|
|
Selector().GetMatcher()->SetState(arca->nextstate);
|
|
if (Selector().GetMatcher()->LookAheadLabel(flabel)) {
|
|
return fs_; // Takes epsilon, keeping state with label.
|
|
} else {
|
|
return FilterState::NoState(); // Blocks non-coaccessible path.
|
|
}
|
|
} else {
|
|
return FilterState::NoState(); // Blocks mismatch to multi-epsilon label.
|
|
}
|
|
}
|
|
|
|
// Pushes a label forward when possible.
|
|
FilterState PushLabelFilterArc(Arc *arca, Arc *arcb,
|
|
const FilterState1 &fs1) const {
|
|
auto &labela = LookAheadOutput() ? arca->olabel : arca->ilabel;
|
|
const auto &labelb = LookAheadOutput() ? arcb->olabel : arcb->ilabel;
|
|
if (labelb != 0) { // No place to push.
|
|
return FilterState(fs1, FilterState2(kNoLabel));
|
|
}
|
|
if (labela != 0 && // Wrong lookahead prefix type?
|
|
LookAheadFlags() & kLookAheadNonEpsilonPrefix) {
|
|
return FilterState(fs1, FilterState2(kNoLabel));
|
|
}
|
|
Arc larc(kNoLabel, kNoLabel, Weight::Zero(), kNoStateId);
|
|
if (Selector().GetMatcher()->LookAheadPrefix(&larc)) { // Have prefix arc?
|
|
labela = LookAheadOutput() ? larc.ilabel : larc.olabel;
|
|
arcb->ilabel = larc.ilabel; // Goes forward on that arc,
|
|
arcb->olabel = larc.olabel; // thus pushing the label.
|
|
arcb->weight = Times(arcb->weight, larc.weight);
|
|
arcb->nextstate = larc.nextstate;
|
|
return FilterState(fs1, FilterState2(labela));
|
|
} else {
|
|
return FilterState(fs1, FilterState2(kNoLabel));
|
|
}
|
|
}
|
|
|
|
uint32_t LookAheadFlags() const { return filter_.LookAheadFlags(); }
|
|
|
|
bool LookAheadArc() const { return filter_.LookAheadArc(); }
|
|
|
|
bool LookAheadOutput() const { return filter_.LookAheadOutput(); }
|
|
|
|
Filter filter_; // Underlying filter.
|
|
FilterState fs_; // Current filter state.
|
|
const FST1 &fst1_;
|
|
const FST2 &fst2_;
|
|
Matcher1 matcher1_; // Multi-epsilon matcher for fst1_.
|
|
Matcher2 matcher2_; // Multi-epsilon matcher for fst2_.
|
|
ssize_t narcsa_; // Number of arcs leaving look-ahead match FST.
|
|
|
|
PushLabelsComposeFilter &operator=(const PushLabelsComposeFilter &) = delete;
|
|
};
|
|
|
|
// Convenience class for setting up composition with a default lookahead matcher
|
|
// and filter.
|
|
template <class Arc, MatchType type>
|
|
class DefaultLookAhead {
|
|
public:
|
|
using M = Matcher<Fst<Arc>>;
|
|
using ComposeFilter = SequenceComposeFilter<M>;
|
|
using FstMatcher = M;
|
|
};
|
|
|
|
// Specializes for MATCH_INPUT to allow lookahead.
|
|
template <class Arc>
|
|
class DefaultLookAhead<Arc, MATCH_INPUT> {
|
|
public:
|
|
using M = LookAheadMatcher<Fst<Arc>>;
|
|
using SF = SequenceComposeFilter<M>;
|
|
using ComposeFilter = LookAheadComposeFilter<SF, M>;
|
|
using FstMatcher = M;
|
|
};
|
|
|
|
// Specializes for MATCH_OUTPUT to allow lookahead.
|
|
template <class Arc>
|
|
class DefaultLookAhead<Arc, MATCH_OUTPUT> {
|
|
public:
|
|
using M = LookAheadMatcher<Fst<Arc>>;
|
|
using SF = AltSequenceComposeFilter<M>;
|
|
using ComposeFilter = LookAheadComposeFilter<SF, M>;
|
|
using FstMatcher = M;
|
|
};
|
|
|
|
// Specializes for StdArc to allow weight and label pushing.
|
|
template <>
|
|
class DefaultLookAhead<StdArc, MATCH_INPUT> {
|
|
public:
|
|
using M = LookAheadMatcher<Fst<StdArc>>;
|
|
using SF = SequenceComposeFilter<M>;
|
|
using LF = LookAheadComposeFilter<SF, M>;
|
|
using WF = PushWeightsComposeFilter<LF, M>;
|
|
using ComposeFilter = PushLabelsComposeFilter<WF, M>;
|
|
using FstMatcher = M;
|
|
};
|
|
|
|
// Specializes for StdArc to allow weight and label pushing.
|
|
template <>
|
|
class DefaultLookAhead<StdArc, MATCH_OUTPUT> {
|
|
public:
|
|
using M = LookAheadMatcher<Fst<StdArc>>;
|
|
using SF = AltSequenceComposeFilter<M>;
|
|
using LF = LookAheadComposeFilter<SF, M>;
|
|
using WF = PushWeightsComposeFilter<LF, M>;
|
|
using ComposeFilter = PushLabelsComposeFilter<WF, M>;
|
|
using FstMatcher = M;
|
|
};
|
|
|
|
// Specializes for LogArc to allow weight and label pushing.
|
|
template <>
|
|
class DefaultLookAhead<LogArc, MATCH_INPUT> {
|
|
public:
|
|
using M = LookAheadMatcher<Fst<LogArc>>;
|
|
using SF = SequenceComposeFilter<M>;
|
|
using LF = LookAheadComposeFilter<SF, M>;
|
|
using WF = PushWeightsComposeFilter<LF, M>;
|
|
using ComposeFilter = PushLabelsComposeFilter<WF, M>;
|
|
using FstMatcher = M;
|
|
};
|
|
|
|
// Specializes for LogArc to allow weight and label pushing.
|
|
template <>
|
|
class DefaultLookAhead<LogArc, MATCH_OUTPUT> {
|
|
public:
|
|
using M = LookAheadMatcher<Fst<LogArc>>;
|
|
using SF = AltSequenceComposeFilter<M>;
|
|
using LF = LookAheadComposeFilter<SF, M>;
|
|
using WF = PushWeightsComposeFilter<LF, M>;
|
|
using ComposeFilter = PushLabelsComposeFilter<WF, M>;
|
|
using FstMatcher = M;
|
|
};
|
|
|
|
} // namespace fst
|
|
|
|
#endif // FST_LOOKAHEAD_FILTER_H_
|