package edu.stanford.nlp.sempre;
import java.util.*;
import fig.basic.*;
/**
* Produces predicates (like LexiconFn) but do it from the logical forms
* in the context (inspects the ContextValue of the example).
*
* Takes depth, restrictType, and forbiddenTypes arguments allowing you
* to specify the depth/size and type of (formula) subtrees that you want to
* extract from the context.
*
* ONLY USE WITH TYPES!!
*
* E.g.,
*
* (rule $X (context) (ContextFn (depth 0) (type fb:type.any))
* would extract any unary/entity.
*
* (rule $X (context) (ContextFn (depth 1) (type (-> fb:type.any
* fb:type.any)) (forbidden (-> fb:type.any fb:type.something)) would
* extract all binaries except those with arg1 of type fb:type.something
*
* @author William Hamilton
*/
// TODO(Will): Reintegrate useful functionality from old implementation.
public class ContextFn extends SemanticFn {
// the depth/size of subtrees to extract
private int depth;
// the type that you want to extract
private SemType restrictType = SemType.topType;
// set of types to not extract (overrides restrictType).
// For example, if restrict type is very general (e.g., (-> type.any type.any))
// and you don't want some specific subtype (e.g., (-> type.something type.any))
// then you would say specify (forbidden (-> type.something type.any))
// and all subtypes of (-> type.any type.any) would be permissible
// except the forbidden one(s).
private Set<SemType> forbiddenTypes = new HashSet<SemType>();
public void init(LispTree tree) {
super.init(tree);
for (int i = 1; i < tree.children.size(); i++) {
LispTree arg = tree.child(i);
if ("type".equals(arg.child(0).value)) {
restrictType = SemType.fromLispTree(arg.child(1));
} else if ("depth".equals(arg.child(0).value)) {
depth = Integer.parseInt(arg.child(1).value);
} else if ("forbidden".equals(arg.child(0).value)) {
forbiddenTypes.add(SemType.fromLispTree(arg.child(1)));
} else {
throw new RuntimeException("Unknown argument: " + arg);
}
}
}
public DerivationStream call(final Example ex, final Callable c) {
return new MultipleDerivationStream() {
int index = 0;
List<Formula> formulas;
public Derivation createDerivation() {
if (ex.context == null) return null;
if (formulas == null) {
formulas = new ArrayList<Formula>();
for (int i = ex.context.exchanges.size() - 1; i >= 0; i--) {
ContextValue.Exchange e = ex.context.exchanges.get(i);
extractFormulas(e.formula.toLispTree());
}
}
if (index >= formulas.size()) return null;
Formula formula = formulas.get(index++);
for (SemType forbiddenType : forbiddenTypes) {
if (TypeInference.inferType(formula).meet(forbiddenType).isValid())
return null;
}
return new Derivation.Builder()
.withCallable(c)
.formula(formula)
.type(TypeInference.inferType(formula))
.createDerivation();
}
private void addFormula(Formula formula) {
if (formulas.contains(formula))
return;
formulas.add(formula);
}
// Extract from the logical form.
private void extractFormulas(LispTree formula) {
if (correctDepth(formula, 0) && typeCheck(formula)) {
addFormula(Formulas.fromLispTree(formula));
}
if (formula.isLeaf())
return;
for (LispTree child : formula.children)
extractFormulas(child);
}
private boolean correctDepth(LispTree formula, int currentLevel) {
if (formula.isLeaf()) {
return currentLevel == depth;
} else {
boolean isCorrect = true;
for (LispTree child : formula.children)
isCorrect = isCorrect && correctDepth(child, currentLevel + 1);
return isCorrect;
}
}
private boolean typeCheck(LispTree treeFormula) {
Formula formula = Formulas.fromLispTree(treeFormula);
SemType type = TypeInference.inferType(formula);
type = restrictType.meet(type);
return type.isValid();
}
};
}
}