예제 #1
0
  @Override
  public void debug() {
    System.err.println("###################");
    System.err.printf(
        "hypothesis [class=%s,id=%d,pos=%d,expired=%s,pending=%d]: %s\n",
        getClass(),
        System.identityHashCode(this),
        featurizable.targetPosition,
        hasExpired,
        pendingPhrases.size(),
        this);
    System.err.printf(
        "parent hypothesis [class=%s,id=%d,pos=%d,expired=%s]: %s\n",
        preceedingDerivation.getClass(),
        System.identityHashCode(preceedingDerivation),
        preceedingDerivation.featurizable.targetPosition,
        preceedingDerivation.hasExpired(),
        preceedingDerivation);
    System.err.println("pendingPhrasesCost: " + pendingPhrasesCost);

    DTUHypothesis<TK, FV> hyp = this;
    if (hyp.isDone() != hyp.featurizable.done) {
      System.err.println("Error in AbstractBeamInferer with: " + hyp);
      System.err.println("isDone(): " + hyp.isDone());
      System.err.println("pending phrases: " + hyp.pendingPhrases.size());
      System.err.println("f.done: " + hyp.featurizable.done);
      Derivation<TK, FV> curHyp = hyp;
      while (curHyp != null) {
        System.err.println("  " + curHyp.toString());
        curHyp = curHyp.preceedingDerivation;
      }
      throw new RuntimeException();
    }
  }
예제 #2
0
  @Override
  public String toString() {

    StringBuilder sb = new StringBuilder(super.toString()); // Hypothesis's
    // toString()
    sb.append(" segIx=").append(segmentIdx);
    sb.append(" id=").append(System.identityHashCode(this));
    sb.append(" tOpt=").append(System.identityHashCode(rule));
    sb.append(String.format(" c=[%.3f,%.3f,%.3f]", partialScore(), pendingPhrasesCost, h));

    if (pendingPhrases != null) {
      sb.append(" | expired:").append(hasExpired);
      sb.append(" | pending:");

      for (final PendingPhrase<TK, FV> discTargetPhrase : pendingPhrases) {
        DTURule<TK> opt = (DTURule<TK>) discTargetPhrase.concreteOpt.abstractRule;
        sb.append(" {");
        int si = discTargetPhrase.segmentIdx + 1;
        for (int i = si; i < opt.dtus.length; ++i) {
          if (i > si) sb.append(" ");
          sb.append(
              String.format(
                  "{%s} ([s=%d,e=%d,c=%.3f])",
                  opt.dtus[i].toString(" "),
                  discTargetPhrase.firstPosition,
                  discTargetPhrase.lastPosition,
                  discTargetPhrase.futureCosts[i]));
        }
        sb.append("} ");
      }
    }

    return sb.toString();
  }
예제 #3
0
 // Need PendingPhrase instances to be sorted for recombination:
 @Override
 public int compareTo(PendingPhrase<TK, FV> o) {
   int sig = Integer.signum(this.segmentIdx - o.segmentIdx);
   if (this.concreteOpt == o.concreteOpt) return sig;
   int h1 = System.identityHashCode(concreteOpt);
   int h2 = System.identityHashCode(o.concreteOpt);
   if (h1 == h2)
     // This case (i.e., concreteOpt != o.concreteOpt && h1 == h2) can happen
     // with a 64-bit JVM, though it is very unlikely.
     return sig;
   return Integer.signum(h1 - h2);
 }
예제 #4
0
/**
 * Hypothesis with words that still need to be added to the partial translation (aka, "pending
 * phrases").
 *
 * @author Michel Galley
 */
public class DTUHypothesis<TK, FV> extends Derivation<TK, FV> {

  private static final String MIN_GAP_SIZE_PROPERTY = "minTargetGapSize";
  private static final int MIN_GAP_SIZE =
      Integer.parseInt(System.getProperty(MIN_GAP_SIZE_PROPERTY, "1"));

  static {
    System.err.println("Minimum target gap size: " + MIN_GAP_SIZE);
  }

  private static final double EXPIRATION_PENALTY = 11111.11; // When a
  // DTUHypothesis
  // expires, it
  // suffers this cost
  private static int MAX_TARGET_PHRASE_SPAN = -1; // to make sure it is
  // overridden (an assert will
  // fail otherwise)
  private static int MAX_PENDING_PHRASES =
      Integer.parseInt(System.getProperty("maxPendingPhrases", "2"));

