// $Id$ // vim:tabstop=2 /*********************************************************************** Moses - factored phrase-based language decoder Copyright (C) 2010 Hieu Hoang This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA ***********************************************************************/ #include #include "ChartManager.h" #include "ChartCell.h" #include "ChartHypothesis.h" #include "ChartKBestExtractor.h" #include "ChartTranslationOptions.h" #include "HypergraphOutput.h" #include "StaticData.h" #include "DecodeStep.h" #include "TreeInput.h" #include "moses/FF/StatefulFeatureFunction.h" #include "moses/FF/WordPenaltyProducer.h" #include "moses/OutputCollector.h" #include "moses/ChartKBestExtractor.h" #include "moses/HypergraphOutput.h" #include "moses/TranslationTask.h" using namespace std; namespace Moses { /* constructor. Initialize everything prior to decoding a particular sentence. * \param source the sentence to be decoded * \param system which particular set of models to use. */ ChartManager::ChartManager(ttasksptr const& ttask) : BaseManager(ttask) , m_hypoStackColl(m_source, *this) , m_start(clock()) , m_hypothesisId(0) , m_parser(ttask, m_hypoStackColl) , m_translationOptionList(ttask->options()->syntax.rule_limit, m_source) { } ChartManager::~ChartManager() { clock_t end = clock(); float et = (end - m_start); et /= (float)CLOCKS_PER_SEC; VERBOSE(1, "Translation took " << et << " seconds" << endl); } //! decode the sentence. This contains the main laps. Basically, the CKY++ algorithm void ChartManager::Decode() { VERBOSE(1,"Translating: " << m_source << endl); ResetSentenceStats(m_source); VERBOSE(2,"Decoding: " << endl); //ChartHypothesis::ResetHypoCount(); AddXmlChartOptions(); // MAIN LOOP size_t size = m_source.GetSize(); for (int startPos = size-1; startPos >= 0; --startPos) { for (size_t width = 1; width <= size-startPos; ++width) { size_t endPos = startPos + width - 1; Range range(startPos, endPos); // create trans opt m_translationOptionList.Clear(); m_parser.Create(range, m_translationOptionList); m_translationOptionList.ApplyThreshold(options()->search.trans_opt_threshold); const InputPath &inputPath = m_parser.GetInputPath(range); m_translationOptionList.EvaluateWithSourceContext(m_source, inputPath); // decode ChartCell &cell = m_hypoStackColl.Get(range); cell.Decode(m_translationOptionList, m_hypoStackColl); m_translationOptionList.Clear(); cell.PruneToSize(); cell.CleanupArcList(); cell.SortHypotheses(); } } IFVERBOSE(1) { for (size_t startPos = 0; startPos < size; ++startPos) { cerr.width(3); cerr << startPos << " "; } cerr << endl; for (size_t width = 1; width <= size; width++) { for( size_t space = 0; space < width-1; space++ ) { cerr << " "; } for (size_t startPos = 0; startPos <= size-width; ++startPos) { Range range(startPos, startPos+width-1); cerr.width(3); cerr << m_hypoStackColl.Get(range).GetSize() << " "; } cerr << endl; } } } /** add specific translation options and hypotheses according to the XML override translation scheme. * Doesn't seem to do anything about walls and zones. * @todo check walls & zones. Check that the implementation doesn't leak, xml options sometimes does if you're not careful */ void ChartManager::AddXmlChartOptions() { const std::vector xmlChartOptionsList = m_source.GetXmlChartTranslationOptions(); IFVERBOSE(2) { cerr << "AddXmlChartOptions " << xmlChartOptionsList.size() << endl; } if (xmlChartOptionsList.size() == 0) return; typedef std::vector::const_iterator citer; for(citer i = xmlChartOptionsList.begin(); i != xmlChartOptionsList.end(); ++i) { ChartTranslationOptions* opt = *i; const Range &range = opt->GetSourceWordsRange(); RuleCubeItem* item = new RuleCubeItem( *opt, m_hypoStackColl ); ChartHypothesis* hypo = new ChartHypothesis(*opt, *item, *this); hypo->EvaluateWhenApplied(); ChartCell &cell = m_hypoStackColl.Get(range); cell.AddHypothesis(hypo); } } //! get best complete translation from the top chart cell. const ChartHypothesis *ChartManager::GetBestHypothesis() const { size_t size = m_source.GetSize(); if (size == 0) // empty source return NULL; else { Range range(0, size-1); const ChartCell &lastCell = m_hypoStackColl.Get(range); return lastCell.GetBestHypothesis(); } } /** Calculate the n-best paths through the output hypergraph. * Return the list of paths with the variable ret * \param n how may paths to return * \param ret return argument * \param onlyDistinct whether to check for distinct output sentence or not (default - don't check, just return top n-paths) */ void ChartManager::CalcNBest( std::size_t n, std::vector > &nBestList, bool onlyDistinct) const { nBestList.clear(); if (n == 0 || m_source.GetSize() == 0) { return; } // Get the list of top-level hypotheses, sorted by score. Range range(0, m_source.GetSize()-1); const ChartCell &lastCell = m_hypoStackColl.Get(range); boost::scoped_ptr > topLevelHypos( lastCell.GetAllSortedHypotheses()); if (!topLevelHypos) { return; } ChartKBestExtractor extractor; if (!onlyDistinct) { // Return the n-best list as is, including duplicate translations. extractor.Extract(*topLevelHypos, n, nBestList); return; } // Determine how many derivations to extract. If the n-best list is // restricted to distinct translations then this limit should be bigger // than n. The n-best factor determines how much bigger the limit should be, // with 0 being 'unlimited.' This actually sets a large-ish limit in case // too many translations are identical. const std::size_t nBestFactor = options()->nbest.factor; std::size_t numDerivations = (nBestFactor == 0) ? n*1000 : n*nBestFactor; // Extract the derivations. ChartKBestExtractor::KBestVec bigList; bigList.reserve(numDerivations); extractor.Extract(*topLevelHypos, numDerivations, bigList); // Copy derivations into nBestList, skipping ones with repeated translations. std::set distinct; for (ChartKBestExtractor::KBestVec::const_iterator p = bigList.begin(); nBestList.size() < n && p != bigList.end(); ++p) { boost::shared_ptr derivation = *p; Phrase translation = ChartKBestExtractor::GetOutputPhrase(*derivation); if (distinct.insert(translation).second) { nBestList.push_back(derivation); } } } void ChartManager::WriteSearchGraph(const ChartSearchGraphWriter& writer) const { size_t size = m_source.GetSize(); // which hypotheses are reachable? std::map reachable; Range fullRange(0, size-1); const ChartCell &lastCell = m_hypoStackColl.Get(fullRange); const ChartHypothesis *hypo = lastCell.GetBestHypothesis(); if (hypo == NULL) { // no hypothesis return; } size_t winners = 0; size_t losers = 0; FindReachableHypotheses( hypo, reachable, &winners, &losers); writer.WriteHeader(winners, losers); for (size_t width = 1; width <= size; ++width) { for (size_t startPos = 0; startPos <= size-width; ++startPos) { size_t endPos = startPos + width - 1; Range range(startPos, endPos); TRACE_ERR(" " << range << "="); const ChartCell &cell = m_hypoStackColl.Get(range); cell.WriteSearchGraph(writer, reachable); } } } void ChartManager::FindReachableHypotheses( const ChartHypothesis *hypo, std::map &reachable, size_t* winners, size_t* losers) const { // do not recurse, if already visited if (reachable.find(hypo->GetId()) != reachable.end()) { return; } // recurse reachable[ hypo->GetId() ] = true; if (hypo->GetWinningHypothesis() == hypo) { (*winners)++; } else { (*losers)++; } const std::vector &previous = hypo->GetPrevHypos(); for(std::vector::const_iterator i = previous.begin(); i != previous.end(); ++i) { FindReachableHypotheses( *i, reachable, winners, losers ); } // also loop over recombined hypotheses (arcs) const ChartArcList *arcList = hypo->GetArcList(); if (arcList) { ChartArcList::const_iterator iterArc; for (iterArc = arcList->begin(); iterArc != arcList->end(); ++iterArc) { const ChartHypothesis &arc = **iterArc; FindReachableHypotheses( &arc, reachable, winners, losers ); } } } void ChartManager:: OutputSearchGraphAsHypergraph(std::ostream& out) const { ChartSearchGraphWriterHypergraph writer(options(), &out); WriteSearchGraph(writer); } void ChartManager::OutputSearchGraphMoses(std::ostream &outputSearchGraphStream) const { ChartSearchGraphWriterMoses writer(options(), &outputSearchGraphStream, m_source.GetTranslationId()); WriteSearchGraph(writer); } void ChartManager::OutputBest(OutputCollector *collector) const { const ChartHypothesis *bestHypo = GetBestHypothesis(); if (collector && bestHypo) { const size_t translationId = m_source.GetTranslationId(); const ChartHypothesis *bestHypo = GetBestHypothesis(); OutputBestHypo(collector, bestHypo, translationId); } } void ChartManager::OutputNBest(OutputCollector *collector) const { size_t nBestSize = options()->nbest.nbest_size; if (nBestSize > 0) { const size_t translationId = m_source.GetTranslationId(); VERBOSE(2,"WRITING " << nBestSize << " TRANSLATION ALTERNATIVES TO " << options()->nbest.output_file_path << endl); std::vector > nBestList; CalcNBest(nBestSize, nBestList, options()->nbest.only_distinct); OutputNBestList(collector, nBestList, translationId); IFVERBOSE(2) { PrintUserTime("N-Best Hypotheses Generation Time:"); } } } void ChartManager::OutputNBestList(OutputCollector *collector, const ChartKBestExtractor::KBestVec &nBestList, long translationId) const { std::ostringstream out; if (collector->OutputIsCout()) { // Set precision only if we're writing the n-best list to cout. This is to // preserve existing behaviour, but should probably be done either way. FixPrecision(out); } NBestOptions const& nbo = options()->nbest; bool includeWordAlignment = nbo.include_alignment_info; bool PrintNBestTrees = nbo.print_trees; for (ChartKBestExtractor::KBestVec::const_iterator p = nBestList.begin(); p != nBestList.end(); ++p) { const ChartKBestExtractor::Derivation &derivation = **p; // get the derivation's target-side yield Phrase outputPhrase = ChartKBestExtractor::GetOutputPhrase(derivation); // delete and UTIL_THROW_IF2(outputPhrase.GetSize() < 2, "Output phrase should have contained at least 2 words (beginning and end-of-sentence)"); outputPhrase.RemoveWord(0); outputPhrase.RemoveWord(outputPhrase.GetSize() - 1); // print the translation ID, surface factors, and scores out << translationId << " ||| "; OutputSurface(out, outputPhrase); // , outputFactorOrder, false); out << " ||| "; boost::shared_ptr scoreBreakdown = ChartKBestExtractor::GetOutputScoreBreakdown(derivation); bool with_labels = options()->nbest.include_feature_labels; scoreBreakdown->OutputAllFeatureScores(out, with_labels); out << " ||| " << derivation.score; // optionally, print word alignments if (includeWordAlignment) { out << " ||| "; Alignments align; OutputAlignmentNBest(align, derivation, 0); for (Alignments::const_iterator q = align.begin(); q != align.end(); ++q) { out << q->first << "-" << q->second << " "; } } // optionally, print tree if (PrintNBestTrees) { TreePointer tree = ChartKBestExtractor::GetOutputTree(derivation); out << " ||| " << tree->GetString(); } out << std::endl; } assert(collector); collector->Write(translationId, out.str()); } size_t ChartManager::CalcSourceSize(const Moses::ChartHypothesis *hypo) const { size_t ret = hypo->GetCurrSourceRange().GetNumWordsCovered(); const std::vector &prevHypos = hypo->GetPrevHypos(); for (size_t i = 0; i < prevHypos.size(); ++i) { size_t childSize = prevHypos[i]->GetCurrSourceRange().GetNumWordsCovered(); ret -= (childSize - 1); } return ret; } size_t ChartManager::OutputAlignmentNBest( Alignments &retAlign, const Moses::ChartKBestExtractor::Derivation &derivation, size_t startTarget) const { const ChartHypothesis &hypo = derivation.edge.head->hypothesis; size_t totalTargetSize = 0; size_t startSource = hypo.GetCurrSourceRange().GetStartPos(); const TargetPhrase &tp = hypo.GetCurrTargetPhrase(); size_t thisSourceSize = CalcSourceSize(&hypo); // position of each terminal word in translation rule, irrespective of alignment // if non-term, number is undefined vector sourceOffsets(thisSourceSize, 0); vector targetOffsets(tp.GetSize(), 0); const AlignmentInfo &aiNonTerm = hypo.GetCurrTargetPhrase().GetAlignNonTerm(); vector sourceInd2pos = aiNonTerm.GetSourceIndex2PosMap(); const AlignmentInfo::NonTermIndexMap &targetPos2SourceInd = aiNonTerm.GetNonTermIndexMap(); UTIL_THROW_IF2(sourceInd2pos.size() != derivation.subderivations.size(), "Error"); size_t targetInd = 0; for (size_t targetPos = 0; targetPos < tp.GetSize(); ++targetPos) { if (tp.GetWord(targetPos).IsNonTerminal()) { UTIL_THROW_IF2(targetPos >= targetPos2SourceInd.size(), "Error"); size_t sourceInd = targetPos2SourceInd[targetPos]; size_t sourcePos = sourceInd2pos[sourceInd]; const Moses::ChartKBestExtractor::Derivation &subderivation = *derivation.subderivations[sourceInd]; // calc source size size_t sourceSize = subderivation.edge.head->hypothesis.GetCurrSourceRange().GetNumWordsCovered(); sourceOffsets[sourcePos] = sourceSize; // calc target size. // Recursively look thru child hypos size_t currStartTarget = startTarget + totalTargetSize; size_t targetSize = OutputAlignmentNBest(retAlign, subderivation, currStartTarget); targetOffsets[targetPos] = targetSize; totalTargetSize += targetSize; ++targetInd; } else { ++totalTargetSize; } } // convert position within translation rule to absolute position within // source sentence / output sentence ShiftOffsets(sourceOffsets, startSource); ShiftOffsets(targetOffsets, startTarget); // get alignments from this hypo const AlignmentInfo &aiTerm = hypo.GetCurrTargetPhrase().GetAlignTerm(); // add to output arg, offsetting by source & target AlignmentInfo::const_iterator iter; for (iter = aiTerm.begin(); iter != aiTerm.end(); ++iter) { const std::pair &align = *iter; size_t relSource = align.first; size_t relTarget = align.second; size_t absSource = sourceOffsets[relSource]; size_t absTarget = targetOffsets[relTarget]; pair alignPoint(absSource, absTarget); pair ret = retAlign.insert(alignPoint); UTIL_THROW_IF2(!ret.second, "Error"); } return totalTargetSize; } void ChartManager::OutputAlignment(OutputCollector *collector) const { if (collector == NULL) { return; } ostringstream out; const ChartHypothesis *hypo = GetBestHypothesis(); if (hypo) { Alignments retAlign; OutputAlignment(retAlign, hypo, 0); // output alignments Alignments::const_iterator iter; for (iter = retAlign.begin(); iter != retAlign.end(); ++iter) { const pair &alignPoint = *iter; out << alignPoint.first << "-" << alignPoint.second << " "; } } out << endl; collector->Write(m_source.GetTranslationId(), out.str()); } size_t ChartManager::OutputAlignment(Alignments &retAlign, const Moses::ChartHypothesis *hypo, size_t startTarget) const { size_t totalTargetSize = 0; size_t startSource = hypo->GetCurrSourceRange().GetStartPos(); const TargetPhrase &tp = hypo->GetCurrTargetPhrase(); size_t thisSourceSize = CalcSourceSize(hypo); // position of each terminal word in translation rule, irrespective of alignment // if non-term, number is undefined vector sourceOffsets(thisSourceSize, 0); vector targetOffsets(tp.GetSize(), 0); const vector &prevHypos = hypo->GetPrevHypos(); const AlignmentInfo &aiNonTerm = hypo->GetCurrTargetPhrase().GetAlignNonTerm(); vector sourceInd2pos = aiNonTerm.GetSourceIndex2PosMap(); const AlignmentInfo::NonTermIndexMap &targetPos2SourceInd = aiNonTerm.GetNonTermIndexMap(); UTIL_THROW_IF2(sourceInd2pos.size() != prevHypos.size(), "Error"); size_t targetInd = 0; for (size_t targetPos = 0; targetPos < tp.GetSize(); ++targetPos) { if (tp.GetWord(targetPos).IsNonTerminal()) { UTIL_THROW_IF2(targetPos >= targetPos2SourceInd.size(), "Error"); size_t sourceInd = targetPos2SourceInd[targetPos]; size_t sourcePos = sourceInd2pos[sourceInd]; const ChartHypothesis *prevHypo = prevHypos[sourceInd]; // calc source size size_t sourceSize = prevHypo->GetCurrSourceRange().GetNumWordsCovered(); sourceOffsets[sourcePos] = sourceSize; // calc target size. // Recursively look thru child hypos size_t currStartTarget = startTarget + totalTargetSize; size_t targetSize = OutputAlignment(retAlign, prevHypo, currStartTarget); targetOffsets[targetPos] = targetSize; totalTargetSize += targetSize; ++targetInd; } else { ++totalTargetSize; } } // convert position within translation rule to absolute position within // source sentence / output sentence ShiftOffsets(sourceOffsets, startSource); ShiftOffsets(targetOffsets, startTarget); // get alignments from this hypo const AlignmentInfo &aiTerm = hypo->GetCurrTargetPhrase().GetAlignTerm(); // add to output arg, offsetting by source & target AlignmentInfo::const_iterator iter; for (iter = aiTerm.begin(); iter != aiTerm.end(); ++iter) { const std::pair &align = *iter; size_t relSource = align.first; size_t relTarget = align.second; size_t absSource = sourceOffsets[relSource]; size_t absTarget = targetOffsets[relTarget]; pair alignPoint(absSource, absTarget); pair ret = retAlign.insert(alignPoint); UTIL_THROW_IF2(!ret.second, "Error"); } return totalTargetSize; } void ChartManager::OutputDetailedTranslationReport(OutputCollector *collector) const { if (collector) { OutputDetailedTranslationReport(collector, GetBestHypothesis(), static_cast(m_source), m_source.GetTranslationId()); } } void ChartManager::OutputDetailedTranslationReport( OutputCollector *collector, const ChartHypothesis *hypo, const Sentence &sentence, long translationId) const { if (hypo == NULL) { return; } std::ostringstream out; ApplicationContext applicationContext; OutputTranslationOptions(out, applicationContext, hypo, sentence, translationId); collector->Write(translationId, out.str()); //DIMw if (options()->output.detailed_all_transrep_filepath.size()) { const Sentence &sentence = static_cast(m_source); size_t nBestSize = options()->nbest.nbest_size; std::vector > nBestList; CalcNBest(nBestSize, nBestList, options()->nbest.only_distinct); OutputDetailedAllTranslationReport(collector, nBestList, sentence, translationId); } } void ChartManager::OutputTranslationOptions(std::ostream &out, ApplicationContext &applicationContext, const ChartHypothesis *hypo, const Sentence &sentence, long translationId) const { if (hypo != NULL) { OutputTranslationOption(out, applicationContext, hypo, sentence, translationId); out << std::endl; } // recursive const std::vector &prevHypos = hypo->GetPrevHypos(); std::vector::const_iterator iter; for (iter = prevHypos.begin(); iter != prevHypos.end(); ++iter) { const ChartHypothesis *prevHypo = *iter; OutputTranslationOptions(out, applicationContext, prevHypo, sentence, translationId); } } void ChartManager::OutputTranslationOption(std::ostream &out, ApplicationContext &applicationContext, const ChartHypothesis *hypo, const Sentence &sentence, long translationId) const { ReconstructApplicationContext(*hypo, sentence, applicationContext); out << "Trans Opt " << translationId << " " << hypo->GetCurrSourceRange() << ": "; WriteApplicationContext(out, applicationContext); out << ": " << hypo->GetCurrTargetPhrase().GetTargetLHS() << "->" << hypo->GetCurrTargetPhrase() << " " << hypo->GetFutureScore() << hypo->GetScoreBreakdown(); } // Given a hypothesis and sentence, reconstructs the 'application context' -- // the source RHS symbols of the SCFG rule that was applied, plus their spans. void ChartManager::ReconstructApplicationContext(const ChartHypothesis &hypo, const Sentence &sentence, ApplicationContext &context) const { context.clear(); const std::vector &prevHypos = hypo.GetPrevHypos(); std::vector::const_iterator p = prevHypos.begin(); std::vector::const_iterator end = prevHypos.end(); const Range &span = hypo.GetCurrSourceRange(); size_t i = span.GetStartPos(); while (i <= span.GetEndPos()) { if (p == end || i < (*p)->GetCurrSourceRange().GetStartPos()) { // Symbol is a terminal. const Word &symbol = sentence.GetWord(i); context.push_back(std::make_pair(symbol, Range(i, i))); ++i; } else { // Symbol is a non-terminal. const Word &symbol = (*p)->GetTargetLHS(); const Range &range = (*p)->GetCurrSourceRange(); context.push_back(std::make_pair(symbol, range)); i = range.GetEndPos()+1; ++p; } } } void ChartManager::OutputUnknowns(OutputCollector *collector) const { if (collector) { long translationId = m_source.GetTranslationId(); const std::vector &oovs = GetParser().GetUnknownSources(); std::ostringstream out; for (std::vector::const_iterator p = oovs.begin(); p != oovs.end(); ++p) { out << **p; } out << std::endl; collector->Write(translationId, out.str()); } } void ChartManager::OutputDetailedTreeFragmentsTranslationReport(OutputCollector *collector) const { const ChartHypothesis *hypo = GetBestHypothesis(); if (collector == NULL || hypo == NULL) { return; } std::ostringstream out; ApplicationContext applicationContext; const Sentence &sentence = static_cast(m_source); const size_t translationId = m_source.GetTranslationId(); OutputTreeFragmentsTranslationOptions(out, applicationContext, hypo, sentence, translationId); //Tree of full sentence const StatefulFeatureFunction* treeStructure; treeStructure = StaticData::Instance().GetTreeStructure(); if (treeStructure != NULL) { const vector& sff = StatefulFeatureFunction::GetStatefulFeatureFunctions(); for( size_t i=0; i(hypo->GetFFState(i)); out << "Full Tree " << translationId << ": " << tree->GetTree()->GetString() << "\n"; break; } } } collector->Write(translationId, out.str()); } void ChartManager::OutputTreeFragmentsTranslationOptions(std::ostream &out, ApplicationContext &applicationContext, const ChartHypothesis *hypo, const Sentence &sentence, long translationId) const { if (hypo != NULL) { OutputTranslationOption(out, applicationContext, hypo, sentence, translationId); const TargetPhrase &currTarPhr = hypo->GetCurrTargetPhrase(); out << " ||| "; if (const PhraseProperty *property = currTarPhr.GetProperty("Tree")) { out << " " << *property->GetValueString(); } else { out << " " << "noTreeInfo"; } out << std::endl; } // recursive const std::vector &prevHypos = hypo->GetPrevHypos(); std::vector::const_iterator iter; for (iter = prevHypos.begin(); iter != prevHypos.end(); ++iter) { const ChartHypothesis *prevHypo = *iter; OutputTreeFragmentsTranslationOptions(out, applicationContext, prevHypo, sentence, translationId); } } void ChartManager::OutputSearchGraph(OutputCollector *collector) const { if (collector) { long translationId = m_source.GetTranslationId(); std::ostringstream out; OutputSearchGraphMoses( out); collector->Write(translationId, out.str()); } } //DIMw void ChartManager::OutputDetailedAllTranslationReport( OutputCollector *collector, const std::vector > &nBestList, const Sentence &sentence, long translationId) const { std::ostringstream out; ApplicationContext applicationContext; const ChartCellCollection& cells = GetChartCellCollection(); size_t size = GetSource().GetSize(); for (size_t width = 1; width <= size; ++width) { for (size_t startPos = 0; startPos <= size-width; ++startPos) { size_t endPos = startPos + width - 1; Range range(startPos, endPos); const ChartCell& cell = cells.Get(range); const HypoList* hyps = cell.GetAllSortedHypotheses(); out << "Chart Cell [" << startPos << ".." << endPos << "]" << endl; HypoList::const_iterator iter; size_t c = 1; for (iter = hyps->begin(); iter != hyps->end(); ++iter) { out << "----------------Item " << c++ << " ---------------------" << endl; OutputTranslationOptions(out, applicationContext, *iter, sentence, translationId); } } } collector->Write(translationId, out.str()); } void ChartManager::OutputBestHypo(OutputCollector *collector, const ChartHypothesis *hypo, long translationId) const { if (!collector) return; std::ostringstream out; FixPrecision(out); if (hypo != NULL) { VERBOSE(1,"BEST TRANSLATION: " << *hypo << endl); VERBOSE(3,"Best path: "); Backtrack(hypo); VERBOSE(3,"0" << std::endl); if (options()->output.ReportHypoScore) { out << hypo->GetFutureScore() << " "; } if (options()->output.RecoverPath) { out << "||| "; } Phrase outPhrase(ARRAY_SIZE_INCR); hypo->GetOutputPhrase(outPhrase); // delete 1st & last UTIL_THROW_IF2(outPhrase.GetSize() < 2, "Output phrase should have contained at least 2 words (beginning and end-of-sentence)"); outPhrase.RemoveWord(0); outPhrase.RemoveWord(outPhrase.GetSize() - 1); string output = outPhrase.GetStringRep(options()->output.factor_order); out << output << endl; } else { VERBOSE(1, "NO BEST TRANSLATION" << endl); if (options()->output.ReportHypoScore) { out << "0 "; } out << endl; } collector->Write(translationId, out.str()); } void ChartManager::Backtrack(const ChartHypothesis *hypo) const { const vector &prevHypos = hypo->GetPrevHypos(); vector::const_iterator iter; for (iter = prevHypos.begin(); iter != prevHypos.end(); ++iter) { const ChartHypothesis *prevHypo = *iter; VERBOSE(3,prevHypo->GetId() << " <= "); Backtrack(prevHypo); } } } // namespace Moses