// test-properties.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 // Functions to manipulate and test property bits #ifndef FST_LIB_TEST_PROPERTIES_H__ #define FST_LIB_TEST_PROPERTIES_H__ #include <tr1/unordered_set> using std::tr1::unordered_set; using std::tr1::unordered_multiset; #include <fst/dfs-visit.h> #include <fst/connect.h> DECLARE_bool(fst_verify_properties); namespace fst { // For a binary property, the bit is always returned set. // For a trinary (i.e. two-bit) property, both bits are // returned set iff either corresponding input bit is set. inline uint64 KnownProperties(uint64 props) { return kBinaryProperties | (props & kTrinaryProperties) | ((props & kPosTrinaryProperties) << 1) | ((props & kNegTrinaryProperties) >> 1); } // Tests compatibility between two sets of properties inline bool CompatProperties(uint64 props1, uint64 props2) { uint64 known_props1 = KnownProperties(props1); uint64 known_props2 = KnownProperties(props2); uint64 known_props = known_props1 & known_props2; uint64 incompat_props = (props1 & known_props) ^ (props2 & known_props); if (incompat_props) { uint64 prop = 1; for (int i = 0; i < 64; ++i, prop <<= 1) if (prop & incompat_props) LOG(ERROR) << "CompatProperties: mismatch: " << PropertyNames[i] << ": props1 = " << (props1 & prop ? "true" : "false") << ", props2 = " << (props2 & prop ? "true" : "false"); return false; } else { return true; } } // Computes FST property values defined in properties.h. The value of // each property indicated in the mask will be determined and returned // (these will never be unknown here). In the course of determining // the properties specifically requested in the mask, certain other // properties may be determined (those with little additional expense) // and their values will be returned as well. The complete set of // known properties (whether true or false) determined by this // operation will be assigned to the the value pointed to by KNOWN. // If 'use_stored' is true, pre-computed FST properties may be used // when possible. This routine is seldom called directly; instead it // is used to implement fst.Properties(mask, true). template<class Arc> uint64 ComputeProperties(const Fst<Arc> &fst, uint64 mask, uint64 *known, bool use_stored) { typedef typename Arc::Label Label; typedef typename Arc::Weight Weight; typedef typename Arc::StateId StateId; uint64 fst_props = fst.Properties(kFstProperties, false); // Fst-stored // Check stored FST properties first if allowed. if (use_stored) { uint64 known_props = KnownProperties(fst_props); // If FST contains required info, return it. if ((known_props & mask) == mask) { *known = known_props; return fst_props; } } // Compute (trinary) properties explicitly. // Initialize with binary properties (already known). uint64 comp_props = fst_props & kBinaryProperties; // Compute these trinary properties with a DFS. We compute only those // that need a DFS here, since we otherwise would like to avoid a DFS // since its stack could grow large. uint64 dfs_props = kCyclic | kAcyclic | kInitialCyclic | kInitialAcyclic | kAccessible | kNotAccessible | kCoAccessible | kNotCoAccessible; if (mask & dfs_props) { SccVisitor<Arc> scc_visitor(&comp_props); DfsVisit(fst, &scc_visitor); } // Compute any remaining trinary properties via a state and arcs iterations if (mask & ~(kBinaryProperties | dfs_props)) { comp_props |= kAcceptor | kNoEpsilons | kNoIEpsilons | kNoOEpsilons | kILabelSorted | kOLabelSorted | kUnweighted | kTopSorted | kString; if (mask & (kIDeterministic | kNonIDeterministic)) comp_props |= kIDeterministic; if (mask & (kODeterministic | kNonODeterministic)) comp_props |= kODeterministic; unordered_set<Label> *ilabels = 0; unordered_set<Label> *olabels = 0; StateId nfinal = 0; for (StateIterator< Fst<Arc> > siter(fst); !siter.Done(); siter.Next()) { StateId s = siter.Value(); Arc prev_arc; // Create these only if we need to if (mask & (kIDeterministic | kNonIDeterministic)) ilabels = new unordered_set<Label>; if (mask & (kODeterministic | kNonODeterministic)) olabels = new unordered_set<Label>; bool first_arc = true; for (ArcIterator< Fst<Arc> > aiter(fst, s); !aiter.Done(); aiter.Next()) { const Arc &arc =aiter.Value(); if (ilabels && ilabels->find(arc.ilabel) != ilabels->end()) { comp_props |= kNonIDeterministic; comp_props &= ~kIDeterministic; } if (olabels && olabels->find(arc.olabel) != olabels->end()) { comp_props |= kNonODeterministic; comp_props &= ~kODeterministic; } if (arc.ilabel != arc.olabel) { comp_props |= kNotAcceptor; comp_props &= ~kAcceptor; } if (arc.ilabel == 0 && arc.olabel == 0) { comp_props |= kEpsilons; comp_props &= ~kNoEpsilons; } if (arc.ilabel == 0) { comp_props |= kIEpsilons; comp_props &= ~kNoIEpsilons; } if (arc.olabel == 0) { comp_props |= kOEpsilons; comp_props &= ~kNoOEpsilons; } if (!first_arc) { if (arc.ilabel < prev_arc.ilabel) { comp_props |= kNotILabelSorted; comp_props &= ~kILabelSorted; } if (arc.olabel < prev_arc.olabel) { comp_props |= kNotOLabelSorted; comp_props &= ~kOLabelSorted; } } if (arc.weight != Weight::One() && arc.weight != Weight::Zero()) { comp_props |= kWeighted; comp_props &= ~kUnweighted; } if (arc.nextstate <= s) { comp_props |= kNotTopSorted; comp_props &= ~kTopSorted; } if (arc.nextstate != s + 1) { comp_props |= kNotString; comp_props &= ~kString; } prev_arc = arc; first_arc = false; if (ilabels) ilabels->insert(arc.ilabel); if (olabels) olabels->insert(arc.olabel); } if (nfinal > 0) { // final state not last comp_props |= kNotString; comp_props &= ~kString; } Weight final = fst.Final(s); if (final != Weight::Zero()) { // final state if (final != Weight::One()) { comp_props |= kWeighted; comp_props &= ~kUnweighted; } ++nfinal; } else { // non-final state if (fst.NumArcs(s) != 1) { comp_props |= kNotString; comp_props &= ~kString; } } delete ilabels; delete olabels; } if (fst.Start() != kNoStateId && fst.Start() != 0) { comp_props |= kNotString; comp_props &= ~kString; } } *known = KnownProperties(comp_props); return comp_props; } // This is a wrapper around ComputeProperties that will cause a fatal // error if the stored properties and the computed properties are // incompatible when 'FLAGS_fst_verify_properties' is true. This // routine is seldom called directly; instead it is used to implement // fst.Properties(mask, true). template<class Arc> uint64 TestProperties(const Fst<Arc> &fst, uint64 mask, uint64 *known) { if (FLAGS_fst_verify_properties) { uint64 stored_props = fst.Properties(kFstProperties, false); uint64 computed_props = ComputeProperties(fst, mask, known, false); if (!CompatProperties(stored_props, computed_props)) LOG(FATAL) << "TestProperties: stored Fst properties incorrect" << " (stored: props1, computed: props2)"; return computed_props; } else { return ComputeProperties(fst, mask, known, true); } } } // namespace fst #endif // FST_LIB_TEST_PROPERTIES_H__