  /**
   * This class represents a phrase with one or more discontinuities in its target side. It holds
   * this information: abstract translation option is used to generate the discontinuous phrase, and
   * index of the current contiguous segment.
   */
  public static class PendingPhrase<TK, FV> implements Comparable<PendingPhrase<TK, FV>> {

    public final ConcreteRule<TK, FV> concreteOpt;

    public int segmentIdx; // Current segment of the translation option.
    // For instance, segmentIdx=0 selects "ne" and
    // segmentIdx=1 selects "pas"
    private int firstPosition; // the discontinuous phrases
    // segmentIdx+1,segmentIdx+2,etc.
    private final int lastPosition; // must be generated within the range
    // [firstPosition,lastPosition] of target
    // words.

    private final double[] futureCosts; // future cost associated with each
    // segment

    // Copy constructor:
    public PendingPhrase(PendingPhrase<TK, FV> old) {
      this.concreteOpt = old.concreteOpt;
      this.segmentIdx = old.segmentIdx;
      this.firstPosition = old.firstPosition;
      this.lastPosition = old.lastPosition;
      this.futureCosts = old.futureCosts;
    }

    public PendingPhrase(
        ConcreteRule<TK, FV> concreteOpt,
        int sourceInputId,
        Derivation<TK, FV> hyp,
        CombinedFeaturizer<TK, FV> featurizer,
        Scorer<FV> scorer,
        int segmentIdx,
        int firstPosition,
        int lastPosition) {
      this.segmentIdx = segmentIdx;
      assert (segmentIdx == 0);
      this.firstPosition = firstPosition;
      this.lastPosition = lastPosition;
      this.concreteOpt = concreteOpt;
      this.futureCosts = setFutureCosts(sourceInputId, hyp, featurizer, scorer);
    }

    private static final ThreadLocal<MutableInteger> tlTranslationId =
        new ThreadLocal<MutableInteger>() {
          @Override
          protected MutableInteger initialValue() {
            return new MutableInteger();
          }
        };

    private static class SegId<TK> extends Pair<DTURule<TK>, Integer> {
      private static final long serialVersionUID = 1L;

      SegId(DTURule<TK> o, Integer i) {
        super(o, i);
      }
    }

    @SuppressWarnings("rawtypes")
    private static final ThreadLocal<Map<SegId, Double>> tlCache =
        new ThreadLocal<Map<SegId, Double>>() {
          @Override
          protected Map<SegId, Double> initialValue() {
            return new HashMap<SegId, Double>();
          }
        };

    private double[] setFutureCosts(
        int sourceInputId,
        Derivation<TK, FV> hyp,
        CombinedFeaturizer<TK, FV> featurizer,
        Scorer<FV> scorer) {

      // Do we clear the cache of future cost?
      MutableInteger lastId = tlTranslationId.get();
      @SuppressWarnings("rawtypes")
      Map<SegId, Double> fcCache = tlCache.get();
      if (lastId.intValue() != sourceInputId) {
        fcCache.clear();
        lastId.set(sourceInputId);
      }

      DTURule<TK> opt = (DTURule<TK>) concreteOpt.abstractRule;
      double[] fc = new double[opt.dtus.length];

      assert (segmentIdx == 0);
      for (int i = segmentIdx + 1; i < opt.dtus.length; ++i) {
        SegId<TK> id = new SegId<TK>(opt, i);
        Double score = fcCache.get(id);
        if (score == null) {
          Featurizable<TK, FV> f =
              new DTUFeaturizable<TK, FV>(hyp.sourceSequence, concreteOpt, sourceInputId, i);
          List<FeatureValue<FV>> phraseFeatures = featurizer.ruleFeaturize(f);
          score = scorer.getIncrementalScore(phraseFeatures);
          fcCache.put(id, score);
        }
        fc[i] = score;
        // System.err.printf("Future cost: id=%d phrase={%s} features=%s fc=%.3f\n",
        // translationId, opt.dtus[i], phraseFeatures, fc[i]);
      }
      return fc;
    }

    // Need PendingPhrase instances to be sorted for recombination:
    @Override
    public int compareTo(PendingPhrase<TK, FV> o) {
      int sig = Integer.signum(this.segmentIdx - o.segmentIdx);
      if (this.concreteOpt == o.concreteOpt) return sig;
      int h1 = System.identityHashCode(concreteOpt);
      int h2 = System.identityHashCode(o.concreteOpt);
      if (h1 == h2)
        // This case (i.e., concreteOpt != o.concreteOpt && h1 == h2) can happen
        // with a 64-bit JVM, though it is very unlikely.
        return sig;
      return Integer.signum(h1 - h2);
    }
  }

