00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011 #include "PhraseOrientationFeature.h"
00012 #include "moses/InputFileStream.h"
00013 #include "moses/ScoreComponentCollection.h"
00014 #include "moses/StaticData.h"
00015 #include "moses/Hypothesis.h"
00016 #include "moses/ChartHypothesis.h"
00017 #include "moses/ChartManager.h"
00018 #include <boost/shared_ptr.hpp>
00019
00020
00021 namespace Moses
00022 {
00023 size_t PhraseOrientationFeatureState::hash() const
00024 {
00025 if (!m_distinguishStates) {
00026 return 0;
00027 }
00028
00029 size_t ret = 0;
00030
00031 if (m_leftBoundaryIsSet) {
00032 HashCombineLeftBoundaryRecursive(ret, *this, m_useSparseNT);
00033 }
00034 if (m_rightBoundaryIsSet) {
00035 boost::hash_combine(ret, 42);
00036 HashCombineRightBoundaryRecursive(ret, *this, m_useSparseNT);
00037 }
00038
00039 return 0;
00040 }
00041
00042 bool PhraseOrientationFeatureState::operator==(const FFState& other) const
00043 {
00044 if (!m_distinguishStates) {
00045 return true;
00046 }
00047
00048 const PhraseOrientationFeatureState &otherState = static_cast<const PhraseOrientationFeatureState&>(other);
00049
00050 if (!m_leftBoundaryIsSet && !otherState.m_leftBoundaryIsSet &&
00051 !m_rightBoundaryIsSet && !otherState.m_rightBoundaryIsSet) {
00052 return true;
00053 }
00054 if (m_leftBoundaryIsSet != otherState.m_leftBoundaryIsSet) {
00055 return false;
00056 }
00057 if (m_rightBoundaryIsSet != otherState.m_rightBoundaryIsSet) {
00058 return false;
00059 }
00060
00061 if (m_leftBoundaryIsSet) {
00062 int compareLeft = CompareLeftBoundaryRecursive(*this, otherState, m_useSparseNT);
00063 if (compareLeft != 0) {
00064 return false;
00065 }
00066 }
00067 if (m_rightBoundaryIsSet) {
00068 int compareRight = CompareRightBoundaryRecursive(*this, otherState, m_useSparseNT);
00069 if (compareRight != 0) {
00070 return false;
00071 }
00072 }
00073
00074 return true;
00075 }
00076
00077
00078 const std::string PhraseOrientationFeature::MORIENT("M");
00079 const std::string PhraseOrientationFeature::SORIENT("S");
00080 const std::string PhraseOrientationFeature::DORIENT("D");
00081
00082
00083 PhraseOrientationFeature::PhraseOrientationFeature(const std::string &line)
00084 : StatefulFeatureFunction(6, line)
00085 , m_glueLabelStr("Q")
00086 , m_noScoreBoundary(false)
00087 , m_monotoneScoreBoundary(false)
00088 , m_distinguishStates(false)
00089 , m_lookaheadScore(false)
00090 , m_heuristicScoreUseWeights(false)
00091 , m_useSparseWord(false)
00092 , m_useSparseNT(false)
00093 , m_offsetR2LScores(m_numScoreComponents/2)
00094 , m_useTargetWordList(false)
00095 , m_useSourceWordList(false)
00096 {
00097 VERBOSE(1, "Initializing feature " << GetScoreProducerDescription() << " ...");
00098 ReadParameters();
00099 FactorCollection &factorCollection = FactorCollection::Instance();
00100 m_glueLabel = factorCollection.AddFactor(m_glueLabelStr, true);
00101 VERBOSE(1, " Done." << std::endl);
00102 }
00103
00104
00105 void PhraseOrientationFeature::SetParameter(const std::string& key, const std::string& value)
00106 {
00107 if (key == "glue-label") {
00108 m_glueLabelStr = value;
00109 } else if (key == "no-score-boundary") {
00110 m_noScoreBoundary = Scan<bool>(value);
00111 } else if (key == "monotone-score-boundary") {
00112 m_monotoneScoreBoundary = Scan<bool>(value);
00113 } else if (key == "distinguish-states") {
00114 m_distinguishStates = Scan<bool>(value);
00115 } else if (key == "lookahead-score") {
00116 m_lookaheadScore = Scan<bool>(value);
00117 } else if (key == "heuristic-score-use-weights") {
00118 m_heuristicScoreUseWeights = Scan<bool>(value);
00119 } else if (key == "sparse-word") {
00120 m_useSparseWord = Scan<bool>(value);
00121 } else if (key == "sparse-nt") {
00122 m_useSparseNT = Scan<bool>(value);
00123 } else if (key == "target-word-list") {
00124 m_filenameTargetWordList = value;
00125 } else if (key == "source-word-list") {
00126 m_filenameSourceWordList = value;
00127 } else {
00128 StatefulFeatureFunction::SetParameter(key, value);
00129 }
00130 }
00131
00132
00133 void PhraseOrientationFeature::Load(AllOptions::ptr const& opts)
00134 {
00135 m_options = opts;
00136 if ( !m_filenameTargetWordList.empty() ) {
00137 LoadWordList(m_filenameTargetWordList,m_targetWordList);
00138 m_useTargetWordList = true;
00139 }
00140 if ( !m_filenameSourceWordList.empty() ) {
00141 LoadWordList(m_filenameSourceWordList,m_sourceWordList);
00142 m_useSourceWordList = true;
00143 }
00144 }
00145
00146
00147 void PhraseOrientationFeature::LoadWordList(const std::string& filename,
00148 boost::unordered_set<const Factor*>& list)
00149 {
00150 FEATUREVERBOSE(2, "Loading word list from file " << filename << std::endl);
00151 FactorCollection &factorCollection = FactorCollection::Instance();
00152 list.clear();
00153 std::string line;
00154 InputFileStream inFile(filename);
00155
00156 while (getline(inFile, line)) {
00157 const Factor *factor = factorCollection.AddFactor(line, false);
00158 list.insert(factor);
00159 }
00160
00161 inFile.Close();
00162 }
00163
00164
00165 void PhraseOrientationFeature::EvaluateInIsolation(const Phrase &source,
00166 const TargetPhrase &targetPhrase,
00167 ScoreComponentCollection &scoreBreakdown,
00168 ScoreComponentCollection &estimatedScores) const
00169 {
00170 targetPhrase.SetRuleSource(source);
00171 const Factor* targetPhraseLHS = targetPhrase.GetTargetLHS()[0];
00172
00173 if (const PhraseProperty *property = targetPhrase.GetProperty("Orientation")) {
00174 const OrientationPhraseProperty *orientationPhraseProperty = static_cast<const OrientationPhraseProperty*>(property);
00175 if (m_lookaheadScore) {
00176 LookaheadScore(orientationPhraseProperty, scoreBreakdown, targetPhraseLHS);
00177 }
00178 } else {
00179
00180 UTIL_THROW_IF2(!targetPhrase.GetWord(0).IsOOV(), GetScoreProducerDescription()
00181 << ": Missing Orientation property. "
00182 << "Please check phrase table and glue rules.");
00183 }
00184
00185 IFFEATUREVERBOSE(2) {
00186 FEATUREVERBOSE(2, "BEGIN========EvaluateInIsolation========" << std::endl);
00187 FEATUREVERBOSE(2, source << std::endl);
00188 FEATUREVERBOSE(2, targetPhrase << std::endl);
00189
00190 for (AlignmentInfo::const_iterator it=targetPhrase.GetAlignTerm().begin();
00191 it!=targetPhrase.GetAlignTerm().end(); ++it) {
00192 FEATUREVERBOSE(2, "alignTerm " << it->first << " " << it->second << std::endl);
00193 }
00194
00195 for (AlignmentInfo::const_iterator it=targetPhrase.GetAlignNonTerm().begin();
00196 it!=targetPhrase.GetAlignNonTerm().end(); ++it) {
00197 FEATUREVERBOSE(2, "alignNonTerm " << it->first << " " << it->second << std::endl);
00198 }
00199 }
00200
00201
00202 if (targetPhrase.GetAlignNonTerm().GetSize() != 0) {
00203
00204
00205 MosesTraining::PhraseOrientation phraseOrientation(source.GetSize(), targetPhrase.GetSize(),
00206 targetPhrase.GetAlignTerm(), targetPhrase.GetAlignNonTerm());
00207
00208 PhraseOrientationFeature::ReoClassData* reoClassData = new PhraseOrientationFeature::ReoClassData();
00209
00210
00211
00212 for (AlignmentInfo::const_iterator it=targetPhrase.GetAlignNonTerm().begin();
00213 it!=targetPhrase.GetAlignNonTerm().end(); ++it) {
00214 size_t sourceIndex = it->first;
00215 size_t targetIndex = it->second;
00216
00217
00218
00219 MosesTraining::PhraseOrientation::REO_CLASS l2rOrientation = phraseOrientation.GetOrientationInfo(sourceIndex,sourceIndex,MosesTraining::PhraseOrientation::REO_DIR_L2R);
00220
00221 if ( ((targetIndex == 0) || !phraseOrientation.TargetSpanIsAligned(0,targetIndex))
00222 && (targetPhraseLHS != m_glueLabel) ) {
00223
00224 FEATUREVERBOSE(3, "Left boundary: targetIndex== " << targetIndex);
00225 if (targetIndex != 0) {
00226 FEATUREVERBOSE2(3, " (!=0)");
00227 }
00228 FEATUREVERBOSE2(3, std::endl);
00229
00230 reoClassData->firstNonTerminalPreviousSourceSpanIsAligned = ( (sourceIndex > 0) && phraseOrientation.SourceSpanIsAligned(0,sourceIndex-1) );
00231 reoClassData->firstNonTerminalFollowingSourceSpanIsAligned = ( (sourceIndex < source.GetSize()-1) && phraseOrientation.SourceSpanIsAligned(sourceIndex,source.GetSize()-1) );
00232
00233 FEATUREVERBOSE(4, "firstNonTerminalPreviousSourceSpanIsAligned== " << reoClassData->firstNonTerminalPreviousSourceSpanIsAligned << std::endl);
00234 FEATUREVERBOSE(4, "firstNonTerminalFollowingSourceSpanIsAligned== " << reoClassData->firstNonTerminalFollowingSourceSpanIsAligned << std::endl;);
00235
00236 if (reoClassData->firstNonTerminalPreviousSourceSpanIsAligned &&
00237 reoClassData->firstNonTerminalFollowingSourceSpanIsAligned) {
00238
00239 l2rOrientation = MosesTraining::PhraseOrientation::REO_CLASS_DLEFT;
00240 } else {
00241 reoClassData->firstNonTerminalIsBoundary = true;
00242 }
00243 }
00244
00245 reoClassData->nonTerminalReoClassL2R.push_back(l2rOrientation);
00246
00247
00248
00249 MosesTraining::PhraseOrientation::REO_CLASS r2lOrientation = phraseOrientation.GetOrientationInfo(sourceIndex,sourceIndex,MosesTraining::PhraseOrientation::REO_DIR_R2L);
00250
00251 if ( ((targetIndex == targetPhrase.GetSize()-1) || !phraseOrientation.TargetSpanIsAligned(targetIndex,targetPhrase.GetSize()-1))
00252 && (targetPhraseLHS != m_glueLabel) ) {
00253
00254 FEATUREVERBOSE(3, "Right boundary: targetIndex== " << targetIndex);
00255 if (targetIndex != targetPhrase.GetSize()-1) {
00256 FEATUREVERBOSE2(3, " (!=targetPhrase.GetSize()-1)");
00257 }
00258 FEATUREVERBOSE2(3, std::endl);
00259
00260 reoClassData->lastNonTerminalPreviousSourceSpanIsAligned = ( (sourceIndex > 0) && phraseOrientation.SourceSpanIsAligned(0,sourceIndex-1) );
00261 reoClassData->lastNonTerminalFollowingSourceSpanIsAligned = ( (sourceIndex < source.GetSize()-1) && phraseOrientation.SourceSpanIsAligned(sourceIndex,source.GetSize()-1) );
00262
00263 FEATUREVERBOSE(4, "lastNonTerminalPreviousSourceSpanIsAligned== " << reoClassData->lastNonTerminalPreviousSourceSpanIsAligned << std::endl);
00264 FEATUREVERBOSE(4, "lastNonTerminalFollowingSourceSpanIsAligned== " << reoClassData->lastNonTerminalFollowingSourceSpanIsAligned << std::endl;);
00265
00266 if (reoClassData->lastNonTerminalPreviousSourceSpanIsAligned &&
00267 reoClassData->lastNonTerminalFollowingSourceSpanIsAligned) {
00268
00269 r2lOrientation = MosesTraining::PhraseOrientation::REO_CLASS_DLEFT;
00270 } else {
00271 reoClassData->lastNonTerminalIsBoundary = true;
00272 }
00273 }
00274
00275 reoClassData->nonTerminalReoClassR2L.push_back(r2lOrientation);
00276 }
00277
00278 bool inserted = targetPhrase.SetData("Orientation", boost::shared_ptr<void>(reoClassData));
00279 UTIL_THROW_IF2(!inserted, GetScoreProducerDescription()
00280 << ": Insertion of orientation data attempted repeatedly. ");
00281 }
00282 FEATUREVERBOSE(2, "END========EvaluateInIsolation========" << std::endl);
00283 }
00284
00285
00286 FFState* PhraseOrientationFeature::EvaluateWhenApplied(
00287 const ChartHypothesis& hypo,
00288 int featureID,
00289 ScoreComponentCollection* accumulator) const
00290 {
00291
00292 std::vector<float> newScores(m_numScoreComponents,0);
00293
00294
00295 const TargetPhrase &currTarPhr = hypo.GetCurrTargetPhrase();
00296 const Phrase *currSrcPhr = currTarPhr.GetRuleSource();
00297
00298
00299 PhraseOrientationFeatureState *state = new PhraseOrientationFeatureState(m_distinguishStates,m_useSparseWord,m_useSparseNT);
00300
00301 IFFEATUREVERBOSE(2) {
00302 FEATUREVERBOSE(2, *currSrcPhr << std::endl);
00303 FEATUREVERBOSE(2, currTarPhr << std::endl);
00304
00305 for (AlignmentInfo::const_iterator it=currTarPhr.GetAlignTerm().begin();
00306 it!=currTarPhr.GetAlignTerm().end(); ++it) {
00307 FEATUREVERBOSE(2, "alignTerm " << it->first << " " << it->second << std::endl);
00308 }
00309
00310 for (AlignmentInfo::const_iterator it=currTarPhr.GetAlignNonTerm().begin();
00311 it!=currTarPhr.GetAlignNonTerm().end(); ++it) {
00312 FEATUREVERBOSE(2, "alignNonTerm " << it->first << " " << it->second << std::endl);
00313 }
00314 }
00315
00316
00317 const PhraseOrientationFeature::ReoClassData *reoClassData = NULL;
00318 if (currTarPhr.GetAlignNonTerm().GetSize() != 0) {
00319 const boost::shared_ptr<void> data = currTarPhr.GetData("Orientation");
00320 UTIL_THROW_IF2(!data, GetScoreProducerDescription()
00321 << ": Orientation data not set in target phrase. ");
00322 reoClassData = static_cast<const PhraseOrientationFeature::ReoClassData*>( data.get() );
00323 }
00324
00325
00326 const AlignmentInfo::NonTermIndexMap &nonTermIndexMap =
00327 currTarPhr.GetAlignNonTerm().GetNonTermIndexMap();
00328
00329
00330
00331 size_t nNT = 0;
00332
00333 for (AlignmentInfo::const_iterator it=currTarPhr.GetAlignNonTerm().begin();
00334 it!=currTarPhr.GetAlignNonTerm().end(); ++it) {
00335 size_t sourceIndex = it->first;
00336 size_t targetIndex = it->second;
00337 size_t nonTermIndex = nonTermIndexMap[targetIndex];
00338
00339 FEATUREVERBOSE(2, "Scoring nonTermIndex== " << nonTermIndex << " targetIndex== " << targetIndex << " sourceIndex== " << sourceIndex << std::endl);
00340
00341
00342 const ChartHypothesis *prevHypo = hypo.GetPrevHypo(nonTermIndex);
00343 const TargetPhrase &prevTarPhr = prevHypo->GetCurrTargetPhrase();
00344 const Factor* prevTarPhrLHS = prevTarPhr.GetTargetLHS()[0];
00345
00346 if (const PhraseProperty *property = prevTarPhr.GetProperty("Orientation")) {
00347 const OrientationPhraseProperty *orientationPhraseProperty = static_cast<const OrientationPhraseProperty*>(property);
00348
00349 FEATUREVERBOSE(5, "orientationPhraseProperty: "
00350 << "L2R_Mono " << orientationPhraseProperty->GetLeftToRightProbabilityMono()
00351 << " L2R_Swap " << orientationPhraseProperty->GetLeftToRightProbabilitySwap()
00352 << " L2R_Dright " << orientationPhraseProperty->GetLeftToRightProbabilityDright()
00353 << " L2R_Dleft " << orientationPhraseProperty->GetLeftToRightProbabilityDleft()
00354 << " R2L_Mono " << orientationPhraseProperty->GetRightToLeftProbabilityMono()
00355 << " R2L_Swap " << orientationPhraseProperty->GetRightToLeftProbabilitySwap()
00356 << " R2L_Dright " << orientationPhraseProperty->GetRightToLeftProbabilityDright()
00357 << " R2L_Dleft " << orientationPhraseProperty->GetRightToLeftProbabilityDleft()
00358 << std::endl);
00359
00360 if (m_lookaheadScore) {
00361 LookaheadScore(orientationPhraseProperty, *accumulator, prevTarPhrLHS, true);
00362 }
00363
00364 const PhraseOrientationFeatureState* prevState =
00365 static_cast<const PhraseOrientationFeatureState*>(prevHypo->GetFFState(featureID));
00366
00367
00368
00369
00370 MosesTraining::PhraseOrientation::REO_CLASS l2rOrientation = reoClassData->nonTerminalReoClassL2R[nNT];
00371
00372 IFFEATUREVERBOSE(2) {
00373 FEATUREVERBOSE(2, "l2rOrientation ");
00374 switch (l2rOrientation) {
00375 case MosesTraining::PhraseOrientation::REO_CLASS_LEFT:
00376 FEATUREVERBOSE2(2, "mono" << std::endl);
00377 break;
00378 case MosesTraining::PhraseOrientation::REO_CLASS_RIGHT:
00379 FEATUREVERBOSE2(2, "swap" << std::endl);
00380 break;
00381 case MosesTraining::PhraseOrientation::REO_CLASS_DLEFT:
00382 FEATUREVERBOSE2(2, "dleft" << std::endl);
00383 break;
00384 case MosesTraining::PhraseOrientation::REO_CLASS_DRIGHT:
00385 FEATUREVERBOSE2(2, "dright" << std::endl);
00386 break;
00387 case MosesTraining::PhraseOrientation::REO_CLASS_UNKNOWN:
00388
00389 FEATUREVERBOSE2(2, "unknown->dleft" << std::endl);
00390 break;
00391 default:
00392 UTIL_THROW2(GetScoreProducerDescription()
00393 << ": Unsupported orientation type.");
00394 break;
00395 }
00396 }
00397
00398 if ( (nNT == 0) && reoClassData->firstNonTerminalIsBoundary && !m_monotoneScoreBoundary ) {
00399
00400 if (!m_noScoreBoundary) {
00401
00402
00403 FEATUREVERBOSE(3, "Delaying left-to-right scoring" << std::endl);
00404
00405 std::bitset<3> possibleFutureOrientationsL2R(0x7);
00406 possibleFutureOrientationsL2R[0] = !reoClassData->firstNonTerminalPreviousSourceSpanIsAligned;
00407 possibleFutureOrientationsL2R[1] = !reoClassData->firstNonTerminalFollowingSourceSpanIsAligned;
00408
00409
00410
00411 std::vector<float> scoresL2R;
00412 scoresL2R.push_back( TransformScore(orientationPhraseProperty->GetLeftToRightProbabilityMono()) );
00413 scoresL2R.push_back( TransformScore(orientationPhraseProperty->GetLeftToRightProbabilitySwap()) );
00414 scoresL2R.push_back( TransformScore(orientationPhraseProperty->GetLeftToRightProbabilityDiscontinuous()) );
00415
00416 size_t heuristicScoreIndexL2R = GetHeuristicScoreIndex(scoresL2R, 0, possibleFutureOrientationsL2R);
00417
00418 newScores[heuristicScoreIndexL2R] += scoresL2R[heuristicScoreIndexL2R];
00419 state->SetLeftBoundaryL2R(scoresL2R, heuristicScoreIndexL2R, possibleFutureOrientationsL2R, prevTarPhrLHS, prevState);
00420
00421 if ( (possibleFutureOrientationsL2R & prevState->m_leftBoundaryNonTerminalL2RPossibleFutureOrientations) == 0x4 ) {
00422
00423 FEATUREVERBOSE(5, "previous state: L2R discontinuous orientation "
00424 << possibleFutureOrientationsL2R << " & " << prevState->m_leftBoundaryNonTerminalL2RPossibleFutureOrientations
00425 << " = " << (possibleFutureOrientationsL2R & prevState->m_leftBoundaryNonTerminalL2RPossibleFutureOrientations)
00426 << std::endl);
00427 LeftBoundaryL2RScoreRecursive(featureID, prevState, 0x4, newScores, accumulator);
00428 state->m_leftBoundaryRecursionGuard = true;
00429 }
00430 }
00431
00432 } else {
00433
00434 if ( l2rOrientation == MosesTraining::PhraseOrientation::REO_CLASS_LEFT ) {
00435
00436 newScores[0] += TransformScore(orientationPhraseProperty->GetLeftToRightProbabilityMono());
00437
00438 if (!m_noScoreBoundary && !m_monotoneScoreBoundary) {
00439
00440
00441 LeftBoundaryL2RScoreRecursive(featureID, prevState, 0x1, newScores, accumulator);
00442 }
00443
00444 } else if ( l2rOrientation == MosesTraining::PhraseOrientation::REO_CLASS_RIGHT ) {
00445
00446 newScores[1] += TransformScore(orientationPhraseProperty->GetLeftToRightProbabilitySwap());
00447
00448 if (!m_noScoreBoundary && !m_monotoneScoreBoundary) {
00449
00450
00451 LeftBoundaryL2RScoreRecursive(featureID, prevState, 0x2, newScores, accumulator);
00452 }
00453
00454 } else if ( ( l2rOrientation == MosesTraining::PhraseOrientation::REO_CLASS_DLEFT ) ||
00455 ( l2rOrientation == MosesTraining::PhraseOrientation::REO_CLASS_DRIGHT ) ||
00456 ( l2rOrientation == MosesTraining::PhraseOrientation::REO_CLASS_UNKNOWN ) ) {
00457
00458 newScores[2] += TransformScore(orientationPhraseProperty->GetLeftToRightProbabilityDiscontinuous());
00459
00460 if (!m_noScoreBoundary && !m_monotoneScoreBoundary) {
00461
00462
00463 LeftBoundaryL2RScoreRecursive(featureID, prevState, 0x4, newScores, accumulator);
00464 }
00465
00466 } else {
00467
00468 UTIL_THROW2(GetScoreProducerDescription()
00469 << ": Unsupported orientation type.");
00470 }
00471
00472
00473 if ( m_useSparseWord ) {
00474 SparseWordL2RScore(prevHypo,accumulator,ToString(l2rOrientation));
00475 }
00476 if ( m_useSparseNT ) {
00477 SparseNonTerminalL2RScore(prevTarPhrLHS,accumulator,ToString(l2rOrientation));
00478 }
00479 }
00480
00481
00482
00483
00484 MosesTraining::PhraseOrientation::REO_CLASS r2lOrientation = reoClassData->nonTerminalReoClassR2L[nNT];
00485
00486 IFFEATUREVERBOSE(2) {
00487 FEATUREVERBOSE(2, "r2lOrientation ");
00488 switch (r2lOrientation) {
00489 case MosesTraining::PhraseOrientation::REO_CLASS_LEFT:
00490 FEATUREVERBOSE2(2, "mono" << std::endl);
00491 break;
00492 case MosesTraining::PhraseOrientation::REO_CLASS_RIGHT:
00493 FEATUREVERBOSE2(2, "swap" << std::endl);
00494 break;
00495 case MosesTraining::PhraseOrientation::REO_CLASS_DLEFT:
00496 FEATUREVERBOSE2(2, "dleft" << std::endl);
00497 break;
00498 case MosesTraining::PhraseOrientation::REO_CLASS_DRIGHT:
00499 FEATUREVERBOSE2(2, "dright" << std::endl);
00500 break;
00501 case MosesTraining::PhraseOrientation::REO_CLASS_UNKNOWN:
00502
00503 FEATUREVERBOSE2(2, "unknown->dleft" << std::endl);
00504 break;
00505 default:
00506 UTIL_THROW2(GetScoreProducerDescription()
00507 << ": Unsupported orientation type.");
00508 break;
00509 }
00510 }
00511
00512 if ( (nNT == currTarPhr.GetAlignNonTerm().GetSize()-1) && reoClassData->lastNonTerminalIsBoundary && !m_monotoneScoreBoundary ) {
00513
00514 if (!m_noScoreBoundary) {
00515
00516
00517 FEATUREVERBOSE(3, "Delaying right-to-left scoring" << std::endl);
00518
00519 std::bitset<3> possibleFutureOrientationsR2L(0x7);
00520 possibleFutureOrientationsR2L[0] = !reoClassData->lastNonTerminalFollowingSourceSpanIsAligned;
00521 possibleFutureOrientationsR2L[1] = !reoClassData->lastNonTerminalPreviousSourceSpanIsAligned;
00522
00523
00524
00525 std::vector<float> scoresR2L;
00526 scoresR2L.push_back( TransformScore(orientationPhraseProperty->GetRightToLeftProbabilityMono()) );
00527 scoresR2L.push_back( TransformScore(orientationPhraseProperty->GetRightToLeftProbabilitySwap()) );
00528 scoresR2L.push_back( TransformScore(orientationPhraseProperty->GetRightToLeftProbabilityDiscontinuous()) );
00529
00530 size_t heuristicScoreIndexR2L = GetHeuristicScoreIndex(scoresR2L, m_offsetR2LScores, possibleFutureOrientationsR2L);
00531
00532 newScores[m_offsetR2LScores+heuristicScoreIndexR2L] += scoresR2L[heuristicScoreIndexR2L];
00533 state->SetRightBoundaryR2L(scoresR2L, heuristicScoreIndexR2L, possibleFutureOrientationsR2L, prevTarPhrLHS, prevState);
00534
00535 if ( (possibleFutureOrientationsR2L & prevState->m_rightBoundaryNonTerminalR2LPossibleFutureOrientations) == 0x4 ) {
00536
00537 FEATUREVERBOSE(5, "previous state: R2L discontinuous orientation "
00538 << possibleFutureOrientationsR2L << " & " << prevState->m_rightBoundaryNonTerminalR2LPossibleFutureOrientations
00539 << " = " << (possibleFutureOrientationsR2L & prevState->m_rightBoundaryNonTerminalR2LPossibleFutureOrientations)
00540 << std::endl);
00541 RightBoundaryR2LScoreRecursive(featureID, prevState, 0x4, newScores, accumulator);
00542 state->m_rightBoundaryRecursionGuard = true;
00543 }
00544 }
00545
00546 } else {
00547
00548 if ( r2lOrientation == MosesTraining::PhraseOrientation::REO_CLASS_LEFT ) {
00549
00550 newScores[m_offsetR2LScores+0] += TransformScore(orientationPhraseProperty->GetRightToLeftProbabilityMono());
00551
00552 if (!m_noScoreBoundary && !m_monotoneScoreBoundary) {
00553
00554
00555 RightBoundaryR2LScoreRecursive(featureID, prevState, 0x1, newScores, accumulator);
00556 }
00557
00558 } else if ( r2lOrientation == MosesTraining::PhraseOrientation::REO_CLASS_RIGHT ) {
00559
00560 newScores[m_offsetR2LScores+1] += TransformScore(orientationPhraseProperty->GetRightToLeftProbabilitySwap());
00561
00562 if (!m_noScoreBoundary && !m_monotoneScoreBoundary) {
00563
00564
00565 RightBoundaryR2LScoreRecursive(featureID, prevState, 0x2, newScores, accumulator);
00566 }
00567
00568 } else if ( ( r2lOrientation == MosesTraining::PhraseOrientation::REO_CLASS_DLEFT ) ||
00569 ( r2lOrientation == MosesTraining::PhraseOrientation::REO_CLASS_DRIGHT ) ||
00570 ( r2lOrientation == MosesTraining::PhraseOrientation::REO_CLASS_UNKNOWN ) ) {
00571
00572 newScores[m_offsetR2LScores+2] += TransformScore(orientationPhraseProperty->GetRightToLeftProbabilityDiscontinuous());
00573
00574 if (!m_noScoreBoundary && !m_monotoneScoreBoundary) {
00575
00576
00577 RightBoundaryR2LScoreRecursive(featureID, prevState, 0x4, newScores, accumulator);
00578 }
00579
00580 } else {
00581
00582 UTIL_THROW2(GetScoreProducerDescription()
00583 << ": Unsupported orientation type.");
00584 }
00585
00586
00587 if ( m_useSparseWord ) {
00588 SparseWordR2LScore(prevHypo,accumulator,ToString(r2lOrientation));
00589 }
00590 if ( m_useSparseNT ) {
00591 SparseNonTerminalR2LScore(prevTarPhrLHS,accumulator,ToString(r2lOrientation));
00592 }
00593 }
00594
00595 } else {
00596
00597 UTIL_THROW_IF2(!prevTarPhr.GetWord(0).IsOOV(), GetScoreProducerDescription()
00598 << ": Missing Orientation property. "
00599 << "Please check phrase table and glue rules.");
00600 }
00601
00602 ++nNT;
00603 }
00604
00605 accumulator->PlusEquals(this, newScores);
00606
00607 return state;
00608 }
00609
00610
00611 void PhraseOrientationFeature::LookaheadScore(const OrientationPhraseProperty *orientationPhraseProperty,
00612 ScoreComponentCollection &scoreBreakdown,
00613 const Factor* targetPhraseLHS,
00614 bool subtract) const
00615 {
00616 size_t ffScoreIndex = m_index;
00617
00618 std::vector<float> scoresL2R;
00619 scoresL2R.push_back( TransformScore(orientationPhraseProperty->GetLeftToRightProbabilityMono()) );
00620 scoresL2R.push_back( TransformScore(orientationPhraseProperty->GetLeftToRightProbabilitySwap()) );
00621 scoresL2R.push_back( TransformScore(orientationPhraseProperty->GetLeftToRightProbabilityDiscontinuous()) );
00622 size_t heuristicScoreIndexL2R = 0;
00623 if (targetPhraseLHS != m_glueLabel) {
00624 heuristicScoreIndexL2R = GetHeuristicScoreIndex(scoresL2R, 0);
00625 }
00626
00627 if (subtract) {
00628 scoreBreakdown.PlusEquals(ffScoreIndex+heuristicScoreIndexL2R,
00629 -scoresL2R[heuristicScoreIndexL2R]);
00630 } else {
00631 scoreBreakdown.PlusEquals(ffScoreIndex+heuristicScoreIndexL2R,
00632 scoresL2R[heuristicScoreIndexL2R]);
00633 }
00634
00635 std::vector<float> scoresR2L;
00636 scoresR2L.push_back( TransformScore(orientationPhraseProperty->GetRightToLeftProbabilityMono()) );
00637 scoresR2L.push_back( TransformScore(orientationPhraseProperty->GetRightToLeftProbabilitySwap()) );
00638 scoresR2L.push_back( TransformScore(orientationPhraseProperty->GetRightToLeftProbabilityDiscontinuous()) );
00639 size_t heuristicScoreIndexR2L = 0;
00640 if (targetPhraseLHS != m_glueLabel) {
00641 heuristicScoreIndexR2L = GetHeuristicScoreIndex(scoresR2L, m_offsetR2LScores);
00642 }
00643
00644 if (subtract) {
00645 scoreBreakdown.PlusEquals(ffScoreIndex+m_offsetR2LScores+heuristicScoreIndexR2L,
00646 -scoresR2L[heuristicScoreIndexR2L]);
00647 } else {
00648 scoreBreakdown.PlusEquals(ffScoreIndex+m_offsetR2LScores+heuristicScoreIndexR2L,
00649 scoresR2L[heuristicScoreIndexR2L]);
00650 }
00651 }
00652
00653
00654 size_t PhraseOrientationFeature::GetHeuristicScoreIndex(const std::vector<float>& scores,
00655 size_t weightsVectorOffset,
00656 const std::bitset<3> possibleFutureOrientations) const
00657 {
00658 std::vector<float> weightedScores;
00659 if (m_heuristicScoreUseWeights) {
00660 if (m_weightsVector.empty()) {
00661 m_weightsVector = StaticData::Instance().GetAllWeights().GetScoresForProducer(this);
00662 }
00663 for ( size_t i=0; i<3; ++i ) {
00664 weightedScores.push_back( m_weightsVector[weightsVectorOffset+i] * scores[i] );
00665 }
00666 }
00667
00668 size_t heuristicScoreIndex = 0;
00669 for (size_t i=1; i<3; ++i) {
00670 if (possibleFutureOrientations[i]) {
00671 if (m_heuristicScoreUseWeights) {
00672 if (weightedScores[i] > weightedScores[heuristicScoreIndex]) {
00673 heuristicScoreIndex = i;
00674 }
00675 } else {
00676 if (scores[i] > scores[heuristicScoreIndex]) {
00677 heuristicScoreIndex = i;
00678 }
00679 }
00680 }
00681 }
00682
00683 IFFEATUREVERBOSE(5) {
00684 FEATUREVERBOSE(5, "Heuristic score computation: "
00685 << "heuristicScoreIndex== " << heuristicScoreIndex);
00686 for (size_t i=0; i<3; ++i)
00687 FEATUREVERBOSE2(5, " scores[" << i << "]== " << scores[i]);
00688 if (m_heuristicScoreUseWeights) {
00689 for (size_t i=0; i<3; ++i)
00690 FEATUREVERBOSE2(5, " m_weightsVector[" << weightsVectorOffset+i << "]== " << m_weightsVector[weightsVectorOffset+i]);
00691 for (size_t i=0; i<3; ++i)
00692 FEATUREVERBOSE2(5, " weightedScores[" << i << "]== " << weightedScores[i]);
00693 }
00694 for (size_t i=0; i<3; ++i)
00695 FEATUREVERBOSE2(5, " possibleFutureOrientations[" << i << "]== " << possibleFutureOrientations[i]);
00696 if ( possibleFutureOrientations == 0x7 ) {
00697 FEATUREVERBOSE2(5, " (all orientations possible)");
00698 }
00699 FEATUREVERBOSE2(5, std::endl);
00700 }
00701
00702 return heuristicScoreIndex;
00703 }
00704
00705
00706 void PhraseOrientationFeature::LeftBoundaryL2RScoreRecursive(int featureID,
00707 const PhraseOrientationFeatureState *state,
00708 const std::bitset<3> orientation,
00709 std::vector<float>& newScores,
00710 ScoreComponentCollection* scoreBreakdown) const
00711
00712 {
00713 if (state->m_leftBoundaryIsSet) {
00714 const std::string* recursiveOrientationString;
00715
00716
00717 newScores[state->m_leftBoundaryNonTerminalL2RHeuristicScoreIndex] -= state->m_leftBoundaryNonTerminalL2RScores[state->m_leftBoundaryNonTerminalL2RHeuristicScoreIndex];
00718
00719
00720 std::bitset<3> recursiveOrientation = orientation;
00721 if ( (orientation == 0x4) || (orientation == 0x0) ) {
00722
00723 recursiveOrientationString = &DORIENT;
00724 newScores[2] += state->GetLeftBoundaryL2RScoreDiscontinuous();
00725 } else {
00726 recursiveOrientation &= state->m_leftBoundaryNonTerminalL2RPossibleFutureOrientations;
00727 if ( recursiveOrientation == 0x1 ) {
00728
00729 recursiveOrientationString = &MORIENT;
00730 newScores[0] += state->GetLeftBoundaryL2RScoreMono();
00731 } else if ( recursiveOrientation == 0x2 ) {
00732
00733 recursiveOrientationString = &SORIENT;
00734 newScores[1] += state->GetLeftBoundaryL2RScoreSwap();
00735 } else if ( recursiveOrientation == 0x4 ) {
00736
00737 recursiveOrientationString = &DORIENT;
00738 newScores[2] += state->GetLeftBoundaryL2RScoreDiscontinuous();
00739 } else if ( recursiveOrientation == 0x0 ) {
00740
00741 recursiveOrientationString = &DORIENT;
00742 newScores[2] += state->GetLeftBoundaryL2RScoreDiscontinuous();
00743 } else {
00744 UTIL_THROW2(GetScoreProducerDescription()
00745 << ": Error in recursive scoring.");
00746 }
00747 }
00748
00749 if ( m_useSparseNT ) {
00750 SparseNonTerminalL2RScore(state->m_leftBoundaryNonTerminalSymbol,scoreBreakdown,recursiveOrientationString);
00751 }
00752
00753 FEATUREVERBOSE(6, "Left boundary recursion: " << orientation << " & " << state->m_leftBoundaryNonTerminalL2RPossibleFutureOrientations << " = " << recursiveOrientation
00754 << " --- Subtracted heuristic score: " << state->m_leftBoundaryNonTerminalL2RScores[state->m_leftBoundaryNonTerminalL2RHeuristicScoreIndex] << std::endl);
00755
00756 if (!state->m_leftBoundaryRecursionGuard) {
00757
00758 const PhraseOrientationFeatureState* prevState = state->m_leftBoundaryPrevState;
00759 LeftBoundaryL2RScoreRecursive(featureID, prevState, recursiveOrientation, newScores, scoreBreakdown);
00760 } else {
00761 FEATUREVERBOSE(6, "m_leftBoundaryRecursionGuard" << std::endl);
00762 }
00763 }
00764 }
00765
00766
00767 void PhraseOrientationFeature::RightBoundaryR2LScoreRecursive(int featureID,
00768 const PhraseOrientationFeatureState *state,
00769 const std::bitset<3> orientation,
00770 std::vector<float>& newScores,
00771 ScoreComponentCollection* scoreBreakdown) const
00772
00773 {
00774 if (state->m_rightBoundaryIsSet) {
00775 const std::string* recursiveOrientationString;
00776
00777
00778 newScores[m_offsetR2LScores+state->m_rightBoundaryNonTerminalR2LHeuristicScoreIndex] -= state->m_rightBoundaryNonTerminalR2LScores[state->m_rightBoundaryNonTerminalR2LHeuristicScoreIndex];
00779
00780
00781 std::bitset<3> recursiveOrientation = orientation;
00782 if ( (orientation == 0x4) || (orientation == 0x0) ) {
00783
00784 recursiveOrientationString = &DORIENT;
00785 newScores[m_offsetR2LScores+2] += state->GetRightBoundaryR2LScoreDiscontinuous();
00786 } else {
00787 recursiveOrientation &= state->m_rightBoundaryNonTerminalR2LPossibleFutureOrientations;
00788 if ( recursiveOrientation == 0x1 ) {
00789
00790 recursiveOrientationString = &MORIENT;
00791 newScores[m_offsetR2LScores+0] += state->GetRightBoundaryR2LScoreMono();
00792 } else if ( recursiveOrientation == 0x2 ) {
00793
00794 recursiveOrientationString = &SORIENT;
00795 newScores[m_offsetR2LScores+1] += state->GetRightBoundaryR2LScoreSwap();
00796 } else if ( recursiveOrientation == 0x4 ) {
00797
00798 recursiveOrientationString = &DORIENT;
00799 newScores[m_offsetR2LScores+2] += state->GetRightBoundaryR2LScoreDiscontinuous();
00800 } else if ( recursiveOrientation == 0x0 ) {
00801
00802 recursiveOrientationString = &DORIENT;
00803 newScores[m_offsetR2LScores+2] += state->GetRightBoundaryR2LScoreDiscontinuous();
00804 } else {
00805 UTIL_THROW2(GetScoreProducerDescription()
00806 << ": Error in recursive scoring.");
00807 }
00808 }
00809
00810 if ( m_useSparseNT ) {
00811 SparseNonTerminalR2LScore(state->m_rightBoundaryNonTerminalSymbol,scoreBreakdown,recursiveOrientationString);
00812 }
00813
00814 FEATUREVERBOSE(6, "Right boundary recursion: " << orientation << " & " << state->m_rightBoundaryNonTerminalR2LPossibleFutureOrientations << " = " << recursiveOrientation
00815 << " --- Subtracted heuristic score: " << state->m_rightBoundaryNonTerminalR2LScores[state->m_rightBoundaryNonTerminalR2LHeuristicScoreIndex] << std::endl);
00816
00817 if (!state->m_rightBoundaryRecursionGuard) {
00818
00819 const PhraseOrientationFeatureState* prevState = state->m_rightBoundaryPrevState;
00820 RightBoundaryR2LScoreRecursive(featureID, prevState, recursiveOrientation, newScores, scoreBreakdown);
00821 } else {
00822 FEATUREVERBOSE(6, "m_rightBoundaryRecursionGuard" << std::endl);
00823 }
00824 }
00825 }
00826
00827
00828 void PhraseOrientationFeature::SparseWordL2RScore(const ChartHypothesis* hypo,
00829 ScoreComponentCollection* scoreBreakdown,
00830 const std::string* o) const
00831 {
00832
00833
00834 const ChartHypothesis* currHypo = hypo;
00835 const TargetPhrase* targetPhrase = &currHypo->GetCurrTargetPhrase();
00836 const Word* targetWord = &targetPhrase->GetWord(0);
00837
00838
00839 while ( targetWord->IsNonTerminal() ) {
00840 const AlignmentInfo::NonTermIndexMap &nonTermIndexMap =
00841 targetPhrase->GetAlignNonTerm().GetNonTermIndexMap();
00842 size_t nonTermIndex = nonTermIndexMap[0];
00843 currHypo = currHypo->GetPrevHypo(nonTermIndex);
00844 targetPhrase = &currHypo->GetCurrTargetPhrase();
00845 targetWord = &targetPhrase->GetWord(0);
00846 }
00847
00848 const std::string& targetWordString = (*targetWord)[0]->GetString().as_string();
00849 if (targetWordString != "<s>" && targetWordString != "</s>") {
00850 if ( !m_useTargetWordList || m_targetWordList.find((*targetWord)[0]) != m_targetWordList.end() ) {
00851 scoreBreakdown->PlusEquals(this,
00852 "L2R"+*o+"_tw_"+targetWordString,
00853 1);
00854 FEATUREVERBOSE(3, "Sparse: L2R"+*o+"_tw_"+targetWordString << std::endl);
00855 } else {
00856 scoreBreakdown->PlusEquals(this,
00857 "L2R"+*o+"_tw_OTHER",
00858 1);
00859 FEATUREVERBOSE(3, "Sparse: L2R"+*o+"_tw_OTHER" << std::endl);
00860 }
00861 }
00862
00863
00864
00865 Range sourceSpan = hypo->GetCurrSourceRange();
00866 const InputType& input = hypo->GetManager().GetSource();
00867 const Sentence& sourceSentence = static_cast<const Sentence&>(input);
00868 const Word& sourceWord = sourceSentence.GetWord(sourceSpan.GetStartPos());
00869
00870 const std::string& sourceWordString = sourceWord[0]->GetString().as_string();
00871 if (sourceWordString != "<s>" && sourceWordString != "</s>") {
00872 if ( !m_useSourceWordList || m_sourceWordList.find(sourceWord[0]) != m_sourceWordList.end() ) {
00873 scoreBreakdown->PlusEquals(this,
00874 "L2R"+*o+"_sw_"+sourceWordString,
00875 1);
00876 FEATUREVERBOSE(3, "Sparse: L2R"+*o+"_sw_"+sourceWordString << std::endl);
00877 } else {
00878 scoreBreakdown->PlusEquals(this,
00879 "L2R"+*o+"_sw_OTHER",
00880 1);
00881 FEATUREVERBOSE(3, "Sparse: L2R"+*o+"_sw_OTHER" << std::endl);
00882 }
00883 }
00884 }
00885
00886
00887 void PhraseOrientationFeature::SparseWordR2LScore(const ChartHypothesis* hypo,
00888 ScoreComponentCollection* scoreBreakdown,
00889 const std::string* o) const
00890 {
00891
00892
00893 const ChartHypothesis* currHypo = hypo;
00894 const TargetPhrase* targetPhrase = &currHypo->GetCurrTargetPhrase();
00895 const Word* targetWord = &targetPhrase->GetWord(targetPhrase->GetSize()-1);
00896
00897
00898 while ( targetWord->IsNonTerminal() ) {
00899 const AlignmentInfo::NonTermIndexMap &nonTermIndexMap =
00900 targetPhrase->GetAlignNonTerm().GetNonTermIndexMap();
00901 size_t nonTermIndex = nonTermIndexMap[targetPhrase->GetSize()-1];
00902 currHypo = currHypo->GetPrevHypo(nonTermIndex);
00903 targetPhrase = &currHypo->GetCurrTargetPhrase();
00904 targetWord = &targetPhrase->GetWord(targetPhrase->GetSize()-1);
00905 }
00906
00907 const std::string& targetWordString = (*targetWord)[0]->GetString().as_string();
00908 if (targetWordString != "<s>" && targetWordString != "</s>") {
00909 if ( !m_useTargetWordList || m_targetWordList.find((*targetWord)[0]) != m_targetWordList.end() ) {
00910 scoreBreakdown->PlusEquals(this,
00911 "R2L"+*o+"_tw_"+targetWordString,
00912 1);
00913 FEATUREVERBOSE(3, "Sparse: R2L"+*o+"_tw_"+targetWordString << std::endl);
00914 } else {
00915 scoreBreakdown->PlusEquals(this,
00916 "R2L"+*o+"_tw_OTHER",
00917 1);
00918 FEATUREVERBOSE(3, "Sparse: R2L"+*o+"_tw_OTHER" << std::endl);
00919 }
00920 }
00921
00922
00923
00924 Range sourceSpan = hypo->GetCurrSourceRange();
00925 const InputType& input = hypo->GetManager().GetSource();
00926 const Sentence& sourceSentence = static_cast<const Sentence&>(input);
00927 const Word& sourceWord = sourceSentence.GetWord(sourceSpan.GetEndPos());
00928
00929 const std::string& sourceWordString = sourceWord[0]->GetString().as_string();
00930 if (sourceWordString != "<s>" && sourceWordString != "</s>") {
00931 if ( !m_useSourceWordList || m_sourceWordList.find(sourceWord[0]) != m_sourceWordList.end() ) {
00932 scoreBreakdown->PlusEquals(this,
00933 "R2L"+*o+"_sw_"+sourceWordString,
00934 1);
00935 FEATUREVERBOSE(3, "Sparse: R2L"+*o+"_sw_"+sourceWordString << std::endl);
00936 } else {
00937 scoreBreakdown->PlusEquals(this,
00938 "R2L"+*o+"_sw_OTHER",
00939 1);
00940 FEATUREVERBOSE(3, "Sparse: R2L"+*o+"_sw_OTHER" << std::endl);
00941 }
00942 }
00943 }
00944
00945
00946 void PhraseOrientationFeature::SparseNonTerminalL2RScore(const Factor* nonTerminalSymbol,
00947 ScoreComponentCollection* scoreBreakdown,
00948 const std::string* o) const
00949 {
00950 if ( nonTerminalSymbol != m_glueLabel ) {
00951 const std::string& nonTerminalString = nonTerminalSymbol->GetString().as_string();
00952 scoreBreakdown->PlusEquals(this,
00953 "L2R"+*o+"_n_"+nonTerminalString,
00954 1);
00955 FEATUREVERBOSE(3, "Sparse: L2R"+*o+"_n_"+nonTerminalString << std::endl);
00956 }
00957 }
00958
00959
00960 void PhraseOrientationFeature::SparseNonTerminalR2LScore(const Factor* nonTerminalSymbol,
00961 ScoreComponentCollection* scoreBreakdown,
00962 const std::string* o) const
00963 {
00964 if ( nonTerminalSymbol != m_glueLabel ) {
00965 const std::string& nonTerminalString = nonTerminalSymbol->GetString().as_string();
00966 scoreBreakdown->PlusEquals(this,
00967 "R2L"+*o+"_n_"+nonTerminalString,
00968 1);
00969 FEATUREVERBOSE(3, "Sparse: R2L"+*o+"_n_"+nonTerminalString << std::endl);
00970 }
00971 }
00972
00973
00974 const std::string* PhraseOrientationFeature::ToString(const MosesTraining::PhraseOrientation::REO_CLASS o) const
00975 {
00976 if ( o == MosesTraining::PhraseOrientation::REO_CLASS_LEFT ) {
00977 return &MORIENT;
00978
00979 } else if ( o == MosesTraining::PhraseOrientation::REO_CLASS_RIGHT ) {
00980 return &SORIENT;
00981
00982 } else if ( ( o == MosesTraining::PhraseOrientation::REO_CLASS_DLEFT ) ||
00983 ( o == MosesTraining::PhraseOrientation::REO_CLASS_DRIGHT ) ||
00984 ( o == MosesTraining::PhraseOrientation::REO_CLASS_UNKNOWN ) ) {
00985 return &DORIENT;
00986
00987 } else {
00988 UTIL_THROW2(GetScoreProducerDescription()
00989 << ": Unsupported orientation type.");
00990 }
00991 return NULL;
00992 }
00993
00994
00995 }
00996