// encode.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: johans@google.com (Johan Schalkwyk) // // \file // Class to encode and decoder an fst. #ifndef FST_LIB_ENCODE_H__ #define FST_LIB_ENCODE_H__ #include <climits> #include <tr1/unordered_map> using std::tr1::unordered_map; using std::tr1::unordered_multimap; #include <string> #include <vector> using std::vector; #include <fst/arc-map.h> #include <fst/rmfinalepsilon.h> namespace fst { static const uint32 kEncodeLabels = 0x0001; static const uint32 kEncodeWeights = 0x0002; static const uint32 kEncodeFlags = 0x0003; // All non-internal flags static const uint32 kEncodeHasISymbols = 0x0004; // For internal use static const uint32 kEncodeHasOSymbols = 0x0008; // For internal use enum EncodeType { ENCODE = 1, DECODE = 2 }; // Identifies stream data as an encode table (and its endianity) static const int32 kEncodeMagicNumber = 2129983209; // The following class encapsulates implementation details for the // encoding and decoding of label/weight tuples used for encoding // and decoding of Fsts. The EncodeTable is bidirectional. I.E it // stores both the Tuple of encode labels and weights to a unique // label, and the reverse. template <class A> class EncodeTable { public: typedef typename A::Label Label; typedef typename A::Weight Weight; // Encoded data consists of arc input/output labels and arc weight struct Tuple { Tuple() {} Tuple(Label ilabel_, Label olabel_, Weight weight_) : ilabel(ilabel_), olabel(olabel_), weight(weight_) {} Tuple(const Tuple& tuple) : ilabel(tuple.ilabel), olabel(tuple.olabel), weight(tuple.weight) {} Label ilabel; Label olabel; Weight weight; }; // Comparison object for hashing EncodeTable Tuple(s). class TupleEqual { public: bool operator()(const Tuple* x, const Tuple* y) const { return (x->ilabel == y->ilabel && x->olabel == y->olabel && x->weight == y->weight); } }; // Hash function for EncodeTabe Tuples. Based on the encode flags // we either hash the labels, weights or combination of them. class TupleKey { public: TupleKey() : encode_flags_(kEncodeLabels | kEncodeWeights) {} TupleKey(const TupleKey& key) : encode_flags_(key.encode_flags_) {} explicit TupleKey(uint32 encode_flags) : encode_flags_(encode_flags) {} size_t operator()(const Tuple* x) const { size_t hash = x->ilabel; const int lshift = 5; const int rshift = CHAR_BIT * sizeof(size_t) - 5; if (encode_flags_ & kEncodeLabels) hash = hash << lshift ^ hash >> rshift ^ x->olabel; if (encode_flags_ & kEncodeWeights) hash = hash << lshift ^ hash >> rshift ^ x->weight.Hash(); return hash; } private: int32 encode_flags_; }; typedef unordered_map<const Tuple*, Label, TupleKey, TupleEqual> EncodeHash; explicit EncodeTable(uint32 encode_flags) : flags_(encode_flags), encode_hash_(1024, TupleKey(encode_flags)), isymbols_(0), osymbols_(0) {} ~EncodeTable() { for (size_t i = 0; i < encode_tuples_.size(); ++i) { delete encode_tuples_[i]; } delete isymbols_; delete osymbols_; } // Given an arc encode either input/ouptut labels or input/costs or both Label Encode(const A &arc) { const Tuple tuple(arc.ilabel, flags_ & kEncodeLabels ? arc.olabel : 0, flags_ & kEncodeWeights ? arc.weight : Weight::One()); typename EncodeHash::const_iterator it = encode_hash_.find(&tuple); if (it == encode_hash_.end()) { encode_tuples_.push_back(new Tuple(tuple)); encode_hash_[encode_tuples_.back()] = encode_tuples_.size(); return encode_tuples_.size(); } else { return it->second; } } // Given an arc, look up its encoded label. Returns kNoLabel if not found. Label GetLabel(const A &arc) const { const Tuple tuple(arc.ilabel, flags_ & kEncodeLabels ? arc.olabel : 0, flags_ & kEncodeWeights ? arc.weight : Weight::One()); typename EncodeHash::const_iterator it = encode_hash_.find(&tuple); if (it == encode_hash_.end()) { return kNoLabel; } else { return it->second; } } // Given an encode arc Label decode back to input/output labels and costs const Tuple* Decode(Label key) const { if (key < 1 || key > encode_tuples_.size()) { LOG(ERROR) << "EncodeTable::Decode: unknown decode key: " << key; return 0; } return encode_tuples_[key - 1]; } size_t Size() const { return encode_tuples_.size(); } bool Write(ostream &strm, const string &source) const; static EncodeTable<A> *Read(istream &strm, const string &source); const uint32 flags() const { return flags_ & kEncodeFlags; } int RefCount() const { return ref_count_.count(); } int IncrRefCount() { return ref_count_.Incr(); } int DecrRefCount() { return ref_count_.Decr(); } SymbolTable *InputSymbols() const { return isymbols_; } SymbolTable *OutputSymbols() const { return osymbols_; } void SetInputSymbols(const SymbolTable* syms) { if (isymbols_) delete isymbols_; if (syms) { isymbols_ = syms->Copy(); flags_ |= kEncodeHasISymbols; } else { isymbols_ = 0; flags_ &= ~kEncodeHasISymbols; } } void SetOutputSymbols(const SymbolTable* syms) { if (osymbols_) delete osymbols_; if (syms) { osymbols_ = syms->Copy(); flags_ |= kEncodeHasOSymbols; } else { osymbols_ = 0; flags_ &= ~kEncodeHasOSymbols; } } private: uint32 flags_; vector<Tuple*> encode_tuples_; EncodeHash encode_hash_; RefCounter ref_count_; SymbolTable *isymbols_; // Pre-encoded ilabel symbol table SymbolTable *osymbols_; // Pre-encoded olabel symbol table DISALLOW_COPY_AND_ASSIGN(EncodeTable); }; template <class A> inline bool EncodeTable<A>::Write(ostream &strm, const string &source) const { WriteType(strm, kEncodeMagicNumber); WriteType(strm, flags_); int64 size = encode_tuples_.size(); WriteType(strm, size); for (size_t i = 0; i < size; ++i) { const Tuple* tuple = encode_tuples_[i]; WriteType(strm, tuple->ilabel); WriteType(strm, tuple->olabel); tuple->weight.Write(strm); } if (flags_ & kEncodeHasISymbols) isymbols_->Write(strm); if (flags_ & kEncodeHasOSymbols) osymbols_->Write(strm); strm.flush(); if (!strm) { LOG(ERROR) << "EncodeTable::Write: write failed: " << source; return false; } return true; } template <class A> inline EncodeTable<A> *EncodeTable<A>::Read(istream &strm, const string &source) { int32 magic_number = 0; ReadType(strm, &magic_number); if (magic_number != kEncodeMagicNumber) { LOG(ERROR) << "EncodeTable::Read: Bad encode table header: " << source; return 0; } uint32 flags; ReadType(strm, &flags); EncodeTable<A> *table = new EncodeTable<A>(flags); int64 size; ReadType(strm, &size); if (!strm) { LOG(ERROR) << "EncodeTable::Read: read failed: " << source; return 0; } for (size_t i = 0; i < size; ++i) { Tuple* tuple = new Tuple(); ReadType(strm, &tuple->ilabel); ReadType(strm, &tuple->olabel); tuple->weight.Read(strm); if (!strm) { LOG(ERROR) << "EncodeTable::Read: read failed: " << source; return 0; } table->encode_tuples_.push_back(tuple); table->encode_hash_[table->encode_tuples_.back()] = table->encode_tuples_.size(); } if (flags & kEncodeHasISymbols) table->isymbols_ = SymbolTable::Read(strm, source); if (flags & kEncodeHasOSymbols) table->osymbols_ = SymbolTable::Read(strm, source); return table; } // A mapper to encode/decode weighted transducers. Encoding of an // Fst is useful for performing classical determinization or minimization // on a weighted transducer by treating it as an unweighted acceptor over // encoded labels. // // The Encode mapper stores the encoding in a local hash table (EncodeTable) // This table is shared (and reference counted) between the encoder and // decoder. A decoder has read only access to the EncodeTable. // // The EncodeMapper allows on the fly encoding of the machine. As the // EncodeTable is generated the same table may by used to decode the machine // on the fly. For example in the following sequence of operations // // Encode -> Determinize -> Decode // // we will use the encoding table generated during the encode step in the // decode, even though the encoding is not complete. // template <class A> class EncodeMapper { typedef typename A::Weight Weight; typedef typename A::Label Label; public: EncodeMapper(uint32 flags, EncodeType type) : flags_(flags), type_(type), table_(new EncodeTable<A>(flags)), error_(false) {} EncodeMapper(const EncodeMapper& mapper) : flags_(mapper.flags_), type_(mapper.type_), table_(mapper.table_), error_(false) { table_->IncrRefCount(); } // Copy constructor but setting the type, typically to DECODE EncodeMapper(const EncodeMapper& mapper, EncodeType type) : flags_(mapper.flags_), type_(type), table_(mapper.table_), error_(mapper.error_) { table_->IncrRefCount(); } ~EncodeMapper() { if (!table_->DecrRefCount()) delete table_; } A operator()(const A &arc); MapFinalAction FinalAction() const { return (type_ == ENCODE && (flags_ & kEncodeWeights)) ? MAP_REQUIRE_SUPERFINAL : MAP_NO_SUPERFINAL; } MapSymbolsAction InputSymbolsAction() const { return MAP_CLEAR_SYMBOLS; } MapSymbolsAction OutputSymbolsAction() const { return MAP_CLEAR_SYMBOLS;} uint64 Properties(uint64 inprops) { uint64 outprops = inprops; if (error_) outprops |= kError; uint64 mask = kFstProperties; if (flags_ & kEncodeLabels) mask &= kILabelInvariantProperties & kOLabelInvariantProperties; if (flags_ & kEncodeWeights) mask &= kILabelInvariantProperties & kWeightInvariantProperties & (type_ == ENCODE ? kAddSuperFinalProperties : kRmSuperFinalProperties); return outprops & mask; } const uint32 flags() const { return flags_; } const EncodeType type() const { return type_; } const EncodeTable<A> &table() const { return *table_; } bool Write(ostream &strm, const string& source) { return table_->Write(strm, source); } bool Write(const string& filename) { ofstream strm(filename.c_str(), ofstream::out | ofstream::binary); if (!strm) { LOG(ERROR) << "EncodeMap: Can't open file: " << filename; return false; } return Write(strm, filename); } static EncodeMapper<A> *Read(istream &strm, const string& source, EncodeType type = ENCODE) { EncodeTable<A> *table = EncodeTable<A>::Read(strm, source); return table ? new EncodeMapper(table->flags(), type, table) : 0; } static EncodeMapper<A> *Read(const string& filename, EncodeType type = ENCODE) { ifstream strm(filename.c_str(), ifstream::in | ifstream::binary); if (!strm) { LOG(ERROR) << "EncodeMap: Can't open file: " << filename; return NULL; } return Read(strm, filename, type); } SymbolTable *InputSymbols() const { return table_->InputSymbols(); } SymbolTable *OutputSymbols() const { return table_->OutputSymbols(); } void SetInputSymbols(const SymbolTable* syms) { table_->SetInputSymbols(syms); } void SetOutputSymbols(const SymbolTable* syms) { table_->SetOutputSymbols(syms); } private: uint32 flags_; EncodeType type_; EncodeTable<A>* table_; bool error_; explicit EncodeMapper(uint32 flags, EncodeType type, EncodeTable<A> *table) : flags_(flags), type_(type), table_(table) {} void operator=(const EncodeMapper &); // Disallow. }; template <class A> inline A EncodeMapper<A>::operator()(const A &arc) { if (type_ == ENCODE) { // labels and/or weights to single label if ((arc.nextstate == kNoStateId && !(flags_ & kEncodeWeights)) || (arc.nextstate == kNoStateId && (flags_ & kEncodeWeights) && arc.weight == Weight::Zero())) { return arc; } else { Label label = table_->Encode(arc); return A(label, flags_ & kEncodeLabels ? label : arc.olabel, flags_ & kEncodeWeights ? Weight::One() : arc.weight, arc.nextstate); } } else { // type_ == DECODE if (arc.nextstate == kNoStateId) { return arc; } else { if (arc.ilabel == 0) return arc; if (flags_ & kEncodeLabels && arc.ilabel != arc.olabel) { FSTERROR() << "EncodeMapper: Label-encoded arc has different " "input and output labels"; error_ = true; } if (flags_ & kEncodeWeights && arc.weight != Weight::One()) { FSTERROR() << "EncodeMapper: Weight-encoded arc has non-trivial weight"; error_ = true; } const typename EncodeTable<A>::Tuple* tuple = table_->Decode(arc.ilabel); if (!tuple) { FSTERROR() << "EncodeMapper: decode failed"; error_ = true; return A(kNoLabel, kNoLabel, Weight::NoWeight(), arc.nextstate); } else { return A(tuple->ilabel, flags_ & kEncodeLabels ? tuple->olabel : arc.olabel, flags_ & kEncodeWeights ? tuple->weight : arc.weight, arc.nextstate); } } } } // Complexity: O(nstates + narcs) template<class A> inline void Encode(MutableFst<A> *fst, EncodeMapper<A>* mapper) { mapper->SetInputSymbols(fst->InputSymbols()); mapper->SetOutputSymbols(fst->OutputSymbols()); ArcMap(fst, mapper); } template<class A> inline void Decode(MutableFst<A>* fst, const EncodeMapper<A>& mapper) { ArcMap(fst, EncodeMapper<A>(mapper, DECODE)); RmFinalEpsilon(fst); fst->SetInputSymbols(mapper.InputSymbols()); fst->SetOutputSymbols(mapper.OutputSymbols()); } // On the fly label and/or weight encoding of input Fst // // Complexity: // - Constructor: O(1) // - Traversal: O(nstates_visited + narcs_visited), assuming constant // time to visit an input state or arc. template <class A> class EncodeFst : public ArcMapFst<A, A, EncodeMapper<A> > { public: typedef A Arc; typedef EncodeMapper<A> C; typedef ArcMapFstImpl< A, A, EncodeMapper<A> > Impl; using ImplToFst<Impl>::GetImpl; EncodeFst(const Fst<A> &fst, EncodeMapper<A>* encoder) : ArcMapFst<A, A, C>(fst, encoder, ArcMapFstOptions()) { encoder->SetInputSymbols(fst.InputSymbols()); encoder->SetOutputSymbols(fst.OutputSymbols()); } EncodeFst(const Fst<A> &fst, const EncodeMapper<A>& encoder) : ArcMapFst<A, A, C>(fst, encoder, ArcMapFstOptions()) {} // See Fst<>::Copy() for doc. EncodeFst(const EncodeFst<A> &fst, bool copy = false) : ArcMapFst<A, A, C>(fst, copy) {} // Get a copy of this EncodeFst. See Fst<>::Copy() for further doc. virtual EncodeFst<A> *Copy(bool safe = false) const { if (safe) { FSTERROR() << "EncodeFst::Copy(true): not allowed."; GetImpl()->SetProperties(kError, kError); } return new EncodeFst(*this); } }; // On the fly label and/or weight encoding of input Fst // // Complexity: // - Constructor: O(1) // - Traversal: O(nstates_visited + narcs_visited), assuming constant // time to visit an input state or arc. template <class A> class DecodeFst : public ArcMapFst<A, A, EncodeMapper<A> > { public: typedef A Arc; typedef EncodeMapper<A> C; typedef ArcMapFstImpl< A, A, EncodeMapper<A> > Impl; using ImplToFst<Impl>::GetImpl; DecodeFst(const Fst<A> &fst, const EncodeMapper<A>& encoder) : ArcMapFst<A, A, C>(fst, EncodeMapper<A>(encoder, DECODE), ArcMapFstOptions()) { GetImpl()->SetInputSymbols(encoder.InputSymbols()); GetImpl()->SetOutputSymbols(encoder.OutputSymbols()); } // See Fst<>::Copy() for doc. DecodeFst(const DecodeFst<A> &fst, bool safe = false) : ArcMapFst<A, A, C>(fst, safe) {} // Get a copy of this DecodeFst. See Fst<>::Copy() for further doc. virtual DecodeFst<A> *Copy(bool safe = false) const { return new DecodeFst(*this, safe); } }; // Specialization for EncodeFst. template <class A> class StateIterator< EncodeFst<A> > : public StateIterator< ArcMapFst<A, A, EncodeMapper<A> > > { public: explicit StateIterator(const EncodeFst<A> &fst) : StateIterator< ArcMapFst<A, A, EncodeMapper<A> > >(fst) {} }; // Specialization for EncodeFst. template <class A> class ArcIterator< EncodeFst<A> > : public ArcIterator< ArcMapFst<A, A, EncodeMapper<A> > > { public: ArcIterator(const EncodeFst<A> &fst, typename A::StateId s) : ArcIterator< ArcMapFst<A, A, EncodeMapper<A> > >(fst, s) {} }; // Specialization for DecodeFst. template <class A> class StateIterator< DecodeFst<A> > : public StateIterator< ArcMapFst<A, A, EncodeMapper<A> > > { public: explicit StateIterator(const DecodeFst<A> &fst) : StateIterator< ArcMapFst<A, A, EncodeMapper<A> > >(fst) {} }; // Specialization for DecodeFst. template <class A> class ArcIterator< DecodeFst<A> > : public ArcIterator< ArcMapFst<A, A, EncodeMapper<A> > > { public: ArcIterator(const DecodeFst<A> &fst, typename A::StateId s) : ArcIterator< ArcMapFst<A, A, EncodeMapper<A> > >(fst, s) {} }; // Useful aliases when using StdArc. typedef EncodeFst<StdArc> StdEncodeFst; typedef DecodeFst<StdArc> StdDecodeFst; } // namespace fst #endif // FST_LIB_ENCODE_H__