  public final Set<PendingPhrase<TK, FV>> pendingPhrases; // discontinuous
  // phrases that are
  // still "pending"

  private final int segmentIdx;
  private final double pendingPhrasesCost; // future cost estimation, which
  // currently only accounts for the LM
  // score of "pending" phrases

  private boolean hasExpired = false;

  public static void setMaxTargetPhraseSpan(int m) {
    System.err.println("Setting new maximum target phrase span: " + m);
    MAX_TARGET_PHRASE_SPAN = m;
  }

  public static int getMaxTargetPhraseSpan() {
    return MAX_TARGET_PHRASE_SPAN;
  }

  public static void setMaxPendingPhrases(int m) {
    System.err.println("Setting new maximum number of stacked phrases: " + m);
    MAX_PENDING_PHRASES = m;
  }

  public int pendingWords() {
    int sz = 0;
    for (DTUHypothesis.PendingPhrase<TK, FV> elA : pendingPhrases) {
      DTURule<TK> dtuOpt = (DTURule<TK>) elA.concreteOpt.abstractRule;
      for (int segId = elA.segmentIdx + 1; segId < dtuOpt.dtus.length; ++segId) {
        sz += dtuOpt.dtus[segId].size();
      }
    }
    return sz;
  }

  @Override
  public boolean hasPendingPhrases() {
    return !pendingPhrases.isEmpty();
  }

  /**
   * Hypothesis is "done" when the following conditions are met. (a) The number of untranslated
   * tokens must be zero, the number of "pending" phrases should be zero, and the hypothesis should
   * be unexpired.
   */
  @Override
  public boolean isDone() {
    int nPendingPhrases = (pendingPhrases == null) ? 0 : pendingPhrases.size();
    return super.isDone() && nPendingPhrases == 0 && !hasExpired;
  }

  @Override
  public double finalScoreEstimate() { // normally: score (past cost) + h
    // (future cost)
    if (pendingPhrasesCost <= 0.0) return partialScore() + pendingPhrasesCost + h;
    return partialScore() + h;
  }

  @Override
  public double score() { // as in Hypothesis, score() == finalScoreEstimate()
    return finalScoreEstimate();
  }

  @Override
  public double partialScore() {
    return score + (hasExpired ? -EXPIRATION_PENALTY : 0.0);
  }

  @Override
  public boolean hasExpired() {
    return hasExpired;
  }

  public Rule<TK> getAbstractOption() {
    assert (rule.abstractRule != null);
    return rule.abstractRule;
  }

  public boolean targetOnly() {
    return segmentIdx > 0;
  }

  @Override
  public String toString() {

    StringBuilder sb = new StringBuilder(super.toString()); // Hypothesis's
    // toString()
    sb.append(" segIx=").append(segmentIdx);
    sb.append(" id=").append(System.identityHashCode(this));
    sb.append(" tOpt=").append(System.identityHashCode(rule));
    sb.append(String.format(" c=[%.3f,%.3f,%.3f]", partialScore(), pendingPhrasesCost, h));

    if (pendingPhrases != null) {
      sb.append(" | expired:").append(hasExpired);
      sb.append(" | pending:");

      for (final PendingPhrase<TK, FV> discTargetPhrase : pendingPhrases) {
        DTURule<TK> opt = (DTURule<TK>) discTargetPhrase.concreteOpt.abstractRule;
        sb.append(" {");
        int si = discTargetPhrase.segmentIdx + 1;
        for (int i = si; i < opt.dtus.length; ++i) {
          if (i > si) sb.append(" ");
          sb.append(
              String.format(
                  "{%s} ([s=%d,e=%d,c=%.3f])",
                  opt.dtus[i].toString(" "),
                  discTargetPhrase.firstPosition,
                  discTargetPhrase.lastPosition,
                  discTargetPhrase.futureCosts[i]));
        }
        sb.append("} ");
      }
    }

    return sb.toString();
  }

  /** Compute cost of pending phrases. */
  private double costPendingPhrases() {

    double score = 0.0;

    for (PendingPhrase<TK, FV> pendingPhrase : pendingPhrases) {
      ConcreteRule<TK, FV> opt = pendingPhrase.concreteOpt;
      assert (opt.abstractRule instanceof DTURule);
      DTURule<TK> dtuOpt = (DTURule<TK>) opt.abstractRule;
      for (int i = pendingPhrase.segmentIdx + 1; i < dtuOpt.dtus.length; ++i) {
        score += pendingPhrase.futureCosts[i];
        // System.err.printf("cost pending phrases: %s %f\n",
        // dtuOpt.dtus[i].toString(), score);
      }
    }
    return score;
  }

