package edu.stanford.nlp.sempre;
import fig.basic.*;
import java.io.BufferedReader;
import java.io.IOException;
import java.util.*;
/**
* A Lexicon maps phrases (e.g., born) to lexical entries, which contain a
* formula (e.g., fb:people.person.place_of_birth) and a type.
* This class is meant to be simpler/faster than the normal
* UnaryLexicon/BinaryLexicon.
*
* Note: this class exists because it was annoying to add types into the normal
* UnaryLexicon and I didn't want to break backward compatibility with the rest
* of the code. Also, the normal lexicon is really slow with all those hash
* maps.
*
* The lexicon here only knows about Formulas and fuzzy matching.
* In the lexicon, we have rawPhrase.
* In the user query, we have phrase.
*
* @author Percy Liang
*/
public final class SimpleLexicon {
public static class Entry {
// rawPhrase was the original phrase in the Lexicon
public Entry(String rawPhrase, Formula formula, SemType type, StringDoubleVec features) {
this.rawPhrase = rawPhrase;
this.formula = formula;
this.type = type;
this.features = features;
}
public final String rawPhrase;
public final Formula formula;
public final SemType type;
public final StringDoubleVec features;
@Override public String toString() {
return "[" + rawPhrase + " => " + formula + " : " + type + "]";
}
}
public static class Options {
@Option(gloss = "Path to load lexicon files from") public List<String> inPaths;
@Option(gloss = "Types to allow suffix (last word) matche (for people names") public List<String> matchSuffixTypes;
}
public static Options opts = new Options();
private static SimpleLexicon lexicon;
public static SimpleLexicon getSingleton() {
if (lexicon == null) lexicon = new SimpleLexicon();
return lexicon;
}
private SimpleLexicon() {
if (opts.inPaths == null) return;
for (String path : opts.inPaths) read(path);
}
// Mapping from phrase
Map<String, List<Entry>> entries = new HashMap<String, List<Entry>>();
public void read(String path) {
LogInfo.begin_track("SimpleLexicon.read(%s)", path);
try {
BufferedReader in = IOUtils.openIn(path);
String line;
int numLines = 0;
int oldNumEntries = entries.size();
while ((line = in.readLine()) != null) {
Map<String, Object> map = Json.readMapHard(line);
numLines++;
String rawPhrase = (String) map.get("lexeme");
Formula formula = Formula.fromString((String) map.get("formula"));
// Type
String typeStr = (String) map.get("type");
SemType type = typeStr != null ? SemType.fromString(typeStr) : TypeInference.inferType(formula);
// Features
StringDoubleVec features = null;
Map<String, Double> featureMap = (Map<String, Double>) map.get("features");
if (featureMap != null) {
features = new StringDoubleVec();
for (Map.Entry<String, Double> e : featureMap.entrySet())
features.add(e.getKey(), e.getValue());
features.trimToSize();
}
// Add verbatim feature
Entry entry = new Entry(rawPhrase, formula, type, features);
String phrase = entry.rawPhrase.toLowerCase();
MapUtils.addToList(entries, phrase, entry);
// For last names
String[] parts = phrase.split(" ");
if (opts.matchSuffixTypes != null && opts.matchSuffixTypes.contains(typeStr) && parts.length > 1) {
StringDoubleVec newFeatures = new StringDoubleVec();
if (features != null) { // Copy over features
for (StringDoubleVec.Entry e : features)
newFeatures.add(e.getFirst(), e.getSecond());
}
newFeatures.add("isSuffix", 1);
newFeatures.trimToSize();
Entry newEntry = new Entry(rawPhrase, formula, type, newFeatures);
MapUtils.addToList(entries, parts[parts.length - 1], newEntry);
}
// In the future, add other mechanisms for lemmatization.
}
LogInfo.logs("Read %s lines, generated %d entries (now %d total)", numLines, entries.size() - oldNumEntries, entries.size());
} catch (IOException e) {
throw new RuntimeException(e);
}
LogInfo.end_track();
}
public List<Entry> lookup(String phrase) {
return MapUtils.get(entries, phrase, Collections.EMPTY_LIST);
}
}