|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
#include "ChartKBestExtractor.h" |
|
|
|
#include "ChartHypothesis.h" |
|
#include "ScoreComponentCollection.h" |
|
#include "StaticData.h" |
|
|
|
#include <boost/scoped_ptr.hpp> |
|
|
|
#include <vector> |
|
|
|
using namespace std; |
|
|
|
namespace Moses |
|
{ |
|
|
|
|
|
void ChartKBestExtractor::Extract( |
|
const std::vector<const ChartHypothesis*> &topLevelHypos, std::size_t k, |
|
KBestVec &kBestList) |
|
{ |
|
kBestList.clear(); |
|
if (topLevelHypos.empty()) { |
|
return; |
|
} |
|
|
|
|
|
|
|
std::vector<const ChartHypothesis*>::const_iterator p = topLevelHypos.begin(); |
|
const ChartHypothesis &bestTopLevelHypo = **p; |
|
boost::scoped_ptr<ChartHypothesis> supremeHypo( |
|
new ChartHypothesis(bestTopLevelHypo, *this)); |
|
|
|
|
|
|
|
|
|
for (++p; p != topLevelHypos.end(); ++p) { |
|
|
|
UTIL_THROW_IF2((*p)->GetFutureScore() > bestTopLevelHypo.GetFutureScore(), |
|
"top-level hypotheses are not correctly sorted"); |
|
|
|
|
|
ChartHypothesis *altHypo = new ChartHypothesis(**p, *this); |
|
supremeHypo->AddArc(altHypo); |
|
} |
|
|
|
|
|
boost::shared_ptr<Vertex> targetVertex = FindOrCreateVertex(*supremeHypo); |
|
LazyKthBest(*targetVertex, k, k); |
|
|
|
|
|
|
|
kBestList.reserve(targetVertex->kBestList.size()); |
|
for (std::vector<boost::weak_ptr<Derivation> >::const_iterator |
|
q = targetVertex->kBestList.begin(); |
|
q != targetVertex->kBestList.end(); ++q) { |
|
const boost::shared_ptr<Derivation> d(*q); |
|
assert(d); |
|
assert(d->subderivations.size() == 1); |
|
kBestList.push_back(d->subderivations[0]); |
|
} |
|
} |
|
|
|
|
|
Phrase ChartKBestExtractor::GetOutputPhrase(const Derivation &d) |
|
{ |
|
FactorType placeholderFactor = StaticData::Instance().options()->input.placeholder_factor; |
|
|
|
Phrase ret(ARRAY_SIZE_INCR); |
|
|
|
const ChartHypothesis &hypo = d.edge.head->hypothesis; |
|
const TargetPhrase &phrase = hypo.GetCurrTargetPhrase(); |
|
const AlignmentInfo::NonTermIndexMap &nonTermIndexMap = |
|
phrase.GetAlignNonTerm().GetNonTermIndexMap(); |
|
for (std::size_t pos = 0; pos < phrase.GetSize(); ++pos) { |
|
const Word &word = phrase.GetWord(pos); |
|
if (word.IsNonTerminal()) { |
|
std::size_t nonTermInd = nonTermIndexMap[pos]; |
|
const Derivation &subderivation = *d.subderivations[nonTermInd]; |
|
Phrase subPhrase = GetOutputPhrase(subderivation); |
|
ret.Append(subPhrase); |
|
} else { |
|
ret.AddWord(word); |
|
if (placeholderFactor == NOT_FOUND) { |
|
continue; |
|
} |
|
std::set<std::size_t> sourcePosSet = |
|
phrase.GetAlignTerm().GetAlignmentsForTarget(pos); |
|
if (sourcePosSet.size() == 1) { |
|
const std::vector<const Word*> *ruleSourceFromInputPath = |
|
hypo.GetTranslationOption().GetSourceRuleFromInputPath(); |
|
UTIL_THROW_IF2(ruleSourceFromInputPath == NULL, |
|
"Source Words in of the rules hasn't been filled out"); |
|
std::size_t sourcePos = *sourcePosSet.begin(); |
|
const Word *sourceWord = ruleSourceFromInputPath->at(sourcePos); |
|
UTIL_THROW_IF2(sourceWord == NULL, |
|
"Null source word at position " << sourcePos); |
|
const Factor *factor = sourceWord->GetFactor(placeholderFactor); |
|
if (factor) { |
|
ret.Back()[0] = factor; |
|
} |
|
} |
|
} |
|
} |
|
|
|
return ret; |
|
} |
|
|
|
|
|
boost::shared_ptr<ScoreComponentCollection> |
|
ChartKBestExtractor::GetOutputScoreBreakdown(const Derivation &d) |
|
{ |
|
const ChartHypothesis &hypo = d.edge.head->hypothesis; |
|
boost::shared_ptr<ScoreComponentCollection> scoreBreakdown(new ScoreComponentCollection()); |
|
scoreBreakdown->PlusEquals(hypo.GetDeltaScoreBreakdown()); |
|
const TargetPhrase &phrase = hypo.GetCurrTargetPhrase(); |
|
const AlignmentInfo::NonTermIndexMap &nonTermIndexMap = |
|
phrase.GetAlignNonTerm().GetNonTermIndexMap(); |
|
for (std::size_t pos = 0; pos < phrase.GetSize(); ++pos) { |
|
const Word &word = phrase.GetWord(pos); |
|
if (word.IsNonTerminal()) { |
|
std::size_t nonTermInd = nonTermIndexMap[pos]; |
|
const Derivation &subderivation = *d.subderivations[nonTermInd]; |
|
scoreBreakdown->PlusEquals(*GetOutputScoreBreakdown(subderivation)); |
|
} |
|
} |
|
|
|
return scoreBreakdown; |
|
} |
|
|
|
|
|
TreePointer ChartKBestExtractor::GetOutputTree(const Derivation &d) |
|
{ |
|
const ChartHypothesis &hypo = d.edge.head->hypothesis; |
|
const TargetPhrase &phrase = hypo.GetCurrTargetPhrase(); |
|
if (const PhraseProperty *property = phrase.GetProperty("Tree")) { |
|
const std::string *tree = property->GetValueString(); |
|
TreePointer mytree (boost::make_shared<InternalTree>(*tree)); |
|
|
|
|
|
std::vector<TreePointer> previous_trees; |
|
for (size_t pos = 0; pos < phrase.GetSize(); ++pos) { |
|
const Word &word = phrase.GetWord(pos); |
|
if (word.IsNonTerminal()) { |
|
size_t nonTermInd = phrase.GetAlignNonTerm().GetNonTermIndexMap()[pos]; |
|
const Derivation &subderivation = *d.subderivations[nonTermInd]; |
|
const TreePointer prev_tree = GetOutputTree(subderivation); |
|
previous_trees.push_back(prev_tree); |
|
} |
|
} |
|
|
|
mytree->Combine(previous_trees); |
|
mytree->Unbinarize(); |
|
return mytree; |
|
} else { |
|
UTIL_THROW2("Error: k-best tree output active, but no internal tree structure found"); |
|
} |
|
} |
|
|
|
|
|
ChartKBestExtractor::UnweightedHyperarc ChartKBestExtractor::CreateEdge( |
|
const ChartHypothesis &h) |
|
{ |
|
UnweightedHyperarc edge; |
|
edge.head = FindOrCreateVertex(h); |
|
const std::vector<const ChartHypothesis*> &prevHypos = h.GetPrevHypos(); |
|
edge.tail.resize(prevHypos.size()); |
|
for (std::size_t i = 0; i < prevHypos.size(); ++i) { |
|
const ChartHypothesis *prevHypo = prevHypos[i]; |
|
edge.tail[i] = FindOrCreateVertex(*prevHypo); |
|
} |
|
return edge; |
|
} |
|
|
|
|
|
|
|
boost::shared_ptr<ChartKBestExtractor::Vertex> |
|
ChartKBestExtractor::FindOrCreateVertex(const ChartHypothesis &h) |
|
{ |
|
VertexMap::value_type element(&h, boost::shared_ptr<Vertex>()); |
|
std::pair<VertexMap::iterator, bool> p = m_vertexMap.insert(element); |
|
boost::shared_ptr<Vertex> &sp = p.first->second; |
|
if (!p.second) { |
|
return sp; |
|
} |
|
sp.reset(new Vertex(h)); |
|
|
|
UnweightedHyperarc bestEdge; |
|
bestEdge.head = sp; |
|
const std::vector<const ChartHypothesis*> &prevHypos = h.GetPrevHypos(); |
|
bestEdge.tail.resize(prevHypos.size()); |
|
for (std::size_t i = 0; i < prevHypos.size(); ++i) { |
|
const ChartHypothesis *prevHypo = prevHypos[i]; |
|
bestEdge.tail[i] = FindOrCreateVertex(*prevHypo); |
|
} |
|
boost::shared_ptr<Derivation> bestDerivation(new Derivation(bestEdge)); |
|
#ifndef NDEBUG |
|
std::pair<DerivationSet::iterator, bool> q = |
|
#endif |
|
m_derivations.insert(bestDerivation); |
|
assert(q.second); |
|
sp->kBestList.push_back(bestDerivation); |
|
return sp; |
|
} |
|
|
|
|
|
|
|
void ChartKBestExtractor::GetCandidates(Vertex &v, std::size_t k) |
|
{ |
|
|
|
|
|
|
|
|
|
const ChartArcList *arcList = v.hypothesis.GetArcList(); |
|
if (arcList) { |
|
for (std::size_t i = 0; i < arcList->size(); ++i) { |
|
const ChartHypothesis &recombinedHypo = *(*arcList)[i]; |
|
boost::shared_ptr<Vertex> w = FindOrCreateVertex(recombinedHypo); |
|
assert(w->kBestList.size() == 1); |
|
v.candidates.push(w->kBestList[0]); |
|
} |
|
} |
|
} |
|
|
|
|
|
void ChartKBestExtractor::LazyKthBest(Vertex &v, std::size_t k, |
|
std::size_t globalK) |
|
{ |
|
|
|
if (v.visited == false) { |
|
|
|
assert(v.kBestList.size() == 1); |
|
|
|
GetCandidates(v, globalK); |
|
v.visited = true; |
|
} |
|
|
|
|
|
while (v.kBestList.size() < k) { |
|
assert(!v.kBestList.empty()); |
|
|
|
|
|
boost::shared_ptr<Derivation> d(v.kBestList.back()); |
|
LazyNext(v, *d, globalK); |
|
|
|
if (v.candidates.empty()) { |
|
break; |
|
} |
|
|
|
boost::weak_ptr<Derivation> next = v.candidates.top(); |
|
v.candidates.pop(); |
|
|
|
v.kBestList.push_back(next); |
|
} |
|
} |
|
|
|
|
|
void ChartKBestExtractor::LazyNext(Vertex &v, const Derivation &d, |
|
std::size_t globalK) |
|
{ |
|
for (std::size_t i = 0; i < d.edge.tail.size(); ++i) { |
|
Vertex &pred = *d.edge.tail[i]; |
|
|
|
std::size_t k = d.backPointers[i] + 2; |
|
LazyKthBest(pred, k, globalK); |
|
if (pred.kBestList.size() < k) { |
|
|
|
continue; |
|
} |
|
|
|
boost::shared_ptr<Derivation> next(new Derivation(d, i)); |
|
|
|
std::pair<DerivationSet::iterator, bool> p = m_derivations.insert(next); |
|
if (p.second) { |
|
v.candidates.push(next); |
|
} |
|
} |
|
} |
|
|
|
|
|
ChartKBestExtractor::Derivation::Derivation(const UnweightedHyperarc &e) |
|
{ |
|
edge = e; |
|
std::size_t arity = edge.tail.size(); |
|
backPointers.resize(arity, 0); |
|
subderivations.reserve(arity); |
|
for (std::size_t i = 0; i < arity; ++i) { |
|
const Vertex &pred = *edge.tail[i]; |
|
assert(pred.kBestList.size() >= 1); |
|
boost::shared_ptr<Derivation> sub(pred.kBestList[0]); |
|
subderivations.push_back(sub); |
|
} |
|
score = edge.head->hypothesis.GetFutureScore(); |
|
} |
|
|
|
|
|
ChartKBestExtractor::Derivation::Derivation(const Derivation &d, std::size_t i) |
|
{ |
|
edge.head = d.edge.head; |
|
edge.tail = d.edge.tail; |
|
backPointers = d.backPointers; |
|
subderivations = d.subderivations; |
|
std::size_t j = ++backPointers[i]; |
|
score = d.score; |
|
|
|
score -= subderivations[i]->score; |
|
|
|
boost::shared_ptr<Derivation> newSub(edge.tail[i]->kBestList[j]); |
|
subderivations[i] = newSub; |
|
|
|
score += subderivations[i]->score; |
|
} |
|
|
|
} |
|
|