docs: @Override
    public Prediction<DynamicAssignment, DynamicAssignment> getBestPredictions(DynamicAssignment dynamicInput,
        DynamicAssignment dynamicOutput, int numPredictions) {
      if (!factorGraph.getVariables().isValidAssignment(dynamicInput)) {
        // Cannot make predictions
        return Prediction.create(dynamicInput, dynamicOutput, Double.NEGATIVE_INFINITY, new double[0],
            Collections.<DynamicAssignment> emptyList());
      }
      // Compute max-marginals conditioned on the input
      Assignment input = factorGraph.getVariables().toAssignment(dynamicInput);
      FactorGraph conditionalFactorGraph = factorGraph.getFactorGraph(dynamicInput).conditional(input);
      MaxMarginalSet maxMarginals = marginalCalculator.computeMaxMarginals(conditionalFactorGraph);
  
      List<Assignment> bestAssignments = Lists.newArrayList();
      try {
        for (int i = 0; i < numPredictions; i++) {
          bestAssignments.add(maxMarginals.getNthBestAssignment(i));
        }
      } catch (ZeroProbabilityError e) {
        // Occurs if all outputs have zero probability under the given assignment.
        // Safely ignored (setting bestAssignments to the empty list).
      }
  
      // Need marginals in order to compute the true partition function.
      double logPartitionFunction = Double.NEGATIVE_INFINITY;
      try {
        MarginalSet marginals = marginalCalculator.computeMarginals(conditionalFactorGraph);
        logPartitionFunction = marginals.getLogPartitionFunction();
      } catch (ZeroProbabilityError e) {
        // If this occurs, the factor graph assigns zero probability 
        // to everything given dynamicInput.
        return Prediction.create(dynamicInput, dynamicOutput, Double.NEGATIVE_INFINITY, 
            new double[0], Collections.<DynamicAssignment>emptyList());
      }
      
      List<DynamicAssignment> predictedOutputs = Lists.newArrayList();
      double[] scores = new double[bestAssignments.size()];
      for (int i = 0; i < bestAssignments.size(); i++) {
        Assignment bestAssignment = bestAssignments.get(i);
        scores[i] = conditionalFactorGraph.getUnnormalizedLogProbability(bestAssignment) - logPartitionFunction;
  
        Assignment outputAssignment = bestAssignment.intersection(
            getOutputVariables(conditionalFactorGraph, outputVariablePattern));
        predictedOutputs.add(factorGraph.getVariables().toDynamicAssignment(outputAssignment,
            conditionalFactorGraph.getAllVariables()));
      }
  
      double outputScore = Double.NEGATIVE_INFINITY;
      if (dynamicOutput != null) {
        DynamicAssignment dynamicInputAndOutput = dynamicInput.union(dynamicOutput); 
        if (factorGraph.getVariables().isValidAssignment(dynamicInputAndOutput)) {
          Assignment inputAndOutput = factorGraph.getVariables().toAssignment(dynamicInputAndOutput);
          FactorGraph jointConditioned = factorGraph.getFactorGraph(dynamicInputAndOutput)
              .conditional(inputAndOutput);
          
          try {
            MarginalSet conditionedMarginals = marginalCalculator.computeMarginals(jointConditioned);
            outputScore = conditionedMarginals.getLogPartitionFunction() - logPartitionFunction;
          } catch (ZeroProbabilityError e) {
            // outputScore is already set as if output had zero probability.
          }
        }
      }
  
      return Prediction.create(dynamicInput, dynamicOutput, outputScore, scores, predictedOutputs);
    }