package edu.stanford.nlp.sempre.tables; import java.util.*; import edu.stanford.nlp.sempre.*; import fig.basic.LogInfo; import fig.basic.Option; /** * Return 1 if |pred| and |target| represent the same list and 0 otherwise. * * This is similar to FreebaseValueEvaluator, but * - does not give partial credits * - also check the type of the values (number, date, ...) * * @author ppasupat */ public class TableValueEvaluator implements ValueEvaluator { public static class Options { @Option(gloss = "Allow type conversion on predicted values before comparison") public boolean allowMismatchedTypes = false; @Option(gloss = "Allow matching on normalized strings (e.g. remove parentheses)") public boolean allowNormalizedStringMatch = true; @Option(gloss = "When comparing number values, only consider the value and not the unit") public boolean ignoreNumberValueUnits = true; @Option(gloss = "Strict date evaluation (year, month, and date all have to match)") public boolean strictDateEvaluation = false; } public static Options opts = new Options(); public double getCompatibility(Value target, Value pred) { List<Value> targetList = ((ListValue) target).values; if (!(pred instanceof ListValue)) return 0; List<Value> predList = ((ListValue) pred).values; // Make unique predList = new ArrayList<>(new HashSet<>(predList)); if (targetList.size() != predList.size()) return 0; for (Value targetValue : targetList) { boolean found = false; for (Value predValue : predList) { if (getItemCompatibility(targetValue, predValue)) { found = true; break; } } if (!found) return 0; } return 1; } // ============================================================ // Item Compatibility // ============================================================ // Compare one element of the list. protected boolean getItemCompatibility(Value target, Value pred) { if (pred instanceof ErrorValue) return false; // Never award points for error if (pred == null) { LogInfo.warning("Predicted value is null!"); return false; } if (target instanceof DescriptionValue) { String targetText = ((DescriptionValue) target).value; if (pred instanceof NameValue || pred instanceof DescriptionValue) { // Just has to match the description String predText = (pred instanceof NameValue) ? ((NameValue) pred).description : ((DescriptionValue) pred).value; if (predText == null) predText = ""; if (opts.allowNormalizedStringMatch) { targetText = StringNormalizationUtils.aggressiveNormalize(targetText); predText = StringNormalizationUtils.aggressiveNormalize(predText); } return targetText.equals(predText); } else if (pred instanceof NumberValue) { if (opts.allowMismatchedTypes) { NumberValue targetNumber = StringNormalizationUtils.parseNumberLenient(targetText); return targetNumber != null && targetNumber.equals(pred); } } } else if (target instanceof NumberValue) { NumberValue targetNumber = (NumberValue) target; if (pred instanceof NumberValue) { // Compare number return compareNumberValues(targetNumber, (NumberValue) pred); } else if (pred instanceof DateValue) { // Assume year DateValue date = (DateValue) pred; return date.year == targetNumber.value && date.month == -1 && date.day == -1; } else if (pred instanceof NameValue || pred instanceof DescriptionValue) { // Try converting NameValue String into NumberValue if (opts.allowMismatchedTypes) { NumberValue predNumber = StringNormalizationUtils.toNumberValue(pred); return predNumber != null && compareNumberValues(targetNumber, predNumber); } } } else if (target instanceof DateValue) { DateValue targetDate = (DateValue) target; if (pred instanceof DateValue) { // Compare date and date return compareDateValues(targetDate, (DateValue) pred); } } return target.equals(pred); } protected boolean compareNumberValues(NumberValue target, NumberValue pred) { if (opts.ignoreNumberValueUnits) { return Math.abs(target.value - pred.value) < 1e-6; } else { return target.equals(pred); } } protected boolean compareDateValues(DateValue target, DateValue pred) { if (opts.strictDateEvaluation) { return target.equals(pred); } else { // If a field in target is not blank (-1), pred must match target on that field if (target.year != -1 && target.year != pred.year) return false; if (target.month != -1 && target.month != pred.month) return false; if (target.day != -1 && target.day != pred.day) return false; return true; } } }