// replace-util.h // 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. // // Copyright 2005-2010 Google, Inc. // Author: riley@google.com (Michael Riley) // // \file // Utility classes for the recursive replacement of Fsts (RTNs). #ifndef FST_LIB_REPLACE_UTIL_H__ #define FST_LIB_REPLACE_UTIL_H__ #include <vector> using std::vector; #include <tr1/unordered_map> using std::tr1::unordered_map; using std::tr1::unordered_multimap; #include <tr1/unordered_set> using std::tr1::unordered_set; using std::tr1::unordered_multiset; #include <map> #include <fst/connect.h> #include <fst/mutable-fst.h> #include <fst/topsort.h> namespace fst { template <class Arc> void Replace(const vector<pair<typename Arc::Label, const Fst<Arc>* > >&, MutableFst<Arc> *, typename Arc::Label, bool); // Utility class for the recursive replacement of Fsts (RTNs). The // user provides a set of Label, Fst pairs at construction. These are // used by methods for testing cyclic dependencies and connectedness // and doing RTN connection and specific Fst replacement by label or // for various optimization properties. The modified results can be // obtained with the GetFstPairs() or GetMutableFstPairs() methods. template <class Arc> class ReplaceUtil { public: typedef typename Arc::Label Label; typedef typename Arc::Weight Weight; typedef typename Arc::StateId StateId; typedef pair<Label, const Fst<Arc>*> FstPair; typedef pair<Label, MutableFst<Arc>*> MutableFstPair; typedef unordered_map<Label, Label> NonTerminalHash; // Constructs from mutable Fsts; Fst ownership given to ReplaceUtil. ReplaceUtil(const vector<MutableFstPair> &fst_pairs, Label root_label, bool epsilon_on_replace = false); // Constructs from Fsts; Fst ownership retained by caller. ReplaceUtil(const vector<FstPair> &fst_pairs, Label root_label, bool epsilon_on_replace = false); // Constructs from ReplaceFst internals; ownership retained by caller. ReplaceUtil(const vector<const Fst<Arc> *> &fst_array, const NonTerminalHash &nonterminal_hash, Label root_fst, bool epsilon_on_replace = false); ~ReplaceUtil() { for (Label i = 0; i < fst_array_.size(); ++i) delete fst_array_[i]; } // True if the non-terminal dependencies are cyclic. Cyclic // dependencies will result in an unexpandable replace fst. bool CyclicDependencies() const { GetDependencies(false); return depprops_ & kCyclic; } // Returns true if no useless Fsts, states or transitions. bool Connected() const { GetDependencies(false); uint64 props = kAccessible | kCoAccessible; for (Label i = 0; i < fst_array_.size(); ++i) { if (!fst_array_[i]) continue; if (fst_array_[i]->Properties(props, true) != props || !depaccess_[i]) return false; } return true; } // Removes useless Fsts, states and transitions. void Connect(); // Replaces Fsts specified by labels. // Does nothing if there are cyclic dependencies. void ReplaceLabels(const vector<Label> &labels); // Replaces Fsts that have at most 'nstates' states, 'narcs' arcs and // 'nnonterm' non-terminals (updating in reverse dependency order). // Does nothing if there are cyclic dependencies. void ReplaceBySize(size_t nstates, size_t narcs, size_t nnonterms); // Replaces singleton Fsts. // Does nothing if there are cyclic dependencies. void ReplaceTrivial() { ReplaceBySize(2, 1, 1); } // Replaces non-terminals that have at most 'ninstances' instances // (updating in dependency order). // Does nothing if there are cyclic dependencies. void ReplaceByInstances(size_t ninstances); // Replaces non-terminals that have only one instance. // Does nothing if there are cyclic dependencies. void ReplaceUnique() { ReplaceByInstances(1); } // Returns Label, Fst pairs; Fst ownership retained by ReplaceUtil. void GetFstPairs(vector<FstPair> *fst_pairs); // Returns Label, MutableFst pairs; Fst ownership given to caller. void GetMutableFstPairs(vector<MutableFstPair> *mutable_fst_pairs); private: // Per Fst statistics struct ReplaceStats { StateId nstates; // # of states StateId nfinal; // # of final states size_t narcs; // # of arcs Label nnonterms; // # of non-terminals in Fst size_t nref; // # of non-terminal instances referring to this Fst // # of times that ith Fst references this Fst map<Label, size_t> inref; // # of times that this Fst references the ith Fst map<Label, size_t> outref; ReplaceStats() : nstates(0), nfinal(0), narcs(0), nnonterms(0), nref(0) {} }; // Check Mutable Fsts exist o.w. create them. void CheckMutableFsts(); // Computes the dependency graph of the replace Fsts. // If 'stats' is true, dependency statistics computed as well. void GetDependencies(bool stats) const; void ClearDependencies() const { depfst_.DeleteStates(); stats_.clear(); depprops_ = 0; have_stats_ = false; } // Get topological order of dependencies. Returns false with cyclic input. bool GetTopOrder(const Fst<Arc> &fst, vector<Label> *toporder) const; // Update statistics assuming that jth Fst will be replaced. void UpdateStats(Label j); Label root_label_; // root non-terminal Label root_fst_; // root Fst ID bool epsilon_on_replace_; // see Replace() vector<const Fst<Arc> *> fst_array_; // Fst per ID vector<MutableFst<Arc> *> mutable_fst_array_; // MutableFst per ID vector<Label> nonterminal_array_; // Fst ID to non-terminal NonTerminalHash nonterminal_hash_; // non-terminal to Fst ID mutable VectorFst<Arc> depfst_; // Fst ID dependencies mutable vector<bool> depaccess_; // Fst ID accessibility mutable uint64 depprops_; // dependency Fst props mutable bool have_stats_; // have dependency statistics mutable vector<ReplaceStats> stats_; // Per Fst statistics DISALLOW_COPY_AND_ASSIGN(ReplaceUtil); }; template <class Arc> ReplaceUtil<Arc>::ReplaceUtil( const vector<MutableFstPair> &fst_pairs, Label root_label, bool epsilon_on_replace) : root_label_(root_label), epsilon_on_replace_(epsilon_on_replace), depprops_(0), have_stats_(false) { fst_array_.push_back(0); mutable_fst_array_.push_back(0); nonterminal_array_.push_back(kNoLabel); for (Label i = 0; i < fst_pairs.size(); ++i) { Label label = fst_pairs[i].first; MutableFst<Arc> *fst = fst_pairs[i].second; nonterminal_hash_[label] = fst_array_.size(); nonterminal_array_.push_back(label); fst_array_.push_back(fst); mutable_fst_array_.push_back(fst); } root_fst_ = nonterminal_hash_[root_label_]; if (!root_fst_) FSTERROR() << "ReplaceUtil: no root FST for label: " << root_label_; } template <class Arc> ReplaceUtil<Arc>::ReplaceUtil( const vector<FstPair> &fst_pairs, Label root_label, bool epsilon_on_replace) : root_label_(root_label), epsilon_on_replace_(epsilon_on_replace), depprops_(0), have_stats_(false) { fst_array_.push_back(0); nonterminal_array_.push_back(kNoLabel); for (Label i = 0; i < fst_pairs.size(); ++i) { Label label = fst_pairs[i].first; const Fst<Arc> *fst = fst_pairs[i].second; nonterminal_hash_[label] = fst_array_.size(); nonterminal_array_.push_back(label); fst_array_.push_back(fst->Copy()); } root_fst_ = nonterminal_hash_[root_label]; if (!root_fst_) FSTERROR() << "ReplaceUtil: no root FST for label: " << root_label_; } template <class Arc> ReplaceUtil<Arc>::ReplaceUtil( const vector<const Fst<Arc> *> &fst_array, const NonTerminalHash &nonterminal_hash, Label root_fst, bool epsilon_on_replace) : root_fst_(root_fst), epsilon_on_replace_(epsilon_on_replace), nonterminal_array_(fst_array.size()), nonterminal_hash_(nonterminal_hash), depprops_(0), have_stats_(false) { fst_array_.push_back(0); for (Label i = 1; i < fst_array.size(); ++i) fst_array_.push_back(fst_array[i]->Copy()); for (typename NonTerminalHash::const_iterator it = nonterminal_hash.begin(); it != nonterminal_hash.end(); ++it) nonterminal_array_[it->second] = it->first; root_label_ = nonterminal_array_[root_fst_]; } template <class Arc> void ReplaceUtil<Arc>::GetDependencies(bool stats) const { if (depfst_.NumStates() > 0) { if (stats && !have_stats_) ClearDependencies(); else return; } have_stats_ = stats; if (have_stats_) stats_.reserve(fst_array_.size()); for (Label i = 0; i < fst_array_.size(); ++i) { depfst_.AddState(); depfst_.SetFinal(i, Weight::One()); if (have_stats_) stats_.push_back(ReplaceStats()); } depfst_.SetStart(root_fst_); // An arc from each state (representing the fst) to the // state representing the fst being replaced for (Label i = 0; i < fst_array_.size(); ++i) { const Fst<Arc> *ifst = fst_array_[i]; if (!ifst) continue; for (StateIterator<Fst<Arc> > siter(*ifst); !siter.Done(); siter.Next()) { StateId s = siter.Value(); if (have_stats_) { ++stats_[i].nstates; if (ifst->Final(s) != Weight::Zero()) ++stats_[i].nfinal; } for (ArcIterator<Fst<Arc> > aiter(*ifst, s); !aiter.Done(); aiter.Next()) { if (have_stats_) ++stats_[i].narcs; const Arc& arc = aiter.Value(); typename NonTerminalHash::const_iterator it = nonterminal_hash_.find(arc.olabel); if (it != nonterminal_hash_.end()) { Label j = it->second; depfst_.AddArc(i, Arc(arc.olabel, arc.olabel, Weight::One(), j)); if (have_stats_) { ++stats_[i].nnonterms; ++stats_[j].nref; ++stats_[j].inref[i]; ++stats_[i].outref[j]; } } } } } // Gets accessibility info SccVisitor<Arc> scc_visitor(0, &depaccess_, 0, &depprops_); DfsVisit(depfst_, &scc_visitor); } template <class Arc> void ReplaceUtil<Arc>::UpdateStats(Label j) { if (!have_stats_) { FSTERROR() << "ReplaceUtil::UpdateStats: stats not available"; return; } if (j == root_fst_) // can't replace root return; typedef typename map<Label, size_t>::iterator Iter; for (Iter in = stats_[j].inref.begin(); in != stats_[j].inref.end(); ++in) { Label i = in->first; size_t ni = in->second; stats_[i].nstates += stats_[j].nstates * ni; stats_[i].narcs += (stats_[j].narcs + 1) * ni; // narcs - 1 + 2 (eps) stats_[i].nnonterms += (stats_[j].nnonterms - 1) * ni; stats_[i].outref.erase(stats_[i].outref.find(j)); for (Iter out = stats_[j].outref.begin(); out != stats_[j].outref.end(); ++out) { Label k = out->first; size_t nk = out->second; stats_[i].outref[k] += ni * nk; } } for (Iter out = stats_[j].outref.begin(); out != stats_[j].outref.end(); ++out) { Label k = out->first; size_t nk = out->second; stats_[k].nref -= nk; stats_[k].inref.erase(stats_[k].inref.find(j)); for (Iter in = stats_[j].inref.begin(); in != stats_[j].inref.end(); ++in) { Label i = in->first; size_t ni = in->second; stats_[k].inref[i] += ni * nk; stats_[k].nref += ni * nk; } } } template <class Arc> void ReplaceUtil<Arc>::CheckMutableFsts() { if (mutable_fst_array_.size() == 0) { for (Label i = 0; i < fst_array_.size(); ++i) { if (!fst_array_[i]) { mutable_fst_array_.push_back(0); } else { mutable_fst_array_.push_back(new VectorFst<Arc>(*fst_array_[i])); delete fst_array_[i]; fst_array_[i] = mutable_fst_array_[i]; } } } } template <class Arc> void ReplaceUtil<Arc>::Connect() { CheckMutableFsts(); uint64 props = kAccessible | kCoAccessible; for (Label i = 0; i < mutable_fst_array_.size(); ++i) { if (!mutable_fst_array_[i]) continue; if (mutable_fst_array_[i]->Properties(props, false) != props) fst::Connect(mutable_fst_array_[i]); } GetDependencies(false); for (Label i = 0; i < mutable_fst_array_.size(); ++i) { MutableFst<Arc> *fst = mutable_fst_array_[i]; if (fst && !depaccess_[i]) { delete fst; fst_array_[i] = 0; mutable_fst_array_[i] = 0; } } ClearDependencies(); } template <class Arc> bool ReplaceUtil<Arc>::GetTopOrder(const Fst<Arc> &fst, vector<Label> *toporder) const { // Finds topological order of dependencies. vector<StateId> order; bool acyclic = false; TopOrderVisitor<Arc> top_order_visitor(&order, &acyclic); DfsVisit(fst, &top_order_visitor); if (!acyclic) { LOG(WARNING) << "ReplaceUtil::GetTopOrder: Cyclical label dependencies"; return false; } toporder->resize(order.size()); for (Label i = 0; i < order.size(); ++i) (*toporder)[order[i]] = i; return true; } template <class Arc> void ReplaceUtil<Arc>::ReplaceLabels(const vector<Label> &labels) { CheckMutableFsts(); unordered_set<Label> label_set; for (Label i = 0; i < labels.size(); ++i) if (labels[i] != root_label_) // can't replace root label_set.insert(labels[i]); // Finds Fst dependencies restricted to the labels requested. GetDependencies(false); VectorFst<Arc> pfst(depfst_); for (StateId i = 0; i < pfst.NumStates(); ++i) { vector<Arc> arcs; for (ArcIterator< VectorFst<Arc> > aiter(pfst, i); !aiter.Done(); aiter.Next()) { const Arc &arc = aiter.Value(); Label label = nonterminal_array_[arc.nextstate]; if (label_set.count(label) > 0) arcs.push_back(arc); } pfst.DeleteArcs(i); for (size_t j = 0; j < arcs.size(); ++j) pfst.AddArc(i, arcs[j]); } vector<Label> toporder; if (!GetTopOrder(pfst, &toporder)) { ClearDependencies(); return; } // Visits Fsts in reverse topological order of dependencies and // performs replacements. for (Label o = toporder.size() - 1; o >= 0; --o) { vector<FstPair> fst_pairs; StateId s = toporder[o]; for (ArcIterator< VectorFst<Arc> > aiter(pfst, s); !aiter.Done(); aiter.Next()) { const Arc &arc = aiter.Value(); Label label = nonterminal_array_[arc.nextstate]; const Fst<Arc> *fst = fst_array_[arc.nextstate]; fst_pairs.push_back(make_pair(label, fst)); } if (fst_pairs.empty()) continue; Label label = nonterminal_array_[s]; const Fst<Arc> *fst = fst_array_[s]; fst_pairs.push_back(make_pair(label, fst)); Replace(fst_pairs, mutable_fst_array_[s], label, epsilon_on_replace_); } ClearDependencies(); } template <class Arc> void ReplaceUtil<Arc>::ReplaceBySize(size_t nstates, size_t narcs, size_t nnonterms) { vector<Label> labels; GetDependencies(true); vector<Label> toporder; if (!GetTopOrder(depfst_, &toporder)) { ClearDependencies(); return; } for (Label o = toporder.size() - 1; o >= 0; --o) { Label j = toporder[o]; if (stats_[j].nstates <= nstates && stats_[j].narcs <= narcs && stats_[j].nnonterms <= nnonterms) { labels.push_back(nonterminal_array_[j]); UpdateStats(j); } } ReplaceLabels(labels); } template <class Arc> void ReplaceUtil<Arc>::ReplaceByInstances(size_t ninstances) { vector<Label> labels; GetDependencies(true); vector<Label> toporder; if (!GetTopOrder(depfst_, &toporder)) { ClearDependencies(); return; } for (Label o = 0; o < toporder.size(); ++o) { Label j = toporder[o]; if (stats_[j].nref <= ninstances) { labels.push_back(nonterminal_array_[j]); UpdateStats(j); } } ReplaceLabels(labels); } template <class Arc> void ReplaceUtil<Arc>::GetFstPairs(vector<FstPair> *fst_pairs) { CheckMutableFsts(); fst_pairs->clear(); for (Label i = 0; i < fst_array_.size(); ++i) { Label label = nonterminal_array_[i]; const Fst<Arc> *fst = fst_array_[i]; if (!fst) continue; fst_pairs->push_back(make_pair(label, fst)); } } template <class Arc> void ReplaceUtil<Arc>::GetMutableFstPairs( vector<MutableFstPair> *mutable_fst_pairs) { CheckMutableFsts(); mutable_fst_pairs->clear(); for (Label i = 0; i < mutable_fst_array_.size(); ++i) { Label label = nonterminal_array_[i]; MutableFst<Arc> *fst = mutable_fst_array_[i]; if (!fst) continue; mutable_fst_pairs->push_back(make_pair(label, fst->Copy())); } } } // namespace fst #endif // FST_LIB_REPLACE_UTIL_H__