  /** Merge current DTUHypothesis with PendingPhrase instances. */
  public List<DTUHypothesis<TK, FV>> mergeHypothesisAndPendingPhrase(
      int sourceInputId,
      CombinedFeaturizer<TK, FV> featurizer,
      Scorer<FV> scorer,
      SearchHeuristic<TK, FV> heuristic) {

    if (hasExpired) return new LinkedList<DTUHypothesis<TK, FV>>();

    for (PendingPhrase<TK, FV> pendingPhrase : pendingPhrases) {
      if (pendingPhrase.lastPosition < length) {
        hasExpired = true;
        return new LinkedList<DTUHypothesis<TK, FV>>();
      }
    }

    List<DTUHypothesis<TK, FV>> nextHyps = new LinkedList<DTUHypothesis<TK, FV>>();

    for (PendingPhrase<TK, FV> currentPhrase : pendingPhrases) {

      DTURule<TK> dtuOpt = (DTURule<TK>) currentPhrase.concreteOpt.abstractRule;

      if (currentPhrase.segmentIdx + 1 < dtuOpt.dtus.length) {

        int currentSegmentIdx = currentPhrase.segmentIdx + 1;

        if (currentPhrase.firstPosition <= length) {
          nextHyps.add(
              new DTUHypothesis<TK, FV>(
                  sourceInputId,
                  currentPhrase.concreteOpt,
                  length,
                  this,
                  featurizer,
                  scorer,
                  heuristic,
                  currentPhrase,
                  currentSegmentIdx,
                  currentPhrase.concreteOpt.abstractRule));
        }
      }
    }

    return nextHyps;
  }

  /** Constructor used for 1st segment of a discontinuous phrase. */
  public DTUHypothesis(
      int sourceInputId,
      ConcreteRule<TK, FV> translationOpt,
      int insertionPosition,
      Derivation<TK, FV> baseHyp,
      CombinedFeaturizer<TK, FV> featurizer,
      Scorer<FV> scorer,
      SearchHeuristic<TK, FV> heuristic) {

    super(
        sourceInputId,
        translationOpt,
        translationOpt.abstractRule,
        insertionPosition,
        baseHyp,
        featurizer,
        scorer,
        heuristic, /*
                    * targetPhrase=
                    */
        getSegment(translationOpt.abstractRule, 0),
        /* hasPendingPhrases= */ hasPendingPhrases(translationOpt, baseHyp, true, false),
        /* segmentIdx= */ 0);

    // Copy old pending phrases from parent hypothesis:
    this.pendingPhrases = new TreeSet<PendingPhrase<TK, FV>>();
    if (baseHyp instanceof DTUHypothesis) {
      Set<PendingPhrase<TK, FV>> oldPhrases = ((DTUHypothesis<TK, FV>) baseHyp).pendingPhrases;
      for (PendingPhrase<TK, FV> oldPhrase : oldPhrases) {
        this.pendingPhrases.add(new PendingPhrase<TK, FV>(oldPhrase));
        int lastPosition = oldPhrase.lastPosition;
        if (lastPosition < this.length) this.hasExpired = true;
      }
    }

    // First segment of a discontinuous phrase has both source and target:
    this.segmentIdx = 0;

    // If parent hypothesis has expired, so does the current:
    if (baseHyp.hasExpired()) this.hasExpired = true;

    // Add new pending phrases:
    // assert (MAX_TARGET_PHRASE_SPAN >= 0);
    if (translationOpt.abstractRule instanceof DTURule) {
      PendingPhrase<TK, FV> newPhrase =
          new PendingPhrase<TK, FV>(
              translationOpt,
              sourceInputId,
              this,
              featurizer,
              scorer,
              0,
              this.length + MIN_GAP_SIZE,
              this.length + MAX_TARGET_PHRASE_SPAN);
      pendingPhrases.add(newPhrase);
    }

    // Too many pending phrases?:
    if (pendingPhrases.size() > MAX_PENDING_PHRASES) this.hasExpired = true;

    // Estimate future cost for pending phrases:
    pendingPhrasesCost = costPendingPhrases();
    checkExpiration();
  }

