package edu.stanford.nlp.sempre;
import java.util.*;
import fig.basic.*;
/**
* Similar to LexiconFn, but list all approximate matches from a FuzzyMatchable instance.
*
* @author ppasupat
*/
public class FuzzyMatchFn extends SemanticFn {
public static class Options {
@Option public int verbose = 0;
}
public static Options opts = new Options();
public enum FuzzyMatchFnMode { UNARY, BINARY, ENTITY,
ORDER_BEFORE, ORDER_AFTER, ORDER_NEXT, ORDER_PREV, ORDER_ADJACENT };
private FuzzyMatchFnMode mode;
// Generate all possible denotations regardless of the phrase
private boolean matchAny = false;
public void init(LispTree tree) {
super.init(tree);
for (int i = 1; i < tree.children.size(); i++) {
String value = tree.child(i).value;
if ("unary".equals(value)) this.mode = FuzzyMatchFnMode.UNARY;
else if ("binary".equals(value)) this.mode = FuzzyMatchFnMode.BINARY;
else if ("entity".equals(value)) this.mode = FuzzyMatchFnMode.ENTITY;
else if ("any".equals(value)) this.matchAny = true;
else if ("before".equals(value)) this.mode = FuzzyMatchFnMode.ORDER_BEFORE;
else if ("after".equals(value)) this.mode = FuzzyMatchFnMode.ORDER_AFTER;
else if ("next".equals(value)) this.mode = FuzzyMatchFnMode.ORDER_NEXT;
else if ("prev".equals(value)) this.mode = FuzzyMatchFnMode.ORDER_PREV;
else if ("adjacent".equals(value)) this.mode = FuzzyMatchFnMode.ORDER_ADJACENT;
else throw new RuntimeException("Invalid argument: " + value);
}
}
@Override
public DerivationStream call(Example ex, Callable c) {
return new LazyFuzzyMatchFnDerivs(ex, c, mode, matchAny);
}
// ============================================================
// Derivation Stream
// ============================================================
public static class LazyFuzzyMatchFnDerivs extends MultipleDerivationStream {
final Example ex;
final FuzzyMatchable matchable;
final Callable c;
final String query;
final List<String> sentence;
final FuzzyMatchFnMode mode;
final boolean matchAny;
int index = 0;
List<Formula> formulas;
public LazyFuzzyMatchFnDerivs(Example ex, Callable c, FuzzyMatchFnMode mode, boolean matchAny) {
this.ex = ex;
if (ex.context != null && ex.context.graph != null && ex.context.graph instanceof FuzzyMatchable)
this.matchable = (FuzzyMatchable) ex.context.graph;
else
this.matchable = null;
this.c = c;
this.query = (matchAny || c.getChildren().isEmpty()) ? null : c.childStringValue(0);
if (c.getRule().rhs.size() == 1 && Rule.phraseCat.equals(c.getRule().rhs.get(0))) {
sentence = ex.getTokens();
} else if (c.getRule().rhs.size() == 1 && Rule.lemmaPhraseCat.equals(c.getRule().rhs.get(0))) {
sentence = ex.getLemmaTokens();
} else {
sentence = null;
}
this.mode = mode;
this.matchAny = matchAny;
if (opts.verbose >= 2)
LogInfo.logs("FuzzyMatchFn[%s]%s.call: %s",
this.mode, (this.matchAny ? "[matchAny]" : ""), this.query);
}
@Override
public Derivation createDerivation() {
if (matchable == null) return null;
if (query == null && !matchAny) return null;
// Compute the formulas if not computed yet
if (formulas == null) {
if (matchAny)
formulas = new ArrayList<>(matchable.getAllFormulas(mode));
else if (sentence != null)
formulas = new ArrayList<>(matchable.getFuzzyMatchedFormulas(sentence, c.getStart(), c.getEnd(), mode));
else
formulas = new ArrayList<>(matchable.getFuzzyMatchedFormulas(query, mode));
}
// Use the next formula to create a derivation
if (index >= formulas.size()) return null;
Formula formula = formulas.get(index++);
SemType type = TypeInference.inferType(formula);
FeatureVector features = new FeatureVector();
if (FeatureExtractor.containsDomain("fuzzyMatch")) {
features.add("fuzzyMatch", "mode=" + mode);
if (matchAny)
features.add("fuzzyMatch", "mode=" + mode + "_any");
}
return new Derivation.Builder()
.withCallable(c)
.formula(formula)
.type(type)
.localFeatureVector(features)
.createDerivation();
}
}
}