  // Constructor used with successors:
  public DTUHypothesis(
      int sourceInputId,
      ConcreteRule<TK, FV> translationOpt,
      int insertionPosition,
      Derivation<TK, FV> baseHyp,
      CombinedFeaturizer<TK, FV> featurizer,
      Scorer<FV> scorer,
      SearchHeuristic<TK, FV> heuristic,
      PendingPhrase<TK, FV> currentPhrase,
      int currentSegmentIdx,
      Rule<TK> actualTranslationOption) {

    super(
        sourceInputId,
        translationOpt,
        actualTranslationOption,
        insertionPosition,
        baseHyp,
        featurizer,
        scorer,
        heuristic,
        getSegment(translationOpt.abstractRule, currentSegmentIdx),
        hasPendingPhrases(
            translationOpt,
            baseHyp,
            false,
            currentSegmentIdx + 1 == getNumberSegments(translationOpt.abstractRule)),
        currentSegmentIdx);
    assert (actualTranslationOption == translationOpt.abstractRule);

    // Copy pending phrases from parent hypothesis, and move current pending
    // phrase from pendingPhrases to current partial hypothesis:
    pendingPhrases = new TreeSet<PendingPhrase<TK, FV>>();
    Set<PendingPhrase<TK, FV>> oldPhrases = ((DTUHypothesis<TK, FV>) baseHyp).pendingPhrases;
    for (PendingPhrase<TK, FV> oldPhrase : oldPhrases) {

      if (oldPhrase != currentPhrase) {
        // This is NOT the phrase selected as successor:
        pendingPhrases.add(new PendingPhrase<TK, FV>(oldPhrase));
      } else {
        DTURule<TK> dtuOpt = (DTURule<TK>) currentPhrase.concreteOpt.abstractRule;
        // This IS the phrase selected as successor:
        if (currentPhrase.segmentIdx + 2 >= dtuOpt.dtus.length)
          continue; // just appended the last pending phrase
        PendingPhrase<TK, FV> tmpPhrase = new PendingPhrase<TK, FV>(currentPhrase);
        tmpPhrase.segmentIdx = currentPhrase.segmentIdx + 1;
        tmpPhrase.firstPosition = this.length + MIN_GAP_SIZE;
        pendingPhrases.add(tmpPhrase);
      }
      if (oldPhrase.lastPosition < this.length) this.hasExpired = true;
    }
    // this.currentPendingPhrase = tmpPhrase;
    this.segmentIdx = currentSegmentIdx;
    assert (currentSegmentIdx > 0);

    // Too many pending phrases?:
    if (pendingPhrases.size() > MAX_PENDING_PHRASES) this.hasExpired = true;

    pendingPhrasesCost = costPendingPhrases();
    checkExpiration();
  }

  // Constructor used during nbest list generation:
  public DTUHypothesis(
      int sourceInputId,
      ConcreteRule<TK, FV> translationOpt,
      int insertionPosition,
      Derivation<TK, FV> baseHyp,
      Derivation<TK, FV> nextHyp,
      CombinedFeaturizer<TK, FV> featurizer,
      Scorer<FV> scorer,
      SearchHeuristic<TK, FV> heuristic,
      Set<Rule<TK>> seenOptions) {

    super(
        sourceInputId,
        translationOpt,
        getAbstractOption(nextHyp.featurizable),
        insertionPosition,
        baseHyp,
        featurizer,
        scorer,
        heuristic,
        getTranslation(nextHyp),
        !nextHyp.featurizable.done,
        nextHyp.featurizable.getSegmentIdx());

    if (nextHyp instanceof DTUHypothesis) {
      DTUHypothesis<TK, FV> dtuNextHyp = (DTUHypothesis<TK, FV>) nextHyp;
      this.pendingPhrases = dtuNextHyp.pendingPhrases;
      this.hasExpired = dtuNextHyp.hasExpired;
      this.segmentIdx = dtuNextHyp.segmentIdx;
    } else {
      this.pendingPhrases = new TreeSet<PendingPhrase<TK, FV>>();
      this.hasExpired = false;
      this.segmentIdx = 0;
    }

    if (!this.hasExpired && baseHyp instanceof DTUHypothesis) {
      DTUHypothesis<TK, FV> dtuBaseHyp = (DTUHypothesis<TK, FV>) baseHyp;
      if (dtuBaseHyp.hasExpired && (nextHyp.untranslatedTokens != 0)) this.hasExpired = true;
    }

    seenOptions.add(translationOpt.abstractRule);
    pendingPhrasesCost = costPendingPhrases();
    checkExpiration();
  }

  // Determine whether hypothesis is bound to expire:
  private void checkExpiration() {
    // Note: this code leads to translation failures in some rare cases.

    if (hasExpired) return; // already expired

    if (pendingPhrases == null || pendingPhrases.isEmpty())
      return; // can't expire since there are no pending phrases

    // Will any pending phrase be applicable at the next step of decoding?
    int nextPosition = this.length + 1;
    for (PendingPhrase<TK, FV> pp : this.pendingPhrases) {
      if (pp.firstPosition <= nextPosition && nextPosition <= pp.lastPosition) // Answer:
        // yes
        return;
      // System.err.printf("Fail: %d <= %d <= %d\n", pp.firstPosition,
      // nextPosition, pp.lastPosition);
    }
    // System.err.println("Hypothesis bound to expire: "+this);
    hasExpired = true; // Answer: no
  }

  private static <TK, FV> RawSequence<TK> getTranslation(Derivation<TK, FV> hyp) {

    if (hyp instanceof DTUHypothesis) {

      DTUHypothesis<TK, FV> dtuHyp = (DTUHypothesis<TK, FV>) hyp;
      Rule<TK> opt = hyp.rule.abstractRule;

      if (opt instanceof DTURule) {
        DTURule<TK> dtuOpt = (DTURule<TK>) opt;
        return dtuOpt.dtus[dtuHyp.segmentIdx];
      }
    }

    return hyp.rule.abstractRule.target;
  }

  private static <TK, FV> Rule<TK> getAbstractOption(Featurizable<TK, FV> f) {
    return (f instanceof DTUFeaturizable) ? ((DTUFeaturizable<TK, FV>) f).abstractOption : null;
  }

  private static <TK> RawSequence<TK> getSegment(Rule<TK> option, int idx) {
    if (option instanceof DTURule) return ((DTURule<TK>) option).dtus[idx];
    return option.target;
  }

  private static <TK> int getNumberSegments(Rule<TK> option) {
    if (option instanceof DTURule) return ((DTURule<TK>) option).dtus.length;
    return 1;
  }

  /**
   * Returns true if the current hypothesis shouldn't be considered done because some pending
   * phrases still need to be appended to translation.
   */
  private static <TK, FV> boolean hasPendingPhrases(
      ConcreteRule<TK, FV> translationOpt,
      Derivation<TK, FV> baseHyp,
      boolean firstSegmentInOpt,
      boolean lastSegmentInOpt) {
    boolean pendingPhrases = false;

    // Two cases:
    // (1) translationOpt contains a gap in the target, in which case we must
    // return no.
    if (translationOpt.abstractRule instanceof DTURule && firstSegmentInOpt) pendingPhrases = true;

    // (2) baseHyp contains some pending phrase (that is not about to be
    // deleted).
    else if (baseHyp instanceof DTUHypothesis) {
      DTUHypothesis<TK, FV> dtuHyp = (DTUHypothesis<TK, FV>) baseHyp;
      int nPendingPhrases = dtuHyp.pendingPhrases.size();
      pendingPhrases = nPendingPhrases > 1 || (nPendingPhrases == 1 && !lastSegmentInOpt);
    }

    return pendingPhrases;
  }

  @Override
  public void debug() {
    System.err.println("###################");
    System.err.printf(
        "hypothesis [class=%s,id=%d,pos=%d,expired=%s,pending=%d]: %s\n",
        getClass(),
        System.identityHashCode(this),
        featurizable.targetPosition,
        hasExpired,
        pendingPhrases.size(),
        this);
    System.err.printf(
        "parent hypothesis [class=%s,id=%d,pos=%d,expired=%s]: %s\n",
        preceedingDerivation.getClass(),
        System.identityHashCode(preceedingDerivation),
        preceedingDerivation.featurizable.targetPosition,
        preceedingDerivation.hasExpired(),
        preceedingDerivation);
    System.err.println("pendingPhrasesCost: " + pendingPhrasesCost);

    DTUHypothesis<TK, FV> hyp = this;
    if (hyp.isDone() != hyp.featurizable.done) {
      System.err.println("Error in AbstractBeamInferer with: " + hyp);
      System.err.println("isDone(): " + hyp.isDone());
      System.err.println("pending phrases: " + hyp.pendingPhrases.size());
      System.err.println("f.done: " + hyp.featurizable.done);
      Derivation<TK, FV> curHyp = hyp;
      while (curHyp != null) {
        System.err.println("  " + curHyp.toString());
        curHyp = curHyp.preceedingDerivation;
      }
      throw new RuntimeException();
    }
  }
}